1 条题解
-
0
神仙题。对第一篇题解做出了一些补充说明并说了一下自己的理解。
首先你考虑去刻画小偷走的过程,发现它必然是一个划分螺旋矩阵的过程,如图所示,借用了一下第一篇题解的图:

其中红色的是你走的路线,黑色是你划分出来的矩阵,并且我们发现,你总按照上,右,下,左的循环去走。
我们以此来考虑一个很神的 DP 状态,定义 表示你在矩形 内,所有满足条件的路线数量,并且当前你沿着 方向运动, 分别表示你当前在的这个矩形的上、右、左、下边界, 就是你行走的状态, 表示在向上走, 表示在向右走,以此类推。继续考虑转移,我们用小矩阵去更新大矩形,以当前正在向上为例,也就是我们要找到一个高度去右拐,此时最左边的一列已经访问过了,所以要 ,于是如果不考虑障碍物,转移式为 ,解图大概是这样的(为 AI 大模型生成,仅供参考):

此时考虑障碍物的问题,我们记路径上是否有警察即为 ,具体的,从当前位置走到转弯点的那条边上有没有障碍,维护是简单的随便前缀和可以求得,把 乘入 DP 转移即可。
然后剩下的转移式类似去考虑即可,加入障碍物也是简单的。接着就是要去优化这个 DP 了,我们把求和式拆一下,还是以向上为例,那么就可以拆成 $f_{u,r,d,l+1,1}\times check_u+\sum_{k=u+1}^d (f_{u,r,d,l+1,1}\times check_k)$,后面那部分恰好是 ,那么我们的转移就被优化到了 。此时再去考虑空间我问题,你注意到每一个来源状态的矩形大小都减少了 ,也就是说当前层只依赖长度和宽度和少 的层,我们对长度和宽度的和做滚动数组即可。
#include<bits/stdc++.h> using namespace std; const int N=105; int n,m,mod; int x,y; int s1[N][N]; int s2[N][N]; int f[2][N][N][N][4]; int main(){ cin>>n>>m>>mod>>y>>x; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ char c; cin>>c; while(c!='+'&&c!='*')cin>>c; s1[i][j]=s1[i][j-1]+(c=='*'); s2[i][j]=s2[i-1][j]+(c=='*'); } } for(int s=2;s<=n+m;s++){ int tmp=s&1; int last=tmp^1; for(int h=1;h<s;h++){ int w=s-h; if(h>n||w>m)continue; for(int u=1;u+h-1<=n;u++){ int d=u+h-1; for(int l=1;l+w-1<=m;l++){ int r=l+w-1; if(d<x||r<y)continue; f[tmp][u][l][d][0]= ( f[last][u+1][l][d][0] + (s2[d][l]==s2[u-1][l]) * (f[last][u][l+1][d][1]+(u==x&&l==y)) )%mod; f[tmp][u][l][d][1]= ( f[last][u][l][d][1] + (s1[u][r]==s1[u][l-1]) * (f[last][u+1][l][d][2]+(u==x&&r==y)) )%mod; f[tmp][u][l][d][2]= ( f[last][u][l][d-1][2] + (s2[d][r]==s2[u-1][r]) * (f[last][u][l][d][3]+(d==x&&r==y)) )%mod; f[tmp][u][l][d][3]= ( f[last][u][l+1][d][3] + (s1[d][r]==s1[d][l-1]) * (f[last][u][l][d-1][0]+(d==x&&l==y)) )%mod; } } } } cout<<f[(n+m)&1][1][1][n][0]; }
- 1
信息
- ID
- 2779
- 时间
- 14000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 5
- 上传者