题解
题目链接
P10133 [USACO24JAN] Balancing Bacteria B
题目大意
对一个数列,加上若干次从 \(N\) 的位置开始到 \(1\) 的位置,以 \(-1\) 为公差的等差数列,其中首项 \(L\) 满足 \((1≤L≤N)\),且末项到 \(0\) 即结束。求最少使用多少次这样的操作可以使得数组所有项均为 \(0\) 。
解题思路
\(①\) 贪心 + 维护等差数列元素
常理下思考,本题可以按照顺序依次使每个元素变为 \(0\) 。而应当以何种顺序?显然是从左到右,\(a_1\) ~ \(a_n\) 。因为要修改离右边远的,就一定会修改到离右边近的,反之则不然。
而将某个元素变为 \(0\) 的同时,修改操作也会对右边所有数造成影响,若暴力修改右边每个元素,则全部操作的总时间复杂度会到 \(O(n^2)\) ,显然是不能接受的。
该影响可以通过维护等差数列元素来 \(O(n)\) 解决。这里 \(now\) 为当前等差数列元素,\(d\) 为公差。
long long ans = 0, now = 0, d = 0;for (int i = 1; i <= n; ++i) {now += d;a[i] += now;d -= a[i];now -= a[i];ans += abs(a[i]);}
\(②\) 二阶差分
对原数组 \(l\) ~ \(n\) 区间进行等差数列的修改,等价于对一阶差分数组 \(l\) ~ \(n\) 区间进行区间加相同值的操作,等价于对二阶差分数组 \(l\) 位置进行单点修改。因此求出二阶差分数组,并与最终全 \(0\) 二阶差分数组相比较求和即可得出答案。
long long ans = 0;for (int i = 1; i <= n; ++i) {d[i] = a[i] - a[i - 1];d2[i] = d[i] - d[i - 1];ans += abs(d2[i]);}
完整代码
//二阶差分
#include <bits/stdc++.h>using namespace std;signed main() {ios::sync_with_stdio(0), cin.tie(0);int n;cin >> n;vector<long long> a(n + 1), d(n + 1), d2(n + 1);for (int i = 1; i <= n; ++i) cin >> a[i];long long ans = 0;for (int i = 1; i <= n; ++i) {d[i] = a[i] - a[i - 1];d2[i] = d[i] - d[i - 1];ans += abs(d2[i]);}cout << ans << "\n";return 0;
}