

题目思路
将一个最小生成树的图,添加一些边,使得成为一个完全图,并且生成的完全图的最小生成树还是原树
算法分析
构建最小生成树的Kruskal算法
- 首先将所有的边按照从小到大的顺序排序
- 对于一条边(x,y,w),如果x和y在不在一个连通块中,就说明他们之间没有边相连那么我们相连之后,现在这两个点,各自所在的连通块(集合),都拥有了一个最短边,也就是(x,y,w)
最小生成树已经确定了,那么原来两个连通块的其他点怎么办?
假设(S_x)表示为(x)之前所在的连通块,那么(S_y)表示(y)之前所在的连通块。
由于我们不能破坏最小生成树,所以我们原来这两个连通块必须具有如下性质:假设点A属于(S_x)这个集合,点B属于(S_y)这个集合,那么A与B之间的距离,必须大于之前的(w)否者就会破坏之前的最小生成树,所以说
[A与B之间的距离最小为w+1
]
假如(S_x)有(p)个元素,然后(S_y)有(q)个元素,那么将(S_x)和(S_y)连通块中所有的点相连,就会增加:
[p × q - 1条边
]
每条边的最小长度为(w + 1)
所以就会得出
[(w + 1) × (p × q)为两个连通块成为完全图的最小代价
]
实现代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 6010;
struct Edge
{
int a, b, w;
bool operator < (const Edge & t)
{
return w < t.w;
}
}e[N];
int n;
int T;
int p[N], s[N];
int find(int x)
{
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin >> T;
while(T --)
{
cin >> n;
for(int i = 0;i < n - 1;i++)
{
cin >> e[i].a >> e[i].b >> e[i].w;
}
sort(e, e + n - 1);
for(int i = 1;i <= n;i++) p[i] = i, s[i] = 1;
int res = 0;
for(int i = 0;i < n - 1;i++)
{
int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
if(a != b)
{
p[a] = b;
res += (s[b] * s[a] - 1) * (w + 1);
s[b] += s[a];
}
}
cout << res << endl;
}
return 0;
}