【TC SRM 597 DIV1 900】【TC12703】LittleElephantAndBoard(组合数学)

传送门


题解:

我们先不考虑方向,把每列的两个色块拿出来,发现其实RG,GB,RB的数量都是确定的,没有确定只有位置和方向,我们发现两个块必然有至少有一个位置颜色是相同的,也就是说在确定每个位置放什么之后,确定第一块的方向就确定所有块的方向。

现在是 A , B , C A,B,C A,B,C三种类型的块在 1 ∗ m 1*m 1m的方格上铺。

最后再来观察这个限制:所有 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 x1段。

设现在序列中有 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 yeoy=zeozoy+oz=ge

解出 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+ry1)
g g g个组里面选择 e e e个为偶数,方案数为 ( g e ) {g\choose e} (eg)
g − e g-e ge个奇数组里面选择 o y oy oy个作为 B B B开头的,方案数为 ( g − e o y ) {g-e\choose oy} (oyge)
所有偶数组考虑反序,方案数为 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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值