#include <set>
#include <iostream>

using namespace std;

#define int long long

const int N = 2e5 + 10;

int par[N], c[N];
set<pair<int, int>> s[N];

void make_set(int x) {
  par[x] = x;
  s[x].insert({c[x], 1});
}

int find_set(int x) {
  if (x == par[x]) {
    return x;
  }
  return par[x] = find_set(par[x]);
}

void union_sets(int x, int y) {
  x = find_set(x);
  y = find_set(y);
  if (x != y) {
    if (s[x].size() > s[y].size()) {
      swap(x, y);
    }
    par[x] = y;
    for (auto z : s[x]) {
      auto it = s[y].lower_bound({z.first, -1});
      int cur = z.second;
      if (!(it == s[y].end() || it->first != z.first)) {
        cur += it->second;
        s[y].erase(it);
      }
      s[y].insert({z.first, cur});
    }
  }
}

int32_t main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);    
  int n, q;
  cin >> n >> q;
  for (int i = 1; i <= n; ++i) {
    cin >> c[i];
    make_set(i);
  }
  while (q--) {
    int x, y, z;
    cin >> x >> y >> z;
    if (x - 1) {
      int w = find_set(y);
      auto it = s[w].lower_bound({z, -1});
      if (it == s[w].end() || it->first != z) {
        cout << "0\n";
      } else {
        cout << it->second << '\n';
      }
    } else {
      union_sets(y, z);
    }
  }
  return 0;
}
// C++(GCC 9.2.1)
#include <bits/stdc++.h>
using namespace std;
#define repex(i, a, b, c) for(int i = a; i < b; i += c)
#define repx(i, a, b) repex(i, a, b, 1)
#define rep(i, n) repx(i, 0, n)
#define repr(i, a, b) for(int i = a; i >= b; i--)
#define a first
#define b second
int c[202020];

// https://github.com/atcoder/live_library/blob/master/uf.cpp
// UnionFind
// coding: https://youtu.be/TdR816rqc3s?t=726
// comment: https://youtu.be/TdR816rqc3s?t=6822
// -> 一部改変.
struct UnionFind{
    vector<int> d;           // 生徒同士が, 同じ集団に属するか否かを管理.
    vector<map<int, int>> c; // 生徒を含む集団が属するクラス情報を管理.
    set<int> s;              // 合流済みの生徒であるか判定.
    UnionFind(int n = 0): d(n, -1), c(n) {}
    int find(int x){
        return (d[x] < 0) ? x : (d[x] = find(d[x]));
    }
    bool unite(int ox, int oy, int cx, int cy){
        int x = find(ox);
        int y = find(oy);
        if(x == y) return false;
        if(d[x] > d[y]) swap(x, y);
        d[x] += d[y];
        d[y] = x;
        
        // 生徒を含む集団が属するクラス情報を更新.
        if(s.count(ox) == 0) c[x][cx]++;
        if(s.count(oy) == 0) c[x][cy]++;
        for(auto &p : c[y]) c[x][p.a] += p.b;
        
        // 合流済みフラグ設定.
        s.insert(ox);
        s.insert(oy);
        return true;
    }
    bool same(int x, int y){
        return find(x) == find(y);
    }
    int size(int x){
        return -d[find(x)];
    }
    int getClassSize(int ox, int cy){
        int x = find(ox);
        return c[x][cy];
    }
    void debug(){
        puts("--- d[i] start ---");
        rep(i, d.size()) printf("%d ", d[i]);
        puts("\n--- d[i]  end ----");
        puts("--- find start ---");
        rep(i, d.size()) printf("%d ", find(i));
        puts("\n--- find  end ----");
        return;
    }
    void debugClass(int ox){
        int x = find(ox);
        puts("--- debugClass start ---");
        for(auto &p : c[x]) printf("class=%d size=%d\n", p.a, p.b);
        puts("\n--- debugClass  end ----");
        return;
    }
};

int main(){
    
    // 1. 入力情報.
    int N, Q;
    scanf("%d %d", &N, &Q);
    rep(i, N) scanf("%d", &c[i]);
    
    // 2. クエリ回答.
    UnionFind uf(N);
    rep(i, Q){
        // 2-1. クエリ種別.
        int f;
        scanf("%d", &f);
        
        // 2-2. f = 1 の 場合.
        if(f == 1){
            int a, b;
            scanf("%d %d", &a, &b);
            a--, b--;
            uf.unite(a, b, c[a], c[b]);
        }
        
        // 2-3. f = 2 の 場合.
        if(f == 2){
            int x, y;
            scanf("%d %d", &x, &y);
            x--;
            int ans = uf.getClassSize(x, y);
            if(uf.size(x) == 1 && c[x] == y) ans++; // 生徒 x のみの場合を考慮.
            printf("%d\n", ans);
        }
    }
    return 0;
    
}