【CH6201】走廊泼水节

时间:2022-05-24 04:50:46

这是一道最小生成树相关的题目

题目要求在一棵最小生成树的基础上增加一些边变成一张完全图,但是这张图的最小生成树仍然是原来的树,求增加的边的边权的和最小是多少。

我们首先将边按照边权升序排列,像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)累加到答案中即可。

【CH6201】走廊泼水节【CH6201】走廊泼水节
 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 }
AC Code