BZOJ3827 : [Poi2014]Around the world

把环倍长,破环成链

设f[i]表示i一次性能飞达的最右边的点,因为f[]单调递增,所以可以$O(n)$求出

这样就形成了一个树结构,对于每个节点,在其到根节点路径上二分出深度最大的点,使得其飞过一圈

常数优化:

注意到答案只有两种,所以可以进行最优性剪枝

内存优化:

只需要开3个200W的数组即可,分别表示相邻两站的距离、f[i]、存该点到根节点路径上所有点的栈

对于树结构的存储,注意到f[]单调递增,所以我们可以求出c[i]表示父亲为i的点的开始的位置

在DFS时,如果这个点第一次走到,那么它下一步就走向c[i],否则走向++c[i]

对于这个数组中的每个数,可以拆成两半,各11位,分别放进f[i]和b[i]的后11位中

 

#include<cstdio>
const int N=2000010,B=2097151;
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';}
int n,m,i,j,d,x,lim,ans,t,k,l,r,u,mid;unsigned int a[N],b[N],f[N];
inline void set(int x){(f[x]&=B)|=(i&2047)<<21,(b[x]&=B)|=i>>11<<21;}
inline void cal(int x){
  if(!k){
    l=1,r=t-1;
    while(l<=r){
      mid=(l+r)>>1;
      if((b[mid]&B)>=x)l=(u=mid)+1;else r=mid-1;
    }
    ans=t-u,k=1;
  }else{
    if(t>ans&&(b[t-ans]&B)<x)k=2;
    if(t+1>ans&&(b[t-ans+1]&B)>=x)ans--,k=2;
  }
}
int main(){
  read(n),read(m);
  for(i=1;i<=n;i++){
    read(x);a[i]=a[i+n]=x;
    if(lim<x)lim=x;
  }
  while(m--){
    read(d);
    if(d<lim){puts("NIE");continue;}
    for(j=i=1;i<=n*2;i++){
      while(j<n*2&&d>=a[j])d-=a[j++];
      f[i]=j;
      d+=a[i];
      b[i]=0;
    }
    k=f[b[t=1]=n*2]=0;
    for(i=n*2-1;i;i--)set(f[i]&B);
    while(t){
      x=b[t]&B;
      if((f[b[t+1]&B]&B)!=x){
        if(x<=n)cal(x+n);
        if(k==2)break;
        i=(b[x]>>21<<11)|(f[x]>>21);
      }else{
        i=(b[t+1]&B)+1;
        if((f[i]&B)!=x)i=0;
      }
      if(i)++t,b[t]=(b[t]>>21<<21)|i;else t--;
    }
    printf("%d\n",ans);
  }
  return 0;
}

  

 

posted @ 2015-01-08 20:53  Claris  阅读(331)  评论(0编辑  收藏  举报