• 树套树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都是极好的,千万不要学我

    #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
    ",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
    ",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
    ",ans);
            }
            if(opt==5)
            {
                scanf("%d%d%d",&l,&r,&k);ans=2147483233;
                Tree.asknextSeg(1,1,n,l,r,k);
                printf("%d
    ",ans);
            }
        }
    }
    View Code

    线段树套平衡树很简单(思想层面)但它是我们学习树套树的基础,建议仔细学习一个,然后做以下例题。

    这个东西练熟了,我们就可以更好的了解其他形式的树套树,我们还可以更好的形成"树套树"的思想

    这个思想有助于我们写出很多数据结构毒瘤题的正解/暴力

    例题:

    bzoj2141  排队   注:此题线段树套平衡树略难,可以参考网上的分块套树状数组做法

    bzoj1901  ZOJ2112 Dynamic Rankings    注:此题也叫“可持久化主席树”所以绝对有树状数组套主席树的做法

    bzoj2120  数颜色   注:正解带修改莫队

    题解我会慢慢写,毕竟树套树写一个也不是那么简单啊QAQ

  • 相关阅读:
    Ubantu 查看系统资源占用
    C do {...} while (0) 在宏定义中的作用
    Redis架构设计
    Ubantu 安装boost环境
    Ubuntu 安装谷歌拼音输入法
    Ubuntu C/C++开发环境的安装和配置
    ubuntu 14.04英文环境设置成中文
    自己动手写可视化软件(代码已开源)
    探秘Tomcat——连接篇
    探秘Tomcat——连接器和容器的优雅启动
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/8024685.html
Copyright © 2020-2023  润新知