二分小专题

P1102 A-B 数对

P1102 A-B 数对
暴力枚举还是很好做的,直接上双层循环OK
二分思路:查找边界情况,找出最大下标和最小下标,两者相减+1即为答案所求
废话不多说,上代码

//暴力O(n^3) 72pts
// #include<bits/stdc++.h>
// using namespace std;
// const int N = 1100;
// int a[N],b[N],c[N];

// int main()
// {
//     int n;cin>>n;
//     int cnt = 0;
//     for(int i = 1;i <= n;i++)cin>>a[i];
//     for(int i = 1;i <= n;i++)cin>>b[i];
//     for(int i = 1;i <= n;i++)cin>>c[i];
//     for(int i = 1;i <= n;i++)
//     {
//         for(int j = 1;j <= n;j++)
//         {
//             for(int z = 1;z <= n;z++)
//             {
//                 if(a[i] < b[j] && b[j] < c[z] ) cnt++;
//             }
//         }
//     }
//     cout<<cnt<<"\n";
//     return 0;
// }

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
using ll = long long;
ll a[N],b[N],c[N];
ll n;
int bs1(int x)
{
    ll l = -1,r = n+1;
    while(l+1 != r)
    {
        ll mid = (l+r)>>1;
        if(a[mid] < x)
        {
            l = mid;
        }else 
        {
            r = mid;
        }
    }
    if(a[l] < x) return l;
    return 0;
}
int bs2(int x)
{
    ll l = -1,r = n+1;
    while(l+1 != r)
    {
        ll mid = (l+r)>>1;
        if(c[mid] <= x)
        {
            l = mid;
        }else 
        {
            r = mid;
        }
    }
    if(c[r] > x) return n-r+1;
    return 0;
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    ll cnt = 0;
    for(int i = 1;i <= n;i++)cin>>a[i];
    for(int i = 1;i <= n;i++)cin>>b[i];
    for(int i = 1;i <= n;i++)cin>>c[i];
    //因为要二分a[i]和c[i],所以我们需要对其进行排序操作

    sort(a+1,a+1+n);
    sort(c+1,c+1+n);
    for(int i = 1;i <= n;i++)
    {
        ll x = bs1(b[i]);
        ll y = bs2(b[i]);
        cnt += x * y;
    }
    cout<<cnt<<"\n";
    return 0;
}


P8667 [蓝桥杯 2018 省 B] 递增三元组

P8667 [蓝桥杯 2018 省 B] 递增三元组
一开始也是暴力做法,三层for循环拿到手
二分思想:观察这个式子我们可以看出,b[i] 介于a[i] 和 c[i] 之间,可以选择枚举b[i],再套用二分查找模板查找a[i]中小于b[i]的部分,还有c[i]中大于b[i]的部分
注意:该l,r的取值分别是 -1 和 n+1,因为你的c[i]最终要返回 n-r+1,所以你需要把指针定在n+1上,确保搜索范围的右边界在数组外。这样可以避免数组越界的问题
废话不多说,上代码

//暴力O(n^3) 72pts
// #include<bits/stdc++.h>
// using namespace std;
// const int N = 1100;
// int a[N],b[N],c[N];

// int main()
// {
//     int n;cin>>n;
//     int cnt = 0;
//     for(int i = 1;i <= n;i++)cin>>a[i];
//     for(int i = 1;i <= n;i++)cin>>b[i];
//     for(int i = 1;i <= n;i++)cin>>c[i];
//     for(int i = 1;i <= n;i++)
//     {
//         for(int j = 1;j <= n;j++)
//         {
//             for(int z = 1;z <= n;z++)
//             {
//                 if(a[i] < b[j] && b[j] < c[z] ) cnt++;
//             }
//         }
//     }
//     cout<<cnt<<"\n";
//     return 0;
// }

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
using ll = long long;
ll a[N],b[N],c[N];
ll n;
int bs1(int x)
{
    ll l = -1,r = n+1;
    while(l+1 != r)
    {
        ll mid = (l+r)>>1;
        if(a[mid] < x)
        {
            l = mid;
        }else 
        {
            r = mid;
        }
    }
    if(a[l] < x) return l;
    return 0;
}
int bs2(int x)
{
    ll l = -1,r = n+1;
    while(l+1 != r)
    {
        ll mid = (l+r)>>1;
        if(c[mid] <= x)
        {
            l = mid;
        }else 
        {
            r = mid;
        }
    }
    if(c[r] > x) return n-r+1;
    return 0;
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    ll cnt = 0;
    for(int i = 1;i <= n;i++)cin>>a[i];
    for(int i = 1;i <= n;i++)cin>>b[i];
    for(int i = 1;i <= n;i++)cin>>c[i];
    //因为要二分a[i]和c[i],所以我们需要对其进行排序操作

    sort(a+1,a+1+n);
    sort(c+1,c+1+n);
    for(int i = 1;i <= n;i++)
    {
        ll x = bs1(b[i]);
        ll y = bs2(b[i]);
        cnt += x * y;//每个 a[j] 可以与每个 c[z] 组合
    }
    cout<<cnt<<"\n";
    return 0;
}


P2440 木材加工

P2440 木材加工
很明显这道题就是二分答案,跟二分查找略显不同的是,它需要一个证明的过程判断结果的正确性
废话少说,上代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5+10;
ll a[N];
ll n,k;
bool check(int mid,int k)//长度为mid,段数为k
{
    int cnt = 0;
    for(int i = 1;i <= n;i++)
    {
        cnt += a[i] / mid;
    }
    if(cnt >= k)return true;
    else return false;
}
int main()
{
    cin>>n>>k;
    ll sum = 0;
    for(int i = 1;i <= n;i++)cin>>a[i];
    for(int i = 1;i <= n;i++)
    {
        sum += a[i];
    }
    if(sum < k)cout<<0<<"\n";
    else //需要二分之前先对木材进行排序
    {
        sort(a+1,a+1+n);
        ll l = -1,r = 1e8+10;
        while(l+1 != r)
        {
            ll mid = (l+r) >> 1;
            if(check(mid,k))
            {
                l = mid;
            }
            else r = mid;
        }
        cout<<l<<"\n";
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值