小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语句你应该输出一行,一个数,代表询问的答案。
数据范围
100%的数据:n,m < = 80000
题解Here!
Splay比较好做,那就一发 Splay 吧。
1. Top 和 Bottom 实质上就是把序列中的一个数放到第一个和最后一个。
具体实现可以把要操作的节点提到根节点,然后把左子树合并到右子树上或把右子树合并到左子树上。
2. Insert操作就是把要操作的节点和前驱后继交换位置,即:把两个点的特征值即编号进行交换。
3. Ask操作询问序列中在此节点前面的数有多少个,提到根节点输出左儿子size就可以了。
4. Query操作就是一个简单的查询序列中第s个数的编号,相当于查询第k大,kth即可。
由于我们需要定位编号为s的节点在树中的位置,所以我使用了 id 数组记录并维护。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 80010
using namespace std;
int n,m,root=1,c=1,id[MAXN];
struct node{
int son[2];
int f,v,s;
}a[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<‘0‘||c>‘9‘){if(c==‘-‘)w=-1;c=getchar();}
while(c>=‘0‘&&c<=‘9‘){date=date*10+c-‘0‘;c=getchar();}
return date*w;
}
inline void clean(int rt){
a[rt].son[0]=a[rt].son[1]=a[rt].f=a[rt].v=a[rt].s=0;
}
inline void pushup(int rt){
if(!rt)return;
a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+1;
id[a[a[rt].son[0]].v]=a[rt].son[0];id[a[a[rt].son[1]].v]=a[rt].son[1];
}
inline void turn(int rt,int k){
int x=a[rt].f,y=a[x].f;
a[x].son[k^1]=a[rt].son[k];
if(a[rt].son[k])a[a[rt].son[k]].f=x;
a[rt].f=y;
if(y)a[y].son[a[y].son[1]==x]=rt;
a[x].f=rt;
a[rt].son[k]=x;
pushup(x);pushup(rt);
}
void splay(int rt,int ancestry){
while(a[rt].f!=ancestry){
int x=a[rt].f,y=a[x].f;
if(y==ancestry)turn(rt,a[x].son[0]==rt);
else{
int k=a[y].son[0]==x?1:0;
if(a[x].son[k]==rt){turn(rt,k^1);turn(rt,k);}
else{turn(x,k);turn(rt,k);}
}
}
if(ancestry==0)root=rt;
}
int kth(int rt,int x){
int y=a[rt].son[0];
if(a[y].s+1==x)return rt;
else if(x<=a[y].s)return kth(y,x);
else return kth(a[rt].son[1],x-a[y].s-1);
}
inline void newnode(int x){
int rt=c++;
a[rt].son[0]=a[rt].son[1]=0;
a[rt].v=x;a[rt].s=1;id[x]=rt;
if(rt==1)return;
a[rt-1].son[1]=rt;a[rt].f=rt-1;
splay(rt,0);
}
inline void top(int x){
int rt=id[x];
splay(rt,0);
if(!a[rt].son[0])return;
if(!a[rt].son[1]){
a[rt].son[1]=a[rt].son[0];
a[rt].son[0]=0;
}
else{
int y=kth(root,a[a[rt].son[0]].s+2);
a[a[root].son[0]].f=y;
a[y].son[0]=a[root].son[0];
a[root].son[0]=0;
splay(y,0);
}
}
inline void bottom(int x){
int rt=id[x];
splay(rt,0);
if(!a[rt].son[1])return;
if(!a[rt].son[0]){
a[rt].son[0]=a[rt].son[1];
a[rt].son[1]=0;
}
else{
int y=kth(root,a[a[rt].son[0]].s);
a[a[root].son[1]].f=y;
a[y].son[1]=a[root].son[1];
a[root].son[1]=0;
splay(y,0);
}
}
inline void insert(int x,int y){
if(!y)return;
splay(id[x],0);
int p,q,k=kth(root,a[a[id[x]].son[0]].s+y+1);
p=a[k].v;q=id[x];
swap(id[x],id[p]);swap(a[q].v,a[k].v);
}
inline int ask(int x){
x=id[x];
splay(x,0);
return a[a[x].son[0]].s;
}
int main(){
char ch[10];
int x,y;
n=read();m=read();
for(int i=1;i<=n;i++){
x=read();
newnode(x);
}
while(m--){
scanf("%s",ch);x=read();
switch(ch[0]){
case ‘T‘:top(x);break;
case ‘B‘:bottom(x);break;
case ‘I‘:y=read();insert(x,y);break;
case ‘A‘:printf("%d\n",ask(x));break;
case ‘Q‘:printf("%d\n",a[kth(root,x)].v);break;
}
}
return 0;
}