题意:
给定长度为n的序列a,q组询问,
每组询问给出两个数L和x,问序列的前L个数,有多少个子集满足异或和等于x。
数据范围:n,q<=1e5,0<=a(i)<220
解法:
先假设只有一组询问,可以将前L个数插入线性基,求出插入成功的数的数量cnt,
那么自由元的数量就是L-cnt,
如果线性基中能通过异或组合出x,那么满足条件的子集数量就是2L-cnt,
即每个自由元对应选与不选。
如果无法组合出x,那么满足条件的子集数量就是0。
但是这题有多组询问,对每组询问都重新建立线性基显然不行。
可以对询问离线之后按L从小到大排序,这样L就是单调的了,
在从左到右建立线性基的过程中计算答案即可。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
const int mod=1e9+7;
vector<pair<int,int> >Q[maxm];
int ans[maxm];
int p2[maxm];
int d[maxm];
int a[maxm];
int n,q;
bool add(int x){
for(int i=30;i>=0;i--){
if(x>>i&1){
if(d[i]){
x^=d[i];
}else{
d[i]=x;
return 1;//插入成功
}
}
}
return 0;//插入失败
}
bool check(int x){
for(int i=30;i>=0;i--){
if(x>>i&1){
if(d[i]){
x^=d[i];
}else{
return 0;//无法组成x
}
}
}
return 1;//可以组成x
}
signed main(){
p2[0]=1;
for(int i=1;i<maxm;i++)p2[i]=p2[i-1]*2%mod;
//
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=q;i++){
int l,x;cin>>l>>x;
Q[l].push_back({x,i});
}
int cnt=0;
for(int i=1;i<=n;i++){
int ok=add(a[i]);
if(ok)cnt++;
for(auto p:Q[i]){
int t=check(p.first);//判断能否组成x
if(t){
ans[p.second]=p2[i-cnt];//i-cnt是自由元的数量
}
}
}
for(int i=1;i<=q;i++){
cout<<ans[i]<<endl;
}
return 0;
}