树套树学习笔记

shadom / 2023-08-08 / 原文

直接正文

不得不说,这玩意是真TM的恶心,调了我两天

总的分析

什么时候需要使用树套树呢?在很多条件需要你使用一种树形结构维护,同时又加上了区间或是其它格外的限制时,可以使用树套树。像让你维护区间第k大,区间排名之类的两种数据结构的功能综合到了一道题中时。

线段树套平衡树

思路

这是树套树中最普通的一种,但能维护的东西却非常的多。他可以维护所有平衡树树的操作再加上区间限制。

我们只需要正常的建一颗线段树,对于每颗线段树,都建立一颗平衡树,每次查询时合并区间就行。

易错点

在修改节点时,记得对原序列进行修改,递归时,记得注意if语句里的边界问题。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
#define int int
struct node{
	int lc,rc;
	int key,value;
	int size;
}e[10000000+10];
int tot,a[N],n,m;
int read()
{
	char ch = getchar();
	int x = 0, flag = 1;
	while (ch != '-' && (ch < '0' || ch > '9'))
		ch = getchar();
	if (ch == '-')
	{
		ch = getchar();
		flag = -1;
	}
	while (ch >= '0' && ch <= '9')
	{
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * flag;
}

struct FHQ_TEAP{
	int root=0;//根节点编号
	int new_t(int x){//新建节点 
		e[++tot].size=1;
		e[tot].value=x;
		e[tot].key=rand();
		return tot;//返回编号 
	}
	void update(int x){//上传 
		e[x].size=e[e[x].lc].size+e[e[x].rc].size+1;
		return ;
	}
	void split(int x,int k,int &a,int &b){//按照 k 的值来分裂子数 
		if(!x){
			a=b=0;//赋值成 0 不然可能会变成一个奇奇怪怪的值 
			return ;
		}
		if(e[x].value<=k){//左边的都划分到左树,因为左子树的值肯定比k更小
			a=x;//左树的左节点化为它,接下来去右子树,因为剩下的树都比k大
			split(e[x].rc,k,e[x].rc,b); 
			update(x);
		}
		else{
			b=x;
			split(e[x].lc,k,a,e[x].lc);
			update(x);
		}
		return ;
	}
	int merge(int a,int b){//合并两颗子树 
		if(!a||!b)return a+b;
		if(e[a].key<e[b].key){//key值也要遵循小根堆的性质 
			e[a].rc=merge(e[a].rc,b);//因为往右递归,所以做左边肯定是符合但的
			update(a);
			return a;//当前节点以a为根 
		}
		else{
			e[b].lc=merge(a,e[b].lc);
			update(b);
			return b;
		}
	}
	void init(int k){//添加 k
		int a,b;//左树右数 
		split(root,k,a,b);
		root=merge(merge(a,new_t(k)),b);//合并三棵树 
		return ;
	}
	void outint(int k){//删除k
		int a,b,c;
		split(root,k,a,b);//以k为值划为两颗树
		split(a,k-1,a,c);//以k-1为值划为两颗树,c中即为值为k的数 
		c=merge(e[c].lc,e[c].rc);
		root=merge(merge(a,c),b);
		return ;
	}
	void build(int l,int r){
		for(int i=l;i<=r;i++){
			init(a[i]);//加入 
		}
	}
	int findrank(int k){//查询k的排名
		int a,b,ans;
		split(root,k-1,a,b);
		ans=e[a].size;
		root=merge(a,b);//合并回去
		return ans; 
	}
	int ask(int x,int k){//排名为k的数
		if(e[e[x].lc].size>=k)return ask(e[x].lc,k);//$!!!
		else if(e[e[x].lc].size+1==k)return e[x].value;//value即为排名为k的值 
		else return ask(e[x].rc,k-e[e[x].lc].size-1); 
	}
	int query_pre(int k){//查找前驱
		int a,b;
	//	cout<<2<<" "; 
		split(root,k-1,a,b);
		int ans;
		if(e[a].size==0){
			root=merge(a,b);
			return -2147483647;
		}
		ans=ask(a,e[a].size);
		root=merge(a,b);
		return ans;
	}
	int query_suf(int k){//查找后继 
		int a,b;
		split(root,k,a,b);
		int ans;
		if(e[b].size==0){
			root=merge(a,b);
			return 2147483647;
		}
		ans=ask(b,1);
		root=merge(a,b);
		return ans;
	}
}FT[N<<2];
struct XIANDUANSHU{
	void build(int rt,int l,int r){
		FT[rt].build(l,r);
		if(l!=r){//建树 
			int mid=(l+r)>>1;
			build(rt<<1,l,mid);
			build(rt<<1|1,mid+1,r);
		}
	}
	int find_rank(int rt,int l,int r,int L,int R,int k){//查询排名
		int ans=0;
		if(L<=l&&r<=R){//在查询区间
			ans=FT[rt].findrank(k);
			return ans;//又几个比它小 
		}
		int mid=(l+r)>>1;
		if(L<=mid)ans+=find_rank(rt<<1,l,mid,L,R,k);
		if(mid+1<=R)ans+=find_rank(rt<<1|1,mid+1,r,L,R,k);
		return ans;
	}
	int find_k(int l,int r,int rank){//找排名为k的数 
		int a=0,b=1e8,mid,sum,ans=-1;
		mid=(a+b)>>1;
		while(a<=b){//使用二分查找,因为数越大,排名越高 
			mid=(a+b)>>1;
			sum=find_rank(1,1,n,l,r,mid);
			if(sum+1<=rank){
				ans=mid;
				a=mid+1;//排名小了
			}
			else b=mid-1;
		}
		return ans;
	}
	void change(int rt,int l,int r,int x,int k){
		int mid=(l+r)>>1;
		FT[rt].outint(a[x]);//删除 x位置上的数
		FT[rt].init(k);//插入k
		if(l==r)return ;//到叶子节点了,结束 
		if(x<=mid)change(rt<<1,l,mid,x,k);//在左子树 
		else change(rt<<1|1,mid+1,r,x,k);
		return ; 
	}
	int qianqu(int rt,int l,int r,int L,int R,int k){
		int ans=-2147483647;
	//	cout<<1; 
		if(L<=l&&r<=R){
			return FT[rt].query_pre(k);
		}
		int mid=(l+r)>>1;
		if(l==r)return ans;
		if(L<=mid)ans=max(ans,qianqu(rt<<1,l,mid,L,R,k));//前驱取最大值 
		if(mid+1<=R)ans=max(qianqu(rt<<1|1,mid+1,r,L,R,k),ans);
		return ans;
	}
	int houji(int rt,int l,int r,int L,int R,int k){
		int ans=2147483647;
		if(L<=l&&r<=R){
			return FT[rt].query_suf(k);
		}
		int mid=(l+r)>>1;
		if(l==r)return ans;
		if(L<=mid)ans=min(ans,houji(rt<<1,l,mid,L,R,k));//后继取最小值 
		if(mid+1<=R)ans=min(houji(rt<<1|1,mid+1,r,L,R,k),ans);
		return ans;
	}
}ST;
int main(){
	n=read();
	m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	ST.build(1,1,n);//建树
	for(int i=1;i<=m;i++){
		int x,opt,l,r,k;
		opt=read();
		if(opt==1){//查询区间排名 
			l=read(),r=read(),k=read();
			printf("%d\n",ST.find_rank(1,1,n,l,r,k)+1);
		}
		if(opt==2){//区间排名为k的数 
			l=read(),r=read(),k=read();
			printf("%d\n",ST.find_k(l,r,k));
		}
		if(opt==3){//单点修改
			x=read(),k=read();
			ST.change(1,1,n,x,k);//将x点修改为k 
			a[x]=k;//@!!!!!!!!
		}
		if(opt==4){//查找前驱 
			l=read(),r=read(),k=read();
			printf("%d\n",ST.qianqu(1,1,n,l,r,k));
		}
		if(opt==5){
			l=read(),r=read(),k=read();
			printf("%d\n",ST.houji(1,1,n,l,r,k));
		}
	} 
	return 0;
}