『笔记』数学数论(五)

\[\Huge{(五)裴蜀定理与中国剩余定理} \]

裴蜀定理

内容

裴蜀定理,又称贝祖定理(Bézout's lemma)

\(a,b\) 满足 \(\gcd(a,b) \mid c\),则存在 \(x,y\) 使得 \(ax+by=c\) ,其中 \(a \in Z^*,b \in Z^*\)

证明

显然有 \(\gcd(a,b) \mid a,\gcd(a,b) \mid b\)

因为 \(x,y \in Z^*\)

所以 \(\gcd(a,b) \mid ax,\gcd(a,b) \mid by\)

要使得 \(ax + by = c\) ,则 \(c\) 一定是 \(\gcd(a,b)\) 的倍数,

又有 \(a \in Z^*,b \in z^*\)

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

证毕。

应用

P4549 【模板】裴蜀定理

可由裴蜀定理计算两个数推广得来,对于本题的答案,我们只需不断求其中两个数的最大公约数,直到计算出所有数的最大公约数即为答案。

/*

Name: P4549 【模板】裴蜀定理

Solution: 裴蜀定理推广
   

By Frather_

*/
#include <iostream>
#include <cstdio>

using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
int n;
int x;
int ans;
/*=============================================自定义函数*/
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
/*=================================================主函数*/
signed main()
{
    n = read();
    for (int i = 1; i <= n; i++)
    {
        int tmp = read();
        x = tmp >= 0 ? tmp : -tmp;
        ans = gcd(ans, x);
    }
    printf("%d\n", ans);
    return 0;
}

中国剩余定理

『物不知数』问题


《孙子算法》

今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?
答曰:“二十三”。
术曰:三三数之剩二,置一百四十;五五数之剩三,置六十三,七七数之剩二,置三十,并之。得二百三十三,以二百一十减之,即得。凡三三数之剩一,则置七十;五五数之剩一,则置二十一;七七数之剩一,则置十五;一百六以上以一百五减之即得。

翻译:

一个整数除以3余2、除以5余3、除以7余2,求这个整数。
答案:23
解法:由于除以3余2,因此加上一个140;由于除以5余3,因此加上一个63;由于除以7余2,因此加上一个30;这三个数的和是140+63+30=233,再减去210,就得到了23了。

也就是说,

  • 只要是除以 \(3\) 余了一个 \(1\) ,就加上一个 \(70\)

  • 只要是除以 \(5\) 余了一个 \(1\) ,就加上一个 \(21\)

  • 只要是除以 \(7\) 余了一个 \(1\) ,就加上一个 \(15\)

然后累加。超过了 \(106\) 就减去 \(105\) 就行了。

思想

求解如下同余方程组

\[\left\{ \begin{aligned} x & \equiv a_{1}\left(\bmod n_{1}\right) \\ x & \equiv a_{2}\left(\bmod n_{2}\right) \\ & \vdots \\ x & \equiv a_{k}\left(\bmod n_{k}\right) \end{aligned} \right. \]

其中 \(n_1,n_2,\cdots,n_k\) 两两互质。

  1. 计算所有模数的积 \(n\)

  2. 对于第 \(i\) 个方程:

    a. 计算 \(m_{i}=\cfrac{n}{n_{j}}\);

    b. 计算 \(m_{i}\) 在模 \(n_{i}\) 意义下的 逆元 \(m_{i}^{-1}\);

    c. 计算 \(c_{i}=m_{i} m_{i}^{-1}\)

  3. 方程組的唯一解为: \(a=\sum_{i=1}^{k} a_{i} c_{i}\pmod n\)

注意:执行 c.不要对 \(n_{i}\) 取模

伪代码

n ← 1;
ans ← 0;
for i = 1 to k
    n ← n * n[i];

for i = 1 to k
{
    m ← n / n[i] b ← inv(m, n[i]); // b * m mod n[i] = 1
    ans ← (ans + a[i] * m * b) mod n;
}
return ans;

例题

『物不知数』问题

  1. \(n=3\times 5\times 7=105\)

  2. 三人同行 七十 希:

    \[n_1=3,\\ m_1=n/n_1=35,\\ m_1^{-1}\equiv 2\pmod 3 \]

    \[c_1=35\times 2=70 \]

  3. 五树梅花 廿一 支:

    \[\begin{aligned} n_2 &=5,\\ m_2 &=n/n_2 \\ &=21,\\ m_2^{-1} &\equiv 1\pmod 5 \end{aligned} \]

    \[\begin{aligned} c_2 &=21\times 1 \\ &=21 \end{aligned} \]

  4. 七子团圆正 半月:\

    \[\begin{aligned} n_3 &=7,\\ m_3 &=n/n_3\\ &=15,\\ m_3^{-1} &\equiv 1\pmod 7 \end{aligned} \]

    \[\begin{aligned} c_3 &=15\times 1 \\ &=15 \end{aligned} \]

  5. 所以方程组的唯一解为

    百零五

    \[\begin{aligned} a &\equiv 2\times 70+3\times 21+2\times 15\\ &\equiv 233\\ &\equiv 23 \pmod {105} \end{aligned} \]

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

/*

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

Solution: 中国剩余定理
   

By Frather_

*/
#include <iostream>
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
int n;

const int _ = 1000010;

int a[_], m[_];
int p = 1;
int m_[_];
int x, y;

int ans;
/*=============================================自定义函数*/
void ex_gcd(int a, int b, int &x, int &y)
{
    if (!b)
    {
        x = 1;
        y = 0;
        return;
    }
    ex_gcd(b, a % b, x, y);
    int t = x;
    x = y;
    y = t - y * (a / b);
}
/*=================================================主函数*/
signed main()
{
    n = read();
    for (int i = 1; i <= n; i++)
    {
        m[i] = read();
        p *= m[i];
        a[i] = read();
    }
    for (int i = 1; i <= n; i++)
    {
        m_[i] = p / m[i];
        x = 0;
        y = 0;
        ex_gcd(m_[i], m[i], x, y);
        ans += a[i] * m_[i] * (x >= 0 ? x : x + m[i]);
    }
    printf("%lld\n", ans % p);
    return 0;
}

P2480 [SDOI2010]古代猪文


暂时稍微放一下吧。。
posted @ 2021-04-18 21:26  Frather  阅读(175)  评论(0编辑  收藏  举报