第八届广西大学生程序设计大赛暨2025邀请赛 L题思路分享(数论,最小生成树)
https://ac.nowcoder.com/acm/contest/110811/L
题意概述
给定一张 \(n\) 个点的完全图,每个点有点权 \(a_i\)。\(u\)-\(v\) 的边权为 \(gcd(a_i+a_j)+lcm(a_i+a_j)\),求图的最小生成树的边权和。
\(1\le n \le 10^5\),\(1 \le a_i \le 10^6\)。
思路
考虑枚举最大公因数 \(d\),找到所有点权为 \(d\) 的倍数的点。在所有满足条件的点中,将所有点与点权最小的点连边即可。
记值域为 \(V\),时间复杂度和边的数量都是调和级数级别,连完边后跑 \(kruskal\) 即可。
时间复杂度 \(\mathcal{O}(V\log V)\)。
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
class dsu{
public:
int n,cnt_cc;
vector<int> p,sz;
dsu(int _n){
n = _n;
cnt_cc = n;
p = vector<int>(n+1);
for (int i=1;i<=n;i++){
p[i] = i;
}
sz = vector<int>(n+1,1);
}
int find(int x){
int root = x;
while (p[root]!=root) root = p[root];
while (x!=root){
int next = p[x];
p[x] = root;
x = next;
}
return root;
}
void unite(int a,int b){
a = find(a);
b = find(b);
if (a==b) return;
if (sz[a]>=sz[b]){
sz[a] += sz[b];
p[b] = a;
}
else{
sz[b] += sz[a];
p[a] = b;
}
cnt_cc--;
}
};
const int MAXN = 1e6+5;
vector<int> V[MAXN];
void solve(){
int n;
cin >> n;
vector<int> a(n+1);
for (int i=1;i<=n;i++){
cin >> a[i];
V[a[i]].push_back(i);
}
vector<array<ll,3>> eds;
for (int d=1;d<MAXN;d++){
int x = d;
while (x<MAXN && V[x].empty()){
x += d;
}
int first = V[x].front();
while (x<MAXN){
for (auto& v:V[x]){
if (v!=first){
eds.push_back({d+(ll)x*a[first]/d,first,v});
}
}
x += d;
}
}
sort(eds.begin(),eds.end());
dsu ds(n);
ll res = 0;
for (auto& [w,u,v]:eds){
if (ds.find(u)!=ds.find(v)){
res += w;
ds.unite(u,v);
}
}
cout << res << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号