题意:给你一个n*m的矩阵,从矩阵中的一点(x,y)以(vx,vy)的方向出发,碰到矩阵壁反弹, 若能从矩阵的四个角出去,则输出出去点的坐标,若不能则输出-1。
这题刚开始想用模拟,看其是否能出去,结果超时了啊T^T,超时就很难受了,然后去看了一下大佬的代码,。。是真的长。。本来想放弃的,然后想着都已经开始了,还是看看把,真的佩服这些大佬啊,。。对扩展欧几里得不够熟练也是出现了很多问题,一直wa是真的难受。去看了一下扩展欧几里得算法,还是没有很懂,还是要多练习啊。
题解:大佬题解
思路:这题我们考虑从(x,y)出发,经过矩阵壁反弹,但是我们可以看做把矩阵翻折出去,就可以把其路径看做一条直线,这样就能把它的路径简单化。然后我们延长路径到(0,y0)上,我们可以看做从(0,y0)出发,我们可以看做一个斜率为1的直线,那么y0 = y-x,所以我们可以得到公式
a*n + (y-x) = b*m。然后我们转换一下公式a*n + b*m = (x-y) 那么我们就可以用扩展欧几里得来求出a*n + b*m = gcd(n, m)。如果(x-y)%gcd(n,m) == 0,说明这题有解,反之无解输出-1。然后我们要取a的尽量小的正整数值,不能只对m取模,要对m/gcd(n,m)取模,然后根据算出来的a和b的奇偶性来确定到达的位置。
然后根据一开始输入的方向不同,我们把它都先转换为(1,1)的方向,然后最后再转换回去就好了。如果vx = -1 ,我们设置一个变量fx = 1,后面判断是否要转换回来,并且使x = n-x,y同理。大概就是这么个思路,但是其实自己没有很懂呀。
AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
int n, m, x, y, vx, vy;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x = 1;
y = 0;
return a;
}
int ans = exgcd(b, a%b, y, x);
y -= (a/b)*x;
return ans;
}
int main(){
scanf("%d%d%d%d%d%d",&n,&m,&x,&y,&vx,&vy);
if(x%n==0&&y%m==0){ //开始就在四角的点上
printf("%d %d\n",x,y);
return 0;
}
if(vx == 0){ //预处理判断vx等于0的情况
if(x%n == 0){
if(vy == 1)
printf("%d %d\n",x, m);
else
printf("%d %d\n",x, 0);
}
else
puts("-1");
return 0;
}
if(vy == 0){ //预处理判断vy等于0的情况
if(y%m == 0){
if(vx == 1)
printf("%d %d\n",n, y);
else
printf("%d %d\n",0, y);
}
else
puts("-1");
return 0;
}
int fx = 0,fy = 0;
if(vx == -1){ //x的方向为反的,则记录一下,并转换位置。(转换位置的原题,画一下图,应该就能看出来了)
x = n - x;
fx = 1;
}
if(vy == -1){
y = m - y;
fy = 1;
}
ll a, b;
ll gcd = exgcd(n, m, a, b); //用扩展欧几里得求出gcd,a,b
if((x - y)%gcd!=0){
puts("-1");
return 0;
}
ll t = (x - y)/gcd; //这里a*n + b*m = gcd(n,m) => a'*n + b'*m = (x-y)
a *= t;
b *= t;
ll m1 = m/gcd,n1 = n/gcd; //我们要使a和b尽量小,则把除数变到最小
ll a1 = (a%m1+m1+m1-1)%m1+1; //加两个m1防止出现负数,-1:为了使a1不为0,后面再加一就好了
ll b1 = -((x - y)-a1*n)/m; //根据a*n + b*m = (x-y) 转换成
ll ansx, ansy;
if(a1&1) //奇数的时候,x在n的位置
ansx = n;
else
ansx = 0;
if(b1&1)
ansy = m;
else
ansy = 0;
if(fx) //判断之前是否转换过。
ansx = n-ansx;
if(fy)
ansy = m-ansy;
printf("%lld %lld\n",ansx, ansy);
return 0;
}