<p>You are given a 2D integer array <code>items</code> where <code>items[i] = [price<sub>i</sub>, beauty<sub>i</sub>]</code> denotes the <strong>price</strong> and <strong>beauty</strong> of an item respectively.</p>

<p>You are also given a <strong>0-indexed</strong> integer array <code>queries</code>. For each <code>queries[j]</code>, you want to determine the <strong>maximum beauty</strong> of an item whose <strong>price</strong> is <strong>less than or equal</strong> to <code>queries[j]</code>. If no such item exists, then the answer to this query is <code>0</code>.</p>

<p>Return <em>an array </em><code>answer</code><em> of the same length as </em><code>queries</code><em> where </em><code>answer[j]</code><em> is the answer to the </em><code>j<sup>th</sup></code><em> query</em>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> items = [[1,2],[3,2],[2,4],[5,6],[3,5]], queries = [1,2,3,4,5,6]
<strong>Output:</strong> [2,4,5,5,6,6]
<strong>Explanation:</strong>
- For queries[0]=1, [1,2] is the only item which has price &lt;= 1. Hence, the answer for this query is 2.
- For queries[1]=2, the items which can be considered are [1,2] and [2,4]. 
  The maximum beauty among them is 4.
- For queries[2]=3 and queries[3]=4, the items which can be considered are [1,2], [3,2], [2,4], and [3,5].
  The maximum beauty among them is 5.
- For queries[4]=5 and queries[5]=6, all items can be considered.
  Hence, the answer for them is the maximum beauty of all items, i.e., 6.
</pre>

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

<pre>
<strong>Input:</strong> items = [[1,2],[1,2],[1,3],[1,4]], queries = [1]
<strong>Output:</strong> [4]
<strong>Explanation:</strong> 
The price of every item is equal to 1, so we choose the item with the maximum beauty 4. 
Note that multiple items can have the same price and/or beauty.  
</pre>

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

<pre>
<strong>Input:</strong> items = [[10,1000]], queries = [5]
<strong>Output:</strong> [0]
<strong>Explanation:</strong>
No item has a price less than or equal to 5, so no item can be chosen.
Hence, the answer to the query is 0.
</pre>

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

<ul>
	<li><code>1 &lt;= items.length, queries.length &lt;= 10<sup>5</sup></code></li>
	<li><code>items[i].length == 2</code></li>
	<li><code>1 &lt;= price<sub>i</sub>, beauty<sub>i</sub>, queries[j] &lt;= 10<sup>9</sup></code></li>
</ul>

"""
Brute force:
For each query, go through items, if lower, update max beauty

Items: n
Queries: m
Time complexity: n * m
Space complexity: 
- Base: m
- Extra: zero

Goal: n + m

Optimized:
Order queries in descending order -> n log n
Order items in descending order -> m log m
- For the same price, only keep highest beauty

Go down items
- Current highest query
- Compare to item
- If query is higher or equal to item price
    - Set beauty for that item
    - Go to next query, do same check

[6, 5, 4, 3, 2, 1]
[[5, 6], [3, 5], [2, 4], [1, 2]]

6 --> 6
5 --> 6
4 --> 5
3 --> 5
2 --> 4
1 --> 2

- Assume queries are unique, otherwise will need to keep track of original index

Time complexity: nlogn + mlogm


Binary tree
Order by price
For each query, find item where beauty is less

n + m * logn


"""

# class Solution:
#     def maximumBeauty(self, items: List[List[int]], queries: List[int]) -> List[int]:
#         sorted_items = sorted(items, key=lambda item: (-item[1], -item[0]))
#         print("sorted_items", sorted_items)
#         desc_queries = sorted(queries, key=lambda query: -query)
#         print("desc_queries", desc_queries)
        
#         current_item_index = 0
#         max_beauty = []
#         for query in desc_queries:
#             while current_item_index < len(sorted_items):
#                 if sorted_items[current_item_index][0] <= query:
#                     max_beauty.append(sorted_items[current_item_index][1])
#                     break
#                 else:
#                     current_item_index += 1
#             if current_item_index == len(sorted_items):
#                 max_beauty.append(0)
#         print("max_beauty", max_beauty)
#         query_to_max_beauty = {}
#         for index, query in enumerate(desc_queries):
#             query_to_max_beauty[query] = max_beauty[index]
#         print("query_to_max_beauty", query_to_max_beauty)
#         max_beauty_ordered = []
#         for query in queries:
#             max_beauty_ordered.append(query_to_max_beauty[query])
#         print("max_beauty_ordered", max_beauty_ordered)
#         return max_beauty_ordered

class Solution:
    def maximumBeauty(self, items: List[List[int]], queries: List[int]) -> List[int]:
        if len(items) == 0:
            return [0 for i in range(len(queries))]
        elif len(queries) == 0:
            return []
        filtered_items = {}
        for item in items:
            if item[0] not in filtered_items:
                filtered_items[item[0]] = item[1]
            else: 
                filtered_items[item[0]] = max(filtered_items[item[0]], item[1])
        items_list = [[key, val] for key, val in filtered_items.items()]
        items_list.sort(key=lambda x : x[0])
        prices = [item[0] for item in items_list]
        curr_max = 0
        n = len(items_list)
        for i in range(n):
            items_list[i][1] = max(items_list[i][1], curr_max)
            curr_max = max(curr_max, items_list[i][1])
        m = len(queries)
        res = []
        for i in range(m):
            idx = bisect.bisect(prices, queries[i]-1)
            if 0 <= idx < n and queries[i] < prices[idx]:
                idx -= 1
            if 0 <= idx < n:
                res.append(items_list[idx][1])
            elif idx >= n:
                res.append(items_list[-1][1])
            else:
                res.append(0)
        return res
802