bzoj4705: 棋盘游戏

Description

有一个N*M的棋盘,初始每个格子都是白色的。
行操作是指选定某一行,将这行所有格子的颜色取反(黑白互换)。
列操作是指选定某一列,将这列所有格子的颜色取反。
XX进行了R次行操作C次列操作(可能对某行或者某列操作了多次),最后棋盘上有S个黑色格子。
问有多少种不同的操作方案。两种操作方案不同,当且仅当对某行或者某列操作次数不同(也就是说与操作的顺序无关)。
方案数可能很大,输出它对10^9+7取模的结果。

Input

输入只有5个整数N,M,R,C,S。
N,M,R,C≤100000,0≤S≤N*M。

Output

输出有且仅有一个整数,表示答案对10^9+7取模的结果。
对行和列分别考虑,若有a行操作奇数次,b列操作奇数次,则有am+bn-ab个格子为黑色,可以枚举a求出对应的b,这时问题转化为n个物品分成k段,有x段含奇数个物品的方案数,打表可知当n-x为奇数时方案数为0,否则方案数为C(k-1+(n-x)/2,k-1)*C(k,x)
#include<cstdio>
typedef long long i64;
const int P=1000000007;
int n,m,a,b;
i64 s,ans=0;
int iv[210007],fiv[210007],fac[210007];
i64 C(int n,int m){
    if(n<m)return 0;
    return i64(fac[n])*fiv[m]%P*fiv[n-m]%P;
}
i64 F(int n,int k,int x){
    if(n-x&1)return 0;
    return C(k,x)*C(k-1+(n-x>>1),k-1)%P;
}
int main(){
    scanf("%d%d%d%d%lld",&n,&m,&a,&b,&s);
    iv[1]=1;
    int mx=(n>m?n:m)*2+7;
    for(int i=2;i<=mx;++i)iv[i]=i64(-P/i)*iv[P%i]%P;
    fac[0]=fiv[0]=1;
    for(int i=1;i<=mx;++i){
        fac[i]=i64(i)*fac[i-1]%P;
        fiv[i]=i64(iv[i])*fiv[i-1]%P;
    }
    for(int i=0,z=n;i<=n;++i,z-=2){
        i64 j=(s-i64(i)*m);
        if(z&&j%z==0){
            j/=z;
            if(j>=0&&j<=m)ans=(ans+F(a,n,i)*F(b,m,j))%P;
        }
    }
    if(n%2==0&&s*2==n*i64(m)){
        int i=n/2;
        i64 v=0;
        for(int j=0;j<=m;++j)v+=F(b,m,j);
        ans=(ans+v%P*F(a,n,i))%P;
    }
    printf("%lld",(ans+P)%P);
    return 0;
}