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;
}

浙公网安备 33010602011771号