5.2 KiB
AcWing
789
. 数的范围
一、题目描述
给定一个按照升序排列的长度为 n
的整数数组,以及 q
个查询。
对于每个查询,返回一个元素 k
的起始位置和终止位置(位置从 0
开始计数)。
如果数组中不存在该元素,则返回 -1 -1
。
输入格式
第一行包含整数 n
和 q
,表示数组长度和询问个数。
第二行包含 n
个整数(均在 1∼10000
范围内),表示完整数组。
接下来 q
行,每行包含一个整数 k
,表示一个询问元素。
输出格式
共 q
行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1
。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
命名规则 为描述方便,下面文中提示的:
升序理解为不降序,即按顺序输入的数字,每个数字对比前面的数字,可等于可大于,但不能小于。
降序理解为不升序,即按顺序输入的数字,每个数字对比前面的数字,可等于可小于,但不能大于。
二、函数定义与用途
\large lower\_bound
用途:
升序的情况下,lower\_bound
返回第一个 大于等于val
的位置。
降序的情况下,lower\_bound
返回第一个 小于等于val
的位置。
\large upper\_bound
用途:
升序的情况下,upper\_bound
返回第一个 大于val
的位置。
降序的情况下,upper\_bound
返回第一个 小于val
的位置。
三、STL
内置方法
\large lower\_bound
升序
int p = lower_bound(q, q + n, x) - q;
降序
int p = lower_bound(q, q + n, x, greater<int>()) - q;
\large upper\_bound
升序
int p = upper_bound(q, q + n, x) - q;
降序
int p = upper_bound(q, q + n, x, greater<int>()) - q;
四、手写左闭右开(推荐写法)
升序
int lower_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] >= x)
r = mid;
else
l = mid + 1;
}
return l;
}
int upper_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] > x)
r = mid;
else
l = mid + 1;
}
return l;
}
降序
int lower_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] <= x)
r = mid;
else
l = mid + 1;
}
return l;
}
int upper_bound(int q[], int l, int r, int x) {
while (l < r) {
int mid = (l + r) / 2;
if (q[mid] < x)
r = mid;
else
l = mid + 1;
}
return l;
}
五、经验总结
-
二分模板在网上非常多,在边界处理上有各种各样的处理方式,感受后,认为
STL
的思路是最好的:左闭右开 -
一般来讲,
STL
可以处理大于等于,大于,小于等于,小于,一般的数字二分够用 -
但是,由于二分不一定是数字二分,有时需要用
check
函数,这里STL
就无法使用check
函数了,所以,终极解法还是手写二分,容易扩展! -
查找左边界,可以直接
lower\_bound
,如果想要查找右边界,可以使用upper\_bound
然后再减1
。
六、实现代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int q[N];
// 左闭右开 [ )
int lower_bound(int l, int r, int x) {
while (l < r) {
int mid = (l + r) >> 1;
if (q[mid] >= x)
r = mid;
else
l = mid + 1;
}
return l;
}
int upper_bound(int l, int r, int x) {
while (l < r) {
int mid = (l + r) >> 1;
if (q[mid] > x)
r = mid;
else
l = mid + 1;
}
return l;
}
int main() {
int T;
cin >> n >> T;
for (int i = 0; i < n; i++) cin >> q[i];
while (T--) {
int x;
cin >> x;
int p = lower_bound(0, n, x); //[0,n)
if (q[p] != x) {
puts("-1 -1");
continue;
}
printf("%d ", p);
p = upper_bound(0, n, x);
printf("%d\n", p - 1);
}
return 0;
}