树套树学习笔记

直接正文

不得不说,这玩意是真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; }

__EOF__

本文作者shAdom
本文链接https://www.cnblogs.com/shadom/p/17614728.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   shAdomOvO  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示