JZOJ4019. 【雅礼联考DAY02】Path

15 篇文章 0 订阅

题意

给定一个 n∗ m 的网格,你在左下角 (n,1),你只能往前走或者右拐,障碍和走过的点不能走。求走到 (y,x) 的方案数 mod k 的值。

数据范围

n,m ≤ 100,k ≤ 10^9.

Analysis

首先一眼非常不可做,我们画图看看,发现它走的路线一定是圈套圈。类似于层层矩形相嵌。此时有想法了,我们考虑对矩形 DP D P 。设 fp,i,j,k,l f p , i , j , k , l ,表示当前方向为 p p ,矩形左上角为i,j,右下角为 k,l k , l 的方案。
在拐点统计方案,考虑倒着从终点开始做。但是直接统计是 O(n5) O ( n 5 ) 的,需要在拐点枚举走多少步。考虑每一次拐前的矩形,发现每一次都是少一列,或少一行的矩形。那我们可以考虑矩形构建组合起来。例如 f0,i,j,k,l=f0,i,j,k,l1+f1,i+1,j,k,l f 0 , i , j , k , l = f 0 , i , j , k , l − 1 + f 1 , i + 1 , j , k , l ,考虑路线组合前矩形的形状。但是空间复杂度爆炸了。
由于每一次矩形行列总和加 1 1 。所以我们可以枚举行列总和,然后滑动数组优化空间,就可以通过这道题目。P.SDP的初值有点奇妙,不能直接赋值,只有刚好从起点走了一个方向才能赋 1 1 <script type="math/tex" id="MathJax-Element-10">1</script>。

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 100 + 5;
char s[N][N];
int f[2][N][N][N][4],h[N][N],li[N][N];
int n,m,x,y,K;
int main()
{
    scanf("%d%d%d",&n,&m,&K);
    scanf("%d%d",&x,&y); swap(x,y);
    for (int i = 1 ; i <= n ; ++i)
        for (int j = 1 ; j <= m ; ++j) scanf(" %c",&s[i][j]);
    for (int i = 1 ; i <= n ; ++i)
        for (int j = 1 ; j <= m ; ++j)
        {
            h[i][j] = h[i - 1][j] + (s[i][j] == '*');
            li[i][j] = li[i][j - 1] + (s[i][j] == '*');
        }
    for (int i = 0 ; i <= n + m - 2 ; ++i)
        for (int j = y ; j >= 1 && y - j <= i ; --j)
            for (int l = y ; l <= m && l - j <= i ; ++l)
                for (int k = x ; k <= n ; ++k)
                {
                    int d = k + l - i - j,s = i & 1,nx = s ^ 1;
                    if (d > x || d < 0) continue;
                    f[s][j][k][l][0] = (l > y) * f[nx][j][k][l - 1][0] + (li[d][j - 1] == li[d][l] && d < x) * f[nx][j][k][l][1];
                    f[s][j][k][l][1] = (k > x) * f[nx][j][k - 1][l][1] + (h[d - 1][l] == h[k][l] && l > y) * f[nx][j][k][l - 1][2];
                    f[s][j][k][l][2] = (j < y) * f[nx][j + 1][k][l][2] + (li[k][j - 1] == li[k][l] && k > x) * f[nx][j][k - 1][l][3];
                    f[s][j][k][l][3] = (d < x) * f[nx][j][k][l][3] + (h[d - 1][j] == h[k][j] && j < y) * f[nx][j + 1][k][l][0];
                    f[s][j][k][l][0] += (d == x && l == y && li[d][j - 1] == li[d][l]);
                    f[s][j][k][l][1] += (k == x && l == y && h[d - 1][l] == h[k][l]);
                    f[s][j][k][l][2] += (k == x && j == y && li[k][j - 1] == li[k][l]);
                    f[s][j][k][l][3] += (d == x && j == y && h[d - 1][j] == h[k][j]);
                    for (int now = 0 ; now < 4 ; ++now) f[s][j][k][l][now] %= K;
                }
    printf("%d\n",f[(n + m - 2) & 1][1][n][m][3]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值