40.线性代数
线性代数
矩阵乘法
我们定义了乘法,那么我们就可以用使用快速幂来实现矩阵运算
常见的递推形式
当然,这种形式很容易推到到有
这个是两个函数相互有关系,但这些关系都是线性的
-
可以把矩阵运算看成和数值的运算类似,可以用线段树等数据结构维护
-
动态DP
高斯消元
一个线性方程组有
数放在最右列,得到一个
高斯消元通过多次变换把方程组转化为多个一元一次方程
高斯消元具体步骤:
- 枚举每一列,找到第一个
线性方程组的解有
高斯-约当消元法
高斯-约旦消元法的前提是 (假设有唯一解)
消元过程如下
- 从第
列开始,选择一个非 的系数(一般是最大值)所在的行,把这一行与第一行交换,此时 是主元 - 把
的系数转换成 1 - 利用主元
的系数,把其他行的这一列的主元消去 - 重复以上步数,直到把每行都变成只有对角线上存在主元,且系数都为
,则答案就是最后一列的数字
std::string gaussvector<ld>> &a { // 传入增广矩阵
int n = a.size();
int c = 0, r = 0;
for (c = 0, r = 0, c < n; c ++) {
int tmp = r;
for (int i = r; i < n; i++)
if (sgn(a[i][c]))
tmp = i;
if (sgn(a[tmp][c]) == 0)
continue;
std::swap(a[tmp], a[r]);
for (int i = n; i >= c; i--)
a[r][i] /= a[r][c];
for (int i = r + 1; i < n; i++)
if (sgn(a[i][c]))
for (int j = n; j >= c; j--)
a[i][j] -= a[r][j] * a[i][c];
r += 1;
}
if (r < n) {
for (int i = r; i < n; i++)
if (sgn(a[i][n]))
return "NoSolution";
return "InfSolution";
}
for (int i = n - 1; i >= 0; i--)
for (int j = i + 1; j < n; j++)
a[i][n] -= a[j][n] * a[i][j];
return "OK";
}
线性基
基是线性代数中的一个概念,一个向量空间可以由一组基线性组合而成
而线性基指的是模
我们可以把这样一种线性的思想带到异或中去来组成线性基
线性基解决以下几类问题
从
个数中选出任意个数,使得异或和最大
一种朴素的思想是
线性基的构造
- 非高斯消元构造
考虑到如果每个数的二进制位数都不同,那么这个集合
如果有两个数的二进制位数相同,那么通过
- 高斯消元构造线性基
利用高斯消元来构造线性基会更加直观
把原数组构成的矩阵进行高斯消元,化成简化阶梯矩阵
如果后面有全
线性基的应用
最小异或和
只需要判断是否有
最大异或和
若用基本方法求线性基,对于最后求出的基
如果用高斯消元的方法求线性基,由于只有对角线上存在
第
高斯消元后,第
//带求第 k 大的板子
struct Linear_basis {
i64 num[70]{}, zero = 0;
const int B = 62;
bool insert(i64 x) {
for (int i = B; i >= 0; i --) if (x & (1ll << i)) {
if (num[i]) x ^= num[i];
else {
num[i] = x;
return true;
}
}
zero ++;
return false;
}
i64 query_min(i64 x) {
for (int i = B; i >= 0; i --)
x = std::min(x, x ^ num[i]);
return x;
}
i64 query_max(i64 x) {
for (int i = B; i >= 0; i --)
x = std::max(x, x ^ num[i]);
return x;
}
i64 query_kth(i64 k) {
i64 x = 0;
k >>= zero;
std::vector<i64> p;
for (int i = 0; i <= B; i ++) if (num[i]) p.emplace_back(num[i]);
for (int i = (int)p.size() - 1; i >= 0; i --) {
if (k & (1ll << i)) x = std::max(x, x ^ p[i]);
else x = std::min(x, x ^ p[i]);
}
return x;
}
} lb;