#include <iostream>
#include <numeric>
#include <unordered_map>
#include <vector>
using namespace std;
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define REP(i, n) FOR(i,0,n)

// UnionFind Tree (0-indexed), based on size of each disjoint set
struct UnionFind
{
    std::vector<int> par;
    vector<unordered_map<int, int>> cou;
    UnionFind(int N = 0) : par(N), cou(N) {
        iota(par.begin(), par.end(), 0);
    }
    int find(int x) { return (par[x] == x) ? x : (par[x] = find(par[x])); }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) return false;
        if (cou[x].size() < cou[y].size()) std::swap(x, y);
        par[y] = x;
        for (auto [a, b] : cou[y]) {
            cou[x][a] += b;
        }
        cou[y].clear();
        return true;
    }
};

int main()
{
    cin.tie(nullptr), ios::sync_with_stdio(false);
    int N, Q;
    cin >> N >> Q;
    UnionFind uf(N);
    vector<int> C(N);
    for (auto &x : C) {
        cin >> x;
        x--;
    }

    REP(i, N) {
        uf.cou[i][C[i]] = 1;
    }

    while (Q--) {
        int q, x, y;
        cin >> q >> x >> y;
        x--, y--;
        if (q == 1) {
            uf.unite(x, y);
        } else {
            cout << uf.cou[uf.find(x)][y] << '\n';
        }
    }
}

#include <bits/stdc++.h> 
#include<cmath>
#include<string>
#define pb push_back
#define x first
#define y second
#define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define all(x) x.begin(),x.end()
#define int long long
#define ll long long
using namespace std;
 
void __print(int x) {cerr << x;}
void __print(long x) {cerr << x;}
//void __print(long long x) {cerr << x;}
void __print(unsigned x) {cerr << x;}
void __print(unsigned long x) {cerr << x;}
void __print(unsigned long long x) {cerr << x;}
void __print(float x) {cerr << x;}
void __print(double x) {cerr << x;}
void __print(long double x) {cerr << x;}
void __print(char x) {cerr << '\'' << x << '\'';}
void __print(const char *x) {cerr << '\"' << x << '\"';}
void __print(const string &x) {cerr << '\"' << x << '\"';}
void __print(bool x) {cerr << (x ? "true" : "false");}
 
template<typename T, typename V>
void __print(const pair<T, V> &x) {cerr << '{'; __print(x.first); cerr << ','; __print(x.second); cerr << '}';}
template<typename T>
void __print(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), __print(i); cerr << "}";}
void _print() {cerr << "]\n";}
template <typename T, typename... V>
void _print(T t, V... v) {__print(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifndef ONLINE_JUDGE
#define debug(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define debug(x...)
#endif
 
// look for  all edge cases
//search for a pattern


const int nax = 2e5 + 2;
int n,q;
vector<int> section;
vector<int> parent;
map<int,int> m[nax];
vector<int> si;
void solve();

void merge(int a , int b){
	while(parent[a] != a){
		a = parent[a];
	}
	while(parent[b] != b){
		b = parent[b];
	}
	
	if(a == b){
		return;
	}
	
	if(si[b] > si[a]){
		swap(a , b);
	}
	
	si[a] += si[b];
	parent[b] = a;
	for(auto it : m[b]){
		m[a][it.x] += m[b][it.x];
	}
	m[b].clear();
	
	
}

signed main(){
	
	cin >> n >> q;
	section.resize(n+1);
	si.assign(n+1,0);
	parent.assign(n+1, 0);
	for(int i =1 ;i <= n;i++){
		cin >> section[i];
	}
	
	for(int i=1 ; i <=n ;i++){
		si[i] = 1;
		parent[i] = i;
		m[i][section[i]] += 1;
	}
	
	while(q--){
		solve();
	}
	
	return 0;
	
}

void solve(){
	int type;
	cin >> type;
	int a,b;
	cin >>a >> b;
	
	if(type == 1){
		merge(a , b);
	}
	else{
		while(a != parent[a]){
			a = parent[a];
		}
		cout << m[a][b] << "\n";

	}
	
	
}
