题目描述
已知一个长度为n的整数数列
a
1
,
a
2
,
.
.
.
,
a
n
{a_1,a_2,...,a_n}
a1,a2,...,an.
给定查询参数l,r,问在
a
l
,
a
l
+
1
,
.
.
.
,
a
r
{a_l,a_{l+1},...,a_r}
al,al+1,...,ar .
区间内,有多少子序列满足异或和等于k。也就是说,对于所有的x,y (I ≤ x ≤ y ≤ r),能够满足
a
x
⨁
a
x
+
1
⨁
.
.
.
⨁
a
y
=
k
{a_x \bigoplus a_{x+1} \bigoplus ... \bigoplus a_y = k}
ax⨁ax+1⨁...⨁ay=k的x,y有多少组。
输入格式
输入文件第一行,为3个整数n,m,k.(1<=n,m,k,
a
i
{a_i}
ai<=100000)
第二行为空格分开的n个整数,即
a
1
,
a
2
,
.
.
a
n
{a_1,a_2,..a_n}
a1,a2,..an.
接下来m行,每行两个整数
l
j
,
r
j
{l_j,r_j}
lj,rj表示一次查询.
输出格式
输出文件共m行,对应每个查询的计算结果.
思路
维护一个异或前缀和序列,表示1到i的异或和,之后问题就变成了:区间[l-1,r]中,有多少对数异或为k,开个桶记录异或值,直接莫队即可.
(注意要用到异或性质(异或前缀和)zhui[1-(l-1)]^zhui[1-j]=zhui[l-j]这样可以既包含所有的前缀又能通过桶将其O(1)解决.)
代码
#include<bits/stdc++.h>
#define in read()
#define N 100005
using namespace std;
int n,m,k,zhi,he,num;
int a[N],ans[N],zhui[N],tt[1000005];//tt为桶,记录值为(a[i]^k)的序列的数量.
struct node{
int l,r,poi,pos;}q[N];
inline int in{
int i=0;char ch;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch)){i=(i<<3)+(i<<1)+(ch^48);ch=getchar();}
return i;
}
inline bool cmp(node a,node b)
{
if(a.pos==b.pos)return a.r<b.r;
return a.pos<b.pos;
}
inline void add(int x)
{
zhi+=tt[zhui[x]^k];
tt[zhui[x]]++;
return;
} //先统计再累加新序列.
inline void jian(int x)
{
tt[zhui[x]]--;
zhi-=tt[zhui[x]^k];
return;
}//先删序列再除与该序列有关的可行解.
int main()
{
n=in,m=in,k=in;
int di=sqrt(n);
for(int i=1;i<=n;i++)
{
a[i]=in;
zhui[i]=zhui[i-1]^a[i];//统计前缀异或和
}
for(int i=1;i<=m;i++)
{
q[i].l=in,q[i].r=in;
q[i].l--,q[i].poi=i,q[i].pos=q[i].l/di;//注意l要减一!
}
sort(q+1,q+m+1,cmp);//分块
int l=1,r=0;
for(int i=1;i<=m;i++)
{
int ll=q[i].l,rr=q[i].r;
while(l<ll)jian(l++);
while(r>rr)jian(r--);
while(l>ll)add(--l);
while(r<rr)add(++r);
ans[q[i].poi]=zhi;
}//莫队
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}