1 条题解
-
0
题意理解
有一个长度为 的整数数组 (下标从 到 )。
每次操作:选择下标 ,将 减少 ,并将 增加 。
可以执行任意次操作。
目标:所有元素变成相等的非负整数。
求最少操作次数,若不可能则输出 。
操作分析
- 一次操作相当于:
- 数组下标是环形的: 时 是 。
- 总和变化: 所以每操作一次,总和减少 。
最终状态分析
设最终所有数都等于 ( 整数),进行了 次操作。
初始总和 。
最终总和:。所以:
且 。
目标:最小化 ,即最大化 (因为 、 固定)。
可行性的关键条件
设 表示从位置 传递到 的操作次数( 从 到 , 时传递到 )。
注意:每次操作是一次“传递”,所以 就是执行了多少次从 到 的操作。对于位置 :
- 从 收到 次增加(每次 +1)
- 向 送出 次减少(每次 -2)
- 初始值是
最终值:
这里下标模 : 就是 。
环形递推
我们有 个方程():
整理得:
其中 是环形条件。
求解方法
我们不知道 和 ,但:
. 最大化 :
因为 最小化,所以 最大可取 。
但 必须使得存在非负整数解 。. 枚举 :
从 到 递推:需要满足:
- 分子
- 分子为偶数
- 结果
. 环形验证:
最后检查 的方程:. 为什么只尝试 或 :
因为递推中每一步都需要分子为偶数, 的奇偶性影响整个环。
但实际解如果存在, 只有两种可能。枚举即可。
算法步骤
. 计算总和 。 . 令 (最大可能的目标值)。 . 尝试 和 :
- 递推计算 到
- 若某步出现负数或奇数,则失败
- 最后检查环形条件 . 如果成功,总操作次数为:
因为 总和正好是 。 . 否则输出 。
c++实现
#include <bits/stdc++.h> using namespace std; using ll = long long; void solve() { int n; cin >> n; vector<ll> a(n); ll sum = 0; for (int i = 0; i < n; i++) { cin >> a[i]; sum += a[i]; } // 最大化最终值 target = floor(sum / n) ll target = sum / n; // f[i] 表示从 i 传递到 i+1 的操作次数 vector<ll> f(n, 0); bool ok = true; // 枚举 f[0] = 0 或 1(奇偶性) for (int start = 0; start <= 1; start++) { f[0] = start; ok = true; // 递推 f[1] 到 f[n-1] for (int i = 1; i < n; i++) { ll need = a[i] + f[i-1] - target; if (need < 0 || need % 2 != 0) { ok = false; break; } f[i] = need / 2; } // 检查环形条件 if (ok && (a[0] + f[n-1] - 2 * f[0] == target)) { // 总操作次数 = sum(f[i]) ll total_ops = 0; for (ll x : f) total_ops += x; cout << total_ops << "\n"; return; } } // 无解 cout << "-1\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
复杂度
每个测试用例 ,总 不超过 ,可通过。
举例验证
例 :
,
,尝试 :
- 不行。
尝试 :
检查环形: ✅
总操作数 ,输出 ,与题目一致。
总结
- 每次操作总和减 ,最终值 越大操作次数越少。
- 最大化 尝试。
- 转化为环形递推,枚举 的两种奇偶性。
- 验证非负整数解即可。
- 1
信息
- ID
- 6649
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者