莫比乌斯反演从零开始(数学不好的来看这个)

困扰了我半年的莫比乌斯,今天终于有了进展。本人是数论小白,数学基础不好,对数学不敏感,对数学公式推导转化头疼,所以才学的慢,下面我以一个数学不好的人的视角学习。

为了解决哪些问题?

∑ ∑ f ( x ) \sum\sum f(x) f(x)这样的式子,比如 ∑ i = 1 n ∑ j = 1 n g c d ( i , j ) \sum_{i=1}^n\sum_{j=1}^ngcd(i,j) i=1nj=1ngcd(i,j)之类的

莫比乌斯函数

u ( n ) = { 1 n = 1 ( − 1 ) k n 无 平 方 数 因 子 , k 为 不 同 因 子 个 数 0 n 有 大 于 1 的 平 方 数 因 子 u(n) = \begin{cases} 1 & n = 1 \\ (-1)^k & n无平方数因子,k为不同因子个数 \\ 0& n有大于1的平方数因子 \end{cases} u(n)=1(1)k0n=1nkn1

重要的性质

∑ d ∣ n u ( d ) = [ n = 1 ] \sum_{d|n}u(d)=[n=1] dnu(d)=[n=1]

莫比乌斯函数的线性筛
类似线性筛素数,线性筛出莫比乌斯函数的值及其前缀和。

const int N=1e6+10,M=1e6;
int cnt,vis[N],prime[N],Smu[N],mu[N];
void init_mu() { //线性筛莫比乌斯函数
	Smu[1]=mu[1]=1;
	cnt=0;
	for(int i=2; i<=M; i++) {
		if(vis[i]==0) {
			prime[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1; j<=cnt; j++) {
			if(i*prime[j]>M)break;
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
			else mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=2;i<=M;i++)Smu[i]=Smu[i-1]+mu[i]; 
}

如何使用?

前置知识:整除分块(一学就懂)

套路1
[ g c d ( i , j ) = 1 ] = ∑ d ∣ g c d ( i , j ) u ( d ) [gcd(i,j)=1]=\sum_{d|gcd(i,j)}u(d) [gcd(i,j)=1]=dgcd(i,j)u(d)
[ g c d ( a 1 , a 2 , a 3 … … a n ) = 1 ] = ∑ d ∣ g c d ( a 1 , a 2 , a 3 … … a n ) u ( d ) [gcd(a_1,a_2,a_3……a_n)=1]=\sum_{d|gcd(a_1,a_2,a_3……a_n)}u(d) [gcd(a1,a2,a3an)=1]=dgcd(a1,a2,a3an)u(d)
套路2:枚举d,这样就不用判断 d|gcd(i,j) ,就可以将其消去
∑ i = 1 n ∑ j = 1 m ∑ d ∣ g c d ( i , j ) 1 = ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] = ∑ i = 1 n / d ∑ j = 1 m / d = [ n d ] [ m d ] \sum_{i=1}^n\sum_{j=1}^m \sum_{d|gcd(i,j)}1=\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=d]=\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}=[\frac{n}{d}][\frac{m}{d}] i=1nj=1mdgcd(i,j)1=i=1nj=1m[gcd(i,j)=d]=i=1n/dj=1m/d=[dn][dm]
即求满足:gcd(i,j)等于d或d的倍数的对数。
证明:d|i 的 i 有 [ n d ] [\frac{n}{d}] [dn]个,d|j 的 j 有 [ m d ] [\frac{m}{d}] [dm]个。这些 i,j 两两组合的gcd均为d或d的倍数。

例题1.1

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = 1 ] ( n < m ) \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=1] (n<m) i=1nj=1m[gcd(i,j)=1]n<m

1.由套路1变形得到
∑ i = 1 n ∑ j = 1 m ∑ d ∣ g c d ( i , j ) u ( d ) \sum_{i=1}^n\sum_{j=1}^m\sum_{d|gcd(i,j)}u(d) i=1nj=1mdgcd(i,j)u(d)
2.由套路2变形得到
∑ d = 1 n u ( d ) ∗ [ n d ] [ m d ] \sum_{d=1}^nu(d)*[\frac{n}{d}][\frac{m}{d}] d=1nu(d)[dn][dm]
3.u(d)的前缀和筛出来,最后整除分块即可

由于线性筛是O(n)的,所以单次查询情况下,时间复杂度并没有降下来,但在多次查询的情况下,是快了很多的。

代码如下

long long solve(long long n,long long m) {
	if(n==0||m==0)return 0;
	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+=(Smu[r]-Smu[l-1])*(n/l)*(m/l);
	}
	return ans;
}

例题1.2

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = k ] ( n < m ) \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=k] (n<m) i=1nj=1m[gcd(i,j)=k]n<m
变形得到
∑ i = 1 n / k ∑ j = 1 m / k [ g c d ( i , j ) = 1 ] \sum_{i=1}^{n/k}\sum_{j=1}^{m/k}[gcd(i,j)=1] i=1n/kj=1m/k[gcd(i,j)=1]
就是例题1.1了。

long long solve(long long n,long long m,long long k) {
	n/=k,m/=k;
	if(n==0||m==0)return 0;
	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+=(Smu[r]-Smu[l-1])*(n/l)*(m/l);
	}
	return ans;
}

例题1.3

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = p r i m e ] ( n < m ) \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=prime] (n<m) i=1nj=1m[gcd(i,j)=prime]n<m
1.枚举 p,变形得到
∑ p ∈ p r i m e ∑ i = 1 n / p ∑ j = 1 m / p [ g c d ( i , j ) = 1 ] \sum_{p\in prime}\sum_{i=1}^{n/p}\sum_{j=1}^{m/p}[gcd(i,j)=1] pprimei=1n/pj=1m/p[gcd(i,j)=1]
2.后面这个式子就是例题1嘛,变=>
∑ p ∈ p r i m e ∑ d = 1 n / p u ( d ) ∗ [ n p d ] ∗ [ m p d ] \sum_{p\in prime}\sum_{d=1}^{n/p}u(d)*[\frac{n}{pd}]*[\frac{m}{pd}] pprimed=1n/pu(d)[pdn][pdm]
3.令k=pd,变=>
∑ p ∈ p r i m e ∑ d = 1 n / p u ( k p ) ∗ [ n k ] ∗ [ m k ] \sum_{p\in prime}\sum_{d=1}^{n/p}u(\frac{k}{p})*[\frac{n}{k}]*[\frac{m}{k}] pprimed=1n/pu(pk)[kn][km]
4.枚举k,变=>
∑ k = 1 n ∑ p ∈ p r i m e , p ∣ k u ( k p ) ∗ [ n k ] ∗ [ m k ] \sum_{k=1}^n\sum_{p\in prime,p|k}u(\frac{k}{p})*[\frac{n}{k}]*[\frac{m}{k}] k=1npprime,pku(pk)[kn][km]

5.令 f ( x ) = ∑ p ∈ p r i m e , p ∣ x u ( x p ) f(x)=\sum_{p \in prime,p|x}u(\frac{x}{p}) f(x)=pprime,pxu(px),变=>
∑ k = 1 n f ( k ) ∗ [ n k ] ∗ [ m k ] \sum_{k=1}^nf(k)*[\frac{n}{k}]*[\frac{m}{k}] k=1nf(k)[kn][km]
6.由整除分块可知,若可以求出f(x)函数的前缀和,就可以轻而易举的求出这个式子了。怎么求???(等等,之后再写)

例题1.4

∑ i = 1 n ∑ j = 1 n g c d ( i , j ) \sum_{i=1}^{n}\sum_{j=1}^ngcd(i,j) i=1nj=1ngcd(i,j)

1.枚举 x,gcd(i,j)=x
∑ x = 1 n x × ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = x ] \sum_{x=1}^nx\times\sum_{i=1}^{n}\sum_{j=1}^n[gcd(i,j)=x] x=1nx×i=1nj=1n[gcd(i,j)=x]
= ∑ x = 1 n x × ∑ i = 1 n / x ∑ j = 1 n / x [ g c d ( i , j ) = 1 ] =\sum_{x=1}^nx\times\sum_{i=1}^{n/x}\sum_{j=1}^{n/x}[gcd(i,j)=1] =x=1nx×i=1n/xj=1n/x[gcd(i,j)=1]

2.根据套路1变形
= ∑ x = 1 n x × ∑ i = 1 n / x ∑ j = 1 n / x ∑ d ∣ g c d ( i , j ) u ( d ) =\sum_{x=1}^nx\times\sum_{i=1}^{n/x}\sum_{j=1}^{n/x}\sum_{d|gcd(i,j)}u(d) =x=1nx×i=1n/xj=1n/xdgcd(i,j)u(d)

3.再根据套路2,枚举d
= ∑ x = 1 n x × ∑ d = 1 n / x [ n d x ] [ n d x ] u ( d ) =\sum_{x=1}^nx\times\sum_{d=1}^{n/x}[\frac{n}{dx}][\frac{n}{dx}]u(d) =x=1nx×d=1n/x[dxn][dxn]u(d)

f ( i ) = ∑ d = 1 i [ i d ] [ i d ] u ( d ) f(i)=\sum_{d=1}^i[\frac{i}{d}][\frac{i}{d}]u(d) f(i)=d=1i[di][di]u(d),这个式子整除分块 O ( n 1 / 2 ) O(n^{1/2}) O(n1/2)
原 式 = ∑ x = 1 n x × f ( [ n x ] ) 原式=\sum_{x=1}^nx\times f([\frac{n}{x}]) =x=1nx×f([xn])
这个式子也整除分块 O ( n 1 / 2 ) O(n^{1/2}) O(n1/2)

总时间复杂度: O ( n ) O(n) O(n)

long long f(long long n) {
	long long ans=0;
	for(int l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		ans+=1ll*(n/l)*(n/l)*(Smu[r]-Smu[l-1]);
	}
	return ans;
}
int main() {
	init_mu();
	long long n,ans=0;
    cin>>n;
    for(int l=1,r;l<=n;l=r+1){
    	r=n/(n/l);
    	ans+=1ll*(r-l+1)*(l+r)/2*f(n/l);
	}
	cout<<ans;
}

例题2.1

∑ i = 1 n ∑ j = 1 m f ( i j ) , n < = m \sum_{i=1}^n \sum_{j=1}^m f(ij),n<=m i=1nj=1mf(ij)n<=m
【注意】其中 f ( x ) f(x) f(x) 表示 x 的因子个数。

1.将 d(i,j) 展开

f ( i j ) = ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = 1 ] f(ij)=\sum_{x|i}\sum_{y|j} [gcd(x,y)=1] f(ij)=xiyj[gcd(x,y)=1]
2.原式转化,变=>
∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = 1 ] \sum_{i=1}^n \sum_{j=1}^m \sum_{x|i}\sum_{y|j} [gcd(x,y)=1] i=1nj=1mxiyj[gcd(x,y)=1]

3.更换枚举项,枚举x,y,变=>
∑ x = 1 n ∑ y = 1 m [ n x ] [ m y ] [ g c d ( x , y ) = 1 ] \sum_{x=1}^n\sum_{y=1}^m [\frac{n}{x}][\frac{m}{y}][gcd(x,y)=1] x=1ny=1m[xn][ym][gcd(x,y)=1]
= ∑ i = 1 n ∑ j = 1 m [ n i ] [ m j ] [ g c d ( i , j ) = 1 ] =\sum_{i=1}^n\sum_{j=1}^m [\frac{n}{i}][\frac{m}{j}][gcd(i,j)=1] =i=1nj=1m[in][jm][gcd(i,j)=1]

4.套路1变形,变=>
∑ i = 1 n ∑ j = 1 m [ n i ] [ m j ] ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{i=1}^n\sum_{j=1}^m [\frac{n}{i}][\frac{m}{j}]\sum_{d|gcd(i,j)}\mu(d) i=1nj=1m[in][jm]dgcd(i,j)μ(d)

5.与套路2的思路一致,枚举d,这样就不用考虑 d|gcd(i,j),变=>
∑ d = 1 n ∑ x = 1 n / d ∑ y = 1 m / d [ n d x ] [ m d y ] μ ( d ) \sum_{d=1}^{n} \sum_{x=1}^{n/d}\sum_{y=1}^{m/d}[\frac{n}{dx}][\frac{m}{dy}]\mu(d) d=1nx=1n/dy=1m/d[dxn][dym]μ(d)
= ∑ d = 1 n μ ( d ) ∑ x = 1 n / d [ n d x ] ∑ y = 1 m / d [ m d y ] =\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{n/d}[\frac{\frac{n}{d}}{x}]\sum_{y=1}^{m/d}[\frac{\frac{m}{d}}{y}] =d=1nμ(d)x=1n/d[xdn]y=1m/d[ydm]

6.整除分块
优化:后面两坨的形式一样,可以使用整除分块预处理出函数 g ( x ) = ∑ i = 1 x [ x i ] g(x)=\sum_{i=1}^x [\frac{x}{i}] g(x)=i=1x[ix]的所有值,预处理时间复杂度 O ( n 3 / 2 ) O(n^{3/2}) O(n3/2)。然后每次O(n)求,时间复杂度 O ( T n + n 3 / 2 ) O(Tn+n^{3/2}) O(Tn+n3/2)
继续优化:预处理完第一步之后,求得式子就是 ∑ d = 1 n u ( d ) × g ( n / d ) × g ( m / d ) \sum_{d=1}^n u(d)\times g(n/d)\times g(m/d) d=1nu(d)×g(n/d)×g(m/d) ,这步还可以再整除分块。时间复杂度 O ( T n 1 / 2 + n 3 / 2 ) O(Tn^{1/2}+n^{3/2}) O(Tn1/2+n3/2)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=5e4;
long long mu[N],prime[N],cnt,vis[N],Smu[N],g[N];
void init_Smu() {
	Smu[1]=mu[1]=1;
	for(int i=2; i<=M; i++) {
		if(vis[i]==0) {
			prime[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1; j<=cnt; j++) {
			if(i*prime[j]>M)break;
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
			else mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=2; i<=M; i++)Smu[i]=Smu[i-1]+mu[i];
	for(int i=1; i<=M; i++) {
		for(int l=1,r=2; l<=i; l=r+1) {
			r=i/(i/l);
			g[i]+=(r-l+1)*(i/l);
		}
	}
}
int main() {
	long long T,n,m;
	cin>>T;
	init_Smu();
	while(T--) {
		cin>>n>>m;
		if(n>m)swap(n,m);
		long long ans=0;
		for(int l=1,r=2;l<=n;l=r+1){
			r=min(n/(n/l),m/(m/l));
			ans+=(Smu[r]-Smu[l-1])*g[n/l]*g[m/l];
		}
		cout<<ans<<endl;
	}
	return 0;
}

例题2.2

例题链接没A掉

题目描述: T组询问,一张 n×m 的数表,其中 aij 为能同时整除 i 和 j 的所有自然数之和。给定 b,计算数表中不大于 b 的数之和。T,n<=1e5。
问题分析:
∑ i = 1 n ∑ j = 1 m f ( g c d ( i , j ) ) [ f ( g c d ( i , j ) ) < = b ] , n < = m \sum_{i=1}^{n}\sum_{j=1}^{m}f(gcd(i,j))[f(gcd(i,j))<=b],n<=m i=1nj=1mf(gcd(i,j))[f(gcd(i,j))<=b]n<=m
【注意】其中 f ( x ) f(x) f(x) 表示 x 的因子的和。

1.先不考虑后面那个限制,枚举 g c d ( i , j ) = x gcd(i,j) = x gcd(i,j)=x ,并用套路1变形
∑ x = 1 n ∑ i = 1 n ∑ j = 1 m f ( x ) [ g c d ( i , j ) = x ] \sum_{x=1}^n\sum_{i=1}^{n}\sum_{j=1}^{m}f(x)[gcd(i,j)=x] x=1ni=1nj=1mf(x)[gcd(i,j)=x]
= ∑ x = 1 n ∑ i = 1 n / x ∑ j = 1 m / x f ( x ) [ g c d ( i , j ) = 1 ] \sum_{x=1}^n\sum_{i=1}^{n/x}\sum_{j=1}^{m/x}f(x)[gcd(i,j)=1] x=1ni=1n/xj=1m/xf(x)[gcd(i,j)=1]
= ∑ x = 1 n f ( x ) ∑ i = 1 n / x ∑ j = 1 m / x ∑ d ∣ g c d ( i , j ) u ( d ) =\sum_{x=1}^nf(x)\sum_{i=1}^{n/x}\sum_{j=1}^{m/x}\sum_{d|gcd(i,j)}u(d) =x=1nf(x)i=1n/xj=1m/xdgcd(i,j)u(d)
2.根据套路2,枚举 d 变形
∑ x = 1 n f ( x ) ∑ d = 1 n / x [ n d x ] [ m d x ] u ( d ) \sum_{x=1}^nf(x)\sum_{d=1}^{n/x}[\frac{n}{dx}][\frac{m}{dx}]u(d) x=1nf(x)d=1n/x[dxn][dxm]u(d)
3.观察后面那个式子,令 g ( i ) = ∑ d = 1 i [ i d ] [ m / x d ] u ( d ) g(i)=\sum_{d=1}^i[\frac{i}{d}][\frac{m/x}{d}]u(d) g(i)=d=1i[di][dm/x]u(d) 是可以整除分块O(sqrt(n))求出来的
∑ x = 1 n f ( x ) g ( [ n x ] ) \sum_{x=1}^nf(x)g([\frac{n}{x}]) x=1nf(x)g([xn])
4.发现又可以整除分块。
方法1:O(n) 求出 f(x) 的时候,把 f(x)>b 的项当成0 ,并计算前缀和。TLE

方法2:对查询的 b 升序排序,用树状数组维护前缀和,最初 f(x) 为 0,随便 b 的增大,单点修改

【错误】

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=1e5+5;
const long long mod=(1ll<<31);

struct quest {
	long long n,m,b,id;
} q[N];
struct node {
	long long v,id;
} f[N];
int cmp1(node x,node y) {
	return x.v<y.v;
}
int cmp(quest x,quest y) {
	return x.b<y.b;
}
long long ans[N],c[N];

long long cnt,vis[N],prime[N],Smu[N],mu[N];
void init_Smu() { //线性筛莫比乌斯函数
	Smu[1]=mu[1]=1;
	for(int i=2; i<=M; i++) {
		if(vis[i]==0) {
			prime[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1; j<=cnt; j++) {
			if(i*prime[j]>M)break;
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
			else mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=2; i<=M; i++)Smu[i]=Smu[i-1]+mu[i];
}
void init_S() {
	for(int i=1; i<=M; i++) {
		for(int j=i; j<=M; j+=i) {
			f[j].v+=i;
		}
		f[i].id=i;
	}
	sort(f+1,f+M+1,cmp1);
}

int lowbit(int x) {
	return x&(-x);
}
void update(long long k,long long x) {
	for(int i=k; i<=M; i+=lowbit(i))c[i]+=x;
}
long long query(long long l,long long r) {
	long long ans=0;
	for(int i=r; i>0; i-=lowbit(i))ans+=c[i];
	for(int i=l-1; i>0; i-=lowbit(i))ans-=c[i];
	return ans;
}
long long g(long long n,long long m) {
	long long ans=0;
	for(int l=1,r; l<=n; l=r+1) {
		r=min(n/(n/l),m/(m/l));
		ans+=(Smu[r]-Smu[l-1])*(n/l)*(m/l);
		ans=ans%mod;
	}
	return ans;
}

int main() {
	freopen("1538.txt","r",stdin);
	init_S();
	init_Smu();
	long long T;
	cin>>T;
	for(int i=1; i<=T; i++) {
		scanf("%lld%lld%lld",&q[i].n,&q[i].m,&q[i].b);
		if(q[i].n>q[i].m)swap(q[i].n,q[i].m);
		q[i].id=i;
	}
	sort(q+1,q+T,cmp);

    int now=0;
	for(int i=1; i<=T; i++) {
		while(f[now+1].v<=q[i].b&&now<M) {
			now++;
			update(f[now].id,f[now].v);
		}
		long long ret=0;
		for(int l=1,r; l<=q[i].n; l=r+1) {
			r=q[i].n/(q[i].n/l);
			ret+=query(l,r)*g(q[i].n/l,q[i].m/l);
			ret=ret%mod;
		}
		ans[q[i].id]=ret;
	}
	for(int i=1; i<=T; i++)printf("%lld\n",ans[i]);
	return 0;
}

例题3

∑ i = a b ∑ j = c d [ g c d ( i , j ) = k ] \sum_{i=a}^b\sum_{j=c}^d [gcd(i,j)=k] i=abj=cd[gcd(i,j)=k]

1.容斥一下就是例题1.2了

a n s = ∑ i = 1 b ∑ j = 1 d [ g c d ( i , j ) = k ] − ∑ i = 1 a − 1 ∑ j = 1 d [ g c d ( i , j ) = k ] − ∑ i = 1 b ∑ j = 1 c − 1 [ g c d ( i , j ) = = k ] + ∑ i = 1 a − 1 ∑ j = 1 c − 1 [ g c d ( i , j ) = k ] ans=\sum_{i=1}^b\sum_{j=1}^d [gcd(i,j)=k]-\sum_{i=1}^{a-1}\sum_{j=1}^d [gcd(i,j)=k]-\sum_{i=1}^b\sum_{j=1}^{c-1}[gcd(i,j)==k]+\sum_{i=1}^{a-1}\sum_{j=1}^{c-1}[gcd(i,j)=k] ans=i=1bj=1d[gcd(i,j)=k]i=1a1j=1d[gcd(i,j)=k]i=1bj=1c1[gcd(i,j)==k]+i=1a1j=1c1[gcd(i,j)=k]

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值