Skip to content
0

行列式 / 矩阵树定理 / BEST 定理 笔记

一、行列式

对于一个 n×n 的(即 n 阶的)矩阵 A,定义它的行列式

det(A)=p((1)μ(p)+1i=1nAi,pi)

其中 p1nn 的一个排列,μ(p) 在这里表示 p 的逆序对对数。

定理 1

对于一个右上三角矩阵 Ai,第 i 行只有 Ai,[i,n]0 矩阵),det(A)=i=1nAi,i

证明:考虑构造使得乘积不为 0 的排列 p,第 n 行只能选择 An,n,即 pn=n,第 n1 行只能选择 n1,n,其中 n 已被选择,故 pn1=n1,可以得到 pi=i,此时 p 中没有逆序对。

定理 2

交换 Ai,Aj(i<j) 两行得到 Adet(A)=det(A)

证明:原操作相当于交换 pi,pj,考虑 p 中逆序对个数的变化。分类讨论可以证明变化总是奇数,故 1 的指数奇偶改变,取反。

定理 3

将某行的每一个数乘上一个数 kdet(A)=kdet(A)

证明:因为每一行只取一个数,故在每次乘积上都乘了个 k

定理 4

有两行相同的矩阵,det=0

证明:由定理 2,交换这两行,det 取反,但是交换前后矩阵一致,故 det=0

定理 5

|a+cb+def|=|abef|+|cdef|

证明:乘法分配律

定理 6

|abc+kad+kb|=|abcd|

证明:|abkakb|=|abab|=0

实现

根据定理 1 & 6,使用高斯消元 / 辗转相除法,即可求出行列式。

点击查看代码(高斯消元)
cpp
bool op = false;
for (int i = 1; i < n; i++) {
    for (int j = i; j < n; j++)
        if (a[j][i]) {
            if (j != i)
                op ^= 1;
            swap(a[i], a[j]);
            break;
        }
    if (!a[i][i]) {
        cout << 0;
        return 0;
    }
    ans = ans * a[i][i] % mod;
    for (int j = i + 1; j < n; j++) {
        int x = a[j][i] * inv(a[i][i]) % mod;
        for (int k = i; k < n; k++)
            a[j][k] = (a[j][k] - a[i][k] * x % mod + mod) % mod;
    }
}
cout << (op ? (mod - ans) % mod : ans);
点击查看代码(辗转相除法)
cpp
bool op = false;
for (int i = 1; i <= n; i++) {
    for (int j = i + 1; j <= n; j++) {
        while (a[i][i]) {
            int div = a[j][i] / a[i][i];
            for (int k = i; k <= n; k++)
                a[j][k] = (a[j][k] - div * a[i][k] % mod + mod) % mod;
            swap(a[i], a[j]);
            op ^= 1;
        }
        swap(a[i], a[j]);
        op ^= 1;
    }
    ans = ans * a[i][i] % mod;
}
cout << (op ? (mod - ans) % mod : ans);

二、余子式

注意本节符号与其它节的不同。

定义

假设 n 阶矩阵 A 的第 i 行第 j 列是 ai,j,将第 i 行第 j 列去掉,剩下的 n1 阶矩阵的行列式记作 Mi,j,称为“余子式”。

Ai,j=(1)i+jMi,j,称为“代数余子式”。

与主子式的关系

选定一个 i,则有

detA=j=1nai,jAi,j

选定一个 j,也有

detA=i=1nai,jAi,j

太菜了,不会证明啊。

三、矩阵树定理

1. 无向图无权情况

定义 Di,i=degiAi,j 表示是否有边 ij

定义 L=DA,将 L 的第 i 行与第 i 列(i 可取任意数)删去后,det(L) 即为图的生成树个数。

2. 有向图外向树

外向树,即从根指向四周。

定义 Di,i=indegiAi,j 表示是否有边 ij

删去 L 的第 i 行与第 i 列,则表示以 i 为根。

3. 有向图内向树

内向树,即从四周指向根。

定义 Di,i=outdegiAi,j 表示是否有边 ij

4. 重边情况

将“是否有边”改成“边的个数即可”。

5. 刷题总结

(1). P6178 【模板】Matrix-Tree 定理

生成树的权值是边权的乘积,其意义就是在一条边有边权种选择,本质就是重边。

(2). P4208 [JSOI2008] 最小生成树计数

将边权 w 从小到大考虑,当考虑到 w 时,小于 w 的边权一定已经将树连成了若干个连通块,按照 Kruskal,每个连通块包含的点集一定是确定的,可以考虑反证法,如果包含的点集不确定,那么这多种点集必然可以合并进一个更大的点集。

将连通块缩点后,对 w 的边做生成树计数即可,各个边权的答案独立,使用乘法原理解决。

(3). P2144 [FJOI2007] 轮状病毒

裸的矩阵树定理就能做,但是 n=100 还要跑高精度还是太慢了,有没有更快的方法?

分析一下矩阵 L(注意有 n+1 个点)。

|n11111131001113100101300100031110013|

那矩阵树定理做 n 阶矩阵肯定去掉神秘的第一行第一列,变成:

|3100113100013000003110013|

n 阶这玩意的行列式是 An,要求的最终答案就是 An

可是 (1,n)(n,1)1 还是破坏了矩阵的规律,考虑用余子式和主子式的关系。

选定第一行做行列式向代数余子式的转换。

删除 (1,1)

|3100113100013000003110013|

则得到

3×(1)1+1×|3100130000310013|

这回右边的矩阵很有规律了,记 n 阶这玩意的行列式是 Bn,则删除 (1,1) 得到 3Bn1

删除 (1,2)

|3100113100013000003110013|

则得到

1×(1)1+2×|1100030000311013|

考虑再对这个矩阵的第一列做转化,当选中 (1,1) 时为 1×(1)1+1×Bn2=Bn2;当选中 (n1,1) 时发现变成了一个左下三角矩阵,且对角线皆为 1,则这一部分是 1×(1)n1+1×(1)n2=(1)2n1=1。总的就是 1×(1)1+2×(Bn21)=Bn21

删除 (1,n),得

1×(1)1+n×|1310013000031001|

同上,选中第一列做转化,当选中 (n1,1) 时为 1×(1)n1+1×Bn2=(1)n+1×Bn2;当选中 (1,1) 时为右上三角矩阵,对角线皆为 11×(1)1+1×(1)n2=(1)n+1。总的即为 1×(1)1+n×[(1)n+1×Bn2+(1)n+1]=Bn21

终于,我们得到了 An=3Bn12Bn22

接着考虑求 B 的递推式,用“删除 (1,2)”时相同的方法推两次即可以得到 Bn=3Bn1Bn2

用复杂的线性代数知识就可以得到 An=3An1An2+2,我太菜了实在不会 QAQ。

四、BEST 定理

对于一个有向图的欧拉回路,将除了起点 s 以外每个点的最后走的一条出边,构成一棵树,可以得到一棵以 s 为根的内向树。

假如走出了环,回路最后一定要回到 s,而在点 x 最后走的一条边走出了一个环,再次回到点 x 后它将无边可走,不能回到 s

起点有 degs!deg 是出度)种排列选择的可能,其它每个点还有 (degi1)! 种可能,所以总欧拉回路数是

T×(degs)!×is(degi1)!

其中 T 是图的内向树个数。

若无规定起点,则要除去循环同构的部分。s 在欧拉回路序列中一共出现了 degs 次,将这 degs 个位置分别移动到序列开头,并将选中位置的前面丢到序列末尾,这样产生了 degs 个循环同构的欧拉回路,除去他们。

T×(degi1)!
最近更新