这是一个经典问题
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;
}