线性基专题

线性基的定义:

类似极小线性方程组,只不过线性基的运算是针对异或运算而言;

线性基的特点:

1.一个数列的线性基可以通过异或操作表示全部的数

2.线性基不可被除他之外的数表示

3.线性基的极值就是数列异或的极值

线性基习题:

给定 n个整数(可能重复),现在需要从中挑选任意个整数,使得选出整数的异或和最大。

请问,这个异或和的最大可能值是多少。

这道题是典型的线性基板子;

这里贴一个讲得很好的链接

题解 P3812 【【模板】线性基】 - 洛谷专栏

线性基的求法:

#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个元素,可以构成2^{n-cnt}-1个真子集,因为线性基可以构成任意的异或,那么线性基的异或和真子集的异或值必然为0,必然是完全平方数。所以构成完全平方数的集合至少有2^{n-cnt}-1个;

那么还会有更多吗?我们不妨想一下,这里线性基的含义代表的是什么,对于一个真子集的集合异或值,假设他的线性基个数为x,如果我们使用了不属于x中的线性基,那么结果必然不为0。所以说这里的线性基的含义是:

他组合成的组合可以应对所有情况;

下面从另一个方面证明完全平方数的集合只有2^{n-cnt}-1个;

假设集合个数多于2^{n-cnt}-1,那么这个集合肯定从线性基中取了某个数设为f[i],假设这个线性基在这个集合中只出现奇数次,那么集合的异或值中,i处的异或值必然为1,但是由于线性基中没有了f[i]这个数,于是无法解决这个集合的异或值中i的位置出现1的情况;

假设出现了偶数次,那么我们可以知道,其他的线性基不会出现。于是我们将f[i]从这个集合中取出,那么剩下的数构成的集合必然为2^{n-cnt}-1中的集合,这个结论和这个集合不属于2^{n-cnt}-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;
}

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值