1 条题解
-
0
题目简述
给定一个由 个互不相同的正整数组成的数组 。你可以选择一个正整数 (),然后将每个 替换为 。要求操作后的数组中恰好包含两个不同的数值。题目保证这样的 一定存在,你只需输出任意一个。
解题思路
设 表示使用模数 操作后,数组 中不同余数的个数。
我们尝试从 开始,如果此时 ,则直接输出 。否则 ,即所有数模 的余数相同,也就是说所有 要么全是偶数,要么全是奇数。考虑一般情况:假设 ,即所有 都等于同一个值 。那么对于 ,每个 可以写为 。
- 如果 是偶数,则 ;
- 如果 是奇数,则 。
因此,从 翻倍到 时,余数最多分裂成两个值 和 。若所有 同奇偶,则 ,否则 。
由于 互不相同且 , 从 时的 开始,随着 不断翻倍,最终一定会出现分裂(因为当 时,,此时 )。因此,存在某个 使得 且 。我们只需从 开始不断翻倍,直到 为止。
算法步骤
- 读入 组数据。
- 对每组数据:
- 读入 和数组 。
- 初始化 。
- 循环:
- 计算所有 并放入集合 。
- 如果 ,输出 并退出循环。
- 否则 。
复杂度分析
- 每次检查 需要 时间。
- 最多翻倍 次,因为当 时必然有 。
- 每组数据时间复杂度 ,完全满足 的限制。
正确性证明
引理 1:若 ,则 。
证明:如前所述,所有 ,则 只能是 或 ,因此至多两种。引理 2:存在 使得 且 。
证明:当 时 ,。当 足够大使得 时,,由于 互不相同,。因此从 到 足够大的过程中, 从 变为 。由引理 1,每次翻倍 要么不变要么从 变成 ,所以必然存在某个 使得 且 。此时 即为所求。算法正确性:我们从 开始,若 则直接输出;否则 ,此时继续翻倍。根据上述论证,一定会在某个 时出现 ,因此算法总能找到合法解。
参考代码
#include <bits/stdc++.h> using namespace std; int main() { int t; cin >> t; while (t--) { int n; cin >> n; vector<long long> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } long long k = 2; while (true) { set<long long> s; for (long long x : a) { s.insert(x % k); } if (s.size() == 2) { cout << k << "\n"; break; } k *= 2; } } return 0; }
总结
本题的核心在于利用模数翻倍时余数的分裂性质,通过倍增法快速找到满足条件的 。该方法简洁高效,且符合题目要求的数据范围。
- 1
信息
- ID
- 6217
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者