#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ULL;
#define ff first
#define ss second
#define ALL(x) (x).begin(), (x).end()
#define eb emplace_back
#define pb push_back
#define FOR(i,n) for(int i=0;i<(n);++i)
#define pii pair<int,int>
#define vi vector<int>
#define pq priority_queue
#define mii map<int,int>
#define IOS ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int oo = 2000000000; // 2e9
using namespace std;

ll score(string s){
	vector<ll> cnt(10);
	iota(ALL(cnt), 0); //0,1,2,3,4,5,6.....9

	//compute sum
	for(auto c : s) cnt[c-'0'] *= 10; // x * 10 * 10.......

	return accumulate(ALL(cnt), 0);
}
int main() {
	IOS;int k; cin>>k;
	string s, t; cin>>s>>t;
	vector<ll> cnt(10, k); //1~9 (size), k cards for every digit. (cnt)
	for(auto c : s+t){ //a way to write for all s and t.
		cnt[c-'0']--; //already flipped.
	}
	//go through all combinations
	ll win = 0;
	for(int i = 1; i<=9; i++) for(int j = 1; j<=9; j++){
		s.back() = '0' + i;
		t.back() = '0' + j;
		if(score(s) > score(t)){
			win += cnt[i] * (cnt[j] - (i == j));
		}
	}

	ll rem_cards = 9 * k - 8;
	//Probability = win / [all combinations -> (9 * k - 8)*(9*k-8-1)]
	cout<<(double)win/(rem_cards*(rem_cards - 1))<<'\n';

}
// https://atcoder.jp/contests/abc183/tasks/abc183_f

#include <bits/stdc++.h>
using namespace std;

#define REP(i,n)   for(int i=0; i<(int)(n); i++)
#define FOR(i,b,e) for(int i=(b); i<=(int)(e); i++)
#define ITR(c,it)  for(auto it = c.begin(); it != c.end(); it++)
#define DEBUG 1
#if DEBUG
  #define _GLIBCXX_DEBUG
  #define DUMP(a) REP(_i, a.size()) cout << a[_i] << (_i + 1 == a.size() ? "\n" : " ")
  #define DUMP2D(b) REP(_j, b.size()) DUMP(b[_j]); cout << endl
#else
  #define DUMP(a)
  #define DUMP2D(b)
#endif

//------------------------------------------------------------------------------
class UnionFindTree {
public:
  int n;
  vector<int> par;
  vector<int> rnk;
  vector<map<int, int>> mp;

  void init(int _n, vector<int> &c) {
    n = _n;
    par = vector<int>(n);
    rnk = vector<int>(n, 0);
    mp = vector<map<int, int>>(n);
    REP(i, n) {
      par[i] = i;
      mp[i][c[i]] = 1;
    }
  }

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

  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;

    if (rnk[x] < rnk[y]) {
      par[x] = y;
      if (mp[y].size() < mp[x].size()) swap(mp[y], mp[x]);
      ITR(mp[x], it) mp[y][it->first] += it->second;
      mp[x].clear();
    } else {
      par[y] = x;
      if (mp[x].size() < mp[y].size()) swap(mp[x], mp[y]);
      ITR(mp[y], it) mp[x][it->first] += it->second;
      mp[y].clear();
      if (rnk[x] == rnk[y]) rnk[x]++;
    }
  }

  bool same(int x, int y) {
    return find(x) == find(y);
  }

  int cnt(int x, int c) {
    x = find(x);
    return mp[x][c];
  }
};

//------------------------------------------------------------------------------
const int N_MAX = 2*1e5;
const int Q_MAX = 2*1e5;

int N, Q;
vector<int> C;

UnionFindTree uf;

int main() {
  cin >> N >> Q;
  C = vector<int>(N);
  REP(i, N) cin >> C[i];

  uf.init(N, C);
  REP(i, Q) {
    int q, a, b;
    cin >> q >> a >> b;
    if (q == 1) uf.unite(a - 1, b - 1);
    if (q == 2) cout << uf.cnt(a - 1, b) << endl;
  }
}
