[除草]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

 

代码实现:

View Code
  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 }
posted @ 2012-05-27 22:15  zcwwzdjn  阅读(1731)  评论(0编辑  收藏  举报