BZOJ4444 : [Scoi2015]国旗计划
首先将坐标离散化,因为区间互不包含,可以理解为对于每个起点输出最少需要多少个战士。
将环倍长,破环成链,设$f[i]$表示区间左端点不超过$i$时右端点的最大值,可以通过$O(n)$递推求出。
那么如果将$f[i]$看成$i$的祖先的话,它实际上形成了一棵以$2n$为根的树。
首先暴力计算出1号点的答案$t$,设$L=t-1$。
然后dfs这棵树,用一个栈按深度依次保存每个点到根路径上的点。
对于一个点,只需要从$L$开始暴力枚举答案,然后$O(1)$检验即可。
因为每个点的答案相差不超过1,所以除去离散化后,整个算法的时间复杂度为$O(n)$。
#include<cstdio> #include<algorithm> #define N 400010 int n,m,i,x,y,L,a[N/2][2],b[N],st[N/2],f[N*2],g[N*2],nxt[N*2],q[N*2],t,ans[N]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline int lower(int x){ int l=1,r=m,mid,t; while(l<=r)if(b[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1; return t; } inline void up(int&x,int y){if(x<y)x=y;} void dfs(int x){ q[++t]=x; if(x<=m)for(int i=L;;i++)if(q[t-i]>=x+m){ans[x]=i;break;} for(int i=g[x];i;i=nxt[i])dfs(i); t--; } int main(){ read(n),read(m); for(m=0,i=1;i<=n;i++)read(a[i][0]),read(a[i][1]),b[++m]=a[i][0],b[++m]=a[i][1]; for(std::sort(b+1,b+m+1),i=1;i<=n;i++){ st[i]=x=lower(a[i][0]),y=lower(a[i][1]); if(x<y)up(f[x],y),up(f[x+m],y+m); else up(f[1],y),up(f[x],y+m),up(f[x+m],m+m); } for(i=1;i<=m+m;i++)up(f[i],f[i-1]); for(i=1;i<m+m;i++)nxt[i]=g[f[i]],g[f[i]]=i; for(L=-1,i=1;i<=m;i=f[i])L++; dfs(m+m); for(i=1;i<=n;i++)printf("%d ",ans[st[i]]); return 0; }