题意大致是说,给出一个长为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][l−1,r],[l+1,r],[l,r+1],[l,r−1] 的答案,那么可以在 O(n√n) 的复杂度内求出所有询问的答案。
降低复杂度的环节:离线处理询问,对每个询问按照 l/sz为第一关键字,r为第二关键字 由小到大排序,这样使复杂度降为了
O(n√n) ,证明略。
下面附上来自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; }