您的位置:首页 > 其它

【HNOI2016模拟4.10】 K小数查询

2016-04-20 19:35 323 查看

Description

维护一个长度为n的序列,使得其支持m次操作,包括区间插入和区间求k小数。

n,m<=80000,在任何时候|ai|<=5000000

Solution

一看到区间第k大/小,就想到了主席树。

但这个是区间修改!

怎么做呢?

(分块大法好)

观察到时限7s,果断上分块。(复杂度好不科学)

分块大法好?!

用每一个块维护排过序后的块和原来的块,然后对于每次询问,

二分答案?!

不科学的复杂度。

负数二分怎么打?

平移就好喽……

Code

#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
,b
;
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);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: