题解:
考虑朴素DP,我们可以状压来转移。
继续观察性质:
如果是
n∗n
n
∗
n
的方阵,那么副对角线元素相同。
如果是
n∗m
n
∗
m
的方阵,那么设
d=gcd(n,m)
d
=
gcd
(
n
,
m
)
,每个
d∗d
d
∗
d
的方阵都与第一个相同。且副对角线相同。
这意味着我们只需要枚举 d∗d d ∗ d 的矩阵中向左移动 dx d x ,向下移动了 dy=d−dx d y = d − d x 即可确定整个矩形,同时状压也不必要了,因为前 d d 步不会走过重复节点。注意要满足,否则无法遍历完矩形,为非法路径。
我们直接对着 d∗d d ∗ d 的矩形做DP。 枚举以哪个点为终点做路径DP即可。注意有许多点的DP是相同的,我们可以一起做。(也就是这篇博客中的枚举轮数)。
#include <bits/stdc++.h>
using namespace std;
const int RLEN=1<<18|1;
inline int rd(int x=0) {return (scanf("%d",&x),x);}
const int N=55,mod=998244353;
int n,m,st,bl,ans;
int fir[N][N],f[N][N],g[N][N];
char s[N][N];
inline int gcd(int x,int y) {return y ? gcd(y,x%y) : x;}
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int mul(int x,int y) {return (unsigned long long)x*y%mod;}
inline void solve() {
n=rd(),m=rd(),st=gcd(n,m),bl=n*m/st,ans=0;
for(int i=0;i<n;i++) scanf("%s",s[i]);
for(int tx=0,ty=st;tx<=st;++tx,--ty) if(gcd(tx,n)==1 && gcd(ty,m)==1) {
memset(fir,0x3f,sizeof(fir));
for(int i=1,stx=0,sty=0;i<=bl;i++,stx=(stx+tx)%n,sty=(sty+ty)%m)
for(int dx=0;dx<=tx;++dx) for(int dy=0;dy<=ty;++dy)
if(s[(stx+dx)%n][(sty+dy)%m]=='1') fir[dx][dy]=min(fir[dx][dy],i);
for(int t=1;t<=bl;t++) {
memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); f[0][0]=1; g[tx][ty]=1;
for(int i=0;i<=tx;i++)
for(int j=0;j<=ty;j++) {
if(i && fir[i-1][j]>t) f[i][j]=add(f[i][j],f[i-1][j]);
if(j && fir[i][j-1]>t) f[i][j]=add(f[i][j],f[i][j-1]);
}
for(int i=tx;i>=0;i--)
for(int j=ty;j>=0;j--) {
if(i<tx && fir[i+1][j]>=t) g[i][j]=add(g[i][j],g[i+1][j]);
if(j<ty && fir[i][j+1]>=t) g[i][j]=add(g[i][j],g[i][j+1]);
}
for(int i=0;i<=tx;i++)
for(int j=0;j<=ty;j++) if((i+j)&&fir[i][j]==t)
ans=add(ans,mul(mul(f[i][j],g[i][j]),(t-1)*st+i+j));
}
} cout<<ans<<'\n';
}
int main() {
for(int i=rd();i;i--) solve();
}