TopCoder SRM 564 Div1 Medium AlternateColors2

問題

r個の赤の球、g個の緑の球、b個の青の球がある。
この球を以下の手順を繰り返して並べる。


赤があれば赤の球を並べる。
緑があれば緑の球を並べる。
青があれば青の球を並べる。
(最初に戻る)


r + g + b = nであり、k番目に並べた球は赤い球だったことがわかっている。
n, kが与えられるとき、条件を満たすr, g, bの組の個数を求めよ。

制約条件

n≦100000
k≦n

方針

取り出した球の文字列を考える。
rgbrgbrbrbrrrのようになるが、
この文字列ひとつについて、数の組(r, g, b)が一対一で対応している。


なので、この文字列のうち、条件を満たすものが何通りあるかを数えればよい。


dp[i][j][k]を、今球をi個並べて、直前がjの色で、集合kの球を使い切った状態であるようなときの場合の数とする。
これを更新していくdpをすればいい。


場合わけして数式で直接的に求めることもできる。

ソースコード

ll dp[100010][8][3];

class AlternateColors2 {
	public:
	long long countWays(int n, int k) {
		
		memset(dp, 0, sizeof(dp));
		for(int i = 1; i < 8; i++) dp[0][i][2] = 1;
		
		rep(i, n) rep(j, 8) rep(p, 3){
			if(dp[i][j][p] == 0) continue;
			if(j == 0) continue;
			
			int nxt = p;
			rep(l, 3){
				nxt = (nxt + 1) % 3;
				if(j & 1 << nxt) break;
			}
			if(i == k - 1 && nxt != 0) continue;
			
			dp[i + 1][j][nxt] += dp[i][j][p];
			dp[i + 1][j ^ 1 << nxt][nxt] += dp[i][j][p];
		}
		ll ans = 0;
		rep(i, 3) ans += dp[n][0][i];
		return ans;
	}
};