招募军队(最小生成树)

题意

\(n\)个城市,\(m\)对建交关系。对于城市\(i\),其直接招募军队的费用是\(a_i\)。如果城市\(i\)与城市\(j\)建交,城市\(i\)还可以通过私下关系从城市\(j\)间接招募军队,费用为\(w_{i,j}\),当然前提是城市\(j\)已经直接或间接招募了军队。
你需要最大化实际费用\(c\)\(\sum\limits_{i=1}^n a_i\)差值的绝对值,约定每个城市只需招募一次军队。

数据范围

\(1 \leq n \leq 100000\)
\(1 \leq m \leq 100000\)

题解

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 100010, M = 2 * N;

int n, m;
ll aa[N];
int p[N];

struct Edge
{
    int a, b;
    ll c;

    bool operator < (const Edge &t) const
    {
        return c < t.c;
    }
}e[M];

int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) scanf("%lld", &aa[i]);
    for(int i = 0; i <= n; i ++) p[i] = i;
    for(int i = 1; i <= m; i ++) {
        int a, b;
        ll c;
        scanf("%d%d%lld", &a, &b, &c);
        e[i] = {a, b, c};
    }
    ll tmp = 0;
    for(int i = 1; i <= n; i ++) {
        e[i + m] = {0, i, aa[i]};
        tmp += aa[i];
    }
    sort(e + 1, e + m + n + 1);
    ll ans1 = 0, ans2 = 0;
    for(int i = 1; i <= m + n; i ++) {
        int a = e[i].a, b = e[i].b;
        ll c = e[i].c;
        int pa = find(a), pb = find(b);
        if(pa != pb) {
            p[pa] = pb;
            ans1 += c;
        }
    }
    for(int i = 0; i <= n; i ++) p[i] = i;
    for(int i = m + n; i >= 1; i --) {
        int a = e[i].a, b = e[i].b;
        ll c = e[i].c;
        int pa = find(a), pb = find(b);
        if(pa != pb) {
            p[pa] = pb;
            ans2 += c;
        }
    }
    printf("%lld\n", max(tmp - ans1, ans2 - tmp));
    return 0;
}
posted @ 2022-03-15 15:21  pbc的成长之路  阅读(30)  评论(0编辑  收藏  举报