#include <bits/stdc++.h>
using namespace std;
#include <math.h>
#include <iomanip>
#include <cstdint>
#include <string>
#include <sstream>
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }
#define rep(i,n) for (int i = 0; i < (n); ++i)
typedef long long ll;
using P = pair<int,int>;
const int INF=1001001001;
const int mod =1e9+7;

struct Unionfind{
   vector<int>d;
   vector<map<int,int>>mp;
   Unionfind(int n):d(n,-1),mp(n) {}
   int root(int x){
     if(d[x]<0){return x;}
     return d[x]=root(d[x]);
   }
   bool unite(int x,int y){
     x=root(x);y=root(y);
     if(x==y){return false;}
     if(d[x]>d[y]){swap(x,y);}
     for(auto p:mp[y]){
       mp[x][p.first]+=p.second;
     }
     d[x]+=d[y];
     d[y]=x;
     return true;
   }
   bool same(int x,int y){return root(x)==root(y);}
   int size(int x){return -d[root(x)];}
};
int main() {
  int N,Q;cin>>N>>Q;
  Unionfind uf(N);
  rep(i,N){
    int c;cin>>c;
    uf.mp[i][c]=1;//集団iの中でクラスiにいる人数
  }
  rep(i,Q){
    int q;cin>>q;
    if(q==1){
      int a,b;cin>>a>>b;a--;b--;
      uf.unite(a,b);
    }
    else{
      int x,y;cin>>x>>y;x--;
      int px=uf.root(x);
      cout<<uf.mp[px][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;
}
