CF331D1 Escaping on Beaveractor 题解

## 前言。
本题解是 $D1$ 的题解:  
这篇题解是在 $D1$ 的基础上进行编写的,所以该代码仅仅针对该题,对于另外两个加强版是无法通过的,这里注重从 $0$ 开始的由简入难的思考过程。

这个基础题的思路就是模拟,因为这个 $b$ 实在是太小了。
## 分析。
### 题意简述:
给定一个 $n$ 阶方正地图,地图上存在 $m$ 条路标,其中保证任意的路径与坐标轴平行且两两互不相交,随后 $m$ 行是路标的起始坐标 $\left(x_1,y_1\right)$ 和终点坐标 $\left(x_2,y_2\right)$。随后给出 $q$ 次询问,每次询问给出海狸的初始坐标 $\left(x_i,y_i\right)$ 和初始的走向共四种情况,其中 ```U```、```D```、```L``` 和 ```R``` 分别表示向上移动,向下移动,向左运动和向右运动,还有距离开始经过的时间。时间的限制条件为 $t_i\leq 10^{15}$ 个单位时间。输出 $q$ 行。每行应包含两个整数,海狸在每个询问中当时间为 $t_i$ 时所处的坐标。如果在 $t_i$ 时间内成功离开地图,则打印他最后访问的地图内的坐标。 
### 正式的讲解:
因为有 $t_i$ 的限制条件,所以我们从中入手,显然这个数据太大了,如果对于每个询问都直接模拟会直接超时,所以我们考虑找一个简便的方法来减少每次询问的次数。首先我们需要根据这些路标把整个图建出来,然后开始模拟。有一个很显然的思路是找循环节,如果海狸进了循环,那么无论 $t_i$ 多大,都会在一个特定的范围内循环,所以直接找到循环节后找到 $t_i$ 所对应的那个坐标就行。那么此时这个循环的情况查询时间就成了 $O\left(1\right)$。这个情况在每次询问中直接预处理然后直接对循环节取余即可。

然后这样可以证明,很容易出现循环节,所以可行。剩下的直接模拟即可,时间复杂度就大大降低了。然后最后处理一下直接跑出跑出地图,就直接结束模拟即可,因为海狸已经跑出地图并留下最后的访问过的坐标了。

代码如下,仅供参考:

#include<iostream>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
int n,b,x1,x2,y_1,y2,q1,x,y;
long long t;
char way;
int disx,disy;
long long timee;
int G[1005][1005],vis[1005][1005],sum[1005][1005];
struct node{
    int x,y;
}a[1005*1005];
int dx[7]={0,1,0,-1,0};
int dy[7]={0,0,1,0,-1};
map <char, int> ma;
void add(int x1,int y_1,int x2,int y2){//建图。 
    int dis=0;
    if (x1==x2){
        (y_1<y2)?dis=2:dis=4;
        //处理上下移动的情况。 
    }
    else{
        (x1<x2)?dis=1:dis=3;
        //处理左右移动的情况。 
    }
    G[x2][y2]=dis;
    while (x1!=x2||y_1!=y2){
        G[x1][y_1]=dis;
        x1+=dx[dis];
        y_1+=dy[dis];
    }
}
int main(){
    ma['R']=1;
    ma['U']=2;
    ma['L']=3;
    ma['D']=4;
    cin>>n>>b;
    for (int i=1;i<=n;i++){
        cin>>x1>>y_1>>x2>>y2;
        //cout<<"yep!\n";
        add(x1,y_1,x2,y2);
    }
    cin>>q1;
    for (int i=1;i<=q1;i++){
        timee=0;
        cin>>x>>y>>way>>t;
        disx=dx[ma[way]];
        disy=dy[ma[way]];//看看距离。 
        while(x>=0&&x<=b&&y>=0&&y<=b&&t>0){
            if(G[x][y]){//如果存在路标。 
                disx=dx[G[x][y]];
                disy=dy[G[x][y]];
            }
            if(G[x][y]&&vis[x][y]==i){//如果存在循环节。 
                /*t%=(timee-(long long)sum[x][y]);
                x=a[(long long)sum[x][y]+t].x;
                y=a[(long long)sum[x][y]+t].y;
                break;*/
                long long summ=sum[x][y];
                long long xhj=timee-summ;
                t%=xhj;
                x=a[summ+t].x;
                y=a[summ+t].y;
                break;
            }
            sum[x][y]=timee;
            a[timee].x=x;
            a[timee++].y=y;
            vis[x][y]=i;
            t--;
            x+=disx;
            y+=disy;//更新新的坐标.
        }
        if(x<0){
            x=0;
        }
        if(x>b){
            x=b;
        }
        if(y<0){
            y=0;
        }
        if(y>b){
            y=b;
        }//判断边界情况,是否越界,越界就记录海狸出去的那一个点。 
        cout<<x<<" "<<y<<"\n";
    }
    return 0;
}
/*
4 2
0 0 1 0
2 0 2 1
2 2 1 2
0 2 0 1
15
1 1 R 7
0 0 U 3
1 1 R 888888888888887
0 0 U 404040404040403
2 1 D 1000000000000000
2 0 R 324354768576473
2 0 L 325436475453245
2 0 U 764536452343654
2 0 D 943654765876545
1 1 U 1000000000000000
1 1 D 989898789898789
1 1 L 123456787654344
0 1 L 0
0 1 L 1
0 1 L 999999999999993
*/


## 后记。
一个很好想的思路,但是细节蛮多的。

大家如有疑问,可以在评论区提出,我会尽力解答的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值