最大公约数和最小公倍数问题
最大公约数和最小公倍数问题
题目描述
输入两个正整数 $x_0, y_0$,求出满足下列条件的 $P, Q$ 的个数:
-
$P,Q$ 是正整数。
-
要求 $P, Q$ 以 $x_0$ 为最大公约数,以 $y_0$ 为最小公倍数。
试求:满足条件的所有可能的 $P, Q$ 的个数。
输入格式
一行两个正整数 $x_0, y_0$。
输出格式
一行一个数,表示求出满足条件的 $P, Q$ 的个数。
样例 #1
样例输入 #1
3 60
样例输出 #1
4
提示
$P,Q$ 有 $4$ 种:
- $3, 60$。
- $15, 12$。
- $12, 15$。
- $60, 3$。
对于 $100\%$ 的数据,$2 \le x_0, y_0 \le {10}^5$。
【题目来源】
NOIP 2001 普及组第二题
Answer1
这个问题的关键在于理解,由于$P$ 和$Q$ 的最大公约数为$x_0$,且它们的最小公倍数为$y_0$,所以实际上$P$ 和$Q$ 必须是$x_0$ 的倍数,且$P/Q$ 或$Q/P$ 必须是互质的(即除了$x_0$ 外,它们没有其他公约数)。
因此,正确的方法是先计算$y_0$ 相对于$x_0$ 的比值,即$n = y_0 / x_0$,然后找到所有$n$ 的因子(这里的因子指的是能整除$n$ 的数)。对于每一个因子$i$,我们检查如果$i$ 与$n / i$ 互质,那么这对因子就可以形成一对合法的$P, Q$。
这段代码中新增加了一个辅助方法 gcd 用于计算两个数的最大公约数,它是通过欧几里得算法实现的。主方法 countPairs 中的核心逻辑是遍历所有可能的$i$ 值,如果$i$ 是$n$ 的因子,并且$i$ 与$n / i$ 互质(即它们的最大公约数为 1),那么这对因子就符合题目条件。如果$i$ 和$n / i$ 相等,那么这种情况只能算作一对$P, Q$(因为它们是相同的数),否则我们可以通过交换$P$ 和$Q$ 的值来得到另外一对不同的$P, Q$,因此总数翻倍。
public class Main {
public static void main(String[] args) {
int x0 = 3; // 示例输入
int y0 = 60; // 示例输入
System.out.println(countPairs(x0, y0));
}
private static int countPairs(int x0, int y0) {
int n = y0 / x0; // n 是 y0 除以 x0
int nn = y0 % x0;
if (nn != 0) return 0; // 检查y是否是x的倍数。如果不是,说明不存在满足条件的P,Q对,直接输出 0。
int count = 0; // 用于存储满足条件的 P, Q 对的数量
for (int i = 1; i * i <= n; i++) {
if (n % i == 0) {
// 检查 i 和 n/i 是否互质
if (gcd(i, n / i) == 1) {
// 如果 i*i == n,这种情况只能算一次,因为 P 和 Q 是一样的
if (i * i == n) {
count++;
} else {
// 否则,P 和 Q 可以互换位置,所以计数翻倍
count += 2;
}
}
}
}
return count;
}
// 辅助方法:计算最大公约数(GCD)
private static int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
}
Answer2
符号约定
我们使用$(a,b)$表示$a,b$这两个数的最大公因数,使用$[a,b]$表示$a,b$这两个数的最小公倍数。
我们使用$a\mid b$表示$a$能整除$b$,或者说$b$能被$a$整除;
我们使用$a\nmid b$表示$a$不能整除$b$,或者说$b$不能被$a$整除。
算术基本定理
任何大于1的整数都可以被分解成若干个素数的幂的乘积,且不计素因子的排列顺序时分解方法是唯一的。
例:$720=2^4\cdot 3^2\cdot 5^1$
上面的操作叫做素因数分解。若将素因子从小到大排列,就得到了标准素数分解式。
最大公因数和最小公倍数与素数分解式有什么联系呢?
举个例子:
我们尝试分解样例中的提到的一组数$12,15$:
$12=2^2\cdot 3^1,\quad 15=3^1\cdot 5^1$
为了方便下面的讨论,尽管$12$没有素因子$5$,$15$没有素因子$2$,但我们还是要把非共有的素因子写成$0$次幂的形式:
$12=2^2\cdot 3^1\cdot 5^0,\quad 15=2^0\cdot 3^1\cdot 5^1$
将两个数素数分解式中每个素因子的指数部分取两个中的最小值,就得到了两个数的最大公因数:
$(12,15)=2^0\cdot 3^1\cdot 5^0=3$
将两个数素数分解式中每个素因子的指数部分取两个中的最大值,就得到了两个数的最小公倍数:
$[12,15]=2^2\cdot 3^1\cdot 5^1=60$
倒着推我们可以发现,要使$(P,Q)=3$且$[P,Q]=60$,$P,Q$这两个数的素数分解式必须满足下面3个条件:
素因子$2$的指数的最大值为$2$,最小值为$0$.$\quad \Rightarrow$ 其中一个素数分解式中$2$的指数为$2$,而另一个为$0$;
素因子$5$的指数的最大值为$1$,最小值为$0$.$\quad \Rightarrow$ 其中一个素数分解式中$5$的指数为$1$,而另一个为$0$;
素因子$3$的指数的最大值为$1$,最小值为$1$.$\quad \Rightarrow$ 两个素数分解式中$3$的指数均为$1$;
像$2$和$5$这样的,指数在$x_0,y_0$的素数分解式中不同的素因子,能导致$P,Q$可变;而像$3$这样的,指数在$x_0,y_0$的素数分解式中相同的素因子,在$P,Q$中的指数一定是相同的,没法变化。
计算方法
如何快捷地寻找$x_0,y_0$的指数不同的素因子呢?
对$\dfrac{y_0}{x_0}$进行素因数分解,得到的指数大于0的素因子即是导致$P,Q$可变的素因子。
我们只需要统计这样的素因子的数量,就像把每种素因子看成不同的球,要放进$P,Q$这两个不同的袋子里,每一种素因子都有放进$P$和放进$Q$这两种选择。
假设$\dfrac{y_0}{x_0}$的指数大于0的素因子有$n$个,那么就能产生$2^n$对$P,Q$的组合。
如果$x_0$不能整除$y_0$呢?
这表明不存在符合条件的$P,Q$.
反证:
不妨假设存在符合条件的$P\in\mathbb Z_+$,
使得:$x_0$是$P$的因数,那么将有$x_0\mid P$;
使得:$y_0$是$P$的倍数,那么将有$P\mid y_0$;
根据整除的传递性,有$x_0\mid y_0$;
这个结论与题设矛盾,故假设不成立。
于是当$x_0\nmid y_0$时,我们输出0作为答案。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(bf.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
int count = 0; //统计素因数的个数
if (y % x != 0) {
System.out.println(0);
return;
} else {
int q = y / x;
int cf = 2; //用来试验整除性的因数
while (q > 1) { //等于1时标志着分解完毕
if (q % cf == 0) {
count++;
while (q % cf == 0) { //若能整除就除到底
q /= cf;
}
}
cf++;
}
}
System.out.println(1 << count); //使用位运算来产生2的方幂
}
}
解题步骤和思想如下:
-
验证条件:首先检查 $y$ 是否是 $x$ 的倍数。如果不是,说明不存在满足条件的 $P, Q$ 对,直接输出 0。
-
计算商:计算 $y / x$ 的值,此操作基于最大公约数和最小公倍数的关系:如果 $P, Q$ 的最大公约数为 $x$,最小公倍数为 $y$,那么 $P \times Q = x \times y$。由于 $P, Q$ 都是 $x$ 的倍数,考虑 $P = x \times a, Q = x \times b$,其中 $a, b$ 互质,那么 $a \times b = y / x$。
-
质因数分解:通过遍历,对 $y / x$ 进行质因数分解,计算出所有的素因数。每次当发现 $quotient$ 可以被当前的素因数 $currentFactor$ 整除时,就一直除以这个因数,直到不能整除为止。在这个过程中,每发现一个新的素因数,就增加计数器 $count$ 的值。
-
计算对数:因为每个素因数可以独立选择分配给 $P$ 或 $Q$(但至少要分配给一个),所以如果有 $n$ 个不同的素因数,就会有 $2^n$ 种分配方式。这是因为每个素因数都有两种选择(分配给 $P$ 或 $Q$),所有素因数的组合就构成了不同的 $P, Q$ 对。最终,通过 $1 << count$ 使用位运算来快速计算 $2^n$,即 $2$ 的 $count$ 次方,输出这个值作为结果。