填格子

### Description

  给你一个\(2*m\)的网格,每个格子必须涂成红绿蓝(\(RGB\))中的一种,要求每个\(2*2\)的网格中每种颜色都要出现过,并且对于整个网格,任意两个相邻的格子颜色不能相同,现给出三种颜色的出现次数,输出合法的方案数对\(10^9+7\)取模

  数据范围:\(m<=10^6\)

  

Solution

  首先注意到因为行数是\(2\)颜色数是\(3\),所以我们可以只考虑每一列没有出现过的颜色个数,记这个组成的序列为\(A\)

  因为\(2*2\)网格中所有的颜色都要出现过一次,所以\(A\)中的相邻元素一定不同,我们考虑其中一种颜色\(R\),显然\(R\)将整个\(A\)分成了若干段,每段只有\(G\)\(B\),考虑这个段数,总共只有三种情况:\(R+1\)\(R\)\(R-1\),计算的方式类似,我们可以计算三次然后把结果加起来

  假设现在\(GB\)的段数为\(x\),我们可以枚举其中长度为奇数的段总共有多少段,然后根据\(G\)\(B\)的出现次数的差,我们可以确定长度为奇数的段中,形如\(GBG\)的段比\(BGB\)的多多少

  然后我们就可以直接用组合数计算了,具体一点就是(假设\(B>G\)):

\[\begin{aligned} tmp&=B-G\\ ans&=\sum\limits_{i=tmp}^x [(i+tmp)\%2==0]\binom x i\cdot\binom i{\frac{i+tmp}{2}}\cdot\binom{\frac{G+B+i}{2}-1}{x-1}\cdot 2^{x-i} \end{aligned} \]

  前面两个组合数算的是奇数段的,具体就是从\(x\)段中选出\(i\)段作为奇数段,然后为了满足\(B\)\(G\)的出现次数差,\(BGB\)的出现次数应该为\(\frac{i+tmp}{2}\),后面算的是偶数段,为了保证每段都是偶数所以先集体除以\(2\)然后直接插板,因为有\(GB\)\(BG\)两种形式所以还要乘上一个\(2^{x-i}\)

  最后统计答案的时候记得\(R+1\)\(R-1\)的方案数要乘\(2\),因为上下两行可交换;\(R\)的方案数要乘\(4\),因为除了上下两行可交换以外\(R\)的段和\(GB\)的段也可以交换位置

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+10,MOD=1e9+7;
int fac[N],invfac[N],pw2[N];
int n,m,T,R,G,B;
int ans;
int mul(int x,int y){return 1LL*x*y%MOD;}
int plu(int x,int y){return (1LL*x+y)-(1LL*x+y>=MOD?MOD:0);}
int ksm(int x,int y){
	int ret=1,base=x;
	for (;y;y>>=1,base=mul(base,base))
		if (y&1) ret=mul(ret,base);
	return ret;
}
void prework(int n){
	fac[0]=1;
	for (int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	invfac[n]=ksm(fac[n],MOD-2);
	for (int i=n-1;i>=0;--i) invfac[i]=mul(invfac[i+1],i+1);
	pw2[0]=1;
	for (int i=1;i<=n;++i) pw2[i]=mul(pw2[i-1],2);
}
int C(int n,int m){return n<m?0:mul(fac[n],mul(invfac[m],invfac[n-m]));}
int calc(int r,int g,int b){
	if (r<=0) return 0;
	int ret=0,tmp=b-g;
	for (int i=tmp;i<=r;++i){
		if ((i+tmp)%2) continue;
		ret=plu(ret,mul(C(r,i),mul(C(i,(i+tmp)/2),mul(C((g+b+i)/2-1,r-1),pw2[r-i]))));
	}
	return ret;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	scanf("%d",&T);
	prework(N-10);
	for (int o=1;o<=T;++o){
		scanf("%d%d%d%d",&n,&R,&G,&B);
		R=n-R; G=n-G; B=n-B;
		if (R>G) swap(R,G);
		if (R>B) swap(R,B);
		if (G>B) swap(B,G);
		ans=mul(2,calc(R-1,G,B));
		ans=plu(ans,mul(4,calc(R,G,B)));
		ans=plu(ans,mul(2,calc(R+1,G,B)));
		printf("%d\n",ans);
	}
}
posted @ 2018-12-09 08:06  yoyoball  阅读(308)  评论(0编辑  收藏  举报