BOJ 474. 小妹妹送很多快递

题意:给出一张图,保证图是连通图。每条路上会有c个敌人。现在需要每个节点向其他的所有节点送信,只有信使的数量大于等于敌人的个数,才能通过该条路。信使会同时出发。问至少需要多少信使,才能完成上述任务。

思路:最小生成树。利用并查集维护图的连通。

          并查集多维护的信息:1.连通块的个数,当整个图只有一个连通块时,退出图的构建。

                                              2.该连通块中节点的个数。当两个块相连的时候,需要加上从一个块到另一个块信使的个数。

坑:即使路上没有敌人,仍然需要一个信使去送信。需要在连通块合并时特判。

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
 
using namespace std;
 
template<class T>
inline bool read(T &n){
    T x = 0, tmp = 1; char c = getchar();
    while ((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if (c == EOF) return false;
    if (c == '-') c = getchar(), tmp = -1;
    while (c >= '0' && c <= '9') x *= 10, x += (c - '0'), c = getchar();
    n = x*tmp;
    return true;
}
 
template <class T>
inline void write(T n) {
    if (n < 0) {
        putchar('-');
        n = -n;
    }
    int len = 0, data[20];
    while (n) {
        data[len++] = n % 10;
        n /= 10;
    }
    if (!len) data[len++] = 0;
    while (len--) putchar(data[len] + 48);
}
 
struct edge{
    int u,v,cost;
    bool operator < (const edge & a) const{
        return cost < a.cost;
    }
};
 
const int MAX = 101000;
int N,T,M;
edge G[MAX];
long long ans;
 
struct DisjointSet{
    static const int MAX = 10010;
    int father[MAX], rank[MAX],cnt,sum[MAX];
    void clear(int n){
        cnt = n;
        memset(rank,0,sizeof(int) * (n + 1));
        for(int i = 1; i <= n; ++i)
            father[i] = i,sum[i] = 1;
    }
    int find(int x){
        return x = x == father[x] ? x : find(father[x]);
    }
    void merge(edge e){
        int x= e.u, y = e.v;
        x = find(x),y = find(y);
        if(x != y){
            cnt--;
 
            if(e.cost == 0)
                ans += 2 * sum[x] * sum[y];
            else
                ans += 2 * sum[x] * sum[y] * e.cost;
 
            if(rank[x] > rank[y]){
                father[y] = x;
                sum[x]  += sum[y];
            }
            else{
                father[x] = y;
                sum[y] += sum[x];
                if(rank[x] == rank[y])
                    rank[y]++;
            }
        }
    }
};
 
DisjointSet s;
 
int main(void)
{
    read(T);
    while(T--){
        read(N),read(M);
        s.clear(N);
        ans = 0;
        for(int i = 0 ; i < M; ++i)
            read(G[i].u),read(G[i].v),read(G[i].cost);
        sort(G,G + M);
        for(int i = 0 ; i < M; ++i){
            if(s.cnt == 1) break;
            else if(s.find(G[i].u) != s.find(G[i].v)){
                s.merge(G[i]);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值