学习笔记 ---- 莫比乌斯反演总结
文章目录
- 概述
- 前置知识
- 数论分块
- 狄利克雷卷积
- 积性函数
- 莫比乌斯函数
- 定义
- 性质
- 莫比乌斯反演
- 因子形式
- 倍数形式
- 例题
- 周期性字符串
- 互质数对
- [NOI2010D1T1]能量采集
- BZOJ2820 YY的GCD
- [SDOI2015] 约数个数和
- BZOJ4407 于神之怒加强版
- 【BZOJ2693】jzptab最小公倍数之和
- [bzoj3529-Sdoi2014] 数表
- 练习题
- 51nod 1675 序列变换
- BZOJ3561 DZY Loves Math VI
- [bzoj3309]DZY Loves Math
- [bzoj4816--Sdoi2017] 数字表格
- [hdoj4746]Mophues
- 技巧总结
概述
对于一些函数 f ( n ) f(n) f(n),如果很难直接求出它的值,但是容易求出其约数或倍数的 g ( n ) g(n) g(n)和,并且 f f f与 g g g 满足一定关系。那么可以通过莫比乌斯反演求出 f ( n ) f(n) f(n)。
前置知识
数论分块
点这里
狄利克雷卷积
还不会,先鸽着
积性函数
设 T T T 为函数 f f f 的定义域。如果 ∀ a , b ∈ T 且 g c d ( a , b ) = 1 \forall a,b \in T 且 gcd(a,b)=1 ∀a,b∈T且gcd(a,b)=1 都有 f ( a b ) = f ( a ) × f ( b ) f(ab) = f(a) \times f(b) f(ab)=f(a)×f(b),那么称函数 f f f 为积性函数。
一个很好的性质是几乎所有积性函数都能用 线性筛 筛出来。
常见的积性函数有欧拉函数和莫比乌斯函数
莫比乌斯函数
定义
μ ( n ) \mu(n) μ(n) 为莫比乌斯函数,定义为:
μ ( n ) = { 1 n = 1 0 n 含有平方因子 ( − 1 ) k k 为 n 本质不同的质因子数 \mu(n) = \begin{cases} 1 & \text{ } n=1 \\ 0 & \text{ } n含有平方因子 \\ (-1)^k & \text{ } k为n本质不同的质因子数 \end{cases} μ(n)=⎩ ⎨ ⎧10(−1)k n=1 n含有平方因子 k为n本质不同的质因子数
性质
- 莫比乌斯函数是积性函数
- ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d) = [n=1] ∑d∣nμ(d)=[n=1]
第二条性质非常重要,可以将一个逻辑表达式的值转化为对 μ \mu μ 函数求和。下面证明一下:
-
当 n = 1 n = 1 n=1 时: ∑ d ∣ n μ ( d ) = μ ( 1 ) = 1 \sum_{d|n}\mu(d) = \mu(1) = 1 ∑d∣nμ(d)=μ(1)=1,显然等于右边。
-
当 n ≠ 1 n \ne 1 n=1 时:我们考虑写出 n n n 质因数分解后的形式 n = p 1 c 1 × p 2 c 2 × . . . × p k c k n = p_1^{c_1} \times p_2^{c_2} \times ... \times p_k^{c_k} n=p1c1×p2c2×...×pkck。然后如果一个因子 d d d 含有某个 p 1 ∼ p k p_1 \sim p_k p1∼pk 的幂次大于 1 1 1,那么根据莫比乌斯函数的定义,此时 μ ( d ) = 0 \mu(d) = 0 μ(d)=0。因此我们只需要考虑每个质因数是否出现在 d d d 中 即可。
那么有 ∑ d ∣ n μ ( d ) = ∑ i = 0 k ( k i ) ( − 1 ) i = ( 1 − 1 ) k = 0 \sum_{d|n}\mu(d) = \sum_{i = 0}^{k} \binom{k}{i}(-1)^i = (1 -1)^k = 0 ∑d∣nμ(d)=∑i=0k(ik)(−1)i=(1−1)k=0
综上,得证。
补充结论: [ g c d ( i , j ) = 1 ] = ∑ d ∣ g c d ( i , j ) μ ( d ) [gcd(i, j) = 1] = \sum_{d|gcd(i, j)}\mu(d) [gcd(i,j)=1]=∑d∣gcd(i,j)μ(d)
这个由上述性质可以直接得到。
求莫比乌斯函数模版:
int mu[N], prime[N], tot;
bool vis[N];
void pre() {
mu[1] = 1; // 注意1是特殊的
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;} // 根据定义,质数的莫比乌斯函数为-1
for(int j = 1; i * prime[j] < N && j <= tot; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0; // 含有平方因子,莫比乌斯函数为0
break;
}
mu[i * prime[j]] = -mu[i]; // 根据积性函数的性质, mu[i * prime[j]] = mu[i] * mu[prime[j]] = -mu[i]
}
}
}
莫比乌斯反演
因子形式
若 F ( n ) = ∑ d ∣ n f ( d ) F(n) = \sum_{d|n}f(d) F(n)=∑d∣nf(d),则 f ( n ) = ∑ d ∣ n μ ( n d ) F ( d ) f(n) = \sum_{d|n}\mu(\frac{n}{d})F(d) f(n)=∑d∣nμ(dn)F(d)。
证明:
将
F
(
d
)
F(d)
F(d) 带入,那么有:
∑
d
∣
n
μ
(
n
d
)
F
(
d
)
\sum_{d|n}\mu(\frac{n}{d})F(d)
∑d∣nμ(dn)F(d)
=
∑
d
∣
n
μ
(
n
d
)
∑
k
∣
d
f
(
k
)
= \sum_{d|n}\mu(\frac{n}{d})\sum_{k|d}f(k)
=∑d∣nμ(dn)∑k∣df(k)
=
∑
k
∣
n
f
(
k
)
∑
t
∣
n
k
μ
(
t
)
= \sum_{k|n}f(k)\sum_{t|\frac{n}{k}}\mu(t)
=∑k∣nf(k)∑t∣knμ(t)
根据莫比乌斯函数的性质 2 2 2:当 n k = 1 \frac{n}{k} = 1 kn=1 时,右边的和式为 1 1 1,此时 n = k n = k n=k,那么贡献为 f ( n ) 。 f(n)。 f(n)。 否则右边的和式为 0 0 0, k k k 没有贡献。
因此结果为 右边 = f ( n ) = 左边 右边=f(n)=左边 右边=f(n)=左边。
倍数形式
若 F ( n ) = ∑ n ∣ d f ( d ) F(n) = \sum_{n|d}f(d) F(n)=∑n∣df(d),则 f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) f(n) = \sum_{n|d}\mu(\frac{d}{n})F(d) f(n)=∑n∣dμ(nd)F(d)
证明:
将
F
(
d
)
F(d)
F(d) 带入,那么有:
∑
n
∣
d
μ
(
d
n
)
F
(
d
)
\sum_{n|d}\mu(\frac{d}{n})F(d)
∑n∣dμ(nd)F(d)
=
∑
n
∣
d
μ
(
d
n
)
∑
d
∣
k
f
(
k
)
=\sum_{n|d}\mu(\frac{d}{n})\sum_{d|k}f(k)
=∑n∣dμ(nd)∑d∣kf(k)
=
∑
n
∣
k
f
(
k
)
∑
t
∣
k
n
μ
(
t
)
=\sum_{n|k}f(k)\sum_{t|\frac{k}{n}}\mu(t)
=∑n∣kf(k)∑t∣nkμ(t)
只有当 k n = 1 \frac{k}{n} =1 nk=1 时, f ( k ) f(k) f(k) 有贡献。此时 k = n k = n k=n,只有一个 f ( n ) f(n) f(n) 算入到了答案中。
因此 右边 = f ( n ) = 左边 右边=f(n)=左边 右边=f(n)=左边。
实际上,上述两个形式并不太常用。通常情况下只用到 [ g c d ( i , j ) = 1 ] = ∑ d ∣ g c d ( i , j ) μ ( d ) [gcd(i, j) = 1] = \sum_{d|gcd(i, j)}\mu(d) [gcd(i,j)=1]=∑d∣gcd(i,j)μ(d) 这一个性质就足够了。但是回归本质:如果我们发现 F F F 函数好求,而 f f f 函数不好求,并且 F F F 与 f f f 函数满足上述两种关系之一。那么我们可以通过求出 F F F 来反演得到 f f f。
例题
周期性字符串
点这里
题意:
如果一个字符串
s
s
s 可以由另一个字符串
t
(
t
≠
s
)
t(t\ne s)
t(t=s) 重复若干次得到,那么称
s
s
s 具有周期性。给定一个
n
n
n,求有多少长度为
n
n
n 的周期字符串。
1
≤
n
≤
1
0
6
1 \leq n \leq 10^6
1≤n≤106
分析:
首先可以 d p dp dp。设 f i f_{i} fi 表示长度为 i i i 的周期性字符串个数。那么有转移 f i = ∑ j ∣ i 2 6 j − f j f_{i} = \sum\limits_{j|i}26^j - f_j fi=j∣i∑26j−fj。预处理 26 26 26 的幂次复杂度可做到 O ( n ln n ) O(n\ln n) O(nlnn)。能够通过本题,但是如果 n n n 开到 1 0 7 10^7 107 就容易寄。
我们考虑莫比乌斯反演。设 g i g_i gi 表示长度为 i i i 且非周期性字符串个数,设 h i h_i hi 表示所有长度为 i i i 的因数的非周期性字符串个数。那么显然 h n = ∑ d ∣ n g ( d ) h_n = \sum_{d|n}g(d) hn=∑d∣ng(d),答案就是 ∑ d ∣ n , d ≠ n g ( d ) \sum_{d|n,d \ne n}g(d) ∑d∣n,d=ng(d)。
我们发现 h i = 2 6 i h_i = 26^i hi=26i。这是因为任意一个长度为 i i i 的字符串 s s s 都可以对应一个最小的长度是 i i i 因数的非周期字符串 t t t。任意一个长度为 i i i 因数非周期性字符串 t t t 也都可以通过复制拼接唯一对应一个长度为 i i i 的字符串 s s s。因此这建立了一个双射,所以二者是相等的。
然后我们只需要把所以 d ∣ n , d ≠ n d|n,d\ne n d∣n,d=n 的 g ( d ) g(d) g(d) 用 h h h 反演出来即可。时间复杂度 O ( n × n = n ) O(\sqrt{n} \times \sqrt{n} = n) O(n×n=n)
CODE:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
const LL mod = 1e9 + 7;
int n, mu[N], prime[N], tot;
bool vis[N];
LL f[N], F[N], res;
inline LL Pow(LL x, LL y) {
LL res = 1LL, k = x;
while(y) {
if(y & 1) res = (res * k) % mod;
y >>= 1;
k = (k * k) % mod;
}
return res;
}
void Get_mu() {
mu[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
for(int j = 1; j <= tot && i * prime[j] < N; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
mu[i * prime[j]] = -mu[i];
}
}
}
void calc(int n) {
for(int d = 1; d * d <= n; d ++ ) {
if(n % d == 0) {
F[n] = ((F[n] + 1LL * mu[d] * f[n / d] % mod) % mod + mod) % mod;
if(d * d != n) F[n] = ((F[n] + 1LL * mu[n / d] * f[d] % mod) % mod + mod) % mod;
}
}
}
int main() {
Get_mu();
cin >> n;
for(int i = 1; i * i <= n; i ++ ) {
if(n % i == 0) {
f[i] = Pow(26LL, 1LL * i);
f[n / i] = Pow(26LL, 1LL * (n / i));
}
}
for(int i = 1; i * i <= n; i ++ ) {
if(n % i == 0 && i != n) {
calc(i); res = (res + F[i]) % mod;
if(i * i != n && n / i != n) calc(n / i), res = (res + F[n / i]) % mod;
}
}
cout << res << endl;
return 0;
}
互质数对
点这里
题意:
给定一个
n
n
n,求
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
1
]
\sum_{i = 1}^{n}\sum_{j = 1}^{n}[gcd(i, j) = 1]
∑i=1n∑j=1n[gcd(i,j)=1]。
n
≤
1
0
7
n \leq 10^7
n≤107
分析:
看到
[
g
c
d
(
i
,
j
)
=
1
]
[gcd(i, j) = 1]
[gcd(i,j)=1],想到莫比乌斯函数。
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
1
]
\sum_{i = 1}^{n}\sum_{j = 1}^{n}[gcd(i, j) = 1]
∑i=1n∑j=1n[gcd(i,j)=1]
=
∑
i
=
1
n
∑
j
=
1
n
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
=\sum_{i = 1}^{n}\sum_{j = 1}^{n}\sum\limits_{d|gcd(i, j)}\mu(d)
=∑i=1n∑j=1nd∣gcd(i,j)∑μ(d)
把枚举质因数提前
= ∑ d = 1 n μ ( d ) × ⌊ n d ⌋ × ⌊ n d ⌋ =\sum_{d=1}^{n}\mu(d) \times \left \lfloor \frac{n}{d} \right \rfloor \times \left \lfloor \frac{n}{d} \right \rfloor =∑d=1nμ(d)×⌊dn⌋×⌊dn⌋
处理出
u
(
i
)
u(i)
u(i) 的前缀和,然后一维数论分块就行。
复杂度
O
(
n
)
O(\sqrt{n})
O(n)
CODE:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 10;
int n, prime[N], tot;
bool vis[N];
LL mu[N], sum[N], res;
void Get_mu() {
mu[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
for(int j = 1; j <= tot && 1LL * i * prime[j] < 1LL * N; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[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];
}
int main() {
Get_mu();
cin >> n;
int l = 1, r;
while(l <= n) {
r = n / (n / l);
res = res + (sum[r] - sum[l - 1]) * (1LL * n / l) * (1LL * n / l);
l = r + 1;
}
cout << res << endl;
return 0;
}
[NOI2010D1T1]能量采集
点这里
题意:给你
n
,
m
n, m
n,m,求
∑
i
=
1
n
∑
j
=
1
m
2
×
g
c
d
(
i
,
j
)
−
1
\sum_{i = 1}^{n}\sum_{j = 1}^{m}2 \times gcd(i, j) - 1
∑i=1n∑j=1m2×gcd(i,j)−1。
1
≤
n
,
m
≤
1
0
5
1 \leq n, m \leq10^5
1≤n,m≤105。
分析:
看到
g
c
d
(
i
,
j
)
gcd(i, j)
gcd(i,j),可以考虑莫比乌斯函数。
枚举
g
c
d
gcd
gcd:
∑
i
=
1
n
∑
j
=
1
m
2
×
g
c
d
(
i
,
j
)
−
1
\sum_{i = 1}^{n}\sum_{j = 1}^{m}2 \times gcd(i, j) - 1
∑i=1n∑j=1m2×gcd(i,j)−1
=
∑
i
=
1
n
∑
j
=
1
m
∑
d
=
1
m
i
n
(
n
,
m
)
(
2
×
d
−
1
)
×
(
[
g
c
d
(
i
,
j
)
=
d
]
)
=\sum_{i = 1}^{n}\sum_{j =1}^{m}\sum_{d=1}^{min(n, m)}(2 \times d - 1) \times ([gcd(i, j) = d])
=∑i=1n∑j=1m∑d=1min(n,m)(2×d−1)×([gcd(i,j)=d])
提前枚举 g c d gcd gcd
= ∑ d = 1 m i n ( n , m ) ( 2 × d − 1 ) × ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] =\sum_{d = 1}^{min(n,m)}(2\times d- 1) \times \sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = d] =∑d=1min(n,m)(2×d−1)×∑i=1n∑j=1m[gcd(i,j)=d]
右边的两个求和符号上指标同时除以 d d d,变为枚举 d d d 的倍数:
= ∑ d = 1 m i n ( n , m ) ( 2 × d − 1 ) × ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] =\sum_{d = 1}^{min(n, m)}(2 \times d - 1) \times \sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor} \sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i, j) = 1] =∑d=1min(n,m)(2×d−1)×∑i=1⌊dn⌋∑j=1⌊dm⌋[gcd(i,j)=1]
=
∑
d
=
1
m
i
n
(
n
,
m
)
(
2
×
d
−
1
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
∑
k
∣
g
c
d
(
i
,
j
)
μ
(
k
)
=\sum_{d = 1}^{min(n, m)}(2 \times d - 1)\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}\sum_{k|gcd(i, j)}\mu(k)
=∑d=1min(n,m)(2×d−1)∑i=1⌊dn⌋∑j=1⌊dm⌋∑k∣gcd(i,j)μ(k)
=
∑
d
=
1
m
i
n
(
n
,
m
)
(
2
×
d
−
1
)
∑
k
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
k
)
×
⌊
n
d
k
⌋
×
⌊
m
d
k
⌋
=\sum_{d = 1}^{min(n, m)}(2\times d - 1)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor
=∑d=1min(n,m)(2×d−1)∑k=1min(⌊dn⌋,⌊dm⌋)μ(k)×⌊dkn⌋×⌊dkm⌋
然后考虑对于 d d d 而言, ⌊ n d ⌋ \left \lfloor \frac{n}{d} \right \rfloor ⌊dn⌋ 和 ⌊ m d ⌋ \left \lfloor \frac{m}{d} \right \rfloor ⌊dm⌋ 成块状分布,可以二维分块得到 n ′ = ⌊ n d ⌋ n' = \left \lfloor \frac{n}{d} \right \rfloor n′=⌊dn⌋ 和 m ′ = ⌊ m d ⌋ m' = \left \lfloor \frac{m}{d} \right \rfloor m′=⌊dm⌋。对于 n ′ n' n′ 和 m ′ m' m′ 分别相同的的一段内,右边和式的贡献是相同的,变成了 ∑ k = 1 m i n ( n ′ , m ′ ) μ ( k ) × ⌊ n ′ k ⌋ × ⌊ m ′ k ⌋ \sum_{k = 1}^{min(n', m')}\mu(k) \times \left \lfloor \frac{n'}{k} \right \rfloor \times \left \lfloor \frac{m'}{k} \right \rfloor ∑k=1min(n′,m′)μ(k)×⌊kn′⌋×⌊km′⌋。也可以再二维整除分块一次,总复杂度就是 O ( n × n = n ) O(\sqrt{n} \times \sqrt{n} = n) O(n×n=n) 的。
实际上复杂度还能做到 O ( n ) O(\sqrt{n}) O(n),这个后边会讲。
CODE:
// 整除套整除分块 好像能做到 O(n)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int n, m, prime[N], tot;
LL mu[N], sum[N], res;
bool vis[N];
void Get_mu() {
mu[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
for(int j = 1; j <= tot && i * prime[j] < N; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[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];
}
LL calc(int nt, int mt) {
int t = min(nt, mt);
int l = 1, r; LL res = 0;
while(l <= t) {
r = min({t, nt / (nt / l), mt / (mt / l)});
res = res + 1LL * (nt / l) * (mt / l) * (sum[r] - sum[l - 1]);
l = r + 1;
}
return res;
}
int main() {
scanf("%d%d", &n, &m);
Get_mu();
int t = min(n, m);
int l = 1, r;
while(l <= t) {
r = min({t, n / (n / l), m / (m / l)});
res = res + calc(n / l, m / l) * (1LL * (l + r) * (r - l + 1) - 1LL * (r - l + 1));
l = r + 1;
}
printf("%lld\n", res);
return 0;
}
BZOJ2820 YY的GCD
点这里
题意:
T
T
T 次询问,每次给你
n
,
m
n,m
n,m,问
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
p
]
,
p
代表质数
\sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = p],p代表质数
∑i=1n∑j=1m[gcd(i,j)=p],p代表质数。
T
≤
1
0
4
,
1
≤
n
,
m
≤
1
0
7
T \leq 10^4,1 \leq n,m \leq 10^7
T≤104,1≤n,m≤107
分析:
首先看到 逻辑表达式的值 和
g
c
d
(
i
,
j
)
gcd(i, j)
gcd(i,j),考虑莫反。
如果我们定义
f
(
x
)
=
{
1
x
∈
P
0
x
∉
P
f(x) = \begin{cases} 1 & \text{ } x\in P \\ 0 & \text{ } x \notin P \end{cases}
f(x)={10 x∈P x∈/P,
P
P
P 代表素数集合。那么所要求的就是:
∑
i
=
1
n
∑
j
=
1
m
f
(
g
c
d
(
i
,
j
)
)
\sum_{i=1}^{n}\sum_{j = 1}^{m}f(gcd(i, j))
∑i=1n∑j=1mf(gcd(i,j))
=
∑
d
=
1
m
i
n
(
n
,
m
)
f
(
d
)
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
=\sum_{d=1}^{min(n, m)}f(d) \sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = d]
=∑d=1min(n,m)f(d)∑i=1n∑j=1m[gcd(i,j)=d]
=
∑
d
=
1
m
i
n
(
n
,
m
)
f
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
g
c
d
(
i
,
j
)
=
1
]
=\sum_{d = 1}^{min(n, m)}f(d)\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i, j) = 1]
=∑d=1min(n,m)f(d)∑i=1⌊dn⌋∑j=1⌊dm⌋[gcd(i,j)=1]
=
∑
d
=
1
m
i
n
(
n
,
m
)
f
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
∑
k
∣
g
c
d
(
i
,
j
)
μ
(
k
)
=\sum_{d = 1}^{min(n, m)}f(d)\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}\sum_{k|gcd(i, j)}\mu(k)
=∑d=1min(n,m)f(d)∑i=1⌊dn⌋∑j=1⌊dm⌋∑k∣gcd(i,j)μ(k)
=
∑
d
=
1
m
i
n
(
n
,
m
)
f
(
d
)
∑
k
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
k
)
×
⌊
n
d
k
⌋
×
⌊
m
d
k
⌋
=\sum_{d = 1}^{min(n, m)}f(d)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor,\left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor
=∑d=1min(n,m)f(d)∑k=1min(⌊dn⌋,⌊dm⌋)μ(k)×⌊dkn⌋×⌊dkm⌋
首先 f f f 数组的求解是简单的,可以在 O ( N ) O(N) O(N) 的复杂度内预处理出来。然后我们可以使用上一题的思路,二次分块,但是那样每次询问复杂度是 O ( n ) O(n) O(n) 的,总复杂度就变成 O ( T n ) O(Tn) O(Tn),无法接受。
怎么办呢?我们好像已经化到了最简,无法进一步优化了。
这里有一个很通用的技巧,也算一个很重要的套路:
当我们需要二次分块时,一般第二个求和符号后面的分母都是两个变量的乘积。尝试把乘积提前枚举,并将式子变形。往往能得到一个可以预处理的函数,使得复杂度少一个 n \sqrt{n} n。
我们来尝试一下:
首先理解一下式子的含义:相当于把所有满足 d k ≤ m i n ( n , m ) dk \leq min(n, m) dk≤min(n,m) 的数对 ( d , k ) (d, k) (d,k) 都贡献一个 f ( d ) × μ ( k ) × ⌊ n d k ⌋ ⌊ m d k ⌋ f(d) \times \mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \left \lfloor \frac{m}{dk} \right \rfloor f(d)×μ(k)×⌊dkn⌋⌊dkm⌋ 到答案中。
设
d
k
=
T
dk = T
dk=T,那么有:
∑
d
=
1
m
i
n
(
n
,
m
)
f
(
d
)
∑
k
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
k
)
×
⌊
n
d
k
⌋
×
⌊
m
d
k
⌋
\sum_{d = 1}^{min(n, m)}f(d)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor,\left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor
∑d=1min(n,m)f(d)∑k=1min(⌊dn⌋,⌊dm⌋)μ(k)×⌊dkn⌋×⌊dkm⌋
=
∑
T
=
1
m
i
n
(
n
,
m
)
⌊
n
T
⌋
×
⌊
m
T
⌋
∑
d
∣
T
f
(
d
)
×
μ
(
T
d
)
=\sum_{T = 1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \sum_{d|T}f(d) \times \mu(\frac{T}{d})
=∑T=1min(n,m)⌊Tn⌋×⌊Tm⌋∑d∣Tf(d)×μ(dT)。
设 F ( T ) = ∑ d ∣ T f ( d ) × μ ( T d ) F(T) = \sum_{d|T}f(d) \times \mu(\frac{T}{d}) F(T)=∑d∣Tf(d)×μ(dT)。那么原式变成了
∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ × F ( T ) \sum_{T = 1}^{min(n, m)}\left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \times F(T) ∑T=1min(n,m)⌊Tn⌋×⌊Tm⌋×F(T)。
我们发现 F ( T ) F(T) F(T) 是可以在 O ( N ln N ) O(N\ln N) O(NlnN) 的复杂度内预处理出来的,因此每次询问只需要一次二维数论块就可以了。
为什么这样做复杂度就能够优化呢?
我们考虑变形前的式子中 两个求和符号都是与每次给定的
n
n
n 与
m
m
m 相关的,这样就无法预处理。而如果我们让第一个求和符号枚举乘积,第二个求和符号枚举因子,那么第二个求和就只与乘积
T
T
T的大小有关,与
n
,
m
n, m
n,m 无关了。因此一般可以预处理。
实际上本题的预处理可以做到线性。一些较难的题目也会在预处理上做文章,需要线性的做法才能通过。而 F F F 数组大多都是两个函数的 卷积,可以根据卷积的一些性质来求。
总复杂度就是 O ( N ln N + T n ) O(N \ln N + T\sqrt{n}) O(NlnN+Tn),其中 N N N 是 n n n 的上限。
CODE:
// 好像会线性了?
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 10;
LL f[N], sum[N];
int T, n, m, mu[N];
int prime[N], tot;
bool vis[N];
void Get_mu() {
mu[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1; f[i] = 1LL;}
for(int j = 1; j <= tot && 1LL * i * prime[j] < 1LL * N; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
f[i * prime[j]] = mu[i];
break;
}
mu[i * prime[j]] = -mu[i];
f[i * prime[j]] = -f[i] + mu[i];
}
}
for(int i = 1; i < N; i ++ ) sum[i] = sum[i - 1] + f[i];
}
LL calc(int n, int m) {
int l = 1, r; LL res = 0;
int t = min(n, m);
while(l <= t) {
r = min({t, n / (n / l), m / (m / l)});
res = res + 1LL * (n / l) * (m / l) * (sum[r] - sum[l - 1]);
l = r + 1;
}
return res;
}
int main() {
Get_mu();
scanf("%d", &T);
while(T -- ) {
scanf("%d%d", &n, &m);
printf("%lld\n", calc(n, m));
}
return 0;
}
[SDOI2015] 约数个数和
点这里
题意:设
d
(
x
)
d(x)
d(x) 为
x
x
x 的因数个数。
T
T
T 次询问,每次给定
n
,
m
n, m
n,m,求
∑
i
=
1
n
∑
j
=
1
m
d
(
i
×
j
)
\sum_{i = 1}^{n}\sum_{j = 1}^{m}d(i \times j)
∑i=1n∑j=1md(i×j) 。
T
≤
5
×
1
0
4
,
1
≤
n
,
m
≤
5
×
1
0
4
T \leq 5 \times 10^4,1\leq n, m \leq 5 \times 10^4
T≤5×104,1≤n,m≤5×104
首先有一个对本题而言非常重要的结论:
d
(
x
×
y
)
=
∑
i
∣
x
∑
j
∣
y
[
g
c
d
(
i
,
j
)
=
1
]
d(x \times y) = \sum_{i|x}\sum_{j|y}[gcd(i, j) = 1]
d(x×y)=∑i∣x∑j∣y[gcd(i,j)=1]
证明一下:
设
P
(
n
)
P(n)
P(n) 表示
n
n
n 质因数分解后的质因子集合。
那么设
P
(
x
×
y
)
=
{
p
1
,
p
2
,
.
.
.
,
p
k
}
P(x \times y) = \left \{ p_1,p_2,...,p_k\right \}
P(x×y)={p1,p2,...,pk}
设
x
=
p
1
c
1
p
2
c
2
.
.
.
p
k
c
k
x = p_1^{c_1} p_2^{c_2}...p_k^{c_k}
x=p1c1p2c2...pkck
设
y
=
p
1
t
1
p
2
t
2
.
.
.
p
k
t
k
y = p_1^{t_1}p_2^{t_2}...p_k^{t_k}
y=p1t1p2t2...pktk
那么很显然
x
×
y
x \times y
x×y 的因子数量为
∏
i
=
1
k
(
c
i
+
t
i
+
1
)
\prod_{i=1}^{k}(c_i+t_i+1)
∏i=1k(ci+ti+1)
然后考虑
∑
i
∣
x
∑
j
∣
y
[
g
c
d
(
i
,
j
)
=
1
]
\sum_{i|x}\sum_{j|y}[gcd(i, j) = 1]
∑i∣x∑j∣y[gcd(i,j)=1] 什么时候会对答案有贡献。
需要对于
p
1
∼
p
k
p_1 \sim p_k
p1∼pk 这
k
k
k 个质因数,
x
x
x 不含 或者
y
y
y 不含。
对于一个素数 p i p_i pi
- x x x 不含, y y y 含。这种情况有 t i t_i ti 种
- x x x 含, y y y 不含。这种情况有 c i c_i ci 种
- x x x 不含,且 y y y 不含。这种情况有 1 1 1 种。
因此每一个 i i i 的方案数都是都是 c i + t i + 1 c_i + t_i + 1 ci+ti+1 种。由于是分步。需要把它们乘起来,总方案数就是 ∏ i = 1 k ( c i + t i + 1 ) \prod_{i=1}^{k}(c_i + t_i + 1) ∏i=1k(ci+ti+1)。每一种方案都唯一对应了一个数对 ( p , q ) (p, q) (p,q),满足 p ∣ x , q ∣ y p|x,q|y p∣x,q∣y 且 g c d ( p , q ) = 1 gcd(p, q) = 1 gcd(p,q)=1。
那么原问题就变成求
∑
i
=
1
n
∑
j
=
1
m
∑
p
∣
i
∑
q
∣
j
[
g
c
d
(
p
,
q
)
=
1
]
\sum_{i = 1}^{n}\sum_{j=1}^{m}\sum_{p|i}\sum_{q|j}[gcd(p,q )=1]
∑i=1n∑j=1m∑p∣i∑q∣j[gcd(p,q)=1]
=
∑
i
=
1
n
∑
j
=
1
m
∑
p
∣
i
∑
q
∣
j
∑
k
∣
g
c
d
(
p
,
q
)
μ
(
k
)
=\sum_{i = 1}^{n}\sum_{j=1}^{m}\sum_{p|i}\sum_{q|j}\sum_{k|gcd(p, q)}\mu(k)
=∑i=1n∑j=1m∑p∣i∑q∣j∑k∣gcd(p,q)μ(k)
变成先枚举 k k k,再枚举 p , q p,q p,q,最后枚举 i , j i, j i,j 的形式
= ∑ k = 1 m i n ( n , m ) μ ( k ) ∑ p = 1 ⌊ n k ⌋ ∑ q = 1 ⌊ m k ⌋ ⌊ n k p ⌋ × ⌊ m k q ⌋ =\sum_{k = 1}^{min(n, m)}\mu(k)\sum_{p = 1}^{\left \lfloor \frac{n}{k} \right \rfloor}\sum_{q=1}^{\left \lfloor \frac{m}{k} \right \rfloor} \left \lfloor \frac{n}{kp} \right \rfloor \times \left \lfloor \frac{m}{kq} \right \rfloor =∑k=1min(n,m)μ(k)∑p=1⌊kn⌋∑q=1⌊km⌋⌊kpn⌋×⌊kqm⌋
= ∑ k = 1 m i n ( n , m ) μ ( k ) × ∑ p = 1 ⌊ n k ⌋ ⌊ n k p ⌋ × ∑ q = 1 ⌊ m k ⌋ ⌊ m k q ⌋ =\sum_{k=1}^{min(n, m)}\mu(k) \times \sum_{p=1}^{\left \lfloor \frac{n}{k} \right \rfloor} \left \lfloor \frac{n}{kp} \right \rfloor \times \sum_{q=1}^{\left \lfloor \frac{m}{k} \right \rfloor}\left \lfloor \frac{m}{kq} \right \rfloor =∑k=1min(n,m)μ(k)×∑p=1⌊kn⌋⌊kpn⌋×∑q=1⌊km⌋⌊kqm⌋
记 f ( n ) = ∑ i = 1 n ⌊ n i ⌋ f(n) = \sum_{i=1}^{n}\left \lfloor \frac{n}{i} \right \rfloor f(n)=∑i=1n⌊in⌋
那么原式就是
= ∑ k = 1 m i n ( n , m ) μ ( k ) × f ( ⌊ n k ⌋ ) × f ( ⌊ m k ⌋ ) =\sum_{k=1}^{min(n, m)}\mu(k)\times f(\left \lfloor \frac{n}{k} \right \rfloor) \times f(\left \lfloor \frac{m}{k} \right \rfloor) =∑k=1min(n,m)μ(k)×f(⌊kn⌋)×f(⌊km⌋)
然后数论分块预处理
f
f
f,每次询问一维数论分块求出答案即可。
时间复杂度
O
(
n
n
+
T
n
)
O(n\sqrt{n} + T \sqrt{n})
O(nn+Tn)
CODE:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e4 + 10;
int T, n, m;
int mu[N], prime[N], tot, sum[N];
LL S[N];
bool vis[N];
void get_mu() {
mu[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i, mu[i] = -1;}
for(int j = 1; j <= tot && i * prime[j] < N; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[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 getS(int n) {
int l = 1, r;
while(l <= n) {
r = n / (n / l);
S[n] = (S[n] + 1LL * (r - l + 1) * (n / l));
l = r + 1;
}
}
inline LL calc(int n, int m) {
int t = min(n, m);
int l = 1, r; LL res = 0;
while(l <= t) {
r = min({t, n / (n / l), m / (m / l)});
res = res + S[n / l] * S[m / l] * 1LL * (sum[r] - sum[l - 1]);
l = r + 1;
}
return res;
}
int main() {
get_mu();
for(int i = 1; i < N; i ++ ) getS(i);
scanf("%d", &T);
while(T -- ) {
scanf("%d%d", &n, &m);
printf("%lld\n", calc(n, m));
}
return 0;
}
BZOJ4407 于神之怒加强版
点这里
题意:
给你
T
,
k
T,k
T,k,
T
T
T次询问,每次询问给定
n
,
m
n,m
n,m,求
∑
i
=
1
n
∑
j
=
1
m
g
c
d
(
i
,
j
)
k
m
o
d
1
0
9
+
7
\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i, j)^k \ mod \ 10^9+7
∑i=1n∑j=1mgcd(i,j)k mod 109+7
1
≤
T
≤
2
×
1
0
3
1 \leq T \leq 2 \times 10^3
1≤T≤2×103,
1
≤
n
,
m
≤
5
×
1
0
6
1 \leq n, m \leq 5 \times 10^6
1≤n,m≤5×106
分析:
看到
g
c
d
gcd
gcd,还是想到枚举
g
c
d
gcd
gcd,然后乘一个逻辑表达式。
∑
i
=
1
n
∑
j
=
1
m
g
c
d
(
i
,
j
)
k
m
o
d
1
0
9
+
7
\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i, j)^k \ mod \ 10^9+7
∑i=1n∑j=1mgcd(i,j)k mod 109+7
=
∑
d
=
1
m
i
n
(
n
,
m
)
d
k
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
=\sum_{d=1}^{min(n,m)}d^k\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i, j) = d]
=∑d=1min(n,m)dk∑i=1n∑j=1m[gcd(i,j)=d]
右边的两个求和老演员了。
=
∑
d
=
1
m
i
n
(
n
,
m
)
d
k
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
g
c
d
(
i
,
j
)
=
1
]
=\sum_{d=1}^{min(n, m)}d^k \sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor} \sum_{j=1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i, j)=1]
=∑d=1min(n,m)dk∑i=1⌊dn⌋∑j=1⌊dm⌋[gcd(i,j)=1]
=
∑
d
=
1
m
i
n
(
n
,
m
)
d
k
∑
t
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
t
)
×
⌊
n
d
t
⌋
×
⌊
m
d
t
⌋
=\sum_{d=1}^{min(n, m)}d^k \sum_{t = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(t) \times \left \lfloor \frac{n}{dt} \right \rfloor \times \left \lfloor \frac{m}{dt} \right \rfloor
=∑d=1min(n,m)dk∑t=1min(⌊dn⌋,⌊dm⌋)μ(t)×⌊dtn⌋×⌊dtm⌋
= ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ ∑ d ∣ T d k × μ ( T d ) =\sum_{T=1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \sum_{d|T} d^k \times \mu(\frac{T}{d}) =∑T=1min(n,m)⌊Tn⌋×⌊Tm⌋∑d∣Tdk×μ(dT)
设 F ( T ) = ∑ d ∣ T d k × μ ( T d ) F(T) = \sum_{d|T}d^k \times \mu(\frac{T}{d}) F(T)=∑d∣Tdk×μ(dT)
= ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ × F ( T ) =\sum_{T=1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \times F(T) =∑T=1min(n,m)⌊Tn⌋×⌊Tm⌋×F(T)
求出
F
(
T
)
F(T)
F(T) 的前缀和每次询问一维数论分块就可以了。
时间复杂度
O
(
n
log
n
+
T
n
)
O(n\log n + T\sqrt{n})
O(nlogn+Tn)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e6 + 10;
const LL mod = 1e9 + 7;
int T, n, m, tot;
bool vis[N];
LL k, mu[N], prime[N], f[N], val[N];
LL sum[N];
inline LL Pow(LL x, LL y) {
LL res = 1LL, k = x;
while(y) {
if(y & 1) res = (res * k) % mod;
y >>= 1;
k = (k * k) % mod;
}
return res;
}
void pre() {
for(int i = 1; i < N; i ++ ) val[i] = Pow(1LL * i, k);
mu[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
for(int j = 1; i * prime[j] < N && j <= tot; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for(int i = 1; i < N; i ++ ) {
for(int j = 1; j * i < N; j ++ ) {
f[i * j] = (f[i * j] + mu[i] * val[j] % mod) % mod;
}
}
for(int i = 1; i < N; i ++ ) sum[i] = (sum[i - 1] + f[i]) % mod;
}
LL calc(int n, int m) {
LL res = 0;
int l = 1, r, t = min(n, m);
while(l <= t) {
r = min({n / (n / l), m / (m / l), t});
res = (res + ((sum[r] - sum[l - 1]) % mod + mod) % mod * 1LL * (n / l) % mod * (m / l) % mod) % mod;
l = r + 1;
}
return res;
}
int main() {
scanf("%d%lld", &T, &k);
pre();
while(T -- ) {
scanf("%d%d", &n, &m);
printf("%lld\n", calc(n, m));
}
return 0;
}
【BZOJ2693】jzptab最小公倍数之和
题意:
给你一个
T
T
T,
T
T
T组询问,每次给定一个
n
,
m
n,m
n,m。求
∑
i
=
1
n
∑
j
=
1
m
l
c
m
(
i
,
j
)
\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)
∑i=1n∑j=1mlcm(i,j)。答案对
1
0
8
+
9
10^8 +9
108+9 取模。
1
≤
T
≤
1
0
4
1\leq T \leq 10^4
1≤T≤104,
1
≤
n
,
m
≤
1
0
7
1 \leq n,m \leq 10^7
1≤n,m≤107
分析:
我们都知道
l
c
m
(
i
,
j
)
=
i
×
j
g
c
d
(
i
,
j
)
lcm(i, j) = \frac{i \times j}{gcd(i, j)}
lcm(i,j)=gcd(i,j)i×j
所以有
∑
i
=
1
n
∑
j
=
1
m
l
c
m
(
i
,
j
)
\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)
∑i=1n∑j=1mlcm(i,j)
=
∑
i
=
1
n
∑
j
=
1
m
i
×
j
g
c
d
(
i
,
j
)
=\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{i\times j}{gcd(i, j)}
=∑i=1n∑j=1mgcd(i,j)i×j
=
∑
d
=
1
m
i
n
(
n
,
m
)
1
d
∑
i
=
1
n
∑
j
=
1
m
i
×
j
×
[
g
c
d
(
i
,
j
)
=
d
]
=\sum_{d=1}^{min(n, m)}\frac{1}{d} \sum_{i = 1}^{n}\sum_{j=1}^{m}i\times j \times [gcd(i, j) = d]
=∑d=1min(n,m)d1∑i=1n∑j=1mi×j×[gcd(i,j)=d]
=
∑
d
=
1
m
i
n
(
n
,
m
)
1
d
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
(
i
d
)
×
(
j
d
)
×
[
g
c
d
(
i
,
j
)
=
1
]
=\sum_{d=1}^{min(n, m)}\frac{1}{d}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{m}{d} \right \rfloor}(id) \times (jd) \times [gcd(i, j) = 1]
=∑d=1min(n,m)d1∑i=1⌊dn⌋∑j=1⌊dm⌋(id)×(jd)×[gcd(i,j)=1]
=
∑
d
=
1
m
i
n
(
n
,
m
)
d
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
i
×
j
×
∑
k
∣
g
c
d
(
i
,
j
)
μ
(
k
)
=\sum_{d=1}^{min(n, m)}d\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}i \times j \times \sum_{k|gcd(i, j)}\mu(k)
=∑d=1min(n,m)d∑i=1⌊dn⌋∑j=1⌊dm⌋i×j×∑k∣gcd(i,j)μ(k)
=
∑
d
=
1
m
i
n
(
n
,
m
)
d
∑
k
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
k
)
∑
i
=
1
⌊
n
d
k
⌋
∑
j
=
1
⌊
m
d
k
⌋
(
i
k
)
×
(
j
k
)
=\sum_{d=1}^{min(n, m)}d\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(k)\sum_{i = 1}^{\left \lfloor \frac{n}{dk} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{dk} \right \rfloor}(ik) \times (jk)
=∑d=1min(n,m)d∑k=1min(⌊dn⌋,⌊dm⌋)μ(k)∑i=1⌊dkn⌋∑j=1⌊dkm⌋(ik)×(jk)
=
∑
d
=
1
m
i
n
(
n
,
m
)
d
∑
k
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
k
)
×
k
2
×
f
(
⌊
n
d
k
⌋
)
×
f
(
⌊
m
d
k
⌋
)
=\sum_{d =1}^{min(n, m)}d\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times k^2 \times f(\left \lfloor \frac{n}{dk} \right \rfloor) \times f(\left \lfloor \frac{m}{dk} \right \rfloor)
=∑d=1min(n,m)d∑k=1min(⌊dn⌋,⌊dm⌋)μ(k)×k2×f(⌊dkn⌋)×f(⌊dkm⌋)
其中 f ( n ) = ∑ i = 1 n i = n × ( n + 1 ) 2 f(n) = \sum_{i=1}^{n}i = \frac{n \times (n + 1)}{2} f(n)=∑i=1ni=2n×(n+1)
然后设 T = d k T = dk T=dk
=
∑
T
=
1
m
i
n
(
n
,
m
)
f
(
⌊
n
T
⌋
)
×
f
(
⌊
m
T
⌋
)
∑
d
∣
T
d
2
×
(
T
d
)
×
μ
(
d
)
=\sum_{T=1}^{min(n, m)}f(\left \lfloor \frac{n}{T} \right \rfloor) \times f(\left \lfloor \frac{m}{T} \right \rfloor) \sum_{d|T} d^2 \times (\frac{T}{d}) \times \mu(d)
=∑T=1min(n,m)f(⌊Tn⌋)×f(⌊Tm⌋)∑d∣Td2×(dT)×μ(d)
=
∑
T
=
1
m
i
n
(
n
,
m
)
f
(
⌊
n
T
⌋
)
×
f
(
⌊
m
T
⌋
)
∑
d
∣
T
d
×
T
×
μ
(
d
)
=\sum_{T=1}^{min(n, m)}f(\left \lfloor \frac{n}{T} \right \rfloor) \times f(\left \lfloor \frac{m}{T} \right \rfloor) \sum_{d|T} d \times T \times \mu(d)
=∑T=1min(n,m)f(⌊Tn⌋)×f(⌊Tm⌋)∑d∣Td×T×μ(d)
设
F
(
T
)
=
T
×
∑
d
∣
T
d
×
μ
(
d
)
F(T) = T\times \sum_{d|T}d\times \mu(d)
F(T)=T×∑d∣Td×μ(d)
F
(
T
)
F(T)
F(T) 可以在
O
(
n
ln
n
)
O(n \ln n)
O(nlnn) 的复杂度内预处理,但这样还是会超时。
其实我们可以对于每个
T
T
T,求出
F
(
T
)
=
∑
d
∣
T
d
×
μ
(
d
)
F(T) = \sum_{d|T}d\times \mu(d)
F(T)=∑d∣Td×μ(d),然后再乘
T
T
T 就可以了。
接着发现
F
F
F 是积性函数,因为对于互质的
n
,
m
n, m
n,m,
F
(
n
m
)
=
F
(
n
)
×
F
(
m
)
F(nm) = F(n) \times F(m)
F(nm)=F(n)×F(m)。这是因为
n
,
m
n, m
n,m 互质,所以任意一个
n
m
nm
nm 的因数
t
t
t 都能表示成
n
n
n 的一个因数
p
p
p 乘
m
m
m 的一个因数
q
q
q的形式。而由于莫比乌斯函数是积性函数,因此
t
×
μ
(
t
)
=
p
×
q
×
μ
(
p
)
×
μ
(
q
)
t\times\mu(t) = p \times q \times \mu(p)\times \mu(q)
t×μ(t)=p×q×μ(p)×μ(q)。证明了
F
F
F 是积性函数。
我们考虑在线性筛的过程中求出
F
F
F 函数:
对于当前
i
i
i 和素数
p
p
p
- i % p ≠ 0 i \% p \ne 0 i%p=0,那么 F [ i × p ] = F [ i ] × F [ p ] F[i \times p] = F[i] \times F[p] F[i×p]=F[i]×F[p]。因为 i , p i,p i,p 互质
- i % p = 0 i \% p =0 i%p=0,那么 F [ i × p ] = F [ i ] F[i \times p] = F[i] F[i×p]=F[i]。分枚举因数 d d d 时包不包括新加进来的 p p p 两种情况考虑:如果不包括,那就是 F [ i ] F[i] F[i]。如果包括,那么此时 d d d 中 p p p 的幂次一定大于 1 1 1, μ ( d ) = 0 \mu(d) = 0 μ(d)=0,没有贡献。
- 对于质数 p p p, F [ p ] = 1 − p F[p] = 1 - p F[p]=1−p
这样就能线性求出
F
F
F 了。然后每次询问一维数论分块就好了。
复杂度
O
(
n
+
T
n
)
O(n + T\sqrt{n})
O(n+Tn)
CODE:
// 两次分块可以把复杂度做到O(n),但是如果多测就寄了
// 一般套路是考虑把第二次分块里的分母(不换元)提出来枚举,然后里面的函数观察发现是积性函数可以提前预处理
// 然后就可以变成一次分块了。时间复杂度O(T√n)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e8 + 9;
const int N = 1e7 + 10;
int T, n, m, mu[N], prime[N], tot;
bool vis[N];
LL F[N];
void pre() {
mu[1] = 1; F[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1; F[i] = (mod + 1LL - i);}
for(int j = 1; 1LL * i * prime[j] < 1LL * N && j <= tot; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
F[i * prime[j]] = F[i];
break;
}
mu[i * prime[j]] = -mu[i];
F[i * prime[j]] = F[i] * F[prime[j]] % mod;
}
}
for(int i = 1; i < N; i ++ ) {
F[i] = F[i] * 1LL * i % mod;
F[i] = (F[i] + F[i - 1]) % mod;
}
}
inline LL f(int n) {
return 1LL * (n + 1LL) * n / 2 % mod;
}
LL calc(int n, int m) {
int t = min(n, m);
int l = 1, r; LL res = 0;
while(l <= t) {
r = min({n / (n / l), m / (m / l), t});
res = ((res + f(n / l) * f(m / l) % mod * (F[r] - F[l - 1]) % mod) % mod + mod) % mod;
l = r + 1;
}
return res;
}
int main() {
pre();
scanf("%d", &T);
while(T -- ) {
scanf("%d%d", &n, &m);
printf("%lld\n", calc(n, m));
}
return 0;
}
[bzoj3529-Sdoi2014] 数表
点这里
题意:
设
f
(
n
)
=
∑
d
∣
n
d
f(n) = \sum_{d|n}d
f(n)=∑d∣nd。
Q
Q
Q 次询问,每次给你
n
,
m
,
a
n, m, a
n,m,a,求
∑
i
=
1
n
∑
j
=
1
m
f
(
g
c
d
(
i
,
j
)
)
[
f
(
g
c
d
(
i
,
j
)
)
≤
a
]
\sum_{i = 1}^{n}\sum_{j = 1}^{m}f(gcd(i, j))[f(gcd(i, j)) \leq a]
∑i=1n∑j=1mf(gcd(i,j))[f(gcd(i,j))≤a]
1
≤
n
,
m
≤
1
0
5
1 \leq n, m \leq 10^5
1≤n,m≤105,
1
≤
Q
≤
2
×
1
0
4
1 \leq Q \leq 2 \times 10^4
1≤Q≤2×104
分析:
[
f
(
g
c
d
(
i
,
j
)
)
≤
a
]
[f(gcd(i, j)) \leq a]
[f(gcd(i,j))≤a] 的限制不好做。
我们考虑设一个新函数
g
(
n
)
=
{
f
(
n
)
f
(
n
)
≤
a
0
f
(
n
)
>
a
g(n) = \begin{cases} f(n) & \text{} f(n)\leq a \\ 0 & \text{} f(n) > a \end{cases}
g(n)={f(n)0f(n)≤af(n)>a
那么式子变成了
∑
i
=
1
n
∑
j
=
1
m
g
(
g
c
d
(
i
,
j
)
)
\sum_{i = 1}^{n}\sum_{j = 1}^{m}g(gcd(i, j))
∑i=1n∑j=1mg(gcd(i,j))
由于
g
c
d
gcd
gcd 的取值比较小,我们可以求出所有的
f
(
n
)
f(n)
f(n)。然后按照
f
(
n
)
f(n)
f(n) 从小到大的顺序把
n
n
n 排序,把询问按照
a
a
a 从小到大的顺序排序后,我们就可以每次加入一些
n
n
n,把他们的
g
(
n
)
g(n)
g(n) 由
0
0
0 变为
f
(
n
)
f(n)
f(n)。
然后考虑上面的式子怎么求:
∑
i
=
1
n
∑
j
=
1
m
g
(
g
c
d
(
i
,
j
)
)
\sum_{i = 1}^{n}\sum_{j = 1}^{m}g(gcd(i, j))
∑i=1n∑j=1mg(gcd(i,j))
=
∑
d
=
1
m
i
n
(
n
,
m
)
g
(
d
)
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
=\sum_{d=1}^{min(n, m)}g(d)\sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = d]
=∑d=1min(n,m)g(d)∑i=1n∑j=1m[gcd(i,j)=d]
=
∑
d
=
1
m
i
n
(
n
,
m
)
g
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
g
c
d
(
i
,
j
)
=
1
]
=\sum_{d=1}^{min(n, m)}g(d)\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i, j) = 1]
=∑d=1min(n,m)g(d)∑i=1⌊dn⌋∑j=1⌊dm⌋[gcd(i,j)=1]
=
∑
d
=
1
m
i
n
(
n
,
m
)
g
(
d
)
∑
k
=
1
m
i
n
(
⌊
n
d
⌋
,
⌊
m
d
⌋
)
μ
(
k
)
×
⌊
n
d
k
⌋
×
⌊
m
d
k
⌋
=\sum_{d=1}^{min(n, m)}g(d)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor,\left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor
=∑d=1min(n,m)g(d)∑k=1min(⌊dn⌋,⌊dm⌋)μ(k)×⌊dkn⌋×⌊dkm⌋
=
∑
T
=
1
m
i
n
(
n
,
m
)
⌊
n
T
⌋
×
⌊
m
T
⌋
∑
d
∣
T
g
(
d
)
×
μ
(
T
d
)
=\sum_{T = 1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \sum_{d|T}g(d)\times \mu(\frac{T}{d})
=∑T=1min(n,m)⌊Tn⌋×⌊Tm⌋∑d∣Tg(d)×μ(dT)
设
F
(
T
)
=
∑
d
∣
T
g
(
d
)
×
μ
(
T
d
)
F(T) = \sum_{d|T}g(d) \times \mu(\frac{T}{d})
F(T)=∑d∣Tg(d)×μ(dT)
那么我们需要维护出来
F
F
F 的前缀和。
每次新加入一个
d
d
d 就把它的所有倍数
T
T
T 加上
f
(
d
)
×
μ
(
T
d
)
f(d) \times \mu(\frac{T}{d})
f(d)×μ(dT)。那么我们需要一个单点修改,区间求和的数据结构。树状数组就可以。时间复杂度是 调和级数 加上树状数组的复杂度。
O
(
n
log
2
n
+
Q
n
log
n
)
O(n\log^2 n + Q\sqrt{n}\log n)
O(nlog2n+Qnlogn)。
CODE:
#include<bits/stdc++.h>
#define int long long
using namespace std; // 首先容易求出 f(d), 然后离线就好了
const int N = 1e5 + 10;
const int mod = (1LL << 31);
int n, m, q;
int mu[N], prime[N], tot, idx[N];
bool vis[N];
int f[N], F[N], c[N], ans[N];
struct qq {
int n, m, v, id;
}qc[N];
int lowbit(int x) {return x & -x;}
void add(int x, int y) {
for(; x < N; x += lowbit(x)) c[x] = (c[x] + y) % mod;
}
int ask(int x) {
int res = 0;
for(; x; x -= lowbit(x)) res = (res + c[x]) % mod;
return res;
}
void pre() {
mu[1] = 1;
for(int i = 2; i < N; i ++ ) {
if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
for(int j = 1; i * prime[j] < N && j <= tot; j ++ ) {
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for(int i = 1; i < N; i ++ ) {
for(int j = 1; j * i < N; j ++ ) {
f[j * i] = (f[j * i] + (i)) % mod;
}
}
}
bool cmp(qq x, qq y) {
return x.v < y.v;
}
bool cmpf(int x, int y) {
return f[x] < f[y];
}
void Add(int x) { // 加入下标为x的
for(int i = 1; i * x < N; i ++ ) {
add(i * x, (f[x] * mu[i] + mod) % mod);
}
}
int calc(int n, int m) {
int t = min(n, m);
int l = 1, r; int res = 0;
while(l <= t) {
r = min({n / (n / l), m / (m / l), t});
res = (res + (1LL * ((ask(r) - ask(l - 1)) % mod) * (n / l) % mod * (m / l) % mod) + mod) % mod;
l = r + 1;
}
return res;
}
signed main() {
pre();
scanf("%lld", &q);
for(int i = 1; i <= q; i ++ ) {
int v; scanf("%lld%lld%lld", &n, &m, &v);
qc[i] = (qq) {n, m, v, i};
}
sort(qc + 1, qc + q + 1, cmp);
for(int i = 1; i < N; i ++ ) idx[i] = i;
sort(idx + 1, idx + N, cmpf);
int now = 1;
for(int i = 1; i <= q; i ++ ) {
while(now < N && f[idx[now]] <= qc[i].v) {
Add(idx[now]);
now ++;
}
ans[qc[i].id] = calc(qc[i].n, qc[i].m);
}
for(int i = 1; i <= q; i ++ ) printf("%lld\n", ans[i]);
return 0;
}
练习题
51nod 1675 序列变换
点这里
题意:
给你两个长度为
n
n
n 的序列
a
,
b
a,b
a,b,求有多少对
(
x
,
y
)
(x,y)
(x,y),满足以下两个条件:
- g c d ( x , y ) = 1 gcd(x, y) = 1 gcd(x,y)=1
- a b x = b a y a_{b_x} = b_{a_y} abx=bay
1 ≤ n ≤ 1 0 5 , 1 ≤ a i , b i ≤ n 1 \leq n \leq 10^5,1\leq a_i,b_i \leq n 1≤n≤105,1≤ai,bi≤n
BZOJ3561 DZY Loves Math VI
点这里
题意:
给定正整数
n
,
m
n,m
n,m,求:
∑
i
=
1
n
∑
j
=
1
n
l
c
m
(
i
,
j
)
g
c
d
(
i
,
j
)
\sum_{i = 1}^{n}\sum_{j = 1}^{n}lcm(i, j)^{gcd(i, j)}
∑i=1n∑j=1nlcm(i,j)gcd(i,j)
1
≤
n
,
m
≤
5
×
1
0
5
1 \leq n,m \leq 5 \times 10^5
1≤n,m≤5×105
[bzoj3309]DZY Loves Math
点这里
题意:
对于正整数
n
n
n,定义
f
(
n
)
f(n)
f(n) 为
n
n
n 所含质因子的的最大幂指数。特别的
f
(
1
)
=
0
f(1) = 0
f(1)=0
T
T
T 组询问,每次给定
a
,
b
a,b
a,b,求
∑
i
=
1
a
∑
j
=
1
b
f
(
g
c
d
(
i
,
j
)
)
\sum_{i = 1}^{a}\sum_{j = 1}^{b}f(gcd(i, j))
∑i=1a∑j=1bf(gcd(i,j))
1
≤
T
≤
1
0
4
,
1
≤
a
,
b
≤
1
0
7
1 \leq T \leq 10^4,1\leq a,b \leq 10^7
1≤T≤104,1≤a,b≤107
[bzoj4816–Sdoi2017] 数字表格
点这里
题意:
f
(
n
)
f(n)
f(n) 表示斐波那契第
n
n
n 项。
T
T
T 组询问,每次给定
n
,
m
n,m
n,m。求
∏
i
=
1
n
∏
j
=
1
m
f
[
g
c
d
(
i
,
j
)
]
\prod_{i = 1}^{n}\prod_{j = 1}^{m}f[gcd(i, j)]
∏i=1n∏j=1mf[gcd(i,j)]。答案对
1
0
9
+
7
10^9 + 7
109+7 取模。
1
≤
T
≤
1000
,
1
≤
n
,
m
≤
1
0
6
1 \leq T \leq 1000,1\leq n, m \leq 10^6
1≤T≤1000,1≤n,m≤106
[hdoj4746]Mophues
点这里
题意:
Q
Q
Q 组询问,每次给你
n
,
m
,
p
n, m, p
n,m,p。求
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
≤
p
]
\sum_{i = 1}^{n} \sum_{j = 1}^{m}[gcd(i, j) \leq p]
∑i=1n∑j=1m[gcd(i,j)≤p]
1
≤
Q
≤
5
×
1
0
3
,
2
≤
n
,
m
,
p
≤
5
×
1
0
5
1 \leq Q \leq 5 \times 10^3, 2 \leq n,m,p \leq 5 \times 10^5
1≤Q≤5×103,2≤n,m,p≤5×105
技巧总结
- 遇到与 g c d gcd gcd 有关的式子可以考虑莫比乌斯反演
- 把枚举 g c d gcd gcd 的式子提前是常见思路
- 遇到需要二次分块或者 分母是两个变量乘积 的情况,可以考虑交换枚举顺序,先枚举乘积。
- 遇到对 g c d gcd gcd 的大小有限制的题目可以考虑离线,把 g c d gcd gcd 由小到大依次加入。