zoukankan      html  css  js  c++  java
  • BZOJ3110 ZJOI2013 K大数查询

    3110: [Zjoi2013]K大数查询

    Time Limit: 20 Sec  Memory Limit: 512 MB

    Description

    有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,a<=b<=N
    1操作中abs(c)<=N
    2操作中c<=Maxlongint

      为什么BZOJ的排版这么鬼里鬼气。

      一道整体二分的入门题?反正我是做了蛮久的。

      都说了是整体二分了,那就分一下吧。

      自我感觉,整体二分是这么一个东西:

        二分答案都会,对于每组询问,二分答案再check看是否满足。

        那么整体二分,就是对所有询问加操作一起二分。

        二分的同样是答案,然后询问就可以分成两部分了。

        同时把操作也分成两部分。

        一部分是对前面询问要产生贡献的,一部分是已经计算完对后面的询问贡献的(不会改变/不会影响的)。

        然后两部分递归求解。

      以区间k大为例,若有多个询问,可以整体二分。

      把比二分答案mid大的数加1,check就看区间和是否大于k。

      递归求解,直到答案l==r为止。

      所以整体二分大概就长成这样:

      solve(optl,optr,l,r){

        if(optl>optr)return;

        if(l==r){

          for(int i=optl;i<=optr;++i)

            if(opt[i]是询问)

              Ans[opt[i].id]=l;

          return;

        }

        ……

        solve(optl,optl+tot1-1,l,mid);

        solve(optl+tot1,optr,mid+1,r);

      }

      中间判断一般就是树状数组?线段树?差分?……

      按照XHR犇所说,整体二分复杂度要和操作序列的长度线性相关。

      也就是说当前长度是len那么复杂度就要和len线性相关,不然破坏复杂度。

      回头来看这道题。

      答案明显是满足可二分性的。那么试着整体二分。

      按照套路,solve(optl,optr,l,r)。

      搞出一个mid,来看看怎么check。

      对于加边操作,如果c>mid,就区间+1。

      然后这个操作就丢进右边队列。

      如果c<=mid,则对答案无贡献,丢进左边。

      对于询问,就是查询区间和sum。

      如果sum<k,意味着第k大一定不比mid大,丢进左边。

      否则k-=sum,丢进右边(想想主席树的操作)。

      然后就是递归处理了。

      对于上面那个数据结构,树状数组肯定比线段树好是吧......

      我还去%了一波Me2O3学姐的树状数组区间修改......顺便A了codevs的线段树板子。

      HoHo。

      然后就把板子压进结构体我会说

    #include    <iostream>
    #include    <cstdio>
    #include    <cstdlib>
    #include    <algorithm>
    #include    <vector>
    #include    <cstring>
    #include    <queue>
    #include    <complex>
    #include    <stack>
    #define LL long long int
    #define dob double
    using namespace std;
    
    const int N = 100010;
    struct Data{
      int type,l,r,c,id;
      bool operator <(const Data &a)const{
        return id<a.id;
      }
    }opt[N],que1[N],que2[N];
    int n,m,tim;LL Ans[N];
    
    struct BIT{
      LL A1[N],A2[N],A3[N];
      int vis1[N],vis2[N],vis3[N];
      inline int lb(int k){
        return k&-k;
      }
      
      inline void update(LL *A,int *vis,int x,int val){
        for(;x<=n;x+=lb(x)){
          if(vis[x]!=tim)A[x]=0;
          A[x]+=val;vis[x]=tim;
        }
      }
      
      inline LL query(LL *A,int *vis,int x,LL ans=0){
        for(;x;x-=lb(x)){
          if(vis[x]!=tim)A[x]=0;
          ans+=A[x];vis[x]=tim;
        }
        return ans;
      }
      
      inline void add(int l,int r,int dt){
        update(A2,vis2,l,dt);update(A2,vis2,r+1,-dt);
        update(A3,vis3,l,dt*l);update(A3,vis3,r+1,-dt*(r+1));
      }
      
      inline LL sum(int l,int r){
        LL sl=A1[l-1]+l*query(A2,vis2,l-1)-query(A3,vis3,l-1);
        LL sr=A1[r]+(r+1)*query(A2,vis2,r)-query(A3,vis3,r);
        return sr-sl;
      }
    }T;
    
    inline int gi(){
      int x=0,res=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
      while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
      return x*res;
    }
    
    /*
      整体二分?
      二分答案c,求:
      solve(optl,optr,ansl,ansr);
      即work出操作在[ol,or],答案在[al,ar]?
      先搞出一个答案mid,再把opt扫一边。
      
      如果是1 
      如果c>mid,就丢进树状数组里面。
      区间+1?
      三个树状数组的事情。
      然后把操作扔进右边QwQ
      如果c<=mid 就不管它
      然后把操作丢进左边。
      
      如果是2
      查询一下和。
      如果sum<k
      则意味着前面的操作里凑不出k个比mid大的数。
      则第k大一定比mid小。
      丢进左边。
      否则 丢进右边。
      然后
      solve(optl,opt1+sizel-1,ansl,mid);
      solve(optr-size2+1,optr,mid+1,ansr);
      直到l==r为止。
    */
    
    inline void solve(int optl,int optr,int l,int r){
      if(optl>optr)return;++tim;
      if(l==r){
        for(int i=optl;i<=optr;++i)
          if(opt[i].type==2)
            Ans[opt[i].id]=l;
        return;
      }
      int mid=((l+r+2*n)>>1)-n,tot1=0,tot2=0;
      for(int i=optl;i<=optr;++i){
        if(opt[i].type==1){
          if(opt[i].c>mid){
            T.add(opt[i].l,opt[i].r,1);
            que2[++tot2]=opt[i];
          }
          else que1[++tot1]=opt[i];
        }
        else{
          LL sum=T.sum(opt[i].l,opt[i].r);
          if(sum<opt[i].c){
            opt[i].c-=sum;
            que1[++tot1]=opt[i];
          }
          else que2[++tot2]=opt[i];
        }
      }
      int k=optl;
      for(int i=1;i<=tot1;++i)opt[k++]=que1[i];
      for(int i=1;i<=tot2;++i)opt[k++]=que2[i];
      solve(optl,optl+tot1-1,l,mid);
      solve(optr-tot2+1,optr,mid+1,r);
    }
    
    int main()
    {
      freopen("K大数查询.in","r",stdin);
      freopen("K大数查询.out","w",stdout);
      n=gi();m=gi();
      for(int i=1;i<=m;++i){
        opt[i].type=gi();
        opt[i].l=gi();opt[i].r=gi();
        opt[i].c=gi();opt[i].id=i;
      }
      solve(1,m,-n,n);sort(opt+1,opt+m+1);
      for(int i=1;i<=m;++i)
        if(opt[i].type==2)
          printf("%lld
    ",Ans[i]);
      fclose(stdin);fclose(stdout);
      return 0;
    }
    K大数查询
  • 相关阅读:
    Hadoop环境搭建2_hadoop安装和运行环境
    Hadoop环境搭建1_JDK+SSH
    Linux5_环境变量
    Linux4_文件操作
    Linux3_文件系统
    Linux2_小技巧
    Linux1_Ubuntu的安装
    PhoneGap移动开发框架
    iOS 使用GitHub托管代码(github desktop使用)
    MRC和ARC混编
  • 原文地址:https://www.cnblogs.com/fenghaoran/p/7429950.html
Copyright © 2011-2022 走看看