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

 

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

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

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

例题:

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

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

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

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

posted @ 2017-12-11 19:26  探险家Mr.H  阅读(383)  评论(2编辑  收藏  举报