莫比乌斯反演学习笔记
莫比乌斯函数
定义
性质
莫比乌斯函数是积性函数。
其中 。 表示狄利克雷卷积。
证明:
设 。
则有
补充结论:
显然。
线性求莫比乌斯函数
由于莫比乌斯函数是积性函数,故可以用线性筛求。
具体实现如下:
mu[1]=1;
for (int i=2;i<=n;i++) {
if (!f[i]) prime[++cnt]=i,mu[i]=-1;
for (int j=1;j<=cnt&&prime[j]*i<=n;j++) {
f[prime[j]*i]=1;
if (i%prime[j]==0) {
mu[prime[j]*i]=0;
break;
} else mu[prime[j]*i]=-mu[i];
}
}
莫比乌斯反演
设 为两个数论函数。
形式一:
这种形式下, 被称为 的莫比乌斯变换, 被称为 的莫比乌斯反演。
容易发现当 时满足这个结论。
形式二:
例题
洛谷P2522 [HAOI2011]Problem b
每个区间左右端点都 ,然后就变成了求 。
然后是经典 Trick 拆成四个类似的式子,然后推导以下:
然后数论分块优化一下即可。
// Think twice,code once.
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=50000;
int T,cnt,prime[50005],mu[50005];
bool f[50005];
int calc(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() {
mu[1]=1;
for (int i=2;i<=N;i++) {
if (!f[i]) prime[++cnt]=i,mu[i]=-1;
for (int j=1;j<=cnt&&prime[j]*i<=N;j++) {
f[prime[j]*i]=1;
mu[prime[j]*i]=-mu[i];
if (i%prime[j]==0) {mu[prime[j]*i]=0;break;}
}
}
for (int i=2;i<=N;i++) mu[i]+=mu[i-1];
scanf("%d",&T);
while (T--) {
int l1,r1,l2,r2,k;
scanf("%d%d%d%d%d",&l1,&r1,&l2,&r2,&k);
l1=(l1+k-1)/k;
r1=r1/k;
l2=(l2+k-1)/k;
r2=r2/k;
if (l1>r1||l2>r2) {puts("0");continue;}
printf("%d\n",calc(r1,r2)-calc(l1-1,r2)-calc(r1,l2-1)+calc(l1-1,l2-1));
}
return 0;
}
洛谷P2257 YY的GCD
与上一道题类似,可以简单地写出式子
然而这样做复杂度是 的,显然过不去。
考虑进一步优化:设 ,则:
然后后面这个式子可以预处理,于是复杂度就降到了 。
// Think twice, code once.
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e7;
int T,n,m;
int cnt,prime[10000005],mu[10000005];
int f[10000005];
long long sum[10000005];
int main() {
mu[1]=1;
f[1]=1;
for (int i=2;i<=N;i++) {
if (!f[i]) prime[++cnt]=i,mu[i]=-1;
for (int j=1;j<=cnt&&(long long)prime[j]*i<=N;j++) {
f[prime[j]*i]=1;
if (i%prime[j]==0) {
mu[prime[j]*i]=0;
break;
} else mu[prime[j]*i]=-mu[i];
}
}
for (int i=1;i<=cnt;i++)
for (int j=prime[i];j<=N;j+=prime[i])
sum[j]+=mu[j/prime[i]];
for (int i=1;i<=N;i++) sum[i]+=sum[i-1];
scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&m);
long long ans=0;
for (int l=1,r;l<=min(n,m);l=r+1) {
r=min(n/(n/l),m/(m/l));
ans+=(sum[r]-sum[l-1])*(n/l)*(m/l);
}
printf("%lld\n",ans);
}
return 0;
}
洛谷P3327 [SDOI2015]约数个数和
反演来咯~
首先一个性质:
具体证明见 的博客。
于是:
到这里就可以用莫比乌斯函数的性质把 给拆掉了,但是这里介绍一种用反演的做法:
先把 替换成 ,然后定义:
则:
设:
则:
易得答案为 。根据莫比乌斯反演形式二:
故:
发现当 时没有贡献,故:
求 和答案都可以使用数论分块,时间复杂度
// Think twice, code once.
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int T,n,m;
int cnt,prime[50005],mu[50005];
int f[50005];
long long sum[50005];
int main() {
mu[1]=1;
for (int i=2;i<=50000;i++) {
if (!f[i]) prime[++cnt]=i,mu[i]=-1;
for (int j=1;j<=cnt&&prime[j]*i<=50000;j++) {
f[prime[j]*i]=1;
if (i%prime[j]==0) {mu[prime[j]*i]=0;break;}
else mu[prime[j]*i]=-mu[i];
}
}
for (int i=2;i<=50000;i++) mu[i]+=mu[i-1];
for (int i=1;i<=50000;i++)
for (int l=1,r;l<=i;l=r+1) {
r=i/(i/l);
sum[i]+=(long long)(r-l+1)*(i/l);
}
scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&m);
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+=(mu[r]-mu[l-1])*sum[n/l]*sum[m/l];
}
printf("%lld\n",ans);
}
return 0;
}
分类:
学习笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效