莫反
前置知识
一些数论函数
狄利克雷卷积
数论函数 \(f(n)\) 和 \(g(n)\) 的狄利克雷卷积,记作 \(f * g\),定义为数论函数:
莫比乌斯函数
定义
例如:\(\mu(12) = 0\),\(\mu(30) = -1\),\(\mu(210) = 1\)。
性质
$\large \bm {证明}$
容易发现,当 \(n = 1\) 时完全正确。
令 \(f(n) = \sum_{d|n}\mu(d)\)。
对于 \(\forall n = p1^{k1} \cdot p2^{k2} \cdots\),若 \(\exists k_i, k_i > 1\),则此时 \(\mu(n) = 0\),可以忽略。
故在此只有 \(x\) 形如 \(n = p1 \cdot p2 \cdot p3 \cdots\) 会对答案产生影响。
故只需考虑因子个数 \(k\)。
由组合数学,有 \(f(n) = \sum_{i = 0}{k} \binom{k}{i}(-1)^i = (1 - 1)^k = 0^k\)。
得证。
推论等式
预处理
由定义易知,\(\mu(n)\) 是积性函数(但不是完全积性函数),即对于 \(\forall (x, y), gcd(x, y) = 1\),有 \(\mu(x\cdot y) = \mu(x) \cdot \mu(y)\)。
则根据积性函数的性质,我们可以很容易地使用埃氏筛或线性筛预处理 \(1\sim n\) 的莫比乌斯函数。
//埃氏筛
bitset<10000010> vis;
int mu[1000010];
void init_e(int n){
for(int i = 1; i <= n; i ++) mu[i] = 1;
for(int i = 2; i <= n; i ++) if(!vis[i]){
mu[i] = -1;
for(int j = 2 * i; j <= n; j += i) {
vis[j] = 1;
if((j / i) % i == 0) mu[j] = 0;
else mu[j] *= -1;
}
}
}
// 线性筛
bitset<10000010> vis;
int mu[1000010];
int p[1000010], cnt;
void init_x(int n) {
mu[1] = 1;
for(int i = 2; i <= n; i ++){
if (!vis[i]) p[cnt ++] = i, mu[i] = -1;
for (int j = 0; p[j] * i <= n; j++) {
vis[p[j] * i] = true;
if (i % p[j] == 0) break;
mu[i * p[j]] = -mu[i];
}
}
}
莫比乌斯反演
公式
$\large \bm {证明}$
以从右向左证为例:
\[\sum_{d | n} f(d) \cdot \mu(\frac{n}{d}) \]\[= \sum_{d | n} \mu(\frac{n}{d}) \sum_{d' | d} g(d') \]\[= \sum_{d'|n}g(d')\sum_{d} [\frac{n}{d} | \frac{n}{d'}] \mu (\frac{n}{d}) \]\[= \sum_{d'|n}g(d')\sum_{d} [d'' | \frac{n}{d'}] \mu (d'') \]\[= \sum_{d'|n}g(d')[\frac{n}{d'}=1] = g(n) \]证毕。
一般用法
莫反主要用来推式子。
下面以一道题为例:
求 \(\sum_{i = 1}^{n}\sum_{j = 1}^{m} \gcd(i,j)\),其中 \(n \le m \le 10^7\)。
推式子:
将原本的求和拆分成求个数:
将 \(gcd\) 中的 \(k\) 消掉:
则含 \(gcd\) 的表达式由 \(\epsilon\) 定义得:
由 \(\mu * 1 = \epsilon\) 替换得:
交换顺序,提前 \(\mu\) 得:
发现右边部分可以直接求和,由此直接得:
令 \(T = dk\),
由 \(id * \mu = \varphi\) 得:
至此,式子全部推完。
那么,针对当前这个式子,我们神奇地发现它可以使用数论分块 \(\mathcal O(\sqrt{n})\) 处理完成。
最终时间:\(\mathcal O(n\sqrt{n})\)。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
bitset<10000010> vis;
int phi[1000010], s[1000010];
int p[1000010], cnt;
void init(int n){
vis[1] = 1;
s[1] = 1;
for(int i = 2; i <= n; i ++){
if(!vis[i]) p[++ cnt] = i, phi[i] = i - 1;
for(int j = 1; j <= cnt && p[j] * i <= n; ++ j){
vis[i * p[j]] = 1;
if(!(i % p[j])) { phi[i * p[j]] = phi[i] * p[j]; break; }
phi[i * p[j]] = phi[i] * (p[j] - 1);
}
s[i] = s[i - 1] + phi[i];
}
}
signed main(){
int n; scanf("%lld", &n);
init(n);
int ans = 0;
for(int i = 1, j; i <= n; i = j + 1){
j = n / (n / i);
ans += (n / i) * (n / i) * (s[j] - s[i - 1]);
}
printf("%lld\n", ans);
return 0;
}
恭喜你,已经学会了莫反,去推更多的式子吧!
例题们
[集训队互测 2010] Crash的数字表格 / JZPTAB
题意:求 \(\sum_{i = 1}^n \sum_{j = 1}^m \text{lcm}(i, j)\)
解法:
P3911 最小公倍数之和
求 \(\sum_{i = 1}^{n}\sum_{j = 1}^{n}\text{lcm}(a_i, a_j)\)。
我们发现直接求并不可求,因为 \(a_i\) 和 \(i\) 没有关系,于是我们更改策略。
让我们用 \(c_i\) 表示 \(i\) 这个数在 \(a\) 中出现的次数,记 \(n\) 为 \(\max\{a_i\}\)。
那么原式就等价于:
使用线性筛和预处理即可 \(O(n \log{n})\) 通过本题。

浙公网安备 33010602011771号