「最小生成树计数」题解

题面 \(\text{Description}\)

  • 给出一个由 \(n\) 个点 \(m\) 条边构成的简单无向加权图。
  • 求最小生成树 MST 的个数。
  • \(n\le 100,m\le 1000\)
  • \(\text{Luogu}\) 题目

做法 \(\text{Solution}\)

不会 Matrix Tree ,因而用的是时间复杂度较高的做法。
首先,我们需要知道最小生成树的一个性质:

同一个图的每个最小生成树中,边权相等的边数量相等。

证明(上网随便找了一个比较清晰的):
最下生成树性质的证明.png

于是,我们就可以很显然的想出一种做法:先跑一遍 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;
}

posted @ 2021-06-16 23:16  LJC001151  阅读(144)  评论(0)    收藏  举报