题目链接:洛谷 BZOJ

这道题的方法是用dfs进行搜索容斥时,dp统计答案(感觉痛苦的同学建议做做)

一个局部最小值就可以覆盖最少四个点(放在四个角上),最多九个(放在中间)

所以最多只会有8个局部最小值,可以考虑一下状压

我们可以考虑从小到大地放入每个元素

dp[i][j]表示在选择的局部最小值状态为j时,已经选了i个数,此时的方案数

这张图里面X代表的是局部最小值,红色的代表已经被选择了

那么思考dp[i][j]应该从哪里转移过来

如果这次选的不是局部最小值,那么思考可以选取哪些位置

所有的局部最小值的位置已经不能放了

还未选取的局部最小值周围的所有位置也不能放

这个可以通过预处理每个状态得出总共有多少个位置,记为cnt[j]

其他的点就都可以选取了,总共有n*m-cnt[j]-(i-1)个

所以转移方程就是dp[i][j]+=dp[i−1][j]×(cnt[j]−i+1);

如果选取的是局部最小值,那么就枚举这次选的是第k个局部最小值

转移方程就是dp[i][j]+=dp[i-1][j^(1<<k)];

然后我们来思考这样一个问题:

我们只保证了这几个一定是局部最小值,外面会不会有其他的局部最小值呢?

所以统计的方案是有多余的

考虑用容斥来解决这个问题

减去外面有一个多余的方案,再加上两个多余的方案,等等...也就是奇加偶减

用dfs来实现这个过程,最多能够搜索到2的8次方个状态,所以复杂度是有保障的

跑得非常快

#include<bits/stdc++.h>
using namespace std;
const int mod=12345678;
int dx[9]={0,-1,-1,-1,0,1,1,1,0};
int dy[9]={1,1,0,-1,-1,-1,0,1,0};
int ans,n,m,posx[10],posy[10],cnt[100005],maxa,vis[5][10];
int dp[30][305];
char a[5][10];
int moc(int x)
{
	if(x>=mod) return x-mod;
	else if(x<0) return x+mod;
	return x;
}
int dpp()
{
	memset(dp,0,sizeof(dp));
	maxa=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(a[i][j]=='X') 
			{
				posx[++maxa]=i;
				posy[maxa]=j;
			}
		}
	}
	for(int i=0;i<(1<<maxa);i++)
	{
		memset(vis,0,sizeof(vis));
		cnt[i]=n*m;
		for(int j=0;j<maxa;j++)
		{
			if(!((1<<j)&i))
			{
				for(int k=0;k<9;k++)
				{
					int xx=posx[j+1]+dx[k];
					int yy=posy[j+1]+dy[k];
					if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&!vis[xx][yy])
					vis[xx][yy]=1,cnt[i]--;
				}
			}
		}
	}
	dp[0][0]=1;
	for(int i=1;i<=n*m;i++)
	{
		for(int j=0;j<(1<<maxa);j++)
		{
			dp[i][j]=1ll*dp[i-1][j]*(max(0,cnt[j]-i+1))%mod;
			for(int k=0;k<maxa;k++)
			{
				if((1<<k)&j)
				{
					dp[i][j]=moc(dp[i][j]+dp[i-1][j^(1<<k)]);
				}
			}
		}
	}
	return dp[n*m][(1<<maxa)-1];
}
bool check(int x,int y)
{
	for(int i=0;i<9;i++)
	if(x+dx[i]>=1&&x+dx[i]<=n&&y+dy[i]>=1&&y+dy[i]<=m&&a[x+dx[i]][y+dy[i]]=='X') 
	return false;
	return true;
}
void dfs(int kind)
{
	ans=moc(ans+kind*dpp());
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(check(i,j))
			{
				a[i][j]='X';
				dfs(-kind);
				a[i][j]='.';
			}
		}
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%s",a[i]+1);
	}
	dfs(1);
	cout<<ans;
	return 0;
}