线性基的定义:
类似极小线性方程组,只不过线性基的运算是针对异或运算而言;
线性基的特点:
1.一个数列的线性基可以通过异或操作表示全部的数
2.线性基不可被除他之外的数表示
3.线性基的极值就是数列异或的极值
线性基习题:
给定 n个整数(可能重复),现在需要从中挑选任意个整数,使得选出整数的异或和最大。
请问,这个异或和的最大可能值是多少。
这道题是典型的线性基板子;
这里贴一个讲得很好的链接
线性基的求法:
#include<iostream>
using namespace std;
typedef long long LL;
LL f[70];
bool flag=false;
void insert(LL x){
for(int i=63; i>=0; i--){
if(!f[i] && (x&1LL<<i)){f[i]=x;return;}
else if(f[i] && (x&1LL<<i))x=x^f[i];
}
flag=true;
}
LL qmax(){
LL res=0;
for(int i=63; i>=0; i--){
res=max(res,res^f[i]);
}
return res;
}
int main(){
int n;
cin>>n;
for(int i=0; i<n; i++){
LL a;
cin>>a;
insert(a);
}
cout<<qmax()<<endl;
}
https://vjudge.net/problem/CodeForces-895C/origin
思路:我们可以知道,一个数必然可以表示为质数的乘积,那么作为一个完全平方数y,必然y=x*x;x假设有z个质因子,那么y有z个质因子,同时质因子的指数必然为偶数。那么我们得到一个结论,完全平方数的质因子的指数为偶数。
有数据范围1-70可知,期间有19个质数,那么换而言之,对于1-70的数,可以由这些质数构成。
所以对于【1,70】之间数的任意个数乘积,他都可以用这19个质数表示,我们可以用二进制的位数表示这19个质数,比如第0位代表2,第1位代表3,依次类推。
有了上述的分析,下面进行分析;
首先线性基的个数必然和数列中的数有关,对于线性基中某个数,例如f[i]!=0,那么f[i]必然由j>i前面的线性基和数列中某个数异或而成,或者就是数列中的某个数,
这样我们可以得到一个对应关系:一个线性基对应数列中的一个元素;
假设我们求出了数列的质因子构成的线性基,那么这个线性基可以表示数列中任意的数相异或的值。这里的异或我们可以转化为数的乘积。也就是对于n个数的数列而言,我们有cnt个线性基,那么这cnt个线性基可以表示数列间的任意乘积,那么我们从n个数中取出构成线性基的cnt个元素(上述证明的对应关系),那么还剩下n-cnt个元素,可以构成-1个真子集,因为线性基可以构成任意的异或,那么线性基的异或和真子集的异或值必然为0,必然是完全平方数。所以构成完全平方数的集合至少有
-1个;
那么还会有更多吗?我们不妨想一下,这里线性基的含义代表的是什么,对于一个真子集的集合异或值,假设他的线性基个数为x,如果我们使用了不属于x中的线性基,那么结果必然不为0。所以说这里的线性基的含义是:
他组合成的组合可以应对所有情况;
下面从另一个方面证明完全平方数的集合只有-1个;
假设集合个数多于-1,那么这个集合肯定从线性基中取了某个数设为f[i],假设这个线性基在这个集合中只出现奇数次,那么集合的异或值中,i处的异或值必然为1,但是由于线性基中没有了f[i]这个数,于是无法解决这个集合的异或值中i的位置出现1的情况;
假设出现了偶数次,那么我们可以知道,其他的线性基不会出现。于是我们将f[i]从这个集合中取出,那么剩下的数构成的集合必然为-1中的集合,这个结论和这个集合不属于
-1中的集合相互矛盾。故得证;
代码如下:注意爆int
#include<iostream>
using namespace std;
const int mod=1e9+7;
typedef long long LL;
int cnt[75],p[19]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
LL h[100010];
int f[22];
void insert(int x){
for(int i=19; i>=0; i--){
if(!f[i] && x>>i&1){
f[i]=x;
return ;
}
else if(f[i] && x>>i&1)x^=f[i];
}
}
int main(){
int n;
cin>>n;
h[0]=1;
for(int i=1; i<=n; i++)h[i]=(h[i-1]<<1)%mod;
for(int i=0; i<n; i++){
int a;
cin>>a;
int mask=0;
for(int j=0; j<19; j++){
while((a%p[j])==0){
a/=p[j];
// cout<<p[j]<<endl;
mask^=(1<<j);
}
// if(a==1)break;
}
insert(mask);
}
int cnt=0;
for(int i=19; i>=0; i--){
if(f[i])cnt++;
}
// cout<<cnt<<endl;
cout<<h[n-cnt]-1<<endl;
}