<p>You are given the <code>root</code> of a <strong>binary tree</strong> with <code>n</code> nodes. Each node is assigned a unique value from <code>1</code> to <code>n</code>. You are also given an array <code>queries</code> of size <code>m</code>.</p>

<p>You have to perform <code>m</code> <strong>independent</strong> queries on the tree where in the <code>i<sup>th</sup></code> query you do the following:</p>

<ul>
	<li><strong>Remove</strong> the subtree rooted at the node with the value <code>queries[i]</code> from the tree. It is <strong>guaranteed</strong> that <code>queries[i]</code> will <strong>not</strong> be equal to the value of the root.</li>
</ul>

<p>Return <em>an array </em><code>answer</code><em> of size </em><code>m</code><em> where </em><code>answer[i]</code><em> is the height of the tree after performing the </em><code>i<sup>th</sup></code><em> query</em>.</p>

<p><strong>Note</strong>:</p>

<ul>
	<li>The queries are independent, so the tree returns to its <strong>initial</strong> state after each query.</li>
	<li>The height of a tree is the <strong>number of edges in the longest simple path</strong> from the root to some node in the tree.</li>
</ul>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2022/09/07/binaryytreeedrawio-1.png" style="width: 495px; height: 281px;" />
<pre>
<strong>Input:</strong> root = [1,3,4,2,null,6,5,null,null,null,null,null,7], queries = [4]
<strong>Output:</strong> [2]
<strong>Explanation:</strong> The diagram above shows the tree after removing the subtree rooted at node with value 4.
The height of the tree is 2 (The path 1 -&gt; 3 -&gt; 2).
</pre>

<p><strong class="example">Example 2:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2022/09/07/binaryytreeedrawio-2.png" style="width: 301px; height: 284px;" />
<pre>
<strong>Input:</strong> root = [5,8,9,2,1,3,7,4,6], queries = [3,2,4,8]
<strong>Output:</strong> [3,2,3,2]
<strong>Explanation:</strong> We have the following queries:
- Removing the subtree rooted at node with value 3. The height of the tree becomes 3 (The path 5 -&gt; 8 -&gt; 2 -&gt; 4).
- Removing the subtree rooted at node with value 2. The height of the tree becomes 2 (The path 5 -&gt; 8 -&gt; 1).
- Removing the subtree rooted at node with value 4. The height of the tree becomes 3 (The path 5 -&gt; 8 -&gt; 2 -&gt; 6).
- Removing the subtree rooted at node with value 8. The height of the tree becomes 2 (The path 5 -&gt; 9 -&gt; 3).
</pre>

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

<ul>
	<li>The number of nodes in the tree is <code>n</code>.</li>
	<li><code>2 &lt;= n &lt;= 10<sup>5</sup></code></li>
	<li><code>1 &lt;= Node.val &lt;= n</code></li>
	<li>All the values in the tree are <strong>unique</strong>.</li>
	<li><code>m == queries.length</code></li>
	<li><code>1 &lt;= m &lt;= min(n, 10<sup>4</sup>)</code></li>
	<li><code>1 &lt;= queries[i] &lt;= n</code></li>
	<li><code>queries[i] != root.val</code></li>
</ul>

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

# looking at the problem we need to figure out the root of the tree
# in logN time minimum since we have M queries
# We can most likely either do this in N + M time or MLogN + N time
# I think it is probably best to compute each query ahead of time when computing heights

# approach 1)
# 1st DFS
# go down the tree and find the maximum height of the tree that is located on the other side
# if the other side of the tree is None then the height automatically becomes the parent's height
# for that specific query

# approach 2)
# find out how many levels the tree loses by removing a particular node
# do a set intersection of the nodes as you recurse up the tree
# if it contains all of the nodes of a certain height then you automatically
# go up to the next height. Once a height is done, you remove it from the list to avoid going through it again
# keep combining values

# approach 3)
#

class Solution:
    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:

        query_ans = defaultdict(int)
        h = defaultdict(set)
        node_heights = defaultdict(int)
        q_ans = defaultdict(int)
        ans = []
        max_height = 0

        def traverse(root, height):
            nonlocal max_height

            if root == None:
                return

            h[height].add(root.val)
            node_heights[root.val] = height
            max_height = max(height, max_height)
            traverse(root.left, height + 1)
            traverse(root.right, height + 1)

        def traverse2(root):

            if root == None:
                return (-1, set())

            if root.left == None and root.right == None:
                max_h = max_height
                cur_set = {root.val}
                while len(cur_set.intersection(h[max_h])) == len(h[max_h]) and max_h >= 0:
                    cur_set -= h[max_h]
                    max_h -= 1

                q_ans[root.val] = max_h
                return max_h, cur_set

            l, set_l = traverse2(root.left)
            r, set_r = traverse2(root.right)
            if l != -1 and r != -1:
                max_h = min(l, r)
            elif l == -1:
                max_h = r
            else:
                max_h = l
            cur_set = set_l.union(set_r)
            cur_set.add(root.val)
            while len(cur_set.intersection(h[max_h])) == len(h[max_h]) and max_h >= 0:
                cur_set -= h[max_h]
                max_h -= 1

            q_ans[root.val] = max_h
            return max_h, cur_set
            


        traverse(root, 0)
        traverse2(root)
        ans = []
        for val in queries:
            ans.append(q_ans[val])

        print(q_ans)
        print(h)
        return ans
        
69763