二逼平衡树(线段树套平衡树)
线段树套平衡树
【题目】
先引入模板题:二逼平衡树。
本题要求实现一个数据结构,可以查询:
区间内x的排名/前驱/后继,排名为k的数;支持单点修改。
【思考】
来源:二逼平衡树-线段树套无旋Treap
看到排名,可以联想到平衡树,但平衡树并不能直接实现区间操作。
看到区间修改,可以联想到线段树。
我们就把两个数据结构放在一起:
外层线段树,内层平衡树。
具体来说,在线段树的每个节点上(对每个区间)开一颗平衡树。
【操作】
1.前驱后继,在区间内找到最大/最小。
2.求排名,在区间内统计\(<x\)的数的个数,全部加起来再+1。
3.单点修改,在所有含\(x\)的平衡树上,暴力删数并插入。
4.排名为\(k\)的树,二分答案,查询排名是否符合。
怎么保证找到的在序列中呢?一个数及更大的数(\(<\)序列中比它大的第一个数)查询到的排名相同,与其他都不同,所以最小符合条件的肯定是要查询的,且在序列中。
网上鲜有用无旋treap的,大多用splay。
无旋treap好写,为什么不用呢?
#include <bits/stdc++.h>
using namespace std;
struct treap {
int val[100005],size[100005],son[100005][2],rt,tot,key[100005];
void update(int a) {
//if(!a)printf("OK\n");
size[a]=size[son[a][0]]+size[son[a][1]]+1;
}
int new_code(int a) {
size[++tot]=1;
val[tot]=a;
key[tot]=rand();
return tot;
}
void ins(int a)
{
int x=0,y=0;
val_split(rt,a,x,y);
rt=merge(merge(x,new_code(a)),y);
}
void del(int a)
{
int x=0,y=0,z=0;
val_split(rt,a,x,z);
val_split(x,a-1,x,y);
y=merge(son[y][0],son[y][1]);
rt=merge(merge(x,y),z);
}
int merge(int a, int b) {
if (!a||!b)
return a+b;
if (key[a]<key[b])
{
son[a][1]=merge(son[a][1],b);
update(a);
return a;
}
else
{
son[b][0]=merge(a,son[b][0]);
update(b);
return b;
}
}
void val_split(int now,int k,int &x,int &y)
{
if(!now)x=y=0;
else
{if(val[now]<=k)
{
x=now;
val_split(son[now][1],k,son[now][1],y);
}
else
{
y=now;
val_split(son[now][0],k,x,son[now][0]);
}
update(now);
}
}
void kth_split(int now,int k,int &x,int &y)
{
if(!now)x=y=0;
else
{if(k<=size[son[now][0]])
{
y=now;
kth_split(son[now][0],k,x,son[now][0]);
}
else
{
x=now;
kth_split(son[now][1],k-size[son[now][0]]-1,son[now][1],y);
}
update(now);
}
}
int kth(int now,int k)
{
while(1)
{
if(k<=size[son[now][0]])now=son[now][0];
else if(k==size[son[now][0]]+1)return now;
else k-=size[son[now][0]]+1,now=son[now][1];
}
}
int kth_find(int a)
{
int x=0,y=0,sum;
val_split(rt,a-1,x,y);
sum=size[x]+1;
rt=merge(x,y);
return sum;
}
int val_find(int a)
{
return val[kth(rt,a)];
}
int pre(int a)
{
int x=0,y=0,sum;
val_split(rt,a-1,x,y);
sum=val[kth(x,size[x])];
rt=merge(x,y);
return sum;
}
int nxt(int a)
{
int x=0,y=0,sum;
val_split(rt,a,x,y);
sum=val[kth(y,1)];
rt=merge(x,y);
return sum;
}
} tree;
int main()
{
//freopen("P3369_6.in","r",stdin);
//freopen("ber.out","w",stdout);
int n,opt,a;
for(scanf("%d",&n);n--;)
{
scanf("%d%d",&opt,&a);
if(opt==1)
{
tree.ins(a);
}
if(opt==2)
{
tree.del(a);
}
if(opt==3)
{
printf("%d\n",tree.kth_find(a));
}
if(opt==4)
{
printf("%d\n",tree.val_find(a));
}
if(opt==5)
{
printf("%d\n",tree.pre(a));
}
if(opt==6)
{
printf("%d\n",tree.nxt(a));
}
}
}