二分(洛谷题单总结)
换了二分板子
以前的板子
int l = 0, r = n - 1;
while(l<r){
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
int l = 0, r = n - 1;
while(l<r){
int mid = l + r >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
整数型
//左侧
int l=0,r=n+1;
while(l+1<r){
int mid=l+r>>1;
if(a[mid]<=q) l=mid;//找最大值
else r=mid;
}
return l
//右侧
int l=0,r=n+1;
while(l+1<r){
int mid=l+r>>1;
if(a[mid]>=q) r=mid;//找最小值
else l=mid;
}
return r;
[!tip]
左边,始终保证l在可行区域
右边,始终保证r在可行区域
注意:这里区间使用的是开区间!!!
浮点型
double l=-100,r=100;
while(r-l>1e-5){
double mid=(l+r)/1;
if(mid*mid*mid<=y) l=mid;
else r=mid;
}
return l;
[!tip] 输出几位小数可以这样使用
cout << fixed << setprecision(6) << x << endl; // 123.456789(小数点后6位)
洛谷官方题单【算法1-6】二分查找与二分答案
P2249 【深基13.例1】查找 - 洛谷(板子题)
1.普通板子
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=1e6+10;
int a[N];
int n, m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n;i++){
cin >> a[i];
}
while(m--){
int q;
cin >> q;
int l = 0, r = n + 1;
while (l + 1 < r)
{
int mid = l + r >> 1;
if (a[mid] >= q)
r = mid;
else
l = mid;
}
cout<< (a[r] == q ? r : -1)<<" ";
}
}
2.stl
[!tip] 二分
q.lower_bound(x); //返回容器中第一个大于等于x的数的迭代器
q.upper_bound(x); //返回容器中第一个大于x的数的迭代器
对于函数的话,则是返回地址,所以答案要减去地址
lower_bound(a,a+n,x)-a //下标从0开始
lower_bound(a+1,a+n+1,x)-a //下标从1开始
lower_bound就能取得最小的a数组的下标i,满足ai⩾x。
upper_bound(a,a+n,x)-a //下标从0开始
upper_bound(a+1,a+n+1,x)-a //下标从1开始
upper_bound就能取得最小的a数组的下标i,满足ai>x。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=1e6+10;
int a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n;i++){
cin >> a[i];
}
while(m--){
int q;
cin >> q;
int ans = lower_bound(a + 1, a + n + 1, q) - a;
if(a[ans]!=q)
cout << -1 << " ";
else
cout << ans << " ";
}
}
P1102 A-B 数对 - 洛谷
upper_bound和lower_bound使用学习
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=2e5+10;
LL a[N],ans;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, c;
cin >> n >> c;
for (int i = 0; i < n;i++){
cin >> a[i];
}
sort(a, a + n);
for (int i = 0; i < n;i++){
ans += (upper_bound(a, a + n, a[i] + c) - lower_bound(a, a + n, a[i] + c));
}
cout << ans << endl;
}
P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷
考虑一点,高度越高,得到木材越少
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
const int N=1e6+10;
int a[N];
int n, m, mx;
bool check(int x){
int h = 0;
for (int i = 0; i < n;i++){
if(a[i]>x)
h += a[i] - x;
}
return h >= m;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n;i++){
cin >> a[i];
mx = max(mx, a[i]);
}
int l = 0, r = mx + 1;
while(l+1<r){
int mid = l + r >> 1;
if(check(mid))
l = mid;
else
r = mid;
}
cout << l << endl;
}
P1024 [NOIP 2001 提高组] 一元三次方程求解 - 洛谷
[!tip] 浮点数求解
cout << fixed << setprecision(2);//固定输出为两位小数
要注意精度问题
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=2e5+10;
double a, b, c, d;
double f(double x){//用double注意
return a * x * x * x + b * x * x + c * x + d;
}
double find(double l,double r){
while(r-l>0.0001){//开小点
double mid = (l + r) / 2;
if(f(mid)*f(r)<0)
l = mid;
else
r = mid;
}
return l;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout << fixed << setprecision(2);
cin >> a >> b >> c >> d;
for (int i = -100; i <= 100;i++){
double y1 = f(i), y2 = f(i + 1);
if(!y1)
cout << i*1.0 << " ";//注意这里*0.1,因为i是整数
if(y1*y2<0){
cout << find(i, i + 1)<<" ";
}
}
}
P1678 烦恼的高考志愿 - 洛谷
[!warning] 注意
一个是特判,特判左右端点
还有是开LL!!!
#include <bits/stdc++.h>
using namespace std;
#define int long long//开LL啊!!!
const int mod = 998244353;
const int N=1e5+10;
int a[N];
int n, m;
int find(int x){
int l = -1, r = n;
while(l+1<r){
int mid = l + r >> 1;
if(a[mid]<=x)
l = mid;
else
r = mid;
}
return l;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n;i++){
cin >> a[i];
}
sort(a, a + n);
int ans = 0;
for (int i = 0; i < m;i++){
int x;
cin >> x;
int l = find(x);
//特判
if(l==-1)
ans += a[0] - x;
else if(l==n-1)//所有元素<=x时,l移动到 n-1的位置
ans += x - a[n - 1];
else
ans += min(abs(a[l] - x), abs(a[l + 1] - x));
}
cout<<ans<<endl;
}
P2440 木材加工 - 洛谷
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=1e5+10;
int n, k, a[N];
bool check(int x){
LL y = 0;
for (int i = 0; i < n;i++){
y += a[i] / x;
}
return y >= k;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 0; i < n;i++){
cin >> a[i];
}
int l = 0, r = 1e8 + 10; // 长度
while(l+1<r){
int mid = l + r >> 1;
if(check(mid))
l = mid;
else
r = mid;
}
cout << l << endl;
}
P2678 [NOIP 2015 提高组] 跳石头 - 洛谷
[!hint]
二分找最短跳跃距离的最大值
用check判断是否满足小于等于删去m个石头
注意:要加上0和L两点,所以从a[1]开始记
即考虑a[0]=0,a[n+1]=L
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=50010;
int L, n, m;
int a[N];
bool check(int x){
int last = 0, cnt = 0;
for (int i = 1; i <= n+1;i++){//考虑a[0]=0和a[n+1]=L
if(a[i]-a[last]<x)
cnt++;
else
last = i;
}
return cnt <= m;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> L >> n >> m;
for (int i = 1; i <= n;i++){
cin >> a[i];
}
a[n+1] = L;
int l = 0, r =L+1;
while(l+1<r){
int mid = l + r >> 1;
if(check(mid))
l = mid;
else
r = mid;
}
cout << l << endl;
}
P3853 [TJOI2007] 路标设置 - 洛谷
[!warning]
二分板子里l,r初始值为开区间
[!hint]
增加路标不能只加上中间
反例:L=101,n=3,k=2,a={0,96,101},正确答案为 32,而该解法答案为 48。正确解法的方案是用两个新增的点来三等分 [0,96] 这个区间,把 96 这一段分割成 32,32,32。
所以二分空旷指数
求增加路标个数->(l-1)/q(下取整)
注意mid不能为0,不过我的二分不太受影响
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=1e5+10;
int L, n, k, p[N];
bool check(int x){
int res = 0;//新增路标数
for (int i = 1; i < n;i++){
res += (p[i + 1] - p[i] - 1) / x;
}
return res <= k;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> L >> n >> k;
for (int i = 1; i <= n;i++){
cin >> p[i];
}
int l = 0, r = L;//l,r开区间!!!
while(l+1<r){
int mid = l + r >> 1;
//虽然mid不能为0,不过l=0时,r最小是为2->mid=1
if(check(mid))
r = mid;
else
l = mid;
}
cout << r << endl;
}
P1182 数列分段 Section II - 洛谷
[!hint]
求最小值的话,r为可行区
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=2e5+10;
int n, m;
int a[N];
bool check(int x){
int cur = 0, ans = 1;//ans考虑最后一段
for (int i = 0; i < n;i++){
if(cur+a[i]>x){//这个分段好!
cur = 0;
ans++;
}
cur += a[i];
}
return ans <= m;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int l = 0, r = 0;
for (int i = 0; i < n;i++){
cin >> a[i];
l = max(l, a[i]);
r += a[i];
}
l -= 1, r += 1;
while(l+1<r){
int mid = l + r >> 1;
if(check(mid))
r = mid;
else
l = mid;
}
cout << r << endl;
}
P1163 银行贷款 - 洛谷
本题需要财经知识
推导过程

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=2e5+10;
double n, m, k, l, r;
bool check(double x){
return pow(1.0 / (1.0 + x), k) >= 1 - n / m * x;//推导的
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
l = 0, r = 10;
while(r-l>0.000001){
double mid = (l + r) / 2;
if(check(mid))
r = mid;
else
l = mid;
}
cout << fixed << setprecision(1) << r*100 << endl;
}
P3743 小鸟的设备 - 洛谷
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=2e5+10;
double n, p;
double a[N], b[N];
bool check(double x){
double cnt = 0.0;
for (int i = 0; i < n;i++){
double res = b[i] - a[i] * x;
if(res<0){
cnt += -res;//计算 x时,需要冲的电
}
}
return cnt <= x * p;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> p;
for (int i = 0; i < n;i++){
cin >> a[i] >> b[i];
}
double l = 0.0, r = 1e10;
while(r-l>0.000001){//只能开1e-6,再小点会TLE
double mid = (l + r) / 2;
if(check(mid))
l = mid;
else
r = mid;
}
if(r==1e10){
cout << -1 << endl;
}else{
cout << fixed<<setprecision(10)<<l << endl;
}
}
CF
Problem - D - Codeforces(1059div3)(交互)(二分)(1400)
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 998244353;
const int N=2e5+10;
int qry(int tag,int l,int r){
int x;
cout << tag << " " << l << " " << r << endl;
cin >> x;
return x;
}
void solve()
{
int n;
cin >> n;
int c = qry(2, 1, n) - qry(1, 1, n);
int l = 0, r = n + 1;
while(l+1<r){
int mid = l + r >> 1;
if(qry(1,1,mid)==qry(2,1,mid)){//找到刚好满足的l
l = mid;
}
else
r = mid;
}
cout << "! " << l + 1 << " " << l + c<< endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
while (T--)
{
solve();
}
}
碎碎念
2025.11.8 一天把二分重新梳理了一遍,换了一个二分的板子,感觉比之前的好写多了。

浙公网安备 33010602011771号