TLE*9
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
//#pragma GCC optimize(2) //吸氧
//#pragma GCC optimize(3,"Ofast","inline") //吸臭氧
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
inline int read(){
char ch=getchar();
int x=0,w=1;
while(ch<'0'||ch>'9'){
if(ch=='-')
w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*w;
}
int n,m,r,mod,id=0,cnt=0;
int head[1000005],a[1000005],tag[4000005],t[4000005],val[1000005];
int depth[1000005],size[1000005],son[1000005],fa[1000005],top[1000005],num[1000005];
struct node{
int v,next;
}edge[2000005];
void add(int u,int v){
edge[++id].v=v;
edge[id].next=head[u];
head[u]=id;
}
int ls(int p){
return p<<1;
}
int rs(int p){
return p<<1|1;
}
void pushup(int p){
t[p]=(t[ls(p)]+t[rs(p)])%mod;
}
void build(int p,int l,int r){
if(l==r){
t[p]=(a[l]%mod);
return;
}
int mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
pushup(p);
}
void f(int p,int l,int r,int k){
tag[p]+=k;
t[p]=(t[p]+k*(r-l+1))%mod;
}
void pushdown(int p,int l,int r){
if(tag[p]){
int mid=(l+r)>>1;
f(ls(p),l,mid,tag[p]);
f(rs(p),mid+1,r,tag[p]);
tag[p]=0;
}
}
void update(int tl,int tr,int p,int l,int r,int k){
if(tl<=l&&r<=tr){
f(p,l,r,k);
return;
}
pushdown(p,l,r);
int mid=(l+r)>>1;
if(tl<=mid)
update(tl,tr,ls(p),l,mid,k);
if(tr>mid)
update(tl,tr,rs(p),mid+1,r,k);
pushup(p);
}
int query(int tl,int tr,int p,int l,int r){
int res=0;
if(tl<=l&&r<=tr){
res=t[p]%mod;
return res;
}
pushdown(p,l,r);
int mid=(l+r)>>1;
if(tl<=mid)
res=(res+query(tl,tr,ls(p),l,mid))%mod;
if(tr>mid)
res=(res+query(tl,tr,rs(p),mid+1,r))%mod;
res%=mod;
return res;
}
void dfs1(int u,int f){
depth[u]=depth[f]+1;//标记每个点的深度
fa[u]=f;//标记每个点的父亲
size[u]=1;//标记每个非叶子节点的子树大小
int max_son=-1e9;//记录重儿子的儿子数
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v;
if(v==f)
continue;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>max_son){
son[u]=v;//标记每个非叶子节点的重儿子编号
max_son=size[v];
}
}
}
void dfs2(int u,int topf){
num[u]=++cnt;//标记每个点的新编号
a[cnt]=val[u];//把每个点的初始值赋到新编号上来
top[u]=topf;//标记这个点所在链的顶端
if(!son[u])//如果没有儿子则返回
return;
dfs2(son[u],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v;
if(v==fa[u] || v==son[u])
continue;
dfs2(v,v);//对于每一个轻儿子都有一条从它自己开始的链
}
}
int qRange(int x,int y){
int res=0;
while(top[x]!=top[y]){//当两个点不在同一条链上
if(depth[top[x]]<depth[top[y]])//把x点改为所在链顶端的深度更深的那个点
swap(x,y);
res+=query(num[top[x]],num[x],1,1,n);//res加上x点到x所在链顶端这一段区间的点权和
res%=mod;
x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
}
//直到两个点处于一条链上
if(depth[x]>depth[y])//把x点改为深度更深的那个点
swap(x,y);
res=(res+query(num[x],num[y],1,1,n))%mod;//再加上此时两个点的区间和
return res;
}
int qSon(int u){
int res=(query(num[u],num[u]+size[u]-1,1,1,n))%mod;//子树区间右端点为id[x]+siz[x]-1
return res;
}
void upRange(int x,int y,int k){
k%=mod;
while(top[x]!=top[y]){
if(depth[top[x]]<depth[top[y]])
swap(x,y);
update(num[top[x]],num[x],1,1,n,k);
}
if(depth[x]>depth[y])
swap(x,y);
update(num[x],num[y],1,1,n,k);
}
void upSon(int x,int k){
k%=mod;
update(num[x],num[x]+size[x]-1,1,1,n,k);
}
int main(int argc, const char * argv[]) {
n=read();m=read();r=read();mod=read();
for(int i=1;i<=n;i++)
val[i]=read();
for(int i=1;i<=n-1;i++){
int x=read(),y=read();
add(x,y);
add(y,x);
}
depth[0]=0;
dfs1(r,0);
dfs2(r,r);
build(1,1,n);
for(int i=1;i<=m;i++){
int op=read();
int x,y,z;
if(op==1){
x=read();y=read();z=read();
upRange(x,y,z);
}
if(op==2){
x=read();y=read();
printf("%d\n",qRange(x,y));
}
if(op==3){
x=read();z=read();
upSon(x,z);
}
if(op==4){
x=read();
printf("%d\n",qSon(x));
}
}
return 0;
}