#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
//const int N = 100;
int p;
int to[N], nex[N], head[N];
int dfs[N], fa[N], sz[N], dep[N], nd[N], son[N], top[N], d[N];
int idx;//边的序号
void add(int a, int b)//增加一条a到b的有向边
{
to[++idx] = b;//边的终点
nex[idx] = head[a];//同一起点的上一条边
head[a] = idx;//以a为起点的最后一条边
}
int cnt = 0;
void dfs1(int u,int father, int depth)
{
dep[u] = depth;
fa[u] = father;
sz[u] = 1;
for(int i = head[u]; i!= -1; i = nex[i])
{
int t = to[i];
if(t == fa[u]) continue;
dfs1(t, u, depth+1);
sz[u] += sz[t];
if(sz[son[u]] < sz[t]) son[u] = t;
}
}
void dfs2(int u, int tt)//节点u的顶部节点为tt
{
dfs[u] = ++cnt;
top[u] = tt;
nd[cnt] = d[u];//dfs序为cnt的值
if(!son[u]) return;//没有重孩子
dfs2(son[u], tt);//先搜索重孩子
for(int i = head[u]; i != -1; i = nex[i])
{
int t = to[i];
if(t == son[u] || t == fa[u]) continue;
dfs2(t, t);//轻链开端的顶部就是他自己
}
}
struct node//节点rt所包含的信息
{
int l,r,lz,v;
}tree[N<<2];
void push_up(int rt)
{
tree[rt].v = (tree[rt<<1].v + tree[rt<<1|1].v)%p;
}
void push_down(int rt)
{
if(tree[rt].lz)
{
tree[rt<<1].lz += tree[rt].lz;
tree[rt<<1|1].lz += tree[rt].lz;
tree[rt<<1].v = tree[rt<<1].v + (tree[rt<<1].r - tree[rt<<1].l+1)*tree[rt].lz;
tree[rt<<1|1].v = tree[rt<<1|1].v + (tree[rt<<1|1].r - tree[rt<<1|1].l+1)*tree[rt].lz;
tree[rt].lz = 0;
}
}
void bulid(int rt, int l, int r)
{
tree[rt] = {l, r};
if(l == r)
{
tree[rt].v = nd[l];
return ;
}
int mid = (l + r)>>1;
bulid(rt<<1, l, mid);
bulid(rt<<1|1, mid+1, r);
push_up(rt);
}
void modify(int rt, int l, int r, int k)
{
if(l <= tree[rt].l && tree[rt].r <= r)
{
tree[rt].lz += k;
tree[rt].v = (tree[rt]. v +k * (tree[rt].r - tree[rt].l + 1))%p;
return;
}
push_down(rt);
int mid = (tree[rt].l+tree[rt].r)>>1;
if(l <= mid) modify(rt<<1, l, r, k);
if(mid + 1 <= r) modify(rt<<1|1, l ,r ,k);
push_up(rt);
}
int query(int rt, int l, int r)
{
if(l <= tree[rt].l && tree[rt].r <= r)
{
return tree[rt].v%p;
}
push_down(rt);
int ans = 0;
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(l <= mid) ans = (ans + query(rt<<1, l, r))%p;
if(mid + 1 <= r) ans =( ans + query(rt<<1|1, l, r))%p;
return ans%p;
}
void modify_tree(int u, int k)
{
modify(1, dfs[u], dfs[u] + sz[u] - 1, k);
}
int query_tree(int u)
{
return query(1, dfs[u], dfs[u] + sz[u] - 1)%p;
}
void modify_path(int u, int v, int k)
{
while(top[u] != top[v])
{
if(dep[top[u]] < dep[top[v]]) swap(u, v);
modify(1, dfs[top[u]], dfs[u], k);
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u , v);
modify(1, dfs[v], dfs[u], k);
}
int query_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 + query(1, dfs[top[u]], dfs[u]))%p;
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u, v);
ans = (ans + query(1, dfs[v], dfs[u]))%p;
return ans;
}
int main()
{
memset(head, -1, sizeof(head));
int n,m,root;
cin>>n>>m>>root>>p;
for(int i = 1; i <= n; ++i)
{
cin>>d[i];
}
for(int i = 1; i <= n-1; ++i)
{
int a,b;
cin>>a>>b;
add(a, b);
add(b, a);
}
dfs1(root, -1, 1);
dfs2(root, root);
bulid(1, 1, n);
for(int i = 1; i <= m; ++i)
{
int op,x,y,z;;
cin>>op;
if(op == 1)
{
cin>>x>>y>>z;
modify_path(x, y, z);
}
else if(op == 2)
{
cin>>x>>y;
cout<<query_path(x, y)%p<<endl;
}
else if(op == 3)
{
cin>>x>>z;
modify_tree(x, z);
}
else if(op == 4)
{
cin>>x;
cout<<query_tree(x)%p<<endl;
}
}
}