主席树套树状数组——带修区间第k大zoj2112

主席树带修第k大 
https://www.cnblogs.com/Empress/p/4659824.html 讲的非常好的博客

首先按静态第k大建立起一组权值线段树(主席树) 然后现在要将第i个值从a改为b, 由主席树前缀和的性质可知修改第i个值会对T[i]...T[n]棵权值线段树造成相同的影响 如果暴力进行维护必定超时 那么如何优化这种修改? 我们知道动态维护前缀和可以用bit在log时间内完成 那么对于动态维护具有前缀和性质的主席树,我们也可以套上bit来完成 update:将第i个值从a改成b 根据bit的原理,第i个元素的修改会影响第i+lowbit(i)个元素 那么现在第i棵树修改了,第i+lowbit(i)棵树也应该影响 即第i棵树的位置a -1,同样的第i+lowbit(i)棵树的位置a -1 同理这批树的位置b +1即可 query:询问区间[l,r]第k大元素 即求T[r]-T[l]的前缀和即可 查询时要把bit对应的修改套上

 

一些实现上的细节

  按照静态主席树建立 T[1-n]
  用相同结构的树状数组 S[1-n]来维护 T[1-n]
  S[i]表示一棵权值线段树的根节点

  update:修改位置i,必然要修改S[i],那么S[i+lowbit i]也要修改
  query [l,r]:转化成前缀和问题,静态的初始主席树 T[r]-T[l-1], 再加上树状数组中修改的量 S[r]-S[l-1],

由于需要到子树里更新或查询,开一个数组use[i]表示第i棵树状数组当前在哪个子树更新或查询

我写的不知道为什么在zoj会段错误。。

在洛谷上就能过。。

#include<bits/stdc++.h>
using namespace std;
#define maxn 600005
inline int low(int x){return x&(-x);}
struct Q{char op[2];int l,r,k;}op[maxn];
int n,a[maxn],m,tot,b[maxn],q;

struct Node{int lc,rc,sum;}T[maxn*50];
int rt[maxn],S[maxn],use[maxn],size;
int build(int l,int r){
    int now=++size;
    if(l==r)return now;
    int mid=l+r>>1;
    T[now].lc=build(l,mid);
    T[now].rc=build(mid+1,r);
    return now;
}
int update(int last,int pos,int v,int l,int r){
    int now=++size;
    T[now]=T[last];T[now].sum+=v;
    if(l==r)return now;
    int mid=l+r>>1;
    if(pos<=mid)T[now].lc=update(T[last].lc,pos,v,l,mid);
    else T[now].rc=update(T[last].rc,pos,v,mid+1,r);
    return now;
}
int Sum(int x){//查询前x棵树的修改值 
    int res=0;
    while(x){
        res+=T[T[use[x]].lc].sum;
        x-=low(x);
    } 
    return res;
}
void Update(int x,int pos,int v){
    while(x<=n){
        S[x]=update(S[x],pos,v,1,m);
        x+=low(x);
    }
}
int query(int L,int R,int st,int ed,int l,int r,int k){
    if(l==r)return l;
    int mid=l+r>>1;
    int sum=Sum(R)-Sum(L)+T[T[ed].lc].sum-T[T[st].lc].sum;
    if(k<=sum){
        for(int i=L;i;i-=low(i))
            use[i]=T[use[i]].lc;
        for(int i=R;i;i-=low(i))
            use[i]=T[use[i]].lc;
        return query(L, R, T[st].lc, T[ed].lc,l,mid,k);
    }
    else {
        for(int i=L;i;i-=low(i))
            use[i]=T[use[i]].rc;
        for(int i=R;i;i-=low(i))
            use[i]=T[use[i]].rc;
        return query(L, R, T[st].rc, T[ed].rc,mid+1,r, k-sum);
    }
}
void init(){
    m=tot=size=0;memset(rt,0,sizeof rt);
    memset(S,0,sizeof S);
}
int main(){

{
        init();
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[++tot]=a[i];
        for(int i=1;i<=q;i++){
            scanf("%s",op[i].op);
            if(op[i].op[0]=='Q')scanf("%d%d%d",&op[i].l,&op[i].r,&op[i].k);
            else {
                scanf("%d%d",&op[i].l,&op[i].r);
                b[++tot]=op[i].r;
            }
        }
        sort(b+1,b+1+tot);//离散化 
        m=unique(b+1,b+1+tot)-(b+1);

        rt[0]=build(1,m);
        for(int i=1;i<=n;i++){//建立静态主席树 
            int pos=lower_bound(b+1,b+1+m,a[i])-b;
            rt[i]=update(rt[i-1],pos,1,1,m);
            S[i]=rt[0]; 
        }
        for(int i=1;i<=q;i++){
            int l=op[i].l,r=op[i].r,k=op[i].k;
            if(op[i].op[0]=='Q'){//查询,准备好要用到的线段树 
                for(int j=l-1;j;j-=low(j))use[j]=S[j];
                for(int j=r;j;j-=low(j))use[j]=S[j];
                cout<<b[query(l-1,r,rt[l-1],rt[r],1,m,k)]<<'\n';
            }
            else {
                int pos1=lower_bound(b+1,b+1+m,a[l])-b;
                int pos2=lower_bound(b+1,b+1+m,r)-b;
                Update(l,pos1,-1);Update(l,pos2,1);
                a[l]=r;
            }
        }
    }    
} 

下面是一份模板。。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define lson l, m
#define rson m+1, r
const int N=60005;
int a[N], Hash[N];
int T[N], L[N<<5], R[N<<5], sum[N<<5];
int S[N];
int n, m, tot;
struct node
{
    int l, r, k;
    bool Q;
}op[10005];

int build(int l, int r)
{
    int rt=(++tot);
    sum[rt]=0;
    if(l!=r)
    {
        int m=(l+r)>>1;
        L[rt]=build(lson);
        R[rt]=build(rson);
    }
    return rt;
}

int update(int pre, int l, int r, int x, int val)
{
    int rt=(++tot);
    L[rt]=L[pre], R[rt]=R[pre], sum[rt]=sum[pre]+val;
    if(l<r)
    {
        int m=(l+r)>>1;
        if(x<=m)
            L[rt]=update(L[pre], lson, x, val);
        else
            R[rt]=update(R[pre], rson, x, val);
    }
    return rt;
}

int lowbit(int x)
{
    return x&(-x);
}

int use[N];
void add(int x, int pos, int val)
{
    while(x<=n)
    {
        S[x]=update(S[x], 1, m, pos, val);
        x+=lowbit(x);
    }
}

int Sum(int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=sum[L[use[x]]];
        x-=lowbit(x);
    }
    return ret;
}

int query(int u, int v, int lr, int rr, int l, int r, int k)
{
    if(l>=r)
        return l;
    int m=(l+r)>>1;
    int tmp=Sum(v)-Sum(u)+sum[L[rr]]-sum[L[lr]];
    if(tmp>=k)
    {
        for(int i=u;i;i-=lowbit(i))
            use[i]=L[use[i]];
        for(int i=v;i;i-=lowbit(i))
            use[i]=L[use[i]];
        return query(u, v, L[lr], L[rr], lson, k);
    }
    else
    {
        for(int i=u;i;i-=lowbit(i))
            use[i]=R[use[i]];
        for(int i=v;i;i-=lowbit(i))
            use[i]=R[use[i]];
        return query(u, v, R[lr], R[rr], rson, k-tmp);
    }
}

void modify(int x, int p, int d)
{
    while(x<=n)
    {
        S[x]=update(S[x], 1, m, p, d);
        x+=lowbit(x);
    }
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int q;
        scanf("%d%d", &n, &q);
        tot=0;
        m=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &a[i]);
            Hash[++m]=a[i];
        }
        for(int i=0;i<q;i++)
        {
            char s[10];
            scanf("%s", s);
            if(s[0]=='Q')
            {
                scanf("%d%d%d", &op[i].l, &op[i].r, &op[i].k);
                op[i].Q=1;
            }
            else
            {
                scanf("%d%d", &op[i].l, &op[i].r);
                op[i].Q=0;
                Hash[++m]=op[i].r;
            }
        }
        sort(Hash+1, Hash+1+m);
        int mm=unique(Hash+1, Hash+1+m)-Hash-1;
        m=mm;
        T[0]=build(1, m);
        for(int i=1;i<=n;i++)
            T[i]=update(T[i-1], 1, m, lower_bound(Hash+1, Hash+1+m, a[i])-Hash, 1);
        for(int i=1;i<=n;i++)
            S[i]=T[0];
        for(int i=0;i<q;i++)
        {
            if(op[i].Q)
            {
                for(int j=op[i].l-1;j;j-=lowbit(j))
                    use[j]=S[j];
                for(int j=op[i].r;j;j-=lowbit(j))
                    use[j]=S[j];
                printf("%d\n", Hash[query(op[i].l-1, op[i].r, T[op[i].l-1], T[op[i].r], 1, m, op[i].k)]);
            }
            else
            {
                modify(op[i].l, lower_bound(Hash+1, Hash+1+m, a[op[i].l])-Hash, -1);
                modify(op[i].l, lower_bound(Hash+1, Hash+1+m, op[i].r)-Hash, 1);
                a[op[i].l]=op[i].r;
            }
        }
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/zsben991126/p/10765363.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值