非旋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;
}

浙公网安备 33010602011771号