pub trait Zero: PartialEq + Sized {
    fn zero() -> Self;
    #[inline]
    fn is_zero(&self) -> bool {
        self == &Self::zero()
    }
}
pub trait One: PartialEq + Sized {
    fn one() -> Self;
    #[inline]
    fn is_one(&self) -> bool {
        self == &Self::one()
    }
}
macro_rules !zero_one_impls {($({$Trait :ident $method :ident $($t :ty ) *,$e :expr } ) *) =>{$($(impl $Trait for $t {#[inline ] fn $method () ->Self {$e } } ) *) *} ;}
zero_one_impls !({Zero zero u8 u16 u32 u64 usize i8 i16 i32 i64 isize u128 i128 ,0 } {Zero zero f32 f64 ,0. } {One one u8 u16 u32 u64 usize i8 i16 i32 i64 isize u128 i128 ,1 } {One one f32 f64 ,1. } );
pub trait IterScan: Sized {
    type Output;
    fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output>;
}
pub trait MarkedIterScan: Sized {
    type Output;
    fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output>;
}
#[derive(Clone, Debug)]
pub struct Scanner<'a> {
    iter: std::str::SplitAsciiWhitespace<'a>,
}
mod scanner_impls {
    use super::*;
    impl<'a> Scanner<'a> {
        #[inline]
        pub fn new(s: &'a str) -> Self {
            let iter = s.split_ascii_whitespace();
            Self { iter }
        }
        #[inline]
        pub fn scan<T: IterScan>(&mut self) -> <T as IterScan>::Output {
            <T as IterScan>::scan(&mut self.iter).expect("scan error")
        }
        #[inline]
        pub fn mscan<T: MarkedIterScan>(&mut self, marker: T) -> <T as MarkedIterScan>::Output {
            marker.mscan(&mut self.iter).expect("scan error")
        }
        #[inline]
        pub fn scan_vec<T: IterScan>(&mut self, size: usize) -> Vec<<T as IterScan>::Output> {
            (0..size)
                .map(|_| <T as IterScan>::scan(&mut self.iter).expect("scan error"))
                .collect()
        }
        #[inline]
        pub fn iter<'b, T: IterScan>(&'b mut self) -> ScannerIter<'a, 'b, T> {
            ScannerIter {
                inner: self,
                _marker: std::marker::PhantomData,
            }
        }
    }
    macro_rules !iter_scan_impls {($($t :ty ) *) =>{$(impl IterScan for $t {type Output =Self ;#[inline ] fn scan <'a ,I :Iterator <Item =&'a str >>(iter :&mut I ) ->Option <Self >{iter .next () ?.parse ::<$t >() .ok () } } ) *} ;}
    iter_scan_impls !(char u8 u16 u32 u64 usize i8 i16 i32 i64 isize f32 f64 u128 i128 String );
    macro_rules !iter_scan_tuple_impl {($($T :ident ) *) =>{impl <$($T :IterScan ) ,*>IterScan for ($($T ,) *) {type Output =($(<$T as IterScan >::Output ,) *) ;#[inline ] fn scan <'a ,It :Iterator <Item =&'a str >>(_iter :&mut It ) ->Option <Self ::Output >{Some (($(<$T as IterScan >::scan (_iter ) ?,) *) ) } } } ;}
    iter_scan_tuple_impl!();
    iter_scan_tuple_impl!(A);
    iter_scan_tuple_impl !(A B );
    iter_scan_tuple_impl !(A B C );
    iter_scan_tuple_impl !(A B C D );
    iter_scan_tuple_impl !(A B C D E );
    iter_scan_tuple_impl !(A B C D E F );
    iter_scan_tuple_impl !(A B C D E F G );
    iter_scan_tuple_impl !(A B C D E F G H );
    iter_scan_tuple_impl !(A B C D E F G H I );
    iter_scan_tuple_impl !(A B C D E F G H I J );
    iter_scan_tuple_impl !(A B C D E F G H I J K );
    pub struct ScannerIter<'a, 'b, T> {
        inner: &'b mut Scanner<'a>,
        _marker: std::marker::PhantomData<fn() -> T>,
    }
    impl<'a, 'b, T: IterScan> Iterator for ScannerIter<'a, 'b, T> {
        type Item = <T as IterScan>::Output;
        #[inline]
        fn next(&mut self) -> Option<Self::Item> {
            <T as IterScan>::scan(&mut self.inner.iter)
        }
    }
}
pub mod marker {
    use super::*;
    use std::{iter::FromIterator, marker::PhantomData};
    #[derive(Debug, Copy, Clone)]
    pub struct Usize1;
    impl IterScan for Usize1 {
        type Output = usize;
        #[inline]
        fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output> {
            Some(<usize as IterScan>::scan(iter)?.checked_sub(1)?)
        }
    }
    #[derive(Debug, Copy, Clone)]
    pub struct Chars;
    impl IterScan for Chars {
        type Output = Vec<char>;
        #[inline]
        fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output> {
            Some(iter.next()?.chars().collect())
        }
    }
    #[derive(Debug, Copy, Clone)]
    pub struct CharsWithBase(pub char);
    impl MarkedIterScan for CharsWithBase {
        type Output = Vec<usize>;
        #[inline]
        fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output> {
            Some(
                iter.next()?
                    .chars()
                    .map(|c| (c as u8 - self.0 as u8) as usize)
                    .collect(),
            )
        }
    }
    #[derive(Debug, Copy, Clone)]
    pub struct Collect<T: IterScan, B: FromIterator<<T as IterScan>::Output>> {
        size: usize,
        _marker: PhantomData<fn() -> (T, B)>,
    }
    impl<T: IterScan, B: FromIterator<<T as IterScan>::Output>> Collect<T, B> {
        pub fn new(size: usize) -> Self {
            Self {
                size,
                _marker: PhantomData,
            }
        }
    }
    impl<T: IterScan, B: FromIterator<<T as IterScan>::Output>> MarkedIterScan for Collect<T, B> {
        type Output = B;
        #[inline]
        fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output> {
            Some(
                (0..self.size)
                    .map(|_| <T as IterScan>::scan(iter).expect("scan error"))
                    .collect::<B>(),
            )
        }
    }
}
#[macro_export]
macro_rules !min {($e :expr ) =>{$e } ;($e :expr ,$($es :expr ) ,+) =>{std ::cmp ::min ($e ,min !($($es ) ,+) ) } ;}
#[macro_export]
macro_rules !chmin {($dst :expr ,$($src :expr ) ,+) =>{{let x =std ::cmp ::min ($dst ,min !($($src ) ,+) ) ;$dst =x ;} } ;}
#[macro_export]
macro_rules !max {($e :expr ) =>{$e } ;($e :expr ,$($es :expr ) ,+) =>{std ::cmp ::max ($e ,max !($($es ) ,+) ) } ;}
#[macro_export]
macro_rules !chmax {($dst :expr ,$($src :expr ) ,+) =>{{let x =std ::cmp ::max ($dst ,max !($($src ) ,+) ) ;$dst =x ;} } ;}
pub use sparse_graph::{
    Adjacency, BidirectionalGraphScanner, BidirectionalSparseGraph, DirectedGraphScanner,
    DirectedSparseGraph, SparseGraph, TreeGraphScanner, UndirectedGraphScanner,
    UndirectedSparseGraph,
};
pub mod sparse_graph {
    use super::*;
    use std::{iter, marker::PhantomData, ops, slice};
    type Marker<T> = PhantomData<fn() -> T>;
    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
    pub struct DirectedEdge;
    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
    pub struct UndirectedEdge;
    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
    pub struct BidirectionalEdge;
    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
    pub struct Adjacency {
        pub id: usize,
        pub to: usize,
    }
    impl Adjacency {
        pub fn new(id: usize, to: usize) -> Adjacency {
            Adjacency { id, to }
        }
    }
    /// Static Sparse Graph represented as Compressed Sparse Row.
    #[derive(Debug, Clone)]
    pub struct SparseGraph<D> {
        vsize: usize,
        pub start: Vec<usize>,
        pub elist: Vec<Adjacency>,
        pub edges: Vec<(usize, usize)>,
        _marker: Marker<D>,
    }
    impl<D> SparseGraph<D> {
        /// Return the number of vertices.
        pub fn vertices_size(&self) -> usize {
            self.vsize
        }
        /// Return the number of edges.
        pub fn edges_size(&self) -> usize {
            self.edges.len()
        }
        /// Return an iterator over graph vertices.
        pub fn vertices(&self) -> ops::Range<usize> {
            0..self.vertices_size()
        }
        /// Return a slice of adjacency vertices.
        pub fn adjacencies(&self, v: usize) -> slice::Iter<'_, Adjacency> {
            self.elist[self.start[v]..self.start[v + 1]].iter()
        }
    }
    pub trait SparseGraphConstruction: Sized {
        fn construct_graph(vsize: usize, edges: Vec<(usize, usize)>) -> SparseGraph<Self>;
    }
    impl<D: SparseGraphConstruction> SparseGraph<D> {
        /// Construct graph from edges.
        pub fn from_edges(vsize: usize, edges: Vec<(usize, usize)>) -> Self {
            D::construct_graph(vsize, edges)
        }
    }
    impl SparseGraphConstruction for DirectedEdge {
        fn construct_graph(vsize: usize, edges: Vec<(usize, usize)>) -> SparseGraph<Self> {
            let mut start: Vec<_> = iter::repeat(0).take(vsize + 1).collect();
            let mut elist = Vec::with_capacity(edges.len());
            unsafe { elist.set_len(edges.len()) }
            for (from, _) in edges.iter().cloned() {
                start[from] += 1;
            }
            for i in 1..=vsize {
                start[i] += start[i - 1];
            }
            for (id, (from, to)) in edges.iter().cloned().enumerate() {
                start[from] -= 1;
                elist[start[from]] = Adjacency::new(id, to);
            }
            SparseGraph {
                vsize,
                start,
                elist,
                edges,
                _marker: PhantomData,
            }
        }
    }
    impl SparseGraphConstruction for UndirectedEdge {
        fn construct_graph(vsize: usize, edges: Vec<(usize, usize)>) -> SparseGraph<Self> {
            let mut start: Vec<_> = iter::repeat(0).take(vsize + 1).collect();
            let mut elist = Vec::with_capacity(edges.len() * 2);
            unsafe { elist.set_len(edges.len() * 2) }
            for (from, to) in edges.iter().cloned() {
                start[to] += 1;
                start[from] += 1;
            }
            for i in 1..=vsize {
                start[i] += start[i - 1];
            }
            for (id, (from, to)) in edges.iter().cloned().enumerate() {
                start[from] -= 1;
                elist[start[from]] = Adjacency::new(id, to);
                start[to] -= 1;
                elist[start[to]] = Adjacency::new(id, from);
            }
            SparseGraph {
                vsize,
                start,
                elist,
                edges,
                _marker: PhantomData,
            }
        }
    }
    impl SparseGraphConstruction for BidirectionalEdge {
        fn construct_graph(vsize: usize, edges: Vec<(usize, usize)>) -> SparseGraph<Self> {
            let mut start: Vec<_> = iter::repeat(0).take(vsize + 1).collect();
            let mut elist = Vec::with_capacity(edges.len() * 2);
            unsafe { elist.set_len(edges.len() * 2) }
            for (from, to) in edges.iter().cloned() {
                start[to] += 1;
                start[from] += 1;
            }
            for i in 1..=vsize {
                start[i] += start[i - 1];
            }
            for (id, (from, to)) in edges.iter().cloned().enumerate() {
                start[from] -= 1;
                elist[start[from]] = Adjacency::new(id * 2, to);
                start[to] -= 1;
                elist[start[to]] = Adjacency::new(id * 2 + 1, from);
            }
            SparseGraph {
                vsize,
                start,
                elist,
                edges,
                _marker: PhantomData,
            }
        }
    }
    pub type DirectedSparseGraph = SparseGraph<DirectedEdge>;
    pub type UndirectedSparseGraph = SparseGraph<UndirectedEdge>;
    pub type BidirectionalSparseGraph = SparseGraph<BidirectionalEdge>;
    pub struct SparseGraphScanner<U: IterScan<Output = usize>, T: IterScan, D> {
        vsize: usize,
        esize: usize,
        _marker: Marker<(U, T, D)>,
    }
    impl<U: IterScan<Output = usize>, T: IterScan, D> SparseGraphScanner<U, T, D> {
        pub fn new(vsize: usize, esize: usize) -> Self {
            Self {
                vsize,
                esize,
                _marker: PhantomData,
            }
        }
    }
    impl<U: IterScan<Output = usize>, T: IterScan, D: SparseGraphConstruction> MarkedIterScan
        for SparseGraphScanner<U, T, D>
    {
        type Output = (SparseGraph<D>, Vec<<T as IterScan>::Output>);
        fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output> {
            let mut edges = Vec::with_capacity(self.esize);
            let mut rest = Vec::with_capacity(self.esize);
            for _ in 0..self.esize {
                edges.push((U::scan(iter)?, U::scan(iter)?));
                rest.push(T::scan(iter)?);
            }
            let graph = SparseGraph::from_edges(self.vsize, edges);
            Some((graph, rest))
        }
    }
    pub type DirectedGraphScanner<U, T> = SparseGraphScanner<U, T, DirectedEdge>;
    pub type UndirectedGraphScanner<U, T> = SparseGraphScanner<U, T, UndirectedEdge>;
    pub type BidirectionalGraphScanner<U, T> = SparseGraphScanner<U, T, BidirectionalEdge>;
    pub struct TreeGraphScanner<U: IterScan<Output = usize>, T: IterScan> {
        vsize: usize,
        _marker: Marker<(U, T)>,
    }
    impl<U: IterScan<Output = usize>, T: IterScan> TreeGraphScanner<U, T> {
        pub fn new(vsize: usize) -> Self {
            Self {
                vsize,
                _marker: PhantomData,
            }
        }
    }
    impl<U: IterScan<Output = usize>, T: IterScan> MarkedIterScan for TreeGraphScanner<U, T> {
        type Output = (UndirectedSparseGraph, Vec<<T as IterScan>::Output>);
        fn mscan<'a, I: Iterator<Item = &'a str>>(self, iter: &mut I) -> Option<Self::Output> {
            UndirectedGraphScanner::<U, T>::new(self.vsize, self.vsize - 1).mscan(iter)
        }
    }
}
fn main() {
    #![allow(unused_imports, unused_macros)]
    use std::io::{stdin, stdout, BufWriter, Read as _, Write as _};
    let mut _in_buf = Vec::new();
    stdin().read_to_end(&mut _in_buf).expect("io error");
    let _in_buf = unsafe { String::from_utf8_unchecked(_in_buf) };
    let mut scanner = Scanner::new(&_in_buf);
    macro_rules !scan {() =>{scan !(usize ) } ;(($($t :tt ) ,*) ) =>{($(scan !($t ) ) ,*) } ;([$t :tt ;$len :expr ] ) =>{(0 ..$len ) .map (|_ |scan !($t ) ) .collect ::<Vec <_ >>() } ;([$t :ty ;$len :expr ] ) =>{scanner .scan_vec ::<$t >($len ) } ;([$t :ty ] ) =>{scanner .iter ::<$t >() } ;({$e :expr } ) =>{scanner .mscan ($e ) } ;($t :ty ) =>{scanner .scan ::<$t >() } ;}
    let _out = stdout();
    let mut _out = BufWriter::new(_out.lock());
    macro_rules !print {($($arg :tt ) *) =>(::std ::write !(_out ,$($arg ) *) .expect ("io error" ) ) }
    macro_rules !println {($($arg :tt ) *) =>(::std ::writeln !(_out ,$($arg ) *) .expect ("io error" ) ) }
    macro_rules! echo {
        ($iter :expr ) => {
            echo!($iter, '\n')
        };
        ($iter :expr ,$sep :expr ) => {
            let mut iter = $iter.into_iter();
            if let Some(item) = iter.next() {
                print!("{}", item);
            }
            for item in iter {
                print!("{}{}", $sep, item);
            }
            println!();
        };
    }
    let n = scan!();
    let (g, _) = scan!({ TreeGraphScanner::<marker::Usize1, ()>::new(n) });
    let mut ans = (n * 2, std::usize::MAX);
    for u in 0..n {
        let deg = g.tree_deg(u);
        let x = deg.iter().cloned().fold(1usize, |x, y| x.saturating_mul(y));
        chmin!(ans, (deg.len() + 1, x));
    }
    for (u, v) in g.edges.iter().cloned() {
        let deg = g.tree_deg2(u, v);
        let x = deg.iter().cloned().fold(1usize, |x, y| x.saturating_mul(y));
        chmin!(ans, (deg.len(), x));
    }
    println!("{} {}", ans.0, ans.1);
}
impl UndirectedSparseGraph {
    fn deg_dfs(&self, u: usize, p: usize, d: usize, deg: &mut Vec<usize>) {
        let mut dg = 0;
        if deg.len() == d {
            deg.push(0);
        }
        for a in self.adjacencies(u).filter(|a| a.to != p) {
            dg += 1;
            self.deg_dfs(a.to, u, d + 1, deg);
        }
        chmax!(deg[d], dg);
    }
    pub fn tree_deg(&self, root: usize) -> Vec<usize> {
        let mut deg = vec![];
        self.deg_dfs(root, self.vertices_size(), 0, &mut deg);
        deg.pop();
        deg
    }
    pub fn tree_deg2(&self, root: usize, root2: usize) -> Vec<usize> {
        let mut deg = vec![2];
        self.deg_dfs(root, root2, 1, &mut deg);
        self.deg_dfs(root2, root, 1, &mut deg);
        deg.pop();
        deg
    }
}

Measured memory_bytes: 2212