#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const int INF = 0x3FFFFFFF;
typedef long long int64;
typedef vector<int> vi;
typedef pair<int, int> pii;
int n, q, rt, mod;
int val[MAXN];
vi edge[MAXN];
int dep[MAXN];
int par[MAXN];
int son[MAXN];
int size[MAXN];
int id[MAXN];
int nval[MAXN];
int top[MAXN];
struct node
{
int l, r;
int64 sum;
int64 add = 0;
} tr[MAXN * 4];
void dfs1(int now, int p, int d)
{
par[now] = p;
dep[now] = d;
size[now] = 1;
if (edge[now].size() == 1 && now != rt)
{
return;
}
for (auto &u : edge[now])
{
if (u == par[now])
continue;
dfs1(u, now, d + 1);
size[now] += size[u];
if (size[u] > size[son[now]])
son[now] = u;
}
}
int cnt = 0;
void dfs2(int now, int topp)
{
top[now] = topp;
id[now] = ++cnt;
nval[cnt] = val[now];
if (!son[now])
return;
dfs2(son[now], topp);
for (auto &u : edge[now])
{
if (u == son[now] || u == par[now])
continue;
dfs2(u, u);
}
}
inline void pushup(int now)
{
tr[now].sum = (tr[now << 1].sum + tr[now << 1 | 1].sum) % mod;
}
inline void calc(int now, int k)
{
tr[now].sum = (tr[now].sum + k * (tr[now].r - tr[now].l + 1)) % mod;
tr[now].add = (tr[now].add + k) % mod;
}
inline void pushdown(int now)
{
calc(now << 1, tr[now].add%mod);
calc(now << 1 | 1, tr[now].add%mod);
tr[now].add = 0;
}
void buildtree(int now, int l, int r)
{
tr[now].l = l;
tr[now].r = r;
if (l == r)
{
tr[now].sum = nval[l] % mod;
return;
}
int mid = (l + r) >> 1;
buildtree(now << 1, l, mid);
buildtree(now << 1 | 1, mid + 1, r);
pushup(now);
}
void addtoSegment(int now, int l, int r, int k)
{
if (l <= tr[now].l && r >= tr[now].r)
{
calc(now, k%mod);
return;
}
pushdown(now);
int mid = (tr[now].l + tr[now].r) >> 1;
if (l <= mid)
addtoSegment(now << 1, l, r, k);
if (r > mid)
addtoSegment(now << 1 | 1, l, r, k);
pushup(now);
}
int query(int now, int l, int r)
{
if (l <= tr[now].l && r >= tr[now].r)
return tr[now].sum % mod;
pushdown(now);
int mid = (tr[now].l + tr[now].r) >> 1;
int64 ret = 0;
if (l <= mid)
ret = (ret + query(now << 1, l, r)) % mod;
if (r > mid)
ret = (ret + query(now << 1 | 1, l, r)) % mod;
return ret;
}
void addtoPath(int u, int v, int k)
{
if (top[u] == top[v])
{
if (id[u] > id[v])
swap(u, v);
addtoSegment(1, id[u], id[v], k);
return;
}
if (dep[u] < dep[v])
swap(u, v);
addtoSegment(1, id[top[u]], id[u], k);
addtoPath(par[top[u]], v, k);
}
int queryPath(int u, int v)
{
if (top[u] == top[v])
{
if (id[u] > id[v])
swap(u, v);
return query(1, id[u], id[v]);
}
if (dep[u] < dep[v])
swap(u, v);
int ret = query(1, id[top[u]], id[u]) % mod;
ret = (ret + queryPath(par[top[u]], v)) % mod;
return ret;
}
inline void addtoSubtree(int root, int k)
{
int r = id[root];
addtoSegment(1, r, r + size[root] - 1, k);
}
inline int querySubtree(int root)
{
int r = id[root];
return query(1, r, r + size[root] - 1) % mod;
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> q >> rt >> mod;
for (int i = 1; i <= n; i++)
cin >> val[i];
for (int i = 1; i < n; i++)
{
int x, y;
cin >> x >> y;
edge[x].push_back(y);
edge[y].push_back(x);
}
dfs1(rt, 0, 1);
dfs2(rt, rt);
buildtree(1, 1, n);
while (q--)
{
int op, x, y, z;
cin >> op;
switch (op)
{
case 1:
cin >> x >> y >> z;
addtoPath(x, y, z);
break;
case 2:
cin >> x >> y;
cout << queryPath(x, y) << '\n';
break;
case 3:
cin >> x >> z;
addtoSubtree(x, z);
break;
case 4:
cin >> x;
cout << querySubtree(x) << '\n';
break;
}
}
return 0;
}