前言
已经不知道第几次修改了。修过很多锅,也陆续新增了少许内容。数学的魅力就在于,它是没有被穷尽的。我也不相信它会有被穷尽的一天。
Comment. 用
P
\mathbb{P}
P 表示质数集,用
■
\blacksquare
■ 表示证毕,用 Comment
表示注释。
min25 \texttt{min25} min25 筛 / / / 洲阁筛
min25 \texttt{min25} min25 筛或洲阁筛的主要目的是求积性函数 f ( x ) f(x) f(x) 的前缀和。——其实也可以拓展到部分非积性函数 [2]。
Comment. 其实它也不能叫做筛法吧,但是其思想确实是筛法的。
我们首先用直观的方法描述之。该算法的总体思路是:
- 第一步,求出质数 p p p 对应的 f ( p ) f(p) f(p) 的前缀和。
- 第二步,求出 f ( x ) f(x) f(x) 的前缀和。
为什么会这样?因为质数的幂——相较于质数而言——数量很少。
小质数
一般的积性函数是复杂的。所以我们需要用 完全积性函数 去 拟合 原函数:只需要找到完全积性函数 ℏ ( x ) \hbar(x) ℏ(x) 使得 ℏ ( p ) = f ( p ) ( p ∈ P ) \hbar(p)=f(p)\;(p\in\mathbb{P}) ℏ(p)=f(p)(p∈P) 不变。因为 step one \text{step one} step one 只需要求出 ∑ p ∈ P f ( p ) \sum_{p\in\mathbb{P}}f(p) ∑p∈Pf(p) 。
Comment. 其实也可以不是完全积性的,可见后文 zzt \texttt{zzt} zzt 求和法。
用 g ( n , j ) g(n,j) g(n,j) 表示 [ 2 , n ] [2,n] [2,n] 去除前 j j j 个质数的倍数之后,剩余数字的 f ( x ) f(x) f(x) 之和。注意:要求 g ( n , j ) g(n,j) g(n,j) 时刻包含 [ 2 , n ] [2,n] [2,n] 之间所有质数的 f ( x ) f(x) f(x),也就是说,去除的是质数的 “真倍数”(不包含质数本身)。
形式化地,记
{
p
}
\{p\}
{p} 为从小到大的质数(角标从
1
1
1 开始),则
g
(
n
,
j
)
=
∑
i
=
1
j
f
(
p
i
)
+
∑
i
=
1
n
[
p
1
∤
i
∧
p
2
∤
i
∧
⋯
∧
p
j
∤
i
]
f
(
i
)
g(n,j)=\sum_{i=1}^{j}f(p_i)+\sum_{i=1}^{n}[p_1\nmid i\land p_2\nmid i\land\cdots\land p_j\nmid i]\;f(i)
g(n,j)=i=1∑jf(pi)+i=1∑n[p1∤i∧p2∤i∧⋯∧pj∤i]f(i)
我们的目标是求出 g ( n , + ∞ ) g(n,+\infty) g(n,+∞),也就是只留下了质数。
考虑
d
p
\tt dp
dp 求解。转移过程就是去掉
k
p
j
(
k
>
1
)
kp_j\;(k>1)
kpj(k>1) 。显然
k
k
k 不应该是前
(
j
−
1
)
(j{\rm-}1)
(j−1) 个质数的倍数,因为那是已经被移除的数。又因为
f
(
x
)
f(x)
f(x) 是完全积性函数,有
f
(
k
p
j
)
=
f
(
p
j
)
f
(
k
)
f(kp_j)=f(p_j)f(k)
f(kpj)=f(pj)f(k),于是有转移式
g
(
n
,
j
)
=
g
(
n
,
j
−
1
)
−
f
(
p
j
)
[
g
(
⌊
n
p
j
⌋
,
j
−
1
)
−
sumf
(
j
−
1
)
]
(
p
j
⩽
n
)
g(n,j)=g(n,j{\rm-}1)-f(p_j)\left[ g\left(\left\lfloor{\scriptsize\frac{n}{p_j}}\right\rfloor,j{\rm-}1\right) -\operatorname{sumf}(j{\rm-}1) \right]\;(p_j\leqslant\sqrt{n})
g(n,j)=g(n,j−1)−f(pj)[g(⌊pjn⌋,j−1)−sumf(j−1)](pj⩽n)
这里 sumf ( j − 1 ) = ∑ i = 1 j − 1 f ( p i ) \operatorname{sumf}(j{\rm-}1)=\sum_{i=1}^{j-1}f(p_i) sumf(j−1)=∑i=1j−1f(pi),因为 g g g 里面没有挖掉质数本身,减去它以修正。
特别地,如果 n < p j 2 n<p_j^{\thinspace 2} n<pj2,显然没有更多的数字需要被筛掉,此时 g ( i , j ) = g ( i , j − 1 ) g(i,j)=g(i,j{\rm-}1) g(i,j)=g(i,j−1) 。若在 j j j 这一维上做滚动数组,则无需修改这些位置。同时可知,需要的质数最大是 n \sqrt n n,所以直接线性筛即可求出 sumf \operatorname{sumf} sumf 了。
我们要对
g
(
i
,
0
)
g(i,0)
g(i,0) 赋初值。所以又涉及一个问题是,拟合函数的前缀和要易于求解。一般来说,我们会选择单项式作为拟合函数。不会真的有毒瘤题目需要用可以杜教筛的数论函数来拟合吧。
Comment. 多项式不总是完全积性的。将其拆解为多个单项式,分别求 g g g 再相加即可。
n n n 的范围很大。注意到求解 g ( n , j ) g(n,j) g(n,j) 时,只递归到 n ′ = ⌊ n v ⌋ n'=\lfloor{n\over v}\rfloor n′=⌊vn⌋ 的值;最后我们会看到,若求解 s s s 长度的前缀和,则我们只需求出 n = ⌊ s v ⌋ ( v ∈ N + ) n=\lfloor{s\over v}\rfloor\;(v\in\N^+) n=⌊vs⌋(v∈N+) 的答案,这样的 n n n 只有 O ( s ) \mathcal O(\sqrt{s}) O(s) 个。
具体复杂度计算见后文。
大质数
注意下面的方法中 f ( 1 ) f(1) f(1) 总是会被算漏。记得最后加上。
min25 \texttt{min25} min25 筛
我们前面只求出了质数的求和是 g ( s , + ∞ ) g(s,+\infty) g(s,+∞),所以现在我们只需要考虑合数了。
好消息:合数的最小质因数是不超过 s \sqrt{s} s 的!于是咱可以枚举这玩意儿。记 x x x 的最小质因数为 γ ( x ) ( x ⩾ 2 ) \gamma(x)\;(x\geqslant 2) γ(x)(x⩾2) 。
记 h ( n , j ) = ∑ i = 2 n [ γ ( i ) ⩾ p j ] f ( i ) h(n,j)=\sum_{i=2}^{n}[\gamma(i)\geqslant p_j]\;f(i) h(n,j)=∑i=2n[γ(i)⩾pj]f(i) 。欲求即 h ( s , 1 ) h(s,1) h(s,1) 。
枚举最小质因子与其幂次,可以写出转移式
h
(
n
,
j
)
=
g
(
n
,
+
∞
)
−
sumf
(
j
−
1
)
+
∑
i
⩾
j
∑
k
=
1
⌊
log
p
i
n
⌋
f
(
p
i
k
)
[
h
(
⌊
n
p
i
k
⌋
,
i
+
1
)
+
[
k
≠
1
]
]
h(n,j)=g(n,+\infty)-\operatorname{sumf}(j{-}1) \\ +\sum_{i\geqslant j} \sum_{k=1}^{\lfloor\log_{p_i}n\rfloor} f(p_i^{\thinspace k})\left[ h\Big(\Big\lfloor{ \scriptsize{n\over p_i^{\thinspace k}}} \Big\rfloor,i{\rm +}1\Big) +[k \ne 1] \right]
h(n,j)=g(n,+∞)−sumf(j−1)+i⩾j∑k=1∑⌊logpin⌋f(pik)[h(⌊pikn⌋,i+1)+[k=1]]
用 g ( n , + ∞ ) − sumf ( j − 1 ) g(n,+\infty)-\operatorname{sumf}(j{-}1) g(n,+∞)−sumf(j−1) 计算了 [ 2 , n ] [2,n] [2,n] 中的质数 p c ( c ⩾ j ) p_c\;(c\geqslant j) pc(c⩾j),用 f ( p i k ) ⋅ h ( … ) f(p_i^{\thinspace k})\cdot h(\dots) f(pik)⋅h(…) 计算了含至少两个质因子的合数,用 [ k ≠ 1 ] f ( p i k ) [k\ne 1]\;f(p_i^{\thinspace k}) [k=1]f(pik) 计算了质数的幂,不重不漏。
与 step one \text{step one} step one 同理,只在 p i ⩽ n p_i\leqslant\sqrt{n} pi⩽n 时转移。有趣的是,我们可以直接 递归,而且 不进行记忆化。详细复杂度计算见后文。
洲阁筛
令
h
(
n
,
j
)
h(n,j)
h(n,j) 始终包含
[
2
,
s
]
[2,s]
[2,s] 全体质数。这是利于转移的要求。
h
(
n
,
j
)
=
h
(
n
,
j
+
1
)
+
∑
k
=
1
⌊
log
p
j
n
⌋
f
(
p
j
k
)
[
h
(
⌊
i
p
j
k
⌋
,
k
+
1
)
−
sumf
(
j
)
+
[
k
≠
1
]
]
h(n,j)=h(n,j{+}1)+ \sum_{k=1}^{\lfloor\log_{p_j}n\rfloor} f(p_j^{\thinspace k})\left[ h\Big( \Big\lfloor{\scriptsize \frac{i}{p_j^{\thinspace k}} }\Big\rfloor, k{+}1 \Big)-\operatorname{sumf}(j)+[k\ne 1] \right]
h(n,j)=h(n,j+1)+k=1∑⌊logpjn⌋f(pjk)[h(⌊pjki⌋,k+1)−sumf(j)+[k=1]]
转移条件仍为 p j ⩽ n p_j\leqslant\sqrt{n} pj⩽n 。初值为 h ( n , + ∞ ) = g ( n , + ∞ ) h(n,+\infty)=g(n,+\infty) h(n,+∞)=g(n,+∞),相当于在 g g g 的基础上接着递推。
没错,二者明明只是递归和递推的区别,但它的名字就变成了洲阁筛。小编也很好奇。
代码实现
嗯,直接上代码。以板题 f ( p k ) = p k ⋅ ( p k − 1 ) f(p^k)=p^k\cdot (p^k{-}1) f(pk)=pk⋅(pk−1) 为例:需要用 x 2 x^2 x2 和 x x x 分别拟合,于是将 g g g 设为 pair \texttt{pair} pair 类型。
这里的代码是递归的。在 补充信息
中的题目有递推版代码。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MOD = 1e9+7, SQRTN = 100005;
int primes[SQRTN], primes_size;
bool isPrime[SQRTN];
struct Node{
int one, two;
Node() = default;
Node(int x):one(x),two(int(llong(x)*x%MOD)){}
Node(int _o,int _t):one(_o),two(_t){}
Node operator * (const Node &t) const {
return Node(int(llong(one)*t.one%MOD),
int(llong(two)*t.two%MOD));
}
Node operator - (const Node &t) const {
return Node((one-t.one+MOD)%MOD,(two-t.two+MOD)%MOD);
}
int val() const { return (two-one+MOD)%MOD; }
};
Node sumf[SQRTN];
void sieve(int n){
memset(isPrime+2,true,n-1);
for(int i=2; i<=n; ++i){
if(isPrime[i]){
primes[++ primes_size] = i;
Node &v = sumf[primes_size] = sumf[primes_size-1];
if((v.one += i) >= MOD) v.one -= MOD;
v.two = int((v.two+llong(i)*i)%MOD);
}
for(int j=1; j<=primes_size&&primes[j]<=n/i; ++j){
isPrime[i*primes[j]] = false;
if(i%primes[j] == 0) break;
}
}
}
const int inv2 = (MOD+1)>>1, inv3 = (MOD+1)/3;
int haxi[2][SQRTN]; // value or divisor
inline int& index_(const llong &x,const llong &n){
return (x < SQRTN) ? haxi[0][x] : haxi[1][n/x];
}
llong w[SQRTN<<1]; ///< positions to get sum
Node g[SQRTN<<1]; ///< 2 times SQRTN
void step_one(const llong &n){
int tot = 0; ///< allocate index
for(llong i=1; i<=n; i=n/(n/i)+1){
w[++ tot] = n/i; index_(n/i,n) = tot;
g[tot].one = int(((w[tot]%MOD+1)*(w[tot]%MOD)>>1)%MOD)-1;
g[tot].two = int((w[tot]<<1|1)%MOD*(w[tot]%MOD+1)
%MOD*(w[tot]%MOD)%MOD*inv2%MOD*inv3%MOD)-1;
}
for(int j=1; j<=primes_size; ++j){
if(primes[j] > n/primes[j]) break;
for(int i=1; i<=tot; ++i){
if(primes[j] > w[i]/primes[j]) break;
g[i] = g[i]-Node(primes[j])*(
g[index_(w[i]/primes[j],n)]-sumf[j-1]);
}
}
}
inline llong func(const llong &x){
return (x-1)%MOD*(x%MOD)%MOD; // definition
}
int step_two(const llong &x,int i,const llong &n){
if(primes[i] > x) return 0;
int res = g[index_(x,n)].val()-sumf[i-1].val();
for(; i<=primes_size&&primes[i]<=x/primes[i]; ++i){
llong t = primes[i], fk = x/t;
for(; t<=fk; t*=primes[i])
res = int((func(t*primes[i])+func(t)
*step_two(x/t,i+1,n)+res)%MOD);
}
return (res >= 0) ? res : (res+MOD);
}
int main(){
sieve(SQRTN-1); // just do it
llong n; scanf("%lld",&n); step_one(n);
printf("%d\n",step_two(n,1,n)+1);
return 0;
}
时间复杂度
小质数
step one \text{step one} step one 的复杂度比较好算,因为转移是 O ( 1 ) \mathcal O(1) O(1) 的,只算转移次数。每个 w w w 要用到不超过 w \sqrt{w} w 的质数,所以复杂度约为
∑ i = 1 n n i ln n i ≈ ∫ 1 n n x ln ( n x ) d x \sum_{i=1}^{\sqrt{n}}\frac{\sqrt{n\over i}}{\ln\sqrt{n\over i}}\approx \int_{1}^{\sqrt{n}}{\sqrt{n}\over \sqrt{x}\ln\left({n\over x}\right)}\text dx i=1∑nlninin≈∫1nxln(xn)ndx
2021 / 8 / 1 u p d a t e \tt 2021/8/1\;update 2021/8/1update:由于被 H a n d I n D e v i l \sf HandInDevil HandInDevil 臭骂了一顿,稍微讲讲用积分计算复杂度的事儿。它实际上是将数论函数的求和,转化为了连续函数的积分。这是否能作为渐进复杂度呢?
一般而言,原数论函数是单调的,且不存在奇点。在此情形下,考虑用 ∫ δ δ + 1 g ( x ) d x \int_{\delta}^{\delta+1} g(x)\text dx ∫δδ+1g(x)dx 拟合 f ( δ ) ( δ ∈ Z ) f(\delta)\;(\delta\in\Z) f(δ)(δ∈Z),则结果是偏小(或偏大)的;而用 ∫ δ − 1 δ g ( x ) d x \int_{\delta-1}^{\delta}g(x)\text dx ∫δ−1δg(x)dx 去拟合 f ( δ ) f(\delta) f(δ) 又会偏大(或偏小)。由于有 0 0 0 是奇点的风险,上界(或下界)可能是 ∫ 1 n g ( x ) d x + f ( 1 ) \int_1^ng(x)\text dx+f(1) ∫1ng(x)dx+f(1),下界(或上界)是 ∫ 2 n + 1 g ( x ) d x \int_2^{n+1}g(x)\text dx ∫2n+1g(x)dx 。二者往往是等阶的,那么就 “夹逼” 出了确界。
现在回到正题上。由于我已知道了结果,我告诉你
∫
1
x
ln
(
n
x
)
d
x
=
O
[
x
ln
(
n
x
)
]
\int{1\over \sqrt{x}\ln({n\over x})}\text dx=\mathcal O\left[{\sqrt x\over\ln({n\over x})}\right]
∫xln(xn)1dx=O[ln(xn)x]
因为
ln
x
\ln x
lnx 型函数作分母比较特殊:右式的导数实际上是
1
2
x
ln
(
n
x
)
+
1
x
ln
2
(
n
x
)
\frac{1}{2\sqrt x\ln({n\over x})}+\frac{1}{\sqrt x\ln^2({n\over x})}
2xln(xn)1+xln2(xn)1,但是在大
O
\mathcal O
O 表示法下就是左式。代回原式可知复杂度为
O
(
n
3
4
ln
n
)
\mathcal O\left(\frac{n^{3\over 4}}{\ln n}\right)
O(lnnn43)
min25 \texttt{min25} min25 筛
复杂度即 h h h 计算中的求和次数和。这和 [ k ≠ 1 ] f ( p i k ) [k\ne 1]\;f(p_i^{\thinspace k}) [k=1]f(pik) 被计入的次数相同。
注意到递归的本质是搜索,可以设这个 f ( p i k ) f(p_i^{\thinspace k}) f(pik) 实际上来自 f ( l ) f(l) f(l) 。设 l = m p i l=mp_i l=mpi,则 p i p_i pi 恰为 m m m 的最大质因子,因为 k ≠ 1 k\ne 1 k=1 。另一方面,设 big ( x ) \operatorname{big}(x) big(x) 为 x x x 的最大质因子,则 x big ( x ) ⩽ s x\operatorname{big}(x)\leqslant s xbig(x)⩽s 必须在 big ( x ) \operatorname{big}(x) big(x) 处统计贡献。因此它的复杂度就是 ∑ i = 2 s [ i big ( i ) ] ⩽ s \sum_{i=2}^{s}[i\operatorname{big}(i)]\leqslant s ∑i=2s[ibig(i)]⩽s 。
Lemma. 对于实数 α ∈ ( 0 , 1 ) \alpha\in(0,1) α∈(0,1),令 Q ( n ) = { i ⩽ n : big ( i ) ⩽ i α } Q(n)=\{i\leqslant n:\operatorname{big}(i)\leqslant i^{\alpha}\} Q(n)={i⩽n:big(i)⩽iα},则 ∣ Q ( n ) ∣ ∼ n ρ ( α − 1 ) |Q(n)|\sim n\rho(\alpha^{-1}) ∣Q(n)∣∼nρ(α−1),其中 ρ \rho ρ 是 Dickman function \text{Dickman function} Dickman function [3]。
若令 M ( n ) = { i : i big ( i ) ⩽ n } M(n)=\{i:i\operatorname{big}(i)\leqslant n\} M(n)={i:ibig(i)⩽n},则 ∀ a ∈ ( 0 , 1 ) , ∣ M ( n ) ∣ = Ω ( n α ) \forall a\in(0,1),\;|M(n)|=\Omega(n^\alpha) ∀a∈(0,1),∣M(n)∣=Ω(nα) 。因为 Lemma \text{Lemma} Lemma 告诉我们 P = { i ⩽ n α : big ( i ) ⩽ n 1 − α } P=\{i\leqslant n^\alpha:\operatorname{big}(i)\leqslant n^{1-\alpha}\} P={i⩽nα:big(i)⩽n1−α} 的大小是 Ω ( n α ) \Omega(n^\alpha) Ω(nα),而 P ⊆ M ( n ) P\subseteq M(n) P⊆M(n) 。
另一方面,取 t = ⌈ log log n ⌉ t=\lceil\log\log n\rceil t=⌈loglogn⌉,则 { x ⩽ n : big ( x ) ⩽ p t } \{x\leqslant n:\operatorname{big}(x)\leqslant p_t\} {x⩽n:big(x)⩽pt} 大小不超过 ( log n ) t = o ( n log log n ) (\log n)^t=o({n\over\log\log n}) (logn)t=o(loglognn),因为每个质因数的次数不超过 log n \log n logn 。而其他的 i big ( i ) ⩽ n i\operatorname{big}(i)\leqslant n ibig(i)⩽n 的数满足 i ⩽ n p t ⩽ n log log n i\leqslant{n\over p_t}\leqslant{n\over\log\log n} i⩽ptn⩽loglognn,因此 ∣ M ( n ) ∣ = O ( n log log n ) |M(n)|=\mathcal O({n\over\log\log n}) ∣M(n)∣=O(loglognn) 。
于是乎,我们得到 ∣ M ( n ) ∣ = Θ ( n 1 − ϵ ) |M(n)|=\Theta(n^{1-\epsilon}) ∣M(n)∣=Θ(n1−ϵ) 。也就是说,它的渐进复杂度是错误的(从严格的数学意义上)。
Comment. 这里 n 1 − ϵ n^{1-\epsilon} n1−ϵ 指优于 n n n 又劣于 n α ( α < 1 ) n^\alpha\;(\alpha<1) nα(α<1),应该容易理解。
但是打表发现, n ⩽ 1 0 13 n\leqslant 10^{13} n⩽1013 时,对于 p ⩽ n 1 / 4 p\leqslant n^{1/4} p⩽n1/4,满足 big ( i ) = p \operatorname{big}(i)=p big(i)=p 的 i i i 的个数是 n \sqrt{n} n 级别的,而一共只有 O ( n 1 / 4 ln n ) \mathcal O(\frac{n^{1/4}}{\ln n}) O(lnnn1/4) 个 p p p,因此其提供的贡献是 n 3 / 4 ln n n^{3/4}\over\ln n lnnn3/4 的 [3]。对于 big ( i ) ⩾ n 1 / 4 \operatorname{big}(i)\geqslant n^{1/4} big(i)⩾n1/4 不难发现其提供贡献是 O ( n 3 / 4 ln n ) \mathcal O({n^{3/4}\over\ln n}) O(lnnn3/4) 的。
一句话:渐进复杂度是错误的,但是它跑得足够快。
洲阁筛
这就很简单了。质数密度
π
(
n
)
≃
n
ln
n
\pi(n)\simeq{n\over\ln n}
π(n)≃lnnn,很容易看出
∑
j
=
2
⌊
log
2
n
⌋
π
(
w
j
)
=
o
(
π
(
n
)
)
\sum_{j=2}^{\lfloor\log_2 n\rfloor}\pi(\sqrt[j]{w})=o(\pi(n))
∑j=2⌊log2n⌋π(jw)=o(π(n)),因此复杂度为
∑
w
=
1
n
∑
j
=
1
log
w
π
(
n
/
w
j
)
=
O
(
∑
w
=
1
n
π
(
w
)
)
=
O
(
n
3
/
4
ln
n
)
\sum_{w=1}^{\sqrt n}\sum_{j=1}^{\log w}\pi\left(\sqrt[j]{n/w}\right) =\mathcal O\left(\sum_{w=1}^{\sqrt{n}}\pi(w)\right) =\mathcal O\left({n^{3/4}\over\ln n}\right)
w=1∑nj=1∑logwπ(jn/w)=O⎝
⎛w=1∑nπ(w)⎠
⎞=O(lnnn3/4)
这就是所谓 “质数的幂的数量是很少的——相较于质数而言”。
z z t \sf zzt zzt 求和法
源自 [4],也可以参阅 [5] 获得更多信息。
若原函数在素数处的取值可以被易于计算前缀和的数论函数拟合,则可以用该方法。后面会说到,这种方法有 min25 \texttt{min25} min25 筛的影子。
前置知识:狄利克雷生成函数和杜教筛。
考虑
D
i
r
i
c
h
l
e
t
\rm Dirichlet
Dirichlet 双曲线法计算
f
∗
g
f*g
f∗g 在所有
⌊
n
v
⌋
\lfloor{n\over v}\rfloor
⌊vn⌋ 位置的前缀和。若
f
,
g
f,g
f,g 的非零项密度为
O
(
1
ln
n
)
\mathcal O({1\over\ln n})
O(lnn1),则计算
f
∗
g
f*g
f∗g 的前
x
x
x 项的和的复杂度是
x
ln
n
{\sqrt x\over\ln n}
lnnx,故总复杂度是
∑
x
=
1
⌊
n
S
⌋
n
x
ln
n
=
O
(
n
2
/
3
S
ln
n
)
\sum_{x=1}^{\lfloor{n\over S}\rfloor}{\sqrt{n\over x}\over\ln n}=\mathcal O\left({n^{2/3}\over\sqrt{S}\ln n}\right)
x=1∑⌊Sn⌋lnnxn=O(Slnnn2/3)
而前 S S S 项是狄利克雷卷积求出的,则复杂度为 O ( S log S ln 2 n ) \mathcal O({S\log S\over\ln^2 n}) O(ln2nSlogS) 。仍取 S = n 2 / 3 S=n^{2/3} S=n2/3,但复杂度已经是 O ( n 2 / 3 ln n ) \mathcal O({n^{2/3}\over\ln n}) O(lnnn2/3) 了,而且不基于线性筛(不基于积性)。
考虑狄利克雷生成函数,设
F
p
F_p
Fp 为质数
p
p
p 上的狄利克雷生成函数,则
F
(
x
)
=
[
∏
p
⩽
n
1
/
6
F
p
(
x
)
]
[
∏
n
1
/
6
<
p
⩽
n
1
/
2
F
p
(
x
)
]
[
∏
n
1
/
2
<
p
F
p
(
x
)
]
F(x)=\left[\prod_{p\leqslant n^{1/6}}F_p(x)\right] \left[\prod_{n^{1/6}<p\leqslant n^{1/2}}F_p(x)\right] \left[\prod_{n^{1/2}<p}F_p(x)\right]
F(x)=⎣
⎡p⩽n1/6∏Fp(x)⎦
⎤⎣
⎡n1/6<p⩽n1/2∏Fp(x)⎦
⎤⎣
⎡n1/2<p∏Fp(x)⎦
⎤
先求第二部分。
定理:恰含 k k k 个质因子,且最小质因子至少为 n α ( α < 0.5 ) n^\alpha\;(\alpha<0.5) nα(α<0.5) 的 n n n 以内的数的个数是 O [ ( α − 1 − 1 ) k n ln − 1 n ] \mathcal O[(\alpha^{-1}{-}1)^kn\ln^{-1}n] O[(α−1−1)knln−1n] 。简记为 ϕ k ( n α , n ) \phi_k(n^\alpha,n) ϕk(nα,n) 。
证明:当
k
=
1
k=1
k=1 时显然成立。归纳法,枚举最小质因子
x
∈
[
2
,
n
]
x\in[2,\sqrt{n}]
x∈[2,n],则
ϕ
k
(
x
,
n
x
)
=
O
(
(
α
−
1
−
1
)
k
n
x
log
x
)
\phi_k(x,{n\over x})=\mathcal O(\frac{(\alpha^{-1}{-}1)^kn}{x\log x})
ϕk(x,xn)=O(xlogx(α−1−1)kn),于是估算
ϕ
k
+
1
(
n
α
,
n
)
≈
∫
n
α
n
d
x
ln
x
(
α
−
1
−
1
)
k
⋅
n
x
ln
x
=
O
[
(
α
−
1
−
1
)
k
+
1
n
ln
−
1
n
]
\phi_{k+1}(n^{\alpha},n)\approx \int_{n^\alpha}^{\sqrt{n}}\frac{\text{d}x}{\ln x}\frac{(\alpha^{-1}{-}1)^k\cdot n}{x\ln x} =\mathcal O[(\alpha^{-1}{-}1)^{k+1}n\ln^{-1}n]
ϕk+1(nα,n)≈∫nαnlnxdxxlnx(α−1−1)k⋅n=O[(α−1−1)k+1nln−1n]
即证。 ■ \blacksquare ■
为了叙述方便,设第二部分对应的积性函数是 f 2 ( x ) f_2(x) f2(x),并记 P 2 = P ∩ ( n 1 / 6 , n 1 / 2 ] \mathbb P_2=\mathbb P\cap(n^{1/6},n^{1/2}] P2=P∩(n1/6,n1/2] 。
考虑忽略质因子顺序,每次任选某个质数,将其指数加一。设非积性的函数 inc ( p ) = [ p ∈ P 2 ] f 2 ( p ) \text{inc}(p)=[p\in\mathbb P_2]\;f_2(p) inc(p)=[p∈P2]f2(p),我们从 inc ( x ) \text{inc}(x) inc(x) 开始,递推求出 inc k + 1 = inc k ∗ inc ( k ⩾ 1 ) \text{inc}^{k+1}=\text{inc}^k\ast\text{inc}\;(k\geqslant 1) inck+1=inck∗inc(k⩾1) 。
由定理一知 inc k ( x ) \text{inc}^k(x) inck(x) 非零项密度是 O ( 1 ln n ) \mathcal O({1\over\ln n}) O(lnn1) 。因此这样乘法的复杂度是 O ( n 2 / 3 ln n ) \mathcal O({n^{2/3}\over\ln n}) O(lnnn2/3) 。注意 k ⩽ ⌊ 1 α ⌋ k\leqslant \lfloor{1\over\alpha}\rfloor k⩽⌊α1⌋ 是常数,因此可以全部求出。
将它们累加,得到 g ( x ) = ∑ k ⩾ 1 g k ( x ) g(x)=\sum_{k\geqslant 1}g_k(x) g(x)=∑k⩾1gk(x) 。设 n = ∏ p i t i n=\prod p_i^{t_i} n=∏piti,不难发现,其值是二项式系数 g ( n ) = ( ∑ t i ) ! ∏ ( t i ! ) ∏ inc ( p i ) t i g(n)={(\sum t_i)!\over\prod(t_i!)}\prod\text{inc}(p_i)^{t_i} g(n)=∏(ti!)(∑ti)!∏inc(pi)ti 。
于是构造积性函数 fit ( p t ) = inc ( p ) t ( t ! ) − 1 ( p ∈ P 2 ) \text{fit}(p^t)=\text{inc}(p)^t(t!)^{-1}\;(p\in\mathbb P_2) fit(pt)=inc(p)t(t!)−1(p∈P2),我们求出 g ( n ) ( ∑ t i ) ! {g(n)\over(\sum t_i)!} (∑ti)!g(n) 为 fit ( x ) \text{fit}(x) fit(x) 的前缀和。注意到 fit ( p ) = inc ( p ) = f 2 ( p ) \text{fit}(p)=\text{inc}(p)=f_2(p) fit(p)=inc(p)=f2(p),因此用 powerful number \text{powerful number} powerful number 的方法,可以 O ( n ) \mathcal O(\sqrt{n}) O(n) 从 fit \text{fit} fit 前缀和过渡到 f 2 f_2 f2 的前缀和。
先考虑怎么算第一部分。直接依次加入质数即可。每次加入时
O
(
n
log
p
n
)
\mathcal O(\sqrt{n}\log_p n)
O(nlogpn) 暴力更新,复杂度
∑
p
∈
P
1
n
log
p
n
∼
∫
1
n
1
/
6
d
p
ln
n
n
log
p
n
∼
n
li
(
n
1
/
6
)
=
O
(
n
2
/
3
ln
n
)
\sum_{p\in\mathbb P_1}\sqrt{n}\log_p n\sim\int_{1}^{n^{1/6}}\frac{\text{d}p}{\ln n}\sqrt{n}\log_p n\\ \sim\sqrt{n}\operatorname{li}(n^{1/6})=\mathcal O\left({n^{2/3}\over\ln n}\right)
p∈P1∑nlogpn∼∫1n1/6lnndpnlogpn∼nli(n1/6)=O(lnnn2/3)
怎么算第三部分。用易于求解前缀和的数论函数来拟合之,用类似 min25 \texttt{min25} min25 的方式,筛掉小质数。但是我们直接求出小质数对应的积性函数,则可以用上面的方法(第二部分 + + + 第一部分)做到 O ( n 2 / 3 log n ) \mathcal O({n^{2/3}\over\log n}) O(lognn2/3) 。
Comment. 若对
p
<
n
p<\sqrt{n}
p<n 直接依次加入,则复杂度是
O
(
n
3
/
4
log
n
)
\mathcal O({n^{3/4}\over\log n})
O(lognn3/4) 的。这似乎就是
min25
\texttt{min25}
min25 呢(枚举每个小质数并更新
O
(
n
)
\mathcal O(\sqrt{n})
O(n) 个前缀和值),但是这就不再要求是积性函数了。估计跟第二部分原理相同。
然后现在的问题是,知 f 3 ∗ a 1 = a 2 f_3\ast a_1=a_2 f3∗a1=a2,反推 f 3 f_3 f3 。由于 f 3 f_3 f3 在 [ 1 , n ] [1,\sqrt{n}] [1,n] 内只有 f 3 ( 1 ) = 1 f_3(1)=1 f3(1)=1,因此算 w w w 前缀和时,需要枚举的部分只有 O ( w n ) \mathcal O({w\over\sqrt n}) O(nw) 长,易知其复杂度为 O ( n log n ) \mathcal O(\sqrt{n}\log n) O(nlogn) 。
算出来 f 3 f_3 f3 后,我们将其与 f 1 ∗ f 2 f_1\ast f_2 f1∗f2 合并。仍然利用 f 3 f_3 f3 在 [ 1 , n ] [1,\sqrt{n}] [1,n] 内只有 f 3 ( 1 ) = 1 f_3(1)=1 f3(1)=1 非零的性质,合并还是 O ( n log n ) \mathcal O(\sqrt{n}\log n) O(nlogn) 的。
所以我们得到了总复杂度为 O ( n 2 / 3 log n ) \mathcal O({n^{2/3}\over\log n}) O(lognn2/3) 的,素数处取值是多项式的积性函数的求和法。但是,据原作者说,这个做法并没有效率提升 😂
SBT \text{SBT} SBT 割线法
由于我能力较弱,这里就只做实用主义的探讨。
算法最初的目标是,求解 σ 0 ( n ) = ∑ d ∣ n 1 \sigma_0(n)=\sum_{d\mid n}1 σ0(n)=∑d∣n1 约数个数函数的前缀和。
方案是,用折线去拟合曲线 x y = n xy=n xy=n,使得曲线下方(含边界)的整点就是折线下方(不含边界)的全体整点。
仍然用 D i r i c h l e t \rm Dirichlet Dirichlet 双曲线法,则 x , y ⩽ n x,y\leqslant\sqrt{n} x,y⩽n 部分 O ( 1 ) \mathcal O(1) O(1) 即得,因此只需计算 y ⩽ n y\leqslant\sqrt{n} y⩽n 的部分。
从 x = ⌈ n + 1 y ⌉ , y = 1 + ⌊ n ⌋ x=\lceil{n+1\over y}\rceil,\;y=1+\lfloor{\sqrt n}\rfloor x=⌈yn+1⌉,y=1+⌊n⌋ 开始,每次找正整数 p , q p,q p,q 满足 ( x + p ) ( y − q ) > n (x{+}p)(y{-}q)>n (x+p)(y−q)>n,让 q p q\over p pq 最大的前提下最小化 p p p 。特别地,若 y = 1 y=1 y=1 则终止该过程。不难发现这样得到的折线符合要求且唯一存在。
怎么找?利用 Stern-Brocot Tree \text{Stern-Brocot Tree} Stern-Brocot Tree 即可。其简单讲解在此。
一个重要的点在于,判断某子树内是否还有可能会用到的斜率。也就是说,目前得到两个分数 a b , c d {a\over b},{c\over d} ba,dc,已知 a b a\over b ba 可行而 c d c\over d dc 不可行,问是否存在互质的正整数 p , q p,q p,q 使得 p a + q c p b + q d \frac{pa+qc}{pb+qd} pb+qdpa+qc 可行。
代码给出的判断是:若 ( x + d ) 2 ⋅ a ⩾ n ⋅ b (x{+}d)^2\cdot a\geqslant n\cdot b (x+d)2⋅a⩾n⋅b 就停止。尚不清楚其原理。
这个做法被证明是 O ~ ( n 1 / 3 ) \tilde{\mathcal O}(n^{1/3}) O~(n1/3) 的,最多是 O ( n 1 / 3 log 3 n ) \mathcal O(n^{1/3}\log^3 n) O(n1/3log3n) 的,据称可被证明是 O ( n 1 / 3 log n ) \mathcal O(n^{1/3}\log n) O(n1/3logn) 。
补充信息
这道题是非积性函数,也可以做。内有递推版代码。
据说 min25 \texttt{min25} min25 有了新版,不知道啥情况,这里只 m a r k \rm mark mark 一下。
R e f e r e n c e \rm Reference Reference
[1] jokerwyt. Min 25筛入门[EB/OL]. blog.csdn.net/j….
[2] GuessYCB. 关于min_25筛的一些理解[EB/OL]. cnblogs.com/G….
[3] 朱震霆. 一些特殊的数论函数求和问题[EB/OL]. private-link.
[4] whzzt. 积性函数求和问题的一种筛法[EB/OL]. blog.csdn.net/w….
[5] negiizhao. zzt求和法的简化版. negiizhao.blog.uoj.ac/blog/7165.