Description D e s c r i p t i o n
根据题目描述以及最基础的找规律(真的很基础),答案就是
Solution S o l u t i o n
其实问题的核心就在于求解
我们可以令 f(x) f ( x ) 表示 gcd(i,j) gcd ( i , j ) 为 x x 的个数。
所以我们有
当然这个可以用 Mobius 反演在 O(nn−−√) O ( n n ) 的时间内得到解决,但这里讲一种 O(nlgn) O ( n lg n ) 的做法。
考虑如何计算 f(x) f ( x )
找到
1⋯n
1
⋯
n
及
1⋯m
1
⋯
m
中所有能整除
x
x
的数组成的数对, 答案就是
但是可以想到这些数对中可能有 gcd gcd 为 x x 的倍数的。
举个例子,令 ,满足
x|i
x
|
i
且
x|j
x
|
j
的数对
(i,j)
(
i
,
j
)
有:
共 12 12 对,但是可以枚举得到 f(2)=9 f ( 2 ) = 9 ,满足的数对有:
这是为什么呢?因为 gcd(4,4)=4,gcd(4,8)=4,gcd(6,6)=6 gcd ( 4 , 4 ) = 4 , gcd ( 4 , 8 ) = 4 , gcd ( 6 , 6 ) = 6 。这些 gcd gcd 值都是 x x 的倍数。
而且观察到 。
这样我们就可以这样计算
f(x)
f
(
x
)
后面的部分就是将那些 gcd gcd 为 x x 的倍数的数都删掉。
写个伪代码吧
Tex 代码如下:
\renewcommand{\tab}[1]{\hskip{#1 em}\hskip{#1 em}}
\renewcommand{\func}{\text}
\renewcommand{\for}{\textbf{for} ~~}
\renewcommand{\to}{~~ \textbf{to} ~~}
\renewcommand{\downto}{~~ \textbf{downto} ~~}
\renewcommand{\return}{\textbf{return} ~~}
\renewcommand{\div}{~~ \textbf{div} ~~}
\renewcommand{\type}[1]{\textbf {#1} ~}
\begin{aligned}
& \tab{0} \func{Solve}(\type{int} n, \type{int} m) \\
& \tab{1} ans \leftarrow 0 \\
& \tab{1} t \leftarrow \min \{n, m\} \\
& \tab{1} \for s \leftarrow t \downto 1 \\
& \tab{2} f[s] \leftarrow (n \div s) \times (m \div s) \\
& \tab{2} \for i \leftarrow 2 \to t \div s \\
& \tab{3} f[s] \leftarrow f[s] - f[i \times s] \\
& \tab{2} ans \leftarrow ans + f[s] \times s \\
& \tab{1} \return ans
\end{aligned}
Code C o d e
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define min(x, y) ((x) < (y) ? (x) : (y))
long long f[100005];
int main() {
long long n, m, ans = 0;
scanf("%lld%lld", &n, &m);
long long t = min(n, m);
for (long long s = t; s >= 1; s--) {
f[s] = (n / s) * (m / s);
for (long long i = 2; i * s <= t; i++)
f[s] -= f[i * s];
}
for (long long i = 1; i <= t; i++)
ans += f[i] * i;
printf("%lld\n", 2 * ans - n * m);
return 0;
}