動態主席樹(帶修改的區間第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\n",p[query(1,lshh,root[b[i].l-1],root[b[i].r],b[i].k)]);
        }
        return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM