bzoj2669: [cqoi2012]局部极小值 计数Dp 状态压缩Dp与容斥

bzoj2669: [cqoi2012]局部极小值

Description

有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次。如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值。
给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵。

Input

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

Output

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

Sample Input

3 2
X.
..
.X

Sample Output

60

分析

计数Dp的一道难题,但是处处是套路。
首先1到nm之间的每个整数恰好出现一次,肯定是从小到大放。
其次,给出所有局部极小值的位置,包含了一个“有且仅有”这些最小值的条件,数据范围又好小。
于是容斥。
想考虑包含若干个极小值的情况,然后一个一个枚举其他最小值存在的情况即可。
然后不难发现最小值最多8个,于是状态压缩。
这题大致的思路已经出来了。
只需要考虑状态压缩的过程。
假设当前状态为 f i , s 表示放到第 i 个数,极小值位置情况是 s
考虑 i 最后放的位置。
如果放在某个极小值上面,那就让 s 让出一个极小值的位置
f i 1 , t f i , s , t = s { i } , i s
如果不放在极小值上面,那么不可放在任何一个还没有被放的极小值的旁边,因为之后放的数肯定比当前的数大。
f i 1 , s m a x ( c n t [ s ] i + 1 , 0 ) f i , s 其中 c n t [ s ] 为状态 s 下可以放的位置个数。
时间复杂度是 O ( D f s 2 8 n m ) 其中Dfs是容斥的复杂度,大概10000吧。

代码

/**************************************************************
    Problem: 2669
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:60 ms
    Memory:1352 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int P = 12345678;
int n, m, tim, ret, top, ans, b[4][7], x[8], y[8], bin[9], f[29][512], cnt[512];
bool a[4][7];
bool Paint(int x, int y, bool p) {
    for(int dx = -1; dx <= 1; ++dx)
        for(int dy = -1; dy <= 1; ++dy) {
            int nx = x + dx, ny = y + dy; 
            if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if(p && a[nx][ny]) return false;
            if(!p && b[nx][ny] != tim) b[nx][ny] = tim, ++ret;
        }
    if(!p && b[x][y] != tim) b[x][y] = tim, ++ret;
    return true;
}
void Up(int &x, int y) {x += y; if(x >= P) x -= P; if(x < 0) x += P;}
int Dp() {
    top = 0;
    for(int i = 0;i < n; ++i)
        for(int j = 0;j < m; ++j)
            if(a[i][j]) x[top] = i, y[top++] = j;
    for(int s = 0;s < bin[top]; ++s) {
        ret = 0; ++tim;
        for(int i = 0;i < top; ++i) if(~s & bin[i])
            Paint(x[i], y[i], 0);
        cnt[s] = n * m - ret;
    }
    f[0][0] = 1;
    for(int i = 1;i <= n * m; ++i)
        for(int s = 0;s < bin[top]; ++s) {
            f[i][s] = 1LL * f[i - 1][s] * max(cnt[s] - i + 1, 0) % P;
            for(int j = 0;j < top; ++j) if(s & bin[j]) 
                Up(f[i][s], f[i - 1][s ^ bin[j]]);
        }
    return f[n * m][bin[top] - 1];
}
void Nxt(int,int,int);
void Dfs(int,int,int);
void Dfs(int i, int j, int s) {
    if(i == n) {Up(ans, s * Dp()); return;}
    Nxt(i, j, s); 
    if(!a[i][j] && Paint(i, j, 1)) {
        a[i][j] = 1;
        Nxt(i, j, -s);
        a[i][j] = 0;
    }
}
void Nxt(int i, int j, int s) {
    if(j == m - 1) Dfs(i + 1, 0, s);
    else Dfs(i, j + 1, s);
}
int main() {
    bin[0] = f[0][0] = 1; for(int i = 1;i <= 8; ++i) bin[i] = bin[i - 1] << 1;
    scanf("%d%d", &n, &m); char s[7];
    for(int i = 0;i < n; ++i) {
        scanf("%s", s);
        for(int j = 0; j < m; ++j)
            a[i][j] = s[j] == 'X';
    }
    Dfs(0, 0, 1);
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/80024836