莫比乌斯反演
莫比乌斯函数
定义
定义莫比乌斯函数
即:在 \(x \neq 1\) 时,对 \(x\) 分解质因数,如果其中有一个质因子的幂次大于等于 \(2\) ,则有 \(\mu(x) = 0\) ;否则,若 \(x\) 有奇数个质因子,则 \(\mu(x) = -1\) ,若 \(x\) 有偶数个质因子,则 \(\mu(x) = 1\)。
在 \(x = 1\) 时,有 \(\mu(x) = 1\)。
性质
-
莫比乌斯函数是积性函数
即:对于任意互质的整数 \(a , b\) ,都有 \(\mu(ab)=\mu(a) \mu(b)\) ,利用这个性质,可以利用欧拉筛在 \(\Theta(n)\) 的时间内求出 \(\forall_{i \leq n} \mu(i)\) ,并可用杜教筛快速计算 \(\sum\limits_{i=1}^{n} \mu(i)\)
-
莫比乌斯函数 \(\mu\) 在迪利克雷卷积中为常数函数 \(1\) 的逆元。
即:对任意数论函数 \(f(x)\)、\(g(x)\) ,若有 \(f(n)=\sum\limits_{d|n}g(d)\) 则有 \(g(n)=\sum\limits_{d|n}f(d)\mu(\frac{n}{d})\) ,这里数论函数 $g(n) 即为数论函数 \(f(n)\) 的莫比乌斯反演。
重要结论
- \(\epsilon(x) = \sum\limits_{d|x} \mu(d)\)
即
证明:由性质 2 可以推出 \(\mu*1=\epsilon\) ,写成迪利克雷卷积即可。
-
\([\gcd(i,j)=1] = \sum\limits_{d|\gcd(i,j)}\mu(d)\)
证明:由结论1可得,\(\sum\limits_{d|\gcd(i,j)}\mu(d) = \epsilon(\gcd(i,j))\) ,当且仅当 \(\gcd(i,j) = 1\) 时,其值为 \(\epsilon(1) = 1\)
莫比乌斯函数求值
莫比乌斯反演一般会用到莫比乌斯函数的前缀和,下面提供两种计算方法,各有优劣。
欧拉筛
欧拉筛适用于对积性函数求值,上文提到 \(\mu\) 为积性函数,故可以利用欧拉筛在 \(\Theta(n)\) 的时间内求出 \(\forall_{i \leq n} \mu(i)\)。
通过欧拉筛求积性函数的值,需要确定 \(3\) 种情况下函数的取值:\(x\) 为质数、 \(x = y \cdot p\) 且 \(y \mod p \neq 0\) 、 \(x = y \cdot p\) 且 \(y \mod p = 0\) ,其中 \(p\) 是 \(x\) 的最小质因子。
-
当 \(x\) 为质数时,根据定义可得 \(\mu(x)=1\)
-
当 \(x = y \cdot p\) 且 \(y \mod p \neq 0\) 时,一定有 \(p^2|x\) ,根据定义, \(mu(x)=0\)
-
当 \(x = y \cdot p\) 且 \(y \mod p = 0\) 时,若 \(y\) 含有平方因子,则 \(\mu(x)=\mu(y)=0\) ;若 \(y\) 不含平方因子,根据定义可知:\(\mu(x)=-\mu(y)\) 。综上,当 \(y \mod p = 0\) 时, \(\mu(x)=-mu(y)\)
由此可以写出线性时间求解莫比乌斯函数的代码:
inline void Euler(int n) {
mu[1]=1; //根据定义得
for(int i=2;i<=n;i++) {
if(!vis[i]) mu[i]=-1,pr[++pcnt]=i; //情况1:质数
for(int j=1;j<=pcnt&&i*pr[j]<=n;j++) {
vis[i*pr[j]]=true;
if(i%pr[j]==0) {
mu[i*pr[j]]=0;break; //情况2:相同质因子
}
mu[i*pr[j]]=-mu[i]; //情况3:新的不同质因子
}
}
}
杜教筛
有时题目要求快速计算 \(\mu(i)\) 的前缀和,即 \(\sum\limits_{i=1}^{n}\mu(i)\) 的值,这时杜教筛可以在低于 \(\Theta(n)\) 的时间内求出前缀和在某个位置上的值。
杜教筛要求找出一个 \(g(x)\) 与当前函数 \(f(x)\) 做迪利克雷卷积,且卷积的前缀和好求,而根据之前提到的性质: \(\mu * 1 = \epsilon\) ,\(f(x)\) 直接取常数函数 \(1\) 即可。
定义 \(sum(x)=\sum\limits_{i=1}^{x} \mu(i)\)
根据杜教筛的式子 \(g(1)sum(n)=\sum\limits_{i=1}^{n}(f * g)(i) - \sum\limits_{i=2}^{n} g(i)sum(\lfloor \frac{n}{i} \rfloor)\) ,用常数函数 \(1\) 替换 \(g\) ,用 \(\mu\) 替换 \(f\) 可得: \(sum(n)=\sum\limits_{i=1}^{n}\epsilon(i) - \sum\limits_{i=2}^{n} sum(\lfloor \frac{n}{i} \rfloor)\) ,显然, \(\sum\limits_{i=1}^{n}\epsilon(i) = 1\),而\(\sum\limits_{i=2}^{n} sum(\lfloor \frac{n}{i} \rfloor)\) 可用数论分块在加速计算。
直接递归计算的时间复杂度为 \(\Theta(n^{\frac{3}{4}})\) ,如果提前用欧拉筛计算出出前 \(n^{\frac{2}{3}}\) 项的前缀和,则时间复杂度可以降低至 \(\Theta({n^{\frac{2}{3}}})\)
代码:
unordered_map<long long,long long>mp1; //map是O(logn)查询修改,选用unordered_map可以获得更优的O(1)查询修改
long long SumMu(long long n) { //先调用 Euler(MAXN-1) 预处理前 MAXN 项的 sumMu
if(n<MAXN) return sumM[n]; //如果当前的n在预处理的范围内,就直接返回
if(mp.find(n)!=mp.end()) return mp[n]; //如果当前的值之前求过,就直接返回
long long res=1; //epsilon前缀和=1
for(long long i=2;i<=n;i++) { //数论分块
int j=n/(n/i);
res-=1ll*(j-i+1)*SumMu(n/i);
i=j;
}
mp[n]=res; //记忆化加速下次查询
return res;
}
应用
[POI2007]ZAP-Queries
给出 \(a,b,d\),求 \(\sum\limits_{x=1}^{a}\sum\limits_{y=1}^{b} [\gcd(x,y)=d]\),多组数据。
上文证明 \([\gcd(x,y)=1]=\sum\limits_{d|\gcd(x,y)}\mu(d)\) ,而题目要求 \([\gcd(x,y)=d]\)。考虑将 \(d\) 化成 \(1\) ,用 \(x \cdot d\) 换 \(x\),\(y \cdot d\) 换 \(y\),得到 \(\sum\limits_{x=1}^{\lfloor \frac{a}{d} \rfloor}\sum\limits_{y=1}^{\lfloor \frac{b}{d} \rfloor}[\gcd(x,y)=1]\)。
令 \(f(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=1]\),用莫比乌斯函数替换 \(\gcd(i,j)=1\) 的条件,即有 \(f(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{k|i,k|j}\mu(k)\)。考虑先枚举 \(k\) ,发现 \(k\) 的范围为 \([1,\min(n,m)]\) 令 \(n \leq m\),即可将上式化为 \(f(n,m)=\sum\limits_{k=1}^{n}\mu(k)\sum\limits_{k|i}^{n}\sum\limits_{k|j}^{m}1\)。由于小于 \(n\) 的 \(k\) 的倍数共有 \(\lfloor\frac{n}{k}\rfloor\) 个,\(m\) 同理,则有 \(f(n,m)=\sum\limits_{k=1}^{n}\mu(k)\lfloor\frac{n}{k}\rfloor\lfloor\frac{n}{k}\rfloor\),预处理出 \(\mu\) 的前缀和后,利用数论分块可在 \(\Theta(\sqrt{n})\) 的时间内计算 \(f(n,m)\)。
题目要求的式子就等于 \(f(\lfloor\frac{a}{d}\rfloor,\lfloor\frac{b}{d}\rfloor)\),利用欧拉筛预处理 \(\mu\) 的前缀和,即可实现 \(\Theta(n)\) 预处理, \(O(\sqrt{n})\) 回答一次询问。
回答询问代码:
inline int calc(int n,int m) {
if(n>m) swap(n,m);
int res=0;
for(int i=1;i<=n;i++) { //数论分块
int j=min(n/(n/i),m/(m/i));
res+=(sum[j]-sum[i-1])*(n/i)*(m/i); //sum为mu的前缀和
i=j;
}
return res;
}
PGCD - Primes in GCD Table
给定 \(n\) 和 \(m\) ,求 \(1 \leq x \leq n,1 \leq y \leq m\) 且 \(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对,至多 \(10\) 次询问。
即是求: \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(n,m) \in Prime]\)。莫比乌斯反演套路:先枚举 \(\gcd(i,j)\) 的值,可将原式化为 \(\sum\limits_{g \in Prime}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=g]\) ,继续用上一题的套路,将 \(g\) 化为 \(1\),得到 \(\sum\limits_{g \in Prime}\sum\limits_{i=1}^{\lfloor \frac{n}{g}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{g} \rfloor}[\gcd(i,j)=1]\),转化 \(\gcd(i,j)=1\) 的条件得到:\(\sum\limits_{g \in Prime}\sum\limits_{i=1}^{\lfloor \frac{n}{g}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{g} \rfloor}\sum\limits_{d|i,d|j}\mu(d)\)。和上一题一样,令 \(f(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=1]\),则有 \(f(n,m)=\sum\limits_{k=1}^{n}\mu(k)\lfloor\frac{n}{k}\rfloor\lfloor\frac{n}{k}\rfloor\),即可将原式化为 \(\sum\limits_{g \in Prime} f(\lfloor \frac{n}{g} \rfloor,\lfloor \frac{m}{g} \rfloor)\) ,令 \(g(x)=[x \in Prime]\),显然,\(g\) 的前缀和可以在筛出质数后 \(\Theta(n)\) 预处理,则可以将原式化为 \(\sum\limits_{i=1}^{n} g(i) f(\lfloor \frac{n}{g} \rfloor,\lfloor \frac{m}{g} \rfloor)\),用数论分块求解即可。
这里是两层数论分块的嵌套,回答一次询问的时间复杂度为 \(\Theta(n^{\frac{3}{4}})\) ,本题询问量不大,可以通过。
回答询问代码:
inline long long f(int n,int m) { //数论分块求f(n,m)
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) {
int j=min(n/(n/i),m/(m/i));
res+=1ll*(smu[j]-smu[i-1])*(n/i)*(m/i);
i=j;
}
return res;
}
inline long long calc(int n,int m) {
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) {
int j=min(n/(n/i),m/(m/i));
res+=1ll*(spr[j]-spr[i-1])*f(n/i,m/i); //spr为g的前缀和
i=j;
}
return res;
}
YY的GCD
给定 \(n\) 和 \(m\) ,求 \(1 \leq x \leq n,1 \leq y \leq m\) 且 \(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对,至多 \(10^4\) 次询问。
随着询问次数的增加,上一题的方法已经无法通过,考虑优化。
拆开 \(f\) 得到:\(\sum\limits_{i=1}^{n} g(i) \sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor} \lfloor \frac{n}{ij} \rfloor \lfloor \frac{m}{ij} \rfloor \mu(j)\),令 \(k=i \cdot j\),则原式化为 \(\sum\limits_{i=1}^{n} g(i) \sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor} \lfloor \frac{n}{k} \rfloor \lfloor \frac{m}{k} \rfloor \mu(\frac{k}{i})\) ,变换枚举顺序,先枚举 \(k\) ,得到 \(\sum\limits_{k=1}^{n}\sum\limits_{i|k}g(i)\mu( \frac{k}{i}) \lfloor\frac{n}{k}\rfloor \lfloor\frac{m}{k}\rfloor\)。可以发现,最后两项与 \(i\) 无关,可以提到前面,得到 \(\sum\limits_{k=1}^{n}\lfloor\frac{n}{k}\rfloor \lfloor\frac{m}{k}\rfloor\sum\limits_{i|k}g(i)\mu(\frac{k}{i})\)。令 \(h(x)=\sum\limits{i|x}g(i)\mu(\frac{k}{i})\),即有 \(h(x)=\sum\limits_{i \in Prime,i|x}\mu(\frac{i}{i})\)。推导至此,即可在欧拉筛后枚举每个质数的倍数计算 \(h(x)\) 的值,认为质数个数约为 \(\frac{n}{\ln n}\) 时,计算 \(h(x)\) 的时间复杂度约为为 \(\Theta(n)\),可以通过。
关键代码:
inline void init(int n) {
Euler(n); //欧拉筛质数和mu
for(int i=1;i<=pcnt;i++) { //枚举质数求h的值
for(int j=1;pr[i]*j<=n;j++) {
sum[pr[i]*j]+=mu[j];
}
}
for(int i=1;i<=n;i++) {
smu[i]=smu[i-1]+mu[i];
sum[i]+=sum[i-1];
}
}
inline long long calc(int n,int m) {
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) { //数论分块
int j=min(n/(n/i),m/(m/i));
res+=1ll*(sum[j]-sum[i-1])*(n/i)*(m/i);
i=j;
}
return res;
}
当然,\(h(x)\)的值也可用欧拉筛在线性时间内筛出.
和之前考虑欧拉筛 \(\mu\) 一样,仍然考虑欧拉筛会遇到的三种情况:\(x\) 为质数、 \(x = y \cdot p\) 且 \(y \mod p \neq 0\) 、 \(x = y \cdot p\) 且 \(y \mod p = 0\) ,其中 \(p\) 是 \(x\) 的最小质因子。
-
当 \(x\) 为质数时,由于 \(1\) 不是质数,故只会枚举到 \(i=x\) 的情况,即当 \(x\) 为质数时, \(h(x)=\mu(\frac{x}{x})=\mu(1)=1\)。
-
当 \(x = y \cdot p\) 且 \(y \mod p \neq 0\) 时,\(p\) 是 \(x\) 相比于 \(y\) 多出的一个新的质因子,这会导致 \(h(y)\) 中枚举到的 \(\frac{y}{i}\) 变成 \(\frac{yp}{i}\),根据 \(\mu\) 的定义得出 \(\mu(\frac{yp}{i})=-mu(\frac{y}{i})\),则有\(\mu(\frac{x}{i})=-mu(\frac{y}{i})\)。再考虑由于 \(p\) 而多出来的部分,因为枚举到的 \(i\) 为质数,故只会多枚举一个 \(p\),则只会多贡献一个 \(\mu(\frac{x}{p})=\mu(y)\)。 由此得出:当 \(x = y \cdot p\) 且 \(y \mod p \neq 0\) 时, \(h(x)=-h(y)+\mu(p)\)。
-
当 \(x = y \cdot p\) 且 \(y \mod p = 0\) 时,\(p\) 是 \(x\) 的一个幂次大于等于 \(2\) 的质因子,根据 \(\mu\) 定义,由于枚举到的 \(i\) 均为质数,故当 \(i \neq p\) 时,所有的 \(\mu(\frac{x}{i})=0\)。最后剩下一个新枚举到的 \(p\) ,对结果的贡献为 \(\mu(\frac{x}{p})=\mu(y)\)。由此得出:当 \(x = y \cdot p\) 且 \(y \mod p = 0\) 时, \(h(x)=\mu(y)\)
据此可以利用欧拉筛在 \(\Theta(n)\) 的时间内求解 \(h(x)\) 的值和前缀和,略快于上一个做法。
关键代码:
inline void Euler(int n) {
mu[1]=1;f[1]=0; //根据定义得到
for(int i=2;i<=n;i++) {
if(!vis[i]) mu[i]=-1,pr[++pcnt]=i,f[i]=1; //情况1
for(int j=1;j<=pcnt&&i*pr[j]<=n;j++) {
vis[i*pr[j]]=true;
if(i%pr[j]==0) {
mu[i*pr[j]]=0;
f[i*pr[j]]=mu[i];break; //情况2
}
mu[i*pr[j]]=-mu[i];
f[i*pr[j]]=-f[i]+mu[i]; //情况3
}
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+f[i];
}
inline long long calc(int n,int m) {
if(n>m) swap(n,m);
long long res=0;
for(int i=1;i<=n;i++) { //数论分块
int j=min(n/(n/i),m/(m/i));
res+=(sum[j]-sum[i-1])*(n/i)*(m/i);
i=j;
}
return res;
}
To be continued...

浙公网安备 33010602011771号