【题解】CQOI2012局部最小值

  上课讲的一道题,感觉也挺厉害的~正解是容斥 + 状压dp。首先我们容易发现一共可能的局部最小值数量是十分有限的,最多也只有 (8) 个。所以我们可以考虑状压。

  建立出状态 (f[i][j]) 表示我们从小到大往方格当中填数,填完前(i) 个数之后,局部最小值的填充状态为 (j) 的方案数。这样一共有两种转移 :

(f[i][j] = f[i - 1][j] * (g[j] - ((i - 1) - |j|)) + sum f[i][j'])

  分别表示加入了一个局部最小值 / 其他位置的值。其中的 (g[j]) 表示在 (j) 的状态下可以放入的非局部最小值的个数。但这样做出来还是不够的——我们虽然保证了要求的位置上一定是局部最小值,但是其余的位置上也有可能是局部最小值,而这是不符合要求的。我们考虑用容斥来处理,用全集 - 至少有一个非局部最小值成为了局部最小值的方案数,+至少两个,-至少三个……

  每一个dfs出来的状态都用上面的方法dp加入到答案中去就可以了。

#include <bits/stdc++.h>
using namespace std;
#define maxn 400
#define maxm 500
#define int long long
#define mod 12345678
int n, m, N, tot, cnt, ans;
int Map[maxn][maxn], id[maxn][maxn];
int f[maxn][maxm], g[maxm], T;
int dx[10] = {-1, 0, 1, -1, 1, -1, 0, 1};
int dy[10] = {-1, -1, -1, 0, 0, 1, 1, 1};

struct node
{
    int x, y;
}P[maxn];

void Up(int& x, int y) { x = (x + y) % mod; }

int DP()
{
    int K = (1 << tot) - 1;
    for(int k = 0; k <= K; k ++)
    {
        g[k] = 0; int tem = k, len = 0;
        while(tem) len += (tem & 1), tem >>= 1; 
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= m; j ++)
            {
                int flag = 0; if(Map[i][j]) continue;
                for(int l = 0; l < 8; l ++)
                {
                    int x = i + dx[l], y = j + dy[l];
                    if(Map[x][y] && !((1 << (id[x][y] - 1)) & k)) 
                    { flag = 1; break; }
                }   
                if(!flag) g[k] ++;  
            } 
        g[k] += len;
    }
    memset(f, 0, sizeof(f)); f[0][0] = 1;
    for(int i = 1; i <= N; i ++)
        for(int j = 0; j <= K; j ++)
        {
            Up(f[i][j], f[i - 1][j] * (g[j] - (i - 1)));
            int tem = j, len = 0; 
            while(tem) len ++, tem >>= 1; 
            for(int k = 0; k < len; k ++) 
                if(j & (1 << k)) Up(f[i][j], f[i - 1][j ^ (1 << k)]);
        }
    return f[N][K];
}

void DFS(int x, int y)
{
    if(y > m) x += 1, y = 1;
    if(x > n)
    {   
        if((tot - cnt) & 1) ans = (ans - DP() + mod) % mod;
        else ans = (ans + DP()) % mod;
        return;
    }
    DFS(x, y + 1);
    if(Map[x][y]) return;
    for(int i = 0; i < 8; i ++)
        if(Map[x + dx[i]][y + dy[i]]) return;
    Map[x][y] = 1, P[++ tot].x = x, P[tot].y = y; id[x][y] = tot;
    DFS(x, y + 1);
    tot --, Map[x][y] = 0, id[x][y] = 0;
}

signed main()
{
    scanf("%lld%lld", &n, &m); N = n * m;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
        {
            char c; cin >> c;
            if(c == 'X') 
            {
                P[++ tot].x = i, P[tot].y = j;
                Map[i][j] = 1, id[i][j] = tot;
            }
        }
    cnt = tot;
    for(int i = 1; i <= tot; i ++)
    {
        T |= (1 << (i - 1));
        for(int j = 0; j < 8; j ++)
            if(Map[P[i].x + dx[j]][P[i].y + dy[j]]) 
            {
                printf("0");
                return 0;
            }
    }
    DFS(1, 1);
    printf("%lld
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/twilight-sx/p/9456353.html