中国剩余定理

中国剩余定理

对于一组同余方程,\(m_{1},m_{2}...m_{n}两两互质\)

\[\left\{\begin{array}{l} {x \equiv a_{1}\left(\bmod m_{1}\right)} \\ {x \equiv a_{2}\left(\bmod m_{2}\right)} \\ {\cdots} \\ {x \equiv a_{n}\left(\bmod m_{n}\right)} \end{array}\right. \]

有整数解。并且在模\(M = m_{1}*m_{2}..m_{n}\)下的解是唯一的,解为

\[\boldsymbol{x} \equiv\left(\boldsymbol{a}_{1} M_{1} M_{1}^{-1}+a_{2} M_{2} M_{2}^{-1}+\ldots+a_{k} M_{k} M_{k}^{-1}\right) \bmod M \]

其中\(M_{i} = \frac{M}{m_{i}},M^{-1}_{i}\)\(M_{i}\)\(m_{i}\)的逆元

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){
    if(b == 0){
        d = a,x = 1,y = 0;
        return;
    }
    ex_gcd(b,a%b,d,y,x);
    y -= x*(a/b);
}
ll china(ll a[],ll m[],int n){//x = a (mod m)
    ll M = 1,y,d,x = 0;
    for(int i = 0; i < n; i++)M *= m[i];
    for(int i = 0; i < n; i++){
        ll w = M/m[i];
        ex_gcd(m[i],w,d,d,y);
        x = (x + a[i] * w * y) % M;
    }
    if((x + M) % M == 0) return M;
    return (x + M) % M;
}
int main(){
    int n;
    cin >> n;
    ll a[maxn],m[maxn];
    for(int i = 0; i < n; i++){
        cin >> a[i] >> m[i];
    }
    cout << china(a,m,n) << endl;
    return 0;
}

扩展中国剩余定理

\(m_{1},m_{2}...m_{n}不一定两两互质时\)

\[\left\{\begin{array}{l} {x \equiv a_{1}\left(\bmod m_{1}\right)} \\ {x \equiv a_{2}\left(\bmod m_{2}\right)} \\ {\cdots} \\ {x \equiv a_{n}\left(\bmod m_{n}\right)} \end{array}\right. \]

对于\(\begin{cases}x \equiv a_1(\mod m_1)\\x\equiv a_2(\mod m_2) \end{cases}\)

进行变化有

\(\begin{cases} x=a_1+k_1\times m_1 \\ x=a_2 +k_2\times m_2 \end{cases}\)

\(a_1 + m_1\times k_1 = a_2 + m_2\times k_2\\m_1k_1 = (a_2 - a_1) + m_2k_2\)

方程有解当且仅当\(gcd(m_1,m_2)|(a_2 - a_1)\)

\(gcd(m_1,m_2) = d\)

\(\frac{m_1k_1}{d} = \frac{a_2 - a_1}{d} + \frac{m_2k_2}{d} \\ \frac{m_1}{d}k_1 \equiv \frac{a_2 - a_1}{d}(\mod \frac{m_2}{d})\)

两边同时除\(\frac{m_1}{d}\)

\(k_1 \equiv inv(\frac{m_1}{d},\frac{m_2}{d}) * \frac{c_2 - c_1}{d} (\mod \frac{m_2}{d}) \\ k_1 = inv(\frac{m_1}{d},\frac{m_2}{d}) * \frac{a_2 - a_1}{d} + \frac{m_2}{d} * y\)

把原式\(x = a_1 + k_1m_1\)带入

\(x = inv(\frac{m_1}{d},\frac{m_2}{d}) * \frac{a_2 - a_1}{d} * m_1 + y\frac{m_1m_2}{d} + a_1 \\ x \equiv inv(\frac{m_1}{d},\frac{m_2}{d}) * \frac{a_2 - a_1}{d} * m_1 + a_1(\mod \frac{m_1m_2}{d})\)

那么答案就是\(x \equiv a(\mod m)\)

其中\(a = (inv (\frac{m_1}{d}, \frac{m_2}{d}) * \frac{a_2 - a_1}{d}) \%\frac{m_2}{d} *m_1 + a_1\\m = \frac{m_1m_2}{d}\)

不断地进行两两合并,然后利用线性同余方程求出这两个同余方程的组的x,然后继续合并

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){
    if(b == 0){
        d = a,x = 1,y = 0;
        return;
    }
    ex_gcd(b,a%b,d,y,x);
    y -= x * (a/b);
}
ll mull(ll a,ll b,ll mod){
    ll ans = 0;
    while(b){
        if(b & 1)ans = (ans + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return ans;
}
ll ex_china(ll a[],ll m[],int n){//x = a(mod m)
    ll M = m[0];
    ll ans = a[0];
    ll d,x,y;
    for(int i = 1; i < n; i++){
        ex_gcd(M,m[i],d,x,y);
        if((ans - a[i]) % d)return -1;
        ll c = ((a[i] - ans) % m[i] + m[i]) % m[i];
        ll mod = m[i]/d;
        x = mull(x,c/d,mod);
        ans += x * M;
        M *= mod;
        ans = (ans % M + M) % M;
    }
    return ans;
}
int main(){
    int n;
    ll a[maxn],m[maxn];
    cin >> n;
    for(int i = 0; i < n; i++){
        cin >> a[i] >> m[i];
    }
    cout << ex_china(a,m,n) << endl;
    return 0;
}
posted @ 2020-01-26 14:18  Emcikem  阅读(198)  评论(0编辑  收藏  举报