目录(假的
-
狄利克雷卷积基础知识
- 数论函数
- 狄利克雷卷积定义
- 狄利克雷卷积性质
- 常用卷积
-
卷积计算方法
- 最暴力的暴力
- 稍好的暴力
- 优美的暴力
-
莫比乌斯反演(待填坑)
-
杜教筛
- 经典杜教筛
- 杜教筛应用
- 杜教筛应用一
- 杜教筛应用二
- 杜教筛总结
背景
本人即将去CTS&APIO2019,由于一些特殊原因,发现自己数论突然变得很菜。
就决定在去的前一天,翻出来以前的数论学习资料看一看。翻到了czgj的校内狄利克雷卷积课件,发现其中提到了的任意数列 f ( n ) f(n) f(n)和 g ( n ) g(n) g(n)的狄利克雷卷积 ( f ∗ g ) ( n ) (f*g)(n) (f∗g)(n)(从1到n,每一项都求出来)的 O ( n l o g n ) O(nlogn) O(nlogn)求法。然而我没有反应过来。
后来仔细想了想,发现了一个 O ( n H ( n ) ) O(nH(n)) O(nH(n))的做法。很开心。
这里 H ( n ) H(n) H(n)表示调和级数的第 n n n项。 H ( n ) ≈ l n ( n ) H(n)\approx ln(n) H(n)≈ln(n)。具体后文有介绍。
但是不知道他的log的底数是不是e。不管了,我的还算快的。
本文会提到一点狄利克雷卷积的基础知识,一个暴力的卷积及反演计算方法,以及一个显而易见,却十分优秀的快速卷积方法。
以及常用的三种杜教筛。
想写杜教筛,但是有点懒,还没写完。
update2019.7.9:现在写完杜教筛了,但是没写莫比乌斯反演
狄利克雷卷积基础知识
为了记录一下我对于狄利克雷卷积的理解,也帮助大家复习一下,先介绍点基础知识。
数论函数
狄利克雷卷积是定义在数论函数上的,它是一个处理数论相关问题的有效工具。因此,为了对新人友善一点,也方便我日后复习,就在这里把数论函数相关也提一下。
定义
数论函数
在数论上,算术函数(或称数论函数)指定义域为正整数、陪域为复数的函数,每个算术函数都可视为复数的序列。
通俗地说,数论函数与普通函数基本一样,只是涉及的取值是在正整数范围内而已。
积性函数
对于一个数论函数 f ( n ) f(n) f(n),若 ∀ ( a , b ) = 1 , f ( a b ) = f ( a ) f ( b ) \forall(a,b)=1,f(ab)=f(a)f(b) ∀(a,b)=1,f(ab)=f(a)f(b),我们就称 f f f为积性函数。
就是说,如果a和b互质,那么对于积性函数 f ( n ) f(n) f(n),总有 f ( a b ) = f ( a ) f ( b ) f(ab)=f(a)f(b) f(ab)=f(a)f(b)。
特别地,对于每一个积性函数,都有 f ( 1 ) = 1 f(1)=1 f(1)=1。
完全积性函数
我们观察到,积性函数的定义里,
a
a
a和
b
b
b互质这个东西,一看就很烦。
如果对于数论函数
f
(
n
)
f(n)
f(n),有
∀
a
,
b
f
(
a
b
)
=
f
(
a
)
f
(
b
)
\forall a,b\quad f(ab)=f(a)f(b)
∀a,bf(ab)=f(a)f(b),则我们称
f
(
n
)
f(n)
f(n)为完全积性函数。
栗子
这只是几个常用栗子,更多栗子,请珂学上网,前往wiki(我写本文的时候刚到墙外)某墙外网站_Multiplicative_function
单位函数
ϵ
(
n
)
=
{
1
n
=
1
0
n
≠
1
\epsilon(n)=\begin{cases}1&n=1\\0&n\ne1\end{cases}
ϵ(n)={10n=1n=1
这是完全积性函数。
幂函数
i
d
k
(
n
)
=
n
k
id_k(n)=n^k
idk(n)=nk
i
d
0
(
n
)
=
1
(
n
)
=
n
0
=
1
id_0(n)=1(n)=n^0=1
id0(n)=1(n)=n0=1
i
d
1
(
n
)
=
i
d
(
n
)
=
n
id_1(n)=id(n)=n
id1(n)=id(n)=n
都是完全积性函数。
除数函数
σ
k
(
n
)
=
∑
d
∣
n
d
k
\sigma_k(n)=\sum\limits_{d\mid n}d^k
σk(n)=d∣n∑dk,即
n
n
n的所有约数的
k
k
k次方的和。
σ
0
(
n
)
=
d
(
n
)
=
∑
d
∣
n
1
\sigma_0(n)=d(n)=\sum\limits_{d\mid n}1
σ0(n)=d(n)=d∣n∑1,即
n
n
n的约数个数。
σ
1
(
n
)
=
σ
(
n
)
=
∑
d
∣
n
d
\sigma_1(n)=\sigma(n)=\sum\limits_{d\mid n}d
σ1(n)=σ(n)=d∣n∑d,即
n
n
n的约数和。
这是积性函数,但不是完全积性函数。
欧拉函数
定义式
φ
(
n
)
=
∑
i
=
1
n
−
1
[
(
i
,
n
)
=
1
]
\varphi(n)=\sum\limits_{i=1}^{n-1}[(i,n)=1]
φ(n)=i=1∑n−1[(i,n)=1],即
1
1
1到
(
n
−
1
)
(n-1)
(n−1)中,与
n
n
n互质的数的个数。
是个积性函数。
另一个公式
φ
(
n
)
=
n
∏
p
∣
n
,
p
i
s
a
p
r
i
m
e
(
1
−
1
p
)
\varphi(n)=n\prod\limits_{p\mid n,p\,is\,a\,prime}(1-\frac{1}{p})
φ(n)=np∣n,pisaprime∏(1−p1)
这里
p
p
p即为
n
n
n的所有质因子。
证明:考虑对于 φ ( n ) \varphi(n) φ(n),其中 n = p k n=p^k n=pk且 p p p为质数,我们有 φ ( n ) = n − n p = n ( 1 − 1 p ) \varphi(n)=n-\frac{n}{p}=n(1-\frac{1}{p}) φ(n)=n−pn=n(1−p1)(所有的数,减去有 p p p作为质因子的数)。
由于
φ
(
n
)
\varphi(n)
φ(n)为积性函数,设
n
=
p
1
k
1
p
2
k
2
⋯
p
m
k
m
n=p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m}
n=p1k1p2k2⋯pmkm,(就是质因数分解)
(我不用
∏
\prod
∏是因为个人觉得这种证明,还是展开来写更清晰)
我们有:
φ
(
n
)
=
φ
(
p
1
k
1
)
φ
(
p
2
k
2
)
⋯
φ
(
p
m
k
m
)
=
(
p
1
k
1
p
2
k
2
⋯
p
m
k
m
)
(
(
1
−
1
p
1
)
(
1
−
1
p
2
)
⋯
(
1
−
1
p
m
)
)
=
n
∏
p
∣
n
,
p
i
s
a
p
r
i
m
e
(
1
−
1
p
)
\begin{aligned}\varphi(n)=&\varphi(p_1^{k_1})\varphi(p_2^{k_2})\cdots \varphi(p_m^{k_m})\\=&\left(p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m}\right)\left((1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_m})\right)\\=&n\prod\limits_{p\mid n,p\,is\,a\,prime}(1-\frac{1}{p})\end{aligned}
φ(n)===φ(p1k1)φ(p2k2)⋯φ(pmkm)(p1k1p2k2⋯pmkm)((1−p11)(1−p21)⋯(1−pm1))np∣n,pisaprime∏(1−p1)
得证。
莫比乌斯函数
设
n
=
p
1
q
1
p
2
q
2
p
3
q
3
⋯
p
k
q
k
n=p_1^{q_1}p_2^{q_2}p_3^{q_3}\cdots p_k^{q_k}
n=p1q1p2q2p3q3⋯pkqk,其中
p
p
p为
n
n
n的质因子。
μ
(
n
)
=
{
1
n
=
1
0
(
max
i
=
1
k
q
k
)
⩾
2
(
−
1
)
k
o
t
h
e
r
w
i
s
e
\mu(n)=\begin{cases}1&n=1\\0&(\max\limits_{i=1}^{k}q_k)\geqslant2\\(-1)^k&otherwise\end{cases}
μ(n)=⎩⎪⎪⎨⎪⎪⎧10(−1)kn=1(i=1maxkqk)⩾2otherwise
这是积性函数。
狄利克雷卷积定义
对于两个数论函数
f
(
n
)
,
g
(
n
)
f(n),g(n)
f(n),g(n),定义他们的狄利克雷卷积为
h
(
n
)
=
(
f
∗
g
)
(
n
)
=
∑
d
∣
n
f
(
d
)
g
(
n
d
)
=
∑
a
b
=
n
f
(
a
)
g
(
b
)
h(n)=(f*g)(n)=\sum\limits_{d\mid n}f(d)g(\frac{n}{d})=\sum\limits_{ab=n}f(a)g(b)
h(n)=(f∗g)(n)=d∣n∑f(d)g(dn)=ab=n∑f(a)g(b)
p.s.注意到 ∑ a b = n f ( a ) g ( b ) \sum\limits_{ab=n}f(a)g(b) ab=n∑f(a)g(b)这个式子,它和一般的卷积 ∑ a + b = n f ( a ) g ( b ) \sum\limits_{a+b=n}f(a)g(b) a+b=n∑f(a)g(b)长得非常像。
狄利克雷卷积基础性质
可以看到,有了这些性质,我们似乎就可以将其视为一个群了。
这些性质也使得我们在做题时更加方便。
交换律
( f ∗ g ) ( n ) = ( g ∗ f ) ( n ) (f*g)(n)=(g*f)(n) (f∗g)(n)=(g∗f)(n)
证明:如果用 ∑ a b = n f ( a ) g ( b ) \sum\limits_{ab=n}f(a)g(b) ab=n∑f(a)g(b)这个式子,就是显然的。
结合律
( f ∗ g ) ∗ h = f ∗ ( g ∗ h ) (f*g)*h=f*(g*h) (f∗g)∗h=f∗(g∗h)
证明:考虑展开,易证。算了,我还是写一下吧。
(
(
f
∗
g
)
∗
h
)
(
n
)
=
∑
d
1
d
2
=
n
(
f
∗
g
)
(
d
1
)
h
(
d
2
)
=
∑
d
1
d
2
=
n
(
∑
d
3
d
4
=
d
1
f
(
d
3
)
g
(
d
4
)
)
h
(
d
2
)
=
∑
d
3
d
4
d
2
=
n
f
(
d
3
)
g
(
d
4
)
h
(
d
2
)
=
∑
d
1
d
2
d
3
=
n
f
(
d
1
)
g
(
d
2
)
h
(
d
3
)
\begin{aligned}((f*g)*h)(n)&=\sum\limits_{d_1d_2=n}(f*g)(d_1)\,h(d_2)\\&=\sum\limits_{d_1d_2=n}\left(\sum\limits_{d_3d_4=d_1}f(d_3)g(d_4)\right)h(d_2)\\&=\sum\limits_{d_3d_4d_2=n}f(d_3)g(d_4)h(d_2)\\&=\sum\limits_{d_1d_2d_3=n}f(d_1)g(d_2)h(d_3)\end{aligned}
((f∗g)∗h)(n)=d1d2=n∑(f∗g)(d1)h(d2)=d1d2=n∑(d3d4=d1∑f(d3)g(d4))h(d2)=d3d4d2=n∑f(d3)g(d4)h(d2)=d1d2d3=n∑f(d1)g(d2)h(d3)
那么,
f
∗
(
g
∗
h
)
f*(g*h)
f∗(g∗h)也可以化成这种形式。
推论1:若是 ( f 1 ∗ f 2 ∗ f 3 ∗ ⋯ ∗ f k ) ( n ) (f_1*f_2*f_3*\cdots*f_k)(n) (f1∗f2∗f3∗⋯∗fk)(n),就可直接表示为 ∑ d 1 d 2 d 3 ⋯ d k = n f 1 ( d 1 ) f 2 ( d 2 ) f 3 ( d 3 ) ⋯ f k ( d k ) \sum\limits_{d_1d_2d_3\cdots d_k=n}f_1(d_1)f_2(d_2)f_3(d_3)\cdots f_k(d_k) d1d2d3⋯dk=n∑f1(d1)f2(d2)f3(d3)⋯fk(dk)
推论2:我们可以对狄利克雷卷积进行类似快速幂的操作。姑且称之为快速卷吧。
对加法的分配律
( f + g ) ∗ h = f ∗ h + g ∗ h (f+g)*h=f*h+g*h (f+g)∗h=f∗h+g∗h
证明:展开即可。
(
(
f
+
g
)
∗
h
)
(
n
)
=
∑
a
b
=
n
(
f
+
g
)
(
a
)
h
(
b
)
=
∑
a
b
=
n
(
f
(
a
)
h
(
b
)
+
g
(
a
)
h
(
b
)
)
=
∑
a
b
=
n
f
(
a
)
h
(
b
)
+
∑
a
b
=
n
g
(
a
)
h
(
b
)
=
f
∗
h
+
g
∗
h
\begin{aligned}((f+g)*h)(n)&=\sum\limits_{ab=n}(f+g)(a)\,h(b)\\&=\sum\limits_{ab=n}\left(f(a)h(b)+g(a)h(b)\right)\\&=\sum\limits_{ab=n}f(a)h(b)+\sum\limits_{ab=n}g(a)h(b)\\&=f*h+g*h\end{aligned}
((f+g)∗h)(n)=ab=n∑(f+g)(a)h(b)=ab=n∑(f(a)h(b)+g(a)h(b))=ab=n∑f(a)h(b)+ab=n∑g(a)h(b)=f∗h+g∗h
存在单位元
f ∗ ϵ = ϵ ∗ f = f f*\epsilon=\epsilon*f=f f∗ϵ=ϵ∗f=f
证明:考虑单位函数 ϵ \epsilon ϵ的定义,这就是显然的。
关于积性
若 f , g f,g f,g为积性函数,则 f ∗ g f*g f∗g依旧为积性函数。
证明:
考虑,我们要证
(
f
∗
g
)
(
n
)
=
(
f
∗
g
)
(
a
)
(
f
∗
g
)
(
b
)
∀
(
a
,
b
)
=
1
,
a
b
=
n
(f*g)(n)=(f*g)(a)\,(f*g)(b)\quad\forall(a,b)=1,ab=n
(f∗g)(n)=(f∗g)(a)(f∗g)(b)∀(a,b)=1,ab=n。
对于左式:
(
f
∗
g
)
(
n
)
=
∑
d
∣
n
f
(
d
)
g
(
n
d
)
\begin{aligned}(f*g)(n)&=\sum\limits_{d\mid n}f(d)g(\frac{n}{d})\end{aligned}
(f∗g)(n)=d∣n∑f(d)g(dn)
对于右式:
(
f
∗
g
)
(
a
)
(
f
∗
g
)
(
b
)
=
∑
d
1
∣
a
f
(
d
1
)
g
(
a
d
1
)
∑
d
2
∣
b
f
(
d
2
)
g
(
b
d
2
)
(
1.1
)
=
∑
d
1
∣
a
,
d
2
∣
b
f
(
d
1
)
f
(
d
2
)
g
(
a
d
1
)
g
(
b
d
2
)
(
1.2
)
=
∑
d
1
∣
a
,
d
2
∣
b
f
(
d
1
d
2
)
g
(
a
b
d
1
d
2
)
(
1.3
)
=
∑
d
∣
n
f
(
d
)
g
(
n
d
)
(
1.4
)
\begin{aligned}(f*g)(a)(f*g)(b)&=\sum\limits_{d_1\mid a}f(d_1)g(\frac{a}{d_1})\sum\limits_{d_2\mid b}f(d_2)g(\frac{b}{d_2})&(1.1)\\&=\sum\limits_{d_1\mid a,d_2\mid b}f(d_1)f(d_2)g(\frac{a}{d_1})g(\frac{b}{d_2})&(1.2)\\&=\sum\limits_{d_1\mid a,d_2\mid b}f(d_1d_2)g(\frac{ab}{d_1d_2})&(1.3)\\&=\sum\limits_{d\mid n}f(d)g(\frac{n}{d})&(1.4)\end{aligned}
(f∗g)(a)(f∗g)(b)=d1∣a∑f(d1)g(d1a)d2∣b∑f(d2)g(d2b)=d1∣a,d2∣b∑f(d1)f(d2)g(d1a)g(d2b)=d1∣a,d2∣b∑f(d1d2)g(d1d2ab)=d∣n∑f(d)g(dn)(1.1)(1.2)(1.3)(1.4)
注意到(1.2)可以化到(1.3)的原因是: a a a和 b b b互质,所以 d 1 d_1 d1和 d 2 d_2 d2、 a d 1 \frac{a}{d_1} d1a和 b d 2 \frac{b}{d_2} d2b互质;而 f f f和 g g g又都是积性的。
常用卷积
-
φ ∗ 1 = i d \varphi*1=id φ∗1=id
即 ∑ d ∣ n φ ( d ) = n \sum_{d|n}\varphi(d)=n ∑d∣nφ(d)=n
我们可以在 [ 1 , n ] [1,n] [1,n]这些数和枚举 n n n的每一个约数 d d d时,累加起的每一个与 d d d互质的数中间建立一一对应关系。- i i i可以对应到在算与 n ( n , i ) \frac{n}{(n,i)} (n,i)n互质的数时,累加起来的 i ( n , i ) \frac{i}{(n,i)} (n,i)i
- 在算与 n n n的约数 d d d互质的数的时候累加起的 j j j,可对应到 j ⋅ n d j\cdot\frac{n}{d} j⋅dn
-
μ ∗ 1 = ϵ \mu*1=\epsilon μ∗1=ϵ
首先,当 n = 1 n=1 n=1时,显然成立
当 n > 1 n>1 n>1时,因为 μ \mu μ和 1 1 1都是积性函数,因此 μ ∗ 1 \mu*1 μ∗1也是积性函数。
同时,当 n n n质因数分解后,有质因数的次数大于1, μ ( n ) \mu(n) μ(n)就会等于0。
所以我们只用证明当 n n n为质数时, ∑ d ∣ n μ ( d ) = 0 \sum_{d|n}\mu(d)=0 ∑d∣nμ(d)=0就行了。
∑ d ∣ n μ ( d ) = μ ( 1 ) + μ ( n ) = 1 + ( − 1 ) 1 = 0 \sum_{d|n}\mu(d)=\mu(1)+\mu(n)=1+(-1)^1=0 ∑d∣nμ(d)=μ(1)+μ(n)=1+(−1)1=0 -
i d ∗ 1 = σ 1 ∗ 1 = d id*1=\sigma\qquad1*1=d id∗1=σ1∗1=d( i d k ∗ 1 = σ k id_k*1=\sigma_k idk∗1=σk)
根据定义,显然成立 -
其他
比如 φ ∗ d = φ ∗ 1 ∗ 1 = i d ∗ 1 = σ \varphi*d=\varphi*1*1=id*1=\sigma φ∗d=φ∗1∗1=id∗1=σ
卷积计算方法
我可能会混用
f
(
i
)
f(i)
f(i)这种偏向函数的表述方式和
f
i
f_i
fi这种偏向数列的表述方式。他们是相同的含义,看得懂就好。好吧,我基本上没有这么用
我们先做出约定:现在我们给定序列
f
i
f_i
fi和
g
i
g_i
gi,下标从
1
1
1到
n
n
n。
现在,我们要求序列
h
i
h_i
hi,下标从
1
1
1到
n
n
n。其中
h
(
n
)
=
∑
d
∣
n
f
(
d
)
g
(
n
d
)
h(n)=\sum\limits_{d\mid n}f(d)g(\frac{n}{d})
h(n)=d∣n∑f(d)g(dn)
最暴力的暴力
我们考虑,可以直接对于每一个
i
i
i,枚举所有小于
i
i
i的正整数
j
j
j,并判断
j
j
j是否为
i
i
i的约数。
如果是,就将他的贡献
f
(
j
)
g
(
i
j
)
f(j)g(\frac{i}{j})
f(j)g(ji)加到
h
(
i
)
h(i)
h(i)上。
这个暴力一看就很丑,很明显,时间是 O ( n 2 ) O(n^2) O(n2)的,很没用。我们考虑优化。
稍好的暴力
有一个从初学判单个素数时,就知道的结论:一个正整数 n n n,只会有 O ( n ) O(\sqrt{n}) O(n)个约数。并且,约数几乎是成对出现的(除了完全平方数)。因此,每一对中较小的那个总是小于 n \sqrt{n} n的。
我们大可对于每个 i i i,只枚举1到 i \sqrt{i} i这几个数,遇到一个约数 j j j,就把这一对约数的贡献 f ( j ) g ( i j ) + f ( i j ) g ( j ) f(j)g(\frac{i}{j})+f(\frac{i}{j})g(j) f(j)g(ji)+f(ji)g(j)加到 h ( i ) h(i) h(i)上面(注意考虑当 j = i j=\sqrt{i} j=i时,就只用加一个了)。
这样虽然还是很丑,但是已经做到了 O ( n n ) O(n\sqrt{n}) O(nn)的复杂度了。
优美的暴力
做法
我们考虑换一个方向,不从
i
i
i去找能贡献
h
(
i
)
h(i)
h(i),而是从
j
j
j去找,
j
j
j是哪些
i
i
i的约数。
具体地,我们只要每一次加上
j
j
j,就可以了。算贡献的时候,式子与最暴力的暴力相同。
复杂度分析
粗略一看,这个的时间很不对。好像,都要到
O
(
n
2
)
O(n^2)
O(n2)了。但是,真的是这样的吗?
我们考虑,对于每一个
j
j
j,在每一回都加上
j
j
j的过程中,只用做
⌈
n
j
⌉
\left\lceil\frac{n}{j}\right\rceil
⌈jn⌉次。
因此,它的总时间复杂度就是:
∑
i
=
1
n
⌈
n
i
⌉
⩽
∑
i
=
1
n
(
n
i
+
1
)
=
n
+
n
∑
i
=
1
n
1
i
=
n
+
n
H
(
n
)
\begin{aligned}\sum\limits_{i=1}^{n}\left\lceil\frac{n}{i}\right\rceil&\leqslant\sum\limits_{i=1}^{n}\left(\frac{n}{i}+1\right)\\&=n+n\sum\limits_{i=1}^{n}\frac{1}{i}\\&=n+nH(n)\end{aligned}
i=1∑n⌈in⌉⩽i=1∑n(in+1)=n+ni=1∑ni1=n+nH(n)
其中
H
(
n
)
H(n)
H(n)表示调和级数的第
n
n
n项。
tip.调和级数
H
(
n
)
=
∑
i
=
1
n
1
i
=
l
n
(
n
)
+
γ
+
ϵ
n
H(n)=\sum\limits_{i=1}^{n}\frac{1}{i}=ln(n)+\gamma+\epsilon_n
H(n)=i=1∑ni1=ln(n)+γ+ϵn
其中,
γ
\gamma
γ表示欧拉-马歇罗尼常数,约为0.577,
ϵ
n
\epsilon_n
ϵn约为
1
2
n
\frac{1}{2n}
2n1。可以看到,这两者在OI数据范围下皆可忽略。
因此,
H
(
n
)
≈
l
n
n
H(n)\approx ln\,n
H(n)≈lnn。
这个方法已经十分优秀了,是 O ( n H ( n ) ) O(nH(n)) O(nH(n))的。
一个毫无用处的讨论
话说我在想到这个算法之后,欣喜万分。
但是,我随即又口胡出了一个优化,而且,好像,加上这个优化后,会更快。
做法是这样的:
考虑到稍好的暴力,我们发现我们只要做前
n
\sqrt{n}
n个。
那么这里,我们同样只用做前
n
\sqrt{n}
n个。
具体的,就是在统计
j
j
j作为约数的过程中,从
j
2
j^2
j2开始统计,以避免重复。
并使用稍好的暴力的式子进行计算贡献。
用类似之前的方法,复杂度分析出来,是这样的:
O
(
n
H
(
n
)
)
O(nH(\sqrt{n}))
O(nH(n))
欸,好激动啊。
但是,对数里的那个根号,好像,只会让结果除以二欸。
然后我们再看,发现其实他的常数是又大了2的。
所以,我的这个优化并没有什么用。
这是可以理解的,因为对于原来的方法和现在的,他的每一步都没有浪费,每一遍循环都能恰好找到合法的转移。所以说,这个优化唯一的作用,大概就是类似循环展开的吧,而且只有2层。
最后的讨论
经过前面一个毫无用处的讨论,我决定再好好审视一下现在这个算法。
我发现,现在已经可以做到每一次的循环全部起到作用,不会再有循环到了一个
a
a
a和
b
b
b,却发现这是不合法的或者此前已经算过了。
也就是说,如果还是一个一个地从
f
,
g
f,g
f,g算到
h
h
h,是不会再有比这个更快的算法了。
那么,唯一的突破方案,就只有用类似杜教筛或者min25筛那样的方法,把一些信息并在一起了吧。
这个算法复杂度是
O
(
n
H
(
n
)
)
O(nH(n))
O(nH(n))的,而且常数较小。已经挺接近O(n)了。
起码,五六百万还是可以轻松跑过的。
而且,我最后提出的那一个毫无用处的讨论,其实还是有点用的。
那样会好写一点,能少几个if语句。
代码
这个代码就是我依据我那个毫无意义的讨论得出的算法写出来的。
#include<bits/stdc++.h>
#define LL long long
#define MOD 998244353
#define MAXN 5000000
using namespace std;
template<typename T>void Read(T &cn)
{
char c;int sig = 1;
while(!isdigit(c = getchar()))if(c == '-')sig = -1;cn = c-48;
while(isdigit(c = getchar()))cn = cn*10+c-48;cn*=sig;
}
template<typename T>void Write(T cn)
{
if(!cn){putchar('0');return;}
if(cn<0){putchar('-');cn = 0-cn;}
int wei = 0;T cm = 0;int cx = cn%10;cn=cn/10;
while(cn)wei++,cm = cm*10+cn%10,cn=cn/10;
while(wei--)putchar(cm%10+48),cm=cm/10;
putchar(cx+48);
}
LL f[MAXN+1],g[MAXN+1],h[MAXN+1];
int n;
template<typename T>void getit(T a[],int n)
{
for(int i = 1;i<=n;i++)Read(a[i]);
}
template<typename T>void ran_getit(T a[],int n)
{
for(int i = 1;i<=n;i++)a[i] = i;
}
template<typename T>void outit(T a[],int n)
{
for(int i = 1;i<=n;i++)
{
Write(a[i]);putchar(' ');
}
putchar('\n');
}
void juan_ji(LL f[],LL g[],LL h[],int n)
{
memset(h,0,sizeof(h[0])*(n+1));
int Sqr = sqrt(n);
for(int i = 1;i<=Sqr;i++)
{
h[i*i] = (h[i*i] + f[i]*g[i]%MOD)%MOD;
for(int j = i+1;j*i<=n;j++)
{
h[i*j] = (h[i*j] + f[i]*g[j]%MOD + f[j]*g[i]%MOD)%MOD;
}
}
}
int main()
{
Read(n);
getit(f,n);
getit(g,n);
juan_ji(f,g,h,n);
outit(h,n);
return 0;
}
例题
此处就给出一道题(来自czgj的那个课件)
给出一个序列
f
(
1
)
f(1)
f(1)到
f
(
n
)
f(n)
f(n)和
k
k
k,定义
f
k
(
n
)
=
∑
d
1
d
2
⋯
d
k
=
n
f
(
d
1
)
f
(
d
2
)
⋯
f
(
d
n
)
f_k(n)=\sum\limits_{d_1d_2\cdots d_k=n}f(d_1)f(d_2)\cdots f(d_n)
fk(n)=d1d2⋯dk=n∑f(d1)f(d2)⋯f(dn)
求
f
k
(
1
)
,
f
k
(
2
)
⋯
f
k
(
n
)
f_k(1),f_k(2)\cdots f_k(n)
fk(1),fk(2)⋯fk(n)。
解:不难发现,这就是 f f f序列 k k k次卷积后的结果。那么,我们可以直接利用快速幂的方法以及上文介绍的卷积方法,就可以做到 O ( n H ( n ) l o g 2 k ) O(nH(n)log_2k) O(nH(n)log2k)的复杂度,很好了。
结语
关于这个算法的思考告诉我了一件事——任何一个,哪怕是很辣鸡的算法,也都要定下心来,好好分析。或许,就有非常奇妙的事情发生。(莫名鸡汤)
莫比乌斯反演(待填坑)
此坑太大,先放着不填。
杜教筛
杜教筛是个好东西,它是一种特殊的求和方法,而其中的关键就是狄利克雷卷积。
可以这么说,杜教筛是目前OI界,最成熟的算法层面的狄利克雷卷积的应用吧。
但是我还是太弱的,并没有非常好地掌握狄利克雷卷积的知识,所以,先把坑放在这儿。
u p d a t e 2019.07.08 : update2019.07.08: update2019.07.08: 我学成归来,开始填坑了。
u p d a t e 2019.09.22 : update2019.09.22: update2019.09.22: 我发现分类出了bug,其实我原称的“第二种杜教筛”和“第三种杜教筛”,都是经典的应用特例,故现在改称“杜教筛应用一”“杜教筛应用二”。你不高兴看大可以不看。
约定
我们约定
f
(
i
)
,
g
(
i
)
,
h
(
i
)
f(i),g(i),h(i)
f(i),g(i),h(i)为三个数论函数,可能是给定的或者自己找的。
F
(
i
)
,
G
(
i
)
,
H
(
i
)
F(i),G(i),H(i)
F(i),G(i),H(i)分别为
f
(
i
)
,
g
(
i
)
,
h
(
i
)
f(i),g(i),h(i)
f(i),g(i),h(i)的前缀和。(栗子:
F
(
i
)
=
∑
i
=
1
n
f
(
i
)
F(i)=\sum\limits_{i=1}^{n}f(i)
F(i)=i=1∑nf(i))
要求的东西统一称作
S
(
n
)
S(n)
S(n)。
经典杜教筛
描述
给定一个数论函数 f ( i ) f(i) f(i),求 S ( n ) = F ( i ) = ∑ i = 1 n f ( i ) S(n)=F(i)=\sum\limits_{i=1}^{n}f(i) S(n)=F(i)=i=1∑nf(i)
推导
假设我们能找到两个数论函数
g
(
n
)
,
h
(
n
)
g(n),h(n)
g(n),h(n),使得
h
(
n
)
=
∑
d
∣
n
g
(
d
)
f
(
n
d
)
h(n)=\sum\limits_{d|n}g(d)f(\frac{n}{d})
h(n)=d∣n∑g(d)f(dn)
那么
H
(
n
)
=
∑
i
=
1
n
h
(
i
)
(
2.1
)
=
∑
i
=
1
n
∑
d
∣
i
g
(
d
)
f
(
i
d
)
(
2.2
)
=
∑
i
=
1
n
g
(
i
)
∑
i
j
⩽
n
f
(
j
)
(
2.3
)
=
∑
i
=
1
n
g
(
i
)
F
(
n
i
)
(
2.4
)
=
g
(
1
)
F
(
n
)
+
∑
i
=
2
n
g
(
i
)
F
(
⌊
n
i
⌋
)
(
2.5
)
\begin{aligned}H(n)&=\sum\limits_{i=1}^{n}h(i)&(2.1)\\&=\sum\limits_{i=1}^{n}\sum\limits_{d|i}g(d)f\left(\frac{i}{d}\right)&(2.2)\\&=\sum\limits_{i=1}^{n}g(i)\sum\limits_{ij\leqslant n}f(j)&(2.3)\\&=\sum\limits_{i=1}^{n}g(i)F\left(\frac{n}{i}\right)&(2.4)\\&=g(1)F(n)+\sum\limits_{i=2}^{n}g(i)F\left(\left\lfloor\frac{n}{i}\right\rfloor\right)&(2.5)\end{aligned}
H(n)=i=1∑nh(i)=i=1∑nd∣i∑g(d)f(di)=i=1∑ng(i)ij⩽n∑f(j)=i=1∑ng(i)F(in)=g(1)F(n)+i=2∑ng(i)F(⌊in⌋)(2.1)(2.2)(2.3)(2.4)(2.5)
(
(
2.2
)
(2.2)
(2.2)到
(
2.3
)
(2.3)
(2.3)是最妙的地方,考虑对于每一个
g
(
i
)
g(i)
g(i),我们会把它和哪些
f
(
i
)
f(i)
f(i)相乘)
(
(
2.2
)
(2.2)
(2.2)到
(
2.4
)
(2.4)
(2.4)这一类的变换在下文中会出现多次,之后就不写全了)
所以
g
(
1
)
F
(
n
)
=
H
(
n
)
−
∑
i
=
2
n
g
(
i
)
F
(
⌊
n
i
⌋
)
(
2.6
)
g(1)F(n)=H(n)-\sum\limits_{i=2}^{n}g(i)F(\left\lfloor\frac{n}{i}\right\rfloor)\qquad(2.6)
g(1)F(n)=H(n)−i=2∑ng(i)F(⌊in⌋)(2.6)
我们假设你找到了一个很好的函数 g ( n ) g(n) g(n),使得 H ( n ) , G ( n ) H(n),G(n) H(n),G(n)都可以快速计算。我们就可以递归地去计算 F ( n ) F(n) F(n)了。
注意到对于 n n n, ⌊ n i ⌋ \left\lfloor\frac{n}{i}\right\rfloor ⌊in⌋只会有 O ( n ) O(\sqrt{n}) O(n)种取值,所以 ( 2.6 ) (2.6) (2.6)中的那个 ∑ i = 2 n g ( i ) F ( ⌊ n i ⌋ ) \sum_{i=2}^{n}g(i)F(\left\lfloor\frac{n}{i}\right\rfloor) ∑i=2ng(i)F(⌊in⌋)是可以 O ( n ) O(\sqrt{n}) O(n)算的。
具体来说,对于每一种 ⌊ n i ⌋ \left\lfloor\frac{n}{i}\right\rfloor ⌊in⌋的取值(假设从 i 1 i_1 i1到 i 2 i_2 i2, ⌊ n i ⌋ \left\lfloor\frac{n}{i}\right\rfloor ⌊in⌋是同一个值),都是 F ( ⌊ n i ⌋ ) ∑ i 1 i 2 g ( i ) F(\left\lfloor\frac{n}{i}\right\rfloor)\sum_{i_1}^{i_2} g(i) F(⌊in⌋)∑i1i2g(i)的形式。而 ∑ i 1 i 2 g ( i ) \sum_{i_1}^{i_2} g(i) ∑i1i2g(i)是可以利用 G G G快速求的。
进一步优化,我们可以利用线性筛预处理 1 1 1到 n 2 3 n^{\frac{2}{3}} n32的 f ( i ) f(i) f(i)值,如果询问到已经预处理出来的 F F F值,就可以不用往下递归了(至于为什么是 n 2 3 n^{\frac{2}{3}} n32,在底下的复杂度分析里有讲)。
时间复杂度
(我们假设
H
H
H和
G
G
G都可以
O
(
1
)
O(1)
O(1)求)
先分析
(
2.6
)
(2.6)
(2.6)式(就是先不预处理前
n
2
3
n^{\frac{2}{3}}
n32个)
我们发现,如果要求
F
(
n
)
F(n)
F(n),那我们要求出所有的
F
(
⌊
n
i
⌋
)
F(\left\lfloor\frac{n}{i}\right\rfloor)
F(⌊in⌋),这会有
O
(
n
)
O(\sqrt{n})
O(n)种取值。这
O
(
n
)
O(\sqrt{n})
O(n)个值,如果我们要求出来,看起来是还要用其他
F
F
F值的。但是,感性理解一下,你会发现你就只会用到这
O
(
n
)
O(\sqrt{n})
O(n)个值了。
只要证明对于
⌊
n
i
⌋
\left\lfloor\frac{n}{i}\right\rfloor
⌊in⌋,所有的
⌊
⌊
n
i
⌋
j
⌋
\left\lfloor\frac{\left\lfloor\frac{n}{i}\right\rfloor}{j}\right\rfloor
⌊j⌊in⌋⌋都与
⌊
n
i
j
⌋
\left\lfloor\frac{n}{ij}\right\rfloor
⌊ijn⌋相等就行了。
你可以感性理解这件事,也可以证明一下。
在这里我给出我的证明:
设
n
=
q
∗
i
j
+
r
(
r
<
i
j
)
n=q*ij+r(r<ij)
n=q∗ij+r(r<ij),很明显,
⌊
n
i
j
⌋
=
q
\left\lfloor\frac{n}{ij}\right\rfloor=q
⌊ijn⌋=q
代入,化简,可以发现:
⌊
n
i
⌋
=
q
∗
j
+
⌊
r
i
⌋
\left\lfloor\frac{n}{i}\right\rfloor=q*j+\left\lfloor\frac{r}{i}\right\rfloor
⌊in⌋=q∗j+⌊ir⌋
则
⌊
⌊
n
i
⌋
j
⌋
=
q
+
⌊
⌊
r
i
⌋
j
⌋
=
q
\left\lfloor\frac{\left\lfloor\frac{n}{i}\right\rfloor}{j}\right\rfloor=q+\left\lfloor\frac{\left\lfloor\frac{r}{i}\right\rfloor}{j}\right\rfloor=q
⌊j⌊in⌋⌋=q+⌊j⌊ir⌋⌋=q
证讫。
那么,我们对于这些取值,分别算贡献即可。随便算一波,大概会视你的姿势水平高低,得到一个 n 5 6 n^{\frac{5}{6}} n65之类的式子,而且你深知这个上界大大夸大了。不过这不重要,因为这种不预处理前缀的写法不是杜教筛的通常写法,他太慢了。
那接下来讲讲预处理前缀的分析。
我们设求前
z
z
z个的值(这里设一下,是为了说明为什么预处理前
n
2
3
n^{\frac{2}{3}}
n32个是最优的)
那么复杂度有两部分,一部分是预处理,是
O
(
z
)
O(z)
O(z)的。
另一部分,是求没有预处理出来的那些
F
F
F的复杂度。我们假设每一次都重新算一遍,这样会得到上限。
∑
i
=
2
n
z
n
i
(
3.1
)
⩽
∑
i
=
1
n
z
n
i
(
3.2
)
=
n
∑
i
=
1
n
z
i
−
1
2
(
3.3
)
⩽
2
n
⋅
(
n
z
)
1
2
(
3.4
)
=
2
⋅
n
z
(
3.5
)
\begin{aligned}&\sum\limits_{i=2}^{\frac{n}{z}}\sqrt{\frac{n}{i}}\qquad&(3.1)\\\leqslant&\sum\limits_{i=1}^{\frac{n}{z}}\sqrt{\frac{n}{i}}&(3.2)\\=&\sqrt{n}\sum\limits_{i=1}^{\frac{n}{z}}i^{-\frac{1}{2}}&(3.3)\\\leqslant&2\sqrt{n}\cdot \left(\frac{n}{z}\right)^{\frac{1}{2}}&(3.4)\\=&2\cdot\frac{n}{\sqrt{z}}&(3.5)\end{aligned}
⩽=⩽=i=2∑znini=1∑zninni=1∑zni−212n⋅(zn)212⋅zn(3.1)(3.2)(3.3)(3.4)(3.5)
注意到
(
3.3
)
(3.3)
(3.3)到
(
3.4
)
(3.4)
(3.4)用了一步积分。
那复杂度为
O
(
z
+
n
⋅
z
−
1
2
)
O(z+n\cdot z^{-\frac{1}{2}})
O(z+n⋅z−21),显然,
z
z
z取
n
2
3
n^{\frac{2}{3}}
n32时最优。为
O
(
n
2
3
)
O(n^{\frac{2}{3}})
O(n32)
事实上,在这个
z
z
z的取值下,
1
⋯
n
z
1\cdots \frac{n}{z}
1⋯zn每一个
⌊
n
i
⌋
\left\lfloor\frac{n}{i}\right\rfloor
⌊in⌋的值都不同,所以这个时间复杂度很对。
在实际使用的时候,你可以视两部分的常数差异而微调
z
z
z的取值。
代码
是这道题:Luogu P4213 【模板】杜教筛(Sum)
注意两处(我第一遍写的时候没有处理好):
- 一定要记忆化起来,可以用unordered_map,或者直接用(总数的n)/(当前这个数cn)作为下标存起来,但是map会t到怀疑人生
- 因为这是多组询问,预处理可以适当多一点。(当然,你也可以根据输入的n去算要预处理多少)
#include<bits/stdc++.h>
#define LL long long
#define MAXN 3000000
#define INF 2100000000
#define MAXM 2000
using namespace std;
template<typename T>void Read(T &cn)
{
char c;int sig = 1;
while(!isdigit(c = getchar()))if(c == '-')sig = -1;cn = c-48;
while(isdigit(c = getchar()))cn = cn*10+c-48;cn*=sig;
}
template<typename T>void Write(T cn)
{
if(!cn){putchar('0');return;}
if(cn<0){putchar('-');cn = 0-cn;}
int wei = 0;T cm = 0;int cx = cn%10;cn=cn/10;
while(cn)wei++,cm = cm*10+cn%10,cn=cn/10;
while(wei--)putchar(cm%10+48),cm=cm/10;
putchar(cx+48);
}
int n,t;
int f1[MAXN+1],f2[MAXN+1],F2[MAXN+1];
LL F1[MAXN+1];
int pri[MAXN+1],ssh[MAXN+1],xiao[MAXN+1],plen;
LL M1[MAXM+1];
int M2[MAXM+1];
void yuchu(int cn)
{
memset(ssh,0,sizeof(ssh)); plen = 0; ssh[1] = 1;
f1[1] = f2[1] = 1;
for(int i = 2;i<=cn;i++)
{
if(!ssh[i]){
pri[++plen] = i;
xiao[i] = plen;
f1[i] = i-1; f2[i] = -1;
for(int j = i;1ll*i*j<=cn;j=j*i)xiao[i*j] = plen,ssh[i*j] = 1,f1[i*j] = i*j-j,f2[i*j] = 0;
}
for(int j = 1;j<xiao[i] && 1ll*pri[j]*i<=cn;j++)
{
for(int k = pri[j];1ll*k*i<=cn;k = k*pri[j])xiao[k*i] = j,ssh[k*i] = 1,f1[k*i] = f1[k]*f1[i],f2[k*i] = f2[k]*f2[i];
}
}
F1[0] = F2[0] = 0;
for(int i = 1;i<=cn;i++)F1[i] = F1[i-1]+f1[i],F2[i] = F2[i-1]+f2[i];
}
LL djs1(int cn)
{
if(cn <= MAXN)return F1[cn];
if(M1[n/cn] != -INF)return M1[n/cn];
LL guo = 1ll*cn*(cn+1)/2;
for(int l = 2,r;l<=cn;l = r+1)
{
r = cn/(cn/l);
guo = guo - (r-l+1) * djs1(cn/l);
}
return M1[n/cn] = guo;
}
LL djs2(int cn)
{
if(cn <= MAXN)return F2[cn];
if(M2[n/cn] != -INF)return M2[n/cn];
int guo = 1;
for(int l = 2,r;l<=cn;l = r+1)
{
r = cn/(cn/l);
guo = guo - (r-l+1)* djs2(cn/l);
}
return M2[n/cn] = guo;
}
int main()
{
yuchu(MAXN);
Read(t);
while(t--)
{
Read(n);
for(int i = 0;i<=n/MAXN;i++)M1[i] = -INF,M2[i] = -INF;
Write(djs1(n)); putchar(' '); Write(djs2(n)); putchar('\n');
}
return 0;
}
然后,这边还有一种写法,好像要快一点
LL djs1(int cn)
{
if(cn <= MAXN)return F1[cn];
if(M1[n/cn] != -INF)return M1[n/cn];
LL guo = 1ll*cn*(cn+1)/2,lin = floor(sqrt(cn));
for(int i = 1;i<=lin;i++)guo = guo - djs1(i)*(cn/i - cn/(i+1));
lin = cn/(lin+1);
for(int i = 2;i<=lin;i++)guo = guo - djs1(cn/i);
return M1[n/cn] = guo;
}
杜教筛应用
杜教筛应用一
描述
求
S
(
n
)
=
∑
i
=
1
n
f
(
i
)
g
(
i
)
S(n)=\sum\limits_{i=1}^{n}f(i)g(i)
S(n)=i=1∑nf(i)g(i)
通常有
g
g
g为完全积性函数
推导
首先说一句,事实上你可以把
f
(
i
)
g
(
i
)
f(i)g(i)
f(i)g(i)看成一个新函数
f
′
(
i
)
f'(i)
f′(i),然后用经典方法找到一个对应的
g
′
(
i
)
g'(i)
g′(i)求和。
这里单独列出来讨论,是为了一般性地思考这个问题。
经典推导(雾
我们以一个特殊的例子来说明,以此来体现这到底是个什么过程
求: S ( n ) = ∑ i = 1 n φ ( i ) i S(n)=\sum\limits_{i=1}^{n}\varphi(i)i S(n)=i=1∑nφ(i)i
没什么头绪,因为我们不知道应该卷什么。
那我们先试一试吧。
设
f
(
n
)
=
n
⋅
φ
(
n
)
,
h
(
n
)
=
f
∗
g
f(n)=n\cdot\varphi(n),h(n)=f*g
f(n)=n⋅φ(n),h(n)=f∗g
h
(
n
)
=
∑
d
∣
n
f
(
d
)
g
(
n
d
)
=
∑
d
∣
n
φ
(
d
)
⋅
d
⋅
g
(
n
d
)
\begin{aligned}h(n)=&\sum\limits_{d|n}f(d)g(\frac{n}{d})\\=&\sum\limits_{d|n}\varphi(d)\cdot d\cdot g(\frac{n}{d})\end{aligned}
h(n)==d∣n∑f(d)g(dn)d∣n∑φ(d)⋅d⋅g(dn)
我们发现,
g
g
g如果取
i
d
id
id函数(
i
d
(
n
)
=
n
id(n)=n
id(n)=n)就会血赚。
就有
h
(
n
)
=
∑
d
∣
n
φ
(
d
)
n
=
n
2
h(n)=\sum_{d|n}\varphi(d)n=n^2
h(n)=∑d∣nφ(d)n=n2
则
H
(
n
)
=
∑
i
=
1
n
i
2
=
n
(
n
+
1
)
(
2
n
+
1
)
6
H(n)=\sum_{i=1}^n i^2=\frac{n(n+1)(2n+1)}{6}
H(n)=∑i=1ni2=6n(n+1)(2n+1)
套用前文所述即可。
一般性推导
我们设
f
′
(
n
)
=
f
(
n
)
g
(
n
)
,
h
(
n
)
=
∑
d
∣
n
f
(
d
)
g
(
d
)
g
′
(
n
d
)
f'(n)=f(n)g(n),h(n)=\sum\limits_{d|n}f(d)g(d)g'(\frac{n}{d})
f′(n)=f(n)g(n),h(n)=d∣n∑f(d)g(d)g′(dn)
此时,我们很想把
g
′
g'
g′直接定为
g
g
g,然后把
g
(
d
)
g
′
(
n
d
)
g(d)g'(\frac{n}{d})
g(d)g′(dn)直接并为
g
(
n
)
g(n)
g(n)。
什么时候能这么做?
当
g
g
g为完全积性函数的时候。
因此我们才有了一开始描述中的那个条件:
g
g
g为完全积性函数。
所以,
H
(
n
)
=
∑
i
=
1
n
g
(
i
)
∑
d
∣
i
f
(
d
)
=
∑
i
=
1
n
g
(
i
)
⋅
(
f
∗
1
)
(
i
)
H(n)=\sum\limits_{i=1}^{n}g(i)\sum\limits_{d|i}f(d)=\sum\limits_{i=1}^{n}g(i)\cdot(f*1)(i)
H(n)=i=1∑ng(i)d∣i∑f(d)=i=1∑ng(i)⋅(f∗1)(i)
(这个式子是为了快速求
H
(
n
)
H(n)
H(n)推的,显然,他需要
f
f
f有很好的性质)
同时,
H
(
n
)
=
∑
i
=
1
n
h
(
i
)
=
∑
i
=
1
n
∑
d
∣
i
f
′
(
d
)
g
(
i
d
)
=
∑
i
=
1
n
g
(
i
)
S
(
⌊
n
i
⌋
)
H(n)=\sum\limits_{i=1}^{n}h(i)=\sum\limits_{i=1}^{n}\sum\limits_{d|i}f'(d)g(\frac{i}{d})=\sum\limits_{i=1}^{n}g(i)S(\left\lfloor\frac{n}{i}\right\rfloor)
H(n)=i=1∑nh(i)=i=1∑nd∣i∑f′(d)g(di)=i=1∑ng(i)S(⌊in⌋)
我们就有
S
(
n
)
=
H
(
n
)
−
∑
i
=
2
n
g
(
i
)
S
(
⌊
n
i
⌋
)
S(n)=H(n)-\sum\limits_{i=2}^{n}g(i)S(\left\lfloor\frac{n}{i}\right\rfloor)
S(n)=H(n)−i=2∑ng(i)S(⌊in⌋)
就可以求了。
我们还可以发现, h ( n ) = ( f ∗ 1 ) ( n ) ⋅ g ( n ) h(n)=(f*1)(n)\cdot g(n) h(n)=(f∗1)(n)⋅g(n)这样就比较好看了。
然后,仿佛这个东西只能求一下 f ( n ) = φ ( n ) , g ( n ) = n d f(n)=\varphi(n),g(n)=n^d f(n)=φ(n),g(n)=nd
u p d a t e : update: update: 其实带 ϵ \epsilon ϵ的也是能求的。
杜教筛应用二
描述
求 S ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) S(n)=\sum\limits_{i=1}^{n}(f*g)(i) S(n)=i=1∑n(f∗g)(i)
推导
这一种好像就比较厉害了(并没有)。
先扯一种特殊情况:
我们相当于在经典杜教筛里能快速求
F
,
G
F,G
F,G,想利用这两个求出
H
H
H。
那就可以直接
S
(
n
)
=
∑
i
=
1
n
∑
d
∣
i
f
(
d
)
g
(
i
d
)
=
∑
i
=
1
n
f
(
i
)
G
(
⌊
n
i
⌋
)
S(n)=\sum\limits_{i=1}^{n}\sum\limits_{d|i}f(d)g(\frac{i}{d})=\sum\limits_{i=1}^{n}f(i)G(\left\lfloor\frac{n}{i}\right\rfloor)
S(n)=i=1∑nd∣i∑f(d)g(di)=i=1∑nf(i)G(⌊in⌋),在
O
(
n
)
O(\sqrt{n})
O(n)的时间内求出。
但是这太受限了。
首先,我们设
h
(
n
)
=
(
f
∗
1
)
(
n
)
h(n)=(f*1)(n)
h(n)=(f∗1)(n)。
我们可以推出来这样一个式子:
∑
i
=
1
n
S
(
⌊
n
i
⌋
)
(
4.1
)
=
∑
i
=
1
n
∑
d
∣
i
(
f
∗
g
)
(
d
)
(
4.2
)
=
∑
i
=
1
n
∑
d
1
∣
i
∑
d
2
∣
d
1
g
(
d
2
)
f
(
d
1
d
2
)
(
4.3
)
=
∑
i
=
1
n
∑
d
2
∣
i
g
(
d
2
)
∑
d
3
∣
i
d
2
f
(
d
3
)
(
4.4
)
=
∑
i
=
1
n
∑
d
∣
i
g
(
d
)
⋅
(
f
∗
1
)
(
i
d
)
(
4.5
)
=
∑
i
=
1
n
g
(
i
)
H
(
⌊
n
i
⌋
)
(
4.6
)
\begin{aligned}&\sum_{i=1}^{n}S\left(\left\lfloor\frac{n}{i}\right\rfloor\right)\qquad&(4.1)\\=&\sum_{i=1}^{n}\sum_{d|i}(f*g)(d)&(4.2)\\=&\sum_{i=1}^{n}\sum_{d_1|i}\sum_{d_2|d_1}g(d_2)f\left(\frac{d_1}{d_2}\right)&(4.3)\\=&\sum_{i=1}^{n}\sum_{d_2|i}g(d_2)\sum_{d_3|\frac{i}{d_2}}f(d_3)&(4.4)\\=&\sum_{i=1}^{n}\sum_{d|i}g(d)\cdot(f*1)\left(\frac{i}{d}\right)&(4.5)\\=&\sum_{i=1}^{n}g(i)H\left(\left\lfloor\frac{n}{i}\right\rfloor\right)&(4.6)\end{aligned}
=====i=1∑nS(⌊in⌋)i=1∑nd∣i∑(f∗g)(d)i=1∑nd1∣i∑d2∣d1∑g(d2)f(d2d1)i=1∑nd2∣i∑g(d2)d3∣d2i∑f(d3)i=1∑nd∣i∑g(d)⋅(f∗1)(di)i=1∑ng(i)H(⌊in⌋)(4.1)(4.2)(4.3)(4.4)(4.5)(4.6)
这就很杜教筛了。
继续往下:
∑
i
=
1
n
S
(
⌊
n
i
⌋
)
=
∑
i
=
1
n
g
(
i
)
H
(
⌊
n
i
⌋
)
S
(
n
)
=
∑
i
=
1
n
g
(
i
)
H
(
⌊
n
i
⌋
)
−
∑
i
=
2
n
S
(
⌊
n
i
⌋
)
\begin{aligned}\sum_{i=1}^{n}S\left(\left\lfloor\frac{n}{i}\right\rfloor\right)&=\sum_{i=1}^{n}g(i)H\left(\left\lfloor\frac{n}{i}\right\rfloor\right)\\S(n)&=\sum_{i=1}^{n}g(i)H\left(\left\lfloor\frac{n}{i}\right\rfloor\right)-\sum_{i=2}^{n}S\left(\left\lfloor\frac{n}{i}\right\rfloor\right)\end{aligned}
i=1∑nS(⌊in⌋)S(n)=i=1∑ng(i)H(⌊in⌋)=i=1∑ng(i)H(⌊in⌋)−i=2∑nS(⌊in⌋)
这样,如果
H
,
G
H,G
H,G都是能快速求出的,
S
(
n
)
S(n)
S(n)就能在
O
(
n
2
3
)
O(n^{\frac{2}{3}})
O(n32)的复杂度内求出。
你可能会觉得,这还是没什么用。但是这就可以搞 f f f为 φ \varphi φ了。(尽管这也是可以用经典杜教筛做的,用 S ( n ) = ∑ i = 1 n g ( i ) F ( ⌊ n i ⌋ ) S(n)=\sum_{i=1}^{n}g(i)F\left(\left\lfloor\frac{n}{i}\right\rfloor\right) S(n)=∑i=1ng(i)F(⌊in⌋)一边弄 F F F一边弄 S S S)
杜教筛总结
你也许已经看出来这两个应用的本质了:应用一就是卷上了
g
g
g,应用二就是卷上了
1
1
1。
而这一通操作的理由,都是为了计算出
H
(
n
)
H(n)
H(n)。
我之所以还没有删掉他们,就是因为这算是比较好的两个应用实例,公式推到一半,熟练的话就可以直接看出来这样就行了,不用继续去试了。
综上所述,杜教筛的要义就是:
- 你想求前缀和
- 你把求前缀和的东西整个看成一个函数 f f f,二话不说,先卷起来
- 你开始琢磨 H H H和 G G G怎么求会比较简单。
这件事情就给一些杜教筛套min25的毒瘤留下了迫害幼小儿童的机会。
结语
卷积的这一块内容,乍一看,好像是需要强大的记忆力和见多识广。能不能做出来那道题,全靠运气
(想找到那个奇妙的函数,就好像,要找到爱情(雾))。
但是,我还是坚持自己从文化课带出来的一点点经验。理解,感受到其中的精妙,感受到想出这个东西的人为什么会这么想,才是王道。