1. 程式人生 > 其它 >筆記-矩陣樹定理

筆記-矩陣樹定理

無向圖生成樹計數

考慮構建拉普拉斯矩陣:

\[L(i,j)=\left\{ \begin{aligned} -m(i,j)(i\ne j) \\ deg(i,j)(i=j) \\ \end{aligned} \right. \]

在去某一行和列後 \(det(L)\) 即為答案。

證明略(後面有時間會補)。

模板:P4111

行列式即為變換為上三角矩陣後對角線上的乘積,所以高斯消元即可。

但是這道垃圾題模數不是質數,我們通過輾轉相除即可。

複雜度 \(\tilde{O}((nm)^3)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9;
int n, m, num, id[11][11];
char s[11];
ll L[101][101];
int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
ll Gauss() {
	ll ret = 1;
	int N = num - 1;
	for (int i = 1; i <= N; i++) {
		for (int j = i + 1; j <= N; j++) {//消第 j 行 
			while (L[j][i]) {
				ll rate = L[i][i] / L[j][i];
				for (int k = i; k <= N; k++) {
					L[i][k] = (L[i][k] - L[j][k] * rate % mod + mod) % mod;
				}
				swap(L[i], L[j]);
				ret = -ret;
			}
		}
		ret = ret * L[i][i] % mod;
		ret = (ret + mod) % mod;
	}
	return ret;
}
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		scanf("%s", s + 1);
		for (int j = 1; j <= m; j++) {
			if (s[j] == '.') id[i][j] = ++num;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (!id[i][j]) continue;
			for (int k = 0; k < 4; k++) {
				if (!id[i + dx[k]][j + dy[k]]) continue;
				L[id[i][j]][id[i][j]]++;
				L[id[i][j]][id[i + dx[k]][j + dy[k]]]--;
			}
		}
	}
	cout << Gauss() << "\n";
	return 0;
} 

有向圖有根外向樹計數

重新定義拉普拉斯矩陣:

\[L(i,j)=\left\{ \begin{aligned} -m(i,j)(i\ne j) \\ out(i)(i=j) \\ \end{aligned} \right. \]

有向圖有根外向樹計數

重新定義拉普拉斯矩陣:

\[L(i,j)=\left\{ \begin{aligned} -m(i,j)(i\ne j) \\ in(i)(i=j) \\ \end{aligned} \right. \]

注意有向圖定根必須去掉根的那行和那列。