Codeforces 617E: XOR and Favorite Number(莫队算法)

题目链接

题意大致是说,给出一个长为n(n<=1e5)的数组,给定一个k(k<=1e6),给出m(m<=1e5)个询问,每组询问中回答 从a_l到a_r有多少个连续的子序列满足异或和等于k

这里采用莫队的方法

使用普通莫队的前提:对于序列上的区间询问问题,如果从 [l, r][l,r] 的答案能够 O(1) 扩展到 [l - 1, r], [l + 1, r],[l, r + 1],[l, r - 1][l1,r],[l+1,r],[l,r+1],[l,r1] 的答案,那么可以在 O(nn​​) 的复杂度内求出所有询问的答案。

降低复杂度的环节:离线处理询问,对每个询问按照 l/sz为第一关键字,r为第二关键字 由小到大排序,这样使复杂度降为了
O(nn​​) ,证明略。

下面附上来自jlx的代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int maxn=1e5+7;
const int maxv=1<<20;
int pre[maxn],pos[maxn];
int cnt[maxv],k;
LL ans[maxn];
int L=1,R=0;
LL Ans;

struct Query
{
    int l,r,id;
    bool operator<(const Query& b) const    //pos[l]为第一关键字,r为第二 
    {
        if(pos[l]==pos[b.l]) return r<b.r;
        return pos[l]<pos[b.l];
    }                                        //按照如此排出来的顺序,可以降低复杂度 
}Q[maxn];

void add(int x)
{
    Ans+=cnt[pre[x]^k];
    ++cnt[pre[x]];
}
void del(int x)
{
    --cnt[pre[x]];
    Ans-=cnt[pre[x]^k];
}

int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m>>k;
    int sz=sqrt(n);    //sz:块的大小 
    for(int i=1;i<=n;i++)
    {
        cin>>pre[i];
        pre[i]^=pre[i-1];
        pos[i]=i/sz;
    }
    for(int i=1;i<=m;i++)
    {
        cin>>Q[i].l>>Q[i].r;
        Q[i].id=i;
    }
    sort(Q+1,Q+1+m);
    Ans=0;
    cnt[0]=1;
    for(int i=1;i<=m;i++)
    {
        while(L>Q[i].l)
        {
            --L;
            add(L-1);
        }
        while(L<Q[i].l)
        {
            del(L-1);
            ++L;
        }
        while(R>Q[i].r)
        {
            del(R);
            --R;
        }
        while(R<Q[i].r)
        {
            ++R;
            add(R);
        }
        ans[Q[i].id]=Ans;
    }
    for(int i=1;i<=m;i++)
        cout<<ans[i]<<endl;
}
原文地址:https://www.cnblogs.com/Just--Do--It/p/7200191.html