Loading

AT_agc040_e

很有意思的题。

首先考虑只有操作 \(1\),则答案显然 \(\ge \sum\limits_{i=1}^{n} [a_i > a_{i + 1}]\),即划分出最少的不降子段,同时每个子段又可以被一次操作消除,所以答案为 \(\sum\limits_{i=1}^{n} [a_i > a_{i + 1}]\)

对于只有操作 \(2\),同理,答案为划分出最少的不升子段的个数。

然后考虑把这两种结合起来,考虑把 \(a\) 序列拆成两个序列 \(p, q\),满足 \(\forall i, a_i = p_i + q_i\),我们要求 \(p\) 只能用操作 \(1\) 消除,\(q\) 只能用操作 \(2\) 消除,那么把 \(p\)\(q\) 消除完的方案数之和就是最终方案数。

考虑大力 DP,\(f_{i, j}\) 表示 \(p_i = j\),前 \(i\) 个数最少要操作多少次,则

\[f_{i, j} = \min\limits_{k = 0}^{a_{i - 1}}f_{i - 1, k} + [k > j] + [a_{i - 1} - k < a_i - j] \]

化简一下,得到

\[f_{i, j} = \min\limits_{k = 0}^{a_{i - 1}}f_{i - 1, k} + [j < k] + [j < a_i - a_{i - 1} + k] \]

容易发现 \(j\) 越小,越容易满足中括号里的条件,也就是说 \(f_{i, j}\) 关于 \(j\) 单调不增。另外,对于 \(f_{i, 0}\),肯定是从某个 \(f_{i - 1, k} + C_1(0 \le C_1 \le 2)\),那么 \(f_{i, a_i}\) 也可以从 \(f_{i - 1, k} + C_2(0 \le C_2 \le 2)\) 转移来,由于 \(C_1 \le C_2 + 2\),所以 \(f_{i, 0} \le f_{i, a_i} + 2\)也就是说 \(f_{i, j}\)\(j\) 分成了三个不同的值域段。

于是枚举 \(i\),记 \(d = f_{i - 1, a_i}\),那么肯定存在两个分界点 \(p \le q\),使得

\[\forall j \in [0, p] f_{i, j} = d + 2 \]

\[\forall j \in [p + 1, q] f_{i, j} = d + 1 \]

\[\forall j \in [q + 1, a_i] f_{i, j} = d \]

这里认为 \([l, r](l > r) = \varnothing\)

于是每次考虑 \(p, q, d\) 的变化即可,这只和 \(a_i - a_{i - 1}\) 有关,时间复杂度 \(\mathcal{O}(n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 2e5 + 10, mod = 998244353;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
    cout << arg << ' ';
    dbg(args...);
}
namespace Loop1st {
int n, d, p = -1, q = -1, a[N];
void main() {
    cin >> n;
    for (int i = 1; i <= n + 1; i++) {
        cin >> a[i];
        int del = a[i] - a[i - 1];
        if (del >= 0) {
            p = min(q, p + del);
            q += del;
        } else {
            p = min(p, q + del); p = max(p, -1);
            if (q >= a[i]) {
                d++;
                q = p; p = -1;
            }
        }
    }
    cout << d << '\n';
}

}
int main() {
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) Loop1st::main();
    return 0;
}
posted @ 2026-01-11 14:57  循环一号  阅读(2)  评论(0)    收藏  举报