#include <algorithm>
#include <iostream>
#include <iterator>
#include <stack>
#include <tuple>
#include <utility>
#include <vector>
namespace lc {
struct Edge {
	int to;
	Edge() : to(0) { }
	explicit Edge(int to) : to(to) { }
};
}
namespace lc {
template <typename EdgeType>
class AdjacencyList {
public:
	typedef std::vector<EdgeType> ListType;
private:
	std::vector<ListType> m_lists;
public:
	explicit AdjacencyList(int n = 0)
		: m_lists(n)
	{ }
	int size() const { return m_lists.size(); }
	template <typename... Args>
	void add_edge(int u, Args&&... args){
		m_lists[u].emplace_back(args...);
	}
	const ListType &operator[](int u) const { return m_lists[u]; }
};
}
namespace lc {
class HeavyLightDecomposer {
public:
	struct Connection {
		int local_index;
		int child_path;
		explicit Connection(int li = -1, int cp = -1) :
			local_index(li), child_path(cp)
		{ }
	};
	struct Segment {
		int path;
		int first;
		int last;
		explicit Segment(int path = -1, int first = -1, int last = -1) :
			path(path), first(first), last(last)
		{ }
	};
private:
	typedef std::pair<int, int> pii;
	struct Path {
		int parent_vertex;
		std::vector<Connection> children;
		std::vector<int> vertices;
		int depth;
	};
	std::vector<Path> m_paths;
	std::vector<int> m_path_ids;
	std::vector<int> m_local_indices;
	template <typename EdgeType>
	std::vector<int> compute_subtree_size(
		const AdjacencyList<EdgeType> &conn, int root) const
	{
		const int n = conn.size();
		std::vector<int> subtree_size(n);
		std::vector<bool> passed(n), gathered(n);
		std::stack<pii> count_stack;
		count_stack.push(pii(root, 0));
		while(!count_stack.empty()){
			const pii p = count_stack.top();
			count_stack.pop();
			const int u = p.first, i = p.second;
			if(i == 0){
				passed[u] = true;
				count_stack.push(pii(u, 1));
				for(size_t j = 0; j < conn[u].size(); ++j){
					const int v = conn[u][j].to;
					if(passed[v]){ continue; }
					count_stack.push(pii(v, 0));
				}
			}else{
				int sum = 1;
				gathered[u] = true;
				for(size_t j = 0; j < conn[u].size(); ++j){
					const int v = conn[u][j].to;
					if(!gathered[v]){ continue; }
					sum += subtree_size[v];
				}
				subtree_size[u] = sum;
			}
		}
		return subtree_size;
	}
public:
	HeavyLightDecomposer()
		: m_paths(0)
		, m_path_ids(0)
		, m_local_indices(0)
	{ }
	template <typename EdgeType>
	explicit HeavyLightDecomposer(
		const AdjacencyList<EdgeType> &conn, int root = 0)
		: m_paths(0)
		, m_path_ids(conn.size(), -1)
		, m_local_indices(conn.size(), -1)
	{
		const std::vector<int> subtree_size = compute_subtree_size(conn, root);
		std::stack<pii> decompose_stack;
		decompose_stack.push(pii(root, -1));
		while(!decompose_stack.empty()){
			const pii p = decompose_stack.top();
			decompose_stack.pop();
			const int parent_vertex = p.second;
			const int pid = m_paths.size();
			m_paths.push_back(Path());
			Path &path = m_paths.back();
			path.parent_vertex = parent_vertex;
			if(parent_vertex >= 0){
				const int parent_pid = m_path_ids[parent_vertex];
				const int parent_index = m_local_indices[parent_vertex];
				m_paths[parent_pid].children.push_back(
					Connection(parent_index, pid));
				path.depth = m_paths[parent_pid].depth + 1;
			}else{
				path.depth = 0;
			}
			int cur = p.first;
			while(cur >= 0){
				const int st_size = subtree_size[cur], threshold = st_size / 2;
				m_path_ids[cur] = pid;
				m_local_indices[cur] = path.vertices.size();
				path.vertices.push_back(cur);
				int heavy_edge = -1;
				for(size_t i = 0; i < conn[cur].size(); ++i){
					const int v = conn[cur][i].to;
					if(subtree_size[v] > subtree_size[cur]){ continue; }
					if(heavy_edge < 0 && subtree_size[v] >= threshold){
						heavy_edge = v;
					}else{
						decompose_stack.push(pii(v, cur));
					}
				}
				cur = heavy_edge;
			}
		}
	}
	int path_count() const { return m_paths.size(); }
	int path_length(int p) const { return m_paths[p].vertices.size(); }
	int path_depth(int p) const { return m_paths[p].depth; }
	int parent_path_id(int p) const {
		if(m_paths[p].parent_vertex < 0){ return -1; }
		return m_path_ids[m_paths[p].parent_vertex];
	}
	int parent_local_index(int p) const {
		if(m_paths[p].parent_vertex < 0){ return -1; }
		return m_local_indices[m_paths[p].parent_vertex];
	}
	int path_id(int v) const { return m_path_ids[v]; }
	int local_index(int v) const { return m_local_indices[v]; }
	int vertex_id(int p, int i) const { return m_paths[p].vertices[i]; }
	std::vector<Segment> shortest_path(int u, int v) const {
		std::vector<Segment> usegs, vsegs, result;
		int up = path_id(u), ul = local_index(u);
		int vp = path_id(v), vl = local_index(v);
		while(up != vp){
			const bool update_u = path_depth(up) >= path_depth(vp);
			const bool update_v = path_depth(up) <= path_depth(vp);
			if(update_u){
				usegs.push_back(Segment(up, 0, ul + 1));
				ul = parent_local_index(up);
				up = parent_path_id(up);
			}
			if(update_v){
				vsegs.push_back(Segment(vp, 0, vl + 1));
				vl = parent_local_index(vp);
				vp = parent_path_id(vp);
			}
		}
		for(int i = 0; i < static_cast<int>(usegs.size()); ++i){
			const Segment &s = usegs[i];
			result.push_back(Segment(s.path, s.last - 1, s.first - 1));
		}
		result.push_back(Segment(up, ul, vl + (ul > vl ? -1 : 1)));
		for(int i = static_cast<int>(vsegs.size()) - 1; i >= 0; --i){
			result.push_back(vsegs[i]);
		}
		return result;
	}
};
}
namespace lc {
template <typename Traits>
class LazySegmentTree {
public:
	typedef typename Traits::value_type value_type;
	typedef typename Traits::modifier_type modifier_type;
private:
	Traits m_traits;
	size_t m_size;
	std::vector<value_type> m_values;
	std::vector<modifier_type> m_modifiers;
	void initialize(){
		for(int i = static_cast<int>(m_size) - 2; i >= 0; --i){
			m_values[i] = m_traits(
				m_values[i * 2 + 1], m_values[i * 2 + 2]);
		}
	}
	value_type modify(
		int a, int b, int k, int l, int r, const modifier_type &modifier)
	{
		if(r <= a || b <= l){
			return m_traits.resolve(r - l, m_values[k], m_modifiers[k]);
		}
		if(a <= l && r <= b){
			m_modifiers[k] =
				m_traits.merge_modifier(m_modifiers[k], modifier);
			return m_traits.resolve(r - l, m_values[k], m_modifiers[k]);
		}
		const int c = (l + r) / 2, lk = k * 2 + 1, rk = k * 2 + 2;
		const auto p = m_traits.split_modifier(m_modifiers[k], r - l);
		m_modifiers[k] = m_traits.default_modifier();
		modify(l, c, lk, l, c, p.first);
		modify(c, r, rk, c, r, p.second);
		const auto q = m_traits.split_modifier(modifier, c - std::max(a, l));
		const value_type vl = modify(a, b, lk, l, c, q.first);
		const value_type vr = modify(a, b, rk, c, r, q.second);
		m_values[k] = m_traits(vl, vr);
		return m_values[k];
	}
	value_type query(int a, int b, int k, int l, int r){
		if(r <= a || b <= l){ return m_traits.default_value(); }
		if(a <= l && r <= b){
			return m_traits.resolve(r - l, m_values[k], m_modifiers[k]);
		}
		const int c = (l + r) / 2, lk = k * 2 + 1, rk = k * 2 + 2;
		const auto p = m_traits.split_modifier(m_modifiers[k], c - l);
		m_values[k] = m_traits.resolve(r - l, m_values[k], m_modifiers[k]);
		m_modifiers[k] = m_traits.default_modifier();
		modify(l, c, lk, l, c, p.first);
		modify(c, r, rk, c, r, p.second);
		const value_type vl = query(a, b, lk, l, c);
		const value_type vr = query(a, b, rk, c, r);
		return m_traits(vl, vr);
	}
public:
	explicit LazySegmentTree(
		size_t size = 0, const Traits &traits = Traits())
		: m_traits(traits)
		, m_size(1)
		, m_values()
		, m_modifiers()
	{
		while(m_size < size){ m_size *= 2; }
		m_values.resize(m_size * 2 - 1, m_traits.default_value());
		m_modifiers.resize(m_size * 2 - 1, m_traits.default_modifier());
		initialize();
	}
	template <typename Iterator>
	LazySegmentTree(
		Iterator first, Iterator last, const Traits &traits = Traits())
		: m_traits(traits)
		, m_size(1)
		, m_values()
		, m_modifiers()
	{
		const size_t n = distance(first, last);
		while(m_size < n){ m_size *= 2; }
		m_values.resize(m_size * 2 - 1, m_traits.default_value());
		m_modifiers.resize(m_size * 2 - 1, m_traits.default_modifier());
		copy(first, last, m_values.begin() + m_size - 1);
		initialize();
	}
	void modify(int a, int b, const modifier_type &modifier){
		modify(a, b, 0, 0, m_size, modifier);
	}
	value_type query(int a, int b){
		return query(a, b, 0, 0, m_size);
	}
};
}
using namespace std;
typedef long long ll;
typedef lc::Edge Edge;
typedef tuple<ll, ll> tll;				// timestamp, value
typedef tuple<ll, ll, ll, ll> tllll;	// left, right, partial, all
const ll INF = 1000000000000000000ll;
struct SegtreeTraits {
	typedef tllll value_type;
	typedef tll modifier_type;
	value_type default_value() const { return tllll(0, 0, -INF, 0); }
	modifier_type default_modifier() const { return tll(-1, 0); }
	pair<tll, tll> split_modifier(const tll &m, size_t k) const {
		return make_pair(m, m);
	}
	tll merge_modifier(const tll &a, const tll &b) const { return max(a, b); }
	tllll operator()(const tllll &a, const tllll &b) const {
		if(get<2>(a) < get<3>(a)){ return b; }
		if(get<2>(b) < get<3>(b)){ return a; }
		const tllll result(
			max(get<0>(a), get<3>(a) + get<0>(b)),
			max(get<1>(b), get<1>(a) + get<3>(b)),
			max(max(get<2>(a), get<2>(b)), get<1>(a) + get<0>(b)),
			get<3>(a) + get<3>(b));
		return result;
	}
	tllll resolve(size_t n, const tllll &v, const tll &m) const {
		if(get<0>(m) < 0){ return v; }
		if(get<1>(m) > 0){
			const ll sum = get<1>(m) * n;
			return tllll(sum, sum, sum, sum);
		}else{
			const ll val = get<1>(m);
			return tllll(val, val, val, val * n);
		}
	}
};
int main(){
	ios_base::sync_with_stdio(false);
	int n, q;
	cin >> n >> q;
	vector<int> w(n);
	for(int i = 0; i < n; ++i){ cin >> w[i]; }
	lc::AdjacencyList<Edge> conn(n);
	for(int i = 1; i < n; ++i){
		int a, b;
		cin >> a >> b;
		--a; --b;
		conn.add_edge(a, b);
		conn.add_edge(b, a);
	}
	const lc::HeavyLightDecomposer decomp(conn);
	const int m = decomp.path_count();
	vector<lc::LazySegmentTree<SegtreeTraits>> segtrees(m);
	for(int i = 0; i < m; ++i){
		const int len = decomp.path_length(i);
		vector<tllll> values(len);
		for(int j = 0; j < len; ++j){
			const int x = w[decomp.vertex_id(i, j)];
			values[j] = tllll(x, x, x, x);
		}
		segtrees[i] = lc::LazySegmentTree<SegtreeTraits>(
			values.begin(), values.end());
	}
	for(int ts = 0; ts < q; ++ts){
		int t, a, b, c;
		cin >> t >> a >> b >> c;
		--a; --b;
		const vector<lc::HeavyLightDecomposer::Segment> route =
			decomp.shortest_path(a, b);
		if(t == 1){
			for(size_t i = 0; i < route.size(); ++i){
				const int p = route[i].path;
				int first = route[i].first, last = route[i].last;
				if(first > last){
					swap(first, last);
					++first; ++last;
				}
				segtrees[p].modify(first, last, tll(ts, c));
			}
		}else if(t == 2){
			const int len = route.size();
			vector<tllll> values(len);
			vector<ll> integral(len + 1);
			ll answer = -INF;
			for(int i = 0; i < len; ++i){
				const int p = route[i].path;
				int first = route[i].first, last = route[i].last;
				if(first > last){
					const tllll v = segtrees[p].query(last + 1, first + 1);
					values[i] = tllll(
						get<1>(v), get<0>(v), get<2>(v), get<3>(v));
				}else{
					const tllll v = segtrees[p].query(first, last);
					values[i] = v;
				}
				integral[i] = get<3>(values[i]);
				answer = max(answer, get<2>(values[i]));
			}
			ll sum = 0;
			for(int i = 0; i < len; ++i){
				const ll t = integral[i];
				integral[i] = sum;
				sum += t;
			}
			integral.back() = sum;
			for(int l = 0; l < len; ++l){
				for(int r = l + 1; r < len; ++r){
					const ll x = integral[r] - integral[l + 1];
					answer = max(
						answer, get<1>(values[l]) + get<0>(values[r]) + x);
				}
			}
			cout << answer << endl;
		}
	}
	return 0;
}

Measured memory_bytes: 64920