一、前置芝士:高斯消元
https://blog.youkuaiyun.com/xyz32768/article/details/78574746
二、行列式的定义
一个 n n 阶方阵(行数和列数相等的矩阵)
的行列式为:
记为 det(A) det ( A ) 或 |A| | A | 。
三、行列式的性质
(1) |A|=|AT| | A | = | A T |
(2) |AB|=|A||B| | A B | = | A | | B |
(3)矩阵的一行乘上 k k 之后,行列式乘上
。
(4)如果矩阵的每一行内数的和都为 0 0 ,每一列内数的和都为
,那么行列式为 0 0 。
(5)交换矩阵的两行或两列,行列式取反。
(6)矩阵的一行减去另一行的
倍,行列式不变。
性质(6)是求解行列式的关键。
四、求解行列式
暴力求解是 O(n2×n!) O ( n 2 × n ! ) 的。
利用高斯消元求解行列式。
根据性质(6),我们可以 for i i 从
到 n n ,用第
行把所有满足 j∈[i+1,n] j ∈ [ i + 1 , n ] 的位置 (j,i) ( j , i ) 消成 0 0 ,也就是用第
行去消第 j j 行。
最后原矩阵被消成一个上三角矩阵(对于每个
,第 i i 行前
个元素全部为 0 0 的矩阵)。
显然这时候矩阵的行列式为对角线上元素的乘积。
复杂度
。
代码:
double det(int n) {
int i, j, k;
For (i, 1, n) {
int p = i;
For (j, i + 1, n)
if (fabs(mat[j][i]) > fabs(mat[p][i])) p = j;
if (p != i) For (j, i, n) swap(mat[i][j], mat[p][j]);
For (j, i + 1, n) {
double tmp = mat[j][i] / mat[i][i];
For (k, i, n) mat[j][k] -= mat[i][k] * tmp;
}
}
double ans = 1;
For (i, 1, n) ans *= mat[i][i];
return ans;
}
如果行列式特别大且题目要求取模,则可以利用逆元:
int det(int n) {
int i, j, k, res = 1;
For (i, 1, n) {
int qaq = qpow(a[i][i], ZZQ - 2);
For (j, i + 1, n) {
int tmp = 1ll * a[j][i] * qaq % ZZQ;
For (k, 1, n) a[j][k] = (a[j][k] - 1ll * a[i][k] *
tmp % ZZQ + ZZQ) % ZZQ;
}
}
For (i, 1, n) res = 1ll * res * a[i][i] % ZZQ;
return res;
}
如果模数不是质数,可以利用辗转相除的方法,复杂度多一个 log log 。
具体地,如果当前要用第 i i 行去消第
行,那么:
(1)第 i i 行减掉第
行的