中國剩餘定理和擴展中國剩餘定理——楊子曰數學

中國剩餘定理——楊子曰數學

超鏈接:數學合集


問:今有物不知其數,三三數之剩二,五五數之剩三,七七數之剩二。問物幾何?


換成人話(這纔不是人話好嗎):

解方程:
{xa1(mod m1)xa2(mod m2)xa3(mod m3)xan(mod mn) \left\{ \begin{aligned} x & \equiv a_1(mod\ m_1)\\ x & \equiv a_2(mod\ m_2)\\ x & \equiv a_3(mod\ m_3)\\ \vdots\\ x & \equiv a_n(mod\ m_n)\\ \end{aligned} \right. (m1,m2,m3mnIt very important)(m_1,m_2,m_3 \cdots \cdots m_n互質,It'\ very \ important)


中國剩餘定理

我們先來講腫麼做,再來講爲啥可以這樣做

How to do?

  1. 求出mim_i的lcm
    比如文章最上面的那道題,我們先求出m=lcm(3,5,7)=105m=lcm(3,5,7)=105
  2. Mi=mmiM_i=\frac{m}{m_i}
    於是,我們有了:M1=105/3=35M2=105/5=21,M3=105/7=15M_1=105/3=35,M_2=105/5=21,M_3=105/7=15
  3. 讓M_i擴大一定的倍數s,使得sMi mod mi=ais*M_i\ mod\ m_i=a_i
    在這個例子中:M1 mod m1=35 mod 3=2M_1\ mod\ m_1=35\ mod\ 3=2
    歐,發現不用擴大就已經滿足了M1 mod m1=a1M_1\ mod\ m_1=a_1
    繼續,M2 mod m2=21 mod 5=1M_2\ mod\ m_2=21\ mod\ 5=1
    餘數需要變成3,那我們就把被除數變成原來的3倍
    也就是63 mod 5=363\ mod\ 5=3
    然後,M3 mod m3=15 mod 7=1M_3\ mod\ m_3=15\ mod\ 7=1
    餘數得是2,那我們就要把M3M_3變爲2倍
    也就是:30 mod 7=230\ mod\ 7=2
  4. 把擴大後的mim_i全部加起來,我們就得到了答案:
    ans=M1+M2+M3=35+63+30=128ans=M_1+M_2+M_3=35+63+30=128
  5. 如果你喜歡的話把ans mod m(lcm)ans\ mod\ m(它們的lcm),使答案落在0~m之間
    ans=ans mod m=128 mod 105=23ans=ans\ mod\ m=128\ mod\ 105=23
    OK,完事

Why這樣做我們可以找到答案?

其實MiM_i就是除了mim_i以外的其他所有mim_i的lcm(←別忘了它們是兩兩互質的)
也就是說M_i擴大了任意被模其他mim_i都是不受影響的

那我們就可以隨便擴大,讓Mi mod mi=aiM_i\ mod \ m_i=a_i不就好了嗎?
至於如何讓MiM_i變成我們想要的值,其實我們先要算的是MiM_i在模mim_i下的逆元(逆元,什麼?),也就是我們想讓kMi mod mi=1k*M_i\ mod\ m_i=1,這是再把kMik*M_i擴大相應的倍數,就行了

OK,完事

int china()
{
    int ans=0,lcm=1,x,y;
    for(int i=1;i<=n;i++) lcm*=m[i];
    for(int i=1;i<=n;i++)
    {
        int M=lcm/m[i];
        ex_gcd(M,m[i],x,y);
        x=(x%m[i]+m[i])%m[i];
        ans=(ans+M*x*a[i])%lcm;
    }
    return (ans+lcm)%lcm;
}

擴展中國剩餘定理

{xa1(mod m1)xa2(mod m2)xa3(mod m3)xan(mod mn) \left\{ \begin{aligned} x &amp; \equiv a_1(mod\ m_1)\\ x &amp; \equiv a_2(mod\ m_2)\\ x &amp; \equiv a_3(mod\ m_3)\\ \vdots\\ x &amp; \equiv a_n(mod\ m_n)\\ \end{aligned} \right.
一樣的方程只不過這裏的mim_i不互質了


它與中國剩餘定理半毛錢關係也木有~~
我們先來看一看如果只有兩個方程的情況:
{xa1(mod m1)xa2(mod m2) \left\{ \begin{aligned} x &amp; \equiv a_1(mod\ m_1)\\ x &amp; \equiv a_2(mod\ m_2)\\ \end{aligned} \right.
這個方程組其實可以轉化成:
{x=a1+k1m1x=a2+k2m2 \left\{ \begin{aligned} x=a_1+k_1*m_1\\ x=a_2+k_2*m_2\\ \end{aligned} \right.
然後我們把兩式相減,就得到了:
k1m1k2m2=a2a1k_1*m_1-k_2*m_2=a_2-a_1
哦,有沒有發現這是一個形如這個的方程:ax+by=ca*x+b*y=c

也就是說我們可以用擴展歐幾里得解決(那是什麼?

於是,我們就得到了k1k_1k2-k_2

然後x0=k1m1+a1x_0=k_1*m_1+a_1

我們也就知道了方程的通解是:x=x0+mlcm(m1,m2)x=x_0+m*lcm(m_1,m_2)(不停擴大或減小lcm(m1,m2)lcm(m_1,m_2)對它們的餘數是沒有影響的)

也就是說:xx0(mod lcm(m1,m2))x \equiv x_0(mod\ lcm(m_1,m_2))

有沒有發現我們把兩個方程合併成了 一個!!

然後不停地合併下去,我們就得到了答案!

poj2891:

#include<iostream>
#include<cstdio> 
#define ll long long 
using namespace std;

int n;

ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b); 
}

void ex_gcd(ll a,ll b,ll &x,ll &y){
    if(!b) x=1,y=0;
    else ex_gcd(b,a%b,y,x),y-=(a/b)*x;
}

ll mul(ll a,ll b,ll p){
    ll ans=0;
    while(b){
        if (b%2) ans=(ans+a)%p;
        a=(a+a)%p;
        b/=2;
    }
    return ans;
}


int main(){
    ll m2,a2,m,ans;
    while(~scanf("%d",&n)){
        bool flag=0;
        scanf("%lld%lld",&m,&ans);
        for (int i=2;i<=n;i++){
            scanf("%lld%lld",&m2,&a2);
            a2=(a2-ans%m2+m2)%m2;//a2-a1 
            ll d=gcd(m2,m),x,y;
            if (a2%d) flag=1;//方程無解
            ex_gcd(m,m2,x,y);
            x=mul(x,a2/d,m2);//解k1*m1-k2*m2=a2-a1 (如果你想過掉【洛谷P4777】這裏要加一個龜速乘)
            ans+=x*m;
            m*=m2/d;//m1=lcm(m1,m2) 
            ans=(ans%m+m)%m;
        } 
        if (flag) printf("-1\n");
        else printf("%lld\n",ans);  
    }
    return 0;
}

OK,完事

參考:
https://www.cnblogs.com/freinds/p/6388992.html
https://blog.csdn.net/niiick/article/details/80229217
https://www.cnblogs.com/Miracevin/p/9254795.html
https://www.luogu.org/recordnew/show/19877167
https://www.luogu.org/recordnew/show/19921331

於HG機房

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章