Problem p02539 (C++) — buggy submission, verdict: Runtime Error

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cmath>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
#define vll vector<ll>
#define vvvl vector<vvl>
#define vvl vector<vector<ll>>
#define VV(a, b, c, d) vector<vector<d>>(a, vector<d>(b, c))
#define VVV(a, b, c, d) vector<vvl>(a, vvl(b, vll(c, d)));
#define re(c, b) for (ll c = 0; c < b; c++)
#define all(obj) (obj).begin(), (obj).end()
typedef long long int ll;
typedef long double ld;
using namespace std;

inline ll mpow(ll a, ll b, ll p = -1) {
  ll ret = 1, num = a;
  while (b > 0) {
    if (b & 1)
      ret = (ret * num) % p;
    num = (num * num) % p;
    b /= 2;
  }
  return ret;
}

#include <random>
// modpow(x,2,mod) == aとなるxを返す
// 存在しないなら-1
// mod は素数
ll modsqrt(ll a, ll mod) {
  a %= mod;
  if (a == 0)
    return 0LL;
  if (mod == 2)
    return 1LL;
  if (mpow(a, (mod - 1) / 2, mod) != 1)
    return -1LL;
  if (mod % 4 == 3)
    return mpow(a, mod / 4 + 1, mod);
  ll q = mod - 1, m = 0;
  while (q % 2 == 0)
    q >>= 1, m++;
  mt19937 mt;
  ll z;
  do {
    z = mt() % mod;
  } while (mpow(z, (mod - 1) / 2, mod) != mod - 1);
  ll c = mpow(z, q, mod);
  ll t = mpow(a, q, mod);
  ll r = mpow(a, (q + 1) >> 1, mod);
  for (; m > 1; --m) {
    ll tmp = mpow(t, 1LL << (m - 2), mod);
    if (tmp != 1)
      r = r * c % mod, t = t * (c * c % mod) % mod;
    c = c * c % mod;
  }
  return r;
}

template <ll P, ll proot> struct NTT {
  ll g;
  vector<ll> primes;
  NTT(){};
  ll get_mod() { return P; }
  void ntt(vector<ll> &f, bool inv) {
    int n, N = f.size();
    g = mpow(proot, P / N, P);
    vector<ll> angles(N);
    angles[0] = 1;
    for (int i = 1; i < N; i++)
      angles[i] = (angles[i - 1] * g) % P;
    for (n = 0;; n++)
      if (N == (1 << n))
        break;
    for (int m = 0; m < N; m++) {
      int m2 = 0;
      for (int i = 0; i < n; i++)
        if (m & (1 << i))
          m2 |= (1 << (n - 1 - i));
      if (m < m2)
        swap(f[m], f[m2]);
    }

    for (int i = 0; i < N; i++)
      f[i] %= P;
    vector<ll> power(N / 2);
    power[0] = 1;
    for (int t = 1; t < N; t *= 2) {
      ll w = angles[N / (2 * t)];
      if (inv)
        w = angles[N - N / (2 * t)];
      for (int i = 1; i < t; i++)
        power[i] = (power[i - 1] * w) % P;
      for (int i = 0; i < N; i += 2 * t) {
        for (int j = 0; j < t; j++) {
          ll x = f[i + j], y = f[i + t + j];
          f[i + j] = (x + y * power[j]) % P;
          f[i + t + j] = x - (y * power[j]) % P;
          if (f[i + t + j] < 0)
            f[i + t + j] += P;
        }
      }
    }
    if (inv) {
      ll inv = mpow(N, P - 2, P);
      for (int i = 0; i < N; i++)
        f[i] = (f[i] * inv) % P;
    }
  }
  vector<ll> conv(vector<ll> A, vector<ll> B) {
    assert(A.size() == B.size());
    ll N = A.size();
    vector<ll> C(N);
    ntt(A, 0);
    ntt(B, 0);
    for (int i = 0; i < N; i++)
      C[i] = (A[i] * B[i]) % P;
    ntt(C, 1);
    return C;
  }
};

vector<ll> int32mod_conv(vector<ll> a, vector<ll> b, ll P) {
  if (P == 998244353)
    return NTT<998244353, 3>().conv(a, b);
  NTT<167772161, 3> ntt1;
  NTT<469762049, 3> ntt2;
  NTT<1224736769, 3> ntt3;
  vector<ll> A = ntt1.conv(a, b);
  vector<ll> B = ntt2.conv(a, b);
  vector<ll> C = ntt3.conv(a, b);
  ll N = A.size();
  vector<ll> ret(N);
  ll x = ntt1.get_mod();
  ll y = ntt2.get_mod();
  ll z = ntt3.get_mod();
  ll ix = mpow(x, y - 2, y);
  ll ixy = mpow((x * y) % z, z - 2, z);
  for (int i = 0; i < N; i++) {
    ll v = ((B[i] - A[i]) * ix) % y;
    if (v < 0)
      v += y;
    ll xxv = A[i] + x * v;
    v = ((C[i] - (xxv % z)) * ixy) % z;
    if (v < 0)
      v += z;
    ret[i] = ((xxv % P) + ((x * y) % P) * v) % P;
  }
  return ret;
}

template <ll P> struct FPS : vector<ll> {
  using vector<ll>::vector;

  static vector<ll> itable; // integral用の逆元テーブル

  FPS(vll a) {
    ll N = 1, n = a.size();
    while (N < n)
      N *= 2;
    this->resize(n);
    for (int i = 0; i < n; i++)
      (*this)[i] = a[i];
  }

  FPS operator+(const FPS &r) { return FPS(*this) += r; }
  FPS operator-(const FPS &r) { return FPS(*this) -= r; }
  FPS operator*(const FPS &r) { return FPS(*this) *= r; }
  FPS operator/(const FPS &r) { return FPS(*this) /= r; }
  FPS operator+(const ll &r) { return FPS(*this) += r; }
  FPS operator-(const ll &r) { return FPS(*this) -= r; }
  FPS operator*(const ll &r) { return FPS(*this) *= r; }
  FPS operator/(const ll &r) { return FPS(*this) /= r; }

  // 多項式との演算
  FPS operator+=(const FPS &r) {
    if (r.size() > this->size())
      this->resize(r.size());
    for (int i = 0; i < r.size(); i++) {
      (*this)[i] += r[i];
      if ((*this)[i] > P)
        (*this)[i] -= P;
    }
    return *this;
  }

  FPS operator-=(const FPS &r) {
    if (r.size() > this->size())
      this->resize(r.size());
    for (int i = 0; i < r.size(); i++) {
      (*this)[i] -= r[i];
      if ((*this)[i] < 0)
        (*this)[i] += P;
    }
    return *this;
  }
  FPS operator*=(FPS r) {
    ll n = max(this->size(), r.size()) * 2, N = 1;
    while (N < n)
      N *= 2;
    this->resize(N);
    r.resize(N);
    return (*this) = (int32mod_conv(*this, r, P));
  }
  FPS operator/=(FPS r) { return (*this) *= r.inv(); }

  // 定数との演算
  FPS operator+=(const ll &r) {
    if (this->empty())
      this->resize(1);
    (*this)[0] += r;
    if ((*this)[0] > P)
      (*this)[0] -= P;
    return *this;
  }
  FPS operator-=(const ll &r) {
    if (this->empty())
      this->resize(1);
    (*this)[0] -= r;
    if ((*this)[0] < 0)
      (*this)[0] += P;
    return *this;
  }
  FPS operator*=(const ll &r) {
    if (this->empty())
      this->resize(1);
    const ll n = this->size();
    for (int i = 0; i < n; i++)
      (*this)[i] = ((*this)[i] * r) % P;
    return *this;
  }

  FPS operator/=(const ll &r) {
    if (this->empty())
      this->resize(1);
    ll inv = mpow(r, P - 2, P);
    const ll n = this->size();
    for (int i = 0; i < n; i++)
      (*this)[i] = ((*this)[i] * inv) % P;
    return *this;
  }
  FPS operator>>(int sz) const {
    if (this->size() <= sz)
      return {};
    FPS ret = *this;
    ret.erase(ret.begin(), ret.begin() + sz);
    return ret;
  }
  FPS operator<<(int sz) const {
    FPS ret = *this;
    ret.insert(ret.begin(), sz, 0);
    return ret;
  }

  FPS prefix(int deg) {
    return FPS(this->begin(), this->begin() + min((int)this->size(), deg));
  }

  FPS inv(int deg = -1) {
    assert((*this)[0] != 0);
    const int n = this->size();
    if (deg == -1)
      deg = n;
    FPS ret({mpow((*this)[0], P - 2, P)});
    for (int i = 1; i < deg; i <<= 1)
      ret = (ret + ret - ret * ret * prefix(i << 1)).prefix(i << 1);
    return ret.prefix(deg);
  }

  FPS integral() {
    const int n = (int)this->size();
    FPS ret(n + 1);
    ret[0] = 0;
    ll inv[n + 2];
    inv[0] = inv[1] = 1;
    for (int i = 0; i < n; i++) {
      ret[i + 1] = ((*this)[i] * inv[i + 1]) % P;
      inv[i + 2] = ((-(P / (i + 2)) * inv[P % (i + 2)]) % P + P) % P;
    }
    return ret;
  }

  FPS diff() {
    const int n = (int)this->size();
    FPS ret(max(0, n - 1));
    for (int i = 1; i < n; i++)
      ret[i - 1] = ((*this)[i] * i) % P;
    return ret;
  }

  FPS log(int deg = -1) {
    assert((*this)[0] == 1);
    const int n = (int)this->size();
    if (deg == -1)
      deg = n;
    return (this->diff() * this->inv(deg)).prefix(deg - 1).integral();
  }

  FPS exp(int deg = -1) {
    assert((*this)[0] == 0);
    const int n = this->size();
    if (deg == -1)
      deg = n;
    FPS ret(vll{1});
    for (int i = 1; i < deg; i <<= 1) {
      ret = (ret * (prefix(i << 1) + 1LL - ret.log(i << 1))).prefix(i << 1);
    }
    return ret.prefix(deg);
  }
  void print(FPS a) {
    ll n = a.size();
    for (int i = 0; i < n - 1; i++)
      printf("%lld ", a[i]);
    printf("%lld\\n", a[n - 1]);
  }

  // 初めて0以外が現れるのが奇数次数or初めに現れる数が平方根でない場合不可能(size0のFPSを返す)
  FPS sqrt(int deg = -1) {
    const int n = this->size();
    if (deg == -1)
      deg = n;
    if ((*this)[0] == 0) {
      for (int i = 1; i < n; i++) {
        if ((*this)[i] != 0) {
          if (i & 1) {
            std::cout << -1 << '\\n';
            exit(0);
          }
          if (deg - i / 2 <= 0)
            break;
          FPS tmp = (*this >> i).sqrt(deg - i / 2);
          if (tmp.size() == 0)
            return {};
          FPS ret = tmp << (i / 2);
          if (ret.size() < deg)
            ret.resize(deg, 0);
          return ret;
        }
      }
      return FPS(deg, 0);
    }
    ll x = modsqrt((*this)[0], P);
    if (x == -1)
      return {};
    FPS ret({x});
    ll inv2 = mpow(2, P - 2, P);
    for (int i = 1; i < deg; i <<= 1)
      ret = (ret + this->prefix(i << 1) * ret.inv(i << 1)) * inv2;
    return ret.prefix(deg);
  }

  FPS pow(ll k, int deg = -1) const {
    const int n = (int)this->size();
    if (deg == -1)
      deg = n; // 桁数に注意
    for (ll i = 0; i < n; i++) {
      if ((*this)[i] != 0) {
        ll rev = mpow((*this)[i], P - 2, P);
        FPS C = (*this);
        C *= rev;
        FPS D(deg);
        for (ll j = i; j < n; j++)
          D[j - i] = C[j];
        D = (D.log(deg) * k).exp(deg) * mpow((*this)[i], k, P);
        FPS E(deg);
        if (i * k > deg)
          return E;
        auto S = i * k;
        for (ll j = 0; j + S < deg && j < D.size(); j++)
          E[j + S] = D[j];
        return E;
      }
    }
    return *this;
  }

  ll Nth_element(ll N, FPS p, FPS q) {
    if (N <= 20)
      return (p / q)[N];
    FPS tmp(q.size(), 0);
    for (int i = 0; i < q.size(); i++)
      tmp[i] = (i % 2 ? (P - q[i]) : q[i]);
    p *= tmp;
    q *= tmp;
    FPS p2(p.size() / 2);
    FPS q2(q.size() / 2);
    if (N % 2)
      for (int i = 1; i < p.size(); i += 2)
        p2[i / 2] = p[i];
    else
      for (int i = 0; i < p.size(); i += 2)
        p2[i / 2] = p[i];
    for (int i = 0; i < q.size(); i += 2)
      q2[i / 2] = q[i];
    return Nth_element(N / 2, p2, q2);
  }
};

#define Q 998244353
#define N_MAX 1000000
ll fac[N_MAX + 1], inv[N_MAX + 1], finv[N_MAX + 1];
ll comb(ll n, ll k) {
  if (n < 0 || k < 0 || n < k)
    return 0;
  return (((fac[n] * finv[n - k]) % Q) * finv[k]) % Q;
}
ll perm(ll n, ll k) {
  if (n < 0 || k < 0 || n < k)
    return 0;
  return (fac[n] * finv[n - k]) % Q;
}
void init() {
  fac[0] = finv[0] = fac[1] = finv[1] = inv[1] = 1;
  for (int i = 2; i <= N_MAX; i++) {
    fac[i] = (fac[i - 1] * i) % Q;
    inv[i] = ((-(Q / i) * inv[Q % i]) % Q + Q) % Q;
    finv[i] = (finv[i - 1] * inv[i]) % Q;
  }
}

typedef FPS<998244353> fps;
int main() {
  init();
  ll n;
  std::cin >> n;
  vll cnt(200000, 0);
  for (int i = 0; i < 2 * n; i++) {
    ll a;
    std::cin >> a;
    cnt[a]++;
  }

  priority_queue<pair<int, fps>, vector<pair<int, fps>>,
                 greater<pair<int, fps>>>
      que;

  auto binom = [&](ll l, ll r) {
    return (((fac[l] * finv[r]) % Q) * finv[l - r]) % Q;
  };

  vll dfc(n + 1, 1);
  for (ll i = 0; i < n; i++)
    dfc[i + 1] = (dfc[i] * (2 * i + 1)) % Q;

  // fps t{1};
  for (ll i = 0; i < 200000; i++) {
    if (cnt[i] == 0)
      continue;
    fps tmp(cnt[i] / 2 + 1);
    for (ll j = 0; j <= cnt[i] / 2; j++) {
      ll ret = (dfc[j] * binom(cnt[i], 2 * j)) % Q;
      tmp[j] = ret;
    }
    que.push({tmp.size(), tmp});
  }

  while (que.size() >= 2) {
    auto f = que.top().second;
    que.pop();
    auto g = que.top().second;
    que.pop();
    fps mg = f * g;
    que.emplace(mg.size(), mg);
  }

  fps ans = que.top().second;

  ll A = 0;
  for (ll i = 0; i < min(n + 1, (ll)ans.size()); i++) {
    ll x = dfc[n - i];
    if (i % 2) {
      A -= (ans[i] * x) % Q;
      if (A < 0)
        A += Q;
    } else {
      A = (A + (ans[i] * x) % Q) % Q;
    }
  }
  std::cout << A << '\\n';
}


Error: 0

Fixed submission:

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cmath>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
#define vll vector<ll>
#define vvvl vector<vvl>
#define vvl vector<vector<ll>>
#define VV(a, b, c, d) vector<vector<d>>(a, vector<d>(b, c))
#define VVV(a, b, c, d) vector<vvl>(a, vvl(b, vll(c, d)));
#define re(c, b) for (ll c = 0; c < b; c++)
#define all(obj) (obj).begin(), (obj).end()
typedef long long int ll;
typedef long double ld;
using namespace std;

inline ll mpow(ll a, ll b, ll p = -1) {
  ll ret = 1, num = a;
  while (b > 0) {
    if (b & 1)
      ret = (ret * num) % p;
    num = (num * num) % p;
    b /= 2;
  }
  return ret;
}

#include <random>
// modpow(x,2,mod) == aとなるxを返す
// 存在しないなら-1
// mod は素数
ll modsqrt(ll a, ll mod) {
  a %= mod;
  if (a == 0)
    return 0LL;
  if (mod == 2)
    return 1LL;
  if (mpow(a, (mod - 1) / 2, mod) != 1)
    return -1LL;
  if (mod % 4 == 3)
    return mpow(a, mod / 4 + 1, mod);
  ll q = mod - 1, m = 0;
  while (q % 2 == 0)
    q >>= 1, m++;
  mt19937 mt;
  ll z;
  do {
    z = mt() % mod;
  } while (mpow(z, (mod - 1) / 2, mod) != mod - 1);
  ll c = mpow(z, q, mod);
  ll t = mpow(a, q, mod);
  ll r = mpow(a, (q + 1) >> 1, mod);
  for (; m > 1; --m) {
    ll tmp = mpow(t, 1LL << (m - 2), mod);
    if (tmp != 1)
      r = r * c % mod, t = t * (c * c % mod) % mod;
    c = c * c % mod;
  }
  return r;
}

template <ll P, ll proot> struct NTT {
  ll g;
  vector<ll> primes;
  NTT(){};
  ll get_mod() { return P; }
  void ntt(vector<ll> &f, bool inv) {
    int n, N = f.size();
    g = mpow(proot, P / N, P);
    vector<ll> angles(N);
    angles[0] = 1;
    for (int i = 1; i < N; i++)
      angles[i] = (angles[i - 1] * g) % P;
    for (n = 0;; n++)
      if (N == (1 << n))
        break;
    for (int m = 0; m < N; m++) {
      int m2 = 0;
      for (int i = 0; i < n; i++)
        if (m & (1 << i))
          m2 |= (1 << (n - 1 - i));
      if (m < m2)
        swap(f[m], f[m2]);
    }

    for (int i = 0; i < N; i++)
      f[i] %= P;
    vector<ll> power(N / 2);
    power[0] = 1;
    for (int t = 1; t < N; t *= 2) {
      ll w = angles[N / (2 * t)];
      if (inv)
        w = angles[N - N / (2 * t)];
      for (int i = 1; i < t; i++)
        power[i] = (power[i - 1] * w) % P;
      for (int i = 0; i < N; i += 2 * t) {
        for (int j = 0; j < t; j++) {
          ll x = f[i + j], y = f[i + t + j];
          f[i + j] = (x + y * power[j]) % P;
          f[i + t + j] = x - (y * power[j]) % P;
          if (f[i + t + j] < 0)
            f[i + t + j] += P;
        }
      }
    }
    if (inv) {
      ll inv = mpow(N, P - 2, P);
      for (int i = 0; i < N; i++)
        f[i] = (f[i] * inv) % P;
    }
  }
  vector<ll> conv(vector<ll> A, vector<ll> B) {
    assert(A.size() == B.size());
    ll N = A.size();
    vector<ll> C(N);
    ntt(A, 0);
    ntt(B, 0);
    for (int i = 0; i < N; i++)
      C[i] = (A[i] * B[i]) % P;
    ntt(C, 1);
    return C;
  }
};

vector<ll> int32mod_conv(vector<ll> a, vector<ll> b, ll P) {
  if (P == 998244353)
    return NTT<998244353, 3>().conv(a, b);
  NTT<167772161, 3> ntt1;
  NTT<469762049, 3> ntt2;
  NTT<1224736769, 3> ntt3;
  vector<ll> A = ntt1.conv(a, b);
  vector<ll> B = ntt2.conv(a, b);
  vector<ll> C = ntt3.conv(a, b);
  ll N = A.size();
  vector<ll> ret(N);
  ll x = ntt1.get_mod();
  ll y = ntt2.get_mod();
  ll z = ntt3.get_mod();
  ll ix = mpow(x, y - 2, y);
  ll ixy = mpow((x * y) % z, z - 2, z);
  for (int i = 0; i < N; i++) {
    ll v = ((B[i] - A[i]) * ix) % y;
    if (v < 0)
      v += y;
    ll xxv = A[i] + x * v;
    v = ((C[i] - (xxv % z)) * ixy) % z;
    if (v < 0)
      v += z;
    ret[i] = ((xxv % P) + ((x * y) % P) * v) % P;
  }
  return ret;
}

template <ll P> struct FPS : vector<ll> {
  using vector<ll>::vector;

  static vector<ll> itable; // integral用の逆元テーブル

  FPS(vll a) {
    ll N = 1, n = a.size();
    while (N < n)
      N *= 2;
    this->resize(n);
    for (int i = 0; i < n; i++)
      (*this)[i] = a[i];
  }

  FPS operator+(const FPS &r) { return FPS(*this) += r; }
  FPS operator-(const FPS &r) { return FPS(*this) -= r; }
  FPS operator*(const FPS &r) { return FPS(*this) *= r; }
  FPS operator/(const FPS &r) { return FPS(*this) /= r; }
  FPS operator+(const ll &r) { return FPS(*this) += r; }
  FPS operator-(const ll &r) { return FPS(*this) -= r; }
  FPS operator*(const ll &r) { return FPS(*this) *= r; }
  FPS operator/(const ll &r) { return FPS(*this) /= r; }

  // 多項式との演算
  FPS operator+=(const FPS &r) {
    if (r.size() > this->size())
      this->resize(r.size());
    for (int i = 0; i < r.size(); i++) {
      (*this)[i] += r[i];
      if ((*this)[i] > P)
        (*this)[i] -= P;
    }
    return *this;
  }

  FPS operator-=(const FPS &r) {
    if (r.size() > this->size())
      this->resize(r.size());
    for (int i = 0; i < r.size(); i++) {
      (*this)[i] -= r[i];
      if ((*this)[i] < 0)
        (*this)[i] += P;
    }
    return *this;
  }
  FPS operator*=(FPS r) {
    ll n = max(this->size(), r.size()) * 2, N = 1;
    while (N < n)
      N *= 2;
    this->resize(N);
    r.resize(N);
    return (*this) = (int32mod_conv(*this, r, P));
  }
  FPS operator/=(FPS r) { return (*this) *= r.inv(); }

  // 定数との演算
  FPS operator+=(const ll &r) {
    if (this->empty())
      this->resize(1);
    (*this)[0] += r;
    if ((*this)[0] > P)
      (*this)[0] -= P;
    return *this;
  }
  FPS operator-=(const ll &r) {
    if (this->empty())
      this->resize(1);
    (*this)[0] -= r;
    if ((*this)[0] < 0)
      (*this)[0] += P;
    return *this;
  }
  FPS operator*=(const ll &r) {
    if (this->empty())
      this->resize(1);
    const ll n = this->size();
    for (int i = 0; i < n; i++)
      (*this)[i] = ((*this)[i] * r) % P;
    return *this;
  }

  FPS operator/=(const ll &r) {
    if (this->empty())
      this->resize(1);
    ll inv = mpow(r, P - 2, P);
    const ll n = this->size();
    for (int i = 0; i < n; i++)
      (*this)[i] = ((*this)[i] * inv) % P;
    return *this;
  }
  FPS operator>>(int sz) const {
    if (this->size() <= sz)
      return {};
    FPS ret = *this;
    ret.erase(ret.begin(), ret.begin() + sz);
    return ret;
  }
  FPS operator<<(int sz) const {
    FPS ret = *this;
    ret.insert(ret.begin(), sz, 0);
    return ret;
  }

  FPS prefix(int deg) {
    return FPS(this->begin(), this->begin() + min((int)this->size(), deg));
  }

  FPS inv(int deg = -1) {
    assert((*this)[0] != 0);
    const int n = this->size();
    if (deg == -1)
      deg = n;
    FPS ret({mpow((*this)[0], P - 2, P)});
    for (int i = 1; i < deg; i <<= 1)
      ret = (ret + ret - ret * ret * prefix(i << 1)).prefix(i << 1);
    return ret.prefix(deg);
  }

  FPS integral() {
    const int n = (int)this->size();
    FPS ret(n + 1);
    ret[0] = 0;
    ll inv[n + 2];
    inv[0] = inv[1] = 1;
    for (int i = 0; i < n; i++) {
      ret[i + 1] = ((*this)[i] * inv[i + 1]) % P;
      inv[i + 2] = ((-(P / (i + 2)) * inv[P % (i + 2)]) % P + P) % P;
    }
    return ret;
  }

  FPS diff() {
    const int n = (int)this->size();
    FPS ret(max(0, n - 1));
    for (int i = 1; i < n; i++)
      ret[i - 1] = ((*this)[i] * i) % P;
    return ret;
  }

  FPS log(int deg = -1) {
    assert((*this)[0] == 1);
    const int n = (int)this->size();
    if (deg == -1)
      deg = n;
    return (this->diff() * this->inv(deg)).prefix(deg - 1).integral();
  }

  FPS exp(int deg = -1) {
    assert((*this)[0] == 0);
    const int n = this->size();
    if (deg == -1)
      deg = n;
    FPS ret(vll{1});
    for (int i = 1; i < deg; i <<= 1) {
      ret = (ret * (prefix(i << 1) + 1LL - ret.log(i << 1))).prefix(i << 1);
    }
    return ret.prefix(deg);
  }
  void print(FPS a) {
    ll n = a.size();
    for (int i = 0; i < n - 1; i++)
      printf("%lld ", a[i]);
    printf("%lld\\n", a[n - 1]);
  }

  // 初めて0以外が現れるのが奇数次数or初めに現れる数が平方根でない場合不可能(size0のFPSを返す)
  FPS sqrt(int deg = -1) {
    const int n = this->size();
    if (deg == -1)
      deg = n;
    if ((*this)[0] == 0) {
      for (int i = 1; i < n; i++) {
        if ((*this)[i] != 0) {
          if (i & 1) {
            std::cout << -1 << '\\n';
            exit(0);
          }
          if (deg - i / 2 <= 0)
            break;
          FPS tmp = (*this >> i).sqrt(deg - i / 2);
          if (tmp.size() == 0)
            return {};
          FPS ret = tmp << (i / 2);
          if (ret.size() < deg)
            ret.resize(deg, 0);
          return ret;
        }
      }
      return FPS(deg, 0);
    }
    ll x = modsqrt((*this)[0], P);
    if (x == -1)
      return {};
    FPS ret({x});
    ll inv2 = mpow(2, P - 2, P);
    for (int i = 1; i < deg; i <<= 1)
      ret = (ret + this->prefix(i << 1) * ret.inv(i << 1)) * inv2;
    return ret.prefix(deg);
  }

  FPS pow(ll k, int deg = -1) const {
    const int n = (int)this->size();
    if (deg == -1)
      deg = n; // 桁数に注意
    for (ll i = 0; i < n; i++) {
      if ((*this)[i] != 0) {
        ll rev = mpow((*this)[i], P - 2, P);
        FPS C = (*this);
        C *= rev;
        FPS D(deg);
        for (ll j = i; j < n; j++)
          D[j - i] = C[j];
        D = (D.log(deg) * k).exp(deg) * mpow((*this)[i], k, P);
        FPS E(deg);
        if (i * k > deg)
          return E;
        auto S = i * k;
        for (ll j = 0; j + S < deg && j < D.size(); j++)
          E[j + S] = D[j];
        return E;
      }
    }
    return *this;
  }

  ll Nth_element(ll N, FPS p, FPS q) {
    if (N <= 20)
      return (p / q)[N];
    FPS tmp(q.size(), 0);
    for (int i = 0; i < q.size(); i++)
      tmp[i] = (i % 2 ? (P - q[i]) : q[i]);
    p *= tmp;
    q *= tmp;
    FPS p2(p.size() / 2);
    FPS q2(q.size() / 2);
    if (N % 2)
      for (int i = 1; i < p.size(); i += 2)
        p2[i / 2] = p[i];
    else
      for (int i = 0; i < p.size(); i += 2)
        p2[i / 2] = p[i];
    for (int i = 0; i < q.size(); i += 2)
      q2[i / 2] = q[i];
    return Nth_element(N / 2, p2, q2);
  }
};

#define Q 998244353
#define N_MAX 1000000
ll fac[N_MAX + 1], inv[N_MAX + 1], finv[N_MAX + 1];
ll comb(ll n, ll k) {
  if (n < 0 || k < 0 || n < k)
    return 0;
  return (((fac[n] * finv[n - k]) % Q) * finv[k]) % Q;
}
ll perm(ll n, ll k) {
  if (n < 0 || k < 0 || n < k)
    return 0;
  return (fac[n] * finv[n - k]) % Q;
}
void init() {
  fac[0] = finv[0] = fac[1] = finv[1] = inv[1] = 1;
  for (int i = 2; i <= N_MAX; i++) {
    fac[i] = (fac[i - 1] * i) % Q;
    inv[i] = ((-(Q / i) * inv[Q % i]) % Q + Q) % Q;
    finv[i] = (finv[i - 1] * inv[i]) % Q;
  }
}

typedef FPS<998244353> fps;
int main() {
  init();
  ll n;
  std::cin >> n;
  vll cnt(200000, 0);
  for (int i = 0; i < 2 * n; i++) {
    ll a;
    scanf("%lld", &a);
    cnt[a]++;
  }

  priority_queue<pair<int, fps>, vector<pair<int, fps>>,
                 greater<pair<int, fps>>>
      que;

  auto binom = [&](ll l, ll r) {
    return (((fac[l] * finv[r]) % Q) * finv[l - r]) % Q;
  };

  vll dfc(n + 1, 1);
  for (ll i = 0; i < n; i++)
    dfc[i + 1] = (dfc[i] * (2 * i + 1)) % Q;

  // fps t{1};
  for (ll i = 0; i < 200000; i++) {
    if (cnt[i] == 0)
      continue;
    fps tmp(cnt[i] / 2 + 1);
    for (ll j = 0; j <= cnt[i] / 2; j++) {
      ll ret = (dfc[0] * binom(cnt[i], 2 * j)) % Q;
      tmp[j] = ret;
    }
    que.push({tmp.size(), tmp});
  }

  while (que.size() >= 2) {
    auto f = que.top().second;
    que.pop();
    auto g = que.top().second;
    que.pop();
    fps mg = f * g;
    que.emplace(mg.size(), mg);
  }

  fps ans = que.top().second;

  ll A = 0;
  for (ll i = 0; i < ans.size(); i++) {
    ll x = dfc[n - i];
    if (i % 2) {
      A -= (ans[i] * x) % Q;
      if (A < 0)
        A += Q;
    } else {
      A = (A + (ans[i] * x) % Q) % Q;
    }
  }
  std::cout << A << '\\n';
}