这题直接做不好做。
最多只能够做个50分。
设 f[i][j][r][g] f [ i ] [ j ] [ r ] [ g ] 表示做到第i列,第i列的状态为j,目前红色的个数是r,绿色的个数是g。
转移很简单。
倘若将这个矩阵的第三行补出来,第三行的每一块颜色是同列的3种颜色中没有出现过的那个。
根据题目信息得出第三行的答案。
题目的第三个条件,提醒道,第三行没有两个颜色相同的块相邻。
所以,将一种颜色看成是分界点,另两种颜色的块根据条件插入。
第三行颜色为x,y,z的个数可根据题目的R,G,B得出。
假设第三行的x有X个,y有Y个,z有Z个。那么分成了x-1或x段。设段数为g。
可知每一段不是yzyz….,就是zyzy….。
每一段的长度或奇或偶,段长为偶数的z的个数和y的个数是相同的。设段长为偶数的段为e。
受到y有Y个,z有Z个的限制,我们需要知道段长为奇数的段的开头为y的段数。
综上,可得方程组:
Y−e−oy=Z−e−oz Y − e − o y = Z − e − o z
oy+oz=g−e o y + o z = g − e
联立,可以解得 oy,oz o y , o z ,要判断他们是否合法。
然后就可以算答案了。
隔板问题:设剩余的y的个数为 r=Y−e−oy r = Y − e − o y ,则有 Crr+g−1 C r + g − 1 r 种方案。
长为奇数的段的开头有oy个Y,所以 Coyg−e C g − e o y 。
长为偶数的段开头是y是z都可以 2e 2 e
还有段的顺序任意。
乘起来就好了。
陷阱:最后要乘个2,因为第三段的一种情况对应答案的2种情况。
#include
#include
#include
#include
#include
#define N 1000010
#define mo 1000000007
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
LL i,j,k,l,n,R,G,B,ans,temp;
LL g,e,r,oy,oz,Y,Z;
LL cnt[3];
LL jc[N],ny[N],_2[N];
LL ksm(LL x,LL y){
LL rs=1;
for(;y;y>>=1,x=(x*x)%mo)if(y&1)rs=(rs*x)%mo;
return rs;
}
void pre(){
int i;
jc[0]=jc[1]=ny[0]=ny[1]=1;
fo(i,2,N-10)jc[i]=(jc[i-1]*i)%mo;
ny[N-10]=ksm(jc[N-10],mo-2);
fd(i,N-11,2)ny[i]=(ny[i+1]*(i+1))%mo;
_2[0]=1;
fo(i,1,N-10)_2[i]=(_2[i-1]*2)%mo;
}
LL C(LL n,LL m){
if(m<0)return 0;
return ((jc[n]*ny[m])%mo*ny[n-m])%mo;
}
int main(){
pre();
scanf("%lld%lld%lld%lld",&n,&R,&G,&B);
cnt[0]=n-R;
cnt[1]=n-G;
cnt[2]=n-B;
LL mx=0;
fo(i,0,2)if(cnt[i])
fo(g,cnt[i]-1,cnt[i]){
if(i==0)Y=cnt[1],Z=cnt[2];
if(i==1)Y=cnt[0],Z=cnt[2];
if(i==2)Y=cnt[0],Z=cnt[1];
fo(e,0,g){
if(!((Y-Z+g-e)&1)){
oy=(Y-Z+g-e)>>1;
oz=g-e-oy;
if(oy<0||oz<0)continue;
r=Y-e-oy;
mx=max(mx,r+g-1);
temp=(((C(r+g-1,r)*C(g,e))%mo*C(g-e,oy))%mo*_2[e])%mo;
ans=(ans+temp)%mo;
}
}
}
ans=(ans*2)%mo;
printf("%lld",ans);
return 0;
}