zoukankan      html  css  js  c++  java
  • [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}^nsum_{j=1}^ic[j]=sum_{i=1}^nc[i](n-i+1)\ =(n+1)sum_{i=1}^nc[i]-sum_{i=1}^nc[i] imes i ]

    那我们做两个树状数组,分别维护(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;
    }
    
    
  • 相关阅读:
    php 1231
    php 1229
    php 1228
    php 0103
    php 1227
    php 1230
    php 0104
    flex弹性布局学习
    ps抠图的几种方法
    sql2005 不同的日期展示形式
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/10904727.html
Copyright © 2011-2022 走看看