#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 5;
ll P;
int n, q, s, times;
int a[N], id[N], o[N], size[N], deep[N], son[N], top[N], f[N];
int num, h[N];
struct edge
{
int v, next, dis;
} e[N << 1];
ll lazy[N];
inline void insert(int u, int v)
{
e[++num].v = v;
e[num].next = h[u];
h[u] = num;
}
struct tree
{
int l, r;
ll a;
} t[N << 2];
inline int read()
{
int s = 0, f = 0;
char ch = getchar();
while (ch < 48 || ch > 57)
{
if (ch == '-')
f = 1;
ch = getchar();
}
while (ch >= 48 && ch <= 57)
{
s = (s << 3) + (s << 1) + (ch ^ 48);
ch = getchar();
}
return f ? -s : s;
}
inline void dfs1(int u, int fa)
{
f[u] = fa;
deep[u] = deep[fa] + 1;
size[u] = 1;
int maxson = -1;
for (int i = h[u]; i; i = e[i].next)
{
int v = e[i].v;
if (v == fa)
continue;
dfs1(v, u);
size[u] += size[v];
if (maxson < size[v])
{
maxson = size[v];
son[u] = v;
}
}
}
inline void dfs2(int u, int utop)
{
id[u] = ++times;
a[times] = o[u];
top[u] = utop;
if (!son[u])
return;
dfs2(son[u], utop);
for (int i = h[u]; i; i = e[i].next)
{
int v = e[i].v;
if (v == f[u] || v == son[u])
continue;
dfs2(v, v);
}
}
inline void pushup(int p)
{
t[p].a = (t[p << 1].a + t[p << 1 | 1].a) % P;
}
inline void pushdown(int p)
{
if (lazy[p])
{
t[p << 1].a = (t[p << 1].a + (t[p << 1].r - t[p << 1].l + 1) * lazy[p]) % P;
t[p << 1 | 1].a = (t[p << 1 | 1].a + (t[p << 1 | 1].r - t[p << 1 | 1].l + 1) * lazy[p]) % P;
lazy[p << 1] = (lazy[p << 1] + lazy[p]) % P;
lazy[p << 1 | 1] = (lazy[p << 1 | 1] + lazy[p]) % P;
lazy[p] = 0;
}
}
inline void build(int p, int l, int r)
{
t[p].l = l;
t[p].r = r;
if (l == r)
{
t[p].a = a[l];
return;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
inline void update(int p, int x, int y, int k)
{
if (x <= t[p].l && t[p].r <= y)
{
lazy[p] = (lazy[p] + k) % P;
t[p].a = (t[p].a + (t[p].r - t[p].l + 1) * k) % P;
return;
}
pushdown(p);
int mid = (t[p].l + t[p].r) >> 1;
if (x <= mid)
{
update(p << 1, x, y, k);
}
if (y > mid)
{
update(p << 1 | 1, x, y, k);
}
pushup(p);
}
inline int query(int p, int x, int y)
{
if (x <= t[p].l && t[p].r <= y)
{
return t[p].a;
}
pushdown(p);
int mid = (t[p].l + t[p].r) >> 1, ans = 0;
if (x <= mid)
{
ans = (ans + query(p << 1, x, y)) % P;
}
if (y > mid)
{
ans = (ans + query(p << 1 | 1, x, y)) % P;
}
return ans;
}
inline void update_edge(int x, int y, int k)
{
while (top[x] != top[y])
{
if (deep[top[x]] < deep[top[y]])
swap(x, y);
update(1, id[top[x]], id[x], k);
x = f[top[x]];
}
if (deep[x] > deep[y])
swap(x, y);
update(1, id[x], id[y], k);
}
inline int query_edge(int x, int y)
{
ll ans = 0;
while (top[x] != top[y])
{
if (deep[top[x]] < deep[top[y]])
swap(x, y);
ans = (ans + query(1, id[top[x]], id[x])) % P;
x = f[top[x]];
}
if (deep[x] > deep[y])
swap(x, y);
ans = (ans + query(1, id[x], id[y])) % P;
return ans;
}
inline void update_tree(int x, int k)
{
update(1, id[x], id[x] + size[x] - 1, k);
}
inline int query_tree(int x)
{
return query(1, id[x], id[x] + size[x] - 1);
}
signed main()
{
freopen("P3384.in", "r", stdin);
n = read(), q = read(), s = read(), P = read();
for (int i = 1; i <= n; ++i)
{
o[i] = read();
}
for (int i = 1; i < n; ++i)
{
int u = read(), v = read();
insert(u, v);
insert(v, u);
}
dfs1(s, 0);
dfs2(s, s);
build(1, 1, n);
while (q--)
{
int x, y, z, k = read();
printf("%d %d\n", q, k);
if (k == 1)
{
x = read(), y = read(), z = read();
update_edge(x, y, z % P);
}
else if (k == 2)
{
x = read(), y = read();
printf("%d\n", query_edge(x, y));
}
else if (k == 3)
{
x = read(), z = read();
update_tree(x, z % P);
}
else
{
x = read();
printf("%d\n", query_tree(x));
}
}
return 0;
}