题面:
假定有一个无限长的数轴,数轴上每个坐标上的数都是 \(0\) 。
现在,我们首先进行 \(n\) 次操作,每次操作将某一位置 \(x\) 上的数加 \(c\) 。
接下来,进行 \(m\) 次询问,每个询问包含两个整数 \(l\) 和 \(r\) ,求出在区间 \([l,r]\) 之间的所有数的和。
原题链接:802. 区间和 - AcWing
保序离散化
离散化就是把大而分散的一段段使用到的稀疏区间,整合映射到连续的一段较小的稠密区间里,然后就可以通过普通前缀和公式来计算连续一段的区间和,本质上就是化大为小,把稀疏离散化简为稠密连续的一段。—— acvv_motibodo
建立键值对(离散化模板)
去重:原数组中可能拥有重复元素
//使用库函数快速去重
vector<int> alls; //存储所有待离散化的值
sort(alls.begin(), alls.end()); //排序
//去重:unique负责去重并返回数组尾端点,erase负责释放多余空间
alls.erase(unique(alls.begin(), alls.end()), alls.end());
求值:二分求出键值化后的值
//找到第一个大于等于x的位置
int find(int x) {
int l = 0, r = alls.size() - 1;
while (l < r) {
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; //映射到1,2,...n
}
解题思路
迄今为止看到思路最清晰的图解之一,来自liangshang大佬,搬运仅作学习,非常感谢:
代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 3e5 + 10; //1e5会报SE
int n, m;
int a[N], s[N];
vector<int> alls; //alls数组中存储的均为坐标(key)
vector<PII> add, query; //对应两种操作,插入与查询
//找到第一个大于等于x的位置
int find(int x) {
int l = 0, r = alls.size() - 1;
while (l < r) {
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; //映射到1,2,...n
}
int main()
{
cin >> n >> m;
//存储键值对
for (int i = 0; i < n; i++) {
int x, c;
cin >> x >> c;
add.push_back({ x,c });
alls.push_back(x);
}
//存储左右端点,一同建立映射
for (int i = 0; i < m; i++) {
int l, r;
cin >> l >> r;
query.push_back({ l,r });
alls.push_back(l);
alls.push_back(r);
}
//去重,建立索引
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
//处理插入,未涉及添加操作的端点值为默认0
for (auto i : add) {
int x = find(i.first); //键
a[x] += i.second; //值
}
//预处理前缀和
for (int i = 1; i <= alls.size(); i++)
s[i] = s[i - 1] + a[i];
//处理查询
for (auto i : query) {
int l = find(i.first);
int r = find(i.second);
cout << s[r] - s[l - 1] << endl;
}
}
标签:end,int,mid,back,push,alls,区间,802,AcWing
From: https://www.cnblogs.com/haruhomu/p/17883867.html