线性基学习笔记

线性基学习笔记

——by sunzz3183


引入

学高斯消元后就要学线性基啦!建议先看懂高斯消元!

介绍

给定 \(n\) 个数 \(a_i\),求一个基底

基底就是一个线性空间,即线性基。

线性基中的 \(t\) 个数 \(p_i\)\(a\) 中的个数都可以被 \(p\)若干数通过异或得出。

求法

高斯消元

直接贴代码:

void add(int num){
    for(int i=61;i>=0;i--)
        if((num>>i)&1)
            if(!p[i]){p[i]=num;return;}
            else num^=p[i];
    flg=1;//判断有没有0的存在
    return;
}

清新易懂,我们从大到小来枚举当前 \(num_{(2)}\) 的第 \(i\) 位,如果发现 \(p_i\) 已经有元素了,就让 \(num=num \oplus p_i\) 否则插入当前的 \(num\)

问题

线性基通常有如下三个用法:

  1. 查询一个数是否能被集合中其他数异或表示

  2. 求一个集合异或最大/最小值

  3. 求一个集合异或的第k大值

下列给定三种方法的代码

查询

bool ask(){
    for(int i=61;i>=0;i--)
        if(x>>i&1)x^=p[i];
    return !x;
}

最大最小

inline int getmax(){
    int ans=0;
    for(int i=61;i>=0;i--)
        ans=max(ans,ans^p[i]);
    return ans;
}
inline int getmin(){
    if(flg)return 0;//注意判零的存在
    for(int i=0;i<=61;i++)
        if(p[i])return p[i];
}

第 k 大

\(k\) 进行二进制拆分,然后进行选数。

注意,我们可能会在插入的时候插入了一个类似这样的一个数 \(1111111111_{(2)}\)。那么显然在求第 \(k\) 大的时候,会错序。

所谓我们要对 \(p_i\) 进行重构,让 \(p_i\) 成为 \(10000000_{(2)}\) 这样的数。

void rebuild(){
    for(int i=61;i>=0;i--)
        for(int j=i-1;j>=0;j--)
            if(p[i]>>j&1)p[i]^=p[j];
    for(int i=0;i<=61;i++)if(p[i])d[cnt++]=p[i];cnt--;
}
inline int getk(int k){
    k-=flg;
    if(k>=1ll<<cnt)return -1;
    int ans=0,i=0;
    while(k)ans^=(k&1)?d[i]:0,k>>=1,i++;
    return ans;
}
posted @ 2023-05-16 21:42  sunzz3183  阅读(18)  评论(0编辑  收藏  举报
Live2D