莫比乌斯反演学习笔记
基本公式
莫比乌斯函数
原式子
\[\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;
}
例题
解说
题目让我们求的是
\[\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
解说
上面的例题似乎并没有反演,只是用了一下莫比乌斯函数的性质,这里再放一个反演的例题。
题目让我们求的答案为
\[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;
}
幸甚至哉,歌以咏志。
签名:
我将轻轻叹息,叙述这一切,
许多许多年以后:
林子里有两条路,我——
选择了行人稀少的那一条,
它改变了我的一生。