整体二分学习笔记

整体二分

整体二分是什么?

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

  1. 询问的答案具有可二分性;
  2. 修改对判定答案的贡献互相独立,修改之间互不影响;
  3. 修改对判定答案的贡献是一个确定的值,与判定标准无关;
  4. 贡献满足交换律、结合律,并且具有可加性;
  5. 题目允许离线算法。

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

解释

\([l,r]\) 为答案的值域,\([L,R]\) 为答案的定义域(即当前只考虑下标在 \([L,R]\) 内的操作和询问,且这些询问的答案落在 \([l,r]\) 内)。

  • 首先,将所有操作 按时间顺序 存入数组,然后开始分治。
  • 在每一层分治中,借助数据结构(通常是树状数组)统计当前询问的答案与 \(mid\) 的关系。
  • 根据查询结果与 \(mid\) 的关系(小于等于 \(mid\) 或大于 \(mid\)),将当前处理的操作序列划分为 \(q1\)\(q2\) 两份,并分别递归处理。
  • \(l = r\) 时,找到答案,记录并返回。

需要注意的是,在整体二分过程中,若当前处理的值域为 \([l,r]\),则最终答案不在 \([l,r]\) 内的询问会在其他时候处理。

例子:查询区间第 \(k\)

将所有询问一起进行二分。假设当前二分值域区间为 \([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
    • 扫描结束后,需要还原树状数组(即对所有加过 \(1\) 的位置再次减 \(1\));
    • 递归处理左子问题 solve(L, mid, lop) 和右子问题 solve(mid+1, R, rop)
  3. 初始时,所有插入操作和询问操作构成初始操作序列 ops0,值域范围为 \([1, N]\),其中 \(N\) 是离散化后不同值的个数。


例题

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

题意:查询区间第 \(k\) 小。

思路:如上例所述。


P3527 [POI 2011] MET-Meteors

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

数据范围:\(1 \le n, m, k \le 3 \times 10^5\)

hint 1:首先确定整体二分的值域,即我们二分的答案。考虑加入一个虚拟的修改操作。

hint 2:使用树状数组配合差分实现修改。

思路
将天数作为值域,即用 \(1\)\(k\) 作为整体二分的值域。定义递归函数 solve(L, R, l, r),其中 \([L,R]\) 为值域(天数),\([l,r]\) 为询问集合。
对于当前二分到的值域区间 \([L,R]\),取 \(mid = (L+R)/2\)。如果 \([L,mid]\) 内的修改操作已经能使某个询问达到指标,则将该询问放入左子区间 \([l, m]\)\(m = (l+r)/2\)),否则放入右子区间。
修改操作可以用树状数组配合差分实现(线段树亦可)。
如何判断无解?可以加入一个虚拟的修改操作,即给区间 \([1,m]\) 加上无穷大,并将修改操作总数 \(k\) 加一。最后输出时,若答案等于 \(k\),则表示无解。


P2617 Dynamic Rankings

题意:查询区间第 \(k\) 小,带单点修改。

hint:考虑使用支持修改的数据结构。

思路:与静态区间第 \(k\) 小的模板类似,只是需要支持修改操作。可以将修改操作视为先删除原值再插入新值,配合树状数组维护即可。


P3332 [ZJOI2013] K 大数查询

题意:查询区间第 \(k\) 大,带插入操作(在区间内每个位置插入一个数)。

思路:与上一题类似,整体二分配合树状数组。注意此处是第 \(k\) 大,二分时需相应调整条件。


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

题意:静态二维区间查询第 \(k\) 小。

思路:将一维树状数组扩展为二维树状数组即可。


P4175 [CTSC2008] 网络管理

题意:一棵 \(n\) 个结点的树,每个点带点权。有 \(m\) 次操作,每次操作要么更改单点点权,要么查询树链上的第 \(k\) 大点权。

hint 1:初始点权视为增加点权,插入在所有操作的最前面。更改点权可以看成删除原值再增加新值,即拆成两次修改。

思路
整体二分时,二分答案 \(mid\),将大于 \(mid\) 的修改转化为单点权值 \(\pm 1\)。对于树链查询第 \(k\) 大,转化为链上权值之和是否等于 \(k\)
二分条件:如果链上大于 \(mid\) 的点数小于 \(k\),则答案小于等于 \(mid\),否则答案大于 \(mid\)
考虑每个节点维护到根路径上的信息,则单点修改变成子树修改,链查询变成四个单点查询(需借助 LCA)。子树对应 DFS 序上的一个区间,区间加法、单点查询可以用树状数组差分技巧转化为单点差分、区间前缀和。
LCA 可以在 DFS 时用 Tarjan 算法离线处理。
关于无解判断:可以往权值中加入一个 \(-1\),若答案为 \(-1\) 则真实答案为无解。

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