【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;
}

浙公网安备 33010602011771号