#include using namespace std; using vi = vector; using vvi = vector; const int INF = 1e9; const int NEG = -INF; struct UnionFind { vector par, sz; UnionFind(int n) : par(n, -1), sz(n, 1) {} int root(int x) { if (par[x] == -1) return x; return par[x] = root(par[x]); } bool unite(int x, int y) { x = root(x); y = root(y); if (x == y) return false; if (sz[x] < sz[y]) swap(x, y); par[y] = x; sz[x] += sz[y]; return true; } }; bool is02(int x, int y) { return (x == 0 && y == 2) || (x == 2 && y == 0); } void solve() { int N; cin >> N; vvi G(N); vector> edges; 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); edges.push_back({u, v}); } vi A(N); for (int i = 0; i < N; i++) { cin >> A[i]; } // -------------------------------------------------------- // 同じ値の連結成分を縮約 // -------------------------------------------------------- UnionFind uf(N); for (auto [u, v] : edges) { if (A[u] == A[v]) { uf.unite(u, v); } } vi id(N, -1); int M = 0; for (int v = 0; v < N; v++) { if (uf.root(v) == v) { id[v] = M++; } } vi cid(N); for (int v = 0; v < N; v++) { cid[v] = id[uf.root(v)]; } vi val(M); vi cap(M, 0); for (int v = 0; v < N; v++) { int c = cid[v]; val[c] = A[v]; cap[c]++; } vvi T(M); for (auto [u, v] : edges) { int x = cid[u]; int y = cid[v]; if (x == y) continue; T[x].push_back(y); T[y].push_back(x); } // -------------------------------------------------------- // // dp[v][p][a] // // p: // 親との 0-2 辺を「起爆辺」として選んでいるか // // a: // この成分が active (= 最終的に1になる) か // // 値: // v 以下で縮約できる「成分間の辺」の最大本数 // // ※ 親-v の辺自体は親側で数える // // -------------------------------------------------------- vector,2>> dp(M); for (int v = 0; v < M; v++) { for (int p = 0; p < 2; p++) { for (int a = 0; a < 2; a++) { dp[v][p][a] = NEG; } } } function dfs = [&](int v, int par) { for (int u : T[v]) { if (u == par) continue; dfs(u, v); } // 親との起爆辺を使うか for (int parent_used = 0; parent_used <= 1; parent_used++) { // この成分が active か for (int active = 0; active <= 1; active++) { // 1成分は最初から active if (val[v] == 1) { if (!active) continue; if (parent_used) continue; } // 0/2成分で親辺を使っているなら active if (val[v] != 1) { if (parent_used && !active) continue; } // 容量不足 if (parent_used > cap[v]) continue; int base = 0; vector gain; bool ok = true; for (int u : T[v]) { if (u == par) continue; // ----------------------------------------- // v-u を起爆辺にしない // ----------------------------------------- int no_use = NEG; for (int child_active = 0; child_active <= 1; child_active++) { if (dp[u][0][child_active] <= NEG / 2) { continue; } int add = dp[u][0][child_active]; // 両端 active ならこの辺も縮約できると仮定 if (active && child_active) { add++; } no_use = max(no_use, add); } if (no_use <= NEG / 2) { ok = false; break; } base += no_use; // ----------------------------------------- // v-u を 0-2 の起爆辺として使用 // ----------------------------------------- if (active && is02(val[v], val[u])) { if (dp[u][1][1] > NEG / 2) { int use = dp[u][1][1] + 1; gain.push_back( use - no_use ); } } } if (!ok) continue; // ------------------------------------------------ // 選べる子方向の起爆辺の最大数 // // 嘘ポイント: // 「成分サイズ = 独立に使えるport数」 // と仮定している // ------------------------------------------------ int K = cap[v] - parent_used; // activeでないなら、1本も起爆辺を選べない if (!active) { if (parent_used) continue; dp[v][0][0] = max(dp[v][0][0], base); continue; } // value=1 なら起爆辺はそもそも存在しない if (val[v] == 1) { dp[v][0][1] = max(dp[v][0][1], base); continue; } // 0/2で active になるには、 // // 親辺を使っていない場合: // 子方向で最低1本必要 // // 親辺を使っている場合: // 0本でもよい int need = (parent_used ? 0 : 1); if ((int)gain.size() < need) continue; if (K < need) continue; sort( gain.rbegin(), gain.rend() ); int cur = base; int used = 0; // activeにするため最低1本必要 while (used < need) { cur += gain[used]; used++; } // あとは得するものだけ選ぶ while ( used < K && used < (int)gain.size() && gain[used] > 0 ) { cur += gain[used]; used++; } dp[v][parent_used][1] = max( dp[v][parent_used][1], cur ); } } }; dfs(0, -1); // 同値成分内部の辺は全部縮約できると仮定 int inner = N - M; int cross = max( dp[0][0][0], dp[0][0][1] ); int ans = inner + cross; ans = min(ans, N - 1); cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); solve(); }