题意:n个数,m次查询操作,问你在区间(li,ri)中有多少个子序列的异或和为k
详解:异或为二进制不进位加法,a^b=c,a^c=b,b^c=a;
首先异或和满足前缀,也就是说设sum[i]为a[1]^a[2]^...^a[i],那么a[i]^a[i+1]^...^a[j]=sum[j]^sum[i-1]
而且异或不仅满足交换律,而且对于a^b=c时,a^c=b,b^c=a这两个式子同样成立
那么就好做了,假设当前i到j这个子串的异或和为k,就说明sum[j]^sum[i-1]=k,也就是sum[i-1]^k=sum[j],sum[j]^k=sum[i-1]
然后在区间转移的时候,设cnt[i]为当前区间值为i的前缀有多少个,然后对于增加序列长度的操作,假设新加的位置为r+1,我们先将cnt[sum[r+1]]++,然后求出ans+=cnt[sum[r+1]^k],左边扩展也是如此,不过注意,向左扩展时,对ans的更新是用sum[l-1]的,因为是sum[j]与sum[i-1]可以满足前缀
而且向右扩展的时候,如果sum[r+1]^k=sum[l-1]的话,ans++,因为我们更新的时候没有计算[l...r+1]区间的影响,所以要维护一下
而对于区间缩小的情况,就ans先减,再更新cnt,因为要先消除贡献再减cnt,其它步骤类似就好了
#include<bits/stdc++.h>
#include<algorithm>
#include <math.h>
using namespace std;
#define ll long long
typedef pair<ll ,ll>pa;
#define pre(i,x,n) for(int i=x;i<=n;i++)
#define rep(i,n,x) for(int i=n;i>=x;i--)
//priority_queue<ll ,vector<ll>,greater<ll> >q;
ll n,m,a[100010],tem,ans=0,cnt[110010]={0},k,sum[100010];
struct mo
{
int l,r,id;
}q[100010];
int cmp(mo x,mo y)
{
if(x.l/tem==y.l/tem)
return x.r<y.r;
return x.l<y.l;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
tem=sqrt(n);
pre(i,1,n){scanf("%lld",&a[i]),a[i]=a[i]^a[i-1];}
pre(i,1,m)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
int l=1,r=0;
pre(i,1,m)
{
while(r<q[i].r)
{
r++;
cnt[a[r]]++;
ans+=cnt[a[r]^k];
if((a[r]^k)==a[l-1])
ans++;
}
while(r>q[i].r)
{
ans-=cnt[a[r]^k];
cnt[a[r]]--;
if((a[r]^k)==a[l-1])
ans--;
r--;
}
while(l<q[i].l)
{
ans-=cnt[a[l-1]^k];
cnt[a[l]]--;
l++;
}
while(l>q[i].l)
{
l--;
cnt[a[l]]++;
ans+=cnt[a[l-1]^k];
}
sum[q[i].id]=ans;
}
pre(i,1,m){printf("%lld\n",sum[i]);}
return 0;
}