#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
long long gcd(long long a, long long b) {
  long long c = a % b;
  while (c != 0) {
    a = b;
    b = c;
    c = a % b;
  }
  return b;
}
long long extGCD(long long a, long long b, long long &x, long long &y) {
  if (b == 0) {
    x = 1;
    y = 0;
    return a;
  }
  long long d = extGCD(b, a % b, y, x);
  y -= a / b * x;
  return d;
}
struct UnionFind {
  vector<long long> data;
  UnionFind(int sz) { data.assign(sz, -1); }
  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if (x == y) return (false);
    if (data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }
  int find(int k) {
    if (data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }
  long long size(int k) { return (-data[find(k)]); }
};
long long M = 1000000007;
vector<long long> fac(2000011);
vector<long long> ifac(2000011);
long long mpow(long long x, long long n) {
  long long ans = 1;
  while (n != 0) {
    if (n & 1) ans = ans * x % M;
    x = x * x % M;
    n = n >> 1;
  }
  return ans;
}
long long mpow2(long long x, long long n, long long mod) {
  long long ans = 1;
  while (n != 0) {
    if (n & 1) ans = ans * x % mod;
    x = x * x % mod;
    n = n >> 1;
  }
  return ans;
}
void setcomb() {
  fac[0] = 1;
  ifac[0] = 1;
  for (long long i = 0; i < 2000010; i++) {
    fac[i + 1] = fac[i] * (i + 1) % M;
  }
  ifac[2000010] = mpow(fac[2000010], M - 2);
  for (long long i = 2000010; i > 0; i--) {
    ifac[i - 1] = ifac[i] * i % M;
  }
}
long long comb(long long a, long long b) {
  if (a == 0 && b == 0) return 1;
  if (a < b || a < 0) return 0;
  long long tmp = ifac[a - b] * ifac[b] % M;
  return tmp * fac[a] % M;
}
long long perm(long long a, long long b) {
  if (a == 0 && b == 0) return 1;
  if (a < b || a < 0) return 0;
  return fac[a] * ifac[a - b] % M;
}
long long modinv(long long a) {
  long long b = M, u = 1, v = 0;
  while (b) {
    long long t = a / b;
    a -= t * b;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  u %= M;
  if (u < 0) u += M;
  return u;
}
long long modinv2(long long a, long long mod) {
  long long b = mod, u = 1, v = 0;
  while (b) {
    long long t = a / b;
    a -= t * b;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  u %= mod;
  if (u < 0) u += mod;
  return u;
}
template <int mod>
struct ModInt {
  int x;
  ModInt() : x(0) {}
  ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
  ModInt &operator+=(const ModInt &p) {
    if ((x += p.x) >= mod) x -= mod;
    return *this;
  }
  ModInt &operator-=(const ModInt &p) {
    if ((x += mod - p.x) >= mod) x -= mod;
    return *this;
  }
  ModInt &operator*=(const ModInt &p) {
    x = (int)(1LL * x * p.x % mod);
    return *this;
  }
  ModInt &operator/=(const ModInt &p) {
    *this *= p.inverse();
    return *this;
  }
  ModInt operator-() const { return ModInt(-x); }
  ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
  ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
  ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
  ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
  bool operator==(const ModInt &p) const { return x == p.x; }
  bool operator!=(const ModInt &p) const { return x != p.x; }
  ModInt inverse() const {
    int a = x, b = mod, u = 1, v = 0, t;
    while (b > 0) {
      t = a / b;
      swap(a -= t * b, b);
      swap(u -= t * v, v);
    }
    return ModInt(u);
  }
  ModInt pow(int64_t n) const {
    ModInt ret(1), mul(x);
    while (n > 0) {
      if (n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }
  friend ostream &operator<<(ostream &os, const ModInt &p) { return os << p.x; }
  friend istream &operator>>(istream &is, ModInt &a) {
    int64_t t;
    is >> t;
    a = ModInt<mod>(t);
    return (is);
  }
  static int get_mod() { return mod; }
};
using mint = ModInt<mod>;
vector<vector<mint>> mul(vector<vector<mint>> a, vector<vector<mint>> b) {
  int i, j, k;
  mint t;
  int n = a.size(), m = b[0].size(), l = a[0].size();
  vector<vector<mint>> c(n, vector<mint>(m));
  for (i = 0; i < n; i++) {
    for (j = 0; j < m; j++) {
      t = 0;
      for (k = 0; k < l; k++) t += a[i][k] * b[k][j];
      c[i][j] = t;
    }
  }
  return c;
}
vector<vector<mint>> mat_pow(vector<vector<mint>> x, long long n) {
  long long k = x.size();
  vector<vector<mint>> ans(k, vector<mint>(k, 0));
  for (int i = 0; i < k; i++) ans[i][i] = 1;
  while (n != 0) {
    if (n & 1) ans = mul(ans, x);
    x = mul(x, x);
    n = n >> 1;
  }
  return ans;
}
template <int mod>
struct NumberTheoreticTransform {
  vector<int> rev, rts;
  int base, max_base, root;
  NumberTheoreticTransform() : base(1), rev{0, 1}, rts{0, 1} {
    assert(mod >= 3 && mod % 2 == 1);
    auto tmp = mod - 1;
    max_base = 0;
    while (tmp % 2 == 0) tmp >>= 1, max_base++;
    root = 2;
    while (mod_pow(root, (mod - 1) >> 1) == 1) ++root;
    assert(mod_pow(root, mod - 1) == 1);
    root = mod_pow(root, (mod - 1) >> max_base);
  }
  inline int mod_pow(int x, int n) {
    int ret = 1;
    while (n > 0) {
      if (n & 1) ret = mul(ret, x);
      x = mul(x, x);
      n >>= 1;
    }
    return ret;
  }
  inline int inverse(int x) { return mod_pow(x, mod - 2); }
  inline unsigned add(unsigned x, unsigned y) {
    x += y;
    if (x >= mod) x -= mod;
    return x;
  }
  inline unsigned mul(unsigned a, unsigned b) {
    return 1ull * a * b % (unsigned long long)mod;
  }
  void ensure_base(int nbase) {
    if (nbase <= base) return;
    rev.resize(1 << nbase);
    rts.resize(1 << nbase);
    for (int i = 0; i < (1 << nbase); i++) {
      rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (nbase - 1));
    }
    assert(nbase <= max_base);
    while (base < nbase) {
      int z = mod_pow(root, 1 << (max_base - 1 - base));
      for (int i = 1 << (base - 1); i < (1 << base); i++) {
        rts[i << 1] = rts[i];
        rts[(i << 1) + 1] = mul(rts[i], z);
      }
      ++base;
    }
  }
  void ntt(vector<int> &a) {
    const int n = (int)a.size();
    assert((n & (n - 1)) == 0);
    int zeros = __builtin_ctz(n);
    ensure_base(zeros);
    int shift = base - zeros;
    for (int i = 0; i < n; i++) {
      if (i < (rev[i] >> shift)) {
        swap(a[i], a[rev[i] >> shift]);
      }
    }
    for (int k = 1; k < n; k <<= 1) {
      for (int i = 0; i < n; i += 2 * k) {
        for (int j = 0; j < k; j++) {
          int z = mul(a[i + j + k], rts[j + k]);
          a[i + j + k] = add(a[i + j], mod - z);
          a[i + j] = add(a[i + j], z);
        }
      }
    }
  }
  vector<int> multiply(vector<int> a, vector<int> b) {
    int need = a.size() + b.size() - 1;
    int nbase = 1;
    while ((1 << nbase) < need) nbase++;
    ensure_base(nbase);
    int sz = 1 << nbase;
    a.resize(sz, 0);
    b.resize(sz, 0);
    ntt(a);
    ntt(b);
    int inv_sz = inverse(sz);
    for (int i = 0; i < sz; i++) {
      a[i] = mul(a[i], mul(b[i], inv_sz));
    }
    reverse(a.begin() + 1, a.end());
    ntt(a);
    a.resize(need);
    return a;
  }
  vector<int> pol_pow(vector<int> a, int n) {
    int need = a.size() * n - (n - 1);
    int nbase = 1;
    while ((1 << nbase) < need) nbase++;
    ensure_base(nbase);
    int sz = 1 << nbase;
    a.resize(sz, 0);
    ntt(a);
    int inv_sz = inverse(sz);
    for (int i = 0; i < sz; i++) {
      a[i] = mul(mpow(a[i], n), inv_sz);
    }
    reverse(a.begin() + 1, a.end());
    ntt(a);
    a.resize(need);
    return a;
  }
};
int main() {
  M = 998244353;
  setcomb();
  long long n, cnt[111111] = {};
  cin >> n;
  long long h;
  for (long long i = 0; i < (n * 2); i++) cin >> h, cnt[--h]++;
  sort(cnt, cnt + 100000);
  long long cnt2;
  NumberTheoreticTransform<mod> ntt;
  vector<int> x(1, 1);
  for (long long i = 0; i < (100000); i++) {
    if (i == 0)
      cnt2 = 1;
    else {
      if (cnt[i] != cnt[i - 1]) {
        vector<int> p(cnt[i - 1] / 2 + 1, 0);
        for (long long j = 0; j < (cnt[i - 1] / 2 + 1); j++) {
          if (j == 0)
            p[0] = 1;
          else {
            long long res = p[j - 1];
            res *= comb(cnt[i - 1] - (j - 1) * 2, 2);
            res %= M;
            p[j] = res;
          }
        }
        x = ntt.multiply(x, ntt.pol_pow(p, cnt2));
        cnt2 = 1;
      } else
        cnt2++;
    }
  }
  vector<int> pl(cnt[99999] / 2 + 1, 0);
  for (long long j = 0; j < (cnt[99999] / 2 + 1); j++)
    pl[j] = comb(cnt[99999], j * 2);
  x = ntt.multiply(x, ntt.pol_pow(pl, cnt2));
  x.resize(n + 1, 0);
  mint k[111111];
  k[0] = 1;
  for (long long i = 0; i < (n); i++)
    k[(i + 1) * 2] = k[i * 2] / (i + 1) * comb((i + 1) * 2, 2);
  mint ans = 0;
  for (long long i = 0; i < (n + 1); i++) {
    if (i % 2 == 0)
      ans += k[2 * (n - i)] * x[i];
    else
      ans -= k[2 * (n - i)] * x[i];
  }
  cout << ans << endl;
}
