HDU 5514 Collision(扩展欧几里得+解方程)——2014ACM/ICPC亚洲区北京站

传送门

Matt is playing a naive computer game with his deeply loved pure girl.

The playground is a rectangle with walls around. Two balls are put in different positions inside the rectangle. The balls are so tiny that their volume can be ignored. Initially, two balls will move with velocity (1, 1). When a ball collides with any side of the rectangle, it will rebound without loss of energy. The rebound follows the law of refiection (i.e. the angle at which the ball is incident on the wall equals the angle at which it is reflected).

After they choose the initial position, Matt wants you to tell him where will the two balls collide for the first time.
 

Input
The first line contains only one integer T which indicates the number of test cases.

For each test case, the first line contains two integers x and y. The four vertices of the rectangle are (0, 0), (x, 0), (0, y) and (x, y). (1 ≤ x, y ≤ 10 5)

The next line contains four integers x 1, y 1, x 2, y 2. The initial position of the two balls is (x 1, y 1) and (x 2, y 2). (0 ≤ x 1, x 2 ≤ x; 0 ≤ y 1, y 2 ≤ y)
 

Output
For each test case, output “Case #x:” in the first line, where x is the case number (starting from 1).

In the second line, output “Collision will not happen.” (without quotes) if the collision will never happen. Otherwise, output two real numbers x c and y c, rounded to one decimal place, which indicate the position where the two balls will first collide.
 

Sample Input
  
  
3
10 10
1 1 9 9
10 10
0 5 5 10
10 10
1 0 1 10
 

Sample Output
  
  
Case #1:
6.0 6.0
Case #2:
Collision will not happen.
Case #3:
6.0 5.0

Hint
In first example, two balls move from (1, 1) and (9, 9) both with velocity (1, 1), the ball starts from (9, 9) will rebound at point (10, 10) then move with velocity (−1, −1). The two balls will meet each other at (6, 6).

题目大意:

给了一个 xy 的矩阵,在这个矩阵中有两个小球(体积可以忽略):小球1 (x1,y1) , 小球2 (x2,y2) 现在这两个小球都以 (1,1) 的速度在运动,如果碰到边界会发生反弹(不损失能量),现在求两个小球第一次碰撞时的坐标。

解题思路:

我们可以将这个小球的运动在 x 轴 和 y 轴上进行分解,在 x 轴上是匀速直线运动,同理在 y 轴上也是匀速直线运动。现在小球1 和 小球2 的坐标有四种情况:
1) x1=x2 and y1=y2
2) x1=x2 and y1y2
3) x1x2 and y1=y2
4) x1x2 and x1y2
现在就四种情况分别进行讨论:
如果是第一种情况的话,第一次碰撞的点就是 (x1,y1) || (x2,y2)
如果是第二种情况的话,我们首先设在第一次碰撞的时间为 ty ,第一次碰撞的点为 (xp,yp)
那么就有 下图所示方程成立:
这里写图片描述
更正:对于图中的第二个式子有错误,应该是: ty=ypy1

然后可以解得: ty = (2y(y1+y2))2 ,然后 (xp,yp) 就能确定了
同理,如果是第三种情况的话,我们可以解出 tx = (2x(x1+x2))2 , 然后 (xp,yp) 也能确定了
如果是第四种情况的话就只需要加上一个周期就行了,对于 x 轴来说,
tx = (2x(x1+x2))2+ax  aa x
ty = (2y(y1+y2))2+by  bb y
然后又因为 tx = ty , 然后联力两个方程组得到如下方程:
(2x(x1+x2))2+ax=(2y(y1+y2))2+by
移项得:
axby=(2y(y1+y2))2(2x(x1+x2))2

这就是我们所常见的一元二次方程,然后扩展欧几里得一下求得 a 的最小正整数解就 OK 了。
需要注意的是,这里有一个小 trick:因为方程里有除法操作,所以我们先把所有的变量乘以 2,然后在操作就行了,还有就是 如果最后求得的解如果大于 x 或者 y <script type="math/tex" id="MathJax-Element-480">y</script> 的话,就是反弹了,然后减一下就行,具体看代码。
代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline LL GCD(LL a, LL b){
    if(b == 0) return a;
    return GCD(b, a%b);
}
void Exgcd(LL a, LL b, LL &x, LL &y){
    if(b == 0){
        x = 1, y = 0;
        return;
    }
    LL x1, y1;
    Exgcd(b, a%b, x1, y1);
    x = y1;
    y = x1 - (a/b)*y1;
}
int main()
{
    int T; scanf("%d", &T);
    for(int cas=1; cas<=T; cas++){
        LL x, y, x1, x2, y1, y2;
        scanf("%lld%lld%lld%lld%lld%lld", &x, &y, &x1, &y1, &x2, &y2);
        x<<=1, y<<=1, x1<<=1, y1<<=1, x2<<=1, y2<<=1;
        printf("Case #%d:\n",cas);
        LL tx = (2*x-(x1+x2))/2, ty = (2*y-(y1+y2))/2;
        if(x1==x2 && y1==y2) printf("%.1f %.1f\n", 0.5*x1, 0.5*y1);
        else if(x1 == x2){
            if(y1 > y2) swap(y1, y2);
            x1 = (x1+ty)%(2*x), y1 = (y1+ty)%(y*2);
            if(x1 > x) x1 = 2*x - x1;
            if(y1 > y) y1 = 2*y - y1;
            printf("%.1f %.1f\n", 0.5*x1, 0.5*y1);
        }
        else if(y1 == y2){
            if(x1 > x2) swap(x1, x2);
            x1 = (x1+tx)%(2*x), y1 = (y1+tx)%(y*2);
            if(x1 > x) x1 = 2*x - x1;
            if(y1 > y) y1 = 2*y - y1;
            printf("%.1f %.1f\n", 0.5*x1, 0.5*y1);
        }
        else{
            LL tp = ty - tx;
            LL d = GCD(x, y);
            if(tp % d) puts("Collision will not happen.");
            else{
                LL a = x / d, b = y / d, c = tp / d, xx;
                Exgcd(a, b, xx, ty);
                xx *= c;
                xx = (xx%b+b)%b;
                tx = tx + x*xx;
                x1 = (x1+tx)%(2*x), y1 = (y1+tx)%(y*2);
                if(x1 > x) x1 = 2*x - x1;
                if(y1 > y) y1 = 2*y - y1;
                printf("%.1f %.1f\n", 0.5*x1, 0.5*y1);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值