動態主席樹(帶修改的區間第k大)(樹套樹)

動態主席樹(帶修改的區間第k大)(樹套樹)

基本思想

區間第k小的問題咱們能夠用靜態主席樹來維護,可是一些題目每每會增長修改操做,那麼咱們應該怎麼作呢,先看例題。node

給定一個含有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。
輸出格式:
對於每一次詢問,你都須要輸出他的答案,每個輸出佔單獨的一行。

這道題若是隻用靜態主席樹是不可能的,由於有修改操做,靜態主席樹是不能修改的,咱們就須要能支持修改的動態主席樹。 咱們想,單點修改,區間查詢,這不是樹狀數組最擅長的嗎。可是顯然,樹狀數組是不能維護這個東西的,因此咱們就須要套一個主席樹去維護,用一顆主席樹維護原序列的信息,再用主席樹維護一個樹狀數組(其實這個算是樹套樹了)去維護修改操做的信息。c++

實現方法

首先仍是離散化,但要注意,修改的值也須要離散化,由於修改的值和原值位置不一樣,因此這裏咱們選擇用指針對地址進行操做數組

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$。 建樹和維護本質上都是基於一顆原線段樹進行修改,因此可使用同一個函數進行操做。spa

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;
}
相關文章
相關標籤/搜索