莫比乌斯反演学习笔记
【前言】
运用莫比乌斯反演通常可以将复杂的求和柿子简单化。
除特殊说明,约定所有除法均为整除。
【前置芝士】
【整除分块】
设 \(N/i′=N/i\) 相等,则 \(i′\) 的最大值为 \(N/(N/i)\)。
证明:
必要性:\(\left\lfloor\dfrac{N}{i'}\right\rfloor\) 与 \(\left\lfloor\dfrac{N}{i}\right\rfloor\) 相等,也就是 \(\left\lfloor\dfrac{N}{i'}\right\rfloor=\left\lfloor\dfrac{N}{i}\right\rfloor\),得到 \(\dfrac{N}{i'}≥\left\lfloor\dfrac{N}{i}\right\rfloor\)。
两边取倒数,不等号变向 \(\dfrac{i'}{N}≤1/(\left\lfloor\dfrac{N}{i}\right\rfloor)\)。
两边同乘 \(N\),得到 \(i'≤N/(\left\lfloor\dfrac{N}{i}\right\rfloor)\)。
也就是 \(i'≤\left\lfloor\dfrac{N}{\left\lfloor\dfrac{N}{i}\right\rfloor}\right\rfloor\)。
充分性=:令 \(i'=\left\lfloor\dfrac{N}{\left\lfloor\dfrac{N}{i}\right\rfloor}\right\rfloor\)。
得到 \(\left\lfloor\dfrac{N}{i'}\right\rfloor=\left\lfloor\dfrac{N}{\left\lfloor\dfrac{N}{\left\lfloor\dfrac{N}{i}\right\rfloor}\right\rfloor}\right\rfloor=\left\lfloor\dfrac{N}{i}\right\rfloor\)。
证毕。
也就是说我们知道了一个区间的左端后,令 \(r=\left\lfloor\dfrac{N}{\left\lfloor\dfrac{N}{l}\right\rfloor}\right\rfloor\) 整个区间 \([l,r]\) 的 \(\left\lfloor\dfrac{N}{i}\right\rfloor\) 的值是一样的。
然后就可以愉快的 \(O(\sqrt N)\) 分块了。
【狄利克雷卷积与数论函数】
【基本符号】
两个函数 \(f(n),g(n)\) 的狄利克雷卷积写作 \(f*g\)。
定义:\(f*g=\sum\limits_{d|n} f(d)g(n/d)\)。
它满足以下显然的运算律:
- \(f*g=g*f\)。
- \((f*g)*h=f*(g*h)\)。
- \((f+g)*h=f*h+g*h\)。
然后是在卷积中常用到的几个数论函数:
- \(I(n)\),恒等函数,无论 \(n\) 为何值,\(I(n)=1\)。
- \(\varepsilon(n)\),元函数,是卷积中的单位元,当 \(n=1\) 时,\(\varepsilon(n)=1\),否则为 \(0\)。
- \(id(n)\),单位函数,\(id(n)=n\)。
- \(\varphi(n)\),欧拉函数,小于等于 \(n\) 的整数中,与 \(n\) 互质的数的个数。
- \(\mu(n)\),莫比乌斯函数,定义见后。
其中 \(1\sim3\) 都是完全积性函数,\(4,5\) 是积性函数。
定理:两个积性函数的狄利克雷卷积还是积性函数。
当 \(\varepsilon=f*g\) 时,我们称 \(f,g\) 互逆。
【数论函数】
这里给出更加全面的数论函数的介绍。
常用数论函数表:
函数名称 | 符号 | 定义 | 积性 | 卷积 | |
---|---|---|---|---|---|
恒等函数 | \(I(n)\) | 永远等于 \(1\) | 完全积性函数 | ||
元函数 | \(\varepsilon(n)\) | 当 \(n=1\) 时,\(\varepsilon(n)=1\),否则为 \(0\) | 完全积性函数 | ||
单位函数 | \(id(n)\) | 函数值为 \(n\) | 完全积性函数 | ||
幂函数 | \(id^x(n)\) | 函数值为 \(n^x\) | 完全积性函数 | ||
莫比乌斯函数 | \(\mu(n)\) | \(\mu * I = e\) | 积性函数 | \(\mu*I=e\) | |
欧拉函数 | \(\varphi(n)\) | 小于等于 \(n\) 的整数中,与 \(n\) 互质的数的个数 | 积性函数 | \(\varphi*I=id\) | |
约数个数函数 | \(d(n)\),也作 \(\sigma_0(n)\) | \(n\) 的约数个数 | 积性函数 | \(I*I=d\) | |
约数和函数 | \(\sigma(n)\) | \(n\) 的约数和 | 积性函数 | \(I*id=\sigma\) | |
除数函数 | \(\sigma_k(n)\) | \(n\) 的约数 \(k\) 次方和 | 积性函数 | \(I*id^k=\sigma_k\) |
欧拉函数 \(\varphi(n)=\sum\limits_{i=1}^n [\gcd(i,n)=1]\)。
它是积性函数,可以使用线性筛求出。
简单代码:
phi[1] = 1;
if(i is a prime) phi[i] = i - 1;
// now, we get a prime named p,and a number i
if(i % p) phi[i * p] = phi[i] * (p - 1);
else {phi[i * p] = phi[i] * p; break;}
不仅如此,约束函数也是可以线性筛的。
公式挺好推的,例如 \(\sigma(n)\)。(顺便带上一个 \(\mu(n)\))
int num = i * prm[j];
if(i % prm[j])
mu[num] = - mu[i], sum[num] = sum[i] * (prm[j] + 1);
else{
mu[num] = 0;
sum[num] = (prm[j] + 1) * sum[i] - prm[j] * sum[i / prm[j]];
break;
}
【卷积结论】
即:\(\sum_{d\mid n}\varphi(d)=n\)。
证明一下,设 \(F=I*\varphi\),因为 \(F\) 为积性函数,所以只需证明对于所有 \(F(prime^k)=prime\) 即可。
Q.E.D
我们还可以得到一下结论:\(I*\varphi =id\to \mu * id = \varphi\)
即:\(\sum_{d\mid n}\mu(d)(n/d)=\varphi(n)\)。
这只是众多反演结论中的一个,但已经有力地证明了卷积的强大作用。
【反演公式】
设 \(F(n)=\sum\limits_{d|n} f(d)\)。
显然 \(F(n)=\sum\limits_{d|n} 1\times f(d)\)
写成卷积的形式 \(F=I*f\)。
如果 \(f\) 很容易求,那么可以直接根据定义推得 \(F\)。
但如果 \(F\) 易求而需要反推 \(f\),就两边同时乘 \(I^{-1}\) 得:
也就是说,只需要构造一个函数与 \(I\) 互逆,那么用其卷积就可以简单得到答案。
于是我们构造了 \(\mu * I=\varepsilon\),也就是莫比乌斯函数。
然后就有了反演公式:
-
嵌入式反演公式:最基本的公式。
由 \(\mu *I=\varepsilon\) 得 \(\sum\limits_{d|n}\mu(d)=[n=1]\)。
-
约数式反演公式:
若有:
\[F(n)=\sum\limits_{d|n} f(d) \]则:
\[f(n)=\sum\limits_{d|n} \mu(d)F(n/d) \]也作:
\[f(n)=\sum\limits_{d|n} \mu(n/d)F(d) \]简单证明:\(F=f*I\to f=F*I^{-1}\to f=F*\mu\)。
-
倍数式反演公式:
若有:
\[F(n)=\sum\limits_{n|d} f(d) \]则:
\[f(n)=\sum\limits_{n|d} \mu(d/n)F(d) \]这个不是卷积形式,需要证明一下。
将 \(F(n)\) 的定义代入得:
\[=\sum\limits_{n|d}\mu(d/n)\sum\limits_{d|t} f(t) \]考虑枚举 \(d=kn\)。
\[=\sum\limits_{k}\mu(k)\sum\limits_{nk|t} f(t) \]交换求和符号得:
\[=\sum\limits_{t}f(t)\sum\limits_{nk|t}\mu(k) \]\[=\sum\limits_{t}f(t) \sum\limits_{n|t,k|(t/n)} \]根据嵌入式反演公式得:
\[=\sum\limits_{t}f(t)[t=n]=f(n) \]证毕。
【简单应用】
首先提示,本文中默认 \(n\leq m\)。
练手题:
求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=1]\)。
直接用反演公式:
交换求和顺序得:
运用整除分块 + 预处理 \(\mu\) 的前缀和可以 \(O(n+\sqrt n)\) 解决。
求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=prime]\)。
首先这题可以用 \(\varphi(n)\) 直接上。
但是没有反演魔术,很没有做题体验。
考虑针对每个质数单独处理,因为质数分布为 \(\log\log n\) 的,所以不用担心复杂度。
然后针对质数 \(p\),我们需要统计:
考虑枚举 \(i,j\) 分别为 \(p\) 的几倍:
然后就是和上面一样的问题了。
和上一题一样,有 \(T\leq 10^4\) 组数据,\(n\leq 10^7\)。
枚举质数的方法跑步过去了,\(\varphi\) 的 native
做法也不太行。
将上一步最后的柿子整理得:
令 \(T=dp\),并交换求和顺序:
再换一下顺序:
前面整除分块,后面预处理,时间变为 \(O(n+T\sqrt n)\)。
求 \(\sum\limits_{i=1}^n \sum\limits_{j=1}^m d(ij)\)
这题有一个很难想的点:
将每个质因子分开考虑,设 \(x=p^a\times x',y=p^b\times y'\)。
那么显然:\(xy=p^a\times p^b\times x'y'\)。
在等式左边,我们选择 \(p\) 的指数有 \(a+b+1\) 种选择,分别为 \(p^0,p^1,\cdots,p^{a+b}\)。
在等式右边,我们同样有 \(a+b+1\) 种选择,因为 \(x,y\) 互质,所以为从 \(x\) 中选 \(1\sim a\) 个或从 \(y\) 中选 \(1\sim b\) 个,或都不选。
所以两边形成了一一映射,故等价。
然后就是快乐反演时间:
设 \(g(x)=\sum_{i=1}^{x} \lfloor x/i\rfloor\)。
显然 \(g\) 可以 \(O(n\sqrt n)\) 整除分块预处理出来,查询也可以 \(O(T\sqrt n)\) 整除分块出来。
求:\(\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sigma_1(\gcd(i,j))[\sigma_1(\gcd(i,j))\leq a]\)。
先反演一下:
或许可以稍微快一点:
设 \(g(T)=\sum_{d\mid T}\sigma_1(d)\mu(T/d)\)
没有上述限制的话,预处理一下就没了。
考虑到每次 \(\sigma_1(d)\leq a\) 才能有作用,我们考虑将查询离线,按照 \(a\) 升序。
每次将能够记录的值插入到某个数据结构中去。
这个数据结构需要支持:插入、查询区间和,且效率至少为 \(O(\sqrt n)\),显然树状数组即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MP make_pair
#define X first
#define Y second
using namespace std;
const int MX = 1e5;
const int N = 1e5 + 10;
const int M = 2e4 + 10;
int Q, tot, mu[N], sum[N], prm[N], c[N], ans[M];
bool vis[N];
struct Query{int n, m, a, id;} q[M];
pair<int, int> f[N];
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
bool cmp(Query x, Query y){return x.a < y.a;}
void Pre_Work(){
mu[1] = 1, sum[1] = 1;
for(int i = 2; i <= MX; i ++){
if(!vis[i])
prm[++ tot] = i, mu[i] = -1, sum[i] = i + 1;
for(int j = 1, num; j <= tot && (num = i * prm[j]) <= MX; j ++){
vis[num] = true;
if(i % prm[j])
mu[num] = - mu[i], sum[num] = sum[i] * (prm[j] + 1);
else{
mu[num] = 0;
sum[num] = (prm[j] + 1) * sum[i] - prm[j] * sum[i / prm[j]];
break;
}
}
}
for(int i = 1; i <= MX; i ++) f[i] = MP(sum[i], i);
sort(f + 1, f + MX + 1);
}
int Modify(int x, int v){for(; x <= MX; x += x & -x) c[x] += v;}
int Ask(int x){int v = 0; for(; x; x -= x & -x) v += c[x]; return v;}
int Solve(int n, int m){
if(n > m) swap(n, m);
int sum = 0;
for(int l = 1, r; l <= n; l = r + 1){
r = min(n / (n / l), m / (m / l));
sum += (Ask(r) - Ask(l - 1)) * (n / l) * (m / l);
}
return sum;
}
int main(){
Q = read();
for(int i = 1; i <= Q; i ++){
int n = read(), m = read(), a = read();
q[i] = (Query){n, m, a, i};
}
sort(q + 1, q + Q + 1, cmp);
Pre_Work();
int j = 1;
for(int i = 1; i <= Q; i ++){
while(j <= MX && f[j].X <= q[i].a){
int d = f[j].Y;
for(int T = d; T <= MX; T += d)
Modify(T, mu[T / d] * sum[d]);
j ++;
}
ans[q[i].id] = Solve(q[i].n, q[i].m) & ((1LL << 31) - 1);
}
for(int i = 1; i <= Q; i ++) printf("%d\n", ans[i]);
return 0;
}
【总结】
莫比乌斯反演,本质是利用莫比乌斯函数与其他函数间卷积关系,对函数做一系列简化,从而更高效的解决问题。
或许在学习之后可以看看和式的相关介绍,再看文中的某些 \(\sum\) 之间的转换就很自然了。
参考资料&特别鸣谢:
完结撒花