引理

首先给出莫比乌斯函数 μ(n)\mu(n) 的定义:

n=i=1kpiciμ(n)={0,ci2(1)k,ci<2n=\prod_{i=1}^k p_i^{c_i}\\ \mu(n)=\left \{ \begin{aligned} &0,\exist c_i\ge 2\\ &(-1)^k,\forall c_i<2\\ \end{aligned} \right.

特别地,n=1n=1 时看作 k=0k=0 因此 μ(1)=1\mu(1)=1

若函数 S(n)S(n) 定义为:

S(n)=dnμ(n)S(n)=\sum_{d|n}\mu(n)

则其取值一定为下列情况:

S(n)={1,n=10,n>1S(n)=\left\{\begin{aligned} &1,n=1\\ &0,n>1 \end{aligned}\right.

n=1n=1 时,结果显然成立,下面证明 n>1n>1 时的情况,首先分解质因子:

n=i=1kpiciLet d=i=1k0pibin=\prod_{i=1}^{k}p_i^{c_i}\\ \text{Let }d=\prod_{i=1}^{k_0}p_i^{b_i}

当任意 bi>1b_i>1 时,μ(d)=0\mu(d)=0,所以不用考虑这种情况,因此就是从 kk 个质因子中选几个进行求和的问题了。

S(n)=Ck0(1)0+Ck1(1)1++Ckk(1)kS(n)=C_k^0(-1)^0+C_k^1(-1)^1+\cdots+C_k^k(-1)^k

由二项式定理可知:

(1+x)k=Ck0x0+Ck1x1++Ckkxk(1+x)^k=C_k^0x^0+C_k^1x^1+\cdots+C_k^kx^k

所以:

S(n)=(11)k=0S(n)=(1-1)^k=0

证毕。

定理

莫比乌斯反演是一个反解的过程,第一个版本是枚举 nn 的约数 dd

F(n)=dnf(d)f(n)=dnμ(d)F(nd)F(n)=\sum_{d|n}f(d)\\ \Rightarrow f(n)=\sum_{d|n}\mu(d)F(\frac{n}{d})

直接把定理的结论展开进行证明:

RHS=dnμ(d)F(nd)=dnμ(d)t(n/d)f(t)=tnf(t)d(n/t)μ(d)=tnf(t)S(nt)=f(n)=LHS\begin{aligned} \text{RHS}&=\sum_{d|n}\mu(d)F(\frac{n}{d})\\ &=\sum_{d|n}\mu(d)\sum_{t|(n/d)}f(t)\\ &=\sum_{t|n}f(t)\sum_{d|(n/t)}\mu(d)\\ &=\sum_{t|n}f(t)S(\frac{n}{t})\\ &=f(n)=\text{LHS}\\ \end{aligned}

交换求和顺序时,观察到 d=1d=1tt 可以取遍 nn 的所有约数,那当枚举每一个 tt 时,与之对应的 dd 一定有 t(n/d)t|(n/d),也就是 d(n/t)d|(n/t),这个写成倍数然后代一下就能看出来。

还有另一个版本,枚举 nn 的倍数 dd

F(n)=ndf(d)f(n)=ndμ(dn)F(d)F(n)=\sum_{n|d}f(d)\\ \Rightarrow f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)

证明:

Let d=kn,kNRHS=ndμ(dn)dtf(t)=ntf(t)k(t/n)μ(k)=ntf(t)S(tn)=f(n)=LHS\text{Let }d=kn,k\in \N^*\\ \begin{aligned} \text{RHS}&=\sum_{n|d}\mu(\frac{d}{n})\sum_{d|t}f(t)\\ &=\sum_{n|t}f(t)\sum_{k|(t/n)}\mu(k)\\ &=\sum_{n|t}f(t)S(\frac{t}{n})\\ &=f(n)=\text{LHS} \end{aligned}

下面是例题。

一个取整公式

下面这个公式是成立的。

abc=a/bc\lfloor\frac{a}{bc}\rfloor = \lfloor\frac{\lfloor a/b\rfloor }{c}\rfloor

证明:

Let a=kb+r,k=qc+pWhere r[0,b),p[0,c)a=kb+r=qbc+pb+rqbc+b(c1)+b1=qbc+bc1abcbc(q+1)1bc=q+11bc<q+1LHS=q=RHS\text{Let }a=kb+r, k=qc+p\\ \text{Where }r\in[0,b),p\in[0,c)\\ \begin{aligned} a&=kb+r\\ &=qbc+pb+r\\ &\le qbc+b(c-1)+b-1\\ &=qbc+bc-1\\ \frac{a}{bc}&\le \frac{bc(q+1)-1}{bc}\\ &= q+1-\frac{1}{bc}\\ &\lt q+1\\ \therefore \text{LHS}&=q=\text{RHS} \end{aligned}

原文链接在这里。

Problem b

对于给出的 n 个询问,每次求有多少个数对 (x,y),满足 a≤x≤b,c≤y≤d,且 gcd(x,y)=k。

题目链接:AcWing 2702P2522

首先用前缀和的思想将其转化为求一个矩形内满足要求的个数。

F(n)=i=1aj=1b[ngcd(i,j)]f(n)=i=1aj=1b[n=gcd(i,j)]F(n)=ndf(d)\begin{aligned} F(n)&=\sum_{i=1}^a\sum_{j=1}^b[n|\gcd(i,j)]\\ f(n)&=\sum_{i=1}^a\sum_{j=1}^b[n=\gcd(i,j)]\\ F(n)&=\sum_{n|d}f(d) \end{aligned}

根据莫比乌斯反演,那么就有:

f(n)=ndμ(dn)F(d)=iNμ(i)ainbin\begin{aligned} f(n)&=\sum_{n|d}\mu(\frac{d}{n})F(d)\\ &=\sum_{i\in\N^*} \mu(i) \lfloor\frac{a}{in}\rfloor\lfloor\frac{b}{in}\rfloor \end{aligned}

这里 kk 的上限就是当右侧两个整除结果出现 0 之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 50010;
int primes[N], mu[N], smu[N], cnt;
bool st[N];
int k;

void init(int n) {
mu[1] = smu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!st[i]) {
primes[cnt++] = i;
mu[i] = -1;
}
for (int j = 0; primes[j] <= n/i; j++) {
st[i*primes[j]] = true;
if (i % primes[j] == 0) {
break;
}
mu[i*primes[j]] = -mu[i];
}

smu[i] = smu[i-1]+mu[i];
}

}

int get(int a, int b) {
a /= k, b /= k;
int res = 0;
for (int i = 1, j; i <= min(a, b); i = j+1) {
j = min(a/(a/i), b/(b/i));

res += (smu[j]-smu[i-1])*(a/i)*(b/i);
}
return res;
}

int main() {
int T;
cin >> T;
init(N-1);
while (T--) {
int a, b, c, d;
cin >> a >> b >> c >> d >> k;
printf("%d\n", get(a-1, c-1) + get(b, d) - get(b, c-1) - get(a-1, d));
}
return 0;
}

约数个数和

d(x)d(x) 为约数个数,给定 n,mn,mi=1nj=1md(ij)\sum_{i=1}^n\sum_{j=1}^md(ij)

题目链接:P3327AcWing 1358

引理:d(ij)=xiyj[gcd(x,y)=1]d(ij)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]

证明:对于任意一个公共质因子 pip_i,都有 x,yx, y 的取值都有 (1,1),(pi,1),,(piαi,1),(1,pi),,(1,piβi)(1, 1),(p_i,1),\cdots,(p_i^{\alpha_i},1),(1,p_i),\cdots, (1,p_i^{\beta_i}),因此最终有 (αi+βi+1)(\alpha_i+\beta_i+1) 种取值,这和约数个数是等价的,如果不同时包含 pip_i,对应次数置为 0 即可。

F(t)=i=1nj=1mxiyj[tgcd(x,y)]f(t)=i=1nj=1mxiyj[t=gcd(x,y)]F(t)=tdf(d)f(t)=tdμ(dt)F(d)\begin{aligned} F(t)&=\sum_{i=1}^n\sum_{j=1}^m \sum_{x|i}\sum_{y|j}[t|\gcd(x,y)]\\ f(t)&=\sum_{i=1}^n\sum_{j=1}^m \sum_{x|i}\sum_{y|j}[t=\gcd(x,y)]\\ F(t)&=\sum_{t|d}f(d)\\ f(t)&=\sum_{t|d}\mu(\frac{d}{t})F(d) \end{aligned}

F(t)F(t) 变形时,先考虑到 x,yx,y 分别可以取遍 1n,1m1\sim n, 1\sim m,并且内部式子和 i,ji,j 无关,因此内部式子的值只看 i,ji,j 中有多少 x,yx,y 的倍数。

F(t)=x=1ny=1mnxmy[tgcd(x,y)]\begin{aligned} F(t)&=\sum_{x=1}^n\sum_{y=1}^m \lfloor\frac{n}{x}\rfloor\lfloor\frac{m}{y}\rfloor[t|\gcd(x,y)]\\ \end{aligned}

由于 tx,tyt|x, t|y 所以只需要枚举 tt 的倍数的那个系数,设 x=x/t,y=y/t,n=n/t,m=m/tx'=x/t, y'=y/t,n'=n/t,m'=m/t,这里都是向下取整,那个符号就不写了,结合文章开头提到的取整公式,则有:

F(t)=x=1ny=1mnxmy=(x=1nnx)(y=1mmy)\begin{aligned} F(t)&=\sum_{x'=1}^{n'}\sum_{y'=1}^{m'} \lfloor\frac{n'}{x'}\rfloor\lfloor\frac{m'}{y'}\rfloor\\ &=(\sum_{x'=1}^{n'} \lfloor\frac{n'}{x'}\rfloor)(\sum_{y'=1}^{m'}\lfloor\frac{m'}{y'}\rfloor) \end{aligned}

本质上就是常数可以提到外面去。

h(n)=x=1nnxf(t)=tdμ(dt)F(d)=tdμ(dt)h(nd)h(md)=k=1min(n,m)μ(k)h(nk)h(mk)\begin{aligned} h(n)&=\sum_{x=1}^n\lfloor\frac{n}{x}\rfloor\\ f(t)&=\sum_{t|d}\mu(\frac{d}{t})F(d)\\ &=\sum_{t|d}\mu(\frac{d}{t})h(\lfloor\frac{n}{d}\rfloor)h(\lfloor\frac{m}{d}\rfloor)\\ &=\sum_{k=1}^{\min(n',m')}\mu(k)h(\lfloor\frac{n'}{k}\rfloor)h(\lfloor\frac{m'}{k}\rfloor) \end{aligned}

要求的是 f(1)f(1),因此最终要求的就是:

i=1min(n,m)μ(i)h(ni)h(mi)\sum_{i=1}^{\min(n,m)}\mu(i)h(\lfloor\frac{n}{i}\rfloor)h(\lfloor\frac{m}{i}\rfloor)

可以预处理 h(x)h(x) 数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N = 50010;
bool st[N];
int primes[N], cnt, mu[N], smu[N], h[N];

void init(int n) {
mu[1] = smu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!st[i]) {
mu[i] = -1;
primes[cnt++] = i;
}
for (int j = 0; primes[j] <= n/i; j++) {
st[i*primes[j]] = true;
if (i % primes[j] == 0) break;

mu[i*primes[j]] = -mu[i];
}

smu[i] = smu[i-1] + mu[i];
}

for (int s = 1; s <= n; s++) {
for (int i = 1, j; i <= s; i = j+1) {
j = s/(s/i);
h[s] += (j-i+1)*(s/i);
}
}
}

int main() {
init(N-1);
int T; scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
LL res = 0;
for (int i = 1, j; i <= n && i <= m; i = j+1) {
j = min(n/(n/i), m/(m/i));
res += (LL)(smu[j]-smu[i-1])*h[n/i]*h[m/i];
}
printf("%lld\n", res);
}
return 0;
}