二分模板 Acwing 789 数的范围
二分一定有解,若出现无解,一定是题目中无解二分步骤:定义check函数,先找到一个x,使得区间左边满足条件区间右边不满足条件,
定义mid = l + r >> 1去判断于x的关系,此时需要判断边界关系,例如当a[mid]小于x时,说明二分值在x的左边,此时缩小范围为【mid,r】,
即令 l = mid,此时返回check函数,使 mid = l + r +1 >> 1,因为在这里mid是向下取整,不加一的话,当 l = r - 1是 ,mid = l,此时循环后的区间
还是【l,r】,会形成死循环,所以 + 1后范围会变成 mid = r ,得到我们的x的第一次出现的值,在循环最后一次,我们的 l 和 r都在x 的两侧
此时 l = r = x,因此当无解时,判断最后一轮a[ l ] != x ,即为无解,此题要判断两次,所以再进行一次二分,判断x的结束的位置,以下为代码

1 #include<iostream> 2 using namespace std; 3 4 const int N = 100010; 5 int n,m; 6 int a[N]; 7 8 int main() 9 { 10 scanf("%d%d", &n, &m); 11 for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]); 12 13 while (m -- ) 14 { 15 int x,l = 0, r = n - 1; 16 scanf("%d", &x); 17 18 while(l < r) 19 { 20 int mid = l + r >> 1; 21 if(a[mid] >= x) r = mid; 22 else l = mid + 1; 23 } 24 if(a[l] != x) cout <<"-1 -1" << endl; 25 26 else 27 { 28 cout << l << ' '; 29 30 int l = 0,r = n - 1; 31 32 while(l < r) 33 { 34 int mid = l + r + 1>> 1; 35 if(a[mid] <= x) l = mid ; 36 else r = mid - 1; 37 } 38 cout << l << endl; 39 } 40 } 41 return 0; 42 }
例题:Problem - B - Codeforces 通过二分不断缩小区间取搜索满足条件的最小的天数

1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 5 const int N = 210,inf = 1e9; 6 int n,p,w,t; 7 int _; 8 9 signed main() 10 { 11 ios::sync_with_stdio(0); 12 cin.tie(0); 13 cout.tie(0); 14 cin >>_; 15 while(_ --) 16 { 17 cin >> n >> p >> w >> t; 18 int l = 0,r = n; 19 int ans = n; 20 //cout << n << p << w << t; 21 while(l <= r) 22 { 23 int mid = (l + r) / 2; 24 //cout << mid << " "; 25 int renwu = (n - 1) / 7 + 1; 26 int zong = 0; 27 if(mid * 2 >= renwu){ //当前天数*2大于任务总数 28 zong += renwu * t; //只用任务数×每个任务的分数 29 }else 30 {//否则只用当前区间内的数字*2*每个任务的分数 31 zong += mid * 2 * t; 32 } 33 zong += mid * w; //再加上这个二分区间每天上课的分数 34 if(zong >= p)//如果分数多了,区间向左边移动 35 { 36 ans = min(ans,mid); 37 r = mid - 1; 38 }//否则分数若不够,区间向右移 39 else {l = mid + 1;} 40 } 41 //cout << n <<" " << ans << " "; 42 cout << n - ans << endl; 43 } 44 return 0; 45 }
相关题目: Problem - D - Codeforces
k越大答案一定满足,要求k的最小值,具有单调性可以二分答案。我们每次可以维护x,y区间,那么它的可移动区间就是[x - k,y + k],最后和题目给出的每个
区间取并集,目的是使范围越小越好(精确),新的范围存为tx和ty,如果存在,更新范围,如果不存在,二分更新答案

1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 5 const int N = 3e5 + 10,inf = 1e9; 6 int n,m; 7 int _; 8 int a[N],b[N]; 9 10 int main() 11 { 12 scanf("%d", &_); 13 while(_ --) 14 { 15 int n; 16 scanf("%d", &n); 17 for (int i = 1; i <= n; i ++ ) scanf("%d%d", &a[i], &b[i]); 18 19 int l = 0,r = 1e9; 20 21 while(l < r) 22 { 23 int mid = l + r >> 1; 24 int ok = 1; 25 int x = 0,y = 0; 26 for (int i = 1; i <= n; i ++ ) 27 { 28 int tx = max(x - mid,a[i]), ty = min(y + mid,b[i]); 29 if(tx <= ty) x = tx,y = ty; 30 else { 31 ok = 0; 32 break; 33 } 34 } 35 if(ok) r = mid; 36 else l = mid + 1; 37 } 38 printf("%d\n", l); 39 } 40 return 0; 41 }
3.Problem - 1692E - Codeforces 这道题要求使数组和为s的最小操作次数,我们可以转换为求一段和为s的最长子序列,长度最长即为操作次数最短

1 #include<bits/stdc++.h> 2 using namespace std; 3 #define endl '\n' 4 #define int long long 5 #define cy cout << "YES" << endl 6 #define cn cout << "NO" << endl 7 #define c1 cout << -1 << endl 8 9 int _,n,k; 10 const int N = 2e5 + 10,inf = 1e9; 11 const int mod = 1e9 + 7; 12 int s[N],a[N]; 13 vector<int> g[N]; 14 15 signed main() 16 { 17 cin >> _; 18 while(_ -- ){ 19 cin >> n >> k; 20 int l,r; 21 int p = -inf; 22 for (int i = 1; i <= n; i ++ ) cin >> a[i],s[i] = s[i - 1] + a[i]; 23 if(s[n] < k) {cout << -1 << endl;continue;} 24 25 for (int i = 1; i <= n; i ++ ){ 26 l = i,r = n; 27 int ans = 0; 28 while(l < r){ 29 int mid = l + r + 1 >> 1; 30 if(s[mid] - s[i - 1] <= k) l = mid; //选取一段和为s的最长的子序列可以使得操作次数最少 31 else r = mid - 1; 32 } 33 if(!l) continue; 34 ans = l - i + 1; 35 p = max(p,ans); //不断更新和为s的序列最长长度 36 } 37 if(p == -inf) c1; 38 else cout << n - p << endl; 39 } 40 return 0; 41 }