Skip to content

算法Algorithm

批量二分

Parallel binary search · 离线批量二分 · 并行二分

把多个单调查询的二分中点按更新日期分桶,每轮从同一初态重放一次,以共享判定状态求各自首次达标时间。

形式陈述 ​

每个问题有自己的答案日期 ​

给定初始整数数组A[0..n),以及按日期1,…,M排列的点增量(iₜ,δₜ),其中δₜ≥0。Aₜ表示应用前t条更新后的数组,A₀为初态。每个查询j给半开区间[lⱼ,rⱼ)和阈值gⱼ,要求最小日期

tj=min{t∈{0,…,M}:∑i=ljrj−1At[i]≥gj},

集合为空时返回“不存在”。初始元素和阈值允许为负;保证单调的是此后每次增量非负。查询与全部更新在计算前已知。

各查询的判定Pⱼ(t)先假后真,因此可单独做二分查找。批量二分保留这些独立的分界区间,却把同一轮中点按日期分组,用一遍更新重放同时完成这一轮所有判定。[1, Solution与Implementation;2, PDF pp.6–12]

英文“parallel”在此指多条二分过程按轮一起推进;单线程程序就能执行,不承诺使用多个处理器,也不把它与并行机器模型中的有序查找混为一谈。

直觉

真正昂贵的是把状态恢复到某一天 ​

若每个查询单独二分,每试一个日期都从A₀重做前缀更新,就会反复建立相同状态。把一轮所有中点排序后,只需从日期0向前走:到日期t时,回答所有中点恰为t的查询,然后继续更新。

附件用树状数组维护点增量与区间和。日期是0,…,M的连续整数,可直接开M+1个桶,无须每轮排序所有中点。桶里存查询编号,而不是把查询内容复制M份。

虚拟边界不访问不存在的状态 ​

每个查询保存lo=-1、hi=M+1,约定虚拟P(-1)=假、P(M+1)=真。真实状态只定义在0,…,M。只要hi−lo>1,就取mid=floor((lo+hi)/2),它必在真实范围内;若P(mid)真则hi=mid,否则lo=mid。

对每条查询独立保持“不超过lo的真实日期全假,从hi起的真实日期全真”。单调性保证更新边界后仍成立;区间缩小保证终止。结束时hi是第一个真日期,若hi=M+1则所有真实日期全假,返回不存在。哨兵是边界约定,不需要真的生成第M+1天。

一轮的状态不变量 ​

每轮先从A₀重新线性建立树状数组。先处理日期0的桶,再依次应用第1、2、…条更新;应用第t条后处理日期t的桶。于是处理每个桶时,结构恰表示Aₜ,其中每条前缀更新已执行一次,之后的更新尚未执行。

同一桶里的查询只读取状态,不改变数组,所以处理顺序不影响彼此。算出的真假与分别重放至mid再判定完全一致;共享重放只节省重复工作,不改变任何一条二分的逻辑。

例子与边界

三轮找出不同的首次日期 ​

取A₀=[0,0,0,0],六条更新依次为(1,+2)、(3,+3)、(0,+1)、(2,+4)、(1,+0)、(3,+2)。查询0问[0,2)的和何时至少3;查询1问[2,4)何时至少5。初始边界都为(-1,7)。

轮次 查询0的中点、区间和、更新后边界 查询1的中点、区间和、更新后边界
1 t=3,和3,(-1,3) t=3,和3,(3,7)
2 t=1,和2,(1,3) t=5,和7,(3,5)
3 t=2,和2,(2,3) t=4,和7,(3,4)

答案分别为3和4。第二轮不是为两个查询各重放一次,而是在同一遍顺扫中先处理日期1的桶,再处理日期5的桶。

若增加查询2:“全数组和至少0”,答案是0,第三轮会真的检查日期0。再问[1,3)至少7、[0,1)至少2、空区间[2,2)至少1,答案均不存在。完整六查询的最终边界为lo=[2,3,−1,6,6,6]、hi=[3,4,0,7,7,7]。

日期零与空更新序列 ​

日期0的桶必须在第一条更新之前回答,否则可能把首次日期错记成0。M=0时初始间隔(-1,1)的中点就是0,一次判定便区分初态已满足和永不满足。空区间的和恒为零:阈值≤0时答案0,阈值>0时不存在。

n=0时只允许空区间,不能有带合法位置的点更新;仍能回答这些恒定谓词。q=0时无须任何二分轮次,输入校验仍需要读取给定数据。

离线并不能补回丢失的单调性 ​

单元素初态0,四条增量为+5、−5、0、0,阈值为3。真实谓词依次是假、真、假、假、假。第一轮mid=2得到假,二分将lo改成2,已经排除了真正首次满足的日期1,最终可能返回不存在。

因此本页接口直接拒绝负增量。某个允许负更新的特殊查询若还能另行证明P(t)单调,可以设计相应批量判定器;但“所有输入已知”本身不是这个证明。各查询的区间也固定不变,不能一边二分一边按前次结果偷偷改动l、r。

推论与应用

每轮建立和重放都要收费 ​

设q个查询。一次轮次建立初始树需O(n),清空桶并分配中点O(M+q),顺扫M条更新和至多q次区间判定需O((M+q)log(n+1)),还要计各操作自身的常数工作。区间长度M+2每轮至少近似减半,至多⌈log₂(M+2)⌉轮,故总上界为

O(1+[n+M+q+(M+q)log⁡(n+1)]log⁡(M+2)).

公式保留每轮n项,不能把“清空数据结构”当作免费。空间为O(1+n+M+q):初始数组、当前树、输入更新、边界及桶;每个未决查询在当前轮只进入一个桶。保存全部判定日志会另占O(q log(M+2))。

样例六查询运行三轮,附件每轮完整重放六条更新,共18次更新和18次谓词判定,重新建立初态数组共读取12个单元。可以只扫到该轮最大非空桶,但这只是实现优化,不能把未采用的优化拿来解释当前运行次数。

以上假设下标、数值和累积和均可装入常数个机器字。Python任意精度整数避免定宽溢出,却不能免除大数加减的位成本。附件 audit=True 每个日期重建朴素数组核对状态,是测试模式,超出该核心复杂度。

与其他离线方法的分工 ​

离线动态连通按边的生效寿命进入时间线段树,并借回滚在兄弟分支间恢复状态;这里每轮从相同初态向前重放,不维护分支历史。CDQ三维支配统计则把全部更新—查询贡献分配到唯一分治结点,允许正负权,不要求“随日期只会由假变真”。

在批量查询终结任务中,先证明每个查询的单调性,再交三轮桶记录与最终首次日期。若只有独立二分的中点而没有共享状态的重放不变量,就还没有完成本算法的核心工作。

参考资料
  1. Himanshu Jaju,Parallel Binary Search [tutorial],作者教程,Solution、Implementation、Pseudo Code:按中点分组并逐轮重放。本文对所选点增量/区间和接口独立核算初始化、查询数和边界日期成本。
  2. Alex Tung,M1842 Another RMQ Tutorial,HKOI,2018-04-28,原始讲义合集,物理第6–12页的离线批量二分;该例沿值域阈值排序,本页沿更新时间重放。
关系图谱6 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系

使用的工具