您的位置:首页 > 其它

JZOJ3332【NOI2013模拟】棋盘游戏 特判边界的计数问题(BZOJ 4705)

2016-09-20 21:09 423 查看

题目大意

有一个n∗m的棋盘,初始每个格子都是白色的。

行操作是指选定某一行,将这行所有格子的颜色取反(黑白互换)。

列操作是指选定某一列,将这列所有格子的颜色取反。

现在知道进行了R次行操作C次列操作(可能对某行或者某列操作了多次),最后棋盘上有S个黑色格子。

问有多少种不同的操作方案。两种操作方案不同,当且仅当对某行或者某列操作次数不同(也就是说与操作的顺序无关)。

方案数可能很大,输出它对109+7取模的结果。

n,m,R,C≤1e5

0≤S≤N∗M

解题思路

一个显然的思路就是对于同一行或同一列翻转两次显然是抵消的。那么我们可以设最后又x行翻转了,有y列翻转了,那么黑色的格子数显然是xm+ny−2xy个。而题目要求了

xm+ny−2xy=Sy=S−xmn−2x

显然我们可以通过枚举x来确定y的值,但是会存在分母等于0的情况,即2x=n,我们发现当满足这种情况是y的数量不影响黑色格子的数量,而且只有当S=n∗m2时y才有解,并且可以去规定范围内的任意值。那么我们就可以分类讨论。

1. 2x≠n:方案数=(nx)∗(my)∗(R−x2+n−1n−1)∗(C−y2+m−1m−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
% 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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: