#include <stdio.h>
#define Maxn 100005
int N,M,R,P;
int tree[Maxn<<2],tag[Maxn<<2];
int head[Maxn<<1],to[Maxn<<1],next[Maxn<<1],cnt;
int top[Maxn],fa[Maxn],val[Maxn],dfn[Maxn],dep[Maxn],rank[Maxn],siz[Maxn],son[Maxn],dfw;
inline int lc(int p){return p<<1;}
inline int rc(int p){return p<<1|1;}
inline void push_up(int p){tree[p]=(tree[lc(p)]+tree[rc(p)])%P;}
inline void build_tree(int p,int l,int r)
{
if(l==r)
{
tree[p]=val[rank[l]]%P;
return;
}
int mid=(l+r)>>1;
build_tree(lc(p),l,mid);
build_tree(rc(p),mid+1,r);
push_up(p);
}
inline void update_node(int p,int l,int r,int k)
{
tree[p]=(tree[p]+(r-l+1)*k%P)%P;
tag[p]=tag[p]+k;
return;
}
inline void push_down(int p,int l,int r)
{
int mid=(l+r)>>1;
update_node(lc(p),l,mid,tag[p]);
update_node(rc(p),mid+1,r,tag[p]);
tag[p]=0;
}
inline void update_tree(int p,int l,int r,int ul,int ur,int k)
{
if(ul<=l&&r<=ur)
{
update_node(p,l,r,k);
return;
}
push_down(p,l,r);
int mid=(l+r)>>1;
if(mid>=ul) update_tree(lc(p),l,mid,ul,ur,k);
if(mid<ur) update_tree(rc(p),mid+1,r,ul,ur,k);
push_up(p);
}
inline int query_tree(int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) return tree[p]%P;
push_down(p,l,r);
int mid=(l+r)>>1;
long long ans=0;
if(mid>=ql) ans+=query_tree(lc(p),l,mid,ql,qr);
if(mid+1<=qr) ans+=query_tree(rc(p),mid+1,r,ql,qr);
return ans%P;
}
inline void dfs1(int p,int f)
{
fa[p]=f;
dep[p]=dep[f]+1;
siz[p]=1;
for(int i=head[p];i;i=next[i]) if(to[i]!=f)
{
dfs1(to[i],p);
siz[p]+=siz[to[i]];
if(siz[to[i]]>siz[son[p]]) son[p]=to[i];
}
}
void dfs2(int p,int tp)
{
dfn[p]=(++dfw);
rank[dfw]=p;
top[p]=tp;
if(son[p]) dfs2(son[p],tp);
for(int i=head[p];i;i=next[i]) if(to[i]!=fa[p]&&to[i]!=son[p])
{
dfs2(to[i],to[i]);
}
}
inline void swap(int &a,int &b){int tmp=a;a=b;b=tmp;}
inline int q1(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_tree(1,1,N,dfn[u],dfn[top[u]]))%P;
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ans+=query_tree(1,1,N,dfn[u],dfn[v]);
return ans%P;
}
inline void u1(int u,int v,int k)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update_tree(1,1,N,dfn[u],dfn[top[u]],k);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update_tree(1,1,N,dfn[u],dfn[v],k);
}
inline int q2(int p){return query_tree(1,1,N,dfn[p],dfn[p]+siz[p]-1);}
inline void u2(int p,int k){update_tree(1,1,N,dfn[p],dfn[p]+siz[p]-1,k);}
inline void add(int u,int v)
{
next[++cnt]=head[u];
to[cnt]=v;
head[u]=cnt;
}
int main()
{
scanf("%d%d%d%d",&N,&M,&R,&P);
for(int i=1;i<=N;i++) scanf("%d",&val[i]);
for(int i=1,u,v;i<N;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs1(R,R);
dfs2(R,R);
build_tree(1,1,N);
int opt,a,b,c;
for(int i=1;i<=M;i++)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d%d",&a,&b,&c);
u1(a,b,c);
}
else if(opt==2)
{
scanf("%d%d",&a,&b);
printf("%d\n",q1(a,b)%P);
}
else if(opt==3)
{
scanf("%d%d",&a,&b);
u2(a,b);
}
else
{
scanf("%d",&a);
printf("%d\n",q2(a));
}
}
}