AcWing 802. 区间和

发布时间 2023-12-07 20:21:19作者: 蒟蒻爬行中

题面:
假定有一个无限长的数轴,数轴上每个坐标上的数都是 \(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大佬,搬运仅作学习,非常感谢:
image

代码

#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;
	}
}