二分刷题记录(洛谷题单)区间的甄别

前提:

二分条件:
1、有范围
2、区间
3、能判某个点是否满足条件,并能缩小边界
4、单调

板子

需要注意取值

例题: 【深基13.例1】查找

最大值最小

		while(l < r){
            ll mid = (l + r + 1) >> 1;
            if(a[mid] < t)
                l = mid;
            else
                r = mid - 1;
        }

最小值最大

		while(l <= r){
            ll mid = (l + r) >> 1;
            if(a[mid] < t)
                l = mid + 1;
            else
                r = mid - 1;
        }

A-B 数对

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long

const int maxn = 1e6 + 5;

ll a[maxn];
map<ll, ll> num;

int main(){
    ll n, m;
    scanf("%lld %lld",&n, &m);
    int cnt = 0;
    for (int i = 1; i <= n; ++i){
        ll t;
        scanf("%lld", &t);
        if(!num[t])
            a[++cnt] = t;
        ++num[t];
    }
    sort(a + 1, a + cnt + 1);
    ll ans = 0;
    for (int i = 1; i <= cnt; ++i){
        ll l = 1, r = cnt;
        ll goal = a[i] + m;
        while(l < r){
            ll mid = (l + r) >> 1;
            if(a[mid] < goal)
                l = mid + 1;
            else
                r = mid;
        }
        if(a[l] != goal) continue;
        ans += 1ll * num[a[i]] * num[a[l]];
    }
    cout << ans << endl;
    return 0;
}

P1024 [NOIP2001 提高组] 一元三次方程求解

我居然连想法都错了
首先给定的答案是小数,所以可以推断答案是处在[-1, 1]中,而不是[-100,100],遍历区间再二分

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1e6 + 5;
double a, b, c, d;
inline double ge(double i){
    return a * i * i * i + b * i * i + c * i + d;
}

int main(){
    cin >> a >> b >> c >> d;
    int cnt = 0;
    for (int i = -100; i <= 100; ++i){
        double templ = ge(i*1.0);
        double tempr = ge(i + 1.0);
        if(templ == 0) {
            printf("%.2f ", (double)i);
            ++cnt;
            continue;
        }
        if(templ * tempr < 0){
            double l = i, r = i + 1.0;
            while(r - l >= 1e-3){
                double mid = (l + r) / 2.0;
                if(ge(l) * ge(mid) <= 0){
                    r = mid;
                }
                else
                    l = mid;
            }
                printf("%.2f ", l);
                ++cnt;
        }
        if(cnt >= 3)
            break;
    }
        return 0;
}

P1678 烦恼的高考志愿

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<climits>
using namespace std;
const int maxn = 1e6 + 5;
#define ll long long
ll n, m;
ll a[maxn], b[maxn];

int main(){
    fill(b, b + maxn, LLONG_MAX);
    cin >> m >> n;
    for (int i = 1; i <= m; ++i){
        cin >> b[i];
    }
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
    }
    sort(b + 1, b + m + 1);
    ll ans = 0;
    for (int i = 1; i <= n; ++i){
        ll l = 1, r = m;
        while(l <= r){
            ll mid = (l + r) >> 1;
            if(b[mid] < a[i])
                l = mid + 1;
            else
                r = mid - 1;
        }
        ans += min(abs(b[l - 1] - a[i]),min(abs(b[l] - a[i]), abs(b[l + 1] - a[i])));
    }
    cout << ans;
    return 0;
}

P1163 银行贷款

有公式,还可以用倍增,复杂度与二分相同
在这里插入图片描述

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<climits>
using namespace std;
const int maxn = 1e6 + 5;
#define ll long long

double a, b, c;
double gett(double mid){
    double temp = 0;
    double te = (1.0 + mid);
    for (int i = 0; i < c; ++i){
        temp += 1.0 * b / te;
        te *= (1.0 + mid);
    }
    return temp;
}

int main(){
    cin >> a >> b >> c;
    double l = 0, r = a;
    while(r - l >= 0.0001){
        double mid = (l + r) / 2.0;
        double temp = gett(mid);
        if(temp >= a){
            l = mid;
        }
        else if(temp < a)
            r = mid;
    }
    l = l * 100.0;
    printf("%.1f", l);
    return 0;
}

P1873 [COCI 2011/2012 #5] EKO / 砍树

做过,当时一次过,现在wa一发
这题因为是最大中的最小,所以用了(l <= r),(l < r) 会wa

ll n, m;
ll a[maxn], pre[maxn];
bool check(ll mid){
    ll sum = 0;
    for (int i = 1; i <= n; ++i){
        if(a[i] - mid > 0)
            sum += (a[i] - mid);
    }
    if(sum >= m) return true;
    return false;
}

int main() {
	IOS;
    cin >> n >> m;
    ll r = -1, l = INT_MAX;
    for(int i = 1; i <= n; ++i){
        cin >> a[i];
        r = max(r, a[i]);
        l = min(l, a[i]);
    }
    while (l <= r){
        ll mid = (l + r) / 2;
        if (check(mid))
            l = mid + 1;
        else
            r = mid - 1;
    }
    cout << r;
    return 0;
}

P2440 木材加工

最大值最小化

ll n, m;
ll a[maxn];
bool check(ll mid){
    ll sum = 0;
    for (int i = 1; i <= n; ++i){
            sum += a[i] / mid;
    }
    if(sum >= m) return true;
    return false;
}

int main() {
	IOS;
	// freopen("P1908_6.in","r",stdin);//读入数据
	// freopen("P1908.out","w",stdout); //输出数据
    cin >> n >> m;
    ll r = -1, l = 0;
    for(int i = 1; i <= n; ++i){
        cin >> a[i];
        r = max(r, a[i]);
    }
    while (l <= r){
        ll mid = (l + r) / 2;
        if(mid == 0){
            r = 0;
            break;
        }
        if (check(mid))
            l = mid + 1;
        else
            r = mid - 1;
    }
    cout << r;
    return 0;
}

P2678 [NOIP2015 提高组] 跳石头

最小值最大,二分能够得到的长度,而不是石块,判断这个石头用不用移动,记录下移动块数是否超过题目给定

ll n, m, L;
ll a[maxn];
bool check(ll mid){
    ll sum = 0, cnt = 0;
    ll pre = 0;
    bool flag = true;
    for (int i = 1; i <= n; ++i){
        if(a[i] - pre < mid){
            ++cnt;
            flag = false;
        }
        if(!flag){
            flag = true;
        }
        else {
            pre = a[i];
        }
        if(cnt > m)
            return false;
    }
    if(L - pre < mid)
        ++cnt;
    if(cnt <= m) return true;
    return false;
}

int main() {
	IOS;
	// freopen("P1908_6.in","r",stdin);//读入数据
	// freopen("P1908.out","w",stdout); //输出数据
    cin >> L >> n >> m;
    ll l = 0, r = LLONG_MAX;
    for(int i = 1; i <= n; ++i){
        cin >> a[i];
    }
    sort(a + 1, a + n + 1);
    ll ans = 0;
    while(l <= r){
        ll mid = (l + r) / 2;
        if(check(mid)){
            //ans = mid;
            l = mid + 1;
        }
        else
            r = mid - 1;
    }
    //cout << ans;
    cout << r;
    return 0;
}

P1182 数列分段 Section II

ll n, m;
ll a[maxn];
bool check(ll mid){
    ll cnt = 1, temp = 0;
    for (int i = 1; i <= n; ++i){
        if(temp + a[i] <= mid)
            temp += a[i];
        else {
            temp = a[i];
            ++cnt;
        }
    }
    if(cnt > m)
        return true;
    return false;
}

int main() {
	IOS;
	// freopen("P1908_6.in","r",stdin);//读入数据
	// freopen("P1908.out","w",stdout); //输出数据
    cin >> n >> m;
    ll l = 0, r = 0;
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
        r += a[i];
        l = max(l, a[i]);
    }
    while(l < r){
        ll mid = (l + r) / 2;
        if(check(mid)) {
            l = mid + 1;
        }
        else
            r = mid;
    }
    cout << l;
    return 0;
}

P3853 [TJOI2007]路标设置

wa疯了,因为刚好除尽需要减一

ll n, m, L, k;
ll a[maxn];
bool check(ll mid){
    ll cnt = 0;
    for (int i = 2; i <= n; ++i){
        cnt += (a[i] - a[i - 1]) / mid + ((a[i] - a[i - 1]) % mid == 0 ? -1 : 0);
    }
    if(cnt > k)
        return true;
    return false;
}

int main() {
	IOS;
	// freopen("P1908_6.in","r",stdin);//读入数据
	// freopen("P1908.out","w",stdout); //输出数据
    cin >> L >> n >> k;
    a[0] = 0;
    ll l = 1, r = 0;
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
        if(i > 1)
        r = max(r, a[i] - a[i - 1]);
    }
    while (l < r){
        ll mid = (l + r) / 2;
        if (check(mid)){
            l = mid + 1;
        }
        else
            r = mid;
    }
    cout << l;
    return 0;
}

P3743 kotori的设备

需要搞清楚设备能量和充能力的关系

ll n, m, L, k;
pair<ll, ll> a[maxn];

bool check(double mid){
    double sum = 0, temp = mid * k;
    for (int i = 1; i <= n; ++i){
        if(mid * a[i].first > a[i].second)
            sum += 1.0 * (mid * a[i].first - a[i].second);
    }
    return temp >= sum;
}

int main() {
	IOS;
	// freopen("P1908_6.in","r",stdin);//读入数据
	// freopen("P1908.out","w",stdout); //输出数据
    cin >> n >> k;
    double l = 0, r = 1e10 + 5.0;
    ll sum = 0;
    for (int i = 1; i <= n; ++i){
        cin >> a[i].first >> a[i].second;
        sum+=a[i].first;
    }
    if(sum <= k) {
        cout << -1;
        return 0;
    }
    //能使用多久
    while (r - l >= 0.00001){
        double mid = (l + r) / 2;
        if (check(mid)){
            l = mid;
        }
        else
            r = mid;
    }
    cout << l;
    return 0;
}

推荐博客

https://blog.csdn.net/Mr_dimple/article/details/114656142

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值