莫比乌斯反演学习笔记
莫比乌斯反演学习笔记
参考资料
前置知识:
主要内容:
莫比乌斯函数 <讲>
莫比乌斯反演 <讲>
莫比乌斯例题 <讲>[HAOI2011]Problem b
LCMSUM
[国家集训队]Crash的数字表格
[SDOI2015]约数个数和
积性函数
定义:如果 \(\gcd(x,y)=1\) 并且 \(f(xy)=f(x)f(y)\),则 \(f(n)\) 为积性函数。
性质:如果 \(f(x)\) 和 \(g(x)\) 均为积性函数,那么如下函数也是积性函数:
例子:
单位函数:\(\epsilon(x)=[x=1]\)。
常数函数:\(1(x)=1\)。
欧拉函数:\(\varphi(x)=\sum\limits_{i=1}^x[\gcd(x,i)=1]\)。
莫比乌斯函数也是,下文会讲。
狄利克雷(Dirichlet)卷积
定义:
两个数论函数 \(f\) 和 \(g\) 的狄利克雷卷积 \((f*g)\) 为
性质:满足交换和结合律,\(\epsilon\) 是该函数单位元,每个函数卷它都得本身。
例子:
(这里的 \(\mu\) 就是过会儿的莫比乌斯函数)。
莫比乌斯函数
定义:莫比乌斯函数符号为 \(\mu\),
说明:如果有 \(x=\prod\limits_{i=1}^kp_i^{c_i}\),那么
- 如果 \(x=1\),那么 \(\mu(x)=1\)。
- 如果 \(\forall i:c_i=1\),那么 \(\mu(x)=(-1)^k\)。
- 否则 \(\mu(x)=0\)。
性质:
- \(\mu*1=\epsilon\),即 \(\sum\limits_{d\mid x}\mu(d)=\epsilon(x)\),或
-
\(\sum\limits_{d|n}\frac{\mu(d)}{d}=\frac{\varphi(n)}{n}\)。
-
反演结论:\([\gcd(i,j)=1]=\epsilon(\gcd(i,j))=\sum\limits_{d|\gcd(i,j)}\mu(d)\)。
-
拓展:\(\varphi*1=ID\),\(\sum\limits_{d|nm}=\sum\limits_{x|n}\sum\limits_{y|m}[\gcd(x,y)=1]\)。
线性筛求莫比乌斯函数:
void Mobius(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!np[i]) p[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*p[j]<=n;j++){
np[i*p[j]]=1;
if(i%p[j]==0){mu[i*p[j]]=0;break;}
mu[i*p[j]]=-mu[i];
}
}
}
莫比乌斯反演
如果有两个数量函数 \(f\) 和 \(g\),满足
那么就有
证明:
即已知 \(f=g*1\),证明 \(g=f*\mu\)。
证:\(f*\mu=g*1*\mu=g*\epsilon=g\)。
经典例题
[HAOI2011]Problem b
\(T\) 组测试数据,给定 \(a,b,c,d,k\) 求
\[\sum\limits_{i=a}^b\sum\limits_{j=c}^d[\gcd(i,j)=k] \]
数据范围:\(1\le T,a,b,c,d,k\le 5\times 10^4\)。
如果令
容斥一下,则有题目中的式子 \(=f(b,d)+f(a-1,c-1)-f(a-1,d)-f(b,c-1)\)。
然后
然后用莫比乌斯函数前缀和 \(+\) 整除分块计算即可,时间复杂度 \(\Theta(N+T\sqrt n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
//&Start
#define lng long long
#define lit long double
#define kk(i,n) "\n "[i<n]
const int inf=0x3f3f3f3f;
const lng Inf=1e17;
//&Mobius
const int N=5e4;
bitset<N+10> np;
int mu[N+10],cnt,p[N+10];
void Mobius(){
mu[1]=1;
for(int i=2;i<=N;i++){
if(!np[i]) p[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*p[j]<=N;j++){
np[i*p[j]]=1;
if(i%p[j]==0){mu[i*p[j]]=0;break;}
mu[i*p[j]]=-mu[i];
}
}
for(int i=1;i<=N;i++) mu[i]+=mu[i-1];
}
//&Fenk
int Fenk(int n,int m){
int res=0,mn=min(n,m);
for(int l=1,r;l<=mn;l=r+1){
r=min(n/(n/l),m/(m/l));
res+=(mu[r]-mu[l-1])*(n/l)*(m/l);
}
return res;
}
//&Data
int t,a,b,c,d,k;
//&Main
int main(){
Mobius();
scanf("%d",&t);
for(int ti=1;ti<=t;ti++){
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
printf("%d\n",
+Fenk(b/k,d/k)
+Fenk((a-1)/k,(c-1)/k)
-Fenk((a-1)/k,d/k)
-Fenk(b/k,(c-1)/k)
);
}
return 0;
}
LCMSUM
\(T\) 组数据,给定 \(n\),求 $$\sum\limits_{i=1}^n\operatorname{lcm}(i,n)$$
数据范围:\(1\le T\le 3\times 10^5\),\(1\le n\le 10^6\)。
令
然后码的时候,维护一个前缀和
即可,然后总时间复杂度就是 \(\Theta(n\log\log n+T)\)。
Code
#include <bits/stdc++.h>
using namespace std;
//&Start
#define lng long long
#define lit long double
#define kk(i,n) "\n "[i<n]
const int inf=0x3f3f3f3f;
const lng Inf=1e17;
//&Eular
const int N=1e6;
bitset<N+10> np;
int phi[N+10],cnt,p[N+10];
lng f[N+10];
void Eular(){
for(int i=2;i<=N;i++){
if(!np[i]) p[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&i*p[j]<=N;j++){
np[i*p[j]]=1;
if(i%p[j]==0){phi[i*p[j]]=phi[i]*p[j];break;}
else phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
f[1]=1;
for(int i=2;i<=N;i++) f[i]=1ll*phi[i]*i/2;
for(int j=1;j<=cnt;j++)
for(int i=1;i*p[j]<=N;i++)
f[i*p[j]]+=f[i]; //用f数组自得sum
}
//&Data
int t,n;
//&Main
int main(){
Eular();
scanf("%d",&t);
for(int ti=1;ti<=t;ti++){
scanf("%d",&n);
printf("%lld\n",f[n]*n);
}
return 0;
}
[国家集训队]Crash的数字表格
单组测试数据,给定 \(n,m\) ,求
\[\sum\limits_{i=1}^n\sum\limits_{j=1}^m\operatorname{lcm}(i,j)\bmod 20101009 \]
数据范围:\(1\le n,m\le 10^7\)。
\(n\le m\),一气呵成:
将 \(x=dk\) 带入:
然后筛 \(\mu(k)\) 时顺便计算 \(h(k)=k\mu(k)\),最后狄利克雷前缀和求 \(f(k)=\sum\limits_{k|x}k\mu(k)\)。
别忘了膜拜 \(20101009\),时间复杂度 \(\Theta(N+n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
//&Start
#define lng long long
#define lit long double
#define kk(i,n) "\n "[i<n]
const int inf=0x3f3f3f3f;
const lng Inf=1e17;
//&Mobius
const int N=1e7;
const int mod=20101009;
bitset<N+10> np;
int mu[N+10],cnt,p[N+10],f[N+10];
void Mobius(){
f[1]=mu[1]=1;
for(int i=2;i<=N;i++){
if(!np[i]) p[++cnt]=i,mu[i]=-1;
f[i]=(mu[i]*i+mod)%mod;
for(int j=1;j<=cnt&&i*p[j]<=N;j++){
np[i*p[j]]=1;
if(i%p[j]==0){mu[i*p[j]]=0;break;}
mu[i*p[j]]=-mu[i];
}
}
for(int j=1;j<=cnt;j++)
for(int i=1;i*p[j]<=N;i++)
(f[i*p[j]]+=f[i])%=mod; //狄利克雷前缀和
}
//&Data
int n,m,ans;
int bitfun(int x){
lng res=1ll*x*f[x]%mod;
(res*=1ll*(n/x+1)*(n/x)/2%mod)%=mod;
(res*=1ll*(m/x+1)*(m/x)/2%mod)%=mod; //如上
//这个1ll不乘要爆long long,30分。
return (int)res;
}
//&Main
int main(){
Mobius();
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
for(int i=1;i<=n;i++)
(ans+=bitfun(i))%=mod;
printf("%d\n",ans);
return 0;
}
[SDOI2015]约数个数和
\(T\) 组测试数据,给定 \(n,m\),求
\[\sum\limits_{i=1}^n\sum\limits_{j=1}^md(ij) \]其中 \(d(x)=\sum\limits_{k|x}\)。
数据范围:\(1\le n,m,T\le 5\times 10^4\)。
\(n\le m\):
设整除分块函数 \(\operatorname{fenk}(n)=\sum\limits_{i=1}^n\lfloor\frac{n}{i}\rfloor\)。
所以上式
然后分块套分块,加个莫比乌斯前缀和,即可。
Code
#include <bits/stdc++.h>
using namespace std;
//&Start
#define lng long long
#define lit long double
#define kk(i,n) "\n "[i<n]
const int inf=0x3f3f3f3f;
const lng Inf=1e17;
//&Mobius
const int N=5e4;
bitset<N+10> np;
int p[N+10],cnt,mu[N+10];
void Mobius(){
mu[1]=1;
for(int i=2;i<=N;i++){
if(!np[i]) p[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*p[j]<=N;j++){
np[i*p[j]]=1;
if(i%p[j]==0){mu[i*p[j]]=0;break;}
mu[i*p[j]]=-mu[i];
}
}
for(int i=2;i<=N;i++) mu[i]+=mu[i-1];//莫比乌斯前缀和
}
//&Fenk
lng fk[N+10];
lng Fenk(int n){//fenk函数
lng res=0;
for(int l=1,r;l<=n;l=r+1)
r=n/(n/l),res+=1ll*(r-l+1)*(n/l);
return res;
}
//&Data
int t,n,m;
//&Main
int main(){
Mobius();
for(int i=1;i<=N;i++)
fk[i]=Fenk(i);
scanf("%d",&t);
for(int ti=1;ti<=t;ti++){
scanf("%d%d",&n,&m);
if(n>m) n^=m^=n^=m;
lng ans=0;
for(int l=1,r;l<=n;l=r+1){//外层大分块
r=min(n/(n/l),m/(m/l));
ans+=fk[n/l]*fk[m/l]*(mu[r]-mu[l-1]);
}
printf("%lld\n",ans);
}
return 0;
}
如果有感悟或经验,后期会更新,感谢支持。
祝大家学习愉快!