zoukankan      html  css  js  c++  java
  • 动态主席树(带修改的区间第k大)(树套树)

    动态主席树(带修改的区间第k大)(树套树)

    基本思想

    区间第k小的问题我们可以用静态主席树来维护,但是一些题目往往会增加修改操作,那么我们应该怎么做呢,先看例题。

    给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。
    
    输入格式:
    第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。
    第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t
    Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。
    C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。
    输出格式:
    对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。
    

    这道题如果只用静态主席树是不可能的,因为有修改操作,静态主席树是不能修改的,我们就需要能支持修改的动态主席树。
    我们想,单点修改,区间查询,这不是树状数组最擅长的吗。但是显然,树状数组是不能维护这个东西的,所以我们就需要套一个主席树去维护,用一颗主席树维护原序列的信息,再用主席树维护一个树状数组(其实这个算是树套树了)去维护修改操作的信息。

    实现方法

    首先还是离散化,但要注意,修改的值也需要离散化,因为修改的值和原值位置不同,所以这里我们选择用指针对地址进行操作

        void work(){
            sort(dis+1,dis+num+1,cmp);p[0]=-1;/*dis表示一个指针变量的数组,num表示需要离散的数据的数量*/
            for(int i=1,j=0;i<=num;++i)
            {
                if(*dis[i]!=p[j])p[++j]=*dis[i];
                *dis[i]=j;
            }
        }
    

    构建

    维护原序列的主席树跟静态主席树一样,直接复制过来也可以,但为了防止打挂,还是重打一遍更好。而维护树状数组的主席树其实也差不多

    修改

    用树状数组维护一个区间修改的信息,每一个节点的范围跟树状数组没有区别,但我们需要用主席树去维护这个树状数组,树状数组的每一个节点都是一颗值域线段树,保存树状树状每一个节点的根,每次修改就对树状数组包含这个元素的节点进行修改,每次修改都相当于删除一个元素再插入一个元素,每次维护都要基于这个节点的原本信息进行修改(+1或-1)。由于每次都要维护log个节点,每个节点要新增(log)个节点所以时空复杂度均为(log^2)
    建树和维护本质上都是基于一颗原线段树进行修改,所以可以使用同一个函数进行操作。

        int modify(int l,int r,int x,int k,int o){
            int y=++cnt;
            t[y]=t[x];t[y].x+=o;
            if(l==r)return y;
            int mid=(l+r)>>1;
            if(k<=mid)t[y].l=modify(l,mid,t[x].l,k,o);
            else t[y].r=modify(mid+1,r,t[x].r,k,o);
            return y;
        }
    

    查询

    查询时,我们既要查询原序列的信息,又要查询修改信息,所以我们需要把树状数组需要查询的节点全部储存到一个数组里,再进行查询,原序列的查询方式跟静态主席树一样,树状数组的查询就与树状数组的区间查询一样,只不过把每次访问节点改为这个节点代表的值域线段树,注意,每一次查询的所有有关信息的访问必须同时进行

    int query(int l,int r,int s1,int s2,int k){
        if(l==r)return l;
        int x=t[t[s2].l].x-t[t[s1].l].x;
        for(int i=1;i<=tot1;++i)x-=t[t[q1[i]].l].x;
        for(int i=1;i<=tot2;++i)x+=t[t[q2[i]].l].x;
        int mid=(l+r)>>1;
        if(x>=k)
        {
            for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].l;
            for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].l;
            return query(l,mid,t[s1].l,t[s2].l,k);
        }
        else
        {
            for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].r;
            for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].r;
            return query(mid+1,r,t[s1].r,t[s2].r,k-x);
        }
    }
    

    代码

    #include<bits/stdc++.h>
    using namespace std;
    inline int gi(){
        char a=getchar();int b=0;
        while(a<'0'||a>'9')a=getchar();
        while(a>='0'&&a<='9')b=b*10+a-'0',a=getchar();
        return b;
    }
    const int N=1e4+50;
    struct node  {int l,r,x;}  t[N*900];
    struct ppp  {int l,r,op,k;}  b[N];
    int cmp(int* x,int* y)  {return *x<*y;}
    int a[N],p[N*5],n,m,tot1,tot2,lshh,cnt=1,root[N],root1[N],q1[N],q2[N];   int *lsh[N*5];
    void work(){
        sort(lsh+1,lsh+lshh+1,cmp);p[0]=-1;
        for(int i=1,j=0;i<=lshh;++i)
        {
            if(*lsh[i]!=p[j])p[++j]=*lsh[i];
            *lsh[i]=j;
        }
    }
    int modify(int l,int r,int x,int k,int o){
        int y=++cnt;
        t[y]=t[x];t[y].x+=o;
        if(l==r)return y;
        int mid=(l+r)>>1;
        if(k<=mid)t[y].l=modify(l,mid,t[x].l,k,o);
        else t[y].r=modify(mid+1,r,t[x].r,k,o);
        return y;
    }
    int query(int l,int r,int s1,int s2,int k){
        if(l==r)return l;
        int x=t[t[s2].l].x-t[t[s1].l].x;
        for(int i=1;i<=tot1;++i)x-=t[t[q1[i]].l].x;
        for(int i=1;i<=tot2;++i)x+=t[t[q2[i]].l].x;
        int mid=(l+r)>>1;
        if(x>=k)
        {
            for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].l;
            for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].l;
            return query(l,mid,t[s1].l,t[s2].l,k);
        }
        else
        {
            for(int i=1;i<=tot1;++i)q1[i]=t[q1[i]].r;
            for(int i=1;i<=tot2;++i)q2[i]=t[q2[i]].r;
            return query(mid+1,r,t[s1].r,t[s2].r,k-x);
        }
    }
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;++i)
        {
            a[i]=gi();
            lsh[++lshh]=&a[i];
        }
        for(int i=1;i<=m;++i)
        {
            char aa=getchar();
            while(!(aa=='Q'||aa=='C'))aa=getchar();
            b[i].l=gi();
            b[i].r=gi();
            if(aa=='C')
            {
                b[i].op=1;
                lsh[++lshh]=&b[i].r;
            }
            else b[i].k=gi();
        }
        work();
        for(int i=1;i<=n;++i)
            root1[i]=root[1];
        for(int i=1;i<=n;++i)
            root[i]=modify(1,lshh,root[i-1],a[i],1);
        for(int i=1;i<=m;++i)
            if(b[i].op)
            {
                int x=b[i].l,y=b[i].r,s=a[x];a[x]=y;
                while(x<=n)
                {
                    root1[x]=modify(1,lshh,root1[x],s,-1);
                    root1[x]=modify(1,lshh,root1[x],y,1);
                    x+=(x&(-x));
                }
            }
            else
            {
                tot1=0,tot2=0;int x=b[i].l-1;
                while(x){q1[++tot1]=root1[x];x-=(x&(-x));}x=b[i].r;
                while(x){q2[++tot2]=root1[x];x-=(x&(-x));}
                printf("%d
    ",p[query(1,lshh,root[b[i].l-1],root[b[i].r],b[i].k)]);
            }
            return 0;
    }
    
  • 相关阅读:
    安装VMtools vim编辑器的使用 压缩包命令 Linux下的用户管理 (第三天)
    VM虚拟机安装 常用Linux命令 网卡配置 (第二天)
    数据库的交互模式 常用的dos命令 (第一天)
    Validate US Telephone Numbers FreeCodeCamp
    Arguments Optional FreeCodeCamp
    Everything Be True FreeCodeCamp
    Binary Agents FreeCodeCamp
    Steamroller FreeCodeCamp
    Drop it FreeCodeCamp
    Smallest Common Multiple FreeCodeCamp
  • 原文地址:https://www.cnblogs.com/ljq-despair/p/8639394.html
Copyright © 2011-2022 走看看