#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <iomanip>
#define ll long long
#define MAX_N 200001

using namespace std;

long long MOD = 1000000007;

struct unionSet{
    int parent[MAX_N];
    int size[MAX_N];
    vector<map<int,int>> dict=vector<map<int,int>>(MAX_N,map<int,int>());

    unionSet(int num){
        for(int i=0; i<num; i++){
            size[i]=1;
            parent[i]=i;
        }
    }

    //find root of x
    int find(int x){
        if (x==parent[x]){
            return x;
        }else{
            return parent[x]=find(parent[x]);
        }
    }

    //check if roots of x and y are same
    bool same(int x, int y){
        return (find(x)==find(y));
    }

    // unite sets of x and y
    void unite(int x, int y){
        x=find(x);
        y=find(y);
        if(x==y)return;

        if(size[x]>size[y]){
            for(auto kv:dict[y]){
                int k=kv.first;
                int v=kv.second;

                if(dict[x].count(k)>0){
                    dict[x][k]+=v;
                }else{
                    dict[x][k]=v;
                }
            }

            parent[y]=x;
            size[x]+=size[y];
        }else{
            for(auto kv:dict[x]){
                int k=kv.first;
                int v=kv.second;

                if(dict[y].count(k)>0){
                    dict[y][k]+=v;
                }else{
                    dict[y][k]=v;
                }
            }

            parent[x]=y;
            size[y]+=size[x];
        }
    }
};

template<typename T>
void cout_vec(vector<T> &vec){
    for(int i=0; i<vec.size(); i++){
        if (i!=0){
            cout << ' ';
        }
        cout << vec[i];
    }
    cout << endl;
}

int main(){
    int N, Q;
    cin >> N >> Q;

    unionSet us(N);

    vector<int> cls(N,0);
    for(int i=0; i<N; i++){
        cin >> cls[i];
        cls[i]--;
        us.dict[i][cls[i]]=1;
    }

    for(int i=0; i<Q; i++){
        int c,x,y;
        cin >> c >> x >> y;
        x--;y--;

        x=us.find(x);
        if(c==1){
            us.unite(x,y);
        }else if(us.dict[x].find(y)!=us.dict[x].end()){
            cout << us.dict[x][y] << '\n';
        }else{
            cout << "0\n";
        }
    }
    
    /*
    for(int i=0; i<N; i++){
        cout << us.size[i] << ' ';
    }
    */
}
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define forin(in ,n) for(ll i=0; i<n; i++) cin>>in[i]
#define forout(out) for(ll i=0; i<(ll)out.size(); i++) cout<<out[i]<<endl
#define rep(i, n) for (ll i = 0; i < n; ++i)
#define rep_up(i, a, n) for (ll i = a; i < n; ++i)
#define rep_down(i, a, n) for (ll i = a; i >= n; --i)
#define P pair<ll, ll>

#define all(v) v.begin(), v.end()
#define fi first
#define se second
#define vvvll vector<vector<vector<ll>>>
#define vvll vector<vector<ll>>
#define vll vector<ll>
#define pqll priority_queue<ll>
#define pqllg priority_queue<ll, vector<ll>, greater<ll>>

constexpr ll INF = (1ll << 60);
constexpr ll mod = 1000000007;
//constexpr ll mod = 998244353;
constexpr double pi = 3.14159265358979323846;
template <typename T>
inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T>
inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T>
void pt(T val) {
    cout << val << "\n";
}
template <typename T>
void pt_vll(vector<T> &v) {
    ll vs = v.size();
    rep(i, vs) {
        cout << v[i];

        if (i == vs - 1)
            cout << "\n";
        else
            cout << " ";
    }
}
ll mypow(ll a, ll n) {
    ll ret = 1;
    if(n==0) return 1;
    if(a==0) return 0;
    rep(i, n) {
        if (ret > (ll)(9e18 + 10) / a) return -1;
        ret *= a;
    }
    return ret;
}
long long modpow(long long a, long long n, long long mod) {
    long long res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return res;
}
long long modinv(long long a, long long m) {
    long long b = m, u = 1, v = 0;
    while (b) {
        long long t = a / b;
        a -= t * b; swap(a, b);
        u -= t * v; swap(u, v);
    }
    u %= m;
    if (u < 0) u += m;
    return u;
}

ll digsum(ll n) {
    ll res = 0;
    while(n > 0) {
        res += n%10;
        n /= 10;
    }
    return res;
}

struct UnionFind {
    vector<ll> par,size;

    UnionFind(ll N) : par(N) { //最初はすべてが根であるとして初期化
        size.resize(N,1);
        for(ll i=0;i<N;i++) par[i] = i;
    }

    ll root(ll x){ //データxの木の根を再帰で得る
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }

    void unite(ll x, ll y){ //xとyの木を併合
        ll rx = root(x);
        ll ry = root(y);
        if(rx == ry) return; //同じ木にあるときはそのまま
        if(size[rx]>size[ry]){
            par[ry]=rx;
            size[rx] += size[ry];
        }else{
            par[rx] = ry;
            size[ry] += size[rx];
        }
        return;
    }

    bool same(ll x, ll y){ //2つのデータx,yが属する木が同じならtrue
        ll rx = root(x);
        ll ry = root(y);
        return rx == ry;
    }

    ll treesize(ll x){return size[root(x)];}
};

int main() {
    ll n,m,t,x,y,q,k,ans=0;
    cin>>n>>q;
    vll c(n);
    vector<map<ll, ll>> mp(n);
    UnionFind uf(n);
    rep(i,n){
        cin>>c[i];
        c[i]--;
        mp[i][c[i]] = 1;
    }
    rep(i,q){
        cin>>t>>x>>y;
        if(t==1){
            x--;y--;
            if(uf.same(x,y)) continue;
            x=uf.root(x);
            y=uf.root(y);
            uf.unite(x,y);
            if(y==uf.root(x)) swap(x,y);
            for(auto e:mp[y]){
                mp[x][e.first] += e.second;
            }
        }else{
            pt(mp[uf.root(x-1)][y-1]);
        }
    }
}