线性基

线性基

定义

线性基

一个由若干数组成的集合(S),定义它的线性基为最小的一个集合(T)使得(S)(T)能够异或出的值域(集合)相同

张成

一个集合(S),随意取若干个数异或,能够凑出的数的集合称为集合(S)的张成,表示为(span(S))

线性相关

如果一个集合内有某个数满足:去除这个数后的集合可以凑出这个数。那么称集合(S)线性相关。否则为线性无关

求法

将每个数看做一个01串,按位操作,从高到低,在没加入线性基的数中找,看有没有当前位为1的,如果有,将其加入线性基中,并用现在加入线性基中的这个数 消去 原本线性基中其他数当前位的1(高斯消元)。如果没有,跳过当前位直接对下一位进行操作

这样消出的线性基满足每一列最多只有一个1,并且从前往后,线性基中每个数的第一个1所在位置依次右移,即前面的数严格大于后面的数。
类似于这样的形式:

[1001010101\ 0101010101\ 0010110101\ 0000011010\ .\ .\ .\ ]

证明

考虑这样为什么这样求出的线性基是合法的。(不知道怎么证这样一定是最小的)
由线性基的定义可得,如果(S)集合中的每个数,都可以被(T)集合表示出,那么这组线性基一定合法,因此我们只需要考虑能否凑出(S)集合即可。
对于(S)集合中的任意一个数的任意一位,如果这一位无法被凑出,意味着这一位是1,但线性基中却没有这一位是1的数,但如果这样,这个无法被凑出的数就应该在当时被加入线性基中,所以这样的数不存在。

你可能会有这样的疑问:是否可能有一个数因为线性基中某几位被捆绑在一起而无法凑出?
考虑几个位为什么会捆绑在一起?因为一开始加入的某个数在这几位上是1,但后面却没有在这一位上是1的数(消去后)。
一个数如果会因为某几位被捆绑而无法凑出,只可能是实际上是10,但只能凑出11(线性基中只有11,没有1x和01).但这种情况是不可能的,因为当我们将11加入线性基时,由于10的首位是1,所以11会对10进行消数的操作,使得10变为01,这时,第二位是1的数是存在的,所以11不会被捆绑在一起。

性质/用法

性质1

1,假设原来的集合大小为(|S|),线性基大小为(|T|),对于任意一个数(x),如果它可以被线性基中的数表示出来,那么从集合(S)中选若干个数(xor)(x)的方案数为(2^{|S| - |T|})
证明:考虑(|S| - |T|)实际上就是集合中没有加入线性基中的数的个数,我们在这个集合中任意选一些数的方案数为(2^{|S| - |T|}),而每一种取法异或出来的数,都可以由线性基中的某一个确定的方案凑出。将这2个相同的数异或,可以得到(0),因此我们有(2^{|S| - |T|})种凑出(0)的方案。
因此对于任意一个数,如果我们可以用线性基凑出,我们可以拿它和凑(0)的方案进行异或,所以肯定也有(2^{|S| - |T|})种方案,否则肯定就凑不出了。

用法1

线性基可以判断任意一个数是否可以被集合(S)内的数凑出。

从高位开始,依次操作,如果当前位为1,那么异或上线性基中当前位为1的数,否则不操作。最后判断结果是否为0,为0即可以凑出(一个数在异或后变成了0,肯定是因为这个数异或上了它自己)

由此我们可以引申出另一个经典用法:用线性基来求一个集合的最大异或和。
从高位开始贪心,每次检查异或上线性基的当前位是否可以使得答案变大,如果可以,就异或,否则跳过。

求最大异或和的代码:

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 55
#define LL long long

int n, tot;
LL maxn, ans;
LL f[AC];

inline LL read()
{
    LL x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

void pre()
{
    n = read(), maxn = 1LL << 50;
    for(R i = 1; i <= n; i ++, maxn = 1LL << 50) 
    {
        LL x = read();
        for(R j = 50; ~j; j --, maxn >>= 1)
        {
            if(!(x & maxn)) continue;
            if(!f[j]) {f[j] = x; break;}
            else x ^= f[j];
        }
    }
}

void work()
{
    for(R i = 51; ~i; i --)
        if((ans ^ f[i]) > ans) ans ^= f[i];
    printf("%lld
", ans);
}

int main()
{
    //freopen("in.in", "r", stdin);
    pre();
    work();
//	fclose(stdin);
    return 0;
}
原文地址:https://www.cnblogs.com/ww3113306/p/10342527.html