莫比乌斯反演
莫比乌斯函数
\(\mu(d)\) 本质上是一个经过总结规律得出的容斥系数、定义如下:
- \(d=1\) 时, \(\mu(d)=1\)
- \(d=\prod_{i=1}^k p_i\) ,其中 \(p_i\) 为互异素数,则 \(\mu(d)=(-1)^k\) ,即质因数次数最大为 1
- 其他情况,\(\mu(d)=0\) ,即存在次数大于 1 的质因数
有如下性质:
-
\(\sum_{d|n}\mu(d)=[n=1]\) ,证明如下:
-
\(n=1\) 时成立
-
\(n>1\) ,可分解为 \(n=\prod_{i=1}^k p_i^{a_i}\) ,\(n\) 的因子的 \(\mu\) 值不为 0 只有质因数次数都为 1 的因子
显然由 \(r\) 个质因数构成的因子有 \(C_k^r\) 个
则 \(\sum_{d|n}\mu(d)=\sum_{i=0}^k(-1)^k C_{k}^i\) ,即所有不为 0 的因子的贡献和
由二项式定理:\((x+y)^k=\sum_{i=0}^k C_{k}^i x^i y^{n-i}\)
得 \(\sum_{i=0}^k(-1)^k C_{k}^i=[(-1)+1]^k=0\)
-
-
\(\sum_{d|n}\dfrac{\mu(d)}{d}=\dfrac{\varphi(n)}{n}\)
用狄利克雷卷积可证
-
积性函数
积性函数自然可以线性筛
const int N = 1e7 + 5;
int vis[N], pr[N], cnt, mu[1];
void get() {
mu[1] = 1;
for (int i = 2; i <= 1e7; i++) {
if (!vis[i]) pr[++cnt] = i;
for (int j = 1, x; j <= cnt && i * pr[j] <= 1e7; j++) {
vis[x = i * pr[j]] = 1;
if (i % pr[j] == 0) { mu[x] = 0; break; }
mu[x] = -mu[i];
}
}
}
莫比乌斯反演
公式
证明:
这里还有第二个形式,在推导中更为常用
证明,另 \(t=\frac{d}{n}\) :
莫比乌斯反演公式的本质上都是 \(\sum_{d|n}\mu(d)=[n=1]\)
实际情况,推导时用莫反公式却麻烦,用这个性质更为简单
但是完整用公式推导可以更好地练习
例
[POI2007] Queries
求 \(\sum_{i=1}^a\sum_{j=1}^b[\gcd(i,j)=k]\)
sol 1
令 \(F(n)\) 为满足 \(n|\gcd(i,j)\) 的 \((i,j)\) 对数,\(f(n)\) 为满足 \(n=\gcd(i,j)\) 的 \((i,j)\) 对数,所求为 \(f(k)\)
由于 \(n|\gcd(i,j)\) ,则 \(i,j\) 都是 \(n\) 的倍数,组合数学知识得到 \(F(n)=\lfloor\dfrac{a}{n}\rfloor\lfloor\dfrac{b}{n}\rfloor\)
显然 \(F(n)=\sum_{n|d}f(d)\) ,这是莫反的第二种形式,反演得到
预处理 \(\mu\) 的前缀和,整除分块即可
sol 2
难以理解?考虑不用反演公式
显然 \(i,j\) 都要是 \(k\) 的倍数,枚举这个倍数,即可转换为互质问题
令 \(x=\lfloor\frac{a}{k}\rfloor,y=\lfloor\frac{b}{k}\rfloor\)
将 \(\sum_{d|n}\mu(d)=[n=1]\) 代入
枚举 \(\gcd\)
各回各家
后面两个 \(\sum\) 可以直接求?
整除分块即可
code
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 5e4 + 5;
int Ti, n, m, K, vis[N], pr[N], cnt, mu[N], res, s[N];
int main() {
mu[1] = 1;
for (int i = 2; i <= 5e4; i++) {
if (!vis[i]) pr[++cnt] = i, mu[i] = -1;
for (int j = 1, x; j <= cnt && i * pr[j] <= 5e4; j++) {
vis[x = i * pr[j]] = 1;
if (i % pr[j] == 0) { mu[x] = 0; break; }
mu[x] = -mu[i];
}
}
for (int i = 1; i <= 5e4; i++) s[i] = s[i - 1] + mu[i];
scanf("%d", &Ti);
while (Ti--) {
scanf("%d%d%d", &n, &m, &K);
n /= K, m /= K;
if (n > m) swap(n, m);
res = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
res += (s[r] - s[l - 1]) * (n / l) * (m / l);
}
printf("%d\n", res);
}
}
[SDOI2015] 约数个数和
求 \(\sum_{i=1}^n\sum_{j=1}^m\sigma_0(ij)\)
重要性质:\(\sigma_0(ij)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]\)
证明:
接下来推导
其中 \(f(n)=\sum_{i=1}^n\lfloor\dfrac{n}{i}\rfloor\) 。
\(n,m\le 50000\) ,其实直接暴力求即可。
由于
所以 \(f\) 是可以线性筛的。此处用暴力实现
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 50005;
int Ti, n, m, pr[N], cnt, mu[N], sm[N];
LL ans, dv[N];
bitset<N> vis;
int main() {
mu[1] = 1;
for (int i = 2; i <= 50000; i++) {
if (!vis[i]) pr[++cnt] = i, mu[i] = -1;
for (int j = 1, x; j <= cnt && i * pr[j] <= 50000; j++) {
x = i * pr[j], vis[x] = 1;
if (i % pr[j]) mu[x] = -mu[i];
else { mu[x] = 0; break; }
}
}
for (int i = 1; i <= 50000; i++) {
sm[i] = sm[i - 1] + mu[i];
for (int l = 1, r; l <= i; l = r + 1) {
r = i / (i / l);
dv[i] += 1ll * (r - l + 1) * (i / l);
}
}
scanf("%d", &Ti);
for (; Ti--; ) {
scanf("%d%d", &n, &m);
if (n > m) n ^= m ^= n ^= m;
ans = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += dv[n / l] * dv[m / l] * (sm[r] - sm[l - 1]);
}
printf("%lld\n", ans);
}
}
[国家集训队]Crash的数字表格
求 \(\sum_{i=1}^n\sum_{j=1}^m\text{lcm}(i,j)\)
基本上比较套路地推导,尝试几遍就可以了
此处假设 \(n\le m\)
令 \(f(x)=\sum_{i=1}^x i\) ,进一步化为
此时已经可以用两个整除嵌套分块完成 \(O(n)\) 的做法。
这里还有一个常见套路:令 \(T=dk\) ,并枚举 \(T\)
看后面这一个 \(T\sum_{d|T}\mu(d)*d\) ,设函数 \(F(T)=\sum_{d|T}\mu(d)*d\) ,要求 \(T*F(T)\) 的前缀和
\(F(T)\) 显然是积性函数。
考虑 \(F(p^k)=\sum_{i=0}^k\mu(p^i)*p^i\)
由于在 \(i\ge 2\) 时 \(\mu(p^i)=0\) ,所以 \(F(p^k)=\mu(p^0)*p^0+\mu(p^1)*p^1=1-p\) ,可以 \(O(1)\) 求
所以 \(F\) 可以线性筛。枚举 \(x\) 和质数 \(p\)
- \(x\perp p\) ,\(F(xp)=F(x)F(p)\)
- 否则,\(\mu(xp)=0\) ,\(F(xp)=\sum_{d|xp}\mu(d)*d=\mu(xp)*xp+F(x)=F(x)\)
一个数论分块即可,\(O(\sqrt n)\)
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const LL P = 20101009;
const int N = 1e7 + 5;
int n, m, vis[N], pr[N], cnt;
LL f[N], res;
inline LL S(int n) { return 1ll * n * (n + 1) / 2 % P; }
int main() {
f[1] = 1;
for (int i = 2; i <= 1e7; i++) {
if (!vis[i]) pr[++cnt] = i, f[i] = (P + 1 - i) % P;
for (int j = 1, x; j <= cnt && i * pr[j] <= 1e7; j++) {
vis[x = i * pr[j]] = 1;
if (i % pr[j] == 0) { f[x] = f[i]; break; }
f[x] = f[i] * f[pr[j]] % P;
}
}
for (int i = 2; i <= 1e7; i++) f[i] = (f[i] * i % P + f[i - 1]) % P;
scanf("%d%d", &n, &m);
res = 0;
if (n > m) n ^= m ^= n ^= m;
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
(res += S(n / l) * S(m / l) % P * (f[r] - f[l - 1] + P) % P) %= P;
}
printf("%lld", res);
}
总结
-
对于 \(\gcd\) 的问题用 \(\sum_{d|n}\mu(d)=[n=1]\) 能解决大部分
-
常见套路
- 枚举 \(\gcd\)
- 枚举倍数
- 和式变化,约数项提到前面
- 换元,再提到前面
-
一种形式(假设 \(n\le m\) )
\[\begin{aligned} \sum_{i=1}^n\sum_{j=1}^mf(\gcd(i,j))&= \sum_{d=1}^{n}\sum_{i=1}^n\sum_{j=1}^m f(d)[\gcd(i,j)=d]\\ &=\sum_{d=1}^{n}\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor} \sum_{j=1}^{\lfloor\frac{m}{d}\rfloor} f(d)[\gcd(i,j)=1]\\ &=\sum_{d=1}^{n}f(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor} \sum_{j=1}^{\lfloor\frac{m}{d}\rfloor} \sum_{k|\gcd(i,j)}\mu(k)\\ &=\sum_{d=1}^{n}f(d)\sum_{k=1}^{\lfloor\frac{n}{d}\rfloor}\mu(k) \sum_{i=1}^{\lfloor\frac{n}{dk}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{dk}\rfloor}1\\ &=\sum_{d=1}^{n}f(d)\sum_{k=1}^{\lfloor\frac{n}{d}\rfloor}\mu(k) \lfloor\dfrac{n}{dk}\rfloor\lfloor\dfrac{m}{dk}\rfloor \end{aligned} \]还是令 \(T=dk\)
\[\sum_{T=1}^n\lfloor\dfrac{n}{T}\rfloor\lfloor\dfrac{m}{T}\rfloor \sum_{d|T}f(d)\mu(\dfrac{T}{d}) \] -
多加练习,找到感觉