题目大意
兹瓷区间加和区间求k小。
分块大法好
我们将序列分块,每一块维护add标记和排序后的序列。
对于区间加,跨过整块就直接打标记,多余部分暴力修改然后重构所在块。
对于区间求k小,显然可以二分答案,然后转化为判定问题。
在一个块内求
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=80000+10,maxc=690+10,maxd=5000000;
int belong[maxn],add[maxn],a[maxn],size[maxn/maxc+10];
int b[maxn/maxc+10][maxc];
int i,j,k,l,r,mid,t,n,m,c,x;
void build(int x){
int i;
size[x]=0;
fo(i,(x-1)*c+1,min(x*c,n)) b[x][++size[x]]=a[i];
sort(b[x]+1,b[x]+size[x]+1);
}
int binary(int x,int y){
int l=0,r=size[x],mid;
while (l<r){
mid=(l+r+1)/2;
if (b[x][mid]+add[x]<y) l=mid;else r=mid-1;
}
return l;
}
int get(int j,int k,int x){
int i,ans=0;
int l=belong[j],r=belong[k];
if (l==r){
fo(i,j,k)
if (a[i]+add[l]<x) ans++;
return ans;
}
fo(i,l+1,r-1) ans+=binary(i,x);
fo(i,j,l*c)
if (a[i]+add[l]<x) ans++;
fo(i,(r-1)*c+1,k)
if (a[i]+add[r]<x) ans++;
return ans;
}
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int main(){
freopen("kth2.in","r",stdin);freopen("kth.out","w",stdout);
n=read();
c=690;
fo(i,1,n){
a[i]=read();
belong[i]=(i-1)/c+1;
}
fo(i,1,belong[n]) build(i);
m=read();
while (m--){
t=read();j=read();k=read();x=read();
if (t==1){
l=belong[j];r=belong[k];
if (l==r){
fo(i,j,k) a[i]+=x;
build(l);
continue;
}
fo(i,l+1,r-1) add[i]+=x;
fo(i,j,l*c) a[i]+=x;
fo(i,(r-1)*c+1,k) a[i]+=x;
build(l);build(r);
}
else{
l=-maxd;r=maxd;
while (l<r){
mid=(l+r+1)/2;
if (get(j,k,mid)<=x-1) l=mid;else r=mid-1;
}
printf("%d\n",l);
}
}
}