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

你可能感兴趣的:(ZOJ)