Documente Academic
Documente Profesional
Documente Cultură
Olimpiada - cls9
2023-3
PROBLEME DE INFORMATICĂ
date la olimpiade
ONI
ı̂n
Dacă ştii să rezolvi problemele date ı̂n ultimii 10-20 de ani
atunci vei şti să rezolvi problemele care se vor da ı̂n acest an!
https://www.scribd.com/user/552245048/Adi-Rabaea
2023-3-13
Dedication
( in ascending order! )
1
https://www.femalefirst.co.uk/books/carol-lynne-fighter-1034048.html
2
https://otiliaromea.bandcamp.com/track/dor-de-el
3
https://en.wikipedia.org/wiki/To_be,_or_not_to_be
4
https://www.youtube.com/watch?v=eMtcDkSh7fU
ii
Prefaţă
Stilul acestor cărţi este ... ca şi cum aş vorbi cu nepoţii mei (şi chiar cu mine ı̂nsumi!) ... ı̂ncercând
ı̂mpreună să găsim o rezolvare cât mai bună pentru o problemă dată la olimpiadă.
Ideea, de a scrie aceste culegeri de probleme date la olimpiadele de informatică, a apărut acum
câţiva ani când am ı̂ntrebat un student (care nu reuşea să rezolve nişte probleme foarte simple):
”Ce te faci dacă un elev, care ştie că eşti student şi că studiezi şi informatică, te roagă, din când ı̂n
când, să-l ajuţi să rezolve câte o problemă de informatică dată la gimnaziu la olimpiadă, sau pur
şi simplu ca temă de casă, şi tu, aproape de fiecare dată, nu ı̂l poţi ajuta? Eu cred că nu este prea
bine şi poate că ... te faci ... de râs!” Pe vremea mea (!), când eram elev de gimnaziu, un student
era ca un fel de ... zeu! Cu trecerea anilor am ı̂nţeles că nu este chiar aşa! Şi ı̂ncă ceva: nu am
reuşit să ı̂nţeleg de ce, atunci când cineva nu poate să rezolve corect o problemă de informatică
de clasa a 6-a, dată la olimpiada de informatică sau ca temă de casă, foloseşte această scuză: ”eu
nu am făcut informatică ı̂n liceu!” şi acest cineva este ”zeul” sau ”zeiţa” despre care vorbeam!.
5
Sunt convins că este important să studiem cu atenţie cât mai multe probleme rezolvate! . Cred
că sunt utile şi primele versiuni ale acestor cărţi ... ı̂n care sunt prezentate numai enunţurile şi
indicaţiile ”oficiale” de rezolvare. Acestea se găsesc ı̂n multe locuri; aici ı̂ncerc să le pun pe ”toate
la un loc”! Fiecare urcă spre vârf ... cât poate! Sunt multe poteci care duc spre vârf iar această
carte este ... una dintre ele!
Limbajul de programare se alege ı̂n funcţie de problema pe care o avem de rezolvat. Cu nişte
ani ı̂n urmă alegerea era mai simplă: dacă era o problemă de calcul se alegea Fortran iar dacă era
o problemă de prelucrarea masivă a datelor atunci se alegea Cobol. Acum alegerea este ceva mai
6 7
dificilă! :-) Vezi, de exemplu, IOI2020 şi IOI2019 , IOI2015 .
Cred că, de cele mai multe ori, este foarte greu să gândim ”simplu” şi să nu ”ne complicăm”
atunci când cautăm o rezolvare pentru o problemă dată la olimpiadă. Acesta este motivul pentru
care vom analiza cu foarte mare atenţie atât exemplele date ı̂n enunţurile problemelor cât şi
”restricţiile” care apar acolo (ele sigur ”ascund” ceva interesant din punct de vedere al algoritmului
8
de rezolvare!) .
Am ı̂nceput câteva cărţi (pentru clasele de liceu) cu mai mulţi ani ı̂n urmă, pentru perioada
2000-2007 ([29] - [33]), cu anii ı̂n ordine crescătoare!). A urmat o pauză de câţiva ani (destul de
mulţi!). Am observat că acele cursuri s-au ”ı̂mprăştiat” un pic ”pe net” ([48] - [56])! Încerc acum
să ajung acolo unde am rămas ... plecând mereu din prezent ... până când nu va mai fi posibil ...
aşa că, de această dată, anii sunt ı̂n ordine ... descrescătoare! :-)
”Codurile sursă” sunt cele ”oficiale” (publicate pe site-urile olimpiadelor) sau publicate pe alte
site-uri (dacă mi s-a părut că sunt utile şi se poate ı̂nvăţa câte ceva din ele).
Pentru liceu perioada acoperită este de ”azi” (până când va exista acest ”azi” pentru mine!)
până ı̂n anul 2000 (aveam deja perioada 2000-2007!).
Pentru gimnaziu perioada acoperită este de ”azi” până ı̂n anul 2010 (nu am prea mult timp
9
disponibil şi, oricum, calculatoarele folosite la olimpiade ı̂nainte de 2010 erau ceva mai ’slabe’ şi
iii
11
depăşeşte pragul ”exascale”! (1.102 Exaflop/s). ”Peta” a fost depăşit ı̂n 2008, ”tera” ı̂n 1997,
12
”giga” ı̂n 1972. . Pentru ce a fost mai ı̂nainte, vezi https://en.wikipedia.org/wiki/Li
st_of_fastest_computers.
13
O mică observaţie: ı̂n 2017 a fost prima ediţie a olimpiadei EJOI ı̂n Bulgaria şi ... tot
14
ı̂n Bulgaria a fost şi prima ediţie a olimpiadei IOI ı̂n 1989. Dar ... prima ediţie a olimpiadei
15
IMO (International Mathematical Olympiad) a fost ı̂n România ı̂n 1959. Tot ı̂n România s-au
ţinut ediţiile din anii 1960, 1969, 1978, 1999 şi 2018. Prima ediţie a olimpiadei BOI (Balkan
Olympiad in Informatics) a fost ı̂n România ı̂n 1993 la Constanţa. Prima ediţie a olimpiadei
CEOI (Central-European Olympiad in Informatics) a fost ı̂n România ı̂n 1994 la Cluj-Napoca.
Revenind la ... “culegerile noastre” ... mai departe, probabil, va urma completarea unor
informaţii ı̂n ”Rezolvări detaliate” ... pentru unele probleme numai (tot din cauza lipsei timpului
necesar pentru toate!). Prioritate vor avea problemele de gimnaziu (nu pentru că sunt mai ’uşoare’
ci pentru că ... elevii de liceu se descurcă şi singuri!). Acum, ı̂n martie 2022, am ı̂nceput şi
redactarea unei culegeri de probleme date la bacalaureat ı̂n ultimii câţiva ani (câţi voi putea!).
Îmi aduc aminte că exista o interesantă vorbă de duh printre programatorii din generaţia mea:
”nu se trage cu tunul ı̂ntr-o muscă” . Sensul este: nu se scrie un cod complicat dacă se
poate scrie un cod simplu şi clar! Asta ı̂ncerc eu ı̂n ”Rezolvări detaliate”.
Vom ı̂ncerca, ı̂mpreună, şi câteva probleme de ... IOI ... dar asta este o treabă ... nu prea
uşoară! Cred totuşi că este mai bine să prezint numai enunţuri ale problemelor date la IOI ı̂n
ultimii câţiva ani! (asta aşa, ca să vedem cum sunt problemele la acest nivel!). Cei care ajung
acolo sau vor să ajungă acolo (la IOI) sigur nu au nevoie de ajutorul meu! Se descurcă singuri!
”ALGORITMI utili la olimpiadele de informatică”, separat pentru gimnaziu şi liceu, sper să
fie de folos, aşa cum cred că sunt [1] - [28], [34] - [47], [57] - [83], ... şi multe alte cărţi şi site-uri!.
Ar fi interesant să descoperim noi ı̂nşine cât mai mulţi algoritmi ... ı̂n loc să-i ı̂nvăţăm pur şi
simplu!
O altă mică observaţie: ce am strâns şi am scris ı̂n aceste cărţi
se adresează celor interesaţi de aceste teme! Nu cârcotaşilor! Sunt
evidente sursele ”de pe net” (şi locurile ı̂n care au fost folosite) aşa
că nu sunt necesare ”ghilimele anti-plagiat”, referinţe şi precizări
suplimentare la fiecare pas!
Şi un ultim gând: criticile şi sfaturile sunt utile dacă au valoare!
Dacă sunt numai aşa ... cum critică lumea la un meci de fotbal ...
sau cum, pe bancă ı̂n parc, ”ı̂şi dă cu părerea” despre rezolvarea
problemelor economice ale ţării ... atunci ... !!!
16
”I’m only responsible for what I say, not for what you understand.”
Adrese interesante (rezultatele elevilor români):
https://stats.ioinformatics.org/halloffame/
https://stats.ioinformatics.org/tasks/
http://stats.ioinformatics.org/results/ROU
Adresele acestor cursuri:
https://www.scribd.com/user/550183580/Adrian-Rabaea
https://www.scribd.com/user/552245048/Adi-Rabaea
https://drive.google.com/drive/folders/1hC5PZuslCdS95sl37SW46H-qy59GRDGZ
Adrese utile (programe şcolare):
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20IX-a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20X_a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20XI-a.pdf
17
”I want to thank God most of all because without God I wouldn’t be able to do any of this.”
Adrian R.
17
I.d.k.: ”I don’t know who the author is.”
v
Despre autor
18
nume: Răbâea Aurel-Adrian, 18.03.1953 - ...
vi
Cuprins
Prefaţă iii
Cuprins vii
Lista figurilor xv
1 ONI 2022 1
1.1 colibri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 geogra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 schi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2 ONI 2021 12
2.1 Le Mans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.1.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2 Oposumi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.3 ELHC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3.2 *Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4 ONI 2019 20
4.1 amat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2 Comun . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.3 pro3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
vii
4.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.4 Cub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.4.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.5 fibofrac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.5.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.6 telefon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.6.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5 ONI 2018 71
5.1 bazaf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.1.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2 mexitate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.2.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.3 plaja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
5.3.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.4 bsrec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.4.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.5 numinum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.5.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
5.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
5.6 rosii mici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
5.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
5.6.2 Cod sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
5.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Index 512
Bibliografie 514
4.1 amat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
xv
A.38 Execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
A.39 Rezultat execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
A.40 Fişiere apărute după compilare! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
A.41 Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿ . . . . . . . . . . . . . . . . . . 491
A.42 Lista programelor de utilizat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
A.43 Selectare Code::Blocks IDE pentru fişierele .cpp . . . . . . . . . . . . . . . . . . 492
A.44 Editare+Compilare+Execuţie pentru test02 . . . . . . . . . . . . . . . . . . . . . 492
A.45 Selectare tab ce conţine test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 493
xvii
Lista programelor
4.1.1 alex-nnlog.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.1.2 alex-nqlog.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.1.3 amat eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.1.4 brut.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.1.5 brut IQ 9000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1.6 chiorean amat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.1.7 solution.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.1.8 sursa test.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.2.1 comun back.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2.2 comun chiorean.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2.3 comun eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.2.4 comun io.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.2.5 comun nlog sub.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.2.6 comun sol.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.2.7 sol nlog.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.3.1 pro3 AT1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.3.2 pro3 CC1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.3.3 pro3 CC2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.3.4 pro3 LB1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.4.1 cub chiorean.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.4.2 cub chiorean brut.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.4.3 cub chiorean n3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.4.4 cub100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.4.5 cub100 int.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.5.1 fibofrac CC1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.5.2 fibofrac CC2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.5.3 fibofrac CC3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.5.4 fibofrac CC4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.5.5 fibofrac CC5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.5.6 fibofrac TC1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.6.1 alex.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.6.2 telefon-adrian-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.6.3 telefon-bicsi-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.6.4 tudor telefon v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.1.1 1 bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.1.2 2 bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.1.3 3 bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.1.4 4 bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.2.1 mexitate1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.2.2 mexitate2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.3.1 plaja N+K.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.3.2 plaja ternara.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.3.3 plaja1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.3.4 plaja2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.3.5 plaja3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.3.6 plaja4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.4.1 bsrec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.4.2 bsrec2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.5.1 1 numinum 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
xviii
5.5.2 2 numinum 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
5.6.1 rosiimici.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
5.6.2 rosiimici2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6.1.1 arhipelag 100p 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
6.1.2 arhipelag 100p 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
6.2.1 mirror 100p 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
6.2.2 mirror 100p 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
6.2.3 mirror 100p 3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
6.2.4 mirror 100p 4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
6.2.5 mirror 100p 5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
6.2.6 mirror 100p 6.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
6.3.1 okcpp 97p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
6.3.2 okcpp 100p 1 doar linux.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
6.3.3 okcpp 100p 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
6.4.1 adlic 100p 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
6.4.2 adlic 100p 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
6.4.3 adlic 100p 3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
6.5.1 bomboane 100p 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
6.5.2 bomboane 100p 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
6.5.3 bomboane 100p 3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.6.1 orase 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
7.1.1 civilizatie BICSI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
7.1.2 civilizatie100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
7.2.1 cmmdc BICSI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
7.3.1 livada dan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
7.3.2 Livada100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
7.4.1 dif2 bicsi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
7.4.2 dif2 eric.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
7.4.3 dif2 N log2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
7.5.1 leduri bicsi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
7.5.2 leduri eric.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
7.6.1 omogene eric.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
7.6.2 omogene n 4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
7.6.3 omogeneMG.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
8.1.1 cubul cp1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
8.1.2 cubul cp2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
8.1.3 cubul gcc 2matrici.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
8.1.4 cubul gcc umplere ciur.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
8.1.5 cubul mot e.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
8.2.1 risc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
8.2.2 risc n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
8.2.3 risc nlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
8.3.1 roboti aib.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
8.3.2 roboti dp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
8.3.3 roboti greedy.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
8.4.1 casa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
8.4.2 casa slow.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
8.4.3 casa2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
8.5.1 lenes.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
8.5.2 lenes2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
8.5.3 lenes3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
8.6.1 sipet.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
9.1.1 harta.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
9.1.2 harta Adriana.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
9.1.3 harta brut 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
9.1.4 harta brut 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
9.1.5 harta eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
9.1.6 harta pit.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
9.1.7 harta1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
9.2.1 qvect eugen bf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
9.2.2 qvect eugen fs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
9.2.3 qvect eugen std.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
9.2.4 qvect inter.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
9.2.5 qvect mink.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
9.2.6 qvect qsort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
9.2.7 qvect qsortscanf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
9.2.8 qvect vs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
9.3.1 tg 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
9.3.2 tg on.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
9.3.3 tg on v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
9.3.4 tg on2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
9.3.5 tg onsqrtn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
9.4.1 CC2 progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
9.4.2 CC3 progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
9.4.3 CM progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
9.4.4 EN progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
9.4.5 PIT progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
9.4.6 SP progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
9.5.1 reflex eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
9.5.2 reflex LS brut.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
9.5.3 reflex LS Euclid 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
9.5.4 reflex LS med.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
9.5.5 reflex LS Sr 100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
9.5.6 reflex vs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
9.6.1 traseu carmen nerec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
9.6.2 traseu carmen rec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
9.6.3 traseu eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
9.6.4 traseu vspit.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
10.1.1 aranjare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
10.2.1 gradina.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
10.3.1 split.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
10.4.1 momente.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
10.5.1 secvente.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
10.6.1 spider.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
11.1.1 PIT 7segmente.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
11.1.2 CC 7segmente.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
11.1.3 RH 7segmente.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
11.2.1 PIT copaci.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
11.2.2 RH copaci.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
11.2.3 VI copaci.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
11.3.1 PIT1 intersectii.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
11.3.2 PIT2 intersectii.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
11.3.3 brut intersectii.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
11.3.4 GM intersectii.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
11.3.5 VI intersectii.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
11.4.1 CTpalindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
11.4.2 RHpalindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
11.4.3 VIpalindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
11.5.1 PRIVsstabil 1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
11.5.2 PRIVsstabil 2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
11.5.3 VI sstabil.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
11.6.1 GMunuab 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
11.6.2 GMunuab back.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
11.6.3 GMunuab n patrat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
11.6.4 VI unuzero.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
12.1.1 poligon.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
12.2.1 stalpi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
12.3.1 tort.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
12.4.1 ape.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
12.5.1 ec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
12.6.1 furnici.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
13.1.1 cern100v1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
13.1.2 cern100v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
13.2.1 cmmmc60.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
13.2.2 cmmmc100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
13.3.1 simetric20.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
13.3.2 simetric40.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
13.3.3 simetric100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
13.4.1 pesti.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
13.5.1 plaja.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
13.6.1 tango.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
14.1.1 joc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
14.2.1 perspic.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
14.3.1 rafturi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
14.4.1 br.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
14.5.1 origami.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
14.6.1 patrate.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
15.1.1 ab.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
15.2.1 iepuras.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
15.3.1 palind.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
15.3.2 palindvxn.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
15.3.3 palindvxv.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
15.4.1 auto.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
15.4.2 autonk.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
15.5.1 div rares.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
15.6.1 teatru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
15.6.2 teatru n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
15.6.3 teatru n3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
16.1.1 carteleC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
16.1.2 cartele1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
16.1.3 cartele2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
16.1.4 cartele3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
16.2.1 PARITATE.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
16.2.2 Paritate.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
17.1.1 FLORI.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
17.1.2 flori1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
17.1.3 flori2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
17.2.1 PLUTONC.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
17.2.2 PLUTCARM.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
17.2.3 PLUTRADU.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
17.2.4 pluton1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
17.2.5 pluton2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
18.1.1 numere.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
18.1.2 numere.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
18.2.1 MaxD1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
18.2.2 MaxD2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
19.1.1 EXP.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
19.1.2 Expresie1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
19.1.3 Expresie2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
19.1.4 Expresie3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
19.2.1 REACT QS.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
19.2.2 REACTIVI.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
19.2.3 reactivp.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
19.2.4 reactivi.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
20.1.1 TEXT.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
20.1.2 text.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
20.2.1 numere.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
20.2.2 numere.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
21.1.1 poarta.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
21.2.1 mouce.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
B.4.1exponentiere rapida1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
B.4.2exponentiere rapida2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
B.4.3exponentiere rapida3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
B.4.4exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
B.5.1exponentiere naiva MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
B.5.2exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
B.6.1secventa cod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
C.2.1cautare binara-v1-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
C.2.2cautare binara-v1-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
C.3.1cautare binara-v2-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
C.3.2cautare binara-v2-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
C.4.1cautare binara-v3-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
C.4.2cautare binara-v3-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Capitolul 1
ONI 2022
1.1 colibri
Problema 1 - Colibri 100 de puncte
Se dau N triplete de numere naturale ai , bi , ci , unde ci j 0 şi 1 & i & N , fiecare reprezentând
câte un număr raţional qi egal cu:
ai
1 bi
ci
Cerinţe
Găsiţi un subşir nevid al şirului q1 , q2 , ..., qN al cărui produs al valorilor să fie maxim posibil.
Date de intrare
Date de ieşire
Pe prima linie a fişierului de ieşire colibri.out se află un şir de N cifre. Cifra i, unde 1 & i & N ,
este 1 dacă şi numai dacă qi este selectat ı̂n subşirul soluţie, altfel este 0. Cifrele şirului nu se vor
separa prin spaţii.
# Punctaj Restricţii
1 30 N & 19 şi ai , bi , ci & 9
2 20 N & 19
3 20 ai , bi , ci & 9
4 30 Fără restricţii suplimentare
1
CAPITOLUL 1. ONI 2022 1.1. COLIBRI 2
Exemple:
colibri.in colibri.out
5 01001
001
242
477
123
032
Explicaţie:
În exemplu N 5, q1 10 , q2 45 , q3 77 , q4 32 , q2 32 .
Produsul maxim posibil este egal cu 3. Acesta se poate obţine luând fie subşirul constând din
numerele q2 şi q5 , fie luând subşirul format din numerele q2 , q3 şi q5 . Cu alte cuvinte, şi răspunsul
01101 este corect.
O variantă de subşir care este soluţie şi are produsul ı̂n 1, conţine toate fracţiile
pozitive supraunitare şi pozitive echiunitare. De asemenea conţine şi cele mai mici 2 K
fracţii negative cu menţiunea că cele mai mari două dintre acestea formează un produs mai
mare sau egal cu 1.
În continuare ştim că nu exista soluţie ı̂n 1, şi ı̂ncercăm să găsim una ı̂n (0, 1). Dacă
există atunci conţine cel mult 2 fracţii. Fie este formată din cea mai mare fracţie pozitivă,
fie este formată din cele mai mici 2 fracţii negative.
Cel mai mare produs al unui subşir este 0 atunci când nu exista nicio fracţie pozitivă, există
cel mult o fracţie negativă şi cel puţin o fracţie cu rezultatul 0.
Produsul aparţine intervalului , 1 când există o singură fracţie cu valoare negativă.
Pentru a rezolva problema vom ı̂mparţi fracţiile ı̂n 7 categorii ı̂n funcţie de rezultat.
Mai departe ne vom referi prin
Categoria I la mulţimea fracţiilor cu rezultatul ı̂n , 1
Categoria II la mulţimea fracţiilor cu rezultatul ı̂n 1, 1
CAPITOLUL 1. ONI 2022 1.2. GEOGRA 3
O idee de rezolvare este să considerăm fiecare din cele 7 intervale ı̂n ordine descrescătoare şi să
verificăm dacă există un subşir de fracţii care ı̂nmulţite dau o valoare ı̂n intervalul la care ne
referim.
1, . Pentru a obţine un produs mai mare ca 1 e nevoie ca cel puţin una din următoarele
propoziţii sa fie adevărate:
– există o fracţie ı̂n VII;
– există două fracţii ı̂n I;
– există o fracţie ı̂n I şi una ı̂n II;
– există o fracţie ı̂n I şi una ı̂n III care ı̂nmulţite dau mai mult decât 1.
Pentru a obţine valoarea maximă se aleg ı̂n această ordine:
– toate fracţiile din VII.
– cele mai mici fracţii din mulţimea I două câte două. Astfel, după alegere, ı̂n I fie nu
rămâne nimic (dacă ¶I ¶ este număr par), fie rămâne cea mai mare fracţie (¶I ¶ este
impar).
– dacă a rămas o fracţie ı̂n I şi există o fracţie ı̂n II atunci ambele sunt alese.
– dacă a rămas o fracţie ı̂n I şi există o fracţie ı̂n III al căror produs este % 1 atunci
ambele sunt alese.
1, 1. Din moment ce suntem aici ı̂nseamnă că nu există un subşir ale cărui fracţii să
dea un produs % 1. Pentru a obţine un produs egal cu 1 e nevoie ca cel puţin una din
următoarele sa fie adevărată:
– există o fracţie ı̂n VI;
– există 2 fracţii ı̂n II;
– există o fracţie ı̂n I şi una ı̂n III care ı̂nmulţite dau 1.
0, 1. Din moment ce suntem aici ı̂nseamnă că nu există un subşir ale cărui fracţii să
dea un produs ' 1. Pentru a obţine un produs din 0, 1 e nevoie ca cel puţin una din
următoarele sa fie adevărată:
– există o fracţie ı̂n V ;
– există o fracţie ı̂n II şi una ı̂n III;
– există o fracţie ı̂n I şi una ı̂n III care ı̂nmulţite aparţin 0, 1;
– există 2 fracţii ı̂n III.
Se alege subşirul care obţine cel mai mare produs raportat la cele 4 cazuri de mai sus.
0, 0. Dacă există cel puţin o fracţie cu numărătorul 0 atunci răspunsul este 0.
1, 0 sau 1, 1 sau , 1. Dacă suntem ı̂n acestă situaţie atunci cu siguranţă
testul conţine o singură fracţie şi aceasta este negativă. Prin urmare aceasta reprezintă şi
soluţia problemei.
1.2 geogra
Problema 2 - Geogra 100 de puncte
Pasionaţi de geografie, Alex şi Răzvan joacă online Geoguessr.
Harta lumii este alcătuită din N locaţii numerotate de la 1 la N , fiecare desemnând un punct ı̂n
plan de coordonate Xi , Yi . Alex a studiat atent toate cele N locaţii şi a determinat o listă de L
CAPITOLUL 1. ONI 2022 1.2. GEOGRA 4
Cerinţe
La finalul unei runde este posibil ca mai multe locaţii din cele N să corespundă cu şirul R de
informaţii aflate de Alex, aşa că acesta ı̂şi pune două ı̂ntrebări existenţiale:
1. Care este numărul K de locaţii dintre cele N care respectă şirul R de informaţii aflate pe
parcursul rundei? Spunem că o locaţie i respectă şirul R dacă pentru orice caracteristică j avem
ca Rj Zi,j sau Rj 1.
2. Se dă câte o valoare Wi pentru fiecare locaţie i din cele N , 1 & i & N . Considerând locaţiile
care respectă şirul R, fie acestea 1 & i1 , i2 , ..., iK & N , pentru un punct P de coordonate ı̂ntregi
A, B din plan, nu neapărat unul corespunzător unei locaţii dintre cele N , definim
dP Wi1 ¶A Xi1 ¶ ¶B Yi1 ¶ Wi2 ¶A Xi2 ¶ ¶B Yi2 ¶ ... WiK ¶A XiK ¶ ¶B YiK ¶.
Care este punctul P din plan pentru care dP este minim? Dacă sunt mai multe astfel de
puncte P , se cere cel cu A minim. Dacă ı̂ncă este egalitate, atunci se cere cel cu B minim.
Se dă un număr C " r1, 2x. Pentru C 1 să se afişeze răspunsul la prima ı̂ntrebare a lui Alex
pentru fiecare din cele Q runde. Pentru C 2 să se afişeze răspunsul la a doua ı̂ntrebare a lui
Alex pentru fiecare din cele Q runde.
Date de intrare
Pe prima linie se află numărul natural C, reprezentând cerinţa care trebuie rezolvată.
Pe cea de-a doua linie se află numerele naturale N şi L, reprezentând numărul locaţiilor şi,
respectiv, numărul caracteristicilor studiate de Alex.
Urmează descrierile celor N locaţii, ı̂n ordine de la 1 la N , fiecare pe câte doua linii. Descrierea
unei locaţii i se face după cum urmează:
Pe prima linie se află numerele naturale Xi şi Yi . Dacă C 2, ı̂n continuarea acestor doua
valori se află numărul natural Wi . Valorile de pe linie sunt separate prin spaţii.
Pe a doua linie se afla Zi,1 , Zi,2 , ..., Zi,L , separate prin spaţii.
Pe următoarea linie se află numărul natural Q, reprezentând numărul de runde din cadrul
jocului.
Urmează descrierile celor Q runde, ı̂n ordine de la 1 la Q, fiecare pe câte o linie. Descrierea
unei runde i se face prin şirul de valori R1 , R2 , ..., RL găsit de Alex ı̂n runda respectivă, valorile
fiind separate prin spaţii.
CAPITOLUL 1. ONI 2022 1.2. GEOGRA 5
Date de ieşire
Se vor afişa Q linii. Dacă C 1, linia i va conţine numărul locaţiilor care respectă şirul R
de informaţii aflate de Alex ı̂n runda i. Dacă C 2, linia i va conţine doua numere naturale A
şi B, reprezentând coordonatele punctului P căutat pentru care dP este minim ı̂n runda i, unde
1 & i & N.
# Punctaj Restricţii
1 9 C 1 şi 1 & N, Q, L, Xi , Yi & 10
2 11 C 1 şi Ti i pentru 1 & i & L
3 17 C 1
4 9 C 2 şi 1 & N, Q, L, Xi , Yi , Wi &i , Yi & 10 000
6 7 C 2 şi 1 & N, Q & 400 şi 1 & Xi , Yi & 250 000
7 7 C 2 şi 1 & N, Q & 1 000
8 12 C 2 şi Wi 1
9 21 C 2
Exemple:
geogra.in geogra.out
1 1
74 3
14 7
1101 4
31 2
1110
72
0100
97
1010
39
0000
56
0010
44
1100
5
1010
-1 0 -1 0
-1 -1 -1 -1
-1 1 -1 -1
1 1 -1 0
CAPITOLUL 1. ONI 2022 1.2. GEOGRA 6
2 9 7
74 3 7
141 5 2
1101 7 2
311 3 1
1110
725
0100
971
1010
392
0000
561
0010
441
1100
5
1010
-1 0 -1 0
-1 -1 -1 -1
-1 1 -1 -1
1 1 -1 0
Explicaţii:
1.3 schi
Problema 3 - Schi 100 de puncte
Ultimul sezon rece a fost unul cu multa zăpadă căzută ı̂n zona montană, prin urmare magazinul
de articole sportive din staţiunea de schi Semenic a avut vânzări mari, mai ales de clăpari (bocanci
speciali pentru schiat). Acum fiind sfârşit de sezon, angajaţii magazinului constată că le-au rămas
N cutii goale ı̂n care au fost ambalate perechile de clăpari vândute. Aceste cutii au forma unui
paralelipiped dreptunghic la care faţa superioară constituie capacul. Capacul şi (evident) faţa
opusă capacului sunt de formă pătrată, iar ı̂nălţimea este una oarecare, ea depinzând de modelul
de clăpari. La unele cutii capacul este prezent, la altele capacul a dispărut.
Aceste cutii trebuie predate unei firme de reciclare, dar până ajunge maşina pentru reciclat
ı̂n Semenic, cutiile trebuie depozitate. Prin urmare unul dintre angajaţii magazinului primeşte
sarcina să depoziteze cutiile punându-le una peste alta sub forma unui turn. Angajatul ia cutiile
ı̂n ordinea ı̂n care le găseşte ı̂n magazin şi le pune una peste alta. Pentru a asigura o oarecare
stabilitate a turnului, el le aşează astfel ı̂ncât axele care unesc centrul capacului cu centrul feţei
opuse capacului de la fiecare cutie să fie coliniare. Cutiile sunt astfel lăsate pe rând să cadă de
la o ı̂nălţime suficient de mare, cu feţele laterale perpendiculare pe podea şi paralele cu cele ale
cutiilor deja plasate, până când ı̂ntâlnesc una dintre aceste cutii sau podeaua. În ceea ce priveşte
aşezarea cutiilor care au capacul lipsă, angajatul le aşează fie cu golul format prin lipsa capacului
ı̂n sus, fie ı̂n jos.
Cerinţe
Cunoscând N (numărul de cutii din magazin), ordinea ı̂n care cutiile sunt aşezate ı̂n turn,
iar pentru fiecare cutie latura capacului, ı̂nălţimea cutiei şi dacă are capac sau, ı̂n cazul ı̂n care
capacul lipseşte, dacă cutia este aşezată cu golul ı̂n sus sau cu golul ı̂n jos, determinaţi:
1. ı̂nălţimea turnului astfel format;
2. numărul de cutii ale căror feţe laterale sunt vizibile dacă se priveşte turnul din lateral.
Date de intrare
Pe prima linie a fişierului de intrare schi.in se află numărul C, număr care poate fi 1 sau 2 şi
reprezintă cerinţa care trebuie rezolvată.
Pe ce de-a doua linie se află numărul natural N, reprezentând numărul de cutii care sunt aşezate
ı̂n turn.
Pe fiecare dintre următoarele N linii se află câte trei valori L, H şi M , separate prin câte un
spaţiu.
Valorile de pe linia i 2 (1 & i & N ) reprezintă informaţii referitoare la cea de-a i-a cutie
adăugată ı̂n turn, şi anume:
Date de ieşire
CAPITOLUL 1. ONI 2022 1.3. SCHI 9
# Punctaj Restricţii
1 12 C 1 şi N & 2000
2 12 C 2 şi N & 2000
3 8 C 1 şi nu avem nicio cutie aşezată cu golul ı̂n sus
4 8 C 2 şi nu avem nicio cutie aşezată cu golul ı̂n sus
5 8 C 1 şi nu avem nicio cutie aşezată cu golul ı̂n jos
6 8 C 2 şi nu avem nicio cutie aşezată cu golul ı̂n jos
7 22 C 1
8 22 C 2
Dacă C este 1, fişierul de ieşire schi.out va conţine pe prima linie răspunsul pentru cerinţa 1
(ı̂nălţimea turnului format).
Dacă C este 2, fişierul de ieşire schi.out va conţine pe prima linie răspunsul pentru cerinţa 2
(numărul de cutii ale căror feţe laterale sunt vizibile dacă se priveşte turnul din lateral).
Exemple:
schi.in schi.out
1 13
5
20 3 2
371
911
11 5 1
12 6 0
2 3
5
20 3 2
371
911
11 5 1
12 6 0
Explicaţii:
Înălţimea turnului de cutii este 13. Privind din lateral sunt vizibile 3 cutii, şi anume cele care
au capacele cu laturile 20, 3 şi 12.
CAPITOLUL 1. ONI 2022 1.3. SCHI 10
De asemenea, cutiei cj i se mai impune o restricţie şi de către podea: topj ' Hj .
Calculând pentru cutia cj restricţiile impuse de toate cutiile c1 , c2 , ..., cj 1 şi de către podea,
putem afla topj ca fiind maximul dintre acestea.
Înălţimea turnului va fi ı̂n final maxrtop1 , top2 , ..., topN x.
Pentru a determina care cutii sunt vizibile din lateral, vom defini ”intervalul de vizibilitate” al
unei cutii intervalul de ı̂nălţimi la care acea cutie este vizibilă (evident ne va interesa pentru câte
cutii acest interval are lungime nenulă).
La ı̂nceput, pentru toate cutiile de forma ci iniţializăm intervalul de vizibilitate cu topi
Hi , topi (intervalul de ı̂nălţimi pe care ı̂l ocupă cutia ci ). Cutia ci poate fi ascunsă/acoperită
parţial sau total doar de cutii cj , pentru care Lj % Li . Aşadar, ne vom uita la astfel de cutii cj ,
care fie nu acoperă ı̂n niciun fel cutia ci , fie o acoperă total, deci deja putem stabili că ci nu este
vizibilă din lateral, fie parţial, caz ı̂n care cj acoperă fie un prefix, fie un sufix al intervalului de
vizibilitate al lui ci .
Matematic, dacă notăm vizi intervalul de vizibilitate al lui ci calculat până la un pas interme-
diar şi dorim să actualizăm acest interval pe baza unei cutii cj cu Lj % Li , realizăm diferenţa de
intervale/mulţimi: vizi vizi topj Hj , topj . Este uşor de observat din procesul de plasare al
cutiilor, că această diferenţă va rezulta mereu ı̂ntr-un interval.
2
Rezolvând astfel ambele cerinţe ale problemei, obţinem complexitatea O N .
Soluţie ı̂n complexitate liniară - 100 de puncte. Pentru a obţine 100 de puncte, va fi
nevoie de o rafinare a soluţiei de mai sus.
Pentru a rezolva prima cerinţă vom ı̂ncerca să calculăm din nou topi pentru fiecare cutie ci .
Observaţia care va reduce complexitatea este că nu avem nevoie să calculăm restricţiile impuse de
c1 , ..., ci1 , cutiei ci , ci doar de către o parte dintre ele.
Notăm ci1 , ci2 , ..., cik (1 & i1 $ i2 $ ... $ ik $ i) cutiile care ar putea ı̂ntrerupe căderea lui ci .
Observăm ca acest şir de cutii este descrescător după L: dacă am avea cij şi cij1 cu Lij $ Lij1 ,
cutia cij ar fi inutilă (oricum ar cădea, ci se va lovi mai ı̂ntâi de cij1 , nu de cij ).
O altă observaţie utilă este că pentru două cutii cij şi cij1 cu Lij % Lij1 % Li , restricţia
impusă de cij1 cutiei ci va fi cel puţin la fel de mare ca restricţia impusă de cij . Cu alte cuvinte,
ı̂n subşirul descrescător de cutii ci1 , ci2 , ..., cik , pentru a determina topi vom calcula restricţiile
doar cu un sufix al acestui subşir (cutiile cu L mai mic decât Li , şi cutia cu L minim mai mare
decât Li ).
După ce am calculat topi , eliminăm din subşir sufixul format din cutii cu L mai mic decât
Li şi adăugăm cutia ci la finalul subşirului. Astfel per total numărul de restricţii calculat pentru
toate cutiile este direct proporţional cu numărul de ştergeri din acest subşir, iar din moment ce o
cutie este adăugată şi ştearsă din subşir cel mult o dată, complexitatea va fi liniară.
Pentru a determina vizibilitatea cutiilor, vom folosi noţiunea de interval de vizibilitate descrisă
mai sus. Vom actualiza aceste intervale pentru toate cutiile mai ı̂ntâi considerând doar acoperirile
CAPITOLUL 1. ONI 2022 1.3. SCHI 11
realizate de cutiile de tipul 2 (cu golul ı̂n sus) şi apoi separat cele realizate de cutii de tipul 0 (cu
golul ı̂n jos).
Vom explica ı̂n continuare procesul pentru acoperirile produse de cutii de tipul 2. Vom procesa
cutiile ı̂n ordinea ı̂n care au fost adăugate ı̂n turn. În momentul de faţă suntem la cutia ci de un
tip oarecare şi vrem să vedem ce cutii de tipul 2 o acoperă parţial/total. Evident aceste cutii, ca
să poată acoperi ci , trebuiau adăugate ı̂nainte şi să aibă L mai mare decât Li .
Dintre cutiile de tip 2 care respectă aceste condiţii, ne interesează top-ul maxim (extremitatea
superioară maximă) a acestor cutii care poate acoperi un prefix al intervalului de vizibilitate
pentru ci .
O observaţie care ne va duce la soluţia dorită este că dacă după o cutie de tip 2 se adaugă la
un pas ulterior o cutie cu L mai mare (de orice tip), cutia de tip 2 nu va mai acoperi pe nimeni
ı̂ncepând cu acest moment.
De aceea, la pasul i putem menţine subşirul de cutii de tip 2: ci1 , ci2 , ..., cik (1 & i1 $ i2 $ ... $
ik $ i) cu proprietatea că Li1 % Li2 % ... % Lik . Când procesăm cutia ci eliminăm din subşir toate
cutiile cu L mai mic decât Li (acestea nu pot acoperi ci şi nicio altă cutie adăugată ı̂n viitor).
Presupunem că rămânem acum cu k cutii ı̂n subşir, pentru a determina ce prefix din intervalul
de vizibilitate al lui ci ar fi acoperit, trebuie să determinăm maxrtopi1 , topi2 , ..., topik x. Acesta
nu este altceva decât un maxim pe prefix pentru subşirul nostru, pe care ı̂l putem menţine ı̂n
complexitate constantă atunci când modificăm subşirul. După acest pas, ı̂n cazul ı̂n care ci este
de tipul 2 o adăugăm la finalul subşirului şi continuăm cu cutia i 1.
Pentru determinarea acoperirilor produse de cutii de tipul 0 (cu golul ı̂n jos) strategia este
similară, ı̂nsă cutiile vor trebui parcurse ı̂n ordinea inversă a adăugării lor ı̂n turn (”de sus ı̂n
jos”).
În final, complexitatea obţinută pentru ambele cerinţe este O N .
ONI 2021
2.1 Le Mans
Problema 1 - Le Mans 100 de puncte
Ne aflăm ı̂nainte de ı̂nceputul faimoasei curse de anduranţă de la Le Mans. După cum bine
stiţi, ı̂ntr-o cursă de anduranţă maşina care a parcurs cea mai mare distanţă pe parcursul cursei
este considerată câştigătoare.
Anul acesta Federaţia Internaţională de Automobilism (FIA) a făcut câteva schimbări majore
cu privire la desfăşurarea cursei. Anul acesta cursa va dura exact T secunde şi vor participa N
echipe, fiecare echipă având câte o maşină, iar fiecare maşină poate pleca de pe oricare dintre cele
M poziţii din grila de start.
De asemenea, FIA a impus câteva reguli care au nemulţumit echipele participante:
Fiecare maşină este obligată să se deplaseze cu o viteză constantă pe parcursul ı̂ntregii curse.
Astfel, a i-a maşină se va deplasa cu viteza de v i metri pe secundă.
Dacă o maşină pleacă de pe o poziţie j din grila de start, aceasta se află la o distanţă de
pj metri după linia de start, iar această distanţă este luată ı̂n considerare ca o distanţă
deja parcursă ı̂n cadrul cursei.
Cerinţe
Ca semn de protest asupra noului regulament, echipele au hotărât să se aşeze ı̂n grila de start
astfel ı̂ncât diferenţa maximă dintre distanţele parcurse de oricare două maşini să fie cât mai mică
posibil.
Date de intrare
N - numărul de maşini,
Pe a doua linie se află N numere separate prin câte un spaţiu, reprezentând şirul v de viteze
ale maşinilor.
Pe a treia linie se află M numere separate prin câte un spaţiu, reprezentând şirul p - distanţele
faţă de linia de start a poziţiilor de start din grilă.
Date de ieşire
Fişierul lemans.out va conţine pe prima linie un singur număr, reprezentând valoarea minimă
posibilă a diferenţei maxime dintre distanţele parcurse de oricare două maşini. Pe a doua linie se
vor afla N numere ı̂ntre 1 şi M separate prin câte un spaţiu, al i-lea număr reprezentând poziţia
de start din grilă a maşinii cu numărul i.
12
CAPITOLUL 2. ONI 2021 2.1. LE MANS 13
Pot exista mai multe distribuţii ale maşinilor pe grila de start, ce oferă o soluţie optimă. Se
acceptă orice soluţie corectă.
Subtask 1 - 8 puncte - M 1,
Subtask 2 - 9 puncte - M 2,
Subtask 3 - 10 puncte - N, M & 7,
Subtask 4 - 19 puncte - v i & 10 şi pi & 10 ,
3 6
Exemple:
1. Ml şi Mr ı̂ncep de pe aceeaşi poziţie, caz ı̂n care putem să alegem toate maşinile de pe acea
poziţie.
2. Ml ı̂ncepe de pe poziţia P2 şi Mr ı̂ncepe de pe poziţia P1 . Atunci, pentru celelalte maşini
alegem poziţiile astfel ı̂ncât distanţa parcursă plecând de la acea poziţie să fie cât mai aproape
de intervalul dat de distanţele parcurse de maşinile Ml şi Mr .
CAPITOLUL 2. ONI 2021 2.2. OPOSUMI 14
2.2 Oposumi
Problema 2 - Oposumi 100 de puncte
O familie de oposumi are o vizuină cu N niveluri şi N N 1©2 camere dispuse ı̂n formă
de matrice triunghiulară cu N linii. În fiecare cameră poate locui un singur oposum. Vizuina a
fost săpată ı̂n pământ de către oposumi, iar nivelul 1 (cel mai de sus) este cel mai apropiat de
suprafaţa solului. Pe fiecare nivel I se află I camere. Dacă avem I $ J, atunci nivelul I va fi
poziţionat mai sus decât nivelul J, adică nivelul I va fi mai aproape de suprafaţa solului decât
nivelul J. În familia de oposumi se află exact N N 1©2 membri cu vârste cuprinse ı̂ntre 1 şi
N N 1©2, vârste distincte. Regula de bază ı̂n vizuina familiei de oposumi este următoarea:
ı̂n camera de pe linia I şi coloana J trebuie să locuiască un oposum mai tânăr decât ı̂n camerele
de pe poziţiile I 1, J respectiv I 1, J 1. Un oposum de vârsta X se consideră mai tânăr
decât un oposum de vârsta Y dacă X $ Y . Fiecare oposum vrea să ştie care e cel mai de sus nivel
pe care se poate poziţiona. Din păcate, ei nu au lăbuţele făcute să programeze, aşa că membrii
familiei de oposumi vă cer vouă ajutorul.
CAPITOLUL 2. ONI 2021 2.2. OPOSUMI 15
Cerinţe
Date de intrare
Pe prima linie a fiş ierului de intrare oposumi.in se găseşte numărul T ce poate avea valoarea
1 sau 2 astfel ı̂ncât:
Dacă T are valoarea 1, atunci se cere rezolvarea cerinţei 1, iar ı̂n continuare se va regăsi
numărul natural N reprezentând numărul de niveluri ale vizuinii.
Dacă T are valoarea 2, atunci se cere rezolvarea cerinţei 2, iar ı̂n continuare se va regăsi
numărul natural N reprezentând numărul de niveluri ale vizuinii, urmat de numărul natural
K ce reprezintă vârsta oposumului ce se doreşte poziţionat pe un nivel cât mai de sus.
Date de ieşire
Exemple:
2.3 ELHC
Problema 3 - ELHC 100 de puncte
După şase ani de lucru, Charles a terminat de curăţat instalaţiile pentru producerea negrului
de fum din Copşa Mică. Pentru a se ţine departe de mesele de Blackjack, el s-a angajat la CERN,
unde va lucra la noul accelerator de particule numit Even Larger Hadron Collider (ELHC).
ELHC are forma unui tunel circular cu o circumferinţă de P kilometri, P fiind un număr prim.
De-a lungul tunelului sunt plasaţi P senzori numerotaţi de la 0 la P 1, distanţa dintre doi senzori
consecutivi fiind de exact 1 kilometru.
Un experiment efectuat ı̂n ELHC constă ı̂n studierea unei particule de tip G, 1 & G $ P .
Dacă această particulă este ridicată la nivelul de energie k şi este lansată din dreptul senzorului
k
0 ı̂n direct, ia senzorului 1, ea va parcurge exact G kilometri prin tunel şi apoi se va dezintegra,
declanşând ı̂n acel moment senzorul s ı̂n dreptul căruia are loc dezintegrarea particulei.
Se consideră că experimentul are date complete dacă, lansând P 1 particule de tip G ridicate
la toate nivelurile de energie k de la 1 la P 1, este posibil să declanşăm toţi senzorii s numerotaţi
cu valori ı̂ntre 1 şi P 1, adică toţi senzorii din tunel mai puţin senzorul 0.
Cerinţe
Dându-se T perechi de numere G şi P , determinaţi dacă experimentul pentru studierea par-
ticulei de tip G ı̂ntr-un tunel de circumferinţă P produce date complete.
Date de intrare
Fişierul de intrare elhc.in conţine pe prima linie un număr T , reprezentând numărul de ex-
perimente care vor fi efectuate. Pe fiecare din următoarele T linii se află câte două numere G
şi P separate printr-un spaţiu, reprezentând efectuarea unui experiment cu o particulă de tip G
ı̂ntr-un tunel de circumferinţă P .
Date de ieşire
În fişierul de ieşire elhc.out se va afla o singură linie cu T biţi scrişi unul după altul, adică
fără spaţii ı̂ntre ei. Al i-lea bit este 1 dacă pentru cel de-al i-lea experiment putem obţine date
complete, şi 0 ı̂n caz contrar.
CAPITOLUL 2. ONI 2021 2.3. ELHC 17
Exemple:
Propunători:
Ştefania Ionescu, University of Zurich,
Alexandru Petrescu, University of Oxford (Keble College),
Vlad Gavrilă, University of Cambridge (Churchill College)
Autori editorial: Vlad Gavrilă, Ştefan Manolache
Soluţie parţială - 7 puncte, respectiv 21 de puncte
Prima observaţ ie necesară este că o particulă G lansată la nivelul de energie k ı̂ntr-un tunel
k
de circumferinţă P va activa senzorul G moduloP .
Astfel, prima abordare este să trecem prin puterile lui GmoduloP şi să marcăm ı̂ntr-un vector
de apariţie care dintre senzorii 1, 2, ..., P 1 au fost activaţi.
2
Putem face asta calculând fiecare putere individual, liniar, ı̂n O T P , pentru a rezolva
problema pentru 7 puncte, sau să calculăm ı̂n O T P toate puterile, pentru a rezolva problema
pentru 21 de puncte.
Soluţie parţială - 74 puncte
A doua observaţie importantă este că valorile senzorilor pe care ı̂i activăm sunt periodice.
k1
Dacă G 1 moduloP , atunci G
k
G, Gk2 G2, etc. Aşadar, odată ce activăm senzorul 1,
incrementând nivelul k de energie, suntem siguri că am activat toţi senzorii pe care puteam să-i
activăm.
P 1
Mai mult, avem garanţia că G 1moduloP , folosind Mica Teoremă a lui Fermat. Astfel,
P 1
dacă G este prima putere a lui G egală cu 1moduloP , ı̂nseamnă că până atunci nu am ı̂ntâlnit
CAPITOLUL 2. ONI 2021 2.3. ELHC 18
2 3 P 1
nicio repetiţie, deci toate valorile 1, 2, ..., P 1 sunt atinse de catre G, G , G , G (nu neapărat
ı̂n ordinea aceasta). În schimb, dacă există o putere a lui G mai mică, egală cu 1moduloP , atunci
nu este posibil să atingem toate numerele dorite, din cauza periodicităţii.
Aşadar, problema noastră se reduce la: un experiment cu o particulă G ı̂ntr-un tunel de
lungime P are date complete G
P 1
este cea mai mică putere a lui G egală cu 1moduloP .
A treia observaţie de care avem nevoie este că, dacă k este cel mai mic exponent pentru care
G 1 moduloP , atunci k ¶P 1. Această afirmaţie se poate demostra uşor prin reducere la
k
absurd: dacă k este cel mai mic exponent pentru care G 1 moduloP , dar k j P 1, atunci
k
restul r al ı̂mpărţirii lui P 1 la k ar fi un exponent mai mic decât k pentru care G 1moduloP ,
r
de exponenţiere rapidă, putem obţine puterile lui G corespunzătoare divizorilor lui P 1 ı̂n
O log P D P 1, (unde D nÓ este numărul divizorilor lui n, care ı̂n medie este log N şi
niciodată nu este mai mare de 2 n). Ó
Astfel, complexitatea finală a soluţiei pentru 74 de puncte este O T P log P D P 1.
Soluţie completă
Pornind de la soluţia precedentă, observăm că este suficient să verificăm doar puterile
P 1 P 1 P 1
p1 K1 , px K2 , ... p Kx ,
x
Cum cel puţin unul dintre numerele K1 , K2 , ..., Kx este un multiplu al lui k, obţinem faptul
că pentru a confirma existenţa lui k este suficient să verificăm doar dacă există un y pentru care
G y 1moduloP .
K
Deoarece numărul de factori primi al unui număr creşte asimptotic cu O logloglogP P , obţinem
Ó log P
2
complexitatea finală O T P log log P
(păstrând acelaşi algoritm de factorizare a lui P , dar
şi exponenţierea rapidă pentru obţinerea puterilor lui K din soluţia precedentă).
Altă optimizare, nenecesară pentru obţinerea punctajului maxim, este să folosim Ciurul lui
Eratostene pentru a precalcula toate numerele prime mai mici decât valoarea maximă a lui P ,
pentru a optimiza obţinerea divizorilor fiecărui P din fişierul de intrare.
19
Capitolul 4
ONI 2019
4.1 amat
Problema 1 - amat 100 de puncte
Pasionat de informatică şi de puzzle-uri, Dorel a construit o matrice A de dimensiunea N M
lipind mai multe piese dreptunghiulare de diferite dimensiuni. Fiecare piesă este compusă din
elemente de dimensiunea 1 1 şi reţin o aceeaşi valoare (vezi exemplele). Matricea rezultată nu
are spaţii libere, iar piesele din care este compusă nu se suprapun. Nu există două piese cu aceeaşi
valoare.
Deşi iniţial părea că acest design este unul inedit, nu a durat mult până când Dorel s-a plictisit.
Astfel, acum el doreşte să ”upgradeze” matricea construită. Dorel alege o submatrice delimitată
de coordonatele x1, y1 - colţul stânga-sus, x2, y2 - colţul dreapta-jos (1 & x1 & x2 & N ,
1 & y1 & y2 & M ), unde creşte toate valorile elementelor submatricei cu valoarea V .
Dorel efectuează ı̂n ordine Q operaţii de upgrade, operaţii numerotate de la 1 la Q. La
finalizarea celor Q operaţii de upgrade, toate elementele din matrice au valoarea mai mare sau
egală cu K. După o operaţie de upgrade, structura iniţială a matricei se modifică.
Cerinţe
Cum priceperea lui Dorel este proverbială, trebuie să ı̂l ajutaţi ı̂n rezolvarea următoarelor
cerinţe:
1) determinarea coordonatelor piesei cu număr maxim de elemente ı̂nainte ca Dorel să efectueze
operaţiile de upgrade;
2) determinarea numărului minim de operaţii de upgrade după care toate elementele matricei
au valoarea mai mare sau egală cu K.
Date de intrare
Datele de intrare se citesc din fişierul amat.in, care are următoarea structură:
a pe prima linie se află numărul natural C, care poate fi egal cu 1 sau 2, ı̂n funcţie de cerinţa
ce trebuie rezolvată;
a pe linia următoare se află două numerele naturale N şi M cu semnificaţia din enunţ;
a pe următoarele N linii se găsesc elementele matricei A.
a dacă C 2 atunci fişierul de intrare mai conţine:
- pe linia N 2 numerele naturale Q K cu semnificaţiile din enunţ;
- pe următoarele Q linii descrierea submatricelor asupra cărora se efectuează operaţii de up-
grade de forma: x1 y1 x2 y2 V
Date de ieşire
20
CAPITOLUL 4. ONI 2019 4.1. AMAT 21
Exemple
#define x1 first.first
#define y1 first.second
#define x2 second.first
#define y2 second.second.first
ifstream fin("amat.in");
ofstream fout("amat.out");
int qq,k,n,m;
int main()
{
int optiune;
fin >> optiune >> n >> m;
CAPITOLUL 4. ONI 2019 4.1. AMAT 23
int st = 1;
int dr = qq;
int rasp = 0;
while(st <= dr)
{
int mij = (st + dr) / 2;
int ok = 1;
for(int i = 1; i <= mij; i++)
dp[q[i].x1][ q[i].y1 ] += q[i].val,
dp[q[i].x2 + 1][ q[i].y2 + 1] += q[i].val,
dp[q[i].x1][ q[i].y2 + 1 ] -= q[i].val,
dp[q[i].x2 + 1][ q[i].y1] -= q[i].val;
if(ok)
rasp = mij, dr = mij - 1;
else
st = mij + 1;
}
#define x1 first.first
#define y1 first.second
#define x2 second.first
#define y2 second.second.first
ifstream fin("amat.in");
ofstream fout("amat.out");
int qq,k,n,m;
int main()
{
int optiune;
fin >> optiune >> n >> m;
CAPITOLUL 4. ONI 2019 4.1. AMAT 24
int st = 1;
int dr = qq;
int rasp = 0;
while(st <= dr)
{
int mij = (st + dr) / 2;
int ok = 1;
for(int i = 1; i <= mij; i++)
for(int j = q[i].x1; j <= q[i].x2; j++)
dp[j][ q[i].y1 ] += q[i].val,
dp[j][ q[i].y2 + 1] -= q[i].val;
if(ok)
rasp = mij, dr = mij - 1;
else
st = mij + 1;
}
ifstream f("amat.in");
ofstream g("amat.out");
struct submatrix
{
int x1, y1, x2, y2;
int k;
} ap[2003];
struct qry
{
int x1, y1, x2, y2, w;
} Q[100001];
memset(A, 0, sizeof(A));
CAPITOLUL 4. ONI 2019 4.1. AMAT 25
return 1;
}
int main()
{
int i, j, x, x1, y1, x2, y2;
if (c == 1)
{
for(i = 0; i <= 2000; ++i)
ap[i].y1 = ap[i].x1 = 2003;
int Max_arie = 0;
for(i = 0; i <= 2000; ++i)
if (ap[i].k > Max_arie)
{
Max_arie = ap[i].k;
x1 = ap[i].x1;
x2 = ap[i].x2;
y1 = ap[i].y1;
y2 = ap[i].y2;
}
else
if (ap[i].k == Max_arie)
{
if (ap[i].x1 > x1 || ap[i].x1 == x1 && ap[i].y1 > y1)
{
x1 = ap[i].x1;
x2 = ap[i].x2;
y1 = ap[i].y1;
y2 = ap[i].y2;
}
}
g << x1 << " " << y1 << " " << x2 << " " << y2 << "\n";
}
else
{
CAPITOLUL 4. ONI 2019 4.1. AMAT 26
f >> q >> K;
for(i = 1; i <= q; ++i)
{
f >> x1 >> y1 >> x2 >> y2 >> x;
Q[i] = {x1, y1, x2, y2, x};
}
i = 1, j = q;
while (i <= j)
{
int mij = (i+j) >> 1;
int k = verif(mij);
if (k)
j = mij - 1;
else
i = mij + 1;
}
return 0;
}
#define x1 first.first
#define y1 first.second
#define x2 second.first
#define y2 second.second.first
ifstream fin("amat.in");
ofstream fout("amat.out");
int qq,k,n,m;
int main()
{
int optiune;
fin >> optiune >> n >> m;
int rasp = 0;
for(int mij = 1; mij <= qq; mij++)
{
int ok = 1;
if(init[i][j] < k)
ok = 0;
}
if(ok)
{
fout << mij << ’\n’;
return 0;
}
}
return 0;
}
#define x1 first.first
#define y1 first.second
#define x2 second.first
#define y2 second.second.first
ifstream fin("amat.in");
ofstream fout("amat.out");
int qq,k,n,m;
int bad;
int main()
{
int optiune;
fin >> optiune >> n >> m;
int rasp = 0;
for(int mij = 1; mij <= qq; mij++)
{
for(int i = q[mij].x1; i <= q[mij].x2; i++)
for(int j = q[mij].y1; j <= q[mij].y2; j++)
{
if(init[i][j] < k && init[i][j] + q[mij].val >= k )
bad--;
init[i][j] += q[mij].val;
}
if(bad == 0)
{
fout << mij << ’\n’;
return 0;
}
CAPITOLUL 4. ONI 2019 4.1. AMAT 28
return 0;
}
#include <bits/stdc++.h>
ifstream fin("amat.in");
ofstream fout("amat.out");
struct query
{
int x1, y1, x2, y2, v;
};
struct subtaskAnswer
{
int x1, y1, x2, y2;
};
int n, m, i, j, q, k, c;
int mat[NMAX + 5][NMAX + 5], aux[NMAX + 5][NMAX + 5];
query queries[100000 + 5];
void DEBUG_endmatrix()
{
for (i = 1 ; i <= n ; i++)
{
for (j = 1 ; j <= m ; j++)
{
cout << aux[i][j] << ’ ’;
}
cout << ’\n’;
}
}
void subtask()
{
int crt, cnt, maxCnt = 0, i, j, lin, col;
subtaskAnswer subAns;
crt = mat[i][j];
cnt = 0;
for (lin = i ; lin <= n ; lin++)
{
if (mat[lin][j] != crt) break;
for (col = j ; col <= m ; col++)
{
if (mat[lin][col] != crt) break;
aux[lin][col] = 1;
cnt++;
}
}
subAns.x2 = lin - 1;
subAns.y2 = col - 1;
maxCnt = cnt;
}
}
}
/**
cout << nrQ << ’\n’;
DEBUG_endmatrix();
cout << "-----------------\n";
*/
return 1;
}
void maintask()
{
int ls = 1, ld = q, mij, best = q;
if (solve(mij))
{
best = mij;
ld = mij - 1;
}
else
{
ls = mij + 1;
}
}
int main()
{
fin >> c;
fin >> n >> m;
assert(c == 1 || c == 2);
assert(2 <= n && n <= NMAX);
CAPITOLUL 4. ONI 2019 4.1. AMAT 30
if (c == 1)
{
subtask();
}
else
{
fin >> q >> k;
assert(VALMIN <= k && k <= VALMAX);
assert(1 < q && q <= 100000);
maintask();
}
return 0;
}
int main()
{
ifstream cin("amat.in");
ofstream cout("amat.out");
if (task == 1)
{
const int kMax = 5000;
vector<int> lt(kMax, -1), rt(kMax, -1), up(kMax, -1), dw(kMax, -1);
}
else
{
int q, k; cin >> q >> k;
int sol = 0;
for (int step = 1, adv = 1; step; adv ? step *= 2 : step /= 2)
{
if (sol + step > q)
{
adv = 0;
}
else
{
vector<vector<int>> mars(n + 1, vector<int>(m + 1, 0));
{
adv = 0;
continue;
}
sol += step;
}
}
return 0;
}
ifstream fin("amat.in");
ofstream fout("amat.out");
struct query
{
int x1, y1, x2, y2, v;
};
struct subtaskAnswer
{
int x1, y1, x2, y2;
};
int n, m, i, j, q, k, c;
int mat[NMAX + 5][NMAX + 5], aux[NMAX + 5][NMAX + 5];
query queries[100000 + 5];
void DEBUG_endmatrix()
{
for (i = 1 ; i <= n ; i++)
{
for (j = 1 ; j <= m ; j++)
{
cout << aux[i][j] << ’ ’;
}
cout << ’\n’;
}
}
void subtask()
{
int crt, cnt, maxCnt, i, j, lin, col;
subtaskAnswer subAns;
crt = mat[i][j];
CAPITOLUL 4. ONI 2019 4.1. AMAT 33
cnt = 0;
for (lin = i ; lin <= n ; lin++)
{
if (mat[lin][j] != crt) break;
for (col = j ; col <= m ; col++)
{
if (mat[lin][col] != crt) break;
aux[lin][col] = 1;
cnt++;
}
}
maxCnt = cnt;
}
}
}
/**
cout << nrQ << ’\n’;
DEBUG_endmatrix();
cout << "-----------------\n";
*/
return 1;
}
void maintask()
{
int ls = 1, ld = q, mij, best = q;
while (ls <= ld)
{
mij = (ls + ld) / 2;
if (solve(mij))
{
best = mij;
ld = mij - 1;
}
CAPITOLUL 4. ONI 2019 4.2. COMUN 34
else
{
ls = mij + 1;
}
}
int main()
{
fin >> c;
fin >> n >> m;
assert(c == 1 || c == 2);
assert(2 <= n && n <= NMAX);
assert(2 <= m && m <= NMAX);
if (c == 1)
{
subtask();
}
else
{
fin >> q >> k;
assert(VALMIN <= k && k <= VALMAX);
assert(1 < q && q <= 100000);
maintask();
}
return 0;
}
4.2 Comun
Problema 2 - Comun 100 de puncte
Tocmai ai primit un şir v de K numere naturale nenule distincte. Plecând de la acest şir, te-ai
gândit să construieşti un şir w de N numere naturale distincte, astfel ı̂ncât un număr x este ı̂n
şirul w dacă şi numai dacă exista iniţial ı̂n şirul v sau se pot alege cel puţin două numere din şirul
v astfel ı̂ncât x este cel mai mare divizor comun al acelor numere.
De exemplu, dacă v r4, 6, 7x atunci w r1, 2, 4, 6, 7x.
Uimit de proprietăţile matematice frumoase ale noului şir w, ai uitat din păcate şirul original
v de la care ai pornit.
CAPITOLUL 4. ONI 2019 4.2. COMUN 35
Cerinţe
Dându-se şirul w, să se găsească un şir posibil iniţial v având un număr minim de elemente.
Date de intrare
Fişierul de intrare comun.in conţine pe prima linie un număr natural N . Pe cea de-a doua
linie se află N numere naturale nenule distincte, ı̂n ordine strict crescătoare, reprezentând
şirul w.
Date de ieşire
Fişierul de ieşire comun.out va conţine pe prima linie numărul minim K de elemente ale
şirului v. Pe cea de-a doua linie se vor afla K numere naturale distincte, ı̂n ordine strict
crescătoare, reprezentând şirul propriu-zis.
a Toate valorile din fişierul de intrare sunt numere naturale nenule mai mici sau egale cu
100000.
a Pentru teste ı̂n valoare de 15 puncte, toate valorile din fişierul de intrare sunt mai mici sau
egale cu 20.
a Pentru teste ı̂n valoare de 50 de puncte, toate valorile din fişierul de intrare sunt mai mici
sau egale cu 2000.
a Se garantează că există măcar o soluţie.
a Dacă există mai multe şiruri iniţiale cu număr minim de elemente, oricare este acceptat.
Exemple
Soluţie 15p
Există o multitudine de soluţii care se ı̂ncadrează ı̂n aceste limite. ı̂n continuare va fi prezentată
una dintre posibilele soluţii.
O primă observaţie este că cel mai mare număr se regăseşte mereu ı̂n şir, deoarece nu se poate
obţine aplicând c.m.m.d.c. asupra altor numere. Mai mult, dacă un număr din w este cel mai
mare divizor comun al altor numere din w, nu are sens să ı̂l includem ı̂n soluţie, deoarece orice
număr pe care l-ar putea genera x ı̂l putem genera, ı̂n schimb, cu numerele care ı̂l generează.
Din aceste considerente, se poate deduce că soluţia de lungime minimă este unică. Un algoritm
pentru a o calcula este următorul: dacă există un număr x care este c.m.m.d.c. al altor numere
ı̂ncă neeliminate, ı̂l eliminăm. Algoritmul se va opri atunci când nu se mai pot elimina numere.
N
O implementare naivă a acestui algoritm are complexitate O 2 N şi ar trebui să obţină
minimum 15 puncte. ı̂n continuare vom optimiza algoritmul pentru a rula mai rapid.
Soluţie 50p
Observaţia cheie este că, ı̂n cadrul raţionamentului de mai sus, este suficient să considerăm
doar perechi de câte două numere.
CAPITOLUL 4. ONI 2019 4.2. COMUN 36
Un algoritm este următorul: pentru fiecare pereche x, y de numere distincte din şirul de
intrare, calculăm d cmmdc x, y şi ı̂l ştergem din şir. ştergerea se poate face folosind un vector
caracteristic.
2
O astfel de soluţie are complexitate O N log V (unde V este valoarea maximă din şirul de
intrare) şi ar trebui să obţină minimum 50 de puncte.
Soluţie 100p
O altă modalitate de a optimiza algoritmul menţionat anterior este că, pentru a ne decide
dacă un număr trebuie eliminat sau nu, este de ajuns să calculăm cel mai mare divizor comun al
multiplilor săi din şir.
În acest caz, putem menţine şirul ı̂ntr-un vector caracteristic şi să verificăm fiecare număr
folosind un algoritm foarte asemănător ciurului lui Eratostene.
2
Această soluţie, deşi la prima vedere are complexitate O N V log V , se poate demonstra
că complexitatea este de fapt O N V log V . Demonstraţia este lăsată ca exerciţiu. O astfel
de soluţie ar trebui să obţină punctajul maxim.
Soluţie alternativă
Se poate demonstra că, ı̂n loc să considerăm toţi multiplii unui număr x, putem considera doar
perechile de multipli ı̂n care unul dintre cele două este cel mai mic multiplu al lui x din şir.
În acest caz, complexitatea soluţiei este tot O N V log V , din aceleaşi considerente ca şi
soluţia precedentă.
int main()
{
ifstream cin("comun.in");
ofstream cout("comun.out");
int n;
cin >> n;
vector<int> v(n);
int maxx = 0;
for (int i = 0; i < n; ++i)
{
cin >> v[i];
maxx = max(maxx, v[i]);
}
long long ap = 0;
for (auto x : v)
ap |= (1LL << (x - 1));
vector<int> ans;
return 0;
}
ifstream fin("comun.in");
ofstream fout("comun.out");
int main()
{
fin >> n;
for (i = 1 ; i <= n ; i++)
{
fin >> x;
v[x] = 1;
valmax = max(valmax, x);
}
g_c_d = 0;
if (g_c_d == i)
v[i] = 0, n--;
}
return 0;
}
int n;
int a[N + 5], ap[N + 5];
vector <int> v;
int main()
{
freopen("comun.in", "r", stdin);
freopen("comun.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n ; ++i)
scanf("%d", &a[i]), ++ap[a[i]]; ///citesc sirul si vad ce numere
///apar in el
printf("%d\n", v.size());
for(auto it : v)
printf("%d ", it);
return 0;
}
while (b)
{
int r = a % b;
a = b;
b = r;
}
return a;
}
int main()
{
ifstream cin("comun.in");
ofstream cout("comun.out");
vector<int> v(n);
int maxx = 0;
for (auto& x : v)
{
cin >> x;
maxx = max(maxx, x);
}
v.clear();
for (auto x : v)
cout << x << " ";
cout << endl;
return 0;
}
int main()
{
ifstream cin("comun.in");
ofstream cout("comun.out");
int n;
cin >> n;
v = Sparsify(v);
vector<int> out;
return 0;
}
if (all_gcd == i)
vals[i] = false;
}
return vals;
}
int main()
{
ifstream cin("comun.in");
ofstream cout("comun.out");
int n;
cin >> n;
v = Sparsify(v);
vector<int> out;
return 0;
}
if (all_gcd == i)
vals[i] = false;
}
return vals;
}
int main()
{
ifstream cin("comun.in");
CAPITOLUL 4. ONI 2019 4.3. PRO3 42
ofstream cout("comun.out");
int n;
cin >> n;
vector<bool> v(100001, false);
v = Sparsify(v);
vector<int> out;
for (int i = 0; i < (int)v.size(); ++i)
if (v[i])
out.push_back(i);
return 0;
}
4.3 pro3
Problema 3 - pro3 100 de puncte
Se consideră 3 progresii aritmetice de numere naturale nenule.
Notăm cu Pi , 1 & i & 3, mulţimile formate cu elementele progresiei i.
Fie P P1 < P2 < P3 reuniunea mulţimilor P1 , P2 , P3 .
Cerinţe
Date de intrare
Date de ieşire
Pentru teste ı̂n valoare de 40 puncte, 0 $ ai , ri & 10 şi 0 $ ni & 10 , 1 & i & 3
2 6
a
Pentru teste ı̂n valoare de 72 puncte, 0 $ ai , ri & 10 şi 0 $ ni & 10 , 1 & i & 3
2 9
a
Pentru teste ı̂n valoare de 100 puncte, 0 $ ai , ri & 10 şi 0 $ ni & 10 , 1 & i & 3
6 9
a
Exemple
Soluţie 20p
Se poate utiliza un vector de frecvenţă pentru a determina dacă un număr natural cuprins
ı̂ntre 1 şi cel mai mare element dintre cele 3 progresii face parte din măcar o progresie.
Soluţia necesită un spaţiu de memorie foarte mare şi nu poate fi utilizată pentru valori mari ale
numărului de termeni ale unei progresii, dar va obţine aproximativ 20 30 de puncte, ı̂n funcţie
de diverse optimizări de memorie.
Soluţie 40p
Se poate realiza o interclasare concomitentă a celor 3 şiruri, fără a mai construi un vector cu
rezultatele reuniuni. La fiecare pas al interclasării se va determina elementul minim al celor trei
progresii şi se va incrementa indicele corespunzător.
După ce unul dintre cele 2 şiruri se va termina” se va testa care dintre şiruri s-a terminat şi se
va acţiona asemănător pe două şiruri apoi pe un singur şir.
Această soluţie are complexitate O n1 n2 n3 şi ar trebui să obţină minimum 40 de puncte.
Soluţie 72p
Observaţia cheie este că elementele comune a două sau mai multe progresii aritmetice, dacă
există, formează tot o progresie aritmetică.
Astfel, observăm că este mai uşor şi mai eficient să calculăm elementele comune a două/trei
progresii şi să folosim principiul includerii şi excluderii pentru a calcula răspunsul.
Deoarece ı̂n acest caz termenii iniţiali şi raţiile progresiilor sunt cel mult egale cu 100, se pot
găsi primii doi termeni ai progresiei comune (şi, implicit, raţia acesteia), verificând pe rând valori
candidat, ı̂n ordine crescătoare. Dacă există termeni comuni, se poate demonstra că primul dintre
ei şi raţia nu pot depăşi 2 1003.
3 3
Această soluţie are complexitate O r1 r2 r3 a1 a2 a3 şi ar trebui să obţină
minimum 72 de puncte.
Soluţie 100p
ı̂n soluţia anterioară ne-am bazat pe faptul că primul termen şi raţia progresiei comune a două
progresii a1 Xr1 şi a2 Xr2 sunt mai mici sau egale cu a1 r1 a2 r2 . ı̂n loc să verificăm
fiecare candidat pe rând, vom itera doar prin elementele progresiei 1. Astfel, numărul de paşi este
O a1 r1 a2 r2 © a1 r1 O a2 r2 . Este esenţial că această complexitate nu depinde
de parametrii progresiei 1, astfel că intersecţia celor trei progresii se poate calcula intersectând
progresiv progresia 1 cu progresia 2 şi rezultatul cu progresia 3.
Complexitatea acestei soluţii este O r1 r2 r3 a1 a2 a3 şi ar trebui să obţină 100 de
puncte.
ifstream fin("pro3.in");
ofstream fout("pro3.out");
return 0;
}
int main()
{
for(int i = 1; i <= 3; i++)
fin >> a[i] >> r[i] >> n[i];
fout << n[1] + n[2] + n[3] - inter2(1, 2) - inter2(1, 3) -
inter2(2, 3) + inter3() << ’\n’;
return 0;
}
#include <fstream>
int main()
{
ifstream f("pro3.in");
ofstream g("pro3.out");
//-------------------------------------------------------------------
// determin cate elemente sunt comune sirurilor 1 si 2
// ecuatia r1*x - r2*y = (a2 - r2) - (a1 - r1)
// ecuatia are solutii daca cmmdc(r1,r2) divide (a2 - r2) - (a1 - r1)
d=cmmdc(r1,r2);
t = (a2 - r2) - (a1 - r1);
if (t%d==0) // ecuatia are solutii
{
//determin primul element comun celor doua siruri
i=1;j=1;x0=0;y0=0;
while (i<=n1 && j<=n2)
{
if (a1+r1*(i-1) < a2+r2*(j-1)) i++;
if (a1+r1*(i-1) > a2+r2*(j-1)) j++;
if (a1+r1*(i-1) == a2+r2*(j-1)) {x0 = i; y0 = j; break;}
}
//-------------------------------------------------------------------
// determin cate elemente sunt comune sirurilor 1 si 3
// ecuatia r1*x - r3*y = (a3 - r3) - (a1 - r1)
// ecuatia are solutii daca cmmdc(r1,r3) divide (a3 - r3) - (a1 - r1)
d=cmmdc(r1,r3);
t = (a3 - r3) - (a1 - r1);
if (t%d==0) // ecuatia are solutii
{
//determin primul element comun celor doua siruri
i=1;j=1;x0=0;y0=0;
while (i<=n1 && j<=n3)
{
if (a1+r1*(i-1) < a3+r3*(j-1)) i++;
if (a1+r1*(i-1) > a3+r3*(j-1)) j++;
if (a1+r1*(i-1) == a3+r3*(j-1)) {x0 = i; y0 = j; break;}
}
if (x0 == 0) {a13 = 0; r13 = 0; n13 = 0;}
else
{
a13 = a1 + r1*(x0 - 1);
r13 = (r1*r3)/d;
if (an1 < an3)
n13 = (an1 - a13)/r13 + 1;
else
n13 = (an3 - a13)/r13 + 1;
//-------------------------------------------------------------------
// determin cate elemente sunt comune sirurilor 2 si 3
// ecuatia r2*x - r3*y = (a3 - r3) - (a2 - r2)
// ecuatia are solutii daca cmmdc(r2,r3) divide (a3 - r3) - (a2 - r2)
d=cmmdc(r2,r3);
t = (a3 - r3) - (a2 - r2);
if (t%d==0) // ecuatia are solutii
{
//determin primul element comun celor doua siruri
i=1;j=1;x0=0;y0=0;
while (i<=n2 && j<=n3)
{
if (a2+r2*(i-1) < a3+r3*(j-1)) i++;
if (a2+r2*(i-1) > a3+r3*(j-1)) j++;
if (a2+r2*(i-1) == a3+r3*(j-1)) {x0 = i; y0 = j;break;}
}
if (x0 == 0) {a23 = 0; r23 = 0; n23 = 0;}
else
{
a23 = a2 + r2*(x0 - 1);
r23 = (r2*r3)/d;
if (an2 < an3)
n23 = (an2 - a23)/r23 + 1;
else
n23 = (an3 - a23)/r23 + 1;
}
}
else // ecuatia nu are solutii
{a23 = 0;r23 = 0;n23 = 0;}
//---------------------------------------------------
// determin cate elemente comune au cele 3 siruri
if (n12 && n23 && n13)
{
// prima progresie este a12,r12,n12 si a doua este a3,r3,n3
d=cmmdc(r12,r3);
t = (a3 - r3) - (a12 - r12);
// aplic PINEX
//g<< " ------------------------------------------"<<"\n";
f.close();
g.close();
return 0;
}
#include <fstream>
#define int long long
struct pro
{
long long a,r,n,an;
};
d=i;
i=r;
r=d%i;
}
return i;
}
pro z;
d=cmmdc(x.r,y.r);
t = (y.a - y.r) - (x.a - x.r);
if (t%d==0) // ecuatia are solutii
{
//determin primul element comun celor doua siruri
i=1;j=1;x0=0;
while (i<=x.n && j<=y.n)
{
if (x.a+x.r*(i-1) < y.a+y.r*(j-1)) i++;
if (x.a+x.r*(i-1) > y.a+y.r*(j-1)) j++;
if (x.a+x.r*(i-1) == y.a+y.r*(j-1)) {x0 = i; break;}
}
x.an=x.a+x.r*(x.n-1);
y.an=y.a+y.r*(y.n-1);
return z;
}
int32_t main()
{
ifstream f("pro3.in");
ofstream g("pro3.out");
pro x,y,z,xy,xz,yz,xyz;
xy = progresie(x,y);
xz = progresie(x,z);
yz = progresie(y,z);
xyz = progresie(xy,z);
// aplic PINEX
g << x.n + y.n + z.n - xy.n - xz.n - yz.n + xyz.n;
f.close();
g.close();
CAPITOLUL 4. ONI 2019 4.3. PRO3 49
return 0;
}
#include <bits/stdc++.h>
struct Prog
{
long long a, r, n;
};
int main()
{
ifstream cin("pro3.in");
ofstream cout("pro3.out");
CAPITOLUL 4. ONI 2019 4.4. CUB 50
return 0;
}
4.4 Cub
Problema 4 - Cub 100 de puncte
Ionel are de rezolvat o nouă problemă. El trebuie să construiască un şir de N numere naturale.
Numerele din şir pot avea ca divizori primi doar numere prime de o cifră. După construirea şirului,
Ionel a constatat că există subsecvenţe ı̂n şir pentru care produsul elementelor este cubul unui
număr natural.
Cerinţe
Ionel vrea să determine numărul subsecvenţelor din şirul construit care au produsul elementelor
o valoare ce este cubul unui număr natural.
Date de intrare
Fişierul de intrare cub.in va conţine pe prima linie numărul natural N , iar pe linia următoare
se vor afla N numere naturale separate prin câte un spaţiu, elementele şirului construit de Ionel.
Date de ieşire
Fişierul de ieşire cub.out va conţine pe prima linie un număr natural reprezentând numărul
subsecvenţelor din şirul construit care au produsul elementelor egal cu o valoare ce este cubul unui
număr natural.
Restricţii şi precizări
a N şi elemente şirului sunt numere naturale din intervalul 2, 1000000
a Prin subsecvenţă a unui şir se ı̂nţelege o succesiune de unul sau mai mulţi termeni din şir
aflaţi pe poziţii consecutive.
a Pentru teste ı̂n valoare de 20 de puncte, N & 1000.
a Pentru teste ı̂n valoare de 40 de puncte, N & 10000.
Exemple
cub.in cub.out Explicaţii
8 6 Sunt 6 subsecvenţe ı̂n şir cu produsul elementelor egal cu o val-
15 3 5 15 7 63 21 125 oare care este cubul unui număr natural:
15 3 5 15
7 63 21
125
15 3 5 15 7 63 21
7 63 21 125
15 3 5 15 7 63 21 125
Timp maxim de executare/test: Windows: 0.6 secunde, Linux: 0.3 secunde
Memorie: total 64 MB
Dimensiune maximă a sursei: 10 KB
CAPITOLUL 4. ONI 2019 4.4. CUB 51
Vom calcula exponenţii valorilor 2, 3, 5 şi 7 din descompunerea ı̂n factori primi a fiecărui
număr din şir, precum şi exponenţii cumulaţi crescător. ı̂n funcţie de restul ı̂mpărţirii la 3 al
sumei exponenţilor cumulaţi se atribuie un cod de la 0 la 80 pentru fiecare suma cumulată până
la poziţia curentă din şir. Două poziţii cu acelaşi cod determină o subsecvenţă cu proprietatea
cerută, deci se vor număra pentru fiecare cod câte perechi de poziţii cu acelaşi cod există. Pentru
fiecare k poziţii cu acelaşi cod, numărul subsecvenţelor existente va avea valoarea k k 1©2,
fapt uşor de demonstrat. Pentru punctaj maxim, se utilizează un algoritm de complexitate O n.
ifstream fin("cub.in");
ofstream fout("cub.out");
int convert()
{
return crtState[3] * 3 * 3 * 3 + crtState[2] * 3 * 3 +
crtState[1] * 3 + crtState[0];
}
int main()
{
fin >> n;
for (i = 1 ; i <= n ; i++)
{
fin >> x;
crtState[j] %= 3;
}
states[convert()]++;
}
states[0]++;
return 0;
}
ifstream fin("cub.in");
ofstream fout("cub.out");
int convert()
{
return crtState[3] * 3 * 3 * 3 + crtState[2] * 3 * 3 +
crtState[1] * 3 + crtState[0];
}
int main()
{
fin >> n;
for (i = 1 ; i <= n ; i++)
{
fin >> x;
crtState[j] %= 3;
}
v[i] = convert();
}
return 0;
}
ifstream fin("cub.in");
ofstream fout("cub.out");
int main()
{
fin >> n;
for (i = 1 ; i <= n ; i++)
{
fin >> x;
{
nr++;
x /= divs[j];
state[j][i]++;
}
state[j][i] %= 3;
}
}
return 0;
}
ifstream f("cub.in");
ofstream g("cub.out");
int main()
{
d[0]=1;
i=0;
while(d[i]<=1000000)
{
i++;
d[i]=d[i-1]*2;
}
i--;
t[0]=1;
j=0;
while(t[j]<=1000000)
{
j++;
t[j]=t[j-1]*3;
}
CAPITOLUL 4. ONI 2019 4.4. CUB 54
j--;
c[0]=1;
k=0;
while(c[k]<=1000000)
{
k++;
c[k]=c[k-1]*5;
}
k--;
s[0]=1;r=0;
while(s[r]<=1000000)
{
r++;
s[r]=s[r-1]*7;
}
r--;
//g<<"i2="<<i<<" i3="<<j<<" i5="<<k<<" i7="<<r<<"\n"; //19 12 8 7
// calculez numerele mai mici decat 1000000 ce sunt divizibile
// doar cu 2, 3 ,5 sau 7
for(i1=0;i1<=i;i1++)
for(j1=0;j1<=j;j1++)
for(k1=0;k1<=k;k1++)
for(r1=0;r1<=r;r1++)
{
p=d[i1]*t[j1]*c[k1]*s[r1];
if(p<=1000000)
{
ex2[p]=i1;
ex3[p]=j1;
ex5[p]=k1;
ex7[p]=r1;
//w++;
}
}
sol=viz[0];
for(i=0;i<=80;i++)
sol=sol+viz[i]*(viz[i]-1)/2;
g<<sol<<’\n’ ;
g.close();
return 0;
}
ifstream f("cub.in");
ofstream g("cub.out");
int e2,e3,e5,e7,v[1000002];
int cod,viz[82],x;
CAPITOLUL 4. ONI 2019 4.4. CUB 55
int n,i,j,k,r,i1,j1,k1,r1,d[22],t[15],c[11],s[10];
unsigned long long p,sol;
int ex2[1000002],ex3[1000002],ex5[1000002],ex7[1000002];
int main()
{
d[0]=1;
i=0;
while(d[i]<=1000000)
{
i++;
d[i]=d[i-1]*2;
}
i--;
t[0]=1;
j=0;
while(t[j]<=1000000)
{
j++;
t[j]=t[j-1]*3;
}
j--;
c[0]=1;
k=0;
while(c[k]<=1000000)
{
k++;
c[k]=c[k-1]*5;
}
k--;
s[0]=1;
r=0;
while(s[r]<=1000000)
{
r++;
s[r]=s[r-1]*7;
}
r--;
//g<<"i2="<<i<<" i3="<<j<<" i5="<<k<<" i7="<<r<<"\n"; //19 12 8 7
// calculez numerele mai mici decat 1000000 ce sunt divizibile
// doar cu 2, 3 ,5 sau 7
for(i1=0;i1<=i;i1++)
for(j1=0;j1<=j;j1++)
for(k1=0;k1<=k;k1++)
for(r1=0;r1<=r;r1++)
{
p=1ll*d[i1]*t[j1]*c[k1]*s[r1];
if(p<=1000000)
{
ex2[p]=i1;
ex3[p]=j1;
ex5[p]=k1;
ex7[p]=r1;
//w++;
}
}
//g<<"nr numere ="<<w<<"\n";// 1273
// citesc numerele si formez exponentii cumulati
// ai lui 2, 3 ,5 si 7
for(i=1;i<=n;i++)
{
x=v[i]; ;
e2=(e2+ex2[x])%3;
e3=(e3+ex3[x])%3;
e5=(e5+ex5[x])%3;
e7=(e7+ex7[x])%3;
cod=e2*27+e3*9+e5*3+e7;//scriu in baza 3
CAPITOLUL 4. ONI 2019 4.5. FIBOFRAC 56
viz[cod]++;
}
sol=viz[0];
for(i=0;i<=80;i++)
sol=sol+viz[i]*(viz[i]-1)/2;
g<<sol<<’\n’ ;
g.close();
return 0;
}
4.5 fibofrac
Problema 5 - fibofrac 100de puncte
Fie şirul Fibonacci dat prin F1 1, F2 1 şi relaţia de recurenţă Fk Fk1 Fk2 , k ' 3.
Se consideră un număr natural N .
Cerinţe
Să se scrie un program care determină numărul F al fracţiilor diferite ireductibile subunitare,
ce se pot forma utilizând primii N termeni ai şirului Fibonacci.
Date de intrare
Date de ieşire
Fişierul de ieşire fibofrac.out va conţine pe prima linie numărul F ,cu semnificaţia de mai sus.
Exemple
Soluţia 1
Generăm termenii şirului Fibonacci şi formăm toate perechile posibile F a©F b cu F a $ F b.
Pentru fiecare pereche calculăm F a, F b şi contorizăm pentru acele perechi care au (Fa, Fb) = 1.
Deoarece termenii şirului Fibonacci cresc repede se obţin erori pentru N % 80. Soluţia are ordin de
2
complexitate O n şi obţine aproximativ 24 puncte. Pentru obţinerea unor puncte suplimentare
s-ar putea lucra cu numere mari.
Soluţia 2
Dacă o fracţie F a©F b este ireductibilă atunci F a, F b este 1. Se cunoaşte ı̂nsă că F a, F b =
F a, b. Cum F a, F b trebuie să fie 1, rezultă că F a, b poate fi F 1 sau F 2, pentru că F 1 1
şi F 2 1. Aşadar contorizăm toate perechile i, j cu i $ j pentru care i, j este 1 sau 2. Soluţia
obţine aproximativ 40 puncte.
Soluţia 3
În plus faţă de consideraţiile de la varianta anterioară, se ştie că numărul de perechi ordonate
x & y care au x, y d şi x & y & M este φ 1 φ 2 φ 3 ... φ M ©d, unde M este un
număr natural iar φ t reprezintă numărul de numere mai mici decât t şi prime cu t (indicatorul
lui Euler).
În cazul nostru d poate fi 1 sau 2, de unde se obţine expresia:
2 φ 1 φ 2 φ 3 ... φ N ©2 φ N ©2 1 φ N ©2 2 ... φ N
Din acest număr trebuie scăzut numărul fracţiile identice cu cele numărate anterior, deoarece
au fost numărate de două ori fracţiile de tipul 1©F a, având ı̂n vedere că primii 2 termeni ai şirului
Fibonacci sunt 1. Mai trebuie deasemenea scăzute şi fracţiile 1©1 şi 2©2 care nu sunt subunitare.
Soluţia obţine 100 puncte şi are complexitate O N log N .
#include <fstream>
int main()
{
long long i,j,n,sol;
ifstream f("fibofrac.in");
ofstream g("fibofrac.out");
f >> n;
fib[1]=1;fib[2]=1;
for(i=3;i<=n;i++)
fib[i]=fib[i-1]+fib[i-2];
sol=0;
CAPITOLUL 4. ONI 2019 4.5. FIBOFRAC 58
for(i=2;i<n;i++)
for(j=i+1;j<=n;j++)
if (cmmdc(fib[i],fib[j])==1) sol++;
g<<sol<<" ";
f.close();
g.close();
return 0;
}
#include <fstream>
#define nmax 1000003
int n,i,j;
ifstream f("fibofrac.in");
ofstream g("fibofrac.out");
int main()
{
f>>n;
// calculez fi(i)
for (i=1;i<=n;i++)
fi[i] = i-1;
fi[1]=1;
for (i=2;i<=n;i++)
for (j=i+i;j<=n;j+=i)
fi[j] -= fi[i];
for(i=1;i<=n/2;i++)
T+=2*fi[i];
for(i=n/2+1;i<=n;i++)
T+=fi[i];
f.close();
g.close();
return 0;
}
#include <fstream>
}
return i;
}
int main()
{
long long i,j,n,sol;
ifstream f("fibofrac.in");
ofstream g("fibofrac.out");
f >> n;
sol=0;
for(i=2;i<n;i++)
for(j=i+1;j<=n;j++)
if (cmmdc(i,j)==1 || cmmdc(i,j)==2) sol++;
g<<sol<<" ";
f.close();
g.close();
return 0;
}
#include <fstream>
#define nmax 1000003
char v[nmax];
int prime[nmax/5],nr=0;
int b[nmax/5],nf=0;
if (f)
{
b[++j]=prime[i];
}
else
CAPITOLUL 4. ONI 2019 4.5. FIBOFRAC 60
i++;
}
nf=j;
}
int main()
{
ifstream f("fibofrac.in");
ofstream g("fibofrac.out");
int n,i,j;
f>>n;
ciur(n);
for(i=1;i<=n/2;i++)
T+=2*fi[i];
for(i=n/2+1;i<=n;i++)
T+=fi[i];
f.close();
g.close();
return 0;
}
#include <fstream>
#include <cassert>
#define nmax 1000003
int n,i,j;
ifstream f("fibofrac.in");
ofstream g("fibofrac.out");
int main()
{
f>>n;
assert(n>0 && n < 10000001);
// calculez fi(i)
for (i=1;i<=n;i++)
fi[i] = i-1;
fi[1]=1;
for (i=2;i<=n;i++)
for (j=i+i;j<=n;j+=i)
fi[j] -= fi[i];
for(i=1;i<=n/2;i++)
T+=2*fi[i];
CAPITOLUL 4. ONI 2019 4.6. TELEFON 61
for(i=n/2+1;i<=n;i++)
T+=fi[i];
f.close();
g.close();
return 0;
}
#include <bits/stdc++.h>
using namespace std;
ifstream fin("fibofrac.in");
ofstream fout("fibofrac.out");
int phi[NMAX], n, i, j;
long long ans;
int main()
{
fin >> n;
for (int i = 1; i <= n; ++i)
phi[i] = i-1;
for (int i = 2; i <= n; ++i)
for (int j = 2 * i; j <= n; j += i)
phi[j] -= phi[i];
ans -= n - 1;
return 0;
}
4.6 telefon
Problema 6 - telefon 100 de puncte
Dorel, plictisit de puzzle-ul pe care l-a upgradat ieri, a decis să meargă afară cu ceilalţi copii.
El ı̂i priveşte pe cei N copii cum joacă telefonul fără fir”.
Jocul decurge ı̂n felul următor:
- Iniţial, copiii se aşază pe axa Ox, copilul i la distanţa Xi metri faţă de origine.
- Copilul cel mai aproape de origine alege un cuvânt secret şi ı̂l transmite celui din dreapta lui;
cel din dreapta lui ı̂l transmite următorului şi aşa mai departe până se ajunge la ultimul copil.
Pentru a transmite cuvântul, fiecare copil trebuie să meargă până la copilul din dreapta lui.
Toţi copiii se deplasează cu viteza constantă de 1 metru/secundă.
Cu toate acestea, pentru a evita deplasarea fiecare copil dispune de un dispozitiv de tip walkie-
talkie ce permite transmiterea unui cuvânt mai departe. Toate staţiile walkie-talkie au o rază de
acţiune R, setată la ı̂nceputul unei runde de joc (exprimată ı̂n metri) ce nu poate fi modificată
pe parcursul jocului. Staţiile sunt conectate la aceeaşi sursă de alimentare care are B unităţi de
energie.
CAPITOLUL 4. ONI 2019 4.6. TELEFON 62
ı̂n funcţie de raza de acţiune setată, copiii pot sau nu să folosească sistemul walkie-talkie pentru
a nu se mai deplasa. Mai exact, dacă un copil ar trebui să parcurgă o distanţă mai mică sau egală
cu R ca să transmită cuvântul celui din dreapta sa şi bateria sursei are cel puţin R unităţi de
energie rămase, acesta poate folosi sistemul walkie-talkie ca să transmită instantaneu cuvântul
secret, iar bateria se va descărca cu R unităţi de energie. Cu toate acestea, chiar şi cu sistemul
walkie-talkie, un copil nu are voie să transmită mesajul decât primului copil situat ı̂n dreapta lui.
Copiii doresc ca jocul să se termine cât mai repede, aşa că vor seta o rază de acţiune convenabilă
şi vor alege să folosească sau nu sistemul walkie-talkie, pentru a minimiza timpul necesar ca toţi
cei N copii să afle cuvântul secret.
Dorel doreşte să se alăture jocului, aşa că ı̂n a doua parte a jocului va intra şi el ı̂n rând. Dorel
se va aşeza pe axa Ox, undeva ı̂ntre primul şi ultimul copil, la o anumită distanţă de origine unde
nu se află un alt copil.
Cerinţe
Date de intrare
Fişierul de intrare telefon.in conţine pe prima linie două numere naturale N şi B cu
semnificaţia din enunţ. Pe cea de-a doua linie se află N numere naturale nenule distincte Xi ,
ı̂n ordine strict crescătoare, unde Xi reprezintă distanţa copilului i faţă de origine, 1 & i & N .
Date de ieşire
Fişierul de ieşire telefon.out va conţine două numere naturale C1 C2, separate printr-un
spaţiu, unde C1 reprezintă răspunsul la cerinţa 1 iar C2 răspunsul la cerinţa 2.
a 2N 105
a 1B109, 1Xi109
a Se garantează că Dorel are cel puţin o poziţie liberă pe care se poate aşeza
a Un copil poate alege ı̂ntre a se deplasa sau a folosi walkie-talkie pentru a transmite un mesaj
a Copiii pot seta o noua rază de acţiune a walkie-talkie când Dorel intră ı̂n joc
a Pentru prima cerinţă se acordă 40 puncte
a Pentru a doua cerinţă se acordă 60 puncte
a Pentru teste ı̂n valoare de 15 puncte N, B & 10
2
Exemple
6 15 86 N=6, B=15
7 9 12 16 21 27 X[1-6]=[7,9,12,16,21,27]
Pentru a rezolva cerinţa 1 e suficient să considerăm că R poate lua valori doar din mulţimea
distanţelor dintre oricare 2 copii.
Pentru a rezolva a 2-a cerinţă trebuie să observăm că R poate să nu aparţină din mulţimea
distanţelor dintre oricare 2 copii. De asemenea, trebuie să ı̂l aşezăm pe Dorel astfel ı̂ncât să
spargem o distanţă ı̂n două bucăţi. Mai exact, noi am vrea o distanţă X mai mare decât R (raza
noastra curentă) pe care să o spărgem” ı̂n 2 bucăţi, de lungimi R respectiv X R. Mereu vom
folosi distanţa de lungime R dar dacă ne uităm mai atent observăm ca există cazuri ı̂n care am
dori să folosim şi o bucată de lungime X R aşa că vom căuta cea mai mare distanţa X cu
proprietatea că X & 2R (pentru a maximiza X R). Pentru a nu căuta liniar acest X putem
menţine un pointer ı̂n faţă.
Soluţie - 15 puncte
Se observă că pentru o valoare fixată a lui R strategia optimă este să luăm cele mai mari
distanţe mai mici sau egale cu R. Astfel, prima dată vom calcula distanţele ı̂ntre oricare 2 copii
consecutivi şi le vom sorta crescător. Având acest vector sortat, pentru fiecare valoare a lui R ı̂n
intervalul 1, B căutăm cea mai mare distanţă mai mică sau egală cu R şi vom verifica cât de
mult ne putem extinde ı̂n stânga acestei distanţe (ı̂n limita bateriei) pentru a minimiza cât mai
mult durata jocului.
2
Această soluţie are complexitate O N B
Soluţie - 50 puncte
Pentru a optimiza soluţia anterioară observăm că pentru o valoare fixată a lui R este suficientă
o singură parcurge a vectorului de distanţe folosind 2 pointeri. Pointerul din dreapta ne spune cea
mai mare distanţă mai mică sau egală cu R iar pointerul din stânga cat de mult ne putem extinde.
De fiecare dată când incrementam pointerul din dreapta vom verifica dacă dapăşim limita bateriei,
caz ı̂n care vom incrementa şi pointerul din stânga.
Complexitate: O N B
Soluţie - 70 puncte
Optimizând ı̂n continuare algoritmul, observăm că nu trebuie să iteram prin toate valorile pe
care le poate lua R. În schimb, vom ı̂ncepe cu R 1 şi vom incrementa R ı̂n funcţie de distanţa
maximă pe care o considerăm la momentul actual(pointerul din dreapta).
Pentru a 2-a cerinta este necesar sa testam toate valorile pe care le poate lua R si nu doar din
multimea distantelor.
Complexitate: O N log N B
CAPITOLUL 4. ONI 2019 4.6. TELEFON 64
int32_t main()
{
ifstream cin("telefon.in");
ofstream cout("telefon.out");
sort(dist.begin(), dist.end());
int i;
for(i = 1; i*i <= m; i++)
vals.push_back(i),vals.push_back(m/i);
sort(vals.begin(), vals.end());
int first = 0;
int semnal = 0;
int second = 0;
int extra = 0;
current = dist[0];
long long z = 0;
cout<<max(z,total-nokid) << ’ ’;
cout<<max(z,total-best) << ’\n’;
}
int32_t main()
{
ifstream cin("telefon.in");
ofstream cout("telefon.out");
vector<int> X(N);
X.erase(X.begin());
--N;
sort(X.begin(), X.end());
vector<int> partial(X.size());
answer = numeric_limits<int>::max();
N = X.size();
for (int cover = N + 1, end = -1, magic = 0; cover > 0; --cover)
{
int max_cover = B / cover;
// 3 ways
// we cover cover of already existant values
// we cover cover - 1 of already existent values + some part
// of the biggest
// we cover cover - 2 of already + the biggest <= 2 * max_cover
if (end == -1)
continue;
// ignore Dorel
int now = partial[N - 1] - partial[end];
if (end - cover >= 0)
now += partial[end - cover];
// Dorel once
if (cover > 0)
{
now = partial[N - 1] - partial[end];
if (end - (cover - 1) >= 0)
now += partial[end - (cover - 1)];
if (end < N - 1)
now -= min(max_cover, X[N - 1]);
// Dorel twice
if (cover > 1)
CAPITOLUL 4. ONI 2019 4.6. TELEFON 67
{
now = partial[N - 1] - partial[end];
if (end - (cover - 2) >= 0)
now += partial[end - (cover - 2)];
if (!opts.count("cands_are_dists"))
{
cands.clear();
for (int i = 1; i <= magic && i <= v; ++i)
{
cands.push_back(i);
cands.push_back(v / i);
}
sort(cands.rbegin(), cands.rend());
sort(cands.begin(), cands.end());
}
{
now -= dists[l];
++l;
}
/*
for (auto x : dists) cerr << x << " "; cerr << endl;
cerr << "delta: " << delta << endl;
cerr << "(phones: " << phones << ")" << endl;
cerr << "bests: " << bests[0] << " " << bests[1]
<< " " << bests[2] << endl;
cerr << endl;
*/
}
int32_t main()
{
ifstream cin("telefon.in");
ofstream cout("telefon.out");
vector<int> dists;
return 0;
}
ifstream fin("telefon.in");
CAPITOLUL 4. ONI 2019 4.6. TELEFON 69
ofstream fout("telefon.out");
int n, b, i, x, y, total;
vector<int> d, signals;
void subtask()
{
int ls = 0, ld = 0, best = 0, saved = 0;
while (ld < d.size() && d[ld] <= b)
{
saved += d[ld++];
while ( (ld - ls) * d[ld - 1] > b )
saved -= d[ls++];
void solve()
{
int ls = 0, ld = 0, split = 0, bonus[3], saved = 0, best = 0;
split = ld + 1;
while (split < d.size() && d[split] <= 2 * signals[k])
split++; /// Fixate split
bonus[0] = bonus[1] = 0;
if (split > 0 && ld != d.size())
{
bonus[0] = min(signals[k], d[split - 1]);
bonus[1] = max(d[split - 1] - signals[k], 0LL);
if (bonus[1] > signalSize) bonus[1] = 0;
}
saved = 0;
int spaceLeft = b / signalSize - (ld - ls);
if (spaceLeft <= 0)
{
for (int r = ls ; r < ld ; r++)
{
if (r - ls == 0 || r - ls == 1)
saved += (bonus[r - ls] > d[r] ? bonus[r - ls] : d[r]);
else
saved += d[r];
}
}
else if (spaceLeft == 1)
{
for (int r = ls ; r < ld ; r++)
{
if (r - ls == 0)
saved += (bonus[1] > d[r] ? bonus[1] : d[r]);
else
saved += d[r];
}
saved += bonus[0];
}
else
{
for (int r = ls ; r < ld ; r++)
saved += d[r];
int32_t main()
{
fin >> n >> b;
fin >> x;
x = y;
}
sort(d.begin(), d.end());
for (i = 0 ; i < d.size() ; i++)
{
cout << d[i] << ’ ’;
}
subtask();
solve();
return 0;
}
ONI 2018
5.1 bazaf
Problema 1 - bazaf 100 de puncte
În matematică factorialul unui număr natural nenul K este notat cu K! şi este egal cu produsul
numerelor naturale nenule mai mici sau egale cu K.
Exemple: 1! 1; 2! 1 2 2; 3! 1 2 3 6, ... , K! 1 2 3 ... K.
Orice număr natural N poate fi descompus cu ajutorul numerelor factoriale astfel:
unde coeficienţii fi , cu 1 & i & m sunt numere naturale şi ı̂n plus fm j 0;
Exemple: 20 1!20; 20 1!6 2!4 3!1; 20 1!0 2!1 3!3;
Dintre toate aceste descompuneri posibile există o singură descompunere, numită descom-
punere ı̂n bază factorială care respectă suplimentar condiţiile 0 & fi & i, cu 1 & i $ m şi
0 $ fm & m.
Exemple: 6 1!0 2!0 3!1; 17 1!1 2!2 3!2; 119 1!1 2!2 3!3 4!4;
Cerinţe
1. Să se determine descompunerea ı̂n bază factorială a unui număr natural X dat.
2. Cunoscând o descompunere oarecare a unui număr natural Y să se determine descompunerea
ı̂n bază factorială a acestuia.
Date de intrare
Date de ieşire
71
CAPITOLUL 5. ONI 2018 5.1. BAZAF 72
2 & X & 10
15
a
a 1 $ m & 100 000
a 0 & fi & 10
9
a Pentru rezolvarea corectă a primei cerinţă se va acorda 30% din punctaj, iar pentru cea de-a
doua cerinţă se va acorda 70% din punctaj.
Exemple:
Pentru rezolvarea primei cerinţe se construieşte un vector cu toate valorile lui k! pentru k & 20
apoi printr-o strategie Greedy se determină care este cel mai apropiat factorial mai mic decât
numărul dat, se calculează coeficientul acelui termen, se scade din numărul dat termenul respectiv,
se memorează ı̂n vectorul de descompuneri coeficientul calculat şi se reia operaţia până când
numărul curent devine 0.
Pentru rezolvarea celei de-a doua cerinţe se observă ı̂ncă de la ı̂nceput că o soluţie care să
calculeze numărul Y şi apoi să determine descompunerea ı̂n bază factorială folosind ideea de la
cerinţa anterioară nu poate obţine puncte deoarece descompunerea dată are un număr foarte mare
de termeni şi corespunde unui număr Y foarte mare.
Soluţia constă ı̂ntr-o observaţie matematică şi anume că se poate parcurge vectorul descom-
punerii şi dacă un coeficient este mai mare decât limita impusă vom calcula cât din valoarea
respectivă va fi transportată către următorul coeficient.
Se actualizează astfel atât coeficientul curent cât şi cel următor şi ı̂n cazul cănd ultimul coefi-
cient este mai mare decât limita impusă se adaugă noi coeficienţi.
O astfel de soluţie nu presupune calcularea numărului asociat descompunerii.
Exemplu: Pentru descompunerea 4 7 12 14 9 se trece prin următoarele etape
4 7 12 14 9 (7 1! = 1 + 3 2!)
4 1 15 14 9 (15 2! = 0 + 5 3!)
4 1 0 19 9 (19 3! = 3 + 4 4!)
4 1 0 3 13 (13 4! = 3 + 2 5!) s-a mai adăugat un termen
5 1 0 3 3 2
#include<stdio.h>
CAPITOLUL 5. ONI 2018 5.1. BAZAF 73
#include<algorithm>
#include<vector>
#include<cassert>
long long n;
int v[NMAX], p;
vector<int> answer;
void solve1()
{
scanf("%lld",&n);
assert(1 <= n && n <= 1LL * 1000000000000000);
findBazaFactoriala(n);
}
void solve2()
{
scanf("%lld",&n);
assert(1 <= n && n <= 100000);
for(int i = 1; i <= n; i++)
{
assert(0 <= v[i] && v[i] <= 1000000000);
scanf("%d",&v[i]);
}
int main ()
{
freopen("bazaf.in","r",stdin);
freopen("bazaf.out","w",stdout);
scanf("%d", &p);
CAPITOLUL 5. ONI 2018 5.1. BAZAF 74
return 0;
}
#include<fstream>
#include<iostream>
ifstream fin("bazaf.in");
ofstream fout("bazaf.out");
int nx,m,i;
ll v,x[21],fi[100005];
ll f[21],p,c,r;
int main()
{
f[0]=1;
for (i=1;i<=20;i++)
{
f[i]=f[i-1]*i;
}
fin>>p;
if (p==1)
{
fin >> v;
nx=0;
for(int j=20;j>=1;j--)
{
x[j]=v/f[j];
if(x[j]>0 && nx==0)
{
nx=j;
}
v=v%f[j];
}
fout<<nx;
for(int j=1;j<=nx;j++)
{
fout<<" "<<x[j];
}
}
if (p==2)
{
fin >> m;
for(i=1;i<=m;i++)
fin >> fi[i];
for(i=1;i<=m-1;i++)
{
c=fi[i]/(i+1);
r=fi[i]%(i+1);
fi[i]=r;
CAPITOLUL 5. ONI 2018 5.1. BAZAF 75
fi[i+1]+=c;
}
while (fi[m]>m)
{
c=fi[m]/(m+1);
r=fi[m]%(m+1);
fi[m]=r;
fi[++m]+=c;
}
fout.close();
fin.close();
return 0;
}
long long n;
long long v[NMAX], p;
vector<int> answer;
void solve1()
{
scanf("%lld",&n);
assert(1 <= n && n <= 1LL * 1000000000000000);
findBazaFactoriala(n);
}
void solve2()
{
long long fact = 1, value = 0;
scanf("%lld",&n);
CAPITOLUL 5. ONI 2018 5.1. BAZAF 76
findBazaFactoriala(value);
}
int main ()
{
freopen("bazaf.in","r",stdin);
freopen("bazaf.out","w",stdout);
scanf("%d", &p);
assert(1 <= p && p <= 2);
if(p == 1)
solve1();
else
solve2();
return 0;
}
ifstream fin("bazaf.in");
ofstream fout("bazaf.out");
int nx,m,i,j;
ll v,x[21],fi[100005];
ll f[21],p,c,r;
int main()
{
f[0]=1;
for (i=1;i<=20;i++)
{
f[i]=f[i-1]*i;
}
fin>>p;
if (p==1)
{
fin >> v;
nx=0;
for(int j=20;j>=1;j--)
{
x[j]=v/f[j];
if(x[j]>0 && nx==0)
{
nx=j;
}
v=v%f[j];
}
fout<<nx;
for(int j=1;j<=nx;j++)
fout<<" "<<x[j];
}
CAPITOLUL 5. ONI 2018 5.2. MEXITATE 77
if (p==2)
{
fin >> m;
long long s=0;
for(i=1;i<=m;i++)
{
fin >> fi[i];
s+=fi[i]*f[i];
}
v=s;
nx=0;
for(int j=20;j>=1;j--)
{
x[j]=v/f[j];
if(x[j]>0 && nx==0)
{
nx=j;
}
v=v%f[j];
}
fout<<nx;
for(int j=1;j<=nx;j++)
{
fout<<" "<<x[j];
}
fout.close();
fin.close();
return 0;
}
5.2 mexitate
Problema 2 - mexitate 100 de puncte
Se dă o matrice A cu N linii şi M coloane cu elemente numere naturale nu neapărat distincte.
Pentru o submatrice definim mex-ul acesteia ca fiind cea mai mică valoare naturală nenulă care
nu apare ı̂n aceasta.
Cerinţe
Să se calculeze produsul mex-urilor tuturor submatricelor având K linii şi L coloane ale matricei
A.
Date de intrare
Fişierul mexitate.in conţine pe prima linie patru numere naturale N , M , K şi L separate
printr-un spaţiu cu semnificaţia din enunţ.
Pe fiecare dintre următoarele N linii se află câte M numere naturale nenule, despărţite prin
câte un spaţiu, reprezentând valorile matricei.
Date de ieşire
Exemple:
2 3 2 cu mex-ul 5
314
2 3 1 cu mex-ul 4
112
3 1 4 cu mex-ul 5
126
Soluţie 20 de puncte:
Pentru fiecare submatrice de K * L, se parcurg elementele şi se pun ı̂ntr-un vector de frecvenţă.
Pentru a determina răspunsul (primul element lipsă), este suficient să parcurgem vectorul de
frecvent̆ă si să aflăm prima poziţie cu frecvenţa 0.
Observăm că mutarea la stânga-dreapta o facem de ordinul O(N * M), iar mutarea ı̂n jos
de ordinul O(N). Complexitatea până ı̂n acest punct devine O(N * M * K + N * L), ignorând
momentan partea ı̂n care trebuie să determinăm rapid mex-ul din vectorul de frecvenţă.
2. Avem restrictia N * M ¡= 400.000
=¿ K * L ¡= 400.000
=¿ min(K, L) ¡= sqrt(400.000)
Daca K ¡= L
=¿ k ¡= sqrt(400.000) = sqrt(VMAX)
=¿ complexitatea devine O(N * M * sqrt(VMAX) + N * L)
În schimb, dacă K ¿ L este nevoie să rotim matricea şi să aplicăm acelaşi procedeu, complexi-
tatea devenind O(N * M * L + M * K), adică O(N * M * sqrt(VMAX) + M * K).
Aplicând acest hibrid, complexitatea devine O(min(N * M * sqrt(VMAX) + N * L, N * M
*sqrt(VMAX) + M * K)). Din moment ce N * L respectiv M * K sunt irelevante, complexitatea
finală va fi: O(N * M * sqrt(VMAX)).
3. Rămâne de văzut cum putem determina rapid care este mex-ul cu ajutorul vectorului de
frecvenţă (din moment ce acesta este foarte mare, nu putem merge element cu element).
Aplicând primele 2 observaţii, deducem că avem O(N * M * sqrt(VMAX)) valori care se
modifică (update-uri), respectiv O(N * M) interogări de mex (query-uri).
Putem echilibra această complexitate dacă am avea o structură ce foloseste O(1) pe update
şi O(sqrt(VMAX)) pe query. Putem ţine bucăţi de dimensiune sqrt(VMAX) pe vectorul de
frecvenţă, fiecare bucată având marcat câte elemente apar cel puţin o dată. Vectorul de frecvenţă
şi informaţia pentru fiecare bucată de radical se pot menţine uşor la update ı̂n O(1). La query,
pentru fiecare bucată de radical putem să o sărim dacă toate elementele apar ı̂n acel interval, re-
spectiv să aplicam un brute-force dacă răspunsul este ı̂n interiorul acelui interval. Mai exact, sărim
elemente din radical ı̂n radical (vom face asta de maxim sqrt(VMAX) ori), respectiv căutam el-
ement cu element intr-un interval de lungime radical. Astfel, vom putea determina valoarea
mex-ului ı̂n maxim sqrt(VMAX) paşi.
}
}
void query()
{
for(int buc = 1; ; buc++)
{
if(fb[buc] == bucket)
continue;
for(int i = (buc - 1) * bucket + 1; ; i++)
{
if(!f[i])
{
answer *= i;
ans += i;
if(answer >= MOD)
answer %= MOD;
break;
}
}
break;
}
}
void initializeaza()
{
addElements(1,1,k,l);
query();
}
int main ()
{
int value, sign;
srand(time(0));
freopen("mexitate.in","r",stdin);
freopen("mexitate.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&k,&l);
int aux = n;
n = m;
m = aux;
CAPITOLUL 5. ONI 2018 5.2. MEXITATE 81
aux = l;
l = k;
k = aux;
}
else
{
for(int i = 1; i <= n; i++)
{
matrix[i].push_back(0);
for(int j = 1; j <= m; j++)
{
matrix[i].push_back(matrix2[i][j]);
}
}
}
int last = 1;
for(int i = 1; i <= n * m; i++)
{
indexB[i] = last;
if(i % bucket == 0)
last++;
}
initializeaza();
for(int i = 1; i <= n - k + 1; i++)
{
sign = ((i&1) ? +1 : -1);
if(sign == 1)
{
for(int j = 2; j <= m - l + 1; j++)
{
deleteElements(i, j - 1, i + k - 1, j - 1);
addElements(i, j + l - 1, i + k - 1, j + l - 1);
query();
}
if(i <= n - k)
{
deleteElements(i, m - l + 1, i, m);
addElements(i + k, m - l + 1, i + k, m);
query();
}
}
else
{
for(int j = m - l; j >= 1; j--)
{
deleteElements(i, j + l, i + k - 1, j + l);
addElements(i, j, i + k - 1, j);
query();
}
if(i <= n - k)
{
deleteElements(i, 1, i, l);
addElements(i + k, 1, i + k, l);
query();
}
}
}
printf("%lld\n", answer);
return 0;
}
#include <cassert>
int main()
{
freopen("mexitate.in", "r", stdin);
freopen("mexitate.out", "w", stdout);
int n, m, k, l;
vector<vector<int>>a(n, vector<int>(m,0));
vector<int>frecv(n * m + 5), buckets(n * m + 5);
int bucket = 1;
for(;bucket*bucket < n*m ;bucket++);
for(auto&line: a)
for(auto &elem: line)
{
cin >> elem;
assert(1 <= elem && elem <= n * m);
}
if(k > l)
{
vector<vector<int>> b = a;
swap(n, m);
swap(k, l);
a = vector<vector<int>>(n, vector<int>(m,0));
if (i + k < n)
{
for(int step = 0 ; step < l; step++)
{
int col = left[(i & 1) xor 1] + (-1) *dir * step;
Update(frecv, buckets, bucket, a[i][col], -1);
Update(frecv, buckets, bucket, a[i + k][col], 1);
}
}
}
5.3 plaja
Problema 3 - plaja 100 de puncte
Zizi ı̂şi va petrece concediul ı̂n această vară ı̂ntr-o frumoasă staţiune de la Marea Neagră. Acolo
va sta N zile. Zilele sunt numerotate de la 1 la N . În fiecare dintre cele N zile de concediu, ea
intenţionează să facă plajă un număr cât mai mare de unităţi de timp. Va trebui să ţină seama
totuşi de prognoza meteo, care este nefavorabilă ı̂n K dintre cele N zile, respectiv ı̂n zilele z1 , z2 ,
..., zk . În fiecare dintre aceste K zile va ploua sau va fi prea mult soare, iar Zizi va trebui să-şi
limiteze timpii de plajă la cel mult t1 , t2 , ..., tk unităţi de timp.
Deasemenea, din motive de confort fizic, Zizi doreşte ca diferenţa ı̂n valoare absolută a timpilor
de plajă ı̂ntre oricare două zile consecutive să nu depăşească T .
Cerinţe
Cunoscând zilele z1 , z2 , ..., zk ı̂n care există limitările t1 , t2 , ..., tk pentru timpul de plajă şi
valoarea T , să se determine numărul maxim de unităţi de timp pe care Zizi le poate petrece la
plajă ı̂ntr-o singură zi dintre cele N zile de concediu.
Date de intrare
Fişierul plaja.in conţine pe prima linie trei numere naturale N K şi T separate printr-un
spaţiu, reprezentând numărul de zile de concediu, numărul de zile ı̂n care există limitări pentru
timpul de plajă şi diferenţa maximă admisă a timpilor de plajă pentru oricare două zile consecutive.
CAPITOLUL 5. ONI 2018 5.3. PLAJA 84
Pe fiecare dintre următoarele K linii se află câte două numere z şi t, despărţite printr-un spaţiu,
reprezentând numărul de ordine al unei zile cu limitări pentru timpul de plajă, respectiv limita
de timp pentru ziua respectivă.
Valorile z1 , z2 , ..., zk sunt ı̂n ordine strict crescătoare.
Date de ieşire
Fişierul plaja.out va conţine pe prima linie un singur număr natural tmax, reprezentând
numărul maxim de unităţi de timp pe care Zizi le poate petrece făcând plajă ı̂ntr-una din cele N
zile de concediu.
Exemple:
un şir de dimensiune N , ceea ce duce la depăşire de memorie pentru testele cu N foarte mare.
Punctaj acordat: aproximativ 65 de puncte.
Soluţie O(K * log T)
Se observă că parcurgând zilele cu limitări pentru timpul de plajă, este posibil să găsim o zi
d[i] cu restricţia de timp t[i], care face imposibil faptul ca ı̂n ziua d[i + 1] să se atingă valoarea t[i
+ 1].
Astfel, vom parcurge cele K zile cu restricţii şi pentru fiecare valoare t[i] vom reduce valoarea
t[i + 1] la o nouă valoare posibil a fi atinsă. Apoi acelaşi lucru se va face pornind de la sfârşitul
şirului t către ı̂nceputul său.
În etapa următoare se poate căuta binar răspunsul pentru fiecare pereche de zile de forma (d[i],
d[i + 1]), iar verificarea posibilităţii de a se atinge o valoare propusă t0, de face printr-o testare
relativ simplă:
d[i + 1] - d[i] ¿= (t0 - t[i] + T - 1) / T + (t0 - t[i + 1] + T - 1) / T;
Punctaj acordat: 100 de puncte
Solutie O(k) (Balţatu Andrei-Mircea)
Iniţial se iau cele k zile restricţionate şi se aduc la valorile maximale posibile, adică pentru o
zi 1 ¡= i ¡= m cu valoarea initială t(i) şi pozitia x(i), dacă există o altă zi j şi t(j) + T * abs(x(i)
- x(j)) ¡ t(i), atunci t(i) se scade deoarece este restricţionat de alte zile. (abs - funcţia modul).
Apoi observăm că putem afla răspunsul ı̂ntre oricare două zile consecutive ı̂n O(1). Având cele
două zile (X - ı̂n stanga, Y - ı̂n dreapta), ştim că fiecare va creşte cu T ı̂n drumul spre ziua maximă.
Astfel, aducem pe minimul dintre ele, adică min(t(X), t(Y)) la un număr nr ¡= max(t(X), t(Y))
cât mai mare, după care ambele ı̂ncep să crească cu T alternativ, spre o pozitia P necunoscută.
În funcţie de paritatea secventei de zile rămase după ce am crescut minimul dintre ele, putem
afla poziţia de valoare maximă (P) ı̂n funcţie de paritatea secvenţei de zile rămase. Punctaj: 100
de puncte
int n, k, T;
int main()
{
freopen("plaja.in", "r", stdin);
freopen("plaja.out", "w", stdout);
int n, k;
cin >> n >> k >> T;
for (int i = 1; i<= k; i++)
cin >> z[i] >> timp[i];
#include <bits/stdc++.h>
#define int long long
struct point_wall
{
int pos, val;
point_wall(int _pos, int _val)
{
pos = _pos;
val = _val;
}
};
int n, k, t;
vector<point_wall> restr;
vector<point_wall> Normalize(vector<point_wall> A)
{
vector<point_wall> res(A.begin(), A.end());
// from left
point_wall mn = A.front();
for (int i = 0; i < A.size(); i++)
{
// check if present element is a better restriction
if (1LL * t * abs(A[i].pos - mn.pos) + mn.val > A[i].val)
mn = A[i];
// from right
mn = A.back();
for (int i = A.size() - 1; i >= 0; i--)
{
// check if present element is a better restriction
if (1LL * t * abs(A[i].pos - mn.pos) + mn.val > A[i].val)
mn = A[i];
return res;
}
dif -= steps;
x.val += 1LL * steps * t;
return mx;
}
int32_t main()
{
freopen("plaja.in", "r", stdin);
freopen("plaja.out", "w", stdout);
cin.sync_with_stdio(false);
if (restr.back().pos < n)
restr.push_back(point_wall(n, 1LL * n * t + (1 << 30)));
if (restr.front().pos > 1)
restr.insert(restr.begin(), point_wall(1, 1LL * n * t + (1 << 30)));
restr = Normalize(restr);
int mx = 0;
for (int i = 0; i < restr.size() - 1; i++)
mx = max(mx, Local_maxima(restr[i], restr[i + 1]));
ifstream fin("plaja.in");
ofstream fout("plaja.out");
int N, K, T;
VLL d, t;
int main()
{
fin >> N >> K >> T;
d = t = VLL(K + 1);
LL res = 0, l, r, m, tmp;
ifstream fin("plaja.in");
ofstream fout("plaja.out");
struct Entry
{
int day, sgn;
bool operator < (const Entry& e) const
{
return day < e.day || ( day == e.day && sgn > e.sgn);
}
};
int N, K, T;
vector<long long> d, t;
int main()
{
fin >> N >> K >> T;
int day, time;
while (l <= r)
{
m = (l + r) / 2;
if (Try(m))
{
res = m;
l = m + 1;
}
else
r = m - 1;
}
e.push_back( {1, 0} );
e.push_back({N + 1, -1});
int a, b;
for (int i = 0; i < K; i++)
if (t[i] < t0)
{
long long x = (t0 - t[i] - 1) / T;
a = max(d[i] - x, 1LL);
b = min(d[i] + x + 1, (long long)(N + 1));
e.push_back({a, 1});
e.push_back({b, -1});
}
sort(e.begin(), e.end());
return false;
}
ifstream fin("plaja.in");
ofstream fout("plaja.out");
int N, K, T;
vector<long long> d, t;
int main()
{
fin >> N >> K >> T;
int day, time;
for (int i = 0; i < K; ++i)
{
CAPITOLUL 5. ONI 2018 5.4. BSREC 91
while (l <= r)
{
m = (l + r) / 2;
if (Try(m))
{
res = m;
l = m + 1;
}
else
r = m - 1;
}
e[a]++;
e[b]--;
}
5.4 bsrec
Problema 4 - bsrec 100 de puncte
Fie un vector v sortat crescător cu N elemente naturale nenule
distincte pe care nu le cunoaştem, dar pe care ne propunem să le
determinăm.
Având la dispoziţie acest vector v, cu ajutorul următorului algo-
ritm de căutare binară (vezi Figura 1) putem răspunde la query-uri
de forma:
”Dându-se un număr X şi un interval a, b se cere să se determine
cel mai mic element mai mare decât X aflat ı̂n intervalul determinat
de indicii a şi b, interval din vectorul v.”
Se cunosc paşii pe care algoritmul de cautare binară i-a urmat
pentru diferite valori ale tripletului X, a, b.
CAPITOLUL 5. ONI 2018 5.4. BSREC 92
Cerinţe
Dându-se N (lungimea vectorului), Q (numărul de query-uri apelate) şi cele Q query-uri, să
se determine vectorul iniţial. Dacă există mai multe soluţii se va afişa soluţia minim lexicografică.
Dacă nu există soluţie se va afişa valoarea -1.
Un vector V 1 se consideră mai mic lexicografic decât un alt vector V 2 dacă există un indice i
astfel ı̂ncât: V 11 V 21, V 12 V 22, ..., V 1i 1 V 2i 1 şi V 1i $ V 2i.
Cele Q query-uri sunt formate din:
Date de intrare
Fişierul bsrec.in conţine pe prima linie o valoare T reprezentând numărul de teste ce vor fi
efectuate.
Pentru fiecare din cele T teste se va citi de pe prima linie valoarea N (numărul de elemente
ale vectorului), respectiv Q (numărul de query-uri), despărţite prin câte un spaţiu.
Următoarele linii descriu cele Q query-uri.
În cadrul unui query, prima linie va conţine o pereche de numere (X, M ) despărţite printr-un
spaţiu, unde X reprezintă valoarea căutată ı̂n vector, iar M numărul de paşi efectuaţi ı̂n căutarea
binară a celei mai mici valori din vector care este mai mare decât X.
Pe următoarea linie a query-ului se găseşte perechea de valori (a, b) având semnificaţia de mai
sus.
Următoarele M 1 linii conţin perechi (st, dr) de valori naturale despărţite prin câte un
spaţiu care reprezintă indicii stânga, respectiv dreapta ce sunt afişaţi ı̂n urma fiecărei iteraţii a
algoritmului de mai sus.
Date de ieşire
Fişierul bsrec.out va conţine T linii, linia i conţinând răspunsul pentru testul i. Dacă testul
admite soluţie, se vor afişa N numere naturale nenule strict crescătoare ce vor reprezenta valorile
vectorului v.
Deoarece există mai multe soluţii, se va afişa soluţia minim lexicografică. Dacă testul NU
admite soluţie, se va afişa -1.
a 1 & T & 10
a 1 & N, Q & 1000
a 1 & X & 1 000 000 000
a 1 & st & dr & N , cu excepţia ultimei perechi din căutarea binară unde st % dr
a Pentru 20% din punctajul total există teste cu 1 & N, Q & 10 şi soluţia minim lexicografică
admite valori până ı̂n 20
a Se garantează că M & 15
Exemple:
Parcurgem toate iteraţiile căutarilor binare. Pentru o valoare X (pe care o căutam binar) şi
o pereche (lef t, right) din căutarea binară se poate deduce o restricţionare de valoare pentru
elementul din mijloc mid lef t right©2, ı̂n funcţie de alegerea făcută de iteraţia următoare.
Mai exact, pentru o pereche (lef t, right) urmată de o pereche (lef t2, right2) avem următoarele
2 cazuri:
1. left == left2 şi right2 ¡ right: acest caz presupune că conditia v[mid] ¡ X este falsă, deci
v[mid] ¿= x
2. left ¡ left2 şi right2 == right: cazul presupune faptul că conḑitia v[mid] ¡ x este adevarată,
deci v[mid] ¡= x - 1
În urma tuturor acestor relaţii, putem deduce că pentru fiecare element v[i] din vector (i de
la 1 la N ), această valoare este mărginită inferior de o valoare downi, respectiv superior de o
valoare upi. Dacă elementul nu este mărginit, downi este automat 0, iar upi = INF.
Ultima restricţie pe care trebuie să o impunem este faptul că v[i - 1] ¡ v[i], pentru orice i de la
2 la N.
Pentru a determina soluţia minimă lexicografic, aplicăm o strategie Greedy parcurgând vectorul
de la stânga la dreapta. Presupunând că am calculat deja soli, soluţia pentru elementul i,
soli 1 va fi maximul dintre soli 1 şi downi 1. La final, rămâne de verificat dacă soli
¡= upi pentru fiecare i de la 1 la N . Dacă toate restricţiile se respectă, avem soluţie şi o afisăm.
Altfel, răspunsul e -1.
void readAndRestrict()
{
int value, steps;
void setRestrictions()
{
for(int i = 1; i <= n; i++)
{
down[i] = 0;
up[i] = INF;
}
}
int main ()
{
freopen("bsrec.in","r",stdin);
freopen("bsrec.out","w",stdout);
scanf("%d",&tests);
for(; tests; tests--)
{
scanf("%d%d",&n,&q);
setRestrictions();
int have_sol = 1;
for(int i = 1; i <= n && have_sol; i++)
{
sol[i] = max(sol[i - 1] + 1, down[i]);
if(sol[i] > up[i]) {
have_sol = 0;
}
}
if(!have_sol)
{
printf("-1\n");
}
else
{
for(int i = 1; i <= n; i++)
printf("%d ", sol[i]);
printf("\n");
CAPITOLUL 5. ONI 2018 5.4. BSREC 95
}
}
return 0;
}
int main()
{
ifstream fin("bsrec.in");
ofstream fout("bsrec.out");
int t;
fin >> t;
while(t--)
{
int n, q;
fin >> n >> q;
while(q--> 0)
{
int x, mid, m;
fin >> x >> m;
for(int i = 1;i <= m;i++)
{
int l, r;
fin >> l >> r;
if (i > 1)
{
if(l == mid+1)
{
maxx[mid] = min(maxx[mid], x - 1);
}
else
{
minn[mid] = max(minn[mid], x);
}
}
mid = (l+r)/2;
}
}
vector<int>sol;
int x = 0;
if(sol.size() == n)
{
for(auto x: sol)
fout << x << " ";
fout <<"\n";
}
else
fout << "-1\n";
}
CAPITOLUL 5. ONI 2018 5.5. NUMINUM 96
return 0;
}
5.5 numinum
Problema 5 - numinum 100 de puncte
Se consideră următoarea structură de date:
Cerinţe
Cunoscând numărătorul, respectiv numitorul a două fracţii ireductibile diferite din struc-
tură, determinaţi numărul minim de segmente de dreaptă cu care putem conecta ı̂n structura
dată, cele două fracţii.
Date de intrare
Pe prima linie a fişierului de intrare numinum.in se găseşte un număr natural N .
Pe fiecare dintre următoarele N linii se găsesc câte 4 numere naturale xi , yi , ai , bi , 1 & i & N ,
despărţite prin câte un spaţiu unde xi , yi reprezintă numărătorul, respectiv numitorul primei
fracţii de pe linia i 1, iar ai , bi reprezintă numărătorul, respectiv numitorul celei de-a doua
fracţii de pe linia i 1.
Date de ieşire
Fişierul de ieşire numinum.out va conţine N linii. Pe linia i se va scrie numărul minim de
segmente de dreaptă necesare pentru a conecta, pe structura dată, fracţia xyi cu fracţia ab i .
i i
Exemple:
numinum.in numinum.out Explicaţii
1 6 N=1
4325 x1 4, y1 3; a1 2, b1 5.
Pentru a conecta fracţia 34 cu fracţia 52 avem nevoie de minim 6
segmente, după cum urmează:
4/3 1/3 1/2 1/1 2/1 2/3 2/5
Timp maxim de executare/test: 0.2 secunde
Memorie: total 128 MB din care pentru stivă 32 MB
Dimensiune maximă a sursei: 10 KB
CAPITOLUL 5. ONI 2018 5.5. NUMINUM 97
Ce putem deduce ı̂n schimb este faptul că dacă am determina prima pereche din cele comune
celor 2 perechi, aceasta pereche ar fi o pereche care se poate determina ”ı̂n mod direct” din punctul
de ı̂ntâlnire.
Definim că dintr-o pereche (a,b) putem ajunge ”ı̂n mod direct” ı̂n altă pereche (c,d) dacă
putem aplica algoritmul de scăderi repetate doar pentru una din variabile (ori a = c şi modificăm
doar b, ori b = d şi modificam doar a).
Astfel, pornind de la 2 perechi (a,b) şi (c,d), am redus problema la alte 2 perechi (a2, b2) şi
(c2, d2), perechi care se pot ı̂ntâlni ”ı̂n mod direct” ı̂ntr-un punct comun. Din acest punct, putem
determina ı̂n O 1 distanţa dintre cele 2 perechi aplicând formule simple ı̂n funcţie de cazuri.
Exemplu de caz:
A2 ¡ B2, C2 ¿ D2 - din acest caz deducem că prin scăderi repetate A2 rămâne pe loc ı̂n timp
ce B2 scade cu căte A2, respectiv D2 rămâne pe loc şi C2 scade cu câte D2.
Din moment ce ştim că aceste perechi se vor ı̂ntâlni ı̂ntr-un punct comun avem relaţiile:
a B2 - k1 * A2 = D2
a C2 - k2 * D2 = A2
Din aceste 2 relaţii putem determina k1 (distanţa primei perechi până ı̂n punctul comun)
respectiv k2 (distanţa celei de a doua perechi până ı̂n punctul comun. Distanţa ı̂ntre cele 2
perechi este desigur k1 + k2.
Restul cazurilor rămâne tema de gandire!
Soluţia 4: prof. Marcel Drăgan - Colegiul Naţional ”Samuel Von Brukenthal”, Sibiu
Pentru complexitate liniară folosim scăderi repetate asupra ambelor fracţii parcurgând astfel
structura ı̂napoi spre vârf. La fiecare pas transformăm fracţia ı̂n componenta căreia intră valoarea
numerică mai mare (la numărator sau numitor) şi numărăm paşii realizaţi.
Ne oprim atunci când cele două fracţii devin identice.
Pentru complexitate logaritmică folosim ı̂n loc de scăderi repetate restul ı̂mpărţirii (la fel
ca ı̂n Algoritmul lui Euclid). La această modalitate trebuie să avem grijă la situaţia ı̂n care
transformarea fracţiei trece dincolo de prima fracţie comună celor două şiruri de transformări
neobţinând astfel numărul minim de segmente. Pentru aceasta la fiecare pas verificăm dacă
fracţiile nu au ajuns cumva pe aceeaşi ramură:
p1==p2 şi (q1-q2)%p1==0 sau q1==q2 şi (p1-p2)%q1==0.
Dacă au ajuns calculăm numărul de segmente dintre cele două fracţii:
(p1-p2)/q1 sau (q1-q2)/p1
şi ajustăm numărul total de segmente.
#include <fstream>
#include <cassert>
int main()
{
long long a1,b1,a2,b2,T;
long long w1[nmax],w2[nmax];
long long d, i, j, k, c, r; // teorema impartirii cu rest
long long k1,k2,ok1,ok2,s;
ifstream fin("numinum.in");
ofstream fout("numinum.out");
fin >> T;
assert(T>=1 && T<=10000);
for(k=1;k<=T;k++)
{
fin >> a1 >> b1 >> a2 >> b2;
assert(a1>=1 && a1<=1000000000);
CAPITOLUL 5. ONI 2018 5.5. NUMINUM 99
// prima fractie.......................................................
// determin fractia continua atasata primei fractii
d = a1;i = b1; r = d%i; k1 = 0;
if (r==0) w1[++k1] = d/i;
while (r)
{
r = d%i;
c = d/i;
w1[++k1] = c;
d = i;
i = r;
}
// fractia a doua..........................................
// determin fractia continua atasata cele de-a doua fractii
d = a2;i = b2; r = d%i; k2 = 0;
if (r==0) w2[++k2] = d/i;
while (r)
{
r = d%i;
c = d/i;
w2[++k2] = c;
d = i;
i = r;
}
break;
}
}
// afisez rezultatul
fout << ok1 + ok2 - s << "\n";
}
CAPITOLUL 5. ONI 2018 5.5. NUMINUM 100
fin.close();
fout.close();
return 0;
}
#include<stdio.h>
#include<vector>
#include<algorithm>
#define x first.first
#define y first.second
#define dist second
int a, b, c, d, T;
vector< pair<pair<int,int>, int> > mylist[3];
int c;
if(a < b)
{
if(a > 1)
{
c = b / a;
getList(index, a, b % a);
}
else
{
c = b - 1;
getList(index, 1, 1);
}
}
else
{
if(b > 1)
{
c = a / b;
getList(index, a % b, b);
}
else
{
c = a - 1;
getList(index, 1, 1);
}
}
void clearAll()
{
mylist[1].clear();
mylist[2].clear();
}
int main ()
CAPITOLUL 5. ONI 2018 5.5. NUMINUM 101
freopen("numinum.in","r",stdin);
freopen("numinum.out","w",stdout);
scanf("%d",&T);
for(int k=1;k<=T;k++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
getList(1,a,b);
getList(2,c,d);
int ans = 0;
for(int i = lim1 - 1; i > lca; i--)
ans += mylist[1][i].dist;
if(lca == lim1)
{
int A = mylist[2][lca].x;
int B = mylist[2][lca].y;
int C;
if(A < B)
{
C = mylist[2][lca - 1].x;
ans += (B - C) / A;
}
else
{
C = mylist[2][lca - 1].y;
ans += (A - C) / B;
}
printf("%d\n",ans);
clearAll();
continue;
}
if(lca == lim2)
{
int A = mylist[1][lca].x;
int B = mylist[1][lca].y;
int C;
if(A < B)
{
C = mylist[1][lca - 1].x;
ans += (B - C) / A;
}
else
{
C = mylist[1][lca - 1].y;
ans += (A - C) / B;
}
printf("%d\n", ans);
clearAll();
continue;
}
int A = mylist[1][lca].x;
int B = mylist[1][lca].y;
int C = mylist[2][lca].x;
int D = mylist[2][lca].y;
CAPITOLUL 5. ONI 2018 5.6. ROSII MICI 102
if(A < B)
{ // C > D || A == C
if(A == C)
ans += modul(B - D) / A;
else
ans += (C - A) / D + (B - D) / A;
}
else
{// A > B && C < D || B == D
if(B == D)
ans += modul(A - C) / B;
else
ans += (A - C) / B + (D - B) / C;
}
printf("%d\n", ans);
clearAll();
}
return 0;
}
Cerinţe
Să se calculeze aria maximă a unei submatrice care respectă specificaţiile din enunţ, pentru
fiecare din cele Q sarcini date de către bunic.
Date de intrare
Fişierul rosiimici.in conţine pe prima linie trei numere naturale N , M şi Q separate printr-un
spaţiu, având semnificaţia din enunţ.
Pe fiecare dintre următoarele N linii se află câte M numere naturale despărţite prin câte un
spaţiu, reprezentând valorile matricei.
Pe următoarele Q linii se află câte un număr natural x, reprezentând dimensiunea unei roşii.
Date de ieşire
Fişierul rosiimici.out va conţine pe primele Q linii câte un număr natural, reprezentând aria
maximă cerută pentru fiecare sarcină, ı̂n ordinea ı̂n care acestea apar ı̂n fişierul de intrare.
Exemple:
Soluţia este submatricea cu colţul stânga sus ı̂n (1,2) şi colţul
dreapta jos ı̂n (3, 4). Aria acesteia este 9. Pentru rezolvarea
celei de a treia sarcini, Dan mută ultima coloană ı̂n faţa primei
coloane, obţinând matricea:
Soluţia este submatricea cu colţul stânga sus ı̂n (1,1) şi colţul
dreapta jos ı̂n (3, 2). Aria acesteia este 6.
Rezolvare 30 de puncte:
Pentru 30 de puncte putem precalcula răspunsul pentru fiecare valoare.
Astfel, pentru o valoare x calculăm inaltime[i] = numărul de elemente mai mici sau egale cu
x de pe coloane i. Având vectorul de ı̂nălţimi calculat putem folosi o abordare de tip greedy.
În primul rând sortam coloanele după ı̂nălţime. Apoi, pentru o coloană i, aria maximă a unui
dreptunghi este inaltime[i] * numărul de coloane care au ı̂nălţimea mai mare sau egală cu ı̂nălţimea
coloanei, soluţia fiind coloana care maximizează expresia de mai sus.
O abordare similară este calcularea vectorului inaltime pentru fiecare query ı̂n parte.
2 2
Complexităţi : O n m Q sau O Q n m
Rezolvare 50 de puncte:
Pentru aproximativ 50 de puncte ne vom folosi de faptul că fiecare coloană este sortată.
Astfel, vom folosi algoritmul descris ı̂n problema bsrec pentru calcularea ı̂n m*log(n) paşi a
vectorui inaltime.
CAPITOLUL 5. ONI 2018 5.6. ROSII MICI 104
O altă aborare este parcurgerea matricei ı̂n ordinea sortată a elementelor şi menţinerea vec-
torului inaltime sortat la fiecare pas, deoarece adăugarea elementului duce la modificarea unei
singure poziţii ı̂n vector, şi anume inaltimej .
2 2
Complexităţi : O n logn m Q sau O Q mlogn sau O n m Q
int main()
{
ifstream fin("rosiimici.in");
ofstream fout("rosiimici.out");
int n, m, q;
fin >> n >> m >> q;
int sol = 0;
for (int val = 1;val <= n*m; ++val)
{
for(auto j: positions[val])
{
height[j]++;
sum[height[j]]++;
sol = max(sol,sum[height[j]] * height[j]);
}
ans[val] = sol;
}
while(q-- > 0)
{
int x;
CAPITOLUL 5. ONI 2018 5.6. ROSII MICI 105
fin >> x;
assert(1 <= x && x <= n*m);
fout << ans[x] << "\n";
}
return 0;
}
#include <bits/stdc++.h>
int n, m, q;
int a[NMAX][NMAX];
int rasp[QMAX];
int column_height[NMAX], which_column[NMAX], actual_pos[NMAX];
void Prep()
{
for (int i = 1; i <= m; i++)
{
column_height[i] = 0;
which_column[i] = actual_pos[i] = i;
}
int i = 0, j = 0;
int mx = 0;
while (i < pos.size() && j < queries.size())
{
pair<int, int> x = pos[i];
pair<int, int> y = queries[j];
Swap(actual_pos[x.second], ans);
mx = max(mx,
actual_pos[x.second] *
CAPITOLUL 5. ONI 2018 5.6. ROSII MICI 106
column_height[actual_pos[x.second]]);
i++;
}
else
{
rasp[y.second] = mx;
j++;
}
}
int main()
{
freopen("rosiimici.in", "r", stdin);
freopen("rosiimici.out", "w", stdout);
cin.sync_with_stdio(false);
sort(queries.begin(), queries.end());
sort(pos.begin(), pos.end(), cmp);
Prep();
ONI 2017
6.1 arhipelag
Problema 1 - arhipelag 100 de puncte
În regiunea Ionia a lumii greceşti antice, regiune ce corespunde teritoriului actual al Mării
Egee, există mai multe insule. Harta mării este reprezentată de o matrice de dimenisuni N M ,
având valori de 1 şi 0, iar fiecare element din matrice reprezintă o zonă de dimensiune 1 1 din
mare. Liniile matricei sunt numerotate de la 1 la N , de sus ı̂n jos, iar coloanele de la 1 la M , de
la stânga la dreapta. Astfel, colţul din stânga sus al matricei este asociat zonei (1, 1), iar colţul
din dreapta jos corespunde zonei (N , M ).
Un element care conţine valoarea 0 reprezintă faptul că ı̂n acea zonă se află apă. O insulă este
determinată de un dreptunghi format ı̂n totalitate din valori de 1. Se garantează faptul că toate
zonele care conţin valoarea 1 formează dreptunghiuri valide şi că oricare două insule sunt separate
de apă. De exemplu, Figura 1 de mai jos reprezintă o hartă validă, ı̂n timp ce Figura 2 şi Figura
3 NU reprezintă o hartă validă.
Cerinţe
Ionienii, fiind oameni practici, doresc construirea unui far-bibliotecă (aşezat pe o platformă 1
1), ı̂ntr-o zonă acoperită de apă. Poziţia platformei va fi aleasă ı̂ntr-o celulă C astfel ı̂ncât suma
distanţelor dintre toate insulele şi C să fie minimă.
Distanţa dintre o celulă C şi o insulă este definită ca fiind minimul dintre distanţele Manhattan
dintre C şi fiecare celulă care aparţine insulei (distanţa poate trece atât prin alte insule, cât şi
prin zone acoperite de apă).
Distanţa Manhattan dintre două celule aflate pe linia x1 şi coloana y1, respectiv pe linia x2 şi
coloana y2, este definită ca —x1 - x2— + —y1 - y2—, unde —x— reprezintă valoarea absolută a
lui x.
Date de intrare
Fişierul de intrare arhipelag.in conţine, pe prima linie, valorile N şi M , având semnificaţia din
enunţ. Următoarele N linii conţin câte M valori binare, separate de câte un spaţiu, reprezentând
harta mării.
Date de ieşire
107
CAPITOLUL 6. ONI 2017 6.1. ARHIPELAG 108
Exemple:
În descrierea soluţiei, C x, y reprezintă o celulă aflată pe linia x şi coloana y, iar
D x1, y1, x2, y2 reprezintă o insulă care are colţul stânga sus ı̂n x1, y1 şi colţul dreapta jos
ı̂n x2, y2.
Soluţia 1 (15 puncte)
Se iterează toate celulele care conţin apă şi pentru fiecare dintre acestea se calculează suma
distanţelor către toate insulele. Distanţa dintre o celulă C si o insulă se calculează iterând toate
celulele care aparţin insulei şi păstrând distanţa Manhattan minimă dintre o astfel de celulă şi
celula C.
2 2
Complexitate: O N M .
Soluţia 2 (35 de puncte)
Se procedează asemanator ca ı̂n soluţia 1, observând faptul că distanţa dintre o celulă C x, y
şi o insulă D x1, y1, x2, y2 se poate calcula ı̂n O 1 astfel:
Dist C, D dx dy, unde:
a dx =
` x1 - x, dacă x ¡ x1
` x - x2, dacă x ¿ x2
` 0, altfel
a dy =
` y1 - y, dacă y ¡ y1
` y - y2, dacă y ¿ y2
` 0, altfel
CAPITOLUL 6. ONI 2017 6.1. ARHIPELAG 109
Complexitate: O N M N R IN SU LE
Soluţia 3 (55 de puncte)
Se observă faptul că pentru o celulă C x, y , avem două costuri asociate: costul implicat de
alegerea liniei x şi costul implicat de alegerea coloanei y; aceste două costuri sunt independente (nu
depind unul de celălalt). Astfel, se precalculează 2 vectori total dxx (1 & x & N ) şi total dy y
(1 & y & M ), reprezentând costul asociat cu alegerea liniei x, respectiv a coloanei y, iar apoi se
iterează toate celulele care conţin apă şi se alege cea care are total dx total dy minim.
Valoarea total dxx se calculează astfel: se itereaza toate insulele, iar pentru fiecare insulă
D x1, y1, x2, y2, se adaugă la total dxx valoarea dx, definită la fel ca in solutia 2. Analog, se
calculează valorile total dy y .
Complexitate: O N N R IN SU LE M N R IN SU LE
Solutia 4 (100 de puncte)
Se procedează asemănător ca ı̂n soluţia 3, cu excepţia modalităţii de calculare a vectorilor
total dxx şi total dy y .
Cum se calculează total dxx (total dy y se calculează ı̂n mod similar):
a se calculează intervalele ı̂nchise x1i, x2i, liniile pe care se intinde insula i
a total dx1 se calculează ca ı̂n soluţia 3, ı̂n O N R IN SU LE
a se iterează x de la 2 la N , la fiecare pas ţinând minte 2 variabile:
toRight -= startHere[i];
}
}
int main() {
ifstream cin("arhipelag.in");
CAPITOLUL 6. ONI 2017 6.1. ARHIPELAG 110
ofstream cout("arhipelag.out");
while(A[xr + 1][j])
++xr;
int yl = j;
int yr = j;
while(A[i][yr + 1])
++yr;
++startX[xl];
++endX[xr];
++startY[yl];
++endY[yr];
}
}
}
return 0;
}
int el[kMaxN][kMaxN];
struct Rectangle
{
CAPITOLUL 6. ONI 2017 6.1. ARHIPELAG 111
vector<Rectangle> rectangles;
rectangles.push_back(r);
}
int lazy_x[kMaxN];
int lazy_y[kMaxN];
int X[kMaxN];
int Y[kMaxN];
int main()
{
ifstream cin("arhipelag.in");
ofstream cout("arhipelag.out");
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i += 1)
for (int j = 1; j <= m; j += 1)
cin >> el[i][j];
lazy_x[itr.x + itr.lx] += 1;
lazy_y_sum -= 1;
lazy_y[itr.y + 1] += 1;
sum_y += itr.y;
lazy_y[itr.y + itr.ly] += 1;
}
int mn = 1e9;
CAPITOLUL 6. ONI 2017 6.2. MIRROR 112
6.2 mirror
Problema 2 - mirror 100 de puncte
Numim ”oglinda” numărului natural nenul a, numărul b, obţinut prin modificarea fiecărei cifre din
reprezentarea sa binară, de exemplu pentru a 22 10 10110 2 se obţine 01001 2 9 10 b.
Cerinţe
Cunoscându-se numerele naturale N , K şi cele N numere natural nenule, scrieţi un program
care:
1) Transformă ı̂n baza doi termenii şirului dat obţinându-se un nou şir format din alipirea
cifrelor binare. Din acest şir se vor determina şi afişa, separate prin câte un spaţiu, toate
reprezentările ı̂n baza 10 corespunzătoare secvenţelor alăturate de exact K cifre binare, parcurse
de la stânga la drepta. Dacă ultima secvenţă nu are exact K cifre binare, atunci aceasta nu se va
mai lua ı̂n considerare.
2) Să aplice K transformări asupra şirului iniţial, ı̂nlocuind la fiecare pas orice termen cu
”oglinda” sa. Asupra termenilor care devin zero nu se vor mai efectua alte operaţii. După
efectuarea celor K transformări, să se determine cea mai lungă secvenţă de numere care au cifra 1
pe aceeaşi poziţie ı̂n reprezentarea lor ı̂n baza doi. Dacă sunt mai multe astfel de secvenţe având
lungimea maximă, se va afişa cea mai din stânga.
Date de intrare
Fişierul de intrare mirror.in conţine pe primul rând numărul C, reprezentând cerinţa. Pe al
doilea rând se află scrise numerele naturale N şi K. Pe rândul al treilea sunt cele N numere ale
şirului separate prin câte un spaţiu.
Date de ieşire
Dacă C 1, atunci ı̂n fişierul de ieşire mirror.out se vor scrie separate prin câte un spaţiu,
toate numerele cerute ı̂n enunţ.
Dacă C 2, atunci ı̂n fişierul de ieşire mirror.out se va scrie pe prima linie lungimea maximă
a secvenţei determinate, iar pe următoarea linie separate prin spaţiu, poziţia primului şi ultimului
termen din secvenţă (prima poziţie este 1).
Restricţii şi precizări
a 1 & N &100 000
a 0 & K & 30
a Elementele şirului sunt mai mici decât 2 000 000 001;
a Pentru 30% din teste cerinţa va fi C 1.
CAPITOLUL 6. ONI 2017 6.2. MIRROR 113
Exemple:
i
Soluţia problemei se bazează pe generarea unui şir de 31 de valori egale cu 2 1, rezultând
astfel doar cifre de 1 ı̂n scrierea binară a numerelor din şir şi utilizând aceste numere pentru
transformările cerute.
La cerinţa 1: cu ajutorul operaţiilor pe biţi, considerăm un număr numit masca, care iniţial
este 1 şi pe care ı̂l mărim până când aceasta ajunge la cifra de unu cea mai semnificativă a
elementelor şirului dat, astfel ı̂ncât la operaţia de ”şi pe biţi” ı̂ntre masca şi număr să determinăm
cifra binară de pe poziţia curentă şi să realizăm construcţia numerelor cerute (cu K cifre binare).
La cerinţa 2: realizăm cele K transformări asupra şirului dat, ţinând cont de faptul că multe
numere ajung zero după un număr de transformări şi determinând platouri de numere nenule,
căutând printre ele, cifră binară cu cifră binară cel mai lung platou de cifre de 1 care se află
0 1
respectiv pe aceeaşi poziţie, ca putere a lui doi (2 , 2 , ...)
FILE *fin=fopen("mirror.in","r");
ofstream fout("mirror.out");
int v[1000005],n,ct,k;
void rezolva1()
{
int masca=0,i,y=1<<(k-1),nr,x=0;
for(i=1; i<=n; i++)
{
nr=v[i];
if(masca==0)
masca=1;
while(masca<nr)
CAPITOLUL 6. ONI 2017 6.2. MIRROR 114
masca<<=1;
if(masca>nr)masca>>=1;
while(masca)
{
if(masca&nr)
x=x+y;
y/=2;
if(y==0)
{
fout<<x<<" ";
y=1<<k-1;
x=0;
}
masca>>=1;
}
}
fout<<’\n’;
}
if(l>lmax)
{
lmax=l;
ddr=dr;
}
masca<<=1;
}
while(ok==1);
dr=ddr;
st=ddr-lmax+1;
return lmax;
}
void secventa()
{
int st=0,dr,i,lung,lmax=0,sst,ddr;
i=1;
while(v[i]==0&&i<=n)
i++;
if(i<=n)
for( ; i<=n; i++)
if(v[i]!=0)
if(st==0)
st=dr=i;
else
dr++;
else
{
CAPITOLUL 6. ONI 2017 6.2. MIRROR 115
if(st)
{
lung=lungime(st,dr);
if(lung>lmax)
{
lmax=lung;
sst=st;
ddr=dr;
}
st=0;
}
}
if(st)
{
lung=lungime(st,dr);
if(lung>lmax)
{
lmax=lung;
sst=st;
ddr=dr;
}
}
fout<<lmax<<’\n’<<sst<<" "<<ddr<<’\n’;
}
int main()
{
int x=1,i,j,masca;
fscanf(fin,"%d%d%d",&ct,&n,&k);
for(i=1; i<=n; i++)
fscanf(fin,"%d",&v[i]);
if(ct==1)
{
rezolva1();
return 0;
}
masca>>=1;
}
if(cont<k)
CAPITOLUL 6. ONI 2017 6.2. MIRROR 116
v[r]=0;
else
{
// modific toate cifrele odata daca k impar,
// altfel cifrele raman la fel
if(k%2==1)
for(j=1; j<=nr; j++)
if(u[j]==0)
u[j]=1;
else
u[j]=0;
if(i>nr)
v[r]=0;
else
{
p=1;
x=0;
for(j=nr; j>=i; j--)
{
x=x+p*u[j];
p<<=1;
}
v[r]=x;
}
}
}
secventa();
return 0;
}
char A[1000000][32],x2[32];
int main()
{
ifstream in("mirror.in");
ofstream out("mirror.out");
int c,k,a[30][33],s,nc,max=0;
long n,x,r,p2,p,lmax,psf,psfmin,nr,i,j,l;
in>>c; in>>n>>k;
if(c==1)
{ //cer 1
p2=1;
for(i=1;i<=k-1;i++)
p2=p2*2;
nr=0;
r=0;
p=p2;
nc=0;
while(nr<n)
{
s=0; i=0;
CAPITOLUL 6. ONI 2017 6.2. MIRROR 117
a[i][0]=j;
s=s+j;
i++;
}
x=r;
for(l=0;l<i;l++)
for(j=a[l][0];j>0;j--)
{
x=x+a[l][j]*p;
p=p/2;
nc++;
if(nc==k)
{
out<<x<<" ";
x=0;
p=p2;
nc=0;
}
}
r=x;
}
}
else
{ //cer 2
for(i=0;i<n;i++)
{
in>>x; nc=0;
if(x==0)
nc=1;
while(x!=0)
{
x2[nc++]=x%2;
x=x/2;
}
for(j=1;j<=k;j++)
{
r=j%2;
nc--;
if(nc>=1)
while(x2[nc-1]==r && nc>0)
nc--;
}
if(nc>max)
max=nc;
for(j=0;j<nc;j++)
{
if(k%2==1)
A[i][j]=!x2[j];
else
A[i][j]=x2[j];
}
}
if(A[i][j]==1)
l++;
else
{
if(l>lmax)
{
lmax=l;
psf=i;
psfmin=i;
}
if(l==lmax)
psf=i;
if(psf<psfmin)
psfmin=psf;
l=0;
}
}
i=i-1;
if(A[i][j]==1 && l>lmax)
{
lmax=l;
psf=i+1;
psfmin=psf;
}
}
in.close();
out.close();
return 0;
}
int C, N, K;
int v[MAX_N];
int main()
{
ifstream cin("mirror.in");
ofstream cout("mirror.out");
if(C == 1)
{
int currentValue = 0;
int nrBits = 0;
++nrBits;
if(nrBits == K)
{
cout << currentValue << " ";
currentValue = 0;
nrBits = 0;
}
}
}
int maxBit = 0;
for(int bit = 31; bit >= 0; --bit)
{
if(v[i] & (1 << bit))
{
maxBit = bit;
break;
}
}
return 0;
CAPITOLUL 6. ONI 2017 6.2. MIRROR 120
FILE *fin=fopen("mirror.in","r");
ofstream fout("mirror.out");
int v[1000005],n,ct,k;
int w[33];
int cautare_binara(int x)
{
int st=1,dr=31,mij;
if(x>=w[31])
return w[31];
if(x<=w[1])
return w[1];
while(st<=dr)
{
mij=(st+dr)>>1;
if(w[mij]==x)
return x;
if(x>w[mij] && x<=w[mij+1])
return w[mij+1];
if(x<w[mij])
dr=mij-1;
else
st=mij+1;
}
return -1;
}
int transforma(int x)
{
int y=cautare_binara(x);
y=(xˆy);
return y;
}
void rezolva1()
{
int masca=0,i,j,y=1<<k-1,nr,x=0;
for(i=1; i<=n; i++)
{
nr=v[i];
if(masca==0)
masca=1;
while(masca<nr)
masca<<=1;
if(masca>nr)masca>>=1;
while(masca)
{
if(masca&nr)
x=x+y;
y/=2;
if(y==0)
{
fout<<x<<" ";
y=1<<k-1;
x=0;
}
masca>>=1;
}
}
CAPITOLUL 6. ONI 2017 6.2. MIRROR 121
fout<<’\n’;
}
dr=ddr;
st=ddr-lmax+1;
return lmax;
}
void secventa()
{
int st=0,dr=0,i,lung,lmax=0,sst,ddr;
i=1;
while(v[i]==0&&i<=n)
i++;
if(i<=n)
for( ; i<=n; i++)
if(v[i]!=0)
if(st==0)
st=dr=i;
else
dr++;
else
{
if(st)
{
lung=lungime(st,dr);
if(lung>lmax)
{
lmax=lung;
sst=st;
ddr=dr;
}
st=0;
}
}
if(dr)
{
lung=lungime(st,dr);
if(lung>lmax)
{
lmax=lung;
sst=st;
ddr=dr;
CAPITOLUL 6. ONI 2017 6.2. MIRROR 122
}
}
fout<<lmax<<’\n’<<sst<<" "<<ddr<<’\n’;
}
int main()
{
int x=1,i,j,masca;
for(i=1; i<=31; i++)
{
x=x<<1;
w[i]=x-1;
}
fscanf(fin,"%d%d%d",&ct,&n,&k);
for(i=1; i<=n; i++)
fscanf(fin,"%d",&v[i]);
if(ct==1)
{
rezolva1();
return 0;
}
secventa();
return 0;
}
FILE *fin;
ofstream fout("mirror.out");
int posd(int x)
{
///d[i] este cea mai mica putere a lui 2 care depaseste pe x
int i=0;
while(d[i]<=x)
i++;
return i;
}
int main()
{
int i,q,p,j,jmax,l,pmax;
d[0]=1;
for(i=1;i<=32;i++)
{
CAPITOLUL 6. ONI 2017 6.2. MIRROR 123
d[i]=d[i-1]*2;
}
fin=fopen("mirror.in","rt");
fscanf(fin,"%d %d %d",&C,&N,&K);
if(C==1)
{
fscanf(fin,"%d",&z);
x=z;
p=posd(x);
afisareK(x,p);
for(i=2;i<=N;i++)
{
fscanf(fin,"%d",&z);
y=z;
q=posd(y);
x=x*d[q]+y;
p+=q;
afisareK(x,p);
}
}
else
{
for(j=0;j<33;j++)
{
posL[j]=0;
maxL[j]=0;
}
pmax=0;
for(i=1;i<=N;i++)
{
fscanf(fin,"%d",&z);
x=z;
for(j=0;j<=31;j++)
{
v[j]=x%2;
x=x/2;
if(v[j]==1){
p=j;
}
}
if(p>pmax)pmax=p;
for(l=1;l<=K && p>=0;l++)
{
while(p>=0 && v[p]==l%2)
{
v[p]=0;
p--;
}
}
for(j=0;j<=pmax;j++)
{
q=v[j];
if(K%2) q=1-q;
if(j>p) q=0;
if(q==1)
{
L[j]++;
if(L[j]==1)pos[j]=i;
if(L[j]>maxL[j])
{
maxL[j]=L[j];
posL[j]=pos[j];
}
}
else
{
L[j]=0;
}
}
}
jmax=0;
for(j=0;j<=pmax;j++)
CAPITOLUL 6. ONI 2017 6.2. MIRROR 124
{
if((maxL[j]>maxL[jmax])||
(maxL[j]==maxL[jmax] && posL[j]<posL[jmax]))
{
jmax=j;
}
}
fout<<maxL[jmax]<<"\n";
fout<<posL[jmax]<<" "<<posL[jmax]+maxL[jmax]-1<<"\n";
}
fclose(fin);
fout.close();
return 0;
}
int c, n, k;
vector<int> el;
void Solve1()
{
vector<bool> bits;
for (auto itr : el)
{
int p = 30;
for (; p >= 0; p -= 1)
if ((1 << p) & itr)
break;
if (p == -1)
p = 0;
for (; p >= 0; p -= 1)
bits.push_back(!!((1 << p) & itr));
}
ofstream cout("mirror.out");
return;
}
if (p == -1)
p = 0;
vector<bool> bits;
for (int i = 0; i <= p; i += 1)
CAPITOLUL 6. ONI 2017 6.2. MIRROR 125
k = k & 1;
int x = 0;
while (bits.size())
{
x *= 2;
x += k ˆ bits.back();
bits.pop_back();
}
a = x;
}
void Solve2()
{
int mx = -1;
int where = 0;
right++;
}
ofstream cout("mirror.out");
return;
}
int main()
{
ifstream cin("mirror.in");
cin >> c >> n >> k;
el.resize(n);
if (c == 1)
Solve1();
else
Solve2();
return 0;
}
CAPITOLUL 6. ONI 2017 6.3. OKCPP 126
6.3 okcpp
Problema 3 - okcpp 100 de puncte
Despre numărul natural N spunem că are proprietatea okcpp dacă oricum alegem K cifre ale
sale vom găsi printre ele cel puţin P cifre distincte (oricare k cel puţin p).
Cerinţe
(1) Fiind date numerele naturale K, P , A şi B să se calculeze şi să se afişeze numărul de
numere okcpp din intervalul A, B .
(2) Fiind date numerele naturale K, P şi N să se calculeze şi să se afişeze cel mai mic număr
okcpp care este mai mare sau egal cu N .
Date de intrare
Date de ieşire
Dacă C 1, atunci ı̂n fişierul de ieşire okcpp.out se va scrie numărul de numere okcpp din
intervalul A; B .
Dacă C 2, atunci ı̂n fişierul de ieşire okcpp.out se va scrie cel mai mic număr natural okcpp
care este mai mare sau egal cu N .
a 1 & P & 10
a P & K & numărul de cifre al lui N & 18
a Pentru 20% din teste cerinţa va fi C 1
Pentru cerinţa C 1 vom avea 0 & A $ B $ 10 şi B A & 10000
18
a
a Pentru cerinţa C 2 se garantează că există ı̂ntotdeauna soluţie
Exemple:
okcpp.in okcpp.out
Explicaţii
1 3 Avem K 4 şi P 2. ı̂n intervalul [99997;100001] sunt trei
5 2 99997 100001 numere okcpp: 99997, 99998 şi 100001.
2 100023 Avem K 5, P 3 şi N 99997. Se observă uşor că
5 3 99997 numerele 99997, 99998 , ..., 100022 nu corespund. Primul
număr care corespunde cerinţelor este 100023.
Timp maxim de executare/test: 0.1 secunde
Memorie: total 32 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 10 KB
ifstream fin("okcpp.in");
ofstream fout("okcpp.out");
int C,T,K,P;
long long A,B,X,N;
int v[10002], nv, x[10002], nx;
do
{
ok=1;
for(i=1;i<nw;i++)
if(w[i]<w[i+1])
ok=0; aux=w[i]; w[i]=w[i+1]; w[i+1]=aux;
} while(!ok);
s1=0;
for(i=1;i<=P-1;i++) s1=s1+w[i];
if((nw>=P-1 && s1<=K-1)||(nw<P-1 && s1+P-1-nw<=K-1))
{
s2=0;
CAPITOLUL 6. ONI 2017 6.3. OKCPP 128
for(i=P;i<=9;i++) s2=s2+w[i];
w[0]=1000;
s3=s1;
i=min(nw+1,P-1);
h=w[i];
while(i>=1 && (K-1-s3)/(P-i)+h>=w[i-1])
{
r=(K-1-s3)/(P-i);
if(r+h>w[i-1]) r=w[i-1]-h;
s3=s3+r*(P-i);
i--;
h=w[i];
}
r=(K-1-s3)/(P-i);
if(r+h>w[i-1]) r=w[i-1]-h;
h=h+r;
s3=s3+r*(P-i);
z=(K-1-s3)%(P-i)+s3+h*(10-(P-1));
if(z<nv) return 0;
return 1;
}
else
return 0;
}
int main()
{
int i,j,l,ok,r,i1,i2,ok1,aux,c;
fin>>C;
if(C==1)
{
fin>>K>>P>>A>>B;
c=0;
for(N=A;N<=B;N++)
{
X=N;
nv=0;
do
{
v[++nv]=X%10;
X=X/10;
} while(X);
for(i=1;i<=nv/2;i++)
{
aux=v[i];
v[i]=v[nv+1-i];
v[nv+1-i]=aux;
}
if(P==1 || verificare(v,nv)==1)
c++;
}
fout<<c;
}
if(C==2)
{
fin>>K>>P>>N;
X=N;
nv=0;
do
{
v[++nv]=X%10;
X=X/10;
} while(X);
for(i=1;i<=nv/2;i++)
{
aux=v[i];
v[i]=v[nv+1-i];
v[nv+1-i]=aux;
CAPITOLUL 6. ONI 2017 6.3. OKCPP 129
ok=0;
if(P==1 || verificare(v,nv)==1)
{
fout<<N<<"\n";
ok=1;
}
if(ok==0)
{
nx=0;
for(i=1;i<=nv;i++)
if(v[i]<9)
nx++;x[nx]=i;
i1=1; i2=nx;
while(i1<=i2)
{
i=(i1+i2)/2;
///pastrez prefix de lungime i, crescand a i-a cifra
ok1=0;
aux=v[x[i]];
for(l=v[x[i]]+1;l<=9 && 0==ok1;l++)
{
v[x[i]]=l;
if(verificare(v,x[i])==1)
ok1=1;
}
v[x[i]]=aux;
if(ok1==1)
i1=i+1;
else
i2=i-1;
}
i=x[i2];
for(l=v[i]+1;l<=9 && i>=1 && 0==ok;l++)
{
v[i]=l;
if(verificare(v,i)==1)
{
for(r=i+1;r<=nv;r++)
{
v[r]=0;
while(verificare(v,r)==0)
v[r]++;
}
ok=1;
for(j=1;j<=nv;j++)
fout<<v[j];
fout<<"\n";
}
}
}
if(ok==0)
{
v[1]=1;
if(verificare(v,1)==1)
{
nv++;
for(r=2;r<=nv;r++)
{
v[r]=0;
while(verificare(v,r)==0)
v[r]++;
}
ok=1;
for(j=1;j<=nv;j++)
fout<<v[j];
fout<<"\n";
CAPITOLUL 6. ONI 2017 6.3. OKCPP 130
}
}
///if(ok==0) fout<<-1<<"\n";
}
fout.close();
fin.close();
return 0;
}
vector<int> C(10);
for (char d : S) C[d - ’0’]++;
int i = S.length() - 1;
bool found = false;
for (; i >= 0; i--)
{
C[S[i] - ’0’]--;
for (char d = S[i] + 1; d <= ’9’; d++)
{
C[d - ’0’]++;
if (canFill(C, K, P, S.length() - i - 1))
{
found = true;
S[i] = d;
break;
}
C[d - ’0’]--;
}
if (found) break;
}
if (i < 0)
{
S = string(S.length() + 1, ’0’);
S[0] = ’1’;
i = 0;
C[1]++;
if (!canFill(C, K, P, S.length() - 1)) return "-1";
CAPITOLUL 6. ONI 2017 6.3. OKCPP 131
return S;
}
int main()
{
ifstream f("okcpp.in");
ofstream g("okcpp.out");
int C, K, P, ns;
long long A, B, X;
string S;
f >> C ;
if(C == 1)
{
f >> K >> P >> A >> B;
ns = 0;
for( X = A; X <= B ; X ++)
{
S = to_string(X);
if(isGood(S, K, P))
{
ns ++;
}
g << ns << endl;
}
}
else
{
f >> K >> P >> S;
g << findNext(S, K, P) << endl;
}
return 0;
}
#include <cassert>
#include <algorithm>
#include <iostream>
#include <vector>
return start;
}
CAPITOLUL 6. ONI 2017 6.3. OKCPP 132
vector<int> ans;
if (k > 0)
return true;
else
return false;
}
ok = true;
ans = digits;
return ;
}
return;
}
if (ok)
{
digit = current_digit;
return;
}
}
return;
}
{
vector<int> digits(10, 0);
for (char itr : n)
digits[itr - ’0’] += 1;
if (IsOk(digits, k, p))
return n;
int to_match = 0;
while (n.size())
{
to_match += 1;
digits[n.back() - ’0’] -= 1;
bool ok = false;
int digit = 0;
SolveFixed(digits, n.back() - ’0’, k, p, to_match, ok, digit);
n.pop_back();
if (ok)
{
digits[digit] += 1;
n += char(’0’ + digit);
return AddStuffAtEnd(n, digits, ans);
}
}
bool ok = false;
int digit = 0;
SolveFixed(digits, -1, k, p, to_match, ok, digit);
if (ok)
{
digits[digit] += 1;
n = "1";
n += char(’0’ + digit);
return AddStuffAtEnd(n, digits, ans);
}
else
return "-1";
}
return a;
}
int main()
{
freopen("okcpp.in", "r", stdin);
freopen("okcpp.out", "w", stdout);
int c;
cin >> c;
if (c == 2)
{
CAPITOLUL 6. ONI 2017 6.4. ADLIC 134
int k, p;
string n;
cin >> k >> p >> n;
cout << Solve(k, p, n) << ’\n’;
}
else
{
int k, p;
int64 left, right;
cin >> k >> p >> left >> right;
cout << Solve1(k, p, left, right) << ’\n’;
}
return 0;
}
6.4 adlic
Problema 4 - adlic 100 de puncte
Pentru următorul an şcolar admiterea celor N elevi ı̂n liceu se va face pe baza unor evaluări
complexe.
Fiecare dintre viitorii elevi ai clasei a IX-a va primi, ı̂n urma testelor şi probelor pe care le va
sus ţine, un punctaj (număr natural nenul) cu care va participa la admiterea electronică.
Repartizarea fiecărui elev ı̂n clase se face ı̂n ordinea ı̂nscrierii respectând criteriile:
a Primul elev se repartizează ı̂n clasa cu numărul de ordine 1.
a ı̂n clasa ı̂n care este repartizat un elev nu există, până la momentul repartizării sale, nici un
punctaj mai mare decât al său.
a Numărul claselor să fie cât mai mic posibil.
Cerinţe
Determinaţi:
1. Punctajul primului elev care nu ar mai putea fi repartizat ı̂n prima clasă ı̂n condiţiile ı̂n
care toţi elevii ı̂şi doresc să fie repartizaţi ı̂n prima clasă (se aplică doar la cerinţa 1).
2. Numărul claselor ce se vor forma respectând criteriile.
Date de intrare
Fişierul de intrare adlic.in conţine pe primul rând numărul C a cărui valoare poate fi 1 sau
2, apoi separat de un spaţiu numărul natural N .
Pe liniile următoare se găsesc cele N punctaje ale elevilor ı̂n ordinea ı̂nscrierii, numere naturale
nenule despărţite prin câte un spaţiu.
Date de ieşire
Exemple:
CAPITOLUL 6. ONI 2017 6.4. ADLIC 135
adlic.in adlic.out
Explicaţii
19 2 4 se repartizează ı̂n prima clasă, iar 2 trebuie repartizat ı̂n
4 2 4 2 7 10 9 11 8 cea de-a doua clasă
29 3 O soluţie posibilă este cea ı̂n care se formează clasele:
4 2 4 2 7 10 9 11 8 4479
2 2 10 11
8
Timp maxim de executare/test: 1.5 secunde
Memorie: total 32 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 10 KB
1. Se parcurge fişierul adlic.in determinându-se primul termen mai mic decât predecesorul
său - complexitate O n
2. Se formează vectorul v ı̂n care se reţine ultimul element al subşirurilor crescătoate. V este
creat descrescător. La citirea din fişier elementul curent actualizează v ı̂nlocuind primul element
mai mic decât el. Dacă un asemenea element nu există se crează o nouă poziţie ı̂n v. Căutarea ı̂n
2
v se face binar. Complexitate nlog n. În cazul căutării ı̂n n se obţine punctaj parţial.
ifstream f("adlic.in");
ofstream g("adlic.out");
f>>w;
k=Bins(1,n,w);
if(k==n+1)
v[++n]= w;
else
v[k]=w;
g<<n;
}
return 0;
}
int N, cerinta;
int punctaje[MAX_N], ultim[MAX_N];
int main()
{
ifstream f("adlic.in");
ofstream g("adlic.out");
if(cerinta == 1)
{
int maxP = 0;
int ans = 0;
for(int i = 1; i <= N; ++i)
if(punctaje[i] < punctaje[i - 1])
{
ans = punctaje[i];
break;
}
if(clasa)
ultim[clasa] = punctaje[i];
else
{
++nrClase;
ultim[nrClase] = punctaje[i];
CAPITOLUL 6. ONI 2017 6.4. ADLIC 137
}
}
f.close();
g.close();
return 0;
}
long mx[1000000];
int main()
{
ifstream in("adlic.in");
ofstream out("adlic.out");
int V,rep;
long p,i=0,k,st,dr, max, m; //j;
in>>V;
if(V==1) // cerinta 1
{
in>>p; max=p; k=1;
while(!in.eof())
{
in>>p;
if(p>=max)
max=p;
else
{
out<<p;
break;
}
}
//cout<<k;
}
else // cerinta 2
{
k=0;
while(in>>p)
{
st=1;dr=k;
while(st<=dr)
{
m=(st+dr)/2;
if(p>=mx[m])
dr=m-1;
else
st=m+1;
}
if(st>k)
{
k++;
mx[k]=p;
}
else
{
mx[st]=p;
}
}
out<<k;
// cout<<k;
}
CAPITOLUL 6. ONI 2017 6.5. BOMBOANE 138
in.close();
out.close();
return 0;
}
6.5 bomboane
Problema 5 - bomboane 100 de puncte
Zeno are n cutii cu bomboane, iar ı̂n fiecare cutie se găseşte un număr natural nenul de
bomboane. Zeno poate ı̂mpărţi bomboanele din toate cutiile colegilor ı̂n două moduri: frăţeşte
sau diferenţiat. ı̂mpărţirea frăţească se realizează astfel:
- numărul de colegi care primesc bomboane din fiecare cutie este acelaşi (dacă din prima cutie
primesc bomboane k colegi şi din cutia 2 vor primi tot k colegi, şi din cutia 3 tot k colegi etc).
- bomboanele din fiecare cutie se ı̂mpart ı̂n mod egal ı̂ntre cei k colegi, aceştia primind un
număr nenul de bomboane;
- ı̂n final ı̂n fiecare cutie trebuie să rămână un număr identic de bomboane (posibil zero) care
ı̂i revin lui Zeno. De exemplu dacă n 3, iar ı̂n cutii se găsesc 14, 23 respectiv 17 bomboane, din
prima cutie oferă câte 4 bomboane pentru 3 colegi, din a doua cutie câte 7 bomboane pentru 3
colegi, iar din ultima cutie câte 5 bomboane pentru 3 colegi, iar ı̂n fiecare cutie rămân 2 bomboane.
ı̂mpărţirea diferenţiată se realizează ı̂n felul următor:
- dintre colegii care primesc bomboane din aceeaşi cutie fiecare coleg primeşte un număr diferit
de bomboane (număr nenul), neexistând doi colegi care primesc număr identic de bomboane din
aceeaşi cutie;
- din fiecare cutie Zeno oferă bomboane unui număr cât mai mare de colegi.
- diferenţele ı̂n modul dintre numărul de bomboane primite consecutiv de doi colegi sunt
distincte două câte două. De exemplu dacă n 3, iar ı̂n cutii se găsesc 14, 23 respectiv 17
bomboane, bomboanele din prima cutie se pot ı̂mpărţi astfel (3, 4, 6, 1), bomboanele din a doua
cutie (6, 2, 7, 1, 3, 4), iar bomboanele din a treia cutie se pot ı̂mpărţi astfel (2, 1, 3, 7, 4).
Cerinţe
Cunoscând n numărul de cutii şi numărul de bomboane din fiecare cutie să se scrie un program
care determină:
a) Numărul maxim de colegi care pot primi bomboane, dacă Zeno alege ı̂mpărţirea frăţească.
b) O modalitate de ı̂mpărţire a bomboanelor din fiecare cutie, dacă se face ı̂mpărţirea
diferenţiată.
Date de intrare
Fişierul de intrare bomboane.in conţine pe prima linie două numere naturale p (numărul
cerinţei de rezolvat), şi n (numărul de cutii), despărţite printr-un spaţiu. Pe următoarea linie se
găsesc n valori naturale, separate prin câte un spaţiu, reprezentând numărul de bomboane din
fiecare cutie.
Date de ieşire
Dacă p 1 se va rezolva numai punctul a) din cerinţă. În acest caz fişierul de ieşire bom-
boane.out va conţine o valoare naturală reprezentând numărul maxim de colegi care pot primi
bomboane, dacă Zeno alege ı̂mpărţirea frăţească.
Dacă p 2 se rezolvă numai punctul b). Fişierul de ieşire bomboane.out va conţine n linii. Pe
fiecare linie i, prima valoare nri reprezintă numărul maxim de colegi care pot primi bomboane din
cutia i, urmată de nri valori separate prin câte un spaţiu reprezentând o modalitate de ı̂mpărţire
a bomboanelor din cutia i, dacă Zeno alege ı̂mpărţirea diferenţiată.
a 1 & p & 2;
Dacă p 1 atunci 1 & n & 10 000 şi 1 & numărul de bomboane din cutii & 10 .
6
a
a Dacă p 2 atunci 1 & n & 200 şi 1 & numărul de bomboane din cutii & 100 000.
a Dacă există mai multe soluţii se poate afişa oricare.
a Pentru rezolvarea fiecărei cerinţe se acordă 50% din punctaj.
Exemple:
bomboane.in bomboane.outExplicaţii
13 3 Se rezolvă numai punctul a). Numărul maxim de colegi care
14 23 17 pot primi bomboane dacă Zeno alege ı̂mpărţirea frăţească e 3.
2 3 14 23 17 43461 Se rezolvă numai punctul b). Din prima cutie pot primi bom-
6 6 2 7 1 3 4 boane maxim 4 colegi. O modalitate de ı̂mpărţire astfel ı̂ncât
521374 fiecare coleg să primească un număr diferit de bomboane, iar
diferenţele dintre bomboanele primite de doi colegi consecutivi
să fie distincte două câte două este (3,4,6,1). Este corectă şi
soluţia (1, 2, 7, 4).
Timp maxim de executare/test: 0.2 secunde
Memorie: total 16 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 10 KB
a) ı̂mpărţirea frăţească
fie a[i] - numarul de bomboane din cutia i
Metoda 1 - 10% din punctajul cerinţei
Se generează toate valorile pentru numărul de colegi şi se verifică dacă numărul de bomboane
din fiecare cutie ı̂mpărţit la numărul de colegi dă acelaşi rest.
Complexitate O(N*max(a[i])
Metoda 2 - 30% din punctajul cerinţei
Se observă că numărul de bomoane care pot rămâne ı̂n fiecare cutie este cuprins ı̂ntre 0 şi
min(a[i]).
Complexitate O(N*min(a[i])
Metoda 3 - 100% din punctaj
fie a[i] - numarul de bomboane din cutia i
Trebuie determinat un numar k astfel incat fiecare a[i] sa dea acelasi rest la impartirea la k.
a[1] = x1 * k + r
a[2] = x2 * k + r
......
a[n] = x3 * k + r
Daca facem diferenta intre oricare doua elemente ale vectorului a[i] si a[j] obtinem:
a[i] - a[j] = xi * k + r - (xj * k + r) = (xi - xj) * k
Prin urmare diferenta intre doua elemente ale vectorului este multiplu de k.
Pentru k maxim acesta va fi cmmdc(a[1] - a[2], a[2] - a[3], a[3] - a[4] ... a[n-1] - a[n]).
Un algoritmul de rezolvare:
- pentru a evita valori negative se determina min = min(a[1], a[2] ... a[n])
- se scade min din elementele vectorului a[1] = a[1] - min, a[2] = a[2] - min ... a[n] = a[n] - min
- se determina k = cmmdc(a[1], a[2] ... a[n]).
b) ı̂mpărţirea diferenţiată
Fie ci numarul maxim de colegi care pot primi bomboane din cutia i
Pentru a determina ci se determina cea mai mare valoare pentru care
ci * (ci + 1) / 2 ¡= a[i]
Metoda 1 - 30% din punctaj
Se calculează numărul maxim de elevi care pot primi bomboane dintr-o cutie. Se observă că
pentru ca acest număr să fie maxim trebuie găsiţi cei mai mici termeni, respectiv 1, 2, 3 ... Se
ı̂ncearcă formarea unui vector cu numărul de bomboane distribuit. Se procedează asemănător
CAPITOLUL 6. ONI 2017 6.5. BOMBOANE 140
sortării prin inserţie. Se adaugă ultimul termen la sfârşitul vectorului. Dacă se obţine o diferenţă
deja creată, acesta se deplasează spre ı̂nceputul vectorului până când diferenţele sunt distincte.
Metoda 2
Cea mai simplă modalitate de a afişa o ı̂mpărtire este
1 ci 2 ci-1 3 ci-2 .... (de exemplu pt 10 bomboane 1, 4, 2, 3)
La afişarea primei perechi trebuie avut ı̂n vedere faptul că dacă ci * (ci + 1) ¡ a[i] atunci se
afişează 1 şi ci + diferenţa (a[i] - ci * (ci+1) / 2
De exemplu pentru a[i] = 10 se determină ci = 4.
Împărţirea diferenţiată 1 4 2 3
Pentru a[i] = 12 ı̂mpărţirea diferenţiată 1 6 2 3 (prima pereche 1 şi 4 + 12 - 10)
Algoritmul
Datorită faptului că la fiecare pas diferenţa ı̂ntre 2 elemente consecutive scade, se respectă
cerinţa ca elementele consecutive să aibă diferenţa distinctă.
ifstream in("bomboane.in");
ofstream out("bomboane.out");
int n,p,a[10002],i,k;
int nr_max_col(int n)
{
//determinam valoarea maxima pentru k, a.i k * (k + 1) / 2 <= n
int k;
for (k = 1; ; k++)
if (k * (k + 1) / 2 > n)
return k-1;
}
int main()
{
in >> p >> n;
CAPITOLUL 6. ONI 2017 6.5. BOMBOANE 141
if(p == 1)
{
int vmin = a[1];
out << k;
}
else
{
int l, r;
for(i = 1; i <= n; i++){
k = nr_max_col(a[i]); // determinam valoarea maxima pentru k,
// a.i k * (k + 1) / 2 <= a[i]
l = 1; // se porneste cu 2 indici l = 1
r = k; // si r = k si se afiseaza alternativ
l = l + 1;
r = r - 1;
}
return 0;
}
return a;
}
int main()
{
ifstream in("bomboane.in");
ofstream out("bomboane.out");
int p,n,i,k=0,j,nb;
long b[10001], c[10001], min, s, r, st, dr;
in>>p>>n;
// cout<<n<<endl;
if(p==1) //cerinta 1
{
in>>b[1]; min=b[1];
for(i=2;i<=n;i++)
{
in>>b[i];
if(b[i]<min)
min=b[i];
}
for(i=1;i<=n;i++)
b[i]=b[i]-min;
i=1;
while(b[i]==0)
i++;
k=b[i];
for(i=2;i<=n;i++)
if(b[i]!=0)
k=cmmdc(k,b[i]);
out<<k;
}
else //cerinta 2
{
for(i=1;i<=n;i++)
{
in>>b[i];
c[i]=sqrt(2*b[i]);
if(c[i]*(c[1]+1)>2*b[i])
c[i]--;
s=0;
j=0;
do
{
j++;
s=s+j;
} while(s<=b[i]);
if(s==b[i])
r=j;
else
{
s=s-j;
j--;
r=j+b[i]-s;
}
st=1;
dr=j;
p=1;
out<<j<<’ ’;
while(st<=dr)
{
if(p==1)
out<<1<<’ ’<<r<<’ ’;
else
if(st==dr)
out<<st<<’ ’;
else
out<<st<<’ ’<<dr<<’ ’;
st++;
dr--;
p++;
CAPITOLUL 6. ONI 2017 6.5. BOMBOANE 143
out<<endl;
}
}
in.close();
out.close();
return 0;
}
ifstream fin("bomboane.in");
ofstream fout("bomboane.out");
int p,n,a[10005];
void impf()
{
int amin=a[1], i, k;
for(i=2;i<=n;i++)
amin=min(amin,a[i]);
k=a[1]-amin;
for(i=2;i<=n;i++)
k=cmmdc(k,a[i]-amin);
fout<<k;
}
void d(int s)
{
int a,i,r=(sqrt(8*s+1)-1)/2;
a=(r+1)/2;
fout << r << " " << a << " ";
s=s-a;
for(i=1;i<=(r-2)/2;i++,s=s-2*a)
{
fout << a+i << " " << a-i << " ";
}
if(r%2==0)
fout << s << "\n";
else
fout << s-1 << " 1\n";
}
void impd()
{
for(int i=1;i<=n;i++)
d(a[i]);
}
int main()
{
fin>>p>>n;
for(int i=1;i<=n;i++)
fin>>a[i];
if(p==1)
impf();
else
impd();
fout.close();
CAPITOLUL 6. ONI 2017 6.6. ORASE 144
return 0;
}
6.6 orase
Problema 6 - orase 100 de puncte
În tărâmul Jupânului există N + 1 oraşe. Acestea au fost construite ı̂n linie dreaptă, ı̂ncepând
cu cel ı̂n care este casa Jupânului. Între oricare 2 oraşe consecutive s-a construit câte un drum.
Pentru fiecare drum, se cunoaşte lungimea lui, exprimată ı̂n metri şi viteza cu care se poate
parcurge, exprimată ı̂n metri pe secundă.
Cerinţe
Date de intrare
Date de ieşire
Fişierul de ieşire orase.out va conţine pe prima linie un număr natural R ce reprezintă partea
ı̂ntreagă a timpului minim de parcurgere a distanţei dintre oraşul 0 şi oraşul N .
1 & N & 5 10
4
a
a 1 & X & 10
7
4
a lungimea drumului dintre oricare 2 oraşe este un număr natural din intervalul 1, 10
4
a viteza iniţială dintre oricare 2 oraşe consecutive este un număr natural din intervalul 1, 10
Tipul 1: pentru 5% din punctaj N & 10 şi X & 10
Tipul 2: pentru alte 10% din punctaj N & 10 şi X & 10
3 3
Tipul 3: pentru alte 15% din punctaj 1 & N & 5 10 , 1 & X & 10 , distanţele sunt mai mici
4 4
decât 200 şi se garantează că vitezele finale vor fi mai mici sau egale decât 1000
Tipul 4: pentru alte 20% din punctaj 1 & N & 5 10 , 1 & X & 10 şi toate distanţele sunt
4 7
egale
Tipul 5: pentru restul de 50% din punctaj 1 & N & 5 10 şi 1 & X & 10
4 7
Exemple:
CAPITOLUL 6. ONI 2017 6.6. ORASE 145
Velea Alexandru
Structura solutiei
1) Observaţii generale
2) Soluţie ı̂n O N X - 15 puncte
3) Soluţie restricţii de tipul 3 - 15 puncte
4) Soluţie restricţii de tipul 4 - 20 puncte
5) Soluţia oficială - 100 puncte
Pentru un drum care are lungime D şi viteza V, timpul de deplasare este de D/V
Dacă mărim viteza drumului de la V la (V+1), timpul de deplasare devine D/(V+1)
Astfel, timpul salvat ı̂n urma măririi vitezei este de (D/V) - (D/(V+1)) = D/(V*(V+1))
Se observă că prima ı̂mbunătăţire salvează D/(V*(V+1)) secunde, a doua ı̂mbunătăţire
D/((V+1)*(V+2)), etc
Astfel, fiecare ı̂mbunătăţire salvează din ce ı̂n ce mai puţin timp.
2) Soluţie ı̂n O N X
Folosind observaţiile de la punctul 1, putem alege o strategie greedy de ı̂mbunătăţire a dru-
murilor.
Dacă am avea de făcut o singură ı̂mbunătăţire, am alege să o facem pe drumulul unde aceasta
ar salva cât mai mult timp.
Acesta fiind drumul cu D/(V*(V+1)) maxim.
Astfel, putem demonstra că tot timpul este bine să alegem să ı̂mbunătăţim drumul care ne
salvează cât mai mult timp.
Dacă am avea de făcut doar o ı̂mbunătăţire, e bine să alegem acel drum. După ce l-am ales,
următoarea ı̂mbunătăţire va fi mai mică decât cea precedentă, iar acest lucru ne garantează faptul
că dacă am putea să facem acest lucru acum, nu am avea niciun motiv să facem ı̂mbunătăţirea pe
drumul respectiv mai tarziu.
Iteram de X ori peste toate drumurile, la fiecare pas alegem drumul care salvează cel mai mult
timp şi ı̂l ı̂mbunătăţim.
3) Soluţia pentru tipul 3
Observăm că dacă am vrea să ı̂mbunătăţim un drum care are distanţa D, este cel mai bine să
ı̂mbunătăţim drumul care are viteza minimă, pentru a maximiza timpul câştigat.
Astfel, faptul că distanţele sunt mai mici decât 200, permite o optimizare a algoritmului de la
pasul 2.
În loc să iterăm la fiecare pas prin toate drumurile şi să ı̂l alegem pe cel care salvează cât mai
mult timp, putem itera prin distanţele drumurilor, aflăm care este viteza minimă a unui drum
cu distanţa respectivă, iar mai apoi ı̂mbunătăţim unul dintre drumurile care aveau viteza minimă
pentru distanţa fixată (este posibil să fie mai multe astfel de drumuri)
Astfel, fiecare ı̂mbunătăţire se poate realiza ı̂n 200 de iteraţii, nu ı̂n N (50 000).
CAPITOLUL 6. ONI 2017 6.6. ORASE 146
Pentru a afla drumul de viteză minimă pentru o distanţă fixată, putem să reţinem drumurile
ı̂ntr-o matrice de frecvenţe
drumuri[d][v] reprezentând câte drumuri au distanţa d şi viteza v
mai ţinem un şir viteza minima[d] reprezentând cea mai mică viteză a unui drum cu distanţa
d
- echivalent cu prima valoare v pentru care drumuri[d][v] are o valoare nenulă.
Dacă la un pas am ales să ı̂mbunătăţim un drum care are distanţa d şi viteza v, trebuie să
micşorăm numărul de drumuri cu viteza v: drumuri[d][v] -= 1
Mărim numărul de drumuri cu viteza (v + 1): drumuri[d][v + 1] += 1
* dacă nu mai avem drumuri cu viteza v, mărim viteza minima[d]:
if (drumuri[d][v] == 0) { viteza\_minima[d] += 1; }
4) Soluţia pentru tipul 4
Asemănător cu tipul 3, doar că aici distanţele nu sunt până ı̂n 200, ci toate distanţele sunt
egale.
Astfel, ne interesează doar vitezele, distanţa fiind necesară doar la calcularea rezultatului.
O soluţie ar fi să ţinem un şir de frecvenţe pentru fiecare viteză, şi să aplicăm următorul
algoritm:
int i = 1;
while (x)
{
if (frecventa[i] > x)
{
frecventa[i + 1] += frecventa[i];
x -= frecventa[i];
frecventa[i] = 0;
}
else
{
frecventa[i] -= x;
frecventa[i + 1] += x;
x = 0;
}
i += 1;
}
O astfel de soluţie se comportă foarte bine când valoarea lui N este mare, dar dacă N = 1, de
exemplu, ar face X paşi.
Ca să scăpăm de cazul acesta, am putea căuta binar cea mai mică viteză.
Astfel putem afla câte ı̂mbunătăţiri trebuie să facem: suma din(max(0, viteza cautata binar -
viteza[i]))
Dacă numărul de ı̂mbunătăţiri depăşeşte X, reducem viteza, altfel o mărim.
La final, ı̂mbunătăţim toate drumurile ¡= viteza căutată pâna la ea, şi mai ı̂mbunătăţim unele
drumuri cu acea viteză (nu le putem ı̂mbunătăţi pe toate) dacă mai avem ı̂mbunătăţiri rămase.
5) Soluţia oficială
Privind ideile de la punctul 1, am putea ı̂ncerca să căutam binar valoarea ultimului timp salvat.
Notăm ultimul timp salvat cu T
Astfel, am putea calcula pentru un drum de câte ori trebuie să ı̂l ı̂mbunătăţim, ajungând la
viteza V, astfel ı̂ncât ı̂mbunătăţirea de la V la V+1 să fie ¡ T
Acest lucru se poate rezolva tot cu o căutare binară a numărului vitezei, sau folosind o ecuaţie
de gradul 2
Notăm cu D distanţa drumului; trebuie să ı̂i găsim viteza V
D / (V * (V + 1)) < T
D / T < V * (V + 1))
0 < V * V + V - D / T
0 = V * V + V - D / T
delta = 1 + 4 * D / T
V1,2 = (-1 +- sqrt(1 + 4 * D / T)) / 2
#include <cmath>
#include <iomanip>
#include <fstream>
#include <iostream>
#include <queue>
#include <vector>
struct Road
{
int distance;
int speed;
int id;
}
};
int64 x;
vector<Road> roads;
int Read()
{
ifstream cin("orase.in");
int test_type;
cin >> test_type;
int n;
cin >> n >> x;
roads.resize(n);
return 1;
}
return answer;
}
int64 answer = 0;
for (auto itr : upgrades)
answer += itr;
return answer;
}
int64 mx_upgrades = 0;
long double mx_where = 0;
{
long double mid = (right + left) / 2;
int64 num_operations = GetNumOperations(mid);
if (num_operations == x)
{
mx_upgrades = num_operations;
mx_where = mid;
break;
}
if (num_operations > x)
{
left = mid;
int64 sum = 0;
for (int i = 0; i < (int)roads.size(); i += 1)
{
roads[i].speed += upgrades[i];
sum += upgrades[i];
}
x -= sum;
double answer = 0;
for (auto& itr : roads)
answer += itr.Time();
return answer;
}
int main()
{
long double answer = Solve();
ofstream cout("orase.out");
cout << int64(answer) << ’\n’;
return 0;
}
ONI 2016
7.1 civilizatie
Problema 1 - civilizatie 100 de puncte
În vremuri străvechi Pământul era locuit de către o civilizaţie neobişnuită condusă după reguli
matematice foarte riguroase. Această civilizaţie era formată din mai multe oraşe-stat asemeni
oraşelor antice. Fiecare oraş s-a dezvoltat treptat pornind de la un singur cartier de formă pătrată
cu suprafaţa de un hectar, ı̂n jurul căruia se adăugau ı̂n fiecare an cartiere de câte un hectar
fiecare ı̂n felul următor: ı̂n primul an s-a format cartierul iniţial, ı̂n al doilea an oraşul s-a extins
formând patru noi cartiere ı̂n toate cele patru puncte cardinale, ı̂n anul următor oraşul s-a extins
cu 8 noi cartiere dispuse ı̂n jurul cartierelor deja formate, şi aşa mai departe, ı̂n fiecare an oraşul
extinzându-se cu ı̂ncă un rând de cartiere.
Extinderea unui oraş se opreşte când ı̂ntâlneşte un alt oraş sau dacă, deşi nu a ı̂ntâlnit ı̂ncă
un alt oraş, ajunge la marginea hărţii pe oricare dintre cele patru puncte cardinale. Două oraşe
se ı̂ntâlnesc când suprafeţele ocupate de ele ajung să se atingă sau ı̂ntre cartierele marginale ale
celor două oraşe se mai află doar un hectar.
Cerinţe
1. Dimensiunea suprafeţei (ı̂n hectare) pe care ar ocupa-o după t ani, dacă nu ar ı̂ntâlni nici
un alt oraş şi nici nu ar ajunge la marginea hărţii.
150
CAPITOLUL 7. ONI 2016 7.1. CIVILIZATIE 151
2. Timpul scurs până când toate cele N oraşe şi-au ı̂ncetat extinderea, ı̂ncepută din cartierele
iniţiale ale căror coordonate se citesc din fişier, şi aria suprafeţei din hartă rămasă neocupată,
exprimată ı̂n hectare.
Date de intrare
Fişierul de intrare civilizatie.in conţine pe prima linie un număr natural p. Pentru toate
testele de intrare, p poate avea doar valoarea 1 sau valoarea 2.
A doua linie a fişierului conţine două numere naturale x şi y reprezentând dimensiunile hărţii.
A treia linie a fişierului conţine numărul natural t.
A patra linie a fişierului conţine numărul natural N .
Pe următoarele N linii se găsesc câte două numere i şi j reprezentând coordonatele iniţiale ale
celor N oraşe.
Date de ieşire
Exemple:
Cerinţa 1 - Soluţia 1
Considerăm un tablou bidimensional, ı̂n care fiecare hectar este un element al tabloului.
În acest tablou considerăm coordonatele de origine ale oraşului ı̂n mijlocul tabloului.
pentru t=1 coordonatele noilor cartiere sunt chiar coordonatele iniţiale;
pentru t=2 coordonatele noilor cartiere sunt date de: —x-x0—+—y-y0—=1;
pentru t=3 coordonatele noilor cartiere sunt date de: —x-x0—+—y-y0—=2;
...
pentru cazul general —x-x0—+—y-y0—=t-1;
Pentru a calcula dimensiunea suprafeţei parcurgem tabloul bidimensional şi contorizăm ele-
mente care respectă condiţia:
—x-x0—+—y-y0—+—z-z0—¡=t-1.
Se observă că nu avem nevoie efectiva de tabloul tridimensional, pentru că totul se reduce la
relaţii ı̂ntre coordonate.
Cerinţa 1 - Soluţia 2
Analizând structura obţinută observăm că pentru fiecare valoare a lui t se formează structuri
ı̂n formă de romb cu numărul de cuburi egal cu suma termenilor unei progresi aritmetice cu raţia
4:
pentru t=1 avem un singur cub;
pentru t=2 avem cubul anterior ı̂n jurul căruia s-au adăugat ı̂nca 4: 1+4;
pentru t=3 avem cuburile anterioare ı̂n jurul căruia s-au adăugat ı̂nca 8: 1+4+8;
pentru t=4 avem cuburile anterioare ı̂n jurul căruia s-au adăugat ı̂nca 12: 1+4+8+12;
...
În concluzie numărul de cuburi din fiecare romb se poate calcula după formula:
1+4+8+12+16+...+4(k-1)=
1+4(1+2+3+4+...+k-1)=
1+4k(k-1)/2=
1+2k(k-1)
Cerinţa 2 - Soluţia 1
Folosim un tablou bidimensional de dimensiune N N ı̂n care reţinem după câţi ani s-ar
ı̂ntâlnii oraşele două câte două ignorându-le pe toate celelalte (ı̂niţial punctul de ı̂ntâlnire va fi la
jumătatea drumului).
Folosim un tablou unidimensional de dimensiune N ı̂n care reţinem cel mai mic număr de ani
după care un oraş ar ajunge la una dintre margini.
Alegem pe baza acestor două tablouri oraşele care se opresc primele şi recalculăm momentele
de ı̂ntânire ale acestor oraşe cu toate celelalte (ı̂ntâlnirea nu va mai fi pentru toate la jumătatea
drumului).
Reluăm acest procedeu cu cele rămase până când alegem toate civilizaţiile.
3
Complexitate O N
Cerinţa 2 - Soluţia 2
Pentru a reduce timpul de execuţie folosim un tablou unidimensional ı̂n care reţinem minimele
pe linii. Aceste minime le actualizăm atunci când recalculăm momentele de ı̂ntalnire dintre oraşele
alese şi celelalte oraşe.
int main()
{
int p, t;
ifstream cin("civilizatie.in");
ofstream cout("civilizatie.out");
cin >> p;
cin >> maxx >> maxy;
Extend[1] = 1;
for(int i = 2; i < MAXD; ++i)
Extend[i] = Extend[i - 1] + 4 * i - 4;
cin >> t;
if(p == 1)
{
cout << Extend[t];
return 0;
}
else
{
cin >> n;
int cnt = n;
for(now = 1; cnt; ++now)
{
for(int z = 0; z < Meets[now].size(); ++z)
{
Pair p = Meets[now][z];
if(Stopped[p.first] == -1 &&
now == getTime(p.first, p.second))
CAPITOLUL 7. ONI 2016 7.1. CIVILIZATIE 154
{
ToStop.push_back(p.first);
}
}
Stopped[no] = now;
--cnt;
ToStop.clear();
}
return 0;
}
ifstream fin("civilizatie.in");
ofstream fout("civilizatie.out");
for(int i=1;i<=n;i++)
{
lMin[i]=nx+ny;
for(int j=1;j<=n;j++)
{
difMin[i][j]=abs(x[i]-x[j])+abs(y[i]-y[j])+1;
difMin2[i][j]=difMin[i][j]/2;
if(difMin2[i][j]<lMin[i] && i!=j)
{
lMin[i]=difMin2[i][j];jlMin[i]=j;
}
}
}
dMin[i]=difMin2[i][j];
}
nr++;
lMin[i]=nx+ny;
for(int k=1;k<=n;k++) // pt toate elementele de pe col i
if(!dMin[k] || !dMin[i])
if(difMin2[k][i]!=dMin[i])
{
difMin2[k][i]=difMin2[i][k]=difMin[k][i]-dMin[i];
if(difMin2[k][i]<lMin[i] &&
k!=i &&
(!dMin[k] || !dMin[i]))
{
lMin[i]=difMin2[k][i];jlMin[i]=k;
}
if(jlMin[k]==i)
{
lMin[k]=nx+ny;
for(int ik=1;ik<=n;ik++) // pt toate elementele
// de pe linia k
if(difMin2[k][ik]<lMin[k] && k!=ik )
{
lMin[k]=difMin2[k][ik];jlMin[k]=ik;
}
}
}
}
// calcul suprafete
CAPITOLUL 7. ONI 2016 7.2. CMMDC 156
for(int i=1;i<=n;i++)
{
t=dMin[i];
ct[i]=2*t*(t-1)+1;
}
return 0;
}
7.2 cmmdc
Problema 2 - cmmdc 100 de puncte
Fie un şir de numere naturale nenule a1 , a2 , ..., an şi un număr natural k.
Cerinţe
Să se determine un grup de k numere din şir care au proprietatea ca cel mai mare divizor
comun al lor este maxim. Dacă există mai multe astfel de grupuri, se cere acel grup pentru care
suma elementelor este maximă.
Date de intrare
Fişierul cmmdc.in conţine pe prima linie numerele naturale n şi k separate prin spaţiu. Pe
linia a doua se găsesc numerele naturale a1 , a2 , ..., an separate prin câte un spaţiu.
Date de ieşire
Fişierul cmmdc.out conţine pe prima linie un număr natural reprezentând cel mai mare
divizor comun a exact k numere din şir, maxim posibil. Pe linia a doua, separate prin câte un
spaţiu şi ordonate descrescător, se află cele k numere din şir care dau cel mai mare divizor comun
maxim.
Exemple:
Se construieşte un vector de frecvenţe (v i = de câte ori apare i ı̂n sirul de numere dat);
Se face rationamentul urmator: dacă k numere au cmmdc-ul egal cu x, atunci ele sunt fie egale
cu x, fie sunt multiplii ai numarului x.
Astefel se parcurge cu o variabila x descrescator intervalul 1 000 000 2 si pentru fiecare
valoare a lui x se determina daca exista cel putin k numere egale cu x sau multiplii de-i lui x, cu
un algoritm asemanator cu Ciurul lui Eratostene.
Aceasta determinare se face parcurgand multiplii lui x descrescator, astfel prima submultime
determinata este solutia ceruta. Cel mai mare multiplu a lui x care teoretic poate sa apara printre
cele n numere se poate calcula in functie de valoarea maxima din sirul a (notata max a si care
oricum nu depaseste 1 000 000).
Complexitate:
O n pentru constructia vectorului v.
O max a log max a pentru determinarea rezultatului (unde max a e maximul din sirul
a)
Expresia de mai sus e aproximarea pentru:
int main()
{
ifstream fin("cmmdc.in");
ofstream fout("cmmdc.out");
if(FreqFact[i] >= k)
{
fout << i << endl;
for(int j = maxx / i * i; j; j -= i)
while(Freq[j] > 0)
{
fout << j << " ";
if(--k == 0)
return 0;
--Freq[j];
}
}
return 0;
}
7.3 livada
Problema 3 - livada 100 de puncte
Fermierul Quinto are o livadă plină cu pomi fructiferi. Livada are N rânduri, numerotate de
la 1 la N , pe fiecare rând aflându-se câte M pomi fructiferi, numerotaţi de la 1 la M . Livada lui
Quinto este una specială, aşa că pentru unii pomi se cunoaşte cantitatea de fructe (exprimată ı̂n
kg) care poate fi culeasă, iar pentru alţii aceasta poate fi determinată pe baza unei formule.
Quinto şi-a propus să recolteze C kg de fructe din pomii aflaţi ı̂n livada lui. Acesta foloseşte
un utilaj modern pentru culesul fructelor. Utilajul poate fi folosit pe oricare din rândurile livezii,
dar poate aduna doar fructele dintr-un şir consecutiv de pomi, ı̂ncepând cu primul pom de pe
rândul respectiv, neavând posibilitatea de a culege parţial fructele dintr-un pom.
Preocupat de frumuseţea livezii sale, Quinto s-a gândit la restricţii suplimentare pentru
recoltarea cantităţii C de fructe. Astfel, el doreşte să adune fructele din pomi de pe maximum R
rânduri diferite, pentru ca N R rânduri să rămână complete.
Deasemenea, el doreşte să culeagă cu prioritate pomii care au o cantitate cât mai mică de
fructe, pentru ca ı̂n livadă să rămână cei mai roditori pomi. Quinto şi-a dat seama că este dificil
să culeagă fix C kg de fructe, prin urmare este mulţumit şi cu o cantitate mai mare, care respectă
celelalte condiţii impuse de el.
Cerinţe
Determinaţi cea mai mică valoare X posibilă astfel ı̂ncât să se poată culege, ı̂n condiţiile de
mai sus, o cantitate de cel puţin C kg de fructe şi orice pom din care se culeg fructe să conţină
cel mult X kg de fructe.
Date de intrare
Date de ieşire
CAPITOLUL 7. ONI 2016 7.3. LIVADA 159
Fişierul de ieşire livada.out va conţine o singură valoare scrisă pe prima linie, care reprezintă
cea mai mică valoare a cantităţii de fructe (exprimată ı̂n kg) dintr-un pom cules, astfel ı̂ncât să
fie respectate toate restricţiile problemei.
a Atenţie la determinarea fiecărei valori Aij pentru că ı̂n formulă sunt produse care pot
32
să furnizeze valori mai mari decât 2 1.
a 1 & C & 10
18
Exemple:
livada.in livada.outExplicaţii
5 6 18 4 3 6 5 4 Sunt 5 rânduri cu câte 6 pomi pe fiecare rând. Figura alăturată
2741351 arată matricea care se obţine conform formulelor precizate.
25263 Se doreşte culegerea a cel puţin 18 de kg de fructe de pe maxim
4 rânduri din cele 5.
ı̂n figura alăturată, este prezentată o soluţie
posibilă ı̂n care cantitatea maximă culeasă
dintr-un pom este de 4 kg.
Nu se pot culege 18 de kg de fructe de pe
maxim 4 rânduri astfel ı̂ncât să fie culeşi doar pomi cu cantitate
de fructe 3kg (ı̂n acest caz se pot culege cel mult 8 kg).
Timp maxim de executare/test: 0.5 secunde
Memorie: total 64 MB
Dimensiune maximă a sursei: 15 KB
Problema se rezolvă prin căutarea soluţiei (căutare binară sau secvenţială). Se caută cantitatea
minimă de fructe culeasă dintr-un singur pom fructifer (se verifică pe rând valori posibile şi se
alege valoarea minimă care respectă restricţiile problemei).
Soluţii parţiale 30 / 70 puncte
Odată fixată valoarea pentru cantitatea maximă de kg de fructe culese dintr-un singur pom,
se determină cantitatea maximă de fructe care poate fi culeasă din ı̂ntreaga livadă astfel ı̂ncât să
fie respectate restricţiile problemei.
Această valoare poate fi aflată dacă se determină pentru fiecare rând cantitatea maximă de
fructe care poate fi culeasă din pomii de pe acel rând, ı̂nsumând pe rând valorile de pe acest rând
până la prima valoare mai mare decât valoarea fixată, şi se memorează aceste valori ı̂ntr-un vector,
2
care este apoi sortat descrescător. Este suficient un algoritm de sortare de complexitate O N .
Se ı̂nsumează apoi primele R valori din vectorul sortat, această sumă reprezentând cantitatea
maximă de fructe care pot fi adunate din livadă.
După determinarea cantităţii maxime care poate fi culeasă din ı̂ntreaga livadă, se verifică dacă
aceasta este cel puţin egală cu cantitatea C cerută ı̂n enunţ.
O implementare a ideii de mai sus care foloseşte căutarea secvenţială a soluţiei obţine 30
puncte, complexitatea algoritmului fiind O N M M axV al, unde N e numărul de rânduri,
M e numărul de pomi de pe fiecare rând, iar M axV al e cea mai mare cantitate de fructe dintr-un
singur pom.
O implementare a ideii de mai sus care foloseşte căutarea binară a soluţiei obţine 70 puncte,
complexitatea algoritmului fiind O N M log M axV al.
CAPITOLUL 7. ONI 2016 7.3. LIVADA 160
void Citire()
{
int i, j, x, y, z, w, u;
ifstream fin(inFile);
fin >> n >> m >> C >> R;
fin >> x >> y >> z >> w >> u;
for (i = 1; i <= m; ++i)
fin >> a[1][i];
for (i = 2; i <= n; ++i)
fin >> a[i][1];
for (i = 2; i <= n; ++i)
for (j = 2; j <= m; ++j)
a[i][j]=(1LL*x*a[i-1][j] + 1LL*y*a[i][j-1] +
1LL*z*a[i-1][j-1] + w) % u;
fin.close();
}
void Procesare()
{
int i, j;
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
{
mx[i][j] = max(mx[i][j-1], a[i][j]);
s[i][j] = s[i][j-1] + a[i][j];
}
}
void CautBin()
{
ofstream fout(outFile);
int st, dr, sol, mij, i;
long long suma;
sol = 0;
st = 1;
dr = 1000000000;
while (st <= dr)
{
mij = (st + dr) / 2;
for (i = 1; i <= n; ++i)
t[i] = Linia(i, mij);
sort(t + 1, t + n + 1);
suma = 0;
for (i = n; i > n - R; i--)
suma += t[i];
if (suma >= C)
{
sol = mij;
dr = mij - 1;
}
else
st = mij + 1;
}
int main()
{
Citire();
Procesare();
CautBin();
return 0;
}
ifstream fin("livada.in");
ofstream fout("livada.out");
int A[NMax+5][MMax+5],Max[NMax+5][MMax+5];
long long Sum[NMax+5][MMax+5];
int N,M,R,Sol;
long long C;
void Read()
CAPITOLUL 7. ONI 2016 7.3. LIVADA 162
{
long long x,y,z,w,u;
fin>>N>>M>>C>>R;
fin>>x>>y>>z>>w>>u;
void Precalculate()
{
for(int i = 1; i <= N; ++i)
for(int j = 1; j <= M; ++j)
{
Sum[i][j] = A[i][j] + Sum[i][j-1];
Max[i][j] = max(Max[i][j-1],A[i][j]);
}
}
sort(V+1,V+k+1,greater<long long>());
void Solve()
{
int Left = 1, Right = MaxVal;
while(Left <= Right)
{
int Mid = (Left + Right) / 2;
if(Check(Mid))
CAPITOLUL 7. ONI 2016 7.4. DIF2 163
{
Sol = Mid;
Right = Mid - 1;
}
else
Left = Mid + 1;
}
}
void Print()
{
fout<<Sol<<"\n";
}
int main()
{
Read();
Precalculate();
Solve();
Print();
return 0;
}
7.4 dif2
Problema 4 - dif2 100 de puncte
Sandu a studiat la ora de informatică mai multe aplicaţii cu vectori de numere naturale, iar
acum are de rezolvat o problemă interesantă. Se dă un şir X X1 , X2 , ..., Xn de numere naturale
nenule şi două numere naturale p1 şi p2 , unde p1 $ p2 . Sandu trebuie să construiască un nou şir
Y Y1 , Y2 , ..., Ynn cu n n elemente obţinute din toate produsele de câte două elemente din
şirul X (fiecare element din şirul Y este de forma Xi Xj , 1 & i,j & n). Sandu are de calculat două
valori naturale d1 şi d2 obţinute din şirul Y . Valoarea d1 este egală cu diferenţa maximă posibilă
dintre două valori ale şirului Y . Pentru a obţine valoarea d2 , Sandu trebuie să considere că şirul
Y are elementele ordonate descrescător iar d2 va fi diferenţa dintre valorile aflate pe poziţiile p1
şi p2 ı̂n şirul ordonat descrescător. Sandu a găsit rapid valorile d1 şi d2 şi, pentru a le verifica, vă
roagă să le determinaţi şi voi.
Cerinţe
Date de intrare
Fişierul de intrare dif2.in va conţine pe prima linie un număr natural C care poate fi doar
1 sau 2. Dacă C 1, atunci pe linia a doua se va afla numărul natural n. Dacă C 2, atunci
pe linia a doua se vor afla numerele naturale n, p1 şi p2 separate prin câte un spaţiu. ı̂n ambele
cazuri, pe următoarele n linii se vor afla elementele şirului X, câte un număr natural pe fiecare
linie a fişierului.
Date de ieşire
În cazul C 1, fişierul de ieşire dif2.out va conţine pe prima linie valoarea d1 egală cu
diferenţa maximă dintre oricare două valori din şirul Y . ı̂n cazul C 2 fişierul de ieşire va conţine
pe prima linie un număr natural d2 reprezentând diferenţa dintre valorile aflate pe poziţiile p1 şi
p2 din şirul Y , presupunând că ar fi ordonat descrescător.
a 3 $ n $ 300 000
a 1 & p1 $ p2 & n2
a 1 & Xi $ 300 000, i 1..n
CAPITOLUL 7. ONI 2016 7.4. DIF2 164
a Pentru teste valorând 30 de puncte vom avea C 1, iar pentru teste valorând 70 de puncte
vom avea C 2.
a Pentru teste valorând 10 puncte vom avea C 2 şi n & 100
Exemple:
Se observă că valoarea d1 este egală cu diferenţa dintre valoarea maximă şi valoarea minimă a
elementelor din şirul Y . Se pot calcula valorile extreme ale elementelor lui X iar d1 va fi diferenţa
pătratelor lor.
Pentru d2 nu se poate construi şirul Y şi apoi să se ordoneze descrescător deoarece există o
2
limitare a memoriei disponibile şi algoritmi k log k pentru sortare (k n ).
2
Voi prezenta un algoritm de complexitate O DIMn n log DIMV , unde DIMV este valoarea
maxima din X.
Se vor număra câte perechi de elemente din X au valoarea mai mică sau egală decât i. Se
poate face numărarea ı̂n O n dacă vom precalcula un sir ci cu numărul de valori din X mai
mici sau egale cu i.
Pentru a afla pe ce poziţie va fi un produs p, vom utiliza şirul c precalculat şi vom numără
pentru fiecare element din şir câte elemente contribuie la construirea unei valori ce nu depăşeşte
p.
Vom utiliza căutarea binară pentru determinarea celor două valori. Nu e necesar să facem
sortări descrescătoare, se deduce uşor poziţia necesară dacă vom face calculul ı̂n cazul ordonării
crescătoare.
Soluţia comisiei (studenţi LUCIAN BICSI şi PETRU-ERIC STAVARACHE)
Soluţie O(V logV):
Ţinem un vector de frecvenţă f req i pentru valorile elementelor ı̂mpreună cu sume parţiale
pe prefixe pe acest vector Sumi.
Pentru a afla produsul aflat pe o poziţie Z, căutăm binar produsul, iar pentru un produs P
fixat trebuie sa vedem câte perechi (a, b) exista cu a b & P .
Pentru asta vom parcurge fiecare valoare posibila, iar pentru o valoare A fixata deducem ca
B & P ©A. Aşadar toate elementele cu valoarea cel mult B vor forma perechi posibile ı̂mpreună
cu A. Vor exista f A SumB astfel de perechi.
Dacă numărul total de perechi de acest fel este & Z, atunci soluţia ' P .
2
Solutie O N log V :
Similar cu soluţia anterioara, doar ca in loc sa ţinem un vector de frecventa, sortam vectorul
iniţial.
CAPITOLUL 7. ONI 2016 7.4. DIF2 165
return ans;
}
int32_t main()
{
ifstream cin("dif2.in");
ofstream cout("dif2.out");
int t;
long long a, b;
sort(V + 1, V + n + 1);
if(t == 1)
cout << 1LL * V[n] * V[n] - 1LL * V[1] * V[1] << endl;
else
CAPITOLUL 7. ONI 2016 7.4. DIF2 166
return 0;
}
int v[MAX_N];
int f[MAX_V];
LL S[MAX_V];
int n;
LL p1, p2;
LL test1()
{
return 1LL * v[n] * v[n] - 1LL * v[1] * v[1];
}
LL before(LL prod)
{
LL ret = 0;
for(int i = min(1LL * v[n], prod); i >= 1; --i)
{
LL other = prod / i;
return ret;
}
LL query(LL poz)
{
LL i, step;
LL mx = 1LL * v[n] * v[n];
for(step = 1; step < mx; step *= 2);
return i + 1;
}
int32_t main()
{
ifstream in("dif2.in");
int c;
in >> c;
assert(c == 1 || c == 2);
in >> n;
assert(3 < n && n < MAX_N);
if(c == 2)
in >> p1 >> p2;
sort(v + 1, v + n + 1);
ofstream out("dif2.out");
if(c == 1)
{
out << test1() << "\n";
return 0;
}
else
{
p2 = 1LL * n * n - p2 + 1;
p1 = 1LL * n * n - p1 + 1;
out << query(p1) - query(p2) << "\n";
}
return 0;
}
return ans;
}
return ans + 1;
}
template<typename T>
int main()
{
freopen("dif2.in", "r", stdin);
freopen("dif2.out", "w", stdout);
CAPITOLUL 7. ONI 2016 7.5. LEDURI 168
int t;
long long a, b;
Read(t);
Read(n);
if(t == 2)
{
Read(a);
Read(b);
}
sort(V + 1, V + n + 1);
if(t == 1)
cout << 1LL * V[n] * V[n] - 1LL * V[1] * V[1] << endl;
else
cout << kth(1LL * n * n - a + 1) - kth(1LL * n * n - b + 1) << endl;
return 0;
}
7.5 leduri
Problema 5 - leduri 100 de puncte
Am un cablu cu N leduri (numerotate de la 1 la N ) aşezate echidistant. Iniţial, unele leduri
sunt aprinse, iar altele sunt stinse. Ledurile sunt legate ı̂ntre ele astfel ı̂ncât atingerea fiecărui
led produce modificarea atât a stării lui, cât şi a ledurilor vecine lui. Deci, dacă se atinge ledul i
(2 & i & N 1) atunci se modifică stările ledurilor i 1, i şi i 1. Dacă se atinge ledul 1, atunci
se modifică stările ledurilor 1 şi 2, iar dacă se atinge ledul N , atunci se modifică stările ledurilor
N 1 şi N . Vreau să modific starea ledurilor astfel ı̂ncât să semene cu cablul cu N leduri pe care
ı̂l are Ionuţ, prietenul meu (două cabluri seamănă dacă pentru orice i 1..N stările ledurilor de
pe poziţia i sunt identice).
Cerinţe
Cunoscând cum arată cablul lui Ionuţ, ajutaţi-mă să determin numărul minim de atingeri ale
unor leduri astfel ı̂ncât cablul meu să arate ca şi cablul lui Ionuţ.
Date de intrare
Fişierul de intrare leduri.in conţine pe prima linie numărul natural N . Pe a doua linie sunt
N cifre binare separate prin câte un spaţiu reprezentând stările ledurilor de pe cablul meu. Cifra
de pe poziţia i este 0 dacă ledul i este stins, respectiv este 1 dacă ledul i este aprins (i 1..N ).
Pe a treia linie sunt N cifre binare separate prin câte un spaţiu, reprezentând stările ledurilor de
pe cablul lui Ionuţ.
Date de ieşire
Fişierul de ieşire leduri.out va conţine pe prima linie un singur număr natural reprezentând
numărul minim de atingeri ale unor leduri astfel ı̂ncât cablul meu să arate ca şi cablul lui Ionuţ.
Exemple:
În primul rând, se observa becurile pot fi atinse in orice ordine, fără a produce schimbări de
efect ı̂n final. O a doua observaţie utilă ı̂n rezolvarea problemei este că, pentru a transforma
configuraţia iniţială ı̂n cea finala, orice bec ajunge sa fie atins maximum o data.
Acest lucru rezultă din faptul că atingerea unui bec pentru a doua oara produce neutralizarea
primei operaţii.
n
Din cele doua observaţii se deduce că numărul de seturi de operaţii diferite posibile este 2
(orice set de operaţii se poate vedea ca atingerea unei submulţimi de becuri din cele n).
Soluţia pentru 20p:
Pentru 20% din teste, n & 20, deci se poate simula orice set de operaţii şi, pentru cele care
produc efectul dorit, să se calculeze numărul de operaţii şi să se compare cu minimul obţinut de
până atunci.
n
Complexitatea acestei soluţii este O 2 .
Soluţia pentru 100p:
La o analiză mai atentă a configuraţiilor posibile şi a modurilor din care se poate ajunge la
acestea, se observă că există maximum două soluţii de transformare valide. Mai exact, aplicând
o strategie tip Greedy pentru a transforma şirul iniţial ı̂n cel final, se observă că setul de operaţii
este unic determinat de atingerea sau nu a primului element. Mai exact:
Să presupunem că am ales să atingem primul element (cazul ı̂n care nu ı̂l atingem se va trata
ı̂n mod analog). Se disting două cazuri:
1) Becul 1 din starea actuală e diferit de becul 1 ı̂n starea finală, unde singura variantă posibilă
de a rezolva această problemă este să atingem becul 2 (deoarece becul 1 nu mai poate fi schimbat,
din observaţiile de mai sus)
2) Stările celor doua becuri coincid, caz ı̂n care nu avem voie sa atingem becul 2 (dacă l-am
atinge, nu am mai putea modifica ı̂napoi starea becului 1)
Inductiv, se demonstrează că atingerile tuturor celor n becuri sunt unic determinate.
În final, se poate să obţinem o stare diferită pentru ultimul bec, caz in care soluţia obtinută
nu este corectă.
Soluţia de 100p tratează cele două cazuri (atingerea sau nu a primului bec), după care simulează
strategia descrisă mai sus pentru a obţine o configuraţie care diferă de cea finală la cel mult ultima
poziţie. Dacă cele doua valori coincid, se actualizează eventualul număr minim de operaţii. Dacă
acest minim nu a fost actualizat, nu vor exista soluţii (ı̂n cadrul problemei, ı̂nsă, orice test admite
soluţie validă).
Extra:
Pentru o mai bună ı̂nţelegere a corectitudinii problemei, propunem ı̂ncă o demonstraţie, mai
formală:
Datorita comutativităţii setului de operaţii, este suficient şi necesar să aducem ambele
configuraţii la o stare intermediară.
Să considerăm (S0) = 000 ... 000 şi (S1) = 000 ... 001.
Lemă: Din orice stare iniţială se poate ajunge ı̂n S0 sau S1
Demonstraţie: Rezultă din algoritmul descris mai sus (de fiecare dată când găsim un bec
aprins, atingem becul imediat următor şi repetăm).
În continuare, dacă se poate ajunge din S0 ı̂n S1 sau invers, atunci se poate ajunge din orice
stare ı̂n orice stare. Ne interesează acum să analizăm când se ı̂ntâmplă acest lucru.
CAPITOLUL 7. ONI 2016 7.5. LEDURI 170
int main()
{
freopen("leduri.in", "r", stdin);
freopen("leduri.out", "w", stdout);
cin >> n;
A1[1] ˆ= 1;
A1[2] ˆ= 1;
int a = transform(A0);
int b = transform(A1);
if(ans == 1000000)
ans = -1;
return 0;
}
int n;
bool v[MAX_N];
bool init[MAX_N];
bool fin[MAX_N];
void apply(int x)
{
v[x] ˆ= 1;
if(x - 1 >= 1)
v[x - 1] ˆ= 1;
if(x + 1 <= n)
v[x + 1] ˆ= 1;
}
int go()
{
int cnt = 0;
return cnt;
}
ofstream out("leduri.out");
int main()
{
ifstream in("leduri.in");
in >> n;
assert(1 <= n && n <= 100000);
int s1 = go();
v[1] ˆ= 1;
v[2] ˆ= 1;
int s2 = go() + 1;
return 0;
}
7.6 omogene
Problema 6 - omogene 100 de puncte
Se consideră o matrice cu L linii şi C coloane care memorează doar valori din mulţimea 0,1,2. O
submatrice nevidă (formată din cel puţin o linie şi cel puţin o coloană) a acestei matrice o numim
omogenă dacă numărul valorilor de 0 este egal cu numărul de valori de 1 şi egal cu numărul
valorilor de 2.
De exemplu, ı̂n matricea
0 1 2 0
1 2 0 1
sunt şase submatrice omogene, acestea fiind:
012 120 012 120 120 201
120 201
Submatricele a treia şi a patra sunt formate din prima linie a matricei iniţială, iar submatricele
a cincea şi a şasea sunt formate din a doua linie.
Cerinţe
Date de intrare
Fişierul omogene.in conţine pe prima linie numerele naturale L şi C. Pe următoarele L linii
se află câte C numere naturale separate prin spaţii reprezentând câte o linie din matrice.
Date de ieşire
Exemple:
Rezolvăm mai ı̂ntâi problema liniară. Se consideră un vector t de lungime n care memorează
doar valori din mulţimea {1,2,3}. Să se determine numărul secvenţelor omogene (adică ı̂n care
numărul valorilor de 0 este egal cu numărul valorilor de 1 şi egal cu numărul valorilor de 2).
Construim sirul sumelor partiale:
s0[i]= numărul valorilor de 0 din t[1..i]
s1[i]= numărul valorilor de 1 din t[1..i]
s2[i]= numărul valorilor de 2 din t[1..i]
Apoi construim şirurile
d01[0] = d12[0] = d20[0] = 0 şi
d01[i] = s0[i] - s1[i], i=1..n
d12[i] = s1[i] - s2[i] , i=1..n
d20[i] = s2[i] - s0[i], i=1..n
Vom determina apoi câte perechi de triplete { d01[i], d12[i], d20[i] } egale sunt ı̂n acest şir de
lungime n+1 (i=0..n). Dacă două triplete { d01[i],d12[i], d20[i] } şi { d01[k],d12[k], d20[k] } (i ¡ j)
sunt identice, atunci secvenţa t[i+1], t[i+2],t[j] este o secvenţă omogenă.
Pentru a afla numărul perechilor de triplete egale, se poate sorta şirul tripletelor. ı̂n şirul
sortat, tripletele identice sunt alăturate. Dacă identificăm X triplete identice, atunci numărul
perechilor de triplete identice este X(X-1)/2 (oricare două).
Deci complexitatea determinării numărului de secvenţe omogene din vectorul t este dată se
sortarea tripletelor, deci O n log n.
CAPITOLUL 7. ONI 2016 7.6. OMOGENE 174
Revenind la problema bidimensională: Din restricţii, L & C şi L C &65 536. De unde rezultă
că
L & sqrt 65536 256.
Deci numărul de linii din matrice nu poate fi mai mare de 256.
Construim două matrice de sume parţiale:
z[i][j] = numărul de valori de 0 aflate pe coloana j, ı̂ncepând de la poziţia (i,j) ı̂n sus.
u[i][j] = numărul de valori de 1 aflate pe coloana j, ı̂ncepând de la poziţia (i,j) ı̂n sus.
Pentru orice două linii k şi p (unde k¡=p), dorim să aflăm câte submatrice omogene de ı̂nălţime
H=p-k+1 există ı̂ntre cele două linii. Vom construi deci vectorul t0[j] = z[p][j]-z[k-1][j], j=1..C,
deci ı̂n care t[j] memorează numărul valorilor de 0 aflate pe coloana j ı̂ntre liniile k şi p. Similar,
se construieşte t1[j]=u[p][j]-u[k-1][j], j=1..C, deci ı̂n care t1[j] memorează numărul valorilor de 1
aflate pe coloana j ı̂ntre liniile k şi p. Atunci t2[j]=H-t0[j]-t1[j], pentru j=1..C.
Apoi problema se rezolvă ca ı̂n cazul unidimensional.
Complexitatea va fi deci O L L C logC
int v[MAX_N][MAX_M];
int n, m;
int s[MAX_N][MAX_M][3];
LL ans = 0;
int vect[MAX_M][3];
//vect[i][j] -> partial sum of j over the i prefix
struct state
{
int v[3];
//v[0] -> cnt[0] - cnt[1]
//v[1] -> cnt[0] - cnt[2]
//v[2] -> cnt[1] - cnt[2]
state sts[MAX_M];
CAPITOLUL 7. ONI 2016 7.6. OMOGENE 175
state getState(int i)
{
state ret;
ret.v[0] = vect[i][0] - vect[i][1];
ret.v[1] = vect[i][0] - vect[i][2];
ret.v[2] = vect[i][1] - vect[i][2];
return ret;
}
LL ret = 0;
for(int i = 1, j; i <= m; i = j)
{
j = i + 1;
while(j <= m && sts[i] == sts[j])
j++;
LL diff = j - i;
ret += diff * (diff - 1) / 2;
}
return ret;
}
int main()
{
ifstream in("omogene.in");
ofstream out("omogene.out");
in >> n >> m;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
in >> v[i][j];
int z[MAXL][MAXC];
int u[MAXL][MAXC];
int d[MAXL][MAXC];
int L, C;
long long sol;
void NrSubmat()
{
int i, j, i2, j2, zero, unu, doi;
for (i = 1; i <= L; ++i)
for (j = 1; j <= C; ++j)
for (i2 = i; i2 <= L; ++i2)
for (j2 = j; j2 <= C; ++j2)
{
zero=z[i2][j2]-z[i-1][j2]-z[i2][j-1]+z[i-1][j-1];
unu=u[i2][j2]-u[i-1][j2]-u[i2][j-1]+u[i-1][j-1];
doi=d[i2][j2]-d[i-1][j2]-d[i2][j-1]+d[i-1][j-1];
if (zero == unu && zero == doi)
sol++;
}
}
void Citire()
{
int i, j, x;
ifstream fin(inFile);
fin >> L >> C;
for (i = 1; i <= L; ++i)
for (j = 1; j <= C; ++j)
{
fin >> x;
z[i][j] = z[i-1][j]+z[i][j-1]-z[i-1][j-1]+(x == 0);
u[i][j] = u[i-1][j]+u[i][j-1]-u[i-1][j-1]+(x == 1);
d[i][j] = d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(x == 2);
}
fin.close();
}
void Afisare()
{
ofstream fout(outFile);
fout << sol << "\n";
fout.close();
}
int main()
{
Citire();
NrSubmat();
Afisare();
return 0;
}
ifstream f("omogene.in");
ofstream g("omogene.out");
struct nod
{
int d01,d12,d20;
};
CAPITOLUL 7. ONI 2016 7.6. OMOGENE 177
nod v[10001];
int a[318][10001];
int z[318][10001],u[318][10001];
int t0,t1,t2,st0,st1,st2;
long long sol;
int n,m;
void citire()
{
f>>n>>m;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
f>>a[i][j];
f.close();
}
void prelucrare()
{
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
z[i][j]=z[i-1][j]+(a[i][j]==0);
u[i][j]=u[i-1][j]+(a[i][j]==1);
}
for(int k=1;k<=n;++k)
for(int p=k;p<=n;++p)
{
int d=p-k+1;
st0=st1=st2=0;
v[0].d01=v[0].d12=v[0].d20=0;
for(int j=1;j<=m;++j)
{
t0=z[p][j]-z[k-1][j];st0+=t0;
t1=u[p][j]-u[k-1][j];st1+=t1;
t2=d-t0-t1;st2+=t2;
v[j].d01=st0-st1;
v[j].d12=st1-st2;
v[j].d20=st2-st0;
}
sort(v,v+m+1,test);
int x=1;
for(int i=1;i<=m;++i)
if(v[i-1].d01==v[i].d01 &&
v[i-1].d12==v[i].d12 &&
v[i-1].d20==v[i].d20)
x++;
else
{
sol=sol+1ll*x*(x-1)/2;
x=1;
}
sol=sol+1ll*x*(x-1)/2;
}
}
int main()
{
citire();
prelucrare();
g<<sol<<’\n’;
return 0;
}
CAPITOLUL 7. ONI 2016 7.6. OMOGENE 178
ONI 2015
8.1 cub
Problema 1 - cub 100 de puncte
Sărbătorile de iarnă tocmai s-au ı̂ncheiat. Florinel doreşte să-şi ajute
părinţii la despodobirea bradului. Tubul luminos pe care l-au folosit
3
anul acesta este mai special. Are N becuri luminoase numerotate de
3
la 1 la N , iar fiecare bec care este inscripţionat cu un număr prim, va
rămâne mereu aprins.
Cutia ı̂n care trebuie strâns tubul este un cub de latură N . Becul cu
numărul 1, trebuie pus ı̂n colţul de coordonate (1,1,1), restul ı̂n spirală
până la umplerea nivelului, apoi nivelul următor ı̂n sens invers, ş.a.m.d.
Cerinţe
Date de intrare
Fişierul de intrare cub.in conţine pe prima linie un număr natural p. Pentru toate testele de
intrare, numărul p poate avea doar valoarea 1 sau valoarea 2.
Pe a doua linie a fişierului de intrare, sunt scrise două numere naturale N şi V separate printr-
un spaţiu reprezentând dimensiunea cubului şi valoarea becului pentru care trebuie determinate
coordonatele.
Date de ieşire
a Dacă valoarea lui p este 1, se va rezolva numai cerinţa 1. În acest caz, ı̂n fişierul de ieşire
cub.out se vor scrie trei numere naturale x y z, separate prin câte un spaţiu, reprezentând
coordonatele becului cu valoarea V .
179
CAPITOLUL 8. ONI 2015 8.1. CUB 180
a Dacă valoarea lui p este 2, se va rezolva numai cerinţa 2. În acest caz, fişierul de ieşire
cub.out va conţine 4 linii. Pe fiecare linie i, se va scrie câte un număr natural fi, reprezentând
numărul de becuri inscripţionate cu numere prime de pe faţa i.
Exemple:
cub.in cub.outExplicaţii
1 3 10 222 Atenţie! Pentru acest test se rezolvă doar cerinţa 1).
linia 2, coloana 2, nivel 2 - este becul 10
2 3 10 4343 Atenţie! Pentru acest test se rezolvă doar cerinţa 2).
4 - becuri inscripţionate cu numere prime pe faţa 1: 2, 3, 17, 19
3 - becuri inscripţionate cu numere prime pe faţa 2: 3, 5, 23
4 - becuri inscripţionate cu numere prime pe faţa 3: 5, 7, 13, 23
3 - becuri inscripţionate cu numere prime pe faţa 4: 7, 11, 19
Timp maxim de executare/test: 0.5 secunde
Memorie: total 8 MB
Dimensiune maximă a sursei: 10 KB
Dacă N este un număr par, se vor folosi la fiecare plecare 4 direcţii matricea fiind completată
astfel.
Dacă N este impar elementul din centru va fi completat ultimul.
Ultimul element completat pe nivelul 1, decide poziţia de plecare ı̂n umplerea nivelului următor,
parcurgând circular spre exterior ı̂n sens trigonometric matricea de la nivelul 2.
Matricea de pe nivelul 2 se poate obţine din valorile matricii de la nivelul 1 astfel:
B ij 2 n n 1 Aij .
unde A este matricea asociată primului nivel, iar B este matricea asociată nivelului 2.
Se observă că matricile de nivel mai mare decât 2, se pot obţine din matricile de la nivelele
anterioare astfel:
a Valoarea unui element de pe nivelul 3 se obţine adunând la valoarea de pe nivelul 1 cu
aceleaşi coordonate valoarea 2*N*N.
a Valoarea unui element de pe nivelul 4 se obţine adunând la valoarea de pe nivelul 2 cu
aceleaşi coordonate valoarea 2*N*N.
a Valoarea unui element de pe nivelul 5 se obţine adunând la valoarea de pe nivelul 3 cu
aceleaşi coordonate valoarea 2*N*N. ş.a.m.d.
Pentru rezolvarea cerinţei 1 (20 pct.): ajunge să scădem ı̂n mod repetat din valoarea V numărul
2*N*N, ţinând minte că trecem peste 2 nivele la fiecare scădere. Coordonatele X şi Y le vom obţine
căutând valoarea rămasă ı̂n matricile A sau B, iar coordonata Z, se obţine din numărul total de
scăderi*2 + valoarea 1 sau 2, ı̂n funcţie de nivelul matricii la care ajungem.
Pentru rezolvarea cerinţei 2 (80 pct.): ajunge să testăm elementele de pe chenarul matricilor
A şi B:
CAPITOLUL 8. ONI 2015 8.1. CUB 181
#include <fstream>
#include <bitset>
ifstream f("cub.in");
ofstream g("cub.out");
int m=2;
bitset<8000005> pr;
void prim(int n)
{
int i=1,j;
for (i=1;i<=n;i++)
pr[i]=true;
pr[1]=false;
for (i=2;i<=n/2;i++)
if (pr[i])
{
j=i+i;
while (j<=n)
{
pr[j]=false;
j+=i;
}
}
}
int main()
{
int v,n,n2,k,z,p,i,j,f1=0,f2=0,f3=0,f4=0;
m=2; pr[1]=2; pr[2]=3;
f>>v;
f>>n>>k;
n2=n*n;
prim(n2*n);
if (v==1)
{
z=1;
v=0;
while (k-v>n2)
{
z++;
v=v+n2;
}
if (z%2==1)
{
for (p=1;;p++)
{
for (j=p;j<=n-p+1;j++)
{
v++;
if (v==k)
{
CAPITOLUL 8. ONI 2015 8.1. CUB 182
{
g<<i<<" "<<p<<" "<<z<<"\n";
g.close();
return 0;
}
}
}
}
}
else
{
for (z=1;z<=n;z++)
if (z%2==1)
{
p=1; v=(z-1)*n2;
// coltul st-sus
v++;
if (pr[v]) { f1++; f4++; }
// coltul stanga-jos
v++;
if (pr[v]) { f3++; f4++; }
v=v+n2-(4*n-4);
}
else
{
p=1;
v=v+n2-(4*n-4);
for (i=p+1; i<=n-p; i++)
{
v++;
if (pr[v]) f4++;
}
if (pr[v]) f3++;
}
g<<f1<<"\n"<<f2<<"\n"<<f3<<"\n"<<f4<<"\n";
g.close();
}
return 0;
}
#include <fstream>
ifstream f("cub.in");
ofstream g("cub.out");
int pr[1000000],m=2;
int prim(int x)
{
int i=1;
if (x==1) return 0;
if (x==2 || x==3)
return 1;
while (pr[i]<=x/2)
{
if (x%pr[i]==0)
return 0;
i++;
}
m++;
pr[m]=x;
return 1;
}
int main()
{
int v,n,n2,k,x,y,z,p,i,j,f1=0,f2=0,f3=0,f4=0;
m=2;
pr[1]=2;
pr[2]=3;
f>>v;
CAPITOLUL 8. ONI 2015 8.1. CUB 185
f>>n>>k;
n2=n*n;
if (v==1)
{
z=1;
v=0;
while (k-v>n2)
{
z++;
v=v+n2;
}
if (z%2==1)
{
for (p=1;;p++)
{
for (j=p;j<=n-p+1;j++)
{
v++;
if (v==k)
{
g<<p<<" "<<j<<" "<<z<<"\n";
g.close();
return 0;
}
}
for (i=p+1;i<=n-p+1;i++)
{
v++;
if (v==k)
{
g<<i<<" "<<n-p+1<<" "<<z<<"\n";
g.close();
return 0;
}
}
for (j=n-p;j>=p;j--)
{
v++;
if (v==k)
{
g<<n-p+1<<" "<<j<<" "<<z<<"\n";
g.close();
return 0;
}
}
for (i=n-p;i>=p+1;i--)
{
v++;
if (v==k)
{
g<<i<<" "<<p<<" "<<z<<"\n";
g.close();
return 0;
}
}
}
}
else
{
v=v+n2+1;
for (p=1;;p++)
{
for (j=p;j<=n-p+1;j++)
{
v--;
if (v==k)
{
g<<p<<" "<<j<<" "<<z<<"\n";
g.close();
return 0;
}
}
for (i=p+1;i<=n-p+1;i++)
{
v--;
if (v==k)
CAPITOLUL 8. ONI 2015 8.1. CUB 186
{
g<<i<<" "<<n-p+1<<" "<<z<<"\n";
g.close();
return 0;
}
}
for (j=n-p;j>=p;j--)
{
v--;
if (v==k)
{
g<<n-p+1<<" "<<j<<" "<<z<<"\n";
g.close();
return 0;
}
}
for (i=n-p;i>=p+1;i--)
{
v--;
if (v==k)
{
g<<i<<" "<<p<<" "<<z<<"\n";
g.close();
return 0;
}
}
}
}
}
else
{
for (z=1;z<=n;z++)
if (z%2==1)
{
p=1; v=(z-1)*n2;
// coltul st-sus
v++;
k=prim(v);
if (k) { f1++; f4++; }
// coltul stanga-jos
v++;
k=prim(v);
CAPITOLUL 8. ONI 2015 8.1. CUB 187
}
return 0;
}
int a[201][201],b[201][201];
int p,n,v,i,j,x,k,sum,f1,f2,f3,f4,nr;
int prim(int m)
{
int d;
if (m<2 ||(m>2 && m%2==0)) return 0;
for(d=2;d*d<=m;d++)
if(m%d==0) return 0;
return 1;
}
int main()
{
fin>>p;
fin>>n>>v;
///nivel 1 - matrice A
x=1;
k=0;
while(x<=n/2)
{
///spre stanga - linia x
for(j=x;j<=n-x;j++)
a[x][j]=++k;
if (n%2==1) a[n/2+1][n/2+1]=++k;
//afisare(a);
///nivel 2 - matrice B
sum=2*n*n+1;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
b[i][j]=sum-a[i][j];
CAPITOLUL 8. ONI 2015 8.1. CUB 189
//afisare(b);
if (p==1)
{
///cerinta 1
x=v;
int nivel=0;
while(x>2*n*n)
x=x-2*n*n, nivel+=2;
int stop=0;
for(i=1;i<=n && stop==0; i++)
for(j=1;j<=n && stop==0; j++)
{
if(a[i][j]==x)
{
fout<<i<<" "<<j<<" "<<nivel+1<<endl;
stop=1;
}
if(b[i][j]==x)
{
fout<<i<<" "<<j<<" "<<nivel+2<<endl;
stop=1;
}
}
}
else
{
///cerinta 2
f1=f2=f3=f4=0;
///fata 1 - linia 1 A,B,.... 200*200 elemente=40.000 nr de testat
for(j=1;j<=n;j++)
{
nr=a[1][j];
while(nr<n*n*n)
{
if (prim(nr)) f1++;
nr+=2*n*n;
}
nr=b[1][j];
while(nr<n*n*n)
{
if (prim(nr)) f1++;
nr+=2*n*n;
}
}
///fata 2 - coloana n A,B,....
for(i=1;i<=n;i++)
{
nr=a[i][n];
while(nr<n*n*n)
{
if (prim(nr)) f2++;
nr+=2*n*n;
}
nr=b[i][n];
while(nr<n*n*n)
{
if (prim(nr)) f2++;
nr+=2*n*n;
}
}
///fata 3 - linia n A,B,....
for(j=1;j<=n;j++)
{
nr=a[n][j];
while(nr<n*n*n)
{
if (prim(nr)) f3++;
nr+=2*n*n;
}
nr=b[n][j];
while(nr<n*n*n)
{
if (prim(nr)) f3++;
nr+=2*n*n;
}
}
CAPITOLUL 8. ONI 2015 8.1. CUB 190
return 0;
}
int a[101][101][101],n,v,l,c,x,y,z,nivel,i,j,k,colt,lim,p;
bool ciur[1000001];
int f1,f2,f3,f4;
void afisare()
{
int i,j;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
printf("%4d",a[i][j][nivel]);
printf("\n");
}
printf("\n");
}
int main()
{
freopen ("cub.in","r",stdin);
freopen ("cub.out","w",stdout);
scanf("%d",&p);
scanf("%d %d",&n,&v);
nivel=0;
k=0;
ciur[1]=ciur[0]=true; /// adica nu e prim;
for(i=2;i<=n*n*n;i++)
if (ciur[i]==false)
for(j=2;j<=n*n*n/i;j++)
ciur[i*j]=true;
f1=f2=f3=f4=0;
/// SE COMPLETEAZA INTREGUL CUB
while(nivel<n)
{
nivel++;
///nivel impar - plec din 1,1
l=c=1;
while(l<=n/2)
{
///dir1
CAPITOLUL 8. ONI 2015 8.1. CUB 191
for(j=c;j<=n-c;j++)
{
k++;
a[l][j][nivel]=k;
if (k==v && p==1) printf("%d %d %d\n",l,j,nivel);
}
///dir2
for(i=l;i<=n-l;i++)
{
k++;
a[i][n-c+1][nivel]=k;
if (k==v && p==1) printf("%d %d %d\n",i,n-c+1,nivel);
}
///dir3
for(j=n-c+1;j>c;j--)
{
k++;
a[n-l+1][j][nivel]=k;
if (k==v && p==1) printf("%d %d %d\n",n-l+1,j,nivel);
}
///dir4
for(i=n-l+1;i>l;i--)
{
k++;
a[i][c][nivel]=k;
if (k==v && p==1) printf("%d %d %d\n",i,c,nivel);
}
l++;
c++;
}
if (n%2==1)
{
k++;
a[n/2+1][n/2+1][nivel]=k;
if (k==v && p==1) printf("%d %d %d\n",n/2+1,n/2+1,nivel);
}
// afisare();
if (nivel<n)
{
colt=a[1][1][nivel];
nivel++; /// nivelul par
lim=colt+n*n*2;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
a[i][j][nivel]=(colt-1)+lim-a[i][j][nivel-1];
if (a[i][j][nivel]==v && p==1)
printf("%d %d %d\n",i,j,nivel);
}
k+=n*n;
// afisare();
}
}
///fata 1
for(nivel=1;nivel<=n;nivel++)
for(j=1;j<=n;j++)
if(ciur[a[1][j][nivel]]==false)
{
// printf("%d ",a[1][j][nivel]);
f1++;
}
if (p==2) printf("%d\n",f1);
///fata 2
for(nivel=1;nivel<=n;nivel++)
for(i=1;i<=n;i++)
if(ciur[a[i][n][nivel]]==false)
{
// printf("%d ",a[i][n][nivel]);
f2++;
}
if (p==2) printf("%d\n",f2);
///fata 3
for(nivel=1;nivel<=n;nivel++)
for(j=1;j<=n;j++)
if(ciur[a[n][j][nivel]]==false)
{
CAPITOLUL 8. ONI 2015 8.1. CUB 192
// printf("%d ",a[n][j][nivel]);
f3++;
}
if (p==2) printf("%d\n",f3);
///fata 4
for(nivel=1;nivel<=n;nivel++)
for(i=1;i<=n;i++)
if(ciur[a[i][1][nivel]]==false)
{
// printf("%d ",a[i][1][nivel]);
f4++;
}
if (p==2) printf("%d\n",f4);
return 0;
}
int prim(int x)
{
int i,L;
if (x<2) return 0;
L=(int)sqrt((double)x);
for (i=0;p[i]<=L;i++)
if(x%p[i]==0)
return 0;
return 1;
}
int a[202][202];
int dx[4]={0,1,0,-1}, dy[4]={1,0,-1,0};
CAPITOLUL 8. ONI 2015 8.1. CUB 193
int main()
{
ifstream fi("cub.in"); ofstream fo("cub.out");
int i,j,k,d,p,N,V;
fi>>p>>N>>V;
if (p==1)
{
for(i=0;i<=N+1;i++)
a[0][i]=a[N+1][i]=a[i][0]=a[i][N+1]=1;
i=j=1;
d=0;
for(k=1;k<=N*N;k++)
{
a[i][j]=k;
if(!a[i+dx[d]][j+dy[d]])
{
i+=dx[d];
j+=dy[d];
}
else
{
d=(d+1)%4;
i+=dx[d];
j+=dy[d];
}
}
k=V/(N*N);
V%=(N*N);
if (k&1)
V=N*N+1-V;
for (i=1;i<=N;i++)
for (j=1;j<=N;j++)
if (a[i][j]==V)
{
fo<<i<<" "<<j<<" "<<k+1<<"\n";
break;
}
}
else
{
int f[5],t;
f[1]=f[2]=f[3]=f[4]=0;
for (i=1;i<=N;i++)
for (j=1;j<=N;j++)
for (t=0;t<4;t++)
{
if (i&1)
k=j+(i-1)*N*N+t*(N-1);
else
k=i*N*N-j+1-t*(N-1);
f[t+1]+=prim(k);
}
for (t=1;t<=4;t++)
fo<<f[t]<<"\n";
}
return 0;
}
8.2 risc
Problema 2 - risc 100 de puncte
Pentru a participa la un concert, n persoane s-au aşezat la coadă pe un singur rând ı̂n aşteptarea
deschiderii casei de bilete. Înălţimile celor n persoane sunt toate distincte. Pe baza acestei
informaţii cruciale, agenţii de securitate au decis ca din motive de ... securitate, ordinea per-
soanelor care aşteaptă la coadă trebuie schimbată ı̂n funcţie de ı̂nălţimile lor.
Astfel, agentii de pază vor alege, pe rând, câte o persoană şi o vor trimite la sfârşitul rândului.
După fiecare operaţie de tipul acesta, să-i spunem ”de mutare”, rândul se restrânge, ocupându-
se poziţia rămasă liberă. Strategia agenţilor de pază este aceasta: la terminarea tuturor operaţiilor
de mutare, riscul minim de securitate se obţine dacă toate persoanele aflate ı̂n dreapta persoanei
celei mai ı̂nalte vor fi mai ı̂nalte decât cele aflate ı̂n stânga persoanei cele mai ı̂nalte. În plus,
ı̂nalţimile persoanelor vor fi crescătoare până la poziţia k a persoanei celei mai ı̂nalte şi de-
screscătoare după poziţia k.
Mai exact: dacă h1 , h2 , ..., hn sunt ı̂nălţimile persoanelor după finalizarea operaţiilor de
mutare, atunci: există o poziţie k, cu 1 & k & n astfel ı̂ncât
Cerinţe
Cunoscând numărul de persoane n şi ı̂nălţimile h1 , h2 , ..., hn ale acestora să se scrie un program
care determină :
1. Poziţia persoanei celei mai ı̂nalte ı̂n rândul iniţial, ı̂n cazul ı̂n care nu sunt necesare operaţii
de mutare.
2. Numărul minim de mutări necesare pentru ca rândul de persoane să prezinte un risc minim
de securitate.
Date de intrare
Pe prima linie a fişierului de intrare risc.in se găseşte numărul natural p a cărui valoare poate
fi doar 1 sau 2.
Pe a doua linie a fişierului de intrare se află numărul natural n cu semnificaţia din enunţ.
Pe a treia linie se găsesc n numere naturale, distincte: h1 , h2 , ..., hn , separate prin câte un
singur spaţiu, reprezentând ı̂nălţimile persoanelor.
Date de ieşire
Exemple:
CAPITOLUL 8. ONI 2015 8.2. RISC 195
Cerinţa 1. 20 puncte
Se determină ı̂nalţimea hmax şi poziţia pmax a celei mai ı̂nalte persoane.
Pentru fiecare poziţie 2 & i & pmax se verifică dacă h[i] ¿ h[i -1] şi similar, pentru fiecare
poziţie i % pmax se verifică dacă pentru persoana i aflată ı̂n dreapta acesteia se verifică dacă
hi $ hi 1. Pentru a determina dacă toate persoanele aflate ı̂n dreapta poziţiei pmax sunt
mai ı̂nalte decât cele aflate ı̂n stânga se verifică condiţia hpmax 1 $ hn.
Cerinţa 2. Total - 80 de puncte
Soluţia 1 - Complexitate O n n, prof. Carmen Popescu, C. N. ”Gheorge Lazăr” Sibiu
Se marchează valorile care trebuie mutate folosind un vector boolean. Simultan cu aceste
mutări vom calcula şi următoarele valori:
mx = maximul valorilor ce se mută la sfârşit
mn = minimul valorilor ce vor fi ı̂n dreapta maximului după mutări
Pentru fiecare ı̂nălţime hi, i $ k căutăm toate valorile din faţa sa care sunt mai mari, adică
hj % hi cu j $ i (este ı̂ncălcată prima condiţie de mai sus), acestea vor fi ”mutate”.
Vom ”muta” apoi la sfârşit toate valorile aflate ı̂n dreapta maximului care nu respectă cerinţa
a doua de mai sus, deci pentru fiecare ı̂nălţime hi cu i % k 1 vom ”muta” la sfârşit valorile
din faţa sa, care sunt mai mici decât hi, adică mutăm valorile hj cu j % k şi j $ i pentru care
hj $ hi.
”Mutăm” apoi valorile care au rămas ı̂n stânga maximului şi care sunt mai mari decât minimul
valorilor din dreapta (condiţia a treia de mai sus), deci mai mari decât mn.
Acum toate valorile sunt ı̂n ”jumătatea” potrivită, ı̂nsă s-ar putea ca elementele care se aflau
iniţial ı̂n dreapta maximului şi nu au fost mutate să fie mai mici decât maximul valorilor mutate la
sfârşit (ı̂ncalcă condiţia a doua de mai sus) si să trebuiască să le ”mutăm” la sfârşit. Vom ”muta”
aşadar la sfârşit valorile hi $ mx cu i % k.
O altă posibilitate de a ”aranja” şirul dat este să mutăm maximul la sfârşitul şirului şi apoi să
reluăm ”mutările” ca mai sus. Se va alege cea mai favorabilă din cele două variante.
Această soluţie obţine 30 de puncte din 80 posibile.
CAPITOLUL 8. ONI 2015 8.2. RISC 196
Cu roşu s-au reprezentat cele mai mici numere din şir aflate ı̂n ordine crescătoare ı̂n parcurgerea
şirului de la stânga la dreapta. Numărul acestora este Asci 3 (pentru i ' 5). Cu albastru sunt
figurate cele mai mari numere din şir aflate ı̂n ordine descrescătoare ı̂n parcurgerea şirului de la
stânga la dreapta. Numărul lor este Desc 2, iar capătul din stânga al acestui subşir ı̂ncepe la
poziţia pmax 7.
Numerele marcate cu negru trebuie să fie mutate. Ordinea mutării poate fi aleasă astfel ı̂ncât
să respecte cerinţa de risc minim.
pmax 7, Asc7 Desc 3 2 % Ascn 3.
Prin urmare, M n Ascpmax Desc 10 5 5
O altă situaţie este exemplificată mai jos:
CAPITOLUL 8. ONI 2015 8.2. RISC 197
#include <fstream>
#include <vector>
#include <algorithm>
int main()
{
ifstream fin("risc.in");
ofstream fout("risc.out");
if ( V == 1 )
{
bool ok = true;
for (int i = 0; i < n && ok; ++i)
{
if ( i && i <= pmax && a[i - 1] > a[i] )
ok = false;
if ( i == pmax )
k = cntAsc;
}
fin.close();
fout.close();
return 0;
}
#include <iostream>
#include <fstream>
#include <algorithm>
#include <bitset>
int a[100001];
int n,mx,p;
ifstream f("risc.in");
ofstream g("risc.out");
bitset<100001> b;
bitset<100001> c;
int main()
{
int v,i,j,k,mn;
f>>v>>n;
for (i=1;i<=n;i++)
{
f>>a[i];
if (a[i]>mx)
{
mx=a[i]; p=i;
}
}
if (v==1)
{
// ordinea din stanga
for (i=1;i<p;i++)
if (a[i]>a[i+1])
{
g<<-1<<"\n";
return 0;
}
// CAZUL 2:
// mutam maximul la sfarsit pe urma mutam ce nu respecta ordinea
// mn = minimul valorilor ce se vor afla in dreapta
mn=a[p];
int k1=1; // am mutat maximul la sfarsit
for (i=2;i<=n;i++)
if (i!=p)
for (j=i-1;j>=1;j--)
if (j!=p && a[j]>a[i] && !c[j])
{
c[j]=true;
k1++;
if (a[j]<mn) mn=a[j];
}
// cautam valorile ramase nemutate si care sunt mai mari decat
// minimul celor mutate si ele vor fi mutate
for (i=1;i<=n;i++)
if (i!=p && !c[i] && a[i]>mn)
k1++;
return 0;
}
int main()
{
ifstream fin("risc.in");
ofstream fout("risc.out");
if ( V == 1 )
{
bool ok = true;
for (int i = 0; i < n && ok; ++i)
{
if ( i && i <= pmax && a[i - 1] > a[i] )
ok = false;
if ( i == pmax )
k = cntAsc;
}
fin.close();
fout.close();
return 0;
}
8.3 roboti
Problema 3 - roboti 100 de puncte
O firmă de construcţii imobiliare a achiziţionat recent un teren dreptunghiular de dimensiuni
N M . Terenul este ı̂mpărţit ı̂n parcele de dimensiune 1 1. Pe unele dintre cele N M
parcele sunt plantaţi copaci. Firma doreşte construirea unui grandios complex comercial şi este
necesară defrişarea ı̂ntregului teren. ı̂n acest scop sunt utilizaţi roboţi, fiecare robot având baza
un pătrat de latură L. Suprafaţa defrişată de fiecare robot la un moment dat este chiar aria de
acoperire a robotului, L L. Fiecare robot pătrunde prin colţul stânga sus de coordonate 1, 1,
se poate deplasa doar ı̂n dreapta şi ı̂n jos şi poate părăsi suprafaţa numai prin colţul dreapta jos,
de coordonate N, M .
Cerinţe
Cunoscând dimensiunile N , M ale terenului şi coordonatele parcelelor ı̂n care sunt plantaţi
copaci se cere:
1. Numărul minim de roboţi necesari defrişării ı̂ntregului teren.
2. Să se răspundă la Q interogări de forma k, unde k este un număr natural. Pentru fiecare
interogare de această formă va trebui determinată latura minimă a unui robot astfel ı̂ncât să fie
necesari pentru defrişare cel mult k roboţi.
Date de intrare
Date de ieşire
a Dacă valoarea lui p este 1, se va rezolva numai cerinţa 1. În acest caz, ı̂n fişierul de
ieşire roboti.out se va scrie un singur număr natural n1 , reprezentând numărul minim de roboţi
utilizaţi.
a Dacă valoarea lui p este 2, se va rezolva numai cerinţa 2. În acest caz, ı̂n fişierul de ieşire
roboti.out se vor scrie Q linii. Fiecare linie i va conţine câte un număr natural ni , reprezentând
latura minimă a unui robot astfel ı̂ncât pentru defrişare să fie utilizaţi cel mult ki roboţi.
Exemple:
1688415 1 p 1
3352655 Dacă roboţii au latura 4, pentru defrişarea ı̂ntregului teren este
4738684 necesar un singur robot.
Atenţie! Pentru acest test se rezolvă doar cerinţa 1.
2 688415 41 p 2
3 352655 Prima valoare din fişierul de ieşire reprezintă latura minimă pe
4 738682 care o pot avea roboţii astfel ı̂ncât pentru defrişarea ı̂ntregului
1 3 teren să fie necesar un singur robot, conform primei interogări.
A doua valoare din fişierul de ieşire reprezintă latura minimă pe
care o pot avea roboţii astfel ı̂ncât pentru defrişarea ı̂ntregului
teren să fie necesari cel mult trei roboţi, conform celei de-a doua
interogări.
Atenţie! Pentru acest test se rezolvă doar cerinţa 2.
Se caută binar latura minimă a roboţilor care trebuie folosiţi pentru a nu depăşi numărul admis
de roboţi utilizaţi. Numărul de roboţi folosiţi se face la fel ca la cerinţa 1. Pentru un algoritm de
tip greedy, complexitatea soluţiei la cerinţa 2 este O Q n T log T
struct Cell
{
int i, j;
bool operator < (const Cell& c) const
{
if (i != c.i )
return i < c.i;
return j > c.j;
}
};
int n, m;
vector<Cell> cl;
int mx[1002][1002];
int main()
{
ifstream fin("roboti.in");
ofstream fout("roboti.out");
int x, y, p, T, L;
sort(cl.begin(), cl.end());
if (p == 1) // O(T * log(n)ˆ2)
{
fin >> L;
fout << CountRobots(L) << ’\n’;
}
else // O(Q * T * log(n)ˆ3)
{
int Q, k, lo, hi, Lmin;
{
Lmin = L;
hi = L - 1;
}
else
lo = L + 1;
}
}
}
fin.close();
fout.close();
}
return cnt;
}
ifstream fin("roboti.in");
ofstream fout("roboti.out");
int main()
{
int x, y, p, T, L;
if ( p == 1 )
{
fin >> L;
fout << CountRobots(L) << ’\n’;
}
else
{
int Q, k, lo, hi, Lmin;
if (CountRobots(L) <= k)
{
Lmin = L;
hi = L - 1;
}
else
lo = L + 1;
}
}
}
fin.close();
fout.close();
}
int CountRobots(int L)
{
int cnt = 0;
a = vector<VI>(n + 1, VI(m + 2));
return cnt;
}
/*
prof. Constantin Galatan
O(Q * T * log n)
*/
#include <iostream>
#include <fstream>
#include <algorithm>
#include <deque>
#define I q1.front().first
#define J q1.front().second
int main()
{
ifstream fin("roboti.in");
ofstream fout("roboti.out");
int x, y, p, T, L;
sort(q.begin(), q.end());
if ( p == 1 )
{
fin >> L;
fout << CountRobots(L) << ’\n’;
}
else
{
int Q, k, lo, hi, Lmin;
if ( CountRobots(L) <= k)
{
Lmin = L;
hi = L - 1;
}
else
lo = L + 1;
}
}
}
fin.close();
fout.close();
}
int CountRobots(int L)
{
int lastJ, lastI, i1, i2, j1, cnt = 0;
bool first(true);
for (q1 = q; !q1.empty(); q1.swap(q2))
{
cnt++;
CAPITOLUL 8. ONI 2015 8.3. ROBOTI 208
for ( ; !q1.empty(); )
{
// cobor cat pot pe verticala
if (!q1.empty() && J <= lastJ && J > lastJ - L)
{
q1.pop_front();
continue;
}
if ( J > lastJ )
{
i1 = I, i2 = min(n, i1 + L - 1), j1 = max(J, L);
lastJ = j1;
lastI = i1;
first = false;
}
}
CAPITOLUL 8. ONI 2015 8.4. CASA 209
return cnt;
}
8.4 casa
Problema 4 - casa 100 de puncte
În această poveste este vorba despre o casă cu mai multe camere. O cameră are forma unui
pătrat de latură 1. Dacă două camere au un perete comun, atunci se poate trece dintr-o cameră
ı̂n alta. Casa nu are neapărat formă dreptunghiulară.
O asemenea casă poate fi descrisă ı̂n povestea noastră ı̂n două moduri:
- prin matricea minimală: o matrice cu elemente 0 şi 1 ı̂n care există N valori egale cu 1, ce
corespund camerelor, iar prima linie, ultima linie, prima coloană şi ultima coloană au cel puţin
un element egal cu 1.
- prin construcţie: un şir de N 1 perechi ai , bi 1 & i $ n ı̂n care ai " r1, 2, ..., ix şi
bi " rN, S, E, V x. Camerele vor fi numerotate de la 1 la n. Perechea ai , bi precizează poziţia
camerei i 1 faţă de camera ai : E ı̂nseamnă la dreapta (est), N deasupra (nord), V la stânga
(vest), S dedesubt (sud). Observaţi că pentru prima cameră nu există nicio precizare!
De exemplu, casa de mai sus poate fi descrisă de şirul (1 E) (2 E) (2 S) (3 S), adică a doua
cameră e ”lipită” la est de prima cameră, următoarea (a treia) la est de camera 2, a patra la sud
de camera 2, iar ultima la sud de camera 3.
Cerinţe
1. Se dă descrierea unei case prin construcţie şi se cere descrierea acesteia prin matricea
minimală.
2. Se dă descrierea unei case prin matricea minimală şi se cere descrierea acesteia prin
construcţie.
Date de intrare
Date de ieşire
Dacă valoarea lui p este 1 atunci se va rezolva numai cerinţa 1. În acest caz fişierul
casa.out va conţine pe prima linie două numere naturale M şi N , separate prin câte un sin-
gur spaţiu reprezentând numărul de linii respectiv numărul de coloane ale matricei minimale, iar
pe următoarele M linii câte N valori 0 sau 1 separate prin câte un singur spaţiu.
Dacă valoarea lui p este 2 atunci se va rezolva numai cerinţa 2. În acest caz fişierul casa.out
va conţine pe prima linie două numere naturale N r şi C separate printr-un singur spaţiu. N r
reprezintă numărul de camere, iar C poziţia camerei 1 (cel mai mic număr de ordine al unei
CAPITOLUL 8. ONI 2015 8.4. CASA 210
coloane care conţine valoarea 1 ı̂n prima linie). Următoarele N r 1 linii vor conţine fiecare câte
două valori separate printr-un singur spaţiu, reprezentând descrierea unei case prin construcţie
conform precizărilor din enunţ. Coloanele vor fi numerotate ı̂ncepând de la 1, iar dacă există mai
multe soluţii va fi afişată cea mai mică ı̂n ordine lexicografică: perechea k, l va fi afişată ı̂naintea
perechii k, l dacă k $ k sau dacă k k şi l $ l (adică E ¡ N ¡ S ¡ V).
Exemple:
Cerinţa 1.
Se determină câţi paşi se fac spre nord, sud, est, vest, pornind din camera 1. Astfel se determină
dimensiunile matricei, apoi, având poziţionată corect camera 1, se completează matricea cu 1
urmând succesiv paşii daţi .
Cerinţa 2.
Se determină uşor poziţia primei camere, pe linia 1. Fiecare din camerele următoare se deter-
mină căutând pentru camera anterioară ı̂n cele 4 direcţii vecinii cu valoarea 1 care nu au fost deja
enumeraţi.
Deoarece nu se cunosc limitele maxime ale dimensiunilor m şi n ale matricei, dar se cunoaşte
produsul lor m*n ¡= 100 000 se foloseşte ı̂n locul unei matrice un vector de dimensiune 100 000
(liniarizare matrice).
#define M 100001
int p[M][4],t[M];
int main()
{
ifstream fi("casa.in");
ofstream fo("casa.out");
CAPITOLUL 8. ONI 2015 8.4. CASA 211
int a[M],m,n,i,j,k,v,nc,op;
char c[M],pct[7]="xENSV";
fi>>op;
if (op==1)
{
fi>>nc;
for (i=1;i<nc;i++)
fi>>a[i]>>c[i];
int max_e=0,max_v=0,max_n=0,max_s=0;
for (i=1;i<=nc;i++)
{
for(j=0;j<4;j++)
p[i+1][j]=p[a[i]][j];
switch(c[i])
{
case ’N’:
p[i+1][2]--;
p[i+1][1]++;
if(p[i+1][1]>max_n)
max_n=p[i+1][1];
break;
case ’S’:
p[i+1][1]--;
p[i+1][2]++;
if(p[i+1][2]>max_s)
max_s=p[i+1][2];
break;
case ’E’:
p[i+1][3]--;
p[i+1][0]++;
if(p[i+1][0]>max_e)
max_e=p[i+1][0];
break;
case ’V’:
p[i+1][0]--;
p[i+1][3]++;
if(p[i+1][3]>max_v)
max_v=p[i+1][3];
break;
}
}
m=max_n+max_s+1;
n=max_e+max_v+1;
fo<<m<<" "<<n<<"\n";
p[1][1]=max_n+1;
p[1][2]=max_v+1;
for (i=1;i<nc;i++)
{
switch(c[i])
{
case ’N’:
p[i+1][1]=p[a[i]][1]-1;
p[i+1][2]=p[a[i]][2];
break;
case ’S’:
p[i+1][1]=p[a[i]][1]+1;
p[i+1][2]=p[a[i]][2];
break;
case ’E’:
p[i+1][1]=p[a[i]][1];
p[i+1][2]=p[a[i]][2]+1;
break;
case ’V’:
p[i+1][1]=p[a[i]][1];
p[i+1][2]=p[a[i]][2]-1;
break;
}
}
for (i=1;i<=nc;i++)
CAPITOLUL 8. ONI 2015 8.4. CASA 212
t[(p[i][1]-1)*n+p[i][2]-1]=1;
for (i=1;i<=m;i++)
{
for (j=1;j<=n;j++)
fo<<t[(i-1)*n+j-1]<<" ";
fo<<"\n";
}
}
if (op==2)
{
fi>>m>>n;
nc=0;
k=0;
for (j=1;j<=n;j++)
{
fi>>t[j-1];
nc+=t[j-1];
if (k==0 && t[j-1]==1)
k=j;
}
for (i=2;i<=m;i++)
for (j=1;j<=n;j++)
{
fi>>t[(i-1)*n+j-1];
nc+=t[(i-1)*n+j-1];
}
fo<<nc<<" "<<k<<"\n";
p[1][0]=1;
p[1][1]=k;
t[k-1]=2;
k=1;
v=1;
while (v<nc)
{
i=p[k][0];
j=p[k][1];
p[v][2]=4;
}
k++;
}
for (k=2;k<=nc;k++)
{
i=p[k][0];
j=p[k][1];
switch(p[k][2])
{
case 1: j--; break;
case 2: i++; break;
case 3: i--; break;
case 4: j++; break;
}
fo<<t[(i-1)*n+j-1]-1<<" "<<pct[p[k][2]]<<"\n"; }
}
return 0;
}
struct casa
{
int x,y;
} coada[100002];
int caz,t,leg,lin,col,lmax,cmax,i,j,n,m,st,dr,k;
char c;
int main()
{
fin>>caz;
if (caz==1)
{
fin>>t;
coada[1].x=coada[1].y=1;
lmax=1;
cmax=1;
for(i=2;i<=t;i++)
{
fin>>leg>>c;
lin=coada[leg].x;
col=coada[leg].y;
if (c==’E’) col++;
if (c==’V’) col--;
if (c==’N’) lin--;
if (c==’S’) lin++;
coada[i].x=lin;
coada[i].y=col;
// a[lin][col]=1;
if (lin>lmax) lmax=lin;
if (col>cmax) cmax=col;
}
fout<<lmax<<" "<<cmax<<endl;
CAPITOLUL 8. ONI 2015 8.4. CASA 214
bool a[lmax+2][cmax+2];
///initializare
for(i=1;i<=lmax;i++)
for(j=1;j<=cmax;j++)
a[i][j]=0;
///copiere din coada
a[1][1]=1;
for(i=2;i<=t;i++) a[coada[i].x][coada[i].y]=1;
///afisare
for(i=1;i<=lmax;i++)
{
for(j=1;j<cmax;j++)
fout<<a[i][j]<<" ";
fout<<a[i][j]<<endl; ///ultimul de pe linie
}
}
if (caz==2)
{
fin>>n>>m;
bool a[n+2][m+2]; ///definire locala de matrice
///initializare bordura
for(i=0;i<=n+1;i++) a[i][0]=a[i][m+1]=0;
for(j=1;j<=m+1;j++) a[0][j]=a[n+1][j]=0;
///prelucrare
t=0;
lmax=0;
cmax=0;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
fin>>a[i][j];
t+=a[i][j];
if ((lmax+cmax==0) && (a[i][j]==1)) lmax=i,cmax=j;
}
fout<<t<<" "<<cmax<<"\n";
st=dr=1;
coada[1].x=lmax;
coada[1].y=cmax;
a[lmax][cmax]=0;
while(st<=dr)
{
// st - nr camerei vecina
lmax=coada[st].x;
cmax=coada[st].y;
for(k=0;k<4;k++)
if (a[lmax+dx[k]][cmax+dy[k]]==1)
{
fout<<st;
if (k==0) fout<<" E\n";
if (k==1) fout<<" N\n";
if (k==2) fout<<" S\n";
if (k==3) fout<<" V\n";
a[lmax+dx[k]][cmax+dy[k]]=0;
dr++;
coada[dr].x=lmax+dx[k];
coada[dr].y=cmax+dy[k];
}
st++;
}
}
return 0;
}
ifstream f("casa.in");
ofstream g("casa.out");
struct matrice
{
int l,c;
} q[100010];
int a[100010];
int dx[4]={0,1,-1,0}, dy[4]={1,0,0,-1},dx1[]={0,-1,1,0},dy1[]={-1,0,0,1};
char dir1[]="ESNV";
int var,i,j,n,lmax,cmax,cmin,v,m,nr,lmin;
char ch;int dir[256];
int main()
{
f>>var;
dir[’E’]=0;
dir[’S’]=1;
dir[’N’]=2;
dir[’V’]=3;
if(var==1)
{
f>>n;
q[1].l=1;
q[1].c=1;
cmin=lmin=1000000000;
for(i=2;i<=n;i++)
{
f>>v>>ch;
q[i].l=q[v].l+dx[dir[ch]];
q[i].c=q[v].c+dy[dir[ch]];
if(q[i].l>lmax)
lmax=q[i].l;
if(q[i].l<lmin)
lmin=q[i].l;
if(q[i].c>cmax)
cmax=q[i].c;
if(q[i].c<cmin)
cmin=q[i].c;
}
if(cmin<=0)
{
cmax+=(-cmin)+1;
for(i=1;i<=n;i++)
q[i].c+=(-cmin)+1;
}
if(lmin<=0)
{
lmax+=(-lmin);
for(i=1;i<=n;i++)
q[i].l+=(-lmin)+1;
}
g<<lmax<<" "<<cmax<<’\n’;
sort(q+1,q+n+1,cmp);
CAPITOLUL 8. ONI 2015 8.5. LENES 216
int k=1;
for(i=1;i<=lmax;i++)
{
for(j=1;j<=cmax;j++)
if(i==q[k].l&&j==q[k].c)
{
g<<1<<" ";k++;
}
else
g<<0<<" ";
g<<’\n’;
}
return 0;
}
f>>n>>m;
int x,c1;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
f>>x;
if(x==1)
{
nr++;
if(nr==1)
c1=j;
a[(i-1)*m+j]=-1;
}
}
g<<nr<<" "<<c1<<’\n’;
q[1].l=1;
q[1].c=c1;
a[c1]=1;
int p=1,u=1;
nr=1;
while(p<=u)
{
for(int k=0;k<4;k++)
{
int l=q[p].l+dx[k];
int c=q[p].c+dy[k];
int t=(l-1)*m+c;
if(l>=1&&l<=n&&c>=1&&c<=m&&a[t]==-1)
{
nr++;
g<<a[(q[p].l-1)*m+q[p].c]<<" "<<dir1[k]<<’\n’;
a[t]=nr;
u++;
q[u].l=l;
q[u].c=c;
}
}
p++;
}
return 0;
}
8.5 lenes
Problema 5 - lenes 100 de puncte
CAPITOLUL 8. ONI 2015 8.5. LENES 217
Leneşul este un animal foarte leneş. El se deplasează numai ı̂n linie dreaptă, dar face din când
ı̂n când câte un popas. ı̂n această problemă leneşul trebuie să traverseze de la nord la sud şi
ı̂napoi un teren reprezentat de o matrice de dimensiuni M N cu valori numere naturale. Valorile
reprezintă efortul cerut pentru traversarea zonei respective. Leneşul va alege o coloană pentru
traversarea matricei, iar pentru popasuri, ı̂n număr de k1, va alege zone alăturate drumului din
coloana din stânga sau cea din dreapta. ı̂n cazul ı̂n care se va ı̂ntoarce va proceda la fel, dar va
face k2 popasuri. Regulile problemei cer ca cele două drumuri să nu aibă zone comune.
Cerinţe
Cunoscând dimensiunile M , N ale terenului, numărul de popasuri k1, k2 şi efortul pentru
traversarea fiecărei zone a terenului, să se determine:
1. Efortul minim de parcurgere a terenului de la Nord la Sud, folosind k1 popasuri.
2. Efortul minim de parcurgere a terenului de la Nord la Sud şi ı̂napoi de la Sud la Nord,
folosind k1 popasuri la deplasarea Nord - Sud, respectiv k2 popasuri la deplasarea Sud - Nord.
Date de intrare
Date de ieşire
a Dacă valoarea lui p este 1, se va rezolva numai cerinţa 1. ı̂n acest caz fişierul lenes.out va
conţine un singur număr natural reprezentând efortul minim necesar pentru traversarea terenului
ı̂n condiţiile date de la Nord la Sud.
a Dacă valoarea lui p este 2, se va rezolva numai cerinţa 2. ı̂n acest caz fişierul lenes.out va
conţine un singur număr natural reprezentând efortul minim necesar pentru traversarea terenului
ı̂n condiţiile date ı̂n ambele sensuri de la Nord la Sud şi de la Sud la Nord.
Exemple:
2 19 p 2
3722 Leneşul traversează terenul de la Nord la Sud pe coloana a 6-a
2 1 33 9 99 4 7 cu popasuri ı̂n zonele (2, 7), (3, 7), iar de la Sud la Nord pe
1 1 44 9 99 2 3 coloana a 2 - a, cu popasuri ı̂n zonele (3, 1) şi (2, 1). Efortul
2 1 55 9 99 2 2 de deplasare ı̂ntre zonele (3, 6) şi (3, 2) este nul.
Atenţie! Pentru acest test se rezolvă doar cerinţa 2.
#include <stdio.h>
#include <string.h>
#include <algorithm>
int get_line_sum( int lin, int k, bool up = true, bool down = true )
CAPITOLUL 8. ONI 2015 8.5. LENES 219
{
int res = Sum[lin];
return res;
}
int case1()
{
return ret;
}
int case2()
{
left[1] = V[1];
for ( int i = 2; i <= N; ++ i )
left[i] = min(V[i], left[i - 1]);
right[N] = V[N];
for ( int i = N - 1; i > 0; -- i )
right[i] = min(V[i], right[i + 1]);
if ( parity )
{
parity = false;
-- i;
}
else
CAPITOLUL 8. ONI 2015 8.5. LENES 221
{
parity = true;
}
swap( k1, k2 );
}
return ret;
}
int main()
{
freopen(IN, "r", stdin);
freopen(OUT, "w", stdout);
swap(N, M);
return 0;
}
int a[502][502],sum[502],m,n;
int mn=INT_MAX;
ifstream f("lenes.in");
ofstream g("lenes.out");
int main()
{
int i,j,i1,i2,i3,ok,j1,k1,k2,k3,k4,p;
CAPITOLUL 8. ONI 2015 8.5. LENES 222
int s1,s2,s;
f>>p;
f>>m>>n>>k1>>k2;
for (i=1;i<=m;i++)
{
for (j=1;j<=n;j++)
{
f>>a[i][j];
sum[j] += a[i][j];
}
a[i][0]=INT_MAX;
a[i][n+1]=INT_MAX;
}
for (j=1;j<=n;j++)
{
a[0][j]=INT_MAX;
a[m+1][j]=INT_MAX;
// sortam coloana j
for (i=1;i<m;i++)
for (i1=i+1;i1<=m;i1++)
if (a[i][j]>a[i1][j])
{
s=a[i][j];
a[i][j]=a[i1][j];
a[i1][j]=s;
}
if (p==1)
{
for (j=1;j<n;j++)
{
s=calc(j,k1);
if (s<mn) mn=s;
}
g<<mn<<"\n";
}
else
{
int cc;
// cazul 1: doua coloane alaturate (j si j+1)
for (j=2;j<n-1;j++)
{
for (int g=1;g<=2;g++)
{
s=0;
for (i=1;i<=k1;i++)
s=s+a[i][j-1];
for (i=1;i<=k2;i++)
s+=a[i][j+2];
s=s+sum[j]+sum[j+1];
if (s<mn) mn=s;
s1=k1; k1=k2; k2=s1;
}
}
ok=0;
if (i1>0 &&
a[i1][j-1]>=a[i3][j+3] &&
a[i1][j-1]>a[i2][j+1]) // inlocuim un popas de pe
// coloana j-1 cu popas pe
{
// coloana j+1
s=s+a[i2][j+1]-a[i1][j-1];
i1--;
i2++;
ok=1;
}
else
if (i3>0 &&
a[i3][j+3]>=a[i1][j-1] &&
a[i3][j+3]>a[i2][j+1]) // inlocuim un popas de pe
// coloana j-1 cu popas pe
{
// coloana j+1
s=s+a[i2][j+1]-a[i3][j+3];
i3--;
i2++;
ok=1;
}
} while (ok==1);
s+=sum[j]+sum[j+2];
if (s<mn)
mn=s;
i1=k1+1;
i2=1;
for (i=1;i<=k2;i++)
if (i1<=m && a[i1][2]<a[i2][4])
{
s1+=a[i1][2]; i1++;
}
else
{
s1+=a[i2][4]; i2++;
}
s1+=sum[1]+sum[3];
if (s1<mn)
mn=s1;
s1=k1;
k1=k2;
k2=s1;
}
i1=k1+1;
i2=1;
for (i=1;i<=k2;i++)
if (i1<=m && a[i1][n-1]<a[i2][n-3])
{
s1+=a[i1][n-1]; i1++;
}
else
CAPITOLUL 8. ONI 2015 8.5. LENES 224
{
s1+=a[i2][n-3]; i2++;
}
s1+=sum[n]+sum[n-2];
if (s1<mn)
mn=s1;
s1=k1;
k1=k2;
k2=s1;
}
g<<mn<<"\n";
}
}
int sum[502][502],a[502][502],m,n;
int mn=INT_MAX;
ifstream f("lenes.in");
ofstream g("lenes.out");
int main()
{
int p,k1,k2,i,j,s,s1,s2,k,i1,i2,i3,gg;
int mn=INT_MAX;
CAPITOLUL 8. ONI 2015 8.5. LENES 225
f>>p;
f>>m>>n>>k1>>k2;
for (i=1;i<=m;i++)
for (j=1;j<=n;j++)
f>>a[j][i];
for (j=1;j<=n;j++)
{
sort(a[j]+1,a[j]+m+1);
sum[j][1]=a[j][1];
for (i=2;i<=m;i++)
sum[j][i]=a[j][i]+sum[j][i-1];
}
if (p==1)
{
for (i=1;i<=n;i++)
{
s=calc(i,k1);
if (s<mn) mn = s;
}
g<<mn<<’\n’;
}
else
{
// cazul 1: doua coloane consecutive
for (i=2;i<n-1;i++)
{
s = sum[i-1][k1] + sum[i][m] + sum[i+1][m] + sum[i+2][k2];
if (s<mn) mn=s;
// coloanele 1 si 3
s = sum[1][m] + sum[3][m];
if (n>3)
{
for (j=0;j<=k2;j++)
if (k1+j<=m && k2>j)
{
s1 = sum[2][k1+j];
s1 += sum[4][k2-j];
s1 += s;
if (s1<mn) mn=s1;
}
CAPITOLUL 8. ONI 2015 8.6. SIPET 226
}
else
{
s=s+sum[2][k1+k2];
if (s<mn) mn=s;
}
// coloanele n-2 si n
if (n>3)
{
s = sum[n-2][m] + sum[n][m];
for (j=0;j<=k1;j++)
if (k2+j<=m && k1>j)
{
s1 = sum[n-3][k1-j];
s1 += sum[n-1][k2+j];
s1 += s;
if (s1<mn) mn=s1;
}
}
i1=k1; k1=k2; k2=i1;
}
s = s2 + calc(i1,k1);
if (s<mn) mn=s;
}
}
g<<mn<<’\n’;
}
}
8.6 sipet
Problema 6 - sipet 100 de puncte
Un arheolog a găsit un sipet interesant. După ce l-a deschis cu grijă, a
constatat cu surprindere că sipetul conţine bănuţi de aur. Uitându-se mai
atent a mai găsit ceva: un pergament ascuns ı̂ntr-un compartiment secret al
sipetului, cu un text scris ı̂ntr-o limbă antică, pe care, din fericire, arheologul
o cunoştea. Din text a reieşit că un grup de negustori foarte bogaţi a vrut
să ascundă ı̂n mare secret averea breslei lor, formată din monede de aur,
deoarece se prevestea un război cumplit. Negustorii ştiau că există şanse ca această comoară să
fie găsită şi confiscată de duşmani, deci s-au sfătuit cum e mai bine să procedeze, cum să ascundă
comoara. Arheologul a reuşit să deducă din text următoarele:
a) Cele N monede, care formau averea breslei, au fost ı̂mpărţite ı̂n maximum trei feluri de
grămezi, formate din p1, p2 şi p3 bănuţi, p1, p2 şi p3 fiind numere prime consecutive, p1 $ p2 $ p3.
Fiecare grămadă a fost pusă ı̂n ı̂ntregime ı̂ntr-un sipet.
b) Este posibil să existe 0 (zero) grămezi formate din p1 sau p2 sau p3 monede, scopul fiind
să se obţină o ı̂mpărţire ı̂n care numărul monedelor rămase nedistribuite să fie minim, iar dacă
există mai multe posibilităţi, se alege aceea pentru care numărul de grămezi este mai mare. Dacă
există mai multe astfel de soluţii, se consideră corectă oricare dintre ele.
c) Monedele care nu au putut fi distribuite conform regulilor stabilite, au fost donate bisericii.
Cerinţe
CAPITOLUL 8. ONI 2015 8.6. SIPET 227
Scrieţi un program care determină numărul maxim S de sipete şi numărul sipetelor cu p1, p2
respectiv p3 monede, precum şi suma donată bisericii.
Date de intrare
Fişierul sipet.in conţine, pe prima linie numărul natural T , iar pe următoarele T linii câte
două numerele naturale N şi p1, despărţite printr-un singur spaţiu.
Date de ieşire
Fişierul sipet.out va conţine pe primele T linii câte 5 numere naturale, separate prin câte
un spaţiu: S, x, y, z şi r, reprezentând numărul maxim S de sipete, numărul x de sipete cu
p1 monede, numărul y de sipete cu p2 monede, respectiv numărul z de sipete cu p3 monede şi
numărul r de monede donate bisericii, corespunzătoare datelor de intrare de pe linia T 1 a
fişierului sipet.in. Dacă există mai multe soluţii corecte, este acceptată oricare dintre ele.
Exemple:
3) verificăm dacă ı̂n cele 2 seturi de valori posibile exista valori comune. Din setul de valori
comune alegem soluţia cu p minim şi S maxim
Explicarea complexităţii:
Amintim ca a aparţine intervalului 0.. N ©p1 şi b, c şi p aparţin intervalul 0..p1.
De asemenea p1 p1 & N .
Numărul de valori posibile din prima expresie va fi O N ©p1 p1 O N
Numărul de valori posibile din a doua expresie va fi O p1 p1 O N
Specificăm că verificarea egalităţii se va face folosind un vector de apariţii ı̂n O 1
#include <stdio.h>
#include <assert.h>
#define NMax 10000010
struct pereche
{
int a, b;
};
int main()
{
scanf("%d", &Tes);
while ( Tes -- )
{
scanf("%d%d", &N, &p[1]);
assert(isPrime(p[1]));
for ( int i = 2; i <= 3; ++ i )
for ( p[i] = p[i - 1] + 1; !isPrime(p[i]); ++ p[i] );
return 0;
}
ONI 2014
9.1 harta
Problema 1 - harta 100 de puncte
Pe baza unei imagini preluate din satelit, se realizează harta unei mici localităţi. Localitatea
ocupă o suprafaţă dreptunghiulară, cu laturile orientate pe direcţiile Nord-Sud, respectiv Est-Vest.
Studiind imaginea obţinută de la satelit, cartografii au constatat că toate cele k clădiri au
forma unor dreptunghiuri distincte. Imaginea poate fi reprezentată sub forma unui tablou cu
n m celule aşezate pe n linii numerotate de la 1 la n şi m coloane numerotate de la 1 la m.
Numim drum, un dreptunghi al tabloului care străbate ı̂ntreaga localitate pe direcţia Est-Vest
şi are un număr maxim de linii sau un dreptunghi care străbate ı̂ntreaga localitate pe direcţia
Nord-Sud şi are un număr maxim de coloane. Drumurile, evident, nu trebuie să treacă prin
clădiri.
Cartografii sunt interesaţi ca pe această hartă să fie reprezentate la scară doar clădirile, nu
şi drumurile. De aceea, pentru realizarea hărţii, lăţimile drumurilor au fost reduse la o singură
celulă.
Tabloul care reprezintă imaginea localităţii se codifică astfel: 1 pentru o celulă ocupată de o
clădire şi 0 pentru o celulă neocupată.
Cerinţe
Cunoscând n, m şi k, precum şi tabloul care codifică imaginea, se cere să se determine:
1. Numărul S de celule ocupate de către clădirea pătratică cu latura maximă şi numărul
de clădiri C alese dintre celelalte k 1 clădiri, cu proprietatea că fiecare dintre ele ”ı̂ncape” ı̂n
interiorul clădirii pătratice cu latură maximă, fără să se suprapună peste celulele marginale ale
acesteia.
2. Tabloul care reprezintă harta, ı̂n urma prelucrării imaginii iniţiale.
Date de intrare
Fişierul de intrare harta.in conţine pe prima linie un număr natural p. Pentru toate testele
de intrare, numărul p poate avea doar valoarea 1 sau valoarea 2.
Pe linia a doua se găsesc numerele naturale n, m şi k separate prin câte un spaţiu.
Pe fiecare dintre următoarele k linii, se găsesc câte patru numere naturale i1 j1 i2 j2 separate
prin câte un spaţiu, primele două numere reprezentând coordonatele celulei din extremitatea Nord-
Vest, iar ultimele două, coordonatele celulei din extremitatea Sud-Est pentru fiecare dintre cele k
clădiri.
Date de ieşire
a Dacă valoarea lui p este 1, atunci se va rezolva numai cerinţa 1. În acest caz, ı̂n fişierul de
ieşire harta.out se vor scrie cele două numere S şi C având semnificaţia descrisă la cerinţa 1,
separate printr-un singur spaţiu.
a Dacă valoarea lui p este 2, atunci se va rezolva numai cerinţa 2. În acest caz, fişierul de
ieşire harta.out va conţine tabloul care reprezintă harta obţinută pe baza imaginii din satelit.
Fişierul va avea n1 linii. Pe fiecare linie se vor găsi câte m1 valori 0 sau 1 separate prin câte un
singur spaţiu. Celulele situate pe marginile clădirilor vor avea valoarea 1. Celulele din interiorul
clădirilor, ca şi cele din exterior, vor avea valoarea 0.
230
CAPITOLUL 9. ONI 2014 9.1. HARTA 231
Exemple:
Cerinţa a) - 20 puncte
Se determină latura Lmax a celei mai mari clădiri pătratice. Apoi, pentru fiecare clădire, se
determină dacă laturile sale de lungimi L şi H ı̂ndeplinesc condiţia: L $ Lmax 1 şi H $ Lmax 1.
Cerinţa b) Total - 80 de puncte
Soluţia 1 - 30 de puncte
Liniile şi coloanele care trebuie şterse trebuie să nu conţină valori 1. Se reţin numerele de
ordine acele acestor linii şi coloane. Dacă determinarea acestor linii sau coloane se face prin
parcurgerea lor şi ştergerea se fece linie cu linie, respectiv coloană cu coloană, atunci, ı̂n funcţie
de alte optimizări, se pot obţine 30 de puncte.
Soluţia 2 - 55 de puncte
CAPITOLUL 9. ONI 2014 9.1. HARTA 232
Se reţin la fel ca ı̂n soluţia anterioară numerele de ordine ale linilor şi coloanelor care trebuie
şterse. Pentru fiecare drum pe hartă, se şterg numărul maxim posibil de linii, respectiv coloane
adiacente.
Solutia 3 - 80 de puncte
În timpul citirii se generează clădirile ı̂n interiorul matricei a conform cerinţelor de afişare.
Pentru a evita operaţiile de ştergere de linii/coloane se marchează liniile/coloanele care trebuie
sa fie şterse astfel:
- initial a[i][0]=0; respectiv a[0][j]=0;
- dacă linia i / coloana j intersectează cel puţin o clădire, atunci a[i][0]=1; respectiv a[0][j]=1;
- dacă a[i][0]==0 si a[i+1][0]==0, atunci a[i][0]=2; (marcăm linia i pentru eliminare)
- dacă a[0][j]==0 si a[0][j+1]==0, atunci a[0][j]=2; (marcăm coloana j pentru eliminare)
- se afişează toate elementele a[i][j] pentru care a[i][0]!=2 && a[0][j]!=2
Soluţia 4 - 80 de puncte
Se reţin ı̂n şirurul x toate liniile pe care se găseşte cel puţin o celulă marginală ocupată de
o clădire, iar ı̂n şirul y toate coloanele pe care se găseşte cel puţin o celulă marginală ocupată
de o clădire. Pentru fiecare dreptunghi i1 j1 i2 j2, se inserează ı̂n x şi y şi coordonatele celulei
i1 1, j1 1 situate ı̂n exteriorul cădirii.
ı̂n şirurile xa şi xb se reţin liniile şi coloanele tuturor celulelor marginale ocupate de clădiri.
Se ordonează crescător şirurile x şi y, apoi, pentru fiecare valoare din şirul xa, se caută binar
poziţia i a acesteia ı̂n şirul x, iar pentru fiecare valoare din şirul ya, se caută binar poziţia j a
acesteia ı̂n şirul y.
Poziţia i, j obţinută astfel reprezintă noile coordonate ale unei celule marginale ocupate de
o clădire pe hartă.
#include <fstream>
#include <vector>
#include <algorithm>
#define pb push_back
#define DIM 1501
ifstream fin("harta.in");
ofstream fout("harta.out");
struct Dr
{
int i1, j1, i2, j2;
Dr(int _i1, int _j1, int _i2, int _j2)
: i1(_i1), j1(_j1), i2(_i2), j2(_j2) {
}
};
vector<Dr> d;
int main()
{
fin >> T >> N >> M >> k;
int i1, j1, i2, j2;
x.pb(0), y.pb(0);
s1[0] = true;
s2[0] = true;
for ( int i = 0; i < k; ++i )
{
fin >> i1 >> j1 >> i2 >> j2;
d.pb(Dr(i1, j1, i2, j2));
L = i2 - i1 + 1, H = j2 - j1 + 1;
if ( L == H && L > Lmax )
Lmax = L;
if ( !s1[i1 - 1] )
x.pb(i1 - 1), s1[i1 - 1] = true;
if ( !s2[j1 - 1] )
y.pb(j1 - 1), s2[j1 - 1] = true;
imax = max(imax, i2);
jmax = max(jmax, j2);
}
if ( T == 1 )
{
for ( int i = 0; i < k; ++i )
{
L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
if ( L < Lmax - 1 && H < Lmax - 1 )
nr_dr++;
}
fout << Lmax * Lmax << ’ ’ << nr_dr << ’\n’;
}
else
{
c1 = VI(imax + 1);
c2 = VI(jmax + 1);
n = 0, m = 0; int i, j;
for (size_t k = 0; k < xa.size(); ++k )
{
i= GetPos(b1, xa[k]);
CAPITOLUL 9. ONI 2014 9.1. HARTA 234
j = GetPos(b2, ya[k]);
n = max(n, i), m = max(m, j);
A[i][j] = 1;
}
if ( n < M ) n++;
if ( m < M ) m++;
WriteMatr(A, n, m);
}
fin.close();
fout.close();
return 0;
}
while ( lo <= hi )
{
mid = lo + (hi - lo) / 2;
if ( v[mid] == val )
return mid;
if ( val < v[mid] )
hi = mid - 1;
else
lo = mid + 1;
}
return 0;
}
# include <fstream>
# include <cstdio>
int n,m,Lmax,p,k,S,s,C;
int x[1501],linii[1501],coloane[1501];
struct dreptunghi
{
int i1,i2,j1,j2;
};
dreptunghi d[1001],dmax,d2[1001],d1[1001];
void read ()
{
FILE *fin=fopen("harta.in","rt");
fscanf(fin,"%d%d%d%d",&p,&n,&m,&k);
for(int i=1;i<=k;++i)
{
fscanf(fin,"%d%d%d%d",&d[i].i1,&d[i].j1,&d[i].i2,&d[i].j2);
d1[i]=d2[i]=d[i];
CAPITOLUL 9. ONI 2014 9.1. HARTA 235
}
fclose(fin);
}
void solve()
{
int a,b;
if(p==1)
{
for(int i=1;i<=k;i++)
{
a=(d[i].i2-d[i].i1+1);
b=(d[i].j2-d[i].j1+1);
if(a==b)
{
s=a*b;
if(s>S)
{
S=s;
dmax=d[i];
}
}
}
for(int i=1;i<=k;i++)
{
if(d[i].i2-d[i].i1+1<=dmax.i2-dmax.i1-1&&
d[i].j2-d[i].j1+1<=dmax.j2-dmax.j1-1)
C++;
}
fprintf(fout,"%d %d\n",S,C);
}
else
{
int i,j,di=0,dj=0,ix=1,iy,xx=1,yy=1;
dreptunghi aux;
for(i=1;i<k;i++)
for(j=i+1;j<=k;j++)
{
if(d[i].i1>d[j].i1)
{
aux=d[i];
d[i]=d[j];
d[j]=aux;
}
if(d2[i].i2>d2[j].i2)
{
aux=d2[i];
d2[i]=d2[j];
d2[j]=aux;
}
if(d1[i].j1>d1[j].j1)
{
aux=d1[i];
d1[i]=d1[j];
d1[j]=aux;
}
}
for(i=1;i<=k;i++)
{
if(d[i].i1-2>=xx)
for(int ix=xx;ix<=d[i].i1-2;ix++)
linii[ix]=1;
if(d[i].i2>=xx)
xx=d[i].i2+1;
if(d1[i].j1-2>=yy)
for(int iy=yy;iy<=d1[i].j1-2;iy++)
coloane[iy]=1;
if(d1[i].j2>=yy)
CAPITOLUL 9. ONI 2014 9.1. HARTA 236
yy=d1[i].j2+1;
}
if(n-xx>=1)
for(int ix=xx;ix<=n-1;ix++)
linii[ix]=1;
if(m-yy>=1)
for(int iy=yy;iy<=m-1;iy++)
coloane[iy]=1;
int ii=1,iii=1;
i=1;
while(i<=n)
{
if(!linii[i])
{
while(d[ii].i1==i&&ii<=k)
{
x[d[ii].j1]=x[d[ii].j2]=d[ii].i2-d[ii].i1+1;
for(int jj=d[ii].j1+1;jj<d[ii].j2;jj++)
x[jj]=1;
ii++;
}
while(d2[iii].i2==i&&iii<=k)
{
for(int jj=d2[iii].j1+1;jj<d2[iii].j2;jj++)
x[jj]=1;
iii++;
}
for(j=1;j<=m;j++)
if(!coloane[j])
if(!x[j])
fprintf(fout,"0 ");
else
{
fprintf(fout,"1 ");
x[j]--;
}
fprintf(fout,"\n");
}
i++;
}
}
}
int main()
{
read();
solve();
fclose(fout);
return 0;
}
struct Drept
{
int i1, j1, i2, j2;
CAPITOLUL 9. ONI 2014 9.1. HARTA 237
} d[DIM];
int main()
{
fin >> T >> N >> M >> k;
int i1, j1, i2, j2;
if ( T == 1 )
{
for ( int i = 0; i < k; ++i )
{
L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
if ( L < Lmax - 1 && H < Lmax - 1 )
nr_dr++;
}
if ( k > 1 )
{
for ( int j = col + 1; j < col + k; ++j )
a[1][j] = 2;
col += k - 1;
}
}
if ( k > 1 )
{
for ( int i = lin + 1; i < lin + k; ++i )
a[i][1] = 2;
lin += k - 1;
}
}
CAPITOLUL 9. ONI 2014 9.1. HARTA 238
WriteMatr(a, N, M);
}
fin.close();
fout.close();
return 0;
}
void DeleteLine(int L)
{
for ( int j = 1; j <= M; ++j )
for ( int i = L; i < N; ++i )
a[i][j] = a[i + 1][j];
N--;
}
void DeleteColumn(int C)
{
for ( int i = 1; i <= N; ++i )
for ( int j = C; j < M; ++j )
a[i][j] = a[i][j + 1];
M--;
}
ifstream fin("harta.in");
ofstream fout("harta.out");
struct Drept
{
int i1, j1, i2, j2;
} d[DIM];
int main()
{
fin >> T >> N >> M >> k;
int i1, j1, i2, j2;
{
fin >> i1 >> j1 >> i2 >> j2;
d[i].i1 = i1, d[i].j1 = j1, d[i].i2 = i2, d[i].j2 = j2;
L = i2 - i1 + 1, H = j2 - j1 + 1;
if ( L == H && L > Lmax )
Lmax = L;
if ( T == 1 )
{
for ( int i = 0; i < k; ++i )
{
L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
if ( L < Lmax - 1 && H < Lmax - 1 )
nr_dr++;
}
if ( k > 1 )
{
for ( int j = col + 1; j < col + k; ++j )
a[1][j] = 2;
col += k - 1;
}
}
if ( k > 1 )
{
for ( int i = lin + 1; i < lin + k; ++i )
a[i][1] = 2;
lin += k - 1;
}
}
WriteMatr(a, N, M);
}
CAPITOLUL 9. ONI 2014 9.1. HARTA 240
fin.close();
fout.close();
return 0;
}
N -= k;
}
M -= k;
}
struct cladire
{
short x1, y1, x2, y2;
int l, L;
} C[1002];
short n, m, k;
bool A[1502][1502];
short l[1502], c[1502];
int main()
{
int i, j, p, Max, a, nr;
freopen("harta.in", "r", stdin);
freopen("harta.out","w", stdout);
scanf("%d", &p);
scanf("%hd %hd %hd", &n, &m, &k);
for (i=1; i<=k; ++i)
scanf("%hd %hd %hd %hd", &C[i].x1, &C[i].y1, &C[i].x2, &C[i].y2);
if (p == 1)
{
Max = 0;
for (i=1; i<=k; ++i)
{
C[i].l = C[i].x2 - C[i].x1 + 1;
C[i].L = C[i].y2 - C[i].y1 + 1;
if (C[i].l == C[i].L)
if (C[i].l > Max)
Max = C[i].l;
}
CAPITOLUL 9. ONI 2014 9.1. HARTA 241
return 0;
}
ifstream fin("harta.in");
ofstream fout("harta.out");
int p,n,m,k;
int lung,lat,lx;
int patrate[52],lmax;
char a[1502][1502];
int i1,j1,i2,j2,i;
int l1,c1;
int S,C;
CAPITOLUL 9. ONI 2014 9.1. HARTA 242
int main()
{
fin>>p>>n>>m>>k;
if(p==1)
{
for(i=1;i<=k;i++)
{
fin>>i1>>j1>>i2>>j2;
lat=i2-i1+1;
lung=j2-j1+1;
lx=lat;
if(lung>lx)lx=lung;
patrate[lx]++;
if(lat==lung)
if(lat>lmax)
lmax=lat;
}
S=lmax*lmax;
C=0;
for(i=1;i<=lmax-2;i++)
C=C+patrate[i];
fout<<S<<" "<<C;
}
else
{
for(i=1;i<=k;i++)
{
fin>>i1>>j1>>i2>>j2;
for(l1=i1;l1<=i2;l1++)
{
a[l1][j1]=1;
a[l1][j2]=1;
a[l1][0]=1;//marcat linie care ramane
}
for(c1=j1;c1<=j2;c1++)
{
a[i1][c1]=1;
a[i2][c1]=1;
a[0][c1]=1;//marcat coloana care ramane
}
}
for(l1=1;l1<=n-1;l1++)
if(a[l1][0]==0 && a[l1+1][0]==0)
a[l1][0]=2;//linia l1 se va sterge
for(c1=1;c1<=m-1;c1++)
if(a[0][c1]==0 && a[0][c1+1]==0)
a[0][c1]=2;//coloana c1 se va sterge
fout<<"\n";
}
}
}
fout.close();
fin.close();
return 0;
}
/* 100 puncte
Constantin Galatan
*/
#include <fstream>
#include <vector>
#include <algorithm>
#define pb push_back
#define DIM 1501
ifstream fin("harta.in");
ofstream fout("harta.out");
struct Dr
{
int i1, j1, i2, j2;
Dr(int _i1, int _j1, int _i2, int _j2)
: i1(_i1), j1(_j1), i2(_i2), j2(_j2) {
}
};
VI xa, ya, x, y;
vector<Dr> d;
int imax, jmax;
int T, N, M, n, m, k, L, H, Lmax;
bool A[DIM][DIM], s1[DIM], s2[DIM];
int main()
{
fin >> T >> N >> M >> k;
int i1, j1, i2, j2;
x.pb(0), y.pb(0);
s1[0] = true, s2[0] = true;
for ( int i = 0; i < k; ++i )
{
fin >> i1 >> j1 >> i2 >> j2;
d.pb(Dr(i1, j1, i2, j2));
L = i2 - i1 + 1, H = j2 - j1 + 1;
if ( L == H && L > Lmax )
Lmax = L;
if ( !s1[i1 - 1] )
x.pb(i1 - 1), s1[i1 - 1] = true;
if ( !s2[j1 - 1] )
y.pb(j1 - 1), s2[j1 - 1] = true;
imax = max(imax, i2); jmax = max(jmax, j2);
}
if ( T == 1 )
One();
else
Two();
fin.close();
fout.close();
return 0;
}
CAPITOLUL 9. ONI 2014 9.2. QVECT 244
void One()
{
int nr_dr(0);
for ( int i = 0; i < k; ++i )
{
L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
if ( L < Lmax - 1 && H < Lmax - 1)
nr_dr++;
}
fout << Lmax * Lmax << ’ ’ << nr_dr << ’\n’;
}
void Two()
{
sort(x.begin(), x.end());
sort(y.begin(), y.end());
int i, j;
for (size_t k = 0; k < xa.size(); ++k )
{
i = lower_bound(x.begin(), x.end(), xa[k]) - x.begin();
j = lower_bound(y.begin(), y.end(), ya[k]) - y.begin();
n = max(n, i), m = max(m, j);
A[i][j] = 1;
}
if ( n < M ) n++;
if ( m < M ) m++;
WriteMatr(A, n, m);
}
9.2 qvect
Problema 2 - qvect 100 de puncte
Se consideră N vectori cu elemente ı̂ntregi, numerotaţi de la 1 la N , sortaţi crescător, fiecare
vector având un număr precizat de elemente.
Cerinţe
Date de intrare
CAPITOLUL 9. ONI 2014 9.2. QVECT 245
Fişierul de intrare qvect.in conţine pe prima linie două numerele naturale N Q, separate
printr-un spaţiu, ce reprezintă numărul de vectori, respectiv numărul de ı̂ntrebări.
Pe fiecare dintre următoarele N linii se găseşte descrierea unui vector sub forma: k a1 a2 ... ak ,
unde k reprezintă numărul de elemente, iar a1 , ..., ak reprezintă elementele vectorului, separate
prin câte un spaţiu.
Pe fiecare dintre următoarele Q linii se găseşte descrierea unei ı̂ntrebări sub forma unui triplet
de numere naturale: t i j, separate prin câte un spaţiu, unde t reprezintă tipul ı̂ntrebării şi poate
lua numai valorile 1 sau 2, iar i şi j au semnificaţia precizată ı̂n cerinţă.
Date de ieşire
Fişierul de ieşire qvect.out va conţine Q numere ı̂ntregi, câte unul pe linie, reprezentând ı̂n
ordine, răspunsurile la cele Q ı̂ntrebări.
Restricţii şi precizări
a 1 & N, i, j & 100
a 1 & Q & 1 000
a 1 & t & 2
a 1 & k & 5 000
a -1 000 000 000 & a1 , a2 , ...ak & 1 000 000 000
a Prin valoarea aflată pe poziţia mediană a unui vector a cu k elemente se ı̂nţelege valoarea
elementului situat pe poziţia [k/2], adică partea ı̂ntreagă a lui k / 2.
a 15% dintre teste vor conţine numai ı̂ntrebări de tipul 1
a 15% dintre teste vor conţine numai ı̂ntrebări de tipul 2
Exemple:
qvect.in qvect.out
Explicaţii
33 13 Prima ı̂ntrebare este de tipul 2. Vectorul nou obţinut
7 1 4 5 8 11 18 19 3 prin interclasarea vectorilor numerotaţi cu 2 şi cu 3 este
6 2 4 5 10 21 29 10 următorul: 2, 4, 5, 10, 13, 14, 15, 15, 21, 29 şi conţine
4 13 14 15 15 6+4=10 elemente, valoarea elementului median este 13.
223 A doua ı̂ntrebare este de tipul 1. Diferenţa minimă se
123 obţine pentru perechea (10,13), unde valoarea 10 aparţine
213 vectorului numerotat cu 2, iar valoarea 13 aparţine vectoru-
lui numerotat cu 3.
A treia ı̂ntrebare este de tipul 2. Poziţia mediană ı̂n vec-
torul nou obţinut prin interclasare este (7+6+4)/2 = 8, deci
valoarea ce se găseşte pe poziţia mediană este 10.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 8 MB din care pentru stivă 2 MB
Dimensiune maximă a sursei: 15 KB
ifstream f("qvect.in");
ofstream g("qvect.out");
int a[102][5002];
int z[500002];
return z[nr/2];
}
int main()
{
int n, q, op, i, j, x, y;
f >> n >> q;
for (i=1; i<=n; ++i)
{
f >> x;
a[i][0] = x;
for (j=1; j<=x; ++j)
f>> a[i][j];
}
while ( q-- )
{
f >> op >> x >> y;
switch (op)
{
case 1: {
g << q1(x, y) << "\n";
break;
};
case 2: {
CAPITOLUL 9. ONI 2014 9.2. QVECT 247
ifstream f("qvect.in");
ofstream g("qvect.out");
int a[101][5001];
int nr[101];
if (z == 0) return 0;
if (z < Min) Min = z;
return Min;
}
if (nr >= p)
{
if (ok)
{
rez = sol;
if (nr == p) return sol;
}
d = val - 1;
}
else
s = val + 1;
CAPITOLUL 9. ONI 2014 9.2. QVECT 248
return rez;
}
int main()
{
int n, q, op, i, j, x, y, m;
f >> n >> q;
for (i=1; i<=n; ++i)
{
f >> x;
a[i][0] = x;
nr[i] = nr[i-1] + x;
for (j=1; j<=x; ++j)
f>> a[i][j];
}
while ( q-- )
{
f >> op >> x >> y;
switch (op)
{
case 1:
{
g << q1(x, y) << "\n";
break;
};
case 2:
{
m = (nr[y] - nr[x-1]) / 2;
g << q2(x, y, m) << "\n";
break;
};
}
}
return 0;
}
int a[102][5002];
int nr[102];
if (z == 0) return 0;
if (z < Min) Min = z;
return Min;
}
CAPITOLUL 9. ONI 2014 9.2. QVECT 249
nr += nr0;
}
if (nr >= p)
{
if (ok)
{
rez = sol;
if (nr == p) return sol;
}
d = val - 1;
}
else
s = val + 1;
}
return rez;
}
int main()
{
int n, q, op, i, j, x, y, m;
while ( q-- )
{
scanf("%d%d%d", &op, &x, &y);
switch (op)
{
case 1: {
printf("%d\n", q1(x, y));
break;
};
case 2: {
m = (nr[y] - nr[x-1]) >> 1;
printf("%d\n", q2(x, y, m));
break;
};
}
}
return 0;
}
#include <algorithm>
#include <fstream>
ifstream f("qvect.in");
ofstream g("qvect.out");
void citeste()
{
f>>n>>q;
for(int i=1;i<=n;i++)
{
f>>a[i][0];
for(int j=1;j<=a[i][0];j++)
f>>a[i][j]; poz[i]=1;
}
}
int abs(int x)
{
return max(x,-x);
}
if(i<=nr1)
{
if(m==2)
dif=min(dif, abs(a[n1][i]-y));
}
else
{
if(m==1)
dif=min(dif, abs(x-a[n2][j]));
}
return dif;
}
for(i=1;i<=n;i++)
b[i]=c[i];
return n;
}
for(i=n1+1;i<=n2;i++)
k=interc(k,i,nr);
return b[nr];
}
int main()
{
citeste();
int k,t,i,j;
for(k=1;k<=q;k++)
{
f>>t>>i>>j;
if(t==1)
g<<cerinta1(i,j)<<endl;
else
g<<cerinta2(i,j)<<endl;
}
return 0;
}
ifstream f("qvect.in");
ofstream g("qvect.out");
void citeste()
{
f>>n>>q;
for(int i=1;i<=n;i++)
{
f>>a[i][0];
for(int j=1;j<=a[i][0];j++)
f>>a[i][j];
poz[i]=1;
}
}
int abs(int x)
{
return max(x,-x);
}
nr2=a[n2][0];
while(i<=nr1 && j<=nr2)
{
if(a[n1][i]<a[n2][j])
{ x=a[n1][i];i++;
if(m==2)
dif=min(dif, abs(x-y));
m=1;
}
else
if(a[n1][i]>a[n2][j])
{
y=a[n2][j];j++;
if(m==1)
dif=min(dif, abs(x-y));
m=2;
}
else
if(a[n1][i]==a[n2][j])
return 0;
}
if(i<=nr1)
{
if(m==2)
dif=min(dif, abs(a[n1][i]-y));
}
else
{
if(m==1)
dif=min(dif, abs(x-a[n2][j]));
}
return dif;
}
for(i=n1;i<=n2;i++)
{
j=poz[i];
if(j<=a[i][0] && a[i][j]<mi)
{
mi=a[i][j];
p=i;
}
}
poz[p]++;
return mi;
}
for(i=n1;i<=n2;i++)
nr+=a[i][0];
nr/=2;
for(i=1;i<=nr;i++)
med=minim(n1,n2);
return med;
}
int main()
{
citeste();
int k,t,i,j;
CAPITOLUL 9. ONI 2014 9.2. QVECT 253
for(k=1;k<=q;k++)
{
f>>t>>i>>j;
if(t==1)
g<<cerinta1(i,j)<<endl;
else
g<<cerinta2(i,j)<<endl;
}
return 0;
}
ifstream f("qvect.in");
ofstream g("qvect.out");
void citeste()
{
f>>n>>q;
for(int i=1;i<=n;i++)
{
f>>a[i][0];
for(int j=1;j<=a[i][0];j++)
f>>a[i][j];
poz[i]=1;
}
}
int abs(int x)
{
return max(x,-x);
}
if(i<=nr1)
{
if(m==2)
CAPITOLUL 9. ONI 2014 9.2. QVECT 254
dif=min(dif, abs(a[n1][i]-y));
}
else
{
if(m==1)
dif=min(dif, abs(x-a[n2][j]));
}
return dif;
}
nr/=2;
k=min(nr,a[n1][0]);
for(i=1;i<=k;i++)
b[i]=a[n1][i];
for(i=n1+1;i<=n2;i++)
{
for(j=1;j<=a[i][0] && j<=nr;j++)
b[++k]=a[i][j];
sort(b+1,b+k+1);
k=min(k,nr);
}
return b[k];
}
int main()
{
citeste();
int k,t,i,j;
for(k=1;k<=q;k++)
{
f>>t>>i>>j;
if(t==1)
g<<cerinta1(i,j)<<endl;
else
g<<cerinta2(i,j)<<endl;
}
return 0;
}
void citeste()
{
freopen("qvect.in", "r", stdin);
poz[i]=1;
}
}
CAPITOLUL 9. ONI 2014 9.2. QVECT 255
int abs(int x)
{
return max(x,-x);
}
if(i<=nr1)
{
if(m==2)
dif=min(dif, abs(a[n1][i]-y));
}
else
{
if(m==1)
dif=min(dif, abs(x-a[n2][j]));
}
return dif;
}
nr/=2;
k=min(nr,a[n1][0]);
for(i=1;i<=k;i++)
b[i]=a[n1][i];
for(i=n1+1;i<=n2;i++)
{
for(j=1;j<=a[i][0] && j<=nr;j++)
b[++k]=a[i][j];
sort(b+1,b+k+1);
k=min(k,nr);
}
return b[k];
}
int main()
{
citeste();
freopen("qvect.out","w", stdout);
CAPITOLUL 9. ONI 2014 9.2. QVECT 256
int k,t,i,j;
for(k=1;k<=q;k++)
{
scanf("%d%d%d", &t, &i, &j);
if(t==1)
printf("%d\n",cerinta1(i,j));
else
printf("%d\n",cerinta2(i,j));
}
return 0;
}
ifstream fin("qvect.in");
ofstream fout("qvect.out");
if(d<0)d=-d;
if(d<dmin)dmin=d;
if(dmin==0)return 0;
if(a[i1][i]<a[j1][j])
i++;
else
j++;
}
return dmin;
}
return q;
}
q=b[i];
r=-1;
while(p<=q && r==-1)
{
m=p+(q-p)/2;
if(v==a[i][m])
return m;
if(v<a[i][m])
q=m-1;
else
p=m+1;
}
return -1;
}
void sortare()
{
int i,j,k,p,q,r,s,ok;
n=k;
ok=0;
while(ok==0)
{
r=0;
s=0;
do
{
p=r+1;
q=p;
while(q+1<=n && v[q]<=v[q+1])
q++;
s++;
r=q+1;
while(r+1<=n && v[r]<=v[r+1])
r++;
while(i<=q)
w[++k]=v[i++];
while(j<=r)
w[++k]=v[j++];
i=p;
for(j=1;j<=k;j++)
v[i++]=w[j];
}
} while(r<n);
if(s==1) ok=1;
}
CAPITOLUL 9. ONI 2014 9.2. QVECT 258
return s;
}
return -1;
}
p=1;
q=n;
while(p<=q)
{
m=p+(q-p)/2;
s=mmse(i1,j1,v[m]);
if(s<nr/2)
p=m+1;
else
q=m-1;
}
while(exista(i1,j1,v[p])==0)
p++;
return v[p];
}
int main()
{
int i,j,t,i1,j1;
fin>>N>>Q;
n=0;
for (i=1;i<=N;i++)
{
fin>>b[i];
for (j=1;j<=b[i];j++)
CAPITOLUL 9. ONI 2014 9.3. TG 259
{
fin>>a[i][j];
n++;
v[n]=a[i][j];
}
}
sortare();
for (i=1;i<=Q;i++)
{
fin>>t>>i1>>j1;
if(t==1)
fout<<dif_min(i1,j1);
else
fout<<mediana(i1,j1);
fout<<"\n";
}
fout.close();
fin.close();
return 0;
}
9.3 tg
Problema 3 - tg 100 de puncte
Fie un număr natural N . Spunem că a, b, c este un triplet geometric
Ó
limitat de N , dacă a, b
şi c sunt trei numere naturale astfel ı̂ncât 1 & a $ b $ c & N şi b a c.
Cerinţe
Date de intrare
Date de ieşire
Exemple:
Complexitate O N N
Pentru fiecare a din mulţimea r1, 2, ..., N 2x şi pentru fiecare c din mulţimea a 2, a 3, ..., N
se verifică dacă a c este pătrat perfect şi ı̂n caz afirmativ se calculează b sqrt a c şi se
contorizează rezultatul.
Complexitate O N sqrt N
Pentru fiecare a din mulţimea 1, 2, ..., N 2 se observă că dacă ar exista tripletul a, b, c atunci
ar trebui să avem c b b©a, deci b b ar trebui să fie multiplu al lui a şi ı̂n acelaşi timp ar trebui
să fie pătrat perfect, iar b % a.
Ne-ar ajuta astfel să ştim care este cel mai mic pătrat perfect multiplu al lui a. Fie a1 acest
număr. Am avea a1 a x, unde x este cel mai mic posibil astfel incat a1 să fie pătrat perfect.
e e e f f f
Dacă a p11 p22 ... pkk , atunci x p11 p22 ... pkk unde fi ei mod 2 , adică x este
produsul factorilor primi care apar la puteri impare ı̂n descompunerea lui a.
Se mai observă apoi că orice alt multiplu al lui a care este si pătrat perfect va fi de forma
a2 a x k , unde k ' 1. Pentru k 1 se obtine a1 . Deoarece trebuie să avem b Õb % a a,
2
atunci patratele perfecte care ne interesează pentru obţinerea lui c se obţin pentru k % xa . Astfel
Ó Õ
a x k si c b b©a x k . Deoarece c & N vom avea k & N
2
putem determina b x
.
ÕCu alte cuvinte
Õ pentru fiecare
Õ a din mulţimea r 1, 2, ..., N 2x vom parcurge k din mulţimea
a a a
r x 1 , x 2 , ..., x x şi astfel vom obţine toate tripletele geometrice căutate, care sunt
Ó 2
de forma ( a, b a x k, c x k ).
Pentru determinarea lui x putem folosi algoritmul de descompunere ı̂n factori primi O sqrt a.
Complexitate O N
Ideea de rezolvare este asemănatoare cu cea anterioară. Se incearcă diminuarea efortului de
calculare la fiecare pas a lui x prin construirea vectorului xi = cel mai mic numar natural care
ı̂nmulţit cu i produce un pătrat perfect, adică vom avea i xi cel mai mic patrat perfect multiplu
al lui i.
Se procedează asemănător cu algoritmul ”Ciurul lui Eratostene”. Se iniţializează xi cu 0 şi
se parcurge ı̂n ordinea 1, 2, 3, ..., N . Daca avem xi 0 atunci vom marca xi j j i pentru
toti 1 & i j j & N .
ifstream fin("tg.in");
ofstream fout("tg.out");
int N;
bool ok[4000010];
long long sol;
int main()
{
fin >> N;
for (int i = 1; i <= N; ++i)
if (!ok[i])
for (int j = 1; i * j * j <= N; ++j)
{
sol += j - 1;
ok[i * j * j] = true;
}
fin.close();
fout.close();
}
ifstream fin("tg.in");
ofstream fout("tg.out");
int v[1000010];
int main()
{
long long n,a,i,k,s,p,n4,n4a;
fin>>n;
n4=n/4;
s=0;
for(a=1;a<=n4;a++)
{
if (v[a]==0)
{
n4a=n4/a;
for(i=1;i*i<=n4a;i++)
{
v[a*i*i]=a;
}
k=(int)sqrt(n/a);
if(k%2==0)
p=k/2*(k-1);
else
p=(k-1)/2*k;
s=s+p;
}
}
fout<<s<<endl;
fout.close();
fin.close();
return 0;
}
ifstream fin("tg.in");
ofstream fout("tg.out");
int v[4000010];
int main()
{
long long n,s,a,x,k1,k2,t,d,c,b,i,k,s1,p,r;
fin>>n;
s=0;
t=0;
for (a=1;a<=n-2;a++)
{
if(v[a]==0)//i are toti factorii primi cu exponenti impari
{
b=(n-2)/a;
for(i=1;i*i<=b;i++)
v[a*i*i]=a;
}
CAPITOLUL 9. ONI 2014 9.3. TG 262
x=v[a];
k1=sqrt(a/x);
k2=sqrt(n/x);
s=s+k2-k1;
}
fout<<s<<endl;
fout.close();
fin.close();
return 0;
}
ifstream fin("tg.in");
ofstream fout("tg.out");
int main()
{
int a,c,s,n,ua,uc,uac;
long long bb;
double r;
fin>>n;
s=0;
for(a=1;a<=n-2;a++)
{
ua=a%10;
for (c=a+2;c<=n;c++)
{
uc=c%10;
uac=(ua*uc)%10;
if(uac==0 || uac==1 || uac==4 ||
uac==5 || uac==6 || uac==9)
{
bb=a;
bb=bb*c;
r=sqrt(bb);
if(r==(int)r)
s++;
}
}
}
fout<<s;
fout.close();
fin.close();
return 0;
}
ifstream fin("tg.in");
ofstream fout("tg.out");
int main()
{
long long n,s,a,x,k1,k2,t,d,c;
fin>>n;
s=0;
for (a=1;a<=n-2;a++)
{
CAPITOLUL 9. ONI 2014 9.4. PROGRESIE 263
x=1;
t=a;
d=2;
while(d*d<=t)
{
c=0;
while(t%d==0)
{
c++;
t=t/d;
}
if(c%2==1) x=x*d;
d++;
}
if(t>1) x=x*t;
k1=sqrt(a/x);
k2=sqrt(n/x);
s=s+k2-k1;
}
fout<<s<<endl;
fout.close();
fin.close();
return 0;
}
9.4 progresie
Problema 4 - progresie 100 de puncte
Cerinţe
Să se determine un şir strict Ócrescător, cu lungimea N , format din numere naturale nenule,
1 & a1 $ a2 $ a3 $ ... $ aN & 2N N , cu proprietatea că oricare trei termeni distincţi ai şirului nu
sunt ı̂n progresie aritmetică, adică pentru oricare numere naturale i, j şi k cu 1 & i $ j $ k & N ,
este ı̂ndeplinită condiţia: ai ak j 2 aj . Prin x s-a notat partea ı̂ntreagă a lui x.
De Óexemplu, pentru N 5, cel mai mare termen al şirului va trebui să fie mai mic sau egal cu
2 5 5% adică aN & 22, deci o soluţie este: 1, 2, 4, 5, 10.
Date de intrare
Fişierul de intrare progresie.in conţine pe primul rând numărul natural N cu semnificaţia de
mai sus.
Date de ieşire
În fişierul de ieşire progresie.out se vor scrie pe primul rând, despărţite prin câte un spaţiu,
cele N elemente ale şirului ai , 1 & i & N .
Restricţii şi precizări
a 3 & N & 20 000
a Dacă soluţia nu este unică, se va accepta orice soluţie corectă.
Exemple:
progresie.in progresie.out Explicaţii
5 1 2 4 5 10 N 5; aN & 22;
Un şir strict crescător format din 5 numere naturale
nenule cu proprietatea că oricare 3 termeni ai săi nu
sunt ı̂n progresie aritmetică este: 1, 2, 4, 5, 10
7 3 5 6 11 12 14 15 N 7; aN & 37;
Un şir strict crescător format din 7 numere naturale
nenule cu proprietatea că oricare 3 termeni ai săi nu
sunt ı̂n progresie aritmetică este: 3, 5, 6, 11, 12, 14, 15
CAPITOLUL 9. ONI 2014 9.4. PROGRESIE 264
Varianta 1
Această soluţie utilizează un algoritm asemănător cu algoritmul lui Eratostene de determinare
a numerelor prime. Se utilizează un vector care iniţial pe toate poziţiile este iniţializat cu 1 şi apoi
ı̂ncepând cu a doua poziţie se ”elimină” din acest şir toate poziţiile obţinute cu formula 2*poziţia
curentă - i unde i ia toate valorile anterioare poziţiei curente. Se avansează apoi ı̂n şir până la
prima valoare nenulă. Soluţia are un ordin de complexitate O n log n şi obţine aproximativ
30% din punctaj.
Varianta 2
Fie Tn mulţimea tuturor ı̂ntregilor nenegativi a căror scriere ı̂n baza 3 conţine cel mult n cifre,
toate fiind diferite de cifra 2.
Numărul de elemente din Tn este 2n deoarece elementele lui Tn sunt n-uple formate din 0 şi 1,
iar cel mai mare ı̂ntreg din Tn este 111....11 = (3n - 1)/2. Tn nu va conţine trei numere distincte
ı̂n progresie aritmetică deoarece dacă x, y, z " Tn 2y x z, numărul 2y conţine numai cifrele 0
şi 2 ı̂n reprezentarea sa ı̂n baza trei. Rezultă că x y z, ţinând seama de algoritmul de adunare
ı̂n baza trei, ceea ce contrazice ipoteza. Soluţia obţine aproximativ 50% din punctaj.
Varianta 3
Soluţia are la bază un mecanism constructiv, observând că din prima stare notată S1 r1, 2x
se poate trece ı̂n starea următoare reunind elementele lui S1 cu elementele lui S1 adunate cu prima
putere a lui 3. Se obţine astfel S2 r1, 2, 4, 5x. Apoi se obţine S3 r1, 2, 4, 5, 10, 11, 13, 14x. Se
continuă mecanismul atât timp cât valorile din starea curentă sunt mai mici sau egale cu N . Se
poate demonstra prin inducţie matematică că acest algoritm conduce la mulţimi cu proprietatea
cerută de problemă. Ordinul de complexitate al acestei soluţii este O n şi obţine punctaj maxim.
Varianta 4 prof. Eugen Nodea - Colegiul Naţional ”Tudor Vladimirescu” Tg. Jiu
Se poate construi un şir care respectă cerinţele folosind relaţia de recurenţă:
a[2*i] = 3 * a[i] - 2
a[2*i+1] = 3 * a[i] - 1, i=0, ..., N, cu a[0] = 1
Complexitate : O N
Varianta 5 prof. Piţ-Rada Ionel-Vasile, Colegiu Naţional ”Traian” Drobeta Turnu - Severin
Se construieşte şirul v[1], v[2], ..., v[N-1], format din numere naturale nenule cu proprietatea
că
(*) oricare două secvenţe adiacente din şirul v[] au sume diferite, astfel:
- primii trei termeni sunt 1, 2, 1
- al patrulea termen nu poate fi 1, 2, 3, 4 deci va fi aleasă valoarea 5, apoi pentru termenii
cinci, şase şi şapte se pot alege valorile 1, 2, 1 şi se obţine şirul cu şapte termeni 1, 2, 1, 5, 1, 2, 1
- al optulea termen nu poate fi dintre 1, 2, 3, ...,13 şi se va alege valoarea 14, apoi iaraşi se pot
completa următorii şapte termeni egali cu primii şapte termeni şi se obtine un şir cu 15 termeni
1, 2, 1, 5, 1, 2, 1, 14, 1, 2, 1, 5, 1, 2, 1
p
- apare astfel ideea de a construi un şir care pe poziţiile i egale cu puteri ale lui doi, i 2 , să
aibă valoarea egală cu suma termenilor anteriori plus 1,
v[i] = 1 +v[1] + v[2] + ... + v[i-1],
apoi următorii i 1 termeni vor fi aleşi identici cu primii obţinându-se un şir cu 2 i 1 termeni
şi procesul va fi repetat.
Şirul cerut de problemă se va obţine prin a[1]=1 şi a[k+1]=a[k]+v[k], pentru 1 & k $ N .
Din şirul 1, 2, 1, 5, 1, 2, 1, 14, 1, 2, 1, 5, 1, 2, 1 se va obţine şirul 1, 2, 4, 5, 10, 11, 13, 14, 28,
29, 31, 32, 37, 38, 40, 41, ...
Algoritmul permite implementare cu complexitatea O N .
CAPITOLUL 9. ONI 2014 9.4. PROGRESIE 265
#include <fstream>
#include <math.h>
int main()
{
ifstream f("progresie.in");
ofstream g("progresie.out");
int n,i,s,x,ok,c;
f>>n;
s=0;
for(i=1;i<=2*n*sqrt(n);i++)
{
x=i;
ok=1;
do
{
c=x%3;
if (c==2) ok=0;
x=x/3;
} while (x>0&&ok);
if (ok)
if (s<n)
{
s++;
g<<i<<" ";
}
else
break;
g<<"\n";
f.close();
g.close();
return 0;
}
#include <fstream>
#include <math.h>
#include <iostream>
int a[nmax];
int main()
{
ifstream f("progresie.in");
ofstream g("progresie.out");
f>>n;
a[1]=1;a[2]=2;k=2;
i=1;
un=0;
p=3;
while (!un)
{
t=k;
for(j=1;j<=k;j++)
{
x=a[j]+p;
if (x<=3*n*sqrt(n)) a[++t]=x;
else un=1;
if (!un)
k=2*k;
else
k=t;
i++;
p=p*3;
}
// cout << t ;
for(i=1;i<=n;i++)
g<<a[i]<<" ";
g<<"\n";
f.close();
g.close();
return 0;
}
int v[2200002];
int main()
{
int n,j,u,i,x;
ifstream f("progresie.in") ;
ofstream g("progresie.out");
f>>n;
g<<1<<" "<<2;
v[1]=v[2]=1;
u=2;
for(i=3;i<=n;i++)
{
x=2*u;
for(j=u-1;j>=1;j--)
if(v[j]==1 && v[x-j]!=1)
v[x-j]=2;
u++;
while(v[u]) u++;
v[u]=1;
g<<" "<<u;
}
g<<endl;
return 0;
}
# include <cstdio>
# include <cmath>
using namespace std;
int N, K, i;
int a[100003];
int main()
{
freopen("progresie.in", "r", stdin);
freopen("progresie.out","w",stdout);
scanf("%d", &N);
a[0] = 1;
for (i=0; i<=N; ++i)
{
a[2*i] = 3 * a[i] - 2;
a[2*i+1] = 3 * a[i] - 1;
}
return 0;
}
#include<fstream>
int N,K,s,a,i,v[220009],j;
long long p;
int main()
{
fin>>N;
s=0;
a=1;
fout<<a+s<<" ";
p=1;
for (i=1;i<N;i++)
{
if(i==p)
{
p=p*2;
v[i]=s+1;
s=s+v[i];
j=1;
}
else
{
v[i]=v[j];
s=s+v[i];
j++;
}
fout<<a+s<<" ";
}
fout.close();
fin.close();
return 0;
}
#include <stdio.h>
#include <math.h>
int N, Limit;
bool b[2 * NMax * NMax];
int sol[NMax];
sol[x] = v;
if ( bkt( x + 1, v + 1) )
return true;
return false;
}
int main()
{
freopen("progresie.in", "r", stdin);
freopen("progresie.out", "w", stdout);
scanf("%d", &N);
Limit = 2 * N * sqrt(N);
if ( bkt( 1, 1 ) )
{
for ( int i = 1; i <= N; ++ i)
printf("%d ", sol[i]);
printf("\n");
}
else
return 0;
}
9.5 reflex
Problema 5 - reflex 100 de puncte
La un concurs de robotică, ı̂n timpul prezentării, un roboţel cu corp cilindric cu diametrul de
o unitate scapă de sub control şi se deplasează ı̂ntr-un ring de formă dreptunghiulară. Ringul este
ı̂mpărţit ı̂n N M pătrate identice, cu latura de o unitate, aşezate pe N linii şi M coloane.
Robotul poate părăsi ringul numai pe la colţuri, acestea
fiind numerotate de la 1 la 4, colţul cu numărul 1 fiind cel din
stânga jos apoi restul fiind numerotate ı̂n sens trigonometric.
Suprafaţa ringului este delimitată de exterior prin intermediul
a patru pereţi despărţitori: doi pereţi ”verticali” (aşezaţi de
la colţul 1 la colţul 4, respectiv de la colţul 2 la colţul 3) şi doi
pereţi ”orizontali” (aşezaţi de la colţul 1 la colţul 2, respectiv
de la colţul 3 la colţul 4), fără a bloca ieşirile, ca ı̂n desenul alăturat.
CAPITOLUL 9. ONI 2014 9.5. REFLEX 269
Robotul pătrunde ı̂n ring prin colţul cu numărul 1 sub un unghi de 45 grade şi cu o viteză de
un pătrat/s.
Ciocnirile cu pereţii sunt considerate perfect elastice (robotul nu-şi pierde din viteză) iar
unghiul de incidenţă este egal cu cel de reflexie.
Cerinţe
Date de intrare
Fişierul de intrare reflex.in conţine pe prima linie două numere naturale N şi M , separate
printr-un singur spaţiu.
Date de ieşire
Fişierul de ieşire reflex.out va conţine pe prima linie două numere naturale S şi C, separate
printr-un singur spaţiu, S reprezentând numărul de secunde după care robotul va ieşi din ring, iar
C reprezintă numărul colţului prin care acesta va ieşi. Pe a doua linie, fişierul de ieşire va conţine
două numere naturale H şi V , separate printr-un spaţiu, H reprezentând numărul de ciocniri cu
pereţii orizontali ai ringului, iar V numărul de ciocniri cu pereţii verticali.
Exemple:
57 13 4 2 1
Se parcurg 13 pătrate, ieşirea se face
la colţul 4 şi de produc 2 ciocniri cu
pereţii orizontali (ı̂n punctele a şi c
respectiv o ciocnire cu pereţii verti-
cali ı̂n punctul b).
Soluţia 1 - 40 de puncte
Se simulează deplasarea robutului din pătrat ı̂n pătrat pe diagonală (x x 1 şi y z 1),
se verifică ciocnirea cu un petete orizontal (y 1 sau y N ) sau cu un perete vertical (x 1 sau
x M ). Se verifică dacă s-a ajuns ı̂ntr-un colţ astfel:
- Colţul 1 (stânga jos) cu x 1 şi y 1;
- Colţul 2 (dreapta jos) cu x M şi y 1;
CAPITOLUL 9. ONI 2014 9.5. REFLEX 270
În urma fiecărei ciocniri se schimba direcţia de deplasare pe axa x sau y, ı̂n funcţie de peretele
ciocnit.
Pentru rezolvarea cerinţei 1) se numără pătraţelele parcurse păna se ajunge ı̂ntr-un colţ, viteza
fiind de un pătrat/s, timpul ı̂n secunde coincide cu numărul de pătrate parcurse.
În cazul unei ciocniri cu un perete se numără această ciocnire ı̂n funtie de perete (orizontal
sau vertical).
Soluţia 2 - 68 de puncte
Se simulează deplasarea robotului din perete ı̂n perete şi se ı̂nâlnesc două situaţii:
- se poate face un salt pe x şi y cu min N, M (N -lungimea şi M -lăţimea ringului);
- se produce o ciocnire ı̂nainte de a parcurge o distanţă egală cu min N, M , situaţie ı̂n care
incrementarea se face fără depăşirea lui N şi M sau 1.
ı̂n acest caz timpul (nr. de pătrate parcurse) se poate calcula mai simplu după relaţia:
S V M 1, ı̂n care S - nr. de secunde, V - nr. de ciocniri cu pereţii verticali, M - lungimea
ringului.
Solutia 3 - 100 de puncte
Dacă se analizează parcursul robotului desfăşurat (vezi imaginea de mai jos) şi se notează
n N 1 şi m M 1 se poate observa că numărul de ciocniri cu pereţii orizontali H, respectiv
numărul de ciocniri cu pereţii verticali V sunt daţi de relaţiile:
H N ©cmmdc N, M şi V M ©cmmdc N, M ı̂n care cu cmmdc N, M s-a notat cel mai
mare divizor comun al lui N şi M .
Nr. de pătrate parcurse şi implicit timpul ı̂n secunde se poate calcual ca S cmmmc N, M 1.
Colţul ı̂n care se produce ieşirea se poate determina din nr. de ciocniri cu pereţii orizontali şi
verticali astfel:
- pt. număr impar de ciocniri cu pereţii orizontali se poate ieşi pe latura de sus adică colţurile
3 sau 4.
- pt. V par singura posibilitate de ieşire este colţul 2 (pe latura de jos);
- pt. H impar ieşirea se va face pe latura din dreapta, colţurile 2 sau 3;
- pt. H par ieşirea se face pe latura sin stânga singura posibilitate fiind colţul 4 .
Colţul exact se determină prin combinarea posibilitătilor rezultate ı̂n funtie de paritatea lui H
şi V .
ifstream f("reflex.in");
ofstream g("reflex.out");
int main()
CAPITOLUL 9. ONI 2014 9.5. REFLEX 271
{
f >> n >> m;
--n, --m;
//cmmdc
a = n;
b = m;
while(b)
{
r = a % b;
a = b; b = r;
}
//cmmmc
cmmmc = n * m / a;
v = n / a;
h = m / a;
c = 1;
if (v % 2 == 0)
c = 4;
else
if (h % 2 == 0)
c = 2;
else
c = 3;
struct punct
{
long x,y;
};
int main()
{
long long n,m,x=1,N, S, E, W;
int dx,dy,colt=0;
punct c1,c2,c3,c4,b;
ifstream inFile;
ofstream outFile;
N=0;
S=0;
E=0;
W=0;
inFile >> n >> m;
c1.x=1; c1.y=1;
c2.x=m; c2.y=1;
c3.x=m; c3.y=n;
c4.x=1; c4.y=n;
b=c1;
dx=1;
dy=1;
do
{
x++;
CAPITOLUL 9. ONI 2014 9.5. REFLEX 272
b.x+=dx;
b.y+=dy;
//cout << "B("<<b.y<<"."<<b.x<<")\n ";
if (b.x == m) {dx= -1; E++;} //lat E
if (b.y == n) {dy= -1; N++;} //lat N
if (b.x == 1) {dx = 1; W++;} //lat W
if (b.y == 1) {dy = 1; S++;} //lat S
if ((b.x==c1.x) && (b.y==c1.y)) {colt=1; S--; W--;}
if ((b.x==c2.x) && (b.y==c2.y)) {colt=2; S--; E--;}
if ((b.x==c3.x) && (b.y==c3.y)) {colt=3; N--; E--;}
if ((b.x==c4.x) && (b.y==c4.y)) {colt=4; N--; W--;}
} while (!colt);
inFile.close();
outFile.close();
return 0;
}
int main()
{
long n,m,N,M,CMMDC,nl,nh;
long long s;
int colt=0;
ifstream inFile;
ofstream outFile;
//CMMMC = N * M / CMMDC;
//nl=CMMMC/M;
//nh=CMMMC/N;
nl=N/CMMDC;
nh=M/CMMDC;
if (nl%2==0)
colt=4;
else
if (nh%2==0)
colt=2;
else
colt=3;
s = nl;
s = s * M + 1;
CAPITOLUL 9. ONI 2014 9.5. REFLEX 273
inFile.close();
outFile.close();
return 0;
}
struct punct
{
long long x,y;
};
int main()
{
long long n,m,N, S, E, W;
long long s=1;
int dx,dy,colt=0;
punct c1,c2,c3,c4,b;
ifstream inFile;
ofstream outFile;
c1.x=1; c1.y=1;
c2.x=m; c2.y=1;
c3.x=m; c3.y=n;
c4.x=1; c4.y=n;
b=c1;
if (n>m)
dx=m-1;
else
dx=n-1;
dy=dx;
do
{
//cout <<"dx="<<dx<< " dy="<<dy<<"\n";
if ( (b.x+dx <= m) && (b.y+dy <= n) && (b.x+dx >=1) && (b.y+dy>=1) )
{
b.x+=dx;
b.y+=dy;
}
else
{
if (b.x+dx > m)
{
if (dy > 0)
b.y+=m-b.x;
else
b.y-=m-b.x;
b.x=m;
}
else
if (b.y+dy > n)
{
if (dx > 0)
CAPITOLUL 9. ONI 2014 9.5. REFLEX 274
b.x+=n-b.y;
else
b.x-=n-b.y;
b.y=n;
}
else
if (b.x+dx < 1)
{
if (dy > 0)
b.y+=b.x-1;
else
b.y-=b.x-1;
b.x=1;
}
else
if (b.y+dy < 1)
{
if (dx > 0)
b.x+=b.y-1;
else
b.x-=b.y-1;
b.y=1;
}
}
inFile.close();
outFile.close();
return 0;
}
int main()
{
long n,m,N,M,CMMDC,nl,nh;
long long s;
int colt=0;
ifstream inFile;
CAPITOLUL 9. ONI 2014 9.5. REFLEX 275
ofstream outFile;
N=n-1;
M=m-1;
CMMDC=cmmdc(N,M);
// CMMMC = N * M / CMMDC;
nl=N/CMMDC;
nh=M/CMMDC;
if (nl%2==0)
colt=4;
else
if (nh%2==0)
colt=2;
else
colt=3;
s = nl;
s = s * M + 1;
inFile.close();
outFile.close();
return 0;
}
int main()
{
long n,m,N,M,CMMDC,nl,nh;
long long s;
int colt=0;
ifstream inFile;
ofstream outFile;
N=n-1;
M=m-1;
CMMDC=cmmdc(N,M);
// CMMMC = N * M / CMMDC;
nl=N/CMMDC;
nh=M/CMMDC;
if (nl%2==0)
colt=4;
else
CAPITOLUL 9. ONI 2014 9.6. TRASEU 276
if (nh%2==0)
colt=2;
else
colt=3;
s = nl;
s = s * M + 1;
inFile.close();
outFile.close();
return 0;
}
9.6 traseu
Problema 6 - traseu 100 de puncte
ı̂ntr-un oraş există un hotel de formă cubică, cu N etaje, nu-
merotate de la 1 la N . Suprafaţa fiecărui etaj K (1 & K & N )
este pătratică şi este ı̂mpărţită ı̂n N N camere identice alăturate,
dispuse pe N linii şi N coloane, fiecare cameră având drept
etichetă un triplet de numere naturale KLC (K=etajul, L=linia,
C=coloana, 1 & L, C & N ), ca ı̂n imaginea alăturată.
Dintre cele N N N camere ale hotelului, una este specială
deoarece ı̂n ea locuieşte de mult timp un şoricel. Fiind isteţ, el ştie
eticheta camerei ı̂n care se află precum şi eticheta camerei ı̂n care
bucătarul hotelului depozitează alimente.
Studiind hotelul, şoricelul a constatat că pe fiecare etaj, din orice cameră poate intra ı̂n toate
camerele care au un perete comun cu aceasta (existând un mic orificiu pentru aerisire).
De asemenea, şoricelul a constatat că din fiecare cameră (situată la etajele 2, 3,
..., sau N 1) poate intra ı̂n camera situată imediat deasupra ei şi ı̂n camera situată
imediat sub ea.
Fiind un şoricel binecrescut, el nu intră ı̂n nicio cameră ocupată de clienţi ca să
nu-i deranjeze.
Hotelul având mulţi clienţi, şoricelul trebuie să-şi găsească cel mai scurt traseu de
la camera lui la camera cu alimente, traseu care să treacă printr-un număr minim de
camere, toate neocupate.
Cerinţe
Date de intrare
Date de ieşire
Exemple:
traseu.in traseu.outExplicaţii
34 7 Hotelul are trei etaje (1,2 şi 3). Pe fiecare etaj sunt 3*3 camere.
111 111 şoricelul se află ı̂n camera cu eticheta 1 1 1 iar camera cu alimente
333 112 are eticheta 3 3 3.
331 113 Sunt 4 camere ocupate de
211 123 clienţi. Acestea au etichetele
311 133 : 3 3 1, 2 1 1, 3 1 1, 3 1 3.
313 233 Traseul cel mai scurt trece
333 prin T=7 camere.
Sunt mai multe astfel de
trasee. De exemplu:
1) (1 1 1, 1 1 2, 1 1 3, 1 2 3,
1 3 3, 2 3 3, 3 3 3)
2) (1 1 1, 1 1 2, 1 1 3, 2 1 3, 2
2 3, 3 2 3, 3 3 3)
3) (1 1 1, 1 2 1, 1 3 1, 1 3 2, 2 3 2, 3 2 3, 3 3 3) etc.
Cel mai mic astfel de traseu (ı̂n sens lexicografic) este traseul 1).
Timp maxim de executare/test: 1.0 secunde
Memorie: total 16 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 10 KB
- Poate fi accesibilă oricare din cele 6 camere cu dacă elemntul masivului corespunzător
etichetei camerei este 0 sau este mai mare ca A[x][y][z]
- Vizităm şi marcăm fiecare cameră accesibilă memorând valoarea A[x][y][z]+1 ı̂n elementul
corespunzător etichetei camerei ı̂n masiv. Valoare memorată reprezentând numărul minim
de camere prin care trebuie să treacă şoricelul pentru a ajunge ı̂n camera cu eticheta curentă,
plecând din camera iniţială.
- Poziţiile camerelor care au fost vizitate le vom reţine ı̂ntr-o coadă, ı̂n ordinea vizitării lor.
- Primul elemnt din coadă reţine poziţia (K1,L1,C1), memorat pe poziţia 1.
- Cât timp nu a fost vizitată camera cu alimente, vom vizita pentru poziţia curentă p din
coadă (x,y,z) cele maximum 6 camere accesibile. Vom adăuga la finalul cozii poziţiile fiecărei
camere accesibile, ı̂n ordinea vizitării. Trecem la poziţia p+1 din coadă şi acest procedeu.
- La final, valoarea T=A[K2][L2][C2] reprezintă numărul minim de camere prin care trece
taseul cel mai scurt al şoricelului de la camera lui la camera cu alimente.
- Traseul se va reconstitui plecând de la camera cu alimente către camera şoricelului. Se
vizitează cele maximum 6 camere accesibile marcate cu T-1, ı̂n ordinea din desen, pentru
a obţine traseul cel mai mic ı̂n sens lexicografic. Dintre acestea se alege prima cameră
marcată cu T-1. Apoi, pentru camera aleasă se vizitează camerele accesibile marcate cu T-2
ı̂n ordinea din imagine şi din nou se alege prima dintre acestea, etc. Traseul a fost obţinut
dacă s-a ajuns la camera şoricelului marcată cu 1.
- Poziţiile acestor T camere alese se vor memora ı̂ntr-un vector ale căror componente se vor
afişa ı̂n ordine inversă.
3
Complexitate O N
ofstream g("traseu.out");
void citeste()
{
ifstream f("traseu.in");
f>>n>>m;
f>>k1>>l1>>c1>>k2>>l2>>c2;
for(i=1;i<=m;i++)
{
f>>x>>y>>z;
a[x][y][z]=-1;
}
}
void bordare()
{
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++)
{
a[0][i][j]=-1;
a[n+1][i][j]=-1;
a[i][0][j]=-1;
a[i][n+1][j]=-1;
a[i][j][0]=-1;
a[i][j][n+1]=-1;
}
}
void coada()
{
p=u=1;
c[1].x=k1;
c[1].y=l1;
c[1].z=c1;
a[k1][l1][c1]=1;
while((p<=u)&& (a[k2][l2][c2]==0))
{
x=c[p].x;
y=c[p].y;
z=c[p++].z;
for(i=0;i<6;i++)
{
xc=x+dx[i];
yc=y+dy[i];
zc=z+dz[i];
if (a[xc][yc][zc]==0)
{
u++;
a[xc][yc][zc]=a[x][y][z]+1;
c[u].x=xc;
c[u].y=yc;
c[u].z=zc;
}
}
}
}
if (a[x][y+1][z]==val-1)
++y;
else
if (a[x+1][y][z]==val-1)
++x;
--val;
c[val].x=x;
c[val].y=y;
c[val].z=z;
}
}
int main()
{
citeste();
bordare();
coada();
int val=a[k2][l2][c2];
g<<val<<endl;
c[val].x=k2;
c[val].y=l2;
c[val].z=c2;
drum(val);
g.close();
return 0;
}
ofstream g("traseu.out");
void citeste()
{
ifstream f("traseu.in");
f>>n>>m;
f>>k1>>l1>>c1>>k2>>l2>>c2;
for(i=1;i<=m;i++)
{
f>>x>>y>>z;
a[x][y][z]=-1;
}
}
void bordare()
{
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++)
{
a[0][i][j]=-1;
a[n+1][i][j]=-1;
a[i][0][j]=-1;
a[i][n+1][j]=-1;
a[i][j][0]=-1;
a[i][j][n+1]=-1;
}
}
CAPITOLUL 9. ONI 2014 9.6. TRASEU 281
void coada()
{
p=u=1;
c[1].x=k1;
c[1].y=l1;
c[1].z=c1;
a[k1][l1][c1]=1;
while((p<=u)&&(a[k2][l2][c2]==0))
{
x=c[p].x;
y=c[p].y;
z=c[p++].z;
for(i=0;i<6;i++)
{
xc=x+dx[i];
yc=y+dy[i];
zc=z+dz[i];
if (a[xc][yc][zc]==0)
{
u++;
a[xc][yc][zc]=a[x][y][z]+1;
c[u].x=xc;
c[u].y=yc;
c[u].z=zc;
}
}
}
}
int main()
{
citeste();
bordare();
coada();
int val=a[k2][l2][c2];
g<<val<<endl;
g<<k1<<’ ’<<l1<<’ ’<<c1<<endl;
drum(k2,l2,c2,val);
//cout<<val;
g.close();
return 0;
}
ifstream f("traseu.in");
ofstream g("traseu.out");
CAPITOLUL 9. ONI 2014 9.6. TRASEU 282
struct cel
{
short k, l, c;
};
queue <cel> q;
void lee()
{
short k;
cel x, y;
x.k = k1; x.l = l1; x.c = c1;
M[x.k][x.l][x.c] = 1;
T[x.k][x.l][x.c] = -1;
q.push(x);
while(!q.empty())
{
x = q.front(); q.pop();
for (k=0; k<6; ++k)
{
y.k = x.k + dz[k];
y.l = x.l + dx[k];
y.c = x.c + dy[k];
if (a[y.k][y.l][y.c] == 0)
if (M[x.k][x.l][x.c] + 1 < M[y.k][y.l][y.c])
{
if (T[y.k][y.l][y.c] == 0)
T[y.k][y.l][y.c] = x.k*1000000 + x.l*1000 + x.c;
else
{
if(T[y.k][y.l][y.c] > x.k*1000000+x.l*1000+x.c)
T[y.k][y.l][y.c] = x.k*1000000+x.l*1000+x.c;
}
M[y.k][y.l][y.c] = M[x.k][x.l][x.c] + 1;
q.push(y);
}
else
if (M[x.k][x.l][x.c] + 1 == M[y.k][y.l][y.c])
{
if (T[y.k][y.l][y.c] > x.k*1000000 + x.l*1000 + x.c)
T[y.k][y.l][y.c] = x.k*1000000 + x.l*1000 + x.c;
}
}
}
}
int main()
{
short k, l, c;
CAPITOLUL 9. ONI 2014 9.6. TRASEU 283
f >> n >> m;
for (k=0; k<=n+1; ++k)
for (l=0; l<=n+1; ++l)
for (c=0; c<=n+1; ++c)
{
M[k][l][c] = inf;
if (k==0 || l==0 || c==0)
a[k][l][c] = 1;
if (k==n+1 || l==n+1 || c==n+1)
a[k][l][c] = 1;
}
while (m--)
{
f >> k >> l >> c;
a[k][l][c] = 1;
}
lee();
ifstream fin("traseu.in");
ofstream fout("traseu.out");
struct eticheta
{
char K, L, C;
};
eticheta tail[1000001],poz;
int tf, tl, tlen, i, j;
int main()
{
fin>>N>>M>>K1>>L1>>C1>>K2>>L2>>C2;
for(i=1;i<=M;i++)
{
fin>>K>>L>>C;
A[K][L][C]=-1;
}
A[K1][L1][C1]=1*10;
tail[0].K=K1;
tail[0].L=L1;
tail[0].C=C1;
tf=0;
tl=0;
tlen=1;
while(A[K2][L2][C2]==0 && tlen>0)
{
k=tail[tf].K;
l=tail[tf].L;
c=tail[tf].C;
for (j=0;j<=5;j++)
{
K=k+dk[j];
CAPITOLUL 9. ONI 2014 9.6. TRASEU 284
L=l+dl[j];
C=c+dc[j];
if(1<=K && K<=N && 1<=L && L<=N &&
1<=C && C<=N && A[K][L][C]==0)
{
A[K][L][C]=j + (1 + A[k][l][c] / 10) * 10;
tl++;
tail[tl].K=K;
tail[tl].L=L;
tail[tl].C=C;
tlen++;
if(K==K2 && L==L2 && C==C2)break;
}
}
tf++;
tlen--;
}
T=A[K2][L2][C2]/10;
K=K2;
L=L2;
C=C2;
for(i=T;i>=1;i--)
{
tail[i].K=K;
tail[i].L=L;
tail[i].C=C;
j=A[K][L][C]%10;
j=5-j;
K=K+dk[j];
L=L+dl[j];
C=C+dc[j];
}
fout<<T<<"\n";
for (i=1;i<=T;i++)
{
fout<<(int)tail[i].K<<" "<<(int)tail[i].L<<" "<<(int)tail[i].C<<"\n";
}
fout.close();
fin.close();
return 0;
}
ONI 2013
10.1 aranjare
Problema 1 - aranjare 100 de puncte
Toată lumea ştie că Mirel are 2 N sticluţe cu parfum aşezate pe un raft cu 2 N poziţii,
numerotate de la 1 la 2 N . El are N sticluţe cu parfum cumpărate din ţară şi alte N sticluţe cu
parfum cumpărate din Franţa.
Sticluţele cumpărate din ţară sunt etichetate cu r1 , r2 , r3 , ..., rN , iar sticluţele cumpărate din
Franţa sunt etichetate cu f1 , f2 , f3 , ..., fN . Fiecare sticluţă are asociată valoarea cu care a fost
cumpărată.
Iniţial, Mirel are aşezate pe primele N poziţii sticluţele cumpărate din ţară sortate
crescător după valoare, iar pe următoarele N poziţii sticluţele cumpărate din Franţa sortate tot
crescător după valoare. Astfel, cele 2 N sticluţe cu parfum sunt aşezate ı̂n felul următor:
r1 , r2 , r3 , ..., rN , f1 , f2 , f3 , ..., fN . Mai exact, sticluţa ri se află pe poziţia i, iar sticluţa fi se află
pe poziţia N i, pentru i din intervalul 1, N .
Prietenul său cel mai bun, Marian, s-a gândit să-i facă o surprinză şi să-i schimbe aranjarea
sticluţelor cu parfum ı̂n următoarea ordine: r1 , f1 , r2 , f2 , r3 , f3 , ..., rN , fN . Cum Marian are două
mâini, el poate face numai următorul tip de operaţie: ia două sticluţe cu parfum de pe raft (de
pe două poziţii diferite) şi le interschimbă.
Cerinţe
Dându-se numărul N , şi 2 N valori reprezentând valoarea fiecărei sticluţe cu parfum, ajutaţi-l
pe Marian să facă operaţiile necesare pentru a schimba ordinea sticluţelor cu parfum ı̂n ordinea
precizată ı̂n enunţ.
Date de intrare
Fişierul de intrare aranjare.in conţine pe prima linie numărul N , iar pe următoarea linie 2 N
numere naturale, separate prin câte un spaţiu. Primele N numere reprezintă valorile sticluţelor
cumpărate din ţară, iar următoarele N numere reprezintă valorile sticluţelor cumpărate din Franţa.
Atât primele N , cât şi ultimele N numere sunt sortate crescător ı̂n funcţie de valoare.
Date de ieşire
Fişierul de ieşire aranjare.out va conţine mai multe linii. Pe fiecare linie se vor afla două
numere diferite x şi y din intervalul 1, 2 N , semnificând faptul că Marian trebuie să interschimbe
sticluţa de pe poziţia x cu sticluţa de pe poziţia y.
285
CAPITOLUL 10. ONI 2013 10.1. ARANJARE 286
Exemple:
aranjare.in aranjare.out
Explicaţii
3 24 În explicaţia de mai jos, fiecare sticluţă are numele etichetei
135235 35 urmat de valoarea ei ı̂n paranteză.
34 Şirul iniţial este: r1 1r2 3r3 5f1 2f2 3f3 5
După prima mutare devine: r1 1f1 2r3 5r2 3f2 3f3 5
După a doua mutare devine: r1 1f1 2f2 3r2 3r3 5f3 5
După ultima mutare devine: r1 1f1 2r2 3f2 3r3 5f3 5
Timp maxim de executare/test: 0.5 secunde
Memorie: total 16 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 5 KB
Se observă că valorile sticluţelor din fişierul de intrare nu influenţează cu nimic rezolvarea
problemei, deoarece ele trebuie rearanjate doar după etichetă, deci atât configuraţia iniţială cât şi
cea finală se pot deduce cunoscând doar valoarea N .
Configuraţie iniţială: r1 r2 r3 ...rN f1 f2 f3 ...fN
Configuraţie finală: r1 f1 r2 f2 r3 f3 ...rN fN
Putem calcula pentru orice sticluţă poziţia finală pe care trebuie să o mutăm, astfel că putem
rezolva problema folosind algoritmul:
pentru i de la 1 la 2 N
` dacă pe poziţia i nu avem sticluţa care trebuie:
t facem swap ı̂ntre i şi pozitia pe care se află sticluţa ce trebuie aşezată pe poziţia i
La fiecare pas aducem pe poziţia corectă cel puţin o sticluţă, deci rezultă că sunt necesare cel
mult 2 N astfel de mutări pentru a duce configuraţia iniţială la configuraţia finală.
Complexitate: O N
void read ()
{
ifstream fin ("aranjare.in");
fin>>n;
}
int main()
{
read ();
ofstream fout ("aranjare.out");
for(int i=1;i<=2*n;++i)
p[i]=i,
v[i]=i;
for(int i=1;i<n;++i)
{
CAPITOLUL 10. ONI 2013 10.2. GRADINA 287
if (v[2*i]!=n+i)
{
v[p[n+i]]=v[2*i];
p[v[2*i]]=p[n+i];
fout<<2*i<<" "<<p[n+i]<<"\n";
}
if (v[2*i+1]!=i+1)
{
v[p[i+1]]=v[2*i+1];
p[v[2*i+1]]=p[i+1];
fout<<2*i+1<<" "<<p[i+1]<<"\n";
}
}
return 0;
}
10.2 gradina
Problema 2 - gradina 100 de puncte
Păcală a reuşit să ducă la bun sfârşit ı̂n elegerea cu boierul căruia-i fusese slugă şi, conform
ı̂nvoielii, boierul trebuie să-l răsplătească dându-i o parte din livada sa cu pomi fructiferi. Boierul
este un om foarte ordonat, aşa că livada sa este un pătrat cu latura de N metri unde, pe vremuri,
fuseseră plantate N rânduri cu câte N pomi fiecare. Orice pom fructifer putea fi identificat
cunoscând numărul rândului pe care se află şi poziţia sa ı̂n cadrul rândului respectiv. Cu timpul,
unii pomi s-au uscat şi acum mai sunt doar P pomi. Păcală trebuie să-şi delimiteze ı̂n livadă o
grădină pătrată cu latura de K metri.
Cerinţe
Cunoscând dimensiunile livezii şi grădinii, numărul pomilor din livadă şi poziţia fiecăruia,
determinaţi numărul maxim de pomi dintr-o grădină pătrată de latură K şi numărul modurilor
ı̂n care poate fi amplasată grădina cu numărul maxim de pomi.
Date de intrare
Date de ieşire
Exemple:
CAPITOLUL 10. ONI 2013 10.2. GRADINA 288
Pentru a putea determina grădina cu număr maxim de pomi vom calcula un tablou bidimen-
sional S cu sume parţiale:
S ij = suma elementelor din subtabloul care are colţul stânga-sus de coordonate (1,1) şi
colţul dreapta-jos de coordonate i, j , adică numărul pomilor din această zonă;
Bazându-ne pe tabloul S, pentru fiecare poziţie i, j , determinăm numărul pomilor din sub-
tabloul care are colţul stânga-sus de coordonate i k 1, j k 1 şi colţul dreapta-jos de
coordonate i, j , după formula:
void read ()
{
FILE *fin=fopen("gradina.in","rt");
fscanf(fin,"%d%d%d",&n,&p,&k);
for(int i=1;i<=p;++i)
{
int x, y;
fscanf(fin,"%d%d",&x,&y);
a[x][y]=1;
CAPITOLUL 10. ONI 2013 10.3. SPLIT 289
fclose(fin);
}
void solve ()
{
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];
for(int i=k;i<=n;++i)
for(int j=k;j<=n;++j)
{
int np = a[i][j]-a[i-k][j]-a[i][j-k]+a[i-k][j-k];
if (np>pmax)
{
pmax=np;
nsol=1;
}
else
if (np==pmax)
++nsol;
}
}
int main ()
{
read ();
solve ();
10.3 split
Problema 3 - split 100 de puncte
Fie un şir a1 , a2 , ..., aN de numere naturale. Se ı̂mparte şirul ı̂n patru secvenţe astfel ı̂ncât
orice element din şir să aparţină unei singure secvenţe şi fiecare secvenţă să conţină cel puţin
două elemente. Mai exact, se identifică trei indici i $ j $ k astfel ı̂ncât prima secvenţă este
formată din elementele a1 , a2 , ..., ai , a doua din elementele ai1 , ai2 , ..., aj , a treia din elementele
aj 1 , aj 2 , ..., ak şi ultima din elementele ak1 , ak2 , ..., an . Pentru fiecare secvenţă se determină
costul ei ca fiind diferenţa dintre valoarea maximă şi cea minimă din acea secvenţă.
Cerinţe
Să se determine o ı̂mpărţire a şirului ı̂n patru secvenţe astfel ı̂ncât suma costurilor celor patru
secvenţe să fie maximă.
Date de intrare
Fişierul split.in conţine pe prima linie numărul natural N . Pe linia a doua se găsesc N numere
naturale, separate prin câte un spaţiu, reprezentând elementele şirului a.
Date de ieşire
Fişierul split.out conţine pe prima linie un singur număr natural reprezentând suma maximă
a costurilor celor patru secvenţe. Pe linia a doua se află trei numere naturale i, j şi k, separate
prin câte un spaţiu, cu semnificaţia din enunţ.
Exemple:
split.in split.outExplicaţii
11 29 Cele 4 secvenţe sunt:
9 7 3 0 2 1 8 6 0 11 4 479 9 7 3 0 (cost 9 - 0 = 9)
2 1 8 (cost 8 - 1 = 7)
6 0 (cost 6 - 0 = 6)
11 4 (cost 11 - 4 = 7)
O altă soluţie care obţine tot suma maximă 29 este 5 7
9, dar nu are i minim.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 16 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 5 KB
Se construiesc mai ı̂ntâi liniar vectorii maxst, minst, maxdr, mindr, de lungime N :
minsti = valoarea minimă din secvenţa a1..i
maxsti = valoarea maximă din secvenţa a1..i
mindri = valoarea minimă din secvenţa ai..N
maxdri = valoarea minimă din secvenţa ai..N
Pentru fiecare j (4 & j & n 4):
a vom căuta poziţia i plecând de la j spre stânga şi determinând la fiecare pas minimul şi
maximul din intervalul i..j (aceste două valori aparţin secvenţei a doua). Evident, prima secvenţă
are costul dat de maxsti 1 minsti 1.
a vom căuta poziţia k plecând de la j 1 dpre dreapta şi determinând la fiecare pas minimul
şi maximul din intervalul j 1..k (aceste două valori aparţin secvenţei a treia). Evident, ultima
secvenţă are costul dat de maxdrk 1 mindrk 1.
a calculează costul celor 4 secvenţe şi actualizează costul maxim al celor 4 secvenţe
2
Complexitatea totală este O N .
int main()
{
int i, j, k, x, di, dk, maxim, minim, xi, xj, xk;
CAPITOLUL 10. ONI 2013 10.3. SPLIT 291
// citire
ifstream fin(inFile);
fin >> n;
for (i = 1; i <= n; i++)
fin >> a[i];
fin.close();
// calcul
solmax = 0;
di = dk = 0;
xi = 2; xj = 4; xk = 6;
for (j = 4; j <= n - 3; j++)
{
// maximul din stanga: i=2..j-2
if (a[j] > a[j - 1])
{
maxim = a[j];
minim = a[j - 1];
}
else
{
maxim = a[j - 1];
minim = a[j];
}
}
else
{
maxim = a[j+2];
minim = a[j+1];
}
x = solst + soldr;
if (x > solmax)
{
solmax = x;
xi = di;
xj = j;
xk = dk;
}
}
ofstream fout(outFile);
fout << solmax << "\n";
fout << xi << " " << xj << " " << xk << "\n";
fout.close();
return 0;
}
10.4 momente
Problema 4 - momente 100 de puncte
G are un ceas digital care afişează ora printr-o valoare ı̂ntre 0 şi 23 sub forma unui număr de
una sau două cifre, minutul printr-o valoare ı̂ntre 0 şi 59 sub forma unui număr de exact două cifre
(prima cifră este 0 dacă numărul de minute care trebuie afişat este mai mic decât 10) şi secunda
printr-o valoare ı̂ntre 0 şi 59 sub forma unui număr de exact doua cifre (dacă numărul de secunde
care trebuie afişat este mai mic decât 10, atunci prima cifră este 0). Aceste informaţii apar ı̂n
ordinea: numărul de ore, numărul de minute, numărul de secunde şi sunt separate prin câte un
spaţiu. Exemple: 23 39 17 (pentru ora 23, 39 minute şi 17 secunde) , 1 00 01 (pentru ora 1, 0
minute şi o secundă) sau 0 02 02 (pentru ora 0, 2 minute şi 2 secunde).
G observă că dacă alătură aceste trei valori poate construi un număr natural. Asfel, pentru
exemplele de mai sus obţine numerele 233917, 10001 şi respectiv 202 (Atenţie! Numărul rezultat
nu ı̂ncepe cu 0 - eventualele cifre nule aflate la ı̂nceputul lui sunt eliminate!). G mai observă că
există momente de timp, când numărul astfel format este un palindrom, cum este cazul celui de-al
doilea şi celui de-al treilea exemplu. G denumeşte aceste momente de timp momente palindromice
şi doreşte să afle câte astfel de momente sunt ı̂ntr-un interval de timp dat.
Un interval de timp este situat pe parcursul anului 2013 fiind precizat prin data şi ora exactă
când ı̂ncepe şi data şi ora exactă când se termină. Data este precizată prin doua numere care
reprezintă luna şi ziua, iar ora exactă sub forma afişată de ceasul digital al lui G.
Cerinţe
Date de intrare
Fişierul de intrare momente.in conţine pe prima linie numărul natural k cu semnificaţia din
enunţ. Pe fiecare dintre următoarele k linii se află câte 10 valori naturale separate prin câte un
spaţiu. Primele cinci numere reprezintă luna, ziua, ora, minutul şi secunda când ı̂ncepe intervalul
de timp dat. Următoarele cinci numere reprezintă luna, ziua, ora, minutul şi secunda când se
termină intervalul de timp dat.
Date de ieşire
Fişierul de ieşire momente.out va conţine k linii. Pe linia i (1 & i & k) se va afla un singur
număr care va reprezenta numărul de momente palindromice din intervalul i.
Exemple:
Ideea de plecare in rezolvarea problemei este sa determinam cate palindromuri sunt intr-o zi
”intreaga” (de la ora 0 la 23:59:59). Putem determina acest numar prin simulare directa, adica
testand daca fiecare moment din acest interval este sau nu palindrom. Astfel determinam ca sunt
699 palindromuri ı̂ntr-o zi.
Obtinerea raspunsului pentru un singur interval necesită să determinăm:
1 câte zile ”ı̂ntregi” sunt ı̂n intervalul dat (aceasta putem să o facem prin diverse metode -
chiar printr-o metodă ”brută”);
- un tablou precalculat ı̂n care memorăm numărul de palindromuri pentru fiecare secundă de
pe parcursul unei zile =¿ complexitate constantă pentru a determina acest rezultat şi deci
complexitate O k pentru ı̂ntrega soluţie (100 de puncte);
CAPITOLUL 10. ONI 2013 10.4. MOMENTE 294
- un tabou precalculat ı̂n care reţinem momentele de timp care sunt palindromice şi aplicăm o
căutare binară ı̂n acest tablou =¿ complexitate O k log 700 pentru ı̂ntrega soluţie (100
de puncte);
- metoda brută: determinarea numerelor afişate de ceas din secundă ı̂n secundă şi numărarea
celor care sunt palindromice (obţinând cel mult 50 puncte)
Soluţiile bazate pe simularea brută (parcurgerea fiecărui interval din secundă ı̂n secundă) obţin
cel mult 20 puncte.
59
60
61 begin
62
63 mom[0] := 1;
64
65 precalc;
66
67 assign(fo,’momente.out’);
68 assign(fi,’momente.in’);
69 rewrite(fo);
70 reset(fi);
71 readln(fi,k);
72
73 for i := 1 to k do
74
75 begin
76
77 ct3 := 0; ct2 := 0; ct1 := 0; ctz := 0;
78
79
80 read(fi,luna1,zi1,ora1,min1,sec1);
81 read(fi,luna2,zi2,ora2,min2,sec2);
82
83
84 if ora1+min1+sec1=0 then
85 ctz:=1;
86
87 luna := luna1;
88 zi := zi1;
89 while not ((zi=zi2) and (luna=luna2)) do
90 begin
91 inc(zi);
92 if zifin[luna]+1=zi then
93 begin
94 zi := 1;
95 inc(luna)
96 end;
97 inc(ctz);
98 end;
99 dec(ctz);
100
101
102 ct2 := 699*ctz; //zile complete*699 /zi
103
104 nr := ora1*3600 + min1*60 + sec1;
105
106 if nr>0 then
107 ct1 := 699 - mom[nr-1]
108 else
109 ct1 := 0;
110
111 nr := ora2*3600 + min2*60 + sec2;
112
113 ct3 := mom[nr];
114
115 writeln(fo,ct1+ct2+ct3);
116
117 end;
118
119 close(fo);
120 end.
10.5 secvente
Problema 5 - secvente 100 de puncte
Considerăm şirul de numere naturale nenule distincte a1 , a2 , ..., aN . Notăm cu Li lungimea
maximă a unei secvenţe de elemente cu valori consecutive care se poate obţine prin ordonarea
CAPITOLUL 10. ONI 2013 10.5. SECVENTE 296
crescătoare a primelor i elemente din şirul dat. De exemplu, pentru şirul 7, 2, 3, 8, 20, 4, 10, 9
avem:
L1 1, L2 1, L3 2, L4 2, L5 2, L6 3, L7 3, L8 4.
Cerinţe
Date de intrare
Fişierul secvente.in conţine pe prima linie numărul natural N . Pe fiecare din următoarele N
linii se găseşte câte un număr natural, deci pe linia i 1 se va afla elementul ai , pentru i 1...N .
Date de ieşire
a 3 & N & 200 000 a 1 & ai & 1 000 000, pentru orice i 1...N a Pentru 35% din teste se
garantează că N & 1000
Exemple:
secvente.in secvente.out
Explicaţii
8 1 L 1. şirul : 7. Lungime maximă 1
7 1 L2. şirul: 7,3. Lungime maximă 1
3 2 L 3. şirul: 7,3,2. şirul sortat este 2,3,7. Lungimea maximă este
2 2 2 (dată de secvenţa 2,3)
8 2 L 4. şirul: 7,3,2,8. Lungime maximă 2 (dată de 2,3)
20 3 L5. şirul: 7,3,2,8,20. Lungime maximă 2 (dată de 2,3).
4 3 L 6. şirul: 7,3,2,8,20,4. şirul sortat este 2,3,4,7,8,20. Lungimea
10 4 maximă este 3 (dată de secvenţa 2,3,4).
9 L 7. şirul: 7,3,2,8,20,4,10. Lungime maximă 3 (dată de 2,3,4).
L 8. şirul: 7, 3, 2, 8, 20, 4, 10, 9.
şirul sortat este 2,3,4,7,8,9,10,20. Lungimea maximă este 4 (dată
de secvenţa 7,8,9,10).
Timp maxim de executare/test: 0.2 secunde
Memorie: total 16 MB din care pentru stivă 8 MB
Dimensiune maximă a sursei: 5 KB
Soluţie 35 pct
O primă idee ar fi să avem un vector de vizite pentru a şti la fiecare pas dacă un element se
află ı̂n secvenţa curentă (care ı̂ncepe pe poziţia 1). Un algoritm de rezolvare este următorul:
` vz v i = true;
` crt = numărul de valori de 1 consecutive ı̂n vectorul vz ı̂n jurul poziţiei v i
` dacă crt % best
t best crt
` scrie best
CAPITOLUL 10. ONI 2013 10.5. SECVENTE 297
Complexitate: O(N*N)
Soluţie 100 pct
Pornind de la ideea anterioară, putem renunţa la vz şi să-l ı̂nlocuim cu pos, unde posx este:
Pentru valorile x care aparţin unei secvenţe de valori consecutive dar nu se află pe marginile
secvenţei, se observă că posx este irelevant.
Noul algoritm de rezolvare este:
best = 0
pentru fiecare i de la 1 la N
` begin v i, poziţia unde ı̂ncepe secvenţa singurului element v i
` end v i, poziţia unde se termină secvenţa singurului element v i
` dacă posv i 1! 0, adică elementul v i 1 se află deja ı̂ntr-o secvenţă
t begin posv i 1, actualizăm ı̂nceputul secvenţei ı̂n care se află elementul v i
` dacă posv i 1! 0, adică elementul v i 1 se află deja ı̂ntr-o secvenţă
t end posv i 1, actualizăm sfârşitul secvenţei ı̂n care se află elementul v i
` deci elementul v i se află ı̂n interiorul secvenţei begin, end. Cum poziţiile de ı̂nceput
şi de sfârşit al elementelor din interiorul intervalului sunt irelevante, actualizăm pos
doar pentru elementele din capăt:
t posbegin end
t posend begin
` dacă end begin 1 % best:
t best end begin 1
` scrie best
bool vz[vMax];
int v[nMax];
int pos[vMax];
int n;
void read()
{
scanf("%d\n", &n);
CAPITOLUL 10. ONI 2013 10.6. SPIDER 298
assert(vz[v[i+1]] == false);
vz[v[i+1]] = true;
}
}
void solve()
{
int best = 0;
pos[begin] = end;
pos[end] = begin;
int main()
{
freopen(infile, "r", stdin);
freopen(outfile, "w", stdout);
read();
solve();
fclose(stdin);
fclose(stdout);
return 0;
}
10.6 spider
Problema 6 - spider 100 de puncte
Omul păianjen (Spiderman) sare de pe o clădire pe alta, aflată ı̂n imediata vecinătate, ı̂n nord,
est, sud sau vest. Clădirile din cartierul omului păianjen au o ı̂nălţime exprimată ı̂n numere
naturale şi sunt aşezate pe m rânduri, câte n pe fiecare rând.
Spiderman va alege să sară pe una dintre clădirile vecine, care are ı̂nălţimea mai mică sau egală,
iar diferenţa de ı̂nălţime este minimă. Dacă există mai multe clădiri vecine de aceeaşi ı̂nălţime,
omul păianjen aplică ordinea preferenţială nord, est, sud, vest, dar nu sare ı̂ncă o dată pe o clădire
pe care a mai sărit. Scopul omului păianjen este acela de a reuşi să facă un număr maxim de
sărituri succesive.
Cerinţe
Scrieţi un program care determină numărul maxim de sărituri succesive, pe care ı̂l poate
efectua, pornind de la oricare dintre clădiri, precum şi poziţiile clădirilor care formează drumul
maxim.
Date de intrare
Fişierul spider.in conţine, pe prima linie, două numere naturale, m şi n, despărţite printr-un
spaţiu. Fiecare dintre următoarele m rânduri conţine n numere naturale, separate prin câte un
spaţiu, reprezentând ı̂nălţimile clădirilor.
CAPITOLUL 10. ONI 2013 10.6. SPIDER 299
Date de ieşire
a 0 $ m, n & 1000
a ı̂nălţimile clădirilor sunt numere naturale din intervalul [1,10000000]
a ı̂n orice zonă pătratică de 2x2 clădiri vecine există cel mult 2 clădiri de aceeaşi ı̂nălţime.
Exemple:
Soluţie 35 pct
Se definesc două matrice: A si B cu maxim 1 000 de rânduri şi maxim 1 000 de coloane,
precum şi două tablouri, v şi w, având cel mult 1 000 000 de elemente. ı̂n matricea A se citesc
valorile ı̂nălţimilor clădirilor (numere naturale din intervalul [1, 10 000 000]. Se construieşte câte
unui drum, pornind de la toate elementele matricei A, şi se reţine drumul de lungime maximă.
Se utilizează o funcţie care primeşte, prin intermediul parametrului pas, numărul de ordine al
drumului care se construieşte. Funcţia ı̂ntoarce ı̂nălţimea clădirii cu diferenţa de ı̂nălţime minimă,
şi care respectă condiţiile din enunţ. Funcţia verifică ı̂n matricea B dacă clădirea de coordonate
i, j a mai fost - sau nu - vizitată de Spiderman: dacă B ij pas, atunci clădirea de coordonate
i, j a fost deja vizitată pe parcursul drumului cu numărul de ordine pas, deci clădirea nu este
eligibilă. Dacă B ij este diferit de pas, atunci se alege clădirea respectivă şi lui B ij i se
atribuie valoarea pas.
Se utilizează un subprogram care primeşte, prin parametrii de intrare r şi c, coordonatele unei
clădiri şi construieşte drumul care poate fi parcurs pornind de la aceea clădire. Pentru selectarea
unei clădiri se utilizează funcţia descrisă mai sus. Dacă drumul cu numărul de ordine actual
este mai lung decât precedentul drum, se va reţine lungimea drumului ı̂n variabila k, precum şi
elementele vectorului de direcţii v ı̂n vectorul w, care va avea lungimea k şi va reţine direcţiile
corespunzătoare drumului maxim. Elementele vectorului v se definesc astfel:
v i 1, dacă direcţia aleasă pentru săritura i este Nord;
v i 2, dacă direcţia aleasă pentru săritura i este Est;
v i 3, dacă direcţia aleasă pentru săritura i este Sud;
v i 4, dacă direcţia aleasă pentru săritura i este Vest.
Programul principal va apela acest subprogram pentru toate elementele matricei A, pornind
de la elementul de coordonate 1, 1, parcurgând matricea de la linia 1 la linia m, şi elementele
CAPITOLUL 10. ONI 2013 10.6. SPIDER 300
pe linii, de la 1 la n. Datele unui drum se vor reţine doar dacă lungimea sa este strict mai mare
decât cea mai mare lungime obţinută până ı̂n acel moment. La scrierea datelor ı̂n fişierul de ieşire
(k şi cooronatele clădirilor care formează drumul maxim) se va porni de la coordonatele primei
clădiri, iar coordonatele clădirilor care formează drumul maxim se vor determina prin interpretarea
corectă a elementelor vectorului w.
Complexitate: O N N M M
Soluţie 100 pct
Pornind de la soluţia anterioară, observăm că, dacă nu am avea două valori identice pe poziţii
alăturate, nu are sens să calculăm din fiecare poziţie de ı̂nceput care este lungimea maximă,
deoarece putem stoca ı̂ntr-o matrice auxiliară bestij lungimea maximă a unei secvenţe care
ı̂ncepe pe poziţia i, j , iar atunci când determinăm un drum să actualizăm toate aceste costuri
ale poziţiilor prin care trece.
Cum putem avea două valori vecine cu aceeaşi valoare, acest tablou best trebuie schimbat puţin
ı̂n bestxy d având semnificaţia: lungimea maximă a unei secvenţe care ı̂ncepe pe poziţia x, y
care nu face prima săritură ı̂n direcţia d (d poate avea valori de la 0 la 3, fiecare valoare semnificând
una din cele 4 direcţii posibile).
Complexitate: O N M
int best[nMax][nMax][5];
int h[nMax][nMax];
int n, m;
point sol;
int steps;
if (p == outside)
{
return -1;
}
return h[p.first][p.second];
}
void read()
{
if (best[x][y][d])
{
return best[x][y][d];
}
best[x][y][d] = 1;
point prv;
if (d == 4)
{
prv = outside;
}
else
{
prv = make_pair(x + ii[d], y + jj[d]);
}
if (nxt.first != outside)
{
best[x][y][d] +=
solve(nxt.first.first, nxt.first.second, (nxt.second + 2) % 4);
}
return best[x][y][d];
}
void solve()
{
}
}
void write()
{
while(sol != outside)
{
printf("%d %d\n", sol.first, sol.second);
point aux = getNextPosition(sol, prv).first;
prv = sol;
sol = aux;
}
}
int main()
{
freopen(infile, "r", stdin);
freopen(outfile, "w", stdout);
read();
solve();
write();
fclose(stdin);
fclose(stdout);
return 0;
}
ONI 2012
11.1 7segmente
Problema 1 - 7segmente 100 de puncte
Un indicator cu 7 segmente este un dispozitiv de afişaj electronic destinat
afişării unei cifre zecimale. Aceste dispozitive sunt utilizate pe scară largă ı̂n
ceasuri digitale, contoare electronice şi alte aparate, pentru afişarea informaţiilor
numerice. Cele 7 segmente au fost notate cu literele a, b, c, d, e, f, g, după
modelul din figura alăturată. Afişarea uneia din cifrele de la 1 la 9 constă ı̂n
aprinderea anumitor segmente din cele 7, după cum urmează:
Cifră 1 2 3 4 5 6 7 8 9
Segmente b, c a, b, a, b, c, b, c, f, a, c, d, a, c, d, a, b, c a, b, c, a, b, c,
aprinse d, e, g d, g g f, g e, f, g d, e, f, g d, f, g
Proiectarea diverselor sisteme de afişaj trebuie să ţină cont şi de puterea necesară pentru
afişarea unei cifre. Pentru aprinderea unui segment este necesară o putere de 1 mW. Astfel, ı̂n
funcţie de cifra afişată, dispozitivul necesită o putere egală cu numărul de segmente aprinse la
afişarea cifrei respective. Puterea necesară pentru afişarea unui număr natural este egală cu suma
puterilor necesare afişării fiecăreia dintre cifrele sale.
Cerinţe
Să se scrie un program care citeşte două numere naturale nenule n şi p, (numărul n având
toate cifrele nenule) şi calculează:
a numărul natural k reprezentând puterea necesară pentru afişarea numărului n;
a cel mai mare număr natural t, format numai din cifre nenule, mai mic sau egal decât n, care
necesită pentru afişare o putere de cel mult p mW .
Date de intrare
Prima linie a fişierului de intrare 7segmente.in conţine două numere naturale nenule n şi p
(numărul n având toate cifrele nenule), despărţite printr-un spaţiu, cu semnificaţia de mai sus.
Date de ieşire
Fişierul de ieşire 7segmente.out va conţine pe o singură linie, cele două numere naturale
nenule k şi t (numărul t având toate cifrele nenule), separate printr-un spaţiu, cu semnificaţia de
mai sus.
1 & n $ 10 ;
19
a
a 2 & p & 150;
a pentru rezolvarea primei cerinţe se va acorda 20% din punctaj, iar pentru rezolvarea celei
de-a doua cerinţe se va acorda 80% din punctaj.
Exemple:
303
CAPITOLUL 11. ONI 2012 11.1. 7SEGMENTE 304
7segmente.in 7segmente.outExplicaţii
7654 12 18 7511 Numărul n este 7654; puterea necesară pentru afişare este
3+6+5+4=18 mW, iar cel mai mare număr, mai mic sau egal cu
7654, format numai din cifre nenule, care necesită pentru afişare
o putere de cel mult 12 mW, este 7511.
Timp maxim de executare/test: 0.1 secunde
Memorie: total 2 MB din care pentru stivă 2 MB
Dimensiune maximă a sursei: 5 KB
Numărul de dispozitive de tip 7 segmente necesare pentru afişarea numărului n este egal cu
numărul de cifre al lui n. Puterea necesară pentru afişarea numărului n se calculează ca suma
puterilor necesare pentru afişarea fiecărei cifre ı̂n parte, ı̂n funcţie de numărul de segmente utilizat
la afişarea fiecărei cifrei, conform tabelului.
În vederea determinării numărului t se calculează pentru ı̂nceput numărul de cifre pe care-l
poate avea acesta. Având ı̂n vedere că cifra ce consumă cea mai mică putere este 1 (2 mW) şi nu
sunt utilizate cifre de 0, determinăm numărul de cifre al lui t ca fiind min(numărul de cifre al lui
n, p/2).
Separăm cifrele lui n ı̂ntr-un vector şi iniţializăm un alt vector, care are numărul de poziţii
egal cu numărul de cifre al lui t, cu 1. Din puterea dată scădem puterea utilizată pentru cifrele
de 1 şi apoi cât timp mai este putere de utilizat (putere disponibilă) şi cifre de aflat, determinăm
secvenţial cifrele lui t.
Se porneşte de la vectorul care are pe toate poziţiile cifra 1 şi se actualizează poziţiile acestuia
de la stânga la dreapta (de la cifra cea mai semnificativă către cifra cea mai puţin semnificativă) cu
cifra ce ţine cont de datele problemei până când puterea disponibilă devine nulă sau se completează
numărul de cifre.
Pentru determinarea unei cifre se ţine cont că aceasta trebuie să fie cifra cea mai mare, mai
mică sau egală cu cifra de pe aceeaşi poziţie a lui n şi a cărei utilizare nu epuizează puterea
disponibilă. La fiecare selectare a unei astfel de cifre se reactualizează puterea rămasă neutilizată.
O ”capcană” care trebuie evitată este situaţia ı̂n care o cifră determinată pe o anumită poziţie
este strict mai mică decât cifra de pe aceeaşi poziţie a lui n, situaţie ı̂n care la determinarea
următoarelor cifre ale lui t nu trebuie să mai ţinem cont de faptul că acestea trebuie să fie mai
mici decât cifrele lui n de pe poziţiile corespunzătoare.
#include<stdio.h>
#include<string.h>
char s[30];
int p,w,pc,i,prest,j,ok,ok2;
int x[10]={0,2,5,5,4,5,6,3,7,6};
int main()
{
freopen("7segmente.in","r",stdin);
freopen("7segmente.out","w",stdout);
scanf("%s %d",s,&p);
w=strlen(s);
pc=0;
for (i=0;i<w;i++)
pc+=x[s[i]-’0’];
printf("%d ",pc);
CAPITOLUL 11. ONI 2012 11.1. 7SEGMENTE 305
prest=p;
ok=0;
ok2=1;
for (i=0;i<w;i++)
{
for (j=(s[i]-’0’)*ok2+9*(1-ok2);j>0;j--)
if (x[j]+(w-i-1)*2<=prest)
break;
if (j>0)
{
ok=1;
printf("%d",j);
}
if (j==0 && ok==1)
printf("%d",j);
if (j<s[i]-’0’)
ok2=0;
prest-=x[j];
}
fclose(stdin);
fclose(stdout);
return 0;
}
#include <fstream>
#include <math.h>
ifstream f("7segmente.in");
ofstream g("7segmente.out");
int main()
{
cifra pc=0,i=0,nc=0,aux=0,dmin=0,dmax=0,cifra,egalcif=1;
f>>n>>p;
v[0]=0;
i=0;
while (n>0)
{
v[++i]=n%10;
switch (n%10)
{
case 1 : pc=pc+2;break;
case 2 : pc=pc+5;break;
case 3 : pc=pc+5;break;
case 4 : pc=pc+4;break;
case 5 : pc=pc+5;break;
case 6 : pc=pc+6;break;
case 7 : pc=pc+3;break;
case 8 : pc=pc+7;break;
case 9 : pc=pc+6;break;
CAPITOLUL 11. ONI 2012 11.1. 7SEGMENTE 306
n=n/10;
}
v[0]=i;
g<<pc<<" ";
// oglindesc cifrele
for(i=1;i<=v[0]/2;i++)
{
aux=v[i];
v[i]=v[v[0]+1-i];
v[v[0]+1-i]=aux;
}
t[0]=nc;cc=1;
egalcif=1;
while (p>0&&cc<=t[0])
{
dmin=10;cifra=1;dmax=0;
if (t[0]<v[0]||egalcif==0)
if (i>=dmax&&p-pw[i]+pw[1]>=0)
{
cifra=i;
dmax=i;
}
}
// daca am setat o cifra mai mica, restul cifrelor pot fi mai mari
if (v[cc]-cifra!=0)
egalcif=0;
for(i=1;i<=t[0];i++)
g<<t[i];
g<<"\n";
f.close();
g.close();
return 0;
}
#include <cstdio>
int N, P;
int x[] = { 0, 2, 5, 5, 4, 5, 6, 3, 7, 6 };
int v[NMAX];
int cer1;
int rez[NMAX], nrCifre;
char c = ’1’;
while (1)
{
scanf("%c", &c);
if (c == ’ ’)
break;
v[N++] = c - ’0’;
}
scanf("%d", &P);
//cerinta 1
for (int i = 0; i < N; cer1 += x[v[i++]]);
printf("%d ", cer1);
//cerinta 2
nrCifre = (P / 2) < N ? (P / 2) : N;
if (nrCifre < N)
{
printf("%c", P & 1 ? ’7’ : ’1’);
for (int i = 0; i < nrCifre - 1; printf("1"), ++i)
;
return 0;
}
P -= N << 1;
bool miss = false;
int i;
for (i = 0; i < N; ++i)
{
P += 2;
int j = miss ? 9 : v[i];
while (x[j] > P)
--j;
rez[i] = j;
P -= x[j];
if (rez[i] != v[i])
miss = true;
}
return 0;
}
11.2 copaci
Problema 2 - copaci 100 de puncte
Se consideră n copaci de diferite ı̂nălţimi, aflaţi ı̂n linie dreaptă la distanţe egale, numerotaţi
de la 1 la n. Pentru fiecare copac se cunoaşte ı̂nălţimea sa Hi . Cum şi copacii simt nevoia să
socializeze, fiecare dintre ei are prieteni printre ceilalţi copaci.
Prietenii oricărui copac i se pot afla atât la stânga, cât şi la dreapta sa. Relaţiile de prietenie
sunt definite ı̂n felul următor: pentru fiecare copac i considerăm un şir d1 , d2 , ..., dx reprezentând
prietenii copacului i situaţi ı̂n dreapta sa şi un şir s1 , s2 , ..., sy reprezentând prietenii copacului
i situaţi ı̂n stânga acestuia. Copacii din cele două şiruri corespunzătoare unui copac i formează
ı̂mpreună lista prietenilor acestuia.
Şirurile corespunzătoare copacului i se definesc astfel:
1. a d1 i 1 (dacă i n, atunci copacul i nu are niciun prieten la dreapta sa, şirul d rămânând
vid);
a pentru fiecare k ' 2, dk este cel mai mic indice (1 & dk & n) cu proprietatea că dk % dk1
şi Hdk % Hdk1 . Dacă dk nu există, atunci lista de prieteni la dreapta ai copacului i s-a
ı̂ncheiat şi construirea şirului se opreşte la acest pas.
2. a s1 i 1 (daca i 1, atunci copacul i nu are niciun prieten la stânga sa, sirul s rămânând
vid);
a pentru fiecare k ' 2, sk este cel mai mare indice (1 & sk & n) cu proprietatea că sk $ sk1
şi Hsk % Hsk1 . Dacă sk nu există, atunci lista de prieteni la stânga ai copacului i s-a
ı̂ncheiat şi construirea şirului se opreşte la acest pas.
De exemplu, ı̂n figura de mai jos sunt reprezentaţi 7 copaci, fiecare având precizată sub el
valoarea ı̂nălţimii sale. Primul copac din stânga are indicele 1, iar ultimul are indicele 7.
Copacul 1 este prieten cu copacul 2 fiind vecini, cu
copacul 5 (deoarece copacul 5 este primul din dreapta lui
2 cu ı̂nălţimea mai mare strict decât ı̂nălţimea lui 2). La
dreapta copacului 5 nu exista niciun copac cu ı̂nălţimea
mai mare strict decât a sa, deci singurii prieteni ai co-
pacului 1 sunt 2 şi 5.
Pentru copacul 3, prietenii la stânga sa sunt copacii 2 şi 1, iar cei de la dreapta sa sunt copacii
4 şi 5. Pentru copacul 6, singurul prieten la stânga este copacul 5, iar la dreapta copacul 7.
Copacul 7 poate avea prieteni doar la stânga, aceştia sunt 6 şi 5 (la stânga copacului 5 nu mai
există niciun copac cu ı̂nălţimea mai mare strict decât 8).
Grădinarul Marian vrea să aleagă 3 copaci diferiţi dintre cei n pentru a-i planta ı̂n altă grădină.
El doreşte ca dintre cei 3 copaci, oricum ar alege A si B, 2 dintre ei, atunci A este prieten cu B şi
B este prieten cu A (relaţiile de prietenie se consideră cele stabilite iniţial). Marian are mai multe
opţiuni şi se ı̂ntreabă ı̂n câte moduri distincte poate alege cei 3 copaci cu proprietatea cerută.
Cerinţe
Determinaţi ı̂n câte moduri se pot alege 3 copaci diferiţi dintre cei n cu proprietatea că, oricum
am alege 2 copaci dintre cei 3, fie aceştia copacul A şi copacul B, atunci A este prieten cu B şi B
este prieten cu A.
Date de intrare
Fişierul de intrare copaci.in conţine pe prima linie un număr natural n, reprezentând numărul
de copaci, iar pe a doua linie n numere naturale nenule, separate prin câte un spaţiu, reprezentând
ı̂nălţimile copacilor.
Date de ieşire
Fişierul de ieşire copaci.out va conţine pe prima linie un număr natural reprezentând numărul
de moduri ı̂n care Marian poate alege 3 copaci cu proprietatea din enunţ.
Exemple:
copaci.in copaci.outExplicaţii
7 4 Copacul 1 este prieten cu copacii: 2, 5
6423858 Copacul 2 este prieten cu copacii: 1, 3, 4, 5
Copacul 3 este prieten cu copacii: 1, 2, 4, 5
Copacul 4 este prieten cu copacii: 1, 2, 3, 5
Copacul 5 este prieten cu copacii: 1, 2, 4, 6, 7
Copacul 6 este prieten cu copacii: 5, 7
Copacul 7 este prieten cu copacii: 5, 6
Modurile in care Marian poate alege cei 3 copaci sunt:
(1, 2, 5), (2, 4, 5), (2, 3, 4), (5, 6, 7).
Timp maxim de executare/test: 0.7 secunde
Memorie: total 16 MB din care pentru stivă 4 MB
Dimensiune maximă a sursei: 10 KB
Se observă că dacă am avea un triunghi (A, B, C) cu A ¡ B ¡ C, atunci copacul B trebuie să
aibă ı̂nălţimea minimă dintre cei 3 copaci.
O altă observaţie este că dacă fixăm copacul B ca fiind de ı̂nălţime minimă dintre cei 3 copaci,
atunci există cel mult un triunghi centrat in B.
Astfel trebuie să iterăm de la 1 la N şi fixăm copacul B, iar A şi C le stabilim ca fiind primii
copaci din stânga, respectiv din dreapta copacului B, cu proprietatea că au ı̂nălţimile mai mari sau
egale cu ı̂nălţimea copacului B. Acesta este un posibil triunghi, ı̂nsă trebuie să verificăm concret
dacă A şi C sunt prieteni reciproci. Pentru aceasta folosim 2 vectori ST i şi DRi care semnifică
până unde ı̂n stânga, respectiv ı̂n dreapta, copacul i are ı̂nălţimea maximă. Vectorii se pot calcula
ı̂n O N Hmax sau, mai eficient, cu o stivă ı̂n complexitate O N . Ambele metode intră ı̂n
timp.
Complexitate finală: O N Hmax sau O N .
int main()
{
freopen("copaci.in","r",stdin);
freopen("copaci.out","w",stdout);
scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&h[i]);
for (j=2;j<n;j++)
{
/*caut in stanga primul i cu h[i]>=h[j]*/
i=j-1;
while (0<i && h[i]<h[j])i--;
CAPITOLUL 11. ONI 2012 11.2. COPACI 310
printf("%d",nr);
fclose(stdout);
fclose(stdin);
return 0;
}
int N, rez;
int h[NMAX], st[NMAX], dr[NMAX];
scanf("%d\n", &N);
for (int i = 0; i < N; scanf("%d ", h + i), ++i);
printf("%d\n", rez);
return 0;
CAPITOLUL 11. ONI 2012 11.2. COPACI 311
int N, sol;
int ST[maxn], DR[maxn], H[maxn];
int val[valmax];
ST[i] = p;
val[ H[i] ] = i;
}
}
void calc_dr()
{
memset(val, 0, sizeof(val));
int i, j, p;
DR[i] = p;
val[ H[i] ] = i;
}
}
int main()
{
FILE *f1=fopen("copaci.in", "r"), *f2=fopen("copaci.out", "w");
int i, j, p, q;
calc_st();
calc_dr();
p = ST[i], q = DR[i];
if(p == 0 || q == N+1) continue;
fclose(f1); fclose(f2);
return 0;
}
11.3 intersecţii
Problema 3 - intersecţii 100 de puncte
Dreptunghiul ABCD are laturile de lungimi w şi h, numere naturale pare. Acest dreptunghi
este desenat pe o foaie de matematică şi este descompus ı̂n w h pătrate de latură 1.
Vârfurile A, B, C şi D sunt plasate ı̂n colţurile unor pătrate
de latură 1. Se alege un punct P din interiorul dreptunghiului
ABCD, situat ı̂n colţul unui pătrat de latură 1 şi se uneşte prin
segmente de dreaptă cu cele patru colţuri ale dreptunghiului. Un-
ele segmente intersectează pătrate de latură 1 ı̂n exact două puncte
distincte, altele ı̂ntr-un singur punct.
Numim pătrat 2-intersectat, un pătrat de latură 1 intersectat
de un segment ı̂n exact 2 puncte distincte. ı̂n dreptunghiul din figura alăturată, segmentul P A
trece prin 3 pătrate 2-intersectate, segmentul P B trece prin 9 pătrate 2-intersectate, segmentul
P C trece prin 13 pătrate 2-intersectate, iar segmentul P D prin 7.
Cerinţe
Se dau două numere naturale w şi h reprezentând lungimile laturilor dreptunghiului ABCD,
un număr natural n şi n numere naturale x1 , x2 , ..., xn . Punctul P se plasează, pe rând, ı̂n toate
punctele interioare dreptunghiului ABCD care sunt colţuri ale unor pătrate de latură 1. Pentru
fiecare valoare xi (1 & i & n), determinaţi numărul de segmente distincte care trec prin exact
xi pătrate 2-intersectate.
Date de intrare
Fişierul de intrare intersectii.in conţine pe prima linie trei numere naturale w, h (reprezentând
dimensiunile dreptunghiului) şi n. Următoarele n linii conţin câte un număr natural xi cu
semnificaţia de mai sus.
Date de ieşire
Exemple:
Se observă că dimensiunile dreptunghiului sunt numere pare, dar, nu putem restrânge analiza
problemei la un sfert din dimensiunea dreptunghiului iniţial decât dacă studiem unele cazuri
particulare.
Nu are importanţă cum alegem colţul fix, deci putem studia doar cazul colţului A iar la
final vom multiplica rezultatul cu patru. Putem considera că vârful A, este originea unui sistem
cartezian de coordonate.
Pentru a calcula numărul de intersecţii al unui segment P A, se observă că pentru un punct
P de coordonate x şi y prime intre ele, din acest dreptunghi, avem intersectate exact x y 1
pătrate de latura 1, iar daca x şi y nu sunt prime ı̂ntre ele, atunci dacă d cmmdc x, y , numărul
de intersecţii al segmentului P A va fi in acest caz x y d.
Pentru a reduce complexitatea algoritmului, putem face o optimizare a calculului valorii cmmdc
pentru toate posibilităţile de alegere ale coordonatelor punctului P realizând o variantă 2D a
ciurului lui Eratostene. Vom folosi un vector de vizitare a coordonatelor şi pentru un punct P xy
nevizitat, cu cmmdc x, y 1, marcăm ca vizitate punctele din dreptunghi de coordonate d x
şi d y nevizitate ca având un număr de intersecţii dat de valoarea d x y 1. Astfel voi
contoriza toate valorile distincte de intersecţii, având astfel posibilitatea de a răspunde la fiecare
test ı̂n timp 1.
Deci complexitatea algoritmului devine O h w n. .
int s[5000],W,H,Q,i,j,x,y,r,k,H12,W12,H2,W2;
int main()
{
freopen("intersectii.in","r",stdin);
freopen("intersectii.out","w",stdout);
scanf("%d%d%d",&W,&H,&Q);
W2=W/2;
H2=H/2;
H12=(H-1)/2;
W12=(W-1)/2;
for (i=1;i<=W12;i++)
for (j=1;j<=H12;j++)
{
x=i;
y=j;
while (y){r=x%y; x=y; y=r;}
s[i+j-x]+=4;
x=W-i;
y=j;
while (y){r=x%y; x=y; y=r;}
s[W-i+j-x]+=4;
x=i;
CAPITOLUL 11. ONI 2012 11.3. INTERSECŢII 314
y=H-j;
while (y){r=x%y; x=y; y=r;}
s[i+H-j-x]+=4;
x=W-i;
y=H-j;
while (y){r=x%y; x=y; y=r;}
s[W-i+H-j-x]+=4;
}
if (W%2==0)
for (j=1;j<=H12;j++)
{
x=W2;
y=j;
while (y){r=x%y; x=y; y=r;}
s[W2+j-x]+=4;
x=W2;
y=H-j;
while (y){r=x%y ;x=y; y=r;}
s[W2+H-j-x]+=4;
}
if (H%2==0)
for (i=1;i<=W12;i++)
{
x=i;
y=H2;
while (y){r=x%y; x=y; y=r;}
s[i+H2-x]+=4;
x=W-i;
y=H2;
while (y){r=x%y; x=y; y=r;}
s[W-i+H2-x]+=4;
}
for (i=1;i<=Q;i++)
{
scanf("%d",&k);
printf("%d\n",s[k]);
}
fclose(stdout);
fclose(stdin);
return 0;
}
int s[5000],W,H,Q,i,j,x,y,r,k;
int main()
{
freopen("intersectii.in","r",stdin);
freopen("intersectii.out","w",stdout);
scanf("%d%d%d",&W,&H,&Q);
for (i=1;i<=W-1;i++)
{
if (i<W && i<H)s[i]++;
for (j=i+1;j<=H-1;j++)
{
x=i; y=j;
while (y){r=x%y; x=y; y=r;}
CAPITOLUL 11. ONI 2012 11.3. INTERSECŢII 315
s[i+j-x]++;
if(i<H && j<W)
s[i+j-x]++;
}
}
for (i=1;i<=Q;i++)
{
scanf("%d",&k);
printf("%d\n",4*s[k]);
}
fclose(stdout);
fclose(stdin);
return 0;
}
int H,W,Q,i,j,n,I,J,N,cnt[4010],poz,m1,m2,m3,m4;
int main()
{
freopen("intersectii.in","r",stdin);
freopen("intersectii.out","w",stdout);
scanf("%d%d%d",&W,&H,&Q);
for(i=1;i<W;++i)
for(j=1;j<H;++j)
{
m1=i+j-euclid(i,j);
m2=i+H-j-euclid(i,H-j);
m3=W-i+H-j-euclid(W-i,H-j);
m4=W-i+j-euclid(W-i,j);
cnt[m1]++;
cnt[m2]++;
cnt[m3]++;
cnt[m4]++;
}
poz=W+H;
//for(i=1;i<=poz;++i) cnt[i]+=cnt[i-1];
for(;Q;Q--)
{
scanf("%d",&i);
if(i>poz)
printf("0\n");
else
printf("%d\n",cnt[i]);
}
return 0;
}
#include<bitset>
int H,W,NT,i,j,n,I,J,N,c[4110],poz;
bitset<2110> v[2110];
int main()
{
freopen("intersectii.in","r",stdin);
freopen("intersectii.out","w",stdout);
scanf("%d%d%d",&W,&H,&NT);
for(i=1;i<W;i++)
for(j=1;j<H;j++)
if(!v[i][j])
{
n=i+j-1;
for(I=i,J=j,N=n;I<W && J<H;I+=i,J+=j,N+=n)
{
v[I][J]=1;
c[N]++;
if(poz<N) poz=N;
}
}
//for(i=1;i<=poz;++i) c[i]+=c[i-1];
for(;NT;NT--)
{
scanf("%d",&i);
if(i>poz)
printf("0\n");
else
printf("%d\n",4*c[i]);
}
return 0;
}
int W, H, T, N;
int res[maxn], viz[maxn], d[maxn], a[maxn];
int main()
{
FILE *f1=fopen("intersectii.in", "r"), *f2=fopen("intersectii.out", "w");
int i, j, p, q, k;
N = max(W, H) + 1;
}
}
p = j / d[j];
q = i / a[p];
if(q % d[j] == 0)
a[j] = d[j] * a[p];
else
a[j] = a[p];
if(i == 1)
{
p = j;
res[p] += 4;
}
else
if(j == 1)
{
p = i;
res[p] += 4;
}
else
if(i == j)
{
p = i;
res[p] += 4;
}
else
{
p = i + j - a[j];
res[p] += 4;
}
}
}
while(T--)
{
fscanf(f1, "%d\n", &p);
fprintf(f2, "%d\n", res[p]);
}
fclose(f1);
fclose(f2);
return 0;
}
11.4 palindrom
Problema 4 - palindrom 100 de puncte
Cu mult timp ı̂n urmă, ı̂ntr-un tărâm foarte, foarte ı̂ndepărtat, a existat o ţară numită Tnamap.
Locuitorii acestei ţări puteau să aplice instantaneu transformări asupra cifrelor unui număr, uti-
lizând un tablou de corespondenţe T.
O cifră c a unui număr poate fi ı̂nlocuită cu
cifra corespunzătoare ei, T c.
Dalv şi Sogard, doi indivizi speciali ai acestei societăţi ciudate se aflau ı̂n drum spre INO când
au conştientizat că pot transforma instantaneu, folosind număr minim de transformări de cifre,
CAPITOLUL 11. ONI 2012 11.4. PALINDROM 318
orice număr N ı̂ntr-un palindrom divizibil cu un număr natural K. Dacă sunt mai multe astfel
de numere, ı̂l determină pe cel mai mare.
Voi puteţi?
Cerinţe
Date de intrare
Pe prima linie a fişierului palindrom.in sunt memorate 10 cifre distincte, separate prin câte
un spaţiu, reprezentând valorile T0 , T1 , ..., T9 .
Pe a doua linie sunt memorate cifrele numărului N , iar pe cea de a treia linie un numărul
natural K.
Date de ieşire
Fişierul palindrom.out va conţine pe prima linie numărul maxim care se poate obţine aplicând
transformări succesive numărului N , iar pe a doua linie palindromul divizibil cu K, de valoare
maximă, ce se poate obţine din numărul N , efectuând un număr minim de transformări asupra
cifrelor.
1 & N $ 10
1.000.000
a ;
a N are un număr par de cifre;
a 2 & K & 20;
a se garantează faptul că toate testele au soluţie;
a pentru rezolvarea primei cerinţe se va acorda 20% din punctaj, iar pentru rezolvarea celei
de-a doua cerinţe se va acorda 80% din punctaj.
Exemple:
int p[sigma];
char s[nMax];
int co[sigma][sigma];
int dp[nMax][kMax];
char chr[nMax][kMax];
char prv[nMax][kMax];
char sol[nMax];
int t[nMax];
int n, k;
void read()
{
for(int i = 0; i < sigma; ++i)
{
scanf("%d", &p[i]);
}
CAPITOLUL 11. ONI 2012 11.4. PALINDROM 320
scanf("\n");
scanf("%s\n", s + 1);
scanf("%d", &k);
}
void init()
{
n = strlen(s + 1);
t[0] = 1;
for(int i = 1; i <= n; ++i)
{
t[i] = (t[i-1] * 10) % k;
s[i] -= ’0’;
}
void solve()
{
for(int i = (n>>1); i >= 1; --i)
{
int first = i;
int last = n - i + 1;
cost += dp[i+1][prvDiv];
if(cost < dp[i][j])
{
dp[i][j] = cost;
chr[i][j] = d;
prv[i][j] = prvDiv;
}
}
void write()
{
printf("%d\n", dp[1][0]);
int last = 0;
for(int i = 1; i <= (n>>1); ++i)
{
sol[i] = sol[n - i + 1] = chr[i][last] + ’0’;
last = prv[i][last];
CAPITOLUL 11. ONI 2012 11.4. PALINDROM 321
int main()
{
freopen(infile, "r", stdin);
freopen(outfile, "w", stdout);
read();
init();
solve();
write();
fclose(stdin);
fclose(stdout);
return 0;
}
void citire()
{
freopen("palindrom.in", "r", stdin);
freopen("palindrom.out", "w", stdout);
void preprocesareCosturiTranzitie()
{
int uz[10];
cost[i][i] = 0;
uz[i] = 1;
while (uz[P[j]] == 0)
{
uz[P[j]] = 1;
cost[i][P[j]] = ++cc;
CAPITOLUL 11. ONI 2012 11.4. PALINDROM 322
j = P[j];
}
}
}
void preprocesarePuteri10()
{
putere[0] = 1;
for (int i = 1; i < N; ++i)
putere[i] = (putere[i - 1] * 10) % K;
}
void cerintaA()
{
for (int i = 0; i < N; ++i)
{
int j = 9;
while (cost[x[i]][j] == -1)
--j;
printf("%d", j);
}
printf("\n");
}
void cerintaB()
{
preprocesarePuteri10();
costMin[1][0] = 0;
for (int i = 1; i < K; ++i)
costMin[1][i] = -1;
if (partial[restAdaugat][0] == -1
|| costTranf < partial[restAdaugat][0])
{
partial[restAdaugat][0] = costTranf;
partial[restAdaugat][1] = j;
}
}
}
int restTarget = (j + l) % K;
if (costMin[k][restTarget] == -1
|| costMin[k][restTarget]
> costMin[1 - k][j] + partial[l][0])
{
CAPITOLUL 11. ONI 2012 11.4. PALINDROM 323
if (costMin[k][restTarget]
== costMin[1 - k][j] + partial[l][0])
{
if (compar[j][restTarget] == 0)
{
if (rez[i][restTarget] < partial[l][1])
{
costMin[k][restTarget] = costMin[1 - k][j]
+ partial[l][0];
rez[i][restTarget] = partial[l][1];
back[i][restTarget] = j;
}
}
else
if (compar[j][restTarget] == 1)
{
costMin[k][restTarget] = costMin[1 - k][j]
+ partial[l][0];
rez[i][restTarget] = partial[l][1];
back[i][restTarget] = j;
}
}
}
}
//refac comparatiile
for (int j = 0; j < K; ++j)
for (int l = 0; l < K; ++l)
if (compar[j][l] == 0)
if (rez[i][j] > rez[i][l])
compar[j][l] = 1;
else if (rez[i][j] < rez[i][l])
compar[j][l] = -1;
}
int poz = 0;
int i = N / 2 - 1;
while (i >= 0)
{
rezFinal[cnt++] = rez[i][poz];
poz = back[i][poz];
--i;
}
preprocesareCosturiTranzitie();
cerintaA();
//cerintaB();
return 0;
}
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
int N, K;
char A[maxn], sol[maxn];
int perm[maxcifre], put[maxn];
int ajunge[maxcifre][maxcifre];
int D[maxn][maxk];
void preproc_puteri()
{
int i;
put[0] = 1;
for(i=1; i<=N; i++)
put[i] = (put[i-1] * 10) % K;
}
void preproc_ajunge()
{
int i, j, p;
int main()
{
FILE *f1=fopen("palindrom.in", "r"), *f2=fopen("palindrom.out", "w");
int i, j, p, q;
preproc_puteri();
preproc_ajunge();
int jumatate = N / 2;
//prima cerinta
for(i=jumatate; i>=1; i--)
{
int cif1 = A[i] - ’0’;
int cif2 = A[N-i+1] - ’0’;
int finish = 0;
CAPITOLUL 11. ONI 2012 11.4. PALINDROM 325
if(i == 1) finish = 1;
fprintf(f2, "\n");
D[jumatate+1][0] = 0;
int finish = 0;
if(i == 1) finish = 1;
if(D[i+1][nou_rest]+ajunge[cif1][q]+ajunge[cif2][q] == cost)
CAPITOLUL 11. ONI 2012 11.5. SSTABIL 326
{
//este ok ok ok
sol[i] = (q + ’0’);
break;
}
}
}
}
fprintf(f2, "\n");
fclose(f1);
fclose(f2);
return 0;
}
11.5 sstabil
Problema 5 - sstabil 100 de puncte
Numim număr sstabil orice număr natural care este format dintr-o singură cifră sau care are
suma oricăror două cifre vecine strict mai mare decât nouă.
Asupra oricărui număr care nu este sstabil se pot efectua operaţii de ı̂nlocuire a oricăror două
cifre vecine care au suma strict mai mică decât zece cu o cifră egală cu suma lor.
Operaţiile de ı̂nlocuire pot fi aplicate, ı̂n acelaşi condiţii, şi asupra numerelor rezultate după
fiecare ı̂nlocuire, de câte ori este nevoie, până când se obţine un număr sstabil.
De exemplu, 291 este număr sstabil deoarece 2+9¿9 şi 9+1¿9, iar 183 nu este sstabil pentru
că 1+8¡10. Din numărul 2453, efectuând o singură ı̂nlocuire, putem obţine 653 sau 293 (număr
sstabil) sau 248. Numărul 653, nefiind sstabil, permite o nouă operaţie de ı̂nlocuire, obţinând
astfel numărul 68, care este sstabil. Analog, din numărul 248 se poate obţine numărul sstabil 68.
Cerinţe
Scrieţi un program care să determine cel mai mare număr natural sstabil care se poate obţine
dintr-un număr natural dat, aplicând una sau mai multe operaţii de ı̂nlocuire de tipul menţionat.
Date de intrare
Fişierul de intrare sstabil.in conţine pe prima linie un număr natural n, reprezentând numărul
de cifre al numărului dat, iar pe linia a doua, separate prin câte un spaţiu, cifrele acestui număr.
Date de ieşire
Exemple:
CAPITOLUL 11. ONI 2012 11.5. SSTABIL 327
De la dreapta la stânga se determină cifrele , cât mai mici, ale unui număr sstabil. Procedând
astfel vom obţine un număr sstabil cu număr maxim de cifre şi care are primele cifre cele mai mari
(faţă de un alt număr sstabil care ar avea acelaşi număr de cifre). Vom demonstra ı̂n continuare.
Demonstraţia soluţiei pentru problema sstabil”: ...
int a[1000005],b[1000005];
int n,i,j,r,p,k,s,t;
int main()
{
freopen("sstabil.in","r",stdin);
freopen("sstabil.out","w",stdout);
scanf("%d\n",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
k=0;
r=n;
while (r>0)
{
j=r;
s=0;
do
{
s=s+a[j];
j--;
} while (j>0 && s<10);
k++;
if (s<10)
{
b[k]=s;
r=j;
}
else
if (k==1)
{
p=r; t=a[p];
while (s-t>=10)
{
p--;
t=t+a[p];
}
b[k]=t;
r=p-1;
}
else
{
p=r; t=a[p];
while (s-t>9 || t+b[k-1]<10)
{
p--;
CAPITOLUL 11. ONI 2012 11.5. SSTABIL 328
t=t+a[p];
}
b[k]=t;
r=p-1;
}
}
for (i=k;i>=1;i--)
printf("%d",b[i]);
fclose(stdout);
fclose(stdin);
return 0;
}
int a[1000005],b[1000005];
int n,i,j,r,p,k,s,t;
int main()
{
freopen("sstabil.in","r",stdin);
freopen("sstabil.out","w",stdout);
scanf("%d\n",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
a[0]=9;
a[n+1]=9;
b[0]=9;
k=0;
r=n;
while (r>0)
{
j=r; s=0;
do
{
s=s+a[j];
j--;
} while (s<10);
p=r;
t=a[p];
k++;
b[k]=t;
r=p-1;
}
for (i=k;i>=1;i--)
printf("%d",b[i]);
fclose(stdout);
fclose(stdin);
return 0;
}
int a[1000005],b[1000005];
int n,i,j,r,p,k,s,t;
int main()
CAPITOLUL 11. ONI 2012 11.6. UNUZERO 329
{
freopen("sstabil.in","r",stdin);
freopen("sstabil.out","w",stdout);
scanf("%d\n",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
a[0]=9;
a[n+1]=9;
b[0]=9;
k=0;
r=n;
while (r>0)
{
j=r; s=0;
do
{
s=s+a[j];
j--;
} while (s<10);
p=r;
t=a[p];
k++;
b[k]=t;
r=p-1;
}
for (i=k;i>=1;i--)
printf("%d",b[i]);
fclose(stdout);
fclose(stdin);
return 0;
}
11.6 unuzero
Problema 6 - unuzero 100 de puncte
Se consideră un şir format din N 2 cifre binare, care conţine cel puţin o cifră 1 şi cel puţin
trei cifre 0; prima şi ultima cifră a şirului sunt 0.
Numim 1-secvenţă o succesiune formată numai din cifre 1, aflate pe poziţii consecutive ı̂n acest
şir, delimitată de câte o cifră 0.
Corina construieşte un astfel de şir, ı̂n care numărul de cifre 1 ale fiecărei 1-secvenţe să fie
cuprins ı̂ntre două numere naturale date, p şi q (p & q).
Cerinţe
Scrieţi un program care să determine un număr natural K, egal cu restul ı̂mpărţirii la 666013
a numărului de şiruri distincte, de tipul celui construit de Corina.
Date de intrare
Fişierul de intrare unuzero.in conţine pe prima linie numărul natural N , iar pe cea de a doua
linie numerele naturale p şi q (p & q), separate printr-un spaţiu.
Date de ieşire
CAPITOLUL 11. ONI 2012 11.6. UNUZERO 330
a 1 & p & q $ N $ 1 000 000. a Pentru 20% din teste N & 25, iar pentru alte 40% din teste
25 $ N & 1 000.
Exemple:
Se observă că problema nu poate obţine decât 20 de puncte dacă vom ı̂ncerca să generăm toate
soluţiile posibile pe care apoi să contorizăm si 60 de puncte pentru lungimi mai mici de 1000.
Ideea de rezolvare se bazează pe observarea unei relaţii de recurenţă ce se poate deduce după o
scurtă analiză. Putem considera că utilizăm cifrele zero şi unu iar secvenţele căutate sunt formate
din valori successive de unu. Dacă presupunem că am format deja o secvenţă de 1 pentru o lungime
dată, atunci ı̂n stânga va fi evident 0.
Fie U i, numărul de configuraţii corecte de lungime i, terminate cu 1 şi Z i, numărul de
configuraţii corecte de lungime i, terminate cu 0.
Atunci, avem relaţia de recurenţă următoare:
=Zj
ia
U i
j ib
int mod=666013,a,b;
int s[1000100],u[1000100],z[1000100],n;
int main()
{
ifstream in("unuzero.in");
ofstream out("unuzero.out");
CAPITOLUL 11. ONI 2012 11.6. UNUZERO 331
in>>n>>a>>b;
z[0]=1;
s[0]=1;
for(int i=1;i<=n;++i)
{
z[i]=z[i-1]+u[i-1]; // daca punem 0
z[i]%=mod;
if(i-a>=0)
if(i-b-1>=0)
u[i]=s[i-a]-s[i-b-1];
else
u[i]=s[i-a];
if(u[i]<0) u[i]+=mod;
u[i]%=mod;
s[i]=s[i-1]+z[i];
s[i]%=mod;
}
out<<(z[n]+u[n]-1+(z[n]+u[n]-1<0?mod:0))%mod<<’\n’;
out.close();
return 0;
}
int n,a,b,sol;
int v[100100];
ifstream in("unuzero.in");
ofstream out("unuzero.out");
if(!ok) return;
sol++;
/*for(int i=1;i<=n;++i)
{
out<<v[i];
}
out<<’\n’;*/
}
void back(int k)
{
if(k>n)
verif();
else
for(int i=0;i<=1;++i)
{
v[k]=i;
back(k+1);
}
}
int main()
CAPITOLUL 11. ONI 2012 11.6. UNUZERO 332
{
in>>n>>a>>b;
back(1);
out<<sol%666013<<’\n’;
out.close();
return 0;
}
int mod=666013,a,b;
int u[1000100],z[1000100],n;
int main()
{
ifstream in("unuzero.in");
ofstream out("unuzero.out");
in>>n>>a>>b;
z[0]=1;
z[1]=1;
if(a==1) u[1]=1;
for(int i=2;i<=n;++i)
{
z[i]=z[i-1]+u[i-1]; // daca punem 0
z[i]%=mod;
for(int j=i-b;j<=i-a;++j) // daca punem 1
{
if(j>=0)
{
u[i]+=z[j];
u[i]%=mod;
}
}
}
out<<(z[n]+u[n]-1+(z[n]+u[n]-1<0?mod : 0))%mod<<’\n’;
out.close();
return 0;
}
int N, A, B;
int s[maxn], n[maxn];
int main()
{
FILE *f1=fopen("unuzero.in", "r"), *f2=fopen("unuzero.out", "w");
int i, p;
n[0] = s[0] = 1;
for(i=1; i<=N; i++)
{
n[i] = n[i-1];
// negru pe pozitia i
CAPITOLUL 11. ONI 2012 11.6. UNUZERO 333
n[i] %= mod;
}
fclose(f1);
fclose(f2);
return 0;
}
ONI 2011
12.1 poligon
Problema 1 - poligon 100 de puncte
Poligonul de tragere este un teren special amenajat ı̂n cadrul căruia se fac exerciţii şi se execută
trageri cu arme de foc. Comandantul plasează câte o ţintă ı̂n toate punctele aflate la distanţele
Ri , 1 & i & n faţă de punctul de tragere (origine) şi care au coordonatele carteziene numai numere
naturale nenule.
Specialiştii ı̂n armament români au creat recent o nouă armă sub forma unui tun laser care
ı̂şi lansează razele pe o traiectorie rectilinie şi are capacitatea de a distruge toate ţintele aflate pe
direcţia de tragere.
Cerinţe
Date de intrare
Fişierul de intrare poligon.in conţine pe prima linie numărul n de distanţe la care vor fi
plasate ţinte, iar pe a doua linie n numere naturale nenule distincte separate printr-un spaţiu, ce
reprezintă aceste distanţe.
Date de ieşire
Fişierul de ieşire poligon.out va conţine 3 linii. Pe prima linie se va scrie numărul ţintelor
plasate ı̂n poligon. Pe a doua linie se va scrie numărul minim de lovituri de tun laser cu care se pot
doborı̂ toate ţintele, iar pe a treia linie se va scrie numărul de ţinte doborâte la fiecare lovitură,
separate printr-un spaţiu, ı̂n ordinea crescătoare a unghiurilor direcţiilor cu axa OX.
334
CAPITOLUL 12. ONI 2011 12.1. POLIGON 335
Exemple:
poligon.in poligon.outExplicaţii
6 10 Avem 6 distante: 5,10,15,7,13,17.
5 10 15 7 13 17 6 În poligon vor fi plasate 10 ţinte (punctele negre marcate pe
113311 figură) care pot fi doborâte din 6 lovituri iar la fiecare lovitură
se vor doborı̂ câte 1, 1, 3, 3, 1, 1 ţinte.
Timp maxim de executare/test: 0.4 secunde
Memorie: total 2 MB
Dimensiune maximă a sursei: 5 KB
Observăm că pentru a determina ţintele active din poligon trebuie determinat numărul de
2 2 2
soluţii al ecuaţiei R x y unde R reprezintă raza cercurilor de activare. ı̂n vederea rezolvării
şi cerinţelor ulterioare, coordonatele ţintelor active se vor ı̂ncărca ı̂ntr-o structura de date, pe care
se vor face interogări suplimentare. Micşorarea timpului de rezolvare a acestei ecuaţii are la bază
observaţia că soluţiile sunt simetrice faţă de prima bisectoare.
În vederea determinării numărului minim de lovituri trebuie să determinăm câte drepte dis-
tincte se formează unind coordonatele ţintelor active cu originea sistemului de axe. Având ı̂n
vedere ı̂nsă că trebuie să determinăm şi numărul de puncte coliniare de pe fiecare dreapta, ı̂n
sens trigonometric, vom face sortarea coordonatelor ţintelor active după panta acestora. Se poate
utiliza relaţia conform căreia dacă două puncte A x1 , y1 şi B x2 , y2 sunt coliniare cu originea
sistemului de axe atunci x1 y2 x2 y1 .
După sortare se determină câte puncte fac parte din aceeaşi categorie şi se afişează rezultatele.
ifstream f1("poligon.in");
ofstream f2("poligon.out");
int n,i,c,s,aux,r[1001],j,k;
int x[10000],y[10000];
int main()
{
f1>>n;
c=0;
for (i=1;i<=n;i++)
{
f1>>r[i];
for (j=1;j<=r[i]-1;j++)
{
k=(int)sqrt((double)r[i]*r[i]-j*j);
if (k*k+j*j==r[i]*r[i])
{
c++;
x[c]=j; y[c]=k;
}
}
}
f2<<c<<"\n";
for (i=1;i<c;i++)
for (j=i+1;j<=c;j++)
if (x[i]*y[j]>x[j]*y[i])
CAPITOLUL 12. ONI 2011 12.2. STALPI 336
{
aux=x[i]; x[i]=x[j]; x[j]=aux;
aux=y[i]; y[i]=y[j]; y[j]=aux;
}
x[0]=0; y[0]=1;
s=0;
for (i=1;i<=c;i++)
{
if (x[i-1]*y[i]<x[i]*y[i-1])
s++;
}
f2<<s<<"\n";
s=1;
for (i=2;i<=c;i++)
{
if (x[i-1]*y[i]==x[i]*y[i-1])
{
s++;
}
else
{
f2<<s<<" ";
s=1;
}
}
f2<<s;
f2.close();
f1.close();
return 0;
}
12.2 stalpi
Problema 2 - stalpi 100 de puncte
Între doi stâlpi verticali aflaţi pe malurile unui râu (de o parte şi de alta a râului) se află
legate două cabluri bine ı̂ntinse, paralele cu solul, având distanţa dintre ele egală cu d centimetri.
Cablurile sunt folosite pentru traversarea râului ı̂n caz de inundaţii. Stâlpii sunt notaţi cu A şi B,
iar cablurile cu 1 şi 2 ca ı̂n figura de mai jos.
Pe cabluri există desenate câte n puncte colorate cu diverse culori, culorile fiind codificate prin
numerele 1, 2, 3,..., k. Poziţia punctelor pe fiecare cablu este dată prin distanţa faţă de stâlpul
A pentru fiecare punct. Punctele de pe fiecare cablu sunt numerotate cu 1, 2, 3 ,..., n. Pe fiecare
cablu există cel puţin un punct colorat cu fiecare culoare. Pentru a uşura deplasarea pe cablu,
primarul hotărăşte să lege cu sârmă perechi de puncte de aceeaşi culoare, unul de pe primul cablu,
iar celălalt de pe al doilea cablu, astfel ı̂ncât:
- pentru fiecare culoare să existe o singură pereche de puncte ı̂ntre care să fie legătură;
- lungimea totală de sârmă folosită să fie minimă.
Cerinţe
Să se scrie un program care determină lungimea minimă a sârmei ce va fi folosită pentru
rezolvarea problemei şi o mulţime de perechi de puncte ce urmează a fi legate pentru a obţine
acest minim.
Date de intrare
- pe a treia linie n perechi de numere, formate din distanţa faţă de stâlpul A la fiecare punct
şi culoarea asociată punctului, separate prin câte un spaţiu, aflate pe cablul 2.
Date de ieşire
Fişierul de ieşire stalpi.out va conţine pe prima linie valoarea minimă cerută, iar pe
următoarele k linii numerele de ordine ale punctelor ce vor fi legate cu sârmă, separate printr-un
spaţiu, ı̂ntâi cele de pe cablu 1, urmate de cele de pe cablu 2, ı̂n ordinea crescătoare a culorilor.
Exemple:
stalpi.in stalpi.out
Explicaţii
3 100 211.803 Sunt n 3 perechi de puncte, k 2 culori, codificate cu 1 şi 2.
50 1 200 2 100 1 32 Necesarul minim de sârmă este 211.803.
250 2 100 1 300 2 21 Se leagă punctul P3 de punctul Q2 (ambele au culoarea 1).
Se leagă punctul P2 de punctul Q1 (ambele au culoarea 2).
Timp maxim de executare/test: 0.2 secunde
Memorie: total 4 MB
Dimensiune maximă a sursei: 5 KB
Folosind datele de intrare, pentru fiecare culoare c din mulţimea r1, 2, ..., k x se construiesc doi
vectori:
a a0 , a1 , ..., a30000 , unde ai este codul punctului aflat la distanţa i de stâlpul A de pe
primul cablu (0 dacă nu există punct).
b b0 , b1 , ..., b30000 , unde bi este codul punctului aflat la distanţa i de stâlpul A de pe al
doilea cablu (0 dacă nu există punct).
Parcurgem folosind acelaşi indice i (0, 1, 2, ..., 30 000) ambii vectori a şi b şi actualizăm trei
variabile xa, xb şi min (iniţial min este 20 000), astfel:
dacă ai j 0 si xa=0, atunci xa=i
dacıa bi j 0 si xb=0, atunci xb=i
dacă ai j 0 si xb j 0 si —i-xb—¡min, atunci xa=i; min=—i-xb—
dacă bi j 0 si xa j 0 si —i-xa—¡min, atunci xb=i; min=—i-xb—
CAPITOLUL 12. ONI 2011 12.2. STALPI 338
Õ
S d2 xa xb2
Introducem xa şi xb ı̂n doi vectori x şi y.
Afişăm s şi elementele vectorilor x şi y.
În algoritm am folosit:
prof. Pit-Rada Ionel Vasile, Colegiul National ”Traian”, Drobeta Turnu Severin
Se construiesc tablourile cablu1[ ] şi cablu2[ ], fiecare de câte 30 000 de elemente de tip int.
Pentru fiecare punct de pe fiecare cablu, poziţia i din şirul de date citit şi perechea (distanţă,
culoare) specifice fiecărui punct le-am memorat prin
cablu1[distanţă] = i*100+culoare-1, respectiv
cablu2[distanţă] = i*100+culoare-1
În procesul de citire a datelor şi construire a tablourilor cablu1[ ] şi cablu2[ ] am calculat
numărul de culori k ca fiind valoarea maximă a valorilor culorilor citite.
Pentru fiecare culoare cul, ı̂n ordinea 1,2, ..., k, am utilizat tablourile cablu1[ ] şi cablu2[ ]
pentru sortarea ı̂n complexitate O n a punctelor de aceeaşi culoare de pe fiecare cablu, preluând
distanţa faţă de capătul stâng al cablului pe care se află şi poziţia din şirul datelor de intrare ı̂n
tablourile x1[] şi z1[] cu n1 elemente, respectiv x2[] şi z2[] cu n2 elemente.
Folosind un algoritm asemănător cu cel de la interclasarea a doi vectori ordonaţi am determinat
ı̂n complexitate O n diferenţa cea mai mică dif t, ı̂n valoare absolută, dintre distanţele faţă de
capetele din stânga ale cablurilor, ale celor două puncte de aceeaşi culoare cul aflate pe cabluri
diferite, x1i şi x2j , 1 & i & n1 şiÔ1 & j & n2.
Apoi am calculat, cu formula dif t dif t d d, lungimea sârmei care va uni cele două
puncte, ca lungime a ipotenuzei ı̂ntr-un triunghi dreptunghic de catete dif t şi d şi am adăugat-o
sumei dmin. Am memorat prin xcul i1 şi y cul i2 perechea de poziţii i1 şi i2, din şirul de
date de intrare, corespunzătoare celor două puncte alese.
La final am afişat cu trei zecimale exacte valoarea dmin şi apoi cele k perechi xi, y i,
1 & i & k.
int cablu1[30001],cablu2[30001];
int x1[10001],x2[10001],z1[10001],z2[10001],x[101],y[101];
int n1,n2,n,i,d,k,poz,cul,d1,d2,j,dif,dift,i1,i2,w1,w2,w,m1,m2;
CAPITOLUL 12. ONI 2011 12.2. STALPI 339
double dmin;
ifstream f1("stalpi.in");
ofstream f2("stalpi.out");
int main()
{
f1>>n>>d;
k=0;
d1=0;
for (i=1;i<=n;i++)
{
f1>>poz>>cul;
cablu1[poz]=i*100+cul-1;
if (cul>k) k=cul;
if (poz>d1)d1=poz;
}
d2=0;
for (i=1;i<=n;i++)
{
f1>>poz>>cul;
cablu2[poz]=i*100+cul-1;
if (poz>d2)d2=poz;
}
dmin=0;
for (cul=1;cul<=k;cul++)
{
n1=0;
m1=0;
for (i=0;i<=d1;i++)
{
if (cablu1[i])
{
m1++;
if (cablu1[i]%100+1 == cul)
{
n1++;
x1[n1]=i;
z1[n1]=cablu1[i]/100;
}
}
}
n2=0;
m2=0;
for (i=0;i<=d2;i++)
{
if (cablu2[i])
{
m2++;
if (cablu2[i]%100+1 == cul)
{
n2++;
x2[n2]=i;
z2[n2]=cablu2[i]/100;
}
}
}
i=1;
j=1;
dift=x1[i]-x2[j];
i1=z1[i];
i2=z2[j];
if (dift<0) dift=-dift;
while (i<=n1 && j<=n2 && dift!=0)
{
dif=x1[i]-x2[j];
if (dif<0)dif=-dif;
if (dif<dift)
{
dift=dif;
i1=z1[i];
CAPITOLUL 12. ONI 2011 12.3. TORT 340
i2=z2[j];
}
if (x1[i]<x2[j])
i++;
else
j++;
}
x[cul]=i1;
y[cul]=i2;
dmin=dmin+sqrt((double)dift*dift+d*d);
}
w1=(int)floor(dmin);
w2=(int)floor((dmin-w1)*1000);
f2<<w1<<".";
if (w2<10)
f2<<"00"<<w2;
else
if (w2<100)
f2<<"0"<<w2;
else
f2<<w2;
f2<<"\n";
for (cul=1;cul<=k;cul++)
{
f2<<x[cul]<<" "<<y[cul]<<"\n";
}
f2.close();
f1.close();
return 0;
}
12.3 tort
Problema 3 - tort 100 de puncte
De ziua lui, Gigel a primit un tort de formă dreptunghiulară, ornat cu un caroiaj ce ı̂mparte
tortul ı̂n m n pătrate, ı̂n fiecare pătrat aflându-se câte o cireaşă sau o căpşună. Caroiajul cu
fructe este reprezentat printr-o matrice cu 0 şi 1, 0 ı̂nsemnând cireaşă şi 1 căpşună.
Sărbătoritul are dreptul să taie k felii de tort. O felie se poate obţine prin tăierea după liniile
caroiajului, dintr-un capăt ı̂n celălalt, având lăţimea egală cu 1, de pe oricare latură a tortului,
codificate cu N, E, S, V. Gigel fiind mare amator de căpşuni vrea să taie cele k felii astfel ı̂ncât
numărul căpşunilor din aceste felii să fie cât mai mare.
Spre exemplu, dacă tortul iniţial este reprezentat ca o matrice având 6 6 linii şi coloane,
după 3 tăieturi N, E, V bucata rămasă şi feliile obţinute vor fi conform figurii alăturate.
Cerinţe
Să se scrie un program care să determine numărul de posibilităţi de tăiere a k felii de tort,
pentru a obţine un număr maxim de căpşuni. Două variante ı̂n care diferă doar ordinea de tăiere,
CAPITOLUL 12. ONI 2011 12.3. TORT 341
dar rămâne aceeaşi bucată de tort, nu sunt considerate distincte. De exemplu, dacă numărul
maxim de căpşuni se poate obţine prin una din variantele : VSNNV sau VVNSN, acestea nu sunt
considerate distincte.
Date de intrare
Pe prima linie a fişierului de intrare tort.in sunt scrise dimensiunile tortului, m şi n şi numărul
k al feliilor de tort tăiate de Gigel, separate prin câte un spaţiu. Pe următoarele m linii e descris
caroiajul cu fructe printr-o matrice cu valori de 0 şi 1.
Date de ieşire
Prima linie a fişierului tort.out va conţine numărul maxim de căpşuni care poate fi obţinut
din cele k felii de tort. Pe linia a doua se va găsi numărul de posibilităţi distincte de a obţine
numărul maxim de căpşuni.
Exemple:
După tăierea celor k felii din tort, rămâne un dreptunghi de dimensiuni m m1 n n1,
unde m1 şi n1 reprezintă numarul feliilor orizontale, respectiv verticale (n1 n2 k). Trebuie ca
acest dreptunghi să conţină cât mai puţine valori 1 (capsuni). Construim o matrice ı̂n care pentru
1 & i & m şi 1 & j & n memoram numărul de capşuni din dreptunghiul cu vârfurile opuse 1, 1
şi i, j , după care numărul de capşuni din orice submatrice se gaseşte ı̂n O 1. Vom determina
pentru fiecare k1 cu valori de la 0 la k toate submatricele de dimensiuni m k1 n k k1 cu
număr minim de 1 printr-o singura parcurgere a matricei obţinănd o complexitate O m n k ,
suficientă pentru dimensiunile date.
Algoritm tort
citeste m,n,k
pt. i=1,m
pt. j=1,n citeste Aij sf.
sf.
B[1,1]=A[1,1]; min=m*n+1;
pt. i=2,m B[i,1]=B[i-1,1]+A[i,1] sf.
pt. j=2,n B[1,j]=B[1,j-1]+A[1,j] sf.
pt. i=2,m
pt. j=2,n Bij=B[i,j-1]+B[i-1,j]-B[i-1,j-1] sf.
sf.
pt. k1=0,k1
pt. i=m-k1,m
pt. j=n-k+k1,n
S=Bij-B[i-m+k1,j]-B[i,j-n+k-k1]+B[i-m+k1,j-n+k-k1]
CAPITOLUL 12. ONI 2011 12.4. APE 342
int a[M][M];
int main()
{
FILE *fi,*fo;
int i,j,m,n,k,k1,min,s,np;
fi=fopen("tort.in","rt");
fscanf(fi,"%d %d %d",&m,&n,&k);
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
fscanf(fi,"%d",&a[i][j]);
fclose(fi);
min=m*n+1;
for(i=2;i<=m;i++)
a[i][1]+=a[i-1][1];
for(j=2;j<=n;j++)
a[1][j]+=a[1][j-1];
for(i=2;i<=m;i++)
for(j=2;j<=n;j++)
a[i][j]+=(a[i-1][j]+a[i][j-1]-a[i-1][j-1]);
for(k1=0;k1<=k;k1++)
for(i=m-k1;i<=m;i++)
for(j=n-k+k1;j<=n;j++)
{
s=a[i][j]-a[i-m+k1][j]-a[i][j-n+k-k1]+a[i-m+k1][j-n+k-k1];
if(s<min)
{
min=s;
np=1;
}
else
if(s==min) np++;
}
fo=fopen("tort.out","wt");
fprintf(fo,"%d\n%d\n",a[m][n]-min,np);
fclose(fo);
return 0;
}
12.4 ape
Problema 4 - ape 100 de puncte
CAPITOLUL 12. ONI 2011 12.4. APE 343
Mihai crede că mă pricep la informatică şi mă roagă să ı̂l ajut la efectuarea unor calcule. Mi-a
povestit că ı̂n vacanţă a fost la ţară la bunici. Bunicii lui se ocupă de piscicultură şi au preluat
spre utilizare o zonă de teren unde se află lacuri, heleştee şi bălţi. Sunt plătite taxe speciale ı̂n
funcţie de suprafeţele acestor ape. Bunicului i se pare că cei de la oficiul unde se plătesc taxele au
date greşite ı̂n dosare, despre ariile acestor suprafeţe de apă şi l-a rugat pe Mihai să ı̂i calculeze cu
aproximaţie aceste arii. Mihai a studiat problema şi s-a hotărât să ı̂nconjoare fiecare apă, mergând
pe conturul acesteia. Pasul lui are lungimea de 1 metru. La fiecare pas Mihai foloseşte o busolă
şi ı̂şi notează ı̂ntr-un carneţel direcţia ı̂nspre care a fost efectuat pasul Nord, Sud, Est sau Vest.
După fiecare pas Mihai actualizează şi numărul de paşi pe care i-a făcut.
Cerinţe
Date de intrare
Fişierul de intrare ape.in are pe prima linie numărul P de paşi ai traseului. ı̂n linia 2 se află
un şir de P litere mari, fără spaţii ı̂ntre ele, din mulţimea {N, S, E, V} reprezentând traseul.
Date de ieşire
Fişierul de ieşire ape.out va conţine patru numere naturale separate prin câte un spaţiu:
primul număr reprezintă dimensiunea pe direcţia Vest-Est şi al doilea număr reprezintă dimensi-
unea pe direcţia Nord-Sud a suprafeţei dreptunghiulare de arie minimă care cuprinde ı̂n interior
sau pe margini suprafaţa apei delimitată de traseu; al treilea număr reprezintă sensul parcurgerii,
iar al patrulea număr reprezintă aria.
Exemple:
Presupunem că pornim cu poziţia x; y 0; 0. ı̂n timpul parcurgerii putem păstra maximul
şi minimul absciselor (xM şi xm) şi respectiv al ordonatelor (yM şi ym ) din care vom calcula
dimensiunile suprafeţei dreptunghiulare minime.
Pentru calcularea ariei vom porni din nou din 0; 0. Ne imaginăm că putem marca traseul
ı̂ntr-o matrice ı̂n care toate poziţiile sunt marcate iniţial cu 0. După fiecare pas ajungem din
poziţia anterioară x; y la poziţia curentă x1; y1.
Vom parcurge traseul presupunând că o facem ı̂n sens orar şi vom efectua marcaje ı̂n matrice
ı̂n anumite puncte din traseu, şi anume ı̂n acelea care constituie, abscisele minime şi respectiv
maxime relative la aceeaşi ordonată y. Cu alte cuvinte fiecare bandă orizontală de ı̂nălţime 1 şi
lungime oricât de mare va intersecta apa formându-se una sau mai multe zone dreptunghiulare
de ı̂nălţime 1 şi lungime egală cu diferenţa dintre abscisa maximă şi cea minimă relative la acea
zonă.
Se poate observa că de fapt trebuie doar adunate/scăzute abscisele extreme care corespund
acelor zone de apă. Mai mult se poate observa că nu contează ordinea ı̂n care se adună/scad acele
valori, astfel că se pot efectua operaţiile atunci când ajungem la aceste extreme, şi deci nu avem
nevoie de matrice.
Dacă la sfârşitul calculelor obţinem valoare negativă, atunci sensul parcurgerii este cel invers
orar şi aria se calculează scăzând din modulul valorii ariei calculate numărul celulelor parcurse,
iar ı̂n cazul ı̂n care valoarea este pozitivă valoarea se păstrează nemodificată.
Complexitate O p
int P,i,x=0,y=0,s=0,xm=0,xM=0,ym=0,yM=0,sens;
char a,b,c;
int main()
{
ifstream f1("ape.in",ios::in);
ofstream f2("ape.out",ios::out);
f1>>P;
f1>>a;
c=a;
for (i=2;i<=P+1;i++)
{
if (i>P)
b=c;
else
f1>>b;
if (a==’N’)
switch (b)
{
case ’N’ : {s=s-x-1; y++; break;}
case ’E’ : {x++; break;}
case ’V’ : {s=s-x-1; x--; break;}
}
else
if (a==’V’)
switch (b)
{
case ’N’ : {y++; break;}
case ’V’ : {x--; break;}
case ’S’ : {s=s+x; y--; break;}
}
else
if (a==’S’)
switch (b)
{
case ’S’ : {s=s+x; y--; break;}
case ’V’ : {x--; break;}
CAPITOLUL 12. ONI 2011 12.5. EC 345
if (x>xM) xM=x;
if (x<xm) xm=x;
if (y>yM) yM=y;
if (y<ym) ym=y;
a=b;
}
if (s<0)
{
sens=1;
s=-s;
s=s-P;
}
else
sens=0;
f2<<sens<<" "<<s<<endl;
f1.close();
f2.close();
return 0;
}
12.5 ec
Problema 5 - ec 100 de puncte
Alexandru are la dispoziţie un tablou pătratic de dimensiune n cu numere ı̂ntregi şi k ecuaţii
de tipul I şi II. Ecuaţiile de tipul I sunt de forma: ax b c, cu a, b, c numere naturale, iar
2
ecuaţiile de tipul II sunt de forma: ax bx c d, cu a, b, c, d numere naturale.
Alexandru ı̂şi propune să determine pentru fiecare tip de ecuaţie: numărul lor şi câte dintre
ele au rădăcinile ı̂n tabloul dat.
Cerinţe
Să se scrie un program care determină numărul de ecuaţii de tipul I, câte dintre acestea au
exact o rădăcină ı̂n tablou, respectiv numărul de ecuaţii de tipul II şi câte dintre acestea au exact
ambele rădăcini ı̂n tablou.
Date de intrare
Fişierul de intrare ec.in va conţine: pe prima linie numerele naturale n şi k separate printr-un
spaţiu, pe următoarele n linii elementele tabloului separate prin câte un spaţiu, iar pe următoarele
k linii ecuaţiile ı̂n forma din enunţ, câte una pe fiecare linie.
Date de ieşire
Fişierul de ieşire ec.out va conţine pe prima linie două numere separate printr-un spaţiu
reprezentând numărul de ecuaţii de tipul I, respectiv numărul de ecuaţii de tipul I care au ex-
act o rădăcină, aflată ı̂n tablou, iar pe a doua linie tot două numere separate printr-un spaţiu
reprezentând numărul de ecuaţii de tipul II, respectiv numărul de ecuaţii de tipul II cu exact două
rădăcini, ambele rădăcini ı̂n tablou.
CAPITOLUL 12. ONI 2011 12.5. EC 346
Exemple:
ec.in ec.out
Explicaţii
25 21 Prima ecuaţie este de tipul I si are rădăcina 0, care nu se găseşte
12 31 ı̂n tablou.
2 -1 A doua ecuaţie este de tipul II şi are două rădăcini egale cu -1,
1x+0=0 care se găsesc ı̂n tablou.
20xˆ2+40x+20=0 A treia ecuaţie este de tipul I şi are rădăcina 2, care se găseşte
101x+200=402 ı̂n tablou.
2xˆ2+1x+4=0 A patra ecuaţie este de tipul II şi nu are rădăcinile ı̂n tablou.
1xˆ2+1x+3=5 A cincea ecuaţie este de tipul II şi are rădăcinile -2 şi 1, dar nu
sunt amândouă ı̂n tablou.
Timp maxim de executare/test: 0.8 secunde
Memorie: total 4 MB
Dimensiune maximă a sursei: 5 KB
int main()
{
freopen("ec.in", "r", stdin);
freopen("ec.out", "w", stdout);
L = N*N;
for (int i = 0; i < L; ++i)
{
int x;
scanf("%d ",&x);
v[x+10000] = 1;
}
if (line[j] == ’x’)
{
coef[nr_coef++] = val;
val = 0;
if (line[j + 1] == ’ˆ’)
j+=3;
else
++j;
}
if (line[j] == ’=’)
{
coef[nr_coef++] = val;
val = 0;
}
}
coef[nr_coef++] = val;
if (nr_coef == 3)
{
//ec gr1
++nr_ec1;
if (coef[0] == 0) continue;
sol = ((long double)(coef[2] - coef[1])) / coef[0];
if (is_int(sol) && v[(int)sol+10000])
++sol_ec1;
}
else
{
//ec gr 2
++nr_ec2;
bool ok = true;
int delta = coef[1]*coef[1] - 4*coef[0]*(coef[2] - coef[3]);
if (coef[0] == 0)
CAPITOLUL 12. ONI 2011 12.6. FURNICI 348
{
if (coef[1] == 0)
continue;
sol = ((long double)(coef[3] - coef[2])) / coef[1];
if (is_int(sol) && v[(int)sol+10000])
++sol_ec2;
continue;
}
12.6 furnici
Problema 6 - furnici 100 de puncte
La Institutul de cercetare al insectelor s-a descoperit că dacă furnicile sunt puse pe o bară
metalică, ele au un comportament bine definit după următoarele reguli:
1. Imediat cum a fost pusă pe bară ea ı̂şi ı̂ncepe deplasarea ı̂n sensul ı̂n care a fost orientată,
cu viteza constantă de 1cm/s. Furnica nu se opreşte cât timp se află pe bara metalică chiar
dacă se ciocneşte cu altă furnică.
2. Dacă pe drum nu se ı̂ntâlneşte cu altă furnică ea ı̂şi va continua deplasarea până când va
cădea de pe bară.
3. Când două furnici se ı̂ntâlnesc, ele ı̂şi schimbă amândouă instantaneu sensul de deplasare.
Cerinţe
Ştiind că pe o bară metalică de lungime L cm se plasează exact N furnici ı̂n poziţii cunoscute şi
cu sensul iniţial de deplasare cunoscut, să se scrie un program care calculează numărul de secunde
după care va cădea de pe bară şi ultima furnică de la momentul iniţial. Toate furnicile ı̂şi ı̂ncep
deplasarea concomitent.
Date de intrare
Fişierul de intrare furnici.in conţine pe prima linie două numere naturale L şi N separate
printr-un spaţiu. Apoi urmează N linii cu câte 2 valori: pozi şi sensi separate printr-un spaţiu,
pozi este un număr natural care reprezintă coordonata la care se află furnica i la momentul iniţial,
iar sensi este un caracter din mulţimea {’S’, ’D’} ce arată sensul de deplasare iniţial pe care ı̂l
are furnica i (S pentru stânga şi D pentru dreapta).
Date de ieşire
Fişierul de ieşire furnici.out va conţine un singur număr care reprezintă timpul la care a căzut
ultima furnică.
a 1 $ L $ 10 000 000
a 0 $ N $ 100 000
a 0 & pozi & L
CAPITOLUL 12. ONI 2011 12.6. FURNICI 349
Exemple:
Soluţia 1: 20 puncte
Se memorează ı̂ntr-un vector de lungime L (lungimea barei metalice) pe poziţia k direcţia
ı̂n care se deplasează furnica ce se afla pe bara la punctul de coordonata k. Apoi se simulează
deplasările cu pasul o unitate de timp pana când tot vectorul devine nul. Rezultatul este numarul
de paşi. Pentru a evita ciocnirile ı̂n coordonate raţionale este suficient să se observe că cioncirile ı̂n
aceste coordonate sunt de forma k 0.5. Lungimea vectorului se dublează şi direcţia de deplasare
a furnicilor care se află la coordonata k va fi salvată pe poziţia 2 k. Rezultatul este numărul de
paşi / 2.
Complexitate : O(L*timpul de cădere al ultimei furnici)
Soluţia 2: 50-60 puncte
Se observă că dacă sortăm furnicile crescător după coordonata la care se află iniţial pe bară,
furnica aflată pe poziţia k ı̂n vectorul sortat va rămâne pe poziţia k tot timpul simulării. (cu alte
cuvinte: dacă are x furnici ı̂n stânga şi y ı̂n dreapta, x şi y rămân constante. Furnicile care cad
se consideră prezente şi ele).
În vectorul de furnici sortat memorăm ca valoare reală coordonata la care se află fiecare furnică.
Parcurgem vectorul şi calculăm timpul maxim ı̂n care furnicile se pot deplasa fără să se ciocnească,
La o nouă parcurgere actualizăm coordonatele şi schimbăm sensurile la eventualele ciocniri (cel
puţin una).
CAPITOLUL 12. ONI 2011 12.6. FURNICI 350
Se observă că dacă la pasul curent nu este nici o furnică orientată spre exteriorul barei, la pasul
viitor cel puţin una va fi orientată spre exterior şi o scoatem din calcul ţinând cont de timpul care
s-a scurs până aceasta a căzut. Repetăm până când bara se eliberează. Rezultatul este maximul
dintre aceşti timpi.
2
Complexitate : O N
Soluţie 3: 100 puncte
Trebuie să se observe următorul lucru :
În desen sunt două furnici ı̂n punctele A şi B care se deplasează ı̂n dreapta respectiv ı̂n stânga.
Deplasarea furnicii A : L©2 L©2 Y L Y
Deplasarea furnicii B : L©2 L©2 X L X
Se observă că după L secunde furnicile sunt ı̂n acelaşi loc ca la ı̂nceput dar cu sensuri de
deplasare inversate. Să consideram scenariul ı̂n care furnicile ”trec una prin alta” atunci când se
intalnesc:
După L secunde furnica A se va afla ı̂n punctul B iar furnica B ı̂n punctul A. Dacă nu ţinem
cont de notarea furnicilor (fapt care nu contează) suntem ı̂n acelaşi caz ca ı̂n regulile din enunţ
după L secunde.
Aceasta proprietate este adevarată doar ı̂n cazul in care furnicile au aceeaşi viteză (1cm/s
specificat ı̂n enunţ).
Proprietatea este adevarată şi pentru N furnici.
Deci problema cere să se calculeze maximul dintre distanţele pe care le au de parcurs furnicile
neţinand cont de ciocnirile dintre ele.
Complexitate O N
#include <cstdio>
int L, N, rez, x;
char dir;
int main()
{
freopen("furnici.in", "r", stdin);
freopen("furnici.out", "w", stdout);
printf("%d\n", rez);
return 0;
}
ONI 2010
13.1 cern
Problema 1 - cern 100 de puncte
”CERN este un acronim folosit pentru a desemna Laboratorul Eu-
ropean pentru Fizica Particulelor Elementare. Acronimul s-a păstrat
de la vechea denumire ı̂n limba franceză, şi anume Conseil Européen
pour la Recherche Nucléaire. Acesta este cel mai mare laborator de
cercetare a particulelor elementare din lume, situat ı̂n suburbia nord-
vestică a Genevei, chiar pe graniţa dintre Elveţia şi Franţa. Funcţia
primară a complexului CERN este de a furniza acceleratoare de par-
ticule elementare şi alte tipuri de infrastructuri necesare fizicii par-
ticulelor de energii ı̂nalte.”
Acceleratorul de particule CERN este dispus sub forma a 3 cercuri cu aceeaşi rază, tangente
exterioare două câte două, numerotate pe figură cu 1, 2, 3. Traiectoria unei particule elementare
porneşte din unul din punctele marcate pe figură cu A, B, C, D, E, F şi se deplasează cu viteză
constantă de 10/unitatea de timp numai pe circumferinţa cercurilor.
La trecerea printr-un punct de tangenţă dintre două cercuri particula ı̂şi schimbă atât sensul
de deplasare, cât şi cercul pe care se deplasează.
Astfel, dacă sensul de deplasare a fost la un moment dat trigonometric,
la trecerea printr-un punct de tangenţă devine invers trigonometric şi dacă
sensul de deplasare a fost invers trigonometric, la trecerea printr-un punct
de tangenţă devine trigonometric.
Cerinţe
o
Ştiind că cercurile ce formează acceleratorul sunt marcate din grad ı̂n grad ı̂ncepând cu 0 , ı̂n
sens trigonometric (aşa cum se indică ı̂n figura alăturată), să se scrie un program, care, cunoscând
punctul iniţial şi sensul de deplasare al unei particule, să determine poziţia particulei ı̂n accelerator
după un număr dat de unităţi de timp.
Date de intrare
Prima linie a fişierului de intrare cern.in conţine un caracter p ce indică punctul de plecare
al particulei.
A doua linie a fişierului de intrare conţine două numere ı̂ntregi s şi t, separate printr-un spaţiu,
ce indică sensul de deplasare (1 pentru sens trigonometric şi -1 pentru sens invers trigonometric),
respectiv numărul de unităţi de timp cât durează deplasarea.
Date de ieşire
Pe prima linie a fişierului de ieşire cern.out se vor scrie două numere naturale g şi c, sep-
arate printr-un spaţiu, ce reprezintă numărul de grade, ı̂n sens trigonometric, respectiv cercul,
corespunzătoare poziţiei finale unde se va găsi particula după trecerea celor t unităţi de timp.
352
CAPITOLUL 13. ONI 2010 13.1. CERN 353
a p " {’A’,’B’,’C’,’D’,’E’,’F’}
a s " {-1, 1}
a 0 & t & 1.000.000.000
a 0 & g & 359
a c " {1, 2, 3}
a pentru toate seturile de date de intrare, poziţia finală a particulei nu coincide cu unul dintre
punctele de tangenţă dintre cercuri.
Exemple:
cern.in cern.out
Explicaţii
A 200 3 Particula pleacă din punctul A ı̂n sens trigonometric şi are traseul:
o
1 320 a 180 pe cercul 1 ı̂n sens trigonometric
o
a 60 pe cercul 2 ı̂n sens invers trigonometric
o
a 80 pe cercul 3 ı̂n sens trigonometric
o
Poziţia finală este la 200 pe cercul 3
Timp maxim de executare/test: 0.1 secunde
Memorie: total 2 MB din care pentru stivă 1 MB
Dimensiune maximă a sursei: 20 KB
Deoarece toate cercurile au aceeaşi rază, triunghiul format cu centrele celor 3 cercuri este
o
echilateral şi va avea ı̂n consecinţă toate unghiurile de 60 . În felul acesta punctele de plecare ale
particulei sunt aşezate după cum urmează:
o
a ’A’ - 0 pe cercul 1
o
a ’B’ - 60 pe cercul 1
o
a ’C’ - 120 pe cercul 2
o
a ’D’ - 180 pe cercul 2
o
a ’E’ - 240 pe cercul 3
o
a ’F’ - 300 pe cercul 3
Următoarea observaţie legată de rezolvarea problemei este că după
o
un număr de 6π unităţi de timp (1080 ) particula va trece din nou prin
punctul de plecare, indiferent care este acesta. Problema nu necesită tipuri de date structurate
ı̂nsă presupune o analiza atentă a tuturor situaţiilor ı̂n care apar treceri de la un cerc la altul şi
o o
respectiv ı̂n care se fac treceri de la 360 la 0 ı̂n concordanţă cu sensul de deplasare.
Aceste situaţii sunt:
1. cercul 1 tangenţă cu cercurile 2 şi 3
2. cercul 2 tangenţă cu cercurile 3 şi 1
3. cercul 3 tangenţă cu cercurile 1 şi 2
o
4. trecerea prin 360 pentru cercurile 1 şi 3
Se analizează toate aceste situaţii şi se stabileşte modul de trecere de la un cerc la altul ţinând
cont desigur şi de sensul de deplasare al particulei. Problema poate fi abordată iterativ, recursiv
sau cel mai interesant numai prin tratarea punctelor de tangenţă (soluţia cea mai rapidă, ce nu
necesită instrucţiuni repetitive ci numai instrucţiuni de decizie).
7 ofstream fo("cern.out");
8
9 long t;
10 int g,s,cerc;
11 char p;
12
13 int main()
14 { fi>>p>>s>>t;
15 t=t%1080;
16
17 switch(p)
18 { case ’A’:
19 if (s==1)
20 if(t<180) fo<<t<<" 1\n", t=0; else t=t-180,cerc=2,p=’G’;
21 else
22 if(t<120) fo<<360-t<<" 1\n",t=0; else t=t-120,cerc=3,p=’I’;
23 break;
24 case ’B’:
25 if (s==1)
26 if(t<120) fo<<t+60<<" 1\n", t=0; else t=t-120,cerc=2,p=’G’;
27 else
28 if(t<180) fo<<(360-(t-60))%360<<" 1\n",t=0;
29 else t=t-180,cerc=3,p=’I’;
30 break;
31 case ’C’:
32 if (s==1)
33 if(t<180) fo<<t+120<<" 2\n", t=0; else t=t-180,cerc=3,p=’H’;
34 else
35 if(t<120) fo<<120-t<<" 2\n",t=0; else t=t-120,cerc=1,p=’G’;
36 break;
37 case ’D’:
38 if (s==1)
39 if(t<120) fo<<t+180<<" 2\n", t=0; else t=t-120,cerc=3,p=’H’;
40 else
41 if(t<180) fo<<180-t<<" 2\n",t=0; else t=t-180,cerc=1,p=’G’;
42 break;
43 case ’E’:
44 if (s==1)
45 if(t<180) fo<<(240+t)%360<<" 3\n", t=0;
46 else t=t-180,cerc=1,p=’I’;
47 else
48 if(t<120) fo<<240-t<<" 3\n",t=0; else t=t-120,cerc=2,p=’H’;
49 break;
50
51 case ’F’:
52 if (s==1)
53 if(t<120) fo<<(300+t)%360<<" 3\n", t=0;
54 else t=t-120,cerc=1,p=’I’;
55 else
56 if(t<180) fo<<300-t<<" 3\n",t=0; else t=t-180,cerc=2,p=’H’;
57 break;
58 }
59
60 s=-s;
61
62 while(t)
63 { switch(p)
64 { case ’G’:
65 if(s==1)
66 if(cerc==1)
67 if (t>60)t=t-60,p=’I’; else fo<<180+t<<" 1\n",t=0;
68 else
69 if (t>300) t=t-300,p=’H’; else fo<<t<<" 2\n",t=0;
70 else
71 if(cerc==1)
72 if(t>300) t=t-300,p=’I’;else fo<<(540-t)%360<<" 1\n",t=0;
73 else
74 if(t>60) t=t-60,p=’H’; else fo<<360-t<<" 2\n",t=0;
75
76 cerc=3;
77 break;
78 case ’H’:
79 if(s==1)
80 if(cerc==3)
81 if (t>300)t=t-300,p=’I’; else fo<<120+t<<" 3\n",t=0;
82 else
CAPITOLUL 13. ONI 2010 13.1. CERN 355
42 pozitie=0;
43 sens=1;
44 cerc=2;
45 }
46 break;
47 case 0 : if (cerc==2)
48 {
49 pozitie=180;
50 sens=sens*(-1);
51 cerc=1;
52 }
53 // trecerea prin 0 pentru cercurile 1 si 3
54 if (cerc==1&&sens==-1) pozitie=360;
55 if (cerc==3&&sens==-1) pozitie=360;
56 break;
57 case 300 : if (cerc==2)
58 {
59 pozitie=120;
60 sens=sens*(-1);
61 cerc=3;
62 }
63 break;
64 case 120 : if (cerc==3)
65 {
66 pozitie=300;
67 sens=sens*(-1);
68 cerc=2;
69 }
70 break;
71 case 60 : if (cerc==3)
72 {
73 pozitie=240;
74 sens=sens*(-1);
75 cerc=1;
76 }
77 break;
78 case 240 : if (cerc==1)
79 {
80 pozitie=60;
81 sens=sens*(-1);
82 cerc=3;
83 }
84 break;
85
86 case 360 : if (cerc==2&&sens==1)
87 {
88 pozitie=180;
89 cerc=1;
90 sens=sens*(-1);
91 }
92 // situatia cand trecem prin 360 grade
93 // pe cercurile 1 si 3
94 if (cerc==1&&sens==1) pozitie=0;
95 if (cerc==3&&sens==1) pozitie=0;
96 break;
97 }
98 pozitie=pozitie+sens;
99 }
100
101 g<<pozitie%360<<" "<<cerc<<"\n";
102 f.close();
103 g.close();
104
105 return 0;
106 }
13.2 cmmmc
Problema 2 - cmmmc 100 de puncte
CAPITOLUL 13. ONI 2010 13.2. CMMMC 357
Cerinţe
Date de intrare
Prima linie a fişierului cmmmc.in conţine un număr natural k. Următoarele k linii din acest
fişier vor conţine câte un număr natural; linia i 1 va conţine numărul ni (i 1, 2, ..., k) .
Date de ieşire
Fişierul cmmmc.out va conţine k linii. Pe fiecare dintre acestea se vor afla trei numere. Cele
trei numere de pe linia i vor reprezenta:
- primul, numărul de perechi ordonate care au cel mai mic multiplu comun egal cu ni ;
- următoarele două, numerele care alcătuiesc perechea ordonată care are cel mai mic multiplu
comun egal cu ni şi a căror sumă este minimă, afişate ı̂n ordine crescătoare.
Exemple:
Observăm, că am numărat perechile formate din numere distincte de două ori şi perechea
n, n o singură dată, aşa că adăugăm unu şi ı̂mpărţim la doi, pentru a răspunde la subpunctul
a).
Pentru a rezolva subpunctul b), observăm că pentru a obţine suma minimă, trebuie să con-
siderăm fiecare pi la puterea qi ı̂ntr-unul din numere, iar la puterea 0 ı̂n celălalt număr. Încercăm
toate cele 2k perechi ce se pot forma astfel şi alegem perechea cu suma minimă.
Pentru a genera aceste perechi, iterăm cu o variabilă de la 0 la 2k 1, pe care o convertim ı̂n
baza doi. Dacă cifra i ı̂n baza doi este egal cu 0, vom considera că pi apare ı̂n primul număr la
puterea qi şi ı̂n al doilea număr la puterea 0, iar dacă cifra i este egal cu 1, pi va apărea ı̂n primul
număr la puterea 0 şi ı̂n al doilea număr la puterea qi .
Pentru a implementa un algoritm cât mai rapid pentru descompunerea ı̂n factori primi, se reco-
mandă generarea numerelor prime folosind Ciurul lui Eratostene. Această abordare implementat
cu grijă duce la obţinerea punctajului maxim.
O altă soluţie posibilă este determinarea tuturor divizorilor lui n şi verificarea pentru fiecare
pereche de divizori, dacă are cel mai mare divizor comun egal cu n. Pentru a determina divizorii
unui număr este de ajuns să verificăm divizorii până la radicalul numărului şi când am găsit
divizorul d, acesta va implica că am găsit şi divizorul n©d. Cazul pătratelor perfecte trebuie tratat
separat.
Această abordare obţine 60 de puncte.
121
122 return 0;
123 }
70 VB isPrime;
71 VI primes;
72 VLL compacted;
73
74 void Erast()
75 {
76 int gyok = sqrt(double(MAX));
77 isPrime.RS(gyok + 1, true);
78 FOR(i, 2, gyok >> 1)
79 if (isPrime[i])
80 for(int j=i+i; j<=gyok; j+=i) isPrime[j]=false;
81 FOR(i, 2, gyok)
82 if (isPrime[i]) primes.PB(i);
83 }
84
85 void BuildFactors()
86 {
87 factors.CL, compacted.CL;
88 LL aux = n;
89 int iPrime = 0, gyok = sqrt(double(n));
90 while (iPrime < primes.SZ && aux > 1)
91 {
92 int exponent = 0, prime = primes[iPrime];
93 if (prime > gyok) break;
94 while (!(aux % prime))
95 {
96 ++exponent;
97 aux /= prime;
98 }
99 if (exponent) factors.PB(MP(prime, exponent));
100 if (aux < isPrime.SZ && isPrime[aux]) break;
101 ++iPrime;
102 }
103 if (aux > 1) factors.PB(MP(aux, 1));
104 FORN(i, factors.SZ)
105 {
106 LL factor = factors[i].X;
107 FOR(j, 2, factors[i].Y) factor *= factors[i].X;
108 compacted.PB(factor);
109 }
110 }
111
112 PLL Solve()
113 {
114 nSol = 1;
115 FORN(i, factors.SZ) nSol *= (2*factors[i].Y + 1);
116 nSol = nSol / 2 + 1;
117 PLL ans;
118 LL minSum = (LL)MAX + MAX + 1;
119 FORN(i, 1 << factors.SZ)
120 {
121 LL x = 1, y = 1;
122 FORN(j, factors.SZ)
123 if (i & (1 << j)) x *= compacted[j];
124 else y *= compacted[j];
125 if (minSum > x + y)
126 {
127 minSum = x + y;
128 ans = MP(x, y);
129 }
130 }
131 if (ans.X > ans.Y) swap(ans.X, ans.Y);
132 return ans;
133 }
134
135 int main()
136 {
137 Erast();
138
139 fin >> test;
140 FORN(t, test)
141 {
142 fin >> n;
143 BuildFactors();
144 PII sol = Solve();
145 fout << nSol << " " << sol.X << " " << sol.Y << endl;
CAPITOLUL 13. ONI 2010 13.3. SIMETRIC 362
146 }
147 fin.close();
148 fout.close();
149
150 return 0;
151 }
13.3 simetric
Problema 3 - simetric 100 de puncte
O matrice pătratică A care are P linii şi P coloane este simetrică dacă şi numai dacă pentru
orice indici i şi j ı̂ntre 1 şi P avem că Ai,j Aj,i . Astfel, matricea din figura 1 este simetrică, iar
cea din figura 2 nu este, deoarece există cel puţin o pereche de indici (de exemplu i 2 şi j 3),
pentru care Ai,j este diferit de Aj,i .
Pentru o matrice dată cu M linii şi N coloane, definim submatricea de vârfuri l1 , c1 şi l2 , c2 ,
cu 1 & l1 & l2 & M şi 1 & c1 & c2 & N , ca fiind tabloul format din toate elementele de coordonate
i şi j astfel ı̂ncât l1 & i & l2 şi c1 & j & c2 .
Cerinţe
Se dă o matrice cu M linii şi N coloane ı̂n care toate elementele sunt numere naturale. Fie L
latura maximă a unei submatrici simetrice din această matrice. Pentru fiecare dimensiune i ı̂ntre
1 si L să se determine câte submatrici simetrice şi cu latura i ale matricii date există.
Date de intrare
Prima linie a fişierului simetric.in conţine numerele M şi N , separate de exact un spaţiu,
reprezentând numărul de linii, şi respectiv de coloane, ale matricii care se citeşte. Fiecare din
următoarele M linii conţine câte N numere naturale, despărţite de exact un spaţiu, reprezentând
elementele matricii.
Date de ieşire
Fişierul de ieşire simetric.out conţine exact L linii, unde L este latura maximă a unei sub-
matrici simetrice din matricea considerată. Linia i conţine numărul de submatrici simetrice de
latură i.
Exemple:
CAPITOLUL 13. ONI 2010 13.3. SIMETRIC 363
2 #include<string.h>
3 #define maxn 512
4
5 int N, M, V[ maxn ][ maxn ], nr[ maxn];
6
7 int simetric( int x, int y, int l)
8 {
9
10 for( int i = x; i <= x + l - 1; i++)
11 for( int j = y ; j <= y + l - 1; j++)
12 if( V[i][j] != V[ x + j - y ][ y + i - x ] )
13 return 0;
14 return 1;
15 }
16
17 int main()
18 {
19 freopen("simetric.in","r",stdin);
20 freopen("simetric.out","w",stdout);
21
22 memset( nr, 0, sizeof( nr ));
23
24 scanf("%d %d", &N, &M);
25
26 for( int i = 1; i <= N; ++i)
27 for( int j = 1; j <= M; ++j)
28 scanf("%d", &V[ i ][ j ]);
29 int optim = 1;
30 for( int i = 1; i <= N; ++i)
31 for( int j = 1; j <= M; ++j)
32 for( int l = 1; l + i - 1 <= N && l + j - 1 <= M; ++l)
33 if( simetric( i, j, l) )
34 {nr[ l ]++; if( l > optim) optim = l;}
35 else break;
36 for( int i = 1; i <= optim; i++)
37 printf("%d\n", nr[i]);
38
39 return 0;
40 }
34
35 nr[ l ]++;
36 if( l > optim )
37 optim = l;
38 }
39 }
40
41 for( int i = 1; i <= optim; i++)
42 printf("%d\n", nr[i]);
43
44 return 0;
45 }
13.4 pesti
Problema 4 - pesti 100 de puncte
Nicuşor trebuie să aibă grijă, pe perioada vacanţei, de cei n peşti aflaţi ı̂n acvariile de la Muzeul
de ştiinţe ale Naturii din Constanţa. Peştii sunt numerotaţi cu numerele distincte de la 1 la n şi
sunt asezaţi ı̂n n acvarii identice, câte un peştişor ı̂n câte un acvariu. Iniţial, peştişorul numerotat
cu numărul 1 stă ı̂n acvariul etichetat cu numărul 1, peştişorul numerotat cu numărul 2 stă ı̂n
acvariul etichetat cu numărul 2, ..., peştişorul numerotat cu numărul n stă ı̂n acvariul etichetat
cu numărul n. Cele n acvarii sunt aşezate unul lângă altul, ı̂n ordinea crescătoare a etichetelor.
Cele n acvarii formează o grupă.
Pentru ca peştii să se dezvolte frumos şi să nu se plictisească, ei trebuie reaşezaţi zilnic ı̂n
acvarii.
CAPITOLUL 13. ONI 2010 13.4. PESTI 366
Cerinţe
Scrieţi un program care să citească două numere naturale nenule n şi x, n reprezentând numărul
de peştişori şi x reprezentând numărul unui peştişor, şi care să determine:
- numărul z de zile ı̂n care Nicuşor ı̂şi desfăşoară activitatea;
- eticheta y a acvariului ı̂n care se găseşte peştişorul cu numărul x la ı̂ncheierea activităţii lui
Nicuşor;
- prima zi, u, ı̂n care ı̂n peştişorul cu numărul x a ajuns ı̂n acvariul etichetat cu numărul y şi
nu a mai fost mutat.
Date de intrare
Fişierul de intrare pesti.in conţine o singură linie pe care sunt scrise cele două numere naturale
n şi x, separate printr-un spaţiu.
Date de ieşire
Fişierul de ieşire pesti.out conţine o singură linie pe care sunt scrise cele trei numere naturale
z, y şi u (ı̂n această ordine), separate prin câte un spaţiu.
Exemple:
pesti.in pesti.out
Explicaţii
96 372 Nicuşor ı̂şi desfăşoară activitatea timp de z 3 zile. Peştişorul cu
numărul x 6 se va afla ı̂n ziua a treia ı̂n acvariul cu numărul y 7
şi ajunge ı̂n acest acvariu ı̂n ziua u 2.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 64 MB din care pentru stivă 32 MB
Dimensiune maximă a sursei: 10 KB
CAPITOLUL 13. ONI 2010 13.4. PESTI 367
Cerinţa a) numărul z de zile până la ı̂ncheierea activităţii desfăţurată de Nicuşor este cel mai
mare număr natural care verifică inecuaţia 2 $ n, v ' 3.
z
Cerinţa b) eticheta y a acvariului ı̂n care se găseşte, ı̂n ziua z, peştişorul cu numărul x
Pentru a determina poziţia finală a peştişorului cu numărul x, trebuie să reţinem la fiecare pas
ı̂n ce grupă şi ı̂n ce acvariu se află.
Dacă, la un moment dat peştele se află ı̂n acvariul x ı̂n cadrul unei grupe ce conţine acvariile
etichetate cu a, a 1, ..., b, pe o poziţie pară ı̂n cadrul grupei, atunci el va ajunge ı̂n acvariul
a b©2 x a 1©2 din subgrupa din dreapta, iar dacă se află pe o poziţie impară ı̂n cadrul
grupei, atunci el va ajunge ı̂n acvariul a x a©2 din subgrupa din stânga.
Cerinţa c) prima zi, u, ı̂n care ı̂n peştişorul cu numărul x a ajuns ı̂n acvariul etichetat cu
numărul y şi nu a mai fost mutat
Este suficient să utilizăm o singură variabilă pe care să o actualizăm de fiecare dată când
poziţia curentă a peştelui este diferită de poziţia anterioară.
Această abordare rulează ı̂n timp O log n şi foloseşte memorie O 1.
Aproximativ 60-70% din punctaj se poate obţine şi prin simularea efectivă a mişcării peştilor.
Observăm că, pentru a genera poziţia peştilor dintr-o zi, avem nevoie doar de poziţia acestora din
ziua anterioară, şi mărimea grupelor din ziua anterioară. Această observaţie duce la ideea de a
reţine două matrici de câte două linii a şi groupSize, ı̂n care o linie va codifica starea pentru ziua
curentă, iar cealaltă pentru ziua anterioară. Menţionăm că, numărul de elemente dintr-o linie a
matricii groupSize se modifică dinamic de la o zi la alta, ı̂n schimb o linie din matricea a are
ı̂ntotdeauna n elemente. Această abordare rulează ı̂n timp O n log n şi foloseşte memorie O n.
Dacă se reţin stările pentru fiecare din zile, memoria folosită se modifică la O n log n şi se pot
obţine 40-50 puncte.
32 else
33 {
34 c=(a+b)/2; //grupa din dreapta
35 x=c+(x-a+1)/2;
36 a=c+1;
37 }
38
39 u++;
40 if(x!=p) zi=u;
41 // g<<" in ziua "<<u<<" pozitia "<<x<<’\n’;
42 } while (b-a>1);
43
44 //g<<" la final "<<z<<’ ’<<x<<’ ’<<zi<<’\n’;
45 g<<z<<’ ’<<x<<’ ’<<zi<<’\n’;
46 f.close();
47 g.close();
48 return 0;
49 }
13.5 plaja
Problema 5 - plaja 100 de puncte
Primăria oraşului Constanţa reamenajează plaja din staţiunea Mamaia. Aceasta este reprezen-
tată ca o zonă dreptunghiulară cu lăţimea de a unităţi şi lungimea de b unităţi. Pe plajă sunt
trasate linii paralele cu laturile dreptunghiului astfel ı̂ncât să formeze pătrate cu latura de o
unitate, numite zone.
Pe plajă se vor pune obiecte: umbrele şi prosoape. Se consideră că dacă un obiect intră ı̂n
interiorul unei zone, o ocupă ı̂n ı̂ntregime. Se poziţionează u umbrele de soare. ı̂ntr-o zonă se
poate aşeza cel mult o umbrelă.
N turişti vin şi ı̂şi aşează prosoapele pe plajă. Un prosop are formă dreptunghiulară şi va
fi aşezat paralel cu laturile dreptunghiului. Turiştii ı̂şi pot aşeza prosoapele pe zone libere sau
peste prosoape deja aşezate. Un turist nu ı̂şi poate aşeza ı̂nsă prosopul pe plajă dacă suprafaţa
acoperită de acesta include cel puţin o zonă ı̂n care se află o umbrelă.
M localnici au suprafeţe favorite pentru aşezarea prosoapelor. O suprafaţă favorită are forma
unui dreptunghi cu laturile paralele cu laturile dreptunghiului care marchează plaja. După ce
turiştii termină aşezarea prosoapelor, localnicii verifică dacă zonele din suprafaţa favorită sunt
libere (neacoperite de prosoape aşezate de turişti sau de umbrele).
Cerinţe
Date de intrare
Fişierul de intrare plaja.in conţine pe prima linie trei numere naturale, separate prin câte un
spaţiu, a, b şi u, având semnificaţia din enunţ.
Fiecare din următoarele u linii conţine o pereche de numere naturale x y, reprezentând o zonă
ı̂n care se găseşte o umbrelă.
Următoarea linie din fişier conţine un număr natural N , reprezentând numărul de turişti.
Următoarele N linii descriu prosoapele turiştilor. Fiecare linie conţine 4 numere naturale x1
y1 x2 y2 , ce reprezintă colţurile unui prosop. Linia următoare conţine o singură valoare, M ,
reprezentând numărul de localnici.
¬ ¬ ¬ ¬
Pe următoarele M linii se află câte 4 numere, separate prin câte un spaţiu, x1 y1 x2 y2 , ce
reprezintă colţurile unei suprafeţe favorite.
Date de ieşire
CAPITOLUL 13. ONI 2010 13.5. PLAJA 369
Fişierul de ieşire plaja.out conţine pe prima linie două numere naturale separate printr-un
spaţiu. Primul număr reprezintă numărul de turişti care şi-au aşezat prosoapele pe plajă, iar cel
de-al doilea număr reprezintă numărul de localnici ale căror zone favorite sunt libere.
Exemple:
Prima cerinţă cere determinarea numărului de dreptunghiuri care nu conţin ı̂n interiorul lor
nicio zonă ı̂n care se găseşte o umbrelă.
Rezolvarea pentru acest pas are complexitate O N U , testând pentru fiecare dreptunghi din
cele N dacă există o umbrelă pe care să o includă. Testarea se va face parcurgând efectiv lista de
umbrele. Această abordare garantează obţinerea a 40 de puncte.
Pentru a rezolva eficient şi cea de-a doua cerinţă, procedăm
ı̂n felul următor: atunci când avem un dreptunghi de coordonate
x1 y1 x2 y2 pe care trebuie să ı̂l aşezăm pe plajă, incrementăm cu
1 valoarea din celulele x1 , y1 si x2 1, y2 1 şi scădem cu 1
valoarea din celulele x1 , y2 1 şi x2 1, y1 , după cum se observă
ı̂n desenul alăturat.
Atunci când facem suma elementelor pe orice submatrice de
colţuri 1, 1 şi i, j , observăm că se va aduna o unitate ı̂n orice
celulă care aparţine dreptunghiului considerat. Astfel, de fiecare dată când avem un dreptunghi
care trebuie aşezat, adunăm sau scădem 1 ı̂n celulele menţionate. După amplasarea tuturor
celor N dreptunghiuri, calculăm suma Si,j pe orice submatrice de colţuri 1, 1 şi i, j , folosind
următoarea relaţie:
CAPITOLUL 13. ONI 2010 13.5. PLAJA 370
Si,j Si1,j Si,j 1 Si1,j 1 Vi,j , unde Vi,j este valoarea de pe poziţia i, j .
Dacă Si,j % 0, atunci i, j este acoperit de cel puţin un prosop. Facem din nou sume parţiale
pe această matrice nou obţinută, ı̂n care considerăm Si,j 1 dacă şi numai dacă i, j este acoperit
¬ ¬ ¬ ¬
de cel puţin un prosop. Pentru a testa ı̂n O 1 dacă o suprafaţă x1 , y1 , x2 , y2 din cele M este
ı̂n ı̂ntregime liberă, calculăm suma Sx¬2 ,y2¬ Sx¬2 ,y1¬ 1 Sx¬1 1,y2¬ Sx¬1 1,y1¬ 1 . Dacă această sumă
este 0, ı̂nseamnă că suprafaţa este liberă, iar ı̂n caz contrar va exista cel puţin o zonă ı̂n ı̂nteriorul
suprafeţei care este acoperită.
Complexitatea finală este O N U A B M N .
13.6 tango
Problema 6 - tango 100 de puncte
Un tango este format din fraze muzicale, fiecare dintre acestea având 8 timpi muzicali. Timpii
muzicali au aceeaşi durată.
La fel de importantă ca melodia unui tango este şi dansul asociat ei. Mişcările efectuate ı̂n
timpul dansului se numesc figuri. Succesiunea de figuri efectuate ı̂n timpul dansului formează o
coregrafie. Două coregrafii se consideră diferite dacă succesiunea figurilor care le alcătuiesc este
diferită. O coregrafie frumoasă asociată unui tango are particularitatea următoare: atunci când
se termină o frază muzicală trebuie să se termine şi o figură.
D şi S se pregătesc pentru primul lor concurs de dans şi ei lucreaza momentan la coregrafia
de tango. Chiar dacă va fi primul lor concurs, ei deja ştiu n figuri de dans şi au calculat pentru
fiecare dintre aceste figuri câţi timpi muzicali durează. Fiindcă le place foarte mult să danseze
ı̂mpreună, ei vor să pregătească o coregrafie frumoasă pentru o piesă care durează exact k timpi
muzicali.
Cerinţe
Determinaţi numărul coregrafiilor frumoase modulo 999983 pentru o piesă, care: dureaza exact
k timpi muzicali, respectă condiţiile de mai sus şi sunt formate doar din cele n figuri cunoscute
de D şi S (mai este prea puţin timp până la concurs, ca ei să inveţe şi figuri noi).
Date de intrare
Pe prima linie a fişierului de intrare tango.in se află numerele naturale nenule n şi k, separate
printr-un singur spaţiu. Pe a doua linie se află exact n numere separate prin câte un spaţiu,
reprezintând lungimile figurilor.
Date de ieşire
În fişierul de ieşire tango.out se va afişa numărul de coregrafii posibile modulo 999983.
Restricţii şi precizări
a n & 100 000
a k & 2 000 000 000
a k va fi ı̂ntotdeauna divizibil cu 8
a 1 & lungimea unei figuri & 8
a pentru 30% din teste va exista o singură figură de o anumită lungime
a pentru 50% din teste n & 30
a pentru 70% din teste lungimile figurilor vor fi numai valori din mulţimea {2, 4, 6, 8}
a Prin a modulo b se ı̂nţelege restul ı̂mpărţirii lui a la b.
Exemple:
tango.in tango.out Explicaţii
3 16 66049 Sunt 16 timpi muzicali deci o coregrafie frumoasă se va dansa pe
118 16 / 8 = 2 fraze muzicale.
Dacă notăm figurile cu litere, avem figura A de lungime 1, figura B
de lungime 1 şi figura C de lungime 8. Prima frază muzicală poate
fi alcătuită din orice secvenţă alcătuită din opt bucăţi de A sau B,
deci ı̂n total 28 = 256 posibilităţi. ı̂ncă o posibilitate de alcătuire
a primei fraze este printr-un singur C. Rezultă un total de 257
posibilităţi. Pentru a doua frază avem tot atâtea posibilităţi, deci
ı̂n total există 257 * 257 = 66049 coregrafii frumoase posibile.
Cum 66049 modulo 999983 = 66049, se obţine rezultatul 66049.
Primul pas va fi să calculăm numărul de posibilităţi pentru a dansa figuri care acoperă exact
o frază muzicală. Tratăm cazul ı̂n care figurile au lungime pară şi acestea sunt unice (acest caz
apare ı̂n 20% din teste). Vom ı̂ncerca să descompunem o frază ı̂n toate modurile posibile ı̂n figuri.
Avem doar următoarele posibilităţi:
1)8=2+2+2+2
2)8=2+2+4
3)8=2+4+2
4)8=2+6
5)8=4+2+2
6)8=4+4
7)8=6+2
8)8=8
În funcţie de ce figuri avem disponibile, o parte din aceste posibilităţi pot dispărea. De exemplu
dacă avem doar 2 şi 6 atunci putem forma doar variantele 1), 4) şi 7).
Tratăm acum cazul ı̂n care avem doar timpi pari, dar nu sunt unici. Să exemplificăm că avem
2 figuri de lungime 2 şi 3 de lungime 6. Atunci pentru cazul 1) vor fi 24 moduri de a fi dansat
(pentru fiecare figură de lungime 2 avem 2 posibilităţi), iar pentru cazurile 4) şi 7) vor fi 6 moduri
de a fi dansate (2 * 3).
Să extindem la cazul ı̂n care figurile au şi timpi impari. ı̂n acest caz sunt mai multe moduri
de a descompune o frază ı̂n figuri:
8=1+1+1+1+1+1+1+1
8=1+1+1+1+1+1+2+0
...........................
8 = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8
Cum aceste cazuri sunt prea multe pentru a fi calculate de mână, se pot folosi 8 foruri imbricate
pentru a selecta valorile pentru x1, x2, ..., x8 (de la 0 la 8) astfel ı̂ncât x1 + x2 .... + x8 = 8.
Pentru a nu număra o posibilitate de mai multe ori, trebuie avut grijă să nu generăm cazuri de
forma 8 = 1 + 2 + 0 + 2 + 3 + 0 + 0, ci doar cazuri ı̂n care 0-urile sunt la coadă (vezi soluţia
oficială). Alte metode mai elegante pentru a genera numărul de posibilităţi sunt programarea
dinamică specifică problemei rucsacului, sau metoda backtracking.
Al doilea pas este să calculăm numărul de moduri de a dansa ı̂ntreg tangoul.
Pentru a afla această valoare, trebuie să calculăm numărul de posibilităţi de a dansa o frază
muzicală la puterea (k / 8). Pentru a obţine punctaj maxim, acest calcul trebuie făcut folosind
exponenţiere logaritmică.
95 }
ONI 2009
14.1 joc
Problema 1 - joc 100 de puncte
Pentru un concurs de design de jocuri, Gigel vrea să construiască un joc. La joc participă n
concurenţi numerotaţi de la 1 la n. Fiecare concurent are la dispoziţie câte un şir de m ı̂ncăperi,
numerotate de la 1 la m. Scopul jocului este de a găsi o comoară ascunsă ı̂n una din aceste
ı̂ncăperi. Fiecare ı̂ncăpere conţine un cod, număr natural, fie egal cu 0, fie având cel puţin 2 cifre.
Ultima cifră indică numărul de etape de penalizare, adică numărul de etape ı̂n care concurentul nu
are voie să părăsească ı̂ncăperea. Numărul obţinut prin eliminarea ultimei cifre a codului indică
numărul ı̂ncăperii ı̂n care se va deplasa acesta la următoarea etapă sau la expirarea penalizării.
Există două excepţii de la regula de definire a codului: numărul 9999 codifică o ı̂ncăpere conţinând
o comoară, iar numărul 0 o ı̂ncăpere conţinând o capcană.
În etapa 1 fiecare jucător intră ı̂n ı̂ncăperea 1 din şirul său de ı̂ncăperi. În funcţie de codul
găsit ı̂n ı̂ncăpere sunt posibile următoarele situaţii:
- codul găsit este 9999 ceea ce ı̂nseamnă că jucătorul este câştigător şi jocul se ı̂ncheie la finalul
acestei etape;
- codul găsit este 0 ceea ce duce la eliminarea sa din joc;
- pentru celelalte coduri, după efectuarea etapelor de penalizare, jucătorul efectuează o de-
plasare ı̂n ı̂ncăperea indicată de cod. De exemplu la ı̂ntâlnirea codul 157, după efectuarea celor 7
etape de penalizare jucătorul se va deplasa ı̂n camera 15.
Trecerea de la o etapă la alta se realizează simultan pentru toţi concurenţii.
Cerinţe
Fiind dat numărul n de concurenţi, numărul m de ı̂ncăperi alocate fiecărui concurent, şi co-
durile din cele n m ı̂ncăperi să se determine câştigătorul jocului, numărul ı̂ncăperii ı̂n care a găsit
comoara, numărul de etape parcurse până când câştigătorul găseşte comoara precum şi numărul
de concurenţi eliminaţi din joc până la etapa respectivă (inclusiv).
Date de intrare
Prima linie a fişierului de intrare joc.in conţine două numere naturale n şi m, separate printr-
un spaţiu, reprezentând numărul concurenţilor, respectiv numărul ı̂ncăperilor.
Următoarele n linii conţin câte m numere naturale, separate prin câte un spaţiu, reprezentând
codurile din fiecare ı̂ncăpere.
Date de ieşire
Prima linie a fişierului de ieşire joc.out va conţine patru numere naturale separate prin câte
un spaţiu, reprezentând indicele câştigătorului, numărul ı̂ncăperii unde a găsit comoara, numărul
etapei ı̂n care a câştigat şi respectiv numărul de concurenţi eliminaţi din joc.
375
CAPITOLUL 14. ONI 2009 14.1. JOC 376
Exemple:
joc.in joc.out
Explicaţii
48 2571
Câştigă jucătorul al 2-lea, după 7 etape, iar ı̂ncăperea
0 9999 41 50 61 70 80 30 ı̂n care a găsit comoara este ı̂ncăperea 5. ı̂n cele 7
30 80 60 60 9999 21 40 50 etape a fost eliminat un singur concurent şi anume
20 30 40 50 61 71 81 9999 primul concurent.
20 30 50 0 61 71 9999 41 Încăperile prin care trece jucătorul câştigător până
la final sunt:
1 3 6 2 8 5
Timp maxim de executare/test: 0.2 secunde
Memorie: total 2 MB din care pentru stivă 1 MB
Se citesc din fişier numerele n (numărul de jucători) şi m (numărul de ı̂ncăperi pentru fiecare
concurent). Pentru fiecare jucător i (1 & i & n) se execută următoarea secvenţă de paşi:
- se citeşte ı̂n vectorul a şirul său de ı̂ncăperi.
- se ı̂ncepe de la ı̂ncăperea 1 (a1) şi etapa 1
- pentru fiecare ı̂ncăpere la care a ajuns, se marchează ı̂ncăperea ca fiind vizitată, iar valoarea
din ı̂ncăpere poate fi:
` 0 - jucătorul este eliminat - se reţine ı̂ntr-un vector, pe poziţia i numărul etapei ı̂n care
a fost eliminat; se ı̂ncheie analiza şirului său de ı̂ncăperi
` 9999 - jucătorul a câştigat; se compară numărul etapei cu un minim şi se retine valoarea
minimă, indicele jucătorului şi numărul ı̂ncăperii ı̂n care a ajuns; se ı̂ncheie analiza
şirului său de ı̂ncăperi
` o valoare nenulă şi ¡ 9999; se calculează indicele j al ı̂ncăperii ı̂n care urmează să se
catapulteze; sunt posibile două cazuri:
t dacă acest indice este egal cu ı̂ncăperea ı̂n care a ajuns sau ı̂ncăperea ı̂n care
urmează să ajungă a mai fost vizitată, se ı̂ncheie analiza şirului de ı̂ncăperi,
deoarece jucătorul intră ı̂ntr-un ciclu infinit de mutări.
t indicele reprezintă o ı̂ncăpere nevizitată, se măreşte numărul etapei cu ultima cifră
a valorii codului şi se reia vizitarea cu ı̂ncăperea j.
După executarea secvenţei descrise pentru toţi jucătorii, se numără câţi jucători au fost pı̂nă
ı̂n etapa ı̂n care a fost stabilit câştigătorul (minimul calculat), inclusiv acea etapa şi se scriu ı̂n
fişier numerele cerute.
int a[1001],n,m,t[401],s[401],v[401];
ifstream f("joc.in");
ofstream g("joc.out");
int main()
{
int i,j,x,castigator=0,nrp=0,viz[1001],l[1001];
long etapa,e_min=320000000,c_min,i_min;
f>>n>>m;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
f>>a[j];
viz[j]=0;
CAPITOLUL 14. ONI 2009 14.2. PERSPIC 377
etapa=1;
j=1;
viz[1]=1;
castigator=0;
while(!castigator&&t[i]!=-1)
{
x=a[j];
viz[j]=1;
if(x==0)
{
t[i]=-1;
v[i]=etapa;
}
else
if(x==9999)
{
if(etapa<e_min)
{
c_min=i;
e_min=etapa;
i_min=j;
}
castigator=1;
}
else
{
if(j==x/10) break;
j=x/10;
if(viz[j]==1)
break;
else
etapa=etapa+x%10+1;
}
}
}
nrp=0;
for(i=1;i<=n;i++)
if(t[i]==-1&&v[i]<=e_min)
nrp++;
g<<c_min<<" "<<i_min<<" "<<e_min<<" "<<nrp<<endl;
f.close();
g.close();
return 0;
}
14.2 perspic
Problema 2 - perspic 100 de puncte
Se consideră o matrice pătratică cu N linii şi N coloane ce conţine toate numerele naturale de
la 1 la N N . Asupra matricei se definesc trei tipuri de operaţii codificate astfel:
a C i j - interschimbarea coloanelor i şi j ale matricei
a R i j - interschimbarea liniilor i şi j ale matricei
a E i j x y - interschimbarea elementului de pe linia i şi coloana j cu elementul de pe linia x
şi coloana y.
Asupra matricei se efectuează un set de M astfel de operaţii.
Cerinţe
Se cere să se determine numărul minim de aplicări complete ale acestui set de operaţii după
care se ajunge din nou ı̂n starea iniţială. În cadrul setului operaţiile se efectuează mereu ı̂n aceeaşi
ordine şi nu se poate sări peste o operaţie. Deoarece numărul acesta poate fi foarte mare se cere
restul ı̂mpărţirii sale la 13007.
CAPITOLUL 14. ONI 2009 14.2. PERSPIC 378
Date de intrare
Fişierul perspic.in conţine pe prima linie numerele naturale N şi M , separate printr-un spaţiu,
reprezentând dimensiunea matricei şi respectiv numărul de operaţii dintr-un set. Pe următoarele
M linii se descriu operaţiile setului.
Date de ieşire
Exemple:
La un moment dat se va ajunge ca P aplicat de k ori lui i să dea din nou i. Practic, acest număr
k va reprezenta numărul minim de aplicări ale setului după care i se va afla din nou ı̂n poziţia
iniţială. Pentru orice multiplu al lui k acest lucru va fi de asemenea adevarat.
CAPITOLUL 14. ONI 2009 14.2. PERSPIC 379
Vom avea
H i - numărul minim de aplicări ale setului de operaţii pentru care i să se afle din nou ı̂n
poziţia iniţială.
Pentru că vrem ca toate numerele i să ajungă din nou ı̂n poziţiile lor iniţiale vom căuta un
multiplu comun al tuturor valorilor H i, deci trebuie să calculăm
unde cmmdc a, b se calculează cu algorimul lui Euclid. Acest calcul va trece doar 60% din teste
deoarece rezultatul poate deveni foarte mare.
În general se pot parcurge toate numerele prime de la 1 la N N şi, pentru fiecare număr prim
p, se determină care este puterea cea mai mare e astfel ı̂ncât să existe un i pentru care H i sa
e e
fie divizibil cu p . Soluţia se va ı̂nmulţi cu p , obţinând ı̂n final cmmmc dorit.
void dump()
{
int i, j;
printf("%d %d\n", n, m);
for (i = 0; i < n; ++i)
{
for (j = 0; j < n; ++j)
printf(" %d", A[pos(i, j)]);
printf("\n");
}
}
void read_eval()
{
int i, j, z, a, b, c, d, aux;
char op[10];
scanf("%d%d", &n, &m);
break;
case ’E’:
scanf("%d%d", &c, &d);
--c, --d;
sw(pos(a, b), pos(c, d));
break;
default:
//printf("ERROR: %s\n", op);
break;
}
}
}
void cycles()
{
int i, len, j, d, pow, x;
if (len)
{
// printf("<<< %d\n", len);
for (x=len, d=0; x>1 && P[d]*P[d] <= x && d < np; ++d)
{
// printf("e: %d, %d, %d\n", d, P[d], x);
for (pow = 0; x % P[d] == 0; ++pow)
x /= P[d];
if (E[P[d]] < pow)
E[P[d]] = pow;
}
void gen_primes()
{
int a, j, i;
P[np++] = 2;
for (a = 3; a < n * n; a += 2)
{
char prim = 1;
for (j = 0; prim && (P[j] * P[j] <= a) && j < np; ++j)
prim = ((a % P[j]) != 0);
if (prim)
P[np++] = a;
}
}
void dump_exp()
{
int i;
printf("exp:\n");
for (i = 0; i < n * n; ++i)
if (E[P[i]])
printf(" %dˆ%d", P[i], E[P[i]]);
printf("\n");
}
void dump_primes()
{
int i;
CAPITOLUL 14. ONI 2009 14.3. RAFTURI 381
printf("primes:\n");
for (i = 0; i < np; ++i)
printf(" %d", P[i]);
printf("\n");
}
int cmmmc()
{
int i, res, j;
for (res = 1, i = 1; i < n * n; ++i)
for (j = 0; j < E[i]; ++j)
res = (((long long)res * (i % MOD)) % MOD);
return res;
}
int main(void)
{
freopen("perspic.in", "rt", stdin);
freopen("perspic.out", "wt", stdout);
read_eval();
// dump();
gen_primes();
// dump_primes();
cycles();
printf("%d\n", cmmmc());
return 0;
}
14.3 rafturi
Problema 3 - rafturi 100 de puncte
Într-o bibliotecă se află C dulapuri identice aşezate unul lângă altul pe peretele unei ı̂ncăperi,
dulapurile fiind numerotate de la stânga spre dreapta cu numerele naturale de la 1 la C. Fiecare
dulap conţine 1000 de rafturi, situate vertical unul deasupra altuia, rafturile fiecărui dulap fiind
numerotate de la 1 la 1000 de jos ı̂n sus.
Fiecare dulap este prevăzut cu o scară cu care se poate ajunge la orice raft. Dacă bibliotecara
urcă scara unui anumit dulap D până la un anumit nivel k, ea va putea aduna orice carte de pe
rafturile 1 până la k inclusiv, din dulapul D şi din dulapurile ı̂nvecinate (dulapul D 1 şi dulapul
D 1).
Cunoscând dulapurile şi rafturile de unde trebuie luate cărţi, bibliotecara doreşte să adune
toate cărţile cerute, dar suma ı̂nălţimilor până la care trebuie să urce să fie minimă.
Cerinţe
Scrieţi un program care să determine suma minimă a ı̂nălţimilor până la care trebuie să urce
bibliotecara pentru a aduna toate cărţile cerute.
Date de intrare
Prima linie a fişierului de intrare rafturi.in conţine două numere naturale C şi N , separate
printr-un spaţiu, reprezentând numărul dulapurilor şi respectiv numărul cărţilor pe care bibliote-
cara trebuie să le adune.
Următoarele N linii conţin câte două numere naturale a b, separate printr-un spaţiu,
reprezentând numărul dulapului, respectiv numărul raftului de unde trebuie luată o carte.
Date de ieşire
Fişierul de ieşire rafturi.out va conţine un singur număr natural reprezentând suma minimă
a ı̂nălţimilor până la care trebuie să urce bibliotecara pentru a aduna toate cărţile cerute.
CAPITOLUL 14. ONI 2009 14.3. RAFTURI 382
Exemple:
La citirea datelor reţinem ı̂n vectorul carti, pentru fiecare dulap, ı̂nălţimea cea mai mare de
pe care trebuie luată cartea.
Construim un vector inaltimi ı̂n care la fiecare pas vom calcula suma minimă a ı̂nălţimilor
rafturilor de pe care sunt coborı̂te cărţile până ı̂n acel moment.
Aceasta se obţine astfel:
inaltimi[0]=0
inaltimi[1]=carti[1]
inaltimi[2]=carti[2]
int C;
long N;
int carti[MAXN+1];
long inaltime[MAXN+1];
printf("\n");
}
void citire(void)
CAPITOLUL 14. ONI 2009 14.4. BR 383
{
FILE *f;
int x,y;
long i;
f=fopen("rafturi.in","r");
fscanf(f,"%d%ld",&C,&N);
for (i=1;i<=N;i++)
{
fscanf(f,"%d%d",&y,&x);
if (carti[y]<x) carti[y]=x;
}
carti[0]=0;
// afisare(carti);
fclose(f);
}
void calcul(void)
{
long h;
int i,j;
inaltime[0]=0;
for (i=1;i<=C;i++)
{
inaltime[i]=MAXV+1;
h=carti[i];
if (inaltime[i-1]+h<inaltime[i])
inaltime[i]=inaltime[i-1]+h;
for (j=i-1; j>i-3 && j>0 ;j--)
{
if (h<carti[j])
h=carti[j];
if (inaltime[j-1]+h<inaltime[i])
inaltime[i]=inaltime[j-1]+h;
}
}
}
void afisare(void)
{
FILE *g;
g=fopen("rafturi.out","w");
fprintf(g,"%ld\n",inaltime[C]);
//printf("%ld\n",inaltime[C]);
fclose(g);
}
int main()
{
citire();
calcul();
afisare();
return 0;
}
14.4 br
Problema 4 - br 100 de puncte
N prieteni, numerotaţi de la 1 la N , beau bere fără alcool la o masă rotundă. Pentru fiecare
prieten i se cunoaşte Ci - costul berii lui preferate. Din când ı̂n când, câte un prieten, fie el k,
cumpără câte o bere pentru o secvenţă de prieteni aflaţi pe poziţii consecutive la masă, inclusiv
lui, ı̂n sensul acelor de ceasornic. El este dispus să cheltuiască x bani şi doreşte să facă cinste la
un număr maxim posibil de prieteni.
Cerinţe
CAPITOLUL 14. ONI 2009 14.4. BR 384
Se cere numărul de beri pe care le va cumpăra fiecare prieten k ı̂n limita sumei x de bani de
care dispune. ı̂n caz că x este mai mare decât costul berilor pentru toţi prietenii de la masă, se
vor achiziţiona maxim N beri.
Date de intrare
Prima linie a fişierului de intrare br.in conţine două numere naturale N şi T separate printr-un
spaţiu reprezentând numărul de prieteni şi respectiv numărul de prieteni care doresc să facă cinste
prietenilor săi.
A doua linie a fişierului de intrare conţine N numere naturale C1 , C2 ..., CN , separate prin câte
un spaţiu, reprezentând costurile berilor preferate de fiecare prieten. Berea pentru prietenul i are
costul Ci .
Fiecare din următoarele T linii conţine câte două numere separate printr-un spaţiu:
k1 x1
k2 x2
...
kT xT
precizând indicele câte unui prieten care face cinste şi respectiv suma de bani de care acesta
dispune.
Date de ieşire
Fişierul de ieşire br.out va conţine T linii, fiecare cu un singur număr Di reprezentând numărul
de beri pe care le va cumpăra prietenul ki cu suma de bani xi in condiţiile problemei.
Exemple:
br.in br.outExplicaţii
54 3 Prietenul 1 cumpără câte o bere pentru el şi pentru prietenii 2, 3.
10 5 15 22 13 4 Costul celor 3 beri este 30.
1 32 0 Prietenul 4 cumpără 4 beri: câte una pentru el şi pentru prietenii
4 50 5 5, 1, 2. Costul celor 4 beri este 50.
19 Cu 9 bani prietenul 1 nu poate cumpăra nici măcar berea lui.
4 200 Prietenul 4 cumpără 5 beri. Costul celor 5 beri este sub limita de
cost 200.
Timp maxim de executare/test: 0.1 secunde
Memorie: total 16 MB din care pentru stivă 1 MB
int n,m,k,x,sum[nmax];
int main()
{
freopen("br.in","r",stdin);
freopen("br.out","w",stdout);
int i,j,ii,rez;
scanf("%d %d",&n,&m);
FOR(i,1,n+1)
{
scanf("%d",&sum[i]);
sum[i]+=sum[i-1];
}
FOR(ii,0,m)
{
scanf("%d %d",&k,&x);
rez = 0;
if(sum[n]-sum[k-1] <=x)
{
rez += n-k+1;
x -= sum[n] - sum[k-1];
k = 0;
}
else
k--;
rez += n * (x / sum[n]);
x %= sum[n];
for(j=k,i=21;i>=0;--i)
if(pt(i)+j <= n && sum[pt(i)+j] - sum[k] <=x)
j += pt(i);
rez += j - k;
if(rez > n) rez = n;
printf("%d\n",rez);
}
return 0;
}
14.5 origami
Problema 5 - origami 100 de puncte
Costel este pasionat de arta orientală a confecţionării obiectelor de hârtie, origami, dar este
abia la ı̂nceput şi trebuie să se familiarizeze cu operaţiile de ı̂ndoire corectă a hârtiei. El are la
dispoziţie o foaie de hârtie pătrată, ruptă dintr-un caiet de matematică, având dimensiunea de
exact N N pătrăţele. ı̂ndoiturile trebuie realizate exact pe o linie orizontală sau verticală.
Sunt permise două tipuri de ı̂ndoituri:
- ı̂ndoitura de tipul 1, ı̂ndoitură verticală executată la X pătrăţele faţă de marginea stângă
a foii: partea din stânga a foii se pliază către dreapta, de-a lungul liniei verticale aflate la
distanţa de X pătrăţele faţă de marginea stângă;
- ı̂ndoitura de tipul 2, ı̂ndoitură orizontală executată la X pătrăţele faţă de marginea supe-
rioară a foii: partea de sus a foii se pliază ı̂n jos, de-a lungul liniei aflate la distanţa de X
pătrăţele faţă de marginea de sus a hârtiei.
În urma realizării unei succesiuni de ı̂ndoituri, din foaia iniţială de hârtie se va obţine un obiect,
care va avea o formă dreptunghiulară, cu ı̂nălţimea H, lăţimea M şi având grosimea egală cu
numărul maxim de foi care se suprapun ı̂n cadrul obiectului obţinut.
Cerinţe
Dată fiind o succesiune de ı̂ndoituri aplicată unei foi de dimensiune N N , scrieţi un program
care să determine ı̂nălţimea, lăţimea şi grosimea obiectului obţinut.
Date de intrare
Date de ieşire
Fişierul de ieşire origami.out va conţine, pe o singură linie, trei numere naturale nenule H, L,
G, separate prin câte un spaţiu, reprezentând ı̂nălţimea, lăţimea şi respectiv grosimea obiectului
obţinut.
Exemple:
origami.in origami.out
Explicaţii
4 326 Hârtia are 4 unităţi ı̂nălţime şi 4 unităţi lăţime. Prima ı̂ndoitură
3 se realizează de la stânga la dreapta, de-a lungul celei de-a treia
13 linii verticale faţă de marginea stângă a foii. Se obţine o foaie de
23 ı̂nălţime 4, lăţime 3 şi grosime 2. A doua ı̂ndoitură se realizează
11 ı̂ndoind partea superioară a foii, ı̂n jos, de-a lungul celei de-a
treia linii orizontale faţă se marginea de sus a foii. Se obţine
un obiect de ı̂nălţime 3, lăţime 3 şi grosime 4. După a treia
ı̂ndoitură se obţine obiectul final, având ı̂nălţimea 3, lăţimea 2
şi grosimea 6.
Timp maxim de executare/test: 0.1 secunde
Memorie: total 16 MB din care pentru stivă 1 MB
CAPITOLUL 14. ONI 2009 14.5. ORIGAMI 387
Reprezentarea foii de hârtie se va realiza cu ajutorul unui tablou bidimensional F , având iniţial
N linii şi N coloane şi elementele egale cu zero.
Deoarece ı̂ndoiturile se pot realiza doar pe liniile dintre elementele tabloului, F ij va
reprezenta grosimea foii ı̂n zona pătrăţelei de pe linia i şi coloana j.
Matricea F va reflecta modificările aduse foii iniţiale după fiecare ı̂ndoitură din fişierul de
intrare, astfel:
- Pentru ı̂ndoitura de tip 1:
` Cazul 1: Distanta faţă de marginea stăngă a foii a liniei de-a lungul căreia se realizează
ı̂ndoitura (Lin) este mai mică decât jumatatea lăţimii hârtiei, adică 2 Lin & M .
ı̂n acest caz, realizez ı̂ndoitura suprapunând primele Lin linii, oglindite, peste
următoarele Lin linii, ı̂nsumând elementele corespunzătoare din F .
Mut apoi toate elementele matricei către stânga.
` Cazul 2: Distanta faţă de marginea stăngă a foii a liniei de-a lungul căreia se realizează
ı̂ndoitura (Lin) este mai mare decât jumatatea lăţimii hârtiei, adică 2 Lin % M .
Putem reduce acest caz la cazul precedent, mutând coloanele care, prin ı̂ndoire, depăşesc
marginea dreaptă a foii la sfârşitul tabloului, oglindite.
Se aplică apoi procedeul de la cazul precedent, ı̂ndoind foaia la distanţa M Lin
elemente.
- Pentru ı̂ndoitura de tip 2 se procedează asemănător.
3
Algoritmul are, ı̂n cazul cel mai defavorabil, complexitatea O N r N .
// indoi
for(j=1;j<=CatIndoi;j++)
for(i=1;i<=N;i++) F[i][2*CatIndoi-j+1] += F[i][j];
// indoi
for(i=1;i<=CatIndoi;i++)
for(j=1;j<=M;j++) F[2*CatIndoi-i+1][j] += F[i][j];
int main()
{
ifstream f(Fin);
int N; // dimensiunea initiala a hartiei
int M; // latimea hartiei, initial = N
int Nr; // numarul indoiturilor
//initializez hartia
for(i=1;i<=N;i++) for(j=1;j<=M;j++) F[i][j] = 1;
for(i=1;i<=Nr;i++)
{
f>>Tip>>Lin;
indoaie(N,M,Tip,Lin);
}
f.close();
ofstream g(Fou);
g<<N<<" "<<M<<" "<<Max<<’\n’;
/*
for(i=1;i<=N;i++)
{
for(j=1;j<=M;j++) g<<F[i][j]<<" ";
g<<’\n’;
}
*/
g.close();
return 0;
}
14.6 patrate
Problema 6 - patrate 100 de puncte
Fiind date două numere naturale n şi p se cere să se găsească un număr natural m & 350.000
cu proprietatea că el poate fi scris atât ca sumă de p pătrate perfecte nenule, cât şi ca sumă de
p 1 pătrate perfecte nenule, ..., cât şi ca sumă de n pătrate perfecte nenule.
Cerinţe
Date de intrare
Prima linie a fişierului de intrare patrate.in conţine două numere naturale n şi p separate
printr-un spaţiu, având semnificaţia de mai sus.
Date de ieşire
Exemple:
O primă idee, greu de implementat, este de a genera toate submulţimile de 2, 3, ..., n elemente
ale mulţimii numerelor naturale şi apoi de ale căuta pe acelea cu proprietatea că dau aceeaşi
sumă a pătratelor. Acesta idee este inutilizabilă din punct de vedere practic deoarece numărul de
submulţimi este foarte mare.
Soluţia se bazează pe o idee constructivă, mai precis pe construirea din aproape ı̂n aproape a
soluţiei.
Să demonstrăm pentru ı̂nceput, din punct de vedere matematic, că există un număr natural
m care se poate scrie, simultan ca suma de 2, 3, ..., n pătrate perfecte nenule, de numere ı̂ntregi.
Vom demonstra prin inducţie această proprietate.
P(1):
2 2
Pentru n 2 se găseşte m2 1 1 2
2 2 2 2 2
Pentru n 3 se găseşte m3 1 4 2 2 3 17
P(k): Presupunem relaţia adevărată pentru n k, cu alte cuvinte există un număr m care se
2 2 2 2 2
scrie ca suma de 2, 3, ..., k pătrate de numere ı̂ntregi nenule. Aşadar mk a1 a2 b1 b2 b3
2 2 2
... l1 l2 ... lk
P(k+1): Vom demonstra acum ca şi relaţia P(k+1) este adevărată.
Mai ı̂ntâi vom utiliza un alt rezultat, uşor de demonstrat, şi anume că orice număr natural
p ' 7 poate fi scris sub forma p a b c . Vom aplica acest rezultat pentru mk şi vom obţine
2 2 2
2 2 2
3 numere ı̂ntregi a, b, c, astfel ı̂ncât mk a b c .
Odată mk scris sub acesta formă avem :
2 2 2 2 2 2 2 2 2 2 2 2
mk1 mk c a2 b2 a1 a2 c b1 b2 b3 c ... l1 l2 ... lk c .
2 2 2
Exemplu m3 17 3 3 1 ,
2 2 2 2 2 2 2 2 2 2
de unde m4 17 1 3 3 1 4 1 2 2 3 1 18
Aşadar P(k+1) este adevărată deci P(n) este adevărată pentru orice n natural.
Soluţia urmăreşte mecanismul descris anterior, cu observaţia că pentru a genera un număr m
2 2 2
cât mai mic, fără a avea pretenţia că este cel mai mic, facem descompunerea mk a b c ı̂n
aşa fel ı̂ncât c să fie minim.
int test(int x)
{
int i,j;
FOR(i, 1, 10000)
{
if(x < i*i) break;
j = int(sqrt(x - i * i));
if(j*j + i*i != x) continue;
a = i;
b = j;
return 1;
}
return 0;
}
int doit(int x)
{
int i;
FOR(i, 1, 1000)
if(test(x + i * i))
CAPITOLUL 14. ONI 2009 14.6. PATRATE 391
{
c = i;
return x + i * i;
}
return -1;
}
int main()
{
int i,j,l,k;
freopen("patrate.in","r",stdin);
freopen("patrate.out","w",stdout);
scanf("%d", &n);
scanf("%d", &p);
j = 2;
A[0] = B[0] = 1;
C[0] = 0;
l = 1;
FOR(i, 3, n + 1)
{
j = doit(j);
A[l] = a ;
B[l] = b ;
C[l] = c ;
l++;
}
printf("%d\n", j);
FOR(i,p,n+1)
{
k = n - i;
printf("%d %d",A[k],B[k]);
FOR(j,k+1, l) printf(" %d",C[j]);
printf("\n");
}
return 0;
}
ONI 2008
15.1 ab
Problema 1 - ab 100 de puncte
Una din cele mai noi pasiuni ale lui Zăhărel este să studieze diverse proprietăţi ale permutărilor.
De exemplu, este interesat de permutările ı̂n care cel mai lung subşir crescător şi cel mai lung
subşir descrescător au lungimi date.
Cerinţe
Să se scrie un program care determină o permutare de lungime N ı̂n care cel mai lung subşir
crescător are lungime A şi cel mai lung subşir descrescător are lungime B.
Date de intrare
Date de ieşire
Fişierul de ieşire ab.out va conţine pe prima linie N numere separate prin câte un spaţiu,
reprezentând o permutare care respectă condiţiile de mai sus. Dacă există mai multe soluţii, se
va afişa cea minimă din punct de vedere lexicografic.
Exemple:
ab.in ab.out
Explicaţii
423 1432 Cel mai lung subşir crescător are lungime 2 (1 4, 1 3 sau 1 2), iar cel
mai lung subşir descrescător are lungime 3 (4 3 2).
O altă soluţie posibilă este 2 4 3 1, dar aceasta nu este minimă din punct
de vedere lexicografic.
Timp maxim de executare/test: 0.1 secunde
392
CAPITOLUL 15. ONI 2008 15.1. AB 393
Mircea Paşoi
Vom prezenta un algoritm prin care se poate obţine o permutare de lungime N cu cel mai lung
subşir crescător de lungime A şi cel mai lung subşir descrescător de lungime B:
1) Se ı̂mpart numerele de la 1 la N (luate ı̂n ordinea asta) ı̂n A grupuri de lungime cel mult B
fiecare. Deasemenea, trebuie să existe cel puţin un grup de lungime fix B.
2) Se inversează elementele din fiecare grup
Orice subşir descrescător va face parte dintr-un grup, iar cum lungimea unui grup este maxim
B (şi există unul cu fix B), cel mai lung subşir descrescător va avea lungime B.
Orice subşir crescător va fi format cu câte un element din fiecare grup. Cum sunt A grupuri,
cel mai lung subşir crescător va avea lungime A.
Pentru a obţine o soluţie minimă lexicografic trebuie ca grupurile de la ı̂nceput să fie cât mai
mici ca mărime.
Exemplu pentru N 10, A 4, B 3
Se ı̂mparte ı̂n 4 grupuri, fiecare de lungime maxim 3 astfel ı̂ncât grupurile de la ı̂nceput să fie
cât mai mici:
1 — 2 3 4 — 5 6 7 — 8 9 10
Se inversează elementele din fiecare grup şi se obţine permutarea:
1 4 3 2 7 6 5 10 9 8
int N, A, B, size[MAX_A];
int main(void)
{
int i, j, n;
n = N;
for (i = A-1; i >= 0; --i)
{
size[i] = min(n-i, B);
n -= size[i];
}
for (i = 0; i < A; ++i)
{
for (j = n+size[i]; j > n; --j)
printf("%d ", j);
n += size[i];
}
printf("\n");
return 0;
}
CAPITOLUL 15. ONI 2008 15.2. IEPURAS 394
15.2 iepuras
Problema 2 - iepuras 100 de puncte
Un iepuraş se găseşte ı̂ntr-o grădină plină de surprize. Harta grădinii poate fi reprezentată sub
forma unei table dreptunghiulare cu m linii, numerotate de la 1 la m de sus ı̂n jos, şi n coloane,
numerotate de la 1 la n de la stânga la dreapta. ı̂n fiecare celulă a acestei grădini se poate găsi
cel mult una dintre următoarele surprize: săgeată, pom, zid, trapă, morcov, bombă.
O săgeată indică una din direcţiile nord, sud, est, vest. Odată ajuns ı̂ntr-o celulă conţinând
o astfel de săgeată iepuraşul ı̂şi va continua deplasarea ı̂n sensul indicat de săgeată, iar aceasta
dispare.
Într-o celulă ı̂n care se găseşte un pom sau un zid iepuraşul nu poate să pătrundă, ı̂nsă dacă
se ”loveşte” de un pom, el ı̂şi păstrează direcţia, ı̂nsă schimbă sensul (dacă se deplasa spre nord,
ı̂şi va schimba sensul spre sud, dacă se deplasa spre est, se va deplasa după aceea spre vest etc).
Dacă iepuraşul intră ı̂ntr-o celulă conţinând o trapă, toate zidurile aflate pe teren dispar şi vor
apărea alte ziduri, ı̂n poziţii precizate. Dacă iepuraşul va trece din nou printr-o celulă conţinând o
trapă, zidurile nou construite dispar şi vor reapărea zidurile iniţiale. Mai exact există două grupe
de ziduri care comută la fiecare trecere printr-o celulă care conţine o trapă.
Dacă iepuraşul va trece de două ori prin vecinătatea unei celule conţinând o bombă (adică
prin celulele ı̂nvecinate la sud, nord, est sau vest cu celula conţinând bombă), bomba va exploda
iar iepuraşul se transformă instantaneu ı̂n ı̂ngeraş. De asemenea, dacă iepuraşul ı̂ntră ı̂ntr-o celulă
conţinând o bombă se transformă instantaneu ı̂n ingeras.
Iepuraşul va ronţăi toţi morcovii care ı̂i ies ı̂n cale. Evident că dacă trece a doua oară prin
aceeaşi celulă, la a doua trecere nu va mai găsi morcov.
Surprizele din grădină sunt codificate astfel: 1 pentru săgeată spre nord, 2 pentru săgeată spre
vest, 3 pentru săgeată spre sud, 4 pentru săgeată spre est, 5 pentru pom, 6 pentru bombă, 7
pentru morcov, 8 pentru zid, 9 pentru trapă. Căsuţele libere de pe harta grădinii se codifică cu
0.
Iniţial, se cunosc poziţia şi sensul de deplasare ale iepuraşului. Expediţia acestuia se termină
ı̂n următoarele situaţii:
- la explozia unei bombe, caz ı̂n care se transformă ı̂n ı̂ngeraş;
- la părăsirea grădinii (adică la ieşirea ı̂n afara zonei dreptunghiulare date), caz ı̂n care se
rătăceşte;
- ı̂n momentul ı̂n care reuşeşte să ronţăie toţi morcovii, caz ı̂n care este fericit.
Cerinţe
Fiind data harta grădinii se cere să se determine starea finală a iepuraşului (ı̂ngeraş, rătăcit
respectiv fericit), numărul de morcovi ronţăiţi până ı̂n momentul terminării expediţiei, precum şi
numărul de paşi pe care ı̂i face iepuraşul până la acest moment.
Date de intrare
Pe prima linie a fişierului de intrare iepuras.in se găsesc două numere ı̂ntregi m şi n, separate
printr-un spaţiu, reprezentând numărul de linii, respectiv numărul de coloane ale hărţii.
Linia a doua a fişierului conţine trei numere naturale, separate prin câte un spaţiu, reprezentând
linia si coloana poziţiei iniţiale a iepuraşului pe hartă precum şi direcţia spre care acesta este
orientat. Direcţia este codificată astfel: 1 pentru nord, 2 pentru vest, 3 pentru sud, 4 pentru est.
Următoarele m linii conţin câte n numere ı̂ntregi separate prin câte un spaţiu, reprezentând
codificarea hărţii grădinii, conform celor precizate mai sus.
Următoarea linie conţine un singur număr natural t reprezentând numărul de celule ce vor
conţine ziduri după prima trecere printr-o celulă conţinând o trapă. Următoarele t linii conţin
câte două numere naturale i şi j, separate printr-un spaţiu, reprezentând coordonatele câte unei
celule ce va conţine zid după o primă trecere printr-o celulă conţinând o trapă.
Date de ieşire
CAPITOLUL 15. ONI 2008 15.2. IEPURAS 395
Fişierul de ieşire iepuras.out va conţine pe prima sa linie unul dintre cuvintele INGERAS,
RATACIT respectiv FERICIT, corespunzător stării finale a iepuraşului.
A doua linie a fişierului va conţine două numere ı̂ntregi separate printr-un spaţiu, reprezentând
linia şi coloana ultimei poziţii de pe teren a iepuraşului, adică poziţia ı̂n care a murit, sau ı̂n care
a devenit fericit, respectiv ultima poziţie a sa de pe teren, ı̂nainte de a se rătăci.
A treia linie a fişierului va conţine două numere ı̂ntregi separate printr-un spaţiu, reprezentând
numărul de morcovi culeşi până ı̂n momentul terminării expediţiei, respectiv numărul de paşi pe
care ı̂i face iepuraşul până ajunge ı̂n starea finală.
a poziţia iniţială a iepuraşului este una validă, adică este o celulă liberă din interiorul terenului;
a pentru datele de test, se asigură că iepuraşul va face un număr finit de paşi până la terminarea
expediţiei sale;
a 1 & m, n & 200
a 0 & t & 20
a numărul maxim de ziduri aflate la un moment dat pe teren este de cel mult 50, şi este posibil
ca la un moment dat să nu existe niciun zid pe teren.
a Se garantează că pentru datele de intrare iepuraşul nu poate ajunge simultan ı̂n două din
cele trei stări.
a Pomii şi trapele sunt obiecte care rămân permanent ı̂n teren, ı̂n poziţiile lor iniţiale.
a ı̂ntr-o celulă a grădinii se poate găsi la un moment dat o singură surpriză (pom, zid, trapă,
morcov, săgeată, bombă)
Exemple:
Pentru fiecare celulă ı̂n care ”intră” iepuraşul se verifică, ı̂n ordine, următoarele:
- dacă a iesit din teren, atunci iepuraşul e rătăcit
- dacă celula conţine un morcov acesta este ronţăit (se contorizează numărul de morcovi
ronţăiţi)
CAPITOLUL 15. ONI 2008 15.2. IEPURAS 396
int m,n;
char a[201][201];
int di[4]={-1,0,1,0};
int dj[4]={0,-1,0,1};
struct zid
{
int i,j;
};
zid z1[50],z2[50];
for (k=0;k<4;k++)
{
i1=i+di[k];
j1=j+dj[k];
return 0;
}
main()
{
int k,i0,j0,d,i,j,n1=0,n2=0,pz=1;
long nrm,p=0,q=0;
ifstream f("iepuras.in");
ofstream g("iepuras.out");
CAPITOLUL 15. ONI 2008 15.2. IEPURAS 397
nrm=0;
f>>m>>n;
f>>i0>>j0>>d;
for (i=1;i<=m;i++)
for (j=1;j<=n;j++)
{
f>>a[i][j];
if (a[i][j]==’7’) nrm++;
if (a[i][j]==’8’)
{
z1[n1].i=i;
z1[n1].j=j;
n1++;
}
}
f>>n2;
for (i=0;i<n2;i++)
f>>z2[i].i>>z2[i].j;
f.close();
i=i0;
j=j0;
while (1)
{
i=i+di[d-1];
j=j+dj[d-1];
p++;
// afara
if (afara(i,j))
{
g<<"RATACIT"<<endl;
g<<i-di[d-1]<<" "<<j-dj[d-1]<<endl;
g<<q<<" "<<p;
break;
}
// morcov
if (a[i][j]==’7’)
{
q++;
a[i][j]=’0’;
}
if (a[i][j]==’9’)
{
if (pz==1)
{
for (k=0;k<n1;k++)
a[z1[k].i][z1[k].j]=’0’;
for (k=0;k<n2;k++)
CAPITOLUL 15. ONI 2008 15.3. PALIND 398
a[z2[k].i][z2[k].j]=’8’;
pz=2;
}
else
{
for (k=0;k<n2;k++)
a[z2[k].i][z2[k].j]=’0’;
for (k=0;k<n1;k++)
a[z1[k].i][z1[k].j]=’8’;
pz=1;
}
}
g.close();
}
15.3 palind
Problema 3 - palind 100 de puncte
Ana a descoperit că are o adevărată pasiune pentru palindroame. Un şir de numere este
palindrom dacă se citeşte la fel de la stânga la dreapta şi de la dreapta la stânga (primul număr
este egal cu ultimul, al doilea cu penultimul etc). Ea are un şir cu N numere naturale şi vrea ca
orice subsecvenţă de lungime impară a şirului să fie palindrom. Pentru a-şi ı̂ndeplini dorinţa ea
poate efectua asupra şirului mai multe operaţii. O operaţie constă ı̂n alegerea unui element din
şir şi incrementarea sau decrementarea lui cu o unitate. Bineı̂nţeles, Ana doreşte să utilizeze un
număr minim de operaţii pentru ca şirul obţinut să respecte proprietatea amintită mai sus (orice
subsecvenţă de lungime impară să fie palindrom).
Cerinţe
Determinaţi pentru Ana numărul minim de operaţii pe care trebuie să-l efectueze pentru
ca orice subsecvenţă de lungime impară a şirului obţinut ı̂n urma efectuării operaţiilor să fie
palindrom. De asemenea aflaţi şi numărul de şiruri finale distincte pe care le poate obţine efectuând
acest numar minim de operaţii.
Date de intrare
Date de ieşire
Fişierul de ieşire palind.out va conţine T linii, pe linia i aflându-se două numere, reprezentând
raspunsul pentru al i-lea set de date de intrare. Primul numar este numarul minim de operaţii,
iar al doilea numărul de şiruri distincte finale care se pot obţine efectuând numărul minim de
operaţii.
a 1 & T & 20
a 1 & N & 10.000
a Elementele şirului sunt numere naturale din intervalul 1, 7.000
a subsecvenţă a unui şir este un subşir de numere care apar pe poziţii consecutive
a Toate testele folosite la corectare vor avea T 20
a Pentru 20% din teste 1 & N & 100
a Pentru 20% din teste valoarea maximă din oricare şir este cel mult 500 şi N ' 101
Exemple:
Adrian Airinei
Pentru ca orice subsecvenţă de lungime impară să fie palindrom şirul trebuie să fie de forma
XY XY XY XY...XY .
Astfel putem rezolva independent problema pentru poziţiile pare şi impare.
Dacă fixăm primul element din şir observăm că trebuie să minimizăm o sumă de forma
—V-A1—+—V-A2—+....+—V-An—.
Primele idei sunt de a fixa fiecare valoare posibilă şi apoi de a parcurge şirul ı̂n O N sau vec-
torul frecvenţelor ı̂n O V M AX pentru a obţine O V M AX N respectiv O V M AX V M AX .
Pentru a optimiza soluţia vom calcula
cnti = suma care se obţine
dacă fixăm valoarea V i luăm ı̂n calcul doar valorile din şir mai mici decât i.
Observăm că dacă există N R numere mai mici decât i 1 ı̂n şir
cnti cnti 1 N R C,
unde C = câte numere sunt egale cu i 1.
Vom aplica un procedeu asemănător şi pentru numerele mai mari decât i.
O altă soluţie ar fi să observăm că elementul V este de fapt mediana şirului A1A2...An care
se poate determina ı̂n O N lgN cu o sortare sau ı̂n O N cu statistici de ordine.
int sol[2][2];
void baga(int t)
{
int i, nr, val, x, mmin = INF;
memset(cnt, 0, sizeof(cnt));
for(val = nr = 0, i = 1; i < MAXN; i++)
val += nr, val += A[t][i-1], nr += A[t][i-1], cnt[i] += val;
for(nr = val = 0, i = MAXN-2; i >= 1; i--)
val += nr, val += A[t][i+1], nr += A[t][i+1], cnt[i] += val;
for(nr = 0, i = 1; i < MAXN; i++)
{
if(cnt[i] == mmin) nr++;
if(cnt[i] < mmin) mmin = cnt[i], nr = 1;
}
sol[t][0] = mmin, sol[t][1] = nr;
}
void solve(void)
{
int i, val, x;
memset(A, 0, sizeof(A));
scanf("%d", &N);
for(i = 1; i <= N; i++)
scanf("%d", &x), A[i&1][x]++;
baga(0), baga(1);
printf("%d %d\n", sol[0][0]+sol[1][0], sol[0][1]*sol[1][1]);
}
void brute(void)
{
int p[2][2], i, j, x, nr, val, t, mmin;
int main(void)
{
freopen("palind.in", "rt", stdin);
freopen("palind.out", "wt", stdout);
int teste;
return 0;
}
void solve(void)
{
int i, val, t, x, j, nr, mmin = INF;
scanf("%d", &N);
for(i = 1; i <= N; i++)
scanf("%d", &x), A[i&1][ ++cnt[i&1] ] = x;
int main(void)
{
freopen("palind.in", "rt", stdin);
freopen("palind.out", "wt", stdout);
int teste;
return 0;
}
void brute(void)
{
int p[2][2], i, j, x, nr, val, t, mmin;
memset(A, 0, sizeof(A));
scanf("%d", &N);
for(i = 1; i <= N; i++)
scanf("%d ", &x), A[i&1][x]++;
int main(void)
{
freopen("palind.in", "rt", stdin);
freopen("palind.out", "wt", stdout);
int teste;
return 0;
}
15.4 auto
Problema 4 - auto 100 de puncte
Se consideră o autostradă dispusă ı̂n linie dreaptă având N puncte de acces (intrare şi ieşire). ı̂n
fiecare punct de acces există containere pentru colectarea deşeurilor, toate containerele au aceeaşi
capacitate şi ı̂n fiecare punct de acces pot fi mai multe astfel de containere.
Firma care asigură curăţenia dispune de un singur mijloc de transport al containerelor. Acest
mijloc de transport poate ı̂ncărca exact un număr K de containere. Accesul mijlocului de transport
pe autostradă se face cu restricţii pentru a nu perturba traficul şi din acest motiv trebuie ca la
fiecare acces pe autostradă să fie colectate exact atâtea containere cât este capacitatea maşinii,
dar dintr-un punct de colectare trebuie să ia exact un container, deci dacă se intră pe autostradă
la punctul de acces P , unde P & N K 1, atunci trebuie să ia containere de la punctele de
acces numerotate cu P, P 1, P 2, ..., P K 1, ı̂n aceste puncte de acces scade cu 1 numărul
containerelor rămase.
Firma trebuie să găsească toate valorile posibile pentru K astfel ı̂ncât să poată colecta toate
containerele.
Cerinţe
Se cere să se găsească toate valorile posibile pentru K astfel ı̂ncât să fie adunate toate con-
tainerele.
Date de intrare
Fişierul de intrare auto.in va conţine pe prima linie numărul natural T , reprezentând numărul
de seturi de date de intrare. În continuare urmează seturile de date de intrare, fiecare pe cate două
linii. Pe prima linie a unui set se află numărul N , având semnificaţia din enunţ. Pe următoarea
linie se află N numere naturale separate printr-un spaţiu, reprezentând numărul de containere din
fiecare punct de acces.
Date de ieşire
Fişierul de ieşire auto.out va conţine T linii, pe linia i aflându-se răspunsul pentru al i-lea set
de date de intrare. Valorile posibile pentru K se vor afişa ı̂n ordine crescatoare, separate printr-un
spaţiu.
a 2 & T & 30
a 2 & N & 9 000
a 1&K&N
a 0 & numărul de containere din fiecare punct de acces & 10 000
Exemple:
Pentru un K fixat, putem calcula uşor ı̂n O N K dacă reprezintă soluţie sau nu. Această
3
abordare are complexitate O N şi obţine aproximativ 30 puncte.
Putem folosi o stivă ı̂n care avem la un moment dat invervalele sortate crescător după capătul
dreapta. Când suntem la al i-lea punct de intrare scoatem din stivă intervalele care nu conţin
punctul acesta şi eventual introducem dacă este nevoie intervalul i, i k 1. Această soluţie are
2
complexitate O N şi obtine 70 puncte.
Pentru a optimiza această soluţie observăm că dacă suma tuturor valorilor este SU M este
necesar să considerăm valorile posibile pentru K care este divizor al lui SU M . Astfel complexitatea
devine O N DIV N , unde N DIV este numărul de divizori ai lui SU M mai mici sau egali cu
N.
int check(int K)
{
int i, j, t = 1, p = 0, val = 0;
return 1;
}
void read_and_solve(void)
{
int i, j, k; long d = 0;
scanf("%d\n", &N), NR = 0;
assert(N >= 1 && N <= 9000);
CAPITOLUL 15. ONI 2008 15.4. AUTO 404
int main(void)
{
int teste, start, end;
start = clock();
end = clock();
return 0;
}
int check(int K)
{
int i, j, t = 1, p = 0, val = 0;
return 1;
}
void read_and_solve(void)
{
int i, j, k; long d = 0;
scanf("%d\n", &N), NR = 0;
for(i = 1; i <= N; i++) scanf("%d ", &A[i]), d += (long)A[i];
int main(void)
{
int teste;
return 0;
}
15.5 div
Problema 5 - div 100 de puncte
Se citesc două numere naturale M şi N .
Cerinţe
Să se elimine o secvenţă de cifre din numărul N pentru a obţine un număr divizibil cu M de
valoare maximă.
Date de intrare
Fişierul de intrare div.in conţine pe prima linie numărul natural nenul M iar pe a doua linie
numărul natural N .
Date de ieşire
Fişierul de ieşire div.out va conţine două numere ı̂ntregi i1 şi i2 separate prin câte un spaţiu,
reprezentând indicii primei, respectiv ultimei cifre care vor fi şterse. Cifrele lui N se indexează de
la 1, de la stânga la dreapta. Dacă sunt mai multe soluţii se va scrie cea pentru care primul indice
este cel mai mic. Dacă nu trebuie eliminată nici o cifră se vor scrie două cifre de 0.
Exemple:
#include <iostream>
int digits[MV + 1] ;
int putere[MV + 1] ;
int psm[MV + 1] ;
int suff[MV + 1] ;
int n ;
putere[0] = 1 ;
if (psm[n] == 0)
{
fprintf(out, "0 0") ;
return 0 ;
}
CAPITOLUL 15. ONI 2008 15.6. TEATRU 407
bool found(false) ;
int ansi(1), ansj(n) ;
idx1 ++ ;
idx2 ++ ;
}
}
}
}
}
15.6 teatru
Problema 6 - teatru 100 de puncte
Alina este mare iubitoare de teatru. Directorul teatrului i-a oferit şansa să joace ı̂n mai multe
spectacole, ca figurant, deocamdată. Costumiera de scenă a decis să-i dea C costume diferite
dintre cele care sunt destinate acestei stagiuni. Alina va duce costumele acasă şi le va ajusta ca
să-i vină bine. Stagiunea durează N zile consecutive şi ı̂n fiecare zi se joacă câte o piesă. Aceeaşi
piesă se va juca, desigur ı̂n una sau mai mai multe zile ale stagiunii. Fiecărei piese i se asociază un
unic costum de figurant, deci pentru fiecare piesă ı̂n care joacă, Alina trebuie să ı̂mbrace un singur
costum, acela asociat piesei respective. Costumele de figuranţi sunt identificate prin literele mari
ale alfabetului englez: A, B, C, ..., X, Y, Z. Alina are voie să-şi aleagă cele C costume diferite.
Cerinţe
CAPITOLUL 15. ONI 2008 15.6. TEATRU 408
Cunoscând costumul asociat fiecărei zile a stagiunii, ajutaţi-o pe Alina să-şi aleagă cele C
costume diferite, ı̂n aşa fel ı̂ncât să poată juca ı̂ntr-un număr cât mai mare de piese consecutive.
Date de intrare
Fişierul de intrare teatru.in conţine pe prima linie două numere naturale Z şi C despărţite
printr-un spaţiu. Z este numărul de zile din stagiune iar C este numărul de costume diferite pe
care Alina le poate primi. Pe linia a doua, se găsesc Z caractere, litere mari ale alfabetului englez.
Caracterul al i-lea identifică costumul de figurant care trebuie ı̂mbrăcat ı̂n spectacolul din ziua i.
Date de ieşire
În fişierul de ieşire teatru.out se va scrie pe prima linie un număr natural N , reprezentând
numărul maxim de spectacole consecutive ı̂n care Alina poate juca. Pe linia a doua se scriu N
caractere, fără spaţii ı̂ntre ele, corespunzătoare costumelor care vor fi ı̂mbrăcate ı̂n cele N piese
de teatru alese, ı̂n ordinea spectacolelor ı̂n care va juca. Dacă există mai multe soluţii de lungime
N atunci se afişează cea căreia ı̂i corespunde o zi de ı̂nceput mai aproape de ı̂nceputul stagiunii.
Exemple:
Problema admite o soluţie ı̂n timp liniar. Se păstrează poziţiile de ı̂nceput şi de sfârşit p şi u
ale secvenţei curente. Iniţial, u avansează de la poziţia 1 până când numărul de caractere diferite
din mulţime depăşeşte C. Urmează incrementarea lui p cu numărul de poziţii necesar pentru a
readuce mulţimea la cardinalul C. Se incrementează din nou u atâta timp numărul de caractere
distincte ale secvenţei nu este mai mare dacât C. Procesul continuă ı̂n felul acesta, până când se
parcurg toate elementele şirului. Inserarea şi extragerea elementelor din secvenţă se face ı̂n timp
constant cu ajutorul unui şir al frecvenţelor.
2 3
O soluţie de complexitate O N va primi 40 de puncte, iar una O N 20 puncte.
char s[55001];
long imax, jmax, Lmax, n, k;
long f[91];
void Citeste();
void Afis();
CAPITOLUL 15. ONI 2008 15.6. TEATRU 409
void Calculeaza();
int main()
{
freopen("teatru.in", "r", stdin);
freopen("teatru.out", "w", stdout);
Citeste();
Calculeaza();
Afis();
return 0;
}
void Citeste()
{
scanf("%d%d", &n, &k);
scanf("%c", &s[0]); // citeste ’\n’
for (long int i = 0; i < n; i++ )
scanf("%c", s + i);
}
void Afis()
{
printf("%d\n", Lmax);
for (long int i = imax; i <= jmax; i++ )
printf("%c", s[i]);
printf("\n");
}
void Calculeaza()
{
long int i = 0, j = 0, nr = 0;
f[s[0]]++; nr++;
do
{
while (nr <= k && j < n)
{
j++;
if ( j >= n ) break;
if (f[s[j]] == 0)
{
f[s[j]]++;
nr++;
}
else f[s[j]]++;
}
if (j - i > Lmax)
{
Lmax = j - i;
imax = i; jmax = j - 1;
}
while ( nr > k )
{
f[s[i]]--;
if ( f[s[i]] == 0 ) nr--;
i++;
}
int main(void)
CAPITOLUL 15. ONI 2008 15.6. TEATRU 410
{
int i, j, k;
printf("%d\n", bst_len);
for (i = start; i < start+bst_len; ++i)
printf("%c", S[i]);
printf("\n");
return 0;
}
char s[55001];
long imax, jmax, Lmax, n, k;
long f[91];
void Citeste();
void Afis();
void Calculeaza();
int main()
{
freopen("teatru.in", "r", stdin);
freopen("teatru.out", "w", stdout);
Citeste();
Calculeaza();
Afis();
return 0;
}
void Citeste()
{
scanf("%d%d", &n, &k);
scanf("%c", &s[0]); // citeste ’\n’
for (long int i = 0; i < n; i++ )
scanf("%c", s + i);
}
void Afis()
{
printf("%d\n", Lmax);
for (long int i = imax; i <= jmax; i++ )
printf("%c", s[i]);
printf("\n");
}
void Calculeaza()
{
long int i = 0, j = 0, nr = 0, p;
nr = 0;
for ( p = ’A’; p <= ’Z’; p++ )
if ( f[p] ) nr++;
OJI 2007
16.1 Cartele
În sediul unei firme se intră doar cu ajutorul cartelelor magnetice. De câte ori se schimbă
codurile de acces, cartelele trebuie formatate. Formatarea presupune imprimarea unui model
prin magnetizare. Dispozitivul ı̂n care se introduc cartelele, numit cititor de cartele, verifică acest
model. Toate cartelele au aceleaşi dimensiuni, suprafaţa pătrată şi grosimea neglijabilă. Cele două
feţe plane ale unei cartele se ı̂mpart fiecare ı̂n N N celule pătrate, identice ca dimensiuni. Prin
formatare unele celule, marcate cu negru ı̂n exemplu, se magnetizează permiţând radiaţiei infraroşii
să treacă dintr-o parte ı̂n cealaltă a cartelei. În interiorul cititorului de cartele se iluminează
uniform una dintre feţele cartelei. De cealaltă parte fasciculele de lumină care străbat cartela sunt
analizate electronic. Pentru a permite accesul ı̂n clădire modelul imprimat pe cartelă trebuie să
coincidă exact cu modelul şablonului care memorează codul de intrare. Prin fanta dispozitivului
nu se pot introduce mai multe cartele deodată. Cartela se poate introduce prin fantă cu oricare
dintre muchii spre deschizătura fantei şi cu oricare dintre cele două feţe orientate către şablon.
După introducere cartela se dispune ı̂n plan paralel cu şablonul, lipit de acesta, astfel ı̂ncât cele
patru colţuri ale cartelei se suprapun exact cu colţurile şablonului. Modelele imprimate pe cele
două feţe ale unei cartele sunt identice. Unei celule magnetizate ı̂i corespunde pe faţa opusă tot
o celulă magnetizată, iar unei celule nemagnetizate ı̂i corespunde una nemagnetizată. O celulă
magnetizată este transparentă pentru radiaţia infraroşie indiferent de faţa care se iluminează.
Un angajat al firmei are mai multe cartele. Pe unele dintre acestea a fost imprimat noul cod
de intrare, iar pe altele sunt coduri mai vechi. Pentru a afla care sunt cartelele care-i permit
accesul ı̂n sediul firmei angajatul este nevoit să le verifice pe toate, introducn̂du-le pe rând, ı̂n
toate modurile pe care le consideră necesare, ı̂n fanta cititorului de cartele.
Cerinţă
Scrieţi un program care determină care dintre cartele permite accesul ı̂n sediul firmei.
Date de intrare
Fişierul de intrare cartele.in conţine pe prima linie două numere naturale N şi C despărţite
printr-un spaţiu. N este dimensiunea tablourilor care reprezintă modelul şablon şi modelele
cartelelelor. C reprezintă numărul de cartele. Urmează C 1 blocuri de câte N linii fiecare.
Primul bloc de N linii codifică şablonul. Următoarele C blocuri de câte N linii codifică fiecare
câte o cartelă. Pe fiecare linie sunt câte N valori ı̂ntregi, despărţite printr-un singur spaţiu.
Celulelor magnetizate le corespunde valoarea 1, iar celorlalte, valoarea 0.
412
CAPITOLUL 16. OJI 2007 16.1. CARTELE 413
Date de ieşire
În fişierul de ieşire cartele.out se vor scrie C linii, câte o valoare pe linie. Pe linia i se va scrie
valoarea 1 dacă cartela i permite accesul ı̂n clădire şi valoarea 0 ı̂n caz contrar.
Restricţii şi precizări
1 $ N, C & 50
Exemplu
cartele.in cartele.out Explicaţii
32 1 Datele de intrare corespund situaţiei din figură.
010 0 Cartela 1 se potriveşte perfect şablonului, dacă
001 se roteşte ı̂n sens trigonometric cu 90 de grade.
100 Cartela 2 nu se potriveşte şablonului, indiferent
100 de modul ı̂n care se introduce ı̂n fantă.
001
010
001
001
010
Timp maxim de execuţie/test: 1 secundă
Pentru fiecare cartelă, se compară element cu element, matricea care reprezintă sablonul, cu
următoarele tablouri:
1. Cartela
2. Cartela rotită cu 90 grade
3. Cartela rotită cu 180 grade
4. Cartela rotită cu 270 grade
Dacă nu s-a gasit o coincidenţă, se ı̂ntoarce cartela, printr-o operaţie de oglindire faţă de linia
i n©2, (sau faţă de coloana j n©2), după care se compară şablonul cu următoarele tablouri:
5. Cartela oglindită
6. Cartela oglindită rotită cu 90 grade
7. Cartela oglindită rotită cu 180 grade
8. Cartela oglindită rotită cu 270 grade
Rotirile se pot face ı̂n sens trigonometric sau orar.
Dacă s-a găsit o coincidenţă la oricare dintre paşii de mai sus, se opreşte căutarea, se afisează
1 şi se trece la prelucrarea următoarei cartele.
Dacă nici după pasul 8 nu s-a gasit o potrivire exactă, se afişează 0 şi se trece la prelucrarea
următoarei cartele.
#define DIM 51
ifstream fin("cartele.in");
ofstream fout("cartele.out");
int Egale();
void Inverseaza(); // intoarce cartela pe partea cealalta
// (oglindire fata de linia i = n/2)
CAPITOLUL 16. OJI 2007 16.1. CARTELE 414
int main()
{
Rezolva();
fin.close();
fout.close();
return 0;
}
void Rezolva()
{
fin >> n >> C;
int i, j, r;
int identice = 1;
if ( !identice ) Inverseaza();
}
if ( identice )
fout << 1 << ’\n’;
else
fout << 0 << ’\n’;
}
}
int Egale()
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if ( a[i][j] != b[i][j] )
return 0;
return 1;
}
void Inverseaza()
{
int i, j, temp;
for (i = 1; i <= n / 2; i++)
for (j = 1; j <= n; j++)
temp = b[i][j], b[i][j] = b[n-i+1][j], b[n-i+1][j] = temp;
}
void Roteste()
{
int i, j;
for (i = 1; i <= n; i++)
for (j = 1; j<= n; j++)
aux[n-j+1][i] = b[i][j];
b[i][j] = aux[i][j];
}
Varianta 1:
44 {
45 st.nextToken(); b[i][j]=(int)st.nval;
46 if(b[i][j]!=a[i][j]) identice=false;
47 }
48
49 for(int f=1;f<=2&&!identice;f++) // pentru fata 1 si 2
50 {
51 for(r=1;r<=4&&!identice;r++) // la a patra rotatie se revine la matricea
initiala
52 {
53 roteste(); // cu 90 in sens trigonometric
54 if(egale()) identice=true;
55 }
56 if(!identice) inverseaza();
57 }
58 if(identice) out.println(1); else out.println(0);
59 }// for k
60 }// rezolva(...)
61
62 static boolean egale()
63 {
64 for(int i=1;i<=n;i++)
65 for(int j=1; j<=n; j++)
66 if(a[i][j]!=b[i][j] ) return false;
67 return true;
68 }// egale(...)
69
70 static void inverseaza()
71 {
72 int i, j, temp;
73 for(i=1;i<=n/2;i++)
74 for(j=1;j<=n;j++) { temp=b[i][j]; b[i][j]=b[n-i+1][j]; b[n-i+1][j]=temp; }
75 } // inverseaza(...)
76
77 static void roteste()
78 {
79 int i, j;
80 for(i=1;i<=n;i++)
81 for(j=1;j<=n;j++) aux[n-j+1][i]=b[i][j];
82
83 for(i=1;i<=n;i++)
84 for(j=1;j<=n;j++) b[i][j]=aux[i][j];
85 }// roteste(...)
86 }// class
Varianta 2:
28 st.nextToken(); a[i][j]=(int)st.nval;
29 }
30
31 for(k=1;k<=c;k++)
32 {
33 for(i=1;i<=n;i++) // citesc cartela k
34 for(j=1;j<=n;j++)
35 {
36 st.nextToken(); b[i][j]=(int)st.nval;
37 }
38
39 ok=true;
40 for(i=1;i<=n&&ok;i++) // direct
41 for(j=1;j<=n&&ok;j++)
42 if(a[i][j]!=b[i][j]) ok=false;
43 if(ok) {out.println(1); continue;} // cu alta cartela
44
45 ok=true;
46 for(i=1;i<=n&&ok;i++) // rotit cu 90 (ceas!)
47 for(j=1;j<=n&&ok;j++)
48 if(a[i][j]!=b[j][n-(i-1)]) ok=false;
49 if(ok) {out.println(1); continue;} // cu alta cartela
50
51 ok=true;
52 for(i=1;i<=n&&ok;i++) // rotit cu 180 (ceas!)
53 for(j=1;j<=n&&ok;j++)
54 if(a[i][j]!=b[n-(i-1)][n-(j-1)]) ok=false;
55 if(ok) {out.println(1); continue;} // cu alta cartela
56
57 ok=true;
58 for(i=1;i<=n&&ok;i++) // rotit cu 270 (ceas!) <==> 90 trig
59 for(j=1;j<=n&&ok;j++)
60 if(a[i][j]!=b[n-(j-1)][i]) ok=false;
61 if(ok) {out.println(1); continue;} // cu alta cartela
62
63 ok=true;
64 for(i=1;i<=n&&ok;i++) // invers + direct
65 for(j=1;j<=n&&ok;j++)
66 if(a[i][j]!=b[i][n-(j-1)]) ok=false;
67 if(ok) {out.println(1); continue;} // cu alta cartela
68
69 ok=true;
70 for(i=1;i<=n&&ok;i++) // invers + rotit 90 (ceas!)
71 for(j=1;j<=n&&ok;j++)
72 if(a[i][j]!=b[n-(j-1)][n-(i-1)]) ok=false;
73 if(ok) {out.println(1); continue;} // cu alta cartela
74
75 ok=true;
76 for(i=1;i<=n&&ok;i++) // invers + rotit 180 (ceas!)
77 for(j=1;j<=n&&ok;j++)
78 if(a[i][j]!=b[n-(i-1)][j]) ok=false;
79 if(ok) {out.println(1); continue;} // cu alta cartela
80
81 ok=true;
82 for(i=1;i<=n&&ok;i++) // invers + rotit cu 270 (ceas!) <==> 90 trig
83 for(j=1;j<=n&&ok;j++)
84 if(a[i][j]!=b[j][i]) ok=false;
85 if(ok) {out.println(1); continue;} // cu alta cartela
86
87 out.println(0); // nu s-a potrivit
88 } // for k
89 out.close();
90 }// main
91 }// class
Varianta 3:
8
9 static int[][] a=new int[DIM][DIM]; // sablonul
10 static int[][] b=new int[DIM][DIM]; // cartela
11
12 static int n, c;
13
14 public static void main(String[] args) throws IOException
15 {
16 int i, j, k;
17
18 st=new StreamTokenizer(new BufferedReader(new FileReader("1.in")));
19 out=new PrintWriter(new BufferedWriter(new FileWriter("cartele.out")));
20
21 st.nextToken(); n=(int)st.nval;
22 st.nextToken(); c=(int)st.nval;
23
24 for(i=1;i<=n;i++) // citesc sablonul
25 for(j=1;j<=n;j++)
26 {
27 st.nextToken(); a[i][j]=(int)st.nval;
28 }
29
30 for(k=1;k<=c;k++)
31 {
32 for(i=1;i<=n;i++) // citesc cartela k
33 for(j=1;j<=n;j++)
34 {
35 st.nextToken(); b[i][j]=(int)st.nval;
36 }
37
38 if(egale(1,0,0,0, 0,1,0,0)) {out.println(1); continue;} // direct
39 if(egale(0,1,0,0, 0,0,1,0)) {out.println(1); continue;} // rotit cu 90 (ceas!)
40 if(egale(0,0,1,0, 0,0,0,1)) {out.println(1); continue;} // rotit cu 180 (ceas!)
41 if(egale(0,0,0,1, 1,0,0,0)) {out.println(1); continue;} // rotit cu 270 (ceas!)
42 if(egale(1,0,0,0, 0,0,0,1)) {out.println(1); continue;} // invers + direct
43 if(egale(0,0,0,1, 0,0,1,0)) {out.println(1); continue;} // invers + rotit 90 (ceas
!)
44 if(egale(0,0,1,0, 0,1,0,0)) {out.println(1); continue;} // invers + rotit 180 (
ceas!)
45 if(egale(0,1,0,0, 1,0,0,0)) {out.println(1); continue;} // invers + rotit cu 270 (
ceas!)
46
47 out.println(0); // nu s-a potrivit
48 } // for k
49 out.close();
50 }// main
51
52 static boolean egale(int i1,int j1, int ni1, int nj1, int i2, int j2, int ni2, int nj2
)
53 {
54 int i,j;
55 boolean ok=true;
56 for(i=1;i<=n&&ok;i++)
57 for(j=1;j<=n&&ok;j++)
58 if(a[i][j]
59 !=
60 b[i*i1+j*j1+(n-i+1)*ni1+(n-j+1)*nj1][i*i2+j*j2+(n-i+1)*ni2+(n-j+1)*nj2])
61 ok=false;
62 return ok;
63 }// egale(...)
64 }// class
16.2 Paritate
În vederea asigurării unei transmiteri cât mai exacte a informaţiilor pe reţea, transmiterea se
efectuează caracter cu caracter, fiecare caracter fiind dat prin codul său ASCII, adică o grupă de
8 biţi (octet). Pentru fiecare 8 biţi transmişi se calculează un bit de paritate care are valoarea 0
(dacă codul ASCII al caracterului conţine un număr par de cifre binare 1) sau 1 (ı̂n caz contrar).
Deoarece ı̂n problema noastră se transmit numai caractere ASCII standard, cu codul ASCII
din intervalul [32,127], codul lor ASCII are bitul 7 (primul bit din stn̂ga) egal cu 0. Pe această
poziţie va fi pus bitul de paritate, economisind astfel câte un bit pentru fiecare caracter transmis.
CAPITOLUL 16. OJI 2007 16.2. PARITATE 419
De exemplu, dacă mesajul care trebuie trasmis conţine caracterele ”Paritate”, succesiunea de biţi
transmisă va fi:
În plus, pe lângă caracterele amintite, ı̂n mesaj mai poate să apară un caracter special care
indică trecerea la ı̂nceputul unui nou rând. Acest caracter are codul ASCII 10.
Cerinţă
Să se scrie un program care să verifice dacă un text a fost sau nu transmis corect.
Date de intrare
Fişierul de intrare paritate.in are pe prima linie o succesiune de caractere ’0’ şi ’1’ care
reprezintă mesajul transmis. Între caractere nu există spaţii. Linia se termină cu caracterul
marcaj de sfârşit de linie (newline).
Date de ieşire
Fişierul de ieşire paritate.out are pe prima linie mesajul DA dacă textul a fost transmis corect
sau NU ı̂n caz contrar. În cazul ı̂n care mesajul de pe prima linie este DA liniile următoare vor
conţine textul transmis ı̂n clar. În cazul ı̂n care mesajul de pe prima linie este NU linia următoare
va conţine numerele de ordine ale caracterelor care nu au fost transmise corect, ı̂n ordine strict
crescătoare, separate prin câte un spaţiu.
Restricţii şi precizări
a Cei 8 biţi ai codului ASCII a unui caracter se numerotează de la 0 la 7, de la dreapta la
stânga, cel mai din stânga bit fiind bitul 7 iar cel mai din dreapta bitul 0.
a Textul transmis are cel mult 60000 caractere.
a Numărul de caractere ’0’ şi ’1’ din prima linie a fişierului de intrare este multiplu de 8.
a Codurile ASCII ale caracterelor din text aparţin mulţimii r10, 32 127x, codul 10 ı̂nsemnând
trecerea la ı̂nceputul unui rând nou.
a Nici o linie din fişierul de iec sire nu va avea mai mult de 255 caractere.
a Caracterele din text sunt numerotate ı̂ncepând de la 0.
a mesajele DA/NU din prima linie a fişierului de ieşire se scriu cu majuscule.
Exemplul 1:
paritate.in
0101000011100001011100100110100101110100111000010111010001100101
paritate.out Explicaţie
DA Toate codurile sunt
Paritate
Exemplul 2:
paritate.in
1101000011100001111100100110100101110100111000010111010011100101
paritate.out Explicaţie
NU Primul caracter a fost transmis ca succesiunea de biţi 11010000
027 ceea ce ı̂nseamnă că fără bitul de paritate ar fi trebuit să existe
un număr impar de cifre 1, ceea ce este fals. Deci caracterul nu
a fosttransmis corect. Acelaşi lucru se verifică şi pentru
caracterele cu numerele de ordine 2 şi 7.
Exemplul 3:
paritate.in
010000011111101001101001000010100110010100001010011010100110111101101001
paritate.out Explicaţie
DA Toate codurile sunt corecte.
Azi În text există două caractere cu cod ASCII 10
e
joi
Timp maxim de execuţie/test: 1 secundă
CAPITOLUL 16. OJI 2007 16.2. PARITATE 420
int main()
{
f=fopen("paritate.in", "rt");
g=fopen("paritate.out", "wt");
while (c!=’\n’)
{
i++; //pozitie caracter
BitP=c-’0’; //bitul de paritate
Cod=0; //aici formez codul
Nr1=0; //cati de 1
a[i]=1; //pun 1
Eroare=i; //si retin pozitia
}
fscanf(f, "%c", &c);
}
fclose(g);
return 0;
}
Varianta 1: Versiunea este o prelucrare a variantei oficiale; comentariile din sursa au rămas
nemodificate pentru că sunt un exemplu bun!
38 Bit=c-’0’;
39 if(Bit==1) Nr1++; // daca e 1 il numar
40 Cod=Cod*2+Bit; // formez codul
41 }
42
43 if((Nr1+BitP)%2==0) // daca cod corect
44 a[i]=Cod; // pun caracterul in vector
45 else // altfel
46 {
47 a[i]=1; // pun 1
48 Eroare=i; // si retin pozitia
49 }
50 ++k;
51 }// while
52
53 if(Eroare==0) // daca nu sunt erori
54 {
55 out.println("DA"); // scrie DA si
56 for(j=0;j<=i;j++) // afiseaza cele i+1 caractere
57 if(a[j]==10) // avand grija la caracterul cu codul 10
58 out.println();
59 else // altfel
60 out.print((char)a[j]); // scrie caracterul
61 }
62 else // eroare!!!
63 {
64 out.println("NU"); // scrie NU si
65 for(j=0;j<Eroare;j++)
66 if(a[j]==1) // cauta erorile - cod 01
67 out.print(j+" "); // si afiseaza pozitia lor
68 out.println(Eroare); // afiseaza pozitia ultimei erori
69 }
70
71 out.close();
72 }// main
73 }// class
Capitolul 17
OJI 2006
17.1 Flori
Cristina Bohm
Fetiţele din grupa mare de la grădiniţă culeg flori şi vor să ı̂mpletească coroniţe pentru festivi-
tatea de premiere. În grădină sunt mai multe tipuri de flori. Fiecare dintre cele n fetiţe culege un
buchet având acelaşi număr de flori, ı̂nsă nu neapărat de acelaşi tip. Pentru a ı̂mpleti coroniţele
fetiţele se ı̂mpart ı̂n grupe. O fetiţă se poate ataşa unui grup numai dacă are cel puţin o floare de
acelaşi tip cu cel puţin o altă fetiţă din grupul respectiv.
Cerinţă
Fiind dat un număr natural n reprezentând numărul fetiţelor şi numărul natural k reprezentând
numărul de flori dintr-un buchet, să se determine grupele care se formează.
Date de intrare
Fişierul de intrare flori.in conţine pe prima linie, separate printr-un spaţiu, numerele naturale
n şi k, reprezentând numărul de fetiţe şi respectiv numărul de flori din fiecare buchet. Fiecare
dintre următoarele n linii conţine, pentru fiecare fetiţă, câte k valori separate prin câte un spaţiu
reprezentând tipurile de flori culese.
Date de ieşire
Fişierul de ieşire flori.out va conţine pe fiecare linie câte o grupă formată din numerele de
ordine ale fetiţelor separate prin câte un spaţiu, ı̂n ordine crescătoare, ca ı̂n exemplu.
Restricţii şi precizări
a 1 & n & 150
a 1 & k & 100
a Tipul unei flori este un număr ı̂ntreg din intervalul 0, 100.
a Într-o grupă numerele de ordine ale fetiţelor trebuie date ı̂n ordine strict crescătoare.
a În fişierul de ieşire grupele vor fi afişate ı̂n ordinea crescătoare a numărului de ordine al
primei fetiţe din grupă.
Exemplu
flori.in flori.out Explicaţie
54 134 Fetiţele 1 şi 3 au cules amândouă flori de tipul 1,
1234 2 iar fetiţele 1 şi 4 au cules amândouă flori de tipurile
5696 5 2,3 şi 4, deci toate cele trei fetiţe (1, 3, 4) se vor afla
1111 ı̂n aceeaşi grupă. Fetiţele 2 şi 5 vor forma fiecare câte
2443 o grupă deoarece nu au cules flori de acelaşi tip cu
7777 nici una dintre celelalte fetiţe.
Timp de rulare/test: 1 secundă
Soluţia comisiei
423
CAPITOLUL 17. OJI 2006 17.1. FLORI 424
- construiesc matricea a definita astfel : pe linia i sunt tipurile distincte de flori ale fetiţei cu
numărul de ordine i
- a[i][0] = numărul de elemente de pe linia i; acesta va deveni 0 dacă linia a fost reunită
ı̂n altă linie
- vectorul viz are n elemente şi pe parcursul prelucrării , fetiţele care ajung ı̂n aceeaşi grupă
vor avea aceeaşi valoare ı̂n vectorul viz: de exemplu, dacă fetiţa 3 ajunge ı̂n grupa ı̂n care
e fetiţa 1 atunci viz[3]=viz[1];
- inţial viz[i]=i ı̂nsemnând că fiecare fetiţă e ı̂n grupă doar ea cu ea;
- apelul irelj(i,j) verifică dacă i e ı̂n relaţie cu j: caută pe linia i şi j un tip de floare
comun fetiţelor i şi j
- funcţia reuneste face reuniunea mulţimilor de pe liniile i şi j ı̂n linia i; dacă s-a
făcut o astfel de reuniune, scad i (i ) şi astfel se rezolvă situaţia ı̂n care de exemplu
i rel j, not ( i rel k) , j rel k; executând i--, k va ajunge tot ı̂n grupă cu
i; altfel k ar ajunge ı̂n altă grupă
- afişarea grupelor presupune selectarea din vectorul viz a poziţiilor care au aceeaşi valoare:
toate poziţiile i care au viz[i]=1 (de exemplu) sunt ı̂n prima grupă; pun pe 0 poziţiile
afişate pentru a nu le mai relua o dată.
FILE *f=fopen("Flori.In","r");
FILE *g=fopen("Flori.Out","w");
int n,k,a[150][150];
for(j=1;j<=lg;j++)
if (val==a[linie][j])
return 1;
return 0;
}
int main()
{
int viz[150],i,j,val,ok;
fscanf(f,"%d %d",&n,&k);
for(i=1;i<=n;i++)
for(j=1;j<=k;j++)
{
fscanf(f,"%d",&val);
if(!apartine(val,i))
{
a[i][0]++; // pe prima coloana am nr. de tipuri distincte
// de flori
a[i][ a[i][0] ]=val; // in multimea de pe linia i am tipurile
// distincte de flori al fetitei i
}
}
for(i=1;i<=n;i++)
viz[i]=i; //initial exista n grupe
for(i=1;i<=n;i++)
{
ok=0;
if(a[i][0])
{
for(j=i+1;j<=n;j++)
if(irelj(i,j))
{
viz[j]=viz[i]; //j trebuie sa ajunga in grupa cu i
reuneste(i,j); //reunesc in linia i linia j
a[j][0]=0;//consider ca in multimea j am 0 elemente acuma
ok=1;
}
}
for(i=1;i<=n;i++)
if(viz[i])
{
fprintf(g,"%d ",i);
for(j=i+1;j<=n;j++)
if(viz[i]==viz[j])
{
fprintf(g,"%d ",j);
viz[j]=0; //ca sa nu mai fie prelucrat
}
fprintf(g,"\n");
}
fclose(g);
return 0;
}
Variantă iterativă:
Variantă recursivă:
Listing 17.1.3: flori2.java
1 import java.io.*;
2 class Flori2
3 {
4 static int n,k,ng;
5 static char[][] a=new char[151][101];
6 static int[] gf=new int[151];
7
8 public static void main(String[] args) throws IOException
9 {
10 int i,j,fij;
11 StreamTokenizer st=new StreamTokenizer(
12 new BufferedReader(new FileReader("flori.in")));
13 PrintWriter out=new PrintWriter(
CAPITOLUL 17. OJI 2006 17.2. PLUTON 427
17.2 Pluton
Marinel Şerban
În timpul acţiunii ”Furtună ı̂n deşert” din cauza unei furtuni de nisip, n soldaţi s-au rătăcit de
plutoanele lor. După trecerea furtunii se pune problema regrupării acestora pe plutoane. Pentru
aceasta se folosesc plăcuţele de identificare pe care soldaţii le poartă la gât. Pe aceste plăcuţe
sunt scrise numere care pot identifica fiecare soldat şi plutonul din care acesta face parte. Astfel,
soldaţii din acelaşi pluton au numărul de identificare format din aceleaşi cifre, dispuse ı̂n altă
ordine şi numerele de identificare sunt unice. De exemplu, numerele de identificare 78003433,
83043073, 33347008 indică faptul că cei trei soldaţi care le poartă fac parte din acelaşi pluton.
Cerinţă
Fiind date cele n numere de pe plăcuţele de identificare, să se regrupeze cei n soldaţi pe
plutoane, indicându-se numărul de plutoane găsite (un pluton refăcut trebuie să aibă minimum
un soldat), numărul de soldaţi din cel mai numeros pluton, numărul de plutoane care au acest
număr maxim de soldaţi precum şi componenţa unui astfel de pluton (cu număr maxim de soldaţi
regrupaţi).
Date de intrare
Fişierul de intrare pluton.in conţine pe prima linie numărul n de soldaţi recuperaţi, iar pe
fiecare dintre următoarele n linii câte un număr de identificare a celor n soldaţi.
Date de ieşire
Fişierul de ieşire pluton.out va conţine pe prima linie numărul de plutoane refăcute. Linia a
doua va conţine numărul de soldaţi din cel mai numeros pluton refăcut. Linia a treia va conţine
numărul de plutoane care au numărul maxim de soldaţi recuperaţi. Linia a patra va conţine
CAPITOLUL 17. OJI 2006 17.2. PLUTON 428
componenţa unui astfel de pluton, cu număr maxim de soldaţi recuperaţi, numerele de identificare
ale soldaţilor din componenţă fiind scrise unul după altul separate prin câte un spaţiu.
Restricţii şi precizări
a 0 $ n & 4000
a 0 $ număr de identificare $ 2.000.000.000
Observaţii
Deoarece linia a patra conţine numerele de identificare ale soldaţilor unuia dintre plutoanele
cu un număr maxim de soldaţi, pot exista mai multe soluţii corecte. Se poate alege oricare dintre
acestea.
Se acordă punctaje parţiale astfel: pentru valoarea corectă de pe prima linie se acordă 30%
din punctaj; pentru valorile corecte de pe prima şi a doua linie se acordă 50% din punctaj, pentru
valorile corecte de pe prima, a doua şi a treia linie se acordă 70% din punctaj, iar pentru rezolvarea
corectă a tuturor cerinţelor se acordă punctajul integral aferent testului.
Exemplu
pluton.in pluton.out Explicaţie
10 6 Au fost recuperaţi soldaţi din 6 plutoane
1223 3 distincte, cei mai mulţi soldaţi recuperaţi
123 2 dintr-un pluton fiind ı̂n număr de 3.
666 321 312 123
321 Există 2 plutoane cu număr maxim de
7890 soldaţi recuperaţi (3), unul dintre ele
2213 fiind format din soldaţii cu numerele
312 321 312 123.
655
1000 De remarcat că şi soluţia
1322 1223 2213 1322 este corectă.
Timp de rulare/test: 1 secundă
Soluţia comisiei
Soluţia 1:
ı̂n timpul citirii creez un nou vector care conţine pe poziţiile corespunzătoare numerele de
identificare din vectorul iniţial ı̂n ordinea descrescatoare a cifrelor
ı̂n etapa a doua se parcurge vectorul nou format grupând toate numerele de identificare
identice; după formarea unui grup (pluton) se determină mărimea acestuia reţinâdu-se acesta
dacă e cel mai numeros găsit până ı̂n acel moment sau contorizându-l dacă numărul de soldaţi
determinat este egal cu cel maxim determinat anterior.
Soluţia 2:
citesc numerele de identificare ı̂n vectorul a[]
detectez numărul maxim de elemente ale unui pluton şi reţin maximul
se utilizeaza notiunea de lista: dintr-o lista fac parte toti membrii unui pluton
se construieste un vector ajutator care retine pentru fiecare element elementul care il urmeaza
in lista
pe parcursul formarii listelor se determina lista cea mai numeroasa precum si numarul de
liste de acest tip
afisarea se face utilizand informatiile din vetorul urm
long a[MAX];
unsigned char b[MAX], c[MAX], Pus[MAX];
long G[MAX][2];
unsigned char cifre[10], cifreTest[10];
long n, Nre, NrG, maxe, btest, ctest;
unsigned char OK;
int ToatePuse(void)
{
int i, x;
x = TRUE;
for (i=1; i<=n; i++)
x*=Pus[i];
return x;
}
void Citire(void)
{
int i, j;
long x;
void Sortare(void)
{
int i, j;
long min, pm, x, aux;
void FormarePlutoane(void)
{
int i, j;
void Afisare(void)
{
int i, j;
long x;
Nre=0;
for (i=1; i<=NrG; i++)
if (G[i][0]==maxe)
Nre++;
i=1;
while (x!=a[i]) i++; //il caut in a
fprintf(Fout, "%ld ", a[i]); //scriu reprezentantul plutonului
btest=b[i]; ctest=c[i]; //retin caracteristicile
Cifrele(a[i], cifreTest); //aflu cifrele
i++; //cerinta 4
fprintf(Fout, "\n");
}
int main(void)
{
Fin=fopen("pluton.in", "rt"); //deschid fisierul ’pluton.in’ pentru citire
Fout=fopen("pluton.out", "wt");//deschid fisierul ’pluton.out’ pentru scriere
Citire();
Sortare();
FormarePlutoane();
Afisare();
fclose(Fin);
fclose(Fout);
return 0;
}
#include <conio.h>
#include <cstring>
while (a)
{ x[a%10]++;
a/=10;
}
while (b)
{ y[b%10]++;
b/=10;
}
for (i=0;i<10;i++)
if (x[i]!=y[i])
return 0;
return 1;
}
int main()
{ long int x[4000],t;
int y[4000],z[4000],a[4000],b[4000],urm[4000],max,q,i1,j,p;
memset(b,0,sizeof(b));
memset(urm,0,sizeof(urm)); // urm[i] memoreaza indicele urmatorului soldat
// din acelasi pluton cu i
int n,i,k;
ifstream f("pluton.in");
for (i=0;i<n;i++)
{ f>>x[i];
k=0;
t=x[i];
y[i]=0; z[i]=0;
while (t)
{ y[i]=y[i] | (1<<(t%10));
t=t/10; z[i]++;
}
}
for (j=i+1;j<n;j++)
if ((a[j]==0)&&(y[i]==y[j])&&(z[i]==z[j]))
{ if (verif(x[i],x[j]))
{ a[j]=t;
urm[i1]=j;
i1=j;
k++;
}
}
ofstream g("pluton.out");
g<<t<<endl;
g<<max<<endl;
g<<q<<endl;
i=p;
do
{ g<<x[i]<<" ";
i=urm[i];
} while (i!=0);
g.close();
f.close();
return 0;
}
int main()
{
long i,j,p,pmax;
long aux;
CAPITOLUL 17. OJI 2006 17.2. PLUTON 434
fstream f;
f.open("pluton.in",ios::in);
f>>n; //se citesc din fisierul pluton.in numarul n si
for(i=1;i<=n;i++) //elementele vectorului Nr si se completeaza valorile
{ //corespunzatoare in vectorul NrSort
f>>Nr[i];
NrSort[i]=SortareCifre(Nr[i]);
}
f.close();
i++; //s-au adus langa soldatul i toti posibilii sai colegi de pluton
nrp++; //deci s-a mai refacut un pluton intre pozitiile p si i-1 din
//cei doi vectori Nr si NrSort
if(i-p>maxx) //daca numarul de soldati din ultimul pluton refacut (adica i-p)
{ //este mai mare strict decat max se actualizeaza valorile
maxx=i-p; //variabilelor max si nrmax
nrmax=1;
pmax=p; //pmax retine pozitia la care incepe in vectorul Nr plutonul
} //cu numar maxim de soldati
else
if(i-p==maxx) nrmax++; //daca numarul de soldati din ultimul pluton refacut
} //este egal cu max se actualizeaza valoarea lui nrmax
return 0;
}
19
20 while(nr!=0) { fc[(int)(nr%10)]++; nr=nr/10; nc++; }
21
22 k=0;
23 for(i=9;i>=0;i--)
24 if(fc[i]!=0)
25 for(j=1;j<=fc[i];j++) { k++; x[k]=i; }
26
27 for(i=1;i<=nc;i++) nrcd=nrcd*10+x[i];
28 return nrcd;
29 }// cifre(...)
30
31 public static void main(String[] args) throws IOException
32 {
33 int i,j;
34 int max,npmax,pmax;
35
36 StreamTokenizer st=new StreamTokenizer(
37 new BufferedReader(new FileReader("9-pluton.in")));
38 PrintWriter out=new PrintWriter(
39 new BufferedWriter(new FileWriter("pluton.out")));
40
41 st.nextToken(); n=(int)st.nval;
42 for(i=1;i<=n;i++) {st.nextToken(); ni[i]=(int)st.nval;}
43
44 for(i=1;i<=n;i++) nid[i]=cifre(ni[i]);
45
46 np=0;
47 for(i=1;i<=n;i++)
48 {
49 if(nip[i]!=0) continue;
50 np++;
51 nip[i]=np;
52 for(j=i+1;j<=n;j++)
53 if(nip[j]==0)
54 if(nid[j]==nid[i]) nip[j]=np;
55 }
56
57 out.println(np);
58
59 for(i=1;i<=np;i++)
60 for(j=1;j<=n;j++) if(nip[j]==i) z[i]++;
61
62 max=0;
63 npmax=0;
64 pmax=0;
65
66 for(i=1;i<=np;i++) if(z[i]>max) {max=z[i];pmax=i;}
67 out.println(max);
68
69 for(i=1;i<=np;i++) if(z[i]==max) npmax++;
70 out.println(npmax);
71
72 for(i=1;i<=n;i++) if(nip[i]==pmax) out.print(ni[i]+" ");
73
74 out.println();
75 out.close();
76 }// main
77 }// class
12
13 static void nrcd(int nr,int i0)
14 {
15 int i,j,k,nc;
16 int nrcd1, nrcd2; // nr cu cifre descrescatoare = |nrcd1|nrcd2|
17
18 for(i=0;i<=9;i++) fc[i]=0;
19 for(i=0;i<=10;i++) x[i]=0;
20
21 nc=0;
22 while(nr!=0) { fc[(int)(nr%10)]++; nr=nr/10; nc++; }
23
24 k=0;
25 for(i=0;i<=9;i++)
26 if(fc[i]!=0)
27 for(j=1;j<=fc[i];j++) { k++; x[k]=i; }
28
29 nrcd1=0;
30 for(i=nc;i>=6;i--) nrcd1=nrcd1*10+x[i];
31
32 nrcd2=0;
33 for(i=5;i>=1;i--) nrcd2=nrcd2*10+x[i];
34
35 nid1[i0]=nrcd1;
36 nid2[i0]=nrcd2;
37 }// cifre(...)
38
39 public static void main(String[] args) throws IOException
40 {
41 int i, j, max, npmax, pmax;
42
43 StreamTokenizer st=new StreamTokenizer(
44 new BufferedReader(new FileReader("pluton.in")));
45 PrintWriter out=new PrintWriter(
46 new BufferedWriter(new FileWriter("pluton.out")));
47
48 st.nextToken(); n=(int)st.nval;
49 for(i=1;i<=n;i++) {st.nextToken(); ni[i]=(int)st.nval;}
50 for(i=1;i<=n;i++) nrcd(ni[i],i);
51
52 np=0;
53 for(i=1;i<=n;i++)
54 {
55 if(nip[i]!=0) continue;
56 np++;
57 nip[i]=np;
58 for(j=i+1;j<=n;j++)
59 if(nip[j]==0)
60 if((nid1[j]==nid1[i])&&(nid2[j]==nid2[i])) nip[j]=np;
61 }
62 out.println(np);
63
64 for(i=1;i<=np;i++)
65 for(j=1;j<=n;j++) if(nip[j]==i) z[i]++;
66
67 max=0; npmax=0; pmax=0;
68
69 for(i=1;i<=np;i++) if(z[i]>max) {max=z[i];pmax=i;}
70 out.println(max);
71
72 for(i=1;i<=np;i++) if(z[i]==max) npmax++;
73 out.println(npmax);
74
75 for(i=1;i<=n;i++) if(nip[i]==pmax) out.print(ni[i]+" ");
76
77 out.println(); out.close();
78 }// main
79 }// class
Capitolul 18
OJI 2005
18.1 Numere
Doru Popescu Anastasiu
Mircea este pasionat de programare. El a ı̂nceput să rezolve probleme din ce ı̂n ce mai
grele. Astfel a ajuns la o problemă, care are ca date de intrare un tablou pătratic cu n linii şi
2
n coloane, componente tabloului fiind toate numerele naturale distincte de la 1 la n . Pentru a
verifica programul pe care l-a scris ı̂i trebuie un fişier care să conţină tabloul respectiv. După ce a
creat acest fişier, fratele său, pus pe şotii ı̂i umblă ı̂n fişier şi ı̂i schimbă câteva numere consecutive,
cu numărul 0. Când se ı̂ntoarce Mircea de la joacă constată cu stupoare că nu ı̂i merge programul
pentru testul respectiv. După câteva ore de depanare ı̂şi dă seama că programul lui este corect şi
că fişierul de intrare are probleme.
Cerinţă
Scrieţi un program care să-l ajute pe Mircea, găsindu-i cel mai mic şi cel mai mare dintre
numerele consecutive schimbate de fratele său.
Date de intrare
În fişierul numere.in se dă pe prima linie n, iar pe următoarele n linii elementele tabloului,
câte n elemente pe o linie, separate ı̂ntre ele prin câte un spaţiu, după modificările făcute de fratele
lui Mircea.
Date de ieşire
În fişierul numere.out se va scrie pe un singur rând cu un singur spaţiu ı̂ntre ele numerele
cerute (primul fiind cel mai mic).
Restricţii şi precizări
a 0 $ n & 500.
a Fratele lui Mircea schimbă cel puţin un număr ı̂n fişier.
a Numerele schimbate de fratele lui Mircea sunt mai mici sau cel mult egale cu 60000.
Exemplu
numere.in numere.out Explicaţie
3 24 În fişierul de intrare au fost ı̂nlocuite cu 0
507 numerele 2, 3, 4.
001
698
Timp maxim de execuţie: 1 secundă/test
Soluţia oficială
Se foloseşte un vector cu componente 0 sau 1, x x1 , ..., xm , unde m este 64000 dacă
2 2
numărul de componente al tabloului (n ) este mai mare decât 64000, respectiv m n ı̂n celălalt
caz.
Iniţial vectorul x are toate componentele 0. Pe măsură ce se citesc numere v din fişier, com-
ponentele corespunzătoare din x se schimbă ı̂n 1 (xv 1).
437
CAPITOLUL 18. OJI 2005 18.1. NUMERE 438
După citirea numerelor din fişier se obţine ı̂n x o secvenţă de 0. Indicii corespunzători acestei
secvenţe formează mulţimea de numere consecutive care au fost ı̂nlocuite cu 0 de fratele lui Mircea.
O altă modalitate de rezolvare constă ı̂n calculul sumei tuturor numerelor din tablou şi
obţinerea astfel a sumei secvenţei de numere consecutive şterse de Mircea. Din păcate sumele
sunt prea mari şi depăşesc tipurile predefinite. Dacă se folosesc implementări pe numere mari se
obţine punctajul maxim, altfel doar jumătate din punctaj.
Soluţie prezentată ı̂n GInfo nr. 15/3
Pentru rezolvarea acestei probleme este suficient să utilizăm un şir de biţi care vor indica dacă
un număr apare sau nu ı̂n fişier.
Vom avea nevoie ı̂ntotdeauna de cel mult 250.000 de biţi, deci 31250 de octeţi.
Iniţial toţi biţii vor avea valoarea 0, iar pe măsură ce sunt citite numerele care formează
matricea, valoarea bitului corespunzător unui număr citit devine 1.
Şirul de biţi va conţine ı̂n final o secvenţă de zerouri care va reprezenta soluţia problemei.
Există şi posibilitatea de a utiliza heap-ul pentru a păstra 250.000 de valori booleene sau ı̂ntregi
care vor permite şi ele determinarea secvenţei care lipseşte.
18.2 MaxD
Maria şi Adrian Niţă
Fiind elev ı̂n clasa a IX-a, George, ı̂şi propune să studieze capitolul divizibilitate cât mai
bine. Ajungând la numărul de divizori asociat unui numă natural, constată că sunt numere ı̂ntr-un
interval dat, cu acelaşi număr de divizori.
De exemplu, ı̂n intervalul 1, 10, 6, 8 şi 10 au acelaşi număr de divizori, egal cu 4. De asemenea,
4 şi 9 au acelaşi număr de divizori, egal cu 3 etc.
Cerinţă
Scrieţi un program care pentru un interval dat determină care este cel mai mic număr din
interval ce are număr maxim de divizori. Dacă sunt mai multe numere cu această proprietate se
cere să se numere câte sunt.
Date de intrare
Fişierul de intrare maxd.in conţine pe prima linie două numere a şi b separate prin spaţiu
(a & b) reprezentând extremităţile intervalului.
Date de ieşire
Fişierul de ieşire maxd.out va conţine pe prima linie trei numere separate prin câte un spaţiu
min nrdiv contor cu semnificaţia:
min = cea mai mică valoare din interval care are număr maxim de divizori
nrdiv = numărul de divizori ai lui min
contor = câte numere din intervalul citit mai au acelaşi număr de divizori egal cu nrdiv
Restricţii şi precizări
a 1 & a & b & 1.000.000.000
a 0 & b a & 10.000
Punctaj
Dacă aţi determinat corect min, obţineţi 50% din punctaj.
Dacă aţi determinat corect nrdiv, obţineţi 20% din punctaj
Dacă aţi determinat corect contor, obţineţi 30% din punctaj.
Exemple
CAPITOLUL 18. OJI 2005 18.2. MAXD 440
Varianta 1:
15 st.nextToken(); b=(int)st.nval;
16 x=new int[b-a+2];
17 for(i=a;i<=b;i++) x[i-a+1]=descfact(i);
18 int max=-1;
19 int imax=-1;
20 for(i=1;i<=b-a+1;i++)
21 if(x[i]>max) { max=x[i]; imax=i; }
22 int nrmax=0;
23 for(i=1;i<=b-a+1;i++) if(x[i]==max) nrmax++;
24
25 PrintWriter out=new PrintWriter(
26 new BufferedWriter(new FileWriter("maxd.out")));
27 out.println((imax+a-1)+" "+max+" "+nrmax);
28 out.close();
29 t2=System.currentTimeMillis();
30 System.out.println("Timp = "+(t2-t1));
31 }
32
33 static int descfact(int nr)
34 {
35 int d;
36 int nrd;
37 int p=1;
38
39 d=2;
40 nrd=0;
41 while(nr%d==0) { nrd++; nr=nr/d; }
42 p=p*(nrd+1);
43
44 d=3;
45 while(d*d<=nr)
46 {
47 nrd=0;
48 while(nr%d==0) { nrd++; nr=nr/d; }
49 p=p*(nrd+1);
50 d=d+2;
51 }
52 if(nr>1) p*=2;
53 return p;
54 }
55 }
Varianta 2:
OJI 2004
19.1 Expresie
Exemple
exp.in exp.out exp.in exp.out
2 0 2 1
4 4 24
32 81 100 19 32 81 100 18 33
51
Timp maxim de execuţie: 1 secundă/test
443
CAPITOLUL 19. OJI 2004 19.1. EXPRESIE 444
Dacă toate elementele ı̂ndeplinesc această condiţie, expresia este un număr natural şi se trece
la afişare.
Se poate renunţa la vectorul de 30000 de elemente, păstrându-se ı̂n locul acestuia un vector
ı̂n care se memorează numerele prime mai mici decât 30000 şi un vector care arată la ce puteri
apar aceste numere ı̂n descompunere. Această abordare introduce ı̂n plus operaţii pentru a găsi
indicele unui anumit număr prim; se poate folosi cu succes căutarea binară. Pe de altă parte, la
descompunerea ı̂n factori primi se va testa numai ı̂mpărţirea prin numere prime.
Analiza complexităţii Ó
Descompunerea unui număr x ı̂n factori primi are ordinul de complexitate O x, dacă nu se
foloseşte lista de numere prime.
Ó Pasul de descompunere şi completare a vectorului P are deci ordinul de complexitate O n
30000.
Citirea datelor, verificarea dacă expresia este un număr natural şi afişarea au ordinul de com-
plexitate O n.
Ó al algoritmului de rezolvare a acestei probleme este O n,
În concluzie, ordinul de complexitate
dacă facem abstracţie de constanta 30000.
49 end;
50 end;
51
52 begin
53 cit;
54 prime;
55
56 for i := 1 to dp do
57 f[i] := 0;
58 for i := 1 to n do
59 begin
60 j := 1;
61 while x[i]<>1 do
62 begin
63 while x[i] mod p[j]=0 do
64 begin
65 inc(f[j]);
66 x[i] := x[i] div p[j];
67 end;
68 inc(j);
69 end;
70 end;
71 sw := true;
72 for i := 1 to dp do
73 if f[i] mod m<>0 then
74 begin
75 sw := false;
76 break;
77 end;
78
79 assign(ff, ’exp.out’); rewrite(ff);
80 if not sw then
81 writeln(ff, 0)
82 else
83 begin
84 writeln(ff, 1);
85 for i := 1 to dp do
86 if f[i]>0 then
87 writeln(ff, p[i], ’ ’, f[i] div m);
88 end;
89 close(ff);
90 end.
Prima variantă:
25 {
26 out.println(1);
27 for(i=2;i<30000;i++)
28 if(p[i]!=0) out.println(i+" "+p[i]);
29 }
30 out.close();
31 }
32
33 static void descfact(int nr)
34 {
35 int d=2;
36 while(nr%d==0)
37 {
38 p[d]++;
39 nr=nr/d;
40 }
41 d=3;
42 while(d<=nr)
43 {
44 while(nr%d==0) { p[d]++; nr=nr/d; }
45 d=d+2;
46 }
47 }
48 }
A doua variantă:
48 }
49 ok=true;
50 for(i=1;i<=nnp;i++)
51 if(e[i]%m==0) e[i]=e[i]/m; else {ok=false; break;}
52 }
53
54 static void prime()
55 {
56 int i,j;
57 p[1]=2; p[2]=3; nnp=2;
58 i=5;
59 while(i<valmax)
60 {
61 if(estePrim(i)) p[++nnp]=i;
62 i+=2;
63 }
64 }
65
66 static boolean estePrim(int nr) // folosind lista numerelor prime !
67 {
68 int i=1;
69 while((p[i]*p[i]<nr)&&(nr%p[i]!=0)) i++;
70 if(p[i]*p[i]>nr) return true; return false;
71 }
72 }
A treia variantă:
19.2 Reactivi
Se ştie că, pentru a evita accidentele sau deprecierea reactivilor, aceştia trebuie să fie stocaţi
ı̂n condiţii de mediu speciale. Mai exact, pentru fiecare reactiv x, se precizează intervalul de
temperatură minx , maxx ı̂n care trebuie să se ı̂ncadreze temperatura de stocare a acestuia.
Reactivii vor fi plasaţi ı̂n frigidere.
Orice frigider are un dispozitiv cu ajutorul căruia putem stabili temperatura (constantă) care
va fi ı̂n interiorul acelui frigider (exprimată ı̂ntr-un număr ı̂ntreg de grade Celsius).
Cerinţă
Scrieţi un program care să determine numărul minim de frigidere necesare pentru stocarea
reactivilor chimici.
Date de intrare
Fişierul de intrare react.in conţine:
pe prima linie numărul natural N , care reprezintă numărul de reactivi;
pe fiecare dintre următoarele N linii se află min max (două numere ı̂ntregi separate printr-un
spaţiu); numerele de pe linia x 1 reprezintă temperatura minimă, respectiv temperatura maximă
de stocare a reactivului x.
Date de ieşire
Fişierul de ieşire react.out va conţine o singură linie pe care este scris numărul minim de
frigidere necesar.
Exemple
react.in react.out react.in react.out react.in react.out
3 2 4 3 5 2
-10 10 25 -10 10
-25 57 10 12
20 50 10 20 -20 10
30 40 7 10
78
Timp maxim de execuţie: 1 secundă/test
Notăm cu D numărul de temperaturi ı̂ntregi din intervalul care conţine temperaturile din
enunţ. Se observă că D este cel mult 201.
Citirea datelor de intrare are ordinul de complexitate O N .
Sortarea intervalelor după capătul din dreapta are ordinul de complexitate O N logN .
Urmează F paşi, unde F este numărul de frigidere selectate. Deoarece fiecare frigider este
setat la o temperatură ı̂ntreagă, F & D.
În cadrul unui pas, determinarea intervalului care se termină cel mai repede, pe baza vectorului
sortat, are ordinul de complexitate O 1. Eliminarea intervalelor care conţin un anumit punct
(sfârşitul intervalului care se termină cel mai repede) are ordinul de complexitate O N .
Afişarea rezultatului are ordinul de complexitate O 1.
În concluzie, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O N
D N logN ; deoarece ı̂n general D % logN , considerăm ordinul de complexitate ca fiind O N D.
struct Frigider
{
char min;
char max;
} f[10001], r[10001];
int Cate, i, j, N;
char min, max;
FILE* Fisier;
void Ordoneaza(void)
{ //sortare prin selectie
int i, j, ptmin;
char tmax, tmin;
for (i=1; i<=N-1; i++)
{
tmin=r[i].min; tmax=r[i].max; ptmin=i;
for (j=i+1; j<=N; j++)
if (r[j].min<tmin) //crescator dupa temperatura minima
{ tmax=r[j].max; tmin=r[j].min; ptmin=j; }
else
if (r[j].min==tmin)//pentru temperatura minima egala
if (r[j].max>tmax) //descrescator dupa cea maxima
{ tmax=r[j].max; tmin=r[j].min; ptmin=j; }
r[ptmin].min=r[i].min; r[ptmin].max=r[i].max;
r[i].min=tmin; r[i].max=tmax;
}
}
int main()
{
CAPITOLUL 19. OJI 2004 19.2. REACTIVI 453
char x, y;
Fisier=fopen("reactivi.in", "rt");
fscanf(Fisier, "%d", &N);
for (i=1; i<=N; i++)
fscanf(Fisier, "%d %d", &r[i].min, &r[i].max, &x, &y);
Ordoneaza(); //ordonez intervalele de temperatura
//crescator dupa temperatura minima si
//descrescator dupa temperatura maxima
fclose(Fisier);
Fisier=fopen("reactivi.out", "wt");
fprintf(Fisier, "%d\n", Cate);
fclose(Fisier);
return 0;
}
39 Else
40 If (f[i].min<=min) And (f[i].max>=max) Then
41 Begin Cauta := i; Break End
42 Else
43 If (f[i].max<min) Or (f[i].min>max) Then
44 Cauta := -1
45 End;
46 End;
47
48 Procedure Intersectie(j: Integer; min, max: ShortInt);
49 Begin
50 f[j].min := Maxim(min, f[j].min);
51 f[j].max := Minim(max, f[j].max)
52 End;
53
54 Procedure Ordoneaza;
55 Var i, j, ptmin: Integer;
56 tmax, tmin: ShortInt;
57 Begin
58 For i := 1 To N-1 Do
59 Begin
60 tmin := r[i].min; tmax := r[i].max; ptmin := i;
61 For j := i+1 To N Do
62 If r[j].min<tmin Then
63 Begin
64 tmax := r[j].max; tmin := r[j].min; ptmin := j
65 End
66 Else
67 If r[j].min=tmin Then
68 If r[j].max>tmax Then
69 Begin
70 tmax := r[j].max; tmin := r[j].min; ptmin := j
71 End;
72 r[ptmin].min := r[i].min; r[ptmin].max := r[i].max;
73 r[i].min := tmin; r[i].max := tmax
74 End
75 End;
76
77
78 Begin
79 Assign(Fisier, ’reactivi.in’); Reset(Fisier);
80 ReadLn(Fisier, N);
81 For i := 1 To N Do
82 ReadLn(Fisier, r[i].min, r[i].max);
83 Ordoneaza;
84 f[1].min := r[1].min; f[1].max := r[1].max; Cate := 1;
85 For i := 2 To N Do
86 Begin
87 min := r[i].min; max := r[i].max;
88 j := Cauta(min, max);
89 If j>0 Then
90 Intersectie(j, min, max)
91 Else
92 Begin
93 Inc(Cate);
94 f[Cate].min := min;
95 f[Cate].max := max
96 End
97 End;
98 Close(Fisier);
99 Assign(Fisier, ’reactivi.out’); ReWrite(Fisier);
100 WriteLn(Fisier, Cate);
101 Close(Fisier)
102 End.
OJI 2003
20.1 Text
Vasile lucrează intens la un editor de texte. Un text este format din unul sau mai multe
paragrafe. Orice paragraf se termină cu Enter şi oricare două cuvinte consecutive din acelaşi
paragraf sunt separate prin spaţii (unul sau mai multe). În funcţie de modul de setare a paginii,
numărul maxim de caractere care ı̂ncap ı̂n pagină pe o linie este unic determinat (Max).
Funcţia pe care Vasile trebuie să o implementeze acum este alinierea ı̂n pagină a fiecărui
paragraf din text la stânga şi la dreapta. Pentru aceasta el va trebui să ı̂mpartă fiecare paragraf
ı̂n linii separate de lungime Max (fiecare linie terminată cu Enter).
Împărţirea se realizează punând numărul maxim posibil de cuvinte pe fiecare linie, fără
ı̂mpărţirea cuvintelor ı̂n silabe.
Pentru aliniere stânga-dreapta, Vasile trebuie să repartizeze spaţii ı̂n mod uniform ı̂ntre cu-
vintele de pe fiecare linie, astfel ı̂ncât ultimul caracter de pe linie să fie diferit de spaţiu, iar
numărul total de caractere de pe linie să fie egal cu Max. Excepţie face numai ultima linie din
paragraf, care rămâne aliniată la stânga (cuvintele fiind separate printr-un singur spaţiu, chiar
dacă linia nu este plină).
În general, este puţin probabil ca alinierea să fie realizabilă prin plasarea aceluiaşi număr de
spaţii ı̂ntre oricare două cuvinte consecutive de pe linie. Vasile consideră că este mai elegant ca,
dacă ı̂ntre unele cuvinte consecutive trebuie plasat un spaţiu ı̂n plus faţă de alte perechi de cuvinte
consecutive, acestea să fie plasate la ı̂nceputul liniei.
Cerinţă
Scrieţi un program care să citească lungimea unei linii şi textul dat şi care să alinieze textul
la stânga şi la dreapta.
Date de intrare
Fişierul de intrare text.in conţine pe prima linie Max, lungimea maximă a unui rând. Pe
următoarele linii este scris textul.
Date de ieşire
Fişierul de ieşire text.out conţine textul aliniat stânga-dreapta.
Restricţii şi precizŭri
a 2 & Max & 1000.
a Lungimea maximă a oricărui cuvânt din text este 25 caractere şi nu depăşeşte Max.
a Lungimea unui paragraf nu depăşeşte 1000 de caractere.
a Soluţia este unică.
Exemple
text.in text.out
20 Vasile are multe
Vasile are multe bomboane bune. bomboane bune.
Explicaţie
Pe prima linie au fost plasate câte 3 spaţii ı̂ntre cuvintele consecutive.
456
CAPITOLUL 20. OJI 2003 20.1. TEXT 457
text.in text.out
20 Ana are mere.
Ana are mere. Ion are multe pere
Ion are multe pere galbene? galbene?
Explicaţie
Între Ion şi are există 2 spaţii, ı̂ntre are şi multe - 2 spaţii, iar ı̂ntre multe şi pere - 1 spaţiu.
Observaţi că paragraful Ana are mere. (care are lungimea mai mică decât 20) a rămas aliniat
la stânga, iar ultima linie din fiecare paragraf rămâne aliniată la stânga, cuvintele consecutive
fiind separate printr-un singur spaţiu.
Timp maxim de executare: 1 secundă/test.
Fiecare paragraf se preia ı̂ntr-un vector de string-uri, elementele vectorului conţinând cuvintele
din paragraf. Se parcurge acest vector, ı̂ncepând cu prima poziţie, determinând cel mai mare
indice i1 care permite plasarea cuvintelor de pe poziţiile 1, ..., i1 pe acelaşi rând. Se destribuie
spaţiile disponibile, conform cerinţei problemei şi se afişează această linie. Se continuă prelucrarea
vectorului ı̂ncepând cu poziţia i1 1, şi aşa mai departe!
char prg[NMax];
ifstream fin(InFile);
ofstream fout(OutFile);
char cuv[NMax][LgMax];
int Max, nrl, LgLinie;
void afiseaza_paragraf(void);
void afiseaza_linie(int);
int main()
{
fin>>Max;
fin.get();
int gata=0;
while (!gata)
{
fin.getline(prg,NMax);
if (!fin.good())
gata=1;
else
afiseaza_paragraf();
}
fin.close();
fout.close();
return 0;
}
void afiseaza_paragraf()
{
char *p;
int LgCuv;
CAPITOLUL 20. OJI 2003 20.1. TEXT 458
LgLinie=-1;
nrl=0;
p=strtok(prg," ");
while (p)
{
LgCuv=strlen(p);
if (LgLinie+LgCuv+1<=Max)
{
strcpy(cuv[nrl],p);
nrl++;
LgLinie+=LgCuv+1;
}
else
{
afiseaza_linie(0);
nrl=1;
LgLinie=LgCuv;
strcpy(cuv[0],p);
}
p=strtok(NULL," ");
}
afiseaza_linie(1);
}
void afiseaza_linie(int u)
{
int i, j, cate, rest, nrsp;
if (u)
for (i=0; i<nrl-1; i++)
fout<<cuv[i]<<’ ’;
else
{
cate=Max-LgLinie;
rest=cate%(nrl-1);
nrsp=cate/(nrl-1);
if (nrl>0)
fout<<cuv[nrl-1]<<endl;
else
fout<<endl;
}
21
E cerul sus
Ca niste-ntinse brate
N-au crengile de ce sa se agate
0000:0000 32 31 0D 0A 45 20 63 65 72 75 6C 20 73 75 73 0D
0000:0010 0A 43 61 20 6E 69 73 74 65 2D 6E 74 69 6E 73 65
0000:0020 20 62 72 61 74 65 0D 0A 4E 2D 61 75 20 63 72 65
0000:0030 6E 67 69 6C 65 20 64 65 20 63 65 20 73 61 20 73
0000:0040 65 20 61 67 61 74 65 0D 0A 1A
CAPITOLUL 20. OJI 2003 20.1. TEXT 459
71 }
72 }
73
74 static void ultimulRand() throws IOException
75 {
76 int i;
77 out.print(s[cs]);
78 for(i=cs+1;i<=cd;i++) out.print(" "+s[i]);
79 out.println();
80 }
81 }
20.2 Numere
Gigel este un mare pasionat al cifrelor. Orice moment liber şi-l petrece jucându-se cu numere.
Jucându-se astfel, ı̂ntr-o zi a scris pe hârtie 10 numere distincte de câte două cifre şi a observat
că printre acestea există două submulţimi disjuncte de sumă egală.
Desigur, Gigel a crezut că este o ı̂ntâmplare şi a scris alte 10 numere distincte de câte două
cifre şi spre surpriza lui, după un timp a găsit din nou două submulţimi disjuncte de sumă egală.
Cerinţă
Date 10 numere distincte de câte două cifre, determinaţi numărul de perechi de submulţimi
disjuncte de sumă egală care se pot forma cu numere din cele date, precum şi una dintre aceste
perechi pentru care suma numerelor din fiecare dintre cele două submulţimi este maximă.
Date de intrare
Fişierul de intrare numere.in conţine pe prima linie 10 numere naturale distincte separate
prin câte un spaţiu x1 x2 ... x10 .
Date de ieşire
Fişierul de ieşire numere.out conţine trei linii. Pe prima linie se află numărul de perechi de
submulţimi de sumă egală, precum şi suma maximă obţinută, separate printr-un spaţiu. Pe linia
a doua se află elementele primei submulţimi, iar pe linia a treia se află elementele celei de a doua
submulţimi, separate prin câte un spaţiu.
NrSol Smax NrSol - numărul de perechi; Smax - suma maximă
x1 ... xk elementele primei submulţimi
y1 ... yp elementele celei de a doua submulţimi
Restricţii şi precizări
a 10 & xi , yi & 99, pentru 1 & i & 10.
a 1 & k, p & 9.
a Ordinea submulţimilor ı̂n perechi nu contează.
a Perechea de submulţimi determinată nu este obligatoriu unică.
Exemplu
numere.in numere.out
60 49 86 78 23 97 69 71 32 10 130 276
78 97 69 32
60 49 86 71 10
Explicaţie:
130 de soluţii; suma maximă este 276; s-au folosit 9 din cele 10 numere; prima submulţime are
4 elemente, a doua are 5 elemente.
Timp maxim de executare: 1 secundă/test
ifstream f("numere.in");
ofstream g("numere.out");
void citire()
{
int i;
for (i=1; i<=10; i++) f>>a[i];
f.close();
}
void rezolva()
{
int vi[11]={0,0,0,0,0,0,0,0,0,0,0}, vj[11]={0,0,0,0,0,0,0,0,0,0,0};
int i, ii, j, k, x, gasit, si, sj, jj, disjuncti;
int cati1;
si=0;
for (i=1; i<=10; i++)
if (vi[i]) si+=a[i];
for (i=1; i<=10; i++) vj[i]=vi[i];
for (jj=ii+1; jj<=1022; jj++)
{
j=10;
while(vj[j]) vj[j--]=0; //adun 1 la vj
vj[j]=1;
sj=0;
for(j=1; j<=10; j++)
if(vj[j])
sj+=a[j];
if(si==sj)
{
disjuncti=1;
if(disjuncti)
{
solutii++;
cati1=0;
for (i=1; i<=10; i++)
cati1+=(vi[i]+vj[i]);
if (cati1>UzMax)
if (si>Smax)
{
UzMax=cati1;
Smax=si;
for (i=1; i<=10; i++)
s1max[i]=vi[i];
for (i=1; i<=10; i++)
s2max[i]=vj[i];
}
}
CAPITOLUL 20. OJI 2003 20.2. NUMERE 462
}
}
}
}
void afisare()
{
int i;
g<<solutii<<" "<<Smax<<endl;
int main()
{
citire();
rezolva();
afisare();
return 0;
}
38 }// main
39
40 static int suma(int i)
41 {
42 int s=0,k=0;
43 for(k=0;k<=9;k++) if( (i&(1<<k)) != 0 ) s+=x[k];
44 return s;
45 }
46
47 static void afis(int i)
48 {
49 int k=0;
50 while(i!=0)
51 {
52 if(i%2!=0) out.print(x[k]+" ");
53 k++;
54 i/=2;
55 }
56 out.println();
57 }
58 }// class
Capitolul 21
OJI 2002
21.1 Poarta
Se consideră harta universului ca fiind o matrice cu 250 de linii şi 250 de coloane. În fiecare
celulă se găseşte o aşa numită poartă stelară, iar ı̂n anumite celule se găsesc echipaje ale porţii
stelare.
La o deplasare, un echipaj se poate deplasa din locul ı̂n care se află ı̂n oricare alt loc ı̂n care
se găseşte o a doua poartă, ı̂n cazul nostru ı̂n orice altă poziţie din matrice.
Nu se permite situarea simultană a mai mult de un echipaj ı̂ntr-o celulă. La un moment dat
un singur echipaj se poate deplasa de la o poartă stelară la alta.
Cerinţă
Dându-se un număr p (1 $ p $ 5000) de echipaje, pentru fiecare echipaj fiind precizate poziţia
iniţială şi poziţia finală, determinaţi numărul minim de deplasări necesare pentru ca toate echipa-
jele să ajungă din poziţia iniţială ı̂n cea finală.
Datele de intrare
Se citesc din fişierul text poarta.in ı̂n următorul format:
pe prima linie numărul natural p reprezentând numărul de echipaje,
pe următoarele p linii câte 4 numere naturale, primele două reprezentând coordonatele
poziţiei iniţiale a unui echipaj (linie coloană), următoarele două reprezentând coordonatele poziţiei
finale a aceluiaşi echipaj (linie coloană).
Datele de ieşire
Pe prima linie a fişierului text poarta.out se scrie un singur număr reprezentând numărul
minim de deplasări necesar.
Restricţii şi precizări
coordonatele poziţiilor iniţiale şi finale ale echipajelor sunt numere naturale din intervalul
1, 250;
poziţiile iniţiale ale celor p echipaje sunt distincte două câte două;
poziţiile finale ale celor p echipaje sunt distincte două câte două.
Exemplu
poarta.in poarta.out
3 4
1234
6539
3412
464
CAPITOLUL 21. OJI 2002 21.1. POARTA 465
Fie NrStationare numărul echipajelor staţionare (care au poziţiile iniţiale şi finale egale) şi
NrCircuite numărul circuitelor grafului orientat format astfel: nodurile sunt echipajele şi există
arc de la echipajul i la echipajul j dacă şi numai dacă poziţia finală a echipajului i coincide cu
poziţia iniţială a echipajului j.
Atunci NrMinDeplasari=p+NrCircuite-NrStationare.
21.2 Mouse
Un experiment urmăreşte comportarea unui şoricel pus ı̂ntr-o cutie dreptunghiulară, ı̂mpărţită
ı̂n m n cămăruţe egale de formă pătrată. Fiecare cămăruţă conţine o anumită cantitate de hrană.
Şoricelul trebuie să pornească din colţul 1, 1 al cutiei şi să ajungă ı̂n colţul opus, mâncând cât
mai multă hrană. El poate trece dintr-o cameră ı̂n una alăturată (două camere sunt alăturate dacă
au un perete comun), mănâncă toată hrana din cămăruţă atunci când intră şi nu intră niciodată
ı̂ntr-o cameră fără hrană.
Cerinţă
Stabiliţi care este cantitatea maximă de hrană pe care o poate mânca şi traseul pe care ı̂l poate
urma pentru a culege această cantitate maximă.
Datele de intrare
Fişierul de intrare mouse.in conţine pe prima linie două numere m şi n reprezentând numărul
de linii respectiv numărul de coloane ale cutiei, iar pe următoarele m linii cele m n numere
reprezentând cantitatea de hrană existentă ı̂n fiecare cămăruţă, câte n numere pe fiecare linie,
separate prin spaţii. Toate valorile din fişier sunt numere naturale ı̂ntre 1 şi 100.
Datele de ieşire
În fişierul de ieşire mouse.out se vor scrie
a pe prima linie două numere separate printr-un spaţiu: numărul de cămăruţe vizitate şi
cantitatea de hrană maximă culeasă;
a pe următoarele linii un traseu posibil pentru cantitatea dată, sub formă de perechi de numere
(linie coloană) ı̂ncepând cu 1 1 şi terminând cu m n.
Exemplu
mouse.in mouse.out
24 7 21
1263 11
3412 21
22
12
13
14
24
Explicaţie
CAPITOLUL 21. OJI 2002 21.2. MOUSE 467
Dacă m şi n sunt pare atunci numărul de cămăruţe vizitate este mn 1 iar cantitatea de hrană
maximă culeasă este suma cantităţilor de hrană din toate cămăruţele cu excepţia celei care are
cea mai mică cantitate şi se află pe linia i şi coloana j şi i j este număr impar. Traseul este
determinat de o parcurgere pe verticală sau orizontală şi ocolirea acelei cămăruţe.
Dacă m este impar atunci numărul de cămăruţe vizitate este mn iar cantitatea de hrană
maximă culeasă este suma cantităţilor de hrană din toate cămăruţele. Traseul este determinat de
o parcurgere pe orizontală.
Analog pentru situaţia ı̂n care n este impar.
36 i=1;
37 while(i+1<m)
38 {
39 for(j=1;j<=n;j++) out.println(i+" "+j);
40 i++;
41 for(j=n;j>=1;j--) out.println(i+" "+j);
42 i++;
43 }
44 for(j=1;j<=n;j++) out.println(m+" "+j);
45 }
46
47 static void nimpar()
48 {
49 int i,j;
50 j=1;
51 out.println(m*n+" "+s);
52 while(j+1<n)
53 {
54 for(i=1;i<=m;i++) out.println(i+" "+j);
55 j++;
56 for(i=m;i>=1;i--) out.println(i+" "+j);
57 j++;
58 }
59 for(i=1;i<=m;i++) out.println(i+" "+n);
60 }
61
62 static void mnpare()
63 {
64 int i,j;
65 imin=0;jmin=0;min=101;
66 for(i=1;i<=m;i++)
67 for(j=1;j<=n;j++)
68 if((i+j)%2==1)
69 if(a[i][j]<min) { min=a[i][j]; imin=i; jmin=j; }
70 out.println((m*n-1)+" "+(s-a[imin][jmin]));
71
72 j=1;
73 while(j+1<jmin) // stanga
74 {
75 for(i=1;i<=m;i++) out.println(i+" "+j);
76 j++;
77 for(i=m;i>=1;i--) out.println(i+" "+j);
78 j++;
79 }
80
81 i=1;
82 while(i+1<imin) // sus
83 {
84 out.println(i+" " +j);
85 out.println(i+" " +(j+1));
86 out.println((i+1)+" " +(j+1));
87 out.println((i+1)+" " +j);
88 i=i+2;
89 }
90
91 out.println(i+" "+j); // patratel
92 if((i==imin)&&(j+1==jmin)) out.println((i+1)+" " +j);
93 else out.println(i+" " +(j+1));
94 out.println((i+1)+" " +(j+1));
95
96 i=i+2;
97 while (i<m) // jos
98 {
99 out.println(i+" " +(j+1));
100 out.println(i+" " +j);
101 out.println((i+1)+" " +j);
102 out.println((i+1)+" " +(j+1));
103 i=i+2;
104 }
105
106 j=j+2;
107 while(j+1<=n) // dreapta
108 {
109 for(i=m;i>=1;i--) out.println(i+" "+j);
110 j++;
111 for(i=1;i<=m;i++) out.println(i+" "+j);
CAPITOLUL 21. OJI 2002 21.2. MOUSE 469
112 j++;
113 }
114 }
115 }//class
470
Appendix A
”Instalare” C++
Instalarea este foarte uşoară (este de tipul ”next -> next -> ... -> next”) iar pe in-
ternet sunt multe site-uri de ajutor. De exemplu:
https://www.pbinfo.ro/?pagina=intrebari-afisare&id=26
https://www.youtube.com/watch?v=CLkWRvAwLO8
https://infoas.ro/lectie/112/tutorial-instalare-codeblocks-usor-introducere-in-in
https://www.competentedigitale.ro/c/oji2019/Kit_OJI_2017.rar
471
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 472
A.1.1 Code::Blocks
Pentru versiuni mai noi, de Code::Blocks şi compilatoare, se poate accesa site-ul
http://www.codeblocks.org/downloads/binaries
de unde se poate descărca, de exemplu, codeblocks-20.03mingw-setup.exe.
Versiuni mai vechi, dar foarte bune, se pot descărca de la adresa
28
Code::Blocks este un IDE (integrated development environment) pentru C/C++, un fel de Notepad ... mai
sofisticat, cu multe butoane care lansează ı̂n execuţie diverse programe, de exeplu g++.exe
29
g++.exe este compilatorul de C++, programul care va verifica dacă instrucţiunile noastre sunt ok sau nu ...
şi care ne va supăra mereu cu erorile pe care ni le arată ... ... Este “o mică artă” să ı̂nţelegem mesajele de
eroare pe care le vedem pe ecran şi să fim ı̂n stare să “depanăm” programele noastre!
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 473
http://www.codeblocks.org/downloads/source/5
Mai precis:
https://sourceforge.net/projects/codeblocks/files/Binaries/20.03/
http://sourceforge.net/projects/codeblocks/files/Binaries/17.12
http://sourceforge.net/projects/codeblocks/files/Binaries/16.01
De preferat este să avem cel puţin două partiţii: C:¯, D:¯, ... şi să “lăsăm ı̂n pace” partiţia C:¯
pentru sistemul de operare şi programele instalate!
În figura de mai sus se poate observa că pe D:¯ există un folder de lucru pentru programele ı̂n
C++, folder care se numeşte Programe C++. Aici vom salva toate programele C++ pe care
le vom scrie, eventual organizate pe mai multe subfoldere.
Click-dreapta cu mouse-ul şi selectăm “New -¿ Text document” ca ı̂n figura următoare.
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 474
Dacă vom executa două click-uri pe numele fşierului p01.cpp sau un click pentru a marca
fişierul şi apoi un ¡Enter¿, se va declanşa Code::Blocks cu p01.cpp ı̂n fereastra de editare şi, ce
este şi mai important, cu Programe C++ ca folder curent de lucru pentru Code::Blocks.
Adică, aici vor apărea toate fişiere generate de Code::Blocks pentru p01.cpp.
Figura A.9: Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 476
Dacă avem fişierele p01.cpp, ..., p05.cpp, ı̂n folderul nostru de lucru, şi facem dublu-click pe
fiecare ... vor apărea toate ...
A.2 winlibs
A.2.1 GCC şi MinGW-w64 pentru Windows
Se descarcă de la
http://winlibs.com/#download-release
unul dintre fişierele:
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 481
winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.7z dimensiune fişier = 148 MB
winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.zip dimensiune fişier = 324 MB
winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.7z dimensiune
fişier = 52.1 MB
winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.zip dimensi-
une fişier = 141 MB
A.2.2 PATH
Trebuie pusă ı̂n PATH calea pentru D:\mingw64\bin\. În “Type here to search” scrieţi: path
şi apoi click pe “Edit the system environment variables”, ca ı̂n figura următoare:
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 482
Se selecteaza “Path” şi click pe “Edit”. Apare fereastra “Edit Environment Variables”
C:¯path
Dacă totul este OK atunci se trece la instalarea IDE-ului preferat (Integrated Development
30
Environment ), de exemplu Code::Blocks 20.03 (sau Eclipse, Visual Studio Code, Dev C++,
NetBeans, şi altele).
A.2.3 CodeBlocks
CodeBlocks se poate descărca de la http://www.codeblocks.org/downloads/26. Sunt
mai multe variante dar eu am descărcat numai codeblocks-20.03-setup.exe care are 35.7 MB.
La instalare am lăsat totul implicit, deci ... Next, Next, ..., Next până la sfârşit!
La prima lansare ı̂n execuţie este necesară setarea locaţiei compilatorului de C++ (gcc-ul pe
care tocmai l-am instalat!).
30
https://en.wikipedia.org/wiki/Integrated_development_environment
https://ro.wikipedia.org/wiki/Mediu_de_dezvoltare
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 485
Exponenţiere rapidă
În figura B.1 este considerat numărul nr 234 care ı̂n baza 2 se scrie sub forma 11101010 (celula
C10).
Pe R3 (rândul 3) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 10: se ”şterge” ulima cifră (care este egală cu nr%10 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 10).
Pe R10 (rândul 10) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 2: se ”şterge” ulima cifră (care este egală cu nr%2 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 2, la fel cum se ı̂ntâmplă ı̂n baza 10).
Observaţia 1. Cifrele se obţin ı̂n ordine inversă. Prima cifră obţinută ca rest este de fapt ultima
cifră din număr (vezi R4 şi R11).
Pe R6 şi R16 sunt precizate puterile bazei.
Valoarea numărului nr este suma produselor dintre cifre şi puterile corespunzătoare:
ı̂n baza 10: 4*1 + 3*10 + 2*100 = 234 (R3 şi R6)
ı̂n baza 2: 0*1 + 1*2 + 0*4 + 1*8 + 0*16 + 1*32 + 1*64 + 1*128 = 234 (R13 şi R16)
494
APPENDIX B. EXPONENŢIERE RAPIDĂ B.2. NOTAŢII, RELAŢII ŞI FORMULE 495
2 2 2 4 2 8 2 16
a0 a a1 a0 a a2 a1 a a3 a2 a a4 a3 a
2 32 2 64 2 128
a5 a4 a a6 a5 a a7 a6 a
Dacă notăm ek ”exponentul (puterea)” la care apare ak ı̂n produs, atunci putem observa
uşor că şirul e0 , e1 , e2 , e3 , ... se obţine prin ı̂mpărţiri succesive ale lui n prin 2 şi preluı̂nd restul
rezultatului. Pentru a folmaliza acest lucru vom considera şirul n0 , n1 , n2 , n3 , ... definit astfel:
n0 n,
w
nk nk1 ©2 (câtul ı̂mpărţirii!) k '1
Folosind aceste notaţii, putem scrie:
~
n0 n;
a0 a;
e n0 %2 " r0, 1x;
0
1 a0 , dacă e0 1;
p0
e
a00 sau, altfel scris: p0 w
1, dacă e0 0;
(B.2.1)
nk nk1 ©2 k ' 1; nk1 j 0 desigur ! ... dar şi nk1 j 1
ak1 , k ' 1;
2
ak
ek nk %2 " r0, 1x, k ' 0;
pk1 ak , k ' 0, dacă ek 1; ak ak modifică produsul pk1
1
p w
k k ' 0, dacă ek 0; ak 1 nu modifică produsul pk1
0
pk1 ,
B.4 Codul
Codul pentru relaţiile (B.2.1) devine:
Observaţia 2. În acest cod actualizarea lui p se face după actualizările pentru a şi n.
Observaţia 3. În codul următor actualizarea lui p se face ı̂naintea actualizărilor pentru a şi n,
corespunzător relaţiilor (B.4.2).
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 497
~
iniţializări:
p1 1;
n1 n;
a1 a;
calcul pentru k ' 0:
e
k nk1 %2 " r0, 1x; k ' 0 (B.4.2)
pk1 ak1 , k ' 0, dacă ek 1; ak1
1
ak1 modifică produsul pk1
k w
p
k ' 0, dacă ek 0; ak1
0
pk1 , 1 nu modifică produsul pk1
actualizări k ' 0:
a ak1 , k ' 0;
2
k
nk nk1 ©2 k ' 0; şi nk1 j 0 desigur !
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 498
Observaţia 4. Instrucţiunile care sunt ”ı̂n plus” se pot elimina. Codul următor arată acest lucru:
Observaţia 5. Produsul poate deveni foarte mare şi din cauza asta se cere rezultatul modulo un
număr prim. Codul următor arată acest lucru:
14 a = (a * a)%MOD;
15 n = n / 2;
16 nropr=nropr+6; // n%2, p*a, a*a, (a * a)%MOD si n/2
17 }
18 return p;
19 }
20
21 int main()
22 {
23 int a=1234;
24 int n=1e+9; // 10ˆ9
25 int rezn; // rezultat exponentiere naiva
26
27 rezn=exponentiere_rapida(a,n);
28
29 cout<<"rezr = "<<rezn<<" nropr = "<<nropr<<"\n";
30
31 return 0;
32 }
33 /*
34 rezr = 376 nropr = 180
35
36 Process returned 0 (0x0) execution time : 0.021 s
37 Press any key to continue.
38 */
Numărul de operaţii:
cu metoda naivă acest număr este 2 000 000 000
cu metoda rapidă este 180.
Iar ca număr de operaţii ... una este să faci 2 miliarde de operaţii şi alta este să faci
numai 180 de operaţii de acelaşi tip (de fapt sunt numai 30 de paşi dar la fiecare pas se fac 5
sau 6 operaţii aritmetice)!
31
şi nu mai trebuie ”atâtea formule matematice”!
31
Este o glumă!
Appendix C
Căutare binară
C.1 Mijlocul = ?
Care este poziţia (indicele) ”mijlocului” unei zone dintr-un vector?
În figura C.1 sunt trei formule pentru determinarea mijlocului unei zone [i1, i2] din vector:
mij1 = (i1+i2)/2
mij2 = i1+(i2-i1)/2
mijdr = (i1+i2+1)/2
Toate trei dau rezultat corect dacă secvenţa [i1, ..., i2] are un număr impar de elemente.
Când secvenţa [i1, ..., i2] are un număr par de elemente apar două elemente la mijloc ... şi ...
primele două formule dau ”mijlocul din stânga” iar a treia formulă dă ”mijlocul din dreapta”.
Atunci când căutăm o valoare ı̂ntr-un vector avem la dispoziţie căutarea secvenţială. Dacă
ştim că vectorul este sortat (şir crescător de numere) atunci este bine să folosim căutarea binară
pentru că aceasta ”găseste” numărul căutat (sau ”spune” că acel număr nu este ı̂n şir)
ı̂n 10 paşi dacă şirul are 1000 de elemnte (de fapt 1024 dar ... aproximăm!),
ı̂n 20 de paşi dacă şirul are 1,000,000 de elemente,
ı̂n 30 de paşi dacă şirul are 1,000,000,000 de elemente,
...
Explicaţia simplă este că noi tot ı̂njumătăţim intervalul de căutare până când acest interval
rămâne cu un singur element ... şi vedem dacă acel element este cel căutat (dacă elementul rămas
nu este cel căutat ... ı̂nseamnă că acel element ”căutat” nu există ı̂n şir).
Putem să ne gândim la drumul invers: plecăm de la o secvenţă cu un singur element şi dublăm
secvenţa (invers faţă de cum am făcut ı̂njumătăţind secvenţa) până ajungem la o secvenţă care
are atâtea elemente câte a avut şirul iniţial (ne oprim imediat dacă am depăşit acel număr!).
Lungimile secvenţelor sunt: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, ...
Observăm că ı̂n 10 paşi, plecând din 1, ajungem să depăşim 1000.
502
APPENDIX C. CĂUTARE BINARĂ C.2. POZIŢIE OARECARE 503
70
71 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
72
73 int n=sizeof(a)/sizeof(int);
74
75 cout<<"prima pozitie in vector = 0\n";
76 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
77
78 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
79
80 cout<<endl;
81 cout<<"nr cautari = "<<nrc<<"\n";
82
83 return 0;
84 }
55 int main()
56 {
57 int x; // caut x in vectorul a[]
58
59 //x=1; // secventa pe primele pozitii
60 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
61 //x=3; // exista numai un 3
62 //x=4; // exista secventa de 4
63 //x=9; // secventa pe ultimele pozitii
64
65 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
66
67 int n=sizeof(a)/sizeof(int);
68
69 cout<<"prima pozitie in vector = 0\n";
70 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
71
72 cautareBinaraRec1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
73
74 cout<<endl;
75 cout<<"nr cautari = "<<nrc<<"\n";
76
77 return 0;
78 }
43 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
44 {
45 if(v[st]==x)
46 {
47 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
48
49 return true;
50 }
51
52 else
53 {
54 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
55 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
56
57 return false;
58 }
59 }
60
61 return true; // sau return false; ... oricum nu ajunge aici ... !!!
62 }
63
64 // ------------------------------------------------------------------
65
66 int main()
67 {
68 int x; // caut x in vectorul a[]
69
70 //x=1; // secventa pe primele pozitii
71 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
72 //x=3; // exista numai un 3
73 //x=4; // exista secventa de 4
74 //x=9; // secventa pe ultimele pozitii
75
76 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
77
78 int n=sizeof(a)/sizeof(int);
79
80 cout<<"prima pozitie in vector = 0\n";
81 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
82
83 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
84
85 cout<<endl;
86 cout<<"nr cautari = "<<nrc<<"\n";
87
88 return 0;
89 }
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19
20 // ------------------------------------------------------------------
21
22 bool cautareBinaraIter1(int x, int v[], int st, int dr)
23 {
24 int mij;
25
26 while(st != dr)
27 {
28 nrc++;
29 mij=(st+dr+1)/2; // actualizez zona de cautare
30
31 cout<<"caut "<<x<<" in ";
32 afisv(v,st,dr);
33 cout<<"\n\n";
34
35 if(x >= v[mij])
36 st=mij; // caut in dreapta
37 else
38 dr=mij-1; // caut in stanga
39
40 cout<<"v["<<st<<"] = "<<v[st]<<" v["<<dr<<"] = "<<v[dr]<<endl;
41 //getchar();
42 }
43
44 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
45 {
46 if(v[st]==x)
47 {
48 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
49 return true;
50 }
51 else
52 {
53 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
54 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
55 return false;
56 }
57 }
58
59 return true; // sau return false; ... oricum nu ajunge aici ... !!!
60 }
61 // ------------------------------------------------------------------
62
63 int main()
64 {
65 int x; // caut x in vectorul a[]
66
67 //x=1; // secventa pe primele pozitii
68 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
69 //x=3; // exista numai un 3
70 //x=4; // exista secventa de 4
71 //x=9; // secventa pe ultimele pozitii
72
73 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
74 int n=sizeof(a)/sizeof(int);
75
76 cout<<"prima pozitie in vector = 0\n";
77 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
78
79 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
80
81 cout<<endl;
82 cout<<"nr cautari = "<<nrc<<"\n";
83
84 return 0;
85 }
APPENDIX C. CĂUTARE BINARĂ C.4. POZIŢIA DIN DREAPTA 509
”Vecini” ...
D.1 Direcţiile N, E, S, V
În figura D.1 sunt două modele (notaţii) utile ı̂n probleme ı̂n care se folosesc directiile N, E, S şi
V (NE, NV, SE şi SV se deduc uşor!).
510
APPENDIX D. ”VECINI” ... D.1. DIRECŢIILE N, E, S, V 511
19
10 , 303 exponenţiere rapidă, 18
ˆ=, 172
şir al frecvenţelor, 408 first, 68, 209, 311
şir circular, 385 fracţie continuă, 97
1LL, 88 front(), 88, 209
512
INDEX INDEX 513
push back, 37, 39, 65, 68, 81, 88, 90, 112, swap, 209
205, 209, 244 sync with stdio, 88
push front, 209
tehnica ”meet in the middle”, 170
rbegin(), 68 tehnica greedy, 203
recurenţă, 319 template, 168
relaţie de recurenţă, 330 top(), 311
resize, 149 trage cu tunul, iv
reverse, 134 true, 244
sau exclusiv, 170 typedef, 244
second, 68, 209, 311 typename, 168
set, 68
size, 65 unique, 244
size(), 37, 39, 65, 67, 68, 88, 90, 134, 149 Universitatea Ovidius, vi
sort, 65, 67, 68, 90, 131, 168, 175, 205, 209, upper bound, 168, 248
244 using, 89
greater, 131
sortare, 14, 103, 159, 197, 203, 218, 399, 403 valoarea mediană, 7
sortare prin numărare, 197 vector, 37, 39, 65, 67, 68, 81, 88, 90, 112,
srand, 81 125, 131, 134, 149, 205, 206, 244
stack, 311 back(), 125
statistici de ordine, 399 begin(), 131
stivă, 309, 403 end(), 131
strategie de tip Meet In The Middle, 227 pop back(), 125
strategie Greedy, 72, 93 push back, 125
strategie greedy, 145 resize, 125
string, 68, 131, 134 size(), 125
struct, 26, 88, 90, 112, 149, 175, 205, 244 vector de frecvenţă, 14, 78, 104, 164
sufix, 10 vector de tuple, 14
sumă parţială, 7 vector de vizitare, 313
sume parţiale, 160, 164, 288, 330, 385 vectorul frecvenţelor, 399
Bibliografie
[1] Aho, A., Hopcroft, J., Ullman, J.D.; Data strutures and algorithms, Addison Wesley, 1983
[2] Andreica M.I.; Elemente de algoritmică - probleme şi soluţii, Cibernetica MC, 2011
[3] Andonie R., Gârbacea I.; Algoritmi fundamentali, o perspectivă C++, Ed. Libris, 1995
[4] Atanasiu, A.; Concursuri de informatică. Editura Petrion, 1995
[5] Bell D., Perr M.; Java for Students, Second Edition, Prentice Hall, 1999
[21] Knuth, D.E.; The art of computer programming, vol. 4A: Combinatorial algorithms, Part 1,
Addison Wesley, 2011.
[22] Lambert,K. A., Osborne,M.; Java. A Framework for Programming and Problem Solving,
PWS Publishing, 1999
[23] Laaksonen A.; Guide to competitive programming, Springer, 2017
[24] Livovschi, L.; Georgescu H.; Analiza şi sinteza algoritmilor. Ed. Enciclopedică, Bucureşti,
1986.
[25] Niemeyer, P., Peck J.; Exploring Java, O’Reilly, 1997.
514
BIBLIOGRAFIE BIBLIOGRAFIE 515
[26] Odăgescu, I., Smeureanu, I., Ştefănescu, I.; Programarea avansată a calculatoarelor personale,
Ed. Militară, Bucureşti 1993
[27] Odăgescu, I.; Metode şi tehnici de programare, Ed. Computer Lobris Agora, Cluj, 1998
[28] Popescu Anastasiu, D.; Puncte de articulaţie şi punţi ı̂n grafuri, Gazeta de Informatică nr.
5/1993
[29] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Lista_probleme_2000-2007.pdf
[30] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Rezolvari_C09.pdf
517
What’s the next?
ORNL’s Frontier First to Break the Exaflop Ceiling
Over time
the following steps
will lead you to the value
you seek for yourself
now!