【ZOJ2112】Dynamic Ranking(主席树)

传送门:ZOJ2112 Dynamic Ranking

I Think

    题意:求数列区间第k大+修改操作
    算法:静态主席树+动态修改主席树(树状数组区间修改区间查询)
    思路:理解了主席树和树状数组在某种意义上都存储前缀和的共同性质就好。
    实现:Insert函数是非递归实现,还要注意到在树状数组中修改后,原数组a[]也需要更新。

Code

代码参考大神:http://blog.csdn.net/u014664226/article/details/47839973

#include<cstdio>
#include<iostream>
#include<algorithm>
#define lowbit(x) x&(-x)
using namespace std;
//zoj 2112 静态主席树+动态修改主席树
const int sm = 6e4+1000;
const int sn = 2400000;

int n,m,cs,cnt,tot,f;
int a[sm],t[sm],T[sm],S[sm];
int ls[sn],rs[sn],c[sn],use[sm];
struct query {
    int ind,l,r,k;
}q[50000+10];

char ch;
void read(int &x) {
    x=0,f=1,ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    x*=f;
}
int hash(int x) { return lower_bound(t+1,t+cnt+1,x)-t; }
void Build(int &rt,int l,int r) {
    rt=++tot,c[rt]=0;
    if(l==r) return;
    int m=(l+r)>>1;
    Build(ls[rt],l,m);
    Build(rs[rt],m+1,r);
}
int Insert(int pre,int p,int val) {
    int RT=++tot;
    int rt=RT,l=1,r=cnt,m;
    c[rt]=c[pre]+val;
    while(l<r) {
        m=(l+r)>>1;
        if(p<=m) {
            rs[rt]=rs[pre],ls[rt]=++tot;
            pre=ls[pre],rt=ls[rt],r=m;
        }
        else {
            ls[rt]=ls[pre],rs[rt]=++tot;
            pre=rs[pre],rt=rs[rt],l=m+1;
        }
        c[rt]=c[pre]+val;
    }
    return RT;
}
void Modify(int x,int p,int val) {
    for(int i=x;i<=n;i+=lowbit(i)) 
        S[i]=Insert(S[i],p,val);
}
int Sum(int x) {
    int ret=0;
    for(int i=x;i;i-=lowbit(i))
        ret+=c[ls[use[i]]];
    return ret;
}
int Query(int L,int R,int k) {
    int lft=T[--L],rgt=T[R],l=1,r=cnt,mid,tmp;
    for(int i=L;i;i-=lowbit(i)) use[i]=S[i];
    for(int i=R;i;i-=lowbit(i)) use[i]=S[i];
    while(l<r) {
        mid=(l+r)>>1;
        tmp=c[ls[rgt]]-c[ls[lft]]+Sum(R)-Sum(L); //静态主席树上的差量+树状数组上的差量
        if(tmp>=k) {
            r=mid,lft=ls[lft],rgt=ls[rgt];
            for(int i=L;i;i-=lowbit(i)) use[i]=ls[use[i]]; //use[i]也相应向下一层
            for(int i=R;i;i-=lowbit(i)) use[i]=ls[use[i]];
        }
        else {
            l=mid+1,k-=tmp,lft=rs[lft],rgt=rs[rgt];
            for(int i=L;i;i-=lowbit(i)) use[i]=rs[use[i]];
            for(int i=R;i;i-=lowbit(i)) use[i]=rs[use[i]];
        }
    }
    return l;
}
int main() {
    read(cs);
    while(cs--) {
        read(n),read(m),tot=0,cnt=0;
        for(int i=1;i<=n;++i) 
            read(a[i]),t[++cnt]=a[i];
        for(int i=1;i<=m;++i) {
            ch=getchar();
            if(ch=='C') {
                read(q[i].l),read(q[i].k);
                q[i].ind=2,t[++cnt]=q[i].k;
            }
            else {
                read(q[i].l),read(q[i].r);
                q[i].ind=1,read(q[i].k);
            }
        }
        sort(t+1,t+cnt+1);
        cnt=unique(t+1,t+cnt+1)-t-1;
        Build(T[0],1,cnt);
        for(int i=1;i<=n;++i) 
            T[i]=Insert(T[i-1],hash(a[i]),1),S[i]=T[0];//?
        for(int i=1;i<=m;++i) {
            if(q[i].ind==1)
                printf("%d\n",t[Query(q[i].l,q[i].r,q[i].k)]);
            else {
                Modify(q[i].l,hash(a[q[i].l]),-1);
                Modify(q[i].l,hash(q[i].k),1);
                a[q[i].l]=q[i].k;//下次再改动该点值需要调用modify
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值