[R31F]矩形求和2
数据规模:,,,矩形坐标都在 内。
思路
前缀化:设 ,即矩阵左上角 的子矩阵和。则矩形 的答案用容斥:
按位递归算 :把下标看成 位二进制数,从最高位逐层划分。当前层处理 ,令 ,左右两半的 和记为 ( 最高位为 用 ,为 用 ):
- 且 :所有异或的最高位都是 ,递归进左半。
- : 的部分贡献 ( 取满左半时 遍历整个左半); 的部分把 减 后递归进右半。
- :对称,贡献 , 减 后递归进右半。
- :同半的 两两块贡献 ,异半的两块贡献 ,最后 各减 后递归进左半。
递归只有一条长度为 的路径,每层 ,所以单个 是 。这本质上是二叉 trie 上的数字 DP。
数据结构:把 放进完全二叉树的堆式存储(下标 是叶子,内部节点存子树和,均模 )。修改 时沿祖先链逐个加 ;查询 时按上面的规则取左右子树和即可。
复杂度
时间 (修改 ,查询 ),空间 。 时节点数 。
仓颉实现
查看仓颉实现
import std.convert.*
import std.env.*
const MOD: Int64 = 998244353
// G(a, b) = sum_{i < a, j < b} f[i xor j],模 MOD。
// 用二叉 trie(完全二叉树,堆式存储)上的按位递归计算,每层 O(1)。
func g(a0: Int64, b0: Int64, n: Int64, tot: Array<Int64>): Int64 {
if (a0 == 0 || b0 == 0) {
return 0
}
var a = a0
var b = b0
var node: Int64 = 1
var ans: Int64 = 0
var level = n
while (level > 0) {
let half: Int64 = 1 << (level - 1)
let aHi = a > half
let bHi = b > half
if (aHi && bHi) {
let totL = tot[node * 2]
let totR = tot[node * 2 + 1]
ans = (ans + half * totL) % MOD
let bh = (b - half) % MOD
let ah = (a - half) % MOD
ans = (ans + bh * totR) % MOD
ans = (ans + ah * totR) % MOD
a = a - half
b = b - half
node = node * 2
} else if (aHi) {
let totL = tot[node * 2]
ans = (ans + (b % MOD) * totL) % MOD
a = a - half
node = node * 2 + 1
} else if (bHi) {
let totL = tot[node * 2]
ans = (ans + (a % MOD) * totL) % MOD
b = b - half
node = node * 2 + 1
} else {
node = node * 2
}
level = level - 1
}
ans = (ans + tot[node]) % MOD
return ans
}
main() {
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 size: Int64 = 1 << n
let tot = Array<Int64>(2 * size, { _ => 0 })
let fv = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
for (i in 0..size) {
tot[size + i] = fv[i] % MOD
}
var k = size - 1
while (k >= 1) {
tot[k] = (tot[k * 2] + tot[k * 2 + 1]) % MOD
k = k - 1
}
for (_ in 0..Q) {
let parts = reader.readln().getOrThrow().split(" ", removeEmpty: true)
if (parts[0] == "1") {
let i = Int64.parse(parts[1])
let v = Int64.parse(parts[2]) % MOD
var node = size + i
while (node >= 1) {
tot[node] = (tot[node] + v) % MOD
node = node / 2
}
} else {
let x1 = Int64.parse(parts[1])
let y1 = Int64.parse(parts[2])
let x2 = Int64.parse(parts[3])
let y2 = Int64.parse(parts[4])
var r = (g(x2 + 1, y2 + 1, n, tot) - g(x1, y2 + 1, n, tot) - g(x2 + 1, y1, n, tot) + g(x1, y1, n, tot)) % MOD
if (r < 0) {
r = r + MOD
}
println(r)
}
}
}