JOYOI TYVJ1391 走廊泼水节 Kruskal应用

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值