Atcoder Beginnner Contest 265 problem E 题解

这篇博客详细解析了Atcoder Beginner Contest 265问题E,介绍了一个人在有障碍物的地图上通过传送器进行移动的路径计数问题。博主采用三维动态规划的方法,通过枚举三种操作的次数来确定人的位置,并避免在无限大的地图上枚举每个点。代码实现部分给出了解决方案。

题目链接:点我

题目大意

一个人初始在 ( 0 , 0 ) (0,0) (0,0)的位置。他有一个传送器。他会进行 N N N次操作,每次操作进行一下三种中的一种(当前他在 ( x , y ) (x,y) (x,y)的位置):

  • ( x , y ) (x,y) (x,y)传送到 ( x + A , x + B ) (x+A,x+B) (x+A,x+B)的位置;
  • ( x , y ) (x,y) (x,y)传送到 ( x + C , x + D ) (x+C,x+D) (x+C,x+D)的位置;
  • ( x , y ) (x,y) (x,y)传送到 ( x + E , x + F ) (x+E,x+F) (x+E,x+F)的位置;

另外,在地图上有 M M M个点有障碍物。这个人不能传送到这些点上。

问这个人从初始点开始,经过 N N N次操作后能得到多少不同的路径。答案对 998244353 998244353 998244353取模。

第一行输入 N , M N,M N,M,第二行输入 A , B , C , D , E , F A,B,C,D,E,F A,B,C,D,E,F,接下来 M M M行,每行两个整数,表示障碍物的坐标。

样例输入1

2 2
1 1 1 2 1 3
1 2
2 2

样例输出1

5

样例解释1

有以下五条路径:

  • ( 0 , 0 ) → ( 1 , 1 ) → ( 2 , 3 ) (0,0) \rightarrow (1,1) \rightarrow (2,3) (0,0)(1,1)(2,3)
  • ( 0 , 0 ) → ( 1 , 1 ) → ( 2 , 4 ) (0,0) \rightarrow (1,1) \rightarrow (2,4) (0,0)(1,1)(2,4)
  • ( 0 , 0 ) → ( 1 , 3 ) → ( 2 , 4 ) (0,0) \rightarrow (1,3) \rightarrow (2,4) (0,0)(1,3)(2,4)
  • ( 0 , 0 ) → ( 1 , 3 ) → ( 2 , 5 ) (0,0) \rightarrow (1,3) \rightarrow (2,5) (0,0)(1,3)(2,5)
  • ( 0 , 0 ) → ( 1 , 3 ) → ( 2 , 6 ) (0,0) \rightarrow (1,3) \rightarrow (2,6) (0,0)(1,3)(2,6)

样例输入2

10 3
-1000000000 -1000000000 1000000000 1000000000 -1000000000 1000000000
-1000000000 -1000000000
1000000000 1000000000
-1000000000 1000000000

样例输出2

0

样例输入3

300 0
0 0 1 0 0 1

样例输出3

292172978

数据范围

  • 1 ≤ N ≤ 300 1 \leq N \leq 300 1N300
  • 0 ≤ M ≤ 1 0 5 0 \leq M \leq 10^5 0M105
  • − 1 0 9 ≤ A , B , C , D , E , F ≤ 1 0 9 -10^9 \leq A,B,C,D,E,F \leq 10^9 109A,B,C,D,E,F109
  • ( A , B ) , ( C , D ) , ( E , F ) (A,B),(C,D),(E,F) (A,B),(C,D),(E,F)是互不相同的
  • − 1 0 9 ≤ X i , Y i ≤ 1 0 9 -10^9 \leq X_i,Y_i \leq 10^9 109Xi,Yi109
  • 所有 ( X i , Y i ) (X_i,Y_i) (Xi,Yi)是互不相同的
  • 所有输入均为整型数

解析

本蒟蒻第一次考场拿下第五题!!!(纪念一下)

首先想到的是最最最暴力的 B F S BFS BFS,时间复杂度 O ( 3 N ) O(3^N) O(3N),(铁定能过)

一般对于这种数路径,又有模数的问题,很容易就想到 d p dp dp

但这道题的地图是无限大的,所以不可能枚举点。然而我们发现,如果我们确定了三种操作的次数,那么这个人最终到达的点也可以被确定。因此,我们不妨枚举三个操作所用的次数,这样就可以知道当前这个人在那个点,如下:

//三层枚举
int gx=i*A+j*C+k*E;
int gy=i*B+j*D+k*F;

定义 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示这个人走到 ( i , j , k ) (i,j,k) (i,j,k)表示的点处的方案数。

然后,这个点只可能由三种状态转移而来,即 f [ i − 1 ] [ j ] [ k ] , f [ i ] [ j − 1 ] [ k ] , f [ i ] [ j ] [ k − 1 ] f[i-1][j][k],f[i][j-1][k],f[i][j][k-1] f[i1][j][k],f[i][j1][k],f[i][j][k1]。用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]将它们累加。

如果说 ( i , j , k ) (i,j,k) (i,j,k)这个点是一个障碍物,那么 f [ i ] [ j ] [ k ] = 0 f[i][j][k]=0 f[i][j][k]=0.

i + j + k = = N i+j+k==N i+j+k==N时,将答案累加即可。

比较有趣的一点是,这道题的时间限制是 3 s 3s 3s,众所周知,测评器 1 s 1s 1s能跑 1 0 7 10^7 107次循环,而 30 0 3 = 2.7 × 1 0 7 300^3=2.7 \times 10^7 3003=2.7×107,刚好是 3 s 3s 3s的时限。本蒟蒻也是从这里受到了启发。

好了,话不多说,上代码:


代码实现:

#include<iostream>
#include<map>
using namespace std;
#define int long long
const int N=305,mod=998244353,base=1e9;
int n,m,a,b,c,d,e,F;
int f[N][N][N],ans;
int x,y;
map<int,bool>mp;
inline int get_point(int x,int y){
    return (x-1)*base+y;
}
signed main(){
    cin>>n>>m>>a>>b>>c>>d>>e>>F;
    for(int i=1;i<=m;i++) cin>>x>>y,mp[get_point(x,y)]=1;//坐标很大,我们可以将二维坐标一维化,然后用map存储
    f[0][0][0]=1;//初始在(0,0)的位置,所以
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++){
            if(i+j>n) break;
            for(int k=0;k<=n;k++){
                if(i+j+k>n) break; //玄学优化
                if(i==0&&j==0&&k==0) continue; //起始点不用更新
                int gx=i*a+j*c+k*e;
                int gy=i*b+j*d+k*F;
                if(mp[get_point(gx,gy)]) {f[i][j][k]=0;continue;} //障碍物
                if(i) f[i][j][k]=(f[i][j][k]+f[i-1][j][k])%mod;
                if(j) f[i][j][k]=(f[i][j][k]+f[i][j-1][k])%mod;
                if(k) f[i][j][k]=(f[i][j][k]+f[i][j][k-1])%mod;
                //一定要注意是否为0,不然数组可能会越界
                if(i+j+k==n) ans=(ans+f[i][j][k])%mod;
            }
        }
    cout<<ans;
    //跑出来刚好2700ms,神不神奇呢
}

完结撒花~~~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值