题意大致是说,给出一个长为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+;
const int maxv=<<;
int pre[maxn],pos[maxn];
int cnt[maxv],k;
LL ans[maxn];
int L=,R=;
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=;i<=n;i++)
{
cin>>pre[i];
pre[i]^=pre[i-];
pos[i]=i/sz;
}
for(int i=;i<=m;i++)
{
cin>>Q[i].l>>Q[i].r;
Q[i].id=i;
}
sort(Q+,Q++m);
Ans=;
cnt[]=;
for(int i=;i<=m;i++)
{
while(L>Q[i].l)
{
--L;
add(L-);
}
while(L<Q[i].l)
{
del(L-);
++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=;i<=m;i++)
cout<<ans[i]<<endl;
}