填格子

Description

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

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

  

Solution

  首先注意到由于行数是\(2\)颜色数是\(3\),因此咱们能够只考虑每一列没有出现过的颜色个数,记这个组成的序列为\(A\)spa

  由于\(2*2\)网格中全部的颜色都要出现过一次,因此\(A\)中的相邻元素必定不一样,咱们考虑其中一种颜色\(R\),显然\(R\)将整个\(A\)分红了若干段,每段只有\(G\)\(B\),考虑这个段数,总共只有三种状况:\(R+1\)\(R\)\(R-1\),计算的方式相似,咱们能够计算三次而后把结果加起来code

  假设如今\(GB\)的段数为\(x\),咱们能够枚举其中长度为奇数的段总共有多少段,而后根据\(G\)\(B\)的出现次数的差,咱们能够肯定长度为奇数的段中,形如\(GBG\)的段比\(BGB\)的多多少ip

  而后咱们就能够直接用组合数计算了,具体一点就是(假设\(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}\)string

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

  

  代码大概长这个样子io

#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);
    }
}
相关文章
相关标签/搜索