- 确定判定的函数使得要找的值
k
在二分的边界上 - 看
k
在哪一边,写出check
并包含k
- 看
check
的区域在左边还是右边,然后拿相应的模版做。
最后一步思路如此可行,因为在决定是l=mid
还是r=mid
的时候,由于k
是在边界上,这样做的话保证了新的边界一定包含k
。可根据题目加深理解为什么这么思考可行。
以题目说明:
789. 数的范围
给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。
如果数组中不存在该元素,则返回“-1 -1”。
输入格式
第一行包含整数n和q,表示数组长度和询问个数。
第二行包含n个整数(均在1~10000范围内),表示完整数组。
接下来q行,每行包含一个整数k,表示一个询问元素。
输出格式
共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回“-1 -1”。
首先来找起始位置i
。如下图,要使得i
在二分边界上,需要按照 $\ge k$ 和 $<k$ 来划分,这样i
落在 $\ge k$ 的边界上,所以check为A[mid]>=k
,并且由于判定的区域在右边,所以r=mid
。这样就相对应地拿区间[l, r]
被划分成[l, mid]
和[mid + 1, r]
的模版来用。
接着再来找终止位置j
。如下图,要使得j
在二分边界上,需要按照 $>k$ 和 $\le k$ 来划分,这样j
落在 $\le k$ 的边界上,所以check为A[mid]<=k
,并且由于判定的区域在左边,所以l=mid
。这样就相对应地拿区间[l, r]
被划分成[l, mid - 1]
和[mid, r]
的模版来用。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5;
int n, q, A[N], k;
int main() {
scanf("%d%d", &n, &q);
for (int i=0; i<n; i++) scanf("%d", &A[i]);
while (q--) {
scanf("%d", &k);
int l=0, r=n-1;
while (l<r) {
int mid=l+r>>1;
if (A[mid]>=k) r=mid;
else l=mid+1;
}
if (A[l]!=k) printf("-1 -1\n");
else {
printf("%d ", l);
l=0, r=n-1;
while (l<r) {
int mid=l+r+1>>1;
if (A[mid]<=k) l=mid;
else r=mid-1;
}
printf("%d\n", l);
}
}
}