#include <bits/stdc++.h>
using namespace std;
constexpr int N = 2e5 + 100;

int n, q;
int fa[N], clas[N];
map<int, map<int, int>> son_num;

int Find(int x) {
    // return fa[x] == x ? fa[x] : fa[x] = Find(fa[x]);
    if (fa[x] == x) return fa[x];
    else return fa[x] = Find(fa[x]);
}

void Union(int x, int y) {
    x = Find(x);
    y = Find(y);
    if (x != y) {
        if (son_num[x].size() >= son_num[y].size()) {
            fa[y] = x;
            for (const auto &[_class, num] : son_num[y]) {
                son_num[x][_class] += num;
            }
        } else {
            fa[x] = y;
            for (const auto &[_class, num] : son_num[x]) {
                son_num[y][_class] += num;
            }
        }
    }
}

void Init() {
    for (int i = 0; i < N; i++) {
        fa[i] = i;
    }
}

int main() {
    // freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    Init();
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> clas[i];
        son_num[i][clas[i]] = 1;
    }
    for (int i = 0; i < q; i++) {
        int op, x, y;
        cin >> op >> x >> y;
        if (op == 1) {
            Union(x, y);
        } else {
            cout << son_num[Find(x)][y] << "\n";
        }
    }
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

// #define int long long
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (!isdigit(c)) { if (c == '-') f = - f; c = getchar(); }
    while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    return x * f;
}

const int inf = 1e9 + 1e7, N = 2e5 + 1e1;
const long long INF = 1e18 + 1e9;

struct Edge { int nxt, to; } e[N << 1];
struct Node { int ls, rs, w; } ;
int head[N], fa[N], rt[N], c[N], n, q, tot;

struct WSegmentTree {
    #define ls(x) tree[x].ls
    #define rs(x) tree[x].rs
    Node tree[N << 5];
    int nodecnt;

    void Pushup(int x) { tree[x].w = tree[ls(x)].w + tree[rs(x)].w; return ; }

    void Insert(int l, int r, int p, int &x) {
        if (!x) x = (++ nodecnt);
        if (l == r) tree[x].w = 1;
        else {
            int mid = l + r >> 1;
            if (p <= mid) Insert(l, mid, p, ls(x));
            else Insert(mid + 1, r, p, rs(x)); Pushup(x);
        } return ;
    }

    void Merge(int l, int r, int u, int &x) {
        if (!u || !x) { x += u; return ; }
        if (l == r) tree[x].w += tree[u].w;
        else {
            int mid = l + r >> 1;
            Merge(l, mid, ls(u), ls(x)), Merge(mid + 1, r, rs(u), rs(x)), Pushup(x);
        } return ;
    }

    int Query(int l, int r, int p, int x) {
        if (!x) return 0;
        if (l == r) return tree[x].w;
        else {
            int mid = l + r >> 1, res = 0;
            if (p <= mid) res += Query(l, mid, p, ls(x));
            else res += Query(mid + 1, r, p, rs(x));
            return res;
        } return 0;
    }
} wst;

void Addedge(int u, int v) { e[++ tot] = (Edge){ head[u], v }, head[u] = tot; return ; }

int Find(int x) { return (fa[x] == x ? x : fa[x] = Find(fa[x])); }

signed main() {

    n = read(), q = read();
    for (int i = 1; i <= n; i ++) fa[i] = i, c[i] = read();
    for (int i = 1; i <= n; i ++) wst.Insert(1, n, c[i], rt[i]);
    while (q --) {
        int op = read(), x = read(), y = read();
        if (op == 1) {
            int u = Find(x), v = Find(y);
            if (u != v) wst.Merge(1, n, rt[u], rt[v]), fa[u] = v;
        }
        else printf("%d\n", wst.Query(1, n, y, rt[Find(x)]));
    }

    return 0;
}