最大公约数和最小公倍数
1. 基础概念
1.1 质数的定义
质数(Prime Number)是一个重要的数学概念,它的定义如下:
如果一个大于1的自然数只有1和它本身两个因数,那么这个数就被称为质数(或素数)。
换句话说,对于一个质数 p p p,它满足以下条件:
- p p p 是一个大于1的自然数;
- 如果 p p p 能被一个小于 p p p 且大于1的自然数整除,那么 p p p 就不是质数。
根据定义,我们可以得到一些质数的例子:
- 2是最小的质数,因为它只有1和2两个因数;
- 3、5、7、11、13、17、19等都是质数;
- 4、6、8、9、10、12等都不是质数,因为它们除了1和本身之外,还有其他因数。
值得注意的是,1不是质数,因为质数的定义要求一个数必须大于1。同时,负整数和0也不是质数。
在数论中,质数有许多重要的性质和应用,例如:
- 任何一个大于1的自然数都可以唯一地表示为质数的乘积(质因数分解);
- 两个数的最大公约数可以通过它们的质因数分解求得;
- 质数的分布有一定的规律,但至今没有一个简单的公式可以生成所有的质数。
理解质数的定义是学习数论的基础,在探索最大质因数和最小质因数时,我们会频繁用到质数的概念。
1.2 合数的定义
1.2 合数的定义
合数(Composite Number)是与质数相对的一个概念,它的定义如下:
如果一个大于1的自然数除了1和它本身之外,还有其他因数,那么这个数就被称为合数。
换句话说,对于一个合数 c c c,它满足以下条件:
- c c c 是一个大于1的自然数;
- 存在一个小于 c c c 且大于1的自然数 d d d,使得 d d d 能整除 c c c。
根据定义,我们可以得到一些合数的例子:
- 4是最小的合数,因为它有1、2和4三个因数;
- 6、8、9、10、12、14、15、16等都是合数;
- 2、3、5、7、11、13、17、19等都不是合数,因为它们只有1和本身两个因数。
值得注意的是,1既不是质数也不是合数,因为合数的定义要求一个数必须大于1。同时,负整数和0也不是合数。
质数和合数是互补的概念,它们构成了所有大于1的自然数的两个不相交的子集。换句话说:
- 任何一个大于1的自然数要么是质数,要么是合数;
- 没有一个数既是质数又是合数;
- 1既不是质数也不是合数。
在学习最大质因数和最小质因数时,我们主要关注的是合数,因为质数只有1和本身两个因数,而合数的因数结构更加复杂,需要进行质因数分解等操作。
1.3 因数的定义
因数(Factor)是一个基本的数学概念,它描述了两个数之间的整除关系。因数的定义如下:
如果一个整数 a a a 能被另一个整数 b b b 整除,那么我们称 a a a 是 b b b 的因数,也称 b b b 是 a a a 的倍数。
换句话说,对于两个整数 a a a 和 b b b,如果存在一个整数 k k k,使得 a × k = b a \times k = b a×k=b,那么我们说:
- a a a 是 b b b 的因数;
- b b b 是 a a a 的倍数;
- b b b 能被 a a a 整除;
- a a a 能整除 b b b。
根据定义,我们可以得到一些因数的例子:
- 1和6都是6的因数,因为 1 × 6 = 6 1 \times 6 = 6 1×6=6 和 2 × 3 = 6 2 \times 3 = 6 2×3=6;
- 1、2、3、4、6、12都是12的因数;
- 对于任意整数 a a a,1和 a a a 都是 a a a 的因数,因为 1 × a = a 1 \times a = a 1×a=a。
值得注意的是,因数可以是正整数、负整数或者0:
- 如果 a a a 是 b b b 的因数,那么 − a -a −a 也是 b b b 的因数,因为 ( − a ) × ( − k ) = b (-a) \times (-k) = b (−a)×(−k)=b;
- 0是0的因数,因为 0 × k = 0 0 \times k = 0 0×k=0 对于任意整数 k k k 都成立;
- 0不是其他任何非零整数的因数,因为 0 × k = 0 ≠ a 0 \times k = 0 \neq a 0×k=0=a 对于任意非零整数 a a a 和任意整数 k k k 都成立。
在学习最大质因数和最小质因数时,我们主要关注正整数的因数。对于一个合数,我们通常需要找到它的所有因数,或者找到它的质因数(即那些是质数的因数)。因数的概念是理解质因数分解的基础。
1.3.1 天秀的苹果
天秀有 n n n 个苹果,她想把这些苹果分成两部分,分别给她的朋友小红和小明。天秀希望小红和小明得到的苹果数量之差的绝对值尽可能小。
请你帮助天秀计算,她应该如何分配苹果,才能使得小红和小明得到的苹果数量之差的绝对值最小。
输入格式
输入一个正整数 n n n,表示天秀有 n n n 个苹果。
输出格式
输出一个整数,表示小红和小明得到的苹果数量之差的最小绝对值。
样例输入1
10
样例输出1
0
样例解释1
天秀可以将10个苹果平均分成两部分,小红和小明各得到5个苹果,它们的差的绝对值为0。
样例输入2
7
样例输出2
1
样例解释2
天秀可以将7个苹果分成3个和4个两部分,小红和小明分别得到3个和4个苹果,它们的差的绝对值为1。这是差的绝对值最小的分配方案。
数据范围
对于所有评测用例, 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1≤n≤109。
题目解析
这道题考查的是数论中的奇偶性知识。我们可以发现,当苹果数量为偶数时,可以平均分配,差的绝对值为0;当苹果数量为奇数时,差的绝对值至少为1。因此,答案就是苹果数量除以2的余数。
解题思路
-
观察题目,我们需要将苹果分成两部分,且两部分的差的绝对值尽可能小。
-
考虑苹果数量的奇偶性:
- 如果苹果数量为偶数,我们可以将苹果平均分成两部分,每部分都有 n 2 \frac{n}{2} 2n 个苹果,此时差的绝对值为 0,这是最优解。
- 如果苹果数量为奇数,我们无法平均分配。最优的分配方案是将苹果分成 n − 1 2 \frac{n-1}{2} 2n−1 和 n + 1 2 \frac{n+1}{2} 2n+1 两部分,此时差的绝对值为 1,这是最小的差的绝对值。
-
综上所述,问题可以转化为判断苹果数量的奇偶性:
- 如果苹果数量为偶数,答案为 0;
- 如果苹果数量为奇数,答案为 1。
-
在代码实现中,我们可以使用取模运算符
%
来判断一个数是奇数还是偶数:- 如果一个数除以 2 的余数为 0,那么它是偶数;
- 如果一个数除以 2 的余数为 1,那么它是奇数。
-
因此,我们可以将苹果数量 n n n 除以 2,并取余数作为答案。这就是这道题的解题思路。
这个解题思路的核心是利用了奇偶性的性质,将问题简化为判断苹果数量的奇偶性。通过取模运算,我们可以很容易地判断一个数是奇数还是偶数,从而得到答案。
C++解答
#include <iostream>using namespace std;int main() {int n;cin >> n;cout << n % 2 << endl;return 0;
}
这个C++解答的时间复杂度为 O ( 1 ) O(1) O(1),空间复杂度为 O ( 1 ) O(1) O(1)。我们只需要读入苹果数量 n n n,然后用取模运算符 %
计算 n n n 除以 2 的余数即可。当 n n n 为偶数时,余数为 0;当 n n n 为奇数时,余数为 1。这个余数就是小红和小明得到的苹果数量之差的最小绝对值。
1.3.2 天秀的因数个数
天秀有一个正整数 n n n,她想知道 n n n 有多少个正因数。
正整数 a a a 是正整数 b b b 的因数,当且仅当存在整数 k k k,使得 b = a × k b = a \times k b=a×k。
请你帮助天秀计算 n n n 的正因数的个数。
输入格式
输入一个正整数 n n n。
输出格式
输出一个整数,表示 n n n 的正因数的个数。
样例输入1
12
样例输出1
6
样例解释1
12 的正因数有 1、2、3、4、6、12,共有 6 个。
样例输入2
36
样例输出2
9
样例解释2
36 的正因数有 1、2、3、4、6、9、12、18、36,共有 9 个。
数据范围
对于所有评测用例, 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1≤n≤106。
解题思路
-
正整数 n n n 的正因数包括 1 和 n n n 本身,以及在区间 [ 2 , n ] [2, \sqrt{n}] [2,n] 中所有能整除 n n n 的数及其对应的商。
-
我们可以遍历区间 [ 1 , n ] [1, \sqrt{n}] [1,n] 中的所有整数 i i i:
- 如果 i i i 能整除 n n n,那么 i i i 是 n n n 的一个因数;
- 如果 i ≠ n i \neq \sqrt{n} i=n,那么 n i \frac{n}{i} in 也是 n n n 的一个因数。
-
我们用一个变量 c n t cnt cnt 来记录因数的个数,初始时 c n t = 0 cnt = 0 cnt=0:
- 对于每个能整除 n n n 的 i i i,将 c n t cnt cnt 增加 1;
- 如果 i ≠ n i \neq \sqrt{n} i=n,再将 c n t cnt cnt 增加 1。
-
最终,变量 c n t cnt cnt 的值就是 n n n 的正因数的个数。
C++解答
#include <iostream>
#include <cmath>using namespace std;int main() {int n;cin >> n;int cnt = 0;for (int i = 1; i <= sqrt(n); i++) {if (n % i == 0) {cnt++;if (i != n / i) {cnt++;}}}cout << cnt << endl;return 0;
}
这个C++解答的时间复杂度为 O ( n ) O(\sqrt{n}) O(n),空间复杂度为 O ( 1 ) O(1) O(1)。我们通过遍历区间 [ 1 , n ] [1, \sqrt{n}] [1,n] 中的所有整数,判断每个整数是否为 n n n 的因数,并统计因数的个数。由于一个数的因数总是成对出现(除了完全平方数),所以我们只需要遍历到 n \sqrt{n} n 即可。
1.3.3 天秀的完美数
天秀在学习完数的概念。一个正整数如果等于除它本身外的所有因数之和,就称为完美数。例如,6 就是一个完美数,因为 6 = 1 + 2 + 3。
现在,天秀想知道在给定的区间 [ l , r ] [l, r] [l,r] 内,有多少个完美数。
请你帮助天秀计算完美数的个数。
输入格式
输入两个正整数 l l l 和 r r r,表示查询的区间。
输出格式
输出一个整数,表示区间 [ l , r ] [l, r] [l,r] 内完美数的个数。
样例输入1
1 10
样例输出1
1
样例解释1
在区间 [ 1 , 10 ] [1, 10] [1,10] 内,只有 6 是完美数。
样例输入2
1 100
样例输出2
2
样例解释2
在区间 [ 1 , 100 ] [1, 100] [1,100] 内,有两个完美数:6 和 28。
数据范围
对于所有评测用例, 1 ≤ l ≤ r ≤ 1 0 6 1 \leq l \leq r \leq 10^6 1≤l≤r≤106。
解题思路
-
根据完美数的定义,我们可以判断一个数是否为完美数:
- 遍历从 1 到 n \sqrt{n} n 的所有整数 i i i;
- 如果 i i i 是 n n n 的因数,就将 i i i 和 n i \frac{n}{i} in 加到因数和中(注意要避免重复加);
- 如果因数和等于 n n n,那么 n n n 就是完美数。
-
我们可以遍历区间 [ l , r ] [l, r] [l,r] 中的每个数,判断它是否为完美数,并统计完美数的个数。
-
为了优化时间复杂度,我们可以预处理出所有不超过 1 0 6 10^6 106 的完美数,然后在查询时直接判断区间内有多少个预处理出的完美数。
C++解答
#include <iostream>
#include <vector>
#include <cmath>using namespace std;const int MAXN = 1e6;vector<int> perfectNumbers;void init() {for (int n = 2; n <= MAXN; n++) {int sum = 1;for (int i = 2; i <= sqrt(n); i++) {if (n % i == 0) {sum += i;if (i != n / i) {sum += n / i;}}}if (sum == n) {perfectNumbers.push_back(n);}}
}int main() {init();int l, r;cin >> l >> r;int cnt = 0;for (int num : perfectNumbers) {if (l <= num && num <= r) {cnt++;}}cout << cnt << endl;return 0;
}
这个C++解答的时间复杂度为 O ( M A X N × M A X N + q ) O(\sqrt{MAXN} \times MAXN + q) O(MAXN×MAXN+q),空间复杂度为 O ( M A X N ) O(MAXN) O(MAXN),其中 q q q 是完美数的个数。我们首先预处理出所有不超过 1 0 6 10^6 106 的完美数,然后在查询时遍历预处理出的完美数,判断它们是否在查询区间内,并统计个数。预处理的时间复杂度为 O ( M A X N × M A X N ) O(\sqrt{MAXN} \times MAXN) O(MAXN×MAXN),查询的时间复杂度为 O ( q ) O(q) O(q)。由于完美数非常稀少,所以实际运行时间很快。
1.3.4 天秀的因数平方和
天秀得到了一个正整数 n n n,她想知道 n n n 的所有因数(包括 1 1 1 和 n n n 本身)的平方和是多少。
例如,如果 n = 10 n = 10 n=10,那么它的因数有 1 , 2 , 5 , 10 1, 2, 5, 10 1,2,5,10,因数的平方和为 1 2 + 2 2 + 5 2 + 1 0 2 = 130 1^2 + 2^2 + 5^2 + 10^2 = 130 12+22+52+102=130。
请你帮助天秀计算因数的平方和。
输入格式
输入一个正整数 n n n。
输出格式
输出一个整数,表示 n n n 的所有因数的平方和。
样例输入1
10
样例输出1
130
样例解释1
10 10 10 的因数有 1 , 2 , 5 , 10 1, 2, 5, 10 1,2,5,10,因数的平方和为 1 2 + 2 2 + 5 2 + 1 0 2 = 130 1^2 + 2^2 + 5^2 + 10^2 = 130 12+22+52+102=130。
样例输入2
25
样例输出2
651
样例解释2
25 25 25 的因数有 1 , 5 , 25 1, 5, 25 1,5,25,因数的平方和为 1 2 + 5 2 + 2 5 2 = 651 1^2 + 5^2 + 25^2 = 651 12+52+252=651。
数据范围
对于 30 % 30\% 30% 的评测用例, 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1≤n≤106;
对于 60 % 60\% 60% 的评测用例, 1 ≤ n ≤ 1 0 12 1 \leq n \leq 10^{12} 1≤n≤1012;
对于所有评测用例, 1 ≤ n ≤ 1 0 18 1 \leq n \leq 10^{18} 1≤n≤1018。
解题思路
-
直接计算因数平方和的时间复杂度为 O ( n ) O(\sqrt{n}) O(n),对于 n ≤ 1 0 18 n \leq 10^{18} n≤1018 的情况会超时。
-
我们可以利用因数的性质,通过分解质因数来计算因数平方和。设 n = p 1 a 1 × p 2 a 2 × . . . × p k a k n = p_1^{a_1} \times p_2^{a_2} \times ... \times p_k^{a_k} n=p1a1×p2a2×...×pkak,其中 p i p_i pi 为质因数, a i a_i ai 为对应的指数,那么 n n n 的因数平方和可以表示为:
∑ d ∣ n d 2 = ∏ i = 1 k ( 1 + p i 2 + p i 4 + . . . + p i 2 a i ) \displaystyle\sum_{d | n} d^2 = \prod_{i=1}^{k} (1 + p_i^2 + p_i^4 + ... + p_i^{2a_i}) d∣n∑d2=i=1∏k(1+pi2+pi4+...+pi2ai)
其中 d ∣ n d | n d∣n 表示 d d d 为 n n n 的因数。
-
对于每个质因数 p i p_i pi,我们可以计算出 1 + p i 2 + p i 4 + . . . + p i 2 a i 1 + p_i^2 + p_i^4 + ... + p_i^{2a_i} 1+pi2+pi4+...+pi2ai 的值,然后将所有质因数对应的值相乘,就得到了因数平方和。
-
为了快速分解质因数,我们可以使用预处理素数表的方法,然后在分解质因数时直接判断每个数是否为质数,并计算其指数。
C++解答
#include <iostream>
#include <vector>using namespace std;typedef long long ll;const int MAXN = 1e6;vector<int> primes;
bool isPrime[MAXN + 1];void sieve() {fill(isPrime, isPrime + MAXN + 1, true);isPrime[0] = isPrime[1] = false;for (int i = 2; i * i <= MAXN; i++) {if (isPrime[i]) {for (int j = i * i; j <= MAXN; j += i) {isPrime[j] = false;}}}for (int i = 2; i <= MAXN; i++) {if (isPrime[i]) {primes.push_back(i);}}
}ll power(ll x, ll exp) {ll result = 1;while (exp > 0) {if (exp & 1) {result *= x;}x *= x;exp >>= 1;}return result;
}ll solve(ll n) {ll result = 1;for (int p : primes) {if (p * p > n) {break;}if (n % p == 0) {ll exp = 0;while (n % p == 0) {n /= p;exp++;}result *= (power(p, 2 * exp + 2) - 1) / (p * p - 1);}}if (n > 1) {result *= (n * n + 1);}return result;
}int main() {sieve();ll n;cin >> n;ll ans = solve(n);cout << ans << endl;return 0;
}
这个C++解答的时间复杂度为 O ( M A X N + log n ) O(\sqrt{MAXN} + \log n) O(MAXN+logn),空间复杂度为 O ( M A X N ) O(MAXN) O(MAXN)。我们首先预处理出所有不超过 1 0 6 10^6 106 的素数,然后在分解质因数时直接判断每个数是否为素数,并计算其指数。对于每个质因数,我们可以在 O ( 1 ) O(1) O(1) 的时间内计算出它对应的 1 + p i 2 + p i 4 + . . . + p i 2 a i 1 + p_i^2 + p_i^4 + ... + p_i^{2a_i} 1+pi2+pi4+...+pi2ai 的值,然后将所有质因数对应的值相乘,就得到了因数平方和。由于质因数的个数不超过 log n \log n logn,所以分解质因数的时间复杂度为 O ( log n ) O(\log n) O(logn)。
需要注意的是,由于答案可能很大,所以我们需要使用 long long
类型来存储结果。同时,在计算指数时,我们可以使用快速幂算法来优化时间复杂度。
1.3.5 天秀的最大公因数
天秀有两个正整数 a a a 和 b b b,她想知道它们的最大公因数是多少。
最大公因数(Greatest Common Divisor, GCD)是指两个或多个整数共有约数中最大的一个。例如,12 和 18 的最大公因数是 6。
请你帮助天秀计算最大公因数。
输入格式
输入两个正整数 a a a 和 b b b。
输出格式
输出一个整数,表示 a a a 和 b b b 的最大公因数。
样例输入1
12 18
样例输出1
6
样例输入2
17 23
样例输出2
1
样例输入3
1024 2048
样例输出3
1024
样例输入4
123456 654321
样例输出4
3
样例输入5
1000000000 1
样例输出5
1
数据范围
- 对于 30 % 30\% 30% 的数据, 1 ≤ a , b ≤ 1000 1 \leq a, b \leq 1000 1≤a,b≤1000;
- 对于 60 % 60\% 60% 的数据, 1 ≤ a , b ≤ 1 0 6 1 \leq a, b \leq 10^6 1≤a,b≤106;
- 对于 100 % 100\% 100% 的数据, 1 ≤ a , b ≤ 1 0 9 1 \leq a, b \leq 10^9 1≤a,b≤109。
解题思路
-
计算最大公因数的经典算法是欧几里得算法(Euclidean algorithm)。欧几里得算法基于以下原理:
- 如果 b = 0 b = 0 b=0,那么 gcd ( a , b ) = a \gcd(a, b) = a gcd(a,b)=a;
- 否则, gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a, b) = \gcd(b, a \bmod b) gcd(a,b)=gcd(b,amodb)。
-
我们可以递归地应用欧几里得算法,直到 b = 0 b = 0 b=0 为止。此时, a a a 就是最大公因数。
-
在实现时,我们可以将欧几里得算法改写成迭代形式,避免递归调用的开销。
C++解答
#include <iostream>using namespace std;int gcd(int a, int b) {while (b != 0) {int temp = b;b = a % b;a = temp;}return a;
}int main() {int a, b;cin >> a >> b;int ans = gcd(a, b);cout << ans << endl;return 0;
}
这个C++解答的时间复杂度为 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b))),空间复杂度为 O ( 1 ) O(1) O(1)。欧几里得算法的迭代次数不超过 log ( min ( a , b ) ) \log(\min(a, b)) log(min(a,b)),因为每次迭代时,b
的值都会减小到 a % b
,而 a % b
的值不会超过 a / 2
。
需要注意的是,在计算 a % b
时,如果 a < b
,结果就是 a
本身。所以在迭代过程中,a
和 b
的大小关系可能会发生变化,但这并不影响算法的正确性。
另外,样例5中给出了一个极端情况,当 a
和 b
互质时,它们的最大公因数为1。这种情况下,欧几里得算法会在第一次迭代后就终止。
欧几里得算法是计算最大公因数的高效方法,它避免了直接枚举所有公因数的做法,时间复杂度较低。同时,它也是许多其他算法的基础,例如扩展欧几里得算法、求解线性丢番图方程等。
1.4 最大公约数和最小公倍数的定义
最大公约数(GCD)
两个或多个整数的公约数,是指同时整除这几个整数的整数。而最大公约数就是这些公约数里面最大的一个。
例如,12和18的公约数有1, 2, 3, 6,其中最大的是6,所以12和18的最大公约数是6。
如果几个整数的最大公约数是1,我们称这几个整数互质。
最小公倍数(LCM)
两个或多个整数的公倍数,是指同时被这几个整数整除的整数。而最小公倍数就是这些公倍数里面最小的一个。
例如,12和18的公倍数有36, 72, 108, …,其中最小的是36,所以12和18的最小公倍数是36。
最大公约数和最小公倍数的关系
对于两个整数a和b,它们的最大公约数记为gcd(a, b),最小公倍数记为lcm(a, b),那么它们满足以下关系:
a × b = g c d ( a , b ) × l c m ( a , b ) a \times b = gcd(a, b) \times lcm(a, b) a×b=gcd(a,b)×lcm(a,b)
换句话说,两个数的乘积等于它们的最大公约数与最小公倍数的乘积。这个性质在解题时非常有用。
求最大公约数的方法
求最大公约数有多种方法,最常用的是欧几里得算法,也叫辗转相除法。它的原理是:
g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a, b) = gcd(b, a \bmod b) gcd(a,b)=gcd(b,amodb)
不断地对a和b进行取模运算,直到b为0为止,此时a就是最大公约数。
例如,求12和18的最大公约数的过程如下:
g c d ( 12 , 18 ) = g c d ( 18 , 12 ) = g c d ( 12 , 6 ) = g c d ( 6 , 0 ) = 6 gcd(12, 18) = gcd(18, 12) = gcd(12, 6) = gcd(6, 0) = 6 gcd(12,18)=gcd(18,12)=gcd(12,6)=gcd(6,0)=6
求最小公倍数的方法
有了最大公约数,求最小公倍数就简单了。根据上面提到的性质,我们有:
l c m ( a , b ) = a × b g c d ( a , b ) lcm(a, b) = \frac{a \times b}{gcd(a, b)} lcm(a,b)=gcd(a,b)a×b
所以,我们先求出最大公约数,然后用两数之积除以最大公约数,就得到了最小公倍数。
最大公约数和最小公倍数在数论中有许多应用,例如约分、求通分母、一次同余方程的求解等。熟练掌握它们的定义和求法,对解题很有帮助。
1.4.2 天秀的最小公倍数问题
天秀学习了最小公倍数的概念后,想要运用这个知识解决一些问题。
给定两个正整数 a a a 和 b b b,请你帮助天秀计算它们的最小公倍数。
输入格式
输入两个正整数 a a a 和 b b b。
输出格式
输出一个整数,表示 a a a 和 b b b 的最小公倍数。
样例输入1
3 5
样例输出1
15
样例输入2
12 18
样例输出2
36
样例输入3
1024 2048
样例输出3
2048
样例输入4
123456789 987654321
样例输出4
121932631112635269
样例输入5
1000000000 1
样例输出5
1000000000
数据范围
- 对于 30 % 30\% 30% 的数据, 1 ≤ a , b ≤ 1000 1 \leq a, b \leq 1000 1≤a,b≤1000;
- 对于 60 % 60\% 60% 的数据, 1 ≤ a , b ≤ 1 0 6 1 \leq a, b \leq 10^6 1≤a,b≤106;
- 对于 100 % 100\% 100% 的数据, 1 ≤ a , b ≤ 1 0 9 1 \leq a, b \leq 10^9 1≤a,b≤109。
题目解析
这道题考查的是最小公倍数的计算。我们知道,两个数的最小公倍数可以通过它们的最大公约数来计算。设 g c d ( a , b ) gcd(a, b) gcd(a,b) 为 a a a 和 b b b 的最大公约数,那么它们的最小公倍数 l c m ( a , b ) lcm(a, b) lcm(a,b) 可以表示为:
l c m ( a , b ) = a × b g c d ( a , b ) lcm(a, b) = \frac{a \times b}{gcd(a, b)} lcm(a,b)=gcd(a,b)a×b
因此,我们可以先用欧几里得算法计算出最大公约数,然后用上述公式计算最小公倍数。
需要注意的是,最小公倍数的值可能会很大,有可能超出 int
的范围。因此,我们需要使用 long long
类型来存储结果。
样例中包含了一些常见的情况:
- 样例1是普通情况, l c m ( 3 , 5 ) = 15 lcm(3, 5) = 15 lcm(3,5)=15;
- 样例2中一个数是另一个数的倍数, l c m ( 12 , 18 ) = 18 lcm(12, 18) = 18 lcm(12,18)=18;
- 样例3中两个数是倍数关系, l c m ( 1024 , 2048 ) = 2048 lcm(1024, 2048) = 2048 lcm(1024,2048)=2048;
- 样例4是两个大数的情况,结果已经超出了
int
的范围; - 样例5中考察 b = 1 b = 1 b=1 的特殊情况, l c m ( a , 1 ) = a lcm(a, 1) = a lcm(a,1)=a。
通过这个问题,天秀可以深入理解最小公倍数的概念,并掌握求解最小公倍数的方法。同时,这个问题也提醒我们在计算过程中要注意数据范围,必要时使用更大的数据类型。
好的,我先给出题目的解题思路,然后再提供C++解答。
解题思路
-
最小公倍数的定义:两个或多个整数的公倍数中最小的一个。
-
最小公倍数与最大公约数的关系:设 a a a 和 b b b 的最大公约数为 g c d ( a , b ) gcd(a, b) gcd(a,b),最小公倍数为 l c m ( a , b ) lcm(a, b) lcm(a,b),则有:
l c m ( a , b ) = a × b g c d ( a , b ) lcm(a, b) = \frac{a \times b}{gcd(a, b)} lcm(a,b)=gcd(a,b)a×b
这个关系表明,我们可以通过计算最大公约数来求得最小公倍数。
-
计算最大公约数可以使用欧几里得算法(辗转相除法):
g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a, b) = gcd(b, a \bmod b) gcd(a,b)=gcd(b,amodb)
不断对 a a a 和 b b b 进行取模运算,直到 b b b 为 0 0 0 为止,此时 a a a 就是最大公约数。
-
有了最大公约数,根据步骤2中的关系式,我们可以计算出最小公倍数。
-
注意,最小公倍数可能会很大,有可能超出
int
的范围。因此,我们需要使用long long
类型来存储结果。
C++解答
#include <iostream>
using namespace std;typedef long long ll;ll gcd(ll a, ll b) {return b == 0 ? a : gcd(b, a % b);
}ll lcm(ll a, ll b) {return a / gcd(a, b) * b;
}int main() {ll a, b;cin >> a >> b;cout << lcm(a, b) << endl;return 0;
}
这个解答中:
-
我们定义了一个
long long
类型的别名ll
,以处理可能的大数据。 -
gcd
函数使用了欧几里得算法的递归实现来计算最大公约数。 -
lcm
函数根据最小公倍数和最大公约数的关系,通过最大公约数来计算最小公倍数。注意这里我们先进行除法运算,然后再进行乘法运算,以避免中间结果溢出。 -
在主函数中,我们读入
a
和b
,调用lcm
函数计算最小公倍数并输出结果。
这个解答的时间复杂度为 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b))),空间复杂度为 O ( 1 ) O(1) O(1)。
希望这个详细的解题思路和C++解答对你有所帮助!如果你有任何其他问题,欢迎随时问我。
好的,我给你出一道结合最大公约数和最小公倍数知识点的应用题。
1.4.3 天秀的蛋糕分配问题
天秀有 n n n 个朋友,她想分给每个朋友一块蛋糕。她有两种蛋糕,一种是 a a a 克重的小蛋糕,另一种是 b b b 克重的大蛋糕。
天秀希望每个朋友分到的蛋糕重量都是一样的,且尽可能大。同时,为了不浪费,分给每个朋友的蛋糕重量必须是 a a a 和 b b b 的整数倍。
请你帮天秀计算,每个朋友最多能分到多少克的蛋糕。
输入格式
第一行输入三个正整数 n n n, a a a, b b b,分别表示朋友的数量,小蛋糕的重量和大蛋糕的重量。
输出格式
输出一个整数,表示每个朋友最多能分到的蛋糕重量。
样例输入1
3 2 6
样例输出1
6
样例输入2
5 3 7
样例输出2
21
样例输入3
10 4 6
样例输出3
12
数据范围
- 对于 30 % 30\% 30% 的数据, 1 ≤ n , a , b ≤ 1000 1 \leq n, a, b \leq 1000 1≤n,a,b≤1000;
- 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1≤n≤109, 1 ≤ a , b ≤ 1 0 18 1 \leq a, b \leq 10^{18} 1≤a,b≤1018。
题目解析
这道题看似是一个简单的分配问题,但实际上考察的是最小公倍数的应用。
-
每个朋友分到的蛋糕重量必须是 a a a 和 b b b 的整数倍,因此每个朋友分到的蛋糕重量实际上是 a a a 和 b b b 的公倍数。
-
为了让每个朋友分到尽可能大的蛋糕,我们应该选择 a a a 和 b b b 的最小公倍数作为每个朋友分到的蛋糕重量。
-
最小公倍数可以通过最大公约数来计算:
l c m ( a , b ) = a × b g c d ( a , b ) lcm(a, b) = \frac{a \times b}{gcd(a, b)} lcm(a,b)=gcd(a,b)a×b
-
因此,我们首先用欧几里得算法计算出 a a a 和 b b b 的最大公约数,然后用上述公式计算出最小公倍数即可。
-
需要注意的是,最小公倍数可能会很大,有可能超出
int
的范围。因此,我们需要使用long long
类型来存储结果。
好的,下面是这个问题的C++解答:
#include <iostream>
using namespace std;typedef long long ll;ll gcd(ll a, ll b) {return b == 0 ? a : gcd(b, a % b);
}ll lcm(ll a, ll b) {return a / gcd(a, b) * b;
}int main() {ll n, a, b;cin >> n >> a >> b;cout << lcm(a, b) << endl;return 0;
}
解释:
-
我们定义了
long long
类型的别名ll
,以处理可能的大数据。 -
gcd
函数使用欧几里得算法的递归实现来计算最大公约数。 -
lcm
函数根据最小公倍数和最大公约数的关系,通过最大公约数来计算最小公倍数。注意这里我们先进行除法运算,然后再进行乘法运算,以避免中间结果溢出。 -
在主函数中,我们读入
n
,a
和b
。但实际上,我们并不需要使用n
的值,因为每个朋友分到的蛋糕重量只取决于a
和b
的最小公倍数。 -
我们调用
lcm
函数计算a
和b
的最小公倍数,并输出结果。
这个解答的时间复杂度为 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b))),空间复杂度为 O ( 1 ) O(1) O(1)。
需要注意的是,虽然题目中给出了 n
的范围,但实际上我们并不需要使用 n
的值。这是因为无论有多少个朋友,每个朋友分到的蛋糕重量都是一样的,都是 a
和 b
的最小公倍数。
这个问题很好地展示了最小公倍数在实际应用中的作用。通过计算最小公倍数,我们可以解决许多看似复杂的问题。
好的,下面我再给你出一道应用最大公约数和最小公倍数知识点的题目,并提供5个经典的样例数据。
1.4.4 天秀的时钟问题
天秀有两个时钟,一个时钟每 a a a 分钟走一格,另一个时钟每 b b b 分钟走一格。现在两个时钟同时从12点开始走,请问经过多少分钟后,两个时钟会再次同时指向12点?
输入格式
输入两个正整数 a a a 和 b b b,分别表示两个时钟走一格的分钟数。
输出格式
输出一个整数,表示两个时钟再次同时指向12点所需的分钟数。
样例输入1
2 3
样例输出1
6
样例输入2
5 7
样例输出2
35
样例输入3
12 18
样例输出3
36
样例输入4
1000000007 1000000009
样例输出4
1000000007000000009
样例输入5
123456789 987654321
样例输出5
121932631112635269
数据范围
- 对于 30 % 30\% 30% 的数据, 1 ≤ a , b ≤ 1000 1 \leq a, b \leq 1000 1≤a,b≤1000;
- 对于 60 % 60\% 60% 的数据, 1 ≤ a , b ≤ 1 0 6 1 \leq a, b \leq 10^6 1≤a,b≤106;
- 对于 100 % 100\% 100% 的数据, 1 ≤ a , b ≤ 1 0 18 1 \leq a, b \leq 10^{18} 1≤a,b≤1018。
题目解析
这道题看起来是一个时钟问题,但实际上是一个最小公倍数的应用。
-
两个时钟再次同时指向12点,实际上就是两个时钟走过的格数的最小公倍数。
-
设第一个时钟走 x x x 格,第二个时钟走 y y y 格,则有:
a × x = b × y a \times x = b \times y a×x=b×y
这实际上就是求 a a a 和 b b b 的最小公倍数。
-
我们知道,最小公倍数可以通过最大公约数来计算:
l c m ( a , b ) = a × b g c d ( a , b ) lcm(a, b) = \frac{a \times b}{gcd(a, b)} lcm(a,b)=gcd(a,b)a×b
-
因此,我们首先用欧几里得算法计算出 a a a 和 b b b 的最大公约数,然后用上述公式计算出最小公倍数即可。
-
最小公倍数就是两个时钟再次同时指向12点所需的分钟数。
-
需要注意的是,最小公倍数可能会非常大,有可能超出
long long
的范围(例如样例4)。但在本题中,我们只需要输出结果,不需要对结果进行其他操作,所以不用担心溢出问题。
这个问题展示了最小公倍数在解决实际问题中的另一个应用。通过抽象问题,我们可以将问题转化为最小公倍数的计算。
好的,下面是这个问题的C++解答:
#include <iostream>
using namespace std;typedef long long ll;ll gcd(ll a, ll b) {return b == 0 ? a : gcd(b, a % b);
}ll lcm(ll a, ll b) {return a / gcd(a, b) * b;
}int main() {ll a, b;cin >> a >> b;cout << lcm(a, b) << endl;return 0;
}
解释:
-
我们定义了
long long
类型的别名ll
,以处理可能的大数据。 -
gcd
函数使用欧几里得算法的递归实现来计算最大公约数。 -
lcm
函数根据最小公倍数和最大公约数的关系,通过最大公约数来计算最小公倍数。注意这里我们先进行除法运算,然后再进行乘法运算,以避免中间结果溢出。 -
在主函数中,我们读入
a
和b
,表示两个时钟走一格的分钟数。 -
我们调用
lcm
函数计算a
和b
的最小公倍数,并输出结果。这个结果就是两个时钟再次同时指向12点所需的分钟数。
这个解答的时间复杂度为 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b))),空间复杂度为 O ( 1 ) O(1) O(1)。
需要特别注意的是样例4,在这个样例中,a
和 b
都是非常大的质数,它们的最小公倍数就是它们的乘积,这个结果已经超出了 long long
的范围。但在本题中,我们只需要输出结果,不需要对结果进行其他操作,所以不用担心溢出问题。在实际应用中,如果需要对这样的大数进行其他操作,就需要使用高精度计算了。
这个问题展示了如何将实际问题抽象为数学问题,并运用最大公约数和最小公倍数的知识来解决问题。这种思维方式在解决许多实际问题时都非常有用。