<p>You are given a 2D integer array <code>rectangles</code> where <code>rectangles[i] = [l<sub>i</sub>, h<sub>i</sub>]</code> indicates that <code>i<sup>th</sup></code> rectangle has a length of <code>l<sub>i</sub></code> and a height of <code>h<sub>i</sub></code>. You are also given a 2D integer array <code>points</code> where <code>points[j] = [x<sub>j</sub>, y<sub>j</sub>]</code> is a point with coordinates <code>(x<sub>j</sub>, y<sub>j</sub>)</code>.</p>

<p>The <code>i<sup>th</sup></code> rectangle has its <strong>bottom-left corner</strong> point at the coordinates <code>(0, 0)</code> and its <strong>top-right corner</strong> point at <code>(l<sub>i</sub>, h<sub>i</sub>)</code>.</p>

<p>Return<em> an integer array </em><code>count</code><em> of length </em><code>points.length</code><em> where </em><code>count[j]</code><em> is the number of rectangles that <strong>contain</strong> the </em><code>j<sup>th</sup></code><em> point.</em></p>

<p>The <code>i<sup>th</sup></code> rectangle <strong>contains</strong> the <code>j<sup>th</sup></code> point if <code>0 &lt;= x<sub>j</sub> &lt;= l<sub>i</sub></code> and <code>0 &lt;= y<sub>j</sub> &lt;= h<sub>i</sub></code>. Note that points that lie on the <strong>edges</strong> of a rectangle are also considered to be contained by that rectangle.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2022/03/02/example1.png" style="width: 300px; height: 509px;" />
<pre>
<strong>Input:</strong> rectangles = [[1,2],[2,3],[2,5]], points = [[2,1],[1,4]]
<strong>Output:</strong> [2,1]
<strong>Explanation:</strong> 
The first rectangle contains no points.
The second rectangle contains only the point (2, 1).
The third rectangle contains the points (2, 1) and (1, 4).
The number of rectangles that contain the point (2, 1) is 2.
The number of rectangles that contain the point (1, 4) is 1.
Therefore, we return [2, 1].
</pre>

<p><strong class="example">Example 2:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2022/03/02/example2.png" style="width: 300px; height: 312px;" />
<pre>
<strong>Input:</strong> rectangles = [[1,1],[2,2],[3,3]], points = [[1,3],[1,1]]
<strong>Output:</strong> [1,3]
<strong>Explanation:
</strong>The first rectangle contains only the point (1, 1).
The second rectangle contains only the point (1, 1).
The third rectangle contains the points (1, 3) and (1, 1).
The number of rectangles that contain the point (1, 3) is 1.
The number of rectangles that contain the point (1, 1) is 3.
Therefore, we return [1, 3].
</pre>

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

<ul>
	<li><code>1 &lt;= rectangles.length, points.length &lt;= 5 * 10<sup>4</sup></code></li>
	<li><code>rectangles[i].length == points[j].length == 2</code></li>
	<li><code>1 &lt;= l<sub>i</sub>, x<sub>j</sub> &lt;= 10<sup>9</sup></code></li>
	<li><code>1 &lt;= h<sub>i</sub>, y<sub>j</sub> &lt;= 100</code></li>
	<li>All the <code>rectangles</code> are <strong>unique</strong>.</li>
	<li>All the <code>points</code> are <strong>unique</strong>.</li>
</ul>

class Solution:
    def countRectangles(self, rectangles: List[List[int]], points: List[List[int]]) -> List[int]:
        # brute force - for each point, check if it fits into each rectangle
        # official solution = note that the heights at at most 100 points

        # create array of lists for all possible heights
        arr = []
        for _ in range(101):
            arr.append([])

        # store each length for each possible height
        for l, h in rectangles:
            arr[h].append(l)

        # sort each length array
        for a in arr:
            a.sort()

        # use binary search on the lengths of each array for each point
        ans = []
        for x, y in points:
            s = 0 # sum
            # we already know that only lengths in index y and above
            # are valid since everything less does not meet the height requirement
            for i in range(y, len(arr)):
                a = arr[i]
                left = 0
                right = len(a)
                # binary search with possible duplicate values
                while left < right:
                    mid = (left+right)//2
                    if a[mid] >= x:
                        right = mid
                    else:
                        left = mid + 1
                # left points to leftmost such that a[left] >= x which means
                # that everything starting at this index is within the length limits
                s += len(a) - left
            ans.append(s)
        return ans


        # NOT WORKING BELOW
        # better solution O(points * log rectangles)
        # idea is that a point fits if both the x and y coordinates fit
        # so can sort the rectangles by length and then height
        # and for each point, find the index of where the points fit the x axis based on lengths
        # and using that index as the new left, find the index where the point fits based on heights
        # and then difference between the index and the total length is the number of points

        # ordered  = sorted(rectangles) # O(r * log r) time, O(r) space
        # print(ordered)
        # ans = []
        # for x,y  in points: # O(n * (logr + rlogr))
        #     # binary search on leftmost value equal to the target for x/length
        #     left = 0
        #     right = len(ordered)
        #     while left < right:
        #         mid = (left+right)//2
        #         if ordered[mid][0] >= x:
        #             right = mid
        #         else:
        #             left = mid + 1
        #     # left should point to the leftmost x, or where x should be if it doesnt exists
        #     # so ordered[left][0] >= x which means x is within bounds on the x axis
        #     leftIdx = left

        #     # have to check if it fits on the y axis
        #     yordered = sorted(ordered[left:], key = lambda x: x[1])
        #     # use left as new bounds
        #     # since everything left of this left is out of bounds for the point
        #     left = 0
        #     right = len(yordered)
        #     while left < right:
        #         mid = (left+right)//2
        #         if yordered[mid][1] >= y:
        #             right = mid
        #         else:
        #             left = mid + 1
        #     # left should point to leftmost y, or where y should be if it doesnt exists
        #     ans.append(len(ordered)-(left+leftIdx))
        # return ans
2707