今天学习了矩阵树定理,就是求解无向图最小生成树个数的一个东西
首先需要构造无向图的基尔霍夫矩阵,我们记为矩阵K。矩阵K满足,
其中D是这个无向图的度数矩阵,在对角线上值是每个点的度数,其他位置的值都是0,A是这个无向图的出入度矩阵,第 行 列元素表示点 到点 之间的边数
这个矩阵,随意选择下标相同的一行一列挖去,那么剩下的行列式的值就是这个无向图的最小生成树个数。
行列式具有以下性质(针对行,列同理)
- 交换任意两行,行列式的值乘-1
- 如果任意两行相同,行列式的值为0
- 行列式中某一行的系数可以提取到行列式外
- 行列式中,某一行可以表示成两个数相加的形式,那么可以从这一行拆开,行列式的值就是拆分后两个行列式的和
- 行列式对应矩阵做第三类初等变换,某一行乘k加到另一行,行列式的值不变
通过这些性质,我们知道了行列式的值可以使用高斯消元来求解
P4111 [HEOI2015]小 Z 的房间
这是一个矩阵树的模板题,按上述的方式建图,并且最后注意高斯消元求解行列式的时候,我们需要取mod,因此我们要使用辗转相除的方法来完成一次消元,因此整体的时间复杂度
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
int Gcd(int a,int b){if (b == 0) return a; return Gcd(b , a%b);}
int Lcm(int a, int b){ return a/Gcd(a,b)*b;}
inline long long read(){
long long f = 1, x = 0;char ch = getchar();
while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
const int maxn = 1e3 + 10;
const LL mod = 1e9;
char s[maxn][maxn];
int id[maxn][maxn];
LL a[maxn][maxn];
int n,m,tot;
void add(int u,int v){
a[u][v]--;
a[v][u]--;
a[u][u]++;
a[v][v]++;
}
LL gauss(int n){
LL ans = 1;
for(int i=1; i<=n; i++){
for(int k=i+1; k<=n; k++){
while(a[k][i]){
LL d = a[i][i] / a[k][i];
for(int j=i; j<=n; j++){
a[i][j] = (a[i][j] - 1ll * d * a[k][j] % mod + mod) % mod;
}
swap(a[i],a[k]);
ans = -ans;
}
}
ans = 1ll * ans * a[i][i] % mod;
ans = (ans + mod) % mod;
}
return ans;
}
int main(){
n = read(),m = read(),tot = 0;
for(int i=1; i<=n; i++){
scanf("%s",s[i] + 1);
for(int j=1; j<=m; j++){
if (s[i][j] == '.'){
id[i][j] = ++tot;
}
}
}
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
if (s[i][j] == '.'){
if (id[i-1][j]){
add(id[i-1][j],id[i][j]);
}
if (id[i][j-1]){
add(id[i][j-1],id[i][j]);
}
}
}
}
cout << (gauss(tot - 1) % mod + mod) % mod << endl;
return 0;
}```
来源:CSDN
作者:风声sp
链接:https://blog.csdn.net/CCCCTong/article/details/104753337