1 条题解
-
0
题目题解
问题理解
给定数组 ,已知存在一个“优美”数组 (满足 )和一个正整数 ,以及一个子集 ,使得:
$$b_i = \begin{cases} a_i \cdot x, & i \in S, \\ a_i, & i \notin S. \end{cases} $$要求输出任意一个可能的 。
第一步:必要条件
对于相邻位置 和 ,考虑 和 的可能来源:
-
若两者都乘以 ,则 ,,因此 。
由于 , 是正整数,所以 。 -
若只有 乘以 ,则 ,,此时 不一定成立。
-
若只有 乘以 ,则 ,,此时 不一定成立。
-
若两者都未乘 ,则 ,,因此 。
因此,无论哪种情况, 和 必须满足:
$$\gcd(b_i, b_{i+1}) \mid \frac{b_i}{\gcd(b_i, b_{i+1})} \quad \text{或类似?} $$更精确地,我们可以从整除性推导出 必须满足的条件。
第二步:导出 的约束
设 。
$$x \text{ 是 } \frac{b_i}{\gcd(b_i, b_{i+1})} \text{ 的倍数}, \quad \forall 1 \le i < n. $$
注意到 ,且 是公共因子。
经过分析(见官方解法), 必须是每个 的倍数,即:这是因为:
- 若 和 都含有 ,则 ,而 是整数,不影响。
- 若只有 含有 ,则 ,,且 ,所以 ,即 ?不直接。 更准确: 必须整除 ,所以 必须整除 。
同理,若只有 含有 ,则 必须被 整除,即 ,所以 ,这等价于 。
因此,对所有 , 必须整除
$$\frac{b_i}{\gcd(b_i, b_{i+1})} \quad \text{和} \quad \frac{b_{i+1}}{\gcd(b_i, b_{i+1})}. $$实际上,我们只需要取所有 的最小公倍数即可。
第三步:充分性
可以证明,若 取所有这些 的最小公倍数,则一定存在对应的 和 。
构造方法:从后往前,每次取 某值,可确保 。由于题目保证存在解,该 一定合法。
第四步:算法实现
- 初始化 ,。
- 从后往前遍历数组 :
- 输出 。
为什么从后往前?因为这样可以逐步累积后缀的 ,使得 正好是需要的因子。
时间复杂度
- 每个测试用例 。
- 总复杂度 $O(\sum n \log \max b_i) \le 6\times 10^5 \cdot \log 10^9$,可接受。
代码实现
#include <bits/stdc++.h> using namespace std; using ll = long long; ll gcd(ll a, ll b) { while (b) { a %= b; swap(a, b); } return a; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; vector<ll> b(n); for (int i = 0; i < n; i++) { cin >> b[i]; } ll g = 0, ans = 1; for (int i = n - 1; i >= 0; i--) { g = gcd(g, b[i]); ll val = b[i] / g; // 这里一定是整数除法 // 计算 lcm(ans, val) ll d = gcd(ans, val); // 检查溢出:如果 ans / d > 1e9 / val,则结果会超过 1e9 if (ans / d > 1000000000 / val) { ans = 1000000000; } else { ans = ans / d * val; } } cout << ans << '\n'; } return 0; }
验证样例
测试用例 输出 1 343 2 2 3 4 4 6 与题目输出一致。
总结
本题的关键在于:
- 从相邻元素的 推导出 必须整除 。
- 取所有这样的值的最小公倍数即得一个可行 。
- 利用后缀 技巧高效计算。
-
- 1
信息
- ID
- 6247
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者