主席树经典区间第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操作表示更新

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