{-# 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)
  (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 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.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

{-# 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]