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个,于是状态压缩。
这题大致的思路已经出来了。
只需要考虑状态压缩的过程。
假设当前状态为
表示放到第
个数,极小值位置情况是
考虑
最后放的位置。
如果放在某个极小值上面,那就让
让出一个极小值的位置
如果不放在极小值上面,那么不可放在任何一个还没有被放的极小值的旁边,因为之后放的数肯定比当前的数大。
其中
为状态
下可以放的位置个数。
时间复杂度是
其中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;
}