莫比乌斯反演和数论函数

感觉一直没有认真学过数论,这次好好记一下。

前置知识:整除分块,筛法。

一些符号说明:

\([...]\):这个是艾弗森括号,意思是若括号内的表达式成立则为 \(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\))的值。

  1. \(k = 1\),由于 \(\sum \limits_{d | p} 1(d)\mu(\frac{p}{d}) = \epsilon(p) = 0\),所以 \(\mu(1) + \mu(p) = 0\),可以得到 \(\mu(p) = -1\)
  2. \(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\)):

\[\begin{aligned} \sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}[(i, j) = k] &= \sum\limits_{i = 1}^{n / k}\sum\limits_{j = 1}^{m / k}[(ik, jk) = k] \\ &= \sum\limits_{i = 1}^{n / k}\sum\limits_{j = 1}^{m / k} [(i, j) = 1] \\ \end{aligned} \]

然后由例题一可知 \(\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\)):

\[\begin{aligned} \sum\limits_{i = 1}^{n} \sum \limits_{j = 1}^{m} \sum\limits_{a | i} \sum\limits_{b | j} \epsilon((a, b)) &= \sum\limits_{a = 1}^{n}\sum\limits_{b = 1}^{m}\lfloor \frac na \rfloor \lfloor \frac mb \rfloor\epsilon((a, b)) \\ &= \sum\limits_{a = 1}^{n}\sum\limits_{b = 1}^{m}\lfloor \frac na \rfloor \lfloor \frac mb \rfloor \sum\limits_{d | a, d | b}\mu(d) \\ &= \sum\limits_{d = 1}^{n}\mu(d) (\sum\limits_{i = 1}^{n / d} \sum\limits_{j = 1}^{m / d} \lfloor \frac{n}{di} \rfloor \lfloor \frac{m}{dj} \rfloor) \\ &= \sum\limits_{d = 1}^{n}\mu(d) (\sum\limits_{i = 1}^{n / d} \sum\limits_{j = 1}^{m / d} \lfloor \frac{n / d}{i} \rfloor \lfloor \frac{m / d}{j} \rfloor) \\ &= \sum\limits_{d = 1}^{n}\mu(d) (\sum\limits_{i = 1}^{n / d} \lfloor \frac{n / d}{i} \rfloor) (\sum\limits_{j = 1}^{m / d} \lfloor \frac{m / d}{j} \rfloor) \\ &= \sum\limits_{d = 1}^{n} \mu(d) \cdot S(n / d) \cdot S(m / d) \end{aligned} \]

这里的 \(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
*/
posted @ 2024-02-23 10:09  Pengzt  阅读(36)  评论(0编辑  收藏  举报