【学习笔记】矩阵树定理(Matrix-Tree)
整理的算法模板合集: ACM模板
点我看算法全家桶系列!!!
实际上是一个全新的精炼模板整合计划
目录
- 一、矩阵树定理
- 二、常用定理
- 三、例题
- 1. Luogu P6178 【模板】Matrix-Tree 定理(外向树)
- 2. P4455 [CQOI2018]社交网络(内向树)
- 3. Luogu P4336 [SHOI2016]黑暗前的幻想乡(容斥,矩阵树定理)
- 4. P3317 [SDOI2014]重建
- 5. BZOJ3659 : Which Dreamed It(BEST 定理)
一、矩阵树定理
对于无向图 GGG
定义 GGG 的度数矩阵 DDD 满足:
dij={degii=j0i≠jd_{i j}=\left\{\begin{array}{rl}\operatorname{deg}_{i} & i=j \\0 & i \neq j\end{array}\right. dij={degi0i=ji=j
即:矩阵 DDD 是除了对角线以外各个点值都为 000 的矩阵,D[i][i]
表示 iii 号点的度数。
定义 GGG 的邻接矩阵 CCC 满足:
cij={0i=jadjiji≠jc_{i j}=\left\{\begin{array}{rl}0 & i=j \\\text{adj}_{i j} & i \neq j\end{array}\right. cij={0adjiji=ji=j
即:矩阵 CCC 记录两点之间的度数,C[i][j]
表示 iii 号点与 jjj 号点之间的边数
定义 GGG 的基尔霍夫矩阵 L(G)=D−CL(G)=D-CL(G)=D−C, 则图 GGG 的生成树数量是 L(G)L(G)L(G) 的任意一个代数余子式。
矩阵树定理完整证明(包括下面的常用定理证明):https://www.luogu.com.cn/blog/juruodewo/solution-p6178
二、常用定理
定理 1(矩阵树定理,无向图行列式形式) 对于任意的 iii,都有
t(G)=detL(G)(1,2,⋯,i−1,i+1,⋯,n1,2,⋯,i−1,i+1,⋯,n)t(G) = \det L(G)\binom{1,2,\cdots,i-1,i+1,\cdots,n}{1,2,\cdots,i-1,i+1,\cdots,n} t(G)=detL(G)(1,2,⋯,i−1,i+1,⋯,n1,2,⋯,i−1,i+1,⋯,n)
其中记号 L(G)(1,2,⋯,i−1,i+1,⋯,n1,2,⋯,i−1,i+1,⋯,n)\displaystyle L(G)\binom{1,2,\cdots,i-1,i+1,\cdots,n}{1,2,\cdots,i-1,i+1,\cdots,n}L(G)(1,2,⋯,i−1,i+1,⋯,n1,2,⋯,i−1,i+1,⋯,n) 表示矩阵 L(G)L(G)L(G) 的第 1,⋯,i−1,i+1,⋯,n1,\cdots,i-1,i+1,\cdots,n1,⋯,i−1,i+1,⋯,n 行与第 1,⋯,i−1,i+1,⋯,n1,\cdots,i-1,i+1,\cdots,n1,⋯,i−1,i+1,⋯,n 列构成的子矩阵。也就是说,无向图的 Laplace 矩阵具有这样的性质,它的所有 n−1n-1n−1 阶主子式都相等。
定理 2(矩阵树定理,无向图特征值形式) 设 λ1,λ2,⋯,λn−1\lambda_1, \lambda_2, \cdots, \lambda_{n-1}λ1,λ2,⋯,λn−1 为 L(G)L(G)L(G) 的 n−1n - 1n−1 个非零特征值,那么有
t(G)=1nλ1λ2⋯λn−1t(G) = \frac{1}{n}\lambda_1\lambda_2\cdots\lambda_{n-1}t(G)=n1λ1λ2⋯λn−1
定理 3(矩阵树定理,有向图根向形式) 对于任意的 kkk,都有
troot(G,k)=detLout(G)(1,2,⋯,k−1,k+1,⋯,n1,2,⋯,k−1,k+1,⋯,n)t^{root}(G,k) = \det L^{out}(G)\binom{1,2,\cdots,k-1,k+1,\cdots,n}{1,2,\cdots,k-1,k+1,\cdots,n} troot(G,k)=detLout(G)(1,2,⋯,k−1,k+1,⋯,n1,2,⋯,k−1,k+1,⋯,n)
因此如果要统计一张图所有的根向树形图,只要枚举所有的根 kkk 并对 troot(G,k)t^{root}(G,k)troot(G,k) 求和即可。
定理 4(矩阵树定理,有向图叶向形式) 对于任意的 kkk,都有
tleaf(G,k)=detLin(G)(1,2,⋯,k−1,k+1,⋯,n1,2,⋯,k−1,k+1,⋯,n)t^{leaf}(G,k) = \det L^{in}(G)\binom{1,2,\cdots,k-1,k+1,\cdots,n}{1,2,\cdots,k-1,k+1,\cdots,n} tleaf(G,k)=detLin(G)(1,2,⋯,k−1,k+1,⋯,n1,2,⋯,k−1,k+1,⋯,n)
因此如果要统计一张图所有的叶向树形图,只要枚举所有的根 kkk 并对 tleaf(G,k)t^{leaf}(G,k)tleaf(G,k) 求和即可。
定理 5 (BEST 定理) 设 GGG 是有向欧拉图,那么 GGG 的不同欧拉回路总数 ec(G)ec(G)ec(G) 是
ec(G)=troot(G,k)×∏v∈V(deg(v)−1)!ec(G) = t^{\text{ root}}(G,k)\times \prod_{v\in V}(\deg (v) - 1)! ec(G)=t root(G,k)×v∈V∏(deg(v)−1)!
其中 troot(G,k)t^{ \text{root}}(G,k)troot(G,k) 表示以 root\text{root}root 为根的生成树的数量。
三、例题
1. Luogu P6178 【模板】Matrix-Tree 定理(外向树)
Problem
Solution
显然当边权为 111 时,本题即为生成树计数模板题。
本题中,计算的是所有生成树的边权之和,定义生成树的边权为所有边的边权的乘积,所以我们这里的度数矩阵中的值应该是 度数 ×\times× 边权 。所以对于每一条边 (x,y,z)(x,y,z)(x,y,z):
若图是有向图:
度数矩阵 DDD:
外向树中
D[y][y] += z, D[y][y] += z
内向树中
D[x][x] += z, D[x][x] += z
邻接矩阵 CCC:
- 无论是内外向树,显然均有:
C[x][y] += z, C[x][y] += z;
- 无论是内外向树,显然均有:
我们计算出图 GGG 的基尔霍夫矩阵 L(G)=D−CL(G) = D − CL(G)=D−C ,计算 L(G)L(G)L(G) 的代数余子式即可。
即删去指定的根所在的行和列,求剩下的矩阵行列式,我们删去根所在的第一行和第一列,然后高斯消元计算代数余子式的行列式即可。
Code
// Problem: P6178 【模板】Matrix-Tree 定理
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6178
// Memory Limit: 250 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3 + 7, mod = 1e9 + 7;int n, m, s, t, k, ans;
int a[N][N];int qpow(int a, int b)
{int res = 1;while(b) {if(b & 1) res = res * a % mod;a = a * a % mod;b >>= 1;}return res;
}int guass()
{int det = 1;for (int i = 2; i <= n; ++ i) {for (int j = i; j <= n; ++ j) {if(a[j][i]) {swap(a[i], a[j]);if(i != j) det = mod - det;break;}}int inv = qpow(a[i][i], mod - 2);for (int j = i + 1; j <= n; ++ j) {if(a[j][i]) {int tmp = a[j][i] * inv % mod;for (int k = i; k <= n; ++ k) {a[j][k] = (a[j][k] - a[i][k] * tmp % mod + mod) % mod;}}}}for (int i = 2; i <= n; ++ i) det = det * a[i][i] % mod;return det;
}signed main()
{scanf("%lld%lld%lld", &n, &m, &t);for (int i = 1; i <= m; ++ i) {int x, y, z;scanf("%lld%lld%lld", &x, &y, &z);if(t == 0) {a[x][x] = (a[x][x] + z) % mod;a[y][y] = (a[y][y] + z) % mod;//L(G) = D - Ca[x][y] = (a[x][y] - z + mod) % mod;a[y][x] = (a[y][x] - z + mod) % mod;}else {a[y][y] = (a[y][y] + z) % mod;//L(G) = D - Ca[x][y] = (a[x][y] - z + mod) % mod;}}cout << guass() << endl;return 0;
}
2. P4455 [CQOI2018]社交网络(内向树)
Problem
Solution
和 Luogu P6178 【模板】Matrix-Tree 定理 几乎一摸一样,外向树改为内向树即可。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3 + 7, mod = 1e4 + 7;int n, m, s, t, k, ans;
int a[N][N];int qpow(int a, int b)
{int res = 1;while(b) {if(b & 1) res = res * a % mod;a = a * a % mod;b >>= 1;}return res;
}int guass()
{int det = 1;for (int i = 2; i <= n; ++ i) {for (int j = i; j <= n; ++ j) {if(a[j][i]) { swap(a[i], a[j]);if(i != j) det = mod - det;break;}}int inv = qpow(a[i][i], mod - 2);for (int j = i + 1; j <= n; ++ j) {if(a[j][i]) {int tmp = a[j][i] * inv % mod;for (int k = i; k <= n; ++ k) {a[j][k] = (a[j][k] - a[i][k] * tmp % mod + mod) % mod;}}}}for (int i = 2; i <= n; ++ i) det = det * a[i][i] % mod;return det;
}signed main()
{scanf("%lld%lld", &n, &m);for (int i = 1; i <= m; ++ i) {int x, y;scanf("%lld%lld", &x, &y);a[x][x] ++ ;a[y][x] -- ;}cout << guass() << endl;return 0;
}
3. Luogu P4336 [SHOI2016]黑暗前的幻想乡(容斥,矩阵树定理)
Problem
n≤17,P=109+7n\le 17, P = 10^9+7n≤17,P=109+7
Solution
题目就是要求由 n−1n-1n−1 个公司每个公司一条边建成的生成树的方案数。
显然求生成树的方案数,我们直接用矩阵树定理计算即可。
现在考虑满足 每个公司都只负责一条边的方案数 如何计算。
显然有:n≤17+计数问题=容斥n\le 17 + 计数问题 = \text{容斥}n≤17+计数问题=容斥
我们可以先用矩阵树定理计算 n−1n-1n−1 个公司包含的所有边集的生成树的个数,显然这里算出来的生成树,不一定 n−1n-1n−1 个公司都参与了建设,我们用容斥原理减去不合法的即可。
显然就是枚举有多少个公司没有参与建设,然后利用容斥原理奇加偶减,答案减去容斥原理计算出的结果,变成奇减偶加,即:减去 111 个公司没有参与,由剩下的 n−2n-2n−2 个公司建成的生成树的个数,加上 222 个公司没有参与,由剩下的 n−3n-3n−3 个公司建成的生成树的个数,减去 333 个公司没有参与,由剩下的 n−4n-4n−4 个公司建成的生成树的个数 ⋯\cdots⋯
我们可以通过二进制枚举来枚举具体选择了那几个公司的边,处理出此时的基尔霍夫矩阵,然后直接利用矩阵树定理高斯消元 O(n3)O(n^3)O(n3) 计算代数余子式的行列式即可。
注意我们矩阵树定理计算的时候是可以处理重边的,所以如果一条边被多个公司覆盖,就把它当成重边即可,这样都当成重边算,最后减下来是没有重边的。
Time
O(2n×n3logP)O(2^{n}\times n^3\log P)O(2n×n3logP)
Code
// Problem: P4336 [SHOI2016]黑暗前的幻想乡
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4336
// Memory Limit: 250 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
#define int long long
using namespace std;const int N = 100 + 7, M = 1e5 + 7, mod = 1e9 + 7;int n, m[M], s, t, k, ans, a[N][N];
int siz[M];
int u[N][N], v[N][N];int qpow(int a, int b)
{int res = 1;while(b) {if(b & 1) res = res * a % mod;a = a * a % mod;b >>= 1;}return res;
}int guass2(int n)// 辗转相除 = TLE
{int det = 1;for (int i = 2; i <= n; ++ i) {for (int j = i + 1; j <= n; ++ j) {while(a[j][i]) {int t = a[i][i] / a[j][i];for (int k = i; k <= n; ++ k)a[i][k] = (a[i][k] - t * a[j][k] + mod) % mod;swap(a[i], a[j]);det = -det;} }det = (det * a[i][i]) % mod;if(det == 0) return 0;}return (det + mod) % mod;
}int guass(int n)// 求逆元 = AC
{int det = 1;for (int i = 2; i <= n; ++ i) {for (int j = i; j <= n; ++ j) {if(a[i][j]) {swap(a[i], a[j]);if(i != j) det = mod - det;break;}}int inv = qpow(a[i][i], mod - 2);for (int j = i + 1; j <= n; ++ j) {if(a[j][i]) {int tmp = a[j][i] * inv % mod;for (int k = i; k <= n; ++ k) {a[j][k] = (a[j][k] - a[i][k] * tmp % mod + mod) % mod;}}}}for (int i = 2; i <= n; ++ i)det = det * a[i][i] % mod;return det;
}signed main()
{scanf("%lld", &n); for (int i = 1; i <= n - 1; ++ i) {scanf("%lld", &m[i]);for (int j = 1; j <= m[i]; ++ j) {scanf("%lld%lld", &u[i][j], &v[i][j]);int x = u[i][j], y = v[i][j];a[x][x] ++ ;a[y][y] ++ ;a[x][y] = (a[x][y] + mod - 1) % mod;a[y][x] = (a[y][x] + mod - 1) % mod;}} ans = guass(n);for (int i = 1; i <= (1 << (n - 1)) - 1; ++ i) {for (int j = 1; j <= n; ++ j) for (int k = 1; k <= n; ++ k) a[j][k] = 0;int cnt = 0;int tmp = i, j;for (j = 1; tmp; tmp >>= 1, ++ j) {if((tmp & 1) == 0) continue;cnt ++ ; for (int k = 1; k <= m[j]; ++ k) {int x = u[j][k], y = v[j][k];a[x][x] ++ ;a[y][y] ++ ;a[x][y] = (a[x][y] + mod - 1) % mod;a[y][x] = (a[y][x] + mod - 1) % mod;}}if(cnt == n - 1) continue;ans = (ans - ((((n - 1) - cnt) & 1) ? 1 : -1) * guass(n) + mod) % mod;}printf("%lld\n", ans);return 0;
}
4. P3317 [SDOI2014]重建
5. BZOJ3659 : Which Dreamed It(BEST 定理)
Weblink
https://darkbzoj.tk/problem/3659
Problem
Solution
BEST 定理的模板题,注意题目中规定两种完成任务的方式算作不同当且仅当使用钥匙的顺序不同,即对每个欧拉回路,111 号房间可以沿着任意一条出边出发,从而答案还要乘以 111 号房间的出度。
O(n3logP)O(n^3\log P)O(n3logP)
%https://www.cnblogs.com/clrs97/p/5348718.html
Code
【学习笔记】矩阵树定理(Matrix-Tree)相关推荐
- 基尔霍夫矩阵矩阵树定理学习笔记
背景: 好多东西没学. 勇士被快船惊天大逆转!!! 快船NBNBNB. 紧接着下午打球水杯被搞烂了......... 正题: Part1Part1Part1行列式: 对于一个n∗nn*nn∗n的矩阵A ...
- [XSY]Tree Ext(矩阵树定理,拉格朗日插值,最小生成树,二分)
Tree Ext 这道题相当于把3道题合了起来. 要求修复的边中恰好有 k 条白边: 五颜六色的幻想乡(附拉格朗日插值法求多项式系数 ) + bzoj2654 tree(WQS二分 新科技get) 是 ...
- 学习小记-----行列式矩阵树定理Kirchhoff's theorem
为什么我的标题要加上Kirchhoff's theorem呢,是因为之前我查这个定理是用这个英文在谷歌上查的,然后,,,,我看了20多分钟的英文维基百科,然后爬墙去做别的题目了QAQ 行列式 前置知识 ...
- 行列式入门与矩阵树定理完整证明
文章目录 前置技能 行列式 定义 性质 拉普拉斯展开 线性性 可乘性 可加性 不重性 可倍加性 转置不变性 可交换性 行可交换性 列可交换性 优化行列式的计算 矩阵树定理 前置定义 一些引理 转置引理 ...
- 矩阵树定理2020HDU多校第6场j-Expectation[位运算+期望]
矩阵树定理 用于求解图上面生成树的个数,生成树的个数等于基尔霍夫矩阵的任何一个N-1阶主子式的行列式的绝对值 矩阵树模板 struct Matrix_Tree {ll a[N][N];Matrix_T ...
- Ext.Net学习笔记22:Ext.Net Tree 用法详解
上面的图片是一个简单的树,使用Ext.Net来创建这样的树结构非常简单,代码如下: <ext:TreePanel runat="server"><Root> ...
- Wannafly挑战赛23F-计数【原根,矩阵树定理,拉格朗日插值】
正题 题目链接:https://ac.nowcoder.com/acm/contest/161/F 题目大意 给出nnn个点的一张图,求它的所有生成树中权值和为kkk的倍数的个数.输出答案对ppp取模 ...
- 最小生成树、矩阵树定理、Prufer序列总结
Kruskal算法 按边权排序,从小到大合并不在同一集合两点即可 Prim算法 每次加入一个到当前已选点集最近的点 P2619 [国家集训队]Tree I 考虑二分,每次给白边加上一个mid,通过这种 ...
- 《数据结构、算法与应用 —— C++语言描述》学习笔记 — 竞赛树
<数据结构.算法与应用 -- C++语言描述>学习笔记 - 竞赛树 一.赢者树 二.二叉树的数组描述(补充) 1.声明 2.实现 三.赢者树 1.抽象数据类型 2.赢者树的表示 3.声明 ...
最新文章
- R语言caret包构建机器学习回归模型(regression model)、使用DALEX包进行模型解释分析、特征重要度、偏依赖分析等
- 函数和常用模块【day04】: 总结(十二)
- JPA_‘Basic‘ attribute type should not be a container怎么解决
- iOS Hacker obfuscator-llvm Xcode集成配置
- OJ系统原理与实现:Python自动化测试另一个Python程序功能是否正确
- python 函数
- 如何面对不讲信用的人
- Java设计模式应用——工厂模式
- Spring Boot Admin 2.3.1 发布,轻量的图形化监控工具
- HDUacm2095
- 类似金山打字的窗口打字游戏代码版--注释很多,不用讲解
- python 倒计时手机app打卡_摆脱拖延症,这些APP让你的时间更高效!
- 正点原子 fac_us=SystemCoreClock/8000000
- 大榕树BASIS QQ群
- 【疑难杂症】Oculus Quest2 手机配对时找不到5-digit-code
- 全国高校人工智能选修课该怎么上?附赠全套PPT
- Linux 往事:一个不会像 GNU 那样大而专业的 OS 是如何成为主流的?
- RF SeleniumLibrary 关键字分类解读
- Java基础汇总(十四)——LinkedList,Queue
- 一生万物的太极文化,与荣耀“1+8+N”智慧全场景的沈阳碰撞