(模板)中國剩餘定理 重學筆記 POJ1006
資料推薦
有很大幫助的部落格
——(本文多處引用其中金句,特此註明出處並鞠躬感謝博主。)
主要定理
- 定理一: 幾個數相加,如果任何一個數不能被div整除,那麼這幾個數的和一定不能被div整除。
- 定理二: 兩數不能整除,若除數擴大(或縮小)了幾倍,而被除數不變,則其商和餘數也同時擴大(或縮小)相同的倍數(餘數必小於除數)。
主要步驟
- 求出各個除數(div)的最小公倍數(lcm)。
- 求出各個除數的基礎數(base),某個除數的基礎數應該滿足幾個性質:1. 能被除了這個除數以外的所有除數整除,2.除以這個除數的餘數符合題意。
- 將所有基礎數求和後取模最小公倍數,結果即為答案。
相關證明
上面的那個部落格非常的經典,在此直接引用並深鞠躬感謝博主。
(1)最小公倍數就不用解釋了,跳過(記住,這裡討論的都是兩兩互質的情況)
(2)觀察求每個數對應的基礎數時候的步驟,比如第一個。105÷3=35。顯然這個35是除了當前這個數不能整除以外都能夠被其他數整除,就是其他數的最小公倍數。相當於找到了最小的開始值,用它去除以3發現正好餘2。那麼這個基礎數就是35。記住35的特徵,可以整除其他數但是不能被3整除,並且餘數是2。體現的還不夠明顯,再看下5對應的基礎數。21是其他數的最小公倍數,但是不能被5整除,用21除以5得到的餘數是1,而要求的數除以5應該是餘1的。所以餘數被擴大,就得到了相應的基礎數63。記住這個數的特徵,可以被其他數整除但是被5除應該餘三。同理,我們得到了第三個基礎數23,那麼他的特徵就是:可以被其他數整除,但是不能被7整除,並且餘數為2。
(3)第三步基礎數加和,為什麼要這樣做呢?利用就是上面提到的定理1。
35+63+30=128。對於3來說,可以把63+30的和看作一個整體,應該他們都可以被3整除。看著上面寫出的三個數的特徵,運用定理1來說,就是在35的基礎上加上一個可以被3整除的倍數,那麼得到的結果依然還是滿足原先的性質的,就是128除以同樣還是餘2的。同理,對於5還說,這個數被除之後會剩餘3;對於7來說,被除之後剩餘2。所以說,我們當前得到的這個數是滿足題目要求的一個數。但是這個數是不是最小的,那就不一定了。(4)應該不能確定是不是最小的數,這個時候就要用到他們的最小公倍數了。最小公倍數顧名思義,一定是一個同時被幾個數整除的最小的一個數,所以減去它剩餘下來的餘數還是符合題意要求的。當然也同樣可以運用定理1來解釋,只不過是加法變成了減法,道理還是一樣的。當然具體要不要剪還是要看和lcm的大小關係的。
我的程式碼
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=int(20);
int n;
ll div[maxn],r[maxn];
long long exgcd(ll a,ll b,ll &x,ll &y) {
if(b==0) {x=1,y=0; return a;}
ll res=exgcd(b,a%b,x,y), tmp=y;
y=x-y*(a/b), x=tmp;
return res;
}
ll china(ll n,ll *div,ll *r) {
ll lcm=1,d,y,x=0;
for(int i=1;i<=n;i++)
lcm*=div[i];
for(int i=1;i<=n;i++) {
ll base=lcm/div[i];
exgcd(div[i],base,d,y);
x=(x+y*base*r[i])%lcm;
}
return (x+lcm)%lcm;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&div[i],&r[i]);
printf("%lld\n",china(n,div,r));
return 0;
}
例題 POJ1006
題目描述
人自出生起就有體力,情感和智力三個生理週期,分別為23,28和33天。一個週期內有一天為峰值,在這一天,人在對應的方面(體力,情感或智力)表現最好。通常這三個週期的峰值不會是同一天。現在給出三個日期,分別對應於體力,情感,智力出現峰值的日期。然後再給出一個起始日期,要求從這一天開始,算出最少再過多少天后三個峰值同時出現。
題解
由題意可知,設滿足條件的那天是第res天,每個生理週期的週期長度分別為T1,T2,T3,每個峰值出現的日期分別為D1,D2,D3,則滿足以下等式:
由此可以列出同餘線性方程組,用中國剩餘定理求解即可。
程式碼
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
long long exgcd(ll a,ll b,ll &x,ll &y) {
if(b==0) {
x=1, y=0;
return a;
} else {
long long res=exgcd(b,a%b,x,y), tmp=y;
y=x-y*(a/b), x=tmp;
return res;
}
}
long long China(int n,ll *div,ll *r) {
long long lcm=1,x=0;
for(int i=1;i<=n;i++)
lcm*=div[i];
for(int i=1;i<=n;i++) {
long long base=lcm/div[i],tmp,cur;
exgcd(div[i],base,tmp,cur);
x=(x+cur*base*r[i])%lcm;
}
return (x+lcm)%lcm;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE
long long div[5]={0,23,28,33},r[5]={};
int cur,n=3,cas=0;
while(scanf("%lld%lld%lld%d",&r[1],&r[2],&r[3],&cur)==4 && ~cur) {
for(int i=1;i<=n;i++)
r[i]%=div[i];
long long res=China(n,div,r)-cur;
if(res<=0) res+=21252;
if(res>21252) res-=21252;
printf("Case %d: the next triple peak occurs in %lld days.\n",++cas,res);
}
return 0;
}