题意:对一个一个长度为n的数组,有q个询问:前l个数的子序列异或和为x的有多少个?结果对1e9+7取余
首先介绍一下线形基(来自OI Wiki):
线性基是向量空间的一组基,通常可以解决有关异或的一些题目。
通俗一点的讲法就是由一个集合构造出来的另一个集合,它有以下几个性质:
-
线性基的元素能相互异或得到原集合的元素的所有相互异或得到的值。
-
线性基是满足性质 1 的最小的集合。
-
线性基没有异或和为 0 的子集。
-
线性基中每个元素的异或方案唯一,也就是说,线性基中不同的异或组合异或出的数都是不一样的。
-
线性基中每个元素的二进制最高位互不相同。
对于线性基的数组a[],a[i]表示最高位的1在第i位的值
构造线性基的方法:对原集合的每一个数p转化为二级制,从高位向低位扫,对于第i位是1,若a[i]==0,令a[x]=p,扫描结束;
若a[i]!=0,令p^=a[i],继续扫描。
代码:
void insert(int p) {
//具体从最高位多少开始扫描,视具体情况而定
for(int i=30;i>=0;i--) {
//若i>31,使用(1LL)<<i
if(p&1<<i){
if(!a[i]) {
a[i]=p;
break;
}
p^=a[i];
}
}
}
通过上面插入过程,我们可以知道对一个数判断其是否可以被异或出来,如果成功插入则不可,否则可以。
对于本题,我们离线查询,对查询按照l从小到大排序,然后向线性基中插入数,插入成功的数有tot,已经遍历的数组元素个数为cnt,若可以异或出来,则子序列数为2^(cnt-tot)
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
ll bit[maxn],num[maxn];
ll ans[maxn],a[maxn];
struct node{
int l,x,id;
bool operator<(const node &a) const{
if(l!=a.l) return l<a.l;
return x<a.x;
}
}b[maxn];
int tot,cnt,n,m;
void inse(int x) {
for(int i=28;i>=0;i--) {
if(x&1<<i) {
if(!bit[i]) {
bit[i]=x;
tot++;
break;
}
x^=bit[i];
}
}
}
bool check(int x) {
for(int i=28;i>=0;i--) {
if(x&1<<i) {
if(!bit[i]) return 0;
x^=bit[i];
}
}
return 1;
}
signed main() {
scanf("%d%d",&n,&m);
num[0]=1;
for(int i=1;i<=n;i++) num[i]=(num[i-1]<<1)%mod;
for(int i=1;i<=n;i++) scanf("%lld",a+i);
for(int i=1;i<=m;i++) {
scanf("%d%d",&b[i].l,&b[i].x);
b[i].id=i;
}
sort(b+1,b+1+m);
for(int i=1;i<=m;i++) {
while(cnt<b[i].l) inse(a[++cnt]);
if(check(b[i].x)) ans[b[i].id]=num[cnt-tot];
}
for(int i=1;i<=m;i++) {
printf("%lld\n",ans[i]);
}
}