主席樹經典區間第k大模板(不帶修改&&帶修改)

此模板作用爲經典的查詢區間第k大

不帶修改:複雜度O(nlogn)
帶修改:複雜度O(n∗(logn)^2)

不帶修改的主席樹就是在前一棵樹的基礎上重建其中一條鏈,其餘的鏈都和原來的公用,相當於logn的複雜度新建一顆線段樹

代碼如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+5;
const int maxm=8e5+5;
int T[maxn],ls[maxm],rs[maxm],sum[maxm];
int a[maxn],b[maxn];
int n,m,cnt;
int build(int l,int r)
{
    int now=++cnt;
    if(l<r)
    {
        int mid=l+r>>1;
        ls[now]=build(l,mid);
        rs[now]=build(mid+1,r);
    }
    return now;
}
void build_new(int id,int pos)
{
    T[id]=++cnt;
    sum[cnt]=sum[T[id-1]]+1;
    int l=1,r=m,now=cnt,pre=T[id-1];
    while(ls[pre]||rs[pre])
    {
        int mid=l+r>>1;
        if(pos>mid)
        {
            ls[now]=ls[pre];
            rs[now]=++cnt;
            sum[rs[now]]=sum[rs[pre]]+1;
            now=rs[now];
            pre=rs[pre];
            l=mid+1;
        }
        else
        {
            rs[now]=rs[pre];
            ls[now]=++cnt;
            sum[ls[now]]=sum[ls[pre]]+1;
            now=ls[now];
            pre=ls[pre];
            r=mid;
        }
    }
}
int query(int a,int b,int l,int r,int k)
{
    if(l==r) return l;
    int all=sum[ls[b]]-sum[ls[a]];
    int mid=l+r>>1;
    if(k<=all)
        return query(ls[a],ls[b],l,mid,k);
    return query(rs[a],rs[b],mid+1,r,k-all);
}
int main()
{
    int q;
    cin>>n>>q;
    for(int i=1;i<=n;i++)
        cin>>a[i],b[i]=a[i];
    sort(b+1,b+n+1);
    m=unique(b+1,b+n+1)-b-1;
    T[0]=build(1,m);
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(b+1,b+m+1,a[i])-b;
        build_new(i,pos);
    }
    for(int i=1;i<=q;i++)
    {
        int x,y,k;
        cin>>x>>y>>k;
        cout<<b[query(T[x-1],T[y],1,m,k)]<<endl;
    }
    return 0;
}

 

待修改的主席樹需要利用樹狀數組維護更新信息

爲什麼要用樹狀數組來維護呢,因爲用樹狀數組可以在logn的複雜度下重組出任意一點的修改信息

乍聽起來比較複雜,實際上原理非常簡單,只是實現較爲麻煩

當出現更新操作時,顯然我們不能把所有的樹都重新構建一遍,那麼對於更新, 我們不修改這些已經建好的樹, 而去新建一批樹S,用來記錄更新,而這批線段樹,我們用樹狀數組來維護,僅需要logn的複雜度即可完成查詢

劃重點:此樹狀數組的每個節點都是一顆線段樹

對於每個點的詢問,其真實值爲,即原來的值加上S樹層層疊加得到的值

具體來說,對於k位置的詢問,其真實值就是原來樹k位置的值加上所有相關S樹的k位置的值的總和

代碼如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+5;
const int maxm=8e5+5;
int n,m,cnt;
struct que
{
    char s[3];
    int l,r,k;
}Q[maxn];
int T[maxn],S[maxn],ls[maxm],rs[maxm],sum[maxm];
int a[maxn],b[maxn];
int use[maxn][2];
int lowbit(int x)
{
    return x&-x;
}
int build(int l,int r)
{
    int now=++cnt;
    if(l<r)
    {
        int mid=l+r>>1;
        ls[now]=build(l,mid);
        rs[now]=build(mid+1,r);
    }
    return now;
}
int build_new(int id,int pos,int op,int v)
{
    int root=++cnt;
    int pre;
    if(id==1)
        pre=T[0];
    else pre=op?S[id-1]:T[id-1];
    if(op&&v) pre=S[id];
    sum[root]=sum[pre]+v;
    int l=1,r=m,now=root;
    while(ls[pre]||rs[pre])
    {
        int mid=l+r>>1;
        if(pos>mid)
        {
            ls[now]=ls[pre];
            rs[now]=++cnt;
            sum[rs[now]]=sum[rs[pre]]+v;
            pre=rs[pre];
            now=rs[now];
            l=mid+1;
        }
        else
        {
            rs[now]=rs[pre];
            ls[now]=++cnt;
            sum[ls[now]]=sum[ls[pre]]+v;
            pre=ls[pre];
            now=ls[now];
            r=mid;
        }
    }
    return root;
}
void update(int loc,int newval)
{
    int pos=lower_bound(b+1,b+m+1,a[loc])-b;
    int x=loc;
    while(x<=n)
    {
        S[x]=build_new(x,pos,1,-1);
        x+=lowbit(x);
    }
    pos=lower_bound(b+1,b+m+1,newval)-b;
    x=loc;
    while(x<=n)
    {
        S[x]=build_new(x,pos,1,1);
        x+=lowbit(x);
    }
    a[loc]=newval;
    return ;
}
void work(int l,int r,int op)
{
    if(op==0)
    {
        while(l)
        {
            use[l][0]=ls[use[l][0]];
            l-=lowbit(l);
        }
        while(r)
        {
            use[r][1]=ls[use[r][1]];
            r-=lowbit(r);
        }
    }
    else
    {
        while(l)
        {
            use[l][0]=rs[use[l][0]];
            l-=lowbit(l);
        }
        while(r)
        {
            use[r][1]=rs[use[r][1]];
            r-=lowbit(r);
        }
    }
}
int getsum(int pos,int op)
{
    int res=0;
    while(pos)
    {
        res+=sum[ls[use[pos][op]]];
        pos-=lowbit(pos);
    }
    return res;
}
int query(int L,int R,int a,int b,int l,int r,int k)
{
    if(l==r) return l;
    int all=getsum(R,1)-getsum(L,0)+sum[ls[b]]-sum[ls[a]];
    int mid=l+r>>1;
    if(k<=all)
    {
        work(L,R,0);
        return query(L,R,ls[a],ls[b],l,mid,k);
    }
    else
    {
        work(L,R,1);
        return query(L,R,rs[a],rs[b],mid+1,r,k-all);
    }
}
int main()
{
    int q;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),b[i]=a[i];
    int all=n;
    for(int i=1;i<=q;i++)
    {
        scanf("%s%d%d",Q[i].s,&Q[i].l,&Q[i].r);
        if(Q[i].s[0]=='Q')
            scanf("%d",&Q[i].k);
        else b[++all]=Q[i].r;
    }
    sort(b+1,b+all+1);
    m=unique(b+1,b+all+1)-b-1;
    T[0]=build(1,m);
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(b+1,b+m+1,a[i])-b;
        T[i]=build_new(i,pos,0,1);
    }
    for(int i=1;i<=n;i++)
        S[i]=build_new(i,1,1,0);
    for(int i=1;i<=q;i++)
    {
        if(Q[i].s[0]=='Q')
        {
            int x=Q[i].l-1,y=Q[i].r;
            while(x)
            {
                use[x][0]=S[x];
                x-=lowbit(x);
            }
            while(y)
            {
                use[y][1]=S[y];
                y-=lowbit(y);
            }
            printf("%d\n",b[query(Q[i].l-1,Q[i].r,T[Q[i].l-1],T[Q[i].r],1,m,Q[i].k)]);
        }
        else update(Q[i].l,Q[i].r);
    }
    return 0;
}

上述代碼支持兩種操作,Q操作表示查詢,C操作表示更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章