[R44C]贺卡
- 难度 提高
- 时限 1s
- 空限 256m
- 前缀和
对于 的数据,,,。
思路
剪下的图形是区间 内 个宽为 、底边共线的矩形条组成的整体。它的周长可以按 水平边 + 竖直边 两部分拆开计算。
水平边:底部是一条连续的水平线,长度为 ;顶部虽然高低起伏,但每个矩形顶部水平段长度恰为 ,整体投影覆盖 ,所以顶部水平总长恒为 ,与高度无关。水平边合计 。
竖直边:左边界从地面升起 、右边界降到地面 ,各贡献一条长为 、 的竖直边;相邻两个矩形条之间因高度差 产生台阶,每级台阶贡献一段长度为 的竖直边。竖直边合计 。
因此单次询问的答案为:
其中差和部分是相邻高度差绝对值的一段区间和,令 ,预处理前缀和 ,则 ,每次询问 回答。
以样例 , 为例:水平 ,竖直 ,合计 ,与样例一致。
复杂度
- 预处理差分前缀和:。
- 次询问,每次 ,合计 。
- 总时间复杂度 ,空间复杂度 ,可在 1s/256MB 限制内通过。
仓颉实现
import std.convert.*
import std.env.*
main(): Int64 {
let reader = getStdIn()
let first = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let n = first[0]
let q = first[1]
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
// a 是 0-indexed;为了公式方便用 0-indexed 推导
// prefixD[i] = sum_{j=0}^{i-1} |a[j+1]-a[j]|,即 prefixD[0]=0
// 区间 [l,r](1-indexed,闭)的差和 = sum_{i=l}^{r-1} |a[i+1]-a[i]| = prefixD[r-1] - prefixD[l-1]
let nn = n
let prefixD = Array<Int64>(nn + 1, { _ => 0 })
var acc = Int64(0)
for (i in 1..nn) {
var d = a[i] - a[i - 1]
if (d < 0) { d = -d }
acc += d
prefixD[i] = acc
}
let writer = getStdOut()
var k = Int64(0)
while (k < q) {
k += 1
let line = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let l = line[0]
let r = line[1]
// 周长 = 2*(r-l+1) + a[l] + a[r] + (prefixD[r-1] - prefixD[l-1])
// a 是 0-indexed,所以 a[l-1], a[r-1]
var ans = Int64(2) * (r - l + 1)
ans += a[l - 1]
ans += a[r - 1]
ans += prefixD[r - 1] - prefixD[l - 1]
writer.writeln(ans.toString())
}
return 0
}