这是一道最小生成树相关的题目
题目要求在一棵最小生成树的基础上增加一些边变成一张完全图,但是这张图的最小生成树仍然是原来的树,求增加的边的边权的和最小是多少。
我们首先将边按照边权升序排列,像kruskal一样,之后我们扫描每一条边,设当前的边(x,y,z),x所在的并查集为Sx,y所在的并查集为Sy,此时应当合并这两个S。
假设u∈Sx,v∈Sy,若(u,v)≠(x,y),则在最终的完全图上,我们肯定要在uv之间加上一条边,于是,这条边与uv在生成树上的路径构成了一个环,但是为了保证(x,y)依然在最小生成树上,那么(u,v)的边权一定大于(x,y)的边权,为了让答案最小化,这个边权为z+1。
那么,Sx,Sy之间一共会增加|Sx|*|Sy|-1条边,我们只需要将(z+1)*(|Sx|*|Sy|-1)累加到答案中即可。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 inline int read() { 7 int ret=0; 8 int op=1; 9 char c=getchar(); 10 while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();} 11 while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar(); 12 return ret*op; 13 } 14 typedef long long ll; 15 ll ans; 16 struct node { 17 int x,y,dis; 18 bool operator <(const node &xx) const { 19 return dis<xx.dis; 20 } 21 }a[6010]; 22 int n; 23 int fa[6010],size[6010]; 24 inline int find(int x) { 25 if(fa[x]==x) return x; 26 return fa[x]=find(fa[x]); 27 } 28 int solve() { 29 n=read(); 30 for(int i=1;i<n;i++) { 31 a[i].x=read(); 32 a[i].y=read(); 33 a[i].dis=read(); 34 } 35 sort(a+1,a+n); 36 for(int i=1;i<=n;i++) 37 fa[i]=i,size[i]=1; 38 ans=0; 39 for(int i=1;i<n;i++) { 40 int x=find(a[i].x); 41 int y=find(a[i].y); 42 if(x==y) continue ; 43 ans+=(long long)(a[i].dis+1)*(size[x]*size[y]-1); 44 fa[x]=y; 45 size[y]+=size[x]; 46 } 47 printf("%lld\n",ans); 48 return 0; 49 } 50 int main() { 51 int t=read(); 52 while(t--) { 53 solve(); 54 } 55 }