CF982E Billiard

Solution

将图经过小学就学了的反转转化成一条斜率为 (1) 的直线,那么有解就是经过了 (a)(n) 的同时经过了 (b)(m) ,又因为起点 (S(x,y)) 也经过这条直线,所以 (y) 轴交点为 ((0,y-x))

然后就可得到:

[an+(y-x)=bmRightarrow an-bm=x-yRightarrow an+b(-m)=x-y ]

用扩展欧几里得求解完,再 (\%) 出个最小值即可。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;
int n,m,x,y,vx,vy;
int rex,rey;

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

ll exgcd(ll a,ll b,ll &x1, ll &y1){
    if(!b){
        x1=1;
        y1=0;
        return a;
    }
    ll ans=exgcd(b,a%b,x1,y1);
    ll t=x1;
    x1=y1;
    y1=t-a/b*y1;
    return ans;
}

int main(){
    n=read();m=read();x=read();y=read();vx=read();vy=read();
    //判断平行情况
    if(vx==0){
        if(x==0||x==n){
            if(vy==1) printf("%d %d
",x,m);
            else printf("%d %d
",x,0);
        }
        else puts("-1");
        return 0;
    }
    if(vy==0){
        if(y==0||y==m){
            if(vx==1) printf("%d %d
",n,y);
            else printf("%d %d
",0,y);
        }
        else puts("-1");
        return 0;
    }
    if(vx==-1) rex=1,x=n-x;
    if(vy==-1) rey=1,y=m-y;
    //这里就是将速度反转过来了
    ll a,b,gcd;
    gcd=exgcd(n,m,a,b);
    if((x-y)%gcd){//判断无解
        puts("-1");return 0;
    }
    a*=(x-y)/gcd,b*=(x-y)/gcd;//解出来的真正的a,b
    int n1=n/gcd,m1=m/gcd;
    ll a1=(a%m1+m1-1)%m1+1,b1=-((x-y)-a1*n)/m,ansx=n,ansy=m;//最小解
    if(a1%2==0) ansx=n-ansx;//经过偶数个n,此时应该是0
    if(b1%2==0) ansy=m-ansy;//经过偶数个m,此时应该是0
    if(rex) ansx=n-ansx;//反转
    if(rey) ansy=m-ansy;//反转
    printf("%lld %lld
",ansx,ansy);
    return 0;
}
原文地址:https://www.cnblogs.com/jasony/p/13733790.html