## [$AcWing$ $789$. 数的范围](https://www.acwing.com/problem/content/791/) ### 一、题目描述 给定一个按照升序排列的长度为 $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$ **输入样例:** ```cpp {.line-numbers} 6 3 1 2 2 3 3 4 3 4 5 ``` **输出样例:** ```cpp {.line-numbers} 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$ 升序 ```c++ int p = lower_bound(q, q + n, x) - q; ``` 降序 ```c++ int p = lower_bound(q, q + n, x, greater()) - q; ``` $\large upper\_bound$ 升序 ```c++ int p = upper_bound(q, q + n, x) - q; ``` 降序 ```c++ int p = upper_bound(q, q + n, x, greater()) - q; ``` ### 四、手写左闭右开(推荐写法) 升序 ```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; } ``` 降序 ```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 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; } ```