2016 Multi-University Training Contest 1 I. Solid Dominoes Tilings
Solid Dominoes Tilings
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 235 Accepted Submission(s): 143
Problem Description
Dominoes are rectangular tiles with nice 2 × 1 and 1 × 2 sizes.
The tiling is called solid if it is not possible to split the tiled rectangle by a straight line, not crossing the interior of any tile. For example, on the picture below the tilings (a) and (b) are solid, while the tilings (c) and (d) are not.
![](http://acm.split.hdu.edu.cn/data/images/C704-1009-1.jpg)
Now the managers of the company wonder, how many different solid tilings exist for an m × n rectangle. Help them to find that out.
The tiling is called solid if it is not possible to split the tiled rectangle by a straight line, not crossing the interior of any tile. For example, on the picture below the tilings (a) and (b) are solid, while the tilings (c) and (d) are not.
![](http://acm.split.hdu.edu.cn/data/images/C704-1009-1.jpg)
Now the managers of the company wonder, how many different solid tilings exist for an m × n rectangle. Help them to find that out.
Input
The input file contains m and n(1≤m,n≤16).
Output
Output one integer number mod 1e9+7 - the number of solid tilings of m×n rectangle with 2 × 1 and 1 × 2 pavement tiles.
Sample Input
2 2
5 6
8 7
Sample Output
0
6
13514
Hint
All solid tilings for the 5×6 rectangle are provided on the picture below:
![](http://acm.split.hdu.edu.cn/data/images/C704-1009-2.jpg)
题意: 在经典的多米诺骨牌覆盖上加入限制: 覆盖后的骨牌不能被一条直线(横切、竖切)分成两部分。 求合法覆盖的方案数。
题解: ....这道题...以它的数据组数,除了在预处理出所有答案后打表,貌似没办法了..... 还有题解里面说的轮廓线dp是什么鬼? 不是状压dp一下就搞定了吗? 当然,这题比较好的一点,也是卡住我的一点就是其中的一个容斥过程。 1、首先状压dp出g[n][m]表示大小为n*m的时候,随意放置的方案数。 可以先深搜出所有的合法转移状态,存储到邻接表中。 显然合法转移数很少,那么,即使是在m=16时也只有100w种。 1<=m<=16的总转移数也只有200w种 所以这一步的总复杂度为O(n*总转移数) 2、接着,暴力来说,我们对于每个矩阵只要枚举矩阵的列的分割线,就能容斥了。 这样容斥出来的是列不能分割的情况。 行的如果在容斥一遍就不行了。 我一开始完全没有想到预处理答案打表。。。造成了思路的阻塞。。。 其实对于某种列的分割情况来说, 行的情况,是可以递推得: 令F[n]表示长为n的棋盘在枚举的宽度m且在枚举的切割方案下的行稳定方案。 那么显然 F[n] = g[n][m] - F[1]*g[n-1][m] - ..... -F[n - 1]*g[1][m] 即,利用第一条分割线做容斥。 这一步要O(n^2) 所以整个第二步打表过程要O(sigma(2^(m-1)*n*n), 1<= m <= 16) 即O(2^m * n^2) 从复杂度来说,我的方法应该是比较快的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 const int N = 18, M = 2000010, MOD = 1e9 + 7; 2 int head[1 << N], son[M], nex[M], tot; 3 int ans[N][N], blocks[N]; 4 int width; 5 int G[N][1 << N], g[N][N]; 6 7 inline int add(int x, int y) { 8 return ((x + y) % MOD + MOD) % MOD; 9 } 10 11 inline int mul(int x, int y) { 12 return ((x * 1ll * y) % MOD + MOD) % MOD; 13 } 14 15 inline void addEdge(int u, int v) { 16 son[tot] = v, nex[tot] = head[u]; 17 head[u] = tot++; 18 } 19 20 inline void searchNexState(int goalState, int nowState, int d) { 21 if(d == width) addEdge(goalState, nowState); 22 else if((goalState >> d) & 1) { 23 if(d < width - 1 && (goalState >> (d + 1) & 1)) { 24 int nexState = nowState; 25 nexState |= (1 << d) | (1 << (d + 1)); 26 searchNexState(goalState, nexState, d + 2); 27 } 28 searchNexState(goalState, nowState, d + 1); 29 } else searchNexState(goalState, nowState | (1 << d), d + 1); 30 } 31 32 inline void getTransfer(int n) { 33 width = n, tot = 0; 34 for(int i = 0; i < (1 << n); ++i) { 35 head[i] = -1; 36 searchNexState(i, 0, 0); 37 } 38 // printf("%d\n", tot); 39 } 40 41 inline void getG(int n, int m) { 42 for(int tab = head[(1 << m) - 1]; tab != -1; tab = nex[tab]) 43 G[1][son[tab]] = 1; 44 for(int i = 1; i < n; ++i) { 45 for(int u = 0; u < (1 << m); ++u) G[i + 1][u] = 0; 46 for(int u = 0; u < (1 << m); ++u) { 47 if(G[i][u]) { 48 for(int tab = head[u]; tab != -1; tab = nex[tab]) 49 G[i + 1][son[tab]] = add(G[i + 1][son[tab]], G[i][u]); 50 } 51 } 52 } 53 for(int i = 1; i <= n; ++i) g[i][m] = G[i][(1 << m) - 1]; 54 } 55 56 inline void search(int w, int now, int len) { 57 if(w >= width) { 58 blocks[len++] = now; 59 static int F[N], G[N]; 60 for(int n = 1; n <= 16; ++n) { 61 int cnt = 1; 62 for(int i = 0; i < len; ++i) cnt = mul(cnt, g[n][blocks[i]]); 63 F[n] = G[n] = cnt; 64 for(int h = 1; h < n; ++h) 65 F[n] = add(F[n], -mul(F[h], G[n - h])); 66 if(len & 1) ans[n][width] = add(ans[n][width], F[n]); 67 else ans[n][width] = add(ans[n][width], -F[n]); 68 } 69 } else { 70 search(w + 1, now + 1, len); 71 blocks[len] = now; 72 search(w + 1, 1, len + 1); 73 } 74 } 75 76 inline void init() { 77 for(int m = 1; m <= 16; ++m) { 78 width = m; 79 getTransfer(m); 80 getG(16, m); 81 search(1, 1, 0); 82 } 83 } 84 85 int n, m; 86 int main() { 87 init(); 88 while(scanf("%d%d", &n, &m) == 2) printf("%d\n", ans[n][m]); 89 return 0; 90 }