线性基

前置知识

对于若干向量组成的线性空间 \(V\),其存在子集 \(B\).
\(B\) 是线性无关的(指任意向量不能被其他向量通过线性组合表示)。
而且 \(B\) 的张成(其通过任意线性组合表示的所有向量)为 \(V\).

引入

OI 中的基,一般都运用于解决异或的问题。
两个二进制有 \(len\) 位的数异或,也就是两个 \(len\) 维向量线性组合。
若有序列 \(\{a_i\}\),值域是 \([1,m]\),其线性基就是不多于 $\left \lceil \log_2(m) \right \rceil $ 个数,通过异或的组合可以凑成序列的所有数。
这个线性基第 \(i\) 位的二进制最高位也是 \(i\).

构建

构建的其中一种是这样:
若我们插入一个数 \(x\),设最高位是 \(i\).
1.若当前线性基第 \(i\) 位有值 \(a_i\),那么再次插入 \((x\otimes a_i)\),直到 \(x=0\).
2.若 \(a_i\) 还没有值,那么就令 \(a_i=x\) 并退出。
可见插入一个数复杂度是 \(O(\log)\) 的。

若退出时 \(x\not =0\) ,就代表为了表示 \(x\),线性基新赋值了一个数。
反之,那么代表 \(x\) 已经被表示了。

笔者发现,若线性基已经满了,再插入的数,都是能够表示的。
所以此时线性基已经能表示 \(2^n-1\) 里的所有数了。

应用

是否能凑出某数

如同插入一个数那样,看结尾时 \(x\) 是否是 \(0\).

最大值

查询任意组合异或出的最大值。
从高到低位依次枚举,设当前到了线性基的第 \(i\) 位,
若线性基 \(a_i\) 有值:且当前答案的第 \(i\) 位还没有值,那么令答案异或上 \(a_i\)。反之,则跳过。
这样做每次答案都不减,故正确。
若查询加上 \(x\) 任意组合的最大值,令答案初始值为 \(x\) 即可。

code
#include<bits/stdc++.h>
using namespace std;
using LL=long long;
const int N=55;
LL n,a[N];
void ins(LL x) {
	for(int i=54; i>=0; i--) {
		if(x&(1ll<<i)) {
			if(a[i]) x^=a[i];
			else {a[i]=x; break;}
		}
	}
}
LL qmax() {
	LL res=0;
	for(int i=54; i>=0; i--) {
		res=max(res,res^a[i]);
	}
	return res;
}
int main() {
	scanf("%lld",&n);
	for(int i=1; i<=n; i++) {
		LL u; scanf("%lld",&u);
		ins(u);
	}
	printf("%lld\n",qmax());
	return 0;
} 

注意代码中 1ll<<i 一定要写。

线性基合并

直接将其中一个插入另一个即可。复杂度 \(O(\log^2)\).

posted @ 2023-08-27 21:10  s1monG  阅读(6)  评论(0编辑  收藏  举报