离散化算法以acwing 802题为例:802. 区间和 - AcWing题库
先解释离散化算法:离散化算法是哈希算法的一种,就是将一系列很稀疏的但是值比较大的数据映射到一系列很紧密且值较小易于处理的数据上,比如这一题,数据范围是 -10^9~10^9,由于我们的整数是4个字节,所以如果按照前缀和的算法的话那么所需的空间为2*10^9*4/(1024*1024)=8000mb,超过了一般算法题所能给出的空间,另外c++一秒约处理10^8次计算,这样的数据范围也明显超出了时间要求。写这道题需要用到前缀和的知识(链接:前缀和与差分 - 一心如旧 - 博客园 (cnblogs.com))以及STL中的vector容器以及部分常用的函数;
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> PII; const int N = 3e5 + 10; int a[N]; int n, m;
由于n和m的最大可能是10^5,每个n代表一个坐标,每个m代表两个坐标,所以我们的数组大小开到300010;
a数组的下标用来存储离散化之后的坐标,a[i]用来储存这些坐标点上的值以及其前缀和(先储存值,后储存前缀和);
int main() { vector<int> p; vector<PII> add, query; cin >> n >> m; while (n--) { int x, c; cin >> x >> c; add.push_back({x, c}); p.push_back(x); } while (m--) { int l, r; cin >> l >> r; p.push_back(l), p.push_back(r); query.push_back({l, r}); }
创建三个vector数组,其中p来存储需要处理的坐标(即我们在本题中只使用了p中的坐标),add用来存储相应的坐标应该做怎样的处理,query数组用来储存询问的坐标对;
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
sort函数对p数组进行排序,unique函数使相邻的相同数据只保留一个,并返回处理后的尾坐标,erase删除区间内的所有数据(unique只是移动数据,使得不重复的数据在数组头和返回的尾坐标之间,而后面的数据还在),进行这种操作的原因是我们其实是将原坐标离散化成了它在数组p中的坐标+1,如果有重复的数据,那么同一个数据就对应两个坐标,会导致不准确。
另外进行排序还为了进行前缀和,如果有一个大坐标 j 在p中的位置在小坐标 i 前面,那么最后a[1]~a[i]就会包含原坐标 j 的值;
for (auto item : add) { int x = find(p, item.first); a[x] += item.second; }
find函数是为了找到add数组元素中的坐标数据在p元素的坐标+1(方便进行前缀和运算);
int find(vector<int> &p, int x) { int l = 0, r = p.size() - 1; while (l < r) { int mid = l + r >> 1; if (p[mid] >= x) r = mid; else l = mid + 1; } return r + 1; }
由于处理后的p为有序数组,所以可以使用二分法进行搜索,比遍历快;(此函数应在主函数外)
for (int i = 1; i <= p.size(); i++) a[i] += a[i - 1]; for (auto item : query) { int l = find(p, item.first); int r = find(p, item.second); cout << a[r] - a[l - 1] << endl; } return 0; }
最后进行前缀和算法,输出答案。
另外,unique函数可以使用STL中的函数,也可以自己写一个:
vector<int>::iterator unique(vector<int>::iterator a, vector<int>::iterator b) { auto i = a + 1, j = a; while (i != b) { if (*i != *(i - 1)) *(j++) = *i; i++; } return j; }