终于补回来了...
考虑状压DP,设$f_{i,j}$表示已经填了$1\cdots i$,占用局部极小值状态为$j$的方案数,$g_i$表示除去状态为$\text{~}i$的局部极小值格子及其八邻居后还剩多少格子,这些格子满足$i$的局部极小值都被占据后再放新数仍然是合法的
转移时,我们可以把新数放在局部极小值的位置上,也可以放在其他地方,那么转移就是$f_{i,j}=f_{i-1,j}(g_j-(i-1))+\sum\limits_{k\in j}f_{i-1,j-k}$
设题目中给出局部极小值的集合为$S$,那么答案不是$f_{nm,S}$,因为它的含义是“至少满足$S$中格子都是局部极小值”,它还包括了一些$x\notin S$但$x$是局部极小值的情况
所以考虑容斥,答案是$\sum\limits_{S\subset S'}(-1)^{|S'|-|S|}f_{nm,S'}$,搜索$S'$并DP即可,搜索时把$S'-S$中有格子与$S$中格子八邻居的情况剪掉会快很多
#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=12345678,go[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
int mul(int a,int b){return a*(ll)b%mod;}
void inc(int&a,int b){(a+=b)%=mod;}
char s[6][9];
int n,m,cm,pos[26][2];
bool ch[26],mp[6][9];
bool ok(int x,int y){return 0<x&&x<=n&&0<y&&y<=m;}
bool check(){
int i,j,k;
memset(mp,0,sizeof(mp));
for(i=1;i<=cm;i++){
if(ch[i])mp[pos[i][0]][pos[i][1]]=1;
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
if(mp[i][j]){
for(k=0;k<8;k++){
if(mp[i+go[k][0]][j+go[k][1]])return 0;
}
}
}
}
return 1;
}
int f[29][256],g[256],bit[256],xp[10][2],cx;
int dp(){
int N,i,j,k,x,y,tx,ty;
N=1<<cx;
memset(g,0,N<<2);
for(i=0;i<N;i++){
memset(mp,0,sizeof(mp));
for(j=0;j<cx;j++){
x=xp[j+1][0];
y=xp[j+1][1];
if(~i>>j&1){
mp[x][y]=1;
for(k=0;k<8;k++){
tx=x+go[k][0];
ty=y+go[k][1];
if(ok(tx,ty))mp[tx][ty]=1;
}
}
}
for(j=1;j<=n;j++){
for(k=1;k<=m;k++)g[i]+=!mp[j][k];
}
}
f[0][0]=1;
for(i=1;i<=n*m;i++){
for(j=0;j<N;j++){
if(i>=bit[j]){
f[i][j]=mul(f[i-1][j],g[j]-(i-1));
for(k=0;k<cx;k++){
if(j>>k&1)inc(f[i][j],f[i-1][j^(1<<k)]);
}
}
}
}
return f[n*m][N-1];
}
int ans;
void dfs(int now,int siz){
if(now>cm)return;
ch[now]=0;
dfs(now+1,siz);
ch[now]=1;
if(check()){
siz++;
cx++;
xp[cx][0]=pos[now][0];
xp[cx][1]=pos[now][1];
inc(ans,(siz&1?-1:1)*dp());
dfs(now+1,siz);
cx--;
}
ch[now]=0;
}
int main(){
int i,j,k,x,y;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%s",s[i]+1);
for(j=1;j<=m;j++){
if(s[i][j]=='X'){
cx++;
xp[cx][0]=i;
xp[cx][1]=j;
}
}
}
if(cx==0){
putchar('0');
return 0;
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
if(s[i][j]=='.'){
for(k=0;k<8;k++){
x=i+go[k][0];
y=j+go[k][1];
if(ok(x,y)&&s[x][y]=='X'){
k=-1;
break;
}
}
if(~k){
cm++;
pos[cm][0]=i;
pos[cm][1]=j;
}
}
}
}
for(i=1;i<256;i++)bit[i]=bit[i>>1]+(i&1);
ans=dp();
dfs(1,0);
inc(ans,mod);
printf("%d",ans);
}