( 图论专题 )【 矩阵树定理 】

( 图论专题 )【 矩阵树定理 】

首先,矩阵树定理用于求解一个图上的生成树个数。

实现方式是:A为邻接矩阵,D为度数矩阵,则基尔霍夫(Kirchhoff)矩阵即为:K=D−A。具体实现中,记 f 为Kirchhoff矩阵,则若存在 E(u,v) ,则a[u][u]++, a[v][v]++, a[u][v]−−, a[v][u]−− 。即 f [ i ] [ i ] 为 i 点的度数,f[ i ] [ j ] 为 i, j 之间边的条数的相反数。

  这样构成的矩阵的行列式的值,就为生成树的个数。而求解行列式的快速方法为使用高斯消元进行消元消处上三角矩阵,则有对角线上的值的乘积 = 行列式的值。一般而言求解生成树个数的题目数量会非常庞大,需要取模处理。取模处理中,不能出现小数,于是使用辗转相除法:(其中因为消的是行列式,所以与消方程有所不同。交换两行行列式的值变号,且消元只能将一行的数 * k 之后加到别的行上。)

// 计算行列式的值,运用了辗转相除法,mod不是素数也能取模( 不能用费马小定理 )
int Gauss()
{
    int ans = 1;
    for ( int i=1; i

 

例题:小 Z 的房间  LibreOJ - 2122

小 Z 突然有了一个大房子,房子里面有一些房间。事实上,小 Z 的房子可以看做是一个包含 n×m 个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。

小 Z 想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,小 Z 不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,小 Z 不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,小 Z 希望统计一共有多少种可行的方案。你能帮帮他吗?

第一行两个数分别表示 n 和 m。
接下来 n 行,每行 m 个字符,每个字符都会是 . 或者 *,其中 . 代表房间,* 代表柱子。

一行一个整数,表示合法的方案数 mod1e9。

样例输入

3 3
...
...
.*.

样例输出

15

注意:本题模数不是质数,不满足费马小定理,要用辗转相除的思想解高斯消元。

#include
#define int long long

using namespace std;

const int mod = 1e9;
const int maxn = 105;
int f[maxn][maxn];
char mp[maxn][maxn];
int nxt[4][2] = {0,1,0,-1,1,0,-1,0};
int n,m,tot;

// 计算行列式的值,运用了辗转相除法,mod不是素数也能取模( 不能用费马小定理 )
int Gauss()
{
    int ans = 1;
    for ( int i=1; i>n>>m;
    for ( int i=1; i<=n; i++ ) {
        for ( int j=1; j<=m; j++ ) {
            char str;cin>>str;
            if ( str=='.' ) mp[i][j]=++tot;
        }
    }
    for ( int i=1; i<=n; i++ ) {
        for ( int j=1; j<=m; j++ ) {
            for ( int k=0; k<4; k++ ) {
                int ii = i+nxt[k][0];
                int jj = j+nxt[k][1];
                if ( ii<=0||jj<=0||ii>n||jj>m ) continue ;
                add( mp[ii][jj], mp[i][j] );
            }
        }
    }
    cout << Gauss() << endl;

    return 0;
}

 

你可能感兴趣的:(算法树之图论)