前言
Min25 筛是一种对于满足特定条件积性函数的前缀和的亚线性筛法。虽说 Min25 筛对积性函数有一定要求,但其实常见的很多积性函数都是能被筛出来的。另外,Min25 筛也能引出不少灵活变化,故个人感觉也是一种非常有趣的筛法。
Min25 筛
核心思想
下文默认记 P
为素数集,Pi
代表其中第 i
大的素数并默认 p∈P
。若函数 f(x)
满足:
- f(p)
存在多项式表示;
- f(pe)
可被快速计算;
- 若 a⊥b
,则 f(ab)=f(a)f(b)
。
Min25 筛可在 O(lognn43)
的复杂度内计算 i=1∑nf(i)
,其核心思想即将 [1,n]
分为质数、1
和剩余数三个部分进行讨论:
i=1∑nf(i)=i=1∑n[i∈P]f(i)+i=1∑n[i∈P]f(i)=i=1∑n[i∈P]f(i)+f(1)+p∈P∑1≤pe≤n∑f(pe)i=1∑⌊pen⌋f(i)
筛素数答案
既然前面提到了在素数处 f
应有多项式表示,那么我们可以对每个幂次分开考虑。即我们可以把问题简化为计算 f(p)=pk
的情形,只要对所有的 k
分别计算出结果再乘上对应系数即可。
首先我们考虑求解:
i=1∑n[i∈P]f(i)
考虑先线性筛出不大于 n
的所有素数作为 P
。记 minp(x)
代表 x
的最小素因子,令:
g(n,j)=i=2∑n[i∈P or minp(i)>Pj]f(i)
则有:
i=1∑n[i∈P]f(i)=g(n,∣P∣)
考虑如何对 g
进行状态转移。g(n,j)
在 g(n,j−1)
的基础上少了所有非质数并且最小质因子恰为 Pj
的数作为因变量的 f
值。显然,这些数可以由一个最小质因子大于 Pj
的数 t
乘上 Pj
得到。因为 f
是积性函数且 t⊥Pj
,因此 f(tPj)=f(Pj)f(t)
。考虑把 f(Pj)
提出来,则需要减去的部分即为:
f(Pj)[g(⌊Pjn⌋,j−1)−i=1∑j−1f(Pi)]
式子中的 i=1∑j−1f(Pi)
本质上就是 i=1∑j−1Pik
,因此完全可以在筛素数的时候一并处理出来。
完整的状态转移即:
g(n,j)=⎩⎨⎧g(n,j−1)−f(Pj)[g(⌊Pjn⌋,j−1)−i=1∑j−1f(Pi)]g(n,j−1)Pj2≤notherwise
对于初值,有 g(n,0)=i=2∑nf(i)
。如果次数较低,比如平方和立方和都是有公式可以快速得出的,而如果次数较高的话就是多项式插值经典问题了。
接下来讨论一些实现上的细节。首先,第二维可以滚动以节约空间。另外由于 n
比较大,故显然需要对第一维离散化。
又由向下取整的一个性质:
⌊b⌊an⌋⌋=⌊abn⌋
故转移时所需要的第一维一定形如 ⌊xn⌋
形式,我们可以对这 O(n)
个数离散化一下。如果用 std::map 的话会多白给一个 O(logn)
,一种更高效的做法是用两个数组,其中 indx1[x]
表示 x
离散化后的下标,而 indx2[x]
表示 xn
对应的下标。这样的好处在于两个数组的下标都不会超过 n
。在访问 x
离散化后的下标时,若 x<n
则可直接访问 indx1[x]
,而若 x≥n
则可访问 indx2[xn]
,实属精妙。
筛非质数答案
接下来我们考虑求解:
i=1∑n[i∈P]f(i)
由于 f
在素数处有多项式表示,因此在筛 g
的时候我们可以按每一个幂次分开计算。但是如果把合数也拉进来,就不可以拆开了。因此这一部分中 f
指的就是原方程。
令:
s(n,j)=i=2∑n[minp(i)≥Pj]f(i)
很显然,我们最终想要的答案即:
i=1∑nf(i)=s(n,1)+f(1)
现在我们来考虑如何求解 s(n,1)
。对 s
进行状态转移的时候,我们可以沿用分类的思想,即把它分成素数处之和和剩余数处之和两部分:
s(n,j)=i=Pj+1∑n[i∈P]f(i)+i=Pj+1∑n[i∈P]f(i)
第一部分我们之前已经解决了,就是 g(n,∣P∣)−k=1∑j−1f(Pk)
。
对于第二部分,既然是满足 minp≥Pj
的合数,那么自然可以表示为 Pje⋅t
的形式(且满足 Pje⊥t
)。与此同时,显然有 minp(t)≥Pj+1
。我们可以先枚举最小素因子 Pk
,然后对其枚举幂次 e
,然后利用积性函数的性质作如下计算:
k=j∑∣P∣Pke+1≤n∑[f(Pke)⋅s(⌊Pken⌋,k+1)+f(Pke+1)]
最后加上 f(Pke+1)
的原因是前面利用积性函数性质的部分无法计算形如 f(pk)
处的值。
完整的状态转移如下:
s(n,j)=g(n,∣P∣)−k=1∑j−1f(Pk)+k=j∑∣P∣Pke+1≤n∑[f(Pke)⋅s(⌊Pken⌋,k+1)+f(Pke+1)]
直接递归搜即可,不需要记忆化复杂度也是对的(证明不来),如果有多个状态可以考虑开个结构体一起转移。
应用
模板
首先可以试试洛谷上的模板题:Luogu P5325: Min25筛。同时附上 我的代码 以供参考。
那对于常见的一些数论函数呢?
欧拉函数 φ
:
- φ(p)=p−1
,是个多项式;
- φ(pe)=pe(1−p1)=pe−1(p−1)
,可以被快速计算;
- φ
是个积性函数。
莫比乌斯函数 μ
:
- μ(p)=1
;
- μ(pe)=0 (e>1)
;
- μ
是积性函数。
除数函数 d
:
- d(p)=2
;
- d(pe)=e+1
;
- d
是积性函数。
显然上述三种函数都是可以用 Min25 筛求解的。更多的例子这里就不列举了,大家可以自己试试看~
类积性函数?
注意到 Min25 筛求解前缀和的过程本质上就是个 DP。考虑是否可以魔改一下 DP 方程使得其适用于一些其他的 f
函数,比如类似于满足 ∀a⊥b, f(ab)=f(a)+f(b)
的函数 f
。
前段时间乱逛计蒜客的时候发现了这道题:Jisuanke A2243: A Simple Math Question。不妨就拿它作为例子吧。为了避免混淆下面简述题意时我对原题中的部分符号进行了修改。
定义函数 t
:
t(x)=ax3+bx2+cx+d
定义函数 f
:
f(i=1∏kpiai)=i=1∑kait(pi)
其中 i=1∏kpi
代表数 n
的唯一分解式,且定义 f(1)=0
。
给定 n,a,b,c,d
,试求解:i=1∑nf(i)
。
原题数据范围大概是 108
级别的,标程大概是勒让德定理。这里我们不妨把这道题加强一番,假定 n
是 1010
级别,看看 Min25 筛能否解决之。
首先观察一下 f
:
- f(p)=t(p)=ap3+bp2+cp+d
,嗯有多项式表示;
- f(pe)=e⋅t(p)
,还是能够快速计算的;
- 虽然它并不是一个积性函数。但还是满足:∀ a⊥b,f(ab)=f(a)+f(b)
我们先来考虑 s
的转移。首先回顾一下对于积性函数我们的转移是:
s(n,j)=i=Pj+1∑n[i∈P]f(i)+i=Pj+1∑n[i∈P]f(i)=g(n,∣P∣)−k=1∑j−1f(Pk)+k=j∑∣P∣Pke+1≤n∑[f(Pke)⋅s(⌊Pken⌋,k+1)+f(Pke+1)]
对于第一部分,也就是 i=Pj+1∑n[i∈P]f(i)
,不必对其进行修改。由于我们在筛 s
的时候只会用到 g(n,∣P∣)
,其中只包含素数处的值,因此我们大可将其当作积性函数来筛出 g
,筛 g
的过程保持和上文一样就行了。
对于第二部分,发现本质上要解决的是由 pe⊥k∑f(k)
快速得出 pe⊥k∑f(pe⋅k)
的问题。对于积性函数,有:
pe⊥k∑f(pe⋅k)=f(pe)pe⊥k∑f(k)
而对于本题中的函数,如果我们记这个和式的项数为 m
,则有:
pe⊥k∑f(pe⋅k)=pe⊥k∑[f(pe)+f(k)]=m⋅f(pe)+pe⊥k∑f(k)
也就是说,第二部分可以改成如下形式:
k=j∑∣P∣Pke+1≤n∑[m⋅f(Pke)+s(⌊Pken⌋,k+1)+f(Pke+1)]
当然,这会让这一部分的复杂度多一个快速幂带来的 O(logn)
。
接下来就是怎么确定 m
了,不难发现这就是求对于函数 y(n)=1
的 s
值。因此我们只需要在处理函数 f
时,同时处理函数 y
,并且在对函数 f
的 s
进行转移时借助函数 y
转移过来的 s
即可。实现的时候开一个结构体一起搜就好了。
具体的实现可以参考一下 我的代码。
带条件的前缀和?
这也是我自己 YY 的一种应用,不排除有更优秀的做法。对此我也 YY 了一道题:春燕的板子题(也没有严格验过题,而且由于洛谷私人题库有总时限限制所以可能有点卡常,这只是一个 Proof of concept)。
本质上我就是把洛谷上的模板题拉过来对所求前缀和加了一个限制 i
余数的条件。更正式地表示即给定 r,d(r<d≤6)
,求:
i=0∑n[i≡r(modd)]⋅f(i)
只需要 DP 时再多一维表示模数为 r
即可。而对于 s
,可以对 r
种状态开一个结构体同时转移,这样子复杂度多了一个 r
并解决了问题。当时只是自己写着玩搞了搞,代码就不放了,大家也可以试着自己 YY 一下。
前缀积?
Min25 筛的思想是否可以应用在非前缀和的地方,比如…… 前缀积?对此我自己 YY 了一道题:春燕的数列(没有严格验过题,只是一个 Proof of concept)。
由乘法原理,题目本质即求解:
i=1∏nd(i)(mod109+7)
首先回顾一下 d
的性质:
- d(p)=2
;
- d(pe)=e+1
;
- d
是积性函数。
考虑魔改 s
的转移。我们首先修改一下 s
的定义:
s(n,j)=i=2∏n[minp(i)≥Pj]d(i)
依然把它拆成素数和非素数部分考虑:
s(n,j)=i=Pj+1∏n[i∈P]d(i)⋅i=Pj+1∏n[i∈P]d(i)
对于第一部分,由于 d(p)=2
,因此第一部分就是 2m
,其中 m
为对应区间中质数个数。因此我们依然没有必要魔改 g
,只需要通过 g
筛出质数个数即可。
对于第二部分,需要解决的变成了由 pe⊥k∏f(k)
快速得出 pe⊥k∏f(pe⋅k)
的问题。有:
pe⊥k∏f(pe⋅k)=pe⊥k∏[f(pe)⋅f(k)]=f(pe)mpe⊥k∏f(k)
对于 m
则与前例相同,同时筛一下 y(n)=1
的 s
值即可。
小结
Min25 筛是一个非常精妙并且灵活多变的 DP。自己在学习的时候就感觉它很精妙,因此就思考了它能否应用在更多的场景,也就 YY 出了后几种粗浅的应用。大概就当是抛砖引玉一下吧,感觉可能还会有更加有趣的变形,欢迎大家一起讨论鸭~