#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 5;
int n, m, rt, mod, a[M];
vector <int> G[M];
struct segment_tree {
int tree[M * 4], tag[M * 4];
int ls(int x) { return x << 1; };
int rs(int x) { return x << 1 | 1; }
inline void push_up(int p) { tree[p] = (tree[ls(p)] + tree[rs(p)]) % mod; }
inline void change(int p, int l, int r, int k) {
tree[p] = (tree[p] + k * (r - l + 1)) % mod;
tag[p] = (tag[p] + k) % mod;
}
inline void push_down(int p, int l, int r) {
if(tag[p]) {
int mid = (l + r) >> 1;
change(ls(p), l, mid, tag[p]), change(rs(p), mid + 1, r, tag[p]);
tag[p] = 0;
}
}
inline void update(int l, int r, int p, int pl, int pr, int k) {
if(l <= pl && pr <= r) change(p, pl, pr, k);
push_down(p, pl, pr);
int mid = (l + r) >> 1;
if(pl <= mid) update(l, r, ls(p), pl, mid, k);
if(pr > mid) update(l, r, rs(p), mid + 1, pr, k);
push_up(p);
}
int qry(int l, int r, int p, int pl, int pr) {
if(l <= pl && pr <= r) return tree[p];
push_down(p, pl, pr);
int mid = (l + r) >> 1, res = 0;
if(pl <= mid) res = (res + qry(l, r, ls(p), pl, mid)) % mod;
if(pr > mid) res = (res + qry(l, r, rs(p), mid + 1, pr)) % mod;
return res;
}
}t;
int tim, fa[M], son[M], siz[M], top[M], dfn[M], dep[M];
void dfs1(int u, int f) {
fa[u] = f, dep[u] = dep[f] + 1;
for(auto v : G[u]) {
if(v == f) continue;
dfs1(v, u), siz[u] += siz[v];
if(son[u] == 0 || siz[son[u]] < siz[v]) son[u] = v;
}
siz[u]++;
}
void dfs2(int u, int f) {
dfn[u] = ++tim;
if(son[u]) top[son[u]] = top[u], dfs2(son[u], u);
for(auto v : G[u]) {
if(v == f || v == son[u]) continue;
top[v] = v, dfs2(v, u);
}
}
inline void update_path(int u, int v, int k) {
while(top[u] != top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
t.update(dfn[top[u]], dfn[u], 1, 1, n, k);
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u, v);
t.update(dfn[v], dfn[u], 1, 1, n, k);
}
int qry_path(int u, int v) {
int ans = 0;
while(top[u] != top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
ans = (ans + t.qry(dfn[top[u]], dfn[u], 1, 1, n)) % mod;
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u, v);
ans = (ans + t.qry(dfn[v], dfn[u], 1, 1, n)) % mod;
return ans;
}
inline void update_subtree(int u, int k) { t.update(dfn[u], dfn[u] + siz[u] - 1, 1, 1, n, k); }
int qry_subtree(int u) { return t.qry(dfn[u], dfn[u] + siz[u] - 1, 1, 1, n); }
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> rt >> mod;
for(int i = 1; i <= n; i++) cin >> a[i];
int u, v;
for(int i = 1; i < n; i++) {
cin >> u >> v;
G[u].push_back(v), G[v].push_back(u);
}
dfs1(1, 0), top[1] = 1, dfs2(1, 0);
int opt, x, y, z;
while(m--) {
cin >> opt;
if(opt == 1) cin >> x >> y >> z, update_path(x, y, z);
if(opt == 2) cin >> x >> y, cout << qry_path(x, y) << "\n";
if(opt == 3) cin >> x >> y, update_subtree(x, y);
if(opt == 4) cin >> x, cout << qry_subtree(x) << "\n";
}
return 0;
}