Problem p02539 (C++)

Buggy submission (status: Time Limit Exceeded):
#pragma GCC optimize("Ofast", "unroll-loops")

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ld long double

template <ll mod = 998244353LL, ll root = 3LL>
void ntt(vector<ll> &a, bool rev = false) {
  auto modpow = [](ll p, int q) {
    ll res = 1LL;
    while (q) {
      if (q & 1)
        res = res * p % mod;
      p = p * p % mod;
      q >>= 1;
    }
    return res;
  };
  const int sz = a.size();
  if (sz == 1)
    return;
  vector<ll> b(sz);
  int r = rev ? (mod - 1 - (mod - 1) / sz) : (mod - 1) / sz;
  ll s = modpow(root, r);
  vector<ll> kp(sz / 2 + 1, 1);
  for (int i = 0; i < sz / 2; ++i)
    kp[i + 1] = kp[i] * s % mod;
  for (int i = 1, l = sz / 2; i < sz; i <<= 1, l >>= 1) {
    for (int j = 0, r = 0; j < l; ++j, r += i) {
      for (int k = 0, s = kp[i * j]; k < i; ++k) {
        ll p = a[k + r], q = a[k + r + sz / 2];
        b[k + 2 * r] = (p + q) % mod;
        b[k + 2 * r + i] = ((p - q) % mod + mod) % mod * s % mod;
      }
    }
    swap(a, b);
  }
  if (rev) {
    s = modpow(sz, mod - 2);
    for (int i = 0; i < sz; i++) {
      a[i] = a[i] * s % mod;
    }
  }
}

template <ll mod = 998244353LL, ll root = 3LL>
vector<ll> convolute(vector<ll> a, vector<ll> b) {
  const int sz = (int)a.size() + (int)b.size() - 1;
  int t = 1;
  while (t < sz)
    t *= 2;
  a.resize(t, 0);
  b.resize(t, 0);
  ntt<mod, root>(a);
  ntt<mod, root>(b);
  for (int i = 0; i < t; ++i)
    a[i] = a[i] * b[i] % mod;
  ntt<mod, root>(a, true);
  a.resize(sz);
  return a;
}

constexpr ll mod = 998244353LL;

ll fact(ll k, int sgn = 0) {
  static vector<ll> fac(2, 1LL);
  static vector<ll> inv(2, 1LL);
  static vector<ll> finv(2, 1LL);
  static ll nx = 2LL;
  while (nx <= k) {
    fac.push_back(fac[nx - 1] * nx % mod);
    inv.push_back(mod - inv[mod % nx] * (mod / nx) % mod);
    finv.push_back(finv[nx - 1] * inv[nx] % mod);
    ++nx;
  }
  if (sgn == 0)
    return fac[k];
  return finv[k];
}

ll comb(ll a, ll b) {
  if (b < 0 || b > a)
    return 0;
  return fact(a) * fact(b, 1) % mod * fact(a - b, 1) % mod;
}

ll calc_A(int n) {
  static vector<ll> A(2, 1);
  int i = A.size();
  while (i <= n) {
    ll Ai = A[i - 1] * (2 * i - 1) % mod;
    A.push_back(Ai);
    ++i;
  }
  return A[n];
}

int N;
map<int, int> cnt;

void input(void) {
  cin >> N;
  for (int i = 0; i < 2 * N; ++i) {
    int hi;
    cin >> hi;
    ++cnt[hi];
  }
}

using func = vector<ll>;

void print(func f) {
  for (auto &fi : f)
    cout << fi << " ";
  cout << endl;
}

func merge(void) {
  vector<func> fs;
  for (auto &e : cnt) {
    int c = e.second;
    func tmp(c / 2 + 1);
    for (int p = 0; p <= c / 2; ++p) {
      tmp[p] = (p & 1 ? (-1LL) : (1LL)) * comb(c, 2 * p) * calc_A(p) % mod;
      if (tmp[p] < 0)
        tmp[p] += mod;
    }
    fs.push_back(tmp);
  }
  sort(fs.begin(), fs.end(),
       [](func a, func b) { return a.size() > b.size(); });
  func ret = fs[0];
  for (int i = 1; i < fs.size(); ++i)
    ret = convolute<>(ret, fs[i]);
  return ret;
}

ll solve(void) {
  func f1 = merge();
  func f2(N + 1);
  for (int i = 0; i <= N; ++i)
    f2[i] = calc_A(i);
  func f = convolute<>(f1, f2);
  return f[N];
}

int main(void) {
  input();
  cout << solve() << endl;
  return 0;
}

Fixed submission:
#pragma GCC optimize("Ofast", "unroll-loops")

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ld long double

template <ll mod = 998244353LL, ll root = 3LL>
void ntt(vector<ll> &a, bool rev = false) {
  auto modpow = [](ll p, int q) {
    ll res = 1LL;
    while (q) {
      if (q & 1)
        res = res * p % mod;
      p = p * p % mod;
      q >>= 1;
    }
    return res;
  };
  const int sz = a.size();
  if (sz == 1)
    return;
  vector<ll> b(sz);
  int r = rev ? (mod - 1 - (mod - 1) / sz) : (mod - 1) / sz;
  ll s = modpow(root, r);
  vector<ll> kp(sz / 2 + 1, 1);
  for (int i = 0; i < sz / 2; ++i)
    kp[i + 1] = kp[i] * s % mod;
  for (int i = 1, l = sz / 2; i < sz; i <<= 1, l >>= 1) {
    for (int j = 0, r = 0; j < l; ++j, r += i) {
      for (int k = 0, s = kp[i * j]; k < i; ++k) {
        ll p = a[k + r], q = a[k + r + sz / 2];
        b[k + 2 * r] = (p + q) % mod;
        b[k + 2 * r + i] = ((p - q) % mod + mod) % mod * s % mod;
      }
    }
    swap(a, b);
  }
  if (rev) {
    s = modpow(sz, mod - 2);
    for (int i = 0; i < sz; i++) {
      a[i] = a[i] * s % mod;
    }
  }
}

template <ll mod = 998244353LL, ll root = 3LL>
vector<ll> convolute(vector<ll> a, vector<ll> b) {
  const int sz = (int)a.size() + (int)b.size() - 1;
  int t = 1;
  while (t < sz)
    t *= 2;
  a.resize(t, 0);
  b.resize(t, 0);
  ntt<mod, root>(a);
  ntt<mod, root>(b);
  for (int i = 0; i < t; ++i)
    a[i] = a[i] * b[i] % mod;
  ntt<mod, root>(a, true);
  a.resize(sz);
  return a;
}

constexpr ll mod = 998244353LL;

ll fact(ll k, int sgn = 0) {
  static vector<ll> fac(2, 1LL);
  static vector<ll> inv(2, 1LL);
  static vector<ll> finv(2, 1LL);
  static ll nx = 2LL;
  while (nx <= k) {
    fac.push_back(fac[nx - 1] * nx % mod);
    inv.push_back(mod - inv[mod % nx] * (mod / nx) % mod);
    finv.push_back(finv[nx - 1] * inv[nx] % mod);
    ++nx;
  }
  if (sgn == 0)
    return fac[k];
  return finv[k];
}

ll comb(ll a, ll b) {
  if (b < 0 || b > a)
    return 0;
  return fact(a) * fact(b, 1) % mod * fact(a - b, 1) % mod;
}

ll calc_A(int n) {
  static vector<ll> A(2, 1);
  int i = A.size();
  while (i <= n) {
    ll Ai = A[i - 1] * (2 * i - 1) % mod;
    A.push_back(Ai);
    ++i;
  }
  return A[n];
}

int N;
map<int, int> cnt;

void input(void) {
  cin >> N;
  for (int i = 0; i < 2 * N; ++i) {
    int hi;
    cin >> hi;
    ++cnt[hi];
  }
}

using func = vector<ll>;

void print(func f) {
  for (auto &fi : f)
    cout << fi << " ";
  cout << endl;
}

func merge(void) {
  vector<func> fs;
  for (auto &e : cnt) {
    int c = e.second;
    func tmp(c / 2 + 1);
    for (int p = 0; p <= c / 2; ++p) {
      tmp[p] = (p & 1 ? (-1LL) : (1LL)) * comb(c, 2 * p) * calc_A(p) % mod;
      if (tmp[p] < 0)
        tmp[p] += mod;
    }
    fs.push_back(tmp);
  }
  sort(fs.begin(), fs.end(),
       [](func a, func b) { return a.size() > b.size(); });
  vector<func> new_func;
  while (fs.size() > 1) {
    new_func = vector<func>((fs.size() + 1) / 2);
    for (int i = 0; i < fs.size(); i += 2) {
      new_func[i / 2] =
          (i + 1 == fs.size()) ? fs[i] : convolute<>(fs[i], fs[i + 1]);
    }
    fs = new_func;
  }
  return fs[0];
}

ll solve(void) {
  func f1 = merge();
  func f2(N + 1);
  for (int i = 0; i <= N; ++i)
    f2[i] = calc_A(i);
  func f = convolute<>(f1, f2);
  return f[N];
}

int main(void) {
  input();
  cout << solve() << endl;
  return 0;
}