题目描述
小T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用1到n的正整数给每本书都编了号。
小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有X本书,那么放回去时这本书上面就只可能有X-1、X或X+1本书。
当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。
久而久之,小T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:(1)编号为X的书在书柜的什么位置;(2)从上到下第i本书的编号是多少。
输入格式:
第一行有两个数n,m,分别表示书的个数以及命令的条数;第二行为n个正整数:第i个数表示初始时从上至下第i个位置放置的书的编号;第三行到m+2行,每行一条命令。命令有5种形式:
1. Top S——表示把编号为S的书放在最上面。
2. Bottom S——表示把编号为S的书放在最下面。
3. Insert S T——T∈{-1,0,1},若编号为S的书上面有X本书,则这条命令表示把这本书放回去后它的上面有X+T本书;
4. Ask S——询问编号为S的书的上面目前有多少本书。
5. Query S——询问从上面数起的第S本书的编号。
输出格式:
对于每一条Ask或Query语句你应该输出一行,一个数,代表询问的答案。
输入样例
10 10
1 3 2 7 5 8 10 4 9 6
Query 3
Top 5
Ask 6
Bottom 3
Ask 3
Top 6
Insert 4 -1
Query 5
Query 2
Ask 2
输出样例
2
9
9
7
5
3
说明
100%的数据,n,m <= 80000
*******************************
题目分析:
Top
把指定元素编号旋转到根
1.若此时根节点无左子树,则直接返回
2.若此时根节点无右子树,则直接交换左右子树
3.左右子树均存在
找到当前序列中排名为size[ch[rt][0]+2的编号,设为y
(即找到当前根节点元素在序列中的的下一个元素编号,后继)
令根的左子树成为y的左子树(ch[y][0]=ch[rt][0])
Bottom
把指定元素编号旋转到根
1.若此时根节点无右子树,则直接返回
2.若此时根节点无左子树,则直接交换左右子树
3.左右子树均存在
找到当前序列中排名为size[ch[rt][0]的编号,设为y
(即找到当前根节点元素在序列中的的上一个元素编号,前驱)
令根的右子树成为y的右子树(ch[y][1]=ch[rt][1])
Insert
t=0时直接忽略操作
把指定元素编号旋转到根
若t=1,找到根节点后继,交换编号及元素信息
若t=-1,找到根节点前驱,交换编号及元素信息
Ask
把指定元素编号旋转到根
输出根节点左子树大小
Query
常规find操作
****************************************
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
return f*x;
}
void print(int x)
{
if(x<0){putchar(‘-‘);x=-x;}
if(x>9)print(x/10);
putchar(x%10+‘0‘);
}
int n,m,rt;
int v[100010],pos[100010],size[100010];
int ch[100010][2],fa[100010],sz;
char ss[20];
void update(int p)
{
size[p]=size[ch[p][0]]+size[ch[p][1]]+1;
}
void rotate(int& p,int x) {
int y=fa[x],z=fa[y];
int t=(ch[y][0]==x);
if(y==p) p=x;
else if(ch[z][0]==y) ch[z][0]=x;
else ch[z][1]=x;
fa[y]=x; fa[ch[x][t]]=y; fa[x]=z;
ch[y][t^1]=ch[x][t]; ch[x][t]=y;
update(y);update(x);
}
void splay(int& p,int x)
{
while(x!=p)
{
int y=fa[x],z=fa[y];
if(y!=p)
{
if((ch[y][0]==x)^(ch[z][0]==y)) rotate(p,x);
else rotate(p,y);
}
rotate(p,x);
}
}
void ins(int x)
{
v[++sz]=x; pos[x]=sz;
size[sz]=1; ch[sz][0]=ch[sz][1]=0;
if (sz>1)
{
ch[sz-1][1]=sz;fa[sz]=sz-1;
splay(rt,sz);
}
}
int find(int p,int k)
{
int ss=size[ch[p][0]];
if(ss+1==k) return p;
else if(ss>=k) return find(ch[p][0],k);
else return find(ch[p][1],k-ss-1);
}
void top_bottom(int d)
{
int x=read(),y; x=pos[x];
splay(rt,x);//指定元素旋转到根
if(!ch[x][d]) return;//第1种情况
if(!ch[x][d^1]) {ch[x][d^1]=ch[x][d]; ch[x][d]=0; return;}//第2种情况
if(d==0) y=find(rt,size[ch[x][0]]+2);//元素置顶,找到根的后继
else y=find(rt,size[ch[x][0]]);//元素置底,找到根的前驱
fa[ch[rt][d]]=y;//合并子树
ch[y][d]=ch[rt][d];
ch[rt][d]=0;
splay(rt,y);//伸展保证复杂度
}
void change()
{
int x=read(),d=read(),y; if(d==0) return;//d=0直接忽略
splay(rt,pos[x]);//指定元素旋转到根
if(d==1) y=find(rt,size[ch[pos[x]][0]]+2);//元素提前,找到其前驱
else y=find(rt,size[ch[pos[x]][0]]);//元素置后,找到其后继
int tv=v[y],tpos=pos[x];//交换信息
swap(pos[x],pos[tv]);
swap(v[tpos],v[y]);
}
int get()
{
int x=read(); x=pos[x];
splay(rt,x);
return size[ch[x][0]];
}
int main()
{
n=read();m=read();rt=1;
for(int i=1;i<=n;++i)
{
int x=read();ins(x);
}
while(m--)
{
scanf("%s",&ss);
if(ss[0]==‘T‘) top_bottom(0);
else if(ss[0]==‘B‘) top_bottom(1);
else if(ss[0]==‘I‘) change();
else if(ss[0]==‘A‘) print( get() ),putchar(‘\n‘);
else if(ss[0]==‘Q‘) print( v[ find(rt,read()) ] ),putchar(‘\n‘);
}
return 0;
}