#pragma GCC optimize("Ofast", "unroll-loops")

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ld long double

template<ll mod = 998244353LL, ll root = 3LL>
void ntt(vector<ll>& a, bool rev = false){
    auto modpow = [](ll p, int q){
        ll res = 1LL;
        while (q){
            if (q & 1) res = res * p % mod;
            p = p * p % mod;
            q >>= 1;
        }
        return res;
    };
    const int sz = a.size();
    if (sz == 1) return;
    vector<ll> b(sz);
    int r = rev ? (mod - 1 - (mod - 1) / sz) : (mod - 1) / sz;
    ll s = modpow(root, r);
    vector<ll> kp(sz / 2 + 1, 1);
    for(int i = 0; i < sz / 2; ++i) kp[i + 1] = kp[i] * s % mod;
    for(int i = 1, l = sz / 2; i < sz; i <<= 1, l >>= 1){
        for(int j = 0, r = 0; j < l; ++j, r += i){
            for(int k = 0, s = kp[i * j]; k < i; ++k){
                ll p = a[k + r], q = a[k + r + sz / 2];
                b[k + 2 * r] = (p + q) % mod;
                b[k + 2 * r + i] = ((p - q) % mod + mod) % mod * s % mod;
            }
        }
        swap(a, b);
    }
    if(rev){
        s = modpow(sz, mod - 2);
        for(int i = 0; i < sz; i++){ a[i] = a[i] * s % mod; }
    }
}

template<ll mod = 998244353LL, ll root = 3LL>
vector<ll> convolute(vector<ll> a, vector<ll> b){
    const int sz = (int)a.size() + (int)b.size() - 1;
    int t = 1;
    while (t < sz) t *= 2;
    a.resize(t, 0); b.resize(t, 0);
    ntt<mod, root>(a); ntt<mod, root>(b);
    for (int i = 0; i < t; ++i)
        a[i] = a[i] * b[i] % mod;
    ntt<mod, root>(a, true);
    a.resize(sz);
    return a;
}

constexpr ll mod = 998244353LL;

ll fact(ll k, int sgn = 0) {
	static vector<ll> fac(2, 1LL);
	static vector<ll> inv(2, 1LL);
	static vector<ll> finv(2, 1LL);
	static ll nx = 2LL;
	while (nx <= k) {
		fac.push_back(fac[nx - 1] * nx % mod);
		inv.push_back(mod - inv[mod % nx] * (mod / nx) % mod);
		finv.push_back(finv[nx - 1] * inv[nx] % mod);
		++nx;
	}
	if (sgn == 0)
		return fac[k];
	return finv[k];
}

ll comb(ll a, ll b) {
	if (b < 0 || b > a) return 0;
	return fact(a) * fact(b, 1) % mod * fact(a - b, 1) % mod;
}

ll calc_A(int n){
	static vector<ll> A(2, 1);
	int i = A.size();
	while (i <= n){
		ll Ai = A[i - 1] * (2 * i - 1) % mod;
		A.push_back(Ai);
		++i;
	}
	return A[n];
}

int N;
map<int, int> cnt;

void input(void){
	cin >> N;
	for (int i = 0; i < 2 * N; ++i){
		int hi; cin >> hi;
		++cnt[hi];
	}
}

using func = vector<ll>;

void print(func f){
	for (auto& fi : f)
		cout << fi << " ";
	cout << endl;
}

func merge(void){
	vector<func> fs;
	for (auto& e : cnt){
		int c = e.second;
		func tmp(c / 2 + 1);
		for (int p = 0; p <= c / 2; ++p){
			tmp[p] = (p & 1 ? (-1LL) : (1LL)) * comb(c, 2 * p) * calc_A(p) % mod;
			if (tmp[p] < 0) tmp[p] += mod;
		}
		fs.push_back(tmp);
	}
	sort(
		fs.begin(), fs.end(),
		[](func a, func b){
			return a.size() > b.size();
		}
	);
	vector<func> new_func;
	while (fs.size() > 1){
		new_func = vector<func>((fs.size() + 1) / 2);
		for (int i = 0; i < fs.size(); i += 2){
			new_func[i / 2] = (i + 1 == fs.size()) ? fs[i] : convolute<>(fs[i], fs[i + 1]);
		}
		fs = new_func;
	}
	return fs[0];
}

ll solve(void){
	func f1 = merge();
	func f2(N + 1);
	for (int i = 0; i <= N; ++i)
		f2[i] = calc_A(i);
	func f = convolute<>(f1, f2);
	return f[N];
}

int main(void){
	input();
	cout << solve() << endl;
	return 0;
}

Measured memory_bytes: 16104