【BZOJ】1901: Zju2112 Dynamic Rankings

【题意】带修改的查询区间第k小

【算法】树状数组套可持久化线段树

【题解】对于树状数组上的每个节点,维护可持久化权值线段树(节点为权值),从而达到查询前缀和的目的。

对于每次修改,在待修改线段树基础上运用可持久化性质来修改,先删除原数字,再加入新数字

注意记录修改后的原数字,方便后来删除。

★注意区分权值范围和数组范围的区别!!!

复杂度O(n log2n)。

注意点全部标注在代码中,细节巨多。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
using namespace std;
int read()
{
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
const int maxn=10010;
struct tree{int l,r,sum;}t[10000010];//空间开大!!!100w都过不去啊。。。 
char s[10];
int cnt1,cnt2,a1[maxn],a2[maxn],a[maxn],b1[maxn],b2[maxn],b3[maxn],n,m,root[maxn],tot,c[maxn*2],sz=0;

void insert(int l,int r,int x,int &y,int v,int c){
    y=++sz;//设置新节点!!! 
    t[y]=t[x];t[y].sum+=c;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(v<=mid)insert(l,mid,t[x].l,t[y].l,v,c);
    else insert(mid+1,r,t[x].r,t[y].r,v,c);
}
int ask(int l,int r,int v){
    if(l==r)return l;
    int sum=0;
    for(int i=1;i<=cnt1;i++)sum-=t[t[a1[i]].l].sum;
    for(int i=1;i<=cnt2;i++)sum+=t[t[a2[i]].l].sum;
    int mid=(l+r)>>1;
    if(sum>=v){
        for(int i=1;i<=cnt1;i++)a1[i]=t[a1[i]].l;
        for(int i=1;i<=cnt2;i++)a2[i]=t[a2[i]].l;
        return ask(l,mid,v);
    }
    else{
        for(int i=1;i<=cnt1;i++)a1[i]=t[a1[i]].r;
        for(int i=1;i<=cnt2;i++)a2[i]=t[a2[i]].r;
        return ask(mid+1,r,v-sum);//转入右边要减去左边的 
    }
}
    
int lowbit(int x){return x&(-x);}
void modify(int x,int k,int p){for(int i=x;i<=n;i+=lowbit(i))insert(1,tot,root[i],root[i],k,p);} 
//分清,tot是权值线段树范围,n是数组范围 
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        a[i]=read();
        c[++tot]=a[i];
    }
    for(int i=1;i<=m;i++){
        scanf("%s%d%d",s,&b1[i],&b2[i]);
        if(s[0]=='Q')b3[i]=read();
        else c[++tot]=b2[i];
    }
    sort(c+1,c+tot+1);
    tot=unique(c+1,c+tot+1)-c-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(c+1,c+tot+1,a[i])-c;
    for(int i=1;i<=m;i++)if(!b3[i])b2[i]=lower_bound(c+1,c+tot+1,b2[i])-c;
    for(int i=1;i<=n;i++)modify(i,a[i],1);
    for(int i=1;i<=m;i++){
        if(b3[i]){
            cnt1=0;cnt2=0;
            for(int j=b1[i]-1;j>=1;j-=lowbit(j))a1[++cnt1]=root[j];
            for(int j=b2[i];j>=1;j-=lowbit(j))a2[++cnt2]=root[j];//用根啊老铁 
            printf("%d\n",c[ask(1,tot,b3[i])]);//询问给的是离散化后的值,要还原 
        }
        else{
            modify(b1[i],a[b1[i]],-1);
            a[b1[i]]=b2[i];
            modify(b1[i],b2[i],1);
        }
    }
    return 0;
}
View Code

 

posted @ 2017-08-21 11:30  ONION_CYC  阅读(234)  评论(0编辑  收藏  举报