题目描述
假定有一个无限长的数轴,数轴上每个坐标上的数都是 00。
现在,我们首先进行 nn 次操作,每次操作将某一位置 xx 上的数加 cc。
接下来,进行 mm 次询问,每个询问包含两个整数 ll 和 rr,你需要求出在区间 [l,r][l,r] 之间的所有数的和。
输入格式
第一行包含两个整数 nn 和 mm。
接下来 nn 行,每行包含两个整数 xx 和 cc。
再接下来 mm 行,每行包含两个整数 ll 和 rr。
输出格式
共 mm 行,每行输出一个询问中所求的区间内数字和。
数据范围
思路
1.前缀和:
区间和是典型的前缀和问题。
(1)建立一个sum数组,记录该元素以前(包括该元素)的所有值之和
(2)若要计算一个区间的长度,直接使用前缀和之差即可。
2.离散化:
由于该问题的数据范围>>操作个数,数据分布较为离散,若仅仅使用前缀和算法的计算复杂度较高。所以我们需要对要操作的数进行离散化
(1)读入所有加入的位置和查找的左右边界,放入a数组中。a数组记录所有可能会用到的下标
(2)对a数组的所有元素进行排序+去重。
(3)a中元素对应的下标即为前缀和数组对应下标。需要使用二分查找方法来将前缀和数组下标和目标区间的上下界进行查找(对应)
3.使用的数据结构:
(1)使用pair形式存储单个操作的[下标,值]、单次计算的[下界,上界];使用vector存储所有的操作和计算数据,vector中的每一个元素是一个pair
(2)使用一维数组(vector)的形式存储前缀和、使用到的下标。
代码:
#include <iostream>
#include <vector>
#include <algorithm>
#define N 300010
using namespace std;
int find(vector<int> a, int x){
int left = 0, right = a.size()-1;
int mid;
while(left <= right){
mid = left + (right - left)/2;
if(a[mid] == x) return mid;
else if(x < a[mid]) right = mid - 1;
else left = mid + 1;
}
return right;
}
int main(){
vector<int>a; //所有用到的下标
vector<pair<int,int>> add, check;
int n,m;
cin >> n >> m;
//1.插入元素的插入
for(int i = 0; i < n; i ++ ){
int x,c;
cin>>x>>c;
add.push_back({x,c});
a.push_back(x);
}
//2.查询元素的插入
for(int i = 0; i < m; i ++ ){
int l,r;
cin>>l>>r;
a.push_back(l);
a.push_back(r);
check.push_back({l,r});
}
//3.去重
sort(a.begin(),a.end());
a.erase(unique(a.begin(),a.end()), a.end());//排序后删除重复元素
//4.插入操作
int value[N] = {0};
for(auto &it : add){
int pos = find(a, it.first);
value[pos] += it.second;
}
//5.计算前缀和
int sum[N] = {0};
for(int i = 1; i <= a.size(); i++){
sum[i] = sum[i - 1] + value[i - 1];
}
//6.计算结果并输出
for(auto & it : check){
int left = find(a, it.first);
int right = find(a, it.second);
int result = sum[right + 1] - sum[left];
cout<<result<<endl;
}
return 0;
}