离散化是将大坐标映射为紧凑编号:提取所有操作涉及的坐标(x、l、r),排序去重后按顺序赋1,2,3…编号;用lower_bound二分查找实现O(log n)映射;后续算法在编号空间运行,结果仍为原意义数值。

直接用数组下标表示超大坐标(比如 −10⁹ 到 10⁹)根本不可行——数组开不了那么大,内存会爆,索引也越界。离散化不是“缩放”或“四舍五入”,而是把所有实际出现的坐标值提取出来,排序去重后,按顺序赋予它们 1、2、3…… 这样的紧凑编号,让原坐标和编号一一对应,后续所有操作都基于这个小范围编号数组进行。
只保留真正用到的坐标点
离散化的起点不是原始数据本身,而是所有“会被访问或修改”的坐标位置。比如你在数轴上要: - 在位置 x 处加一个数(修改操作) - 查询区间 [l, r] 的和(查询操作) 那 x、l、r 就是必须纳入离散化的关键值。哪怕整个数轴有百亿个点,你真正动的可能就几百个——这些才是 alls 数组要收的全部元素。
- 别漏掉任何一次操作涉及的坐标,包括修改点、查询左端点、查询右端点
- 即使某个坐标重复出现多次(如多个操作都在 x=5),alls 里也只存一次,靠后续去重保证
- 负数、零、极大正数一视同仁,统统进 alls,排序后自然排好序
排序 + 去重生成紧凑编号表
对 alls 排序后去重,得到的就是一个严格递增、无重复的坐标快照列表。它的长度就是离散化后的最大编号,也是你后续数组(如差分数组 a[] 或前缀和 s[])需要开的大小。
- 排序用 sort(alls.begin(), alls.end()) 即可
- 去重用 unique + erase 组合:alls.erase(unique(alls.begin(), alls.end()), alls.end())
- 此时 alls[i] 表示“第 i+1 小的原始坐标”,而 i+1 就是该坐标的离散化编号(从 1 开始更方便前缀和)
用二分快速完成坐标→编号映射
每次遇到原始坐标 x,不能遍历 alls 查找,要用 lower_bound 或手写二分,定位 x 在 alls 中首次出现的位置(即第一个 ≥x 的下标),再 +1 得到编号。
- 标准写法:int pos = lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;
- 手写二分也行,逻辑一致:找最左边界,确保相等值映射到同一编号
- 这个 pos 就是你能安全使用的数组下标,比如 a[pos] += c,s[pos] -= c 等
在编号空间上跑算法,结果还原回原意义
离散化完成后,所有计算都在编号 1~alls.size() 范围内进行。比如做差分+前缀和求区间和: - 对每个修改 (x, c),算出 pos = find(x),执行 a[pos] += c - 对每个查询 [l, r],算出 pl = find(l), pr = find(r),答案就是 s[pr] − s[pl−1]
- 注意:s 数组是前缀和,下标从 1 开始,s[0]=0,所以 s[pl−1] 才是 l 左侧的和
- 只要所有 l、r 都进了 alls,find 就一定能返回合法编号,不会越界
- 最终输出的是数值结果(如区间和),不是编号——编号只是中间工具

















