BZOJ 1901 Zju2112 Dynamic Rankings

title: ‘BZOJ 1901 Zju2112 Dynamic Rankings’
categories: BZOJ
date: 2016-1-6 13:23:00
tags: [主席树,树状数组]


Description

给定一个含有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,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,你必须输出正确的回答。

Input

第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。第二行有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。

Output

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

Sample

input.txt
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

output.txt
3
6

Hint

20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。

Solution

第一次打主席树就是这种题。。。
带修改的区间第k大,具体做法可以看一波clj论文:论文戳这里
首先用线段树求全局第k大,感觉跟Splay差不多,根据左右儿子的size值来考虑进入左孩子还是右孩子。
然后根据权值线段树的运算法则,若用T(l,r)表示区间[l,r]的权值线段树,可以以用T(1,r)-T(1,l-1)表示之,这就相当于是用线段树表示了前缀和。因此我们只需要建T(1,1),T(1,2),….,T(1,n)这n棵线段树即可,用可持久化线段树得到所有的T只需要O(nlogn)的时间和空间。
然而若需要修改,则要修改n个前缀和,单次修改和询问复杂度会上升到O(nlogn)。
有没有什么方便维护的前缀和?对了,树状数组!于是我们可以开O(logn)个权值线段树,表示与树状数组类似的意义来维护这个前缀和,因此每次询问和修改的复杂度只需要O((logn)^2)。
哦对了= = 注意离散化,23333

Code

#include<map>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

#define maxn 20000+5
#define maxm 2400000+5

using namespace std;

struct Ha_Tree{
    int l,r;
    int sum;
}tr[maxm];

struct Query_kth{
    char s[1];
    int x,y,z;
}q[maxn];

int ori[maxn],a[maxn],c[maxn],root[maxn],L[maxn],R[maxn];
int n,m,cnt,tot,ind,k,pl,pr;

int find(int val){
    int l=1,r=tot;
    while(l<=r){
        int mid=(l+r)>>1;
        if(c[mid]>val) r=mid-1;
        else if(c[mid]<val) l=mid+1;
        else return mid;
    }
}

void Update(int l,int r,int last,int &k,int pos,int val){
    k=++ind;
    tr[k]=tr[last]; tr[k].sum+=val;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) Update(l,mid,tr[last].l,tr[k].l,pos,val);
    else Update(mid+1,r,tr[last].r,tr[k].r,pos,val);
}

void Modify(int x,int pos,int val){
    for(int i=x;i<=n;i+=i&-i)
        Update(1,tot,root[i],root[i],pos,val);
}

int Query(int l,int r,int k){
    if(l==r) return l;
    int mid=(l+r)>>1;
    int suml=0,sumr=0;
    for(int i=1;i<=pl;i++) suml+=tr[tr[L[i]].l].sum;
    for(int i=1;i<=pr;i++) sumr+=tr[tr[R[i]].l].sum;
    if(sumr-suml>=k){
        for(int i=1;i<=pl;i++) L[i]=tr[L[i]].l;
        for(int i=1;i<=pr;i++) R[i]=tr[R[i]].l;
        return Query(l,mid,k);
    }
    else{
        for(int i=1;i<=pl;i++) L[i]=tr[L[i]].r;
        for(int i=1;i<=pr;i++) R[i]=tr[R[i]].r;
        return Query(mid+1,r,k-(sumr-suml));
    }
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&ori[i]),a[++cnt]=ori[i];
    for(int i=1;i<=m;i++){
        scanf("%s",q[i].s);
        scanf("%d%d",&q[i].x,&q[i].y);
        if(q[i].s[0]=='C') a[++cnt]=q[i].y;
        if(q[i].s[0]=='Q') scanf("%d",&q[i].z);
    }
    a[0]=-0x3f3f3f3f;
    sort(a+1,a+1+cnt);
    for(int i=1;i<=cnt;i++)
        if(a[i]!=a[i-1]) c[++tot]=a[i];   
    for(int i=1;i<=n;i++){
        int pos=find(ori[i]);
        Modify(i,pos,1);
    }
    for(int i=1;i<=m;i++)
        if(q[i].s[0]=='C'){
            int pos1=find(ori[q[i].x]),pos2=find(q[i].y);
            Modify(q[i].x,pos1,-1); Modify(q[i].x,pos2,1);
            ori[q[i].x]=q[i].y;     
        }
        else{
            int l=q[i].x-1,r=q[i].y; pl=pr=0;
            for(int j=l;j;j-=j&-j) L[++pl]=root[j];
            for(int j=r;j;j-=j&-j) R[++pr]=root[j];
            printf("%d\n",c[Query(1,tot,q[i].z)]);
        }
    return 0;
}
发布了28 篇原创文章 · 获赞 0 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览