\(Matrix-tree\) 定理用来解决一类生成树计数问题,以下前置知识内容均是先基于无向无权图来介绍的。有关代数余子式的部分不是很明白,如果有错误还请指出……

部分内容参考至:\(Blog\_1\) & \(Blog\_2\)

 

\(Laplacian\) 矩阵

定义

在 \(n\) 个点的无向图中:

度数矩阵 \(D\) 即为一个 \(n*n\) 的矩阵,定义为 \(D[i][i]=i\) 号点的度数,\(D[i][j]=0\) \((i \ne j)\)。

邻接矩阵 \(A\) 即为一个 \(n*n\) 的矩阵,定义为 \(A[i][j]=i, j\) 之间的边数。

则其拉普拉斯矩阵其实就是图的度数矩阵减去邻接矩阵,即 \(L=D-A\)。

对此,可以直接的总结出 \(laplacian\) 矩阵中各元素的值为:

\[ L_{i,j}= \left \{ \begin{aligned}
degree(i)      & & i = j \\
-\ Edge\_number(i, j) & & i \ne j\\
\end{aligned} \right.\]

 

矩阵行列式性质及求法

行列式

在一个 \(n*n\) 的矩阵中,选出 \(n\) 个互相不同行不同列的数,其乘积的值冠以符号 \((-1)^t\),得到式子:\((-1)^t\ ·\ a_{1,p_1}\ ·\ a_{2,p_2}\ ·\ a_{3,p_3}\ ·\ ...\ ·\ a_{n,p_n}\);

其中,\(p\) 为自然数 \(1~n\) 的一个排列,\(t\) 为这个排列的逆序对数,可知形同上式的项共有 \(n!\) 个。所有这些项的代数和称为矩阵的 \(n\) 阶行列式,简记为 \(det(i,j)\),其中数 \(i,j\) 为行列式 \(D\) 的 \((i,\ j)\) 元。

也可以理解为矩阵 \(A\) 任意一行(列)的各元素与其对应的代数余子式乘积之和。

行列式的性质

· 互换矩阵的两行(列),行列式变号

· 矩阵有两行(列)成比例(比例系数 \(k\)),则行列式的值为 \(0\)

· 矩阵的某一行(列)中的所有元素都乘以同一个数 \(k\),新行列式的值等于原行列式的值乘上数 \(k\)

· 把矩阵的某一行(列)加上另一行(列)的 \(k\) 倍,则行列式的值不变

行列式的求法

按照定义来求行列式的复杂度实在是太高了,考虑一些更快的方法。

给出一个上三角/下三角矩阵,其行列式的值为对角线的乘积,因为只有 \(p={1,2,3...n}\) 时,乘积项中才没有 \(0\) 出现。

采用高斯消元的方法,把矩阵消为一个上三角矩阵后,然后求出对角线的积,便是该矩阵的行列式的值。

 

高斯消元

高斯消元

上面提到了高斯消元,我之前也学习过,不过忘记的差不多了……(之前的博客)

这里简单放一下,顺便做个复习补充。

模意义下的高斯消元

如果要求的矩阵不允许出现实数,且需要取模,则采用辗转相除的高斯消元法,至于这个辗转相除,复杂度高不了多少。

众所周知 高斯消元需要做除法,而模意义下是没有除法拿来做的。计算高斯消元的时候,如果模数是质数,那么除法逆元可以直接用费马小定理解决。否则我们对于矩阵中的两行,不断地把主元较大的那一行减去主元较小的那一行,最终一定有一行主元为 \(0\),也就完成了消元。

发现以上思想是更相减损术的思想,效率太低,就可以拿辗转相除代替。

需要注意的是,我们在做的时候会交换行,而由行列式的性质,我们需要统计一下交换的次数:如果次数为奇数,那么最后的答案还要乘上模意义下的 \(-1\)。

 

inline void Gaussian() {
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j) while( kir[j][i] ) {
int tmp = kir[i][i] / kir[j][i];
for(int k = i; k <= n; ++k) {
kir[i][k] = (kir[i][k] - tmp * kir[j][k] % mod + mod) % mod;
swap(kir[i][k], kir[j][k]);
}
ans = (mod - ans) % mod;
}
}

 

\(Matrix-tree\) 定理

定理内容

应用中,无向图的生成树个数就等于这副图的 \(laplacian\) 矩阵的任意一个代数余子式值,也可以理解为 \(laplacian\) 矩阵一个余子式的行列式的值。

余子式和代数余子式

\(M_{i,j}\) 表示矩阵的一个余子式,指去掉元素 \(a_{i,j}\) 所在的行和列后剩余的矩阵部分。

\(C_{i,j}\) 表示矩阵的一个代数余子式,定义为 \(C_{i,j}=(-1)^{i+j}M_{i,j}\)。

定理总结

· 存在性质:\(laplacian\) 矩阵的任意一个代数余子式值都相同。

· 求得 \(laplacian\) 矩阵后,取一个余子式用高斯消元得到新矩阵的上三角矩阵,对角线乘积的绝对值就是生成树个数。

定理推广

对于消去自环影响的有向图,也可以求以 \(i\) 为根的外向树/内向树个数,此时的 \(laplacian\) 矩阵定义为:

\[ L_{i,j}= \left \{ \begin{aligned}
in\_degree(i)\ or\ out\_degree(i)  & & i = j \\
-\ Edge\_number(i\ to\ j)     & & i \ne j\\
\end{aligned} \right.
\]

 

例题及拓展性质

\([SDOI2014]\) 重建

给一个图,每条边有出现概率,求这个图恰好为一棵树的概率。

考虑 \(laplacian\) 矩阵的意义:之所以能够进行生成树计数是对于其关联矩阵在计数 \(n−1\) 条边的集合时,当 \(n−1\) 条边中存在环就会产生线性组合而导致行列式为零,否则恰好对角线上均为关联矩阵中所赋的值,使得 \(det(B_{i,j})^2\) 就为 \(1\)(有关这一部分的理解请看文章首部给出的 \(Blog\_2\) 链接)。

因此可以令邻接矩阵中存下边权,以计数生成树中边权的乘积。

这道题告诉我们:邻接矩阵中的的权可以不是 \(1\),而是其他权值,比如概率。

了解了这个之后,就直接放一篇别人的题解 \(Blog\_3\) 在这里好了。

 

#include <cmath>
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; #define scnaf scanf const double eps = 1e-8;
const int maxn = 50 + 5;
int n; double p[maxn][maxn], tmp, ans; inline double Abs(double x) { return x < 0 ? -x : x; } inline void Gaussian() {
for(int maxx = 1, i = 1; i < n; maxx = ++i) {
for(int j = i + 1; j < n; ++j) if( Abs(p[j][i]) > Abs(p[maxx][i]) ) maxx = j;
if( maxx != i ) for(int j = 1; j < n; ++j) swap(p[i][j], p[maxx][j]);
for(int k = i + 1; k < n; ++k) {
double mul = p[k][i] / p[i][i];
for(int j = i; j < n; ++j) p[k][j] = p[k][j] - p[i][j] * mul;
}
if( Abs(p[i][i]) < eps ) { ans = 0.0; return ; }
}
for(int i = 1; i < n; ++i) ans = ans * p[i][i];
} int main(int argc, char const *argv[])
{
freopen("nanjolno.in", "r", stdin);
freopen("nanjolno.out", "w", stdout); scnaf("%d", &n), tmp = 1.0, ans = 1.0;
for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) {
scnaf("%lf", &p[i][j]);
if( i < j ) tmp = tmp * (1.0 - p[i][j]);
p[i][j] = p[i][j] < eps ? eps : min(p[i][j], 1.0 - eps);
p[i][j] = p[i][j] / (1.0 - p[i][j]);
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j) if( i != j ) p[i][i] = p[i][i] - p[i][j];
Gaussian(), printf("%.6lf\n", Abs(ans * tmp)); fclose(stdin), fclose(stdout);
return 0;
}

 

其他题目

\([SHOI2016]\) 黑暗前的幻想乡

\([HEOI2015]\) 小Z的房间

\([FJOI2007]\) 轮状病毒

\([JSOI2008]\) 最小生成树计数

 

                         林下带残梦,叶飞时忽惊。

Matrix-tree 定理的一些整理的更多相关文章

  1. BZOJ.4031.[HEOI2015]小Z的房间(Matrix Tree定理 辗转相除)

    题目链接 辗转相除解行列式的具体实现? 行列式的基本性质. //864kb 64ms //裸的Matrix Tree定理.练习一下用辗转相除解行列式.(因为模数不是质数,所以不能直接乘逆元来高斯消元. ...

  2. [bzoj1016][JSOI2008]最小生成树计数 (Kruskal + Matrix Tree 定理)

    Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...

  3. @总结 - 7@ 生成树计数 —— matrix - tree 定理(矩阵树定理)与 prüfer 序列

    目录 @0 - 参考资料@ @0.5 - 你所需要了解的线性代数知识@ @1 - 矩阵树定理主体@ @证明 part - 1@ @证明 part - 2@ @证明 part - 3@ @证明 part ...

  4. 【证明与推广与背诵】Matrix Tree定理和一些推广

    [背诵手记]Matrix Tree定理和一些推广 结论 对于一个无向图\(G=(V,E)\),暂时钦定他是简单图,定义以下矩阵: (入)度数矩阵\(D\),其中\(D_{ii}=deg_i\).其他= ...

  5. 数学-Matrix Tree定理证明

    老久没更了,冬令营也延期了(延期后岂不是志愿者得上学了?) 最近把之前欠了好久的债,诸如FFT和Matrix-Tree等的搞清楚了(啊我承认之前只会用,没有理解证明--),FFT老多人写,而Matri ...

  6. SPOJ.104.Highways([模板]Matrix Tree定理 生成树计数)

    题目链接 \(Description\) 一个国家有1~n座城市,其中一些城市之间可以修建高速公路(无自环和重边). 求有多少种方案,选择修建一些高速公路,组成一个交通网络,使得任意两座城市之间恰好只 ...

  7. HDU 4305 Lightning Matrix Tree定理

    题目链接:https://vjudge.net/problem/HDU-4305 解法:首先是根据两点的距离不大于R,而且中间没有点建立一个图.之后就是求生成树计数了. Matrix-Tree定理(K ...

  8. BZOJ.4894.天赋(Matrix Tree定理 辗转相除)

    题目链接 有向图生成树个数.矩阵树定理,复习下. 和无向图不同的是,度数矩阵改为入度矩阵/出度矩阵,分别对应外向树/内向树. 删掉第i行第i列表示以i为根节点的生成树个数,所以必须删掉第1行第1列. ...

  9. BZOJ.1016.[JSOI2008]最小生成树计数(Matrix Tree定理 Kruskal)

    题目链接 最小生成树有两个性质: 1.在不同的MST中某种权值的边出现的次数是一定的. 2.在不同的MST中,连接完某种权值的边后,形成的连通块的状态是一样的. \(Solution1\) 由这两个性 ...

  10. [模板]Matrix Tree定理

    结论:一个图的生成树个数等于它的度数矩阵减邻接矩阵得到的矩阵(基尔霍夫矩阵)的任意一个n-1阶主子式的行列式的绝对值 证明:不会 求法:高斯消元 例题:[HEOI2013]小Z的房间 #include ...

随机推荐

  1. SAP HUM 拆包之后的HU号码依旧存在

    比如HU 194811210666,已经被执行了Unpack操作. HUMO看这个HU号码, 执行, 可以发现,这个HU是空的,里面什么都没有. 双击HU号码,进入HU显示界面, 系统状态竟然是PHE ...

  2. python的学习笔记01_4基础数据类型列表 元组 字典 集合 其他其他(for,enumerate,range)

    列表 定义:[]内以逗号分隔,按照索引,存放各种数据类型,每个位置代表一个元素 特性: 1.可存放多个值 2.可修改指定索引位置对应的值,可变 3.按照从左到右的顺序定义列表元素,下标从0开始顺序访问 ...

  3. 学习RenderScript,以此来修改LiveWallpaper

    先留个坑,花5天的时间来填满.

  4. Android编程示例:创建机场计划模拟器应用程序

    在本文中,我们将演示如何使用Android Studio和Java编程语言创建一个示例Android应用程序,从“临时”实现高级响应用户界面的功能.本文中讨论的应用程序将实现机场航班时刻表模拟的功能. ...

  5. Python 经典面试题汇总之框架篇

    前端和框架 1.谈谈你对http协议的认识 浏览器本质,socket客户端遵循Http协议 HTTP协议本质:通过\r\n分割的规范,请求响应之后断开链接 ==> 短连接.无状态 具体: Htt ...

  6. 团队项目(六)- Alpha阶段项目复审(江山代有才人秃)

    排名仅代表个人观点: 小组名字 优点 缺点&Bug报告 排名 中午吃啥队 从测试链接来看,作为一个订餐的APP,有着跟现在的订餐APP相似的功能,很完整,是一个踏踏实实做出来的项目 向购物车中 ...

  7. 简单易懂的单元测试框架-gtest(一)

    简介     gtest是google开源的一个单元测试框架,以其简单易学的特点被广泛使用.该框架以第三方库的方式插入被测代码中.同其他单元测试框架相似,gtest也通过制作测试样例来进行代码测试.同 ...

  8. c#面试题汇总(1)

    c#面试题汇总 From: https://www.cnblogs.com/suzhiyong1988/p/5069385.html   下面的参考解答只是帮助大家理解,不用背,面试题.笔试题千变万化 ...

  9. js斐波拉切

    如下: //1 1 2 3 5 8 13 21...//斐波拉切 function fei(n){ if(n==1 || n==2){ return 1 }else{ return fei(n-1)+ ...

  10. 前端架构师 摘自《前端架构设计》-micah godbolt

    作为前端架构师,你经常需要制定,或至少能够掌握以上所列的每一项内容.流程中的任何一个环节出现问题,都会迅速演变为开发人员的痛苦,或者导致网站无法持续满足用户要求,甚至崩溃.  前端架构师的用户是开发人 ...