矩阵树定理与BEST定理
行列式
定义:\(\det(A)\),又记作 \(\vert A\vert\),等于 \(\sum_p (-1)^{\tau(p)} \prod_{i = 1}^nA_{i, p_i}\),其中 \(p\) 为 \(1 \sim n\) 的排列,\(\tau(p)\) 表示排列 \(p\) 的逆序对数。
1. 对于一个上三角矩阵,他的行列式等于主对角线所有值的乘积。
要使 \(A_{n, p_n} \ne 0\),\(p_n\) 只能取 \(n\);要使 \(A_{n - 1, p_{n - 1}} \ne 0\),\(p_{n - 1} \in \{n - 1, n\}\),但是 \(n\) 已经被用过了,\(p_{n - 1} = n - 1\)。
以此类推,使 \(\prod A_{i, p_i}\) 非零的只有唯一一个排列 \(p_i = i\),此时 \(\tau(p) = 0\)。
2. 单位矩阵的行列式为 \(1\),即 \(\vert I\vert = 1\)。
根据 \(I_{i, j} = [i = j]\) 以及 定理1 易证。
3. 交换矩阵两行,行列式变号。
交换 \(i, j\) 等价于交换每个排列的 \(p_i, p_j\),这会使所有 \(\tau(p)\) 奇偶性改变,相当于整体乘了个 \(-1\)。
4. 将矩阵某行乘上一个常数,行列式乘上相同常数。
不妨将第 \(r\) 行所有元素乘 \(k\),\(\prod_{i = 1}^n A_{i, p_i}\) 恰好包含一个第 \(r\) 行元素,把 \(k\) 提出来。
5. 若矩阵有相同的两行,则行列式为 \(0\)。
交换两行会使行列式变号,但是矩阵没变,即 \(\vert A\vert = -\vert A\vert \implies \vert A\vert = 0\)。
6. 若矩阵有两行存在倍数关系,则行列式为 \(0\)。
\(k \vert A\vert = -k\vert A\vert \implies \vert A\vert = 0\)。
7. 若两个矩阵至多有一行不等,将这不等的一行相加得到的新矩阵的行列式等于原行列式之和。
设不等的行编号为 \(c\),新矩阵 \(S\) 满足 \(S_i = A_i = B_i,\ S_c = A_c + B_c(i \ne c)\)。
\(\prod_{i = 1}^nS_{i, p_i} = (A_{c, p_c} + B_{c, p_c})\prod_{i = 1}^n[i\ne c]A_{i, p_i} = \prod_{i = 1}^nA_{i, p_i} + \prod_{i = 1}^nB_{i, p_i}\)。
8. 将矩阵的某行加上另一行的倍数,行列式不变。
设把 \(c\) 的 \(k\) 倍加到 \(d\) 上,设新矩阵为 \(C\)。
设 \(B_i = A_i, B_d = kA_c(i \ne c)\),根据定理 7可得 \(\vert A\vert + \vert B\vert = \vert C\vert\),根据定理6可得 \(\vert B\vert = 0\)。
高斯消元
步骤略。
题意:求一个 \(n \times n\) 的矩阵 \(A\) 在模 \(10^9 + 7\) 意义下的逆矩阵。
将 \(A\) 进行线性变换相当于左乘一个矩阵,也就是说把 \(A\) 变换(消元)到单位矩阵 \(I\),相当于左乘 \(A^{-1}\)。
有恒等式 \(A^{-1} \times A = I\),那么在 \(A\) 变换到 \(I\) 的同时 \(I\) 也做同样的变换,最后得到 \(A^{-1} \times I = A^{-1}\)。
矩阵可逆当且仅当 \(\vert A\vert \ne 0\),即满秩。submission
题意:给定一个 \(n\) 阶行列式,求 \(\vert A\vert\)。结果对 \(p\) 取模,不保证 \(p\) 是素数。
把 \(A\) 消成上三角矩阵,最后答案就是主对角线之积。
加上某一行的倍数不改变答案,而交换两行会使符号改变,因此记录交换了多少次。
没有必要对一行乘某个常数的操作,否则还有额外记录乘了多少东西。
\(p\) 没有特殊性质,不能求逆元,因此采用类似「辗转相除法」对两行消元,时间复杂度 \(O(n^3\log p)\)。
#include<bits/stdc++.h>
#define eb emplace_back
#define ep emplace
using namespace std;
using ll = long long;
int n, p, sgn; ll a[605][605];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> p;
for(int i = 1; i <= n; ++ i) {
for(int j = 1; j <= n; ++ j) {
cin >> a[i][j];
a[i][j] %= p;
}
}
for(int k = 1; k <= n; ++ k) {
for(int i = k; i <= n; ++ i) {
if(a[i][k]) {
if(i != k) {
swap(a[i], a[k]), sgn ^= 1;
}
break;
}
}
if(!a[k][k]) return cout << 0, 0;
for(int i = k + 1; i <= n; ++ i) {
while(a[i][k]) {
if(a[i][k] < a[k][k]) {
swap(a[i], a[k]), sgn ^= 1;
}
ll q = a[i][k] / a[k][k];
for(int j = k; j <= n; ++ j) {
a[i][j] = (a[i][j] + p - q * a[k][j] % p) % p;
}
}
}
}
ll tmp = 1;
for(int i = 1; i <= n; ++ i) tmp = tmp * a[i][i] % p;
cout << (sgn ? (p - tmp) % p : tmp);
return 0;
}
矩阵树定理
无向图生成树个数
设 \(G\) 是一个有 \(n\) 个点的无向图,定义度数矩阵 \(D\) 为:
设 \(e(i, j)\) 表示点 \(i\) 与点 \(j\) 相连的边数,并定义邻接矩阵 \(A\) 为:
定义拉普拉斯矩阵:
记图 \(G\) 的所有生成树个数为 \(t\)。
其中记号 \(L(G)\binom{1, 2, \cdots, i - 1, i + 1, \cdots, n}{1, 2, \cdots, i - 1, i + 1, \cdots, n}\) 表示矩阵 \(L\) 的第 \([1, i)(i, n]\) 行与 \([1, i)(i, n]\) 列构成的子矩阵。
无向图拉普拉斯矩阵所有 \(n - 1\) 阶主子式都相等。
有向图生成树个数
定义出度矩阵 \(D_{out}(i, i) = \deg^{out}(i), D_{out}(i, j \ne i) = 0\)。
设 \(e(i, j)\) 表示 \(i\) 指向 \(j\) 的有向边数,定义邻接矩阵 \(A\):
定义出度拉普拉斯矩阵 \(L_{out}\):
同理定义入度矩阵 \(D_{in}, L_{in}\)。
记以 \(r\) 为根的所有内向生成树个数为 \(t^{root}(r)\),内向表示所有边全部指向父亲。
记以 \(r\) 为根的所有外向生成树个数为 \(t^{leaf}(r)\),外向表示所有边全部指向儿子。
边权积的和
对于一条 \((u, v, w)\) 会对该方案贡献 \(w\) 的系数,这等价于拆成 \(w\) 条 \((u, v)\),方案数乘 \(w\)。
BEST定理
设 \(G\) 是有向欧拉图,那么 \(G\) 的不同欧拉回路总数:
欧拉图任意节点为根的外向树数量相等,且所有节点出度和入度相等(这也说明 \(t^{root}(k) = t^{leaf}(k)\))。
注意:BEST定理只能在欧拉图上使用。
P5807 【模板】BEST 定理 | Which Dreamed It
题意:求从 \(1\) 开始的欧拉回路数量。
对于一条欧拉回路,保留除 \(1\) 外所有点路径中的最后一条出边,\(1\) 的出边全部删掉。
此时图中一定无环。
这样除了 \(1\) 每个点会被保留恰好一条出边,且整张图连通,形成一颗以 \(1\) 为根的内向树。
我们钦定一棵内向树,然后对于其他边任意定顺序,一定唯一对应一个欧拉回路。
充分性有了,考虑必要性,显然每一个欧拉回路都唯一对应一颗内向树和其中遍历出边的顺序。
因此总方案就是
由此可以引出BEST定理,统计整张图本质相同的欧拉回路个数(循环同构)。
\(1\) 会在一个欧拉回路会出现 \(d_1\) 次,每种方案都能旋转生成 \(d_1\) 中以 \(1\) 为起点的方案,因此每种方案被重复计算 \(d_1\) 次:

浙公网安备 33010602011771号