莫比乌斯反演——从入门到入土
莫比乌斯反演
前置芝士
整除分块 洛谷 P2261
清醒的头脑
对于 \(\varphi\) 和 \(\mu\) 函数的认识
关键公式
\(\displaystyle\sum_{d|n}^{}\varphi(d)=n\)
\(\displaystyle\sum_{d|n}^{}\mu(d)=[n=1]\)
应用
下面,对于一道简单的题目进行分析。
洛谷P2257 YY的GCD
题目就是要求解
\(\qquad \qquad \qquad \qquad \qquad \qquad \displaystyle\sum_{i=1}^{N}{\sum_{j=1}^{M}{\sum_{p \in Prime}{[\gcd(i,j)=p]}}}\)
令\(\min(N,M)=N\)
套路将枚举p的移到前面
\(= \displaystyle\sum_{p \in Prime}\sum_{i=1}^{N}{\sum_{j=1}^{M}{{[\gcd(i,j)=p]}}}\)
\(= \displaystyle\sum_{p \in Prime}\sum_{i=1}^{\lfloor \frac{N}{p} \rfloor}{\sum_{j=1}^{\lfloor \frac{M}{p} \rfloor}{{[\gcd(i,j)=1]}}}\)
看到了后面有套路公式,直接扔过去
\(= \displaystyle\sum_{p \in Prime}\sum_{i=1}^{\lfloor \frac{N}{p} \rfloor}{\sum_{j=1}^{\lfloor \frac{M}{p} \rfloor}{{\sum_{d|i \And d|j}{\mu(d)}}}}\)
套路枚举d
\(= \displaystyle\sum_{p \in Prime}\sum_{1 \leq d \leq N}{\mu(d)}\sum_{i=1}^{\lfloor \frac{N}{p} \rfloor}{\sum_{j=1}^{\lfloor \frac{M}{p} \rfloor}{{[d|i]*[d|j]}}}\)
观察到后面那个其实就是枚举在 \(i,j\) 合适的范围之内找到d的倍数,显然可以化解成下面的形式
\(= \displaystyle\sum_{p \in Prime}\sum_{1 \leq d \leq N}{\mu(d)}\lfloor \frac{N}{dp} \rfloor \lfloor \frac{M}{dp} \rfloor\)
其实推式子到这一步已经结束了,但是呢,明显,这个式子明显大于 \(\mathbf{O}(N)\) 所以要进一步优化,笔者到这里也想不到下一步优化了,不过确实非常巧妙
令 \(T = dp\)
显然有 \(d|T\quad p|T\) ,并且 \(T \leq N\) ,且原式可以换成如下形式
\(= \displaystyle\sum_{p \in Prime}\sum_{1 \leq d \leq N}{\mu(d)}\lfloor \frac{N}{T} \rfloor \lfloor \frac{M}{T} \rfloor\)
这边枚举一手 p 和 T
\(= \displaystyle\sum_{p \in Prime}\sum_{1 \leq T \leq N,q|T}{\mu(\frac{T}{p})}\lfloor \frac{N}{T} \rfloor \lfloor \frac{M}{T} \rfloor\)
\(= \displaystyle\sum_{1 \leq T \leq n}\sum_{p \in Prime,q|T}{\mu(\frac{T}{p})}\lfloor \frac{N}{T} \rfloor \lfloor \frac{M}{T} \rfloor\)
\(= \displaystyle\sum_{1 \leq T \leq n}\lfloor \frac{N}{T} \rfloor \lfloor \frac{M}{T} \rfloor\sum_{p \in Prime,q|T}{\mu(\frac{T}{p})}\)
很舒服,发现前面可以整除分块,但是后面呢? 单独拿出来康康
\(\qquad \qquad \displaystyle\sum_{p \in Prime,q|T}{\mu(\frac{T}{p})}\)
因为在循环中,我们的嵌套循环中的第二个循环明显可以把 \(T\) 看成是一个常数,所以说 用中文说出这个和式的含义
要求 \(T\) 除以 \(T\) 的所有质因数的 \(\mu\) 值
说到这里,预处理已经十分明显了。
#include <bits/stdc++.h>
#define debug puts("I ak IOI several times");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
t=0; register char ch=getchar(); register int fflag=1;
while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
while(('0'<=ch&&ch<='9')){t=((t<<1)+(t<<3))+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){
read(t);read(args...);
}
template <typename T>inline void write(T x){
if(x<0) putchar('-'),x=~(x-1); int s[40],top=0;
while(x) s[++top]=x%10,x/=10; if(!top) s[++top]=0;
while(top) putchar(s[top--]+'0');
}
int n,T,m;
bool notprime[10000008];
int miu[10000008],c[10000008],sum[10000008];
int prime[5000008],cnt;
void Shai(int K){
notprime[1]=1; miu[1]=1;
for(int i=2;i<=K;++i){
if(!notprime[i]){
prime[++cnt]=i;
miu[i]=-1;
}
for(int j=1;j<=cnt;++j){
int t=prime[j]*i;
if(t>K) break;
notprime[t]=1;
if(i%prime[j]==0) break;
miu[t]=-miu[i];
}
}
for(int i=1;i<=cnt;++i)
for(int j=1;j*prime[i]<=K;++j)
c[prime[i]*j]+=miu[j];
for(int i=1;i<=K;++i) sum[i]=sum[i-1]+c[i];
}
int main(){
read(T);
Shai(10000005);
while(T--){
read(n,m);
if(n>m) swap(n,m);
long long ans=0;
long long l,r;
l=r=1;
//for(int dp=1;dp<=n;++dp) ans+=(n/dp)*(m/dp)*c[dp];
for(;r<=n&&l<=n;l=r+1){
long long nowans=(n/l)*(m/l);
r=min(n/(n/l),m/(m/l));
ans+=nowans*(long long)(sum[r]-sum[l-1]);
}
cout<<ans<<endl;
}
return 0;
}
//Welcome back,Chtholly.