莫比乌斯反演题目技巧与策略笔记
莫比乌斯反演题目技巧与策略笔记
0. 学习目标
莫比乌斯反演不是一个孤立公式,而是一类“从累加量还原精确量”的方法。算法题里通常不会直接告诉你“这里要反演”,而是藏在以下结构里:
- 从“至少满足某个因子条件”还原到“恰好满足某个值”;
- 从“约数贡献总和”还原到“单点贡献”;
- 从“倍数贡献总和”还原到“恰好 gcd 为某个值”;
- 用 \(\mu\) 把 \([\gcd(i,j)=1]\) 这样的条件拆成可枚举的除法块。
这份笔记重点放在竞赛题中的识别、转化和计算策略。
1. 基础函数与核心恒等式
1.1 莫比乌斯函数 \(\mu(n)\)
例如:
从容斥角度看,\(\mu(d)\) 就是处理“同时被 \(d\) 的这些质因子整除”时的容斥系数。
1.2 核心恒等式
最重要的是:
也就是:
其中:
- \(1(n)=1\),常数函数;
- \(\varepsilon(1)=1,\varepsilon(n)=0(n>1)\),卷积单位元。
这个式子可以理解成:除了 \(n=1\) 以外,所有非互质情况都会被容斥抵消掉。
1.3 互质指示函数
由核心恒等式可得:
因为令:
则右边是:
当 \(g=1\) 时等于 \(1\),当 \(g>1\) 时等于 \(0\)。
这个式子是 gcd 计数题中最常用的拆法。
2. 两类反演形式
2.1 约数和反演
如果:
那么:
也可以写成:
含义:
\(F(n)\) 是由 \(n\) 的所有约数贡献累加得到的,现在要还原每个 \(f(n)\) 的真实贡献。
典型例子:
即:
所以:
展开得到:
2.2 倍数和反演
如果:
在上界为 \(N\) 时,通常写成:
那么:
含义:
\(F(n)\) 是所有 \(n\) 的倍数贡献之和,现在要从“被 \(n\) 整除”还原出“恰好等于 \(n\)”。
这是 gcd 计数中最常见的形式。
如果:
而:
那么只要 \(\gcd(i,j)=d\),且 \(x\mid d\),它就会被 \(F(x)\) 统计到,所以:
这就是倍数和反演。
3. 什么时候想到莫比乌斯反演
3.1 出现 gcd 恰好等于某值
如果题目问:
或:
通常考虑莫比乌斯反演。
常见转化:
等价于:
然后用:
3.2 “恰好”不好算,“至少”好算
例如:
- 恰好 \(\gcd=i\) 不好算;
- 但两个数都能被 \(i\) 整除很好算。
此时令:
那么:
这是倍数和反演。
3.3 和式中有互质限制
例如:
可以拆成:
交换枚举顺序:
这类题的关键是:把 \(\gcd=1\) 转成“枚举公共因子 \(d\)”。
3.4 出现 \(\sum\limits_{d\mid n}\) 或 \(\sum\limits_{n\mid d}\)
如果题目结构中出现:
考虑约数和反演。
如果出现:
考虑倍数和反演。
4. 常用套路总结
套路一:直接拆互质指示函数
题型
求:
推导
利用:
所以:
交换枚举顺序:
结论
复杂度
如果已经线性筛出 \(\mu\),直接求是 \(O(\min(n,m))\)。
如果需要多组询问,可以预处理 \(\mu\) 前缀和,并用整除分块优化到 \(O(\sqrt{n})\)。
套路二:求 gcd 恰好等于 \(k\)
题型
求:
的数对个数。
推导
令:
则:
范围变成:
所以答案是:
套路三:倍数和反演求 gcd 分布
题型
给定若干个数,求 gcd 恰好为 \(x\) 的非空子集数量。
设数组中有 \(cnt[v]\) 个数等于 \(v\)。
关键定义
令:
也就是数组中有多少个数是 \(x\) 的倍数。
那么 gcd 是 \(x\) 的倍数的非空子集数是:
令:
因为 gcd 恰好为 \(x,2x,3x,\dots\) 的子集都会被 \(F(x)\) 统计到,所以:
因此:
这是一种从大到小递推的倍数和反演写法。
也可以写成:
实战写法
通常不用显式套 \(\mu\),而是倒序减倍数:
for(int x=V;x>=1;x--){
f[x]=pow2[c[x]]-1;
for(int y=x+x;y<=V;y+=x){
f[x]=(f[x]-f[y]+mod)%mod;
}
}
这就是倍数和反演的工程写法。
5. 整除分块
很多莫比乌斯题最后会变成:
如果 \(n,m\) 很大,多组询问不能 \(O(n)\) 求。
注意:
在一段连续的 \(d\) 上取值不变。
对于当前位置 \(l\),令:
那么下一段右端点为:
在区间 \([l,r]\) 内:
所以:
如果预处理:
那么区间和是:
于是单次询问复杂度约为 \(O(\sqrt n)\)。
6. 线性筛求 \(\mu\)
6.1 思路
莫比乌斯函数满足:
- \(\mu(1)=1\);
- 如果 \(p\nmid i\),那么 \(\mu(ip)=-\mu(i)\);
- 如果 \(p\mid i\),那么 \(ip\) 含有平方因子,\(\mu(ip)=0\)。
6.2 代码模板
const int N=10000000+5;
int pri[N],tot,mu[N],vis[N],sumMu[N];
void get_mu(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
pri[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&i*pri[j]<=n;j++){
int p=pri[j];
vis[i*p]=1;
if(i%p==0){
mu[i*p]=0;
break;
}else{
mu[i*p]=-mu[i];
}
}
}
for(int i=1;i<=n;i++) sumMu[i]=sumMu[i-1]+mu[i];
}
7. 例题一:互质数对数量
题意
给定 \(n,m\),求:
的有序数对数量。
分析
直接枚举 \(i,j\) 是 \(O(nm)\)。
使用互质指示函数:
所以:
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N=1000000+5;
int pri[N],tot,mu[N],vis[N],sumMu[N];
void get_mu(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
pri[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&i*pri[j]<=n;j++){
int p=pri[j];
vis[i*p]=1;
if(i%p==0){
mu[i*p]=0;
break;
}else mu[i*p]=-mu[i];
}
}
for(int i=1;i<=n;i++) sumMu[i]=sumMu[i-1]+mu[i];
}
int solve(int n,int m){
if(n>m) swap(n,m);
int ans=0;
for(int l=1,r;l<=n;l=r+1){
int a=n/l,b=m/l;
r=min(n/a,m/b);
ans+=(sumMu[r]-sumMu[l-1])*a*b;
}
return ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
get_mu(min(n,m));
cout<<solve(n,m)<<endl;
return 0;
}
重点
这道题体现的是最基础的套路:
8. 例题二:gcd 等于 \(k\) 的数对数量
题意
给定 \(n,m,k\),求:
的有序数对数量。
分析
令:
则:
所以问题变成:
答案就是例题一的形式。
代码
int ans_gcd_k(int n,int m,int k){
n/=k;
m/=k;
if(n<=0||m<=0) return 0;
return solve(n,m);
}
重点
遇到:
不要直接反演,先整体除以 \(k\),转成:
9. 例题三:求数组中 gcd 为 1 的非空子集数
题意
给定一个长度为 \(n\) 的数组 \(a\),求有多少个非空子集,使得子集中所有数的 gcd 等于 \(1\)。
答案对 \(mod\) 取模。
分析
设最大值为 \(V\)。
令:
那么从这些 \(c_x\) 个数中选非空子集,得到的子集 gcd 一定是 \(x\) 的倍数。
所以:
表示 gcd 是 \(x\) 的倍数的非空子集数量。
令:
有:
倍数和反演:
答案是:
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N=1000000+5;
const int mod=1000000007;
int n,a[N],cnt[N],c[N],f[N],pw[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
int V=0;
for(int i=1;i<=n;i++){
cin>>a[i];
cnt[a[i]]++;
V=max(V,a[i]);
}
pw[0]=1;
for(int i=1;i<=n;i++) pw[i]=pw[i-1]*2%mod;
for(int x=1;x<=V;x++){
for(int y=x;y<=V;y+=x){
c[x]+=cnt[y];
}
}
for(int x=V;x>=1;x--){
f[x]=(pw[c[x]]-1+mod)%mod;
for(int y=x+x;y<=V;y+=x){
f[x]=(f[x]-f[y]+mod)%mod;
}
}
cout<<f[1]<<endl;
return 0;
}
重点
这道题的关键不是先想 \(\mu\),而是先定义:
因为这个东西非常好算:
再通过倍数和反演得到恰好值。
10. 例题四:按 gcd 加权求和
题意
求:
分析
把 gcd 的值按 \(d\) 分类:
而:
所以:
令:
那么可以整理为:
内部:
其实就是:
因为:
所以:
结论
重点
这种题说明:不一定最后都出现 \(\mu\),有时候反演整理以后会变成 \(\varphi\)。
11. 竞赛实战中的解题流程
Step 1:判断是不是 gcd / divisor 结构
看到以下关键词要敏感:
- \(\gcd(i,j)=1\)
- \(\gcd(i,j)=k\)
- \(\gcd\) 加权求和
- 互质数对
- 数组子集 gcd
- 倍数、约数贡献
- lcm 和 gcd 联合出现
Step 2:区分“恰好”和“至少”
常见对应:
| 目标 | 容易统计的量 |
|---|---|
| gcd 恰好为 \(x\) | 两个数都被 \(x\) 整除 |
| 子集 gcd 恰好为 \(x\) | 子集所有数都被 \(x\) 整除 |
| \(\gcd(i,j)=1\) | 枚举公共因子 \(d\) |
| \(\gcd(i,j)=k\) | 除以 \(k\),变成互质 |
Step 3:写出 \(F\) 和 \(f\)
这是最重要的一步。
如果是约数和:
如果是倍数和:
不要一上来背公式,先把“谁统计了谁”说清楚。
Step 4:选择实现方式
常用实现有三种:
方式一:显式用 \(\mu\)
适合互质数对:
方式二:倒序减倍数
适合 gcd 分布、子集 gcd:
for(int x=V;x>=1;x--){
f[x]=F[x];
for(int y=x+x;y<=V;y+=x){
f[x]-=f[y];
}
}
方式三:整除分块
适合多组大范围询问:
for(int l=1,r;l<=n;l=r+1){
int a=n/l,b=m/l;
r=min(n/a,m/b);
ans+=(sumMu[r]-sumMu[l-1])*a*b;
}
12. 常见坑点
12.1 有序数对和无序数对
和:
是否算不同,要看题意。
如果从有序数对转无序数对,不能盲目除以 \(2\),因为 \(i=j\) 的情况要单独处理。
12.2 \(\gcd(i,j)=1\) 中是否包含 \(0\)
很多公式默认:
如果范围包含 \(0\),要特别处理:
例如:
但:
只有 \(x=1\)。
12.3 上界要取最小值
求:
时,\(d\) 只需要枚举到:
12.4 取模时注意负数
倍数减法:
f[x]=(f[x]-f[y]+mod)%mod;
不能直接:
f[x]-=f[y];
除非最后统一处理且不会溢出。
12.5 整除分块右端点
对于:
相同的一段,右端点是:
如果有两个变量 \(n,m\),则:
13. 总结
莫比乌斯反演题的核心不是背公式,而是识别“累加关系”。
最常用的判断方式:
- 如果看到“互质”:
- 如果看到“gcd 恰好为 \(x\)”:
优先考虑倍数和反演。
- 如果看到“约数贡献总和”:
考虑约数和反演。
- 如果答案里有:
并且多组询问,考虑整除分块和 \(\mu\) 前缀和。
一句话概括:
莫比乌斯反演就是用容斥把“被约数或倍数重复统计的贡献”拆回“恰好贡献”。

浙公网安备 33010602011771号