题目原文
题目描述
假定有一个无限长的数轴,数轴上每个坐标上的数都是
0
0
0。
现在,我们首先进行 n n n 次操作,每次操作将某一位置 x x x 上的数加 c c c。
接下来,进行 m m m 次询问,每个询问包含两个整数 l l l 和 r r r,你需要求出在区间 [ l , r ] [l,r] [l,r] 之间的所有数的和。
输入格式
第一行包含两个整数
n
n
n 和
m
m
m。
接下来 n n n 行,每行包含两个整数 x x x 和 c c c。
再接下来 m m m 行,每行包含两个整数 l l l 和 r r r。
输出格式
共
m
m
m 行,每行输出一个询问中所求的区间内数字和。
数据范围
−
1
0
9
≤
x
≤
1
0
9
,
−10^9≤x≤10^9,
−109≤x≤109,
1
≤
n
,
m
≤
1
0
5
,
1≤n,m≤10^5,
1≤n,m≤105,
−
1
0
9
≤
l
≤
r
≤
1
0
9
,
−10^9≤l≤r≤10^9,
−109≤l≤r≤109,
−
10000
≤
c
≤
10000
−10000≤c≤10000
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5
解题思路
乍一看题目,有点像前缀和(AcWing 795. 前缀和 (原题链接)),但是区间范围过大( − 1 0 9 ≤ x ≤ 1 0 9 −10^9≤x≤10^9 −109≤x≤109),不宜用直接用数组储存,且实际上题中用到的区间范围并不多( 1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1≤n,m≤105),所以使用离散化操作。
离散化操作:将很大长度的一个区间映射到较小范围上。只需将所有出现过的端点进行排序和去重,并按此编号即可。例如:出现过的端点值: 1 , 5 , 400 , 50 , 10 , 5 , 1,5,400,50,10,5, 1,5,400,50,10,5, 对其进行排序、去重,即可得: 1 , 5 , 10 , 50 , 400 , 1,5,10,50,400, 1,5,10,50,400, 将这几个值与下标 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5 相对应(为何下标不从0开始见代码注释)。这就是离散化后的结果。
此时,若在 x = 400 x = 400 x=400 处加上 40 40 40 ,在 x = 10 x = 10 x=10 处加上 10 10 10,则有数组 a [ 5 ] + = 40 , a [ 3 ] + = 10 , a[5] += 40,a[3]+=10, a[5]+=40,a[3]+=10, 若要求区间 [ 5 , 400 ] [5,400] [5,400] 中所有数之和,即求数组在下标 2 − 5 2-5 2−5 之间的和。
可以发现,此时经过离散化后,端点值变为了 1 0 5 10^5 105 之内的数,可以用数组进行储存,并使用前缀和的方法进行求解。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
vector<PII> insert, query; //用来存放插入、询问操作的序列对
vector<int> nums; //存放出现过的端点数值,用于离散化
const int N = 3e5 + 10;
int a[N], s[N]; //进行前缀和操作的两个数组
//快速找到某端点离散化后对应的数组下标
int find(int x)
{
int l = 0, r = nums.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (nums[mid] < x) l = mid + 1;
else r = mid;
}
return r + 1;
//返回r + 1 是为数组下标从1开始,便于前缀和处理
}
int main()
{
int n, m;
cin >> n >> m;
//存储插入序列对,并记录出现过的端点值
while (n -- )
{
int x, c;
cin >> x >> c;
insert.push_back({x, c});
nums.push_back(x);
}
//存储询问序列对,并记录出现过的端点值
while (m -- )
{
int l, r;
cin >> l >> r;
query.push_back({l, r});
nums.push_back(l);
nums.push_back(r);
}
//对出现过的端点值进行排序、去重
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
//处理插入操作
for (auto item : insert)
{
int x = find(item.first);
a[x] += item.second;
}
//进行前缀和处理
for (int i = 1; i <= nums.size(); i ++)
{
s[i] = s[i - 1] + a[i];
}
//处理询问操作
for (auto item : query)
{
int l = find(item.first);
int r = find(item.second);
cout << s[r] - s[l - 1] << endl;
}
return 0;
}