洛谷 P10928 走廊泼水节

题目描述

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

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

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

输入格式

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

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

接下来 \(N-1\) 行,每行三个整数 \(X,Y,Z\),表示 \(X\) 节点与 \(Y\) 节点之间存在一条边,长度为 \(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\leq t\leq 10\),\(1 \le N \le 6000\),\(1 \le Z \le 100\)。

算法分析

题目讲的很明确,就是要求加成完全图所需的最小边权。这题很好想,因为要想最小生成树还是原树的话,那么新加进来的边权就是这两个节点的最大权值加一,所以我们只需要对原树跑一边最小生成树,求出每一步合并之后的点数,加上\((cnt[x] \times cnt[y]-1) \times (w+1)\)即可,此题只需要证明一下,其他很简单。

AC代码

#include<bits/stdc++.h>
using namespace std;
struct node{
    int x,y,z;//结构体
};
bool cmp(node a,node b){//排序规则
    return a.z<b.z;
}
int T,n;
node a[6005];
int fa[6005];
int cnt[6005];
long long ans = 0;//不开long long见祖宗
int find(int x){//并查集
    if(fa[x]==x){
        return x;
    }else{
        return fa[x] = find(fa[x]);
    }
}
void kruskal(){
    sort(a+1,a+n,cmp);//排序
    for(int i = 1; i<n; i++){
        int x = find(a[i].x);
        int y = find(a[i].y);
        if(x!=y){
            ans+=(cnt[x]*cnt[y]-1)*(a[i].z+1);//加上权值
            fa[x] = y;//合并
            cnt[y]+=cnt[x];//点数相加
        }
    }
}
int main(){
    cin>>T;
    while(T--){
        cin>>n;
        ans = 0;//多测不清空,爆零两行泪
        for(int i = 1; i<n; i++){
            fa[i] = i;//初始化
            cnt[i] = 1;
            cin>>a[i].x>>a[i].y>>a[i].z;
        }
        cnt[n] = 1;
        fa[n] = n;//添上
        kruskal();//最小生成树
        cout<<ans<<"\n";//输出答案
    }
    return 0;
}

总结

此题很简单,只需稍加证明,就可以得出解题思路,代码也是经典的最小生成树模板,但是记得此题有多测,要清空,总体来说这道题还是很简单的,算简单的绿题了。

posted @ 2026-08-21 23:37  xmgene  阅读(10)  评论(0)    收藏  举报