卧薪尝胆,厚积薄发。
dec
Date: Sun Dec 23 18:59:55 CST 2018 In Category: NoCategory

Description:

$2\times n$ 的网格每个格子可以时候红黄蓝三种颜色之一,要求恰有 $R$ 个红色, $Y$ 个黄色, $B$ 个蓝色,相邻格子不能染上相同颜色,每个 $2\times 2$ 的块要包含全部三种颜色,问方案数。
$1\leqslant n\leqslant 10^6$

Solution:

首先发现一个重要的性质是如果有一个 $2\times 2$ 的块有三个位置已经确定了,那么最后一个位置也就确定了,因为如果有一个没出现,那么一定是没出现的那个,如果三个都出现了,那么由于不能相邻所以只能是对角线上的那个,但是我们不能简单地将那些可以推出来的位置忽略,因为他们也是占用颜色的,将所有的合法方案打出来会发现每一个颜色在下方对应的颜色是永远不变的,也就是说我们可以枚举每个颜色对应哪个颜色,这个只有两种情况,这样我们发现可以计算出某个数在第一行的出现次数,另一种很妙的转化就是说考虑每个没有在这一列的两个颜色中出现过的颜色,他们一定也是满足两两不能相邻的,这时我们也可以枚举第一个出现的颜色,注意最后答案要乘二,因为如果确定了没有出现过的颜色,那么对应了两种方案,即上下行交换。
总之无论用何种问题转化,我们的问题就变成了 $n$ 个位置,每个位置可以填上某个颜色,要求同种颜色不能相邻,并且给出每种颜色出现次数。
然后剩下的计数就很神奇了,我们分类讨论第一种颜色是否涂了最后一个格子,如果涂了,那么会把整个序列分成 $x=s_1-1$ 段,否则是 $x=s_1$ 段,然后我们再枚举这 $x$ 段中长度为奇数的段有多少个,设为 $w$ 个,那么显然对于奇数段,如果确定了第一个颜色,后面的颜色都确定了,偶数段的方案数一定为 $2$ ,那么我们可以设 $x_2$ 为第二种颜色位于奇数段开头的个数, $x_3$ 表示第三种颜色,那么我们会得到两个方程,联立一下可以解出 $x_2$ 和 $x_3$ ,剩下的用 $2$ 的幂算一下偶数段,然后奇数段和第一种颜色的放置用组合数算一下就行了。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int m,r,g,b;
#define MAXN 1000010
#define MOD 1000000007
int fac[MAXN],inv[MAXN];
int pows[MAXN];
int power(int a,int b)
{
int res = 1;
while(b > 0)
{
if(b & 1)res = 1ll * res * a % MOD;
a = 1ll * a * a % MOD;
b = b >> 1;
}
return res;
}
void init(int n)
{
fac[0] = 1;
for(int i = 1;i <= n;++i)fac[i] = 1ll * fac[i - 1] * i % MOD;
inv[n] = power(fac[n],MOD - 2);
for(int i = n - 1;i >= 0;--i)inv[i] = 1ll * (i + 1) * inv[i + 1] % MOD;
pows[0] = 1;
for(int i = 1;i <= n;++i)pows[i] = pows[i - 1] * 2 % MOD;
return;
}
int C(int n,int m){return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;}
int solve(int m,int r,int g,int b)
{
int ans = 0;
for(int x = r - 1;x <= r;++x)
{
if(x <= 0)continue;
for(int w = 0;w <= r;++w)
{
if((x - w + g - b) & 1)continue;
int gs = (x - w + g - b) / 2,bs = x - w - gs;
if(gs < 0 || bs < 0)continue;
int rem = m - r - x - w;
if(rem < 0 || rem & 1)continue;
rem = rem / 2;
ans = (ans + 1ll * C(rem + x - 1,x - 1) * C(x - w,gs) % MOD * C(x,w) % MOD * pows[w] % MOD) % MOD;
}
}
return ans;
}
int calc(int m,int R,int G,int B)
{
init(m);
int ans = 0;
int r = (B + G - R) / 2;
int g = (R + B - G) / 2;
int b = (R + G - B) / 2;
ans = (ans + solve(m,r,g,b)) % MOD;
ans = (ans + solve(m,g,r,b)) % MOD;
ans = (ans + solve(m,b,g,r)) % MOD;
return ans;
}
int main()
{
scanf("%d%d%d%d",&m,&r,&g,&b);
cout << calc(m,r,g,b) * 2 % MOD << endl;
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡