#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<iomanip>
#include<tuple>
#include<cstring>
#include<bitset>

#define REP(i,n) for(ll i=0;i<(ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((ll)(x).size())
#define pb push_back
#define eb emplace_back

#define INF 1000000000
#define INFLL 1000000000000000000LL

using namespace std;

template<class T>bool chmax(T& a, const T& b) { if (a < b) { a = b; return 1; } return 0; }
template<class T>bool chmin(T& a, const T& b) { if (b < a) { a = b; return 1; } return 0; }

using ll = long long;
using PP = pair<ll, ll>;

#define MAX_N 200000+100

class UnionFind {

	vector<int> par;//親

	vector<int> ran;//rank

public:

	UnionFind(int n) {
		par.resize(n);
		ran.resize(n);
		for (int i = 0; i < n; i++) {
			par[i] = i;
			ran[i] = 0;
		}
	}

	void init(int n) {
		par.resize(n);
		ran.resize(n);
		for (int i = 0; i < n; i++) {
			par[i] = i;
			ran[i] = 0;
		}
	}

	//根を求める
	int find(int x) {
		if (par[x] == x) {
			return x;
		}
		else
			return par[x] = find(par[x]);//辺の縮約を行っている
	}

	//併合
	void unite(int x, int y) {
		x = find(x);
		y = find(y);
		if (x == y)return;

		if (ran[x] < ran[y]) {
			par[x] = y;
		}
		else {
			par[y] = x;
			if (ran[x] == ran[y])ran[x]++;
		}
	}

	//同じ木に属するか
	bool same(int x, int y) {
		return find(x) == find(y);
	}
};

int N, Q, C[MAX_N], bel[MAX_N];
map<ll, ll> mp[MAX_N];
vector<ll> aff[MAX_N];

int main() {

	cin >> N >> Q;
	REP(i, N) {
		cin >> C[i];
		C[i]--;
	}

	REP(i, N) {
		bel[i] = i;
		mp[bel[i]][C[i]] = 1;
		aff[i].push_back(i);
	}

	UnionFind uf(N);

	while (Q--) {
		int a, x, y;
		cin >> a >> x >> y;
		x--, y--;
		if (a == 1) {
			if (!uf.same(x, y)) {
				uf.unite(x, y);

				if (SZ(aff[bel[x]]) > SZ(aff[bel[y]]))
					swap(x, y);

				ll belx = bel[x];

				for (auto&& v : aff[belx]) {
					aff[bel[y]].push_back(v);
					bel[v] = bel[y];
				}

				aff[belx].clear();

				for (auto&& m : mp[belx]) {
					mp[bel[y]][m.first] += m.second;
				}

				mp[belx].clear();
			}
		}
		if (a == 2) {
			cout << mp[bel[x]][y] << endl;
		}
	}

	return 0;
}
#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <deque>
#include <ios>
#include <iostream>
#include <limits>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <utility>

using namespace std;

using i64 = int64_t;
using u64 = uint64_t;
using i32 = int32_t;
using pi64 = pair<i64, i64>;

constexpr i64 MAXN = 3*100*1000LL+5LL;
constexpr i64 MOD = 1000000007LL;
constexpr i64 INF64 = MOD * MOD;
#define vec vector
#define let const
#define DRi64(x) i64 x; cin >> x;
#define DRS(x) string x; cin >> x;
#define DRVi64(v, n) vec<i64> v(n); { for (i64 i = 0; i < n; ++i) { cin >> v[i]; }}

#ifdef DEBUG
#define P(x) cerr << x << "\n"
#else
#define P(x)
#endif

i64
find_(const i64 a, vec<i64>& parents)
{
    if (parents[a] != a)
    {
        parents[a] = find_(parents[a], parents);
    }
    return parents[a];
}

void
union_(const i64 a, const i64 b, vec<i64>& parents, vec<i64>& sizes,
        vec<map<i64, i64>>& class_hists)
{
    let i64 r_a = find_(a, parents);
    let i64 r_b = find_(b, parents);
    if (r_a == r_b)
    {
        return;
    }
    if (sizes[r_a] < sizes[r_b])
    {
        union_(r_b, r_a, parents, sizes, class_hists);
        return;
    }
    sizes[r_a] += sizes[r_b];
    parents[r_b] = r_a;
    // merge map of smaller vertex into that of larger vertex
    for (let auto& [cls, cnt] : class_hists[r_b])
    {
        class_hists[r_a][cls] += cnt;
    }
}

int
main()
{
    ios_base::sync_with_stdio(false);

    DRi64(N); DRi64(Q);
    DRVi64(C, N);

    // Ci \in [1, N]
    vec<i64> class_hist(N + 1, 0);
    for (let i64 c : C)
    {
        ++class_hist[c];
    }

    vec<i64> parents(N + 1);
    iota(parents.begin(), parents.end(), 0LL);
    vec<i64> sizes(N + 1, 1);
    vec<map<i64, i64>> class_hists(N + 1);
    for (i64 i = 1; i <= N; ++i)
    {
        class_hists[i][C[i - 1]] = 1;
    }

    for (; Q-->0;)
    {
        DRi64(typ);
        if (typ == 1)
        {
            DRi64(A); DRi64(B);
            union_(A, B, parents, sizes, class_hists);
        }
        else if (typ == 2)
        {
            DRi64(X); DRi64(Y);
            if (let i64 r_X = find_(X, parents); class_hists[r_X].count(Y))
            {
                cout << class_hists[r_X][Y] << "\n";
            }
            else
            {
                cout << "0\n";
            }
        }
        else
        {
            abort();
        }
    }

    return 0;
}
