前言
不知不觉鸽了这么久了,马上也要到今年的冬令营了… 还是早点把这个去年的想法写完吧。
这篇文章主要讨论曾经常在 OI 里出现的积性函数求和问题。
基本定义和性质
记号 ⌊ x ⌋ \lfloor x \rfloor ⌊x⌋表示不超过 x x x的最大整数,记号 ⌈ x ⌉ \lceil x \rceil ⌈x⌉表示不小于 x x x的最小整数。
记号 d ∣ n d | n d∣n表示 d d d整除 n n n, gcd ( x , y ) \gcd(x, y) gcd(x,y)表示 x x x和 y y y的最大公约数, [ x ] [x] [x]在 x x x为真时值为 1 1 1,否则值为 0 0 0。
定义函数 f : Z + → F f : \mathbb{Z}^{+} \rightarrow \mathbb{F} f:Z+→F为数论函数,其中 F \mathbb{F} F是一个域,称由全体数论函数构成的集合为 U \mathcal{U} U。
若数论函数 g : Z + → F g : \mathbb{Z}^{+} \rightarrow \mathbb{F} g:Z+→F满足 ∀ x , y ∈ Z + , gcd ( x , y ) = 1 , ∃ g ( x y ) = g ( x ) g ( y ) \forall x, y \in \mathbb{Z}^{+}, \gcd(x, y) = 1, \exists g(xy) = g(x)g(y) ∀x,y∈Z+,gcd(x,y)=1,∃g(xy)=g(x)g(y),则称 g g g为积性函数,称由全体积性函数构成的集合为 V \mathcal{V} V。
若数论函数 h : Z + → F h : \mathbb{Z}^{+} \rightarrow \mathbb{F} h:Z+→F满足 ∀ x , y ∈ Z + , ∃ h ( x y ) = h ( x ) h ( y ) \forall x, y \in \mathbb{Z}^{+}, \exists h(xy) = h(x)h(y) ∀x,y∈Z+,∃h(xy)=h(x)h(y),则称 h h h为完全积性函数。
定义 P P P为素数集合,集合中的第 i i i个元素是第 i i i小的素数 p i p_i pi。对于一个正整数 x = ∏ p i e x i x = \prod p_i^{e_{x_i}} x=∏piexi,定义该正整数的坐标 e x = [ x ] P = ( e x , 1 , e x , 2 , e x , 3 , … ) e_x = [x]_P = (e_{x,1}, e_{x,2}, e_{x,3}, \ldots) ex=[x]P=(ex,1,ex,2,ex,3,…)。
对 p ∈ P p \in P p∈P,定义 V p = { 1 , p , p 2 , p 3 , … } V_p = \{1, p, p^2, p^3, \ldots\} Vp={1,p,p2,p3,…},表示 p p p的非负次幂构成的集合。
定义 f p ( n ) = f ( n ) [ n ∈ V p ] f_p(n) = f(n) [n \in V_p] fp(n)=f(n)[n∈Vp],即保留 f f f中下标在 V p V_p Vp中的项后的数论函数。
定义 U \mathcal{U} U上的 Dirichlet 卷积运算 ∗ * ∗,满足 ( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) g ( n d ) (f * g)(n) = \sum_{d | n} f(d) g\left(\frac{n}{d} \right) (f∗g)(n)=∑d∣nf(d)g(dn),用 ∏ ^ \hat{\prod} ∏^表示 Dirichlet 卷积的连续运算,Dirichlet 卷积有以下性质:
性质一: f ∈ V ⟺ f = ∏ ^ p ∈ P f p f \in \mathcal{V} \iff f = {\hat{\prod}}_{p \in P} f_p f∈V⟺f=∏^p∈Pfp,其中 f p f_p fp只有下标在 V p V_p Vp中的位置有值。
证明: 若 f ∈ V f \in \mathcal{V} f∈V,比较等式两端系数可得 f = ∏ ^ p ∈ P f p f = {\hat{\prod}}_{p \in P} f_p f=∏^p∈Pfp;若 f = ∏ ^ p ∈ P f p f = {\hat{\prod}}_{p \in P} f_p f=∏^p∈Pfp,容易验证 f ∈ V f \in \mathcal{V} f∈V。
性质二: ∀ f , g ∈ V , f ∗ g ∈ V \forall f, g \in \mathcal{V}, f * g \in \mathcal{V} ∀f,g∈V,f∗g∈V
证明: f ∗ g = ∏ ^ p ∈ P f p g p f * g = {\hat{\prod}}_{p \in P} f_p g_p f∗g=∏^p∈Pfpgp,由性质一有 f ∗ g ∈ V f * g \in \mathcal{V} f∗g∈V。
性质三: ∀ f ∈ V , ∃ g = f − 1 s . t . f ∗ g = ϵ \forall f \in \mathcal{V}, \exists g = f^{-1}~ \mathrm{s.t.} f*g=\epsilon ∀f∈V,∃g=f−1 s.t.f∗g=ϵ,其中 ϵ ( x ) = [ x = 1 ] \epsilon(x) = [x = 1] ϵ(x)=[x=1]
定义 n ∈ Z + n \in \mathbb{Z}^{+} n∈Z+的整除集合为 D n = { ⌊ n m ⌋ ∣ m ∈ Z + } D_n = \left \{ \lfloor \frac{n}{m} \rfloor | m \in \mathbb{Z}^{+} \right \} Dn={⌊mn⌋∣m∈Z+},整除集合有如下性质。
性质一: D n = { 0 , 1 , 2 , … , ⌊ n ⌋ , ⌊ n ⌊ n ⌋ ⌋ , ⌊ n ⌊ n ⌋ − 1 ⌋ , … , n } D_n = \left \{ 0, 1, 2, \ldots, \lfloor\sqrt{n}\rfloor, \left \lfloor \frac{n}{\lfloor \sqrt{n} \rfloor} \right \rfloor, \left \lfloor \frac{n}{\lfloor \sqrt n\rfloor - 1} \right \rfloor, \ldots, n \right \} Dn={0,1,2,…,⌊n⌋,⌊⌊n⌋n⌋,⌊⌊n⌋−1n⌋,…,n}
证明: 当 m ≤ n m \le \sqrt n m≤n时, ⌊ n / m ⌋ \lfloor n/m \rfloor ⌊n/m⌋的取值已经被包含在集合中,否则 ⌊ n m ⌋ ≤ ⌊ n ⌋ \left \lfloor \frac{n}{m} \right \rfloor \le \lfloor \sqrt n \rfloor ⌊mn⌋≤⌊n⌋。而此时 m > n m > \sqrt n m>n,若 ⌊ n m ⌋ ≠ ⌊ n m + 1 ⌋ \lfloor \frac{n}{m} \rfloor \ne \lfloor \frac{n}{m + 1} \rfloor ⌊mn⌋=⌊m+1n⌋,则有 n m + 1 ≤ ⌊ n m ⌋ ≤ n m \frac{n}{m + 1} \le \left \lfloor \frac{n}{m} \right \rfloor \le \frac{n}{m} m+1n≤⌊mn⌋≤mn,但 n m − n m + 1 < 1 \frac{n}{m} - \frac{n}{m + 1} < 1 mn−m+1n<1,因此 ⌊ n m ⌋ = ⌊ n m + 1 ⌋ + 1 \lfloor \frac{n}{m} \rfloor = \lfloor \frac{n}{m + 1} \rfloor + 1 ⌊mn⌋=⌊m+1n⌋+1,于是结论成立。
性质二: D ( ⌊ n / m ⌋ ) ⊆ D ( n ) , n , m ∈ Z + D\left( \left \lfloor n/m \right \rfloor \right) \subseteq D(n), n,m \in \mathbb{Z}^{+} D(⌊n/m⌋)⊆D(n),n,m∈Z+
证明: 令 n = k a b + r b + c , r ∈ [ 0 , a ) ∩ Z , c ∈ [ 0 , b ) ∩ Z n = kab + rb + c, r \in [0, a) \cap \mathbb{Z}, c \in [0, b) \cap \mathbb{Z} n=kab+rb+c,r∈[0,a)∩Z,c∈[0,b)∩Z,则 ⌊ n / a b ⌋ = k = ⌊ ( k a + r ) / a ⌋ = ⌊ ⌊ n / b ⌋ / a ⌋ \left \lfloor n/ab\right \rfloor = k = \left \lfloor (ka+r)/a \right \rfloor = \left \lfloor \lfloor n/b \rfloor /a \right \rfloor ⌊n/ab⌋=k=⌊(ka+r)/a⌋=⌊⌊n/b⌋/a⌋,取 b = m b = m b=m可得结论成立。
性质三: 令 U n ( x ) = min y ∈ D n , y ≥ x y U_n(x) = \min_{y \in D_n, y \ge x} y Un(x)=miny∈Dn,y≥xy,其中 1 ≤ x ≤ n 1 \le x \le n 1≤x≤n,则 ⌊ n / U n ( x ) ⌋ = ⌊ n / x ⌋ \lfloor n / U_n(x) \rfloor = \lfloor n/x \rfloor ⌊n/Un(x)⌋=⌊n/x⌋
证明: 不妨设 y = ⌊ n / x ⌋ y = \lfloor n / x \rfloor y=⌊n/x⌋,那么 x y ≤ n x y \le n xy≤n,于是 x ≤ n / y x \le n / y x≤n/y,因此这样的 x x x的上界为 ⌊ n / y ⌋ ∈ D n \lfloor n / y \rfloor \in D_n ⌊n/y⌋∈Dn,因此 ⌊ n / U n ( x ) ⌋ ≤ ⌊ n / x ⌋ \lfloor n / U_n(x) \rfloor \le \lfloor n / x \rfloor ⌊n/Un(x)⌋≤⌊n/x⌋,而另一个方向是显然的,于是得证。
推论: ∀ x , y ∈ [ 1 , n ] ∩ Z , x y ≤ n ⟺ U n ( x ) U n ( y ) ≤ n \forall x, y \in [1, n] \cap \mathbb{Z}, xy \le n \iff U_n(x) U_n(y) \le n ∀x,y∈[1,n]∩Z,xy≤n⟺Un(x)Un(y)≤n
对 f ∈ U , n ∈ Z + f \in \mathcal{U}, n \in \mathbb{Z}^{+} f∈U,n∈Z+定义 f f f定义在 n n n的整除集合上的前缀和函数为 S n , f ( x ) = ∑ y ≤ U n ( x ) f ( y ) , 1 ≤ x ≤ n S_{n,f}(x) = \sum_{y \le U_n(x)} f(y), 1 \le x \le n Sn,f(x)=∑y≤Un(x)f(y),1≤x≤n,那么在存储 S n , f S_{n, f} Sn,f时我们只要存储 D n D_n Dn中所有下标的值即可。
定理: 给定 S n , f S_{n, f} Sn,f和 S n , g S_{n, g} Sn,g,可以在 O ( n 2 / 3 log 1 / 3 n ) O(n^{2/3} \log^{1/3} n) O(n2/3log1/3n)的时间内求得 S n , f ∗ g S_{n, f * g} Sn,f∗g。
证明: 即对 m ∈ D n m \in D_n m∈Dn求 S n , f ∗ g ( m ) = ∑ x ∈ D m ( S n , f ( ⌊ m / x ⌋ ) − S n , f ( ⌊ m / U m ( x + 1 ) ⌋ ) ) S n , g ( x ) S_{n, f * g}(m) = \sum_{x \in D_m} \left(S_{n, f}(\lfloor m / x \rfloor) - S_{n, f}(\lfloor m / U_m(x + 1) \rfloor)\right) S_{n, g}(x) Sn,f∗g(m)=∑x∈Dm(Sn,f(⌊m/x⌋)−Sn,f(⌊m/Um(x+1)⌋))Sn,g(x),由此可以在 O ( m ) O(\sqrt m) O(m) 的时间内对特定的 m m m求得该值。另一方面,满足 x , y ∈ D n , x y ≤ S x, y \in D_n, xy \le S x,y∈Dn,xy≤S的 ( x , y ) (x, y) (x,y)对数是 O ( S log S ) O(S \log S) O(SlogS)的,因此由性质二和性质三的推论,对所有 m ≤ S m \le S m≤S可以在 O ( S log S ) O(S \log S) O(SlogS)内求得答案。而对超过 S S S的 m m m消耗的总时间为 O ( ∫ 1 n / S ( n / x ) 1 / 2 ) = O ( n / S 1 / 2 ) O(\int_1^{n/S} (n/x)^{1/2}) = O(n / S^{1/2}) O(∫1n/S(n/x)1/2)=O(n/S1/2),取 S = n 2 / 3 log − 2 / 3 n S = n^{2/3} \log^{-2/3} n S=n2/3log−2/3n时得到 O ( n 2 / 3 log 1 / 3 n ) O(n^{2/3} \log^{1/3} n) O(n2/3log1/3n)的时间复杂度,故命题正确。
另外注意到,若 f f f和 g g g是积性的,我们可以在 O ( S ) O(S) O(S)的时间内求得前 S S S项的值,于是复杂度可以降到 O ( n 2 / 3 ) O(n^{2/3} ) O(n2/3)。
特别地,如果 S n , f ( x ) = c S_{n, f}(x) = c Sn,f(x)=c对所有 x ≤ n x \le \sqrt n x≤n成立,合并可以用暴力在 O ( n log n ) O(\sqrt n \log n) O(nlogn)的时间内完成。
定义 Powerful Number 为所有的满足 ∀ k ∈ Z + , e x , k ≠ 1 \forall k \in \mathbb{Z}^{+}, e_{x, k} \ne 1 ∀k∈Z+,ex,k=1的 x x x,令 W W W为全体 Powerful Number 组成的集合。
性质: n n n以内的 Powerful Number 的数目是 Θ ( n ) \Theta(\sqrt n) Θ(n)的。
证明: 一方面,所有的平方数均 powerful,因此数目是 Ω ( n ) \Omega(\sqrt{n}) Ω(n)的。另一方面,所有的 powerful number 总能表示为 a 2 b 3 a^2 b^3 a2b3的形式,这样的数的数目为 O ( ∫ 1 n 1 / 3 ( n / x 3 ) 1 / 2 d x ) + O ( n 1 / 3 ) = O ( n 1 / 2 ) O(\int_1^{n^{1/3}} (n/x^3)^{1/2} \mathrm{d} x) + O(n^{1/3}) = O(n^{1/2}) O(∫1n1/3(n/x3)1/2dx)+O(n1/3)=O(n1/2),因此原命题得证。
定理: 若 f f f中只有 Powerful Number 处有值,且我们有 S n , g S_{n, g} Sn,g,我们可以在 O ~ ( n 0.6 ) \tilde{O}(n^{0.6}) O~(n0.6)的时间内求得 S n , f ∗ g S_{n, f * g} Sn,f∗g。
证明: 由 Powerful Number 的性质,按照 m 2 / 3 m^{2/3} m2/3分段可知, S m , f S_{m, f} Sm,f中值不同的项的数目是 O ( m 1 / 3 ) O(m^{1/3}) O(m1/3)的。同样采用类似的方法,平衡较小部分的复杂度 O ~ ( S ) \tilde{O}(S) O~(S)和较大部分的复杂度 O ( n / S 2 / 3 ) O(n / S^{2/3}) O(n/S2/3)可在 S = O ~ ( n 0.6 ) S = \tilde{O}(n^{0.6}) S=O~(n0.6)时取得平衡。于是命题成立。
一种筛法
OI 中出现的积性函数求和问题往往分为两种:给定 f p f_p fp的生成规则,求 S n , f ( n ) S_{n, f}(n) Sn,f(n)或是求出 S n , f S_{n, f} Sn,f。
由于对于 n < p ≤ n \sqrt n < p \le n n<p≤n的 p p p是 O ( n log − 1 n ) O(n \log^{-1} n) O(nlog−1n)的,为了给出生成规则我们一般对于这一部分的 p p p一定有 f ( p ) = y ( p ) f(p) = y(p) f(p)=y(p),其中 y ( x ) = ∑ i ∈ T a i x i , T ⊂ Z + y(x) = \sum_{i \in T} a_i x^i, T \subset \mathbb{Z}^{+} y(x)=∑i∈Taixi,T⊂Z+,并且 ∣ T ∣ |T| ∣T∣是一个常数,且其中元素的大小不太大。
只求单一的 S n , f ( n ) S_{n, f}(n) Sn,f(n)的值可以用 Min-25 筛,虽然是 Ω ( n c ) ∀ c < 1 \Omega(n^{c}) \ \forall c < 1 Ω(nc) ∀c<1的但在现在的范围 ( ∼ 1 0 11 ) (\sim 10^{11}) (∼1011)里还是非常 work 的,求 S n , f S_{n, f} Sn,f的传统方法有:
- 杜教筛:已知 S n , f S_{n, f} Sn,f和 S n , g S_{n, g} Sn,g,可以在 O ( n 2 / 3 ) O(n^{2/3}) O(n2/3)内求 S n , f ∗ g − 1 S_{n, f * g^{-1}} Sn,f∗g−1。
- 洲阁筛(扩展埃筛):若 ∣ S ∣ |S| ∣S∣是常数,可以在 O ( n 3 / 4 log − 1 n ) O(n^{3/4} \log^{-1} n) O(n3/4log−1n)内求 S n , f S_{n, f} Sn,f。
以上资料可以在《2016/2018年候选队论文集》以及 Min-25的博客 中找到。在博客中还有另一个比本文方法复杂度稍劣的方法,并且和本文方法一样没有很大的效率提升。
下面我们将介绍一种 O ( n 2 / 3 log − 1 n ) O(n^{2/3} \log^{-1} n) O(n2/3log−1n)的求 S n , f S_{n, f} Sn,f的方法。设 f = ∏ ^ p ∈ P f p f = \hat{\prod}_{p \in P} f_p f=∏^p∈Pfp,并定义 f D = ∏ ^ p ∈ D f p f_{\mathcal{D}} = \hat{\prod}_{p \in \mathcal{D}} f_p fD=∏^p∈Dfp,那么:
定理一: 给定 f p f_p fp,可以在 O ( n log p n ) O(\sqrt n \log_p n) O(nlogpn)的时间内由 S n , g S_{n, g} Sn,g推得 S n , g ∗ f p S_{n, g * f_p } Sn,g∗fp。
证明: 暴力枚举 f p f_p fp中的每一项并更新即可。
因此,若我们对于 p ≤ n 1 / α p \le n^{1/\alpha} p≤n1/α使用该方法更新,其复杂度为 O ( α n 1 / α + 1 / 2 ) O(\alpha n^{1/\alpha + 1/2}) O(αn1/α+1/2)。
接下来我们将 f = ∏ ^ f p f = \hat{\prod} f_p f=∏^fp按照 p p p分成三个部分: D 1 = P ∩ [ 1 , n 1 / α ] \mathcal{D}_1 = P \cap [1, n^{1/\alpha}] D1=P∩[1,n1/α], D 2 = P ∩ ( n 1 / α , n 1 / 2 ] \mathcal{D}_2 = P \cap (n^{1/\alpha}, n^{1/2}] D2=P∩(n1/α,n1/2], D 3 = P ∩ ( n 1 / 2 , n ] \mathcal{D}_3 = P \cap (n^{1/2}, n] D3=P∩(n1/2,n]。若我们求出了 S n , f D 2 S_{n, f_{\mathcal{D}_2}} Sn,fD2和 S n , f D 3 S_{n, f_{\mathcal{D}_3}} Sn,fD3,由之前的性质,我们可以在 O ( α n 1 / α + 1 / 2 ) O(\alpha n^{1/\alpha + 1/2}) O(αn1/α+1/2)的时间内将其合并为 S n , f S_{n, f} Sn,f。首先考虑如何求 S n , f D 2 S_{n, f_{\mathcal{D}_2}} Sn,fD2。
定理二: ∀ 1 ≤ k ≤ log 2 n \forall 1 \le k \le \log_2 n ∀1≤k≤log2n,恰含 k k k个素因子的素因子大小至少为 n 1 / α n^{1/ \alpha} n1/α的数的个数是 O ( ( α + 1 ) k n log − 1 n ) O((\alpha + 1)^k n \log^{-1} n) O((α+1)knlog−1n)的。
证明: 当
k
=
1
k = 1
k=1时该式成立。若当
k
=
m
k = m
k=m时该式成立,考虑
k
=
m
+
1
k = m + 1
k=m+1时这样的数的个数显然为
O
(
(
α
+
1
)
m
∫
n
1
/
α
n
d
x
ln
x
n
x
ln
x
+
(
α
+
1
)
m
∫
n
1
/
α
n
d
x
ln
x
)
=
O
(
(
α
+
1
)
m
+
1
n
log
−
1
n
)
O\left((\alpha + 1)^m\int_{n^{1/\alpha}}^n \frac{\mathrm{d} x}{\ln x} \frac{n}{x \ln x} + (\alpha + 1)^m\int_{n^{1/\alpha}}^n \frac{\mathrm{d} x}{\ln x} \right) = O((\alpha + 1)^{m + 1} n \log^{-1} n)
O((α+1)m∫n1/αnlnxdxxlnxn+(α+1)m∫n1/αnlnxdx)=O((α+1)m+1nlog−1n)
传统的计算
S
n
,
f
D
2
S_{n, f_{\mathcal{D}_2}}
Sn,fD2的方法总要考虑素因子的顺序,这导致了状态的两个维度的产生。即便每个状态的转移已经最优,复杂度也难以优化。因此我们尝试减少这一部分的状态数:如果我们不考虑素因子的顺序会发生什么呢?也就是说,令
g
k
(
x
)
=
f
D
2
(
x
)
[
∑
e
x
,
i
=
k
]
g_k(x) = f_{\mathcal{D}_2}(x) [\sum e_{x,i} = k]
gk(x)=fD2(x)[∑ex,i=k],我们希望通过计算
g
k
−
1
g_{k - 1}
gk−1与
∑
p
∈
D
2
f
p
\sum_{p \in \mathcal{D}_2} f_p
∑p∈D2fp的卷积来推得
S
n
,
g
k
S_{n, g_k}
Sn,gk。
定理三: 若 α \alpha α为常数,则计算一次这样的卷积的时间复杂度为 O ( n 2 / 3 log − 1 n ) O(n^{2/3} \log^{-1} n) O(n2/3log−1n)。
证明: 由于只在素数及其幂次处有值,可以注意到 S n , ∑ p ∈ D 2 f p S_{n, \sum_{p \in \mathcal{D}_2} f_p} Sn,∑p∈D2fp中不同的值的期望个数是 O ( n 1 / 2 log − 1 n ) O(n^{1/2} \log^{-1} n) O(n1/2log−1n)的。因次在计算 S n , g k − 1 ∗ ∑ p ∈ D 2 f p S_{n, g_{k - 1} * \sum_{p \in \mathcal{D}_2} f_p} Sn,gk−1∗∑p∈D2fp时,对一个 m m m单次枚举的复杂度可以做到 O ( m 1 / 2 log − 1 m ) O(m^{1/2} \log^{-1} m) O(m1/2log−1m):我们仍然按照 m \sqrt m m 分段,较大部分时我们的素数个数是除了一个 log 的,而较小时值的个数也有限。而由定理二,若 α \alpha α为常数,则计算较小部分的复杂度也可以做到 O ( S log − 1 n ) O(S \log^{-1} n) O(Slog−1n),于是取 S = O ( n 2 / 3 ) S = O(n^{2/3}) S=O(n2/3)可以达到定理中的时间复杂度。
由定理三,当 α \alpha α为常数时,我们总的计算时间复杂度为 O ( n 2 / 3 log − 1 n ) O(n^{2/3} \log^{-1} n) O(n2/3log−1n)。
但我们究竟算了什么呢?容易发现,对一个数 x x x来说,其对答案的贡献为 ( ∑ e x , i ) ! ∏ f D 2 ( p i ) e x , i ( e x , i ) ! − 1 (\sum e_{x, i})! \prod f_{\mathcal{D}_2}(p_i)^{e_{x,i}} (e_{x,i})!^{-1} (∑ex,i)!∏fD2(pi)ex,i(ex,i)!−1,因此若 f D 2 ( p i e ) = f D 2 ( p i ) e ( e ! ) − 1 f_{\mathcal{D}_2} (p_i^{e}) = f_{\mathcal{D}_2}(p_i)^e (e!)^{-1} fD2(pie)=fD2(pi)e(e!)−1,我们算出的值恰好就是 S n , g k S_{n, g_k} Sn,gk的 k ! k! k!倍,只要在最后除去即可。
注意到若我们构造另一个积性函数 f 0 f_0 f0满足 f 0 ( p i e ) = f D 2 ( p i ) e ( e ! ) − 1 f_0(p_i^e) = f_{\mathcal{D}_2}(p_i)^e (e!)^{-1} f0(pie)=fD2(pi)e(e!)−1,那么 f D 2 ∗ f 0 − 1 f_{\mathcal{D}_2} * f_0^{-1} fD2∗f0−1不为 0 0 0的位置一定是 Powerful Number 集合 W W W的子集。于是我们可以先求出 f 0 f_0 f0的前缀和,再和一个 Powerful 的函数做卷积,这样我们就在 O ( n 2 / 3 log − 1 n ) O(n^{2/3} \log^{-1} n) O(n2/3log−1n)的时间内求出了 S n , f D 2 S_{n, f_{\mathcal{D}_2}} Sn,fD2。
那么如何求 S n , f D 3 S_{n, f_{\mathcal{D}_3}} Sn,fD3呢,我们将问题转化为 ∣ T ∣ |T| ∣T∣个求所有最小素因子超过 n \sqrt n n的数的 k k k次方之和的问题,令 u k ( x ) = x k [ x ∈ P ] [ x > n ] u_k(x) = x^k [x \in P] [x > \sqrt n] uk(x)=xk[x∈P][x>n],即求 S n , u k S_{n, u_k} Sn,uk。注意到超过 n \sqrt n n的因子至多只有一个,令 v k ( x ) = x k [ ∀ p i > n , ∃ e x , i = 0 ] v_k(x) = x^k [\forall p_i > \sqrt n, \exists e_{x, i} = 0] vk(x)=xk[∀pi>n,∃ex,i=0],则有: S n , u k ∗ ( v k + ϵ ) = S n , i d k S_{n, u_k * (v_k + \epsilon)} = S_{n, id^k} Sn,uk∗(vk+ϵ)=Sn,idk,而 S n , u k S_{n, u_k} Sn,uk也只有 n \sqrt n n以上的项有值,因此有 S n , v k + ϵ S_{n, v_k + \epsilon} Sn,vk+ϵ和 S n , i d k S_{n, id^k} Sn,idk后可以用一个简单的容斥做到 O ( n log n ) O(\sqrt n \log n) O(nlogn)求 S n , u k S_{n, u_k} Sn,uk。我们可以利用之前的方法求出 S n , v k S_{n, v_k} Sn,vk,这样我们这一部分的时间复杂度也是 O ( n 2 / 3 log − 1 n ) O(n^{2/3} \log^{-1} n) O(n2/3log−1n)。
不妨取 α = 6 \alpha = 6 α=6,总的时间复杂度即为 O ( n 2 / 3 log − 1 n ) O(n^{2/3} \log^{-1} n) O(n2/3log−1n)。
下面是一份示例代码,不保证没有错误、也希望有人能写一个常数更小一点的版本。
const int mod = 1e9 + 7;
inline void inc(int &a, int b){ (a += b) < mod ?: a -= mod; }
inline void inc(int &a, i64 b){ a = (a + b) % mod; }
inline void dec(int &a, int b){ (a -= b) >= 0 ?: a += mod; }
inline void dec(int &a, i64 b){ inc(a, mod - b % mod); }
int fpow(int a, int t){
static int r;
for (r = 1; t; t >>= 1, a = (i64)a * a % mod) if (t & 1) r = (i64)r * a % mod;
return r;
}
const int unit = 7;
i64 x;
int B, S, RS, size, count, fac[unit], ifac[unit], inv[unit];
vector<int> primes, pi;
struct array_t {
int f[unit];
array_t(){ memset(f, 0, unit * 4); }
int &operator[](int i){ return f[i]; }
int to_val(){
int sum = 0;
for (int i = 0; i < unit; ++i) inc(sum, (i64)f[i] * ifac[i]);
return sum;
}
} ;
inline void inc(array_t &a, array_t &b){
for(int i = 0; i < unit; ++i) if (b[i]) inc(a[i], b[i]);
}
inline void inc(array_t &a, array_t &b, int v){
for(int i = 0; i + 1 < unit; ++i) if (b[i]) inc(a[i + 1], (i64)b[i] * v);
}
template<class value_t>
struct data_t {
vector<value_t> lsum, ssum;
data_t(){ lsum.resize(size), ssum.resize(size); }
void update(int p, vector<int> g){
int i, j, e, l = S / p;
for (i = 1; i <= S; ++i) dec(ssum[i], lsum[S]);
for (i = S; i >= 2; --i) dec(lsum[i], lsum[i - 1]);
for (i = 1; i <= S; ++i) for (j = i, e = 0; j <= l; inc(ssum[i], (i64)ssum[j *= p] * g[++e]));
for (i = 2; i <= S; ++i) dec(ssum[i - 1], ssum[i]);
for (i = S; i >= 1; --i) if (lsum[i]) {
for (j = i, e = 0; j <= l; inc(lsum[j *= p], (i64)lsum[i] * g[++e]));
for (j = x / p / j; j; j /= p) inc(ssum[j], (i64)lsum[i] * g[++e]);
}
for (i = 2; i <= S; ++i) inc(lsum[i], lsum[i - 1]);
inc(ssum[S], lsum[S]);
for (i = S; i >= 2; --i) inc(ssum[i - 1], ssum[i]);
}
void gsum(){
int i;
for (i = 2; i <= S; ++i) inc(lsum[i], lsum[i - 1]);
inc(ssum[S], lsum[S]); if (S == RS) lsum[S] = ssum[S];
for (i = S; i >= 2; --i) inc(ssum[i - 1], ssum[i]);
}
void gdif(){
int i;
for (i = 2; i <= S; ++i) dec(ssum[i - 1], ssum[i]);
dec(ssum[S], lsum[S]);
for (i = S; i >= 2; --i) dec(lsum[i], lsum[i - 1]);
}
void add(i64 a, value_t b){ inc(a <= S ? lsum[a] : ssum[x / a], b); }
void print(){
debug("function address %llu: \n", u64(this));
for (int i = 1; i <= S; ++i) debug("S(%d) = %d\n", i, lsum[i]);
for (int i = S; i >= 1; --i) debug("S(%llu) = %d\n", x / i, ssum[i]);
}
} ;
void init(){
int i, j;
S = sqrtl(x), RS = x / S, size = S + 1, B = powl(x, 1.0 / unit) + 1e-7, pi.resize(size);
vector<char> sieve(size);
for (fac[0] = i = 1; i < unit; ++i) fac[i] = (i64)fac[i - 1] * i;
for (inv[1] = 1, i = 2; i < unit; ++i) inv[i] = mod - (i64)(mod / i) * inv[mod % i] % mod;
for (ifac[0] = i = 1; i < unit; ++i) ifac[i] = (i64)ifac[i - 1] * inv[i] % mod;
for (i = 4; i <= S; i += 2) sieve[i] = true;
for (i = 9; i <= S; i += 6) sieve[i] = true;
for (i = 5; i * i <= S; i += 2) if (!sieve[i]) {
for (j = i * i; j <= S; j += 6 * i) sieve[j] = true;
for (j = i * (i + 6 - 2 * (i % 3)); j <= S; j += 6 * i) sieve[j] = true;
}
primes.pb(0);
for (i = 2; i <= S; ++i) {
pi[i] = pi[i - 1] + !sieve[i];
if (!sieve[i]) primes.pb(i), ++count;
}
primes.pb(size);
}
data_t<int> solve(vector<vector<int>> f){
vector<vector<int>> g(count + 1), h(count + 1);
int i, j, k, l, v, m;
for (i = 1; i <= count; ++i) {
m = f[i].size(), g[i].resize(m), h[i].resize(m);
if (i > pi[B]) {
for (g[i][0] = j = 1; j < m; ++j) g[i][j] = (i64)g[i][j - 1] * f[i][1] % mod;
for (j = 0; j < m; ++j) g[i][j] = (i64)g[i][j] * ifac[j] % mod;
} else g[i] = f[i];
for (j = 0; j < m; ++j) for (h[i][j] = f[i][j], k = 0; k < j; ++k) dec(h[i][j], (i64)h[i][k] * g[i][j - k]);
}
data_t<int> F, G;
vector<int> fval(count + 1);
for (i = 1; i <= count; ++i) fval[i] = g[i][1];
for (i = 1; i <= count; ++i) inc(fval[i], fval[i - 1]);
{
data_t<array_t> ts;
i64 block = std::max((i64)S, std::min(x, i64(powl(x, 2.0 / 3) * 9)));
block = x / (x / block);
vector<i64> nxt(size);
function<void(int, i64, int, int)> dfs = [&](int pos, i64 cur, int cnt, int now){
int i, u, l, r;
for (i = pos; cur * primes[i] * primes[i] <= block; ++i) {
i64 t = cur, a;
int e = 0, b;
while (t * primes[i] * primes[i] <= block) {
t *= primes[i], ++e;
dfs(i + 1, t, cnt + e, (i64)now * g[i][e] % mod);
a = t * primes[i], b = (i64)now * g[i][e + 1] % mod * fac[cnt + e + 1] % mod;
if (a <= S) nxt[a] = a, inc(ts.lsum[a][cnt + e + 1], b);
else inc(ts.ssum[x / a][cnt + e + 1], b);
}
}
now = (i64)now * fac[cnt + 1] % mod;
for (i = pos; cur * primes[i] <= S; ++i) {
u = cur * primes[i], nxt[u] = u;
inc(ts.lsum[u][cnt + 1], (i64)now * (fval[i] + mod - fval[i - 1]));
}
i64 m = x / cur;
l = i, r = count;
if (cur * S > block) r = pi[block / cur];
for (u = l; u <= r; ++u) inc(ts.ssum[m / primes[u]][cnt + 1], (i64)now * (fval[u] + mod - fval[u - 1]));
} ;
dfs(pi[B] + 1, 1, 0, 1);
++ts.lsum[nxt[1] = 1][0];
ts.gsum();
for (i = 1; i <= S; ++i) if (!nxt[i]) nxt[i] = nxt[i - 1];
l = x / block - 1;
for (i = l; i >= 1; --i) {
i64 m = x / i;
array_t sum;
sum[0] = 1;
for (j = pi[B] + 1; (i64)i * primes[j] <= S && j <= count; ++j) inc(sum, ts.ssum[i * primes[j]], fval[j] + mod - fval[j - 1]);
for (; (i64)primes[j] * primes[j] <= m && j <= count; ++j) inc(sum, ts.lsum[m / primes[j]], fval[j] + mod - fval[j - 1]);
for (; j <= count; j = v + 1) {
k = nxt[m / primes[j]];
v = (i64)k * S > m ? pi[m / k] : count;
inc(sum, ts.lsum[k], fval[v] + mod - fval[j - 1]);
}
ts.ssum[i] = sum;
}
for (i = 1; i <= S; ++i) G.lsum[i] = ts.lsum[i].to_val(), G.ssum[i] = ts.ssum[i].to_val();
}
for (i = pi[B]; i >= 1; --i) G.update(primes[i], g[i]);
{
vector<pair<i64, int>> vh;
function<void(int, i64, int)> search = [&](int pos, i64 rem, int now){
vh.emplace_back(x / rem, now);
while ((i64)primes[pos] * primes[pos] <= rem) {
i64 t = rem / primes[pos] / primes[pos];
int e = 2;
for (; t; t /= primes[pos], ++e) if (h[pos][e]) search(pos + 1, t, (i64)now * h[pos][e] % mod);
++pos;
}
} ;
search(1, x, 1);
sort(vh.begin(), vh.end());
for (i = 0; i + 1 < vh.size(); ++i) if (vh[i].first == vh[i + 1].first) inc(vh[i + 1].second, vh[i].second), vh[i].second = 0;
vector<pair<i64, int>> vv;
for (i = 0; i < vh.size(); ++i) if (vh[i].second) vv.pb(vh[i]);
vh.swap(vv), vv.clear();
vh.emplace_back(x + 1, 0);
vector<int> hsum(RS + 1), hcnt(size), nxt(size);
for (auto it : vh) inc(it.first > S ? hsum[x / it.first] : hcnt[it.first], it.second);
for (i = RS - 1; i >= 1; --i) inc(hsum[i], hsum[i + 1]);
for (i = 1; i <= S; ++i) inc(hcnt[i], hcnt[i - 1]);
nxt[S] = S;
for (i = S - 1; i >= 1; --i) nxt[i] = hcnt[i] == hcnt[i + 1] ? nxt[i + 1] : i;
i64 low = pow(x, 0.6);
low = x / (x / low);
for (j = 0; vh[j].first <= low; ++j) {
for (i = 1; i * vh[j].first <= S; ++i) inc(F.lsum[i * vh[j].first], (i64)vh[j].second * (G.lsum[i] + mod - G.lsum[i - 1]));
for (i64 it = x / vh[j].first; i * vh[j].first <= low && i <= S; ++i) inc(F.ssum[it / i], (i64)vh[j].second * (G.lsum[i] + mod - G.lsum[i - 1]));
}
F.gsum(), low = std::min(x / low, S + 1LL);
for (i = S; i >= low; --i) for (j = 0; i * vh[j].first <= S; ++j) inc(F.ssum[i], (i64)vh[j].second * (G.ssum[i * vh[j].first] + mod - G.lsum[S]));
for (i = low - 1; i >= 1; --i) {
int w = 0;
i64 m = x / i;
for (j = 0; i * vh[j].first <= S; ++j) inc(w, (i64)vh[j].second * G.ssum[i * vh[j].first]);
for (; vh[j].first <= S; ++j) inc(w, (i64)vh[j].second * G.lsum[m / vh[j].first]);
for (j = 1; i * (j + 1) <= RS && j <= S; ++j) inc(w, (i64)G.lsum[j] * (hsum[i * j] + mod - hsum[i * (j + 1)]));
if (j <= S) inc(w, (i64)G.lsum[j] * hsum[i * j]);
F.ssum[i] = w;
}
}
return F;
}
struct lagrange{
vector<int> init, coef;
int n;
lagrange(vector<int> init): init(init){
int i;
coef.resize(n = init.size());
vector<int> ifac(n), inv(n);
for (ifac[0] = i = 1; i < n; ++i) {
inv[i] = i > 1 ? mod - (i64)inv[mod % i] * (mod / i) % mod : 1;
ifac[i] = (i64)ifac[i - 1] * inv[i] % mod;
}
for (i = 0; i < n; ++i) {
coef[i] = (i64)init[i] * ifac[n - i - 1] % mod * ifac[i] % mod;
if ((n & 1) == (i & 1)) coef[i] = mod - coef[i];
}
}
int get(i64 w){
int x = w % mod;
if (x < n) return init[x];
int res = 0, a = 1, i;
for (i = 0; i < n; ++i) {
res = ((i64)res * (x - i) + (i64)a * coef[i]) % mod;
a = (i64)a * (x - i) % mod;
}
return (res + mod) % mod;
}
};
vector<vector<int>> generate(function<int(int, int)> func){
int i, e;
i64 t;
vector<vector<int>> res(count + 1);
for (i = 1; i <= count; ++i) for (res[i].pb(t = e = 1); t * primes[i] <= x; t *= primes[i], ++e) res[i].pb(func(primes[i], e));
return res;
}
vector<vector<int>> conv(vector<vector<int>> a, vector<vector<int>> b){
int i, j, k, l;
decltype(a) c(count + 1);
for (i = 1; i <= count; ++i) for (c[i].resize(l = a[i].size()), j = 0; j < l; ++j) for (k = 0; j + k < l; ++k) inc(c[i][j + k], (i64)a[i][j] * b[i][k]);
return c;
}
data_t<int> primesum(int s){
int i, j, e, L = std::max(s + 2, S);
i64 t;
vector<int> coef(L + 1);
for (i = 1; i <= L; ++i) coef[i] = 1;
for (i = 2; i <= L; ++i) if (coef[i] == 1) for (coef[i] = fpow(i, s), j = 2; i * j <= L; ++j) coef[i * j] = (i64)coef[i] * coef[j] % mod;
vector<int> psum(coef.begin(), coef.begin() + s + 3);
for (i = 1; i < psum.size(); ++i) inc(psum[i], psum[i - 1]);
lagrange sol(psum);
data_t<int> fw = solve(generate([=](int p, int e){ return fpow(p, e * s); }));
for (i = 1; i <= S; ++i) fw.ssum[i] = (sol.get(x / i) + mod - fw.ssum[i]) % mod, fw.lsum[i] = 0;
for (i = S; i >= 1; --i) for (j = 2; i * j <= S; ++j) dec(fw.ssum[i], (i64)coef[j] * fw.ssum[i * j]);
return fw;
};
template<class value_t>
data_t<value_t> work(data_t<value_t> a, const function<int(int)> &op){
for (int i = 1; i <= S; ++i) a.ssum[i] = op(a.ssum[i]), a.lsum[i] = op(a.lsum[i]);
return a;
}
template<class value_t>
data_t<value_t> work(data_t<value_t> a, data_t<value_t> b, const function<int(int, int)> &op){
data_t<value_t> c;
for (int i = 1; i <= S; ++i) {
c.ssum[i] = op(a.ssum[i], b.ssum[i]);
c.lsum[i] = op(a.lsum[i], b.lsum[i]);
}
return c;
}
data_t<int> polysum(vector<int> poly){
int n = poly.size(), i, j;
vector<int> num(n + 2);
for (i = 1; i <= n + 1; ++i) {
for (j = n - 1; j >= 0; --j) num[i] = ((i64)num[i] * i + poly[j]) % mod;
num[i] = (num[i] + num[i - 1]) % mod;
}
lagrange sol(num);
data_t<int> res;
for (i = 1; i <= S; ++i) res.lsum[i] = sol.get(i), res.ssum[i] = sol.get(x / i);
return res;
}