problem
已知一个长度为 n n n 的整数数列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,给定查询参数 l , r l,r l,r,问在 a l , a l + 1 , . . . , a r a_l,a_{l+1},...,a_r al,al+1,...,ar 区间内,有多少子序列满足异或和等于 k k k。也就是说,对于所有的 x , y ( l ≤ x ≤ y ≤ r ) x,y (l ≤ x ≤ y ≤ r) x,y(l≤x≤y≤r),能够满足 a x ⊕ a x + 1 ⊕ ⋯ ⊕ a y = k a_x \oplus a_{x+1} \oplus \cdots \oplus a_y = k ax⊕ax+1⊕⋯⊕ay=k 的 x , y x,y x,y 有多少组。
数据范围: 1 ≤ n , m ≤ 1 0 5 1 ≤ n, m ≤ 10^5 1≤n,m≤105, 0 ≤ k , a i ≤ 1 0 5 0 ≤ k, a_i ≤ 10^5 0≤k,ai≤105。
solution
这题其实挺简单的。
我们先求一个前缀异或和,把问题转化成求 [ l − 1 , r ] [l-1,r] [l−1,r] 中有多少对异或和为 k k k 的点。
注意到 k k k 是全局的,也即,每次询问不是完全独立的,我们也许可以从上一次询问得到一些信息来优化复杂度。
这便启示我们考虑莫队算法了。
维护一个桶(记作 c n t cnt cnt),插入一个数 x x x 时,我们在 k ⊕ x k\oplus x k⊕x 位置 + 1 +1 +1,同时把 c n t x cnt_x cntx 加进答案里。删除反着做即可。
时间复杂度 O ( n n ) O(n\sqrt n) O(nn)。
code
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m,k,S,Xor[N],in[N],ans[N],cnt[N];
struct Query{int l,r,id;}Q[N];
bool operator<(const Query &p,const Query &q){
return (in[p.l]==in[q.l])?p.r<q.r:in[p.l]<in[q.l];
}
void Del(int pos,int &now) {now-=cnt[Xor[pos]],cnt[k^Xor[pos]]--;}
void Add(int pos,int &now) {now+=cnt[Xor[pos]],cnt[k^Xor[pos]]++;}
int main(){
scanf("%d%d%d",&n,&m,&k),S=sqrt(n);
for(int i=1,x;i<=n;++i) scanf("%d",&x),Xor[i]=Xor[i-1]^x;
for(int i=1;i<=m;++i) scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].l--,Q[i].id=i;
for(int i=1;i<=n;++i) in[i]=(i-1)/S+1;
sort(Q+1,Q+m+1);
int L=1,R=0,now=0;
for(int i=1;i<=m;++i){
while(L<Q[i].l) Del(L++,now);
while(L>Q[i].l) Add(--L,now);
while(R<Q[i].r) Add(++R,now);
while(R>Q[i].r) Del(R--,now);
ans[Q[i].id]=now;
}
for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}