调了一下午,人已经麻了
#include<bits/stdc++.h>//plugdp by shaymin
using namespace std;
typedef long long ll;
const int MOD=40001;
int head[600000],nxt[600000];//HASH表
int n,m,area[13][13]/*地图*/,endx,endy/*结束点坐标*/,pre=1,now;
ll ans,f[2][600000],state[2][600000]/*状态*/,cnt[2]/*状态数*/;
int bit[14];//便于提取插头,实际上bit[i] = 4**i
int get(ll s,int num){//获取 s 状态的第 num 位
return (s>>(num*2)) & ((1<<2)-1);//建议手模 (注:这里是4进制表示的!)
}
void change(ll &s,int num,int v){//改变 s 状态的第 num 位为 v
s^=get(s,num)<<(num*2);//第 num 位改为0
s|=v<<(num*2);//第 num 位改为v
}
void hashin(ll s,ll num);//存入HASH表
void dp();
int main(){
// freopen("plugdp.in","r",stdin);
// freopen("plugdp.out","w",stdout);
scanf("%d%d",&n,&m);
char s[15];
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++){
if(s[j]=='.'){
area[i][j]=1;
endx=i; endy=j;//记录终点位置
}
}
}
// bit[0]=1;
// for(int i=1;i<=13;i++)
// bit[i]=bit[i-1]<<2;
//读入及初始化↑
dp();
printf("%lld",ans);
return 0;
}
void dp(){
f[now][1]=1; cnt[now]=1; state[now][1]=0;//DP的初始化
for(int i=1;i<=n;i++){
for(int j=1;j<=cnt[now];j++) state[now][j]<<=2;//换行操作 因为是4进制表示所以要左移两位
for(int j=1;j<=m;j++){//枚举每一个格子
memset(head,0,sizeof(head));//清空HASH表
swap(now,pre);//交换pre与now (滚动数组)
cnt[now]=0;//清空状态
// cout<<cnt[now]<<" "<<cnt[pre]<<endl;
for(int k=1;k<=cnt[pre];k++){
// cout<<"tty ak ioi"<<endl;
ll s=state[pre][k],num=f[pre][k];//提取上一个格子的状态、dp数组的值
ll p1=get(s,j-1),p2=get(s,j);//获取左边格子的插头、上面格子的插头
if(area[i][j]==0){
// cout<<"tty ak ioi"<<endl;
if(p1==0&&p2==0)
hashin(s,num);
}//有障碍,此时若无上插头、左插头,直接存之前状态进HASH表
else if(p1==0&&p2==0){
ll saves=s;
change(s,j-1,1); change(s,j,2);
hashin(s,num);
s=saves;
}//无左插头、上插头 此时直接添加下插头、右插头(即新开一个连通分量)
else if((p1==0&&p2!=0)||(p2==0&&p1!=0)){
if(area[i+(p1>0)][j+(p2>0)])//这些情况时不需要改状态
hashin(s,num);
if(area[i+(p2>0)][j+(p1>0)]){//这些情况时需要改状态
ll saves=s;
change(s,j-1,p2); change(s,j,p1);
hashin(s,num);
s=saves;
}
}//无左插头或者无上插头
//需要合并连通分量的情况↓ (各状态的图)
else if(p1==1&&p2==1){//左插头是左括号,上插头也是左括号
int flag=1;
for(int q=j+1;q<=m;q++){//寻找p2匹配的右括号
int nows=get(s,q);
if(nows==1) flag++;
else if(nows==2) flag--;//括号匹配操作
if(flag==0){
ll saves=s;
change(s,j-1,0); change(s,j,0); change(s,q,1);//改变括号
hashin(s,num);
s=saves;
break;
}
}
}
else if(p1==2&&p2==2){//左插头是右括号,上插头也是右括号
int flag=1;
for(int q=j-2;q>=1;q--){//寻找p1匹配的左括号
int nows=get(s,q);
if(nows==2) flag++;
else if(nows==1) flag--;
if(flag==0){
ll saves=s;
change(s,j-1,0); change(s,j,0); change(s,q,2);
hashin(s,num);
s=saves;
break;
}
}
}
else if(p1==2&&p2==1){//左插头是右括号,上插头是左括号
ll saves=s;
change(s,j-1,0); change(s,j,0);
hashin(s,num);
s=saves;
}
else if(p1==1&&p2==2){//左插头是左括号,上插头是右括号,即最终匹配完成
if(i==endx&&j==endy)//此时若是终点则统计答 案数
ans+=num;
}
}
// for(int i=1;i<=cnt[now];i++){
// cout<<f[now][state[now][i]]<<" ";
// }
// cout<<endl;
}
}
}
void hashin(ll s,ll num){
ll u=s%MOD+1;
for(int i=head[u];i;i=nxt[i]){//查找对应状态,累加方案数
if(state[now][i]==s){
f[now][i]+=num;
return ;
}
}
//如果目前HASH表内还没有该状态,则存入↓
nxt[++cnt[now]]=head[u];
head[u]=cnt[now];
state[now][cnt[now]]=s;
f[now][cnt[now]]=num;
}