[数论][组合数学]棋盘游戏

题目描述

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

Input
输入只有5个整数N,M,R,C,S。

Output
输出有且仅有一个整数,表示答案对10^9+7取模的结果。

Sample Input
2 2 2 2 4

Sample Output
4

Data Constraint
对于20%的数据,满足N,M,R,C≤4。
对于60%的数据,满足N,M,R,C≤1500。
对于100%的数据,满足N,M,R,C≤100000,0≤S≤N*M。

分析

首先容易知道反转偶数次就等于没有操作
那么假设我们进行了行的有效操作x次,列的有效操作y次那么容易知道:

S=xm+yn2xy S = x m + y n − 2 x y

然后 乱搞移一下
y=Sxmn2x y = S − x m n − 2 x

显然我们可以通过枚举x或y来获得另一个
然后我们发现分母在 x=n2 x = n 2 时为0
然后又发现 x=n2 x = n 2 时y值不影响S值, Snm2 S ≡ n ∗ m 2
所以显然有:
xn2 x ≠ n 2 时,

ans=Cnx×Cmy×Crx2+n1n1×Ccy2+m1m1 a n s = C x n × C y m × C n − 1 r − x 2 + n − 1 × C m − 1 c − y 2 + m − 1

x=n2 x = n 2 S=nm2 S = n ∗ m 2 时,

ans=Cnx×Crx2+n1n1×Cc+m1m1 a n s = C x n × C n − 1 r − x 2 + n − 1 × C m − 1 c + m − 1

然后注意是否大于0,是否偶数什么的即可
第一次用Latex,公式若有错误请指出

#include <iostream>
#include <cstdio>
#define rep(i,a,b) for (i=a;i<=b;i++)
const long long MOD=1e9+7;
typedef long long ll;
using namespace std;
int n,m,r,c;
ll ans,s;
ll f[200001],ny[200001];

ll Power(ll x,ll y) {
    ll ans=1;
    while (y) {
        if (y&1) ans=ans*x%MOD;
        x=x*x%MOD;
        y>>=1;
    }
    return ans;
}

ll C(int m,int n) {
    if (n<0||m<0||n-m<0) return 0;
    return f[n]*ny[m]%MOD*ny[n-m]%MOD;
}

int main() {
    int i,x,y;
    f[0]=ny[0]=1;
    rep(i,1,200000) {
        f[i]=f[i-1]*i%MOD;
        ny[i]=ny[i-1]*Power(i,MOD-2)%MOD;
    }
    scanf("%d%d%d%d%lld",&n,&m,&r,&c,&s);
    rep(i,0,min(r,n)) {
        if (i*2!=n) {
            if ((s-(long long)i*m)%(n-2*i)!=0) continue;
            int j=(s-(long long)i*m)/(n-2*i);
            if ((r-i)&1||(c-j)&1||c-j<0||j<0) continue;
            ans=(ans+C(i,n)*C(j,m)%MOD*C(n-1,(r-i)/2+n-1)%MOD*C(m-1,(c-j)/2+m-1)%MOD)%MOD;
        }
        else
        if ((ll)2*s==(ll)n*m) {
            if ((r-i)&1) continue;
            ans=(ans+C(i,n)*C(n-1,(r-i)/2+n-1)%MOD*C(m-1,c+m-1)%MOD)%MOD;
        }
    }
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值