TopCoder SRM 615 Div1 Hard AlternativePiles
問題
一列にカードが並んでいて、i番目のカードの色はC[i]で与えられ、R, G, B, Wのどれか。
整数Mが与えられるとき、Wのカードに以下の条件を満たすようにR, G, Bの色を塗る塗り方は何通りあるか、mod 10^9+7で求めよ。
条件:
適当な整数Dをきめたとき、R, GがちょうどM*Dこずつある。
長さ2*Dの数列M組であって、
i番目の数列をS_iとすると、
C[S_i[j]] = 'R' (jが偶数のとき)
C[S_i[j]] = 'G' (jが奇数のとき)
を満たしており、どの異なる二つのS_iも同じ要素を持たないような数列の組S_iをとることができる。
制約条件
カードの枚数≦5000
M≦50
方針
カードからBであるものを取り除いた並びが、
RRRGGGRRRGGGのような並びをしていてほしい。
これはD = 2, M = 4のときに条件を満たしている。
このような並びがS_iを取れるというのはどういう条件を満たしているか考える。
まずはR, GがMの倍数個出現していないとならない。
任意のiについて、先頭i文字を見たとき、
Rの出現した個数<Gの出現した個数になっているとダメ
Rの出現した個数>Gの出現した個数 + Mになっているとダメ。
逆に|G|≦|R|≦|G| + Mを満たすとき、必ずSが取れることがわかる。
(後ろからG, R, G, Rととっていけば条件を崩さないように取れるから)
なので、dp[pos][Rの個数 - Gの個数][Rの個数 % M] := 場合の数
というDPをすればよい。
ソースコード
const int mod = (int)1e9 + 7; int dp[2][60][60]; class AlternativePiles { public: int count(string C, int M) { int n = C.size(); memset(dp, 0, sizeof(dp)); dp[0][0][0] = 1; int cur = 0, next = 1; rep(i, n){ memset(dp[next], 0, sizeof(dp[next])); rep(j, M + 1) rep(l, M + 1) if(dp[cur][j][l]) rep(k, 3){ if(C[i] != 'W' && C[i] != "RBG"[k]) continue; int nj = j + 1 - k; int nl = (l + (k == 0)) % M; if(0 <= nj && nj <= M){ dp[next][nj][nl] += dp[cur][j][l]; if(dp[next][nj][nl] >= mod) dp[next][nj][nl] -= mod; } } swap(cur, next); } return dp[cur][0][0]; } };