#include <bits/stdc++.h> #define OnlineJudge
using std::cin; using std::cout;
constexpr int N = 1e5 + 5; int head[N], cnt = 0; int n, m, root, P, u, v, initial_value[N], value[N], opt; int top[N], size[N], son[N], dep[N], fa[N], id[N], num = 0;
class Edge { public: int to, next;
Edge(const int to = 0, const int next = 0) : to(to), next(next) {} } e[N << 1];
void add_edge(int u, int v) { e[cnt] = Edge(v, head[u]); head[u] = cnt++; }
struct SegmentTree { struct Node { int left, right; long long sum, add;
explicit Node(const int l = 0, const int r = 0, const int s = 0, const int a = 0) : left(l), right(r), sum(s), add(a) {} };
Node tr[N << 2];
SegmentTree() {}
void make_lazy(const int k, const long long value) { (tr[k].sum += (tr[k].right - tr[k].left + 1) * value) %= P; (tr[k].add += value) %= P; }
void push_up(const int k) { const int left_child = k << 1, right_child = k << 1 | 1; tr[k].sum = tr[left_child].sum + tr[right_child].sum; tr[k].sum %= P; }
void push_down(const int k) { if (tr[k].add == 0) return void();
const int left_child = k << 1, right_child = k << 1 | 1; make_lazy(left_child, tr[k].add); make_lazy(right_child, tr[k].add);
tr[k].add = 0; }
void build_tree(const int k, const int left, const int right) { tr[k] = Node(left, right);
if (tr[k].left == tr[k].right) { tr[k].sum = value[left] % P; return void(); }
const int mid = (tr[k].left + tr[k].right) >> 1; const int lc = k << 1, rc = k << 1 | 1;
build_tree(lc, left, mid); build_tree(rc, mid + 1, right);
push_up(k); }
void modify(const int k, const int left, const int right, const long long value) { if (tr[k].left >= left && tr[k].right <= right) { make_lazy(k, value); return void(); }
push_down(k);
const int mid = (tr[k].left + tr[k].right) >> 1; const int lc = k << 1, rc = k << 1 | 1;
if (right <= mid) { modify(lc, left, right, value); } else if (left > mid) { modify(rc, left, right, value); } else { modify(lc, left, mid, value); modify(rc, mid + 1, right, value); }
push_up(k); }
long long query(const int k, const int left, const int right) { if (tr[k].left >= left && tr[k].right <= right) { return tr[k].sum % P; }
push_down(k);
const int mid = (tr[k].left + tr[k].right) >> 1; const int lc = k << 1, rc = k << 1 | 1;
if (right <= mid) { return query(lc, left, right) % P; } if (left > mid) { return query(rc, left, right) % P; } return (query(lc, left, mid) + query(rc, mid + 1, right)) % P; } } seg;
void dfs(int u, int father) { fa[u] = father; size[u] = 1; dep[u] = dep[father] + 1;
for (int i = head[u]; ~i; i = e[i].next) { int v = e[i].to; if (v == father) { continue; }
dfs(v, u); size[u] += size[v]; if (son[u] == 0 || size[v] > size[son[u]]) { son[u] = v; } } }
void dfs2(int u, int link_top) { top[u] = link_top; id[u] = ++num; value[num] = initial_value[u];
if (son[u] == 0) return; dfs2(son[u], link_top);
for (int i = head[u]; ~i; i = e[i].next) { int v = e[i].to; if (v == fa[u] || v == son[u]) { continue; }
dfs2(v, v); } }
using std::swap;
void update_range(int u, int v, const long long value) { while (top[u] != top[v]) { #ifndef OnlineJudge cout << u << ' ' << top[u] << " " << v << " " << top[v] << '\n'; #endif if (dep[top[u]] < dep[top[v]]) { swap(u, v); }
seg.modify(1, id[top[u]], id[u], value); u = fa[top[u]]; }
if (dep[u] > dep[v]) { swap(u, v); } seg.modify(1, id[u], id[v], value); }
long long query_range(int u, int v) { long long retval = 0;
while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) { swap(u, v); }
(retval += seg.query(1, id[top[u]], id[u])) %= P; u = fa[top[u]]; }
if (dep[u] > dep[v]) { swap(u, v); } (retval += seg.query(1, id[u], id[v])) %= P;
return retval; }
void update_subtree(const int u, const long long value) { seg.modify(1, id[u], id[u] + size[u] - 1, value); }
long long query_subtree(const int u) { return seg.query(1, id[u], id[u] + size[u] - 1); }
using std::cin; using std::cout;
int main() { std::ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
std::memset(head, -1, sizeof(head)); cin >> n >> m >> root >> P;
for (int i = 1; i <= n; i++) { cin >> initial_value[i]; }
for (int i = 1; i < n; i++) { cin >> u >> v;
add_edge(u, v); add_edge(v, u); }
dfs(root, 0); dfs2(root, root);
seg.build_tree(1, 1, n);
for (int i = 1; i <= m; i++) { cin >> opt;
switch (opt) { int x, y; long long z;
case 1: cin >> x >> y >> z; update_range(x, y, z); break; case 2: cin >> x >> y; cout << query_range(x, y) << '\n'; break; case 3: cin >> x >> z; update_subtree(x, z); break; case 4: cin >> x; cout << query_subtree(x) << '\n'; break; default: break; } }
return 0; }
|