欢迎光临
我们一直在努力

信奥赛C++提高组csp-s之数论基础专题课:从同余到分数模运算5(案例实践:青蛙的约会)

信奥赛C++提高组csp-s之数论基础专题课:从同余到分数模运算5(案例实践:青蛙的约会)

在这里插入图片描述

课程目标

  • 理清脉络:理解同余、裴蜀定理、扩展欧几里得、乘法逆元、分数模运算之间的逻辑关系。
  • 掌握核心:熟练运用扩展欧几里得算法求解不定方程及逆元。
  • 实战应用:能够解决相关的数论模板题和简单变式题。

  • 第三部分:案例实战(青蛙的约会)

    研究案例:P1516 青蛙的约会
    题目描述

    两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。

    我们把这两只青蛙分别叫做青蛙 A 和青蛙 B,并且规定纬度线上东经

    0

    0

    0 度处为原点,由东往西为正方向,单位长度

    1

    1

    1 米,这样我们就得到了一条首尾相接的数轴。设青蛙 A 的出发点坐标是

    x

    x

    x,青蛙 B 的出发点坐标是

    y

    y

    y。青蛙 A 一次能跳

    m

    m

    m 米,青蛙 B 一次能跳

    n

    n

    n 米,两只青蛙跳一次所花费的时间相同。纬度线总长

    L

    L

    L 米。现在要你求出它们跳了几次以后才会碰面。

    输入格式

    输入只包括一行五个整数

    x

    ,

    y

    ,

    m

    ,

    n

    ,

    L

    x,y,m,n,L

    x,y,m,n,L

    输出格式

    输出碰面所需要的次数,如果永远不可能碰面则输出一行一个字符串 Impossible。

    输入输出样例 1
    输入 1

    1 2 3 4 5

    输出 1

    4

    说明/提示

    对于

    100

    %

    100\\%

    100% 的数据,

    1

    x

    ,

    y

    ,

    m

    ,

    n

    2

    ×

    10

    9

    1 \\le x, y, m, n \\le 2 \\times 10^9

    1x,y,m,n2×109

    x

    y

    x \\ne y

    x=y

    1

    L

    2.1

    ×

    10

    9

    1 \\le L \\le 2.1 \\times 10^9

    1L2.1×109

    思路分析

    本题是经典的线性同余方程问题。两只青蛙在长度为 L 的环上跳,起始坐标分别为 x 和 y,步长分别为 m 和 n,同时同向朝西跳。问跳多少次后相遇。

    • 设跳了 t 次后相遇,此时青蛙 A 的位置为

      (

      x

      +

      m

      t

      )

      m

      o

      d

      L

      (x + m t) \\bmod L

      (x+mt)modL,青蛙 B 的位置为

      (

      y

      +

      n

      t

      )

      m

      o

      d

      L

      (y + n t) \\bmod L

      (y+nt)modL

    • 相遇条件:

      (

      x

      +

      m

      t

      )

      (

      y

      +

      n

      t

      )

      (

      m

      o

      d

      L

      )

      (x + m t) \\equiv (y + n t) \\pmod{L}

      (x+mt)(y+nt)(modL)

    • 移项得:

      (

      m

      n

      )

      t

      (

      y

      x

      )

      (

      m

      o

      d

      L

      )

      (m – n) t \\equiv (y – x) \\pmod{L}

      (mn)t(yx)(modL)

    令 (a = m – n),(c = y – x),则方程化为:

    a

    t

    c

    (

    m

    o

    d

    L

    )

    a t \\equiv c \\pmod{L}

    atc(modL) 这是一个标准的一元线性同余方程。

    关键步骤
  • 处理负数:在模运算中,通常将系数转化为非负剩余,避免扩展欧几里得算法中的符号混乱。因此先对 (a) 和 (c) 取模:

    a

    =

    (

    (

    m

    n

    )

    m

    o

    d

    L

    +

    L

    )

    m

    o

    d

    L

    ,

    c

    =

    (

    (

    y

    x

    )

    m

    o

    d

    L

    +

    L

    )

    m

    o

    d

    L

    a = ((m – n) \\bmod L + L) \\bmod L,\\quad c = ((y – x) \\bmod L + L) \\bmod L

    a=((mn)modL+L)modL,c=((yx)modL+L)modL 这样

    0

    a

    ,

    c

    <

    L

    0 \\le a, c < L

    0a,c<L

  • 特殊情况 (a = 0):

    • 若 (a = 0),则方程变为

      0

      t

      c

      (

      m

      o

      d

      L

      )

      0 \\cdot t \\equiv c \\pmod{L}

      0tc(modL)

    • 若 (c = 0),说明任何 t 都满足,但实际含义是两只青蛙起始就在同一位置(模 L 意义下),所以 t = 0。
    • 若 (

      c

      0

      c \\neq 0

      c=0),则无解,输出 Impossible。

  • 一般情况:利用扩展欧几里得算法求解。

    • g

      =

      gcd

      (

      a

      ,

      L

      )

      g = \\gcd(a, L)

      g=gcd(a,L),并找到一组整数解

      (

      t

      0

      ,

      k

      0

      )

      (t_0, k_0)

      (t0,k0) 满足

      a

      t

      0

      +

      L

      k

      0

      =

      g

      a t_0 + L k_0 = g

      at0+Lk0=g

    • 原方程有解当且仅当

      g

      c

      g \\mid c

      gc。若不整除,输出 Impossible。

  • 构造通解:

    • 方程两边同时除以 (g) 得到:

      a

      g

      t

      c

      g

      (

      m

      o

      d

      L

      g

      )

      \\frac{a}{g} t \\equiv \\frac{c}{g} \\pmod{\\frac{L}{g}}

      gatgc(modgL) 此时

      gcd

      (

      a

      /

      g

      ,

      L

      /

      g

      )

      =

      1

      \\gcd(a/g, L/g) = 1

      gcd(a/g,L/g)=1,因此 a/g 在模 L/g 下有逆元。

    • 一个特解为

      t

      =

      t

      0

      (

      c

      /

      g

      )

      t = t_0 \\cdot (c/g)

      t=t0(c/g)(因为

      a

      t

      0

      g

      (

      m

      o

      d

      L

      )

      a t_0 \\equiv g \\pmod{L}

      at0g(modL),乘以 c/g 得

      a

      (

      t

      0

      c

      /

      g

      )

      c

      (

      m

      o

      d

      L

      )

      a \\cdot (t_0 \\cdot c/g) \\equiv c \\pmod{L}

      a(t0c/g)c(modL)

    • 通解形式:

      t

      =

      t

      0

      (

      c

      /

      g

      )

      +

      k

      (

      L

      /

      g

      )

      t = t_0 \\cdot (c/g) + k \\cdot (L/g)

      t=t0(c/g)+k(L/g),其中

      k

      Z

      k \\in \\mathbb{Z}

      kZ

  • 求最小非负解:

    • m

      o

      d

      =

      L

      /

      g

      mod = L / g

      mod=L/g,则最小非负解为

      t

      =

      (

      t

      0

      (

      c

      /

      g

      )

      )

      m

      o

      d

      m

      o

      d

      t = (t_0 \\cdot (c/g)) \\bmod mod

      t=(t0(c/g))modmod,再调整为非负数。

    • 注意乘法

      t

      0

      (

      c

      /

      g

      )

      t_0 \\cdot (c/g)

      t0(c/g) 可能溢出,故采用取模运算避免:先分别对

      t

      0

      c

      /

      g

      t_0 和 c/g

      t0c/g 取模 mod,再相乘取模。即:

      t

      =

      (

      (

      t

      0

      m

      o

      d

      m

      o

      d

      )

      ×

      (

      (

      c

      /

      g

      )

      m

      o

      d

      m

      o

      d

      )

      )

      m

      o

      d

      m

      o

      d

      t = ( (t_0 \\bmod mod) \\times ((c/g) \\bmod mod) ) \\bmod mod

      t=((t0modmod)×((c/g)modmod))modmod

    • 最后若结果为负,加上 (mod) 使其非负(实际取模后已保证

      0

      t

      <

      m

      o

      d

      0 \\le t < mod

      0t<mod)。

  • 输出:得到的 t 即为所需跳的次数。


  • 代码实现

    #include <bits/stdc++.h>
    using namespace std;

    typedef long long ll; // 使用 long long 避免溢出

    // 扩展欧几里得:求 a*x + b*y = gcd(a,b) 的一组整数解 (x,y)
    // 返回 gcd(a,b)(保证非负)
    ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
    x = 1; y = 0;
    return a > 0 ? a : a; // 确保 gcd 为正数
    }
    ll g = exgcd(b, a % b, y, x); // 递归求解
    y -= a / b * x; // 回溯更新 y
    return g;
    }

    int main() {
    ll x, y, m, n, L;
    cin >> x >> y >> m >> n >> L;

    // 将系数化为模 L 下的非负剩余
    ll a = ((m n) % L + L) % L; // a = (m-n) mod L
    ll c = ((y x) % L + L) % L; // c = (y-x) mod L

    // 特殊情况:a == 0
    if (a == 0) {
    if (c == 0) cout << 0 << endl; // 已在同一点
    else cout << "Impossible" << endl;
    return 0;
    }

    ll t0, k0; // 用于存储 exgcd 得到的特解
    ll g = exgcd(a, L, t0, k0); // g = gcd(a, L)

    // 无解条件:c 不能被 g 整除
    if (c % g != 0) {
    cout << "Impossible" << endl;
    return 0;
    }

    ll mod = L / g; // 通解的周期
    // 计算 t = t0 * (c/g) 对 mod 取模的最小非负数
    // 为避免乘法溢出,分步取模
    ll t = ( (t0 % mod) * ((c / g) % mod) ) % mod;
    // 调整到 [0, mod-1] 区间(取模结果已在区间内,但确保非负)
    t = (t + mod) % mod;

    cout << t << endl;
    return 0;
    }


    功能分析

    本程序实现了“青蛙的约会”问题的求解,主要功能模块如下:

  • 输入处理:读取五个整数 (x, y, m, n, L),均为 long long 类型。
  • 方程标准化:将原方程

    (

    m

    n

    )

    t

    (

    y

    x

    )

    (

    m

    o

    d

    L

    )

    (m-n)t \\equiv (y-x) \\pmod{L}

    (mn)t(yx)(modL)的系数化为模 L 下的非负剩余,避免后续负数处理。

  • 特判分支:当

    m

    n

    (

    m

    o

    d

    L

    )

    m \\equiv n \\pmod{L}

    mn(modL) 时,若初始位置相同则答案为 0,否则无解。

  • 扩展欧几里得求解:通过递归函数 exgcd 计算

    gcd

    (

    a

    ,

    L

    )

    \\gcd(a, L)

    gcd(a,L) 及一组特解,时间复杂度

    O

    (

    log

    L

    )

    O(\\log L)

    O(logL)

  • 解的存在性判断:检查 c 能否被

    gcd

    (

    a

    ,

    L

    )

    \\gcd(a, L)

    gcd(a,L) 整除,若不能则输出 Impossible。

  • 最小非负解计算:
    • 利用公式

      t

      =

      t

      0

      (

      c

      /

      g

      )

      (

      m

      o

      d

      L

      /

      g

      )

      t = t_0 \\cdot (c/g) \\pmod{L/g}

      t=t0(c/g)(modL/g) 得到最小非负整数解。

    • 通过两次取模运算避免乘法溢出

      t

      0

      t_0

      t0

      c

      /

      g

      c/g

      c/g 均可能较大,但取模后相乘仍在 long long 范围内)。

  • 输出结果:打印跳的次数或 Impossible。
  • 复杂度分析

    • 时间复杂度:

      O

      (

      log

      L

      )

      O(\\log L)

      O(logL),单次递归调用。

    • 空间复杂度:

      O

      (

      1

      )

      O(1)

      O(1),仅使用常数个变量。

    更多系列知识,请查看专栏:《信奥赛C++提高组csp-s知识详解及案例实践》: https://blog.csdn.net/weixin_66461496/category_13113932.html


    各种学习资料,助力大家一站式学习和提升!!!

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    cout<<"########## 一站式掌握信奥赛知识! ##########";
    cout<<"############# 冲刺信奥赛拿奖! #############";
    cout<<"###### 课程购买后永久学习,不受限制! ######";
    return 0;
    }

    1、csp信奥赛高频考点知识详解及案例实践:

    CSP信奥赛C++动态规划: https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

    CSP信奥赛C++标准模板库STL: https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

    信奥赛C++提高组csp-s知识详解及案例实践: https://blog.csdn.net/weixin_66461496/category_13113932.html

    2、csp信奥赛冲刺一等奖有效刷题题解:

    CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

    信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新) https://blog.csdn.net/weixin_66461496/category_13125089.html

    3、GESP C++考级真题题解:

    在这里插入图片描述

    GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

    在这里插入图片描述

    GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

    在这里插入图片描述 GESP(C++ 七级+八级)真题题解(持续更新): https://blog.csdn.net/weixin_66461496/category_13117178.html

    4、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

    https://edu.csdn.net/lecturer/7901 点击跳转

    · 文末祝福 ·

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    cout<<"跟着王老师一起学习信奥赛C++";
    cout<<" 成就更好的自己! ";
    cout<<" csp信奥赛一等奖属于你! ";
    return 0;
    }

    赞(0)
    未经允许不得转载:171主机测评 » 信奥赛C++提高组csp-s之数论基础专题课:从同余到分数模运算5(案例实践:青蛙的约会)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址