kmjp's blog

競技プログラミング参加記です

yukicoder : No.584 赤、緑、青の色塗り

またゴリ押しして通してしまった。
https://yukicoder.me/problems/no/584

問題

N個のマスが並んでおり、R個を赤、G個を緑、B個を青で塗る。何にも塗られないマスがあってもよい。
この時、以下を満たすようにしたい。

  • 白以外の同色マスが2つ隣接してはいけない。
  • 白以外のマスが3つ以上連続してはいけない。

塗り方は何通りあるか。

解法

白マスは(N-(R+G+B))個あるので、それらの間と両端、計A=(N-(R+G+B)+1)個の隙間に、色つきのマスを当てはめていくことを考える。
これらの隙間のうち、2個連続色が塗られた箇所がS2個、1個連続色が塗られた箇所がS1個、色が塗られたマスが無い箇所がS0個とする。
以下の2つの式より、S0,S1,S2を1つ定めれば残り2つも定まるので、1個に関し総当たりしよう。

  • 2*S2 + S1 = R+G+B
  • S2 + S1 + S0 = A

S0,S1,S2が定まると、まずそれらのA箇所への割り振り埋め方が \displaystyle {}_A C_{S0} \times {}_{A-S0} C_{S1} 通りある。
次に、例えば2個色つきマスが連続する場所に入る赤・緑・青のマス数をr2,g2,b2、1個色つきマスがある場所に入るマス数をr1,g1,b1とする。

この時、例えばr2を総当たりする。
r2を定めると、r2+r1=Rよりr1が定まる。
この時点で赤マスを埋めてしまおう。赤が入る連続マス群が \displaystyle {}_{S2} C_{r2} \times {}_{S1} C_{r1} 通りある。
(2マス連続の場合どちらに入るかはあとでまとめて計算するので、まずは無視)
こうすると、(S2-r2)個の2連続マスは青と緑の対になることが確定するので、そこも埋める。

あとは、残り(G-(S2-r2))個の緑マスと、(B-(S2-b2))個の青マスを、赤と対になる2連続マス、もしくは空いた1マスのどこかに埋めることになる。
これは結局 \displaystle {}_{(G-(S2-r2))+(B-(S2-b2))} C_{(B-(S2-b2))}通りである。
最後に、2連続色つきマスがあるS2箇所は、2色のうちどちらが先に来るかを考えると2^S2通り組み合わせが増える。

結局、S2とr2の2変数を総当たりし、 \displaystyle {}_A C_{S0} \times {}_{A-S0} C_{S1} \times {}_{S2} C_{r2} \times {}_{S1} C_{r1} \times {}_{(G-(S2-r2))+(B-(S2-b2))} C_{(B-(S2-b2))} \times 2^{S2} の総和を取ればよいのでO(N^2)で解ける。

int N,R,G,B,S;
int mo=1000000007;

const int CN=4001;
ll C[CN][CN];
ll p2[3050];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	FOR(i,CN) for(j=0;j<=i;j++) C[i][j]=(j==0||j==i)?1:(C[i-1][j-1]+C[i-1][j])%mo;
	p2[0]=1;
	FOR(i,3030) p2[i+1]=1LL*p2[i]*2%mo;
	
	cin>>N>>R>>G>>B;
	S=N-R-G-B;
	int slot=S+1;
	
	ll ret=0;
	for(int s2=0;s2<=slot;s2++) {
		int s1=R+G+B-s2*2;
		int s0=slot-s1-s2;
		if(s1<0 || s0<0) continue;
		
		ll pat=1LL*C[slot][s2]*C[s0+s1][s0]%mo;
		ll hoge=0;
		for(int r2=0;r2<=min(R,s2);r2++) {
			ll pat1=C[s2][r2];
			int r1=R-r2;
			if(r1>s1) continue;
			(pat1*=C[s1][r1])%=mo;
			int bl=B-(s2-r2);
			int gl=G-(s2-r2);
			if(bl<0 || gl<0) continue;
			(hoge += pat1*C[bl+gl][gl]%mo)%=mo;
		}
		(ret += pat*hoge%mo*p2[s2])%=mo;
	}
	
	cout<<ret<<endl;
	
	
}

まとめ

本番O(N^3)解法をインラインアセンブラでゴリ押しした。