codeforces gym 2016-2017 NEERC, Moscow Subregional K. Knights of the Old Republic 最小生成树+dp

注:本文思路部分引自鸟神的题解
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 3e6+5;
typedef __int64 ll;
ll a[N], b[N], s[N], fa[N];


struct edge
{
    int u, v, val;
}ed[N];

bool cmp( edge a, edge b )
{
    return a.val < b.val;
}

int root( int x )
{
    if( fa[x] == x ) return x;
    else{
        fa[x] = root( fa[x] );
        return fa[x];
    }
}

int main()
{
    int n, m, i;
    cin >> n >> m;
    for( i = 1; i <= n; i ++ ) fa[i] = i;
    for( i = 1; i <= n; i ++ ) scanf("%I64d%I64d", &a[i], &b[i]), s[i] = a[i]*b[i];
    for( i = 1; i <= m; i ++ ){
        scanf("%d %d %d", &ed[i].u, &ed[i].v, &ed[i].val);
    }
    sort( ed+1, ed+m+1, cmp );
    for( i = 1; i <= m; i ++ ){
        int x = root(ed[i].u), y = root(ed[i].v);
        if( x != y ){
            ll A = max( a[x], max( a[y], (ll)ed[i].val ) ), B = min( b[x], b[y] );
            //A取三者最大值,B取两个点集内最小单个费用
            s[x] = min( s[x]+s[y], A*B );
            a[x] = A;//注意修改,维护的是点集内能有多少个自由移动的点
            b[x] = B;
            fa[y] = x;
        }
    }
    ll ans = 0;
    for( i = 1; i <= n; i ++ ){
        if( fa[i] == i ) ans += s[i];//存在不同的连通块
    }
    cout << ans << endl;
}
/*
题意:给n个点,每个点需要传送a个大哥,穿一个大哥需要b的费用,每条边导通的条件是两边的大哥数量大于边权
只要每个点有过,注意,有过a个大哥即可,问最小花费
思路:
蜜汁最小生成树
这样我们可以维护一个dp,st代表点集,dp[st]代表搞定了st的所有需求花费的最小费用
对于两个点集p和q
如果不连这条边 答案为dp[p]+dp[q]
如果连,那么这两个点集内边权比这条边权小的边就必然会被导通,因为大的既然都能自由移动了,小的不还是很容易嘛
(这里不必考虑点集内具体的连边情况)

那这样的话,不妨从小到大枚举边,因为这样则两个点集内的点必然会导通
用krukal实现这样的过程,搞出了一个最小生成树

这么写可能有很多地方觉得不一定对,但仔细观察合并过程,大部分情况都是考虑进来的
有点像二极管的导通啊~
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值