#include #include #include #include #include #include template class ModInt { using u64 = std::uint_fast64_t; public: using type = std::uint_fast64_t; u64 a; constexpr ModInt(const u64 x = 0) noexcept : a(x % Modulus) {} constexpr u64 &value() noexcept { return a; } constexpr const u64 &value() const noexcept { return a; } constexpr ModInt operator+(const ModInt rhs) const noexcept { return ModInt(*this) += rhs; } constexpr ModInt operator*(const ModInt rhs) const noexcept { return ModInt(*this) *= rhs; } constexpr ModInt &operator+=(const ModInt rhs) noexcept { a += rhs.a; if (a >= Modulus) { a -= Modulus; } return *this; } constexpr ModInt &operator*=(const ModInt rhs) noexcept { a = a * rhs.a % Modulus; return *this; } static type identity_zero() { return 0; } static type identity_one() { return 1; } }; template struct Matrix { using T = typename Semiring::type; size_t n_row, n_col; T zero = Semiring::identity_zero(), one = Semiring::identity_one(); std::vector> data; Matrix() {} Matrix(size_t n, size_t m) : n_row(n), n_col(m), data(n, std::vector(m, zero)) {} Matrix(size_t n) : n_row(n), n_col(n), data(n, std::vector(n, zero)) {} Matrix(std::vector> &arr) : n_row(arr.size()) { n_col = arr.size() ? arr[0].size() : 0; data.resize(n_row); for (size_t row = 0; row < n_row; row++) data[row] = arr[row]; } inline const std::vector &operator[](const int k) const { return data.at(k); } inline std::vector &operator[](const int k) { return data.at(k); } static Matrix identity(size_t n) { Matrix mat(n); for (size_t i = 0; i < n; i++) mat[i][i] = Semiring::identity_one(); return mat; } }; template Matrix operator*(const Matrix &A, const Matrix &B) { size_t n = A.n_row, m = B.n_col, p = A.n_col; assert(p == B.n_row); Matrix C(n, m); for (size_t i = 0; i < n; i++) for (size_t j = 0; j < m; j++) for (size_t k = 0; k < p; k++) C[i][j] = (C[i][j] + A[i][k] * B[k][j]); return C; } template struct SegmentTree { using T = typename Monoid::type; int N_, N; std::vector dat; SegmentTree(const int N_) : N_(N_) { assert(N_ > 0); N = 1; while (N < N_) { N <<= 1; } dat.assign(2 * N - 1, Monoid::identity()); } SegmentTree(const std::vector &dat_) : N_(dat_.size()) { assert(N_ > 0); N = 1; while (N < N_) { N <<= 1; } dat.assign(2 * N - 1, Monoid::identity()); std::copy(dat_.begin(), dat_.end(), dat.begin() + N - 1); for (int i = N - 2; i >= 0; i--) { dat[i] = Monoid::merge(dat[2 * i + 1], dat[2 * i + 2]); } } int size() { return N_; } void update(int key, T val) { assert(0 <= key && key < N_); key += N - 1; dat[key] = val; while (key > 0) { key = (key - 1) / 2; dat[key] = Monoid::merge(dat[2 * key + 1], dat[2 * key + 2]); // rewrite here } } // [a, b) T query(int low, int high) { assert(0 <= low && low <= high && high <= N_); return query(low, high, 0, 0, N); } T query(int low, int high, int key, int left, int right) { if (right <= low || high <= left) return Monoid::identity(); if (low <= left && right <= high) return dat[key]; int mid = (left + right) / 2; return Monoid::merge( query(low, high, 2 * key + 1, left, mid), query(low, high, 2 * key + 2, mid, right)); // rewrite here } }; template struct RangeMaxQuery { using type = T; static type identity() { return 0; } static type merge(const type &l, const type &r) { return std::max(l, r); } }; template struct RangeMatrixMult { using type = T; static type identity() { return T::identity(2); } static type merge(const type &l, const type &r) { return l * r; } }; // graph by adjacency list template struct Edge { int dst; T weight; Edge(int dst, T weight) : dst(dst), weight(weight) {} bool operator<(const Edge &e) const { return weight > e.weight; } }; template struct Graph { int V; std::vector>> E; Graph(int V) : V(V) { E.resize(V); } void add_edge(int src, int dst, T weight) { E[src].emplace_back(dst, weight); } }; template struct HeavyLightDecomposition { const Graph g; // vid: 新しい頂点番号。root から heavy 枝で繋がる頂点に優先して // 番号が付けられ、さらに root に近い頂点に繋がる部分木で同じことが行われる // heavy: 各頂点に対する heavy 枝の端点 // head: 同じ vid をもつ頂点のうち最も root に近い頂点 // parent: 各頂点の親頂点 std::vector vid, head, heavy, parent; HeavyLightDecomposition(const Graph g, int root = 0) : g(g), vid(g.V, -1), head(g.V), heavy(g.V, -1), parent(g.V) { dfs(root, -1); bfs(root); } int dfs(int v, int par) { // 部分木の頂点の数を求めて、最大の部分木の枝を heavy とする // 実装上は頂点ごとに heavy となる枝の端点を持っておく parent[v] = par; int sub = 1, max_sub = 0; for (Edge child : g.E[v]) { if (child.dst != par) { int child_sub = dfs(child.dst, v); sub += child_sub; if (child_sub > max_sub) { max_sub = child_sub; heavy[v] = child.dst; } } } return sub; } void bfs(int root = 0) { // heavy 枝でつながる頂点から番号を割り振っていく int k = 0; std::queue que({root}); while (not que.empty()) { int r = que.front(); que.pop(); for (int v = r; v != -1; v = heavy[v]) { vid[v] = k++; head[v] = r; for (Edge child : g.E[v]) { if (child.dst != parent[v] and child.dst != heavy[v]) que.push(child.dst); } } } } int lca(int u, int v) { while (head[u] != head[v]) { if (vid[u] > vid[v]) std::swap(u, v); v = parent[head[v]]; } if (vid[u] > vid[v]) std::swap(u, v); return u; } template Ret query_path(int u, int v, const Ret &one, const Query &query, const Merge &merge, bool edge = false) { // query(u, v, ti, q, f, edge):= 頂点 u と v // を通るパスに対する取得クエリを処理する。one は単位元, q // は列に対するクエリを返す演算で, f // は列とは列同士の演算結果をマージする演算である。edge を true にした場合, // 頂点クエリではなく辺クエリとして処理する。q や f // は非可換演算でもよいが最後に l,r // をマージするときにどちらかを反転する必要があることに注意。 Ret left = one, right = one; for (;; v = parent[head[v]]) { if (vid[u] > vid[v]) std::swap(u, v), std::swap(left, right); if (head[u] == head[v]) break; left = merge(query(vid[head[v]], vid[v] + 1), left); } return merge(merge(query(vid[u] + edge, vid[v] + 1), left), right); } template void add(int u, int v, const Q &q, bool edge = false) { for (;; v = parent[head[v]]) { if (vid[u] > vid[v]) std::swap(u, v); if (head[u] == head[v]) break; q(vid[head[v]], vid[v] + 1); } q(vid[u] + edge, vid[v] + 1); } }; void yosupo() { int N, Q; std::cin >> N >> Q; Graph g(N); for (int i = 1; i < N; i++) { int p; std::cin >> p; g.add_edge(p, i, 1); } HeavyLightDecomposition hld(g); for (int i = 0; i < Q; i++) { int u, v; std::cin >> u >> v; std::cout << hld.lca(u, v) << std::endl; } } void spoj_qtree() { int T; std::cin >> T; for (int t = 0; t < T; t++) { int N; std::cin >> N; Graph g(N); std::vector> edges(N - 1); for (int i = 0; i < N - 1; i++) { int a, b, w; std::cin >> a >> b >> w; a--, b--; edges[i] = std::make_tuple(a, b, w); g.add_edge(a, b, w); g.add_edge(b, a, w); } HeavyLightDecomposition hld(g); SegmentTree> rmq(N); for (auto &&e : edges) { int a, b, w; std::tie(a, b, w) = e; rmq.update(std::max(hld.vid[a], hld.vid[b]), w); } while (true) { std::string s; std::cin >> s; if (s == "DONE") break; if (s == "QUERY") { int a, b; std::cin >> a >> b; a--, b--; std::cout << hld.query_path( a, b, 0, [&](int a, int b) { return rmq.query(a, b); }, RangeMaxQuery::merge) << std::endl; } else { int idx, w; std::cin >> idx >> w; idx--; int a, b; std::tie(a, b, std::ignore) = edges[idx]; rmq.update(std::max(hld.vid[a], hld.vid[b]), w); } } } } void yuki650() { int N; std::cin >> N; Graph g(N); std::vector> edges(N - 1); for (int i = 0; i < N - 1; i++) { int a, b; std::cin >> a >> b; g.add_edge(a, b, 1); g.add_edge(b, a, 1); edges[i] = std::make_pair(a, b); } HeavyLightDecomposition hld(g); SegmentTree>>> segt(N); for (auto &&e : edges) { int a, b; std::tie(a, b) = e; segt.update(std::max(hld.vid[a], hld.vid[b]), Matrix>::identity(2)); } int Q; std::cin >> Q; for (int i = 0; i < Q; i++) { /* std::cout << "----------" << std::endl; for (int i = 0; i < N - 1; i++) { int a, b; std::tie(a, b) = edges[i]; int idx = std::max(hld.vid[a], hld.vid[b]); Matrix> val = segt.query(idx, idx + 1); std::cout << val[0][0] << ' ' << val[0][1] << ' ' << val[1][0] << ' ' << val[1][1] << std::endl; } std::cout << "----------" << std::endl; */ char t; std::cin >> t; if (t == 'x') { int idx, x00, x01, x10, x11; std::cin >> idx >> x00 >> x01 >> x10 >> x11; Matrix> mat(2, 2); mat[0][0] = x00, mat[0][1] = x01, mat[1][0] = x10, mat[1][1] = x11; int a, b; std::tie(a, b) = edges[idx]; segt.update(std::max(hld.vid[a], hld.vid[b]), mat); } else { int u, v; std::cin >> u >> v; Matrix> mat = Matrix>::identity(2); Matrix> res = hld.query_path( u, v, mat, [&](int a, int b) { return segt.query(a, b); }, RangeMatrixMult>>::merge, true); std::cout << res[0][0] << ' ' << res[0][1] << ' ' << res[1][0] << ' ' << res[1][1] << std::endl; } } } int main() { std::cin.tie(0); std::ios_base::sync_with_stdio(false); // yosupo(); // spoj_qtree(); yuki650(); return 0; }