【学习笔记】莫比乌斯反演

这东西是真的神奇,但是稍微熟悉了之后就感觉没啥了

莫比乌斯函数

定义

我们通常记 \(\mu(x)\) 为莫比乌斯函数

\[\mu(x) = \left\{ \begin{aligned} &1 &&x=1 \\ &0 &&x 含有平方因子 \\ &(-1)^k &&k为本质不同的 x 的质因子的数量 \\ \end{aligned} \right. \]

我们先不用考虑这东西可以干什么,反正就是有这么个东西,而且它还有非常好的性质

性质

性质一

莫比乌斯函数为积性函数,即满足 \(\mu(n) = \mu(p^k)\mu(\frac{n}{p^k})\)

性质二

由莫比乌斯函数定义可以发现,对于 \(\mu(p^k)\),当 \(k=1\) 时为 \(-1\),其余情况均为 \(0\)

性质三

\[\sum_{d|n} \mu(d) = \left\{ \begin{aligned} &1 &&n = 1 \\ &0 &&n > 1\\ \end{aligned} \right. \]

换一种表达方式就是:

\[\sum_{d|n}\mu(d) = [n=1] \]

性质四

\[\varphi(n) = \sum_{d|n} \mu(d)\dfrac{n}{d} \]

莫比乌斯反演

基本公式:

若存在两个函数 \(F(n),f(n)\) 使得

\[F(n) = \sum_{d|n} f(n) \]

那么就有

\[f(n) = \sum_{d|n} \mu(d)F(\frac{n}{d}) \]

例题

例题一

问题描述:

给定 \(n,m\),求下列式子的值。

\[\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j) = 1] \]

详细分析:

一般来说,我们看到这种题都会考虑将后面这个神奇的条件转化为莫比乌斯函数的相关内容。
我们的莫比乌斯函数有这样的一条性质:\(\sum_{d|n}\mu(n) = [n = 1]\) 是不是跟这个长得非常像,所以就套进去嘛

\[\sum_{i=1}^n\sum_{j=1}^m\sum_{d|gcd(i,j)}\mu(d) \]

然后就继续进行化简

\[\begin{aligned} & = \sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}\mu(d) \\ & = \sum_{d=1}^{\min(n,m)}\sum_{i=1}^n [d|i] \sum_{j=1}^m [d|j] \ \mu(d) \\ & = \sum_{d=1}^{\min(n,m)} \lfloor \dfrac{n}{d} \rfloor \times \lfloor \dfrac{m}{d} \rfloor \times \mu(d) \end{aligned} \]

我们会发现在我们枚举上面这个式子的过程中,\(\lfloor \dfrac{n}{d} \rfloor\)\(\lfloor \dfrac{m}{d} \rfloor\) 的值,一定是在某些长度的段上都是不变的,那么对于这些段的答案很显然不用一一枚举,而只需要预处理出 \(\mu(d)\) 的前缀和,然后直接按段处理就好了。这也被称为数论分块,可以将复杂度优化为 \(O(T\sqrt n)\)\(T\) 为询问数量

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long N = 1e5;
long long miu[N + 5],prime[N + 5],tot,sum[N + 5];
bool no_prime[N + 5];
void init(){
	miu[1] = 1;
	no_prime[0] = no_prime[1] = true;
	for(long long i=2; i<=N; i++){
		if(!no_prime[i]){
			miu[i] = -1;
			prime[++tot] = i;
		}
		for(long long j=1; j<=tot && i * prime[j] <= N; j++){
			no_prime[i * prime[j]] = true;
			if(i % prime[j] == 0){	mu[i * prime[j]] = 0; break;}  //注意线性筛的 break 的条件 
			else	miu[i * prime[j]] = miu[i] * -1;
			//miu 是个积性函数,所以这相当于 miu[i] * miu[prime[j]] 因为 prime[j] 为负数,所以 miu[prime[j]] = -1 
		}
	}
	for(long long i=1; i<=N; i++){
		sum[i] = sum[i-1] + miu[i];
	}
}
int main(){
	init();
	long long t;
	scanf("%lld",&t);
	while(t--){
		long long x,y;
		scanf("%lld%lld",&x,&y);
		long long ans=0;
		for(long long i = 1; i <= x && i <= y;){
			long long nxt = min(x / (x / i),y / (y / i));
			ans += (sum[nxt] - sum[i-1]) * (x/i) * (y/i);
			// sum 的加减是在求 sum(miu[d]) 在这一段的上 a/i 和 b/i 都相等所以可以认为是常数,可以直接提出来
			i = nxt + 1;  //nxt 一定不要忘了加一 
		}
		printf("%lld\n",ans);
	}
	return 0;
}

我们就以此来看看,如何快速处理出莫比乌斯函数的值,也就是同欧拉函数一样使用线性筛。
首先若 \(n\) 为质数,那么 \(\mu(n) = -1\),这是因为它只含有一个质因子
那么对于下面这个条件:
if(i % prime[j] == 0)
也就意味着,对于我们筛去的这个数 \(i \times prime_j\) 来说,它含有平方因子,也就是它肯定含有 \(prime_j ^ 2\) 这个因子,所以这个数的莫比乌斯函数的值为 \(0\)
然后对于这个条件
else
则意味着 \(prime_j\) 是在 \(i \times prime_j\) 里的一个质因子,且不含有平方因子,那么根据莫比乌斯函数的积性求解就好了

posted @ 2022-05-19 19:29  linyihdfj  阅读(66)  评论(0编辑  收藏  举报