【BZOJ】P2669 局部极小值
装压dp+容斥
由于当n=4,m=7时,局部极小值(下面简称极小值)最多只有8个。(自己模拟一下)
这样,我们就可以装压了。
状态定义
\(dp_{i,j}\)表示当前放到第i,极小值的状态为j时的方案数
状态转移
分两种情况:
1.在极小值中放i:
\(dp_{i,j<<(1<<(k-1))}+=dp_{i-1,j}(k \notin j)\)
2.在其他点钟放i
\(dp_{i,j}+=dp_{i,j-1} \times A\)
(A表示可以放的方案数)
对于A,我们可以预处理出每个j状态可行的方案数
此时的答案就是\(dp_{n \times m,(1<<tot)-1}\) (tot表示极小值的个数)
思考与质疑
想一想,要是在放的过程中某一个不是极小值的点变成极小值了咋办?
可以枚举那些点变成了极小值,每次计算dp值后在更新最终答案。
考虑到这些可能会有重合,(1个非极小值点变成了极小值必定包含了2个点变成了极小值的情况)
这就需要容斥了。
大致流程
1.开个dfs,枚举那些非极小值点可以变成极小值
2.当dfs到终点时,dp一下,并将最终答案更新
总之,就是dfs套dp
代码:
#include<bits/stdc++.h>
#define int long long
#define MOD 12345678
using namespace std;
int n,m,X[101],Y[101],tot,ans,Size[(1<<8)+10];
char mp[11][11];
bool can[11][11],mark[11][11];
const int mx[]= {1,0,-1,0,1,1,-1,-1,0},my[]= {0,1,0,-1,1,-1,1,-1,0};
int dp[30][(1<<8)+10],sum[(1<<8)+10];
int DP() {
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
for(int i=0; i<(1<<tot); i++) {
int res=0;
memset(mark,false,sizeof(mark));
for(int j=1; j<=tot; j++) {
if((1<<(j-1))&i)continue;
mark[X[j]][Y[j]]=true;
}
for(int j=1; j<=n; j++) {
for(int k=1; k<=m; k++) {
bool flag=true;
for(int o=0; o<=8; o++) {
int nx=j+mx[o],ny=k+my[o];
if(mark[nx][ny]||can[j][k]) {
flag=false;
break;
}
}
res+=flag;
}
}
sum[i]=res;
}
dp[0][0]=1;
for(int i=1; i<=n*m; i++) {
for(int j=0; j<(1<<tot); j++) {
if(Size[j]>i)continue;
dp[i][j]+=dp[i-1][j]*(max(sum[j]+Size[j]-i+1,0LL)),dp[i][j]%=MOD;
for(int k=1; k<=tot; k++) {
if((1<<(k-1))&j)continue;
dp[i][j|(1<<(k-1))]+=dp[i-1][j],dp[i][j|(1<<(k-1))]%=MOD;
}
}
}
return dp[n*m][(1<<tot)-1];
}
bool check(int x,int y) {
for(int i=0; i<=8; i++) {
int nx=x+mx[i],ny=y+my[i];
if(can[nx][ny])return false;
}
return true;
}
void DFS(int x,int y,int pos) {
if(y==m+1)x++,y=1;
if(x==n+1) {
ans+=pos*DP(),ans+=MOD,ans%=MOD;
return;
}
DFS(x,y+1,pos);
if(check(x,y)) {
can[x][y]=true,X[++tot]=x,Y[tot]=y;
DFS(x,y+1,pos*-1);
can[x][y]=false,tot--;
}
}
signed main() {
for(int i=1; i<(1<<8); i++)Size[i]=Size[i^(i&-i)]+1;
scanf("%lld %lld",&n,&m);
for(int i=1; i<=n; i++)scanf("%s",mp[i]+1);
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
if(mp[i][j]=='X') {
can[i][j]=true;
X[++tot]=i,Y[tot]=j;
}
}
}
DFS(1,1,1);
cout<<ans;
return 0;
}