bzoj 4031
矩阵树定理裸题
首先介绍一下矩阵树定理:
矩阵树定理是用来解决一个图上生成树个数的问题,可以分为无向图矩阵树定理和有向图矩阵树定理,而本题应用的是无向图矩阵树定理
那么首先提出几个概念:
一.邻接矩阵:
这个应该都知道...
就是这样一个矩阵:第$u$行第$v$列的元素为1的条件是当且仅当$(u,v)\in E$,其中$E$为边集,我们记这个矩阵为$A$
二.度数矩阵:
一个图的度数矩阵仅有第$i$行第$i$列有值,且这个值是点$i$的入边数量(如果是无向图,那么就是这个点周围所有边的数量),我们记这个矩阵为$D$
三.基尔霍夫矩阵:
一个特殊的矩阵,我们记这个矩阵为$C$,则有$C=D-A$(是的!这里出现了矩阵减法!)
那么矩阵树定理的内容如下:
一个图的生成树个数,等于它的基尔霍夫矩阵去掉某一行一列(行列编号相同)后对应行列式的值的绝对值
(这里有一个引申定理:Cayley定理:一个n个节点的生成树个数=$n^{n-2}$,但是与本题无关)
(我并不想证明这两个东西)
那么有了矩阵树定理,本题就变得异常简单了:相邻两点之间建边(注意不要建重,同时去掉不能用的点),然后矩阵树定理裸上即可
但是还需要讨论几个问题:
第一:行列式怎么求值?
(讲道理,这是线性代数中的内容...但是还是应该提一下)
首先,行列式可以按某一行或某一列进行展开然后求值,但这样求的时间复杂度过高,无法承受
所以我们用这种方法:
我们假设原来的行列式长这样:
(由于是由邻接矩阵和度数矩阵生成的,所以肯定是个方阵)
那么,根据行列式运算规则,我们可以采取类似高斯消元的方法整理这个行列式,将这个行列式整理成一个“上三角”形式(要注意,交换行列式两行时行列式值要取相反数)
那么,我们整理一下,就可以得到新的行列式长这样:
那么,这个行列式的值就是$\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; }