题解:CF342D Xenia and Dominoes
显然做法是计数 dp。
把 O 视作障碍,设 O 的坐标为
但这样会算重,但只有四个方向,所以可以直接暴力容斥算。
再想如何 dp。注意到行数很少,且一个多米诺顶多管两列,我们考虑按列 dp。第
先考虑没有障碍的情况。设当前 dp 到第
考虑存在障碍的情况。我们先算出第
所以有
容斥是
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 10005
#define M 10
#define int long long
const int mod = 1e9+7;
int n=3,m,i,j,ans,x,y,L,R,U,D,f[N][M];
char mp[M][N];
int dp(){
memset(f,0,sizeof(f));
f[0][7]=1;
for(int i=1;i<=m;i++){
int s=0;
for(int j=1;j<=n;j++){
if(mp[j][i]=='X') s^=(1<<(j-1));
}
for(int j=0;j<=7;j++){
if(s&j) continue;
int ns=(j^s);
f[i][ns]=(f[i][ns]+f[i-1][7-j])%mod;
if(j==3 || j==6) f[i][ns]=(f[i][ns]+f[i-1][7])%mod;
if(j==7) f[i][ns]=(f[i][ns]+f[i-1][3]+f[i-1][6])%mod;
}
}
return f[m][7];
}
signed main(){
scanf("%lld",&m);
for(i=1;i<=n;i++) scanf("%s",mp[i]+1);
for(i=1;i<=n;i++){
for(j=1;j<=m;j++) if(mp[i][j]=='O') x=i,y=j;
}
mp[x][y]='X';
for(L=0;L<=1;L++){
if(L && (y<3 || mp[x][y-1]=='X' || mp[x][y-2]=='X')) continue;
for(R=0;R<=1;R++){
if(R && (y>m-2 || mp[x][y+1]=='X' || mp[x][y+2]=='X')) continue;
for(U=0;U<=1;U++){
if(U && (x<3 || mp[x-1][y]=='X' || mp[x-2][y]=='X')) continue;
for(D=0;D<=1;D++){
if(D && (x>1 || mp[x][y+1]=='X' || mp[x][y+2]=='X')) continue;
if(!L && !R && !U && !D) continue;
if(L) mp[x][y-1]='X',mp[x][y-2]='X';
if(R) mp[x][y+1]='X',mp[x][y+2]='X';
if(U) mp[x-1][y]='X',mp[x-2][y]='X';
if(D) mp[x+1][y]='X',mp[x+2][y]='X';
int res=dp();
if((L+R+U+D)&1) ans=(ans+res)%mod;
else ans=(ans-res+mod)%mod;
if(L) mp[x][y-1]='.',mp[x][y-2]='.';
if(R) mp[x][y+1]='.',mp[x][y+2]='.';
if(U) mp[x-1][y]='.',mp[x-2][y]='.';
if(D) mp[x+1][y]='.',mp[x+2][y]='.';
}
}
}
}
printf("%lld\n",ans);
return 0;
}