一直都知道要用Matrix-Tree定理来解决生成树计数问题,但是拖到今天才来学。博主数学不好也只能跟着各位大佬博客学一下它的应用以及会做题,证明实在是不会。
推荐博客https://blog.csdn.net/u011815404/article/details/89091011(Matrix-Tree定理)
https://blog.csdn.net/u011815404/article/details/99679527(写得无敌好的生成树计数了)
那么比较常见的生成树计数问题就三种:①生成树计数②同边权边较少的MST计数③同边权边较少的MST计数,针对这3个问题有3种解决办法。
最简单的生成树计数就是Matrix-Tree定理的模板题啦,直接求出Kirchhoff 矩阵然后求它的n-1阶行列式绝对值。
Highways
代码抄袭参考的大佬的。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=10+5; int n,m; LL K[N][N]; LL det(int n){ //求矩阵K的n-1阶顺序主子式 LL ret=1; for(int i=1;i<=n-1;i++){ //枚举主对角线上第i个元素 for(int j=i+1;j<=n-1;j++){ //枚举剩下的行 while(K[j][i]){ //辗转相除 int t=K[i][i]/K[j][i]; for(int k=i;k<=n-1;k++) //转为倒三角 K[i][k]=K[i][k]-t*K[j][k]; swap(K[i],K[j]); //交换i、j两行 ret=-ret; //取负 } } ret=ret*K[i][i]; } return abs(ret); } int main() { int T; cin>>T; while (T--) { scanf("%d%d",&n,&m); memset(K,0,sizeof(K)); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); K[x][x]++; K[y][y]++; K[x][y]--; K[y][x]--; } printf("%lld\n",det(n)); } return 0; }
MST计数的话,如果同边权边较少的话可以考虑用暴力。它基于MST的两条性质:
- 每种权值的边的数量是固定的
- 不同的生成树中,某一种权值的边任意加入需要的数量后,形成的联通块状态是相同的
意思就是对于所有的MST对于边权例如为w的边的数量是一定的。这就启发我们暴力搜索每种边权选的使用的边,然后用乘法原理计算出方案数。这种办法因为是枚举所有的使用同边权使用情况所以时间复杂度是O(2^k*m)(k是同边权边数)。但是因为这个解法有局限性所以也不是特别有用。
去掉同边权边数限制的话就是更广泛的最小生成树计数了,要用到缩点+Matrix-Tree定理的解法。具体就是还是枚举每个边权i,然后把非边权i的边加入到图中然后缩点,对缩完点之后的图求Kirchhoff 矩阵利用Matrix-Tree定理求出生成树个数那么这个就是边权i的方案数了,然后全部边权乘法原理乘起来就是答案了。
给出一幅图求MST数量,缩点+Matrix-Tree定理解决。(这道题的数据也可以用暴力搜索解决)
代码还没写这里先贴个大佬的模板:
#include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<utility> #include<stack> #include<queue> #include<vector> #include<set> #include<map> #include<bitset> #define EPS 1e-9 #define PI acos(-1.0) #define INF 0x3f3f3f3f #define LL long long #define Pair pair<int,int> const int MOD = 31011; const int N = 1000+5; const int dx[] = {0,0,-1,1,-1,-1,1,1}; const int dy[] = {-1,1,0,0,-1,1,-1,1}; using namespace std; struct Edge { int x,y; int dis; bool operator < (const Edge &rhs) const { return dis<rhs.dis; } } edge[N],tr[N]; int n,m; int father[N]; int G[N][N]; int tot,bel[N],val[N]; int Find(int x) { if(father[x]!=x) return father[x]=Find(father[x]); return x; } int Gauss(int n) { int res=1; for(int i=1; i<=n; i++) { for(int k=i+1; k<=n; k++) { while(G[k][i]) { int d=G[i][i]/G[k][i]; for(int j=i; j<=n; j++) G[i][j]=(G[i][j]-1LL*d*G[k][j]%MOD+MOD)%MOD; swap(G[i],G[k]); res=-res; } } res=1LL*res*G[i][i]%MOD,res=(res+MOD)%MOD; } return res; } int Kruskal() { sort(edge+1,edge+m+1); for(int i=1; i<=n; i++) father[i]=i; int cnt=0; for(int i=1; i<=m; i++) { int fu=Find(edge[i].x); int fv=Find(edge[i].y); if(fu==fv) continue; father[fu]=fv,tr[++cnt]=edge[i]; if(edge[i].dis!=val[tot]) val[++tot]=edge[i].dis; } return cnt; } void addTreeEdge(int v) { for(int i=1; i<n&&tr[i].dis!=v; i++){ int x=tr[i].x; int y=tr[i].y; father[Find(x)]=Find(y); } for(int i=n-1; i&&tr[i].dis!=v; i--){ int x=tr[i].x; int y=tr[i].y; father[Find(x)]=Find(y); } } void rebuild(int v) { memset(G,0,sizeof(G)); for(int i=1; i<=m; i++){ if(edge[i].dis==v){ int x=bel[edge[i].x]; int y=bel[edge[i].y]; G[x][y]--; G[y][x]--; G[x][x]++; G[y][y]++; } } } int main() { scanf("%d%d",&n,&m); for(int i=1; i<=m; i++) scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].dis); int cnt=Kruskal(); if(cnt!=n-1) { printf("0\n"); } else{ int res=1; for(int i=1; i<=tot; i++) { for(int i=1; i<=n; i++) father[i]=i; addTreeEdge(val[i]); int blo=0; for(int i=1; i<=n; i++) if(Find(i)==i) bel[i]=++blo; for(int i=1; i<=n; i++) bel[i]=bel[Find(i)]; rebuild(val[i]); res=1LL*res*Gauss(blo-1)%MOD; } printf("%d\n",res); } return 0; }
原文地址:https://www.cnblogs.com/clno1/p/11420707.html