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

#pragma region Macros
using ll = long long;
#define int ll
using pii = pair<int, int>;
using tiii = tuple<int, int, int>;
template<class T = int> using V = vector<T>;
template<class T = int> using VV = V<V<T>>;
#define IOS\
    ios::sync_with_stdio(false);\
    cin.tie(0);\
    cout.tie(0);
#define FOR(i,l,r) for(int i=(l);i<int(r);++i)
#define REP(i,n) FOR(i,0,n)
#define REPS(i,n) FOR(i,1,n+1)
#define RFOR(i,l,r) for(int i=(l);i>=int(r);--i)
#define RREP(i,n) RFOR(i,n-1,0)
#define RREPS(i,n) RFOR(i,n,1)
#define mp make_pair
#define mt make_tuple
#define pb push_back
#define eb emplace_back
#define all(x) (x).begin(),(x).end()
#define SORT(name) sort(name.begin(), name.end())
#define RSORT(name)\
    SORT(name);\
    reverse(all(name));
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
inline void Yes(bool b = true) {cout << (b ? "Yes" : "No") << '\n';}
inline void YES(bool b = true) {cout << (b ? "YES" : "NO") << '\n';}
template <class T> inline void print(T x){ cout << x << '\n';}
template<typename T1,typename T2> inline void chmin(T1 &a, T2 b){ if(a > b) a = b; }
template<typename T1,typename T2> inline void chmax(T1 &a, T2 b){ if(a < b) a = b; }

const ll LLINF = (1LL<<60);
const int INF = (1LL<<30);
const double DINF = std::numeric_limits<double>::infinity();
#pragma endregion

const int MOD = 1000000007;

const int MAX_N = 100010;
int N, Q;
V<> C;

class UnionFind2 {
    public:
        UnionFind2(){}
        // size: Union-Find で扱いたい要素の数
        void Init(int size, const V<>& c) {
            m_Data.resize(size);
            REP(i, size) {
                m_Data[i][c[i]] = 1;
                m_Data[i][-1] = i;
            }
        }

        // x と y の属する集合を併合
        // 既に一緒な集合内にいるなら false を
        // 新たに併合されたなら true を返す
        bool Unite(int x, int y) {
            x = Find(x), y = Find(y);
            if(x == y) { return (false); }
            if(x > y) { swap(x, y); }
            for(auto& p : m_Data[y]) {
                int key = p.first;
                int val = p.second;
                if(key == -1) { continue; }
                m_Data[x][key] += val;
                m_Data[y][key] = 0;
            }
            m_Data[y][-1] = x;
            return (true);
        }

        // 要素 k が属する集合の根を返す
        int Find(int k) {
            //DBG("  Find k: %lld\n", k);
            if(m_Data[k][-1] == k) {
                // k が根
                return (k);
            }
            // k の上をたどる
            int n_k = Find(m_Data[k][-1]);
            m_Data[k][-1] = n_k;
            return n_k;
        }

        // 要素 k と同じ集合のクラス l の要素数を返す
        int Size(int k, int l) {
            int root = Find(k);
            return m_Data[root][l];
        }

    private:
        // 根: 各クラスの要素数
        //     key: クラス val: クラスの数
        // 子: 根の id (key: -1 に入れる)
        vector< map<int, int> > m_Data;
};

signed main() {
    IOS;

    cin >> N >> Q;
    C.resize(N);
    REP(i, N) {
        cin >> C[i];
        C[i]--;
    }
    UnionFind2 uf;
    uf.Init(N, C);
    REP(i, Q) {
        int q, x, y;
        cin >> q >> x >> y;
        x--; y--;
        if(q == 1) {
            uf.Unite(x, y);
        }
        else {
            print(uf.Size(x, y));
        }
    }

    return 0;
}

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

const int N = 2e5 + 5;
int fa[N], n, q, siz[N];
map<int, int> m[N];
int find(int x) {
	if (x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}

void merge(int x, int y) {
	x = find(x);
	y = find(y);
	if (x == y) return;
	if (m[x].size() > m[y].size()) swap(x, y);
	fa[x] = y;
//	siz[y] += siz[x];
	for (auto p : m[x]) {
		m[y][p.first] += p.second;
	}
}

int main() {
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++) {
    	int x;
    	scanf("%d", &x);
    	m[i][x] = 1;
    	fa[i] = i;
    }
    for (int i = 1; i <= q; i++) {
    	int op, x, y;
    	scanf("%d %d %d", &op, &x, &y);
    	if (op == 1) {
    		merge(x, y);
    	} else {
    		x = find(x);
    		if (m[x].count(y) == 0) {
    			puts("0");
    			continue;
    		}
    		printf("%d\n", m[x][y]);
    	}
    }
    return 0;
}