莫比乌斯反演学习笔记

引入

对于数论问题中的一些函数 f(n),如果很难直接求出它的值,却容易求出其倍数和或约数和 g(n),那么可以通过莫比乌斯反演化简运算,求得 f(n) 的值。

定义

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

μ(n)={1n=10n(1)kkn

n=i=1kpici,其中 pi 为质因子,ci1。上述定义表示:

1.当 n=1 时,μ(n)=1

2.当 n1 时:

  • 当存在 i[1,k],使得 ci>1 时,μ(n)=0,也就是只要有某个质因子出现次数超过一次,μ(u) 就等于 0

  • 当任意 i[1,k],都满足 ci=1 时,μ(n)=(1)kk 即为出现的质因子的总个数。

代码实现

根据定义,μ(1)μ(n) 可以在线性筛的过程中同时得到,时间复杂度为 O(n)

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

性质

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

证明

n=i=1kpici,n=i=1kpi

那么 d|nμ(d)=d|nμ(d)=i=0k(ki)(1)i=(1+(1))k

根据二项式定理,只有当 k=0n=1 时原式值为 1,反之则为 0

结论

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

这个等式的证明比较显然,根据前面提到的性质,只有当 gcd(i,j)=1 时,才满足μ(d)=1,反之 μ(d)=0

莫比乌斯变换

形式一

f(n)=d|ng(d)g(n)=d|nμ(d)f(nd)

形式二

f(n)=n|dg(d)g(n)=n|dμ(dn)f(d)

「HAOI 2011」Problem b

题意

T 组询问,每次询问求值:

i=xni=ym[gcd(i,j)=k]

1T,x,y,n,m,k5×104

思路

根据差分的思路,可以将每次询问拆成四个相同形式的式子:

i=1ni=1m[gcd(i,j)=k]

化简该式子:

i=1nki=1mk[gcd(i,j)=1]

利用莫比乌斯函数的性质,进一步化简,得到:

i=1nki=1mkd|gcd(i,j)μ(d)

变换求和顺序,先枚举 d|gcd(i,j) 可得:

d=1μ(d)i=1nk[d|i]i=1mk[d|j]

1nk 中,d 的倍数有 nkd 个,故原式化为:

d=1min(nk,mk)μ(d)nkdmkd

于是这个式子就可以用数论分块求解。时间复杂度为 O(Tn)

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e4+10;
int prime[N],mu[N],tot;bool vis[N];
void init()
{
	mu[1]=1;
	for(int i=2;i<N;i++)
	{
		if(!vis[i]) prime[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&prime[j]*i<N;j++)
		{
			vis[prime[j]*i]=true;
			if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=2;i<N;i++) mu[i]+=mu[i-1];
}
int solve(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()
{
	int T,n,m,x,y,k;scanf("%d",&T);init();
	while(T--)
	{
		scanf("%d%d%d%d%d",&x,&n,&y,&m,&k);
	    printf("%d\n",solve(n/k,m/k)-solve((x-1)/k,m/k)-solve(n/k,(y-1)/k)+solve((x-1)/k,(y-1)/k));
	}
	return 0;
}

Crash的数字表格

求值:

i=1nj=1mlcm(i,j)

对 20101009$ 取模。

1n,m107

思路

不难发现,原式等价于:

i=1nj=1mijgcd(i,j)

枚举 d=gcd(i,j)

d=1ni=1ndj=1md[gcd(i,j)=1] ijd

d 提到前面来,套用莫比乌斯函数:

d=1ndi=1ndj=1mdd|gcd(i,j)μ(d)ij

单独计算后面两项的值,记

sum(n,m)=i=1nj=1md|gcd(i,j)μ(d)ij

变换求和顺序,先枚举 d|gcd(i,j) 可得:

d=1d|ind|jmμ(d)ij

i=id,j=jd,带入式子可得:

d=1μ(d)d2i=1ndj=1mdij

注意到这个式子前半部分可以预处理前缀和用整除分块做,后半部分则可以直接计算,那么原式就是:

i=1nd×sum(nd,md)

于是总的时间复杂度就为 O(n+m)

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e7+10,mod=20101009;
int mu[N],sum[N],prime[N],tot;bool vis[N];
void init()
{
	mu[1]=1;
	for(int i=2;i<N;i++)
	{
		if(!vis[i]) prime[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&prime[j]*i<N;j++)
		{
			vis[prime[j]*i]=true;
			if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<N;++i) sum[i]=(sum[i-1]+1ll*i*i%mod*(mu[i]+mod))%mod;
}
int Sum(int n,int m){return (1ll*n*(n+1)/2%mod)*(1ll*m*(m+1)/2%mod)%mod;}
int func(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=(res+1ll*(sum[r]-sum[l-1]+mod)*Sum(n/l,m/l)%mod)%mod;
    }
    return res;
}
int solve(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=(res+1ll*(r-l+1)*(l+r)/2%mod*func(n/l,m/l)%mod)%mod;
    }
    return res;
}
int main()
{
	int n,m;scanf("%d%d",&n,&m);init();printf("%d\n",solve(n,m));
	return 0;
}

约数个数和

T 组询问。每次询问给定 n,m,求:

i=1nj=1md(ij)

其中 d(x)x 的约数个数。

1T,n,m50000

思路:

首先需要用到 d 函数的一个特殊性质,即:

d(ij)=x|iy|i[gcd(x,y)=1]

考虑将这个式子化简:

d(ij)=x|iy|i[gcd(x,y)=1]

=x|iy|ip|gcd(x,y)μ(p)

=p=1μ(p)x|iy|i[p|gcd(x,y)]

=p|i,p|jμ(p)x|iy|i[p|gcd(x,y)]

=p|i,p|jμ(p)x|ipy|jp1

=p|i,p|jμ(p)d(ip)d(jp)

带回原式,得:

i=1nj=1md(ij)

=i=1nj=1mp|i,p|jμ(p)d(ip)d(jp)

=p=1min(n,m)μ(p)i=1nj=1m[p|i][p|j]d(ip)d(jp)

=p=1min(n,m)μ(p)i=1npj=1mpd(i)d(j)

=p=1min(n,m)μ(p)i=1npd(i)j=1mpd(j)

对于 d 函数,也可以在线性筛的同时求出。那么就可以做到 O(n) 预处理,O(Tn) 询问。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e4+10;
#define LL long long
LL d[N];int t[N],mu[N],prime[N],tot;bool vis[N];//t表示最小质因子的出现次数 
void init()
{
	mu[1]=d[1]=1;
	for(int i=2;i<N;i++)
	{
		if(!vis[i]) prime[++tot]=i,d[i]=2,mu[i]=-1,t[i]=1;
		for(int j=1;j<=tot&&prime[j]*i<N;j++)
		{
			vis[prime[j]*i]=true;
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;
				d[i*prime[j]]=d[i]/(t[i]+1)*(t[i]+2);
 			    t[i*prime[j]]=t[i]+1;
 			    break;
			}
			mu[i*prime[j]]=-mu[i];
			d[i*prime[j]]=d[i]*2;
		    t[i*prime[j]]=1;
		}
	}
	for(int i=1;i<N;i++) mu[i]+=mu[i-1],d[i]+=d[i-1];
}
LL solve(int n,int m)
{
	LL 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])*d[n/l]*d[m/l];
	}
	return res;
}
int main()
{
	int T,n,m;scanf("%d",&T);init();
	while(T--) scanf("%d%d",&n,&m),printf("%lld\n",solve(n,m));
	return 0;
}
posted @   曙诚  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示