1 条题解
-
0
题解:Hankson 的逆问题(数论 + 质因数分解)
一、解题核心思路
本题是 GCD/LCM 逆问题,核心是通过已知条件推导未知整数
x的约束,最终通过质因数分解统计合法x的个数。关键在于将x的约束转化为质因数指数的约束,利用数论性质简化问题:二、关键数论性质与约束推导
已知
x需满足:gcd(x, a₀) = a₁lcm(x, b₀) = b₁
核心推导:
-
约束 1 分析:
x必须是a₁的倍数(a₁ | x),否则gcd(x, a₀)不可能为a₁。- 设
a' = a₀ / a₁,则x/a₁与a'必须互质(gcd(x/a₁, a') = 1),否则gcd(x, a₀)会大于a₁。
-
约束 2 分析:
x必须是b₁的约数(x | b₁),否则lcm(x, b₀)不可能为b₁。- 设
m = b₁ / a₁,若b₁不能被a₁整除(b₁ % a₁ ≠ 0),则x不存在(因x需同时是a₁的倍数和b₁的约数)。
-
质因数指数约束: 对任意质数
p,设v_p(n)表示n的质因数分解中p的指数。则:- 约束 1 转化为:
min(v_p(x), v_p(a₀)) = v_p(a₁) - 约束 2 转化为:
max(v_p(x), v_p(b₀)) = v_p(b₁)
- 约束 1 转化为:
三、具体解题步骤
步骤 1:初步无解判断
- 若
b₁ % a₁ ≠ 0→ 无解(x需同时是a₁的倍数和b₁的约数)。 - 计算
a' = a₀ / a₁、m = b₁ / a₁(x = a₁ * k,k需整除m)。
步骤 2:质因数分解
对以下数字进行质因数分解(试除法,适用于
≤ 2×10⁹的数字):m:x = a₁*k中k的质因数来源。a':判断k需排除的质因数。a₁、b₀、b₁:获取各质因数的指数,用于约束判断。
步骤 3:逐质因数约束校验与计数
对
m的每个质因数p,结合约束推导v_p(x)的合法取值数量:- 获取指数:
v_a1 = v_p(a₁)(a₁中p的指数)v_b0 = v_p(b₀)(b₀中p的指数)v_b1 = v_p(b₁)(b₁中p的指数)
- 分情况讨论:
- 若
p整除a'(k不能含p):v_p(x) = v_a1,需满足max(v_a1, v_b0) = v_b1→ 不满足则无解。
- 若
p不整除a'(k可含p):- 若
v_b0 < v_b1→v_p(x)必须为v_b1(仅 1 种选择)。 - 若
v_b0 == v_b1→v_p(x)可在[v_a1, v_b1]中取值(共v_b1 - v_a1 + 1种选择)。
- 若
- 若
步骤 4:补充校验
a₁的额外质因数a₁可能含不在m中的质因数p(k不含p,故v_p(x) = v_p(a₁)),需校验max(v_p(a₁), v_p(b₀)) = v_p(b₁)→ 不满足则无解。步骤 5:统计合法
x的个数所有质因数的合法取值数量的乘积,即为答案(质因数指数选择独立)。
四、完整代码实现
#include <iostream> #include <map> #include <algorithm> using namespace std; // 质因数分解:返回 n 的质因数及其指数(n ≥ 1) map<int, int> factorize(int n) { map<int, int> res; if (n == 1) return res; for (int i = 2; i * i <= n; ++i) { while (n % i == 0) { res[i]++; n /= i; } } if (n > 1) res[n]++; return res; } // 计算 n 中质因数 p 的指数(n ≥ 1,p ≥ 2) int get_exponent(int n, int p) { int cnt = 0; while (n % p == 0 && n > 0) { cnt++; n /= p; } return cnt; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; while (n--) { int a0, a1, b0, b1; cin >> a0 >> a1 >> b0 >> b1; // 初步无解判断:b1 必须是 a1 的倍数 if (b1 % a1 != 0) { cout << 0 << '\n'; continue; } int a_prime = a0 / a1; int m_val = b1 / a1; // 质因数分解 auto m_factors = factorize(m_val); auto a_prime_factors = factorize(a_prime); auto a1_factors = factorize(a1); long long ans = 1; // 处理 m 的所有质因数 for (auto &[p, exp_m] : m_factors) { int v_a1 = get_exponent(a1, p); int v_b0 = get_exponent(b0, p); int v_b1 = get_exponent(b1, p); // 判断 p 是否整除 a' bool p_divides_a_prime = (a_prime_factors.count(p) > 0); if (p_divides_a_prime) { // 约束:v_p(x) = v_a1,需满足 max(v_a1, v_b0) = v_b1 if (max(v_a1, v_b0) != v_b1) { ans = 0; break; } } else { // 约束:v_p(x) ∈ [v_a1, v_b1] if (v_a1 > v_b1) { ans = 0; break; } // 根据 v_b0 与 v_b1 的关系计算可选数 if (v_b0 < v_b1) { // 必须取 v_b1,仅 1 种选择 continue; } else { // v_b0 == v_b1,可选数为 v_b1 - v_a1 + 1 ans *= (v_b1 - v_a1 + 1); } } } // 处理 a1 的质因数中不在 m 中的部分 if (ans != 0) { for (auto &[p, exp_a1] : a1_factors) { if (m_factors.count(p) == 0) { // 约束:v_p(x) = exp_a1,需满足 max(exp_a1, v_b0) = v_b1 int v_b0 = get_exponent(b0, p); int v_b1 = get_exponent(b1, p); if (max(exp_a1, v_b0) != v_b1) { ans = 0; break; } } } } cout << ans << '\n'; } return 0; }五、代码解释
- 质因数分解函数:
factorize用试除法分解数字,返回质因数及其指数;get_exponent快速获取单个质因数的指数。 - 初步无解判断:快速排除
b1不能被a1整除的情况,减少无效计算。 - 质因数约束处理:逐质因数校验约束,统计合法取值数量,利用乘法原理累计答案。
- 补充校验:处理
a1中未出现在m中的质因数,确保所有约束都被满足。
六、复杂度分析
- 时间复杂度:每组数据的质因数分解时间为
O(√n)(n ≤ 2×10⁹,√n ≤ 4.5×10⁴),n ≤ 2000组数据,总时间可接受。 - 空间复杂度:存储质因数的映射表,空间开销极小(质因数个数不超过
log2(2×10⁹) ≈ 31)。
该代码通过数论推导简化约束,利用质因数分解精准统计合法解,高效解决了题目中的逆问题。
- 1
信息
- ID
- 5411
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者