// 愚直全探索 #include using namespace std; using ull = unsigned long long; struct Node { ull mask; // この縮約頂点に含まれる元頂点集合 int val; // 0,1,2 bool operator<(const Node& rhs) const { if (mask != rhs.mask) return mask < rhs.mask; return val < rhs.val; } }; int N; vector> E; // memo[state] = ここから可能な最大操作回数 map, int> memo; // state 内で、元頂点 v がどの縮約頂点に属するか vector get_id(const vector& state) { vector id(N); for (int i = 0; i < (int)state.size(); i++) { ull m = state[i].mask; for (int v = 0; v < N; v++) { if ((m >> v) & 1ULL) { id[v] = i; } } } return id; } int dfs(vector state) { // mask 順に canonicalize sort(state.begin(), state.end()); if (memo.count(state)) { return memo[state]; } int M = state.size(); auto id = get_id(state); // 現在の縮約木の辺 set> edges; for (auto [u,v] : E) { int a = id[u]; int b = id[v]; if (a == b) continue; if (a > b) swap(a,b); edges.insert({a,b}); } int ans = 0; for (auto [a,b] : edges) { int x = state[a].val; int y = state[b].val; // 偶奇が異なるので縮約不可 if ((x & 1) != (y & 1)) { continue; } vector nxt; for (int i = 0; i < M; i++) { if (i == a || i == b) continue; nxt.push_back(state[i]); } Node merged; merged.mask = state[a].mask | state[b].mask; merged.val = (x + y) / 2; nxt.push_back(merged); ans = max( ans, 1 + dfs(nxt) ); } return memo[state] = ans; } void solve() { cin >> N; E.resize(N-1); for (auto &[u,v] : E) { cin >> u >> v; --u; --v; } vector A(N); for (int &x : A) { cin >> x; } assert(N <= 63); vector init; for (int v = 0; v < N; v++) { init.push_back({ 1ULL << v, A[v] }); } memo.clear(); cout << dfs(init) << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); solve(); }