codeforces 724c Ray Tracing

好题

原题:

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.

2 ≤ n, m ≤ 100 000, 1 ≤ k ≤ 100 000

 

题意:

在一个m*n的围墙内有一个激光从(0,0)发射,每过1s这个激光的x和y都会+1,如果激光撞到墙上就会反射,方向改变90°,如果撞到墙的四个角就停止,在墙内有k个测试点,现在问激光第一次经过每个测试点的时间是多少

 

首先易证激光一定会停下来而且每个点只会经过一次

正解是exgcd,思路非常妙

首先如果激光射到墙上,为了简化问题不让这个激光反射而是把这个围墙(连带着里面的探测点)沿着被撞倒的墙对称过去,差不多是酱紫:

然后当这个激光经过所有点的x都等于y,易证lcm(n,m)后激光停下

测试点原坐标为(x,y),随着围墙翻转过去后的坐标是(2*r*n±x,2*q*m±y)

如果翻转偶数次,测试点在围墙内的相对位置没变,坐标=2*r*n+x(这里只拿x举例),如果翻转奇数次,坐标=(2*r-1)*n+n-x=2*r*n-x

酱紫的话就有四种情况,只需要取这四种情况的最小值即可

然后对于每个测试点的每种情况,当两坐标相等的时候这个测试点被激光经过,即2*r*n+x=2*q*m+y(这里拿一种情况举例,其它类似(将x/y变为-x/-y即可))

n,m,x,y都是已知的,边形就可以得到2*n*r+2*m*q=y-x

这个就是exgcd的标准形式(注意只有r和q是未知的)

然后搞exgcd就可以辣

最后判一下无解,进行变化一下就可以得到一种情况的答案,与其它情况比较即可

注意exgcd后得出的x(这里的x不是坐标)需要(x%(b/gcd(a,b))+b/gcd(a,b))%(b/gcd(a,b)),先膜再加是防止负数,再膜一次为了保证取到最小值(如果不膜保证解合法但不一定最小)

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define ll long long
 8 ll read(){ll z=0,mark=1;  char ch=getchar();
 9     while(ch<'0'||ch>'9'){if(ch=='-')mark=-1;  ch=getchar();}
10     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
11     return z*mark;
12 }
13 ll n,m,k,oo;
14 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
15 void exgcd(ll a,ll b,ll &x,ll &y){
16     if(!b){  x=1,y=0;  return ;}
17     exgcd(b,a%b,x,y);
18     ll c=x;  x=y,y=c-a/b*y;
19 }
20 ll q(ll sx,ll sy){
21     ll a=(n<<1),b=-(m<<1),c=sy-sx,x,y;
22     exgcd(a,b,x,y);  ll d=gcd(a,b);
23     if(c%d)  return oo;
24     ll md=abs(b/d);
25     x*=c/d;  x=(x%md+md)%md;//如果不膜解合法但不是最小
26     ll z=2*x*n+sx;
27     return (z<0||z>=oo)?oo:z;
28 }
29 ll ans[11000000];
30 int main(){//freopen("ddd.in","r",stdin);
31     cin>>n>>m>>k;  oo=n*m/gcd(n,m)+1;
32     ll x,y,z;
33     for(int i=1;i<=k;++i){
34         x=read(),y=read(),z=oo;
35         z=min(z,q(x,y)),z=min(z,q(-x,-y));
36         z=min(z,q(-x,y)),z=min(z,q(x,-y));
37         printf("%I64d\n",(z==oo)?-1:z);
38     }
39     return 0;
40 }
View Code

 

转载于:https://www.cnblogs.com/JSL2018/p/6405639.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值