Problem p00482 (C++) — buggy submission, verdict: Memory Limit Exceeded

#include <bits/stdc++.h>

using namespace std;

#define fst(t) std::get<0>(t)
#define snd(t) std::get<1>(t)
#define thd(t) std::get<2>(t)
#define unless(p) if (!(p))
#define until(p) while (!(p))

using ll = long long;
using P = std::tuple<int, int>;

const int dx[8] = {-1, 1, 0, 0, -1, -1, 1, 1},
          dy[8] = {0, 0, -1, 1, -1, 1, -1, 1};

template <typename T> T expt(T a, T n, T mod = std::numeric_limits<T>::max()) {
  T res = 1;
  while (n) {
    if (n & 1) {
      res = res * a % mod;
    }
    a = a * a % mod;
    n >>= 1;
  }
  return res;
}

constexpr ll MOD = 100000;
int M, N;
std::string flag[30];
std::vector<int> v;
int dp[20][20][18000][2];

// pr: (r, c) 以前 N マスにあった"JO"を記録したもの
// flag_j: (r, c) の左隣に"J"があるか
// 条件を満たさない旗の個数を計算する
ll rec(int r, int c, int pr, int flag_j) {
  if (r == M) {
    return 1;
  }

  int idx = std::lower_bound(v.begin(), v.end(), pr) - v.begin();

  assert(v[idx] == pr);

  if (dp[r][c][idx][flag_j] != -1) {
    return dp[r][c][idx][flag_j];
  }

  int nr = r, nc = c + 1;
  if (c + 1 == N) {
    nr = r + 1;
    nc = 0;
  }

  ll res = 0;
  if (flag[r][c] == 'J' || flag[r][c] == '?') {
    res = rec(nr, nc, (pr << 1) & ((1 << N) - 1), nr == r);
  }

  if (flag[r][c] == 'O' || flag[r][c] == '?') {
    res = (res + rec(nr, nc, ((pr + flag_j) << 1) & ((1 << N) - 1), 0)) % MOD;
  }

  if (flag[r][c] == 'I' || flag[r][c] == '?') {
    unless(pr >> (N - 1) & 1) {
      res = (res + rec(nr, nc, (pr << 1) & ((1 << N) - 1), 0)) % MOD;
    }
  }

  return dp[r][c][idx][flag_j] = res;
}

int main() {
  std::cin.tie(nullptr);
  std::ios::sync_with_stdio(false);

  std::cin >> M >> N;

  for (int i = 0; i < (1 << N); ++i) {
    unless(i & (i >> 1)) { v.emplace_back(i); }
  }

  int numQuestions = 0;
  for (int i = 0; i < M; ++i) {
    std::cin >> flag[i];

    numQuestions += std::count(flag[i].begin(), flag[i].end(), '?');
  }

  memset(dp, -1, sizeof(dp));
  ll res = (expt<ll>(3, numQuestions, MOD) + MOD - rec(0, 0, 0, 0)) % MOD;
  std::cout << res << std::endl;
}


Error: MLE

Fixed submission:

#include <bits/stdc++.h>

using namespace std;

#define fst(t) std::get<0>(t)
#define snd(t) std::get<1>(t)
#define thd(t) std::get<2>(t)
#define unless(p) if (!(p))
#define until(p) while (!(p))

using ll = long long;
using P = std::tuple<int, int>;

const int dx[8] = {-1, 1, 0, 0, -1, -1, 1, 1},
          dy[8] = {0, 0, -1, 1, -1, 1, -1, 1};

template <typename T> T expt(T a, T n, T mod = std::numeric_limits<T>::max()) {
  T res = 1;
  while (n) {
    if (n & 1) {
      res = res * a % mod;
    }
    a = a * a % mod;
    n >>= 1;
  }
  return res;
}

constexpr ll MOD = 100000;
int M, N;
std::string flag[30];
std::map<int, int> dp;

// pr: (r, c) 以前 N マスにあった"JO"を記録したもの
// flag_j: (r, c) の左隣に"J"があるか
// 条件を満たさない旗の個数を計算する
ll rec(int r, int c, int pr, int flag_j) {
  if (r == M) {
    return 1;
  }

  int hash = pr + (1 << 20) * (r + 32 * (c + 32 * flag_j));

  if (dp.count(hash) > 0) {
    return dp[hash];
  }

  int nr = r, nc = c + 1;
  if (c + 1 == N) {
    nr = r + 1;
    nc = 0;
  }

  ll res = 0;
  if (flag[r][c] == 'J' || flag[r][c] == '?') {
    res = rec(nr, nc, (pr << 1) & ((1 << N) - 1), nr == r);
  }

  if (flag[r][c] == 'O' || flag[r][c] == '?') {
    res = (res + rec(nr, nc, ((pr + flag_j) << 1) & ((1 << N) - 1), 0)) % MOD;
  }

  if (flag[r][c] == 'I' || flag[r][c] == '?') {
    unless(pr >> (N - 1) & 1) {
      res = (res + rec(nr, nc, (pr << 1) & ((1 << N) - 1), 0)) % MOD;
    }
  }

  return dp[hash] = res;
}

int main() {
  std::cin.tie(nullptr);
  std::ios::sync_with_stdio(false);

  std::cin >> M >> N;

  int numQuestions = 0;
  for (int i = 0; i < M; ++i) {
    std::cin >> flag[i];

    numQuestions += std::count(flag[i].begin(), flag[i].end(), '?');
  }

  ll res = (expt<ll>(3, numQuestions, MOD) + MOD - rec(0, 0, 0, 0)) % MOD;
  std::cout << res << std::endl;
}