题目大意:
给一个N * M(N和M都不超过15)的带有一些障碍的棋盘, 考虑用1 * 2的多米诺骨牌去覆盖它, 需满足: 任意相邻两行之间有至少一个骨牌跨越, 任意相邻两列之间有至少一个骨牌跨越, 不要求把棋盘放满, 障碍不能被覆盖. 求方案数.
简要分析:
初看是一道喜闻乐见的插头题...
仔细一想如果要强行记状态的话状态数达到了2^30...但是求出一个棋盘任意放的方案数是可以做到O(N * M * 2 ^ (M + 1))的, 于是考虑能不能用一些子矩阵的随意覆盖的方案数来拼凑出答案. 容斥? 试试看: 整个随意放的答案 - 有1列不满足要求的答案 + 有2列不满要求的答案 - ...
看上去很靠谱啊, 设我们已经有了s[x1][y1][x2][y2]表示一个子矩阵的随意覆盖方案数. 记t[x1][x2]为x1行到x2行的方案数. 若有两列i, j不满足要求(即i列和i + 1列没有跨越, j列和j + 1列没有跨越, 不妨设i < j), 那么t[x1][y1] = s[x1][0][x2][i] * s[x1][i + 1][x2][j] * s[x1][j + 1][x2][m - 1], 其他情况也可以以此类推.
看上去还是很靠谱啊...不过似乎忘记考虑相邻两行也必须跨越了. 再容斥一遍的话, 复杂度太高. 所以我们需要充分利用计算出来的t[x1][x2], 而这个状态对行是没有限制的...如果我们知道第一个不满足要求的行, 那么就可以用补集转化算出答案了! 所以记f[i]为前i行每一行都满足条件的答案, 首先f[i] = t[0][i], 那么我们可以枚举第一个不满足的行j(0 <= j < i), 即第j行是从上往下第一行跟下一行没跨越, 于是把f[i]减掉f[j] * t[j + 1][i]. 这样可以保证方案计数不重不漏~
然后速度果断垫底...难道我还是写得太裸T_T
代码实现:
1 #include <cstdio>
2 #include <cstring>
3
4 template<class T>
5 inline void swap(T &a, T &b) { 6 T t = a; 7 a = b; 8 b = t; 9 } 10
11 #define UPT(x, y) { \
12 (x) += (y); \ 13 if ((x) >= kMod) (x) -= kMod; \ 14 if ((x) < 0) (x) += kMod; \ 15 } 16
17 #define GB(x, p) ((x) >> (p) & 1)
18 #define CB(x, p, b) { \
19 (x) += (((b) - GB(x, p)) << (p)); \ 20 } 21
22 const int kMaxN = 15, kMaxM = 15, kMod = 19901013; 23 int n, m; 24 char buf[kMaxN][kMaxM + 1]; 25 bool inr[kMaxN + 1][kMaxM + 1]; 26
27 struct HashMap { 28 static const int kSize = 65536; 29 int cnt, pos[kSize], end[kSize], val[kSize]; 30 void clear() { 31 cnt = 0; 32 memset(pos, -1, sizeof(pos)); 33 } 34 void push(int x, int v) { 35 if (pos[x] != -1) { 36 UPT(val[pos[x]], v); 37 return; 38 } 39 pos[x] = cnt; 40 end[cnt] = x; 41 val[cnt ++] = v; 42 } 43 int ask(int x) { 44 if (pos[x] != -1) return val[pos[x]]; 45 return 0; 46 } 47 } hash_map[2]; 48
49 HashMap *des = hash_map + 0; 50 HashMap *src = hash_map + 1; 51
52 int ans = 0; 53 int s[kMaxN][kMaxM][kMaxN][kMaxM], t[kMaxN][kMaxN]; 54 int f[kMaxN]; 55
56 inline int CountOne(int x) { 57 int res = 0; 58 while (x) { 59 ++ res; 60 x &= x - 1; 61 } 62 return res; 63 } 64
65 int main() { 66 scanf("%d%d", &n, &m); 67 for (int i = 0; i < n; ++ i) scanf("%s", buf[i]); 68 for (int x1 = 0; x1 < n; ++ x1) 69 for (int y1 = 0; y1 < m; ++ y1) 70 for (int y2 = y1 + 1; y2 <= m; ++ y2) { 71 memset(inr, false, sizeof(inr)); 72 for (int i = x1; i < n; ++ i) 73 for (int j = y1; j < y2; ++ j) 74 if (buf[i][j] == '.') inr[i][j] = true; 75 des->clear(); 76 des->push(0, 1); 77 for (int i = x1; i < n; ++ i) { 78 for (int j = y1; j < y2; ++ j) { 79 swap(des, src); 80 des->clear(); 81 for (int k = 0; k < src->cnt; ++ k) { 82 int u = src->end[k], v, val = src->val[k]; 83 int p = GB(u, j), q = GB(u, j + 1); 84 if (p == 0 && q == 0) { 85 des->push(u, val); 86 if (inr[i][j]) { 87 if (inr[i + 1][j]) { 88 v = u; 89 CB(v, j, 1); 90 des->push(v, val); 91 } 92 if (inr[i][j + 1]) { 93 v = u; 94 CB(v, j + 1, 1); 95 des->push(v, val); 96 } 97 } 98 } 99 else if (!p || !q) { 100 v = u; 101 CB(v, j, 0) CB(v, j + 1, 0); 102 des->push(v, val); 103 } 104 } 105 } 106 swap(des, src); 107 des->clear(); 108 for (int k = 0; k < src->cnt; ++ k) 109 des->push(src->end[k] << 1, src->val[k]); 110 s[x1][y1][i][y2 - 1] = des->ask(0); 111 } 112 } 113 static int log2[1 << (kMaxM - 1)]; 114 for (int i = 0; i < (m - 1); ++ i) log2[1 << i] = i; 115 for (int mask = 0; mask < (1 << (m - 1)); ++ mask) { 116 int sgn = (CountOne(mask) & 1) ? -1 : 1, delta = 0; 117 for (int x1 = 0; x1 < n; ++ x1) 118 for (int x2 = x1; x2 < n; ++ x2) { 119 t[x1][x2] = 1; 120 int now = mask, p, las = -1; 121 while (now) { 122 p = now & -now; 123 now -= p; 124 p = log2[p]; 125 t[x1][x2] = ((long long)t[x1][x2] * s[x1][las + 1][x2][p]) % kMod; 126 las = p; 127 } 128 t[x1][x2] = ((long long)t[x1][x2] * s[x1][las + 1][x2][m - 1]) % kMod; 129 } 130 f[0] = t[0][0]; 131 for (int i = 1; i < n; ++ i) { 132 f[i] = t[0][i]; 133 for (int j = 0; j < i; ++ j) { 134 int d = ((long long)f[j] * t[j + 1][i]) % kMod; 135 UPT(f[i], -d); 136 } 137 } 138 delta = sgn * f[n - 1]; 139 UPT(ans, delta); 140 } 141 printf("%d\n", ans); 142 return 0; 143 }