<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 LazySegmentTree:
    def __init__(self, data, default=0, func=max):
        self._default = default
        self._func = func
        self._len = len(data)
        self._size = 1 << (self._len - 1).bit_length()
        self._lazy = [None] * (2 * self._size)
        self.data = [default] * (2 * self._size)
        self.data[self._size:self._size + self._len] = data
        for i in reversed(range(self._size)):
            self.data[i] = func(self.data[2 * i], self.data[2 * i + 1])

    def __len__(self):
        return self._len

    def _push(self, idx):
        if self._lazy[idx] is not None:
            self.data[idx * 2] = self.data[idx * 2 + 1] = self._lazy[idx]
            self._lazy[idx * 2] = self._lazy[idx * 2 + 1] = self._lazy[idx]
            self._lazy[idx] = None

    def _update(self, idx, tl, tr, l, r, new_val):
        if l > r:
            return
        if l == tl and r == tr:
            self.data[idx] = new_val
            self._lazy[idx] = new_val
        else:
            self._push(idx)
            tm = (tl + tr) // 2
            self._update(idx * 2, tl, tm, l, min(r, tm), new_val)
            self._update(idx * 2 + 1, tm + 1, tr, max(l, tm + 1), r, new_val)
            self.data[idx] = self._func(self.data[idx * 2], self.data[idx * 2 + 1])

    def set_value(self, l, r, value):
        self._update(1, 0, self._size - 1, l, r, value)

    def _get(self, idx, tl, tr, pos):
        if tl == tr:
            return self.data[idx]
        self._push(idx)
        tm = (tl + tr) // 2
        if pos <= tm:
            return self._get(idx * 2, tl, tm, pos)
        else:
            return self._get(idx * 2 + 1, tm + 1, tr, pos)

    def get(self, pos):
        return self._get(1, 0, self._size - 1, pos)
    
    def _query(self, idx, tl, tr, l, r):
        if l > r:
            return -float('inf')  # Using negative infinity as the default for max queries
        if l == tl and tr == r:
            return self.data[idx]
        self._push(idx)
        tm = (tl + tr) // 2
        return max(self._query(idx * 2, tl, tm, l, min(r, tm)), 
                   self._query(idx * 2 + 1, tm + 1, tr, max(l, tm + 1), r))

    def query_range(self, l, r):
        return self._query(1, 0, self._size - 1, l, r)
    
    def __repr__(self):
        return "LazySegmentTree({0})".format(self.data)
     
    def __delitem__(self, idx):
        self[idx] = self._default

    def __getitem__(self, idx):
        if isinstance(idx, slice):
            start=idx.start
            stop=idx.stop
        else:
            start=idx
            stop=idx+1
        return self.query_range(start,stop)

    def __setitem__(self, idx, value):
        if isinstance(idx, slice):
            start=idx.start
            stop=idx.stop
        else:
            start=idx
            stop=idx+1
        self.set_value(start,stop,value)


class Solution:
    def fallingSquares(self, positions: List[List[int]]) -> List[int]:
        s=set()
        for x,a in positions:
            s.add((x,0))
            s.add((x,1))
            s.add((x+a,0))
            s.add((x+a,1))
        d={}
        index=0
        for i in sorted(s):
            d[i]=index
            index+=1
        st=LazySegmentTree([0]*len(d),default=0,func=max)

        ans=[]
        ma=0

        for x,a in positions: 
            cur_h = st[d[(x,1)]:d[(x+a,0)]]+a 
            st[d[(x,1)]:d[(x+a,0)]]=cur_h
            ma=max(ma,cur_h)
            ans.append(ma)
        return ans
18200