【题目大意】
给你一个n*m的矩形,光线从(0,0)出发,沿右上方向以每秒根号2米的速度运动,碰到矩形边界就会反弹(符合物理规律的反弹),询问k个点,这些点都在矩形内部且不在矩形边界上,求光经过这些点的最小时间。如果光不会经过这个点,输出-1.
【数据范围】
0<m,n,k<=100000
【吐槽】
最后1个小时都砸这道题上了,一直是搜索/模拟 的思路,然而正解是扩展欧几里得。(据说模拟也是可以的,然而我不会,sro_姬树流_orz)
【题解】
把矩形对称展开,最后小球在横纵坐标均为maxx=mn/gcd(m,n)处被吸收。
原坐标为(x,y)的小球经过轴对称展开,其坐标为(2kn±x,2sm±y),k,s为整数.要使得在吸收前经过点,则坐标必须在线段(0, 0)到(maxx, mxx)之间。
即要解方程2kn±x=2sm±y,求为正最小的2kn±x。利用扩展欧几里得解方程。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
using namespace std;
long long n,m,k,aa,bb,maxx;
inline long long read()
{
long long x=,f=; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
return x*f;
}
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==) {x=; y=; return a;}
long long r=exgcd(b,a%b,y,x);
y-=a/b*x;
return r;
}
long long gao(long long x,long long y)
{
long long c=x+y,dx,dy;
long long r=exgcd(aa,bb,dx,dy);
if(c%r) return maxx+;
long long mo=bb/r; dx*=c/r;
if(mo<) mo=-mo;
dx=(dx%mo+mo)%mo;
long long ans=*n*dx-x;
if(ans<||ans>maxx) return maxx+;
return ans;
}
long long minn(long long a,long long b) {return a<b?a:b;}
long long solve(long long x,long long y)
{
long long r=__gcd(n,m);
maxx=n*m/r;
long long ans=maxx+;
ans=minn(ans,gao(x,y));
ans=minn(ans,gao(x,-y));
ans=minn(ans,gao(-x,y));
ans=minn(ans,gao(-x,-y));
if(ans==maxx+) return -;
else return ans;
}
int main()
{
//freopen("cin.in","r",stdin);
//freopen("cout.out","w",stdout);
n=read(); m=read(); k=read();
aa=*n; bb=-*m;
for(int i=;i<=k;i++)
{
long long x=read(),y=read();
printf("%I64d\n",solve(x,y));
}
return ;
}