一、前置芝士:高斯消元
https://blog.csdn.net/xyz32768/article/details/78574746
二、行列式的定義
一個 階方陣(行數和列數相等的矩陣) 的行列式爲:
記爲 或 。
三、行列式的性質
(1)
(2)
(3)矩陣的一行乘上 之後,行列式乘上 。
(4)如果矩陣的每一行內數的和都爲 ,每一列內數的和都爲 ,那麼行列式爲 。
(5)交換矩陣的兩行或兩列,行列式取反。
(6)矩陣的一行減去另一行的 倍,行列式不變。
性質(6)是求解行列式的關鍵。
四、求解行列式
暴力求解是 的。
利用高斯消元求解行列式。
根據性質(6),我們可以 for 從 到 ,用第 行把所有滿足 的位置 消成 ,也就是用第 行去消第 行。
最後原矩陣被消成一個上三角矩陣(對於每個 ,第 行前 個元素全部爲 的矩陣)。
顯然這時候矩陣的行列式爲對角線上元素的乘積。
複雜度 。
代碼:
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;
}
如果模數不是質數,可以利用輾轉相除的方法,複雜度多一個 。
具體地,如果當前要用第 行去消第 行,那麼:
(1)第 行減掉第 行的 倍。這時候, 被消成 。
(2)交換第 行和第 行。注意,這個操作會導致行列式的取反,要用一個變量記錄當前行列式是否被取反。
(3)如果 ,我們就成功地用第 行消去了第 行。否則繼續(1)(2)。
代碼:
int solve() {
int i, j, k; bool tr = 0;
For (i, 1, n) For (j, i + 1, n) {
int a = mat[i][i], b = mat[j][i];
while (b) {
int tmp = a / b; a %= b; swap(a, b);
For (k, i, n) mat[i][k] = (mat[i][k] - 1ll * tmp * mat[j][k]
% ZZQ + ZZQ) % ZZQ;
For (k, i, n) swap(mat[i][k], mat[j][k]); tr ^= 1;
}
}
int ans = 1; For (i, 1, n) ans = 1ll * ans * mat[i][i] % ZZQ;
return tr ? (ZZQ - ans) % ZZQ : ans;
}
五、應用:生成樹計數之矩陣-樹定理
階無向圖的一些定義:
鄰接矩陣 :有邊 則 ,否則爲 。特別地,如果圖有重邊,那麼 和 等於邊 的條數。
度數矩陣 : 爲點 的度數,即由 發出的邊數。 其他位置的數爲 。
基爾霍夫矩陣: 。
基爾霍夫矩陣每行內數的和和每列內的和都爲 ,所以行列式爲 。
餘子式:一個矩陣 的餘子式 表示 去掉第 行第 列後得到矩陣的行列式。
矩陣-樹定理,又稱基爾霍夫定理。先說結論:
無向圖的生成樹個數等於其基爾霍夫矩陣 的任意餘子式 (注意是 不是 )的行列式。
如果圖不連通,那麼任意餘子式 爲 。
證明:如果圖不連通,那麼每個連通塊內的點構成的矩陣仍然是基爾霍夫矩陣。
而連通塊不止一個,所以去掉第 行第 列之後,一定有一個連通塊仍然是基爾霍夫矩陣,也就是行列式爲 。
六、證明
一
首先證明:一棵樹,其基爾霍夫矩陣的任意餘子式 等於 。
爲了方便討論,我們交換第 行和第 行,再交換第 列和第 列,我們要證明的只是 。
定 爲根。
將 到 的點重新排列,使同一個子樹內的點編號形成一個區間。
我們會發現,把第 行第 列去掉,就相當於分離了 的所有子樹。
我們可以把這個 階方陣拆成 的度數個,每個小矩陣爲一個子樹的基爾霍夫矩陣,根爲 則矩陣第 行第 列加一。
易得 爲這些小矩陣的行列式之積(因爲一個 到 的排列和一個 到 的排列合併起來,相當於對應 位置的乘積相乘,逆序對數相加)。
我們還要引入一個定理:
將基爾霍夫矩陣的第 行第 列憑空加上 之後,行列式等於 。
我們考慮用歸納法同時證明兩個結論。
考慮行列式的式子中,每個排列 對行列式的貢獻。
證明第二個結論:
先討論 時,易得這對行列式的貢獻爲( 的度數加 )再乘以所有子樹的(根所在主對角線位置加 後得到的行列式),也就是 的度數加 。
再考慮如果 ,那麼 一定是 的子節點。 的取法數爲 的度數。
分析一下可以得到,這時候 必須爲 ,否則對行列式的貢獻爲 。
這樣看上去對行列式的貢獻爲 ,但是由於 和 貢獻了一個逆序對,對行列式的貢獻爲 。
所以, 對行列式的貢獻爲 ( 的度數)。
貢獻相加一下就證明了結論二。
於是結論一也得證。
二
一起來證明 Matrix-Tree 定理。
先構建一個輔助矩陣 , 一個 的矩陣, 爲邊數。
對於第 條邊 , 。
易得 。
根據 Binet-Cauthy 定理:
表示 只保留列向量集合 後得到的矩陣。
表示 只保留行向量集合 後得到的矩陣。
這個定理簡單地說,一個 的矩陣乘以一個 的矩陣後的行列式,等於枚舉一個大小爲 的集合 且 ,求 和 分別只保留 對應的列向量和行向量集合後的方陣乘積的行列式,並把所有 對應求出的行列式加起來。
所以:
設 表示 去掉第 行第 列後的子矩陣,
表示邊集 構成的基爾霍夫矩陣。
討論下這個式子。
相當於在 中選出 條邊。
(1)選出的邊構成環或不連通。
實際上, 條邊連通 個點的形態只能是樹。
所以構成環和不連通其實是一樣的。
由之前證出的定理得到貢獻爲 。
(2)選出的邊構成樹。
根據基爾霍夫矩陣的性質,得到貢獻爲 。
於是得證!!!!!
七、題目 & 推廣
[BZOJ4031][HEOI2015]小Z的房間:
https://www.lydsy.com/JudgeOnline/problem.php?id=4031
[BZOJ4596][Shoi2016]黑暗前的幻想鄉:
https://www.lydsy.com/JudgeOnline/problem.php?id=4596
Alice:爲什麼我們要證明矩陣-樹定理呢?
Bob:在應用中,矩陣-樹定理有一些拓展。
下面給出兩種常見拓展。
(1)有向圖生成樹計數。
①內向樹生成樹計數。
爲鄰接矩陣, 爲出度矩陣。
。
以 爲根的內向生成樹個數就是 的餘子式 的行列式。
②外向樹生成樹計數。
爲鄰接矩陣, 爲入度矩陣。
。
以 爲根的外向生成樹個數就是 的餘子式 的行列式。
[BZOJ5297][Cqoi2018]社交網絡:
https://www.lydsy.com/JudgeOnline/problem.php?id=5297
(2)邊帶權。
給定一個邊帶權的圖,求其所有生成樹的邊權積之和。
爲邊權矩陣, 爲發出邊權和矩陣。
即對於一條邊 ,邊權爲 , 。
當然如果有重邊, 爲所有邊 的權值和。
等於由 發出的所有邊的權值之和。
對於任意 , 。
。
所有生成樹的邊權積之和等於 的任意餘子式 的行列式。
[BZOJ3534][Sdoi2014]重建:
https://www.lydsy.com/JudgeOnline/problem.php?id=3534