【知识点】矩阵树定理
简介:
用于无向图生成树计数。
结论:
令$D$为图$G$的度数矩阵($D_{i,i}$的值为$i$的度数),$C$为邻接矩阵,$A$(基尔霍夫矩阵)为$D$-$C$。
将$A$去掉任意一行和一列得到$A'$,则$A'$的行列式即为图$G$的生成树个数。
做法:
一个矩阵的行列式等于将矩阵高斯消元成对角线矩阵后对角线值的乘积,于是高斯消元即可。
注意计算行列式时如果你交换了两行那么答案需要取相反数。
代码([HEOI2015]小 Z 的房间):
注意这道题模数不是质数,需要辗转相除式消元。
#include<bits/stdc++.h> #define maxn 105 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define mod 1000000000 #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; ll id[maxn][maxn],A[maxn][maxn],tot; char mp[maxn][maxn]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void add(ll u,ll v){A[u][u]++,A[v][v]++,A[u][v]--,A[v][u]--;} inline ll Gauss(ll n){ ll ans=1; for(ll i=1;i<=n;i++){ for(ll k=i+1;k<=n;k++) while(A[k][i]){ ll t=A[i][i]/A[k][i]; for(ll j=1;j<=n;j++) A[i][j]=(A[i][j]-t*A[k][j]%mod+mod)%mod; swap(A[k],A[i]),ans=-ans; } ans=ans*A[i][i]%mod,ans=(ans+mod)%mod; } return ans; } int main(){ ll n=read(),m=read(); for(ll i=1;i<=n;i++) scanf("%s",mp[i]+1); for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++){ if(mp[i][j]=='.'){ id[i][j]=++tot; if(id[i-1][j]) add(id[i-1][j],id[i][j]); if(id[i][j-1]) add(id[i][j-1],id[i][j]); } } printf("%lld\n",Gauss(tot-1)); return 0; }