Problem p02563 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
#define REP(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
#define FOR(i, a, b) for (int i = (a), i##_len = (b); i < i##_len; i++)
#define ALL(x) (x).begin(), (x).end() // sortなどの引数を省略したい
#ifdef _DEBUG
#define PRE_COMMAND                                                            \\
  std::cin.rdbuf(in.rdbuf());                                                  \\
  std::cout << fixed << setprecision(15);
#else
#define PRE_COMMAND cout << fixed << setprecision(15);
#endif
const double EPS = 1e-10, PI = acos(-1.0);
template <class T> auto MAX(T &seq) {
  return *max_element(seq.begin(), seq.end());
}
template <class T> auto MIN(T &seq) {
  return *min_element(seq.begin(), seq.end());
}
template <class T> auto SUM(T &seq) {
  T temp{0};
  auto &temp2 = temp[0];
  return accumulate(seq.begin(), seq.end(), temp2);
}
template <class T> void SORT(T &seq) { sort(seq.begin(), seq.end()); }
template <class T, class S> void SORT(T &seq, S &sort_order) {
  sort(seq.begin(), seq.end(), sort_order);
}
template <class T> void SORTR(vector<T> &seq) {
  sort(seq.begin(), seq.end(), greater<T>());
}
template <class T, class S, class R> long long pow(T n_0, S k_0, R mod_0) {
  long long n = n_0 % mod_0, k = k_0, mod = mod_0, now = 1;
  while (k) {
    if (k & 1)
      now = (now * n) % mod;
    n = (n * n) % mod;
    k >>= 1;
  }
  return now;
}
template <typename T> istream &operator>>(istream &is, vector<T> &vec) {
  for (T &x : vec)
    is >> x;
  return is;
}
struct ntt {
  using ll = long long;
  using vl = vector<ll>;
  array<vl, 2> exp_list{(vl)(1), (vl)(1)};
  vl result;
  int s, h, n;
  ll r = 3, mod, ninv;
  ntt(vl &x_pre, vl &y_pre, ll mod_pre) {
    s = (int)(x_pre.size() + y_pre.size()) - 1, h = 1, n = 2, mod = mod_pre;
    vl x = x_pre, y = y_pre;
    for (; n < s; n *= 2, h++) {
    }
    ninv = pow(n, mod - 2, mod);
    x.resize(n, 0);
    y.resize(n, 0);
    convolve(x, y);
  }
  // 高速フーリエ変換を行う、inv=trueで逆変換
  void ntt_calc(vl &x, bool inv = false) {
    ll now = 1;
    // 処理前の並び替え
    for (int i = 0; i < n; i++) {
      int j = 0;
      for (int k = 0; k < h; k++) {
        j |= ((i >> k) & 1) << (h - 1 - k);
      }
      if (i < j) {
        swap(x[i], x[j]);
      }
    }
    ll e = pow(r, (mod - 1) / n, mod);
    if (inv)
      e = pow(e, mod - 2, mod);
    if ((int)exp_list[inv].size() != n) {
      exp_list[inv].assign(n, 0);
      for (int i = 0; i < n; i++, now = now * e % mod) {
        exp_list[inv][i] = now;
      }
    }
    // 各ステップについて処理
    for (int b = 1; b < n; b *= 2) {
      // 各ブロックのj個目
      int m = n / b;
      const int m2 = 2 * b;
      ll w, u, t;
      for (int j = 0; j < b; j++) {
        w = exp_list[inv][j * m >> 1];
        // kを先頭とするブロックに対する処理
        for (int k = j; k < n; k += m2) {
          u = x[k], t = x[k + b] * w % mod;
          x[k] = u + t;
          if (x[k] >= mod)
            x[k] -= mod;
          x[k + b] = u - t;
          if (x[k + b] < 0)
            x[k + b] += mod;
        }
      }
    }
    if (inv) {
      for (int i = 0; i < n; i++) {
        x[i] = x[i] * ninv % mod;
      }
    }
  }
  // aとbの畳み込みを計算
  void convolve(vl &x, vl &y) {
    ntt_calc(x);
    ntt_calc(y);
    for (int i = 0; i < (int)x.size(); i++) {
      x[i] = x[i] * y[i] % mod;
    }
    ntt_calc(x, true);
    x.resize(s);
    result = x;
  }
};

// 負の数にも対応した mod (a = -11 とかでも OK)
inline long long mod(long long a, long long m) {
  long long res = a % m;
  if (res < 0)
    res += m;
  return res;
}

// 拡張 Euclid の互除法
long long extGCD(long long a, long long b, long long &p, long long &q) {
  if (b == 0) {
    p = 1;
    q = 0;
    return a;
  }
  long long d = extGCD(b, a % b, q, p);
  q -= a / b * p;
  return d;
}

// 逆元計算 (ここでは a と m が互いに素であることが必要)
long long modinv(long long a, long long m) {
  long long x, y;
  extGCD(a, m, x, y);
  return mod(x, m); // 気持ち的には x % m だが、x が負かもしれないので
}

// Garner のアルゴリズム, x%MOD, LCM%MOD を求める (m
// は互いに素でなければならない) for each step, we solve "coeffs[k] * t[k] +
// constants[k] = b[k] (mod. m[k])"
//      coeffs[k] = m[0]m[1]...m[k-1]
//      constants[k] = t[0] + t[1]m[0] + ... + t[k-1]m[0]m[1]...m[k-2]
long long Garner(vector<long long> b, vector<long long> m, long long MOD) {
  m.push_back(MOD); // banpei
  vector<long long> coeffs((int)m.size(), 1);
  vector<long long> constants((int)m.size(), 0);
  for (int k = 0; k < (int)b.size(); ++k) {
    long long t = mod((b[k] - constants[k]) * modinv(coeffs[k], m[k]), m[k]);
    for (int i = k + 1; i < (int)m.size(); ++i) {
      (constants[i] += t * coeffs[i]) %= m[i];
      (coeffs[i] *= m[k]) %= m[i];
    }
  }
  return constants.back();
}

vector<long long> calc_ntt(vector<long long> x, vector<long long> y,
                           long long mod) {
  ntt ntt1(x, y, 167772161);
  ntt ntt2(x, y, 469762049);
  ntt ntt3(x, y, 1224736769);
  vector<long long> r1 = ntt1.result, r2 = ntt2.result, r3 = ntt3.result;
  int n = (int)r1.size();
  vector<long long> ret(n), mod_v = {167772161, 469762049, 1224736769};
  for (int i = 0; i < n; i++) {
    ret[i] = Garner({r1[i], r2[i], r3[i]}, mod_v, mod);
  }
  return ret;
}

int main() {
  PRE_COMMAND
  int n, m;
  cin >> n >> m;
  vector<ll> a(n), b(m);
  cin >> a >> b;
  vector<ll> ans = calc_ntt(a, b, 998244353);
  n = (int)ans.size();
  for (int i = 0; i < n; i++) {
    cout << ans[i];
    if (i != n - 1) {
      cout << " ";
    } else {
      cout << endl;
    }
  }
}

Error: TLE

Fixed submission:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
#define REP(i, n) for (int i = 0, i##_len = (n); i < i##_len; ++i)
#define FOR(i, a, b) for (int i = (a), i##_len = (b); i < i##_len; i++)
#define ALL(x) (x).begin(), (x).end() // sortなどの引数を省略したい
#ifdef _DEBUG
#define PRE_COMMAND                                                            \\
  std::cin.rdbuf(in.rdbuf());                                                  \\
  std::cout << fixed << setprecision(15);
#else
#define PRE_COMMAND cout << fixed << setprecision(15);
#endif
const double EPS = 1e-10, PI = acos(-1.0);
template <class T> auto MAX(T &seq) {
  return *max_element(seq.begin(), seq.end());
}
template <class T> auto MIN(T &seq) {
  return *min_element(seq.begin(), seq.end());
}
template <class T> auto SUM(T &seq) {
  T temp{0};
  auto &temp2 = temp[0];
  return accumulate(seq.begin(), seq.end(), temp2);
}
template <class T> void SORT(T &seq) { sort(seq.begin(), seq.end()); }
template <class T, class S> void SORT(T &seq, S &sort_order) {
  sort(seq.begin(), seq.end(), sort_order);
}
template <class T> void SORTR(vector<T> &seq) {
  sort(seq.begin(), seq.end(), greater<T>());
}
template <class T, class S, class R> long long pow(T n_0, S k_0, R mod_0) {
  long long n = n_0 % mod_0, k = k_0, mod = mod_0, now = 1;
  while (k) {
    if (k & 1)
      now = (now * n) % mod;
    n = (n * n) % mod;
    k >>= 1;
  }
  return now;
}
template <typename T> istream &operator>>(istream &is, vector<T> &vec) {
  for (T &x : vec)
    is >> x;
  return is;
}
struct ntt {
  using ll = long long;
  using vl = vector<ll>;
  array<vl, 2> exp_list{(vl)(1), (vl)(1)};
  vl result;
  int s, h, n;
  ll r = 3, mod, ninv;
  ntt(vl &x_pre, vl &y_pre, ll mod_pre) {
    s = (int)(x_pre.size() + y_pre.size()) - 1, h = 1, n = 2, mod = mod_pre;
    vl x = x_pre, y = y_pre;
    for (; n < s; n *= 2, h++) {
    }
    ninv = pow(n, mod - 2, mod);
    x.resize(n, 0);
    y.resize(n, 0);
    convolve(x, y);
  }
  // 高速フーリエ変換を行う、inv=trueで逆変換
  void ntt_calc(vl &x, bool inv = false) {
    ll now = 1;
    // 処理前の並び替え
    for (int i = 0; i < n; i++) {
      int j = 0;
      for (int k = 0; k < h; k++) {
        j |= ((i >> k) & 1) << (h - 1 - k);
      }
      if (i < j) {
        swap(x[i], x[j]);
      }
    }
    ll e = pow(r, (mod - 1) / n, mod);
    if (inv)
      e = pow(e, mod - 2, mod);
    if ((int)exp_list[inv].size() != n) {
      exp_list[inv].assign(n, 0);
      for (int i = 0; i < n; i++, now = now * e % mod) {
        exp_list[inv][i] = now;
      }
    }
    // 各ステップについて処理
    for (int b = 1; b < n; b *= 2) {
      // 各ブロックのj個目
      int m = n / b;
      const int m2 = 2 * b;
      ll w, u, t;
      for (int j = 0; j < b; j++) {
        w = exp_list[inv][j * m / 2];
        // kを先頭とするブロックに対する処理
        for (int k = 0; k < n; k += m2) {
          u = x[j + k], t = x[j + k + b] * w % mod;
          x[j + k] = (u + t) % mod;
          x[j + k + b] = (u - t + mod) % mod;
        }
      }
    }
    if (inv) {
      for (int i = 0; i < n; i++) {
        x[i] = x[i] * ninv % mod;
      }
    }
  }
  // aとbの畳み込みを計算
  void convolve(vl &x, vl &y) {
    ntt_calc(x);
    ntt_calc(y);
    for (int i = 0; i < (int)x.size(); i++) {
      x[i] = x[i] * y[i] % mod;
    }
    ntt_calc(x, true);
    x.resize(s);
    result = x;
  }
};

// 負の数にも対応した mod (a = -11 とかでも OK)
inline long long mod(long long a, long long m) {
  long long res = a % m;
  if (res < 0)
    res += m;
  return res;
}

// 拡張 Euclid の互除法
long long extGCD(long long a, long long b, long long &p, long long &q) {
  if (b == 0) {
    p = 1;
    q = 0;
    return a;
  }
  long long d = extGCD(b, a % b, q, p);
  q -= a / b * p;
  return d;
}

// 逆元計算 (ここでは a と m が互いに素であることが必要)
long long modinv(long long a, long long m) {
  long long x, y;
  extGCD(a, m, x, y);
  return mod(x, m); // 気持ち的には x % m だが、x が負かもしれないので
}

// Garner のアルゴリズム, x%MOD, LCM%MOD を求める (m
// は互いに素でなければならない) for each step, we solve "coeffs[k] * t[k] +
// constants[k] = b[k] (mod. m[k])"
//      coeffs[k] = m[0]m[1]...m[k-1]
//      constants[k] = t[0] + t[1]m[0] + ... + t[k-1]m[0]m[1]...m[k-2]
long long Garner(vector<long long> b, vector<long long> m, long long MOD) {
  m.push_back(MOD); // banpei
  vector<long long> coeffs((int)m.size(), 1);
  vector<long long> constants((int)m.size(), 0);
  for (int k = 0; k < (int)b.size(); ++k) {
    long long t = mod((b[k] - constants[k]) * modinv(coeffs[k], m[k]), m[k]);
    for (int i = k + 1; i < (int)m.size(); ++i) {
      (constants[i] += t * coeffs[i]) %= m[i];
      (coeffs[i] *= m[k]) %= m[i];
    }
  }
  return constants.back();
}

vector<long long> calc_ntt(vector<long long> x, vector<long long> y,
                           long long mod) {
  ntt ntt1(x, y, 167772161);
  ntt ntt2(x, y, 469762049);
  ntt ntt3(x, y, 1224736769);
  vector<long long> r1 = ntt1.result, r2 = ntt2.result, r3 = ntt3.result;
  int n = (int)r1.size();
  vector<long long> ret(n), mod_v = {167772161, 469762049, 1224736769};
  for (int i = 0; i < n; i++) {
    ret[i] = Garner({r1[i], r2[i], r3[i]}, mod_v, mod);
  }
  return ret;
}

int main() {
  PRE_COMMAND
  int n, m;
  cin >> n >> m;
  vector<ll> a(n), b(m);
  cin >> a >> b;
  vector<ll> ans = calc_ntt(a, b, 998244353);
  n = (int)ans.size();
  for (int i = 0; i < n; i++) {
    cout << ans[i];
    if (i != n - 1) {
      cout << " ";
    } else {
      cout << endl;
    }
  }
}