正题
不能再半途而废了。
让我们现在开始讲一下Matirx-Tree定理。
其实这个定理是用来解决关于“用图建树的方案树”之类的问题的。
先讲讲简单易懂的高斯消元
首先我们要了解几个定理及其证明。
1.我们定义一个n*n的矩阵A,它的行列式为
p是1到n的一个排列,laowang(p)指的是其中的逆序对个数。其实就是排列一个p使得后面行列不相交
2.那我们根据定义式,就可以知道,任意交换两行i,j,行列式就会乘上-1.
因为交换i,j两行的时候,其他的行都是不变的,我们把这单独两行提出来,就会发现逆序对的改变都会恰好。所以我们就知道,行列式会变为原来的相反数。
3.接着,我们还要推导一个东西,当有两行一样时,行列式为0.
比如说这两行是i,j,首先不讨论这两行,也就是说把这两行先提出来,我们就会发现,当时,我们可以知道,行列式的符号是不用改变的,因为不存在逆序对,也就是说行列式是,而当的时候,行列式的符号改变了,所以行列式是,又因为i,j两行完全相同,所以说,加/减的东西也相同。
4.下一个,当一行的数全部乘上了一个k,行列式也乘上k。
显然的,你直接把中间某一项乘k,再把k提出来即可。
5.当某一行是另一行的k倍,行列式为0.(k!=0)
比如说我们当前的行列式是det,把一行乘k,行列式为k*det,又因为现在两行都相同,所以k*det=0,又因为k!=0 ,所以det必为0.
6.当某一行加上另一行的k倍时,行列式不变。
因为行列式具有简单的"分配律",你给第i行都加上第j行的k倍(逐位加),就相当于,把整个矩阵的复制一遍,把其中第i行挖空,再填入第j行的k,然后把两个矩阵的行列式相加,这样很明显是可以成立的,因为相当于你把第i行拆开来罢了。
我们就可以用第6条定理完成求解行列式。(可能要带log?
怎么求呢。。。
用高斯消元转化为一个上三角矩阵即可.
for(int j=1;j<=n;j++){
for(int i=j+1;i<=n;i++)
while(d[i][j]){
long long temp=d[j][j]/d[i][j];
for(int k=j;k<=n;k++)
d[j][k]=(d[j][k]-d[i][k]*temp%Mod+Mod)%Mod,swap(d[j][k],d[i][k]);
ans*=-1;
}
ans*=d[j][j];
ans%=Mod;
}
我们试着辗转相除来解决这个问题,因为一行可以加上或减去另一行的k倍,所以我们联想到了辗转相除。我们不断地拿第j行第j列中的元素出来,让它与下面的元素比较,直到把他下面的全部变为0。那我们一个一个操作,对于d[j][j]和d[i][j](i>=j+1)
我们不断将两行辗转相除,并且交换,交换的代价就是-1;
最后我们把对角线乘起来就是答案。
Matrix-Tree定理
直接背吧,不太会证.
无向图
n个点m条边的一张没有自环的图,保证联通,求生成树个数。
我们构造一个n*n的矩阵A,对于A[i][j]来说,当 i==j 时,A[i][j]存的是i点的度数(无向图)。当i != j 时,存的是i,j之间有多少条边,有边为-边数,没边就是0.
然后我们把任意的第i行和第i列挖掉,把剩下的向左上对齐,求剩下矩阵的行列式即可。证明OI中不需要知道.
有向图
求外向树,把d[i][i]改成一个点的入度即可,外向树也就是叶向树,用leaf-in记忆
求内向树,把d[i][i]改成一个点的出度即可.
求以k为根的生成树个数时,就把第k行第k列挖掉即可.
欧拉回路
首先如果入度不等于出度,那么无解,否则d[i][i]中存的就是第i个点的入度数(或者出度数),由于是欧拉回路,所以从哪个点开始都是可以的,所以将第k行第k列挖掉求行列式再乘上(每个点的度数-1)即可,这样求出来的欧拉回路是循环同构的,所以题目要求计数的时候要注意.
但是,要注意,由于没有定义-1的阶乘,所以要保证原图联通,而且特判掉只有一个点,没有边的情况.
在至少有一条边的情况下,这个BEST定理还是十分有用的,练习代码可以使用这一题P5807 Which Dreamed It