zoukankan      html  css  js  c++  java
  • 二次离线莫队

          正常的莫队如果其单次插入或者删除的时间开销是$f(n)$,那么我们的莫队的时间开销就是$O(f(n)*nsqrt n)$或者$O((f(n)*n)^{1.5})$次方,当$f(n)$过大,比如$log^2(n)$,那么时间开销就无法接受。二次离线莫队可以把时间开销降到$O(nf(n)+n^{1.5})$ 

          设 $x$ 对区间 $[l,r]$ 的贡献为 $f(x,[l,r])$ 

          我们考虑区间端点变化对答案的影响: 以$[l..r]$ 变成 $[l..(r+k)]$为例

          增量是 $sum_{x=r+1}^{r+k} f(x,[l,x-1])=sum_{x=r+1}^{r+k}  f(x,[1,x-1])-f(x,[1,l-1])  $ (这一步要求询问有可加性,或者经过处理后有可加性)

          前半部分自然可以O(1)处理,后面部分本质上是O(N^{1.5})次的对$f(x,[1,y])$的询问,只要能对给定x进行一些预处理后可以$O(1)$返回$f(x,[1,y])$,就得到一个时间开销是$O(N^{1.5}+Nf(N))$,空间开销$O(N^{1.5})$的优秀做法

          但空间开销$O(N^{1.5})$在有些时候也是不可接受的,我们试图进行空间优化。我们发现每次询问时$y$形成区间,每次存储用区间存就能降到$O(N)$了

         我们来看2道例题:

            洛谷P4887 

               处理前缀询问的时候,有异或运算的交换律,即$a xor b=c  Leftrightarrow  a xor c=b$ 开一个桶$t$,$t[i]$表示当前前缀中与$i$异或有$k$个数位为1的数有多少个。 则每加入一个数$a[i]$,对于所有$popcount(x)==k$的x

           $++t[a[i] xor x]$即可。

     

    //#pragma GCC optimize(3,"Ofast","inline")
    //#pragma GCC optimize("-Ofast","-funroll-all-loops")
    #include<bits/stdc++.h>
    //#define getchar nc
    #define sight(c) ('0'<=c&&c<='9')
    #define swap(a,b) a^=b,b^=a,a^=b
    #define LL long long
    #define debug(a) cout<<#a<<" is "<<a<<"
    "
    #define dput(a) puts("a")
    #define eho(x) for(int i=head[x];i;i=net[i])
    #define fi first
    #define se second
    #define N  100007
    inline char nc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    template <class T>
    inline void read(T &x){// unsigned
        static char c;
        for (c=getchar();!sight(c);c=getchar());
        for (x=0;sight(c);c=getchar())x=x*10+c-48;
    }
    template <class T> void write(T x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);}
    template <class T> inline void writeln(T x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('
    '); }
    template <class T> inline void writel(T x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); }
    using namespace std;
    struct Node{
        int x,y,id; LL ans; 
    }Q[N];
    int a[N],blo[N],n,m,k,SIZ;
    int A[N],cnt[N],pre[N],now[N];
    vector<int> buf;
    vector<array<int,3>> V[N];
    LL ans[N]; 
    signed main() {
        #ifdef LOCAL
        //    freopen("test.in", "r", stdin);
        //    freopen("test.out", "w", stdout);
        #endif
        read(n); read(m); read(k);
        for (int i=1;i<=n;i++) read(a[i]);
        SIZ=sqrt(n);
        for (int i=1;i<=n;i++) blo[i]=(i-1)/SIZ+1;
        for (int i=1;i<=m;i++) {
            read(Q[i].x); read(Q[i].y); 
            Q[i].id=i;
        }
        sort(Q+1,Q+m+1,[&](const Node& A,const Node& B){return blo[A.x]==blo[B.x]?A.y<B.y:A.x<B.x;});
        for (int i=1;i<16384;i++) cnt[i]=cnt[i-(i&-i)]+1;
        for (int i=0;i<16384;i++) if (cnt[i]==k) buf.push_back(i);
        for (int i=1;i<=n;i++) {
            pre[i]=A[a[i]];  //F(x,1,x-1) 
            for (auto x:buf) A[x^a[i]]++;
            now[i]=A[a[i]];
        }
        for (int i=1,L=1,R=0;i<=m;i++) {
            int l=Q[i].x,r=Q[i].y;
            if (L>l) V[R].push_back({l,L-1,i});
            while (L>l) --L,Q[i].ans-=now[L];//need -f(x,x,x)
            if (R<r) V[L-1].push_back({R+1,r,-i});
            while (R<r) ++R,Q[i].ans+=pre[R];
            if (L<l) V[R].push_back({L,l-1,-i});//need +f(x,x,x)
            while (L<l) Q[i].ans+=now[L],L++;
            if (R>r) V[L-1].push_back({r+1,R,i});
            while (R>r) Q[i].ans-=pre[R],R--; 
        }
        memset(A,0,sizeof A);
        for (int i=1,id,l,r,op;i<=n;i++) {
            for(auto x:buf) ++A[a[i]^x];
            for (auto x:V[i]) {
                l=x[0]; r=x[1]; op=x[2]<0?-1:1; id=x[2]*op;
                for (int j=l;j<=r;j++) {
                    Q[id].ans+=op*A[a[j]];
                }
            }
        } 
        for (int i=1;i<=m;i++) Q[i].ans+=Q[i-1].ans,ans[Q[i].id]=Q[i].ans;
        for (int i=1;i<=m;i++) writeln(ans[i]);
        return 0;
    }

    洛谷P5501

    我们考虑$a[x]$对$[L,R]$ 的贡献$f(x,[L,R])$,有$f(x,[L,R])=cnt_{x,[L,R]}+a[x]*(1+num_{x,[L,R]})$,$cnt_{x,[L,R]}$表示在$[L,R]$中比$a[x]$大的数和,$num_{x,[L,R]}$表示比$a[x]$小的数的和,我们发现$f(x,[L,R])$不满足可加性,令$g(x,[L,R])=cnt_{x,[L,R]}+a[x]*num_{x,[L,R]}$就满足可加性了。

    有几个要注意的点,维护前缀和不能用树状数组,树状数组不能$O(1)$查询,而是要用分块。这题卡常数,结构体中调用函数比较慢,在最后统计后半部分的贡献时直接调用Tree的que会导致常数大到T飞,要全部写开来,还有就是要用莫队的奇偶优化,能快一半。

    //#pragma GCC optimize(3,"Ofast","inline")
    #pragma GCC optimize("-Ofast","-funroll-all-loops")
    #include<bits/stdc++.h>
    #define getchar nc
    #define sight(c) ('0'<=c&&c<='9')
    #define swap(a,b) a^=b,b^=a,a^=b
    #define LL long long
    #define debug(a) cout<<#a<<" is "<<a<<"
    "
    #define dput(a) puts("a")
    #define eho(x) for(int i=head[x];i;i=net[i])
    #define fi first
    #define se second
    #define N 500007
    #define M 100007
    inline char nc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    template <class T>
    inline void read(T &x){// unsigned
        static char c;
        for (c=getchar();!sight(c);c=getchar());
        for (x=0;sight(c);c=getchar())x=x*10+c-48;
    }
    template <class T> void write(T x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);}
    template <class T> inline void writeln(T x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('
    '); }
    template <class T> inline void writel(T x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); }
    using namespace std;
    struct Node{
        int x,y,id; LL ans; 
    }Q[N];
    int a[N],blo[N],n,m,k,SIZ;
    int A[N],cnt[N];
    vector<int> buf;
    vector<array<int,3>> V[N];
    LL ans[N],pre[N],now[N],sum[N]; 
    struct Tree{
        LL q[M],g[M];
        int BLO;
        #define L(x) (x&-x) 
        LL que(LL x){
            return g[x/BLO]+q[x];
        } 
        void add(LL x,LL dla) {
            int s1=x/BLO,s2=(M-1)/BLO;
            for (int i=x;i<s1*BLO+BLO&&i<M;i++) q[i]+=dla;
            for (int i=s1+1;i<=s2;i++) g[i]+=dla;
        }
        void clear() {
            memset(q,0,sizeof q);
            memset(g,0,sizeof g);
            BLO=sqrt(M);
    //        for (int i=0;i<M;i++) id[i]=i/BLO;
        }
    }Q1,Q2;
    signed main() {
    //    #ifdef LOCAL
        freopen("test.in", "r", stdin);
        freopen("test.out", "w", stdout);
    //    #endif
        read(n); read(m);
        Q1.clear(); Q2.clear();
        for (int i=1;i<=n;i++) read(a[i]),sum[i]=sum[i-1]+a[i];
        SIZ=sqrt(n);
        for (int i=1;i<=n;i++) blo[i]=(i-1)/SIZ+1;
        for (int i=1;i<=m;i++) {
            read(Q[i].x); read(Q[i].y); 
            Q[i].id=i;
        }
        sort(Q+1,Q+m+1,[&](const Node& A,const Node& B){return blo[A.x]==blo[B.x]?A.y<B.y:A.x<B.x;});for (int i=1;i<=n;i++) {
            pre[i]=1ll*a[i]*(Q1.que(a[i]-1))+Q2.que(M-1)-Q2.que(a[i]);
            Q1.add(a[i],1); Q2.add(a[i],a[i]);
            now[i]=1ll*a[i]*(Q1.que(a[i]-1))+Q2.que(M-1)-Q2.que(a[i]);
        } for (int i=1,L=1,R=0;i<=m;i++) {
            int l=Q[i].x,r=Q[i].y;
            if (L>l) V[R].push_back({l,L-1,i});
            while (L>l) --L,Q[i].ans-=now[L];//need -f(x,x,x)
            if (R<r) V[L-1].push_back({R+1,r,-i});
            while (R<r) ++R,Q[i].ans+=pre[R];
            if (L<l) V[R].push_back({L,l-1,-i});//need +f(x,x,x)
            while (L<l) Q[i].ans+=now[L],L++;
            if (R>r) V[L-1].push_back({r+1,R,i});
            while (R>r) Q[i].ans-=pre[R],R--; 
        }
        Q1.clear(); Q2.clear();
        int BLO=Q1.BLO;
        for (int i=1,id,l,r,op;i<=n;i++) {
            Q1.add(a[i],1); Q2.add(a[i],a[i]);
            for (auto x:V[i]) {
                l=x[0]; r=x[1]; op=x[2]<0?-1:1; id=x[2]*op;
                for (int j=l;j<=r;j++) {
                    Q[id].ans+=1ll*op*(1ll*a[j]*(Q1.q[a[j]-1]+Q1.g[(a[j]-1)/BLO])+(Q2.q[M-1]+Q2.g[(M-1)/BLO])-(Q2.q[a[j]]+Q2.g[a[j]/BLO]));
                }
            }
        } 
        for (int i=1;i<=m;i++) Q[i].ans+=Q[i-1].ans,ans[Q[i].id]=Q[i].ans+sum[Q[i].y]-sum[Q[i].x-1];
        for (int i=1;i<=m;i++) writeln(ans[i]);
        return 0;
    } 
    /*
    4 1
    1 2 2 3
    2 3
    */
    /*
    4 1
    1 2 2 3
    2 3
    */
  • 相关阅读:
    Gulp插件less的使用
    并行与并发的区别
    原生javascript实现异步的7种方式
    原生JS实现bind()函数
    职责链模式
    Nginx如何启用ETag,提高访问速度
    前端性能优化:配置ETag
    迭代器模式
    JS里关于事件的常被考察的知识点:事件流、事件广播、原生JS实现事件代理
    cdn网络加速
  • 原文地址:https://www.cnblogs.com/rrsb/p/14719434.html
Copyright © 2011-2022 走看看