1. 程式人生 > 其它 >洛谷 P4777 【模板】擴充套件中國剩餘定理(EXCRT)

洛谷 P4777 【模板】擴充套件中國剩餘定理(EXCRT)

傳送門


拓展中國剩餘定理EXCRT

早知道有這東西就不學CRT了嗚嗚嗚(ljCRT)
這個比CRT範圍更廣更快更好寫……
雖然我寫掛了,並且調了兩天四五個小時
還是這一堆式子:

\[\begin{cases} x\equiv b_1\pmod {a_1}\\ x\equiv b_2\pmod {a_2}\\ x\equiv b_3\pmod {a_3}\\ \cdots\cdots\cdots\cdots\\ \end{cases}\]

但是 \(a_i\) 不需要互質了。
首先考慮通解:
假設我們已經求出了某個同餘方程的一個特解 \(x\),那麼通解就等於 \(x+ka\)
於是我們就有了一個思路——不斷合併同餘方程。
那麼問題來了,怎麼合併兩個同餘方程式?
先求出其中一個的通解,設為 \(ans+xa_1\)

,則有:

\[ans+xa_1\equiv b_2\pmod {a_2} \]

移項得:

\[xa_1\equiv b_2-ans\pmod {a_2} \]

這個就可以轉化成:

\[xa_1+ya_2=b_2-ans \]

再使用 \(exgcd\) 求得 \(x\) 的一個解,新的特解就等於 \(ans+xa_1\)
接下來就把這個特解繼續帶入下一個同餘方程式,一步步計算即可。
注意要使用龜速乘。

注意事項

主要問題在龜速乘上,請看龜速乘

AC程式碼

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100005;
int n;
long long m,x,y,ans;
long long a[maxn],b[maxn];
inline long long ksc(long long a,long long b,long long mod){
    return (a*b-(long long)((long double)a/mod*b)*mod+mod)%mod;
}
long long exgcd(long long a,long long b){
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    long long gcd=exgcd(b,a%b);
    swap(x,y);
    y=y-(a/b)*x;
    return gcd;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]);
    m=a[1],ans=b[1]%a[1];
    for(int i=2;i<=n;i++){
        long long gcd=exgcd(m%a[i],a[i]);
        long long c=b[i]-ans;
        x=ksc(x,c/gcd,a[i]);
        long long M=m/gcd*a[i];
        ans=((ans+x*m)%M+M)%M;
        m=M;
    }
    cout<<(ans%m+m)%m;
    return 0;
}