动态主席树(带修改的区间第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;
}
原文地址:https://www.cnblogs.com/ljq-despair/p/8639394.html