<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 SegTreeNode:
    def __init__(self, left=-1, right=-1, val=0, lazy_tag=None, left_node=None, right_node=None):
        self.left = left  # 区间左边界
        self.right = right  # 区间右边界
        self.mid = left + (right - left) // 2  # 区间中间点
        self.left_node = left_node  # 区间左节点
        self.right_node = right_node  # 区间右节点
        self.val = val  # 节点值（区间值）
        self.lazy_tag = lazy_tag  # 区间问题的延迟更新标记


# 动态开点线段树类
class SegmentTree:
    def __init__(self, function):
        # 动态开点线段树，开始时只建立一个根节点，代表整个区间。
        # 当需要访问线段树的某棵子树（某个子区间）时，再建立代表这个子区间的节点。
        # 动态开点和非动态的线段树，核心思路思路是一模一样的，只是每次要对左右节点做操作的时候，先提前判断一下左右节点是否存在，
        # 如果不存在我们先构建左右节点，如果存在则继续操作。
        # 建立根节点
        self.tree = SegTreeNode(0, int(1e9))
        # function 是一个函数，左右区间的聚合方法
        self.function = function

    # 向上更新 node 节点区间值，节点的区间值等于该节点左右子节点元素值的聚合计算结果
    def __push_up(self, node):
        left_node = node.left_node
        right_node = node.right_node
        # 需要查看左右节点是否存在，动态开点的前提是左右节点都已经构造过，但是这里的写法存疑
        # 应该是三种情况都要覆盖掉，否则子节点更新完成值后，父节点如果不是左右都存在的情况下，是不会更新父节点的值的，
        # 这样以来 query interval 的时候，如果interval是父节点的区间，会导致取值的结果是没有更新前的结果，而不是子节点更新过的结果。

        # 情况1：左右节点都存在，父节点是左右节点聚合之后结果
        if left_node and right_node:
            node.val = self.function(left_node.val, right_node.val)
        # 情况2：左节点存在，父节点是左结果更新之后的结果
        elif left_node:
            node.val = left_node.val
        # 情况3：右节点存在，父节点是右结果更新之后的结果
        elif right_node:
            node.val = right_node.val

    # 区间查询，查询区间为 [q_left, q_right] 的区间值
    def query_interval(self, q_left, q_right):
        return self.__query_interval(q_left, q_right, self.tree)

    # 区间查询，在线段树的 [left, right] 区间范围中搜索区间为 [q_left, q_right] 的区间值
    def __query_interval(self, q_left, q_right, node):
        # 节点所在区间被 [q_left, q_right] 所覆盖
        if node.left >= q_left and node.right <= q_right:
            # 直接返回节点值
            return node.val
        # 节点所在区间与 [q_left, q_right] 无关
        if node.right < q_left or node.left > q_right:
            return 0

        # 向下更新节点所在区间的左右子节点的值和懒惰标记
        self.__push_down(node)

        # 左子树查询结果
        res_left = 0
        # 右子树查询结果
        res_right = 0

        # 在左子树中查询
        if q_left <= node.mid:
            # 同理单点更新，如果左节点还没有构造，先构造左节点
            if not node.left_node:
                node.left_node = SegTreeNode(node.left, node.mid)
            res_left = self.__query_interval(q_left, q_right, node.left_node)
        # 在右子树中查询
        if q_right > node.mid:
            # 同理单点更新，如果右节点还没有构造，先构造右节点
            if not node.right_node:
                node.right_node = SegTreeNode(node.mid + 1, node.right)
            res_right = self.__query_interval(q_left, q_right, node.right_node)

        # 返回左右子树元素值的聚合计算结果
        return self.function(res_left, res_right)

    # 区间更新，将区间为 [q_left, q_right] 上的元素值增减 val
    def update_interval(self, q_left, q_right, val):
        self.__update_interval(q_left, q_right, val, self.tree)

    # 区间更新
    def __update_interval(self, q_left, q_right, val, node):
        # 节点所在区间被 [q_left, q_right] 所覆盖
        if node.left >= q_left and node.right <= q_right:
            # 将当前节点的延迟标记赋值为 val
            node.lazy_tag = val
            # 当前节点所在区间大小
            # interval_size = (node.right - node.left + 1)
            # 当前节点所在区间每个元素值增加 val
            # node.val += val * interval_size
            node.val = val
            return
        # 节点所在区间与 [q_left, q_right] 无关
        if node.right < q_left or node.left > q_right:
            return

        # 向下更新节点所在区间的左右子节点的值和懒惰标记
        self.__push_down(node)

        # 在左子树中更新区间值
        if q_left <= node.mid:
            # 同理单点更新，如果左节点还没有构造，先构造左节点
            if not node.left_node:
                node.left_node = SegTreeNode(node.left, node.mid)
            self.__update_interval(q_left, q_right, val, node.left_node)
        # 在右子树中更新区间值
        if q_right > node.mid:
            # 同理单点更新，如果右节点还没有构造，先构造右节点
            if not node.right_node:
                node.right_node = SegTreeNode(node.mid + 1, node.right)
            self.__update_interval(q_left, q_right, val, node.right_node)

        # 向上更新当前节点的值
        self.__push_up(node)

    # 向下更新 node 节点所在区间的左右子节点的值和懒惰标记
    def __push_down(self, node):
        # 先判断一下当前节点是否被标记过，如果没有被标记过说明并没有更新到此区间，直接跳过
        lazy_tag = node.lazy_tag
        if not node.lazy_tag:
            return

        # 同理单点更新，如果左节点还没有构造，先构造左节点
        if not node.left_node:
            node.left_node = SegTreeNode(node.left, node.mid)
        # 同理单点更新，如果右节点还没有构造，先构造右节点
        if not node.right_node:
            node.right_node = SegTreeNode(node.mid + 1, node.right)

        # 如果当前节点左节点存在延迟标记
        # if node.left_node.lazy_tag:
        #     # 更新左子节点懒惰标记，这里是增减的 val的版本
        #     node.left_node.lazy_tag += lazy_tag  # 更新左子节点懒惰标记
        # # 如果当前节点左节点不存在延迟标记
        # else:
        #     # 更新左子节点懒惰标记，直接赋值
        node.left_node.lazy_tag = lazy_tag
        # left_size = (node.left_node.right - node.left_node.left + 1)
        # 左子节点每个元素值增加 lazy_tag
        # node.left_node.val += lazy_tag * left_size
        node.left_node.val = lazy_tag

        # 如果当前节点右节点存在延迟标记
        # if node.right_node.lazy_tag:
        #     #  更新右子节点懒惰标记，这里是增减的 val 的版本
        #     node.right_node.lazy_tag += lazy_tag
        # # 如果当前节点右节点不存在延迟标记
        # else:
        #     # 更新右子节点懒惰标记，直接赋值
        node.right_node.lazy_tag = lazy_tag
        # right_size = (node.right_node.right - node.right_node.left + 1)
        # 右子节点每个元素值增加 lazy_tag
        # node.right_node.val += lazy_tag * right_size
        node.right_node.val = lazy_tag
        # 更新当前节点的懒惰标记
        node.lazy_tag = None


class Solution:
    def fallingSquares(self, positions: List[List[int]]) -> List[int]:
        segment_tree = SegmentTree(lambda x, y: max(x, y))
        res = []
        max_height = 0
        for position in positions:
            left, size = position[0], position[1]
            right = left + size - 1
            cur_height = segment_tree.query_interval(left, right)
            new_height = cur_height + size
            segment_tree.update_interval(left, right, new_height)
            max_height = max(max_height, new_height)
            res.append(max_height)

        return res
27300