XOR and Favorite Number
这把CF明显题风不太对,前四道一眼题,最后来个模板题。不过正好给我科普了莫队这个姿势。莫队其实更像是一种思想,而不是一个具体的算法。总结下,它能处理的问题是无修改的区间查询,方法是按总长度的平方根分块,然后将询问排序(先按左端点所属块,再按右端点),离线求解。
这道题,先预处理前缀异或结果,记为 prefix , [l,r] 的答案就是 prefix[l−1] 异或 prefix[r] 。用莫队的时候,就是将左端点固定在块内(当然每次块内移动还是要花 O(n0.5) 的时间去处理的),右端点不断往后推。总复杂度 O(n1.5) 。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,k;
int a[100010];
int prefix[100010];
int cnt[1<<20];
struct query{
int l;
int r;
int block;
int id;
bool operator<(const query &other)const{
if(block!=other.block){
return block<other.block;
}
return r<other.r;
}
}qs[100010];
ll ans [100010];
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
prefix[i] = prefix[i-1]^a[i];
}
int SQRT = sqrt(n);
for(int i=0;i<m;i++){
scanf("%d%d",&qs[i].l,&qs[i].r);
qs[i].block = qs[i].l/SQRT;
qs[i].id = i;
}
sort(qs,qs+m);
int preL;
int preR;
ll tmp = 0;
for(int q=0;q<m;q++){
int L = qs[q].l;
int R = qs[q].r;
if(q==0 || qs[q-1].block!=qs[q].block || L>preR){ //新块或者该区间和上个区间没有交集
memset(cnt,0,sizeof(cnt));
preL=L;
preR=L-1;
tmp=0;
cnt[prefix[qs[q].l-1]]++;
}else{
//块内调整左边,注意代码顺序.
if(L>preL){
for(int i=preL;i<L;i++){
cnt[prefix[i-1]]--;
tmp-=cnt[ prefix[i-1]^k ];
}
}else if(L<preL){
for(int i=preL-1;i>=L;i--){
tmp+=cnt[ prefix[i-1]^k ];
cnt[prefix[i-1]]++;
}
}
preL=L;
}
//右边界往后推
for(int i=preR+1;i<=R;i++){
tmp+=cnt[ prefix[i]^k ];
cnt[prefix[i]]++;
}
preR=R;
ans[qs[q].id] = tmp;
}
for(int i=0;i<m;i++){
printf("%I64d\n",ans[i]);
}
return 0;
}