矩陣樹定理也稱Matrix-Tree定理或Kirchhoff定理。這個定理提供了一種方式使用一個特殊的矩陣的行列式來計算一個圖的生成樹的數(shù)量。
對于一個無向圖來說,我們可以構(gòu)造它的Laplace矩陣L,其中:
如果節(jié)點(diǎn)i有度為d,那么L[i,i] = d
如果節(jié)點(diǎn)i和節(jié)點(diǎn)j之間有邊相連,那么L[i,j] = L[j,i] =-1
其他情況L[i,j]=0
例如下圖的Laplace矩陣:
那么我們從L中任意刪除某一行和某一列剩下的矩陣,對其求行列式,例如刪除第一行和第一列:
那么這個圖的生成樹的數(shù)量為3,可以證明刪除任意行列的行列式的值不變。
注意,Cayley定理是矩陣樹定理的一個特殊情況,因?yàn)镃ayley定理說明,一個n個節(jié)點(diǎn)的組成樹的數(shù)量為這正好是n個節(jié)點(diǎn)完全圖的生成樹的數(shù)量,因?yàn)椋?/p>
去掉第一行第一列之后:
例題:
你突然有了一個大房子,房子里面有一些房間。事實(shí)上,你的房子可以看做是一個包含n×m個格子的格狀矩形,每個格子是一個房間或者是一個柱子。在一開始的時候,相鄰的格子之間都有墻隔著。
你想要打通一些相鄰房間的墻,使得所有房間能夠互相到達(dá)。在此過程中,你不能把房子給打穿,或者打通柱子(以及柱子旁邊的墻)。同時,你不希望在房子中有小偷的時候會很難抓,所以你希望任意兩個房間之間都只有一條通路?,F(xiàn)在,你希望統(tǒng)計一共有多少種可行的方案,答案對取模。
輸入格式:
第一行兩個整數(shù)n,m。
接下來n行,每行m個字符 . 或 *,其中 . 代表房間,* 代表柱子。
輸出格式:
一行一個整數(shù),表示合法的方案數(shù)對取模后的值。
輸入輸出樣例
說明/提示
數(shù)據(jù)規(guī)模與約定
對于20%的數(shù)據(jù),n,m≤3。
對于50%的數(shù)據(jù),n,m≤5。
有40%的數(shù)據(jù),min(n,m)≤3。
有30%的數(shù)據(jù),不存在柱子。
對于100%的數(shù)據(jù),1≤n,m≤9
代碼如下:
#include <bits/stdc++.h> using namespace std; #define FR freopen("in.txt", "r", stdin) #define FW freopen("out.txt", "w", stdout) typedef long long ll; int mp[15][15]; ll laplace[100][100]; int n, m; int pos[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; int tot = 1; const ll mod = 1000000000; int main() { scanf("%d %d", &n, &m); for (int r = 1; r <= n; r++) { for (int c = 1; c <= m; c++) { char t; scanf(" %c", &t); if (t == '.') { mp[r][c] = tot++; } } } for (int r = 1; r <= n; r++) { for (int c = 1; c <= m; c++) { if (mp[r][c] == 0) continue; for (int p = 0; p < 4; p++) { int dr = r + pos[p][0]; int dc = c + pos[p][1]; if (mp[dr][dc] == 0) continue; laplace[mp[r][c]][mp[r][c]]++; laplace[mp[r][c]][mp[dr][dc]] = laplace[mp[dr][dc]][mp[r][c]] = -1; } } } // 化上三角矩陣 tot--; ll sgn = 1; for (int r = 1; r < tot; r++) { int d = r; for (int k = r; k < tot; k++) { if (laplace[k][r] != 0) { d = k; break; } } swap(laplace[d], laplace[r]); if (laplace[r][r] == 0) { printf("0"); return 0; } for (int k = r + 1; k < tot; k++) { while (laplace[k][r] != 0) { swap(laplace[r], laplace[k]); sgn *= -1; ll di = laplace[k][r] / laplace[r][r]; for (int j = r; j < tot; j++) { laplace[k][j] = ((laplace[k][j] - laplace[r][j] * di) % mod + mod) % mod; } } } } ll ans = 1; for (int i = 1; i < tot; i++) { ans = ((ans * laplace[i][i]) % mod + mod) % mod; } ans = (ans * sgn + mod) % mod; printf("%lld", ans); return 0; }
C語言網(wǎng)提供由在職研發(fā)工程師或ACM藍(lán)橋杯競賽優(yōu)秀選手錄制的視頻教程,并配有習(xí)題和答疑,點(diǎn)擊了解:
一點(diǎn)編程也不會寫的:零基礎(chǔ)C語言學(xué)練課程
解決困擾你多年的C語言疑難雜癥特性的C語言進(jìn)階課程
從零到寫出一個爬蟲的Python編程課程
只會語法寫不出代碼?手把手帶你寫100個編程真題的編程百練課程
信息學(xué)奧賽或C++選手的 必學(xué)C++課程
藍(lán)橋杯ACM、信息學(xué)奧賽的必學(xué)課程:算法競賽課入門課程
手把手講解近五年真題的藍(lán)橋杯輔導(dǎo)課程