【题目链接】
【思路要点】
- 考虑从前向后、从后向前各做一次状压DP,在询问时合并信息。
- 注意到问题等价于用\(1*2\)和\(1*1\)的棋子填满棋盘,我们可以把\(1*1\)的棋子一并在状压DP时考虑进去。
- 合并答案时只需要枚举\(2^N\)个状态,将满足条件的DP值相乘,累加入答案即可。
- 举例来说,在下图中,合法的状态应当满足在标号为4处已经填上棋子,在标号和为3的绿色和蓝色方格,是否填上棋子的状态应当是一样的。显然对于每一个这样的情况,有且只有一种填补方案:用\(1*2\)的棋子竖直填补所有的空格。
- 时间复杂度\(O(2^N*N^2)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 20; const int MAXS = 131072; const int P = 1e9 + 7; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, u, bit[MAXN], mp[MAXN][MAXN]; int dp[MAXN][MAXN][MAXS], pd[MAXN][MAXN][MAXS]; void update(int &x, int y) {x = (x + y) % P; } void work() { dp[1][1][0] = 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { int tx = i, ty = j + 1; if (j == m) tx++, ty = 1; for (int s = 0; s <= u; s++) if (dp[i][j][s] != 0) { int tmp = dp[i][j][s]; if ((s & bit[j]) != 0 && mp[i][j]) continue; if ((s & bit[j]) != 0) { update(dp[tx][ty][s ^ bit[j]], tmp); continue; } if (mp[i][j]) update(dp[tx][ty][s], tmp); else { update(dp[tx][ty][s], tmp); update(dp[tx][ty][s ^ bit[j]], tmp); if (j != 1 && (s & bit[j - 1]) != 0) update(dp[tx][ty][s ^ bit[j - 1]], tmp); } } } pd[n][m][0] = 1; for (int i = n; i >= 1; i--) for (int j = m; j >= 1; j--) { int tx = i, ty = j - 1; if (j == 1) tx--, ty = m; for (int s = 0; s <= u; s++) if (pd[i][j][s] != 0) { int tmp = pd[i][j][s]; if ((s & bit[j]) != 0 && mp[i][j]) continue; if ((s & bit[j]) != 0) { update(pd[tx][ty][s ^ bit[j]], tmp); continue; } if (mp[i][j]) update(pd[tx][ty][s], tmp); else { update(pd[tx][ty][s], tmp); update(pd[tx][ty][s ^ bit[j]], tmp); if (j != m && (s & bit[j + 1]) != 0) update(pd[tx][ty][s ^ bit[j + 1]], tmp); } } } } int main() { read(n), read(m); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) read(mp[i][j]); bit[1] = 1; for (int i = 2; i <= m; i++) bit[i] = bit[i - 1] << 1; u = (1 << m) - 1; work(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (mp[i][j]) { printf("0 "); continue; } int ans = 0; for (int s = 0; s <= u; s++) { if (s & bit[j]) continue; update(ans, 1ll * dp[i][j][s] * pd[i][j][s] % P); } printf("%d ", ans); } printf("\n"); } return 0; }