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

题解

题目链接

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;
}
posted @ 2024-09-11 01:51  medicos  阅读(115)  评论(0)    收藏  举报