莫比乌斯反演
unravel_chess · · 个人记录
———— 非常感谢 newam nesia 热情帮助审核 和 排版,给予了很多知识上的指导
1.莫比乌斯函数的定义
如果将任意一个整数进行质因数分解:
那么就可以据此定义它的莫比乌斯函数:
《蓝书》 上面的定义:
当n包含相等的质因子时候,
当n的所有质因子个不相等的时候:
- 计算n 的所有约数 的 莫比乌斯函数的和
s(n) -
n = 1 , s(n) = \mu(1) = 1 -
n > 1, s(n) = 0
证明:
-
看因式分解
n = p_1^{d_1}p_2^{d_2}p_3^{d_3}···p_k^{d_k} -
n > 1, 所以
k >= 1 -
定义
n 的约数d ,d = p_1^{a_1}p_2^{a_2}p_3^{a_3}···p_k^{a_k}, \space\space\space 0 <= a_i <= d_i -
如果这个约数某一个次数是大于等于
2 , 那就是 0, 就不用考虑a_i >= 2 的情况了 -
那么就只需要计算
a_i == 0 || a_i == 1 的情况了。 -
最后莫比乌斯函数取决于
p_1 到p_k 有多少个取1, 取的情况一共有k 种
-
然后使用二项式定理来计算
-
其实就是
(1 - 1)^k -
和容斥原理的证明极为相似
-
证毕
2.莫比乌斯函数的定理
所谓反演就是反过来表示它:
若
则
大部分题目可以直接套用这个定理,对于
证明:
因为当
那么就看有多少个
-
i | \frac{n}{d} \Leftrightarrow id | n \Leftrightarrow i | \frac{n}{d}
不难看出,后面的项是莫比乌斯函数的和, 就是
因此这一项只有当
由此可以得到下面的式子:
到这里证明完毕。
题目中,一般会采用这个形式:
若
则
那么
就是说
也相当于
从而得出:
- 右半边的和式其实就是上面提到的
S(\frac{i}{n}) 。 - 因此 只有当
i == n 的时候才为1 , 其余时候都为0 。
显然:
- 到这里证明完毕。
一个题目:Luogu Problem B
[HAOI2011]Problem b
题目描述
对于给出的
输入格式
第一行一个整数
输出格式
共
样例 #1
样例输入 #1
2
2 5 1 5 1
1 5 1 5 2
样例输出 #1
14
3
提示
对于
解
这是一个容斥原理的思想, 在最后统计结果的时候会用到
-
求
f(k) -
枚举
n 的倍数,枚举完:</font>F(n) = \sum_{n | d}f(d) -
带入莫比乌斯反演定理, 发现
f(n) 是可以使用公式来算的:
-
找
x 和y 都是d 的倍数的点 , -
有
\left\lfloor \frac{a}{d} \right\rfloor 个x , 有\left\lfloor \frac{b}{d} \right\rfloor 个y -
任取一个
x 或者y 都可以满足条件
- 用
d' 表示\frac{d}{n} , 因为更加直观。
-
a' = \frac{a}{n} $ $ b' = \frac{b}{n}
-
形如
\left\lfloor \frac{k}{x} \right\rfloor 的东西, 当x > k 的时候一定是下取整的结果,只有O\sqrt{n} 种取值 -
可以使用整数分块
- 一个公式
-
这是和
x 相等的一段的最后一个点的值。 -
对于
a' 而言,有O\sqrt{a'} 个 -
对于
b' 而言,有O\sqrt{b'} 个 -
只需要从
1 看到min(a', b') 就可以了
也就是说{\left\lfloor \frac{a'}{d'} \right\rfloor} {\left\lfloor \frac{b'}{d'} \right\rfloor} 的值是相同的
- 让这个相同值为
C
可以发现这个是莫比乌斯函数的和。
先预处理一遍莫比乌斯函数的前缀和,
从而在
很多莫比乌斯反演的题差不多都是这个过程。
二倍经验, Nice!
P3455 [POI2007]ZAP-Queries
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5;
int prime[N], cnt, mu[N];
bool st[N];
int sum[N];
int T;
int a, b, c, d, k;
void init()
{
mu[1] = 1;
for (int i = 2; i < N; i++)
{
if (!st[i])
{
prime[++cnt] = i,
mu[i] = -1;
}
for (int j = 1; prime[j] * i < N; j++)
{
st[prime[j] * i] = true;
if (i % prime[j] == 0)
{
break;
}
mu[prime[j] * i] = -mu[i];
}
}
for (int i = 1; i < N; i++)
sum[i] = sum[i - 1] + mu[i];
}
int g(int k, int x)
{
return k / (k / x);
}
int f(int a, int b, int k)
{
a = a / k, b = b / k;
int res = 0;
int n = min(a, b);
for (int l = 1, r; l <= n; l = r + 1)
{
r = min(n, min(g(a, l), g(b, l)));
res += (sum[r] - sum[l - 1]) * (a / l) * (b / l);
}
return res;
}
signed main()
{
ios::sync_with_stdio(false);
init();
cin >> T;
while (T--)
{
cin >> a >> b >> c >> d >> k;
cout << f(b, d, k) - f(a - 1, d, k) - f(b, c - 1, k) + f(a - 1, c - 1, k) << "\n";
}
return 0;
}
类似应用了莫比乌斯反演的题目 Luogu P2398 GCD SUM
Next \space\space Question \space\space Luogu 约数个数和
题目要求
- 转化一下
d(i*j) d(i * j) = \sum_{x | i}\sum_{y | j} [gcd(x, y) == 1] - 这一步骤很难想滴。
解释这个转化:
- 可以去看Siyuan的博客,在后面有莫比乌斯函数对此的证明。
<!-- 考虑把每一个因子一一映射。
如果
- 如果
c <= a ,那么在i 中选择。 - 如果
c > a , 那么我们将c 减去a , 在j 中选择p^{c - a} (在j 中选择p^e 表示的是p^{a + e} ) -->
证明:
-
那这样:
约数个数:
(d_1 + a_1 + 1)(d_2 + a_2 + 1) ···(d_k + a_k + 1) -
然后取
p 的时候,不是x 取p ,\space y = 1 , 就是翻过来的情况。
证明完毕。
设:
则:
所以:
- 考虑
F(d) 怎么求:
设:
那么:
设:
有:
令:
则:
-
tips:$ 这是到 $ min(N, M) -
这个
H(x) 是可以先预处理的, 然后这玩意又得分块去做 -
再预处理一遍前缀和,利用和上面那个题目类似的方法去分块, 大概有
O(\sqrt{max(n ,m)}) 块,每一块的左右端点也是可以用g(x) 知道的。 -
总的来说,使用了两次分块
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e4 + 10;
int T;
int n, m;
int prime[N], cnt, mu[N];
int sum[N], h[N];
bool st[N];
int g(int k, int x)
{
return k / (k / x);
}
void init()
{
mu[1] = 1;
for (int i = 2; i <= N; i++)
{
if (!st[i])
{
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; prime[j] * i <= N; j++)
{
st[prime[j] * i] = true;
if (i % prime[j] == 0)
break;
mu[prime[j] * i] = -mu[i];
}
}
for (int i = 1; i <= N; i++)
sum[i] = sum[i - 1] + mu[i];
for (int i = 1; i <= N; i++)
{
for (int l = 1, r; l <= i; l = r + 1)
{
r = min(i, g(i, l));
h[i] += (r - l + 1) * (i / l);
}
}
return;
}
signed main()
{
init();
cin >> T;
while (T--)
{
cin >> n >> m;
int res = 0;
int k = min(n, m);
for (int l = 1, r; l <= k; l = r + 1)
{
r = min(k, min(g(n, l), g(m, l)));
res += (sum[r] - sum[l - 1]) * h[n / l] * h[m / l];
}
cout << res << endl;
}
return 0;
}
对前面的一个补充结论:
- 这个东西很重要,是莫比乌斯函数最常用的了。
-
[n==1]$表示只有当$n=1$成立时,返回值为$1$;否则返回值为$0
说白了,就是那个