(1)定理
- 不同的最小生成树中,每种权值的边出现的个数是确定的。
- 如果 A A A, B B B同为 G G G的最小生成树,如果 A A A, B B B都是从小到大加边,那么每种权值加完后图的联通性相同。
- 如果在最小生成树 A A A中权值为 v v v的边有 k k k条,用任意 k k k条权值为 v v v的边替换 A A A中的权为 v v v的边且不产生环的方案都是一颗合法的最小生成树。
(2)算法
-
先用Kruskal算法记录每种权值在最小生成树中的出现次数,然后对于每种权值的边分别求出方案,利用乘法原理即可。
-
代码
struct build
{
int l,r;
int cnt;
}a[maxm];
int Kruskal()
{
sort(edge + 1, edge + m + 1);
//使用并查集来维护连通块状态
for(int i = 1; i <= n; i++)
f[i] = i;
int res = 0;
//记录每种权值的边的总数(记录左右边界即可)和在最小生成树中的出现次数
for(int i = 1; i <= m; i++)
{
if(edge[i].dis != edge[i - 1].dis)
{
tot++;
a[tot].l = i;
a[tot - 1].r = i - 1;
}
int x = Find(edge[i].u);
int y = Find(edge[i].v);
if(x != y)
{
f[y] = x;
a[tot].cnt++;
res += edge[i].dis;
}
}
a[tot].r = m;
return res;
}
利用dfs实现定理3中的选边方案
//遍历每种权值的所有边,都有选/不选两种选择
void dfs(int x, int now, int num)
{
if(now == a[x].r + 1)
{
if(num == a[x].cnt)
sum++;
return;
}
int fx = Find(edge[now].u);
int fy = Find(edge[now].v);
if(fx != fy)
{
f[fx] = fy;
dfs(x, now + 1, num + 1);
f[fx] = fx;
f[fy] = fy;
}
dfs(x, now + 1, num);
}
还有一种Matrix-Tree定理也可以求最小生成树计数问题。