#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <bitset>
#include <queue>
#include <functional>
#include <time.h>
#include <math.h>
#include <map>
using namespace std;
#define ll long long
#define ull unsigned long long
const ll N = 4e6+5,M=4e6+5;
const ll logN=20;
const ll Maxl = 1E18 + 10;
const ll memMax = 0x3f;
ll res;
ll sign;
char c;
inline ll readi() {
sign = 1;
while (!isdigit(c=getchar()))
if (c == '-')
sign = -1;
else if (c == EOF)
return -1;
res = c - '0';
while (isdigit(c=getchar())) res = res * 10 + (c - '0');
return res * sign;
}
inline char readc() {
while ((c = getchar()) == 13 || c == 32 || c == 10)
;
return c;
}
struct way{ //Kur的存边方式不同
ll to,nxt;
}w[N*2+10]; //树
ll head[N+5],idx,val[N+5],fa[N+5],son[N+5],dep[N+5],size[N+5],seg[N+5],rev[N+5],top[N+5];
ll n,m,type,x,y;
ll sum[N*4],tip[N*4]; //4倍
void addlist(ll a,ll b){
idx++;
w[idx].nxt=head[a];
w[idx].to=b;
head[a]=idx;
}
void dfs1(ll now,ll pre){
dep[now]=dep[pre]+1;
fa[now]=pre;
size[now]=1;
ll to;
for(ll e=head[now];e;e=w[e].nxt){
to=w[e].to;
if(to==pre)continue;
dfs1(to,now);
size[now]+=size[to];
if(size[to]>size[son[now]])son[now]=to; //更新son
}
}
void dfs2(ll now,ll pre){ //Only rev以序列为key ; 其余皆以原tree为key
if(son[now]){
seg[son[now]]=++seg[0];
rev[seg[0]]=son[now];
top[son[now]]=top[now];
dfs2(son[now],now);
}
ll to;
for(ll e=head[now];e;e=w[e].nxt){
to=w[e].to;
if(!top[to]){ //与dfn[]似,有vis的功能
seg[to]=++seg[0];
rev[seg[0]]=to;
top[to]=to; //另起门户
dfs2(to,now);
}
}
}
void build(ll k,ll l,ll r){
if(l==r){
sum[k]=val[rev[l]]; //从序列 => rev => 原tree(val[i])
return; //递归边界要return
}
ll mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1 | 1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1 | 1];
}
void addtip(int k,int l,int r,ll v){
sum[k]+=(r-l+1)*v;
tip[k]+=v;
}
void pushdown(int k,int l,int r,ll mid){
if(tip[k]==0)return;
addtip(k<<1,l,mid,tip[k]);
addtip(k<<1 | 1,mid+1,r,tip[k]);
tip[k]=0;
}
void change(ll k,ll l,ll r,ll x,ll v){
if(x<l || x>r)return;
if(l==r && l==x){
sum[k]+=v;
return;
}
ll mid=l+r>>1;
change(k<<1,l,mid,x,v);
change(k<<1 | 1,mid+1,r,x,v);
sum[k]=sum[k<<1]+sum[k<<1 | 1];
return;
}
void modify(ll k,ll l,ll r,ll x,ll y,ll v){ //标记永久化
if(x>r || y<l)return;
if(x<=l && y>=r){
addtip(k,l,r,v);
return;
}
ll mid=l+r>>1;
//sum[k]+=(ll)(min(r,y)-max(l,x)+1)*v;
pushdown(k,l,r,mid);
modify(k<<1,l,mid,x,y,v);
modify(k<<1 | 1,mid+1,r,x,y,v);
sum[k]=sum[k<<1]+sum[k<<1 | 1];
}
ll query(ll k,ll l,ll r,ll x,ll y){
if(x>r || y<l)return 0;
//if(x<=l && y>=r) return sum[k]+(r-l+1)*tip[k]; //时时刻刻+tip
if(x<=l && y>=r) return sum[k];
//ll res=(ll)(min(r,y)-max(l,x)+1)*tip[k]; //计算本身标签贡献
ll res=0;
ll mid=l+r>>1;
pushdown(k,l,r,mid);
res+=query(k<<1,l,mid,x,y);
res+=query(k<<1 | 1,mid+1,r,x,y);
return res;
}
ll ask(ll x,ll y){ //操作序列(线段树前提) => 连续性
ll res=0;
ll fx=top[x],fy=top[y];
while(fx!=fy){ //不在一条重链上
if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
res+=query(1,1,seg[0],seg[fx],seg[x]);//先查询,后跳跃
x=fa[fx];fx=top[x];
}
//在同一条重链上
if(dep[x]<dep[y])swap(x,y);
res+=query(1,1,seg[0],seg[y],seg[x]);
return res;
//top fa?都是操作原tree
}
int main() { //轻重边对称与关联 => Only跑1种即可
n=readi();m=readi();
for(ll i=1;i<=n;i++)val[i]=readi();
for(ll i=1;i<n;i++){
x=readi();y=readi();
addlist(x,y);addlist(y,x);
}
//初始化
dfs1(1,0);
seg[0]=seg[1]=rev[1]=top[1]=1; //root手动初始化 => seg[0]为个数
dfs2(1,0);
build(1,1,seg[0]);
//for(ll i=1;i<=n;i++)cout << size[i]<<endl;
//prllf("%lld\n",ask(1,4));
for(ll i=1;i<=m;i++){
type=readi();x=readi();
if(type==1){
y=readi();
change(1,1,seg[0],seg[x],y); //x => seg => 序列(线段树)
}else if(type==2){
y=readi();
modify(1,1,seg[0],seg[x],seg[x]+size[x]-1,y);
}else{
printf("%lld\n",ask(1,x));
}
}
return 0;
}