可修改的区间第K大 BZOJ1901 ZOJ2112

http://blog.csdn.net/u014492306/article/details/47981315   //变相离线做法

离散化缩小区间范围,做两大个线段树,第一个就是普通的持久化树,有个前缀和就好。

第二个用线段树套树状数组,每次询问就把这两个都求出来加一下。

更改就更改第二个,其实更改的时候只需要建一条链然后重复用这条链衍生就好了,但是为了抄的方便,就不改了。。。

当然这个空间上比较优秀的只有O(nlogn).

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using namespace std;
const int N=6e4+5;
const int M=2600006;
int m,n,nn,tot;
int a[N],f[N],T[N],S[N];
int sum[M],l[M],r[M];
int use[N];
int h(int x) {return lower_bound(f+1,f+nn+1,x)-f;}
void update(int pr,int lx,int rx,int v,int k){
    l[++tot]=l[pr],r[tot]=r[pr],sum[tot]=sum[pr]+k;
    if(lx==rx) return;
    int mid=(lx+rx)>>1;
    if(v<=mid) l[tot]=tot+1,update(l[pr],lx,mid,v,k);
    else r[tot]=tot+1,update(r[pr],mid+1,rx,v,k);
}
int Sum(int x){
    int res=0;
    for(int i=x;i;i-=lowbit(i)) res+=sum[l[use[i]]];
    return res;
}
void add(int x,int v,int k){
    int temp;
    for(int i=x;i<=n;i+=lowbit(i)) {
        temp=S[i];
        S[i]=tot+1;
        update(temp,1,nn,v,k);
    }
}
int query(int L,int R,int k){
    for(int i=L-1;i;i-=lowbit(i)) use[i]=S[i];
    for(int i=R;i;i-=lowbit(i)) use[i]=S[i];
    int lx=1,rx=nn,lt=T[L-1],rt=T[R];
    while(lx<rx) {
        int mid=(lx+rx)>>1;
        int tmp=Sum(R)-Sum(L-1)+sum[l[rt]]-sum[l[lt]];
        if(k<=tmp) {
            rx=mid;
            for(int i=L-1;i;i-=lowbit(i)) use[i]=l[use[i]];
            for(int i=R;i;i-=lowbit(i)) use[i]=l[use[i]];
            lt=l[lt],rt=l[rt];
        }
        else {
            lx=mid+1,k-=tmp;
            for(int i=L-1;i;i-=lowbit(i)) use[i]=r[use[i]];
            for(int i=R;i;i-=lowbit(i)) use[i]=r[use[i]];
            lt=r[lt],rt=r[rt];
        }
    }
    return f[lx];
}
char op[5];
int q[10005][4],Ta;
int main(){
    for(scanf("%d",&Ta);Ta--;) {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i) scanf("%d",a+i),f[i]=a[i];
        nn=n;
        for(int i=1;i<=m;++i) {
            scanf("%s",op);
            if(op[0]=='Q') {
                scanf("%d%d%d",&q[i][1],&q[i][2],&q[i][3]);
                q[i][0]=1;
            }
            else {
                scanf("%d%d",&q[i][1],&q[i][2]);
                q[i][0]=0;
                f[++nn]=q[i][2];
            }
        }
        sort(f+1,f+1+nn);
        nn=unique(f+1,f+nn+1)-f-1;
        tot=0,T[0]=0;
        for(int i=1;i<=n;++i) T[i]=tot+1,update(T[i-1],1,nn,h(a[i]),1);
        for(int i=1;i<=n;++i) S[i]=0;
        for(int i=1;i<=m;++i) {
            if(q[i][0]) printf("%d\n",query(q[i][1],q[i][2],q[i][3]));
            else {
                add(q[i][1],h(a[q[i][1]]),-1);
                add(q[i][1],h(q[i][2]),1);
                a[q[i][1]]=q[i][2];
            }
        }
    }
    return 0;
}

如果强制在线的话,只能一开始就用线段树套树状数组了,空间复杂度O(nlog(1e9)log(1e9)),为什么是1e9是因为你没办法事先离散化,因为你不知道更改的时候他要改成什。

http://blog.sina.com.cn/s/blog_4a0c4e5d0101c3yj.html

 

posted @ 2018-01-03 17:44  Billyshuai  阅读(261)  评论(0编辑  收藏  举报