[BZOJ3110]ZJOI2013-K大数查询

[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\)

整理一下,

\[a[j]=\sum _{i=1}^{j} c[i],sum(l,r)=\sum _{i=1}^{r}a[i]-\sum _{i=1}^{l-1}a[i] \]

那么:

\[\sum_{i=1}^na[i]=\sum_{i=1}^n\sum_{j=1}^ic[j]=\sum_{i=1}^nc[i](n-i+1)\\ =(n+1)\sum_{i=1}^nc[i]-\sum_{i=1}^nc[i]\times i \]

那我们做两个树状数组,分别维护\(c[i]\)\(c[i]\times 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\n",-ans[i]);}
    return 0;
}

posted @ 2019-05-22 11:13  GavinZheng  阅读(160)  评论(0编辑  收藏  举报