AcWing 346. 走廊泼水节
题意理解
这道题目说的很清楚,就是让我们将一个最小生成树的图,添加一些边,使得这张图成为一个完全图.
但是我们这张图的最小生成树,必须还是原来那张图的最小生成树.
也就是说两张图的最小生成树表示是一模一样的.
算法解析
根据上面的信息,我们不难发现这道题目和最小生成树算法联系紧密,那么现在我们的主要问题就在于如何去构造最小生成树.
我们可以考虑最小生成树算法中的Kruskal算法.
首先将所有的边按照从小到大的顺序排序.
此时我们保证了是最小生成树的完美生成法则.
对于每一条边(x,y,w)(x,y,w)而言,他们之间有某种关系.
假如说xx和yy不在同一个连通块(集合)之中,也就是他们之间没有边相连
那么我们相连之后,现在这两个点,各自所在的连通块(集合),都拥有了一个最短边,也就是(x,y,w)(x,y,w).
最小生成树是已经确定了,但是对于这原来两个连通块的其他点怎么办?
首先我们设Sx表示为x之前所在的连通块 那么Sy表示为y之前所在的连通块.
.
因为我们不能破坏这个最小生成树,所以我们这原来的两个连通块中的点就必须有如下性质.
假如说点A属于Sx这个集合之中 点B属于Sy这个集合之中.
那么点AA与点BB之间的距离,必须要大于之前的ww,否则就会破坏之前的最小生成树
所以说(A,B)之间的距离最小为w+1
假如说我们知道
Sx有p个元素,然后Sy有q个元素.
那么将
Sx与Sy连通块的所有点相连.
显然这个两个连通块会增加.
p×q−1条边
然后每一条边的最小长度为
w+1
所以我们会得出
(w+1)×(p∗q−1)为两个连通块成为完全图的最小代价
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct rec { int x, y, z; } edge[6010];
int fa[6010], s[6010], n, T;
long long ans;
bool operator <(rec a, rec b) {
return a.z < b.z;
}
int get(int x) {
if (x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
int main() {
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i < n; i++)
scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].z);
sort(edge + 1, edge + n);
for (int i = 1; i <= n; i++)
fa[i] = i, s[i] = 1;
ans = 0;
for (int i = 1; i < n; i++) {
int x = get(edge[i].x);
int y = get(edge[i].y);
if (x == y) continue;
ans += (long long)(edge[i].z + 1) * (s[x] * s[y] - 1);
fa[x] = y;
s[y] += s[x];
}
cout << ans << endl;
}
}
细雨斜风作晓寒,
淡烟疏柳媚晴滩。
入淮清洛渐漫漫。
雪沫乳花浮午盏,
蓼茸蒿笋试春盘。
人间有味是清欢。

浙公网安备 33010602011771号