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;
}

浙公网安备 33010602011771号