题意
给出一个长度为n的数组A和q个询问,每次询问给出两个数l和x,问有多少种方案从前l个元素中取若干个出来使得其异或和恰好为x。取模。
n,q≤105,Ai<220n,q≤105,Ai<220
分析
刚看完题想到的是离线然后FWT,想了想发现其实并不用。
只要对于所有的前缀,将其线性基搞出来。
对于每个询问,先判断能否从前缀l的线性基中表示出x,若不行则答案为0。
否则的话,设其线性基中有cnt个元素,答案就是2l−cnt2l−cnt。因为除了线性基以外的元素可以任意选,然后再通过线性基中的元素将异或和补成x即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
const int MOD=1000000007;
int n,q,bin[N],bas[N][25],cnt[N];
bool check(int l,int x)
{
for (int i=20;i>=0;i--)
if (x&bin[i])
{
if (!bas[l][i]) return 0;
x^=bas[l][i];
}
return x==0;
}
int main()
{
scanf("%d%d",&n,&q);
bin[0]=1;
for (int i=1;i<=max(n,20);i++) bin[i]=bin[i-1]*2%MOD;
for (int i=1;i<=n;i++)
{
for (int j=0;j<20;j++) bas[i][j]=bas[i-1][j];
cnt[i]=cnt[i-1];
int x;scanf("%d",&x);
for (int j=20;j>=0;j--)
if (x&bin[j])
{
if (!bas[i][j]) {bas[i][j]=x;cnt[i]++;break;}
else x^=bas[i][j];
}
}
while (q--)
{
int l,x;scanf("%d%d",&l,&x);
if (!check(l,x)) puts("0");
else printf("%d\n",bin[l-cnt[l]]);
}
return 0;
}