[JSOI2018]机器人
题目描述#
一个的网格,有一个机器人一开始在,每次机器人可以向右或向下走一步,的右边是,的下边是,机器人需要不重不漏地走完所有格子回到。
现在给出若干障碍,对于每一个上述行走方案,走到障碍时会停止,求所有方案的行走的格子的个数和。
解题思路#
首先考虑什么样的方案是合法的。
- 性质一:对于一个格子,和的行走方向必须是一样的,否则,要么会走两次,要么走不到
推论:每条副对角线的方向是一样的
- 性质二:第条副对角线的方向和第条的方向是一样的
证明:
把性质一推广到模意义下,副对角线也推广到模意义下,那么和方向应该是一样的,也就是第条对角线和第条对角线方向一样,也就是差为的对角线方向一样,同理可得差为的对角线也一样,那么量条对角线方向一样当且仅当存在使得,也就是,由裴蜀定理,该式成立当且仅当,也就是说x和方向一样。
推论:如果把行走的方向看成一个序列,则序列存在一个循环节是,原因是每一步必定会到下一个对角线,所以该序列和对角线是一样的。
设在一个循环节中向下走了步,向右走了步,即
那么横坐标回到需要经过个循环节,纵坐标回到需要经过个循环节,两个坐标都回到1需要个循环节,而总共有个循环节。
也就是说,我们要让
对一个质因子,及其在的指数,讨论:
若,则,那么左式一定会比比右式在p的指数上少,所以,同理。
若,则即,即,所以左边还是一定比右边少指数,所以
综上所述 ,.
即,易得这是路径合法的充要条件。
因此可以枚举合法的。
计算答案时,考虑枚举撞到障碍的轮数,以及撞到的障碍,因为有循环节,所以所有循环节里州的路线是一样的,也就是说,如果第一轮走到了这个点,那么之后所有循环节里到这个位置时都必须能走。
方便起见,我们可以把所有循环节的障碍重叠起来,设前个循环的障碍重叠起来得到的图是
那么我们在第轮撞到的充要条件是:
1.该路线在可以从左上走到右下。
2.该路线在可以从左上走到或。
这又等价于:
1.该路线在可以从左上走到或。
2.该路线在可以从走到右下。
因此我们只需要知道在或中从左上走到某个点,或从某个点走到右下的方案数即可,显然可以dp出来。
因此总的复杂度是,显然没有一维能够跑满,所以可以轻松通过。
#include<bits/stdc++.h>
using namespace std;
const int N =55;
const int mod = 998244353;
char s[N][N];
int f[N][N],g[N][N];
int gcd(int a,int b)
{
if(!b)return a;
return gcd(b,a%b);
}
int ans=0;
bool A[N][N],B[N][N];
void get(int n,int m)
{
for(int i=0;i<=n+1;i++)
for(int j=0;j<=m+1;j++)
f[i][j]=g[i][j]=0;
f[1][1]=(A[1][1]==0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if((i==1&&j==1)||A[i][j])continue;
f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
}
g[n][m]=(B[n][m]==0);
for(int i=n;i>=1;i--)
for(int j=m;j>=1;j--)
{
if((i==n&&j==m)||B[i][j])continue;
g[i][j]=(g[i+1][j]+g[i][j+1])%mod;
}
}
#define R(x) (x%m+1)
#define D(x) (x%n+1)
int n,m;
void solve()
{
ans=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
int d=gcd(n,m),C=n*m/d;
for(int dx=0;dx<=d;dx++)
if(gcd(dx,n)==1&&gcd(d-dx,m)==1)
{
int dy=d-dx;
for(int i=1;i<=dx+1;i++)
for(int j=1;j<=dy+1;j++)
A[i][j]=B[i][j]=0;
int x=1,y=1;
for(int c=1;c<=C;c++)
{
for(int i=x,a=1;a<=dx+1;i=D(i),a++)
for(int j=y,b=1;b<=dy+1;j=R(j),b++)
A[a][b]|=(s[i][j]=='1');
get(dx+1,dy+1);
for(int i=x,a=1;a<=dx+1;i=D(i),a++)
for(int j=y,b=1;b<=dy+1;j=R(j),b++)
if(s[i][j]=='1')ans=(ans+1ll*((c-1)*(dx+dy)+a-1+b-1)%mod*(f[a-1][b]+f[a][b-1])%mod*g[a][b]%mod)%mod;
for(int i=x,a=1;a<=dx+1;i=D(i),a++)
for(int j=y,b=1;b<=dy+1;j=R(j),b++)
B[a][b]|=(s[i][j]=='1');
for(int a=1;a<=dx;x=D(x),a++);
for(int b=1;b<=dy;y=R(y),b++);
}
}
printf("%d\n",ans);
}
int main()
{
int T;
cin>>T;
while(T--)
{
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现