欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[NOIP模拟] Road (并查集)

程序员文章站 2022-05-22 13:35:51
...

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

本题结束 :

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

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