常见积性函数线性筛

线性筛素数

比埃氏筛更优的线性筛,线性体现在每个数会被它最小的质因子筛掉,最小的质因子显然只有一个,每个数只会被访问一次,严格的线性复杂度。

一个数被访问是由枚举“除去它最小的质因子后的值 i ”,再乘上“最小质因子 prime[j](以下简写为p[j])”实现的。

例如 8 是在 i=4,p[j]=2 时被访问的,12 是在 i=6,p[j]=2 时被访问的,9i=3,p[j]=3 (可以看到需要先把 i 加入质数中再枚举质数)。

若当前的 i 没有被访问,说明 i 最小的质因子是它本身,显然 i 就是个质数。

为了保证当前质因子是乘积的最小质因子,当 i%p[j]==0 时停止对 j 的枚举,因为此时 i 中保证有质因子 p[j],当 p[j] 继续增大时,ip[j] 乘积的最小质因子不会再是 p[j],而是 i 中的最小质因子。

for(int i=2; i<=n; i++) { if(!vis[i]) p[++cnt] = i; for(int j=1; j<=cnt && i*p[j]<=n; j++) { vis[ i*p[j] ] = 1; if(i%p[j]==0) break; //与埃氏筛唯一的区别 } }

积性函数

积性函数是指当 ab 互质时满足 f(ab)=f(a)f(b) 的函数。常见的像欧拉函数 φ ,约数个数 d, 约数和 σ,莫比乌斯函数 μ。所有的积性函数都能线筛(大概)。

我们在线性筛时,每个数是由 ip[j] 乘起来得到的。

i%p[j]0,因为 p[j] 是质数所以 ip[j] 互质,f(ip[j])=f(i)f(p[j])

i%p[j]=0,我们一般需要考虑这个数中最小的质因子 p[j] 的出现次数对它函数值的影响,一般需要另设一个函数 g 来记录最小质因子的出现次数,当 i%p[j]=0 时,g[ip[j]]=g[i]+1,而 f 值就要具体情况具体分析了。

欧拉函数 φ 线筛

欧拉函数 φ,表示比 x 小的与 x 互质的数的个数,求单个的话是 sqrt 复杂度的 (如果忽略预处理的复杂度就是 sqrt/log)。

x=p1k1p2k2p3k3...pnkn,则φ(x)=xi=1npi1pi

这个很好证,就是考虑每个质因子对 φ 值的贡献。

φ 是积性函数也就显然了,因为互质的两个数没有相同的 p,两个数的质因子贡献互不影响。

i%p[j]0φ(ip[j])=φ(i)φ(j)=φ(i)(p[j]1)

i%p[j]=0 ,由于指数 kj 不会对答案造成影响,而 p[j] 已经对 i 产生过贡献,所以直接乘上 p[j] 即可,φ(ip[j])=φ(i)p[j]

for(int i=2; i<=n; i++) { if(!vis[i]) { p[++cnt] = i; phi[i] = i-1; } for(int j=1; j<=cnt && i*p[j]<=n; j++) { vis[ i*p[j] ] = 1; if(i%p[j]==0) { phi[ i*p[j] ] = phi[i] * p[j]; break; } phi[ i*p[j] ] = phi[i] * (p[j]-1); } }

约数个数 d 线筛

一个显然的结论:若x=p1k1p2k2p3k3...pnkn,则d(x)=i=1n(ki+1)

xy 互质,那么 xy 没有相同的质因子,显然d(xy)=d(x)d(y),所以 d 是个积性函数,考虑同样的线筛方式。

i%p[j]0d(ip[j])=d(i)d(p[j])=d(i)2

i%p[j]=0 ,相当于 p[j] 的指数从 kj 变成了 kj+1,辣么它的d就从一大坨乘上 kj+1 变成一大坨乘上 kj+2

所以有必要开一个 g 记录最小质因子出现次数,g[ip[j]]=g[i]+1,d[ip[j]]=d[i]/(g[i]+1)(g[i]+2)。记得当 i 是质数时,d[i]=2,g[i]=1

d[1] = 1; for(int i=2; i<=n; i++) { if(!vis[i]) { p[++cnt] = i; d[i] = 2; g[i] = 1; } for(int j=1; j<=cnt && i*p[j]<=n; j++) { vis[ i*p[j] ] = 1; if(i%p[j]==0) { d[ i*p[j] ] = d[i] / (g[i]+1) * (g[i]+2); g[ i*p[j] ] = g[i] + 1; break; } d[ i*p[j] ] = d[i] * 2; g[ i*p[j] ] = 1; } }

约数和 σ 线筛

x=p1k1p2k2p3k3...pnkn,那么有一个结论是

σ(x)=(1+p1+p12+...+p1k1)(1+p2+p22+...+p2k2)...(1+pn+pn2+...+pnkn)

简写就是 σ(x)=i=1nj=0kipij

因为从每个括号中各选一项相乘得到的数一定是 x 的约数,而且是一一对应的,将所有项加起来就是约数和了。

由于互质的两个数质因子互不相同,联乘可以叠在一起,所以 σ 是积性函数。

i%p[j]0σ(ip[j])=σ(i)σ(p[j])=σ(i)(p[j]+1)

i%p[j]=0, 相当于将 pj 的指数 kj 加一,则 pj 对答案的贡献从 (1+pj+pj2+...+pjkj) 变为 (1+pj+pj2+...+pjkj+pjkj+1),而不会对其他质因数造成影响。

g[i] 表示 i 中不能被 pji 的最小质因子)整除的约数的和,
σ(i)=(1+pj+pj2+...+pjkj)g[i]

σ(ip[j])=(1+pj+pj2+...+pjkj)pjg[i]+g[i]
=(1+pj+pj2+...+pjkj+pjkj+1)g[i]=σ(i)p[j]+g[i]

然后注意 g 数组的赋值:当 p[j]i 的质因子时,g[ip[j]]=g[i],否则 g[ip[j]]=f[i],这个应该好理解。

代码中 σf 表示:

f[1] = 1; for(int i=2; i<=n; i++) { if(!vis[i]) { p[++cnt] = i; f[i] = i+1; g[i] = 1; } for(int j=1; j<=cnt && i*p[j]<=n; j++) { vis[ i*p[j] ] = 1; if(i%p[j]==0) { f[ i*p[j] ] = f[i] * p[j] + g[i]; g[ i*p[j] ] = g[i]; break; } f[ i*p[j] ] = f[i] * (p[j]+1); g[ i*p[j] ] = f[i]; } }

2020.06.06 update:

这玩意今天模拟赛出了个应用。。谔谔。

大概是问你 i=1nn%i,这玩意儿显然可以用整除分块来根号复杂度做,但是这题多组询问的 T 达到了 1e6,那么就找规律。

发现这个数列:i=1nnii1,4,8,15,21...

和刚刚筛出来的 σ1,3,4,7,6...

为啥是这个前缀和呢? @159 给出了理性的证明:nii 可以等价于小于等于 n 的数中有多少个数是 i 的倍数,再乘个
i,把所有 i 的倍数加起来,就是把所有数的因数和加起来。

莫比乌斯函数 μ 线筛

想做反演题你得先把它筛出来。

莫比乌斯函数是 人类自己捏出来的 一个容斥系数函数,来个通俗一点的定义:

若x=1,则μ(x)=1;当 x>1 时分解 x=p1k1p2k2p3k3...pnkn

k1=k2=k3=...=kn=1μ(x)=(1)n

否则μ(x)=0


μ 显然是个积性函数,感性证明:

xy 互质,若 μ(x)=0μ(y)=0,说明有一个质因子的指数大于1,那么xy那个质因子指数也大于1,μ(xy)=μ(x)μ(y)=0

x0y0 时,μ(x)=(1)nx,μ(y)=(1)ny,μ(xy)=(1)nx+ny=μ(x)μ(y)

考虑线筛:

i%p[j]0μ(ip[j])=μ(i)μ(p[j])=μ(i),因为质数的 μ 都是 1

i%p[j]=0,说明 p[j] 这个质因子将在 ip[j] 出现两次,那μ(ip[j]) 就等于 0 呗。

mu[1] = 1; for(int i=2; i<=n; i++) { if(!vis[i]) { p[++cnt] = i; mu[i] = -1; } for(int j=1; j<=cnt && i*p[j]<=n; j++) { vis[ i*p[j] ] = 1; if(i%p[j]==0) break; //mu[i*p[j]]=0;就没必要写了。 mu[ i*p[j] ] = -mu[i]; } }

参考:

https://www.cnblogs.com/cjoieryl/p/8268373.html

https://www.cnblogs.com/zhoushuyu/p/8275530.html

https://www.cnblogs.com/TheRoadToTheGold/p/8228969.html


__EOF__

本文作者枫叶晴
本文链接https://www.cnblogs.com/maple276/p/12741425.html
关于博主:菜菜菜
版权声明:呃呃呃
声援博主:呐呐呐
posted @   maple276  阅读(257)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示