二分(洛谷题单总结)

换了二分板子

以前的板子
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 银行贷款 - 洛谷

本题需要财经知识

推导过程

image

#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 一天把二分重新梳理了一遍,换了一个二分的板子,感觉比之前的好写多了。

posted @ 2025-11-08 21:04  Seren_blingbling  阅读(47)  评论(0)    收藏  举报