二逼平衡树(线段树套平衡树)

线段树套平衡树【题目】
先引入模板题:二逼平衡树
本题要求实现一个数据结构,可以查询:
区间内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));
		}
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值