整体二分学习笔记

整体二分

整体二分是什么?

可以使用整体二分解决的题目需要满足以下性质:

  1. 询问的答案具有可二分性

  2. 修改对判定答案的贡献互相独立,修改之间互不影响效果

  3. 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值

  4. 贡献满足交换律,结合律,具有可加性

  5. 题目允许使用离线算法

    ——许昊然《浅谈数据结构题几个非经典解法》

解释

记 [𝑙,𝑟][l,r] 为答案的值域,[𝐿,𝑅][L,R] 为答案的定义域.(也就是说求答案时仅考虑下标在区间 [𝐿,𝑅][L,R] 内的操作和询问,这其中询问的答案在 [𝑙,𝑟][l,r] 内)

  • 我们首先把所有操作 按时间顺序 存入数组中,然后开始分治.
  • 在每一层分治中,利用数据结构(常见的是树状数组)统计当前查询的答案和 𝑚𝑖𝑑mid 之间的关系.
  • 根据查询出来的答案和 𝑚𝑖𝑑mid 间的关系(小于等于 𝑚𝑖𝑑mid 和大于 𝑚𝑖𝑑mid)将当前处理的操作序列分为 𝑞1q1 和 𝑞2q2 两份,并分别递归处理.
  • 当 𝑙 =𝑟l=r 时,找到答案,记录答案并返回即可.

需要注意的是,在整体二分过程中,若当前处理的值域为 [𝑙,𝑟][l,r],则此时最终答案范围不在 [𝑙,𝑟][l,r] 的询问会在其他时候处理.

P3527 [POI 2011] MET-Meteors

题意

给出一个环形序列,被分为 \(m\) 段。有 \(n\) 个国家,序列的第 \(i\) 段属于国家 \(o_i\)。接下来有 \(k\) 次事件,每次给环形序列上的一个区间加上一个正整数。每个国家有一个期望 \(p_i\),求出每个国家在序列上所有位置的值的和到达 \(p_i\) 的最早时间(或报告无法达到)。

\(1 \le n, m, k \le 3· 10 ^ 5\)

思路

首先确定整体二分的值域即我们二分的答案。

因为询问问的是天数,所以我们用 \(1\)\(k\) 作为我们整体二分的值域。

我们设整体二分的函数为 \(solve(L,R,l,r)\),其中 \(L\),\(R\) 表示值域,\(l\),\(r\) 表示询问。

对于当前二分到的值域区间 L,R,M=(L+R)/2,如果 [L,M] 的修改操作能够直接使得询问达成指标,则我们把这个询问放进询问区间 [l,m] 里,其中 m=(l+r)/2。

最后还原一下修改即可。

修改操作可以用树状数组+差分来实现,线段树也可以。

如何判断无解的情况呢?

我们可以加入一个虚拟的修改操作,即让区间 [1,m] 加上无穷大,让修改操作的个数 k 加一,最后输出答案的时候判断如果等于 k 即是无解。

P3834 【模板】可持久化线段树 2

题意

查询区间第 \(k\)

思路

静态区间第 \(k\) 小,简单模板题,我觉得应该第一个讲这道题的

经典做法是用朱熹树和分块在线回答,但是今天讲的是整体二分

整体二分 是一种 优雅 的离线算法,它将对所有询问(包括修改)一起进行二分答案,配合树状数组能够以 \(O((n+m)\log n \log V)\) 的复杂度解决问题

我们将所有询问一起进行二分。假设当前二分值域区间为 \([L,R]\)(离散化后的值域,\(L,R\) 均为下标),令 \(mid = \lfloor (L+R)/2 \rfloor\)。对于每个询问,我们需要知道区间内有多少个数 \(\le mid\),记作 \(cnt\)

  • \(cnt \ge k\),说明答案在 \([L,mid]\) 中;
  • 否则,答案在 \((mid,R]\) 中,且在该子区间内相当于寻找第 \(k-cnt\) 小的数

为了快速计算 \(cnt\),我们可以在序列上使用树状数组。具体做法如下:

  1. 将所有操作(包括初始的 \(n\) 个“插入”操作和 \(m\) 个“询问”操作)按顺序排列
    • 插入操作:\((pos, val)\),表示在位置 \(pos\) 上有一个值为 \(val\) 的数
    • 询问操作:\((l, r, k, id)\),表示询问 \([l,r]\) 中第 \(k\) 小的数,编号为 \(id\)
  2. 整体二分函数 solve(L, R, ops) 处理当前操作集合 ops,值域范围为 \([L,R]\)
    • ops 为空,直接返回。
    • \(L = R\),则 ops 中的所有询问的答案即为 \(L\) 对应的原数值,记录后返回
    • \(mid = (L+R)/2\)
    • 初始化树状数组为空。
    • 从左到右扫描 ops 中的每个操作:
      • 如果是插入操作 \((pos, val)\)
        • \(val \le mid\),则在树状数组的 \(pos\) 位置加 \(1\),并将该操作放入左子问题 lop
        • 否则,不修改树状数组,直接放入右子问题 rop
      • 如果是询问操作 \((l, r, k, id)\)
        • 查询树状数组中 \([l,r]\) 的和,记为 \(cnt\)
        • \(cnt \ge k\),说明答案在左半区间,将询问放入 lop
        • 否则,说明答案在右半区间,将询问的 \(k\) 减去 \(cnt\) 后放入 rop
    • 扫描结束后,需要将树状数组还原(即对所有在 \(pos\) 处加 \(1\) 的位置再次减 \(1\)
    • 递归处理左子问题 solve(L, mid, lop) 和右子问题 solve(mid+1, R, rop)
  3. 初始时,所有插入操作和询问操作构成初始操作序列 ops0,值域范围是 \([1, N]\),其中 \(N\) 是离散化后不同值的个数

P2617 Dynamic Rankings

题意

查询区间第 \(k\) 小,带修改

思路

  1. 将所有操作(包括初始序列的插入)按时间顺序排列成一个操作序列

  2. 对答案的值域进行二分,设当前值域为 \([L,R]\),取中点 \(mid = \lfloor (L+R)/2 \rfloor\)

  3. 遍历当前操作序列:

  4. 对于修改操作(如插入或删除一个值 \(v\)):

  • \(v \le mid\),则在树状数组的对应位置上加上该操作的贡献(\(+1\)\(-1\)),并将该操作划分到“左子区间”(答案可能在 \([L,mid]\)
  • 否则直接划分到“右子区间”(答案可能在 \([mid+1,R]\)
  1. 对于查询操作 \([l,r,k]\)
  • 用树状数组查询当前区间内 \(\le mid\) 的数的个数 \(cnt\)
  • \(cnt \ge k\),说明答案 \(\le mid\),划分到左子区间
  • 否则说明答案 \(> mid\),将 \(k\) 减去 \(cnt\) 后划分到右子区间
  1. 将当前层树状数组上的修改回滚(保证递归下层时树状数组为空)

  2. 将划分好的左右子区间操作按顺序放回原数组,递归处理左右子区间,直到 \(L=R\),此时所有属于该区间的查询答案即为 \(L\) 对应的原值

P3332 [ZJOI2013] K 大数查询

题意

查询区间第 \(k\) 小,带插入

思路

和上一道题差不多

P1527 [国家集训队] 矩阵乘法

题意

静态二维区间查询第 \(k\)

思路

将树状数组改为二维树状数组即可

posted @ 2026-03-08 14:32  _Flins  阅读(2)  评论(0)    收藏  举报