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

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);
		}
	}
};

const ll INF = 1e17;

struct RURMQ {
	struct type {
		ll left, right, all, sum, size;
		type() : left(-INF), right(-INF), all(-INF), sum(0), size(0) {}
		type(ll val) : left(val), right(val), all(val), sum(val), size(1) {}
		type(ll l, ll r, ll a, ll s, ll si) : left(l), right(r), all(a), sum(s), size(si) {}
		bool operator==(const type& r) const {
			return left == r.left && right == r.right && all == r.all && sum == r.sum && size == r.size;
		}
	};
	static type id1() { return type(); }
	static type id2() { return type(-INF, -INF, -INF, -INF, -1); }
	static type op1(const type& l, const type & r) {
		return type(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 type op2(const type& l, const type & r) {
		if (r == id2()) return l;
		if (l.size < 0) return r;
		ll val = r.all;
		if (val > 0) {
			return type(val * l.size, val * l.size, val * l.size, val * l.size, l.size);
		}
		else {
			return type(val, val, val, val * l.size, l.size);
		}
	}
};

template <typename M>
class lSegmentTree {
	using T = typename M::type;
	const int n;
	vector<T> data, lazy;
	int size(int n) {
		int res = 1;
		while (res < n) res <<= 1;
		return res;
	}
	void push(int node) {
		if (lazy[node] == M::id2()) return;
		if (node < n) {
			lazy[node * 2] = M::op2(lazy[node * 2], lazy[node]);
			lazy[node * 2 + 1] = M::op2(lazy[node * 2 + 1], lazy[node]);
		}
		data[node] = M::op2(data[node], lazy[node]);
		lazy[node] = M::id2();
	}
	void suc(int l, int r, int node, int lb, int ub, T val) {
		if (ub <= l || r <= lb) return;
		push(node);
		if (l <= lb && ub <= r) {
			lazy[node] = val;
			return;
		}
		int c = (lb + ub) / 2;
		suc(l, r, node * 2, lb, c, val);
		suc(l, r, node * 2 + 1, c, ub, val);
		data[node] = M::op1(M::op2(data[node * 2], lazy[node * 2]), M::op2(data[node * 2 + 1], lazy[node * 2 + 1]));
	}
	T sub(int l, int r, int node, int lb, int ub) {
		if (ub <= l || r <= lb) return M::id1();
		push(node);
		if (l <= lb && ub <= r) {
			return data[node];
		}
		int c = (lb + ub) / 2;
		return M::op1(sub(l, r, node * 2, lb, c), sub(l, r, node * 2 + 1, c, ub));
	}
public:
	lSegmentTree(int n_) : n(size(n_)), data(n * 2, M::id1()), lazy(n * 2, M::id2()) {}
	void init(const vector<T>& data_) {
		for (int i = 0; i < (int)data_.size(); i++)
			data[i + n] = data_[i];
		for (int i = n - 1; i >= 0; i--)
			data[i] = M::op1(data[i * 2], data[i * 2 + 1]);
		fill(lazy.begin(), lazy.end(), M::id2());
	}
	void update(int l, int r, T val) {
		suc(l, r + 1, 1, 0, n, val);
	}
	T find(int l, int r) {
		return sub(l, r + 1, 1, 0, n);
	}
};

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<RURMQ::type> base(N);
	for (int i = 0; i < N; i++) {
		base[hld[i]] = RURMQ::type(A[i]);
	}
	lSegmentTree<RURMQ> lst(N);
	lst.init(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, RURMQ::type(c, c, c, c, -1));
			});
		}
		else {
			RURMQ::type 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 = RURMQ::op1(res1, t);
				}
				else {
					res2 = RURMQ::op1(lst.find(l, r), res2);
				}
			});
			printf("%d\n", (int)RURMQ::op1(res1, res2).all);
		}
	}
	return 0;
}

Measured memory_bytes: 75900