题目
(来源:New Online OJ,可以在这个 OJ 上评测该题,也可在 洛谷 P9238 上提交评测)
题目描述
给定
n
n
n 个按顺序摆好的硬币,一开始只有第
1
1
1 个硬币朝下,其他硬币均朝上。
你每次操作可以选择任何一个整数
i
i
i 并将所有满足
j
m
o
d
i
=
0
j \mod i = 0
jmodi=0 的位置
j
j
j 的硬币翻转。
求最少需要多少次操作可以让所有硬币都朝上。
输入、输出格式
输入一行包含一个整数 n n n。
输出一行包含一个整数表示最少需要的操作次数。
数据范围
- 对于 30% 的评测用例, n ≤ 5 × 1 0 6 n \le 5 \times 10^6 n≤5×106;
- 对于 70% 的评测用例, n ≤ 1 0 9 n \le 10^9 n≤109;
- 对于 100% 的评测用例, 1 ≤ n ≤ 1 0 18 1 \le n \le 10^{18} 1≤n≤1018。
样例
样例1
输入
7
输出
6
样例2
输入
1131796
输出
688042
前置知识点
本题是数论题,需要对初等数论以下几个知识有所了解:
- Möbius 函数 μ ( n ) \mu(n) μ(n)
- Dirichlet 卷积 f ∗ g f * g f∗g
- 数论分块
- Euler 筛(线性筛)求数论函数的值
- 杜教筛求数论函数前缀和
以上知识点的学习可参考 OI Wiki。
题解
分析
题意分析
题意即每次选择一个数 i i i,将 i i i 的全部倍数翻转。容易看出,一个数翻转两次等价于不翻转,所以每个数至多被选择一次。
同时可以看出,每个硬币只可能被它的因数(包括它本身)翻转,例如硬币 12 只能通过 1, 2, 3, 4, 6, 12 翻转。
初始时硬币 1 朝下,因为 1 的因数只有它自己,所以必须翻转 1 的倍数,此时除 1 之外所有硬币朝下。
然后考虑翻转编号为质数 p p p 的硬币, p p p 的因数只有 1 1 1 和 p p p, 1 1 1 已经选择过,不可能再选择一次,所以必定选择一次质数 p p p 以翻转。
然后考虑 n = p 1 p 2 n = p_1 p_2 n=p1p2,也就是可以分解为两个质数乘积的合数。 n n n 已经被 1 , p 1 , p 2 1, p_1, p_2 1,p1,p2 翻转一次,剩余的因数只有 n n n 本身,所以必定选择 n n n。
继续考虑 n = p 1 p 2 p 3 n = p_1 p_2 p_3 n=p1p2p3, n n n 已经被 1 , p 1 , p 2 , p 3 , p 1 p 2 , p 1 p 3 , p 2 p 3 1, p_1, p_2, p_3, p_1 p_2, p_1 p_3, p_2 p_3 1,p1,p2,p3,p1p2,p1p3,p2p3 翻转,剩余的因数同样只有 n n n 本身,所以必定选择 n n n。
以此类推,所有可以分解为若干个互异质因数之积的整数 n = p 1 p 2 ⋯ p k n = p_1 p_2 \cdots p_k n=p1p2⋯pk 都需要选择一次。这样的 n n n (包括 1 1 1)被称为 无平方因子数 。
下面证明除此以外的其他数(即有平方因子数)都已经翻转到正面。设有平方因子数 n n n 的所有互异质因数为 p 1 , … , p k p_1, \dots, p_k p1,…,pk,容易看出 n ≠ p 1 ⋯ p k n \ne p_1 \cdots p_k n=p1⋯pk(否则和有平方因子矛盾,有平方因子代表存在质数 p p p 满足 p 2 ∣ n p^2 \mid n p2∣n),则 n n n 已经被 1 , p 1 , ⋯ , p k , p 1 p 2 , ⋯ , p k − 1 p k , ⋯ , p 1 ⋯ p k 1, p_1, \cdots, p_k, p_1 p_2, \cdots, p_{k - 1} p_k, \cdots, p_1\cdots p_k 1,p1,⋯,pk,p1p2,⋯,pk−1pk,⋯,p1⋯pk 无平方因子数筛去。 n n n 的因数中,无平方因子数的个数为 2 k 2^k 2k(等价于 { p 1 , … , p k } \{p_1, \dots, p_k\} {p1,…,pk} 的子集数), 2 k 2^k 2k 是偶数,所以硬币 n n n 已经被翻转了偶数次,依然是正面。
因此只需要统计 [ 1 , n ] [1, n] [1,n] 中的无平方因子数个数。无平方因子数的 Möbius 函数值为 ± 1 \pm 1 ±1,其他数为 0,因此问题等价于求 Möbius 函数的绝对值(或平方)的前缀和:
∑ i = 1 n μ 2 ( k ) \sum_{i = 1}^n \mu^2(k) i=1∑nμ2(k)
前缀和性质
首先证明 Möbius 函数平方的一个重要性质:
μ 2 ( n ) = ∑ d 2 ∣ n μ ( d ) . \mu^2(n) = \sum_{d^2 \mid n} \mu(d). μ2(n)=d2∣n∑μ(d).
证明:设 n n n 的标准分解式为 p 1 α 1 ⋯ p k α k p_1^{\alpha_1} \cdots p_k^{\alpha_k} p1α1⋯pkαk,则 d 2 ∣ n d^2 \mid n d2∣n 说明 d d d 的标准分解式只能包含次数 α i ≥ 2 \alpha_i \ge 2 αi≥2 的质因数 p i p_i pi。
设
I
(
k
)
=
[
k
=
1
]
I(k) = [k = 1]
I(k)=[k=1],
n
′
=
p
1
⌊
α
1
/
2
⌋
⋯
p
k
⌊
α
k
/
2
⌋
n' = p_1^{\lfloor \alpha_1 / 2 \rfloor} \cdots p_k^{\lfloor \alpha_k / 2 \rfloor}
n′=p1⌊α1/2⌋⋯pk⌊αk/2⌋,则
d
∣
n
′
d \mid n'
d∣n′,于是
∑
d
2
∣
n
μ
(
d
)
=
∑
d
∣
n
′
μ
(
d
)
=
I
(
n
′
)
\sum_{d^2 \mid n} \mu(d) = \sum_{d \mid n'} \mu(d) = I(n')
d2∣n∑μ(d)=d∣n′∑μ(d)=I(n′)
而 I ( n ′ ) = 1 I(n') = 1 I(n′)=1 则说明 n ′ = 1 n' = 1 n′=1, ⌊ α 1 / 2 ⌋ = ⋯ = ⌊ α k / 2 ⌋ = 0 \lfloor \alpha_1 / 2 \rfloor = \cdots = \lfloor \alpha_k / 2 \rfloor = 0 ⌊α1/2⌋=⋯=⌊αk/2⌋=0,也就是 α 1 = ⋯ = α k = 1 \alpha_1 = \cdots = \alpha_k = 1 α1=⋯=αk=1,对应地 μ 2 ( n ) = 1 \mu^2(n) = 1 μ2(n)=1。所以 I ( n ′ ) = μ 2 ( n ) I(n') = \mu^2(n) I(n′)=μ2(n),命题证毕。
然后据此推导 Möbius 函数平方的一个重要前缀和性质:
∑
k
=
1
n
μ
2
(
k
)
=
∑
k
=
1
⌊
n
⌋
⌊
n
k
2
⌋
μ
(
k
)
.
\sum_{k = 1}^n \mu^2(k) = \sum_{k = 1}^{\lfloor \sqrt{n} \rfloor} \left \lfloor \frac{n}{k^2} \right \rfloor \mu(k).
k=1∑nμ2(k)=k=1∑⌊n⌋⌊k2n⌋μ(k).
证明:将 μ 2 ( k ) \mu^2 (k) μ2(k) 用上一定理展开,然后交换求和顺序即可。交换求和顺序的方法参考自 https://zhuanlan.zhihu.com/p/499839696 。
∑ k = 1 n ∑ d 2 ∣ k μ ( d ) = ∑ d = 1 ⌊ n ⌋ ∑ d 2 ∣ k , k ≤ n μ ( d ) = ∑ d = 1 ⌊ n ⌋ μ ( d ) ∑ d 2 ∣ k , k ≤ n 1 = ∑ d = 1 ⌊ n ⌋ μ ( d ) ⌊ n d 2 ⌋ . \begin{aligned} \sum_{k = 1}^n \sum_{d^2 \mid k} \mu(d) &= \sum_{d = 1}^{\lfloor \sqrt{n} \rfloor} \sum_{d^2 \mid k, k \le n} \mu(d) \\ &= \sum_{d = 1}^{\lfloor \sqrt{n} \rfloor} \mu(d) \sum_{d^2 \mid k, k \le n} 1 \\ &= \sum_{d = 1}^{\lfloor \sqrt{n} \rfloor} \mu(d) \left \lfloor \frac{n}{d^2} \right \rfloor. \end{aligned} k=1∑nd2∣k∑μ(d)=d=1∑⌊n⌋d2∣k,k≤n∑μ(d)=d=1∑⌊n⌋μ(d)d2∣k,k≤n∑1=d=1∑⌊n⌋μ(d)⌊d2n⌋.
到这一步,如果使用 Euler 筛预处理出 [ 1 , n ] [1, \sqrt{n}] [1,n] 的 Möbius 函数值,然后直接计算上述和式,已经可以获得 70% 的分数(蓝桥杯是 OI 赛制)。但是,上述算法对 n = 1 0 18 n = 10^{18} n=1018 的情况依然会超时。
数论分块
我们需要进一步处理,继续降低 n n n 达到 1 0 18 10^{18} 1018 时的复杂度。观察到和式中包含了 ⌊ n d 2 ⌋ \left \lfloor \frac{n}{d^2} \right \rfloor ⌊d2n⌋,可以考虑使用数论分块。
数论分块 ∑ f ( k ) g ( ⌊ n k ⌋ ) \sum f(k) g \left( \left \lfloor \frac{n}{k} \right \rfloor \right) ∑f(k)g(⌊kn⌋) 要求能够预处理出 f ( n ) f(n) f(n) 的前缀和,这里 μ ( n ) \mu(n) μ(n) 的前缀和可以用杜教筛来快速计算。但这里 ⌊ n d 2 ⌋ \left \lfloor \frac{n}{d^2} \right \rfloor ⌊d2n⌋ 的形式和普通的数论分块并不一致,需要推导新的数论分块公式。
数论分块的关键是,对于给定的 l l l,确定 r = max { i ∈ Z : ⌊ n l 2 ⌋ = ⌊ n i 2 ⌋ } r = \max \{i \in \mathbb{Z}: \left \lfloor \frac{n}{l^2} \right \rfloor = \left \lfloor \frac{n}{i^2} \right \rfloor\} r=max{i∈Z:⌊l2n⌋=⌊i2n⌋} 的值。设 y = ⌊ n l 2 ⌋ y = \left \lfloor \frac{n}{l^2} \right \rfloor y=⌊l2n⌋,这里需要确定的就是满足 ⌊ n x 2 ⌋ = y \left \lfloor \frac{n}{x^2} \right \rfloor = y ⌊x2n⌋=y 的最大整数 x x x。容易看出 y ≤ n x 2 < y + 1 y \le \frac{n}{x^2} < y + 1 y≤x2n<y+1,因此 n y + 1 < x 2 ≤ n y \frac{n}{y + 1} < x^2 \le \frac{n}{y} y+1n<x2≤yn, x x x 的最大值即为 ⌊ n y ⌋ \left \lfloor \sqrt{\frac{n}{y}} \right \rfloor ⌊yn⌋。
因此,仿照普通的数论分块,这里的数论分块代码如下:
ll prefix_mu2(size_t n)
{
// lower_sqrt 表示开方后向下取整
size_t l = 1, r, bound = lower_sqrt(n);
ll ans = 0;
while (l <= bound)
{
r = lower_sqrt(n / (n / (l * l)));
ans += (n / (l * l)) * (prefix_mu(r) - prefix_mu(l - 1)); // prefix_mu 表示 Möbius 函数前缀和
l = r + 1;
}
return ans;
}
其中 lower_sqrt
函数可以用二分法实现。
μ \mu μ 前缀和 & 杜教筛
最后只需要计算 k ≤ 1 0 9 k \le 10^9 k≤109 时 μ ( k ) \mu(k) μ(k) 的前缀和即可。
可以预先用 Euler 筛求出 k ≤ 1 0 7 k \le 10^7 k≤107 的 μ \mu μ 值与前缀和,再用杜教筛处理 k > 1 0 7 k > 10^7 k>107 的前缀和。
复杂度分析
杜教筛复杂度
根据 OI-Wiki 杜教筛 的结论,若预处理出前 m α m^\alpha mα 的 μ \mu μ 前缀和,单次使用杜教筛计算 μ ( i ) \mu(i) μ(i) 的前 m m m 项和的时间复杂度上界为 O ( m 1 − α ) O(m^{1 - \alpha}) O(m1−α)。
这里由于 m ≤ 1 0 9 m \le 10^9 m≤109,至少应该预处理出前 1 0 6 10^6 106 的前缀和,否则单次使用杜教筛的时间复杂度将大于预处理复杂度。
数论分块的分块数
由于这里使用了新的数论分块技巧,仿照经典数论分块的方法,这里 ⌊ n d 2 ⌋ \left \lfloor \frac{n}{d^2} \right \rfloor ⌊d2n⌋ 的取值不会超过 2 n 1 / 3 2n^{1 / 3} 2n1/3 个数,从而分块数不会超过 2 n 1 / 3 2n^{1 / 3} 2n1/3,证明如下:
- 当 d ≤ n 1 / 3 d \le n^{1 / 3} d≤n1/3 时, ⌊ n d 2 ⌋ \left \lfloor \frac{n}{d^2} \right \rfloor ⌊d2n⌋ 至多有 n 1 / 3 n^{1 / 3} n1/3 个值;
- 当 d > n 1 / 3 d > n^{1 / 3} d>n1/3 时, n d 2 ≤ n 1 / 3 \frac{n}{d^2} \le n^{1 / 3} d2n≤n1/3,由于向下取整, ⌊ n d 2 ⌋ \left \lfloor \frac{n}{d^2} \right \rfloor ⌊d2n⌋ 同样至多有 n 1 / 3 n^{1 / 3} n1/3 个值。
每个分块都使用了二分法求开平方值,开平方的总时间复杂度是 O ( n 1 / 3 log n ) O(n ^ {1 / 3} \log n) O(n1/3logn)
总复杂度
在数论分块中,若预处理出前 n α n^\alpha nα 的 μ \mu μ 前缀和,则当 r ≤ n α r \le n^\alpha r≤nα 时,计算这个区间的 μ \mu μ 前缀和是常数复杂度;当 r > n α r > n^\alpha r>nα 时,则需要调用杜教筛进行计算。
这里设 α > 1 3 \alpha > \frac{1}{3} α>31(在杜教筛复杂度一节,已经至少预处理了 1 0 6 10^6 106),统计 l > n α l > n^\alpha l>nα 的区间个数。此时 ⌊ n l 2 ⌋ \left \lfloor \frac{n}{l^2} \right \rfloor ⌊l2n⌋ 的取值至多有 n 1 − 2 α n^{1 - 2\alpha} n1−2α 个,也就是至多有 n 1 − 2 α n^{1 - 2\alpha} n1−2α 个区间。这里每个区间都要调用一次杜教筛,复杂度为 O ( ( n ) 1 − 2 α ) = O ( n 1 / 2 − α ) O((\sqrt n)^{1 - 2\alpha}) = O(n^{1 / 2 - \alpha}) O((n)1−2α)=O(n1/2−α),所以总的时间复杂度为 O ( n 3 / 2 − 3 α ) O(n^{3 / 2 - 3\alpha}) O(n3/2−3α)。
由上式看出 α \alpha α 越接近 1 / 2 1/2 1/2 越好,但由于预处理前 n α n^{\alpha} nα 项的的空间复杂度为 O ( n α ) O(n^\alpha) O(nα), n = 1 0 18 n = 10^{18} n=1018 时会 MLE,所以并不能直接令 α = 1 / 2 \alpha = 1 / 2 α=1/2。考虑预处理前 1 0 7 10^7 107 项,则 α = 7 / 18 \alpha = 7 / 18 α=7/18,时间复杂度为 O ( n 3 / 2 − 3 × 7 / 18 ) = O ( n 1 / 3 ) O(n^{3 / 2 - 3 \times 7 / 18}) = O(n^{1 / 3}) O(n3/2−3×7/18)=O(n1/3)。结合前面的开平方复杂度,总的时间复杂度为 O ( n 1 / 3 log n ) O(n^{1 / 3} \log n) O(n1/3logn),恰好可以通过本题。
代码
汇总以上全部思想,完整的算法步骤如下:
- 用 Euler 筛预处理 k ≤ 1 0 7 k \le 10^7 k≤107 的 Möbius 函数值及其前缀和;
- 编写函数,使用杜教筛处理 k > 1 0 7 k > 10^7 k>107 的 Möbius 函数前缀和;
- 使用数论分块计算 μ 2 \mu^2 μ2 的前缀和。
#include <iostream>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using ll = long long;
constexpr size_t MAXN = 1e7;
constexpr size_t INF = 1e9;
ll mu[MAXN + 1], smu[MAXN + 1];
bool is_prime[MAXN + 1];
int primes[MAXN];
void preprocess()
{
// Euler seive
constexpr size_t n = MAXN;
size_t cnt = 0;
memset(is_prime + 1, 1, n);
is_prime[0] = is_prime[1] = false;
mu[1] = 1;
for (size_t i = 2; i <= n; i++)
{
if (is_prime[i])
{
primes[cnt++] = i;
mu[i] = -1;
}
for (size_t j = 0; j < cnt; j++)
{
if ((ll) i * primes[j] > n)
{
break;
}
is_prime[i * primes[j]] = false;
if (i % primes[j] == 0)
{
mu[i * primes[j]] = 0;
break;
}
else
{
mu[i * primes[j]] = -mu[i];
}
}
}
// Calculate the prefix sum
for (size_t i = 1; i <= n; i++)
{
smu[i] = smu[i - 1] + mu[i];
}
}
unordered_set<size_t> set; // Store the mu[i] that has been calculated
unordered_map<size_t, ll> fmu;
ll prefix_mu(size_t n)
{
if (n <= MAXN)
{
return smu[n];
}
else if (set.find(n) != set.end()) // This value has been calculated
{
return fmu[n];
}
// Dujiao seive
size_t l = 2, r;
ll ans = 1;
while (l <= n)
{
r = n / (n / l);
ans -= (r - l + 1) * prefix_mu(n / l);
l = r + 1;
}
set.insert(n);
fmu[n] = ans;
return ans;
}
// return the largest number x that x ^ 2 <= y
ll lower_sqrt(ll y)
{
size_t l = 1, r = INF;
ll ans;
while (l <= r)
{
size_t mid = (l + r) / 2;
if (mid * mid <= y)
{
ans = mid;
l = mid + 1;
}
else
{
r = mid - 1;
}
}
return ans;
}
// Calculate the result
ll prefix_mu2(size_t n)
{
size_t l = 1, r, bound = lower_sqrt(n);
ll ans = 0;
while (l <= bound)
{
r = lower_sqrt(n / (n / (l * l)));
ans += (n / (l * l)) * (prefix_mu(r) - prefix_mu(l - 1));
l = r + 1;
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
size_t n;
cin >> n;
preprocess();
cout << prefix_mu2(n) << '\n';
return 0;
}
扩展
在这个程序中,输入比较大的 n n n,可以发现 ∑ μ 2 ( i ) \sum \mu^2(i) ∑μ2(i) 总是约等于 n n n 的 0.6 倍,这背后是否有什么规律呢?
解析数论的研究内容之一就是数论函数的前 n n n 项平均的极限,即
lim n → ∞ 1 n ∑ k = 1 n f ( k ) . \lim_{n \to \infty} \frac{1}{n}\sum_{k = 1}^n f(k). n→∞limn1k=1∑nf(k).
在维基百科的 Square-free integer 条目中提到,
μ
2
\mu^2
μ2 的平均值的极限为
π
2
/
6
\pi^2 / 6
π2/6,即:
lim
n
→
∞
1
n
∑
k
=
1
n
[
μ
(
k
)
]
2
=
6
π
2
≈
0.6079.
\lim_{n \to \infty} \frac{1}{n}\sum_{k = 1}^n [\mu(k)]^2 = \frac{6}{\pi^2} \approx 0.6079.
n→∞limn1k=1∑n[μ(k)]2=π26≈0.6079.
通过这个结论也可以验证算法在 n n n 较大时输出的结果。
写在最后
本题解的主要思想来自知乎文章 https://zhuanlan.zhihu.com/p/521413862 和超理论坛讨论 https://chaoli.club/index.php/8624 ,经过自己的思考、测试和细节补充最终得到这份题解。
我只是初步涉猎初等数论和 ACM 数论题目,目前我也只想到这一种解法,如有更好的解法或其他问题,欢迎在评论区仪器交流~