題解
題目連結
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;
}