当前位置: 代码迷 >> 综合 >> codeforce C. Ray Tracing(扩展欧几里得|模拟)
  详细解决方案

codeforce C. Ray Tracing(扩展欧几里得|模拟)

热度:35   发布时间:2023-12-06 20:14:32.0

Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined)
C. Ray Tracing
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard 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 nm and k (2?≤?n,?m?≤?100?0001?≤?k?≤?100?000) — 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?-?11?≤?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
Note

In the first sample, the ray will consequently pass through the points (0,?0)(1,?1)(2,?2)(3,?3). Thus, it will stop at the point (3,?3) after3 seconds.

In the second sample, the ray will consequently pass through the following points: (0,?0)(1,?1)(2,?2)(3,?3)(2,?4)(1,?3)(0,?2),(1,?1)(2,?0)(3,?1)(2,?2)(1,?3)(0,?4). The ray will stop at the point (0,?4) after 12 seconds. It will reflect at the points (3,?3)(2,?4),(0,?2)(2,?0) and (3,?1).


题目大意:

一条射线从(0,0)射出,速度为√2,方向与x轴夹角45°,碰到边界会镜面反射,方向对称,速度不变,直到射到顶点被吸收。题目给的是一个n*m的空间,四周都是墙壁,然后有k个询问,每次询问的是空间内非角的一个点,你要得出的是最早到达这个点的时刻,如果不能到达则输出-1.


分析:

模拟射线的轨迹, 记录所有的起点,所谓的起点就是碰到墙布上的点,和起始点(0,0)。然后模拟4个运动方向,1斜线右上方,2为斜向左下,3为斜向左上,4为斜向右下。如果一个起点往1或者2方向运动,那么它的x-y的差始终是不会变的,因为x与y是同加同减的。同理往3或者4方向运动时候,该射线的x+y的和是不会变的,因为x与y的加减是异号的。

详细解释请看下面代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const long long INF = 1e10 + 5;
long long vis1[N*2], vis2[N*2], vis3[N*2], vis4[N*2];
int n, m, k;
long long slove(int x, int y)
{long long res = INF, tmp;if(vis1[x - y + N] != -1)   //如果该点可以由1方向射过来{tmp = vis1[x - y + N];      //到达1方向的起点的时间if(x > y)           //因为1方向只能从最左边的墙和最下边的墙的起点射出来,所以看离最下边的墙近还是离最左边的墙近,那个近就是从哪射出来的tmp += y;elsetmp += x;res = min(res, tmp);}if(vis2[x - y + N] != -1){tmp = vis2[x - y + N];if(n - x > m - y)tmp += m - y;elsetmp += n - x;res = min(res, tmp);}if(vis3[x + y] != -1){tmp = vis3[x + y];if(n - x < y)tmp += n - x;elsetmp += y;res = min(res, tmp);}if(vis4[x + y] != -1){tmp = vis4[x + y];if(x < m - y)tmp += x;elsetmp += m - y;res = min(res, tmp);}if(res != INF)return res;elsereturn -1;
}
int main()
{int px, py, dir, tx, ty;long long tmp, time, ans;   //时间需要用long longwhile(cin >> n >> m >> k){memset(vis1, -1, sizeof(vis1));         //全部都设为-1memset(vis2, -1, sizeof(vis2));memset(vis3, -1, sizeof(vis3));memset(vis4, -1, sizeof(vis4));px = py = time = 0; dir = 1;        //dir为方向,px/py为当前点while(true)         //模拟所有起点的过程{if(dir == 1){vis1[px - py +N] = time;        //因为px-py可能小于0,所以加上Ntmp = min(n - px, m - py);      //是先到最上边的墙,还是先到最右边的墙px += tmp;          //把达到墙需要的时间加上py += tmp;time += tmp;if(px == n && py == m)  //如果进入右上定点则结束break;else if(px == n)dir = 3;else if(py == m)dir = 4;}else if(dir == 2){vis2[px - py + N] = time;tmp = min(px, py);px -= tmp;py -= tmp;time += tmp;if(px == 0 && py == 0)break;else if(px == 0)dir = 4;else if(py == 0)dir = 3;}else if(dir == 3){vis3[px + py] = time;tmp = min(px, m - py);px -= tmp;py += tmp;time += tmp;if(px == 0 && py == m)break;else if(px == 0)dir = 1;else if(py == m)dir = 2;}else if(dir == 4){vis4[px + py] = time;tmp = min(n - px, py);px += tmp;py -= tmp;time += tmp;if(px == n && m == 0)break;else if(px == n)dir = 2;else if(py == 0)dir = 1;}}while(k--){scanf("%d%d", &tx, &ty);ans = slove(tx, ty);printf("%I64d\n", ans);}}return 0;
}/*_ooOoo_o8888888o88" . "88(| -_- |)O\  =  /O____/`---'\____.'  \\|     |//  `./  \\|||  :  |||//  \/  _||||| -:- |||||-  \|   | \\\  -  /// |   || \_|  ''\---/''  |   |\  .-\__  `-`  ___/-. /___`. .'  /--.--\  `. . __."" '<  `.___\_<|>_/___.'  >'"".| | :  `- \`.;`\ _ /`;.`/ - ` : | |\  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^I have a dream!A AC deram!!orz orz orz orz orz orz orz orz orz orz orzorz orz orz orz orz orz orz orz orz orz orzorz orz orz orz orz orz orz orz orz orz orz*/


扩展欧几里得解法:

把矩形对称展开,最后小球在横纵坐标均为 maxx=mn/gcd(m,n) 处被吸收。 
原坐标为 (x,y) 的小球经过轴对称展开,其坐标为 (2kn±x,2sm±y),k,s .要使得在吸收前经过点,则坐标必须在线段(0, 0)到(maxx, maxx)之间。 
即要解方程 2kn±x=2sm±y ,求为正最小的 2kn±x 。利用扩展欧几里得解方程。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL n, m;
LL exgcd(LL a, LL b, LL &x, LL &y)
{if(b==0){x=1;y=0;return a;}LL r=exgcd(b, a%b, y, x);y-=a/b*x;return r;
}
LL get_min_answer(LL a, LL b, LL c, LL &x, LL &y)
{LL gcd=exgcd(a, b, x, y);if(c%gcd) return -1;LL s=b/gcd;x*=c/gcd;if(s<0) s=-s;x=(x%s+s)%s;return 0;
}
LL solution(LL dx, LL dy, LL Max)
{LL x, y;if(get_min_answer(2*n, -2*m, dy-dx, x, y)==-1)return Max+1;LL tx=2*x*n+dx;if(tx<0||tx>Max) return Max+1;return  tx;
}
LL solve(LL x, LL y)
{LL gcd=__gcd(n, m);LL Max=n*m/gcd, ans=Max+1;ans=min(ans, solution(-x, -y, Max));ans=min(ans, solution(x, -y, Max));ans=min(ans, solution(-x, y, Max));ans=min(ans, solution(x, y, Max));if(ans==Max+1) return -1;return ans;
}
int main()
{LL k, x, y;while(cin>>n>>m>>k){while(k--){cin>>x>>y;cout<<solve(x, y)<<endl;}}return 0;
}