#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include <iostream>
using namespace std;
#define ll long long
const int Maxn=100001;
int n,m,r,p,a[Maxn],x,y,now[Maxn*2],id[Maxn*2],Cnt,topf[Maxn*2],top[Maxn*2];
int head[Maxn*2],next[Maxn*2],to[Maxn*2],cnt,son[Maxn*2],size[Maxn*2],deep[Maxn*2],father[Maxn*2];
struct node
{
int w;
int left;
int right;
int v;
}tree[Maxn*4];
void Spread_Tree(int pp)
{
if(tree[pp].v) {
tree[pp*2].w+=tree[pp].v*(tree[pp*2].right-tree[pp*2].left+1);
tree[pp*2+1].w+=tree[pp].v*(tree[pp*2+1].right-tree[pp*2+1].left+1);
tree[pp*2].v+=tree[pp].v;
tree[pp*2+1].v+=tree[pp].v;
tree[pp*2].w%=p;
tree[pp*2+1].w%=p;
tree[pp].v=0;
}
}
void Build_Tree(int index,int l,int r)
{
tree[index].left=l;
tree[index].right=r;
if(l==r) return;
int mid=(l+r)/2;
Build_Tree(index*2,l,mid);
Build_Tree(index*2+1,mid+1,r);
}
int Insert_Tree(int index)
{
if(tree[index].left==tree[index].right) {
tree[index].w=now[tree[index].left]%p;
return tree[index].w%p;
}
tree[index].w=(Insert_Tree(index*2)+Insert_Tree(index*2+1))%p;
return tree[index].w%p;
}
int Query_Tree(int index,int l,int r)
{
if(tree[index].left>=l&&tree[index].right<=r) return tree[index].w%p;
int mid=(tree[index].left+tree[index].right)/2;
Spread_Tree(index);
long long ans=0;
if(l<=mid)
ans+=Query_Tree(index*2,l,r);
if(r>mid)
ans+=Query_Tree(index*2+1,l,r);
return ans%p;
}
void Change_Tree(int index,int l,int r,int k)
{
if(tree[index].right<=r&&tree[index].left>=l) {
tree[index].w+=k*(tree[index].right-tree[index].left+1);
tree[index].v+=k;
return;
}
Spread_Tree(index);
int mid=(tree[index].left+tree[index].right)/2;
if(l<=mid)
Change_Tree(index*2,l,r,k);
if(r>mid)
Change_Tree(index*2+1,l,r,k);
tree[index].w=(tree[index*2].w+tree[index*2+1].w)%p;
}
void Insert(int u,int v)
{
++cnt;
next[cnt]=head[u];
head[u]=cnt;
to[cnt]=v;
}
void dfs_1(int u,int f)
{
deep[u]=deep[f]+1;
size[u]=1;
father[u]=f;
for(int i=head[u]; i; i=next[i]) {
int v=to[i];
if(v!=f) {
dfs_1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
}
void dfs_2(int u,int topf)
{
id[u]=++Cnt;
now[Cnt]=a[u];
top[u]=topf;
if(!son[u]) return;
dfs_2(son[u],topf);
for(int i=head[u]; i; i=next[i]) {
int v=to[i];
if(v==father[u]||v==son[u]) continue;
dfs_2(v,v);
}
}
int Query_Interval(int x,int y)
{
int ans=0;
while(top[x]!=top[y]) {
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ans+=(Query_Tree(1,id[top[x]],id[x]));
ans%=p;
x=father[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
ans+=Query_Tree(1,id[x],id[y]);
return ans%p;
}
int Change_Interval(int x,int y,int k)
{
while(top[x]!=top[y]) {
if(deep[top[x]]<deep[top[y]]) swap(x,y);
Change_Tree(1,id[top[x]],id[x],k);
x=father[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
Change_Tree(1,id[x],id[y],k);
}
void Change_Point(int index,int k)
{
Change_Tree(1,id[index],id[index]+size[index]-1,k);
}
int Query_Point(int index)
{
return Query_Tree(1,id[index],id[index]+size[index]-1);
}
int main()
{
cin>>n>>m>>r>>p;
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1; i<n; i++) {
cin>>x>>y;
Insert(x,y);
Insert(y,x);
}
dfs_1(r,0);
dfs_2(r,r);
Build_Tree(1,1,n);
Insert_Tree(1);
for(int i=1; i<=m; i++) {
int A,B,C,D;
cin>>A;
if(A==1) { cin>>B>>C>>D; Change_Interval(B,C,D); }
if(A==2) { cin>>B>>C; cout<<Query_Interval(B,C)<<endl; }
if(A==3) { cin>>B>>C; Change_Point(B,C); }
if(A==4) { cin>>B; cout<<Query_Point(B)<<endl; }
}
return 0;
}