JZOJ 4391. 【GDOI2016模拟3.16】装饰

题目

这里写图片描述
这里写图片描述

题解

这题直接做不好做。
最多只能够做个50分。
f [ i ] [ j ] [ r ] [ g ] 表示做到第i列,第i列的状态为j,目前红色的个数是r,绿色的个数是g。
转移很简单。
倘若将这个矩阵的第三行补出来,第三行的每一块颜色是同列的3种颜色中没有出现过的那个。
根据题目信息得出第三行的答案。
题目的第三个条件,提醒道,第三行没有两个颜色相同的块相邻。
所以,将一种颜色看成是分界点,另两种颜色的块根据条件插入。
第三行颜色为x,y,z的个数可根据题目的R,G,B得出。
假设第三行的x有X个,y有Y个,z有Z个。那么分成了x-1或x段。设段数为g。
可知每一段不是yzyz….,就是zyzy….。
每一段的长度或奇或偶,段长为偶数的z的个数和y的个数是相同的。设段长为偶数的段为e。
受到y有Y个,z有Z个的限制,我们需要知道段长为奇数的段的开头为y的段数。
综上,可得方程组:
Y e o y = Z e o z
o y + o z = g e
联立,可以解得 o y , o z ,要判断他们是否合法。
然后就可以算答案了。
隔板问题:设剩余的y的个数为 r = Y e o y ,则有 C r + g 1 r 种方案。
长为奇数的段的开头有oy个Y,所以 C g e o y
长为偶数的段开头是y是z都可以 2 e
还有段的顺序任意。
乘起来就好了。
陷阱:最后要乘个2,因为第三段的一种情况对应答案的2种情况。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 1000010
#define mo 1000000007
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
LL i,j,k,l,n,R,G,B,ans,temp;
LL g,e,r,oy,oz,Y,Z;
LL cnt[3];
LL jc[N],ny[N],_2[N];
LL ksm(LL x,LL y){
    LL rs=1;
    for(;y;y>>=1,x=(x*x)%mo)if(y&1)rs=(rs*x)%mo;
    return rs;
}
void pre(){
    int i;
    jc[0]=jc[1]=ny[0]=ny[1]=1;
    fo(i,2,N-10)jc[i]=(jc[i-1]*i)%mo;
    ny[N-10]=ksm(jc[N-10],mo-2);
    fd(i,N-11,2)ny[i]=(ny[i+1]*(i+1))%mo;
    _2[0]=1;
    fo(i,1,N-10)_2[i]=(_2[i-1]*2)%mo;
}
LL C(LL n,LL m){
    if(m<0)return 0;
    return ((jc[n]*ny[m])%mo*ny[n-m])%mo;
}
int main(){
    pre();
    scanf("%lld%lld%lld%lld",&n,&R,&G,&B);
    cnt[0]=n-R;
    cnt[1]=n-G;
    cnt[2]=n-B;
    LL mx=0;
    fo(i,0,2)if(cnt[i])
    fo(g,cnt[i]-1,cnt[i]){
        if(i==0)Y=cnt[1],Z=cnt[2];
        if(i==1)Y=cnt[0],Z=cnt[2];
        if(i==2)Y=cnt[0],Z=cnt[1];
        fo(e,0,g){
            if(!((Y-Z+g-e)&1)){
                oy=(Y-Z+g-e)>>1;
                oz=g-e-oy;
                if(oy<0||oz<0)continue;
                r=Y-e-oy;
                mx=max(mx,r+g-1);
                temp=(((C(r+g-1,r)*C(g,e))%mo*C(g-e,oy))%mo*_2[e])%mo;
                ans=(ans+temp)%mo;
            }
        }
    }
    ans=(ans*2)%mo;
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huangjingyuan107/article/details/81087244