题目
定义跳跃操作:对数字 x 进行参数为 y 的跳跃操作,会将 x 变为 2y−x。给定 n 个跳跃操作,编号 i 的参数为 ai。初始 x=0,你可以选择一些(可以选 0 个)操作,按编号从小到大的顺序依次执行,求最终 x 的最大值。
对于 100% 的数据,1≤n≤105,−109≤ai≤109。
思路
设选出的操作参数按顺序为 b1,b2,…,bt,并记执行到第 j 步后的值为 xj(x0=0)。根据操作定义 xj=2bj−xj−1。
引入交替和 Sj=xj/2,即 S0=0,且
Sj=bj−Sj−1.
于是 b1,b2,…,bt 展开后
St=bt−bt−1+bt−2−⋯+(−1)t−1b1,
而最终 x=2St。问题等价于:从原数组中按原顺序选一个子序列,最大化其交替和 St。
关键观察:第 j 次选择的变换是 S↦bj−S,这是一个关于 bj 的反射。若记「当前已选若干项后,所有可能得到的 S 值集合」为 V,那么再选一项 bj 后,新集合为 V∪{bj−S∣S∈V}。
反射保持区间结构,因此 V 始终是一个以某个实数为中心、对称翻转得到的集合。只要维护 V 中的最大值 maxS 和最小值 minS 即可:
- 不选 ai:集合不变;
- 选 ai:每个元素变为 ai−S,最大值变为 ai−minS,最小值变为 ai−maxS。
于是转移为
newMaxSnewMinS=max(maxS, ai−minS),=min(minS, ai−maxS).
初始时只选了空子序列,S=0,故 maxS=minS=0。最终答案为 2×maxS。
注意 maxS 的绝对值不超过 n⋅max∣ai∣≤1014,用 Int64 完全不会溢出。
复杂度
- 时间复杂度:O(n),每个元素一次常数时间转移。
- 空间复杂度:O(n) 存储输入数组(也可边读边转移优化到 O(1) 额外空间)。
仓颉实现
import std.env.*
import std.convert.*
main(): Int64 {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
var maxS: Int64 = 0
var minS: Int64 = 0
for (i in 0..n) {
let v = a[i]
let nmax = if (maxS > v - minS) { maxS } else { v - minS }
let nmin = if (minS < v - maxS) { minS } else { v - maxS }
maxS = nmax
minS = nmin
}
println((2 * maxS).toString())
return 0
}