初晕 2025-08-05 18:41 采纳率: 100%
浏览 7
已结题

P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

#include <bits/stdc++.h>
using namespace std; 
typedef long long l;
const int N=15;
int m[N],a[N];
l e(l a,l b,l &x,l &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    l d=e(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int main(){
    int n;
    cin>>n;
    l M=1;
    for(int i=1;i<=n;i++){
        cin>>m[i]>>a[i];
        M*=m[i];
    }
    l t=0;
    for(int i=1;i<=n;i++){
        l x,y;
        l Mi=M/m[i];
        e(Mi,m[i],x,y);
        t=(t+a[i]*Mi%M*x)%M;
    }
    t=(t+M)%M;
    cout<<t<<endl;
    return 0;
}

这段代码错一个样例,有没有人帮忙解决一下

  • 写回答

3条回答 默认 最新

  • 檀越@新空间 2025-08-05 18:42
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    这段代码是用于求解中国剩余定理(CRT)的模板,但确实存在一个错误,导致在某些情况下无法正确输出结果。下面我将详细分析问题,并给出解决方案


    🚨 问题分析

    1. 函数 e 的定义不正确

    你定义了一个函数 e,它应该是一个扩展欧几里得算法的实现,用于求两个数的最大公约数以及贝祖等式中的系数 xy,即:

    $$ \text{gcd}(a, b) = ax + by $$

    然而,在你的函数中,参数顺序是:
    e(l a, l b, l &x, l &y),而实际上应该是:

    l e(l a, l b, l &x, l &y)
    

    这在 C++ 中是允许的,但更常见的写法是 e(l a, l b, l &x, l &y),所以这部分没有语法错误。

    2. 关键错误:Mi * x % M 的计算方式不正确

    在 CRT 中,我们需要计算:

    $$ M_i \cdot x_i \mod m_i = 1 $$

    其中 $ x_i $ 是 $ M_i^{-1} \mod m_i $,也就是 $ M_i $ 在模 $ m_i $ 下的逆元

    但是在你的代码中,你用了:

    t = (t + a[i] * Mi % M * x) % M;
    

    这里的问题在于:Mi % M 是不必要的,因为 Mi = M / m[i],而 M 是所有模数的乘积,所以 Mim[i] 的倍数吗?不是!Mi 是其他模数的乘积,所以 Mi 可能比 M 小,因此 Mi % M 等于 Mi

    但是,更重要的是,你在计算 Mi * x 时,没有考虑模的意义正确的做法是计算 Mi * x % m[i],而不是 Mi * x % M


    ✅ 正确的中国剩余定理实现逻辑

    中国剩余定理的步骤如下:

    1. 计算所有模数的乘积 $ M = m_1 \times m_2 \times \cdots \times m_n $
    2. 对于每个 $ i $:
      • 计算 $ M_i = M / m_i $
      • 找到 $ x_i $,使得 $ M_i \cdot x_i \equiv 1 \mod m_i $ (即 $ x_i = M_i^{-1} \mod m_i $)
      • 累加 $ t = (t + a_i \cdot M_i \cdot x_i) \mod M $

    🔧 修改后的代码

    下面是修正后的版本,解决了上述问题:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long l;
    
    const int N = 15;
    
    int m[N], a[N];
    
    // 扩展欧几里得算法
    l e(l a, l b, l &x, l &y) {
        if (b == 0) {
            x = 1, y = 0;
            return a;
        }
        l d = e(b, a % b, y, x);
        y -= a / b * x;
        return d;
    }
    
    int main() {
        int n;
        cin >> n;
        l M = 1;
        for (int i = 1; i <= n; i++) {
            cin >> m[i] >> a[i];
            M *= m[i];
        }
    
        l t = 0;
        for (int i = 1; i <= n; i++) {
            l Mi = M / m[i];
            l x, y;
            l g = e(Mi, m[i], x, y); // 求 Mi 和 m[i] 的 gcd 及 x, y
            // 确保 Mi 和 m[i] 互质,否则无解
            if (g != 1) {
                cout << "无解" << endl;
                return 0;
            }
            // 计算 Mi 的逆元
            l inv_Mi = (x % m[i] + m[i]) % m[i]; // 确保为正数
            t = (t + a[i] * Mi * inv_Mi) % M;
        }
    
        t = (t + M) % M; // 避免负数
        cout << t << endl;
        return 0;
    }
    

    ⚠️ 注意事项

    • 必须保证所有模数 $ m_i $ 两两互质,否则 CRT 不适用。
    • 如果模数不互质,需要使用扩展中国剩余定理(EXCRT)
    • 在代码中加入判断 if (g != 1) 是为了处理非互质情况,避免错误。

    ✅ 总结

    | 问题 | 解决方案 | |------|----------| | Mi * x % M 错误 | 改为 Mi * x % m[i],即计算逆元 | | 没有处理非互质情况 | 添加 if (g != 1) 判断 | | 负数处理 | t = (t + M) % M |


    如果你提供具体的样例输入和输出,我可以进一步验证修改后的代码是否通过。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 8月13日
  • 已采纳回答 8月5日
  • 创建了问题 8月5日