给定一棵 N 个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。
求增加的边的权值总和最小是多少。
注意: 树中的所有边权均为整数,且新加的所有边权也必须为整数。
输入格式
第一行包含整数 t,表示共有 t 组测试数据。
对于每组测试数据,第一行包含整数 N。
接下来 N−1 行,每行三个整数 X,Y,Z,表示 X 节点与 Y 节点之间存在一条边,长度为 Z。
输出格式
每组数据输出一个整数,表示权值总和最小值。
每个结果占一行。
数据范围
1≤N≤6000
1≤Z≤100输入样例:
2 3 1 2 2 1 3 3 4 1 2 3 2 3 4 3 4 5
输出样例:
4 17
先介绍什么是完全图:
完全图:对于图中的任意一点a,点a除了自己外对于图中所有点均有一条边相连
解题思路:
我们先假设a, b两个边分别在两个完全图中 a, b的权值为w
此时我们要将左边和右边的完全图合并,假设左边完全图和右边完全图的点的数量分别为n, m要将两个完全图合并为一个完全图即对于左边完全图中的任意一点都要向右边完全图中任意一点连一条边,因此连接的边的总数为n * m;
1:首先我们先按边的大小从小到大排序,假设枚举到点a, b时,他们的权重为w.因为边是从小到大排序的所以当前枚举的边即为两个完全图中权值最大的边。
2:我们加边的权值应该加多少:
{
1:若加的边的权值小于w,则原a, b, w就不是最小生成树,不可行
2:若加的边的权值等于w, 则有两个一样的最小生成数,但题意要求需要满足唯一的最 小生成数,因此不可行
3:若加的权值大于w,由于权值从小到大排序,所以两个完全图的权值全都小于w,因 此加上一个大于w的数不影响最后结果,又因为求的是权值的最小值,所以我们把每 条边加上w + 1即可;
}
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 6010; int n; int p[N];//记录的是N的祖宗节点 int cnt[N];//记录的是节点数量 struct Edge { int a, b, w; bool operator < (const Edge &W) const { return w < W.w; } }edges[N]; int find(int x)//并查集模板 { if (p[x] != x) p[x] = find(p[x]); return p[x]; } int main() { int T; cin >> T; while (T -- ) { cin >> n; for (int i = 1; i <= n; i ++ )//初始化 { p[i] = i; cnt[i] = 1; } for (int i = 0; i < n - 1; i ++ ) { int a, b, w; scanf("%d %d %d", &a, &b, &w); edges[i] = {a, b, w}; } sort(edges, edges + n - 1);//按权值从小到大排序 int res = 0; for (int i = 0; i < n - 1; i ++ ) { int a = find(edges[i].a), b = find(edges[i].b), w = edges[i].w; if (a != b) { p[a] = b; res += (cnt[a] * cnt[b] - 1) * (w + 1);//对于a, b所在的两个完全图中 //总共要连接cnt[a] * cnt[b]条边,但是要除去a -> b本身的边(最小生成树定义) cnt[b] += cnt[a];//新的完全图b的节点个数 } } cout << res << endl; } return 0; }