<p>There are several squares being dropped onto the X-axis of a 2D plane.</p>

<p>You are given a 2D integer array <code>positions</code> where <code>positions[i] = [left<sub>i</sub>, sideLength<sub>i</sub>]</code> represents the <code>i<sup>th</sup></code> square with a side length of <code>sideLength<sub>i</sub></code> that is dropped with its left edge aligned with X-coordinate <code>left<sub>i</sub></code>.</p>

<p>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 <strong>on the top side of another square</strong> or <strong>on the X-axis</strong>. 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.</p>

<p>After each square is dropped, you must record the <strong>height of the current tallest stack of squares</strong>.</p>

<p>Return <em>an integer array </em><code>ans</code><em> where </em><code>ans[i]</code><em> represents the height described above after dropping the </em><code>i<sup>th</sup></code><em> square</em>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/04/28/fallingsq1-plane.jpg" style="width: 500px; height: 505px;" />
<pre>
<strong>Input:</strong> positions = [[1,2],[2,3],[6,1]]
<strong>Output:</strong> [2,5,5]
<strong>Explanation:</strong>
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].
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> positions = [[100,100],[200,100]]
<strong>Output:</strong> [100,100]
<strong>Explanation:</strong>
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.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>1 &lt;= positions.length &lt;= 1000</code></li>
	<li><code>1 &lt;= left<sub>i</sub> &lt;= 10<sup>8</sup></code></li>
	<li><code>1 &lt;= sideLength<sub>i</sub> &lt;= 10<sup>6</sup></code></li>
</ul>

class Node:
    def __init__(self, start, end):
        self.left = None  # left child of the interval
        self.right = None  # right child of the interval
        self.start = start  # start index of the interval
        self.end = end  # end index of the interval
        self.mid = (start + end) >> 1  # middle index for splitting intervals
        self.value = 0  # sum value of the current interval
        self.add = 0  # lazy propagation value to add to children nodes


class SegmentTree:
    def __init__(self):
        # Initialize the segment tree with a very large end index, representing positions
        self.root = Node(1, int(1e9))

    def modify(self, start, end, value, node=None):
        # Update the segment tree in the range [start, end] with value 'value'
        if start > end:
            return
        if node is None:
            node = self.root
        # If node's interval is completely within [start, end], update directly
        if node.start >= start and node.end <= end:
            node.value = value
            node.add = value
            return
        self.pushdown(node)
        # If intervals overlap, move down the tree
        if start <= node.mid:
            self.modify(start, end, value, node.left)
        if end > node.mid:
            self.modify(start, end, value, node.right)
        self.pushup(node)

    def query(self, start, end, node=None):
        # Query the maximum value in the range [start, end]
        if start > end:
            return 0
        if node is None:
            node = self.root
        if node.start >= start and node.end <= end:
            return node.value
        self.pushdown(node)
        max_value = 0
        if start <= node.mid:
            max_value = max(max_value, self.query(start, end, node.left))
        if end > node.mid:
            max_value = max(max_value, self.query(start, end, node.right))
        return max_value

    def pushup(self, node):
        # Recalculates the value of internal node after modifications to its children
        node.value = max(node.left.value, node.right.value)

    def pushdown(self, node):
        # Propagates updates to children nodes and creates them if they don't exist
        if node.left is None:
            node.left = Node(node.start, node.mid)
        if node.right is None:
            node.right = Node(node.mid + 1, node.end)
        if node.add:
            node.left.value = node.add
            node.right.value = node.add
            node.left.add = node.add
            node.right.add = node.add
            node.add = 0


class Solution:
    def fallingSquares(self, positions: List[List[int]]) -> List[int]:
        max_height = 0  # Track the maximum height so far
        result = []  # Store the resulting heights
        tree = SegmentTree()  # Initialize a new Segment Tree

        for left, size in positions:
            right = left + size - 1
            # Find current max height within [left, right] and add size to it
            height = tree.query(left, right) + size
            max_height = max(max_height, height)
            result.append(max_height)
            # Update the height in the segment tree
            tree.modify(left, right, height)

        return result

28100