bzoj2669: [cqoi2012]局部极小值
DP太恐怖恐怖怖了。。。。。
2^28肯定不兹瓷
考虑对于'X'的点不会超过8个,用二进制表示
f[i][j]表示当前填了第i个数字,状态为j的方案数
由小到大填
f[i+1][j]=f[i][j]*num[j] num表示当前这个状态,有多少位置可以填。为了保证合法,没被填的'X'周围的格子肯定不能填。
f[i+1][j^(1<<k)]=f[i][j] 填在第k个'X'
然后还要容斥一波,因为无法保证'.'的位置不是局部最小值,这个用dfs搜出每一种情况容斥即可
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const LL mod=12345678; const int dx[8]={-1,0,1,0,1,1,-1,-1}; const int dy[8]={0,-1,0,1,1,-1,1,-1}; int n,m;char ss[10][10]; int id,maxp,mp[10][10]; bool v[10][10];LL num[1100]; void init() { id=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(ss[i][j]=='X')mp[i][j]=++id; maxp=(1<<id)-1; for(int zt=0;zt<=maxp;zt++) { num[zt]=0; memset(v,true,sizeof(v)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(ss[i][j]=='X') { v[i][j]=false; num[zt]++; if(!(zt&(1<<mp[i][j]-1))) { for(int k=0;k<=7;k++) { int ti=i+dx[k],tj=j+dy[k]; if(ti>0&&ti<=n&&tj>0&&tj<=m&&v[ti][tj]==true) v[ti][tj]=false, num[zt]++; } } } num[zt]=n*m-num[zt]; } } LL f[40][1100]; LL DP() { init(); memset(f,0,sizeof(f));f[0][0]=1; for(int i=0;i<n*m;i++) for(int zt=0;zt<=maxp;zt++) if(f[i][zt]>0) { int cc=0; for(int k=0;k<id;k++) if(!(zt&(1<<k))) f[i+1][zt^(1<<k)]=(f[i+1][zt^(1<<k)]+f[i][zt])%mod; else cc++; f[i+1][zt]=(f[i+1][zt]+f[i][zt]*max(num[zt]-(i-cc),0LL))%mod; } return f[n*m][maxp]; } LL ans; void dfs(int x,int y,int c)//容斥 { if(x==n+1) { if(c%2==0)ans=(ans+DP())%mod; else ans=((ans-DP())%mod+mod)%mod; return ; } bool bk=true; if(ss[x][y]=='X')bk=false; else { for(int k=0;k<=7;k++) { int tx=x+dx[k],ty=y+dy[k]; if(tx>0&&tx<=n&&ty>0&&ty<=m&&ss[tx][ty]=='X') {bk=false;break;} } } if(y==m)dfs(x+1,1,c); else dfs(x,y+1,c); if(bk==true) { ss[x][y]='X'; if(y==m)dfs(x+1,1,c+1); else dfs(x,y+1,c+1); ss[x][y]='.'; } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",ss[i]+1); ans=0; dfs(1,1,0); printf("%lld\n",ans); return 0; }
pain and happy in the cruel world.