属性检查
- 难度 提高+
- 时限 1s
- 空限 256m
- 差分优化 DP
数据规模:,,,且所有记录中恰好有 个 。
思路
用「已加给智力的点数」作状态。 处理到某条记录时,若此前已经出现过 个 ,其中 个加给了智力,那么当前智力恒为 ,力量恒为 。也就是说,历史决策对后续的唯一影响就是这两个数字,而它们由 完全确定, 又是随时间确定的。于是可以只维护 :已获得 点属性、其中 点加给智力时,最多通过的检查数,其中 。
每条记录的转移。 三种记录对应三件事:
- :这一点要么加给力量( 不变),要么加给智力( 加一),因此 边界 、, 增加 。
- (智力检查):智力为 ,通过条件是 。
- (力量检查):力量为 ,通过条件是 ,即 。
后两种记录对所有 的判定结果都是「一个区间内通过、区间外不通过」,所以它们的转移就是对一段连续的 整体加 :智力检查加在 ,力量检查加在 。当 或 时区间为空,任何分配都过不了,直接跳过。
为什么能只记差分。 取 的转移只发生在 处,而区间加可以攒在差分数组里延迟处理。于是每条记录都只做 次差分修改;遇到 时,先把差分数组沿 展开成真实的 值,再按上面的式子做一次原地取 (从大到小扫 ,避免覆盖),最后把差分数组清零。每条检查记录累计的加法一定作用在当前这层 上,因为两层之间的所有修改都在遇到 时结算完毕。
边界与初值。 初始 ,只有 一个状态,。注意 内的每个 都是可达的(把 个点随意分给智力即可),其值总是某个合法方案通过数,非负且有限,所以初值统一取 不会污染 ,也不需要负无穷哨兵。差分数组长度取 ,右端点 时不会越界。所有记录处理完后答案还要再展开一次差分,取 。
正确性。 对 归纳: 是「恰好 点智力」这一类方案通过数的最大值,而每条检查能否通过只取决于 ,与具体把哪几个 分给智力无关,因此同一类内取最大值不丢失信息; 处的取 恰好对应「这一点加给哪一边」的两种选择。所以归纳结束后 描述的就是最优分配。
复杂度
每条记录 ,共 条;每个 处展开并清空长度为 的差分数组,总计 。时间 ,空间 。输入规模达到 个数,需要按字节扫描的快速读入。
仓颉实现
查看仓颉实现
import std.env.*
var gdata = Array<Byte>(0, { _ => 0 })
var gpos: Int64 = 0
var glen: Int64 = 0
func nextInt(): Int64 {
while (gpos < glen && Int64(gdata[gpos]) <= 32) {
gpos += 1
}
var neg = false
if (gpos < glen && Int64(gdata[gpos]) == 45) {
neg = true
gpos += 1
}
var x: Int64 = 0
while (gpos < glen && Int64(gdata[gpos]) > 32) {
x = x * 10 + Int64(gdata[gpos]) - 48
gpos += 1
}
if (neg) {
return -x
}
return x
}
main() {
gdata = getStdIn().readToEnd().getOrThrow().toArray()
glen = gdata.size
let n = nextInt()
let m = nextInt()
let dp = Array<Int64>(m + 2, { _ => 0 })
let diff = Array<Int64>(m + 3, { _ => 0 })
var z: Int64 = 0
for (_ in 0..n) {
let r = nextInt()
if (r == 0) {
var cur: Int64 = 0
for (i in 0..=z) {
cur += diff[i]
dp[i] += cur
}
for (i in 0..=(z + 1)) {
diff[i] = 0
}
var p = z + 1
while (p >= 1) {
if (dp[p - 1] > dp[p]) {
dp[p] = dp[p - 1]
}
p -= 1
}
z += 1
} else if (r > 0) {
if (r <= z) {
diff[r] += 1
diff[z + 1] -= 1
}
} else {
let lim = z + r
if (lim >= 0) {
diff[0] += 1
diff[lim + 1] -= 1
}
}
}
var cur: Int64 = 0
var ans: Int64 = 0
for (i in 0..=z) {
cur += diff[i]
let v = dp[i] + cur
if (v > ans) {
ans = v
}
}
println(ans)
}