1 条题解
-
0
C. Dora 与 C++ 详细题解
问题重述
给定长度为 的数组 ,每次操作可以选择任意一个元素增加 或 。可以执行任意次操作(包括 次),求最终数组极差(最大值减最小值)的最小可能值。
关键观察
观察 1: 的情况
当 时,每个元素只能增加 的整数倍。设 。
将所有元素对 取模,得到余数 。由于每次操作增加 ,每个元素可以变成 的形式( 为非负整数)。
结论:最小值可以通过调整每个元素到某个区间 内来实现,其中 是某个 的余数加上若干倍 。
观察 2: 的情况
根据裴蜀定理(Bézout's identity),存在整数 使得:
这意味着我们可以通过组合操作,使任意元素增加或减少 。因此,问题等价于 的情况。
设 ,则每个元素可以变成 的形式( 为任意整数,可以为负)。
观察 3:转化为模 的问题
将每个元素对 取模:
由于可以加减 的任意整数倍,每个元素可以变成任意与 模 同余的数。因此,问题转化为:选择 个模 同余类,每个类选择一个代表数,使得最大值与最小值的差最小。
观察 4:最优策略
将所有余数排序:。
考虑将所有数调整到区间 内:
- 对于 ,这些数原本余数较小,需要加上 才能进入区间
- 对于 ,这些数保持原余数即可
此时,数组的最大值为 ,最小值为 。
极差为:
更简单的计算方式:考虑循环相邻差,最小极差为:
$$\text{ans} = \min_{i=1}^{n} ( (r_{i-1} + d) - r_i ) $$其中定义 (循环处理)。
算法步骤
- 计算
- 将每个 替换为
- 对余数数组排序
- 初始答案 = (最大减最小)
- 遍历 到 ,更新答案 = 答案
- 输出答案
时间复杂度
- 排序:
- 遍历:
- 总复杂度:
完整代码
#include <bits/stdc++.h> using namespace std; int main() { ios_base::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n, a, b; cin >> n >> a >> b; int d = gcd(a, b); vector<int> r(n); for (int i = 0; i < n; i++) { int x; cin >> x; r[i] = x % d; } sort(r.begin(), r.end()); int ans = r[n - 1] - r[0]; for (int i = 1; i < n; i++) { ans = min(ans, r[i - 1] + d - r[i]); } cout << ans << "\n"; } return 0; }代码说明
- 计算最大公约数:
gcd(a, b)使用 C++17 的std::gcd - 取模:
- 排序:方便后续遍历
- 答案计算:
- 初始答案:排序后最大值减最小值
- 循环相邻差:考虑将前 个数加 后的情况
示例验证
以第二个测试用例为例:
- ,
- ,(因为 ,任何数模 都是 )
- 排序后 ,
输出 ,与样例一致。
以第一个测试用例为例:
- ,
- ,
- 排序后
- 遍历:: ,: ,:
- 最小值 ,与样例一致。
总结
本题的核心技巧:
- 利用裴蜀定理将 和 归约到
- 将问题转化为模 的余数处理
- 排序后通过循环相邻差求最小极差
- 时间复杂度
- 1
信息
- ID
- 6324
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者