莫比乌斯反演学习笔记

莫比乌斯函数

定义

\[\mu(n) = \begin{cases} 1 & n = 1 \\ 0 & n\text{ 含有平方因子} \\ (-1)^k & \text{其中 }k\text{ 为 }n\text{ 本质不同的质因子个数} \end{cases} \]

性质

莫比乌斯函数是积性函数。

\[\sum\limits_{d \mid n} \mu(d) = \varepsilon(n) \\ \mu * 1 = \varepsilon \]

其中 \(\varepsilon(n) = [n = 1], 1(n) = 1\)\(*\) 表示狄利克雷卷积。

证明:

\(n = \sum\limits_{i=1}^k {p_i}^{e_i}, n' = \sum\limits_{i=1}^k p_i\)

则有

\[\sum\limits_{d \mid n} \mu(d) = \sum\limits_{d \mid n'} \mu(d) = \sum\limits_{i=0}^k \binom{k}{i} \cdot (-1)^i = \sum\limits_{i=0}^k \binom{k}{i} \cdot (-1)^i \cdot 1^{k-i} = (-1 + 1)^k = [n = 1] \]

补充结论:

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

显然。

线性求莫比乌斯函数

由于莫比乌斯函数是积性函数,故可以用线性筛求。

具体实现如下:

mu[1]=1;
for (int i=2;i<=n;i++) {
    if (!f[i]) prime[++cnt]=i,mu[i]=-1;
    for (int j=1;j<=cnt&&prime[j]*i<=n;j++) {
        f[prime[j]*i]=1;
        if (i%prime[j]==0) {
            mu[prime[j]*i]=0;
            break;
        } else mu[prime[j]*i]=-mu[i];
    }
}

莫比乌斯反演

\(f(n), g(n)\) 为两个数论函数。

形式一:

\[f(n) = \sum\limits_{d \mid n} g(d) \Rightarrow g(n) = \sum\limits_{d \mid n} \mu(d) f(\dfrac{n}{d}) \]

这种形式下, \(f(n)\) 被称为 \(g(n)\) 的莫比乌斯变换,\(g(n)\) 被称为 \(f(n)\) 的莫比乌斯反演。

容易发现当 \(f(n) = \varepsilon(n), g(n) = \mu(n)\) 时满足这个结论。

形式二:

\[f(n) = \sum\limits_{n \mid d} g(d) \Rightarrow g(n) = \sum\limits_{n \mid d} \mu(\dfrac{d}{n}) f(d) \]

例题

洛谷P2522 [HAOI2011]Problem b

Link

每个区间左右端点都 \(\div k\),然后就变成了求 \(\gcd(x, y) = 1\)

然后是经典 Trick 拆成四个类似的式子,然后推导以下:

\[\sum\limits_{x=1}^n \sum\limits_{y=1}^m [\gcd(x, y) = 1] \\ = \sum\limits_{x=1}^n \sum\limits_{y=1}^m \sum\limits_{d \mid \gcd(x,y)} \mu(d) \\ = \sum\limits_{d=1}^{\min(n,m)} \mu(d) \sum\limits_{x=1}^n [d \mid x] \sum\limits_{y=1}^m [d \mid y] \\ = \sum\limits_{d=1}^{\min(n,m)} \mu(d) \lfloor\dfrac{n}{d}\rfloor \lfloor\dfrac{m}{d}\rfloor \]

然后数论分块优化一下即可。

// Think twice,code once.
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N=50000;

int T,cnt,prime[50005],mu[50005];
bool f[50005];

int calc(int n,int m) {
	int res=0;
	for (int l=1,r;l<=min(n,m);l=r+1) {
		r=min(n/(n/l),m/(m/l));
		res+=(mu[r]-mu[l-1])*(n/l)*(m/l);
	}
	return res;
}

int main() {
	mu[1]=1;
	for (int i=2;i<=N;i++) {
		if (!f[i]) prime[++cnt]=i,mu[i]=-1;
		for (int j=1;j<=cnt&&prime[j]*i<=N;j++) {
			f[prime[j]*i]=1;
			mu[prime[j]*i]=-mu[i];
			if (i%prime[j]==0) {mu[prime[j]*i]=0;break;}
		}
	}
	for (int i=2;i<=N;i++) mu[i]+=mu[i-1];
	scanf("%d",&T);
	while (T--) {
		int l1,r1,l2,r2,k;
		scanf("%d%d%d%d%d",&l1,&r1,&l2,&r2,&k);
		l1=(l1+k-1)/k;
		r1=r1/k;
		l2=(l2+k-1)/k;
		r2=r2/k;
		if (l1>r1||l2>r2) {puts("0");continue;}
		printf("%d\n",calc(r1,r2)-calc(l1-1,r2)-calc(r1,l2-1)+calc(l1-1,l2-1));
	}
	return 0;
}

洛谷P2257 YY的GCD

Link

与上一道题类似,可以简单地写出式子

\[\sum\limits_{p \in \mathbb{P}} \sum\limits_{x=1}^n \sum\limits_{y=1}^m [\gcd(x,y) = p] \\ = \sum\limits_{p \in \mathbb{P}} \sum\limits_{d=1}^{\lfloor\frac{\min(n, m)}{p}\rfloor} \mu(d) \lfloor\dfrac{n}{pd}\rfloor \lfloor\dfrac{m}{pd}\rfloor \]

然而这样做复杂度是 \(O(T \dfrac{n \sqrt{n}}{\ln n})\) 的,显然过不去。

考虑进一步优化:设 \(k = pd\),则:

\[\sum\limits_{p \in \mathbb{P}} \sum\limits_{d=1}^{\lfloor\frac{\min(n, m)}{p}\rfloor} \mu(d) \lfloor\dfrac{n}{pd}\rfloor \lfloor\dfrac{m}{pd}\rfloor \\ = \sum\limits_{p \in \mathbb{P}} \sum\limits_{d=1}^{\lfloor\frac{\min(n, m)}{p}\rfloor} \mu(d) \lfloor\dfrac{n}{k}\rfloor \lfloor\dfrac{m}{k}\rfloor \\ = \sum\limits_{k=1}^{\min(n,m)} \lfloor\dfrac{n}{k}\rfloor \lfloor\dfrac{m}{k}\rfloor \sum\limits_{p \mid k, p \in \mathbb{P}} \mu(\dfrac{k}{p}) \]

然后后面这个式子可以预处理,于是复杂度就降到了 \(O(T \sqrt{n})\)

// Think twice, code once.
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N=1e7;

int T,n,m;
int cnt,prime[10000005],mu[10000005];
int f[10000005];
long long sum[10000005];

int main() {
	mu[1]=1;
	f[1]=1;
	for (int i=2;i<=N;i++) {
		if (!f[i]) prime[++cnt]=i,mu[i]=-1;
		for (int j=1;j<=cnt&&(long long)prime[j]*i<=N;j++) {
			f[prime[j]*i]=1;
			if (i%prime[j]==0) {
				mu[prime[j]*i]=0;
				break;
			} else mu[prime[j]*i]=-mu[i];
		}
	}
	for (int i=1;i<=cnt;i++)
		for (int j=prime[i];j<=N;j+=prime[i])
			sum[j]+=mu[j/prime[i]];
	for (int i=1;i<=N;i++) sum[i]+=sum[i-1];
	scanf("%d",&T);
	while (T--) {
		scanf("%d%d",&n,&m);
		long long ans=0;
		for (int l=1,r;l<=min(n,m);l=r+1) {
			r=min(n/(n/l),m/(m/l));
			ans+=(sum[r]-sum[l-1])*(n/l)*(m/l);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

洛谷P3327 [SDOI2015]约数个数和

Link

反演来咯~

首先一个性质:

\[d(nm) = \sum\limits_{x \mid n} \sum\limits_{y \mid m} [\gcd(x, y) = 1] \]

具体证明见 \(\color{black}{\text{S}}\color{red}{\text{iyuan}}\)博客

于是:

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

到这里就可以用莫比乌斯函数的性质把 \([\gcd(x, y) = 1]\) 给拆掉了,但是这里介绍一种用反演的做法:

\[\sum\limits_{i = 1}^n \sum\limits_{j = 1}^m \sum\limits_{x \mid i} \sum\limits_{y \mid j} [\gcd(x, y) = 1] = \sum\limits_{x = 1}^n \sum\limits_{y = 1}^m \left\lfloor\dfrac{n}{x}\right\rfloor \left\lfloor\dfrac{m}{y}\right\rfloor [\gcd(x, y) = 1] \]

先把 \(x, y\) 替换成 \(i, j\),然后定义:

\[f(x) = \sum\limits_{i = 1}^n \sum\limits_{j = 1}^m \left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{j}\right\rfloor [\gcd(i, j) = x] \\ g(x) = \sum\limits_{x \mid d} f(d) \]

则:

\[g(x) = \sum\limits_{i = 1}^n \sum\limits_{j = 1}^m \left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{j}\right\rfloor [x \mid \gcd(i, j)] \\ = \sum\limits_{i = 1}^\left\lfloor\frac{n}{x}\right\rfloor \sum\limits_{j = 1}^\left\lfloor\frac{m}{x}\right\rfloor \left\lfloor\dfrac{\left\lfloor\frac{n}{x}\right\rfloor}{i}\right\rfloor \left\lfloor\dfrac{\left\lfloor\frac{m}{x}\right\rfloor}{j}\right\rfloor \\ = \sum\limits_{i = 1}^\left\lfloor\frac{n}{x}\right\rfloor \left\lfloor\dfrac{\left\lfloor\frac{n}{x}\right\rfloor}{i}\right\rfloor \cdot \sum\limits_{j = 1}^\left\lfloor\frac{m}{x}\right\rfloor \left\lfloor\dfrac{\left\lfloor\frac{m}{x}\right\rfloor}{j}\right\rfloor \]

设:

\[s(x) = \sum\limits_{i = 1}^x \left\lfloor\dfrac{x}{i}\right\rfloor \]

则:

\[g(x) = s(\left\lfloor\dfrac{n}{x}\right\rfloor) s(\left\lfloor\dfrac{m}{x}\right\rfloor) \]

易得答案为 \(f(1)\)。根据莫比乌斯反演形式二:

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

故:

\[f(1) = \sum\limits_{1 \mid d} \mu(\dfrac{d}{1}) g(d) = \sum\limits_{d = 1}^\infty \mu(d) g(d) \]

发现当 \(d > \min(n, m)\) 时没有贡献,故:

\[f(1) = \sum\limits_{i = 1}^\min(n, m) \mu(i) g(i) = \sum\limits_{i = 1}^\min(n, m) \mu(i) s(\left\lfloor\dfrac{n}{i}\right\rfloor) s(\left\lfloor\dfrac{m}{i}\right\rfloor) \]

\(s\) 和答案都可以使用数论分块,时间复杂度 \(O(T \sqrt{n})\)

// Think twice, code once.
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int T,n,m;
int cnt,prime[50005],mu[50005];
int f[50005];
long long sum[50005];

int main() {
	mu[1]=1;
	for (int i=2;i<=50000;i++) {
		if (!f[i]) prime[++cnt]=i,mu[i]=-1;
		for (int j=1;j<=cnt&&prime[j]*i<=50000;j++) {
			f[prime[j]*i]=1;
			if (i%prime[j]==0) {mu[prime[j]*i]=0;break;}
			else mu[prime[j]*i]=-mu[i];
		}
	}
	for (int i=2;i<=50000;i++) mu[i]+=mu[i-1];
	for (int i=1;i<=50000;i++)
		for (int l=1,r;l<=i;l=r+1) {
			r=i/(i/l);
			sum[i]+=(long long)(r-l+1)*(i/l);
		}
	scanf("%d",&T);
	while (T--) {
		scanf("%d%d",&n,&m);
		if (n>m) swap(n,m);
		long long ans=0;
		for (int l=1,r;l<=n;l=r+1) {
			r=min(n/(n/l),m/(m/l));
			ans+=(mu[r]-mu[l-1])*sum[n/l]*sum[m/l];
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2023-01-12 20:18  Mine_King  阅读(54)  评论(0编辑  收藏  举报