莫比乌斯反演(入门)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a1s4z5/article/details/51333840

细细算来,看反演已经有一两个星期了
刚开始的时候也是走了不少的弯路
和其他的算法一样,只要你懂了,就会有一种不过如此的感觉(误
感觉反演还是刚入门,不过还是先写一篇不完全的总结吧,不然过段时间就要忘记了

虽说看反演看了好久才懂,但是现在回头看看,其实很多时间还是花费在弯路上,真正的输出时间不过是最近的一两天

建议的前置技能:

容斥的简单应用

唯一分解定理

欧拉函数的定义

积性函数的定义

然后就可以看反演啦

反演推荐的资料还是贾志鹏线性筛
不要在电脑上直接过一遍就算看过了,这个资料上面有一些不加证明就给出的结论,最好能一个一个自己证明出来
弱在电脑上看了好久并没有看懂,然后打印出来一点一点啃下来
其实也不需要太久的时间,如果你中途不用去补前置知识点的话,一两天足以,重要的还是不要跳着看

现在感觉反演重要的是他的两种形式 ,即使看不懂,也最好能记下来
当初眼花看成一种形式的也是绕了好多弯路

形式一

如果有 f(n)=d|ng(d)
那么 g(n)=d|nμ(d)f(nd)


形式二

如果有 f(n)=n|dg(d)
那么 g(n)=n|dμ(dn)f(d)

还是讲一个题吧
计算:i=1Nj=1Mgcd(i,j)kmod(1e9+7))(N,M1e5,k10)

讲之前一些符号の约定
[bool]为逻辑格符号,即当方括号中间的式子成立的时候值为1,不成立的时候为0
比如 i=1n[nmodi=0]就是n的约数的个数
d|nd就是对n的每一个约数求和
显然 d|n1也是n的约数的个数

一个显然的正确做法就是一一的枚举所有的数对,然后一一算出gcd,然后一一的累加起来,时间复杂度是o(n*m)
虽然是正确的,但是会超时,因为NM会达到1e10.

所以两两枚举是肯定不行的,这个时候你想起了gcd(a,b)是小于等于min(a,b)的,换句话说,gcd最大不会超过1e5
那么枚举gcd可不可以呢?看起来是可以的样子,让我们尝试一下
现在的问题转化为了对于每一个d (1dmin(N,M),我们要计算出有多少对i,j满足 gcd(i,j)=d
好像没有什么卵用。因为如果暴力算的话这里的复杂度还是o(n*m),再乘上一个d,用时比刚才纯暴力的方法还慢……..
(如果能够快速的求出gcdd的数对的个数就好了,你不禁这样想道

先不要急,容我们回忆一下莫比乌斯反演的第二种形式
如果有 f(n)=n|dg(d)
那么 g(n)=n|dμ(dn)f(d)

再回忆一下我们要求的问题
(对于每一个 d(1dmin(N,M) ,计算出有多少对i,j满足 gcd(i,j)=d )

不妨设NM (这样的话min(N,M)就可以直接用N来代替惹)
如果我们设要求的东西为g(n)的话,即 g(n)=i=1Nj=1m[gcd(i,j)=d]
那么f(n)是什么呢,根据定义,应该有 f(n)=n|dg(d)
你可能会想到可以设 f(n)=i=1nj=1m[gcd(i,j)modn=0]
那么f(n)应该是很好求的,就是NdMd
每次计算时间复杂度是多少呢?
对于每个 d(1dN),我们可以O(Nd)的计算出f(d)的值
那么总的复杂度呢?
d=1NNdNlog(N)
诶,那么问题不就解决了吗
枚举所有可能d,用神奇的反演大法来计算出gcd恰好为d的数对的个数,然后就可以啦,时间复杂度是O(Nlog(N))也是可以接受的

反演这个东西怎么说呢,其实就是原来要求的东西不好求,于是我们去找一个新的好求的东西,并且惊讶的发现原来不好求的东西用新搞出来的东西也变得好求了起来


update 5.6

最近发现了一个很顺畅的莫比乌斯反演的证明,感觉很有必要记下来

数论函数 : 定义域为正整数的函数(以下的f,g,e,μ,u都为数论函数

定义卷积 : fg=F , F(n)=pq=nf(p)g(q)

卷积存在单位元ι , fι=ιf=f

不难得出 ι(x)=[x=1]

定义普通乘法: f×g=F , F(n)=f(n)g(n)

同样的普通乘法也存在单位元u , u×f=f×u=f

现在我们有两个运算了,要不要混合在一起玩玩呢?

μu=ι

其实μ就是乘法单位元卷积意义下的逆元

然后我们来看看莫比乌斯反演的第一个公式说了什么

如果有 f(n)=d|ng(d)

那么 g(n)=d|nμ(d)f(nd)

第一个等式说的其实是f=gu

第二个等式说的其实是g=fμ

具体如何推下来的请等第三次更新

顺便给出一个求莫比乌斯函数的代码

memset(g,0,sizeof(g));
g[1] = 1;
for(int i=1;i<=n;i++){
    for(int j=i+i;j<=n;j+=i){
        g[j] -= g[i];
    }
}

这个代码的作用其实是求数论函数g和μ的卷积,当g是卷积单位元的时候,最后的结果自然是μ

如果g的初始化为乘法单位元的话,那么卷积出来的结果是卷积单位元

还是一个挺有趣的玩意

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页