AcWing 346. 走廊潑水節
阿新 • • 發佈:2020-04-17
題意理解
這道題目說的很清楚,就是讓我們將一個最小生成樹的圖,新增一些邊,使得這張圖成為一個完全圖.
但是我們這張圖的最小生成樹,必須還是原來那張圖的最小生成樹.
也就是說兩張圖的最小生成樹表示是一模一樣的.
演算法解析
根據上面的資訊,我們不難發現這道題目和最小生成樹演算法聯絡緊密,那麼現在我們的主要問題就在於如何去構造最小生成樹.
我們可以考慮最小生成樹演算法中的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連通塊的所有點相連.
顯然這個兩個連通塊會增加.
然後每一條邊的最小長度為
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; } }