#include <bits/stdc++.h>
using namespace std;
int n, q, c, par[200005];
int uf(int x){
	if (par[x]==x) return x;
	int y = uf(par[x]);
	par[x]=y; return y;
}
map<int, int> raz[200005];
void merge(int x, int y){
	if (raz[x].size() < raz[y].size()) swap(x, y);
	for (map<int, int>::iterator it = raz[y].begin(); it!=raz[y].end(); it++)
		if (raz[x].count(it->first)) raz[x][it->first]+=(it->second);
		else raz[x][it->first]=(it->second);
	par[y] = x; raz[y].clear();
}
int main (){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> q;
for (int i=1; i<=n; i++){
	par[i]=i; cin >> c;
	raz[i][c]=1;
}
while (q--){
	int tip; cin >> tip;
	if (tip==1){
		int a, b; cin >> a >> b;
		if (uf(a)!=uf(b)){
			merge(par[a], par[b]);
		}
	}
	else{
		int x, y; cin >> x >> y;
		int leader = uf(x);
		if (raz[leader].count(y)) cout << raz[leader][y] << endl;
		else cout << 0 << endl;
	}
}
return 0;
}

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





struct UnionFind {
    vector<int> par;

    UnionFind() { }
    UnionFind(int n) : par(n, -1) { }
    void init(int n) { par.assign(n, -1); }
    
    int root(int x) {
        if (par[x] < 0) return x;
        else return par[x] = root(par[x]);
    }
    
    bool issame(int x, int y) {
        return root(x) == root(y);
    }
    
    bool merge(int x, int y, int id = -1) {
        x = root(x); y = root(y);
        if (x == y) return false;
        if (par[x] > par[y]) swap(x, y); // merge technique
        par[x] += par[y];
        par[y] = x;
        return true;
    }
    
    int size(int x) {
        return -par[root(x)];
    }
};

using Query = pair<int,int>; // id, class
int main() {
    int N, Q;
    cin >> N >> Q;
    vector<int> C(N);
    for (int i = 0; i < N; ++i) cin >> C[i], --C[i];

    // build forest
    UnionFind uf(N);
    vector<int> left(N+Q, -1), right(N+Q, -1), ancestor(N+Q);
    iota(ancestor.begin(), ancestor.end(), 0);
    vector<vector<Query>> queries(N+Q);
    int mergeNum = N, queryNum = 0;
    for (int q = 0; q < Q; ++q) {
        int type, x, y;
        cin >> type >> x >> y;
        --x, --y;
        if (type == 1) {
            x = uf.root(x), y = uf.root(y);
            if (x == y) continue;
            uf.merge(x, y);

            // x が親となるように
            if (uf.root(x) != x) swap(x, y);

            left[mergeNum] = ancestor[x];
            right[mergeNum] = ancestor[y];
            ancestor[x] = mergeNum++;
        } else {
            x = uf.root(x);
            int v = ancestor[x];
            queries[v].emplace_back(queryNum++, y);
        }
    }
    
    // dfs
    vector<int> res(queryNum, 0), counter(N, 0);
    auto dfs = [&](auto self, int v) -> void {
        for (auto q : queries[v]) res[q.first] -= counter[q.second];
        if (v < N) counter[C[v]]++; // leaf
        else self(self, left[v]), self(self, right[v]);
        for (auto q : queries[v]) res[q.first] += counter[q.second];
    };
    for (int v = 0; v < N; ++v) {
        if (uf.root(v) != v) continue;
        dfs(dfs, ancestor[v]);
    }
    for (auto val : res) cout << val << endl;
}