积性函数&莫比乌斯反演&筛法学习笔记

积性函数&莫比乌斯反演&筛法

符号约定

P 表示质数集合

在不加说明的情况下,p 为某个质数,pi 表示从小到大第 i 个质数,且 p0=1

minp(x) 表示 x 的最小质因子

理论基础

数论函数

定义在正整数域上的函数,值域在正整数域上的函数称为数论函数

狄利克雷卷积

对于两个数论函数 f,g 定义其狄利克雷卷积为 (fg)(n)=d|nf(d)g(nd)

狄利克雷卷积有如下性质

  1. 交换律: fg=gf

  2. 结合律: f(gh)=(fg)h

  3. 分配律: f(g+h)=fg+fh

  4. 有单位元 ϵ

  5. f,g 均为积性函数,则 fg 也为积性函数

证明下性质5

(fg)(ij)=d|ijf(d)g(ijd),ij(fg)(ij)=d1|id2|jf(d1d2)g(ijd1d2)=d1|id2|jf(d1)f(d2)g(id1)g(jd2)=(d1|if(d1)g(id1))(d2|jf(d2)g(jd2))=(fg)(i)(fg)(j)

在狄利克雷卷积意义下存在逆的定义:

定义 gf 的逆,当且仅当 fg=ϵ

积性函数

若一个数论函数 f 满足 f(1)=1 and gcd(i,j)=1f(ij)=f(i)f(j) ,我们则称它是积性函数

特别的,对于满足 i,j,f(ij)=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,nk0,n

积性函数的筛法

我们可以利用欧拉筛在线性时间内筛出任意积性函数 f

对于欧拉筛我们只需讨论两种情况

  1. ip,p is prime,f(ip)=f(i)(p)
  2. i=pkj,jp,p is prime,f(ip)=f(j)f(pk+1)

对于不同的数论函数具体讨论即能求出

小结论

  1. μI=ϵ

    记n有k个不同素因子

    (μI)(n)=d|nμ(d)=i=0k(1)k(ki)=(11)k=[k=0]=[n=1]

  2. φI=id

    d|nφ(d)=n

    这个的证明考虑分母为n的n个真分数,约分后分子分母互质且每一个分母 d 出现次数为 φ(d)

    考虑每一个分母为 d 的,分子与 d 互质的 φ(d) 个分数大小不一且唯一

    同时乘上 nd 后,分子一一对应[1,n]

  3. μid=φ

    考虑结论2与结论1

    μid=μφI=(μI)φ=ϵφ=φ

莫比乌斯反演

根据我们已经学会的结论,我们能轻松得到莫比乌斯反演的公式了

fI=ggμ=f

由结论1可证

g(n)=d|nf(d)f(n)=d|nμ(d)g(nd)

通过结论1,我们也有

d|nμ(d)=[n=1]

经典式子推导

默认 nm

复杂度记号 O(T1)O(T2) 表示预处理 O(T1) 单次询问 O(T2)

  1. i=1nj=1m[gcd(i,j)=1]

    套路莫反一波

    i=1nj=1md|gcd(i,j)μ(d)

    考虑直接枚举 gcd(i,j) 的因数 d

    d=1ni=1ndj=1mdμ(d)d=1nμ(d)ndmd

    通过欧拉筛预处理 μ 和整除分块可以做到 O(n)O(n

  2. i=1nj=1mgcd(i,j)

    套路枚举 gcd

    d=1ndi=1ndj=1md[gcd(i,j)=1]

    右边是我们熟悉的问题1,转化为

    d=1ndg=1ndμ(g)ndgmdg

    直接计算这个式子复杂度是 O(d=1nnd)=O(n)

    考虑枚举 t=dg 交换一波和式

    t=1nntmtd|tdμ(td)

    不难发现第二重和式就是 (μid)(t)=φ(t)

    简化为

    t=1nntmtφ(t)

    通过欧拉筛预处理 φ 和整除分块同样可以做到 O(n)O(n

  3. i=1nj=1m[gcd(i,j)=1]ij

    类似问题1

    先套路莫反

    i=1nj=1md|gcd(i,j)μ(d)ij=d=1nd2μ(d)i=1ndj=1mdij=d=1nd2μ(d)(nd+1)nd(md+1)md4

    预处理 id2μ ,整除分块可以做到 O(n)O(n

  4. i=1nj=1mlcm(i,j)

    由于 lcm 并没有 gcd 那么优美的性质,我们一般将其转化为 gcd 计算

    i=1nj=1mijgcd(i,j)=d=1n1di=1ndj=1md[gcd(i,j)=1]idjd=d=1ndi=1ndj=1md[gcd(i,j)=1]ij

    第二三重和式为问题3

    简化为

    d=1ndg=1ndg2μ(g)(ndg+1)ndg(mdg+1)mdg4

    由于这个部分式子不够优美,不能像问题2那样凑出一个优美的狄利克雷卷积

    我们直接整除分块套两次处理这个问题

    f(n,m)=d=1nd2μ(d)(nd+1)nd(md+1)md4

    ans=d=1ndf(nd,md)

    时间复杂度 O(??) 不太会算复杂度,如果记忆化后大概比线性略优

  5. d(ij)=x|iy|i[gcd(x,y)=1]

    约数个数的经典转化

    这里放出Siyuan大佬的证明

  6. i=1nj=1md(ij)

    运用问题5的结论,交换和式得

    d=1nμ(d)x=1ndy=1mdnxdmyd

    可以做到 O(n)O(n)

贝尔级数

我们知道对于一个积性函数,在 pk 处的点值决定了所有位置的点值,我们单独考察这些位置的点值。

对于一个积性函数 f,称其贝尔级数 Fp(z)=i=0f(pi)zi。原本不太好描述的狄利克雷卷积被转化成了我们熟悉的幂级数卷积。

感觉这东西形式比狄利克雷生成函数更好一点。

下面罗列一些经典函数的贝尔级数:

(1)ϵ(z)=1(2)I(z)=11z(3)idk(z)=11pkz(4)μ(z)=I1(z)=1z(5)φ(z)=μ(z)id(z)=1z1pz(6)d(z)=I(z)I(z)=1(1z)2

我们回顾数论函数狄利克雷前缀和(即卷 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(pk1npk)=O(nloglogn)

筛法

开始简单科技了

快速求得 i=1nf(i)f 为某个数论函数

由于各种毒瘤出题人的存在,我们的式子可能已经难以更进一步了,达到了线性的优异复杂度

很多时候,式子的瓶颈均在线性筛上(毕竟这玩意是线性的),我们需要更优的方法筛出某个积性函数

更高深的奇怪筛法大多能在比线性略低的复杂度做到这一点

杜教筛

杜教筛的原理很简单,考虑一个性质优异的积性函数 gf 卷一起的前缀和

i=1n(fg)(i)=i=1nd|ig(d)f(id)=d=1ng(d)i=1ndf(i)

我们敏锐的观察到后面的式子是 f 的前 nd 项和

考虑把 d=1 的一项提出来,i=1n(fg)(i)=g(1)i=1nf(i)+d=2ng(d)i=1ndf(i)

i=1nf(i)=d=2ng(d)i=1ndf(i)i=1n(fg)(i)

只要 g 的性质足够优越,使得 fg,g 的前缀和均能快速求得且前面一项可通过整除分块降低复杂度

我们便成功的达到了我们的目的——降低复杂度

(fg)(n) 的前缀和计算复杂度为 T0(n)i=1nf(i) 的计算复杂度为 T1(n)

由于我们共需运算 nf 的前缀和,算上整除分块的复杂度与 n 个取值,枚举取值 i

T1(n)=i=1nO(i)+O(ni)=O(n34)

我们还能再优化一点,考虑线性筛筛出 fM 的取值

复杂度 T1(n)=i=1nMO(ni)=O(nM)+O(M)

M=n23 时复杂度最优

复杂度分析不懂.jpg

懂了!积分即可!

经典例子

  1. i=1nφ(i)

    直接考虑 φ1=id

    显然我们容易求 id,1 的前缀和

  2. i=1nμ(i)

    考虑 μ1=ϵ

    1,ϵ 的前缀和不要太弱智

  3. i=1nφ(i)ik

    考虑 (φidk)idk=idk+1

    ((φidk)idk)(n)=d|nφ(d)dk(nd)k=nkd|nφ(d)=nk+1

    idk 的前缀和可以插值,或斯特林数,伯努利数等方法求出

Powerful Number 筛

Powerful Number

下文将称其为 PN

定义 PN 为一个质因数分解后,每个质因子指数不小于 2 的数

下面介绍一些 PN 的性质

  1. 任意一个 PN 可以表示成 a2b3 的形式

    考虑一种构造:对于指数为偶的质因子直接扔到 a 里,指数为奇的质因子放三个在 b3 这边,其他往 a

  2. [1,n] 的 PN 个数只有 O(n)

    考虑统计 [1,n] 内的 a2b3 形式的数的个数,这一定不比 PN 个数少

    枚举 a,考虑 b 的取值:1nnx23dx=O(n)

如何得到 n 以内的 PN?

考虑筛出 n 内的质数后直接搜索每个质因子的次数,显然不重不漏,复杂度 O(n)

PN 筛

同样是解决积性函数的前缀和问题,PN 筛比杜教筛有更好的通用性

我们需要求得 Sf(n)=i=1nf(i)

构造一个易求前缀和的积性函数 g,要求满足 g(p)=f(p),称为素数拟合

构造积性函数 h,满足 h=f/g

由于 f=gh,对于一个质数 pf(p)=g(p)+h(p),由于我们定义了 g(p)=f(p),那么 h(p)=0

由于 h 的积性,h(n) 有值仅当 n 为 PN

Sf(n)=i=1nf(i)=i=1nd|ih(d)g(id)=d=1nh(d)t=1n/dg(t)=d=1n[dPN]h(d)Sg(nd)

我们仅要求得所有 Sg(ni),和有值的 h(i) 即可

计算 Sg(ni) 可以考虑使用杜教筛,求得 h(i) 也并不难

考虑计算好 h(pk),k>1,搜索 PN 的过程中合并即可

而因为 f=ghf(pk)=i=0kg(pi)h(pki)

简单移项可以得到 h(pk)=f(pk)i=0k1g(pi)h(pki)

容易计算 h(pk),计算所有 h(pk) 不超过 O(n)

容易发现 PN 筛的瓶颈主要在计算 Sg(ni) 上,要么 Sg(n) 非常好求,要么 g 能使用杜教筛

Min_25筛

相比与杜教筛的使用条件,Min_25更为通用

Min_25筛能解决在 f(p) 处取值为关于p的低阶多项式且 f(pc) 能快速求值的满足一定积性条件的函数前缀和问题

i=1nf(i),设 m=n

预处理

这一部分也是洲阁筛的第一部分

我们试图求得 Gk(n)=x=2n[xP]xk

考虑一个辅助 dp:Fk(i,n)=x=2n[xPminp(x)>pi]xk

初值是 F(0,n)=x=2nxk,考虑其转移:

F(i,n)=F(i1,n)pik(F(i1,npi)Gk(pi1))

由于我们第二维仅需取 i[1,n],ni

对于一个固定的 n,第一维仅有前 nlogn 种取值

那么复杂度 O(i=1nnilogn)=O(n34logn)

求解

定义 S(i,n)=x=2n[minp(x)>pi]f(x)

显然当 pinS(i,n)=0

S(i,n)=G(n)G(pi)+ji+1e1f(pje)(S(j,npje)+[e>1])

其中,G(n) 表示 x=2n[xP]f(p)

这一部分的复杂度是不大对的,但在忽略计算函数复杂度的情况下,该算法在 1011 内表现优秀

函数拟合接近 O(n34logn)


发现 Min_25 求的 f 的前缀和所需满足的性质没有积性函数那么严格

仅需满足对于 x=i=1mpiaif(x)=i=1mf(piai)

甚至可以没有交换律:例如 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;
}
posted @   juju527  阅读(181)  评论(1编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示