Appearance
Minimal Segment Cover -- CF 1175E
Difficulty: 2000 * Techniques: greedy interval covering, binary lifting, prefix-max sweep Problem Link
Quick Revisit
- PROBLEM: Minimum segments to cover [x, y], answering many queries
- KEY INSIGHT: Greedy jump function (reach[p] = farthest right from p) + binary lifting for O(log n) per query
- TECHNIQUES: greedy interval covering, binary lifting, prefix-max sweep
- TRANSFER: "Repeated function application" = binary lifting. Interval covering + fast queries = this template.
First Read
We have
Classic interval covering -- the greedy is well-known. But
The Naive Greedy
Standard interval covering: start at
Each "jump" is: from position
But in the worst case, we might make
The Aha Moment: Binary Lifting
Each jump is a function: position
Define:
-- one jump from . -- jumps from .
To answer query
I love when two seemingly unrelated ideas (interval greedy + binary lifting from LCA) combine like this.
Building the reach Array
We don't want to store all segments and scan them. Instead:
- For each coordinate
: over segments with . - Then sweep left to right:
.
After the sweep,
Coordinate convention. This problem uses closed integer intervals
Answering a Query
From position
Implementation Notes
- Coordinates up to
, so the array is manageable. should start as (you're already at , so worst case you reach ). If after processing, it means no segment covers , so a query requiring coverage of is impossible. LOG = 19works because, but it is brittle as a teaching constant -- one tweak to MAXCand the table silently truncates. Prefer a computedLOG = __lg(MAXC) + 1or a visibly conservativeLOG = 20(or 21) so the code stays correct under coordinate changes.
The Code
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXC = 5e5 + 1, LOG = 19;
int reach[MAXC + 1];
int jp[MAXC + 1][LOG];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i <= MAXC; i++) reach[i] = i;
for (int i = 0; i < n; i++) {
int l, r; scanf("%d%d", &l, &r);
reach[l] = max(reach[l], r);
}
for (int i = 1; i <= MAXC; i++)
reach[i] = max(reach[i], reach[i - 1]);
for (int i = 0; i <= MAXC; i++) jp[i][0] = reach[i];
for (int k = 1; k < LOG; k++)
for (int i = 0; i <= MAXC; i++)
jp[i][k] = jp[min(jp[i][k-1], MAXC)][k-1];
while (m--) {
int x, y; scanf("%d%d", &x, &y);
if (reach[x] == x && x != y) { printf("-1\n"); continue; }
int ans = 0, cur = x;
for (int k = LOG - 1; k >= 0; k--) {
if (jp[cur][k] < y) {
cur = jp[cur][k];
ans += (1 << k);
}
}
if (cur < y) { cur = jp[cur][0]; ans++; }
printf("%d\n", cur >= y ? ans : -1);
}
}Transfer Lesson
- Repeated application of a function -- whenever you're "jumping" from state to state and need to do it fast, binary lifting is the tool.
- Interval covering queries = greedy jump function + binary lifting. This is a reusable template.
- Preprocessing the
reacharray with a prefix-max sweep avoids sorting or scanning segments per query.
See also: Greedy | Binary Lifting / LCA | Prefix Sums | Pattern Recognition Guide | Practice Ladder 1700-2100