<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>

"""
PROBLEM: https://leetcode.com/problems/height-of-binary-tree-after-subtree-removal-queries/?envType=problem-list-v2&envId=my4z5092

SOLUTION: Euler Tour
- Do dfs and height=lvl, before and after each call put (node,level) into tour array
- Once you have tour array, find start+end of each node
- Calculate prefix and suffix sums = max_till_index(max height from 0 to i) and max_from_index(max height from i to n)
          "1"
       " /   \"
      "2      3"
     "/"
    "4"
# euler tour array is [(1,0),(2,1),(4,2),(4,2),(2,1),(3,1),(3,1),(1,0)]
- If you observe between (NODE, lvl) ..... (NODE, lvl), you will have all nodes which are subtrees under given NODE

- Now query comes in [2]
    start, end = 1, 4
    [(1,0) | (2,1),(4,2),(4,2),(2,1) | (3,1),(3,1),(1,0)]
    - res = max_height([(1,0)]) OR max_height([(3,1),(3,1),(1,0)]])
    - res = max(  max_depth_till_here[start-1],  max_depth_till_here[end-1] )
    Note: 
    - Handle cases when start=0 and end=n 
    - Here 0 to n indicate index in Euler tours array - NOT NODE THEMSELVES

"""

# 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
from collections import defaultdict

class EulerTour:
    def __init__(self) -> None:
        pass

    def solve(self, node: TreeNode, level: int):
        self.tour.append( (node.val, level) )

        if node.left: self.solve(node.left, level+1)
        if node.right: self.solve(node.right, level+1)

        self.tour.append( (node.val, level) )

    def get_euler_tour(self, root: TreeNode):
        self.tour = []
        self.solve(root, 0)
        return self.tour


class Solution:
    def get_euler_tour(self, node: TreeNode, level: int):
        self.tour.append( (node.val, level) )

        if node.left: self.get_euler_tour(node.left, level+1)
        if node.right: self.get_euler_tour(node.right, level+1)

        self.tour.append( (node.val, level) )

    def treeQueries(self, root: Optional[TreeNode], queries: List[int]) -> List[int]:
        self.tour = []
        self.get_euler_tour(root, 0)
        tour = self.tour    # [ (node, max_depth),... ]    ​

        max_depth_till_here = [0]*len(tour)
        max_depth_from_here = [0]*len(tour)

        # get start and end for each node (node value here) -> IDEALLY YOU SHOULD USE NODE_IDX since node values can repeat (DONT REPEAT IN THIS CASE)
        start_end_map = defaultdict(list)
        for i, tour_ele in enumerate(tour): # saving index of each node in the tour array
            start_end_map[tour_ele[0]].append(i)

        max_val = -1
        for i in range(len(tour)):
            max_val = max(max_val, tour[i][1])
            max_depth_till_here[i] = max_val

        max_val = -1
        for i in reversed(range(len(tour))):
            max_val = max(max_val, tour[i][1])
            max_depth_from_here[i] = max_val


        res = []
        for delete_root in queries:
            [start, end] = start_end_map[delete_root]
            depth = 0
            if start>0:
                depth = max(depth, max_depth_till_here[start-1])
            if end < len(tour)-1:
                depth = max(depth, max_depth_from_here[end+1])
            res.append(depth)
        
        return res



        
72231