把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

初学树套树:线段树套Treap

前言

树套树是一个十分神奇的算法,种类也有很多:像什么树状数组主席树树状数组值域线段树\(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;
}
posted @ 2018-10-28 15:34  TheLostWeak  阅读(432)  评论(0编辑  收藏  举报