[学习笔记]高斯消元、行列式、Matrix-Tree 矩阵树定理

一、前置芝士:高斯消元

https://blog.csdn.net/xyz32768/article/details/78574746

二、行列式的定义

一个 n 方阵(行数和列数相等的矩阵) A 的行列式为:

p 1 n ( 1 ) p i = 1 n A [ i , p i ]

记为 det ( A ) | A |

三、行列式的性质

(1) | A | = | A T |
(2) | A B | = | A | | B |
(3)矩阵的一行乘上 k 之后,行列式乘上 k
(4)如果矩阵的每一行内数的和都为 0 ,每一列内数的和都为 0 ,那么行列式为 0
(5)交换矩阵的两行或两列,行列式取反。
(6)矩阵的一行减去另一行的 k 倍,行列式不变。
性质(6)是求解行列式的关键。

四、求解行列式

暴力求解是 O ( n 2 × n ! ) 的。
利用高斯消元求解行列式。
根据性质(6),我们可以 for i 1 n ,用第 i 行把所有满足 j [ i + 1 , n ] 的位置 ( j , i ) 消成 0 ,也就是用第 i 行去消第 j 行。
最后原矩阵被消成一个上三角矩阵(对于每个 1 i n ,第 i 行前 i 1 个元素全部为 0 的矩阵)。
显然这时候矩阵的行列式为对角线上元素的乘积。
复杂度 O ( n 3 )
代码:

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;
}

如果模数不是质数,可以利用辗转相除的方法,复杂度多一个 log
具体地,如果当前要用第 i 行去消第 j 行,那么:
(1)第 i 行减掉第 j 行的 A [ i , i ] A [ j , i ] 倍。这时候, A [ i , i ] 被消成 A [ i , i ] mod A [ j , i ]
(2)交换第 i 行和第 j 行。注意,这个操作会导致行列式的取反,要用一个变量记录当前行列式是否被取反。
(3)如果 A [ j , i ] = 0 ,我们就成功地用第 i 行消去了第 j 行。否则继续(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;
}

五、应用:生成树计数之矩阵-树定理

n 阶无向图的一些定义:
邻接矩阵 A :有边 ( i , j ) A [ i , j ] = A [ j , i ] = 1 ,否则为 0 。特别地,如果图有重边,那么 A [ i , j ] A [ j , i ] 等于边 ( i , j ) 的条数。
度数矩阵 D D [ i , i ] 为点 i 的度数,即由 i 发出的边数。 D 其他位置的数为 0
基尔霍夫矩阵: C = D A
基尔霍夫矩阵每行内数的和和每列内的和都为 0 ,所以行列式为 0
余子式:一个矩阵 C 的余子式 M [ i , j ] 表示 C 去掉第 i 行第 j 列后得到矩阵的行列式。
矩阵-树定理,又称基尔霍夫定理。先说结论:
无向图的生成树个数等于其基尔霍夫矩阵 C 的任意余子式 M [ i , i ] (注意是 M [ i , i ] 不是 M [ i , j ] )的行列式。
如果图不连通,那么任意余子式 M [ i , i ] 0
证明:如果图不连通,那么每个连通块内的点构成的矩阵仍然是基尔霍夫矩阵。
而连通块不止一个,所以去掉第 i 行第 i 列之后,一定有一个连通块仍然是基尔霍夫矩阵,也就是行列式为 0

六、证明

首先证明:一棵树,其基尔霍夫矩阵的任意余子式 M [ i , i ] 等于 1
为了方便讨论,我们交换第 1 行和第 i 行,再交换第 1 列和第 i 列,我们要证明的只是 M [ 1 , 1 ]
1 为根。
2 n 的点重新排列,使同一个子树内的点编号形成一个区间。
我们会发现,把第 1 行第 1 列去掉,就相当于分离了 1 的所有子树。
我们可以把这个 n 1 阶方阵拆成 1 的度数个,每个小矩阵为一个子树的基尔霍夫矩阵,根为 r 则矩阵第 r 行第 r 列加一。
易得 M [ 1 , 1 ] 为这些小矩阵的行列式之积(因为一个 a b 的排列和一个 b + 1 c 的排列合并起来,相当于对应 A 位置的乘积相乘,逆序对数相加)。
我们还要引入一个定理:
将基尔霍夫矩阵的第 1 行第 1 列凭空加上 1 之后,行列式等于 1
我们考虑用归纳法同时证明两个结论。
考虑行列式的式子中,每个排列 p 对行列式的贡献。
证明第二个结论:
先讨论 p 1 = 1 时,易得这对行列式的贡献为( 1 的度数加 1 )再乘以所有子树的(根所在主对角线位置加 1 后得到的行列式),也就是 1 的度数加 1
再考虑如果 p 1 = i ,那么 i 一定是 1 的子节点。 i 的取法数为 1 的度数。
分析一下可以得到,这时候 p i 必须为 1 ,否则对行列式的贡献为 0
这样看上去对行列式的贡献为 1 ,但是由于 p 1 = i p i = 1 贡献了一个逆序对,对行列式的贡献为 1
所以, p 1 1 对行列式的贡献为 1 的度数)。
贡献相加一下就证明了结论二。
于是结论一也得证。

一起来证明 Matrix-Tree 定理。
先构建一个辅助矩阵 B , 一个 m × n 的矩阵, m 为边数。
对于第 i 条边 ( u , v ) B [ i , u ] = 1 , B [ i , v ] = 1
易得 B T B = C
根据 Binet-Cauthy 定理:

| A B | = | S | = n | A S B S |

A S 表示 A 只保留 向量集合 S 后得到的矩阵。
A S 表示 A 只保留 向量集合 S 后得到的矩阵。
这个定理简单地说,一个 n × m 的矩阵乘以一个 m × n 的矩阵后的行列式,等于枚举一个大小为 n 的集合 S S { 1 , 2 , . . . , m } ,求 A B 分别只保留 S 对应的列向量和行向量集合后的方阵乘积的行列式,并把所有 S 对应求出的行列式加起来。
所以:
A i , j 表示 A 去掉第 i 行第 j 列后的子矩阵,
| C i , i | = | S | = n 1 | B N/A , i , S T B i , N/A , S |

= S = n 1 | C i , i ( S ) |

C ( S ) 表示边集 S 构成的基尔霍夫矩阵。
讨论下这个式子。
相当于在 B 中选出 n 1 条边。
(1)选出的边构成环或不连通。
实际上, n 1 条边连通 n 个点的形态只能是树。
所以构成环和不连通其实是一样的。
由之前证出的定理得到贡献为 0
(2)选出的边构成树。
根据基尔霍夫矩阵的性质,得到贡献为 1
于是得证!!!!!
实现时注意自环。

扫描二维码关注公众号,回复: 2552562 查看本文章

七、题目 & 推广

[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)有向图生成树计数。
①内向树生成树计数。
A 为邻接矩阵, D 出度矩阵。
C = D A
r o o t 为根的内向生成树个数就是 C 的余子式 M [ r o o t , r o o t ] 的行列式。
②外向树生成树计数。
A 为邻接矩阵, D 入度矩阵。
C = D A
r o o t 为根的外向生成树个数就是 C 的余子式 M [ r o o t , r o o t ] 的行列式。
[BZOJ5297][Cqoi2018]社交网络:
https://www.lydsy.com/JudgeOnline/problem.php?id=5297
(2)边带权。
给定一个边带权的图,求其所有生成树的边权积之和。
A 为边权矩阵, D 为发出边权和矩阵。
即对于一条边 ( u , v ) ,边权为 w A [ u , v ] = A [ v , u ] = w
当然如果有重边, A [ u , v ] 为所有边 ( u , v ) 的权值和。
D [ u , u ] 等于由 u 发出的所有边的权值之和。
对于任意 u v D [ u , v ] = 0
C = D A
所有生成树的边权积之和等于 C 的任意余子式 M [ i , i ] 的行列式。
[BZOJ3534][Sdoi2014]重建:
https://www.lydsy.com/JudgeOnline/problem.php?id=3534

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/81413569