#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; // #pragma GCC optimize("O3") // #pragma GCC optimize("unroll-loops") // #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native") // #pragma GCC target("avx512f,avx512dq,avx512cd,avx512bw,avx512vl") using ll = long long; constexpr int INF = 1001001001; constexpr int mod = 1000000007; // constexpr int mod = 998244353; template inline bool chmax(T& x, T y){ if(x < y){ x = y; return true; } return false; } template inline bool chmin(T& x, T y){ if(x > y){ x = y; return true; } return false; } constexpr int dx[] = {1, 0, -1, 0, 1, 1, -1, -1}; constexpr int dy[] = {0, 1, 0, -1, 1, -1, 1, -1}; struct mint { int x; mint() : x(0) {} mint(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {} mint& operator+=(const mint& p){ if((x += p.x) >= mod) x -= mod; return *this; } mint& operator-=(const mint& p){ if((x -= p.x) < 0) x += mod; return *this; } mint& operator*=(const mint& p){ x = (int)(1LL * x * p.x % mod); return *this; } mint& operator/=(const mint& p){ *this *= p.inverse(); return *this; } mint operator-() const { return mint(-x); } mint operator+(const mint& p) const { return mint(*this) += p; } mint operator-(const mint& p) const { return mint(*this) -= p; } mint operator*(const mint& p) const { return mint(*this) *= p; } mint operator/(const mint& p) const { return mint(*this) /= p; } bool operator==(const mint& p) const { return x == p.x; } bool operator!=(const mint& p) const { return x != p.x; } mint pow(int64_t n) const { mint res = 1, mul = x; while(n > 0){ if(n & 1) res *= mul; mul *= mul; n >>= 1; } return res; } // x^(a^b) // warning : x と mod は互いに素 // x != 0 かつ x % mod == 0 かつ a > 0 はこれを呼び出さずに 0 を返すように処理 mint pow2(int64_t a, int64_t b) const { if(b == 0) return *this; if((a %= mod - 1) == 0) return mint(1); int64_t n = 1; while(b > 0){ if(b & 1) (n *= a) %= mod - 1; (a *= a) %= mod - 1; b >>= 1; } return pow(n); } mint inverse() const { return pow(mod - 2); } friend ostream& operator<<(ostream& os, const mint& p){ return os << p.x; } friend istream& operator>>(istream& is, mint& p){ int64_t val; is >> val; p = mint(val); return is; } }; struct HeavyLightDecomposition { using Graph = vector>; int V; Graph& g; vector subtree_size, head, in, out, par, inverse; HeavyLightDecomposition(Graph& g_) : V(g_.size()), g(g_), subtree_size(V), head(V), in(V), out(V), par(V), inverse(V) {} void calc_subtree_size(int cur, int p){ if(g[cur].size() && g[cur][0] == p){ swap(g[cur][0], g[cur].back()); } subtree_size[cur] = 1; par[cur] = p; for(auto& child : g[cur]){ if(child == p) continue; calc_subtree_size(child, cur); subtree_size[cur] += subtree_size[child]; if(subtree_size[g[cur][0]] < subtree_size[child]){ swap(g[cur][0], child); } } } void dfs(int cur, int p, int& times){ in[cur] = times++; inverse[in[cur]] = cur; for(auto& child : g[cur]){ if(child == p) continue; head[child] = (g[cur][0] == child ? head[cur] : child); dfs(child, cur, times); } out[cur] = times; } void build(int root = 0){ calc_subtree_size(root, -1); int t = 0; dfs(root, -1, t); } int get(int v, int k){ for(;;){ int u = head[v]; if(in[v] - k >= in[u]){ // u, v in same group return inverse[in[v] - k]; } k -= in[v] - in[u] + 1; v = par[u]; } } int lca(int u, int v){ for(;; v = par[head[v]]){ if(in[u] > in[v]) swap(u, v); if(head[u] == head[v]) return u; } } // path vector> get_sections(int u, int v, bool is_edge = false){ vector> res; for(;; v = par[head[v]]){ if(in[u] > in[v]) swap(u, v); if(head[u] == head[v]) break; res.emplace_back(in[head[v]], in[v] + 1); } res.emplace_back(in[u] + is_edge, in[v] + 1); return res; } // subtree pair get_section(int v, bool is_edge = false){ return {in[v] + is_edge, out[v]}; } int operator[](const int& v) const { return in[v]; } int edge(int u, int v){ return in[in[u] > in[v] ? u : v]; } }; template struct SegmentTree{ using F = function; int sz; vector seg; const F f; const Monoid M1; SegmentTree(const F f, const Monoid& M1) : f(f), M1(M1) {} SegmentTree(int n, const F f, const Monoid &M1) : f(f), M1(M1) { sz = 1; while(sz < n) sz <<= 1; seg.assign(2 * sz, M1); } void resize(int n){ sz = 1; while(sz < n) sz <<= 1; seg.assign(2 * sz, M1); } void set(int k, const Monoid &x){ seg[k + sz] = x; } void build(){ for(int k = sz - 1; k > 0; --k){ seg[k] = f(seg[k << 1], seg[k << 1 | 1]); } } void update(int k, const Monoid &x){ k += sz; seg[k] = x; while(k >>= 1){ seg[k] = f(seg[k << 1], seg[k << 1 | 1]); } } Monoid query(int a, int b){ Monoid L = M1, R = M1; for(a += sz, b += sz; a < b; a >>= 1, b >>= 1){ if(a & 1) L = f(L, seg[a++]); if(b & 1) R = f(seg[--b], R); } return f(L, R); } Monoid operator[](const int &k) const{ return seg[k + sz]; } // (type = true) : find_last // (type = false) : find_first template int find_subtree(int a, const C &check, Monoid &M, bool type){ while(a < sz){ Monoid nxt = type ? f(seg[a << 1 | type], M) : f(M, seg[a << 1 | type]); if(check(nxt)) a = a << 1 | type; else M = nxt, a = 2 * a + 1 - type; } return a - sz; } template int find_first(int a, const C &check){ Monoid L = M1; if(a <= 0){ if(check(f(L, seg[1]))) return find_subtree(1, check, L, false); return -1; } int b = sz; for(a += sz, b += sz; a < b; a >>= 1, b >>= 1){ if(a & 1){ Monoid nxt = f(L, seg[a]); if(check(nxt)) return find_subtree(a, check, L, false); L = nxt; ++a; } } return -1; } template int find_last(int b, const C &check){ Monoid R = M1; if(b >= sz){ if(check(f(seg[1], R))) return find_subtree(1, check, R, true); return -1; } int a = sz; for(b += sz; a < b; a >>= 1, b >>= 1){ if(b & 1){ Monoid nxt = f(seg[--b], R); if(check(nxt)) return find_subtree(b, check, R, true); R = nxt; } } return -1; } }; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int N; cin >> N; vector a(N), b(N); vector> g(N); for(int i = 0; i < N - 1; ++i){ cin >> a[i] >> b[i]; g[a[i]].emplace_back(b[i]); g[b[i]].emplace_back(a[i]); } HeavyLightDecomposition hld(g); hld.build(); using S = array, 2>; auto op = [&](S l, S r) -> S { S res = {0, 0, 0, 0}; for(int k = 0; k < 2; ++k){ for(int i = 0; i < 2; ++i){ for(int j = 0; j < 2; ++j){ res[i][j] += l[i][k] * r[k][j]; } } } return res; }; S E = {1, 0, 0, 1}; SegmentTree seg(N, op, E); int Q; cin >> Q; for(int q = 0; q < Q; ++q){ char c; cin >> c; if(c == 'x'){ int i; cin >> i; S x; for(int i = 0; i < 2; ++i){ for(int j = 0; j < 2; ++j){ cin >> x[i][j]; } } seg.update(hld.edge(a[i], b[i]), x); } else{ int i, j; cin >> i >> j; auto sections = hld.get_sections(i, j, true); sort(begin(sections), end(sections)); S ans = E; for(auto& [l, r] : sections){ ans = op(ans, seg.query(l, r)); } cout << ans[0][0] << ' ' << ans[0][1] << ' '; cout << ans[1][0] << ' ' << ans[1][1] << '\n'; } } return 0; }