CF1095F Make It Connected

Problem

给你 \(n\) 个点,每个点有一个权值 \(c_i\),已知连接 \(i,j\) 两点的代价为 \(c_i+c_j\),现在还有其他的 \(m\) 种连接方法,连接 \(x,y\) 的费用为 \(w\)

求出让这个图连通的最小代价。

\(1 \le n \le 2 \times 10^5,0 \le m \le 2 \times 10^5,1 \le c_i \le 10^{12}\)

$ 1\le x,y\le n,1 \le w \le 10^{12}$

Input

第一行两个整数 \(n,m\)
第二行 \(n\) 个整数,第 \(i\) 个整数表示 \(c_i\)
接下来 \(m\) 行,每行三个整数 \(x,y,w\),表示一种连接方法。

Output

一行一个整数表示最小代价。

Sample

Input 1

3 2
1 3 3
2 3 5
2 1 1

Output 1

5

Input 2

4 0
1 3 3 7

Output 2

16

Input 3

5 4
1 2 3 4 5
1 2 8
1 3 10
1 4 7
1 5 15

Output 3

18

Solution

首先总边数是 \(\dfrac{n\times (n-1)}{2} + m\) 的,数量级太大,无法实现。

我们发现完全图中共有 \(\dfrac{n\times (n-1)}{2}\) 条边,但实际上可能对答案产生贡献的是权值最小的前 \(n-1\) 条边。

那如何找到这 \(n-1\) 条边呢?

考虑将权值排序,则有 \(c_1 \leq c_2 \leq \cdots c_n\),不难发现将一号节点与剩下 \(n-1\) 个节点连出的边即为权值最小的前 \(n-1\) 条边,证明如下。

\(\forall x,y (x \neq 1,y \neq 1)\),一定有 \(max(c_1+c_x ,c_1+c_y)\leq c_x + c_y\),所以选完 \((1,x)\)\((1,y)\) 两条边后 \(x,y\) 处于同一连通块,两者间不会再连边。

最后将这些边与特殊边共 \(n+m-1\) 条边跑最小生成树算法即可。

代码:

#include <bits/stdc++.h>

using namespace std;

const int kmax = 2e5 + 5;

struct E {
  long long x, y, w;
} a[kmax * 2];

long long f[kmax], ans, v[kmax];
int n, m, c, g = 1;

int F(int x) {
  return x == f[x] ? x : f[x] = F(f[x]);
}

void U(int x, int y) {
  int fx = F(x), fy = F(y);
  if (fx != fy)
    f[fx] = fy;
  return;
}

int main() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    cin >> v[i];
    f[i] = i;
    g = (v[g] > v[i] ? i : g); // 找权值最小值
  }
  for (int i = 1; i <= m; i++) {
    cin >> a[i].x >> a[i].y >> a[i].w;
  }
  for (int i = 1; i <= n; i++) {
    if (g != i) { // 不能与自己连
      a[i + m].x = g;
      a[i + m].y = i;
      a[i + m].w = v[i] + v[g]; // 建边
    }
  }
  sort(a + 1, a + m + n + 1, [](E p, E q) { return p.w < q.w; });
  for (int i = 1; i <= m + n; i++) {
    int fx = F(a[i].x);
    int fy = F(a[i].y);
    if (fx != fy) {
      c++, ans += a[i].w; // 累加答案
      U(fx, fy); // 合并
    }
  }
  cout << ans;
  return 0;
}
posted @ 2023-05-10 12:10  ereoth  阅读(18)  评论(0)    收藏  举报