题目大意
有一个
n∗m
的棋盘,初始每个格子都是白色的。
行操作是指选定某一行,将这行所有格子的颜色取反(黑白互换)。
列操作是指选定某一列,将这列所有格子的颜色取反。
现在知道进行了
R
次行操作
问有多少种不同的操作方案。两种操作方案不同,当且仅当对某行或者某列操作次数不同(也就是说与操作的顺序无关)。
方案数可能很大,输出它对
n,m,R,C≤1e5
0≤S≤N∗M
解题思路
一个显然的思路就是对于同一行或同一列翻转两次显然是抵消的。那么我们可以设最后又
x
行翻转了,有
xm+ny−2xy=Sy=S−xmn−2x
显然我们可以通过枚举 x 来确定
1.
2. 2x=m,S=n∗m2:方案数=(nx)∗(R−x2+n−1n−1)∗(C+m−1m−1)
注意一下范围,直接计算即可。
程序
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 2e5 + 5;
const int Mo = 1e9 + 7;
int Max, Fac[MAXN], Inv[MAXN], n, m, r, c;
LL s;
int Power(int x, int y) {
int Ans = 1;
for (; y; y >>= 1, x = 1ll * x * x % Mo)
if (y & 1) Ans = 1ll * Ans * x % Mo;
return Ans;
}
int C(int m, int n) {
if (m < 0 || n < 0 || m < n) return 0;
return 1ll * Fac[m] * Inv[n] % Mo * Inv[m - n] % Mo;
}
void Prepare() {
Fac[0] = Inv[0] = 1;
for (int i = 1; i <= Max * 2; i ++) Fac[i] = 1ll * Fac[i - 1] * i % Mo;
for (int i = 1; i <= Max * 2; i ++) Inv[i] = 1ll * Inv[i - 1] * Power(i, Mo - 2) % Mo;
}
int main() {
scanf("%d%d%d%d%lld", &n, &m, &r, &c, &s);
Max = max(max(n, m), max(r, c));
Prepare();
int Ans = 0;
for (int i = 0; i <= min(n, r); i ++) {
if (i * 2 == n) {
if ((r - i) & 1 || s != 1ll * n * m / 2) continue;
Ans = (Ans + 1ll * C(n, i) * C((r - i) / 2 + n - 1, n - 1) % Mo * C(c + m - 1, m - 1) % Mo) % Mo;
} else {
if ((s - 1ll * i * m) % (n - 2 * i) != 0) continue;
int j = (s - 1ll * i * m) / (n - 2 * i);
if ((r - i) & 1 || (c - j) & 1 || c - j < 0 || j < 0) continue;
Ans = (Ans + 1ll * C(n, i) * C(m, j) % Mo * C((r - i) / 2 + n - 1, n - 1) % Mo * C((c - j) / 2 + m - 1, m - 1) % Mo) % Mo;
}
}
printf("%d\n", Ans);
}