<更新提示>
前置知识:建议有一点 D i r i c h l e t Dirichlet Dirichlet卷积的基础,会线性筛求积性函数。本文可能会持续更新。
可以看我以前在博客园的博客。
<正文>
0. 符号及约定
1. 1. 1. [ P ] [P] [P]是指正则表达式,当 P P P为 t r u e true true时, [ P ] = 1 [P]=1 [P]=1,当 P P P为 f a l s e false false时, [ P ] = 0 [P]=0 [P]=0。
2. 2. 2. 约数个数函数定义为 τ ( n ) = ∑ d ∣ n 1 \tau(n)=\sum_{d|n}1 τ(n)=∑d∣n1。
3. 3. 3. 约数和函数定义为 σ ( n ) = ∑ d ∣ n d \sigma(n)=\sum_{d|n}d σ(n)=∑d∣nd。
4. 4. 4. 元函数定义为 e ( n ) = [ n = 1 ] e(n)=[n=1] e(n)=[n=1]。
5. 5. 5. 恒等函数定义为 I ( n ) = 1 I(n)=1 I(n)=1。
6. 6. 6. 单位函数定义为 ϵ k ( n ) = n k \epsilon_k(n)=n^k ϵk(n)=nk。
7. 7. 7. 欧拉函数定义为 φ ( n ) = ∑ i = 1 n [ gcd ( i , n ) = 1 ] \varphi(n)=\sum_{i=1}^n[\gcd(i,n)=1] φ(n)=∑i=1n[gcd(i,n)=1]。
8. 8. 8. 设 n = ∏ p i c i n=\prod p_i^{c_i} n=∏pici,则莫比乌斯函数定义为 μ ( n ) = { 1 n = 1 ( − 1 ) k ∀ c i = 1 0 ∃ c i > 1 \mu(n)=\begin{cases}1&n=1\\(-1)^k& \forall c_i=1\\0&\exist c_i>1\end{cases} μ(n)=⎩⎪⎨⎪⎧1(−1)k0n=1∀ci=1∃ci>1。
9. 9. 9. 对于数论函数 f f f,若满足 gcd ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1时,有 f ( a b ) = f ( a ) f ( b ) f(ab)=f(a)f(b) f(ab)=f(a)f(b),则称函数 f f f为积性函数。
10. 10. 10. 对于两个函数 f , g f,g f,g,定义他们的 d i r i c h l e t dirichlet dirichlet卷积为: ( f × g ) ( n ) = ∑ d ∣ n f ( d ) g ( n d ) (f\times g)(n)=\sum_{d|n}f(d)g(\frac{n}{d}) (f×g)(n)=∑d∣nf(d)g(dn),函数 f , g f,g f,g不必要是积性函数。
1. 欧拉筛法
前置知识里已经提到过,以下给出本文可能会使用的线性筛代码:
int flag[N],Prime[N],cnt;
inline void EularSieve(void)
{
for (int i=2;i<=Lim;i++)
{
if ( !flag[i] ) Prime[++cnt] = i;
for (int j=1;j<=cnt&&i*Prime[j]<=Lim;j++)
{
flag[ i*Prime[j] ] = true;
if ( i % Prime[j] == 0 ) break;
}
}
}
这是最朴素的欧拉筛法,不过当我们要筛一些比较复杂的积性函数的时候,线性筛方程的推导可能会非常复杂。
因此我们考虑引入一种更好的线性筛法,目的是能够方便的筛出积性函数的值。
首先,我们注意到积性函数的性质:当 gcd ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1时,有 f ( a b ) = f ( a ) f ( b ) f(ab)=f(a)f(b) f(ab)=f(a)f(b)。那么对于一个任意的正整数 n n n,我们可以用算术基本定理进行分解:
n = ∏ i = 1 k p i c i n=\prod_{i=1}^kp_i^{c_i} n=i=1∏kpici
此时,任意的 i , j ∈ [ 1 , k ] i,j\in[1,k] i,j∈[1,k]都满足 gcd ( p i c i , p j c j ) = 1 \gcd(p_i^{c_i},p_j^{c_j})=1 gcd(pici,pjcj)=1。那么我们就可以得到:
f ( n ) = f ( ∏ i = 1 k p i c i ) = ∏ i = 1 k f ( p i c i ) f(n)=f\left(\prod_{i=1}^kp_i^{c_i}\right)=\prod_{i=1}^kf(p_i^{c_i}) f(n)=f(i=1∏kpici)=i=1∏kf(pici)
那么我们就有一个想法:利用定义求出积性函数 f f f在所有素数幂出的取值,然后直接递推出所有函数值。
具体地说,可以这样递推:
f[1] = 1;
for (int i=2;i<=Lim;i++)
if ( i == Pow[i] ) f[i] = calc(p[i],e[i]);
else f[i] = f[i/Pow[i]] * f[Pow[i]];
其中 p [ i ] p[i] p[i]代表数 i i i的最小素因子, e [ i ] e[i] e[i]代表 i i i的分解式中 p [ i ] p[i] p[i]这个素因子的指数, c a l c calc calc就是求素数幂处取值的函数,而 P o w [ i ] = p [ i ] e [ i ] Pow[i]=p[i]^{e[i]} Pow[i]=p[i]e[i],,这样是不是就符合我们的要求了呢?
那么现在我们的问题就是如何求出 p , e , P o w p,e,Pow p,e,Pow这三个数组,幸运的是,他们都可以在线性筛的过程中求。
根据欧拉筛法每次用一个数的最小素因子筛去这个合数,就可以更新这三个数组的值了。
代码如下:
int p[N],e[N],Pow[N],Prime[N],cnt;
inline void EularSieve(void)
{
Pow[1] = p[1] = 1 , e[1] = 0;
for (int i=2;i<=Lim;i++)
{
if ( p[i] == 0 )
p[i] = Pow[i] = Prime[++cnt] = i , e[i] = 1;
// 判定一个新素数
for (int j=1;j<=cnt&&i*Prime[j]<=Lim;j++)
{
int Next = i * Prime[j];
if ( p[i] == Prime[j] )
{
e[Next] = e[i] + 1;
Pow[Next] = Pow[i] * Prime[j];
// 有相同的素因子
break;
}
else e[Next] = 1 , Pow[Next] = Prime[j];
// 没有相同的素因子
}
}
f[1] = 1;
for (int i=2;i<=Lim;i++)
if ( i == Pow[i] ) f[i] = calc(p[i],e[i]);
else f[i] = f[i/Pow[i]] * f[Pow[i]];
}
2. D i r i c h l e t Dirichlet Dirichlet卷积
2.1 D i r i c h l e t Dirichlet Dirichlet卷积的性质
1. 1. 1. 两个积性函数 f f f和 g g g的 d i r i c h l e t dirichlet dirichlet卷积仍为积性函数。
证明:
设有两个积性函数 f f f和 g g g,则它们的 d i r i c h l e t dirichlet dirichlet卷积为:
h = f × g = ∑ d ∣ n f ( d ) g ( n d ) h=f\times g=\sum_{d|n}f(d)g(\frac{n}{d}) h=f×g=d∣n∑f(d)g(dn)
对于函数 h h h则可以得到:
h ( x ) h ( y ) = ( ∑ d 1 ∣ x f ( d 1