加载中...

信息奥赛题单|整数二分


整数二分

题单 🧑🏻‍💻

题目来源 题目名称 题目链接 备注 难度
洛谷 - P2249 查找 链接🔗 整数二分模版题 🟢
AcWing - 789 数的范围 链接🔗 前缀和与差分 🟢

题解 🚀

查找

题目信息 📚

【题目名称】洛谷 P2249 - 查找
【题目描述】

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

【输入】

第一行 $2$ 个整数 $n$ 和 $m$,表示数字个数和询问次数。

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

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

【输出】

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

【数据范围】

$1≤N≤300$

【输入样例】
11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6
【输出样例】
1 2 -1
【数据范围】

数据保证,$1 \leq n \leq 10^6$,$0 \leq a_i,q \leq 10^9$,$1 \leq m \leq 10^5$

本题输入输出量较大,请使用较快的 IO 方式。

【原题链接】

https://www.luogu.com.cn/problem/P2249


题目解析 🍉

【题目分析】

本题是整数二分一个非常经典的问题,查找第一个 $x$ 出现的下标,理解并应用 y总 在算法基础课上讲解的二分模版即可。

当然,本题也可以使用 STL 中的 lower_bound。 它的返回值是第一个大于等于 $x$ 的迭代器(若找不到,返回 v.end())。

比如对于 1 2 3 3 4 5,查找 $x$

  • 若 $x$ 存在,lower_bound(v.begin(),v.end(),3) 会返回第一个 $3(a[2])$ 的迭代器
    • 若要将其转化成下标,则需要减去 v.begin()
      • lower_bound(v.begin(),v.end(),3) - v.begin() 会返回第一个 $3(a[2])$ 的下标 2
    • 若要转化成「第几个」,则还要在下标的基础上 $+1$。
      • lower_bound(v.begin(),v.end(),3) - v.begin() + 1 会返回第一个 $3(a[2])$ 在数组中是第几个数
  • 若 $x$ 不存在,lower_bound(v.begin(),v.end(),3) 返回 v.end()
【C++ 代码】
#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;
int a[N];
int n, m, x;

int main() {
    ios::sync_with_stdio(false);  //cin读入优化
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    while (m--) {
        cin >> x;

        int l = 1, r = n; //确定边界
        while (l < r) {
            int mid = (l + r) / 2;
            if (a[mid] >= x) r = mid;    //r = mid,模版1
            else l = mid + 1;
        }

        //退出循环,l=r
        if (a[l] != x) cout << "-1 ";
        else cout << l << " ";
    }

    return 0;
}

数的范围

题目信息 📚

【题目名称】数的范围
【题目描述】

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

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

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

【输入】

第一行包含整数 $n$ 和 $q$,表示数组长度和询问个数。

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

接下来 $q$ 行,每行包含一个整数 $k$,表示一个询问元素。

【输出】

共 $q$ 行,每行包含两个整数,表示所求元素的起始位置和终止位置。

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

【输入样例】
6 3
1 2 2 3 3 4
3
4
5
【输出样例】
3 4
5 5
-1 -1
【数据范围】

$1 \le n \le 100000$

$1 \le q \le 10000$

$1 \le k \le 10000$

【原题链接】

https://www.acwing.com/problem/content/791/


题目解析 🍉

【题目分析】

本题是整数二分一个非常经典的问题,理解并应用 y总 在算法基础课上讲解的二分模版即可(本题既要用到二分模版1,也要用到二分模版2,是个很不错的锻炼机会)

【C++ 代码】
#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int n, m, x;
int a[N];

//整数二分
void bs(int x) {
    int l = 1, r = n; //定义边界

    //寻找第一个大于等于x的数
    while (l < r) {
        int mid = l + r >> 1;
        if (a[mid] >= x)    //需要寻找x,a[mid]要满足这一个性质,此时需要x在a[mid]左边,a[mid]要向左边的x靠近
            r = mid;    //模版1
        else
            l = mid + 1;
    }
    if (a[l] != x)
        cout << "-1 ";
    else
        cout << l - 1 << " ";   //原题下标从0开始

    //寻找最后一个小于等于x的数
    l = 1, r = n;
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (a[mid] <= x)
            l = mid;   //模版2,mid = l + r + 1 >> 1
        else
            r = mid - 1;
    }

    if (a[l] != x)
        cout << "-1" << endl;
    else
        cout << l - 1 << endl;  //原题下标从0开始
}

int main() {
    ios::sync_with_stdio(false);  //cin读入优化
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    while (m--) {
        cin >> x;
        bs(x);  //二分
    }

    return 0;
}

文章作者: Rickyの水果摊
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Rickyの水果摊 !
  目录