bzoj1861
bzoj1861[ZJOI2006]书架
题目描述
小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
sol:虽然是模板题,但由于本人是幼儿园水平,十分无奈的看题解了(确实有很多细节写不出来)
最重要的两个变量:
int Pos[N] 标号为i的书在树上的位置
int Id[N] 树上位置为i的节点在书架上的标号[1,n]
然后我还是不会把一个节点移到任意一个位置(多多指教)
对于置顶:把当前节点Splay为根后,把左子树串到后继节点的左子树即可,置底同理
对于移动,只要交换两个点的Pos和Id就可以了,多多Splay防T
巨丑无比的代码
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=80005,inf=0x3f3f3f3f; int n,Q; namespace Pht { int Points=0,Root; int Child[N][2],Parent[N]; int Pos[N];//标号为i的书在树上的位置 int Id[N];//树上位置为i的节点在书架上的标号[0,n+1] int Size[N]; inline void Init(); inline int Check(int x); inline void PushUp(int x); inline void Rotate(int x); inline void Splay(int At,int To); inline void Insert(int Val); inline void Above(int Val); inline void Below(int Val); inline void Move(int Val,int Delta); inline int Ask_Rank(int Val); inline int Ask_Kth(int K); inline void Solve(); inline void Init() { int i; for(i=1;i<=n;i++) Insert(read()); } inline int Check(int x) { return (Child[Parent[x]][0]==x)?0:1; } inline void PushUp(int x) { Size[x]=Size[Child[x][0]]+Size[Child[x][1]]+1; Pos[Id[Child[x][0]]]=Child[x][0]; Pos[Id[Child[x][1]]]=Child[x][1]; } inline void Rotate(int x) { int y,z,oo; y=Parent[x]; z=Parent[y]; oo=Check(x); Child[y][oo]=Child[x][oo^1]; Parent[Child[x][oo^1]]=y; Child[z][Check(y)]=x; Parent[x]=z; Child[x][oo^1]=y; Parent[y]=x; PushUp(x); PushUp(y); } inline void Splay(int At,int To) { while(Parent[At]!=To) { int Father=Parent[At]; if(Parent[Father]==To) { Rotate(At); } else if(Check(At)==Check(Father)) { Rotate(Father); Rotate(At); } else { Rotate(At); Rotate(At); } } Pos[Id[At]]=At; if(To==0) Root=At; } inline void Insert(int Val) { int Now=Root,Par=0; while(Now) { Par=Now; Now=Child[Now][1]; } Now=++Points; if(Par) Child[Par][1]=Now; Child[Now][0]=Child[Now][1]=0; Parent[Now]=Par; Size[Now]=1; Pos[Val]=Now; Id[Now]=Val; Splay(Now,0); } // inline void Remove(int Val) // { // int P1=Ask_Kth(Val-1),P2=Ask_Kth(Val+1); // Splay(P1,0); // Splay(P2,P1); // int Pos=Child[P2][0]; // Child[P2][0]=0; // Size[Pos]=Parent[Pos]=Id[Pos]=0; // Splay(P2,0); // } inline void Above(int Val) { Splay(Pos[Val],0); if(!Child[Root][0]) return; //最上面了 if(!Child[Root][1]) //下面没了直接把左儿子移到右儿子就可以了 { Child[Root][1]=Child[Root][0]; Child[Root][0]=0; } else { int Now=Child[Root][1]; while(Child[Now][0]) Now=Child[Now][0]; Parent[Child[Root][0]]=Now; //把左儿子串到后继节点上,中序遍历仍然正确 Child[Now][0]=Child[Root][0]; Child[Root][0]=0; Splay(Child[Now][0],0); //从新串的节点更新一遍 } } inline void Below(int Val) { Splay(Pos[Val],0); if(!Child[Root][1]) return; if(!Child[Root][0]) { Child[Root][0]=Child[Root][1]; Child[Root][1]=0; } else { int Now=Child[Root][0]; while(Child[Now][1]) Now=Child[Now][1]; //前驱节点 Parent[Child[Root][1]]=Now; Child[Now][1]=Child[Root][1]; Child[Root][1]=0; Splay(Child[Now][1],0); } } inline void Move(int Val,int Delta) { Splay(Pos[Val],0); if(!Delta) return; else if(Delta==1) { int Upper=Child[Root][1],tmp=Pos[Val]; while(Child[Upper][0]) Upper=Child[Upper][0]; swap(Pos[Val],Pos[Id[Upper]]); swap(Id[tmp],Id[Upper]); } else { int Lower=Child[Root][0],tmp=Pos[Val]; while(Child[Lower][1]) Lower=Child[Lower][1]; swap(Pos[Val],Pos[Id[Lower]]); swap(Id[tmp],Id[Lower]); } } inline int Ask_Rank(int Val) { Splay(Pos[Val],0); return Size[Child[Root][0]]; } inline int Ask_Kth(int K) { int Now=Root; for(;;) { if(Size[Child[Now][0]]>=K) { Now=Child[Now][0]; } else if(Size[Child[Now][0]]+1==K) { return Now; } else { K-=(Size[Child[Now][0]]+1); Now=Child[Now][1]; } } } inline void Solve() { int x; char S[10]; Init(); while(Q--) { scanf("%s",S+1); switch (S[1]) { case 'T': Above(read()); break; case 'B': Below(read()); break; case 'I': R(x); Move(x,read()); break; case 'A': Wl(Ask_Rank(read())); break; case 'Q': Wl(Id[Ask_Kth(read())]); break; } } } } int main() { int i; R(n); R(Q); Pht::Solve(); return 0; } /* input 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 output 2 9 9 7 5 3 */