每周题解:走廊泼水节

题目链接

走廊泼水节

题目描述

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

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

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

输入格式

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

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

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

输出格式

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

每个结果占一行。

样例 #1

样例输入 #1

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

样例输出 #1

4
17 

提示

【数据范围】

1 ≤ N ≤ 6000 1≤N≤6000 1N6000
1 ≤ Z ≤ 100 1≤Z≤100 1Z100

算法思想

根据题目描述,将一棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树,求增加的边的权值总和最小是多少。

考虑一种增加边的顺序:按照原树边权从小到大的遍历,对于当前边 a ↔ b a\leftrightarrow b ab的边权为 c c c,将 a a a b b b所属的两个连通块扩充为一个完全图。如下图所示:

在这里插入图片描述

此时,需要在两个连通块之间扩充多少条边才能将其扩充为一个完全图呢?不妨假设两个连通块中分别有 s a s_a sa s b s_b sb个节点,那么任意两个节点之间都要有边相连,则需要 s a × s b − 1 s_a\times s_b-1 sa×sb1条边,即减去当前边 a ↔ b a\leftrightarrow b ab

确定了边数后,增加的这些边的权值又该如何计算呢?不妨假设新增加的边权为 w w w,那么:

  • 如果 w < c w<c w<c, 假设原树为最终完全图的最小生成树,则增加边的过程是按边权从小到大的顺序扩充的,此时新加入边的与原树构成环,可以证明去掉 c c c,保留 w w w会得到一个更优的生成树,不满足图的唯一最小生成树仍然是这棵树的要求。
  • 如果 w = c w=c w=c,按照上相同思路,可以证明去掉 c c c保留 w w w会得到总权值相同的最小生成树, 与题意唯一矛盾。
  • 因此 w > c w>c w>c,则新加入边的最小权重为 w = c + 1 w=c+1 w=c+1

代码实现

#include <bits/stdc++.h>
using namespace std;
const int N = 6005;
struct E {
    int a, b, c;
    bool operator < (const E &e) const
    {
        return c < e.c;
    }
}e[N];
int n, p[N], s[N];
int find(int x)
{
    if(x != p[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; s[i] = 1; }
        for(int i = 1; i < n; i ++) cin >> e[i].a >> e[i].b >> e[i].c;
        sort(e + 1, e + n);
        long long ans = 0;
        for(int i = 1; i < n; i ++)
        {
            int a = e[i].a, b = e[i].b, c = e[i].c;
            a = find(a), b = find(b);
            if(a != b) {
                p[a] = b;
                ans += (long long)(c + 1) * (s[a] * s[b] - 1);
                s[b] += s[a];
            }
        }
        cout << ans << endl;
    }
    return 0;
}
  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值