初始序列为 1,2,3,…,n。聪明的你一定可以通过举例或是画图等方法发现,对于一次操作,给出 R,其实就是把序列的前 R 个数截下来搬到序列末尾。R 如果大于等于剩余牌的数量,要对这个数量取膜。显然正确性没有问题。
那么每次操作,我们按大小分裂出前 R 个点,也就是平衡树中序遍历的前 R 个点,也就是当前序列中前 R 张牌的编号,然后将分裂的两颗树合并,注意由于是把这一段放到末尾,所以 merge 传参数的时候千万别写反。写反的话裂完原样合并你写个寂寞。这时候发一张牌,也就是序列的第一个数,也就是平衡树中序遍历的第一个数,所以直接按大小分裂出大小为 1 的一棵树,编号即这张牌。然后根节点直接赋值为分出来的另一棵树的根,因为发完牌这个数我们就不要了。
```
#include<bits/stdc++.h>
using namespace std;
int read(){
int ss=0,ww=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
ww=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
ss=ss*10+ch-'0';
ch=getchar();
}
return ss*ww;
}
int n,m;
const int N=700100;
int root,cnt,ls[N],rs[N],sz[N],c[N];
int newnode(){
cnt++;
c[cnt]=rand();
sz[cnt]=1;
return cnt;
}
int x,y,z;
void upd(int r){
sz[r]=sz[ls[r]]+sz[rs[r]]+1;
}
void split(int r,int k,int &x,int &y){
if(!r){
x=0;
y=0;
return;
}
if(sz[ls[r]]>=k){
y=r;
split(ls[r],k,x,ls[r]);
}
else{
x=r;
split(rs[r],k-sz[ls[r]]-1,rs[r],y);
}
upd(r);
}
int merge(int r1,int r2){
if(!r1||!r2)
return r1+r2;
if(c[r1]<=c[r2]){
rs[r1]=merge(rs[r1],r2);
upd(r1);
return r1;
}
else{
ls[r2]=merge(r1,ls[r2]);
upd(r2);
return r2;
}
}
int main(){
n=read();
for(int i=1;i<=n;i++)
root=merge(root,newnode());
int rest=n;
for(int i=1;i<=n;i++){
int a;
a=read();
a%=rest;
rest--;
if(a){
split(root,a,x,y);
root=merge(y,x);
}
split(root,1,x,y);
printf("%d\n",x);
root=y;
}
return 0;
}
```