矩阵树定理
为什么生成树计数会与矩阵有关?
因为树没有环 ( ightarrow) 树中的任意一条边都是独一无二不可或缺的 ( ightarrow) "线性无关"。
我们用一个矩阵表示一张图,其中每一列有且仅有两个 (1) 和 (-1) 的位置表示两点存在一条边。
譬如 (n=4) 时,有一条边 ((2,4)) ,则可以表示为:
有多条边我们把它们像图书馆藏书一样靠紧即可。
比如 (n=4,m=3),边为 ((1,2),(1,3),(2,3)),则可以表示为:
这叫做关联矩阵。
那么求生成树的个数,等价于有多少 (n-1) 条边的组合不出现环且联通。
等价于关联矩阵有多少个 (n-1) 列的组合线性无关。
对于我们这个关联矩阵 (n-1) 列的组合,它的行列式只能是 (-1,0,1),其中 (0) 代表线性相关,这是非常好的性质。
容易发现,关联矩阵有一行是没有用的,因为我们知道了边的一端,另一端必然可以知道。
Binet-Cauchy定理
对于 (n imes m) 的矩阵 (A),(m imes n) 的矩阵 (B),有:
(U={1,2,cdots,m}),(A_S) 表示把 (S) 中的列全部抽出来形成的矩阵,(B_S) 表示把 (S) 中的行全部抽出来形成的矩阵。
至于证明,我不会
令关联矩阵为 (D),我们要计算线性无关的方案。
我们令 (A=D,B=D^{T}) 带入上述公式。
发现 (det(AB)=det(DD^T)) 正好就是线性无关的方案数,也就正好是生成树个数。
于是直接计算的复杂度是 (O(n^2m))。
不如直接观察 (DD^T) 代表什么东西。
(DD^T_{i,i}),计算方法就是把 (D) 的第 (i) 行向量点乘自己,也就表示点 (i) 的度数。
(DD^T_{i,j}),手玩得 ((i,j)) 之间边的数量的相反数。
于是这个矩阵(称之基尔霍夫矩阵)就可以直接 (O(edge)) 得到,再花费 (O(n^3)) 高斯消元计算行列式即可。
计算行列式就高斯消元只保留右上三角,左下全是 (0)。
(O(n^3)) 或者 (O(n^3log p))(无逆元辗转相减)。
各类生成树计数
有向生成树
一个记忆的方法:
- 指向根就不用在意根的出边,所以度数矩阵变成出度矩阵。
- 指向叶子就不用在意根的入边,所以度数矩阵变成入度矩阵。
权值为边权乘积 (prod)
直接看成重边即可。
权值为边权和 (sum)
需要计算每条边的出现次数乘上权值,暴力做是 (O(n^3m)) 的,非常不优秀。
我们用一些线性代数知识来优化复杂度。
内向生成树
首先定义一个方阵 (A) 的 余子式 (m_{i,j}) 为 把矩阵第 (i) 行 (j) 列去掉后矩阵的行列式。
代数余子式 (M_{i,j}=m_{i,j} imes (-1)^{i+j})。
于是所有的代数余子式也构成了一个矩阵 (M)。那么我们怎么求这个矩阵呢?
幸好我们有一个叫做拉普拉斯定理的东西:
定义 (A) 的伴随矩阵为 (M^T)((M) 的转置)。那么根据拉普拉斯公式显然有:
其中 (I) 是单位矩阵。(当然这个办法在 (A) 的行列式为 (0) 的时候无效)
这也告诉了我们一种 (O(n^3)) 求代数余子式矩阵 (M) 的方法:
回归正题,我们怎么用这个代数余子式矩阵快速求删除一条边后的矩阵行列式值呢?
由于这是个内向生成树,一条边 (u o v) 的删除等价于修改了基尔霍夫矩阵 (A) 的:
因为它们位于同一行,我们求得了原矩阵的行列式、代数余子式后,就可以用拉普拉斯公式 (O(1)) 修正成删除这条边的行列式。
二者作差便是边 (u o v) 在所有生成树中的出现次数了。
于是我们可以 (O(n^3+m)) 求的内向生成树的权值和。
外向生成树
外向生成树改变的是
改变的是同一列。据百度百科所言,拉普拉斯定理在取列而不是行的时候也是正确的。
所以跟内向生成树做法一致。
其实刚刚看《线性代数》看到了一个性质:行列式转置后值不变。这从侧面印证了外向生成树是可做的。
无向生成树
如果继续使用上面的办法,则删除一条边要改动 (4) 个值……不过百度百科还说,我们上文说的那个拉普拉斯定理只是弱化版本,真正的拉普拉斯定理是:
在 (n) 阶行列式 (D=|a_{i,j}|) 中,任意取定 (k) 行(列)((1 le k le n)),由这 (k) 行(列)的元素所构成的一切 (k) 阶子式与其代数余子式的乘积的和等于行列式 (D) 的值。
上面我们计算有向生成树的时候用到的是 (k=1),现在是 (k=2) 的。不过好在二阶行列式跟叉积一样,很好算,所以依然是可做的。
不过万一有人问:“你这做法太数学化了!有没有什么正常 oier 就能会的做法?”我会告诉你,有的。
另一种求生成树权值和的做法
此做法通用于有向无向。
将一条边权为 (w) 的边用 (wx+1) 的形式表示,套用原来的矩阵树定理就可以了。
即把矩阵的每一个元素换成一次多项式,运算在 (mod x^2) 意义下进行。
这样做的话,选出了超过 (2) 条边就会导致 (x^2) 的出现从而变成 (0)。
有一些非常神秘的细节:
(成功被 UOJ hack 数据搞死了)