BZOJ3224普通平衡树——非旋转treap
题目:
此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
n<=100000 所有数字均在-107到107内。
输入样例:
10 1 106465 4 1 1 317721 1 460929 1 644985 1 84185 1 89851 6 81968 1 492737 5 493598
输出样例:
106465 84185 492737
变量声明:size[x],以x为根节点的子树大小;ls[x],x的左儿子;rs[x],x的右子树;r[x],x节点的随机数;v[x],x节点的权值。
root,树的总根;tot,树的大小。
非旋转treap不同于旋转treap需要靠旋转来维护平衡树的性质,他的操作可以用简单暴力来形容——只有合并和断裂两个操作。他不但有treap的优良性质,还有许多优点:支持可持久化和区间操作,常数比splay小。
下面介绍一下非旋转treap的这两个操作:
1.断裂
就是去掉一条边,把treap拆分成两棵树,对于区间操作可以进行两次断裂来分割出一段区间再进行操作。
以查找value为例,从root往下走,如果v[x]>value,那么下一步走ls[x],之后的点都比x小,把x接到右树上,下一次再接到右树上的点就是x的左儿子。
v[x]<=value与上述类似,在这里不加赘述。
void split(int x,int &lroot,int &rroot,int val) { if(!x) { lroot=rroot=0; return ; } if(v[x]<=val) { lroot=x; split(rs[x],rs[lroot],rroot,val); } else { rroot=x; split(ls[x],lroot,ls[rroot],val); } up(x); }
2.合并
就是把断裂开的树合并起来,因为要维护堆的性质所以按可并堆来合并。
void merge(int &x,int a,int b) { if(!a||!b) { x=a+b; return ; } if(r[a]<r[b]) { x=a; merge(rs[x],rs[a],b); } else { x=b; merge(ls[x],a,ls[b]); } up(x); }
为了方便删除,所以建议把相同权值的点分开来加入树中,不要都放在同一个点。
非旋转treap代码比较短(为了清晰我写的比较长qwq)。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<iostream> using namespace std; int INF=1000000000; int n; int opt,x; int r[100010]; int ls[100010]; int rs[100010]; int size[100010]; int v[100010]; int root; int tot; void up(int x) { size[x]=size[ls[x]]+size[rs[x]]+1; } void build(int &x,int val) { tot++; size[tot]=1; r[tot]=rand(); v[tot]=val; ls[tot]=rs[tot]=0; x=tot; } void merge(int &x,int a,int b) { if(!a||!b) { x=a+b; return ; } if(r[a]<r[b]) { x=a; merge(rs[x],rs[a],b); } else { x=b; merge(ls[x],a,ls[b]); } up(x); } void split(int x,int &lroot,int &rroot,int val) { if(!x) { lroot=rroot=0; return ; } if(v[x]<=val) { lroot=x; split(rs[x],rs[lroot],rroot,val); } else { rroot=x; split(ls[x],lroot,ls[rroot],val); } up(x); } void insert_sum(int val) { int x=0; int y=0; int z=0; build(z,val); split(root,x,y,val); merge(x,x,z); merge(root,x,y); } void delete_sum(int val) { int x=0; int y=0; int z=0; split(root,x,y,val); split(x,x,z,val-1); merge(z,ls[z],rs[z]); merge(x,x,z); merge(root,x,y); } void ask_rank(int val) { int x=0; int y=0; split(root,x,y,val-1); printf("%d\n",size[x]+1); merge(root,x,y); } void ask_sum(int x,int num) { while(size[ls[x]]+1!=num) { if(num<=size[ls[x]]) { x=ls[x]; } else { num-=(size[ls[x]]+1); x=rs[x]; } } printf("%d\n",v[x]); } void ask_front(int val) { int x=0; int y=0; split(root,x,y,val-1); if(size[x]==0) { printf("0\n"); } else { ask_sum(x,size[x]); } merge(root,x,y); } void ask_back(int val) { int x=0; int y=0; split(root,x,y,val); ask_sum(y,1); merge(root,x,y); } int main() { srand(16); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&opt,&x); if(opt==1) { insert_sum(x); } else if(opt==2) { delete_sum(x); } else if(opt==3) { if(tot==0) { printf("0\n"); } else { ask_rank(x); } } else if(opt==4) { if(tot==0) { printf("0\n"); } else { ask_sum(root,x); } } else if(opt==5) { if(tot==0) { printf("0\n"); } else { ask_front(x); } } else if(opt==6) { if(tot==0) { printf("0\n"); } else { ask_back(x); } } } return 0; }