【vjudge训练记录】大一寒假专项训练——二分

训练情况

A题

找到数列中第一个等于 \(x\) 的位置,朴素做法就是数列全部遍历一遍去找,但时间复杂度过高会超时。但是题目保证数列是单调的,我们可以利用二分单调性去找,比如说当前找的数大于要找的数就去往小的找,小于当前要找的数就去往大的找,代码实现中直接使用 STL 库中的 lower_bound 函数找到第一个大于等于 \(x\) 的位置,再进行判断,代码时间复杂度 \(O(qlogn)\)

点击查看代码
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'

using namespace std;

void solve(){
    int n,q; cin>>n>>q;
    vector<int> a(n + 1);
    for(int i = 1;i<=n;i++) cin>>a[i];
    while(q--){
        int x; cin>>x;
        int pos = lower_bound(a.begin() + 1,a.end(),x) - a.begin();
        if(a[pos] == x) cout<<pos<<" ";
        else cout<<-1<<" ";
    }
}

signed main(){
    // int T; cin>>T; while(T--)
    solve();
    return 0;
}

B题

这题有个坑点是起点是 \(x=0\),终点是 \(x=d\)。同样我们观察到这题答案具有二分单调性,如果答案增大会导致删除的石头增多,答案减小会导致删除的石头减小,所以我们选择直接二分答案,去判断删除的石头是否在题目要求的范围内,如果在范围内就去往答案大的找,不在范围内就往答案小的找。二分判断时候我们需要记录上一个石头的位置,如果不满足则当前石头删掉再答案加一,如果满足更新上一个石头的位置,代码时间复杂度 \(O(nlogn)\)

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'

using namespace std;

const int N = 5e4 + 3;

int l,n,m;
int a[N];

bool pd(int x){
    int ans = 0;
    int last = 0;
    for(int i = 1;i<=n+1;i++){
        if(a[i] - last < x){
            ans++;
        } else {
            last = a[i];
        }
    }
    return ans <= m;
}

void solve(){
    cin>>l>>n>>m;
    for(int i = 1;i<=n;i++) cin>>a[i];
    a[n+1]=l;
    int left = 0,right = l,mid;
    while(left < right){
        mid = left + right + 1 >> 1;
        if(pd(mid)) left=mid;
        else right = mid - 1;
    }
    cout<<right<<endl;
}

signed main(){
    // int T; cin>>T; while(T--)
    solve();
    return 0;
}

C题

\(c\) 头牛,塞到 \(n\) 个格子里,显然会空 \(n-c\) 个格子,要让相邻两头的牛的距离尽可能大,我们考虑二分答案。我们观察到二分单调性,但是记住二分单调性的前提是位置要单调,所以我们要先排序,如果答案过大(即牛牛之间的距离过大)会导致空格子不够用,答案小了又无法全部利用全部空格子。所以我们直接二分这个答案,去判断空的格子是否够用,如果够用就往更大的答案找,不够用往更小的答案找,代码时间复杂度 \(O(nlogn)\)

点击查看代码
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'

using namespace std;

const int N = 1e5 + 3;

int n,c;
int a[N];

bool pd(int x){
    int ans = 0;
    int last = a[1];
    for(int i = 2;i<=n;i++){
        if(a[i] - last < x) ans++;
        else last = a[i];
    }
    return ans<=n-c;
}

void solve(){
    cin>>n>>c;
    int ma = 0;
    for(int i = 1;i<=n;i++) cin>>a[i],ma = max(ma,a[i]);
    sort(a+1,a+1+n);
    int l = 0,r = ma,m;
    while(l < r){
        m = l + r + 1 >> 1;
        if(pd(m)) l=m; 
        else r=m-1;
    }
    cout<<l<<endl;
}

signed main(){
    // int T; cin>>T; while(T--)
    solve();
    return 0;
}

D题

这是一题实数二分答案,需要设一个二分精度,当左右边界小于这个精度时退出循环,我们首先考虑一个简单的问题,给你一段长为 \(L\) 的绳子,能分成多少段长为 \(X\) 的绳子,显然答案是 \(\lfloor \frac{L}{X} \rfloor\),那我们这题可以观察到二分单调性,答案过大时会分不出这么多绳子要往答案更小的找,答案过小时会超过 \(k\) 条满足条件显然可以往答案更大的找,代码时间复杂度 \(O(nlogn)\)

点击查看代码
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'

using namespace std;

const double eps = 1e-5;

const int N = 1e4 + 3;

int n,k;
double a[N];

bool pd(double x){
    int ans = 0;
    for(int i = 1;i<=n;i++){
        ans += (int)(a[i]/x);
    }
    return ans>=k;
}

void solve(){
    cin>>n>>k;
    double ma = 0;
    for(int i = 1;i<=n;i++) cin>>a[i],ma = max(ma,a[i]);
    double l = 0,r = ma,m;
    while(l+eps<r){
        m = (l + r) / 2.0;
        if(pd(m)) l=m;
        else r=m;
    }
    int temp = r*100;
    double ans = temp;
    ans /= 100;
    cout<<fixed<<setprecision(2)<<ans<<endl;
}

signed main(){
    // int T; cin>>T; while(T--)
    solve();
    return 0;
}

E题

我们考虑二分答案,首先我们知道答案为 \(x\) 时,每件衣服的湿度都会 \(-ax\),那么对于剩下还没干的衣服就使用吹风机,使用次数为剩余湿度除以 \(b\) 向上取整,答案为 \(x\) 时显然可以使用 \(x\) 次吹风机,所以我们判断要使衣服全干的吹风机次数是否小于等于答案。代码时间复杂度 \(O(nlogn)\)

点击查看代码
#include <bits/stdc++.h>
// #define int long long
#define endl '\n'

using namespace std;

const int N = 5e5 + 3;

int n,a,b;
int w[N];

bool pd(int x){
    int ans = 0;
    for(int i = 1;i<=n;i++){
        if(w[i] - a*x>0){
            int d = w[i] - a*x;
            ans += ceil(1.0*d/b);
        }
    }
    return ans <= x;
}

void solve(){
    cin>>n>>a>>b;
    int ma = 0;
    for(int i = 1;i<=n;i++) cin>>w[i],ma=max(ma,w[i]);
    int l = 0,r = ma,m;
    while(l < r){
        m = l + r >> 1;
        if(pd(m)) r=m;
        else l=m+1;
    }
    cout<<l<<endl;
}

signed main(){
    // int T; cin>>T; while(T--)
    solve();
    return 0;
}

F题

我们考虑二分答案,对于分出来的正方形边长为 \(x\),在更大的长方形中显然能分出 \(\lfloor \frac{h_i}{x} \rfloor \times \lfloor \frac{w_i}{x} \rfloor\) 块,直接判断分出来的块数是否满足条件,如果满足条件去往更大的答案找,不满足条件往更小的答案找,代码时间复杂度 \(O(nlogn)\)

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'

using namespace std;

const int N = 1e5 + 3;

int n,k;
int h[N],w[N];

bool pd(int x){
    int ans = 0;
    for(int i = 1;i<=n;i++){
        ans += (h[i]/x)*(w[i]/x);
    }
    return ans>=k;
}

void solve(){
    cin>>n>>k;
    int ma = 0;
    for(int i = 1;i<=n;i++) cin>>h[i]>>w[i],ma = max(ma,h[i]),ma = max(ma,w[i]);
    int l = 1,r = ma,m;
    while(l<r){
        m = l + r + 1 >> 1;
        if(pd(m)) l=m;
        else r=m-1;
    }
    cout<<r<<endl;
}

signed main(){
    // int T; cin>>T; while(T--)
    solve();
    return 0;
}

G题

数列中的每一位都要去找 \(\ge 2 \times a_i\) 的个数,所以我们只需要在数列中找到第一个 \(\ge 2 \times a_i\) 的位置,代码实现中使用 STL 库中的 lower_bound 函数去找 \(\ge 2 \times a_i\) 的位置(如果找不到会返回最后一个位置),显然那个位置之后的年糕长度都能满足,对答案的贡献就是 \(n - pos + 1\),代码时间复杂度 \(O(nlogn)\)

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'

using namespace std;

void solve(){
    int n; cin>>n;
    vector<int> a(n + 1);
    for(int i = 1;i<=n;i++) cin>>a[i];
    int ans = 0;
    for(int i = 1;i<=n;i++){
        int l = lower_bound(a.begin() + i + 1,a.end(),2*a[i]) - a.begin();
        ans += max(0ll,n-l+1);
    }
    cout<<ans<<endl;
}

signed main(){
    // int T; cin>>T; while(T--)
    solve();
    return 0;
}
posted @ 2025-02-10 18:56  MNNUACM_2024ZY  阅读(72)  评论(0)    收藏  举报