Matrix-Tree 定理小记
发现身边的神仙们都会矩阵树定理,我这个菜鸡还不会,就来学了一下(
声明:本文 几乎 没有什么证明,感兴趣的读者可以看 这里。
行列式
定义
对于一个矩阵 \(A_{i,j}\),它的行列式 \(\det(A)=\sum\limits_{P}(-1)^{\tau(P)}\prod\limits_{i=1}^nA_{i,P_i}\)。
其中 \(P\) 为 \(1\sim n\) 的一个排列,\(\tau(P)\) 为 \(P\) 的逆序对数。
性质
- 上三角矩阵与下三角矩阵的行列式为对角线的乘积;
- 交换矩阵两行,矩阵行列式的值变号;
- 将矩阵的某一行乘 \(k\),则矩阵行列式的值也乘 \(k\);
- 矩阵中如果有两行(列)的值相同,则矩阵行列式的值为 \(0\);
- 将矩阵某一行(列)加上另一行(列)的 \(k\) 倍,行列式的值不变。
代码
使用消元法求解。
#include <bits/stdc++.h>
#define DEBUG fprintf(stderr, "Passing [%s] line %d\n", __FUNCTION__, __LINE__)
#define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout)
#define DC int T = gi <int> (); while (T--)
using namespace std;
typedef long long LL;
typedef pair <int, int> PII;
typedef pair <int, PII> PIII;
template <typename T>
inline T gi()
{
T f = 1, x = 0; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return f * x;
}
const int INF = 0x3f3f3f3f, N = 603, M = N << 1;
int n, mod;
LL a[N][N];
inline LL solve()
{
LL ans = 1, op = 1;
for (int i = 1; i <= n; i+=1)
{
for (int j = i + 1; j <= n; j+=1)
{
while (a[i][i])
{
int p = a[j][i] / a[i][i];
for (int k = i; k <= n; k+=1)
{
a[j][k] = ((a[j][k] - 1ll * p * a[i][k]) % mod + mod) % mod;
swap(a[i][k], a[j][k]);
}
op *= -1;
}
swap(a[i], a[j]);
op *= -1;
}
ans = ans * a[i][i] % mod;
}
return (ans * op % mod + mod) % mod;
}
int main()
{
n = gi <int> (), mod = gi <int> ();
for (int i = 1; i <= n; i+=1) for (int j = 1; j <= n; j+=1) a[i][j] = gi <LL> ();
printf("%lld\n", solve());
return 0;
}
无向图
设一张图 \(G\) 的度数矩阵为 \(D\),邻接矩阵为 \(A\)。
易得:
\[D_{i,j}=
\begin{cases}
\deg(i)&{i=j}\\
0&{i\not =j}
\end{cases}
\]
则图的基尔霍夫(Kirchhoff)矩阵 \(L=D-A\)。
设 \(L'\) 为基尔霍夫矩阵 \(L\) 去掉第 \(i\) 行和第 \(i\) 列(其中 \(1\le i\le n\))得到的新矩阵,则 \(\det(L')\) 即为图 \(G\) 的生成树个数(\(\det(L')\) 为矩阵 \(L'\) 的行列式)。
有向图
修改一下度数矩阵 \(D\) 的定义:
- 若题目要求的是外向树个数,则 \(D\) 为入度矩阵,即:
\[D_{i,j}=
\begin{cases}
\deg_{in}(i)&{i=j}\\
0&{i\not =j}
\end{cases}
\]
- 若题目要求的是内向树个数,则 \(D\) 为出度矩阵,即:
\[D_{i,j}=
\begin{cases}
\deg_{out}(i)&{i=j}\\
0&{i\not =j}
\end{cases}
\]
注意这里依然要去掉一行一列,只是这里去掉的第 \(i\) 行第 \(i\) 列就代表生成树的根为 \(i\)。
代码
#include <bits/stdc++.h>
#define DEBUG fprintf(stderr, "Passing [%s] line %d\n", __FUNCTION__, __LINE__)
#define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout)
#define DC int T = gi <int> (); while (T--)
using namespace std;
typedef long long LL;
typedef pair <int, int> PII;
typedef pair <int, PII> PIII;
template <typename T>
inline T gi()
{
T f = 1, x = 0; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return f * x;
}
const int INF = 0x3f3f3f3f, N = 303, M = N << 1, mod = 1000000007;
int n, m, t;
LL d[N][N], a[N][N], l[N][N];
inline LL solve()
{
LL ans = 1, op = 1;
for (int i = 2; i <= n; i+=1)
{
for (int j = i + 1; j <= n; j+=1)
{
while (l[i][i])
{
int p = l[j][i] / l[i][i];
for (int k = i; k <= n; k+=1)
{
l[j][k] = ((l[j][k] - 1ll * p * l[i][k] % mod) % mod + mod) % mod;
swap(l[i][k], l[j][k]);
}
op *= -1;
}
swap(l[i], l[j]);
op *= -1;
}
ans = ans * l[i][i] % mod;
}
return (ans * op % mod + mod) % mod;
}
int main()
{
//File("");
n = gi <int> (), m = gi <int> (), t = gi <int> ();
for (int i = 1; i <= m; i+=1)
{
int u = gi <int> (), v = gi <int> (), w = gi <int> ();
if (!t)
{
(d[u][u] += w) %= mod, (d[v][v] += w) %= mod;
(a[u][v] += w) %= mod, (a[v][u] += w) %= mod;
}
else
{
(d[v][v] += w) %= mod;
(a[u][v] += w) %= mod;
}
}
for (int i = 1; i <= n; i+=1)
for (int j = 1; j <= n; j+=1)
l[i][j] = d[i][j] - a[i][j];
printf("%lld\n", solve());
return 0;
}
参考学习
https://www.cnblogs.com/zj75211/p/8039443.html
https://www.luogu.com.cn/blog/command-block/ju-zhen-shu-ding-li-xing-lie-shi-post

浙公网安备 33010602011771号