ABC459F

解法

我们先把严格递增这个条件转换非降。

把每个 \(A_i\) 减去 \(i\),这样原来严格递增的一定非降,现在非降的一定递增。

不过为了没有负数,我们还可以再加上 \(n\)

转换后,移动还是与原来一样,从左到右。

在前缀和数组上考虑,如果将 \(a_i\) 移动到 \(a_{i+1}\),那么就相当于将 \(s_i -1\),所以对于每个 \(i\) 现在的 \(s_i\) 一定不大于原来的 \(s_i\)

那么反过来,对于一个新的非降序列 \(B\),总操作数就是对于每个 \(i\)\(A\)\(B\) 前缀值的差之和。

这样我们就相当于要求一个非降序列 \(B\),使其与 \(A\) 每个前缀值的差之和最小。

考虑将原数组分成多个块,每个块的平均值严格递增,这样我们可以通过将块分配为若干向下取整平均数和若干向上取整平均数使得块非降。

会不会造成 \(s_i\) 大于原来的 \(s_i\)

不会,因为只有在这一块的平均值小于等于上一块的平均值时,我们才需要合并这两块,而这样一定是从左往右补的。

接下来只需要把块存进单调栈,按上面的方法合并即可,合并完后按块的总和除以向下取整平均数的余数来分配向下取整平均数和向上取整平均数即可。最后答案就是分配完的数组与 \(A\) 每个前缀值的差之和。

代码

#include <bits/stdc++.h>
#define int long long
const int N = 2e5 + 5;
const int Mod = 1e9 + 7;
using namespace std;
using PII = pair<int, int>;
int T, n;
int a[N], b[N];
int get(PII t)
{
    return t.first / t.second;
}
void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        a[i] += n - i;
    }
    deque<PII> stk;
    for (int i = 1; i <= n; i++)
    {
        PII t = {a[i], 1};
        while (!stk.empty() && get(stk.back()) >= get(t))
        {
            auto [s, c] = stk.back();
            stk.pop_back();
            t.first += s, t.second += c;
        }
        stk.push_back(t);
    }
    int pos = 1;
    while (!stk.empty())
    {
        auto [s, c] = stk.front();
        stk.pop_front();
        int t = s % c;
        for (int i = 1; i <= c - t; i++)
        {
            b[pos] = s / c;
            pos++;
        }
        for (int i = 1; i <= t; i++)
        {
            b[pos] = s / c + 1;
            pos++;
        }
    }
    int s1 = 0, s2 = 0, ans = 0;
    for (int i = 1; i <= n; i++)
    {
        s1 += a[i];
        s2 += b[i];
        ans += s1 - s2;
    }
    cout << ans << endl;
}
signed main()
{
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
posted @ 2026-05-25 23:18  jackzhang2013  阅读(13)  评论(0)    收藏  举报