题解 P3095 【[USACO13DEC]贝西洗牌The Bessie Shuffle】

AquaRio

2019-09-30 22:33:48

Solution

食用效果更佳:[**我的博客**](http://39.105.95.125/index.php/archives/380/) 既然没有正解,那我就来写一发把。 ### 思路: 我们对问题仔细研究,很快就会发现,问题就是给定一个位置 $x$ ,问它经过若干轮置换后会到达哪个位置。 值得注意的是我们需要对置换做出一些调整,因为每次都会删除头部的元素,所以真实的置换是 $p[i]-1$。 举个栗子: $[1, 2, 3, 4, 5] -> [2, 3, 1, 4, 5] -> [3,1,4,5]$ 在这里面,$p[1]=3$,但是 $1$ 在置换到 $3$ 号位置的时候,$2$ 被弹了出来,所以 $1$ 实际上换到的位置是 $p[1]-1=2$ 。 我们瞄一眼数据范围,$1e5$ 好吓人,我们必须找出一种带有 $\log$ 的方法来求出每一个询问的数置换的最终位置。 灵机一动,**倍增**! 我们设 $a[i][j]$ 表示 $j\ (1 \leq j \leq m) $ 在置换 $2^{i}$ 次后的位置。 于是我们就可以找到最终位置辣 (>_<) 但是要注意当中途走到 $0$ 时,说明离开了队列,应及时终止。 ### 代码: ```cpp /* Name: P3095 [USACO13DEC]贝西洗牌The Bessie Shuffle Author: AquaRio Date: 30/09/19 22:01 State: Accepted Anti_Copy: Enabled */ #include<bits/stdc++.h> using namespace std; const int N=100005,M=30; int n,m,q; int a[M][N]; inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-'){f=-1;}ch=getchar();} while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();} return x*f; } int main(){ // freopen("testdata.in","r",stdin); n=read();m=read();q=read(); for(int i=m;i;i--){ int x=read(); a[0][m-x+1]=i-1;//注意置换是P[i]-1 } for(int i=1;i<M;i++) for(int j=1;j<=m;j++) a[i][j]=a[i-1][a[i-1][j]];//倍增预处理 int x,r,len; while(q--){ x=read(); if(x<m) r=m; else r=x,x=m; len=n-r+1;  for(int i=M-1;~i;i--) if((1<<i)<=len && a[i][x]) x=a[i][x],len-=1<<i,r+=1<<i;//倍增查找最终位置 if(len) x=a[0][x],r++;//注意弹出去 cout<<n-r+m-x+1<<endl; } return 0; } ```