天天看點

【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 月賽」白金元首與莫斯科
  • 時間複雜度\(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;
}
           

繼續閱讀