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
题的理解:
//先找出数x在有序数列中第一次出现的位置i,再找出该数最后一次出现的位置j
//即边界问题:位置于i左边的数全都小于x,位置j右侧的数全都大于x
// 因此可以采用二分查找
y总模板
//将数组分为 l ~ mid ,mid+1 ~ r
int mid = l+r>>1;
while(l<r){
if(check(mid)) r = mid; // 目标在mid左边
else l = mid+1;
}
//将数组分为 l ~ mid-1 , mid ~ r
int mid = l+r+1>>1;
while(l < r){
if(check(mid)) l = mid;
else r = mid -1;
}
题解
#include <iostream>
using namespace std;
const int N = 100010;
int arr[N];
int main(){
int n,q ;
scanf("%d",&n);
int x;
scanf("%d",&x);
for(int i = 0 ; i < n ;i++) scanf("%d",&arr[i]);
while(x--){
scanf("%d",&q);
int l =0,r = n-1;
while(l < r){ // 查找第一个出现该数字的下标
int mid = l+r>>1;
if(arr[mid] >=q ) r = mid ;
else l = mid+1;
}
if(arr[l] != q) cout << "-1 -1"<<endl;
else {
cout << l << ' ';
l = 0 , r = n-1;
while(l < r){
int mid = l+r + 1>>1;
if(arr[mid] <= q ) l = mid;
else r = mid -1;
}
cout << l <<endl;
}
}
return 0;
}
举例: 1 2 2 3 3 4 x = 3 , l = 0 , r = 5
设 开始位置 为 i ,结束位置 j
下标 0 ~ 5
查找第一次出现的下标
这里的check条件是 q[mid] >= x 原因 : i右边的数都小于 x
循环 1 : mid = l + r >> 1 ; // mid = 2
q[mid] >= 3 为false ,则mid 左边都是比3小的数,>=3的数都在mid右边,l = mid+1; // l = 3 r =5
循环 2 : mid = l +r >>1 // mid = 4
q[mid] >= 3 为true,则mid右侧都比3大,<=3的数都在mid左侧,r = mid ; // l = 3 r = 4
循环 3 : mid = l + r >> 1 // mid = 3
q[mid] >= 3 为true,则mid右侧都比3大,<=3的数mid左侧,r = mid ;//l = 3, r = 3
l = r ,break;