有一個\(n\)行\(m\)列的整數矩陣,其中\(1\)到\(n*m\)之間的每一個整數剛好出現一次。若是一個格子比全部相鄰格子(相鄰是指有公共邊或公共頂點)都小,咱們說這個格子是局部極小值。node
給出全部局部極小值的位置,你的任務是判斷有多少個可能的矩陣。c++
輸入第一行包含兩個整數\(n\)和\(m\)(\(1<=n<=4, 1<=m<=7\)),即行數和列數。如下\(n\)行每行\(m\)個字符,其中「\(X\)」表示局部極小值,「\(.\)」表示非局部極小值。spa
輸出僅一行,爲可能的矩陣總數除以\(12345678\)的餘數。debug
3 2 X. .. .X
60
首先,咱們發現矩陣的大小很小,而局部最小值的位置也不多,最多隻有\(8\)個最小值。
\[ \begin{matrix} X & . & X &.&X&.&X \\ . & . & . &.&.&.&. \\ X & . & X &.&X&.&X \\ . & . & . &.&.&.&. \\ \end{matrix} \]
因而咱們能夠考慮爆搜,搜出局部最小值的位置,而後在對於每種局部最小值的方案計算方案數。code
對於每種局部最小值已知的狀況,咱們定義\(dp[i][j]\)表示填到第\(i\)個數,局部最小值被填的狀態爲\(j\)的方案數。get
咱們考慮計算出沒種狀態的隨便填的位置爲\(Sum_i\),則對於當前\(dp[i][j]\)有兩種抉擇。input
1.隨便填,即不對\(j\)形成影響,\(dp[i][j]+=dp[i-1][j]*max(Sum_j-i+1,0)\)。it
2.填到局部最小值上,即\(dp[i][j|1<<k]+=dp[i][j],k\notin i\)。ast
dp[0][0] = 1; rep(i, 1, n * m) ret(j, 0, 1 << tot) { dp[i][j] += (dp[i - 1][j] * max(0ll, Sum[j] - i + 1) ) % mod; rep(k, 1, tot)if (!(j & 1 << k - 1))dp[i][j | (1 << k - 1)] += dp[i - 1][j]; }
當前狀態的\(ans\)即爲\(dp[n*m][(1<<tot)-1]\),\(tot\)爲局部最小值的數量。class
而\(Sum_j\)即\(n*m-\)當前狀態全部局部最小值以及其周圍\(8\)個格子的數量。
因爲,一個矩陣可能重複被算屢次,因此咱們要容斥,根據添加的格子的數量進行容斥。
#include <bits/stdc++.h> using namespace std; #define int long long #define reg register #define clr(a,b) memset(a,b,sizeof a) #define Mod(x) (x>=mod)&&(x-=mod) #define abs(a) ((a)<0?-(a):(a)) #define debug(x) cerr<<#x<<"="<<x<<endl; #define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl; #define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl; #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a) #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a) #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a) #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i]) #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(3,"Ofast","inline") inline int Read(void) { int res = 0, f = 1; char c; while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0; do res = (res << 3) + (res << 1) + (c ^ 48); while (c = getchar(), c >= 48 && c <= 57); return f ? res : -res; } template<class T>inline bool Min(T &a, T const&b) {return a > b ? a = b, 1 : 0;} template<class T>inline bool Max(T &a, T const&b) {return a < b ? a = b, 1 : 0;} const int N = 3e2 + 5, M = 1e5 + 5, K = 10, mod = 12345678; const int dx[8] = {1, 0, -1, 1, -1, 1, 0, -1}; const int dy[8] = { -1, -1, -1, 0, 0, 1, 1, 1}; bool MOP1; int n, m, A[9][9], vis[9][9], us[9][9], dp[30][1 << 9], Sum[1 << 9], Ans; char S[15]; inline int X(int x) {return (x + m - 1) / m;} inline int Y(int x) {return x % m ? x % m : m;} struct node { int x, y; } B[15]; void dfs(int pos) { if (pos == n * m + 1) { int tot = 0; rep(i, 1, n)rep(j, 1, m)if (vis[i][j])B[++tot] = (node) {i, j}; clr(dp, 0); ret(i, 0, 1 << tot) { clr(us, 0); int res = 0; rep(j, 1, tot)if (!(i & 1 << j - 1)) { int x = B[j].x, y = B[j].y; if (!us[x][y])us[x][y] = 1, res++; rep(k, 0, 7) { int Dx = B[j].x + dx[k], Dy = B[j].y + dy[k]; if (Dx < 1 || Dy < 1 || Dx > n || Dy > m)continue; if (!us[Dx][Dy])us[Dx][Dy] = 1, res++; } } Sum[i] = n * m - res; } dp[0][0] = 1; rep(i, 1, n * m) ret(j, 0, 1 << tot) { dp[i][j] += (dp[i - 1][j] * max(0ll, Sum[j] - i + 1) ) % mod, Mod(dp[i][j]); rep(k, 1, tot)if (!(j & 1 << k - 1))dp[i][j | (1 << k - 1)] += dp[i - 1][j], Mod(dp[i][j | (1 << k - 1)]); } int op = tot; rep(i, 1, n)rep(j, 1, m)if (A[i][j])op--; if (op & 1) Ans += dp[n * m][(1 << tot) - 1], Mod(Ans); else Ans += mod - dp[n * m][(1 << tot) - 1], Mod(Ans); return; } int x = X(pos), y = Y(pos); if (A[x][y]) { dfs(pos + 1); return; } int flag = 1; rep(i, 0, 7) { int Dx = x + dx[i], Dy = y + dy[i]; if (vis[Dx][Dy])flag = 0; } if (flag) { vis[x][y] = 1; dfs(pos + 1); vis[x][y] = 0; } dfs(pos + 1); } bool MOP2; void _main(void) { n = Read(), m = Read(); rep(i, 1, n) { scanf("%s", S); ret(j, 0, m)vis[i][j + 1] = A[i][j + 1] = (S[j] == 'X'); } Ans = 0; dfs(1); printf("%lld\n", mod - Ans); } signed main() { _main(); return 0; }