RT
#include<bits/stdc++.h>
using namespace std;
#define re register
#define il inline
#define ll long long
const int maxsize=400010;
ll s,mod,tot,num,n,m;
ll head[maxsize],dep[maxsize],sum[maxsize],a[maxsize],lazy[maxsize],fa[maxsize],son[maxsize],dfn[maxsize],ch[maxsize],top[maxsize],size[maxsize];
il ll read()
{
ll x=0,w=0;
char ch=0;
while(!isdigit(ch))
{
w|=ch=='-';
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return w?-x:x;
}
il void write(ll x)
{
if(x>9)
write(x/10);
if(x<0)
{
x=-x;
putchar('-');
}
putchar(x%10+'0');
}
struct edge
{
ll to,nxt;
} e[maxsize];
il void add(int u,int v)
{
e[num++].to=v;
e[num].nxt=head[u];
head[u]=num;
}
// 线段树↓
il void build(int x,int l,int r)
{
if(l==r) {sum[x]=a[l];return;}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
sum[x]=sum[x<<1]+sum[x<<1|1];
sum[x]%=mod;
return;
}
il void down(int x,int l,int r)//标记下放
{
int mid=(l+r)>>1;
lazy[x<<1]+=lazy[x];
lazy[x<<1]%=mod;
lazy[x<<1|1]+=lazy[x];
lazy[x<<1|1]%=mod;
sum[x<<1]+=lazy[x]*(mid+1-l);
sum[x<<1]%=mod;
sum[x<<1|1]+=lazy[x]*(r-mid);
sum[x<<1|1]%=mod;
lazy[x]=0;
return;
}
il void change(int x,int lnow,int rnow,int lwant,int rwant,ll addnum)//区间每个数都加addnum
{
if(lwant<=lnow&&rwant>=rnow){sum[x]+=addnum*(rnow-lnow+1);sum[x]%=mod;lazy[x]+=addnum;lazy[x]%=mod;return;}
if(lnow>rwant||rnow<lwant)return;
int mid=(lnow+rnow)>>1;
if(lazy[x]) down(x,lnow,rnow);
if(mid>=lwant)change(x<<1,lnow,mid,lwant,rwant,addnum);
if(mid<rwant)change(x<<1|1,mid+1,rnow,lwant,rwant,addnum);
sum[x]=(sum[x<<1]+sum[x<<1|1])%mod;
return;
}
il ll ask(int x,int lnow,int rnow,int lwant,int rwant)
{
if(lwant<=lnow&&rwant>=rnow) return sum[x]%mod;
if(lwant>rnow||rwant<lnow) return 0;
int mid=(lnow+rnow)>>1;
if(lazy[x]) down(x,lnow,rnow);
ll leftans=0,rightans=0;
if(mid>=lwant)leftans=ask(x<<1,lnow,mid,lwant,rwant);
if(mid<rwant)rightans=ask(x<<1|1,mid+1,rnow,lwant,rwant);
return (leftans%mod+rightans%mod)%mod;
}
//////////////////////////////////////////////////////////
il void dfs1(int x)//求某节点子树大小+轻重儿子
{
size[x]=1;
for(re int i=head[x]; i; i=e[i].nxt)
{
int v=e[i].to;
if(!dep[v])//OR if(v!=fa[x])
{
dep[v]=dep[x]+1;//向下 深度+1
fa[v]=x;
dfs1(v);
size[x]+=size[v];//更新长度
if(size[v]>size[son[x]]) son[x]=v;//求重儿子
}
}
}
il void dfs2(int x,int xtop)//求DFS序
{
//l[]表示这个点的dfs序
//然后a[]是以dfs序为下标(维护链)的当前点的点值
//ch[]是题目给出的点值
//top[]记录的是这个点所在的链的顶端的那个点,用来跳lca
dfn[x]=++tot;
a[tot]=ch[x];
top[x]=xtop;
if(son[x]) dfs2(son[x],xtop);
for(re int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v!=fa[x]&&v!=son[x]) dfs2(v,v);
}
return;
}
il ll getedgesum(int x,int y)//查询路径上所有节点值的和 操作2
{
ll sum=0;
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
sum+=ask(1,1,tot,dfn[fx],dfn[fy]);
x=fa[fx],fx=top[x];//跳一跳
}
if(dfn[x]>dfn[y]) swap(x,y);
sum+=ask(1,1,tot,dfn[x],dfn[y]);
return sum;
}
il void addedgesum(int x,int y,int addnum)//在路径上每个点的值都加addnum 操作1
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
change(1,1,tot,dfn[fx],dfn[x],addnum);
x=fa[fx],fx=top[x];
}
if(dfn[x]>dfn[y]) swap(x,y);
change(1,1,tot,dfn[x],dfn[y],addnum);
}
int main()
{
n=read();m=read();s=read();mod=read();
for(re int i=1;i<=n;i++) ch[i]=read(),ch[i]%=mod;
for(re int i=1;i<n;i++)
{
int u=read(),v=read();
add(u,v);add(v,u);
}
dep[s]=1;fa[s]=0;
dfs1(s);dfs2(s,s);build(1,1,n);
for(re int i=1;i<=m;i++)
{
int op=read();
if(op==1){int u=read(),v=read(),w=read();addedgesum(u,v,w%mod);}
if(op==2){int u=read(),v=read();write(getedgesum(u,v));puts("");}
if(op==3){int x=read(),w=read();change(1,1,n,dfn[x],dfn[x]+size[x]-1,w);}
if(op==4){int x=read();write(ask(1,1,n,dfn[x],dfn[x]+size[x]-1)%mod);puts("");}
}
return 0;
}