普通平衡树——非旋转treap
Posted JhinLZH
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了普通平衡树——非旋转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与上述类似,在这里不加赘述。
1 void split(int x,int &lroot,int &rroot,int val) 2 { 3 if(!x) 4 { 5 lroot=rroot=0; 6 return ; 7 } 8 if(v[x]<=val) 9 { 10 lroot=x; 11 split(rs[x],rs[lroot],rroot,val); 12 } 13 else 14 { 15 rroot=x; 16 split(ls[x],lroot,ls[rroot],val); 17 } 18 up(x); 19 }
2.合并
就是把断裂开的树合并起来,因为要维护堆的性质所以按可并堆来合并。
1 void merge(int &x,int a,int b) 2 { 3 if(!a||!b) 4 { 5 x=a+b; 6 return ; 7 } 8 if(r[a]<r[b]) 9 { 10 x=a; 11 merge(rs[x],rs[a],b); 12 } 13 else 14 { 15 x=b; 16 merge(ls[x],a,ls[b]); 17 } 18 up(x); 19 }
为了方便删除,所以建议把相同权值的点分开来加入树中,不要都放在同一个点。
非旋转treap代码比较短(为了清晰我写的比较长qwq),但唯一的缺点就是难理解。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<iostream> 6 using namespace std; 7 int INF=1000000000; 8 int n; 9 int opt,x; 10 int r[100010]; 11 int ls[100010]; 12 int rs[100010]; 13 int size[100010]; 14 int v[100010]; 15 int root; 16 int tot; 17 void up(int x) 18 { 19 size[x]=size[ls[x]]+size[rs[x]]+1; 20 } 21 void build(int &x,int val) 22 { 23 tot++; 24 size[tot]=1; 25 r[tot]=rand(); 26 v[tot]=val; 27 ls[tot]=rs[tot]=0; 28 x=tot; 29 } 30 void merge(int &x,int a,int b) 31 { 32 if(!a||!b) 33 { 34 x=a+b; 35 return ; 36 } 37 if(r[a]<r[b]) 38 { 39 x=a; 40 merge(rs[x],rs[a],b); 41 } 42 else 43 { 44 x=b; 45 merge(ls[x],a,ls[b]); 46 } 47 up(x); 48 } 49 void split(int x,int &lroot,int &rroot,int val) 50 { 51 if(!x) 52 { 53 lroot=rroot=0; 54 return ; 55 } 56 if(v[x]<=val) 57 { 58 lroot=x; 59 split(rs[x],rs[lroot],rroot,val); 60 } 61 else 62 { 63 rroot=x; 64 split(ls[x],lroot,ls[rroot],val); 65 } 66 up(x); 67 } 68 void insert_sum(int val) 69 { 70 int x=0; 71 int y=0; 72 int z=0; 73 build(z,val); 74 split(root,x,y,val); 75 merge(x,x,z); 76 merge(root,x,y); 77 } 78 void delete_sum(int val) 79 { 80 int x=0; 81 int y=0; 82 int z=0; 83 split(root,x,y,val); 84 split(x,x,z,val-1); 85 merge(z,ls[z],rs[z]); 86 merge(x,x,z); 87 merge(root,x,y); 88 } 89 void ask_rank(int val) 90 { 91 int x=0; 92 int y=0; 93 split(root,x,y,val-1); 94 printf("%d\n",size[x]+1); 95 merge(root,x,y); 96 } 97 void ask_sum(int x,int num) 98 { 99 while(size[ls[x]]+1!=num) 100 { 101 if(num<=size[ls[x]]) 102 { 103 x=ls[x]; 104 } 105 else 106 { 107 num-=(size[ls[x]]+1); 108 x=rs[x]; 109 } 110 } 111 printf("%d\n",v[x]); 112 } 113 void ask_front(int val) 114 { 115 int x=0; 116 int y=0; 117 split(root,x,y,val-1); 118 ask_sum(x,size[x]); 119 merge(root,x,y); 120 } 121 void ask_back(int val) 122 { 123 int x=0; 124 int y=0; 125 split(root,x,y,val); 126 ask_sum(y,1); 127 merge(root,x,y); 128 } 129 int main() 130 { 131 srand(16); 132 scanf("%d",&n); 133 for(int i=1;i<=n;i++) 134 { 135 scanf("%d%d",&opt,&x); 136 if(opt==1) 137 { 138 insert_sum(x); 139 } 140 else if(opt==2) 141 { 142 delete_sum(x); 143 } 144 else if(opt==3) 145 { 146 ask_rank(x); 147 } 148 else if(opt==4) 149 { 150 ask_sum(root,x); 151 } 152 else if(opt==5) 153 { 154 ask_front(x); 155 } 156 else if(opt==6) 157 { 158 ask_back(x); 159 } 160 } 161 return 0; 162 }
以上是关于普通平衡树——非旋转treap的主要内容,如果未能解决你的问题,请参考以下文章
luoguP5055 模板可持久化文艺平衡树 可持久化非旋转treap