题目描述
已知一个长度为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。
首先异或和满足前缀,也就是说设val[i]为a[1]^a[2]^...^a[i],那么a[i]^a[i+1]^...^a[j]=val[j]^val[i-1]
而且异或不仅满足交换律,而且对于a^b=c时,a^c=b,b^c=a这两个式子同样成立
那么就好做了,假设当前i到j这个子串的异或和为k,就说明val[j]^val[i-1]=k,也就是val[i-1]^k=val[j],val[j]^k=val[i-1]
然后在区间转移的时候,设flagi]为当前区间值为i的前缀有多少个,然后对于增加序列长度的操作,假设新加的位置为r+1,我们先将flag[val[r+1]]++,然后求出ans+=flag[val[r+1]^k],左边扩展也是如此,不过注意,向左扩展时,对ans的更新是用val[l-1]的,因为是val[j]与val[i-1]可以满足前缀
而且向右扩展的时候,如果val[r+1]^k=val[l-1]的话,ans++,因为我们更新的时候没有计算[l...r+1]区间的影响,所以要维护一下
而对于区间缩小的情况,就ans先减,再更新flag,因为要先消除贡献再减flag,其它步骤类似就好了
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,block;
int val[100050],part[100050];
int ans,flag[100050];
struct node
{
int l,r,id;
int ans;
}query[100050];
bool cmp1(node x,node y)
{
if(part[x.l]==part[y.l])
{
return x.r<y.r;
}
return x.l<y.l;
}
bool cmp2(node x,node y)
{
return x.id<y.id;
}
void update(int pos,int op)
{
if(!op)
{
flag[val[pos]]--;
ans-=flag[val[pos]^k];
}
else
{
ans+=flag[val[pos]^k];
flag[val[pos]]++;
}
}
void fun()
{
int i,l=1,r=0;
flag[0]=1;
for(i=1;i<=m;i++)
{
while(r<query[i].r)
{
update(++r,1);
}
while(r>query[i].r)
{
update(r--,0);
}
while(l<query[i].l)
{
update(l-1,0);
l++;
}
while(l>query[i].l)
{
l--;
update(l-1,1);
}
query[i].ans=ans;
}
}
int main()
{
scanf("%d %d %d",&n,&m,&k);
block=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
val[i]^=val[i-1];
part[i]=(i-1)/block;
}
for(int i=1;i<=m;i++)
{
scanf("%d %d",&query[i].l,&query[i].r);
query[i].id=i;
}
sort(query+1,query+1+m,cmp1);
fun();
sort(query+1,query+1+m,cmp2);
for(int i=1;i<=m;i++)
{
printf("%d\n",query[i].ans);
}
return 0;
}