孙子定理,又称之为中国剩余定理 ( C h i n e s e R e m a i n d e r T h e o r e m , C R T ) (Chinese Remainder Theorem, CRT) (ChineseRemainderTheorem,CRT)可以求解如下形式的一元线性同余方程组(其中 n 1 , n 2 , … , n k n_1,n_2,\dots,n_k n1,n2,…,nk两两互质)。
{ x ≡ a 1 ( m o d n 1 ) x ≡ a 2 ( m o d n 2 ) ⋮ x ≡ a k ( m o d n k ) \begin{cases}x\equiv a_1(mod\:n_1)\\x\equiv a_2(mod\:n_2)\\\quad\quad \vdots \\x\equiv a_k(mod\:n_k)\\\end{cases} ⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡a1(modn1)x≡a2(modn2)⋮x≡ak(modnk)
中国剩余定理说明:假设整数 m 1 , m 2 , … , m n m_1,m_2,\dots,m_n m1,m2,…,mn两两互质,则对任意的整数: a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,…,an,方程组有解,并且我们可以用如下方式构造得到:
设 M = m 1 × m 2 × ⋯ × m n = ∏ i = 1 n m i M=m_1\times m_2\times\dots\times m_n=\prod_{i=1}^{n}m_i M=m1×m2×⋯×mn=∏i=1nmi是整数 m 1 , m 2 , … , m n m_1,m_2,\dots,m_n m1,m2,…,mn的乘积,并设 M i = M m i , ∀ i ∈ { 1 , 2 , 3 , … , n } M_i=\frac{M}{m_i},\forall i\in\{1,2,3,\dots,n\} Mi=miM,∀i∈{1,2,3,…,n}是除了 m i m_i mi以外的 n − 1 n-1 n−1个整数的乘积。设 t i t_i ti是 M i M_i Mi模 m i m_i mi下的逆元,则方程组的通解形式为: x = a 1 t 1 M 1 + a 2 t 2 M 2 + ⋯ + a n t n M n + k M = k M + ∑ i = 1 n a i t i M i , k ∈ Z x=a_1t_1M_1+a_2t_2M_2+\dots+a_nt_nM_n+kM=kM+\sum_{i=1}^na_it_iM_i,k\in \Zeta x=a1t1M1+a2t2M2+⋯+antnMn+kM=kM+∑i=1naitiMi,k∈Z。所以在模 M M M的意义下,方程组的只有一个解: x = ( ∑ i = 1 n a i t i M i ) ( m o d M ) x=(\sum_{i=1}^na_it_iM_i)(mod\:M) x=(∑i=1naitiMi)(modM)。
算法流程:
下面我们来通过一道题,加深对孙子定理的运用
猜数字
现有两组数字,每组 k k k个,第一组中的数字分别用 a 1 , a 2 , … , a k a_1,a_2,\dots,a_k a1,a2,…,ak表示,第二组中的数字分别用 b 1 , b 2 , … , b k b_1,b_2,\dots,b_k b1,b2,…,bk表示。其中第二组的数字是两两互素的。求最小的 n ∈ N n\in \Nu n∈N,满足对于 ∀ i ∈ [ i , k ] \forall i\in[i,k] ∀i∈[i,k],有 b i ∣ ( n − a i ) b_i|(n-a_i) bi∣(n−ai)。
输入格式
第一行一个整数 k k k。
第二行 k k k个整数,表示: a 1 , a 2 , … , a k a_1,a_2,\dots,a_k a1,a2,…,ak。
第三行 k k k个整数,表示: b 1 , b 2 , … , b k b_1,b_2,\dots,b_k b1,b2,…,bk。
输出格式
输出一行一个整数,为所求的答案 n n n。 1 ≤ k ≤ 10 , ∣ a i ∣ ≤ 1 0 9 , 1 ≤ b i ≤ 6 × 1 0 3 ∏ i = 1 k b i ≤ 1 0 18 1≤k≤10,∣a_i∣≤10^9,1≤b_i≤6×10^3\prod_{i=1}^k b_i\le 10^{18} 1≤k≤10,∣ai∣≤109,1≤bi≤6×103∏i=1kbi≤1018。
这是孙子定理的典型运用,同样也是一道模板题,在这我我们观察到数据的范围很大,所以在计算的最后一步我们不能直接使用乘法运算,而是使用快速乘。
#include
#include
using namespace std;
typedef long long ll;
#define endl '\n'
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define _for(i, a, b) for (int i=(a); i<=(b); i++)
const int INF = 0x7fffffff;
const int MAXN = 1e5 + 10;
ll m[MAXN], a[MAXN], n;
void exgcd(ll a, ll b, ll &x, ll &y){
if(b == 0){
x = 1;
y = 0;
return ;
}
exgcd(b,a%b,x,y);
ll temp = x;
x = y;
y = temp - a / b * y;
return ;
}
ll qmul(ll n, ll a, ll mod){
ll res = 0;
a = a % mod;
while(n){
if(n & 1) res = (res + a) % mod;
n >>= 1;
a = (a + a) % mod;
}
return res % mod;
}
ll CHT(){
ll res = 0, M = 1;
for(int i = 1; i <= n; i++){
M *= m[i];
}
for(int i = 1; i <= n; i++){
ll x, y;
ll Mi = M / m[i];
exgcd(Mi,m[i],x,y);
x = (x % m[i] + m[i]) % m[i];//防止x为负数
res = (res + qmul(qmul(Mi,x,M),a[i],M)) % M;//使用快速乘不然有的会爆long long
}
if(res < 0) res += M;
return res;
}
int main(){
cin >> n;
for(ll i = 1; i <= n; i++){
cin >> a[i];
}
for(int i= 1; i <= n; i++){
cin >> m[i];
}
for(int i = 1; i <= n; i++){
a[i] = (a[i] % m[i] + m[i]) % m[i];
}
cout << CHT();
return 0;
}