1. 程式人生 > WINDOWS開發 >AcWing 346. 走廊潑水節

AcWing 346. 走廊潑水節

題意理解
這道題目說的很清楚,就是讓我們將一個最小生成樹的圖,新增一些邊,使得這張圖成為一個完全圖.

但是我們這張圖的最小生成樹,必須還是原來那張圖的最小生成樹.

也就是說兩張圖的最小生成樹表示是一模一樣的.

演算法解析
根據上面的資訊,我們不難發現這道題目和最小生成樹演算法聯絡緊密,那麼現在我們的主要問題就在於如何去構造最小生成樹.

我們可以考慮最小生成樹演算法中的Kruskal演算法.

首先將所有的邊按照從小到大的順序排序.
此時我們保證了是最小生成樹的完美生成法則.

對於每一條邊(x,y,w)(x,w)而言,他們之間有某種關係.
假如說xx和yy不在同一個連通塊(集合)之中,也就是他們之間沒有邊相連

那麼我們相連之後,現在這兩個點,各自所在的連通塊(集合),都擁有了一個最短邊,也就是(x,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,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;
	}
}