lixiaotao 2020-01-29
本题是经典的莫队算法
莫队算法是离线查询的一种复杂度优秀的暴力算法。
首先我们需要注意异或的几个性质,相同数字异或等于0,所以我们考虑前缀和,因为从ai-aj的异或值就等于pre[i-1]^pre[j] 前缀和的异或,因为相同部分会抵消。
莫队一般和分块相结合,我们需要维护一个cnt数组,cnt[a[x]]表示异或结果是a[x]的值有多少,这就可以表示贡献度。
几个坑点:
1.异或值可以大于原值,所以首先要注意int,其次数组范围要开大一点
2.为什么在维护add的时候要先算贡献再加,而sub时要先减?
因为有一种特殊情况会使x^k=x,也就是k=0的时候,那么这个时候不能算这个区间,因为这是自身的匹配是空匹配,因为前缀和是pre[i-1]^pre[j],这种情况就是pre[i-1]^pre[i-1]这里面是没有值的。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1<<20;
ll ans[N];
ll cnt[N];
ll pos[N];
ll a[N];
ll res;
int n,m,k;
struct node{
int l,r;
int k;
}q[N];
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];
}
void add(int x){
res+=cnt[a[x]^k]; //贡献
cnt[a[x]]++;
}
void sub(int x){
cnt[a[x]]--;
res-=cnt[a[x]^k];
}
int main(){
int i;
cin>>n>>m>>k;
int block=sqrt(n);
for(i=1;i<=n;i++){
cin>>a[i];
a[i]^=a[i-1];
pos[i]=(i-1)/block+1;
}
for(i=1;i<=m;i++){
cin>>q[i].l>>q[i].r;
q[i].k=i;
}
sort(q+1,q+1+m,cmp);
int l=1;
int r=0;
cnt[0]=1;
for(i=1;i<=m;i++){
while(q[i].l<l){
l--;
add(l-1);
}
while(q[i].r>r){
r++;
add(r);
}
while(q[i].l>l){
sub(l-1);
l++;
}
while(q[i].r<r){
sub(r);
r--;
}
ans[q[i].k]=res;
}
for(i=1;i<=m;i++)
cout<<ans[i]<<endl;
}