Transportation(最小生成树,虚拟节点)

题意

\(N\)个岛屿,现在需要将它们联通起来。

可以选择在岛上建立飞机场,花费为\(X_i\);也可以在岛上建立港口,花费为\(Y_i\);还可以在两个岛屿\(A_i\)\(B_i\)之间修路,花费为\(Z_i\)

如果两个岛上都有飞机场,则两者可以联通;如果两个岛上都有港口,则两者可以联通;如果两个岛之间有路,则两者可以联通。

两个岛之间也可以通过其他岛间接联通。

求使得所有岛联通的最小花费是多少。

题目链接:https://atcoder.jp/contests/abc270/tasks/abc270_f

数据范围

\(2 \leq N \leq 2 \times 10^5\)
\(1 \leq M \leq 2 \times 10^5\)
\(1 \leq X_i, Y_i, Z_i \leq 10^9\)

思路

如果只能修路的话,问题就可以转换为一个最小生成树问题。那么加上前两种方式应该怎么处理呢?

考虑建立两个虚拟节点,第一个虚拟节点向每个节点(除了两个虚拟节点)连权值为\(X_i\)的边,第二个虚拟节点向每个节点(除了两个虚拟节点)连权值为\(Y_i\)的边。

如果有至少一个岛需要修建飞机场,则第一个虚拟节点就要被联通;如果有至少一个岛需要修建港口,则第二个虚拟节点就要被联通。

因此我们跑4次Kruskal算法:不连通两个虚拟节点,只联通第一个虚拟节点,只联通第二个虚拟节点,两个虚拟节点都联通。然后取4者中的最小值即可。

代码

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

using namespace std;

typedef long long ll;

const int N = 200010, M = 10 * N;

int n, m;
int p[N];
ll X[N], Y[N];
struct Edge
{
    int u, v;
    ll w;

    bool operator < (const Edge &t) const {
        return w < t.w;
    }
}a[M], b[M], c[M], d[M];

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

ll kruskal(struct Edge a[], int len, int num)
{
    for(int i = 1; i <= n + 2; i ++) p[i] = i;
    ll res = 0;
    int cnt = 0;
    sort(a + 1, a + len + 1);
    for(int i = 1; i <= len; i ++) {
        int u = a[i].u, v = a[i].v;
        ll w = a[i].w;
        int pu = find(u), pv = find(v);
        if(pu != pv) {
            res += w;
            p[pu] = pv;
            cnt ++;
        }
    }
    if(cnt < num - 1) res = 1e18;
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) scanf("%lld", &X[i]);
    for(int i = 1; i <= n; i ++) scanf("%lld", &Y[i]);
    for(int i = 1; i <= m; i ++) {
        int u, v;
        ll w;
        scanf("%d%d%lld", &u, &v, &w);
        a[i] = {u, v, w}, b[i] = {u, v, w}, c[i] = {u, v, w}, d[i] = {u, v, w};
    }
    for(int i = 1; i <= n; i ++) {
        b[m + i] = {n + 1, i, X[i]};
        c[m + i] = {n + 2, i, Y[i]};
        d[m + i] = {n + 1, i, X[i]};
    }
    for(int i = 1; i <= n; i ++) {
        d[m + n + i] = {n + 2, i, Y[i]};
    }
    ll ans = kruskal(a, m, n);
    ans = min(ans, kruskal(b, m + n, n + 1));
    ans = min(ans, kruskal(c, m + n, n + 1));
    ans = min(ans, kruskal(d, m + 2 * n, n + 2));
    printf("%lld\n", ans);
    return 0;
}
posted @ 2022-10-08 15:11  pbc的成长之路  阅读(117)  评论(0编辑  收藏  举报