久々のマイ解法記事。
解けたんですがTLで見た解法や解説の解法と違ってて「???」となっていたのでとりあえず自分の解法をまとめます。 maspyさんの書かれた解説とほぼ同じな気がしますので見方のひとつくらいに思ってください。
考えたのは全体的に数値の遷移は二次式になるよなぁ、と思ったこと。 二乗の値はが増えるとは増えるし1次とか0次とかの情報あると嬉しいよなぁ、といろいろ書き下して思いました。
じゃあ二次式として3つの値を持たせたdpにすればよさそうね。というわけで
dp[0][0]は二乗した結果がになってほしいのでになんかを代入したらになる平方の二次式が良いでしょう。
これを用意して最後にを代入します。別にとかにして最後に代入でも同じになると思いますたぶん。言うなればこの二次式は「までYYを通った回数を代入した時のスコア」なので
ではサンプルのこいつで考えてみましょう
2 2 YY XY
- dp[0][0]はさっきも定義した通り
- dp[1][0]はY->Xに辿るのでxが増えたりしません。
- dp[0][1]はY->Yで辿るのでxがx+1になってくれますね。
- dp[1][1]は左からはがそのまま来ます。上からはY->Yなのでさらにxにx+1が代入されになります。合計で。
というわけで答えは最後にをぶちこんだ定数項のと出力。
一般化するならば遷移元の二次式がとして
- Y→Yならxにx+1を代入したを遷移先に加算
- それ以外ならをそのまま加算
となる。
と、ここまで書いたらには最終的にスタートからゴールまでのルートの個数が入るし、にはY->Yを通った回数の総和×2が入りそうな上にそれはY->Yになっている場所を探してコンビネーションを求めれば普通に求まりそうだからそういう方法を使ってるのかなーと思いました。
int main() { ll h, w; cin >> h >> w; auto s = read<string>(h); vector dp(h, vector(w, vector<mint2>(3, 0))); dp[0][0][2] = 1; REP(i, h) { REP(j, w) { if (i < h - 1) { if (s[i][j] == 'Y' && s[i + 1][j] == 'Y') { dp[i + 1][j][2] += dp[i][j][2]; dp[i + 1][j][1] += dp[i][j][2] * 2 + dp[i][j][1]; dp[i + 1][j][0] += dp[i][j][2] + dp[i][j][0] + dp[i][j][1]; } else { REP(k, 3) dp[i + 1][j][k] += dp[i][j][k]; } } if (j < w - 1) { if (s[i][j] == 'Y' && s[i][j + 1] == 'Y') { dp[i][j + 1][2] += dp[i][j][2]; dp[i][j + 1][1] += dp[i][j][2] * 2 + dp[i][j][1]; dp[i][j + 1][0] += dp[i][j][2] + dp[i][j][0] + dp[i][j][1]; } else { REP(k, 3) dp[i][j + 1][k] += dp[i][j][k]; } } } } cout << dp.back().back()[0].val(); return 0; }