蒙德里安的梦想
题面
求把 \(N \times M\) 的棋盘分割成若干个 \(1 \times 2\) 的长方形,有多少种方案
例如当 \(N = 2, M = 4\) 时,共有 5 中方案。当 \(N = 2, M = 3\) 时,共有 3 种方案

\(1 \le N, M \le 11\)
题解
可以先转化一下题意,其实可以转化为放横着方块的合法方案数,如果横着的方块已经放好了,那么要将整个棋盘填满,放竖着的方块也只有一种方案
那么考虑两个问题
怎么判断一个已经放好横块的棋盘是否合法,怎么放横块?
我们放好横块后,只需要对于每一列看连续空格的个数是否都是偶数即可
可以这样放横块,设 \(f(i,j)\) 表示已经放好了前 \(i - 1\) 列,从 \(i - 1\) 列到第 \(i\) 列的横块的集合为 \(j\) 的方案数
因为只有 \(1 \times 2\) 的横块,所以我们只需要考虑 \(i - 2 \to i - 1\) 是如何放的,会不会影响到当前这列的状态
设当前状态为 \(j\) ,前面的状态为 \(k\) ,两个状态一定不能有交集,也就是 \(j \& k = 0\) 。
另外,如果 \(k, j\) 的状态都确定了,那么第 \(i - 1\) 列的状态也就确定了,又因为我们要保证每一列的连续空格数是否为偶数,所以我们还需要判断一下 \(j \mid k\) 这个状态
朴素写法是 \(O(n^24^n)\) 的,会TLE
所以我们加些预处理,具体的时间复杂度多少我也不知道,会很低
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>using namespace std;typedef long long ll;const int N = 13, M = (1 << 11) + 7;int n, m;
ll f[N][M];
bool st[M];
vector <int> fac[M];void solve () {memset (st, 0, sizeof st);for (int i = 0; i < (1 << n); i ++) {while (fac[i].size ()) fac[i].pop_back ();}//预处理出合法的匹配状态for (int i = 0; i < (1 << n); i ++) {int cnt = 0, ok = 1;for (int k = 0; k < n; k ++) {if ((i >> k) & 1) {if (cnt & 1) ok = 0;cnt = 0;} else {cnt ++;}}if (cnt & 1) ok = 0;st[i] = ok;}for (int i = 0; i < (1 << n); i ++) {for (int j = 0; j < (1 << n); j ++) {if (i & j) continue;if (st[i | j]) {fac[i].push_back (j);}}}//dp转移memset (f, 0, sizeof f);f[0][0] = 1;for (int i = 1; i <= m; i ++) {for (int j = 0; j < (1 << n); j ++) {for (auto k : fac[j]) {f[i][j] += f[i - 1][k];}}}cout << f[m][0] << endl;
}int main () {while ((cin >> n >> m) && (n || m)) {solve ();}return 0;
}