record
#include <bits/stdc++.h>
using namespace std;
#define hh putchar('\n')
#define kg putchar(' ')
#define debug puts("debug")
namespace quickread{
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void write(T x){
if(x<0) putchar('-'),x=~x+1;
if(x>9) write(x/10);
putchar(x%10^48);
}
}
using namespace quickread;
const int N=1e6+10,inf=2147483647;
int n,m,root,a[N],b[N],mod;
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
struct node{int value,tag;};
node segment_tree[N];
void push_up(int p){segment_tree[p].value=(segment_tree[ls].value+segment_tree[rs].value)%mod;}
void push_down(int l,int r,int p){
if(!segment_tree[p].tag) return void();
segment_tree[ls].tag+=segment_tree[p].tag,segment_tree[rs].tag+=segment_tree[p].tag;
segment_tree[ls].value+=(mid-l+1)*segment_tree[p].tag,segment_tree[rs].value+=(r-mid)*segment_tree[p].tag;
segment_tree[ls].value%=mod,segment_tree[rs].value%=mod;
segment_tree[p].tag=0;
}
void build(int l,int r,int p){
segment_tree[p]=node{0,0};
if(!(l^r)) return segment_tree[p].value=b[l]%mod,void();
build(l,mid,ls),build(mid+1,r,rs);
push_up(p);
}
void update(int lt,int rt,int l,int r,int p,int x){
if(lt<=l&&r<=rt) return segment_tree[p].tag+=x,segment_tree[p].value+=(r-l+1)*x,segment_tree[p].value%=mod,void();
push_down(l,r,p);
if(lt<=mid) update(lt,rt,l,mid,ls,x);
if(rt>mid) update(lt,rt,mid+1,r,rs,x);
push_up(p);
}
int query(int lt,int rt,int l,int r,int p){
if(lt<=l&&r<=rt) return segment_tree[p].value;
push_down(l,r,p);
int ans=0;
if(lt<=mid) ans+=query(lt,rt,l,mid,ls);
if(rt>mid) ans+=query(lt,rt,mid+1,r,rs);
return ans;
}
int num,son[N],id[N],fa[N],dep[N],siz[N],top[N];
vector<int> tree[N];
void dfs1(int u,int f){
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(int v:tree[u])
if(v^f){
dfs1(v,u);
siz[u]+=siz[v];
if(!son[u]||siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int topx){
id[u]=++num;a[num]=b[u];
top[u]=topx;
if(!son[u]) return void();
dfs2(son[u],topx);
for(int v:tree[u])
if(v^fa[u]&&v^son[u]) dfs2(v,v);
}
void update_range(int x,int y,int z){
while(top[x]^top[y]){
if(dep[top[y]]<dep[top[y]]) swap(x,y);
update(id[top[x]],id[x],1,n,1,z);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(id[x],id[y],1,n,1,z);
}
int query_range(int x,int y){
int ans=0;
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=(ans+query(id[top[x]],id[x],1,n,1))%mod;
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=query(id[x],id[y],1,n,1);
return ans%mod;
}
signed main(){
read(n),read(m),read(root),read(mod);
for(int i=1;i<=n;++i) read(b[i]);
for(int i=1,u,v;i<n;++i) read(u),read(v),tree[u].push_back(v),tree[v].push_back(u);
dfs1(root,0),dfs2(root,root),build(1,n,1);
for(int i=1,opt,x,y,z;i<=m;++i){
read(opt);
if(opt==1) read(x),read(y),read(z),update_range(x,y,z);
else if(opt==2) read(x),read(y),write(query_range(x,y)),hh;
else if(opt==3) read(x),read(y),update(id[x],id[x]+siz[x]-1,1,n,1,y);
else read(x),write(query(id[x],id[x]+siz[x]-1,1,n,1)%mod),hh;
}
return 0;
}