CPH-第九章 区间查询

区间查询类问题,是后续高级数据结构与算法的重要基础。核心目标是在不同约束下高效处理以下问题:

  • 区间求和
  • 最值查询
  • 单点更新
  • 区间更新

9.1 静态数组查询

  • 针对数组元素不修改、仅回答查询的场景,有两种针对性的高效方案。

前缀和数组(区间和查询)

  • 适用场景:仅需计算区间和,无元素更新。
  • 预处理:\(O(n)\) 时间构造前缀和数组,prefix[k] 表示原数组从开头到位置 k 的总和。
  • 查询公式:任意区间 \([a, b]\) 的和可通过容斥在 \(O(1)\) 算出:sum(a,b) = prefix[b] - prefix[a-1]。
  • 二维扩展:二维前缀和可通过容斥公式 \(S(A)-S(B)-S(C)+S(D)\) 计算任意矩形子数组的和,同样支持 \(O(1)\) 查询。

稀疏表(Sparse Table,区间最值查询)

  • 适用场景:静态数组的区间最小值 / 最大值 / gcd 查询 ,不支持元素修改。
  • 预处理:\(O(n \log n)\) 时间,预计算所有长度为 \(2^k\) 的区间的最值。
  • 查询原理:
    • 对于任意区间 \([a, b]\),取最大的 \(k\) 使得 \(2^k\) ≤ 区间长度。
    • 用两个长度为 \(2^k\) 的重叠区间覆盖目标区间,取两者最值即可。
    • 查询时间 \(O(1)\)。
  • 特点:是静态区间最值的最优实现,常数极小,效率极高。

9.2 树状数组(Binary Indexed Tree / Fenwick Tree)

  • 定位:前缀和数组的动态升级版,支持单点更新 + 前缀和查询,两项操作均为 \(O(\log n)\)。
  • 核心原理:
    • 每个位置 k 对应一段区间,区间长度为 p(k) = k & -k(k 的二进制最低位 1 对应的数值)。
    • tree[k] 存储原数组中以 \(k\) 结尾、长度为 \(p(k)\) 的区间和。
  • 两个核心操作:
    • 前缀和查询:将 \([1, k]\) 拆分为若干个树状数组区间,累加得到结果。
    • 单点更新:修改位置 \(k\) 的值后,同步向上更新所有包含 \(k\) 的树状数组区间。
  • 优势:代码极短、运行常数远小于线段树。
  • 局限性:是仅支持前缀和类操作,无法直接处理区间最值。

9.3 线段树(Segment Tree)

  • 定位:通用型区间数据结构,支持几乎所有可合并的区间查询(和、最值、gcd、位运算等)与单点更新,两项操作均为 \(O(\log n)\)。
  • 结构设计:
    • 本质是一棵二叉树,叶子节点对应数组单个元素,内部节点存储对应区间的聚合结果。
    • 通常将数组长度补到 \(2\) 的幂,用大小为 \(2*n\) 的数组存储整棵树。
  • 核心操作
    • 区间查询:将目标区间拆分为 \(O(\log n)\) 个树节点,合并节点结果得到答案。
    • 单点更新:修改叶子节点后,自底向上更新所有父节点的值。
    • 区间更新:配合懒标记,区间加、区间赋值等操作同样可以在 \(O(\log n)\) 内完成
  • 与树状数组对比
    • 树状数组:仅擅长前缀和,代码简单、常数小。
    • 线段树:通用性极强,支持所有可合并的区间操作,但内存占用更大、实现稍复杂。

9.4 附加技巧

离散化 / 索引压缩(Index Compression)

  • 问题背景:当数组索引范围极大(如 \(10^9\))但实际用到的索引数量很少时,直接开数组会造成内存溢出。
  • 解法:将所有用到的索引收集排序,映射为连续的 \(1,2,3,\cdots\) 编号,保持相对顺序不变,再在压缩后的索引上建立数据结构。
  • 前提:必须提前知道所有会用到的索引,属于离线优化技巧。

区间更新

  • 问题背景:需要对整个区间 \([a, b]\) 统一加上一个值,同时支持查询。
  • 差分思想:构造差分数组,区间加 \(x\) 等价于在 \(a\) 处 \(+x\)、\(b + 1\) 处 \(-x\),仅需两次单点更新。
  • 实现方式:用树状数组或线段树维护差分数组,即可高效支持区间更新 + 单点查询。
posted @ 2026-10-30 15:00  LifeLotus  阅读(2)  评论(0)    收藏  举报