莫比乌斯反演专题训练及解答总结

莫比乌斯反演专题训练及解答总结

需要掌握的两个公式:

公式1:

f(x)=d|xmin(n,m)g(d)   =>   g(x)=d|xmin(n,m)u(d)f(xd)

公式2:
f(x)=x|dmin(n,m)g(d)   =>   g(x)=x|dmin(n,m)u(dx)f(d)

重点掌握常用的第二个公式。

至于相关证明和结论,参见吉大附中的PDF文档,写的真的挺好的,给个赞!

http://7xqmhv.com1.z0.glb.clouddn.com/%E8%8E%AB%E6%AF%94%E4%B9%8C%E6%96%AF%E5%8F%8D%E6%BC%94.pdf

想要学会莫比乌斯反演之前呢?先要学会素数线性筛及莫比乌斯函数的求法,关于素数线性筛和莫比乌斯函数的求法,可以自己百度,也可以看看PDF文档中的求莫比乌斯函数相关知识点。

掌握了莫比乌斯反演这两个公式之后,我们能干啥呢?请看以下例题:

入门典例:(可参见博客:https://blog.sengxian.com/algorithms/mobius-inversion-formula ) (百度居然搜索不到这篇博客,写的这么好,质量这么高)

入门例题

典例1: 令1<= i <=n, 1<= j<=m,求GCD(x,y)=K的个数(OJ题目: HDU 1695)。

直接能想到的就是枚举每个i,j,来求结果,算法复杂度为 O(n2) ,如果这样解,等着去超时吧!

接下来我们看看,利用莫比乌斯反演是怎么将这个复杂度给降下来的!

这个时候,我们设g(x)为gcd(i,j)=x的个数,f(x)为x|gcd(i,j)的个数,那么:

f(x)=x|dmin(n,m)g(d)   =>   g(x)=x|dmin(n,m)u(dx)f(d)

f(x)=x|gcd(i,j) 的个数为 nxmx
(如何证明呢? 如果gcd(i,j)为x的倍数,那么i和j一定为x的倍数,对于所有的i,那么 ix(1,nx) ,同理可证明j,对于所有 1in/x 1jm/x ,组成的所有(i’ ,j’ ),各自扩大x倍之后,即为所有的 x|gcd(i,j) 的对数,结论得证)
所以我们得到:
g(x)=x|dmin(n,m)u(dx)f(d)=x|dmin(n,m)u(dx)ndmd

这个时候,如果线性时间内求出了莫比乌斯函数,你就会神奇的发现,这道题所要求的 g(K) 的复杂度降到了 On ,莫比乌斯反演的魅力出来了, 有木有!

然而,你会发现, nd 出现了很多重复的结果(例如 74 , 75 , 76 等结果都一样),如果我们枚举 nd 的结果,就会发现n/d的结果最多只会有 2n 个。

(证明:对于所有的 ni (1<= i <=n )的结果,只会有 2n 个:
对于所有的 1in ,由于i只有 n 个取值,那么结果也只可能有 n 个;
对于所有的 nin 1nin1n 之间,所以结果也只可能有 n 个;
综上,结论得证。

所以,我们如果枚举n/d的结果的话,求得u的前缀和的话,就可以将复杂度从O(n)降到 O(n) ,关于分块的思想(分块的概念就是说存在连续的i,使得n/i的结果一样,这些连续的i为一块),可以看我推荐的那篇博客。

如何枚举分块呢? 设 ni 为一个分块里面的结果的话,那么和它相等的末尾的那个i即为 nni ,如果实在想不明白,证明见推荐的那篇博客。

详细实现步骤,见推荐博客吧!
——————以下为转载内容———————-

例:n = 32, m = 40, k = 2n=32,m=40,k=2,红色为相等的段。
这里写图片描述
对于相等的段,我们求取 μ 的前缀和,即可批量计算这一个段的答案。

ll F(int n, int m, int d) {
if (n > m) swap(n, m);
ll ans = 0;
n /= d, m /= d;
for (int i = 1, last = 1; i <= n; i = last + 1) {
    last = min(n / (n / i), m / (m / i));
    ans += (ll)(sum[last] - sum[i - 1]) * (n / i) * (m / i);
} 
return ans;
}

对于位置 i,找到下一个相等的位置的代码为 min(n/(n/i),m/(m/i)) 。对于 n 来说,我们要找到最大的 j,满足:

njni

可以拆掉左边的底:

njni

继续化简:

jnni
所以 j=nni ,对 m同理,取两个j 最小值即为都不变的段 [i, j][i,j]。

总之,复杂度为 O(n+m)
这个 f(k) 以及分块非常重要,几乎题题都会用到。

——转载结束———-

例2:设a<=i<=b, c<=j<=d,求gcd(i,j)=k的个数(BZOJ 2301)

这是典例1的变形,只需要用容斥原理求出来即可:我们设典例1中的结果为ans(n,m),那么这题的答案就为ans(b,d)-ans(a-1,d)-ans(c-d,b)+ans(a-1,c-1)。这里我们必须要使用典例1中的最后优化为复杂度 O(n) 的算法,不然会超时。

例3:求有多少数对(x,y)(1<=x<=n,1<=y<=n)满足gcd(x,y)为质数(BZOJ 2818)
在典例1,例2的基础上,我们可以得到:

ans=pni=1n/pμ(i)nipnip

令T=i*p,那么有:
ans=TnnTnTp|Tμ(Tp)

我们这时候想到,如果把 p|Tμ(Tp) 想办法先求出来,然后求出它的前缀和,那么就能将它的复杂度降到原来的 O(n) 了。我们令
sum1(T)=p|Tμ(Tp)
,设 T=p1p2p3.....pn ,那么 sum1(T)=u(p2p3...pn)+u(p1p3...pn)+...+u(p1p2p3...pn1) ,如果这个时候来另外一个质数p’来更新T,那么:
if (T%p’==0) :
sum1(Tp)=μ(T) (没耐心写公式了,自己放到sum1(T)的那一串式子中去试,很简单的啦!此处别忘了break)
else :
sum1(Tp)=μ(T)sum1(T)
以上求sum1的方法是不是可以放到素数筛里面去呢?嘿嘿(PS,这种递推的思想很重要,要掌握啊!)!
最后对sum1求个前缀和,便可以利用分块思想在 O(n) 的复杂度里求出结果了。

接下来给两个进阶题

例4:给出n,m,求 ni=1mj=1lcm(i,j) (BZOJ 2154)
首先,对于i,j的最小公倍数,我们有 lcm(i,j)=ijgcd(i,j) ,所以有

ans=i=1nj=1mlcm(i,j)=i=1nj=1mijgcd(i,j)
我们令 d=gcd(i,j) 可以得到
ans=i=1nj=1m(i/d)(j/d)d
由于i和j各自除以gcd(i,j)之后,一定为互质的数,所以我们令
F(x,y)=i=1xj=1yij[gcd(i,j)==1]
那么对于所有的d,我们有
ans=d=1min(n,m)dF((n/d),(m/d))
接下来我们需要来解决 F(x,y) 的问题,对于任意的 1in,1jm 我们设 f(x) 为gcd(i,j)=x时,所有的i*j之和, g(x) 为x|gcd(i,j)时,所有的i*j之和,那么对于 g(x) 来说,我们有 g(x)=xxsum(nx,mx) 其中 sum(n,m)=n(n+1)/2m(m+1)/2 。证明同典例1中的关于i’,j’的证明思想,对于 nx nx 中的任意(i’,j’),所有对数的乘积之和为 sum((n/x),(m/x)) 各自再扩大x倍之后,即为 g(x)=xxsum((n/x),(m/x)) 所以,我们将原式由莫比乌斯反演化为了
f(x)=x|dmin(n,m)μ(dx)g(d)=x|dmin(n,m)μ(dx)ddsum((n/d),(m/d))
而我们求的为f(1),
F((n/d),(m/d))=f(1)=i=1min((n/d),(m/d))μ(i)iisum(n/(id),m/(id))
所以
ans=d=1min(n,m)di=1min((n/d),(m/d))μ(i)iisum(n/(id),m/(id))
我们令T=id,枚举T,得到
ans=T=1min(n,m)sum(n/T,m/T)i|T(T/i)μ(i)ii=T=1min(n,m)sum(n/T,m/T)i|TTμ(i)i=T=1min(n,m)Tsum(n/T,m/T)i|Tiμ(i)
对于后缀和
MyF(T)=i|Tiμ(i)
放到素数筛里面进行递推,有(我就不写递推过程了,自己例举一下每一项用单个p1,p1,p3,pi的形式表示,递推一下,就出来啦!):
if(T%p==0) :
MyF(T*p)=MyF(T)
else:
MyF(T*p)=(1-p)MyF(T)
这个时候我们就将这个后缀和能以O(1)时间复杂度算出来了,可以再考虑分块优化,就能将计算复杂度降到 O(n) 了。
最后,等等,为啥我预处理了所有数据范围后超时啊?嘿嘿,因为BZOJ算的时限为所有测试的总时限,将预处理的规模变为min(n,m)+233的规模就行了。最后5s+过了。
例5:给出n,求 f(n)=ni=1ij=1ij[(i,j)=1] (HDU 6134)
先把f(n)画张表出来,有:
这里写图片描述
设:g(x)为gcd(i,j)=x的所有 ij 之和
f(x)为x|gcd(i,j)的所有 ij 之和
则有:
f(x)=x|dmin(n,m)g(d)   =>   g(x)=x|dmin(n,m)u(dx)f(d)
得到:
ans=g(1)=dmin(n,m)μ(d)f(d)

这个时候,对于f(x)=x|gcd(i,j)我们可以将i,j缩小x倍,n对应缩小到了n/x,所以f(d)为表中前n/d列的所有数取上整之和,观察数表,我们发现从第i列到第i+1列,只是加上了第i列中i的因数个数+1,我们将这个规律放到素数筛的递推关系中去求F(T)(F(T)代表T的因数的个数,有点抽象,自己好好想想):
if(T%p==0):
F(T*p)=2*F(T)-F(T/p) (别忘了break)
else:
F(T*p)=2*F(T)
最后用个for循环到1e6递推出所有的 f1(x) ( f1(x) 代表数表中第x列之和)即可,最后,得到
ans=dmin(n,m)μ(d)f(d)=dmin(n,m)μ(d)f1(n/d)
利用分块,最后可以将求ans的复杂度降到 O(n) ,哈哈!

还有这个题

菜菜的我能力还不够,还没做的题:BZOJ 3529 数表

—–end—–

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值