[R81E] 均值
数据规模:,,。
思路
记前缀和 ,约定 。 等价于 而 等价于 。于是问题变成:为每个 选一个 ,使相邻两项的差落在 ,且每项落在上面那个长度恰好为 的窗口里。
关键在于可行的 集合始终是一个区间。设第 步结束时可行的 恰好是 。固定一个 ,能接在它后面的 是 ;把所有 对应的区间并起来,得到 ,再与窗口 取交,仍是区间。因此 某一步 就说明交集为空,无解,输出 。
有了整条可行区间链之后倒着定值最省事:令 ,再对 取 每次都把 取到 与上界的较小值,既保证 ,又保证 。
正确性
可行集合确实是区间。 对 归纳。 时只有 。设第 步的可行集合为 ,则所有能被某个可行 推出的 构成 (区间并起来仍是区间,因为相邻区间彼此相接),再与第 个窗口取交。两个区间之交为空当且仅当 ,否则就是一个区间。所以判空的那一步恰好等价于真的无解,也说明第 条约束只通过 的取值范围影响后续,区间递推没有丢信息。
倒推出的 都落在区间里。 对 ,。设 已满足 ,则 ,又 ,所以 ,第 条约束 成立。
差值的上下界。 ,故 。若 ,则 ,;否则 ,而 ,故 。因此每个 。
三条合起来:输出 当且仅当无解;非 时输出的 一定满足全部约束。
复杂度
时间 ,空间 。 最大约 ,用 64 位整数即可。
仓颉实现
查看仓颉实现
import std.convert.*
import std.env.*
main() {
let reader = getStdIn()
let head = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let n = head[0]
let k = head[1]
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let nn = n
let lo = Array<Int64>(nn + 1, { _ => 0 })
let hi = Array<Int64>(nn + 1, { _ => 0 })
var ok = true
for (i in 1..=n) {
let ai = a[i - 1]
var l = ai * i
if (lo[i - 1] > l) {
l = lo[i - 1]
}
var h = ai * i + i - 1
if (hi[i - 1] + k < h) {
h = hi[i - 1] + k
}
lo[i] = l
hi[i] = h
if (l > h) {
ok = false
break
}
}
if (!ok) {
println(-1)
} else {
let s = Array<Int64>(nn + 1, { _ => 0 })
s[n] = hi[n]
let b = Array<Int64>(nn, { _ => 0 })
var i = n
while (i >= 1) {
var prev = hi[i - 1]
if (s[i] < prev) {
prev = s[i]
}
s[i - 1] = prev
b[i - 1] = s[i] - prev
i -= 1
}
let sb = StringBuilder()
for (j in 0..n) {
if (j > 0) {
sb.append(" ")
}
sb.append(b[j])
}
println(sb.toString())
}
}