#include <bits/stdc++.h>
using namespace std;
#define TASK "FAKERNUM"
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef pair<int, int> ii;
const int MAX_N = 1e5;
const int MAX_Q = 5e5;
const int MOD = (int)1e9 + 7;
template <class X, class Y>
bool maximize(X& x, const Y& y) {
if (x >= y) return false;
x = y;
return true;
};
template <class X, class Y>
bool minimize(X& x, const Y& y) {
if (x <= y) return false;
x = y;
return true;
};
struct Query {
int type, u, v;
ll x;
Query() {};
};
int n, q, timer;
int tin[MAX_N + 5], tout[MAX_N + 5], par[MAX_N + 5], depth[MAX_N + 5], id[MAX_N + 5];
bool match[20][20];
ll a[MAX_N + 5];
vector<int> adj[MAX_N + 5];
vector<Query> queries;
void preDfs(int u, int p) {
tin[u] = ++timer;
id[tin[u]] = u;
par[u] = p;
depth[u] = depth[p] + 1;
for (int v : adj[u]) {
if (v == p) continue;
preDfs(v, u);
};
tout[u] = timer;
};
bool isAncestor(int u, int v) {
return tin[u] <= tin[v] && tout[u] >= tout[v];
};
bool fakerNumber(ll x) {
string s = to_string(x);
if (count(all(s), '3') + count(all(s), '6') != s.size()) return false;
int n = s.size();
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) match[i][j] = false;
int numPalin = 0;
for (int i = 0; i < n; i++) {
match[i][i] = true;
numPalin++;
};
for (int i = 0; i + 1 < n; i++) {
match[i][i + 1] = s[i] == s[i + 1];
numPalin += match[i][i + 1];
};
for (int l = n - 3; l >= 0; l--) {
for (int r = l + 2; r < n; r++) {
match[l][r] = match[l + 1][r - 1] && (s[l] == s[r]);
numPalin += match[l][r];
};
};
return (double)numPalin / (n * (n + 1) / 2) > 0.5;
};
namespace SUBTASK_1 {
const int N = MAX_N;
const int Q = 5000;
ll b[MAX_N + 5];
int subtreeQuery(int u) {
int res = 0;
for (int i = tin[u]; i <= tout[u]; i++) {
int v = id[i];
if (fakerNumber(b[v])) res++;
};
return res;
};
int pathQuery(int u, int v) {
if (depth[u] > depth[v]) swap(u, v);
int res = 0;
while (depth[v] != depth[u]) {
res += fakerNumber(b[v]);
v = par[v];
};
while (u != v) {
res += fakerNumber(b[u]);
res += fakerNumber(b[v]);
u = par[u], v = par[v];
};
res += fakerNumber(b[u]);
return res;
};
void updatePath(int u, int v, ll val) {
if (depth[u] > depth[v]) swap(u, v);
while (depth[v] != depth[u]) {
b[v] += val;
v = par[v];
};
while (u != v) {
b[u] += val;
b[v] += val;
u = par[u], v = par[v];
};
b[u] += val;
};
void Solve() {
for (int u = 1; u <= n; u++) b[u] = a[u];
for (const Query& query : queries) {
int type = query.type, u = query.u;
if (type == 1) {
int v = query.v;
ll x = query.x;
updatePath(u, v, x);
};
if (type == 2) {
int v = query.v;
cout << pathQuery(u, v) << '\n';
};
if (type == 3) {
cout << subtreeQuery(u) << '\n';
};
};
};
}; // namespace SUBTASK_1
namespace SUBTASK_2 {
const int N = MAX_N;
const int Q = MAX_Q;
int up[N + 5][18];
int pref[N + 5], f[N + 5];
bool checkSubtask() {
for (const Query& query : queries)
if (query.type == 1) return false;
return true;
};
void dfs(int u) {
up[u][0] = par[u];
for (int i = 1; (1 << i) <= n; i++)
up[u][i] = up[up[u][i - 1]][i - 1];
f[u] = f[par[u]] + fakerNumber(a[u]);
for (int v : adj[u]) {
if (v == par[u]) continue;
dfs(v);
};
};
int __lca(int u, int v) {
if (isAncestor(u, v)) return u;
if (isAncestor(v, u)) return v;
for (int i = 17; i >= 0; i--)
if ((1 << i) <= n && !isAncestor(up[u][i], v))
u = up[u][i];
return up[u][0];
};
void Solve() {
for (int i = 1; i <= n; i++) pref[i] = pref[i - 1] + fakerNumber(a[id[i]]);
dfs(1);
for (const Query& query : queries) {
int type = query.type, u = query.u;
if (type == 2) {
int v = query.v;
int lca = __lca(u, v);
cout << f[u] + f[v] - 2 * f[lca] + fakerNumber(a[lca]) << '\n';
};
if (type == 3) {
cout << pref[tout[u]] - pref[tin[u] - 1] << '\n';
};
};
};
}; // namespace SUBTASK_2
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
if (fopen(TASK ".INP", "r")) {
freopen(TASK ".INP", "r", stdin);
freopen(TASK ".OUT", "w", stdout);
};
cin >> n >> q;
for (int u = 1; u <= n; u++) {
cin >> a[u];
};
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
};
queries.resize(q);
for (Query& query : queries) {
cin >> query.type >> query.u;
if (query.type == 1) cin >> query.v >> query.x;
if (query.type == 2) cin >> query.v;
};
preDfs(1, 1);
if (SUBTASK_2::checkSubtask())
return SUBTASK_2::Solve(), 0;
SUBTASK_1::Solve();
// cout << endl;
};