前置芝士:
假设有 a 1 ⃗ \vec{a_1} a1, a 2 ⃗ \vec{a_2} a2, ⋯ \cdots ⋯, a n ⃗ \vec{a_n} an 等 n n n 个向量所构成的向量组。
然后定义一个常数组 c 1 c_1 c1, c 2 c_2 c2, c 3 c_3 c3, ⋯ \cdots ⋯, c n c_n cn,让他们两两相乘,得到一个子空间 c 1 a 1 ⃗ c_1\vec{a_1} c1a1, c 2 a 2 ⃗ c_2\vec{a_2} c2a2, c 3 a 3 ⃗ c_3\vec{a_3} c3a3, ⋯ \cdots ⋯, c n a n ⃗ c_n\vec{a_n} cnan。其中 a n ∈ N a_n\in \textbf{N} an∈N。
假设一个向量组 a 1 , a 2 , a 3 , ⋯ , a n a_1,a_2,a_3,\cdots,a_n a1,a2,a3,⋯,an 和另外的一个向量组 b 1 , b 2 , b 3 , ⋯ , b n b_1,b_2,b_3,\cdots,b_n b1,b2,b3,⋯,bn 可以用上面的方法互相转换,那么他们是等价的。
给定一个向量组 a 1 , a 2 , a 3 , ⋯ , a n a_1,a_2,a_3,\cdots,a_n a1,a2,a3,⋯,an,现在要求求出一组他们的基。
需要使用高斯等消元法来求解。
当使用高斯消元消完某一列的时候,会形成一个三角形,类似于 ┕图 1 1 1┑,这个三角一定是线性无关的。原因,这里面的任意一个向量无法被其他向量表示。
容易发现时间复杂度为 O ( n 3 ) O(n^3) O(n3)。
对于任意的一组向量 { x ⃗ 1 , x ⃗ 2 . x ⃗ 3 , ⋯ , x ⃗ n } \{\vec x_1,\vec x_2.\vec x_3,\cdots,\vec x_n\} {x1,x2.x3,⋯,xn},他都可以用上面所述的方法生成一个东西叫做子空间,这个子空间里面的任意基底都是等价的。
重要:假设有一个向量组 { x ⃗ 1 , x ⃗ 2 , x ⃗ 3 ⋯ x ⃗ n } \{\vec x_1,\vec x_2,\vec x_3\cdots\vec x_n\} {x1,x2,x3⋯xn},其最大线性无关组的大小一定是小于等于他的维度的。假设维度为 63 63 63,那么 63 63 63 个向量构成的向量组可以代替这 n n n 个向量组。线性基的重中之重
大量优化需要使用的时间。
例题 洛谷 P3812
给定 n n n 个整数,求在这些数中选取多少个数让异或和最大。 1 ≤ n ≤ 50 1\le n\le 50 1≤n≤50, 0 ≤ a i ≤ 2 50 0\le a_i\le 2^{50} 0≤ai≤250。
实际上就是求最大的生成子空间。
暴力时间复杂度 O ( 2 n ) O(2^n) O(2n),明显超时。
首先找到 n n n 个向量的线性基,大约有 50 50 50 个有用的。
然后进行贪心,将所有的向量的线性基进行异或就是答案。
#include
#define int long long
using namespace std;
int a[100];
signed main() {
int n;
cin >> n;
for (int i = 0; i < n; i ++)
cin >> a[i];
int k = 0;
for (int i = 50; i >= 0; i --) {
for (int j = k; j < n; j ++)
if (a[j] >> i & 1) {
swap (a[j], a[k]);
break;
}
if (a[k] >> i & 1) {
for (int j = 0; j < n; j ++)
if (j != k)
if (a[j] >> i & 1)
a[j] ^= a[k];
k ++;
if (k >= n)
break;
}
}
int res = 0;
for (int i = 0; i < k; i ++)
res ^= a[i];
cout << res << '\n';
return 0;
}
图
图
1
1
1:
其中黑色框好的为非 0 0 0 数,其他的 0 0 0。