范围查询(Range) 数据结构(上) Tsinghua OJ PA1

范围查询(Range)

描述

数轴上有n个点,对于任一闭区间 [a, b],试计算落在其内的点数。

输入

第一行包括两个整数:点的总数n,查询的次数m。

第二行包含n个数,为各个点的坐标。

以下m行,各包含两个整数:查询区间的左、右边界a和b。

输出

对每次查询,输出落在闭区间[a, b]内点的个数。

Example

Input

5 2
1 3 7 9 11
4 6
7 12

Output

0
3

限制

0 ≤ n, m ≤ 5×105

对于每次查询的区间[a, b],都有a ≤ b

各点的坐标互异

各点的坐标、查询区间的边界a、b,均为不超过10^7的非负整数

时间:2 sec

内存:256 MB

算法

#include <stdio.h>
//加速IO
const int MAX_IO = 1 << 25;
struct FastIO
{
    char inBuff[MAX_IO];
    char outBuff[MAX_IO];
    FastIO()
    {
        setvbuf(stdin, inBuff, _IOFBF, MAX_IO);
        // setvbuf(stdout, outBuff, _IOFBF, MAX_IO);
    }
} fastio;
//区分开下标与数值,Rank是下标,int是数值
typedef int Rank;
const int MAX_NUM = 5e5 + 10;
int numbers[MAX_NUM];
//归并排序用的B数组
int B[MAX_NUM];
int n, m;
//构造三个序列A、B、C,其中B必须为独立的内存空间,A和C可以为待排序序列的子序列
//B记录[lo, mi)的值,C记录[mi, hi)的值,A用于存储归并结果,B和C已经有序
//归并过程:在B和C中逐次选取较小的值,存入A中,直至B和C全部耗尽为止
//技巧:b和c相等时优先消耗B中的值,因为C和A是同一序列,即使C未消耗完也不需要填充到A中
void merge(Rank lo, Rank mi, Rank hi)
{
	//归并思路
    Rank i, j, k;
    int *A = numbers + lo;
    int size_B = mi - lo, size_C = hi - mi;
    for (int i = 0; i < size_B; ++i)
        B[i] = A[i];
    int *C = numbers + mi;
    for (i = j = k = 0; j < size_B && k < size_C; ++i)
    {
        if (C[k] < B[j])
            A[i] = C[k++];
        else
            A[i] = B[j++];
    }
    for (; j < size_B; ++j)
        A[i++] = B[j];
}
//归并排序:不断二分直至为单元素序列,因为单元素序列自然有序,因此可以开始归并
void mergeSort(Rank lo, Rank hi)
{
    if ((hi - lo) < 2)
        return;
    int mi = (lo + hi) >> 1;
    mergeSort(lo, mi);
    mergeSort(mi, hi);
    merge(lo, mi, hi);
}
//二分法求下界,即返回序列中大于等于e的秩最小者的下标
Rank lowerBound(const int &e)
{
    Rank mi, lo = 0, hi = n;
    while (lo < hi)
    {
        mi = (lo + hi) >> 1;
        //若中间值小于e,表明前半部分必然都小于e,后半部分可能有大于等于e的值
        //因此在后半部分中,e可以作为下界
        //使后半部分持续收缩,直至lo = mi+1 且 lo == hi
        //此时lo必然对应大于等于e且秩最小者的下标
        (numbers[mi] < e) ? lo = mi + 1 : hi = mi;
    }
    return lo;
}
//二分法求上界,即返回序列中大于e的秩最小者的下标
Rank upperBound(const int &e)
{
    Rank mi, lo = 0, hi = n;
    while (lo < hi)
    {
        mi = (lo + hi) >> 1;
        //若e小于中间值,表明后半部分必然都大于e,前半部分可能有小于等于e的值
        //因此在前半部分中,e可以作为上界
        //使前半部分持续收缩,直至hi = mi 且 lo == hi
        //此时lo必然对应大于e且秩最小者的下标
        (e < numbers[mi]) ? hi = mi : lo = mi + 1;
    }
    return lo;
}

int main(int argc, char const *argv[])
{
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; ++i)
        scanf("%d", &numbers[i]);
    mergeSort(0, n);
    int a, b;
    Rank lo, hi;
    for (int i = 0; i < m; ++i)
    {
        scanf("%d %d", &a, &b);
        lo = lowerBound(a), hi = upperBound(b);
        printf("%d\n", (hi - lo));
    }

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值