#pragma GCC optimize("Ofast")

#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <algorithm>
#include <iomanip>
#include <set>
#include <map>
#include <bitset>
#include <cmath>
#include <functional>

using namespace std;

#define REP(i,n) for(ll (i) = (0);(i) < (n);++i)
#define REV(i,n) for(ll (i) = (n) - 1;(i) >= 0;--i)
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
#define SHOW1d(v,n) {REP(WW,n)cerr << v[WW] << ' ';cerr << endl;}
#define SHOW2d(v,WW,HH) {REP(W_,WW){REP(H_,HH)cerr << v[W_][H_] << ' ';cerr << endl;}}
#define ALL(v) v.begin(),v.end()
#define Decimal fixed<<setprecision(20)
#define INF 1000000000
#define LLINF 1000000000000000000LL
#define MOD 1000000007LL

typedef long long ll;
typedef pair<ll,ll> P;

struct UF {
	vector<int> par; // 親のインデックスを記憶する配列
	vector<int> sz; // サイズを記憶する。
	vector<map<int, int> > value;
	// 初期化
	UF(int n):par(n),sz(n),value(n){
		for(int i = 0; i < n; i++){
			par[i] = i;sz[i] = 1;
		}
	}
	// 親を求める
	int find(int x) {
		if (par[x] == x) return x;
		else return par[x] = find(par[x]);
	}
	// xとyの属する集合を併合
	void unite(int x, int y) {
		x = find(x); y = find(y);
		if (x == y) return;
		if(sz[x] > sz[y])swap(x, y);
		par[x] = y;
		sz[y] += sz[x];
		// マージデクはここでやる(x -> y)
		for(pair<int, int> const &ele: value[x]) {
			value[y][ele.FI] += ele.SE;
		}
	}
	// xとyが同じ集合ならtrue
	bool same(int x, int y) { return find(x) == find(y); }
	// 素の集合のサイズを求める
	int size(int n){return sz[find(n)];}
};

int main(){
	cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);

	int N, Q;cin >> N >> Q;
	UF uf(N);

	REP(i, N) {
		int c;cin >> c;c--;
		uf.value[i][c]++;
	}

	REP(i, Q) {
		int type, a, b;cin >> type >> a >> b;
		a--;b--;
		if(type == 1) {
			a = uf.find(a);
			b = uf.find(b);
			if(uf.same(a, b))continue;
			uf.unite(a, b);
		}
		else {
			a = uf.find(a);
			cout << uf.value[a][b] << endl;
		}
	}

	return 0;
}

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using vl = vector<ll>;
using vc = vector<char>;
using vvl = vector<vl>;
using vvc = vector<vc>;
using pll = pair<ll, ll>;
using vpll = vector<pll>;
#define rep(i, n) for(ll i = 0; i < (ll)(n); i++)
#define rep2(i, m, n) for(ll i = (ll)(m); i < (ll)(n); i++)
#define rep_inv(i, n, m) for(ll i = (ll)(n); i > (ll)(m); i--)

bool compare2nd(vl a, vl b){
  if(a[1] <= b[1])
    return true;
  else
    return false;
}

bool pair_same(vl a, vl b){
  if(((a[1] == b[1]) && (a[2] == b[2])) || ((a[1] == b[2]) && (a[2] == b[1])))
    return true;
  else
    return false;
}

int main(){
  ll N;
  cin >> N;

  vvl xy(N, vl(3));
  rep(i, N){
    cin >> xy[i][0] >> xy[i][1];
    xy[i][2] = i;
  }

  vvl xy1(N, vl(3));
  vvl xy2(N, vl(3));
  
  stable_sort(xy.begin(), xy.end());
  copy(xy.begin(), xy.end(), xy1.begin());
  
  stable_sort(xy.begin(), xy.end(), compare2nd);
  copy(xy.begin(), xy.end(), xy2.begin());

  /*
  cout << "x : \n";
  rep(i, N){
    cout << xy1[i][0] << ' ' << xy1[i][1] << ' ' << xy1[i][2] << "\n";
  }

  cout << "y : \n";
  rep(i, N){
    cout << xy2[i][0] << ' ' << xy2[i][1] << ' ' << xy2[i][2] << "\n";
  }
  */

  vvl cands;

  // x の max - min, max2nd - min, max - min2nd をインデックスと一緒に格納
  cands.push_back({xy1[N - 1][0] - xy1[0][0], xy1[N - 1][2], xy1[0][2]});
  cands.push_back({xy1[N - 1][0] - xy1[1][0], xy1[N - 1][2], xy1[1][2]});
  cands.push_back({xy1[N - 2][0] - xy1[0][0], xy1[0][2], xy1[N - 2][2]});

  // y の max - min, max2nd - min, max - min2nd をインデックスと一緒に格納
  cands.push_back({xy2[N - 1][1] - xy2[0][1], xy2[N - 1][2], xy2[0][2]});
  cands.push_back({xy2[N - 1][1] - xy2[1][1], xy2[N - 1][2], xy2[1][2]});
  cands.push_back({xy2[N - 2][1] - xy2[0][1], xy2[0][2], xy2[N - 2][2]});

  stable_sort(cands.begin(), cands.end(), greater<vl>());

  /*
  cout << "\n";
  cout << "cands\n";
  rep(i, cands.size()){
    cout << cands[i][0] << ' ' << cands[i][1] << ' ' << cands[i][2] << "\n";
  }
  */

  if(!pair_same(cands[0], cands[1]))
    cout << cands[1][0] << "\n";
  else
    cout << cands[2][0] << "\n";
  
  return 0;
}
