小红的图上删边

文章讲述了如何通过计算数字末尾0的数量来解决D-小红图上的删边问题,利用2和5的特性简化乘法运算,并结合Kruskal算法求得最小生成树,最后输出不在最小生成树中的边权之和作为答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D-小红的图上删边_牛客周赛 Round 22 (nowcoder.com) 

这道题的本质是最小生成树,路径的权值是两端点乘积的末尾0的个数,如果把两数直接乘起来会越界,看了几个大佬的题解,发现他们对2取模,对5取模

思考后发现

两个个位数相乘只有2 * 5, 4 * 5, 8 * 5 会在末尾产生0,而2是4和8的因数,那也就意味着一个数能拆出几对{2, 5}, 那这个数末尾就有几个0

若a * b = c, 则a 拆出2的个数加上b拆出2的个数就是c拆出2的个数, 5 同理

计算边权时数字越界的问题就解决了

再利用kruskal算法解决最小生成树问题,那些没有在最小生成树里的边权之和就是答案

#include <iostream>
#include <queue>
#include <string>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;

#define ll unsigned long long
#define INF 0x7fffffff
typedef pair<int, int> PII;

struct Node {
	int x, y, v;
};
int n, m;
ll w[100005];
int fa[100005];
Node edge[100005];
PII fun_(ll a) {
	PII r;
	while (a % 2 == 0 && a > 0) {
		r.first++;
		a /= 2;
	}
	while (a % 5 == 0 && a > 0) {
		r.second++;
		a /= 5;
	}
	return r;
}
int fun(ll a, ll b) {
	PII p = fun_(a), q = fun_(b);
	return min((p.first + q.first), (p.second + q.second));
}
int findfa(int x) {
	if (fa[x] == x) return x;
	return fa[x] = findfa(fa[x]);
}
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> w[i];
	for (int i = 0; i < m; i++) {
		cin >> edge[i].x >> edge[i].y;
		edge[i].v = fun(w[edge[i].x], w[edge[i].y]);
	}
	sort(edge, edge + m, [](Node x, Node y)->bool {
		return x.v < y.v;
		});
	int ans = 0;
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 0; i < m; i++) {
		int x = findfa(edge[i].x), y = findfa(edge[i].y), v = edge[i].v;
		if (x == y) ans += v;
		else fa[x] = y;
	}
	cout << ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云儿乱飘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值