<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 Solution:
    def fallingSquares(self, positions: List[List[int]]) -> List[int]:
        
        class TreeNode():
            
            def __init__(self, lb, rb):
                self.left = None
                self.right = None
                self.inc = 0
                self.lazy = 0

                self.lb = lb
                self.rb = rb

            def __str__(self):
                return "lb : %d, rb : %d, lazy : %d, inc : %d" % (self.lb, self.rb, self.lazy, self.inc)

            def add(self, lb, rb, inc):

                if lb <= self.lb and self.rb <= rb:
                    self.inc = inc
                    #print('f', self.lb, self.rb, inc)
                    self.lazy = inc
                    return

                mid = (self.lb + self.rb) // 2

                self.pushdown()

                if not (self.lb > rb or lb > mid):
                    self.left.add(lb, rb, inc)

                if not (mid + 1 > rb or lb > self.rb):
                    self.right.add(lb, rb, inc)

                self.inc = max(self.left.inc, self.right.inc)

            def pushdown(self):
                mid = (self.lb + self.rb) // 2

                if self.left is None:
                    self.left = TreeNode(self.lb, mid)

                if self.right is None:
                    self.right = TreeNode(mid + 1, self.rb)

                if self.lazy:
                    self.left.lazy = self.lazy
                    self.right.lazy = self.lazy

                    self.left.inc = self.lazy
                    self.right.inc = self.lazy

                    self.lazy = 0


            def getmax(self, lb, rb):

                if lb <= self.lb and self.rb <= rb:
                    return self.inc

                self.pushdown()
                mid = (self.lb + self.rb) // 2

                res = 0
                if not (self.lb > rb or lb > mid) and self.left:
                    res = max(self.left.getmax(lb, rb), res)
                if not (mid + 1 > rb or lb > self.rb) and self.right:
                    res = max(self.right.getmax(lb, rb), res)

                return res
                

        left = min(i for i, _ in positions)
        right = max(i + j - 1 for i, j in positions)

        root = TreeNode(left, right)

        res = []
        for i, j in positions:
            inc = root.getmax(i, i + j - 1) + j
            #print(i, i + j - 1, inc)
            root.add(i, i + j - 1, inc)
            res.append(root.inc)

            # print(inc)
            # print(root)
            # if root.left:
            #     print(root.left)
            # if root.right:
            #     print(root.right)
            # print()

        return res
27100