またゴリ押しして通してしまった。
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箇所への割り振り埋め方が通りある。
次に、例えば2個色つきマスが連続する場所に入る赤・緑・青のマス数をr2,g2,b2、1個色つきマスがある場所に入るマス数をr1,g1,b1とする。
この時、例えばr2を総当たりする。
r2を定めると、r2+r1=Rよりr1が定まる。
この時点で赤マスを埋めてしまおう。赤が入る連続マス群が通りある。
(2マス連続の場合どちらに入るかはあとでまとめて計算するので、まずは無視)
こうすると、(S2-r2)個の2連続マスは青と緑の対になることが確定するので、そこも埋める。
あとは、残り(G-(S2-r2))個の緑マスと、(B-(S2-b2))個の青マスを、赤と対になる2連続マス、もしくは空いた1マスのどこかに埋めることになる。
これは結局通りである。
最後に、2連続色つきマスがあるS2箇所は、2色のうちどちらが先に来るかを考えると2^S2通り組み合わせが増える。
結局、S2とr2の2変数を総当たりし、の総和を取ればよいので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)解法をインラインアセンブラでゴリ押しした。