zoukankan      html  css  js  c++  java
  • [ AHOI 2013 ] 作业 & [ BZOJ 3809 ] Gty的二逼妹子序列

    (\)

    Description


    给出一个长为 (n) 的数列 (A)(k),多次询问:

    对于一个区间 ([L_i,R_i]),问区间内有多少个数在 ([a_i,b_i]) 内,以及这些数共有多少个不同的值。

    • (nle 10^5,mle 10^6)

    By wangyisong1996加强数据

    (\)

    Solution


    看到最后一行心都凉了......

    真的佩服松松松的速度 不知道比我高到哪里去了

    卡常卡到想吐(见代码部分吧)`

    (\)

    首先肯定莫队,然后考虑第一问。

    直接离散化之后权值树状数组,每次新加进来一个就在对应权值处 (+1) ,删除 (-1)

    对于第二问,我们无法确定当前区间里有多少个是个问题。

    于是直接再开一个辅助桶,以及另一个询问用的权值树状数组。

    加入时若以前没有(桶为空),则在这一权值处 (+1) ,删除时若桶清空成 (0) ,则在对应权值处 (-1)

    回答就直接区间减法即可。

    注意 lower_boundupper_bound 的时候可能越界,所以要加上哨兵。

    (\)

    Code


    还是说一下卡常用了点啥吧.....

    • BZOJ 专用的 int 优化

    • 读入 & 输出优化

    • 莫队对询问排序时的奇偶性讨论

    • 很迷的块的大小,实测 (frac{N}{sqrt M}) 最快

      关于这个粘一个洛谷日报上的证明

      我们设块长度为 (S) ,那么对于任意多个在同一块内的询问,挪动的距离就是 (n),一共(frac{n}{S}) 个块,移动的总次数就是(frac{n^2}{S}),移动可能跨越块,所以还要加上一个 (mS) 的复杂度,总复杂度为 (O(frac{n^2}{S}+mS)) ,我们要让这个值尽量小,(S)(frac{n}{sqrt{m}}) 是最优的,此时复杂度为

      [O(frac{n^2}{frac{n}{sqrt{m}}}+m(frac{n}{sqrt{m}}))=O(nsqrt{m}) ]

    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 100010
    #define M 1000010
    #define Rg register
    #define gc getchar
    using namespace std;
     
    inline int rd(){
      int x=0; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
      return f?-x:x;
    }
     
    inline void print(int x){
      Rg int y=10,len=1;
      while(x>=y){y=(y<<1)+(y<<3);++len;}
      while(len--){y/=10;putchar('0'+x/y);x%=y;}
    }
     
    int n,m,tot,ans,ans1[M],ans2[M],s[N],bl[N],cnt[N],tmp[N];
     
    struct Q{int l,r,L,R,id;}q[M];
     
    inline bool cmp(Q x,Q y){
      if(bl[x.l]!=bl[y.l]) return bl[x.l]<bl[y.l];
      return bl[x.l]&1?x.r<y.r:x.r<y.r;
    }
     
    struct BIT{
      int c[N];
      inline int lowbit(int x){return x&-x;}
      inline void add(int p,int x){
        for(;p<=n;p+=lowbit(p)) c[p]+=x;
      }
      inline int query(int p){
        int res=0;
        for(;p;p-=lowbit(p)) res+=c[p];
        return res;
      }
    }bitcnt,bitsum;
     
    inline void add(int p){
      ++cnt[s[p]];
      bitsum.add(s[p],1);
      if(cnt[s[p]]==1) bitcnt.add(s[p],1);
    }
     
    inline void del(int p){
      --cnt[s[p]];
      bitsum.add(s[p],-1);
      if(!cnt[s[p]]) bitcnt.add(s[p],-1);
    }
     
    int main(){
      n=rd(); m=rd();
      int t=n/sqrt(m);
      for(Rg int i=1;i<=n;++i){
        tmp[i]=s[i]=rd();
        bl[i]=i/t+1;
      }
      sort(tmp+1,tmp+1+n);
      for(Rg int i=1;i<=n;++i){
        tmp[++tot]=tmp[i];
        while(tmp[i+1]==tmp[i]) ++i;
      }
      tmp[++tot]=2000000000;
      for(Rg int i=1;i<=n;++i) s[i]=lower_bound(tmp+1,tmp+1+tot,s[i])-tmp;
      for(Rg int i=1;i<=m;++i){
        q[i].l=rd(); q[i].r=rd(); q[i].id=i;
        q[i].L=lower_bound(tmp+1,tmp+1+tot,rd())-tmp;
        q[i].R=upper_bound(tmp+1,tmp+1+tot,rd())-tmp-1;
      }
      sort(q+1,q+1+m,cmp);
      int l=1,r=1;
      cnt[s[1]]=1;
      bitcnt.add(s[1],1);
      bitsum.add(s[1],1);
      for(Rg int i=1;i<=m;++i){
        if(q[i].L>q[i].R){
          ans1[q[i].id]=ans2[q[i].id]=0;
          continue;
        }
        while(l<q[i].l){del(l);++l;}
        while(l>q[i].l){--l;add(l);}
        while(r>q[i].r){del(r);--r;}
        while(r<q[i].r){++r;add(r);}
        ans1[q[i].id]=bitsum.query(q[i].R)-bitsum.query(q[i].L-1);
        ans2[q[i].id]=bitcnt.query(q[i].R)-bitcnt.query(q[i].L-1);
      }
      for(Rg int i=1;i<=m;++i){
        print(ans1[i]); putchar(' ');
        print(ans2[i]); putchar('
    ');
      }
      return 0;
    }
    
  • 相关阅读:
    算法题解:旋转数组的最小数字
    算法题解:连续子数组的最大和及其下标
    算法题解:快速排序算法(维基百科版)
    c++入门之类——进一步剖析
    c++入门之运算符重载
    c++入门之浅入浅出类——分享给很多想形象理解的人
    c++入门之再话内存和引用
    c++入门之引用
    c++入门之内置数组和array比较
    c++入门之结构体初步
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9995496.html
Copyright © 2011-2022 走看看