Skip to content

一、莫比乌斯函数

1. 定义

n=p1t1p2t2p3t3pktk,其中 pin 的互异质因子。

μ(n)={(1)qti=11n=10Otherwise.

2. 性质

性质一

n1,则 d|nμ(d)=0

n=p1t1p2t2p3t3pktk,其中 pin 的互异质因子。

因为 d|n,所以 d 是由 n 的质因子中选择若干个相乘得到的。

如果 d 是由一个以上相同的质因子组成的,则 μ(d)=0,所以只有在 p1pq 间选择若干个组成 d,才会对答案有贡献。

若选择 i 个质因子,则方案数为 Cqi,贡献为 (1)iCqi

所以:

d|nμ(d)=i=0q(1)iCqi

x=1 代入 (x+1)n=i=0nxiCni,有:

i=0q(1)iCqi=0

性质二

μ(x) 是积性函数。

ab 互质,且 a 的质因子后有 p 项,b 的有 q 项。

μ(a)μ(b) 均不为 0 时,那么明显 ab 的质因子有 p+q 项,因为 (1)p+q=(1)p(1)q,所以 μ(ab)=μ(a)μ(b)

μ(a)μ(b) 至少有一项为 0 时,那么明显 ab 也会有重复的质因子,所以 μ(ab)=0=μ(a)μ(b)

二、莫比乌斯反演的套路

没有算法,全是套路。

1. P3455 [POI2007] ZAP-Queries

求式子:

i=1aj=1b[gcd(i,j)=d]

为书写简便,假设式子中的 ab。下文中的 x/y 均表示 xy

套路一

gcd(i,j)=d 化为 gcd(i,j)=1

全部同时除以 d 即可。

i=1a/dj=1b/d[gcd(i,j)=1]

套路二

运用莫比乌斯函数的性质一转换。

由性质一,可得若 n=1,则 d|nμ(d)=1,所以,将 [gcd(i,j)=1] 转换一下:

i=1a/dj=1b/dx|gcd(i,j)μ(x)

套路三

x 的和号改为枚举 x 的形式。

因为 xgcd(i,j),所以 xmin(a/d,b/d),如果 x|gcd(i,j),那么 [x|gcd(i,j)]=1,所以:

i=1a/dj=1b/dx=1a/dμ(x)[x|gcd(i,j)]

套路四

让每个函数都只和他前面的和号有关系。

容易发现 μ(x) 和前两个和号没有半毛钱关系,直接提前,所以:

x=1a/dμ(x)i=1a/dj=1b/d[x|gcd(i,j)]

套路五

把判断整除 gcd 拆开。

容易发现 [x|gcd(i,j)] 一部分等价于 [x|ix|j],也就是 [x|i][x|j]

x=1a/dμ(x)i=1a/dj=1b/d[x|i][x|j]

此时再用一下套路四:

x=1a/dμ(x)i=1a/d[x|i]j=1b/d[x|j]

套路六

把判断整除拆开。

i=1a/d[x|i] 就是求 1a/d 中,x 的倍数的个数,明显可以直接用 a/d/x 求出,所以:

x=1a/dμ(x)(a/d/x)(b/d/x)

至此,化简完成,x=1a/dμ(x) 可以用前缀和求出,后面的部分可以用整除分块求出。

Code:

cpp
#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N=1e5+10;
int n,a,b,c,d,k,ans,q[N];
namespace Mu{
	int prime[N],tot,mu[N];
	bool isprime[N];
	void getmu(int x){
		memset(isprime,true,sizeof isprime);
		isprime[1]=false;
		mu[1]=1;
		for(int i=2;i<=x;i++){
			if(isprime[i]){
				prime[++tot]=i;
				mu[i]=-1;
			}
			for(int j=1;j<=tot and i*prime[j]<=x;j++){
				isprime[i*prime[j]]=false;
				if(!(i%prime[j]))break;
				mu[i*prime[j]]=-mu[i];
			}
		}
	}
}using namespace Mu;
signed main(){
	IOS;
	getmu(5e4);
	for(int i=1;i<=5e4;i++)
		q[i]=q[i-1]+mu[i];
	
	cin>>n;
	while(n--){
		cin>>a>>b>>k;
		a/=k;
		b/=k;
		if(a>b)swap(a,b);
		ans=0;
		for(int l=1,r;l<=a;l=r+1){
			r=min(a/(a/l),b/(b/l));
			ans+=(q[r]-q[l-1])*(a/l)*(b/l);
		}
		cout<<ans<<"\n";
	}
	return 0;
}

2. P4449 于神之怒加强版

求式子:

i=1nj=1mgcd(i,j)k

套路七

将求值部分改为枚举取值并乘上合法个数。

显然的,上面的式子可以枚举 gcd(i,j) 的值,假设 d=gcd(i,j),那么就可以化简为:

i=1nj=1md=1ndk[gcd(i,j)=d]

此时,运用套路四:

d=1ndki=1nj=1m[gcd(i,j)=d]

发现后半部分跟上一题一模一样,直接运用上一题相同的方法化简:

d=1ndkx=1n/dμ(x)(n/d/x)(m/d/x)

套路八

运用向上/下取整的性质化简。

性质:

abc=abc,abc=abc

所以:

d=1ndkx=1n/dμ(x)(n/(dx))(m/(dx))

套路九

分母出现两个枚举的数的积时,改为枚举这个积。

T=dx,所以:

T=1n(n/T)(m/T)d|Tdkμ(Td)

此时,只要求出后半部分 d|Tdkμ(Td) 就好了。

f(T)=d|Tdkμ(Td),很容易可以得到这是一个积性函数,所以将 T 分解质因子,T=p1t1p2t2p3t3pqtq,那么由积性函数的性质:

f(T)=i=1qf(piti)=i=1qpik(ti1)μ(pi)+piktiμ(1)=i=1qpik(ti1)+pikti

于是就可以线性筛了,原式变为:

T=1n(n/T)(m/T)f(T)

使用整除分块解决。

3. P1829 [国家集训队] Crash的数字表格 / JZPTAB

求式子:

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

运用 lcm(a,b)=abgcd(a,b) 的关系,很容易可以得到:

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

运用套路七:

d=1ni=1nj=1mij/d[gcd(i,j)=d]

运用套路一,但是出现了一个没见过的 ij/d,设 i=i/d,j=j/d,则:

ij/d=idjd/d=ijd

所以:

d=1ni=1n/dj=1m/dijd[gcd(i,j)=1]

运用套路二;

d=1ni=1n/dj=1m/dx|gcd(i,j)ijdμ(x)

运用套路三:

d=1ni=1n/dj=1m/dx=1n/dijdμ(x)[x|gcd(i,j)]

运用套路四:

d=1ndx=1n/dμ(x)i=1n/dj=1m/dij[x|gcd(i,j)]

运用套路五:

d=1ndx=1n/dμ(x)i=1n/dj=1m/dij[x|i][x|j]

套路十

将整除中的除数化为 1

因为 [1|p]=1,所以这样做可以直接化简掉 [x|p] 这样的东西,所以我们在和号的边界里除掉一个 x,再在求值里乘回一个 x

d=1ndx=1n/dμ(x)i=1n/d/xj=1m/d/xijx2[1|i][1|j]d=1ndx=1n/dμ(x)i=1n/d/xj=1m/d/xijx2

再次运用套路四:

d=1ndx=1n/dμ(x)x2i=1n/d/xij=1m/d/xj

后面两个和号明显就是一个等差数列,直接变成式子:

d=1ndx=1n/dμ(x)x2(1+n/d/x)(n/d/x)2(1+m/d/x)(m/d/x)2

化简完成,但是这式子也太抽象了,根本没法求,想办法通过用多几个函数来表示方便一点:

观察到第二个和号开始后面的部分可以用整除分块求,那么定义一个函数 f(p,q) 表示该部分:

f(p,q)=x=1pμ(x)x2(1+p/x)(p/x)2(1+q/x)(q/x)2

观察到最后的两个分式可以 O(1) 求,那么定义一个函数 g(p,q) 表示该部分:

g(p,q)=p(p+1)2q(q+1)2f(p,q)=x=1pμ(x)x2g(p/x,q/x)ans=d=1ndf(n/d,m/d)

使用整除分块解决。

4. P3312 [SDOI2014] 数表

最近更新