#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10,M=N<<1;
int head[N],ver[M],nxt[M],tot=0;
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
int n,m;
int poi[N];
int id[N],nwpoi[N],cnt=0;
int dpt[N],size_[N],top[N]; //每个点的深度,每个点为根的子树大小,所在重链的顶点
int fa[N],son[N];//父节点,重儿子
ll tree[N<<2],lazy[N<<2];//线段树相关
void dfs1(int x,int f) //寻找重儿子
{
dpt[x]=dpt[f]+1; //记录深度
fa[x]=f; //记录父节点
size_[x]=1; //子树至少有他自己
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(y==f) continue;
dfs1(y,x);
size_[x]+=size_[y]; //子树大小加起来
if(size_[son[x]]<size_[y]) son[x]=y; //打擂台记录重儿子
}
}
void dfs2(int x,int t) //t:当前重链的起点是谁
{
id[x]=++cnt; //记录DFS序
nwpoi[cnt]=poi[x]; //节点的权值重新排序
top[x]=t; //记录这个节点所在重链的起点
if(!son[x]) return ;
dfs2(son[x],t); //优先搜索重儿子,重儿子一定在当前这条重链内
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y); //轻儿子一定是另外一条重链的开头
}
}
inline void push_up(int node)
{
tree[node]=tree[node<<1]+tree[node<<1|1];
}
void func(int node,int start,int end,int k)
{
lazy[node]=lazy[node]+k;
tree[node]=tree[node]+k*(end-start+1);
}
void push_down(int node,int start,int end)
{
if(lazy[node]==0) return;
ll mid=start+end>>1;
int lnode=node<<1;
int rnode=node<<1|1;
func(lnode,start,mid,lazy[node]);
func(rnode,mid+1,end,lazy[node]);
lazy[node]=0;
}
void build(int node,int start,int end)
{
lazy[node]=0;
if(start==end)
{
tree[node]=nwpoi[start];
return ;
}
int mid=start+end>>1;
int lnode=node<<1;
int rnode=node<<1|1;
build(lnode,start,mid);
build(rnode,mid+1,end);
push_up(node);
}
void update(int node,int start,int end,int l,int r,int val)
{
if(l<=start&&end<=r)
{
tree[node]+=val*(end-start+1);
lazy[node]+=val;
return ;
}
push_down(node,start,end);
int mid=start+end>>1;
int lnode=node<<1;
int rnode=node<<1|1;
if(l<=mid) update(lnode,start,mid,l,r,val);
if(r>mid) update(rnode,mid+1,end,l,r,val);
push_up(node);
}
ll query(int node,int start,int end,int l,int r)
{
if(end<l||start>r) return 0;
if(l<=start&&end<=r)
return tree[node];
push_down(node,start,end);
int mid=start+end>>1;
int lnode=node<<1;
int rnode=node<<1|1;
ll lsum=query(lnode,start,mid,l,r);
ll rsum=query(rnode,mid+1,end,l,r);
return lsum+rsum;
push_up(node);
}
void update_path(int x,int y,int k)
{
while(top[x]!=top[y]) //如果不在同一重链
{
if(dpt[top[x]]<dpt[top[y]]) swap(x,y);
update(1,1,n,id[top[x]],id[x],k);
x=fa[top[x]]; //向上跳,直到位于同一重链
}
if(dpt[x]<dpt[y]) swap(x,y);
update(1,1,n,id[y],id[x],k);
}
ll query_path(int x,int y)
{
ll res=0;
while(top[x]!=top[y])
{
if(dpt[top[x]]<dpt[top[y]]) swap(x,y);
res+=query(1,1,n,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dpt[x]<dpt[y]) swap(x,y);
res+=query(1,1,n,id[y],id[x]);
return res;
}
void update_tree(int x,int k)
{
int l=id[x],r=id[x]+size_[x]-1; //子树的dfs序是连续的一段
update(1,1,n,l,r,k);
}
ll query_tree(int x)
{
int l=id[x],r=id[x]+size_[x]-1;
return query(1,1,n,l,r);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",poi+i);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1,1);
dfs2(1,1);
build(1,1,n);
scanf("%d",&m);
while(m--)
{
int k,x,y,val;
scanf("%d%d",&k,&x);
if(k==1)
{
scanf("%d%d",&y,&val);
update_path(x,y,val); //修改路径上的节点权值
}
else if(k==2)
{
scanf("%d",&val);
update_tree(x,val); //修改子树上的节点权值
}
else if(k==3)
{
scanf("%d",&y);
ll ans=query_path(x,y); //询问路径权值和
printf("%lld\n",ans);
}
else if(k==4)
{
ll ans=query_tree(x); //询问子树权值和
printf("%lld\n",ans);
}
}
return 0;
}