[loj2731]「JOISC 2016 Day 1」棋盘游戏
题目大意
有一个3 × n的棋盘,你在上面玩游戏。开始时,棋盘有一些格子上已经摆上了棋子,剩下的格子都是空的。每次你可以选择一个空的格子摆上棋子,这个格子必须满足以下两个条件之一:
- 这个格子上下两格都有棋子;
- 这个格子左右两格都有棋子。
你想知道有多少种不同的摆满棋盘的摆放顺序。
思路
首先判断无解,如果四个角没有棋子或者一三行出现了连续两个空格子,那么就无解.
然后发现整个图变成了这个样子:
oxoxoxoxo
xxxxoxxxo
oxoxoxoxo
我们发现一三行的格子任意时刻都可以放上棋子,只有中间一行的棋子我们需要考虑先后顺序.
考虑根据中间那一行的连通性来划分联通块,每一个块之间可以独立计算,不互相影响.
然后对于每一个块,我们考虑对最后的摆放的位置序列进行dp,每次将一个列的元素添加到序列中,不难发现对于每一列中间的元素,我们有两种方式将它加入进去:
- 上下都有棋子
- 左右都有棋子
为了不算重,我们将两者都满足的情况算成第一种,对于第一种情况的转移,不难发现和旁边两列摆放的位置没有任意影响,但是对于第二种,我们要求旁边两列的棋子均摆放在这一列的前面.
于是设状态f[i][j][0/1]表示第i列的中间那个棋子是第j个加进序列,且是通过第1/2种方案加进去的.
直接转移是\(n^3\)的,可以用前缀和优化至\(n^2\).
/*=======================================
* Author : ylsoi
* Time : 2019.2.26
* Problem : game
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
const int maxn=2000+10;
const int mod=1e9+7;
int n,f[maxn][maxn*3][2];
char s[4][maxn];
int fac[maxn*3],ifac[maxn*3];
void inc(int &x,int y){(x+=y)>=mod ? x-=mod : x<0 ? x+=mod : 0;}
int qpow(int x,int y){
int ret=1; x%=mod;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ret;
}
void math_init(){
fac[0]=1;
REP(i,1,6000)fac[i]=1ll*fac[i-1]*i%mod;
ifac[6000]=qpow(fac[6000],mod-2);
DREP(i,5999,0)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
int C(int x,int y){
if(x<0 || y<0 || x<y)return 0;
return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
int main(){
File();
read(n);
REP(i,1,3)scanf("%s",s[i]+1);
REP(i,1,n){
if(s[1][i]=='x' && (i==1 || i==n || s[1][i-1]=='x' || s[1][i+1]=='x'))puts("0"),exit(0);
if(s[3][i]=='x' && (i==1 || i==n || s[3][i-1]=='x' || s[3][i+1]=='x'))puts("0"),exit(0);
}
math_init();
int sum=0,now=0,ans=1;
if(s[2][1]=='x'){
f[1][1][0]=1,now=1;
if(n==1 || s[2][2]=='o')sum=1,now=0;
}
REP(i,2,n){
int cur=(s[1][i]=='x')+(s[3][i]=='x');
if(s[2][i]=='o'){
sum+=cur;
ans=1ll*ans*fac[cur]%mod*C(sum,cur)%mod;
continue;
}
++cur;
now+=cur;
REP(j,1,now){
int per=fac[cur-1];
if(now==cur){
if(j==cur)f[i][j][0]=per;
if(i<n && j!=cur)f[i][j][1]=per;
}
else{
inc(f[i][j][0],1ll*f[i-1][now-cur][0]*per%mod*C(j-1,cur-1)%mod);
inc(f[i][j][0],1ll*(f[i-1][now-cur][1]-f[i-1][max(j-cur,0)][1])*per%mod*C(j-1,cur-1)%mod);
if(i<n){
if(cur==2)inc(f[i][j][1],1ll*f[i-1][min(now-cur,j-1)][0]*per%mod*(now-j)%mod);
if(cur==3){
inc(f[i][j][1],1ll*f[i-1][min(now-cur,j-1)][0]*per%mod*C(now-j,2)%mod);
inc(f[i][j][1],1ll*f[i-1][max(j-2,0)][0]*per%mod*(j-1)%mod*(now-j)%mod);
}
}
}
}
if(i==n || s[2][i+1]=='o'){
sum+=now;
int ts=0;
REP(j,1,now)inc(ts,(f[i][j][0]+f[i][j][1]*(i<n))%mod);
ans=1ll*ans*ts%mod*C(sum,now)%mod;
now=0;
}
REP(j,1,now){
inc(f[i][j][0],f[i][j-1][0]);
inc(f[i][j][1],f[i][j-1][1]);
}
}
printf("%d\n",ans);
return 0;
}