ZOJ 2112 Dynamic Rankings(主席树-动态第k大)

Description
给出一个长度为n的序列a,两种操作
C x v:将第x个元素的值改成v
Q l r k:查询区间[l,r]中第k大的元素
Input
第一行为一个整数t表示用例组数,每组用例第一行为两个整数n和m分别表示序列长度和操作数,第二行n个整数表示序列a,之后m行每行一种操作
(0< t<=4,1<=n<=50000,1<=m<=10000)
Output
对于每次查询,输出查询结果
Sample Input
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
3
6
3
6
Solution
主席树动态第k大,设序列与操作中不同的数有s个,如果完全使用动态主席树,空间复杂度为O((n+m)*log n*log s),在这道题的内存限制下开不下这么大的数组,所以需要建两种主席树,第一种就是静态主席树维护每个前缀序列,第二种的动态主席树维护修改操作,每次查询时对这两棵树分别查询然后做和即可,这样的空间复杂度为O(nlog s+m*log n*log s)
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define maxn 50005
#define lowbit(x) (x&(-x))
struct Tree
{
    int left,right,data;
}T[50*maxn];
int t,n,m,a[maxn],b[2*maxn],pos[maxn],root[2*maxn],L[maxn],R[maxn],K[maxn],tot,cnt,res;
char O[maxn];
vector<int>p,q;
void build(int &i,int x,int l,int r)
{
    T[++tot]=T[i];
    i=tot;
    T[i].data++;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(x<=mid) build(T[i].left,x,l,mid);
    else build(T[i].right,x,mid+1,r);   
}
void insert(int &i,int x,int y,int l,int r)
{
    T[++tot]=T[i];
    i=tot;
    T[i].data+=y;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(x<=mid)  insert(T[i].left,x,y,l,mid);
    else insert(T[i].right,x,y,mid+1,r);
}
void bit_insert(int i,int x,int y) 
{
    while(i<=n)
    {
        insert(root[i],x,y,1,res);
        i+=lowbit(i);
    }
}
int query(int k,int l,int r)
{
    if(l==r) return l;
    int mid=(l+r)>>1,cnt1=0,cnt2=0;
    for(int i=0;i<p.size();i++) cnt1+=T[T[p[i]].left].data;
    for(int i=0;i<q.size();i++) cnt2+=T[T[q[i]].left].data;
    if(cnt2-cnt1>=k)
    {
        for(int i=0;i<p.size();i++) p[i]=T[p[i]].left;
        for(int i=0;i<q.size();i++) q[i]=T[q[i]].left;
        return query(k,l,mid);
    }
    else
    {
        for(int i=0;i<p.size();i++) p[i]=T[p[i]].right;
        for(int i=0;i<q.size();i++) q[i]=T[q[i]].right;
        return query(k-(cnt2-cnt1),mid+1,r);
    }
}
int solve(int l,int r,int k)
{
    p.clear();q.clear();
    if(l>0) p.push_back(root[l+n]);//若l=0则不能将root[n]加到p中 
    q.push_back(root[r+n]);
    while(l>0)
    {
        p.push_back(root[l]);
        l-=lowbit(l);
    }
    while(r>0)
    {
        q.push_back(root[r]);
        r-=lowbit(r);
    }
    return query(k,1,res);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        tot=cnt=0;
        memset(root,0,sizeof(root));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[++cnt]=a[i];
        for(int i=1;i<=m;i++)
        {
            char op[11];
            scanf("%s%d%d",op,&L[i],&R[i]);O[i]=op[0];
            if(op[0]=='Q') scanf("%d",&K[i]);
            else b[++cnt]=R[i];
        }
        sort(b+1,b+cnt+1); 
        res=unique(b+1,b+cnt+1)-b-1;//利用去重函数得到节点数 
        for(int i=1;i<=n;i++) pos[i]=lower_bound(b+1,b+res+1,a[i])-b;//pos记录每个值在主席树中的位置 
        for(int i=1;i<=n;i++)
        {
            root[i+n]=root[i+n-1];
            build(root[i+n],pos[i],1,res);//建主席树 
        }
        for(int i=1;i<=m;i++)
        {
            if(O[i]=='C')
            {
                bit_insert(L[i],pos[L[i]],-1);//消除原先值的影响 
                pos[L[i]]=lower_bound(b+1,b+res+1,R[i])-b;//更新pos值 
                bit_insert(L[i],pos[L[i]],1);//加入更新值的影响 
            }
            else
            {
                int ans=solve(L[i]-1,R[i],K[i]); 
                printf("%d\n",b[ans]);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值