http://codeforces.com/problemset/problem/617/E
题意:长度为n(1e5)的数组,m(1e5)询问和一个目标值k,每个询问需要你回答对于区间【l,r】,其内有多少对i、j(i<=j)满足a[i]^a[i+1]^...^a[j]==k。
思路:可以使用离线查询,故,可以考虑使用莫队算法。使用之前需要一些脑洞来分析这个问题。
要点分析:
1.对于区间【l,r】,其内的a[i]^a[i+1]^...^a[j]=pre[i-1]^pre[j]。
2.如果a^b=c,那么a^c=b,如果pre[i]^pre[j]=k,那么pre[i]=pre[j]^k。
3.对于区间【l,r】,如果此时其答案为temp,且你知道区间【l-1,r】内的每一个pre值的个数cnt,那么对于【l,r+1】,其答案则为temp+cnt[ pre[r^k] ]。即使O(1)时间内实现了区间的一步移动(其他方向的也是如此)
注意事项:下面代码中update()函数需重点理解,cnt和temp更新的先后顺序不同,重点体会。
AC代码(不用cout输出ans会快300ms):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
using namespace std;
typedef long long LL;
const int maxn=(1e5)+5;
const int maxk=(1e6)*3;
int n,m,k,block;
int pre[maxn],a[maxn],cnt[maxk+5];
LL ans[maxn];
LL temp;
struct node{
int l,r,id,pos;
}q[maxn];
int cmp(const node& a,const node& b){
if(a.pos!=b.pos) return a.pos<b.pos;
return a.r<b.r;
}
void update(int cur,int add){
if(add==1){
temp+=cnt[cur^k];
cnt[cur]++;
}
else{
cnt[cur]--;
temp-=cnt[cur^k];
}
}
void solve(){
temp=0;
if(k==a[1]) temp=1;
cnt[0]++;
cnt[a[1]]++;
int l=1;
int r=1;
for(int i=1;i<=m;i++){
while(l<q[i].l){
update(pre[l-1],-1);
l++;
}
while(l>q[i].l){
l--;
update(pre[l-1],1);
}
while(r<q[i].r){
r++;
update(pre[r],1);
}
while(r>q[i].r){
update(pre[r],-1);
r--;
}
ans[q[i].id]=temp;
}
}
int main (){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
pre[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pre[i]=pre[i-1]^a[i];
}
int block=sqrt(m);
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
q[i].pos=q[i].l/block;
}
sort(q+1,q+m+1,cmp);
memset(cnt,0,sizeof(cnt));
solve();
for(int i=1;i<=m;i++)
printf("%I64d\n",ans[i]);
}
return 0;
}