90pts求调
查看原帖
90pts求调
1376172
w150432楼主2025/6/15 21:25
#include <bits/stdc++.h>

#define int long long

using namespace std;

const int INF = 1e18 + 10;

int t;
int a, b, c, d;

int solve(int a, int b, int c, int d)
{
    if (c < 0 || d < 0) return -1;
	if (a == c && b == d) return 0;
	
	int res = 0;
	while (c != 0 && d != 0)
	{
		if (a == c && b == d) return res;
		
		//确保比c比d大
		if (c < d)
		{
		    swap(c, d);
		    swap(a, b); 
		}
		
		if (b == d && c >= a && c % d == a % d) return res + (c - a) / d;
		if (c % d == 0 && ((!a && b == d) || (a == d && !b))) return res + c / d;
		
		res += c / d;
		c %= d;
	}
	
	if (a == c && b == d) return res;
	
	return -1;
}

int solve2(int a, int b, int c, int d)
{
    int tmp = solve(a, b, c, d);
    return tmp == -1 ? INF : tmp;
}

signed main()
{
    cin >> t;
    
    while (t -- )
    {
        cin >> a >> b >> c >> d;
        
        if (a == c && b == d)
        {
            puts("0"); 
            continue;
        }
            //一开始就相等的情况,不需要操作
        
        //当a,b两个数都小等于0,我们可以转换成a,b大于0的情况
	    if (a <= 0 && b <= 0)
	    {
	        a = -a;
	        b = -b;
	        c = -c;
	        d = -d;
	    }
	    
	    //当a,b中已有一个小于零,且这个数字是a,我们让a为正的,而b则为负的方便后面的处理
	    if (a < 0)
	    {
	        swap(a, b);
	        swap(c, d); 
	    }
	    
	    if (a >= 0 && b >= 0) //我们的正常情况
	    { 
		    if (c < 0 || d < 0) 
		    {
		        puts("-1");
		        continue;
		    }
		    cout << solve(a, b, c, d) << "\n";	
		    continue;
	    }
	    
	    //当我们的a和b是异号的时候,如果c,d也异号的时候
	    
	    //如果a,c和b,d同样异号无解
	    if (c < 0 && d > 0) 
	    {
	        puts("-1");
	        continue;
	    }
	   
	    //当a,c同号,b,d同号时候只能是让绝对值大的数加上绝对值小的数因为不能够变符号
        if (c >= 0 && d <= 0)
        {
            cout << solve(c, -d, a, -b) << "\n" ; 
            continue;
        }
        
        //当我们的a和b是异号的时候,如果c,d也同号的时候
        
        //确保c和d都大等于零
        if (c < 0 && d < 0) 
        {
            a = -a;
	        b = -b;
	        c = -c;
	        d = -d;
        }
        
        //确保a,c,d都是大于0的
        if (a < 0)
        {
            swap(a, b);
	        swap(c, d);
        }
        
        vector<array<int, 3>> q;
        int num = 0; //操作次数
        
        //预处理点c,d倒推时到达每一个点以及他的操作次数,这里用的相当于欧几里的算法
        for (int x = c, y = d ; x > 0 && y > 0 ; )
        {
            if (y >= x)
            {
                num += y / x;
                y %= x;
            }
            else
            {
                q.push_back({y, x, num});
                num += x / y;
                x %= y;
            }
        }
        
        
        int ans = INF;
        num = 0;
        
        while (a > 0 && b < 0)
        {
            //向上走到x坐标轴上
            if (a + b == 0)
            {
                ans = min(ans, num + 1 + solve2(a, 0, c, d));
                break;
            }
            
            //向上走,不用考虑穿过x坐标轴 
            if (a + b < 0)
            {
                num += (-b) / a;
                b = - ((-b) % a);
                continue;
            }
            
            //向左走枚举(c, d)倒推途中的点
            for (auto item : q)
            {
                int y = item[0], nx = item[1];
                if (y <= a + b && (a - y) % (-b) == 0) //(a, a + b)为最靠右的落点 
                {
                    int k = (a + b - y) / (-b); //本组第几个点 
                    int x = a + k * b; //落点的横坐标
                    if (x <= nx && (nx - x) % y == 0) 
                        ans = min(ans, num + item[2] + 1 + k + (nx - x) / y); //落点能在(c, d)路径上 
                
                }
            }
            
            //向上拐到x轴上,相当于先走到(-b, a) 再到(-b, 0) 走的次数刚好是 a / -b次
            if (a % (-b) == 0) 
                ans = min(ans, num + a / -b + solve2(-b, 0, c, d));
                
            num += a / (-b);
            a %= (-b);
        }
        
        if (a >= 0 && b >= 0)
            ans = min(ans, num + solve2(a, b, c, d));
        
        if (ans >= INF) puts("-1");
        else cout << ans << "\n";
    }
    
    return 0;
}
2025/6/15 21:25
加载中...