[R42D]超市
数据规模:,,,。
思路
支付金额 确定后,下标 被唯一确定为「最大的满足 的下标」,获得的价值为 。也即支付金额 落在某个桶 内时(约定 ), 固定,价值随 线性增长。
定义 (桶 能取到的最小价值),定义 (桶 能取到的价值上界 ,最后一段 )。由于 、 均严格递增, 与 都是严格递增序列,并且满足
所以各桶对应的价值区间 互不相交、依次排列。对一次询问 ,最优支付只有两种来源:
- 卡在某个桶的最低点:若 ,只需支付 即可获得价值 。由于 递增,应取满足 的最小 ,支付 。对 二分第一个 的位置即可。
- 在某个桶内部恰好达到 :若 ,则支付 落在该桶内(),价值恰为 。由上述区间互不相交,满足条件的 至多一个,即 的最大下标,再判断 是否成立即可。
对每次询问,两组候选取最小值即为答案,每个询问 。
复杂度
时间复杂度 ,空间复杂度 。
仓颉实现
import std.convert.*
import std.env.*
main(): Int64 {
let reader = getStdIn()
let head = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let n = head[0]
let q = head[1]
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let b = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let INF: Int64 = 4000000005
let out = StringBuilder()
var qi: Int64 = 0
while (qi < q) {
let w = Int64.parse(reader.readln().getOrThrow())
var ans: Int64 = -1
// 候选 A:取最小的满足 val_d >= w 的 a_d
var lo: Int64 = 0
var hi: Int64 = n - 1
var f: Int64 = -1
while (lo <= hi) {
let mid = (lo + hi) / 2
if (a[mid] + b[mid] >= w) {
f = mid
hi = mid - 1
} else {
lo = mid + 1
}
}
if (f != -1) {
ans = a[f]
}
// 候选 B:val_d < w < gap_d 时支付 w - b_d
lo = 0
hi = n - 1
var p: Int64 = -1
while (lo <= hi) {
let mid = (lo + hi) / 2
if (a[mid] + b[mid] < w) {
p = mid
lo = mid + 1
} else {
hi = mid - 1
}
}
if (p != -1) {
var gapv: Int64 = INF
if (p < n - 1) {
gapv = a[p + 1] + b[p]
}
if (w < gapv) {
let cand = w - b[p]
if (ans == -1 || cand < ans) {
ans = cand
}
}
}
out.append(ans.toString())
out.append("\n")
qi += 1
}
print(out.toString())
return 0
}