68.Acwing基础课第204题-简单-表达整数的奇怪方式

68.Acwing基础课第204题-简单-表达整数的奇怪方式

题目描述

\(给定 2n 个整数 a_1,a_2,…,a_n 和 m_1,m_2,…,m_n,求一个最小的非负整数 x,满足 ∀i∈[1,n],x≡m_i(mod a_i)\)

输入格式

\(第一行包含整数 n\)

\(第 2…n+1 行:第 i+1 行包含两个整数 a_i 和 m_i,数之间用空格隔开\)

输出格式

输出最小非负整数 x,如果 x 不存在,则输出 −1

数据范围

\(1≤ai≤2^{31}−1\),
\(0≤m_i<a_i\)
\(1≤n≤25\)
\(所有 m_i 的最小公倍数在 64 位有符号整数范围内\)

输入样例:

2
8 7
11 9

输出样例:

31

代码:

// 包含输入输出流头文件(cin/cout用于输入输出)
#include <iostream>
#include <algorithm>

using namespace std;

// 定义长整型别名:防止大数运算溢出(方程组模数/余数可能很大)
typedef long long LL;

// 扩展欧几里得算法核心函数(适配LL类型)
// 功能:求解 ax + by = gcd(a, b) 的一组整数解 (x, y),并返回gcd(a, b)
// 参数:a,b-方程系数;x,y-引用传递,存储求得的一组解
// 返回值:d = gcd(a, b)(a和b的最大公约数)
LL exgcd(LL a, LL b, LL &x, LL &y)
{
    // 递归边界:b=0时,方程为 a*1 + 0*0 = a → 特解x=1, y=0
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }

    // 递归调用:欧几里得核心(gcd(a,b)=gcd(b,a%b)),交换x/y简化后续推导
    LL d = exgcd(b, a % b, y, x);
    // 回溯推导当前层解:y = y' - (a/b)*x(y'是递归返回的解)
    y -= a / b * x;
    return d;
}

int main()
{
    int n; // n:同余方程组的个数(x ≡ a_i (mod m_i),共n个方程)
    cin >> n;

    LL x = 0;    // 最终满足所有方程的解
    LL m1, a1;   // 第一个方程:x ≡ a1 (mod m1)
    cin >> m1 >> a1;

    // 迭代合并后续n-1个方程,逐步求解满足所有方程的解
    for (int i = 0; i < n - 1; i ++ )
    {
        LL m2, a2; // 当前要合并的方程:x ≡ a2 (mod m2)
        cin >> m2 >> a2;

        LL k1, k2; // 存储扩展欧几里得的解
        // 调用exgcd求解:m1*k1 + m2*k2 = gcd(m1, m2)
        LL d = exgcd(m1, m2, k1, k2);

        // 合并方程的核心判断:
        // 现有解满足 x = a1 + k*m1,要满足 x = a2 + t*m2 → a1 + k*m1 = a2 + t*m2
        // 整理得:m1*k - m2*t = a2 - a1 → 等价于 m1*k1 + m2*k2 = a2 - a1
        // 该方程有解的充要条件:gcd(m1,m2) 能整除 (a2 - a1)
        if ((a2 - a1) % d)
        {
            x = -1; // 无解,标记x=-1并退出循环
            break;
        }

        // ========== 求解合并后的方程特解 ==========
        // 1. 扩展欧几里得求得的k1是 m1*k1 + m2*k2 = d 的解,需放大 (a2-a1)/d 倍
        k1 *= (a2 - a1) / d;
        // 2. 调整k1到最小正整数解:k1的通解为 k1 + k*(m2/d),取模保证解最小且正
        LL t = m2 / d;
        k1 = (k1 % t + t) % t;

        // 3. 计算合并后方程的一个特解:x = k1*m1 + a1(满足前两个方程)
        x = k1 * m1 + a1;

        // ========== 更新合并后的方程参数 ==========
        // 合并后的方程为:x ≡ a1_new (mod m_new)
        // m_new = lcm(m1, m2) = |m1*m2| / d(最小公倍数)
        LL m = abs(m1 / d * m2);
        a1 = k1 * m1 + a1; // 新的余数a1(合并后方程的a)
        m1 = m;            // 新的模数m1(合并后方程的m)
    }

    // 若有解,将最终解调整到模m1的最小非负整数解
    if (x != -1) 
        x = (a1 % m1 + m1) % m1;

    // 输出结果:-1表示无解,否则为满足所有方程的最小非负解
    cout << x << endl;

    return 0;
}
posted @ 2026-04-09 16:52  CodeMagicianT  阅读(13)  评论(0)    收藏  举报