#include #include #include #include #include #include #include #include #include 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 "Src/DataStructure/SegmentTree/SegmentTree.hpp" // #include "Src/DataStructure/DisjointSetUnion/DisjointSetUnion.hpp" #include #include 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 namespace zawa { template class ChminMonoidData { private: std::optional priority_{}; U value_{}; public: ChminMonoidData() = default; ChminMonoidData(const U& value) : priority_{std::nullopt}, value_{value} {} ChminMonoidData(const T& priority, const U& value) : priority_{priority}, value_{value} {} constexpr bool infty() const noexcept { return !priority_.has_value(); } constexpr const T& priority() const noexcept { return priority_.value(); } constexpr const U& value() const noexcept { return value_; } friend constexpr bool operator<(const ChminMonoidData& l, const ChminMonoidData& r) { if (l.infty()) return false; else if (r.infty()) return true; else return l.priority() < r.priority(); } }; template struct ChminMonoid { using Element = ChminMonoidData; static Element identity() noexcept { return Element{}; } // タイブレークはl側を優先するようになっている。 static Element operation(const Element& l, const Element& r) noexcept { return (r < l ? r : l); } }; } // namespace zawa #include namespace zawa { template class SparseTable { private: using Value = typename Structure::Element; std::vector L; std::vector> dat; public: SparseTable() : L{}, dat{} {} SparseTable(const std::vector& a) : L(a.size() + 1), dat{} { for (u32 i{1} ; i < L.size() ; i++) { L[i] = L[i - 1] + (i >> (L[i - 1] + 1)); } dat.resize(L.back() + 1); dat[0] = a; for (u32 i{1}, len{2} ; i < dat.size() ; i++, len <<= 1) { dat[i] = dat[i - 1]; for (u32 j{} ; j + len - 1 < dat[i].size() ; j++) { dat[i][j] = Structure::operation(dat[i - 1][j], dat[i - 1][j + (len >> 1)]); } } } Value product(u32 l, u32 r) const { assert(l <= r); assert(l < dat[0].size()); assert(r <= dat[0].size()); u32 now{L[r - l]}; return Structure::operation(dat[now][l], dat[now][r - (1 << now)]); } friend std::ostream& operator<<(std::ostream& os, const SparseTable& spt) { for (u32 i{}, len{1} ; i < spt.dat.size() ; i++, len <<= 1) { os << "length = " << len << '\n'; for (u32 j{} ; j + len - 1 < spt.dat[i].size() ; j++) { os << spt.dat[i][j] << (j + len == spt.dat[i].size() ? '\n' : ' '); } } return os; } }; } // namespace zawa namespace zawa { template class LowestCommonAncestor { private: using Monoid = ChminMonoid; public: LowestCommonAncestor() = default; LowestCommonAncestor(const std::vector>& tree, V r = V{}) : n_{tree.size()}, depth_(tree.size()), L_(tree.size()), R_(tree.size()), st_{} { std::vector init; init.reserve(2 * size()); auto dfs{[&](auto dfs, V v, V p) -> void { depth_[v] = (p == INVALID ? 0u : depth_[p] + 1); L_[v] = (u32)init.size(); for (auto x : tree[v]) { if (x == p) { continue; } init.emplace_back(depth_[v], v); dfs(dfs, x, v); } R_[v] = (u32)init.size(); }}; dfs(dfs, r, INVALID); st_ = SparseTable(init); } V operator()(V u, V v) const { assert(verify(u)); assert(verify(v)); if (L_[u] > L_[v]) { std::swap(u, v); } return u == v ? u : st_.product(L_[u], R_[v]).value(); } V lca(V u, V v) const { return (*this)(u, v); } inline u32 depth(V v) const noexcept { assert(verify(v)); return depth_[v]; } u32 distance(V u, V v) const { assert(verify(u)); assert(verify(v)); return depth(u) + depth(v) - 2u * depth((*this)(u, v)); } bool isAncestor(V p, V v) const { assert(verify(p)); assert(verify(v)); return L_[p] <= L_[v] and R_[v] <= R_[p]; } protected: u32 left(V v) const noexcept { return L_[v]; } inline usize size() const { return n_; } inline bool verify(V v) const { return v < (V)size(); } private: static constexpr V INVALID{static_cast(-1)}; usize n_{}; std::vector depth_, L_, R_; SparseTable st_; }; } // namespace zawa namespace zawa { template class AuxiliaryTree : public LowestCommonAncestor { public: using Super = LowestCommonAncestor; AuxiliaryTree() = default; AuxiliaryTree(const std::vector>& T, V r = 0u) : Super{ T, r }, T_(T.size()), dist_(T.size()), used_(T.size()) {} V construct(const std::vector& vs) { assert(vs.size()); clear(); vs_ = vs; return build(); } const std::vector& operator[](V v) const { assert(Super::verify(v)); return T_[v]; } inline bool contains(V v) const { assert(Super::verify(v)); return used_[v]; } inline u32 parentEdgeLength(V v) const { assert(contains(v)); return dist_[v]; } std::vector current() const { return vs_; } private: std::vector> T_{}; std::vector vs_{}; std::vector dist_{}; std::vector used_{}; void addEdge(V p, V v) { assert(Super::depth(p) < Super::depth(v)); T_[p].push_back(v); T_[v].push_back(p); dist_[v] = Super::depth(v) - Super::depth(p); } V build() { std::sort(vs_.begin(), vs_.end(), [&](V u, V v) -> bool { return Super::left(u) < Super::left(v); }); vs_.erase(std::unique(vs_.begin(), vs_.end()), vs_.end()); usize k{vs_.size()}; std::vector stack; stack.reserve(2u * vs_.size()); stack.emplace_back(vs_[0]); for (usize i{} ; i + 1 < k ; i++) { if (!Super::isAncestor(vs_[i], vs_[i + 1])) { V w{Super::lca(vs_[i], vs_[i + 1])}; V l{stack.back()}; stack.pop_back(); while (stack.size() and LowestCommonAncestor::depth(w) < LowestCommonAncestor::depth(stack.back())) { addEdge(stack.back(), l); l = stack.back(); stack.pop_back(); } if (stack.empty() or stack.back() != w) { stack.emplace_back(w); vs_.emplace_back(w); } addEdge(w, l); } stack.emplace_back(vs_[i + 1]); } while (stack.size() > 1u) { V l{stack.back()}; stack.pop_back(); addEdge(stack.back(), l); } for (V v : vs_) { used_[v] = true; } return stack.back(); } void clear() { for (V v : vs_) { T_[v].clear(); used_[v] = false; dist_[v] = 0u; } vs_.clear(); } }; } // namespace zawa using namespace zawa; // #include "atcoder/modint" // using mint = atcoder::modint998244353; const int SQ = 500; int main() { std::cin.tie(nullptr); std::cout.tie(nullptr); std::ios::sync_with_stdio(false); int N; std::cin >> N; std::vector> g(N); for (int i = 0 ; i < N - 1 ; i++) { int u, v; std::cin >> u >> v; u--; v--; g[u].push_back(v); g[v].push_back(u); } int Q; std::cin >> Q; std::vector T(Q), V(Q); for (int i = 0 ; i < Q ; i++) { std::cin >> T[i] >> V[i]; V[i]--; } AuxiliaryTree at{g}; std::vector black(N); std::vector mark(N); for (int i = 0 ; i < Q ; i += SQ) { std::vector vs; vs.reserve(SQ); for (int j = 0 ; j < SQ and i + j < Q ; j++) if (!mark[V[i + j]]) { vs.push_back(V[i + j]); mark[V[i + j]] = true; } const auto root = at.construct(vs); const int INF = (int)1e9; std::vector dist1(N, INF), dist(N, INF); auto rec1 = [&](auto rec, int v, int p) -> int { if (!mark[v] and black[v]) dist1[v] = 0; for (int x : g[v]) if (x != p) dist1[v] = std::min(dist1[v], rec(rec, x, v) + 1); return dist1[v]; }; rec1(rec1, 0, -1); auto rec2 = [&](auto rec, int v, int p, int pval) -> void { int fir = INF, sec = INF; for (int x : g[v]) { const int val = 1 + (x == p ? pval : dist1[x]); if (fir > val) { sec = fir; fir = val; } else if (sec > val) { sec = val; } } if (!mark[v] and black[v]) dist[v] = 0; else dist[v] = fir; for (int x : g[v]) if (x != p) { const int val = dist1[x] + 1; rec(rec, x, v, val == fir ? sec : fir); } }; rec2(rec2, 0, -1, INF); std::vector atdep(N, -1); auto dfs = [&](auto dfs, int v, int p, int d) -> void { atdep[v] = d; for (auto x : at[v]) if (x != p) dfs(dfs, x, v, d + 1); }; dfs(dfs, root, -1, 0); for (int j = 0 ; j < SQ and i + j < Q ; j++) { mark[V[i + j]] = false; if (T[i + j] == 1) black[V[i + j]] = !black[V[i + j]]; else if (T[i + j] == 2) { const int s = V[i + j]; int ans = dist[s]; std::vector> que; que.push_back({s, -1, 0}); for (int qt = 0 ; qt < std::ssize(que) ; qt++) { const auto [v, p, d] = que[qt]; if (black[v]) { ans = std::min(ans, d); } else { for (auto x : at[v]) if (x != p) { const auto w = at.parentEdgeLength(atdep[x] > atdep[v] ? x : v); que.push_back({x, v, d + w}); } } } std::cout << ans << '\n'; } else assert(false); } } }