洛谷P2257 YY的GCD
标签
- 莫比乌斯反演
- 线性筛
前言
- 这题貌似和莫反没多大关系,就是用到了一个莫比乌斯函数的性质了,其他就是推公式,优化和式。
- 我的第一道懵逼反演…真的好难好难…而且套路特别多,要多做。可能过段时间我就会觉得特别简单吧!
- 这一条是我过了3天我再次回到这篇博客里编辑的,我想说,真的挺简单的,无非就是那些更换枚举项,反演替换,筛一下什么什么之类的了。觉得自己三天前太笨了,这么显然的东西都搞了半天…
简明题意
- 给定n,m,求使得gcd(i,j)的值是素数,有多少对这样的ij
思路
- 把题目写成公式形式,就是让你求:
∑ 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=1∑nj=1∑m[gcd(i,j)==p](p是所有质数)考虑到 g c d ( i , j ) gcd(i,j) gcd(i,j)的值不可能超过 m i n ( i , j ) min(i,j) min(i,j),因此p的取值应该是 m i n ( n , m ) min(n,m) min(n,m),也就是p是所有在 [ 1 , m i n ( n , m ) ] [1,min(n,m)] [1,min(n,m)]范围内的质数,而不是所有质数,但为了简单表述,下文中 “所有质数” 表示在 [ 1 , m i n ( n , m ) ] [1,min(n,m)] [1,min(n,m)]范围内的所有质数
- 我们枚举任意质数,也就是把括号中的条件写到式子中,就是
∑ p : 所 有 质 数 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = p ] \sum_{p:所有质数}\sum_{i=1}^n \sum_{j=1}^m[gcd(i,j)==p] p:所有质数∑i=1∑nj=1∑m[gcd(i,j)==p] - 根据套路,是要把条件
[
g
c
d
(
i
,
j
)
=
=
p
]
[gcd(i,j)==p]
[gcd(i,j)==p]转换成
[
g
c
d
(
i
,
j
)
=
=
1
]
[gcd(i,j)==1]
[gcd(i,j)==1]的。所以我们套路一下,原式就变成:
∑ p : 所 有 质 数 ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] [ g c d ( i , j ) = = 1 ] \sum_{p:所有质数}\sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]}[gcd(i,j)==1] p:所有质数∑i=1∑[pn]j=1∑[pm][gcd(i,j)==1]
为什么会有这样的套路呢?因为 g c d ( i , j ) = = 1 gcd(i,j)==1 gcd(i,j)==1这样的式子往往更好化简或干一些其他的事情,所以通常见到 g c d ( i , j ) = = p gcd(i,j)==p gcd(i,j)==p就更换枚举上界,使它变成 g c d ( i , j ) = = 1 gcd(i,j)==1 gcd(i,j)==1
- 然后莫比乌斯函数有一个极其重要的结论
∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n} \mu(d)=[n==1] d∣n∑μ(d)=[n==1]
对于一个整数 n > 1 n>1 n>1,他的所有约数的莫比乌斯函数值之和,都是0,而1的所有约数莫比乌斯函数之和是1
- 现在我们把这里的
n
n
n替换成
g
c
d
(
i
,
j
)
gcd(i,j)
gcd(i,j),我们就得到了下面的式子
∑ d ∣ g c d ( i , j ) μ ( d ) = [ g c d ( i , j ) = = 1 ] \sum_{d|gcd(i,j)} \mu(d)=[gcd(i,j)==1] d∣gcd(i,j)∑μ(d)=[gcd(i,j)==1] - 然后我们把它代回原式,可以得:
∑ p : 所 有 质 数 ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{p:所有质数}\sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]} \sum_{d|gcd(i,j)} \mu(d) p:所有质数∑i=1∑[pn]j=1∑[pm]d∣gcd(i,j)∑μ(d) - 然后我们把
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
\sum_{d|gcd(i,j)} \mu(d)
∑d∣gcd(i,j)μ(d)写到条件中去,去枚举每一个d,显然
g
c
d
(
i
,
j
)
<
=
m
i
n
(
n
,
m
)
gcd(i,j)<=min(n,m)
gcd(i,j)<=min(n,m),也就是
d
<
=
m
i
n
(
n
,
m
)
d<=min(n,m)
d<=min(n,m),但我们现在的上下界不是nm,而是
[
n
p
]
,
[
m
p
]
[\frac np],[\frac mp]
[pn],[pm]因此,
d
<
=
m
i
n
(
[
n
p
]
,
[
m
p
]
)
d<=min([\frac np],[\frac mp])
d<=min([pn],[pm]),原式变成:
∑ p : 所 有 质 数 ∑ d = 1 d < = m i n ( [ n p ] , [ m p ] ) ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] ( μ ( d ) ∗ [ d ∣ g c d ( i , j ) ] ) \sum_{p:所有质数}\sum _{d=1}^{d<=min([\frac np],[\frac mp])} \sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]} \left(\mu(d) * [d|gcd(i,j)] \right) p:所有质数∑d=1∑d<=min([pn],[pm])i=1∑[pn]j=1∑[pm](μ(d)∗[d∣gcd(i,j)])
发现了吗,这一步实际上就是把 ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{d|gcd(i,j)} \mu(d) ∑d∣gcd(i,j)μ(d),和式中的判断语句写到条件中
- 接下来,我们可以移项,移动
μ
(
d
)
\mu(d)
μ(d)那一项,然后就成了:
∑ p : 所 有 质 数 ∑ d = 1 d < = m i n ( [ n p ] , [ m p ] ) ( μ ( d ) ∗ ∑ i = 1 [ n p ] ∑ j = 1 [ m p ] [ d ∣ g c d ( i , j ) ] ) \sum_{p:所有质数}\sum _{d=1}^{d<=min([\frac np],[\frac mp])} \left( \mu(d) * \sum_{i=1}^{[\frac np]} \sum_{j=1}^{[\frac mp]}[d|gcd(i,j)] \right) p:所有质数∑d=1∑d<=min([pn],[pm])⎝⎛μ(d)∗i=1∑[pn]j=1∑[pm][d∣gcd(i,j)]⎠⎞
∑ i = 1 [ n p ] ∑ j = 1 [ m p ] μ ( d ) \sum\limits_{i=1}^{[\frac np]} \sum \limits_{j=1}^{[\frac mp]} \mu(d) i=1∑[pn]j=1∑[pm]μ(d)式子中,枚举项是i和j,与d无关,因此与d有关的项可以任意移动。也就是与枚举项无关的式子可以任意移项
- 下一步,我们关注这个式子:
∑
i
=
1
[
n
p
]
∑
j
=
1
[
m
p
]
[
d
∣
g
c
d
(
i
,
j
)
]
\sum\limits_{i=1}^{[\frac np]} \sum \limits_{j=1}^{[\frac mp]} [d |gcd(i,j)]
i=1∑[pn]j=1∑[pm][d∣gcd(i,j)],实际上,
d
∣
g
c
d
(
i
,
j
)
d|gcd(i,j)
d∣gcd(i,j)等价于
[
d
∣
i
且
d
∣
j
]
[d|i 且d|j]
[d∣i且d∣j],具体原因可以自行思考(但这个是经常用的,要熟知),所以这个式子就能变成:
∑ i = 1 [ n p ] ∑ j = 1 [ m p ] [ d ∣ i 且 d ∣ j ] \sum\limits_{i=1}^{[\frac np]} \sum \limits_{j=1}^{[\frac mp]} [d|i 且 d|j] i=1∑[pn]j=1∑[pm][d∣i且d∣j] - 对于式子
∑
i
=
1
n
[
d
∣
i
]
\sum \limits_{i=1}^n[d|i]
i=1∑n[d∣i],表示d是i的约数的个数,显然d,2d,3d…这些是i的倍数,所以一共就有
[
n
d
]
[\frac nd]
[dn]个,所以
∑
i
=
1
[
n
p
]
[
d
∣
i
]
=
[
n
p
d
]
\sum \limits_{i=1}^{[\frac np]}[d|i]=[\frac n{pd}]
i=1∑[pn][d∣i]=[pdn],而现在需要d同时是ij的约数,因此答案就是
[
n
p
d
]
∗
[
m
p
d
]
[\frac n{pd}]*[\frac m{pd}]
[pdn]∗[pdm],这样一来,我们和最后两个
∑
∑
\sum\sum
∑∑说拜拜,原式就成了
∑ p : 所 有 质 数 ∑ d = 1 d < = [ n p ] ( μ ( d ) ∗ [ n p d ] ∗ [ m p d ] ) \sum_{p:所有质数}\sum _{d=1}^{d<=[\frac np]} \left( \mu(d) * [\frac n{pd}]*[\frac m{pd}] \right) p:所有质数∑d=1∑d<=[pn](μ(d)∗[pdn]∗[pdm]) - 这时,复杂度大概是
O
(
质
数
个
数
∗
[
n
p
]
)
O(质数个数*[\frac np])
O(质数个数∗[pn]),最后可以拿到50分。接下来,我们考虑优化。首先我们有下面的式子:
∑ i = 1 n ∑ j = 1 [ n i ] f ( i j ) ∗ g ( i ) ∗ h ( j ) = ∑ k = 1 n f ( k ) ∗ ∑ d ∣ k g ( [ k d ] ) h ( d ) \sum_{i=1}^n\sum_{j=1}^{[\frac ni]}f(ij)*g(i)*h(j)=\sum_{k=1}^nf(k)*\sum_{d|k}g([\frac kd])h(d) i=1∑nj=1∑[in]f(ij)∗g(i)∗h(j)=k=1∑nf(k)∗d∣k∑g([dk])h(d)
令 k = i j k=ij k=ij,然后枚举 k k k。这里的 i i i可以不从1枚举到n,可以是任意数,前提是保证后面的d和i性质一样(比如i是[1,n]范围内的质数,那么d|k且d是质数),且计算时保证关于i的函数和关于d的函数是同一性质(还是刚才的例子,式子左边是g(i),右边以d作为自变量的也应该是g而不应该是h,但如果i是顺序从1枚举到n则没有这个限制)
- 所以我们可以令
k
=
p
d
k=pd
k=pd把原始改为枚举
k
k
k的形式。根据上面所说的,
μ
(
)
\mu()
μ()的自变量就应该是
μ
(
k
d
)
\mu(\frac kd)
μ(dk)而不是
μ
(
d
)
\mu(d)
μ(d)(其他题解都说要改为枚举k,但都没有说为什么,上面就是原因)
∑ k = 1 n [ n k ] [ m k ] ∗ ∑ d ∣ k 且 d ∈ p r i m e μ ( k d ) \sum_{k=1}^n[\frac nk][\frac mk]*\sum_{d|k且d \in prime}\mu(\frac kd) k=1∑n[kn][km]∗d∣k且d∈prime∑μ(dk) - 如果直接计算这个式子,会发现
∑
d
∣
k
且
d
∈
p
r
i
m
e
μ
(
k
d
)
\sum\limits_{d|k且d \in prime}\mu(\frac kd)
d∣k且d∈prime∑μ(dk)的前缀和比较难处理。现在问题就在于处理下面式子的前缀和:
f ( k ) = ∑ d ∣ k 且 d ∈ p r i m e μ ( k d ) f(k)=\sum\limits_{d|k且d \in prime}\mu(\frac kd) f(k)=d∣k且d∈prime∑μ(dk) - 显然又是贡献法枚举了,枚举所有的d(且d是质数),然后d会对所有d的倍数 f ( k d ) f(kd) f(kd)产生贡献,这样一来就把 f ( k ) f(k) f(k)处理完了,然后前缀和一下。预处理复杂度是 O ( 约 数 个 数 ∗ n p ) ( p 是 指 每 一 个 约 数 ) O(约数个数*\frac np)(p是指每一个约数) O(约数个数∗pn)(p是指每一个约数),总复杂度就是 O ( 约 数 个 数 ∗ n p + T n ) O(约数个数*\frac np + T\sqrt n) O(约数个数∗pn+Tn),这样能拿到100分,但是可以进一步优化。
这里有一个小注意事项,就是整除分块里面的l和r必须用int,用long long就会超时…
分鸽线(上面就已经做完了,下面是一点优化,个人认为挺重要的,需要掌握)不加下面的优化大概15s,加了下面的优化就是10s
f ( k ) = ∑ d ∣ k 且 d ∈ p r i m e μ ( k d ) f(k)=\sum\limits_{d|k且d \in prime}\mu(\frac kd) f(k)=d∣k且d∈prime∑μ(dk)
- 上式我们枚举的是什么?是k的所有质因子。于是就把
f
(
k
)
f(k)
f(k)换成唯一分解形式:
f ( k ) = μ ( k p 1 ) + μ ( k p 2 ) + . . . + μ ( k p n ) f(k)=\mu (\frac k{p_1})+\mu (\frac k{p_2})+...+\mu (\frac k{p_n}) f(k)=μ(p1k)+μ(p2k)+...+μ(pnk) - 然后发现 f ( k ) f(k) f(k)显然可以线性筛鸭!然后我们就线性筛,最终复杂度就是 O ( n + T n ) O(n + T\sqrt n) O(n+Tn)
- 最后,我讲一下这里的线性筛:
- 就举个例子吧,假设现在枚举的数是k=30,分解质因数后是
k = 2 1 ∗ 3 1 ∗ 5 1 k=2^1*3^1*5^1 k=21∗31∗51 - 那么
f
(
30
)
=
μ
(
30
2
)
+
μ
(
30
3
)
+
μ
(
30
5
)
f(30)=\mu(\frac {30}2)+\mu(\frac {30}3)+\mu(\frac {30}5)
f(30)=μ(230)+μ(330)+μ(530)
-
i
%
p
r
i
m
e
[
j
]
=
=
0
i\%prime[j]==0
i%prime[j]==0
在这个例子里 p r i m e [ j ] prime[j] prime[j]只能等于2(不知道为什么只有2?因为线性筛只会枚举<= i的最小质因子 的质数…),我们就假设现在枚举的质数是2,然后分解质因数是:
k = 2 2 ∗ 3 1 ∗ 5 1 k=2^2*3^1*5^1 k=22∗31∗51
现在去枚举k的质因子p,发现,除了2,对于任意的p, k p \frac kp pk一定含有平方因子,也就是除了质因子2的,其他质因子的贡献都变成了0,所以 i ∗ p r i m e [ j ] = μ ( i ) i * prime[j]=\mu(i) i∗prime[j]=μ(i) -
i
%
p
r
i
m
e
[
j
]
!
=
0
i\%prime[j]!=0
i%prime[j]!=0
这样的话,相当于多加了一个质因子,所以我们只用加上这个质因子的贡献 μ ( k p r i m e [ j ] ) \mu(\frac k{prime[j]}) μ(prime[j]k)。如果你信了的话,就错啦。并不是只用加上这个质因子的贡献的,因为k变大了,其他的质因子的贡献也变化了。每一个的质因子的 μ ( k d ) \mu(\frac kd) μ(dk)中 k d \frac kd dk都扩大了 p r i m e [ j ] prime[j] prime[j]倍,所以根据莫比乌斯函数的性质,增加了一个原来没有的质因子,值应该取反。于是变成 f [ i ∗ p r i m e [ j ] ] = − f ( i ) f[i * prime[j]]=-f(i) f[i∗prime[j]]=−f(i)…还没完,现在还新增了一个质数prime[j],他的贡献是 μ ( i ∗ p r i m e [ j ] p r i m e [ j ] ) \mu(\frac {i*prime[j]}{prime[j]}) μ(prime[j]i∗prime[j])也就是 μ [ i ] \mu[i] μ[i],所以 f [ i ∗ p r i m e [ j ] ] = − f ( i ) + μ [ i ] f[i * prime[j]]=-f(i)+\mu[i] f[i∗prime[j]]=−f(i)+μ[i] - i是质数
这个就太简单了,显然质数只有一个约数就是自己,所有值就是 μ ( 1 ) = 1 \mu(1)=1 μ(1)=1
- 完结~
-
i
%
p
r
i
m
e
[
j
]
=
=
0
i\%prime[j]==0
i%prime[j]==0
注意事项
- 就是整除分块里面的l和r必须用int,用long long就会超时…
总结
- 遇到枚举项中出现诸如[gcd(i,j)==1]的条件式,可以考虑用 ∑ \sum ∑
- [ d ∣ g c d ( i , j ) ] 等 价 于 [ d ∣ i 且 d ∣ j ] [d|gcd(i,j)]等价于[d|i且d|j] [d∣gcd(i,j)]等价于[d∣i且d∣j]
- ∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum \limits_{d|n} \mu(d)=[n==1] d∣n∑μ(d)=[n==1]这个结论极其重要。可以用这个等式替换条件式
- 就是整除分块里面的l和r必须用int,用long long就会超时…
- 下面这一个我认为是这篇题解里最有营养的
∑ i = 1 n ∑ j = 1 [ n i ] f ( i j ) ∗ g ( i ) ∗ h ( j ) = ∑ k = 1 n f ( k ) ∗ ∑ d ∣ k g ( [ k d ] ) h ( d ) \sum_{i=1}^n\sum_{j=1}^{[\frac ni]}f(ij)*g(i)*h(j)=\sum_{k=1}^nf(k)*\sum_{d|k}g([\frac kd])h(d) i=1∑nj=1∑[in]f(ij)∗g(i)∗h(j)=k=1∑nf(k)∗d∣k∑g([dk])h(d)
令 k = i j k=ij k=ij,然后枚举 k k k。这里的 i i i可以不从1枚举到n,可以是任意数,前提是保证后面的d和i性质一样(比如i是[1,n]范围内的质数,那么d|k且d是质数),且计算时保证关于i的函数和关于d的函数是同一性质(还是刚才的例子,式子左边是g(i),右边以d作为自变量的也应该是g而不应该是h,但如果i是顺序从1枚举到n则没有这个限制)
- 见到 ∑ d ∣ n 且 d ∈ p r i m e \sum\limits_{d|n且d \in prime} d∣n且d∈prime∑,就是在枚举n的所有质因子,看到这样的式子脑子里一定要立马蹦出线性筛鸭!!!
AC代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e7 + 10;
bool no_prime[maxn];
int prime[maxn], mu[maxn], f[maxn], pre[maxn];
int shai(int n)
{
int cnt = 0;
mu[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!no_prime[i])
prime[++cnt] = i, mu[i] = -1, f[i] = 1;
for (int j = 1; j <= cnt && prime[j] * i <= n; j++)
{
no_prime[i * prime[j]] = 1;
mu[i * prime[j]] = i % prime[j] == 0 ? 0 : -mu[i];
f[i * prime[j]] = i % prime[j] == 0 ? mu[i] : mu[i] - f[i];
if (i % prime[j] == 0) break;
}
}
for (int i = 1; i <= n; i++)
pre[i] = pre[i - 1] + f[i];
return cnt;
}
long long cal(int n, int m)
{
int l = 1, r; long long ans = 0;
while (l <= n)
{
r = min(n / (n / l), m / (m / l));
ans += (1ll * n / l) * (m / l) * (long long)(pre[r] - pre[l - 1]);
l = r + 1;
}
return ans;
}
int n, m;
void solve()
{
shai(maxn - 10);
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &m);
if (n > m) swap(n, m);
printf("%lld\n", cal(n, m));
}
}
int main()
{
solve();
return 0;
}