#include #include #include // #include // https://github.com/amiast/cpp-utility #ifndef KOTONE_LCA_HPP #define KOTONE_LCA_HPP 1 #include #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 { // A data structure that manages the lowest common ancestors (LCA) of nodes in a tree (forest). struct lca_tree { private: int _num_nodes = 0; std::vector> _tree, _sparse_table; std::vector _depth, _euler, _level, _first, _log, _is_root; dsu _dsu; bool _requires_build = false; bool _is_rooted = true; void _euler_tour(int u, int p, int d) { _depth[u] = d; _first[u] = _euler.size(); _euler.push_back(u); _level.push_back(d); for (int v : _tree[u]) { if (v == p) continue; _euler_tour(v, u, d + 1); _euler.push_back(u); _level.push_back(d); } } void _build() { if (!_requires_build) return; _requires_build = false; _euler.clear(); _level.clear(); for (int i = 0; i < _num_nodes; i++) { if ( (_is_rooted && _is_root[i]) || (!_is_rooted && i == _dsu.leader(i)) ) _euler_tour(i, -1, 0); } int M = _euler.size(); _log.resize(M + 1); for (int i = 2; i <= M; i++) _log[i] = _log[i / 2] + 1; int K = _log[M] + 1; _sparse_table.assign(K, std::vector(M)); for (int i = 0; i < M; i++) _sparse_table[0][i] = i; for (int k = 1; k < K; k++) { for (int i = 0; i + (1 << k) <= M; i++) { int x = _sparse_table[k - 1][i]; int y = _sparse_table[k - 1][i + (1 << (k - 1))]; _sparse_table[k][i] = _level[x] < _level[y] ? x : y; } } } public: lca_tree() {} // Constructs a tree for the specified number of nodes. lca_tree(int num_nodes) : _num_nodes(num_nodes), _dsu(num_nodes) { assert(0 <= num_nodes && num_nodes <= 100000000); _tree.resize(num_nodes); _depth.resize(num_nodes); _first.resize(num_nodes); _is_root.resize(num_nodes, 1); } // Returns the number of nodes in the tree. int size() const noexcept { return _num_nodes; } // Adds an edge between the specified parent and child nodes. // If `child` already has an assigned parent, the tree becomes undirected. // - This alters the behavior of `get_lca`. // // Requires `parent` and `child` to be formerly disconnected. // Requires `0 <= parent < num_nodes`. // Requires `0 <= child < num_nodes`. void add_edge(int parent, int child) { assert(0 <= parent && parent < _num_nodes); assert(0 <= child && child < _num_nodes); assert(!_dsu.connected(parent, child)); if (!_is_root[child]) _is_rooted = false; _tree[parent].push_back(child); _tree[child].push_back(parent); _dsu.merge(parent, child); _is_root[child] = false; _requires_build = true; } // Prompts the tree to build the LCA table immediately. void build() { _build(); } // Returns the lowest common ancestor of nodes `u` and `v`. // If `u` and `v` are disconnected, returns `-1`. // If the tree is undirected due to the effect of `add_edge`, // the LCA is assigned to be an arbitrary node in the tree (or the connected component in a forest). // Requires `0 <= u < num_nodes`. // Requires `0 <= v < num_nodes`. int get_lca(int u, int v) { assert(0 <= u && u < _num_nodes); assert(0 <= v && v < _num_nodes); if (_requires_build) _build(); if (u == v) return u; if (!_dsu.connected(u, v)) return -1; int L = _first[u], R = _first[v]; if (L > R) std::swap(L, R); int k = _log[R - L + 1]; int x = _sparse_table[k][L]; int y = _sparse_table[k][R - (1 << k) + 1]; return _level[x] < _level[y] ? _euler[x] : _euler[y]; } // Returns the distance between nodes `u` and `v`. // If `u` and `v` are disconnected, returns `-1`. // Requires `0 <= u < num_nodes`. // Requires `0 <= v < num_nodes`. int get_distance(int u, int v) { int w = get_lca(u, v); if (w == -1) return -1; return _depth[u] + _depth[v] - _depth[w] * 2; } }; } // namespace kotone #endif // KOTONE_LCA_HPP kotone::lca_tree lca_tree; struct S { int l = -1, r = -1; int64_t dist = 0; }; S op(S a, S b) { if (a.l == -1) return b; if (b.l == -1) return a; return {a.l, b.r, a.dist + b.dist + lca_tree.get_distance(a.r, b.l)}; } S e() { return {}; } int main() { int N; std::cin >> N; lca_tree = kotone::lca_tree(N); std::vector> tree(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); } int time = 0; std::vector order(N), order_of(N), out(N); std::vector> jump(18, std::vector(N)); auto eval_order = [&](auto &eval_order, int u, int p) -> void { order[u] = time; order_of[time++] = u; for (int v : tree[u]) { if (v == p) continue; lca_tree.add_edge(u, v); jump[0][v] = u; eval_order(eval_order, v, u); } out[u] = time; }; eval_order(eval_order, 0, 0); for (int k = 0; k < 17; k++) { for (int i = 0; i < N; i++) { jump[k + 1][i] = jump[k][jump[k][i]]; } } std::vector vec(N); for (int i = 0; i < N; i++) { int c; std::cin >> c; if (c == 0) continue; vec[order[i]] = {i, i, 0}; } atcoder::segtree seg(vec); int Q; std::cin >> Q; while (Q--) { int t; std::cin >> t; if (t == 1) { int v; std::cin >> v; v--; if (seg.get(order[v]).l == -1) seg.set(order[v], {v, v, 0}); else seg.set(order[v], {}); continue; } int x, y; std::cin >> x >> y; x--, y--; S prod; if (x == y) prod = seg.all_prod(); else if (!(order[y] <= order[x] && order[x] < out[y])) prod = seg.prod(order[y], out[y]); else { int d = lca_tree.get_distance(y, x) - 1; for (int k = 0; k < 18; k++) { if (d >> k & 1) x = jump[k][x]; } prod = op(seg.prod(0, order[x]), seg.prod(out[x], N)); } if (prod.l == -1) std::cout << 0 << std::endl; else std::cout << (prod.dist + lca_tree.get_distance(prod.l, prod.r)) / 2 + 1 << std::endl; } }