You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 789. 数的范围

一、题目描述

给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。

对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。

如果数组中不存在该元素,则返回 -1 -1

输入格式 第一行包含整数 nq,表示数组长度和询问个数。

第二行包含 n 个整数(均在 110000 范围内),表示完整数组。

接下来 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

命名规则 为描述方便,下面文中提示的:

升序理解为不降序,即按顺序输入的数字,每个数字对比前面的数字,可等于可大于,但不能小于。

降序理解为不升序,即按顺序输入的数字,每个数字对比前面的数字,可等于可小于,但不能大于。

二、函数定义与用途

\large lower\_bound

用途 升序的情况下,lower\_bound返回第一个 大于等于val的位置。

降序的情况下,lower\_bound返回第一个 小于等于val的位置。

\large upper\_bound 用途 升序的情况下,upper\_bound返回第一个 大于val的位置。

降序的情况下,upper\_bound返回第一个 小于val的位置。

三、STL内置方法

\large lower\_bound

升序

int p = lower_bound(q, q + n, x) - q;

降序

int p = lower_bound(q, q + n, x, greater<int>()) - q;

\large upper\_bound

升序

int p = upper_bound(q, q + n, x) - q;

降序

int p = upper_bound(q, q + n, x, greater<int>()) - q;

四、手写左闭右开(推荐写法)

升序

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;
}

降序

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

六、实现代码

#include <bits/stdc++.h>
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;
}