范围查询(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;
}