二分模板
前提:有单调性一定可以二分。二分不一定有单调性,即无单调性也可以二分。
- 寻找左边界(符合性质的第一个位置):
- 每次二分为 [l, mid] 和 [mid+1, r] 。
while(l<r){
int mid=l+r >> 1; //+优先级大于>>
if(check(mid)) r=mid;
else l=mid+1;
}
- 寻找右边界(符合性质的最后一个位置):
- 每次二分为 [l, mid-1] 和 [mid, r]。
while(l<r){
int mid=l+r+1 >> 1;
if(check(mid)) l=mid;
else r=mid-1;
}
- 取含答案的区间 [l, r]。
- 确定check函数,确保check(mid)==true时更新操作为r=mid / l=mid。
- 判断在check函数返回true/false情况下,应当如何更新区间[l,r]。
- check(mid)==true的情况下:
- ①l=mid。mid更新规则为mid=l+r+1 >> 1;
- ②r=mid。mid更新规则为mid=l+r >> 1;
- 结果:保证了while结束时l==r。
- AcWing 二分模板
题目:
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;
int q[N];
int main(){
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
for(int i=0;i<m;i++){
int x;
scanf("%d",&x);
//二分查找x的区间左端点
int l=0,r=n-1; //确定区间范围
while(l<r){
int mid=l+r >> 1;
if(q[mid]>=x) r=mid; //找大于等于x的第一个位置,r等于这个位置
else l=mid+1;
}
if(q[r]==x){
cout << r << ' ';
r=n-1;
while(l<r){
int mid=l+r+1 >> 1;
if(q[mid]<=x) l=mid; //往右侧减半,找小于等于x的最后一个数,l等于这个位置
else r=mid-1;
}
cout << l << endl;
}else cout << "-1 -1" << endl;
}
return 0;
}
lower_bound() 与 upper_bound()解法
- lower_bound():通过二分查找,在指定区域内查找>=目标值的第一个元素。
- upper_bound():通过二分查找,在指定范围内查找>目标值的第一个元素。
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int n,m;
int q[N];
void solve(int x) {
int l = lower_bound(q, q+n, x) - q; //>=x的第一个元素的下标
int r = upper_bound(q, q+n, x) - q; //>x的第一个元素的下标
if(q[l] == x) printf("%d %d\n", l,r-1);
else printf("-1 -1\n");
}
int main(){
int x;
cin >> n >> m;
for(int i = 0; i < n; i ++) {
cin >> q[i];
}
while(m --) {
cin >> x;
solve(x);
}
return 0;
}
END