rt,不知道哪里的锅 qwq。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 12 + 4;
const int M = 600000 + 5;
const int hsh = 4007;
char s[N][N];
ll n, m, tx, ty, p[N], a[N][N];
ll ans, lst, op, f[M][2];
ll cnt[2], head[hsh + 10], nxt[M], sta[M][2];
//因为是滚动数组,cnt表示两个哈希表分别有多少个数
//sta存的是 % hsh + 1 = 1 ~ hsh 的所有轮廓线状态
void add(ll S, ll w){
ll u = S % hsh + 1;
for(int i=head[u];i;i=nxt[i]){
if(sta[i][op] == S){
f[i][op] += w;
return ;
}
}
//% hsh + 1 中没有实际与它相同的轮廓线,我们就新建一个
nxt[++cnt[op]] = head[u];
head[u] = head[cnt[op]];
sta[cnt[op]][op] = S;
f[cnt[op]][op] = w;
}
void dp(){
cnt[op] = f[1][op] = 1;
sta[1][op] = 0;
for(ll i=1ll;i<=n;i++){
for(ll j=1ll;j<=cnt[op];j++)
sta[j][op] <<= 2ll;//每一次会新增两个插头,所以空出两位放
for(ll j=1;j<=m;j++){
memset(head, 0, sizeof(head));
lst = op, op ^= 1;
cnt[op] = 0;//这行和上面两行都是清空链表、滚动数组
for(ll k=1;k<=cnt[lst];k++){//枚举上一列的轮廓线状态
ll S = sta[k][lst], now = f[k][lst];
//取出第 j 个和第 j + 1 个插头,/ 4^(j-1)%4 即可
ll b1 = (S >> ((j - 1) << 1)) % 4;//j-1是上插头(对应原来的第j+1个)
ll b2 = (S >> (j << 1)) % 4;//j 是左插头
if(!a[i][j]){//这里是障碍
if(!b1 && !b2)//当且仅当两个插头都为 0 时合法
add(S, now);
}
else if(!b1 && !b2){//左、上都被封死
if(a[i + 1][j] && a[i][j + 1])//当且仅当这俩插头都可以走的时候才合法
add(S + p[j - 1] + p[j] * 2, now);//新建一对匹配的括号
}
else if(!b1 && b2){//上边封死左边没封,选一个走,下面情况同理
if(a[i][j + 1])//继承了之前的状态(方向没变插头编号自然也不变),不变
add(S, now);
if(a[i + 1][j])//先把第 j 位变成 0,再把第 j-1 变成 b2
add(S - p[j] * b2 + p[j - 1] * b2, now);
}
else if(b1 && !b2){
if(a[i][j + 1])//变化
add(S - p[j - 1] * b1 + p[j] * b1, now);
if(a[i + 1][j])//继承
add(S, now);
}
else if(b1 == 1 && b2 == 1){//删掉了两个左括号
ll k1 = 1;
for(ll l=j+1;l<=m;l++){//找后面第一个失配的右括号改成左括号
if((S >> (l << 1)) % 4 == 1)
++k1;
if((S >> (l << 1)) % 4 == 2)
--k1;
if(!k1){//前缀和为 -1,找到了多出的右括号
add(S - p[l] - p[j - 1] * b1 - p[j] * b2, now);
//方便理(ctrl)解(c),b1、b2 都是 1,所以可以不写的
//将 l 从 2 改到 1,就是减了一个 p[l] 而已
break;
}
}
}
else if(b1 == 2 && b2 == 2){
ll k1 = 1;
for(ll l=j-2;l>=0;l--){//找前面第一个失配的左括号改成右括号
if((S >> (l << 1)) % 4 == 1)
--k1;
if((S >> (l << 1)) % 4 == 2)
++k1;
if(!k1){//后缀和为 1,找到了多出的右括号
add(S + p[l] - p[j] * b1 - p[j - 1] * b2, now);
//方便理解,b1、b2 都是 1,所以可以不写的
//将 l 从 1 改到 2,就是加了一个 p[l] 而已
break;
}
}
}
else if(b1 == 2 && b2 == 1)//反着的括号,直接去掉括号序列仍然匹配
add(S - p[j - 1] * b1 - p[j] * b2, now);
else if(b1 == 1 && b2 == 2)//顺着的括号对,直接匹配
if(i == tx && j == ty)//已经是最后一个格子了,证明这是一条哈密尔顿回路
ans += now;
}
}
}
}
int main(){
p[0] = 1;
for(int i=1;i<N;i++)
p[i] = p[i - 1] << 2;//预处理不怎么想解释
scanf("%lld%lld", &n, &m);
for(int i=1;i<=n;i++){
scanf("%s", s[i] + 1);
for(int j=1;j<=m;j++)
if(s[i][j] == '.')
a[tx = i][ty = j] = 1;
}
dp();
printf("%lld\n", ans);
return 0;
}