欢迎光临
我们一直在努力

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

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

在这里插入图片描述

课程目标

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

  • 第三部分:案例实战(分数模运算)

    研究案例:P2613 有理数取余
    题目描述

    给出一个有理数

    c

    =

    a

    b

    c=\\frac{a}{b}

    c=ba,求

    c

    m

    o

    d

    19260817

    c \\bmod 19260817

    cmod19260817 的值。

    这个值被定义为

    b

    x

    a

    (

    m

    o

    d

    19260817

    )

    bx\\equiv a\\pmod{19260817}

    bxa(mod19260817) 的解。

    输入格式

    一共两行。

    第一行,一个整数

    a

    a

    a。 第二行,一个整数

    b

    b

    b

    输出格式

    一个整数,代表求余后的结果。如果无解,输出 Angry!。

    输入输出样例 1
    输入 1

    233
    666

    输出 1

    18595654

    说明/提示

    对于所有数据,保证

    0

    a

    10

    10001

    0\\leq a \\leq 10^{10001}

    0a1010001

    1

    b

    10

    10001

    1 \\leq b \\leq 10^{10001}

    1b1010001,且

    a

    ,

    b

    a, b

    a,b 不同时是

    19260817

    19260817

    19260817 的倍数。

    思路分析

    一、题目重述与理解

    题目要求计算有理数

    c

    =

    a

    b

    c = \\frac{a}{b}

    c=ba 对模数 19260817 取余的值。这里的“有理数取余”并不是直接对分数进行除法,而是定义为同余方程

    b

    x

    a

    (

    m

    o

    d

    19260817

    )

    b x \\equiv a \\pmod{19260817}

    bxa(mod19260817) 的解 x 。也就是说,我们需要找到一个整数 x ,使得 b x 和 a 在模 19260817 的意义下相等。

    输入给出两个整数 a 和 b ,它们都是长度可达 10001 位的十进制大整数,因此不能直接用常规的整型存储。输出为计算得到的 x 值(范围在 0 到 19260816 之间),如果无解则输出 Angry!。

    题目的数据范围保证了 a 和 b 不会同时是模数 19260817 的倍数,这为无解判定提供了依据。

    二、数学原理
    1. 同余方程与模逆元

    方程

    b

    x

    a

    (

    m

    o

    d

    M

    )

    b x \\equiv a \\pmod{M}

    bxa(modM)(其中 M = 19260817 )是一个线性同余方程。当 b 与模数 M 互质时,方程有唯一解,解为

    x

    a

    b

    1

    (

    m

    o

    d

    M

    )

    ,

    x \\equiv a \\cdot b^{-1} \\pmod{M},

    xab1(modM), 其中

    b

    1

    b^{-1}

    b1 是 b 模 M 的乘法逆元,即满足

    b

    b

    1

    1

    (

    m

    o

    d

    M

    )

    b \\cdot b^{-1} \\equiv 1 \\pmod{M}

    bb11(modM) 的整数。

    当 b 与 M 不互质时,方程可能无解或有多解。具体地,若

    gcd

    (

    b

    ,

    M

    )

    a

    \\gcd(b, M) \\nmid a

    gcd(b,M)a,则无解;若

    gcd

    (

    b

    ,

    M

    )

    a

    \\gcd(b, M) \\mid a

    gcd(b,M)a,则方程有

    gcd

    (

    b

    ,

    M

    )

    \\gcd(b, M)

    gcd(b,M) 个解。但由于本题的特殊性,我们实际上只需判断

    b

    m

    o

    d

    M

    =

    0

    b \\bmod M = 0

    bmodM=0 的情况,因为题目保证 a 和 b 不同时是 M 的倍数,所以当

    b

    0

    (

    m

    o

    d

    M

    )

    b \\equiv 0 \\pmod{M}

    b0(modM) 时必有

    a

    ≢

    0

    (

    m

    o

    d

    M

    )

    a \\not\\equiv 0 \\pmod{M}

    a0(modM),此时

    g

    c

    d

    (

    b

    ,

    M

    )

    =

    M

    gcd(b, M) = M

    gcd(b,M)=M 显然不整除 a (除非 a 也是 M 的倍数,但被排除),因此无解。

    2. 模数的性质

    模数 M = 19260817 是一个质数(可通过简单检验或查证得知)。因此对于任意

    b

    ≢

    0

    (

    m

    o

    d

    M

    )

    b \\not\\equiv 0 \\pmod{M}

    b0(modM),都有

    gcd

    (

    b

    ,

    M

    )

    =

    1

    \\gcd(b, M) = 1

    gcd(b,M)=1,即 b 在模 M 下存在唯一的逆元。这使得我们可以利用费马小定理来求逆元。

    3. 费马小定理求逆元

    费马小定理指出:若 p 是质数,且 b 不是 p 的倍数,则

    b

    p

    1

    1

    (

    m

    o

    d

    p

    )

    .

    b^{p-1} \\equiv 1 \\pmod{p}.

    bp11(modp). 两边同时除以 ( b )(即乘以 ( b ) 的逆元),得到

    b

    p

    2

    b

    1

    (

    m

    o

    d

    p

    )

    .

    b^{p-2} \\equiv b^{-1} \\pmod{p}.

    bp2b1(modp). 因此,我们可以通过快速幂计算

    b

    p

    2

    m

    o

    d

    p

    b^{p-2} \\bmod p

    bp2modp 来得到 b 的逆元,时间复杂度为

    O

    (

    log

    p

    )

    O(\\log p)

    O(logp)

    三、大整数取模技巧

    由于 a 和 b 可能长达 10001 位,无法直接读入整型变量。常用的处理方法是:用字符串读入,然后逐位计算其对 M 的模。设字符串 s 表示的数为

    d

    k

    d

    k

    1

    d

    0

    d_k d_{k-1} \\dots d_0

    dkdk1d0(十进制),则

    val

    =

    (

    (

    (

    d

    k

    ×

    10

    +

    d

    k

    1

    )

    ×

    10

    +


    )

    ×

    10

    +

    d

    0

    )

    m

    o

    d

    M

    .

    \\text{val} = ( \\cdots ((d_k \\times 10 + d_{k-1}) \\times 10 + \\cdots ) \\times 10 + d_0 ) \\bmod M.

    val=(((dk×10+dk1)×10+)×10+d0)modM. 在每一步中,我们都可以取模,防止中间结果溢出。具体实现:

    int str_mod(const string &s) {
    long long res = 0;
    for (char c : s) {
    res = (res * 10 + (c '0')) % MOD;
    }
    return res;
    }

    这种方法可以安全地将任意长的十进制数转化为模 M 下的值。

    四、无解情况的判定

    根据题目给出的条件:“( a, b ) 不同时是 ( 19260817 ) 的倍数”。因此:

    • 如果

      b

      m

      o

      d

      M

      =

      0

      b \\bmod M = 0

      bmodM=0,那么 b 是 M 的倍数。由条件可知此时 a 一定不是 M 的倍数,即

      a

      m

      o

      d

      M

      0

      a \\bmod M \\neq 0

      amodM=0

    • 代入方程:

      0

      x

      a

      (

      m

      o

      d

      M

      )

      0 \\cdot x \\equiv a \\pmod{M}

      0xa(modM),左边为 0 ,右边为非零,矛盾。因此方程无解,应输出 Angry!。

    反之,若

    b

    m

    o

    d

    M

    0

    b \\bmod M \\neq 0

    bmodM=0,则 b 与 M 互质(因为 M 是质数),方程有唯一解。

    五、算法步骤与复杂度
    算法步骤
  • 用字符串读入 a 和 b 。
  • 分别计算

    a

    =

    a

    m

    o

    d

    M

    a' = a \\bmod M

    a=amodM和 b’ =

    b

    m

    o

    d

    M

    b \\bmod M

    bmodM

  • 若 b’ = 0 ,输出 Angry!,算法结束。
  • 否则,利用快速幂计算 b’ 的逆元

    i

    n

    v

    =

    b

    M

    2

    m

    o

    d

    M

    inv = b'^{M-2} \\bmod M

    inv=bM2modM

  • 计算答案

    a

    n

    s

    =

    a

    ×

    i

    n

    v

    m

    o

    d

    M

    ans = a' \\times inv \\bmod M

    ans=a×invmodM

  • 输出 ans 。
  • 复杂度分析
    • 读入并取模:遍历两个字符串,每个字符处理一次,时间复杂度

      O

      (

      len

      (

      a

      )

      +

      len

      (

      b

      )

      )

      O(\\text{len}(a) + \\text{len}(b))

      O(len(a)+len(b)),空间复杂度

      O

      (

      len

      (

      a

      )

      +

      len

      (

      b

      )

      )

      O(\\text{len}(a) + \\text{len}(b))

      O(len(a)+len(b))

    • 快速幂:计算

      b

      M

      2

      m

      o

      d

      M

      b'^{M-2} \\bmod M

      bM2modM, M 约为

      1.93

      ×

      10

      7

      1.93 \\times 10^7

      1.93×107,二进制位数约 24 位,循环次数约

      log

      2

      M

      25

      \\log_2 M \\approx 25

      log2M25,常数极小。

    • 总时间复杂度:

      O

      (

      len

      (

      a

      )

      +

      len

      (

      b

      )

      +

      log

      M

      )

      O(\\text{len}(a) + \\text{len}(b) + \\log M)

      O(len(a)+len(b)+logM),完全满足题目要求(字符串长度 ≤ 10001)。

    代码实现

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

    const int MOD = 19260817;

    // 快速幂函数:计算 a^b % MOD
    int qpow(long long a, int b) {
    long long res = 1;
    while (b) {
    if (b & 1) res = res * a % MOD; // 若当前二进制位为1,乘上a
    a = a * a % MOD; // a自乘,准备下一位
    b >>= 1; // 右移一位
    }
    return res;
    }

    // 将大整数字符串转换为模 MOD 下的值
    int str_mod(const string &s) {
    long long r = 0;
    for (char c : s) {
    r = (r * 10 + (c '0')) % MOD; // 逐位取模,防止溢出
    }
    return r;
    }

    int main() {
    string sa, sb; // 用字符串读入大整数 a 和 b
    cin >> sa >> sb;

    int a = str_mod(sa); // a 在模 MOD 下的值
    int b = str_mod(sb); // b 在模 MOD 下的值

    if (b == 0) { // 若 b 是 MOD 的倍数,则方程无解
    cout << "Angry!" << endl;
    } else {
    int inv_b = qpow(b, MOD 2); // 用费马小定理求逆元
    int ans = (long long)a * inv_b % MOD; // 计算最终结果
    cout << ans << endl;
    }
    return 0;
    }

    功能分析

    • 大整数取模:利用 str_mod 函数,在读入字符串的过程中不断进行 (r*10 + digit) % MOD 运算,将任意长度的大整数安全地映射到 [0, MOD-1] 范围内。
    • 无解判断:若 b % MOD == 0,则原方程左侧为 0,右侧 a % MOD 必不为 0(题目保证不同时为 MOD 的倍数),故直接输出 "Angry!"。
    • 逆元求解:利用费马小定理和快速幂,在 O(log MOD) 时间内求出 b 的逆元。
    • 结果计算:将 a 与逆元相乘后再取模,得到最终的答案。

    更多系列知识,请查看专栏:《信奥赛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之数论基础专题课:从同余到分数模运算7(案例实践:分数模运算)
    分享到: 更多 (0)

    评论 抢沙发

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