JOYOI TYVJ1391 走廊潑水節 Kruskal應用
題面:
一共有N個OIER打算參加這個潑水節,同時很湊巧的是正好有N個水龍頭。N個水龍頭之間正好有N-1條小道,並且每個水龍頭都可以經過小道到達其他水龍頭(這是一棵樹)。但是OIER們爲了迎接中中的挑戰,決定修建一些個道路,使得每個水龍頭到每個水龍頭之間都有一條直接的道路連接(也就是構成一個完全圖)。但是OIER們很懶,並且記性也不好,他們只會去走那N-1條小道,並且希望所有水龍頭之間修建的道路,都要大於兩個水龍頭之前連接的所有小道(小道當然要是最短的了)。所以神COW們,幫那些OIER們計算一下吧,修建的那些道路總長度最短是多少?
解題過程:
- 開始時候想着順着已經有的MST走一遍,比如走到 x -> y這條邊時,將所有和x相連的邊長和 length(x,y)取一個max。但這種想法是錯誤的,因爲對於還沒有加入到當前MST的節點來說,它有可能連到另外一個已經加入當前MST但卻不是x的節點,這樣的話,它們之間的距離沒有取max,可能比length(x,y)更小,導致x->y並非MST中的邊,不和題意。
- 模擬Kruskal算法的過程,設當前部分MST爲s,被最短邊相連的節點集合爲t,s、t之間其他的邊爲|s| * |t|-1條,令其長度爲length(x,y)+1(這樣使x->y爲MST中的邊以滿足題意),累加這個過程中的(|s||t|-1)(length(x,y)+1)
AC代碼:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,p) for(int i=l;i<=p;i++)
#define MP make_pair
typedef long long ll;
typedef pair<int,int> P;
int T;
int n;
int x,y,z;
int fa[6005];
int sum[6005];
//int a[6005][6005];
struct Edge{
int x,y;
int z;
bool operator<(const Edge &b)const{
return z > b.z;
}
};
priority_queue<Edge> pq;
void init(){
rep(i,1,n) fa[i] = i,sum[i] = 1;
}
int get(int x){
if(fa[x] == x) return fa[x];
else return fa[x] = get(fa[x]);
}
ll unit(int x,int y){
x = get(x);
y = get(y);
ll res = 0;
res += (sum[x]*sum[y]-1);
sum[x] += sum[y];
sum[y] = 0;
fa[y] = x;
return res;
}
int main(){
// freopen("in.txt","r",stdin);
cin >> T;
int x,y,z;
Edge t;
while(T--){
cin >> n;
// memset(a,0,sizeof a);
memset(sum,0,sizeof sum);
init();
rep(i,1,n-1){
cin >> t.x >> t.y >> t.z;
pq.push(t);
}
ll ans = 0;
while(!pq.empty()){
Edge now = pq.top();
//cout << pq.top().z << endl;
pq.pop();
int x = get(now.x),y = get(now.y);
if(x != y) ans += unit(now.x,now.y)*(now.z+1);
//cout << ans/(now.z+1) << endl;
}
//cout << endl;
cout << ans << endl;
}
return 0;
}