矩阵树定理
想必大家应该都会高斯消元吧,不会的话可以看模板,我们现在着重讲一下建矩阵的方法。
前置知识:我们用到的矩阵,也就是基尔霍夫矩阵的任意一个代数余子式是所有生成树的边权积的和。
当所有边边权为1时求的就是生成树的个数了。
我们以下设 \((x,y,z)\) 为 \(x\) 到 \(y\) 有一条边权为 \(z\) 的 无向/有向 边。
1.无向图
假设现在给定一个图 \(G\) .
度数矩阵D:若存在边 \((x,y,z)\) ,则 \(D[x][x]+=z;D[y][y]+=z;\)
邻接矩阵C:若存在边 \((x,y,z)\) ,则 \(C[x][y]+=z;C[y][x]+=z;\)
图G的基尔霍夫矩阵 \(A=D−C\) 。
删去任意一行和任意一列,求剩下的矩阵行列式即可。
2.有向图
假设现在给定一个图G.
度数矩阵D:若存在边 \((x,y,z)\) ,则 外向树中 \(D[y][y]+=z;\) 内向树中 \(D[x][x]+=z;\)
邻接矩阵C:若存在边 \((x,y,z)\) ,则 内向树和外向树中均为 \(C[x][y]+=z;\)
图G的基尔霍夫矩阵 \(A=D−C\) 。
删去指定的根所在的行和列,求剩下的矩阵行列式即可。
本题中即为外向树的情况.
#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;
int n, m, t, x, y, z, ans = 1;
const int N = 305, mod = 1e9 + 7;
int a[N][N];
int ksm(int a, int b, int mod)
{
int res = 1;
for (; b; b >>= 1, a = (LL)a * a % mod)
if (b & 1)res = (LL)res * a % mod;
return res;
}
void work()
{
for (int i = 2, inv, tmp; i <= n; ++i)
{
for (int j = i + 1; j <= n; ++j)
if (!a[i][i] && a[j][i]) {ans = -ans; swap(a[i], a[j]); break;}
inv = ksm(a[i][i], mod - 2, mod);
for (int j = i + 1; j <= n; ++j)
{
tmp = (LL)a[j][i] * inv % mod;
for (int k = i; k <= n; ++k)a[j][k] = (a[j][k] - (LL)a[i][k] * tmp % mod) % mod;
}
}
}
signed main()
{
cin >> n >> m >> t;
for (int i = 1; i <= m; ++i)
{
scanf("%d%d%d", &x, &y, &z);
if (!t)
{
(a[x][x] += z) %= mod; (a[y][y] += z) %= mod;
(a[x][y] -= z) %= mod; (a[y][x] -= z) %= mod;
}
else
{
(a[y][y] += z) %= mod; (a[x][y] -= z) %= mod;
}
}
work();
for (int i = 2; i <= n; ++i)ans = (LL)ans * a[i][i] % mod;
cout << (ans % mod + mod) % mod;
return 0;
}