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