一、前置芝士:高斯消元
https://blog.csdn.net/xyz32768/article/details/78574746
二、行列式的定义
一个
阶方阵(行数和列数相等的矩阵)
的行列式为:
记为 或 。
三、行列式的性质
(1)
(2)
(3)矩阵的一行乘上
之后,行列式乘上
。
(4)如果矩阵的每一行内数的和都为
,每一列内数的和都为
,那么行列式为
。
(5)交换矩阵的两行或两列,行列式取反。
(6)矩阵的一行减去另一行的
倍,行列式不变。
性质(6)是求解行列式的关键。
四、求解行列式
暴力求解是
的。
利用高斯消元求解行列式。
根据性质(6),我们可以 for
从
到
,用第
行把所有满足
的位置
消成
,也就是用第
行去消第
行。
最后原矩阵被消成一个上三角矩阵(对于每个
,第
行前
个元素全部为
的矩阵)。
显然这时候矩阵的行列式为对角线上元素的乘积。
复杂度
。
代码:
double det(int n) {
int i, j, k;
For (i, 1, n) {
int p = i;
For (j, i + 1, n)
if (fabs(mat[j][i]) > fabs(mat[p][i])) p = j;
if (p != i) For (j, i, n) swap(mat[i][j], mat[p][j]);
For (j, i + 1, n) {
double tmp = mat[j][i] / mat[i][i];
For (k, i, n) mat[j][k] -= mat[i][k] * tmp;
}
}
double ans = 1;
For (i, 1, n) ans *= mat[i][i];
return ans;
}
如果行列式特别大且题目要求取模,则可以利用逆元:
int det(int n) {
int i, j, k, res = 1;
For (i, 1, n) {
int qaq = qpow(a[i][i], ZZQ - 2);
For (j, i + 1, n) {
int tmp = 1ll * a[j][i] * qaq % ZZQ;
For (k, 1, n) a[j][k] = (a[j][k] - 1ll * a[i][k] *
tmp % ZZQ + ZZQ) % ZZQ;
}
}
For (i, 1, n) res = 1ll * res * a[i][i] % ZZQ;
return res;
}
如果模数不是质数,可以利用辗转相除的方法,复杂度多一个
。
具体地,如果当前要用第
行去消第
行,那么:
(1)第
行减掉第
行的
倍。这时候,
被消成
。
(2)交换第
行和第
行。注意,这个操作会导致行列式的取反,要用一个变量记录当前行列式是否被取反。
(3)如果
,我们就成功地用第
行消去了第
行。否则继续(1)(2)。
代码:
int solve() {
int i, j, k; bool tr = 0;
For (i, 1, n) For (j, i + 1, n) {
int a = mat[i][i], b = mat[j][i];
while (b) {
int tmp = a / b; a %= b; swap(a, b);
For (k, i, n) mat[i][k] = (mat[i][k] - 1ll * tmp * mat[j][k]
% ZZQ + ZZQ) % ZZQ;
For (k, i, n) swap(mat[i][k], mat[j][k]); tr ^= 1;
}
}
int ans = 1; For (i, 1, n) ans = 1ll * ans * mat[i][i] % ZZQ;
return tr ? (ZZQ - ans) % ZZQ : ans;
}
五、应用:生成树计数之矩阵-树定理
阶无向图的一些定义:
邻接矩阵
:有边
则
,否则为
。特别地,如果图有重边,那么
和
等于边
的条数。
度数矩阵
:
为点
的度数,即由
发出的边数。
其他位置的数为
。
基尔霍夫矩阵:
。
基尔霍夫矩阵每行内数的和和每列内的和都为
,所以行列式为
。
余子式:一个矩阵
的余子式
表示
去掉第
行第
列后得到矩阵的行列式。
矩阵-树定理,又称基尔霍夫定理。先说结论:
无向图的生成树个数等于其基尔霍夫矩阵
的任意余子式
(注意是
不是
)的行列式。
如果图不连通,那么任意余子式
为
。
证明:如果图不连通,那么每个连通块内的点构成的矩阵仍然是基尔霍夫矩阵。
而连通块不止一个,所以去掉第
行第
列之后,一定有一个连通块仍然是基尔霍夫矩阵,也就是行列式为
。
六、证明
一
首先证明:一棵树,其基尔霍夫矩阵的任意余子式
等于
。
为了方便讨论,我们交换第
行和第
行,再交换第
列和第
列,我们要证明的只是
。
定
为根。
将
到
的点重新排列,使同一个子树内的点编号形成一个区间。
我们会发现,把第
行第
列去掉,就相当于分离了
的所有子树。
我们可以把这个
阶方阵拆成
的度数个,每个小矩阵为一个子树的基尔霍夫矩阵,根为
则矩阵第
行第
列加一。
易得
为这些小矩阵的行列式之积(因为一个
到
的排列和一个
到
的排列合并起来,相当于对应
位置的乘积相乘,逆序对数相加)。
我们还要引入一个定理:
将基尔霍夫矩阵的第
行第
列凭空加上
之后,行列式等于
。
我们考虑用归纳法同时证明两个结论。
考虑行列式的式子中,每个排列
对行列式的贡献。
证明第二个结论:
先讨论
时,易得这对行列式的贡献为(
的度数加
)再乘以所有子树的(根所在主对角线位置加
后得到的行列式),也就是
的度数加
。
再考虑如果
,那么
一定是
的子节点。
的取法数为
的度数。
分析一下可以得到,这时候
必须为
,否则对行列式的贡献为
。
这样看上去对行列式的贡献为
,但是由于
和
贡献了一个逆序对,对行列式的贡献为
。
所以,
对行列式的贡献为
(
的度数)。
贡献相加一下就证明了结论二。
于是结论一也得证。
二
一起来证明 Matrix-Tree 定理。
先构建一个辅助矩阵
, 一个
的矩阵,
为边数。
对于第
条边
,
。
易得
。
根据 Binet-Cauthy 定理:
表示 只保留 列向量集合 后得到的矩阵。
表示 只保留 行向量集合 后得到的矩阵。
这个定理简单地说,一个 的矩阵乘以一个 的矩阵后的行列式,等于枚举一个大小为 的集合 且 ,求 和 分别只保留 对应的列向量和行向量集合后的方阵乘积的行列式,并把所有 对应求出的行列式加起来。
所以:
设 表示 去掉第 行第 列后的子矩阵,
表示边集 构成的基尔霍夫矩阵。
讨论下这个式子。
相当于在 中选出 条边。
(1)选出的边构成环或不连通。
实际上, 条边连通 个点的形态只能是树。
所以构成环和不连通其实是一样的。
由之前证出的定理得到贡献为 。
(2)选出的边构成树。
根据基尔霍夫矩阵的性质,得到贡献为 。
于是得证!!!!!
七、题目 & 推广
[BZOJ4031][HEOI2015]小Z的房间:
https://www.lydsy.com/JudgeOnline/problem.php?id=4031
[BZOJ4596][Shoi2016]黑暗前的幻想乡:
https://www.lydsy.com/JudgeOnline/problem.php?id=4596
Alice:为什么我们要证明矩阵-树定理呢?
Bob:在应用中,矩阵-树定理有一些拓展。
下面给出两种常见拓展。
(1)有向图生成树计数。
①内向树生成树计数。
为邻接矩阵,
为出度矩阵。
。
以
为根的内向生成树个数就是
的余子式
的行列式。
②外向树生成树计数。
为邻接矩阵,
为入度矩阵。
。
以
为根的外向生成树个数就是
的余子式
的行列式。
[BZOJ5297][Cqoi2018]社交网络:
https://www.lydsy.com/JudgeOnline/problem.php?id=5297
(2)边带权。
给定一个边带权的图,求其所有生成树的边权积之和。
为边权矩阵,
为发出边权和矩阵。
即对于一条边
,边权为
,
。
当然如果有重边,
为所有边
的权值和。
等于由
发出的所有边的权值之和。
对于任意
,
。
。
所有生成树的边权积之和等于
的任意余子式
的行列式。
[BZOJ3534][Sdoi2014]重建:
https://www.lydsy.com/JudgeOnline/problem.php?id=3534