[R82E] 计算器
数据规模:,。
思路
每条指令都是仿射变换。 记计算器中的数为 :
ADD a:,即 ;FLIP:,即 。
两者都可以写成 的形式,其中 。仿射变换对复合封闭:若 、,则
复合后仍是仿射变换,且一次项系数始终为 ,不会变复杂。
把操作序列拆成两段。 一次询问跳过 ,实际执行的是「前 条」再接「 到 条」。于是只要预处理出:
- :执行完前 条指令后的总体变换 , 为恒等;
- :从第 条执行到第 条的总体变换 , 为恒等。
答案就是 ,每次询问 。
递推。 设 ,则
因为 号指令在最外层。设 ,:若 是 ADD a,则 ;若是 FLIP,则 :
后缀必须从 倒推到 ,因为 依赖 。
数值范围。 恒为 ,常数项最大约为 ,再加上 的量级仍然远小于 ,64 位整数不会溢出。边界上 时取 , 时取 ,都是恒等变换,正好对应「跳过全部指令则结果保持 」,无需特判。
复杂度
预处理前缀与后缀各扫一遍,每次询问 :总时间 ,空间 。
仓颉实现
查看仓颉实现
import std.convert.*
import std.env.*
main() {
let reader = getStdIn()
let head = reader.readln().getOrThrow().split(" ", removeEmpty: true)
let n = Int64.parse(head[0])
let q = Int64.parse(head[1])
let nn = n
let typ = Array<Int64>(nn + 1, { _ => 0 })
let val = Array<Int64>(nn + 1, { _ => 0 })
for (i in 1..=n) {
let parts = reader.readln().getOrThrow().split(" ", removeEmpty: true)
if (parts[0] == "ADD") {
typ[i] = 0
val[i] = Int64.parse(parts[1])
} else {
typ[i] = 1
}
}
// 前缀 f_i(x) = preA[i] * x + preB[i]
let preA = Array<Int64>(nn + 1, { _ => 1 })
let preB = Array<Int64>(nn + 1, { _ => 0 })
for (i in 1..=n) {
if (typ[i] == 0) {
preA[i] = preA[i - 1]
preB[i] = preB[i - 1] + val[i]
} else {
preA[i] = -preA[i - 1]
preB[i] = -preB[i - 1]
}
}
// 后缀 g_i(x) = sufC[i] * x + sufD[i],g_{n+1} 为恒等
let sufC = Array<Int64>(nn + 2, { _ => 1 })
let sufD = Array<Int64>(nn + 2, { _ => 0 })
var i = n
while (i >= 1) {
if (typ[i] == 0) {
sufC[i] = sufC[i + 1]
sufD[i] = sufC[i + 1] * val[i] + sufD[i + 1]
} else {
sufC[i] = -sufC[i + 1]
sufD[i] = sufD[i + 1]
}
i -= 1
}
for (_ in 0..q) {
let parts = reader.readln().getOrThrow().split(" ", removeEmpty: true)
let l = Int64.parse(parts[0])
let r = Int64.parse(parts[1])
let k = Int64.parse(parts[2])
let mid = preA[l - 1] * k + preB[l - 1]
println(sufC[r + 1] * mid + sufD[r + 1])
}
}