#include<iostream>
#include<memory.h>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<stack>
#include<cstring>
#include<vector>
#include<list>
#include<set>
using namespace std;

const long long N = 1000005;

struct Tree
{
    long long l,r,key,tag;
}t[N<<2];

long long dep[N],size[N],fat[N],id[N],son[N],top[N];
//size[v]表示以v为根的子树的节点数
//dep[v]表示v的深度(根深度为1)
//top[v]表示v所在的重链的顶端节点
//fat[v]表示v的父亲
//son[v]表示与v在同一重链上的v的儿子节点（姑且称为重儿子）
//id[v]表示v与其父亲节点的连边（姑且称为v的父边）在线段树中的位置
long long cnt,n,m,root,mod;
long long rid[N],val[N],dp[N][20];
vector <long long> v[N];

void dfs1(long long now,long long fa)
{
    fat[now] = fa;
    dp[now][0] = fa;
    dep[now] = dep[fat[now]] +1;
    size[now] = 1;
    for(long long i = 0; i < v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != fa)
        {
            dfs1(u,now);
            size[now] += size[u];
            if(size[u] > size[son[now]])
                son[now] = u;
        }
    }
}

void dfs2(long long now,long long first)
{
    top[now] = first;
    id[now] = ++cnt;
    rid[id[now]] = now;
    if(!son[now]) return;
    dfs2(son[now],first);
    for( long long i = 0; i <v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != son[now] && u != fat[now])
            dfs2(u,u);
    }
}

void PushUp(long long now)
{
    t[now].key = t[now << 1].key + t[now << 1 | 1].key;
}

void PushDown(long long now)
{
    t[now<<1].tag += t[now].tag;
    t[now<<1].key += t[now].tag*(t[now<<1].r-t[now<<1].l+1);
    t[now<<1|1].tag += t[now].tag;
    t[now<<1|1].key += t[now].tag*(t[now<<1|1].r-t[now<<1|1].l+1);
    t[now].tag = 0;
}

void Build(long long now,long long l,long long r)
{
    t[now].l = l;
    t[now].r = r;
    if(l == r)
    {
        t[now].key = val[rid[l]];
        return;
    }
    long long mid = (l + r) >> 1;
    Build(now << 1,l,mid);
    Build(now << 1 | 1,mid + 1,r);
    PushUp(now);
}

void Update(long long now,long long l,long long r,long long z)
{
    if(l <= t[now].l && t[now].r <= r)
    {
        t[now].tag += z;
        t[now].key += z * (t[now].r - t[now].l + 1);
        return;
    }
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) Update(now << 1,l,r,z);
    else if(l > mid) Update(now << 1 | 1,l,r,z);
    else
    {
        Update(now << 1,l,mid,z);
        Update(now << 1 | 1,mid+1,r,z);
    }
    PushUp(now);
}

long long Query(long long now,long long l,long long r)
{
    if(l <= t[now].l && t[now].r <= r)
        return t[now].key;
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) return Query(now << 1,l,r);
    else if(l > mid) return Query(now << 1 | 1,l,r);
    else
        return Query(now << 1,l,mid)+Query(now << 1 | 1,mid+1,r);
}

long long lca(long long x,long long y)
{
    if(dep[x] < dep[y]) swap(x,y);
    for( long long i = 18; i >= 0; --i)
        if(dep[dp[x][i]] >= dep[y])
            x = dp[x][i];
    if(x == y) return x;
    for( long long i = 18; i >= 0; --i)
        if(dp[x][i] != dp[y][i])
        {
            x = dp[x][i];
            y = dp[y][i];
        }
    return dp[x][0];
}

long long Get_Lca(long long x,long long y)
{
    long long t = root;
    long long lca1 = lca(t,x);
    long long lca2 = lca(t,y);
    long long lca3 = lca(x,y);
    if(dep[lca1] >= max(dep[lca2],dep[lca3])) return lca1;//x在上面
    if(dep[lca2] >= max(dep[lca1],dep[lca3])) return lca2;//y在上面
    if(dep[lca3] >= max(dep[lca2],dep[lca1])) return lca3;//root在上面
}

void S_Update(long long x,long long y,long long z)
{
    long long t = Get_Lca(x,y);
    if(t == root)
    {
        Update(1,1,cnt,z);
        return;
    }
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[t])
            tt = dp[tt][i];
    if(dp[tt][0] != t) Update(1,id[t],id[t]+size[t]-1,z);//tt在t上面
    else
    {
        Update(1,1,id[tt]-1,z);//t到顶端
        Update(1,id[tt]+size[tt],cnt,z);//tt子树后面的点到结束
    }
}

long long S_Query(long long x)
{
    if(x == root) return Query(1,1,n);
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[x])
            tt = dp[tt][i];
    if(dp[tt][0] != x)
        return Query(1,id[x],id[x]+size[x]-1);//tt在x上面
    else
    {

        return Query(1,1,id[tt]-1) + Query(1,id[tt]+size[tt],cnt);
    }
}

int main()
{
    scanf("%I64d%I64d",&n,&m);
    for( long long i = 1; i <= n; ++i) scanf("%I64d",&val[i]);
    for( long long i = 1; i < n; ++i)
    {
        long long a,b;
        scanf("%I64d%I64d",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    root = 1;
    dfs1(root,0);
    dfs2(root,root);
    Build(1,1,cnt);
    for( long long j = 1; j <= 18; ++j)
        for( long long i = 1; i <= cnt; ++i)
            dp[i][j] = dp[dp[i][j-1]][j-1];
    for( long long i = 0; i < m; ++i)
    {
        long long t,x,y,z;
        scanf("%I64d",&t);
        if(t == 1)
        {
            scanf("%I64d",&x);
            root = x;
        }
        else if(t == 2)
        {
            scanf("%I64d%I64d%I64d",&x,&y,&z);
            S_Update(x,y,z);
        }
        else
        {
            scanf("%I64d",&x);
            printf("%I64d\n",S_Query(x));
        }
    }
    return 0;
}


//by ��ά
#include<set>
#include<map>
#include<queue>
#include<ctime>
#include<cmath>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define il inline
#define rg register
#define db double
#define mpr make_pair
#define maxn 200005
#define inf (1<<30)
#define eps 1e-8
#define pi 3.1415926535897932384626L
#define ls l,mid,v<<1
#define rs mid+1,r,v<<1|1
using namespace std;

inline int read()
{
    int ret=0;bool fla=0;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-'){fla=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
    return fla?-ret:ret;
}

struct edge{
	int to,ne;
}e[maxn*2];

int n,m,rt,num,ecnt,fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],val[maxn],in[maxn],out[maxn],dfn[maxn],head[maxn];
ll t[maxn*8],mark[maxn*8];
int st[maxn][22];

void add(int x,int y)
{
	e[++ecnt]=(edge){y,head[x]};head[x]=ecnt;
}

void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	siz[x]=1;
	st[x][0]=fa[x];
	for(int i=1;i<=20;++i)
		st[x][i]=st[st[x][i-1]][i-1];
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]) continue;
		fa[dd]=x;
		dfs(dd);
		siz[x]+=siz[dd];
		if(!son[x]||siz[dd]>siz[son[x]])
			son[x]=dd;
	}
}

void df2(int x,int tp)
{
	dfn[++num]=x;
	in[x]=num;
	top[x]=tp;
	if(son[x]) df2(son[x],tp);
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]||dd==son[x]) continue;
		df2(dd,dd);
	}
	out[x]=num;
}

void upda(int x){t[x]=t[x<<1]+t[x<<1|1];}

void pd(int v,int len1,int len2)
{
	if(mark[v]!=0){
		mark[v<<1]+=mark[v];
		mark[v<<1|1]+=mark[v];
		t[v<<1]+=(ll)mark[v]*len1;
		t[v<<1|1]+=(ll)mark[v]*len2;
		mark[v]=0;
	}
}

void build(int l,int r,int v)
{
	if(l==r){
		t[v]=val[dfn[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(ls);build(rs);
	upda(v);
}

void change(int l,int r,int v,int x,int y,int z)
{
	if(r<x||y<l) return ;
	if(x<=l&&r<=y)
	{
		t[v]+=(ll)(r-l+1)*z;
		mark[v]+=(ll)z;
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	change(ls,x,y,z);change(rs,x,y,z);
	upda(v);
}

ll ask(int l,int r,int v,int x,int y)
{
	if(r<x||y<l) return 0;
	if(x<=l&&r<=y) return t[v];
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	return ask(ls,x,y)+ask(rs,x,y);
}

void find(int l,int r,int v)
{
	if(l==r){
		printf("%I64d ",t[v]);
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	find(ls),find(rs);
}

int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int get(int x)
{
	int y=rt;
	for(int i=20;i>=0;--i)
		if(dep[st[y][i]]>dep[x]) y=st[y][i];
	return y;
}

bool pdin(int x,int y)
{
	return in[y]<=in[x]&&out[x]<=out[y];
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i) val[i]=read();
	for(int i=1,x,y;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
	rt=1;
	dfs(1);
	df2(1,1);
	build(1,n,1);
	//for(int i=1;i<=n;++i) printf("%d ",dfn[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",in[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",out[i]);printf("\n");
	for(int i=1,op,x,y,z;i<=m;++i)
	{
		op=read(),x=read();
		if(op==1){
			rt=x;
		}else if(op==2){
			y=read();z=read();
			int lca=LCA(x,y)^LCA(rt,x)^LCA(rt,y);
			//printf("%d\n",lca);
			//printf("%d\n",lca);
			if(lca==rt) change(1,n,1,1,n,z);
			else if(in[lca]<=in[rt]&&out[rt]<=out[lca]){
				int xx=get(lca);
				change(1,n,1,1,n,z);change(1,n,1,in[xx],out[xx],-z);
			}else{
				change(1,n,1,in[lca],out[lca],z);
			}
			//find(1,n,1);puts("");
		}else{
			if(x==rt){
				printf("%I64d\n",ask(1,n,1,1,n));
			}else if(in[x]<=in[rt]&&out[rt]<=out[x]){
				int xx=get(x);
				printf("%I64d\n",ask(1,n,1,1,n)-ask(1,n,1,in[xx],out[xx]));
			}else printf("%I64d\n",ask(1,n,1,in[x],out[x]));
		}
	}
	return 0;
}


//by ��ά
#include<set>
#include<map>
#include<queue>
#include<ctime>
#include<cmath>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define il inline
#define rg register
#define db double
#define mpr make_pair
#define maxn 100005
#define inf (1<<30)
#define eps 1e-8
#define pi 3.1415926535897932384626L
#define ls l,mid,v<<1
#define rs mid+1,r,v<<1|1
using namespace std;

inline int read()
{
    int ret=0;bool fla=0;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-'){fla=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
    return fla?-ret:ret;
}

struct edge{
	int to,ne;
}e[maxn*2];

int n,m,rt,num,ecnt,fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],val[maxn],in[maxn],out[maxn],dfn[maxn],head[maxn];
ll t[maxn*8],mark[maxn*8];

void add(int x,int y)
{
	e[++ecnt]=(edge){y,head[x]};head[x]=ecnt;
}

void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	siz[x]=1;
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]) continue;
		fa[dd]=x;
		dfs(dd);
		siz[x]+=siz[dd];
		if(!son[x]||siz[dd]>siz[son[x]])
			son[x]=dd;
	}
}

void df2(int x,int tp)
{
	dfn[++num]=x;
	in[x]=num;
	top[x]=tp;
	if(son[x]) df2(son[x],tp);
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]||dd==son[x]) continue;
		df2(dd,dd);
	}
	out[x]=num;
}

void upda(int x){t[x]=t[x<<1]+t[x<<1|1];}

void pd(int v,int len1,int len2)
{
	if(mark[v]){
		mark[v<<1]+=mark[v];
		mark[v<<1|1]+=mark[v];
		t[v<<1]+=mark[v]*len1;
		t[v<<1|1]+=mark[v]*len2;
		mark[v]=0;
	}
}

void build(int l,int r,int v)
{
	if(l==r){
		t[v]=val[dfn[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(ls);build(rs);
	upda(v);
}

void change(int l,int r,int v,int x,int y,int z)
{
	if(r<x||y<l) return ;
	if(x<=l&&r<=y)
	{
		t[v]+=(r-l+1)*z;
		mark[v]+=z;
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	change(ls,x,y,z);change(rs,x,y,z);
	upda(v);
}

ll ask(int l,int r,int v,int x,int y)
{
	if(r<x||y<l) return 0;
	if(x<=l&&r<=y) return t[v];
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	return ask(ls,x,y)+ask(rs,x,y);
}

void find(int l,int r,int v)
{
	if(l==r){
		printf("%I64d ",t[v]);
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	find(ls),find(rs);
}

int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int get(int x)
{
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(in[dd]<=in[rt]&&out[rt]<=out[dd]&&dd!=fa[x]) return dd;
	}
}

bool pdin(int x,int y)
{
	return in[y]<=in[x]&&out[x]<=out[y];
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i) val[i]=read();
	for(int i=1,x,y;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
	rt=1;
	dfs(1);
	df2(1,1);
	build(1,n,1);
	//for(int i=1;i<=n;++i) printf("%d ",dfn[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",in[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",out[i]);printf("\n");
	for(int i=1,op,x,y,z;i<=m;++i)
	{
		op=read(),x=read();
		if(op==1){
			rt=x;
		}else if(op==2){
			y=read();z=read();
			int lca=LCA(x,y)^LCA(rt,x)^LCA(rt,y);
			//printf("%d\n",lca);
			//printf("%d\n",lca);
			if(lca==rt) change(1,n,1,1,n,z);
			else if(in[lca]<=in[rt]&&out[rt]<=out[lca]){
				int xx=get(lca);
				change(1,n,1,1,in[xx]-1,z);change(1,n,1,out[xx]+1,n,z);
			}else{
				change(1,n,1,in[lca],out[lca],z);
			}
			//find(1,n,1);puts("");
		}else{
			if(x==rt){
				printf("%I64d\n",ask(1,n,1,1,n));
			}else if(in[x]<=in[rt]&&out[rt]<=out[x]){
				int xx=get(x);
				printf("%I64d\n",ask(1,n,1,1,in[xx]-1)+ask(1,n,1,out[xx]+1,n));
			}else printf("%I64d\n",ask(1,n,1,in[x],out[x]));
		}
	}
	return 0;
}


//by ��ά
#include<set>
#include<map>
#include<queue>
#include<ctime>
#include<cmath>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define il inline
#define rg register
#define db double
#define mpr make_pair
#define maxn 200005
#define inf (1<<30)
#define eps 1e-8
#define pi 3.1415926535897932384626L
#define ls l,mid,v<<1
#define rs mid+1,r,v<<1|1
using namespace std;

inline int read()
{
    int ret=0;bool fla=0;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-'){fla=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
    return fla?-ret:ret;
}

struct edge{
	int to,ne;
}e[maxn*2];

int n,m,rt,num,ecnt,fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],val[maxn],in[maxn],out[maxn],dfn[maxn],head[maxn];
ll t[maxn*8],mark[maxn*8];
int st[maxn][22];

void add(int x,int y)
{
	e[++ecnt]=(edge){y,head[x]};head[x]=ecnt;
}

void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	siz[x]=1;
	st[x][0]=fa[x];
	for(int i=1;i<=16;++i)
		st[x][i]=st[st[x][i-1]][i-1];
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]) continue;
		fa[dd]=x;
		dfs(dd);
		siz[x]+=siz[dd];
		if(!son[x]||siz[dd]>siz[son[x]])
			son[x]=dd;
	}
}

void df2(int x,int tp)
{
	dfn[++num]=x;
	in[x]=num;
	top[x]=tp;
	if(son[x]) df2(son[x],tp);
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]||dd==son[x]) continue;
		df2(dd,dd);
	}
	out[x]=num;
}

void upda(int x){t[x]=t[x<<1]+t[x<<1|1];}

void pd(int v,int len1,int len2)
{
	if(mark[v]!=0){
		mark[v<<1]+=mark[v];
		mark[v<<1|1]+=mark[v];
		t[v<<1]+=(ll)mark[v]*len1;
		t[v<<1|1]+=(ll)mark[v]*len2;
		mark[v]=0;
	}
}

void build(int l,int r,int v)
{
	if(l==r){
		t[v]=val[dfn[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(ls);build(rs);
	upda(v);
}

void change(int l,int r,int v,int x,int y,int z)
{
	if(r<x||y<l) return ;
	if(x<=l&&r<=y)
	{
		t[v]+=(ll)(r-l+1)*z;
		mark[v]+=(ll)z;
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	change(ls,x,y,z);change(rs,x,y,z);
	upda(v);
}

ll ask(int l,int r,int v,int x,int y)
{
	if(r<x||y<l) return 0;
	if(x<=l&&r<=y) return t[v];
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	return ask(ls,x,y)+ask(rs,x,y);
}

void find(int l,int r,int v)
{
	if(l==r){
		printf("%I64d ",t[v]);
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	find(ls),find(rs);
}

int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int get(int x)
{
	int y=rt;
	for(int i=16;i>=0;--i)
		if(dep[st[y][i]]>dep[x]) y=st[y][i];
	return y;
}

bool pdin(int x,int y)
{
	return in[y]<=in[x]&&out[x]<=out[y];
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i) val[i]=read();
	for(int i=1,x,y;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
	rt=1;
	dfs(1);
	df2(1,1);
	build(1,n,1);
	//for(int i=1;i<=n;++i) printf("%d ",dfn[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",in[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",out[i]);printf("\n");
	for(int i=1,op,x,y,z;i<=m;++i)
	{
		op=read(),x=read();
		if(op==1){
			rt=x;
		}else if(op==2){
			y=read();z=read();
			int lca=LCA(x,y)^LCA(rt,x)^LCA(rt,y);
			//printf("%d\n",lca);
			//printf("%d\n",lca);
			if(lca==rt) change(1,n,1,1,n,z);
			else if(in[lca]<=in[rt]&&out[rt]<=out[lca]){
				int xx=get(lca);
				change(1,n,1,1,n,z);change(1,n,1,in[xx],out[xx],-z);
			}else{
				change(1,n,1,in[lca],out[lca],z);
			}
			//find(1,n,1);puts("");
		}else{
			if(x==rt){
				printf("%I64d\n",ask(1,n,1,1,n));
			}else if(in[x]<=in[rt]&&out[rt]<=out[x]){
				int xx=get(x);
				printf("%I64d\n",ask(1,n,1,1,n)-ask(1,n,1,in[xx],out[xx]));
			}else printf("%I64d\n",ask(1,n,1,in[x],out[x]));
		}
	}
	return 0;
}


//by ��ά
#include<set>
#include<map>
#include<queue>
#include<ctime>
#include<cmath>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define il inline
#define rg register
#define db double
#define mpr make_pair
#define maxn 100005
#define inf (1<<30)
#define eps 1e-8
#define pi 3.1415926535897932384626L
#define ls l,mid,v<<1
#define rs mid+1,r,v<<1|1
using namespace std;

inline int read()
{
    int ret=0;bool fla=0;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-'){fla=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
    return fla?-ret:ret;
}

struct edge{
	int to,ne;
}e[maxn*2];

int n,m,rt,num,ecnt,fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],val[maxn],in[maxn],out[maxn],dfn[maxn],head[maxn];
ll t[maxn*8],mark[maxn*8];

void add(int x,int y)
{
	e[++ecnt]=(edge){y,head[x]};head[x]=ecnt;
}

void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	siz[x]=1;
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]) continue;
		fa[dd]=x;
		dfs(dd);
		siz[x]+=siz[dd];
		if(!son[x]||siz[dd]>siz[son[x]])
			son[x]=dd;
	}
}

void df2(int x,int tp)
{
	dfn[++num]=x;
	in[x]=num;
	top[x]=tp;
	if(son[x]) df2(son[x],tp);
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]||dd==son[x]) continue;
		df2(dd,dd);
	}
	out[x]=num;
}

void upda(int x){t[x]=t[x<<1]+t[x<<1|1];}

void pd(int v,int len1,int len2)
{
	if(mark[v]){
		mark[v<<1]+=mark[v];
		mark[v<<1|1]+=mark[v];
		t[v<<1]+=mark[v]*len1;
		t[v<<1|1]+=mark[v]*len2;
		mark[v]=0;
	}
}

void build(int l,int r,int v)
{
	if(l==r){
		t[v]=val[dfn[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(ls);build(rs);
	upda(v);
}

void change(int l,int r,int v,int x,int y,int z)
{
	if(r<x||y<l) return ;
	if(x<=l&&r<=y)
	{
		t[v]+=(r-l+1)*z;
		mark[v]+=z;
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	change(ls,x,y,z);change(rs,x,y,z);
	upda(v);
}

ll ask(int l,int r,int v,int x,int y)
{
	if(r<x||y<l) return 0;
	if(x<=l&&r<=y) return t[v];
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	return ask(ls,x,y)+ask(rs,x,y);
}

void find(int l,int r,int v)
{
	if(l==r){
		printf("%I64d ",t[v]);
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	find(ls),find(rs);
}

int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int get(int x)
{
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]) continue;
		if(in[dd]<=in[rt]&&out[rt]<=out[dd]) return dd;
	}
}

bool pdin(int x,int y)
{
	return in[y]<=in[x]&&out[x]<=out[y];
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i) val[i]=read();
	for(int i=1,x,y;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
	rt=1;
	dfs(1);
	df2(1,1);
	build(1,n,1);
	//for(int i=1;i<=n;++i) printf("%d ",dfn[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",in[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",out[i]);printf("\n");
	for(int i=1,op,x,y,z;i<=m;++i)
	{
		op=read(),x=read();
		if(op==1){
			rt=x;
		}else if(op==2){
			y=read();z=read();
			int lca=LCA(x,y)^LCA(rt,x)^LCA(rt,y);
			//printf("%d\n",lca);
			//printf("%d\n",lca);
			if(lca==rt) change(1,n,1,1,n,z);
			else if(in[lca]<=in[rt]&&out[rt]<=out[lca]){
				int xx=get(lca);
				change(1,n,1,1,n,z);change(1,n,1,in[xx],out[xx],-z);
			}else{
				change(1,n,1,in[lca],out[lca],z);
			}
			//find(1,n,1);puts("");
		}else{
			if(x==rt){
				printf("%I64d\n",ask(1,n,1,1,n));
			}else if(in[x]<=in[rt]&&out[rt]<=out[x]){
				int xx=get(x);
				printf("%I64d\n",ask(1,n,1,1,n)-ask(1,n,1,in[xx],out[xx]));
			}else printf("%I64d\n",ask(1,n,1,in[x],out[x]));
		}
	}
	return 0;
}


//by ��ά
#include<set>
#include<map>
#include<queue>
#include<ctime>
#include<cmath>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define il inline
#define rg register
#define db double
#define mpr make_pair
#define maxn 200005
#define inf (1<<30)
#define eps 1e-8
#define pi 3.1415926535897932384626L
#define ls l,mid,v<<1
#define rs mid+1,r,v<<1|1
using namespace std;

inline int read()
{
    int ret=0;bool fla=0;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-'){fla=1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
    return fla?-ret:ret;
}

struct edge{
	int to,ne;
}e[maxn*2];

int n,m,rt,num,ecnt,fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],val[maxn],in[maxn],out[maxn],dfn[maxn],head[maxn];
ll t[maxn*8],mark[maxn*8];

void add(int x,int y)
{
	e[++ecnt]=(edge){y,head[x]};head[x]=ecnt;
}

void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	siz[x]=1;
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]) continue;
		fa[dd]=x;
		dfs(dd);
		siz[x]+=siz[dd];
		if(!son[x]||siz[dd]>siz[son[x]])
			son[x]=dd;
	}
}

void df2(int x,int tp)
{
	dfn[++num]=x;
	in[x]=num;
	top[x]=tp;
	if(son[x]) df2(son[x],tp);
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]||dd==son[x]) continue;
		df2(dd,dd);
	}
	out[x]=num;
}

void upda(int x){t[x]=t[x<<1]+t[x<<1|1];}

void pd(int v,int len1,int len2)
{
	if(mark[v]!=0){
		mark[v<<1]+=mark[v];
		mark[v<<1|1]+=mark[v];
		t[v<<1]+=(ll)mark[v]*len1;
		t[v<<1|1]+=(ll)mark[v]*len2;
		mark[v]=0;
	}
}

void build(int l,int r,int v)
{
	if(l==r){
		t[v]=val[dfn[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(ls);build(rs);
	upda(v);
}

void change(int l,int r,int v,int x,int y,int z)
{
	if(r<x||y<l) return ;
	if(x<=l&&r<=y)
	{
		t[v]+=(ll)(r-l+1)*z;
		mark[v]+=(ll)z;
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	change(ls,x,y,z);change(rs,x,y,z);
	upda(v);
}

ll ask(int l,int r,int v,int x,int y)
{
	if(r<x||y<l) return 0;
	if(x<=l&&r<=y) return t[v];
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	return ask(ls,x,y)+ask(rs,x,y);
}

void find(int l,int r,int v)
{
	if(l==r){
		printf("%I64d ",t[v]);
		return ;
	}
	int mid=(l+r)>>1;
	pd(v,mid-l+1,r-mid);
	find(ls),find(rs);
}

int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int get(int x)
{
	for(int i=head[x];i;i=e[i].ne)
	{
		int dd=e[i].to;
		if(dd==fa[x]) continue;
		if(in[dd]<=in[rt]&&out[rt]<=out[dd]) return dd;
	}
}

bool pdin(int x,int y)
{
	return in[y]<=in[x]&&out[x]<=out[y];
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i) val[i]=read();
	for(int i=1,x,y;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
	rt=1;
	dfs(1);
	df2(1,1);
	build(1,n,1);
	//for(int i=1;i<=n;++i) printf("%d ",dfn[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",in[i]);printf("\n");
	//for(int i=1;i<=n;++i) printf("%d ",out[i]);printf("\n");
	for(int i=1,op,x,y,z;i<=m;++i)
	{
		op=read(),x=read();
		if(op==1){
			rt=x;
		}else if(op==2){
			y=read();z=read();
			int lca=LCA(x,y)^LCA(rt,x)^LCA(rt,y);
			//printf("%d\n",lca);
			//printf("%d\n",lca);
			if(lca==rt) change(1,n,1,1,n,z);
			else if(in[lca]<=in[rt]&&out[rt]<=out[lca]){
				int xx=get(lca);
				change(1,n,1,1,n,z);change(1,n,1,in[xx],out[xx],-z);
			}else{
				change(1,n,1,in[lca],out[lca],z);
			}
			//find(1,n,1);puts("");
		}else{
			if(x==rt){
				printf("%I64d\n",ask(1,n,1,1,n));
			}else if(in[x]<=in[rt]&&out[rt]<=out[x]){
				int xx=get(x);
				printf("%I64d\n",ask(1,n,1,1,n)-ask(1,n,1,in[xx],out[xx]));
			}else printf("%I64d\n",ask(1,n,1,in[x],out[x]));
		}
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

const long long N = 500005;

struct Tree
{
    long long l,r,key,tag;
}t[N<<2];

long long dep[N],size[N],id[N],son[N],top[N],w[N];
//size[v]表示以v为根的子树的节点数
//dep[v]表示v的深度(根深度为1)
//top[v]表示v所在的重链的顶端节点
//fat[v]表示v的父亲
//son[v]表示与v在同一重链上的v的儿子节点（姑且称为重儿子）
//id[v]表示v与其父亲节点的连边（姑且称为v的父边）在线段树中的位置
long long cnt,n,m,root,mod;
long long rid[N],val[N],dp[N][20];
vector <long long> v[N];

void dfs1(long long now,long long fa,long long d)
{
    dp[now][0] = fa;
    dep[now] = d;
    size[now] = 1;
    for(long long i = 0; i < v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != fa)
        {
            dfs1(u,now,d+1);
            size[now] += size[u];
            if(size[u] > size[son[now]])
                son[now] = u;
        }
    }
}

void dfs2(long long now,long long first)
{
    top[now] = first;
    id[now] = ++cnt;
    w[cnt] = val[now];
    if(!son[now]) return;
    dfs2(son[now],first);
    for( long long i = 0; i <v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != son[now] && u != dp[now][0])
            dfs2(u,u);
    }
}

void PushUp(long long now)
{
    t[now].key = t[now << 1].key + t[now << 1 | 1].key;
}

void PushDown(long long now)
{
    t[now<<1].tag += t[now].tag;
    t[now<<1].key += t[now].tag*(t[now<<1].r-t[now<<1].l+1);
    t[now<<1|1].tag += t[now].tag;
    t[now<<1|1].key += t[now].tag*(t[now<<1|1].r-t[now<<1|1].l+1);
    t[now].tag = 0;
}

void Build(long long now,long long l,long long r)
{
    t[now].l = l;
    t[now].r = r;
    if(l == r)
    {
        t[now].key = w[l];
        return;
    }
    long long mid = (l + r) >> 1;
    Build(now << 1,l,mid);
    Build(now << 1 | 1,mid + 1,r);
    PushUp(now);
}

void Update(long long now,long long l,long long r,long long z)
{
    if(l > r) return;
    if(l == t[now].l && t[now].r == r)
    {
        t[now].tag += z;
        t[now].key += z * (t[now].r - t[now].l + 1);
        return;
    }
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) Update(now << 1,l,r,z);
    else if(l > mid) Update(now << 1 | 1,l,r,z);
    else
    {
        Update(now << 1,l,mid,z);
        Update(now << 1 | 1,mid+1,r,z);
    }
    PushUp(now);
}

long long Query(long long now,long long l,long long r)
{
    if(l > r) return 0;
    if(l == t[now].l && t[now].r == r)
        return t[now].key;
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) return Query(now << 1,l,r);
    else if(l > mid) return Query(now << 1 | 1,l,r);
    else
        return Query(now << 1,l,mid)+Query(now << 1 | 1,mid+1,r);
}

long long lca(long long x,long long y)
{
    if(dep[x] < dep[y]) swap(x,y);
    for( long long i = 18; i >= 0; --i)
        if(dep[dp[x][i]] >= dep[y])
            x = dp[x][i];
    if(x == y) return x;
    for( long long i = 18; i >= 0; --i)
        if(dp[x][i] != dp[y][i])
        {
            x = dp[x][i];
            y = dp[y][i];
        }
    return dp[x][0];
}

long long Get_Lca(long long x,long long y)
{
    long long t = root;
    long long lca1 = lca(t,x);
    long long lca2 = lca(t,y);
    long long lca3 = lca(x,y);
    if(dep[lca1] >= max(dep[lca2],dep[lca3])) return lca1;//x在上面
    if(dep[lca2] >= max(dep[lca1],dep[lca3])) return lca2;//y在上面
    if(dep[lca3] >= max(dep[lca2],dep[lca1])) return lca3;//root在上面
}

void S_Update(long long x,long long y,long long z)
{
    long long t = Get_Lca(x,y);
    if(t == root)
    {
        Update(1,1,cnt,z);
        return;
    }
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[t])
            tt = dp[tt][i];
    if(dp[tt][0] != t) Update(1,id[t],id[t]+size[t]-1,z);//tt在t上面
    else
    {
        Update(1,1,id[tt]-1,z);//t到顶端
        Update(1,id[tt]+size[tt],cnt,z);//tt子树后面的点到结束
    }
}

long long S_Query(long long x)
{
    if(x == root) return Query(1,1,n);
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[x])
            tt = dp[tt][i];
    if(dp[tt][0] != x)
        return Query(1,id[x],id[x]+size[x]-1);//tt在x上面
    else
    {

        return Query(1,1,id[tt]-1) + Query(1,id[tt]+size[tt],cnt);
    }
}

int main()
{
    scanf("%I64d%I64d",&n,&m);
    for( long long i = 1; i <= n; ++i) scanf("%I64d",&val[i]);
    for( long long i = 1; i < n; ++i)
    {
        long long a,b;
        scanf("%I64d%I64d",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    root = 1;
    dfs1(root,0,1);
    dfs2(root,root);
    Build(1,1,cnt);
    for( long long j = 1; j <= 18; ++j)
        for( long long i = 1; i <= cnt; ++i)
            dp[i][j] = dp[dp[i][j-1]][j-1];
    for( long long i = 0; i < m; ++i)
    {
        long long t,x,y,z;
        scanf("%I64d",&t);
        if(t == 1)
        {
            scanf("%I64d",&x);
            root = x;
        }
        else if(t == 2)
        {
            scanf("%I64d%I64d%I64d",&x,&y,&z);
            S_Update(x,y,z);
        }
        else
        {
            scanf("%I64d",&x);
            printf("%I64d\n",S_Query(x));
        }
    }
    return 0;
}


#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
LL RD(){
    LL out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const LL maxn = 100019,INF = 1e9 + 19;
LL head[maxn],nume = 1;
struct Node{
    LL v,dis,nxt;
    }E[maxn << 3];
void add(LL u,LL v,LL dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
LL num, na;
LL dep[maxn], size[maxn], fa[maxn], wson[maxn], top[maxn], pos[maxn], ori[maxn], cnt;
LL v[maxn];
void dfs1(LL id, LL F){
	size[id] = 1;
	for(LL i = head[id];i;i = E[i].nxt){
		LL v = E[i].v;
		if(v == F)continue;
		dep[v] = dep[id] + 1;
		fa[v] = id;
		dfs1(v, id);
		size[id] += size[v];
		if(size[v] > size[wson[id]])wson[id] = v;
		}
	}
void dfs2(LL id, LL TP){
	pos[id] = ++cnt;
	ori[cnt] = id;
	top[id] = TP;
	if(!wson[id])return ;
	dfs2(wson[id], TP);
	for(LL i = head[id];i;i = E[i].nxt){
		LL v = E[i].v;
		if(v == fa[id] || v == wson[id])continue;
		dfs2(v, v);
		}
	}
#define lid (id << 1)
#define rid (id << 1) | 1
struct seg_tree{
	LL l, r;
	LL lazy, sum;
	}tree[maxn << 2];
void pushup(LL id){tree[id].sum = tree[lid].sum + tree[rid].sum;}
void pushdown(LL id){
	if(tree[id].lazy){
		LL val = tree[id].lazy;
		tree[lid].lazy += val;
		tree[rid].lazy += val;
		tree[lid].sum += (tree[lid].r - tree[lid].l + 1) * val;
		tree[rid].sum += (tree[rid].r - tree[rid].l + 1) * val;
		tree[id].lazy = 0;
		}
	}
void build(LL id, LL l, LL r){
	tree[id].l = l, tree[id].r = r;
	if(l == r){
		tree[id].sum = v[ori[l]];
		return ;
		}
	LL mid = (l + r) >> 1;
	build(lid, l, mid), build(rid, mid + 1, r);
	pushup(id);
	}
void update(LL id, LL val, LL l, LL r){
	pushdown(id);
	if(tree[id].l == l && tree[id].r == r){
		tree[id].sum += (tree[id].r - tree[id].l + 1) * val;
		tree[id].lazy += val;
		return ;
		}
	LL mid = (tree[id].l + tree[id].r) >> 1;
	if(mid < l)update(rid, val, l, r);
	else if(mid >= r)update(lid, val, l, r);
	else update(lid, val, l, mid), update(rid, val, mid + 1, r);
	pushup(id);
	}
LL query(LL id, LL l ,LL r){
	pushdown(id);
	if(tree[id].l == l && tree[id].r == r){
		return tree[id].sum;
		}
	LL mid = (tree[id].l + tree[id].r) >> 1;
	if(mid < l)return query(rid, l, r);
	else if(mid >= r)return query(lid, l, r);
	else return query(lid, l, mid) + query(rid, mid + 1, r);
	}
LL root = 1, jump[maxn][25];
void get_jump(){
	for(LL i = 1;i <= num;i++)jump[i][0] = fa[i];
	for(LL i = 1;i <= 19;i++){
		for(LL j = 1;j <= num;j++){
			jump[j][i] = jump[jump[j][i - 1]][i - 1];
			}
		}
	}
LL LCA(LL x, LL y){
	if(dep[x] < dep[y])swap(x, y);
	for(LL i = 19;i >= 0;i--)if(dep[jump[x][i]] >= dep[y])x = jump[x][i];
	if(x == y)return x;
	for(LL i = 19;i >= 0;i--)if(jump[x][i] != jump[y][i])x = jump[x][i], y = jump[y][i];
	return jump[x][0];
	}
LL real_LCA(LL x, LL y){
	LL lca1 = LCA(x, y), lca2 = LCA(x, root), lca3 = LCA(y, root);
	LL lca = dep[lca1] > dep[lca2] ? lca1 : lca2;
	return dep[lca] > dep[lca3] ? lca : lca3;
	}
LL son_LCA(LL x, LL y = root){
	if(dep[x] >= dep[y])return -1;
	for(LL i = 19;i >= 0;i--)if(dep[jump[y][i]] >= dep[x] + 1)y = jump[y][i];
	if(fa[y] == x)return y;
	return -1;
	}
void change_root(){root = RD();}
void uprange(){
	LL x = RD(), y = RD(), val = RD();
	LL lca = real_LCA(x, y);//prLLf("real_LCA=%d\n", lca);
	if(lca == root){update(1, val, pos[1], pos[1] + size[1] - 1);return ;}
	LL son = son_LCA(lca);//prLLf("son=%d\n", son);
	if(son == -1){update(1, val, pos[lca], pos[lca] + size[lca] - 1);return ;}
	update(1, val, pos[1], pos[1] + size[1] - 1);
	update(1,-val, pos[son], pos[son] + size[son] - 1);
	}
void get_sum(){
	LL x = RD();
	LL son = son_LCA(x);
	if(son == -1){printf("%lld\n", query(1, pos[x], pos[x] + size[x] - 1));return ;}
	printf("%lld\n", query(1, pos[1], pos[1] + size[1] - 1) - query(1, pos[son], pos[son] + size[son] - 1));
	}
int main(){
	num = RD();na = RD();
	for(LL i = 1;i <= num;i++)v[i] = RD();
	for(LL i = 1;i <= num - 1;i++){
		LL u = RD(), v = RD();
		add(u, v, 1), add(v, u, 1);
		}
	dep[1] = 1;
	dfs1(1, -1), dfs2(1, 1);
	build(1, 1, num);
	get_jump();
	for(LL i = 1;i <= na;i++){
		LL cmd = RD();
		if(cmd == 1)change_root();
		else if(cmd == 2)uprange();
		else get_sum();
		}
	return 0;
	}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int a[101010];
vector<int> outgo[101010];
int fa[101010][20];
int dfn[101010];
int dep[101010];
int end[101010];
int pos[101010];
int dfscnt=0;
int n,q;
void search(int now,int f)
{
	fa[now][0]=f;
	for(int i=1;i<20;i++)
		fa[now][i]=fa[fa[now][i-1]][i-1];
	dfn[now]=++dfscnt;
	dep[now]=dep[f]+1;
	pos[dfn[now]]=now;
	for(int i=0;i<outgo[now].size();i++)
	{
		int to=outgo[now][i];
		if(to!=f)
			search(to,now);
	}
	end[now]=dfscnt;
}
int lca(int x,int y)
{
	if(dep[x]>dep[y])
		swap(x,y);
	int dif=dep[y]-dep[x];
	for(int i=0;i<20;i++)
		if(dif&(1<<i))
			y=fa[y][i];
	if(x==y)
		return x;
	for(int i=19;i>=0;i--)
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	return fa[x][0];
}
long long sum[101010<<2];
long long lazy[101010<<2];
#define lch(id) (id<<1)
#define rch(id) (id<<1|1)
inline void update(int id)
{
	sum[id]=sum[lch(id)]+sum[rch(id)];
}
void Build(int id,int l,int r)
{
	if(l==r)
	{
		sum[id]=a[pos[l]];
		return;
	}
	int mid=(l+r)>>1;
	Build(lch(id),l,mid);
	Build(rch(id),mid+1,r);
	update(id);
}
void down(int id,int l,int r)
{
	if(lazy[id])
	{
		int mid=(l+r)>>1;
		sum[lch(id)]+=lazy[id]*(mid-l+1);
		lazy[lch(id)]+=lazy[id];
		sum[rch(id)]+=lazy[id]*(r-mid);
		lazy[rch(id)]+=lazy[id];
		lazy[id]=0;
	}
}
void modify(int id,int l,int r,int ql,int qr,int v)
{
	if(ql>qr)
		return;
	if(ql<=l&&r<=qr)
	{
		lazy[id]+=v;
		sum[id]+=(long long)v*(r-l+1);
		return;
	}
	down(id,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid)
		modify(lch(id),l,mid,ql,qr,v);
	if(qr>mid)
		modify(rch(id),mid+1,r,ql,qr,v);
	update(id);
}
long long query(int id,int l,int r,int ql,int qr)
{
	if(ql>qr)
		return 0;
	if(ql<=l&&r<=qr)
		return sum[id];
	down(id,l,r);
	int mid=(l+r)>>1;
	long long ret=0;
	if(ql<=mid)
		ret+=query(lch(id),l,mid,ql,qr);
	if(qr>mid)
		ret+=query(rch(id),mid+1,r,ql,qr);
	return ret;
}
inline bool in(int x,int y)
{
	return dfn[x]<=dfn[y]&&end[x]>=end[y];
}
inline int jump(int x,int tar)
{
	for(int i=19;i>=0;i--)
		if(dep[fa[x][i]]>dep[tar])
			x=fa[x][i];
	return x;
}
long long query(int x,int root)
{
	if(x==root)
		return sum[1];
	if(in(x,root))
	{
		int y=jump(root,x);
		return query(1,1,n,1,dfn[y]-1)+query(1,1,n,end[y]+1,n);
	}
	return query(1,1,n,dfn[x],end[x]);
}
int lca(int x,int y,int root)
{
	int fxr=lca(x,root);
	int fyr=lca(y,root);
	int fxy=lca(x,y);
	if(fxr==fyr)
		return fxy;
	if(fxy==x)
	{
		if(fyr==y)
			return y;
		return fyr;
	}
	if(fxr==fxy)
		return fyr;
	return fxr;
}
void modify(int x,int root,int v)
{
	if(x==root)
	{
		modify(1,1,n,1,n,v);
		return;
	}
	if(in(x,root))
	{
		int y=jump(root,x);
		modify(1,1,n,1,dfn[y]-1,v);
		modify(1,1,n,end[y]+1,n,v);
		return;
	}
	modify(1,1,n,dfn[x],end[x],v);
}
int main()
{
	scanf("%d %d",&n,&q);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d %d",&u,&v);
		outgo[u].push_back(v);
		outgo[v].push_back(u);
	}
	search(1,0);
	Build(1,1,n);
	int cur_root=1;
	for(int i=1;i<=q;i++)
	{
		int op;
		scanf("%d",&op);
		if(op==1)
		{
			int v;
			scanf("%d",&v);
			cur_root=v;
		}
		else if(op==2)
		{
			int u,v,x;
			scanf("%d %d %d",&u,&v,&x);
			int f=lca(u,v,cur_root);
			modify(f,cur_root,x);
		}
		else
		{
			int u;
			scanf("%d",&u);
			printf("%I64d\n",query(u,cur_root));
		}
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 100005
#define maxlog 32
using namespace std;
inline int qread(){
	int x=0,sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*sign;
}

int n,q;
int a[maxn];
int root=1;

struct edge{
	int from;
	int to;
	int next;
}E[maxn<<1];
int head[maxn];
int size=0;
void add_edge(int u,int v){
	size++;
	E[size].from=u;
	E[size].to=v;
	E[size].next=head[u];
	head[u]=size;
}

int cnt;
int log2n;
int l[maxn],r[maxn];
int deep[maxn],anc[maxn][maxlog];
void dfs(int x,int fa){
	l[x]=++cnt;
	anc[x][0]=fa;
	for(int i=1;i<=log2n;i++){
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=E[i].next){
		int y=E[i].to;
		if(y!=fa){
			deep[y]=deep[x]+1;
			dfs(y,x);
		}
	}
	r[x]=cnt;
}

int lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>=deep[y]){
			x=anc[x][i];
		}
	}
	if(x==y) return x;
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=anc[y][i]){
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return anc[x][0];
}

int tri_lca(int u,int v,int r){
	int l1=lca(u,v);
	int l2=lca(u,r);
	int l3=lca(v,r);
	int max_deep=max(deep[l1],max(deep[l2],deep[l3]));
	if(deep[l1]==max_deep) return l1;
	else if(deep[l2]==max_deep) return l2;
	else return l3;
}

int get_close(int w,int r){
	int x=r;
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>deep[w]){
			x=anc[x][i];
		}
	}
	return x;
}
struct node{
	int l;
	int r;
	long long v;
	long long mark;
	int len(){
		return r-l+1;
	}
}tree[maxn<<2];
void push_up(int pos){
	tree[pos].v=tree[pos<<1].v+tree[pos<<1|1].v;
}
void build(int l,int r,int pos){
	tree[pos].l=l;
	tree[pos].r=r;
	tree[pos].v=0;
	tree[pos].mark=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(l,mid,pos<<1);
	build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
	if(tree[pos].mark){
		tree[pos<<1].mark+=tree[pos].mark;
		tree[pos<<1|1].mark+=tree[pos].mark;
		tree[pos<<1].v+=tree[pos].mark*tree[pos<<1].len();
		tree[pos<<1|1].v+=tree[pos].mark*tree[pos<<1|1].len();
		tree[pos].mark=0;
	}
}
void update(int L,int R,long long v,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		tree[pos].mark+=v;
		tree[pos].v+=(v*tree[pos].len());
		return;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(L<=mid) update(L,R,v,pos<<1);
	if(R>mid) update(L,R,v,pos<<1|1);
	push_up(pos);
}
long long query(int L,int R,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		return tree[pos].v;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	long long ans=0;
	if(L<=mid) ans+=query(L,R,pos<<1);
	if(R>mid) ans+=query(L,R,pos<<1|1);
	return ans;
}

void change(int u,int v,int x){
	int xx=lca(u,v);
	int lca_num=tri_lca(u,v,root);
	if(l[root]<=l[xx]&&r[root]>=r[xx]){
		update(l[xx],r[xx],x,1);
		return;
	}else if(lca_num==root){
		update(1,n,x,1);
		return;
	}else{
		int w2=get_close(lca_num,root);
		update(1,n,x,1);
		update(l[w2],r[w2],-x,1);
	}
}

long long sum(int w){
	if(w==root){
		return query(1,n,1);
	}else if(l[w]<=l[root]&&r[w]>=r[root]){
		int sonw=get_close(w,root);
//		printf("%d\n",query(1,n,1));
//		printf("%d\n",query(l[sonw],r[sonw],1));
		return query(1,n,1)-query(l[sonw],r[sonw],1);
	}else{
		return query(l[w],r[w],1);
	}
}

int main(){
	int u,v,cmd,x;
	n=qread();
	q=qread();
	for(int i=1;i<=n;i++) a[i]=qread();
	for(int i=1;i<n;i++){
		u=qread();
		v=qread();
		add_edge(u,v);
		add_edge(v,u);
	}
	deep[1]=1;
	log2n=log2(n)+1;
	dfs(1,0);
	build(1,n,1);
	for(int i=1;i<=n;i++){
		update(l[i],l[i],a[i],1);
	}
	for(int i=1;i<=q;i++){
		cmd=qread();
		if(cmd==1){
			v=qread();
			root=v;
		}else if(cmd==2){
			u=qread();
			v=qread();
			x=qread();
			change(u,v,x);
		}else{
			v=qread();
			printf("%I64d\n",sum(v));
		}
//		printf("debug: sum=%d\n",query(1,n,1));
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 100005
#define maxlog 32
using namespace std;
inline int qread(){
	int x=0,sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*sign;
}

int n,q;
int a[maxn];
int root=1;

struct edge{
	int from;
	int to;
	int next;
}E[maxn<<1];
int head[maxn];
int size=0;
void add_edge(int u,int v){
	size++;
	E[size].from=u;
	E[size].to=v;
	E[size].next=head[u];
	head[u]=size;
}

int cnt;
int log2n;
int l[maxn],r[maxn];
int deep[maxn],anc[maxn][maxlog];
void dfs(int x,int fa){
	l[x]=++cnt;
	anc[x][0]=fa;
	for(int i=1;i<=log2n;i++){
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=E[i].next){
		int y=E[i].to;
		if(y!=fa){
			deep[y]=deep[x]+1;
			dfs(y,x);
		}
	}
	r[x]=cnt;
}

int lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>=deep[y]){
			x=anc[x][i];
		}
	}
	if(x==y) return x;
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=anc[y][i]){
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return anc[x][0];
}

int tri_lca(int u,int v,int r){
	int l1=lca(u,v);
	int l2=lca(u,r);
	int l3=lca(v,r);
	int max_deep=max(deep[l1],max(deep[l2],deep[l3]));
	if(deep[l1]==max_deep) return l1;
	else if(deep[l2]==max_deep) return l2;
	else return l3;
}

int get_close(int w,int r){
	int x=r;
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>deep[w]){
			x=anc[x][i];
		}
	}
	return x;
}
struct node{
	int l;
	int r;
	long long v;
	long long mark;
	int len(){
		return r-l+1;
	}
}tree[maxn<<2];
void push_up(int pos){
	tree[pos].v=tree[pos<<1].v+tree[pos<<1|1].v;
}
void build(int l,int r,int pos){
	tree[pos].l=l;
	tree[pos].r=r;
	tree[pos].v=0;
	tree[pos].mark=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(l,mid,pos<<1);
	build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
	if(tree[pos].mark){
		tree[pos<<1].mark+=tree[pos].mark;
		tree[pos<<1|1].mark+=tree[pos].mark;
		tree[pos<<1].v+=tree[pos].mark*tree[pos<<1].len();
		tree[pos<<1|1].v+=tree[pos].mark*tree[pos<<1|1].len();
		tree[pos].mark=0;
	}
}
void update(int L,int R,long long v,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		tree[pos].mark+=v;
		tree[pos].v+=(v*tree[pos].len());
		return;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(L<=mid) update(L,R,v,pos<<1);
	if(R>mid) update(L,R,v,pos<<1|1);
	push_up(pos);
}
long long query(int L,int R,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		return tree[pos].v;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	long long ans=0;
	if(L<=mid) ans+=query(L,R,pos<<1);
	if(R>mid) ans+=query(L,R,pos<<1|1);
	return ans;
}

void change(int u,int v,int x){
	int xx=lca(u,v);
	int lca_num=tri_lca(u,v,root);
	if(l[root]<l[xx]||r[root]>r[xx]){
		update(l[xx],r[xx],x,1);
		return;
	}else if(lca_num==root){
		update(1,n,x,1);
		return;
	}else{
		int w2=get_close(lca_num,root);
		update(1,n,x,1);
		update(l[w2],r[w2],-x,1);
		return;
	}
}

long long sum(int w){
	if(l[root]<l[w]||r[root]>r[w]){
		return query(l[w],r[w],1);
	}else{
		if(w==root){
			return query(1,n,1);
		}
		int sonw=get_close(w,root);
//		printf("%d\n",query(1,n,1));
//		printf("%d\n",query(l[sonw],r[sonw],1));
		return query(1,n,1)-query(l[sonw],r[sonw],1);
	}
}

int main(){
	int u,v,cmd,x;
	n=qread();
	q=qread();
	for(int i=1;i<=n;i++) a[i]=qread();
	for(int i=1;i<n;i++){
		u=qread();
		v=qread();
		add_edge(u,v);
		add_edge(v,u);
	}
	deep[1]=1;
	log2n=log2(n)+1;
	dfs(1,0);
	build(1,n,1);
	for(int i=1;i<=n;i++){
		update(l[i],l[i],a[i],1);
	}
	for(int i=1;i<=q;i++){
		cmd=qread();
		if(cmd==1){
			v=qread();
			root=v;
		}else if(cmd==2){
			u=qread();
			v=qread();
			x=qread();
			change(u,v,x);
		}else{
			v=qread();
			printf("%I64d\n",sum(v));
		}
//		printf("debug: sum=%d\n",query(1,n,1));
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 100005
#define maxlog 32
using namespace std;
inline int qread(){
	int x=0,sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*sign;
}

int n,q;
int a[maxn];
int root=1;

struct edge{
	int from;
	int to;
	int next;
}E[maxn<<1];
int head[maxn];
int size=0;
void add_edge(int u,int v){
	size++;
	E[size].from=u;
	E[size].to=v;
	E[size].next=head[u];
	head[u]=size;
}

int cnt;
int log2n;
int l[maxn],r[maxn];
int deep[maxn],anc[maxn][maxlog];
void dfs(int x,int fa){
	l[x]=++cnt;
	anc[x][0]=fa;
	for(int i=1;i<=log2n;i++){
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=E[i].next){
		int y=E[i].to;
		if(y!=fa){
			deep[y]=deep[x]+1;
			dfs(y,x);
		}
	}
	r[x]=cnt;
}

int lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>=deep[y]){
			x=anc[x][i];
		}
	}
	if(x==y) return x;
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=anc[y][i]){
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return anc[x][0];
}

int tri_lca(int u,int v,int r){
	int l1=lca(u,v);
	int l2=lca(u,r);
	int l3=lca(v,r);
	int max_deep=max(deep[l1],max(deep[l2],deep[l3]));
	if(deep[l1]==max_deep) return l1;
	else if(deep[l2]==max_deep) return l2;
	else return l3;
}

int get_close(int w,int r){
	int x=r;
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>deep[w]){
			x=anc[x][i];
		}
	}
	return x;
}
struct node{
	int l;
	int r;
	long long v;
	long long mark;
	int len(){
		return r-l+1;
	}
}tree[maxn<<2];
void push_up(int pos){
	tree[pos].v=tree[pos<<1].v+tree[pos<<1|1].v;
}
void build(int l,int r,int pos){
	tree[pos].l=l;
	tree[pos].r=r;
	tree[pos].v=0;
	tree[pos].mark=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(l,mid,pos<<1);
	build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
	if(tree[pos].mark){
		tree[pos<<1].mark+=tree[pos].mark;
		tree[pos<<1|1].mark+=tree[pos].mark;
		tree[pos<<1].v+=tree[pos].mark*tree[pos<<1].len();
		tree[pos<<1|1].v+=tree[pos].mark*tree[pos<<1|1].len();
		tree[pos].mark=0;
	}
}
void update(int L,int R,long long v,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		tree[pos].mark+=v;
		tree[pos].v+=(v*tree[pos].len());
		return;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(L<=mid) update(L,R,v,pos<<1);
	if(R>mid) update(L,R,v,pos<<1|1);
	push_up(pos);
}
long long query(int L,int R,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		return tree[pos].v;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	long long ans=0;
	if(L<=mid) ans+=query(L,R,pos<<1);
	if(R>mid) ans+=query(L,R,pos<<1|1);
	return ans;
}

void change(int u,int v,int x){
	int xx=lca(u,v);
	int lca_num=tri_lca(u,v,root);
	if(l[root]<=l[xx]&&r[root]>=r[xx]){
		update(l[xx],r[xx],x,1);
		return;
	}else if(lca_num==root){
		update(1,n,x,1);
		return;
	}else{
		int w2=get_close(lca_num,root);
		update(1,n,x,1);
		update(l[w2],r[w2],-x,1);
		return;
	}
}

long long sum(int w){
	if(w==root){
		return query(1,n,1);
	}else if(l[root]<=l[w]&&r[root]>=r[w]){
		return query(l[w],r[w],1);
	}else{
		int sonw=get_close(w,root);
//		printf("%d\n",query(1,n,1));
//		printf("%d\n",query(l[sonw],r[sonw],1));
		return query(1,n,1)-query(l[sonw],r[sonw],1);
	}
}

int main(){
	int u,v,cmd,x;
	n=qread();
	q=qread();
	for(int i=1;i<=n;i++) a[i]=qread();
	for(int i=1;i<n;i++){
		u=qread();
		v=qread();
		add_edge(u,v);
		add_edge(v,u);
	}
	deep[1]=1;
	log2n=log2(n)+1;
	dfs(1,0);
	build(1,n,1);
	for(int i=1;i<=n;i++){
		update(l[i],l[i],a[i],1);
	}
	for(int i=1;i<=q;i++){
		cmd=qread();
		if(cmd==1){
			v=qread();
			root=v;
		}else if(cmd==2){
			u=qread();
			v=qread();
			x=qread();
			change(u,v,x);
		}else{
			v=qread();
			printf("%I64d\n",sum(v));
		}
//		printf("debug: sum=%d\n",query(1,n,1));
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 100005
#define maxlog 32
using namespace std;
inline int qread(){
	int x=0,sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*sign;
}

int n,q;
int a[maxn];
int root=1;

struct edge{
	int from;
	int to;
	int next;
}E[maxn<<1];
int head[maxn];
int size=0;
void add_edge(int u,int v){
	size++;
	E[size].from=u;
	E[size].to=v;
	E[size].next=head[u];
	head[u]=size;
}

int cnt;
int log2n;
int l[maxn],r[maxn];
int deep[maxn],anc[maxn][maxlog];
void dfs(int x,int fa){
	l[x]=++cnt;
	anc[x][0]=fa;
	for(int i=1;i<=log2n;i++){
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=E[i].next){
		int y=E[i].to;
		if(y!=fa){
			deep[y]=deep[x]+1;
			dfs(y,x);
		}
	}
	r[x]=cnt;
}

int lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>=deep[y]){
			x=anc[x][i];
		}
	}
	if(x==y) return x;
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=anc[y][i]){
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return anc[x][0];
}

int tri_lca(int u,int v,int r){
	int l1=lca(u,v);
	int l2=lca(u,r);
	int l3=lca(v,r);
	int max_deep=max(deep[l1],max(deep[l2],deep[l3]));
	if(deep[l1]==max_deep) return l1;
	else if(deep[l2]==max_deep) return l2;
	else return l3;
}

int get_close(int w,int r){
	int x=r;
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>deep[w]){
			x=anc[x][i];
		}
	}
	return x;
}
struct node{
	int l;
	int r;
	long long v;
	long long mark;
	int len(){
		return r-l+1;
	}
}tree[maxn<<2];
void push_up(int pos){
	tree[pos].v=tree[pos<<1].v+tree[pos<<1|1].v;
}
void build(int l,int r,int pos){
	tree[pos].l=l;
	tree[pos].r=r;
	tree[pos].v=0;
	tree[pos].mark=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(l,mid,pos<<1);
	build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
	if(tree[pos].mark){
		tree[pos<<1].mark+=tree[pos].mark;
		tree[pos<<1|1].mark+=tree[pos].mark;
		tree[pos<<1].v+=tree[pos].mark*tree[pos<<1].len();
		tree[pos<<1|1].v+=tree[pos].mark*tree[pos<<1|1].len();
		tree[pos].mark=0;
	}
}
void update(int L,int R,long long v,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		tree[pos].mark+=v;
		tree[pos].v+=(v*tree[pos].len());
		return;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(L<=mid) update(L,R,v,pos<<1);
	if(R>mid) update(L,R,v,pos<<1|1);
	push_up(pos);
}
long long query(int L,int R,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		return tree[pos].v;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	long long ans=0;
	if(L<=mid) ans+=query(L,R,pos<<1);
	if(R>mid) ans+=query(L,R,pos<<1|1);
	return ans;
}

void change(int u,int v,int x){
	int xx=lca(u,v);
	int lca_num=tri_lca(u,v,root);
	if(l[root]<=l[xx]&&r[root]>=r[xx]){
		update(l[xx],r[xx],x,1);
		return;
	}else if(lca_num==root){
		update(1,n,x,1);
		return;
	}else{
		int w2=get_close(lca_num,root);
		update(1,n,x,1);
		update(l[w2],r[w2],-x,1);
	}
}

long long sum(int w){
	if(l[root]<=l[w]&&r[root]>=r[w]){
		return query(l[w],r[w],1);
	}else if(w==root){
		return query(1,n,1);
	}else{
		int sonw=get_close(w,root);
//		printf("%d\n",query(1,n,1));
//		printf("%d\n",query(l[sonw],r[sonw],1));
		return query(1,n,1)-query(l[sonw],r[sonw],1);
	}
}

int main(){
	int u,v,cmd,x;
	n=qread();
	q=qread();
	for(int i=1;i<=n;i++) a[i]=qread();
	for(int i=1;i<n;i++){
		u=qread();
		v=qread();
		add_edge(u,v);
		add_edge(v,u);
	}
	deep[1]=1;
	log2n=log2(n)+1;
	dfs(1,0);
	build(1,n,1);
	for(int i=1;i<=n;i++){
		update(l[i],l[i],a[i],1);
	}
	for(int i=1;i<=q;i++){
		cmd=qread();
		if(cmd==1){
			v=qread();
			root=v;
		}else if(cmd==2){
			u=qread();
			v=qread();
			x=qread();
			change(u,v,x);
		}else{
			v=qread();
			printf("%I64d\n",sum(v));
		}
//		printf("debug: sum=%d\n",query(1,n,1));
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 100005
#define maxlog 32
using namespace std;
inline int qread(){
	int x=0,sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*sign;
}

int n,q;
int a[maxn];
int root=1;

struct edge{
	int from;
	int to;
	int next;
}E[maxn<<1];
int head[maxn];
int size=0;
void add_edge(int u,int v){
	size++;
	E[size].from=u;
	E[size].to=v;
	E[size].next=head[u];
	head[u]=size;
}

int cnt;
int log2n;
int l[maxn],r[maxn];
int deep[maxn],anc[maxn][maxlog];
void dfs(int x,int fa){
	l[x]=++cnt;
	anc[x][0]=fa;
	for(int i=1;i<=log2n;i++){
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=E[i].next){
		int y=E[i].to;
		if(y!=fa){
			deep[y]=deep[x]+1;
			dfs(y,x);
		}
	}
	r[x]=cnt;
}

int lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>=deep[y]){
			x=anc[x][i];
		}
	}
	if(x==y) return x;
	for(int i=log2n;i>=0;i--){
		if(anc[x][i]!=anc[y][i]){
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return anc[x][0];
}

int tri_lca(int u,int v,int r){
	int l1=lca(u,v);
	int l2=lca(u,r);
	int l3=lca(v,r);
	int max_deep=max(deep[l1],max(deep[l2],deep[l3]));
	if(deep[l1]==max_deep) return l1;
	else if(deep[l2]==max_deep) return l2;
	else return l3;
}

int get_close(int w,int r){
	int x=r;
	for(int i=log2n;i>=0;i--){
		if(deep[anc[x][i]]>deep[w]){
			x=anc[x][i];
		}
	}
	return x;
}
struct node{
	int l;
	int r;
	long long v;
	long long mark;
	int len(){
		return r-l+1;
	}
}tree[maxn<<2];
void push_up(int pos){
	tree[pos].v=tree[pos<<1].v+tree[pos<<1|1].v;
}
void build(int l,int r,int pos){
	tree[pos].l=l;
	tree[pos].r=r;
	tree[pos].v=0;
	tree[pos].mark=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(l,mid,pos<<1);
	build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
	if(tree[pos].mark){
		tree[pos<<1].mark+=tree[pos].mark;
		tree[pos<<1|1].mark+=tree[pos].mark;
		tree[pos<<1].v+=tree[pos].mark*tree[pos<<1].len();
		tree[pos<<1|1].v+=tree[pos].mark*tree[pos<<1|1].len();
		tree[pos].mark=0;
	}
}
void update(int L,int R,long long v,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		tree[pos].mark+=v;
		tree[pos].v+=(v*tree[pos].len());
		return;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(L<=mid) update(L,R,v,pos<<1);
	if(R>mid) update(L,R,v,pos<<1|1);
	push_up(pos);
}
long long query(int L,int R,int pos){
	if(L<=tree[pos].l&&R>=tree[pos].r){
		return tree[pos].v;
	}
	push_down(pos);
	int mid=(tree[pos].l+tree[pos].r)>>1;
	long long ans=0;
	if(L<=mid) ans+=query(L,R,pos<<1);
	if(R>mid) ans+=query(L,R,pos<<1|1);
	return ans;
}

void change(int u,int v,int x){
	int xx=lca(u,v);
	int lca_num=tri_lca(u,v,root);
	if(l[root]<l[xx]&&r[root]>r[xx]){
		update(l[xx],r[xx],x,1);
		return;
	}else if(lca_num==root){
		update(1,n,x,1);
		return;
	}else{
		int w2=get_close(lca_num,root);
		update(1,n,x,1);
		update(l[w2],r[w2],-x,1);
		return;
	}
}

long long sum(int w){
	if(w==root){
		return query(1,n,1);
	}else if(l[root]<l[w]&&r[root]>r[w]){
		return query(l[w],r[w],1);
	}else{
		int sonw=get_close(w,root);
//		printf("%d\n",query(1,n,1));
//		printf("%d\n",query(l[sonw],r[sonw],1));
		return query(1,n,1)-query(l[sonw],r[sonw],1);
	}
}

int main(){
	int u,v,cmd,x;
	n=qread();
	q=qread();
	for(int i=1;i<=n;i++) a[i]=qread();
	for(int i=1;i<n;i++){
		u=qread();
		v=qread();
		add_edge(u,v);
		add_edge(v,u);
	}
	deep[1]=1;
	log2n=log2(n)+1;
	dfs(1,0);
	build(1,n,1);
	for(int i=1;i<=n;i++){
		update(l[i],l[i],a[i],1);
	}
	for(int i=1;i<=q;i++){
		cmd=qread();
		if(cmd==1){
			v=qread();
			root=v;
		}else if(cmd==2){
			u=qread();
			v=qread();
			x=qread();
			change(u,v,x);
		}else{
			v=qread();
			printf("%I64d\n",sum(v));
		}
//		printf("debug: sum=%d\n",query(1,n,1));
	}
	return 0;
}


#include<iostream>
#include<memory.h>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<stack>
#include<cstring>
#include<vector>
#include<list>
#include<set>
using namespace std;

const long long N = 100005;

struct Tree
{
    long long l,r,key,tag;
}t[N<<2];

long long dep[N],size[N],fat[N],id[N],son[N],top[N];
//size[v]表示以v为根的子树的节点数
//dep[v]表示v的深度(根深度为1)
//top[v]表示v所在的重链的顶端节点
//fat[v]表示v的父亲
//son[v]表示与v在同一重链上的v的儿子节点（姑且称为重儿子）
//id[v]表示v与其父亲节点的连边（姑且称为v的父边）在线段树中的位置
long long cnt,n,m,root,mod;
long long rid[N],val[N],dp[N][20];
vector <long long> v[N];

void dfs1(long long now,long long fa)
{
    fat[now] = fa;
    dp[now][0] = fa;
    dep[now] = dep[fat[now]] +1;
    size[now] = 1;
    for(long long i = 0; i < v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != fa)
        {
            dfs1(u,now);
            size[now] += size[u];
            if(size[u] > size[son[now]])
                son[now] = u;
        }
    }
}

void dfs2(long long now,long long first)
{
    top[now] = first;
    id[now] = ++cnt;
    rid[id[now]] = now;
    if(!son[now]) return;
    dfs2(son[now],first);
    for( long long i = 0; i <v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != son[now] && u != fat[now])
            dfs2(u,u);
    }
}

void PushUp(long long now)
{
    t[now].key = t[now << 1].key + t[now << 1 | 1].key;
}

void PushDown(long long now)
{
    t[now<<1].tag += t[now].tag;
    t[now<<1].key += t[now].tag*(t[now<<1].r-t[now<<1].l+1);
    t[now<<1|1].tag += t[now].tag;
    t[now<<1|1].key += t[now].tag*(t[now<<1|1].r-t[now<<1|1].l+1);
    t[now].tag = 0;
}

void Build(long long now,long long l,long long r)
{
    t[now].l = l;
    t[now].r = r;
    if(l == r)
    {
        t[now].key = val[rid[l]];
        return;
    }
    long long mid = (l + r) >> 1;
    Build(now << 1,l,mid);
    Build(now << 1 | 1,mid + 1,r);
    PushUp(now);
}

void Update(long long now,long long l,long long r,long long z)
{
    if(l <= t[now].l && t[now].r <= r)
    {
        t[now].tag += z;
        t[now].key += z * (t[now].r - t[now].l + 1);
        return;
    }
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) Update(now << 1,l,r,z);
    else if(l > mid) Update(now << 1 | 1,l,r,z);
    else
    {
        Update(now << 1,l,mid,z);
        Update(now << 1 | 1,mid+1,r,z);
    }
    PushUp(now);
}

long long Query(long long now,long long l,long long r)
{
    if(l <= t[now].l && t[now].r <= r)
        return t[now].key;
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) return Query(now << 1,l,r);
    else if(l > mid) return Query(now << 1 | 1,l,r);
    else
        return Query(now << 1,l,mid)+Query(now << 1 | 1,mid+1,r);
}

long long lca(long long x,long long y)
{
    if(dep[x] < dep[y]) swap(x,y);
    for( long long i = 18; i >= 0; --i)
        if(dep[dp[x][i]] >= dep[y])
            x = dp[x][i];
    if(x == y) return x;
    for( long long i = 18; i >= 0; --i)
        if(dp[x][i] != dp[y][i])
        {
            x = dp[x][i];
            y = dp[y][i];
        }
    return dp[x][0];
}

long long Get_Lca(long long x,long long y)
{
    long long t = root;
    long long lca1 = lca(t,x);
    long long lca2 = lca(t,y);
    long long lca3 = lca(x,y);
    if(dep[lca1] >= max(dep[lca2],dep[lca3])) return lca1;//x在上面
    if(dep[lca2] >= max(dep[lca1],dep[lca3])) return lca2;//y在上面
    if(dep[lca3] >= max(dep[lca2],dep[lca1])) return lca3;//root在上面
}

void S_Update(long long x,long long y,long long z)
{
    long long t = Get_Lca(x,y);
    if(t == root)
    {
        Update(1,1,cnt,z);
        return;
    }
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[t])
            tt = dp[tt][i];
    if(dp[tt][0] != t) Update(1,id[t],id[t]+size[t]-1,z);//tt在t上面
    else
    {
        Update(1,1,id[tt]-1,z);//t到顶端
        Update(1,id[tt]+size[tt],cnt,z);//tt子树后面的点到结束
    }
}

long long S_Query(long long x)
{
    if(x == root) return Query(1,1,n);
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[x])
            tt = dp[tt][i];
    if(dp[tt][0] != x)
        return Query(1,id[x],id[x]+size[x]-1);//tt在x上面
    else
    {

        return Query(1,1,id[tt]-1) + Query(1,id[tt]+size[tt],cnt);
    }
}

int main()
{
    scanf("%lld%lld",&n,&m);
    for( long long i = 1; i <= n; ++i) scanf("%lld",&val[i]);
    for( long long i = 1; i < n; ++i)
    {
        long long a,b;
        scanf("%lld%lld",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    root = 1;
    dfs1(root,0);
    dfs2(root,root);
    Build(1,1,cnt);
    for( long long j = 1; j <= 18; ++j)
        for( long long i = 1; i <= cnt; ++i)
            dp[i][j] = dp[dp[i][j-1]][j-1];
    for( long long i = 0; i < m; ++i)
    {
        long long t,x,y,z;
        scanf("%lld%",&t);
        if(t == 1)
        {
            scanf("%lld",&x);
            root = x;
        }
        else if(t == 2)
        {
            scanf("%lld%lld%lld",&x,&y,&z);
            S_Update(x,y,z);
        }
        else
        {
            scanf("%lld",&x);
            printf("%lld\n",S_Query(x));
        }
    }
    while(1);
    return 0;
}


#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

const long long N = 500005;

struct Tree
{
    long long l,r,key,tag;
}t[N<<2];

long long dep[N],size[N],id[N],son[N],top[N],w[N];
//size[v]表示以v为根的子树的节点数
//dep[v]表示v的深度(根深度为1)
//top[v]表示v所在的重链的顶端节点
//fat[v]表示v的父亲
//son[v]表示与v在同一重链上的v的儿子节点（姑且称为重儿子）
//id[v]表示v与其父亲节点的连边（姑且称为v的父边）在线段树中的位置
long long cnt,n,m,root,mod;
long long rid[N],val[N],dp[N][20];
vector <long long> v[N];

void dfs1(long long now,long long fa,long long d)
{
    dp[now][0] = fa;
    dep[now] = d;
    size[now] = 1;
    for(long long i = 0; i < v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != fa)
        {
            dfs1(u,now,d+1);
            size[now] += size[u];
            if(size[u] > size[son[now]])
                son[now] = u;
        }
    }
}

void dfs2(long long now,long long first)
{
    top[now] = first;
    id[now] = ++cnt;
    w[cnt] = val[now];
    if(!son[now]) return;
    dfs2(son[now],first);
    for( long long i = 0; i <v[now].size(); ++i)
    {
        long long u = v[now][i];
        if(u != son[now] && u != dp[now][0])
            dfs2(u,u);
    }
}

void PushUp(long long now)
{
    t[now].key = t[now << 1].key + t[now << 1 | 1].key;
}

void PushDown(long long now)
{
    t[now<<1].tag += t[now].tag;
    t[now<<1].key += t[now].tag*(t[now<<1].r-t[now<<1].l+1);
    t[now<<1|1].tag += t[now].tag;
    t[now<<1|1].key += t[now].tag*(t[now<<1|1].r-t[now<<1|1].l+1);
    t[now].tag = 0;
}

void Build(long long now,long long l,long long r)
{
    t[now].l = l;
    t[now].r = r;
    if(l == r)
    {
        t[now].key = w[l];
        return;
    }
    long long mid = (l + r) >> 1;
    Build(now << 1,l,mid);
    Build(now << 1 | 1,mid + 1,r);
    PushUp(now);
}

void Update(long long now,long long l,long long r,long long z)
{
    if(l <= t[now].l && t[now].r <= r)
    {
        t[now].tag += z;
        t[now].key += z * (t[now].r - t[now].l + 1);
        return;
    }
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) Update(now << 1,l,r,z);
    else if(l > mid) Update(now << 1 | 1,l,r,z);
    else
    {
        Update(now << 1,l,mid,z);
        Update(now << 1 | 1,mid+1,r,z);
    }
    PushUp(now);
}

long long Query(long long now,long long l,long long r)
{
    if(l <= t[now].l && t[now].r <= r)
        return t[now].key;
    PushDown(now);
    long long mid = (t[now].l + t[now].r) >> 1;
    if(r <= mid) return Query(now << 1,l,r);
    else if(l > mid) return Query(now << 1 | 1,l,r);
    else
        return Query(now << 1,l,mid)+Query(now << 1 | 1,mid+1,r);
}

long long lca(long long x,long long y)
{
    if(dep[x] < dep[y]) swap(x,y);
    for( long long i = 18; i >= 0; --i)
        if(dep[dp[x][i]] >= dep[y])
            x = dp[x][i];
    if(x == y) return x;
    for( long long i = 18; i >= 0; --i)
        if(dp[x][i] != dp[y][i])
        {
            x = dp[x][i];
            y = dp[y][i];
        }
    return dp[x][0];
}

long long Get_Lca(long long x,long long y)
{
    long long t = root;
    long long lca1 = lca(t,x);
    long long lca2 = lca(t,y);
    long long lca3 = lca(x,y);
    if(dep[lca1] >= max(dep[lca2],dep[lca3])) return lca1;//x在上面
    if(dep[lca2] >= max(dep[lca1],dep[lca3])) return lca2;//y在上面
    if(dep[lca3] >= max(dep[lca2],dep[lca1])) return lca3;//root在上面
}

void S_Update(long long x,long long y,long long z)
{
    long long t = Get_Lca(x,y);
    if(t == root)
    {
        Update(1,1,cnt,z);
        return;
    }
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[t])
            tt = dp[tt][i];
    if(dp[tt][0] != t) Update(1,id[t],id[t]+size[t]-1,z);//tt在t上面
    else
    {
        Update(1,1,id[tt]-1,z);//t到顶端
        Update(1,id[tt]+size[tt],cnt,z);//tt子树后面的点到结束
    }
}

long long S_Query(long long x)
{
    if(x == root) return Query(1,1,n);
    long long tt = root;
    for(long long i = 18; i >= 0; --i)
        if(dep[dp[tt][i]] > dep[x])
            tt = dp[tt][i];
    if(dp[tt][0] != x)
        return Query(1,id[x],id[x]+size[x]-1);//tt在x上面
    else
    {

        return Query(1,1,id[tt]-1) + Query(1,id[tt]+size[tt],cnt);
    }
}

int main()
{
    scanf("%I64d%I64d",&n,&m);
    for( long long i = 1; i <= n; ++i) scanf("%I64d",&val[i]);
    for( long long i = 1; i < n; ++i)
    {
        long long a,b;
        scanf("%I64d%I64d",&a,&b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    root = 1;
    dfs1(root,0,1);
    dfs2(root,root);
    Build(1,1,cnt);
    for( long long j = 1; j <= 18; ++j)
        for( long long i = 1; i <= cnt; ++i)
            dp[i][j] = dp[dp[i][j-1]][j-1];
    for( long long i = 0; i < m; ++i)
    {
        long long t,x,y,z;
        scanf("%I64d",&t);
        if(t == 1)
        {
            scanf("%I64d",&x);
            root = x;
        }
        else if(t == 2)
        {
            scanf("%I64d%I64d%I64d",&x,&y,&z);
            S_Update(x,y,z);
        }
        else
        {
            scanf("%I64d",&x);
            printf("%I64d\n",S_Query(x));
        }
    }
    return 0;
}


#include<cstdio>
#include<iostream>
#include<vector>
#define maxn 100010
using namespace std;
typedef long long LL;
vector<int> g[maxn];
struct node{
    LL w,tag;
}A[maxn*4];
int n,q,root=1,dfn=0;
int w[maxn],d[maxn],fa[maxn][22],to[maxn],rk[maxn],L[maxn],R[maxn];
//线段树板子
#define ls (rt<<1)
#define rs (rt<<1|1)
void push_up(int rt){
    A[rt].w=A[ls].w+A[rs].w;
}
void push_down(int rt,int l,int r){
    A[ls].tag+=A[rt].tag;
    A[rs].tag+=A[rt].tag;
    A[rt].w+=A[rt].tag*(LL)(r-l+1);
    A[rt].tag=0;
}
void build(int rt,int l,int r){
    if (l==r){
        A[rt].w=w[rk[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    push_up(rt);
}
//dfs一遍预处理
void dfs(int now,int f){
    //to[]:树->线段树 rk[]:线段树->树
    to[now]=++dfn; rk[dfn]=now;
    L[now]=dfn;
    d[now]=d[f]+1; fa[now][0]=f;
    for (int i=1;i<=20;i++)
        fa[now][i]=fa[fa[now][i-1]][i-1];
    L[now]=R[now]=to[now];
    for (int i=0;i<g[now].size();i++){
        int son=g[now][i];
        if (son!=f){
            dfs(son,now);
            //L[now]=min(L[now],L[son]);
            //R[now]=max(R[now],R[son]);
        }
    }
    R[now]=dfn;
}
//LCA板子
void jump(int &x,int c){    //x点在树上向上跳c层
    for (int i=0;i<=20;i++){
        if (c&1) x=fa[x][i];
        c>>=1;
    }
}
int lca(int x,int y){
    if (d[x]<d[y]) swap(x,y);
    jump(x,d[x]-d[y]);
    if (x==y) return x;
    for (int i=20;i>=0;i--)
        if (fa[x][i]!=fa[y][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    return fa[x][0];
}
int check_son(int u,int v){
    if (lca(u,v)==u) return 1;
    return 0;
}
//线段树板子
void change(int rt,int l,int r,int L,int R,int x){
    if (l>=L && r<=R){
        A[rt].tag+=x;
        LL tmp=x*(LL)(r-l+1);
        while (rt){
            rt>>=1;
            A[rt].w+=tmp;
        }
        return;
    }
    int mid=(l+r)>>1;
    if (L<=mid) change(ls,l,mid,L,R,x);
    if (R>mid) change(rs,mid+1,r,L,R,x);
}
LL query(int rt,int l,int r,int L,int R){
    if (l>=L && r<=R) return A[rt].w+A[rt].tag*(LL)(r-l+1);
    int mid=(l+r)>>1;
    LL sum=0;
    push_down(rt,l,r);
    if (L<=mid) sum+=query(ls,l,mid,L,R);
    if (R>mid) sum+=query(rs,mid+1,r,L,R);
    return sum;
}
//3个操作
void Change_root(int v){
    root=v;
}
void Change(int u,int v,int x){
    int now=lca(u,v);
    if (!check_son(now,root)){
        change(1,1,n,L[now],R[now],x);
        return;
    }
    change(1,1,n,1,n,x);
    int a=lca(root,u),b=lca(root,v);
    if (d[a]>d[b]) u=a;
    else u=b;
    v=root;
    if (u==v) return;
    jump(v,d[v]-d[u]-1);
    change(1,1,n,L[v],R[v],-x);
}
LL Query(int v){
    if (!check_son(v,root)) return query(1,1,n,L[v],R[v]);
    int u=root;
    if (u==v) return query(1,1,n,1,n);
    jump(u,d[u]-d[v]-1);
    return query(1,1,n,1,n)-query(1,1,n,L[u],R[u]);
}
int main(){
#ifdef xunzhen
    freopen("E.in","r",stdin);
    freopen("E.out","w",stdout);
#endif
    cin>>n>>q;
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0);
    build(1,1,n);
    for (int i=1;i<=q;i++){
        int k,u,v,x;
        scanf("%d",&k);
        if (k==1){
            scanf("%d",&v);
            Change_root(v);
        }
        if (k==2){
            scanf("%d%d%d",&u,&v,&x);
            Change(u,v,x);
        }
        if (k==3){
            scanf("%d",&v);
            printf("%I64d\n",Query(v)); //CF上要用I64d
        }
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
int input(){
	int f=1,s=0;char c=getchar();
	while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){s=s*10+c-'0';c=getchar();}
	return s*f;
}
const int N=100050;
struct Edge{int to,ne;}ed[N<<1];
int n,q,rt,dfn_tot;
int dfn[N],tim[N];
int fi[N],tot,fa[N][18];
int v[N],vv[N],dep[N];
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void added(int a,int b){
	ed[++tot].to=b;ed[tot].ne=fi[a];fi[a]=tot;
	ed[++tot].to=a;ed[tot].ne=fi[b];fi[b]=tot;
}
void dfs(const int &x){
	dfn[x]=++dfn_tot;
	for(int i=fi[x];i;i=ed[i].ne)if(!dfn[ed[i].to]){
		fa[ed[i].to][0]=x;
		dep[ed[i].to]=dep[x]+1;
		dfs(ed[i].to);
	}tim[x]=dfn_tot;
}
int lca(int x,int y){
	if(x==y)return x;
	if(dep[x]<dep[y])swap(x,y);
	int d=dep[x]-dep[y];
	for(int i=17;~i;--i)if(d&(1<<i))x=fa[x][i];
	if(x==y)return x;
	for(int i=17;~i;--i){
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	}return fa[x][0];
}
ll sum[N<<2],col[N<<2];
void pushup(int rt){sum[rt]=sum[rt<<1]+sum[rt<<1|1];}
void pushdown(int l,int r,int rt){
	if(col[rt]){
		int mid=l+r>>1;
		sum[rt<<1]+=col[rt]*(mid-l+1);
		sum[rt<<1|1]+=col[rt]*(r-mid);
		col[rt<<1]+=col[rt];
		col[rt<<1|1]+=col[rt];
		col[rt]=0;
	}
}
void build(int l,int r,int rt){
	if(l==r){sum[rt]=vv[l],col[rt]=0;return ;}
	int mid=l+r>>1;
	build(lson);build(rson);
	pushup(rt);
}
void update(int L,int R,int v,int l,int r,int rt){
	if(L>R)return ;
	if(L<=l && R>=r){sum[rt]+=1ll*(r-l+1)*v;col[rt]+=v;return ;}
	pushdown(l,r,rt);
	int mid=l+r>>1;
	if(L<=mid)update(L,R,v,lson);
	if(R>mid)update(L,R,v,rson);
	pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
	if(L>R)return 0;
	if(L<=l && R>=r)return sum[rt];
	pushdown(l,r,rt);
	ll res=0;int mid=l+r>>1;
	if(L<=mid)res=query(L,R,lson);
	if(R>mid)res+=query(L,R,rson);
	return res;
}
int dis(int u,int x,int y){
	int a=lca(u,x),b=lca(u,y);
	return dep[u]+dep[x]-2*dep[a]+dep[u]+dep[y]-2*dep[b];
}
int main(){
	n=input();q=input();
	for(int i=1;i<=n;++i)v[i]=input();
	for(int a,b,i=1;i<n;++i){
		a=input();b=input();
		added(a,b);
	}
	dfs(1);rt=1;
	for(int i=1;i<=n;++i)vv[dfn[i]]=v[i];
	build(1,n,1);
	for(int j=1;j<=17;++j)for(int i=1;i<=n;++i)fa[i][j]=fa[fa[i][j-1]][j-1];
	//for(int i=1;i<=n;++i)cout<<dfn[i]<<" ";cout<<endl;
	for(int op,x,y,z,k,t,w,u,v;q--;){
		op=input();
		if(op==1){rt=input();}
		else if(op==2){
			x=input();y=input();z=input();
			u=lca(x,rt);v=lca(y,rt);w=lca(x,y);
			t=(dep[u]>dep[v] ? u : v);
			if(lca(w,t)!=w){
				update(dfn[w],tim[w],z,1,n,1);
			}else{
				k=rt;
				for(int i=17;~i && dep[k]>dep[t]+1;--i)if(dep[fa[k][i]]>=dep[t]+1)k=fa[k][i];
				col[1]+=z;sum[1]+=1ll*n*z;
				if(k!=t)update(dfn[k],tim[k],-z,1,n,1);
			}
		}else if(op==3){
			x=input();
			if(rt==x)cout<<sum[1]<<endl;
			else if(dfn[x]<=dfn[rt] && tim[x]>=tim[rt]){
				y=rt;z=dep[y]-dep[x]-1;
				for(int i=17;~i;--i)if(z&(1<<i))y=fa[y][i];
				//cout<<y<<endl;cout<<tim[3]<<" "<<tim[3]+1<<" "<<n<<" "<<query(tim[y]+1,n,1,n,1)<<endl;
				printf("%I64d\n",query(1,dfn[y]-1,1,n,1)+query(tim[y]+1,n,1,n,1));
			}else printf("%I64d\n",query(dfn[x],tim[x],1,n,1));
		}
	}
}


#include <bits/stdc++.h>

#define inf 0x3f3f3f3f

#define INF 0x3f3f3f3f3f3f3f3fLL

#define MOD 1000000007

#define pb push_back

#define sq(x) ((x)*(x))

#define x first

#define y second

#define eps 1e-8

#define bpt(x) (__builtin_popcount(x))

#define bptl(x) (__builtin_popcountll(x))

#define bit(x, y) (((x)>>(y))&1)

#define bclz(x) (__builtin_clz(x))

#define bclzl(x) (__builtin_clzll(x))

#define bctz(x) (__builtin_ctz(x))

#define bctzl(x) (__builtin_ctzll(x))

using namespace std;

typedef long long INT;

typedef vector<int> VI;

typedef pair<int, int> pii;

typedef pair<pii, int> pi3;

typedef double DO;

template<typename T, typename U> inline void smin(T &a, U b) {if (a>b) a=b;}

template<typename T, typename U> inline void smax(T &a, U b) {if (a<b) a=b;}

template<typename T> inline void gn(T &x) {char c, sg=0; while (c=getchar(), (c>'9' || c<'0') && c!='-');for((c=='-'?sg=1, c=getchar():0), x=0; c>='0' && c<='9'; c=getchar()) x=(x<<1)+(x<<3)+c-'0';if (sg) x=-x;}

template<class T, class T1>inline void gn(T &x, T1 &y) {gn(x); gn(y);}

template<class T, class T1, class T2>inline void gn(T &x, T1 &y, T2 &z) {gn(x); gn(y); gn(z);}

template<typename T> inline void print(T x) {if (x<0) {putchar('-');return print(-x);}if (x<10) {putchar('0'+x);return;} print(x/10);putchar(x%10+'0');}

template<typename T> inline void println(T x) {print(x), putchar('\n');}

template<typename T> inline void printsp(T x) {print(x), putchar(' ');}

template<class T, class T1> inline void print(T x, T1 y) {printsp(x), println(y);}

template<class T, class T1, class T2> inline void print(T x, T1 y, T2 z) {printsp(x), printsp(y), println(z);}

int power(int a, int b, int m, int ans=1) {

	for (; b; b>>=1, a=1LL*a*a%m) if (b&1) ans=1LL*ans*a%m;

	return ans;

}

#define NN 100100

VI adj[NN];

int a[NN];

int n, L[NN], R[NN], rec[NN];

int dep[NN], pa[NN][20];

void dfs(int u, int fa=0){

	rec[n]=u;

	L[u]=n++;

	dep[u]=dep[fa]+1;

	pa[u][0]=fa;

	for(int i=1; i<18; i++) pa[u][i]=pa[pa[u][i-1]][i-1];

	for(int i=0; i<adj[u].size(); i++){

		int v=adj[u][i];

		if(v==fa) continue;

		dfs(v, u);

	}

	R[u]=n;

}

int k_th(int u, int k){

	for(int i=0; i<18; i++, k>>=1){

		if(k&1) u=pa[u][i];

	}

	return u;

}

int lca(int u, int v){

	if(dep[u]<dep[v]) swap(u, v);

	u=k_th(u, dep[u]-dep[v]);

	if(u==v) return u;

	for(int i=18; i--; ){

		if(pa[u][i]!=pa[v][i]) u=pa[u][i], v=pa[v][i];

	}

	return pa[u][0];

}

#define ls (u<<1)

#define rs (ls|1)

#define mid (L+R>>1)

INT sum[NN<<2], flag[NN<<2];

void push_up(int u){

	sum[u]=sum[ls]+sum[rs];

}

void build(int u, int L, int R){

	flag[u]=0;

	if(L+1==R){

		sum[u]=a[rec[L]];

		return ;

	}

	build(ls, L, mid);

	build(rs, mid, R);

	push_up(u);

}

void push_down(int u, int L, int R){

	if(flag[u]){

		flag[ls]+=flag[u];

		sum[ls]+=flag[u]*(mid-L);

		flag[rs]+=flag[u];

		sum[rs]+=flag[u]*(R-mid);

		flag[u]=0;

	}

}

void update(int u, int L, int R, int l, int r, int x){

	if(l>=R || r<=L) return ;

	if(l<=L && R<=r){

		flag[u]+=x;

		sum[u]+=(INT)x*(R-L);

		return ;

	}

	push_down(u, L, R);

	update(ls, L, mid, l, r, x);

	update(rs, mid, R, l, r, x);

	push_up(u);

}

INT calc(int u, int L, int R, int l, int r){

	if(l>=R || r<=L) return 0;

	if(l<=L && R<=r) return sum[u];

	push_down(u, L, R);

	return calc(ls, L, mid, l, r)+calc(rs, mid, R, l, r);

}

int main() {

#ifndef ONLINE_JUDGE

	freopen("in.in", "r", stdin);

	freopen("out.out", "w", stdout);

#endif

	int n, m;

	gn(n, m);

	for(int i=1; i<=n; i++) gn(a[i]);

	for(int i=1, u, v; i<n; i++){

		gn(u, v);

		adj[u].pb(v);

		adj[v].pb(u);

	}

	dfs(1);

	build(1, 0, n);

	int root=1;

	int tp, u, v, x;

	while(m--){

		gn(tp, u);

		if(tp==1) root=u;

		else if(tp==2){

			gn(v, x);

			int uv=lca(u, v);

			int ru=lca(root, u);

			int rv=lca(root, v);

			if(dep[ru]<dep[uv]) update(1, 0, n, L[uv], R[uv], x);

			else if(ru==root || rv==root) update(1, 0, n, 0, n, x);

			else {

				if(dep[ru]<dep[rv]) swap(ru, rv);

				rv=k_th(root, dep[root]-dep[ru]-1);

				update(1, 0, n, 0, n, x);

				update(1, 0, n, L[rv], R[rv], -x);

			}

		}

		else {

			if(u==root) println(calc(1, 0, n, 0, n));

			else {

				int ru=lca(root, u);

				if(ru==u){

					u=k_th(root, dep[root]-dep[u]-1);

					println(calc(1, 0, n, 0, n)-calc(1, 0, n, L[u], R[u]));

				}

				else println(calc(1, 0, n, L[u], R[u]));

			}

		}

	}

	return 0;

}


//by spli
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#define LL long long
#define RG register
using namespace std;

const int N=1e5+10;
int n,m;
int rt;
int a[N];
struct node{
	int to,nxt;
}e[N<<1];int head[N],cnt;
int fa[N][22],son[N],siz[N],top[N],dep[N];
int dfn[N],ha[N],lst[N];
int tot;
LL sum[N*4+5],tag[N*4+5];

void add(int f,int t){
	cnt++;
	e[cnt]=(node){t,head[f]};
	head[f]=cnt;
}

void dfs1(int u){
	siz[u]=1;
	for(int i=1;i<=18;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
	for(RG int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa[u][0]) continue;
		fa[v][0]=u;
		dep[v]=dep[u]+1;
		dfs1(v);
		siz[u]+=siz[v];
		if(son[u]==-1||siz[son[u]]<siz[v])
			son[u]=v;
	}
}

void dfs2(int u,int tp){
	top[u]=tp;
	++tot;
	dfn[u]=tot;
	ha[tot]=u;
	if(~son[u]) dfs2(son[u],tp);
	for(RG int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa[u][0]||v==son[u]) continue;
		dfs2(v,v);
	}
	lst[u]=tot;
}

int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]][0];
	}
	return dep[x]<dep[y] ? x : y;
}

void pushup(int pos){
	sum[pos]=sum[pos<<1]+sum[pos<<1|1];
}

void build(int pos,int L,int R){
	if(L==R){
		sum[pos]=a[ha[L]];
		return;
	}
	int mid=(L+R)>>1;
	build(pos<<1,L,mid);
	build(pos<<1|1,mid+1,R);
	pushup(pos);
}

void pushdown(int pos,int L0,int L1){
	if(tag[pos]!=0) {
		RG LL v=tag[pos];
		tag[pos<<1]+=v;
		tag[pos<<1|1]+=v;
		sum[pos<<1]+=v*L0;
		sum[pos<<1|1]+=v*L1;
		tag[pos]=0;
	}
}

void update(int pos,int L,int R,int ll,int rr,int v){
	if(L>rr||ll>R) return;
	if(ll<=L&&rr>=R) {
		sum[pos]+=1ll*(R-L+1)*v;
		tag[pos]+=v;
		return;
	}
	int mid=(L+R)>>1;
	pushdown(pos,mid-L+1,R-mid);
	if(rr<=mid) update(pos<<1,L,mid,ll,rr,v);
	else if(ll>mid) update(pos<<1|1,mid+1,R,ll,rr,v);
	else update(pos<<1,L,mid,ll,rr,v),update(pos<<1|1,mid+1,R,ll,rr,v);
	pushup(pos);
}

LL query(int pos,int L,int R,int ll,int rr){
	if(L>rr||ll>R) return 0ll;
	if(ll<=L&&rr>=R) return sum[pos];
	int mid=(L+R)>>1;
	pushdown(pos,mid-L+1,R-mid);
	LL ret=0;
	if(rr<=mid) ret=query(pos<<1,L,mid,ll,rr);
	else if(ll>mid) ret=query(pos<<1|1,mid+1,R,ll,rr);
	else ret=query(pos<<1,L,mid,ll,rr)+query(pos<<1|1,mid+1,R,ll,rr);
	return ret;
}

int search(int u,int g){
/*	int d=dep[g]-dep[u];
	if(d==1) return g;
	for(int i=18;i>=0;--i){
		if(d&(1<<i)) g=fa[g][i],d^=(1<<i);
		if(d==1) return g;
	}*/
	for(int i=18;i>=0;--i)
		if(dep[fa[g][i]]>dep[u]) g=fa[g][i];
	return g;
}

void change(int u,int v,int x){
	if((dfn[rt]<=dfn[u]&&lst[rt]>=lst[u])&&(dfn[rt]<=dfn[v]&&lst[rt]>=lst[v])){
		RG int lca=LCA(u,v);
		update(1,1,n,dfn[lca],lst[lca],(LL)x);
		return;
	}
	RG int uv=LCA(u,v);
	RG int ur=LCA(u,rt);
	RG int vr=LCA(v,rt);
	RG int lca=uv^ur^vr;
	if(lca==rt){
		update(1,1,n,1,n,(LL)x);
		return;
	}
	RG int kid=search(lca,rt);
	update(1,1,n,1,dfn[kid]-1,(LL)x);
	update(1,1,n,lst[kid]+1,n,(LL)x);
}

LL ask(int u){
	if(u==rt) return query(1,1,n,1,n);
	if(dfn[u]<=dfn[rt]&&lst[u]>=lst[rt]) {
		int kid=search(u,rt);
		return query(1,1,n,1,dfn[kid]-1)+query(1,1,n,lst[kid]+1,n);
	}
	else return query(1,1,n,dfn[u],lst[u]);
}

int main(){
//	freopen("in.txt","r",stdin);

	memset(head,-1,sizeof(head));
	memset(son,-1,sizeof(son));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	int x,y;
	for(int i=1;i<n;++i) {
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	rt=1;
	dep[1]=1;
	dfs1(1);
	dfs2(1,1);
//	for(int i=1;i<=n;++i) cout<<ha[i]<<" ";cout<<endl;
	build(1,1,n);

	int op,u,v;
	while(m--) {
		scanf("%d",&op);
		if(op==1) scanf("%d",&rt);
		if(op==2) {
			scanf("%d%d%d",&u,&v,&x);
			change(u,v,x);
		}
		if(op==3) {
			scanf("%d",&u);
			printf("%I64d\n",ask(u));
		}
	}
	return 0;
}

/*
-399
10404
1111
1278
*/


//by spli
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#define LL long long
#define RG register
using namespace std;

const int N=1e5+10;
int n,m;
int rt;
int a[N];
struct node{
	int to,nxt;
}e[N<<1];int head[N],cnt;
int fa[N][22],son[N],siz[N],top[N],dep[N];
int dfn[N],ha[N],lst[N];
int tot;
LL sum[N*4+5],tag[N*4+5];

void add(int f,int t){
	cnt++;
	e[cnt]=(node){t,head[f]};
	head[f]=cnt;
}

void dfs1(int u){
	siz[u]=1;
	for(int i=1;i<=18;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
	for(RG int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa[u][0]) continue;
		fa[v][0]=u;
		dep[v]=dep[u]+1;
		dfs1(v);
		siz[u]+=siz[v];
		if(son[u]==-1||siz[son[u]]<siz[v])
			son[u]=v;
	}
}

void dfs2(int u,int tp){
	top[u]=tp;
	++tot;
	dfn[u]=tot;
	ha[tot]=u;
	if(~son[u]) dfs2(son[u],tp);
	for(RG int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa[u][0]||v==son[u]) continue;
		dfs2(v,v);
	}
	lst[u]=tot;
}

int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]][0];
	}
	return dep[x]<dep[y] ? x : y;
}

void pushup(int pos){
	sum[pos]=sum[pos<<1]+sum[pos<<1|1];
}

void build(int pos,int L,int R){
	if(L==R){
		sum[pos]=a[ha[L]];
		return;
	}
	int mid=(L+R)>>1;
	build(pos<<1,L,mid);
	build(pos<<1|1,mid+1,R);
	pushup(pos);
}

void pushdown(int pos,int L0,int L1){
	if(tag[pos]!=0) {
		RG LL v=tag[pos];
		tag[pos<<1]+=v;
		tag[pos<<1|1]+=v;
		sum[pos<<1]+=v*L0;
		sum[pos<<1|1]+=v*L1;
		tag[pos]=0;
	}
}

void update(int pos,int L,int R,int ll,int rr,int v){
	if(L>rr||ll>R) return;
	if(ll<=L&&rr>=R) {
		sum[pos]+=1ll*(R-L+1)*v;
		tag[pos]+=v;
		return;
	}
	int mid=(L+R)>>1;
	pushdown(pos,mid-L+1,R-mid);
	if(rr<=mid) update(pos<<1,L,mid,ll,rr,v);
	else if(ll>mid) update(pos<<1|1,mid+1,R,ll,rr,v);
	else update(pos<<1,L,mid,ll,rr,v),update(pos<<1|1,mid+1,R,ll,rr,v);
	pushup(pos);
}

LL query(int pos,int L,int R,int ll,int rr){
	if(L>rr||ll>R) return 0ll;
	if(ll<=L&&rr>=R) return sum[pos];
	int mid=(L+R)>>1;
	pushdown(pos,mid-L+1,R-mid);
	LL ret=0;
	if(rr<=mid) ret=query(pos<<1,L,mid,ll,rr);
	else if(ll>mid) ret=query(pos<<1|1,mid+1,R,ll,rr);
	else ret=query(pos<<1,L,mid,ll,rr)+query(pos<<1|1,mid+1,R,ll,rr);
	return ret;
}

int search(int u,int g){
/*	int d=dep[g]-dep[u];
	if(d==1) return g;
	for(int i=18;i>=0;--i){
		if(d&(1<<i)) g=fa[g][i],d^=(1<<i);
		if(d==1) return g;
	}*/
	for(int i=18;i>=0;--i)
		if(dep[fa[g][i]]>dep[u]) g=fa[g][i];
	return g;
}

void change(int u,int v,int x){
/*	if((dfn[rt]<=dfn[u]&&lst[rt]>=lst[u])&&(dfn[rt]<=dfn[v]&&lst[rt]>=lst[v])){
		RG int lca=LCA(u,v);
		update(1,1,n,dfn[lca],lst[lca],(LL)x);
		return;
	}*/
	RG int uv=LCA(u,v);
	RG int ur=LCA(u,rt);
	RG int vr=LCA(v,rt);
	RG int lca=uv^ur^vr;
	if(lca==rt){
		update(1,1,n,1,n,(LL)x);
		return;
	}
	if(dfn[lca]<=dfn[rt]&&lst[lca]>=lst[rt]){
		RG int kid=search(lca,rt);
		update(1,1,n,1,dfn[kid]-1,(LL)x);
		update(1,1,n,lst[kid]+1,n,(LL)x);
	}
	else update(1,1,n,dfn[lca],lst[lca],(LL)x);
}

LL ask(int u){
	if(u==rt) return query(1,1,n,1,n);
	if(dfn[u]<=dfn[rt]&&lst[u]>=lst[rt]) {
		int kid=search(u,rt);
		return query(1,1,n,1,dfn[kid]-1)+query(1,1,n,lst[kid]+1,n);
	}
	else return query(1,1,n,dfn[u],lst[u]);
}

int main(){
	freopen("in.txt","r",stdin);

	memset(head,-1,sizeof(head));
	memset(son,-1,sizeof(son));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	int x,y;
	for(int i=1;i<n;++i) {
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	rt=1;
	dep[1]=1;
	dfs1(1);
	dfs2(1,1);
//	for(int i=1;i<=n;++i) cout<<ha[i]<<" ";cout<<endl;
	build(1,1,n);

	int op,u,v;
	while(m--) {
		scanf("%d",&op);
		if(op==1) scanf("%d",&rt);
		if(op==2) {
			scanf("%d%d%d",&u,&v,&x);
			change(u,v,x);
		}
		if(op==3) {
			scanf("%d",&u);
			printf("%I64d\n",ask(u));
		}
	}
	return 0;
}

/*
-399
10404
1111
1278
*/


//by spli
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#define LL long long
#define RG register
using namespace std;

const int N=1e5+10;
int n,m;
int rt;
int a[N];
struct node{
	int to,nxt;
}e[N<<1];int head[N],cnt;
int fa[N][22],son[N],siz[N],top[N],dep[N];
int dfn[N],ha[N],lst[N];
int tot;
LL sum[N*4+5],tag[N*4+5];

void add(int f,int t){
	cnt++;
	e[cnt]=(node){t,head[f]};
	head[f]=cnt;
}

void dfs1(int u){
	siz[u]=1;
	for(int i=1;i<=18;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
	for(RG int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa[u][0]) continue;
		fa[v][0]=u;
		dep[v]=dep[u]+1;
		dfs1(v);
		siz[u]+=siz[v];
		if(son[u]==-1||siz[son[u]]<siz[v])
			son[u]=v;
	}
}

void dfs2(int u,int tp){
	top[u]=tp;
	++tot;
	dfn[u]=tot;
	ha[tot]=u;
	if(~son[u]) dfs2(son[u],tp);
	for(RG int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa[u][0]||v==son[u]) continue;
		dfs2(v,v);
	}
	lst[u]=tot;
}

int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]][0];
	}
	return dep[x]<dep[y] ? x : y;
}

void pushup(int pos){
	sum[pos]=sum[pos<<1]+sum[pos<<1|1];
}

void build(int pos,int L,int R){
	if(L==R){
		sum[pos]=a[ha[L]];
		return;
	}
	int mid=(L+R)>>1;
	build(pos<<1,L,mid);
	build(pos<<1|1,mid+1,R);
	pushup(pos);
}

void pushdown(int pos,int L0,int L1){
	if(tag[pos]!=0) {
		RG LL v=tag[pos];
		tag[pos<<1]+=v;
		tag[pos<<1|1]+=v;
		sum[pos<<1]+=v*L0;
		sum[pos<<1|1]+=v*L1;
		tag[pos]=0;
	}
}

void update(int pos,int L,int R,int ll,int rr,int v){
	if(L>rr||ll>R) return;
	if(ll<=L&&rr>=R) {
		sum[pos]+=1ll*(R-L+1)*v;
		tag[pos]+=v;
		return;
	}
	int mid=(L+R)>>1;
	pushdown(pos,mid-L+1,R-mid);
	if(rr<=mid) update(pos<<1,L,mid,ll,rr,v);
	else if(ll>mid) update(pos<<1|1,mid+1,R,ll,rr,v);
	else update(pos<<1,L,mid,ll,rr,v),update(pos<<1|1,mid+1,R,ll,rr,v);
	pushup(pos);
}

LL query(int pos,int L,int R,int ll,int rr){
	if(L>rr||ll>R) return 0ll;
	if(ll<=L&&rr>=R) return sum[pos];
	int mid=(L+R)>>1;
	pushdown(pos,mid-L+1,R-mid);
	LL ret=0;
	if(rr<=mid) ret=query(pos<<1,L,mid,ll,rr);
	else if(ll>mid) ret=query(pos<<1|1,mid+1,R,ll,rr);
	else ret=query(pos<<1,L,mid,ll,rr)+query(pos<<1|1,mid+1,R,ll,rr);
	return ret;
}

int search(int u,int g){
/*	int d=dep[g]-dep[u];
	if(d==1) return g;
	for(int i=18;i>=0;--i){
		if(d&(1<<i)) g=fa[g][i],d^=(1<<i);
		if(d==1) return g;
	}*/
	for(int i=18;i>=0;--i)
		if(dep[fa[g][i]]>dep[u]) g=fa[g][i];
	return g;
}

void change(int u,int v,int x){
/*	if((dfn[rt]<=dfn[u]&&lst[rt]>=lst[u])&&(dfn[rt]<=dfn[v]&&lst[rt]>=lst[v])){
		RG int lca=LCA(u,v);
		update(1,1,n,dfn[lca],lst[lca],(LL)x);
		return;
	}*/
	RG int uv=LCA(u,v);
	RG int ur=LCA(u,rt);
	RG int vr=LCA(v,rt);
	RG int lca=uv^ur^vr;
	if(lca==rt){
		update(1,1,n,1,n,(LL)x);
		return;
	}
	if(dfn[lca]<=dfn[rt]&&lst[lca]>=lst[rt]){
		RG int kid=search(lca,rt);
		update(1,1,n,1,dfn[kid]-1,(LL)x);
		update(1,1,n,lst[kid]+1,n,(LL)x);
	}
	else update(1,1,n,dfn[lca],lst[lca],(LL)x);
}

LL ask(int u){
	if(u==rt) return query(1,1,n,1,n);
	if(dfn[u]<=dfn[rt]&&lst[u]>=lst[rt]) {
		int kid=search(u,rt);
		return query(1,1,n,1,dfn[kid]-1)+query(1,1,n,lst[kid]+1,n);
	}
	else return query(1,1,n,dfn[u],lst[u]);
}

int main(){
//	freopen("in.txt","r",stdin);

	memset(head,-1,sizeof(head));
	memset(son,-1,sizeof(son));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	int x,y;
	for(int i=1;i<n;++i) {
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	rt=1;
	dep[1]=1;
	dfs1(1);
	dfs2(1,1);
//	for(int i=1;i<=n;++i) cout<<ha[i]<<" ";cout<<endl;
	build(1,1,n);

	int op,u,v;
	while(m--) {
		scanf("%d",&op);
		if(op==1) scanf("%d",&rt);
		if(op==2) {
			scanf("%d%d%d",&u,&v,&x);
			change(u,v,x);
		}
		if(op==3) {
			scanf("%d",&u);
			printf("%I64d\n",ask(u));
		}
	}
	return 0;
}

/*
-399
10404
1111
1278
*/


#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
typedef long long ll;

int n,m,a[N],rt;

int hd[N],to[N*2],pr[N*2],tot,L[N],R[N],dfn,id[N];
void addedge(int u,int v) {to[++tot]=v;pr[tot]=hd[u];hd[u]=tot;}
int fa[N][17],dep[N];
void dfs(int u,int F,int D)
{
    fa[u][0]=F;
    dep[u]=D;
    L[u]=++dfn;id[dfn]=u;
    for (int i=1;i<=16;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
    for (int i=hd[u];i;i=pr[i]) if (to[i]!=F) dfs(to[i],u,D+1);
    R[u]=dfn;
}
void up(int &u,int d) {for (int i=0;i<=16;i++) if (d&(1<<i)) u=fa[u][i];}
int LCA(int u,int v)
{
    if (dep[u]>dep[v]) up(u,dep[u]-dep[v]);
    else up(v,dep[v]-dep[u]);
    if (u==v) return  u;
    for (int i=16;i>=0;i--) if (fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}

int lc[N*2],rc[N*2],cnt=1;ll sum[N*2],add[N*2];
ll ans;int T,A;
void build(int u,int l,int r)
{
    if (l==r) {sum[u]=a[id[l]];return ;}
    int mid=(l+r)>>1;
    build(lc[u]=++cnt,l,mid);
    build(rc[u]=++cnt,mid+1,r);
    sum[u]=sum[lc[u]]+sum[rc[u]];
}
void query(int u,int l,int r,int L,int R)
{
    if (l==L&&r==R)
    {
        if (T==1) ans+=sum[u];
        else add[u]+=A,sum[u]+=(ll)(r-l+1)*A;
        return ;
    }
    int mid=(l+r)>>1;
    if (T==1) ans+=add[u]*(R-L+1);
    if (R<=mid) query(lc[u],l,mid,L,R);else
    if (mid<L) query(rc[u],mid+1,r,L,R);else
    query(lc[u],l,mid,L,mid),query(rc[u],mid+1,r,mid+1,R);
    sum[u]=sum[lc[u]]+sum[rc[u]]+(ll)(r-l+1)*add[u];
}
void work(int u)
{
    if (u==rt) {query(1,1,n,1,n);return;}
    if (L[u]<=L[rt]&&L[rt]<=R[u])
    {
        int t=rt;
        up(t,dep[t]-dep[u]-1);
        if (L[t]!=1) query(1,1,n,1,L[t]-1);
        if (R[t]!=n) query(1,1,n,R[t]+1,n);
    }
    else query(1,1,n,L[u],R[u]);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);

    for (int u,v,i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }

    dfs(rt=1,0,1);
    build(1,1,n);

    int ty,u,v,t,p;
    while (m--)
    {
        scanf("%d",&ty);
        if (ty==1) scanf("%d",&rt);
        if (ty==2)
        {
            scanf("%d%d",&u,&v);
            t=LCA(u,v);
            p=LCA(u,rt);
            if (dep[p]>dep[t]) t=p;
            p=LCA(v,rt);
            if (dep[p]>dep[t]) t=p;

            scanf("%d",&A);T=2;
            work(t);
        }
        if (ty==3)
        {
            scanf("%d",&u);
            ans=0;T=1;
            work(u);
            printf("%I64d\n",ans);
        }
    }
    return 0;
}
/*
6 7
1 4 2 8 5 7
1 2
3 1
4 3
4 5
3 6

3 1
2 4 6 3
3 4
1 6
2 2 4 -5
1 4
3 3

4 6
4 3 5 6
1 2
2 3
3 4
3 1
1 3
2 2 4 3
1 1
2 2 4 -3
3 1

*/


#include <bits/stdc++.h>

using namespace std;

struct sa {
	int x[3];
} sat;

struct sb {
	int x[27];
} sbt;

int n, q, ra[100005], rb[100005], at, ta, tb, tc;
char x[15], y[35];
vector< sa > va;
vector< sb > vb;

int f1(int a, int b, int c, int d, int e) {
	if (a == c && b == d)
		return va[e].x[2];
	int mi = (c + d) / 2, re = 0;
	if (a < mi && va[e].x[0])
		re += f1(a, min(b, mi), c, mi, va[e].x[0]);
	if (b > mi && va[e].x[1])
		re += f1(max(a, mi), b, mi, d, va[e].x[1]);
	return re;
}

int main() {
	va.push_back(sat);
	vb.push_back(sbt);
	scanf("%d", &q);
	for (int i = 1; i <= q; i++) {
		scanf("%s", x);
		if (x[0] == 'u') {
			scanf("%d", &ta);
			ra[i] = ra[i - ta - 1];
			rb[i] = rb[i - ta - 1];
		} else {
			scanf("%s", y);
			n = strlen(y);
			if (x[0] == 's') {
				scanf("%d", &ta);
				rb[i] = at = vb.size();
				vb.push_back(vb[rb[i - 1]]);
				for (int j = 0; j < n; j++) {
					tb = y[j] - 'a';
					if (vb[at].x[tb])
						vb.push_back(vb[vb[at].x[tb]]);
					else
						vb.push_back(sbt);
					at = vb[at].x[tb] = vb.size() - 1;
				}
				tc = vb[at].x[26];
				vb[at].x[26] = ta;
				ra[i] = at = va.size();
				va.push_back(va[ra[i - 1]]);
				for (int j = 29; j >= 0; j--) {
					va[at].x[2]++;
					tb = !!(ta & (1 << j));
					if (va[at].x[tb])
						va.push_back(va[va[at].x[tb]]);
					else
						va.push_back(sat);
					at = va[at].x[tb] = va.size() - 1;
				}
				va[at].x[2]++;
				if (tc) {
					va.push_back(va[ra[i]]);
					ra[i] = at = va.size() - 1;
					for (int j = 29; j >= 0; j--) {
						va[at].x[2]--;
						tb = !!(tc & (1 << j));
						if (va[at].x[tb])
							va.push_back(va[va[at].x[tb]]);
						else
							va.push_back(sat);
						at = va[at].x[tb] = va.size() - 1;
					}
					va[at].x[2]--;
				}
			} else if (x[0] == 'r') {
				rb[i] = at = vb.size();
				vb.push_back(vb[rb[i - 1]]);
				for (int j = 0; j < n; j++) {
					tb = y[j] - 'a';
					if (vb[at].x[tb])
						vb.push_back(vb[vb[at].x[tb]]);
					else
						vb.push_back(sbt);
					at = vb[at].x[tb] = vb.size() - 1;
				}
				tc = vb[at].x[26];
				vb[at].x[26] = 0;
				ra[i] = ra[i - 1];
				if (tc) {
					va.push_back(va[ra[i]]);
					ra[i] = at = va.size() - 1;
					for (int j = 29; j >= 0; j--) {
						va[at].x[2]--;
						tb = !!(tc & (1 << j));
						if (va[at].x[tb])
							va.push_back(va[va[at].x[tb]]);
						else
							va.push_back(sat);
						at = va[at].x[tb] = va.size() - 1;
					}
					va[at].x[2]--;
				}
			} else {
				ra[i] = ra[i - 1];
				rb[i] = rb[i - 1];
				at = rb[i];
				for (int j = 0; j < n && at != -1; j++) {
					tb = y[j] - 'a';
					if (vb[at].x[tb])
						at = vb[at].x[tb];
					else
						at = -1;
				}
				if (at == -1 || !vb[at].x[26])
					printf("-1\n");
				else
					printf("%d\n", f1(0, vb[at].x[26], 0, 1 << 30, ra[i]));
				fflush(stdout);
			}
		}
	}
	return 0;
}


#include <cstdio>
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <string>
#include <cstring>
#include <sstream>
#include <queue>
#include <iomanip>
#include <algorithm>
#pragma comment(linker, "/STACK:16000000")
using namespace std;

typedef long long ll;

const int Maxn = 100005;
const int Maxm = 20;
const int Maxs = 524288;

int n, q;
vector <int> neigh[Maxn];
int cur, lef[Maxn], rig[Maxn];
int L[Maxn], P[Maxn][Maxm];
int my[Maxn], myval[Maxn];
ll G;
ll sum[Maxs], flag[Maxs];
int root;

void downOn(int v, int sz, ll val)
{
	sum[v] += val * ll(sz); flag[v] += val;
}

void Down(int v, int l, int m, int r)
{
	if (flag[v]) {
		downOn(2 * v, m - l + 1, flag[v]);
		downOn(2 * v + 1, r - m, flag[v]);
		flag[v] = 0;
	}
}

void Union(int v)
{
	sum[v] = sum[2 * v] + sum[2 * v + 1];
}

void Create(int v, int l, int r)
{
	if (l == r) sum[v] = myval[l];
	else {
		int m = l + r >> 1;
		Create(2 * v, l, m); Create(2 * v + 1, m + 1, r);
		Union(v);
	}
}

void Add(int v, int l, int r, int a, int b, ll val)
{
	if (l == a && r == b) downOn(v, r - l + 1, val);
	else {
		int m = l + r >> 1;
		Down(v, l, m, r);
		if (a <= m) Add(2 * v, l, m, a, min(m, b), val);
		if (m + 1 <= b) Add(2 * v + 1, m + 1, r, max(m + 1, a), b, val);
		Union(v);
	}
}

ll Get(int v, int l, int r, int a, int b)
{
	if (l == a && r == b) return sum[v];
	else {
		int m = l + r >> 1;
		Down(v, l, m, r);
		if (b <= m) return Get(2 * v, l, m, a, b);
		if (m + 1 <= a) return Get(2 * v + 1, m + 1, r, a, b);
		return Get(2 * v, l, m, a, m) + Get(2 * v + 1, m + 1, r, m + 1, b);
	}
}

void Traverse(int v)
{
	lef[v] = ++cur; myval[lef[v]] = my[v];
	for (int i = 0; i < neigh[v].size(); i++) {
		int u = neigh[v][i];
		if (P[v][0] == u) continue;
		L[u] = L[v] + 1; P[u][0] = v;
		Traverse(u);
	}
	rig[v] = cur;
}

int getLCA(int a, int b)
{
	if (L[a] < L[b]) swap(a, b);
	for (int i = Maxm - 1; i >= 0; i--)
		if (L[a] - (1 << i) >= L[b])
			a = P[a][i];
	if (a == b) return a;
	for (int i = Maxm - 1; i >= 0; i--)
		if (P[a][i] != P[b][i])
			a = P[a][i], b = P[b][i];
	return P[a][0];
}

int goUp(int a, int b)
{
	for (int i = Maxm - 1; i >= 0; i--)
		if (L[a] - (1 << i) > L[b])
			a = P[a][i];
	return a;
}

bool Has(int v, int u) { return lef[v] <= lef[u] && rig[u] <= rig[v]; }

void Add(int u, int v, int val)
{
	int lca = getLCA(u, v);
	if (lca == root) { G += val; return; }
	if (Has(root, lca)) { Add(1, 1, n, lef[lca], rig[lca], val); return; }
	int ulca = getLCA(root, u);
	int vlca = getLCA(root, v);
	if (L[lca] > L[ulca] && L[lca] > L[vlca]) { Add(1, 1, n, lef[lca], rig[lca], val); return; }
	int C = L[ulca] > L[vlca]? ulca: vlca;
	if (C == root) { G += val; return; }
	int lst = goUp(root, C);
	G += val;
	Add(1, 1, n, lef[lst], rig[lst], -val);
}

ll Get(int v)
{
	if (root == v) return G * ll(n) + sum[1];
	int lca = getLCA(root, v);
	if (Has(root, v) || v != lca) return G * ll(rig[v] - lef[v] + 1) + Get(1, 1, n, lef[v], rig[v]);
	int lst = goUp(root, v);
	return G * ll(n - (rig[lst] - lef[lst] + 1)) + (sum[1] - Get(1, 1, n, lef[lst], rig[lst]));
}

int main()
{
	scanf("%d %d", &n, &q);
	for (int i = 1; i <= n; i++)
		scanf("%d", &my[i]);
	for (int i = 0; i < n - 1; i++) {
		int a, b; scanf("%d %d", &a, &b);
		neigh[a].push_back(b);
		neigh[b].push_back(a);
	}
	root = 1; Traverse(1);
	for (int j = 1; j < Maxm; j++)
		for (int i = 1; i <= n; i++)
			P[i][j] = P[P[i][j - 1]][j - 1];
	Create(1, 1, n);
	while (q--) {
		int typ, a, b, c; scanf("%d %d", &typ, &a);
		if (typ == 1) root = a;
		else if (typ == 2) {
			scanf("%d %d", &b, &c);
			Add(a, b, c);
		} else if (typ == 3)
			printf("%I64d\n", Get(a));
	}
	return 0;
}