整数二分模板:
bool check(int x) {/* … */} // 检查x是否满足某种性质
找大于等于给定数的第一个位置:
区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
找小于等于给定数的最后一个位置:
区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
模板题:
acwing789. 数的范围
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, q;
int main()
{
cin >> n >> q;
for(int i = 0; i < n; i ++ ) cin >> a[i];
while(q -- )
{
int x;
cin >> x;
int l = 0, r = n - 1;
while(l < r)
{
int mid = l + r >> 1;
if(a[mid] >= x) r = mid;
else l = mid + 1;
}
if(a[l] != x) puts("-1 -1");
else
{
cout << l << ' ';
l = 0, r = n - 1;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(a[mid] <= x) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
}
return 0;
}
P1873砍树
老老实实的手写二分
这里要找的是最小的木材,这里选用第二个模板
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
LL l, r, mid;
int a[N];
int main()
{
LL m, n;
cin >> n >> m; //m为需要的木材长度
for(int i = 0; i < n; i ++ )
cin >> a[i];
sort(a, a + n);
l = a[0];
r = a[n - 1];
//因为锯子的取值只可能在所有树高度之间,所以在二分的时候范围从最低到最高就够了
while(l < r)
{
mid = l + r + 1 >> 1; //+1防止进入死循环,这里用mid代表暂时的锯子长度
LL sum = 0; //代表目前所砍树的总和
for(int i = 0; i < n; i ++ ) //将所满足条件的树木累加起来
if(a[i] > mid)
sum += a[i] - mid;
if(sum >= m) //若是总和>=需求量说明有可能砍多了,锯子要向上挪(答案在当前答案或当前答案的上方也就是数组的后面),更新左边界(若此时就是答案,那么左边界也包含了答案)
l = mid;
else
r = mid - 1; //若总和<需求量,说明一定不够,锯子要往下挪,更新右边界
}
cout << l << endl;
return 0;
}