二分法与倍增法
二分法与倍增法
二分法;倍增法。
概念
二分法在有序或单调的性质上,每次排除一半候选范围:找边界、判可行、求最大最小值都常用。写法上要分清「找第一个满足」与「找最后一个满足」,以及 mid 取 (l+r)/2 还是 (l+r+1)/2 避免死循环。倍增法用「2 的幂次步长」快速跳转:预处理倍增表、快速幂、或在大 n 上递推「上一轮结果」都是 CSP-J 常见形态。与 cspj.algo.sorting 区分:二分要求有序性来自题意或预处理,不是学排序本身;与 cspj.algo.recurrence-and-recursion 区分:倍增的递推常带「乘 2 跳步」结构,而不是相邻项线性组合。
CSP-J 常考
cspj-r1-2019第 5 题:单选题,二分或单调性相关判断。cspj-r1-2023第 33–37 题:完善程序整段,实现二分查找或边界判定。cspj-r1-2024第 9 题:单选题,考查二分适用条件或复杂度。cspj-r1-2021第 41–42 题:完善程序,与倍增或快速跳转相关的填空。cspj-r2-2023第 1 题:复赛编程,约瑟夫型「隔 2 取 1」,n 达 10^9 需倍增递推。
// 二分:找最小 x 使得 ok(x) 为真(x 在 [lo, hi])
while (lo < hi) {
int mid = (lo + hi) / 2;
if (ok(mid)) hi = mid;
else lo = mid + 1;
}
// 快速幂:倍增思想
long long pw(long long a, int e) {
long long r = 1;
while (e) {
if (e & 1) r = r * a;
a = a * a; e >>= 1;
}
return r;
}
易错点
- 二分 while 条件与更新写混:
lo=mid与lo=mid+1必须和「找左边界/右边界」配对,否则死循环或漏解。 - 完善程序里
mid=(l+r)/2在极大 l、r 时溢出,应写l+(r-l)/2(初赛若考到会出现在选项或代码行里)。 - 题意不单调仍硬二分:要先确认或转化为单调判定函数。
设有 100 个已排好序的数据元素,采用折半查找时,最大比较次数为()
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
( 2 ) (矩形计数) 平面上有 n 个关键点,求有多少个四条边都和 x 轴或者 y 轴平行的矩形,满足四个顶点都是关键点。给出的关键点可能有重复,但完全重合的矩形只计一 次。
试补全枚举算法。
#include <iostream>23using namespace std;45struct point {6 int x, y, id;7};89bool equals(point a, point b) {10 return a.x == b.x && a.y == b.y;11}1213bool cmp(point a, point b) {14 return ①;15}1617void sort(point A[], int n) {18 for (int i = 0; i < n; i++)19 for (int j = 1; j < n; j++)20 if (cmp(A[j], A[j - 1])) {21 point t = A[j];22 A[j] = A[j - 1];23 A[j - 1] = t;24 }25}2627int unique(point A[], int n) {28 int t = 0;29 for (int i = 0; i < n; i++)30 if (②)31 A[t++] = A[i];32 return t;33}3435bool binary_search(point A[], int n, int x, int y) {36 point p;37 p.x = x;38 p.y = y;39 p.id = n;40 int a = 0, b = n - 1;41 while (a < b) {42 int mid = ③;43 if (④)44 a = mid + 1;45 else46 b = mid;47 }48 return equals(A[a], p);49}5051const int MAXN = 1000;52point A[MAXN];5354int main() {55 int n;56 cin >> n;57 for (int i = 0; i < n; i++) {58 cin >> A[i].x >> A[i].y;59 A[i].id = i;60 }61 sort(A, n);62 n = unique(A, n);63 int ans = 0;64 for (int i = 0; i < n; i++)65 for (int j = 0; j < n; j++)66 if (⑤ && binary_search(A, n, A[i].x, A[j].y) &&67 binary_search(A, n, A[j].x, A[i].y)) {68 ans++;69 }70 cout << ans << endl;71 return 0;72}- 41(③)单选空位 ③3 分
③处应填 ( )
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
( 2 ) (矩形计数) 平面上有 n 个关键点,求有多少个四条边都和 x 轴或者 y 轴平行的矩形,满足四个顶点都是关键点。给出的关键点可能有重复,但完全重合的矩形只计一 次。
试补全枚举算法。
#include <iostream>23using namespace std;45struct point {6 int x, y, id;7};89bool equals(point a, point b) {10 return a.x == b.x && a.y == b.y;11}1213bool cmp(point a, point b) {14 return ①;15}1617void sort(point A[], int n) {18 for (int i = 0; i < n; i++)19 for (int j = 1; j < n; j++)20 if (cmp(A[j], A[j - 1])) {21 point t = A[j];22 A[j] = A[j - 1];23 A[j - 1] = t;24 }25}2627int unique(point A[], int n) {28 int t = 0;29 for (int i = 0; i < n; i++)30 if (②)31 A[t++] = A[i];32 return t;33}3435bool binary_search(point A[], int n, int x, int y) {36 point p;37 p.x = x;38 p.y = y;39 p.id = n;40 int a = 0, b = n - 1;41 while (a < b) {42 int mid = ③;43 if (④)44 a = mid + 1;45 else46 b = mid;47 }48 return equals(A[a], p);49}5051const int MAXN = 1000;52point A[MAXN];5354int main() {55 int n;56 cin >> n;57 for (int i = 0; i < n; i++) {58 cin >> A[i].x >> A[i].y;59 A[i].id = i;60 }61 sort(A, n);62 n = unique(A, n);63 int ans = 0;64 for (int i = 0; i < n; i++)65 for (int j = 0; j < n; j++)66 if (⑤ && binary_search(A, n, A[i].x, A[j].y) &&67 binary_search(A, n, A[j].x, A[i].y)) {68 ans++;69 }70 cout << ans << endl;71 return 0;72}- 42(④)单选空位 ④3 分
④处应填 ( )
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
1 #include <iostream>22 33 using namespace std;44 55 int n,k;66 77 int solve1()88 {99 int l = 0, r = n;1010 while(l <= r){1111 int mid = (l + r) / 2;1212 if (mid * mid <= n) l = mid + 1;1313 else r = mid - 1;1414 }1515 return l - 1;1616 }1717 1818 double solve2(double x)1919 {2020 if (x == 0) return x;2121 for (int i = 0; i < k; i++)2222 x = (x + n / x) / 2;2323 return x;2424 }2525 2626 int main()2727 {2828 cin >> n >> k;2929 double ans = solve2(solve1());3030 cout << ans << ' ' << (ans * ans == n) << endl;3131 return 0;3232 }- 28判断1.5 分
该算法最准确的时间复杂度分析结果为 O(logn+k)。
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
1 #include <iostream>22 33 using namespace std;44 55 int n,k;66 77 int solve1()88 {99 int l = 0, r = n;1010 while(l <= r){1111 int mid = (l + r) / 2;1212 if (mid * mid <= n) l = mid + 1;1313 else r = mid - 1;1414 }1515 return l - 1;1616 }1717 1818 double solve2(double x)1919 {2020 if (x == 0) return x;2121 for (int i = 0; i < k; i++)2222 x = (x + n / x) / 2;2323 return x;2424 }2525 2626 int main()2727 {2828 cin >> n >> k;2929 double ans = solve2(solve1());3030 cout << ans << ' ' << (ans * ans == n) << endl;3131 return 0;3232 }- 29判断1.5 分
当输入为
9801 1时,输出的第一个数为99。
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
- (寻找被移除的元素)问题: 原有长度为 n+1 公差为 1 等差数列,将数列输到程序的数组时移除了一个元素,导致长度为 n 的连续数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素。试补全程序。
#include <iostream>
#include <vector>
using namespace std;
int find_missing(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right){
int mid = left + (right - left) / 2;
if (nums[mid] == mid + ①) {
②;
} else {
③;
}
}
return ④;
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
int missing_number = find_missing(nums);
if (missing_number == ⑤) {
cout << "Sequence is consecutive" << endl;
}else{
cout << "Missing number is " << missing_number << endl;
}
return 0;
}
#include <iostream>2#include <vector>3using namespace std;4int find_missing(vector<int>& nums) {5 int left = 0, right = nums.size() - 1;6 while (left < right){7 int mid = left + (right - left) / 2;8 if (nums[mid] == mid + ①) {9 ②;10 } else {11 ③;12 }13 }14 return ④;15}16int main() {17 int n;18 cin >> n;19 vector<int> nums(n);20 for (int i = 0; i < n; i++) cin >> nums[i];21 int missing_number = find_missing(nums);22 if (missing_number == ⑤) {23 cout << "Sequence is consecutive" << endl;24 }else{25 cout << "Missing number is " << missing_number << endl;26 }27 return 0;28}- 33(①)单选空位 ①3 分
①处应填( )
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
- (寻找被移除的元素)问题: 原有长度为 n+1 公差为 1 等差数列,将数列输到程序的数组时移除了一个元素,导致长度为 n 的连续数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素。试补全程序。
#include <iostream>
#include <vector>
using namespace std;
int find_missing(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right){
int mid = left + (right - left) / 2;
if (nums[mid] == mid + ①) {
②;
} else {
③;
}
}
return ④;
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
int missing_number = find_missing(nums);
if (missing_number == ⑤) {
cout << "Sequence is consecutive" << endl;
}else{
cout << "Missing number is " << missing_number << endl;
}
return 0;
}
#include <iostream>2#include <vector>3using namespace std;4int find_missing(vector<int>& nums) {5 int left = 0, right = nums.size() - 1;6 while (left < right){7 int mid = left + (right - left) / 2;8 if (nums[mid] == mid + ①) {9 ②;10 } else {11 ③;12 }13 }14 return ④;15}16int main() {17 int n;18 cin >> n;19 vector<int> nums(n);20 for (int i = 0; i < n; i++) cin >> nums[i];21 int missing_number = find_missing(nums);22 if (missing_number == ⑤) {23 cout << "Sequence is consecutive" << endl;24 }else{25 cout << "Missing number is " << missing_number << endl;26 }27 return 0;28}- 34(②)单选空位 ②3 分
②处应填( )
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
- (寻找被移除的元素)问题: 原有长度为 n+1 公差为 1 等差数列,将数列输到程序的数组时移除了一个元素,导致长度为 n 的连续数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素。试补全程序。
#include <iostream>
#include <vector>
using namespace std;
int find_missing(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right){
int mid = left + (right - left) / 2;
if (nums[mid] == mid + ①) {
②;
} else {
③;
}
}
return ④;
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
int missing_number = find_missing(nums);
if (missing_number == ⑤) {
cout << "Sequence is consecutive" << endl;
}else{
cout << "Missing number is " << missing_number << endl;
}
return 0;
}
#include <iostream>2#include <vector>3using namespace std;4int find_missing(vector<int>& nums) {5 int left = 0, right = nums.size() - 1;6 while (left < right){7 int mid = left + (right - left) / 2;8 if (nums[mid] == mid + ①) {9 ②;10 } else {11 ③;12 }13 }14 return ④;15}16int main() {17 int n;18 cin >> n;19 vector<int> nums(n);20 for (int i = 0; i < n; i++) cin >> nums[i];21 int missing_number = find_missing(nums);22 if (missing_number == ⑤) {23 cout << "Sequence is consecutive" << endl;24 }else{25 cout << "Missing number is " << missing_number << endl;26 }27 return 0;28}- 35(③)单选空位 ③3 分
③处应填( )
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
- (寻找被移除的元素)问题: 原有长度为 n+1 公差为 1 等差数列,将数列输到程序的数组时移除了一个元素,导致长度为 n 的连续数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素。试补全程序。
#include <iostream>
#include <vector>
using namespace std;
int find_missing(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right){
int mid = left + (right - left) / 2;
if (nums[mid] == mid + ①) {
②;
} else {
③;
}
}
return ④;
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
int missing_number = find_missing(nums);
if (missing_number == ⑤) {
cout << "Sequence is consecutive" << endl;
}else{
cout << "Missing number is " << missing_number << endl;
}
return 0;
}
#include <iostream>2#include <vector>3using namespace std;4int find_missing(vector<int>& nums) {5 int left = 0, right = nums.size() - 1;6 while (left < right){7 int mid = left + (right - left) / 2;8 if (nums[mid] == mid + ①) {9 ②;10 } else {11 ③;12 }13 }14 return ④;15}16int main() {17 int n;18 cin >> n;19 vector<int> nums(n);20 for (int i = 0; i < n; i++) cin >> nums[i];21 int missing_number = find_missing(nums);22 if (missing_number == ⑤) {23 cout << "Sequence is consecutive" << endl;24 }else{25 cout << "Missing number is " << missing_number << endl;26 }27 return 0;28}- 36(④)单选空位 ④3 分
④处应填( )
整组作答后提交批改,成绩计入掌握度 · 已答 0/1
- (寻找被移除的元素)问题: 原有长度为 n+1 公差为 1 等差数列,将数列输到程序的数组时移除了一个元素,导致长度为 n 的连续数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素。试补全程序。
#include <iostream>
#include <vector>
using namespace std;
int find_missing(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right){
int mid = left + (right - left) / 2;
if (nums[mid] == mid + ①) {
②;
} else {
③;
}
}
return ④;
}
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
int missing_number = find_missing(nums);
if (missing_number == ⑤) {
cout << "Sequence is consecutive" << endl;
}else{
cout << "Missing number is " << missing_number << endl;
}
return 0;
}
#include <iostream>2#include <vector>3using namespace std;4int find_missing(vector<int>& nums) {5 int left = 0, right = nums.size() - 1;6 while (left < right){7 int mid = left + (right - left) / 2;8 if (nums[mid] == mid + ①) {9 ②;10 } else {11 ③;12 }13 }14 return ④;15}16int main() {17 int n;18 cin >> n;19 vector<int> nums(n);20 for (int i = 0; i < n; i++) cin >> nums[i];21 int missing_number = find_missing(nums);22 if (missing_number == ⑤) {23 cout << "Sequence is consecutive" << endl;24 }else{25 cout << "Missing number is " << missing_number << endl;26 }27 return 0;28}- 37(⑤)单选空位 ⑤3 分
⑤处应填( )
小苹果
C++时间 1 s内存 512 MB 文件 IO:apple.in → apple.out
小 Y 的桌子上放着 n 个苹果从左到右排成一列,编号为从 1 到 n。
小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。
每天在拿的时候,小苞都是从左侧第 1 个苹果开始、每隔 2 个苹果拿走 1 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为 n 的苹果是在第几天被拿走的?
输入格式
从文件 apple.in 中读入数据。
输入的第一行包含一个正整数 n,表示苹果的总数。
输出格式
输出到文件 apple.out 中。
输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有苹果所需的天数以及拿走编号为 n 的苹果是在第几天。
样例
8
5 5
小苞的桌上一共放了 8 个苹果。
小苞第一天拿走了编号为 1、4、7 的苹果。
小苞第二天拿走了编号为 2、6 的苹果。
小苞第三天拿走了编号为 3 的苹果。
小苞第四天拿走了编号为 5 的苹果。
小苞第五天拿走了编号为 8 的苹果。
数据范围
对于所有测试数据有:1≤n≤109。
| 测试点 | n≤ | 特殊性质 |
|---|---|---|
| 1∼2 | 10 | 无 |
| 3∼5 | 103 | 无 |
| 6∼7 | 106 | 有 |
| 8∼9 | 106 | 无 |
| 10 | 109 | 无 |
特殊性质:小苞第一天就取走编号为 n 的苹果。
部分分
- 20 分
测试点 1∼2:n≤10,无特殊性质。
- 30 分
测试点 3∼5:n≤103,无特殊性质。
- 20 分
测试点 6∼7:n≤106,特殊性质:小苞第一天就取走编号为 n 的苹果。
- 20 分
测试点 8∼9:n≤106,无特殊性质。
- 10 分
测试点 10:n≤109,无特殊性质。
你的代码(C++)
提交后由评测机编译运行,结果见下方76 B / 64 KB
判题结果
假设有序表中有 1000 个元素,则用二分法查找元素 X 最多需要比较( )次。