Problem:
Coloured Mountain Hut
You are in charge of $N$ huts and the roads that connect them. Each hut has a number from $1$ to $N$. There are $N-1$ paths in total. All paths are the same length and can be taken in either direction. Also, from any hut, you can reach any other hut by going through some of the paths.
All the huts are painted with a color to mark them. Every hut has at least one other hut that is the same color as it, so that climbers can move between huts of the same color. For each hut, you are trying to find the hut that can be reached in the shortest travel distance among the other huts of the same color as that hut.
You are given the color of the hut and information about the paths that connect the huts directly to each other. Write a program that, for each hut, find the hut with the shortest travel distance among the other huts of the same color as the hut, and outputs the shortest travel distance. The length of the path is assumed to be 1 for all.
Input
The input is given in the following format.
$N$
$c_1$
$c_2$
:
$c_N$
$s_1$ $t_1$
$s_2$ $t_2$
:
$s_{N-1}$ $t_{N-1}$
In the first line, the number of huts $N$ ($2 \leq N \leq 200,000$) is given. In the following $N$ lines, the color of the $i$-th hut $c_i$ ($1 \leq c_i \leq N$) is given in integers. In the following $N-1$ lines, the information of the path directly connecting the two huts is given. $s_i$ and $t_i$ ($1 \leq s_i < t_i \leq N$) are the numbers of the two huts connected by the $i$-th path. For any two huts, there should be no more than one path that directly connects them.
Output
The output consists of $N$ lines. For hut $i$, find the hut with the shortest travel distance among the other huts of the same color, and output the shortest travel distance in line $i$.
Sample Input and Output
Sample Input
8
1
2
3
3
2
2
3
1
1 2
1 3
2 4
2 5
4 6
5 7
7 8
Sample Output
4
1
3
3
1
2
3
4

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ll = long long;
using pll = pair<ll, ll>;
#define vc vector
using vl = vc<ll>;

#define ov(a, b, c, name, ...) name
#define rep2(i, l, r) for (ll i = (l); i < ll(r); i++)
#define rep1(i, n) rep2(i, 0, n)
#define rep(...) ov(__VA_ARGS__, rep2, rep1)(__VA_ARGS__)
#define fec(...) for (const auto &__VA_ARGS__)
#define per(i, a, b) for (ll i = ll(a) - 1; i >= ll(b); i--)

#define ALL(x) begin(x), end(x)
#define SZ(x) (ll) size(x)
#define LB(v, x) (lower_bound(ALL(v), x) - begin(v))
#define eb emplace_back

bool chmin(auto& a, auto b) { return a > b ? a = b, 1 : 0; }
bool chmax(auto& a, auto b) { return a < b ? a = b, 1 : 0; }
const ll INF = ll(4e18) + 100;

void printvec(const auto& v) {
   for(auto it = begin(v); it != end(v); it++) cout << *it << " ";
   cout << endl;
}

#ifdef LOCAL
#define local(...) __VA_ARGS__
#else
#define local(...)
#endif

template<class G> struct HLD {
   int n;
   G& g;
   vc<int> sub, in, out, head, rev, par, d;
   HLD(G& g, int root = 0) : n(g.size()), g(g), sub(n), in(n), out(n), head(n), rev(n), par(n), d(n) {
      int t = 0;
      head[root] = 0, dfs1(root, -1), dfs2(root, -1, t);
   }
   void dfs1(int x, int p) {
      par[x] = p, sub[x] = 1;
      if(g[x].size() and g[x][0] == p) swap(g[x][0], g[x].back());
      for(auto& e : g[x]) {  // 変更する!
         if(e == p) continue;
         d[e] = d[x] + 1;
         dfs1(e, x);
         sub[x] += sub[e];
         if(sub[g[x][0]] < sub[e]) swap(g[x][0], e);
      }
   }
   void dfs2(int x, int p, int& t) {
      in[x] = t++, rev[in[x]] = x;
      fec(e : g[x]) {
         if(e == p) continue;
         head[e] = g[x][0] == e ? head[x] : e;
         dfs2(e, x, t);
      }
      out[x] = t;
   }
   int la(int v, int k) {
      while(1) {
         int u = head[v];
         if(in[v] - k >= in[u]) return rev[in[v] - k];
         k -= in[v] - in[u] + 1, v = par[u];
      }
   }
   int lca(int u, int v) {
      for(;; v = par[head[v]]) {
         if(in[u] > in[v]) swap(u, v);
         if(head[u] == head[v]) return u;
      }
   }
   // パス・部分木クエリのどちらも、v のデータを in[v] に載せておく
   // 部分木クエリは [in[v] + edge, out[v]) でよい
   // q(l, r, rev): [l, r) の総積、rev なら逆向き
   template<class S> S path_query(int u, int v, auto op, auto e, auto q, bool edge = false) {
      S l = e, r = e;    // 可換なら x = e でよい
      bool rev = false;  // 可換なら不要
      for(;; v = par[head[v]]) {
         if(in[u] > in[v]) swap(u, v), rev ^= 1;
         if(head[u] == head[v]) break;
         S m = q(in[head[v]], in[v] + 1, rev);
         rev ? l = op(l, m) : r = op(m, r);
      }
      return op(l, op(q(in[u] + edge, in[v] + 1, rev), r));
   }
   int dist(int u, int v) { return d[u] + d[v] - 2 * d[lca(u, v)]; }
   int jump(int s, int t, int i) {
      if(!i) return s;
      int l = lca(s, t);
      int dst = d[s] + d[t] - d[l] * 2;
      if(dst < i) return -1;
      if(d[s] - d[l] >= i) return la(s, i);
      i -= d[s] - d[l];
      return la(t, d[t] - d[l] - i);
   }
   // es: 圧縮後の木の辺 (親 → 子、0 が根)
   // itov: 圧縮後の木の各頂点がもとの木でどの頂点か (行きがけ順に格納)
   // 使う際は空グラフとなる場合に注意
   pair<vc<pair<int, int>>, vc<int>> aux_tree(vc<int> xs) {
      if(xs.empty()) return {{}, {}};
      ranges::sort(xs, {}, [&](int v) { return in.at(v); });
      int k = xs.size();
      rep(i, k - 1) xs.eb(lca(xs[i], xs[i + 1]));
      ranges::sort(xs, {}, [&](int v) { return in.at(v); });
      xs.erase(unique(ALL(xs)), end(xs));
      vc<pair<int, int>> es;
      stack<int> sta;
      rep(i, xs.size()) {
         while(!sta.empty()) {
            int j = sta.top(), u = xs[i], v = xs[j];
            if(in[u] >= in[v] && out[u] <= out[v]) {
               es.eb(j, i);
               break;
            }
            sta.pop();
         }
         sta.emplace(i);
      }
      return {es, xs};
   }
};

int main() {
   cin.tie(0)->sync_with_stdio(0);

   int N;
   cin >> N;
   vl C(N);
   rep(i, N) cin >> C.at(i), C.at(i)--;
   vc<vc<int>> ctovs(N);
   rep(i, N) ctovs.at(C.at(i)).eb(i);

   vc<vl> G(N);
   rep(_, N - 1) {
      ll u, v;
      cin >> u >> v;
      u--, v--;
      G.at(u).eb(v);
      G.at(v).eb(u);
   }

   HLD hld(G);
   vl ans(N, INF);
   rep(c, N) {
      auto [es, itov] = hld.aux_tree(ctovs.at(c));
      ll k = itov.size();
      if(k == 0) continue;
      vc<vc<pll>> H(k);
      fec([ i, j ] : es) {
         ll d = hld.dist(itov.at(i), itov.at(j));
         H.at(i).eb(j, d), H.at(j).eb(i, d);
      }
      vl dists(k, INF), id(k, -1);
      priority_queue<pll, vc<pll>, greater<pll>> pq;
      rep(i, k) if(C.at(itov.at(i)) == c) { pq.push({0, i}), dists.at(i) = 0, id.at(i) = i; }
      while(!pq.empty()) {
         auto [d, i] = pq.top();
         pq.pop();
         if(dists.at(i) != d) continue;
         fec([ ni, c ] : H.at(i)) {
            if(chmin(dists.at(ni), d + c)) {
               id.at(ni) = id.at(i);
               pq.push({d + c, ni});
            }
         }
      }
      rep(i, k) fec([ j, d ] : H.at(i)) {
         if(id.at(i) != id.at(j)) {
            chmin(ans.at(itov.at(id.at(i))), dists.at(i) + d + dists.at(j));
            chmin(ans.at(itov.at(id.at(j))), dists.at(i) + d + dists.at(j));
         }
      }

      local(

          cout << "color=" << c + 1 << endl;

          fec([ i, j ] : es) {
             ll u = itov.at(i), v = itov.at(j);
             cout << i << " " << j << "  " << u + 1 << " " << v + 1 << "  " << hld.dist(u, v) << endl;
          }

          printvec(itov);
          printvec(dists); printvec(id);

      );
   }
   fec(ansi : ans) cout << ansi << "\n";
}
accuracy: 1
time_ms: 150
memory_kb: 35600
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ld=long double;
#define fi first
#define se second
#define i128 __int128_t
#define all(x) x.begin(),x.end()
#define rep(i,n) for(int i=0;i<(int)(n);++i)
#define si(a) (long)(a.size())
template<typename T>bool chmin(T&a,T b){return b<a?(a=b,1):0;}
template<typename T>bool chmax(T&a,T b){return b>a?(a=b,1):0;}
const int INF=1e9+100;
const long INFL=3e18+100;
struct _ {
   _() { cin.tie(0)->sync_with_stdio(0), cout.tie(0); }
} __;

struct LCA {
    int N;
    vector<vector<int>> G, P;
    vector<int> dep;
    LCA(vector<vector<int>> G_, int root = 0) : N(si(G_)), G(G_), dep(si(G_)) {
        int lg = 0;
        while ((1 << lg) + 1 < N) lg++;
        P.assign(lg + 1, vector<int>(N));

        dfs(root, -1, 0);
        for (int k = 1; k < si(P); k++) {
            rep(i, N) {
                if (P[k - 1][i] == -1)
                    P[k][i] = -1;
                else
                    P[k][i] = P[k - 1][P[k - 1][i]];
            }
        }
    }
    void dfs(int u, int p, int d) {
        dep[u] = d;
        P[0][u] = p;
        for (int v : G[u])
            if (v != p) dfs(v, u, d + 1);
    }
    int lca(int u, int v) {
        if (dep[u] > dep[v]) swap(u, v);
        rep(k, si(P)) if ((dep[v] - dep[u]) >> k & 1) v = P[k][v];
        if (u == v) return u;
        for (int k = si(P); k--;)
            if (P[k][u] != P[k][v]) {
                u = P[k][u];
                v = P[k][v];
            }
        return P[0][u];
    }
    int dist(int u, int v) {
        int w = lca(u, v);
        return dep[u] + dep[v] - 2 * dep[w];
    }
};
struct AuxiliaryTree : LCA {
    using super = LCA;

    vector<int> idx;
    vector<vector<int>> T;
    AuxiliaryTree(vector<vector<int>> G_, int root = 0)
        : super(G_, root), idx(si(G_)), T(si(G_)) {
        int pos = 0;
        dfs(root, -1, pos);
    }

    void dfs(int v, int p, int &pos) {
        idx[v] = pos++;
        for (int u : G[v])
            if (u != p) dfs(u, v, pos);
    }

    void add_aux_edge(int u, int v) {
        T[u].emplace_back(v);
        T[v].emplace_back(u);
    }

    using super::lca, super::dep;
    void query(vector<int> &vs) {
        assert(!vs.empty());
        sort(all(vs), [&](int a, int b) { return idx[a] < idx[b]; });
        vs.erase(unique(all(vs)), vs.end());

        int k = si(vs);
        stack<int> st;
        st.emplace(vs[0]);
        rep(i, k - 1) {
            int w = lca(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() and dep[w] < dep[st.top()]) {
                    add_aux_edge(st.top(), l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() or st.top() != w) {
                    st.emplace(w);
                    vs.emplace_back(w);
                }
                add_aux_edge(w, l);
            }
            st.emplace(vs[i + 1]);
        }

        while (si(st) > 1) {
            int c = st.top();
            st.pop();
            add_aux_edge(st.top(), c);
        }
    }

    void clear(const vector<int> &ws) {
        for (int w : ws) T[w].clear();
    }
};
template <class T, class S>
bool top2id_chmin(pair<pair<T,S>,pair<T,S>> &p, T v, S k){
    if(p.first.second==k){
        if(p.first.first>v){
            p.first.first=v;
            return 1;
        }
    }
    else if(p.second.second==k){
        if(p.second.first>v){
            p.second.first=v;
            if(p.first.first>p.second.first) swap(p.first,p.second);
            return 1;
        }
    }
    else{
        if(p.first.first>v){
            p.second=p.first;
            p.first=make_pair(v,k);
            return 1;
        }
        if(p.second.first>v){
            p.second=make_pair(v,k);
            if(p.first.first>p.second.first) swap(p.first,p.second);
            return 1;
        }
    }
    return 0;
}

int main(){
    int N;
    cin>>N;
    vector<int> C(N);
    rep(i,N)cin>>C[i],C[i]--;
    vector<vector<int>> G(N);
    rep(i,N-1){
        int s,t;
        cin>>s>>t;
        s--;t--;
        G[s].push_back(t);
        G[t].push_back(s);
    }
    AuxiliaryTree aux(G);
    
    vector<vector<int>> A(N,{0});
    rep(i,N) A[C[i]].push_back(i);

    pair<pair<int,int>,pair<int,int>> mie=make_pair(make_pair(1e9,-2),make_pair(1e9,-3));
    vector<pair<pair<int,int>,pair<int,int>>> mi(N,mie);
    vector<int> ans(N,1e9);
    rep(i,N){
        if(A[i].empty()) continue;
        aux.query(A[i]);

        auto dfs=[&](auto self, int v, int p)->void {
            if(C[v]==i) top2id_chmin(mi[v], 0, -1);
            
            for(int e: aux.T[v]){
                if(e==p) continue;
                self(self, e, v);
                top2id_chmin(mi[v], mi[e].fi.fi+aux.dist(e, v),e);
            }
        };
        dfs(dfs, 0, -1);

        auto dfs2=[&](auto self, int v, int p, int tk)->void {
            if(C[v]==i){
                chmin(ans[v], mi[v].se.fi);
                chmin(ans[v], tk);
                tk=0;
            }
            for(int e: aux.T[v]){
                if(e==p) continue;
                
                if(C[v]==i)self(self, e, v, tk+aux.dist(e,v));
                else{
                    int nk=tk;
                    if(mi[v].fi.se!=e) chmin(nk, mi[v].fi.fi);
                    if(mi[v].se.se!=e) chmin(nk, mi[v].se.fi);
                    self(self, e, v, nk+aux.dist(e,v));
                }

            }
        };
        dfs2(dfs2, 0, -1, 1e9);
        for(auto e: A[i]) mi[e]=mie;
        aux.clear(A[i]);
    }

    rep(i,N){
        cout<<ans[i]<<"\n";
    }
}
/*

*/
accuracy: 1
time_ms: 300
memory_kb: 70936
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

template <class T, T (*op)(T, T), T (*e)()>
struct SegmentTree {
    int n, len, height = 0;
    vector<T> data;
    constexpr void update(int k) noexcept { data[k] = op(data[k << 1 | 0], data[k << 1 | 1]); }

   public:
    SegmentTree() = default;
    SegmentTree(int size) : SegmentTree(vector<T>(size, e())) {}
    SegmentTree(const vector<T> &v) : n(int(v.size())) {
        len = 1, height = 0;
        while (len < n) len <<= 1, height++;
        data.assign(len << 1, e());
        for (int i = 0; i < n; i++) data[i + len] = v[i];
        for (int i = len - 1; i > 0; i--) update(i);
    }
    void set(int i, T x) {
        i += len;
        data[i] = x;
        while (i >>= 1) update(i);
    }
    T get(int i) const {
        i += len;
        return data[i];
    }
    T operator[](int i) const { return get(i); }
    T prod(int l, int r) const {
        l += len, r += len;
        T resl = e(), resr = e();
        while (l < r) {
            if (l & 1) resl = op(resl, data[l++]);
            if (r & 1) resr = op(data[--r], resr);
            l >>= 1, r >>= 1;
        }
        return op(resl, resr);
    }
    T all_prod() const { return data[1]; }
    // check(A[l] * ... * A[r - 1]) = true となる最大の r
    template <class C>
    int max_right(int l, const C &check) const {
        assert(0 <= l && l <= n && check(e()));
        if (l == n) return n;
        l += len;
        T sum = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!check(op(sum, data[l]))) {
                while (l < len) {
                    l = l << 1 | 0;
                    if (check(op(sum, data[l]))) {
                        sum = op(sum, data[l++]);
                    }
                }
                return l - len;
            }
            sum = op(sum, data[l++]);
        } while ((l & -l) != l);
        return n;
    }
    // check(A[l] * ... * A[r - 1]) = true となる最小の l
    template <class C>
    int min_left(int r, const C &check) const {
        assert(0 <= r && r <= n && check(e()));
        if (r == 0) return 0;
        r += len;
        T sum = e();
        do {
            --r;
            while (r > 1 && r % 2) r >>= 1;
            if (!check(op(data[r], sum))) {
                while (r < len) {
                    r = r << 1 | 1;
                    if (check(op(data[r], sum))) {
                        sum = op(data[r--], sum);
                    }
                }
                return r + 1 - len;
            }
            sum = op(data[r], sum);
        } while ((r & -r) != r);
        return 0;
    }
};

struct EulerTour {
    int n;
    vector<int> in, out, pos, depth;
    using S = pair<int, int>;
    static S op(S a, S b) { return min(a, b); }
    static S e() { return {numeric_limits<int>::max(), -1}; }
    SegmentTree<S, op, e> seg;
    void dfs(const vector<vector<int>> &G, int r) {
        int t = 0;
        auto f = [&](auto &&self, int v, int p) -> void {
            in[v] = t, pos[t] = v, t++;
            for (int u : G[v]) {
                if (u == p) continue;
                depth[u] = depth[v] + 1;
                self(self, u, v);
                pos[t] = v, t++;
            }
            out[v] = t;
        };
        f(f, r, -1);
    }

    EulerTour(const vector<vector<int>> &G, int r) : n(ssize(G)), in(n), out(n), pos(2 * n - 1), depth(n), seg(2 * n - 1) {
        dfs(G, r);
        vector<S> init(2 * n - 1);
        for (int i = 0; i < 2 * n - 1; i++) init[i] = {depth[pos[i]], pos[i]};
        seg = SegmentTree<S, op, e>(init);
    }
    int lca(int u, int v) const { return seg.prod(min(in[u], in[v]), max(out[u], out[v])).second; }
    int dist(int u, int v) const { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
};

struct AuxiliaryTree {
    EulerTour et;
    AuxiliaryTree(const vector<vector<int>> &G, int r) : et(G, r) {}
    // 頂点集合 vs とそれらの LCA を含む Auxiliary Tree を構築
    // 返り値: aux tree, aux tree と 元のグラフの対応表 id
    // aux tree の頂点 i は元のグラフの頂点 id[i] に対応
    pair<vector<vector<pair<int, int>>>, vector<int>> get(vector<int> vs) {
        int K = ssize(vs);
        sort(vs.begin(), vs.end(), [&](int a, int b) { return et.in[a] < et.in[b]; });
        for (int i = 0; i < K - 1; i++) {
            vs.emplace_back(et.lca(vs[i], vs[i + 1]));
        }
        sort(vs.begin(), vs.end(), [&](int a, int b) { return et.in[a] < et.in[b]; });
        vs.erase(unique(vs.begin(), vs.end()), vs.end());
        K = ssize(vs);
        stack<int> stk;
        vector<vector<pair<int, int>>> auxt(K);
        for (int i = 0; i < K; i++) {
            while (!stk.empty() && et.out[vs[stk.top()]] < et.in[vs[i]]) stk.pop();
            if (!stk.empty()) {
                int d = et.dist(vs[i], vs[stk.top()]);
                auxt[stk.top()].emplace_back(i, d);
                auxt[i].emplace_back(stk.top(), d);
            }
            stk.emplace(i);
        }
        return {auxt, vs};
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N;
    cin >> N;
    vector<int> C(N), ans(N, N);
    for (int i = 0; i < N; i++) cin >> C[i], C[i]--;
    vector<vector<int>> G(N);
    for (int i = 0; i < N - 1; i++) {
        int s, t;
        cin >> s >> t, s--, t--;
        G[s].emplace_back(t);
        G[t].emplace_back(s);
        if (C[s] == C[t]) ans[s] = ans[t] = 1;
    }

    // 色 i の頂点集合
    vector<vector<int>> groups(N);
    for (int i = 0; i < N; i++) groups[C[i]].emplace_back(i);

    AuxiliaryTree aux(G, 0);
    // 色 c 毎に解く
    vector<int> dp(N, N);
    for (int c = 0; c < N; c++) {
        auto [T, id] = aux.get(groups[c]);
        int M = ssize(T);
        // 色 c の Auxiliary Tree 上で木 dp
        // O(M^2) 時間だけど枝刈りが上手くいってる
        for (int i = 0; i < M; i++) {
            if (C[id[i]] != c || ans[id[i]] == 1) continue;
            auto dfs = [&](auto &&self, int v, int p, int d) -> void {
                for (auto [u, w] : T[v]) {
                    if (u == p) continue;
                    if (C[id[u]] != c) {
                        // u が色 c の頂点でない場合: 次の頂点に移動
                        self(self, u, v, d + w);
                    } else {
                        // u が色 c の頂点の場合: dp を更新
                        dp[i] = min(dp[i], d + w);
                        dp[u] = min(dp[u], d + w);
                    }
                }
            };
            dfs(dfs, i, -1, 0);
        }
        for (int i = 0; i < M; i++) {
            if (C[id[i]] == c) ans[id[i]] = min(ans[id[i]], dp[i]);
            dp[i] = N;
        }
    }

    for (int i = 0; i < N; i++) cout << ans[i] << '\n';
}
accuracy: 1
time_ms: 1340
memory_kb: 50244
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

template <class T, T (*op)(T, T), T (*e)()>
struct SegmentTree {
    int n, len, height = 0;
    vector<T> data;
    constexpr void update(int k) noexcept { data[k] = op(data[k << 1 | 0], data[k << 1 | 1]); }

   public:
    SegmentTree() = default;
    SegmentTree(int size) : SegmentTree(vector<T>(size, e())) {}
    SegmentTree(const vector<T> &v) : n(int(v.size())) {
        len = 1, height = 0;
        while (len < n) len <<= 1, height++;
        data.assign(len << 1, e());
        for (int i = 0; i < n; i++) data[i + len] = v[i];
        for (int i = len - 1; i > 0; i--) update(i);
    }
    void set(int i, T x) {
        i += len;
        data[i] = x;
        while (i >>= 1) update(i);
    }
    T get(int i) const {
        i += len;
        return data[i];
    }
    T operator[](int i) const { return get(i); }
    T prod(int l, int r) const {
        l += len, r += len;
        T resl = e(), resr = e();
        while (l < r) {
            if (l & 1) resl = op(resl, data[l++]);
            if (r & 1) resr = op(data[--r], resr);
            l >>= 1, r >>= 1;
        }
        return op(resl, resr);
    }
    T all_prod() const { return data[1]; }
    // check(A[l] * ... * A[r - 1]) = true となる最大の r
    template <class C>
    int max_right(int l, const C &check) const {
        assert(0 <= l && l <= n && check(e()));
        if (l == n) return n;
        l += len;
        T sum = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!check(op(sum, data[l]))) {
                while (l < len) {
                    l = l << 1 | 0;
                    if (check(op(sum, data[l]))) {
                        sum = op(sum, data[l++]);
                    }
                }
                return l - len;
            }
            sum = op(sum, data[l++]);
        } while ((l & -l) != l);
        return n;
    }
    // check(A[l] * ... * A[r - 1]) = true となる最小の l
    template <class C>
    int min_left(int r, const C &check) const {
        assert(0 <= r && r <= n && check(e()));
        if (r == 0) return 0;
        r += len;
        T sum = e();
        do {
            --r;
            while (r > 1 && r % 2) r >>= 1;
            if (!check(op(data[r], sum))) {
                while (r < len) {
                    r = r << 1 | 1;
                    if (check(op(data[r], sum))) {
                        sum = op(data[r--], sum);
                    }
                }
                return r + 1 - len;
            }
            sum = op(data[r], sum);
        } while ((r & -r) != r);
        return 0;
    }
};

struct EulerTour {
    int n;
    vector<int> in, out, pos, depth;
    using S = pair<int, int>;
    static S op(S a, S b) { return min(a, b); }
    static S e() { return {numeric_limits<int>::max(), -1}; }
    SegmentTree<S, op, e> seg;
    void dfs(const vector<vector<int>> &G, int r) {
        int t = 0;
        auto f = [&](auto &&self, int v, int p) -> void {
            in[v] = t, pos[t] = v, t++;
            for (int u : G[v]) {
                if (u == p) continue;
                depth[u] = depth[v] + 1;
                self(self, u, v);
                pos[t] = v, t++;
            }
            out[v] = t;
        };
        f(f, r, -1);
    }

    EulerTour(const vector<vector<int>> &G, int r) : n(ssize(G)), in(n), out(n), pos(2 * n - 1), depth(n), seg(2 * n - 1) {
        dfs(G, r);
        vector<S> init(2 * n - 1);
        for (int i = 0; i < 2 * n - 1; i++) init[i] = {depth[pos[i]], pos[i]};
        seg = SegmentTree<S, op, e>(init);
    }
    int lca(int u, int v) const { return seg.prod(min(in[u], in[v]), max(out[u], out[v])).second; }
    int dist(int u, int v) const { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
};

struct AuxiliaryTree {
    EulerTour et;
    AuxiliaryTree(const vector<vector<int>> &G, int r) : et(G, r) {}
    // 頂点集合 vs とそれらの LCA を含む Auxiliary Tree を構築
    // 返り値: aux tree, aux tree と 元のグラフの対応表 id
    // aux tree の頂点 i は元のグラフの頂点 id[i] に対応
    pair<vector<vector<pair<int, int>>>, vector<int>> get(vector<int> vs) {
        int K = ssize(vs);
        sort(vs.begin(), vs.end(), [&](int a, int b) { return et.in[a] < et.in[b]; });
        for (int i = 0; i < K - 1; i++) {
            vs.emplace_back(et.lca(vs[i], vs[i + 1]));
        }
        sort(vs.begin(), vs.end(), [&](int a, int b) { return et.in[a] < et.in[b]; });
        vs.erase(unique(vs.begin(), vs.end()), vs.end());
        K = ssize(vs);
        stack<int> stk;
        vector<vector<pair<int, int>>> auxt(K);
        for (int i = 0; i < K; i++) {
            while (!stk.empty() && et.out[vs[stk.top()]] < et.in[vs[i]]) stk.pop();
            if (!stk.empty()) {
                int d = et.dist(vs[i], vs[stk.top()]);
                auxt[stk.top()].emplace_back(i, d);
                auxt[i].emplace_back(stk.top(), d);
            }
            stk.emplace(i);
        }
        return {auxt, vs};
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N;
    cin >> N;
    vector<int> C(N), ans(N, N);
    for (int i = 0; i < N; i++) cin >> C[i], C[i]--;
    vector<vector<int>> G(N);
    for (int i = 0; i < N - 1; i++) {
        int s, t;
        cin >> s >> t, s--, t--;
        G[s].emplace_back(t);
        G[t].emplace_back(s);
        if (C[s] == C[t]) ans[s] = ans[t] = 1;
    }

    // 色 i の頂点集合
    vector<vector<int>> groups(N);
    for (int i = 0; i < N; i++) groups[C[i]].emplace_back(i);

    AuxiliaryTree aux(G, 0);
    // 色 c 毎に解く
    for (int c = 0; c < N; c++) {
        auto [T, id] = aux.get(groups[c]);
        int M = ssize(T);
        // 色 c の Auxiliary Tree 上で木 dp
        // O(M^2) 時間だけど枝刈りが上手くいってる
        vector<int> dp(M, N);
        for (int i = 0; i < M; i++) {
            if (C[id[i]] != c || ans[id[i]] == 1) continue;
            auto dfs = [&](auto &&self, int v, int p, int d) -> void {
                for (auto [u, w] : T[v]) {
                    if (u == p) continue;
                    if (C[id[u]] != c) {
                        // u が色 c の頂点でない場合: 次の頂点に移動
                        self(self, u, v, d + w);
                    } else {
                        // u が色 c の頂点の場合: dp を更新
                        dp[i] = min(dp[i], d + w);
                        dp[u] = min(dp[u], d + w);
                    }
                }
            };
            dfs(dfs, i, -1, 0);
        }
        for (int i = 0; i < M; i++) {
            if (C[id[i]] != c) continue;
            ans[id[i]] = min(ans[id[i]], dp[i]);
        }
    }

    for (int i = 0; i < N; i++) {
        cout << ans[i] << '\n';
    }
}
accuracy: 1
time_ms: 1340
memory_kb: 50236
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

template <class T, T (*op)(T, T), T (*e)()>
struct SegmentTree {
    int n, len, height = 0;
    vector<T> data;
    constexpr void update(int k) noexcept { data[k] = op(data[k << 1 | 0], data[k << 1 | 1]); }

   public:
    SegmentTree() = default;
    SegmentTree(int size) : SegmentTree(vector<T>(size, e())) {}
    SegmentTree(const vector<T> &v) : n(int(v.size())) {
        len = 1, height = 0;
        while (len < n) len <<= 1, height++;
        data.assign(len << 1, e());
        for (int i = 0; i < n; i++) data[i + len] = v[i];
        for (int i = len - 1; i > 0; i--) update(i);
    }
    void set(int i, T x) {
        i += len;
        data[i] = x;
        while (i >>= 1) update(i);
    }
    T get(int i) const {
        i += len;
        return data[i];
    }
    T operator[](int i) const { return get(i); }
    T prod(int l, int r) const {
        l += len, r += len;
        T resl = e(), resr = e();
        while (l < r) {
            if (l & 1) resl = op(resl, data[l++]);
            if (r & 1) resr = op(data[--r], resr);
            l >>= 1, r >>= 1;
        }
        return op(resl, resr);
    }
    T all_prod() const { return data[1]; }
    // check(A[l] * ... * A[r - 1]) = true となる最大の r
    template <class C>
    int max_right(int l, const C &check) const {
        assert(0 <= l && l <= n && check(e()));
        if (l == n) return n;
        l += len;
        T sum = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!check(op(sum, data[l]))) {
                while (l < len) {
                    l = l << 1 | 0;
                    if (check(op(sum, data[l]))) {
                        sum = op(sum, data[l++]);
                    }
                }
                return l - len;
            }
            sum = op(sum, data[l++]);
        } while ((l & -l) != l);
        return n;
    }
    // check(A[l] * ... * A[r - 1]) = true となる最小の l
    template <class C>
    int min_left(int r, const C &check) const {
        assert(0 <= r && r <= n && check(e()));
        if (r == 0) return 0;
        r += len;
        T sum = e();
        do {
            --r;
            while (r > 1 && r % 2) r >>= 1;
            if (!check(op(data[r], sum))) {
                while (r < len) {
                    r = r << 1 | 1;
                    if (check(op(data[r], sum))) {
                        sum = op(data[r--], sum);
                    }
                }
                return r + 1 - len;
            }
            sum = op(data[r], sum);
        } while ((r & -r) != r);
        return 0;
    }
};

struct EulerTour {
    int n;
    vector<int> in, out, pos, depth;
    using S = pair<int, int>;
    static S op(S a, S b) { return min(a, b); }
    static S e() { return {numeric_limits<int>::max(), -1}; }
    SegmentTree<S, op, e> seg;
    void dfs(const vector<vector<int>> &G, int r) {
        int t = 0;
        auto f = [&](auto &&self, int v, int p) -> void {
            in[v] = t, pos[t] = v, t++;
            for (int u : G[v]) {
                if (u == p) continue;
                depth[u] = depth[v] + 1;
                self(self, u, v);
                pos[t] = v, t++;
            }
            out[v] = t;
        };
        f(f, r, -1);
    }

    EulerTour(const vector<vector<int>> &G, int r) : n(ssize(G)), in(n), out(n), pos(2 * n - 1), depth(n), seg(2 * n - 1) {
        dfs(G, r);
        vector<S> init(2 * n - 1);
        for (int i = 0; i < 2 * n - 1; i++) init[i] = {depth[pos[i]], pos[i]};
        seg = SegmentTree<S, op, e>(init);
    }
    int lca(int u, int v) const { return seg.prod(min(in[u], in[v]), max(out[u], out[v])).second; }
    int dist(int u, int v) const { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
};

struct AuxiliaryTree {
    EulerTour et;
    AuxiliaryTree(const vector<vector<int>> &G, int r) : et(G, r) {}
    // 頂点集合 vs とそれらの LCA を含む Auxiliary Tree を構築
    // 返り値: aux tree, aux tree と 元のグラフの対応表 id
    // aux tree の頂点 i は元のグラフの頂点 id[i] に対応
    pair<vector<vector<pair<int, int>>>, vector<int>> get(vector<int> vs) {
        int K = ssize(vs);
        sort(vs.begin(), vs.end(), [&](int a, int b) { return et.in[a] < et.in[b]; });
        for (int i = 0; i < K - 1; i++) {
            vs.emplace_back(et.lca(vs[i], vs[i + 1]));
        }
        sort(vs.begin(), vs.end(), [&](int a, int b) { return et.in[a] < et.in[b]; });
        vs.erase(unique(vs.begin(), vs.end()), vs.end());
        K = ssize(vs);
        stack<int> stk;
        vector<vector<pair<int, int>>> auxt(K);
        for (int i = 0; i < K; i++) {
            while (!stk.empty() && et.out[vs[stk.top()]] < et.in[vs[i]]) stk.pop();
            if (!stk.empty()) {
                int d = et.dist(vs[i], vs[stk.top()]);
                auxt[stk.top()].emplace_back(i, d);
                auxt[i].emplace_back(stk.top(), d);
            }
            stk.emplace(i);
        }
        return {auxt, vs};
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N;
    cin >> N;
    vector<int> C(N), ans(N, N);
    for (int i = 0; i < N; i++) cin >> C[i], C[i]--;
    vector<vector<int>> G(N);
    for (int i = 0; i < N - 1; i++) {
        int s, t;
        cin >> s >> t, s--, t--;
        G[s].emplace_back(t);
        G[t].emplace_back(s);
        if (C[s] == C[t]) ans[s] = ans[t] = 1;
    }

    // 色 i の頂点集合
    vector<vector<int>> groups(N);
    for (int i = 0; i < N; i++) groups[C[i]].emplace_back(i);

    AuxiliaryTree aux(G, 0);
    // 色 c 毎に解く
    for (int c = 0; c < N; c++) {
        auto [T, id] = aux.get(groups[c]);
        int M = ssize(T);
        // 色 c の Auxiliary Tree 上で木 dp
        vector<int> dp(M, N);
        for (int i = 0; i < M; i++) {
            if (C[id[i]] != c || ans[id[i]] == 1) continue;
            auto dfs = [&](auto &&self, int v, int p, int d) -> void {
                for (auto [u, w] : T[v]) {
                    if (u == p) continue;
                    if (C[id[u]] != c) {
                        // u が色 c の頂点でない場合: 次の頂点に移動
                        self(self, u, v, d + w);
                    } else {
                        // u が色 c の頂点の場合: dp を更新
                        dp[i] = min(dp[i], d + w);
                        dp[u] = min(dp[u], d + w);
                    }
                }
            };
            dfs(dfs, i, -1, 0);
        }
        for (int i = 0; i < M; i++) {
            if (C[id[i]] != c) continue;
            ans[id[i]] = min(ans[id[i]], dp[i]);
        }
    }

    for (int i = 0; i < N; i++) {
        cout << ans[i] << '\n';
    }
}
accuracy: 1
time_ms: 1320
memory_kb: 50244
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;

#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif

template <class T, class F>
struct SegmentTree {
    int n, len;
    vector<T> data;
    const F op;
    const T e;

    SegmentTree() = default;
    SegmentTree(int size, const F& operation, const T& identify) : n(size), op(operation), e(identify) { init(); }
    SegmentTree(const vector<T>& A, const F& operation, const T& identify) : n(int(A.size())), op(operation), e(identify) { init(), build(A); }
    void init() {
        len = 1;
        while (len < n) len <<= 1;
        data.assign(len << 1, e);
    }
    void build(const vector<T>& A) {
        for (int i = 0; i < n; i++) data[i + len] = A[i];
        for (int i = len - 1; i > 0; i--) data[i] = op(data[i << 1 | 0], data[i << 1 | 1]);
    }
    void update(int i, T x) {
        i += len, data[i] = x;
        while (i > 1) {
            i >>= 1;
            data[i] = op(data[i << 1 | 0], data[i << 1 | 1]);
        }
    }
    void add(int i, T x) {
        i += len, data[i] += x;
        while (i > 1) {
            i >>= 1;
            data[i] = op(data[i << 1 | 0], data[i << 1 | 1]);
        }
    }
    T prod(int l, int r) const {
        l += len, r += len;
        T resl = e, resr = e;
        while (l < r) {
            if (l & 1) resl = op(resl, data[l++]);
            if (r & 1) resr = op(data[--r], resr);
            l >>= 1, r >>= 1;
        }
        return op(resl, resr);
    }
    T all_prod() const { return data[1]; }
    T operator[](int i) const { return data[i + len]; }

    // check(A[l] * ... * A[r - 1]) = true となる最大の r
    template <class C>
    int max_right(int l, const C& check) const {
        assert(0 <= l && l <= n && check(e));
        if (l == n) return n;
        l += len;
        T sum = e;
        do {
            while (l % 2 == 0) l >>= 1;
            if (!check(op(sum, data[l]))) {
                while (l < len) {
                    l = l << 1 | 0;
                    if (check(op(sum, data[l]))) {
                        sum = op(sum, data[l++]);
                    }
                }
                return l - len;
            }
            sum = op(sum, data[l++]);
        } while ((l & -l) != l);
        return n;
    }

    // check(A[l] * ... * A[r - 1]) = true となる最小の l
    template <class C>
    int min_left(int r, const C& check) const {
        assert(0 <= r && r <= n && check(e));
        if (r == 0) return 0;
        r += len;
        T sum = e;
        do {
            --r;
            while (r > 1 && r % 2) r >>= 1;
            if (!check(op(data[r], sum))) {
                while (r < len) {
                    r = r << 1 | 1;
                    if (check(op(data[r], sum))) {
                        sum = op(data[r--], sum);
                    }
                }
                return r + 1 - len;
            }
            sum = op(data[r], sum);
        } while ((r & -r) != r);
        return 0;
    }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    int N;
    cin >> N;
    vector<int> C(N);
    for (int i = 0; i < N; i++) cin >> C[i], C[i]--;
    vector<vector<int>> G(N);
    for (int i = 0; i < N - 1; i++) {
        int s, t;
        cin >> s >> t, s--, t--;
        G[s].emplace_back(t);
        G[t].emplace_back(s);
    }

    // 色 i の頂点集合
    vector<vector<int>> groups(N);
    for (int i = 0; i < N; i++) groups[C[i]].emplace_back(i);

    // 区間最小値 Segment Tree
    using S = pair<int, int>;
    constexpr S e = {numeric_limits<int>::max(), -1};
    auto op = [](S a, S b) { return min(a, b); };
    // Euler Tour
    int t = 0;
    vector<int> in(N), out(N), depth(N), pos(2 * N - 1);
    auto et = [&](auto&& self, int v, int p) -> void {
        in[v] = t, pos[t] = v, t++;
        for (int u : G[v]) {
            if (u == p) continue;
            depth[u] = depth[v] + 1;
            self(self, u, v);
            pos[t] = v, t++;
        }
        out[v] = t;
    };
    et(et, 0, -1);
    vector<S> init(2 * N - 1);
    for (int i = 0; i < 2 * N - 1; i++) init[i] = {depth[pos[i]], pos[i]};
    SegmentTree seg(init, op, e);
    auto lca = [&](int a, int b) -> int { return seg.prod(min(in[a], in[b]), max(out[a], out[b])).second; };
    auto dist = [&](int a, int b) -> int { return depth[a] + depth[b] - 2 * depth[lca(a, b)]; };

    vector<int> ans(N, N);
    // 色 c 毎に解く
    for (int c = 0; c < N; c++) {
        int M = ssize(groups[c]);
        sort(groups[c].begin(), groups[c].end(), [&](int a, int b) { return in[a] < in[b]; });
        for (int i = 0; i < M - 1; i++) {
            int a = groups[c][i], b = groups[c][i + 1], l = lca(a, b);
            groups[c].emplace_back(l);
        }
        sort(groups[c].begin(), groups[c].end(), [&](int a, int b) { return in[a] < in[b]; });
        groups[c].erase(unique(groups[c].begin(), groups[c].end()), groups[c].end());
        M = ssize(groups[c]);
        // T := 色 c の Auxiliary Tree を構築
        vector<vector<pair<int, int>>> T(M);
        stack<int> stk;
        for (int i = 0; i < M; i++) {
            // stk の top が groups[c][i] の先祖になるまで pop
            while (!stk.empty() && out[groups[c][stk.top()]] < in[groups[c][i]]) stk.pop();
            if (!stk.empty()) {
                int d = dist(groups[c][i], groups[c][stk.top()]);
                T[stk.top()].emplace_back(i, d);
                T[i].emplace_back(stk.top(), d);
            }
            stk.emplace(i);
        }
        // Auxiliary Tree 上の木 dp を各頂点で行う
        vector<int> dp(M, N);
        for (int i = 0; i < M; i++) {
            if (C[groups[c][i]] != c) continue;
            auto dfs = [&](auto&& self, int v, int p, int d) -> void {
                for (auto [u, w] : T[v]) {
                    if (u == p) continue;
                    if (C[groups[c][u]] != c) {
                        // u が色 c の頂点でない場合: 次の頂点に移動
                        self(self, u, v, d + w);
                    } else {
                        // u が色 c の頂点の場合: dp を更新
                        dp[i] = min(dp[i], d + w);
                        dp[u] = min(dp[u], d + w);
                    }
                }
            };
            dfs(dfs, i, -1, 0);
        }
        for (int i = 0; i < M; i++) {
            if (C[groups[c][i]] != c) continue;
            ans[groups[c][i]] = min(ans[groups[c][i]], dp[i]);
        }
    }

    for (int i = 0; i < N; i++) {
        cout << ans[i] << '\n';
    }
}
accuracy: 1
time_ms: 1480
memory_kb: 43224
final_rank: 18

code_snippet: #include <iostream>
#include <iomanip>
#include <cassert>
#include <vector>
#include <algorithm>
#include <utility>
#include <numeric>
#include <tuple>
#include <ranges>
namespace ranges = std::ranges;
namespace views = std::views;
// #include "Src/Number/IntegerDivision.hpp"
// #include "Src/Utility/BinarySearch.hpp"
// #include "Src/Sequence/CompressedSequence.hpp"
// #include "Src/Sequence/RunLengthEncoding.hpp"
// #include "Src/Algebra/Group/AdditiveGroup.hpp"
// #include "Src/DataStructure/FenwickTree/FenwickTree.hpp"


#include <cstdint>
#include <cstddef>

namespace zawa {

using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;

using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

using usize = std::size_t;

} // namespace zawa


#include <concepts>

namespace zawa {

namespace concepts {

template <class T>
concept Semigroup = requires {
    typename T::Element;
    { T::operation(std::declval<typename T::Element>(), std::declval<typename T::Element>()) } -> std::same_as<typename T::Element>;
};

} // namespace concepts

} // namespace zawa


namespace zawa {

namespace concepts {

template <class T>
concept Identitiable = requires {
    typename T::Element;
    { T::identity() } -> std::same_as<typename T::Element>;
};

template <class T>
concept Monoid = Semigroup<T> and Identitiable<T>;

} // namespace

} // namespace zawa

#include <functional>
#include <type_traits>
#include <ostream>

namespace zawa {

template <concepts::Monoid Monoid>
class SegmentTree {
public:

    using VM = Monoid;

    using V = typename VM::Element;

    using OM = Monoid;

    using O = typename OM::Element;

    SegmentTree() = default;

    explicit SegmentTree(usize n) : m_n{ n }, m_dat(n << 1, VM::identity()) {}

    explicit SegmentTree(const std::vector<V>& dat) : m_n{ dat.size() }, m_dat(dat.size() << 1, VM::identity()) {
        for (usize i{} ; i < m_n ; i++) {
            m_dat[i + m_n] = dat[i];
        }
        for (usize i{m_n} ; i-- ; ) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    [[nodiscard]] inline usize size() const noexcept {
        return m_n;
    }

    [[nodiscard]] V get(usize i) const {
        assert(i < size());
        return m_dat[i + m_n];
    }

    [[nodiscard]] V operator[](usize i) const {
        assert(i < size());
        return m_dat[i + m_n];
    }

    void operation(usize i, const O& value) {
        assert(i < size());
        i += size();
        m_dat[i] = OM::operation(m_dat[i], value);
        while (i = parent(i), i) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    void assign(usize i, const V& value) {
        assert(i < size());
        i += size();
        m_dat[i] = value;
        while (i = parent(i), i) {
            m_dat[i] = VM::operation(m_dat[left(i)], m_dat[right(i)]);
        }
    }

    [[nodiscard]] V product(u32 l, u32 r) const {
        assert(l <= r and r <= size());
        V L{ VM::identity() }, R{ VM::identity() };
        for (l += size(), r += size() ; l < r ; l = parent(l), r = parent(r)) {
            if (l & 1) {
                L = VM::operation(L, m_dat[l++]);
            }
            if (r & 1) {
                R = VM::operation(m_dat[--r], R);
            }
        }
        return VM::operation(L, R);
    }

    template <class F>
    requires std::predicate<F, V>
    [[nodiscard]] usize maxRight(usize l, const F& f) {
        assert(l < size());
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(V)>>, "maxRight's argument f must be function bool(T)");
        assert(f(VM::identity()));
        usize res{l}, width{1};
        V prod{ VM::identity() };
        // 現在の見ている頂点の幅をwidthで持つ
        // 境界がある頂点を含む部分木の根を探す
        // (折り返す時は必要以上の幅を持つ根になるが、widthを持っているのでオーバーしない)
        for (l += size() ; res + width <= size() ; l = parent(l), width <<= 1) if (l & 1) {
            if (not f(VM::operation(prod, m_dat[l]))) break; 
            res += width;
            prod = VM::operation(prod, m_dat[l++]);
        }
        // 根から下って、境界を発見する
        while (l = left(l), width >>= 1) {
            if (res + width <= size() and f(VM::operation(prod, m_dat[l]))) {
                res += width;
                prod = VM::operation(prod, m_dat[l++]);
            } 
        }
        return res;
    }

    template <class F>
    requires std::predicate<F, V>
    [[nodiscard]] usize minLeft(usize r, const F& f) const {
        assert(r <= size());
        static_assert(std::is_convertible_v<decltype(f), std::function<bool(V)>>, "minLeft's argument f must be function bool(T)");
        assert(f(VM::identity()));
        usize res{r}, width{1};
        V prod{ VM::identity() };
        for (r += size() ; res >= width ; r = parent(r), width <<= 1) if (r & 1) {
            if (not f(VM::operation(m_dat[r - 1], prod))) break;
            res -= width;
            prod = VM::operation(prod, m_dat[--r]);
        }
        while (r = left(r), width >>= 1) {
            if (res >= width and f(VM::operation(m_dat[r - 1], prod))) {
                res -= width;
                prod = VM::operation(m_dat[--r], prod);
            }
        }
        return res;
    }

    friend std::ostream& operator<<(std::ostream& os, const SegmentTree& st) {
        for (usize i{1} ; i < 2 * st.size() ; i++) {
            os << st.m_dat[i] << (i + 1 == 2 * st.size() ? "" : " ");
        }
        return os;
    }

private:

    constexpr u32 left(u32 v) const {
        return v << 1;
    }

    constexpr u32 right(u32 v) const {
        return v << 1 | 1;
    }

    constexpr u32 parent(u32 v) const {
        return v >> 1;
    }

    usize m_n;

    std::vector<V> m_dat;
};

} // namespace zawa
// #include "Src/DataStructure/DisjointSetUnion/DisjointSetUnion.hpp"


#include <cmath>
#include <limits>

namespace zawa {

template <class V>
class HeavyLightDecomposition {
public:

    static constexpr V Invalid() noexcept {
        return INVALID;
    }

    HeavyLightDecomposition() = default;

    HeavyLightDecomposition(std::vector<std::vector<V>> T, V root = 0u) 
        : n_{T.size()}, par_(n_), top_(n_), idx_(n_), 
        inv_(n_), size_(n_, usize{1}), dep_(n_) {

            auto dfs1{[&](auto dfs, V v, V p, usize d) -> usize {
                par_[v] = p;
                dep_[v] = d;
                if (p != INVALID) {
                    for (u32 i{} ; i + 1 < T[v].size() ; i++) if (T[v][i] == p) {
                        std::swap(T[v][i], T[v].back());
                        break;
                    }
                    assert(T[v].back() == p);
                    T[v].pop_back();
                }
                for (V x : T[v]) {
                    size_[v] += dfs(dfs, x, v, d + 1);
                }
                for (u32 i{1} ; i < T[v].size() ; i++) if (size_[T[v][0]] < size_[T[v][i]]) {
                    std::swap(T[v][0], T[v][i]);
                }
                return size_[v];
            }};

            auto dfs2{[&](auto dfs, V v, V idx, V top) -> V {
                idx_[v] = idx++;
                inv_[idx_[v]] = v;
                top_[v] = top;
                if (T[v].size()) {
                    idx = dfs(dfs, T[v][0], idx, top);
                    for (u32 i{1} ; i < T[v].size() ; i++) {
                        idx = dfs(dfs, T[v][i], idx, T[v][i]);
                    }
                }
                return idx;
            }};

            dfs1(dfs1, root, INVALID, 0u);
            dfs2(dfs2, root, 0u, root);
        }

    inline usize size() const noexcept {
        return n_;
    }

    usize size(V v) const noexcept {
        assert(v < (V)size());
        return size_[v];
    }

    usize depth(V v) const noexcept {
        assert(v < (V)size());
        return dep_[v];
    }

    usize top(V v) const noexcept {
        assert(v < (V)size());
        return top_[v];
    }

    V parent(V v) const noexcept {
        assert(v < (V)size());
        return par_[v];
    }

    V index(V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    V operator[](V v) const noexcept {
        assert(v < (V)size());
        return idx_[v];
    }

    std::vector<std::pair<V, V>> decomp(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        std::vector<std::pair<V, V>> res, ser;
        while (top_[s] != top_[t]) {
            if (dep_[top_[s]] >= dep_[top_[t]]) {
                res.emplace_back(s, top_[s]);
                s = top_[s];
                if (par_[s] != INVALID) s = par_[s];
            }
            else {
                ser.emplace_back(top_[t], t);
                t = top_[t];
                if (par_[t] != INVALID) t = par_[t];
            }
        }
        res.emplace_back(s, t);
        std::reverse(ser.begin(), ser.end());
        res.insert(res.end(), ser.begin(), ser.end()); 
        return res;
    }

    std::vector<std::pair<V, V>> operator()(V s, V t) const {
        return decomp(s, t);
    }

    V lca(V u, V v) const {
        assert(u < (V)size());
        assert(v < (V)size());
        while (top_[u] != top_[v]) {
            if (dep_[top_[u]] >= dep_[top_[v]]) {
                u = top_[u];
                if (par_[u] != INVALID) u = par_[u];
            }
            else {
                v = top_[v];
                if (par_[v] != INVALID) v = par_[v];
            }
        }
        return (dep_[u] <= dep_[v] ? u : v);
    }

    // pはvの祖先か？
    bool isAncestor(V v, V p) {
        assert(v < size());
        assert(p < size());
        if (dep_[v] < dep_[p]) return false;
        while (v != INVALID and top_[v] != top_[p]) {
            v = par_[top_[v]];
        }
        return v != INVALID;
    }

    V levelAncestor(V v, usize step) const {
        assert(v < (V)size());
        if (step > dep_[v]) return INVALID;
        while (true) {
            usize dist{dep_[v] - dep_[top_[v]]};
            if (dist >= step) break;
            step -= dist + 1;
            v = par_[top_[v]];
        }
        step = (dep_[v] - dep_[top_[v]]) - step;
        return inv_[idx_[top_[v]] + step];
    }

    V jump(V s, V t, usize step) const {
        assert(s < (V)size());
        assert(t < (V)size());
        V uu{INVALID}, vv{INVALID};
        usize d{};
        for (auto [u, v] : decomp(s, t)) {
            usize dist{std::max(dep_[u], dep_[v]) - std::min(dep_[u], dep_[v])};
            if (dist >= step) {
                uu = u;
                vv = v;
                d = dist;
                break;
            }
            step -= dist + 1;
        }
        if (uu == INVALID) return INVALID;
        if (dep_[uu] <= dep_[vv]) {
            return inv_[idx_[uu] + step];
        }
        else {
            return inv_[idx_[vv] + (d - step)];
        }
    }

    usize distance(V s, V t) const {
        assert(s < (V)size());
        assert(t < (V)size());
        usize res{};
        for (auto [u, v] : decomp(s, t)) {
            if (dep_[u] > dep_[v]) std::swap(u, v);
            res += dep_[v] - dep_[u];
        }
        return res;
    }

private:
    static constexpr V INVALID{static_cast<V>(-1)};
    usize n_{};
    std::vector<V> par_{}, top_{}, idx_{}, inv_{};
    std::vector<usize> size_{}, dep_{};
};

} // namespace zawa
using namespace zawa;
// #include "atcoder/modint"
// using mint = atcoder::modint998244353;
#include <set>
using namespace std;
struct M {
    using Element = int;
    static Element identity() {
        return (int)1e9;
    }
    static Element operation(Element L, Element R) {
        return min(L, R);
    }
};
int N, Q, T[1000010], V[1000010];
vector<vector<int>> g;
// https://yukicoder.me/submissions/1110541
vector<int> solver() {
    HeavyLightDecomposition hld{g};
    SegmentTree<M> seg(N), seg2(N);
    vector<set<pair<int, int>>> bk(N);
    auto upd = [&](int u, int v) -> void {
        pair<int, int> cur{hld.depth(v), v};
        auto it = bk[u].find(cur);
        if (it != bk[u].end()) bk[u].erase(it);
        else bk[u].insert(cur);
        seg.assign(hld[u], bk[u].size() ? (bk[u].begin()->first - 2 * (int)hld.depth(u)) : M::identity());
        seg2.assign(hld[u], bk[u].size() ? bk[u].begin()->first : M::identity());
    };
    vector<int> deep(N);
    for (int i = 0 ; i < N ; i++) {
        int tp = hld.top(i);
        if (hld.depth(deep[tp]) < hld.depth(i)) deep[tp] = i;
    }
    auto leaf_prod = [&](int v) -> int {
        int u = deep[hld.top(v)];
        tie(u, v) = minmax({hld[u], hld[v]});
        return seg2.product(u, v + 1);
    };
    vector<int> anses;
    for (int i = 0 ; i < Q ; i++) {
        if (T[i] == 1) {
            for (auto [u, v] : hld(V[i], 0)) {
                upd(u, V[i]);
                if (u != v) upd(v, V[i]);
            }
        }
        else if (T[i] == 2) {
            int ans = M::identity();
            for (auto [u, v] : hld(V[i], 0)) {
                if (hld.depth(u) > hld.depth(v)) {
                    ans = min(ans, leaf_prod(u) - 2 * (int)hld.depth(u));
                }
                else {
                    ans = min(ans, leaf_prod(v) - 2 * (int)hld.depth(v));
                }
                tie(u, v) = minmax({hld[u], hld[v]});
                ans = min<int>(ans, seg.product(u, v + 1));
            }
            ans += hld.depth(V[i]);
            anses.push_back(ans);
        }
        else assert(false);
    }
    return anses;
}
int main() {
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin >> N;
    g.resize(N);
    vector<int> C(N);
    vector<vector<int>> buc(N);
    for (int i = 0 ; i < N ; i++) {
        cin >> C[i];
        C[i]--;
        buc[C[i]].push_back(i);
    }
    for (int i = 0 ; i < N - 1 ; i++) {
        int u, v;
        cin >> u >> v;
        u--; v--;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    auto flip = [&](int v) {
        T[Q] = 1;
        V[Q] = v;
        Q++;
    };
    auto query = [&](int v) {
        T[Q] = 2;
        V[Q] = v;
        Q++;
    };
    vector<int> ord;
    ord.reserve(N);
    for (int i = 0 ; i < N ; i++) if (buc[i].size()) {
        for (int v : buc[i]) flip(v);
        for (int v : buc[i]) {
            flip(v);
            query(v);
            ord.push_back(v);
            flip(v);
        }
        for (int v : buc[i]) flip(v);
    } 
    vector<int> ans(N);
    auto p = solver();
    for (int i = 0 ; i < N ; i++) ans[ord[i]] = p[i];
    for (int i = 0 ; i < N ; i++) cout << ans[i] << '\n';
}
accuracy: 1
time_ms: 1050
memory_kb: 63684
final_rank: 14

code_snippet: // AOJ #439
// Coloured Mountain Hut 2025.2.2

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll INF = 1000000000000000000LL;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) {
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

int N;
vector<vector<int>> tree; // 1-indexed
vector<int> col;
 
vector<int> depth, parent, inTime, outTime;
int timer = 0;
 
void dfsTree(int v, int p, int d) {
    depth[v] = d;
    parent[v] = p;
    inTime[v] = timer++;
    for (int nxt : tree[v]) {
        if(nxt == p) continue;
        dfsTree(nxt, v, d+1);
    }
    outTime[v] = timer;
}
 
int LOGN;
vector<vector<int>> upTable;
void buildLCA() {
    LOGN = 32 - __builtin_clz(N);
    upTable.assign(LOGN, vector<int>(N+1, 0));
    for (int v = 1; v <= N; v++){
        upTable[0][v] = parent[v];
    }
    for (int k = 1; k < LOGN; k++){
        for (int v = 1; v <= N; v++){
            int mid = upTable[k-1][v];
            upTable[k][v] = (mid == 0 ? 0 : upTable[k-1][mid]);
        }
    }
}
 
int getLCA(int a, int b) {
    if(depth[a] < depth[b])
        swap(a, b);
    int d = depth[a] - depth[b];
    for (int k = 0; k < LOGN; k++){
        if(d & (1 << k))
            a = upTable[k][a];
    }
    if(a == b) return a;
    for (int k = LOGN-1; k >= 0; k--){
        if(upTable[k][a] != upTable[k][b]){
            a = upTable[k][a];
            b = upTable[k][b];
        }
    }
    return parent[a];
}
 
int getDist(int a, int b) {
    int l = getLCA(a, b);
    return depth[a] + depth[b] - 2 * depth[l];
}
 
pair<vector<int>, vector<vector<pair<int,int>>>> buildVirtualTree(const vector<int> &nodes) {
    vector<int> vt = nodes;
    for (size_t i = 0; i + 1 < nodes.size(); i++){
        int l = getLCA(nodes[i], nodes[i+1]);
        vt.push_back(l);
    }
    sort(vt.begin(), vt.end(), [&](int a, int b){
        return inTime[a] < inTime[b];
    });
    vt.erase(unique(vt.begin(), vt.end()), vt.end());
 
    int M = vt.size();
    vector<vector<pair<int,int>>> vtAdj(M);
    {
        vector<int> st;
        for (int i = 0; i < M; i++){
            while(!st.empty() && outTime[ vt[st.back()] ] <= inTime[ vt[i] ])
                st.pop_back();
            if(!st.empty()){
                int u = st.back(), v = i;
                int w = getDist(vt[u], vt[i]);
                vtAdj[u].push_back({v, w});
                vtAdj[v].push_back({u, w});
            }
            st.push_back(i);
        }
    }
    return {vt, vtAdj};
}
 
struct State {
    ll dist;
    int v;
    int src;
};
 
struct StateComp {
    bool operator()(const State &a, const State &b) const {
        return a.dist > b.dist;
    }
};
 
void multiSourceDijkstra(int M, const vector<vector<pair<int,int>>> &vtAdj,
                           const vector<bool> &active,
                           vector<ll> &bestDist, vector<int> &bestSrc,
                           vector<ll> &secondDist, vector<int> &secondSrc)
{
    bestDist.assign(M, INF);
    secondDist.assign(M, INF);
    bestSrc.assign(M, -1);
    secondSrc.assign(M, -1);
 
    priority_queue<State, vector<State>, StateComp> pq;
    for (int i = 0; i < M; i++){
        if(active[i]){
            bestDist[i] = 0;
            bestSrc[i] = i;
            pq.push({0, i, i});
        }
    }
 
    while(!pq.empty()){
        State st = pq.top();
        pq.pop();
        int v = st.v, s = st.src;
        ll d = st.dist;
        if(bestSrc[v] == s && d != bestDist[v]) continue;
        if(bestSrc[v] != s && d != secondDist[v]) continue;
 
        for(auto &edge : vtAdj[v]){
            int u = edge.first;
            int w = edge.second;
            ll nd = d + w;
            if(bestSrc[u] == s){
                if(nd < bestDist[u]){
                    bestDist[u] = nd;
                    pq.push({nd, u, s});
                }
            } else {
                if(nd < bestDist[u]){
                    if(bestSrc[u] != -1){
                        secondDist[u] = bestDist[u];
                        secondSrc[u] = bestSrc[u];
                        pq.push({secondDist[u], u, secondSrc[u]});
                    }
                    bestDist[u] = nd;
                    bestSrc[u] = s;
                    pq.push({nd, u, s});
                } else if(s != bestSrc[u] && nd < secondDist[u]){
                    secondDist[u] = nd;
                    secondSrc[u] = s;
                    pq.push({nd, u, s});
                }
            }
        }
    }
}
 
vector<ll> ansGlobal;
 
int main() {
    N = Cin();
    col.resize(N+1);
    for (int i = 1; i <= N; i++) col[i] = Cin();

    tree.assign(N+1, vector<int>());
    for (int i = 1; i <= N-1; i++){
        int u = Cin(), v = Cin();
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
 
    depth.resize(N+1);
    parent.resize(N+1);
    inTime.resize(N+1);
    outTime.resize(N+1);
    timer = 0;
    dfsTree(1, 0, 0);
    buildLCA();
 
    // 各色ごとに頂点集合をグループ化
    vector<vector<int>> colorNodes(N+1);
    for (int v = 1; v <= N; v++){
        colorNodes[col[v]].push_back(v);
    }
    ansGlobal.assign(N+1, 0);
 
    // 各色グループごとに、仮想木を構築して multi–source Dijkstra を実行する
    for (int c = 1; c <= N; c++){
        if(colorNodes[c].empty() || colorNodes[c].size() < 2)
            continue;
 
        vector<int> nodes = colorNodes[c];
        sort(nodes.begin(), nodes.end(), [&](int a, int b){
            return inTime[a] < inTime[b];
        });
 
        auto vtPair = buildVirtualTree(nodes);
        vector<int> vtNodes = vtPair.first;
        vector<vector<pair<int,int>>> vtAdj = vtPair.second;
        int M = vtNodes.size();
 
        vector<bool> active(M, false);
        vector<bool> mark(N+1, false);
        for (int v : nodes)
            mark[v] = true;
        for (int i = 0; i < M; i++){
            if(mark[ vtNodes[i] ])
                active[i] = true;
        }
 
        vector<ll> bestDist, secondDist;
        vector<int> bestSrc, secondSrc;
        multiSourceDijkstra(M, vtAdj, active, bestDist, bestSrc, secondDist, secondSrc);
 
        for (int i = 0; i < M; i++){
            if(active[i]){
                ll candidate = (bestSrc[i] == i ? secondDist[i] : bestDist[i]);
                ansGlobal[ vtNodes[i] ] = candidate;
            }
        }
    }
 
    for (int i = 1; i <= N; i++) Cout(ansGlobal[i]);
    return 0;
}
accuracy: 1
time_ms: 230
memory_kb: 45760
final_rank: 3

code_snippet: #line 1 "main.cpp"
// competitive-verifier: PROBLEM https://onlinejudge.u-aizu.ac.jp/problems/0439

#include <iostream>
#include <vector>
#include <utility>
#line 1 "/workspaces/proconenv/lib/tools/auxiliary_tree.hpp"



#include <cstddef>
#line 7 "/workspaces/proconenv/lib/tools/auxiliary_tree.hpp"
#include <algorithm>
#include <stack>
#include <limits>
#include <iterator>
#include <type_traits>
#line 1 "/workspaces/proconenv/lib/tools/lca.hpp"



#include <cstdint>
#line 7 "/workspaces/proconenv/lib/tools/lca.hpp"
#include <cassert>
#include <numeric>
#line 13 "/workspaces/proconenv/lib/tools/lca.hpp"
#include <tuple>
#line 1 "/workspaces/proconenv/lib/tools/ceil.hpp"



#line 6 "/workspaces/proconenv/lib/tools/ceil.hpp"

namespace tools {

template <typename M, typename N>
constexpr ::std::common_type_t<M, N> ceil(const M lhs, const N rhs) {
using T = ::std::common_type_t<M, N>;
assert(rhs != N(0));
return lhs / rhs + T(((lhs > M(0) && rhs > N(0)) || (lhs < M(0) && rhs < N(0))) && lhs % rhs);
}
}


#line 1 "/workspaces/proconenv/lib/tools/less_by.hpp"



namespace tools {

template <class F>
class less_by {
private:
F selector;

public:
less_by(const F& selector) : selector(selector) {
}

template <class T>
bool operator()(const T& x, const T& y) const {
return selector(x) < selector(y);
}
};
}


#line 1 "/workspaces/proconenv/lib/tools/ceil_log2.hpp"



#line 8 "/workspaces/proconenv/lib/tools/ceil_log2.hpp"
#include <array>
#line 10 "/workspaces/proconenv/lib/tools/ceil_log2.hpp"

// Source: https://stackoverflow.com/questions/3272424/compute-fast-log-base-2-ceiling/15327567#15327567
// License: CC BY-SA 3.0
// Author: dgobbi

namespace tools {

template <typename T>
T ceil_log2(T x) {
static_assert(::std::is_integral_v<T>);
assert(x > 0);
if constexpr (::std::is_signed_v<T>) {
return static_cast<T>(::tools::ceil_log2<::std::make_unsigned_t<T>>(x));
} else {
const auto log2 = [](const int w) {
if (w == 8) return 3;
if (w == 16) return 4;
if (w == 32) return 5;
if (w == 64) return 6;
return -1;
};
static_assert(log2(::std::numeric_limits<T>::digits) >= 0);

constexpr auto t = ::std::make_tuple(
::std::array<::std::uint8_t, 3>({
UINT8_C(0xf0),
UINT8_C(0x0c),
UINT8_C(0x02)
}),
::std::array<::std::uint16_t, 4>({
UINT16_C(0xff00),
UINT16_C(0x00f0),
UINT16_C(0x000c),
UINT16_C(0x0002)
}),
::std::array<::std::uint32_t, 5>({
UINT32_C(0xffff0000),
UINT32_C(0x0000ff00),
UINT32_C(0x000000f0),
UINT32_C(0x0000000c),
UINT32_C(0x00000002)
}),
::std::array<::std::uint64_t, 6>({
UINT64_C(0xffffffff00000000),
UINT64_C(0x00000000ffff0000),
UINT64_C(0x000000000000ff00),
UINT64_C(0x00000000000000f0),
UINT64_C(0x000000000000000c),
UINT64_C(0x0000000000000002)
})
);

T y = (((x & (x - 1)) == 0) ? 0 : 1);
T j = ::std::numeric_limits<T>::digits / 2;

for (const auto t_i : ::std::get<log2(::std::numeric_limits<T>::digits) - 3>(t)) {
T k = (((x & t_i) == 0) ? 0 : j);
y += k;
x >>= k;
j >>= 1;
}

return y;
}
}
}


#line 1 "/workspaces/proconenv/lib/tools/floor_log2.hpp"



#line 1 "/workspaces/proconenv/lib/tools/popcount.hpp"



#line 8 "/workspaces/proconenv/lib/tools/popcount.hpp"

namespace tools {

template <typename T>
T popcount(T x) {
static_assert(::std::is_integral_v<T>);
assert(x >= 0);
if constexpr (::std::is_signed_v<T>) {
return static_cast<T>(::tools::popcount<::std::make_unsigned_t<T>>(x));
} else {
const auto log2 = [](const int w) {
if (w == 8) return 3;
if (w == 16) return 4;
if (w == 32) return 5;
if (w == 64) return 6;
return -1;
};
static_assert(log2(::std::numeric_limits<T>::digits) >= 0);

if constexpr (::std::numeric_limits<T>::digits == 8) {
x = (x & UINT8_C(0x55)) + (x >> 1 & UINT8_C(0x55));
x = (x & UINT8_C(0x33)) + (x >> 2 & UINT8_C(0x33));
x = (x & UINT8_C(0x0f)) + (x >> 4 & UINT8_C(0x0f));
} else if constexpr (::std::numeric_limits<T>::digits == 16) {
x = (x & UINT16_C(0x5555)) + (x >> 1 & UINT16_C(0x5555));
x = (x & UINT16_C(0x3333)) + (x >> 2 & UINT16_C(0x3333));
x = (x & UINT16_C(0x0f0f)) + (x >> 4 & UINT16_C(0x0f0f));
x = (x & UINT16_C(0x00ff)) + (x >> 8 & UINT16_C(0x00ff));
} else if constexpr (::std::numeric_limits<T>::digits == 32) {
x = (x & UINT32_C(0x55555555)) + (x >> 1 & UINT32_C(0x55555555));
x = (x & UINT32_C(0x33333333)) + (x >> 2 & UINT32_C(0x33333333));
x = (x & UINT32_C(0x0f0f0f0f)) + (x >> 4 & UINT32_C(0x0f0f0f0f));
x = (x & UINT32_C(0x00ff00ff)) + (x >> 8 & UINT32_C(0x00ff00ff));
x = (x & UINT32_C(0x0000ffff)) + (x >> 16 & UINT32_C(0x0000ffff));
} else if constexpr (::std::numeric_limits<T>::digits == 64) {
x = (x & UINT64_C(0x5555555555555555)) + (x >> 1 & UINT64_C(0x5555555555555555));
x = (x & UINT64_C(0x3333333333333333)) + (x >> 2 & UINT64_C(0x3333333333333333));
x = (x & UINT64_C(0x0f0f0f0f0f0f0f0f)) + (x >> 4 & UINT64_C(0x0f0f0f0f0f0f0f0f));
x = (x & UINT64_C(0x00ff00ff00ff00ff)) + (x >> 8 & UINT64_C(0x00ff00ff00ff00ff));
x = (x & UINT64_C(0x0000ffff0000ffff)) + (x >> 16 & UINT64_C(0x0000ffff0000ffff));
x = (x & UINT64_C(0x00000000ffffffff)) + (x >> 32 & UINT64_C(0x00000000ffffffff));
}

return x;
}
}
}


#line 8 "/workspaces/proconenv/lib/tools/floor_log2.hpp"

namespace tools {

template <typename T>
T floor_log2(T x) {
static_assert(::std::is_integral_v<T>);
assert(x > 0);
if constexpr (::std::is_signed_v<T>) {
return static_cast<T>(::tools::floor_log2<::std::make_unsigned_t<T>>(x));
} else {
const auto log2 = [](const int w) {
if (w == 8) return 3;
if (w == 16) return 4;
if (w == 32) return 5;
if (w == 64) return 6;
return -1;
};
static_assert(log2(::std::numeric_limits<T>::digits) >= 0);

x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
if constexpr (::std::numeric_limits<T>::digits > 8) x |= (x >> 8);
if constexpr (::std::numeric_limits<T>::digits > 16) x |= (x >> 16);
if constexpr (::std::numeric_limits<T>::digits > 32) x |= (x >> 32);
return ::tools::popcount(x) - static_cast<T>(1);
}
}
}


#line 1 "/workspaces/proconenv/lib/tools/pow2.hpp"



#line 6 "/workspaces/proconenv/lib/tools/pow2.hpp"

namespace tools {

template <typename T, typename ::std::enable_if<::std::is_unsigned<T>::value, ::std::nullptr_t>::type = nullptr>
constexpr T pow2(const T x) {
return static_cast<T>(1) << x;
}

template <typename T, typename ::std::enable_if<::std::is_signed<T>::value, ::std::nullptr_t>::type = nullptr>
constexpr T pow2(const T x) {
return static_cast<T>(static_cast<typename ::std::make_unsigned<T>::type>(1) << static_cast<typename ::std::make_unsigned<T>::type>(x));
}
}


#line 19 "/workspaces/proconenv/lib/tools/lca.hpp"

namespace tools {
class lca {
using u32 = ::std::uint32_t;
::std::vector<::std::vector<u32>> m_graph;
::std::vector<u32> m_depth;
::std::vector<u32> m_tour;
::std::vector<u32> m_in;
u32 m_block_size;
::std::vector<::std::vector<u32>> m_sparse_table;
::std::vector<::std::vector<::std::vector<u32>>> m_lookup_table;
::std::vector<u32> m_patterns;

bool built() const {
return !this->m_depth.empty();
}

u32 nblocks() const {
return ::tools::ceil(this->m_tour.size(), this->m_block_size);
}

auto less_by_depth() const {
return ::tools::less_by([&](const auto v) { return this->m_depth[v]; });
}

public:
lca() = default;
explicit lca(const ::std::size_t n) : m_graph(n) {
assert(n >= 1);
}

::std::size_t size() const {
return this->m_graph.size();
}

void add_edge(const ::std::size_t u, const ::std::size_t v) {
assert(!this->built());
assert(u < this->size());
assert(v < this->size());
assert(u != v);
this->m_graph[u].push_back(v);
this->m_graph[v].push_back(u);
}

void build(const ::std::size_t r) {
assert(!this->built());
assert(::std::accumulate(this->m_graph.begin(), this->m_graph.end(), static_cast<::std::size_t>(0), [](const auto sum, const auto& neighbors) { return sum + neighbors.size(); }) == 2 * (this->size() - 1));

this->m_depth.assign(this->size(), ::std::numeric_limits<u32>::max());
this->m_tour.resize(2 * this->size() - 1);
this->m_in.resize(this->size());

u32 t = 0;
::std::stack<::std::pair<u32, u32>> stack;
stack.emplace(r, 0);
while (!stack.empty()) {
const auto [here, depth] = stack.top();
stack.pop();
this->m_tour[t] = here;
if (this->m_depth[here] == ::std::numeric_limits<u32>::max()) {
this->m_depth[here] = depth;
this->m_in[here] = t;
for (const auto next : this->m_graph[here]) {
if (this->m_depth[next] == ::std::numeric_limits<u32>::max()) {
stack.emplace(here, depth);
stack.emplace(next, depth + 1);
}
}
}
++t;
}

if (this->size() > 1) {
this->m_tour.pop_back();
}

this->m_block_size = ::std::max<u32>(1, ::tools::ceil(::tools::ceil_log2(this->m_tour.size()), 2));
this->m_sparse_table.resize(::tools::floor_log2(this->nblocks()) + 1);
this->m_sparse_table[0].resize(this->nblocks());
for (u32 b = 0; (b + 1) * this->m_block_size <= this->m_tour.size(); ++b) {
const auto l = b * this->m_block_size;
const auto r = ::std::min<u32>(l + this->m_block_size, this->m_tour.size());
this->m_sparse_table[0][b] = *::std::min_element(this->m_tour.begin() + l, this->m_tour.begin() + r, this->less_by_depth());
}
for (u32 h = 1; h < this->m_sparse_table.size(); ++h) {
this->m_sparse_table[h].resize(this->nblocks() + UINT32_C(1) - (UINT32_C(1) << h));
for (u32 b = 0; b < this->m_sparse_table[h].size(); ++b) {
this->m_sparse_table[h][b] = ::std::min(this->m_sparse_table[h - 1][b], this->m_sparse_table[h - 1][b + (UINT32_C(1) << (h - 1))], this->less_by_depth());
}
}

this->m_lookup_table.resize(::tools::pow2(this->m_block_size - 1));
for (u32 p = 0; p < this->m_lookup_table.size(); ++p) {
this->m_lookup_table[p].resize(this->m_block_size + 1);
for (u32 l = 0; l <= this->m_block_size; ++l) {
this->m_lookup_table[p][l].resize(this->m_block_size + 1);
}

::std::vector<u32> partial_sum(this->m_block_size);
partial_sum[0] = this->m_block_size;
for (u32 i = 1; i < this->m_block_size; ++i) {
partial_sum[i] = partial_sum[i - 1] - UINT32_C(1) + (((p >> (i - 1)) & UINT32_C(1)) << 1);
}

for (u32 l = 0; l < this->m_block_size; ++l) {
this->m_lookup_table[p][l][l + 1] = l;
for (u32 r = l + 2; r <= this->m_block_size; ++r) {
this->m_lookup_table[p][l][r] = ::std::min(this->m_lookup_table[p][l][r - 1], r - 1, ::tools::less_by([&](const auto i) { return partial_sum[i]; }));
}
}
}

this->m_patterns.resize(this->nblocks());
for (u32 b = 0; b * this->m_block_size < this->m_tour.size(); ++b) {
const auto l = b * this->m_block_size;
const auto r = ::std::min<u32>(l + this->m_block_size, this->m_tour.size());
this->m_patterns[b] = 0;
for (u32 i = l; i + 1 < r; ++i) {
this->m_patterns[b] |= static_cast<u32>(this->m_depth[this->m_tour[i]] < this->m_depth[this->m_tour[i + 1]]) << (i - l);
}
}
}

::std::size_t depth(const ::std::size_t v) const {
assert(this->built());
assert(v < this->size());
return this->m_depth[v];
}

::std::size_t query(::std::size_t u, ::std::size_t v) const {
assert(this->built());
assert(u < this->size());
assert(v < this->size());

::std::tie(u, v) = ::std::minmax({u, v}, ::tools::less_by([&](const auto w) { return this->m_in[w]; }));

const auto l = this->m_in[u];
const auto r = this->m_in[v] + UINT32_C(1);
const auto bl = ::tools::ceil(l, this->m_block_size);
const auto br = r / this->m_block_size;
u32 lca;
if (br < bl) {
lca = this->m_tour[br * this->m_block_size + this->m_lookup_table[this->m_patterns[br]][l % this->m_block_size][r % this->m_block_size]];
} else {
lca = u;
if (bl < br) {
const auto h = ::tools::floor_log2(br - bl);
lca = ::std::min(this->m_sparse_table[h][bl], this->m_sparse_table[h][br - (UINT32_C(1) << h)], this->less_by_depth());
}
if (l < bl * this->m_block_size) {
lca = ::std::min(lca, this->m_tour[(bl - UINT32_C(1)) * this->m_block_size + this->m_lookup_table[this->m_patterns[bl - 1]][l % this->m_block_size][this->m_block_size]], this->less_by_depth());
}
if (br * this->m_block_size < r) {
lca = ::std::min(lca, this->m_tour[br * this->m_block_size + this->m_lookup_table[this->m_patterns[br]][0][r % this->m_block_size]], this->less_by_depth());
}
}

return lca;
}

// for tools::auxiliary_tree
::std::size_t internal_in(const ::std::size_t v) const {
assert(this->built());
assert(v < this->size());
return this->m_in[v];
}
};
}


#line 1 "/workspaces/proconenv/lib/tools/less_by_first.hpp"



#line 5 "/workspaces/proconenv/lib/tools/less_by_first.hpp"

namespace tools {

class less_by_first {
public:
template <class T1, class T2>
bool operator()(const ::std::pair<T1, T2>& x, const ::std::pair<T1, T2>& y) const {
return x.first < y.first;
}
};
}


#line 15 "/workspaces/proconenv/lib/tools/auxiliary_tree.hpp"

namespace tools {
class auxiliary_tree {
::tools::lca m_lca;

public:
auxiliary_tree() = default;
explicit auxiliary_tree(const ::std::size_t n) : m_lca(n) {
}

::std::size_t size() const {
return this->m_lca.size();
}

void add_edge(const ::std::size_t u, const ::std::size_t v) {
this->m_lca.add_edge(u, v);
}

void build(const ::std::size_t r) {
this->m_lca.build(r);
}

::std::size_t depth(const ::std::size_t v) const {
return this->m_lca.depth(v);
}

::std::size_t lca(const ::std::size_t u, const ::std::size_t v) const {
return this->m_lca.query(u, v);
}

class query_result {
::std::vector<::std::pair<::std::size_t, ::std::size_t>> m_parent;
::std::vector<::std::vector<::std::size_t>> m_children;
::std::size_t m_root;

template <typename InputIterator>
query_result(const ::tools::auxiliary_tree& tree, const InputIterator begin, const InputIterator end) {
::std::vector<::std::size_t> X(begin, end);
assert(!X.empty());
::std::sort(X.begin(), X.end(), ::tools::less_by([&](const auto v) { return tree.m_lca.internal_in(v); }));

::std::stack<::std::size_t> stack;
auto it = X.begin();
stack.push(*(it++));
for (; it != X.end(); ++it) {
const auto w = tree.lca(stack.top(), *it);
while (!stack.empty() && tree.depth(w) < tree.depth(stack.top())) {
const auto u = stack.top();
stack.pop();
this->m_parent.emplace_back(u, w);
if (!stack.empty() && tree.depth(w) < tree.depth(stack.top())) {
this->m_parent.back().second = stack.top();
}
}
if (stack.empty() || stack.top() != w) {
stack.push(w);
}
stack.push(*it);
}
while (!stack.empty()) {
const auto u = stack.top();
stack.pop();
if (stack.empty()) {
this->m_parent.emplace_back(u, ::std::numeric_limits<::std::size_t>::max());
this->m_root = u;
} else {
this->m_parent.emplace_back(u, stack.top());
}
}

::std::sort(this->m_parent.begin(), this->m_parent.end(), ::tools::less_by_first{});

this->m_children.resize(this->m_parent.size());
for (const auto& [v, p] : this->m_parent) {
if (v != this->m_root) {
const auto it = ::std::lower_bound(this->m_parent.begin(), this->m_parent.end(), ::std::make_pair(p, ::std::numeric_limits<::std::size_t>::max()), ::tools::less_by_first{});
assert(it != this->m_parent.end());
assert(it->first == p);
this->m_children[::std::distance(this->m_parent.begin(), it)].push_back(v);
}
}
}

public:
class vertices_iterable {
query_result const *m_qr;

public:
class iterator {
query_result const *m_qr;
::std::size_t m_i;

public:
using difference_type = ::std::ptrdiff_t;
using value_type = ::std::size_t;
using reference = const ::std::size_t&;
using pointer = const ::std::size_t*;
using iterator_category = ::std::input_iterator_tag;

iterator() = default;
iterator(query_result const * const qr, const ::std::size_t i) : m_qr(qr), m_i(i) {
}

reference operator*() const {
return this->m_qr->m_parent[this->m_i].first;
}
iterator& operator++() {
++this->m_i;
return *this;
}
iterator operator++(int) {
const auto self = *this;
++*this;
return self;
}
friend bool operator==(const iterator& lhs, const iterator& rhs) {
assert(lhs.m_qr == rhs.m_qr);
return lhs.m_i == rhs.m_i;
}
friend bool operator!=(const iterator& lhs, const iterator& rhs) {
return !(lhs == rhs);
}
};

vertices_iterable() = default;
vertices_iterable(query_result const * const qr) : m_qr(qr) {
}

iterator begin() const {
return iterator(this->m_qr, 0);
};
iterator end() const {
return iterator(this->m_qr, this->m_qr->m_parent.size());
}
};

query_result() = default;

::std::size_t size() const {
return this->m_parent.size();
}

vertices_iterable vertices() const {
return vertices_iterable(this);
}

::std::size_t root() const {
return this->m_root;
}

::std::size_t parent(const ::std::size_t v) const {
const auto it = ::std::lower_bound(this->m_parent.begin(), this->m_parent.end(), ::std::make_pair(v, ::std::numeric_limits<::std::size_t>::max()), ::tools::less_by_first{});
assert(it != this->m_parent.end());
assert(it->first == v);
return it->second;
}

const ::std::vector<::std::size_t>& children(const ::std::size_t v) const {
const auto it = ::std::lower_bound(this->m_parent.begin(), this->m_parent.end(), ::std::make_pair(v, ::std::numeric_limits<::std::size_t>::max()), ::tools::less_by_first{});
assert(it != this->m_parent.end());
assert(it->first == v);
return this->m_children[::std::distance(this->m_parent.begin(), it)];
}

friend ::tools::auxiliary_tree;
};

template <typename InputIterator>
query_result query(const InputIterator begin, const InputIterator end) const {
return query_result(*this, begin, end);
}

template <typename Z, ::std::enable_if_t<::std::is_integral_v<Z>, ::std::nullptr_t> = nullptr>
query_result query(const ::std::vector<Z>& X) const {
return this->query(X.begin(), X.end());
}
};
}


#line 1 "/workspaces/proconenv/lib/tools/rerooting_dp.hpp"



#line 11 "/workspaces/proconenv/lib/tools/rerooting_dp.hpp"

namespace tools {
template <typename R, typename M, typename F_VE, typename F_EV>
class rerooting_dp {
private:
::std::vector<::std::size_t> m_edges;
::std::vector<::std::vector<::std::size_t>> m_graph;
F_VE m_f_ve;
F_EV m_f_ev;

class vertex {
private:
const ::tools::rerooting_dp<R, M, F_VE, F_EV> *m_self;

public:
::std::size_t id;
::std::size_t neighbor_id_of_parent;
::std::vector<::std::size_t> neighbor_ids_of_children;
typename M::T parent_dp;
::std::vector<typename M::T> children_dp;
::std::vector<typename M::T> children_dp_cumsum1;
::std::vector<typename M::T> children_dp_cumsum2;

vertex() = default;
vertex(const vertex&) = default;
vertex(vertex&&) = default;
~vertex() = default;
vertex& operator=(const vertex&) = default;
vertex& operator=(vertex&&) = default;

explicit vertex(const ::tools::rerooting_dp<R, M, F_VE, F_EV> * const self, const ::std::size_t id) :
m_self(self), id(id), parent_dp(M::e()) {
}

::std::size_t parent_edge_id() const {
return this->m_self->m_graph[this->id][this->neighbor_id_of_parent];
}
::std::size_t parent_vertex_id() const {
return this->m_self->m_edges[this->parent_edge_id()] ^ this->id;
}
::std::size_t child_size() const {
return this->neighbor_ids_of_children.size();
}
::std::size_t child_edge_id(const ::std::size_t child_number) const {
return this->m_self->m_graph[this->id][this->neighbor_ids_of_children[child_number]];
}
::std::size_t child_vertex_id(const ::std::size_t child_number) const {
return this->m_self->m_edges[this->child_edge_id(child_number)] ^ this->id;
}
R dp_as_root() const {
return this->m_self->m_f_ev(M::op(this->parent_dp, this->children_dp_cumsum1.back()), this->id);
}
R dp_excluding_parent() const {
return this->m_self->m_f_ev(this->children_dp_cumsum1.back(), this->id);
}
R dp_excluding_child(const ::std::size_t excluded_child_number) const {
return this->m_self->m_f_ev(M::op(this->parent_dp, M::op(this->children_dp_cumsum1[excluded_child_number], this->children_dp_cumsum2[excluded_child_number + 1])), this->id);
}
};

public:
rerooting_dp() = default;
rerooting_dp(const ::tools::rerooting_dp<R, M, F_VE, F_EV>&) = default;
rerooting_dp(::tools::rerooting_dp<R, M, F_VE, F_EV>&&) = default;
~rerooting_dp() = default;
::tools::rerooting_dp<R, M, F_VE, F_EV>& operator=(const ::tools::rerooting_dp<R, M, F_VE, F_EV>&) = default;
::tools::rerooting_dp<R, M, F_VE, F_EV>& operator=(::tools::rerooting_dp<R, M, F_VE, F_EV>&&) = default;

rerooting_dp(const ::std::size_t n, const F_VE& f_ve, const F_EV& f_ev) : m_graph(n), m_f_ve(f_ve), m_f_ev(f_ev) {
assert(n >= 1);
}

::std::size_t size() const {
return this->m_graph.size();
}

::std::size_t add_edge(const ::std::size_t u, const ::std::size_t v) {
this->m_graph[u].push_back(this->m_edges.size());
this->m_graph[v].push_back(this->m_edges.size());
this->m_edges.push_back(u ^ v);
return this->m_edges.size() - 1;
}

::std::vector<R> query() const {
assert(this->m_edges.size() + 1 == this->size());

const int PRE_VERTEX = 1;
const int POST_EDGE = 2;
const int POST_VERTEX = 3;
const ::std::size_t INVALID = ::std::numeric_limits<::std::size_t>::max();

::std::vector<vertex> vertices;
for (::std::size_t i = 0; i < this->size(); ++i) {
vertices.emplace_back(this, i);
}

::std::stack<::std::tuple<int, ::std::size_t, ::std::size_t>> stack;
::std::vector<bool> will_visit(this->size(), false);
stack.emplace(PRE_VERTEX, 0, INVALID);
will_visit[0] = true;
while (!stack.empty()) {
const int type = ::std::get<0>(stack.top());
if (type == PRE_VERTEX) {

const ::std::size_t vertex_id = ::std::get<1>(stack.top());
stack.pop();

vertex& v = vertices[vertex_id];
stack.emplace(POST_VERTEX, vertex_id, INVALID);
for (::std::size_t neighbor_id = 0; neighbor_id < this->m_graph[vertex_id].size(); ++neighbor_id) {
const ::std::size_t child_vertex_id = this->m_edges[this->m_graph[vertex_id][neighbor_id]] ^ vertex_id;
if (will_visit[child_vertex_id]) {
v.neighbor_id_of_parent = neighbor_id;
} else {
v.neighbor_ids_of_children.push_back(neighbor_id);
stack.emplace(POST_EDGE, vertex_id, v.child_size() - 1);
stack.emplace(PRE_VERTEX, child_vertex_id, INVALID);
will_visit[child_vertex_id] = true;
}
}
v.children_dp.resize(v.child_size());

} else if (type == POST_EDGE) {

const ::std::size_t vertex_id = ::std::get<1>(stack.top());
const ::std::size_t child_number = ::std::get<2>(stack.top());
stack.pop();

vertex& v = vertices[vertex_id];
const vertex& c = vertices[v.child_vertex_id(child_number)];
v.children_dp[child_number] = this->m_f_ve(c.dp_excluding_parent(), v.child_edge_id(child_number));

} else { // POST_VERTEX

const ::std::size_t vertex_id = ::std::get<1>(stack.top());
stack.pop();

vertex& v = vertices[vertex_id];

v.children_dp_cumsum1.reserve(v.child_size() + 1);
v.children_dp_cumsum1.push_back(M::e());
for (::std::size_t child_number = 0; child_number < v.child_size(); ++child_number) {
v.children_dp_cumsum1.push_back(M::op(v.children_dp_cumsum1.back(), v.children_dp[child_number]));
}

v.children_dp_cumsum2.reserve(v.child_size() + 1);
v.children_dp_cumsum2.push_back(M::e());
for (::std::size_t child_number = v.child_size(); child_number --> 0;) {
v.children_dp_cumsum2.push_back(M::op(v.children_dp[child_number], v.children_dp_cumsum2.back()));
}
::std::reverse(v.children_dp_cumsum2.begin(), v.children_dp_cumsum2.end());

}
}

stack.emplace(PRE_VERTEX, 0, INVALID);
while (!stack.empty()) {
const ::std::size_t vertex_id = ::std::get<1>(stack.top());
stack.pop();

const vertex& v = vertices[vertex_id];
for (::std::size_t child_number = 0; child_number < v.child_size(); ++child_number) {
vertex& c = vertices[v.child_vertex_id(child_number)];
c.parent_dp = this->m_f_ve(v.dp_excluding_child(child_number), c.parent_edge_id());
stack.emplace(PRE_VERTEX, c.id, INVALID);
}
}

::std::vector<R> result;
result.reserve(this->size());
for (const vertex& v : vertices) {
result.push_back(v.dp_as_root());
}
return result;
}
};
}


#line 1 "/workspaces/proconenv/lib/tools/monoid.hpp"



#line 1 "/workspaces/proconenv/lib/tools/gcd.hpp"



#line 6 "/workspaces/proconenv/lib/tools/gcd.hpp"

namespace tools {
template <typename M, typename N>
constexpr ::std::common_type_t<M, N> gcd(const M m, const N n) {
return ::std::gcd(m, n);
}
}


#line 9 "/workspaces/proconenv/lib/tools/monoid.hpp"

namespace tools {
namespace monoid {
template <typename M, M ...dummy>
struct max;

template <typename M>
struct max<M> {
static_assert(::std::is_arithmetic_v<M>, "M must be a built-in arithmetic type.");

using T = M;
static T op(const T lhs, const T rhs) {
return ::std::max(lhs, rhs);
}
static T e() {
if constexpr (::std::is_integral_v<M>) {
return ::std::numeric_limits<M>::min();
} else {
return -::std::numeric_limits<M>::infinity();
}
}
};

template <typename M, M E>
struct max<M, E> {
static_assert(::std::is_integral_v<M>, "M must be a built-in integral type.");

using T = M;
static T op(const T lhs, const T rhs) {
assert(E <= lhs);
assert(E <= rhs);
return ::std::max(lhs, rhs);
}
static T e() {
return E;
}
};

template <typename M, M ...dummy>
struct min;

template <typename M>
struct min<M> {
static_assert(::std::is_arithmetic_v<M>, "M must be a built-in arithmetic type.");

using T = M;
static T op(const T lhs, const T rhs) {
return ::std::min(lhs, rhs);
}
static T e() {
if constexpr (::std::is_integral_v<M>) {
return ::std::numeric_limits<M>::max();
} else {
return ::std::numeric_limits<M>::infinity();
}
}
};

template <typename M, M E>
struct min<M, E> {
static_assert(::std::is_integral_v<M>, "M must be a built-in integral type.");

using T = M;
static T op(const T lhs, const T rhs) {
assert(lhs <= E);
assert(rhs <= E);
return ::std::min(lhs, rhs);
}
static T e() {
return E;
}
};

template <typename M>
struct multiplies {
private:
using VR = ::std::conditional_t<::std::is_arithmetic_v<M>, const M, const M&>;

public:
using T = M;
static T op(VR lhs, VR rhs) {
return lhs * rhs;
}
static T e() {
return T(1);
}
};

template <>
struct multiplies<bool> {
using T = bool;
static T op(const bool lhs, const bool rhs) {
return lhs && rhs;
}
static T e() {
return true;
}
};

template <typename M>
struct gcd {
private:
static_assert(!::std::is_arithmetic_v<M> || (::std::is_integral_v<M> && !::std::is_same_v<M, bool>), "If M is a built-in arithmetic type, it must be integral except for bool.");
using VR = ::std::conditional_t<::std::is_arithmetic_v<M>, const M, const M&>;

public:
using T = M;
static T op(VR lhs, VR rhs) {
return ::tools::gcd(lhs, rhs);
}
static T e() {
return T(0);
}
};

template <typename M, M E>
struct update {
static_assert(::std::is_integral_v<M>, "M must be a built-in integral type.");

using T = M;
static T op(const T lhs, const T rhs) {
return lhs == E ? rhs : lhs;
}
static T e() {
return E;
}
};
}
}


#line 9 "main.cpp"

int main() {
std::cin.tie(nullptr);
std::ios_base::sync_with_stdio(false);

int N;
std::cin >> N;
std::vector<int> c(N);
for (auto&& c_i : c) {
std::cin >> c_i;
--c_i;
}
tools::auxiliary_tree tree(N);
for (int i = 0; i < N - 1; ++i) {
int s, t;
std::cin >> s >> t;
--s, --t;
tree.add_edge(s, t);
}
tree.build(0);

std::vector<std::vector<int>> huts(N);
for (int v = 0; v < N; ++v) {
huts[c[v]].push_back(v);
}

std::vector<int> answers(N);
std::vector<int> tree2aux(N);
for (int color = 0; color < N; ++color) {
if (huts[color].empty()) continue;

const auto aux = tree.query(huts[color].begin(), huts[color].end());
std::vector<int> aux2tree(aux.size());
{
int aux_v = 0;
for (const auto tree_v : aux.vertices()) {
tree2aux[tree_v] = aux_v;
aux2tree[aux_v] = tree_v;
++aux_v;
}
}

std::vector<int> w;
const auto f_ve = [&](const auto& v, const auto e) {
return (v.second ? 0 : v.first) + w[e];
};
const auto f_ev = [&](const auto e, const auto v) {
return std::make_pair(e, c[aux2tree[v]] == color);
};
tools::rerooting_dp<std::pair<int, bool>, tools::monoid::min<int>, decltype(f_ve), decltype(f_ev)> dp(aux.size(), f_ve, f_ev);
for (const auto tree_v : aux.vertices()) {
if (tree_v == aux.root()) continue;
dp.add_edge(tree2aux[tree_v], tree2aux[aux.parent(tree_v)]);
const auto lca = tree.lca(tree_v, aux.parent(tree_v));
w.push_back(tree.depth(tree_v) + tree.depth(aux.parent(tree_v)) - 2 * tree.depth(lca));
}
const auto partial_answers = dp.query();

for (int aux_v = 0; std::cmp_less(aux_v, aux.size()); ++aux_v) {
if (partial_answers[aux_v].second) {
answers[aux2tree[aux_v]] = partial_answers[aux_v].first;
}
}
}

for (const auto answer : answers) {
std::cout << answer << '\n';
}
return 0;
}
accuracy: 1
time_ms: 250
memory_kb: 30172
final_rank: 2

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct virtualtree{
	long long n;
	vector<vector<long long>> g,p;
	vector<long long> o,d;
	long long cou = 0ll;
	virtualtree(vector<vector<long long>> g) : g(g), n(g.size()) {
		o.resize(n,-1ll);
		p.resize(n);
		d.resize(n);
		for(long long i = 0ll;i < n;i++){
			p[i].resize(20ll);
		}
		d[0ll] = 0ll;
		p[0ll][0ll] = 0ll;
		dfs(0ll);
		for(long long i = 1ll;i < 20ll;i++){
			for(long long j = 0ll;j < n;j++){
				p[j][i] = p[p[j][i - 1ll]][i - 1ll];
			}
		}
	}
	void dfs(long long x){
		o[x] = cou;
		cou++;
		for(long long i : g[x]){
			if(o[i] != -1ll){
				continue;
			}
			p[i][0ll] = x;
			d[i] = d[x] + 1ll;
			dfs(i);
		}
	}
	long long root(long long a,long long b){
		for(long long i = 0ll;i < 20ll;i++){
			if(b & (1ll << i)){
				a = p[a][i];
			}
		}
		return a;
	}
	long long lca(long long u,long long v){
		if(d[u] > d[v]){
			u = root(u,d[u] - d[v]);
		}
		if(d[v] > d[u]){
			v = root(v,d[v] - d[u]);
		}
		long long low = 0ll,high = n;
		while(low < high){
			long long mid = (low + high) / 2ll;
			if(root(u,mid) == root(v,mid)){
				high = mid;
			}else{
				low = mid + 1ll;
			}
		}
		return root(u,low);
	}
	long long dist(long long u,long long v){
		long long lc = lca(u,v);
		return d[u] + d[v] - d[lc] * 2ll;
	}
	vector<vector<pair<long long,long long>>> get(vector<long long> x){
		vector<vector<pair<long long,long long>>> res(x.size(),vector<pair<long long,long long>>());
		auto com = [&](long long a,long long b) -> bool {
			return o[a] < o[b];
		};
		map<long long,long long> t;
		set<long long> c;
		sort(x.begin(),x.end(),com);
		for(long long i = 0ll;i < (long long)x.size();i++){
			c.insert(x[i]);
			t[x[i]] = i;
		}
		x.push_back(x[0ll]);
		stack<long long> s;
		s.push(x[0ll]);
		for(long long i = 1ll;i < (long long)x.size();i++){
			long long lc = lca(x[i - 1ll],x[i]);
			if(!c.count(lc)){
				t[lc] = (long long)c.size();
				c.insert(lc);
				res.push_back({});
			}
			long long last = -1ll;
			while(!s.empty() && d[s.top()] >= d[lc]){
				long long it = s.top();
				s.pop();
				if(last != -1ll && last != it){
					res[t[it]].push_back({t[last],dist(it,last)});
					res[t[last]].push_back({t[it],dist(it,last)});
				}
				last = it;
			}
			if(last != -1ll && last != lc){
				res[t[last]].push_back({t[lc],dist(last,lc)});
				res[t[lc]].push_back({t[last],dist(last,lc)});
			}
			s.push(lc);
			s.push(x[i]);
		}
		return res;
	}
};
int main()
{
	long long n;
	cin >> n;
	vector<vector<long long>> g(n,vector<long long>());
	vector<vector<long long>> a(n,vector<long long>());
	for(long long i = 0ll;i < n;i++){
		long long x;
		cin >> x;
		a[x - 1ll].push_back(i);
	}
	for(long long i = 0ll;i < n - 1ll;i++){
		long long u,v;
		cin >> u >> v;
		u--;v--;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	virtualtree v(g);
	auto f = [](vector<vector<pair<long long,long long>>> g,long long X) -> vector<long long> {
		long long n = g.size();
		vector<long long> p(n,-1ll);
		{
			queue<long long> q;
			q.push(0ll);
			p[0ll] = 0ll;
			while(!q.empty()){
				long long it = q.front();
				q.pop();
				for(pair<long long,long long> i : g[it]){
					if(p[i.first] != -1ll) continue;
					p[i.first] = it;
					q.push(i.first);
				}
			}
		}
		vector<pair<pair<long long,long long>,pair<long long,long long>>> b(n,{{1000000000ll,-1ll},{1000000000ll,-1ll}});
		{
			auto cal = [&](auto cal,long long x) -> pair<pair<long long,long long>,pair<long long,long long>> {
				pair<pair<long long,long long>,pair<long long,long long>> res = {{1000000000ll,-1ll},{1000000000ll,-1ll}};
				for(pair<long long,long long> i : g[x]){
					if(i.first == p[x]) continue;
					pair<pair<long long,long long>,pair<long long,long long>> it = cal(cal,i.first);
					if(it.first.first + i.second < res.first.first){
						res.second = res.first;
						res.first = it.first;
						res.first.first += i.second;
						res.first.second = i.first;
					}else if(it.first.first + i.second < res.second.first){
						res.second = it.first;
						res.second.first += i.second;
						res.second.second = i.first;
					}
					if(it.second.first + i.second < res.second.first){
						res.second = it.second;
						res.second.first += i.second;
						res.second.second = i.first;
					}
					if(i.first < X){
						if(i.second < res.first.first){
							res.second = res.first;
							res.first = {i.second,i.first};
						}else if(i.second < res.second.first){
							res.second = {i.second,i.first};
						}
					}
				}
				b[x] = res;
				return res;
			};
			cal(cal,0ll);
		}
		vector<long long> a(n,1000000000ll);
		{
			queue<long long> q;
			for(pair<long long,long long> i : g[0ll]){
				q.push(i.first);
			}
			while(!q.empty()){
				long long it = q.front();
				q.pop();
				for(pair<long long,long long> i : g[it]){
					if(i.first == p[it]){
						a[it] = a[p[it]] + i.second;
						if(b[p[it]].first.second == it){
							a[it] = min(a[it],b[p[it]].second.first + i.second);
						}else{
							a[it] = min(a[it],b[p[it]].first.first + i.second);
						}
						if(i.first < X){
							a[it] = min(a[it],i.second);
						}
						continue;
					}
					q.push(i.first);
				}
			}
		}
		vector<long long> ans(X);
		for(long long i = 0ll;i < X;i++){
			ans[i] = min(a[i],b[i].first.first);
		}
		return ans;
	};
	vector<long long> ans(n);
	for(long long i = 0ll;i < n;i++){
		if(a[i].empty()) continue;
		vector<long long> it = f(v.get(a[i]),a[i].size());
		auto com = [&](long long a,long long b) -> bool {
			return v.o[a] < v.o[b];
		};
		sort(a[i].begin(),a[i].end(),com);
		for(long long j = 0ll;j < (long long)it.size();j++){
			ans[a[i][j]] = it[j];
		}
	}
	for(long long i = 0ll;i < n;i++){
		cout << ans[i] << endl;
	}
}
accuracy: 1
time_ms: 1800
memory_kb: 90324
final_rank: 20

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<ll,ll>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using ve = vector<vector<int>>;
using vb = vector<bool>;
using vvb = vector<vb>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
#define UB(A,x) (int)(upper_bound(A.begin(),A.end(),x)-A.begin())
// #define MOD 1000000007
#define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T> inline bool chmax(T &a,T b){if(a < b){a = b;return true;}return false;}
template<typename T> inline bool chmin(T &a,T b){if(a > b){a = b;return true;}return false;}
ld dist(ld x1,ld y1,ld x2, ld y2){return sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}


class LCA{
  public:
  int n,root;
  const int beki = 30;
  vector<vector<int>> v;
  vector<vector<int>> nxt;
  vector<int> d;
  LCA(int n,int root = 0) : n(n),root(root),v(n),nxt(n,vector<int>(beki,root)),d(n,-1) {}
  LCA(const vector<vector<int>> &v,int root) : n(v.size()),root(root),v(v),nxt(v.size(),vector<int>(beki,root)),d(n,-1) {}
  void add_edge(int x,int y){
    v[x].push_back(y);
    v[y].push_back(x);
  }
  void bfs(){
    queue<int> que;
    d[root] = 0;
    que.push(root);
    while(!que.empty()){
      int ov = que.front();que.pop();
      for(auto nv : v[ov]){
        if(d[nv] != -1)continue;
        nxt[nv][0] = ov;
        d[nv] = d[ov]+1;
        que.push(nv);
      }
    }
  }
  void build(){
    bfs();
    for(int i = 1;i < 30;i++)for(int j = 0;j < n;j++)nxt[j][i] = nxt[nxt[j][i-1]][i-1];
  }
  int get_lca(int x,int y){
    if(d[x] < d[y])swap(x,y);
    for(int i = 29;i >= 0;i--)if(((d[x]-d[y]) >> i) & 1)x = nxt[x][i];
    if(x == y)return x;
    for(int i = 29;i >= 0;i--)if(nxt[x][i] != nxt[y][i])x = nxt[x][i],y = nxt[y][i];
    return nxt[x][0];
  }
  int dist(int x,int y){
    return d[x]+d[y]-2*d[get_lca(x,y)];
  }
};

// https://beet-aizu.github.io/library/tree/auxiliarytree.cpp.html
struct AuxiliaryTree : LCA{
  vector<int> idx;
  vector<vector<int>> T;
  AuxiliaryTree(int n,int root = 0):LCA(n,root),idx(n),T(n){}

  void dfs(int ov,int pre,int &pos){
    idx[ov] = pos++;
    for(auto nv : v[ov]){
      if(nv != pre)dfs(nv,ov,pos);
    }
  }
  void build(int root = 0){
    LCA::build();
    int pos = 0;
    dfs(root,-1,pos);
  }
  void add_aux_edge(int a,int b){
    T[a].emplace_back(b);
    T[b].emplace_back(a);
  }
  // O(sz(vs)log(sz(vs)))
  void query(vector<int> &vs){
    assert(!vs.empty());
    sort(vs.begin(),vs.end(),[&](int a,int b){return idx[a] < idx[b];});
    vs.erase(unique(vs.begin(),vs.end()),vs.end());
    stack<int> st;
    st.emplace(vs[0]);
    int k = vs.size();
    for(int i = 1;i < k;i++){
      int w = get_lca(vs[i-1],vs[i]);
      if(w != vs[i-1]){
        int l = st.top();st.pop();
        while(!st.empty() && d[w] < d[st.top()]){
          add_aux_edge(st.top(),l);
          l = st.top();st.pop();
        }
        if(st.empty() || st.top() != w){
          st.emplace(w);
          vs.emplace_back(w);
        }
        add_aux_edge(w,l);
      }
      st.emplace(vs[i]);
    }
    while(st.size() > 1){
      int c = st.top();st.pop();
      add_aux_edge(st.top(),c);
    }
  }
  void clear(const vector<int> &ws){
    for(auto ov : ws)T[ov].clear();
  }
};


int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  int n;
  cin >> n;
  vi c(n);cin >> c;
  AuxiliaryTree at(n);
  rep(i,n-1){
    int a,b;cin >> a >> b;
    a--;b--;
    at.add_edge(a,b);
  }
  at.build();
  ve C(n);
  vi res(n,1ll << 50);
  rep(i,n)C[c[i]-1].emplace_back(i);
  vi ds(n),idx(n);
  auto dijk = [&](auto &vs,auto &ss){
    min_priority_queue<P> que;
    for(auto au : vs)ds[au] = 1ll << 50,idx[au] = -1;
    for(auto au : ss)ds[au] = 0,idx[au] = au,que.emplace(ds[au],au);
    while(!que.empty()){
      auto [od,ov] = que.top();que.pop();
      if(ds[ov] < od)continue;
      for(auto nv : at.T[ov]){
        int cst = abs(at.d[nv]-at.d[ov]);
        if(ds[nv] <= ds[ov] + cst)continue;
        ds[nv] = ds[ov] + cst;
        idx[nv] = idx[ov];
        que.emplace(ds[nv],nv);
      }
    }
  };
  rep(i,n){
    if(C[i].empty())continue;
    auto vs = C[i];
    at.query(vs);
    dijk(vs,C[i]);
    for(auto ov : vs){
      for(auto nv : at.T[ov]){
        if(idx[nv] == idx[ov])continue;
        chmin<ll>(res[idx[ov]],at.dist(idx[ov],idx[nv]));
        chmin<ll>(res[idx[nv]],at.dist(idx[ov],idx[nv]));
      }
    }
    at.clear(vs);
  }
  rep(i,n)cout << res[i] << "\n";


  

  return 0;
}
accuracy: 1
time_ms: 460
memory_kb: 74132
final_rank: 11

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<ll,ll>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using ve = vector<vector<int>>;
using vb = vector<bool>;
using vvb = vector<vb>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
#define UB(A,x) (int)(upper_bound(A.begin(),A.end(),x)-A.begin())
// #define MOD 1000000007
#define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T> inline bool chmax(T &a,T b){if(a < b){a = b;return true;}return false;}
template<typename T> inline bool chmin(T &a,T b){if(a > b){a = b;return true;}return false;}
ld dist(ld x1,ld y1,ld x2, ld y2){return sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}


class LCA{
  public:
  int n,root;
  const int beki = 30;
  vector<vector<int>> v;
  vector<vector<int>> nxt;
  vector<int> d;
  LCA(int n,int root = 0) : n(n),root(root),v(n),nxt(n,vector<int>(beki,root)),d(n,-1) {}
  LCA(const vector<vector<int>> &v,int root) : n(v.size()),root(root),v(v),nxt(v.size(),vector<int>(beki,root)),d(n,-1) {}
  void add_edge(int x,int y){
    v[x].push_back(y);
    v[y].push_back(x);
  }
  void bfs(){
    queue<int> que;
    d[root] = 0;
    que.push(root);
    while(!que.empty()){
      int ov = que.front();que.pop();
      for(auto nv : v[ov]){
        if(d[nv] != -1)continue;
        nxt[nv][0] = ov;
        d[nv] = d[ov]+1;
        que.push(nv);
      }
    }
  }
  void build(){
    bfs();
    for(int i = 1;i < 30;i++)for(int j = 0;j < n;j++)nxt[j][i] = nxt[nxt[j][i-1]][i-1];
  }
  int get_lca(int x,int y){
    if(d[x] < d[y])swap(x,y);
    for(int i = 29;i >= 0;i--)if(((d[x]-d[y]) >> i) & 1)x = nxt[x][i];
    if(x == y)return x;
    for(int i = 29;i >= 0;i--)if(nxt[x][i] != nxt[y][i])x = nxt[x][i],y = nxt[y][i];
    return nxt[x][0];
  }
};

// https://beet-aizu.github.io/library/tree/auxiliarytree.cpp.html
struct AuxiliaryTree : LCA{
  vector<int> idx;
  vector<vector<int>> T;
  AuxiliaryTree(int n,int root = 0):LCA(n,root),idx(n),T(n){}

  void dfs(int ov,int pre,int &pos){
    idx[ov] = pos++;
    for(auto nv : v[ov]){
      if(nv != pre)dfs(nv,ov,pos);
    }
  }
  void build(int root = 0){
    LCA::build();
    int pos = 0;
    dfs(root,-1,pos);
  }
  void add_aux_edge(int a,int b){
    T[a].emplace_back(b);
    T[b].emplace_back(a);
  }
  // O(sz(vs)log(sz(vs)))
  void query(vector<int> &vs){
    assert(!vs.empty());
    sort(vs.begin(),vs.end(),[&](int a,int b){return idx[a] < idx[b];});
    vs.erase(unique(vs.begin(),vs.end()),vs.end());
    stack<int> st;
    st.emplace(vs[0]);
    int k = vs.size();
    for(int i = 1;i < k;i++){
      int w = get_lca(vs[i-1],vs[i]);
      if(w != vs[i-1]){
        int l = st.top();st.pop();
        while(!st.empty() && d[w] < d[st.top()]){
          add_aux_edge(st.top(),l);
          l = st.top();st.pop();
        }
        if(st.empty() || st.top() != w){
          st.emplace(w);
          vs.emplace_back(w);
        }
        add_aux_edge(w,l);
      }
      st.emplace(vs[i]);
    }
    while(st.size() > 1){
      int c = st.top();st.pop();
      add_aux_edge(st.top(),c);
    }
  }
  void clear(const vector<int> &ws){
    for(auto ov : ws)T[ov].clear();
  }
};


int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  int n;
  cin >> n;
  vi c(n);cin >> c;
  AuxiliaryTree at(n);
  rep(i,n-1){
    int a,b;cin >> a >> b;
    a--;b--;
    at.add_edge(a,b);
  }
  at.build();
  ve C(n);
  vi res(n,1ll << 50);
  rep(i,n)C[c[i]-1].emplace_back(i);
  vi ds(n),idx(n);
  auto dijk = [&](auto &vs,auto &ss){
    min_priority_queue<P> que;
    for(auto au : vs)ds[au] = 1ll << 50,idx[au] = -1;
    for(auto au : ss)ds[au] = 0,idx[au] = au,que.emplace(ds[au],au);
    while(!que.empty()){
      auto [od,ov] = que.top();que.pop();
      if(ds[ov] < od)continue;
      for(auto nv : at.T[ov]){
        int cst = abs(at.d[nv]-at.d[ov]);
        if(ds[nv] <= ds[ov] + cst)continue;
        ds[nv] = ds[ov] + cst;
        idx[nv] = idx[ov];
        que.emplace(ds[nv],nv);
      }
    }
  };
  rep(i,n){
    if(C[i].empty())continue;
    auto vs = C[i];
    at.query(vs);
    dijk(vs,C[i]);
    for(auto ov : vs){
      for(auto nv : at.T[ov]){
        if(idx[nv] == idx[ov])continue;
        chmin<ll>(res[idx[ov]],at.d[idx[nv]]+at.d[idx[ov]]-2*at.d[at.get_lca(idx[ov],idx[nv])]);
        chmin<ll>(res[idx[nv]],at.d[idx[nv]]+at.d[idx[ov]]-2*at.d[at.get_lca(idx[ov],idx[nv])]);
      }
    }
    at.clear(vs);
  }
  rep(i,n)cout << res[i] << "\n";


  

  return 0;
}
accuracy: 1
time_ms: 460
memory_kb: 74188
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T &a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T &a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
const double pi = acos(-1);
struct linear_sieve {
    vector<int> least_factor, prime_list;
    linear_sieve(int n) : least_factor(n + 1, 0) {
        for (int i = 2; i <= n; i++) {
            if (least_factor[i] == 0) {
                least_factor[i] = i;
                prime_list.push_back(i);
            }
            for (int p : prime_list) {
                if (ll(i) * p > n || p > least_factor[i]) break;
                least_factor[i * p] = p;
            }
        }
    }
};
ll extgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint &operator+=(const modint &p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator-=(const modint &p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator*=(const modint &p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint &operator/=(const modint &p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint &p) const { return modint(*this) += p; }
    modint operator-(const modint &p) const { return modint(*this) -= p; }
    modint operator*(const modint &p) const { return modint(*this) *= p; }
    modint operator/(const modint &p) const { return modint(*this) /= p; }
    bool operator==(const modint &p) const { return x == p.x; }
    bool operator!=(const modint &p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream &operator<<(ostream &os, const modint &p) { return os << p.x; }
    friend istream &operator>>(istream &is, modint &a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class T>
struct Matrix {
    int n;
    vector<vector<T>> m;
    Matrix() : Matrix(0) {}
    Matrix(int x) : Matrix(vector<vector<T>>(x, vector<T>(x, 0))) {}
    Matrix(const vector<vector<T>> &a) {
        n = a.size();
        m = a;
    }
    vector<T> &operator[](int i) { return m[i]; }
    const vector<T> &operator[](int i) const { return m[i]; }
    static Matrix identity(int x) {
        Matrix res(x);
        for (int i = 0; i < x; i++) res[i][i] = 1;
        return res;
    }
    Matrix operator+(const Matrix &a) const {
        Matrix x = (*this);
        return x += a;
    }
    Matrix operator*(const Matrix &a) const {
        Matrix x = (*this);
        return x *= a;
    }
    Matrix &operator+=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                res[i][j] = m[i][j] + a[i][j];
            }
        }
        m = res.m;
        return *this;
    }
    Matrix &operator*=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int k = 0; k < n; k++) {
                for (int j = 0; j < n; j++) {
                    res[i][j] += m[i][k] * a[k][j];
                }
            }
        }
        m = res.m;
        return *this;
    }
    Matrix pow(ll b) const {
        Matrix x = *this, res = identity(n);
        while (b) {
            if (b & 1) {
                res *= x;
            }
            x *= x;
            b >>= 1;
        }
        return res;
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S> &a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable() = default;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct disjointsparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    disjointsparsetable() = default;
    disjointsparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len, e()));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            int shift = 1 << i;
            for (int j = 0; j < (int)v.size(); j += shift << 1) {
                int t = min(j + shift, (int)v.size());
                table[i][t - 1] = v[t - 1];
                for (int k = t - 2; k >= j; k--) table[i][k] = op(v[k], table[i][k + 1]);
                if (v.size() <= t) break;
                table[i][t] = v[t];
                int r = min(t + shift, (int)v.size());
                for (int k = t + 1; k < r; k++) table[i][k] = op(table[i][k - 1], v[k]);
            }
        }
        logtable.resize(1 << len);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        if (l == r) return e();
        if (l >= --r) return table[0][l];
        int len = logtable[l ^ r];
        return op(table[len][l], table[len][r]);
    };
};
pair<int, int> lcatree_op(pair<int, int> a, pair<int, int> b) { return min(a, b); }
pair<int, int> lcatree_e() { return {1000000000, -1}; }
struct lca_tree {
    int n, size;
    vector<int> in, ord, depth;
    sparsetable<pair<int, int>, lcatree_op, lcatree_e> st;
    lca_tree(vector<vector<int>> g, int root = 0) : n((int)g.size()), size(log2(n) + 2), in(n), depth(n, n) {
        depth[root] = 0;
        function<void(int, int)> dfs = [&](int v, int p) {
            in[v] = (int)ord.size();
            ord.push_back(v);
            for (int u : g[v]) {
                if (u == p) continue;
                if (depth[u] > depth[v] + 1) {
                    depth[u] = depth[v] + 1;
                    dfs(u, v);
                    ord.push_back(v);
                }
            }
        };
        dfs(root, -1);
        vector<pair<int, int>> vec((int)ord.size());
        for (int i = 0; i < (int)ord.size(); i++) {
            vec[i] = make_pair(depth[ord[i]], ord[i]);
        }
        st = vec;
    }
    int lca(int u, int v) {
        if (in[u] > in[v]) swap(u, v);
        if (u == v) return u;
        return st.query(in[u], in[v]).second;
    }
    int dist(int u, int v) {
        int l = lca(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};

struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */ lr.emplace_back(l, r); }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL &add_left, const AR &add_right, const EL &erase_left, const ER &erase_right, const O &out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A &add, const E &erase, const O &out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};

template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
struct auxiliary_tree : lca_tree {
    vector<vector<int>> G;
    auxiliary_tree(vector<vector<int>> &g) : lca_tree(g), G(n) {}
    pair<int, vector<int>> query(vector<int> vs, bool decending = false) {
        // decending:親から子の方向のみ辺を貼る
        assert(!vs.empty());
        sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
        int m = vs.size();
        stack<int> st;
        st.push(vs[0]);
        for (int i = 0; i < m - 1; i++) {
            int w = lca(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() && depth[w] < depth[st.top()]) {
                    if (!decending) G[l].push_back(st.top());
                    G[st.top()].push_back(l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() || st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                if (!decending) G[l].push_back(w);
                G[w].push_back(l);
            }
            st.push(vs[i + 1]);
        }
        while (st.size() > 1) {
            int x = st.top();
            st.pop();
            if (!decending) G[x].push_back(st.top());
            G[st.top()].push_back(x);
        }
        // {root,vertex_list}
        return make_pair(st.top(), vs);
    }
    void clear(vector<int> vs) {
        for (int v : vs) G[v].clear();
    }
};
void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    rep(i, n) {
        cin >> a[i];
        a[i]--;
    }
    vector<vector<int>> g(n);
    rep(i, n - 1) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    auxiliary_tree aux_tree(g);
    vector<vector<int>> vec(n);
    rep(i, n) vec[a[i]].push_back(i);
    vector<int> dist(n), idx(n);
    vector<int> ans(n, 1000000000);
    rep(color, n) {
        if (vec[color].empty()) continue;
        auto [root, vs] = aux_tree.query(vec[color]);
        using P = pair<int, int>;
        priority_queue<P, vector<P>, greater<P>> que;
        for (auto v : vs) dist[v] = n, idx[v] = -1;
        for (int s : vec[color]) dist[s] = 0, idx[s] = s, que.push({dist[s], s});
        while (!que.empty()) {
            auto [d, v] = que.top();
            que.pop();
            if (dist[v] != d) continue;
            for (auto u : aux_tree.G[v]) {
                int c = abs(aux_tree.depth[u] - aux_tree.depth[v]);
                if (chmin(dist[u], d + c)) {
                    idx[u] = idx[v];
                    que.push({dist[u], u});
                }
            }
        }
        for (int v : vs) {
            for (int u : aux_tree.G[v]) {
                if (idx[v] == idx[u]) continue;
                chmin(ans[idx[v]], aux_tree.dist(idx[v], idx[u]));
                chmin(ans[idx[u]], aux_tree.dist(idx[v], idx[u]));
            }
        }
        aux_tree.clear(vs);
    }
    rep(i, n) cout << ans[i] << "\n";
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    /*int t;
    cin >> t;
    while (t--) */
    solve();
}
accuracy: 1
time_ms: 220
memory_kb: 123088
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (ll i = s; i < (ll)(t); i++)
#define rrep(i, s, t) for(ll i = (ll)(t) - 1; i >= (ll)(s); i--)
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)

#define TT template<typename T>
TT using vec = vector<T>;
TT bool chmin(T &x, T y) { return x > y ? (x = y, true) : false; }
TT bool chmax(T &x, T y) { return x < y ? (x = y, true) : false; }

const int inf = 1001001001;

struct io_setup {
    io_setup() {
        ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        cout << fixed << setprecision(15);
    }
} io_setup;


struct HLD {
    using vi = vec<int>;
    using pi = pair<int, int>;
    vi in, out, par, root, rev, dep;
    //          親/成分のtop/inの中身→頂点番号
    int n, r;//頂点数、根
    
  
    HLD(vec<vi> &g, int a): n(g.size()), r(a) {
        vi siz(n, 0);
        in = out = root = rev = vi(n);
        par = vi(n, -1);
        dep = vi(n, 0);
        root[r] = r;

        auto dfs_siz = [&](auto f, int v) -> void {
            siz[v]++;
            for(int& to : g[v]) if(to != par[v]) {
                dep[to] = dep[v] + 1;
                par[to] = v;
                f(f, to);
                siz[v] += siz[to];
                if(siz[to] > siz[g[v][0]] || g[v][0] == par[v]) swap(to, g[v][0]);
            }
            return;
        };

        dfs_siz(dfs_siz, r);
        
        int t = 0;

        auto dfs_hld = [&](auto f, int v) -> void {
            rev[t] = v;
            in[v]=t++;
            for(int to : g[v]) if(to != par[v]) {
                root[to] = (to == g[v][0] ? root[v] : to);
                f(f, to);
            }
            out[v] = t;
        };

        dfs_hld(dfs_hld, r);
    }


    //以下、欲しいもののみ書く


    int lca(int a, int b) {
        while(1) {
            if(in[a] > in[b]) swap(a, b);
            if(root[a] == root[b]) return a;
            b = par[root[b]];
        }
    }

    int dist(int a, int b) {
        int lc = lca(a, b);
        return dep[a] + dep[b] - 2 * dep[lc];
    }

    vec<pi> path(int s, int t, bool edge) {
        vec<pi> ls, rs;
        while(root[s] != root[t]) {
            if(dep[root[s]] > dep[root[t]]) {
                ls.emplace_back(in[s] + 1, in[root[s]]);//上り
                s = par[root[s]];
            }
            else {
                rs.emplace_back(in[root[t]], in[t] + 1);//下り
                t = par[root[t]];
            }
        }

        if(dep[s] > dep[t]) ls.emplace_back(in[s] + 1, in[t] + edge);//上り
        else rs.emplace_back(in[s] + edge, in[t] + 1);//下り
        
        reverse(all(rs));
        for(auto &p : rs) ls.push_back(p);
        return ls;
    }

    pi subtree(int u, bool edge) {
        return pi(in[u]+edge, out[u]);
    }

    int kth_ancestor(int v, int k) {
        if(k > dep[v]) return -1;
        while(v >= 0) {
            if(k <= dep[v] - dep[root[v]]) {
                return rev[in[v] - k];
            }
            else {
                k -= dep[v] - dep[root[v]] + 1;
                v = par[root[v]];
            }
        }
    }

    int jump(int s, int t, int k) {
        int m = lca(s, t);
        int le = dep[s] - dep[m];
        int ri = dep[t] - dep[m];
        if(0 <= k && k <= le + ri) {
            if(k < le) return kth_ancestor(s, k);
            else return kth_ancestor(t, le + ri - k);
        }
        return -1;
    }


    vi pre_vs;

    int aux_tree(vi vs, vec<vi> &g) {
        if(vs.empty()) return -1;
        
        auto cmp = [&](int i, int j) { return in[i] < in[j]; };
        sort(all(vs), cmp);
        int m = vs.size();

        rep(i, 0, m-1) vs.push_back(lca(vs[i], vs[i + 1]));
        sort(all(vs), cmp);
        vs.erase(unique(all(vs)), vs.end());

        vi st;
        for(auto v : vs) {
            while(st.size()) {
                int p = st.back();
                if(in[p] < in[v] && in[v] < out[p]) break;
                st.pop_back();
            }
            if(st.size()) {
                g[st.back()].push_back(v);
                g[v].push_back(st.back());
            }
            st.push_back(v);
        }

        swap(vs, pre_vs);
        return pre_vs[0];
    }

    void clean(vec<vi> &g) {
        for(auto v : pre_vs) g[v] = vi();
        pre_vs = vi();
        return;
    }
};  


void solve() {
    int n;
    cin >> n;
    vec<int> C(n);
    rep(i, 0, n) cin >> C[i], C[i]--;
    vec<vec<int>> G(n);
    rep(i, 0, n-1) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        G[u].push_back(v);
        G[v].push_back(u);
    }

    vec<vec<int>> dp(n);
    vec<int> ans(n, inf);
    HLD hld(G, 0);

    vec<vec<int>> cv(n);
    rep(i, 0, n) cv[C[i]].push_back(i);

    vec<vec<int>> g(n);

    rep(c, 0, n) {
        int r = hld.aux_tree(cv[c], g);
        if(r == -1) continue;

        auto dfs = [&](auto f, int v, int p) -> int {
            int res = inf;
            int d = g[v].size();
            dp[v].resize(d);
            
            rep(i, 0, d) {
                int to = g[v][i];
                if(to == p) continue;
                dp[v][i] = f(f, to, v);
                int nw = dp[v][i] + hld.dist(v, to);//ここで到達済み
                chmin(res, nw);
            }
            //res++;
            if(C[v] == c) res = 0;
            return res;
        };

        auto bfs = [&](auto f, int v,  int par, int p) -> void {
            int d =  g[v].size();
            rep(i, 0, d) if(g[v][i] == p) dp[v][i] = par;

            vec<int> L(d+1, inf);
            vec<int> R(d+1, inf);
            rep(i, 0, d) {
                int nw = dp[v][i] + hld.dist(v, g[v][i]);
                L[i+1] = min(L[i], nw);
            }

            rrep(i, 0, d) {
                int nw = dp[v][i] + hld.dist(v, g[v][i]);
                R[i] = min(R[i+1], nw);
            }

            if(C[v] == c) {
                ans[v] = L[d];
            }

            rep(i, 0, d) {
                int to = g[v][i];
                if(to == p) continue;
                int nx = min(L[i], R[i+1]);
                if(C[v] == c) nx = 0;
                f(f, to, nx, v);
            }
        };

        dfs(dfs, r, -1);
        bfs(bfs, r, inf, -1);
        hld.clean(g);
    }

    rep(i, 0, n) cout << ans[i] << '\n';
        

}


int main() {
    int T = 1;
    //cin >> T;
    while(T--) {
        solve();
    }

}
accuracy: 1
time_ms: 210
memory_kb: 50812
final_rank: 4

code_snippet: #include <bits/stdc++.h>
#define REP(i,n)   for(int i=0;i<(int)(n);++i)
#define FOR(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define ALL(v)     (v).begin(),(v).end()
using llong = long long;
using vi    = std::vector<int>;
using vvi   = std::vector<vi>;
using pii   = std::pair<int,int>;
using namespace std;
constexpr int       INF  = 1e9;
constexpr long long LINF = 1e18;
constexpr double    EPS  = 1e-10;
constexpr int       MOD  = 998'244'353;
constexpr int       MOD2 = 1e9+7;

template <typename Type>
inline std::istream &operator>>(std::istream &is, std::vector<Type> &v) {
    for(Type &in : v) is >> in;
    return is;
}

template <typename Type>
inline std::ostream &operator<<(std::ostream &os, const std::vector<Type> &v) {
    for(auto itr = v.cbegin(); itr != v.cend(); ++itr) os << (itr == v.cbegin() ? "" : " ") << *itr;
    return os;
}

#ifdef DEBUG

#include "debug.hpp"
using namespace algorithm;

#else

#define debug(...) static_cast<void>(0)

#endif

// Lowest Common Ancestor（最近共通祖先）.
template <typename T = int>  // T:Type of cost.
class LCA {
    int m_vn;                                           // m_vn:=(ノード数).
    int m_lb;                                           // m_lb:=ceiling(log2(vn)).
    std::vector<std::vector<std::pair<int, T> > > m_g;  // m_g[v][]:=(ノードvの隣接リスト). pair of (to, cost). グラフは木であること．
    std::vector<std::vector<int> > m_par;               // m_par[k][v]:=(ノードvから2^k回辿って到達する親ノード番号). 親がいない場合は-1．
    std::vector<int> m_depth;                           // m_depth[v]:=(ノードvの深さ). 根に連結していない場合は-1．
    std::vector<T> m_dist;                              // m_dist[v]:=(根からノードvまでの距離).
    std::vector<int> m_ord;                             // m_ord[v]:=(DFS木におけるノードvの行きかけ順序).

    bool is_unconnected(int v) const { return m_ord[v] == -1; }
    void bottom_up(int u, int p, int depth, T distance, int &now) {
        assert(m_ord[u] == -1);  // グラフ上に閉路はないこと．
        m_ord[u] = now++;
        m_par[0][u] = p;
        m_depth[u] = depth;
        m_dist[u] = distance;
        for(const auto &[v, cost] : m_g[u]) {
            if(v == p) continue;
            bottom_up(v, u, depth + 1, distance + cost, now);
        }
    }

public:
    LCA() : LCA(0) {}
    explicit LCA(size_t vn) : m_vn(vn), m_lb(1), m_g(vn), m_depth(vn, -1), m_dist(vn, infinity()), m_ord(vn, -1) {
        while(1 << m_lb < order()) m_lb++;
        m_par.assign(m_lb, std::vector<int>(order(), -1));
    }

    static constexpr T infinity() { return std::numeric_limits<T>::max(); }
    // ノード数を返す．
    int order() const { return m_vn; }
    // 無向辺を張る．ノードuとvは非連結であること．
    void add_edge(int u, int v, T cost = 0) {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        m_g[u].emplace_back(v, cost);
        m_g[v].emplace_back(u, cost);
    }
    // 祖先木を構築する．O(|V|*log|V|).
    void build(int rt = 0) {
        assert(0 <= rt and rt < order());
        for(std::vector<int> &v : m_par) std::fill(v.begin(), v.end(), -1);
        std::fill(m_depth.begin(), m_depth.end(), -1);
        std::fill(m_dist.begin(), m_dist.end(), infinity());
        std::fill(m_ord.begin(), m_ord.end(), -1);
        int now = 0;
        bottom_up(rt, -1, 0, 0, now);
        for(int k = 0; k < m_lb - 1; ++k) {
            for(int v = 0; v < order(); ++v) {
                if(m_par[k][v] != -1) m_par[k + 1][v] = m_par[k][m_par[k][v]];
            }
        }
    }
    // ノードvの祖先をk代遡る．O(log|V|).
    int trace_back(int v, int k) const {
        assert(0 <= v and v < order());
        assert(0 <= k and k < order());
        if(k > m_depth[v]) return -1;
        for(int i = 0; i < m_lb; ++i) {
            if(k >> i & 1) v = m_par[i][v];
        }
        return v;
    }
    // 木上のノードuとvの最も近い共通の祖先を求める．O(log|V|).
    int lca(int u, int v) const {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        if(is_unconnected(u) or is_unconnected(v)) return -1;
        if(m_depth[u] > m_depth[v]) std::swap(u, v);
        v = trace_back(v, m_depth[v] - m_depth[u]);  // 同じ深さに合わせる．
        if(u == v) return u;
        for(int k = m_lb - 1; k >= 0; --k) {
            if(m_par[k][u] != m_par[k][v]) u = m_par[k][u], v = m_par[k][v];  // 異なったら根に近づける．
        }
        return m_par[0][u];
    }
    // ノードvの深さを返す．O(1).
    T depth(int v) const {
        assert(0 <= v and v < order());
        return m_depth[v];
    }
    // ノードu, v間の長さを求める．O(log|V|).
    int length(int u, int v) const {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        if(is_unconnected(u) or is_unconnected(v)) return -1;
        return m_depth[u] + m_depth[v] - 2 * m_depth[lca(u, v)];
    }
    // 根とノードv間の距離を返す．O(1).
    T distance(int v) const {
        assert(0 <= v and v < order());
        return m_dist[v];
    }
    // ノードu, v間の距離を求める．O(log|V|).
    T distance(int u, int v) const {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        if(is_unconnected(u) or is_unconnected(v)) return infinity();
        return m_dist[u] + m_dist[v] - 2 * m_dist[lca(u, v)];
    }
    // 木の圧縮．
    // 任意の頂点集合とそのLCAからなる，頂点同士の関係性を維持した木を作成する．O(K*log|V|).
    std::pair<int, std::map<int, std::vector<int> > > auxiliary_tree(std::vector<int> &vs) const {
        assert(std::find_if(vs.begin(), vs.end(), [&](int v) -> bool { return !(0 <= v and v < order()); }) == vs.end());
        std::map<int, std::vector<int> > res;  // res[v][]:=(圧縮した木におけるノードvの隣接リスト).
        vs.erase(std::remove_if(vs.begin(), vs.end(), [&](int v) -> bool { return is_unconnected(v); }), vs.end());
        auto comp = [&](int u, int v) -> bool { return m_ord[u] < m_ord[v]; };
        std::sort(vs.begin(), vs.end(), comp);
        vs.erase(std::unique(vs.begin(), vs.end()), vs.end());
        const int n = vs.size();
        if(n == 0) return {-1, res};
        if(n == 1) {
            res[vs[0]];
            return {vs[0], res};
        }
        std::stack<int> st;
        st.push(vs[0]);
        for(int i = 1; i < n; ++i) {
            int w = lca(vs[i - 1], vs[i]);
            if(w != vs[i - 1]) {
                int v = st.top();
                st.pop();
                while(!st.empty() and m_depth[st.top()] > m_depth[w]) {
                    res[st.top()].push_back(v);
                    res[v].push_back(st.top());
                    v = st.top();
                    st.pop();
                }
                if(st.empty() or st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                res[w].push_back(v);
                res[v].push_back(w);
            }
            st.push(vs[i]);
        }
        while(st.size() > 1) {
            int v = st.top();
            st.pop();
            res[st.top()].push_back(v);
            res[v].push_back(st.top());
        }
        std::sort(vs.begin(), vs.end(), comp);
        return {st.top(), res};  // pair of (root, tree).
    }
};

template<typename Type> inline bool chmin(Type &a,const Type &b){if(a>b){a=b;return true;}return false;}
template<typename Type> inline bool chmax(Type &a,const Type &b){if(a<b){a=b;return true;}return false;}

int main(){
    int n;
    cin>>n;

    vi c(n);
    vvi inv(n);
    REP(i,n){
        cin>>c[i];
        c[i]--;
        inv[c[i]].push_back(i);
    }

    LCA lca(n);
    REP(i,n-1){
        int s,t;
        cin>>s>>t;
        s--, t--;
        lca.add_edge(s,t);
    }
    lca.build();

    vi ans(n,INF);
    REP(i,n){
        if(inv[i].size()==0) continue;
        const auto &&[rt,tree]=lca.auxiliary_tree(inv[i]);
        debug(i,rt,inv[i],tree);

        map<int,vector<pii> > mp;
        for(auto v:inv[i]){
            auto &neighbors=mp[v];
            if(c[v]==i){
                neighbors.emplace_back(0,v);
                neighbors.emplace_back(INF,-1);
            }else{
                neighbors.emplace_back(INF,-1);
                neighbors.emplace_back(INF,-1);
            }
        }
        
        auto bottom_up=[&](auto self,int u,int p)->pii{
            auto &neighbors=mp[u];
            for(auto v:tree.at(u)){
                if(v==p) continue;
                const auto &&[d,w]=self(self,v,u);
                auto len=lca.length(u,v);
                chmin(neighbors[1],{d+len,w});
                if(neighbors[0]>neighbors[1]) swap(neighbors[0],neighbors[1]);
            }
            return neighbors[0];
        };
        bottom_up(bottom_up,rt,-1);

        auto top_down=[&](auto self,int u,int p)->void{
            const auto &neighbors_u=mp[u];
            for(auto v:tree.at(u)){
                if(v==p) continue;
                auto &neighbors_v=mp[v];
                auto len=lca.length(u,v);
                if(neighbors_u[0].second==neighbors_v[0].second) chmin(neighbors_v[1],{neighbors_u[1].first+len,neighbors_u[1].second});
                else chmin(neighbors_v[1],{neighbors_u[0].first+len,neighbors_u[0].second});
                if(neighbors_v[0]>neighbors_v[1]) swap(neighbors_v[0],neighbors_v[1]);
                self(self,v,u);
            }
        };
        top_down(top_down,rt,-1);

        for(const auto &[u,neighbors]:mp){
            if(c[u]==i){
                const auto &[d,v]=(neighbors[0].second==u?neighbors[1]:neighbors[0]);
                debug(u,v,d);
                ans[u]=d;
            }
        }
    }

    for(auto out:ans) cout<<out<<"\n";
}
accuracy: 1
time_ms: 590
memory_kb: 47192
final_rank: 5

code_snippet: #include <bits/stdc++.h>
#define REP(i,n)   for(int i=0;i<(int)(n);++i)
#define FOR(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define ALL(v)     (v).begin(),(v).end()
using llong = long long;
using vi    = std::vector<int>;
using vvi   = std::vector<vi>;
using pii   = std::pair<int,int>;
using namespace std;
constexpr int       INF  = 1e9;
constexpr long long LINF = 1e18;
constexpr double    EPS  = 1e-10;
constexpr int       MOD  = 998'244'353;
constexpr int       MOD2 = 1e9+7;

template <typename Type>
inline std::istream &operator>>(std::istream &is, std::vector<Type> &v) {
    for(Type &in : v) is >> in;
    return is;
}

template <typename Type>
inline std::ostream &operator<<(std::ostream &os, const std::vector<Type> &v) {
    for(auto itr = v.cbegin(); itr != v.cend(); ++itr) os << (itr == v.cbegin() ? "" : " ") << *itr;
    return os;
}

#ifdef DEBUG

#include "debug.hpp"
using namespace algorithm;

#else

#define debug(...) static_cast<void>(0)

#endif

// Lowest Common Ancestor（最近共通祖先）.
template <typename T = int>  // T:Type of cost.
class LCA {
    int m_vn;                                           // m_vn:=(ノード数).
    int m_lb;                                           // m_lb:=ceiling(log2(vn)).
    std::vector<std::vector<std::pair<int, T> > > m_g;  // m_g[v][]:=(ノードvの隣接リスト). pair of (to, cost). グラフは木であること．
    std::vector<std::vector<int> > m_par;               // m_par[k][v]:=(ノードvから2^k回辿って到達する親ノード番号). 親がいない場合は-1．
    std::vector<int> m_depth;                           // m_depth[v]:=(ノードvの深さ). 根に連結していない場合は-1．
    std::vector<T> m_dist;                              // m_dist[v]:=(根からノードvまでの距離).
    std::vector<int> m_ord;                             // m_ord[v]:=(DFS木におけるノードvの行きかけ順序).

    bool is_unconnected(int v) const { return m_ord[v] == -1; }
    void bottom_up(int u, int p, int depth, T distance, int &now) {
        assert(m_ord[u] == -1);  // グラフ上に閉路はないこと．
        m_ord[u] = now++;
        m_par[0][u] = p;
        m_depth[u] = depth;
        m_dist[u] = distance;
        for(const auto &[v, cost] : m_g[u]) {
            if(v == p) continue;
            bottom_up(v, u, depth + 1, distance + cost, now);
        }
    }

public:
    LCA() : LCA(0) {}
    explicit LCA(size_t vn) : m_vn(vn), m_lb(1), m_g(vn), m_depth(vn, -1), m_dist(vn, infinity()), m_ord(vn, -1) {
        while(1 << m_lb < order()) m_lb++;
        m_par.assign(m_lb, std::vector<int>(order(), -1));
    }

    static constexpr T infinity() { return std::numeric_limits<T>::max(); }
    // ノード数を返す．
    int order() const { return m_vn; }
    // 無向辺を張る．ノードuとvは非連結であること．
    void add_edge(int u, int v, T cost = 0) {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        m_g[u].emplace_back(v, cost);
        m_g[v].emplace_back(u, cost);
    }
    // 祖先木を構築する．O(|V|*log|V|).
    void build(int rt = 0) {
        assert(0 <= rt and rt < order());
        for(std::vector<int> &v : m_par) std::fill(v.begin(), v.end(), -1);
        std::fill(m_depth.begin(), m_depth.end(), -1);
        std::fill(m_dist.begin(), m_dist.end(), infinity());
        std::fill(m_ord.begin(), m_ord.end(), -1);
        int now = 0;
        bottom_up(rt, -1, 0, 0, now);
        for(int k = 0; k < m_lb - 1; ++k) {
            for(int v = 0; v < order(); ++v) {
                if(m_par[k][v] != -1) m_par[k + 1][v] = m_par[k][m_par[k][v]];
            }
        }
    }
    // ノードvの祖先をk代遡る．O(log|V|).
    int trace_back(int v, int k) const {
        assert(0 <= v and v < order());
        assert(0 <= k and k < order());
        if(k > m_depth[v]) return -1;
        for(int i = 0; i < m_lb; ++i) {
            if(k >> i & 1) v = m_par[i][v];
        }
        return v;
    }
    // 木上のノードuとvの最も近い共通の祖先を求める．O(log|V|).
    int lca(int u, int v) const {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        if(is_unconnected(u) or is_unconnected(v)) return -1;
        if(m_depth[u] > m_depth[v]) std::swap(u, v);
        v = trace_back(v, m_depth[v] - m_depth[u]);  // 同じ深さに合わせる．
        if(u == v) return u;
        for(int k = m_lb - 1; k >= 0; --k) {
            if(m_par[k][u] != m_par[k][v]) u = m_par[k][u], v = m_par[k][v];  // 異なったら根に近づける．
        }
        return m_par[0][u];
    }
    // ノードvの深さを返す．O(1).
    T depth(int v) const {
        assert(0 <= v and v < order());
        return m_depth[v];
    }
    // ノードu, v間の長さを求める．O(log|V|).
    int length(int u, int v) const {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        if(is_unconnected(u) or is_unconnected(v)) return -1;
        return m_depth[u] + m_depth[v] - 2 * m_depth[lca(u, v)];
    }
    // 根とノードv間の距離を返す．O(1).
    T distance(int v) const {
        assert(0 <= v and v < order());
        return m_dist[v];
    }
    // ノードu, v間の距離を求める．O(log|V|).
    T distance(int u, int v) const {
        assert(0 <= u and u < order());
        assert(0 <= v and v < order());
        if(is_unconnected(u) or is_unconnected(v)) return infinity();
        return m_dist[u] + m_dist[v] - 2 * m_dist[lca(u, v)];
    }
    // 木の圧縮．
    // 任意の頂点集合とそのLCAからなる，頂点同士の関係性を維持した木を作成する．O(K*log|V|).
    std::pair<int, std::map<int, std::vector<int> > > auxiliary_tree(std::vector<int> &vs) const {
        assert(std::find_if(vs.begin(), vs.end(), [&](int v) -> bool { return !(0 <= v and v < order()); }) == vs.end());
        std::map<int, std::vector<int> > res;  // res[v][]:=(圧縮した木におけるノードvの隣接リスト).
        vs.erase(std::remove_if(vs.begin(), vs.end(), [&](int v) -> bool { return is_unconnected(v); }), vs.end());
        auto comp = [&](int u, int v) -> bool { return m_ord[u] < m_ord[v]; };
        std::sort(vs.begin(), vs.end(), comp);
        vs.erase(std::unique(vs.begin(), vs.end()), vs.end());
        const int n = vs.size();
        if(n == 0) return {-1, res};
        if(n == 1) {
            res[vs[0]];
            return {vs[0], res};
        }
        std::stack<int> st;
        st.push(vs[0]);
        for(int i = 1; i < n; ++i) {
            int w = lca(vs[i - 1], vs[i]);
            if(w != vs[i - 1]) {
                int v = st.top();
                st.pop();
                while(!st.empty() and m_depth[st.top()] > m_depth[w]) {
                    res[st.top()].push_back(v);
                    res[v].push_back(st.top());
                    v = st.top();
                    st.pop();
                }
                if(st.empty() or st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                res[w].push_back(v);
                res[v].push_back(w);
            }
            st.push(vs[i]);
        }
        while(st.size() > 1) {
            int v = st.top();
            st.pop();
            res[st.top()].push_back(v);
            res[v].push_back(st.top());
        }
        std::sort(vs.begin(), vs.end(), comp);
        return {st.top(), res};  // pair of (root, tree).
    }
};

template<typename Type> inline bool chmin(Type &a,const Type &b){if(a>b){a=b;return true;}return false;}
template<typename Type> inline bool chmax(Type &a,const Type &b){if(a<b){a=b;return true;}return false;}

int main(){
    int n;
    cin>>n;

    vi c(n);
    vvi inv(n);
    REP(i,n){
        cin>>c[i];
        c[i]--;
        inv[c[i]].push_back(i);
    }

    LCA lca(n);
    REP(i,n-1){
        int s,t;
        cin>>s>>t;
        s--, t--;
        lca.add_edge(s,t);
    }
    lca.build();

    vi ans(n,INF);
    REP(i,n){
        auto vs=inv[i];
        const auto &&[rt,tree]=lca.auxiliary_tree(vs);
        debug(i,rt,vs);
        if(vs.size()==0) continue;

        map<int,vector<pii> > mp;
        for(auto v:vs){
            auto &dist=mp[v];
            if(c[v]==i){
                dist.emplace_back(0,v);
                dist.emplace_back(INF,-1);
            }else{
                dist.emplace_back(INF,-1);
                dist.emplace_back(INF,-1);
            }
        }
        
        auto bottom_up=[&](auto self,int u,int p)->pii{
            auto &dist_u=mp[u];
            for(auto v:tree.at(u)){
                if(v==p) continue;
                auto [d,w]=self(self,v,u);
                auto len=lca.length(u,v);
                chmin(dist_u[1],{d+len,w});
                if(dist_u[0]>dist_u[1]) swap(dist_u[0],dist_u[1]);
            }
            return dist_u[0];
        };
        bottom_up(bottom_up,rt,-1);

        auto top_down=[&](auto self,int u,int p)->void{
            const auto &dist_u=mp[u];
            for(auto v:tree.at(u)){
                if(v==p) continue;
                auto &dist_v=mp[v];
                auto len=lca.length(u,v);
                if(dist_u[0].second==dist_v[0].second) chmin(dist_v[1],{dist_u[1].first+len,dist_u[1].second});
                else chmin(dist_v[1],{dist_u[0].first+len,dist_u[0].second});
                if(dist_v[0]>dist_v[1]) swap(dist_v[0],dist_v[1]);
                self(self,v,u);
            }
        };
        top_down(top_down,rt,-1);

        for(const auto &[u,dist]:mp){
            const auto &[d,v]=(dist[0].second==u?dist[1]:dist[0]);
            debug(u,v,d,lca.length(u,v));
            assert(d==lca.length(u,v));
            if(c[u]==i) ans[u]=d;
        }
    }

    for(auto out:ans) cout<<out<<"\n";
}
accuracy: 1
time_ms: 630
memory_kb: 46348
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;

template<typename T=int> // T: type of cost
struct lca_tree {
  int n, root, l;
  vector<vector<int>> to;
  vector<vector<T>> co;
  vector<int> dep;
  vector<T> costs;
  vector<vector<int>> par;
  lca_tree(int n):n(n),to(n),co(n),dep(n),costs(n) {
    l = 0;
    while ((1<<l) < n) ++l;
    par = vector<vector<int>>(n+1,vector<int>(l,n));
  }
  void add_edge(int a, int b, T c=0) {
    to[a].push_back(b); co[a].push_back(c);
    to[b].push_back(a); co[b].push_back(c);
  }
  void dfs(int v, int d=0, T c=0, int p=-1) {
    if (p != -1) par[v][0] = p;
    dep[v] = d;
    costs[v] = c;
    for (int i = 0; i < to[v].size(); ++i) {
      int u = to[v][i];
      if (u == p) continue;
      dfs(u, d+1, c+co[v][i], v);
    }
  }
  void init(int _root=0) {
    root = _root;
    dfs(root);
    for (int i = 0; i < l-1; ++i) {
      for (int v = 0; v < n; ++v) {
        par[v][i+1] = par[par[v][i]][i];
      }
    }
  }
  // LCA
  int up(int v, int k) {
    for (int i = l-1; i >= 0; --i) {
      int len = 1<<i;
      if (k >= len) k -= len, v = par[v][i];
    }
    return v;
  }
  int lca(int a, int b) {
    if (dep[a] > dep[b]) swap(a,b);
    b = up(b, dep[b]-dep[a]);
    if (a == b) return a;
    for (int i = l-1; i >= 0; --i) {
      int na = par[a][i], nb = par[b][i];
      if (na != nb) a = na, b = nb;
    }
    return par[a][0];
  }
  int length(int a, int b) {
    int c = lca(a,b);
    return dep[a]+dep[b]-dep[c]*2;
  }
  T dist(int a, int b) {
    int c = lca(a,b);
    return costs[a]+costs[b]-costs[c]*2;
  }
};

int main() {
  int N;
  cin >> N;
  lca_tree G(N);
  vector c(N, int());
  vector vc(N, vector<int>());
  for(int i = 0; i < N; i++) {
    cin >> c[i];
    vc[--c[i]].push_back(i);
  }
  for(int i = 0; i < N-1; i++) {
    int s, t;
    cin >> s >> t;
    G.add_edge(--s, --t, 1);
  }
  G.init();
  // pre-orderを計算する
  vector<int> po(N);
  {
    int tim = 0;
    auto dfs = [&](auto dfs, int cur, int par) -> void {
      po[cur] = tim++;
      for(int nxt : G.to[cur]) {
        if(nxt == par) continue;
        dfs(dfs, nxt, cur);
      }
    };
    dfs(dfs, 0, -1);
  }
  vector<int> ans(N, N);
  for(int target = 0; target < N; target++) {
    // auxiliary treeを構築する
    vector<int> v = vc[target];
    if(v.empty()) continue;
    sort(v.begin(), v.end(), [&](int l, int r){return po[l] < po[r];});
    for(int i = 0, sz = int(v.size()); i < sz-1; i++) {
      v.push_back(G.lca(v[i], v[i+1]));
    }
    sort(v.begin(), v.end(), [&](int l, int r){return po[l] < po[r];});
    v.erase(unique(v.begin(), v.end()), v.end());
    unordered_map<int, vector<int>> aux;
    for(int i = 0, sz = int(v.size()); i < sz-1; i++) {
      int lca = G.lca(v[i], v[i+1]);
      aux[lca].push_back(v[i+1]);
      aux[v[i+1]].push_back(lca);
    }
    // auxiliary tree上でdpをする
    unordered_map<int, vector<int>> child;
    auto dfs = [&](auto dfs, int cur, int par) -> void {
      // 各子孫の部分木に対して、最も近い(色targetの)頂点を求める
      vector<pair<int, int>> min_dist;
      for(int nxt : aux[cur]) {
        if(nxt == par) continue;
        dfs(dfs, nxt, cur);
        min_dist.push_back(make_pair(N, nxt));
        for(int v : child[nxt]) {
          min_dist.back() = min(min_dist.back(), make_pair(G.dist(v, cur), nxt));
        }
      }
      sort(min_dist.begin(), min_dist.end());
      child[cur] = (c[cur] == target ? vector<int>{cur} : vector<int>{});
      for(int nxt : aux[cur]) {
        if(nxt == par) continue;
        if(c[cur] == target) {
          for(int v : child[nxt]) {
            ans[v] = min(ans[v], G.dist(v, cur));
            ans[cur] = min(ans[cur], G.dist(v, cur));
          }
        }else {
          for(int v : child[nxt]) {
            if(min_dist[0].second != nxt) {
              ans[v] = min(ans[v], G.dist(v, cur)+min_dist[0].first);
            }else if(int(min_dist.size()) >= 2) {
              ans[v] = min(ans[v], G.dist(v, cur)+min_dist[1].first);
            }
          }
          if(int(child[cur].size()) < int(child[nxt].size())) swap(child[cur], child[nxt]);
          child[cur].insert(child[cur].end(), child[nxt].begin(), child[nxt].end());
        }
      }
    };
    dfs(dfs, v[0], -1);
  }
  for(int i = 0; i < N; i++) {
    cout << ans[i] << "\n";
  }
}
accuracy: 1
time_ms: 820
memory_kb: 62500
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T& a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T& a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
constexpr double eps = 1e-10;
const double pi = acos(-1);
pair<vector<int>, vector<int>> calc_factor(int n) {
    vector<int> least_factor(n + 1, 0), prime_list;
    for (int i = 2; i <= n; i++) {
        if (least_factor[i] == 0) {
            least_factor[i] = i;
            prime_list.push_back(i);
        }
        for (int j = 0; j < (int)prime_list.size() && i * prime_list[j] <= n; j++) {
            least_factor[i * prime_list[j]] = prime_list[j];
            if (prime_list[j] == least_factor[i]) break;
        }
    }
    return make_pair(least_factor, prime_list);
}
ll extgcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint& operator+=(const modint& p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint& operator-=(const modint& p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint& operator*=(const modint& p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint& operator/=(const modint& p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint& p) const { return modint(*this) += p; }
    modint operator-(const modint& p) const { return modint(*this) -= p; }
    modint operator*(const modint& p) const { return modint(*this) *= p; }
    modint operator/(const modint& p) const { return modint(*this) /= p; }
    bool operator==(const modint& p) const { return x == p.x; }
    bool operator!=(const modint& p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream& operator<<(ostream& os, const modint& p) { return os << p.x; }
    friend istream& operator>>(istream& is, modint& a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        assert(n > 0);
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S>& a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
using mint = modint<998244353>;
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};

struct lca_tree {
    int n, size;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree(vector<vector<int>>& g, int root = 0) : n((int)g.size()), size(log2(n) + 2), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
    int parent(int u, int d) {
        for (int k = size - 1; k >= 0; k--) {
            if ((d >> k) & 1) {
                if (u != -1) u = par[k][u];
            }
        }
        return u;
    }
};

struct lca_tree_with_edge_ID {
    int n, size;
    vector<vector<pair<int, int>>> g;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree_with_edge_ID(vector<vector<pair<int, int>>> g_, int root = 0)
        : n((int)g_.size()), size(log2(n) + 2), g(g_), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (auto [i, id] : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};

struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */
        lr.emplace_back(l, r);
    }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL& add_left, const AR& add_right, const EL& erase_left, const ER& erase_right, const O& out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A& add, const E& erase, const O& out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
struct auxiliary_tree : lca_tree {
    vector<int> in;
    vector<vector<int>> G;
    auxiliary_tree(vector<vector<int>>& g) : lca_tree(g), in((int)g.size()), G((int)g.size()) {
        int t = 0;
        dfs(0, -1, t, g);
    }
    void dfs(int v, int p, int& t, vector<vector<int>>& g) {
        in[v] = t++;
        for (auto u : g[v]) {
            if (u != p) dfs(u, v, t, g);
        }
    }
    using lca_tree::depth;
    pair<int, vector<int>> query(vector<int> vs, bool decending = false) {
        // decending:親から子の方向のみ辺を貼る
        assert(!vs.empty());
        sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
        int m = vs.size();
        stack<int> st;
        st.push(vs[0]);
        for (int i = 0; i < m - 1; i++) {
            int w = lca_tree::query(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() && depth[w] < depth[st.top()]) {
                    if (!decending) G[l].push_back(st.top());
                    G[st.top()].push_back(l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() || st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                if (!decending) G[l].push_back(w);
                G[w].push_back(l);
            }
            st.push(vs[i + 1]);
        }
        while (st.size() > 1) {
            int x = st.top();
            st.pop();
            if (!decending) G[x].push_back(st.top());
            G[st.top()].push_back(x);
        }
        // {root,vertex_list}
        return make_pair(st.top(), vs);
    }
    void clear(vector<int> vs) {
        for (int v : vs) G[v].clear();
    }
};
void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    rep(i, n) {
        cin >> a[i];
        a[i]--;
    }
    vector<vector<int>> g(n);
    rep(i, n - 1) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    auxiliary_tree aux_tree(g);
    vector<vector<int>> vec(n);
    rep(i, n) vec[a[i]].push_back(i);
    vector<int> dist(n), idx(n);
    vector<int> ans(n, INF);
    rep(color, n) {
        if (vec[color].empty()) continue;
        auto [root, vs] = aux_tree.query(vec[color]);
        using P = pair<int, int>;
        priority_queue<P, vector<P>, greater<P>> que;
        for (auto v : vs) dist[v] = n, idx[v] = -1;
        for (int s : vec[color]) dist[s] = 0, idx[s] = s, que.push({dist[s], s});
        while (!que.empty()) {
            auto [d, v] = que.top();
            que.pop();
            if (dist[v] != d) continue;
            for (auto u : aux_tree.G[v]) {
                int c = abs(aux_tree.depth[u] - aux_tree.depth[v]);
                if (chmin(dist[u], d + c)) {
                    idx[u] = idx[v];
                    que.push({dist[u], u});
                }
            }
        }
        for (int v : vs) {
            for (int u : aux_tree.G[v]) {
                if (idx[v] == idx[u]) continue;
                chmin(ans[idx[v]], aux_tree.dist(idx[v], idx[u]));
                chmin(ans[idx[u]], aux_tree.dist(idx[v], idx[u]));
            }
        }
        aux_tree.clear(vs);
    }
    rep(i, n) cout << ans[i] << endl;
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    solve();
}
accuracy: 1
time_ms: 450
memory_kb: 57996
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T& a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T& a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
constexpr double eps = 1e-10;
const double pi = acos(-1);
pair<vector<int>, vector<int>> calc_factor(int n) {
    vector<int> least_factor(n + 1, 0), prime_list;
    for (int i = 2; i <= n; i++) {
        if (least_factor[i] == 0) {
            least_factor[i] = i;
            prime_list.push_back(i);
        }
        for (int j = 0; j < (int)prime_list.size() && i * prime_list[j] <= n; j++) {
            least_factor[i * prime_list[j]] = prime_list[j];
            if (prime_list[j] == least_factor[i]) break;
        }
    }
    return make_pair(least_factor, prime_list);
}
ll extgcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint& operator+=(const modint& p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint& operator-=(const modint& p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint& operator*=(const modint& p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint& operator/=(const modint& p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint& p) const { return modint(*this) += p; }
    modint operator-(const modint& p) const { return modint(*this) -= p; }
    modint operator*(const modint& p) const { return modint(*this) *= p; }
    modint operator/(const modint& p) const { return modint(*this) /= p; }
    bool operator==(const modint& p) const { return x == p.x; }
    bool operator!=(const modint& p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream& operator<<(ostream& os, const modint& p) { return os << p.x; }
    friend istream& operator>>(istream& is, modint& a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        assert(n > 0);
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S>& a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
using mint = modint<998244353>;
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};

struct lca_tree {
    int n, size;
    vector<vector<int>> g, par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g_, int root = 0)
        : n((int)g_.size()), size(log2(n) + 2), g(g_), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
    int parent(int u, int d) {
        for (int k = size - 1; k >= 0; k--) {
            if ((d >> k) & 1) {
                if (u != -1) u = par[k][u];
            }
        }
        return u;
    }
};

struct lca_tree_with_edge_ID {
    int n, size;
    vector<vector<pair<int, int>>> g;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree_with_edge_ID(vector<vector<pair<int, int>>> g_, int root = 0)
        : n((int)g_.size()), size(log2(n) + 2), g(g_), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (auto [i, id] : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};

struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */
        lr.emplace_back(l, r);
    }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL& add_left, const AR& add_right, const EL& erase_left, const ER& erase_right, const O& out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A& add, const E& erase, const O& out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
struct auxiliary_tree : lca_tree {
    vector<int> in;
    vector<vector<int>> G;
    auxiliary_tree(vector<vector<int>> g) : lca_tree(g), in((int)g.size()), G((int)g.size()) {
        int t = 0;
        dfs(0, -1, t);
    }
    void dfs(int v, int p, int& t) {
        in[v] = t++;
        for (auto u : lca_tree::g[v]) {
            if (u != p) dfs(u, v, t);
        }
    }
    using lca_tree::depth;
    pair<int, vector<int>> query(vector<int> vs, bool decending = false) {
        // decending:親から子の方向のみ辺を貼る
        assert(!vs.empty());
        sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
        int m = vs.size();
        stack<int> st;
        st.push(vs[0]);
        for (int i = 0; i < m - 1; i++) {
            int w = lca_tree::query(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() && depth[w] < depth[st.top()]) {
                    if (!decending) G[l].push_back(st.top());
                    G[st.top()].push_back(l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() || st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                if (!decending) G[l].push_back(w);
                G[w].push_back(l);
            }
            st.push(vs[i + 1]);
        }
        while (st.size() > 1) {
            int x = st.top();
            st.pop();
            if (!decending) G[x].push_back(st.top());
            G[st.top()].push_back(x);
        }
        // {root,vertex_list}
        return make_pair(st.top(), vs);
    }
    void clear(vector<int> vs) {
        for (int v : vs) G[v].clear();
    }
};
void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    rep(i, n) {
        cin >> a[i];
        a[i]--;
    }
    vector<vector<int>> g(n);
    rep(i, n - 1) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    auxiliary_tree aux_tree(g);
    vector<vector<int>> vec(n);
    rep(i, n) vec[a[i]].push_back(i);
    vector<int> dist(n), idx(n);
    vector<int> ans(n, INF);
    rep(color, n) {
        if (vec[color].empty()) continue;
        auto [root, vs] = aux_tree.query(vec[color]);
        using P = pair<int, int>;
        priority_queue<P, vector<P>, greater<P>> que;
        for (auto v : vs) dist[v] = n, idx[v] = -1;
        for (int s : vec[color]) dist[s] = 0, idx[s] = s, que.push({dist[s], s});
        while (!que.empty()) {
            auto [d, v] = que.top();
            que.pop();
            if (dist[v] != d) continue;
            for (auto u : aux_tree.G[v]) {
                int c = abs(aux_tree.depth[u] - aux_tree.depth[v]);
                if (chmin(dist[u], d + c)) {
                    idx[u] = idx[v];
                    que.push({dist[u], u});
                }
            }
        }
        for (int v : vs) {
            for (int u : aux_tree.G[v]) {
                if (idx[v] == idx[u]) continue;
                chmin(ans[idx[v]], aux_tree.dist(idx[v], idx[u]));
                chmin(ans[idx[u]], aux_tree.dist(idx[v], idx[u]));
            }
        }
        aux_tree.clear(vs);
    }
    rep(i, n) cout << ans[i] << endl;
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    solve();
}
accuracy: 1
time_ms: 480
memory_kb: 67392
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T& a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T& a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
constexpr double eps = 1e-10;
const double pi = acos(-1);
pair<vector<int>, vector<int>> calc_factor(int n) {
    vector<int> least_factor(n + 1, 0), prime_list;
    for (int i = 2; i <= n; i++) {
        if (least_factor[i] == 0) {
            least_factor[i] = i;
            prime_list.push_back(i);
        }
        for (int j = 0; j < (int)prime_list.size() && i * prime_list[j] <= n; j++) {
            least_factor[i * prime_list[j]] = prime_list[j];
            if (prime_list[j] == least_factor[i]) break;
        }
    }
    return make_pair(least_factor, prime_list);
}
ll extgcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint& operator+=(const modint& p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint& operator-=(const modint& p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint& operator*=(const modint& p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint& operator/=(const modint& p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint& p) const { return modint(*this) += p; }
    modint operator-(const modint& p) const { return modint(*this) -= p; }
    modint operator*(const modint& p) const { return modint(*this) *= p; }
    modint operator/(const modint& p) const { return modint(*this) /= p; }
    bool operator==(const modint& p) const { return x == p.x; }
    bool operator!=(const modint& p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream& operator<<(ostream& os, const modint& p) { return os << p.x; }
    friend istream& operator>>(istream& is, modint& a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        assert(n > 0);
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S>& a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
using mint = modint<998244353>;
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};

struct lca_tree {
    int n, size;
    vector<vector<int>> g, par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g_, int root = 0)
        : n((int)g_.size()), size(log2(n) + 2), g(g_), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
    int parent(int u, int d) {
        for (int k = size - 1; k >= 0; k--) {
            if ((d >> k) & 1) {
                if (u != -1) u = par[k][u];
            }
        }
        return u;
    }
};

struct lca_tree_with_edge_ID {
    int n, size;
    vector<vector<pair<int, int>>> g;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree_with_edge_ID(vector<vector<pair<int, int>>> g_, int root = 0)
        : n((int)g_.size()), size(log2(n) + 2), g(g_), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (auto [i, id] : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};

struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */
        lr.emplace_back(l, r);
    }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL& add_left, const AR& add_right, const EL& erase_left, const ER& erase_right, const O& out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A& add, const E& erase, const O& out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
pair<int, vector<pair<int, int>>> aux_tree(vector<int> vs, vector<vector<int>>& g, lca_tree& lt, vector<int>& in) {
    assert(!vs.empty());
    sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
    int m = vs.size();
    stack<int> st;
    st.push(vs[0]);
    vector<pair<int, int>> res;
    for (int i = 0; i < m - 1; i++) {
        int w = lt.query(vs[i], vs[i + 1]);
        if (w != vs[i]) {
            int l = st.top();
            st.pop();
            while (!st.empty() && lt.depth[w] < lt.depth[st.top()]) {
                res.push_back({l, st.top()});
                l = st.top();
                st.pop();
            }
            if (st.empty() || st.top() != w) {
                st.push(w);
            }
            res.push_back({l, w});
        }
        st.push(vs[i + 1]);
    }
    while (st.size() > 1) {
        int x = st.top();
        st.pop();
        res.push_back({x, st.top()});
    }
    for (auto [ver, par] : res) {
        assert(lt.depth[ver] > lt.depth[par]);
    }
    //{root,{{vertex,parent,},...}}
    return make_pair(st.top(), res);
}
void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    rep(i, n) {
        cin >> a[i];
        a[i]--;
    }
    vector<vector<int>> g(n);
    rep(i, n - 1) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    vector<int> in(n);
    int t = 0;
    function<void(int, int)> dfs = [&](int v, int p) {
        in[v] = t++;
        for (auto u : g[v]) {
            if (u != p) dfs(u, v);
        }
    };
    dfs(0, -1);

    lca_tree lt(g);
    vector<vector<int>> vec(n);
    rep(i, n) vec[a[i]].push_back(i);
    vector<int> dist(n), idx(n);
    vector<vector<int>> G(n);
    vector<int> ans(n, INF);
    rep(color, n) {
        if (vec[color].empty()) continue;
        auto [root, vs] = aux_tree(vec[color], g, lt, in);
        using P = pair<int, int>;
        priority_queue<P, vector<P>, greater<P>> que;
        dist[root] = n, idx[root] = -1;
        for (auto [v, par] : vs) {
            dist[v] = n, idx[v] = -1;
            G[v].push_back(par);
            G[par].push_back(v);
        }
        for (int s : vec[color]) dist[s] = 0, idx[s] = s, que.push({dist[s], s});
        while (!que.empty()) {
            auto [d, v] = que.top();
            que.pop();
            if (dist[v] != d) continue;
            for (auto u : G[v]) {
                int c = abs(lt.depth[u] - lt.depth[v]);
                if (chmin(dist[u], d + c)) {
                    idx[u] = idx[v];
                    que.push({dist[u], u});
                }
            }
        }
        for (auto [v, par] : vs) {
            G[v].pop_back();
            G[par].pop_back();
        }
        for (auto [v, par] : vs) {
            int u = par;
            if (idx[v] == idx[u]) continue;
            // if (color == 2) cout << idx[v] << " " << idx[u] << " " << lt.dist(idx[u], idx[v]) << endl;
            chmin(ans[idx[v]], lt.dist(idx[v], idx[u]));
            chmin(ans[idx[u]], lt.dist(idx[v], idx[u]));
        }
    }

    rep(i, n) cout << ans[i] << endl;
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    solve();
}
accuracy: 1
time_ms: 430
memory_kb: 67308
final_rank: 9