1 条题解
-
0
题目重述
给定两个长度为 的数组 和 ,它们声称是某个隐藏数组 的前缀 GCD 和后缀 GCD:
$$p_i = \gcd(a_1, a_2, \dots, a_i), \quad s_i = \gcd(a_i, a_{i+1}, \dots, a_n). $$判断是否存在这样的数组 。
解题思路
1. 必要的基本条件
对于前缀 GCD 序列,我们有单调性: 一定是 的约数,即
同样,后缀 GCD 序列满足
另外,整个数组的 GCD 必须同时等于 和 ,因此
记这个公共值为 。若上述任一条件不成立,直接输出
NO。
2. 提取公共因子
令
则所有 均为正整数,且 。原问题等价于:是否存在数组 使得
$$\gcd(A_1,\dots,A_i) = P_i, \quad \gcd(A_i,\dots,A_n) = S_i, $$其中 也是正整数。最终 。
3. 单个位置的约束
考虑位置 ()。设我们已经知道:
- 前 个数的 GCD 为 ,
- 后 个数的 GCD 为 。
我们需要选择 ,使得:
$$\gcd(P_{i-1}, A_i) = P_i, \quad \gcd(A_i, S_{i+1}) = S_i. $$由于 是 的倍数(由前缀 GCD 性质),记
同理, 是 的倍数,记
则条件变为:
$$\gcd(x \cdot P_i, A_i) = P_i, \quad \gcd(A_i, y \cdot S_i) = S_i. $$将 写成 的形式(因为 必须同时是 和 的倍数)。代入第一式:
$$\gcd(x \cdot P_i,\; P_i S_i t) = P_i \cdot \gcd(x,\; S_i t) = P_i \quad \Longrightarrow \quad \gcd(x, S_i t) = 1. $$第二式:
$$\gcd(P_i S_i t,\; y S_i) = S_i \cdot \gcd(P_i t,\; y) = S_i \quad \Longrightarrow \quad \gcd(P_i t, y) = 1. $$我们希望存在整数 使得这两个互质条件同时成立。
4. 关键观察
由于 , 与 互质(因为 除以 后剩下的因子不会出现在 中)。同理, 与 互质。
那么,取 即可,此时条件变为:
如果这两个条件成立,那么 就是一个合法的选择。
如果其中某个 GCD 大于 1,则无论 取何值都无法同时满足两个互质条件(因为 与 互质, 的质因子只能靠 或 抵消,但 与 不互质时,即使 包含 的因子,也会破坏 的条件)。严格证明略,但结论是:条件 且 是充要的。
5. 边界情况
-
当 :没有 ,只需 。由于 ,所以 。取 (因为 ),条件自动满足( 与 互质?需要验证)。实际上我们统一用公式 对 也成立,但需要额外检查 ,其中 。所以边界条件也包含在后续检查中。
-
当 :类似地,取 ,需要 ,其中 。检查即可。
因此,我们只需对每个 (从 到 )检查 ,以及对每个 (从 到 )检查 。
6. 算法步骤总结
- 验证 (),(),且 。若失败,输出
NO。 - 令 ,计算 ,。
- 对于 到 :
- 令 (整数),若 ,输出
NO。
- 令 (整数),若 ,输出
- 对于 到 :
- 令 (整数),若 ,输出
NO。
- 令 (整数),若 ,输出
- 全部通过则输出
YES。
若需要构造一个合法数组,可取 。
7. 复杂度分析
每个测试用例 ,其中 是数值上限()。所有 总和不超过 ,因此总时间非常充裕。
代码实现(C++)
#include <bits/stdc++.h> using namespace std; using ll = long long; ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } void solve() { int n; cin >> n; vector<ll> p(n), s(n); for (int i = 0; i < n; ++i) cin >> p[i]; for (int i = 0; i < n; ++i) cin >> s[i]; // 基本整除条件 for (int i = 1; i < n; ++i) { if (p[i-1] % p[i] != 0) { cout << "NO\n"; return; } if (s[i] % s[i-1] != 0) { cout << "NO\n"; return; } } if (p[n-1] != s[0]) { cout << "NO\n"; return; } ll g = p[n-1]; vector<ll> P(n), S(n); for (int i = 0; i < n; ++i) { P[i] = p[i] / g; S[i] = s[i] / g; } // 检查中间条件 for (int i = 1; i < n; ++i) { ll x = P[i-1] / P[i]; if (gcd(x, S[i]) != 1) { cout << "NO\n"; return; } } for (int i = 0; i < n-1; ++i) { ll y = S[i+1] / S[i]; if (gcd(P[i], y) != 1) { cout << "NO\n"; return; } } cout << "YES\n"; } int main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; }
- 1
信息
- ID
- 6628
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者