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
    */
  • 相关阅读:
    ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— 小车按键启动和蜂鸣器报警
    ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— 小车指定花式动作
    ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— 小车指定花式动作
    ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— 小车指定花式动作
    ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— 小车前后左右综合实验
    ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— 小车前后左右综合实验
    ZYAR20A 亚克力2驱 蓝牙 298寻迹避障机器人 —— 小车前后左右综合实验
    asp中设置session过期时间方法总结
    asp中设置session过期时间方法总结
    ASP.NET关于Session_End触发与否的问题
  • 原文地址:https://www.cnblogs.com/rrsb/p/14719434.html
Copyright © 2011-2022 走看看