杜教筛
前置知识:积性函数的狄利克雷卷积:
μ∗1=ϵφ∗1=id
求 F(n)=n∑i=1f(i),其中 f 为积性函数。
构造积性函数 g 和 h 使得 h=f∗g 且容易求出 g 和 h 的前缀和。
对于 f=μ,构造 g=1,h=ϵ。
对于 f=φ,构造 g=1,h=id。
推导过程如下:
n∑i=1h(i)=n∑i=1∑d|ig(d)f(id)=n∑d=1⌊nd⌋∑i=1g(d)f(i)=n∑d=1g(d)F(⌊nd⌋)
F(n)=1g(1)(n∑i=1h(i)−n∑i=2g(i)F(⌊ni⌋))
由于 g 为积性函数,所以 g(1)=1。
使用数论分块并递归求解 n∑i=2g(i)F(⌊ni⌋),直接计算复杂度为 O(n34),若使用线性筛预处理出前 n23 项并使用 unordered_map
进行记忆化可以做到 O(n23)。
复杂度证明
考虑 g 和 h 的前缀和可以 O(1) 计算的情况。设 T(n) 为计算 F(n) 的时间复杂度,有
T(n)=Θ(√n)+√n∑i=2(T(i)+T(ni))=Θ(√n)+√n∑i=2(Θ(√i)+Θ(√⌊ni⌋))
将 T(n) 展开一层即可,因为有 ⌊⌊nx⌋y⌋=⌊nxy⌋,所以再展开就会被记忆化。
∵Θ(√i)+Θ(√⌊ni⌋)≥2√√n=2n14∴√n(Θ(√i)+Θ(√⌊ni⌋))≥2n34
假设使用线性筛预处理了前 k 个前缀和,且 k≥√n,则
T(n)=nk∑i=2Θ(√⌊ni⌋)=Θ(n23),□
数论分块套杜教筛
若在杜教筛外面套一次数论分块,时间复杂度仍为 O(n23),因为若事先计算一次 F(n),且使用线性筛优化以及 unordered_map
等进行记忆化,则数论分块过程中用到的 F(⌊nd⌋) 都已经被记忆化。
Powerful Number 筛 (PN 筛)
同样是求积性函数 f 的前缀和 F(n)=n∑i=1f(i)。
需要构造积性函数 g 使得对于任意质数 p 有 f(p)=g(p),且 g 易求前缀和。
Powerful Number
定义:对于 n 的质因数分解 n=∏pkii,n 为 PN 当且仅当 ∀i,ki>1。
性质1:所有 PN 都能表示为 a2b3。
证明1:若 ki 为偶数则直接用 pkii 对 a2 贡献,若 ki 为奇数则先对 b3 进行 p3i 的贡献,然后对 a2 产生 pki−3i 的贡献。
性质2:n 以内的 PN 最多有 O(√n) 个。
证明2:枚举 a 后,考虑满足条件的 b 的个数,所以个数约为
∫√n13√nx2dx=O(√n)
要求出 n 以内的 PN,只需要先线性筛出 √n 以内的质数,再 DFS 搜索出各质数的指数即可,复杂度还是 O(√n)。
PN 筛
构造积性函数 g 使得对于任意质数 p 都满足 f(p)=g(p),记 G(n)=n∑i=1g(i)。
构造函数 h=f/g,由狄利克雷卷积的性质可知 h 为积性函数,h(1)=1 且 f=g∗h。
对于素数 p,有 f(p)=g(1)h(p)+g(p)h(1)=g(p)+h(p),又因为 f(p)=g(p),所以 h(p)=0。又由于 h 为积性函数,故 h(n) 只在 n 为 PN 时不为 0。
根据 f=g∗h,有
F(n)=n∑i=1f(i)=n∑i=1∑d|ih(d)g(id)=n∑d=1h(d)⌊nd⌋∑i=1g(i)=n∑d=1h(d)G(⌊nd⌋)=n∑d=1d is PNh(d)G(⌊nd⌋)
下面考虑如何计算出 h(n)。算出所有 h(pc) 的值即可直接推导。根据 f=g∗h 有 f(pc)=c∑i=0g(pi)h(pc−i),移项得到 h(pc)=f(pc)−c∑i=1g(pi)h(pc−i),先对于每个质数 p 处理出所有 h(pc),之后在 DFS 处理所有 PN 的时候推导出所有的 h(n)。
复杂度证明
线性筛预处理 √n 以内的质数,并对每个质数预处理 h(pc),时间复杂度为 O(√nlognlog2n)=O(√nlogn),空间复杂度为 O(√n)。
对于计算过程中的时间复杂度,假设可以 O(1) 求出 G(⌊nd⌋),则显然为 O(√n)。若使用杜教筛求出 G(⌊nd⌋),则时间复杂度为 O(n23),证明如下(其实与数论分块套杜教筛的复杂度证明相同):
若事先计算一次 G(n),且使用线性筛优化以及 unordered_map
等进行记忆化,则 PN 筛过程中用到的 G(⌊nd⌋) 都已经被记忆化。
综上所述,若可以 O(1) 求出 G(n),则 PN 筛的时间复杂度为 O(√nlogn),否则使用杜教筛求出 G(n) 则时间复杂度为 O(n23)。空间复杂度均为 O(√n)。
Min_25 筛
同样是计算积性函数 f 的前缀和。要求 f(p) 和 f(pk) 容易计算。
方便起见,定义 g(i) 表示将 i 当作质数计算时的 f(i),显然有 f(p)=g(p),并定义 pi 表示第 i 小的质数。特别地,p0=1。
此时的 g 为完全积性函数!即 ∀i,j,g(i×j)=g(i)×g(j) 恒成立。
预处理 sumi=i∑j=1f(pj)=i∑j=1g(pj)。由于一个合数的最小质因子 ≤√n,所以只需要预处理到 ≤√n 的质数,记 cntp 表示这些质数的数量。
计算过程
Step 1
记 Gk(i) 表示 i 以内所有质数以及最小质因子 >pk 的合数的 g 之和。即埃筛第 k 轮所剩下的数的 g 之和。
同样是由于合数的最小质因子 ≤√n,所以 k 只需要筛到 ≤√n 的质数即可。
初始时的 G0(i)=i∑j=2g(j)。考虑使用埃筛的过程进行转移,有:
- 对于 i<p2k 的部分保持不变,即 Gk(i)=Gk−1(i)。
- 对于 p2k≤i 的部分,被删掉的数一定含有质因子 pk,贡献为 −g(pk)Gk−1(⌊ipk⌋)。
- 对于 p2k≤i 的部分,根据 G 的定义,在操作 2 中被删去的数会存在使得 j<k 的 g(pjpk) 被多删去,要将这部分的贡献加回来,即 g(pk)sumk−1。
于是得到以下的递推式:
Gk(i)=Gk−1(i)−[p2k≤i]g(pk)(Gk−1(⌊ipk⌋)−sumk−1)
同时,不难发现 Gk(i) 中 i 的有用取值只有 i=⌊nd⌋,共 O(√n) 种。
Step 2
记 Fk(i)表示 i 以内所有最小质因子 >pk 的数的 f 之和。
分为两部分讨论贡献:
- 质数:所有质数的贡献 Gcntp(i) 减去 p1∼pj 的贡献,为 Gcntp(i)−sumj。
- 合数:枚举最小质因子极其次数,贡献为 ∑j>kpej≤if(pek)(Fj(⌊ipek⌋)+[e≠1]),其中 e≠1 计算的是 pek 的贡献。
容易发现答案就是 F0(n)+f(1)=F0(n)+1。
复杂度证明
对于 Step 1 的复杂度,考虑每个 i=⌊nd⌋,只在枚举 p2k≤i 时进行转移,所以时间复杂度如下:
T(n)=√n∑i=1O(√ilog√i)+O⎛⎜
⎜⎝√nilog√ni⎞⎟
⎟⎠=O⎛⎜
⎜⎝∫√n1√nilog√ni⎞⎟
⎟⎠=O(n34logn)
Step 2 的复杂度为 O(n1−ϵ),证明见集训队论文2018-朱震霆-2.3。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App