此类题是给定一个无向图,求所有生成树的个数,生成树计数要用到Matrix-Tree定理(Kirchhoff矩阵-树定理)
G的度数矩阵D[G]是一个n*n的矩阵,并且满足:当i≠j时,dij=0;当i=j时,dij等于vi的度数
G的邻接矩阵A[G]也是一个n*n的矩阵, 并且满足:如果vi、vj之间有边直接相连,则aij=1,否则为0
我们定义G的Kirchhoff矩阵(也称为拉普拉斯算子)C[G]为C[G]=D[G]-A[G],则Matrix-Tree定理可以描述为:G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n-1阶主子式的行列式的绝对值。所谓n-1阶主子式,就是对于r(1≤r≤n),将C[G]的第r行、第r列同时去掉后得到的新矩阵,用Cr[G]表示。
因为基尔霍夫矩阵i!=j处要么是0,要么是-1,这样处理起来就很方便
#include<map> #include<set> #include<ctime> #include<cmath> #include<queue> #include<stack> #include<vector> #include<cstdio> #include<iomanip> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define pi acos(-1) #define ll long long #define mod 1000000007 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 #define MIN(a,b) a<b ? a:b using namespace std; const double g=10.0,eps=1e-9; const int N=50+10,maxn=500000+10,inf=0x3f3f3f3f; ll D[N]; ll A[N][N]; ll solve(int n) { ll ans=1; for(int i=1;i<n;i++) { for(int j=i+1;j<n;j++) { while(A[j][i]){ ll t=A[i][i]/A[j][i]; for(int k=i;k<n;k++) A[i][k]-=(A[j][k]*t); for(int k=i;k<n;k++) swap(A[i][k],A[j][k]); ans=-ans; } } if(A[i][i]==0)return 0; ans*=A[i][i]; } if(ans<0)ans=-ans; return ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); int n,k,m; while(cin>>n>>m>>k){ for(int i=1;i<=n;i++) { D[i]=A[i][i]=0; for(int j=i+1;j<=n;j++) A[i][j]=A[j][i]=1; } while(m--){ int a,b; cin>>a>>b; A[a][b]=A[b][a]=0; } for(int i=1;i<=n;i++) for(int j=1+i;j<=n;j++) if(A[i][j]) D[i]++,D[j]++; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j)A[i][j]=D[i]; else A[i][j]=-A[i][j]; } } /* for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) cout<<A[i][j]<<" "; cout<<endl; }*/ ll res=solve(n); cout<<res<<endl; } return 0; }