「最小生成树计数」题解
题面 \(\text{Description}\)
- 给出一个由 \(n\) 个点 \(m\) 条边构成的简单无向加权图。
- 求最小生成树 MST 的个数。
- \(n\le 100,m\le 1000\) 。
- \(\text{Luogu}\) 题目
做法 \(\text{Solution}\)
不会 Matrix Tree ,因而用的是时间复杂度较高的做法。
首先,我们需要知道最小生成树的一个性质:
同一个图的每个最小生成树中,边权相等的边数量相等。
证明(上网随便找了一个比较清晰的):

于是,我们就可以很显然的想出一种做法:先跑一遍 Kruskal,记录一下 MST 中的边权对应的数量。然后暴力 DFS 每一种出现的边权,最后根据乘法原理算总情况。
\(\text{Code below ↓}\)
#include<bits/stdc++.h>
#define mod 31011
using namespace std;
struct point{
int x,y,z;
}a[1003],b[1003];
inline bool operator <(const point &A,const point &B)
{
return A.z<B.z;
}
int n,m,t,cnt,ans;
int f[102],d[1003],c[1003];
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
void dfs(int st,int u,int k)
{
if(st>b[k].y)
{
if(u==d[k]) cnt++;
return ;
}
int tmp[102];
for(int i=1;i<=n;i++) tmp[i]=f[i];
int p=find(a[st].x),q=find(a[st].y);
if(p!=q)
{
f[p]=q;
dfs(st+1,u+1,k);
}
for(int i=1;i<=n;i++) f[i]=tmp[i];
dfs(st+1,u,k);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
a[i].x=read(),a[i].y=read(),a[i].z=read();
sort(a+1,a+1+m);
a[0].z=-2333;
for(int i=1;i<=m;i++)
if(a[i].z==a[i-1].z) b[t].y++,c[i]=t;
else b[++t].x=i,b[t].y=i,c[i]=t;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
int p=find(a[i].x),q=find(a[i].y);
if(p!=q)
{
f[p]=q;
d[c[i]]++;
if(++cnt==n-1) break;
}
}
if(cnt!=n-1) return puts("0"),0;
for(int i=1;i<=n;i++) f[i]=i;
ans=1;
for(int i=1;i<=t;i++)
if(d[i])
{
cnt=0;
dfs(b[i].x,0,i);
ans=(ans*cnt)%mod;
for(int j=b[i].x;j<=b[i].y;j++)
{
int p=find(a[j].x),q=find(a[j].y);
if(p!=q) f[p]=q;
}
}
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号