use std::collections::HashMap; use modint::ModInt998244353; use proconio::{input, marker::Usize1}; type Mint = ModInt998244353; type Factors = HashMap; const MAX: usize = 1_000_000; fn main() { input! { n: usize, a: [usize; n], edges: [(Usize1, Usize1); n - 1], } let mut lpf = (0..=MAX).collect::>(); for i in 2..=MAX { if lpf[i] == i { for j in (i * i..=MAX).step_by(i) { if lpf[j] == j { lpf[j] = i; } } } } let factorize = |mut x: usize| { let mut res = HashMap::new(); while x > 1 { let p = lpf[x]; let mut cnt = 0; while x % p == 0 { x /= p; cnt += 1; } res.insert(p, cnt); } res }; let mut graph = vec![vec![]; n]; for &(u, v) in &edges { graph[u].push(v); graph[v].push(u); } let mut pre_order = vec![]; let mut stack = vec![(0, !0)]; while let Some((v, p)) = stack.pop() { pre_order.push(v); graph[v].retain(|&u| u != p); for &u in &graph[v] { stack.push((u, v)); } } let mut dp = a.iter().map(|&a| factorize(a)).collect::>(); for &v in pre_order.iter().rev() { let mut dp_v = std::mem::take(&mut dp[v]); for &u in &graph[v] { merge(&mut dp_v, &dp[u]); } dp[v] = dp_v; } let ans = dp .iter() .map(|factors| { let res = factors.iter().fold(Mint::new(1), |acc, (&p, &cnt)| { acc * Mint::new(p as u64).pow(cnt) }); res.to_string() }) .collect::>() .join("\n"); println!("{ans}"); } fn merge(parent: &mut Factors, child: &Factors) { for (&p, &cnt) in child { let entry = parent.entry(p).or_insert(0); *entry = (*entry).max(cnt); } } #[allow(dead_code)] mod modint { use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; pub type ModInt998244353 = ModInt<998244353>; pub type ModInt1000000007 = ModInt<1000000007>; type ModIntInner = u64; #[derive(Clone, Copy, PartialEq, Eq)] pub struct ModInt { val: ModIntInner, } impl ModInt { const IS_PRIME: bool = is_prime(M as u32); pub const fn modulus() -> ModIntInner { M } pub const fn new(val: ModIntInner) -> Self { assert!(M < (1 << 31)); Self { val: val.rem_euclid(M), } } pub const fn new_unchecked(val: ModIntInner) -> Self { Self { val } } pub const fn val(&self) -> ModIntInner { self.val } pub fn pow(self, mut exp: u64) -> Self { let mut result = Self::new(1); let mut base = self; while exp > 0 { if exp & 1 == 1 { result *= base; } base *= base; exp >>= 1; } result } pub fn inv(self) -> Self { assert!(Self::IS_PRIME); self.pow(M as u64 - 2).into() } } impl std::fmt::Display for ModInt { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.val) } } impl std::fmt::Debug for ModInt { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.val) } } impl std::str::FromStr for ModInt { type Err = std::num::ParseIntError; fn from_str(s: &str) -> Result { let value = s.parse::()?; Ok(ModInt::new(value)) } } impl Neg for ModInt { type Output = Self; fn neg(mut self) -> Self::Output { if self.val > 0 { self.val = M - self.val; } self } } impl>> AddAssign for ModInt { fn add_assign(&mut self, rhs: T) { self.val += rhs.into().val; if self.val >= M { self.val -= M; } } } impl>> SubAssign for ModInt { fn sub_assign(&mut self, rhs: T) { self.val = self.val.wrapping_sub(rhs.into().val); if self.val > M { self.val = self.val.wrapping_add(M); } } } impl>> MulAssign for ModInt { fn mul_assign(&mut self, rhs: T) { self.val = self.val * rhs.into().val % M; } } impl>> DivAssign for ModInt { fn div_assign(&mut self, rhs: T) { *self *= rhs.into().inv(); } } macro_rules! impl_binnary_operators { ($({ $op: ident, $op_assign: ident, $fn: ident, $fn_assign: ident}),*) => {$( impl>> $op for ModInt { type Output = ModInt; fn $fn(mut self, rhs: T) -> ModInt { self.$fn_assign(rhs.into()); self } } impl $op<&ModInt> for ModInt { type Output = ModInt; fn $fn(self, rhs: &ModInt) -> ModInt { self.$fn(*rhs) } } impl>> $op for &ModInt { type Output = ModInt; fn $fn(self, rhs: T) -> ModInt { (*self).$fn(rhs.into()) } } impl $op<&ModInt> for &ModInt { type Output = ModInt; fn $fn(self, rhs: &ModInt) -> ModInt { (*self).$fn(*rhs) } } impl $op_assign<&ModInt> for ModInt { fn $fn_assign(&mut self, rhs: &ModInt) { *self = self.$fn(*rhs); } } )*}; } impl_binnary_operators!( {Add, AddAssign, add, add_assign}, {Sub, SubAssign, sub, sub_assign}, {Mul, MulAssign, mul, mul_assign}, {Div, DivAssign, div, div_assign} ); impl std::iter::Sum for ModInt { fn sum>(iter: I) -> Self { iter.fold(Self::new(0), Add::add) } } impl<'a, const M: ModIntInner> std::iter::Sum<&'a Self> for ModInt { fn sum>(iter: I) -> Self { iter.fold(Self::new(0), Add::add) } } impl std::iter::Product for ModInt { fn product>(iter: I) -> Self { iter.fold(Self::new(1), Mul::mul) } } impl<'a, const M: ModIntInner> std::iter::Product<&'a Self> for ModInt { fn product>(iter: I) -> Self { iter.fold(Self::new(1), Mul::mul) } } macro_rules! impl_rem_euclid_signed { ($($ty:tt),*) => { $( impl From<$ty> for ModInt { fn from(value: $ty) -> ModInt { Self::new_unchecked((value as i64).rem_euclid(M as i64) as ModIntInner) } } )* }; } impl_rem_euclid_signed!(i8, i16, i32, i64, isize); macro_rules! impl_rem_euclid_unsigned { ($($ty:tt),*) => { $( impl From<$ty> for ModInt { fn from(value: $ty) -> ModInt { Self::new_unchecked((value as u64).rem_euclid(M as u64) as ModIntInner) } } )* }; } impl_rem_euclid_unsigned!(u8, u16, u32, u64, usize); const fn is_prime(n: u32) -> bool { const fn miller_rabin(n: u32, witness: u32) -> bool { let (n, witness) = (n as u64, witness as u64); let mut d = n >> (n - 1).trailing_zeros(); let mut y = { let (mut res, mut pow, mut e) = (1, witness, d); while e > 0 { if e & 1 == 1 { res = res * pow % n; } pow = pow * pow % n; e >>= 1; } res }; while d != n - 1 && y != 1 && y != n - 1 { y = y * y % n; d <<= 1; } y == n - 1 || d & 1 == 1 } const WITNESS: [u32; 3] = [2, 7, 61]; if n == 1 || n % 2 == 0 { return n == 2; } let mut i = 0; while i < WITNESS.len() { if !miller_rabin(n, WITNESS[i]) { return false; } i += 1; } true } }