[复习]莫比乌斯反演,杜教筛,min_25筛

[复习]莫比乌斯反演,杜教筛,min_25筛

莫比乌斯反演

做题的时候的常用形式:

g(n)=n|df(d)f(n)=n|dμ(dn)g(d)

实际上还有

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

证明可以看看这里,只需要把式子带回去就可以证了。
式子很简单,做题很有用,技巧很丰富。

主要通过题目来写这一部分的内容。

  • 【BZOJ1101】ZAP
    题意:求i=1aj=1b[gcd(i,j)==d]
    题解:
    f(d)=i=1aj=1b[gcd(i,j)==d],g(n)=n|df(d)
    那么通过式子的含义不难知道g(d)=i=1aj=1b[d|gcd(i,j)]
    那么推导就很简单了

g(d)=i=1aj=1b[d|gcd(i,j)]=i=1a/dj=1b/d[1|gcd(i,j)]=adbdf(d)=d|iμ(id)g(i)=d|iμ(id)aibi=i=1min(a,b)/dμ(i)a/dib/di

考虑最终这个求和式,如果直接单次O(min(a,b)/d)的计算,那么复杂度是O(Tmin(a,b)/d)的,是会TLE的。
实际上,当i[1,n]时,n/i向下取整的取值只有大约2n个左右,所以对于相同的值完全可以合并在一起算,所以我们只需要处理出μ的前缀和即可直接数论分块计算,把单次复杂度优化到了min(a,b)级别。

Ans=i=1nj=1mlcm(i,j)=i=1nj=1mijgcd(i,j)=d=1n1di=1nj=1m[gcd(i,j)=d]ij=d=1ndi=1n/dj=1m/d[gcd(i,j)=1]ij

f(d,n,m)=i=1nj=1m[gcd(i,j)=d]ij,g(n)=n|df(d)

g(d)=i=1nj=1m[d|gcd(i,j)]ij=d2i=1n/dj=1m/dij=d2S([nd])S([md])

其中S(n)=i=1ni=n(n+1)2
那么题目中的式子要求的是f(1)=i=1nμ(i)g(i)
带回到式子中去,可以得到:

Ans=d=1ndi=1n/dj=1m/d[gcd(i,j)=1]ij=d=1ndi=1n/dμ(i)i2S([nid])S([mid])

到了这一步,可以做到O(n)的复杂度了,提前预处理后面整块的前缀和,然后数论分块计算答案即可。
但是如果要复杂度更加优秀的话,我们需要继续推导。

Ans=d=1ndi=1n/dμ(i)i2S([nid])S([mid])=T=1nS([nT])S([mT])d|T,id=Tdμ(i)i2=T=1nS([nT])S([mT])i|TTiμ(i)i2=T=1nS([nT])S([mT])Ti|Tiμ(i)

不难发现最后那一部分是一个积性函数,那么Ti|Tiμ(i),可以在线性时间里面筛出值并算出前缀和,所以对于前半部分数论分块,可以做到单次询问O(n)
考虑这里比前面单词询问线性的优化在了哪里,之前那个的确可以做到回答答案是根号级别的,但是因为每次的n,m的值都不同,导致不得不需要对于每一次不同的询问做一次前缀和,而后面那个则每次需要预处理的东西都是一样的,因此只需要全局做一次预处理即可。
想想这个操作的本质,其实就是数论分块的对象不同而已,因此当有多个和式出现的时候,要考虑清楚到底对于谁进行数论分块,能够达到最好的效果。

现在可以来总结一下。
我们仔细想想,这类题目的难点在哪里呢?
首先是推式子,但是这个熟练之后很简单。注意对于不同的东西数论分块可以得到不同的复杂度。
其次就是为了数论分块求函数的前缀和了。其实这个算不上太难,如果数据范围到了1e7,显然只能线性筛,那就通过线性筛的本质来考虑,如果当前加入的是一个从未出现过的质因子,那么直接乘积即可,否则的话考虑额外加入一个重复质因子时的贡献。对于赋初值而言,只需要单独考虑质数的贡献就好了。如果数据范围并没有到达1e7,如果在1e6的范围内,不要一味考虑线性筛,这个复杂度下,调和级数的埃氏筛也是可行的。同时要对于一些积性函数的狄利克雷卷积有所了解,这样推式子的时候会方便很多。
这里也提供几个比较常用的式子。
1表示常数,即1(i)=1
e表示单位元,e(i)=[i==1]
id表示这个数本身,id(i)=i
μ,φ就不说了。

  • (1μ)(n)=d|nμ(d)=e
    证明并不难,μ考虑的只有单个质因子,当某个质因子出现超过了1μ的值就是0。那么我们假设n=i=1kpiai。那么我们真正有用的值只有2k个。当k>0的时候,显然μ=1μ=1是一一对应的,因此和为0,当k=0的时候,n=1,此时结果为1。因此这两个的狄利克雷卷积就是单位元。

  • (μid)(n)=d|ndμ(nd)=φ(n)
    其实换种写法就很好证明了,d|nndμ(d)
    μ函数又可以称为容斥系数,那么这鬼玩意的本质就是n减去其大于1的约数的倍数,也就是与n互质的数的个数,也就是φ

  • (1φ)(n)=d|nφ(d)=id(n)=id
    可以用狄利克雷卷积的方法证明:1φ=1μid=eid=id

  • i=1n[gcd(i,n)=1]i
    即小于n并且与n互质的数之和。除了莫比乌斯反演可以推之外,还有一个很好的推导方法。假设xn互质,那么nxn互质,因此所有与n互质的数可以两两配对,且和为n。因此上述式子等于nφ(n)2

杜教筛

求解积性函数前缀和。
假设给定的积性函数为f(x),设它的前缀和S(n)=i=1nf(i)
显然不能直接求解我们才会单独把它拿出来考虑。
既然不能直接求解的话,肯定要找一个什么东西来过度,假装我们找到了一个积性函数g(x)
把两者卷积起来:

(gf)(n)=d|ng(d)f(nd)

然后考虑求解这个玩意的前缀和:

i=1n(gf)(i)=i=1nd|ig(d)f(id)=d=1ng(d)j=1n/df(i)=d=1ng(d)S([nd])

然后我们惊奇的发现有这么一个式子:

g(1)S(n)=i=1ng(i)S([ni])i=2ng(i)S([ni])=i=1n(fg)(i)i=2ng(i)S(ni)

那么假装我们可以快速的计算(fg)的前缀和,那么后半部分可以数论分块+递归求解。
这样一来我们就可以还原出S(n)了。
所以。。。实际上杜教筛就是一个构造的过程。
那么能够快速计算前缀和要有多好求呢?比如说我们要求μ的前缀和,那么最好可以O(1)求,而μ1=ee的前缀和就是1,那就很好办了。令f(x)=μ(x),g(x)=1,可以得到

S(n)=1i=2nS([ni])

这个东西?递归算算就好了。当然了,别少了记忆化。
推式子熟练了就很好了,而构造g的时候就记住前面讲的几个常用的狄利克雷卷积的式子,往那个上面靠。这里随便推一道题目可以看看是怎么构造的。

Ans=i=1nj=1nijgcd(i,j)=d=1nd3i=1n/dj=1n/dij[gcd(i,j)=1]=d=1nd3i=1n/dμ(i)i2S2([nid])=T=1nS2([nT])d|Td3(Td)2μ(Td)=T=1nS2([nT])(T2d|Tdμ(Td)=T=1nS2([nT])(T2φ(T))

考虑怎么求f(T)=T2φ(T)的前缀和,这个东西显然是一个积性函数。
发现式子中带φ,所以杜教筛配g的时候往d|Tφ(T)=T上面靠。

(fg)(n)=d|nf(d)g(nd)=d|nd2φ(d)g(nd)

因为要往只有φ上靠,所以令g(x)=x2

(fg)(n)=d|nd2φ(d)g(nd)=n2d|nφ(d)=n3

那就很好办了,i=1ni3=(1+2+...+n)2可以O(1)计算,那么直接杜教筛就可以快速计算前缀和了。

那么杜教筛就是一个具有很强的构造性的东西,这也从一些方面上使得其并不是那么的实用。

min_25筛

min_25筛用于计算积性函数前缀和S(n)=i=1nf(i)
相比于杜教筛而言,min_25筛的题目更加灵活多变
所求的积性函数要满足两个条件

  • f(x)x为质数的时候要存在一个多项式的表示方法。
  • f(xc)x为质数的时候能够快速计算。

至于为什么是这两个条件,与min_25筛的过程之间有着密切的联系。

既然上述的条件中,我们已经把质数单独分割出来了,那么我们应该明白,min_25筛的过程是一个分解过程,即把所有数分成两类,质数以及合数,当然还有特殊的1
这里提前说明几个东西,底下直接把P定义为质数集合,Pi表示的是第i大的质数。

  • 质数部分
    g(n,j)表示的是,小于等于n的所有数i中,其最小质因子p>Pj或者i本身就是质数的所有的if(i)之和。
    等等,前面不是说了只有当i是质数的时候才能方便的计算f(i)吗?别急,这里我们假装所有数都是质数,带入到是质数的式子中一起计算,换句话说,这里就是一个构造过程,不需要管那么多啦QwQ。或者这样想,如果我们知道了g(n,),显然这个东西就是所有质数的和啊QaQ。接下来往下面看吧。
    考虑一下这个东西怎么样才能转移呢,显然我们的转移从j1转移到了j,那么就考虑g(n,j1)g(n,j)多算了些什么。
    多算的部分显然就是那些最小质因子恰好为Pj的数(别忘了上面那个是大于号),那么最小的一个最小质因子为Pj的数是谁呢?Pj2。如果Pj2n都要大了,那么我们什么也没有减掉,意味着g(n,j)=g(n,j1)
    否则的话,想想我们减掉了什么呢?我们先给所有数除掉一个Pj,那么如果剩下的部分的最小质因子还小于等于Pj的话那么显然不合法,也就是g([nPj],j1),但是这样子减多了,把小于Pj的质数的贡献给减掉了,这些质数与Pj构成的合数的最小质因子小于Pj,显然是早就被减掉了。那么我们重新把第1个到第j1个质数的贡献加回来,也就是g(Pj1,j1)
    然后我们要求的是g的和,我们减去的是Pj因子的贡献,而f(x)为积性函数,所以转移可以写成:

g(n,j)={g(n,j1)Pj2>ng(n,j1)f(Pj)(g([nPj],j1)g(Pj1,j1))Pj2n

前面也说了,f(x)x为质数的情况下可以写成一个多项式的形式,所以其实这个求g的本质上可以理解为求f(x)=xk的一个过程。同时,每次都是从j1转移到j,仔细思考,这个过程很类似于埃氏筛法,每次筛去了一个质数的所有倍数的贡献。
那么如果最终要计算出所有质数的贡献,那么显然就是g(n,),事实上我们只需要算到n 以内的所有质数就行了。同时,因为上述转移的过程中是整除,其实在计算过程中的n的取值也只有2n个左右,也只需要离散后记录这些位置就行了。
g的初值也就是g(n,0)自己想想怎么计算吧QwQ。

  • 合数部分
    知道了质数的贡献,而目标函数又是积性函数,所以我们只需要用所有的质数拼出所有的合数就可以计算答案了。
    S(n,j)表示所有最小质因子大于等于Pj的数if(i)的和,注意这里和上面g的描述的区别。
    那么计算S的值的时候显然是先把质数的贡献给算上,这一部分的贡献是g(n,|P|)i=1j1f(Pj),其中P是小于等于n的质数集合。
    接下来考虑合数的贡献,显然每个合数都存在一个最小质因子,那么我们来枚举这个因子,假设为Pk,kj,枚举其幂,假设为e,那么考虑的就是所有包含了Pke的合数的贡献。
    因为f是积性函数,所以把这里每次就考虑只包含Pke的贡献,首先是S([nPke],k+1),因为这里并没有包含1的贡献,意味着Pke本身的贡献没有计算进来,所以要额外加进来。
    所以转移就可以写成:

S(n,j)=g(n,|P|)i=1j1f(Pi)+kj|P|e(f(Pke)S([nPke],k+1)+f(Pke+1))

所以底下就来丢几道题目吧。。。

  • 【LOJ#6053】简单的函数
    发现这个东西是f(pk)=pk,对于质数而言f(p)=p1,当然2比较特殊f(2)=p+1=3,所以我们先把它当成p1算,最后再加上2就好了。
    前面也说了,因为在质数上涉及到的是一个多项式计算,所以本质上要求解的就是f(p)=pk的形式,那么从这个式子就知道我么要求的是k=0k=1的情况。
    g(n,j)表示k=1的情况。即f(p)=p,那么显然有转移:

g(n,j)={g(n,j1)Pj2>ng(n,j1)Pj(g([nPj],j1)g(Pj1,j1))Pj2n

h(n,j)表示k=0的情况,即f(p)=1时的计算,那么有转移:

h(n,j)={h(n,j1)Pj2>nh(n,j1)(h([nPj],j1)h(Pj1,j1))Pj2n

显然有g(n,|p|)h(n,|p|)为所有质数的答案,假设其为F(n)
那么设S(n,j)表示i=2nf(i),其中i的最小质因子大于等于Pj
得到转移:

S(n,j)=F(n)i=1j1f(Pj)+kje(f(Pke)S([nPke],k+1)+f(Pke+1))

那么简单的递归处理即可。
讲讲几个实现的地方,首先要筛的质数只有n以内的,所以完全可以把他们的f的前缀和给求出来,这也就是gS中转移看起来不好算的两部分的计算方法。其次,计算g(n,j)的时候,真正有用的n只有2n个,因为这个过程你可以看成一个数论分块的过程,因此提前把n给数论分块,得到所有有用的n,这样子可以节约大量空间。第三,发现g的转移的中,之和j1相关,意味着在开数组的时候只需要开一维,第二维可以滚掉,并且实际上最终g要用的也只有g(n,|P|),所以滚掉就好了。最后一个,计算S的时候不需要记忆化。
这题代码我重写了一遍,直接戳LOJ的提交记录吧

  • 【UOJ#188】sanrd
    i=lrf(i)的值,其中f(i)表示i的次大质因子。
    看起来这里的这个函数与质数无关,并且也不是一个积性函数了,那么怎么办呢?
    首先实际上我们要求的还是这个函数的前缀和,考虑一下min_25筛最终求解答案的过程,每次我们枚举其当前拥有的最小质因子。那么,通过当前计算的S(n,j)j,我们可以很容易的知道当前数的上一个质因子是Pj1。那么如果以Pj1为质因数为贡献的话,显然就是当前剩下的数中质数的个数,这个可以提前用min_25的前半部分筛出来。否则以更大的质数为贡献,那么枚举当前的最小质因子把它除掉接着递归处理即可,注意Pkc的这个数的次大质因子为Pk,这里的贡献是额外算的。
    代码直接戳UOJ记录
posted @   小蒟蒻yyb  阅读(3482)  评论(20编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2017-12-24 【BZOJ3668】【NOI2014】起床困难综合症(贪心)
点击右上角即可分享
微信分享提示