线性基浅谈

线性基浅谈

一、线性基浅谈

  在了解线性基之前,要简单理解什么是基。在线性代数中,基又称为基地,是刻画向量的工具。对于基底的元素我们称为基向量,向量空间的任意一个元素都可以唯一表示成为基向量的线性组合。同样,线性基也是一种基,它是一种特殊的基,一般用来求异或问题,所以在这里就先按照解决异或问题来讲述。

  这种特殊的基是由64个数字或32个数字组成,具体是由几个数字组成要取决于,原题中给出的数字集合在二进制下的最高位,解释一下:若原题中的答案在int范围内,则运用32位线性基足以,若答案在long long范围内,就一定要用64线性基来解决,高精度另当别论。

  现在对于n个数字(下面称这n个数为原数集):a1a2an,构建线性基,先不提线性基是如何构建出来的,先说一说线性基有什么性质:

  1.原数集中的任意一个数字都能够通过线性基中的元素异或出来(这与线性基的构建方式有关)。

  2.原数集中的数字异或出来的值域与线性基中的元素以后出来的值域相等(通过上一条性质可知)。

  3.线性基中没有异或和为零的非空子集:现在假设存在这样一个子集使得b1b2bx的异或和为零,那么根据异或的性质能得出:b1=b2b3bx,既然b1已经能用除他之外的线性基元素表示出来,我们便没有必要再将b1放在线性集中。

  4.线性基中的选取元素的每一种方案,都对应一个异或值,不存在多种选取方案对应同一个异或值的情况:现在假设存在这种情况,那么我们就会存在一个非空子集的异或值为零,这与上一条性质矛盾。

  5.线性基是满足以上性质的最小集合,即线性基中不存在任何一个多余的元素。

二、线性基的构造

  (在下面描述中,称线性基对应的第i位元素为placei)对于每一个数字num,从高位向低位扫,扫到第x位为1时,若当前placex有数字,则将num异或上placex,即使num=numplacex,并向下继续扫,若placex没有数字,则当前num就使placex,即placex=num

  下面有两种写法:第一种写法是按照上方描述的过程写的,第二种是以上方描述的过程为思想进行改进的,是所有n个数字同时插入。

1
2
3
4
5
6
7
8
void insert(long long x)
{
    for(int i=63;~i;i--) if((x>>i)&1ll)
    {
        if(!place[i]) {place[i]=x;return;}
        else x^=place[i];
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
void insert(int *num)
{
    for(int j=63;~j;j--)
    {
        int p=0;
        for(int i=1;(!p)&&i<=n;i++)
            if((!v[i])&&((num[i]>>j)&1ll)) p=i;
        if(!p) {place[j]=0;continue;}
        v[p]=true,place[j]=num[p];
        for(int i=1;i<=n;i++) if(i!=p&&((num[i]>>j)&1ll))
            num[i]^=num[p];
    }
}

三、线性基应用

  1.询问数字x是否在当前线性基异或集合中:

  我们对于数字x从高位向低位扫,若数字x在二进制下的第i位为1,则异或上placei,若数字x最后异或出来等于零,则数字x在当前线性基的异或集合中,反之则不在。

1
2
3
4
5
6
bool check(long long x)
{
    for(int i=63;~i;i--)
        if((x>>i)&1ll) x^=place[i];
    return x==0;
}

  2.将线性基A和线性基B合并:

  我们将线性基A中的每一个数字都依次插入到线性基B中即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Base
{
    long long place[N];
    void insert(long long x)
    {
        for(int i=63;~i;i--) if((x>>i)&1ll)
        {
            if(!place[i]) {place[i]=x;return;}
            else x^=place[i];
        }
    }
};
void merge(Base A,Base &B)
{
    for(int i=63;~i;i--) if(A.place[i])
        B.insert(A.place[i]);
}

  3.用线性基求当前数集能异或出来的最大数字:

  我们对于当前数集构造线性基,因为二进制下高位的1对于答案的贡献要比下面所有的位数都为1的贡献还要大,所以我们可以进行贪心,若答案异或上当前位数上的数字比答案要大,则异或。

1
2
3
4
5
6
7
8
long long mx()
{
    long long ans=0;
    for(int i=63;~i;i--)
        if((ans^place[i])>place)
            ans^=place[i];
    return ans;
}

  4.用线性基求当前数集能异或出来的最小数字:

  我们对于当前数集构造线性基,直接取出最后一位不是零的数字即可,为什么?仔细想想挺简单的。

1
2
3
4
5
6
long long mn()
{
    for(int i=0;i<=63;i++)
        if(place[i]) return place[i];
    return -1;
}
posted @   Yang1208  阅读(2690)  评论(2编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示
哥伦布
16°
10:09发布
哥伦布
10:09发布
16°
中雨
南风
6级
空气质量
相对湿度
88%
今天
中雨
6°/16°
周一
小雨
0°/11°
周二
4°/19°