一种手打二分模板的推荐

你是否还为手打二分的几个细节苦恼?不知道该用哪种?

  • 到底是l < r还是l<=r
  • 到底是mid=(l+r)/2还是mid=(l+r)>>1还是mid=(l+r+1)>>1
  • 到底是l=mid+1还是l=mid
  • 到底是r=mid-1还是r=mid
  • 到底是ans=l?还是l+1?亦或是l-1
  • 初始边界l=0还是1r=n还是n+1
  • \(\cdots\) \(\cdots\)

我可以提供一个范本:

//sample 1
while(l < r){
    mid = (l + r) >> 1;
    if(check(mid)) l = mid + 1;
    else r = mid;
}
//sample 2
while(l < r){
    mid = (l + r) >> 1;
    if(check(mid)) r = mid;
    else l = mid + 1;
}

考虑到这样进行左右区间划分与线段树相同,习惯上较为有利,我总结出了默认使用这种写法的情况下一些细节设置:

关键点:考察check函数返回值序列与我们所求答案位置的关系。

\(1.\)check函数返回为真时,收缩至左区间(r=mid),则check函数返回值序列形如0,0,...,0,1,1,...,1,终止条件l==r将指向第一个1的位置。

  • 如果答案位置为第一个1的出现位置,则ans=l
  • 如果答案位置为最后一个0的出现位置,则ans=l-1

\(2.\)check函数返回为真时,收缩至右区间(l=mid+1),则check函数返回值序列形如1,1,...,1,0,0,...,0,终止条件l==r将指向第一个0的位置。

  • 如果答案位置为第一个0的出现位置,则ans=l
  • 如果答案位置为最后一个1的出现位置,则ans=l-1

二分的初始边界应该设置为 \([1,n)\),即l=1,r=n+1

posted @ 2025-08-20 21:10  4BboIkm7h  阅读(4)  评论(0)    收藏  举报