1 条题解

  • 0
    @ 2025-11-18 18:11:46

    题意简述

    给定一个长度为 NN 的数组 AA,一次操作可以选择一个区间 [L,R][L,R],将该区间内所有数 +1+1。 目标:让数组变成一个单峰序列(先严格递增,再严格递减),求最少操作次数。

    关键观察

    设最终数组为 BB,要求:

    存在 kk,使得 B1<B2<<BkB_1 < B_2 < \dots < B_kBk>Bk+1>>BNB_k > B_{k+1} > \dots > B_N

    只能通过区间加操作实现。

    问题转化

    由于只能加,不能减,所以 BiAiB_i \ge A_i

    我们考虑从两边向中间构造单峰序列。

    定义:

    left[i]left[i]:将 1i1 \dots i 变成严格递增的最小总增量。

    right[i]right[i]:将 iNi \dots N 变成严格递减的最小总增量。

    那么对于峰在位置 ii,总操作次数为 left[i]+right[i]left[i] + right[i](因为区间加可以覆盖任意区间,所以左右独立操作可以合并成若干次区间加,但这里我们求的是总增量,而一次区间加可以覆盖多个位置,所以总操作次数并不是直接相加,需要仔细考虑)。

    计算 left[i]

    B1<B2<<BiB_1 < B_2 < \dots < B_iBjAjB_j \ge A_j,最小化 (BjAj)\sum (B_j - A_j)

    贪心:设 B1=A1B_1 = A_1,然后 B2=max(A2,B1+1)B_2 = \max(A_2, B_1+1)B3=max(A3,B2+1)B_3 = \max(A_3, B_2+1),依此类推。

    那么:

    l e f t [ 1 ]

    0 left[1]=0 l e f t [ i ]

    l e f t [ i − 1 ] + max ⁡ ( 0 , B i − 1 + 1 − A i ) left[i]=left[i−1]+max(0,B i−1 ​ +1−A i ​ ) 并且 Bi=max(Ai,Bi1+1)B_i = \max(A_i, B_{i-1}+1)

    这样 left[i]left[i] 表示的是总增加量,而不是操作次数。

    计算 right[i]

    类似,从右往左: BN=ANB_N = A_NBN1=max(AN1,BN+1)B_{N-1} = \max(A_{N-1}, B_N+1),依此类推。

    r i g h t [ N ]

    0 right[N]=0 r i g h t [ i ]

    r i g h t [ i + 1 ] + max ⁡ ( 0 , B i + 1 + 1 − A i ) right[i]=right[i+1]+max(0,B i+1 ​ +1−A i ​ ) Bi=max(Ai,Bi+1+1)B_i = \max(A_i, B_{i+1}+1)

    总操作次数

    如果直接取 left[k]+right[k]left[k] + right[k] 作为总增量,那么一次区间加可以给多个位置同时加 1,所以操作次数 ≤ 总增量。 实际上,我们可以构造操作方案使得操作次数 = max(left[k],right[k])\max(left[k], right[k])? 不对,更精确的结论是:

    最少操作次数 = max1iNmax(left[i],right[i])\max_{1 \le i \le N} \max(left[i], right[i]) 为什么? 因为 left[i]left[i] 可以看作是把前 ii 个变成递增所需的“总增量”,这些增量可以通过若干次区间加实现,最少次数就是 left[i]left[i] 本身(因为一次操作可以给多个位置加 1,但递增要求可能迫使某些位置必须分开加)。 实际上,JOI 官方解法给出:

    答案

    max ⁡ 1 ≤ i ≤ N ( max ⁡ ( l e f t [ i ] , r i g h t [ i ] ) ) 答案= 1≤i≤N max ​ (max(left[i],right[i])) 其中 left[i]left[i]right[i]right[i] 按上述递推计算。

    算法步骤

    计算 left[i]left[i]

    left[1]=0left[1] = 0, B1=A1B_1 = A_1

    i=2i=2NN

    l e f t [ i ]

    l e f t [ i − 1 ] + max ⁡ ( 0 , B i − 1 + 1 − A i ) left[i]=left[i−1]+max(0,B i−1 ​ +1−A i ​ ) B i

    max ⁡ ( A i , B i − 1 + 1 ) B i ​ =max(A i ​ ,B i−1 ​ +1) 计算 right[i]right[i]

    right[N]=0right[N] = 0, CN=ANC_N = A_N

    i=N1i=N-111

    r i g h t [ i ]

    r i g h t [ i + 1 ] + max ⁡ ( 0 , C i + 1 + 1 − A i ) right[i]=right[i+1]+max(0,C i+1 ​ +1−A i ​ ) C i

    max ⁡ ( A i , C i + 1 + 1 ) C i ​ =max(A i ​ ,C i+1 ​ +1) 答案 =max1iNmax(left[i],right[i])= \max_{1 \le i \le N} \max(left[i], right[i])

    时间复杂度

    O(N)O(N)

    示例代码(框架)

    cpp #include <bits/stdc++.h> using namespace std; using ll = long long;

    int main() { int N; cin >> N; vector A(N+1); for (int i = 1; i <= N; i++) cin >> A[i];

    vector<ll> left(N+1), right(N+1), B(N+1), C(N+1);
    
    // 从左到右
    B[1] = A[1];
    left[1] = 0;
    for (int i = 2; i <= N; i++) {
        left[i] = left[i-1] + max(0LL, B[i-1] + 1 - A[i]);
        B[i] = max(A[i], B[i-1] + 1);
    }
    
    // 从右到左
    C[N] = A[N];
    right[N] = 0;
    for (int i = N-1; i >= 1; i--) {
        right[i] = right[i+1] + max(0LL, C[i+1] + 1 - A[i]);
        C[i] = max(A[i], C[i+1] + 1);
    }
    
    ll ans = 1e18;
    for (int i = 1; i <= N; i++) {
        ans = min(ans, max(left[i], right[i]));
    }
    cout << ans << endl;
    
    return 0;
    

    }

    总结

    本题核心:

    将问题转化为从左到右的递增序列和从右到左的递减序列的构造。

    利用递推计算两个方向的最小总增量。

    最少操作次数取所有峰位置中两个方向增量的最大值的最小值。

    • 1

    信息

    ID
    5359
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者