#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <random>
#include <stdio.h>
#include <fstream>
#include <functional>
#include <cassert>
using namespace std;
#define all(x) (x).begin(), (x).end()
typedef long long ll;
template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) {
for (int i = 0; i < int(v.size()); i++) os << v[i] << (i + 1 == int(v.size()) ? "" : " ");
return os;
}
#ifdef LOCAL
#define debug(x) cerr << #x << " = " << (x) << " (L" << __LINE__ << ") \n";
#else
#define debug(x) void(0)
#endif
struct HeavyLightDecomposition {
vector<vector<int>> G;
int n, time;
vector<int> par, sub, dep, head, vid, vid_inv;
HeavyLightDecomposition(int n)
: G(n), n(n), time(0), par(n, -1), sub(n), dep(n, 0), head(n), vid(n, -1), vid_inv(n) {}
void add_edge(int u, int v) {
G[u].emplace_back(v);
G[v].emplace_back(u);
}
void build() {
dfs_sz(0);
head[0] = 0;
dfs_hld(0);
for (int i = 0; i < n; i++) vid_inv[vid[i]] = i;
}
int idx(int v) { return vid[v]; }
int la(int v, int k) {
while (1) {
int u = head[v];
if (vid[v] - k >= vid[u]) return vid_inv[vid[v] - k];
k -= vid[v] - vid[u] + 1;
v = par[u];
}
}
int distance(int u, int v) { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; }
int lca(int u, int v) {
for (;; v = par[head[v]]) {
if (vid[u] > vid[v]) swap(u, v);
if (head[u] == head[v]) return u;
}
}
int jump(int s, int t, int i) {
if (i == 0) return s;
int p = lca(s, t), d = dep[s] + dep[t] - 2 * dep[p];
if (d < i) return -1;
if (dep[s] - dep[p] >= i) return la(s, i);
return la(t, d - i);
}
template <typename F> void query_path(int u, int v, const F& f) {
int p = lca(u, v);
for (auto& e : ascend(u, p)) f(e.second, e.first + 1);
f(vid[p], vid[p] + 1);
for (auto& e : descend(p, v)) f(e.first, e.second + 1);
}
vector<pair<int, int>> ascend(int u, int v) {
vector<pair<int, int>> res;
while (head[u] != head[v]) {
res.emplace_back(vid[u], vid[head[u]]);
u = par[head[u]];
}
if (u != v) res.emplace_back(vid[u], vid[v] + 1);
return res;
}
vector<pair<int, int>> descend(int u, int v) {
if (u == v) return {};
if (head[u] == head[v]) return {{vid[u] + 1, vid[v]}};
auto res = descend(u, par[head[v]]);
res.emplace_back(vid[head[v]], vid[v]);
return res;
}
void dfs_sz(int v) {
sub[v] = 1;
if (not G[v].empty() and G[v].front() == par[v]) swap(G[v].front(), G[v].back());
for (int& u : G[v]) {
if (u == par[v]) continue;
par[u] = v;
dep[u] = dep[v] + 1;
dfs_sz(u);
sub[v] += sub[u];
if (sub[u] > sub[G[v].front()]) swap(u, G[v].front());
}
}
void dfs_hld(int v) {
vid[v] = time++;
for (int& u : G[v]) {
if (u == par[v]) continue;
head[u] = (u == G[v][0] ? head[v] : u);
dfs_hld(u);
}
}
};
int ceil_pow2(int n) {
int x = 0;
while ((1ULL << x) < n) x++;
return x;
}
//const ll IINF = (1LL << 60) - 1;
const int IINF = 100;
struct segtree {
using S = int;
S op(S a, S b) { return max(a, b); }
S e() { return -IINF; }
int n, size, log;
vector<S> d;
segtree(int nn) : n(nn) {
log = ceil_pow2(n);
size = 1 << log;
d = vector<S>(2 * size, e());
}
void set(int p, S x) {
assert(0 <= p and p < n);
p += size;
d[p] = x;
while (p > 1) {
p >>= 1;
d[p] = op(d[2 * p], d[2 * p + 1]);
}
}
S prod(int l, int r) {
assert(0 <= l and l <= r and r <= n);
S sml = e(), smr = e();
l += size, r += size;
while (l < r) {
if (l & 1) sml = op(sml, d[l++]);
if (r & 1) smr = op(d[--r], smr);
l >>= 1, r >>= 1;
}
return op(sml, smr);
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> C(n);
vector<vector<int>> c_cnt(n);
for (int i=0;i<n;i++){
cin >> C[i];
C[i]--;
c_cnt[C[i]].push_back(i);
}
HeavyLightDecomposition G(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
G.add_edge(--u, --v);
}
G.build();
//seg_hld: 1.頂点に乗せる値の変更 2.パス上の頂点の値のmax が処理できるデータ構造(パスが曲がる(図1のaみたいな)回分を管理)
//seg:パスが曲がらない(図2のaみたいな)やつの回分を管理
//ans_c: 色cを両端点とする最長回文
//L_c,R_c: 色cの頂点で、dfs順で先、後の頂点番号
//lca_c:L_c,R_cのlca
//dfs(v)
//c = 頂点 v の色
//v = L_c の場合:chmax(ans_c,seg.query(lca_c,v)+2)
//v = R_c の場合:chmax(ans_c,seg.query(lca_c,v)+2,seg_hld.query(L_c,lca_c))
//v = R_c の場合:L_c = lca_c ならば seg.set(v,ans_c) そうでないなら seg_hld.set(L_c,ans_c)
//dfs(隣接頂点)
//v = R_c の場合:L_c = lca_c ならば seg.set(v,-INF) そうでないなら seg_hld.set(L_c,-INF)
segtree seg_hld(n),seg_dig(n);
vector<int> ans(n,0);
vector<int> L(n,-1),R(n,-1);
vector<int> dep(n,0);
vector<int> c_lca(n,0);
for (int c=0;c<n;c++){
if (int(c_cnt[c].size()) > 1){
int u = c_cnt[c][0], v = c_cnt[c][1];
c_lca[c] = G.lca(u,v);
int adj = 0;
for (auto nv:G.G[u]){
if (nv == v){
adj = 1;
}
}
if (adj == 0){
ans[c] = 3;
}
else{
ans[c] = 2;
}
}
else{
ans[c] = 1;
}
}
auto dfs = [&](auto self,int v,int pv)->void {
int c = C[v];
if (int(c_cnt[c].size()) == 1){
L[c] = v;
R[c] = v;
for (auto nv:G.G[v]){
if (nv == pv){continue;}
dep[nv] = dep[v] + 1;
self(self,nv,v);
}
return ;
}
if (L[c] == -1){
//cout << "find_L" << endl;
//cout << c << " " << v << endl;
L[c] = v;
ans[c] = max(ans[c],2 + seg_dig.prod(dep[c_lca[c]],dep[v]));
for (auto nv:G.G[v]){
if (nv == pv){continue;}
dep[nv] = dep[v] + 1;
self(self,nv,v);
}
return ;
}
else{
R[c] = v;
ans[c] = max(ans[c], 2 + seg_dig.prod(dep[c_lca[c]],dep[v]));
int tmp = 0;
auto sub_q = [&](int l,int r){tmp = max(tmp,seg_hld.prod(l,r));};
G.query_path(L[c],c_lca[c],sub_q);
ans[c] = max(ans[c], 2 + tmp);
if (L[c] == c_lca[c]){
seg_dig.set(dep[L[c]],ans[c]);
}
else{
seg_hld.set(G.idx(L[c]),ans[c]);
}
for (auto nv:G.G[v]){
if (nv == pv){continue;}
dep[nv] = dep[v] + 1;
self(self,nv,v);
}
if (L[c] == c_lca[c]){
seg_dig.set(dep[L[c]],-1);
}
else{
seg_hld.set(G.idx(L[c]),-1);
}
}
};
dfs(dfs,0,-1);
cout << *max_element(ans.begin(),ans.end()) << endl;
return 0;
}