分块法:按 k 切块,预处理块内前缀 / 后缀最大值
与双端单调队列面对同一个问题——定长窗口取最值——这里走另一条思路(和 sparse table 同源):把数组每 k 个切成一块。一个长度恰为
k 的窗口,在这种切法下要么正好是一整块,要么横跨相邻两块——绝不会缩在一块内部、也不会跨三块。于是只要预处理出两样东西,每次查询就是一次 max。
两个预处理数组(各自在块内累积,跨块边界重置,做法类似前缀和):
rmax[i]= 从i到它所在块的结尾的最大值(后缀最大,从右往左推);lmax[i]= 从它所在块的开头到i的最大值(前缀最大,从左往右推)。
窗口 [L, R] () 被块边界切成两截:左截 [L, 块尾] 的最大值正是 rmax[L],右截 [块头, R] 的最大值正是 lmax[R]——答案 = max(rmax[L], lmax[R])。
1 · 为什么块大小恰好取 k?
块大小 = k 时,长度 k 的窗口最多只压在相邻两块上,两次查表就够。
- 块大于
k:窗口可能整段缩在一块内部,这块的lmax/rmax反映的是整块而非窗口,会算多 → 退回暴力; - 块小于
k:窗口会横跨好几块,中间整块的最大值还得另外查(这正是 sparse table 用倍增块长 + 两段覆盖任意区间的动机)。
取 k 让「两段正好拼成窗口、不多不少」。
分块 / sparse table 这条路的独特价值:预处理一次后,它能
回答任意区间的最值(不限定长 k),适合数组不变、反复查不同区间的静态场景;代价是不支持动态修改。要边改边查,得换线段树(见 区间查询系列)。而定长 k 且要流式处理时,单调队列开销更低。