Problem: The New Year holidays are over, but Resha doesn't want to throw away the New Year tree. He invited his best friends Kerim and Gural to help him to redecorate the New Year tree.

The New Year tree is an undirected tree with n vertices and root in the vertex 1.

You should process the queries of the two types:

  1. Change the colours of all vertices in the subtree of the vertex v to the colour c. 
  2. Find the number of different colours in the subtree of the vertex v. 

Input

The first line contains two integers n, m (1 ≤ n, m ≤ 4·105) — the number of vertices in the tree and the number of the queries.

The second line contains n integers ci (1 ≤ ci ≤ 60) — the colour of the i-th vertex.

Each of the next n - 1 lines contains two integers xj, yj (1 ≤ xj, yj ≤ n) — the vertices of the j-th edge. It is guaranteed that you are given correct undirected tree.

The last m lines contains the description of the queries. Each description starts with the integer tk (1 ≤ tk ≤ 2) — the type of the k-th query. For the queries of the first type then follows two integers vk, ck (1 ≤ vk ≤ n, 1 ≤ ck ≤ 60) — the number of the vertex whose subtree will be recoloured with the colour ck. For the queries of the second type then follows integer vk (1 ≤ vk ≤ n) — the number of the vertex for which subtree you should find the number of different colours.

Output

For each query of the second type print the integer a — the number of different colours in the subtree of the vertex given in the query.

Each of the numbers should be printed on a separate line in order of query appearing in the input.

Examples

Input

7 10
1 1 1 1 1 1 1
1 2
1 3
1 4
3 5
3 6
3 7
1 3 2
2 1
1 4 3
2 1
1 2 5
2 1
1 6 4
2 1
2 2
2 3


Output

2
3
4
5
1
2


Input

23 30
1 2 2 6 5 3 2 1 1 1 2 4 5 3 4 4 3 3 3 3 3 4 6
1 2
1 3
1 4
2 5
2 6
3 7
3 8
4 9
4 10
4 11
6 12
6 13
7 14
7 15
7 16
8 17
8 18
10 19
10 20
10 21
11 22
11 23
2 1
2 5
2 6
2 7
2 8
2 9
2 10
2 11
2 4
1 12 1
1 13 1
1 14 1
1 15 1
1 16 1
1 17 1
1 18 1
1 19 1
1 20 1
1 21 1
1 22 1
1 23 1
2 1
2 5
2 6
2 7
2 8
2 9
2 10
2 11
2 4


Output

6
1
3
3
2
1
2
3
5
5
1
2
2
1
1
1
2
3

Solution (in JAVA):

Solution:
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.io.BufferedWriter;
import java.util.InputMismatchException;
import java.io.IOException;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Egor Kulikov (egor@egork.net)
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		OutputWriter out = new OutputWriter(outputStream);
		TaskE solver = new TaskE();
		solver.solve(1, in, out);
		out.close();
	}

	static class TaskE {
		public void solve(int testNumber, InputReader in, OutputWriter out) {
			int n = in.readInt();
			int m = in.readInt();
			int[] color = IOUtils.readIntArray(in, n);
			int[] x = new int[n - 1];
			int[] y = new int[n - 1];
			IOUtils.readIntArrays(in, x, y);
			MiscUtils.decreaseByOne(x, y, color);
			BidirectionalGraph graph = BidirectionalGraph.createGraph(n, x, y);
			DFSOrder order = new DFSOrder(graph);
			LongIntervalTree tree = new LongIntervalTree(n) {

				protected long joinValue(long left, long right) {
					return left | right;
				}

				protected long joinDelta(long was, long delta) {
					return delta == -1 ? was : delta;
				}

				protected long accumulate(long value, long delta, int length) {
					return delta == -1 ? value : delta;
				}

				protected long neutralValue() {
					return 0;
				}

				protected long neutralDelta() {
					return -1;
				}
			};
			for (int i = 0; i < n; i++) {
				tree.update(order.position[i], order.position[i], 1L << color[i]);
			}
			for (int i = 0; i < m; i++) {
				int t = in.readInt();
				int v = in.readInt() - 1;
				if (t == 1) {
					int c = in.readInt() - 1;
					tree.update(order.position[v], order.end[v], 1L << c);
				} else {
					out.printLine(Long.bitCount(tree.query(order.position[v], order.end[v])));
				}
			}
		}
	}

	static class InputReader {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int numChars;
		private SpaceCharFilter filter;

		public InputReader(InputStream stream) {
			this.stream = stream;
		}

		public int read() {
			if (numChars == -1) {
				throw new InputMismatchException();
			}
			if (curChar >= numChars) {
				curChar = 0;
				try {
					numChars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (numChars <= 0) {
					return -1;
				}
			}
			return buf[curChar++];
		}

		public int readInt() {
			int c = read();
			while (isSpaceChar(c)) {
				c = read();
			}
			int sgn = 1;
			if (c == '-') {
				sgn = -1;
				c = read();
			}
			int res = 0;
			do {
				if (c < '0' || c > '9') {
					throw new InputMismatchException();
				}
				res *= 10;
				res += c - '0';
				c = read();
			} while (!isSpaceChar(c));
			return res * sgn;
		}

		public boolean isSpaceChar(int c) {
			if (filter != null) {
				return filter.isSpaceChar(c);
			}
			return isWhitespace(c);
		}

		public static boolean isWhitespace(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		public interface SpaceCharFilter {
			public boolean isSpaceChar(int ch);
		}
	}

	static class OutputWriter {
		private final PrintWriter writer;

		public OutputWriter(OutputStream outputStream) {
			writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
		}

		public OutputWriter(Writer writer) {
			this.writer = new PrintWriter(writer);
		}

		public void close() {
			writer.close();
		}

		public void printLine(int i) {
			writer.println(i);
		}
	}

	static class MiscUtils {
		public static void decreaseByOne(int[]... arrays) {
			for (int[] array : arrays) {
				for (int i = 0; i < array.length; i++) {
					array[i]--;
				}
			}
		}
	}

	static class IOUtils {
		public static int[] readIntArray(InputReader in, int size) {
			int[] array = new int[size];
			for (int i = 0; i < size; i++) {
				array[i] = in.readInt();
			}
			return array;
		}

		public static void readIntArrays(InputReader in, int[]... arrays) {
			for (int i = 0; i < arrays[0].length; i++) {
				for (int j = 0; j < arrays.length; j++) {
					arrays[j][i] = in.readInt();
				}
			}
		}
	}

	static abstract class IntervalTree {
		protected int size;

		protected IntervalTree(int size) {
			this(size, true);
		}

		public IntervalTree(int size, boolean shouldInit) {
			this.size = size;
			int nodeCount = Math.max(1, Integer.highestOneBit(size) << 2);
			initData(size, nodeCount);
			if (shouldInit) {
				init();
			}
		}

		protected abstract void initData(int size, int nodeCount);

		protected abstract void initAfter(int root, int left, int right, int middle);

		protected abstract void initBefore(int root, int left, int right, int middle);

		protected abstract void initLeaf(int root, int index);

		protected abstract void updatePostProcess(int root, int left, int right, int from, int to, long delta,
				int middle);

		protected abstract void updatePreProcess(int root, int left, int right, int from, int to, long delta,
				int middle);

		protected abstract void updateFull(int root, int left, int right, int from, int to, long delta);

		protected abstract long queryPostProcess(int root, int left, int right, int from, int to, int middle,
				long leftResult, long rightResult);

		protected abstract void queryPreProcess(int root, int left, int right, int from, int to, int middle);

		protected abstract long queryFull(int root, int left, int right, int from, int to);

		protected abstract long emptySegmentResult();

		public void init() {
			if (size == 0) {
				return;
			}
			init(0, 0, size - 1);
		}

		private void init(int root, int left, int right) {
			if (left == right) {
				initLeaf(root, left);
			} else {
				int middle = (left + right) >> 1;
				initBefore(root, left, right, middle);
				init(2 * root + 1, left, middle);
				init(2 * root + 2, middle + 1, right);
				initAfter(root, left, right, middle);
			}
		}

		public void update(int from, int to, long delta) {
			update(0, 0, size - 1, from, to, delta);
		}

		protected void update(int root, int left, int right, int from, int to, long delta) {
			if (left > to || right < from) {
				return;
			}
			if (left >= from && right <= to) {
				updateFull(root, left, right, from, to, delta);
				return;
			}
			int middle = (left + right) >> 1;
			updatePreProcess(root, left, right, from, to, delta, middle);
			update(2 * root + 1, left, middle, from, to, delta);
			update(2 * root + 2, middle + 1, right, from, to, delta);
			updatePostProcess(root, left, right, from, to, delta, middle);
		}

		public long query(int from, int to) {
			return query(0, 0, size - 1, from, to);
		}

		protected long query(int root, int left, int right, int from, int to) {
			if (left > to || right < from) {
				return emptySegmentResult();
			}
			if (left >= from && right <= to) {
				return queryFull(root, left, right, from, to);
			}
			int middle = (left + right) >> 1;
			queryPreProcess(root, left, right, from, to, middle);
			long leftResult = query(2 * root + 1, left, middle, from, to);
			long rightResult = query(2 * root + 2, middle + 1, right, from, to);
			return queryPostProcess(root, left, right, from, to, middle, leftResult, rightResult);
		}
	}

	static interface Edge {
	}

	static class BidirectionalGraph extends Graph {
		public int[] transposedEdge;

		public BidirectionalGraph(int vertexCount) {
			this(vertexCount, vertexCount);
		}

		public BidirectionalGraph(int vertexCount, int edgeCapacity) {
			super(vertexCount, 2 * edgeCapacity);
			transposedEdge = new int[2 * edgeCapacity];
		}

		public static BidirectionalGraph createGraph(int vertexCount, int[] from, int[] to) {
			BidirectionalGraph graph = new BidirectionalGraph(vertexCount, from.length);
			for (int i = 0; i < from.length; i++) {
				graph.addSimpleEdge(from[i], to[i]);
			}
			return graph;
		}

		public int addEdge(int fromID, int toID, long weight, long capacity, int reverseEdge) {
			int lastEdgeCount = edgeCount;
			super.addEdge(fromID, toID, weight, capacity, reverseEdge);
			super.addEdge(toID, fromID, weight, capacity, reverseEdge == -1 ? -1 : reverseEdge + 1);
			this.transposedEdge[lastEdgeCount] = lastEdgeCount + 1;
			this.transposedEdge[lastEdgeCount + 1] = lastEdgeCount;
			return lastEdgeCount;
		}

		protected int entriesPerEdge() {
			return 2;
		}

		protected void ensureEdgeCapacity(int size) {
			if (size > edgeCapacity()) {
				super.ensureEdgeCapacity(size);
				transposedEdge = resize(transposedEdge, edgeCapacity());
			}
		}
	}

	static class DFSOrder {
		public final int[] position;
		public final int[] end;

		public DFSOrder(Graph graph) {
			this(graph, 0);
		}

		public DFSOrder(Graph graph, int root) {
			int count = graph.vertexCount();
			position = new int[count];
			end = new int[count];
			int[] edge = new int[count];
			int[] stack = new int[count];
			for (int i = 0; i < count; i++) {
				edge[i] = graph.firstOutbound(i);
			}
			stack[0] = root;
			int size = 1;
			position[root] = 0;
			int index = 0;
			while (size > 0) {
				int current = stack[size - 1];
				if (edge[current] == -1) {
					end[current] = index;
					size--;
				} else {
					int next = graph.destination(edge[current]);
					edge[current] = graph.nextOutbound(edge[current]);
					position[next] = ++index;
					stack[size++] = next;
				}
			}
		}

		public DFSOrder(BidirectionalGraph graph) {
			this(graph, 0);
		}

		public DFSOrder(BidirectionalGraph graph, int root) {
			int count = graph.vertexCount();
			position = new int[count];
			end = new int[count];
			int[] edge = new int[count];
			int[] stack = new int[count];
			int[] last = new int[count];
			for (int i = 0; i < count; i++) {
				edge[i] = graph.firstOutbound(i);
			}
			stack[0] = root;
			last[root] = -1;
			int size = 1;
			position[root] = 0;
			int index = 0;
			while (size > 0) {
				int current = stack[size - 1];
				if (edge[current] == -1) {
					end[current] = index;
					size--;
				} else {
					int next = graph.destination(edge[current]);
					if (next == last[current]) {
						edge[current] = graph.nextOutbound(edge[current]);
						continue;
					}
					edge[current] = graph.nextOutbound(edge[current]);
					position[next] = ++index;
					last[next] = current;
					stack[size++] = next;
				}
			}
		}
	}

	static abstract class LongIntervalTree extends IntervalTree {
		protected long[] value;
		protected long[] delta;

		protected LongIntervalTree(int size) {
			this(size, true);
		}

		public LongIntervalTree(int size, boolean shouldInit) {
			super(size, shouldInit);
		}

		protected void initData(int size, int nodeCount) {
			value = new long[nodeCount];
			delta = new long[nodeCount];
		}

		protected abstract long joinValue(long left, long right);

		protected abstract long joinDelta(long was, long delta);

		protected abstract long accumulate(long value, long delta, int length);

		protected abstract long neutralValue();

		protected abstract long neutralDelta();

		protected long initValue(int index) {
			return neutralValue();
		}

		protected void initAfter(int root, int left, int right, int middle) {
			value[root] = joinValue(value[2 * root + 1], value[2 * root + 2]);
			delta[root] = neutralDelta();
		}

		protected void initBefore(int root, int left, int right, int middle) {
		}

		protected void initLeaf(int root, int index) {
			value[root] = initValue(index);
			delta[root] = neutralDelta();
		}

		protected void updatePostProcess(int root, int left, int right, int from, int to, long delta, int middle) {
			value[root] = joinValue(value[2 * root + 1], value[2 * root + 2]);
		}

		protected void updatePreProcess(int root, int left, int right, int from, int to, long delta, int middle) {
			pushDown(root, left, middle, right);
		}

		protected void pushDown(int root, int left, int middle, int right) {
			value[2 * root + 1] = accumulate(value[2 * root + 1], delta[root], middle - left + 1);
			value[2 * root + 2] = accumulate(value[2 * root + 2], delta[root], right - middle);
			delta[2 * root + 1] = joinDelta(delta[2 * root + 1], delta[root]);
			delta[2 * root + 2] = joinDelta(delta[2 * root + 2], delta[root]);
			delta[root] = neutralDelta();
		}

		protected void updateFull(int root, int left, int right, int from, int to, long delta) {
			value[root] = accumulate(value[root], delta, right - left + 1);
			this.delta[root] = joinDelta(this.delta[root], delta);
		}

		protected long queryPostProcess(int root, int left, int right, int from, int to, int middle, long leftResult,
				long rightResult) {
			return joinValue(leftResult, rightResult);
		}

		protected void queryPreProcess(int root, int left, int right, int from, int to, int middle) {
			pushDown(root, left, middle, right);
		}

		protected long queryFull(int root, int left, int right, int from, int to) {
			return value[root];
		}

		protected long emptySegmentResult() {
			return neutralValue();
		}
	}

	static class Graph {
		public static final int REMOVED_BIT = 0;
		protected int vertexCount;
		protected int edgeCount;
		private int[] firstOutbound;
		private int[] firstInbound;
		private Edge[] edges;
		private int[] nextInbound;
		private int[] nextOutbound;
		private int[] from;
		private int[] to;
		private long[] weight;
		public long[] capacity;
		private int[] reverseEdge;
		private int[] flags;

		public Graph(int vertexCount) {
			this(vertexCount, vertexCount);
		}

		public Graph(int vertexCount, int edgeCapacity) {
			this.vertexCount = vertexCount;
			firstOutbound = new int[vertexCount];
			Arrays.fill(firstOutbound, -1);

			from = new int[edgeCapacity];
			to = new int[edgeCapacity];
			nextOutbound = new int[edgeCapacity];
			flags = new int[edgeCapacity];
		}

		public int addEdge(int fromID, int toID, long weight, long capacity, int reverseEdge) {
			ensureEdgeCapacity(edgeCount + 1);
			if (firstOutbound[fromID] != -1) {
				nextOutbound[edgeCount] = firstOutbound[fromID];
			} else {
				nextOutbound[edgeCount] = -1;
			}
			firstOutbound[fromID] = edgeCount;
			if (firstInbound != null) {
				if (firstInbound[toID] != -1) {
					nextInbound[edgeCount] = firstInbound[toID];
				} else {
					nextInbound[edgeCount] = -1;
				}
				firstInbound[toID] = edgeCount;
			}
			this.from[edgeCount] = fromID;
			this.to[edgeCount] = toID;
			if (capacity != 0) {
				if (this.capacity == null) {
					this.capacity = new long[from.length];
				}
				this.capacity[edgeCount] = capacity;
			}
			if (weight != 0) {
				if (this.weight == null) {
					this.weight = new long[from.length];
				}
				this.weight[edgeCount] = weight;
			}
			if (reverseEdge != -1) {
				if (this.reverseEdge == null) {
					this.reverseEdge = new int[from.length];
					Arrays.fill(this.reverseEdge, 0, edgeCount, -1);
				}
				this.reverseEdge[edgeCount] = reverseEdge;
			}
			if (edges != null) {
				edges[edgeCount] = createEdge(edgeCount);
			}
			return edgeCount++;
		}

		protected final GraphEdge createEdge(int id) {
			return new GraphEdge(id);
		}

		public final int addFlowWeightedEdge(int from, int to, long weight, long capacity) {
			if (capacity == 0) {
				return addEdge(from, to, weight, 0, -1);
			} else {
				int lastEdgeCount = edgeCount;
				addEdge(to, from, -weight, 0, lastEdgeCount + entriesPerEdge());
				return addEdge(from, to, weight, capacity, lastEdgeCount);
			}
		}

		protected int entriesPerEdge() {
			return 1;
		}

		public final int addWeightedEdge(int from, int to, long weight) {
			return addFlowWeightedEdge(from, to, weight, 0);
		}

		public final int addSimpleEdge(int from, int to) {
			return addWeightedEdge(from, to, 0);
		}

		public final int vertexCount() {
			return vertexCount;
		}

		protected final int edgeCapacity() {
			return from.length;
		}

		public final int firstOutbound(int vertex) {
			int id = firstOutbound[vertex];
			while (id != -1 && isRemoved(id)) {
				id = nextOutbound[id];
			}
			return id;
		}

		public final int nextOutbound(int id) {
			id = nextOutbound[id];
			while (id != -1 && isRemoved(id)) {
				id = nextOutbound[id];
			}
			return id;
		}

		public final int destination(int id) {
			return to[id];
		}

		public final boolean flag(int id, int bit) {
			return (flags[id] >> bit & 1) != 0;
		}

		public final boolean isRemoved(int id) {
			return flag(id, REMOVED_BIT);
		}

		protected void ensureEdgeCapacity(int size) {
			if (from.length < size) {
				int newSize = Math.max(size, 2 * from.length);
				if (edges != null) {
					edges = resize(edges, newSize);
				}
				from = resize(from, newSize);
				to = resize(to, newSize);
				nextOutbound = resize(nextOutbound, newSize);
				if (nextInbound != null) {
					nextInbound = resize(nextInbound, newSize);
				}
				if (weight != null) {
					weight = resize(weight, newSize);
				}
				if (capacity != null) {
					capacity = resize(capacity, newSize);
				}
				if (reverseEdge != null) {
					reverseEdge = resize(reverseEdge, newSize);
				}
				flags = resize(flags, newSize);
			}
		}

		protected final int[] resize(int[] array, int size) {
			int[] newArray = new int[size];
			System.arraycopy(array, 0, newArray, 0, array.length);
			return newArray;
		}

		private long[] resize(long[] array, int size) {
			long[] newArray = new long[size];
			System.arraycopy(array, 0, newArray, 0, array.length);
			return newArray;
		}

		private Edge[] resize(Edge[] array, int size) {
			Edge[] newArray = new Edge[size];
			System.arraycopy(array, 0, newArray, 0, array.length);
			return newArray;
		}

		protected class GraphEdge implements Edge {
			protected int id;

			protected GraphEdge(int id) {
				this.id = id;
			}
		}
	}
}