题目链接: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]
题解:
Treap模板题。
由于普通二叉搜索树容易退化成链状,考虑到在随机数据下产生的BST是趋近平衡树的,因此Treap就是用“随机”来创造平衡条件。
在原来BST的基础上,我们可以对树上所有节点都另外增加一个随机生成的权值 $dat$,迫使整棵BST满足关于 $dat$ 的“堆性质”。
在Splay的学习中我们已经知道,右旋zig和左旋zag是不会影响BST的“BST性质”的,而通过zig和zag我们正好又可以达到交换父子节点位置的目的,
因此,我们可以参照二叉堆,如果父子两节点不满足堆性质,则用zig或者zag交换两者位置,从而迫使整棵树满足关于 $dat$ 的“堆性质”。
正是由于这颗二叉树,键值 $key$ 满足BST性质,另一个权值 $dat$ 满足堆性质,因此用单词 tree 和 heap 构成了该数据结构的名称 treap。
AC代码:
#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; } } }