莫比乌斯反演笔记
前置知识:常见积性函数
\(\varphi(n)\) 返回 \(1-n\) 中与 \(n\) 互质的数的个数。
\(id(n)\) 返回 \(n\) 。
\(\epsilon(n)\) 返回 \([n=1]\) 。
\(I(n)\) 返回 \(1\) 。
以及接下来讲的 \(\mu\) 函数。
莫比乌斯函数
莫比乌斯函数 \(\mu(n)\) 为积性函数,他的定义如下:
将变量 \(n\) 质因子展开为 \(p_1^{c_1}p_2^{c_2}...p_m^{c_m}\) ,
当 \(n=1\) 是,返回 \(1\) 。
当有 \(i\) 个质因子且 \(\forall c_i=1\) ,返回 \((-1)^{i}\) 。
当以上两点都不满足时,返回 \(0\) 。
根据定义,我们显然可以用线性筛求 1-V 的莫比乌斯函数。
Code:
点击查看代码
mu[1]=1;
for(int i=2;i<=V;i++) {
if(!v[i]) {v[prime[++prime[0]]=i]=i; mu[i]=-1;}
for(int j=1;j<=prime[0];j++) {
if(v[i]<prime[j]||prime[j]>V/i) break;
v[i*prime[j]]=prime[j];
if(v[i]==prime[j]) break;
else mu[i*prime[j]]=-mu[i];
}
}
莫比乌斯函数性质:
\(\sum_{d|x} \mu(d)=[x=1]\)
将 \(x\) 质因子展开后可以简略证明。
莫比乌斯反演
其实莫比乌斯反演就是处理一个求和:
\(\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=1]\)
根据莫比乌斯函数性质,可以做出如下变换:
\(\ \ \ \sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=1]\)
\(=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|\gcd(i, j)}\mu(d)\)
\(=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|i,d|j}\mu(d)\)
然后我们去枚举 \(d\) ,计算 \(\mu(d)\) 计算了几次。
\(=\sum_{d=1}^{min(n,m)}\mu(d) \times \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor\)
这个式子的意思是,对于一个 \(d\) ,他在 \(1-n\) 中的约数有 \(\lfloor \frac{n}{d} \rfloor\) 个,\(m\) 同理。
然后我们就可以 \(O(V)\) 预处理 \(\mu\) 函数前缀和后用整除分块单次 \(O(\sqrt{n}+\sqrt{m})\) 计算求和。
例题
ZAP-Queries
题目要求
\(\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=d]\)
其中 \(n, m, d\) 由题目给出,多测。
题解
我们显然可以给所有数除 \(d\) ,因为假如数对 \(\gcd(i,j)=1\) ,将 \(i,j\) 同时乘 \(d\) 便是合法数对。
则要求变为
\(\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}[\gcd(i,j)=1]\)
我们发现这与上面给出的基本模型一致,用相同的方法拆式子求即可。
复杂度 \(O(V+T(\sqrt{n}+\sqrt{m}))\) 。
Code:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int V=1e7;
int v[V+10], prime[V+10], mu[V+10], s[V+10];
void init() {
mu[1]=1;
for(int i=2;i<=V;i++) {
if(!v[i]) {v[i]=i; prime[++prime[0]]=i; mu[i]=-1;}
for(int j=1;j<=prime[0];j++) {
if(prime[j]>V/i) break;
v[i*prime[j]]=prime[j];
if(v[i]==prime[j]) break;
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=V;i++) s[i]=s[i-1]+mu[i];
}
int n, m, d, ans;
signed main(){
// freopn("P3455.in", "r", stdin);
// freopn("P3455.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
init(); int T; cin>>T;
while(T--) {
cin>>n>>m>>d; ans=0;
for(int i=1;i*d<=min(n, m);i++) {
int j=min(n/(n/i), m/(m/i));
ans+=(n/i/d)*(m/i/d)*(s[j]-s[i-1]);
i=j;
} cout<<ans<<'\n';
}
return 0;
}
YY的GCD
以下设质数集合为 \(P\) 。
题目要求
\(\sum_{k \in P}\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k]\)
多测。
题解
我们发现右边部分遇上题一致,于是先化右边式子。
得
\(\sum_{k \in P}\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor} \mu(d) \times \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor\)
但枚举 \(k\) 是 \(\log\) 级别的,无法负担。
于是考虑枚举 \(kd\) ,记为 \(T\) 。
得
\(\sum_{T=1}^{n} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \times (\sum_{d|T}\mu(d)[\frac{T}{d} \in P])\)
我们把右边括号中的式子记为 \(f(x)\) 。
我们发现对于 \(f(1)-f(V)\) 由于只与质数有关,所以可以用埃氏筛求。
复杂度 \(O(V\log\log V+T(\sqrt{n}+\sqrt{m}))\) 。
Code:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int V=1e7;
int v[V+10], prime[V+10], mu[V+10], s[V+10];
void init() {
mu[1]=1;
for(int i=2;i<=V;i++) {
if(!v[i]) {v[i]=i; prime[++prime[0]]=i; mu[i]=-1;}
for(int j=1;j<=prime[0];j++) {
if(v[i]<prime[j]||prime[j]>V/i) break;
v[prime[j]*i]=prime[j];
if(v[i]==prime[j]) break;
else mu[prime[j]*i]=-mu[i];
}
}
for(int j=1;j<=prime[0];j++)
for(int i=1;prime[j]<=V/i;i++)
s[prime[j]*i]+=mu[i];
for(int i=1;i<=V;i++) s[i]+=s[i-1];
}
int ans, n, m;
signed main(){
// freopn("P2257.in", "r", stdin);
// freopn("P2257.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
init();
int T; cin>>T;
while(T--) {
cin>>n>>m; ans=0;
for(int i=1;i<=min(n, m);i++) {
int j=min(n/(n/i), m/(m/i));
ans+=(n/i)*(m/i)*(s[j]-s[i-1]);
i=j;
} cout<<ans<<'\n';
}
return 0;
}

浙公网安备 33010602011771号