线性基
作者:互联网
线性基
定义
线性基
一个由若干数组成的集合\(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\n", ans);
}
int main()
{
//freopen("in.in", "r", stdin);
pre();
work();
// fclose(stdin);
return 0;
}
标签:11,基中,凑出,集合,异或,线性 来源: https://www.cnblogs.com/ww3113306/p/10342527.html