填格子

### 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)):

[egin{aligned} tmp&=B-G\ ans&=sumlimits_{i=tmp}^x [(i+tmp)\%2==0]inom x icdotinom i{frac{i+tmp}{2}}cdotinom{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
",ans);
	}
}
原文地址:https://www.cnblogs.com/yoyoball/p/10090178.html