P10928 走廊泼水节

P10928 走廊泼水节

题目描述

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

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

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

输入格式

第一行包含整数 ttt,表示共有 ttt 组测试数据。

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

接下来 N−1N-1N1 行,每行三个整数 X,Y,ZX,Y,ZX,Y,Z,表示 XXX 节点与 YYY 节点之间存在一条边,长度为 ZZZ

输出格式

每组数据输出一个整数,表示权值总和最小值。

每个结果占一行。

输入输出样例 #1

输入 #1

2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5

输出 #1

4
17

说明/提示

数据保证,1≤t≤101\leq t\leq 101t101≤N≤60001 \le N \le 60001N60001≤Z≤1001 \le Z \le 1001Z100

对于这题,要令其变为完全图后MST不变,那么新增的每一条边都必须比当前路径最小边权大。考虑到复杂度,我们选用kruskal。我们需要维护一个size数组,用于记录每个连通分量的大小。在合并联通分量A、B时,我们将新增size[A]*size[B]-1条边,这些边的边权则等于当前选择的最短边+1。

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

int find_(vector<int>& p, int x) {
    if (p[x] == x) {
        return p[x];
    }
    return p[x] = find_(p, p[x]);
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<tuple<int, int, int>> e;
        for (int i = 0; i < n - 1; i++) {
            int x, y, z;
            cin >> x >> y >> z;
            e.emplace_back(z, x, y);
        }
        sort(e.begin(), e.end());
        vector<int> p(n + 1);
        iota(p.begin() + 1, p.end(), 1);
        vector<long long> sz(n + 1, 1);
        long long total_sum = 0;
        for (const auto& edge : e) {
            auto [w, u, v] = edge;
            int root_u = find_(p, u);
            int root_v = find_(p, v);
            if (root_u != root_v) {
                long long size_u = sz[root_u];
                long long size_v = sz[root_v];
                total_sum += (size_u * size_v - 1) * (w + 1);
                p[root_v] = root_u;
                sz[root_u] += sz[root_v];
            }
        }
        cout << total_sum << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值