Old Problem [带修改的kth]

来源:互联网 发布:脱口秀大会 知乎 编辑:程序博客网 时间:2024/06/05 23:43

这是一个经典问题

n个数的数列A[1…n]
q次操作 :
1 L R x 区间[L,R] 加 x
2 L R k 询问区间[L,R] 第k大的数

0<q,n<105 ,256Mb 4 seconds

这个问题陈老师在一篇论文中给了3种方法


这里写图片描述

我选择了实现比较简单的 二分+分块

#include<cstdio>#include<cmath>#include<algorithm>using namespace std;const int MAXN=1e5+5,NUM=2e3;int A[MAXN],sA[MAXN],tag[NUM],n,m,num;inline void build(){    for(int i=0;i<=n/num;i++){        tag[i]=0;    }    for(int i=0;i<=n;i++){        sA[i]=A[i];    }    for(int i=0;i<n/num;i++){        sort(sA+i*num,sA+i*num+num);    }}inline void change(int l,int r,int t){    if(l/num==r/num){        for(int i=l;i<=r;i++){            A[i]+=t;        }        if(r/num==n/num) return ;        for(int i=l/num*num;i<r/num*num+num;i++){            sA[i]=A[i];        }        sort(sA+l/num*num,sA+r/num*num+num);        return ;    }    for(int i=l/num+1;i<r/num;i++){        tag[i]+=t;    }    for(int i=l;i<l/num*num+num;i++){        A[i]+=t;    }    for(int i=l/num*num;i<l/num*num+num;i++){        sA[i]=A[i];    }    sort(sA+l/num*num,sA+l/num*num+num);    for(int i=r/num*num;i<=r;i++){        A[i]+=t;        }       if(r/num==n/num) return ;    for(int i=r/num*num;i<r/num*num+num;i++){        sA[i]=A[i];    }    sort(sA+r/num*num,sA+r/num*num+num);}inline int query(int l,int r,int qn){    int cnt=0;    if(l/num==r/num){        for(int i=l;i<=r;i++){            if(A[i]+tag[i/num]<=qn){                cnt++;            }        }        return cnt;    }    for(int i=l;i<l/num*num+num;i++){        if(A[i]+tag[i/num]<=qn){            cnt++;        }    }    for(int i=r/num*num;i<=r;i++){        if(A[i]+tag[i/num]<=qn){            cnt++;        }    }    for(int i=l/num+1;i<r/num;i++){        cnt+=upper_bound(sA+i*num,sA+i*num+num,qn-tag[i])-(sA+i*num);    }    return cnt;}inline int getkth(int l,int r,int k){    int low=-1e9,top=1e9;    while(top-low>1){        int mid=(top+low)>>1;        int kk=query(l,r,mid);        if(kk>=k) top=mid;        else low=mid;    }    return top;}int main(){//  freopen("out","r",stdin);//  freopen("myans","w",stdout);    while(scanf("%d",&n)!=EOF){        num=sqrt(n*log(n)/log(2));        n--;        for(int i=0;i<=n;i++) scanf("%d",&A[i]);        build();        scanf("%d",&m);        while(m--){            int op,l,r,x;            scanf("%d%d%d%d",&op,&l,&r,&x);            l--,r--;            if(op==1){                change(l,r,x);            }else{                int ans=getkth(l,r,x);                printf("%d\n",ans);            }        }    }    return 0;}

第一次写分块,写了个验证程序,也附上吧
数据生成

#include<stdio.h>#include<stdlib.h>#include<time.h>const int MAXN=1e5+5;const int T=10;int A[MAXN];int main(){    freopen("out","w",stdout);    for(int i=0;i<T;i++){        srand((unsigned int)(time(NULL)));        int n=rand()+17;        printf("%d\n",n);        for(int i=0;i<n;i++) printf("%d ",rand()<(rand()+10000)?rand():-rand());        int m=rand()%1234;        printf("\n%d\n",m);        while(m--){            int op=rand()%2+1;            int l=rand()%(n/2+1)+1;            int r=l+rand()%(n/2+1);            int x=op==1?(rand()<(rand()+10000)?rand():-rand()):(rand()%(r-l+1)+1);            printf("%d %d %d %d\n",op,l,r,x);        }    }    return 0;}

暴力验证

#include<stdio.h>#include<algorithm>using namespace std;const int MAXN=1e5+5;int A[MAXN],B[MAXN],n,m;int main(){    freopen("out","r",stdin);    freopen("ans","w",stdout);    while(scanf("%d",&n)!=EOF){        for(int i=1;i<=n;i++) scanf("%d",&A[i]);        scanf("%d",&m);        int op,l,r,x;        while(m--){            scanf("%d%d%d%d",&op,&l,&r,&x);            if(r>n||l>r) {puts("error");exit;}            if(op==1){                for(int i=l;i<=r;i++) A[i]+=x;            }else{                for(int i=l;i<=r;i++) B[i]=A[i];                sort(B+l,B+r+1);                printf("%d\n",B[l+x-1]);            }        }    }    return 0;}
原创粉丝点击