總結「中國剩餘定理」
阿新 • • 發佈:2020-10-27
搬運自遠古的洛咕部落格,故文風與現在有很大不同
CRT 中國剩餘定理
設 \(m_1,m_2,...,m_x\) 是兩兩互質的整數,\(M= \prod_{i=1}^{x} m_i ,M_i= \frac {M}{m_i}\),\(t_i\) 是 \(M_it_i \equiv 1 \ ({\rm {mod}} \ m_i)\) 的一個解,對於任意的整數 \(a_1,a_2,...,a_x\),方程組:
\[\begin{cases} x \equiv a_1 \ ({\rm {mod}} \ m_1) \\ x \equiv a_2 \ ({\rm {mod}} \ m_2) \\ ... \\ x \equiv a_x \ ({\rm {mod}} \ m_x) \\ \end{cases} \]有整數解,解為 \(\sum_{i=1}^x a_iM_it_i\) 。
證明略 定理不就是用來背的嗎
模版:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define lxl long long using namespace std; lxl a[15],b[15]; int n; inline lxl exgcd(lxl a,lxl b,lxl &x,lxl &y) { if(!b) {x=1,y=0;return a;} lxl k=exgcd(b,a%b,x,y); lxl z=x;x=y,y=z-a/b*y; return k; } inline lxl china() { lxl M=1,ans=0; for(int i=1;i<=n;i++) M*=a[i]; for(int i=1;i<=n;i++) { lxl tx,y,Mi=M/a[i]; exgcd(Mi,a[i],tx,y); ans=(ans+b[i]*Mi*tx)%M; } return (ans+M)%M; } int main() { //freopen("P1495.in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]); printf("%lld\n",china()); return 0; }
exCRT 擴充套件中國剩餘定理
說是中國剩餘定理,但是好像和CRT關係不大。
擴充套件中國剩餘定理就是合併線性同餘方程式,解決 \(m_i\) 不互素的情況。
先考慮兩個同餘方程式:
\[\begin{cases} x \equiv a_1 ({\rm {mod}} \ b_1) \\ x \equiv a_2 ({\rm {mod}} \ b_2) \end{cases} \implies\\ \begin{cases} x = a_1 +k_1 * b_1 \\ x \equiv a_2 + k_2 * b_2 \end{cases} \implies\\ a_1 +k_1 * b_1=a_2 + k_2 * b_2 \implies\\ k_1 * b_1+k_2 * b_2=a_2-a_1 \]如果不定方程有解,使用擴充套件歐幾里德演算法求出一個 \(k_1\) 的特解 \(k_0\),則 \(x\) 的一個特解 \(x_0=a_1+k_0 * b_1\)。通解
\[k_i=k_0+u* \frac {b_2}{{\rm{gcd}}(b_1,b_2)},u \in {\rm{Z}} \]則:
\[\begin{aligned} x&=k_i * b_1 +a_1\\ &=u * \frac {b_1b_2}{{\rm{gcd}}(b_1,b_2)}+a_1+k_0 * b_1\\ &=u * {\rm {lcm}}(b_1,b_2)+x_0\\ x& \equiv x_0 \ ({\rm {mod}} \ {\rm {lcm}} (b_1,b_2))\\ \end{aligned} \]於是就把兩個同餘方程式合併成了一個。同理,將所有方程式按此方法合併,答案為最後的 \(x_0\)。
模版:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define lxl long long
#define maxn 100005
using namespace std;
inline lxl times(lxl a,lxl b,lxl mod)
{
lxl ans=0;
while(b>0)
{
if(b%2) ans=(ans+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ans;
}
inline lxl exgcd(lxl a,lxl b,lxl &x,lxl &y)
{
if(!b) {x=1,y=0;return a;}
lxl k=exgcd(b,a%b,x,y);
lxl z=x;x=y,y=z-a/b*y;
return k;
}
int n;
lxl a[maxn],b[maxn];
int main()
{
//freopen("P4777.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]);
lxl M=a[1],ans=b[1];
for(int i=2;i<=n;i++)
{
lxl a1=M,a2=a[i],bi=(b[i]-ans%a2+a2)%a2,x,y;
lxl g=exgcd(a1,a2,x,y);
a2/=g,bi/=g;
x=times(x,bi,a2);
ans+=M*x;
M*=a[i]/g;
ans=(ans+M)%M;
}
printf("%lld\n",(ans+M)%M);
return 0;
}