#include <bits/stdc++.h>          
using namespace std;
using ll = long long;
using ld = long double;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define repp(i, st, en) for (ll i = (ll)st; i < (ll)(en); i++)
#define repm(i, st, en) for (ll i = (ll)st; i >= (ll)(en); i--)
#define all(v) v.begin(), v.end()
template<class in_chmax> void chmax(in_chmax &x, in_chmax y) {x = max(x,y);}
template<class in_chmin> void chmin(in_chmin &x, in_chmin y) {x = min(x,y);}
void Yes() {cout << "Yes" << endl; exit(0);}
void No() {cout << "No" << endl; exit(0);}
template<class in_Cout> void Cout(in_Cout x) {cout << x << endl; exit(0);}
template<class in_vec_cout>
void vec_cout(vector<in_vec_cout> vec) {
    for (in_vec_cout res : vec) {cout << res << " ";}
    cout << endl;
}

const ll inf = 1e18;
const ll mod = 1e9 + 7;

struct UnionFind {
    vector<ll> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2
    vector<ll> siz;
    vector<map<ll,ll>> cnt;

    UnionFind(ll N) : par(N), siz(N,1LL), cnt(N) { //最初は全てが根であるとして初期化
        for(ll i = 0; i < N; i++) par[i] = i;
    }

    ll root(ll x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }

    ll size(ll x) { // xが属する木のサイズを返す
        ll rx = root(x);
        return siz[rx];
    }

    void unite(ll x, ll y) { // xとyの木を併合
        ll rx = root(x); //xの根をrx
        ll ry = root(y); //yの根をry
        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま
        if (siz[rx]<siz[ry]) swap(rx,ry); // sizeが大きいほうをx,小さいほうをyとする。
        siz[rx] += siz[ry]; // サイズの変更
        //xとyの根が同じでない(=同じ木にない)時：yの根ryをxの根rxにつける(siz[rx]>=siz[ry])
        par[ry] = rx;
        for (auto p : cnt[ry]) cnt[rx][p.first] += p.second;
        cnt[ry].clear();
    }

    bool same(ll x, ll y) { // 2つのデータx, yが属する木が同じならtrueを返す
        ll rx = root(x);
        ll ry = root(y);
        return rx == ry;
    }
};

int main() {
    ll N, Q; cin >> N >> Q;
    UnionFind uf(N);
    rep(i,N) {
        ll c; cin >> c; c--;
        uf.cnt[i][c]++;
    }
    vector<ll> res;
    rep(q,Q) {
        ll t; cin >> t;
        ll x, y; cin >> x >> y;
        x--; y--;
        if (t==1) {
            uf.unite(x,y);
        } else {
            x = uf.root(x);
            res.push_back(uf.cnt[x][y]);
        }
    }
    for (ll ans : res) cout << ans << endl;
}
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <stack>
#include <queue>
#include <array>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <random>
#include <chrono>
#include <utility>
#include <numeric>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <sstream>
#include <assert.h>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <unordered_map>

using namespace std;
struct _ { ios_base::Init i; _() { ios_base::sync_with_stdio(false), cin.tie(NULL), cout.tie(NULL); } } _;

vector<map<int, int>> freq;
vector<array<int, 3>> query;
vector<int> c, sze, req, parent;

int find_parent(int v) {
	if (v == parent[v]) return v;
	return parent[v] = find_parent(parent[v]);
}

void union_set(int a, int b) {
	a = find_parent(a);
	b = find_parent(b);

	if (a != b) {
		if (sze[a] < sze[b]) swap(a, b);

		parent[b] = a;
		sze[a] += sze[b];
		for (auto [num, cnt] : freq[b])
			if (req[num]) freq[a][num] += cnt;
	}
	return;
}

int main() {

	int n, q; cin >> n >> q;
	c.resize(n + 1, 0); for (int i = 1; i <= n; i++) cin >> c[i];

	freq.resize(n + 1);
	sze.resize(n + 1, 1);
	parent.resize(n + 1, 0);
	req.resize(n + 1, 0);

	query.resize(q);

	for (int i = 0; i <= q - 1; i++) {
		cin >> query[i][0] >> query[i][1] >> query[i][2];
		if (query[i][0] == 2) {
			req[query[i][2]]++;
		}
	}

	for (int i = 1; i <= n; i++) {
		parent[i] = i;
		if (req[c[i]]) freq[i][c[i]]++;
	}

	for (int i = 0; i <= q - 1; i++) {
		if (query[i][0] == 1) union_set(query[i][1], query[i][2]);
		else {
			cout << freq[find_parent(query[i][1])][query[i][2]] << "\n";
			req[query[i][2]]--;
		}
	}

	return 0;
}