积性函数&莫比乌斯反演&筛法
符号约定
P 表示质数集合
在不加说明的情况下,p 为某个质数,pi 表示从小到大第 i 个质数,且 p0=1
minp(x) 表示 x 的最小质因子
理论基础
数论函数
定义在正整数域上的函数,值域在正整数域上的函数称为数论函数
狄利克雷卷积
对于两个数论函数 f,g 定义其狄利克雷卷积为 (f∗g)(n)=∑d|nf(d)g(nd)
狄利克雷卷积有如下性质
-
交换律: f∗g=g∗f
-
结合律: f∗(g∗h)=(f∗g)∗h
-
分配律: f∗(g+h)=f∗g+f∗h
-
有单位元 ϵ
-
若 f,g 均为积性函数,则 f∗g 也为积性函数
证明下性质5
(f∗g)(ij)=∑d|ijf(d)g(ijd),i⊥j(f∗g)(ij)=∑d1|i∑d2|jf(d1d2)g(ijd1d2)=∑d1|i∑d2|jf(d1)f(d2)g(id1)g(jd2)=(∑d1|if(d1)g(id1))(∑d2|jf(d2)g(jd2))=(f∗g)(i)(f∗g)(j)
在狄利克雷卷积意义下存在逆的定义:
定义 g 是 f 的逆,当且仅当 f∗g=ϵ
积性函数
若一个数论函数 f 满足 f(1)=1 and ∀gcd(i,j)=1→f(ij)=f(i)f(j) ,我们则称它是积性函数
特别的,对于满足 ∀i,j,f(i∗j)=f(i)∗f(j) 的数论函数 f ,我们称它是完全积性函数
常见的积性函数
元函数: ϵ(n)=[n=1]
恒等函数: I(n)=1
单位函数: id(n)=n
幂函数: idk(n)=nk
约数个数函数:d(n) 或 σ0(n)
约数和函数:σ(n)
除数函数: σk(n)
欧拉函数: φ(n)
莫比乌斯函数: μ(n)=⎧⎨⎩1,n=1(−1)k,n为k个不同质数之积0,n有平方因子
积性函数的筛法
我们可以利用欧拉筛在线性时间内筛出任意积性函数 f
对于欧拉筛我们只需讨论两种情况
- i⊥p,p is prime,f(ip)=f(i)(p)
- i=pkj,j⊥p,p is prime,f(ip)=f(j)f(pk+1)
对于不同的数论函数具体讨论即能求出
小结论
-
μ∗I=ϵ
记n有k个不同素因子
(μ∗I)(n)=∑d|nμ(d)=∑ki=0(−1)k(ki)=(1−1)k=[k=0]=[n=1]
-
φ∗I=id
即 ∑d|nφ(d)=n
这个的证明考虑分母为n的n个真分数,约分后分子分母互质且每一个分母 d 出现次数为 φ(d)
考虑每一个分母为 d 的,分子与 d 互质的 φ(d) 个分数大小不一且唯一
同时乘上 nd 后,分子一一对应[1,n]
-
μ∗id=φ
考虑结论2与结论1
μ∗id=μ∗φ∗I=(μ∗I)∗φ=ϵ∗φ=φ
莫比乌斯反演
根据我们已经学会的结论,我们能轻松得到莫比乌斯反演的公式了
f∗I=g⟺g∗μ=f
由结论1可证
g(n)=∑d|nf(d)⟺f(n)=∑d|nμ(d)g(nd)
通过结论1,我们也有
∑d|nμ(d)=[n=1]
经典式子推导
默认 n≤m
复杂度记号 O(T1)−O(T2) 表示预处理 O(T1) 单次询问 O(T2)
-
∑ni=1∑mj=1[gcd(i,j)=1]
套路莫反一波
∑ni=1∑mj=1∑d|gcd(i,j)μ(d)
考虑直接枚举 gcd(i,j) 的因数 d
∑nd=1∑ndi=1∑mdj=1μ(d)∑nd=1μ(d)⌊nd⌋⌊md⌋
通过欧拉筛预处理 μ 和整除分块可以做到 O(n)−O(√n)
-
∑ni=1∑mj=1gcd(i,j)
套路枚举 gcd
∑nd=1d∑ndi=1∑mdj=1[gcd(i,j)=1]
右边是我们熟悉的问题1,转化为
∑nd=1d∑ndg=1μ(g)⌊ndg⌋⌊mdg⌋
直接计算这个式子复杂度是 O(∑nd=1√nd)=O(n)
考虑枚举 t=dg 交换一波和式
∑nt=1⌊nt⌋⌊mt⌋∑d|tdμ(td)
不难发现第二重和式就是 (μ∗id)(t)=φ(t)
简化为
∑nt=1⌊nt⌋⌊mt⌋φ(t)
通过欧拉筛预处理 φ 和整除分块同样可以做到 O(n)−O(√n)
-
∑ni=1∑mj=1[gcd(i,j)=1]ij
类似问题1
先套路莫反
∑ni=1∑mj=1∑d|gcd(i,j)μ(d)ij=∑nd=1d2μ(d)∑ndi=1∑mdj=1ij=∑nd=1d2μ(d)(⌊nd⌋+1)⌊nd⌋(⌊md⌋+1)⌊md⌋4
预处理 id2μ ,整除分块可以做到 O(n)−O(√n)
-
∑ni=1∑mj=1lcm(i,j)
由于 lcm 并没有 gcd 那么优美的性质,我们一般将其转化为 gcd 计算
∑ni=1∑mj=1ijgcd(i,j)=∑nd=11d∑ndi=1∑mdj=1[gcd(i,j)=1]idjd=∑nd=1d∑ndi=1∑mdj=1[gcd(i,j)=1]ij
第二三重和式为问题3
简化为
∑nd=1d∑ndg=1g2μ(g)(⌊ndg⌋+1)⌊ndg⌋(⌊mdg⌋+1)⌊mdg⌋4
由于这个部分式子不够优美,不能像问题2那样凑出一个优美的狄利克雷卷积
我们直接整除分块套两次处理这个问题
记 f(n,m)=∑nd=1d2μ(d)(⌊nd⌋+1)⌊nd⌋(⌊md⌋+1)⌊md⌋4
ans=∑nd=1df(⌊nd⌋,⌊md⌋)
时间复杂度 O(??) 不太会算复杂度,如果记忆化后大概比线性略优
-
d(ij)=∑x|i∑y|i[gcd(x,y)=1]
约数个数的经典转化
这里放出Siyuan大佬的证明

-
∑ni=1∑mj=1d(ij)
运用问题5的结论,交换和式得
∑nd=1μ(d)∑ndx=1∑mdy=1⌊nxd⌋⌊myd⌋
可以做到 O(n)−O(√n)
贝尔级数
我们知道对于一个积性函数,在 pk 处的点值决定了所有位置的点值,我们单独考察这些位置的点值。
对于一个积性函数 f,称其贝尔级数 Fp(z)=∑i=0f(pi)zi。原本不太好描述的狄利克雷卷积被转化成了我们熟悉的幂级数卷积。
感觉这东西形式比狄利克雷生成函数更好一点。
下面罗列一些经典函数的贝尔级数:
ϵ(z)=1I(z)=11−zidk(z)=11−pkzμ(z)=I−1(z)=1−zφ(z)=μ(z)id(z)=1−z1−pzd(z)=I(z)I(z)=1(1−z)2(1)(2)(3)(4)(5)(6)
我们回顾数论函数狄利克雷前缀和(即卷 I)的高效 O(nloglogn) 做法,稍加扩展可以得到数论函数 f 和一个贝尔级数为线性递推的积性函数 g 的狄利克雷卷积的新做法。
记 g(z)=A(z)B(z),我们将卷积分成两步,即卷 A(z) 与卷 1B(z),前者容易 O(|A|) 完成,后者是 |B| 阶线性递推,容易 O(|B|) 完成,故我们得到了一个 O(nloglogn(|A|+|B|)) 的做法。
进一步的,事实上对于数论函数 f 与积性函数 g 的狄利克雷卷积如果所有 g(pk) 的点值可以快速求得(共 nlogn 个点值,总复杂度不超过 O(n)),有通用的 O(nloglogn) 做法。
考虑对于每个质数做一次狄利克雷卷积,即每次仅保留一个质数 p 的所有次幂的 g 的点值,容易分析复杂度为 O(∑p∑k≥1npk)=O(nloglogn)。
筛法
开始简单科技了
快速求得 ∑ni=1f(i),f 为某个数论函数
由于各种毒瘤出题人的存在,我们的式子可能已经难以更进一步了,达到了线性的优异复杂度
很多时候,式子的瓶颈均在线性筛上(毕竟这玩意是线性的),我们需要更优的方法筛出某个积性函数
更高深的奇怪筛法大多能在比线性略低的复杂度做到这一点
杜教筛
杜教筛的原理很简单,考虑一个性质优异的积性函数 g 与 f 卷一起的前缀和
∑ni=1(f∗g)(i)=∑ni=1∑d|ig(d)f(id)=∑nd=1g(d)∑⌊nd⌋i=1f(i)
我们敏锐的观察到后面的式子是 f 的前 ⌊nd⌋ 项和
考虑把 d=1 的一项提出来,∑ni=1(f∗g)(i)=g(1)∑ni=1f(i)+∑nd=2g(d)∑⌊nd⌋i=1f(i)
∑ni=1f(i)=∑nd=2g(d)∑⌊nd⌋i=1f(i)−∑ni=1(f∗g)(i)
只要 g 的性质足够优越,使得 f∗g,g 的前缀和均能快速求得且前面一项可通过整除分块降低复杂度
我们便成功的达到了我们的目的——降低复杂度
若 (f∗g)(n) 的前缀和计算复杂度为 T0(n), ∑ni=1f(i) 的计算复杂度为 T1(n)
由于我们共需运算 √n 个 f 的前缀和,算上整除分块的复杂度与 √n 个取值,枚举取值 i
则 T1(n)=∑√ni=1O(√i)+O(√ni)=O(n34)
我们还能再优化一点,考虑线性筛筛出 f 前 M 的取值
复杂度 T1(n)=∑⌊nM⌋i=1O(√ni)=O(n√M)+O(M)
M=n23 时复杂度最优
复杂度分析不懂.jpg
懂了!积分即可!
经典例子
-
∑ni=1φ(i)
直接考虑 φ∗1=id
显然我们容易求 id,1 的前缀和
-
∑ni=1μ(i)
考虑 μ∗1=ϵ
1,ϵ 的前缀和不要太弱智
-
∑ni=1φ(i)ik
考虑 (φ⋅idk)∗idk=idk+1
((φ⋅idk)∗idk)(n)=∑d|nφ(d)dk(nd)k=nk∑d|nφ(d)=nk+1
idk 的前缀和可以插值,或斯特林数,伯努利数等方法求出
Powerful Number 筛
Powerful Number
下文将称其为 PN
定义 PN 为一个质因数分解后,每个质因子指数不小于 2 的数
下面介绍一些 PN 的性质
-
任意一个 PN 可以表示成 a2b3 的形式
考虑一种构造:对于指数为偶的质因子直接扔到 a 里,指数为奇的质因子放三个在 b3 这边,其他往 a 扔
-
[1,n] 的 PN 个数只有 O(√n)
考虑统计 [1,n] 内的 a2b3 形式的数的个数,这一定不比 PN 个数少
枚举 a,考虑 b 的取值:∫√n13√nx2dx=O(√n)
如何得到 n 以内的 PN?
考虑筛出 √n 内的质数后直接搜索每个质因子的次数,显然不重不漏,复杂度 O(√n)
PN 筛
同样是解决积性函数的前缀和问题,PN 筛比杜教筛有更好的通用性
我们需要求得 Sf(n)=∑ni=1f(i)
构造一个易求前缀和的积性函数 g,要求满足 g(p)=f(p),称为素数拟合
构造积性函数 h,满足 h=f/g
由于 f=g∗h,对于一个质数 p,f(p)=g(p)+h(p),由于我们定义了 g(p)=f(p),那么 h(p)=0
由于 h 的积性,h(n) 有值仅当 n 为 PN
Sf(n)=n∑i=1f(i)=n∑i=1∑d|ih(d)g(id)=n∑d=1h(d)⌊n/d⌋∑t=1g(t)=n∑d=1[d∈PN]h(d)Sg(⌊nd⌋)
我们仅要求得所有 Sg(⌊ni⌋),和有值的 h(i) 即可
计算 Sg(⌊ni⌋) 可以考虑使用杜教筛,求得 h(i) 也并不难
考虑计算好 h(pk),k>1,搜索 PN 的过程中合并即可
而因为 f=g∗h,f(pk)=∑ki=0g(pi)h(pk−i)
简单移项可以得到 h(pk)=f(pk)−∑k−1i=0g(pi)h(pk−i)
容易计算 h(pk),计算所有 h(pk) 不超过 O(√n)
容易发现 PN 筛的瓶颈主要在计算 Sg(⌊ni⌋) 上,要么 Sg(n) 非常好求,要么 g 能使用杜教筛
Min_25筛
相比与杜教筛的使用条件,Min_25更为通用
Min_25筛能解决在 f(p) 处取值为关于p的低阶多项式且 f(pc) 能快速求值的满足一定积性条件的函数前缀和问题
求 ∑ni=1f(i),设 m=√n内质数个数
预处理
这一部分也是洲阁筛的第一部分
我们试图求得 Gk(n)=∑nx=2[x∈P]xk
考虑一个辅助 dp:Fk(i,n)=∑nx=2[x∈P∪minp(x)>pi]xk
初值是 F(0,n)=∑nx=2xk,考虑其转移:
F(i,n)=F(i−1,n)−pki(F(i−1,npi)−Gk(pi−1))
由于我们第二维仅需取 ∀i∈[1,n],⌊ni⌋
对于一个固定的 n,第一维仅有前 √nlogn 种取值
那么复杂度 O(∑√ni=1√nilogn)=O(n34logn)
求解
定义 S(i,n)=∑nx=2[minp(x)>pi]f(x)
显然当 pi≥√n 时 S(i,n)=0
则 S(i,n)=G(n)−G(pi)+∑j≥i+1∑e≥1f(pej)(S(j,npej)+[e>1])
其中,G(n) 表示 ∑nx=2[x∈P]f(p)
这一部分的复杂度是不大对的,但在忽略计算函数复杂度的情况下,该算法在 1011 内表现优秀
函数拟合接近 O(n34logn)
发现 Min_25 求的 f 的前缀和所需满足的性质没有积性函数那么严格
仅需满足对于 x=∏mi=1paii 有 f(x)=∏mi=1f(paii)
甚至可以没有交换律:例如 f(x) 为一个矩阵
我们只需要满足 f(p) 的取值能用 p 的低阶多项式表示,f(pc) 能快速求得,就能使用 Min_25 筛
卡常小技巧
[详细揭秘] min_25 筛的卡常技巧
【模板】Min_25筛
int S(int i,ll x){
if(p[i]>=x)return 0;
int k=(x<=m?ind1[x]:ind2[n/x]);
int ans=sub(sub(f2[k]-f1[k])-sub(id2[i]-id[i]));
for(int j=i+1;j<=len&&1ll*p[j]*p[j]<=x;j++){
ll pr=p[j];
for(int e=1;pr<=x;e++,pr=pr*p[j]){
int v=pr%mod;
ans=add(ans+1ll*v*(v-1)%mod*(S(j,x/pr)+(e!=1))%mod);
}
}
return ans;
}
int main(){
n=read();
iv2=power(2,mod-2);iv3=power(3,mod-2);
pre();
for(ll l=1,r=0;l<=n;l=r+1){
ll v=n/l;
r=n/v;
val[++tot]=v;
if(v<=m)ind1[v]=tot;
else ind2[n/v]=tot;
v=v%mod;
f1[tot]=1ll*v*(v+1)%mod*iv2%mod;f1[tot]--;
f2[tot]=1ll*v*(v+1)%mod*(2ll*v%mod+1)%mod*iv2%mod*iv3%mod;f2[tot]--;
}
for(int i=1;i<=len;i++){
for(int j=1;1ll*p[i]*p[i]<=val[j]&&j<=tot;j++){
int k=(val[j]/p[i]<=m?ind1[val[j]/p[i]]:ind2[n/(val[j]/p[i])]);
f1[j]=sub(f1[j]-1ll*sub(f1[k]-id[i-1])*p[i]%mod);
f2[j]=sub(f2[j]-1ll*sub(f2[k]-id2[i-1])*p[i]%mod*p[i]%mod);
}
}
printf("%d\n",add(S(0,n)+1));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现