#include #include #include // #include // https://github.com/amiast/cpp-utility #ifndef KOTONE_REROOTING_HPP #define KOTONE_REROOTING_HPP 1 #include #include // #include // https://github.com/amiast/cpp-utility #ifndef KOTONE_DSU_HPP #define KOTONE_DSU_HPP 1 #include #include #include namespace kotone { // A basic data structure that monitors connectivity in a graph. // Optionally monitors the potential differences between nodes. // Reference: AtCoder Library template struct dsu { protected: int _num_nodes; bool _defines_pd = true; std::vector _parent_or_size; std::vector _p; T _potential(int v) { leader(v); return _p[v]; } public: dsu() : _num_nodes(0) {} // Creates a graph with the specified `num_nodes` and no edges. dsu(int num_nodes) : _num_nodes(num_nodes), _parent_or_size(num_nodes, -1), _p(num_nodes) { assert(num_nodes >= 0); assert(num_nodes <= 100000000); } // Returns the leader of the connected component containing `v`. int leader(int v) { assert(v >= 0); assert(v < _num_nodes); if (_parent_or_size[v] < 0) return v; int l = leader(_parent_or_size[v]); _p[v] = _p[v] + _p[_parent_or_size[v]]; return _parent_or_size[v] = l; } // Returns whether `u` and `v` belong to the same connected component. bool connected(int u, int v) { return leader(u) == leader(v); } // Returns the potential difference from `u` to `v`. // Requires `u` and `v` to be connected. // Requires all previous `merge()` calls to define potential differences. T potential_diff(int u, int v) { assert(connected(u, v)); assert(_defines_pd); return _potential(v) - _potential(u); }; // Adds an edge between `u` and `v`, // then returns the leader of the merged component. virtual int merge(int u, int v) { _defines_pd = false; return merge(u, v, T{}); } // Adds an edge between `u` and `v`, // then returns the leader of the merged component. // If `u` and `v` are not formerly connected, // defines the potential difference from `u` to `v` as `pd`. virtual int merge(int u, int v, T pd) { if (connected(u, v)) return leader(u); pd = pd + _potential(u) - _potential(v); u = leader(u); v = leader(v); if (_parent_or_size[u] > _parent_or_size[v]) { std::swap(u, v); pd = -pd; } _parent_or_size[u] += _parent_or_size[v]; _parent_or_size[v] = u; _p[v] = pd; return u; } // Returns the size of the connected component containing `v`. int size(int v) { return -_parent_or_size[leader(v)]; } // Returns a vector of connected components as vectors of node indices. // The order of components is undefined. // Node indices in each group's vector appear in ascending order. std::vector> components() { std::vector> temp(_num_nodes), result; for (int i = 0; i < _num_nodes; i++) temp[leader(i)].emplace_back(i); for (int i = 0; i < _num_nodes; i++) { if (temp[i].size()) { result.push_back(std::move(temp[i])); } } return result; } }; // An extended DSU with internal mapping between connected components and a semigroup. // Optionally monitors the potential differences between nodes. template struct extended_dsu : dsu { protected: std::vector _map; public: extended_dsu() : dsu() {} // Creates a graph with the specified `num_nodes` and no edges. // Each connected component is mapped to a value-initialized instance of `S`. extended_dsu(int num_nodes) : dsu(num_nodes), _map(num_nodes) {} // Creates a graph with the specified `num_nodes` and no edges. // Each connected component is mapped to a copy of `init_x`. extended_dsu(int num_nodes, S init_x) : dsu(num_nodes), _map(num_nodes, init_x) {} // Creates a graph with no edges. // For all `v`, maps connected component containing `v` to `vec[v]`. extended_dsu(const std::vector &vec) : dsu(vec.size()), _map(vec) {} // Adds an edge between `u` and `v`, // then returns the leader of the merged component. // Also merges their images under the mapping. int merge(int u, int v) override { this->_defines_pd = false; return merge(u, v, T{}); } // Adds an edge between `u` and `v`, // then returns the leader of the merged component. // Also merges their images under the mapping. // If `u` and `v` are not formerly connected, // defines the potential difference from `u` to `v` as `pd`. int merge(int u, int v, T pd) override { if (this->connected(u, v)) return this->leader(u); S result = op(_map[this->leader(u)], _map[this->leader(v)]); _map[dsu::merge(u, v, pd)] = std::move(result); return this->leader(u); } // Returns a copy of the image mapped from the connected component containing `v`. S get(int v) { return _map[this->leader(v)]; } // Reassigns `x` as the image mapped from the connected component containing `v`, // then returns the leader of the modified component. int set(int v, S x) { v = this->leader(v); _map[v] = std::move(x); return v; } }; } // namespace kotone #endif // KOTONE_DSU_HPP namespace kotone { // Maintains dynamic programming for commutative monoids at different roots of trees in a forest. // Requires the following functions: // - `S merge(S dp_l, S dp_r)` // - `S apply(S dp_child, int child, int parent)` // - `S identity()` template struct rerooting { private: int _size = 0; std::vector> _tree; std::vector _dp; kotone::dsu _dsu; void _dfs_forward(int u, int p) { _dp[u] = identity(); for (int v : _tree[u]) { if (v == p) continue; _dfs_forward(v, u); _dp[u] = merge(_dp[u], apply(_dp[v], v, u)); } } void _dfs_reverse(int u, int p, S parent_acc, std::vector &result) { int deg = static_cast(_tree[u].size()); std::vector prefix(deg + 1, identity()), suffix(deg + 1, identity()); for (int i = 0; i < deg; i++) { int v = _tree[u][i]; if (v == p) prefix[i + 1] = merge(prefix[i], apply(parent_acc, p, u)); else prefix[i + 1] = merge(prefix[i], apply(_dp[v], v, u)); } for (int i = deg - 1; i >= 0; i--) { int v = _tree[u][i]; if (v == p) suffix[i] = merge(suffix[i + 1], apply(parent_acc, p, u)); else suffix[i] = merge(suffix[i + 1], apply(_dp[v], v, u)); } result[u] = prefix[deg]; for (int i = 0; i < deg; i++) { int v = _tree[u][i]; if (v == p) continue; S acc = merge(prefix[i], suffix[i + 1]); _dfs_reverse(v, u, acc, result); } } public: rerooting() {} // Constructs a forest with the specified number of disconnected nodes. // Requires `0 <= num_nodes <= 100000000`. rerooting(int num_nodes) : _size(num_nodes), _dsu(num_nodes) { assert(0 <= num_nodes && num_nodes <= 100000000); _tree.resize(num_nodes); _dp.resize(num_nodes); } // Returns the number of nodes in the forest. int size() const { return _size; } // Adds an edge between nodes `u` and `v`. // Requires `0 <= u, v < size()`. // Requires `u` and `v` to be formerly disconnected. void add_edge(int u, int v) { assert(0 <= u && u < _size); assert(0 <= v && v < _size); assert(!_dsu.connected(u, v)); _tree[u].push_back(v); _tree[v].push_back(u); _dsu.merge(u, v); } // Evaluates and returns a vector of the function at different roots of trees in the forest. std::vector evaluate() { std::vector result(_size); for (int i = 0; i < _size; i++) { if (i != _dsu.leader(i)) continue; _dfs_forward(i, -1); _dfs_reverse(i, -1, identity(), result); } return result; } }; } // namespace kotone #endif // KOTONE_REROOTING_HPP int merge(int a, int b) { return a | b; } int apply(int x, int, int) { return ~x & -~x; } int identity() { return 0; } int main() { int N, M; std::cin >> N >> M; std::vector A(M), count(N); for (int &a : A) { std::cin >> a; a--; count[a] ^= 1; } std::vector> tree(N); kotone::rerooting dp(N); for (int i = 1; i < N; i++) { int u, v; std::cin >> u >> v; u--, v--; tree[u].push_back(v); tree[v].push_back(u); dp.add_edge(u, v); } int xorsum = 0; std::vector vec = dp.evaluate(); std::vector grundy(N); for (int i = 0; i < N; i++) { grundy[i] = std::bit_width((unsigned)apply(vec[i], 0, 0)) - 1; if (count[i]) xorsum ^= grundy[i]; } if (!xorsum) { std::cout << "-1 -1" << std::endl; return 0; } int root = -1; for (int i = 0; i < N; i++) { if (!count[i] || !grundy[i]) continue; if ((grundy[i] ^ xorsum) >= grundy[i]) continue; root = i; break; } for (int i = 0; i < M; i++) { if (A[i] != root) continue; std::cout << i + 1 << ' '; break; } auto solve = [&](auto &solve, int u, int p) -> int { int result = 0; for (int v : tree[u]) { if (v == p) continue; result |= solve(solve, v, u); } return apply(result, 0, 0); }; for (int v : tree[root]) { int g = std::bit_width((unsigned)solve(solve, v, root)) - 1; if (g != (xorsum ^ grundy[root])) continue; std::cout << v + 1 << std::endl; break; } }