树套树学习笔记
直接正文
不得不说,这玩意是真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;
}