[BZOJ3110]ZJOI2013-K大数查询
题面
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
Input
第一行N,M
接下来M行,每行形如1 a b c或2 a b c
Output
输出每个询问的结果
Sample Input
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
1
2
1
Hint
【样例说明】
第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1
的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是
1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3
大的数是 1 。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中c<=Maxlongint
思路
一个整体二分或主席树就能做。这里我们选用整体二分解决。区间修改我们可以利用树状数组·改来解决。
对于整体二分,我们二分的是答案,即“区间k大数”是哪一个。树状数组维护的是每个位置权值小于mid的元素的个数。而对于每一次分治,按时间顺序进行操作,如果是修改操作就在判断插入的数c的权值和mid的关系,小于mid就放左边。同时区间修改一波元素个数。如果是查询操作就统计一下查询一下([l,r])的元素个数,这里查出来的是权值小于mid的元素。我们把查出来的个数跟k比较,小于k放左边,大于k剪掉查出来的数再放右边即可。
定义一个差分数组(c),在区间([l,r])上加(k)的操作就可以修改为在(c[l])上加上(k)和在(c[r+1])上加上(-k)。这样我们只需要对差分数组求一遍前缀和就是最后的答案数组(ans)。
整理一下,
那么:
那我们做两个树状数组,分别维护(c[i])和(c[i] imes i)的前缀和即可。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
#define maxn (int)(5*1e4+1000)
#define inf (int)(1e9+1000)
struct gg{
int ty,l,r;//query=1,add=0
ll k;
int id;
}q[maxn*2],q1[maxn*2],q2[maxn*2];
int cnt,n,m;
ll c1[maxn],c2[maxn];
int ans[maxn],anscnt=0;
int low(int x){return x&-x;}
void upd(int pos, int v) {
int tmp = pos * v;
for (; pos <= n; pos += pos & -pos) {
c1[pos] += v, c2[pos] += tmp;
}
}
ll query(int pos) {
ll tmp = pos + 1, res = 0;
for (; pos; pos &= pos - 1) {
res += c1[pos] * tmp - c2[pos];
}
return res;
}
void add(int l, int r, int v) {
upd(l, v), upd(r + 1, -v);
}
ll query(int l, int r) {
return query(r) - query(l - 1);
}
void solve(int l,int r,int L,int R){
if(l>r||L>R)return;
int mid=(l+r)>>1,cnt1=0,cnt2=0;
if(l==r){
for(int i=L;i<=R;i++){if(q[i].ty)ans[q[i].id]=l;}return;
}
for(int i=L;i<=R;i++){
if(q[i].ty){
ll tmp=query(q[i].l,q[i].r);
if(q[i].k<=tmp)q1[++cnt1]=q[i];
else{q[i].k-=tmp;q2[++cnt2]=q[i];}
}
else{
if(q[i].k<=mid){
add(q[i].l,q[i].r,1);
q1[++cnt1]=q[i];
}
else{
q2[++cnt2]=q[i];
}
}
}
for(int i=1;i<=cnt1;i++){if(!q1[i].ty){add(q1[i].l,q1[i].r,-1);}}
for(int i=1;i<=cnt1;i++){q[L+i-1]=q1[i];}
for(int i=1;i<=cnt2;i++){q[L+cnt1+i-1]=q2[i];}
solve(l,mid,L,cnt1+L-1);solve(mid+1,r,cnt1+L,R);
}
int main(){
// freopen("in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int type,l,r;ll w;scanf("%d%d%d%lld",&type,&l,&r,&w);
if(type==1){q[++cnt]=(gg){0,l,r,-w};}
else{q[++cnt]=(gg){1,l,r,w,++anscnt};}
}
solve(-inf,inf,1,m);
for(int i=1;i<=anscnt;i++){printf("%d
",-ans[i]);}
return 0;
}