CodeForces

来源:互联网 发布:手机模拟笛子软件 编辑:程序博客网 时间:2024/06/18 08:23

点击打开链接


题意:


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 <bits/stdc++.h> 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;  }  


原创粉丝点击