思路:
如何求出任意一段的区间异或值是个问题。
如果a[i] ^ a[i+1] ^ … ^ a[j] = k 相当于 s[i-1]^s[j]==k, s[]为异或前缀和,那么就是求一个区间的前缀和s[i] ^ s[j]的对数。
莫队转移一下就行了。
WA点:区间前缀和可以比k大, 保存前缀和的数字应该开大一点。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//由块号寻找第一个块元素的下标
#define LL(x) ((x-1)*Len+1)
const int maxn=1<<20;
struct node
{
int L, R, k;
}q[maxn];
int pos[maxn];
LL ans[maxn];
LL flag[maxn];//保存当前区间前缀和的个数
int a[maxn];
bool cmp(node a, node b)
{
if(pos[a.L]==pos[b.L])
{
return a.R<b.R;
}
return pos[a.L]<pos[b.L];
}
int n, m, k, Len;
int L=1, R=0;
LL ANS=0;
void add(int x)
{
//先加防止 a[x]^k==a[x] 重复加上
ANS+=flag[a[x]^k];
flag[a[x]]++;
}
void del(int x)
{
//先减防止 a[x]^k==a[x] 重复减去
flag[a[x]]--;
ANS-=flag[a[x]^k];
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
Len=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]=a[i]^a[i-1];
pos[i]=i/Len;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].L,&q[i].R);
q[i].k=i;
}
sort(q+1, q+1+m, cmp);
flag[0]=1;//为了统计a[i]=k
for(int i=1;i<=m;i++)
{
while(L<q[i].L)
{
del(L-1);
L++;
}
while(L>q[i].L)
{
L--;
add(L-1);
}
while(R<q[i].R)
{
R++;
add(R);
}
while(R>q[i].R)
{
del(R);
R--;
}
ans[q[i].k]=ANS;
}
for(int i=1;i<=m;i++)
{
cout<<ans[i]<<endl;
}
return 0;
}