前言
树套树是一个十分神奇的算法,种类也有很多:像什么树状数组套主席树、树状数组套值域线段树、\(zkw\)线段树套\(vector\)等等。
不过,像我这么弱,当然只会最经典的 线段树套\(Treap\) 啦。
线段树我相信大家都会的,\(Treap\)可以看一下这篇博客:简析平衡树(二)——Treap
基本思想
线段树套\(Treap\) 的思想其实非常简单。
简单的说,就是有一棵线段树,它的每一个节点都是一棵\(Treap\),每个\(Treap\)里面都存储了这个节点所表示的区间里的所有的元素。听起来都很强大。
基本操作
下面是一个很严肃的问题:线段树套\(Treap\)有什么用?
其实也很简单,差不多就是将\(Treap\)的询问操作前面全部加了区间二字。
具体如下:
-
查询一个元素\(val\)在区间内的排名
-
查询区间内排名为\(rk\)的元素
-
修改某一位的值
-
查询一个元素\(val\)在区间内的前驱
-
查询一个元素\(val\)在区间内的后继
然后你就能轻松\(A\)掉此题:【洛谷3380】【模板】二逼平衡树(树套树)。
操作的具体实现
接下来,我们来讲一讲上面提到的这些操作具体是如何实现的。
一、查询一个元素\(val\)在区间内的排名
我们可以在线段树上求出每一段需要查询的区间内小于\(val\)的元素的个数,然后将其累加,最后加上\(1\)(这个元素本身),就是这个元素在区间内的排名了。
单次操作时间复杂度:\(O(log^2N)\)【线段树上找区间:\(O(logN)\),\(Treap\)查询排名:\(O(logN)\)】。
二、查询区间内排名为\(rk\)的元素
这个操作相比其他操作就略有些复杂了。
直接查询过于麻烦,而且难以实现,所以我们可以二分这个元素,然后通过查询排名来判断这个元素大了还是小了即可。
单次操作时间复杂度:\(O(log^3N)\)【二分:\(O(logN)\),树套树查询排名:\(O(log^2N)\)】。
三、修改某一位的值
在线段树上找出每一个包含这一位的区间,在\(Treap\)中将原先的元素删去,然后加入新的元素即可。
单次操作时间复杂度:\(O(log^2N)\)【线段树上找区间:\(O(logN)\),\(Treap\)删除+插入:\(O(logN)\)】
四、查询一个元素\(val\)在区间内的前驱
我们可以在线段树上求出每一段需要查询的区间内\(val\)的前驱,然后取\(max\)即可。
单次操作时间复杂度:\(O(log^2N)\)【线段树上找区间:\(O(logN)\),\(Treap\)查询前驱:\(O(logN)\)】。
五、查询一个元素\(val\)在区间内的后继
与查询前驱类似,我们可以在线段树上求出每一段需要查询的区间内\(val\)的后继,然后取\(min\)即可。
单次操作时间复杂度:\(O(log^2N)\)【线段树上找区间:\(O(logN)\),\(Treap\)查询后继:\(O(logN)\)】。
代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 2147483647
#define Inc(x,y) ((x+=y)>=MOD&&(x-=MOD))
#define N 50000
using namespace std;
int n,tot=0,a[N+5];
struct Tree
{
int Son[2],Val,Cnt,Size,Data;
}node[N*30+5];
class FIO
{
private:
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
public:
FIO() {FinNow=FinEnd=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_char(char &x) {while(isspace(x=tc()));}
inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
inline void write_char(char x) {pc(x);}
inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class Class_Treap//Treap
{
private:
#define Rand() ((r*=233333LL)%=2147483647)
#define PushUp(x) (node[x].Size=node[node[x].Son[0]].Size+node[node[x].Son[1]].Size+node[x].Cnt)
#define Rotate(x,d) (k=node[x].Son[d^1],node[x].Son[d^1]=node[k].Son[d],node[k].Son[d]=x,x=k,PushUp(node[x].Son[d]),PushUp(x))
int rt,k;ull r;
inline int Build(int val) {node[++tot].Val=val,node[tot].Cnt=node[tot].Size=1,node[tot].Son[0]=node[tot].Son[1]=0,node[tot].Data=Rand();return tot;}
inline void ins(int &x,int val)
{
if(!x) return (void)(x=Build(val));
++node[x].Size;
if(node[x].Val==val) ++node[x].Cnt;
else if(node[x].Val>val) {ins(node[x].Son[0],val);if(node[x].Data<node[node[x].Son[0]].Data) Rotate(x,1);}
else {ins(node[x].Son[1],val);if(node[x].Data<node[node[x].Son[1]].Data) Rotate(x,0);}
PushUp(x);
}
inline void del(int &x,int val)
{
if(!x) return;
if(node[x].Val==val)
{
if(node[x].Cnt>1) return (void)(--node[x].Cnt,PushUp(x));
if(node[x].Son[0]||node[x].Son[1])
{
if(!node[x].Son[1]||node[node[x].Son[0]].Data>node[node[x].Son[1]].Data) Rotate(x,1),del(node[x].Son[1],val);
else Rotate(x,0),del(node[x].Son[0],val);
}
else x=0;
}
else if(node[x].Val>val) del(node[x].Son[0],val);
else del(node[x].Son[1],val);
PushUp(x);
}
public:
Class_Treap() {r=2333;}
inline bool empty() {return !rt;}
inline void Insert(int val) {ins(rt,val);}
inline void Delete(int val) {del(rt,val);}
inline int get_rank(int val)//与传统的get_rank()有点区别,这里的get_rank()求的是小于val的值的个数
{
register int x=rt,rk=0;
while(x)
{
if(node[x].Val==val) return node[node[x].Son[0]].Size+rk;
node[x].Val>val?x=node[x].Son[0]:(rk+=node[node[x].Son[0]].Size+node[x].Cnt,x=node[x].Son[1]);
}
return rk;
}
inline int get_val(int rk)
{
register int x=rt;
while(x)
{
if(node[node[x].Son[0]].Size>=rk) x=node[x].Son[0];
else if(node[node[x].Son[0]].Size+node[x].Cnt>=rk) return node[x].Val;
else rk-=node[node[x].Son[0]].Size+node[x].Cnt,x=node[x].Son[1];
}
}
inline int get_pre(int val)
{
register int x=rt,pre=-INF;
while(x) node[x].Val<val?(pre=node[x].Val,x=node[x].Son[1]):x=node[x].Son[0];
return pre;
}
inline int get_nxt(int val)
{
register int x=rt,nxt=INF;
while(x) node[x].Val>val?(nxt=node[x].Val,x=node[x].Son[0]):x=node[x].Son[1];
return nxt;
}
};
class Class_SegmentTree//线段树
{
private:
Class_Treap Treap[N<<2];//每个节点都是一棵Treap
inline void Build(int l,int r,int rt)//建树
{
register int i,mid=l+r>>1;
for(i=l;i<=r;++i) Treap[rt].Insert(a[i]);//将每个元素插入Treap中
if(l^r) Build(l,mid,rt<<1),Build(mid+1,r,rt<<1|1);
}
inline void Upt(int l,int r,int rt,int pos,int val)//修改
{
register int i,mid=l+r>>1;
if(l<=pos&&pos<=r) Treap[rt].Delete(a[pos]),Treap[rt].Insert(val);//删除原来的元素,然后插入新元素
if(l^r) pos<=mid?Upt(l,mid,rt<<1,pos,val):Upt(mid+1,r,rt<<1|1,pos,val);
}
inline int get_rank(int l,int r,int rt,int ql,int qr,int val)//求区间排名
{
register int i,res=0,mid=l+r>>1;
if(ql<=l&&r<=qr) return Treap[rt].get_rank(val);
if(ql<=mid) res+=get_rank(l,mid,rt<<1,ql,qr,val);//累加答案
if(mid<qr) res+=get_rank(mid+1,r,rt<<1|1,ql,qr,val);
return res;
}
inline int get_pre(int l,int r,int rt,int ql,int qr,int val)//求前驱
{
register int i,res=-INF,t,mid=l+r>>1;
if(ql<=l&&r<=qr) return Treap[rt].get_pre(val);
if(ql<=mid) t=get_pre(l,mid,rt<<1,ql,qr,val),res=max(res,t);//取max
if(mid<qr) t=get_pre(mid+1,r,rt<<1|1,ql,qr,val),res=max(res,t);
return max(res,t);
}
inline int get_nxt(int l,int r,int rt,int ql,int qr,int val)//求后继
{
register int i,res=INF,t,mid=l+r>>1;
if(ql<=l&&r<=qr) return Treap[rt].get_nxt(val);
if(ql<=mid) t=get_nxt(l,mid,rt<<1,ql,qr,val),res=min(res,t);//取min
if(mid<qr) t=get_nxt(mid+1,r,rt<<1|1,ql,qr,val),res=min(res,t);
return min(res,t);
}
public:
inline void Init() {Build(1,n,1);}
inline void Update(int pos,int val) {Upt(1,n,1,pos,val),a[pos]=val;}
inline int GetRank(int ql,int qr,int val) {return get_rank(1,n,1,ql,qr,val)+1;}//注意将答案加1(该元素本身)
inline int GetVal(int ql,int qr,int rk) {register int l,r,mid;for(mid=(l=0)+(r=1e8)+1>>1;l<r;mid=l+r+1>>1) GetRank(ql,qr,mid)<=rk?l=mid:r=mid-1;return l;}//二分答案
inline int GetPre(int ql,int qr,int val) {return get_pre(1,n,1,ql,qr,val);}
inline int GetNxt(int ql,int qr,int val) {return get_nxt(1,n,1,ql,qr,val);}
}SegmentTree;
int main()
{
register int i,Q,op,x,y,z,l,r,mid;
for(F.read(n),F.read(Q),i=1;i<=n;++i) F.read(a[i]);
for(SegmentTree.Init();Q;--Q)
{
F.read(op),F.read(x),F.read(y);
if(op^3) F.read(z);
switch(op)
{
case 1:F.write(SegmentTree.GetRank(x,y,z)),F.write_char('\n');break;
case 2:F.write(SegmentTree.GetVal(x,y,z)),F.write_char('\n');break;
case 3:SegmentTree.Update(x,y);break;
case 4:F.write(SegmentTree.GetPre(x,y,z)),F.write_char('\n');break;
case 5:F.write(SegmentTree.GetNxt(x,y,z)),F.write_char('\n');break;
}
}
return F.end(),0;
}