ZJOI2006 书架 lg2596
题面见https://www.luogu.org/problemnew/show/P2596
题面就是描述了一个书柜从上到下放着书,可以看作一个序列,每个数的序号为它在从上向下数第几本
一开始建树偷了个懒,就直接一个个insert
因为题目中都是以书的编号进行操作,所以记一个pos[ ]
操作一:把pos[x] splay到根,左儿子都是比它编号小的,那么就把左儿子挂到根的后面,根+1的前面,具体怎么找这个点看代码解释
操作二:同一理
操作三:实际上只更改了x和x的前驱或后继的值,和他们代表的值的位置,只修改这两个标记即可
操作四:把pos[x] splay到根,左儿子都是比它编号小的,那么答案就是st[ls].size
操作五:常规查询第k大
#include<bits/stdc++.h> using namespace std; inline int read(){ int w=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ w=(w<<3)+(w<<1)+ch-48; ch=getchar(); } return w*f; } int n,m,cnt,tot,root,a[500010],pos[500010]; bool debug; struct node{ int val,ch[2],size,f; }st[5000010]; inline void push_up(int x){ int ls=st[x].ch[0];int rs=st[x].ch[1]; st[x].size=st[ls].size+st[rs].size+1; pos[st[ls].val]=ls;pos[st[rs].val]=rs; } inline bool identify(int p){ return st[st[p].f].ch[1]==p; } inline void connect(int x,int fa,int son){ st[x].f=fa;st[fa].ch[son]=x;return; } inline void rotate(int x){ int y=st[x].f;int z=st[y].f; int yson=identify(x);int zson=identify(y); int b=st[x].ch[yson^1]; connect(b,y,yson);connect(y,x,(yson^1));connect(x,z,zson); push_up(y);push_up(x);if(z)push_up(z);return; } inline void splay(int x,int goal){ while(st[x].f!=goal){ int y=st[x].f;int z=st[y].f; int yson=identify(x);int zson=identify(y); if(z!=goal){ if(yson==zson) rotate(y); else rotate(x); } rotate(x); } if(!goal) root=x; return; }//这以上都是常规操作 inline void insert(int x){ cnt++;int now=cnt;st[now].size=1; st[now].val=x;pos[x]=now;st[now].ch[0]=st[now].ch[1]=0; if(cnt>1){//只要当前节点不是根,都要将它和父亲连一下,并splay到根 st[now].f=now-1;st[now-1].ch[1]=now; splay(now,0); } }//建树操作 inline void output(int x){ int ls=st[x].ch[0];int rs=st[x].ch[1]; if(ls) output(ls); printf("%d ",st[x].val); if(rs) output(rs); return; }//输出整颗区间树,debug用的 inline int find(int p,int rk){ int ls=st[p].ch[0];int rs=st[p].ch[1]; if(st[ls].size+1==rk) return p; if(st[ls].size>=rk) return find(ls,rk); return find(rs,rk-st[ls].size-1); }//找树上排名 inline void Top(int x){ x=pos[x];splay(x,0); if(!st[x].ch[0]) return;//如果没有左儿子就不用管了 if(!st[x].ch[1]){//没有右儿子就把左儿子挂过去就完事了 st[x].ch[1]=st[x].ch[0];st[x].ch[0]=0;return; } int ls=st[x].ch[0]; int y=find(root,st[ls].size+2);//根的rank是st[ls].size+1,st[ls].size+2为根的后继 st[ls].f=y;st[y].ch[0]=ls;//根的左子树应该挂在根的后继的左子树 st[x].ch[0]=0;splay(y,0);//最后把更改的点splay到根 return; } inline void Bottom(int x){//操作同上,反向操作即可 x=pos[x];splay(x,0); if(!st[x].ch[1]) return; if(!st[x].ch[0]){ st[x].ch[0]=st[x].ch[1];st[x].ch[1]=0;return; } int ls=st[x].ch[0];int rs=st[x].ch[1]; int y=find(root,st[ls].size); st[rs].f=y;st[y].ch[1]=rs; st[x].ch[1]=0;splay(y,0); return; } inline void change(int x,int y){//题目中的insert操作 x=pos[x];splay(x,0);if(!y) return;//如果y==0,说明就不用修改 int ls=st[x].ch[0]; if(y==-1){//y==-1就找前驱 y=find(root,st[ls].size); } else y=find(root,st[ls].size+2);//y==1就找后缀 swap(pos[st[x].val],pos[st[y].val]);//这要修改两个点的值和两个点值的所在位置 swap(st[x].val,st[y].val);return; } inline void rnk(int x){//常规查排名操作 x=pos[x];splay(x,0); int ls=st[x].ch[0]; printf("%d\n",st[ls].size);return; } inline void kth(int x){//常规排名找点操作 x=find(root,x); printf("%d\n",st[x].val);return; } int main(){ n=read();m=read();int i,j,k;root=1; for(i=1;i<=n;i++){ int x=read();insert(x); } //debug=true; while(m--){ string s;cin>>s;int x,y; if(s[0]=='T'){ x=read();Top(x); } if(s[0]=='B'){ x=read();Bottom(x); } if(s[0]=='I'){ x=read();y=read();change(x,y); } if(s[0]=='A'){ x=read();rnk(x); } if(s[0]=='Q'){ x=read();kth(x); } if(debug){ cout<<"debuging"; output(root); cout<<endl; } } return 0; }