CF 106239K - 线段覆盖
We are given a sorted list of distinct points on a number line. The task is to cover all these points using at most $k$ segments, where a segment can be any interval $[a,b]$ and its cost is its geometric length $ A useful way to reframe the problem is to think of grouping points.
CF 106239K - \u7ebf\u6bb5\u8986\u76d6
Rating: -
Tags: -
Solve time: 46s
Verified: yes
Solution
Problem Understanding
We are given a sorted list of distinct points on a number line. The task is to cover all these points using at most $k$ segments, where a segment can be any interval $[a,b]$ and its cost is its geometric length $|a-b|$. A segment covers a point if the point lies between its endpoints. The goal is to minimize the total length of all chosen segments, and we must report the optimal value for every $k$ from 1 to $n$.
A useful way to reframe the problem is to think of grouping points. If we decide to cover a consecutive group of points $x_l, x_{l+1}, \dots, x_r$ with one segment, the cheapest possible segment is exactly $[x_l, x_r]$, and its cost is $x_r - x_l$. Any optimal solution will always use segments that cover contiguous blocks of the sorted array, because any segment spanning non-contiguous points can be shortened without losing coverage.
The constraints allow up to $2 \times 10^5$ points, which immediately rules out any solution that tries all partitions or uses $O(n^2)$ dynamic programming transitions directly. We need something closer to linear or $O(n \log n)$, and we also need answers for all $k$, which suggests building a global structure once rather than recomputing per $k$.
A subtle point appears when thinking about $k=1$. The only segment must cover all points, so the answer is $x_n - x_1$. For larger $k$, we are effectively allowed to split the array into multiple segments, paying only for the spans inside each segment.
A naive approach might try all ways to split the array into $k$ groups for each $k$, but that leads to combinatorial explosion. Even computing a single $k$-partition optimally with DP costs $O(n^2 k)$ or $O(n^2)$, which is too large.
Approaches
The key observation is that splitting a segment reduces cost in a very structured way. If we cover all points with one segment, the cost is $x_n - x_1$. Suppose we insert a split between $x_i$ and $x_{i+1}$. Instead of one segment $[x_1, x_n]$, we now use two segments $[x_1, x_i]$ and $[x_{i+1}, x_n]$. The new cost becomes
$$(x_i - x_1) + (x_n - x_{i+1})$$
compared to
$$x_n - x_1.$$
The difference is
$$(x_n - x_1) - [(x_i - x_1) + (x_n - x_{i+1})] = x_{i+1} - x_i.$$
This means every possible split between adjacent points has a “saving” equal to the gap between them. A split removes that gap from the total cost because the segment no longer spans across it.
So the problem becomes equivalent to this: we start with cost $x_n - x_1$, and we are allowed to insert up to $k-1$ splits. Each split at position $i$ gives a gain of $x_{i+1} - x_i$. To minimize total cost, we want to maximize the sum of chosen gains, which is achieved by picking the largest gaps.
Thus, for each $k$, the optimal strategy is to choose the $k-1$ largest adjacent gaps and subtract their sum from $x_n - x_1$.
This reduces the problem to sorting gaps once and computing prefix sums, after which each answer is a simple subtraction.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force DP over partitions | $O(n^2)$ or worse | $O(n)$ | Too slow |
| Sort gaps + prefix sums | $O(n \log n)$ | $O(n)$ | Accepted |
Algorithm Walkthrough
- Compute all adjacent differences $d_i = x_{i+1} - x_i$ for $i = 1$ to $n-1$.
These represent the savings obtained if we decide to split between those two points, since a split removes that gap from being covered by a single segment. 2. Sort the array $d$ in descending order.
We want to prioritize the largest savings first because each split is independent and contributes additively to the total improvement.
3. Build a prefix sum array over the sorted gaps, where pref[i] is the sum of the largest $i$ gaps.
This allows us to answer any $k$ efficiently without recomputing sums. 4. Initialize the base cost as $x_n - x_1$, which corresponds to covering everything with a single segment. 5. For each $k$ from 1 to $n$, output:
$$(x_n - x_1) - \text{pref}[k-1]$$
because using $k$ segments means inserting exactly $k-1$ splits.
Why it works
Every valid segmentation corresponds to choosing a subset of gaps where cuts are placed. Each cut removes exactly one adjacent gap from being included in a spanning segment. Since gaps are independent and do not interact, the total improvement is additive. The optimal solution is therefore obtained by selecting the largest $k-1$ improvements, which is exactly what sorting guarantees. No alternative structure can yield a larger reduction because any rearrangement of cuts does not change the multiset of chosen gaps, only which ones are included.
Python Solution
import sys
input = sys.stdin.readline
n = int(input().strip())
x = list(map(int, input().split()))
if n == 1:
print(0)
sys.exit()
gaps = [x[i+1] - x[i] for i in range(n - 1)]
gaps.sort(reverse=True)
pref = [0] * (n)
for i, v in enumerate(gaps, start=1):
pref[i] = pref[i - 1] + v
base = x[-1] - x[0]
ans = []
for k in range(1, n + 1):
ans.append(str(base - pref[k - 1]))
print(" ".join(ans))
The implementation directly mirrors the transformation from segment partitioning to selecting gaps. The only care needed is indexing: there are $n-1$ gaps but answers require $k-1$ of them, so prefix array is built with length $n$ to safely include the zero case.
The base cost is computed once, and every query is a constant-time subtraction.
Worked Examples
Example 1
Input:
3
1 2 3
Gaps are $[1, 1]$, sorted as $[1, 1]$, prefix sums $[0, 1, 2]$. Base cost is $3 - 1 = 2$.
| k | chosen gaps | sum | answer |
|---|---|---|---|
| 1 | none | 0 | 2 |
| 2 | 1 gap | 1 | 1 |
| 3 | 2 gaps | 2 | 0 |
Output:
2 1 0
This shows that each additional segment removes one boundary gap, gradually reducing total cost to zero when every point is isolated.
Example 2
Input:
4
1 3 6 10
Gaps are $[2, 3, 4]$, sorted $[4, 3, 2]$, prefix sums $[0, 4, 7, 9]$. Base cost is $9$.
| k | chosen gaps | sum | answer |
|---|---|---|---|
| 1 | none | 0 | 9 |
| 2 | 4 | 4 | 5 |
| 3 | 4,3 | 7 | 2 |
| 4 | 4,3,2 | 9 | 0 |
This trace highlights that optimal segmentation always cuts at the largest separations first, producing the most balanced cost reduction per additional segment.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | $O(n \log n)$ | Sorting the $n-1$ gaps dominates the runtime |
| Space | $O(n)$ | Storage for gaps and prefix sums |
The solution comfortably fits the constraints since $n \le 2 \times 10^5$, and sorting plus linear prefix computation is well within typical limits.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
import sys as _sys
input = _sys.stdin.readline
n = int(input().strip())
x = list(map(int, input().split()))
if n == 1:
return "0"
gaps = [x[i+1] - x[i] for i in range(n - 1)]
gaps.sort(reverse=True)
pref = [0] * (n)
for i, v in enumerate(gaps, start=1):
pref[i] = pref[i - 1] + v
base = x[-1] - x[0]
ans = []
for k in range(1, n + 1):
ans.append(str(base - pref[k - 1]))
return " ".join(ans)
# provided samples
assert run("3\n1 2 3\n") == "2 1 0"
# minimum size
assert run("1\n100\n") == "0"
# simple spaced points
assert run("2\n1 10\n") == "9 0"
# all equal gaps
assert run("5\n1 2 3 4 5\n") == "4 3 2 1 0"
# large uneven gaps
assert run("4\n1 2 100 101\n") == "100 1 0 0"
| Test input | Expected output | What it validates |
|---|---|---|
| single point | 0 | base case handling |
| two distant points | 9 0 | single gap behavior |
| uniform spacing | 4 3 2 1 0 | consistent greedy reduction |
| mixed large gap | 100 1 0 0 | correctness of sorting gaps |
Edge Cases
For a single point, the algorithm produces no gaps and directly outputs zero, since base cost is zero and no splits are needed.
For example input:
1
42
The gap list is empty, prefix sums are all zero, and every $k$ returns zero. The algorithm never attempts to access invalid indices because the prefix array is sized to $n$, and $k-1$ ranges safely from 0 to 0.
For closely packed points such as:
3
10 11 12
gaps are $[1,1]$, and the algorithm selects them in order. For $k=2$, one gap is removed, reducing cost from 2 to 1, matching the interpretation that a single cut removes exactly one unit of span.
For highly unbalanced spacing such as:
3
1 2 100
gaps are $[1, 98]$. The optimal strategy for $k=2$ selects the large gap first, removing 98 from the total span, leaving only 1, which matches splitting at the dominant separation.