因为不想改题或者动脑子了,所以去学习了矩阵树定理
首先附上一些行列式的知识:
求一个 (n imes n) 的矩阵的行列式等价于求对于所有长度为 (n) 的排列 (P={p_1,p_2,p_3dots p_n})
({det(K)=}sum_{P}^{ };{(}{(-1)}^{ au{(P)}} imes{K}_{1,p1} imes{K}_{2,p_2} imes{K}_ {3,p_3} imescdots imes{K}_ {n,p_n}{)})
或者理解成所有全排列直接对于当前行的对应位置乘积,然后加减考虑排列的逆序对的奇偶性
同时有几个性质:
((1)) 交换两行或者两列,行列式取反
((2)) 如果两行或者两列有倍数关系((1) 倍也算),那么行列式的值为 (0)
((3)) 任意一行或者一列乘上任意数加到任意另一行上,行列式不变
((4)) 上三角矩阵的行列式值是对角线乘积
(上面的行列式都是指行列式的值,下同)
定义基尔霍夫矩阵为度数矩阵减掉邻接矩阵
那么有如下几个定理
((1)) 去掉矩阵的任意一行和一列之后得到的余子式的行列式是当前图的生成树个数
不太会证,不过好背就行了
((2)) 求有向图的定跟的生成内向树的个数,度数矩阵只记录出度,然后消元的时候去掉根所在的行和列即可
((3)) 求生成外向树的个数,度数矩阵只记录入度,消元还是去掉根所在的行和列
要求矩阵树的边权乘积的话直接把度数改成相应的边权和,然后邻接矩阵加上权就行了
其实本质好像并不复杂,但是魔鬼在变式:
高斯消元不能取模
这里考虑辗转相除即可
while(a[j][i]){
int d=a[i][i]/a[j][i];
for(reg int k=i;k<=cnt-1;++k) a[i][k]=((a[i][k]-a[j][k]*d)%mod+mod)%mod;
swap(a[i],a[j]); ans=del(mod,ans);
}
别的慢慢打例题慢慢积累吧
(1.) 轮状病毒
建图一眼,然后交上去 (50),然后发现得高精
并不会高斯消元套着高精,就去打了表,扔到 (oeis) 里面抄了个递推式然后过掉了
具体递推式是:
证明在这个博客里面写得很详细:link
其实大概思路消掉第一行第一列之后维护剩下的行列式求法
然后发现再删掉余子式的第一行第一列就只有五个数
分别记贡献得到 (Mat_A,Mat_B) 然后硬推出的
这里有一个小收获:矩阵行列式等于其任意行或者列的元素乘上其代数余子式的和
(2.) 黑暗前的幻想乡
套上板子之后简单容斥即可
(3.) 重建
先手玩样例发现直接乘是不行的,还得乘上别的不存在的概率
直接把 (frac{p_e}{1-p_e}) 扔进去就行了,然后最后外面乘个权值即可
(4.) 最小生成树计数
先跑个 (Kruskal) ,如果环上的最大值是这条边的权值那么就是加进去
然后跑个矩阵树定理
显然这个做法只有在环上的边权都一样的时候成立
那么接着考虑 (kruskal) 的过程,每连上一个边就是说合并两个联通块
那么边的代替也只能是代替权值相同的边,那么就可以把所有的同权值的边砍掉之后跑 (kruskal)
然后联通块缩成一个点,再连上所有缩完的跑矩阵树就行了,最后把答案一乘就完事了
然后我是真的sb,跟着学怎么把数组开小
(4.) 生成树
好像是个板子就扔掉了