题目:CH6201.
题目大意:给定一棵
n
n
n个点的树,让你扩充成一张完全图,使得原树是这张完全图的唯一最小生成树,并输出加的边的最小边权和.
1
≤
n
≤
6000
1\leq n\leq 6000
1≤n≤6000,边权
≤
100
\leq 100
≤100,数据组数
≤
10
\leq 10
≤10.
这道题用了一个类似于Kruskal的东西,然后顺便计算出了最小边权和.
首先,我们将树拆开,将边排序,然后不断用并查集合并.
每合并一次,我们设合并的两个联通块为 x x x和 y y y,那么合并时就会 x x x和 y y y之间除了已有的这条边本身,其它边的边权都要大于已有的这条边.
而且我们可以发现,其它边的限制条件肯定只有大于这条边和比这条边小的树边,所以我们发现其它边的边权只要设成这条边的边权 + 1 +1 +1即可.
那么我们就kruskal的同时,每当加入一条边,就将答案加上其它的边的边权,我们发现其它边的数量即为
x
x
x的大小乘上
y
y
y的大小减
1
1
1,边权都为这条边的边权加
1
1
1.写成公式即为:
a
n
s
=
a
n
s
+
(
x
.
s
i
z
e
∗
y
.
s
i
z
e
−
1
)
∗
(
v
[
i
]
+
1
)
.
ans=ans+(x.size*y.size-1)*(v[i]+1).
ans=ans+(x.size∗y.size−1)∗(v[i]+1).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=6000;
struct side{
int x,y,v;
bool operator < (const side p)const{return v<p.v;}
}e[N+9];
int ans,n;
int fa[N+9],cnt[N+9];
int get(int u){return fa[u]=u^fa[u]?get(fa[u]):u;}
Abigail into(){
scanf("%d",&n);
for (int i=1;i<n;i++)
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
}
Abigail work(){
ans=0;
for (int i=1;i<=n;i++) fa[i]=i,cnt[i]=1;
stable_sort(e+1,e+n);
for (int i=1;i<n;i++){
ans+=(e[i].v+1)*(cnt[get(e[i].x)]*cnt[get(e[i].y)]-1);
cnt[get(e[i].x)]+=cnt[get(e[i].y)];
fa[get(e[i].y)]=get(e[i].x);
}
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
int T;
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}
本文介绍了一种算法,用于解决将给定树扩充至完全图,且原树为该完全图唯一最小生成树的问题,同时计算扩充边的最小边权总和。通过Kruskal算法的变体,利用并查集合并树的联通块,巧妙计算出所有新增边的边权,最终得出最优解。
刷题记录——CH6201 走廊泼水节(最小生成树)&spm=1001.2101.3001.5002&articleId=87748821&d=1&t=3&u=caafb2c2c6ac4efabd701d59b74df98a)
420

被折叠的 条评论
为什么被折叠?



