提供一个二分做法。
因为当 \(a_i > a_{i + 1}\) 是做操作是不劣的,所以最终 \(a\) 一定单调不降。那么我们二分一个最小的 \(\max(a_i)\) 和最大的 \(\min(a_i)\),答案就是 \(\max(a_i) - \min(a_i)\)。
下面说一下如何 check,以 \(\max(a_i)\) 举例。设当前二分到 \(M\),check 的时候要把 \(> M\) 的元素变小。于是可以计算出最少要 \(-1\) 多少次,把它与最多能 \(+1\) 多少次比较,即可进行 check。
正确性证明。
#include <iostream>
#include <ranges>
#include <vector>using namespace std;
using i64 = long long;constexpr i64 V = 1e12;struct Solution {vector<i64> a;bool check_min(i64 mn){i64 minus = 0, add = 0;for (auto x : a) {if (x > mn)minus += x - mn;elseadd += mn - x;if (minus < add)return false;}return true;}bool check_max(i64 mx){i64 minus = 0, add = 0;for (auto x : a | views::reverse) {if (x < mx)add += mx - x;elseminus += x - mx;if (minus > add)return false;}return true;}void main(){int n;cin >> n;a.resize(n);for (auto& x : a)cin >> x;i64 l = 1, r = V;while (l < r) {i64 mid = (l + r + 1) >> 1;if (check_min(mid))l = mid;elser = mid - 1;}i64 mn = l;l = 1;r = V;while (l < r) {i64 mid = (l + r) >> 1;if (check_max(mid))r = mid;elsel = mid + 1;}i64 mx = l;cout << mx - mn << '\n';}
};int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);int t = 1;cin >> t;while (t-- > 0)Solution().main();return 0;
}