#include <bits/stdc++.h>
using namespace std;
#define lson root<<1
#define rson root<<1|1
const int MAXN = (2e6)+10;
struct Edge
{
int to,next;
}e[MAXN<<1];
struct Segment_Tree
{
int sum,lazy,size;
}tree[MAXN<<2];
int n,q,Root,Mod,cnt,dfs_time;
int head[MAXN],deep[MAXN],top[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN];
int a[MAXN],b[MAXN];
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
inline void add(int u,int v)
{
cnt++;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
inline void push_up(int root)
{
tree[root].sum=tree[lson].sum+tree[rson].sum;
tree[root].sum%=Mod;
}
inline void push_down(int root)
{
tree[lson].lazy+=tree[root].lazy;
tree[rson].lazy+=tree[root].lazy;
tree[lson].sum+=tree[lson].size*tree[root].lazy;
tree[lson].sum%=Mod;
tree[rson].sum+=tree[rson].size*tree[root].lazy;
tree[rson].sum%=Mod;
tree[root].lazy=0;
}
void dfs1(int now,int father,int dep)
{
deep[now]=dep;
fa[now]=father;
size[now]=1;
for(int i=head[now];i;i=e[i].next){
if(e[i].to==father) continue;
dfs1(e[i].to,now,dep+1);
if(size[e[i].to]>size[son[now]]) son[now]=e[i].to;
size[now]+=size[e[i].to];
}
}
void dfs2(int now,int topfather)
{
id[now]=++dfs_time;
a[cnt]=b[now];
top[now]=topfather;
if(!son[now]) return ;
dfs2(son[now],topfather);
for(int i=head[now];i;i=e[i].next){
if(fa[now]==e[i].to || e[i].to==son[now]) continue;
dfs2(e[i].to,e[i].to);
}
}
inline void build(const int root,const int l,const int r)
{
tree[root].size=r-l+1;
if(l==r){
tree[root].sum=a[l];
return ;
}
const int mid=l+r>>1;
build(lson,l,mid);
build(rson,mid+1,r);
push_up(root);
}
inline void update(const int root,const int l,const int r,const int L,const int R,const int k)
{
if(l>=L && r<=R){
tree[root].sum+=tree[root].size*k;
tree[root].sum%=Mod;
tree[root].lazy+=k;
return ;
}
if(tree[root].lazy) push_down(root);
const int mid=l+r>>1;
if(R<=mid) update(lson,l,mid,L,R,k);
else if(L>mid) update(rson,mid+1,r,L,R,k);
else update(lson,l,mid,L,R,k),update(rson,mid+1,r,L,R,k);
push_up(root);
}
inline int query(const int root,const int l,const int r,const int L,const int R)
{
if(l>=L && r<=R) return tree[root].sum;
if(tree[root].lazy) push_down(root);
const int mid=l+r>>1;
if(R<=mid) return query(lson,l,mid,L,R);
else if(L>mid) return query(rson,mid+1,r,L,R);
else return (query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R))%Mod;
push_up(root);
}
inline void Tree_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,1,n,id[top[x]],id[x],k);
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
update(1,1,n,id[x],id[y],k);
}
inline int Tree_query_edge(int x,int y)
{
int ans=0;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ans+=query(1,1,n,id[top[x]],id[x]);
ans%=Mod;
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
return (ans+query(1,1,n,id[x],id[y]))%Mod;
}
inline void Init()
{
dfs1(Root,0,1);
dfs2(Root,Root);
build(1,1,n);
}
inline void work1()
{
int x=read(),y=read(),z=read();
Tree_update_edge(x,y,z);
}
inline void work2()
{
int x=read(),y=read();
printf("%d\n",Tree_query_edge(x,y));
}
inline void work3()
{
int x=read(),z=read();
update(1,1,n,id[x],id[x]+size[x]-1,z);
}
inline void work4()
{
int x=read();
printf("%d\n",query(1,1,n,id[x],id[x]+size[x]-1));
}
int main()
{
n=read(),q=read(),Root=read(),Mod=read();
for(int i=1;i<=n;i++) b[i]=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
Init();
while(q--){
int op=read();
if(op==1) work1();
if(op==2) work2();
if(op==3) work3();
if(op==4) work4();
}
return 0;
}