# include < bits/stdc++.h >
constexpr unsigned lg ( int x ) {
return sizeof ( int ) * 8 - 1 - __builtin_clz ( x ) ;
}
constexpr unsigned lg ( unsigned int x ) {
return sizeof ( unsigned ) * 8 - 1 - __builtin_clz ( x ) ;
}
constexpr unsigned lg ( long x ) {
return sizeof ( long ) * 8 - 1 - __builtin_clzl ( x ) ;
}
constexpr unsigned lg ( unsigned long x ) {
return sizeof ( unsigned long ) * 8 - 1 - __builtin_clzl ( x ) ;
}
constexpr unsigned lg ( long long x ) {
return sizeof ( long long ) * 8 - 1 - __builtin_clzll ( x ) ;
}
constexpr unsigned lg ( unsigned long long x ) {
return sizeof ( unsigned long long ) * 8 - 1 - __builtin_clzll ( x ) ;
}
constexpr unsigned ceil_lg ( int n ) {
return n == 0 ? 0 : 32 - __builtin_clz ( n - 1 ) ;
}
template < typename T > struct SparseTable {
size_t n , logn ;
std :: vector < std :: vector < T >> v ;
std :: function < T (T , T) > F ;
SparseTable () = default;
SparseTable ( const std :: vector < T > & a , std :: function < T ( T , T )> func )
: n ( a . size ()) , logn ( lg (n)) , v (logn + 1 , std :: vector < T >(n + 1 )) , F (func) {
v [ 0 ] = a ;
for ( size_t i = 1 ; i <= logn ; i ++ )
for ( size_t j = 0 ; j + ( 1 << i) - 1 < n ; j ++ )
v [i][j] = F ( v [i - 1 ][j] , v [i - 1 ][j + ( 1 << (i - 1 ))]) ;
}
T query ( size_t l , size_t r ) {
assert (l < r) ;
assert (l < n) ;
assert (r <= n) ;
int s = lg (r - l) ;
return F ( v [s][l] , v [s][r - ( 1 << s)]) ;
}
} ;
struct EulerLCA {
int n ;
std :: vector <int> pos , seq , dep ;
SparseTable <int> st ;
EulerLCA ( const std :: vector < std :: vector < int >> & g , int root ) : n ( g . size ()) , pos (n) , dep (n) {
seq . reserve ( 2 * n) ;
dfs (root , root , g) ;
st = SparseTable < int >(seq , [ & ] ( int u , int v ) { return pos [u] < pos [v] ? u : v ; }) ;
}
void dfs ( int u , int p , const std :: vector < std :: vector < int >> & g ) {
pos [u] = seq . size () ;
seq . push_back (u) ;
for ( auto v : g [u]) {
if (v == p) continue ;
dep [v] = dep [u] + 1 ;
dfs (v , u , g) ;
seq . push_back (u) ;
}
}
int lca ( int u , int v ) {
if ( pos [u] > pos [v]) std :: swap (u , v) ;
return st . query ( pos [u] , pos [v] + 1 ) ;
}
} ;
struct VirtualTree {
int n ;
EulerLCA lca ;
std :: vector < std :: vector <int >> tree ;
VirtualTree ( const std :: vector < std :: vector < int >> & g , int root )
: n ( g . size ()) , lca (g , root) , tree (n) {}
auto build_tree ( const std :: vector < int > & vertices )
-> std :: pair < int , const std :: vector < std :: vector < int >> & > {
auto v (vertices) ;
std :: sort ( v . begin () , v . end () , [ & ] ( int u , int v ) { return lca . pos [u] < lca . pos [v] ; }) ;
int len = v . size () ;
for ( int i = 1 ; i < len ; i ++ ) {
v . push_back ( lca . lca ( v [i - 1 ] , v [i])) ;
}
std :: sort ( v . begin () , v . end () , [ & ] ( int u , int v ) { return lca . pos [u] < lca . pos [v] ; }) ;
v . erase ( std :: unique ( v . begin () , v . end ()) , v . end ()) ;
for ( int i = 1 ; i < ( int ) v . size () ; i ++ ) {
tree [ lca . lca ( v [i - 1 ] , v [i])] . push_back ( v [i]) ;
}
return { v [ 0 ] , tree} ;
}
void clear ( const std :: vector < int > v ) {
for ( auto u : v) {
tree [u] . clear () ;
}
}
void clear ( int root ) {
for ( auto v : tree [root]) {
clear (v) ;
}
tree [root] . clear () ;
}
} ;
template < typename mint > struct Factorial {
std :: vector < mint > fac , invfac ;
Factorial ( int n ) : fac (n + 1 ) , invfac (n + 1 ) {
fac [ 0 ] = 1 ;
for ( int i = 1 ; i <= n ; i ++ ) {
fac [i] = fac [i - 1 ] * i ;
}
invfac [n] = fac [n] . inv () ;
for ( int i = n - 1 ; i >= 0 ; i -- ) {
invfac [i] = invfac [i + 1 ] * (i + 1 ) ;
}
}
mint C ( int n , int k ) {
if (k < 0 || k > n) return 0 ;
assert (( int ) size (fac) > n) ;
return fac [n] * invfac [n - k] * invfac [k] ;
}
mint P ( int n , int m ) {
assert ( ! fac . empty ()) ;
return fac [n] * invfac [n - m] ;
}
template < typename ... Args >
constexpr mint eval ( Args ... args ) {
return ((args > 0 ? fac [args] : invfac [ - args]) * ...) ;
}
} ;
using namespace std ;
using ll = long long ;
template < int MOD >
struct ModInt {
int val ;
ModInt ( ll v = 0 ) : val (v % MOD) { if (val < 0 ) val += MOD ; } ;
ModInt operator+ () const { return ModInt (val) ; }
ModInt operator- () const { return ModInt (MOD - val) ; }
ModInt inv () const {
auto a = val , m = MOD , u = 0 , v = 1 ;
while (a != 0 ) { auto t = m / a ; m -= t * a ; swap (a , m) ; u -= t * v ; swap (u , v) ; }
assert (m == 1 ) ;
return u ;
}
ModInt pow ( ll n ) const {
auto x = ModInt ( 1 ) ;
auto b = * this ;
while (n > 0 ) {
if (n & 1 ) x *= b ;
n >>= 1 ;
b *= b ;
}
return x ;
}
friend ModInt operator+ ( ModInt lhs , const ModInt & rhs ) { return lhs += rhs ; }
friend ModInt operator- ( ModInt lhs , const ModInt & rhs ) { return lhs -= rhs ; }
friend ModInt operator* ( ModInt lhs , const ModInt & rhs ) { return lhs *= rhs ; }
friend ModInt operator/ ( ModInt lhs , const ModInt & rhs ) { return lhs /= rhs ; }
ModInt & operator+= ( const ModInt & x ) { if ((val += x . val ) >= MOD) val -= MOD ; return * this ; }
ModInt & operator-= ( const ModInt & x ) { if ((val -= x . val ) < 0 ) val += MOD ; return * this ; }
ModInt & operator*= ( const ModInt & x ) { val = int64_t (val) * x . val % MOD ; return * this ; }
ModInt & operator/= ( const ModInt & x ) { return * this *= x . inv () ; }
bool operator== ( const ModInt & b ) const { return val == b . val ; }
bool operator!= ( const ModInt & b ) const { return val != b . val ; }
friend std :: istream & operator>> ( std :: istream & is , ModInt & x ) noexcept { return is >> x . val ; }
friend std :: ostream & operator<< ( std :: ostream & os , const ModInt & x ) noexcept { return os << x . val ; }
} ;
using mint = ModInt < 998244353 > ;
int main () {
ios :: sync_with_stdio ( false ) ;
cin . tie ( nullptr ) ;
int n ;
cin >> n ;
vector <int> a ( n ) ;
int mx = 0 ;
for ( auto& x : a ) {
cin >> x ;
mx = max (mx , x) ;
}
vector <int> minp ( mx + 1 ) , primes ;
for ( int i = 2 ; i <= mx ; i ++ ) {
if ( minp [i] == 0 ) {
minp [i] = i ;
primes . push_back (i) ;
}
for ( auto p : primes) {
if (i * p > mx) {
break ;
}
minp [i * p] = p ;
if (i % p == 0 ) {
break ;
}
}
}
vector < vector <int >> p ( mx + 1 ) ;
for ( int i = 0 ; i < n ; i ++ ) {
int x = a [i] ;
while (x > 1 ) {
int f = minp [x] ;
p [f] . push_back (i) ;
while (x % f == 0 ) {
x /= f ;
}
}
}
vector < vector <int >> g ( n ) ;
for ( int i = 1 ; i < n ; i ++ ) {
int u , v ;
cin >> u >> v ;
u --, v --;
g [u] . push_back (v) ;
g [v] . push_back (u) ;
}
VirtualTree tr ( g , 0 ) ;
Factorial < mint > f ( n ) ;
mint ans = 0 ;
for ( int fac = 1 ; fac <= mx ; fac ++ ) {
if ( p [fac] . size () < 3 ) {
continue ;
}
auto res = tr . build_tree ( p [fac]) ;
auto root = res . first ;
const auto& tree = res . second ;
auto dfs = [ & ] ( auto & slf , int u ) -> int {
int size = a [u] % fac == 0 ;
for ( auto v : tree [u]) {
int sz = slf (slf , v) ;
size += sz ;
ans += ( tr . lca . dep [v] - tr . lca . dep [u]) * ( f . C ( p [fac] . size () , 3 ) - f . C (sz , 3 ) - f . C ( p [fac] . size () - sz , 3 )) ;
}
return size ;
} ;
dfs (dfs , root) ;
tr . clear (root) ;
}
cout << ans << '\n' ;
}