Codeforce 724C. Ray Tracing By Assassin

本文介绍了一个关于激光在矩形房间内反射并最终达到指定传感器的模拟算法。文章详细阐述了如何通过枚举不同反射路径来确定激光首次经过各传感器的时间,并提供了一段完整的C++实现代码。
C. Ray Tracing
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
There are k sensors located in the rectangular room of size n × m meters. The i-th sensor is located at point (xi, yi). All sensors are located at distinct points strictly inside the rectangle. 
Opposite corners of the room are located at points (0, 0) and (n, m). Walls of the room are parallel to coordinate axes.
At the moment 0, from the point (0, 0) the laser ray is released in the direction of point (1, 1). The ray travels with a speed of  meters per second. Thus, the ray will reach the point (1, 1) in exactly one second after the start.
When the ray meets the wall it's reflected by the rule that the angle of incidence is equal to the angle of reflection. If the ray reaches any of the four corners, it immediately stops.
For each sensor you have to determine the first moment of time when the ray will pass through the point where this sensor is located. If the ray will never pass through this point, print  - 1 for such sensors.
Input
The first line of the input contains three integers n, m and k (2 ≤ n, m ≤ 100000, 1 ≤ k ≤ 100000) — lengths of the room's walls and the number of sensors.
Each of the following k lines contains two integers xi and yi (1 ≤ xi ≤ n - 1, 1 ≤ yi ≤ m - 1) — coordinates of the sensors. It's guaranteed that no two sensors are located at the same point.
Output
Print k integers. The i-th of them should be equal to the number of seconds when the ray first passes through the point where the i-th sensor is located, or  - 1 if this will never happen. 
Examples
Input
3 3 4
1 1
1 2
2 1
2 2
Output
1
-1
-1
2
Input
3 4 6
1 1
2 1
1 2
2 2
1 3
2 3
Output
1
-1
-1
2
5
-1
Input
7 4 5
1 3
2 2
5 1
5 3
4 3
Output
13
2
9
5
-1

题目大意就是一个n*m的长方形,从(0,0)一束光线沿到(1,1)方向射出,如果遇到边界就反弹,如果到四个角中的一个就停止,给定k个点更新该点第一次光到达的时间,不能到就输出-1

思路是什么?我反正时模拟。。。好多坑点。。。

首先我们要知道有四个可能反射的方向,而检验第i个点沿四个方向可以查到边界上的四个点,检验这四个点是否光经过过,且方向是向第i点的。总共撑死了纪录四个边界上的所有点,400000个吧。这里弱不会哈希,就直接用了四个结构体数组表示了。
那么怎么做!首先模拟光路,总共可能有四个面反射,每个面有两种反射方向,dfs枚举。每次纪录出发点,用一个结构体纪录光线经过该点时的时间和方向(这里要在结构体内用数组,时方向和时间一一对应)。之后一个个检验就行了。。。
注意t一定要用long long!

下面是代码。。。重点还是思路吧,我的代码很丑,而且估计页看不懂。。。

#include<bits/stdc++.h>
#define input freopen("input.txt","r",stdin)
using namespace std;
long long m,n,k;
int pos[100010][2];   //纪录每次的检验值 
long long times[100010];   //每个检验值的时间 
typedef struct node        //结构体,纪录每个光到达边界的数据 
{
    int dir[5];    //注意这是数组!每个方向对应一个时间t 
    long long t[5];
} node;
// 
node a1[100010];//第一行   
node a2[100010];//第n行
node b1[100010];//第一列 
node b2[100010];//第m列 
void handle(int x,int y,int dir,long long t)  //这个时更新光路到达边界的信息,纪录反射点及其方向 
{
    if(x==0)                    //就是枚举。。。 
    {
        a1[y].t[dir]=t;
        a1[y].dir[dir]=1;
    }
    else if(x==n)
    {
        a2[y].t[dir]=t;
        a2[y].dir[dir]=1;
    }
    else if(y==0)
    {
        b1[x].t[dir]=t;
        b1[x].dir[dir]=1;       
    }
    else if(y==m)
    {
        b2[x].t[dir]=t;
        b2[x].dir[dir]=1;       
    }
}
void dfs(int startx,int starty,int dir,long long t)
{
    int shu,heng;
    if(dir==1){  shu=n-startx;  heng=m-starty;}    //还是枚举。。。思考量好大。。。 
    else if(dir==2) {  shu=startx;  heng=m-starty;}
    else if(dir==3){   shu=startx;  heng=starty;}
    else {shu=n-startx;heng=starty;}
    if(shu==heng)          //结束条件 
    {
        handle(startx,starty,dir,t);
        return ;
    }
    if(dir==1)             //各个方向的枚举。。。 
    {
        if(shu<heng)
        {
            handle(startx,starty,dir,t);
            dfs(startx+shu,starty+shu,2,t+shu);
        }
        else 
        {
            handle(startx,starty,dir,t);
            dfs(startx+heng,starty+heng,4,t+heng);
        }
    }
    else if(dir==2)
    {
        if(shu<heng)
        {
            handle(startx,starty,dir,t);
            dfs(startx-shu,starty+shu,1,t+shu);
        }
        else 
        {
            handle(startx,starty,dir,t);
            dfs(startx-heng,starty+heng,3,t+heng);
        }
    }
    else if(dir==3)
    {
        if(shu<heng)
        {
            handle(startx,starty,dir,t);
            dfs(startx-shu,starty-shu,4,t+shu);
        }
        else 
        {
            handle(startx,starty,dir,t);
            dfs(startx-heng,starty-heng,2,t+heng);
        }
    }
    else if(dir==4)
    {
        if(shu<heng)
        {
            handle(startx,starty,dir,t);
            dfs(startx+shu,starty-shu,3,t+shu);
        }
        else 
        {
            handle(startx,starty,dir,t);
            dfs(startx+heng,starty-heng,1,t+heng);
        }
    }
}
void heheda(int i)    //处理查询数组的每一个点 
{
    int x,y,shu,heng;
    long long int minn=10000000000000;  //这个数要足够大,否则无法更新到 
    x=pos[i][0];
    y=pos[i][1];
    shu=x;    //左上角   //各种枚举。。。 
    heng=y;
    if(shu<=heng)
    {
        if(a1[y-shu].dir[1]&&minn>=a1[y-shu].t[1]+shu)
        {
            minn=a1[y-shu].t[1]+shu;
        }
    }
    else 
    {
        if(b1[x-heng].dir[1]&&minn>=b1[x-heng].t[1]+heng)
        {
            minn=b1[x-heng].t[1]+heng;
        }
    }
    shu=n-x;    //左下角 
    heng=y; 
    if(shu<=heng)
    {
        if(a2[y-shu].dir[2]&&minn>=a2[y-shu].t[2]+shu)
        {
            minn=a2[y-shu].t[2]+shu;
        }
    }
    else 
    {
        if(b1[x+heng].dir[2]&&minn>=b1[x+heng].t[2]+heng)
        {
            minn=b1[x+heng].t[2]+heng;
        }
    }   
    shu=n-x;    //右下角 
    heng=m-y;   
    if(shu<=heng)
    {
        if(a2[y+shu].dir[3]&&minn>=a2[y+shu].t[3]+shu)
        {
            minn=a2[y+shu].t[3]+shu;
        }
    }
    else 
    {
        if(b2[x+heng].dir[3]&&minn>=b2[x+heng].t[3]+heng)
        {
            minn=b2[x+heng].t[3]+heng;
        }
    }   
    shu=x;    //右上角 
    heng=m-y;   
    if(shu<=heng)
    {
        if(a1[y+shu].dir[4]&&minn>=a1[y+shu].t[4]+shu)
        {
            minn=a1[y+shu].t[4]+shu;
        }
    }
    else 
    {
        if(b2[x-heng].dir[4]&&minn>=b2[x-heng].t[4]+heng)
        {
            minn=b2[x-heng].t[4]+heng;
        }
    }
    if(minn!=10000000000000)times[i]=minn;  //更新! 
}
int main()
{
    input;
    int i,j;
    while(cin>>n>>m>>k)
    {
        for(i=1;i<=k;i++)
        {
            cin>>pos[i][0]>>pos[i][1];
            times[i]=-1;
        }
        for(i=0;i<=n;i++)       //这样初始化,不要直接都memset,浪费时间 
        {
            memset(b1[i].dir,0,sizeof(b1[i].dir));
            memset(b1[i].t,0,sizeof(b1[i].t));
            memset(b2[i].dir,0,sizeof(b2[i].dir));
            memset(b2[i].t,0,sizeof(b2[i].t));
        }
        for(i=0;i<=m;i++)
        {
            memset(a1[i].dir,0,sizeof(a1[i].dir));
            memset(a1[i].t,0,sizeof(a1[i].t));
            memset(a2[i].dir,0,sizeof(a2[i].dir));
            memset(a2[i].t,0,sizeof(a2[i].t));
        }
        dfs(0,0,1,0);    //搜索一遍光路 
        for(i=1;i<=k;i++)
        {
            heheda(i);
        }
        for(i=1;i<=k;i++)
        {
            cout<<times[i]<<endl;
        }
    }
    return 0;
 } 
/*
注意我用的坐标轴时这样的 
(0,0)(0,1)
(1,0)(1,1) 
*/ 

感觉这挺取巧的,最好各位还是学一学exgcd的那个方法吧,希望对大家有帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值