题解 CF1095F 【Make It Connected】
Description
给定
Input
第一行是两个整数,分别是点数
下面一行
下面
Output
输出一行一个整数,最小代价
Hint
设第
保证特殊边的参数
Solution
显然这个题目是要求一个 MST (最小生成树),但是
我们考虑克鲁斯卡尔算法的本质:
有若干个联通块,每次寻找联通块间权值最小的边将之连结。
考虑对于本题来说,在不考虑特殊边的情况下,连结两个联通块,显然要分别选择两个联通块内点权最小的点连结。于是我们对每个集合维护点权最小的点,每次取出点权前两小的集合进行连边即可。维护点权前两小的集合显然可以用一个堆做。
考虑有特殊边怎么办:
我们把特殊边排序,每次比较当前的特殊边的权值小还是前两个联通块的点权小,选择更小的合并。
注意处理一下当前选出的两个点在一个集合中的情况。
因为一共要做
Code
#include <cstdio>
#include <queue>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
template <typename T>
inline void ReadDb(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
if (ch == '.') {
ch = IPT::GetChar();
double base = 1;
while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
}
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
rg int top=0;
do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 200010;
const int maxm = 400010;
struct Edge {
int from, to;
ll v;
inline bool operator<(const Edge &_others) const {
return this->v < _others.v;
}
};
Edge edge[maxm];
int n, m;
int ufs[maxn], vec[maxn], rk[maxn];
ll ans;
ll MU[maxn];
struct Zay {
int id;
ll v;
inline bool operator<(const Zay &_others) const {
return this->v > _others.v;
}
};
std::priority_queue<Zay>Q;
int find(ci x) {return ufs[x] == x ? x : ufs[x] = find(ufs[x]);}
int main() {
freopen("1.in", "r", stdin);
qr(n); qr(m);
for (rg int i = 1; i <= n; ++i) qr(MU[i]);
for (rg int i = 1; i <= m; ++i) {
qr(edge[i].from); qr(edge[i].to); qr(edge[i].v);
}
std::sort(edge + 1, edge + 1 + m);
edge[m + 1].v = 1ll << 62;
for (rg int i = 1; i <= n; ++i) ufs[i] = i, vec[i] = i, rk[i] = 1, Q.push((Zay){i, MU[i]});
for (rg int i = 1, cur = 1; i < n; ++i) {
while ((cur <= m) && (find(edge[cur].from) == find(edge[cur].to))) ++cur;
Zay t1 = Q.top(); Q.pop(); Zay t2 = Q.top(); Q.pop();
while (find(t1.id) == find(t2.id)) {t2 = Q.top(); Q.pop();}
if ((t1.v + t2.v) <= edge[cur].v) {
int fa = find(t1.id), fb = find(t2.id);
ans += t1.v + t2.v;
if (rk[fa] < rk[fb]) ufs[fb] = fa;
else if (rk[fa] > rk[fb]) ufs[fa] = fb;
else ufs[fa] = fb, ++rk[fb];
Q.push((Zay){find(fa), t1.v});
vec[find(fa)] = vec[fa];
} else {
int fa = find(edge[cur].from), fb = find(edge[cur].to);
ans += edge[cur].v;
if (rk[fa] < rk[fb]) ufs[fb] = fa;
else if (rk[fa] > rk[fb]) ufs[fa] = fb;
else ufs[fa] = fb, ++rk[fb];
Q.push((Zay){find(fa), MU[vec[fa]]});
vec[find(fa)] = vec[fa];
Q.push(t1); Q.push(t2);
}
}
qw(ans, '\n', true);
return 0;
}