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

浙公网安备 33010602011771号