【生成树,堆】【CF1095F】 Make It Connected

Description

给定 n 个点,每个点有点权,连结两个点花费的代价为两点的点权和。另外有 m 条特殊边,参数为 x,y,z。意为如果你选择这条边,就可以花费 z 的代价将点 x 和点 y 连结起来,当然你也可以不选择这条边。求使整个图联通的最小代价

Input

第一行是两个整数,分别是点数 n 和特殊边的数量 m

下面一行 n 个数,第 i 个数代表点 i 的点权

下面 m 行,每行三个数 x,y,z,代表一条特殊边

Output

输出一行一个整数,最小代价

Hint

1  n  2 × 105 ,0  m  2 × 105

设第 i 个点的点权为 ai,第 j 条特殊边的边权为 zj,保证

1  ai,zj  1012

保证特殊边的参数 x  y

Solution

显然这个题目是要求一个 MST (最小生成树),但是 O(n2) 建图显然不可取。

我们考虑克鲁斯卡尔算法的本质:

有若干个联通块,每次寻找联通块间权值最小的边将之连结。

考虑对于本题来说,在不考虑特殊边的情况下,连结两个联通块,显然要分别选择两个联通块内点权最小的点连结。于是我们对每个集合维护点权最小的点,每次取出点权前两小的集合进行连边即可。维护点权前两小的集合显然可以用一个堆做。

考虑有特殊边怎么办:

我们把特殊边排序,每次比较当前的特殊边的权值小还是前两个联通块的点权小,选择更小的合并。

注意处理一下当前选出的两个点在一个集合中的情况。

因为一共要做 O(n) 次,每次会有 O(1) 次对堆的插入和删除操作,于是复杂度 O(nlogn)

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;
}
posted @   一扶苏一  阅读(292)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示