//{{{
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define sz(x) (int)((x).size())
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset(a, b, sizeof(a))
#ifdef LOCAL
#include "prettyprint.hpp"
#endif
// clang-format off
void _print() { cerr << "]\033[0m\n"; }
template <typename T> void __print(T x) { cerr << x; }
template <typename T, typename... V> void _print(T t, V... v) { __print(t); if (sizeof...(v)) cerr << ", "; _print(v...); }
#ifdef LOCAL
#define debug(x...) cerr << "\033[1;34m[" << #x << "] = \033[1;32m["; _print(x)
#define debug_arr(x...) cerr << "\033[1;34m[" << #x << "] = \033[1;32m" << (x) << "\033[0m\n"
#else
#define debug(x...)
#define debug_arr(x...)
#endif
// clang-format on
//}}}

int T;
const int M = 998244353;
const int mod = 998244353;
bool sp = false;

inline int read()
{
    char c = getchar();
    int x = 0;
    while (c < '0' || c > '9')
    {
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        x = (x * 10 + c - '0');
        c = getchar();
    }
    return (int)x;
}

void print(int x)
{
    if (x > 9) print(x / 10);
    putchar(x % 10 + '0');
}

// clang-format off
template <std::uint32_t P> struct MontgomeryModInt32 {
public:
  using i32 = std::int32_t;
  using u32 = std::uint32_t;
  using i64 = std::int64_t;
  using u64 = std::uint64_t;
  using mont = MontgomeryModInt32;

private:
  u32 v;

  static constexpr u32 get_r() {
    u32 iv = P;
    for (u32 i = 0; i != 4; ++i) iv *= 2U - P * iv;
    return -iv;
  }

  static constexpr u32 r = get_r(), r2 = -u64(P) % P;

  static_assert((P & 1) == 1);
  static_assert(-r * P == 1);
  static_assert(P < (1 << 30));

  static constexpr u32 pow_mod(u32 x, u64 y) {
    u32 res = 1;
    for (; y != 0; y >>= 1, x = u64(x) * x % P)
      if (y & 1) res = u64(res) * x % P;
    return res;
  }

public:
  static constexpr u32 get_pr() {
    u32 tmp[32] = {}, cnt = 0;
    const u64 phi = P - 1;
    u64 m = phi;
    for (u64 i = 2; i * i <= m; ++i)
      if (m % i == 0) {
        tmp[cnt++] = i;
        while (m % i == 0) m /= i;
      }
    if (m != 1) tmp[cnt++] = m;
    for (u64 res = 2; res != P; ++res) {
      bool flag = true;
      for (u32 i = 0; i != cnt && flag; ++i) flag &= pow_mod(res, phi / tmp[i]) != 1;
      if (flag) return res;
    }
    return 0;
  }

  MontgomeryModInt32() = default;
  ~MontgomeryModInt32() = default;
  constexpr MontgomeryModInt32(u32 v) : v(reduce(u64(v) * r2)) {}
  constexpr MontgomeryModInt32(const mont &rhs) : v(rhs.v) {}
  static constexpr u32 reduce(u64 x) { return (x + (u64(u32(x) * r) * P)) >> 32; }
  static constexpr u32 norm(u32 x) { return x - (P & -(x >= P)); }
  constexpr u32 get() const {
    u32 res = reduce(v) - P;
    return res + (P & -(res >> 31));
  }
  explicit constexpr operator u32() const { return get(); }
  explicit constexpr operator i32() const { return i32(get()); }
  constexpr mont &operator=(const mont &rhs) { return v = rhs.v, *this; }
  constexpr mont operator-() const {
    mont res;
    return res.v = (P << 1 & -(v != 0)) - v, res;
  }
  constexpr mont inv() const { return pow(-1); } // TODO only for prime
  constexpr mont &operator+=(const mont &rhs) {
    return v += rhs.v - (P << 1), v += P << 1 & -(v >> 31), *this;
  }
  constexpr mont &operator-=(const mont &rhs) {
    return v -= rhs.v, v += P << 1 & -(v >> 31), *this;
  }
  constexpr mont &operator*=(const mont &rhs) { return v = reduce(u64(v) * rhs.v), *this; }
  constexpr mont &operator/=(const mont &rhs) { return this->operator*=(rhs.inv()); }
  friend mont operator+(const mont &lhs, const mont &rhs) { return mont(lhs) += rhs; }
  friend mont operator-(const mont &lhs, const mont &rhs) { return mont(lhs) -= rhs; }
  friend mont operator*(const mont &lhs, const mont &rhs) { return mont(lhs) *= rhs; }
  friend mont operator/(const mont &lhs, const mont &rhs) { return mont(lhs) /= rhs; }
  friend bool operator==(const mont &lhs, const mont &rhs) { return norm(lhs.v) == norm(rhs.v); }
  friend bool operator!=(const mont &lhs, const mont &rhs) { return norm(lhs.v) != norm(rhs.v); }
  friend std::istream &operator>>(std::istream &is, mont &rhs) {
    return is >> rhs.v, rhs.v = reduce(u64(rhs.v) * r2), is;
  }
  friend std::ostream &operator<<(std::ostream &os, const mont &rhs) { return os << rhs.get(); }
  constexpr mont pow(i64 y) const {
    if ((y %= P - 1) < 0) y += P - 1; // TODO phi(P) = P - 1, assume P is a prime number
    mont res(1), x(*this);
    for (; y != 0; y >>= 1, x *= x)
      if (y & 1) res *= x;
    return res;
  }
};

// clang-format on
using mint = MontgomeryModInt32<998244353>;

// clang-format off
// passed test on
// https://judge.yosupo.jp/problem/convolution_mod n,m <= 5e5 for 320ms
// https://judge.yosupo.jp/problem/inv_of_formal_power_series n,m <= 5e5 for 800 ms
#define rep(i, a, b) for (int i = a; i < (b); ++i)
typedef vector<mint> VL;
inline std::uint64_t getlen(std::uint64_t n) { // if n=0, boom
    return --n, n |= n >> 1, n |= n >> 2, n |= n >> 4, n |= n >> 8, n |= n >> 16, n |= n >> 32, ++n;
}
VL inv;
const int nttmod = 998244353, groot = 3;
//LL modpow(LL a, LL p) { LL r=1; for(;p;p>>=1,a=a*a%nttmod) if(p&1) r=r*a%nttmod; return r; }
//mint modpow(mint a, LL p) { mint r=1; for(;p;p>>=1,a=a*a) if(p&1) r=r*a; return r; }

vi rev;  // NOTICE: 2^18 = 262144 2^20 = 1048576 2^21 = 2097152
inline int init_ntt(int lena, int lenb) {
    int n = getlen(lena + lenb - 1);
    int l = 31 - __builtin_clz(n);
    rev.resize(n);
    rep(i, 0, n) rev[i] = (rev[i >> 1] | (i & 1) << l) >> 1;
    return n;
}
void ntt(VL& a, int op) {
    int n = sz(a);
    rep(i, 0, n) if (i < rev[i]) swap(a[i], a[rev[i]]);
    for (int k = 1; k < n; k <<= 1) {
        mint wn = mint(groot).pow( (nttmod - 1) / (k << 1) );
        for (int i = 0; i < n; i += (k << 1)) { mint w = 1; rep(j, 0, k) {
                mint u = a[i | j], v = w * a[i | j | k] ;
                a[i | j] = u + v, a[i | j | k] = u - v ;
                w = w * wn ;
    } } }
    if(op == -1) {
        reverse(1 + all(a));
        mint sinv = mint(n).inv();
        rep(i, 0, n) a[i] = a[i] * sinv ;
    }
}
VL conv(const VL& a, const VL& b) {
    if (a.empty() || b.empty()) return {};
    int n = init_ntt(sz(a), sz(b));
    VL L(a), R(b), out(n);
    L.resize(n), R.resize(n);
    ntt(L, 1), ntt(R, 1);
    rep(i, 0, n) out[i] = L[i] * R[i] ;
    ntt(out, -1);
    return {out.begin(), out.begin() + sz(a) + sz(b) - 1};
}

// clang-format on

VL Inv(VL& f, int n)
{
    if (n == 1) return {f[0].inv()};
    VL g = Inv(f, (n + 1) >> 1);
    VL ff = VL(f.begin(), f.begin() + min(sz(f), n));
    int nn = init_ntt(n, n);
    ff.resize(nn), g.resize(nn);
    ntt(ff, 1), ntt(g, 1);
    for (int i = 0; i < nn; ++i) g[i] = (2 - ff[i] * g[i]) * g[i];
    ntt(g, -1);
    g.resize(n);
    return g;
}

// VL Inverse(VL& f, int n)
//{
// int _n = 1 << (32 - __builtin_clz(n));
// static VL g(1);
// f.resize(_n), g[0] = modpow(f[0], nttmod - 2);
// for (int i = 2; i <= _n; i <<= 1)
//{
// VL h(i);
// for (int j = 0; j < i; j++) h[j] = f[j];
// h = conv(g, conv(g, h)), g.resize(i);
// for (int j = 0; j < i; j++) g[j] = g[j] * mint(2) - h[j];
//}
// return g.resize(n), g;
//}

// VL inverse(VL& f, int n)
//{
// assert(sz(f) && f[0] != 0);
// int _n = getlen(n);
// VL g = {f[0].inv()};

// f.resize(_n);
// for (int i = 2; i <= _n; i <<= 1)
//{
// int inner_n = getlen(i + (i >> 1) - 1);
// int l = 31 - __builtin_clz(inner_n);

// rev.resize(inner_n);
// rep(j, 0, inner_n) rev[j] = (rev[j >> 1] | (j & 1) << l) >> 1;

// VL h(inner_n, 0);
// rep(j, 0, i) h[j] = f[j];
// g.resize(inner_n);

// ntt(h, inner_n), ntt(g, inner_n);
// rep(j, 0, inner_n) g[j] = (mint(2) - g[j] * h[j]) * g[j];
// ntt(g, -1);
// g.resize(i);
//}
// return g.resize(n), g;
//}

// VL inv_rec(VL& f)
//{
// int n = sz(f);
// if (n == 1) return {modpow(f[0], nttmod - 2)};

// VL g(f);
// g.resize((n + 1) / 2);
// inv_rec(g);

// int inner_n = get_len(i + (i >> 1) - 1);
// int l = 31 - __builtin_clz(inner_n);

// int R = init(n << 1);
// rep(j, 0, inner_n) rev[j] = (rev[j >> 1] | (j & 1) << l) >> 1;

// a.resize(R), b.resize(R);
// NTT(R, a, 1), NTT(R, b, 1);
// f(i, 0, R - 1) a[i] = (2 - 1ll * a[i] * b[i] % mod + mod) * b[i] % mod;
// NTT(R, a, -1);
// a.resize(n);
//}

VL differential(VL& f)
{  // f'(x)
    if (sz(f) == 1) return {0};
    VL g(sz(f) - 1);
    for (int i = 1; i < sz(f); i++) g[i - 1] = i * f[i];
    return g;
}
VL integral(VL& f)
{  // ∫f(x)dx
    int n = sz(f) + 1;
    VL g(n);  // g[0] = 0
    if (n == 1) return g;
    int bg = sz(inv) == 0 ? 2 : sz(inv);
    if (sz(inv) < n) inv.resize(n);
    inv[1] = 1;
    for (int i = bg; i < n; i++) inv[i] = inv[nttmod % i] * (nttmod - nttmod / i);
    for (int i = 1; i < n; i++) g[i] = f[i - 1] * inv[i];
    return g;
}
VL log(VL& f, int deg = -1)
{  // log(f(x))
    if (deg == -1) deg = sz(f);
    assert(deg && f[0] == 1);
    VL f_diff = differential(f);
    VL f_inv = Inv(f, deg);
    VL g = conv(f_diff, f_inv);
    g.resize(deg - 1);
    g = integral(g);
    return g;
}

VL exp(VL& f)  // too slow
{
    int n = sz(f);
    assert(sz(f) && f[0] == 0);
    int _n = getlen(n);
    VL g(1);
    f.resize(_n), g[0] = 1;
    for (int i = 2; i <= _n; i <<= 1)
    {
        VL h(g);
        g = log(g, i);

        for (int j = 0; j < i; j++) g[j] = f[j] - g[j];
        g[0] += 1;
        g = conv(g, h);
        g.resize(i);
    }
    return g.resize(n), g;
}

VL Exp(VL& a, int len = -1)
{
    if (len == -1) len = sz(a);
    assert(sz(a) && a[0] == 0);
    if (len == 1) return {1};

    VL g = Exp(a, (len + 1) >> 1);
    VL c = log(g, len);

    for (int i = 0; i < len; ++i) c[i] = a[i] - c[i];
    c[0] += 1;

    int nn = init_ntt(len, len);
    c.resize(nn), g.resize(nn);
    ntt(c, 1), ntt(g, 1);
    for (int i = 0; i < nn; ++i) g[i] = g[i] * c[i];
    ntt(g, -1);
    g.resize(len);
    return g;
}

VL shiftl(const VL& f, int c) { return VL{f.begin() + c, f.end()}; }

VL shiftr(const VL& f, int c)
{
    VL res(sz(f) + c);
    for (int i = 0; i < sz(f); i++) res[i + c] = f[i];
    return res;
}

/*
// not zero
  Poly pow(Poly as,long long k,int deg){
    if(as==Poly(as.size(),M(0))) return Poly(deg,M(0));

    int cnt=0;
    while(as[cnt]==M(0)) cnt++;
    if(cnt*k>=deg) return Poly(deg,M(0));
    as.erase(as.begin(),as.begin()+cnt);
    deg-=cnt*k;

    M c=as[0];
    Poly zs(cnt*k,M(0));
    Poly rs=mul(exp(mul(log(mul(as,c.inv()),deg),M(k)),deg),c.pow(k));
    zs.insert(zs.end(),rs.begin(),rs.end());
    return zs;
  }
*/
VL pow(const VL& f, LL k)
{
    int n = sz(f), pos = 0;
    while (pos < n && f[pos] == 0) pos++;
    if (pos == n || pos * k >= n) return VL(n, 0);

    VL g = shiftl(f, pos);
    mint b0 = f[pos];
    mint b0inv = b0.inv();
    for (auto& ai : g) ai *= b0inv;

    g = log(g);
    for (auto& ai : g) ai *= k;
    g = Exp(g);

    g = shiftr(g, pos * k);
    g.resize(n);

    mint a0 = b0.pow(k);  // k特别大用拓展欧拉定理 k = k % (phi(mod) + mod)
    for (auto& ai : g) ai *= a0;
    return g;
}

// VL ff, gg;
void cdqfft(VL& f, VL& g, int l, int r)
{
    // cal(l, mid) , res->[mid+1, r], cal(mid+1, r);
    if (l == r) return;

    int mid = (l + r) >> 1;
    cdqfft(f, g, l, mid);

    // f[l, mid] * g[0, r-l] -> [mid+1, r]
    VL ff(mid - l + 1), gg(r - l + 1);  // ff, gg must be half interval
    for (int i = l; i <= mid; i++) ff[i - l] = f[i];
    for (int i = 0; i <= r - l; i++) gg[i] = g[i];

    VL res = conv(ff, gg);
    for (int i = mid + 1; i <= r; i++) f[i] = (f[i] + res[i - l]);

    cdqfft(f, g, mid + 1, r);
}

VL f1, f2, f;
int n, m, q;
LL k;
const int N = 2e5 + 10;
int cnt[N];
int h[N];

mint p[N];
mint p2inv[N];
mint inv2[N];

mint C(int nn, int mm)
{
    if (mm == 0 || mm == nn) return 1;
    return p[nn] * inv2[mm] * inv2[nn - mm];
}

mint pairF(int n) { return p[2 * n] * inv2[n] * p2inv[n]; }

struct I
{
    VL vec;
    I(VL& v) : vec(v) {}
    bool operator<(const I& b) const { return sz(vec) > sz(b.vec); }
};

int main()
{
#ifdef LOCAL
    freopen("in", "r", stdin);
    // freopen("out", "w", stdout);
#endif

    p2inv[0] = 1;
    for (int i = 1; i < N; ++i) p2inv[i] = p2inv[i - 1] / 2;

    for (int i = 2; i < N; ++i) p[i] = p[i - 1] * i;
    p[0] = p[1] = 1;
    for (int i = 2; i < N; ++i) p[i] = p[i - 1] * i;

    inv2[0] = 1;
    inv2[1] = 1;
    for (int i = 2; i < N; i++) inv2[i] = inv2[mod % i] * (mod - mod / i);
    for (int i = 2; i < N; i++) inv2[i] = inv2[i - 1] * inv2[i];

    clr(cnt, 0);
    n = read();

    for (int i = 1; i <= 2 * n; ++i)
    {
        h[i] = read();
        cnt[h[i]]++;
    }

    priority_queue<I> pq;
    for (int i = 1; i <= 100000; i++)
    {
        if (cnt[i] <= 1) continue;
        VL b(cnt[i] / 2 + 1);
        for (int j = 0; 2 * j <= cnt[i]; j++) b[j] = pairF(j) * C(cnt[i], 2 * j);
        pq.push(I(b));
    }

    while (pq.size() > 1)
    {
        I h1 = pq.top();
        pq.pop();

        I h2 = pq.top();
        pq.pop();

        VL v = conv(h1.vec, h2.vec);
        pq.push(I(v));
    }

    VL v = VL(n + 1, 0);
    v[0] = 1;

    if (pq.size() == 1)
    {
        I h = pq.top();
        pq.pop();
        v = h.vec;
        v.resize(n + 1);
        assert(v[0] == 1);
    }

    mint ans = 0;
    for (int i = 0; i <= n; i++)
    {
        mint v1 = v[i];
        mint v2 = pairF(n - i);
        mint vv = v1 * v2;
        if (i & 1)
            ans -= vv;
        else
            ans += vv;
    }
    print(ans.get());

    // n++;
    // m++;

    // f.resize(m + 1);
    // clr(cnt, 0);
    // clr(cnt2, 0);
    // f1.resize(n + 1, 0);
    // f2.resize(n + 1, 0);
    // f.resize(n + 1, 0);

    // int a;
    // clr(cnt, 0);
    // for (int i = 0; i < n; ++i)
    //{
    // a = read();
    // cnt[a]++;
    //}

    // for (int i = 1; i <= m; i++)
    //{
    // if (cnt[i] == 0) continue;

    // for (int j = 1; j * i <= m; j++)
    //{
    // f[j * i] += mint(j).inv() * cnt[i];
    //}
    //}

    // VL g = Exp(f);

    // for (int i = 1; i <= q; ++i)
    //{
    // int a, b;
    // a = read();
    // b = read();

    // for (LL j = 1; j * a <= n; j++)
    //{
    // f1[j * a] += -(mint(j).inv());
    //}

    // for (LL j = 1; j * a * (b + 1) <= n; j++)
    //{
    // f2[j * a * (b + 1)] += -(mint(j).inv());
    //}
    //}

    // for (int i = 1; i <= q; ++i)
    //{
    // int a, b;
    // a = read();
    // b = read();
    // cnt[a]++;
    // if (1ll * (b + 1) * a <= n) cnt2[1ll * (b + 1) * a]++;
    //}

    // for (int i = 1; i <= n; i++)
    //{
    // if (cnt[i] == 0) continue;
    // for (LL j = 1; j * i <= n; j++) f1[j * i] -= (mint(j).inv() * cnt[i]);
    //}

    // for (int i = 1; i <= n; i++)
    //{
    // if (cnt2[i] == 0) continue;

    // for (LL j = 1; j * i <= n; j++) f2[j * i] -= (mint(j).inv() * cnt2[i]);
    //}

    // for (int i = 0; i <= n; i++) f[i] = f2[i] - f1[i];

    // assert(f[0] == 0);
    // f[0] = 0;
    // VL g(n + 1, 0);
    // VL g = pow(f, m);
    // debug(g);

    // g.resize(m);

    // inv[1] = 1;
    // for (int i = 2; i < 2000010; i++) inv[i] = inv[nttmod % i] * (nttmod - nttmod / i) %
    // nttmod;

    // scanf("%d", &x);
    // f[i] = x;
    // for (int i = 0; i < m; ++i) g[i] = read();
    // for (int i = 0; i < m; ++i) g[i] = read();

    // res = pow(f, k);
    // VL g = Inv(f, n);

    // g = exp(f);

    // for (int i = 1; i < sz(g); ++i)
    //{
    // print(g[i].get());
    // putchar('\n');
    //}
    // putchar('\n');
}

Measured memory_bytes: 9176