莫比乌斯反演笔记

前置知识:常见积性函数

\(\varphi(n)\) 返回 \(1-n\) 中与 \(n\) 互质的数的个数。
\(id(n)\) 返回 \(n\)
\(\epsilon(n)\) 返回 \([n=1]\)
\(I(n)\) 返回 \(1\)
以及接下来讲的 \(\mu\) 函数。

莫比乌斯函数

莫比乌斯函数 \(\mu(n)\) 为积性函数,他的定义如下:
将变量 \(n\) 质因子展开为 \(p_1^{c_1}p_2^{c_2}...p_m^{c_m}\)
\(n=1\) 是,返回 \(1\)
当有 \(i\) 个质因子且 \(\forall c_i=1\) ,返回 \((-1)^{i}\)
当以上两点都不满足时,返回 \(0\)

根据定义,我们显然可以用线性筛求 1-V 的莫比乌斯函数。

Code:

点击查看代码
mu[1]=1;
for(int i=2;i<=V;i++) {
	if(!v[i]) {v[prime[++prime[0]]=i]=i; mu[i]=-1;}
	for(int j=1;j<=prime[0];j++) {
		if(v[i]<prime[j]||prime[j]>V/i) break;
		v[i*prime[j]]=prime[j];
		if(v[i]==prime[j]) break;
		else mu[i*prime[j]]=-mu[i];
	}
}

莫比乌斯函数性质:

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

\(x\) 质因子展开后可以简略证明。

莫比乌斯反演

其实莫比乌斯反演就是处理一个求和:

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

根据莫比乌斯函数性质,可以做出如下变换:
\(\ \ \ \sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=1]\)

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

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

然后我们去枚举 \(d\) ,计算 \(\mu(d)\) 计算了几次。

\(=\sum_{d=1}^{min(n,m)}\mu(d) \times \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor\)

这个式子的意思是,对于一个 \(d\) ,他在 \(1-n\) 中的约数有 \(\lfloor \frac{n}{d} \rfloor\) 个,\(m\) 同理。
然后我们就可以 \(O(V)\) 预处理 \(\mu\) 函数前缀和后用整除分块单次 \(O(\sqrt{n}+\sqrt{m})\) 计算求和。

例题

ZAP-Queries

题目要求

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

其中 \(n, m, d\) 由题目给出,多测。

题解

我们显然可以给所有数除 \(d\) ,因为假如数对 \(\gcd(i,j)=1\) ,将 \(i,j\) 同时乘 \(d\) 便是合法数对。
则要求变为

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

我们发现这与上面给出的基本模型一致,用相同的方法拆式子求即可。
复杂度 \(O(V+T(\sqrt{n}+\sqrt{m}))\)

Code:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int V=1e7;
int v[V+10], prime[V+10], mu[V+10], s[V+10];
void init() {
	mu[1]=1;
	for(int i=2;i<=V;i++) {
		if(!v[i]) {v[i]=i; prime[++prime[0]]=i; mu[i]=-1;}
		for(int j=1;j<=prime[0];j++) {
			if(prime[j]>V/i) break;
			v[i*prime[j]]=prime[j];
			if(v[i]==prime[j]) break;
			else mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<=V;i++) s[i]=s[i-1]+mu[i];
}
int n, m, d, ans;
signed main(){
//	freopn("P3455.in", "r", stdin);
//	freopn("P3455.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	init(); int T; cin>>T;
	while(T--) {
		cin>>n>>m>>d; ans=0;
		for(int i=1;i*d<=min(n, m);i++) {
			int j=min(n/(n/i), m/(m/i));
			ans+=(n/i/d)*(m/i/d)*(s[j]-s[i-1]);
			i=j;
		} cout<<ans<<'\n';
	}
	return 0;
}

YY的GCD

以下设质数集合为 \(P\)
题目要求

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

多测。

题解

我们发现右边部分遇上题一致,于是先化右边式子。

\(\sum_{k \in P}\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor} \mu(d) \times \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor\)

但枚举 \(k\)\(\log\) 级别的,无法负担。

于是考虑枚举 \(kd\) ,记为 \(T\)

\(\sum_{T=1}^{n} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \times (\sum_{d|T}\mu(d)[\frac{T}{d} \in P])\)

我们把右边括号中的式子记为 \(f(x)\)
我们发现对于 \(f(1)-f(V)\) 由于只与质数有关,所以可以用埃氏筛求。
复杂度 \(O(V\log\log V+T(\sqrt{n}+\sqrt{m}))\)

Code:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int V=1e7;
int v[V+10], prime[V+10], mu[V+10], s[V+10];
void init() {
	mu[1]=1;
	for(int i=2;i<=V;i++) {
		if(!v[i]) {v[i]=i; prime[++prime[0]]=i; mu[i]=-1;}
		for(int j=1;j<=prime[0];j++) {
			if(v[i]<prime[j]||prime[j]>V/i) break;
			v[prime[j]*i]=prime[j];
			if(v[i]==prime[j]) break;
			else mu[prime[j]*i]=-mu[i];
		}
	}
	for(int j=1;j<=prime[0];j++)
		for(int i=1;prime[j]<=V/i;i++)
			s[prime[j]*i]+=mu[i];
	for(int i=1;i<=V;i++) s[i]+=s[i-1];
}
int ans, n, m;
signed main(){
//	freopn("P2257.in", "r", stdin);
//	freopn("P2257.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	init();
	int T; cin>>T;
	while(T--) {
		cin>>n>>m; ans=0;
		for(int i=1;i<=min(n, m);i++) {
			int j=min(n/(n/i), m/(m/i));
			ans+=(n/i)*(m/i)*(s[j]-s[i-1]);
			i=j;
		} cout<<ans<<'\n';
	}
	return 0;
}

接下来的题目比较困难


posted @ 2025-08-30 15:30  Jiaminyo  阅读(14)  评论(0)    收藏  举报