给定一个按照升序排列的长度为 nn 的整数数组,以及 qq 个查询。
对于每个查询,返回一个元素 kk 的起始位置和终止位置(位置从 00 开始计数)。
如果数组中不存在该元素,则返回 -1 -1
。
输入格式
第一行包含整数 nn 和 qq,表示数组长度和询问个数。
第二行包含 nn 个整数(均在 1∼100001∼10000 范围内),表示完整数组。
接下来 qq 行,每行包含一个整数 kk,表示一个询问元素。
输出格式
共 qq 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1
。
数据范围
1≤n≤1000001≤n≤100000
1≤q≤100001≤q≤10000
1≤k≤100001≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
#include<iostream>
#include<cstdio>
using namespace std;
const int mas=1e5+5;
int n,q,v[mas];
int main()
{
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++)
scanf("%d", &v[i]);//两次进行二分,先进行l的二分,再进行r的二分
while(q--)
{
int x;
scanf("%d",&x);
int l=0,r=n-1;
while(l<r)//先进行l的二分
{
int mid=l+r>>1;
if(v[mid]>=x)r=mid;//r等于mid,mid的数值不用进行改变
else l=mid+1;
//左侧进行判断的时候需要注意的是,只有严格小于才可以作为l,否则会有多个等于x的数值
}
if(v[l]!=x)cout<<-1<<" "<<-1<<endl;//不成立
else {//二分r
cout<<l<<" ";
r=n-1;//r和l的数值都发生变换,只需要变换r的数值就可以
while(l<r)
{
int mid=l+r+1>>1;
if(v[mid]<=x)l=mid;
//左侧进行判断的时候需要注意的是,只有严格大于才可以作为r,否则会有多个等于x的数值
else r=mid-1;
}
cout<<r<<endl;
}
}
return 0;
}
总结:
1.整数的二分会遇到边界问题:
模板(1):
while(l<r)
{
int mid=l+r>>1;
if(v[mid]>=x)r=mid;
else l=mid+1;
}
模板(2):
while(l<r)
{
int mid=l+r+1>>1;
if(v[mid]>=x)r=mid;
else l=mid+1;
}
区别:当r=mid的时候,mid=l+r>>1;
当l=mid的时候,mid=l+r+1>>1;
2 因为是寻找左右两个下标
左下标和右下标都需要通过二分进行寻找,否会造成运行超时
左侧进行下标寻找时需要注意:v[mid]<x,l=mid+1才可以作为左边的下标,不可以带上等号,因为如果带上等号就不会是 最左边的数据
右侧进行下标寻找时需要注意:v[mid]>x,r=mid-1才可以作为右边的下标,不可以带上等号,因为如果带上等号就不会是 最右边的数据
3再者就是不论是快排模板还是二分判断的条件都是l<r,不可以带上等于号
4 进行了第一次二分以后,第二次二分的时候,即:二分右侧。l此时的l不用变换,但是右侧的r,需要变换成n-1