P10928 走廊泼水节 题解

题目大意:

给定一棵 n 个节点的树,现在要添加一些边,使它成为一个完全图,并且满足图的唯一最小生成树仍然是原树,求增加的边的最小边权和。

思路:

考虑 kruskal 算法的过程:将边从小到大排序,依次扫描每条边,然后考虑合并边两端的集合。

设这两端的集合为 Sx,Sy,这条边的边权为 w,因为要连成一个完全图,所以这两个集合之间任意一个点对都要连边,其中也包括这条树边。

根据最小生成树的定义可知,我们应该将其他的边都钦定为 w+1,否则就会存在一条边可以替换这条树边,使得的最小生成树不唯一。

综上所述,我们在这棵树上执行类似 kruskal 算法的过程,先将边按从小到大排序,再依次扫描每条边,并查集维护,若两端的集合未被合并,根据乘法原理,应该连的边权和为 (sizexsizey1)(w+1),然后再将两个集合合并即可。

时间复杂度为 O(nlogn)

Code:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 6010;

int T, n;
struct node{
    int a, b, w;
    bool operator <(const node &o) const {
        return w < o.w;
    }
}edges[N];
int p[N], sz[N];
int res;

int find(int x) {
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main() {
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        int a, b, c;
        for(int i = 1; i < n; i++) {
            scanf("%d%d%d", &a, &b, &c);
            edges[i] = {a, b, c};
        }
        sort(edges + 1, edges + n);
        for(int i = 1; i <= n; i++) p[i] = i, sz[i] = 1;
        res = 0;
        for(int i = 1; i < n; i++) {
            int a = edges[i].a, b = edges[i].b, w = edges[i].w;
            a = find(a), b = find(b);
            if(a != b) {
                res += (w + 1) * (sz[a] * sz[b] - 1);
                p[a] = b;
                sz[b] += sz[a];
            }
        }
        printf("%d\n", res);
    }
    return 0;
}
posted @   Brilliant11001  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示