###前置知识
####积性函数
若函数 f ( n ) f(n) f ( n ) 满足当 g c d ( i , j ) = 1 gcd(i,j)=1 g c d ( i , j ) = 1 时, f ( i j ) = f ( i ) f ( j ) f(ij)=f(i)f(j) f ( ij ) = f ( i ) f ( j ) ,那么这个函数为积性函数
常见的积性函数有
1.μ ( n ) \mu(n) μ ( n ) —莫比乌斯函数
2.ϕ ( n ) \phi(n) ϕ ( n ) —欧拉函数
3.d ( n ) d(n) d ( n ) —约数个数
4.σ ( n ) \sigma(n) σ ( n ) —约数和函数
####完全积性函数
待会儿会用到
1.ϵ ( n ) \epsilon(n) ϵ ( n ) —元函数,满足 ϵ ( n ) = [ n = 1 ] \epsilon(n)=[n=1] ϵ ( n ) = [ n = 1 ]
2.I ( n ) I(n) I ( n ) —恒等函数,恒等于1
3.i d ( n ) id(n) i d ( n ) —单位函数,i d ( n ) = n id(n)=n i d ( n ) = n
可能这时你会觉得没用,当时我也是这么觉得的,但是看完下面就不会这么觉得的
####迪利克雷卷积(*)
定义:两个数论函数 f , g f,g f , g 的卷积为 ( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) × g ( n d ) (f*g)(n)=\sum\limits_{d|n}f(d)\times g(\frac{n}{d}) ( f ∗ g ) ( n ) = d ∣ n ∑ f ( d ) × g ( d n ) ,括号表示范围
显然,满足 f ∗ g = g ∗ f , ( f ∗ g ) ∗ h = f ∗ ( g ∗ h ) , ( f + g ) ∗ h = f ∗ h + g ∗ h f*g=g*f,(f*g)*h=f*(g*h),(f+g)*h=f*h+g*h f ∗ g = g ∗ f , ( f ∗ g ) ∗ h = f ∗ ( g ∗ h ) , ( f + g ) ∗ h = f ∗ h + g ∗ h ,即交换律,结合律,分配律
有了这个有什么用呢
之前证明过莫比乌斯反演,其实迪利克雷卷积也可以证明
证明
我们知道,莫比乌斯函数满足 ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum\limits_{d|n}\mu(d)=[n=1] d ∣ n ∑ μ ( d ) = [ n = 1 ]
写成迪利克雷卷积的形式就是 μ ∗ I = ϵ \mu*I=\epsilon μ ∗ I = ϵ
F ( n ) = ∑ i ∣ d f ( i ) ⇒ F = f ∗ I ⇒ F ∗ μ = f ∗ I ∗ μ = f ∗ ϵ = f F(n)=\sum\limits_{i|d}f(i)\Rightarrow F=f*I \Rightarrow F*\mu=f*I*\mu=f*\epsilon=f
F ( n ) = i ∣ d ∑ f ( i ) ⇒ F = f ∗ I ⇒ F ∗ μ = f ∗ I ∗ μ = f ∗ ϵ = f
即 F ∗ μ = f F*\mu=f F ∗ μ = f ,代入后可得 f ( n ) = ∑ i ∣ d μ ( d i ) F ( d ) f(n)=\sum\limits_{i|d}\mu(\frac{d}{i})F(d) f ( n ) = i ∣ d ∑ μ ( i d ) F ( d )
证毕
同时,它还可以用来推欧拉函数与莫比乌斯函数的关系
可以知道欧拉函数有一个性质 ∑ d ∣ n μ ( d ) = n \sum\limits_{d|n}\mu(d)=n d ∣ n ∑ μ ( d ) = n
写成迪利克雷卷积的形式就是 ϕ ∗ I = i d \phi*I=id ϕ ∗ I = i d
⇒ ϕ ∗ I ∗ μ = i d ∗ μ ⇒ ϕ ∗ ϵ = i d ∗ μ \Rightarrow \phi*I*\mu=id*\mu\\\Rightarrow \phi*\epsilon=id*\mu
⇒ ϕ ∗ I ∗ μ = i d ∗ μ ⇒ ϕ ∗ ϵ = i d ∗ μ
即 ϕ = i d ∗ μ ⇒ ϕ ( n ) = ∑ d ∣ n i d ( n d ) μ ( d ) = ∑ d ∣ n n μ ( d ) d \phi=id*\mu\Rightarrow \phi(n)=\sum\limits_{d|n}id(\frac{n}{d})\mu(d)=\sum\limits_{d|n}\frac{n\mu(d)}{d} ϕ = i d ∗ μ ⇒ ϕ ( n ) = d ∣ n ∑ i d ( d n ) μ ( d ) = d ∣ n ∑ d n μ ( d )
###杜教筛
说完上面那些,就可以开始讲杜教筛了
首先,杜教筛是以低于线性时间复杂度来求积性函数前缀和的筛法
即我们需要求的式子是 ∑ i = 1 n f ( i ) \sum\limits_{i=1}^nf(i) i = 1 ∑ n f ( i )
为了求这个东西,我们首先构造两个积性函数,使得 f ∗ g = h f*g=h f ∗ g = h
不妨令 S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum\limits_{i=1}^nf(i) S ( n ) = i = 1 ∑ n f ( i )
f ∗ g = h ⇒ ∑ i = 1 n h ( i ) = ∑ i = 1 n ∑ d ∣ i g ( d ) ∗ f ( d i ) = ∑ d = 1 n g ( d ) ∑ i = 1 ⌊ n d ⌋ f ( i ) f*g=h\Rightarrow\sum\limits_{i=1}^nh(i)=\sum\limits_{i=1}^n\sum\limits_{d|i}g(d)*f(\frac{d}{i})\\=\sum\limits_{d=1}^ng(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)
f ∗ g = h ⇒ i = 1 ∑ n h ( i ) = i = 1 ∑ n d ∣ i ∑ g ( d ) ∗ f ( i d ) = d = 1 ∑ n g ( d ) i = 1 ∑ ⌊ d n ⌋ f ( i )
即 ∑ i = 1 n h ( i ) = ∑ d = 1 n g ( d ) S ( ⌊ n d ⌋ ) \sum\limits_{i=1}^nh(i)=\sum\limits_{d=1}^ng(d)S(\lfloor\frac{n}{d}\rfloor) i = 1 ∑ n h ( i ) = d = 1 ∑ n g ( d ) S (⌊ d n ⌋)
提出第一项得到 g ( 1 ) S ( n ) = ∑ i = 1 n h ( i ) − ∑ d = 2 n g ( d ) S ( ⌊ n d ⌋ ) g(1)S(n)=\sum\limits_{i=1}^nh(i)-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor) g ( 1 ) S ( n ) = i = 1 ∑ n h ( i ) − d = 2 ∑ n g ( d ) S (⌊ d n ⌋)
简而言之,就是用简单的去求复杂的,需要你定义的 h , g h,g h , g 比较好求
###应用
####求 S ( n ) = ∑ i = 1 n μ ( i ) S(n)=\sum\limits_{i=1}^n\mu(i) S ( n ) = i = 1 ∑ n μ ( i )
根据上述式子 g ( 1 ) S ( n ) = ∑ i = 1 n h ( i ) − ∑ d = 2 n g ( d ) S ( ⌊ n d ⌋ ) g(1)S(n)=\sum\limits_{i=1}^nh(i)-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor) g ( 1 ) S ( n ) = i = 1 ∑ n h ( i ) − d = 2 ∑ n g ( d ) S (⌊ d n ⌋) ,我们只需要找到一个 g g g 使得这个式子容易求
显然 μ ∗ I = ϵ \mu*I=\epsilon μ ∗ I = ϵ ,不妨假设 f = μ , g = I , h = ϵ f=\mu,g=I,h=\epsilon f = μ , g = I , h = ϵ
⇒ S ( n ) = 1 − ∑ d = 2 n S ( ⌊ n d ⌋ ) \Rightarrow S(n)=1-\sum\limits_{d=2}^nS(\lfloor\frac{n}{d}\rfloor)
⇒ S ( n ) = 1 − d = 2 ∑ n S (⌊ d n ⌋)
这样就可已通过杜教筛求莫比乌斯函数前缀和
####求 S ( n ) = ∑ i = 1 n ϕ ( i ) S(n)=\sum\limits_{i=1}^n\phi(i) S ( n ) = i = 1 ∑ n ϕ ( i )
同上,可以找到 ϕ ∗ I = i d \phi*I=id ϕ ∗ I = i d ,假设 f = ϕ , g = I , h = i d f=\phi,g=I,h=id f = ϕ , g = I , h = i d
⇒ S ( n ) = ∑ i = 1 n i − ∑ d = 2 n S ( ⌊ n d ⌋ ) \Rightarrow S(n)=\sum\limits_{i=1}^ni-\sum\limits_{d=2}^nS(\lfloor\frac{n}{d}\rfloor)
⇒ S ( n ) = i = 1 ∑ n i − d = 2 ∑ n S (⌊ d n ⌋)
####求 S ( n ) = ∑ i = 1 n i ϕ ( i ) S(n)=\sum\limits_{i=1}^ni\phi(i) S ( n ) = i = 1 ∑ n i ϕ ( i )
相比之前的两个,这个无法直接看出需要配什么积性函数
考虑 h ( n ) = ∑ d ∣ n ( d × ϕ ( d ) ) × g ( n d ) h(n)=\sum\limits_{d|n}(d\times\phi(d))\times g(\frac{n}{d}) h ( n ) = d ∣ n ∑ ( d × ϕ ( d )) × g ( d n )
想要把其中的 d d d 给约掉,可以尝试令 g = i d g=id g = i d
⇒ ∑ d ∣ n ( d × ϕ ( d ) ) × n d = ∑ d ∣ n ϕ ( d ) × n = n ∑ d ∣ n ϕ ( d ) = n 2 \Rightarrow\sum\limits_{d|n}(d\times\phi(d))\times \frac{n}{d}=\sum\limits_{d|n}\phi(d)\times n=n\sum\limits_{d|n}\phi(d)=n^2
⇒ d ∣ n ∑ ( d × ϕ ( d )) × d n = d ∣ n ∑ ϕ ( d ) × n = n d ∣ n ∑ ϕ ( d ) = n 2
似乎可以
代回上面的式子
S ( n ) = ∑ i = 1 n i 2 − ∑ d = 2 n d S ( ⌊ n d ⌋ ) S(n)=\sum\limits_{i=1}^ni^2-\sum\limits_{d=2}^ndS(\lfloor\frac{n}{d}\rfloor)
S ( n ) = i = 1 ∑ n i 2 − d = 2 ∑ n d S (⌊ d n ⌋)
这样就可以通过整除分块快速求出了
###杜教筛的实现
先有线性筛求出一个大概的范围,然后剩下的递归处理,
【模板】杜教筛(Sum)
#include<iostream> #include<cstdio> #include<cmath> #include<unordered_map> using namespace std; typedef long long ll; const int M = 6e6 + 5; bool st[M]; int prime[M], tot, mu[M], sum1[M]; ll sum2[M], phi[M]; unordered_map<ll, ll> Phi; unordered_map<int, int> Mu; void init() { phi[1] = mu[1] = 1; for (int i = 2; i < M; i++) { if (!st[i]) prime[++tot] = i, mu[i] = -1, phi[i] = i - 1; for (int j = 1; i * prime[j] < M; j++) { st[i * prime[j]] = true; if (i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } mu[i * prime[j]] = -mu[i], phi[i * prime[j]] = phi[i] * (prime[j] - 1); } } for (int i = 1; i < M; i++) sum1[i] = sum1[i - 1] + mu[i], sum2[i] = sum2[i - 1] + phi[i]; } ll Djphi(ll x) { if (x < M) return sum2[x]; if (Phi[x]) return Phi[x]; ll res = (ll)x * (x + 1) / 2; for (ll l = 2, r; l <= x; l = r + 1) { r = min(x, x / (x / l)); res -= (ll)(r - l + 1) * Djphi(x / l); } return Phi[x] = res; } int Djmu(ll x) { if (x < M) return sum1[x]; if (Mu[x]) return Mu[x]; int res = 1; for (ll l = 2, r;l <= x; l = r + 1) { r = min(x, (x / (x / l))); res -= (ll)(r - l + 1) * Djmu(x / l); } return Mu[x] = res; } int main() { init(); int T; scanf("%d", &T); while (T--) { ll n; scanf("%lld", &n); printf("%lld %d\n", Djphi(n), Djmu(n)); } return 0; }