You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
5.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## [$AcWing$ $789$. 数的范围](https://www.acwing.com/problem/content/791/)
### 一、题目描述
给定一个按照升序排列的长度为 $n$ 的整数数组,以及 $q$ 个查询。
对于每个查询,返回一个元素 $k$ 的起始位置和终止位置(位置从 $0$ 开始计数)。
如果数组中不存在该元素,则返回 `-1 -1`
**输入格式**
第一行包含整数 $n$ 和 $q$,表示数组长度和询问个数。
第二行包含 $n$ 个整数(均在 $110000$ 范围内),表示完整数组。
接下来 $q$ 行,每行包含一个整数 $k$,表示一个询问元素。
**输出格式**
共 $q$ 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 `-1 -1`
**数据范围**
$1≤n≤100000$
$1≤q≤10000$
$1≤k≤10000$
**输入样例:**
```cpp {.line-numbers}
6 3
1 2 2 3 3 4
3
4
5
```
**输出样例:**
```cpp {.line-numbers}
3 4
5 5
-1 -1
```
<font color='red' size=4><b>命名规则</b></font>
为描述方便,下面文中提示的:
升序理解为不降序,即按顺序输入的数字,每个数字对比前面的数字,可等于可大于,但不能小于。
降序理解为不升序,即按顺序输入的数字,每个数字对比前面的数字,可等于可小于,但不能大于。
### 二、函数定义与用途
$\large lower\_bound$
**用途**
<font color='blue' size=4><b>升序</b></font>的情况下,$lower\_bound$返回第一个 <font color='red' size=4><b>大于等于$val$</b></font>的位置。
<font color='blue' size=4><b>降序</b></font>的情况下,$lower\_bound$返回第一个 <font color='red' size=4><b>小于等于$val$</b></font>的位置。
$\large upper\_bound$
**用途**
<font color='blue' size=4><b>升序</b></font>的情况下,$upper\_bound$返回第一个 <font color='red' size=4><b>大于$val$</b></font>的位置。
<font color='blue' size=4><b>降序</b></font>的情况下,$upper\_bound$返回第一个 <font color='red' size=4><b>小于$val$</b></font>的位置。
### 三、$STL$内置方法
$\large lower\_bound$
<font color='blue' size=4><b>升序</b></font>
```c++
int p = lower_bound(q, q + n, x) - q;
```
<font color='blue' size=4><b>降序</b></font>
```c++
int p = lower_bound(q, q + n, x, greater<int>()) - q;
```
$\large upper\_bound$
<font color='blue' size=4><b>升序</b></font>
```c++
int p = upper_bound(q, q + n, x) - q;
```
<font color='blue' size=4><b>降序</b></font>
```c++
int p = upper_bound(q, q + n, x, greater<int>()) - q;
```
### 四、手写左闭右开(推荐写法)
<font color='blue' size=4><b>升序</b></font>
```cpp {.line-numbers}
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;
}
```
<font color='blue' size=4><b> 降序</b></font>
```cpp {.line-numbers}
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$。
### 六、实现代码
```cpp {.line-numbers}
#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;
}
```