CF2013D 题解

提供一个二分做法。

因为当 \(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;
            else
                add += 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;
            else
                minus += 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;
            else
                r = mid - 1;
        }

        i64 mn = l;
        l = 1;
        r = V;

        while (l < r) {
            i64 mid = (l + r) >> 1;

            if (check_max(mid))
                r = mid;
            else
                l = 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;
}
posted @ 2025-11-09 18:54  David9006  阅读(5)  评论(0)    收藏  举报