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;
}

  

posted @ 2016-03-21 23:48  Claris  阅读(1394)  评论(0编辑  收藏  举报