莫比乌斯函数学习笔记

莫比乌斯函数学习笔记

前言

注意到我并没有将 "莫比乌斯反演" 作为文章的题目,主要原因是莫比乌斯反演可以解决的题目用莫比乌斯函数通常便可以解决。莫比乌斯函数的前置知识有且仅有数论分块。

数论分块

引理 1

a,b,cZ,abc=abc

证明:设 ab=ab+r(0r<1),那么

abc=ab1c=abc+rc=abc

引理 2

nN+,|{nddN+,dn}|2n

证明:

对于 dnndn 种取值;

对于 d>nndn,也只有 n 种取值。

结论

对于常数 n,使得式子 ni=nj 成立且有 ijnj 值最大为 nni,这个值也就是 ni 所在块的右端点。

应用

快速计算 i=1nf(i)g(ni) 的值。

我们从 l=1 开始枚举,每次考虑区间 [l,nni] 的贡献,即 g(nl)ilnnif(i) 的贡献。f 的贡献是容易的,g 的贡献可以直接计算。算完后令 l=nni+1,作为下一个枚举区间的左端点。那么时间复杂度显然是 O(n)

代码:

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

推广

若出现 a1i,a2i,,ani 的多个整除式,要在每个区间的右端点中取最小值。

莫比乌斯函数

μ 为莫比乌斯函数,定义为:

μ(n)={1n=10n 含有平方因子(1)kk 为 n 的本质不同质因子个数

需要知道的是 μ 有以下性质:

性质 1

μ 是积性函数,可以直接用欧拉筛求解。

性质 2

d|nμ(d)={1n=10n1

证明:设 n=i=0kpici,n=i=1kpi,那么 d|nμ(d)=d|nμ(d)=i=0k(ki)(1)i=(1+(1))k

由二项式定理,该式子的值只有在 k=0,即 n=1 时为 1,否则为 0

性质 3

[gcd(i,j)=1]d|gcd(i,j)μ(d)

这个结论比较重要,由 性质 2 是容易得出的。

那么其实莫比乌斯函数的部分就讲完了,让我们看几道例题。

例题

例 1:YY的GCD:求 i=1nj=1m[gcd(i,j)P]P 为全体质数构成的集合。

不妨令 n<m

i=1nj=1m[gcd(i,j)P]=i=1nj=1mpP[gcd(ip,jp)=1]=pPi=1npj=1mp[gcd(i,j)=1]=pPi=1npj=1mpd|gcd(i,j)μ(d)=pPi=1npj=1mpd=1npμ(d)[d|i][d|j]=pPd=1npμ(d)i=1np[d|i]j=1mp[d|j]=pPd=1npμ(d)npdmpd=pPT=1npμ(Tp)nTmT=T=1nnTmTpPp|Tμ(Tp)

那么数论分块求解即可。

代码:

#include <bits/stdc++.h>
#define int long long
#define N 10000005
using namespace std;
vector<int>pri;
bool prm[N];
int mu[N];
int sm[N];
void pme(int n) {
	mu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!prm[i]) pri.push_back(i), mu[i] = -1;
		for (auto j : pri) {
			if (i * j > n) break;
			prm[i * j] = 1;
			if (i % j == 0) {
				mu[i * j] = 0;
				break;
			}
			mu[i * j] = -mu[i];
		}
	}
	for (auto i : pri)
		for (int j = 1; j <= n / i; j++) sm[i * j] += mu[j];
	for (int i = 1; i <= n; i++) sm[i] += sm[i - 1];
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	pme(1e7);
	int T;
	cin >> T;
	while (T--) {
		int n, m;
		cin >> n >> m;
		if (n > m) swap(n, m);
		int l, r, ans = 0;
		for (l = 1; l <= n; l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			ans += (sm[r] - sm[l - 1]) * (n / l) * (m / l);
		}
		cout << ans << '\n';
	}
	return 0;
}

例 2:[SDOI2017]数字表格

题意:第 i 行第 j 列的格子中的数是 fgcd(i,j),其中 gcd(i,j) 表示 i,j 的最大公约数,,Doris 的表格中共有 n×m 个数,她想知道这些数的乘积是多少。发现答案式是乘积,于是考虑将乘积转化为指数求和的形式。

i=1nj=1mf(gcd(i,j))=p=1nf(p)i=1nj=1m[gcd(i,j)=p]=p=1nf(p)i=1npj=1mp[gcd(i,j)=1]=p=1nf(p)i=1npj=1mpd=1npμ(d)npdmpd=T=1np|Tf(p)μ(Tp)nTmT

那么这个东西看上去没怎么见过,实际上就是将数论分块的过程由加和改为累乘,维护的时候拿逆元和快速幂处理一下即可。注意处理 μ(d)<0 时要拿扩展欧拉定理处理一下。

代码:

#include <bits/stdc++.h>
#define N 1000000
#define M (N + 5)
#define int long long
#define mod 1000000007
using namespace std;
int qpow(int x, int y) {
	if (y < 0) y = (y + mod - 1) % (mod - 1);
	int ans = 1;
	while (y) {
		if (y & 1) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}
int n, m;
int f[M];
void gtf() {
	f[1] = 1;
	for (int i = 2; i <= N; i++)
		f[i] = (f[i - 1] + f[i - 2]) % mod;
}
vector<int>prm;
bool is[M];
int mu[M];

int sm[M], fac[M], inv[M];
void solve() {
	mu[1] = 1;
	for (int i = 2; i <= N; i++) {
		if (!is[i]) {
			prm.push_back(i);
			mu[i] = -1;
		} 
		for (auto j : prm) {
			if (i * j > N) break;
			is[i * j] = 1;
			if (i % j == 0) {
				mu[i * j] = 0;
				break;
			}
			mu[i * j] = -mu[i];
		}
	}
	for (int i = 0; i <= N; i++) sm[i] = 1;
	for (int i = 1; i <= N; i++)
		for (int j = 1; j <= N / i; j++)
			sm[i * j] = sm[i * j] * qpow(f[i], mu[j]) % mod;
	fac[0] = 1;
	for (int i = 1; i <= N; i++) fac[i] = fac[i - 1] * sm[i] % mod;
	inv[N] = qpow(fac[N], mod - 2);
	for (int i = N - 1; i >= 0; --i)
		inv[i] = inv[i + 1] * sm[i + 1] % mod;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	gtf();
	solve();
	int T;
	cin >> T;
	while (T--) {
		int n, m;
		cin >> n >> m;
		if (n > m) swap(n, m);
		int l = 1, r = 0, ans = 1;
		while (l <= n) {
			r = min(n / (n / l), m / (m / l));
			ans = ans * qpow(fac[r] * inv[l - 1] % mod, (n / l) * (m / l)) % mod;
			l = r + 1;
		}
		cout << ans << "\n";
	}
	return 0;
}
posted @   长安19路  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示