Monsters And Spells

题目大意

给定一个长度为 \(n\) 的序列 \(a\),你需要构造一个序列 \(b\) 满足:

  • \(b_0 = 0\)
  • 对于任意一个 \(1 < i < n\)\(b_i \ge a_i\)
  • 对于任意一个 \(1 < i < n\)\(b_i\)\(0\)\(1\)\(b_{i-1} + 1\)

构造一个 \(b\) 使得 \(sum = \sum_{i = 1}^{n}{b_i}\) 最小,请输出这个 \(sum\)

思路

转换

对于构造一个 \(b\) 数组是非常困难的,考虑转换 \(a\) 数组,使问题变简单并且不会改变答案。

先考虑无解的情况,只有在当存在一个 \(1 \le i \le n\),使 \(a_i > i\) 无解,因为就算每次都加 \(1\),也不可能大于 \(a_i\)

其次对于每一个 \(i\)(\(2 \le i \le n\)),要想满足 \(b_i \ge a_i\)\(b_{i - 1}\) 至少为 \(a_i - 1\),转化为式子为 \(b_i = \max(a_i, b_{i + 1} - 1)\)

转化后可以发现,不仅不改变答案,而且对于每一个 \(i\),只要取一个不小于 \(b_i\) 的数就可以满足条件。

求解答案

对于每一个处理过的 \(b_i\),分成两种情况:

  1. \(b_i = 0\)\(1\):则 \(b_i\) 就取 \(0\)\(1\)
  2. \(b_i \neq 0\)\(1\):则 \(b_i\) 就取 \(b_{i - 1} + 1\)

可以证明,这种取法一定使最优的。

代码

#include <iostream>
using namespace std;

const long long N = 1000010;
long long n, a[N], ans[N], b[N];

int Main() {
    scanf("%d", &n);
    for (long long i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        if (i - a[i] < 0) {
            cout << -1 << endl;
            return 0;
        }
    }
    b[n] = a[n];
    for (long long i = n - 1; i >= 1; i--) b[i] = max(a[i], b[i + 1] - 1);
    long long ans = 0;
    for (long long i = 1; i <= n; i++) {
        if (b[i] != 0 && b[i] != 1)
            b[i] = b[i - 1] + 1;
        ans += b[i];
    }
    cout << ans << endl;
}

int main() {
    int T; cin >> T;
    while (T--) Main();
    return 0;
}

个人反思

考试的思路

在考试的过程中,因为要构造最小的 \(b\) 数组和数据范围,已经想出是用贪心求解,却没有想到要通过 \(b_i = \max(a_i, b_{i + 1} - 1)\) 来转化题目要求。

怎么改正

  1. 多刷偏思维类的题目,并且要灵活变换,有可能正序做也有可能倒序做。
posted @ 2025-09-29 20:16  wuzihenb  阅读(8)  评论(0)    收藏  举报