2023-6-14

C. Helping the Nature

题意:

给你一个长度为 \(n\) 的数组,每次操作你可以选择以下三种类型:

  • \([1,i]\) 的所有数减一,\((1\leq i \leq n)\)
  • \([i,n]\) 的所有数减一,\((1\leq i \leq n)\)
  • 让所有数加一

请问最少操作几次,可以使得数组所有元素全变成 \(0\)

数据范围:
\(1\leq n \leq 2*10^5\)\(-10^9\leq a_i \leq 10^9\)

\(Tutorial:\)

首先我们直接考虑,不好考虑,但对于这种区间加减某个数的问题,我们可以往差分考虑,把区间加减,改成单点加减
我们设 \(b\)\(a\) 的差分数组,显然 \(a\) 的所有元素为 \(0\) 等价于 \(b\) 的所有元素为 \(0\)
现在我们看看三种类型的操作,对应到差分数组的操作是啥:

  • \(b[1]--\)\(b[i+1]++\)
  • \(b[i]--\)\(b[n+1]++\)
  • \(b[1]++\)\(b[n+1]--\)

由此我们可以得到最小操作次数就为:\(f_+(b)+f_-(b)+|b_1-f_-(b)|\)
其中 \(f_+(b)\) 表示 \(b\) 数组里面的所有正数之和,\(f_-(b)\) 表示 \(b\) 数组里面所有负数的绝对值之和


\(Code:\)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 2e5 + 10;
LL a[N], b[N];

void solve() {

    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if (i == 1) b[i] = a[i];
        else b[i] = a[i] - a[i - 1];
    }

    LL ans = 0;
    for (int i = 2; i <= n; i++) {
        if (b[i] > 0) {
            ans += b[i];
        } else {
            ans += -b[i];
            b[1] -= -b[i];
        }
    }

    ans += abs(b[1]);

    cout << ans << '\n';
    
}

int main() {

    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    //t = 1;
    while (t--) {
        solve();
    }    


    return 0;
}
posted @ 2023-06-16 00:10  jackle  阅读(8)  评论(0)    收藏  举报