P10133 [USACO24JAN] Balancing Bacteria B(二階差分)

medicos發表於2024-09-11

題解

題目連結

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

相關文章