题意
- 给你一个数组和一个数k,然后有m个查询,每次给你l,r,问你[l , r]区间内有多少对(i,j),使得a[i] ^ a[i+1] … ^ a[j] = k
思路
- pref[i]记录a[0] ^ a[1] ^ … ^a[i]的值
- 区间[l, r]的解 = 统计(i, j)使得pref[i-1] ^ pref[j] = k的个数。(因为,a ^ b ^ a = b,所以所有小于i的数被以后两次后就相当于没被异或,而i~j则被异或了一次)
- 然后,为了方便实现,我们换一种说法,相当于统计在[l-1, r]区间内有多少(i, j),使得pref[i] ^ pref[j] = k
- 当然,因为有m个查询我们不能简单的暴力求解,所以这里用到了莫队算法
- 这里给两个我自学时参考的文章:
- http://wenku.baidu.com/link?url=id9fDlyKDaAINJtr6Iq3SYY97VOvec4cdo5QazWreZX3jCNFJU-gRZEreLicE2zoYWjpY4YCe1HmwewpH8bufjeb_aivNOuWcUp0HPlZzCi
- http://blog.csdn.net/bossup/article/details/39236275
- 基本想法就是,离线算法,存储所有的查询,然后对查询进行某种方法的排序,之后暴力的去算
- 但是,这里的暴力也不太一样,是当算出了[l, r]区间的解后,左右移动l 和 r, 来算出[l’ , r’]的解
- 所以,它要求我们可以很快的从[l, r]的解算出, [l-1, r], [l+1, r], [l, r-1], [l, r+1]的解
- 这里用cnt数组来辅助,cnt[v] 表示 [l-1, r]区间内有cnt[v]个pref[i] = v的数, 然后我们就可以很快的算出刚才所述的解,具体方法,详见代码,要注意的是更新ans和cnt的顺序是有要求的,需要仔细考虑
- 最后说个坑点,cnt数组不能开小了。。。因为1e6的数异或之后可不止1e6。。我居然被这个点坑了一个上午。。。
实现
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6+6;
typedef long long ll;
const int B = 316;
int a[maxn];
int pref[maxn];
struct Node{
int l,r,id;
}query[maxn];
int cnt[maxn];
ll Ans[maxn];
bool cmp(Node x, Node y){
if (x.l / B != y.l / B)
return x.l < y.l;
return x.r < y.r;
}
int main(){
int n,m,k;
ios::sync_with_stdio(false);
cin>>n>>m>>k;
for (int i=1;i<=n;i++)
{
cin>>a[i];
}
for (int i=1;i<=n;i++){
pref[i] = a[i] ^ pref[i-1];
}
for (int i=0;i<m;i++){
int l,r;
cin>>query[i].l>>query[i].r;
query[i].id = i;
}
sort(query,query+m,cmp);
int l = 0, r = 1;
ll ans = a[1] == k ? 1 : 0;
cnt[pref[1]]++;
cnt[0]++;
for (int i=0; i < m; i++){
while (l < query[i].l - 1){
cnt[pref[l]]--;
ans -= (ll)cnt[pref[l] ^ k];
l++;
}
while (l > query[i].l - 1){
l--;
ans += (ll)cnt[pref[l] ^ k];
cnt[pref[l]]++;
}
while (r > query[i].r){
cnt[pref[r]]--;
ans -= (ll)cnt[pref[r] ^ k];
r--;
}
while (r < query[i].r){
r++;
ans += (ll)cnt[pref[r] ^ k];
cnt[pref[r]]++;
}
Ans[query[i].id] = ans;
}
for (int i=0;i<m;i++){
cout << Ans[i] << endl;
}
return 0;
}