[BZOJ2669][CQOI2012]局部极小值

题目描述

有一个\(n\)\(m\)列的整数矩阵,其中\(1\)\(n*m\)之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。

给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

Input

输入第一行包含两个整数\(n\)\(m\)\(1<=n<=4, 1<=m<=7\)),即行数和列数。以下\(n\)行每行\(m\)个字符,其中“\(X\)”表示局部极小值,“\(.\)”表示非局部极小值。

Output

输出仅一行,为可能的矩阵总数除以\(12345678\)的余数。

Sample Input

3 2
X.
..
.X

Sample Output

60

首先,我们发现矩阵的大小很小,而局部最小值的位置也很少,最多只有\(8\)个最小值。

\[\begin{matrix} X & . & X &.&X&.&X \\ . & . & . &.&.&.&. \\ X & . & X &.&X&.&X \\ . & . & . &.&.&.&. \\ \end{matrix} \]

于是我们可以考虑爆搜,搜出局部最小值的位置,然后在对于每种局部最小值的方案计算方案数。

对于每种局部最小值已知的情况,我们定义\(dp[i][j]\)表示填到第\(i\)个数,局部最小值被填的状态为\(j\)的方案数。

我们考虑计算出没种状态的随便填的位置为\(Sum_i\),则对于当前\(dp[i][j]\)有两种抉择。

1.随便填,即不对\(j\)造成影响,\(dp[i][j]+=dp[i-1][j]*max(Sum_j-i+1,0)\)

2.填到局部最小值上,即\(dp[i][j|1<<k]+=dp[i][j],k\notin i\)

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\)为局部最小值的数量。

\(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;
}
posted @ 2019-10-08 20:32  dsjkafdsaf  阅读(257)  评论(0编辑  收藏  举报