题目分析
首先,同一根对角线上的行为决策必须一样(要么都往右要么都往下)。
行走是循环的,走到最右就变成最左,走到最左就变成最右了,所以“一根对角线”的意义也是循环的,比如说下图每一种颜色的点都属于同一根对角线。
假设有一根对角线,对于它上面的每一个点
(
x
,
y
)
(x,y)
(x,y),
x
+
y
=
k
x+y=k
x+y=k。当
(
x
,
y
)
(x,y)
(x,y)出了边界后,为了使它拥有合法的意义,需要(
x
+
=
n
x+=n
x+=n或
x
−
=
n
x-=n
x−=n或
y
+
=
m
y+=m
y+=m或
y
−
=
m
y-=m
y−=m),不难发现,最后所有满足
x
+
y
=
k
+
a
n
+
b
m
x+y=k+an+bm
x+y=k+an+bm(
a
,
b
a,b
a,b为整数)的点都在这根对角线上。根据裴蜀定理,设
d
=
g
c
d
(
n
,
m
)
d=gcd(n,m)
d=gcd(n,m),所有满足
x
+
y
=
k
+
t
d
x+y=k+td
x+y=k+td(
d
d
d为整数)的点都在这根对角线上。因此,一共有
d
d
d条不同的对角线。
而机器人每走一步,都一定会到达一根新的对角线,所以机器人的行走决策,存在长度为 d d d的循环节。
假设对于每个 d d d循环节,选择“向下走”走了 d x dx dx步,“向右走”走了 d y dy dy步。显然要 g c d ( d x , n ) = 1 gcd(dx,n)=1 gcd(dx,n)=1,才能到达每一行。要 g c d ( d y , m ) = 1 gcd(dy,m)=1 gcd(dy,m)=1,才能到达每一列。至于充分性……呃,意会(捂脸)……
枚举 d x dx dx, d y dy dy也已知了,接下来的任务就只有确定循环内的决策了。
假设钦定机器人是在第 P P P步决策撞到障碍的,假设我钦定了前 P P P步决策是向下走 x x x步,向右走 y y y步,那么它撞到的障碍物的坐标应该被写成 ( 1 + x + k × d x , 1 + y + k × d y ) (1+x+k \times dx,1+y+k \times dy) (1+x+k×dx,1+y+k×dy)的形式,且 k k k要尽可能小。于是,设 ( 1 + x , 1 + y ) (1+x,1+y) (1+x,1+y)点的“权值”为 x + y + k × d x+y+k \times d x+y+k×d,即钦定在这步决策导致撞障碍物情况下,最早撞障碍物的时间。
接下来开始DP决策,设 f ( i , j , k ) f(i,j,k) f(i,j,k)表示从 ( 1 , 1 ) (1,1) (1,1)走到 ( i , j ) (i,j) (i,j)这个点(即钦定走了 i − 1 i-1 i−1步向下, j − 1 j-1 j−1步向右),走到的最小权值为 k k k(即已经钦定好了的决策导致最早的撞墙时间)的方案数。DP转移就是选择向下还是向右,最后将所有的 k × f ( d x + 1 , d y + 1 , k ) k \times f(dx+1,dy+1,k) k×f(dx+1,dy+1,k)加入答案中。
复杂度分析: k ≤ n ∗ m k \leq n*m k≤n∗m,DP复杂度 O ( n 4 ) O(n^4) O(n4),枚举 d x dx dx复杂度 O ( n ) O(n) O(n),计算权值复杂度 O ( n 4 ) O(n^4) O(n4),总复杂度 O ( n 5 ) O(n^5) O(n5)。
代码
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int mod=998244353;
int T,n,m,d,ans,f[52][52][2502],val[52][52];
char mp[52][52];
int qm(int x) {return x>=mod?x-mod:x;}
int gcd(int x,int y) {return y?gcd(y,x%y):x;}
int DP(int dx,int dy) {
for(RI i=1;i<=dx+1;++i)
for(RI j=1;j<=dy+1;++j)
for(RI k=1;k<=n*m;++k) f[i][j][k]=0;
f[1][1][val[1][1]]=1;
for(RI i=1;i<=dx+1;++i)
for(RI j=1;j<=dy+1;++j)
for(RI k=1;k<=n*m;++k) {
if(!f[i][j][k]) continue;
if(i<=dx) f[i+1][j][min(k,val[i+1][j])]=
qm(f[i+1][j][min(k,val[i+1][j])]+f[i][j][k]);
if(j<=dy) f[i][j+1][min(k,val[i][j+1])]=
qm(f[i][j+1][min(k,val[i][j+1])]+f[i][j][k]);
}
int re=0;
for(RI i=1;i<=n*m;++i) re=qm(re+1LL*i*f[dx+1][dy+1][i]%mod);
return re;
}
int main()
{
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m),d=gcd(n,m),ans=0;
for(RI i=1;i<=n;++i) scanf("%s",mp[i]+1);
for(RI dx=1;dx<=d;++dx) {
if(gcd(dx,n)!=1||gcd(d-dx,m)!=1) continue;
for(RI x=1;x<=dx+1;++x)
for(RI y=1;y<=d-dx+1;++y) {
int tx=x,ty=y,nowd=x-1+y-1;val[x][y]=n*m;
do {
if(mp[tx][ty]=='1') {val[x][y]=nowd;break;}
tx+=dx,ty+=d-dx,nowd+=d;
if(tx>n) tx-=n; if(ty>m) ty-=m;
}while(tx!=x||ty!=y);
}
ans=qm(ans+DP(dx,d-dx));
}
printf("%d\n",ans);
}
return 0;
}