在学习了狄利克雷卷积后,莫比乌斯反演公式就是非常显然的了:
展开来写:
其实如果你掌握了狄利克雷卷积,你并不需要记忆这个公式,而且很多题目也不会直接用到它。
至于莫比乌斯函数的计算,可以利用欧拉筛在
int mu[MAXN];
bool isnp[MAXN];
vector<int> primes;
void init(int n)
{
mu[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!isnp[i])
primes.push_back(i), mu[i] = -1; // 质数为-1
for (int p : primes)
{
if (p * i > n)
break;
isnp[p * i] = 1;
if (i % p == 0)
{
mu[p * i] = 0; // 有平方因数为0
break;
}
else
mu[p * i] = mu[p] * mu[i]; // 互质,利用积性函数性质
}
}
}
在竞赛中,莫比乌斯反演常常用在与
提取公因数
设有
(注意:需要把原函数转化成关于
如果你看着公式推导觉得比较绕,我画了一张图帮助理解:

z轴代表
这个公式还可以推广,比如只有两个求和号的情形:
四个求和号的情形:
(
等。
整除分块
(其实我和我的队友 @朝夕 曾经独立发明了这个算法……)
假如我们要求

所以我们可以把相同的项一起计算。现在的问题是如何找到这些段。我们可以证明,设每一段的左端点为
设
这样就可以写出整除分块的代码:
for (int l = 1, r; l <= n; l = r + 1)
{
r = n / (n / l);
ans += (r - l + 1) * (n / l);
}
很容易证明总段数不超过
这也可以用来计算因数个数函数
来看一下例题:
求且
的二元组数量。
定义
显然,
后面这一块可以用整除分块计算。先预处理出
sum
。同时对两个整除式进行分块,每次取步长较短的一段:for (int l = 1, r; l <= min(n, m); l = r + 1)
{
r = min(n / (n / l), m / (m / l));
ans += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
}
稍微把题目变形一下:
求且
的二元组数量。
现在要求的是
再比如说:
求的值.
好吧,这个题其实跟莫比乌斯函数没什么关系,但也可以用狄利克雷卷积+提取公因数+整除分块化简,本质上差不多。
洛谷P2398的数据较小,可以用复杂度较大的方法解决。但复杂度更优的方法是预处理出
一般地,如果
sum
,则for (int l = 1, r; l <= min(n, m); l = r + 1)
{
r = min(n / (n / l), m / (m / l));
ans += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
}
这种题目,难度主要在于推式子,而且一般不是可以打表看出来的,所以需要把各种数论定理牢记于心。当然,这一块还有很多技巧,这里暂不介绍了。
Pecco:算法学习笔记(目录)zhuanlan.zhihu.com