bzoj 4031

矩阵树定理裸题

首先介绍一下矩阵树定理:

矩阵树定理是用来解决一个图上生成树个数的问题,可以分为无向图矩阵树定理和有向图矩阵树定理,而本题应用的是无向图矩阵树定理

那么首先提出几个概念:

一.邻接矩阵:

这个应该都知道...

就是这样一个矩阵:第$u$行第$v$列的元素为1的条件是当且仅当$(u,v)\in E$,其中$E$为边集,我们记这个矩阵为$A$

二.度数矩阵:

一个图的度数矩阵仅有第$i$行第$i$列有值,且这个值是点$i$的入边数量(如果是无向图,那么就是这个点周围所有边的数量),我们记这个矩阵为$D$

三.基尔霍夫矩阵:

一个特殊的矩阵,我们记这个矩阵为$C$,则有$C=D-A$(是的!这里出现了矩阵减法!)

那么矩阵树定理的内容如下:

一个图的生成树个数,等于它的基尔霍夫矩阵去掉某一行一列(行列编号相同)后对应行列式的值的绝对值

(这里有一个引申定理:Cayley定理:一个n个节点的生成树个数=$n^{n-2}$,但是与本题无关)

(我并不想证明这两个东西)

那么有了矩阵树定理,本题就变得异常简单了:相邻两点之间建边(注意不要建重,同时去掉不能用的点),然后矩阵树定理裸上即可

但是还需要讨论几个问题:

第一:行列式怎么求值?

(讲道理,这是线性代数中的内容...但是还是应该提一下)

首先,行列式可以按某一行或某一列进行展开然后求值,但这样求的时间复杂度过高,无法承受

所以我们用这种方法:

我们假设原来的行列式长这样:

\begin{vmatrix} a_{11} & a_{12} & ... &a_{1n} \\ a_{21} & a_{22} & ... &a_{2n} \\ ... & ... & ... & ...\\ a_{n1} & a_{n2} &... &a_{nn} \end{vmatrix}

(由于是由邻接矩阵和度数矩阵生成的,所以肯定是个方阵)

那么,根据行列式运算规则,我们可以采取类似高斯消元的方法整理这个行列式,将这个行列式整理成一个“上三角”形式(要注意,交换行列式两行时行列式值要取相反数)

那么,我们整理一下,就可以得到新的行列式长这样:

\begin{vmatrix} a_{11} ^{'}& a_{12}^{'} & ... &a_{1n}^{'} \\0 & a_{22}^{'} & ... &a_{2n}^{'} \\ ... & ... & ... & ...\\0 & 0 &... &a_{nn}^{'} \end{vmatrix}

那么,这个行列式的值就是$\prod_{i=1}^{n}a_{ii}^{'}$(注意符号!)

这就是本题的生成树个数

 第二:怎么取模?

可能你会觉得没有什么,正常取模就可以

但是请注意,本题的模数并不是质数!!!

因此,在进行类似高斯消元的方法时,我们是无法直接求逆元的

那么我们采用类似辗转相除的方法,不断交换两行即可

最后贴代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
const ll mode=1000000000;
char ch[15][15];
ll a[90][90];
ll d[90][90];
ll c[90][90];
int idx[15][15];
int n,m,tot;
bool check(int x,int y)
{
    return (x>=1)&&(x<=n)&&(y>=1)&&(y<=m)&&(ch[x][y]=='.');
}
ll Gauss()
{
    ll ans=1,f=1;
    for(int i=1;i<=tot;i++)
    {
        for(int j=i+1;j<=tot;j++)
        {
            ll A=c[i][i],B=c[j][i];
            while(B)
            {
                ll t=A/B;
                A%=B,swap(A,B);
                for(int k=i;k<=tot;k++)c[i][k]=(c[i][k]-t*c[j][k]%mode+mode)%mode;
                for(int k=i;k<=tot;k++)swap(c[i][k],c[j][k]);
                f=-f;
            }
        }
        if(!c[i][i])return 0;
        ans=ans*c[i][i]%mode;
    }
    return (mode+f*ans)%mode;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch[i]+1);
        for(int j=1;j<=m;j++)if(ch[i][j]=='.')idx[i][j]=++tot;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(check(i,j))
            {
                if(check(i+1,j))d[idx[i][j]][idx[i][j]]++,d[idx[i+1][j]][idx[i+1][j]]++,a[idx[i][j]][idx[i+1][j]]=a[idx[i+1][j]][idx[i][j]]=1;
                if(check(i,j+1))d[idx[i][j]][idx[i][j]]++,d[idx[i][j+1]][idx[i][j+1]]++,a[idx[i][j]][idx[i][j+1]]=a[idx[i][j+1]][idx[i][j]]=1;
            }
        }
    }
    tot--;
    for(int i=1;i<=tot;i++)for(int j=1;j<=tot;j++)c[i][j]=((d[i][j]-a[i][j])+mode)%mode;
    printf("%lld\n",Gauss());
    return 0;
}

 

posted @ 2019-05-15 20:58  lleozhang  Views(224)  Comments(0Edit  收藏  举报
levels of contents