莫比乌斯反演学习笔记

基本公式

莫比乌斯函数

原式子

\[\mu(u)= \begin{cases} 1&n=1\\ 0&n\text{含有平方因子}\\ (-1)^k&k\text{为}n\text{的本质不同的质因子个数}\\ \end{cases} \]

代码实现(线性筛)

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

奇妙性质\({\color{#8B0000}{\large \star}}\)

\[\sum_{d\mid n} \mu(d) =\varepsilon(n)= \begin{cases} 1&n=1\\ 0 &n\neq 1\\ \end{cases} \]

(因此运用到题里后经常需要对莫比乌斯函数求前缀和)

莫比乌斯反演\({\color{#8B0000}{\large \star}}\)

\[\text{如果有} f(n)=\sum_{d\mid n} g(d) \text{ 则} g(n)=\sum_{d\mid n} \mu(d) f( \frac{n}{d}) \]

\[\text{如果有} f(n)=\sum_{n\mid d} g(d) \text{ 则} g(n)=\sum_{n\mid d} \mu( \frac{d}{n}) f(d) \]

运用技巧

数论分块\({\color{#8B0000}{\large \star}}\)

解说

\[\text{有时我们会遇到形如} \sum \lfloor \frac{n}{i} \rfloor \text{的式子且} \Theta (n) \text{过不去,这时候就会用到数列分块。} \]

\[\text{显然对于一段数} \lfloor \frac{n}{i} \rfloor \text{都是相等的,那么我们就可以将其分到一块里一并计算。} \]

\[\text{那么现在我们就需要找到一个最大的}j \text{使得} \lfloor \frac{n}{i} \rfloor = \lfloor \frac{n}{j} \rfloor \text{,结论是此时} j= \left \lfloor \frac{n}{\left \lfloor \frac{n}{i}\right \rfloor } \right \rfloor \text{。下面是证明。} \]

\[\begin{aligned} &\left\lfloor\frac{n}{i}\right\rfloor \leq \frac{n}{i}\\ \implies &\left\lfloor\frac{n}{ \left\lfloor\frac{n}{i}\right\rfloor }\right\rfloor \geq \left\lfloor\frac{n}{ \frac{n}{i} }\right\rfloor = \left\lfloor i \right\rfloor=i \\ \implies &i\leq \left\lfloor\frac{n}{ \left\lfloor\frac{n}{i}\right\rfloor }\right\rfloor=j\\ &&\square \end{aligned} \]

此时时间复杂度为\(\Theta(\sqrt n)\)

当然还有二维版本的

\[\sum_{i=1}^{\min (n,m)}\left\lfloor\frac{n}{i} \right\rfloor\left\lfloor\frac{m}{i} \right\rfloor \]

此时将r = n/(n/l)替换成 r = min(n/(n/l), m/(m/l))就完了

(以上证明引自OI Wiki

代码实现

(以洛谷P2261为例,很裸的数论分块)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k;
ll ans,l,r;
int main(){
	scanf("%d%d",&n,&k);
	ans=(ll)n*(ll)k;
	l=1;
	while(l<=n){
		if(k/l) r=min(k/(k/l),(ll)n);
		else r=n;
		ans-=(k/l)*(r-l+1)*(l+r)/2;
		l=r+1;
	}
	printf("%lld\n",ans);
	return 0;
}

例题

解说

洛谷P2522 Problem b

题目让我们求的是

\[\sum_{i=a}^{b}\sum_{j=c}^{d} \varepsilon (\gcd(i,j)==k)\qquad \]

可以拆成四块进行求解,每一块都形如

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

上式可化为

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

\[\displaystyle\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum_{d\mid \gcd(i,j)}\mu(d) \]

变换求和顺序得

\[\displaystyle\sum_{d=1}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\varepsilon (d\mid i)\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\varepsilon (d\mid j) \]

\(1 \sim \lfloor \frac{n}{d} \rfloor\)\(d\)的倍数一共\(\lfloor \frac{n}{kd} \rfloor\)个,所以原式还可以化为

\[\displaystyle\sum_{d=1}\mu(d)\lfloor\frac{n}{kd}\rfloor\lfloor\frac{m}{kd}\rfloor \]

符合数论二维分块的形式,可以 \(\Theta(\sqrt n)\) 搞掉了

代码

#include<bits/stdc++.h>
using namespace std;
const int lzw=5e4+3;
int tot,prime[lzw],mu[lzw],a,b,c,d,t,k;
bool flag[lzw];
void get_mu(){
	mu[1]=1;
	for(int i=2;i<=lzw-3;i++){
		if(!flag[i]) prime[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&prime[j]*i<=lzw-3;j++){
			flag[prime[j]*i]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;
				break;
			}
			mu[i*prime[j]]-=mu[i];
		}
	}
	for(int i=1;i<=lzw-3;i++) mu[i]+=mu[i-1];
}
int solve(int n,int m){
	int res=0,l=1,r;
    while(l<=min(n,m)){
        r=min(n/(n/l),m/(m/l));
        res+=(mu[r]-mu[l-1])*(n/l)*(m/l);
        l=r+1;
    }
    return res;
}
int main(){
	get_mu();
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		printf("%d\n",solve(b/k,d/k)-solve(b/k,(c-1)/k)-solve((a-1)/k,d/k)+solve((a-1)/k,(c-1)/k));
	}
	return 0;
}

例题2

解说

上面的例题似乎并没有反演,只是用了一下莫比乌斯函数的性质,这里再放一个反演的例题。

洛谷P2257 YY的GCD

题目让我们求的答案为

\[ans=\sum_{i=1}^{N}\sum_{j=1}^{M} \varepsilon(gcd(i,j)==prime) \]

不妨设

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

那么答案可以化为

\[ans=\sum_{p\in prime}f(p) \]

再增设一个函数

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

显然(好像不这么显然但是稍微看看能弄明白)它还可以写成另一种形式

\[g(n)=\left\lfloor \frac{N}{n} \right\rfloor \left\lfloor \frac{M}{n} \right\rfloor \]

这个时候我们就珂以反演

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

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

那么我们就珂以接着推答案式子

\[ans=\sum_{p\in prime}f(p)=\sum_{p\in prime}\sum_{p\mid d} \mu(\frac{d}{p})g(d) \]

\[ans=\sum_{p\in prime}\sum_{p\mid d} \mu(\frac{d}{p})\left\lfloor \frac{N}{d} \right\rfloor \left\lfloor \frac{M}{d} \right\rfloor \]

我们把后一个求和换成枚举\(d\)的形式,显然在\(d\)大于\(N\)\(M\)时后面的求和全都是\(0\),因此我们只枚举到\(\min (N,M)\)

\[ans=\sum_{p\in prime}\sum_{d=kp,k\in N^+}^{min(N,M)} \mu(\frac{d}{p})\left\lfloor \frac{N}{d} \right\rfloor \left\lfloor \frac{M}{d} \right\rfloor \]

换一个枚举顺序得

\[ans=\sum_{d=1}^{min(N,M)}\sum_{p\in prime,p\mid d}\mu(\frac{d}{p})\left\lfloor \frac{N}{d} \right\rfloor \left\lfloor \frac{M}{d} \right\rfloor \]

由于第二个求和已经和\(d\)无关,我们可以提一下公因式

\[ans=\sum_{d=1}^{min(N,M)}\left\lfloor \frac{N}{d} \right\rfloor \left\lfloor \frac{M}{d} \right\rfloor\sum_{p\in prime,p\mid d}\mu(\frac{d}{p}) \]

这个时候我们就可以用数列分块搞它了。预处理的时候多处理一下\(\sum_{p\in prime,p\mid d}\mu(\frac{d}{p})\)即可

代码



#include<bits/stdc++.h>
using namespace std;
const int lzw=1e7+3;
typedef long long ll;
int n,m,t,prime[lzw],tot,mu[lzw],g[lzw];
bool flag[lzw];
ll sum[lzw];
void get_mu(){
	flag[1]=1,mu[1]=1;
	for(int i=2;i<=lzw-3;i++){
		if(!flag[i]) flag[i]=1,prime[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&(ll)prime[j]*i<=(ll)lzw-3;j++){
			flag[prime[j]*i]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;
				break;
			}
			mu[i*prime[j]]-=mu[i];
		}
	}
	for(int i=1;i<=tot;i++)
		for(int j=1;(ll)j*prime[i]<=(ll)lzw-3;j++)
			g[j*prime[i]]+=mu[j];
	for(int i=1;i<=lzw-3;i++) sum[i]=sum[i-1]+g[i];
}
ll solve(int n,int m){
	ll res=0;
	int l=1,r;
	while(l<=min(n,m)){
		r=min(n/(n/l),m/(m/l));
		res+=(ll)(n/l)*(m/l)*(sum[r]-sum[l-1]);
		l=r+1;
	}
	return res;
}
int main(){
	get_mu();
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		printf("%lld\n",solve(n,m));
	}
	return 0;
}

幸甚至哉,歌以咏志。

posted @ 2020-09-24 21:36  DarthVictor  阅读(206)  评论(3编辑  收藏  举报
莫挨老子!