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