题意:
给出n,m,表示又m - n + 1个点的编号从n开始到m结束,两个点之间的权值为编号的最小公倍数,然后求最小生成树。
题解:
因为两个数最小公倍数在最小的情况下,是等于这两个数中较大的那一个数。所以可以贪心地对于一个节点a,连上它的倍数。
有一些情况:
①n = 4 m = 10的时候,因为最后会剩下一个9没有数和它相连,最好的情况下是与6连,从小到大枚举9的因数然后在乘以一个更小的因数使得在n~m的范围。
②n = 2 m = 10的时候,2连向了它所有的倍数,3要连向与2相连的6,最后会剩下以2,5,7为根的子树,现在需要合并子树因为都是素数不是第一种情况的合数,那么就直接合并好了。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e6 + 7;
int n, m, prime[N], cnt;
bool isprime[N], vis[N];
LL ans;
void ListofPrime ()
{
for (int i = 2; i <= 1000000; ++i) isprime[i] = true;
for (int i = 2; i <= 1000000; ++i)
{
if (!isprime[i]) continue;
prime[++cnt] = i;
for (int j = 2 * i; j <= 1000000; j += i) isprime[j] = false;
}
}
int main ()
{
scanf ("%d%d", &n, &m);
if (n == m) {
puts("0");
return 0;
}
if (n == 1) {
printf ("%lld\n", (LL)(1 + m) * m / 2 - 1);
return 0;
}
ListofPrime();
for (int i = n; i <= m; ++i)
{
if (vis[i]) continue;
int flag = 0;
for (int j = 2 * i; j <= m; j += i)
{
if (!vis[i] || !vis[j])
{
if (vis[j]) flag = 1;
vis[i] = vis[j] = true;
ans = ans + j;
}
}
if (vis[i] && i != n && !flag) ans = ans + i * n;
}
for (int i = n; i <= m; ++i)
{
if (vis[i]) continue;
if (isprime[i]) ans = ans + i * n;
else
{
for (int j = 2; j <= cnt; ++j)
{
if (i % prime[j] != 0) continue;
for (int k = 1; k < j; ++k)
{
int xi = i / prime[j] * prime[k];
if (xi >= n && xi <= m)
{
ans = ans + i * prime[k];
break;
}
}
break;
}
}
}
cout << ans << endl;
return 0;
}
总结:
写代码之前需要先考虑好所有的情况,不然就会陷入debug的无限死循环QAQ