• [学习笔记]树套树


    说白了,就是在一个树形数据结构上,每个点不再是一个节点,而是另外一个树形数据结构。

    空间时间复杂度大多数都是O(nlogn)

    线段树套平衡树

    许多树套树都可以用线段树套平衡树解决。

    空间O(nlogn)是很可观的。

    各种区间找值的问题,可以游刃有余解决。

    (虽然常数比较大)

    例如模板:

    【模板】二逼平衡树(树套树)

    下标线段树里套权值平衡树

    线段树节点O(4*N)

    平衡树节点O(NlogN)

    对于第k大,外面二分一下,然后查比mid小的数的个数,如果<k那么可以。O(log^3n)可过。

    代码:

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    #define mid ((l+r)>>1)
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=5*1e4+5;
    const int inf=2147483647;
    int rt[4*N];
    struct node{
        int ch[2];
        int fa;
        int sum,sz;
        int val;
    }t[N*20];
    int cur;//id
    int dp[233],dc;
    int n,m;
    int a[N];
    int nc(int v,int f){
        int r=dc?dp[dc--]:++cur;
        t[r].val=v;t[r].fa=f;t[r].sz=t[r].sum=1;
        t[r].ch[0]=t[r].ch[1]=0;
        return r;
    }
    void dele(int o){
        dp[++dc]=o;
    }
    void pushup(int x){
        if(x==0) return;
        t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+t[x].sum;
    }
    void rotate(int x){
        int y=t[x].fa,d=t[y].ch[1]==x;
        t[t[y].ch[d]=t[x].ch[!d]].fa=y;
        t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x;
        t[t[x].ch[!d]=y].fa=x;
        pushup(y);
    }
    void splay(int o,int x,int f){
        while(t[x].fa!=f){
            int y=t[x].fa,z=t[y].fa;
            if(z!=f){
                rotate((t[y].ch[0]==x)==(t[z].ch[0]==y)?y:x);
            }
            rotate(x);
        }
        if(f==0) rt[o]=x;
        pushup(x);
    }
    void ins(int o,int v){//warning!! maybe no rt
        if(!rt[o]){
            rt[o]=nc(v,0);
            return;
        }
        int x=rt[o];
        while(1){
            t[x].sz++;
            if(t[x].val==v){
                t[x].sum++;break;
            }
            int d=t[x].val<v;
            if(!t[x].ch[d]){
                //cout<<" nc "<<endl;
                t[x].ch[d]=nc(v,x);
                x=t[x].ch[d];
                break;
            }
            x=t[x].ch[d];
        }
        splay(o,x,0);
    }
    void build(int o,int l,int r){
        //cout<<o<<" : "<<l<<" "<<r<<" "<<rt[o]<<endl;
        for(reg i=l;i<=r;++i){
        //    cout<<"ins "<<i<<" "<<a[i]<<endl;
            ins(o,a[i]);
        }
    }
    void remove(int o,int v){//warning!! maybe no rt
        int x=rt[o];
        while(t[x].val!=v){
            int d=t[x].val<v;
            x=t[x].ch[d];
        }
        splay(o,x,0);
        t[x].sum--;
        t[x].sz--;
        if(t[x].sum) return;
        dele(x);
        int son=t[x].ch[1];
        if(!son){
            rt[o]=t[x].ch[0];
            t[rt[o]].fa=0;
            return;
        }
        else{
            while(t[son].ch[0]) son=t[son].ch[0];
            splay(o,son,x);
            t[t[son].ch[0]=t[x].ch[0]].fa=son;
            t[son].fa=0;
            pushup(son);
            rt[o]=son;
        }
    }
    int pre(int o,int c){
        int x=rt[o];
        int ret=-inf;
        while(x){
            if(t[x].val<c) ret=max(ret,t[x].val);
            if(t[x].val<c){
                x=t[x].ch[1];
            }
            else x=t[x].ch[0];
        }
        return ret;
    }
    int bac(int o,int c){
        int x=rt[o];
        int ret=inf;
        //cout<<" oooooo "<<o<<endl;
        while(x){
            if(t[x].val>c) ret=min(ret,t[x].val);
            if(t[x].val>c){
                x=t[x].ch[0];
            }
            else x=t[x].ch[1];
        }
        //cout<<" ret "<<ret<<endl;
        return ret;
    }
    int rk(int o,int c){//±ÈcСµÄÊýµÄ¸öÊý 
        int x=rt[o];
        int ret=0;
        int cnt=0;
        while(x){
            ++cnt;
            //if(cnt<=15) cout<<"xx "<<x<<endl;
            if(t[x].val<c){
                ret+=t[x].sum+t[t[x].ch[0]].sz;
                x=t[x].ch[1];
            }
            else if(t[x].val==c){
                ret+=t[t[x].ch[0]].sz;return ret;
            }
            else if(t[x].val>c){
                x=t[x].ch[0];
            }
        }
        return ret;
    }
    void jian(int x,int l,int r){
        if(l==r){
            build(x,l,r);return;
        }
        build(x,l,r);
        jian(x<<1,l,mid);
        jian(x<<1|1,mid+1,r);
    }
    int pai(int x,int l,int r,int L,int R,int c){
        if(L<=l&r<=R){
            return rk(x,c);
        }
        int ret=0;
        if(L<=mid) ret+=pai(x<<1,l,mid,L,R,c);
        if(mid<R) ret+=pai(x<<1|1,mid+1,r,L,R,c);
        return ret;
    }
    void chan(int x,int l,int r,int to,int b,int c){
        if(l==r){
            remove(x,b);ins(x,c);
            return;
        }
        remove(x,b);ins(x,c);
        if(to<=mid) chan(x<<1,l,mid,to,b,c);
        else chan(x<<1|1,mid+1,r,to,b,c);
    }
    int qian(int x,int l,int r,int L,int R,int c){
        if(L<=l&&r<=R){
            return pre(x,c);
        }
        int ret=-inf;
        if(L<=mid) ret=max(ret,qian(x<<1,l,mid,L,R,c));
        if(mid<R) ret=max(ret,qian(x<<1|1,mid+1,r,L,R,c));
        return ret;
    }
    int hou(int x,int l,int r,int L,int R,int c){
        //cout<<" x "<<x<<" l "<<l<<" r "<<r<<endl;
        if(L<=l&&r<=R){
            return bac(x,c);
        }
        int ret=inf;
        if(L<=mid) ret=min(ret,hou(x<<1,l,mid,L,R,c));
        if(mid<R) ret=min(ret,hou(x<<1|1,mid+1,r,L,R,c));
        return ret;
    }
    int kth(int l,int r,int k){
        //cout<<" l r k "<<l<<" "<<r<<" "<<k<<endl;
        int L=0,R=1e8;
        int ret=0;
        while(L<=R){
            int M=(L+R)/2;
            //cout<<L<<" "<<R<<" "<<M<<endl;
            int tmp=pai(1,1,n,l,r,M);
            if(tmp<k) ret=M,L=M+1;
            else R=M-1;
        }
        return ret;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(reg i=1;i<=n;++i)rd(a[i]);
        jian(1,1,n);
    //    cout<<" tot "<<cur<<endl;
    //    for(reg i=1;i<=cur;++i){
    //        cout<<i<<" : "<<t[i].ch[0]<<" "<<t[i].ch[1]<<" "<<t[i].val<<" "<<t[i].fa<<endl;
    //    }
        int op,l,r,p,k;
        while(m--){
            rd(op);
            switch(op){
                case 1:rd(l);rd(r);rd(k);printf("%d
    ",pai(1,1,n,l,r,k)+1);break;//warning!!! +1
                case 2:rd(l);rd(r);rd(k);printf("%d
    ",kth(l,r,k));break;
                case 3:rd(p);rd(k);chan(1,1,n,p,a[p],k);a[p]=k;break;
                case 4:rd(l);rd(r);rd(k);printf("%d
    ",qian(1,1,n,l,r,k));break;
                case 5:rd(l);rd(r);rd(k);printf("%d
    ",hou(1,1,n,l,r,k));break;
            }
        }
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/22 20:22:06
    */
    二逼平衡树

    [国家集训队]排队

    动态逆序对。

    看下面加强版。

    [TJOI2017]不勤劳的图书管理员

    类似动态逆序对。

    考虑(l+1,r-1)区间内的影响。

    要统计小于(大于)a[x](a[y])的个数以及总和。

    线段树套平衡树轻而易举。空间O(2*N+NlogN)

    线段树套动态开点权值线段树也可以。空间O(2*N+Nlog^2N)

     
    线段树套线段树

    一般必然有一个要动态开点。

    空间不太优秀:O(nlogn^2)

    (但是常数小一些,而且好写)

    [HEOI2016/TJOI2016]序列

    列出DP式子,发现是三维偏序。

    (所以果断CDQ优化DP)

    树套树做的话,可以下标线段树套动态开点权值线段树。节点维护dp值最大值

    (当然,万能的线段树套平衡树也可以)

  • 相关阅读:
    简单的描述关于开发部署产生401,500的错误处理
    文件的批量打包下载
    json的序列化与反序列化
    实现MD5的加密和解密
    dropdownlist的OnSelectedIndexChanged方法不触发
    sqlserver错误2,error 40
    C#存储过程调用的三个方法
    SQL Server 错误:924 解决方法
    判断是否在时间间隔内
    切面添加日志
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10008700.html
Copyright © 2020-2023  润新知