# 省选模拟（12.10） T1 submat

SUBMAT

12.10 省选模拟T1

PS:代码中的表格表示枚举容斥当中固定多少位置，获得对应和的方案数。

Source:

/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <map>
#include <bitset>

static const int IN_LEN = 1024 * 1024;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}

///*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}

template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}

inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout);
}

/*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
if (c == '-') iosig = true;
for (x = 0; isdigit(c); c = getchar())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/

const int mod = 1000000000 + 7;
const int MAXN = 10;

int n, m, h, w;
int c[MAXN][MAXN], row[MAXN], col[MAXN];

inline int mod_pow(int a, int b) {
int ans = 1;
for (; b; b >>= 1, a = (long long)a * a % mod)
if (b & 1) ans = (long long)ans * a % mod;
return ans;
}

inline void solve() {
long long ans = 0;
R(n), R(m), R(h), R(w);
for (int i = 0; i <= 4; ++i) c[i][i] = c[i][0] = 1;
for (int i = 2; i <= 4; ++i)
for (int j = 1; j < i; ++j)
c[i][j] = c[i - 1][j] + c[i - 1][j - 1];

for (int cur = 0, s = (1 << h * w); cur < s; ++cur) {
for (int i = 0; i < 4; ++i) row[i] = col[i] = 0;
long long ret = 1;
for (int i = 0; i < h * w; ++i)
if (cur & (1 << i))
row[i / w]++, col[i % w]++;
/*1表示向下相等， 显然相邻两列的和相等，固定了col[i]个数，其他的数满足
和相等即可，直接枚举和，和方案数乘法原理即可，j为枚举和，z表示一共有z列*/
for (int i = 0; i < w; ++i) {
int z = (m - 1 - i) / w + 1, x = 0;
for (int j = 0; j <= col[i]; ++j)
x = (x + mod_pow(c[col[i]][j], z)) % mod;
ret = ret * x % mod;
}
for (int i = 0; i < h; ++i) row[i] = w - row[i]; // 每一行有多少向右相等
for (int i = 0; i < h; ++i) {
int z = (n - 1 - i) / h + 1, x = 0;
switch (row[i]) {
case 0: x = 1;
// 四个都向下相等，情况相同
break ;
case 1: x = 0;
//显然只有这一个数，那么肯定满足向下和向右
break ;
//以下三张表格，表示枚举容斥当中枚举固定多少位置，
//获得的对应和的方案数
case 2: x = mod_pow(2, z) - 2;
// 和/固定个数 0     1    2
//  0          1     2    1
//  1          2^z   4    2
//  2          1     2    1
break ;
case 3: x = mod_pow(3, z) * 2 % mod -
(long long)mod_pow(2, z) * 6 % mod + 6;
(x < 0) ? (x += mod) : (0);
break ;
// 和/固定个数 0     1        2     3
//  0          1     3        3     1
//  1          3^z   3*2^z+3  9     3
//  2          3^z   3*2^z+3  9     3
//  3          1     3        3     1
case 4: x = (long long)mod_pow(6, z) + 2LL * mod_pow(4, z) % mod
- 16LL * mod_pow(3, z) % mod
+ 24LL * mod_pow(2, z) % mod - 14;
x %= mod, (x < 0) ? (x += mod) : (0);
// 和/固定个数 0     1        2          3    4
//  0          1     4        6          4    1
//  1          4^z   4*3^z+4  6*2^z+12   16   4
//  2          6^z   8*3^z    12*2^z+12  24   6
//  3          4^z   4*3^z+4  6*2^z+12   16   4
//  4          1     4        6          4    1
break ;
}
ret = ret * x % mod;
}
ans = (ans + ret) % mod;
}
std::cout << ans;
}

int main() {
freopen("submat.in", "r", stdin);
freopen("submat.out", "w", stdout);
solve();
return 0;
}

• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120