招募军队(最小生成树)
题意
有\(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;
}