link
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,root,mod,op,x,y,k;
int deep[N];
int fa[N];
int son[N];
int tot[N];
int top[N];
int idx[N],cnt;
int b[N],a[N];
struct node{
int u,v,nxt;
}edge[N];
int head[N];
int num=1;
inline void add_edge(int x,int y){
edge[num].u=x;
edge[num].v=y;
edge[num].nxt=head[x];
head[x]=num++;
}
int w[N*4],wl[N*4],wr[N*4],lzy[N*4];
void pushup(int u){
w[u]=(w[u*2]+w[u*2+1])%mod;
}
void build(int u,int l,int r){
wl[u]=l,wr[u]=r;
if(l==r){
w[u]=a[l]%mod;
return;
}
int mid=(l+r)/2;
build(u*2,l,mid),build(u*2+1,mid+1,r);
pushup(u);
}
void maketag(int u,int len,int x){
lzy[u]+=x;
w[u]+=x*len;
lzy[u]%=mod,w[u]%=mod;
}
void pushdown(int u,int l,int r){
int mid=(l+r)/2;
maketag(u*2,mid-l+1,lzy[u]);
maketag(u*2+1,r-mid,lzy[u]);
lzy[u]=0;
}
bool InRange(int L,int R,int l,int r){
return (L<=l)&&(R>=r);
}
bool OutofRange(int L,int R,int l,int r){
return (l>R)||(r<L);
}
int query(int u,int l,int r,int L,int R){
if(InRange(L,R,l,r)) return w[u];
else if(!OutofRange(L,R,l,r)){
int mid=(l+r)/2;
pushdown(u,l,r);
return (query(u*2,l,mid,L,R)+query(u*2+1,mid+1,r,L,R))%mod;
}else return 0;
}
void update(int u,int l,int r,int L,int R,int x){
if(InRange(L,R,l,r)) maketag(u,r-l+1,x);
else if(!OutofRange(L,R,l,r)){
int mid=(l+r)/2;
pushdown(u,l,r);
update(u*2,l,mid,L,R,x);
update(u*2+1,mid+1,r,L,R,x);
pushup(u);
}
}
int dfs1(int now,int f,int dep){
deep[now]=dep;
fa[now]=f;
tot[now]=1;
int maxson=-1;
for(int i=head[now];i!=-1;i=edge[i].nxt){
if(edge[i].v==f) continue;
tot[now]+=dfs1(edge[i].v,now,dep+1);
if(tot[edge[i].v]>maxson)
maxson=tot[edge[i].v],son[now]=edge[i].v;
}
return tot[now];
}
void dfs2(int now,int topf){
idx[now]=++cnt;
a[cnt]=b[now];
top[now]=topf;
if(!son[now]) return;
dfs2(son[now],topf);
for(int i=head[now];i!=-1;i=edge[i].nxt)
if(!idx[edge[i].v])
dfs2(edge[i].v,edge[i].v);
}
int tree_sum(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ans=(ans+query(1,1,n,idx[top[x]],idx[x]))%mod;
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
ans=(ans+query(1,1,n,idx[x],idx[y]))%mod;
return ans;
}
void tree_add(int x,int y,int val){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
update(1,1,n,idx[top[x]],idx[x],val);
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
update(1,1,n,idx[x],idx[y],val);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>root>>mod;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1,x,y;i<=n-1;i++){
cin>>x>>y;
add_edge(x,y),add_edge(y,x);
}
dfs1(root,0,1);
dfs2(root,root);
build(1,1,n);
while(m--){
cin>>op;
if(op==1){
cin>>x>>y>>k;
tree_add(x,y,k%mod);
}
if(op==2){
cin>>x>>y;
cout<<tree_sum(x,y)<<'\n';
}
if(op==3){
cin>>x>>k;
update(1,1,n,idx[x],idx[x]+tot[x]-1,k%mod);
}
if(op==4){
cin>>x;
cout<<query(1,1,n,idx[x],idx[x]+tot[x]-1)<<'\n';
}
}
return 0;
}