D140 最小生成树 Kruskal 算法 P10928 走廊泼水节
D140 最小生成树 Kruskal 算法 P10928 走廊泼水节_哔哩哔哩_bilibili
给定一棵树,增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。求增加的边的权值总和最小值。
注意: 树中的所有边权均为整数,且新加的所有边权也必须为整数。
思路
跑最小生成树的过程,同时补全完全图,累计非树边的代价
图中左树补全完全图,代价为 3;右树补全完全图,代价为 4;两个完全图之间连边怎样计算代价呢?

设两个完全图集合的点集数量分别为 $s_a$ 和 $s_b$
在两个完全图之间连边构成新完全图,还需连 $s_a*s_b$ 条边,其中一条是树边,边权为 $w$,则其余非树边,边权为 $w+1$,
则累加答案 $ans+=(s_a*s_b-1)(w+1)$
枚举完树边,即构成最终完全图,得到最终答案
相关板子:
D08【模板】最小生成树 Kruskal 算法 - 董晓 - 博客园
// 最小生成树 Kruskal算法 O(NlogN) #include<bits/stdc++.h> // #define int long long using namespace std; const int N=6010; int T,n,ans,siz[N],fa[N]; struct E{int x,y,w;}e[N]; //边集 int find(int u){ //并查集的找根 return fa[u]==u?u:fa[u]=find(fa[u]); } void kruskal(){ for(int i=1; i<=n; i++) fa[i]=i,siz[i]=1; sort(e+1,e+n,[&](E a,E b){return a.w<b.w;}); for(int i=1; i<=n-1; i++){ int x=find(e[i].x),y=find(e[i].y); if(x!=y){ ans+=(siz[x]*siz[y]-1)*(e[i].w+1); fa[x]=y; siz[y]+=siz[x]; //合并集合的点数 } } cout<<ans<<"\n"; } signed main(){ cin>>T; while(T--){ cin>>n; for(int i=1,x,y,z; i<n; i++){ cin>>x>>y>>z; e[i]={x,y,z}; } ans=0; kruskal(); } }
浙公网安备 33010602011771号