这代码过了模板,写了快读,改了 __int128,a 数组全部赋 1 了,结果样例没过 + 8WA、2TLE……
#include<bits/stdc++.h>
#define ll long long
#define lll __int128
using namespace std;
const int N = 20 + 4;
const int M = 600000 + 5;
const int hsh = 4007;
char s[N][N];
ll n, m;
lll p[N], a[N][N];
lll ans, lst, op, f[M][2];
lll cnt[2], head[hsh + 10], nxt[M], sta[M][2];
//因为是滚动数组,cnt表示两个哈希表分别有多少个数
//sta存的是 % hsh + 1 = 1 ~ hsh 的所有轮廓线状态
void add(lll S, lll w){
lll 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] = 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(lll i=1;i<=n;i++){
for(lll j=1;j<=cnt[op];j++)
sta[j][op] <<= 2;//每一次会新增两个插头,所以空出两位放
for(lll j=1;j<=m;j++){
memset(head, 0, sizeof(head));
lst = op, op ^= 1;
cnt[op] = 0;//这行和上面两行都是清空链表、滚动数组
for(lll k=1;k<=cnt[lst];k++){//枚举上一列的轮廓线状态
lll S = sta[k][lst], now = f[k][lst];
//取出第 j 个和第 j + 1 个插头,/ 4^(j-1)%4 即可
lll b1 = (S >> ((j - 1) << 1)) % 4;//j-1是上插头(对应原来的第j+1个)
lll 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] * 2ll, 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){//删掉了两个左括号
lll k1 = 1;
for(lll 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){
lll k1 = 1;
for(lll 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] * b2 - p[j - 1] * b1, 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(i == n && j == m)
ans += now;
}
}
}
}
void write(lll x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int main(){
p[0] = 1;
for(int i=1;i<N;i++)
p[i] = p[i - 1] << 2;//预处理不想解释
scanf("%lld%lld", &m, &n);
if(m == 1 && n == 1)
return printf("1\n"), 0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
a[i][j] = 1;
dp();
write(ans);
return 0;
}