前缀

01 / 共享的机制

前置花一次 O(N) 构建累积表 —— 之后的每次区间查询就退化成一次常数级减法。 把这个套路反过来用,就能在区间更新上同样廉价。

01 / 一句话本质
前置花一次 O(N · M) 建好角表 —— 之后的每次矩形求和就是在四个前缀角上做一次容斥
题目O(1) 求 mat[r1..r2][c1..c2] 的和矩阵5×5矩形(1,1)..(3,3)
mat
3
0
1
4
2
5
6
3
2
1
1
2
0
1
5
4
1
0
1
7
1
0
3
0
5
prefix
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
两块网格:左边是输入 mat,右边是前缀表 prefix。前缀表在顶行和左列各多出一行/一列的 0。
0 / 31
阶段
构建
结果
·
0 / 31

02 / 模式骨架

# 一次性建好二维前缀表prefix[0][*] 0; prefix[*][0] 0for i in 0..n−1:for j in 0..m−1:prefix[i+1][j+1] mat[i][j]+ prefix[i][j+1] + prefix[i+1][j]prefix[i][j]# 查询:用容斥求矩形 (r1, c1)..(r2, c2) 的和rectSum(r1, c1, r2, c2) prefix[r2+1][c2+1]prefix[r1][c2+1] prefix[r2+1][c1] + prefix[r1][c1]# 减去多算的两条带,再把重复扣掉的那个角加回来

03 / 什么时候用这个模式

"矩阵中某个矩形的和"
最典型的信号。矩阵不变,查询可以任意顺序、任意次数。建一次,查无数次 —— 只是放到二维。
"块和 / 子矩阵和"
块、瓦片、窗口、区域 —— 凡是读取一块连续矩形单元的,都能塌缩成四次角点读取。
"四角容斥"
这个套路的视觉抓手。两个大矩形在一个角上重叠 —— 两次都减掉,然后加回来一次。
"很多次矩形查询"
摊销规律和一维相同 —— 查询次数太少不划算。单次矩形求和不会比朴素扫描快。

04 / 常见坑

i.
忘了把那个角加回来。
两次减法(prefix[r1][c2+1]prefix[r2+1][c1])都包含了左上角的矩形 (0,0)..(r1−1, c1−1) —— 你把它减了两次。+ prefix[r1][c1]再把它补回来一次。漏掉这步,每次查询都正好少算这个角的和。
ii.
两个维度上同时出现 +1 偏移的差一错误。
前缀表大小是 (n+1) × (m+1),首行首列填 0。读表时,包含边界的远角是 r+1 / c+1,不包含边界的近角是 r / c。在同一次查询里把两种约定混用,是这里最常见的 bug 来源。
iii.
构建时递推方向不对。
每个 prefix[i+1][j+1] 都要读三个已经算好的格子(上、左、左上)。 必须行外层、列内层(或相反)地扫,绝不能斜着走或从下往上 —— 否则读到的是旧值。

— / 什么时候用哪个

一维线性
当对一个固定数组有很多次区间求和查询时使用。
区间和 · 累计 · LC 303
二维网格
用于矩阵上的矩形查询 —— 四个角上做容斥。
矩形区域 · 块和 · LC 304
差分
反过来用:很多次区间更新,最后一次性扫出每个位置的值。
区间加 · 拼车 · 航班预订
哈希映射
统计和等于目标值的子数组数 —— 前缀和 + 已见前缀的哈希表。
和为 K · 整除 K · LC 560