[NOIP模拟] Road (并查集)

[NOIP模拟] Road (并查集)


题目背景

SOURCE:NOIP2015-SHY-8

题目描述

    给出一张 n 个点,m 条边的无向图,摧毁每条边都需要一定的体力,并且花费的体力值各不相同,给定图中两个点 x,y(x ≠ y),每当 ( x , y ) 之间存在路径,就需要不断摧毁当前图中花费体力最少的一条边,直到该路径不联通。他定义 cost( x , y ) 为摧毁 ( x , y ) 之间路径花费的体力和。
    他想要求出以下这个结果:

( cost(i,j))%109(1i,jn)

   其中 i , j ∈ n,并且 i < j 。

输入格式

第一行两个整数 n,m ,表示点数和边数。
接下来 m 行,每行三个整数 x,y,z,表示 x 和 y 之间存在一条花费体力为 z 的无向边。

输出格式

输出一个整数表示所求结果。

样例数据 1

Input
6 7
1 2 10
2 3 2
4 3 5
6 3 15
3 5 4
4 5 3
2 6 6

Output
256

备注

[数据范围]
对 50% 的输入数据 : 1n1001m1000
对 100% 的输入数据 : 1n,m1000001z100000

题解 :

    感觉自己在考场就是个SB, 这么简单的居然看不出来,我还正着跑多集合割点。
重点:
    此题是一道并查集裸题, 从大的边向前跑,对于当前边查看是否联通, 用并查集维护, 不联通说明只要割掉这条边对应的两个联通块里的所有点就不连通了, 于是这条边对答案的贡献为 :

(size[i]size[j]prefix[x])%mod

    prefix[x] 是割掉这条边所需要的代价, 就是前面所有边的和, size 为联通块的大小。
    如果两联通块联通, 就跳过。

下面贴出代码 :

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
#include <map>
#include <vector>
using namespace std;

inline int read() {
    int i = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1; ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
    }
    return i * f;
}

const int MAXN = 1e5 + 5;
const int mod = 1e9;
int prefix[MAXN], z[MAXN], r[MAXN], father[MAXN], sze[MAXN];
struct point {
    int from, to, len;
};
point e[MAXN];

inline int getfather(int x) {
    return father[x] == x ? x : father[x] = getfather(father[x]);
}

inline void set(int x) {
    father[x] = x; r[x] = 1; sze[x] = 1;
}

inline void merge(int x, int y) {
    int gx = getfather(x);
    int gy = getfather(y);
    if(gx == gy) return ;
    if(r[gx] < r[gy]) 
        father[gx] = gy, sze[y] += sze[x];
    else {
        if(r[gx] == r[gy]) ++r[gx];
        father[gy] = gx; sze[x] += sze[y];
    }
}

inline bool comp(const point & a, const point & b) {
    if(a.len == b.len) return a.from < b.from;
    return a.len < b.len;
}

int main() {
    int n = read(), m = read();
    for(int i = 1; i <= m; ++i) 
        e[i].from = read(), e[i].to = read(), e[i].len = read();

    sort(e + 1, e + m + 1, comp);
    for(int i = 1; i <= m; ++i)
        prefix[i] = (prefix[i - 1] + e[i].len) % mod;
    for(int i = 1; i <= n; ++i) set(i);
    long long ans = 0;
    for(int i = m; i >= 1; --i) {
        int gx = getfather(e[i].from);
        int gy = getfather(e[i].to);
        if(gx == gy) continue;
        ans = (long long)(ans + (long long)prefix[i] * sze[gx] % mod * sze[gy] % mod) % mod;
        merge(gx, gy);
    }
    cout<<ans;
}

本题结束 :

感谢阅读本篇文章,喜欢的话,点个赞吧,你的鼓励就是我最大的动力

有什么意见,尽情发表吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值