[关闭]
@Simon-Sun 2022-06-09T13:56:25.000000Z 字数 6217 阅读 381

莫比乌斯反演

未分类


如果你做题见到好多个,例如:


这种东西,那么你就可能会需要用到莫比乌斯反演。
你需要先学两个前置知识:[数论分块]、[狄利克雷卷积]

数论分块

数论是研究整数的。
既然是整数而不是实数那么他就有一些奇怪的性质。
其中一个性质便是除法向下取整。
比如说::



再比如:


不难发现一个数去和一些数做除法,有很多结果相同的,
尤其是后期,重复的越来越多:





太多一样的了!!!
而且对于一个被除数来说,答案相同的除数都是连续的,来个直观一点的:
img
可见被分成了一块一块的,那么我们把这些块同时处理,这就叫做数论分块。
怎么同时处理呢?
首先你要知道什么叫下取整。



那么开始引入性质:

引理1:


证:


其中,我们可以无视他。


这时候我知道你会问,为什么这里面 可以被无视。
得到的是实数,而 也是个实数。
前者的小数部分和后者加起来万一一不小心大于 了,那么省不省略 结果不就不一样了吗?
但是你想!!! 是个整数, 也是整数,两个整数相除得到的小数部分最大应该是
,所以


所以 是可以忽略的。

引理2 (关键!!):

已知
要满足


的最大的 所在块的右端点为

证:


其中:



##
那么我们现在就可以用数论分块去加速一些运算了,来个例题

多组数据,保证数据中 的总和小于
显然得用 的做法了,那么我们现在可以直接数论分块,毕竟是板子。
如果你上边看懂了,那就直接看代码:

  1. ll t,n,ans;
  2. int main(){
  3. t=read();
  4. while(t--) {
  5. n=read();
  6. ans=0;
  7. ll l=1, r;\\1开始
  8. while(l<=n) {
  9. r=n/(n/l);//这是l的块的右端点
  10. ans+=(r-l+1)*(n/l);//直接区间求和
  11. l=r+1;//下一个块
  12. }
  13. printf("%lld\n", ans);
  14. }
  15. }

很简单吧, 很好理解吧。

好那么现在证明一下复杂度:

我们是一个个块枚举的,所以复杂度就是有几个块算几次。
相同的值算一个块,那么就看 有几种取值就行。
①当
就算 互不相同,
撑死 种取值。
②当

所以也是撑死只有 个取值
总共撑死只有 个块。
所以复杂度

还有一个,叫做多维数论分块。

比如


很简单,对 分别算右端点取 就行

  1. r=n/(n/l);
  2. ->
  3. r=min(n/(n/l), m/(m/l));

二维以上同理。
就这么点东西,就这么简单~

狄利克雷卷积

一个很神奇的东西。
两个数论函数 ,我们定义,不要问为啥,就是定义,其狄利克雷卷积为:


然后,你就很神奇的能把两个函数建立起这么一种神奇的关系来。
为什么说神奇呢?

1.交换律


显然我枚举了 的因子
那我枚举 的所有因子。
显然 也是 的所有因子。
也一直是一一对应的
所以就等价了。



简写:

2.结合律


直接上:


显然的:

所以:

看到这一点了就好办了,这个式子的对称性很强。

一个是先算 ,一个是先算 ,不过在这个形式的式子中,谁前谁后重要吗?都一样!!!
得证!!

3.分配律



则有



前置知识储备完毕,现在我们正式开始讲——莫比乌斯反演

莫比乌斯反演

接下来正式开始学习。
首先你要知道几个定义:

反演

我们设:


那我们能不能用 表示 呢?

我们可以直接利用狄利克雷卷积来表示:









此乃反演!!!
会了一个知识点
不做习题怎么行?
回到开头:

这直接
……
好,开始化简!:





现在我们转化一下求和顺序,现枚举

其中, 就是求 的倍数啊。
继续:


其中 可以数论分块
可以用前缀和快速求。
问题来了每一个 怎么求?
线性求!!
因为它是积性的!!
直接在欧拉筛里求!
上代码:

  1. void Euil() {
  2. mu[1]=1;
  3. for(int i=2; i<=1000000; ++i) {
  4. if(!pd[i]) {
  5. zhi[++top]=i;
  6. mu[i]=-1;//一个质数(-1)^1
  7. }
  8. for(int j=1; j<=top&&zhi[j]*i<=1000000; ++j) {
  9. pd[zhi[j]*i]=1;
  10. if(i%zhi[j]==0) break;
  11. else mu[zhi[j]*i]=-mu[i];//一个新的质数。
  12. }
  13. }
  14. for(int i=1; i<=1000000; ++i) {
  15. mu[i]+=mu[i-1];
  16. }
  17. }
  18. int main(){
  19. n=read();
  20. for(int k=1; k<=n; ++i) {
  21. int l=1, r;
  22. int nn=n/k;
  23. while(l<=nn) {
  24. r=nn/(nn/l);
  25. int p=nn/l;
  26. ans+=k*p*p*(mu[r]-mu[l-1]);
  27. l=r+1;
  28. }
  29. } printf("%d", ans);
  30. }

特别鸣谢, @Konnyaku41377 提供的详细理论及知识点讲解

感谢大家的阅读,多多点赞哟~

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注