7665: 学习系列 —— 二分 —— 二分查找上界
[Creator : ]
Description
【问题引入】
很多时候,简单的二分查找并不能满足我们的要求。例如,我们要在下面的数据中查找最后一个 $5$ 的位置。
存储下标 1 2 3 4 5 6 7 8 9 数值 3 5 5 5 5 5 5 5 8如上所示,最后一个数字 $5$ 出现在第 $8$ 个位置。我们称这类问题为二分查找上界,可以用在二分查找,也可以用在二分答案。整个思路和标准二分查找一样。
【步骤 $1$】
我们需要从 $[1,9]$ 中找数据 $5$,因此 $l=1,\ r=9$。由于 $l<r$,我们可以使用二分来查找。这样 $m=(l+r+1)/2=(1+9+1)/2=5$。注意,这里和二分查找下界的不同。此时,$a[5]=5$,二分查找并没有完成,我们需要进一步缩小区域,看是否还有数据 $5$ 的存在。
根据二分的特性,$[1,4]$ 这个区间,肯定不会存在答案。答案只能在 $[5,9]$ 这个区间,注意 $a[5]$ 是可能的。
因此,我们使用 $m$ 的数据更新 $l$,即 $l=m$。也就是说,我们继续 $[l,r]$,即 $[5,9]$ 这个区间进行二分查找。
【步骤 $2$】
我们需要从 $[5,9]$ 中找数据 $5$,因此 $l=5,\ r=9$。由于 $l<r$,我们可以使用二分来查找。这样 $m=(l+r+1)/2=(5+9+1)/2=7$。此时,$a[7]=5$,二分查找并没有完成,我们需要进一步缩小区域,看是否还有数据 $5$ 的存在。
根据二分的特性,$[5,6]$ 这个区间,肯定不会存在答案。答案只能在 $[7,9]$ 这个区间,注意 $a[7]$ 是可能的。
因此,我们使用 $m$ 的数据更新 $l$,即 $l=m$。也就是说,我们继续 $[l,r]$,即 $[7,9]$ 这个区间进行二分查找。
【步骤 $3$】
我们需要从 $[7,9]$ 中找数据 $5$,因此 $l=7,\ r=9$。由于 $l<r$,我们可以使用二分来查找。这样 $m=(l+r+1)/2=(7+9+1)/2=8$。此时,$a[8]=5$,二分查找并没有完成,我们需要进一步缩小区域,看是否还有数据 $5$ 的存在。
根据二分的特性,$[7,7]$ 这个区间,肯定不会存在答案。答案只能在 $[8,9]$ 这个区间,注意 $a[8]$ 是可能的。
因此,我们使用 $m$ 的数据更新 $l$,即 $l=m$。也就是说,我们继续 $[l,r]$,即 $[8,9]$ 这个区间进行二分查找。
【步骤 $4$】
我们需要从 $[8,9]$ 中找数据 $5$,因此 $l=8,\ r=9$。由于 $l<r$,我们可以使用二分来查找。这样 $m=(l+r+1)/2=(8+9+1)/2=9$。此时,$a[9]=8>5$,根据二分的特性,$[9,9]$ 这个区间,肯定不会存在答案。因此答案一定在 $[8,8]$ 这个区间。
因此,我们使用 $m-1$ 的数据更新 $r$,即 $r=m-1$。也就是说,我们继续 $[l,r]$,即 $[8,8]$ 这个区间进行二分查找。
【步骤 $5$】
我们需要从 $[8,8]$ 中找数据 $5$,因此 $l=8,\ r=8$。由于 $l<r$ 不成立,二分查找结束。对应的答案为 $2$。也就是最后一个数字 $5$ 出现在第 $8$ 个位置。
Input
【伪代码】
如果 l<r 成立继续: 更新 m=(l+r+1)/2 以 a[m] 为界限判断条件是否成立 如果 a[m]<=x,说明答案在区间 [m,r] 如果 a[m]>x,说明答案在区间 [l,m-1] 返回答案 l
Output
【C++代码】
const int N=2e6+10; LL a[N]; //在 [l,r] 中找最后一个 x 的下标 //<=x max 即小于等于 x 的最大值 bool check2(LL m,LL x) { //以 a[m] 为界限判断二分是否成立 return a[m]<=x; } LL bsearch2(LL l, LL r, LL x) { while (l<r) { LL m=(l+r+1)/2; if (check1(m, x)) { l = m; } else { r = m-1; } } return l; }