Submit: 13242 Solved: 5675
[Submit][Status][Discuss]
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output
对于操作3,4,5,6每行输出一个数,表示对应答案
Sample Input
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
84185
492737
HINT
Source
思路
splay
事实上,哨兵还是挺有用的,减轻逻辑负担;
由于是模板题:
t节点数值,f节点父亲编号,sz以节点为根的子树尺寸,am节点中数的个数,s节点的左右子编号;
rot()单旋;
splay()伸展(这里没有用双旋,因为脑模单旋和双旋操作量和结果一样,实测双旋也并不比单旋快);
//我可能跟我师傅学了假的双旋,对于单侧链的操作和单旋一致,故而没有任何优化效果,所以和这位大佬学吧,浅谈splay的双旋;
//好吧,可能是题目,实测双旋有很强的优化效果;
ins()添加数;
find1()把要查找的节点旋到树根;
find2()查找序号对应节点;
find3()查找前驱;
find4()查找后继;
del()删除数;
del():减少root节点的am值或是删除root节点;
case1:减少root节点的sz和am值并退出;
case2:
将root的右子树接到root的左子树中的最右子上,同时更新从root的左子树到左子树树中最右子的路径上的节点的sz值;
调整嫁接处的值,root改为原root的左子;
将原root的左子树的最右子旋到根的位置;
代码实现
1 #include<cstdio> 2 const int maxn=3e5; 3 int n,ope,val; 4 int rt,hd; 5 int t[maxn],f[maxn],sz[maxn],am[maxn],s[maxn][2]; 6 void rot(int x){ 7 int y=f[x],z=f[y],l,r; 8 l=s[y][0]==x?0:1,r=l^1; 9 if(y==rt) rt=x; 10 else{ 11 if(s[z][0]==y) s[z][0]=x; 12 else s[z][1]=x; 13 } 14 f[x]=z,f[y]=x,f[s[x][r]]=y; 15 s[y][l]=s[x][r],s[x][r]=y; 16 sz[y]=sz[s[y][0]]+sz[s[y][1]]+am[y]; 17 sz[x]=sz[s[x][0]]+sz[s[x][1]]+am[x]; 18 } 19 void splay(int x){while(x!=rt) rot(x);} 20 void ins(int k,int x,int fa){ 21 if(!rt){rt=++hd,t[rt]=x,sz[rt]=1,am[rt]++;return;} 22 while(k) fa=k,++sz[k],k=s[k][x>t[k]]; 23 k=s[fa][x>t[fa]]=++hd; 24 t[k]=x,sz[k]=1,f[k]=fa,am[k]++; 25 splay(k); 26 } 27 void find1(int k,int x){ 28 if(!k) return; 29 while(s[k][x>t[k]]&&t[k]!=x) k=s[k][x>t[k]]; 30 splay(k); 31 } 32 int find2(int k,int x){ 33 if(x<=sz[s[k][0]]) return find2(s[k][0],x); 34 if(x==sz[s[k][0]]+1) return t[k]; 35 return find2(s[k][1],x-sz[s[k][0]]-1); 36 } 37 int find3(int k,int x,int sum){ 38 if(!k) return sum; 39 if(x>t[k]) return find3(s[k][1],x,t[k]); 40 else return find3(s[k][0],x,sum); 41 } 42 int find4(int k,int x,int sum){ 43 if(!k) return sum; 44 if(x<t[k]) return find4(s[k][0],x,t[k]); 45 else return find4(s[k][1],x,sum); 46 } 47 void del(int k,int x){ 48 if(am[k]>1){am[k]--,sz[k]--;return;} 49 x=s[k][1]; 50 while(s[x][0]) x=s[x][0],sz[x]+=sz[s[k][0]]; 51 f[s[k][0]]=x,s[x][0]=s[k][0],rt=s[k][1]; 52 f[rt]=0; 53 splay(x); 54 } 55 int main(){ 56 freopen("phs.in","r",stdin); 57 freopen("phs.out","w",stdout); 58 ins(rt,-2e9-10,0),ins(rt,2e9+10,0); 59 scanf("%d",&n); 60 for(int i=1;i<=n;i++){ 61 scanf("%d%d",&ope,&val); 62 if(ope==1) ins(rt,val,0); 63 if(ope==2) find1(rt,val),del(rt,0); 64 if(ope==3) find1(rt,val),printf("%d ",sz[s[rt][0]]); 65 if(ope==4) printf("%d ",find2(rt,val+1)); 66 if(ope==5) printf("%d ",find3(rt,val,0)); 67 if(ope==6) printf("%d ",find4(rt,val,0)); 68 } 69 return 0; 70 }
又重新写了一遍,先手模了几棵二叉查找树,直观地认识了一下双旋对树结构的影响(对于“之”字形图,比较容易看出双旋的优越性);
然后,优化了一下逻辑,给出了正确的双旋,使代码更加美观,跑的也稍快了。
1 #include<cstdio> 2 const int maxn=1e5+10; 3 int rt,ts; 4 int t[maxn],sz[maxn],num[maxn]; 5 int f[maxn],s[maxn][2]; 6 void rot(int x){ 7 int y=f[x],z=f[y],l,r; 8 l=s[y][0]==x?0:1,r=l^1; 9 if(y!=rt) s[z][s[z][1]==y]=x; 10 f[x]=z,f[y]=x,f[s[x][r]]=s[x][r]!=0?y:0; 11 s[y][l]=s[x][r],s[x][r]=y; 12 sz[y]=sz[s[y][0]]+sz[s[y][1]]+num[y]; 13 sz[x]=sz[s[x][0]]+sz[s[x][1]]+num[x]; 14 } 15 void splay(int x){ 16 int y,z; 17 while(x!=rt){ 18 y=f[x],z=f[y]; 19 if(y==rt) rot(x),rt=x; 20 else{ 21 rot((s[z][0]==y)==(s[y][0]==x)?y:x),rot(x); 22 if(z==rt) rt=x; 23 } 24 } 25 } 26 void ins(int k,int x){ 27 int fa=0; 28 while(k&&t[k]!=x) fa=k,++sz[k],k=s[k][x>t[k]]; 29 if(t[k]==x) num[k]++,sz[k]++; 30 else{ 31 k=s[fa][x>t[fa]]=++ts; 32 t[k]=x,sz[k]=1,f[k]=fa,num[k]=1; 33 } 34 splay(k); 35 } 36 void del(int k,int x){ 37 while(t[k]!=x) 38 --sz[k],k=s[k][x>t[k]]; 39 num[k]--,sz[k]--; 40 splay(k); 41 if(!num[k]){ 42 rt=x=s[k][0]; 43 while(s[x][1]) sz[x]+=sz[s[k][1]],x=s[x][1]; 44 sz[x]+=sz[s[k][1]],s[x][1]=s[k][1],f[s[k][1]]=x; 45 } 46 } 47 int query(int k,int x){ 48 while(t[k]!=x) k=s[k][x>t[k]]; 49 splay(k); 50 return sz[s[k][0]]; 51 } 52 int search(int k,int x){ 53 if(x<=sz[s[k][0]]) return search(s[k][0],x); 54 if(x<=sz[s[k][0]]+num[k]) return t[k]; 55 return search(s[k][1],x-sz[s[k][0]]-num[k]); 56 } 57 int in_x(int k,int x,int now){ 58 if(!k) return now; 59 if(x>t[k]){ 60 if(num[k]) return in_x(s[k][1],x,t[k]); 61 else return in_x(s[k][1],x,now); 62 } 63 else return in_x(s[k][0],x,now); 64 } 65 int ax_x(int k,int x,int now){ 66 if(!k) return now; 67 if(x<t[k]){ 68 if(num[k]) return ax_x(s[k][0],x,t[k]); 69 else return ax_x(s[k][0],x,now); 70 } 71 else return ax_x(s[k][1],x,now); 72 } 73 int n,opt,x; 74 int main(){ 75 scanf("%d",&n); 76 rt=++ts,t[rt]=1e9,sz[rt]=1,num[rt]=1,ins(rt,-1e9); 77 for(int i=1;i<=n;i++){ 78 scanf("%d%d",&opt,&x); 79 if(opt==1) ins(rt,x); 80 else if(opt==2) del(rt,x); 81 else if(opt==3) printf("%d ",query(rt,x)); 82 else if(opt==4) printf("%d ",search(rt,x+1)); 83 else if(opt==5) printf("%d ",in_x(rt,x,0)); 84 else if(opt==6) printf("%d ",ax_x(rt,x,0)); 85 } 86 return 0; 87 }