THUPC2022初赛 A.最小公倍树 解题报告
给定一个点编号在
[L,R] 范围内的完全图,边(u,v) 的权值为\mathrm{lcm}(u, v) ,请你求出这张图的最小生成树权值和。
L,R \leq 10^6,R - L \leq 10^5
解题思路:
Kruskal 优化建图。
观察到如果直接建图的时间复杂度是
首先我们有
这实际上启发了我们从
而我们可以枚举因子,因子的范围是
时间复杂度
代码:
const int N = 1e6 + 10;
int l,r,f[N],buc[N];
bool vis[N];
ll gcd(ll a,ll b) {
return !b ? a : gcd(b,a%b);
}
ll lcm(ll a,ll b) {
return a / gcd(a,b) * b;
}
int find(int x) {return f[x] == x ? x : f[x] = find(f[x]);}
struct node {
int l,r;
ll w;
friend inline bool operator < (const node &a,const node &b) {
return a.w < b.w;
}
};
vector <node> edge;
void Kruskal() {
sort(edge.begin(),edge.end());
int cnt = 0;
ll ans = 0;
for (auto e : edge) {
int x = e.l,y = e.r;ll w = e.w;
if (find(x) != find(y)) {
//printf("%d %d %lld\n",x,y,w);
f[max(find(x),find(y))] = min(find(x),find(y));
ans += w;
}
}
printf("%lld\n",ans);
}
signed main() {
read(l,r);
ll ans = 0;
for (int i = l;i <= r;++i) f[i] = i,buc[i] = 1;
for (int i = 2;i <= r;++i) {
//if (vis[i]) continue;
int cnt = 0,fis = 0;
for (int j = i;j <= r;j += i) {
if (buc[j] && !fis) fis = j;
if (buc[j]) edge.push_back((node){fis,j,lcm(fis,j)});
vis[j] = 1;
}
if (i >= l) edge.push_back((node){fis,l,lcm(fis,l)});
}
Kruskal();
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。