非旋Treap入门讲义
入门讲义:
https://baijiahao.baidu.com/s?id=1610669523057506534&wfr=spider&for=pc
普通平衡树
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
输入
第一行为n,表示操作的个数
下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
输出
对于操作3,4,5,6每行输出一个数,表示对应答案
样例输入
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
样例输出
106465
84185
492737
Sol
1:加入元素
总结点个数tot增加,将权值赋好
然后在原树中查找其排名x(第多少小)
然后从原树中分裂出x-1个结点的树a
然后将a树与tot代表的树进行合并,再与从前树的剩余部分合并
2:删除权值为v的点
先找出排名x
然后从原树中分裂两个树,一个树有x个点
再从这个树中分裂出有x-1个点的树,再与原树的剩余部分合并
3:合并操作
对根结点编号为a,b的树进行合并。
此时要保证a树中所有点权值小于b树中所有点权值。
如果a,b中有一个为0,则以另一个为根
否则随机合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | /* treap本质为平衡二叉树,即每个结点,其左子结点的权值比它小, 右子结点权值比它大 非旋转treap只是维护树的平衡一种手段罢了。 其可分成两类,一类是维护权值,一类是维护下标序号 对于维护权值的,在insert节点时,先找下排名情况 再对树进行分裂,合并操作 */ #include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef pair< int , int > pii; int tot,son[100005][2],c[100005],val[100005],root; int read() { int x=0,f=1; char ch= getchar (); for (;ch< '0' ||ch> '9' ;ch= getchar ()) if (ch== '-' )f=-1; for (;ch>= '0' &&ch<= '9' ;ch= getchar ())x=x*10+ch- '0' ; return x*f; } int random( int lim) { return rand ()%lim+1; } void updata( int a) { c[a]=c[son[a][0]]+1+c[son[a][1]]; } int getrank( int v) //查找权值为v的数字的排名 { int res=0; for ( int x=root;x;) { if (val[x]<v) //如果当前根结点小于v,则所有v及其左子树均小于v { res+=c[son[x][0]]+1; x=son[x][1]; } else x=son[x][0]; } return res+1; } pii split( int a, int k) //从a这个树中分出K个点来,构成两个树 //其中左边树任意点权值小于右边树任意点的 { if (c[a]==k) //如果a这个树正好有k个结点,则直接返回a这个树,右返回值为0 return make_pair(a,0); if (k==0) return make_pair(0,a); //左边为空树,右为原来a这个树 pii tmp; if (c[son[a][0]]>=k) //如果左子树结点个数大于等于k { tmp=split(son[a][0],k); //从左子树中分离 son[a][0]=tmp.second; //a的左子树只剩下了分离后的结点 updata(a); //更新a的相关值 return make_pair(tmp.first,a); //这k个点所组成的树的树根做为其左返回值, //其它的结点所形成的树做为右返回值 } else //左子树的结点不够用,还要从右边弄点过来 { tmp=split(son[a][1],k-c[son[a][0]]-1); //减去左子树及根结点 son[a][1]=tmp.first; //目标树以a为根,左子树为从前a的左子根 //右子根为从前a的右子树的一部分 updata(a); return make_pair(a,tmp.second); } } int merge( int a, int b) //将根结点编号分为a,b的树进行合并 //此时b树中所有点权值大于a树中的所有点权 { if (!a||!b) return a+b; if (random(c[a]+c[b])<=c[a]) { son[a][1]=merge(son[a][1],b); //将b做为a的右子树 updata(a); return a; } else { son[b][0]=merge(a,son[b][0]); //将a做为b的左子树 updata(b); return b; } } void insert( int v) { tot++; val[tot]=v; memset (son[tot],0, sizeof (son[tot])); c[tot]=1; int x=getrank(v)-1; pii tmp=split(root,x); //从root中分离出前x个结点,tmp.first为分裂后左树的根结点编号 //tmp.second为分裂后右根的根结点编号 root=merge(merge(tmp.first,tot),tmp.second); //先将第tot个点与分离出来的左树合并,再与其右树合并 return ; } void del( int v) //删除权值为v的数字 { int x=getrank(v); pii tmp=split(root,x); int c=tmp.second; tmp=split(tmp.first,x-1); root=merge(tmp.first,c); } int getval( int a, int k) //查找第k小的数字 { if (!k) return 0; if (c[son[a][0]]>=k) return getval(son[a][0],k); if (c[son[a][0]]+1==k) return a; return getval(son[a][1],k-c[son[a][0]]-1); } int main() { srand (20020220); int n=read(); for ( int i=1;i<=n;i++) { int mode=read(),x=read(); if (mode==1) //加入权值为x的数 insert(x); if (mode==2) //删除权值为x的数 del(x); if (mode==3) //查询x数的排名 printf ( "%d\n" ,getrank(x)); if (mode==4) //查询排名为x的数 printf ( "%d\n" ,val[getval(root,x)]); if (mode==5) printf ( "%d\n" ,val[getval(root,getrank(x)-1)]); if (mode==6) printf ( "%d\n" ,val[getval(root,getrank(x+1))]); } return 0; } |
https://www.cnblogs.com/akakw1/p/9892156.html
下面程序中给每个点设置了一个随机的优先级,用于树的合并时使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #include<bits/stdc++.h> using namespace std; inline int gi() { register int x=0,op=1,c; while (c= getchar (),c< '0' ||c> '9' ) if (c== '-' )op=-op; x=c^48; while (c= getchar (),c>= '0' &&c<= '9' )x=(x<<3)+(x<<1)+(c^48); return x*op; } struct node { int son[2]; int val, s; int v; node() { son[0] = son[1] = 0; s = 0; val = rand (); } }t[100001]; void push_up( int p) { t[p].s = t[t[p].son[0]].s + t[t[p].son[1]].s + 1; } void split_v( int p, int v, int &x, int &y) { if (!p) return void (x = y = 0); if (t[p].v>v) { y = p; split_v(t[p].son[0], v, x, t[p].son[0]); } else { x = p; split_v(t[p].son[1], v, t[p].son[1], y); } push_up(p); } void split_k( int p, int k, int &x, int &y) { if (!p) return void (x = y = 0); if (k <= t[t[p].son[0]].s) { y = p; split_k(t[p].son[0], k, x, t[p].son[0]); } else { x = p; split_k(t[p].son[1], k - t[t[p].son[0]].s - 1, t[p].son[1], y); } push_up(p); } int merge( int x, int y) { if (! x || ! y) return x + y; if (t[x].val < t[y].val) //每个点有个随机优先级 { t[x].son[1] = merge(t[x].son[1], y); //将y与x的右子树合并,再做为x的右子树 push_up(x); return x; } else { t[y].son[0] = merge(x, t[y].son[0]); // 将x与y的左子树合并,再做为y的左子树 push_up(y); return y; } } int tot=0; int new_node( int v) { t[++tot].v = v; t[tot].s=1; return tot; } int root=0; int main() { srand ( time (0)); int n=gi(); int op,x; int r1,r2; while (n--) { op=gi(),x=gi(); switch (op) { case 1: //插入 split_v(root,x,root,r1); root=merge(root,merge(new_node(x),r1)); break ; case 2: //删除 split_v(root,x-1,root,r1); split_k(r1,1,r2,r1); root=merge(root,r1); break ; case 3: //排名 split_v(root,x-1,root,r1); printf ( "%d\n" ,t[root].s+1); root=merge(root,r1); break ; case 4: //k小值 split_k(root,x-1,root,r1); split_k(r1,1,r1,r2); printf ( "%d\n" ,t[r1].v); root=merge(root,merge(r1,r2)); break ; case 5: //前驱 split_v(root,x-1,root,r1); split_k(root,t[root].s-1,root,r2); printf ( "%d\n" ,t[r2].v); root=merge(root,merge(r2,r1)); break ; case 6: //后继 split_v(root,x,root,r1); split_k(r1,1,r1,r2); printf ( "%d\n" ,t[r1].v); root=merge(root,merge(r1,r2)); } } return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步