传送门
题解:
我们先不考虑方向,把每列的两个色块拿出来,发现其实RG,GB,RB的数量都是确定的,没有确定只有位置和方向,我们发现两个块必然有至少有一个位置颜色是相同的,也就是说在确定每个位置放什么之后,确定第一块的方向就确定所有块的方向。
现在是 A , B , C A,B,C A,B,C三种类型的块在 1 ∗ m 1*m 1∗m的方格上铺。
最后再来观察这个限制:所有 2 × 2 2\times 2 2×2的方格必须包含所有颜色。
我们发现这个限制其实就是不允许相邻块种类相同。
于是我们知道实际上我们需要考虑的问题就是,有 A , B , C A,B,C A,B,C的色块若干个,要求铺到一条线上,同色色块不能相邻。
我们固定开头的色块种类,那么这是三个形式相同的子问题。
假设颜色 A A A开头,共有 x x x个,那么 A A A将序列划分为了 x x x段或 x − 1 x-1 x−1段。
设现在序列中有 g g g段。
枚举偶数长度段有多少个(其实这步也挺好想的,因为偶数长度的段反序是和原来不同的,需要和奇数段分开)设这个值为 e e e。
由于不允许段为空,这些偶数段需要至少一个 B B B和一个 C C C,设 B B B和 C C C的个数分别为 y , z y,z y,z。设奇数段中以 B B B开头的有 o y oy oy种,以 C C C开头的有 o z oz oz,那么我们根据和差关系可以得到以下两个方程 y − e − o y = z − e − o z o y + o z = g − e y-e-oy=z-e-oz\\ oy+oz=g-e y−e−oy=z−e−ozoy+oz=g−e
解出 o y , o z oy,oz oy,oz,算出剩下的还可以自由填的 B , C B,C B,C还有多少个,记为 r y , r z ry,rz ry,rz。必须有 r y = r z ry=rz ry=rz,不然显然不合法。
那么将剩下的分配到
g
g
g个组里,允许空,方案为
(
g
+
r
y
−
1
r
y
)
{g+ry-1\choose ry}
(ryg+ry−1)
在
g
g
g个组里面选择
e
e
e个为偶数,方案数为
(
g
e
)
{g\choose e}
(eg)
在
g
−
e
g-e
g−e个奇数组里面选择
o
y
oy
oy个作为
B
B
B开头的,方案数为
(
g
−
e
o
y
)
{g-e\choose oy}
(oyg−e)
所有偶数组考虑反序,方案数为
2
e
{2^e}
2e
把该预处理的预处理一下,就可以 O ( m ) O(m) O(m)来算答案了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
cs int mod=1e9+7;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
cs int N=1e6+6;
int fac[N],ifac[N],bin[N];
inline void init_inv(int n){
fac[0]=ifac[0]=bin[0]=1;
for(int re i=1;i<=n;++i){
bin[i]=add(bin[i-1],bin[i-1]);
fac[i]=mul(fac[i-1],i);
}
ifac[n]=power(fac[n],mod-2);
for(int re i=n-1;i;--i)ifac[i]=mul(ifac[i+1],i+1);
}
inline int C(int n,int m){return (n>=0&&m>=0&&n>=m)?mul(fac[n],mul(ifac[n-m],ifac[m])):0;}
class LittleElephantAndBoard{
private:
inline int calc(int x,int y,int z){
int res=0;
for(int re g=x-1,oy,ry,oz,rz;g<=x;++g)
for(int re e=0;e<=g;++e)if(((y^z^g^e)&1)==0){
oy=(y-z+g-e)>>1;
ry=y-oy-e;
oz=g-e-oy;
rz=z-oz-e;
if(oy>=0&&ry>=0&&oz>=0&&rz>=0&&rz==ry)
Inc(res,mul(mul(C(g,e),C(g-e,oy)),mul(C(g+ry-1,ry),bin[e])));
}
return res;
}
public:
int getNumber(int M,int R,int G,int B){
init_inv(M);int res=0;
Inc(res,calc(M-R,M-G,M-B));
Inc(res,calc(M-B,M-R,M-G));
Inc(res,calc(M-G,M-R,M-B));
return add(res,res);
}
};
#ifdef zxyoi
LittleElephantAndBoard Solver;
signed main(){
freopen("RGB.in","r",stdin);
int M,R,G,B;
std::cin>>M>>R>>G>>B;
std::cout<<Solver.getNumber(M,R,G,B);
return 0;
}
#endif