非旋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,则以另一个为根
否则随机合并
/* 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
下面程序中给每个点设置了一个随机的优先级,用于树的合并时使用。
#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; }