算法 · 入门进阶 · 前置:排序、差分与左闭右开区间 · 约 16 分钟
坐标压缩:保留顺序,别弄丢长度
把稀疏端点搬到连续下标,再沿事件扫描。三个压缩单元,不一定只有三单位长。
同一批端点,两把不同的尺
0/6 · 准备事件真实覆盖长度 = Σ(覆盖数 > 0 时的 X[j+1] − X[j])= 0(当前累计)
Δx 一直来自原坐标。拖动布局不会改变答案;下方方块的画面宽度不是计量单位。
收集端点,排序去重:X = [2, 4, 5, 10]。相同端点只留一个位置。
滑块支持方向键及 Home / End。改变区间会重置步骤;窄屏可横向查看完整图形。减少动画时关闭播放与过渡,仍可拖动或单步。
同一步,对照事件与代码
X = [2, 4, 5, 10];D = [1, 1, -1, -1]。最后一个端点只有结束事件,右边没有要计长的单元。
// 输入半开区间:[2,5), [4,10)
X = sort_unique(all_endpoints);
D = zeros(X.size());
for ([l,r] : intervals) {
D[rank(l)] += 1; D[rank(r)] -= 1;
}
active = 0; length = 0;
for (j = 0; j + 1 < X.size(); ++j) {
active += D[j];
if (active > 0) length += X[j+1] - X[j];
}
// 等待扫描
// 当前累计 length = 0;rank 差不是长度先预测:压缩后的三格,为什么是 8 而不是 3?
[2,5) 与 [4,10) 分成 [2,4)、[4,5)、[5,10),宽度依次是 2、1、5。覆盖数 1、2、1 都大于0,所以并集长度是 2+1+5=8;中间一段不乘2。
看完图,带走这些结论
坐标压缩把排序去重后的坐标X[j]映射为连续下标j,保留大小顺序与相等关系,不保留坐标差。计算半开区间的并集长度时,在起点加1、终点减1;先累加当前位置事件,再对覆盖数大于0的单元加上X[j+1]−X[j],而不是加1。
适用条件与边界
本课计算数轴上有限个左闭右开区间[l,r)的连续长度,要求l≤r。空区间[a,a)长度0;负坐标合法。演示使用两个区间和预设,模型支持空集合、至多1000段、−10⁹到10⁹的整数端点,拒绝逆序、非整数、无穷与越界输入。整数只是保证计算精确,不把问题变成统计整数点。
始终保持的规律
应用D[j]后,active等于整个[X[j],X[j+1])的覆盖数。压缩秩只负责定位,X负责保留原长度;累计值等于已扫描正覆盖单元的真实宽度之和。
逐步例子
- 从原创例子[2,5)、[4,10)开始,收集2、5、4、10,数值排序去重得到X=[2,4,5,10],对应秩0、1、2、3。
- 在每段左端的秩加1、右端的秩减1,得到D=[1,1,−1,−1]。同一坐标的事件要累加,不能相互覆盖。空区间的+1与−1在同点抵消。
- 经过X[0]=2,active=1,整段[2,4)都被覆盖,长度是4−2=2。经过4再加1,active=2,[4,5)仍只计一次,贡献5−4=1。
- 经过5减1,active=1,[5,10)贡献10−5=5。总并集长度2+1+5=8,不是压缩后的3格,也不是按重数加权的9。
- 正确性:所有成员变化都只发生在收集的端点。单个区间对边界x的前缀贡献是1(x≥l)−1(x≥r),恰在l≤x<r时为1;逐段相加便是右侧单元的覆盖数。
- 相邻端点形成的半开单元不重不漏;每个正覆盖单元按原宽度计一次,所以结果恰是并集长度。最后一个端点没有右侧单元,不可访问X[m]或凭空补一格。
- 相接[0,3)、[3,8)在3的事件抵消,没有正长度空隙;重复[2,5)两次只把覆盖数变成2,长度仍3。只有[4,4)时没有正长度单元,答案0。
复杂度与数值边界
n段至多2n个不同端点。排序与二分找秩的通用实现为O(n log n)时间、O(n)额外空间;本演示用Map保存秩,查找按通常的哈希表期望常数时间分析,排序仍占主导。并集完全落在最小与最大端点之间,在模型±10⁹范围内长度至多2×10⁹,可由JavaScript Number精确表示。仅检查每个端点是安全整数不足以保证它们的差安全;更大整数要用BigInt或检查过的宽整数。C++应在相减前把端点转成足够宽的整数;不要用位运算压成32位。
常见误区
- 把压缩下标差当真实长度:秩2与秩3只差1,但X[3]−X[2]=10−5=5。原坐标数组不能丢。
- 先算单元再应用左边界事件:左闭右开语义要求先加D[j],然后判断[X[j],X[j+1])的覆盖数。
- 把覆盖数乘上长度来算并集:那得到按重数计算的长度。并集只判断active>0,每段计一次。
- 把所有格都加起来:分离区间之间active=0的单元不应计入。负坐标不导致负宽度,排序后相邻原坐标差仍为正。
- 混淆连续长度与离散点数:实数闭区间[2,5]长度3,而整数点有4个。离散闭区间转[l,r+1)必须先确认对象是整数点并检查r+1溢出;不能无条件加1。
- 把一段动画当作等距变换:压缩只保序,改变了画面比例。几何距离、比例和面积一般不能在秩坐标里直接照算。
自测与答案
[−4,−1)与[2,6)有三个压缩单元。总覆盖长度是多少?如果重复第一段,答案改变吗?
单元宽度是3、3、4,覆盖数1、0、1,所以总长3+4=7,中间空隙不算。重复第一段后覆盖数2、0、1,但并集仍长7。
边界问答:长度相等,是否就完全覆盖?
- 目标的长度和已覆盖长度相等,能否说明每个目标点都被覆盖? 不能仅凭长度判断。原创反例:目标是实数闭区间[2,2],没有选择任何覆盖区间。两边长度都为0,但点2仍未被覆盖。闭区间[2,2]包含一个点;本课半开区间[2,2)才是空集。即使已确认覆盖集合包含于目标集合,长度相等也可能漏掉零长度的孤立点。
- 把整数端点压缩后,怎样避免漏掉实数点或端点? 先区分目标是连续长度、整数点数,还是覆盖每个实数点。实数闭区间[1,2]与[3,4]没有覆盖中间的2.5,不能因为整数2与3相邻就当作无缝相接。检查全部实数点覆盖时,除了相邻端点之间的开区间,还要按原题开闭约定检查端点本身;不能直接套本课只累计正长度单元的结果,也不能无条件把右端点改成r+1。
从本课到真实题目:保留跨度,再处理空隙
2026年10月17日结束的ABC480 C官方讲解明确使用坐标压缩与前缀计数定位未覆盖时间段。这提供了具体的学习应用,不等于有可核实的搜索量或热度。官方讲解的单独发布日期未确认。
本课的区间并集例子是独立原创的准备实验,不是该题的完整解答。原题要求最少额外按键次数,还要结合持续时间、目标时间窗及未覆盖空隙处理,不能拿这里的长度直接当答案。
连续长度不受有限个端点是否计入的影响,但覆盖事件约定、瞬时状态和离散点计数仍必须各自定义。原题的供水瞬间规则应读原题,不能直接用本课半开区间替换而不检查。