D140 最小生成树 Kruskal 算法 P10928 走廊泼水节

D140 最小生成树 Kruskal 算法 P10928 走廊泼水节_哔哩哔哩_bilibili

 

P10928 走廊泼水节 - 洛谷

给定一棵树,增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。求增加的边的权值总和最小值。

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

思路

跑最小生成树的过程,同时补全完全图,累计非树边的代价

图中左树补全完全图,代价为 3;右树补全完全图,代价为 4;两个完全图之间连边怎样计算代价呢?

image

设两个完全图集合的点集数量分别为 $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();
  }
}

 

posted @ 2026-04-19 08:39  董晓  阅读(95)  评论(0)    收藏  举报