#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<iostream>
#include<algorithm>

using namespace std;

#define MP make_pair
#define PB push_back
#define foreach(e,x) for(__typeof(x.begin()) e=x.begin(); e!=x.end(); ++e)

typedef long long LL;
typedef vector<int> VI;
typedef pair<int, int> mapII;

const int MAX_N = 400000 + 10;
const int INF = 2000000 + 10;
const double EPS = 1e-8;

struct INFO
{
	LL maxL, maxR, ans, sum;
	INFO() {
		maxL = maxR = ans = - INF;
		sum = 0;
	}
};

INFO operator + (const INFO &a, const INFO &b) 
{
	INFO ret;
	ret.maxL = max(a.maxL, a.sum + b.maxL);
	ret.maxR = max(b.maxR, b.sum + a.maxR);
	ret.ans = max(max(a.ans, b.ans), a.maxR + b.maxL);
	ret.sum = a.sum + b.sum;
	return ret;
}

struct Tree
{
	int l, r, cover, size;
	INFO info;
	Tree *ch[2];
	Tree() {}
	Tree(int _l, int _r, int *sqn) {
		l = _l, r = _r;
		cover = INF;
		if (l + 1 == r) {
			info.sum = sqn[l];
			info.ans = sqn[l];
			info.maxL = info.maxR = sqn[l];
			size = 1;
			return;
		}
		int mid = (l + r) >> 1;
		ch[0] = new Tree(l, mid, sqn);
		ch[1] = new Tree(mid, r, sqn);
		info = ch[0]->info + ch[1]->info;
		size = ch[0]->size + ch[1]->size;
	}

	void appCover(int x) {
		cover = x;
		info.sum = x * size;
		if (x >= 0) {
			info.maxL = info.maxR = info.ans = x * size;
		} else {
			info.maxL = info.maxR = info.ans = x;
		}
	}

	void relax() {
		if (cover == INF) return;
		ch[0]->appCover(cover);
		ch[1]->appCover(cover);
		cover = INF;
	}

	INFO query(int _l, int _r) {
		if (_l <= l && _r >= r) 
			return info;
		if (_r <= l || _l >= r) return INFO();
		relax();
		return ch[0]->query(_l, _r) + ch[1]->query(_l, _r);
	}

	void modify(int _l, int _r, int c) {
		if (_l <= l && _r >= r) {
			appCover(c);
			return;
		}
		if (_r <= l || _l >= r) 
			return;
		relax();
		ch[0]->modify(_l, _r, c);
		ch[1]->modify(_l, _r, c);
		info = ch[0]->info + ch[1]->info;
	}
};

int N, M, val[MAX_N];
vector<int> adj[MAX_N];
int father[MAX_N], size[MAX_N], depth[MAX_N];
int bfsOrd[MAX_N], pathId[MAX_N], ordInPath[MAX_N], sqn[MAX_N];
Tree *root[MAX_N];

void doBfs(int s)
{
	int *que = bfsOrd;
	int qh = 0, qt = 0;
	father[s] = -1; depth[s] = 0;

	for(que[qt ++] = s; qh < qt; ) {
		int u = que[qh ++];
		foreach(iter, adj[u]) {
			int v = *iter;
			if (v == father[u])
				continue;
			father[v] = u;
			depth[v] = depth[u] + 1;
			que[qt ++] = v;
		}
	}
}

void doSplit()
{
	for(int i = N - 1; i >= 0; -- i) {
		int u = bfsOrd[i];
		size[u] = 1;
		foreach(iter, adj[u]) {
			int v = *iter;
			if (v == father[u]) continue;
			size[u] += size[v];
		}
	}

	memset(pathId, -1, sizeof pathId);
	for(int i = 0; i < N; ++ i) {
		int top = bfsOrd[i];
		if (pathId[top] != -1)
			continue;
		int cnt = 0;
		for(int u = top; u != -1; ) {
			sqn[cnt] = val[u];
			ordInPath[u] = cnt;
			pathId[u] = top;
			++ cnt;

			int next = -1;
			foreach(iter, adj[u]) {
				int v = *iter;
				if (v == father[u])
					continue;
				if (next < 0 || size[next] < size[v])
					next = v;
			}
			u = next;
		}
		root[top] = new Tree(0, cnt, sqn);
	}
}

void modify(int u, int v, int c)
{
	int top;
	for( ; pathId[u] != pathId[v]; ) {
		if (depth[pathId[u]] > depth[pathId[v]]) {
			top = pathId[u];
			root[top]->modify(0, ordInPath[u] + 1, c);
			u = father[top];
		} else {
			top = pathId[v];
			root[top]->modify(0, ordInPath[v] + 1, c);
			v = father[top];
		}
	}
	top = pathId[u];
	int l = min(ordInPath[v], ordInPath[u]);
	int r = max(ordInPath[v], ordInPath[u]);
	root[top]->modify(l, r + 1, c);
}

int query(int u, int v)
{
	int top;
	INFO infou, infov;
	for( ; pathId[u] != pathId[v]; ) {
		if (depth[pathId[u]] > depth[pathId[v]]) {
			top = pathId[u];
			infou = root[top]->query(0, ordInPath[u] + 1) + infou;
			u = father[top];
		} else {
			top = pathId[v];
			infov = root[top]->query(0, ordInPath[v] + 1) + infov;
			v = father[top];
		}
	}
	
	top = pathId[u];
	if (ordInPath[u] < ordInPath[v]) {
		swap(infou.maxL, infou.maxR);
		infou = infou + root[top]->query(ordInPath[u], ordInPath[v] + 1);
		infou = infou + infov;
		return (int)infou.ans;
	} else {
		swap(infov.maxL, infov.maxR);
		infov = infov + root[top]->query(ordInPath[v], ordInPath[u] + 1);
		infov = infov + infou;
		return (int)infov.ans;
	}
}

void solve()
{
	int u, v;
	scanf("%d%d", &N, &M);
	for(int i = 0; i < N; ++ i) {
		scanf("%d", &val[i]);
		adj[i].clear();
	}
	for(int i = 0; i < N - 1; ++ i) {
		scanf("%d%d", &u, &v);
		-- u, -- v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}

	doBfs(0);
	doSplit();

	int t, c;
	for(int i = 0; i < M; ++ i) {
		scanf("%d%d%d%d", &t, &u, &v, &c);
		-- u, -- v;
		if (t == 1) {
			modify(u, v, c);
		} else {
			printf("%d\n", query(u, v));
		}
	}
}

int main()
{
	solve();
	return 0;
}

Measured memory_bytes: 62264