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;
}