区间和
题目描述:
假定有一个无限长的数轴,数轴上每个坐标上的数都是 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
思路
下标太大, 要离散化 映射到相邻数组元素中,
需要写出find(int x)函数,找出数轴点对应的映射后的数组下标, 找到大于等于x的最小的数
用的二分, 需要提前排序 有序
二分返回值:从1开始的下标。因为求区间和时用前缀和,前缀和下标从1开始很方便
为什么返回r + 1,这是变相的让映射后的数组从1开始。此处描述映射后的数组下标对应的数值用的是a数组。
说明:
vector<PII> add, query;
add用于存放 在x的位置上加上一个数c 的 {x和c}
query用于存放 求区间的 {l和r}
alls 存放 把下标x离散化的 x 还有 l和r
也就是说 需要离散化的数组下标全部放到了alls数组里面去
sort(alls) 排序
alls.erase(unique(alls)) 去重
再去做每一个操作 find
操作:
1. 把数读入
2. 离散化
分别处理的操作
1. 插入i的操作
for(auto item: add)
{
int x = find(item.first); // 对应的映射 离散化后的下标
a[x] += item.second(); // 在离散化后的坐标上, 加上要加的数
}
2. 预处理前缀和 映射到从1 到 <= alls.size()啊
for(int i =1; i<= alls.size(); i++) s[i] = s[i-1] + a[i];
3. 询问的操作
for(auto item: query)
{ // 左端点离散化后的值, r右端点离散化后的值
int l = find(item.first), r = find(item.second);
// 中间所有数的和 ,
cout << s[r] - s[l-1] << endl;
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N = 3e5+10;
int n,m;
int a[N], s[N]; //存放映射后的值; 存放映射后的前缀和
vector<int> alls; // alls 存放 把下标x离散化的 x 还有 l和r
vector<PII> add, query;
// add用于存放 在x的位置上加上一个数c 的 {x和c}
// query用于存放 求区间的 {l和r}
int find(int x)
{
int l =0, r = alls.size()-1;
while(l < r)
{ // 二分找到并返回 映射后的1 ~ ````
int mid = l + r >> 1;
if(alls[mid] >= x) // 返回>=x 的 最小位置
r = mid;
else l = mid + 1;
}
return r + 1; // 映射 1 ~ ···
}
int main()
{
cin >> n >> m;
//我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。
for(int i =1; i<=n;i++)
{
int x, c;
cin >> x >> c;
add.push_back({x,c}); // 放 x位置 待加的c数字
alls.push_back(x); // 需要离散化的下标 x
}
for(int i =0;i <m;i++)
{
//读入区间 , l和r都要离散化,ye要加入到alls
int l, r;
cin >> l >> r;
query.push_back({l,r});
alls.push_back(l);
alls.push_back(r);
//要用到的下标, 待离散化的下标放到了alls里边,排序+去重,就是排好序的用到的下标
}
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()),alls.end());
// alls 剩 排好序 需用的下标 此时的alls就已经离散化成很小的范围了
// find x 返回 映射到的 1 ~ n
// 插入操作 a[x] 加上数c
for(auto item: add) // 遍历add 需要x位置插入c的数组
{
int x = find(item.first) ; // x位置 映射的位置
a[x] += item.second; // 数c 就是add[].second
}
// 预处理前缀和
for(int i=1;i<=alls.size(); i++)
{
s[i] = s[i-1] + a[i]; // 预处理 为了求[l,r]区间的前缀和
}
//询问的操作
for(auto item: query)
{
// 左右离散化后的lr下标
int l = find(item.first), r = find(item.second);
cout << s[r] - s[l-1] <<endl; // 输出[l,r]的和
}
return 0;
}