洛谷 P4777 【模板】擴充套件中國剩餘定理(EXCRT)
阿新 • • 發佈:2021-06-12
傳送門
拓展中國剩餘定理EXCRT
早知道有這東西就不學CRT了嗚嗚嗚(ljCRT)
這個比CRT範圍更廣更快更好寫……
雖然我寫掛了,並且調了兩天四五個小時
還是這一堆式子:
但是 \(a_i\) 不需要互質了。
首先考慮通解:
假設我們已經求出了某個同餘方程的一個特解 \(x\),那麼通解就等於 \(x+ka\)。
於是我們就有了一個思路——不斷合併同餘方程。
那麼問題來了,怎麼合併兩個同餘方程式?
先求出其中一個的通解,設為 \(ans+xa_1\)
移項得:
\[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; }