目前我也只做过一些矩阵树的模板题,对于这个神奇的算法了解并不深入,再加上这个算法的证明需要一定的线性代数的基础,所以这篇博客目前只能说是我对于这个定理自己的理解,重点并不在于证明。
问题描述
矩阵树问题直观地说,就是给出一个图,求在这个图中生成树的方案数
问题解法
首先将这个图转换成一个矩阵,这个矩阵每一个点(i,j)
用-1表示是否有一条边从i到j相连,
如果i=j,这个位置就表示i点的度数
根据Matrix-Tree定理就可以得到:
这个图的生成树之和,就是这个矩阵删去任意一行和一列,在剩下的矩阵中的行列式的绝对值。(其实个人认为如果不是dalao看到这里就足够了,反正考场上也不会要你证明)
证明过程相当复杂,再加上我身为中年选手并没有太多时间和精力写这类证明(我看别人的都要看半天,现在一个小时不到就要肝出来明显不现实),因此证明只有主干部分,很多细节并未涉及。
首先我们定义一下几个矩阵
设C矩阵表示我们刚才所说的矩阵
设B矩阵表示一个邻接矩阵,即如有边x表示
不难发现
C=BBT,BT表示B矩阵行列互换后的矩阵
设Br为B矩阵删去r行后的矩阵
设BFr表示将Br中不属于边集F的边删去后的矩阵
经过大约1页纸的证明我们可以得到:
如果F中存在环,那么dat(BFr)=0
再经过大约2页纸的证明还可以得到:(E表示边集)
因为如果F中有环,那么值一定为0,对答案没有贡献,所以这样可以看做将每一种树都统计了。
这就是简洁得不能再简洁的证明。(我自己都觉得很坑人,所以附一下我看的证明的链接 http://blog.csdn.net/u013010295/article/details/47451451)
接下来就是求行列式
通过行列式的性质:
1.交换任意两行,行列式变号。
2.把任意一行乘上一个常数加在另一行上,行列式结果不变
这样我们就可以用高斯消元,求出上三角形,这样一来整个矩阵对行列式有贡献的就只剩下主对角线上的值。将主对角线上的值乘起来就是我们要的行列式。
以下是模板题BZOJ4031的题解
题目描述:
你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。
你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。
分析:
首先对于每一个房间,向周围四个是房间的点连一条边,求最后的生成树的个数即可。(其实就是板)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
using namespace std;
void Read(int &x){x=0;char c;bool flag=0;while(c=getchar(),c!=EOF&&(c<'0'||c>'9')&&c!='-');if(c=='-')flag=1;elsex=c-'0';while(c=getchar(),c!=EOF&&c>='0'&&c<='9')x=x*10+1*(c-'0');if(flag==1)x=-x;
}#define MAXN 200
long long d[MAXN][MAXN],mod;
int w[8][4]={
{
0,-1},{-1,0},{
1,0},{
0,1}},num[MAXN][MAXN];
void swa(int x,int y,int n,int &flag){for(int i=1;i<=n;i++)swap(d[x][i],d[y][i]);flag^=1;
}
long long guess(int n,int m){int r=2,c=2,flag=0;for(;c<=n&&r<=m;r++,c++){int k=r;while(k<=m&&d[k][c]==0)k++;if(k>m) return 0;if(r!=k) swa(k,r,n,flag);for(int i=k+1;i<=m;i++){while(d[i][c]!=0){long long delta=d[i][c]/d[r][c];for(int j=c;j<=n;j++)d[i][j]=(d[i][j]-delta*d[r][j])%mod;if(d[i][c]==0)break;swa(i,r,n,flag);}}}if(r!=m+1)return 0;long long res=1;for(int i=2;i<=n;i++){res*=d[i][i];res%=mod;}if(flag==1)res=-res;return (res+mod)%mod;
}
int n,m,k;
char s[MAXN][MAXN];
int main(){Read(n),Read(m);mod=1000000000;int cnt=0;for(int i=1;i<=n;i++){SF("%s",s[i]);for(int j=0;j<m;j++)if(s[i][j]=='.')num[i][j]=++cnt;}for(int i=1;i<=n;i++)for(int j=0;j<m;j++)if(s[i][j]=='.'){for(int k=0;k<4;k++){int x=i+w[k][0];int y=j+w[k][1];if(x<1||y<0||x>n||y>=m||s[x][y]=='*')continue;d[num[i][j]][num[x][y]]=-1;d[num[i][j]][num[i][j]]++;}}/*for(int i=1;i<=cnt;i++){for(int j=1;j<=cnt;j++)PF("%d ",d[i][j]);PF("\n");}*/long long sum=guess(cnt,cnt);PF("%lld\n",sum);
}