目录
懵逼乌斯反演总结
前置知识
1.1 线性筛 (欧拉筛)
int prime[N] , tot ;
bool is[N] ;
void get_prime( int n )
{
is[0] = is[1] = 1 ;
for(int i = 2 ; i <= n ; i ++ ) {
if( !is[i] ) {
prime[++tot] = i ;
}
for(int j = 1 ; j <= tot && i*prime[j] <= n ; j ++ ) {
is[i*prime[j]] = 1 ;
if( i%prime[j] == 0 ) {
break ;
}
}
}
}
需要注意的是,线性筛有着非常优秀的性质
对于每个数,一定是被其 最小质因子 标记的
换句话说,对于一个数 n = p 1 a 1 p 2 a 2 . . . p k a k n=p_1^{a_1}p_2^{a_2}...p_k^{a_k} n=p1a1p2a2...pkak,它被标记的过程一定是 从 1 1 1 开始,每次加入当前最大的因子,即 p k p_k pk,然后 p k − 1 p_{k-1} pk−1,直到把 p 1 p_1 p1 加完,就标记到了
线性筛可以高效的计算某些数论函数的值,但对于不同的函数可能有不同的处理方式
1.2 整除分块 (数论分块)
考虑计算: ∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \left \lfloor \frac{n}{i} \right \rfloor i=1∑n⌊in⌋
其中 n ≤ 1 0 12 n\leq10^{12} n≤1012
研究 ⌊ n i ⌋ \left \lfloor \frac{n}{i} \right \rfloor ⌊in⌋ 这个函数,首先它随 i i i 增大单调不升;其次,会有很多数值相等的段
这启发我们:将相同的一段打包计算!
引理 1
∀ a , b , c ∈ Z , ⌊ a b c ⌋ = ⌊ ⌊ a b ⌋ c ⌋ \forall a,b,c\in\mathbb{Z} , \left \lfloor \frac{a}{bc} \right \rfloor=\left \lfloor \frac{\left\lfloor \frac{a}{b}\right\rfloor}{c} \right \rfloor ∀a,b,c∈Z,⌊bca⌋=⌊c⌊ba⌋⌋
摘自 oi.wiki
引理 2
⌊ n i ⌋ \large\left \lfloor \frac{n}{i} \right \rfloor ⌊in⌋ 的取值种类不会超过 ⌊ 2 n ⌋ \left\lfloor 2\sqrt n \right\rfloor ⌊2n⌋
这个引理也告诉我们:段数 n \sqrt n n 的结论是 均摊 的结果!
引理 3
对于常数 n n n,使得式子 ⌊ n i ⌋ = ⌊ n j ⌋ \left\lfloor\frac{n}{i}\right\rfloor=\left\lfloor\frac{n}{j}\right\rfloor ⌊in⌋=⌊jn⌋ 成立且满足 i ≤ j ≤ n i\leq j\leq n i≤j≤n 的最大的 j j j 为 ⌊ n ⌊ n i ⌋ ⌋ \large\left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor ⌊⌊in⌋n⌋
这个式子告诉我们如何寻找左端点为 l l l 的段的右端点
实现
数论分块可以在 O ( n ) O(\sqrt n) O(n) 的复杂度内解决形如
∑ i = 1 n ⌊ n i ⌋ f ( i ) \sum_{i=1}^n\left\lfloor\frac{n}{i}\right\rfloor f(i) i=1∑n⌊in⌋f(i) 的求和式
需要能够快速得到 f f f 在某一段区间的 s u m sum sum
要么是预处理前缀和,要么可以借助数学知识直接得到式子
基本模板:
int l = 1 , r ;
while( l <= n ) {
r = n/(n/l) ;// 找到右端点
// 对 i:[l,r], 这个取整式是一个定值
ans -= 1LL*(K/l)*calc(l,r) ;
l = r+1 ;
}
例 1
求: ∑ i = 1 k ⌊ n i ⌋ ⌊ m i ⌋ \sum_{i=1}^k\left\lfloor\frac{n}{i}\right\rfloor\left\lfloor\frac{m}{i}\right\rfloor i=1∑k⌊in⌋⌊im⌋
二维数论分块,为了保证同一段内取值的一定性, r r r 应该取较小的那个转折点
k = min(n,m) ;
int l = 1 , r ;
while( l <= k ) {
r = min( n/(n/l) , m/(m/l) ) ;
ans += 1LL*(n/l)*(m/l)*calc(l,r) ;
l = r+1 ;
}
d d d 维数论分块的复杂度为 O ( d n ) O(d\sqrt n) O(dn)
例 2
上取整数论分块
注意到 ⌈ n i ⌉ = ⌊ n − 1 i ⌋ + 1 \left \lceil \frac{n}{i} \right \rceil=\left\lfloor\frac{n-1}{i}\right\rfloor+1 ⌈in⌉=⌊in−1⌋+1
所以 n n n 的上取整分块等价于 n − 1 n-1 n−1 的下取整分块
需要注意分母不为 0 0 0
例 3
好难的题
一句话题意:对于 x , y ∈ [ l , r ] x,y\in[l,r] x,y∈[l,r],求 g c d ( x , y ) gcd(x,y) gcd(x,y) 的种类数
l ≤ 1 0 9 , r ≤ 1 0 18 l\leq 10^9,r\leq10^{18} l≤109,r≤1018
考虑每个 g c d gcd gcd 能否出现
能想到的是对于 每个 d d d 可以找到区间 [ l , r ] [l,r] [l,r] 中所有它的倍数,然后没法了,不知道怎么恰好取到 d d d
但我们会发现 : g c d ( k d , ( k + 1 ) d ) = d gcd(kd,(k+1)d)=d gcd(kd,(k+1)d)=d ,也就是说 [ l , r ] [l,r] [l,r] 中 只要有两个及以上 d d d 的倍数,那么任取相邻的两个就行
分析一下,当 l ≤ d ≤ r l\leq d\leq r l≤d≤r 时, d d d 本身就在区间中出现一次了,那么只需 2 × d ≤ r 2\times d\leq r 2×d≤r 即可,这部分可以 O ( 1 ) O(1) O(1) 算
接下来,就是要求 ∑ d = 1 l [ ⌈ l d ⌉ < ⌊ r d ⌋ ] \sum_{d=1}^l[\left \lceil \frac{l}{d} \right \rceil < \left\lfloor \frac{r}{d}\right\rfloor] d=1∑l[⌈dl⌉<⌊dr⌋]
枚举 d d d 复杂度还是不行,我们数论分块
一个朴素想法是同时对 l , r l,r l,r 分块,但对 r r r 分块的复杂度是 O ( 1 0 18 ) O(\sqrt {10^{18}}) O(1018),寄
那么只能对 l l l 分块了,确定了 ⌈ l d ⌉ \large\left \lceil \frac{l}{d} \right \rceil ⌈dl⌉ 的值后,合法的 r r r 就成了一段区间,可以 O ( 1 ) O(1) O(1) 算范围
复杂度 O ( l ) O(\sqrt l) O(l)
int T ;
LL l , r , ans ;
void solve()
{
// d>=l
ans = max((r/2)-l+1,0LL) ;
// d < l
if( l > 1 ) { // 不能让分母为 0
LL L = 1 , R = 0 ;
while( L < l ) {
R = (l-1)/((l-1)/L) ;
LL x = (l-1)/L+1+1 , rr = r/x ;
rr = max(0LL,rr-2) ;
while( x<=r/(rr+1) ) rr++ ;//这两行是抹平微小的误差
rr = min(rr,R) ;
ans += max(0LL,rr-L+1) ;
L = R+1 ;
}
}
printf("%lld\n" , ans ) ;
}
例 4
一个小学生都知道的结论: n m o d i = n − ⌊ n i ⌋ i n\mod i=n-\left\lfloor\frac{n}{i}\right\rfloor i nmodi=n−⌊in⌋i
然后就是化化式子
1.3 数学知识
积性函数
定义:若 ∀ x , y \forall x,y ∀x,y 满足 g c d ( x , y ) = 1 gcd(x,y)=1 gcd(x,y)=1,都有 f ( x y ) = f ( x ) × f ( y ) f(xy)=f(x)\times f(y) f(xy)=f(x)×f(y),则 f f f 为积性函数
特别的,若不需要 x , y x,y x,y 互质的条件, ∀ x , y \forall x,y ∀x,y 都有 f ( x y ) = f ( x ) f ( y ) f(xy)=f(x)f(y) f(xy)=f(x)f(y),称 f f f 为完全积性函数
常见的积性函数:
- 单位函数 e ( n ) = [ n = 1 ] e(n)=[n=1] e(n)=[n=1] ,完全积性
- 幂函数 I d k ( n ) = n k \mathrm{Id}_k(n)=n^k Idk(n)=nk, I d 1 \mathrm{Id}_1 Id1 通常简记为 i d id id,完全积性
- 常数函数 1 ( n ) = 1 1(n)=1 1(n)=1,完全积性
- 约数函数
σ
k
(
n
)
=
∑
d
∣
n
d
k
\sigma_k(n)=\sum_{d|n}d^k
σk(n)=∑d∣ndk ,积性
特别的, k = 0 k=0 k=0 时为约数个数函数,可记为 d ( n ) d(n) d(n); k = 1 k=1 k=1 时为约数和函数,记为 σ ( n ) \sigma(n) σ(n) - 欧拉函数 φ ( n ) = ∑ i = 1 n [ g c d ( n , i ) = 1 ] \varphi(n)=\sum_{i=1}^n[gcd(n,i)=1] φ(n)=∑i=1n[gcd(n,i)=1],积性
- 莫比乌斯函数 μ ( n ) = { 1 n = 1 0 n 含有平方因子 ( − 1 ) k k 为 n 的不同质因子个数 } \mu(n)=\begin{Bmatrix} 1& n=1 \\ 0& n含有平方因子\\ (-1)^k& k为n的不同质因子个数 \end{Bmatrix} μ(n)=⎩ ⎨ ⎧10(−1)kn=1n含有平方因子k为n的不同质因子个数⎭ ⎬ ⎫,积性
莫比乌斯函数
μ ( n ) = { 1 n = 1 0 n 含有平方因子 ( − 1 ) k k 为 n 的不同质因子个数 } \mu(n)=\begin{Bmatrix} 1& n=1 \\ 0& n含有平方因子\\ (-1)^k& k为n的不同质因子个数 \end{Bmatrix} μ(n)=⎩ ⎨ ⎧10(−1)kn=1n含有平方因子k为n的不同质因子个数⎭ ⎬ ⎫
形式很奇怪,但实际上它反映了一种 对每个约数考虑的容斥系数
第二条说人话就是:若存在某个质数 p p p, n n n 的质因子分解中 p p p 的指数 ≥ 2 \geq 2 ≥2,则 μ ( n ) = 0 \mu(n)=0 μ(n)=0
性质
∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] d∣n∑μ(d)=[n=1]
证明:
设 n = p 1 a 1 . . . p k a k n=p_1^{a_1}...\ p_k^{a_k} n=p1a1... pkak
显然当 d d d 取到的某个质因子指数 ≥ 2 \geq 2 ≥2 是没有贡献的,只需考虑 0 , 1 0,1 0,1 的情况
设 k k k 个质因子中取 i i i 个指数为 1 1 1,那么原和式即为: ∑ i = 0 k C k i ( − 1 ) i = ( 1 − 1 ) k = [ k = 0 ] \sum_{i=0}^kC_k^i(-1)^i=(1-1)^k=[k=0] i=0∑kCki(−1)i=(1−1)k=[k=0]
( 最后一步相信你是理解的
即 [ n = 1 ] [n=1] [n=1],证毕
求法
积性函数,直接线性筛,好写
int mu[N] , prime[N] , tot ;
bool is[N] ;
void get_Mu( int n )
{
is[0] = is[1] = 1 ; mu[1] = 1 ;
for(int i = 2 ; i <= n ; i ++ ) {
if( !is[i] ) {
prime[++tot] = i ;
mu[i] = -1 ;
}
for(int j = 1 ; j <= tot && prime[j]*i <= n ; j ++ ) {
is[i*prime[j]] = 1 ;
if( i%prime[j] == 0 ) {
mu[i*prime[j]] = 0 ;//已经加过这个因子了
break ;
}
else mu[i*prime[j]] = -mu[i] ;//换了一种因子加
}
}
}
狄利克雷(Dirichlet)卷积
定义两个数论函数 f , g f,g f,g 的狄利克雷卷积为 ( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) g ( n d ) (f*g)(n)=\sum_{d|n}f(d)g(\frac{n}{d}) (f∗g)(n)=d∣n∑f(d)g(dn) 简单点说就是你一半,我一半,再求和
性质:
-
满足交换律、结合律
-
若 f , g f,g f,g 为积性函数, f ∗ g f*g f∗g 也为积性函数 (快速判断一个函数好不好筛)
-
设 e e e 为狄利克雷卷积的单位元,即 ( f ∗ e ) ( n ) = f ( n ) (f*e)(n)=f(n) (f∗e)(n)=f(n)
有 e = μ ∗ 1 = ∑ d ∣ n μ ( d ) e=\mu*1=\sum_{d|n}\mu(d) e=μ∗1=∑d∣nμ(d)
证明: ( f ∗ e ) ( n ) = ∑ d ∣ n f ( d ) e ( n d ) = ∑ d ∣ n f ( d ) ∑ k ∣ n d μ ( k ) = ∑ d ∣ n f ( d ) [ n d = 1 ] (f*e)(n)=\sum_{d|n}f(d)e(\frac{n}{d})=\sum_{d|n}f(d)\sum_{k|\frac{n}{d}}\mu(k)=\sum_{d|n}f(d)[\frac{n}{d}=1] (f∗e)(n)=d∣n∑f(d)e(dn)=d∣n∑f(d)k∣dn∑μ(k)=d∣n∑f(d)[dn=1]
最后一步用了 μ \mu μ 函数的性质
显然只有当 n = d n=d n=d 时才有贡献,贡献为 f ( n ) f(n) f(n)
所以 e = μ ∗ 1 e=\mu *1 e=μ∗1 满足单位元的性质
一些神经奇的结论
-
I d k ∗ 1 = σ k \mathrm{Id_k}*1=\sigma_k Idk∗1=σk
-
φ ∗ 1 = i d \varphi*1=id φ∗1=id
更常见的形式是 n = ∑ d ∣ n φ ( d ) n=\sum_{d|n}\varphi(d) n=d∣n∑φ(d)相信聪明的你一定会证明的 (利用积性函数的性质)
-
φ = I d ∗ μ \varphi=\mathrm{Id}*\mu φ=Id∗μ ,学过的两个函数就神奇地建立了联系!
计算
( f ∗ g ) ( n ) = ∑ d ∣ n f ( d ) g ( n d ) (f*g)(n)=\sum_{d|n}f(d)g(\frac{n}{d}) (f∗g)(n)=d∣n∑f(d)g(dn)
- 求单个函数值: O ( n ) O(\sqrt n) O(n) 暴力枚举因子
- 求
[
1
∼
n
]
[1\sim n]
[1∼n] 所有函数值:
- 暴力卷积,枚举 d d d,枚举倍数,复杂度 O ( n l o g n ) O(n\mathrm{log\ n}) O(nlog n) ,忽略了计算 f , g f,g f,g 的复杂度
- 有些时候的 “卷积” 是只和质因子有关的,Dirichlet 前缀和可以做到 O ( n l o g l o g n ) O(n\mathrm{log logn)} O(nloglogn)
- 更常见的,发现这个函数的某些性质,用线性筛筛出所有函数值,复杂度 O ( n ) O(n) O(n)
莫比乌斯反演
2.1 公式
设 f , g f,g f,g 为两个数论函数
如果有 f ( n ) = ∑ d ∣ n g ( d ) f(n)=\sum_{d|n}g(d) f(n)=d∣n∑g(d) 那么 g ( n ) = ∑ d ∣ n f ( d ) μ ( n d ) g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d}) g(n)=d∣n∑f(d)μ(dn)
学会了狄利克雷卷积后,证明就变得十分简单
1 1 1 式等价于: f = g ∗ 1 f=g*1 f=g∗1
2 2 2 式等价于: g = f ∗ μ g=f*\mu g=f∗μ
只需要 2 2 2 式两边同时卷上一个 1 1 1 即证
2.2 常用(唯一)结论
[ g c d ( i , j ) = 1 ] = ∑ k ∣ i , k ∣ j μ ( k ) [gcd(i,j)=1]=\sum_{k|i,k|j}\mu(k) [gcd(i,j)=1]=k∣i,k∣j∑μ(k)
拆开即可证明
2.3 例题
例 1
求全由小写字母组成、长度为 n n n 的周期性字符串的个数
周期性字符串的定义是 可以由至少一个子串重复大于等于两次得到的字符串
n ≤ 1 0 6 n\leq10^6 n≤106
设 f ( n ) f(n) f(n) 表示长度为 n n n 的所有串, g ( n ) g(n) g(n) 表示长度为 n n n,不具有周期的串
有 f ( n ) = ∑ d ∣ n g ( d ) f(n)=\sum_{d|n}g(d) f(n)=∑d∣ng(d) ,反演得 g ( n ) = ∑ d ∣ n f ( d ) μ ( n d ) g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d}) g(n)=∑d∣nf(d)μ(dn)
f f f 的求法是显然的,那么最终答案 f ( n ) − g ( n ) f(n)-g(n) f(n)−g(n)
感觉是不太符合直觉,但很优秀的做法
例 2
求: ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = 1 ] \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=1] ∑i=1n∑j=1m[gcd(i,j)=1] ,互质数对的个数
n , m ≤ 1 0 12 n,m\leq10^{12} n,m≤1012
直接套用结论
= ∑ i = 1 n ∑ j = 1 m ∑ k ∣ i , k ∣ j μ ( k ) =\sum_{i=1}^n \sum_{j=1}^m \sum_{k|i,k|j}\mu(k) =∑i=1n∑j=1m∑k∣i,k∣jμ(k)
考虑每个 μ ( k ) \mu(k) μ(k) 会贡献几次
= ∑ k = 1 m μ ( k ) ∑ i = 1 [ n / k ] ∑ j = 1 [ m / k ] 1 =\sum_{k=1}^m\mu(k)\sum_{i=1}^{[n/k]}\sum_{j=1}^{[m/k]}1 =∑k=1mμ(k)∑i=1[n/k]∑j=1[m/k]1
= ∑ k = 1 m μ ( k ) ⌊ n k ⌋ ⌊ m k ⌋ =\sum_{k=1}^m\mu(k) \left\lfloor\frac{n}{k}\right\rfloor \left\lfloor\frac{m}{k}\right\rfloor =∑k=1mμ(k)⌊kn⌋⌊km⌋
直接数论分块
例 3
求 ∑ i = 1 n ∑ j = 1 m i j [ g c d ( i , j ) = k ] \sum_{i=1}^n \sum_{j=1}^m ij[gcd(i,j)=k] i=1∑nj=1∑mij[gcd(i,j)=k]
n , m , k ≤ 1 0 12 n,m,k\leq 10^{12} n,m,k≤1012 ,对质数取模
与上一题有两点不同:带系数、不是互质;
既然 g c d gcd gcd 为 k k k,我们不妨直接枚举 i k , j k ik,jk ik,jk
= ∑ i = 1 [ n / k ] ∑ j = 1 [ m / k ] i j k 2 [ g c d ( i , j ) = 1 ] =\sum_{i=1}^{[n/k]}\sum_{j=1}^{[m/k]}ijk^2[gcd(i,j)=1] =∑i=1[n/k]∑j=1[m/k]ijk2[gcd(i,j)=1]
成功转化成 互质数对,带结论
= ∑ i = 1 [ n / k ] ∑ j = 1 [ m / k ] i j k 2 ∑ d ∣ i , d ∣ j μ ( d ) =\sum_{i=1}^{[n/k]}\sum_{j=1}^{[m/k]}ijk^2\sum_{d|i,d|j}\mu(d) =∑i=1[n/k]∑j=1[m/k]ijk2∑d∣i,d∣jμ(d)
= ∑ d = 1 m μ ( d ) k 2 ∑ i = 1 [ n / k / d ] i d ∑ j = 1 [ m / k / d ] j d =\sum_{d=1}^{m}\mu(d)k^2\sum_{i=1}^{[n/k/d]}id\sum_{j=1}^{[m/k/d]}jd =∑d=1mμ(d)k2∑i=1[n/k/d]id∑j=1[m/k/d]jd
= k 2 × ∑ d = 1 m μ ( d ) d 2 × ∑ i = 1 [ n / k / d ] i ∑ j = 1 [ m / k / d ] j =k^2 \times \sum_{d=1}^{m}\mu(d)d^2 \times \sum_{i=1}^{[n/k/d]}i\sum_{j=1}^{[m/k/d]}j =k2×∑d=1mμ(d)d2×∑i=1[n/k/d]i∑j=1[m/k/d]j
后两项就是等差数列了,对 ⌊ n k d ⌋ \left\lfloor \frac{n}{kd} \right\rfloor ⌊kdn⌋, ⌊ m k d ⌋ \left\lfloor \frac{m}{kd} \right\rfloor ⌊kdm⌋ 数论分块即可做到 O ( n ) O(\sqrt n) O(n)
例 4
求 ∑ i = 1 n ∑ j = 1 m l c m ( i , j ) \sum_{i=1}^{n} \sum_{j=1}^{m}lcm(i,j) i=1∑nj=1∑mlcm(i,j)
多次询问, T ≤ 1 0 3 , n , m ≤ 1 0 7 T\leq 10^3,\ n,m\leq10^7 T≤103, n,m≤107
首先可以转化成 g c d gcd gcd 的形式
= ∑ i = 1 n ∑ j = 1 m i j g c d ( i , j ) =\sum_{i=1}^{n} \sum_{j=1}^{m}\frac{ij}{\mathrm{gcd}(i,j)} =∑i=1n∑j=1mgcd(i,j)ij
同样的考虑每个 g c d \mathrm{gcd} gcd 的贡献
= ∑ i = 1 n ∑ j = 1 m ∑ d = 1 m i j d [ g c d ( i , j ) = d ] =\sum_{i=1}^{n} \sum_{j=1}^{m} \sum_{d=1}^{m} \frac{ij}{d}\ [\mathrm{gcd}(i,j)=d] =∑i=1n∑j=1m∑d=1mdij [gcd(i,j)=d]
= ∑ d = 1 m 1 d × ∑ i = 1 n ∑ j = 1 m i j [ g c d ( i , j ) = d ] =\sum_{d=1}^{m} \frac{1}{d} \times \sum_{i=1}^{n} \sum_{j=1}^{m} ij\ [\mathrm{gcd}(i,j)=d] =∑d=1md1×∑i=1n∑j=1mij [gcd(i,j)=d]
= ∑ d = 1 m 1 d × ∑ i = 1 [ n / d ] ∑ j = 1 [ m / d ] i j d 2 [ g c d ( i , j ) = 1 ] =\sum_{d=1}^{m} \frac{1}{d} \times \sum_{i=1}^{[n/d]} \sum_{j=1}^{[m/d]} ijd^2\ [\mathrm{gcd}(i,j)=1] =∑d=1md1×∑i=1[n/d]∑j=1[m/d]ijd2 [gcd(i,j)=1]
= ∑ d = 1 m d × ∑ i = 1 [ n / d ] ∑ j = 1 [ m / d ] i j ∑ k ∣ i , k ∣ j μ ( k ) =\sum_{d=1}^{m} d \times \sum_{i=1}^{[n/d]} \sum_{j=1}^{[m/d]} ij \sum_{k|i,k|j}\mu(k) =∑d=1md×∑i=1[n/d]∑j=1[m/d]ij∑k∣i,k∣jμ(k)
= ∑ d = 1 m d ∑ k = 1 [ m / d ] μ ( k ) × ∑ i = 1 [ n / k d ] ∑ j = 1 [ m / k d ] i k × j k =\sum_{d=1}^{m} d \sum_{k=1}^{[m/d]}\mu(k)\times \sum_{i=1}^{[n/kd]} \sum_{j=1}^{[m/kd]} ik\times jk =∑d=1md∑k=1[m/d]μ(k)×∑i=1[n/kd]∑j=1[m/kd]ik×jk
= ∑ d = 1 m d ∑ k = 1 [ m / d ] μ ( k ) k 2 × ∑ i = 1 [ n / k d ] ∑ j = 1 [ m / k d ] i j =\sum_{d=1}^{m} d \sum_{k=1}^{[m/d]}\mu(k) k^2 \times \sum_{i=1}^{[n/kd]} \sum_{j=1}^{[m/kd]} ij =∑d=1md∑k=1[m/d]μ(k)k2×∑i=1[n/kd]∑j=1[m/kd]ij
以上用到的技巧都与前面的例题相同,相信你一定可以独立完成的
与之前不同的是,这里最外层的 d d d 不是定值,需要枚举,使得复杂度增高
朴素的想法是,对于外层 d d d 的枚举 按 ⌊ n d ⌋ \left\lfloor \frac{n}{d} \right\rfloor ⌊dn⌋, ⌊ m d ⌋ \left\lfloor \frac{m}{d} \right\rfloor ⌊dm⌋ 进行数论分块,这样每段内后面的式子是个定值
要计算后面的式子,可以再对 k k k 的枚举进行分块,与 例 3 一致
然而这样复杂度是 O ( n × n ) = O ( n ) O(\sqrt n \times \sqrt n)=O(n) O(n×n)=O(n) (网上都这么说,但实际打表发现比 n n n 小得多,但题目要卡这种做法的话还是过不了的)
一个优雅且很常用的技巧:更改枚举变量
枚举 d , k d,k d,k 后,需要 d × k d\times k d×k 的值,我们考虑直接枚举乘积
令 T = k d T=kd T=kd ,则 d ∣ T d|T d∣T
= ∑ T = 1 m ∑ i = 1 [ n / T ] i ∑ j = 1 [ m / T ] j × ∑ d ∣ T d × ( T d ) 2 μ ( T d ) =\sum_{T=1}^{m} \sum_{i=1}^{[n/T]}i \sum_{j=1}^{[m/T]}j \times \sum_{d|T}d\times (\frac{T}{d})^2\mu(\frac{T}{d}) =∑T=1m∑i=1[n/T]i∑j=1[m/T]j×∑d∣Td×(dT)2μ(dT)
= ∑ T = 1 m ∑ i = 1 [ n / T ] i ∑ j = 1 [ m / T ] j × ∑ d ∣ T T d μ ( T d ) T =\sum_{T=1}^{m} \sum_{i=1}^{[n/T]}i \sum_{j=1}^{[m/T]}j \times \sum_{d|T}\frac{T}{d}\mu(\frac{T}{d})T =∑T=1m∑i=1[n/T]i∑j=1[m/T]j×∑d∣TdTμ(dT)T
注意后面的式子,设 f ( T ) = ∑ d ∣ T μ ( d ) d T f(T)=\sum_{d|T}\mu(d)d\ T f(T)=∑d∣Tμ(d)d T
先不管 T T T, f ( T ) = ∑ d ∣ T μ ( d ) d f(T)=\sum_{d|T}\mu(d)d f(T)=∑d∣Tμ(d)d
发现这个东西并不是一个卷积。。。
但这个显然也可以暴力 n l o g n n\mathrm{logn} nlogn 求,但是不行
分析一下性质,设 a , b a,b a,b 互质
F ( a b ) , F ( a ) × F ( b ) F(ab),F(a)\times F(b) F(ab),F(a)×F(b) 会相等吗?
分析一下,计算 F ( a b ) F(ab) F(ab) 时,枚举一个它的因子 d d d ,考虑 d d d 必定可以拆分到 a , b a,b a,b 两个数里,设为 d a , d b d_a,d_b da,db
那么在 F ( a b ) F(ab) F(ab) 中有贡献 d × μ ( d ) d\times \mu(d) d×μ(d),在 F ( a ) F(a) F(a) 中 d a μ ( d a ) d_a\mu (d_a) daμ(da),在 F ( b ) F(b) F(b) 中 d b μ ( d b ) d_b\mu(d_b) dbμ(db),后两者乘起来显然等于前者
这样显然是双射关系,那么贡献完全一样, F ( a b ) = F ( a ) × F ( b ) F(ab)=F(a)\times F(b) F(ab)=F(a)×F(b)
积性函数,我们就可以大胆筛了,筛一个函数一般需要考虑这几种转移:
F ( 1 ) = 1 F(1)=1 F(1)=1
F ( p ) = 1 − p F(p)=1-p F(p)=1−p , p p p 为质数
当 i m o d n ≠ 0 i \ \mathrm{mod\ n}≠0 i mod n=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 m o d n = 0 i \ \mathrm{mod\ n}=0 i mod n=0 时 (一般来说这是唯一的难点),这个需要分析一下:
若枚举 i p ip ip 的因子 d d d 本来就是 i i i 的因子,这一部分总贡献就是 F ( i ) F(i) F(i) ;
反之,枚举的 d d d 不是 i i i 的因子,也就是 d d d 质因子分解后 p p p 的指数为 i i i 中 p p p 的指数加 1 1 1
这不就说明,此时 d d d 中 p p p 的指数 ≥ 2 \geq 2 ≥2 吗?
所以这部分贡献为 0 0 0
综上,当 i m o d n = 0 i \ \mathrm{mod\ n}=0 i mod n=0 时, F ( i × p ) = F ( i ) F(i\times p)=F(i) F(i×p)=F(i)
然后就可以直接筛了,预处理一次 O ( n ) O(n) O(n),多次询问,每次只需要一个数论分块,复杂度 O ( T n ) O(T\sqrt n) O(Tn)
#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
const int N = 1e7+10 , mod = 1e8+9 ;
// 需要分块套分块时,考虑枚举第二个块中的某些东西
// 写成更朴素的求和形式,猜积性函数的性质
int n , m ;
int prime[N] , tot ;
LL S[N] , F[N] ;
bool is[N] ;
void get_Mu( int n )
{
is[0] = is[1] = 1 ; F[1] = S[1] = 1 ;
for(int i = 2 ; i <= n ; i ++ ) {
if( !is[i] ) {
prime[++tot] = i ;
F[i] = 1-i ;
}
for(int j = 1 ; j <= tot && prime[j]*i <= n ; j ++ ) {
is[i*prime[j]] = 1 ;
if( i%prime[j] == 0 ) {
F[i*prime[j]] = F[i] ;
break ;
}
else F[i*prime[j]] = F[i]*F[prime[j]]%mod ;
}
S[i] = ( S[i-1] + F[i]*i ) % mod ;
}
}
inline LL V ( int x )
{
return 1LL*x*(x+1)/2%mod ;
}
int main()
{
int T ;
scanf("%d" , &T ) ;
get_Mu(1e7) ;
while( T -- ) {
scanf("%d%d" , &n , &m ) ;
if( n < m ) swap(n,m) ;
LL ans = 0 ;
int l = 1 , r = 0 ;
while( l <= m ) {
r = min( n/(n/l) , m/(m/l) ) ;
// T:[l,r]
ans = ( ans + (S[r]-S[l-1])*V(n/l)%mod*V(m/l) ) % mod ;
l = r+1 ;
}
printf("%lld\n" , (ans+mod)%mod ) ;
}
return 0 ;
}
这个技巧是非常非常非常常用的:多测时,更改枚举变量 + 构造与 n , m n,m n,m 无关的函数 + 推性质线性筛
例 5
本题需要一个结论:
d ( i × j ) = ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = 1 ] d(i\times j)=\sum\limits_{x|i}\sum\limits_{y|j}[gcd(x,y)=1] d(i×j)=x∣i∑y∣j∑[gcd(x,y)=1]
一句话:两数乘积的约数个数,等于两数约数中的互质对个数
简单证明:
依次考虑每种质因子对答案的贡献
对于左边,小学生都知道的公式是 d ( n ) = ( a 1 + 1 ) ( a 2 + 2 ) . . . d(n)=(a_1+1)(a_2+2)... d(n)=(a1+1)(a2+2)..., a i a_i ai 是将 n n n 质因子分解后 p i p_i pi 的指数
考虑质数 p k p_k pk ,设在 i i i 中有 a i a_i ai 个, j j j 中有 a j a_j aj 个,贡献系数就是 a i + a j + 1 a_i+a_j+1 ai+aj+1
对于右边,虽然是求和式,但每个质因子带来的贡献其实是乘上某个值
还是考虑 p k p_k pk,那么 x x x 与 y y y 中 p k p_k pk 的指数不能同时不为 0 0 0
在这个限制下,这一种质因子的方案显然是 a i + 1 + a j + 1 − 1 = a i + a j + 1 a_i+1+a_j+1-1=a_i+a_j+1 ai+1+aj+1−1=ai+aj+1, − 1 -1 −1 是减掉两个都选 0 0 0 的情况,算了两次
那么左右两边就相等了
接下来的推式子,相信聪明的你一定可以完成的!
练习 1
与例 4 一致,对于最后处理出来的函数直接筛
然鹅由于这个函数就是只与质数相关的,可以 Dirichlet 前缀和
练习 2
本题数据范围不大,可以直接暴力卷积,但我们考虑怎么筛
显然 k = 1 k=1 k=1 时就是欧拉反演 (从某佬的blog中听来的名称)
k ≥ 2 k\geq 2 k≥2,无法直接想明白时打表也是很不错的选择哦!
练习 3
有 a a a 的限制,最终是个分段函数,每次询问不得不重新筛?
继承暴力卷积的思想,考虑每个因子的贡献
由此想到将询问离线,动态删除某个因子对其所有倍数的贡献,然后查区间和
直接树状数组可以做到 O ( Q n l o g n + Q l o g 2 n ) O(Q\sqrt n\mathrm{logn}+Q\mathrm{log^2n}) O(Qnlogn+Qlog2n)
更优秀的做法是用分块平衡一下复杂度,最终可以达到 O ( Q n l o g n ) O(Q\sqrt {n\mathrm{logn}}) O(Qnlogn)
练习 4
好题,一定要再看