Bzoj 1901 Dynamic Rankings

来源:互联网 发布:unity3d 热力图 编辑:程序博客网 时间:2024/04/29 15:19

【题意】

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

Input

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

Output

Sample Input

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

Sample Output

3
6

HINT

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

【解题方法】动态第K大,主席树套树状数组。

首先静态主席树这个东西其实比较好懂,就是对于每个前缀[1,1],[1,2]....[1,n]都建一颗线段树,将数据离散化后,在线段树中统计区间内所有值出现的次数,每一个线段树都是相同的形态,那么这样我们得到一个很好的性质,这些线段树可以“相减”,假设当前查询的是区间[l,r]内第k大的值,那么我们用前缀[1, r]这个线段树和前缀[1, l-1]这颗线段树通过相减加上二分就可以找到答案。由于相邻两颗线段树最多只有logn个节点不同,我们没有必要完全新建一颗线段树,只需要把相同的结点用指针指一下就行,然后新建logn个结点,这样一来时空复杂度为n*logn。

对于动态查询的主席树,如果修改一个点的值,我们肯定不能修改所有包含这个点的线段树,时间复杂度无法承受。

那么我们就可以套上树状数组,个人感觉这里比较难懂。

树状数组的每个节点都是一颗线段树,但这棵线段树不再保存每个前缀的信息了,而是由树状数组的sum函数计算出这个前缀的信息,那么显而易见这棵线段树保存的是辅助数组S的值,即S=A[i-lowbit+1]+...+A[i],其中A[i]表示值为i的元素出现的次数。

那么对于每次修改,我们要修改树状数组上的logn棵树,对于每棵树,我们要修改logn个结点,所以时空复杂度为

O((n+q)*logn*logn),由于这道题n比较大,查询次数q比较小,所以我们可以初始时建立一颗静态的主席树,树状数组只保存每次修改的信息,那么时空复杂度降为了O(n*logn+q*logn*logn)。

【参考BLOG1】 http://blog.csdn.net/u014664226/article/details/47839973

【参考BLOG2】 http://www.cnblogs.com/kuangbin/p/3308118.html

【AC 代码】

////BZOJ 1901//Created by just_sort 2016/8/22//All Rights Reserved//#include <vector>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn = 60000+100;const int maxm = maxn*40;int n,q,m,tot,a[maxn];int T[maxn];vector<int>xs;int use[maxn],S[maxn];int getid(int x){    return lower_bound(xs.begin(),xs.end(),x)-xs.begin();}struct Q{    int l,r,k;    int ty;    Q(int ll,int rr,int kk,int ii):l(ll),r(rr),k(kk),ty(ii) {}    Q() {}} query[10010];int lson[maxm],rson[maxm],c[maxm];int Build(int l,int r){    int root=tot++;    c[root]=0;    if(l!=r)    {        int mid=(l+r)/2;        lson[root]=Build(l,mid);        rson[root]=Build(mid+1,r);    }    return root;}int Insert(int root,int pos,int val){    int newroot=tot++,tmp=newroot;    int l=0,r=m-1;    c[newroot]=c[root]+val;    while(l<r)    {        int mid=(l+r)/2;        if(pos<=mid)        {            lson[newroot]=tot++,rson[newroot]=rson[root];            newroot=lson[newroot],root=lson[root];            r=mid;        }        else        {            lson[newroot]=lson[root],rson[newroot]=tot++;            newroot=rson[newroot],root=rson[root];            l=mid+1;        }        c[newroot]=c[root]+val;    }    return tmp;}int lowbit(int x){    return x&-x;}void add(int x,int pos,int val){    while(x<=n)    {        S[x]=Insert(S[x],pos,val);        x+=lowbit(x);    }}int getsum(int x){    int ret=0;    while(x)    {        ret+=c[lson[use[x]]];        x-=lowbit(x);    }    return ret;}int querykth(int left,int right,int k){    int left_root=T[left-1],right_root=T[right];    int l=0,r=m-1;    for(int i=right; i; i-=lowbit(i)) use[i]=S[i];    for(int i=left-1; i; i-=lowbit(i)) use[i]=S[i];    while(l<r)    {        int mid=(l+r)/2;        int temp=getsum(right)-getsum(left-1)+c[lson[right_root]]-c[lson[left_root]];        if(temp>=k){            r=mid;            for(int i=left-1; i; i-=lowbit(i)) use[i]=lson[use[i]];            for(int i=right; i; i-=lowbit(i))  use[i]=lson[use[i]];            left_root=lson[left_root];            right_root=lson[right_root];        }        else{            l=mid+1;            k-=temp;            for(int i=left-1; i; i-=lowbit(i)) use[i]=rson[use[i]];            for(int i=right; i; i-=lowbit(i))  use[i]=rson[use[i]];            left_root=rson[left_root];            right_root=rson[right_root];        }    }    return l;}int main(){    while(scanf("%d%d",&n,&q)!=EOF)    {        tot=m=0;        xs.clear();        for(int i=1; i<=n; i++){            scanf("%d",&a[i]);            xs.push_back(a[i]);        }        char op[10];        for(int i=0; i<q; i++){            scanf("%s",op);            if(op[0]=='Q'){                query[i].ty=0;                scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k);            }else{                query[i].ty=1;                scanf("%d%d",&query[i].l,&query[i].r);                xs.push_back(query[i].r);            }        }        sort(xs.begin(),xs.end());        xs.resize(unique(xs.begin(),xs.end())-xs.begin());        m = xs.size();        T[0]=Build(0,m-1);        for(int i=1; i<=n; i++) T[i]=Insert(T[i-1],getid(a[i]),1);        for(int i=1; i<=n; i++) S[i]=T[0];        for(int i=0; i<q; i++){            if(query[i].ty==0) printf("%d\n",xs[querykth(query[i].l,query[i].r,query[i].k)]);            else{                add(query[i].l,getid(a[query[i].l]),-1);                add(query[i].l,getid(query[i].r),1);                a[query[i].l]=query[i].r;            }        }    }    return 0;}


0 0
原创粉丝点击