CF982E Billiard

Solution

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

然后就可得到:

\[an+(y-x)=bm\Rightarrow an-bm=x-y\Rightarrow 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\n",x,m);
            else printf("%d %d\n",x,0);
        }
        else puts("-1");
        return 0;
    }
    if(vy==0){
        if(y==0||y==m){
            if(vx==1) printf("%d %d\n",n,y);
            else printf("%d %d\n",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\n",ansx,ansy);
    return 0;
}
posted @ 2020-09-26 09:25  jasony_sam  阅读(215)  评论(0编辑  收藏  举报