P4111 [HEOI2015]小Z的房间

传送门

转换题意后就是问你生成树的方案数

就是裸的矩阵树定理

不会证明,只懂结论:

对于一个无向图 G

定义G的度数矩阵 D[G] 是一个 n*n 的矩阵,并且满足:当  i ≠ j  时,d[i][j] = 0,当 i = j 时,d[i][j]等于 vi 的度数

定义G的邻接矩阵 A[G] 是一个 n*n 的矩阵,并且满足:如果 vi,vj 之间有边直接相连,则 a[i][j] = 1 否则为 0

定义G的 Kirchhoff 矩阵 C[G] 为 C[G] = D[G] - A[G],则 Matrix-Tree定理可描述为:

  G的所有不同的生成树的个数等于其 Kirchhoff 矩阵 C[G] 的任何一个 n-1 阶主子式的行列式的绝对值。

   n-1 阶主子式就是将 C[G] 的第 r 行,第 r 列同时去掉后得到的的新矩阵(1 ≤ r ≤ n),用Cr[G]表示

行列式是什么不需要知道,只要知道怎么求

有下面的结论:

1.交换两行/列位置,行列式的值取相反数

2.用一行的倍数减去另一行,行列式的值不变

3.一个上三角行列式的值等于对角线的乘积

然后只要考虑如何把Cr[G]搞成上三角形式的矩阵

考虑高斯消元时的做法,把Cr[G]也用同样的方法搞成上三角形式

因为消元时有除法,而剩余系下没有除法,所以要用辗转相除法:

  设当前位置值为 a ,然后我们要消掉另一行的 b

  那么把 b 整行减去 (a 整行 * [b/a] )    [b/a]表示 b除a下取整

  那么 (a,b) --> (a,b%a)

  然后交换两行继续操作直到有一个为 0

  注意每次交换后ans都要取相反数

计算矩阵行列式的代码如下:

//ans初始为1,A是矩阵
for(int j=1;j<=cnt;j++)
    {
        for(int i=j+1;i<=cnt;i++)
            while(A[i][j])
            {
                long long t=A[j][j]/A[i][j];
                for(int k=j;k<=cnt;k++)
                    A[j][k]=(A[j][k]- t*A[i][k]%mo +mo)%mo,
                    swap(A[i][k],A[j][k]);
                ans*=-1;
            }
        ans=ans*A[j][j]%mo;
    }

 

 

附上完整代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int mo=1e9;
int n,m;
char mp[17][17];
int A[107][107],a[17][17],cnt;//矩阵A就是Cr[G]

long long ans=1;
void slove()
{
    cnt--;//把矩阵最外面一层去掉
    for(int j=1;j<=cnt;j++)
    {
        for(int i=j+1;i<=cnt;i++)
            while(A[i][j])//辗转相除
            {
                long long t=A[j][j]/A[i][j];
                for(int k=j;k<=cnt;k++)
                    A[j][k]=(A[j][k]- t*A[i][k]%mo +mo)%mo,
                    swap(A[i][k],A[j][k]);
                ans*=-1;
            }
        ans=ans*A[j][j]%mo;
    }
    ans=(ans+mo)%mo;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
    for(int i=0;i<=n+1;i++) mp[i][0]=mp[i][m+1]='*';
    for(int i=0;i<=m+1;i++) mp[0][i]=mp[n+1][i]='*';//边界都是不通的

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(mp[i][j]=='.')
            {
                a[i][j]=++cnt;//记录原图的位置对应的矩阵A中的位置
                if(mp[i-1][j]=='.') A[ a[i][j] ][ a[i-1][j] ]=A[ a[i-1][j] ][ a[i][j] ]=1;
                if(mp[i][j-1]=='.') A[ a[i][j] ][ a[i][j-1] ]=A[ a[i][j-1] ][ a[i][j] ]=1;
            }//处理矩阵A
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=cnt;j++)
            if(A[i][j]&&i!=j) A[i][i]++;//处理矩阵A
    slove();
    printf("%lld",ans);
    return 0;
}

 

posted @ 2018-10-11 10:55  LLTYYC  阅读(157)  评论(0编辑  收藏  举报