[除草]BZOJ 1435 [ZJOI2009]多米诺骨牌
题目大意:
给一个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 }