#include <bits/stdc++.h>
#define F first
#define S second
using namespace std;
typedef long long LL;
namespace DS {
  template <typename T, T e, T (*F)(T, T)> struct BIT {
    vector<T> data; int n;
    BIT (int _n = 0) { init(_n); }
    BIT (vector<T> arr) { init(arr); }
    void init(int _n) { n = _n; data.assign(n + 1, e); }
    void init(vector<T> arr) { init(arr.size());
      for (int i = 0 ; i < arr.size() ; i++) m(i, arr[i]);
    }
    T q(int R) { assert(0 <= R && R <= n);
      T ret = e;
      while (R > 0)
        ret = F(ret, data[R]), R -= R & -R;
      return ret;
    }
    void m(int x, T d) { x++;
      while (x <= n)
        data[x] = F(data[x], d), x += x & -x;
    }
    friend ostream& operator << (ostream& out, BIT &bit) {
      out << "===========================================\n";
      cout << "structure type: BIT, size: " << bit.n << '\n';
      for (int i = 0 ; i <= bit.n ; i++)
        cout << "bit[0, " << i << "): " << bit.q(i) << '\n';
      out << "===========================================\n";
      return out;
    }
    friend istream& operator >> (istream& in, BIT &bit) {
      vector<T> arr(bit.n);
      for (auto &v : arr) in >> v;
      bit.init(arr);
      return in;
    }
  };
  template <typename T, T (*F)(T, T)> struct SparseTable {
    int n, lgn; vector<vector<T>> data;
    SparseTable(int _n) { vector<T> arr(n = _n); init(arr); }
    SparseTable(vector<T> arr) { init(arr); }
    void init(vector<T> arr) { n = arr.size();
      data.assign(lgn = __lg(n) + 1, vector<T>(n));
      data[0] = arr;
      for (int h = 0 ; h < lgn - 1 ; h++) {
        for (int i = 0 ; i < n ; i++) {
          if (i + (1 << h) < n)
            data[h + 1][i] = F(data[h][i], data[h][i + (1 << h)]);
          else
            data[h + 1][i] = data[h][i];
        }
      }
    }
    T q(int L, int R) {
      assert(0 <= L && L < n && 0 < R && R <= n && L < R);
      T ret = data[0][L];
      for (int h = lgn - 1 ; h >= 0 ; h--)
        if (L + (1 << h) <= R)
          ret = F(ret, data[h][L]), L += 1 << h;
      return ret;
    }
    friend istream& operator >> (istream &in, SparseTable &st) {
      vector<T> arr(st.n);
      for (auto &v : arr) in >> v;
      st.init(arr);
      return in;
    }
    friend ostream& operator << (ostream &out, SparseTable &st) {
      out << "===========================================\n";
      out << "structure type: SparseTable, size: " << st.n << '\n';
      for (int i = 0 ; i < st.n ; i++)
        for (int j = i + 1 ; j <= st.n ; j++)
          cout << "[" << i << ", " << j << "): " << st.q(i, j) << '\n';
      out << "===========================================\n";
      return out;
    }
  };
}
namespace Math {
  const int MOD107 = 1e9 + 7;
  const int MOD998 = 998244353;
  const int INT_INF = 0x3f3f3f3f;
  const LL LL_INF = ((LL)INT_INF << 32) | INT_INF;
  struct Value { virtual ~Value() = default; };
  template <const int P> struct Mint : Value { int v;
    inline int trim(int _v) { return (_v % P + P) % P; }
    Mint (int _v = 0) : v(trim(_v)) { }
    bool operator == (const Mint &m) const { return v == m.v; }
    bool operator != (const Mint &m) const { return v != m.v; }
    bool operator < (const Mint &m) const { return v < m.v; }
    bool operator <= (const Mint &m) const { return v <= m.v; }
    bool operator > (const Mint &m) const { return v > m.v; }
    bool operator >= (const Mint &m) const { return v >= m.v; }
    friend bool operator == (const int &a, const Mint &m) { return Mint(a) == m.v; }
    friend bool operator != (const int &a, const Mint &m) { return Mint(a) != m.v; }
    friend bool operator < (const int &a, const Mint &m) { return Mint(a) < m.v; }
    friend bool operator <= (const int &a, const Mint &m) { return Mint(a) <= m.v; }
    friend bool operator > (const int &a, const Mint &m) { return Mint(a) > m.v; }
    friend bool operator >= (const int &a, const Mint &m) { return Mint(a) >= m.v; }
    Mint& operator += (const Mint &m) { v = trim(v + m.v); return *this; }
    Mint operator + (const Mint &m) const { Mint t = *this; return t += m; }
    friend Mint operator + (const int &a, const Mint &m) { return Mint(a) + m; }
    Mint& operator ++()  { return *this += 1; }
    Mint operator ++(int) { Mint t = *this; ++*this; return t; }
    Mint operator - () const { return Mint(-v); }
    Mint& operator -= (const Mint &m) { v = trim(v - m.v); return *this; }
    Mint operator - (const Mint &m) const { Mint t = *this; return t -= m; }
    friend Mint operator - (const int &a, const Mint &m) { return Mint(a) - m; }
    Mint& operator --()  { return *this -= 1; }
    Mint operator --(int) { Mint t = *this; --*this; return t; }
    Mint& operator *= (const Mint &m) { v = trim(1ll * v * m.v % P); return *this; }
    Mint operator * (const Mint &m) const { Mint t = *this; return t *= m; }
    friend Mint operator * (const int &a, const Mint &m) { return Mint(a) * m; }
    Mint pow(long long n) const { Mint m = *this, ans = 1; while (n) ans *= (n & 1 ? m : 1), m *= m, n >>= 1; return ans; }
    Mint pow(const Mint &n) const { return pow(n.v); }
    Mint inv() const { return pow(P - 2); }
    Mint& operator /= (const Mint &m) { *this = v * m.inv(); return *this; }
    Mint operator / (const Mint &m) { Mint t = *this; return t /= m; }
    friend Mint operator / (const int &a, const Mint &m) { return Mint(a) / m; }
    friend istream& operator >> (istream &in, Mint &m) { int v; in >> v; m = Mint(v); return in; }
    friend ostream& operator << (ostream &out, const Mint &m) { out << m.v; return out; }
  };
  template <typename T> T add(T a, T b) { return a + b; }
  template <typename T> T max(T a, T b) { return a < b ? b : a; }
  template <typename T> T min(T a, T b) { return a < b ? a : b; }
  unordered_map<int, vector<Value*>> fact_mp, inv_mp, fact_inv_mp;
  template <const int P> void _build(int n);
  template <const int P> Mint<P> fact(int n) {
    assert(0 <= n); _build<P>(n);
    auto &_fact = fact_mp[P];
    return *dynamic_cast<Mint<P>*>(_fact[n]);
  }
  template <const int P> Mint<P> fact_inv(int n) {
    assert(0 <= n); _build<P>(n);
    auto &_fact_inv = fact_inv_mp[P];
    return *dynamic_cast<Mint<P>*>(_fact_inv[n]);
  }
  template <const int P> Mint<P> C(int n, int m) {
    assert(0 <= n && m >= 0 && m <= n); _build<P>(n);
    return fact<P>(n) * fact_inv<P>(m) * fact_inv<P>(n - m);
  }
  template <const int P> Mint<P> inv(int n) {
    assert(0 <= n); _build<P>(n);
    auto &_inv = inv_mp[P];
    return *dynamic_cast<Mint<P>*>(_inv[n]);
  }
  template <const int P> void _build(int n) {
    auto &_fact = fact_mp[P];
    auto &_inv = inv_mp[P];
    auto &_fact_inv = fact_inv_mp[P];
    while ((int)_fact.size() <= n) {
      if ((int)_fact.size() <= 1) {
        _fact.push_back(new Mint<P>(1));
        _inv.push_back(new Mint<P>(1));
        _fact_inv.push_back(new Mint<P>(1));
        continue;
      }
      int x = _fact.size();
      _fact.push_back(new Mint<P>(fact<P>(x - 1) * x));
      _inv.push_back(new Mint<P>(-inv<P>(P % x) * Mint<P>(P / x)));
      _fact_inv.push_back(new Mint<P>(fact_inv<P>(x - 1) * inv<P>(x)));
    }
  }
  template <const int P = MOD998, const int R = 3, const int MAXN = (1 << 23)> struct Poly : vector<Mint<P>> {
    int n;
    Poly (int _n) : n(_n) { this->resize(n); }
    void extend(int x) {
        int N = 1; while (N < x) N <<= 1;
        if (this->size() < N) this->resize(N);
        n = N;
    }
    Poly operator * (const Poly &rhs) {
      Poly a = *this, b = rhs;
      int N = a.size() + b.size();
      a.extend(N); a.ntt(1); b.extend(N); b.ntt(1);
      //cout << "go " << N << endl;
      for (int i = 0 ; i < N ; i++) a[i] *= b[i];
      return a.ntt(-1), a.n = N, a;
      // a.resize(this->size() + rhs.size()), a;
    }
    void ntt(int op) { static vector<int> r(MAXN);
      for (int i = 0 ; i < n ; i++) {
        r[i] = (i & 1) * (n >> 1) + (r[i >> 1] >> 1);
        if (r[i] < i) std::swap(this->at(i), this->at(r[i]));
      }
      for (int m = 2 ; m <= n ; m <<= 1) {
        int k = m >> 1; Mint<P> gn = Mint<P>(R).pow((P - 1) / m);
        for (int i = 0 ; i < n ; i += m) { Mint<P> g = 1;
          for (int j = 0 ; j < k ; j++, g *= gn) {
            Mint<P> tmp = this->at(i + j + k) * g;
            this->at(i + j + k) = this->at(i + j) - tmp;
            this->at(i + j) = this->at(i + j) + tmp;
          }
        }
      }
      if (op == -1) {
        reverse(this->begin() + 1, this->end());
        Mint<P> inv = Mint<P>(n).inv();
        for (int i = 0 ; i < n ; i++)
          this->at(i) *= inv;
      }
    }
    bool operator < (const Poly &rhs) const {
      return this->size() > rhs.size();
    }
  };
}
namespace IO {
}
namespace Graph {
}
namespace Geometry {
}
namespace String {
}

using namespace Math;
const int MOD = MOD998;
int main() {
    ios_base::sync_with_stdio(false); cin.tie(0);
    int n; cin >> n; n *= 2;
    map<int, int> cnt;
    for (int i = 0 ; i < n ; i++) {
        int v; cin >> v;
        cnt[v]++;
    }
    priority_queue<Poly<MOD998, 3, 1 << 23>> pq;
    for (auto &p : cnt) {
        int k = p.S / 2;
        Poly poly(k + 1);
        for (int i = 0 ; i <= k ; i++)
            poly[i] = C<MOD>(p.S, 2 * i) * fact<MOD>(2 * i) * fact_inv<MOD>(i) / Mint<MOD>(2).pow(i);
        pq.push(poly);
    }
    while (pq.size() > 1) {
        auto a = pq.top(); pq.pop();
        auto b = pq.top(); pq.pop();
        pq.push(a * b);
    }
    auto res = pq.top(); pq.pop();
    Mint<MOD998> ans = 0;
    for (int i = 0 ; i <= n / 2 ; i++) {
        assert(n / 2 < res.size());
        if (i % 2 == 0) {
            ans += res[i] * fact<MOD>(n - 2 * i) / Mint<MOD>(2).pow(n / 2 - i) * fact_inv<MOD>(n / 2 - i);
        } else {
            ans -= res[i] * fact<MOD>(n - 2 * i) / Mint<MOD>(2).pow(n / 2 - i) * fact_inv<MOD>(n / 2 - i);
        }
    }
    cout << ans << '\n';
}

