离散化的一道很经典的题
- 区间和
题目
提交记录
讨论
题解
视频讲解
假定有一个无限长的数轴,数轴上每个坐标上的数都是 0
。
现在,我们首先进行 n
次操作,每次操作将某一位置 x
上的数加 c
。
接下来,进行 m
次询问,每个询问包含两个整数 l
和 r
,你需要求出在区间 [l,r]
之间的所有数的和。
输入格式
第一行包含两个整数 n
和 m
。
接下来 n
行,每行包含两个整数 x
和 c
。
再接下来 m
行,每行包含两个整数 l
和 r
。
输出格式
共 m
行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109
,
1≤n,m≤105
,
−109≤l≤r≤109
,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5
离散化的含义便是映射,把有关的数组下标映射为自然数0,1,2,3....或者1,2,3,4,5....,首先使用离散化的前提的条件便是值域跨度很大,但题目的输入和输出中牵扯到的数字下标没有这么大。例如本题:
值域范围为:1e9的范围,但本体在输入时先给某个下标加值,n最多便是1e5,而m每次询问有两个下标(索引),m的范围是1e5,故m * 2 + n 的范围便是3e5 + 10(这个10是为了防止数组越界的问题)。当然,如果数据范围什么时候变小了,大致是一个2*1e5的范围,就可以用我们常用到的前缀和来求解,这个离散化可以完美的使用我们的数组,保证所有的下标都与题目有联系。
附上c++代码:
include
include
include
using namespace std;
const int N = 300010;
typedef pair<int,int> PII;
int n,m;
int a[N],b[N];
vector
vector
int find(int x)
{
int l = 0,r = stu.size() - 1;
while(l < r)
{
int mid = l + r >> 1;
if(stu[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0;i < n;i ++)
{
int x,y;
cin >> x >> y;
stu.push_back(x);
aoi.push_back({x,y});
}
for(int j = 0;j < m;j ++)
{
int l,r;
cin >> l >> r;
stu.push_back(l);
stu.push_back(r);
aio.push_back({l,r});
}
sort(stu.begin(),stu.end());
stu.erase(unique(stu.begin(),stu.end()),stu.end());
for(auto boy:aoi)
a[find(boy.first)] += boy.second;
for(int i = 1;i <= stu.size(); i ++) b[i] = b[i-1] + a[i];
for(auto riu:aio)
printf("%d\n" , b[find(riu.second)] - b[find(riu.first) - 1]);
return 0;
}