[ABC270F] Transportation

[ABC270F] Transportation

题意

\(n\) 个点,有 \(m\) 条可以加上的边,如果两个点同时建立了一种东西,那么也算连了一条边,每条边都有个代价,每个点建一个东西也有不同的代价,问想要让图连通,最少需要多少代价。

思路

显然是最小生成树,但是由于可以见两种东西,所以比较难处理,所以可以将这个点权变成一条边,这样就好处理了,所以可以额外开两个点 \(n + 1\)\(n + 2\),可以将所有点的两种建东西的代价作为边权连向它们,但可以不建东西,所以做四次最小生成树即可。

代码

#include <algorithm>
#include <iostream>

using namespace std;

const int MaxN = 2e5 + 10;

struct S {
  int u, v, w;

  bool operator<(const S &j) const {
    return w < j.w;
  }
} a[MaxN + 2 * MaxN];

int n, m, tot;
long long fa[MaxN], ans = 1e18;
bool vis[MaxN];

int FindFather(int x) {
  return fa[x] < 0 ? x : fa[x] = FindFather(fa[x]);
}

void insert(int x, int y) {
  x = FindFather(x), y = FindFather(y);
  if (fa[x] < fa[y]) {
    swap(x, y);
  }
  fa[y] += fa[x], fa[x] = y;
}

int main() {
  cin >> n >> m;
  for (int i = 1, u; i <= n; i++) {
    cin >> u;
    a[++tot] = {i, n + 1, u};
  }
  for (int i = 1, u; i <= n; i++) {
    cin >> u;
    a[++tot] = {i, n + 2, u};
  }
  for (int i = 1, u, v, w; i <= m; i++) {
    cin >> u >> v >> w;
    a[++tot] = {u, v, w};
  }
  sort(a + 1, a + tot + 1);
  for (int u = 0; u < 4; u++) {
    fill(fa + 1, fa + n + 4, -1);
    fill(vis + 1, vis + n + 4, 0);
    long long sum = 0;
    for (int i = 1; i <= tot; i++) {
      if ((a[i].v == n + 1 && ((u >> 1) & 1) != 1) || (a[i].v == n + 2 && (u & 1) != 1)) {
        continue;
      }
      if (FindFather(a[i].u) != FindFather(a[i].v)) {
        sum += a[i].w, vis[a[i].u] = vis[a[i].v] = 1;
        insert(a[i].u, a[i].v);
      }
    }
    int cnt = 0;
    for (int i = 1; i <= n + 4; i++) {
      cnt += vis[i];
    }
    if (u == 0) {
      if (cnt != n) {
        sum = 1e18;
      }
    } else if (u == 1 || u == 2) {
      if (cnt != n + 1) {
        sum = 1e18;
      }
    } else if (u == 3) {
      if (cnt != n + 2) {
        sum = 1e18;
      }
    }
    ans = min(ans, sum);
  }
  cout << ans << endl;
  return 0;
}
posted @ 2023-05-17 20:39  yabnto  阅读(80)  评论(0编辑  收藏  举报