[NOIP模拟] 拆墙 Wall(最大生成树)

题目描述

    地主的傻儿子豆豆家很大很大,由很多个区域组成。其中有不少封闭的区域,豆豆觉得很不爽于是决定拆墙,把家打通使得他可以访问到每一个区域(包括家外面无限大的区域)。我们用 N 个端点和 M 条边来描述豆豆的家。第 i 个端点的坐标为 (xi,yi) ,第 i 条边连接端点 Ai Bi ,拆除所需要花费的力气为 Ci 。保证所有边只在端点相交,也就是这是一个平面图,也没有重边和自环。

    现在豆豆想知道他最少一共需要花费多少力气?

输入格式

    第一行一个整数 T 表示数据组数。
    每组数据第一行两个整数 NM
    接下来 N 行每行两个整数 Xi 和 Yi。
    接下来 M 行每行三个整数 Ai,Bi,Ci。

输出格式

    每组数据输出两个整数表示最少拆除的墙的数量和拆墙最少需要多少的力气。注意所有墙可能不互相连通。

样例数据

Input 

1
4 4
-1 -1
-1 1
1 1
1 -1
1 2 1
2 3 2
3 4 1
4 1 2
Output

1 1

备注

[数据范围]
对于 30% 的数据, N,M10 T=10
对于 70% 的数据, N5000,M10000 T=1
对于 100% 的数据, N100000; M200000 ΣN300000 ΣM500000 |xi|,|yi|100000 0wi10000 T=3

题解 :

    说实话这道题,真的坑,主要是坑了很多人的时间,这道题讲真出题人就想拿这道题来骗人, 显然这个x, y没有什么卵用, 样例又给的是个框, 让我多想了。
    回归正题, 通过题意我们可以很容易发现,最终我们剩下的图一定是颗树, 且我们要删最小的边, 于是我们可以发现,最优的情况就是最大生成树, 于是我们就按照常理写, 有人会问为什么不考虑不在一个联通块, 话说你写的时候完全不用考虑啊, 你只是关心你并查集的时候这些的点是不是在同一个并查集就行了。

代码 :

这里我按秩合并了的

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
#include <map>
#include <vector>
#define LL long long
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 MAXM = 2e5 + 5;
struct point {
    int from, to, len;
};
point edge[MAXM * 2];
int fa[MAXN], r[MAXN], x[MAXN], y[MAXN];

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

inline void make_set(int x) {
    fa[x] = x; r[x] = 0;
}

inline bool comp(const point & a, const point & b) {
    return a.len > b.len;
}

inline int Union(int x, int y) {
    int gx = getfather(x);
    int gy = getfather(y);
    if(gx == gy) return 0;
    if(r[gx] < r[gy]) 
        fa[gx] = gy;
    else {
        if(r[gx] == r[gy]) ++r[gx];
        fa[gy] = gx;
    }
    return 1;
}

int main() {
    freopen("wall.in", "r", stdin);
    freopen("wall.out", "w", stdout);
    int times = read();
    while(times--) {
        memset(r, 0, sizeof(r));
        int n = read(), m = read();
        for(int i = 1; i <= n; ++i) x[i] = read(), y[i] = read();
        for(int i = 1; i <= m; ++i) {
            edge[i].from = read(), edge[i].to = read(); edge[i].len = read();
        }
        sort(edge + 1, edge + m + 1, comp);
        LL ans = 0; int num = 0;
        for(int i = 1; i <= n; ++i) make_set(i);
        for(int i = 1; i <= m; ++i) {
            if(!Union(edge[i].from, edge[i].to)) ans += edge[i].len, ++num;
        }
        cout<<num<<' '<<ans<<'\n';
    }
}

本题结束 :

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值