莫比乌斯反演小记
基本内容
莫比乌斯函数
定义为 的逆。
一些小性质:
反演内容
我的理解是:
典型例题
例1 P2398 GCD SUM
求
来推下式子:
先枚举 :
再将 除掉:
然后莫比乌斯反演:
枚举 :
将 除掉:
令 ,整理一下:
由于 :
欧拉函数可以直接线性筛预处理前缀和。中间的可以用整除分块 解决。
时间复杂度瓶颈在线性筛的 。
点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN=2e5+5; int n,cnt; int prime[MAXN],vis[MAXN],phi[MAXN],sum[MAXN]; inline void init(int n) { memset(vis,0,sizeof(vis)); phi[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) { prime[++cnt]=vis[i]=i; phi[i]=i-1; } for(int j=1;j<=cnt && i*prime[j]<=n;j++) { vis[i*prime[j]]=prime[j]; if(i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1); else phi[i*prime[j]]=phi[i]*prime[j]; } } for(int i=1;i<=n;i++) sum[i]=sum[i-1]+phi[i]; return; } signed main() { ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n; init(n); int ans=0; for(int l=1,r;l<=n;l=r+1) { r=n/(n/l); ans+=(sum[r]-sum[l-1])*(n/l)*(n/l); } printf("%lld\n",ans); return 0; }
例2 P1829 [国家集训队] Crash的数字表格 / JZPTAB
求:
这道题与上题差不多,因为 。
推式子:
令 ,
然后到这里就可以做了,莫比乌斯函数预处理,然后两次整除分块,时间复杂度 。
点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN=1e7+5; const int MOD=20101009; int n,m,cnt; int prime[MAXN],vis[MAXN],mu[MAXN],sum[MAXN]; inline void init(int n) { mu[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) {mu[i]=-1;prime[++cnt]=i;} for(int j=1;j<=cnt && i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(!(i%prime[j])) {mu[i*prime[j]]=0;break;} else mu[i*prime[j]]=-mu[i]; } } for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+i*i%MOD*(mu[i]+MOD)%MOD)%MOD; return; } inline int S(int r,int l=1) {return ((r+l)*(r-l+1)/2)%MOD;} inline int Sum(int x,int y) {return ((x*(x+1)/2)%MOD)*((y*(y+1)/2)%MOD)%MOD;} inline int solve(int x,int y) { int ans=0; for(int l=1,r;l<=min(x,y);l=r+1) { int r1=x/(x/l),r2=y/(y/l); r=min(r1,r2); ans=(ans+(sum[r]-sum[l-1]+MOD)%MOD*Sum(x/r,y/r)%MOD)%MOD; } return ans; } signed main() { ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m; init(min(n,m)); int ans=0; for(int l=1,r;l<=min(n,m);l=r+1) { int r1=n/(n/l),r2=m/(m/l); r=min(r1,r2); ans=(ans+S(r,l)*solve(n/r,m/r)%MOD)%MOD; } printf("%lld\n",(ans+MOD)%MOD); return 0; }
例3 P3327 [SDOI2015] 约数个数和
这道题有个前置知识:
然后就来推式子:
令 ,
那么到这里就可以做了。
显然 可以整除分块预处理,那么每组数据计算只需要用一次整除分块,整体时间复杂度 。
点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN=5e4+5; int T,n,m,cnt; int prime[MAXN],vis[MAXN],mu[MAXN],summu[MAXN],sum[MAXN]; inline void init(int n) { mu[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) {mu[i]=-1;prime[++cnt]=i;} for(int j=1;j<=cnt && i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(!(i%prime[j])) {mu[i*prime[j]];break;} else mu[i*prime[j]]=-mu[i]; } } for(int i=1;i<=n;i++) summu[i]=summu[i-1]+mu[i]; for(int i=1;i<=n;i++) { int ans=0; for(int l=1,r;l<=i;l=r+1) r=i/(i/l),ans+=(r-l+1)*(i/l); sum[i]=ans; } return; } signed main() { ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>T; init(MAXN); while(T--) { cin>>n>>m; int ans=0; for(int l=1,r;l<=min(n,m);l=r+1) { int r1=n/(n/l),r2=m/(m/l); r=min(r1,r2); ans+=(summu[r]-summu[l-1])*sum[n/l]*sum[m/l]; } printf("%lld\n",ans); } return 0; }
本文作者:Code_AC
本文链接:https://www.cnblogs.com/code-ac/p/17734177.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步