题意:
有三种操作:
F 表示一个人到了队首
B 表示一个人到了队尾
O x 表示第x个进来的人找到距离他人最少的出口出去,同时这些挡路的人也出去,并且按顺序从另一个门进来,问你有多少人出去再进来了。
题解:
这种区间平移的题目很明显可以用fhq_treap做,但是每次怎么找到那个人的位置呢,可以在treap中维护每个人的父亲,找当前的人的排名的时候只往需要每次网上跳,如果当前点是父亲的右儿子,那么将父亲的左儿子的siz+1加到位置当中即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int ch[N][3];// 0左孩子 1右孩子
int val[N];// 每一个点的权值
int pri[N];// 随机生成的附件权值
int siz[N];// 以i为节点的树的节点数量
int tot;// 总结点的数量
int to[N];
int fa[N];
mt19937 rnd(time(NULL));
void update(int x){
siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];
if(ch[x][0])fa[ch[x][0]]=x;
if(ch[x][1])fa[ch[x][1]]=x;
}
unordered_map<int,int>f;
int finds(int x){return f.count(x)?f[x]=finds(f[x]):x;}
int newnode(int v){
siz[++tot]=1;// 新开辟一个节点
val[tot]=v;
to[v]=tot;
pri[tot]=finds(rnd());
f[pri[tot]]=finds(pri[tot]+1);
fa[tot]=tot;
return tot;
}
int merge(int x,int y){
if(!x||!y)return x+y;
if(pri[x]<pri[y]){
ch[x][1]=merge(ch[x][1],y);
update(x);
return x;
}
else {
ch[y][0]=merge(x,ch[y][0]);
update(y);
return y;
}
}
void split(int rt,int k,int &x,int &y){
if(!rt)x=y=0;
else {
if(siz[ch[rt][0]]+1<=k)
x=rt,split(ch[rt][1],k-siz[ch[rt][0]]-1,ch[rt][1],y);
else
y=rt,split(ch[rt][0],k,x,ch[rt][0]);
update(rt);
}
}
char s[5];
int main()
{
srand(unsigned(time(NULL)));
int n;
scanf("%d",&n);
int rt=0,x,all=0;
for(int i=1;i<=n;i++){
scanf("%s",s);
if(s[0]=='O'){
scanf("%d",&x);
//x=to[x];
int now=x,sum=siz[ch[x][0]]+1;
while(rt!=now){
if(ch[fa[now]][1]==now)sum+=siz[ch[fa[now]][0]]+1;
now=fa[now];
}
printf("%d\n",min(sum-1,all-sum));
int r1,r2,r3;
split(rt,sum,r1,r2);
split(r1,sum-1,r1,r3);
rt=merge(r2,r1);
all--;
}
else if(s[0]=='F')
rt=merge(newnode(i),rt),all++;
else
rt=merge(rt,newnode(i)),all++;
}
return 0;
}