private static void RefreshAdditionalData(CartesianTree tree)
 {
     if (tree == null)
     {
         return;
     }
     tree._size = (tree._left == null ? 0 : tree._left._size) +
                  (tree._right == null ? 0 : tree._right._size) + 1;
 }
 /// <summary>
 /// Traverses the tree and calls <param name="action"></param>.
 /// The calls are in increasing order of nodes
 /// </summary>
 public static void Dfs(CartesianTree tree, Action <CartesianTree> action)
 {
     if (tree == null)
     {
         return;
     }
     Dfs(tree._left, action);
     action(tree);
     Dfs(tree._right, action);
 }
        /// <summary>
        /// Erases the node such that it has exactly <param name="itemsToTheLeft"></param>
        /// items to the left of it. It is equivalent to erasing an item that has position
        /// <param name="itemsToTheLeft"></param> in zero-based numeration.
        /// Time complexity: O(logN)
        /// </summary>
        public static void Erase(ref CartesianTree tree, int itemsToTheLeft)
        {
            int curLeftCnt = tree._left == null ? 0 : tree._left._size;

            if (curLeftCnt == itemsToTheLeft)
            {
                tree = Merge(tree._left, tree._right);
            }
            else if (curLeftCnt > itemsToTheLeft)
            {
                Erase(ref tree._left, itemsToTheLeft);
            }
            else
            {
                Erase(ref tree._right, itemsToTheLeft - curLeftCnt - 1);
            }
            RefreshAdditionalData(tree);
        }
        /// <summary>
        /// Finds node such that it has exactly <param name="itemsToTheLeft"></param>
        /// nodes to the left of it.
        /// Time complexity: O(logN)
        /// </summary>
        public static CartesianTree Find(CartesianTree tree, int itemsToTheLeft)
        {
            if (tree == null)
            {
                return(null);
            }
            int curLeftCnt = tree._left == null ? 0 : tree._left._size;

            if (curLeftCnt == itemsToTheLeft)
            {
                return(tree);
            }
            else if (curLeftCnt > itemsToTheLeft)
            {
                return(Find(tree._left, itemsToTheLeft));
            }
            else
            {
                return(Find(tree._right, itemsToTheLeft - curLeftCnt - 1));
            }
        }
        /// <summary>
        /// Inserts new node such that it will have exactly
        /// <param name="itemsToTheLeft"></param> nodes less than it
        /// Time complexity: O(logN)
        /// </summary>
        public static void Insert(ref CartesianTree tree, int data, int priority, int itemsToTheLeft = 0)
        {
            if (tree == null)
            {
                tree = new CartesianTree {
                    _priority = priority, _data = data
                };
                RefreshAdditionalData(tree);
            }
            else
            {
                if (tree._priority > priority)
                {
                    int curLeftCnt = tree._left == null ? 0 : tree._left._size;
                    if (curLeftCnt >= itemsToTheLeft)
                    {
                        Insert(ref tree._left, data, priority, itemsToTheLeft);
                    }
                    else
                    {
                        Insert(ref tree._right, data, priority, itemsToTheLeft - curLeftCnt - 1);
                    }
                    RefreshAdditionalData(tree);
                }
                else
                {
                    CartesianTree tL, tR;
                    Split(tree, itemsToTheLeft, out tL, out tR);
                    tree = new CartesianTree
                    {
                        _priority = priority,
                        _data     = data,
                        _left     = tL,
                        _right    = tR
                    };

                    RefreshAdditionalData(tree);
                }
            }
        }
 /// <summary>
 /// Merges two trees
 /// Time complexity: O(logN)
 /// </summary>
 public static CartesianTree Merge(CartesianTree left, CartesianTree right)
 {
     if (left == null)
     {
         return(right);
     }
     if (right == null)
     {
         return(left);
     }
     if (left._priority > right._priority)
     {
         left._right = Merge(left._right, right);
         RefreshAdditionalData(left);
         return(left);
     }
     else
     {
         right._left = Merge(left, right._left);
         RefreshAdditionalData(right);
         return(right);
     }
 }
        /// <summary>
        /// Splits tree into two trees. The left tree contains exactly
        /// <param name="leftSize"></param> nodes.
        /// Time complexity: O(logN)
        /// </summary>
        public static void Split(CartesianTree tree, int leftSize,
                                 out CartesianTree left, out CartesianTree right)
        {
            left  = null;
            right = null;
            if (tree == null)
            {
                return;
            }
            int curLeftSize = tree._left == null ? 0 : tree._left._size;

            if (curLeftSize >= leftSize)
            {
                Split(tree._left, leftSize, out left, out tree._left);
                right = tree;
            }
            else
            {
                Split(tree._right, leftSize - curLeftSize - 1, out tree._right, out right);
                left = tree;
            }

            RefreshAdditionalData(tree);
        }