You're given a tree consisting of $$$n$$$ nodes. Every node $$$u$$$ has a weight $$$a_u$$$. You want to choose an integer $$$k$$$ $$$(1 \le k \le n)$$$ and then choose $$$k$$$ connected components of nodes that don't overlap (i.e every node is in at most 1 component). Let the set of nodes you chose be $$$s$$$. You want to maximize:$$$$$$\frac{\sum\limits_{u \in s} a_u}{k}$$$$$$In other words, you want to maximize the sum of weights of nodes in $$$s$$$ divided by the number of connected components you chose. Also, if there are several solutions, you want to maximize $$$k$$$.Note that adjacent nodes can belong to different components. Refer to the third sample.

The first line contains the integer $$$n$$$ $$$(1 \le n \le 3 \cdot 10^5)$$$, the number of nodes in the tree. The second line contains $$$n$$$ space-separated integers $$$a_1$$$, $$$a_2$$$, $$$\dots$$$, $$$a_n$$$ $$$(|a_i| \le 10^9)$$$, the weights of the nodes. The next $$$n-1$$$ lines, each contains 2 space-separated integers $$$u$$$ and $$$v$$$ $$$(1 \le u,v \le n)$$$ which means there's an edge between $$$u$$$ and $$$v$$$.

Print the answer as a non-reduced fraction represented by 2 space-separated integers. The fraction itself should be maximized and if there are several possible ways, you should maximize the denominator. See the samples for a better understanding.

NoteA connected component is a set of nodes such that for any node in the set, you can reach all other nodes in the set passing only nodes in the set.In the first sample, it's optimal to choose the whole tree.In the second sample, you only have one choice (to choose node 1) because you can't choose 0 components.In the third sample, notice that we could've, for example, chosen only node 1, or node 1 and node 3, or even the whole tree, and the fraction would've had the same value (-1), but we want to maximize $$$k$$$. In the fourth sample, we'll just choose nodes 1 and 3.

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.array (1,n) . unfoldr processInt . (1,)
    <$> BS.getLine :: IO (UArray Int Int)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom
    where
      {-# INLINE processInt #-}
      processInt = \(!i,!bs) -> do
        (!e,!bs1) <- BS.readInt $ BS.dropWhile (< '-') bs
        return $! ((i,e),(i+1,bs1))

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE processInt #-}
    processInt = \(!i,!bs) -> do
      (!e,!bs1) <- BSL.readInt $ BSL.dropWhile (< '-') bs
      return $! ((i,e),(i+1,bs1))
    domcods :: UArray Int Int
    domcods = A.array (0, 2*n - 3) $ unfoldr processInt (0,bstr)
    codd :: UArray Int Int
    codd = runSTUArray $ do
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, retSum, 1, nonpos)
      GT -> (maxSum, retSum, numCompo, countable')
      EQ | countable -> (maxSum, retSum, 1 + numCompo, nonpos)
         | otherwise -> (maxSum, retSum, numCompo, False)
      where
        (!retSum, !countable', !nonpos)
          | maxSumHere > 0 = (maxSumHere, countable, False)
          | otherwise      = (0,          True,      True)
        {-# INLINE maxSumHere #-}
        !maxSumHere = fromIntegral (as A.! this) + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case compare maxSum msum of
               GT -> (maxSum, sumsub + sumCh,       numCompo,        cntbl)
               LT -> (msum,   sumsub + sumCh, cmp,            cnt         )
               EQ -> (msum,   sumsub + sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent)
      $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  cods <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cods
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> Array Int ListInt
preprocess n = \bstr -> runSTArray $ do
  arr <- A.newArray (1,n) Nil
  forM_ (unfoldr processLine bstr) $ \ !(!l, !r) -> do
    lls <- A.readArray arr l
    A.writeArray arr l $! Cons r lls
    rls <- A.readArray arr r
    A.writeArray arr r $! Cons l rls
  return arr
  where
    {-# INLINE processLine #-}
    processLine = \ !bs0 -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((l,r),bs2)

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> Array Int ListInt -> (Int64,Int)
query n as cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, numCompo, countable_) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, maxSumHere <= 0)
      GT -> (maxSum, maxSumHere, numCompo, maxSumHere <= 0 || countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, maxSum <= 0)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, numCompo, cntbl)
                    !(!msum, !sumsub, cmp, cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, cnt)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ unfoldr (\case
                                       Cons x xs -> Just (x,xs)
                                       Nil -> Nothing)
      $ cods A.! this

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  (cntE, cods) <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BSL.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    processLine = \ !(!i,!bs0) -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \ !(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int ->
         UArray Int Int-> (Int64,Int)
query n as cntE cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, numCompo, countable_) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, maxSumHere <= 0)
      GT -> (maxSum, maxSumHere, numCompo, maxSumHere <= 0 || countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, maxSum <= 0)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, numCompo, cntbl)
                    !(!msum, !sumsub, cmp, cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, cnt)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (< '-'))
    <$> BS.getLine :: IO (UArray Int Int64)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE process #-}
    process = BSL.readInt . BSL.dropWhile (< '-')
    domcods :: UArray Int Int
    domcods = A.listArray (0, 2*n - 3) $ unfoldr process bstr
    codd :: UArray Int Int
    codd = runSTUArray $ do
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, retSum, 1, nonpos)
      GT -> (maxSum, retSum, numCompo, countable')
      EQ | countable -> (maxSum, retSum, 1 + numCompo, nonpos)
         | otherwise -> (maxSum, retSum, numCompo, False)
      where
        !(!retSum, !countable', !nonpos)
          | maxSumHere > 0 = (maxSumHere, countable, False)
          | otherwise      = (0,          True,      True)
        {-# INLINE maxSumHere #-}
        !maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case compare maxSum msum of
               GT -> (maxSum, sumsub + sumCh,       numCompo,        cntbl)
               LT -> (msum,   sumsub + sumCh, cmp,            cnt         )
               EQ -> (msum,   sumsub + sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  cods <- preprocess n <$> BS.getContents
  let (numer,denom) = query n as cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BS.ByteString -> Array Int ListInt
preprocess n bstr = runSTArray $ do
  arr <- A.newArray (1,n) Nil
  forM_ (unfoldr processLine bstr) $ \ !(!l, !r) -> do
    !lls <- A.readArray arr l
    A.writeArray arr l $ Cons r lls
    !rls <- A.readArray arr r
    A.writeArray arr r $ Cons l rls
  return arr
  where
    {-# INLINE processLine #-}
    processLine = \ !bs0 -> do
      (!l,!bs1) <- BS.readInt $ BS.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BS.readInt $ BS.dropWhile (==' ') bs1
      return $ ((l,r),bs2)

query :: Int -> UArray Int Int64 -> Array Int ListInt -> (Int64,Int)
query n as cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let !(!compo_ch,!used_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ unfoldr (\case
                                       Cons x xs -> Just (x,xs)
                                       Nil -> Nothing)
      $ cods A.! this

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  cods <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BSL.ByteString -> Array Int ListInt
preprocess n bstr = runSTArray $ do
  arr <- A.newArray (1,n) Nil
  forM_ (unfoldr processLine bstr) $ \ !(!l, !r) -> do
    !lls <- A.readArray arr l
    A.writeArray arr l $ Cons r lls
    !rls <- A.readArray arr r
    A.writeArray arr r $ Cons l rls
  return arr
  where
    {-# INLINE processLine #-}
    processLine = \ !bs0 -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((l,r),bs2)

query :: Int -> UArray Int Int64 -> Array Int ListInt -> (Int64,Int)
query n as cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let !(!compo_ch,!used_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ unfoldr (\case
                                       Cons x xs -> Just (x,xs)
                                       Nil -> Nothing)
      $ cods A.! this

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (< '-'))
    <$> BS.getLine :: IO (UArray Int Int64)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE process #-}
    process = BSL.readInt . BSL.dropWhile (< '-')
    domcods :: UArray Int Int
    domcods = A.listArray (0, 2*n - 3) $ unfoldr process bstr
    codd :: UArray Int Int
    codd = runSTUArray $ do
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, False)
      GT -> (maxSum, maxSumHere, numCompo, countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, False)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, True)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE processLine #-}
    processLine = \ !(!i,!bs0) -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \ !(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    codd :: UArray Int Int
    codd = runSTUArray $ do
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable_) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, maxSumHere <= 0)
      GT -> (maxSum, maxSumHere, numCompo, maxSumHere <= 0 || countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, maxSum <= 0)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, cnt)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  cods <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cods
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> Array Int ListInt
preprocess n = \bstr -> runSTArray $ do
  arr <- A.newArray (1,n) Nil
  forM_ (unfoldr processLine bstr) $ \ !(!l, !r) -> do
    lls <- A.readArray arr l
    A.writeArray arr l $! Cons r lls
    rls <- A.readArray arr r
    A.writeArray arr r $! Cons l rls
  return arr
  where
    {-# INLINE processLine #-}
    processLine = \ !bs0 -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((l,r),bs2)

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> Array Int ListInt -> (Int64,Int)
query n as cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, numCompo, countable_) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, maxSumHere <= 0)
      GT -> (maxSum, maxSumHere, numCompo, maxSumHere <= 0 || countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, maxSum <= 0)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    (!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, cnt)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ unfoldr (\case
                                       Cons x xs -> Just (x,xs)
                                       Nil -> Nothing)
      $ cods A.! this
{-
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let !(!compo_ch,!used_ch)
              = foldl' (\ !(!x0,!x1) !(!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
-}

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  (cntE, cods) <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    {-# INLINE processLine #-}
    processLine = \ !(!i,!bs0) -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \ !(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int ->
         UArray Int Int-> (Int64,Int)
query n as cntE cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, False)
      GT -> (maxSum, maxSumHere, numCompo, countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, False)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, True)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (< '-'))
    <$> BS.getLine :: IO (UArray Int Int64)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE process #-}
    process = BSL.readInt . BSL.dropWhile (< '-')
    domcods :: UArray Int Int
    domcods = A.listArray (0, 2*n - 3) $ unfoldr process bstr
    codd :: UArray Int Int
    codd = runSTUArray $ do
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, retSum, 1, nonpos)
      GT -> (maxSum, retSum, numCompo, countable')
      EQ | countable -> (maxSum, retSum, 1 + numCompo, nonpos)
         | otherwise -> (maxSum, retSum, numCompo, False)
      where
        (retSum, countable', nonpos)
          | maxSumHere > 0 = (maxSumHere, countable, False)
          | otherwise      = (0,          True,      True)
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case compare maxSum msum of
               GT -> (maxSum, sumsub + sumCh,       numCompo,        cntbl)
               LT -> (msum,   sumsub + sumCh, cmp,            cnt         )
               EQ -> (msum,   sumsub + sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  (cntE, cods) <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    {-# INLINE processLine #-}
    processLine = \ !(!i,!bs0) -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \ !(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int ->
         UArray Int Int-> (Int64,Int)
query n as cntE cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable_) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, maxSumHere <= 0)
      GT -> (maxSum, maxSumHere, numCompo, maxSumHere <= 0 || countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, maxSum <= 0)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, cnt)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  (cntE, cods) <- preprocess n <$> BS.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BS.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    processLine = \ !(!i,!bs0) -> do
      (!l,!bs1) <- BS.readInt $ BS.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BS.readInt $ BS.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \ !(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

query :: Int -> UArray Int Int64 -> UArray Int Int -> UArray Int Int -> (Int64,Int)
query n as cntE cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let !(!compo_ch,!used_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  cods <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cods
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> Array Int ListInt
preprocess n = \bstr -> runSTArray $ do
  arr <- A.newArray (1,n) Nil
  forM_ (unfoldr processLine bstr) $ \ !(!l, !r) -> do
    lls <- A.readArray arr l
    A.writeArray arr l $! Cons r lls
    rls <- A.readArray arr r
    A.writeArray arr r $! Cons l rls
  return arr
  where
    {-# INLINE processLine #-}
    processLine = \ !bs0 -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((l,r),bs2)

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> Array Int ListInt -> (Int64,Int)
query n as cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, numCompo, countable_) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, maxSumHere <= 0)
      GT -> (maxSum, maxSumHere, numCompo, maxSumHere <= 0 || countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, maxSum <= 0)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, cnt)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ unfoldr (\case
                                       Cons x xs -> Just (x,xs)
                                       Nil -> Nothing)
      $ cods A.! this
{-
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let !(!compo_ch,!used_ch)
              = foldl' (\ !(!x0,!x1) !(!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
-}

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE processLine #-}
    processLine = \ !(!i,!bs0) -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \ !(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    codd :: UArray Int Int
    codd = runSTUArray $ do
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, False)
      GT -> (maxSum, maxSumHere, numCompo, countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, False)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, True)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  cods <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BSL.ByteString -> Array Int ListInt
preprocess n bstr = runSTArray $ do
  arr <- A.newArray (1,n) Nil
  forM_ (unfoldr processLine bstr) $ \ !(!l, !r) -> do
    lls <- A.readArray arr l
    A.writeArray arr l $! Cons r lls
    rls <- A.readArray arr r
    A.writeArray arr r $! Cons l rls
  return arr
  where
    {-# INLINE processLine #-}
    processLine = \ !bs0 -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((l,r),bs2)

query :: Int -> UArray Int Int64 -> Array Int ListInt -> (Int64,Int)
query n as cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let !(!compo_ch,!used_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ unfoldr (\case
                                       Cons x xs -> Just (x,xs)
                                       Nil -> Nothing)
      $ cods A.! this

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.array (1,n) . unfoldr processInt . (1,)
    <$> BS.getLine :: IO (UArray Int Int)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom
    where
      {-# INLINE processInt #-}
      processInt = \(!i,!bs) -> do
        (!e,!bs1) <- BS.readInt $ BS.dropWhile (< '-') bs
        return $! ((i,e),(i+1,bs1))

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE processInt #-}
    processInt = \(!i,!bs) -> do
      (!e,!bs1) <- BSL.readInt $ BSL.dropWhile (< '-') bs
      return $! ((i,e),(i+1,bs1))
    -- domcods :: UArray Int Int
    -- domcods = A.array (0, 2*n - 3) $ unfoldr processInt (0,bstr)
    codd :: UArray Int Int
    codd = runSTUArray $ do
      domcods_ <- A.newArray_ (0,2*n-3) :: ST s (STUArray s Int Int)
      forM_ (unfoldr processInt (0,bstr)) $ \ (!i,!e) -> do
        A.writeArray domcods_ i e
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        j <- A.readArray domcods_ i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        dom <- A.readArray domcods_ i
        cod <- A.readArray domcods_ (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, retSum, 1, nonpos)
      GT -> (maxSum, retSum, numCompo, countable')
      EQ | countable -> (maxSum, retSum, 1 + numCompo, nonpos)
         | otherwise -> (maxSum, retSum, numCompo, False)
      where
        (!retSum, !countable', !nonpos)
          | maxSumHere > 0 = (maxSumHere, countable, False)
          | otherwise      = (0,          True,      True)
        {-# INLINE maxSumHere #-}
        !maxSumHere = fromIntegral (as A.! this) + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case compare maxSum msum of
               GT -> (maxSum, sumsub + sumCh,       numCompo,        cntbl)
               LT -> (msum,   sumsub + sumCh, cmp,            cnt         )
               EQ -> (msum,   sumsub + sumCh, cmp + numCompo, cnt && cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent)
      $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  cods <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BSL.ByteString -> Array Int ListInt
preprocess n bstr = runSTArray $ do
  arr <- A.newArray (1,n) Nil
  forM_ (unfoldr processLine bstr) $ \ !(!l, !r) -> do
    lls <- A.readArray arr l
    A.writeArray arr l $! Cons r lls
    rls <- A.readArray arr r
    A.writeArray arr r $! Cons l rls
  return arr
  where
    {-# INLINE processLine #-}
    processLine = \ !bs0 -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((l,r),bs2)

query :: Int -> UArray Int Int64 -> Array Int ListInt -> (Int64,Int)
query n as cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let !(!compo_ch,!used_ch)
              = foldl' (\ !(!x0,!x1) !(!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ unfoldr (\case
                                       Cons x xs -> Just (x,xs)
                                       Nil -> Nothing)
      $ cods A.! this

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  codd <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as codd
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> UArray Int Int
preprocess n bstr = codd
  where
    {-# INLINE process #-}
    process = BSL.readInt . BSL.dropWhile (`elem` " \n\r")
    domcods :: UArray Int Int
    domcods = A.listArray (0, 2*n - 3) $ unfoldr process bstr
    codd :: UArray Int Int
    codd = runSTUArray $ do
      arr <- A.newArray (0,3*n-2) 0
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        when (j < n) $ do
          x <- A.readArray arr (j+1)
          A.writeArray arr (j+1) (x+1)
      A.writeArray arr 0 (n+1)
      forM_ [1..n] $ \ !i -> do
        x <- A.readArray arr (i-1)
        y <- A.readArray arr i
        A.writeArray arr i (x+y)
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray arr dom
        A.writeArray arr dom (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray arr cod
        A.writeArray arr cod (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int -> (Int64,Int)
query n as codd = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, False)
      GT -> (maxSum, maxSumHere, numCompo, countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, False)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, True)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (codd A.!) [codd A.! (this-1) .. codd A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  (cntE, cods) <- preprocess n <$> BS.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BS.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    processLine = \(!i,!bs0) -> do
      (!l,!bs1) <- BS.readInt $ BS.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BS.readInt $ BS.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

query :: Int -> UArray Int Int64 -> UArray Int Int -> UArray Int Int -> (Int64,Int)
query n as cntE cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let (!compo_ch,!used_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int64
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int64
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int64 -> Int -> Int -> ST s Int64
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict
import Data.Int

data ListInt = Cons {-# UNPACK #-} !Int !ListInt | Nil

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . map fromIntegral . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int64)
  (cntE, cods) <- preprocess n <$> BSL.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

{-# INLINE preprocess #-}
preprocess :: Int -> BSL.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    {-# INLINE processLine #-}
    processLine = \ !(!i,!bs0) -> do
      (!l,!bs1) <- BSL.readInt $ BSL.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BSL.readInt $ BSL.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \ !(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

{-# INLINE query #-}
query :: Int -> UArray Int Int64 -> UArray Int Int ->
         UArray Int Int-> (Int64,Int)
query n as cntE cods = (maxSum * fromIntegral numCompo, numCompo)
  where
    maxSum :: Int64; numCompo :: Int
    (maxSum, !sumIncl_, !numCompo, !countable) = dfs (-1) 1
    dfs :: Int -> Int -> (Int64, Int64, Int, Bool)
    dfs parent this = case (compare maxSum maxSumHere) of
      LT -> (maxSumHere, maxSumHere, 1, False)
      GT -> (maxSum, maxSumHere, numCompo, countable)
      EQ | countable -> (maxSum, maxSumHere, 1 + numCompo, True)
         | otherwise -> (maxSum, maxSumHere, numCompo, False)
      where
        {-# INLINE maxSumHere #-}
        maxSumHere = as A.! this + sumCh
        !(!maxSum, !sumCh, !numCompo, !countable)
          = foldl' folder (minBound, 0, 0, True)
            $ map (dfs this) $ children parent this
        {-# INLINE folder #-}
        folder = \ !(!maxSum, !sumCh, !numCompo, !cntbl)
                    !(!msum, !sumsub, !cmp, !cnt)
          -> case (compare maxSum msum, sumsub > 0) of
               (GT,True) -> (maxSum, sumsub + sumCh,     numCompo, cntbl)
               (GT,_)    -> (maxSum,        sumCh,       numCompo, cntbl)
               (LT,True) -> (msum, sumsub + sumCh,            cmp, cnt)
               (LT,_)    -> (msum,          sumCh,            cmp, True)
               (EQ,True) -> (msum, sumsub + sumCh, cmp + numCompo, cnt && cntbl)
               (EQ,_)    -> (msum,          sumCh, cmp + numCompo, cntbl)
    {-# INLINE children #-}
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int)
  (cntE, cods) <- preprocess n <$> BS.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BS.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    processLine = \(!i,!bs0) -> do
      (!l,!bs1) <- BS.readInt $ BS.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BS.readInt $ BS.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

query :: Int -> UArray Int Int -> UArray Int Int -> UArray Int Int -> (Int,Int)
query n as cntE cods = (maxSum * numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let (!compo_ch,!used_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this <= 0 && used_ch)
    maxSum :: Int
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int -> Int -> Int -> ST s Int
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int)
  (cntE, cods) <- preprocess n <$> BS.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BS.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    processLine = \(!i,!bs0) -> do
      (!l,!bs1) <- BS.readInt $ BS.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BS.readInt $ BS.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

query :: Int -> UArray Int Int -> UArray Int Int -> UArray Int Int -> (Int,Int)
query n as cntE cods = (maxSum * numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = numCompo_dfs (-1) False 1
    numCompo_dfs :: Int -> Bool -> Int -> Int
    numCompo_dfs !parent !eqParent !this =
      let !eqThis = sumAround A.! this == maxSum
      in (if eqThis && not eqParent then (1+) else id)
         $ sum $ map (numCompo_dfs this eqThis) $ filter (/= parent)
         $ map (cods A.!) $ [cntE A.! (this-1) .. cntE A.! this - 1]
    maxSum :: Int
    maxSum = maximum $ A.elems sumAround
    sumAround :: UArray Int Int
    sumAround = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      forM_ [0 .. cntE A.! 1 - 1] $ sumAround_dfs arr 1 . (cods A.!)
      return arr
    sumSubtrees_dfs :: STUArray s Int Int -> Int -> Int -> ST s Int
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM [cntE A.! (this-1) .. cntE A.! this - 1]
        $ \outEdge ->
            if cods A.! outEdge == parent then
              return 0
            else
              sumSubtrees_dfs arr this $ cods A.! outEdge
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    sumAround_dfs :: STUArray s Int Int -> Int -> Int -> ST s ()
    sumAround_dfs arr !parent !this = do
      sumAroundParent <- A.readArray arr parent
      sumSubtreeThis <- A.readArray arr this
      let sumAbove = sumAroundParent - max 0 sumSubtreeThis
          sumAroundThis = sumSubtreeThis + max 0 sumAbove
      A.writeArray arr this sumAroundThis
      forM_ [cntE A.! (this-1) .. cntE A.! this - 1]
        $ \outEdge ->
            if cods A.! outEdge == parent then
              return ()
            else
              sumAround_dfs arr this $ cods A.! outEdge

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int)
  (cntE, cods) <- preprocess n <$> BS.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BS.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    processLine = \(!i,!bs0) -> do
      (!l,!bs1) <- BS.readInt $ BS.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BS.readInt $ BS.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

query :: Int -> UArray Int Int -> UArray Int Int -> UArray Int Int -> (Int,Int)
query n as cntE cods = (maxSum * numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let (!compo_ch,!used_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not used_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, sumSubtrees A.! this > 0 && used_ch)
    maxSum :: Int
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int -> Int -> Int -> ST s Int
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE TupleSections#-}
{-# LANGUAGE ExplicitForAll #-}

import Data.Bits
import Data.List
import qualified Data.ByteString.Char8 as BS
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import qualified Data.Array.IArray as A
import qualified Data.Array.MArray.Safe as A
-- import qualified Data.Array.MArray as A
import Data.Array (Array)
import Data.Array.Unboxed (UArray)
import Data.Array.IArray (IArray)
import Data.Array.MArray.Safe (MArray)
import Data.Array.IO.Safe (IOArray, IOUArray)
import Data.Array.ST.Safe (STArray, STUArray, runSTArray, runSTUArray)
{-
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
-}
import Control.Monad
import Control.Monad.ST.Safe
-- import Control.Monad.ST
import Control.Applicative
import Data.Maybe
import Data.Tuple
import Data.Ord
import Control.Monad.State.Strict

main :: IO ()
main = do
  n <- read <$> getLine
  as <- A.listArray (1,n) . unfoldr (BS.readInt . BS.dropWhile (==' '))
    <$> BS.getLine :: IO (UArray Int Int)
  (cntE, cods) <- preprocess n <$> BS.getContents
  let (numer,denom) = query n as cntE cods
  putStrLn $ shows numer $ ' ' : show denom

preprocess :: Int -> BS.ByteString -> (UArray Int Int, UArray Int Int)
preprocess n bstr = (count, sortedCods)
  where
    processLine = \(!i,!bs0) -> do
      (!l,!bs1) <- BS.readInt $ BS.dropWhile (`elem` " \n\r") bs0
      (!r,!bs2) <- BS.readInt $ BS.dropWhile (==' ') bs1
      return $ ((i,l,r),(i+2,bs2))
    domcods = runSTUArray $ do
      domcods <- A.newArray_ (0, 2*n - 3)
      forM_ (unfoldr processLine (0,bstr)) $ \(!i,!l,!r) -> do
        A.writeArray domcods i l
        A.writeArray domcods (i+1) r
      return domcods
    count = runSTUArray $ do
      count <- (A.newArray (0,n) 0 :: forall s. ST s (STUArray s Int Int))
      forM_ [0..2*n-3] $ \ !i -> do
        let j = domcods A.! i
        x <- A.readArray count j
        A.writeArray count j (x+1)
      forM_ [2..n] $ \ !i -> do
        x <- A.readArray count (i-1)
        y <- A.readArray count i
        A.writeArray count i (x+y)
      return count
    sortedCods = runSTUArray $ do
      arr <- A.newArray_ (0,2*n-3)
      count' <- (A.thaw count :: forall s. ST s (STUArray s Int Int))
      forM_ [0,2..2*(n-2)] $ \ !i -> do
        let dom = domcods A.! i
            cod = domcods A.! (i+1)
        j_dom <- A.readArray count' (dom-1)
        A.writeArray count' (dom-1) (j_dom+1)
        A.writeArray arr j_dom cod
        j_cod <- A.readArray count' (cod-1)
        A.writeArray count' (cod-1) (j_cod+1)
        A.writeArray arr j_cod dom
      return arr

query :: Int -> UArray Int Int -> UArray Int Int -> UArray Int Int -> (Int,Int)
query n as cntE cods = (maxSum * numCompo, numCompo)
  where
    numCompo :: Int
    numCompo | maxSum <= 0 = length $ filter (== maxSum) $ A.elems as
             | otherwise   = let (x,!y) = numCompo_dfs (-1) 1 in x
    numCompo_dfs :: Int -> Int -> (Int,Bool)
    numCompo_dfs !parent !this
      = let (!compo_ch,!top_ch)
              = foldl' (\(!x0,!x1) (!y0,!y1) -> (x0 + y0, x1 || y1)) (0,False)
                         $ map (numCompo_dfs this) $ children parent this
        in if sumSubtrees A.! this == maxSum && not top_ch
           then
             (1 + compo_ch, True)
           else
             (compo_ch, False)
    maxSum :: Int
    maxSum = maximum $ A.elems sumSubtrees
    sumSubtrees :: UArray Int Int
    sumSubtrees = runSTUArray $ do
      arr <- A.newArray_ (1,n)
      sumSubtrees_dfs arr (-1) 1
      return arr
    sumSubtrees_dfs :: STUArray s Int Int -> Int -> Int -> ST s Int
    sumSubtrees_dfs arr !parent !this = do
      sumChildren <-
        fmap sum $ forM (children parent this) $ sumSubtrees_dfs arr this
      let ret = sumChildren + as A.! this
      A.writeArray arr this ret
      return $! max 0 ret
    children :: Int -> Int -> [Int]
    children parent this =
      filter (/= parent) $ map (cods A.!) [cntE A.! (this-1) .. cntE A.! this - 1]