• AcWing 222 青蛙的约会


    题目传送门

    一、不定方程推导

    \(a\):青蛙\(a\)的起点
    \(b\):青蛙\(b\)的起点

    \(m\):青蛙\(a\)一次能跳多远
    \(n\):青蛙\(b\)一次能跳多远

    \(L\):一圈的距离

    \((b-a)\):\(a\)要追\(b\)多少米(这个数可能是正的,也可能是负的。正的表示\(b\)\(a\)前面;如果是负的表示\(b\)\(a\)后面)
    \((m-n)\):每跳一次,\(a\)能追\(b\)多少米

    \(x\)是总共跳了多少次

    \(y\)\(a\)\(b\)不一定会在一圈内追完,而是追了\(y\)

    \[(m - n)*x = b - a + y*L \]

    \[\Rightarrow \]

    \[(m - n)*x - y*L = b - a \]

    \((m-n)\):常数, \(L\):常数, \(b-a\):常数,变量:\(x,y\)

    二、是否有整数解

    如果想要有整数解,那么需要满足裴蜀定理\(gcd(m-n,-L) | b-a\) ,否则无解

    三、最小正整数解求解过程

    1 、求\(ax+by=gcd(a,b)\)的一组特解

    利用扩展欧几里得,计算出 \(ax+by=gcd(a,b)\)的一组特解 \(x_0,y_0\)

    2、求\(ax+by=c\)的一组特解

    我们要求的方程通用形式是 \(ax+by=c\),并不是 \(ax+by=gcd(a,b)\),但满足 \(gcd(a,b) |c\)
    也就是 \(c\) 肯定是 \(gcd(a,b)\)的整数倍。可以直接在 \(ax_0+by_0=gcd(a,b)\)这个等式的两端,同时乘以 \(t=c/gcd(a,b)\)
    得到: \(ax_0 * t + by_0 * t =c\)
    换句话说, \(x_0*t\)\(y_0*t\)就是方程 \(ax+by=c\) 的一组特解
    此时 \(x_1= x_0 * c /gcd(a,b) \ \ \ \ y_1= y_0 * c /gcd(a,b)\)

    3、求\(ax+by=c\)的通解

    这个\(x_1,y_1\)只是方程\(ax+by=c\)的一组特解,不一定是最小正整数解,也可能是负的,也可能很大的正数,需要通过通解公式,
    计算出最小的正整数解:

    通解公式: $$x= x_1+ k(b/d)$$ $$y=y_1-k(a/d)$$,其中\((b/d)\)可以理解为 最小缩放单元
    为了防止出现负数,一般采用类似下面的取绝对值办法,整理为正整数单元。

     LL t = abs(b / d);
    

    在当前题目中就是: \(x = x1+ k(-L/d)\) 其中\(k\)是任意整数

    现在 \(x1,L/d\)都是已知的常数,\(k\)可以变化 ,求\(x\)的最小正整数解是多少。

    \(x\)其实本质是就是通过 \(x_1\)在扩大、缩小 \(L/d\) \(k\)倍得到,
    比如\(k=1\),扩大 \(x=x_1+ L/d\)
    比如\(k=2\) 扩大 \(x=x_1+ 2*(L/d)\)

    当然,也可以缩小
    比如\(k=-1\) 缩小 \(x=x_1-(L/d)\)
    比如\(k=-2\) 缩小 \(x=x_1-2*(L/d)\)

    不管怎么样,都最后要满足\(x\)是最小的正整数。

    我们通过循环变化, 就可以得到\(x\)的最小正整数解:

      //方法II:循环尝试法
      LL ans = x1 % t;
      while (ans <= 0) ans += t;
      cout << ans << endl;
    

    当然,也可以直接使用证明过的公式来计算:

      cout << (x % t + t) % t << endl; //求最少跳几次后碰面
    

    四、实现代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    LL exgcd(LL a, LL b, LL &x, LL &y) {
        if (!b) {
            x = 1, y = 0;
            return a;
        }
        LL d = exgcd(b, a % b, y, x);
        y -= a / b * x;
        return d;
    }
    
    int main() {
        LL a, b, m, n, L;
        cin >> a >> b >> m >> n >> L;
    
        LL x, y;
        LL d = exgcd(m - n, -L, x, y);
        if ((b - a) % d)
            puts("Impossible");
        else {
            x *= (b - a) / d;
            LL t = abs(L / d);
            //求最小正整数解
            //方法I:公式法
            cout << (x % t + t) % t << endl; //求最少跳几次后碰面
    
            //方法II:循环尝试法
            // LL ans = x1 % t;
            // while (ans <= 0) ans += t;
            // cout << ans << endl;
        }
        return 0;
    }
    

    五、实现代码II

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    LL exgcd(LL a, LL b, LL &x, LL &y) {
        if (!b) {
            x = 1, y = 0;
            return a;
        }
        LL d = exgcd(b, a % b, y, x);
        y -= a / b * x;
        return d;
    }
    
    int main() {
        LL a, b, m, n, L;
        cin >> a >> b >> m >> n >> L;
    
        //让快的追慢的,如果不是的话,掉过来~,这样做后,可以保证m-n>0,最大公约数是正数d>0
        if (m < n) swap(m, n), swap(a, b);
    
        LL x, y;
        LL d = exgcd(m - n, L, x, y);
        if ((b - a) % d)
            puts("Impossible");
        else {
            x *= (b - a) / d;
            LL t = L / d;
            //求最小正整数解
            //方法I:公式法
            cout << (x % t + t) % t << endl; //求最少跳几次后碰面
    
            //方法II:循环尝试法
            // LL ans = x1 % t;
            // while (ans <= 0) ans += t;
            // cout << ans << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    java基础笔记-类与对象(多态)
    oracle中trim,ltrim,rtrim函数用法
    git stash
    update from select
    oracle 查看主外键约束
    eclipse git 解决冲突
    根据Request获取客户端IP
    简单说说Spring Security 使用(附加验证码登录,自定义认证)
    linux的nohup命令的用法
    Python包管理工具介绍
  • 原文地址:https://www.cnblogs.com/littlehb/p/16308639.html
Copyright © 2020-2023  润新知