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

前言

因为笔者很菜,所以有部分结论没有证明。


积性函数

即为当 \(gcd(i,j)=1\),有 \(f(xy)=f(x)f(y)\),的函数 \(f(x)\),即为积性函数。

常见积性函数

  • 莫比乌斯函数:\(\mu(x)= \left\{\begin{matrix} &1&,n=1 \\ &(-1)^k&, x=p_1 p_2 p_3 ... p_k,\forall p_i!=p_j,p_i\in prime\\ &0&,others \end{matrix}\right.\),第二个条件简单来说就是若 \(x\)分解质因数后每个分解出的素数的指数都\(\leq1\)
  • 欧拉函数:\(\varphi(x)=\sum_{i=1}^{n}[gcd(i,n)=1]\)
  • \(ID\)函数:\(ID(x)=x\)
  • 单位函数:\(\varepsilon (x)=[x=1]\)
  • 常数函数:\(1(x)=1\)

大部分积性函数都可以用线性筛求。

数论分块

即求 \(\sum_{i=1}^{n}\left \lfloor \frac{n}{i} \right \rfloor\),后面还可以带一些积性函数。这里就相当于乘上一个单位函数。暴力要 \(O(n)\),数论分块只需 \(\sqrt n\)

\(Code:\)

inline void solve(int n){
	register int ans=0;
	for(register int l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		ans+=(r-l+1)*(n/l);
	}
	printf("%d\n",ans);
}

狄利克雷卷积

也叫 \(Dirichlet\) 卷积

两个函数 \(a\)\(b\) 的狄利克雷卷积 \(f\)

\[f(n)=\sum_{d|n}a(d) b(\frac{n}{d}) \]

其中

\[f=a*b \]

性质

  • 交换律:\(f*g=g*f\)
  • 结合律:\(f*(g*a)=(f*g)*a\)
  • 任意数论函数与单位函数的卷积为其本身:\(f*\varepsilon=f\)

莫比乌斯函数

\(\mu(x)= \left\{\begin{matrix} &1&,n=1 \\ &(-1)^k&, x=p_1 p_2 p_3 ... p_k,\forall p_i!=p_j,p_i\in prime\\ &0&,others \end{matrix}\right.\)

线性筛求莫比乌斯函数

与筛 \(\varphi\) 函数的写法差不多。

inline void Gmu(){
	mu[1]=1;tot=0;
	for(register int i=2;i<=MAX;++i){
		if(!f[i]){
			p[++tot]=i;
			mu[i]=-1;
		}
		for(register int j=1;j<=tot&&i*p[j]<=MAX;++j){
			f[i*p[j]]=1;
			if(i%p[j]==0){
				mu[i*p[j]]=0;
				break;
			}
			mu[i*p[j]]=-mu[i];
		}
	}
	for(register int i=1;i<=MAX;++i)mu[i]+=mu[i-1]; 
} 

性质

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

证明:

\(n=1\) 时,显然成立

\(n\ne1\)时,将 \(n\) 分解质因数,\(n=p_1^{c_1}p_2^{c_2}p_3^{c_3}... \forall p_i!=p_j,p_i\in prime\)

可以发现对于所有 \(d\),只有当 \(d\) 的所有质因子的次数为 \(1\) 时,\(\mu(d) \ne 0\),而质因子为 \(m\) 的因子为 \(\binom{n}{m}\),易得

\[\sum_{d|n}\mu(d)=\binom{n}{0}-\binom{n}{1}+\binom{n}{2}-....+(-1)^n\binom{n}{n}=\sum_{i=0}^{n}(-1)^i\binom{n}{i} \]

后面就是二项式定理 \((x+y)^n=\sum_{i=0}^{n} \binom{n}{i}x^{n}y^{n-i}\),带入 \(x=1,y=-1\),得:

\[(1+(-1))^n=0^n=\sum_{i=0}^{n}(-1)^i\binom{n}{i}=0 \]

所以

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

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

\(ID\)\(\varphi\) 带入 莫比乌斯反演的公式即可

莫比乌斯反演

若有数论函数 \(f,g\),满足

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

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

证明:

把条件转为狄利克雷卷积的形式,即为

\[f=g*1 \]

两边同时卷 \(\mu\)

\[f*\mu=g*1*\mu \]

因为满足结合律,\(1*\mu=\varepsilon\)

\(f*\mu=g* \varepsilon\)

有因为任何函数卷 \(\varepsilon\) 都为本身,所以

\[f*\mu=g \]

这个卷积的式子就等价于

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

例题

\(\texttt{blog食用}\)

首先可以看出题目求

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

首先很套路地转化为

\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}[gcd(i,j)=1] \]

有一个数论函数 \(\varepsilon(x)\),当\(x=1\)时,函数值为\(1\)否则为\(0\)

后面的\([gcd(i,j)=1]\)可以用\(\varepsilon(gcd(i,j))\)

\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}\varepsilon(gcd(i,j)) \]

\(\mu*1 = \varepsilon\) 反演

\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}\sum_{p|gcd(i,j)}\mu(p) \]

改变求和顺序

\[\sum_{p=1}\mu(p)\sum_{i=1}^{\left \lfloor \frac{a}{d} \right\rfloor}[p \ | \ i]\sum_{j=1}^{\left \lfloor \frac{b}{d} \right \rfloor}[p \ |\ j] \]

后面两个\(\sum\),可以看出求的是\(d\)有多少个倍数在\(\left \lfloor \frac{a}{d} \right\rfloor\)\(\left \lfloor \frac{b}{d} \right \rfloor\)中。显然,答案分别为\(\left \lfloor \frac{a}{pd} \right\rfloor\)
\(\left \lfloor \frac{b}{pd} \right\rfloor\)

转化为

\[\sum_{p=1}\mu(p)\left \lfloor \frac{a}{pd} \right\rfloor\left \lfloor \frac{b}{pd} \right\rfloor \]

然后数论分块加前缀和就做完了

我Code里的是先把\(a/d,b/d\),再做数论分块,这样常数会小一点。

$ Code $

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long 
inline int read(){
	register int x=0,f=0,ch=getchar();
	while('0'>ch||ch>'9')f^=ch=='-',ch=getchar();
	while('0'<=ch&&ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
const int MAX=50100;
int mu[MAX],pri[MAX],f[MAX],tot;
inline void Getmu(int N){
	mu[1]=1;
	for(register int i=2;i<=N;++i){
		if(!f[i]){
			mu[i]=-1;
			pri[++tot]=i;
		} 
		for(register int j=1;j<=tot&&i*pri[j]<=N;++j){
			f[i*pri[j]]=1;
			if(i%pri[j]==0)break;
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(register int i=1;i<=N;++i)mu[i]+=mu[i-1];
} 
int ans;
inline int solve(int n,int m){
	ans=0;
	if(n>m)n^=m^=n^=m;
	for(register int l=1,r;l<=n;l=r+1){
		r=min(n/(n/l),m/(m/l));
		ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
	}
	return ans;
}
int t,a,b,d;
signed main(){
	Getmu(50000);
	t=read();
	while(t--){
		a=read(),b=read(),d=read();
		a/=d,b/=d;
		printf("%lld\n",solve(a,b));
	}
	return 0;
}

\(\texttt{blog食用}\)

题目要求

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

然后再去重。

可以想到枚举\(n\)以内的\(p\),把对答案的贡献改成\(p\times gcd(i,j)=p\)的数的个数

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

化简式子

\[\sum_{p=1}^{n}p\sum_{i=1}^{\left \lfloor \frac{n}{p} \right \rfloor }\sum_{j=1}^{\left \lfloor \frac{n}{p} \right \rfloor }[gcd(i,j)=1] \]

因为\([gcd(i,j)=1]\)只有\(gcd(i,j)=1\)\(1\)的贡献,否则没有,所以可以替换为\(\varepsilon(gcd(i,j))\)

\(\varepsilon(x)\)即为当\(x=1\)时对答案贡献为\(1\),否则为\(0\)

所以现在转化为了

\[\sum_{p=1}^{n}p\sum_{i=1}^{\left \lfloor \frac{n}{p} \right \rfloor }\sum_{j=1}^{\left \lfloor \frac{n}{p} \right \rfloor }\varepsilon(gcd(i,j)) \]

\(Dirichlet\)卷积得

\(\varepsilon =\mu*1 \Leftrightarrow \varepsilon(n)=\sum_{d|n}\mu(d)\)

所以原式转化为

\[\sum_{p=1}^{n}p\sum_{i=1}^{\left \lfloor \frac{n}{p} \right \rfloor }\sum_{j=1}^{\left \lfloor \frac{n}{p} \right \rfloor }\sum_{d|gcd(i,j)}\mu(d) \]

转化一下求和顺序

\[\sum_{p=1}^{n}p\sum_{d=1}{\mu(d)}\sum_{i=1}^{\left \lfloor \frac{n}{p} \right \rfloor }[d\ |\ i]\sum_{j=1}^{\left \lfloor \frac{n}{p} \right \rfloor}[d\ | \ j] \]

后面两个即为求\(\left \lfloor \frac{n}{p} \right \rfloor\)\(d\)的倍数,易知其答案为\(\left \lfloor \frac{n}{pd} \right \rfloor\)

所以答案转化为

\[ans=\sum_{p=1}^{n}p\sum_{d=1}\mu(d)\left \lfloor \frac{n}{pd} \right \rfloor\left \lfloor \frac{n}{pd} \right \rfloor \]

后面那玩意用数论分块和前缀和即可。

\(Code\)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long 
inline int read(){
	register int x=0,f=0,ch=getchar();
	while('0'>ch||ch>'9')f^=ch=='-',ch=getchar();
	while('0'<=ch&&ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
const int MAX=100005;
int n,tot,p[MAX],f[MAX],mu[MAX];
inline void Gmu(){
	mu[1]=1;tot=0;
	for(register int i=2;i<=n;++i){
		if(!f[i]){
			p[++tot]=i;
			mu[i]=-1;
		}
		for(register int j=1;j<=tot&&i*p[j]<=n;++j){
			f[i*p[j]]=1;
			if(i%p[j]==0){
				mu[i*p[j]]=0;
				break;
			}
			mu[i*p[j]]=-mu[i];
		}
	}
	for(register int i=1;i<=n;++i)mu[i]+=mu[i-1]; 
} 
int res,ans;
inline void solve(int n,int p){
	res=0;
	for(register int l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		res+=(mu[r]-mu[l-1])*(n/l)*(n/l);
	}
	ans+=res*p;
}
signed main(){
	n=read();
	Gmu();
	for(register int p=1;p<=n;++p)solve(n/p,p);
	printf("%lld\n",ans);
	return 0;
}

参考

oi-wiki、度娘

posted @ 2020-03-27 11:39  Lates  阅读(233)  评论(0编辑  收藏  举报