以下代码中的定义:
- mindiv[i]: i的最小质因子
- phi[i]: 欧拉函数i的值
- mindivq[i]: i的最小质因子的个数
- d[i]: i的约数个数
- sumd[i]: i的约数和
- miu[i]: 莫比乌斯函数i的值
- inv[i]: i在mod n意义下的乘法逆元
标准筛法
欧拉筛法,可以保证每个数只被自己最小的质因子筛去,时间复杂度O(n)
两种等价实现
易于理解版:
1 | void solve(int n){ |
又短又快版(避免了取模运算):(待更新)
1 | void solve(int n){ |
扩展:求积性函数
积性函数定义:对于一个数论函数 ,满足:若 ,则 的函数 。
由于这个性质,我们可以在筛法的同时求出积性函数的值
先看几个简单的积性函数:
id函数
定义 ,显然满足积性。
这就不用筛了,直接得到值
e函数
又称单位函数,定义
phi函数
欧拉函数,很有用,可以用来求逆元。
定义
即 中,与 互质的数的数量
易得对于质数,,
对于合数有以下公式:
将n带入可得
这就很容易发现它的积性性质
用筛法怎么求呢?
稍微改一下就行了:
1 | void solve(int n){ |
为什么呢?
- 对于
i%pris[j]!=0
的项,它的最小质因子一定是pris[j]
,则由积性函数性质可得phi[k]=phi[i]*phi[pris[j]]
,其中phi[pris[j]]=pris[j]-1
- 对于
i%pris[j]==0
的项,则说明pris[j]
已经在i
中已经出现了,而且是最小的,观察 函数的计算式,可知此时应乘pris[j]
最小质因数的指数
这个就很简单了:
1 | void solve(int n){ |
约数个数
首先约数个数如何求?
分解质因数,得到
则其 约数个数
这个结论由乘法原理易得,每个质因子有 种选法。
积性就不证了,比较显然。
怎么求呢?
观察筛法的过程, i
是质数时或 i%pris[j]!=0
时都比较容易,主要是 i%pris[j]==0
时需要考虑好。
下面来分析一下:
关键在于如何由 d(i)
转移到 d(i*pris[j])
首先筛法保证了 pris[j]
一定是最小质因子,那么由于 i%pris[j]==0
,则意味着最小质因子的指数会 ,这一点在上面的 mindivq
求解过程中也体现了。
会导致什么?
很自然会想到
就是这样。
1 | void solve(int n){ |
约数和
约数和又如何求?
分解质因数,得到
则其 约数和
看起来也是比较显然,我们直接讨论如何利用筛法来求:
依然是利用和上面的类似思路:
只讨论 i%pris[j]==0
的情况:
需要除以
再乘以
其中
这样开两个辅助数组记录
就可以做了:
1 | void solve(int n){ |
miu函数
莫比乌斯函数,定义
用筛法求也很简单:
1 | void solve(int n){ |
乘法逆元
由求逆元的欧拉定理方法易知乘法逆元也是积性的,而且有一个很优美的性质:它是完全积性的。
所以求它变得很简单:
然而乘法逆元有一个递推的方法,更加简单:
首先逆元满足以下等式(定义):
将P % i展开:
进一步展开:
会发现一个神奇的等式:
此时 就是 的逆元!
总结
线性欧拉筛法简洁,积性函数性质优美,充分结合才能发挥更大的效果。同时又要注意到不同函数的性质,用筛法求积性函数的本质是根据积性函数的定义和运算式,利用筛法的特点,构造合适的转移方法。