所谓杜教筛,就是dms教给我们的筛
(逃)
前言
与其说算法,不如说是技巧。
可以在低于线性的时间复杂度(准确的说是
O
(
n
2
3
)
O(n^{\frac{2}{3}})
O(n32))内完成对积性函数的前缀和计算。
解析
考虑求函数
f
f
f 的前缀和:
S
(
n
)
=
∑
i
=
1
n
f
(
i
)
S(n)=\sum_{i=1}^nf(i)
S(n)=i=1∑nf(i)
寻找另一个函数
g
g
g,设
h
=
f
∗
g
h=f*g
h=f∗g,那么有:
∑
i
=
1
n
h
(
i
)
=
∑
i
=
1
n
∑
d
∣
i
g
(
d
)
f
(
i
d
)
=
∑
d
=
1
n
g
(
d
)
∑
i
=
1
⌊
n
d
⌋
f
(
i
)
=
∑
d
=
1
n
g
(
d
)
S
(
⌊
n
d
⌋
)
\sum_{i=1}^nh(i)=\sum_{i=1}^n\sum_{d|i}g(d)f(\frac i d)\\=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor\frac n d\rfloor}f(i)\\=\sum_{d=1}^ng(d)S(\lfloor\dfrac n d\rfloor)
i=1∑nh(i)=i=1∑nd∣i∑g(d)f(di)=d=1∑ng(d)i=1∑⌊dn⌋f(i)=d=1∑ng(d)S(⌊dn⌋)。
尝试凑出
S
(
n
)
S(n)
S(n):
g
(
1
)
S
(
n
)
=
∑
d
=
1
n
g
(
d
)
S
(
⌊
n
d
⌋
)
−
∑
d
=
2
n
g
(
d
)
S
(
⌊
n
d
⌋
)
=
∑
d
=
1
n
h
(
d
)
−
∑
d
=
2
n
g
(
d
)
S
(
⌊
n
d
⌋
)
g(1)S(n)=\sum_{d=1}^ng(d)S(\lfloor\dfrac n d\rfloor)-\sum_{d=2}^ng(d)S(\lfloor\dfrac n d\rfloor)\\=\sum_{d=1}^nh(d)-\sum_{d=2}^ng(d)S(\lfloor\dfrac n d\rfloor)
g(1)S(n)=d=1∑ng(d)S(⌊dn⌋)−d=2∑ng(d)S(⌊dn⌋)=d=1∑nh(d)−d=2∑ng(d)S(⌊dn⌋)
后面的
S
S
S 就可以递归求解了。
时间复杂度
按照上面的式子,可以写出时间复杂度的递推式:
T
(
n
)
=
O
(
n
)
+
∑
i
=
2
n
(
T
(
i
)
+
T
(
n
i
)
)
T(n)=O(\sqrt n)+\sum_{i=2}^{\sqrt n}(T(i)+T(\frac n i))
T(n)=O(n)+i=2∑n(T(i)+T(in))
由于再往下递归就是高阶小量,我们只需要展开一层:
T
(
n
)
=
O
(
n
)
+
∑
i
=
2
n
(
O
(
i
)
+
O
(
n
i
)
)
T(n)=O(\sqrt n)+\sum_{i=2}^{\sqrt n}(O(\sqrt i)+O(\sqrt \frac n i))
T(n)=O(n)+i=2∑n(O(i)+O(in))
由于
O
(
i
)
+
O
(
n
i
)
≥
O
(
2
n
)
=
O
(
n
1
4
)
O(\sqrt i)+O(\sqrt \frac n i)\ge O(2\sqrt{\sqrt n})=O(n^{\frac 1 4})
O(i)+O(in)≥O(2n)=O(n41),所以总的复杂度大概是
O
(
n
3
4
)
O(n^{\frac 3 4})
O(n43)。
考虑先用线性筛预处理出一部分前缀和。
由于算法本身根号分治就有
O
(
n
)
O(\sqrt n)
O(n),所以不妨设预处理的范围
m
>
n
m>\sqrt n
m>n。
那么时间复杂度就变成了:
T
(
n
)
=
O
(
m
)
+
O
(
n
)
+
∑
i
=
2
⌊
n
m
⌋
T
(
⌊
n
i
⌋
)
=
O
(
m
)
+
O
(
n
)
+
∑
i
=
2
⌊
n
m
⌋
O
(
⌊
n
i
⌋
)
=
O
(
m
)
+
O
(
n
)
+
∫
0
n
m
n
x
=
O
(
m
)
+
O
(
n
)
+
f
(
n
m
)
(
f
(
x
)
=
n
x
)
=
O
(
m
)
+
O
(
n
)
+
O
(
n
m
)
≥
O
(
n
2
3
)
T(n)=O(m)+O(\sqrt n)+\sum_{i=2}^{\lfloor\frac n m\rfloor}T(\lfloor\frac n i\rfloor)\\=O(m)+O(\sqrt n)+\sum_{i=2}^{\lfloor\frac n m\rfloor}O(\sqrt{\lfloor\frac n i\rfloor})\\=O(m)+O(\sqrt n)+\int_0^{\frac n m}\sqrt{\frac n x}\\=O(m)+O(\sqrt n)+f(\frac n m)(f(x)=\sqrt{nx})\\=O(m)+O(\sqrt n)+O(\frac{n}{\sqrt m})\\\ge O(n^{\frac 2 3})
T(n)=O(m)+O(n)+i=2∑⌊mn⌋T(⌊in⌋)=O(m)+O(n)+i=2∑⌊mn⌋O(⌊in⌋)=O(m)+O(n)+∫0mnxn=O(m)+O(n)+f(mn)(f(x)=nx)=O(m)+O(n)+O(mn)≥O(n32)
当
m
=
n
2
3
m=n^\frac 2 3
m=n32 时取等。
应用
如何使用杜教筛呢?
举个例子:计算
∑
i
=
1
n
μ
(
i
)
\sum_{i=1}^n\mu(i)
∑i=1nμ(i)。
我们把刚才的核心式子拿下来:
g
(
1
)
S
(
n
)
=
∑
d
=
1
n
h
(
d
)
−
∑
d
=
2
n
g
(
d
)
S
(
⌊
n
d
⌋
)
g(1)S(n)=\sum_{d=1}^nh(d)-\sum_{d=2}^ng(d)S(\lfloor\dfrac n d\rfloor)
g(1)S(n)=d=1∑nh(d)−d=2∑ng(d)S(⌊dn⌋)
首先,我们需要使
∑
d
=
1
n
h
(
d
)
\sum_{d=1}^nh(d)
∑d=1nh(d) 可以很容易的求出来,不然就没有意义了。
同时,我们最好也能让
g
g
g 函数简单一些。
想到:
μ
∗
1
=
e
\mu*1=e
μ∗1=e。
令
g
=
1
,
h
=
e
g=1,h=e
g=1,h=e,就很好的满足了我们的要求。
式子变成:
S
(
n
)
=
1
−
∑
d
=
2
n
S
(
⌊
n
d
⌋
)
S(n)=1-\sum_{d=2}^nS(\lfloor\dfrac n d\rfloor)
S(n)=1−d=2∑nS(⌊dn⌋)
非常简洁,直接求解即可。
实现上,需要开一个 map
或者哈希表进行记忆化。
代码
ll getmu(int n){
if(n<=w) return sum1[n];
if(mp1.count(n)) return mp1[n];
ll ans=1;
for(ll l=2,r;l<=n;l=r+1){
assert(n/l);
r=n/(n/l);
ans-=(r-l+1)*getmu(n/l);
}
return mp1[n]=ans;
}