2669: [cqoi2012]局部极小值
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 537 Solved: 280
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
X.
..
.X
Sample Output
HINT
Source
题解:dp +容斥原理。
这道题的数据范围很小。我们很容易想到状压动归,但是如果我们枚举每一个位置放置什么就一定会存在判断当前数是否已经用过的问题,2^28很显然不能承受。
我们发现局部极小值的点最多8个,所以我们考虑把局部最小值是否已经填过状压成一维,然后考虑从小到大往格子中填数,因为每个数枚举一次所以也就只能填一次。
f[i][j]表示填到数i,局部最小值的填写状态为j。
i数可以填写到的位置可以分成两种,‘x'位置,和非’x'位置。
如果考虑填入‘x'位置,因为是从小到大开始填写,所以直接填就好,因为周围的点要么没填要么填的比这个位置小。我们只需要令j状态中填写的位置k,f[i][j]+=f[i-1][j^(1<<k-1)]
如果考虑填入非‘x’位置,很显然有些位置是不能填的(就是那些未填的'x'位置和他周围的位置),我们可以预处理出每种状态对应的可以填写的位置的个数(即除去那些未填的'x'位置和他周围的位置剩下的位置),但是这些位置中已经填写了(i-1)个数,
所以在递推的时候要减去,即f[i][j]+=f[i-1][j]*max(num[j]-i+1,0)。
但是我们这样算完后发现答案还是不对,为什么呢?因为我们是保证了'x'位置是局部极小值,但是并没有保证'.'的位置不是,局部最小值,所以Ans=多包含至少0个局部极小值-多包含至少1个局部极小值+多包含至少2个局部极小值-...
这个容斥的时候用dfs搜索出那些不是'x'但是有可能成为局部极小值的位置,然后用上面的方式计算一下,在更新最终答案即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 30
#define p 12345678
using namespace std;
int f[N][1<<8],n,m,ans;
int x[N],y[N],num[1<<8],tot,vis[N][N];
int posx[10]={-1,-1,-1,0,0,1,1,1},posy[10]={-1,0,1,-1,1,-1,0,1};
char s[N][N];
int solve()
{
tot=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (s[i][j]=='X')
x[++tot]=i,y[tot]=j;
for (int i=0;i<(1<<tot);i++)
{
int cnt=0;
memset(vis,0,sizeof(vis));
for (int j=1;j<=tot;j++)
if (!((i>>(j-1))&1))
{
vis[x[j]][y[j]]=1;
for (int k=0;k<8;k++)
{
int xx=x[j]+posx[k];
int yy=y[j]+posy[k];
if (xx>0&&yy>0&&xx<=n&&yy<=m)
vis[xx][yy]=1;
}
}
for (int j=1;j<=n;j++)
for (int k=1;k<=m;k++)
if (vis[j][k]) cnt++;
num[i]=n*m-cnt;
}
memset(f,0,sizeof(f));
f[0][0]=1;
for (int i=1;i<=n*m;i++)
for (int j=0;j<(1<<tot);j++)
{
f[i][j]=(f[i][j]+f[i-1][j]*max(num[j]-i+1,0))%p;
for (int k=1;k<=tot;k++)
if (j&(1<<(k-1)))
f[i][j]=(f[i][j]+f[i-1][j^(1<<k-1)])%p;
}
return f[n*m][(1<<tot)-1];
}
void dfs(int x,int y,int k)
{
if (y==m+1)
{
dfs(x+1,1,k);
return;
}
if (x==n+1)
{
if (k%2==0) ans=(ans+solve())%p;
else ans-=solve()%p;
ans=(ans%p+p)%p;
return;
}
dfs(x,y+1,k);
bool f=true;
for (int i=0;i<8;i++)
if (s[x+posx[i]][y+posy[i]]=='X')
{
f=false;
break;
}
if (f&&s[x][y]!='X'){
s[x][y]='X';
dfs(x,y+1,k+1);
s[x][y]='.';
}
}
int main()
{
freopen("a.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')
x[++tot]=i,y[tot]=j;
}
dfs(1,1,0);
printf("%d\n",(ans%p+p)%p);
}