(相信来看这篇博客的人都有题面)
T2以为可以线性递推,然后花了两个小时。然后想了两个小时T1,会了一个能过的算法。但是没时间写,sad.....比赛快结束时,发现T2模数$10^9+7$,内心mmp。
Problem A 生日礼物
题目大意
给定一个$n\times m$的网格图,每个格子中有一个不是0就是1的数,要求对于任意$w\times h$的子矩阵的和都相等,问方案数。
(为了简洁,各式子的范围请自行脑补)
瞎推推不难发现对于一个格子$(x, y)$,满足$a_{x, y} + a_{x + w, y + h} = a_{x + w, y} + a_{x, y + h}$。
移项得:
$\begin{align}a_{x, y} - a_{x + w, y} = a_{x, y + h} - a_{x + w, y + h}\end{align}$
用归纳法能够得出对于每连续的$w + 1$行,对模$h$的剩余相同的列,这$w + 1$行中的第一行和最后一行的上的数之差相等。
对于上图来说就是$a - b = c - d = e - f$。
考虑先确定前$w$行,每次在后面添加一行。显然这一行我们只用考虑前$h$个数(剩下的用式$(1)$来确定)。
考虑每一处$(i, j)$和它上面第$w$个格子上的数的差$d$
- 若$d = 1$,则说明$a_{i - w, j + kh} = 0 (k = 0, 1, \dots)$
- 若$d = 0$,则什么都不能说明
- 若$d = -1$,则说明$a_{i - w, j + kh} = 1 (k = 0, 1, \dots)$
所以变化量能为$1,-1$的只有当这一行上纵坐标模$h$同余于它的位置上的数等于它。
然后发现只要确定左上角的$w\times h$的矩形的状况,剩下的就能用快速幂计算。
然后我的做法就比较沙雕了。
$f_{s}$表示恰好包含$s$中的位置作为横着相同的格子,且将前$w$行填满的方案数。(不考虑这些格子上的数)
这个首先需要考虑这些格子上的数,进行容斥和子集和变换,再将这些格子上的数的贡献减去。
然后单独考虑这个矩阵中每一行的贡献,枚举$0$和$1$的数量,就可以计算贡献了。
Code
1 #include <iostream> 2 #include <cassert> 3 #include <cstdlib> 4 #include <cstdio> 5 using namespace std; 6 typedef bool boolean; 7 8 const int Mod = 1e9 + 7; 9 10 int add(int a, int b) { 11 return ((a += b) >= Mod) ? (a - Mod) : (a); 12 } 13 14 int sub(int a, int b) { 15 return ((a -= b) < 0) ? (a + Mod) : (a); 16 } 17 18 int mul(int a, int b) { 19 return a * 1ll * b % Mod; 20 } 21 22 int qpow(int a, int p) { 23 int rt = 1, pa = a; 24 for ( ; p; p >>= 1, pa = mul(pa, pa)) 25 if (p & 1) 26 rt = mul(rt, pa); 27 return rt; 28 } 29 30 const int S = 1 << 16; 31 32 int n, m, w, h; 33 int f[S], bit[S]; 34 int c[5][5], sc[5][5]; 35 int d[5][5], sd[5][5]; 36 int comb[5][5] = {{1, 0, 0, 0, 0}, {1, 1, 0, 0, 0}, {1, 2, 1, 0, 0}, {1, 3, 3, 1, 0}, {1, 4, 6, 4, 1}}; 37 int invs[17]; 38 39 inline void init() { 40 scanf("%d%d%d%d", &n, &m, &w, &h); 41 } 42 43 int getline(int s, int l) { 44 int rt = (s >> l) & 1; 45 for (int i = 1; i < w; i++) 46 rt = (((s >> (h * i + l)) & 1) << i) | rt; 47 return rt; 48 } 49 50 int getrow(int s, int row) { 51 int msk = (1 << h) - 1; 52 return s >> (h * row) & msk; 53 } 54 55 inline void solve() { 56 d[0][0] = 1; 57 for (int i = 0; i < 4; i++) 58 for (int j = 0; j <= i; j++) { 59 d[i + 1][j] = add(d[i + 1][j], d[i][j]); 60 d[i + 1][j + 1] = add(d[i + 1][j + 1], d[i][j]); 61 } 62 63 int sl_repeat = m / h; 64 for (int i = 0; i <= 4; i++) 65 for (int j = 0; j <= i; j++) 66 sd[i][j] = qpow(d[i][j], sl_repeat); 67 68 bit[0] = 0; 69 for (int i = 0; i < S; i++) 70 bit[i] = bit[i >> 1] + (i & 1); 71 72 int all = 1 << (w * h); 73 for (int s = 0; s < all; s++) { 74 f[s] = (1 << bit[s]); 75 for (int l = 0; l < h; l++) { 76 int sl = getline(s, l); 77 int blank = w - bit[sl]; 78 int cmp = 0; 79 boolean aflag = ((m % h) > l); 80 for (int i = 0; i <= blank; i++) 81 cmp = add(mul(sd[blank][i], (aflag) ? (d[blank][i]) : (1)), cmp); 82 f[s] = mul(f[s], cmp); 83 } 84 } 85 86 int size = w * h; 87 for (int i = 0; i < size; i++) 88 for (int j = 0; j < all; j++) 89 if (!((j >> i) & 1)) 90 f[j] = sub(f[j], f[j ^ (1 << i)]); 91 92 for (int k = 0; k <= 4; k++) 93 for (int c0 = 0; c0 <= k; c0++) { 94 int c1 = k - c0, ava = min(c0, c1); 95 int& res = c[c0][c1]; 96 for (int use = 0; use <= ava; use++) { 97 // res = add(res, mul(comb[c0][use], mul(comb[c1][use], fac[use]))); 98 res = add(res, mul(comb[c0][use], comb[c1][use])); 99 } 100 sc[c0][c1] = qpow(res, n / w - 1); 101 } 102 103 invs[0] = 1; 104 for (int i = 1; i <= 16; i++) 105 invs[i] = qpow(1 << i, Mod - 2); 106 for (int i = 0; i < all; i++) 107 f[i] = mul(f[i], invs[bit[i]]); 108 109 int res = 0; 110 for (int s = 0; s < all; s++) { 111 if (!f[s]) 112 continue; 113 int tmp = 1; 114 for (int r = 0; r < w; r++) { 115 int sr = getrow(s, r); 116 int spe = bit[sr], cmp = 0; 117 boolean aflag = ((n % w) > r); 118 for (int c0 = 0; c0 <= spe; c0++) { 119 cmp = add(cmp, mul(mul(::sc[c0][spe - c0], ((aflag) ? (c[c0][spe - c0]) : (1))), comb[spe][c0])); 120 } 121 tmp = mul(tmp, cmp); 122 // cerr << sr << " " << s << " " << r << " " << cmp << '\n'; 123 } 124 // if (tmp && f[s]) 125 // cerr << s << " " << mul(f[s], tmp) << '\n'; 126 res = add(res, mul(f[s], tmp)); 127 } 128 printf("%d\n", res); 129 } 130 131 int main() { 132 init(); 133 solve(); 134 return 0; 135 }