CodeForces - 724C Ray Tracing 扩展欧几里得+思维

点击打开链接


题意:


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;  
}  


你可能感兴趣的:(codeforces,数学定理,思维)