【NOIP模拟】花园
题面
分析
状压dp是一点儿也不怕被看出的,毕竟看出来了是个状压你也不会做。。
可以发现,在一个九宫格里如果出现了两个X,那肯定是0,所以需要算的情况只有X<=8的时候
如图
于是用状压 dp[i][s]表示在s状态下,填了前i个数的方案数
如图
我们先填X位置,然后ok位置都能填了,这个状态就称为s
根据乘法原理:dp[下一个状态]=dp[上一个状态]*从这个状态到下一个状态的方案数
转移的方案数实际上只与还没有填的ok的格子数量以及s状态的元素有关,而这是可以根据i和s推出来的(s的二进制可知道哪些ok,i的二进制可知道填了多少个数)
转移有两种情况
- 放在极小值位置上
- 放在非极小值位置上
预处理cover[s]表示当集合s的极小值已经填过了,可以填的格子的数量。对于下图,Y也表示极小值,但是不属于当前状态s
所以此时cover[s]=13
- 放在极小值位置上:dp[i+1][s|(1<<j-1)]+=dp[i][s]
- 放在非极小值位置上:dp[i+1][s]+=dp[i][s]*(cover[s]-i)
但是想得到100分,还需要容斥一下
如下图,假设Y1,Y2不是X标记的极小值
先只考虑可能将右边的Y1,Y2两个未被指定的点变成极小值的情况。
方案数=将X1,X2变成极小值的方案数-将X1,X2,Y1变成极小值的方案数-将X1,X2,Y2变成极小值的方案数+将X1,X2,Y1,Y2变成极小值的方案数。
那Y1,Y2还有其它可能的位置呢?
直接DFS暴力搜索出Y1,Y2,Y3……所有可能的位置,并且在搜索的过程中保证指定的位置相邻的格子中没有被指定的位置。每一种的方案的容斥系数=(-1)^|A|,集合A为Y点的集合。
或者是把搜出来是极小值的点标为X,再做一遍DP来容斥也可以。
代码
#include<bits/stdc++.h> using namespace std; #define N 6 #define M 8 #define mod 12345678 #define ll long long char s[10][10]; ll ans,n,m,t,mx,tot,cnt; ll px[20],py[20],tmp[10][10],a[10][10]; ll dp[30][(1<<M)+100],cover[(1<<M)+100],val[(1<<M)+100]; ll dx[]={-1,-1,-1,0,0,0,1,1,1},dy[]={-1,0,1,-1,0,1,-1,0,1}; inline void init() { ans=0; memset(s,0,sizeof(s)); memset(px,0,sizeof(px)); memset(py,0,sizeof(py)); } inline void pre() { memset(cover,0,sizeof(cover)); memset(dp,0,sizeof(dp)); memset(val,0,sizeof(val)); memset(tmp,0,sizeof(tmp)); memset(a,0,sizeof(a)); cnt=0; } ll cal(ll x) { ll ret=0; while(x) { if(x&1)ret++; x>>=1; } return ret; } ll DP() { pre(); for(ll i=1;i<=n;i++) for(ll j=1;j<=m;j++) if(s[i][j]=='X') { px[cnt]=i; py[cnt++]=j; for(ll k=0;k<9;++k) { ll x=i+dx[k]; ll y=j+dy[k]; if(s[x][y]=='X'&&(x!=i||y!=j)) return 0; a[x][y]++; } } mx=(1<<cnt)-1; for(ll i=0;i<=mx;i++) { val[i]=cal(i); for(ll j=0;j<cnt;j++) { if(i&(1<<j)) for(ll k=0;k<=8;++k) { ll x=px[j]+dx[k]; ll y=py[j]+dy[k]; tmp[x][y]++; } else tmp[px[j]][py[j]]=1; } for(ll j=1;j<=n;++j) for(ll k=1;k<=m;++k) { if(s[j][k]=='.'&&tmp[j][k]==a[j][k])cover[i]++; tmp[j][k]=0; } } dp[0][0]=1; for(ll i=1;i<=tot;++i) for(ll st=0;st<=mx;++st) { ll j=i-1-val[st]; if(cover[st]>j) dp[i][st]=(dp[i][st]+(dp[i-1][st]*(cover[st]-j))%mod)%mod; for(ll k=0;k<cnt;++k) if(st&(1<<k)) dp[i][st]=(dp[i][st]+dp[i-1][st^(1<<k)])%mod; } return dp[tot][mx]; } void dfs(ll x,ll y,ll f) { if(x==n+1) { ans+=DP()*((f&1)?-1:1); ans%=mod;ans+=mod;ans%=mod; return; } if(y==m+1) { dfs(x+1,1,f); return ; } dfs(x,y+1,f); if(s[x][y]=='.') { ll flag=1; for(ll i=0;i<9;i++) { ll nx=x+dx[i],ny=y+dy[i]; if(s[nx][ny]=='X'){flag=0;break;} } if(flag) { s[x][y]='X'; dfs(x,y+1,f+1); s[x][y]='.'; } } } int main() { scanf("%lld",&t); while(t--) { init(); scanf("%lld%lld",&n,&m);tot=n*m; for(ll i=1;i<=n;i++)scanf("%s",s[i]+1); dfs(1,1,0); printf("%lld\n",ans); } return 0; }
“Make my parents proud,and impress the girl I like.”