Codeforces Round #199 (Div. 2) D. Xenia and Dominoes
把 'O' 看成 'X',然后枚举它的四个方向看看是否能放,然后枚举 $2^4$ 种可能表示每种方向是否放了,放了的话就标成 'X',就相当于容斥,对于新的图去dp。
dp就是铺地砖,行用二进制来表示是否放了砖块。
#include <bits/stdc++.h> const int MOD = 1e9 + 7; const int N = 1e4 + 7; const int dir[4][2] = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}}; int dp[N][1 << 3], n, sx, sy; char s[3][N]; int mp1[3][N], mp[3][N]; void M(int &a) { if (a >= MOD) a -= MOD; if (a < 0) a += MOD; } int get(int x) { int cnt = 0; while (x) { cnt++; x &= (x - 1); } return cnt; } int DP() { for (int i = 0; i < 8; i++) for (int j = 0; j <= n; j++) dp[j][i] = 0; dp[0][7] = 1; for (int i = 1; i <= n; i++) { int no = mp[0][i] + mp[1][i] * 2 + mp[2][i] * 4; for (int j = 0; j < 8; j++) { if (j & no) continue; dp[i][j | no] = dp[i - 1][7 - j]; if (j == 3 || j == 6) { M(dp[i][j | no] += dp[i - 1][7]); } if (j == 7) { M(dp[i][j | no] += dp[i - 1][3]); M(dp[i][j | no] += dp[i - 1][6]); } } } return dp[n][7]; } int main() { scanf("%d", &n); for (int i = 0; i < 3; i++) scanf("%s", s[i] + 1); for (int i = 0; i < 3; i++) for (int j = 1; j <= n; j++) if (s[i][j] == 'O') sx = i, sy = j, mp1[i][j] = 1; else if (s[i][j] == 'X') mp1[i][j] = 1; std::vector<int> vec; for (int i = 0; i < 4; i++) { bool flag = 1; int x = sx + dir[i][0] * 2, y = sy + dir[i][1] * 2; if (x >= 0 && x < 3 && y >= 1 && y <= n) { for (int j = 1; j <= 2; j++) { if (mp1[sx + dir[i][0] * j][sy + dir[i][1] * j]) flag = 0; } } else { flag = 0; } if (flag) vec.push_back(i); } int S = 1 << vec.size(); int ans = 0; for (int s0 = 1; s0 < S; s0++) { memcpy(mp, mp1, sizeof(mp)); for (int j = 0; j < vec.size(); j++) { int i = vec[j]; if (s0 >> j & 1) { for (int k = 1; k <= 2; k++) { int x = sx + dir[i][0] * k, y = sy + dir[i][1] * k; mp[x][y] = 1; } } } int f = get(s0) & 1 ? 1 : -1; M(ans += f * DP()); } printf("%d\n", ans); return 0; }