BZOJ 3224 - 普通平衡树 - [Treap][Splay]
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224
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
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
HINT
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
题解1:
Treap模板题。
由于普通二叉搜索树容易退化成链状,考虑到在随机数据下产生的BST是趋近平衡树的,因此Treap就是用“随机”来创造平衡条件。
在原来BST的基础上,我们可以对树上所有节点都另外增加一个随机生成的权值 $dat$,迫使整棵BST对于 $dat$ 满足“堆性质”。
在Splay的学习中我们已经知道,右旋zig和左旋zag是不会影响BST的“BST性质”的,而通过zig和zag我们正好又可以达到交换父子节点位置的目的,
因此,我们可以参照二叉堆,如果父子两节点不满足堆性质,则用zig或者zag交换两者位置,从而迫使整棵树满足关于 $dat$ 的“堆性质”。
正是由于这颗二叉树,键值 $key$ 满足BST性质,另一个权值 $dat$ 满足堆性质,因此用单词 tree 和 heap 构成了该数据结构的名称 treap。
AC代码(Treap):
#include<bits/stdc++.h> using namespace std; const int INF=0x7fffffff; const int maxn=1e5+10; /******************************** Treap - st ********************************/ int root,nodecnt; int ch[maxn][2]; int key[maxn],dat[maxn]; int cnt[maxn],siz[maxn]; int NewNode(int val) { int x=++nodecnt; key[x]=val; dat[x]=rand(); cnt[x]=siz[x]=1; ch[x][0]=ch[x][1]=0; return x; } void Pushup(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+cnt[x]; } void Init() { root=nodecnt=0; key[0]=dat[0]=0; cnt[0]=siz[0]=0; ch[0][0]=ch[0][1]=0; } void Build() { Init(); NewNode(-INF); NewNode(INF); ch[root=1][1]=2; Pushup(root); } void zig(int &x) { int lc=ch[x][0]; ch[x][0]=ch[lc][1], ch[lc][1]=x, x=lc; Pushup(ch[x][1]), Pushup(x); } void zag(int &x) { int rc=ch[x][1]; ch[x][1]=ch[rc][0], ch[rc][0]=x, x=rc; Pushup(ch[x][0]), Pushup(x); } int GetRank(int x,int val) { if(x==0) return 0; if(val==key[x]) return siz[ch[x][0]]+1; if(val<key[x]) return GetRank(ch[x][0],val); return siz[ch[x][0]]+cnt[x]+GetRank(ch[x][1],val); } int GetKth(int x,int k) { if(x==0) return INF; if(siz[ch[x][0]]>=k) return GetKth(ch[x][0],k); if(siz[ch[x][0]]+cnt[x]>=k) return key[x]; return GetKth(ch[x][1],k-siz[ch[x][0]]-cnt[x]); } void Insert(int &x,int val) { if(x==0) { x=NewNode(val); return; } if(val==key[x]) { cnt[x]++; Pushup(x); return; } else if(val<key[x]) { Insert(ch[x][0],val); if(dat[x] < dat[ch[x][0]]) zig(x); } else { Insert(ch[x][1],val); if(dat[x] < dat[ch[x][1]]) zag(x); } Pushup(x); } void Remove(int &x,int val) { if(x==0) return; if(val==key[x]) { if(cnt[x]>1) { cnt[x]--; Pushup(x); return; } if(ch[x][0] || ch[x][1]) { if(ch[x][1]==0 || dat[ch[x][0]] > dat[ch[x][1]]) zig(x), Remove(ch[x][1],val); else zag(x), Remove(ch[x][0],val); Pushup(x); } else x=0; return; } (val<key[x])?Remove(ch[x][0],val):Remove(ch[x][1],val); Pushup(x); } int GetPre(int val) { int ans=1; //a[1].val==-INF int x=root; while(x) { if(val==key[x]) { if(ch[x][0]>0) { x=ch[x][0]; while(ch[x][1]>0) x=ch[x][1]; ans=x; } break; } if(key[x]<val && key[x]>key[ans]) ans=x; x=(val<key[x])?ch[x][0]:ch[x][1]; } return key[ans]; } int GetNxt(int val) { int ans=2; //a[2].val==INF int x=root; while(x) { if(val==key[x]) { if(ch[x][1]>0) { x=ch[x][1]; while(ch[x][0]>0) x=ch[x][0]; ans=x; } break; } if(key[x]>val && key[x]<key[ans]) ans=x; x=(val<key[x])?ch[x][0]:ch[x][1]; } return key[ans]; } /******************************** Treap - ed ********************************/ int main() { int n; cin>>n; Build(); while(n--) { int opt,x; scanf("%d%d",&opt,&x); switch(opt) { case 1: Insert(root,x); break; case 2: Remove(root,x); break; case 3: printf("%d\n",GetRank(root,x)-1); break; case 4: printf("%d\n",GetKth(root,x+1)); break; case 5: printf("%d\n",GetPre(x)); break; case 6: printf("%d\n",GetNxt(x)); break; } } }
注:本模板的 $zig(x)$ 和 $zag(x)$ 中 $x$,是旋转前处于父亲节点位置。
题解2:
Splay模板题。
Splay的原理以及实现参考:伸展树(Splay Tree)进阶 - 从原理到实现
AC代码(Splay):
#include<bits/stdc++.h> using namespace std; const int INF=0x7fffffff; const int maxn=1e5+10; /******************************** splay - st ********************************/ #define Key_value ch[ch[root][1]][0] int root,nodecnt; int par[maxn],ch[maxn][2]; int key[maxn],cnt[maxn],siz[maxn]; void NewNode(int &x,int p,int k) { x=++nodecnt; par[x]=p; ch[x][0]=ch[x][1]=0; key[x]=k; cnt[x]=siz[x]=1; } void Pushup(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+cnt[x]; } void Rotate(int x,int type) //旋转,0为左旋zag,1为右旋zig { int y=par[x]; ch[y][!type]=ch[x][type]; par[ch[x][type]]=y; if(par[y]) ch[par[y]][(ch[par[y]][1]==y)]=x; par[x]=par[y]; ch[x][type]=y; par[y]=x; Pushup(y); Pushup(x); } void Splay(int x,int goal) { while(par[x]!=goal) { if(par[par[x]]==goal) Rotate(x,ch[par[x]][0]==x); //左孩子zig,右孩子zag else { int y=par[x]; int type=(ch[par[y]][0]==y); //type=0,y是右孩子;type=1,y是左孩子 if(ch[y][type]==x) { Rotate(x,!type); Rotate(x,type); } else { Rotate(y,type); Rotate(x,type); } } } if(goal==0) root=x; } int GetMin(int x) { while(ch[x][0]) x=ch[x][0]; return x; } int GetMax(int x) { while(ch[x][1]) x=ch[x][1]; return x; } void Init() //初始化,前后各加一个空节点 { root=nodecnt=0; par[0]=ch[0][0]=ch[0][1]=0; cnt[0]=siz[0]=0; key[0]=0; NewNode(root,0,-INF); //头部加入一个空位 NewNode(ch[root][1],root,INF); //尾部加入一个空位 Pushup(ch[root][1]); Pushup(root); } void Insert(int val) { int x=root; while(1) { if(val==key[x]) { cnt[x]++; Pushup(x); Splay(x,0); break; } int type=val>key[x]; if(ch[x][type]==0) { NewNode(ch[x][type],x,val); Pushup(x); Splay(ch[x][type],0); break; } else x=ch[x][type]; } } void Delete(int val) { int x=root; while(x) { if(val==key[x]) { if(cnt[x]>1) { cnt[x]--; Pushup(x); Splay(x,0); return; } if(ch[x][0] && ch[x][1]) { Splay(GetMax(ch[x][0]),0); Splay(GetMin(ch[x][1]),root); par[Key_value]=0; Key_value=0; Pushup(ch[root][1]); Pushup(root); return; } int fa=par[x],type=(ch[fa][1]==x); if(ch[x][0]) { par[ch[x][0]]=fa; ch[fa][type]=ch[x][0]; Pushup(fa); Splay(ch[fa][type],0); return; } if(ch[x][1]) { par[ch[x][1]]=fa; ch[fa][type]=ch[x][1]; Pushup(fa); Splay(ch[fa][type],0); return; } ch[fa][type]=0; Pushup(fa); Splay(fa,0); return; } if(val<key[x]) x=ch[x][0]; else x=ch[x][1]; } } int GetRank_pos; int GetRank(int x,int val) { if(x==0) return 0; if(val==key[x]) return siz[ch[GetRank_pos=x][0]]+1; if(val<key[x]) return GetRank(ch[x][0],val); return siz[ch[x][0]]+cnt[x]+GetRank(ch[x][1],val); } int GetKth_pos; int GetKth(int x,int k) { if(x==0) return 0; if(siz[ch[x][0]]>=k) return GetKth(ch[x][0],k); if(siz[ch[x][0]]+cnt[x]>=k) return key[GetKth_pos=x]; return GetKth(ch[x][1],k-siz[ch[x][0]]-cnt[x]); } /******************************** splay - ed ********************************/ int main() { int n; cin>>n; Init(); while(n--) { int opt,x; scanf("%d%d",&opt,&x); switch(opt) { case 1: Insert(x); break; case 2: Delete(x); break; case 3: printf("%d\n",GetRank(root,x)-1); Splay(GetRank_pos,0); break; case 4: printf("%d\n",GetKth(root,x+1)); Splay(GetKth_pos,0); break; case 5: Insert(x); printf("%d\n",key[GetMax(ch[root][0])]); Delete(x); break; case 6: Insert(x); printf("%d\n",key[GetMin(ch[root][1])]); Delete(x); break; } } }