zoukankan      html  css  js  c++  java
  • bzoj 4031

    矩阵树定理裸题

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

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

    那么首先提出几个概念:

    一.邻接矩阵:

    这个应该都知道...

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

    二.度数矩阵:

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

    三.基尔霍夫矩阵:

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

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

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

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

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

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

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

    第一:行列式怎么求值?

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

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

    所以我们用这种方法:

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

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

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

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

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

    egin{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
    ",Gauss());
        return 0;
    }
  • 相关阅读:
    弹性盒模型的实际应用
    大图滚动--这是精髓实例
    三级联动
    sql
    jsp2
    marquee
    人机五子棋(AI算法有瑕疵)
    Jsp1
    倒计时
    时间
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10826582.html
Copyright © 2011-2022 走看看