You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 346 走廊泼水节

一、题目大意

给定一棵 N 个节点的树,要求 增加若干条边,把这棵树扩充为 完全图,并满足图的 唯一 最小生成树 仍然是这棵树。

求增加的边的权值总和最小是多少

注意 树中的所有边权均为整数,且新加的所有边权也必须为整数。

输入格式 第一行包含整数 t,表示共有 t 组测试数据。

对于每组测试数据,第一行包含整数 N

接下来 N1 行,每行三个整数 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 

二、题目解析

解释(1,4),(1,3),(2,4)共三条边需要连接上,才能构成完全图,(2,3)是不需要连接的,因为它是最小生成树的一部分。

Kruskal算法,在每循环到一条可以合并两个连通块的边edge时,记edge的边长为c,为了形成一个完全图,就要使得两个已经是完全图的连通块中的点有边,但是为了使最后的唯一最小生成树还是原来那棵而且,新增的边一定要大于c

  • 假设新边小于c,因为新增边后会成环,当断开边edge形成的树大小会变小,即不是原来那棵,所以不成立

  • 假设新边等于c,同样的断开edge,会形成一个大小一样但结构不一样的树,不满足唯一,所以也不成立

所以只要在每次新增edge的时候,给两个连通块内的点增加 c+1 长的边即可。

Code

#include <bits/stdc++.h>
using namespace std;

const int N = 6010;

struct Edge {
    int a, b, w;
    const bool operator<(const Edge &t) const {
        return w < t.w;
    }
} e[N];

int n;
int cnt[N]; // 配合并查集使用的,记录家族人员数量
int p[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;

        int el = n - 1;
        // 录入n-1条边
        for (int i = 0; i < el; i++) {
            int a, b, c;
            cin >> a >> b >> c;
            e[i] = {a, b, c};
        }
        // 排序
        sort(e, e + el);

        int res = 0;
        for (int i = 0; i < el; i++) {
            auto x = e[i];
            int a = find(x.a), b = find(x.b), w = x.w;
            if (a != b) {
                // a集合数量b集合数量相乘但需要减去已经建立的最小生成权这条边
                // w是最小的其它的可以建立最小也得大于w,即w+1
                res += (cnt[a] * cnt[b] - 1) * (w + 1);
                p[a] = b;         // 合并到同一集合
                cnt[b] += cnt[a]; // b家族人数增加cnt[a]个,并查集数量合并
            }
        }
        // 输出
        printf("%d\n", res);
    }
    return 0;
}