Problem p02539 (C++)

Buggy submission (status: Time Limit Exceeded):
#include <functional>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

const int MOD = 998244353;

long long modPow(long long a, long long p) {
  if (p == 0)
    return 1;
  auto res = modPow(a, p / 2);
  res = (res * res) % MOD;
  if (p % 2)
    res = (res * a) % MOD;
  return res;
}

long long calcInv(long long a) { return modPow(a, MOD - 2); }

void ntt(vector<long long> &v, int inv) {
  const int n = v.size();
  auto w = modPow(3, (MOD - 1) / n);
  if (inv == -1)
    w = calcInv(w);
  int rev = 0;
  for (int i = 1; i < n - 1; i++) {
    for (int j = n / 2; j > (rev ^= j); j /= 2)
      ;
    if (i < rev)
      swap(v[i], v[rev]);
  }
  for (int m = 1; m < n; m *= 2) {
    auto cur = 1;
    auto rot = modPow(w, n / (2 * m));
    for (int i = 0; i < m; i++) {
      for (int j = i; j < n; j += 2 * m) {
        auto p = v[j];
        auto q = v[j + m] * cur % MOD;
        v[j] = (p + q) % MOD;
        v[j + m] = (p + MOD - q) % MOD;
      }
      cur = (cur * rot) % MOD;
    }
  }
}

vector<long long> convolution(const vector<long long> &a,
                              const vector<long long> &b) {
  int _n = a.size() + b.size();
  int n = 1;
  while (n < _n)
    n *= 2;
  vector<long long> na(n, 0), nb(n, 0);
  for (int i = 0; i < a.size(); i++)
    na[i] = a[i];
  for (int i = 0; i < b.size(); i++)
    nb[i] = b[i];
  ntt(na, 1);
  ntt(nb, 1);
  for (int i = 0; i < n; i++)
    na[i] = na[i] * nb[i] % MOD;
  ntt(na, -1);
  auto inv = calcInv(n);
  for (int i = 0; i < n; i++)
    na[i] = na[i] * inv % MOD;
  na.resize(a.size() + b.size() - 1);
  return na;
}

bool op(const vector<long long> &a, const vector<long long> &b) {
  return a.size() < b.size();
}

int main() {
  const int maxSize = 100001;
  vector<long long> inv(maxSize);
  vector<long long> fact(maxSize);
  vector<long long> factInv(maxSize);
  for (int i = 0; i < 2; i++)
    inv[i] = fact[i] = factInv[i] = 1;
  for (int i = 2; i < maxSize; i++) {
    inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
    fact[i] = fact[i - 1] * i % MOD;
    factInv[i] = factInv[i - 1] * inv[i] % MOD;
  }
  auto comb = [&](int n, int r) {
    if (n < r || r < 0)
      return 0LL;
    return fact[n] * factInv[n - r] % MOD * factInv[r] % MOD;
  };
  auto perm = [&](int n, int r) {
    if (n < r || r < 0)
      return 0LL;
    return fact[n] * factInv[n - r] % MOD;
  };
  vector<long long> inv2pow(maxSize);
  inv2pow[0] = 1;
  for (int i = 1; i < maxSize; i++)
    inv2pow[i] = inv2pow[i - 1] * inv[2] % MOD;
  int N;
  cin >> N;
  vector<int> cnt(100001, 0);
  for (int i = 0; i < 2 * N; i++) {
    int h;
    cin >> h;
    cnt[h]++;
  }
  vector<vector<long long>> p(100001);
  priority_queue<int, vector<int>, function<bool(int, int)>> qu(
      [&](int a, int b) { return p[a].size() < p[b].size(); });
  p[0].push_back(1);
  qu.push(0);
  for (int i = 0; i < cnt.size(); i++) {
    int t = cnt[i];
    if (t < 2)
      continue;
    vector<long long> v(t / 2 + 1);
    v[0] = 1;
    for (int i = 1; i < v.size(); i++) {
      v[i] = perm(t, 2 * i) * inv2pow[i] % MOD * factInv[i] % MOD;
    }
    p[i] = v;
    qu.emplace(i);
  }
  while (qu.size() >= 2) {
    auto a = qu.top();
    qu.pop();
    auto b = qu.top();
    qu.pop();
    p[a] = convolution(p[a], p[b]);
    qu.emplace(a);
  }
  long long res = 0;
  auto &v = p[qu.top()];
  for (int i = 0; i < v.size(); i++) {
    long long rest = N - i;
    long long cur = perm(2 * rest, rest) * inv2pow[rest] % MOD;
    if (i % 2) {
      res = (res + MOD - cur * v[i] % MOD) % MOD;
    } else {
      res = (res + cur * v[i]) % MOD;
    }
  }
  cout << res << endl;
}

Fixed submission:
#include <functional>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

const int MOD = 998244353;

long long modPow(long long a, long long p) {
  if (p == 0)
    return 1;
  auto res = modPow(a, p / 2);
  res = (res * res) % MOD;
  if (p % 2)
    res = (res * a) % MOD;
  return res;
}

long long calcInv(long long a) { return modPow(a, MOD - 2); }

void ntt(vector<long long> &v, int inv) {
  const int n = v.size();
  auto w = modPow(3, (MOD - 1) / n);
  if (inv == -1)
    w = calcInv(w);
  int rev = 0;
  for (int i = 1; i < n - 1; i++) {
    for (int j = n / 2; j > (rev ^= j); j /= 2)
      ;
    if (i < rev)
      swap(v[i], v[rev]);
  }
  for (int m = 1; m < n; m *= 2) {
    auto cur = 1;
    auto rot = modPow(w, n / (2 * m));
    for (int i = 0; i < m; i++) {
      for (int j = i; j < n; j += 2 * m) {
        auto p = v[j];
        auto q = v[j + m] * cur % MOD;
        v[j] = (p + q) % MOD;
        v[j + m] = (p + MOD - q) % MOD;
      }
      cur = (cur * rot) % MOD;
    }
  }
}

vector<long long> convolution(const vector<long long> &a,
                              const vector<long long> &b) {
  int _n = a.size() + b.size();
  int n = 1;
  while (n < _n)
    n *= 2;
  vector<long long> na(n, 0), nb(n, 0);
  for (int i = 0; i < a.size(); i++)
    na[i] = a[i];
  for (int i = 0; i < b.size(); i++)
    nb[i] = b[i];
  ntt(na, 1);
  ntt(nb, 1);
  for (int i = 0; i < n; i++)
    na[i] = na[i] * nb[i] % MOD;
  ntt(na, -1);
  auto inv = calcInv(n);
  for (int i = 0; i < n; i++)
    na[i] = na[i] * inv % MOD;
  na.resize(a.size() + b.size() - 1);
  return na;
}

bool op(const vector<long long> &a, const vector<long long> &b) {
  return a.size() < b.size();
}

int main() {
  const int maxSize = 100001;
  vector<long long> inv(maxSize);
  vector<long long> fact(maxSize);
  vector<long long> factInv(maxSize);
  for (int i = 0; i < 2; i++)
    inv[i] = fact[i] = factInv[i] = 1;
  for (int i = 2; i < maxSize; i++) {
    inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
    fact[i] = fact[i - 1] * i % MOD;
    factInv[i] = factInv[i - 1] * inv[i] % MOD;
  }
  auto comb = [&](int n, int r) {
    if (n < r || r < 0)
      return 0LL;
    return fact[n] * factInv[n - r] % MOD * factInv[r] % MOD;
  };
  auto perm = [&](int n, int r) {
    if (n < r || r < 0)
      return 0LL;
    return fact[n] * factInv[n - r] % MOD;
  };
  vector<long long> inv2pow(maxSize);
  inv2pow[0] = 1;
  for (int i = 1; i < maxSize; i++)
    inv2pow[i] = inv2pow[i - 1] * inv[2] % MOD;
  int N;
  cin >> N;
  vector<int> cnt(100001, 0);
  for (int i = 0; i < 2 * N; i++) {
    int h;
    cin >> h;
    cnt[h]++;
  }
  vector<vector<long long>> p(100001);
  priority_queue<int, vector<int>, function<bool(int, int)>> qu(
      [&](int a, int b) { return p[a].size() > p[b].size(); });
  p[0].push_back(1);
  qu.push(0);
  for (int i = 0; i < cnt.size(); i++) {
    int t = cnt[i];
    if (t < 2)
      continue;
    vector<long long> v(t / 2 + 1);
    v[0] = 1;
    for (int i = 1; i < v.size(); i++) {
      v[i] = perm(t, 2 * i) * inv2pow[i] % MOD * factInv[i] % MOD;
    }
    p[i] = v;
    qu.emplace(i);
  }
  while (qu.size() >= 2) {
    auto a = qu.top();
    qu.pop();
    auto b = qu.top();
    qu.pop();
    p[a] = convolution(p[a], p[b]);
    qu.emplace(a);
  }
  long long res = 0;
  auto &v = p[qu.top()];
  for (int i = 0; i < v.size(); i++) {
    long long rest = N - i;
    long long cur = perm(2 * rest, rest) * inv2pow[rest] % MOD;
    if (i % 2) {
      res = (res + MOD - cur * v[i] % MOD) % MOD;
    } else {
      res = (res + cur * v[i]) % MOD;
    }
  }
  cout << res << endl;
}