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

#include <algorithm>
#include <iostream>
#define int long long
using namespace std;

int mod = 1000000007;
int n, a, b;
int dp1[5001]
       [2]; // dp1[len][nowChar] : 1個以上の1, A個以上の0を交互に配置(1スタート)
int dp2[5001]
       [2]; // dp2[len][nowChar] : nowChar = 0 : A個未満0, 1 : __(dp1を利用)

signed main() {
  int i, j, k;

  cin >> n >> a >> b;
  if (a > b)
    swap(a, b);

  dp1[0][1] = 1;
  for (i = 0; i < b; i++) {
    // dp1[i][0] -> dp1[i + a以上][1]
    for (j = i + a; j < b; j++) {
      dp1[j][1] += dp1[i][0];
      dp1[j][1] %= mod;
    }
    // dp1[i][1] -> dp1[i + 1以上][0]
    for (j = i + 1; j < b; j++) {
      dp1[j][0] += dp1[i][1];
      dp1[j][0] %= mod;
    }
  }

  dp2[0][0] = 1;
  dp2[0][1] = 1;
  for (i = 0; i < n; i++) {
    // dp2[i][0] -> dp2[i + a未満][1]
    for (j = i + 1; j < i + a && j <= n; j++) {
      dp2[j][1] += dp2[i][0];
      dp2[j][1] %= mod;
    }
    // dp2[i][1] -> dp2[i + b未満][0]
    for (j = i + 1; j < i + b && j <= n; j++) {
      dp2[j][0] += dp2[i][1] * dp1[j - i][0] % mod;
      dp2[j][0] %= mod;
      if (i == 0 || j == n) {
        dp2[j][0] += dp2[i][1] * dp1[j - i][1] % mod;
        dp2[j][0] %= mod;
      }
    }
  }

  // 余事象の個数はdp2[n][0] + dp2[n][1];
  int ng = (dp2[n][0] + dp2[n][1]) % mod;

  // 全体2^N個から引けば答え
  int p2 = 1;
  for (i = 0; i < n; i++) {
    p2 *= 2;
    p2 %= mod;
  }

  cout << (p2 + mod - ng) % mod << endl;
  return 0;
}

Error: TLE

Fixed submission:

#include <algorithm>
#include <iostream>
#define int long long
using namespace std;

int mod = 1000000007;
int n, a, b;
int dp[5001][2][2]
      [5001]; // dp[index][nowChar][flag(1:既に条件を満たす)][len] = 何通り？

signed main() {
  int i, j, k, l, m;

  cin >> n >> a >> b;
  if (a > b) {
    swap(a, b);
  }

  // 最初1文字以上の0からスタート
  for (i = 1; i <= n; i++) {
    if (i < a) {
      dp[i][1][0][0] += 1;
    } else if (i < b) {
      dp[i][1][0][i] += 1;
    } else {
      dp[i][1][1][i] += 1;
    }
  }

  // 最初1文字以上の1からスタート
  for (i = 1; i <= n; i++) {
    if (i < b) {
      dp[i][0][0][i] += 1;
    } else {
      dp[i][0][1][i] += 1;
    }
  }

  // i文字使った状態
  for (i = 1; i < n; i++) {
    // 今から1文字以上のj(=0,1)で埋める
    for (j = 0; j < 2; j++) {
      // k=0:未成功, k=1:成功
      for (k = 0; k < 2; k++) {
        // l文字利用できる
        for (l = 0; l <= i; l++) {
          if (dp[i][j][k][l] == 0)
            continue;
          // m文字連続でjを埋める
          for (m = 1; i + m <= n; m++) {
            if (j == 1) {
              dp[i + m][!j][k || (l + m >= b)][l + m] += dp[i][j][k][l];
              if (dp[i + m][!j][k || (l + m >= b)][l + m] >= mod)
                dp[i + m][!j][k || (l + m >= b)][l + m] -= mod;
            } else {
              if (m < a) {
                dp[i + m][!j][k][0] += dp[i][j][k][l];
                if (dp[i + m][!j][k][0] >= mod)
                  dp[i + m][!j][k][0] -= mod;
              } else {
                (dp[i + m][!j][k || (l + m >= b)][l + m] += dp[i][j][k][l]);
                if (dp[i + m][!j][k || (l + m >= b)][l + m] >= mod)
                  dp[i + m][!j][k || (l + m >= b)][l + m] -= mod;
              }
            }
          }
        }
      }
    }
  }

  int ans = 0;
  // i文字使った状態
  for (i = n; i <= n; i++) {
    // 今から1文字以上のj(=0,1)で埋める
    for (j = 0; j < 2; j++) {
      for (l = 0; l <= i; l++) {
        ans += dp[i][j][1][l];
        ans %= mod;
      }
    }
  }

  cout << ans << endl;
  return 0;
}