BZOJ4095 : [Usaco2013 Dec]The Bessie Shuffle
首先将排列和整个序列以及询问都反过来,问题变成给定一个位置$x$,问它经过若干轮置换后会到达哪个位置。
每次置换之后窗口都会往右滑动一个,因此其实真实置换是$p[i]-1$。
对于每个询问,求出轮数,倍增找到最终位置,注意当中途走到$0$时,说明离开了窗口,应及时终止。
时间复杂度$O((m+q)\log n)$。
#include<cstdio> const int N=100010,M=30; int n,m,q,i,j,x,r,k,a[M][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';} int main(){ read(n),read(m),read(q); for(i=m;i;i--)read(x),a[0][m-x+1]=i-1; for(i=1;i<M;i++)for(j=1;j<=m;j++)a[i][j]=a[i-1][a[i-1][j]]; while(q--){ read(x); if(x<m)r=m;else r=x,x=m; k=n-r+1; for(i=M-1;~i;i--)if((1<<i)<=k&&a[i][x])x=a[i][x],k-=1<<i,r+=1<<i; if(k)x=a[0][x],r++; printf("%d\n",n-(r-m+x)+1); } return 0; }