Describe the Painting
MedPrompt
There is a long and thin painting that can be represented by a number line. The painting was painted with multiple overlapping segments where each segment was painted with a unique color. You are given a 2D integer array segments, where segments[i] = [starti, endi, colori] represents the half-closed segment [starti, endi) with colori as the color.
The colors in the overlapping segments of the painting were mixed when it was painted. When two or more colors mix, they form a new color that can be represented as a set of mixed colors.
- For example, if colors
2,4, and6are mixed, then the resulting mixed color is{2,4,6}.
For the sake of simplicity, you should only output the sum of the elements in the set rather than the full set.
You want to describe the painting with the minimum number of non-overlapping half-closed segments of these mixed colors. These segments can be represented by the 2D array painting where painting[j] = [leftj, rightj, mixj] describes a half-closed segment [leftj, rightj) with the mixed color sum of mixj.
- For example, the painting created with
segments = [[1,4,5],[1,7,7]]can be described bypainting = [[1,4,12],[4,7,7]]because:[1,4)is colored{5,7}(with a sum of12) from both the first and second segments.[4,7)is colored{7}from only the second segment.
Return the 2D array painting describing the finished painting (excluding any parts that are not painted). You may return the segments in any order.
A half-closed segment [a, b) is the section of the number line between points a and b including point a and not including point b.
Example 1:
Input: segments = [[1,4,5],[4,7,7],[1,7,9]]
Output: [[1,4,14],[4,7,16]]
Explanation: The painting can be described as follows:
- [1,4) is colored {5,9} (with a sum of 14) from the first and third segments.
- [4,7) is colored {7,9} (with a sum of 16) from the second and third segments.Example 2:
Input: segments = [[1,7,9],[6,8,15],[8,10,7]]
Output: [[1,6,9],[6,7,24],[7,8,15],[8,10,7]]
Explanation: The painting can be described as follows:
- [1,6) is colored 9 from the first segment.
- [6,7) is colored {9,15} (with a sum of 24) from the first and second segments.
- [7,8) is colored 15 from the second segment.
- [8,10) is colored 7 from the third segment.Example 3:
Input: segments = [[1,4,5],[1,4,7],[4,7,1],[4,7,11]]
Output: [[1,4,12],[4,7,12]]
Explanation: The painting can be described as follows:
- [1,4) is colored {5,7} (with a sum of 12) from the first and second segments.
- [4,7) is colored {1,11} (with a sum of 12) from the third and fourth segments.
Note that returning a single segment [1,7) is incorrect because the mixed color sets are different.
Constraints:
1 <= segments.length <= 2 * 104segments[i].length == 31 <= starti < endi <= 1051 <= colori <= 109- Each
coloriis distinct.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach simulates the painting process directly on a number line represented by an array. It iterates through every point for every segment, accumulating the color values. This is straightforward but highly inefficient for large coordinates.
Algorithm
- Find the maximum coordinate
max_coordfrom all segments. - Create a
longarraycolor_sumsof sizemax_coord + 1. - For each segment
[start, end, color]:- For
ifromstarttoend - 1,color_sums[i] += color.
- For
- Initialize an empty list
paintingfor the results. - Iterate from
i = 1tomax_coord:- If the current point
ihas a color sum of 0, skip it. - Identify the start of a new colored segment:
start = i. - Find the end of this segment by advancing a pointer
jas long ascolor_sums[j]equalscolor_sums[start]. - Add the new segment
[start, j, color_sums[start]]to thepaintinglist. - Continue the main loop from
i = j.
- If the current point
- Return the
paintinglist.
Walkthrough
We first determine the maximum coordinate max_coord present in the input segments. An array, let's call it color_sums, of size max_coord + 1 is created to store the final mixed color sum for each point on the number line. We then iterate through each segment [start, end, color]. For each segment, we loop from start to end - 1 and add the color to the corresponding index in the color_sums array. After processing all segments, the color_sums array holds the final mixed color sum for every point. The final step is to scan this color_sums array to consolidate adjacent points with the same color sum into single segments. We iterate through the array, and whenever we find a contiguous block of points with the same non-zero color sum, we form a new segment.
import java.util.ArrayList;import java.util.Arrays;import java.util.List; class Solution { public List<List<Long>> splitPainting(int[][] segments) { int maxCoord = 0; for (int[] segment : segments) { maxCoord = Math.max(maxCoord, segment[1]); } long[] colorSums = new long[maxCoord + 1]; for (int[] segment : segments) { int start = segment[0]; int end = segment[1]; int color = segment[2]; for (int i = start; i < end; i++) { colorSums[i] += color; } } List<List<Long>> result = new ArrayList<>(); int i = 1; while (i <= maxCoord) { if (colorSums[i] == 0) { i++; continue; } long currentSum = colorSums[i]; int start = i; int j = i; while (j <= maxCoord && colorSums[j] == currentSum) { j++; } result.add(Arrays.asList((long)start, (long)j, currentSum)); i = j; } return result; }}Complexity
Time
O(N * M), where N is the number of segments and M is the maximum coordinate. Populating the `colorSums` array dominates the runtime, as for each segment, we might iterate up to M times.
Space
O(M), where M is the maximum coordinate value. This is for storing the `colorSums` array.
Trade-offs
Pros
Simple to understand and implement.
Cons
Very inefficient in terms of time and space if the coordinate range is large.
Leads to Time Limit Exceeded on most platforms for the given constraints.
This approach treats the start and end points of segments as events along the number line. By processing these events in sorted order of their position, we can efficiently calculate the mixed color sum for the intervals between consecutive event points.
Algorithm
- Create a
TreeMap<Integer, Long>to store color changes at each coordinate. The map will automatically keep coordinates sorted. - For each segment
[start, end, color]:- Add
colorto the value at keystart. - Subtract
colorfrom the value at keyend.
- Add
- Initialize an empty list
painting, along current_sum = 0, and anint last_pos. - Iterate through the
(pos, change)entries of theTreeMap:- If
last_posis valid and thecurrent_sumis positive, it means the interval[last_pos, pos)was painted. Create a segment[last_pos, pos, current_sum]. - Before adding, check if it can be merged with the last segment in
painting. Merging is possible if the new segment starts exactly where the last one ended and they have the same color sum. - If mergeable, update the end of the last segment. Otherwise, add the new segment.
- Update
current_sumby addingchange. - Update
last_postopos.
- If
- Return the
paintinglist.
Walkthrough
The core idea is that the mixed color sum only changes at the start or end points of the input segments. These points are our 'event points'. We can represent each segment [start, end, color] as two events: a color addition +color at start, and a color subtraction -color at end. We use a data structure that keeps these events sorted by coordinate, like a TreeMap in Java, where keys are coordinates and values are the net color change at that coordinate. After populating the map, we iterate through its entries, which are already sorted by coordinate. We maintain a current_color_sum and the last_pos. Between two consecutive event points last_pos and current_pos, the color sum is constant. If this sum is positive, we form a segment [last_pos, current_pos, current_color_sum]. We also handle merging adjacent segments that happen to have the same mixed color sum to ensure the output is minimal.
import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.TreeMap; class Solution { public List<List<Long>> splitPainting(int[][] segments) { Map<Integer, Long> map = new TreeMap<>(); for (int[] s : segments) { map.put(s[0], map.getOrDefault(s[0], 0L) + s[2]); map.put(s[1], map.getOrDefault(s[1], 0L) - s[2]); } List<List<Long>> result = new ArrayList<>(); long currentSum = 0; int lastPos = -1; for (Map.Entry<Integer, Long> entry : map.entrySet()) { int pos = entry.getKey(); long change = entry.getValue(); if (lastPos != -1 && pos > lastPos) { if (currentSum > 0) { if (!result.isEmpty() && result.get(result.size() - 1).get(1) == (long)lastPos && result.get(result.size() - 1).get(2) == currentSum) { result.get(result.size() - 1).set(1, (long)pos); } else { result.add(Arrays.asList((long)lastPos, (long)pos, currentSum)); } } } currentSum += change; lastPos = pos; } return result; }}Complexity
Time
O(N log N), where N is the number of segments. Populating the `TreeMap` takes `O(N log K)` where `K` is the number of unique endpoints (`K <= 2N`), so it's effectively `O(N log N)`. Iterating through the map takes `O(K)` or `O(N)`.
Space
O(N), where N is the number of segments. This space is used to store the events in the `TreeMap`.
Trade-offs
Pros
Much more efficient than brute force when coordinates are sparse or the range is large.
Space usage depends only on the number of segments, not the coordinate range.
Cons
The
log Nfactor from sorting or using a tree-based map makes it slightly slower than the difference array approach for the given constraints whereMis not excessively larger thanN.
This approach is an optimization over the brute-force method. Instead of updating every point in a segment, we only mark the changes at the start and end points in a 'difference array'. Then, a single pass to compute prefix sums gives the final color sum for each point. This is highly efficient when the maximum coordinate is not excessively large.
Algorithm
- Find the maximum end coordinate
max_coord. - Create a
longarraydiffof sizemax_coord + 2. - For each segment
[s, e, c], performdiff[s] += canddiff[e] -= c. - Create a
longarraycolorSumsof sizemax_coord + 1. - Compute the prefix sums of
diffto populatecolorSums:colorSums[i] = colorSums[i-1] + diff[i](conceptually, thoughdiffis used directly in the code). - Scan the
colorSumsarray to build the finalpaintinglist by grouping adjacent points with the same non-zero color sum into segments.
Walkthrough
This method avoids the O(N*M) complexity of the naive approach by being smarter about updates. First, we find the maximum coordinate max_coord. We create a difference array diff of size max_coord + 2. For each segment [start, end, color], we perform two operations: diff[start] += color and diff[end] -= color. This marks that from start onwards, the color sum increases by color, and from end onwards, this contribution is removed. After processing all segments, we can determine the actual color sum at each point i by calculating the prefix sum of the diff array. We can build the final colorSums array and then scan it to form the segments, similar to the brute-force approach's final step. This combines the efficiency of the difference array with a simple segmentation logic.
import java.util.ArrayList;import java.util.Arrays;import java.util.List; class Solution { public List<List<Long>> splitPainting(int[][] segments) { int maxCoord = 0; for (int[] s : segments) { maxCoord = Math.max(maxCoord, s[1]); } long[] diff = new long[maxCoord + 2]; for (int[] s : segments) { diff[s[0]] += s[2]; diff[s[1]] -= s[2]; } // First, create the final color sum array from the difference array long[] colorSums = new long[maxCoord + 1]; long currentSum = 0; for (int i = 1; i <= maxCoord; i++) { currentSum += diff[i]; colorSums[i] = currentSum; } // Second, build segments from the color sum array List<List<Long>> painting = new ArrayList<>(); int i = 1; while (i <= maxCoord) { if (colorSums[i] == 0) { i++; continue; } long sum = colorSums[i]; int j = i; while (j <= maxCoord && colorSums[j] == sum) { j++; } painting.add(Arrays.asList((long)i, (long)j, sum)); i = j; } return painting; }}Complexity
Time
O(N + M), where N is the number of segments and M is the maximum coordinate. It takes `O(N)` to find `maxCoord` and populate the difference array, `O(M)` to compute prefix sums, and `O(M)` to build the final segments.
Space
O(M), where M is the maximum coordinate. This is for the difference array and the final color sum array.
Trade-offs
Pros
Very fast for the given constraints.
Linear time complexity with respect to the number of segments and the maximum coordinate.
Cons
Requires space proportional to the maximum coordinate, which can be an issue if coordinates are not bounded reasonably.
Solutions
Solution
class Solution {public List<List<Long>> splitPainting(int[][] segments) { TreeMap<Integer, Long> d = new TreeMap<>(); for (int[] e : segments) { int l = e[0], r = e[1], c = e[2]; d.put(l, d.getOrDefault(l, 0L) + c); d.put(r, d.getOrDefault(r, 0L) - c); } List<List<Long>> ans = new ArrayList<>(); long i = 0, j = 0; long cur = 0; for (Map.Entry<Integer, Long> e : d.entrySet()) { if (Objects.equals(e.getKey(), d.firstKey())) { i = e.getKey(); } else { j = e.getKey(); if (cur > 0) { ans.add(Arrays.asList(i, j, cur)); } i = j; } cur += e.getValue(); } return ans; }}class Solution {public: vector<vector<long long>> splitPainting(vector<vector<int>> &segments) { map<int, long long> d; for (auto &e : segments) { int l = e[0], r = e[1], c = e[2]; d[l] += c; d[r] -= c; } vector<vector<long long>> ans; long long i, j, cur = 0; for (auto &it : d) { if (it == *d.begin()) i = it.first; else { j = it.first; if (cur > 0) ans.push_back({i, j, cur}); i = j; } cur += it.second; } return ans; }};class Solution: def splitPainting(self, segments: List[List[int]]) -> List[List[int]]: d = defaultdict(int) for l, r, c in segments: d[l] += c d[r] -= c s = sorted([[k, v] for k, v in d . items()]) n = len(s) for i in range(1, n): s[i][1] += s[i - 1][1] return [[s[i][0], s[i + 1][0], s[i][1]] for i in range(n - 1) if s[i][1]]Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.