競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 237F

ABC237F

制約から,DPで計算するときに 状態 \( O(NM^3) \), 遷移 \( O(M) \) でも間に合う. LIS の求め方を考えると, LIS の 3つの項が \(x,y,z\) のときを状態に持てばよい.

一旦,LIS を無視して考える. 長さ \(N\) の vector \(v\) であって,各元 \(c \in M\) となるものの個数を DP で数える.
for i in N
  for c in M
    v[i] を c とする
という形で求まる.

再び LIS を思い出す. LIS が \([x,y,z]\) で与えられているとして, 次に \(v_{i}\) を定めようとしている.それを \(c \in M\) とする. LIS の作り方を真似すると,
if        \(c \leq x\) : \([x,y,z] \rightarrow [c,y,z]\)
else if \(c \leq y\) : \([x,y,z] \rightarrow [x,c,z]\)
else if \(c \leq z\) : \([x,y,z] \rightarrow [x,y,c]\)
else              : LIS の長さが 4以上となるので不適
という遷移で,LIS を構成できる.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

mint dp[15][15][15];
mint old[15][15][15];

int main() {
  ll n,m;
  cin >> n >> m;

  dp[m][m][m] = 1;
  rep(i,n) {
    rep(x,m+1) rep(y,m+1) rep(z,m+1) old[x][y][z] = 0;
    swap(dp, old);

    rep(x,m+1) rep(y,m+1) rep(z,m+1) {
      mint now = old[x][y][z];

      rep(c,m){
        if     (c <= x) dp[c][y][z] += now;
        else if(c <= y) dp[x][c][z] += now;
        else if(c <= z) dp[x][y][c] += now;
      }
    }
  }
 
  mint ans = 0;
  rep(x,m) rep(y,m) rep(z,m) if(x < y && y < z)
    ans += dp[x][y][z];
  cout << ans.val() << endl;

  return 0;
}