Kineski teorem o ostatcima
Problem
Zadano je jednadžbi oblika . Značenje ove jednadžbe je da ima ostatak pri dijeljenju s . Nađi najmanji koji zadovoljava sve jednadžbe, ako takav broj ne postoji ispiši .
Rješenje
Rješenje koje se prvo nameće je brute force provjera svih brojeva dok ne nađemo prvi koji zadovoljava jednadžbe. Primijetimo da, u ovisnosti o ulaznim podatcima, rješenje može biti jako veliko, također rješenje ne mora postojati. Stoga brute force nije najsretnija opcija.
Probajmo riješiti problem ako imamo samo dvije jednadžbe. Neka su jednadžbe:
Primijetimo da ne moramo prolaziti sve brojeve nego samo one koji odgovaraju prvoj jednadžbi te njih možemo uspoređivati s drugom jednadžbom kako bi našli najmanje rješenje. Znamo da je rješenje prve jednadžbe oblika , odnosno te možemo početi od sve dok ne nađemo neki koji zadovoljava obje jednadžbe. Rješenje dobivamo za , odnosno prvi broj koji zadovoljava obje jednadžbe je .
Dodajmo treću jednadžbu: .
Sada moramo prolaziti brojeve koji zadovoljavaju obje prve jednadžbe te ih uspoređivati s novom jednadžbom. Primijetimo da sve te brojeve možemo zapisati pomoću nove jednadžbe. U ovom slučaju ta jednadžba je . Do te jednadžbe možemo doći dodavanjem ili početnom broju tražeći sljedeću vrijednost koja zadovoljava obje jednadžbe, no taj postupak može biti spor. Primijetimo da će faktor uz dijeliti i i , a pošto bi htjeli najmanji takav broj dovoljno je naći . Sada smo problem ponovno sveli na samo dvije jednadžbe, a taj smo problem riješili gore pa ćemo primijeniti isti postupak.
Krećemo od i dodajemo 30 sve dok ne pronađemo prvi broj koji zadovoljava drugu jednadžbu.
- , ,
- , ,
- , ,
Postavlja se pitanje kako ćemo otkriti ako rješenje ne postoji. Odgovor je vrlo jednostavan, ako dva puta naiđemo na isti ostatak tada znamo da rješenje ne postoji. Ako rješenje ne postoji prvo ćemo naići upravo na prvi ostatak jer se ostatci ponašaju ciklično.
int n;
pair<int, int> jed[MAXN];
pair<int, int> cur;
long long int sol;
int LCM(int a, int b){
/*znamo od prije*/
}
int main(){
cin >> n;
for(int i = 0; i < n; i++) cin >> jed[i].first >> jed[i].second;
cur = jed[0];
for(int i = 1; i < n; i++){
for(int i = 1; i < n; i++){
bool flag = true;
for(int k = 0; k < jed[i].second; k++){
if((k * cur.second + cur.first) % jed[i].second == jed[i].first){
cur.first = k * cur.second + cur.first;
cur.second = LCM(jed[i].second, cur.second);
flag = false;
break;
}
}
if(flag){
cout << -1;
return 0;
}
}
cout << cur.first;
}
Analiza složenosti
Pošto postoji jednadžbi, a mi postupno zamjenjujemo dvije jednadžbe s jednom novom, to ćemo ponoviti puta. Svaka zamjena je složenosti jer ćemo proći najviše brojeva da nađemo onaj koji nam odgovara ili da shvatimo da nema rješenja, a nakon toga ćemo računati , no ta je složenost manja pa ju možemo zanemariti. Stoga je ukupna složenost , naravno to možemo zapisati i kao .