当前位置: 代码迷 >> 综合 >> 【Matrix-Tree定理】初探矩阵树小结
  详细解决方案

【Matrix-Tree定理】初探矩阵树小结

热度:50   发布时间:2023-09-27 08:50:43.0

目前我也只做过一些矩阵树的模板题,对于这个神奇的算法了解并不深入,再加上这个算法的证明需要一定的线性代数的基础,所以这篇博客目前只能说是我对于这个定理自己的理解,重点并不在于证明。

问题描述

矩阵树问题直观地说,就是给出一个图,求在这个图中生成树的方案数

问题解法

首先将这个图转换成一个矩阵,这个矩阵每一个点(i,j)
用-1表示是否有一条边从ij相连,
如果i=j,这个位置就表示i点的度数
根据Matrix-Tree定理就可以得到:
这个图的生成树之和,就是这个矩阵删去任意一行和一列,在剩下的矩阵中的行列式的绝对值。(其实个人认为如果不是dalao看到这里就足够了,反正考场上也不会要你证明)


证明过程相当复杂,再加上我身为中年选手并没有太多时间和精力写这类证明(我看别人的都要看半天,现在一个小时不到就要肝出来明显不现实),因此证明只有主干部分,很多细节并未涉及。
首先我们定义一下几个矩阵
设C矩阵表示我们刚才所说的矩阵
设B矩阵表示一个邻接矩阵,即如有边x表示 (u,v) ,那么Bux=?1,Bvx=1Bvx=?1
不难发现
C=BBT,BTB
BrBr
BFrBrF
经过大约1页纸的证明我们可以得到:
如果F中存在环,那么dat(BFr)=0
再经过大约2页纸的证明还可以得到:(E表示边集)

dat(Cr)=det(BrBTr)=FE,|F|=n?1det(BFrBFTr)=FE,|F|=n?1det2(BFr)

因为如果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);
}
  相关解决方案