中國剩餘定理,又名孫子定理o(*≧▽≦)ツide
能求解什麼問題呢?spa
問題:設計
一堆物品3d
3個3個分剩2個code
5個5個分剩3個orm
7個7個分剩2個blog
問這個物品有多少個ip
解這題,咱們須要構造一個答案it
咱們須要構造這個答案io
5*7*inv(5*7, 3) % 3 = 1
3*7*inv(3*7, 5) % 5 = 1
3*5*inv(3*5, 7) % 7 = 1
這3個式子對不對,別告訴我逆元你忘了(*´∇`*),忘了的人請翻閱前幾章複習
而後兩邊同乘你須要的數
2 * 5*7*inv(5*7, 3) % 3 = 2
3 * 3*7*inv(3*7, 5) % 5 = 3
2 * 3*5*inv(3*5, 7) % 7 = 2
令
a = 2 * 5*7*inv(5*7, 3)
b = 3 * 3*7*inv(3*7, 5)
c = 2 * 3*5*inv(3*5, 7)
那麼
a % 3 = 2
b % 5 = 3
c % 7 = 2
其實答案就是a+b+c
由於
a%5 = a%7 = 0 由於a是5的倍數,也是7的倍數
b%3 = b%7 = 0 由於b是3的倍數,也是7的倍數
c%3 = c%5 = 0 由於c是3的倍數,也是5的倍數
因此
(a + b + c) % 3 = (a % 3) + (b % 3) + (c % 3) = 2 + 0 + 0 = 2
(a + b + c) % 5 = (a % 5) + (b % 5) + (c % 5) = 0 + 3 + 0 = 3
(a + b + c) % 7 = (a % 7) + (b % 7) + (c % 7) = 0 + 0 + 2 = 2
你看你看,答案是否是a+b+c(。・ω・)ノ゙,徹底知足題意
可是答案,不僅一個,有無窮個,每105個就是一個答案(105 = 3 * 5 * 7)
根據計算,答案等於233,233%105 = 23
若是題目問你最小的那個答案,那就是23了
如下抄自百度百科
1 //n個方程:x=a[i](mod m[i]) (0<=i<n) 2 LL china(int n, LL *a, LL *m){ 3 LL M = 1, ret = 0; 4 for(int i = 0; i < n; i ++) M *= m[i]; 5 for(int i = 0; i < n; i ++){ 6 LL w = M / m[i]; 7 ret = (ret + w * inv(w, m[i]) * a[i]) % M; 8 } 9 return (ret + M) % M; 10 }
要不要來一道題試試手?
poj 1006
http://poj.org/problem?id=1006
問題描述:
人自出生起就有體力,情感和智力三個生理週期,分別爲23,28和33天。一個週期內有一天爲峯值,在這一天,人在對應的方面(體力,情感或智力)表現最好。一般這三個週期的峯值不會是同一天。如今給出三個日期,分別對應於體力,情感,智力出現峯值的日期。而後再給出一個起始日期,要求從這一天開始,算出最少再過多少天后三個峯值同時出現。
分析:
由於23 = 23
28 = 2*2*7
33 = 3*11
知足兩兩互質關係,因此直接套模板就行了
AC代碼:
1 #include<cstdio> 2 typedef long long LL; 3 const int N = 100000 + 5; 4 void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){ 5 if (!b) {d = a, x = 1, y = 0;} 6 else{ 7 ex_gcd(b, a % b, y, x, d); 8 y -= x * (a / b); 9 } 10 } 11 LL inv(LL t, LL p){//若是不存在,返回-1 12 LL d, x, y; 13 ex_gcd(t, p, x, y, d); 14 return d == 1 ? (x % p + p) % p : -1; 15 } 16 LL china(int n, LL *a, LL *m){//中國剩餘定理 17 LL M = 1, ret = 0; 18 for(int i = 0; i < n; i ++) M *= m[i]; 19 for(int i = 0; i < n; i ++){ 20 LL w = M / m[i]; 21 ret = (ret + w * inv(w, m[i]) * a[i]) % M; 22 } 23 return (ret + M) % M; 24 } 25 int main(){ 26 LL p[3], r[3], d, ans, MOD = 21252; 27 int cas = 0; 28 p[0] = 23; p[1] = 28; p[2] = 33; 29 while(~scanf("%I64d%I64d%I64d%I64d", &r[0], &r[1], &r[2], &d) && (~r[0] || ~r[1] || ~r[2] || ~d)){ 30 ans = ((china(3, r, p) - d) % MOD + MOD) % MOD; 31 printf("Case %d: the next triple peak occurs in %I64d days.\n", ++cas, ans ? ans : 21252); 32 } 33 34 }
固然,這個中國剩餘定理只是基礎,面對更強大的敵人,咱們要有更強的武器
好比,m1,m2, ... ,mn兩兩不保證互質,辣怎麼辦(っ °Д °)っ
別怕,看我接着拋代碼
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef long long LL; 5 typedef pair<LL, LL> PLL; 6 PLL linear(LL A[], LL B[], LL M[], int n) {//求解A[i]x = B[i] (mod M[i]),總共n個線性方程組 7 LL x = 0, m = 1; 8 for(int i = 0; i < n; i ++) { 9 LL a = A[i] * m, b = B[i] - A[i]*x, d = gcd(M[i], a); 10 if(b % d != 0) return PLL(0, -1);//答案不存在,返回-1 11 LL t = b/d * inv(a/d, M[i]/d)%(M[i]/d); 12 x = x + m*t; 13 m *= M[i]/d; 14 } 15 x = (x % m + m ) % m; 16 return PLL(x, m);//返回的x就是答案,m是最後的lcm值 17 }
這個代碼我不給予解釋(由於我不會,哇哈哈哈╰(*°▽°*)╯)
遇到須要的題就去套模板吧
(想知道代碼原理的去百度吧,或者看《挑戰程序設計競賽》,我模板是從書裏抄來通過傑哥修改的)
好比poj 2891
http://poj.org/problem?id=2891
【題目大意】
給出k個模方程組:x mod ai = ri。求x的最小正值。若是不存在這樣的x,那麼輸出-1.
【題目分析】
因爲這道題目裏面的ai、ri之間不知足兩兩互質的性質,因此不能用中國剩餘定理直接求解。
辣麼。。。。愉快的套這個模板吧
AC代碼以下:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef long long LL; 5 typedef pair<LL, LL> PLL; 6 LL a[100000], b[100000], m[100000]; 7 LL gcd(LL a, LL b){ 8 return b ? gcd(b, a%b) : a; 9 } 10 void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){ 11 if (!b) {d = a, x = 1, y = 0;} 12 else{ 13 ex_gcd(b, a % b, y, x, d); 14 y -= x * (a / b); 15 } 16 } 17 LL inv(LL t, LL p){//若是不存在,返回-1 18 LL d, x, y; 19 ex_gcd(t, p, x, y, d); 20 return d == 1 ? (x % p + p) % p : -1; 21 } 22 PLL linear(LL A[], LL B[], LL M[], int n) {//求解A[i]x = B[i] (mod M[i]),總共n個線性方程組 23 LL x = 0, m = 1; 24 for(int i = 0; i < n; i ++) { 25 LL a = A[i] * m, b = B[i] - A[i]*x, d = gcd(M[i], a); 26 if(b % d != 0) return PLL(0, -1);//答案,不存在,返回-1 27 LL t = b/d * inv(a/d, M[i]/d)%(M[i]/d); 28 x = x + m*t; 29 m *= M[i]/d; 30 } 31 x = (x % m + m ) % m; 32 return PLL(x, m);//返回的x就是答案,m是最後的lcm值 33 } 34 int main(){ 35 int n; 36 while(scanf("%d", &n) != EOF){ 37 for(int i = 0; i < n; i ++){ 38 a[i] = 1; 39 scanf("%d%d", &m[i], &b[i]); 40 } 41 PLL ans = linear(a, b, m, n); 42 if(ans.second == -1) printf("-1\n"); 43 else printf("%I64d\n", ans.first); 44 } 45 }
你看,全TM是套路
哇哈哈哈╰(*°▽°*)╯
哇哈哈哈╰(*°▽°*)╯
哇哈哈哈╰(*°▽°*)╯
哇哈哈哈╰(*°▽°*)╯
哇哈哈哈╰(*°▽°*)╯
哇哈哈哈╰(*°▽°*)╯