1. 整数二分模板

整数二分是求红色范围的右端点 或者 绿色范围的左端点
红色区间最右侧的那个点即是:序列中最后一个满足某条件(红色条件)的元素
绿色区间最左侧的那个点即是:序列中第一个不满足某条件(红色条件)的元素或是第一个满足某条件(绿色条件)的元素
定义check条件。如果判断mid < target, 那么找到的是小于target但是最接近target的数,就是小于target的最大数。如果判断mid >= target, 那么找到的是大于或者等于target的最接近target的数,就是target或者比target大的最小数。
cpp
模板:
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;
}
cpp
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int arr[N];
int main()
{
int n, q;
scanf("%d%d", &n, &q);
for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
while (q--)
{
int l = 0, r = n - 1, x, mid;
scanf("%d", &x);
// 1 2 2 3 3 4 找第一个3即右区间最左端点
while (l < r)
{
mid = l + r >> 1;
if (arr[mid] >= x) r = mid;
else l = mid + 1;
}
if (arr[l] != x) // 若找不到
cout << "-1 -1" << endl;
else // 找左区间的最右端点
{
cout << l << " " ;
l = 0, r = n - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (arr[mid] <= x) l = mid;
else r = mid - 1;
}
cout << r << endl;
}
}
return 0;
}
2. 浮点数二分模板
cpp
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
A. 方程求根类(最直观)
题 1:求解方程根(单调)
给定实数 a,ba,ba,b(保证存在唯一解),求方程x3+x=ax^3 + x = ax3+x=a的解,输出保留 6 位小数。
check 设计 :x^3 + x >= a(左边随 x 严格递增,单调)。
题 2:对数/指数方程
给定 a>0a>0a>0,求满足
ln(1+x)=a \ln(1+x)=a ln(1+x)=a
的 xxx(唯一解),保留 6 位小数。
check :log(1+x) >= a(单调递增)。
区间 :l=0, r=...(可取大一点,例如 r=1e6)
B. "最小值/最大值 + 可行性"类(最常见的二分答案)
题 3:最小时间完成任务(机器生产)
有 nnn 台机器,第 iii 台每生产 1 件需要 tit_iti 秒。问最少多少秒能生产至少 MMM 件?输出保留 6 位小数(允许机器连续生产,时间是实数)。
check(T) :计算 ∑⌊T/ti⌋≥M\sum \lfloor T/t_i \rfloor \ge M∑⌊T/ti⌋≥M 是否成立。
- 单调性:T 越大能生产越多,更容易满足(false→true)。
这是经典"最小时间"模型,整数/浮点都能做,若题目允许连续或要求小数,就用浮点二分。
题 4:最小加油速度(到达不晚)
你要在 DDD 公里内赶到,最晚时间 TTT 小时。车速 vvv 可取实数,问最小 vvv 使得 D/v≤TD/v \le TD/v≤T。
check(v) :D / v <= T(v 越大越容易满足)。
C. 几何覆盖/距离类(半径、距离、阈值)
题 5:最小半径覆盖所有点(圆覆盖)
给定平面上 nnn 个点,和一个固定圆心 CCC。求最小半径 RRR,使得圆心为 CCC 的圆覆盖所有点。
check® :判断所有点到 CCC 的距离是否都 ≤R\le R≤R。
- 单调性:R 越大越容易覆盖。
(这题其实能直接算最大距离,但它是让你练"浮点二分 + 单调覆盖"的直觉。)
题 6:一维点用 k 个区间覆盖(最小区间长度)
给定数轴上 nnn 个点,允许用 kkk 个长度为 LLL 的闭区间覆盖所有点。求最小 LLL。
check(L) :贪心从左到右放区间,看能否用 ≤k\le k≤k 个覆盖完。
- 单调性:L 越大越容易覆盖。
D. 最大化某个"比值/平均值"的经典套路
题 7:最大平均值子段(或选 k 个物品的最大平均)
给定数组 a1..ana_1..a_na1..an,求长度至少为 mmm 的连续子段最大平均值,输出 6 位小数。
二分答案 avg :
把每个数变成 bi=ai−avgb_i = a_i - avgbi=ai−avg,问是否存在长度 ≥ m 的子段使得 ∑bi≥0\sum b_i \ge 0∑bi≥0。
check(avg):用前缀和 + 最小前缀判定是否存在这样的子段。
- 单调性:avg 越大越难达到(true→false 或反过来,取决于你写的 check)。
这是"二分答案 + 判定"的代表题型之一,含金量很高。
题 8:最大化单位成本收益(分数规划)
有 n 个项目,第 i 个项目收益 pip_ipi、成本 cic_ici,你必须选恰好 k 个项目,最大化 ∑p∑c\frac{\sum p}{\sum c}∑c∑p。
二分 ratio :
令 bi=pi−ratio⋅cib_i = p_i - ratio \cdot c_ibi=pi−ratio⋅ci,问是否能选 k 个使得 ∑bi≥0\sum b_i \ge 0∑bi≥0。
check(ratio) :把所有 bib_ibi 排序取最大的 k 个看和是否 ≥ 0。
- 单调性:ratio 越大越难满足。
E. 物理/概率/连续过程类
题 9:混合浓度(最小倒入量达到目标浓度)
有两种溶液浓度 c1,c2c_1,c_2c1,c2,你要混合成浓度至少为 CCC,其中第一种倒入量为 xxx(实数),总量固定为 VVV。求最小 xxx。
check(x):混合浓度是否 ≥ C(关于 x 线性单调)。
你做题时的"识别口诀"
看到这些特征就该想到浮点二分:
- 答案是实数(要保留若干位小数 / 允许误差)
- 能写
check(x):给定 x 判断"可不可以/行不行/是否满足不等式" check随 x 单调(只翻一次)