There are several squares being dropped onto the X-axis of a 2D plane.
You are given a 2D integer array positions
where positions[i] = [lefti, sideLengthi]
represents the ith
square with a side length of sideLengthi
that is dropped with its left edge aligned with X-coordinate lefti
.
Each square is dropped one at a time from a height above any landed squares. It then falls downward (negative Y direction) until it either lands on the top side of another square or on the X-axis. A square brushing the left/right side of another square does not count as landing on it. Once it lands, it freezes in place and cannot be moved.
After each square is dropped, you must record the height of the current tallest stack of squares.
Return an integer array ans
where ans[i]
represents the height described above after dropping the ith
square.
Example 1:
Input: positions = [[1,2],[2,3],[6,1]] Output: [2,5,5] Explanation: After the first drop, the tallest stack is square 1 with a height of 2. After the second drop, the tallest stack is squares 1 and 2 with a height of 5. After the third drop, the tallest stack is still squares 1 and 2 with a height of 5. Thus, we return an answer of [2, 5, 5].
Example 2:
Input: positions = [[100,100],[200,100]] Output: [100,100] Explanation: After the first drop, the tallest stack is square 1 with a height of 100. After the second drop, the tallest stack is either square 1 or square 2, both with heights of 100. Thus, we return an answer of [100, 100]. Note that square 2 only brushes the right side of square 1, which does not count as landing on it.
Constraints:
1 <= positions.length <= 1000
1 <= lefti <= 108
1 <= sideLengthi <= 106
The problem involves simulating the dropping of squares onto the x-axis. Each square has a left edge position and a side length. Squares fall until they land on the x-axis or another square. The goal is to return an array where each element represents the maximum height of any stack of squares after each square is dropped.
The most efficient approach is to use a segment tree. A segment tree is a data structure that efficiently handles range queries and updates. In this case, we'll use it to track the maximum height at each point along the x-axis.
Why Segment Tree?
Segment Tree Implementation Details:
Node Structure: Each node in the segment tree represents a range of x-coordinates. It stores:
l
, r
: The start and end x-coordinates of the range.v
: The maximum height in this range.add
: A lazy propagation value (explained below). This helps efficiently propagate updates down the tree.Building the Tree: We start with a root node covering the entire x-axis. Nodes are recursively created to subdivide the range.
query(l, r)
: This function finds the maximum height within the range [l, r]. It recursively traverses the tree, using pushdown
to handle lazy updates.
modify(l, r, v)
: This function updates the maximum height in the range [l, r] to v. It uses pushdown
and pushup
to maintain the tree's consistency.
pushdown(node)
: This propagates the add
value down to the children of a node.
pushup(node)
: This updates the v
value of a node based on the v
values of its children.
Dynamic Node Creation: Because the x-coordinates can be large, we create nodes only when needed, making the space complexity more efficient.
class Node:
def __init__(self, l, r):
self.left = None
self.right = None
self.l = l
self.r = r
self.mid = (l + r) >> 1
self.v = 0
self.add = 0
class SegmentTree:
def __init__(self):
self.root = Node(1, int(1e9)) # Adjust range as needed
# ... (query, modify, pushup, pushdown methods as described above) ...
class Solution:
def fallingSquares(self, positions: List[List[int]]) -> List[int]:
ans = []
mx = 0
tree = SegmentTree()
for l, w in positions:
r = l + w - 1
h = tree.query(l, r) + w
mx = max(mx, h)
ans.append(mx)
tree.modify(l, r, h)
return ans
(Note: The complete query
, modify
, pushup
, and pushdown
methods for the SegmentTree
class would be added here. Refer to the detailed solution in the original response for the complete code in Python, Java, C++, Go and TypeScript.)
Time Complexity: O(n log n), where n is the number of squares. Each square's insertion involves a query and a modification operation in the segment tree, both of which take O(log n) time.
Space Complexity: O(n) in the worst case (although it can be less due to dynamic node creation). The space is mainly used to store the segment tree nodes. The size of the tree is related to the number of squares and the range of x-coordinates.
While other approaches like brute force are possible, they would be significantly less efficient (likely O(n^2) or worse) due to the need to repeatedly check for overlaps and calculate maximum heights. The segment tree provides the optimal time complexity for this problem.