关于生成树计数,有一个叫matrix-tree定理的东西.可以参考《生成树的计数及其应
用》.
主要就是把原图用一个 Kirchhoff矩阵存下来,这个矩阵是原图的关联矩阵和自身的
转置的乘积,简单处理就是对角线表示这个节点的度数,然后矩阵中其他元素如果存
在这条边就是-1,否则就是0.
然后这个图的生成树个数就是Kirchhoff矩阵任意n-1阶主子式的行列式值.
UVA 10766:点击打开链接
题意是给定m条边,求反图的生成树个数,k是没用的.
对反图建立Kirchhoff矩阵,然后直接求n-1阶主子式的值就好了.
坑点是要用long double,还有注意重边处理.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
#define maxn 55
int n, m, k;
bool mp[maxn][maxn];
int degree[maxn];
#define eps 1e-10
long double a[maxn][maxn];
long long det () {
long double ans = 1.0;
int i, j, k, col, max_r;
for (k = 0, col = 0; k < n && col < n; k++, col++) {//转化成上三角矩阵
max_r = k;
for (int i = k+1; i < n; i++) { //找到最大的绝对值数所在的行
if (fabs (a[i][col]) > fabs (a[max_r][col]))
max_r = i;
}
if (fabs (a[max_r][col]) <= eps)
return 0;
if (k != max_r) { //交换行
for (int j = col; j < n; j++) {
swap (a[k][j], a[max_r][j]);
}
ans *= (-1);//行列式换行改变符号
}
for (int i = k+1; i < n; i++) { //消去
if (a[i][col]) {
long double tmp = -a[i][col]/a[k][col];
for (int j = col; j < n; j++) {
a[i][j] += tmp*a[k][j];
}
}
}
}
for (i = 0; i < n; i++) ans *= a[i][i];
long long p1 = ceil (ans), p2 = floor (ans);
if (ans-p2 < p1-ans)
return p2;
else
return p1;
}
int main () {
//freopen ("in.txt", "r", stdin);
while (cin >> n >> m >> k) {
memset (mp, 0, sizeof mp);
memset (degree, 0, sizeof degree);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
if (!mp[u][v])
degree[u]++, degree[v]++;
mp[u][v] = mp[v][u] = 1;
}
for (int i = 1; i <= n; i++) degree[i] = n-1-degree[i];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) {
a[i-1][j-1] = degree[i];
}
else if (mp[i][j]) {
a[i-1][j-1] = 0;
}
else
a[i-1][j-1] = -1;
}
}
n--;
long long ans = det ();
cout << ans << "\n";
}
return 0;
}