#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct RURMS {
	static const ll INF = 1e17;
	struct t1 {
		ll left, right, all, sum, size;
		t1() : left(-INF), right(-INF), all(-INF), sum(0), size(0) {}
		t1(ll val) : left(val), right(val), all(val), sum(val), size(1) {}
		t1(ll l, ll r, ll a, ll s, ll si) : left(l), right(r), all(a), sum(s), size(si) {}
		bool operator==(const t1& r) const {
			return left == r.left && right == r.right && all == r.all && sum == r.sum && size == r.size;
		}
	};
	using t2 = ll;
	static t1 id1() { return t1(); }
	static t2 id2() { return -INF; }
	static t1 op1(const t1& l, const t1 & r) {
		return t1(max(l.left, l.sum + r.left), max(r.right, r.sum + l.right), max({ l.all, r.all, l.right + r.left }), l.sum + r.sum, l.size + r.size);
	}
	static t1 op2(const t1& l, const t2 & r) {
		if (r == id2()) return l;
		if (r > 0) return t1(r * l.size, r * l.size, r * l.size, r * l.size, l.size);
		return t1(r, r, r, r * l.size, l.size);
	}
	static t2 op3(const t2& l, const t2 & r) {
		return r == id2() ? l : r;
	}
};

template <typename M>
class LazySegmentTree {
	using T1 = typename M::t1;
	using T2 = typename M::t2;
	struct T {
		T1 val;
		T2 lazy;
		T(T1 v, T2 l) : val(v), lazy(l) {}
	};
	const int h, n;
	vector<T> data;
	void push(int node) {
		if (data[node].lazy == M::id2()) return;
		if (node < n) {
			data[node * 2].lazy = M::op3(data[node * 2].lazy, data[node].lazy);
			data[node * 2 + 1].lazy = M::op3(data[node * 2 + 1].lazy, data[node].lazy);
		}
		data[node].val = M::op2(data[node].val, data[node].lazy);
		data[node].lazy = M::id2();
	}
	void update(int node) {
		data[node].val = M::op1(M::op2(data[node * 2].val, data[node * 2].lazy), M::op2(data[node * 2 + 1].val, data[node * 2 + 1].lazy));
	}
public:
	LazySegmentTree(int n_)
		: h(ceil(log2(n_))), n(1 << h), data(n * 2, T(M::id1(), M::id2())) {}
	LazySegmentTree(int n_, T1 v1)
		: h(ceil(log2(n_))), n(1 << h), data(n * 2, T(v1, M::id2())) {}
	LazySegmentTree(const vector<T1>& data_)
		: h(ceil(log2(data_.size()))), n(1 << h), data(n * 2, T(M::id1(), M::id2())) {
		init(data_);
	}
	void init() {
		for (int i = n - 1; i >= 1; i--) data[i].val = M::op1(data[i * 2].val, data[i * 2 + 1].val);
	}
	void init(const vector<T1>& data_) {
		for (int i = 0; i < (int)data_.size(); i++) data[i + n].val = data_[i];
		init();
	}
	void update(int l, int r, T2 val) {
		l += n, r += n;
		for (int i = h; i > 0; i--) push(l >> i), push(r >> i);
		int tl = l, tr = r;
		r++;
		while (l < r) {
			if (l & 1) data[l].lazy = M::op3(data[l].lazy, val), l++;
			if (r & 1) r--, data[r].lazy = M::op3(data[r].lazy, val);
			l >>= 1; r >>= 1;
		}
		while (tl >>= 1, tr >>= 1, tl) {
			if (data[tl].lazy == M::id2()) update(tl);
			if (data[tr].lazy == M::id2()) update(tr);
		}
	}
	T1 find(int l, int r) {
		l += n, r += n;
		for (int i = h; i > 0; i--) push(l >> i), push(r >> i);
		r++;
		T1 res1 = M::id1(), res2 = M::id1();
		while (l < r) {
			if (l & 1) res1 = M::op1(res1, M::op2(data[l].val, data[l].lazy)), l++;
			if (r & 1) r--, res2 = M::op1(M::op2(data[r].val, data[r].lazy), res2);
			l >>= 1; r >>= 1;
		}
		return M::op1(res1, res2);
	}
};

class HLD {
	vector<vector<int>> G;
	vector<int> vid, head, heavy, par, dep;
	int dfs(int v, int prev) {
		par[v] = prev;
		int cnt = 1, ma = 0;
		for (int to : G[v]) if (to != prev) {
			dep[to] = dep[v] + 1;
			int c = dfs(to, v);
			cnt += c;
			if (ma < c) {
				ma = c;
				heavy[v] = to;
			}
		}
		return cnt;
	}
public:
	HLD(int n) : G(n), vid(n, -1), head(n), heavy(n, -1), par(n), dep(n) {}
	void add(int u, int v) {
		G[u].push_back(v);
		G[v].push_back(u);
	}
	void build() {
		dfs(0, -1);
		int k = 0;
		queue<int> q;
		q.push(0);
		while (!q.empty()) {
			int h = q.front(); q.pop();
			for (int i = h; i != -1; i = heavy[i]) {
				vid[i] = k++;
				head[i] = h;
				for (int j : G[i]) if (j != par[i] && j != heavy[i]) q.push(j);
			}
		}
	}
	int operator[](int v) {
		return vid[v];
	}
	void for_each(int u, int v, function<void(int, int)> f) {
		while (true) {
			if (vid[u] > vid[v]) swap(u, v);
			f(max(vid[head[v]], vid[u]), vid[v]);
			if (head[u] == head[v]) break;
			v = par[head[v]];
		}
	}
	void for_each_directed(int u, int v, function<void(int, int, int)> f) {
		if (vid[u] > vid[v]) {
			f(max(vid[head[u]], vid[v]), vid[u], 1);
			if (head[u] != head[v]) for_each_directed(par[head[u]], v, f);
		}
		else {
			f(max(vid[head[v]], vid[u]), vid[v], 0);
			if (head[u] != head[v]) for_each_directed(u, par[head[v]], f);
		}
	}
};

int main()
{
	ios::sync_with_stdio(false), cin.tie(0);
	int N, Q;
	cin >> N >> Q;
	vector<ll> A(N);
	for (int i = 0; i < N; i++) {
		cin >> A[i];
	}
	HLD hld(N);
	for (int i = 0; i < N - 1; i++) {
		int u, v;
		cin >> u >> v; u--, v--;
		hld.add(u, v);
	}
	hld.build();
	vector<RURMS::t1> base(N);
	for (int i = 0; i < N; i++) {
		base[hld[i]] = RURMS::t1(A[i]);
	}
	LazySegmentTree<RURMS> lst(base);
	while (Q--) {
		int t, a, b, c;
		cin >> t >> a >> b >> c; a--, b--;
		if (t == 1) {
			hld.for_each(a, b, [&](int l, int r) {
				lst.update(l, r, c);
			});
		}
		else {
			RURMS::t1 res1, res2;
			hld.for_each_directed(a, b, [&](int l, int r, int b) {
				if (l > r) swap(l, r);
				if (b) {
					auto t = lst.find(l, r); swap(t.left, t.right);
					res1 = RURMS::op1(res1, t);
				}
				else {
					res2 = RURMS::op1(lst.find(l, r), res2);
				}
			});
			printf("%d\n", (int)RURMS::op1(res1, res2).all);
		}
	}
	return 0;
}

