#include<iostream>
#include<cstdio>
#include<cmath>
#define lc p<<1
#define rc p<<1|1
using namespace std;
int n,m,k,w_make_id,w_opt,x,y,z,w_root,w_mod,w_head[100005],w_dep[100005],w_val[100005],w_new_val[100005],w_heavy_son[100005],w_chain_top[100005],w_id[100005],w_father[100005],w_size[100005];
struct node{
int l,r;
long long w_sum,w_add;
}w_tree[400005];
struct Node{
int w_next,w_to;
}w_edge[400005];
void w_add(int u,int v)
{
w_edge[++k].w_next=w_head[u];
w_edge[k].w_to=v;
w_head[u]=k;
}
void w_push_up(int p)
{
w_tree[p].w_sum=w_tree[lc].w_sum+w_tree[rc].w_sum;
}
void w_push_down(int p)
{
if(!w_tree[p].w_add)
return;
w_tree[lc].w_sum+=(w_tree[lc].r-w_tree[lc].l+1)*w_tree[p].w_add;
w_tree[rc].w_sum+=(w_tree[rc].r-w_tree[rc].l+1)*w_tree[p].w_add;
w_tree[lc].w_add+=w_tree[p].w_add;
w_tree[rc].w_add+=w_tree[p].w_add;
w_tree[p].w_add=0;
}
void w_dfs_0(int u,int v)
{
w_father[u]=v;
w_dep[u]=w_dep[v]+1;
w_size[u]=1;
for(int i=w_head[u];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==v)
continue;
w_dfs_0(w_edge[i].w_to,u);
w_size[u]+=w_size[w_edge[i].w_to];
if(w_size[w_edge[i].w_to]>w_size[w_heavy_son[u]])
w_heavy_son[u]=w_edge[i].w_to;
}
}
void w_dfs_1(int u,int w_top)
{
w_chain_top[u]=w_top;
w_id[u]=++w_make_id;
w_new_val[w_make_id]=w_val[u];
if(!w_heavy_son[u])
return;
w_dfs_1(w_heavy_son[u],w_top);
for(int i=w_head[u];i;i=w_edge[i].w_next)
{
if(w_edge[i].w_to==w_father[u]||w_edge[i].w_to==w_heavy_son[u])
continue;
w_dfs_1(w_edge[i].w_to,w_edge[i].w_to);
}
}
void w_build(int p,int x,int y)
{
w_tree[p]={x,y,w_new_val[x],0};
if(x==y)
return;
int m=x+y>>1;
w_build(lc,x,m);
w_build(rc,m+1,y);
w_push_up(p);
}
void w_update(int p,int x,int y,long long k)
{
if(x<=w_tree[p].l&&w_tree[p].r<=y)
{
w_tree[p].w_sum+=(w_tree[p].r-w_tree[p].l+1)*k;
w_tree[p].w_add+=k;
return;
}
int m=w_tree[p].l+w_tree[p].r>>1;
w_push_down(p);
if(x<=m)
w_update(lc,x,y,k);
if(y>m)
w_update(rc,x,y,k);
w_push_up(p);
}
long long w_query(int p,int x,int y)
{
if(x<=w_tree[p].l&&w_tree[p].r<=y)
return w_tree[p].w_sum;
int m=w_tree[p].l+w_tree[p].r>>1;
w_push_down(p);
long long w_temp=0;
if(x<=m)
w_temp+=w_query(lc,x,y);
if(y>m)
w_temp+=w_query(rc,x,y);
return w_temp;
}
void w_update_chain(int x,int y,int k)
{
while(w_chain_top[x]!=w_chain_top[y])
{
if(w_dep[w_chain_top[x]]<w_dep[w_chain_top[y]])
swap(x,y);
w_update(1,w_id[w_chain_top[x]],w_id[x],k);
x=w_father[x];
}
if(w_dep[x]<w_dep[y])
swap(x,y);
w_update(1,w_id[y],w_id[x],k);
}
long long w_query_chain(int x,int y)
{
long long w_temp=0;
while(w_chain_top[x]!=w_chain_top[y])
{
if(w_dep[w_chain_top[x]]<w_dep[w_chain_top[y]])
swap(x,y);
w_temp+=w_query(1,w_id[w_chain_top[x]],w_id[x]);
x=w_father[x];
}
if(w_dep[x]<w_dep[y])
swap(x,y);
w_temp+=w_query(1,w_id[y],w_id[x]);
return w_temp;
}
void w_update_son_tree(int x,int k)
{
w_update(1,w_id[x],w_id[x]+w_size[x]-1,k);
}
long long w_query_son_tree(int x)
{
return w_query(1,w_id[x],w_id[x]+w_size[x]-1);
}
int main()
{
scanf("%d %d %d %d",&n,&m,&w_root,&w_mod);
for(int i=1;i<=n;i++)
scanf("%d",&w_val[i]);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
w_add(x,y);
w_add(y,x);
}
w_dfs_0(w_root,0);
w_dfs_1(w_root,w_root);
w_build(1,1,n);
while(m--)
{
scanf("%d %d",&w_opt,&x);
if(w_opt==1)
{
scanf("%d %d",&y,&z);
w_update_chain(x,y,z);
}
else if(w_opt==2)
{
scanf("%d",&y);
printf("%d\n",w_query_chain(x,y)%w_mod);
}
else if(w_opt==3)
{
scanf("%d",&y);
w_update_son_tree(x,y);
}
else
printf("%d\n",w_query_son_tree(x)%w_mod);
}
return 0;
}