【BZOJ5205】【LOJ6301】「CodePlus 2018 3 月赛」白金元首与莫斯科

【题目链接】

  • 点击打开链接

【思路要点】

  • 考虑从前向后、从后向前各做一次状压DP,在询问时合并信息。
  • 注意到问题等价于用\(1*2\)和\(1*1\)的棋子填满棋盘,我们可以把\(1*1\)的棋子一并在状压DP时考虑进去。
  • 合并答案时只需要枚举\(2^N\)个状态,将满足条件的DP值相乘,累加入答案即可。
  • 举例来说,在下图中,合法的状态应当满足在标号为4处已经填上棋子,在标号和为3的绿色和蓝色方格,是否填上棋子的状态应当是一样的。显然对于每一个这样的情况,有且只有一种填补方案:用\(1*2\)的棋子竖直填补所有的空格。
    【BZOJ5205】【LOJ6301】「CodePlus 2018 3 月赛」白金元首与莫斯科_第1张图片
  • 时间复杂度\(O(2^N*N^2)\)。

【代码】

#include
using namespace std;
const int MAXN = 20;
const int MAXS = 131072;
const int P = 1e9 + 7;
template  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  void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template  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;
}

你可能感兴趣的:(【算法】压位,【算法】枚举子集,【算法】FWT,【OJ】LOJ,【类型】做题记录,【算法】动态规划,【OJ】BZOJ)