莫比乌斯反演和数论函数
感觉一直没有认真学过数论,这次好好记一下。
前置知识:整除分块,筛法。
一些符号说明:
\([...]\):这个是艾弗森括号,意思是若括号内的表达式成立则为 \(1\),否则为 \(0\)。
有些时候会用 \((i, j)\) 来表示 \(\gcd(i, j)\)。
若非特殊说明,除法均为下取整。
Part 1 基础数论函数 & 狄利克雷卷积
基础数论函数
数论函数指定义域为整数的函数,也可视作一个数列。
一些常用的数论函数:
- 单位函数:\(\epsilon(n) = [n = 1]\)。
- 恒等函数:\(\text{id}_k(n) = n^k\)。特别地,当 \(k = 1\) 的时候通常简记作 \(\text{id}(n)\)。
- 常数函数:\(1(n) = 1\)。
- 除数函数:\(\sigma_k(n) = \sum\limits_{d | n} d^k\)。特别地,当 \(k = 1\) 的时候通常简记作 \(\sigma (n)\),\(\sigma_0(n)\) 通常简记作 \(d(n)\) 或 \(\tau(n)\)。
- 欧拉函数:\(\varphi(n) = \sum\limits_{i = 1}^{n}[(i, n) = 1]\),即表示 \(1\sim n\) 中与 \(n\) 互质的数的个数。
- 莫比乌斯函数:\(\mu(n) = \left\{ \begin{aligned} 1 && n = 1 \\ 0 &&\exists d>1, d^2|n \\ (-1)^{\omega(n)} && \text{otherwise} \end{aligned} \right.\),其中 \(\omega(n)\) 表示 \(n\) 本质不同的质因子个数。
完全积性函数:若函数 \(f\) 满足 \(f(1) = 1\) 且 \(\forall x, y\in \mathbf{N}^*\) 均有 \(f(xy) = f(x)f(y)\),则 \(f\) 为完全积性函数,如 \(1(n)\),\(\epsilon(n)\),\(\varphi(n)\),\(\mu(n)\) 都是完全积性函数。
积性函数:若函数 \(f\) 满足 \(f(1) = 1\) 且 \(\forall x, y\in \mathbf{N}^*, x \perp y\) 均有 \(f(xy) = f(x)f(y)\),则 \(f\) 为积性函数。显然积性函数包含完全积性函数。
积性函数的一个性质:\(f(n) = \prod f(p_i^{x_i})\)。这是因为 \(p_i^{x_i} \perp p_j^{x_j}\)。所以一般研究积性函数可以先研究 \(f(p^k)\) 的值,然后就可以筛了。
狄利克雷(Dirichlet)卷积
定义两个数论函数 \(f(x), g(x)\),则它们的狄利克雷卷积得到的结果 \(h(x)\) 定义为:\(h(x) = \sum\limits_{d | x} f(d)g(\frac{x}{d}) = \sum\limits_{ab = x}f(a)g(b)\)。这可以简记为 \(h = f * g\)。
性质:交换律(\(f * g = g * f\)),结合律(\((f * g) * h = f * (g * h)\)),分配律(\((f + g) * h = f * h + g * h\))。
单位元:单位函数 \(\epsilon\) 是狄利克雷卷积运算中的单位元,即 \(f * \epsilon = f\)。
逆元:对于任意满足 \(f(1) \neq 0\) 的数论函数 \(f\),若存在另一个数论函数 \(g\) 满足 \(f * g = \epsilon\),则称 \(g\) 是 \(f\) 的逆元。显然逆元是唯一的。容易构造出 \(g(n)\):\(g(n) = \frac{\epsilon(n) - \sum\limits_{d > 1, d | n}f(d)g(\frac{n}{d})}{f(1)}\)。
重要结论:
两个积性函数的 Dirichlet 卷积也是积性函数
证明:记 \(h = f * g\)。
则根据积性函数的定义,有:\(\forall a \perp b, h(n) = h(a)h(b)\)。
\(h(a) = \sum\limits_{i | a} f(i)g(\frac{a}{i})\),\(h(b)\) 同理。
则有
\[\begin{aligned}h(n) &= \sum\limits_{i | a}f(i)g(\frac{a}{i})\sum\limits_{j | b}f(j)g(\frac{b}{j}) \\ &= \sum\limits_{i | a}\sum\limits_{j | b} f(i)f(j)g(\frac{a}{i})g(\frac{b}{j}) \end{aligned} \]因为 \(a\perp b\),所以有 \(i\perp j, \frac{a}{i}\perp\frac{b}{j}\),则可继续化为
\[\begin{aligned} h(n) &= \sum\limits_{i | a}\sum\limits_{j | b}f(ij)g(\frac{ab}{ij}) \\ &= \sum\limits_{d | ab} f(d)g(\frac{ab}{d}) \end{aligned} \]所以 \(h(n)\) 为积性函数,原命题得证。\(\square\)
积性函数的逆元也是积性函数。
证明:由于表示出 \(g(n)\) 的式子是一个由大规模到小规模的样子,考虑使用归纳法证明。
设 \(f * g = \epsilon\),不妨令 \(f(1) = 1\)。
当 \(ab = 1\),则 \(g(ab) = g(1) = 1\),此时结论显然成立。
若 \(ab > 1(a\perp b)\),假设当前所有 \(i < ab\) 的结论均成立,则有:
\[\begin{aligned} g(ab) &= -\sum\limits_{d > 1, d | ab}f(d)g(\frac{ab}{d}) \\ &= -\sum\limits_{x | a, y | b, xy > 1} f(xy)g(\frac{ab}{xy}) \\ &= -\sum\limits_{x | a, y | b, xy > 1} f(x)f(y)g(\frac{a}{x})g(\frac{b}{y}) \\ &= f(1)g(ab)-\sum\limits_{x | a}f(x)g(\frac ax)\sum\limits_{y | b}f(y)g(\frac by) \\ &= f(1)f(1)g(a)g(b)-\epsilon(a)\epsilon(b) \\ &= g(a)g(b) - \epsilon(a)\epsilon(b) \\ &= g(a)g(b) \end{aligned} \]所以 \(g\) 为积性函数。\(\square\)
一些常用的 Dirichlet 卷积的例子:
- \(\epsilon = \mu * 1 \iff \epsilon(n) = \sum\limits_{d | n}\mu(n)\)
- \(d = 1 * 1 \iff d(n) = \sum\limits_{d | n}1\)
- \(\sigma = \text{id} * 1 \iff \sigma(n) = \sum\limits_{d | n}d\)
- \(\varphi = \mu * \text{id} \iff \varphi(i) = \sum\limits_{d | n} d\cdot\mu(\frac{n}{d})\)。
Part 2 莫比乌斯反演
引入
\(\mu\) 为莫比乌斯函数,定义为:\(\mu(n) = \left\{ \begin{aligned} 1 && n = 1 \\ 0 &&\exists d>1, d^2|n \\ (-1)^{\omega(n)} && \text{otherwise} \end{aligned} \right.\)。
\(\mu\) 最开始引入的原因是若 \(g = \sum\limits_{d | n}f(d)\),则 \(g = 1 * f\),但是这样只能根据 \(f\) 求 \(g\),而从 \(g\) 推到 \(f\) 的式子显然就变为了 \(f = 1^{-1} * g\),然后 Mobius 就将 \(1^{-1}\) 命名为一个新的函数,就是 \(\mu\) 了。
\(\epsilon\) 是单位元,则有 \(1 * \mu = \epsilon\),我们可以据此对 \(\mu\) 式子进行对到推导:
过程:
首先 \(\mu(1)\) 显然为 \(1\)。
由于 \(1\) 是积性函数,所以 \(\mu = 1^{-1}\) 一定也是积性函数,不妨先考虑 \(\mu(p^k)\)(\(k > 0\))的值。
- \(k = 1\),由于 \(\sum \limits_{d | p} 1(d)\mu(\frac{p}{d}) = \epsilon(p) = 0\),所以 \(\mu(1) + \mu(p) = 0\),可以得到 \(\mu(p) = -1\)。
- \(k > 1\),有 \(\sum\limits_{d | p^k} 1(d)\mu(\frac{p^k}{d}) = \epsilon(k) = 0\),所以 \(\sum\limits_{i = 0}^{k}\mu(p^k) = 0\),对 \(k\) 从小到大考虑并带入 \(\mu(p^k)\) 的值后,易得 \(\mu(p^k) = 0(k > 1)\)。
然后由于 \(\mu\) 是积性函数,所以所有含有平方或更高次方的因子的时候,均有 \(\mu(n) = 0\)。
然后剩下的数肯定就是由若干个本质不同的质数相乘得到的,则 \(\mu(n) = \prod\limits_{p | n, p\in \text{prime}}\mu(p)\),所以 \(\mu(n) = (-1)^{\omega(n)}\),\(\omega(n)\) 就表示 \(n\) 的本质不同的质数个数。
容易发现 \(\mu\) 的式子就是上面的形式。
由 \(\epsilon = 1 * \mu\) 可以推出一个很好的结论:\(\sum\limits_{d | n}\mu(d) = \left\{ \begin{aligned} 1 && n = 1 \\ 0 && n \neq 1 \end{aligned} \right.\),这个结论在下面的例题中会经常用到。
线筛 \(\mu\)
发现 \(\mu\) 是一个积性函数,显然可以线筛。这里就直接放代码了,不懂线筛的可以看 这个。
Code:
bool vis[500010];
int cnt, p[500010], mu[500010];
void init(int n) {
mu[1] = 1;
for(int i = 2; i <= n; ++i) {
if(!vis[i])
mu[i] = -1, p[++cnt] = i;
for(int j = 1; j <= cnt && i * p[j] <= n; ++j) {
mu[i * p[j]] = i % p[j] == 0 ? 0 : -mu[i];
vis[i * p[j]] = 1;
if(i % p[j] == 0)
break;
}
}
}
然后就是介绍怎么反演了。
反演公式
设 \(f, g\) 为数论函数。
第一种:若 \(f(n) = \sum\limits_{d | n}g(d)\),则 \(g(n) = \sum\limits_{d | n}\mu(d)f(\frac{n}{d})\)。证明显然,\(f = g * 1 \iff g = \mu * f\)。
第二种:若 \(f(n) = \sum\limits_{d | n}g(d)\),则 \(g(n) = \sum\limits_{d | n}\mu(\frac{n}{d})f(d)\),这个就是因为 Dirichlet 卷积具有交换律,即 \(\mu * f = f * \mu\)。
莫反的一个重要结论:\(\sum\limits_{d | n} \mu(d) = \epsilon(n)\),当 \(n > 1\) 的时候式子就为 \(0\) 了。证明就是 \(\mu * 1 = \epsilon\)。
拓展:\(\varphi(i) * 1 = \text{id}\)。
证明:
当 \(n = p^k\) 时,
有
\[\begin{aligned} \varphi * 1 &= \sum\limits_{d | p^k}\varphi(d) \\ &= p ^ 0 + p ^ 0\cdot (p - 1) + p ^ 1\cdot (p - 1) + \cdots + p ^ {k - 1} \cdot (p - 1) \\ &= p^k \\ &= \text{id} \end{aligned} \]然后由于 \(\varphi\) 和 \(1\) 是积性函数,所以这里就只证明 \(p^k\) 的取值满足就行了。\(\square\)
由 \(\varphi * 1 = \text{id}\) 可以得到 \(\text{id} * \mu = \varphi\)。
Part 3 一些例题
1 一道经典的题
Problem
给定 \(n, m\),求 \(\sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}[\gcd(i, j) = 1]\) 的值。
要求在 \(\mathcal{O}(n + T\times \sqrt{\min(n, m)})\) 的时间复杂度内解决,\(T\) 为测试数据的组数。
Sol
因为 \(i, j\) 维交换顺序不影响答案,所以不妨令 \(n\le m\)。
\([(i, j) = 1]\) 的这个条件显然与 \(\epsilon((i, j))\) 等价,使用莫比乌斯变换可以变为 \(\sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}\sum\limits_{d | (i, j)}\mu(d)\)。
然后我们交换求和顺序,变为 \(\sum\limits_{d = 1}^{n}\sum\limits_{d | i}^{n}\sum\limits_{d | j}^{m}\mu(d)\),因为 \(d | (i, j) \iff d | i, d | j\)。然后发现 \(\mu(d)\) 可以直接提出,化成:\(\sum\limits_{d = 1}^{n}\mu(d)\sum\limits_{d | i}^{n}\sum\limits_{d | j}^{m}1\),对于每个 \(d\),\(\sum\limits_{i}\sum\limits_{j}1\) 显然为 \(\lfloor\frac{n}{i}\rfloor\times \lfloor\frac{m}{i}\rfloor\)。于是就可以对 \(\mu\) 求前缀和之后对 \(\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{i}\rfloor\) 进行整除分块了。
Code
没有。
2 P2522 [HAOI 2011] Problem b](https://www.luogu.com.cn/problem/P2522)
Problem
有 \(n\) 组询问,每组询问包含五个数 \(a, b, c, d, k\),求 \(\sum\limits_{i = a}^{b}\sum\limits_{j = c}^{d}[(i, j) = k]\)。
\(1 \le n,k \le 5 \times 10^4\),\(1 \le a \le b \le 5 \times 10^4\),\(1 \le c \le d \le 5 \times 10^4\)。
\(2.5s, 250MB\)
Sol
显然可以先将平面上的矩形查询变通过容斥变为前缀矩形的贡献,不妨令 \(\text{solve}(n, m, k)\) 表示 \(\sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}[(i, j) = k]\) 的值,显然答案就是 \(\text{solve}(b, d, k) - \text{solve(a - 1, d, k)} - \text{solve}(b, c - 1, k) + \text{solve}(a - 1, c - 1, k)\)。
考虑如何求解 \(\text{solve}(n, m, k)\)(这里由于交换 \(n, m\) 后的答案不变,不妨令 \(n\le m\)):
然后由例题一可知 \(\sum\limits_{i = 1}^{n / k}\sum\limits_{j = 1}^{m / k} [(i, j) = 1] = \sum\limits_{d = 1}^{n / k}\mu(d)\cdot\lfloor\frac{n}{dk}\rfloor\cdot\lfloor\frac{m}{dk}\rfloor\),整除分块即可。
相同的题:P3455 [POI2007] ZAP-Queries。
Code
#include<bits/stdc++.h>
#define ll long long
#define sz(a) ((int) (a).size())
#define vi vector < int >
#define pb emplace_back
using namespace std;
bool vis[50010];
int cnt, p[50010], mu[50010], ms[50010];
void euler(int n) {
mu[1] = 1;
for(int i = 2; i <= n; ++i) {
if(!vis[i])
mu[i] = -1, p[++cnt] = i;
for(int j = 1; j <= cnt && i * p[j] <= n; ++j) {
mu[i * p[j]] = i % p[j] == 0 ? 0 : -mu[i];
vis[i * p[j]] = 1;
if(p[j] % i == 0)
break;
}
}
for(int i = 1; i <= n; ++i)
ms[i] = ms[i - 1] + mu[i];
}
ll work(int n, int m) {
if(n > m)
swap(n, m);
ll res = 0;
for(int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
res += (n / l) * 1ll * (m / l) * (ms[r] - ms[l - 1]);
}
return res;
}
void solve() {
int a, b, c, d, k;
cin >> a >> b >> c >> d >> k;
cout << work(b / k, d / k) - work((a - 1) / k, d / k) - work(b / k, (c - 1) / k) + work((a - 1) / k, (c - 1) / k) << "\n";
}
int main() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
euler(50000);
int T;
cin >> T;
while(T--)
solve();
return 0;
}
3 P2257 YY的GCD
Problem
给定 \(n, m\),求 \(1 \leq x \leq n\),\(1 \leq y \leq m\) 且 \(\gcd(x, y)\) 为质数的 \((x, y)\) 有多少对。
\(T = 10^4, n, m \leq 10^7\)。
Sol
不妨令 \(n\le m\)。
显然枚举 \(p\in \text{prime}\),变为 \(\sum\limits_{p\in \text{prime}}\sum\limits_{i = 1}^{n/p}\sum\limits_{j = 1}^{m / p}[(i, j) = 1]\),反演一下,变为:\(\sum\limits_{p\in \text{prime},p\le n}\sum\limits_{d = 1}^{n/p}\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor\mu(d)\)。然后发能每次枚举 \(p\) 然后数论分块的复杂度是 \(T\times \mathcal{O}(\frac{n}{\log n})\sqrt{n}\),完全不可接受。发现 \(\lfloor\frac{n}{dp}\rfloor\lfloor\frac{m}{dp}\rfloor\) 的 \(p\),然后 \(d\times p\) 其实是取满了 \([1, n]\) 的,不妨就枚举 \(k = dp\),然后再枚举 \(p\)。于是变为 \(\sum\limits_{k = 1}^{n}\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor\sum\limits_{p \in \text{prime}, p\le k}\mu(k/p)\)。这个时候就很简单了,发现原式的 \(\sum\limits_{p \in \text{prime}, p\le k}\mu(k/p)\) 其实很好预处理的,不妨把这个值记作 \(g(k)\),显然 \(g(k)\) 可以通过枚举质数 \(p\) 然后更新 \(p\) 的倍数的贡献得到,这个的复杂度和埃筛是一样的,为 \(\mathcal{O}(n\ln\ln n)\)。然后再处理出 \(g\) 的前缀和,数论分块即可做到 \(\mathcal{O}(n\ln\ln n + T\sqrt{n})\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define sz(a) ((int) (a).size())
#define vi vector < int >
#define pb emplace_back
using namespace std;
bool vis[10000010];
int cnt, p[10000010], mu[10000010], ms[10000010];
ll g[10000010];
void euler(int n) {
mu[1] = 1;
for(int i = 2; i <= n; ++i) {
if(!vis[i])
mu[i] = -1, p[++cnt] = i;
for(int j = 1; j <= cnt && i * p[j] <= n; ++j) {
mu[i * p[j]] = i % p[j] == 0 ? 0 : -mu[i];
vis[i * p[j]] = 1;
if(p[j] % i == 0)
break;
}
}
for(int i = 1; i <= cnt; ++i)
for(int j = 1; p[i] * j <= n; ++j)
g[p[i] * j] += mu[j];
for(int i = 1; i <= n; ++i)
ms[i] = ms[i - 1] + mu[i], g[i] += g[i - 1];
}
ll work(int n, int m) {
if(n > m)
swap(n, m);
ll res = 0;
for(int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
res += (n / l) * 1ll * (m / l) * (g[r] - g[l - 1]);
}
return res;
}
void solve() {
int n, m;
cin >> n >> m;
cout << work(n, m) << "\n";
}
int main() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
euler(10000000);
int T;
cin >> T;
while(T--)
solve();
return 0;
}
双倍经验:SP4491。
4 P3327 [SDOI2015] 约数个数和
Problem
设 \(d(x)\) 为 \(x\) 的约数个数,给定 \(n,m\),求 \(\sum\limits_{i = 1}^n \sum\limits_{j=1}^md(ij)\)。多测。
\(1\le T,n,m \le 50000\)。
Sol
首先这个 \(d\) 是很麻烦的,将它转化一下:\(d(ij) = \sum\limits_{a | i} \sum\limits_{b | j} [(a, b) = 1]\),证明放最后。然后考虑对式子进行化简(这里默认 \(n\le m\)):
这里的 \(S(n) = \sum\limits_{i = 1}^{n} \lfloor \frac ni \rfloor\)。
然后整除分块即可。
时间复杂度:\(\mathcal{O}((n + T)\sqrt n)\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define sz(a) ((int) (a).size())
#define vi vector < int >
#define pb emplace_back
using namespace std;
bool vis[50010];
int cnt, p[50010], mu[50010], ms[50010];
void euler(int n) {
mu[1] = 1;
for(int i = 2; i <= n; ++i) {
if(!vis[i])
p[++cnt] = i, mu[i] = -1;
for(int j = 1; j <= cnt && i * p[j] <= n; ++j) {
vis[i * p[j]] = 1;
mu[i * p[j]] = i % p[j] == 0 ? 0 : -mu[i];
if(i % p[j] == 0)
break;
}
}
for(int i = 1; i <= n; ++i)
ms[i] = ms[i - 1] + mu[i];
}
int s[50010];
ll work(int n) {
ll res = 0;
for(int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
res += (n / l) * (r - l + 1ll);
}
return res;
}
void init(int n) {
euler(n);
for(int i = 1; i <= n; ++i)
s[i] = work(i);
}
ll g(int n, int m) {
return s[n] * 1ll * s[m];
}
int n, m;
void solve() {
cin >> n >> m;
if(n > m)
swap(n, m);
ll ans = 0;
for(int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += (ms[r] - ms[l - 1]) * g(n / l, m / l);
}
cout << ans << "\n";
}
int main() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
init(50000);
int T;
cin >> T;
while(T--)
solve();
return 0;
}
/*
2
32 43
53 51
*/