#include<bits/stdc++.h>
#define maxn 5000010
#define lc k<<1
#define rc k<<1|1
using namespace std;
struct node1{
int l,r,w,len,lzy;
}tree[maxn];
int a[maxn];
struct node2{
int to,nxt;
}edge[maxn];
int h[maxn],cnt0;
int son[maxn],fa[maxn],deep[maxn],siz[maxn],idx[maxn],top[maxn],wt[maxn],cnt;
long long res;
int n,m,MOD,root;
void add(int u,int v){
edge[++cnt0].to=v;
edge[cnt0].nxt=h[u];
h[u]=cnt0;
}
void dfs1(int u,int f){
deep[u]=deep[f]+1;
fa[u]=f;
siz[u]=1;
for(int i=h[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if(v==f){
continue;
}
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]){
son[u]=v;
}
}
}
void dfs2(int u,int tp){
idx[u]=++cnt;
wt[cnt]=u;
top[u]=tp;
if(!son[u]){
return;
}
dfs2(son[u],tp);
for(int i=h[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if(idx[v])continue;
dfs2(v,v);
}
return ;
}
void update(int k){
tree[k].w=(tree[lc].w+tree[rc].w+MOD)%MOD;
}
void pushdown(int k){
if(!tree[k].lzy)return;
tree[lc].w=(tree[lc].w+tree[rc].len*tree[k].lzy)%MOD;
tree[rc].w=(tree[rc].w+tree[rc].len*tree[k].lzy)%MOD;
tree[lc].lzy=(tree[lc].lzy+tree[k].lzy)%MOD;
tree[rc].lzy=(tree[rc].lzy+tree[k].lzy)%MOD;
tree[k].lzy=0;
}
void build(int k,int l,int r){
tree[k].l=l;
tree[k].r=r;
tree[k].len=r-l+1;
if(l==r){
tree[k].w=a[wt[l]];
return ;
}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
update(k);
}
void Range_update(int k,int l,int r,int v){
if(l<=tree[k].l && tree[k].r<=r){
tree[k].w+=tree[k].len*v;
tree[k].lzy+=v;
return ;
}
pushdown(k);
int mid=(tree[k].l+tree[k].r)>>1;
if(l<=mid) Range_update(lc,l,r,v);
if(r>mid) Range_update(rc,l,r,v);
update(k);
}
int Range_qry(int k,int l,int r){
int ans=0;
if(l<=tree[k].l && tree[k].r<=r){
return tree[k].w;
}
pushdown(k);
int mid=(tree[k].l+tree[k].r)>>1;
if(l<=mid) ans=(ans+Range_qry(lc,l,r))%MOD;
if(r>mid) ans=(ans+Range_qry(rc,l,r))%MOD;
return ans;
}
int Range_tree_qry(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])swap(x,y);
ans=(ans+Range_qry(1,idx[top[x]],idx[x]))%MOD;
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
ans=(ans+Range_qry(1,idx[x],idx[y]))%MOD;
return ans;
}
void Range_tree_update(int x,int y,int v){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])swap(x,y);
Range_update(1,idx[top[x]],idx[x],v);
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
Range_update(1,idx[x],idx[y],v);
}
int main(){
memset(h,-1,sizeof(h));
cin>>n>>m>>root>>MOD;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
}
deep[0]=1;
dfs1(root,0);
dfs2(root,root);
build(1,1,n);
while(m--){
int opt,x,y,z;
cin>>opt;
if(opt==1){
cin>>x>>y>>z;
z=z%MOD;
Range_tree_update(x,y,z);
}else if(opt==2){
cin>>x>>y;
cout<<Range_tree_qry(x,y)<<"\n";
}else if(opt==3){
cin>>x>>z;
Range_update(1,idx[x],idx[x]+siz[x]-1,z%MOD);
}else{
cin>>x;
cout<<Range_qry(1,idx[x],idx[x]+siz[x]-1)<<"\n";
}
}
return 0;
}