『学习笔记』莫比乌斯反演

定义&证明

前置知识(定义)

  • \([sth]=1/0\) ,当 \(sth\) 为真时即 \(1\) ,否则为 \(0\)
  • \(I(n)=1\) ,恒等函数,是完全积性函数
  • \(e(n)=[n=1]\) ,元函数,是完全积性函数
  • \(id(n)=n\) ,单位函数,是完全积性函数
  • \(\varphi(n)=\sum\limits_{i=1}^n[gcd(i,n)=1]\) ,欧拉函数,是积性函数
  • \(\mu(n)\) ,莫比乌斯函数,是积性函数
  • 两个函数的狄利克雷卷积写作 \(f*g\)\((f*g)(n)=\sum\limits_{d\mid n}f(d)g(n/d)\)

莫比乌斯函数的定义

假设有一个函数 \(F(x)=\sum\limits_{d\mid x}f(d)\) ,这个 \(F\) 很好求,但是怎么求 \(f\) 呢?

这就需要我们伟大的莫比乌斯反演了

很容易看出来上面这个式子就是个狄利克雷卷积的形式,即 \(F=f*I\)

于是我们把这个 \(I\) 放到左式,变成 \(F*I^{-1}=f\)\(I^{-1}\) 叫做 \(I\) 在狄利克雷卷积意义下的逆,一个函数的逆和这个函数的狄利克雷卷积为元函数 \(e\)

注意: \(I(x)=1\)\(I\) 是恒等函数的意思
于是我们的莫比乌斯函数诞生了

\[\mu=I^{-1} \]

莫比乌斯函数推导

然后我们易知 \(\mu\) 是个积性函数,因为 \(I\) 也是个积性函数

我们开始研究 \(\mu(p^k),p\in \mathbb{P},k\in \mathbb{N}\) 的情况

  • \(k=0\) 时,显然 \(\mu(1)=1\)
  • \(k=1\) 时,显然 \(\mu(p)=-1\) (由 \(\sum\limits_{d\mid p}\mu(d)I(\frac{p}{d})=e(p)\) 可得,显然 \(d\) 只能取 \(1,p\)
  • \(k\ge 2\) 时,\(\sum\limits_{d\mid p^k}\mu(d)I(\frac{p^k}{d})=e(p^k)=0\),所有的 \(I\) 都为 \(1\) ,即 \(\sum\limits_{d\mid p^k}\mu(d)=\sum\limits_{t=0}^{k}p^t=0\) ,考虑 \(k=2\)\(\mu(p^2)=0\)\(k=3,4,...\)\(\mu(p^k)\) 仍然为 \(0\)

于是我们 \(\mu\) 的终极形态要出来了:\(\mu(n=\prod\limits_{i=1}^{m}p_i^{k_i})\) 呢?
由于 \(\mu\) 是积性函数,所以有:

\[\mu(n=\prod\limits_{i=1}^{m}p_i^{k_i})=\prod\limits_{i=1}^{m}\mu(p_i^{k_i}) \]

显然 \(k_i\ge 2\) 的时候 \(\mu(p_i^{k_i})=0\)

于是当且仅当 \(k_i=1\) 的时候它才不为 \(0\),所以答案即为 \(\mu(n)=\prod\limits_{i=1}^m\mu(p_i)=(-1)^m\)

于是我们 \(\mu\) 的定义便浮出水面:

\[\mu(n)=\begin{cases}0\quad \quad \quad \exists p\in \mathbb{P}, p^2\mid n\\(-1)^m\quad otherwise\end{cases} \]

\(m\)\(n\) 质因子种类数

莫比乌斯反演式

嵌入式

\(\mu\) 的定义得知 \(\mu * I=e\)

\[\therefore\sum\limits_{d\mid n}\mu(n)=e(n)=[n=1] \]

\[\because[n\mid m][\frac{n}{m}=1]=[n=m] \]

\[\therefore[n\mid m]\sum\limits_{d\mid (n/m)}\mu(d)=[n=m] \]

这个式子就很香,这叫嵌入式莫比乌斯反演

约数式

回到开头那个式子?

\[F(n)=\sum\limits_{d\mid n}f(n) \]

\[\therefore \sum\limits_{d\mid n}F(n)\mu(n/d)=f(n) \]

是不是很爽?

似乎还有一种倍数式,但我不会

线性筛莫比乌斯函数

这个太简单了,不讲(

void mobius() {
	mu[1] = 1;
	for (int i = 2; i <= maxn; i++) {
		if (!v[i]) p[++tot] = i, mu[i] = -1;
		for (int j = 1; j <= tot && i <= maxn / p[j]; j++) {
			v[i * p[j]] = 1;
			if (i % p[j] == 0) {
				mu[i * p[j]] = 0;
				break;
			}
			mu[i * p[j]] = -mu[i];
		}
	}
}

整除分块

不会的就去看 command_block 的博客

例题

简单题

  • \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=1]\) (以下 \(gcd(i,j)\) 均用 \((i,j)\) 表示)

考虑嵌入式莫比乌斯反演, \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[(i,j)=1]=\sum\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{d\mid (i,j)}\mu(d)=\sum\limits_{d=1}^{n}\mu(d)\sum\limits_{d\mid i}^{n}\sum\limits_{d\mid j}^{n}1\)

然后这个 \(\sum\limits_{d=1}^{n}\mu(d)\sum\limits_{d\mid i}^{n}\sum\limits_{d\mid j}^{n}1\) 显然是\(=\sum\limits_{d=1}^{n}\mu(d)(\sum\limits_{d\mid i}^{n}1)(\sum\limits_{d\mid j}^{n}1)=\sum\limits_{d=1}^{n}\mu(d)(n/d)(m/d)\)

于是就可以用整除分块+前缀和 \(O(\sqrt{n})\) 解决

int calc(int n) {
	int l = 1, r = 0, res = 0;
	while (l <= n) {
		r = n / (n / l);
		res += (n / l) * (n / l) * (sum[r] - sum[l - 1]);
		l = r + 1;
	}
	return res;
}

「POI2007」ZAP-Queries

  • \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m[(i,j)=k]\)

\(k\) 除过去:

\[\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}[(i,j)=1] \]

然后...就做完了

「NOI2010」能量采集

  • 不难看出这个式子就是 \(2\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}(i,j)-nm\)

于是我们考虑这个式子 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}(i,j)\)

\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}(i,j)=\sum\limits_{k=1}^nk\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[(i,j)=k]\) ,于是此时复杂度就到 \(O(n\sqrt{n})\) ,可以通过本题

不是吧,NOI就这

「SDOI2015」约数个数和

  • \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}d(ij)\)

我们发现一件事:

\[d(ij)=\sum\limits_{x\mid i}\sum\limits_{y\mid j}[(x,y)=1] \]

\[\therefore ans=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{x\mid i}\sum\limits_{y\mid j}[(x,y)=1] \]

改变一下求和顺序

\[ans=\sum\limits_{i=1}^n\sum\limits_{j=1}^m(n/i)(m/j)[(i,j)=1] \]

于是我们可以设:

\[f(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m(n/i)(m/j)[(i,j)=x] \]

\[g(x)=\sum\limits_{x\mid d}f(d) \]

\[g(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^m(n/i)(m/j)[x\mid (i,j)]=\sum\limits_{i=1}^{n/x}\sum\limits_{j=1}^{m/x}(n/ix)(m/jx) \]

\[\because f(x)=\sum\limits_{x\mid d}\mu(d)g(d/n) \]

\[\therefore ans=f(1)=\sum\limits_{i=1}^n\mu(i)g(i) \]

由于 \(g(x)\) 可以通过整除分块预处理出,总复杂度为 \(O(t\sqrt{n})\)

「MCOI-02」Convex Hull

\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\tau(i)\tau(j)\tau(\gcd(i,j))\)

根据套路把 \(\gcd\) 提出来枚举:

\[\begin{aligned}ans&=\sum\limits_{d=1}^{n}\tau(d)\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\tau(i)\tau(j)[\gcd(i,j)=d]\\&=\sum\limits_{d=1}^{n}\tau (d)\sum\limits_{i=1}^{n/d}\sum\limits_{j=1}^{m/d}\tau(id)\tau(jd)[\gcd(i,j)=1]\end{aligned} \]

\[\because [\gcd(i,j)=1]=\sum\limits_{k\mid (i,j)}\mu(k)=\sum\limits_{k\mid i,k\mid j}\mu(k) \]

\[\begin{aligned}\therefore ans&=\sum\limits_{d=1}^{n}\tau(d)\sum\limits_{i=1}^{n/d}\sum\limits_{j=1}^{m/d}\tau(id)\tau(jd)\sum\limits_{k\mid i,k\mid j}\mu(k)\\&=\sum\limits_{d=1}^{n}\tau(d)\sum\limits_{k=1}^{n/d}\mu(k)\sum\limits_{i=1}^{n/dk}\tau(idk)\sum\limits_{j=1}^{m/dk}\tau(jdk)\end{aligned} \]

(上一步是把这个 \(k\) 放到前面枚举了。)

然后大力设 \(t=dk\),那么 \(k=\frac{t}{d}\),我们去枚举这个 \(t\)

\[\begin{aligned}\therefore ans&=\sum\limits_{d=1}^{n}\tau(d)\sum\limits_{d\mid t}^{n}\mu(\frac{t}{d})\sum\limits_{i=1}^{n/t}\sum\limits_{j=1}^{m/t}\tau(it)\tau(jt)\\&=\sum\limits_{t=1}^{n}\sum\limits_{d\mid t}^{n}\tau(d)\mu(\frac{t}{d})\sum\limits_{i=1}^{n/t}\sum\limits_{j=1}^{m/t}\tau(it)\tau(jt)\end{aligned} \]

(上一步是把 \(t\) 丢到式子的最前面了。)

然后呢?我们发现 \(\sum\limits_{d\mid t}^{n}\tau(d)\mu(\frac{t}{d})\) 是一个狄利克雷卷积的形式,并且 \(\tau * \mu=I\) ,也就是这个式子恒等于 \(1\)

为啥?

因为由 \(\tau\) 的定义可得 \(\tau=I*I\) ,由于 \(\mu=I^{-1}\) ,所以把右式的 \(I\) 搞一个过去,就变成了 \(\tau*\mu=I\)

\[\therefore ans=\sum\limits_{t=1}^{n}\sum\limits_{i=1}^{n/t}\sum\limits_{j=1}^{m/t}\tau(it)\tau(jt) \]

这个 \(\tau\) 可以用线性筛在 \(O(n)\) 的复杂度内预处理出来,然后记录 \(\tau(it)\)\(\tau(jt)\) 分别对每一个 \(t\) 的贡献,最后枚举这个 \(t\) 的时候乘起来就行了。

跑起来慢的要死,可能要多提交几遍。听说可以优化,但我不会 \(\mathtt{dirichlet}\) 前缀和啊。

#include <bits/stdc++.h>
#define int long long
namespace mystd {
    inline int read() {
        int w = 1, q = 0;
        char ch = ' ';
        while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
        if (ch == '-') w = -1, ch = getchar();
        while (ch >= '0' && ch <= '9') q = q * 10 + ch - '0', ch = getchar();
        return w * q;
    }
    inline void write(int x) {
        if (x < 0) {
            x = ~(x - 1);
            putchar('-');
        }
        if (x > 9) write(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace std;
using namespace mystd;

const int maxn = 2e6 + 200;
int n, m, p, ans, s1[maxn], s2[maxn];
int f[maxn], d[maxn], vis[maxn], pr[maxn], tot;

void init() {
	d[1] = f[1] = vis[1] = 1;
	for (int i = 2; i <= maxn - 1; i++) {
		if (!vis[i]) {
			pr[++tot] = i;
			f[i] = 1;
			d[i] = 2;
		}
		for (int j = 1; j <= tot && i <= maxn / pr[j]; j++) {
			vis[i * pr[j]] = 1;
			if (i % pr[j] == 0) {
				f[i * pr[j]] = f[i] + 1;
				d[i * pr[j]] = d[i] * (f[i * pr[j]] + 1) / (f[i] + 1);
				break;
			}
			f[i * pr[j]] = 1;
			d[i * pr[j]] = d[i] * d[pr[j]];
		}
	}
}

signed main() {
    init();
    n = read();
    m = read();
    p = read();
    if (n > m) swap(n, m);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n / i; j++) {
            s1[i] = (s1[i] + d[i * j]) % p;
        }
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= m / i; j++) {
            s2[i] = (s2[i] + d[i * j]) % p;
        }
    }
    for (int i = 1; i <= n; i++) {
        ans = (ans + s1[i] * s2[i] % p) % p;
    }
    write(ans);
    return 0;
}

updated 2022/2/5 : 尽管我现在会了狄利克雷前缀和,我还是不会优化/doge

posted @ 2022-01-09 14:52  Ender_32k  阅读(117)  评论(0编辑  收藏  举报