This documentation is automatically generated by competitive-verifier/competitive-verifier
// competitive-verifier: PROBLEM https://atcoder.jp/contests/abc302/tasks/abc302_ex
// competitive-verifier: IGNORE
#include <iostream>
#include <ranges>
#include <vector>
#include "tools/hld.hpp"
#include "tools/undoable_dsu.hpp"
#include "tools/fix.hpp"
#include "tools/join.hpp"
int main() {
std::cin.tie(nullptr);
std::ios_base::sync_with_stdio(false);
int N;
std::cin >> N;
std::vector<int> A(N), B(N);
for (int i = 0; i < N; ++i) {
std::cin >> A[i] >> B[i];
--A[i], --B[i];
}
tools::hld graph(N);
for (int i = 0; i < N - 1; ++i) {
int U, V;
std::cin >> U >> V;
--U, --V;
graph.add_edge(U, V);
}
graph.build(0);
std::vector<int> answers(N);
std::vector<bool> has_cycle(N, false);
tools::undoable_dsu dsu(N);
tools::fix([&](auto&& dfs, const int v) -> void {
const auto same = dsu.same(A[v], B[v]);
const bool has_cycle_A = has_cycle[dsu.leader(A[v])];
const bool has_cycle_B = has_cycle[dsu.leader(B[v])];
dsu.merge(A[v], B[v]);
answers[v] = (v == 0 ? 0 : answers[graph.vparent(v)]);
if (same || has_cycle_A || has_cycle_B) {
has_cycle[dsu.leader(A[v])] = true;
}
if (!has_cycle_A || !has_cycle_B) {
++answers[v];
}
for (const auto c : graph.vchildren(v)) {
dfs(c);
}
dsu.undo();
has_cycle[dsu.leader(A[v])] = has_cycle_A;
has_cycle[dsu.leader(B[v])] = has_cycle_B;
})(0);
std::cout << tools::join(std::ranges::subrange(answers.begin() + 1, answers.end()), " ") << '\n';
return 0;
}
#line 1 "tests/undoable_dsu/leader.test.cpp"
// competitive-verifier: PROBLEM https://atcoder.jp/contests/abc302/tasks/abc302_ex
// competitive-verifier: IGNORE
#include <iostream>
#include <ranges>
#include <vector>
#line 1 "tools/hld.hpp"
#include <algorithm>
#include <cassert>
#include <iterator>
#include <limits>
#include <numeric>
#line 10 "tools/hld.hpp"
#include <stack>
#include <utility>
#line 1 "lib/ac-library/atcoder/dsu.hpp"
#line 7 "lib/ac-library/atcoder/dsu.hpp"
namespace atcoder {
// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
public:
dsu() : _n(0) {}
explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}
int merge(int a, int b) {
assert(0 <= a && a < _n);
assert(0 <= b && b < _n);
int x = leader(a), y = leader(b);
if (x == y) return x;
if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
parent_or_size[x] += parent_or_size[y];
parent_or_size[y] = x;
return x;
}
bool same(int a, int b) {
assert(0 <= a && a < _n);
assert(0 <= b && b < _n);
return leader(a) == leader(b);
}
int leader(int a) {
assert(0 <= a && a < _n);
if (parent_or_size[a] < 0) return a;
return parent_or_size[a] = leader(parent_or_size[a]);
}
int size(int a) {
assert(0 <= a && a < _n);
return -parent_or_size[leader(a)];
}
std::vector<std::vector<int>> groups() {
std::vector<int> leader_buf(_n), group_size(_n);
for (int i = 0; i < _n; i++) {
leader_buf[i] = leader(i);
group_size[leader_buf[i]]++;
}
std::vector<std::vector<int>> result(_n);
for (int i = 0; i < _n; i++) {
result[i].reserve(group_size[i]);
}
for (int i = 0; i < _n; i++) {
result[leader_buf[i]].push_back(i);
}
result.erase(
std::remove_if(result.begin(), result.end(),
[&](const std::vector<int>& v) { return v.empty(); }),
result.end());
return result;
}
private:
int _n;
// root node: -1 * component size
// otherwise: parent
std::vector<int> parent_or_size;
};
} // namespace atcoder
#line 1 "tools/less_by.hpp"
namespace tools {
template <class F>
class less_by {
private:
F selector;
public:
less_by(const F& selector) : selector(selector) {
}
template <class T>
bool operator()(const T& x, const T& y) const {
return selector(x) < selector(y);
}
};
}
#line 1 "tools/pow2.hpp"
#include <type_traits>
#include <cstddef>
namespace tools {
template <typename T, typename ::std::enable_if<::std::is_unsigned<T>::value, ::std::nullptr_t>::type = nullptr>
constexpr T pow2(const T x) {
return static_cast<T>(1) << x;
}
template <typename T, typename ::std::enable_if<::std::is_signed<T>::value, ::std::nullptr_t>::type = nullptr>
constexpr T pow2(const T x) {
return static_cast<T>(static_cast<typename ::std::make_unsigned<T>::type>(1) << static_cast<typename ::std::make_unsigned<T>::type>(x));
}
}
#line 16 "tools/hld.hpp"
namespace tools {
class hld {
bool m_built;
::std::vector<::std::vector<int>> m_graph;
::std::vector<int> m_edges;
::std::vector<int> m_parent;
::std::vector<int> m_depth;
::atcoder::dsu m_dsu;
::std::vector<int> m_out;
::std::vector<int> m_vid2dfs;
::std::vector<int> m_dfs2vid;
::std::vector<int> m_eid2dfs;
::std::vector<int> m_dfs2eid;
::std::vector<::std::vector<int>> m_ancestors;
public:
class vchildren_view : public ::std::ranges::view_interface<vchildren_view> {
::tools::hld const *m_parent;
int m_v;
public:
class iterator {
private:
::tools::hld const *m_parent;
int m_v;
int m_i;
public:
using difference_type = ::std::ptrdiff_t;
using value_type = int;
using reference = int;
using pointer = int*;
using iterator_category = ::std::input_iterator_tag;
iterator() = default;
iterator(::tools::hld const * const parent, const int v, const int i) :
m_parent(parent),
m_v(v),
m_i(i) {
}
reference operator*() const {
return this->m_parent->m_edges[this->m_parent->m_graph[this->m_v][this->m_i]] ^ this->m_v;
}
iterator& operator++() {
++this->m_i;
return *this;
}
iterator operator++(int) {
const auto self = *this;
++*this;
return self;
}
friend bool operator==(const iterator& lhs, const iterator& rhs) {
return lhs.m_parent == rhs.m_parent && lhs.m_v == rhs.m_v && lhs.m_i == rhs.m_i;
}
friend bool operator!=(const iterator& lhs, const iterator& rhs) {
return !(lhs == rhs);
}
};
vchildren_view() = default;
vchildren_view(::tools::hld const * const parent, const int v) :
m_parent(parent),
m_v(v) {
}
iterator begin() const {
return iterator(this->m_parent, this->m_v, 0);
};
iterator end() const {
return iterator(this->m_parent, this->m_v, this->m_parent->m_graph[this->m_v].size());
}
};
hld() = default;
explicit hld(const int n) : m_built(false), m_graph(n) {
assert(n >= 1);
}
int size() const {
return this->m_graph.size();
}
void add_edge(const int u, const int v) {
assert(!this->m_built);
assert(0 <= u && u < this->size());
assert(0 <= v && v < this->size());
this->m_graph[u].push_back(this->m_edges.size());
this->m_graph[v].push_back(this->m_edges.size());
this->m_edges.push_back(u ^ v);
}
void build(const int root) {
assert(!this->m_built);
assert(0 <= root && root < this->size());
assert(::std::ssize(this->m_edges) + 1 == this->size());
this->m_parent.resize(this->size());
this->m_depth.resize(this->size());
this->m_dsu = ::atcoder::dsu(this->size());
this->m_out.resize(this->size());
this->m_vid2dfs.resize(this->size());
this->m_dfs2vid.resize(this->size());
this->m_eid2dfs.resize(this->m_edges.size());
this->m_dfs2eid.resize(this->m_edges.size());
::std::vector<int> subtree_size(this->size());
this->m_parent[root] = ::std::numeric_limits<int>::max();
this->m_depth[root] = 0;
::std::stack<::std::pair<int, bool>> stack;
stack.emplace(root, false);
stack.emplace(root, true);
while (!stack.empty()) {
const auto [here, pre] = stack.top();
stack.pop();
if (pre) {
for (const auto eid : this->m_graph[here]) {
const auto next = this->m_edges[eid] ^ here;
if (here == root || next != (this->m_edges[this->m_parent[here]] ^ here)) {
this->m_parent[next] = eid;
this->m_depth[next] = this->m_depth[here] + 1;
stack.emplace(next, false);
stack.emplace(next, true);
}
}
} else {
subtree_size[here] = 1;
for (const auto eid : this->m_graph[here]) {
const auto child = this->m_edges[eid] ^ here;
if (here == root || child != (this->m_edges[this->m_parent[here]] ^ here)) {
subtree_size[here] += subtree_size[child];
}
}
}
}
for (int v = 0; v < this->size(); ++v) {
if (v != root) {
this->m_graph[v].erase(::std::ranges::find(this->m_graph[v], this->m_parent[v]));
}
if (this->m_graph[v].size() > 1) {
::std::iter_swap(
this->m_graph[v].begin(),
::std::ranges::max_element(
this->m_graph[v],
::tools::less_by([&](const int eid) { return subtree_size[this->m_edges[eid] ^ v]; })
)
);
}
}
int dfs_order = 0;
stack.emplace(root, false);
stack.emplace(root, true);
while (!stack.empty()) {
const auto [here, pre] = stack.top();
stack.pop();
if (pre) {
this->m_vid2dfs[here] = dfs_order;
this->m_dfs2vid[dfs_order] = here;
if (here != root) {
this->m_eid2dfs[this->m_parent[here]] = dfs_order - 1;
this->m_dfs2eid[dfs_order - 1] = this->m_parent[here];
}
++dfs_order;
if (!this->m_graph[here].empty()) {
this->m_dsu.merge(here, this->m_edges[this->m_graph[here].front()] ^ here);
}
for (auto it = this->m_graph[here].rbegin(); it != this->m_graph[here].rend(); ++it) {
stack.emplace(this->m_edges[*it] ^ here, false);
stack.emplace(this->m_edges[*it] ^ here, true);
}
} else {
this->m_out[here] = dfs_order;
}
}
this->m_built = true;
}
int depth(const int v) const {
assert(this->m_built);
assert(0 <= v && v < this->size());
return this->m_depth[v];
}
int vparent(const int v) const {
assert(this->m_built);
assert(0 <= v && v < this->size());
assert(this->m_depth[v] > 0);
return this->m_edges[this->m_parent[v]] ^ v;
}
int eparent(const int v) const {
assert(this->m_built);
assert(0 <= v && v < this->size());
assert(this->m_depth[v] > 0);
return this->m_parent[v];
}
int vancestor(const int v, const int k) {
assert(this->m_built);
assert(0 <= v && v < this->size());
assert(0 <= k && k <= this->m_depth[v]);
if (this->m_ancestors.empty()) {
this->m_ancestors.resize(this->size());
::std::vector<int> targets(this->size());
::std::iota(targets.begin(), targets.end(), 0);
targets.erase(::std::remove(targets.begin(), targets.end(), this->m_dfs2vid[0]), targets.end());
for (const auto t : targets) {
this->m_ancestors[t].push_back(this->vparent(t));
}
for (int g = 1; [&]() {
targets.erase(::std::remove_if(targets.begin(), targets.end(), [&](const int t) {
return this->m_depth[t] < ::tools::pow2(g);
}), targets.end());
return !targets.empty();
}(); ++g) {
for (const auto t : targets) {
this->m_ancestors[t].push_back(this->m_ancestors[this->m_ancestors[t][g - 1]][g - 1]);
}
}
}
int res = v;
for (int g = 0; ::tools::pow2(g) <= k; ++g) {
if ((k >> g) & 1) {
res = this->m_ancestors[res][g];
}
}
return res;
}
::tools::hld::vchildren_view vchildren(const int v) const & {
assert(this->m_built);
assert(0 <= v && v < this->size());
return ::tools::hld::vchildren_view(this, v);
}
const ::std::vector<int>& echildren(const int v) const & {
assert(this->m_built);
assert(0 <= v && v < this->size());
return this->m_graph[v];
}
int vid2dfs(const int v) const {
assert(this->m_built);
assert(0 <= v && v < this->size());
return this->m_vid2dfs[v];
}
int dfs2vid(const int i) const {
assert(this->m_built);
assert(0 <= i && i < this->size());
return this->m_dfs2vid[i];
}
int eid2dfs(const int e) const {
assert(this->m_built);
assert(0 <= e && e < this->size());
return this->m_eid2dfs[e];
}
int dfs2eid(const int i) const {
assert(this->m_built);
assert(0 <= i && i < this->size());
return this->m_dfs2eid[i];
}
int lca(int u, int v) {
assert(this->m_built);
assert(0 <= u && u < this->size());
assert(0 <= v && v < this->size());
while (!this->m_dsu.same(u, v)) {
if (this->m_depth[this->m_dsu.leader(u)] >= this->m_depth[this->m_dsu.leader(v)]) {
u = this->m_edges[this->m_parent[this->m_dsu.leader(u)]] ^ this->m_dsu.leader(u);
} else {
v = this->m_edges[this->m_parent[this->m_dsu.leader(v)]] ^ this->m_dsu.leader(v);
}
}
if (this->m_depth[u] >= this->m_depth[v]) {
return v;
} else {
return u;
}
}
::std::pair<int, int> vsubtree(const int v) const {
assert(this->m_built);
assert(0 <= v && v < this->size());
return ::std::make_pair(this->m_vid2dfs[v], this->m_out[v]);
}
::std::pair<int, int> esubtree(const int v) const {
assert(this->m_built);
assert(0 <= v && v < this->size());
return ::std::make_pair(this->m_depth[v] == 0 ? 0 : this->m_eid2dfs[this->m_parent[v]] + 1, this->m_out[v] - 1);
}
::std::vector<::std::pair<int, int>> vpath(int u, int v) {
assert(this->m_built);
assert(0 <= u && u < this->size());
assert(0 <= v && v < this->size());
::std::vector<::std::pair<int, int>> head, tail;
while (!this->m_dsu.same(u, v)) {
if (this->m_depth[this->m_dsu.leader(u)] >= this->m_depth[this->m_dsu.leader(v)]) {
head.emplace_back(this->m_vid2dfs[u] + 1, this->m_vid2dfs[this->m_dsu.leader(u)]);
u = this->m_edges[this->m_parent[this->m_dsu.leader(u)]] ^ this->m_dsu.leader(u);
} else {
tail.emplace_back(this->m_vid2dfs[this->m_dsu.leader(v)], this->m_vid2dfs[v] + 1);
v = this->m_edges[this->m_parent[this->m_dsu.leader(v)]] ^ this->m_dsu.leader(v);
}
}
if (this->m_depth[u] >= this->m_depth[v]) {
head.emplace_back(this->m_vid2dfs[u] + 1, this->m_vid2dfs[v]);
} else {
head.emplace_back(this->m_vid2dfs[u], this->m_vid2dfs[v] + 1);
}
::std::copy(tail.rbegin(), tail.rend(), ::std::back_inserter(head));
return head;
}
::std::vector<::std::pair<int, int>> epath(int u, int v) {
assert(this->m_built);
assert(0 <= u && u < this->size());
assert(0 <= v && v < this->size());
::std::vector<::std::pair<int, int>> head, tail;
while (!this->m_dsu.same(u, v)) {
if (this->m_depth[this->m_dsu.leader(u)] >= this->m_depth[this->m_dsu.leader(v)]) {
head.emplace_back(this->m_eid2dfs[this->m_parent[u]] + 1, this->m_eid2dfs[this->m_parent[this->m_dsu.leader(u)]]);
u = this->m_edges[this->m_parent[this->m_dsu.leader(u)]] ^ this->m_dsu.leader(u);
} else {
tail.emplace_back(this->m_eid2dfs[this->m_parent[this->m_dsu.leader(v)]], this->m_eid2dfs[this->m_parent[v]] + 1);
v = this->m_edges[this->m_parent[this->m_dsu.leader(v)]] ^ this->m_dsu.leader(v);
}
}
if (this->m_depth[u] > this->m_depth[v]) {
head.emplace_back(this->m_eid2dfs[this->m_parent[u]] + 1, this->m_eid2dfs[this->m_graph[v].front()]);
} else if (this->m_depth[u] < this->m_depth[v]) {
head.emplace_back(this->m_eid2dfs[this->m_graph[u].front()], this->m_eid2dfs[this->m_parent[v]] + 1);
}
::std::copy(tail.rbegin(), tail.rend(), ::std::back_inserter(head));
return head;
}
};
}
#line 1 "tools/undoable_dsu.hpp"
#line 6 "tools/undoable_dsu.hpp"
#include <tuple>
#line 10 "tools/undoable_dsu.hpp"
namespace tools {
class undoable_dsu {
private:
::std::vector<int> m_data;
int m_ncc;
::std::stack<::std::tuple<int, int, int, int, int>> m_history;
int size() const {
return this->m_data.size();
}
public:
undoable_dsu() = default;
explicit undoable_dsu(const int n) : m_data(n, -1), m_ncc(n) {
}
int leader(const int x) const {
assert(0 <= x && x < this->size());
return this->m_data[x] < 0 ? x : this->leader(this->m_data[x]);
}
bool same(const int x, const int y) const {
assert(0 <= x && x < this->size());
assert(0 <= y && y < this->size());
return this->leader(x) == this->leader(y);
}
int merge(int x, int y) {
assert(0 <= x && x < this->size());
assert(0 <= y && y < this->size());
x = this->leader(x);
y = this->leader(y);
if (this->m_data[x] > this->m_data[y]) ::std::swap(x, y);
this->m_history.emplace(x, y, this->m_data[x], this->m_data[y], this->m_ncc);
if (x == y) return x;
this->m_data[x] += this->m_data[y];
this->m_data[y] = x;
--this->m_ncc;
return x;
}
int size(const int x) const {
assert(0 <= x && x < this->size());
return -this->m_data[this->leader(x)];
}
void undo() {
assert(!this->m_history.empty());
const auto [x, y, dx, dy, ncc] = this->m_history.top();
this->m_history.pop();
this->m_data[x] = dx;
this->m_data[y] = dy;
this->m_ncc = ncc;
}
::std::vector<::std::vector<int>> groups() {
::std::vector<::std::vector<int>> res(this->size());
for (int i = 0; i < this->size(); ++i) {
res[this->leader(i)].push_back(i);
}
res.erase(::std::remove_if(res.begin(), res.end(), [](const auto& group) { return group.empty(); }), res.end());
return res;
}
int ncc() const {
return this->m_ncc;
}
};
}
#line 1 "tools/fix.hpp"
#line 6 "tools/fix.hpp"
namespace tools {
template <typename F>
struct fix : F {
template <typename G>
fix(G&& g) : F({::std::forward<G>(g)}) {
}
template <typename... Args>
decltype(auto) operator()(Args&&... args) const {
return F::operator()(*this, ::std::forward<Args>(args)...);
}
};
template <typename F>
fix(F&&) -> fix<::std::decay_t<F>>;
}
#line 1 "tools/join.hpp"
#line 5 "tools/join.hpp"
#include <sstream>
namespace tools {
template <::std::ranges::range R, typename T>
::std::string join(R&& e, const T& d) {
::std::ostringstream ss;
auto it = ::std::ranges::begin(e);
const auto end = ::std::ranges::end(e);
if (it != end) {
ss << *it;
for (++it; it != end; ++it) {
ss << d << *it;
}
}
return ss.str();
}
}
#line 11 "tests/undoable_dsu/leader.test.cpp"
int main() {
std::cin.tie(nullptr);
std::ios_base::sync_with_stdio(false);
int N;
std::cin >> N;
std::vector<int> A(N), B(N);
for (int i = 0; i < N; ++i) {
std::cin >> A[i] >> B[i];
--A[i], --B[i];
}
tools::hld graph(N);
for (int i = 0; i < N - 1; ++i) {
int U, V;
std::cin >> U >> V;
--U, --V;
graph.add_edge(U, V);
}
graph.build(0);
std::vector<int> answers(N);
std::vector<bool> has_cycle(N, false);
tools::undoable_dsu dsu(N);
tools::fix([&](auto&& dfs, const int v) -> void {
const auto same = dsu.same(A[v], B[v]);
const bool has_cycle_A = has_cycle[dsu.leader(A[v])];
const bool has_cycle_B = has_cycle[dsu.leader(B[v])];
dsu.merge(A[v], B[v]);
answers[v] = (v == 0 ? 0 : answers[graph.vparent(v)]);
if (same || has_cycle_A || has_cycle_B) {
has_cycle[dsu.leader(A[v])] = true;
}
if (!has_cycle_A || !has_cycle_B) {
++answers[v];
}
for (const auto c : graph.vchildren(v)) {
dfs(c);
}
dsu.undo();
has_cycle[dsu.leader(A[v])] = has_cycle_A;
has_cycle[dsu.leader(B[v])] = has_cycle_B;
})(0);
std::cout << tools::join(std::ranges::subrange(answers.begin() + 1, answers.end()), " ") << '\n';
return 0;
}