范围查询(Range)

范围查询(Range)

Descriptioin

Let S be a set of n integral points on the x-axis. For each given interval [a, b], you are asked to count the points lying inside.

Input

The first line contains two integers: n (size of S) and m (the number of queries).

The second line enumerates all the n points in S.

Each of the following m lines consists of two integers a and b and defines an query interval [a, b].

Output

The number of points in S lying inside each of the m query intervals.

Example

Input

5 2
1 3 7 9 11
4 6
7 12

Output

0
3

Restrictions

0 <= n, m <= 5 * 10^5

For each query interval [a, b], it is guaranteed that a <= b.

Points in S are distinct from each other.

Coordinates of each point as well as the query interval boundaries a and b are non-negative integers not greater than 10^7.

Time: 2 sec

Memory: 256 MB

解法一:

  1. 原理与要点: 因为坐标的范围比较小,只有1e7,且均为非负数,就很容易想到用前缀和来解决这个问题。

求前缀和(\(a_i = \sum_{j=0}^{i}b_j\)\(b_j\)存储坐标为j的点的个数,\(a_i\)存储区间[0,i]内点的个数),对每次询问的\(l,r\)输出 $ a_r-a_{l-1}$

  1. 遇到的问题:
  2. 时间和空间复杂度:求前缀和时间复杂度\(O(1e7)\),每次查询复杂度\(O(1)\)。理论复杂度比排序+二分的解法更优。空间复杂度\(O(1e7)\)
  3. 特别或创新: 对于这道题的数据量来说,前缀和更快,编程更简单。
#include "iostream"
#include "cstdio"

using namespace std;
const int maxn = 1e7+ 100;
int a[maxn];

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    int x;
    for (int i = 0; i < n; i++) {
        scanf("%d", &x);
        a[x]++;
    }
    for (int i = 1; i < maxn - 1; i++) {
        a[i] += a[i - 1];
    }
    int l, r;
    while (m--) {
        scanf("%d %d", &l, &r);
        if (l != 0)
            printf("%d\n", a[r] - a[l - 1]);
        else
            printf("%d\n", a[r]);
    }
    return 0;
}

解法二:

  1. 原理与要点:将所有的点存到一个数组里然后排序,每次询问时二分查找到左端点和右端点,相减得到查询结果
  2. 遇到的问题:
  3. 时间和空间复杂度:时间复杂度\(O(nlogn)\),空间复杂度\(O(n)\)
  4. 特别或创新:
#include "cstdio"
#include "stdlib.h"
#include "iostream"

using namespace std;
const int maxn = 5e5 + 100;
const int inf = 0x3f3f3f3f;
const int SZ = 1 << 20;  //快速io
struct fastio {
    char inbuf[SZ];
    char outbuf[SZ];

    fastio() {
        setvbuf(stdin, inbuf, _IOFBF, SZ);
        setvbuf(stdout, outbuf, _IOFBF, SZ);
    }
} io;

int cmp(const void *a, const void *b) {
    return *(int *) a - *(int *) b;
}

int a[maxn];

int main() {
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    a[0] = -inf;
    a[n + 1] = inf;
    qsort(a, n + 1, sizeof(a[0]), cmp);
    int x, y;
    int from, to;
    int l, r, mid;
    while (q--) {
        scanf("%d %d", &x, &y);
        l = 0, r = n + 1;
        while (l < r) {
            mid = (l + r + 1) >> 1;
            if (a[mid] >= x) r = mid - 1;
            else l = mid;
        }
        from = l;
        l = 0, r = n + 1;
        while (l < r) {
            mid = (l + r) >> 1;
            if (a[mid] <= y) l = mid + 1;
            else r = mid;
        }
        to = l;
        printf("%d\n", to - from - 1);
    }
    return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值