题意:给定n-1个点,编号从2到n,两点a和b之间的边权重为lcm(a,b)。请找出它们形成的最小生成树。 (2<=n<=10000000)
思路:
- 要让lcm最小,容易想到一种是包含关系,即lcm(a, b)==a, 让每个数向其约数连边。对于没有约数的质数,将其与2相连是最优的,因为在> 不能包含的情况下,最小的倍数就是2倍。
- 总的边权和为(质数的和×2+合数的和),可以用线性筛法预处理出前缀和,复杂度O(n)。
accode:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e7 + 10;
#define ios std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
ll v[maxn], p[maxn], cnt = 0, s[maxn];
inline void init()
{
memset(s, 0, sizeof(s));
//线性筛
for(int i = 2; i < maxn; i++)
{
if(v[i] == 0)
p[++cnt] = i;
for(int j = 1; j <= cnt; j++)
{
if(i * p[j] >= maxn)
break;
v[i*p[j]] = 1;
if(i % p[j] == 0)
break;
}
}
//前缀和
for(int i = 3; i < maxn; i++)
{
if(v[i] == 0)//是质数即加上两倍的这个数
s[i] = s[i-1] + 2 * i;
else s[i] = s[i-1] + i;//是合数,直接相加
}
}
int main()
{
ios;
init();
int T;
scanf("%d", &T);
while(T--)
{
ll n;
scanf("%lld", &n);
printf("%lld\n", s[n]);
}
return 0;
}