[模板]普通平衡树
3224: Tyvj 1728 普通平衡树
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 24082 Solved: 11089
[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
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
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
84185
492737
HINT
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
$Treap$
二叉搜索树+堆,将权值形成二叉搜索树,并且在随机$rand$第二关键字从而将其转成堆,并且在此时不破坏二叉搜索树。因为堆并且随机因素所以期望时间复杂度$O(n\log n)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #include<ctime> #include<climits> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int N=300001; struct node{ int l,r,cnt,num,size,rnk; }tr[N]; int n,root,tot; void update(int k){ tr[k].size=tr[k].cnt; tr[k].size+=tr[tr[k].l].size; tr[k].size+=tr[tr[k].r].size;return; } void zag(int &k){ int tp=tr[k].l; tr[k].l=tr[tp].r; tr[tp].r=k; tr[tp].size=tr[k].size; update(k); k=tp; return; } void zig(int &k){ int tp=tr[k].r; tr[k].r=tr[tp].l; tr[tp].l=k; tr[tp].size=tr[k].size; update(k); k=tp; return; } void insert(int x,int &k){ if(k==0){ k=++tot; tr[k].cnt=tr[k].size=1;tr[k].num=x; tr[k].rnk=rand(); return; } tr[k].size++; if(x==tr[k].num){tr[k].cnt++;return;} if(x<tr[k].num){ insert(x,tr[k].l); if(tr[tr[k].l].rnk<tr[k].rnk)zag(k); }else{ insert(x,tr[k].r); if(tr[tr[k].r].rnk<tr[k].rnk) zig(k); } return; } void del(int x,int &k){ if(x==tr[k].num){ if(tr[k].cnt>1){tr[k].size--,tr[k].cnt--;return;} if(tr[k].l*tr[k].r==0) {k=tr[k].l+tr[k].r;return;} if(tr[tr[k].l].rnk<tr[tr[k].r].rnk){zag(k);del(x,k);return;} else{zig(k);del(x,k);return;} } tr[k].size--; if(x<tr[k].num) del(x,tr[k].l); else del(x,tr[k].r); return; } int rank_x(int x,int k){ if(x==tr[k].num) return tr[tr[k].l].size+1; if(x<tr[k].num) return rank_x(x,tr[k].l); return tr[tr[k].l].size+tr[k].cnt+rank_x(x,tr[k].r); } int rank(int x,int k){ if(k==0) return 0; if(tr[tr[k].l].size<x&&tr[tr[k].l].size+tr[k].cnt>=x) return tr[k].num; if(x<=tr[tr[k].l].size) return rank(x,tr[k].l); return rank(x-tr[tr[k].l].size-tr[k].cnt,tr[k].r); } int pre(int x,int k){ if(k==0) return INT_MIN; if(x<=tr[k].num) return pre(x,tr[k].l); return max(tr[k].num,pre(x,tr[k].r)); } int nex(int x,int k){ if(k==0) return INT_MAX; if(x>=tr[k].num) return nex(x,tr[k].r); return min(tr[k].num,nex(x,tr[k].l)); } int main(){ srand(time(0)); n=read(); while(n--){ int opt=read(),x=read(); if(opt==1) insert(x,root); if(opt==2) del(x,root); if(opt==3) printf("%d\n",rank_x(x,root)); if(opt==4) printf("%d\n",rank(x,root)); if(opt==5) printf("%d\n",pre(x,root)); if(opt==6) printf("%d\n",nex(x,root)); } return 0; }
$Splay$
$Splay$与$Treap$都是在二叉搜索树上维护树高,而$Treap$用第二关键字维护,而$Splay$巧妙的运用双旋维护,均摊时间复杂度是$O(n log n)$,证明请读者自己证明。