bzoj4444 国旗计划 递推

       首先离散化,然后破环成链,那么覆盖住所有边防站相当于再链中覆盖一段[l,l+n],那么强制选择第i为战士就相当于强制左边界为a[i],a[i]表示第i个战士覆盖的左边界。

       另外可以发现,任意两个答案之间不会相差超过1。

       那么可以令f[i]表示左端点<=i时右端点的最大值,那么覆盖[l,l+n]相当于从i,一路沿着i->f[i]走直到i>=l+n,那么步数就相当于最小覆盖上数。发现这构成了一颗树,因此相当于求树中走到一个点的步数。直接暴力求步数即可,因为相差在1以内。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;

int n,m,cnt,tp,len,a[N],b[N],ans[N],num[N<<1],f[N<<2],fst[N<<2],nxt[N<<2],stk[N<<2];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int find(int x){
	int l=1,r=cnt,mid;
	while (l<r){
		mid=(l+r)>>1;
		if (num[mid]<x) l=mid+1; else r=mid;
	}
	return l;
}
void up(int &x,int y){ if (y>x) x=y; }
void dfs(int x){
	stk[++tp]=x; int p;
	if (x<=cnt)
		for (ans[x]=len; ; ans[x]++)
			if (stk[tp-ans[x]]>=x+cnt) break;
	for (p=fst[x]; p; p=nxt[p]) dfs(p);
	tp--;
}
int main(){
	n=read(); int i=read();
	for (i=1; i<=n; i++){
		num[++m]=a[i]=read(); num[++m]=b[i]=read();
	}
	sort(num+1,num+m+1);
	cnt=1;
	for (i=2; i<=m; i++)
		if (num[i]!=num[cnt]) num[++cnt]=num[i];
	m=cnt<<1;
	for (i=1; i<=n; i++){
		a[i]=find(a[i]); b[i]=find(b[i]);
		if (a[i]<b[i]){
			up(f[a[i]],b[i]); up(f[a[i]+cnt],b[i]+cnt);
		} else{
			up(f[1],b[i]); up(f[a[i]],b[i]+cnt); up(f[a[i]+cnt],m);
		}
	}
	for (i=2; i<=m; i++) up(f[i],f[i-1]);
	for (i=1; i<m; i++){
		nxt[i]=fst[f[i]]; fst[f[i]]=i;
	}
	len=-1;
	for (i=1; i<=cnt; i=f[i]) len++;
	dfs(m);
	for (i=1; i<=n; i++) printf("%d ",ans[a[i]]);
	return 0;
}


by lych

2016.3.31

你可能感兴趣的:(离散化,递推)