P10580 [蓝桥杯 2024 国 A] gcd 与 lcm 解题报告
解题报告: P10580 [蓝桥杯 2024 国 A] gcd 与 lcm
1. 问题分析与转化
题目核心
我们需要找到满足以下两个条件的、长度为 \(n\) 的序列 \(A = (a_1, a_2, \cdots, a_n)\) 的数量:
- 最大公约数
gcd(A) = x
- 最小公倍数
lcm(A) = y
初步观察
- 既然序列的最大公约数是 \(x\),那么序列中的每一个数 \(a_i\) 都必须是 \(x\) 的倍数。
- 既然序列的最小公倍数是 \(y\),那么序列中的每一个数 \(a_i\) 都必须是 \(y\) 的约数。
- 综合这两点,每个 \(a_i\) 既是 \(x\) 的倍数也是 \(y\) 的约数。这也意味着 \(x\) 必须是 \(y\) 的约数(即 \(y\) 能被 \(x\) 整除)。题目保证了有解,所以这个条件一定成立。
关键转化
为了简化问题,我们对序列中的每个数 \(a_i\) 都除以 \(x\)。设一个新的序列 \(B = (b_1, b_2, \cdots, b_n)\),其中 \(b_i = \frac{a_i}{x}\)。现在我们来分析新序列 \(B\) 需要满足什么条件:
-
最大公约数条件:
gcd(a_1, ..., a_n) = gcd(x*b_1, ..., x*b_n) = x * gcd(b_1, ..., b_n) = x
这要求gcd(b_1, ..., b_n) = 1
。 -
最小公倍数条件:
lcm(a_1, ..., a_n) = lcm(x*b_1, ..., x*b_n) = x * lcm(b_1, ..., b_n) = y
这要求lcm(b_1, ..., b_n) = y / x
。
现在,原问题就成功转化为了一个更简洁的新问题:
求有多少个长度为 \(n\) 的序列 \(B\),使得其最大公约数为 \(1\),最小公倍数为 \(y/x\)。
2. 核心思想:质因数分解
处理 gcd
和 lcm
问题,最强大的工具就是质因数分解。这是因为一个数的 gcd
和 lcm
可以通过它每个质因子的指数来独立确定。
gcd
取的是各数质因子指数的最小值。lcm
取的是各数质因子指数的最大值。
我们令 \(t = y/x\),并对 \(t\) 进行质因数分解:\(t = p_1^{k_1} \cdot p_2^{k_2} \cdots p_m^{k_m}\)。
对于序列 \(B\) 中的每一个数 \(b_i\),我们也可以分析其质因子的指数。对于 \(t\) 的任意一个质因子 \(p_j\),我们设 \(e_{ij}\) 为 \(p_j\) 在 \(b_i\) 中的指数。
那么,新问题的两个条件可以被分解到每一个质因子上:
gcd(B) = 1
\(\implies\) 对于任意质因子 \(p_j\),指数的最小值为 0。即min(e_{1j}, e_{2j}, ..., e_{nj}) = 0
。lcm(B) = t
\(\implies\) 对于任意质因子 \(p_j\),指数的最大值为 \(k_j\)。即max(e_{1j}, e_{2j}, ..., e_{nj}) = k_j
。
由于每个质因子 \(p_j\) 的指数如何分配是相互独立的,我们可以分别计算出满足条件的指数分配方案数,然后将它们全部乘起来,得到最终答案。
3. 单个质因子的求解:容斥原理
现在,我们只关注一个质因子,比如 \(p_1\)(为了方便,我们简写为 \(p\)),它在 \(t\) 中的指数是 \(k_1\)(简写为 \(k\))。
我们需要为序列 \(b_1, \dots, b_n\) 分配 \(p\) 的指数 \(e_1, \dots, e_n\)。
由于每个 \(b_i\) 都必须是 \(t\) 的约数,所以 \(p\) 在 \(b_i\) 中的指数 \(e_i\) 只能取 0, 1, ..., k
之间的一个整数。
我们的目标是找到满足下面两个条件的指数序列 \((e_1, \dots, e_n)\) 的数量:
- 至少有一个 \(e_i = 0\) (保证
gcd
对应的质因子指数为0) - 至少有一个 \(e_i = k\) (保证
lcm
对应的质因子指数为k)
直接计算满足 "至少..." 的问题比较复杂,我们可以使用容斥原理,通过计算其反面来求解。
-
总情况数 (无任何限制):
每个 \(e_i\) 都有 \(k+1\) 种选择(从 \(0\) 到 \(k\)),共 \(n\) 个数,所以总方案数是 \((k+1)^n\)。 -
不满足条件1的情况 (破坏
gcd
):
即 "没有一个 \(e_i=0\)",等价于 "所有 \(e_i > 0\)"。
此时每个 \(e_i\) 的取值范围是 \([1, k]\),共 \(k\) 种选择。方案数为 \(k^n\)。 -
不满足条件2的情况 (破坏
lcm
):
即 "没有一个 \(e_i=k\)",等价于 "所有 \(e_i < k\)"。
此时每个 \(e_i\) 的取值范围是 \([0, k-1]\),共 \(k\) 种选择。方案数为 \(k^n\)。 -
同时不满足条件1和2的情况:
即 "所有 \(e_i > 0\)" 且 "所有 \(e_i < k\)"。
此时每个 \(e_i\) 的取值范围是 \([1, k-1]\),共 \(k-1\) 种选择。方案数为 \((k-1)^n\)。
根据容斥原理,我们想要的答案是:
总数 - (不满足1) - (不满足2) + (同时不满足1和2)
即:
这就是对于单个质因子 \(p\),其指数的合法分配方案数。
4. 算法实现与总结
综合以上分析,我们的解题步骤如下:
- 读入 \(x, y, n\)。
- 计算 \(t = y/x\)。
- 对 \(t\) 进行质因数分解,得到所有的质因子 \(p_i\) 及其指数 \(k_i\)。
- 例如,如果 \(t=12=2^2 \cdot 3^1\),我们得到
(p=2, k=2)
和(p=3, k=1)
。
- 例如,如果 \(t=12=2^2 \cdot 3^1\),我们得到
- 初始化答案
ans = 1
。 - 遍历每一个质因子的指数 \(k_i\):
- 根据容斥原理公式,计算出该质因子的贡献:
count_i = ((k_i+1)^n - 2*k_i^n + (k_i-1)^n)
。 - 因为 \(n\) 很大,计算幂次需要使用快速幂算法。
- 在计算过程中要时刻对 \(998244353\) 取模。特别注意,减法可能产生负数,需要
(a - b + MOD) % MOD
来保证结果为正。 - 将这个贡献乘到最终答案中:
ans = (ans * count_i) % MOD
。
- 根据容斥原理公式,计算出该质因子的贡献:
- 输出最终的
ans
。
代码实现细节
getprime(y/x)
函数负责对 \(y/x\) 进行质因数分解,它只需要记录下每个质因子的指数 \(k_i\) 即可,质因子本身是什么不重要。qpow(base, exp)
函数是标准的快速幂模板,用于高效计算 \(base^{exp} \pmod{MOD}\)。- 主函数循环处理每个询问,按照上述算法流程计算答案。每次循环开始时,注意清空上一次质因数分解的结果。
这个解法的时间复杂度主要瓶颈在于质因数分解,为 \(O(\sqrt{y/x})\)。由于 \(y/x \le 10^9\),这个复杂度是完全可以接受的。