P2617 Dynamic Rankings-动态主席树

题目描述

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。

对于每一个询问指令,你必须输出正确的回答。

输入输出格式

输入格式:

 

第一行有两个正整数n(1≤n≤100000),m(1≤m≤100000)。分别表示序列的长度和指令的个数。

第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t

  • Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。

  • C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

 

输出格式:

 

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

 

输入输出样例

输入样例#1:

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

输出样例#1:

3
6

说明

10%的数据中,m,n≤100;

20%的数据中,m,n≤1000;

50%的数据中,m,n≤10000。

对于所有数据,m,n≤100000

请注意常数优化,但写法正常的整体二分和树套树都可以以大约1000ms每个点的时间过。

来源:bzoj1901

本题数据为洛谷自造数据,使用CYaRon耗时5分钟完成数据制作。

 

题解:动态主席树查询区间第k小

#include<bits/stdc++.h>
#define begin b+1
#define end b+1+size
using namespace std;
const int maxn=1e5+5;
int a[maxn];
int b[maxn+maxn];
int ca[maxn],cb[maxn],cc[maxn];
int xx[maxn],yy[maxn];
int size=0,cnt=0;
int root[maxn];
int t0,t1;
struct node{
    int l,r,sum;
}sz[maxn*400];//动态开 400倍 
void updata(int l,int r,int &x,int y,int v,int pos)//建树 
{
    if(x==0) x=++cnt;
    sz[x]=sz[y];
    sz[x].sum+=v;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(mid>=pos) updata(l,mid,sz[x].l,sz[y].l,v,pos);
    else updata(mid+1,r,sz[x].r,sz[y].r,v,pos); 
}
int getSum(int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1,ans=0;
    for(int i=1;i<=t0;i++)	ans-=sz[sz[xx[i]].l].sum;
    for(int i=1;i<=t1;i++)	ans+=sz[sz[yy[i]].l].sum;
    if(ans>=k)
    {
        for(int i=1;i<=t0;i++) xx[i]=sz[xx[i]].l;
        for(int i=1;i<=t1;i++) yy[i]=sz[yy[i]].l;
        return getSum(l,mid,k);
    }
    else{
        for(int i=1;i<=t0;i++) xx[i]=sz[xx[i]].r;
        for(int i=1;i<=t1;i++) yy[i]=sz[yy[i]].r;
        return getSum(mid+1,r,k-ans);
    }
}
int low_bit(int x)
{
    return x&(-x);
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[++size]=a[i];
    }
    for(int i=1;i<=m;i++)
    {
        char q[2];
        scanf("%s",q);
        if(q[0]=='Q')
        {
            scanf("%d %d %d",&ca[i],&cb[i],&cc[i]);
        }
        else 
        {
            scanf("%d %d",&ca[i],&cb[i]);
            b[++size]=cb[i];
        }
    }
    sort(b+1,b+1+size);//去重,离散化 
    size=unique(b+1,b+1+size)-b-1;
    for(int i=1;i<=n;i++)
    {
        int p=lower_bound(b+1,b+1+size,a[i])-b;//查询其位置 
        for(int j=i;j<=n;j+=low_bit(j))//树状数组维护 
        {
            updata(1,size,root[j],root[j],1,p);
        }
    }
    for(int i=1;i<=m;i++)
    {
        if(cc[i]==0)
        {
            int p=lower_bound(b+1,b+1+size,a[ca[i]])-b;
            for(int j=ca[i];j<=n;j+=low_bit(j))
            {
                updata(1,size,root[j],root[j],-1,p);
            }
            a[ca[i]]=cb[i];
            p=lower_bound(b+1,b+1+size,a[ca[i]])-b;
            for(int j=ca[i];j<=n;j+=low_bit(j))
            {
                updata(1,size,root[j],root[j],1,p);
            }
        }
        else
        {
            t0=0;
            t1=0;
            for(int j=ca[i]-1;j>0;j-=low_bit(j))
            {
                xx[++t0]=root[j];
            }
            for(int j=cb[i];j>0;j-=low_bit(j))
            {
                yy[++t1]=root[j];
            }
            printf("%d\n",b[getSum(1,size,cc[i])]);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值