Table of Contents
题目
分析
有一个著名的“欧拉公式”,用来计算小于等于n
的正整数中与n
互质的数的个数,记作\(\varphi(n)\)。计算公式为:对于一个正整数n
,若其质因数分解为\(n=p_1^{k_1}p_2^{k_2}...p_m^{k_m}\),则:\(\varphi(n)=n\prod_{i=1}^m(1-\frac{1}{p_i})\)。当然本题要计算的,是与n
不互质的数的个数,所以是\(n-\varphi(n)\)。
准备工作一:欧拉筛计算到\(\sqrt r\)的质数
这是一个常规的算法。其详细的解法可以参考P3383:线性筛素数。
根据题意,\(r\le 10^{12}\),所以\(\sqrt r\le 10^6\)。
计算\(\varphi(n)\)
我们用两个数组来保存中间数据。一个是A[i]
,存储某个数i
的\(\varphi(i)\)。为了避免计算\(\frac{1}{p_i}\)可能出现的很小很小的数字,我们将其变形为:\((p_i-1) / p_i\)。
考虑更大的质因子
我们第一步准备工作中,只算到了\(\sqrt r\)的质因子,但可能还有比这大的质因子——但只可能有一个。我们还要考虑这个情况。我们用一个辅助数组B[i]
来进行处理。方法是:
- 初始时,
A[i]=B[i]=i
,也就是各自等于要计算\(\varphi(i)\)的那个i
。 - 每次用一个\(p_i\)计算
A[i]
的时候,不断将B[i]
中对应的这个质因子去掉。 - 如果最后
B[i]==1
,说明这个数字被之前的质因子分解完毕了。否则,还要计算一次,此时的质因子就是B[i]
。
其他技巧
由于l
和r
本身值很大,但r-l
(区间)较小,所以我们可以进行一个“偏移”映射,也就是\(l\Rightarrow 0, r\Rightarrow (r-l)\)。这样就避免了开一个巨大但十分稀疏的数组。
答案
思考
代码的45行,用B[i]^1
来判断B[i]
是不是等于1
,这是一种古老的做法:因为在C时代,普遍认为这样的异或运算比纯比较运算要快一些。