#include <bits/stdc++.h>
#pragma GCC optimize("O3", "unroll-loops")
#pragma GCC target("sse4.2")
using namespace std;
mt19937 gen(chrono::high_resolution_clock::now().time_since_epoch().count());
template <class T, class U>
inline bool chmax(T &x, U y) {
  return x < y ? x = y, 1 : 0;
}
template <class T, class U>
inline bool chmin(T &x, U y) {
  return x > y ? x = y, 1 : 0;
}
template <class T>
inline void sort(T &a) {
  sort((a).begin(), (a).end());
}
template <class T>
inline void rsort(T &a) {
  sort((a).rbegin(), (a).rend());
}
template <class T>
inline void reverse(T &a) {
  reverse((a).begin(), (a).end());
}
template <class T, class U>
inline istream &operator>>(istream &str, pair<T, U> &p) {
  return str >> p.first >> p.second;
}
template <class T>
inline istream &operator>>(istream &str, vector<T> &a) {
  for (auto &i : a) str >> i;
  return str;
}
template <class T>
inline T sorted(T a) {
  sort(a);
  return a;
}
void read() {}
void print() {}
void println() { cout << '\n'; }
template <class T, class... U>
void read(T &x, U &...u) {
  cin >> x;
  read(u...);
}
template <class T, class... U>
void print(const T &x, const U &...u) {
  cout << x;
  print(u...);
}
template <class T, class... U>
void println(const T &x, const U &...u) {
  cout << x;
  println(u...);
}
template <int md, int maxpw = 23>
struct NTT {
  int mul(int a, int b) { return a * (long long)b % md; }
  int sum(int a, int b) {
    a += b;
    if (a >= md) a -= md;
    return a;
  }
  int diff(int a, int b) {
    a -= b;
    if (a < 0) a += md;
    return a;
  }
  int mpow(int a, int n) {
    int ans = 1;
    for (; n > 0; n >>= 1, a = mul(a, a))
      if (n & 1) ans = mul(ans, a);
    return ans;
  }
  int lst(int n) const { return 31 - __builtin_clz(n); }
  vector<int> sq;
  NTT() {
    int rt = 2;
    while (mpow(rt, 1 << maxpw) != 1 || mpow(rt, 1 << maxpw - 1) == 1) ++rt;
    sq = vector<int>(maxpw + 1);
    sq[maxpw] = rt;
    for (int i = maxpw - 1; i >= 0; --i) sq[i] = mul(sq[i + 1], sq[i + 1]);
  }
  vector<int> ind;
  void dft(vector<int> &a, int n) {
    int LOG = lst(n);
    if (signed((ind).size()) != n) {
      ind.resize(n);
      for (int i = 1; i < n; ++i) {
        int l = lst(i);
        ind[i] = ind[i ^ (1 << l)] | (1 << LOG - 1 - l);
      }
    }
    vector<int> cp(n);
    for (int i = 0; i < n; ++i) cp[i] = a[ind[i]];
    swap(a, cp);
    for (int len = 2; len <= n; len *= 2) {
      int w = sq[lst(len)];
      int l2 = len >> 1, msk = l2 - 1;
      for (int i = 0; i < n; i += len) {
        int c = 1;
        for (int j = 0; j < l2; ++j) {
          int x = a[i + j], y = mul(a[i + l2 + j], c);
          a[i + j] = sum(x, y);
          a[i + j + l2] = diff(x, y);
          c = mul(c, w);
        }
      }
    }
  }
  vector<int> multiply(vector<int> &a, vector<int> &b) {
    int n = 1;
    while (n < signed((a).size()) || n < signed((b).size())) n *= 2;
    n *= 2;
    a.resize(n);
    b.resize(n);
    dft(a, n);
    dft(b, n);
    vector<int> ans(n);
    for (int i = 0; i < n; ++i) ans[i] = mul(a[i], b[i]);
    dft(ans, n);
    reverse(ans.begin() + 1, ans.end());
    int inv = mpow(n, md - 2);
    for (auto &i : ans) i = mul(i, inv);
    return ans;
  }
};
struct zet {
  int val;
  explicit operator int() const { return val; }
  zet(long long x = 0) {
    val = (x >= -998244353 && x < 998244353 ? x : x % 998244353);
    if (val < 0) val += 998244353;
  }
  zet(long long a, long long b) {
    *this += a;
    *this /= b;
  }
  zet &operator+=(zet const &b) {
    val += b.val;
    if (val >= 998244353) val -= 998244353;
    return *this;
  }
  zet &operator-=(zet const &b) {
    val -= b.val;
    if (val < 0) val += 998244353;
    return *this;
  }
  zet &operator*=(zet const &b) {
    val = (val * (long long)b.val) % 998244353;
    return *this;
  }
  friend zet mypow(zet a, long long n) {
    zet res = 1;
    while (n) {
      if (n & 1) res *= a;
      a *= a;
      n >>= 1;
    }
    return res;
  }
  friend zet inv(zet a) { return mypow(a, 998244353 - 2); }
  zet &operator/=(zet const &b) { return *this *= inv(b); }
  friend zet operator+(zet a, const zet &b) { return a += b; }
  friend zet operator-(zet a, const zet &b) { return a -= b; }
  friend zet operator-(zet a) { return 0 - a; }
  friend zet operator*(zet a, const zet &b) { return a *= b; }
  friend zet operator/(zet a, const zet &b) { return a /= b; }
  friend istream &operator>>(istream &stream, zet &a) {
    return stream >> a.val;
  }
  friend ostream &operator<<(ostream &stream, const zet &a) {
    return stream << a.val;
  }
  friend bool operator==(zet const &a, zet const &b) { return a.val == b.val; }
  friend bool operator!=(zet const &a, zet const &b) { return a.val != b.val; }
  friend bool operator<(zet const &a, zet const &b) { return a.val < b.val; }
};
const int N = 2e5 + 100;
zet fact[N];
zet C(int n, int k) {
  if (n < k) return 0;
  return fact[n] / (fact[n - k] * fact[k]);
}
signed main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  cout << fixed << setprecision(12);
  fact[0] = 1;
  for (int i = 1; i < N; ++i) fact[i] = fact[i - 1] * i;
  int n;
  read(n);
  vector<int> h(2 * n);
  read(h);
  map<int, int> cnt;
  for (auto i : h) cnt[i]++;
  vector<vector<int>> v;
  for (auto i : cnt) {
    vector<int> a;
    a.push_back(1);
    for (int k = 1; k + k <= i.second; ++k)
      a.push_back((a.back() * C(i.second - 2 * (k - 1), 2)).val);
    0;
    v.push_back(a);
  }
  NTT<998244353> ntt;
  while (signed((v).size()) > 1) {
    vector<vector<int>> newv;
    for (int i = 0; i + 1 < signed((v).size()); i += 2)
      newv.push_back(ntt.multiply(v[i], v[i + 1]));
    if (signed((v).size()) & 1) newv.push_back(v.back());
    v = move(newv);
  }
  0;
  zet ans = 0;
  vector<zet> x(2 * n + 1);
  x[0] = 1;
  for (int k = 2; k <= 2 * n; k += 2) x[k] = x[k - 2] * (k - 1);
  0;
  for (int k = 0; k < signed((v[0]).size()); ++k)
    ans += mypow(zet(998244353 - 1), k) * v[0][k] * x[2 * n - 2 * k];
  println(ans);
  return 0;
}
