Unverified Commit c928456b authored by 雷蒻's avatar 雷蒻 Committed by GitHub
Browse files

Update parallel-binsearch.md

修改了部分表述,添加了整体二分的复杂度证明
parent 25211ffb
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -18,14 +18,16 @@

## 思路

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

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

需要注意的是,在整体二分过程中,若当前处理的值域为 $[l,r]$,则此时最终答案范围不在 $[l,r]$ 的询问已经被筛掉了。
需要注意的是,在整体二分过程中,若当前处理的值域为 $[l,r]$,则此时最终答案范围不在 $[l,r]$ 的询问会在其他时候处理。



## 详解

@@ -46,9 +48,9 @@

可以对于每个询问进行一次二分;但是,也可以把所有的询问放在一起二分。

先考虑二分的本质:假设要猜一个 $[l,r]$ 之间的数,猜测之后会知道是猜大了,猜小了还是刚好。当然可以从 $l$ 枚举到 $r$,但更优秀的方法是二分:猜测答案是$m = \lfloor\frac{l + r}{2}\rfloor$ ,然后去验证 $m$ 的正确性,再调整边界。
先考虑二分的本质:假设要猜一个 $[l,r]$ 之间的数,猜测之后会知道是猜大了,猜小了还是刚好。当然可以从 $l$ 枚举到 $r$,但更优秀的方法是二分:猜测答案是$m = \lfloor\frac{l + r}{2}\rfloor$ ,然后去验证 $m$ 的正确性,再调整边界。这样做每次询问的复杂度为 $O(n\log_2 n)$ ,若询问次数为 $q$ ,则时间复杂度为 $O(qn\log_2 n)$ 。

回过头来,对于当前的所有询问,可以去猜测所有询问的答案都是 $mid$,然后去依次验证每个询问的答案应该是小于等于 $mid$ 的还是大于 $mid$ 的,并将询问分为两个部分(不大于/大于),对于每个部分继续二分。注意:如果一个询问的答案是大于 $mid$ 的,则在将其划至右侧前需更新它的 $k$,即,如果当前数列中小于等于 $mid$ 的数有 $t$ 个,则将询问划分后实际是在右区间询问第 $k - t$ 小数。如果一个部分的 $l = r$ 了,则结束这个部分的二分。
回过头来,对于当前的所有询问,可以去猜测所有询问的答案都是 $mid$,然后去依次验证每个询问的答案应该是小于等于 $mid$ 的还是大于 $mid$ 的,并将询问分为两个部分(不大于/大于),对于每个部分继续二分。注意:如果一个询问的答案是大于 $mid$ 的,则在将其划至右侧前需更新它的 $k$,即,如果当前数列中小于等于 $mid$ 的数有 $t$ 个,则将询问划分后实际是在右区间询问第 $k - t$ 小数。如果一个部分的 $l = r$ 了,则结束这个部分的二分。利用线段树的相关知识,我们每次将整个答案可能在的区间 $[1,maxans]$ 划分成了若干个部分,这样的划分共进行了 $O(\log_2 maxans)$ 次,一次划分会将整个操作序列操作一次。若对整个序列进行操作,并支持对应的查询的时间复杂度为 $O(T)$ ,则整体二分的时间复杂度为 $O(T\log_2 n)$ 。

试试完成以下代码: