Description
Input
一行四个整数,
M,R,G,B
M
,
R
,
G
,
B
。
Output
一行一个整数,表示可行解数量
(mod 109+7)
(
m
o
d
10
9
+
7
)
。
Sample Input
2 2 1 1
Sample Output
4
Data Constraint
Hint
分析:
因为只有三种颜色,所以相邻两列必有至少一种相同颜色,所以只要第一列顺序确定,剩下的列只有颜色是不定的,位置已经确定。记录每一列缺少的颜色为状态,这样就相当于有
x=M−R
x
=
M
−
R
个
X
X
,个
Y
Y
,个
Z
Z
,要求构造一个长度为的序列,根据条件
3
3
,相邻两个缺少颜色不能相同,所以求这样序列的个数。
我们发现,设序列以开头,并使 y>z y > z 。每一个 X X ,会把序列分成段或 x−1 x − 1 段。假设把序列分成了 g g 段,我们枚举偶数段为,奇数段即为 g−e g − e 。因为偶数段不能为空(两个 X X 不能相邻),所以剩下 y−e y − e , Z Z 剩下个。我们设以 Y Y 开头的序列有个,以 Z Z 开头的序列有个,因为每一个 Y Y 开头奇数序列可以使减 1 1 ,同理,开头奇数序列可以加 1 1 ,但是,偶数块差为,所以有 sumy−sumz=y−z s u m y − s u m z = y − z 。又因为 sumy+sumz=g−e s u m y + s u m z = g − e ,这样就可以解出 sumy,sumz s u m y , s u m z 了,但是要保证这两个数非负且为整数。
那方案数就很好统计了,我们还剩下 r=y−e−sumy r = y − e − s u m y 个 Y Y 和,可以看做 YZ Y Z 对和 ZY Z Y 对,每一个对都可以插入任何一个块中,但是只能有 YZ Y Z 或 ZY Z Y 一个合法,所以相当于把 r r 个小球放入个盒子中,可以不放,方案数为 (r+g−1g−1) ( r + g − 1 g − 1 ) 。
还有就是奇偶数块的排列,我们可以相当于把 g g 块中的块选出来放偶数块,所以方案数为 (ge) ( g e ) 。
对于每一个奇数块,有 sumy s u m y 个以 Y Y 开头,所以要选出个出来放,方案数为 (g−esumy) ( g − e s u m y ) 。
每一个偶数块,可以以 Y Y 开头,也可以以头,方案数为 2e 2 e 。
把这些乘起来就是对于 e e 个偶数块的方案。
然后把为序列头,及 Z Z 为序列头的跑一下,最后给答案乘,因为开头一列可以反过来。可以考虑预处理出逆元,可以跑很快,我的代码没有预处理,但是还是过了。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const LL mod=1e9+7;
const LL maxn=1e6+7;
using namespace std;
LL n,x,y,z,ans;
LL jc[maxn];
void init()
{
LL R,G,B;
scanf("%lld%lld%lld%lld",&n,&R,&G,&B);
x=n-R; y=n-G; z=n-B;
jc[0]=1; jc[1]=1;
for (LL i=2;i<=maxn;i++) jc[i]=(jc[i-1]*i)%mod;
}
LL power(LL x,LL y)
{
if (y==0) return 1;
if (y==1) return x;
LL c=power(x,y/2);
c=(c*c)%mod;
if (y%2) c=(c*x)%mod;
return c;
}
LL c(LL n,LL m)
{
if (n<m) return 0;
return jc[n]*power(jc[m]*jc[n-m]%mod,mod-2)%mod;
}
void calc(LL x,LL y,LL z)
{
if (y<z) swap(y,z);
for (LL i=0;i<=x;i++)
{
if (((x-i+y-z)%2) || ((x-i-y+z)%2)) continue;
LL sumy=(x-i+y-z)/2;
LL sumz=(x-i-y+z)/2;
if ((sumy<0) || (sumz<0)) continue;
LL r=y-i-sumy;
ans=(ans+c(r+x-1,x-1)%mod*c(x,i)%mod*c(x-i,sumy)%mod*power(2,i)%mod)%mod;
}
x--;
for (LL i=0;i<=x;i++)
{
if (((x-i+y-z)%2) || ((x-i-y+z)%2)) continue;
LL sumy=(x-i+y-z)/2;
LL sumz=(x-i-y+z)/2;
if ((sumy<0) || (sumz<0)) continue;
LL r=y-i-sumy;
ans=(ans+c(r+x-1,x-1)%mod*c(x,i)%mod*c(x-i,sumy)%mod*power(2,i)%mod)%mod;
}
}
int main()
{
init();
calc(x,y,z);
calc(y,x,z);
calc(z,x,y);
ans=ans*2%mod;
printf("%lld",ans);
}