维护一个长度为n的序列,使得其支持m次操作,包括区间插入和区间求k小数。
n,m<=80000,在任何时候|ai|<=5000000
一看到区间第k大/小,就想到了主席树。
但这个是区间修改!
怎么做呢?
(分块大法好)
观察到时限7s,果断上分块。(复杂度好不科学)
分块大法好?!
用每一个块维护排过序后的块和原来的块,然后对于每次询问,
二分答案?!
不科学的复杂度。
负数二分怎么打?
平移就好喽……
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 80005
#define M 400
#define inf 5000000
using namespace std;
struct note{int v,w;}a[N],b[N];
bool cmp(note x,note y) {return x.v<y.v;}
int n,m,q,bz,le,ri,k,u,v,lazy[M],l[M],r[M];
int find(int v,int x) {
int le=l[v],ri=r[v]+1,mid;
while (le<ri) {
mid=(le+ri)/2;
if (b[mid].v+lazy[v]<=x) le=mid+1;else ri=mid;
}
return le-l[v];
}
bool check(int x,int le,int ri) {
int u,v,ans=0;
fo(i,1,m) {
if (l[i]<=le&&le<=r[i]) u=i;
if (l[i]<=ri&&ri<=r[i]) v=i;
}
if (u==v) {
fo(i,le,ri) if (a[i].v+lazy[u]<=x) ans++;
if (ans>=k) return 1;else return 0;
}
fo(i,le,r[u]) if (a[i].v+lazy[u]<=x) ans++;
fo(i,l[v],ri) if (a[i].v+lazy[v]<=x) ans++;
fo(i,u+1,v-1) ans+=find(i,x);
if (ans>=k) return 1;else return 0;
}
int main() {
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i].v),b[i].v=a[i].v,b[i].w=i;
m=n/M+((n%M)>0);
fo(i,1,m) {
l[i]=r[i-1]+1;r[i]=min(l[i]+M,n);
sort(b+l[i],b+r[i]+1,cmp);
}
fo(i,1,n) a[b[i].w].w=i;
for(scanf("%d",&q);q;q--) {
scanf("%d%d%d%d",&bz,&le,&ri,&k);
if (bz==1) {
fo(i,1,m) {
if (l[i]<=le&&le<=r[i]) u=i;
if (l[i]<=ri&&ri<=r[i]) v=i;
}
if (u==v) {
fo(i,le,ri) a[i].v+=k,b[a[i].w].v+=k;
sort(b+l[u],b+r[u]+1,cmp);
fo(i,l[u],r[u]) a[b[i].w].w=i;
continue;
}
fo(i,le,r[u]) a[i].v+=k,b[a[i].w].v+=k;
sort(b+l[u],b+r[u]+1,cmp);
fo(i,l[u],r[u]) a[b[i].w].w=i;
fo(i,l[v],ri) a[i].v+=k,b[a[i].w].v+=k;
sort(b+l[v],b+r[v]+1,cmp);
fo(i,l[v],r[v]) a[b[i].w].w=i;
fo(i,u+1,v-1) lazy[i]+=k;
} else {
u=1,v=inf*2;int mid;
while (u<v) {
mid=(u+v)/2;
if (check(mid-inf,le,ri)) v=mid;else u=mid+1;
}
printf("%d\n",u-inf);
}
}
}