定义
离散化就是把无限空间中有限个体映射到有限空间中去,以此提高算法的时空效率。通俗的讲,就是在不改变数据的相对大小的情况下缩小数据。(本质上是一种哈希。)
以上是百度给的定义,直接看起来会觉得非常抽象。用通俗的语言来说就是:
离散化的本质是建立了一段数列到自然数之间的映射关系(value -> index),通过建立新索引,来缩小目标区间,使得可以进行一系列连续数组可以进行的操作比如二分,前缀和等…
使用一个新的数组,这个新数组的值是原序列的下标,利用这个关系,建立起新数组的下标与原序列的关系,桥梁就是 新数组的值 == 原序列的下标(原序列的下标是离散的,不是相邻的)
适用范围
数组中元素值域很大,但个数不多。
应用
常与二分、差分、前缀和、数组、线段树等结合考察。
做题步骤
第一步:用一个辅助的数组把你要离散的所有数据存下来(存入离散化下标)
第二步:排序去重
第三步:插入操作
第四步:前缀和
第五步:查询
那么接下来就根据一道例题来分别解释一下这几个步骤
AcWing 802. 区间和(原题链接)
假定有一个无限长的数轴,数轴上每个坐标上的数都是 00。
现在,我们首先进行 n𝑛 次操作,每次操作将某一位置 x𝑥 上的数加 c𝑐。
接下来,进行 m𝑚 次询问,每个询问包含两个整数 l𝑙 和 r𝑟,你需要求出在区间 [l,r][𝑙,𝑟] 之间的所有数的和。
输入格式
第一行包含两个整数 n和 m。
接下来 n 行,每行包含两个整数 x和 c。
再接下来 m行,每行包含两个整数 l和 r。
输出格式
共 m 行,每行输出一个询问中所求的区间内数字和。
数据范围
−109≤x≤109−109≤𝑥≤109,
1≤n,m≤1051≤𝑛,𝑚≤105,
−109≤l≤r≤109−109≤𝑙≤𝑟≤109,
−10000≤c≤10000输入样例:
3 3 1 2 3 6 7 5 1 3 4 6 7 8
输出样例:
8 0 5
这道题用前缀和也是可以做的(前提是数据范围比较小)
#include <iostream> #include <vector> #include <algorithm> using namespace std; typedef pair<int, int> PII; const int N = 300010; int n, m; int a[N], s[N]; vector<int> alls; vector<PII> add, query; 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; } 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()); // 处理插入 for (auto item : add) { int x = find(item.first); a[x] += item.second; } // 预处理前缀和 for (int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i]; // 处理询问 for (auto item : query) { int l = find(item.first), r = find(item.second); cout << s[r] - s[l - 1] << endl; } return 0; }
离散化模板
vector<int> alls; //存储所有待离散化的值
sort(alls.begin(),alls.end()); //将所有值排序
alls.erase(unique(alls.begin(),alls.end()),alls.end()); //去掉重复元素
//二分求出x对应的离散化的值
int find(int x) //找到第一个大于等于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;
}