EH's Blog

题目情境#

题目链接:https://www.luogu.com.cn/problem/P2249 ↗

题目描述

输入 nn 个不超过 10910^9 的单调不减的(就是后面的数字不小于前面的数字)非负整数 a1,a2,…,ana_1,a_2,\dots,a_{n},然后进行 mm 次询问。对于每次询问,给出一个整数 qq,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 −1-1 。

输入格式

第一行 22 个整数 nn 和 mm,表示数字个数和询问次数。

第二行 nn 个整数,表示这些待查询的数字。

第三行 mm 个整数,表示询问这些数字的编号,从 11 开始编号。

输出格式

输出一行,mm 个整数,以空格隔开,表示答案。

输入输出样例

输入 #1

11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6
plaintext

输出 #1

1 2 -1
plaintext

数据保证,1≤n≤1061 \leq n \leq 10^6,0≤ai,q≤1090 \leq a_i,q \leq 10^9,1≤m≤1051 \leq m \leq 10^5

算法解析#

二分的原理为,利用单调性快速地缩小可行域

  1. 指针的跳跃次数 log⁡n\log n
  2. l+1=rl+1=r 时结束
  3. 可行区的指针最后一定指向答案
  4. 开区间可以正确处理边界

时间复杂度:O(nlog⁡max(ai))O(n\log max(a_i))

Python代码实现#

import bisect
n, m = map(int, input().split())
lis = list(map(int, input().split()))
q_lis = list(map(int, input().split()))
for i in range(m):
    q = q_lis[i]
    id = bisect.bisect_left(lis, q)
    if id >= n or lis[id] != q:
        print(-1, end=" ")
    else:
        print(id + 1, end=" ")
python

C++代码实现#

// 我喜欢的板子
#include<cstdio>
using namespace std;
int n,m,q,a[1000005];

int find(int q){
    int l=0,r=n+1;//开区间
    while(l+1<r){ //l+1=r时结束
        int mid=l+r>>1;
        if(a[mid]>=q) r=mid; //最小化
        else l=mid;
    }
    return a[r]==q ? r : -1;
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)
        scanf("%d",&q), printf("%d ",find(q));
    return 0;
}
c
A5 二分查找
https://ehnotgod.site/blog/a5-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE
Author EH
Published at August 8, 2026