1. 程式人生 > 實用技巧 >拓展中國剩餘定理(exCRT)

拓展中國剩餘定理(exCRT)

演算法簡介

中國剩餘定理用於求同餘方程組。

理論

我們要解

\[\begin{cases} x≡a_1(mod\quad m_1)\\ x≡a_2(mod\quad m_2)\\ ......\\ x≡a_n(mod\quad m_n) \end{cases} \]

下面是仲爺的個性解法:
考慮合併2個方程

\[ x≡a_1\pmod{m_1}\\ x≡a_2\pmod{m_2} \]

\(x=a_1+y_1m_1=a_2+y_2m_2\)
新合併的方程便是\(x≡a_1+y_1m_1≡a_2+y_2m_2\pmod {lcm(m1,m2)}\)
於是用exgcd對\(a_1-a_2=y_2m_2-y_1m_1\)

解出一組合法的\(y_1\)\(y_2\),帶回去算出新合併的方程,繼續合併下去就行。
很抱歉,這個東西我沒調出來,多半是仲爺太強而我太菜了。

下面是主流解法:
假設知道前\(k-1\)個方程的解\(x\),假設\(m=lcm_{i=1}^{k-1}b_i\),那麼\(x+i*m(i\in Z)\)肯定是前\(k-1\)個方程的通解。於是用exgcd解出\(x+i*m≡a_i\pmod {b_i}\)
程式碼(沒過,畢竟\(O(1)\)快速乘有精度問題)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5;
int n;
ll a[N+10],b[N+10];
inline ll Read()
{
	ll x=0,f=1;char ch=getchar();
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	ch=='-'?f=-1:x=ch-'0';
	while(isdigit(ch=getchar())) x=x*10+ch-'0';
	return x*f;
}
ll mul(ll x,ll y,ll md) {return (x*y-(ll)((long double)x/md*y)*md+md)%md;}
ll gcd(ll x,ll y) {return y?gcd(y,x%y):x;}
ll lcm(ll x,ll y) {return x*y/(gcd(x,y));}
void exgcd(ll A,ll B,ll &x,ll &y)
{
	if(!B) {x=1,y=0;return;}
	exgcd(B,A%B,x,y);
	ll tmp=x;
	x=y,y=tmp-(A/B)*y;
}
inline ll excrt()
{
	ll ans=a[1],m=b[1],x,y;
	for(register int i=2;i<=n;++i)
	{
		ll gc=gcd(m,b[i]),tmp=((a[i]-ans)%b[i]+b[i])%b[i];
		exgcd(m,b[i],x,y);
		x=mul(x,tmp/gc,b[i]);
		ans+=m*x;
		m*=b[i]/gc;
		ans=(ans+m)%m;
	}
	return ans;
}
int main()
{
	n=Read();
	for(register int i=1;i<=n;++i) b[i]=Read(),a[i]=Read();
	cout<<excrt();
	return 0;
}