【数论】狄利克雷卷积及其快速计算方法及杜教筛

目录(假的

  • 狄利克雷卷积基础知识

    • 数论函数
    • 狄利克雷卷积定义
    • 狄利克雷卷积性质
    • 常用卷积
  • 卷积计算方法

    • 最暴力的暴力
    • 稍好的暴力
    • 优美的暴力
  • 莫比乌斯反演(待填坑)

  • 杜教筛

    • 经典杜教筛
    • 杜教筛应用
      • 杜教筛应用一
      • 杜教筛应用二
    • 杜教筛总结

背景

本人即将去CTS&APIO2019,由于一些特殊原因,发现自己数论突然变得很菜。

就决定在去的前一天,翻出来以前的数论学习资料看一看。翻到了czgj的校内狄利克雷卷积课件,发现其中提到了的任意数列 f ( n ) f(n) f(n) g ( n ) g(n) g(n)的狄利克雷卷积 ( f ∗ g ) ( n ) (f*g)(n) (fg)(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)=dndk,即 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)=dn1,即 n n n的约数个数。
σ 1 ( n ) = σ ( n ) = ∑ d ∣ n d \sigma_1(n)=\sigma(n)=\sum\limits_{d\mid n}d σ1(n)=σ(n)=dnd,即 n n n的约数和。
这是积性函数,但不是完全积性函数。

欧拉函数
定义式

φ ( n ) = ∑ i = 1 n − 1 [ ( i , n ) = 1 ] \varphi(n)=\sum\limits_{i=1}^{n-1}[(i,n)=1] φ(n)=i=1n1[(i,n)=1],即 1 1 1 ( n − 1 ) (n-1) (n1)中,与 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)=npn,pisaprime(1p1)
这里 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)=npn=n(1p1)(所有的数,减去有 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=p1k1p2k2pmkm,(就是质因数分解)
(我不用 ∏ \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)(p1k1p2k2pmkm)((1p11)(1p21)(1pm1))npn,pisaprime(1p1)

得证。

莫比乌斯函数

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=p1q1p2q2p3q3pkqk,其中 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)=(fg)(n)=dnf(d)g(dn)=ab=nf(a)g(b)

p.s.注意到 ∑ a b = n f ( a ) g ( b ) \sum\limits_{ab=n}f(a)g(b) ab=nf(a)g(b)这个式子,它和一般的卷积 ∑ a + b = n f ( a ) g ( b ) \sum\limits_{a+b=n}f(a)g(b) a+b=nf(a)g(b)长得非常像。

狄利克雷卷积基础性质

可以看到,有了这些性质,我们似乎就可以将其视为一个群了。
这些性质也使得我们在做题时更加方便。

交换律

( f ∗ g ) ( n ) = ( g ∗ f ) ( n ) (f*g)(n)=(g*f)(n) (fg)(n)=(gf)(n)

证明:如果用 ∑ a b = n f ( a ) g ( b ) \sum\limits_{ab=n}f(a)g(b) ab=nf(a)g(b)这个式子,就是显然的。

结合律

( f ∗ g ) ∗ h = f ∗ ( g ∗ h ) (f*g)*h=f*(g*h) (fg)h=f(gh)

证明:考虑展开,易证。算了,我还是写一下吧。
( ( 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} ((fg)h)(n)=d1d2=n(fg)(d1)h(d2)=d1d2=n(d3d4=d1f(d3)g(d4))h(d2)=d3d4d2=nf(d3)g(d4)h(d2)=d1d2d3=nf(d1)g(d2)h(d3)
那么, f ∗ ( g ∗ h ) f*(g*h) f(gh)也可以化成这种形式。

推论1:若是 ( f 1 ∗ f 2 ∗ f 3 ∗ ⋯ ∗ f k ) ( n ) (f_1*f_2*f_3*\cdots*f_k)(n) (f1f2f3fk)(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) d1d2d3dk=nf1(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=fh+gh

证明:展开即可。
( ( 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=nf(a)h(b)+ab=ng(a)h(b)=fh+gh

存在单位元

f ∗ ϵ = ϵ ∗ f = f f*\epsilon=\epsilon*f=f fϵ=ϵf=f

证明:考虑单位函数 ϵ \epsilon ϵ的定义,这就是显然的。

关于积性

f , g f,g f,g为积性函数,则 f ∗ g f*g fg依旧为积性函数。

证明:
考虑,我们要证 ( 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 (fg)(n)=(fg)(a)(fg)(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} (fg)(n)=dnf(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} (fg)(a)(fg)(b)=d1af(d1)g(d1a)d2bf(d2)g(d2b)=d1a,d2bf(d1)f(d2)g(d1a)g(d2b)=d1a,d2bf(d1d2)g(d1d2ab)=dnf(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 dnφ(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} jdn
  • μ ∗ 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 dnμ(d)=0就行了。
    ∑ d ∣ n μ ( d ) = μ ( 1 ) + μ ( n ) = 1 + ( − 1 ) 1 = 0 \sum_{d|n}\mu(d)=\mu(1)+\mu(n)=1+(-1)^1=0 dnμ(d)=μ(1)+μ(n)=1+(1)1=0

  • i d ∗ 1 = σ 1 ∗ 1 = d id*1=\sigma\qquad1*1=d id1=σ11=d i d k ∗ 1 = σ k id_k*1=\sigma_k idk1=σk
    根据定义,显然成立

  • 其他
    比如 φ ∗ d = φ ∗ 1 ∗ 1 = i d ∗ 1 = σ \varphi*d=\varphi*1*1=id*1=\sigma φd=φ11=id1=σ

卷积计算方法

我可能会混用 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)=dnf(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=1nini=1n(in+1)=n+ni=1ni1=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=1ni1=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)=d1d2dk=nf(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=1nf(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=1nf(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)=dng(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=1nh(i)=i=1ndig(d)f(di)=i=1ng(i)ijnf(j)=i=1ng(i)F(in)=g(1)F(n)+i=2ng(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=2ng(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 jin都与 ⌊ 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=qij+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=qj+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 jin=q+jir=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=2znin i=1znin n i=1zni212n (zn)212z n(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+nz21),显然, 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} 1zn每一个 ⌊ 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=1nf(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=1nφ(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)=fg
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)==dnf(d)g(dn)dnφ(d)dg(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)=dnφ(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)=dnf(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=1ng(i)dif(d)=i=1ng(i)(f1)(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=1nh(i)=i=1ndif(d)g(di)=i=1ng(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=2ng(i)S(in)
就可以求了。

我们还可以发现, h ( n ) = ( f ∗ 1 ) ( n ) ⋅ g ( n ) h(n)=(f*1)(n)\cdot g(n) h(n)=(f1)(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=1n(fg)(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=1ndif(d)g(di)=i=1nf(i)G(in),在 O ( n ) O(\sqrt{n}) O(n )的时间内求出。
但是这太受限了。

首先,我们设 h ( n ) = ( f ∗ 1 ) ( n ) h(n)=(f*1)(n) h(n)=(f1)(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=1nS(in)i=1ndi(fg)(d)i=1nd1id2d1g(d2)f(d2d1)i=1nd2ig(d2)d3d2if(d3)i=1ndig(d)(f1)(di)i=1ng(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=1nS(in)S(n)=i=1ng(i)H(in)=i=1ng(i)H(in)i=2nS(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的毒瘤留下了迫害幼小儿童的机会。

结语

卷积的这一块内容,乍一看,好像是需要强大的记忆力和见多识广。能不能做出来那道题,全靠运气
(想找到那个奇妙的函数,就好像,要找到爱情(雾))。
但是,我还是坚持自己从文化课带出来的一点点经验。理解,感受到其中的精妙,感受到想出这个东西的人为什么会这么想,才是王道。

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值