• 初学树套树:线段树套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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    JavaScript Array filter() 方法
    Object.assign方法的使用入门
    使用ES6新特性async await进行异步处理
    win10系统怎么设置软件开机启动
    【ES6学习笔记之】Object.assign()高级编程
    如何使用闭包形成计数器
    多次调用settimeout 如何使用单例模式
    在线表单设计器现在已经开源
    VisualStudio2017集成GitHub
    PHP使用curl替代file_get_contents
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/SegmentTreap.html
Copyright © 2020-2023  润新知