【算法讲8:莫比乌斯函数与莫比乌斯反演】(理论部分)与欧拉筛
前置
补充
- 主要参考:
OI Wiki 莫比乌斯反演 OI WIKI
常见的5个数论函数线性筛求法
线性筛的理解及其应用
把十分重要的内容拎出来了,不是很全。要看全的可以看前置内容的算法讲。
依旧是重要部分有证明,证明出来的话会有底数,且证明方法也同等重要。
如有错误恳请斧正
⌈ \lceil ⌈莫比乌斯函数 ⌋ \rfloor ⌋
定义
- μ ( n ) = { 1 n = 1 0 n 有 大 于 1 的 平 方 因 子 ( − 1 ) ω ( n ) 其 他 \mu(n)=\begin{cases} 1&n=1\\ 0&n有大于1的平方因子\\ (-1)^{\omega(n)}&其他 \end{cases} μ(n)=⎩⎪⎨⎪⎧10(−1)ω(n)n=1n有大于1的平方因子其他
其中 ω ( n ) \omega(n) ω(n) 表示 n n n 的本质不同的质因子个数,是个加性函数。
性质
- 性质一: μ ( n ) \mu(n) μ(n) 是乘性函数。
- 性质二: ∑ d ∣ n μ ( d ) = ε ( n ) \underset{d|n}{\sum}\mu(d)=\varepsilon(n) d∣n∑μ(d)=ε(n),即 μ ∗ 1 = ε \mu*1=\varepsilon μ∗1=ε
证明:性质一、二见【算法讲5:乘性函数(中)】 - 性质三:反演结论: [ gcd ( i , j ) = 1 ] = ∑ d ∣ gcd ( i , j ) μ ( d ) [\gcd(i,j)=1]=\underset{d|\gcd(i,j)}{\sum}\mu(d) [gcd(i,j)=1]=d∣gcd(i,j)∑μ(d)
证明: [ gcd ( i , j ) = 1 ] = ε ( gcd ( i , j ) ) = μ ∗ 1 [\gcd(i,j)=1]=\varepsilon(\gcd(i,j))=\mu*1 [gcd(i,j)=1]=ε(gcd(i,j))=μ∗1,展开即可。
⌈ \lceil ⌈莫比乌斯反演 ⌋ \rfloor ⌋
- 两种反演方式:
设 f 、 g f、g f、g 为数论函数
如果有 f ( n ) = ∑ d ∣ n g ( d ) f(n)=\underset{d|n}{\sum}g(d) f(n)=d∣n∑g(d) 那么有 g ( n ) = ∑ d ∣ n μ ( d ) f ( n d ) g(n)=\underset{d|n}{\sum}\mu(d)f(\frac{n}{d}) g(n)=d∣n∑μ(d)f(dn)
如果有 f ( n ) = ∑ n ∣ d g ( d ) f(n)=\underset{n|d}{\sum}g(d) f(n)=n∣d∑g(d) 那么有 g ( n ) = ∑ n ∣ d μ ( d n ) f ( d ) g(n)=\underset{n|d}{\sum}\mu(\frac{d}{n})f(d) g(n)=n∣d∑μ(nd)f(d)
(1)用狄利克雷卷积证明第一种反演:
已知 f = g ∗ 1 f=g*1 f=g∗1,那么 f ∗ μ = g ∗ 1 ∗ μ = g ∗ ε = g f*\mu=g*1*\mu=g*\varepsilon=g f∗μ=g∗1∗μ=g∗ε=g,即 g = μ ∗ f g=\mu*f g=μ∗f
(2)用数论变换证明第一种反演:
∑ d ∣ n μ ( d ) f ( n d ) = ∑ d ∣ n μ ( d ) ∑ k ∣ n d g ( k ) = ∑ k ∣ n g ( k ) ∑ d ∣ n k μ ( d ) = ∑ k ∣ n g ( k ) ε ( n k ) = g ( n ) □ \begin{aligned} \underset{d|n}{\sum}\mu(d)f(\frac{n}{d})&=\underset{d|n}{\sum}\mu(d)\underset{k|\frac{n}{d}}{\sum}g(k)\\ &=\underset{k|n}{\sum}g(k)\underset{d|\frac{n}{k}}{\sum}\mu(d)\\ &=\underset{k|n}{\sum}g(k)\varepsilon(\frac{n}{k})\\ &=g(n)\qquad \Box \end{aligned} d∣n∑μ(d)f(dn)=d∣n∑μ(d)k∣dn∑g(k)=k∣n∑g(k)d∣kn∑μ(d)=k∣n∑g(k)ε(kn)=g(n)□
(3)用数论变换证明第二种反演:
∑ n ∣ d μ ( d n ) f ( d ) = ∑ + ∞ k = 1 μ ( k ) f ( k n ) = ∑ + ∞ k = 1 μ ( k ) ∑ n k ∣ d g ( d ) = ∑ n ∣ d g ( d ) ∑ k ∣ d n μ ( k ) = ∑ n ∣ d g ( d ) ε ( d n ) = g ( n ) □ \begin{aligned} \underset{n|d}{\sum}\mu(\frac{d}{n})f(d)&=\underset{k=1}{\overset{+\infin}{\sum}}\mu(k)f(kn)\\ &=\underset{k=1}{\overset{+\infin}{\sum}}\mu(k)\underset{nk|d}{\sum}g(d)\\ &=\underset{n|d}{\sum}g(d)\underset{k|\frac{d}{n}}{\sum}\mu(k)\\ &=\underset{n|d}{\sum}g(d)\varepsilon(\frac{d}{n})\\ &=g(n)\qquad \Box \end{aligned} n∣d∑μ(nd)f(d)=k=1∑+∞μ(k)f(kn)=k=1∑+∞μ(k)nk∣d∑g(d)=n∣d∑g(d)k∣nd∑μ(k)=n∣d∑g(d)ε(nd)=g(n)□
⌈ \lceil ⌈线性筛 ⌋ \rfloor ⌋
- 对于一个积性函数,如果该函数 f ( p k ) f(p^k) f(pk) 可以在 O ( l o g N ) O(log N) O(logN) 之内算出来,那么该积性函数可以被线性筛。
线性筛,即在 O ( N ) O(N) O(N) 的时间复杂度下,算出每个整数点位置的 f ( i ) f(i) f(i) 积性函数,或者用来筛出小于等于 n n n 的所有质数 - 线性筛的水还算深,大体内容包含一下部分:
埃筛(埃拉托色尼斯晒)
欧拉筛
欧拉筛可以搭配筛各种积性函数
洲阁筛
杜教筛
Min_25筛
够我学好久了。。 - 出于文章内容要求,我们这里讲的是欧拉筛质数与莫比乌斯函数等
前置知识:埃筛(默认大家都会了) - 为什么要讲这个嘞、、因为这些筛是做出莫比乌斯反演题目的基础
⌈ \lceil ⌈欧拉筛:筛素数 ⌋ \rfloor ⌋
- 例题:【模板】线性筛素数 | 洛谷 P3383
- 与埃筛相比,欧拉筛 之所以时间复杂度为 O ( N ) O(N) O(N) ,因为它保证了所有数字只被它的最小质因子筛去
- 原理:与埃筛类似但不完全一样,我们拿出每一个数 i i i 。
(1)如果 i i i 还没有被筛去,那它就是一个素数。
(2)我们拿 i i i 的所有目前已获得的素数倍 i × p r i m e [ j ] i\times prime[j] i×prime[j] 去筛之后的那些数字,并把他们的标记设置为 1 1 1。因为那些数字明显能被分解为 i × p r i m e [ j ] i\times prime[j] i×prime[j],所以他们不是素数。
(3)其中保证了 p r i m e [ j ] prime[j] prime[j] 是 i × p r i m e [ j ] i\times prime[j] i×prime[j] 的最小质因数。怎么保证的呢?
我们思考 210 = 2 × 3 × 5 × 7 210=2\times3\times5\times7 210=2×3×5×7
肯定有当 i = 3 × 5 × 7 i=3\times5\times7 i=3×5×7 的时候,取得的 p r i m e [ 1 ] = 2 prime[1]=2 prime[1]=2,这时 i × p r i m e [ 1 ] = 210 i\times prime[1]=210 i×prime[1]=210,于是 210 210 210就被筛掉了一次。
如果当 i = 2 × 5 × 7 i=2\times5\times7 i=2×5×7,如果当 p r i m e [ 2 ] = 3 prime[2]=3 prime[2]=3 取到了的话,那么此时 i × p r i m e [ 2 ] = 210 i\times prime[2]=210 i×prime[2]=210,这个数字又要被筛掉一次了!
我们想保证筛掉所有数字都被该数字的最小质因数筛掉,也就是说正向枚举 p r i m e [ 1 ] prime[1] prime[1]到 p r i m e [ c n t ] prime[cnt] prime[cnt],如果存在某一个 p r i m e [ j ] prime[j] prime[j] 是 i i i 的因子了,那么再继续筛的话, p r i m e [ j ] prime[j] prime[j] 都不可能是 i × p r i m e [ j ] i\times prime[j] i×prime[j] 的最小质因数。
bool vis[MAX];
int prime[MAX];
int cnt;
void shai(int n){
for(int i = 2;i <= n;++i){
if(!vis[i])
prime[++cnt] = i; /// (1)
for(int j = 1;j <= cnt && i * prime[j] <= n;++j){
vis[i*prime[j]] = 1; /// (2)
if(i%prime[j]==0)break; /// (3)
}
}
}
- 我们发现了什么?发现代码中的 p r i m e [ j ] prime[j] prime[j] 一定是 i × p r i m e [ j ] i\times prime[j] i×prime[j] 的最小质因数!
这一条非常重要的性质为我们线性筛一些基本的积性函数做出了铺垫。
⌈ \lceil ⌈欧拉筛:筛莫比乌斯函数 ⌋ \rfloor ⌋
- 怎么筛呢?考虑到,莫比乌斯函数是积性函数,我们就要用积性函数的性质,即:
若 m , n ∈ N + m,n\in \mathbb{N}^+ m,n∈N+ 且 m ⊥ n m\perp n m⊥n,则有 f ( m n ) = f ( m ) f ( n ) f(mn)=f(m)f(n) f(mn)=f(m)f(n)
当然我们还需要知道 f ( p ) f(p) f(p) 是多少。 - 带入莫比乌斯函数,根据定义:
μ ( n ) = { 1 n = 1 0 n 有 大 于 1 的 平 方 因 子 ( − 1 ) ω ( n ) 其 他 \mu(n)=\begin{cases} 1&n=1\\ 0&n有大于1的平方因子\\ (-1)^{\omega(n)}&其他 \end{cases} μ(n)=⎩⎪⎨⎪⎧10(−1)ω(n)n=1n有大于1的平方因子其他
其中 ω ( n ) \omega(n) ω(n) 表示 n n n 的本质不同的质因子个数,是个加性函数。 - 考虑到欧拉筛只会把每个数给筛一次,且每次会给出该数字的最小质数与最大因数的乘积
我们就要把筛法往这里靠拢: - 设 p p p 是 n n n 的最小素数
(1) μ ( p ) = − 1 \mu(p)=-1 μ(p)=−1
(2) n = p × i n=p\times i n=p×i 且 p ⊥ i p\perp i p⊥i,那么 μ ( p × i ) = μ ( p ) μ ( i ) = − μ ( i ) \mu(p\times i)=\mu(p)\mu(i)=-\mu(i) μ(p×i)=μ(p)μ(i)=−μ(i)
(3) n = p × i n=p\times i n=p×i 且 p ⊥̸ i p\not\perp i p⊥i,那么 μ ( p × i ) = 0 \mu(p\times i)=0 μ(p×i)=0
我们轻松即可把欧拉筛素数的板子转化为欧拉筛莫比乌斯函数的板子:
bool vis[MAX];
int prime[MAX];
int mu[MAX];
int cnt;
void shai(int n){
mu[1] = 1;
for(int i = 2;i <= n;++i){
if(!vis[i]){
prime[++cnt] = i;
mu[i] = -1;
}
for(int j = 1;j <= cnt && i * prime[j] <= n;++j){
vis[i*prime[j]] = 1;
if(i % prime[j])mu[i*prime[j]] = -mu[i];
else {
mu[i*prime[j]] = 0;
break;
}
}
}
}
⌈ \lceil ⌈欧拉筛:筛欧拉函数 ⌋ \rfloor ⌋
- 欧拉函数的定义及其推导公式是啥:
φ ( n ) = ∑ n i = 1 [ gcd ( i , n ) = 1 ] = n × ∏ p i − 1 p i \varphi(n)=\underset{i=1}{\overset{n}{\sum}}[\gcd(i,n)=1]=n\times\prod\frac{p_i-1}{p_i} φ(n)=i=1∑n[gcd(i,n)=1]=n×∏pipi−1 - 欧拉函数也是积性函数
∀ m , n ∈ N + \forall m,n\in \mathbb{N}^+ ∀m,n∈N+,如果 m ⊥ n m\perp n m⊥n 则有 φ ( m n ) = φ ( m ) φ ( n ) \varphi(mn)=\varphi(m)\varphi(n) φ(mn)=φ(m)φ(n) - 设 p p p 是 n n n 的最小素数:
(1) φ ( p ) = p − 1 \varphi(p)=p-1 φ(p)=p−1
(2) n = p × i n=p\times i n=p×i 有 i ⊥ p i\perp p i⊥p,则有 φ ( i × p ) = φ ( i ) φ ( p ) = φ ( i ) × ( p − 1 ) \varphi(i\times p)=\varphi(i)\varphi(p)=\varphi(i)\times(p-1) φ(i×p)=φ(i)φ(p)=φ(i)×(p−1)
(3) n = p × i n=p\times i n=p×i 有 i ⊥̸ p i\not\perp p i⊥p,则有 φ ( i × p ) = p × i × ∏ p i − 1 p i = p × φ ( i ) \varphi(i\times p)=p\times i\times \prod \frac{p_i-1}{p_i}=p\times\varphi(i) φ(i×p)=p×i×∏pipi−1=p×φ(i)
为啥嘞?因为此时 i i i 包含了所有 n n n 的素数因子。 - 感觉跟上面一样的套路,我作为小萌新,也会生推板子了呢
???胡说你明明之前推错了
bool vis[MAX];
int prime[MAX];
int phi[MAX];
int cnt;
void shai(int n){
phi[1] = 1;
for(int i = 2;i <= n;++i){
if(!vis[i]){
prime[++cnt] = i;
phi[i] = i - 1;
}
for(int j = 1;j <= cnt && i * prime[j] <= n;++j){
vis[i*prime[j]] = 1;
if(i % prime[j])phi[i*prime[j]] = phi[i] * (prime[j] - 1);
else {
phi[i*prime[j]] = phi[i] * prime[j];
break;
}
}
}
}
⌈ \lceil ⌈欧拉筛:筛约数个数函数 ⌋ \rfloor ⌋
- 约数个数函数的定义及其推导是啥?
d ( n ) = τ ( n ) = ∑ d ∣ n 1 = ∏ ( a i + 1 ) d(n)=\tau(n)=\underset{d|n}{\sum}1=\prod(a_i+1) d(n)=τ(n)=d∣n∑1=∏(ai+1)
它也是个积性函数。继续怎么筛呢? - 设 p p p 是 n n n 的最小素数:
(1) d ( p k ) = k + 1 d(p^k)=k+1 d(pk)=k+1
(2) n = p × i n=p\times i n=p×i 有 i ⊥ p i\perp p i⊥p,则有 d ( p × i ) = d ( p ) d ( i ) = 2 d ( i ) d(p\times i)=d(p)d(i)=2d(i) d(p×i)=d(p)d(i)=2d(i)
(3) n = p × i n=p\times i n=p×i 有 i ⊥̸ p i\not\perp p i⊥p,即 i i i 含有 p r i m e [ j ] prime[j] prime[j] 的因子。
我们需要知道 i i i 的最小素数幂次 m p ( i ) mp(i) mp(i) 是多少,然后把 i i i 中的该项的因子全拿出来即可
即: d ( i × p ) = d ( i p m p ( i ) ) d ( p m p ( i ) + 1 ) = d ( i ) m p ( i ) + 1 × ( m p ( i ) + 2 ) d(i\times p)=d(\cfrac{i}{p^{mp(i)}})d(p^{mp(i)+1})=\cfrac{d(i)}{mp(i)+1}\times(mp(i)+2) d(i×p)=d(pmp(i)i)d(pmp(i)+1)=mp(i)+1d(i)×(mp(i)+2) - 那我们还要去维护这个 m p ( i ) mp(i) mp(i),怎么维护呢?
- 设 p p p 是 n n n 的最小素数:
(1) m p ( p ) = 1 mp(p)=1 mp(p)=1
(2) n = p × i n=p\times i n=p×i 有 i ⊥ p i\perp p i⊥p,则有 m p ( n ) = 1 mp(n)=1 mp(n)=1
(3) n = p × i n=p\times i n=p×i 有 i ⊥̸ p i\not\perp p i⊥p,则有 m p ( n ) = m p ( i ) + 1 mp(n)=mp(i)+1 mp(n)=mp(i)+1
因为保证了 p p p 是 n n n 的最小质数,即此时 i i i 的最小质数也是 p p p 。
bool vis[MAX];
int prime[MAX];
int mp[MAX];
int tau[MAX];
int cnt;
void shai(int n){
tau[1] = 1;
mp[1] = 0;
for(int i = 2;i <= n;++i){
if(!vis[i]){
prime[++cnt] = i;
tau[i] = 2;
mp[i] = 1;
}
for(int j = 1;j <= cnt && i * prime[j] <= n;++j){
vis[i*prime[j]] = 1;
if(i % prime[j]){
tau[i*prime[j]] = tau[i] * 2;
mp[i*prime[j]] = 1;
}else {
tau[i*prime[j]] = tau[i] / (mp[i] + 1) * (mp[i] + 2);
mp[i*prime[j]] = mp[i] + 1;
break;
}
}
}
}
⌈ \lceil ⌈欧拉筛:筛约数和函数 ⌋ \rfloor ⌋
- 约数和函数的定义及其推导是啥?
σ ( n ) = ∑ d ∣ n d = ∏ p i a i + 1 − 1 p i − 1 \sigma(n)=\underset{d|n}{\sum}d=\prod\cfrac{p_i^{a_i+1}-1}{p_i-1} σ(n)=d∣n∑d=∏pi−1piai+1−1
它也是个积性函数。继续怎么筛呢?已经直接是复读机了啊喂 - 设 p p p 是 n n n 的最小素数:
(1) σ ( p k ) = p k + 1 − 1 p − 1 \sigma(p^k)=\cfrac{p^{k+1}-1}{p-1} σ(pk)=p−1pk+1−1
(2) n = p × i n=p\times i n=p×i 有 i ⊥ p i\perp p i⊥p,那么 σ ( p × i ) = σ ( p ) σ ( i ) = ( p + 1 ) σ ( i ) \sigma(p\times i)=\sigma(p)\sigma(i)=(p+1)\sigma(i) σ(p×i)=σ(p)σ(i)=(p+1)σ(i)
(3) n = p × i n=p\times i n=p×i 有 i ⊥̸ p i\not\perp p i⊥p,那么我们依然要把 i i i 的最小素数幂次 m p ( i ) mp(i) mp(i) 是多少得到,拿出来后
σ ( p × i ) = σ ( i p m p ( i ) ) σ ( p m p ( i ) + 1 ) = σ ( n ) σ ( p m p ( i ) ) σ ( p m p ( i ) + 1 ) = σ ( n ) × p m p ( i ) + 2 − 1 p m p ( i ) + 1 − 1 \sigma(p\times i)=\sigma(\cfrac{i}{p^{mp(i)}})\sigma(p^{mp(i)+1})=\cfrac{\sigma(n)}{\sigma(p^{mp(i)})}\sigma(p^{mp(i)+1})=\sigma(n)\times \cfrac{p^{mp(i)+2}-1}{p^{mp(i)+1}-1} σ(p×i)=σ(pmp(i)i)σ(pmp(i)+1)=σ(pmp(i))σ(n)σ(pmp(i)+1)=σ(n)×pmp(i)+1−1pmp(i)+2−1
啊这,如果还要用快速幂啥算的话,时间复杂度会大打折扣呀!我们稍微改一下定义好了:
(3 ⋆ ^\star ⋆) n = p × i n=p\times i n=p×i 有 i ⊥̸ p i\not\perp p i⊥p,我们设 m p ( i ) mp(i) mp(i) 表示 i i i 的最小幂因数的幂次方。
说白了就是用 m p ( i ) mp(i) mp(i) 代替原来的 p m p ( i ) p^{mp(i)} pmp(i)
σ ( p × i ) = σ ( i m p ( i ) ) σ ( m p ( i ) + 1 ) = σ ( i ) × m p ( i ) × p 2 − 1 m p ( i ) × p − 1 \sigma(p\times i)=\sigma(\cfrac{i}{mp(i)})\sigma(mp(i)+1)=\sigma(i)\times\cfrac{mp(i)\times p^2-1}{mp(i)\times p-1} σ(p×i)=σ(mp(i)i)σ(mp(i)+1)=σ(i)×mp(i)×p−1mp(i)×p2−1 - 那我们还要去维护这个新的 m p ( i ) mp(i) mp(i),怎么维护呢?
- 设 p p p 是 n n n 的最小素数:
(1) m p ( p ) = p mp(p)=p mp(p)=p
(2) n = p × i n=p\times i n=p×i 有 i ⊥ p i\perp p i⊥p,则有 m p ( n ) = p mp(n)=p mp(n)=p
(3) n = p × i n=p\times i n=p×i 有 i ⊥̸ p i\not\perp p i⊥p,则有 m p ( n ) = m p ( i ) × p mp(n)=mp(i)\times p mp(n)=mp(i)×p
很好理解!好理解个P你之前又推错了
注意不要上溢了。
bool vis[MAX];
int prime[MAX];
ll mp[MAX];
ll sigma[MAX];
int cnt;
void shai(int n){
sigma[1] = 1;
mp[1] = 1;
for(int i = 2;i <= n;++i){
if(!vis[i]){
prime[++cnt] = i;
sigma[i] = i + 1;
mp[i] = i;
}
for(int j = 1;j <= cnt && i * prime[j] <= n;++j){
vis[i*prime[j]] = 1;
if(i % prime[j]){
sigma[i*prime[j]] = sigma[i] * (prime[j] + 1);
mp[i*prime[j]] = prime[j];
}else {
sigma[i*prime[j]] = sigma[i] * (mp[i] * prime[j] * prime[j] - 1) / (mp[i] * prime[j] - 1);
mp[i*prime[j]] = mp[i] * prime[j];
break;
}
}
}
}