令c = a+b, d = A-B,则等价于求{|x|+|y| | ax+by=d || ax+cy=d || bx+cy=d}。对于ax+by=d,用扩展欧几里得算法求得axx+byy=gcd(a,b),是否有解可由d是否为gcd的倍数判断。若有解,原方程的一组解为(x0, y0) = (xx*d/gcd, yy*d/gcd)。令aa=a/gcd,bb=b/gcd,则通解可表示为(x0+k*bb, y0-k*aa)。能使|x|+|y|最小的整点一定是最靠近直线与坐标轴交点的地方,可以由|x|+|y|=C的图像不断平移看出。由于负数取整时是先对它的绝对值取整,再添上负号,所以考虑的范围要是[-x0/bb-1, -x0/bb+1], [y0/aa-1, y0/aa+1]。
#include <cstdio> #define ll long long inline void Min(ll& x, ll y) { if(x < 0) x = y; if(y < 0) return; if(y < x) x = y; } inline ll Abs(ll x) { return x<0?-x:x; } inline ll Sum(ll x, ll y) { return Abs(x)+Abs(y); } ll exgcd(ll a,ll b,ll& x,ll& y) { ll t, ret; if (! b) { x=1, y=0; return a; } ret = exgcd(b, a%b, x, y); t = x, x = y, y = t - a/b*y; return ret; } ll a, b, c, d, A, B, T; ll x, y, gcd, k, min, sum; ll solve(ll a, ll b) { gcd = exgcd(a, b, x, y); if(d%gcd != 0) return -1; x = x*(d/gcd); y = y*(d/gcd); a /= gcd; b /= gcd; min = Sum(x, y); k = -x/b-1; sum = Abs(x+k*b)+Abs(y-k*a); Min(min, sum); k += 1; sum = Abs(x+k*b)+Abs(y-k*a); Min(min, sum); k += 1; sum = Abs(x+k*b)+Abs(y-k*a); Min(min, sum); k = y/a-1; sum = Abs(x+k*b)+Abs(y-k*a); Min(min, sum); k += 1; sum = Abs(x+k*b)+Abs(y-k*a); Min(min, sum); k += 1; sum = Abs(x+k*b)+Abs(y-k*a); Min(min, sum); return min; } int main() { scanf("%lld", &T); while(T --) { scanf("%lld%lld%lld%lld", &A, &B, &a, &b); if(A == B) { printf("0\n"); continue; } c = a + b; d = Abs(A - B); B = solve(a, b); Min(B, solve(a, c)); Min(B, solve(b, c)); printf("%lld\n", B); } return 0; }