点击打开链接
题意:
n*m的矩形内有k个点,四周有围墙围起来。从(0,0)45度发射小球,速度为2√每次遇到墙都正常反弹,直到射到顶点被吸收。问每个点第一次被经过的时刻。
思路:
我们试着将这个反射的过程看成穿过,那么需要将整个矩阵展开(即变成一条直线).即所有穿过的都是关于 x==2*k*n,或者y == 2*kk*m进行对称的,根据轴对称性计算坐标进而可以得到每个点的坐标为(2*k*n±x,2*kk*m±y)。我们又发现最后肯定是在 n*m/gcd(n*m)处被吸收. 因为小球是按照y=x的直线运动,所以当 2*k*n±x=2*kk*m±y 有解.因为求第一次被经过的时刻,所以这里的2*k*n和2*kk*m应当为最小, 即 2*k*n-2*kk*m = ±(x±y) ,这里x,y已知,我们要求的就是最小的k,kk使得等式成立.
上述这个问题可以用扩展欧几里得来解决,即 a*x+b*y = c,求一组可行解使得等式成立(最小解)
#include
using namespace std;
typedef long long ll;
ll n,m,k;
ll ex_gcd(ll a, ll b, ll &x, ll &y){
if(b==0){
x = 1; y = 0;
return a;
}
ll tmp = ex_gcd(b, a%b, y, x);
y -= a/b*x;
return tmp;
}
ll cal(ll a, ll b, ll c,ll & x,ll &y)
{
ll g = ex_gcd(a, b, x, y);
if(c % g) return -1;
x *= c/g;
b /= g;
if(b < 0) b = -b; //ran < 0 时候要变成正数
x = (x % b + b) % b;
return 0;
}
ll gao(ll dx, ll dy, ll M) {
ll k, s;
if(cal(2*n, -2*m, -dx+dy, k, s) == -1)
return M + 1;
ll tx = 2 * k * n + dx;
if(tx < 0 || tx > M) return M + 1;
return tx;
}
ll minL(ll a, ll b) {
return a < b ? a : b;
}
ll solve(ll x, ll y) {
ll g = __gcd(n, m);
ll maxx = 1ll * m / g * n;
ll ans = maxx + 1;
ans = minL(ans, gao(-x, -y, maxx));
ans = minL(ans, gao(-x, y, maxx));
ans = minL(ans, gao(x, -y, maxx));
ans = minL(ans, gao(x, y, maxx));
if(ans == maxx + 1) return -1;
return ans;
}
int main() {
int k;
while(~scanf("%I64d%I64d%I64d", &n, &m, &k)) {
for(int i = 0;i < k;i++) {
ll x, y;
scanf("%I64d%I64d", &x, &y);
printf("%I64d\n", solve(x, y));
}
}
return 0;
}