#include<bits/stdc++.h>
#define INF 0x7fffffff
#define ll long long
using namespace std;
ll read(){
ll k=1,x=0;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') k=-1; ch=getchar();}
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
return k*x;
}
const int N=3e5+10;
struct EDGE{
ll to,pre;
}edge[N];
ll a[N],size[N],fa[N],dep[N],id[N],w[N],son[N],top[N],head[N];
ll cnt,mod,n,m,r;
//================== 以下为线段树模板 ====================
#define up(x) t[x].sum=t[x<<1].sum+t[x<<1|1].sum
struct node{
ll l,r;
ll sum,lazy;
}t[N<<2];
ll len(ll p){
return t[p].r-t[p].l+1;
}
void brush(ll p,ll k){
t[p].lazy+=k%mod;
t[p].sum+=len(p)*k%mod;
}
void push_down(ll p){
brush(p<<1,t[p].lazy);
brush(p<<1|1,t[p].lazy);
t[p].lazy=0;
}
void build(ll p,ll l,ll r){
t[p].l=l;t[p].r=r;
if (l==r){
t[p].sum=w[l];return;
}
ll mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
up(p);
}
void update(ll p,ll l,ll r,ll k){
if (t[p].l>=l&&t[p].r<=r){
brush(p,k);
return;
}
push_down(p);
ll mid=t[p].l+t[p].r>>1;
if (mid>=l) update(p<<1,l,r,k);
if (r>=mid+1) update(p<<1|1,l,r,k);
up(p);
}
ll getans(ll p,ll l,ll r){
if (t[p].l>=l&&t[p].r<=r)
return t[p].sum;
push_down(p);
ll ans=0;
ll mid=t[p].l+t[p].r>>1;
if (l<=mid) ans+=getans(p<<1,l,r);
if (r>=mid+1) ans+=getans(p<<1|1,l,r);
return ans%mod;
}
//================== 以上为线段树模板 ====================
//================== 以下为树剖操作模板 ====================
inline void updatetree(ll s,ll t,ll c){ //s到t的简单路径加上c
while (top[s]!=top[t]){ //倍增思想,深度大的往上跳
if (dep[top[s]]>dep[top[t]]) swap(s,t); //默认t深度大
update(1,id[top[t]],id[t],c); //先维护这一段链
t=fa[top[t]]; //跳到这个链头的父节点,维护下一个链
}
if (dep[s]>dep[t]) swap(s,t); //默认t深度大
update(1,id[s],id[t],c); //维护s与现在t(同深度)的链
}
inline ll getanstree(ll s,ll t){ //查询s到t的简单路径权值和
ll ans=0;
while (top[s]!=top[t]){ //倍增思想,深度大的往上跳
if (dep[top[s]]>dep[top[t]]) swap(s,t); //默认t深度大
ans+=getans(1,id[top[t]],id[t]); //先查询这一段链
t=fa[top[t]]; //跳到这个链头的父节点,查询下一个链
}
if (dep[s]>dep[t]) swap(s,t); //默认t深度大
ans+=getans(1,id[s],id[t]); //查询s与现在t(同深度)的链
return ans%mod;
}
inline void updateson(ll x,ll t){ //以x为根的子树加上t
update(1,id[x],id[x]+size[x]-1,t);
}
inline ll getansson(ll x){ //查询以x为根的子树权值和
return getans(1,id[x],id[x]+size[x]-1)%mod;
}
//================== 以上为树剖操作模板 ====================
//================== 以下为dfs ====================
inline void dfs1(ll x,ll fath,ll deep){
dep[x]=deep;
fa[x]=fath;
size[x]=1;
ll maxson=-1;
for(ll i=head[x];i;i=edge[i].pre){
ll y=edge[i].to;
if(y==fath) continue;
dfs1(y,x,deep+1);
size[x]+=size[y];
if(size[y]>maxson){
son[x]=y;
maxson=size[y];
}
}
}
ll tim;
inline void dfs2(ll x,ll fst){
id[x]=++tim;
w[tim]=a[x];
top[x]=fst;
if(!son[x])return;
dfs2(son[x],fst);
for(ll i=head[x];i;i=edge[i].pre){
ll y=edge[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
//================== 以上为dfs ====================
inline void add(ll u,ll v){ //链式前向星
edge[++cnt].pre=head[u];
edge[cnt].to=v;
head[u]=cnt;
}
int main(){
freopen("in.in","r",stdin) ;
// freopen("in.out","w",stdout) ;
n=read();m=read();r=read();mod=read();
for(ll i=1;i<=n;i++) a[i]=read()%mod;
for(ll i=1;i<n;i++){
ll a,b;
a=read();b=read();
add(a,b);add(b,a);
}
dfs1(r,0,1);
dfs2(r,r);
build(1,1,n);
for (ll i=1;i<=m;i++){
ll opt=read(),x=read();
if (opt==1){
ll y=read(),z=read()%mod;
updatetree(x,y,z);
}
if (opt==2){
ll y=read()%mod;
cout<<getanstree(x,y)<<endl;
}
if (opt==3){
ll z=read()%mod;
updateson(x,z);
}
if (opt==4) cout<<getansson(x)<<endl;
}
return 0;
}