莫比乌斯反演
laihaochen · · 个人记录
目录
1. 两个引理
2. 常见的数论函数
3. 整数分块
4. 积性函数
5. 欧拉函数
6. 狄利克雷卷积
7. 莫比乌斯函数
8. 四个技巧
9. 反演公式
10. 应用
by laihaochen
1. 两个引理
\forall a, b, c \in \mathbb{Z}, \lfloor{\frac{a}{bc}}\rfloor = \left\lfloor{\frac{\lfloor{\frac{a}{b}}\rfloor}{c}}\right\rfloor
证明引理
设
又
证明引理
-
当
d \leq \sqrt{n} 时,\lfloor{\frac{n}{d}}\rfloor 至多有\sqrt{n} 种取值。 -
当
d > \sqrt{n} 时,\lfloor{\frac{n}{d}}\rfloor < \sqrt{n} ,也至多有\sqrt{n} 种取值。
故
2. 常见的数论函数
-
单位函数:
\epsilon(n) = [n = 1] ,[n = 1] 可以理解为一个bool 值,当n = 1 时,[n = 1] = 1 ,当n \neq 1 时,[n = 1] = 0 -
常数函数:
1(n) = 1 -
恒等函数:
id(n) = n -
除数函数:
\sigma_k(n) = \sum_{d | n} d^k ,特别地\sigma_0(n) 通常被记为d(n) 表示n 的因数个数,\sigma_1(n) 通常被记为\sigma(n) 表示因数和。
3. 整数分块
对于任意一个
i (i \leq n) ,我们需要找到一个最大的j(i \leq j \leq n) ,使得\left\lfloor\frac{i}{n}\right\rfloor = \left\lfloor\frac{j}{n}\right\rfloor ,而j = \left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor 。
证明:
由引理
所以我们在求类似
例题:P2261 [CQOI2007]余数求和
题目大意:求
所以,我们可以用整数分块求出
由引理
#include <cstdio>
#include <algorithm>
using namespace std;
long long n, k, gx;//记得开long long
int main() {
scanf ("%lld%lld", &n, &k);
long long ans = n * k;
for (int x = 1; x <= n; x = gx + 1) {
if (k / x == 0) {//如果为0了,那从x到n任意一数的k / x都为零
gx = n;
} else {
gx = min (k / (k / x), n);
}
ans -= (k / x) * (x + gx) * (gx - x + 1) / 2;//等差数列求和公式
}
printf ("%lld\n", ans);
return 0;
}
4. 积性函数
定义:
性质:
若
f, g 为积性函数,则当h(x) = f(x^p), f^p(x), f(x)g(x), \sum_{d | x}f(d)g(\frac{x}{d}) 时,h(x) 也为积性函数。
证明第一个:
又
又
又
又
又
又
又
又
通式:
证明:
又
欧拉函数是积性函数:
证明:
又
不难发现,每一列的所有元素构成模
故有且仅有
故共有
所以欧拉函数是积性函数。
因为欧拉函数是积性函数,所以可以利用线性筛来求。
在求之前,还要推出一个性质:
当
证明:
首先,证明两个引理:
- 当
x, i 不互质,则x + i, i 不互质
设
- 当
x, i 互质,则x + i, i 互质(x < i)
反证:假设
又因为
故
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {//i是质数,phi[i] = i - 1
prime[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && prime[j] * i <= n; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];//性质
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];//利用phi的积性
}
}
6. 狄利克雷卷积
定义:
数论函数
性质:
- 狄利克雷卷积满足交换律和结合律
证明交换律:
令
证明结合律:
- 两个积性函数的卷积也为积性函数
在积性函数的第四个已经证明。
\epsilon 为狄利克雷卷积的单位,即任何函数与\epsilon 都为函数本身。
一些常用的卷积:
下面证明第
设
所以只需证明
设
又
7. 莫比乌斯函数
定义:
可以这样理解:
设
证明:
- 若
\mu(x) = 0 或\mu(y) = 0
则说明
- 若
\mu(x), \mu(y) \neq 0
综上,
因为
mu[1] = 1;//定义
for (int i = 2; i <= n; i++) {
if (!vis[i]) {//i是质数
prime[++cnt] = i;//记录质数
mu[i] = -1;//质数肯定为-1
}
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {//线性筛模板
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
mu[i * prime[j]] = -mu[i];//乘上一个质数就要改变一次符号,当mu[i]是0时还是为0
}
}
性质:
-
\mu * 1 = \epsilon$,也即 $\sum_{d | n} \mu(d) = \epsilon(n)
证明:
-
当
n = 1 时,成立。 -
当
n \neq 1 时,令n = \prod_{i = 1}^k p_i^{c_i} ,\forall d | n 仅当d 无平方因子时对答案才产生贡献。
结合二项式定理可得:
证明:
又
8. 四个技巧
1. \sum_{d | n}\frac{\mu(d)}{d} = \frac{\varphi(n)}{n}
2. \sum_{d | n}\mu(d) = \epsilon = [n = 1]
前面证过。
3. \sum_{i = 1}^n\sum_{j = 1}^mf(i) = \sum_{i = 1}^nf(i)\sum_{j = 1}^m
显然成立。
4. \sum_{k | n} = \sum_{k = 1}^n [k | n]
显然成立。
9. 反演公式
形式一:
若
F(n) = \sum_{d | n} f(d) ,则f(n) = \sum_{d | n}\mu(d)F(\frac{n}{d})
证明:
这一步其实就是更换了
形式二:
若
F(n) = \sum_{n | d} f(d) ,则f(n) = \sum_{n | d} \mu(\frac{d}{n})F(d)
证明:
设
这一步其实就是更换了
一般来说,形式一会比形式二更常用,因为它跟狄利克雷卷积很像。
10. 应用
终于开始实践了
P3455 [POI2007]ZAP-Queries
题目大意:求
(思路:看到了
我们不妨设
我们先把
通过技巧
通过技巧
因为
通过技巧
所以我们有
(下面的
我们发现
所以我们也要求出
这样我们就得到了
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 5e4 + 5;
int n, m, k, cnt, prime[N], mu[N], sum[N];
bool vis[N];
void init (int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + mu[i];
}
}
void solve () {
n /= k, m /= k;
int gx, ans = 0;
for (int x = 1; x <= min (n, m); x = gx + 1) {
gx = min (n / (n / x), m / (m / x));
ans += (sum[gx] - sum[x - 1]) * (n / x) * (m / x);
}
printf ("%d\n", ans);
}
int main() {
init(5e4);
int T;
scanf ("%d", &T);
while (T--) {
scanf ("%d%d%d", &n, &m, &k);
solve ();
}
return 0;
}
P2522 [HAOI2011]Problem b
题目大意:
与
这里就不放代码了。
P2257 YY的GCD
题目大意:求
这次推的就较为简略了
根据
令
我们发现
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e7 + 5;
int n, m, cnt, prime[N];
bool vis[N];
long long f[N], sum[N], mu[N];
void init (int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for (int i = 1; i <= cnt; i++) {
for (int j = 1; j * prime[i] <= n; j++) {
f[j * prime[i]] += mu[j];
}
}
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + f[i];
}
}
void solve () {
long long ans = 0;
for (int x = 1, gx; x <= min (n, m); x = gx + 1) {
gx = min (n / (n / x), m / (m / x));
ans += (long long)(sum[gx] - sum[x - 1]) * (n / x) * (m / x);
}
printf ("%lld\n", ans);
}
int main() {
init(1e7);
int T;
scanf ("%d", &T);
while (T--) {
scanf ("%d%d", &n, &m);
solve ();
}
return 0;
}
P1829 [国家集训队]Crash的数字表格 / JZPTAB
题目大意:求
令
令
我们可以利用整数分块来求解。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e7 + 5, mod = 20101009;
int n, m, ans, mu[N], sum[N];
bool notprime[N];
void init () {
for (int i = 1; i <= n; i++) {
mu[i] = 1;
}
for (int i = 2; i <= n; i++) {
if (notprime[i]) {
continue;
}
mu[i] = -1;
for (int j = 2 * i; j <= n; j += i) {
notprime[j] = 1;
if ((j / i) % i == 0) {
mu[j] = 0;
} else {
mu[j] *= -1;
}
}
}
//根据不同的要求求不同的前缀和
for (int i = 1; i <= n; i++) {
sum[i] = (sum[i - 1] + (1ll * i * i % mod * (mu[i] + mod))) % mod;
}
}
int g (int x, int y) {
return (1ll * (x + 1) * x / 2 % mod) * (1ll * (y + 1) * y / 2 % mod) % mod;
}
int getsum (int a, int b) {
int gx, res = 0;
for (int x = 1; x <= a; x = gx + 1) {
gx = min (a / (a / x), b / (b / x));
res = (res + 1ll * (sum[gx] - sum[x - 1] + mod) * g(a / x, b / x) % mod) % mod;
}
return res;
}
int main() {
scanf ("%d%d", &n, &m);
if (n > m) {
swap (n, m);
}
init();
int gx;
for (int x = 1; x <= n; x = gx + 1) {
gx = min (n / (n / x), m / (m / x));
ans = (ans + (1ll * (gx - x + 1) * (x + gx) / 2) % mod * getsum (n / x, m / x) % mod) % mod;
}
printf ("%d\n", ans);
return 0;
}
P3327 [SDOI2015]约数个数和
题目大意:求
首先证明一个引理:
d(ij) = \sum_{x | i}\sum_{y | j}[\gcd(x, y) = 1]
证明:
我们先考虑
-
等式左边有
p^0, p^1, ……, p^{a + b} 共(a + b + 1) 种因子,故LHS = a + b + 1 -
等式右边可以分三种情况讨论:
-
x = p^m, 1 \leq m \leq a
共
-
y = p^n, 1 \leq n \leq b
共
-
x = 0, y = 0
所以当
因为
有了这个引理,我们可以将
应用整数分块即可。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 50005;
int n, m, cnt, prime[N], mu[N], sum[N];
bool vis[N];
long long g[N];
void init (int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + mu[i];
}
for (int i = 1; i <= n; i++) {
int gx;
for (int x = 1; x <= i; x = gx + 1) {
gx = i / (i / x);
g[i] += (i / x) * (gx - x + 1);
}
}
}
int main() {
init(50000);
int T;
scanf ("%d", &T);
while (T--) {
scanf ("%d%d", &n, &m);
int gx;
long long ans = 0;
for (int x = 1; x <= min (n, m); x = gx + 1) {
gx = min (n / (n / x), m / (m / x));
ans += 1ll * (sum[gx] - sum[x - 1]) * g[n / x] * g[m / x];
}
printf ("%lld\n", ans);
}
return 0;
}
P1447 [NOI2010]能量采集
题目大意:求
令
同样用线性筛
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
long long n, m, cnt, ans, prime[N], phi[N], sum[N];
bool vis[N];
void init (int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prime[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && prime[j] * i <= n; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + phi[i];
}
}
int main() {
init(1e5);
scanf ("%lld%lld", &n, &m);
if (n > m) {
swap(n, m);
}
for (int x = 1, gx; x <= n; x = gx + 1) {
gx = min (n / (n / x), m / (m / x));
ans += (long long)(sum[gx] - sum[x - 1]) * (n / x) * (m / x);
}
printf ("%lld\n", ans * 2ll - n * m);
return 0;
}
P3172 [CQOI2015]选数
题目大意:求出
令
又
另外,这道题用线性筛会炸,要用杜教筛优化
#include <cstdio>
#include <tr1/unordered_map>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5, mod = 1e9 + 7;
int n, k, l, r, ans, cnt, prime[N], mu[N];
bool vis[N];
tr1::unordered_map <int, int> ans_mu;
void init (int n) {
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for (int i = 1; i <= n; i++) {
mu[i] += mu[i - 1];
}
}
int get_mu (int n) {
if (n <= 1e6) {
return mu[n];
}
if (ans_mu[n]) {
return ans_mu[n];
}
int ans = 1;
for (int x = 2, gx; x <= n; x = gx + 1) {
gx = n / (n / x);
ans -= (gx - x + 1) * get_mu (n / x);
}
return ans_mu[n] = ans;
}
int f (int a, int b) {
int res = 1;
while (b > 0) {
if (b & 1) {
res = (1ll * res * a) % mod;
}
b >>= 1;
a = (1ll * a * a) % mod;
}
return res % mod;
}
void solve () {
for (int x = 1, gx; x <= r; x = gx + 1) {
if (l < x) {//这里因为x最多能到r,所以要特判一下,不能直接写min (l, r)因为gcd(a1, a2, ... , an)可能会大于l
gx = r / (r / x);
} else {
gx = min (l / (l / x), r / (r / x));
}
ans = (ans + (1ll * (get_mu (gx) - get_mu(x - 1)) * f (r / x - l / x, n)) % mod) % mod;
}
printf ("%d\n", (ans + mod) % mod);
}
int main() {
scanf ("%d%d%d%d", &n, &k, &l, &r);
l = (l - 1) / k, r /= k;
init (1e6);
solve ();
return 0;
}