#include <algorithm>
#include <cstdio>
#include <iostream>
#include <memory.h>
#define ll long long
#define N 100009
#define lc t << 1
#define rc t << 1 | 1
using namespace std;
ll p;
ll x[N];
struct Seg {
int l, r;
int len;
ll sum;
ll toAdd;
} tr[N << 1];
inline void pushup(int t)
{
Seg* now = &tr[t];
Seg *l_c = &tr[lc], *r_c = &tr[rc];
now->sum = (l_c->sum + r_c->sum) % p;
}
inline void pushdown(int t)
{
Seg* now = &tr[t];
if (now->l == now->r)
return;
Seg *l_c = &tr[lc], *r_c = &tr[rc];
ll A = now->toAdd;
now->toAdd = 0;
(l_c->sum += l_c->len * A % p) %= p;
(r_c->sum += r_c->len * A % p) %= p;
(l_c->toAdd += A) %= p, (r_c->toAdd += A) %= p;
}
void builder(int t, int l, int r)
{
Seg* now = &tr[t];
now->l = l, now->r = r;
now->len = r - l + 1;
now->toAdd = 0;
if (l == r) {
now->sum = x[l];
return;
}
int mid = (l + r) >> 1;
builder(lc, l, mid);
builder(rc, mid + 1, r);
pushup(t);
}
void AddSeq(int t, int l, int r, ll val)
{
Seg* now = &tr[t];
if (l <= now->l && now->r <= r) {
(now->sum += val * now->len) %= p;
(now->toAdd += val) %= p;
return;
}
pushdown(t);
int mid = (now->l + now->r) >> 1;
if (l <= mid)
AddSeq(lc, l, r, val);
if (r > mid)
AddSeq(rc, l, r, val);
pushup(t);
}
ll QuerySeq(int t, int l, int r)
{
Seg* now = &tr[t];
if (l <= now->l && now->r <= r)
return now->sum;
ll res = 0;
pushdown(t);
int mid = (now->l + now->r) >> 1;
if (l <= mid)
(res += QuerySeq(lc, l, r)) %= p;
if (r > mid)
(res += QuerySeq(rc, l, r)) %= p;
return res;
}
struct edge {
int to;
int nxt;
} E[N << 1];
int tt, hd[N];
void addedge(int u, int v)
{
E[++tt] = (edge) {v, hd[u]};
hd[u] = tt;
}
int sz[N], ff[N], d[N];
int top[N], son[N];
ll nodeW[N];
int n, m, root;
void dfs_sz(int u, int fa)
{
ff[u] = fa;
sz[u] = 1;
d[u] = d[fa] + 1;
for (int i = hd[u]; i; i = E[i].nxt) {
int v = E[i].to;
if (v == fa)
continue;
dfs_sz(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]] || sz[v] == sz[son[u]] && v < son[u])
son[u] = v;
}
}
int tI[N], tO[N];
int timer = 0;
void dfs_top(int u, int fa)
{
tI[u] = ++timer;
if (u == son[fa])
top[u] = top[fa];
else
top[u] = u;
dfs_top(son[u], u);
for (int i = hd[u]; i; i = E[i].nxt) {
int v = E[i].to;
if (v == fa || v == son[u])
continue;
dfs_top(v, u);
}
tO[u] = timer;
}
void init()
{
for (int u = 1; u <= n; ++u)
x[tI[u]] = nodeW[u];
builder(1, 1, n);
}
void AddLine(int u, int v, ll val)
{
while (top[u] != top[v]) {
if (d[top[u]] > d[top[v]]) {
AddSeq(1, tI[top[u]], tI[u], val);
u = ff[top[u]];
} else {
AddSeq(1, tI[top[v]], tI[v], val);
v = ff[top[v]];
}
}
if (d[u] > d[v])
swap(u, v);
if (u != v)
AddSeq(1, tI[u], tI[v], val);
}
ll QueryLine(int u, int v)
{
ll res = 0;
while (top[u] != top[v]) {
if (d[top[u]] > d[top[v]]) {
(res += QuerySeq(1, tI[top[u]], tI[u])) %= p;
u = ff[top[u]];
} else {
(res += QuerySeq(1, tI[top[v]], tI[v])) %= p;
v = ff[top[v]];
}
}
if (d[u] > d[v])
swap(u, v);
if (u != v)
(res += QuerySeq(1, tI[u], tI[v])) %= p;
return res;
}
void AddSubTree(int u, ll val)
{
AddSeq(1, tI[u], tO[u], val);
}
ll QuerySubTree(int u)
{
return QuerySeq(1, tI[u], tO[u]);
}
void quick()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
int main()
{
quick();
cin >> n >> m >> root >> p;
for (int i = 1; i <= n; ++i)
cin >> nodeW[i];
for (int i = 1, u, v; i < n; ++i) {
cin >> u >> v;
addedge(u, v);
addedge(v, u);
}
dfs_sz(root, 0);
dfs_top(root, 0);
init();
while (m--) {
int op;
int u, v;
ll val;
cin >> op;
switch (op) {
case 1:
cin >> u >> v >> val;
AddLine(u, v, val);
break;
case 2:
cin >> u >> v;
cout << QueryLine(u, v) << endl;
break;
case 3:
cin >> u >> val;
AddSubTree(u, val);
break;
case 4:
cin >> u;
cout << QuerySubTree(u) << endl;
break;
}
}
return 0;
}