题解 SP4168 【SQFREE - Square-free integers】
解题思路
有平方数因数,等同于有两个相同的质因子,由此联想到一个常见的函数——莫比乌斯函数。
先来看它的定义:
注意到,仅当
更一般地,我们可以把该函数取个平方,令
这样,答案就可以直接表示为
于是我们换个角度,考虑在
显然是
例如
怎么办呢?加加减减弄对呗!
可以发现,当一个数分别是两个质数代表的平方数的倍数,它肯定也是那两个质数的积,代表的平方数的倍数,所以我们可以利用这个“积”来去掉重复计算的部分。我们设
但上面的讨论漏掉了有平方数因数的贡献,这样弄出来对吗?
经过计算,这样是对的!我们宁可让有平方数因数的数没有贡献!
综上所述,我们只让质因子互不相同的数有贡献,贡献的正负取决于其质因子数量的奇偶性。
等等,这不就是 好神奇啊。
但这个式子还是
再考虑优化,
最终时间复杂度是
代码实现
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
const int maxT = 105, maxSqrt = 10000005;
int t, m, sqrtN, pri[maxSqrt / 10], mu[maxSqrt], sum[maxSqrt];
long long n, ans, q[maxT];
bool vis[maxSqrt];
void init() {
mu[1] = sum[1] = 1;
for (int i = 2; i <= sqrtN; i++) {
if (!vis[i]) { pri[++m] = i; mu[i] = -1; }
for (int j = 1; j <= m; j++) {
int k = i * pri[j];
if (k > sqrtN) { break; } vis[k] = true;
if (i % pri[j] == 0) { break; } mu[k] = -mu[i];
}
sum[i] = sum[i - 1] + mu[i];
}
}
int main() {
scanf("%d", &t);
for (int i = 1; i <= t; i++) { scanf("%lld", &q[i]); n = std::max(n, q[i]); }
sqrtN = sqrt(n); init(); // 线性求莫比乌斯函数, 前缀和.
for (int i = 1; i <= t; i++) {
n = q[i]; ans = 0;
for (long long l = 1, r; l <= n; l = r + 1) {
if (n / (l * l) == 0) { break; }
r = sqrt(n / (n / (l * l))); ans += n / (l * l) * (sum[r] - sum[l - 1]);
}
printf("%lld\n", ans);
}
return 0;
}