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.



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(1m,n16).
 

 

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:
 
 
题意:
    在经典的多米诺骨牌覆盖上加入限制:
        覆盖后的骨牌不能被一条直线(横切、竖切)分成两部分。
    求合法覆盖的方案数。
题解:
    ....这道题...以它的数据组数,除了在预处理出所有答案后打表,貌似没办法了.....
    还有题解里面说的轮廓线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)

从复杂度来说,我的方法应该是比较快的。

 

 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 }
View Code

 

 

 

posted @ 2016-08-28 20:23  yanzx6  阅读(225)  评论(0编辑  收藏  举报