树套树Day1线段树套平衡树bzoj3196
您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
应xgy的邀来码树套树了...今天或许能码完这一篇吧...还在发烧,动态区间第k大(权值线段树套线段树or树状数组套主席树)估计码不完了
所以正好分成几天来写,写的细一点
这种题一般很明显...就是又有平衡树性质又有线段树性质应该就是线段树套平衡树了吧
顾名思义,线段树套平衡树,就是对于线段树的每一个点开一个平衡树,利用平衡树的灵活性和线段树对区间处理的强大来解决问题
简单的来说,原来线段树每个点是一个区间,你用平衡树维护每个区间,最后的得到的就是线段树套平衡树
怎么样,理论非常简单吧 然而写起来难的一匹我会说?
来看一下这道题
OPT1:线段树常规查询区间,每一次统计小于k的点的个数再相加。
OPT2:这个是最麻烦也是最精妙的一问,解决方法是二分答案,每二分到一个答案查询一下这个答案在这个区间内的排名,如果排名等于k+1的话返回它的pre即可。注意这里二分满足条件之后不用查询pre,答案直接为left-1,可以证明left-1一定在序列中。
OPT3:相当于线段树的点修改,在平衡树中删除再插入即可。
OPT4:线段树常规查询区间,每一次找区间内比k小的最大的数,然后取max
OPT5:类似于OPT4,每一次找区间内比k大的最小的数,然后取min
大概就是这样了吧- -#
懒得写SBT,拿splay卡时限过的
大家写SBT或者Treap都是极好的,千万不要学我
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<iostream> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxn=4e6+233; const int inf=2147483233; int ans; int a[maxn]; int n,m; struct Seg_Tao_Splay//原代码是分开写的...但是太丑了 { int fa[maxn],size[maxn],son[maxn][2],key[maxn],rt[maxn],cnt[maxn],Size; inline int gt(int x){return son[fa[x]][1]==x;} inline void pushup(int x){size[x]=cnt[x]+size[son[x][1]]+size[son[x][0]];} inline void sclear(int x){fa[x]=son[x][0]=son[x][1]=size[x]=cnt[x]=key[x]=0;} inline void rotate(int x) { int f1=fa[x],f2=fa[f1],wt=gt(x); son[f1][wt]=son[x][wt^1]; fa[son[f1][wt]]=f1; son[x][wt^1]=f1; fa[f1]=x; if (f2) son[f2][son[f2][1]==f1]=x; fa[x]=f2; pushup(f1); pushup(x); } inline void Splay(int x) { for(int faf;faf=fa[x];rotate(x)) if(fa[faf]) rotate((gt(x)==gt(faf))?faf:x); } inline void sinsert(int i,int x)//在Splay里加点 { int now=rt[i],faf=0; if (!rt[i]) { rt[i]=++Size; fa[Size]=son[Size][0]=son[Size][1]=0; size[Size]=cnt[Size]=1; key[Size]=x; return; } while("woxihuankeduoli") { if (x==key[now]) { cnt[now]++; pushup(faf); Splay(now); rt[i]=now; return; } faf=now; now=son[now][key[now]<x]; if(!now) { ++Size; fa[Size]=faf; son[Size][0]=son[Size][1]=0; size[Size]=cnt[Size]=1; key[Size]=x; son[faf][key[faf]<x]=Size; pushup(faf); Splay(Size); rt[i]=Size; return; } } } inline void sfind(int i,int x) { int now=rt[i]; while (1){ if (key[now]==x) { Splay(now); rt[i]=now; return; } else if (key[now]>x) now=son[now][0]; else if (key[now]<x) now=son[now][1]; } } inline int spre(int i) { int now=son[rt[i]][0]; while (son[now][1]) now=son[now][1]; return now; } inline int snext(int i) { int now=son[rt[i]][1]; while (son[now][0]) now=son[now][0]; return now; } inline void sdel(int i) { int now=rt[i]; if (cnt[now]>1) { cnt[now]--; pushup(now); return; } if (!son[now][0]&&!son[now][1]) { sclear(rt[i]); rt[i]=0; return; } if (!son[now][0]) { int prert=now; rt[i]=son[prert][1]; fa[rt[i]]=0; sclear(prert); return; } if (!son[now][1]) { int prert=now; rt[i]=son[prert][0]; fa[rt[i]]=0; sclear(prert); return; } int leftM=spre(i),prert=rt[i]; Splay(leftM); rt[i]=leftM; son[rt[i]][1]=son[prert][1]; fa[son[prert][1]]=rt[i]; sclear(prert); pushup(rt[i]); return; } inline int sfindrank(int i,int x) { int now=rt[i],ans=0; while (1) { if (!now) return ans; if (key[now]==x) return ((son[now][0])?size[son[now][0]]:0)+ans; else if (key[now]<x) { ans+=((son[now][0])?size[son[now][0]]:0)+cnt[now]; now=son[now][1]; } else if (key[now]>x) now=son[now][0]; } } inline int sfindpre(int i,int k) { int now=rt[i]; while (now) { if (key[now]<k) { if (ans<key[now])ans=key[now]; now=son[now][1]; } else now=son[now][0]; } return ans; } inline int sfindnext(int i,int k) { int now=rt[i]; while (now) { if (key[now]>k) { if (ans>key[now]) ans=key[now]; now=son[now][0]; } else now=son[now][1]; } return ans; } //以上为splay操作 下面是线段树操作 inline void insertSeg(int id,int l,int r,int x,int v) { int mid=(l+r)>>1; sinsert(id,v); if (l==r) return; if (x<=mid) insertSeg(id<<1,l,mid,x,v); else insertSeg(id<<1|1,mid+1,r,x,v); } inline void askrankSeg(int id,int l,int r,int lrange,int rrange,int k) { int mid=(l+r)>>1; if (lrange<=l&&r<=rrange) { ans+=sfindrank(id,k); return; } if (lrange<=mid) askrankSeg(id<<1,l,mid,lrange,rrange,k); if (mid+1<=rrange) askrankSeg(id<<1|1,mid+1,r,lrange,rrange,k); } inline void changeSeg(int id,int l,int r,int x,int k) { int mid=(l+r)>>1; sfind(id,a[x]); sdel(id); sinsert(id,k); if (l==r) return; if (x<=mid) changeSeg(id<<1,l,mid,x,k); else changeSeg(id<<1|1,mid+1,r,x,k); } inline void askpreSeg(int id,int l,int r,int lrange,int rrange,int k) { int mid=(l+r)>>1; if (lrange<=l&&r<=rrange) { ans=max(ans,sfindpre(id,k)); return; } if (lrange<=mid) askpreSeg(id<<1,l,mid,lrange,rrange,k); if (mid+1<=rrange) askpreSeg(id<<1|1,mid+1,r,lrange,rrange,k); } inline void asknextSeg(int id,int l,int r,int lrange,int rrange,int k) { int mid=(l+r)>>1; if (lrange<=l&&r<=rrange) { ans=min(ans,sfindnext(id,k)); return; } if (lrange<=mid) asknextSeg(id<<1,l,mid,lrange,rrange,k); if (mid+1<=rrange) asknextSeg(id<<1|1,mid+1,r,lrange,rrange,k); } }Tree; int _max=-2147483233; int opt,l,r,k,le,ri,md; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){scanf("%d",&a[i]);_max=max(_max,a[i]);Tree.insertSeg(1,1,n,i,a[i]);} for(int i=1;i<=m;i++) { scanf("%d",&opt); if(opt==1) { scanf("%d%d%d",&l,&r,&k); ans=0; Tree.askrankSeg(1,1,n,l,r,k); printf("%d\n",ans+1); } if(opt==2) { scanf("%d%d%d",&l,&r,&k); le=0,ri=_max+1; while(le!=ri) { md=(le+ri)>>1; ans=0; Tree.askrankSeg(1,1,n,l,r,md); if(ans<k)le=md+1; else ri=md; } printf("%d\n",le-1); } if(opt==3) { scanf("%d%d",&l,&k); Tree.changeSeg(1,1,n,l,k); a[l]=k; _max=max(_max,k); } if(opt==4) { scanf("%d%d%d",&l,&r,&k);ans=-2147483233; Tree.askpreSeg(1,1,n,l,r,k); printf("%d\n",ans); } if(opt==5) { scanf("%d%d%d",&l,&r,&k);ans=2147483233; Tree.asknextSeg(1,1,n,l,r,k); printf("%d\n",ans); } } }
线段树套平衡树很简单(思想层面)但它是我们学习树套树的基础,建议仔细学习一个,然后做以下例题。
这个东西练熟了,我们就可以更好的了解其他形式的树套树,我们还可以更好的形成"树套树"的思想
这个思想有助于我们写出很多数据结构毒瘤题的正解/暴力
例题:
bzoj2141 排队 注:此题线段树套平衡树略难,可以参考网上的分块套树状数组做法
bzoj1901 ZOJ2112 Dynamic Rankings 注:此题也叫“可持久化主席树”所以绝对有树状数组套主席树的做法
bzoj2120 数颜色 注:正解带修改莫队
题解我会慢慢写,毕竟树套树写一个也不是那么简单啊QAQ