行列式
定義:\(\det(A)\),又記作 \(\vert A\vert\),等於 \(\sum_p (-1)^{\tau(p)} \prod_{i = 1}^nA_{i, p_i}\),其中 \(p\) 為 \(1 \sim n\) 的排列,\(\tau(p)\) 表示排列 \(p\) 的逆序對數。
1. 對於一個上三角矩陣,他的行列式等於主對角線所有值的乘積。
要使 \(A_{n, p_n} \ne 0\),\(p_n\) 只能取 \(n\);要使 \(A_{n - 1, p_{n - 1}} \ne 0\),\(p_{n - 1} \in \{n - 1, n\}\),但是 \(n\) 已經被用過了,\(p_{n - 1} = n - 1\)。
以此類推,使 \(\prod A_{i, p_i}\) 非零的只有唯一一個排列 \(p_i = i\),此時 \(\tau(p) = 0\)。
2. 單位矩陣的行列式為 \(1\),即 \(\vert I\vert = 1\)。
根據 \(I_{i, j} = [i = j]\) 以及 定理1 易證。
3. 交換矩陣兩行,行列式變號。
交換 \(i, j\) 等價於交換每個排列的 \(p_i, p_j\),這會使所有 \(\tau(p)\) 奇偶性改變,相當於整體乘了個 \(-1\)。
4. 將矩陣某行乘上一個常數,行列式乘上相同常數。
不妨將第 \(r\) 行所有元素乘 \(k\),\(\prod_{i = 1}^n A_{i, p_i}\) 恰好包含一個第 \(r\) 行元素,把 \(k\) 提出來。
5. 若矩陣有相同的兩行,則行列式為 \(0\)。
交換兩行會使行列式變號,但是矩陣沒變,即 \(\vert A\vert = -\vert A\vert \implies \vert A\vert = 0\)。
6. 若矩陣有兩行存在倍數關係,則行列式為 \(0\)。
\(k \vert A\vert = -k\vert A\vert \implies \vert A\vert = 0\)。
7. 若兩個矩陣至多有一行不等,將這不等的一行相加得到的新矩陣的行列式等於原行列式之和。
設不等的行編號為 \(c\),新矩陣 \(S\) 滿足 \(S_i = A_i = B_i,\ S_c = A_c + B_c(i \ne c)\)。
\(\prod_{i = 1}^nS_{i, p_i} = (A_{c, p_c} + B_{c, p_c})\prod_{i = 1}^n[i\ne c]A_{i, p_i} = \prod_{i = 1}^nA_{i, p_i} + \prod_{i = 1}^nB_{i, p_i}\)。
8. 將矩陣的某行加上另一行的倍數,行列式不變。
設把 \(c\) 的 \(k\) 倍加到 \(d\) 上,設新矩陣為 \(C\)。
設 \(B_i = A_i, B_d = kA_c(i \ne c)\),根據定理 7可得 \(\vert A\vert + \vert B\vert = \vert C\vert\),根據定理6可得 \(\vert B\vert = 0\)。
高斯消元
步驟略。
P4783 【模板】矩陣求逆
題意:求一個 \(n \times n\) 的矩陣 \(A\) 在模 \(10^9 + 7\) 意義下的逆矩陣。
將 \(A\) 進行線性變換相當於左乘一個矩陣,也就是說把 \(A\) 變換(消元)到單位矩陣 \(I\),相當於左乘 \(A^{-1}\)。
有恆等式 \(A^{-1} \times A = I\),那麼在 \(A\) 變換到 \(I\) 的同時 \(I\) 也做同樣的變換,最後得到 \(A^{-1} \times I = A^{-1}\)。
矩陣可逆當且僅當 \(\vert A\vert \ne 0\),即滿秩。submission
P7112 【模板】行列式求值
題意:給定一個 \(n\) 階行列式,求 \(\vert A\vert\)。結果對 \(p\) 取模,不保證 \(p\) 是素數。
把 \(A\) 消成上三角矩陣,最後答案就是主對角線之積。
加上某一行的倍數不改變答案,而交換兩行會使符號改變,因此記錄交換了多少次。
沒有必要對一行乘某個常數的操作,否則還有額外記錄乘了多少東西。
\(p\) 沒有特殊性質,不能求逆元,因此採用類似「輾轉相除法」對兩行消元,時間複雜度 \(O(n^3\log p)\)。
#include<bits/stdc++.h>
#define eb emplace_back
#define ep emplace
using namespace std;
using ll = long long;
int n, p, sgn; ll a[605][605];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> p;
for(int i = 1; i <= n; ++ i) {
for(int j = 1; j <= n; ++ j) {
cin >> a[i][j];
a[i][j] %= p;
}
}
for(int k = 1; k <= n; ++ k) {
for(int i = k; i <= n; ++ i) {
if(a[i][k]) {
if(i != k) {
swap(a[i], a[k]), sgn ^= 1;
}
break;
}
}
if(!a[k][k]) return cout << 0, 0;
for(int i = k + 1; i <= n; ++ i) {
while(a[i][k]) {
if(a[i][k] < a[k][k]) {
swap(a[i], a[k]), sgn ^= 1;
}
ll q = a[i][k] / a[k][k];
for(int j = k; j <= n; ++ j) {
a[i][j] = (a[i][j] + p - q * a[k][j] % p) % p;
}
}
}
}
ll tmp = 1;
for(int i = 1; i <= n; ++ i) tmp = tmp * a[i][i] % p;
cout << (sgn ? (p - tmp) % p : tmp);
return 0;
}
矩陣樹定理
無向圖生成樹個數
記 \(D\) 為無向圖的度數矩陣,
有向圖生成樹個數