题意:一个n∗m的矩阵,填1到n∗m 的数,有的位置必须小于八连通的相邻位置,求方案数。
设f[i][j] 表示填到i,已经填完的局部最小值的集合为j的方案数。
设cnt[j] 表示对于局部最小值的集合j,不与除j外局部最小值的集合相邻的点的个数。
f[i][j]=f[i−1][j]∗(cnt[j]−i+1)+∑f[i][j−k] (j集合中有元素k)
这样没有考虑除j集合外的点成为局部最小值的情况,深搜容斥一下就行了。
#include <bits/stdc++.h>
using namespace std;
#define mod 12345678
int n,m,sum,ans;
int d[5]={-1,0,1};
char s[11][11];
int pos[11][11],f[29][(1<<9)+10],cnt[(1<<9)+10];
int cal(int x)
{
for(int i=0;i<1<<x;i++)
{
cnt[i]=0;
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
{
int flag=1;
for(int t=0;t<3;t++)
for(int w=0;w<3;w++)
if(s[j+d[t]][k+d[w]]=='X'&&(!(i>>pos[j+d[t]][k+d[w]]-1&1)))
flag=0;
cnt[i]+=flag;
}
}
f[0][0]=1;
for(int i=1;i<=n*m;i++)
for(int j=0;j<1<<x;j++)
{
f[i][j]=f[i-1][j]*(cnt[j]-i+1)%mod;
for(int k=1;k<1<<x;k<<=1)
if(j&k)(f[i][j]+=f[i-1][j-k])%=mod;
}
return f[n*m][(1<<x)-1];
}
void dfs(int x,int y,int cnt)
{
if(x==n+1)
{
if((cnt-sum)&1)(ans-=cal(cnt))%=mod;
else (ans+=cal(cnt))%=mod;
return;
}
int nx=x,ny=y+1;
if(ny==m+1)ny=1,nx++;
if(s[x][y]=='X')
{
pos[x][y]=cnt+1,dfs(nx,ny,cnt+1);
pos[x][y]=0;
}
else
{
dfs(nx,ny,cnt);
int flag=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(s[x+d[i]][y+d[j]]=='X')flag=1;
if(!flag)
{
s[x][y]='X';pos[x][y]=cnt+1;
dfs(nx,ny,cnt+1);
pos[x][y]=0;s[x][y]='.';
}
}
}
int main()
{
//freopen("tt.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]+1);
for(int j=1;j<=m;j++)
if(s[i][j]=='X')
{
sum++;
if(s[i][j-1]=='X'||s[i-1][j]=='X'
||s[i-1][j-1]=='X'||s[i-1][j+1]=='X')
return puts("0"),0;
}
}
dfs(1,1,0);
printf("%d\n",(ans+mod)%mod);
return 0;
}