You have coordinates, timestamps, or array values in 1..1e9, but only n ≤ 2e5 of them actually appear. A Fenwick tree, a segment tree, or a dense boolean array wants an index in 0..k-1, not a hole-ridden 1e9 universe.
Coordinate compression is the rename: replace every value x with its rank among the distinct values. Order is preserved. Distances are not — ranks are adjacent even if the originals were 2 and 10^9.
function uniqueSorted(a) {
return [...new Set(a)].sort((x, y) => x - y);
}
// first index i with uniq[i] >= x, assuming uniq is sorted
function lowerBound(uniq, x) {
let lo = 0;
let hi = uniq.length;
while (lo < hi) {
const mid = lo + ((hi - lo) >> 1);
if (uniq[mid] < x) lo = mid + 1;
else hi = mid;
}
return lo;
}
function compress(a) {
const uniq = uniqueSorted(a);
return a.map((x) => lowerBound(uniq, x));
}In C++ the same two lines are sort(all(v)); v.erase(unique(all(v)), v.end()); then int id = lower_bound(all(v), x) - v.begin();. A hash map from value → rank also works if you only look up values that exist. Query endpoints that might fall between keys still need lower_bound (first rank ≥ L, first rank > R) so a range on the original line maps onto a dense segment.
A hash map alone does not give you 'the next key after L'. Sorting does.
Checks 0/3