【Coel.学习笔记】拓展欧几里得与中国剩余定理

题前碎语

月考结束了,可惜状态一点都不好,要挨骂了\(qwq\)……
最后发现自己实在是不会高级的字符串算法,所以跑来做数论了!
\(PS\):数论是一个比较大的模块,所以今后会加上各种小标签。

笔记内容

包括拓展欧几里得和中国剩余定理。

拓展欧几里得

拓展欧几里得(\(Extended\) \(Euclidean\) \(Algorithm\),以下简称\(Exgcd\))是辗转相除法的拓展,能够在求得两数的最大公约数(\(Greatest\) \(Common\) \(Divisor\),以下简称\(gcd\))的同时求出二元不定方程\(ax+by=gcd(a,b)\)的一个整数解。在\(OI\)中,拓展欧几里得通常用来求解逆元,其适用范围大于只能以质数为模数的费马小定理。
关于能够使用\(Exgcd\)求解逆元的简单证明:


逆元的定义:对于\(ax\equiv 1(mod\ p)\)\(x\)\(a\)关于\(p\)的乘法逆元。
对式子进行变形可得\(ax+kp=1\),故可用\(Exgcd\)求出一个\(x\)值。


实现过程

与辗转相除法的思路相同,假设已经计算出了下一个状态:\(b\)\((a \% b)\)的最大公约数,并且求出了一组 \(x_1, y_1\)使得\(bx_1+(a \% b)y_1 = gcd(a,b)\)
接下来利用这个式子进行转移(证明过程不再赘述):

\[x=y_1, y=x_1-a/b\times y_1 \]

\(b=0\)时即为边界,\(x=1,y=0\),递归求解即可。

inline int Extend_Euclid(int a,int b,int &x,int &y)
{
    if(b==0)
    {
       x=1,y=0;
       return a;
    }
    int r=Extend_Euclid(b,a%b,x,y),temp=x;//x会被覆盖掉,所以加一个temp
    x=y,y=temp-a/b*y;
    return r;
}

中国剩余定理

中国剩余定理(\(Chinese\) \(Remainder\) \(Theorem\),以下简称\(CRT\))又名孙子定理,最早出现在南北朝时期的著作《孙子算经》(也就是鸡兔同笼的同出处)中。
\(PS\):这位孙子不是那位军事家,后者是春秋时期的人。
\(1801\)年,德国数学家高斯在《算术探究》中独立地写出了这条定理,后被指出与孙子的定理相同,故被称为中国剩余定理
限于篇幅,《孙子算经》的原文此处不再给出(其实就是我懒)。
\(CRT\)用于求解形似下式的同余方程组:

\[\begin{cases}x\equiv a_1(mod\ m_1)\\x\equiv a_2(mod\ m_2)\\...\\x\equiv a_n(mod\ m_n)\end{cases} \]

其中\(m\)两两互质。

实现过程

\(M=\prod_{i=1}^nm_i\)\(M_i=\frac{M}{m_i}\)\(t_i=M_i^{-1}\)(即\(t_i\)为乘法逆元),
则方程组的通解为

\[\sum\limits_{i=1}^na_it_iM_i+kM \]

如果要求最小非负整数解,则对\(M\)取模,并让答案保持为非负数(为负数则加回到\(M\)即可)。
代码如下洛谷传送门

#include<cstdio>
#include<cctype>
#define maxn 15
long long n,a[maxn],m[maxn];
inline long long read()//快读
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
inline long long Extend_Euclid(long long a,long long b,long long &x,long long &y)//拓展欧几里得求逆元
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    long long r=Extend_Euclid(b,a%b,x,y);
    long long temp=x;
    x=y;
    y=temp-a/b*y;
    return r;
}
inline long long Chinese_Remainder_Theorem()//算法主体
{
    long long M=1,ans=0;
    for(register int i=1;i<=n;i++)
        M*=m[i];
    for(register int i=1;i<=n;i++)
    {
        long long x,y,Mi=M/m[i];
        Extend_Euclid(Mi,m[i],x,y);
        ans=(ans+Mi*x*a[i])%M;
    }
    return ans<0?ans+M:ans;
}
int main()
{
    n=read();
    for(register int i=1;i<=n;i++)
       m[i]=read(),a[i]=read();//注意题意中对应关系,第一个数是模数,第二个是余数
    printf("%lld",Chinese_Remainder_Theorem());
    return 0;
}

题后闲话

打公式真的累死人……\(LaTeX\)是很好用,但是公式实在太多了好难记啊……
过几天学一下拓展\(CRT\),据说题解里有学校里一位前大佬\(niiick\)的题解,很期待\(qwq\)

posted @ 2021-12-11 17:48  秋泉こあい  阅读(135)  评论(0编辑  收藏  举报