题目描述
已知一个长度为n的整数数列a1,a2,…,an,给定查询参数l、r,问在al,al+1,…,ar区间内,有多少子序列满足异或和等于k。也就是说,对于所有的x,y(l≤x≤y≤r),满足ax⊕ax+1⊕⋯⊕ay=k的x,y有多少组。
输入
输入第一行为3个整数n,m,k。第二行为空格分开的n个整数,即a1,a2,…,an。接下来m行,每行两个整数lj,rj,代表一次查询。
输出
输出共m行,对应每个查询的计算结果。
样例输入
4 5 1 1 2 3 1 1 4 1 3 2 3 2 4 4 4
样例输出
4 2 1 2 1
提示
对于30%的数据,1≤n,m≤1000。
对于100%的数据,1≤n,m≤105,0≤k,ai≤105,1≤lj≤rj≤n。
【小结】
这道题算是裸的莫队算法了,我们只需要求一个前缀异或就可以了。需要用到异或的一些性质:
如果区间 [i,j] 的异或值为k,则 sum[j]^sum[i-1]=k ; sum[i-1]^k=sum[j]; sum[j]^k=sum[i-1]
当区间移动时 如果右端点加了一个值t ,sum[t]=x我们是知道的 所以整个区间的答案增加了 cnt[x^k]个
因为 x^x^k=k;
需要注意到 因为判断的时候始终要用到左端点的前一个值 所以我们输入时直接让l–;
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+3;
int a[maxn];
struct node
{
int l,r,id;
}P[maxn];
int res=0;
int k;
int ans[maxn],cnt[maxn];//cnt[i]表示区间中前缀异或值为i的序列个数
int block;
int cmp(node x,node y)
{
if(x.l/block==y.l/block)//同一块中说明l很相近
return x.r<y.r;
else//l差距很大
return x.l<y.l;
}
int dw[maxn];
void update(int x)
{
res+=cnt[x^k];
cnt[x]++;
}
void deletes(int x)
{
res-=cnt[x^k];
cnt[x]--;
}
int main()
{
int n,q;//n个元素 q次询问
cin>>n>>q>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
dw[i]=dw[i-1]^a[i];
}
for(int i=1;i<=q;i++)
{
cin>>P[i].l>>P[i].r;
P[i].id=i;
P[i].l--;
}
block=(int)sqrt(n);
sort(P+1,P+q+1,cmp);
int L=1,R=0;
for(int i=1;i<=q;i++)
{
while(L<P[i].l)deletes(dw[L++]);
while(L>P[i].l)update(dw[--L]);
while(R<P[i].r)update(dw[++R]);
while(R>P[i].r)deletes(dw[R--]);
ans[P[i].id]=res;
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}