1 条题解
-
0
题意简述
给定一个长度为 的数组 ,一次操作可以选择一个区间 ,将该区间内所有数 。 目标:让数组变成一个单峰序列(先严格递增,再严格递减),求最少操作次数。
关键观察
设最终数组为 ,要求:
存在 ,使得 且 。
只能通过区间加操作实现。
问题转化
由于只能加,不能减,所以 。
我们考虑从两边向中间构造单峰序列。
定义:
:将 变成严格递增的最小总增量。
:将 变成严格递减的最小总增量。
那么对于峰在位置 ,总操作次数为 (因为区间加可以覆盖任意区间,所以左右独立操作可以合并成若干次区间加,但这里我们求的是总增量,而一次区间加可以覆盖多个位置,所以总操作次数并不是直接相加,需要仔细考虑)。
计算 left[i]
要 且 ,最小化 。
贪心:设 ,然后 ,,依此类推。
那么:
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 ) 并且 。
这样 表示的是总增加量,而不是操作次数。
计算 right[i]
类似,从右往左: ,,依此类推。
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 ) 。
总操作次数
如果直接取 作为总增量,那么一次区间加可以给多个位置同时加 1,所以操作次数 ≤ 总增量。 实际上,我们可以构造操作方案使得操作次数 = ? 不对,更精确的结论是:
最少操作次数 = 为什么? 因为 可以看作是把前 个变成递增所需的“总增量”,这些增量可以通过若干次区间加实现,最少次数就是 本身(因为一次操作可以给多个位置加 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])) 其中 和 按上述递推计算。
算法步骤
计算 :
,
对 到 :
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) 计算 :
,
对 到 :
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) 答案 。
时间复杂度
。
示例代码(框架)
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
- 上传者