Sunteți pe pagina 1din 941

PROBLEME DE INFORMATIC€

date la olimpiade

în

2020
2019 2018 2017 2016 2015
2014 2013 2012 2011 2010
2009 2008 2007 2006 2005
2004 2003 2002 2001 2000

la clasa a 9-a

... draft (ciorn ) ...


*** Nobody is perfect ***

Ph.D. Adrian R bâea

9 decembrie 2020
Dedicaµie

I would like to dedicate this book ...

1
to myself ...
in a time when ...
I will not be able ...
2
to be.

That is because ...

3
When I Die Nobody Will Remember Me

1
I Dedicate This Book to Myself  By Carol Lynne
2
To be, or not to be ..., Hamlet, Act III, Scene I, William Shakespeare, 1564-1616
3
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 ecare 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!.
4
Sunt convins c  este important s  studiem cu atenµie cât mai multe probleme rezolvate! Cred
cred c  sunt utile ³i primele versiuni în care sunt prezentate chiar ³i numai enunµurile ³i indicaµiile
"ociale" de rezolvare. Acestea se g sesc în multe locuri; aici încerc s  le pun pe toate la un loc !
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
5 6
dicil ! :-) 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
7
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  posibil ...
a³a c , de aceast  dat , anii sunt în ordine ... descresc toare! :-)
Codurile surs  sunt cele ociale (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
disponibil ³i, oricum, calculatoarele folosite la olimpiade înainte de 2010 erau ceva mai 'slabe' ³i
8
... restricµiile de memorie, din enunµurile problemelor, par 'ciudate' acum!). “i indc  a venit
vorba despre calculatoare mai  slabe sau mai  puternice: laptopul meu¯t u este puµin mai  slab
decât cel mai puternic calculator din lume în 1985 dar ³i ... un pic mai  puternic decât cel mai
9
puternic calculator din lume în 1983. (armaµia este valabil  acum, în 2020).
4
Se poate observa din Coduri surs  c  orice problem  are numeroase soluµii, atât ca algoritmi de rezolvare
cât ³i ca stil de programare! Studiind aceste coduri ... avem ce înv µa ... de³i uneori pare c  'se trage cu tunul' ...
5
IOI2019 ³i IOI2020 au a permis utilizarea limbajelor de programare C++ ³i Java
6
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python ³i Rubi (...)
7
8
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema avârcolaci - ONI2014 clasa a 11-a
Când eram eu elev/student un calculator obi³nuit executa în jur de 1.000.000 de operaµii pe secund , acum
execut  1.000.000.000 de operaµii pe secund , iar mai târziu ... cine stie ce va mai ?!
9
https://en.wikipedia.org/wiki/List_of_fastest_computers

iii
În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: în noiembrie 2017 în
China, în noiembrie 2019 în SUA ³i ... în iunie 2020 în Japonia (Fugaku: 415 petaops, adic 
15 10
415 ˜ 10 operaµii pe secund , adic  ... 415 milioane de ... miliarde de ... operaµii pe secund ).
11
O mic  observaµie: în 2017 a fost prima ediµie a olimpiadei EJOI în Bulgaria ³i ... tot în
12
Bulgaria a fost ³i prima ediµie a olimpiadei IOI în 1989.
Dar ... prima ediµie a olimpiadei IMO (International Mathematical Olympiad) a fost în
13
România în 1959. Tot în România s-au µinut ediµiile din anii 1960, 1969, 1978, 1999 ³i 2018.
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!). Totu³i, vor  prezentate ³i Rezolv ri
detaliate ale problemelor de liceu (pe care le-am considerat în mod subiectiv!) utile.

Î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) nu au nevoie de ajutorul meu! Se descurc  singuri! La
Indicaµii de rezolvare voi prezenta numai ... numele algoritmilor clasici folosiµi în rezolvare.

ALGORITMI utili la olimpiadele de informatic  , separat pentru gimnaziu ³i liceu, sper s 


e de folos, a³a cum cred c  sunt [1] - [28], [34] - [47], [57] - [82], ... ³i multe alte c rµi ³i site-uri!.
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).
Nu sunt necesare preciz ri suplimentare!
“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 ... !!!

"I'm only responsible for what I say,


14
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:


http://adrianrabaea.scienceontheweb.net/
https://www.scribd.com/user/243528817/Adrian-Rabaea
https://drive.google.com/drive/folders/1hC5PZuslCdS95sl37SW46H-qy59GRDGZ

Adrese utile (programe ³colare):


http://www.ise.ro/wp-content/uploads/2017/01/Informatica-si-TIC.pdf
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

Bistriµa, Ph.D. Adrian R bâea


9 decembrie 2020

10
https://www.top500.org/lists/top500/
11
https://ejoi.org/about/
12
https://stats.ioinformatics.org/olympiads/
13
https://en.wikipedia.org/wiki/International_Mathematical_Olympiad
14
https://www.etsy.com/listing/604809336/john-wayne-quotes-i-am-only-responsible
"Acknowledgements"
15
"I want to thank God most of all because without God I wouldn't be able to do any of this."

Bistriµa, 9 decembrie 2020

Adrian R.

15
I.d.k.: "I don't know who the author is."

v
Despre autor16
nume: R bâea Aurel-Adrian, 18.03.1953 - ...
adresa: Str. Valea Ghinzii nr. 21 B, Bistriµa, România
telefon: +40 728 18 03 53 +40 363 10 25 10
email: adrian1803@gmail.com
Lector universitar - Universitatea Tehnic  din Cluj Napoca - Centrul
Universitar Nord din Baia Mare, Facultatea de “tiinµe, Str. Victoriei,
nr. 76, Baia Mare, România, (pensionat: 01.10.2018)
http://www.stiinte.utcluj.ro/
Discipline predate (1992-2018):
Algoritmi ³i structuri de date, Algoritmi în teoria opµiunilor nanciare, Bazele matematice
ale calculatoarelor, Bazele tehnologiei informaµiei, Birotic , Capitole speciale de inteligenµ 
articial , Capitole speciale de teoria algoritmilor, Calcul paralel, Informatic  economic ,
Instruire asistat  de calculator, Limbaje de programare; Programare orientat  pe obiecte,
Programare procedural , Structuri de date,

Studii doctorale în informatic  economic  - Diplom  de doctor (1997-2002):


Institutµia: Academia de Studii Economice, Bucure³ti;
17
Titlul tezei: Algoritmi paraleli ³i aplicaµii pe ma³ini virtual paralele
18
Conduc tor ³tiinµic: Prof. dr. ing. Gheorghe Dodescu
Teme studiate: utilizarea algoritmilor paraleli în teoria opµiunilor nanciare

Studii de specializare în informatic  - Certicat anul V - 'master' (1978-1979):


Instituµia: Facultatea de matematic  ³i informatic , Bucure³ti;
Titlul tezei: Probleme la limit  pentru procese cu cre³teri independente ³i aplicaµii în teoria
a³tept rii
19
Conduc tor ³tiinµic: Prof. dr. Constantin Tudor

Studii universitare de licenµ  în informatic  - Diplom  de licenµ  (1974-1978):


Instituµia: Facultatea de matematic  ³i informatic , Bucure³ti;
Titlul tezei: Metode de comparaµie multipl  în analiza dispersional 
20
Conduc tor ³tiinµic: Prof. dr. Ion V duva

Locuri de munc : (1979-2018):


- (2018-2009) Universitatea Tehnic  din Cluj-Napoca, Centrul Universitar Nord din Baia-
Mare, Facultatea de “tiinµe, Departamentul de Matematic -Informatic 
- (2009-1992) Universitatea "Ovidius" din Constanµa, Facultatea de Matematic  ³i Informa-
tic , Departamentul de Informatic 
- (1992-1979) Centrul de Informatic  ³i organizare CINOR, Bucure³ti
https://scholar.google.com/citations?user=-sSE_1wAAAAJ&hl=en
https://www.scopus.com/authid/detail.uri?origin=resultslist&authorId=56122389200&zone=
http://www.facebook.com/adrian.rabaea

16
https://stiinte.utcluj.ro/files/cv/CV%20Rabaea_Adrian.pdf
17
http://opac.biblioteca.ase.ro/opac/bibliographic_view/149021
18
http://www.ionivan.ro/2015-PERSONALITATI/Dodescu.htm
19
https://sites.google.com/site/ciprianatudor/Home/professor-constantin-tudor
20
https://ro.wikipedia.org/wiki/Ion_V%C4%83duva

vi
Cuprins

Prefaµ  iii
Cuprins vii
Lista gurilor xxi
Lista tabelelor xxiii
Lista programelor xxiv

I OJI - Olimpiada judeµean  de informatic  1


1 OJI 2020 2
1.1 buldo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2 cetate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.3 spiralmatrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

2 OJI 2019 36
2.1 abx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.2 deminare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.3 mostenire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

3 OJI 2018 63
3.1 Cuf r . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.2 fadema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
3.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
3.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

vii
3.3 tnia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

4 OJI 2017 77
4.1 Ace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
4.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
4.2 Admitere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
4.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
4.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
4.3 Roboµi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
4.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

5 OJI 2016 105


5.1 Cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
5.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
5.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
5.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
5.2 pic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
5.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

6 OJI 2015 117


6.1 arc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
6.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
6.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
6.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
6.2 defrag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
6.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
6.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
6.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

7 OJI 2014 135


7.1 cool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
7.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
7.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
7.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
7.2 pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
7.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
7.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
7.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

8 OJI 2013 151


8.1 betasah . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
8.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
8.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
8.2 clepsidru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
8.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
8.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
8.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
9 OJI 2012 175
9.1 elicop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
9.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
9.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
9.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
9.2 roata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
9.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
9.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
9.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

10 OJI 2011 188


10.1 vase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
10.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
10.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
10.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
10.2 cri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
10.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
10.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
10.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205

11 OJI 2010 206


11.1 livada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
11.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
11.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
11.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
11.2 numar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
11.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
11.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
11.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214

12 OJI 2009 215


12.1 expresie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
12.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
12.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
12.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
12.2 placare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
12.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
12.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
12.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218

13 OJI 2008 219


13.1 Concurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
13.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
13.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
13.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
13.2 Pluricex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
13.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
13.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
13.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

14 OJI 2007 clasa a IX-a 226


14.1 Cartele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
14.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
14.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
14.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
14.2 Paritate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
14.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
14.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
14.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
15 OJI 2006 clasa a IX-a 237
15.1 Flori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
15.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
15.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
15.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
15.2 Pluton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
15.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
15.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
15.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248

16 OJI 2005 clasa a IX-a 251


16.1 Numere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
16.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
16.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
16.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
16.2 MaxD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
16.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
16.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
16.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254

17 OJI 2004 clasa a IX-a 257


17.1 Expresie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
17.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
17.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
17.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
17.2 Reactivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
17.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
17.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
17.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268

18 OJI 2003 clasa a IX-a 270


18.1 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
18.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
18.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
18.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
18.2 Numere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
18.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
18.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
18.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276

19 OJI 2002 clasa a IX-a 278


19.1 Poarta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
19.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
19.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
19.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
19.2 Mouse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
19.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
19.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
19.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281

II ONI - Olimpiada naµional  de informatic  284


20 ONI 2020 285
20.1 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
20.1.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
20.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
20.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
20.2 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
20.2.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.3 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.3.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.4 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
20.4.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
20.4.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
20.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
20.5 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
20.5.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
20.5.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
20.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
20.6 *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
20.6.1 *Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
20.6.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
20.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289

21 ONI 2019 290


21.1 amat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
21.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
21.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
21.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
21.2 Comun . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
21.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
21.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
21.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
21.3 pro3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
21.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
21.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
21.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
21.4 Cub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
21.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
21.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
21.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
21.5 bofrac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
21.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
21.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
21.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
21.6 telefon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
21.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
21.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
21.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340

22 ONI 2018 341


22.1 bazaf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
22.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
22.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
22.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
22.2 mexitate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
22.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
22.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
22.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
22.3 plaja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
22.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
22.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
22.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
22.4 bsrec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
22.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
22.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
22.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
22.5 numinum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
22.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
22.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
22.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
22.6 rosii mici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
22.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
22.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
22.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376

23 ONI 2017 377


23.1 arhipelag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
23.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
23.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
23.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
23.2 mirror . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
23.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
23.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
23.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
23.3 okcpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
23.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
23.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
23.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
23.4 adlic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
23.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
23.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
23.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
23.5 bomboane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
23.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
23.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
23.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
23.6 orase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
23.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
23.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
23.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419

24 ONI 2016 420


24.1 civilizatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
24.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
24.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
24.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
24.2 cmmdc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
24.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
24.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
24.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
24.3 livada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
24.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
24.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
24.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
24.4 dif2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
24.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
24.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
24.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
24.5 leduri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
24.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
24.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
24.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
24.6 omogene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
24.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
24.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
24.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
25 ONI 2015 449
25.1 cub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
25.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
25.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
25.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
25.2 risc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
25.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
25.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
25.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
25.3 roboti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
25.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
25.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
25.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
25.4 casa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
25.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
25.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
25.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
25.5 lenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
25.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
25.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
25.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
25.6 sipet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
25.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
25.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
25.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499

26 ONI 2014 500


26.1 harta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
26.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
26.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
26.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514
26.2 qvect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514
26.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
26.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
26.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
26.3 tg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
26.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
26.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
26.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
26.4 progresie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
26.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
26.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
26.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
26.5 reex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
26.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
26.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
26.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
26.6 traseu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
26.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
26.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
26.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554

27 ONI 2013 555


27.1 aranjare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
27.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
27.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
27.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
27.2 gradina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
27.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
27.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
27.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
27.3 split . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
27.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
27.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
27.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
27.4 momente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
27.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
27.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
27.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
27.5 secvente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
27.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
27.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567
27.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
27.6 spider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
27.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
27.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
27.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572

28 ONI 2012 573


28.1 7segmente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573
28.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574
28.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574
28.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
28.2 copaci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
28.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
28.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
28.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
28.3 intersecµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
28.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
28.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
28.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587
28.4 palindrom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587
28.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
28.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
28.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
28.5 sstabil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
28.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
28.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
28.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599
28.6 unuzero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599
28.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
28.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
28.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603

29 ONI 2011 604


29.1 poligon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
29.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
29.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
29.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606
29.2 stalpi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606
29.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607
29.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
29.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
29.3 tort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
29.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
29.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
29.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
29.4 ape . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
29.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
29.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
29.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
29.5 ec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
29.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
29.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
29.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
29.6 furnici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
29.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
29.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
29.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621

30 ONI 2010 622


30.1 cern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
30.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
30.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
30.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
30.2 cmmmc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
30.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627
30.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
30.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
30.3 simetric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
30.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
30.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
30.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
30.4 pesti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
30.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
30.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
30.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
30.5 plaja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
30.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639
30.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
30.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
30.6 tango . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
30.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
30.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
30.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644

31 ONI 2009 645


31.1 joc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
31.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
31.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
31.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
31.2 perspic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
31.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
31.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
31.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
31.3 rafturi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
31.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
31.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
31.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
31.4 br . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
31.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
31.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
31.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
31.5 origami . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656
31.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657
31.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657
31.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
31.6 patrate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
31.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
31.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
31.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
32 ONI 2008 662
32.1 ab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
32.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
32.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
32.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 664
32.2 iepuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 664
32.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665
32.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
32.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
32.3 palind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
32.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
32.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
32.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
32.4 auto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
32.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
32.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
32.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
32.5 div . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
32.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
32.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
32.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
32.6 teatru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
32.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
32.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
32.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681

33 ONI 2007 682


33.1 Agitaµie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
33.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
33.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
33.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
33.2 Coduri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685
33.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
33.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
33.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
33.3 Lacuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
33.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
33.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
33.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
33.4 Secv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692
33.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
33.4.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
33.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
33.5 “otron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
33.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
33.5.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
33.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
33.6 Triunghi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
33.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
33.6.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
33.6.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698

34 ONI 2006 700


34.1 Factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700
34.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
34.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
34.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
34.2 Limbaj . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
34.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
34.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
34.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
34.3 Panouri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
34.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
34.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
34.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
34.4 Pereµi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
34.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
34.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
34.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
34.5 “anµ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
34.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
34.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
34.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
34.6 Zumzi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727
34.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728
34.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
34.6.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731

35 ONI 2005 738


35.1 Bifo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 738
35.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
35.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
35.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
35.2 Romeo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 744
35.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
35.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
35.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
35.3 Seceta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
35.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
35.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
35.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
35.4 Biblos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
35.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757
35.4.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757
35.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757
35.5 Joc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759
35.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
35.5.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
35.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
35.6 Pal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 765
35.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
35.6.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
35.6.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766

36 ONI 2004 769


36.1 Coduri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
36.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
36.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
36.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771
36.2 Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 772
36.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
36.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
36.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
36.3 Poligon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779
36.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
36.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
36.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
36.4 “ablon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
36.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
36.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
36.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
36.5 “ir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
36.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
36.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
36.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 790
36.6 Snipers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 790
36.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
36.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
36.6.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793

37 ONI 2003 795


37.1 Seti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
37.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
37.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
37.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
37.2 Scaune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
37.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
37.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
37.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
37.3 Circular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803
37.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 804
37.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
37.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808
37.4 Criptare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810
37.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
37.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
37.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812
37.5 Ma³ina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814
37.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
37.5.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
37.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
37.6 Operaµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
37.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
37.6.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
37.6.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817

38 ONI 2002 819


38.1 Pentagon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819
38.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
38.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
38.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
38.2 Pod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
38.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823
38.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
38.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
38.3 Suma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832
38.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833
38.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
38.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
38.3.4 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838
38.4 Discuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
38.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844
38.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845
38.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845
38.5 Cod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846
38.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
38.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848
38.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
39 ONI 2001 851
39.1 Ferma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851
39.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
39.1.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
39.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
39.2 Fracµii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854
39.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
39.2.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
39.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
39.3 Tablou . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
39.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856
39.3.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856
39.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856
39.4 Competiµie dicil  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858
39.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858
39.4.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858
39.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859
39.5 Cuvinte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859
39.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
39.5.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
39.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
39.6 Grup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
39.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
39.6.2 *Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
39.6.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863

40 ONI 2000 864


40.1 Algoritm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 864
40.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 866
40.1.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 866
40.1.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
40.2 Cod de identicare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870
40.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870
40.2.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870
40.2.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
40.3 Comoara . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 875
40.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
40.3.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
40.3.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878
40.4 Cuburi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879
40.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 880
40.4.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881
40.4.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
40.5 Fibo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883
40.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884
40.5.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884
40.5.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
40.6 Kommando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 888
40.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
40.6.2 Cod surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
40.6.3 Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 894

Anexa A Un pic de matematic ! 902


A.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902
A.1.1 Prezentare general  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902
A.1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 902

Anexa B Un pic de programare! 903


B.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903
B.1.1 Prezentare general  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903
B.1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903
Glosar 904
Bibliograe 906
Lista autorilor 909
Lista gurilor
2.1 deminare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.2 deminareIR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

4.1 Ace - Fig1 & Fig2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

5.1 Cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105


5.2 pic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

6.1 defrag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

7.1 Pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142


7.2 Pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
7.3 Pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
7.4 Pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
7.5 Pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
7.6 Pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
7.7 Pseudobil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

8.1 betasah . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151


8.2 betasah . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.3 betasah . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.4 betasah . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
8.5 clepsidru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
8.6 clepsidru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
8.7 clepsidru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

9.1 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175


9.2 roata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182

10.1 vase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188


10.2 vase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
10.3 vase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
10.4 cri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197

14.1 Cartele1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226


14.2 Cartele2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229

19.1 Poarta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279


19.2 Mouse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281

21.1 amat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291

25.1 lenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488

26.1 reex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540


26.2 traseu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548

33.1 Sigla ONI 2007 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682


33.2 Sotron1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
33.3 Sotron2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695

34.1 Sigla ONI 2006 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700

xxi
34.2 Pereµi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
34.3 Zumzi1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727
34.4 Zumzi2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728
34.5 Zumzi3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732
34.6 Zumzi4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732

35.1 Sigla ONI 2005 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 738


35.2 Romeo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
35.3 Romeo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
35.4 Romeo1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
35.5 Romeo2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747

36.1 Sigla ONI 2004 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769


36.2 Poligon2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
36.3 Snipers1s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791

37.1 Sigla ONI 2003 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795

38.1 Sigla ONI 2002 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819


38.2 Discuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844

39.1 Sigla ONI 2001 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851

40.1 Sigla ONI 2000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 864


Lista tabelelor
4.3 roboti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

5.1 cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

xxiii
Lista programelor
1.1.1 buldoMN_scanf.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.2 buldo_GM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.3 buldo_invers_PA_.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.4 buldo_MD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.5 buldo_nlognBD_Bun.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1.6 buldoMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1.7 buldonlognMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 cetate_optim_ocial_bogdan_scanf.c . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.2 cetate_GM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.3 cetate_MD2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2.4 cetate_optim_1_bogdan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.5 cetate_optim_ocial_bogdan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.2.6 cetateBFixColMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.2.7 cetateMN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.2.8 fast_unocial_alexandru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.9 fastest_unocial_alexandru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.3.1 spiral_matrix45pBD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.3.2 spiral_matrix75pBD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.3.3 spiralmatrix45p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.3.4 spiralmatrix75p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.3.5 spiralmatrix90pV1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.3.6 spiralmatrix90pV2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.3.7 spiralmatrix90pV3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.3.8 spiralmatrixMNo(n).cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.1.1 abx_EN_90.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.1.2 abx_LB_90.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.1.3 abx_LB_hybrid.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.1.4 abx_LB_hybrid_fast.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.1.5 abx_LB_stable.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.1.6 abx_NM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.2.1 deminare_CC3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.2.2 deminare_EN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.2.3 deminare_GM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.2.4 deminare_LB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.2.5 deminare_MP.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.3.1 mostenire_LB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.3.2 mostenire_MLT.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.3.3 mostenire_MP.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.1.1 cufar-100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.1.2 cufar-dani-100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.2.1 fadema_n6.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.2.2 fadema_n4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.2.3 fadema_n3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.2.4 fadema_n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
3.3.1 bruteQN2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.3.2 bruteN2+Q.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3.3.3 bruteQN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
3.3.4 sursa-dani-NlogN.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.1.1 ace_LS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

xxiv
4.1.2 ace_MirelaM.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.1.3 ace_priv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
4.1.4 ace_prmc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.1.5 ace_tavi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.2.1 mihai100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.2.2 mihai100comments.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
4.2.3 priv100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
4.3.1 roboti_luci.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.3.2 roboti_luci_wosort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.3.3 roboti_priv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.3.4 roboti_prmc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
5.1.1 CC1_cifre.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
5.1.2 CP_cifre.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.1.3 MLT_cifre.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
5.2.1 pic_CP1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.2.2 picMD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
5.2.3 picMLT1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
6.1.1 arc1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
6.1.2 arc2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
6.1.3 arc3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.1.4 arc4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
6.2.1 defrag1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
6.2.2 defrag2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
6.2.3 defrag3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
6.2.4 defrag4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
6.2.5 defrag5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
6.2.6 defrag6.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
7.1.1 CC2_cool.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
7.1.2 cool_eugen_fs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
7.1.3 cool_eugen_std.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
7.1.4 cool_n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
7.2.1 pseudobil.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
7.2.2 pseudobil_EN_100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
7.2.3 pseudobil_LS_100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
7.2.4 pseudobil2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
8.1.1 beta_CM1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
8.1.2 beta_CM2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
8.1.3 beta_CM3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
8.1.4 beta_GB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
8.1.5 beta_ZS.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
8.2.1 CC_clepsidru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
8.2.2 MR_clepsidru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
8.2.3 PD_clepsidru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
8.2.4 ZS_clepsidru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
9.1.1 elicopCC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
9.1.2 elicopDPA.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
9.1.3 elicopGB.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
9.2.1 roataCC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
9.2.2 roataPC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
10.1.1 v1_vase.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
10.1.2 v2_vase.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
10.1.3 v3_vase.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
10.2.1 cri_fstream-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
10.2.2 cricpp1-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
10.2.3 cricpp2-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
10.2.4 cricpp3-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
11.1.1 livada.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
11.1.2 livada_v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
11.2.1 numar_v1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
13.1.1 concurs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
13.2.1 pluricex.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
14.1.1 carteleC.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
14.1.2 cartele1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
14.1.3 cartele2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
14.1.4 cartele3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
14.2.1 PARITATE.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
14.2.2 Paritate.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
15.1.1 FLORI.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
15.1.2 ori1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
15.1.3 ori2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
15.2.1 PLUTONC.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
15.2.2 PLUTCARM.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
15.2.3 PLUTRADU.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
15.2.4 pluton1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
15.2.5 pluton2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
16.1.1 numere.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
16.1.2 numere.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
16.2.1 MaxD1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
16.2.2 MaxD2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
17.1.1 EXP.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
17.1.2 Expresie1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
17.1.3 Expresie2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
17.1.4 Expresie3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
17.2.1 REACT_QS.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
17.2.2 REACTIVI.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
17.2.3 reactivp.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
17.2.4 reactivi.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
18.1.1 TEXT.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
18.1.2 text.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
18.2.1 numere.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
18.2.2 numere.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
19.1.1 poarta.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
19.2.1 mouce.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
21.1.1 alex-nnlog.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
21.1.2 alex-nqlog.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
21.1.3 amat_eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
21.1.4 brut.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
21.1.5 brut_IQ_9000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
21.1.6 chiorean_amat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
21.1.7 solution.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
21.1.8 sursa_test.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
21.2.1 comun_back.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
21.2.2 comun_chiorean.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
21.2.3 comun_eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
21.2.4 comun_io.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
21.2.5 comun_nlog_sub.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
21.2.6 comun_sol.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
21.2.7 sol_nlog.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
21.3.1 pro3_AT1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
21.3.2 pro3_CC1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
21.3.3 pro3_CC2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
21.3.4 pro3_LB1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
21.4.1 cub_chiorean.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
21.4.2 cub_chiorean_brut.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
21.4.3 cub_chiorean_n3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
21.4.4 cub100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
21.4.5 cub100_int.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
21.5.1 bofrac_CC1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
21.5.2 bofrac_CC2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
21.5.3 bofrac_CC3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
21.5.4 bofrac_CC4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
21.5.5 bofrac_CC5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
21.5.6 bofrac_TC1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
21.6.1 alex.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
21.6.2 telefon-adrian-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
21.6.3 telefon-bicsi-100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
21.6.4 tudor_telefon_v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
22.1.1 1_bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
22.1.2 2_bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
22.1.3 3_bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
22.1.4 4_bazaf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
22.2.1 mexitate1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
22.2.2 mexitate2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
22.3.1 plaja_N+K.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
22.3.2 plaja_ternara.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
22.3.3 plaja1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
22.3.4 plaja2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
22.3.5 plaja3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
22.3.6 plaja4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
22.4.1 bsrec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
22.4.2 bsrec2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
22.5.1 1_numinum_100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
22.5.2 2_numinum_100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
22.6.1 rosiimici.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
22.6.2 rosiimici2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
23.1.1 arhipelag_100p_1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
23.1.2 arhipelag_100p_2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
23.2.1 mirror_100p_1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
23.2.2 mirror_100p_2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
23.2.3 mirror_100p_3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
23.2.4 mirror_100p_4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
23.2.5 mirror_100p_5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
23.2.6 mirror_100p_6.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
23.3.1 okcpp_97p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
23.3.2 okcpp_100p_1_doar_linux.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 400
23.3.3 okcpp_100p_2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
23.4.1 adlic_100p_1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
23.4.2 adlic_100p_2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
23.4.3 adlic_100p_3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
23.5.1 bomboane_100p_1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
23.5.2 bomboane_100p_2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
23.5.3 bomboane_100p_3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
23.6.1 orase_100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
24.1.1 civilizatie_BICSI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
24.1.2 civilizatie100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
24.2.1 cmmdc_BICSI.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
24.3.1 livada_dan.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
24.3.2 Livada100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
24.4.1 dif2_bicsi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
24.4.2 dif2_eric.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
24.4.3 dif2_N_log2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
24.5.1 leduri_bicsi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
24.5.2 leduri_eric.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
24.6.1 omogene_eric.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
24.6.2 omogene_n_4.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
24.6.3 omogeneMG.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
25.1.1 cubul_cp1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
25.1.2 cubul_cp2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
25.1.3 cubul_gcc_2matrici.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
25.1.4 cubul_gcc_umplere_ciur.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
25.1.5 cubul_mot_e.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
25.2.1 risc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
25.2.2 risc_n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
25.2.3 risc_nlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
25.3.1 roboti_aib.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
25.3.2 roboti_dp.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
25.3.3 roboti_greedy.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
25.4.1 casa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
25.4.2 casa_slow.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
25.4.3 casa2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484
25.5.1 lenes.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
25.5.2 lenes2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
25.5.3 lenes3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
25.6.1 sipet.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
26.1.1 harta.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
26.1.2 harta_Adriana.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
26.1.3 harta_brut_1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
26.1.4 harta_brut_2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
26.1.5 harta_eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510
26.1.6 harta_pit.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
26.1.7 harta1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512
26.2.1 qvect_eugen_bf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
26.2.2 qvect_eugen_fs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517
26.2.3 qvect_eugen_std.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
26.2.4 qvect_inter.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
26.2.5 qvect_mink.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
26.2.6 qvect_qsort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
26.2.7 qvect_qsortscanf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
26.2.8 qvect_vs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
26.3.1 tg_100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
26.3.2 tg_on.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
26.3.3 tg_on_v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
26.3.4 tg_on2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
26.3.5 tg_onsqrtn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
26.4.1 CC2_progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
26.4.2 CC3_progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
26.4.3 CM_progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
26.4.4 EN_progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
26.4.5 PIT_progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
26.4.6 SP_progresie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
26.5.1 reex_eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
26.5.2 reex_LS_brut.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
26.5.3 reex_LS_Euclid_100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
26.5.4 reex_LS_med.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543
26.5.5 reex_LS_Sr_100p.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 544
26.5.6 reex_vs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
26.6.1 traseu_carmen_nerec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
26.6.2 traseu_carmen_rec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
26.6.3 traseu_eugen.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
26.6.4 traseu_vspit.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
27.1.1 aranjare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
27.2.1 gradina.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
27.3.1 split.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
27.4.1 momente.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
27.5.1 secvente.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567
27.6.1 spider.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
28.1.1 PIT_7segmente.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574
28.1.2 CC_7segmente.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575
28.1.3 RH_7segmente.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
28.2.1 PIT_copaci.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
28.2.2 RH_copaci.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580
28.2.3 VI_copaci.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
28.3.1 PIT1_intersectii.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
28.3.2 PIT2_intersectii.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
28.3.3 brut_intersectii.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
28.3.4 GM_intersectii.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
28.3.5 VI_intersectii.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
28.4.1 CTpalindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
28.4.2 RHpalindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
28.4.3 VIpalindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 593
28.5.1 PRIVsstabil_1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
28.5.2 PRIVsstabil_2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
28.5.3 VI_sstabil.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
28.6.1 GMunuab_100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
28.6.2 GMunuab_back.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
28.6.3 GMunuab_n patrat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
28.6.4 VI_unuzero.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602
29.1.1 poligon.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
29.2.1 stalpi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
29.3.1 tort.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
29.4.1 ape.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
29.5.1 ec.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
29.6.1 furnici.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
30.1.1 cern100v1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
30.1.2 cern100v2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
30.2.1 cmmmc60.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
30.2.2 cmmmc100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
30.3.1 simetric20.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
30.3.2 simetric40.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634
30.3.3 simetric100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
30.4.1 pesti.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
30.5.1 plaja.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
30.6.1 tango.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
31.1.1 joc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
31.2.1 perspic.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
31.3.1 rafturi.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
31.4.1 br.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
31.5.1 origami.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657
31.6.1 patrate.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
32.1.1 ab.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
32.2.1 iepuras.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
32.3.1 palind.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
32.3.2 palindvxn.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
32.3.3 palindvxv.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
32.4.1 auto.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
32.4.2 autonk.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
32.5.1 div_rares.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
32.6.1 teatru.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
32.6.2 teatru_n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
32.6.3 teatru_n3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
33.1.1 agitatie.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
33.2.1 coduri.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
33.3.1 lacuri.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
33.4.1 secv.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
33.5.1 sotron.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
33.6.1 triunghi.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
34.1.1 fact.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
34.1.2 factok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702
34.1.3 fact.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
34.2.1 limbaj.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
34.2.2 limbaj1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
34.2.3 limbaj2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
34.3.1 panouri.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
34.3.2 panouri1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
34.3.3 panouri2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714
34.4.1 pereti.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
34.4.2 peretiok.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718
34.4.3 pereti.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
34.5.1 sant.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
34.5.2 sant1.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
34.5.3 sant.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
34.6.1 zumzi.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
34.6.2 zumzi1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732
34.6.3 zumzi2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734
35.1.1 bifo0.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
35.1.2 bifo1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741
35.1.3 bifo2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742
35.2.1 romeo.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
35.3.1 seceta1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
35.3.2 seceta2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751
35.3.3 seceta3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
35.3.4 seceta4.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 754
35.4.1 biblos1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757
35.4.2 biblos2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
35.4.3 biblos3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759
35.5.1 joc.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
35.6.1 pal.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
36.1.1 COD_OK.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
36.1.2 CODURI.PAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771
36.1.3 coduri.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 772
36.2.1 logic.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
36.2.2 logic0.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
36.2.3 logic1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
36.2.4 logic2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778
36.3.1 poligon.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
36.3.2 poligon.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
36.4.1 sablon.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
36.4.2 sablon0.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
36.4.3 sablon1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
36.5.1 sir.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
36.5.2 sir.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 790
36.6.1 snipers.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
36.6.2 snipers.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793
37.1.1 seti.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
37.1.2 seti0java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
37.1.3 seti1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
37.2.1 scaune.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
37.2.2 scaune1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
37.2.3 scaune2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802
37.3.1 ALLCIRC.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
37.3.2 Circback.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
37.3.3 CIRCULAR.C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
37.3.4 circular1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808
37.3.5 circular2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
37.4.1 criptare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
37.4.2 criptare1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812
37.4.3 criptare2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813
37.5.1 masina.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
37.6.1 operatii.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
38.1.1 pentagon.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
38.1.2 pentagon.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
38.2.1 PODBACK.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
38.2.2 PODDBIG.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
38.2.3 PODIN.CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
38.2.4 pod1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
38.2.5 pod2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829
38.2.6 pod3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830
38.3.1 suma.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
38.3.2 suma.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
38.3.3 becuri1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838
38.3.4 becuri2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
38.3.5 becuri3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
38.4.1 discuri.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845
38.4.2 discuri.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845
38.5.1 cod.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848
38.5.2 codcomb.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
38.5.3 cod.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
39.1.1 ferma1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
39.1.2 ferma2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
39.2.1 fractii.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
39.3.1 tablou1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856
39.3.2 tablou2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857
39.4.1 competitie.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859
39.5.1 cuvinte.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
39.6.1 grup.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
40.1.1 Algoritm-oc.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 866
40.1.2 algoritm1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
40.1.3 algoritm.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
40.2.1 cod2.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870
40.2.2 cod-oc.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
40.2.3 cod1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
40.2.4 cod2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
40.2.5 cod3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 874
40.3.1 comoara-oc.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
40.3.2 comoara.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878
40.4.1 cuburi-oc.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881
40.4.2 cuburi.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
40.5.1 sb.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884
40.5.2 sbmare.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885
40.5.3 bo.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
40.6.1 KMIHAI.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
40.6.2 KOM_MEU.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
40.6.3 Kommando-oc.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891
40.6.4 VERKOMM.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
40.6.5 kommando1.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 894
40.6.6 kommando2.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 895
40.6.7 kommando3.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 896
40.6.8 kommando4.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 898
Partea I

OJI - Olimpiada judeµean  de


informatic 

1
Capitolul 1

OJI 2020

1.1 buldo
Problema 1 - buldo 100 de puncte
Dore³ti s  nivelezi terenul pe care l-ai cump rat, care are l µimea de 1 metru ³i lungimea de
N metri, ind alc tuit din N zone succesive, ecare zon  având lungimea de 1 metru.
Terenul se reprezint  ca un ³ir de N numere naturale h1 , h2 , h3 , ..., hN reprezentând în lµimile
în metri pe care le au zonele din terenul iniµial, privite de la stânga spre dreapta.
Pentru a nivela terenul ai închiriat un buldozer care funcµioneaz  astfel. Se alege o în lµime
H (num r natural) la care ridic m lama buldozerului. Iniµial buldozerul are pe lam  o cantitate
C=0 metri cubi de p mânt. Buldozerul începe s  merg  de la stânga la dreapta ³i când ajunge la
zona i, în funcµie de în lµimea hi a acesteia, se va aa în una dintre urm toarele situaµii:

- dac  hi 'H atunci cantitatea suplimentar  hi  H se adaug  la C ³i nivelul zonei ajunge


la H .
- dac  hi $H atunci se scade din C diferenµa H  hi pentru a aduce nivelul zonei la nivelul
H.

Remarc m faptul c  H trebuie ales iniµial astfel încât de ecare dat  când buldozerul ajunge
în a doua situaµie s  aib  pe lam  sucient p mânt (C ' H  hi ).
Dup  ce buldozerul parcurge cele N zone de lungime 1 pe lama buldozerului e posibil s  mai
r mân  p mânt, dar asta nu te intereseaz , pentru c  la cap tul din dreapta al terenului este un
râu, ³i p mântul r mas se va v rsa acolo.

Cerinµe
Scrieµi un program care calculeaz  în lµimea maxim  H la care poate  ridicat  lama, astfel
încât terenul s  poat   nivelat la acea în lµime.

Date de intrare
Fi³ierul de intrare buldo.in conµine pe prima linie num rul natural N , iar pe a doua linie,
separate prin câte un spaµiu, cele N numere naturale h1 , h2 , h3 , ..., hN , cu semnicaµia din enunµ.

Date de ie³ire
Fi³ierul de ie³ire buldo.out va conµine o singur  linie, pe care va  scris num rul natural H
cerut.

Restricµii ³i preciz ri
a 1&N & 100 000
a În lµimile sunt numere naturale, 1 & hi & 1000 000 000, pentru orice i, 1 & i & N .
a Pentru teste în valoare de 50 de puncte, N & 1 000 ³i hi & 1 000, pentru orice i, 1 & i & N .

Exemple:

2
CAPITOLUL 1. OJI 2020 3

buldo.in buldo.out Explicaµii


4 2 Dac  se xeaz  lama la în lµimea H 2, dup  ce se trece de zona
5216 1 (primul metru pe lungime), aceast  zon  r mâne la în lµimea 2 ³i
C 3 metri cubi de p mânt sunt du³i de lam  la zona 2. Acolo se
vor obµine în total 2+3=5 metri cubi de p mânt, dar se p streaz 
doar 2, iar restul de C 3 se transport  la zona 3. La zona 3 se vor
obµine în total 1+3=4 metri cubi de p mânt, dar se p streaz  doar
2, iar restul de C 2 se transport  la zona 4. La zona 4 se vor obµine
în total 6+2=8 metri cubi de p mânt, dar se p streaz  doar 2, iar
restul de C=6 se arunc  în râu.
Dac  s-ar xa lama la în lµimea H 3, la zona 3 se poate ajunge
doar la în lµimea 2 ³i încercarea e³ueaz  (ind o în lµime mai mic 
decât cea propus ).

Timp maxim de executare/test: 0.3 secunde


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

1.1.1 Indicaµii de rezolvare


prof. Marius Nicoli - C.N. Fraµii Buze³ti - Craiova

Soluµia 1
Pentru ecare în lµime posibil  la care se poate xa lama la prima fâ³ie simul m deplasarea la
dreapta. Timp de executare de ordin H*N (H = în lµimea maxim  pentru fâ³ii iar N = num rul
de f ³ii). Aceteast  soluµie nu se încadreaz  în timp pe toate testele.
Soluµia 2
Observ m c  putem c uta binar rezultatul, având o înbun t µire semnicativ  deoarece nu mai
suntem obligaµi s  test m toate în lµimile posibile.
Soluµia 3
Dac  la poziµia curent  i consider m s = suma valorilor din ³ir, de la început ³i pân  la poziµia
i, atunci s©i este în lµimea maxim  la care putem trece cu lama pe acolo. Soluµia este reprezentat 
minimul dintre valorile s©i. Timpul de executare este de ordin N .
Implementarea corect  a unor abord ri ca a doua ³i a treia de mai sus aduc punctaj maxim.

1.1.2 Cod surs 

Listing 1.1.1: buldoMN_scanf.c


1 #include <stdio.h>
2
3 long long a, b, c, MOD, i, sol, n, s;
4
5 long long min(long long a, long long b)
6 {
7 if(a < b) return a;
8 return b;
9 }
10
11 int main ()
12 {
13 freopen("buldo.in", "r", stdin);
14 freopen("buldo.out", "w", stdout);
15
16 scanf("%lld ", &n);
17 scanf("%lld %lld", &a, &b);
18 s = a;
19 sol = a;
20 s += b;
21 sol = min(sol, s/2);
CAPITOLUL 1. OJI 2020 4

22 for (i=3;i<=n;i++)
23 {
24 ///f[i] = 1 + (1LL*f[i-1]*f[i-2] + f[i-1] + f[i-2]) % MOD;
25 scanf("%lld ", &c);
26 s += c;
27 sol = min(sol, s/i);
28 a = b;
29 b = c;
30 }
31
32 printf("%lld\n", sol);
33 return 0;
34 }

Listing 1.1.2: buldo_GM.cpp


1 #include <fstream>
2 using namespace std;
3 int main()
4 {
5 ifstream in("buldo.in");
6 ofstream out("buldo.out");
7
8 int n,p;
9 long long x,y,w,sp,t;
10
11 in>>n>>x>>y;
12 w=x;
13 sp=x+y;
14 if(w>sp/2)
15 w=sp/2;
16 n=n-2;
17 p=3;
18 while(n--)
19 {
20 //int t=1+(1ll*x*y+x+y)%z;
21 in>>t;
22 sp=sp+t;
23 if(w>sp/p)
24 w=sp/p;
25 p++,x=y,y=t;
26 }
27
28 out<<w<<’\n’;
29 out.close();
30 return 0;
31 }

Listing 1.1.3: buldo_invers_PA_.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 ifstream f("buldo.in");
6 ofstream g("buldo.out");
7
8 const int N = 100010;
9 const int oo = 2000000000;
10 int n,h[N],c[N],v[N],m;
11
12 int main()
13 {
14 f>>n;
15 for(int i=1;i<=n;i++)
16 f>>h[i];
17 v[m]=oo;
18 c[m]=0;
19 for(int i=n;i>=1;i--)
20 {
21 if(h[i]<v[m])
22 {
23 m++;v[m]=h[i];c[m]=1;
24 continue;
CAPITOLUL 1. OJI 2020 5

25
26 }
27
28 int r=h[i]-v[m];
29 c[m]++;
30
31 /// cat timp am suficient sa ridic ultimul nivel la penultimul nivel
32 do
33 {
34 int64_t need = 1LL*c[m]*(v[m-1]-v[m]);
35 if(need<=1LL*r)
36 {
37 c[m-1]+=c[m];
38 r-=need;
39 m--;
40 continue;
41 }
42
43 int hplus=r/c[m];
44 v[m]+=hplus;
45 r=r%c[m];
46 if(r==0)
47 break;
48 m++;
49 v[m]=v[m-1];
50 v[m-1]++;
51 c[m]=c[m-1]-r;
52 c[m-1]=r;
53 break;
54
55 } while(1);
56 }
57
58 g<<v[m];
59 return 0;
60 }

Listing 1.1.4: buldo_MD.cpp


1 #include <fstream>
2 #include <cmath>
3
4 #define vMax 100001
5
6 using namespace std;
7
8 ifstream in("buldo.in");
9 ofstream out("buldo.out");
10
11 long long n,a[vMax];
12
13 int main()
14 {
15 in>>n;
16 for(long long i=1;i<=n;i++)
17 {
18 in>>a[i];
19 }
20
21 long long c=0,h=a[1];
22 for(long long i=2;i<=n;i++)
23 {
24 if(a[i]-h+c>=0)
25 {
26 c+=a[i]-h;
27 }
28 else
29 {
30 long long d=a[i]-h;//dif de acoperit
31 long long dh=floor((double(d+c)/i));//dif cu care trebuie scazuta h
32
33 c-=dh*(i-1);//actualizare cupa
34 h+=dh;//actualizare h
35
36 if(a[i]-h+c<0) out<<"err"<<i;
CAPITOLUL 1. OJI 2020 6

37 c+=a[i]-h;
38 }
39 };
40
41 out << h << endl;
42 return 0;
43 }

Listing 1.1.5: buldo_nlognBD_Bun.cpp


1 //Bodnariuc Danut
2 //O(n*log(mediaElementelor)) - 100p
3 //idee: se cauta binar H in [1,MediaElementelor]
4 //Processor Intel(R) Core(TM) i3-2348M CPU @ 2.30GHz, 2300 Mhz, 2 Core(s)
5 //buldo0..buldo17 <0.12 sec
6
7 #include <fstream>
8
9 #define MAX 100003
10
11 using namespace std;
12
13 ifstream fi("buldo.in");
14 ofstream fo("buldo.out");
15
16 long long H,h[MAX],N,i,C,Lama,s,LamaMax;
17 bool gasitC;
18
19 bool verif(long long H)
20 { //returneaza true daca cu lama
21 long long i,Lama=0; //de inaltime H se poate nivela
22 for(i=1;i<=N;i++)
23 {
24 if(h[i]>=H)Lama+=h[i]-H;
25 else
26 if(Lama>=H-h[i])Lama-=H-h[i];
27 else return false;
28 }
29
30 return true;
31 }
32
33 long long CautareBinara()
34 { //returneaza cel mai mic H
35 long long st,dr,mij; //pentru care nu se poate nivela
36 st=1;dr=LamaMax;
37 while(st<dr)
38 {
39 mij=(st+dr)/2;
40 if(verif(mij)==true)st=mij+1;
41 else dr=mij;
42 }
43
44 return dr;
45 }
46
47 int main()
48 {
49 fi>>N;
50 for(i=1;i<=N;i++)fi>>h[i];
51 for(i=1;i<=N;i++)s+=h[i];
52 LamaMax=s/N+1; //valoarea maxima pt. Lama este media aritm. a sirului
53 fo<<CautareBinara()-1;
54 return 0;
55 }

Listing 1.1.6: buldoMN.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 ifstream fin ("buldo.in");
6 ofstream fout("buldo.out");
CAPITOLUL 1. OJI 2020 7

7
8 long long a, b, c, i, sol, n, s;
9
10 int main ()
11 {
12 fin>>n;
13 fin>>a;
14 if (n == 1)
15 {
16 fout<<a<<"\n";
17 return 0;
18 }
19 fin>>b;
20 s = a;
21 sol = a;
22 s += b;
23 sol = min(sol, s/2);
24 for (i=3;i<=n;i++)
25 {
26 fin>>c;
27 s += c;
28 sol = min(sol, s/i);
29 a = b;
30 b = c;
31 }
32
33 fout<<sol;
34 return 0;
35 }

Listing 1.1.7: buldonlognMN.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 int f[2000010];
6 long long lama;
7 int n, i, mod, st, dr, mid, ok;
8
9 int main ()
10 {
11 ifstream fin ("buldo.in");
12 ofstream fout("buldo.out");
13
14 fin>>n;
15 for (i=1;i<=n;i++)
16 fin>>f[i];
17
18 st = 1;
19 dr = f[1];
20 while (st <= dr)
21 {
22 int mid = (st+dr)/2;
23 lama = f[1] - mid;
24 ok = 1;
25 for (i=2;i<=n;i++)
26 {
27 if (f[i] + lama < mid)
28 {
29 ok = 0;
30 break;
31 }
32 else
33 {
34 lama = f[i] + lama - mid;
35 }
36 }
37
38 if (ok)
39 st = mid+1;
40 else
41 dr = mid-1;
42 }
43
CAPITOLUL 1. OJI 2020 8

44 fout<<dr;
45 return 0;
46 }

1.1.3 *Rezolvare detaliat 

1.2 cetate
Problema 2 - cetate 100 de puncte
Cetatea Vizima din regatul Temeria poate  reprezentat  printr-o matrice cu N linii ³i M
coloane, numerotate începând cu 1. Vizima este o cetate înoritoare, fapt datorat num rului
mare de negustori ³i me³teri prezenµi. Din acest motiv, ec rei celule din matrice îi este atribuit
un prot corespunz tor zonei respective. Regele Foltest dore³te s  reconstruiasc  zidurile cet µii,
dar cum r zboiul cu Imperiul Nilfgaard bate la u³  ³i resursele regatului sunt limitate, el trebuie
s  aleag  o porµiune pe care s  o poat  ap ra, reprezentat  ca o submatrice.
O submatrice este identicat  printr-o conguraµie de patru numere i1 , j1 , i2 , j2 (1 & i1 & i2 &
N , 1 & j1 & j2 & M ), în aceast  ordine, ³i este format  din elementele situate pe liniile consecutive
i1 , i1  1, ..., i2 ³i pe coloanele consecutive j1 , j1  1, ..., j2 ale matricei prin care este reprezentat 
cetatea. Laturile submatricei sunt egale cu num rul de linii, respectiv de coloane din care a preluat
elemente, iar protul submatricei este suma valorilor din celulele sale.
Cerinµe
Scrieµi un program care, cunoscând matricea cet µii ³i o valoare K , determin :
1) protul maxim al unei submatrice cu laturile egale cu K , precum ³i conguraµia prin care
se identic  ea;
2) protul maxim al unei submatrice cu laturile cel mult egale cu K , precum ³i conguraµia
prin care se identic  ea.
Date de intrare
Fi³ierul de intrare cetate.in conµine pe primul rând o valoare c egal  cu 1 sau 2, reprezentând
cerinµa ce urmeaz  a  rezolvat .
Urm toarea linie conµine în ordine N , M ³i K , cu semnicaµia din enunµ, iar pe urm toarele
N linii se a  câte M numere, reprezentând valorile din matricea dat . Numerele aate pe aceea³i
linie a ³ierului sunt separate prin câte un spaµiu.
Date de ie³ire
Fi³ierul de ie³ire cetate.out va conµine pe prima linie protul maxim cerut, conform cerinµei,
iar pe a doua linie va conµine 4 numere naturale, reprezentând conguraµia prin care se identic 
submatricea obµinut . Dac  exist  mai multe submatrice conform cerinµei, se va lua în considerare
cea pentru care conguraµia format  din cele 4 numere de mai sus este minim lexicograc .
Restricµii ³i preciz ri
a 1 & N, M & 400; 1 & K & min N, M 
9 9
a Valorile date pentru matricea cet µii se a  în intervalul 10 , 10 
a Pentru teste în valoare de 20 de puncte, c 1, iar pentru restul testelor, în valoare de 70 de
puncte, c 2. Pentru teste în valoare de 8 puncte, c 1 ³i 1 & N, M & 70. Pentru teste în
valoare de 25 de puncte, c 2 ³i 1 & N, M & 70
a Conguraµia x1 , x2 , x3 , x4  este mai mic  din punctul de vedere lexicograc decât con-
guraµia y1 , y2 , y3 , y4  dac  exist  p astfel încât xp < yp ³i x1 y1 , x2 y2 ... xp1 =
yp1 .

Exemple:
CAPITOLUL 1. OJI 2020 9

cetate.in cetate.out Explicaµii


1 -6 În acest exemplu, se rezolv  cerinµa 1. S-a determinat subma-
343 1133 tricea cu conguraµia i1 1, j1 1, i2 3, j2 3, format  din
-1 -1 -1 -1 elementele aate pe liniile 1, 2 ³i 3 ³i coloanele 1, 2 ³i 3. O alt 
-1 2 -1 -1 submatrice cu protul -6 este cea dat  de conguraµia 1, 2, 3, 4,
-1 -1 -1 -1 care îns  este mai mare din punctul de vedere lexicograc decât
cea a³at , de aceea nu este o soluµie acceptat .
2 3 în acest exemplu, se rezolv  cerinµa 2. Au fost considerate toate
343 2223 submatricele, cu laturile 1, 2 sau 3, iar submatricea g sit , cu
-1 -1 -1 -1 laturile 1 ³i 2, este singura submatrice de prot maxim care
-1 2 1 -1 respect  condiµiile impuse de cerinµ .
-1 -1 -1 -1

Timp maxim de executare/test: 2.0 secunde


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

1.2.1 Indicaµii de rezolvare


stud. Iordache Ioan-Bogdan, Universitatea din Bucure³ti

Soluµie cerinµa 1
Pentru cerinµa 1 este sucient s  precalcul m pentru ecare poziµie (i, j ) din matrice suma
submatricii determinate de colµurile 1, 1  i, j . Acest lucru se poate face u³or în O N ˜ M 
aplicând formula

sumij  matij   sumi  1j   sumij  1  sumi  1j  1

Fix m colµul stânga jos pentru matricea p trat  de dimensiune K , iar suma corespunz toare va 

sumij   sumi  k j   sumij  k   sumi  k j  k 

Complexitate: O N ˜ M
Soluµie cerinµa 2
Dac  x m în toate modurile posibile cele 2 colµuri ale submatricii ³i ne folosim de sumele
2
parµiale de mai sus, obµinem o complexitate de O N ˜ M ˜ K  care obµine în jur de 25 puncte.
Ideea rezolv rii const  în xarea liniilor submatricei c utate în O N ˜ K .
Tot ce mai r mâne de f cut este g sirea coloanelor optime. Folosind sume parµiale pe coloane,
putem reduce acum problema la determinarea unei subsecvenµe de lungime maxim K de sum 
maxim .
Ne vom referi de acum la aceast  problem  mai simpl : dându-se un vector cu M elemente
s  se determine subsecvenµa de sum  maxim , de lungime maxim K .
Pentru ecare poziµie i din vector, reµinem suma primelor i elemente (sume parµiale pe pre-
x). Având xat cap tul dreapta al secvenµei c utate i, ne intereseaza suma parµial  minim 
corespunz toare unei poziµii aate între i  K ³i i  1 (e aceasta poziµie j ). Astfel subsecvenµa
de suma maxim , care are cap tul dreapta i, are cap tul stânga j  1, iar suma ei este diferenµa
dintre suma parµial  de la poziµia i ³i cea de la poziµia j .
R mâne de v zut cum putem determina aceast  sum  parµial  minim  în timp optim.
Consider m ³irul sumelor parµiale pe prex. îl împ rµim în secvenµe de câte K elemente. Pentru
ecare secvenµ  calcul m minim parµial pe sux ³i minim parµial pe prex. Orice subsecvenµ  de
dimensiune K poate  scris  ca reuniunea dintre un sux ³i un prex a dou  secvenµe consecutive.
Astfel putem aa minimul orc rei subsecvenµe în timp constant, iar precalcularea se poate face
liniar.
Obµinem astfel complexitatea nala: O N ˜ M ˜ K  ³i punctajul maxim.

1.2.2 Cod surs 


CAPITOLUL 1. OJI 2020 10

Listing 1.2.1: cetate_optim_ocial_bogdan_scanf.c


1 /*====================================================================
2 * Autor: stud. Iordache Ioan-Bogdan, Universitatea din Bucuresti
3 * Complexitate: O(N*M*K)
4 * Punctaj asteptat: 90p
5 *===================================================================*/
6 #include <stdio.h>
7
8 #define DIM 505
9 #define INF (1LL << 62)
10
11 int n, m, k, a[DIM][DIM], i, j, i1, i2;
12 long long sum[DIM][DIM], v[DIM], min_prefix[DIM], min_suffix[DIM];
13
14 int max(int a, int b)
15 {
16 if(a > b) return a;
17 return b;
18 }
19
20 int main()
21 {
22 freopen("cetate.in", "r", stdin);
23 freopen("cetate.out", "w", stdout);
24
25 int op;
26 scanf("%d", &op);
27 // cin >> op;
28
29 scanf("%d %d %d", &n, &m, &k);
30 // cin >> n >> m >> k;
31
32 for ( i = 1; i <= n; ++i)
33 for ( j = 1; j <= m; ++j)
34 {
35 scanf("%d", &a[i][j]);
36 // cin >> a[i][j];
37 }
38
39 long long best = -INF;
40 int h1 = 0, w1 = 0, h2 = 0, w2 = 0;
41
42 if (op == 1)
43 {
44 for ( i = 1; i <= n; ++i)
45 for ( j = 1; j <= m; ++j)
46 sum[i][j] = a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
47 for ( i = k; i <= n; ++i)
48 {
49 for ( j = k; j <= m; ++j)
50 {
51 long long curr = sum[i][j] - sum[i - k][j] - sum[i][j - k]
52 + sum[i - k][j - k];
53 if (curr > best)
54 {
55 best = curr;
56 h1 = i - k + 1, w1 = j - k + 1, h2 = i, w2 = j;
57 }
58 }
59 }
60 printf("%lld\n", best);
61 printf("%d %d %d %d\n", h1, w1, h2, w2);
62 // cout << best << ’\n’;
63 // cout << h1 << ’ ’ << w1 << ’ ’ << h2 << ’ ’ << w2 << ’\n’;
64 return 0;
65 }
66
67 for ( i = 1; i <= n; ++i)
68 for ( j = 1; j <= m; ++j)
69 sum[i][j] = a[i][j] + sum[i - 1][j];
70
71 for ( i1 = 1; i1 <= n; ++i1)
72 {
73 for ( i2 = i1; i2 < i1 + k && i2 <= n; ++i2)
74 {
CAPITOLUL 1. OJI 2020 11

75 for ( j = 1; j <= m; ++j)


76 v[j] = v[j - 1] + sum[i2][j] - sum[i1 - 1][j];
77
78 for ( j = 1; j <= m; ++j)
79 {
80 if (j % k == 0 || v[j] < v[min_prefix[j - 1]])
81 min_prefix[j] = j;
82 else
83 min_prefix[j] = min_prefix[j - 1];
84 }
85
86 for ( j = m; j >= 0; --j)
87 {
88 if (j == m || (j + 1) % k == 0 || v[j] <= v[min_suffix[j+1]])
89 min_suffix[j] = j;
90 else
91 min_suffix[j] = min_suffix[j + 1];
92 }
93
94 for ( j = 1; j <= m; ++j)
95 {
96 if (j < k)
97 {
98 long long curr = v[j] - v[min_prefix[j - 1]];
99 if (best < curr
100 || (best==curr && h1==i1 && min_prefix[j-1]+1 < w1))
101 {
102 best = curr;
103 h1 = i1, w1 = min_prefix[j - 1] + 1, h2 = i2, w2 = j;
104 }
105 continue;
106 }
107
108 int left = max(0, j - k);
109 int right = j - 1;
110 long long curr;
111 int curr_w1;
112
113 if (v[min_suffix[left]] <= v[min_prefix[right]])
114 curr = v[j] - v[min_suffix[left]],
115 curr_w1 = min_suffix[left] + 1;
116 else
117 curr = v[j] - v[min_prefix[right]],
118 curr_w1 = min_prefix[right] + 1;
119
120 if (best < curr
121 || (best == curr && h1 == i1 && curr_w1 < w1))
122 {
123 best = curr;
124 h1 = i1, w1 = curr_w1, h2 = i2, w2 = j;
125 }
126 }
127 }
128 }
129
130 printf("%lld\n", best);
131 printf("%d %d %d %d\n", h1, w1, h2, w2);
132 // cout << best << ’\n’;
133 // cout << h1 << ’ ’ << w1 << ’ ’ << h2 << ’ ’ << w2 << ’\n’;
134
135 return 0;
136 }
137 /*
138 execution time : 2.020 s
139 */

Listing 1.2.2: cetate_GM.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 int n,m,w,a[550][550],pr[550],su[550];
6 long long sa[550][550],v[550];
7
CAPITOLUL 1. OJI 2020 12

8 struct grup{long long p1;int p2,p3,p4,p5;} sol;


9
10 void act(long long s1,int x1,int y1,int x2,int y2)
11 {
12 if(sol.p1>s1) return;
13 if(sol.p1==s1 && sol.p2<x1) return;
14 if(sol.p1==s1 && sol.p2==x1 && sol.p3<y1) return;
15 if(sol.p1==s1 && sol.p2==x1 && sol.p3==y1 && sol.p4<x2) return;
16 if(sol.p1==s1 && sol.p2==x1 &&
17 sol.p3==y1 && sol.p4==x2 && sol.p5<=y2) return;
18 sol.p1=s1,sol.p2=x1,sol.p3=y1,sol.p4=x2,sol.p5=y2;
19 }
20
21 int main()
22 {
23 ifstream in("cetate.in");
24 ofstream out("cetate.out");
25
26 int t;
27 in>>t>>n>>m>>w;
28
29 sol.p1=-(1LL << 62);
30
31 for(int i=1;i<=n;++i)
32 for(int j=1;j<=m;++j)
33 {
34 in>>a[i][j];
35 sa[i][j]=a[i][j]+sa[i-1][j];
36
37 }
38
39 if(t==1)
40 {
41 for(int i=1;i<=n;++i)
42 for(int j=1;j<=m;++j)
43 sa[i][j]=a[i][j]+sa[i-1][j]+sa[i][j-1]-sa[i-1][j-1];
44 for(int i=w;i<=n;++i)
45 for(int j=w;j<=m;++j)
46 act(sa[i][j]-sa[i-w][j]-sa[i][j-w]+sa[i-w][j-w],i-w+1,j-w+1,i,j);
47 }
48 else
49 {
50 for (int i1=1;i1<=n;++i1)
51 {
52 for (int i2=i1;i2<=n && i2<i1+w;++i2)
53 {
54 for (int j=1;j<=m;++j)
55 v[j]=sa[i2][j]-sa[i1-1][j]+v[j-1];
56 for (int j=1;j<=m;++j)
57 {
58 if(j%w==0 || v[j]<v[pr[j-1]])
59 pr[j]=j;
60 else
61 pr[j]=pr[j-1];
62 }
63 for (int j=m;j>=0; --j)
64 {
65 if(j==m || (j+1)%w==0 || v[j]<=v[su[j+1]])
66 su[j]=j;
67 else
68 su[j]=su[j+1];
69 }
70
71 for (int j=1; j<=m; ++j)
72 {
73 if(j<w)
74 {
75 act(v[j] - v[pr[j-1]],
76 i1, pr[j-1]+1, i2, j);
77 continue;
78 }
79 int left=max(0, j-w);
80 int right=j-1;
81 if(v[su[left]] <= v[pr[right]])
82 act(v[j]-v[su[left]],
83 i1, su[left]+1, i2, j);
CAPITOLUL 1. OJI 2020 13

84 else
85 act(v[j]-v[pr[right]],
86 i1, pr[right]+1, i2, j);
87 }
88 }
89 }
90 }
91
92 long long best_sum;
93 int i1, i2, j1, j2;
94
95 best_sum=sol.p1,i1=sol.p2,j1=sol.p3,i2=sol.p4,j2=sol.p5;
96
97 out << best_sum << ’\n’;
98 out << i1 << ’ ’ << j1 << ’ ’ << i2 << ’ ’ << j2 << ’\n’;
99
100 return 0;
101 }
102 /*
103 execution time : 2.203 s
104 */

Listing 1.2.3: cetate_MD2.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 ifstream in("cetate.in");
6 ofstream out("cetate.out");
7
8 long long c,n,m,k,a[501][501],aa[501][501],s1[501][501],s2[501][501],
9 s2Max[501][501],s2MaxId[501][501],s2Max1[501][501],s2MaxId1[501][501],
10 s2Max2[501][501],s2MaxId2[501][501];
11
12 int main()
13 {
14 in>>c>>n>>m>>k;
15 for(int i=1;i<=n;i++)
16 {
17 for(int j=1;j<=m;j++)
18 {
19 in>>a[i][j];
20 aa[i][j]=a[i][j];
21 }
22 }
23
24 if(c==1)
25 {
26 for(int i=1;i<=n;i++)
27 {
28 s1[i][1]=s1[i-1][1]+a[i][1];
29 for(int j=2;j<=m;j++)
30 {
31 s1[i][j]=s1[i][j-1]+s1[i-1][j]-s1[i-1][j-1]+a[i][j];
32 }
33 }
34
35 long long sMax=s1[k][k],iMax=k,j1Max=k;
36
37 for(int i=k;i<=n;i++)
38 {
39 for(int j=k;j<=m;j++)
40 {
41 if(sMax<s1[i][j]-s1[i][j-k]-s1[i-k][j]+s1[i-k][j-k])
42 {
43 sMax=s1[i][j]-s1[i][j-k]-s1[i-k][j]+s1[i-k][j-k];
44 iMax=i;j1Max=j;
45 }
46 }
47 }
48 out<<sMax<<’\n’<<iMax-k+1<<’ ’<<j1Max-k+1<<’ ’<<iMax<<’ ’<<j1Max<<’\n’;
49
50 return 0;
51 }
CAPITOLUL 1. OJI 2020 14

52
53 long long sMax=a[1][1],iMax=1,j1Max=1,j2Max=1,kMax=1;
54
55 for(int ik=1;ik<=k;ik++)
56 {
57 for(int i=1;i<=n-ik+1;i++)
58 {
59 s1[i][1]=a[i][1];
60 for(int j=2;j<=m;j++)
61 {
62 s1[i][j]=s1[i][j-1]+a[i][j];
63 }
64
65 s2[i][m]=a[i][m];
66 for(int j=m-1;j>0;j--)
67 {
68 s2[i][j]=s2[i][j+1]+a[i][j];
69 }
70
71 s2Max1[i][1]=s2[i][1];
72 s2MaxId1[i][1]=1;
73 for(int j=2;j<=m;j++)
74 {
75 if(s2[i][j]>s2Max1[i][j-1]||j%k==1)
76 {
77 s2Max1[i][j]=s2[i][j];
78 s2MaxId1[i][j]=j;
79 }
80 else
81 {
82 s2Max1[i][j]=s2Max1[i][j-1];
83 s2MaxId1[i][j]=s2MaxId1[i][j-1];
84 }
85 }
86
87 s2Max2[i][m]=s2[i][m];
88 s2MaxId2[i][m]=m;
89 for(int j=m-1;j>0;j--)
90 {
91 if(s2[i][j]>=s2Max2[i][j+1]||j%k==1)
92 {
93 s2Max2[i][j]=s2[i][j];
94 s2MaxId2[i][j]=j;
95 }
96 else
97 {
98 s2Max2[i][j]=s2Max2[i][j+1];
99 s2MaxId2[i][j]=s2MaxId2[i][j+1];
100 }
101 }
102
103 for(int j=1;j<=m;j++)
104 {
105 if(j<=k)
106 {
107 s2Max[i][j]=s2Max1[i][j];
108 if(s2Max[i][j-1]==s2Max1[i][j])
109 {
110 s2MaxId[i][j]=s2MaxId1[i][j-1];
111 }
112 else
113 {
114 s2MaxId[i][j]=s2MaxId1[i][j-1];
115 }
116 }
117 else
118 {
119 if(s2Max1[i][j]>=s2Max2[i][j-k+1])
120 {
121 s2Max[i][j]=s2Max1[i][j];
122 s2MaxId[i][j]=s2MaxId1[i][j];
123 }
124 else
125 {
126 s2Max[i][j]=s2Max2[i][j-k+1];
127 s2MaxId[i][j]=s2MaxId2[i][j-k+1];
CAPITOLUL 1. OJI 2020 15

128 }
129 }
130 }
131
132 for(int j=2;j<=m;j++)
133 {
134 if(s1[i][j]+s2Max[i][j]-s2[i][1]==sMax)
135 {
136 if(iMax>i || iMax==i && j1Max>s2MaxId[i][j])
137 {
138 iMax=i;j1Max=s2MaxId[i][j];j2Max=j;kMax=ik-1;
139 }
140 }
141 else
142 {
143 if(s1[i][j]+s2Max[i][j]-s2[i][1]>sMax)
144 {
145 sMax=s1[i][j]+s2Max[i][j]-s2[i][1];
146 iMax=i;
147 j1Max=s2MaxId[i][j];
148 j2Max=j;kMax=ik-1;
149 }
150 }
151 }
152
153 for(int j=1;j<=m;j++)
154 {
155 a[i][j]+=aa[i+ik][j];
156 }
157 }
158 }
159
160 out<<sMax<<’\n’<<iMax<<’ ’<<j1Max<<’ ’<<iMax+kMax<<’ ’<<j2Max<<’\n’;
161
162 return 0;
163 }
164 /*
165 execution time : 5.510 s
166 */

Listing 1.2.4: cetate_optim_1_bogdan.cpp


1 /*======================================================================
2 * Autor: stud. Iordache Ioan-Bogdan, Universitatea din Bucuresti
3 * Complexitate: O(N*M*K)
4 * Punctaj asteptat: 90p
5 *=====================================================================*/
6 #include <fstream>
7 #include <iostream>
8
9 using namespace std;
10
11 const int DIM = 505;
12 const long long INF = (1LL << 62);
13
14 int n, m, k, a[DIM][DIM];
15 long long sum[DIM][DIM], v[DIM];
16
17 int st, dr;
18 int deq[DIM];
19
20 int main()
21 {
22 #ifndef TESTING
23 ifstream cin("cetate.in");
24 //ofstream cout("cetate.out");
25 #endif
26
27 int op;
28 cin >> op;
29 cin >> n >> m >> k;
30 for (int i = 1; i <= n; ++i)
31 for (int j = 1; j <= m; ++j)
32 cin >> a[i][j];
33
CAPITOLUL 1. OJI 2020 16

34 long long best = -INF;


35 int h1 = 0, w1 = 0, h2 = 0, w2 = 0;
36
37 if (op == 1)
38 {
39 for (int i = 1; i <= n; ++i)
40 for (int j = 1; j <= m; ++j)
41 sum[i][j] = a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
42
43 for (int i = k; i <= n; ++i)
44 {
45 for (int j = k; j <= m; ++j)
46 {
47 long long curr = sum[i][j] - sum[i - k][j] - sum[i][j - k]
48 + sum[i - k][j - k];
49 if (curr > best)
50 {
51 best = curr;
52 h1 = i - k + 1, w1 = j - k + 1, h2 = i, w2 = j;
53 }
54 }
55 }
56
57 cout << best << ’\n’;
58 cout << h1 << ’ ’ << w1 << ’ ’ << h2 << ’ ’ << w2 << ’\n’;
59
60 return 0;
61 }
62
63 for (int i = 1; i <= n; ++i)
64 for (int j = 1; j <= m; ++j)
65 sum[i][j] = a[i][j] + sum[i - 1][j];
66
67 for (int i1 = 1; i1 <= n; ++i1)
68 {
69 for (int i2 = i1; i2 < i1 + k && i2 <= n; ++i2)
70 {
71 for (int j = 1; j <= m; ++j)
72 v[j] = v[j - 1] + sum[i2][j] - sum[i1 - 1][j];
73 st = 1;
74 dr = 1;
75 deq[1] = 0;
76 for (int j = 1; j <= m; ++j)
77 {
78 if (deq[st] == j - k - 1)
79 st++;
80 long long curr = v[j] - v[deq[st]];
81 if (best < curr ||
82 (best == curr && i1 == h1 && deq[st] + 1 < w1))
83 {
84 best = curr;
85 h1 = i1, w1 = deq[st] + 1, h2 = i2, w2 = j;
86 }
87
88 while (st <= dr && v[deq[dr]] > v[j])
89 dr--;
90 deq[++dr] = j;
91 }
92 }
93 }
94
95 cout << best << ’\n’;
96 cout << h1 << ’ ’ << w1 << ’ ’ << h2 << ’ ’ << w2 << ’\n’;
97
98 return 0;
99 }
100 /*
101 58436002
102 60 66 368 363
103
104 Process returned 0 (0x0) execution time : 2.096 s
105 Press any key to continue.
106 */

Listing 1.2.5: cetate_optim_ocial_bogdan.cpp


CAPITOLUL 1. OJI 2020 17

1 /*========================================================================
2 * Autor: stud. Iordache Ioan-Bogdan, Universitatea din Bucuresti
3 * Complexitate: O(N*M*K)
4 * Punctaj asteptat: 90p
5 *=======================================================================*/
6 #include <fstream>
7 #include <iostream>
8
9 using namespace std;
10
11 const int DIM = 505;
12 const long long INF = (1LL << 62);
13
14 int n, m, k, a[DIM][DIM];
15 long long sum[DIM][DIM], v[DIM], min_prefix[DIM], min_suffix[DIM];
16
17 int main()
18 {
19 #ifndef TESTING
20 ifstream cin("cetate.in");
21 ofstream cout("cetate.out");
22 #endif
23
24 int op;
25 cin >> op;
26 cin >> n >> m >> k;
27 for (int i = 1; i <= n; ++i)
28 for (int j = 1; j <= m; ++j)
29 cin >> a[i][j];
30
31 long long best = -INF;
32 int h1 = 0, w1 = 0, h2 = 0, w2 = 0;
33
34 if (op == 1)
35 {
36 for (int i = 1; i <= n; ++i)
37 for (int j = 1; j <= m; ++j)
38 sum[i][j] = a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
39 for (int i = k; i <= n; ++i)
40 {
41 for (int j = k; j <= m; ++j)
42 {
43 long long curr = sum[i][j] - sum[i - k][j] - sum[i][j - k]
44 + sum[i - k][j - k];
45 if (curr > best)
46 {
47 best = curr;
48 h1 = i - k + 1, w1 = j - k + 1, h2 = i, w2 = j;
49 }
50 }
51 }
52
53 cout << best << ’\n’;
54 cout << h1 << ’ ’ << w1 << ’ ’ << h2 << ’ ’ << w2 << ’\n’;
55 return 0;
56 }
57
58 for (int i = 1; i <= n; ++i)
59 for (int j = 1; j <= m; ++j)
60 sum[i][j] = a[i][j] + sum[i - 1][j];
61
62 for (int i1 = 1; i1 <= n; ++i1)
63 {
64 for (int i2 = i1; i2 < i1 + k && i2 <= n; ++i2)
65 {
66 for (int j = 1; j <= m; ++j)
67 v[j] = v[j - 1] + sum[i2][j] - sum[i1 - 1][j];
68
69 for (int j = 1; j <= m; ++j)
70 {
71 if (j % k == 0 || v[j] < v[min_prefix[j - 1]])
72 min_prefix[j] = j;
73 else
74 min_prefix[j] = min_prefix[j - 1];
75 }
CAPITOLUL 1. OJI 2020 18

76
77 for (int j = m; j >= 0; --j)
78 {
79 if (j == m || (j + 1) % k == 0 || v[j] <= v[min_suffix[j+1]])
80 min_suffix[j] = j;
81 else
82 min_suffix[j] = min_suffix[j + 1];
83 }
84
85 for (int j = 1; j <= m; ++j)
86 {
87 if (j < k)
88 {
89 long long curr = v[j] - v[min_prefix[j - 1]];
90 if (best < curr
91 || (best==curr && h1==i1 && min_prefix[j-1]+1 < w1))
92 {
93 best = curr;
94 h1 = i1, w1 = min_prefix[j - 1] + 1, h2 = i2, w2 = j;
95 }
96 continue;
97 }
98
99 int left = max(0, j - k);
100 int right = j - 1;
101 long long curr;
102 int curr_w1;
103 if (v[min_suffix[left]] <= v[min_prefix[right]])
104 curr = v[j] - v[min_suffix[left]],
105 curr_w1 = min_suffix[left] + 1;
106 else
107 curr = v[j] - v[min_prefix[right]],
108 curr_w1 = min_prefix[right] + 1;
109
110 if (best < curr
111 || (best == curr && h1 == i1 && curr_w1 < w1))
112 {
113 best = curr;
114 h1 = i1, w1 = curr_w1, h2 = i2, w2 = j;
115 }
116 }
117 }
118 }
119
120 cout << best << ’\n’;
121 cout << h1 << ’ ’ << w1 << ’ ’ << h2 << ’ ’ << w2 << ’\n’;
122
123 return 0;
124 }
125 /*
126 execution time : 2.028 s
127 */

Listing 1.2.6: cetateBFixColMN.cpp


1 #include <fstream>
2 #include <iostream>
3
4 #define DIM 502
5
6 using namespace std;
7
8 long long a[DIM][DIM], v[DIM], sol, suma, s;
9 int n, m, k, i, j, i1, j1, i2, j2, t;
10 int d[DIM];
11 int p, u, c1, c2;
12
13 int main ()
14 {
15 ifstream fin ("cetate.in");
16 ofstream fout("cetate.out");
17
18 fin>>t>>n>>m>>k;
19 for (i=1;i<=n;i++)
20 for (j=1;j<=m;j++)
CAPITOLUL 1. OJI 2020 19

21 fin>>a[i][j];
22
23 sol = 1LL*DIM*DIM*-1000000000;
24 if (t == 1)
25 {
26
27 for (i=1;i<=n;i++)
28 for (j=1;j<=m;j++)
29 a[i][j] += a[i][j-1] + a[i-1][j] - a[i-1][j-1];
30
31 for (i=k;i<=n;i++)
32 for (j=k;j<=m;j++)
33 {
34 long long suma = a[i][j]-a[i-k][j]-a[i][j-k]+a[i-k][j-k];
35 if (suma > sol)
36 {
37 sol = suma;
38 i1 = i-k+1;
39 j1 = j-k+1;
40 i2 = i;
41 j2 = j;
42 }
43 }
44
45 fout<<sol<<"\n";
46 fout<<i1<<" "<<j1<<" "<<i2<<" "<<j2<<"\n";
47 }
48 else
49 {
50 for (j=2;j<=m;j++)
51 for (i=1;i<=n;i++)
52 a[i][j] += a[i][j-1];
53
54 for (c1 = 1; c1<=m; c1++)
55 for (c2 = c1; c2<=m && c2-c1+1 <= k; c2++)
56 {
57 v[1] = a[1][c2] - a[1][c1-1];
58
59 for (i=2;i<=n;i++)
60 v[i] = a[i][c2] - a[i][c1-1];
61
62 s = v[1];
63 p = 1;
64 if (v[1] > sol)
65 {
66 sol = v[1];
67 i1 = 1;
68 j1 = c1;
69 i2 = 1;
70 j2 = c2;
71 }
72
73 for (i=2;i<=n;i++)
74 {
75 if (s + v[i] >= v[i])
76 {
77 s += v[i];
78 if (i-p+1 > k)
79 {
80 s-=v[p];
81 p++;
82 while (p!=i && v[p] < 0)
83 {
84 s -= v[p];
85 p++;
86 }
87 }
88 }
89 else
90 {
91 s = v[i];
92 p = i;
93 }
94
95 if (s > sol)
96 {
CAPITOLUL 1. OJI 2020 20

97 sol = s;
98 i1 = p;
99 j1 = c1;
100 i2 = i;
101 j2 = c2;
102 }
103 }
104
105 }
106
107 fout<<sol<<"\n";
108 fout<<i1<<" "<<j1<<" "<<i2<<" "<<j2<<"\n";
109 }
110
111 return 0;
112 }
113 /*
114 execution time : 0.892 s
115 */

Listing 1.2.7: cetateMN.cpp


1 #include <fstream>
2 #include <iostream>
3
4 #define DIM 502
5
6 using namespace std;
7
8 long long a[DIM][DIM], v[DIM], sol, suma;
9 int n, m, k, i, j, i1, j1, i2, j2, t;
10 int d[DIM];
11 int p, u, c1, c2;
12
13 int main ()
14 {
15 ifstream fin ("cetate.in");
16 ofstream fout("cetate.out");
17
18 fin>>t>>n>>m>>k;
19 for (i=1;i<=n;i++)
20 for (j=1;j<=m;j++)
21 fin>>a[i][j];
22
23 sol = 1LL*DIM*DIM*-1000000000;
24
25 if (t == 1)
26 {
27
28 for (i=1;i<=n;i++)
29 for (j=1;j<=m;j++)
30 a[i][j] += a[i][j-1] + a[i-1][j] - a[i-1][j-1];
31
32 for (i=k;i<=n;i++)
33 for (j=k;j<=m;j++)
34 {
35 long long suma = a[i][j]-a[i-k][j]-a[i][j-k]+a[i-k][j-k];
36 if (suma > sol)
37 {
38 sol = suma;
39 i1 = i-k+1;
40 j1 = j-k+1;
41 i2 = i;
42 j2 = j;
43 }
44 }
45
46 fout<<sol<<"\n";
47 fout<<i1<<" "<<j1<<" "<<i2<<" "<<j2<<"\n";
48 }
49 else
50 {
51 for (j=2;j<=m;j++)
52 for (i=1;i<=n;i++)
53 a[i][j] += a[i][j-1];
CAPITOLUL 1. OJI 2020 21

54
55 for (c1 = 1; c1<=m; c1++)
56 for (c2 = c1; c2<=m && c2-c1+1 <= k; c2++)
57 {
58 v[1] = a[1][c2] - a[1][c1-1];
59 if (v[1] > sol)
60 {
61 sol = v[1];
62 i1 = 1;
63 j1 = c1;
64 i2 = 1;
65 j2 = c2;
66 ///cout<<1<<" "<<sol<<"\n";
67 }
68
69 for (i=2;i<=n;i++)
70 v[i] = v[i-1] + a[i][c2] - a[i][c1-1];
71 p = 1;
72 u = 1;
73 d[1] = 0;
74 for (i=1;i<=n;i++)
75 {
76 if (v[i] - v[i-1] > sol)
77 {
78 sol = v[i] - v[i-1];
79 i1 = i;
80 j1 = c1;
81 i2 = i;
82 j2 = c2;
83 ///cout<<2<<" "<<sol<<"\n";
84 }
85
86 if (k > 1)
87 {
88
89 if (v[i] - v[d[p]] > sol)
90 {
91 sol = v[i] - v[d[p]];
92 i1 = d[p]+1;
93 j1 = c1;
94 i2 = i;
95 j2 = c2;
96 ///cout<<3<<" "<<sol<<"\n";
97 }
98
99 while (p <= u && v[i] < v[ d[u] ])
100 u--;
101 d[++u] = i;
102 if (i-d[p] == k)
103 p++;
104 }
105 }
106 }
107
108 fout<<sol<<"\n";
109 fout<<i1<<" "<<j1<<" "<<i2<<" "<<j2<<"\n";
110 }
111
112 return 0;
113 }
114 /*
115 execution time : 2.173 s
116 */

Listing 1.2.8: fast_unocial_alexandru.cpp


1 /**
2 Alexandru Petrescu
3 O(N^2 * K * logN)
4 featuring: aib
5 **/
6
7 #include <cstdio>
8 #include <cctype>
9 #include <cstring>
CAPITOLUL 1. OJI 2020 22

10 #include <algorithm>
11
12 FILE *fin = fopen("cetate.in", "r"), *fout = fopen("cetate.out", "w");
13
14 #define BUF_SIZE 1 << 17
15
16 int pos = BUF_SIZE;
17 char buf[BUF_SIZE];
18
19 inline char nextch()
20 {
21 if (pos == BUF_SIZE)
22 fread(buf, BUF_SIZE, 1, fin), pos = 0;
23 return buf[pos++];
24 }
25
26 inline int read()
27 {
28 char ch = nextch();
29 while (ch != ’-’ && !isdigit(ch)) ch = nextch();
30 int s = 1;
31 if (ch == ’-’) s = -1, ch = nextch();
32 int x = ch - ’0’;
33 while (isdigit(ch = nextch())) x = 10 * x + ch - ’0’;
34 return s * x;
35 }
36
37 #define LIM 501
38
39 int n;
40 long long aib[LIM], s[LIM][LIM];
41
42 inline void update(int p, long long x)
43 {
44 while (p <= n)
45 {
46 aib[p] = std::max(aib[p], x);
47 p += p & (-p);
48 }
49 }
50
51 inline long long query(int p)
52 {
53 long long ans = aib[p];
54 do
55 {
56 p -= p & (-p);
57 ans = std::max(ans, aib[p]);
58 } while (p);
59 return ans;
60 }
61
62 int main()
63 {
64 int c = read();
65 int nrlin = read();
66 int nrcol = read();
67 int k = read();
68
69 if (c != 2)
70 return 0;
71 n = nrcol;
72
73 for (int i = 1; i <= nrlin; i++)
74 for (int j = 1; j <= nrcol; j++)
75 s[i][j] = read() + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
76
77 long long ans = s[1][1];
78 int sol1 = 1, sol2 = 1, sol3 = 1, sol4 = 1;
79 for (int i = 1; i <= nrlin; i++)
80 {
81 for (int l = i; l <= nrlin && l - i < k; l++)
82 {
83 memset(aib, 0x80, sizeof(aib[0]) * (nrcol + 1));
84 for (int j = nrcol; j > 0; j--)
85 {
CAPITOLUL 1. OJI 2020 23

86 update(j, s[l][j] - s[i - 1][j]);


87 long long val = query(std::min(j + k - 1, nrcol)) -
88 (s[l][j - 1] - s[i - 1][j - 1]);
89 if (val > ans || (val == ans && i == sol1 && j < sol2))
90 sol1 = i, sol2 = sol4 = j, sol3 = l, ans = val;
91 }
92 }
93 }
94
95 while (s[sol3][sol4] - s[sol3][sol2 - 1] -
96 s[sol1 - 1][sol4] + s[sol1 - 1][sol2 - 1] != ans)
97 sol4++;
98
99 fprintf(fout, "%lld\n%d %d %d %d\n", ans, sol1, sol2, sol3, sol4);
100
101 fclose(fin);
102 fclose(fout);
103
104 return 0;
105 }
106 /*
107 execution time : 6.104 s
108 */

Listing 1.2.9: fastest_unocial_alexandru.cpp


1 /**
2 Alexandru Petrescu
3 O(N^2 * K * logK)
4 featuring: heapuri de mana
5 **/
6
7 #include <cstdio>
8 #include <cctype>
9 #include <cstring>
10 #include <algorithm>
11
12 FILE *fin = fopen("cetate.in", "r"),
13 *fout = fopen("cetate.out", "w");
14
15 #define BUF_SIZE 1 << 17
16
17 int pos = BUF_SIZE;
18 char buf[BUF_SIZE];
19
20 inline char nextch()
21 {
22 if (pos == BUF_SIZE) fread(buf, BUF_SIZE, 1, fin), pos = 0;
23 return buf[pos++];
24 }
25
26 inline int read()
27 {
28 char ch = nextch();
29 while (ch != ’-’ && !isdigit(ch)) ch = nextch();
30 int s = 1;
31 if (ch == ’-’) s = -1, ch = nextch();
32 int x = ch - ’0’;
33 while (isdigit(ch = nextch())) x = 10 * x + ch - ’0’;
34 return s * x;
35 }
36
37 #define LIM 501
38
39 int n, heap[LIM], poz[LIM];
40 long long d[LIM], s[LIM][LIM];
41
42 inline void mySwap(int &p, int q)
43 {
44 int aux = heap[p];
45 heap[p] = heap[q];
46 heap[q] = aux;
47 poz[heap[p]] = p;
48 poz[heap[q]] = q;
49 p = q;
CAPITOLUL 1. OJI 2020 24

50 }
51
52 inline void urcare(int p)
53 {
54 if (p > n) return ;
55 while (p > 1 && d[heap[p]] > d[heap[p / 2]]) mySwap(p, p / 2);
56 }
57
58 inline void coborare(int p)
59 {
60 int q;
61 bool f = 1;
62 while (f && (q = 2 * p) <= n)
63 {
64 q += (q < n && d[heap[q + 1]] > d[heap[q]]);
65 if (d[heap[q]] > d[heap[p]]) mySwap(p, q);
66 else f = 0;
67 }
68 }
69
70 inline void baga(int p, long long val)
71 {
72 heap[++n] = p;
73 d[p] = val;
74 poz[p] = n;
75 urcare(n);
76 }
77
78 inline void scoate(int p)
79 {
80 int u = poz[p];
81 heap[u] = heap[n];
82 poz[heap[n--]] = u;
83 coborare(u);
84 urcare(u);
85 }
86
87 int main()
88 {
89 int c = read();
90 int nrlin = read();
91 int nrcol = read();
92 int k = read();
93
94 if (c != 2)
95 return 0;
96
97 for (int i = 1; i <= nrlin; i++)
98 for (int j = 1; j <= nrcol; j++)
99 s[i][j] = read() + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
100
101 long long ans = s[1][1];
102 int sol1 = 1, sol2 = 1, sol3 = 1, sol4 = 1;
103 for (int i = 1; i <= nrlin; i++)
104 {
105 for (int l = i; l <= nrlin && l - i < k; l++)
106 {
107 n = 0;
108 for (int j = nrcol; j > 0; j--)
109 {
110 baga(j, s[l][j] - s[i - 1][j]);
111 if (j + k <= nrcol) scoate(j + k);
112 long long val = d[heap[1]] - (s[l][j - 1] - s[i - 1][j - 1]);
113 if (val > ans || (val == ans && i == sol1 && j < sol2))
114 sol1 = i, sol2 = sol4 = j, sol3 = l, ans = val;
115 }
116 }
117 }
118
119 while (s[sol3][sol4] - s[sol3][sol2 - 1] -
120 s[sol1 - 1][sol4] + s[sol1 - 1][sol2 - 1] != ans)
121 sol4++;
122
123 fprintf(fout, "%lld\n%d %d %d %d\n", ans, sol1, sol2, sol3, sol4);
124
125 fclose(fin);
CAPITOLUL 1. OJI 2020 25

126 fclose(fout);
127 return 0;
128 }
129 /*
130 execution time : 4.947 s
131 */

1.2.3 *Rezolvare detaliat 

1.3 spiralmatrix
Problema 3 - spiralmatrix 100 de puncte
Parcurgând elementele unei matrice p tratice de dimensiune n în spiral , pornind din colµul
din stânga-sus, în sens orar, de la margini c tre interior, se obµine ³irul strict cresc tor format din
2
toate valorile de la 1 la n , ca în gura de mai jos. Din ³irul dat se obµin dou  sub³iruri disjuncte,
de lungime egal , cu num r maxim de termeni. Primul sub³ir este format din numere consecutive
din prima jum tate a ³irului, ³i trebuie s  conµin  în mod obligatoriu valoarea 1, iar al doilea
este format din numere consecutive din a doua jum tate a ³irului ³i trebuie s  conµin  în mod
2
obligatoriu valoarea n .

1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

Cerinµe
S  se ae poziµia în matrice a celui mai mare termen din primul sub³ir ³i a celui mai mic
termen din al doilea sub³ir.

Date de intrare
Fi³ierul de intrare spiralmatrix.in conµine num rul natural n.

Date de ie³ire
În ³ierul de ie³ire spiralmatrix.out se vor scrie:

- pe prima linie dou  numere, separate printr-un spaµiu, reprezentând mai întâi linia ³i apoi
coloana pe care se a  în matrice cel mai mare termen al primului sub³ir;
- pe a doua linie alte dou  numere, separate printr-un spaµiu, reprezentând mai întâi linia ³i
apoi coloana pe care se a  în matrice cel mai mic termen al celui de al doilea sub³ir.

Restricµii ³i preciz ri
a 1 $ n $ 1000 000 000;
a Pentru teste în valoare de 45 de puncte n este impar;
a Pentru teste în valoare de 45 de puncte n $ 1 000;
a Pentru teste în valoare de 75 de puncte n $ 1000 000;
a Liniile sunt numerotate de sus în jos începând cu 1, iar coloanele sunt numerotate de la
stânga la dreapta începând cu 1;
a Punctajul pe un test se obµine doar dac  sunt corecte toate cele 4 valori.

Exemple:
CAPITOLUL 1. OJI 2020 26

spiralmatrix.inspiralmatrix.out
Explicaµii
5 52 Primul sub³ir este format din valorile de la 1 la 12. Valoarea 12
41 se g se³te pe linia 5 ³i coloana 2.
Al doilea sub³ir este format din valorile de la 14 la 25. Valoarea
14 se g se³te pe linia 4 ³i coloana 1.

4 43 Primul sub³ir este format din valorile de la 1 la 8.


42 Valoarea 8 se g se³te pe linia 4 ³i coloana 3.
Al doilea sub³ir este format din valorile de la 9 la 16.
Valoarea 9 se g se³te pe linia 4 ³i coloana 2.

Timp maxim de executare/test: 0.2 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

1.3.1 Indicaµii de rezolvare


prof. Marcel Dr gan, Colegiul Naµional Samuel von Brukenthal - Sibiu

Varianta 1 - 45p
2
Complexitate O n 
Structuri de date utilizate: matrice
Declar m o matrice de dimensiune nn ³i o parcurgem în spiral  conform enunµului completând
2
valorile. Când ajungem la n ©2 p str m num rul liniei ³i al coloanei curente, aceasta este poziµia
ultimului element al primului sub³ir. Pentru a g si poziµia primului element al celui de al doilea
sub³ir, mai parcurgem înc  unul sau doi pa³i in funcµie de paritatea lui n:

- dac  n este par mai parcurgem un singur pas

- dac  n este impar mai parcurgem doi pa³i

Matricea poate  parcurs  si f r  s  e efectiv declarat  în memorie, deoarece elementele aii
ale ec rei spire k respect  condiµia: k & i, j & n  k  1
Varianta 2 - 75p
Complexitate O n
În loc s  parcurgem matricea element cu element o parcurgem s rind de pe o spir  pe urm -
2
toarea pân  când g sim spira pe care se a  elementul c utat (adic  n ©2). Dup  ce am g sit
spira, o parcurgem element cu element pân  g sim valoarea respectiv .
Pentru calculul valorii de început a ec rei spire folosim urm torul raµionament:
Primul element de pe prima spir  este 1.
Prima spir  are 4 ˜ n  1 elemente, deci primul element de pe spira a doua, a22 are
valoarea egal  cu a11  4 ˜ n  1.
A doua spir  are 4 ˜ n  3 elemente, deci primul element de pe spira a treia a33 are
valoarea egal  cu a22  4 ˜ n  3
...
In general se poate demonstra prin inducµie c : ai  1i  1 aii  4 ˜ n  2 ˜ i  1,
pentru i 1, 2, ..., n©2
Varianta 3 - 90p
Complexitate O log n
În loc s  parcurgem matricea s rind de pe o spir  pe urm toarea, vom folosi c utare binar 
2
pentru a g si spira pe care se a  elementul c utat (adic  n ©2). Dup  ce am g sit spira, putem
calcula pe care dintre cele 4 laturi ale spirei se g se³te valoarea c utat  ³i apoi locul exact.
Pentru calculul valorii de început a ec rei spire folosim urm torul raµionament:
2
ai  1i  1 1  4 ˜ n  1  4 ˜ n  3  ...  4 ˜ n  2 ˜ i  1 14˜i˜n4˜i

Varianta 4 - 90p
Complexitate O log n
CAPITOLUL 1. OJI 2020 27

Deoarece cele dou  sub³iruri au aceea³i lungime, indiferent în ce direcµie parcurgem spirala
e c  o parcurgem de la exterior c tre interior (ca în enunµ) e c  o parcurgem de la interior
c tre exterior locul de împ rµire în cele dou  sub³iruri se va aa în acela³i loc în matrice, doar c 
inversat (adic  în locul ultimului element al primului sub³ir se va aa primul element al celui de
al doilea sub³ir ³i viceversa). Asta înseamn  c  ne putem folosi de o matrice în care valorile sunt
dispuse în spiral  de la interior spre exterior. Într-o astfel de matrice primul element al matricei
2 2
este n , al doilea n  1 ³.a.m.d., iar elementul de început al ec rei spire este un p trat perfect:
2
a11 n
2 2 2
a22 a11  4 ˜ n  1 n  4 ˜ n  1 n  4 ˜ n  4 n  2
...
2
Pentru a g si spira pe care se a  elementul n ©2 folosim c utarea binar .
Odat  g sit  spira prin calcul direct putem calcula pe care dintre cele 4 laturi ale spirei se
g se³te valoarea c utat  ³i apoi locul exact.
Varianta 5 - 90p
Complexitate O 1
În loc s  folosim c utarea binar  pentru a g si spira pe care se a  elementul c utat o calcul m
2
direct prin împ rµirea lui n la radical din 2.

1.3.2 Cod surs 

Listing 1.3.1: spiral_matrix45pBD.cpp


1 //Bodnariuc Danut
2 //brute force O(n*n) -45p
3 //idee: se parcurge matricea spira cu spira element cu element
4
5 #include <fstream>
6
7 #define MAX 100003
8
9 using namespace std;
10
11 ifstream fi("spiralmatrix.in");
12 ofstream fo("spiralmatrix.out");
13
14 int elem_cautat1,elem_cautat2,i,k,n,sp,t,j,sol1i,sol1j,sol2i,sol2j;
15 bool gasit;
16
17 int main()
18 {
19 fi>>n;
20 if(n%2==0)
21 {
22 elem_cautat1=n*n/2;
23 elem_cautat2=n*n/2+1;
24 }
25 else
26 {
27 elem_cautat1=n*n/2;
28 elem_cautat2=n*n/2+2;
29 }
30
31 k=0;
32 sp=n/2; //sp- numar spire
33 gasit=false;
34 for(t=1;t<=sp and gasit==false;t++)
35 {
36 for(j=t;j<=n-t+1 and gasit==false;j++) //linia t
37 { k++;
38 if(k==elem_cautat1){sol1i=t;sol1j=j;}
39 if(k==elem_cautat2){sol2i=t;sol2j=j;gasit=true;}
40 }
41
42 for(i=t+1;i<=n-t+1 and gasit==false;i++) //col n-t+1
43 { k++;
44 if(k==elem_cautat1){sol1i=i;sol1j=n-t+1;}
45 if(k==elem_cautat2){sol2i=i;sol2j=n-t+1;gasit=true;}
46 }
CAPITOLUL 1. OJI 2020 28

47
48 for(j=n-t;j>=t and gasit==false;j--) //linia n-t+1
49 { k++;
50 if(k==elem_cautat1){sol1i=n-t+1;sol1j=j;}
51 if(k==elem_cautat2){sol2i=n-t+1;sol2j=j;gasit=true;}
52 }
53
54 for(i=n-t;i>t and gasit==false;i--) //col n-t+1
55 { k++;
56 if(k==elem_cautat1){sol1i=i;sol1j=t;}
57 if(k==elem_cautat2){sol2i=i;sol2j=t;gasit=true;}
58 }
59 }
60
61 fo<<sol1i<<" "<<sol1j<<’\n’;
62 fo<<sol2i<<" "<<sol2j<<’\n’;
63
64 return 0;
65 }

Listing 1.3.2: spiral_matrix75pBD.cpp


1 //Bodnariuc Danut
2 // O(n/2) -85p
3 //idee: se parcurge matricea sarind spira cu spira
4 //si cand ajung la spira cautata o parcurg elem. cu elem.
5
6 #include <fstream>
7
8 #define MAX 100003
9
10 using namespace std;
11
12 ifstream fi("spiralmatrix.in");
13 ofstream fo("spiralmatrix.out");
14
15 long long elem_cautat1,elem_cautat2,i,k,n,sp,t,j,
16 sol1i,sol1j,sol2i,sol2j,numar_el_spira;
17 bool gasit;
18
19 int main()
20 {
21 fi>>n;
22 if(n%2==0) {elem_cautat1=n*n/2;elem_cautat2=n*n/2+1;}
23 else {elem_cautat1=n*n/2;elem_cautat2=n*n/2+2;}
24
25 k=0;
26 sp=n/2; //sp- numar spire
27 gasit=false;
28
29 for(t=1;t<=sp and gasit==false;t++)
30 {
31 numar_el_spira=4*(n-t+1-t+1)-4;
32 if(k+numar_el_spira<elem_cautat1)k+=numar_el_spira;
33 else
34 {
35 for(j=t;j<=n-t+1 and gasit==false;j++) //linia t
36 { k++;
37 if(k==elem_cautat1){sol1i=t;sol1j=j;}
38 if(k==elem_cautat2){sol2i=t;sol2j=j;gasit=true;}
39 }
40
41 for(i=t+1;i<=n-t+1 and gasit==false;i++) //col n-t+1
42 { k++;
43 if(k==elem_cautat1){sol1i=i;sol1j=n-t+1;}
44 if(k==elem_cautat2){sol2i=i;sol2j=n-t+1;gasit=true;}
45 }
46
47 for(j=n-t;j>=t and gasit==false;j--) //linia n-t+1
48 { k++;
49 if(k==elem_cautat1){sol1i=n-t+1;sol1j=j;}
50 if(k==elem_cautat2){sol2i=n-t+1;sol2j=j;gasit=true;}
51 }
52
53 for(i=n-t;i>t and gasit==false;i--) //col n-t+1
CAPITOLUL 1. OJI 2020 29

54 { k++;
55 if(k==elem_cautat1){sol1i=i;sol1j=t;}
56 if(k==elem_cautat2){sol2i=i;sol2j=t;gasit=true;}
57 }
58 }
59 }
60 fo<<sol1i<<" "<<sol1j<<’\n’;
61 fo<<sol2i<<" "<<sol2j<<’\n’;
62 return 0;
63 }

Listing 1.3.3: spiralmatrix45p.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 ifstream in("spiralmatrix.in");
6 ofstream out("spiralmatrix.out");
7
8 int a[1001][1001],l0=1,l1=1,l2=1,c0=1,c1=1,c2=1;
9
10 int main()
11 {
12 int n,nc=0,l=1,c=0,dl=0,dc=1;
13 in>>n;
14 int nf=n*n/2;
15 for(int i=0;i<=n+1;i++){a[0][i]=1;a[n+1][i]=1;}
16 for(int i=0;i<=n+1;i++){a[i][0]=1;a[i][n+1]=1;}
17 while(l2==1 && c2==1)
18 {
19 if(a[l+dl][c+dc]!=0)
20 {
21 if(a[l][c+1]==0){dl=0;dc=1;}
22 else
23 {if(a[l+1][c]==0){dl=1;dc=0;}
24 else{if(a[l][c-1]==0){dl=0;dc=-1;}
25 else{if(a[l-1][c]==0){dl=-1;dc=0;}
26 else break;}}
27 }
28 }
29
30 l=l+dl;
31 c=c+dc;
32 nc++;
33 a[l][c]=nc;
34 if(nc<=nf){l0=l;c0=c;}
35 else{if(l1==1 && c1==1){l1=l;c1=c;}
36 else{l2=l;c2=c;}}
37 }
38
39 out << l0 << ’ ’ << c0 << endl;
40 if(n%2==0)out << l1 << ’ ’ << c1 << endl;
41 else out << l2 << ’ ’ << c2 << endl;
42 return 0;
43 }

Listing 1.3.4: spiralmatrix75p.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 ifstream in("spiralmatrix.in");
6 ofstream out("spiralmatrix.out");
7
8 int l0=1,l1=1,l2=1,c0=1,c1=1,c2=1;
9
10 int main()
11 {
12 int l=1,c=0,dl=0,dc=1;
13 long long n,nc=0;
14 in>>n;
15 long long nf=n*n/2,p2=4*(n-1);
CAPITOLUL 1. OJI 2020 30

16
17 int k=1;
18 while(p2<nf)
19 {
20 k++;
21 p2=p2+4*(n-2*k+1);
22 }
23
24 l=k,c=k-1;
25 nc=p2-4*(n-2*k+1);
26 int ncc=0;
27 while(nc<nf)
28 {
29 l=l+dl;c=c+dc;nc++;ncc++;
30 if(l+dl<k || n-k+1<l+dl || c+dc<k || n-k+1<c+dc)
31 {
32 if(dl==0 && dc==1){dl=1;dc=0;}
33 else{if(dl==1 && dc==0){dl=0;dc=-1;}
34 else{if(dl==0 && dc==-1){dl=-1;dc=0;}
35 else{dl=0;dc=1;}}}
36 }
37 }
38
39 l0=l;c0=c;
40 if(ncc==4*(n-2*k+1))k++;
41 if(l+dl<k || n-k+1<l+dl || c+dc<k || n-k+1<c+dc)
42 {
43 if(dl==0 && dc==1){dl=1;dc=0;}
44 else{if(dl==1 && dc==0){dl=0;dc=-1;}
45 else{if(dl==0 && dc==-1){dl=-1;dc=0;}
46 else{dl=0;dc=1;}}}
47 }
48
49 l=l+dl;c=c+dc;
50 l1=l;c1=c;
51 if(l+dl<k || n-k+1<l+dl || c+dc<k || n-k+1<c+dc)
52 {
53 if(dl==0 && dc==1){dl=1;dc=0;}
54 else{if(dl==1 && dc==0){dl=0;dc=-1;}
55 else{if(dl==0 && dc==-1){dl=-1;dc=0;}
56 else{dl=0;dc=1;}}}
57 }
58
59 l=l+dl;c=c+dc;
60 l2=l;c2=c;
61 out << l0 << ’ ’ << c0 << endl;
62 if(n%2==0)out << l1 << ’ ’ << c1 << endl;
63 else out << l2 << ’ ’ << c2 << endl;
64
65 return 0;
66 }

Listing 1.3.5: spiralmatrix90pV1.cpp


1 #include <fstream>
2 #include <cmath>
3
4 using namespace std;
5
6 ifstream in("spiralmatrix.in");
7 ofstream out("spiralmatrix.out");
8
9 int l0=1,l1=1,l2=1,c0=1,c1=1,c2=1;
10
11 int main()
12 {
13 long long n,nc=0;
14 int l=1,c=0,dl=0,dc=1;
15 in>>n;
16 long long p2=n/sqrt(2);
17 if(p2*p2==n*n/2)p2--;
18 nc=n*n/2-p2*p2;
19 if(n%2==0)
20 {
21 if(p2%2==0)
CAPITOLUL 1. OJI 2020 31

22 {
23 dl=1;dc=0;
24 l=c=n/2-p2/2+1;c--;
25 if(nc<=p2+1){l+=nc-1;}
26 else{l+=p2;c+=nc-p2-1;}
27 }
28 else
29 {
30 l=c=n/2+1+p2/2;
31 dl=-1;dc=0;
32 if(nc<=p2+1){l-=nc-1;}
33 else
34 { l=n-l+1;
35 c-=nc-p2-1;
36 dl=0;dc=-1;
37 }
38 }
39 }
40 else
41 {
42 if(p2%2==0)
43 {
44 dl=-1;dc=0;
45 l=c=n/2+1+p2/2;
46 if(nc<=p2+1){l+=dl*(nc-1);}
47 else{ dl=0;dc=-1;
48 l-=p2;c-=nc-p2-1;}
49 }
50 else
51 {l=c=n/2-p2/2+1;c--;
52 dl=1;dc=0;
53 if(nc<=p2+1){l+=nc-1;}
54 else{ l+=p2;
55 c+=nc-p2-1;
56 dl=0;dc=1;}
57 }
58 }
59
60 if(n%2==0){l1=l;c1=c;}
61 else
62 {
63 l2=l;c2=c;
64 if(l2+dl<=(n-p2)/2 || n-(n-p2)/2+1<l2+dl ||
65 c2+dc<(n-p2)/2 || n-(n-p2)/2+1<c2+dc)
66 {
67 if(dl==-1 && dc==0){dl=0;dc=-1;}
68 else
69 {
70 if(dl==0 && dc==-1){dl=1;dc=0;}
71 else{ if(dl==1 && dc==0){dl=0;dc=1;}
72 else{if(dl==0 && dc==1){dl=-1;dc=0;}}}
73 }
74 }
75 l1=l2+dl;c1=c2+dc;
76 }
77
78 if(l1+dl<=(n-p2)/2 || n-(n-p2)/2+1<l1+dl ||
79 c1+dc<(n-p2)/2 || n-(n-p2)/2+1<c1+dc)
80 {
81 if(dl==-1 && dc==0){dl=0;dc=-1;}
82 else
83 {
84 if(dl==0 && dc==-1){dl=1;dc=0;}
85 else{ if(dl==1 && dc==0){dl=0;dc=1;}
86 else{if(dl==0 && dc==1){dl=-1;dc=0;}}}
87 }
88 }
89
90 l0=l1+dl;c0=c1+dc;
91 out << l0 << ’ ’ << c0 << endl;
92
93 if(n%2==0)out << l1 << ’ ’ << c1 << endl;
94 else out << l2 << ’ ’ << c2 << endl;
95
96 return 0;
97 }
CAPITOLUL 1. OJI 2020 32

Listing 1.3.6: spiralmatrix90pV2.cpp


1 #include <fstream>
2 #include <cmath>
3
4 using namespace std;
5
6 ifstream in("spiralmatrix.in");
7 ofstream out("spiralmatrix.out");
8
9 int l0=1,l1=1,l2=1,c0=1,c1=1,c2=1;
10
11 int main()
12 {
13 long long n,nc=0;
14 int l=1,c=0,dl=0,dc=1;
15 in>>n;
16
17 //cautare binara p2
18 long long st=1,dr=n,p2=(st+dr)/2;
19 while(st<dr)
20 {
21 p2=(st+dr)/2;
22 if(p2*p2<n*n/2)st=p2+1;
23 else if(p2*p2>n*n/2)dr=p2-1;
24 else dr=st=p2;
25 }
26
27 p2=min(st,dr);
28 if(p2*p2>=n*n/2)p2--;
29 nc=n*n/2-p2*p2;
30 if(n%2==0)
31 {
32 if(p2%2==0)
33 {
34 dl=1;dc=0;
35 l=c=n/2-p2/2+1;c--;
36 if(nc<=p2+1){l+=nc-1;}
37 else{l+=p2;c+=nc-p2-1;}
38 }
39 else
40 {
41 l=c=n/2+1+p2/2;
42 dl=-1;dc=0;
43 if(nc<=p2+1){l-=nc-1;}
44 else{ l=n-l+1;
45 c-=nc-p2-1;
46 dl=0;dc=-1;}
47 }
48 }
49 else
50 {
51 if(p2%2==0)
52 {
53 dl=-1;dc=0;
54 l=c=n/2+1+p2/2;
55 if(nc<=p2+1){l+=dl*(nc-1);}
56 else{ dl=0;dc=-1;
57 l-=p2;c-=nc-p2-1;}
58 }
59 else
60 {
61 l=c=n/2-p2/2+1;c--;
62 dl=1;dc=0;
63 if(nc<=p2+1){l+=nc-1;}
64 else{ l+=p2;
65 c+=nc-p2-1;
66 dl=0;dc=1;}
67 }
68 }
69
70 if(n%2==0){l1=l;c1=c;}
71 else
72 {
73 l2=l;c2=c;
74 if(l2+dl<=(n-p2)/2 || n-(n-p2)/2+1<l2+dl ||
CAPITOLUL 1. OJI 2020 33

75 c2+dc<(n-p2)/2 || n-(n-p2)/2+1<c2+dc)
76 {
77 if(dl==-1 && dc==0){dl=0;dc=-1;}
78 else{if(dl==0 && dc==-1){dl=1;dc=0;}
79 else{ if(dl==1 && dc==0){dl=0;dc=1;}
80 else{if(dl==0 && dc==1){dl=-1;dc=0;}}}}
81 }
82 l1=l2+dl;c1=c2+dc;
83 }
84
85 if(l1+dl<=(n-p2)/2 || n-(n-p2)/2+1<l1+dl ||
86 c1+dc<(n-p2)/2 || n-(n-p2)/2+1<c1+dc)
87 {
88 if(dl==-1 && dc==0){dl=0;dc=-1;}
89 else{if(dl==0 && dc==-1){dl=1;dc=0;}
90 else{ if(dl==1 && dc==0){dl=0;dc=1;}
91 else{if(dl==0 && dc==1){dl=-1;dc=0;}}}}
92 }
93
94 l0=l1+dl;c0=c1+dc;
95 out << l0 << ’ ’ << c0 << endl;
96
97 if(n%2==0)out << l1 << ’ ’ << c1 << endl;
98 else out << l2 << ’ ’ << c2 << endl;
99
100 return 0;}

Listing 1.3.7: spiralmatrix90pV3.cpp


1 #include <fstream>
2 #include <cmath>
3
4 using namespace std;
5
6 ifstream in("spiralmatrix.in");
7 ofstream out("spiralmatrix.out");
8
9 int l0=1,l1=1,l2=1,c0=1,c1=1,c2=1;
10
11 int main()
12 {
13 long long n,nc=0;
14 int l=1,c=0,dl=0,dc=1;
15 in>>n;
16
17 //cautare binara p2
18 long long st=1,dr=n,p2=(st+dr)/2;
19 while(st<dr)
20 {
21 p2=(st+dr)/2;
22 if(1+4*p2*n-4*p2*p2<n*n/2)st=p2+1;
23 else if(1+4*p2*n-4*p2*p2>n*n/2)dr=p2-1;
24 else dr=st=p2;
25 }
26
27 p2=min(st,dr)+1;
28 if(1+4*(p2-1)*n-4*(p2-1)*(p2-1)>=n*n/2)p2--;
29 nc=n*n/2-(1+4*(p2-1)*n-4*(p2-1)*(p2-1));
30
31 if(nc<n-(2*p2-1))
32 {
33 dl=0;dc=1;
34 l=p2;
35 c=nc+p2;
36 }
37 else
38 {
39 if(nc<2*(n-(2*p2-1)))
40 {
41 dl=1;dc=0;
42 l=p2+nc-(n-(2*p2-1));
43 c=p2+n-(2*p2-1);
44 }
45 else
46 {
CAPITOLUL 1. OJI 2020 34

47 if(nc<3*(n-(2*p2-1)))
48 {
49 dl=0;dc=-1;
50 l=n-p2+1;
51 c=n-p2+1-(nc-2*(n-(2*p2-1)));
52 }
53 else
54 {
55 dl=-1;dc=0;
56 c=p2;
57 l=p2+n-(2*p2-1)-(nc-3*(n-(2*p2-1)));
58 }
59 }
60 }
61
62 l0=l;c0=c;
63 if(nc==4*(n-(2*p2-1))-1){dl=0;dc=1;}
64 else
65 {if(l+dl<p2 || n-p2+1<l+dl || c+dc<p2 || n-p2+1<c+dc)
66 {
67 if(dl==0 && dc==1){dl=1;dc=0;}
68 else{if(dl==1 && dc==0){dl=0;dc=-1;}
69 else{if(dl==0 && dc==-1){dl=-1;dc=0;}
70 else{dl=0;dc=1;}}}
71 }
72 }
73
74 l=l+dl;c=c+dc;
75 l1=l;c1=c;
76 if(l+dl<p2 || n-p2+1<l+dl || c+dc<p2 || n-p2+1<c+dc)
77 {
78 if(dl==0 && dc==1){dl=1;dc=0;}
79 else{if(dl==1 && dc==0){dl=0;dc=-1;}
80 else{if(dl==0 && dc==-1){dl=-1;dc=0;}
81 else{dl=0;dc=1;}}}
82 }
83
84 l=l+dl;c=c+dc;
85 l2=l;c2=c;
86 out << l0 << ’ ’ << c0 << endl;
87
88 if(n%2==0)out << l1 << ’ ’ << c1 << endl;
89 else out << l2 << ’ ’ << c2 << endl;
90
91 return 0;}

Listing 1.3.8: spiralmatrixMNo(n).cpp


1 #include <fstream>
2
3 using namespace std;
4
5 long long n, i, j;
6
7 void calcul(long long poz, long long &i, long long &j)
8 {
9 long long k = n;
10 long long d = 1;
11 while (poz > 4*k-4)
12 {
13 poz -= (4*k-4);
14 k -= 2;
15 d++;
16 }
17
18 if (poz <= k)
19 {
20 i = d;
21 j = d+poz-1;
22 }
23 else
24 if (poz <= 2*k-1)
25 {
26 poz -= (k-1);
27 i=d+poz-1;
CAPITOLUL 1. OJI 2020 35

28 j=n-d+1;
29 }
30 else
31 if (poz <= k+k-1+k-1)
32 {
33 poz -= (2*k-2);
34 i = n-d+1;
35 j = n-d+1-poz+1;
36 }
37 else
38 {
39 i = d+(4*k-4-poz+1);
40 j = d;
41 }
42
43 }
44
45 int main ()
46 {
47 ifstream fin ("spiralmatrix.in");
48 ofstream fout("spiralmatrix.out");
49
50 fin>>n;
51
52 calcul(n*n/2, i, j);
53 fout<<i<<" "<<j<<"\n";
54 if (n%2 == 0)
55 calcul(n*n/2+1, i, j);
56 else
57 calcul(n*n/2+2, i, j);
58 fout<<i<<" "<<j<<"\n";
59
60 return 0;
61 }

1.3.3 *Rezolvare detaliat 


Capitolul 2

OJI 2019

2.1 abx
Problema 1 - abx 90 de puncte
Un num r natural n se nume³te putere dac  exist  dou  numere naturale a, b, a ' 1, b ' 2
b 5 2 2
astfel încât n a . De exemplu, numerele 32, 169, 1 sunt puteri (32 2 , 169 13 , 1 1 ), iar
72, 2000 ³i 31 nu sunt puteri. Se citesc numerele naturale N , M ³i un ³ir de N numere naturale
x1 , x2 , ..., xN din intervalul 1, M .
Cerinµe
Pentru ecare din cele N numere xi determinaµi câte un num r natural ri din intervalul 1, M ,
cu proprietatea c  ri este o putere ³i pentru orice alt  putere p din intervalul 1, M  este îndeplinit 
condiµia ¶xi  ri ¶ & ¶xi  p¶, unde ¶x¶ reprezint  valoarea absolut  a lui x (modulul).
Dac  exist  dou  puteri egal dep rtate de xi se va alege puterea cea mai mic . De exemplu
pentru num rul 26, dintre puterile 25 ³i 27 va  ales num rul 25.
Date de intrare
Fi³ierul de intrare abx.in conµine pe prima linie dou  numere N ³i M , iar pe ecare dintre
urm toarele N linii se g se³te câte un num r natural xi (1 & i & N ), cu semnicaµia de mai sus.
Numerele aate pe aceea³i linie a ³ierului sunt separate prin câte un spaµiu.
Date de ie³ire
Fi³ierul de ie³ire abx.out va conµine N linii, pe ecare linie i (1 & i & N ) aându-se num rul
natural ri cu semnicaµia din enunµ.
Restricµii ³i preciz ri
a 1 & N & 5000
10 & M & 10
18
a
a Pentru teste valorând 40 de puncte M & 5000
a Pentru teste valorând 70 de puncte M & 109
Exemple
abx.in abx.out Explicaµii
3
8 1000 343 343 7
2
345 100 100 10
3
99 1000 1000 10
9
999 512 512 2
2
500 121 121 11
3
123 125 125 5
2
124 100 100 10
8
99 256 256 2
256
Timp maxim de executare/test: 1.0 secunde
Memorie: total 32 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: abx.cpp, abx.c sau abx.pas va  salvat  în folderul care are drept nume ID-ul t u.

36
CAPITOLUL 2. OJI 2019 37

2.1.1 Indicaµii de rezolvare


prof. Nistor Moµ - Colegiul Naµional N. B lcescu, Br ila

Soluµie 40p:
Pentru ecare num r x din ³irul dat trebuie s  c ut m cea mai apropiat  putere, deci vom
avea de efectuat N c ut ri. Pentru a c uta r spunsul pentru un num r dat x, vom avansa în cele
dou  direcµii (dreapta, stânga) pân  num rul de la pasul curent este putere. Mai apoi, îl vom
a³a pe cel mai apropiat dintre cele dou .
Pentru a verica dac  un num r y este putere , o variant  este descompunerea lui y în factori
primi. Dac  cel mai mare divizor comun al exponenµilor este mai mare decât 1, y este o putere.
Este esenµial  observaµia c  nu are rost s  veric m de mai multe ori dac  un num r este
putere sau nu. În acest context, putem precalcula pentru ecare num r din intervalul 1, M  dac 
acesta este putere ³i stoca acest lucru într-un vector caracteristic.
O soluµie care rezolv  problema în complexitate O M ˜ N  M  ar trebui s  obµin  cel puµin
40 de puncte.
Soluµie 70p:
Vom considera o variant  diferit  de a genera toate puterile din intervalul 1, M   consider m
b b
ecare baz  posibil  a ³i ad ug m a pornind cu b de la valoarea 2 ³i incrementându-l cât timp a
nu dep ³e³te M . Se observ  c  ³irul rezultat va avea mai puµin de 50.000 de puteri (eliminând du-
plicatele). Pentru a g si cele dou  puteri apropiate de x, vom sorta vectorul, eliminând duplicatele
³i vom efectua o c utare binar  pe acest vector sortat.
Se poate demonstra c  complexitatea acestei soluµii este O sqrt M   N ˜ log M . O astfel
de soluµie ar trebui s  obµin  cel puµin 70 de puncte.
Soluµie 90p:
Pentru subtask-ul M & 10 se poate observa c  soluµia de 70 de puncte nu se mai încadreaz  în
18

limitele de timp ³i de memorie. Pentru a rezolva problema în acest caz, exist  mai multe abord ri;
toate aceste abord ri ar trebui s  obµin  punctajul maxim, dac  sunt implementate cu atenµie:
C utare binar : Pentru un exponent b xat, se poate aa baza maxim  a pentru care ab & x.
b b
Mai apoi, singurii candidaµi pentru r spuns sunt a sau a  1 . Complexitatea algoritmului de
c utare binar  este O b ˜ log M  (exponenµiere iterativ ) sau O log b ˜ log M  (exponenµiere
logaritmic ). O soluµie care caut  binar cele dou  puteri pentru ecare exponent posibil (2 & b &
60) folosind exponenµierea iterativ  are complexitate O N log2 M  ³i ar trebui s  obµin  90 de
puncte. În continuare vom prezenta optimiz ri extra, care îmbun t µesc ³i mai mult timpul de
execuµie.
Considerarea doar a exponenµilor primi: O optimizare substanµial  a soluµiei anterioare
o constituie observaµia c , pentru a considera toate puterile posibile, este sucient s  consider m
b b b d
doar exponenµii care sunt primi, deoarece dac  n a , cu b d , atunci n a  , deci orice
putere de exponent b va  considerat  în cadrul exponentului d. În intervalul 2, 60 sunt doar 17
numere prime.
Abordare hibrid : O alt  soluµie o reprezint  precalcularea puterilor pentru b ' 4 într-o
manier  similar  soluµiei de 70 de puncte ³i folosirea c ut rii binare pentru cazul b 2 sau b 3.
Aceast  soluµie are complexitate O sqrt sqrt M   N ˜ log M . Menµion m c  o astfel de
soluµie va obµine un timp de execuµie foarte bun, nedep ³ind 0.1 secunde pe un test maximal.
Posibile gre³eli:
Erori de precizie: Menµion m c  folosirea unei abord ri oating-point (folosirea funcµiei
pow) este înceat  ³i poate conduce la erori substanµiale de precizie; astfel, nu este recomandat 
s  e folosit  în cadrul soluµiei, dar cu puµin  atenµie poate  folosit  cu succes, mai ales pentru
valori mici de-ale lui M .
Erori de overow: În abordarea de 90 de puncte, ne vom întâmpina de problema overow-
ului, care trebuie tratat  cu grij . O variant  simpl  de a evita astfel de erori este ca, la ecare
înmulµire a ˜ b s  veric m mai întâi dac  a ˜ b & M . Cum a ˜ b poate da rezultate eronate, vom
verica ca a & M ©b. O soluµie care nu trateaz  corect overow-ul va putea obµine între 70  90
de puncte în funcµie de alte detalii de implementare.

2.1.2 Cod surs 

Listing 2.1.1: abx_EN_90.cpp


1 #include <bits/stdc++.h>
CAPITOLUL 2. OJI 2019 38

2
3 using namespace std;
4
5 int n;
6 long long m, a;
7
8 inline long long lgput(int x, int y)
9 {
10 long long ans = 1, aux = x;
11 for(int p = 1; p <= y ; p = p << 1)
12 {
13 if(y & p) ans = ans * aux;
14 aux = aux * aux;
15 }
16 return ans;
17 }
18
19 inline int cautb(long long x, int put)
20 {
21 long long st = 1, dr = pow(x, 1.0 / put);
22 while(st <= dr)
23 {
24 int mij = (1LL * st + dr) / 2;
25 if(lgput(mij, put) > x) dr = mij - 1;
26 else st = mij + 1;
27 }
28 return dr;
29 }
30
31 int main()
32 {
33 ifstream f("17-abx.in");
34 ofstream g("abx.out");
35
36 f >> n >> m;
37 for(int i=1; i <= n; ++i)
38 {
39 f >> a;
40 long long sol = 0, dif = m + 100;
41 for(int j=2; j <= 60 ; ++j)
42 {
43 long long y = cautb(a, j);
44 long long z = lgput(y, j);
45
46 if(abs(a - z) <= dif && z <= m)
47 sol = z, dif = abs(a - z);
48
49 z = lgput(y + 1, j);
50
51 if(abs(a - z) < dif && z <= m)
52 sol = z, dif = abs(a - z);
53 }
54
55 g << sol << "\n";
56 }
57
58 return 0;
59 }

Listing 2.1.2: abx_LB_90.cpp


1 /*
2 * Problema abx (OJI 2019)
3 * Complexitate: O(N*log^2(M))
4 * Autor: Lucian Bicsi
5 */
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 const long long kInf = 2e18 + 10;
12 long long max_base[80];
13
14 long long Power(long long b, int e)
CAPITOLUL 2. OJI 2019 39

15 {
16 long long r = 1;
17 for (int i = 0; i < e; ++i)
18 {
19 if (1.0L * r * b > 6e18)
20 return kInf;
21 r *= b;
22 }
23 return r;
24 }
25
26 long long Root(long long x, int ex)
27 {
28 long long b = 1, e = max_base[ex], ans = 1;
29
30 while (b <= e)
31 {
32 long long m = (b + e) / 2;
33 if (Power(m, ex) <= x)
34 {
35 ans = m;
36 b = m + 1;
37 } else e = m - 1;
38 }
39
40 return ans;
41 }
42
43 int main()
44 {
45 ifstream cin("abx.in");
46 ofstream cout("abx.out");
47
48 int n; long long m; cin >> n >> m;
49 assert(1 <= n && n <= 1e5);
50 assert(10 <= m && m <= 1e18);
51
52 for (int ex = 2; ex <= 64; ++ex)
53 {
54 max_base[ex] = kInf;
55 max_base[ex] = Root(m, ex);
56 }
57
58
59 long long max_diff = 0;
60 for (int i = 0; i < n; ++i)
61 {
62 long long x; cin >> x;
63 long long ans = 1;
64 assert(1 <= x && x <= m);
65
66 for (int ex = 2; ex <= 64; ++ex)
67 {
68 long long root = Root(x, ex);
69 long long c1 = Power(root, ex), c2 = Power(root + 1, ex);
70
71 if (c1 <= m && make_pair(abs(c1 - x), c1) <
72 make_pair(abs(ans - x), ans))
73 ans = c1;
74
75 if (c2 <= m && make_pair(abs(c2 - x), c2) <
76 make_pair(abs(ans - x), ans))
77 ans = c2;
78 }
79
80 cout << ans << ’\n’;
81 max_diff = max(max_diff, abs(ans - x));
82 }
83
84 cerr << "MAXIMUM DIFFERENCE: " << max_diff << endl;
85 }

Listing 2.1.3: abx_LB_hybrid.cpp


1 /*
CAPITOLUL 2. OJI 2019 40

2 * Problema abx (OJI 2019)


3 * Complexitate: O(N*log(M)+cbrt(M))
4 * Autor: Lucian Bicsi
5 */
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 int Sqrt(long long x)
11 {
12 int b = 1, e = 1e9, ans = 1;
13 while (b <= e)
14 {
15 int m = (b + e) / 2;
16 if (1LL * m * m <= x)
17 {
18 b = m + 1;
19 ans = m;
20 }
21 else
22 e = m - 1;
23 }
24 return ans;
25 }
26
27 int main()
28 {
29 ifstream cin("abx.in");
30 ofstream cout("abx.out");
31
32 int n; long long m;
33 cin >> n >> m;
34
35 vector<long long> cands = {1};
36
37 for (int i = 2; 1LL * i * i * i <= m; ++i)
38 {
39 long long aux = m / i;
40 long long cand = i;
41 while (true)
42 {
43 aux /= i;
44 if (aux)
45 {
46 cand *= i;
47 cands.push_back(cand);
48 }
49 else
50 break;
51 }
52 }
53
54 sort(cands.begin(), cands.end());
55
56 cands.erase(unique(cands.begin(), cands.end()), cands.end());
57
58
59 for (int i = 0; i < n; ++i)
60 {
61 long long x; cin >> x;
62 long long ans = 1;
63
64 auto upd = [&](long long now)
65 {
66 if (now > m) return;
67 if (abs(now - x) < abs(ans - x) ||
68 (abs(now - x) == abs(ans - x) && now < ans))
69 ans = now;
70 };
71
72 auto it = lower_bound(cands.begin(), cands.end(), x);
73 upd( *it);
74
75
76 if (it != cands.begin())
77 upd( * prev(it));
CAPITOLUL 2. OJI 2019 41

78
79 long long sqrt = Sqrt(x);
80 upd(sqrt * sqrt);
81 upd((sqrt + 1) * (sqrt + 1));
82
83 cout << ans << ’\n’;
84 }
85
86
87 return 0;
88 }

Listing 2.1.4: abx_LB_hybrid_fast.cpp


1 /*
2 * Problema abx (OJI 2019)
3 * Complexitate: O(N*log(M)+sqrt(sqrt(M)))
4 * Autor: Lucian Bicsi
5 */
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 int Solve2(long long x)
11 {
12 int b = 1, e = 1e9, ans = 1;
13 while (b <= e)
14 {
15 int m = (b + e) / 2;
16 if (1LL * m * m <= x)
17 {
18 b = m + 1;
19 ans = m;
20 } else e = m - 1;
21 }
22 return ans;
23 }
24
25 int Solve3(long long x)
26 {
27 int b = 1, e = 1e6, ans = 1;
28 while (b <= e)
29 {
30 int m = (b + e) / 2;
31 if (1LL * m * m * m <= x)
32 {
33 b = m + 1;
34 ans = m;
35 } else e = m - 1;
36 }
37 return ans;
38 }
39
40 int main()
41 {
42 ifstream cin("abx.in");
43 ofstream cout("abx.out");
44
45 int n; long long m;
46 cin >> n >> m;
47
48 vector<long long> cands = {1};
49
50 for (int i = 2; m / i / i / i / i > 0; ++i)
51 {
52 long long aux = m / i;
53 long long cand = i;
54 while (true)
55 {
56 aux /= i;
57 if (aux)
58 {
59 cand *= i;
60 cands.push_back(cand);
61 }
CAPITOLUL 2. OJI 2019 42

62 else
63 break;
64 }
65 }
66
67 sort(cands.begin(), cands.end());
68
69 cands.erase(unique(cands.begin(), cands.end()), cands.end());
70
71 for (int i = 0; i < n; ++i)
72 {
73 long long x; cin >> x;
74 long long ans = 1;
75
76 auto upd = [&](long long now)
77 {
78 if (now > m) return;
79 if (abs(now - x) < abs(ans - x) ||
80 (abs(now - x) == abs(ans - x) && now < ans))
81 ans = now;
82 };
83
84 auto it = lower_bound(cands.begin(), cands.end(), x);
85
86 upd( *it);
87 if (it != cands.begin())
88 upd( *prev(it));
89
90 long long a2 = Solve2(x);
91
92 upd(a2 * a2);
93 upd((a2 + 1) * (a2 + 1));
94
95 long long a3 = Solve3(x);
96
97 upd(a3 * a3 * a3);
98 upd((a3 + 1) * (a3 + 1) * (a3 + 1));
99
100 cout << ans << ’\n’;
101 }
102
103 return 0;
104 }

Listing 2.1.5: abx_LB_stable.cpp


1 /**
2 * Problema abx (OJI 2019)
3 * Solutie numeric stabila
4 * Autor: Lucian Bicsi
5 */
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 int main()
12 {
13 ifstream cin("abx.in");
14 ofstream cout("abx.out");
15
16 int n; long long m; cin >> n >> m;
17
18 for (int i = 0; i < n; ++i)
19 {
20 long long x; cin >> x;
21 long long ans = 1;
22
23 for (int ex = 2; ex <= 62; ++ex)
24 {
25 long long base = pow(x, 1.0 / ex);
26 for (int it = -1; it < 3; ++it)
27 {
28 long long now = 1;
29 for (int i = 0; i < ex; ++i)
CAPITOLUL 2. OJI 2019 43

30 {
31 long long nxt = now * (base + it);
32 if ((base + it) && nxt / (base + it) != now)
33 nxt = 2e18;
34 now = nxt;
35 }
36
37 if (now > m) continue;
38
39 if (make_pair(abs(x - ans), ans) >
40 make_pair(abs(x - now), now))
41 ans = now;
42 }
43 }
44
45 cout << ans << ’\n’;
46 }
47 return 0;
48 }

Listing 2.1.6: abx_NM.cpp


1 #include <fstream>
2 #include <cmath>
3
4 using namespace std;
5
6 long long v[8192],m;
7 const int npr=18;
8 int pr[npr]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61};
9
10 long long putere(long long x, int n)
11 { long long p=1;
12 for (int i=0;i<n;i++)
13 p*=x;
14 if (p<0)
15 return m+1;
16 return p;
17 }
18
19 int main()
20 {
21 int i,j,n,n1;
22 long long k,p,q,q1,dif;
23
24 ifstream fi("abx.in");
25 ofstream fo("abx.out");
26
27 fi>>n>>m;
28 for (i=1;i<=n;i++)
29 fi>>v[i];
30
31 for (i=1;i<=n;i++)
32 {
33 if (v[i]<3)
34 {
35 fo<<1<<"\n";
36 continue;
37 }
38
39 dif=5000000000; n1=0;
40 for (j=0;j<npr;j++)
41 {
42 k=(long long)pow((long double)v[i],1.0/pr[j]);
43 if (k<2)
44 {
45 n1++;
46 if(n1>1)
47 break;
48 }
49
50 q=putere(k,pr[j]);
51 if (q>v[i])
52 {
53 q1=q;
CAPITOLUL 2. OJI 2019 44

54 q=putere(k-1,pr[j]);
55 }
56 else
57 {
58 q1=putere(k+1,pr[j]);
59 if (q1<=v[i])
60 {
61 q=q1;
62 q1=putere(k+2,pr[j]);
63 }
64 }
65
66 if (v[i]-q<=dif)
67 {
68 p=q;
69 dif=v[i]-q;
70 }
71
72 if (q1<=m && q1-v[i]<dif)
73 {
74 p=q1;
75 dif=q1-v[i];
76 }
77 }
78
79 fo<<p<<"\n";
80 }
81
82 return 0;
83 }

2.1.3 *Rezolvare detaliat 

2.2 deminare
Problema 2 - deminare 90 de puncte
Pe un teren de form  dreptunghiular  format din L linii ³i C coloane sunt plantate M mine.
Liniile sunt numerotate de sus în jos cu valori de la 1 la L iar coloanele sunt numerotate de la
stânga la dreapta cu valori de la 1 la C .
Deoarece r zboiul s-a terminat, speciali³tii vor s  demineze terenul ³i s -l redea utiliz rii pu-
blice.
Mutarea unei mine reprezint  operaµia de transfer a unei mine de la linia x1 ³i coloana y1 la
o poziµie liber , dat  de linia x2 ³i coloana y2 , unde 1 & x1 , x2 & L ³i 1 & y1 , y2 & C .
Deoarece mutarea unei mine este periculoas , trebuie determinat num rul minim de mine care
trebuie mutate din poziµia iniµial  astfel încât toate minele de pe teren s  e a³ezate unele lâng 
altele într-o zon  compact  dreptunghiular , oriunde în cadrul terenului dat, pentru ca apoi s  e
detonate împreun .
Spre exemplu: dac  L 4, C 5, M 8 ³i minele sunt a³ezate iniµial conform gurii de mai
jos (zonele colorate cu negru arat  poziµiile minelor), pentru a se ajunge la o a³ezare a minelor
într-o zon  compact  de form  dreptunghiular  num rul minim de mine mutate este 3.

Figura 2.1: deminare


CAPITOLUL 2. OJI 2019 45

Cerinµe
Cunoscând num rul de linii L ³i de coloane C ale terenului minat, num rul de mine M , precum
³i poziµia ec rei mine, s  se scrie un program care determin :
1. linia sau liniile pe care se g sesc cele mai multe mine;
2. num rul minim de mine mutate, pentru ca toate minele de pe teren s  e a³ezate într-o
zon  compact  cu form  dreptunghiular .

Date de intrare
Fi³ierul de intrare este deminare.in ³i conµine:
- pe prima linie num rul natural V a c rui valoare poate  doar 1 sau 2;
- pe a doua linie dou  numere naturale L ³i C , cu semnicaµia din enunµ;
- pe a treia linie num rul natural M , cu semnicaµia din enunµ;
- pe ecare din urm toarele M linii, câte o pereche de valori xi ³i yi , 1 & i & M , reprezentând
linia, respectiv coloana, unde se a  o min ;
Numerele aate pe aceea³i linie a ³ierului sunt separate prin câte un spaµiu.

Date de ie³ire
Fi³ierul de ie³ire este deminare.out.
Dac  valoarea lui V este 1 atunci prima linie a ³ierului de ie³ire va conµine num rul liniei pe
care se g sesc cele mai multe mine.
Dac  exist  dou  sau mai multe astfel de linii, se vor a³a toate numerele acestora, în ordine
cresc toare, separate prin câte un spaµiu.
Dac  valoarea lui V este 2 atunci ³ierul de ie³ire va conµine pe prima linie num rul minim cerut
de mine mutate. Dac  minele nu pot  a³ezate într-o zon  compact  de form  dreptunghiular ,
în ³ierul de ie³ire se va scrie valoarea 1.

Restricµii ³i preciz ri
a 1 & L, C & 500
a 1&M &L C
a O zon  în care se a  mine a³ezate pe coloane consecutive, pe aceea³i linie sau a³ezate pe
linii consecutive, pe aceea³i coloan  se consider  c  formeaz  o zon  compact  de form  dreptun-
ghiular .
a O zon  compact  de form  dreptunghiular  poate avea num rul de linii ocupate egal cu
num rul de coloane ocupate.
a Pentru teste valorând 20 de puncte, avem V 1
a Pentru teste valorând 70 de puncte, avem V 2
a Pentru teste valorând 20 de puncte, avem V 2 ³i L C & 10000
a Pentru teste valorând 32 de puncte, avem V 2 ³i L C & 100000

Exemple
deminare.in deminare.out Explicaµii
1 4 V=1, deci se rezolv  doar cerinµa 1
4 5 L=4, C=5, M=8.
8 Minele sunt plasate la poziµiile (1,2), (1,5), (2,1), (3,2), (3,5),
1 2 (4,3), (4,4) ³i (4,5).
1 5 Pe linia 1 sunt amplasate 2 mine;
2 1 Pe linia 2 este amplasat  1 min ;
3 2 Pe linia 3 sunt amplasate 2 mine;
3 5 Pe linia 4 sunt amplasate 3 mine;
4 3 Deci, exist  o singur  linie pe care sunt amplasate un num r
4 4 maxim de mine ³i anume linia 4.
4 5
CAPITOLUL 2. OJI 2019 46

2 3 V=2, deci se rezolv  doar cerinµa 2


4 5 L=4, C=5, M=8
8 Minele sunt plasate la poziµiile (1,2), (1,5), (2,1), (3,2), (3,5),
1 2 (4,3), (4,4) ³i (4,5).
1 5 Pentru a obµine o zon  compact  de form  dreptunghiular  tre-
2 1 buie mutate minimum 3 mine. O variant  posibil  este:
3 2 Mina de la poziµia (1,2) se mut  la poziµia (3,3);
3 5 Mina de la poziµia (1,5) se mut  la poziµia (3,4);
4 3 Mina de la poziµia (2,1) se mut  la poziµia (4,2);
4 4 Se obµine o zon  compact  de form  dreptunghiular , având
4 5 colµul din stânga sus la poziµia (3,2) ³i colµul din dreapta jos la
poziµia (4,5).

Timp maxim de executare/test: 0.5 secunde


Memorie: total 32 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: deminare.cpp, deminare.c sau deminare.pas va  salvat  în folderul care are drept nume
ID-ul t u.

2.2.1 Indicaµii de rezolvare


prof. Che³c  Ciprian - Liceul Tehnologic Grigore C. Moisil Buz u

Consideraµii preliminare
Rezolvarea problemei necesit  utilizarea structurilor de date de tip tablou.
Cerinµa 1
Se utilizeaz  un tablou bidimensional cu L linii ³i C coloane pentru memorarea poziµiei minelor
din problem .
Se poate calcula num rul de mine de pe ecare linie ³i rezultatul îl putem reµine într-un tablou
unidimensional.
Se determin  apoi maximul dintre valorile obµinute anterior ³i se parcurge înc  o dat  vectorul
num rului de mine ³i dac  exist  mai multe linii ce reµin un num r egal cu valoarea maxim 
anterior determinat  se a³eaz  indicele liniei respective.
Cerinµa 2
Soluµia I (brute force)
Se descompune num rul de mine M în produs de 2 numere naturale, M a b. Pentru ecare
dintre perechile a ³i b anterior determinate cu proprietatea (1 & a & L ³i 1 & b & C ) se scaneaz 
matricea terenului cu zone dreptunghiulare de dimensiuni a  b ³i se determin  num rul maxim
de valori de 1 dintr-o astfel de zon  sau num rul minim de valori de 0.
Este mai ecient s  determin m num rul de valori de 1 pentru c  acest lucru se poate face
prin însumare, decât num rul de valori de 0, operaµie ce presupune utilizarea repetat  a unei
instrucµiuni de decizie.
Nu ne intereseaz  unde este a³ezat  aceast  zon  în interiorul terenului. Zona astfel determi-
nat  corespunde acelei zone pentru care se vor face cele mai puµine mut ri, având în vedere c 
num rul de mine este M (M a b) ³i c  aceast  zon  are cele mai puµine elemente nule.
Este posibil s  nu existe o astfel de zon , dac  descompunerea lui M nu conµine nici m car o
pereche de numere a ³i b cu 1 & a & L ³i 1 & b & C . În acest caz în ³ierul de ie³ire se a³eaz  1.
Soluµia anterior prezentat  obµine aproximativ 50 puncte.
Soluµia II (Sume parµiale pe matrice)
Reducerea ordinului de complexitate al determin rii sumei maxime al numerelor dintr-o zon 
dreptunghiular  se poate face folosind o matrice auxiliar  în care se reµin sume parµiale.
Mai precis se construie³te o matrice M AT cu acelea³i dimensiuni ca matricea din problem , în
care un element M AT ij  reµine suma tuturor elementelor matricei iniµiale din zona determinat 
de colµul din stânga sus 1, 1 ³i colµul din dreapta jos i, j .
Pe baza acestei matrice num rul de elemente de 1 dintr-o zon  dreptunghiular  oarecare se
poate calcula mai ecient (O 1) cu formula:

k mati  ls  1j  cs  1  mati  ls  1j  1  mati  1j  cs  1  mati  1j  1,

unde ls ³i cs sunt dimensiunile zonei de scanare (vezi gura de mai jos).


CAPITOLUL 2. OJI 2019 47

Pentru datele de intrare din exemplul problemei prezent m în gura de mai jos care este
matricea iniµial  ³i matricea sumelor parµiale ³i cum se calculeaz  ecient num rul de elemente de
1 dintr-o zon  dreptunghiular  oarecare.

Figura 2.2: deminareIR

Num rul de 1 din zona galben  este egal cu num rul de 1 din toate zonele colorate - num rul
de 1 din zona albastr  - num rul de 1 din zona maronie + num rul de 1 din zona colorat 
în ambele culori.
O astfel de soluµie obµine 90 puncte.
Observaµii:
a Are mare importanµ  cum se construie³te matricea sumelor parµiale. Acest subalgoritm se
2 2
poate realiza în O L C  sau O L C L  C  sau O L C . Prima varianta conduce la punctaje
mai mici chiar ³i decât cele obµinute cu brute force!
a O modalitate ecient  de construcµie a acestei matrice folose³te tot algoritmul descris ante-
rior. Fiecare valoare din poziµia i, j  se determin  pe baza valorilor din poziµiile i  1, j , i, j  1
³i i  1, j  1 cât ³i a valorii din matricea iniµial  de pe poziµia i, j .

2.2.2 Cod surs 

Listing 2.2.1: deminare_CC3.cpp


1 // prof. Chesca Ciprian - 100 p
2 // cu constructia matricei sumelor partiale O(lc) si O(sqrt(m))
3 // sursa cu assert
4
5 #include <fstream>
6 #include <cassert>
7
8 using namespace std;
9
10 int a[501][501];
11 int w[501],mat[501][501];
12
13 int main()
14 {
15 int l,c,i,j,k,m,x,y,maxl,s,v,maxm,ls,cs,ok,i1,j1;
16
17 /* l = linii
18 c = coloane
19 m = numar de mine
20 x,y = coordonate mina
21 v = varianta
22 maxm = numarul maxim de mine dintr-o zona de scanare
23 maxl = numarul maxim de mine de pe o linie
CAPITOLUL 2. OJI 2019 48

24 k = contorul pentru numararea minelor dintr-o zona de scanare


25 ok = indicator pentru a determina daca problema are solutie
26 ls, cs = numarul de linii si coloane al zonei de scanare
27 */
28 ifstream f("deminare.in");
29 ofstream g("deminare.out");
30
31 // citire date de intrare
32 f >> v;
33 assert(v>=1 && v <=2);
34
35 f >> l >> c;
36 assert(l>=1 && l<=500);
37 assert(c>=1 && c<=500);
38
39 f >> m;
40 assert(m>=1 && m<=l*c);
41
42 int perechi = 0;
43 while(f>>x>>y)
44 {
45 assert(x>=1 && x<=l);
46 assert(y>=1 && y<=c);
47 perechi++;
48 a[x][y]=1;
49 }
50
51 // prima cerinta
52 if (v == 1)
53 {
54 // cerinta = care este linia pe care se gasesc cele mai multe mine
55 // daca sunt mai multe linii se afiseaza toate in ordine crescatoare
56 // cu spatiu intre ele
57
58 // aflu numarul de mine pe linii
59 for(i=1;i<=l;i++)
60 {
61 s=0;
62 for(j=1;j<=c;j++)
63 if(a[i][j]==1) s++;
64 w[i]=s;
65 }
66
67 // aflu maximul de mine de pe o linie
68 maxl=w[1];
69 for(i=1;i<=l;i++)
70 if (w[i]>maxl) maxl=w[i];
71
72 // afisez linia(iile)
73 for(i=1;i<=l;i++)
74 if (w[i]==maxl) g << i << " ";
75 g << "\n";
76 }
77
78
79 // cerinta a doua
80 if(v == 2)
81 {
82 // cerinta = numarul minim de mutari de mine astfel incat sa se obtina
83 // o suprafata dreptunghiulara
84
85 // aflu numarul de mine in submatricile (1,1) si (i,j) cu (i,j)
86 // de la 1<=i<=l si l<=j<=c
87 // am renuntat sa calculez separat prima linie si prima coloana
88 // pentru ca oricum valorile sunt 0
89
90 for(i=1;i<=l;i++)
91 for(j=1;j<=c;j++)
92 mat[i][j]=a[i][j]+mat[i-1][j]+mat[i][j-1]-mat[i-1][j-1];
93
94 // descompun m in produs de 2 numere naturale
95
96 ok = 0; maxm = 0;
97 for(i=1;i*i<=m;i++)
98 if (m%i==0)
99 {
CAPITOLUL 2. OJI 2019 49

100 ls = i;cs = m/i;


101 // incep scanarea directa
102 if (ls <= l && cs <= c)
103 {
104 ok = 1;
105 for(i1=1;i1<=l-ls+1;i1++)
106 for(j1=1;j1<=c-cs+1;j1++)
107 {
108 k = mat[i1+ls-1][j1+cs-1] - mat[i1+ls-1][j1-1] -
109 mat[i1-1][j1+cs-1] + mat[i1-1][j1-1];
110 if (k > maxm)
111 maxm = k;
112 }
113 }
114
115 ls = m/i;
116 cs = i;
117
118 // si inversa
119 if (ls <= l && cs <= c)
120 {
121 ok = 1;
122 for(i1=1;i1<=l-ls+1;i1++)
123 for(j1=1;j1<=c-cs+1;j1++)
124 {
125 k = mat[i1+ls-1][j1+cs-1] - mat[i1+ls-1][j1-1] -
126 mat[i1-1][j1+cs-1] + mat[i1-1][j1-1];
127 if (k > maxm)
128 maxm = k;
129 }
130 }
131 }
132
133 if (!ok)
134 g << "-1\n";
135 else
136 g << m - maxm << "\n";
137
138 if (perechi!=m)
139 g << "eroare!";
140 }
141
142 return 0;
143 }

Listing 2.2.2: deminare_EN.cpp


1 // prof. Nodea Eugen - 100 puncte cu scanf si printf
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 int L, C, v, m, Max;
8 bool a[501][501];
9 int nL[501];
10 int Nr[501][501];
11
12 int main()
13 {
14 int i, j, k;
15 freopen("deminare.in", "r", stdin);
16 freopen("deminare.out","w", stdout);
17
18 scanf("%d", &v);
19 scanf("%d%d", &L, &C);
20 scanf("%d", &m);
21
22 for(k=1; k<=m; ++k)
23 {
24 scanf("%d%d", &i, &j);
25 a[i][j] = 1;
26 nL[i]++;
27 }
28
CAPITOLUL 2. OJI 2019 50

29 if (v == 1)
30 {
31 for(i=1; i<=L; ++i)
32 if (nL[i] > Max) Max = nL[i];
33 for(i=1; i<=L; ++i)
34 if (nL[i] == Max) printf("%d ", i);
35 return 0;
36 }
37 else
38 {
39 for(i=1; i<=L; ++i)
40 for(j=1; j<=C; ++j)
41 Nr[i][j] = Nr[i-1][j] + Nr[i][j-1] - Nr[i-1][j-1] + a[i][j];
42
43 for(int d=1; d*d<=m; ++d)
44 if (m % d == 0)
45 {
46 int lx = d, ly = m / d;
47
48 ///parcurgem toate submatricile
49 for(i=lx; i<=L; ++i)
50 {
51 int i1 = i - lx;
52 for (j=ly; j<=C; ++j)
53 {
54 int j1 = j - ly;
55 int w = Nr[i][j] - Nr[i][j1] - Nr[i1][j] + Nr[i1][j1];
56 if (w > Max) Max = w;
57 }
58 }
59
60 lx = m/d, ly = d;
61 for(i=lx; i<=L; ++i)
62 {
63 int i1 = i - lx;
64 for (j=ly; j<=C; ++j)
65 {
66 int j1 = j - ly;
67 int w = Nr[i][j] - Nr[i][j1] - Nr[i1][j] + Nr[i1][j1];
68 if (w > Max) Max = w;
69 }
70 }
71 }
72
73 if (Max)
74 printf("%d", m - Max);
75 else
76 printf("-1\n");
77 }
78
79 return 0;
80 }

Listing 2.2.3: deminare_GM.cpp


1 // prof. Gelu Manolache - 100 puncte
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 ifstream in("deminare.in");
8 ofstream out("deminare.out");
9
10 int ma[510][510],a[510][510],v[510],t,lin,col,dim,np,z,mx;
11
12 int main()
13 {
14 in>>t>>lin>>col>>dim;
15 int x,y;
16
17 while(in>>x>>y)
18 np++, ma[x][y]=1;
19
20 if(t==1)
CAPITOLUL 2. OJI 2019 51

21 {
22 for(int i=1;i<=lin;++i)
23 {
24 int w=0;
25 for(int k=1;k<=col;++k)
26 w+=ma[i][k];
27 v[i]=w;
28 }
29
30 mx=v[1];
31 for(int i=1;i<=lin;++i)
32 if(v[i]>mx)
33 mx=v[i];
34
35 for(int i=1;i<=lin;++i)
36 if(v[i]==mx)
37 out<<i<<’ ’;
38
39 out<<’\n’;
40 }
41 else
42 {
43 for(int i=1;i<=lin;++i)
44 for(int j=1;j<=col;++j)
45 a[i][j]=ma[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
46
47 z=0,mx=0;
48 for(int i=1;i*i<=dim;++i)
49 if(dim%i==0)
50 {
51 int l1=i,c1=dim/i;
52 if(l1<=lin && c1<=col)
53 {
54 z=1;
55 for(int ii=1;ii<=lin-l1+1;++ii)
56 for(int jj=1;jj<=col-c1+1;++jj)
57 {
58 int p=a[ii+l1-1][jj+c1-1]-a[ii+l1-1][jj-1];
59 p=p-a[ii-1][jj+c1-1]+a[ii-1][jj-1];
60 if(p>mx)
61 mx=p;
62 }
63 }
64 //====================================
65
66 l1=dim/i,c1=i;
67 if(l1<=lin && c1<=col)
68 {
69 z=1;
70 for(int ii=1;ii<=lin-l1+1;++ii)
71 for(int jj=1;jj<=col-c1+1;++jj)
72 {
73 int p=a[ii+l1-1][jj+c1-1]-a[ii+l1-1][jj-1];
74 p=p-a[ii-1][jj+c1-1]+a[ii-1][jj-1];
75 if(p>mx)
76 mx=p;
77 }
78 }
79 }
80
81 if(z==0)
82 out<<"-1\n";
83 else
84 out<<dim-mx<<"\n";
85 }
86
87 out.close();
88 return 0;
89 }

Listing 2.2.4: deminare_LB.cpp


1 /**
2 * Problema deminare (OJI 2019)
3 * Solutie: O(K * log(K) * divs(K))
CAPITOLUL 2. OJI 2019 52

4 * Autor: Lucian Bicsi


5 */
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 struct SegmTree
11 {
12 vector<int> data, lazy;
13 int n;
14
15 SegmTree(int n) : data(4 * n, 0), lazy(4 * n, 0), n(n) {}
16
17 void update(int node, int b, int e, int l, int r, int x)
18 {
19 if (l > r) return;
20 if (b == l && e == r)
21 {
22 lazy[node] += x;
23 data[node] += x;
24 return;
25 }
26
27 int m = (b + e) / 2;
28
29 update(node * 2, b, m, l, min(r, m), x);
30 update(node * 2 + 1, m + 1, e, max(m + 1, l), r, x);
31
32 data[node] = lazy[node] + max(data[node * 2], data[node * 2 + 1]);
33 }
34
35 void Update(int l, int r, int x)
36 {
37 update(1, 0, n - 1, l, r - 1, x);
38 }
39
40 int Query()
41 {
42 return data[1];
43 }
44 };
45
46 int main()
47 {
48 ifstream cin("deminare.in");
49 ofstream cout("deminare.out");
50
51 int t; cin >> t;
52 int n, m, k;
53
54 cin >> n >> m >> k;
55 if (t == 1)
56 {
57 vector<int> cnt(n, 0);
58
59 int best = 0;
60
61 for (int i = 0; i < k; ++i)
62 {
63 int x, y; cin >> x >> y;
64 cnt[x - 1] += 1;
65 best = max(best, cnt[x - 1]);
66 }
67
68 for (int i = 0; i < n; ++i)
69 if (cnt[i] == best)
70 cout << i + 1 << " ";
71 cout << endl;
72 }
73 else
74 {
75 vector<pair<int, int>> v;
76
77 for (int i = 0; i < k; ++i)
78 {
79 int x, y; cin >> x >> y;
CAPITOLUL 2. OJI 2019 53

80 v.emplace_back(x, y);
81 }
82
83 sort(v.begin(), v.end());
84
85 int best = -1;
86 for (int d = 1; d <= k; ++d)
87 {
88 if (k % d) continue;
89 if (d > m || k / d > n) continue;
90
91 SegmTree st(m + d + 1);
92
93 int j = 0;
94 for (int i = 0; i < k; ++i)
95 {
96 st.Update(v[i].second, v[i].second + d, 1);
97 while (v[j].first == v[i].first - k / d)
98 {
99 st.Update(v[j].second, v[j].second + d, -1);
100 ++j;
101 }
102
103 best = max(best, st.Query());
104 }
105 }
106
107 if (best == -1)
108 cout << -1 << endl;
109 else
110 cout << k - best << endl;
111 }
112 }

Listing 2.2.5: deminare_MP.cpp


1 // prof. Maria Pandele - 100 puncte
2
3 #include <cmath>
4 #include <fstream>
5 #include <vector>
6
7 void solve1(std::vector<std::vector<int>>& a, std::ofstream& cout)
8 {
9 int maxim = 0;
10 std::vector<int> cnt(a.size());
11
12 for (int i = 0; i < a.size(); ++i) {
13 cnt[i] = 0;
14 for (int j = 0; j < a[i].size(); ++j) {
15 cnt[i] += a[i][j];
16 }
17 maxim = std::max(maxim, cnt[i]);
18 }
19
20 for (int i = 0; i < cnt.size(); ++i) {
21 if (cnt[i] == maxim) {
22 cout << i + 1 << " ";
23 }
24 }
25 cout << "\n";
26 }
27
28 int verif(std::vector<std::vector<int>>& sp, int& l, int &c, int&h , int& w) {
29 int ans = -1;
30
31 if (h <= l && w <= c) {
32 for (int i = 1; i <= l - h + 1; ++i) {
33 for (int j = 1; j <= c - w + 1; ++j) {
34 int x = i + h - 1;
35 int y = j + w - 1;
36 ans = std::max(ans, sp[x][y] - sp[i - 1][y] -
37 sp[x][j - 1] + sp[i - 1][j - 1]);
38 }
39 }
CAPITOLUL 2. OJI 2019 54

40 }
41 return ans;
42 }
43
44 int solve2(std::vector<std::vector<int>>& a, int& l, int& c, int& m) {
45 std::vector<std::vector<int>> sp(l + 1, std::vector<int>(c + 1, 0));
46 for (int i = 0; i < l; ++i) {
47 sp[i][0] = a[i][0];
48 }
49
50 for (int j = 0; j < c; ++j) {
51 sp[0][j] = a[0][j];
52 }
53
54 for (int i = 1; i <= l; ++i) {
55 for (int j = 1; j <= c; ++j) {
56 sp[i][j] = sp[i - 1][j] + sp[i][j - 1] - sp[i - 1][j - 1] + a[i - 1][j - 1];
57 }
58 }
59
60 int lim = sqrt(m);
61 int ans = -1;
62 for (int d = 1; d <= lim; ++d) {
63 if (m % d == 0) {
64 int h = d;
65 int w = m / d;
66
67 ans = std::max(ans, verif(sp, l, c, h, w));
68 ans = std::max(ans, verif(sp, l, c, w, h));
69 }
70 }
71 if (ans == -1) {
72 return -1;
73 }
74 return m - ans;
75 }
76
77 int main() {
78 std::ifstream cin("deminare.in");
79 std::ofstream cout("deminare.out");
80
81 int v, l, c, m;
82 cin >> v >> l >> c >> m;
83 std::vector<std::vector<int>> a(l, std::vector<int>(c, 0));
84 int x, y;
85 for (int i = 0; i < m; ++i) {
86 cin >> x >> y;
87 --x; --y;
88 a[x][y] = 1;
89 }
90
91 if (v == 1) {
92 solve1(a, cout);
93 }
94 else {
95 cout << solve2(a, l, c, m) << "\n";
96 }
97 return 0;
98 }

2.2.3 *Rezolvare detaliat 

2.3 mostenire
Problema 3 - mostenire 90 de puncte
Împ ratul cel b trân vrea s  împart  sacii cu galbeni din vistieria palatului celor K feciori
ai s i, numerotaµi de la 1 la K în ordinea vârstei. Feciorul cu num rul 1 este cel mai mare, iar
mezinul are num rul K .
În vistierie sunt N saci plini cu galbeni, a³ezaµi în linie, atât de grei încât nu li se poate schimba
ordinea, iar pe ecare sac este scris num rul de galbeni pe care îi conµine.
CAPITOLUL 2. OJI 2019 55

Împ ratul îl cheam  pe unul dintre feciori ³i îi spune: Fiule, a ta este averea primilor x1
saci! .Feciorul ia sacii ³i pleac  fericit. Apoi, împ ratul cheam  alt fecior ³i îi spune: Fiule, a ta
este averea primilor x2 saci dintre cei r ma³i! . ³i a³a mai departe, pân  ajunge la ultimul fecior
chemat, c ruia îi d  toµi sacii r ma³i.
El nu are o ordine anume în care î³i cheam  feciorii dar are grij  s  cheme ecare fecior exact o
dat . Totodat , pentru a evita certurile între ei, este atent ca ecare fecior s  primeasc  cel puµin
un sac cu galbeni, dar s  NU primeasc  în total mai mulµi galbeni ca un frate mai mare decât el.
Cel mai mic dintre feciorii împ ratului este ³i cel mai viteaz, a³a c  împ ratul ar vrea s  îi dea lui
o sum  de bani cât mai mare, f r  a-i sup ra pe ceilalµi feciori ai s i.

Cerinµe
Cum ar putea împ rµi împ ratul sacii?

Date de intrare
Fi³ierul de intrare mostenire.in conµine pe prima linie numerele naturale N , K , separate de
un spaµiu, cu semnicaµia din enunµ. Pe urm toarele N linii se g se³te câte un num r natural,
reprezentând num rul de galbeni din ecare sac, în ordinea în care ace³tia urmeaz  s  e distribuiµi
ilor.

Date de ie³ire
Fi³ierul de ie³ire mostenire.out va conµine pe prima linie suma de galbeni pe care o va primi
ul cel mic de la împ rat. Pe urm toarele K linii se vor aa câte dou  numere naturale ce
reprezint  num rul de ordine al feciorului, respectiv num rul de saci xi pe care îi prime³te acesta,
în ordinea în care au fost chemaµi de împ rat.

Restricµii ³i preciz ri
a2 & K & 100
aK & N & 100000
a 1 & Num rul de galbeni din ecare sac & 100000
a Galbenii din oricare dintre saci nu pot  împ rµiµi mai multor fraµi
9
a Num rul total de galbeni aaµi în vistierie este mai mic sau egal cu 10
a Împ ratul cel b trân nu are doi feciori cu aceea³i vârst 
a Puteµi a³a orice soluµie în care mezinul prime³te num rul maxim posibil de galbeni
a Pentru ecare test, a³area corect  a num rului maxim de galbeni primiµi de mezin este
notat  cu 40% din punctajul alocat testului
a Pentru teste valorând 10 puncte N K , N & 100
a Pentru teste valorând 30 de puncte 2 & K $ N & 15
a Pentru teste valorând 50 de puncte 2 & K $ N & 100

Exemple
mostenire.in mostenire.outExplicaµii
83 10 Fiul cel mic este chemat primul ³i ia primii 4 saci, primind
1 34 astfel 1  2  3  4 10 galbeni.
2 22 Fiul cel mijlociu este chemat al doilea ³i ia urm torii 2
3 12 saci, primind astfel 5+6=11 galbeni.
4 Fiul cel mare este chemat ultimul ³i ia restul de 2 saci,
5 primind astfel 7  8 15 galbeni.
6
7
8
CAPITOLUL 2. OJI 2019 56

12 4 35 Al doilea u în ordinea vârstei este chemat primul ³i ia


10 23 primii 3 saci, primind astfel 10  5  23 38 de galbeni.
5 44 Fiul cel mic este chemat al doilea ³i ia urm torii 4 saci,
23 13 primind astfel 1  20  4  10 35 de galbeni.
1 32 Fiul cel mare este chemat al treilea ³i ia urm torii 3 saci,
20 primind astfel 12  6  23 41 de galbeni.
4 Al treilea u în ordinea vârstei este chemat ultimul ³i ia
10 restul de 2 saci, primind astfel 18  17 35 de galbeni.
12 O alt  soluµie corect  este:
6 35
23 23
18 34
17 13
42

Timp maxim de executare/test: 0.5 secunde


Memorie: total 32 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB
Sursa: mostenire.cpp, mostenire.c sau mostenire.pas va  salvat  în folderul care are drept
nume ID-ul t u.

2.3.1 Indicaµii de rezolvare


student Maria Pandele - Universitatea din Bucure³ti

Soluµia I - N K - 10p
În acest caz, ecare fecior va lua cate un sac din cei N . Vom împ rµii sacii astfel: feciorul 1 va
lua sacul cu cei mai mulµi galbeni, feciorul 2 pe urm torul ³.a.m.d. pân  când feciorul cel mai mic
ia sacul cu cei mai puµini galbeni. În caz de egalitate nu trebuie s  facem nimic special pentru c 
un fecior mai mare poate primi aceea³i sum  de galbeni ca un fecior mai mic.
Construim 3 vectori:
ai - num rul de galbeni al ec rui sac i în ordinea din ³ierul de intrare;
indi - indicele sacului (iniµial indi i);
f iui - indicele ului ce va lua sacul respectiv (necompletat iniµial).
Sort m descresc tor vectorul a modicând ³i vectorul ind corespunz tor. Pe prima line putem
a³  valoarea celui mai mic sac, aN .
Apoi, vom completa vectorul u printr-o parcurgere de la stânga la dreapta: f iui i.
În acest moment un grup ai, indi, f iui reprezint  nr de galbeni din sac, indicele iniµial
al sacului ³i indicele ului ce va primi acel sac. Pentru a reconstrui împ rµirea, sort m cresc tor
vectorul ind modicând ³i vectorul f iu corespunz tor. Acum în vectorul f iu avem ordinea în care
trebuie chemaµi feciorii de împart.
Pentru c  avem de sortat vectorii, complexitatea timp a acestei soluµii este O N ˜ log N  sau
2
O N , în funcµie de algoritmul de sortare ales.

Soluµia II - brute force - 30p


Vom construi toate variantele posibile de a împarµi sacii. Observ m c  trebuie s  gener m
K  1 poziµii din cele N , e ele s1, s2, ..., sK  1.
Astfel, un u va primi sacii cu indicii intre 1 ³i s1, alt u va primi sacii cu indicii de la
s1  1 la s2, ..., ultimul u va primi sacii cu indicii de la sK  1  1 la N .
Gener m cu un algoritm de tip succesor toate variantele posibile. Vom p stra soluµia în care
ul cel mic ia cei mai mult posibil.
Pentru ecare împ rµire posibil , calcul m cât ia ecare u ³i ordinea în care feciorii sunt
chemaµi folosind un raµionament similar cu cel de la Soluµia I: calculam toate cele K sume de
galbeni într-un vector pe care îl sort m descresc tor.
Atribuim ului cu indicele 1 cea mai mare sum  de galbeni, apoi ului 2 urm toarea cea mai
mare sum  de galbeni (NU uit m ca poate  egal  cu cea precedent ), ³.a.m.d. Mezinul va lua
cea mai mic  sum  de galbeni. Pentru a³area împ rµirii este necesar  înc  o sortare a ilor dup 
indicii sacilor luaµi.
N
Complexitatea acestei soluµii este exponenµial , O 2 . O soluµie de aceast  complexitate ar
trebui s  primeasc  cel puµin 30 de puncte.
CAPITOLUL 2. OJI 2019 57

Soluµia III - O N 3  - 60p


Se observ  c  dac  cel mai mic dintre feciori ia o anumit  sum  de galbeni X , atunci toµi
feciorii mai mari ca el trebuie s  ia cel puµin X galbeni.
Cum ecare fecior va primi o subsecvenµ  continu  de saci, ne putem xa secvenµa de saci pe
care o va primi cel mai mic fecior.
Fie aceast  secvenµ  i, j  cu suma de galbeni X . Trebuie s  veric m dac  putem împarµi
sacii 1, i  1 ³i sacii j  1, N  în cel puµin K  1 subsecvenµe continue cu suma mai mare ca X .
Pentru aceasta, vom face dou  parcurgeri: una pornind din i spre stânga, cealalt  pornind din j
spre dreapta. Vom reµine la ecare pas o variabil  suma ³i un contor cnt, iniµial egale cu 0. Pentru
ecare sac în ordinea parcurgerii, vom aduna num rul de galbeni la suma curent . Când aceasta
va deveni mai mare sau egal  cu X , vom reseta suma ³i vom incrementa cnt. F când acela³i lucru
³i pentru partea din dreapta, putem verica dac  subsecvenµa i, j  este valid , vericând ca suma
celor dou  contoare s  dep ³easc  K  1.
Dintre toate subsecvenµele valide, o vom p stra pe cea cu suma cea mai mare.
3
Aceast  soluµie are complexitate O N  ³i ar trebui s  obµin  cel puµin 60 de puncte.

Reconstrucµia împ rµirii
Se poate adapta soluµia de mai sus pentru a a³a ³i cele K subsecvenµe alese, în cazurile valide.
Pentru a asigna cele K subsecvenµe celor K i, trebuie s  ne asigur m c  averile vor  atribuite
ilor în ordinea vârstei sale. Acest lucru se va realiza prin doi pa³i:
Pasul 1: A m ecare subsecvenµ  c rui u îi aparµine. Pentru aceasta, atribuim ec rei
subsecvenµe numere de la 1 la K , în ordinea descresc toare a num rului de galbeni. Acest lucru
se poate realiza printr-o sortare.
Pasul 2: A³ m lungimea subsecvenµei ³i ul corespunz tor, în ordinea de la stânga la dreapta.
O atenµie special  trebuie acordat  cazului în care soluµia de mai sus va a³a mai mult de
K subsecvenµe, caz în care unele subsecvenµe vecine vor trebui contopite. Din fericire, îns , se
poate observa c  putem alege s  contopim oricare dou  subsecvenµe vecine, soluµia r mânând
valid .
2
Complexitatea reconstrucµiei este O N  sau O N ˜log N , în funcµie de algoritmul de sortare
ales.

Soluµia IV - O N ˜ log sumat otalag albeni - 90p


Putem îmbun t µii soluµia precedent  c utând binar cel mai mare X intre 1, S  (unde S
este suma total  de galbeni din saci), astfel încât s  existe K subsecvenµe în ³irul de saci, cu suma
ec reia cel puµin egal  cu X . Pentru un X xat vom verica dac  exist  K astfel de subsecvenµe.
Dac  nu exist , atunci vom c uta printre valori mai mici.
Pentru a verica dac  un X xat este bun, vom proceda ca ³i în soluµia precedent : pornim
cu o variabil  suma ³i un contor cnt, iniµial egale cu 0. Parcurgem sacii de la 1 la N ³i adun m
pe rând num rul de galbeni din sacul i la suma curent . Când suma devine mai mare sau egala
cu X atunci m rim contorul cnt cu 1 ³i reset m suma. Dac , la nal cnt devine cel puµin egal
cu K atunci X -ul xat este bun. Diferenµa este c , în acest caz, nu mai trebuie s  ne x m
subsecvenµa de sum  egal  cu X .
C utarea binar  va face log S  pa³i, iar la ecare pas avem o parcurgere liniar  pentru veri-
care. Reconstituirea se realizeaz  similar cu soluµia precedent .
Complexitatea nal  este O N ˜ log S   log N . O soluµie cu o complexitate similar  ar
trebui s  primeasc  90 de puncte.

2.3.2 Cod surs 

Listing 2.3.1: mostenire_LB.cpp


1 /*
2 * Problema mostenire (OJI 2019)
3 * Complexitate: O(N*(log(S)+log(N))
4 * Autor: Lucian Bicsi
5 */
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 vector<tuple<int, int, int>> Solve(vector<int> v, int k, int need)
CAPITOLUL 2. OJI 2019 58

12 {
13 int n = v.size();
14
15 vector<tuple<int, int, int>> ans;
16
17 int have = 0, start = 0;
18
19 for (int i = 0; i < n; ++i)
20 {
21 have += v[i];
22 if (have < need)
23 continue;
24 if ((int)ans.size() == k - 1 && i != n - 1)
25 continue;
26
27 ans.emplace_back(have, start, i + 1);
28 have = 0;
29 start = i + 1;
30 }
31
32 return ans;
33 }
34
35 int main()
36 {
37 ifstream cin("mostenire.in");
38 ofstream cout("mostenire.out");
39
40 int n, k;
41 cin >> n >> k;
42
43 assert(k <= 100);
44 assert(k <= n);
45 assert(n <= 100 * 1000);
46
47 vector<int> v(n);
48
49 int total = 0;
50 for (int i = 0; i < n; ++i)
51 {
52 cin >> v[i];
53 total += v[i];
54 }
55
56 assert(total <= 1000 * 1000 * 1000);
57
58 int b = 0, e = total, ans = -1;
59 while (b <= e)
60 {
61 int m = (b + e) / 2;
62 if ((int)Solve(v, k, m).size() == k)
63 {
64 ans = m;
65 b = m + 1;
66 }
67 else
68 e = m - 1;
69 }
70
71 assert(ans != -1);
72 cout << ans << endl;
73
74 auto sol = Solve(v, k, ans);
75
76 sort(sol.rbegin(), sol.rend());
77
78 vector<tuple<int, int, int>> new_sol;
79
80 for (int i = 0; i < k; ++i)
81 {
82 int b, e; tie(ignore, b, e) = sol[i];
83 new_sol.emplace_back(b, i, e - b);
84 }
85
86 sort(new_sol.begin(), new_sol.end());
87
CAPITOLUL 2. OJI 2019 59

88 for (auto itr : new_sol)


89 {
90 int i, cnt; tie(ignore, i, cnt) = itr;
91 cout << i + 1 << " " << cnt << ’\n’;
92 }
93
94 return 0;
95 }

Listing 2.3.2: mostenire_MLT.cpp


1 // prof. Mircea Lupse-Turpan - 90p
2
3 #include <fstream>
4 #include <algorithm>
5
6 using namespace std;
7
8 ifstream fin("mostenire.in");
9 ofstream fout("mostenire.out");
10
11 const int NMax = 100000;
12 const int KMax = 100;
13 int X[NMax + 5];
14 int Index[KMax + 5], Sum[KMax + 5], Bags[KMax + 5];
15 int N,K,TotalSum,Sol = -1;
16
17 void Read()
18 {
19 fin >> N >> K;
20 for(int i = 1; i <= N; ++i)
21 {
22 fin >> X[i];
23 TotalSum += X[i];
24 }
25 }
26
27 bool Check(int Value)
28 {
29 int Sum = 0; int Nr = 0;
30 for(int i = 1; i<= N; ++i)
31 {
32 Sum += X[i];
33 if(Sum >= Value)
34 {
35 Nr++;
36 Sum = 0;
37 }
38 }
39
40 return (Nr >= K);
41 }
42
43 void Solve()
44 {
45 int Left = 1, Right = TotalSum;
46
47 while(Left <= Right)
48 {
49 int Mid = (Left + Right) / 2;
50 if(Check(Mid))
51 {
52 Sol = Mid;
53 Left = Mid + 1;
54 }
55 else
56 Right = Mid - 1;
57 }
58 }
59
60 bool Compare(int A, int B)
61 {
62 return Sum[A] > Sum[B];
63 }
64
CAPITOLUL 2. OJI 2019 60

65 void Print()
66 {
67 int j = 0;
68 fout << Sol << "\n";
69 Sum[1] = Bags[1] = 0;
70 j = 1;
71 for(int i = 1; i<= N; ++i)
72 {
73 Sum[j] += X[i];
74 Bags[j]++;
75 if(Sum[j] >= Sol)
76 {
77 Sum[++j] = 0;
78 Bags[j] = 0;
79 }
80 }
81
82 Sum[K] += Sum[K+1];
83 Bags[K] += Bags[K+1];
84
85 for(int i = 1; i <= K; ++i)
86 Index[i] = i;
87
88 sort(Index + 1, Index + K + 1, Compare);
89
90 for(int i = 1; i <= K; ++i)
91 Sum[Index[i]] = i;
92
93 for(int i = 1; i <= K; ++i)
94 fout << Sum[i] << " " << Bags[i] << "\n";
95 }
96
97 int main()
98 {
99 Read();
100 Solve();
101 Print();
102 return 0;
103 }

Listing 2.3.3: mostenire_MP.cpp


1 // Maria Pandele - 90p
2
3 #include <fstream>
4 #include <iostream>
5 #include <vector>
6 #include <cassert>
7 #include <algorithm>
8
9 const int N = 1e5;
10 const int K = 1e2;
11 const int VALMAX = 1e5;
12 const int SMAX = 1e9;
13
14 struct Fiu
15 {
16 int ind;
17 int s;
18 int st; int dr;
19 };
20
21 bool solve(int s_mezin, std::vector<int>& a, int& k)
22 {
23 int cnt = 0, s = 0;
24
25 for (int i = 0; i < a.size(); ++i)
26 {
27 s += a[i];
28 if (s >= s_mezin) {
29 ++cnt;
30 if (cnt == k) {
31 break;
32 }
33 s = 0;
CAPITOLUL 2. OJI 2019 61

34 }
35 }
36 if (cnt == k) {
37 return true;
38 }
39 return false;
40 }
41
42 void imparte(const int& s_mezin, std::vector<int>& a,
43 const int& k, std::vector<Fiu>& sol)
44 {
45 int cnt = -1, last = -1, s = 0;
46
47 for (int i = 0; i < a.size(); ++i) {
48 s += a[i];
49 if (s >= s_mezin) {
50 ++cnt;
51 sol[cnt].st = last + 2;
52 sol[cnt].dr = i + 1;
53 sol[cnt].s = s;
54 if (cnt == k - 1) {
55 sol[cnt].dr = a.size();
56 for (int j = i + 1; j < a.size(); ++j) {
57 s += a[j];
58 }
59 sol[cnt].s = s;
60 break;
61 }
62 last = i;
63 s = 0;
64 }
65 }
66
67 sort(sol.begin(), sol.end(), [](const Fiu& A, const Fiu& B) -> bool {
68 return A.s > B.s;
69 });
70
71 for (int i = 0; i < sol.size(); ++i) {
72 sol[i].ind = i + 1;
73 }
74
75 sort(sol.begin(), sol.end(), [](const Fiu& A, const Fiu& B) -> bool {
76 return A.st < B.st;
77 });
78 }
79
80 int main()
81 {
82 std::ifstream cin("mostenire.in");
83 std::ofstream cout("mostenire.out");
84
85 int n, k;
86 cin >> n >> k;
87 assert(k <= n && n <= N);
88 assert(2 <= k && k <= K);
89
90 std::vector<int> a(n);
91 long long stotal = 0;
92 for (int i = 0; i < n; ++i) {
93 cin >> a[i];
94 assert(1 <= a[i] && a[i] <= VALMAX);
95 stotal += a[i];
96 }
97
98 assert(stotal <= SMAX);
99
100 int mezin = 0;
101 for (int step = (1 << 30); step; step >>= 1) {
102 if (mezin + step <= stotal&& solve(mezin + step, a, k)) {
103 mezin += step;
104 }
105 }
106
107 assert(mezin != 0);
108 std::vector<Fiu> sol(k);
109 imparte(mezin, a, k, sol);
CAPITOLUL 2. OJI 2019 62

110 cout << mezin << "\n";


111
112 for (int i = 0; i < sol.size(); ++i) {
113 cout << sol[i].ind << " " << sol[i].dr - sol[i].st + 1 << "\n";
114 assert(sol[i].dr - sol[i].st + 1 > 0);
115 }
116
117 return 0;
118 }

2.3.3 *Rezolvare detaliat 


Capitolul 3

OJI 2018

3.1 Cuf r
Problema 1 - Cuf r 100 de puncte
Vr jitoarea cea bun  are un cuf r în care este închis  piatra magic  de c tre piticii l zii cu
ajutorul unui cifru digital. Piticii i-au dat vr jitoarei o cutie în care sunt n cartona³e. Pe ecare
cartona³ este scris un num r natural pe care vr jitoarea îl va folosi s  deschid  lada. Valorile
scrise pe cartona³e sunt distincte între ele.
Pentru a aa cifrul trebuie s  procedeze astfel: extrage ecare cartona³ din cutie ³i apoi
determin  valoarea magic  asociat  num rului natural scris pe cartona³. Pentru ecare cartona³
valoarea magic  este dat  de al k -lea divizor prim al num rului înscris pe acesta. Vr jitoarea
trebuie s  adune valorile magice obµinute pentru cele n cartona³e ³i apoi s  introduc  în ordine
cifrele valorii obµinute, pentru a descuia lada.

Cerinµe
Deoarece vr jitoarea nu are timp la dispoziµie v  roag  pe voi s  o ajutaµi s  rezolve urm toarele
probleme:
1. S  ae valoarea magic  pentru un cartona³ dat;
2. S  ae cifrul cuf rului.

Date de intrare
Fi³ierul de intrare este cufar.in.
Pe prima linie a ³ierului de intrare se g sesc o valoare p care poate  doar 1 sau 2 ³i num rul
n de cartona³e desp rµite prin câte un spaµiu.
Dac  p este 1 pe linia a doua a ³ierului de intrare se g sesc dou  valori reprezentând num rul
de pe cartona³ul dat ³i valoarea k , separate printr-un spaµiu, cu semnicaµia de mai sus.
Dac  p este 2 pe urm toarele n linii ale ³ierului de intrare se g sesc câte dou  valori, separate
prin câte un spaµiu, reprezentând num rul de pe cartona³ ³i valoarea lui k pentru ecare din cele
n cartona³e.

Date de ie³ire
Fi³ierul de ie³ire este cufar.out.
Dac  valoarea lui p este 1, atunci se va rezolva doar cerinµa 1 ³i ³ierul de ie³ire va conµine pe
prima linie valoarea magic  asociat  cartona³ului dat.
Dac  valoarea lui p este 2, atunci se va rezolva doar cerinµa 2 ³i ³ierul de ie³ire va conµine pe
prima linie cifrul necesar deschiderii cuf rului.

Restricµii ³i preciz ri
a 1 & n $ 1000000
a 2 & valoarea înscris  pe un cartona³ & 1000000
a Se garanteaz  c  pentru ecare pereche (num r, k ), num r are cel puµin k divizori primi.
a Pentru rezolvarea corect  a cerinµei 1 se acord  18 puncte
a Pentru rezolvarea corect  a cerinµei 2 se acord  72 de puncte
a Pentru rezultate corecte la cerinµa a doua respectând restricµiile problemei ³i n & 1000 se
acord  18 puncte

63
CAPITOLUL 3. OJI 2018 64

a Pentru rezultate corecte la cerinµa a doua respectând restricµiile problemei ³i n & 500000 se
acord  43 de puncte
a Din ociu se acord  10 puncte.

Exemple
cufar.in cufar.out Explicaµii
11 5 p = 1, n = 1
30 3 Se rezolv  doar prima cerinµ 
Al 3-lea divizor prim al num rului 30 este 5
25 48 p = 2, n = 5
30 3 Se rezolv  doar a doua cerinµ 
64 1 Al 3-lea divizor prim al num rului 30 este 5
105 2 Primul divizor prim al num rului 64 este 2
1001 3 Al 2-lea divizor prim al num rului 105 este 5
5474 4 Al 3-lea divizor prim al num rului 1001 este 13
Al 4-lea divizor prim al num rului 5474 este 23
Suma c utat  va  S = 5 + 2 + 5 + 13 + 23,
de unde rezult  cifrul 48

Timp maxim de executare/test: 1.0 secunde


Memorie: total 128 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

3.1.1 Indicaµii de rezolvare


prof. Cristian Toncea - Colegiul Naµional Nicoale B lcescu Br ila

Se num r  cu ajutorul ciurului lui Eratostene câµi divizori primi are ecare din numerele
naturale mai mici sau egale cu 1000000. În momentul când pentru unul din numerele scrise pe
cartona³e se ajunge la numarul de divizori c utat se adaug  la sum  valoarea num rului prim ce
a condus la atingerea num rului de divizori c utat.

3.1.2 Cod surs 

Listing 3.1.1: cufar-100p.cpp


1 //prof. Cristian Toncea - Colegiul National "Nicolae Balcescu" - Braila
2 #include <cstdio>
3
4 using namespace std;
5
6 int p;
7 long long s=0;
8 long a[1000005],k[1000005]={-1},i,n,j,c,l;
9
10 int main()
11 {
12 freopen("cufar.in","r",stdin);
13 freopen("cufar.out","w",stdout);
14 scanf("%d %lld",&p,&n);
15
16 if(p==1)
17 {
18 scanf("%d%d",&l,&c);
19 for(i=2;i<=500000;i++)
20 if(a[i]==0)
21 for(j=1;j<=1000000/i;j++)
22 {
23 a[i*j]++;
24 if(a[i*j]==c && i*j==l)
25 printf("%ld",i);
26 }
27
28 }
29 else
30 {
CAPITOLUL 3. OJI 2018 65

31 for(i=1;i<=n;i++)
32 {
33 scanf("%d%d",&j,&c);
34 k[j]=c;
35 }
36
37 for(i=2;i<=1000000;i++)
38 if(a[i]==0)
39 for(j=1;j<=1000000/i;j++)
40 {
41 a[i*j]++;
42 if(a[i*j]==k[i*j])
43 {
44 s=s+i;
45 }
46 }
47
48 printf("%lld",s);
49 }
50
51 fclose(stdin);
52 fclose(stdout);
53 return 0;
54 }

Listing 3.1.2: cufar-dani-100p.cpp


1 //student Daniel Posdarascu - Universitatea Bucuresti
2 #include<cstdio>
3
4 #define NMAX 1000005
5
6 int p, n, query[NMAX + 5], num_div[NMAX + 5], notPrime[NMAX + 5];
7 long long answer;
8
9 int main ()
10 {
11 int a, b;
12
13 freopen("cufar.in","r",stdin);
14 freopen("cufar.out","w",stdout);
15
16 scanf("%d",&p);
17
18 scanf("%d",&n);
19 for(int i = 1; i <= n; i++)
20 {
21 scanf("%d %d", &a, &b);
22 query[a] = b;
23 }
24
25 long long answer = 0;
26 for(int i = 2; i <= NMAX; i++)
27 {
28 if(!notPrime[i])
29 {
30 for(int j = i; j <= NMAX; j += i)
31 {
32 num_div[j]++;
33 notPrime[j] = 1;
34 if(num_div[j] == query[j])
35 answer += i;
36 }
37 }
38 }
39
40 printf("%lld\n", answer);
41
42 return 0;
43 }

3.1.3 *Rezolvare detaliat 


CAPITOLUL 3. OJI 2018 66

3.2 fadema
Problema 2 - fadema 100 de puncte
Corina a cump rat de la magazin un material din pânz  colorat , de form  dreptunghiular 
pentru a decupa din el o faµ  de mas  pentru masa din buc t rie. Fiindc  este pasionat  de ³ah,
Corina a ales un material format din n  m p trate de aceea³i dimensiune colorate cu alb sau
negru. P tratele sunt lipite ³i sunt dispuse pe linii ³i coloane paralele cu laturile dreptunghiului din
pânz  care a fost cump rat. Dou  p trate se numesc vecine dac  au în comun o latur . Materialul
din pânz  nu respect  neap rat structura unei table de ³ah, adic  p tratele vecine pe aceea³i linie
sau pe aceea³i coloan  nu sunt în mod necesar colorate în mod alternativ.
Corina î³i propune prin urmare s  decupeze un dreptunghi cu un num r maxim de p trate,
paralel cu laturile dreptunghiului din pânz  care a fost cump rat, care s  respecte alternanµa
culorilor pe o tabl  de ³ah.

Cerinµe
Sa se determine num rul maxim de p trate întregi ale unui dreptunghi cu laturile paralele cu
cele ale materialului cump rat, care poate  decupat astfel încât s  nu existe dou  p trate vecine
având aceea³i culoare.

Date de intrare
Fi³ierul fadema.in conµine pe prima linie dou  numere naturale n ³i m reprezentând num rul
de linii, respectiv num rul de coloane ale materialului din pânz  care a fost cump rat.
Pe ecare dintre urm toarele n linii se a  câte m cifre 0 sau 1 desp rµite prin câte un spaµiu,
reprezentând culorile p tratelor materialului. Cifra 0 codic  culoarea alb , iar cifra 1 codic 
culoarea neagr .

Date de ie³ire
Fi³ierul fadema.out va conµine pe prima linie un singur num r natural A, reprezentând nu-
m rul maxim de p trate ale unui dreptunghi care poate  decupat astfel încât s  respecte cerinµa
din enunµ. Dac  nu exist  dreptunghiuri cu cel puµin dou  p trate având culori alternante, se va
scrie valoarea 1.

Restricµii ³i preciz ri
2 & N & 1000
a
2 & M & 1000
a
a Pentru rezolvarea corect  a cerinµei respectând restricµiile problemei se acord  90 de puncte
a Pentru rezultate corecte respectând restricµiile problemei ³i n, m & 100 se acord  20 de
puncte
a Pentru rezultate corecte respectând restricµiile problemei ³i n, m & 200 se acord  40 de
puncte
a Pentru rezultate corecte respectând restricµiile problemei ³i n, m & 400 se acord  65 de
puncte
a Din ociu se acord  10 puncte

Exemple
fadema.in fadema.out Explicaµii
3 4 6 Dreptunghiul delimitat de liniile 1 ³i 3, respectiv coloanele 2 ³i
0 0 10 3 are 6 p trate (fundal de culoare gri)
1 1 00
1 0 10
4 5 5 Dreptunghiul delimitat de linia 2, respectiv coloanele 1 ³i 5 are
0 1 1 0 1 5 p trate (fundal de culoare gri)
1 0 1 0 1
0 0 1 1 0
1 1 0 1 1

Timp maxim de executare/test: 1.0 secunde


CAPITOLUL 3. OJI 2018 67

Memorie: total 128 MB din care pentru stiv  32 MB


Dimensiune maxim  a sursei: 10 KB

3.2.1 Indicaµii de rezolvare


prof.Constantin G l µan - Colegiul Naµional Liviu Rebreanu Bistriµa

Dreptunghiul cu proprietatea din enunµ este o submatrice delimitat  de dou  coloane i ³i j ,


cu 1 & i & j & m ale matricei date, pe care o numim a. Se va parcurge aceast  submatrice
c utând cea mai lung  secvenµ  de linii alternante (valori 1 ³i 0). Pentru ecare linie a acestei
submatrice nu trebuie s  existe nicio valoare identic  cu valoarea de pe linia anterioar  aat  pe
aceea³i coloan .

Complexitate O n6  - 20 de puncte
4
Se genereaz  în O n  toate submatricile cuprinse între liniile i1 ³i i2, respectiv coloanele j1
2
³i j2. Fiecare asemenea submatrice se parcurge în O n  ³i se veric  validitatea sa.

Complexitate O n4  - 40 de puncte
Se construie³te ³irul r1, r2, ..., rn cu valori 0, 1 sau 2. rk  (1 & k & n) va  0 dac 
ak i este 0, rk  1 dac  ak i 1 ³i rk  2 dac  linia k a submatricei nu respect 
condiµia de alternanµ . În continuare, se va c uta în ³irul r cea mai lung  secvenµ  alternant .
Fie L lungimea acesteia. Atunci num rul de elemente a submatricei alternante este L ˜ j  i  1.
Soluµia va  maximul acestor valori:

sol max L ˜ j  i  1, cu 1 & i & j & m.

Complexitate O n3  - 65 de puncte
Se procedeaz  ca mai sus, doar c  validitatea liniei k se veric  pe m sur  ce se modic 
lungimea liniei, adic  odat  cu cre³terea valorii j . Aceasta duce la eliminarea unei parcurgeri de
linie, iar complexitatea algoritmului scade cu un ordin de m rime.

Complexitate O n2  - 90 puncte
Calcul m submatricea de dimensiune maxim  ce s-ar putea obµine pornind spre dreapta ³i în
jos de la ecare element al matricei ³i respect  cerinµa din enunµ. Facem apoi acela³i calcul spre
sânga ³i în jos.
Realiz m suma dimensiunilor celor dou  submatrici pentru ecare element al matricei ³i ma-
ximul dintre aceste sume este valoarea c utat .
Pentru a calcula submatricea de dimensiune maxima ce s-ar putea obµine pornind spre dreapta
³i în jos de la ecare element al matricei avem nevoie de:
a) Lungimea maxim  lStij  a unui ³ir de elemente cu proprietatea din enunµ pornind de la
2
ecare element aij  al matricei spre stânga. Aceste lungimi pot  calculate în O n  parcurgân
matricea de la dreapta spre stânga. Pentru ecare element
aij ! aij  1 avem lStij  lStij  1  1 ³i
Stij  1 pentru elementele cu aij  aij  1 ³i pentru j m.
b) Lungimea maxim  lJosij  a unui ³ir de elemente cu proprietatea din enunµ pornind de
2
la ecare element aij  al matricei în jos. Aceste lungimi pot  calculate în O n  parcurgând
matricea de jos în sus analog punctului a).
c) Produsul dintre lungimea maxim  lJosij  ³i minimul lungimilor maxime lSt al elemen-
telor care aparµin sub³irului lJosij  (adic  minimul valorilor lStk j  al elementrlor ak j 
pentru k i, i  1, ..., i  lJosij  reprezint  dimensiunea submatricei. Aceste minime pot 
2
calculate în O n  pentru toat  matricea parcurgând-o de jos în sus ³i p strând minimul dintre
lStij  ³i lStij  1 pentru toate elementele aij ! aij  1 iar pentru elementele cu
aij  aij  1 ³i pentru j m p stram lStij .
Analog se procedeaz  ³i pentru submatricea de dimensiune maxima ce s-ar putea obµine por-
nind spre stânga ³i în jos de la ecare element al matricei.
Observaµie: la punctajele de mai sus se adaug  10 puncte din ociu

3.2.2 Cod surs 


CAPITOLUL 3. OJI 2018 68

Listing 3.2.1: fadema_n6.cpp


1 /*
2 Solutie O(n^6)
3 prof. Constantin Galatan
4 */
5 #include <fstream>
6 #include <algorithm>
7
8 using namespace std;
9
10 ifstream fin("fadema.in");
11 ofstream fout("fadema.out");
12
13 const int MaxN = 1001;
14 short a[MaxN][MaxN];
15 short L[MaxN];
16
17 int n, m;
18 int nmax;
19
20 int main()
21 {
22 fin >> n >> m;
23
24 for (int i = 0; i < n; ++i)
25 for (int j = 0; j < m; ++j)
26 fin >> a[i][j];
27
28 for (int i1 = 0; i1 < n; i1++)
29 for (int i2 = i1; i2 < n; i2++)
30 for (int j1 = 0; j1 < m; ++j1)
31 for (int j2 = j1; j2 < m; ++j2)
32 {
33 bool ok = true;
34 for (int i = i1; i <= i2 && ok; ++i)
35 for (int j = j1; j <= j2 && ok; ++j)
36 if ((j < j2 && a[i][j] == a[i][j + 1]) ||
37 (i > i1 && a[i][j] == a[i - 1][j]))
38 {
39 ok = false;
40 }
41 if (ok)
42 nmax = max(nmax, (i2 - i1 + 1) * (j2 - j1 + 1));
43 }
44
45 fout << nmax;
46 fin.close();
47 fout.close();
48 }

Listing 3.2.2: fadema_n4.cpp


1 /*
2 Solutie O(n^4)
3 prof. Constantin Galatan
4 */
5 #include <fstream>
6 #include <algorithm
7
8 using namespace std;
9
10 ifstream fin("fadema.in");
11 ofstream fout("fadema.out");
12
13 const int MaxN = 1001;
14 short a[MaxN][MaxN];
15 short L[MaxN];
16 int n, m;
17 int nmax;
18
19 int main()
20 {
21 fin >> n >> m;
22 for (int i = 0; i < n; ++i)
CAPITOLUL 3. OJI 2018 69

23 for (int j = 0; j < m; ++j)


24 fin >> a[i][j];
25
26 for (int i = 0; i < m; i++)
27 for (int j = i; j < m; j++)
28 {
29 for (int k = 0; k < n; k++)
30 {
31 bool ok = true;
32 for (int c = i + 1; c <= j && ok; c++)
33 if ( a[k][c] == a[k][c - 1])
34 ok = false;
35
36 if (ok)
37 L[k] = a[k][i];
38 else
39 L[k] = 2;
40 }
41
42 int hmax = 0, h = 0;
43 for (int k = 0; k < n; k++)
44 {
45 if ( L[k] == 2 )
46 h = 0;
47 else
48 {
49 if ( h > 0 && L[k - 1] != L[k] )
50 h++;
51 else
52 h = 1;
53 }
54
55 hmax = max(hmax, h);
56 }
57
58 nmax = max(nmax, hmax * (j - i + 1));
59 }
60
61 fout << nmax;
62 fin.close();
63 fout.close();
64 }

Listing 3.2.3: fadema_n3.cpp


1 /*
2 Solutie O(n^3)
3 prof. Constantin Galatan
4 */
5 #include <fstream>
6 #include <algorithm>
7
8 using namespace std;
9
10 ifstream fin("fadema.in");
11 ofstream fout("fadema.out");
12
13 const int MaxN = 1001;
14 short a[MaxN][MaxN];
15 short L[MaxN];
16 int n, m;
17 int nmax;
18
19 int main()
20 {
21 fin >> n >> m;
22
23 for (int i = 0; i < n; ++i)
24 for (int j = 0; j < m; ++j)
25 fin >> a[i][j];
26
27 for (int i = 0; i < m; i++)
28 {
29 for (int j = i; j < m; j++)
30 if (j == i)
CAPITOLUL 3. OJI 2018 70

31 for (int k = 0; k < n; k++)


32 L[k] = a[k][i];
33 else
34 {
35 for (int k = 0; k < n; k++)
36 if (a[k][j] == a[k][j - 1] )
37 L[k] = 2;
38
39 int hmax = 0, h = 0;
40 for (int k = 0; k < n; k++)
41 {
42 if ( L[k] == 2 )
43 h = 0;
44 else
45 {
46 if ( h > 0 && L[k - 1] != L[k] )
47 h++;
48 else
49 h = 1;
50 }
51
52 hmax = max(hmax, h);
53 }
54
55 nmax = max(nmax, hmax * (j - i + 1));
56 }
57 }
58
59 fout << nmax;
60 fin.close();
61 fout.close();
62 }

Listing 3.2.4: fadema_n2.cpp


1 // Prof Marcel Dragan
2 // Colegiul National Samuel von Bruckenthal
3 // Complexitate: O(n^2)
4
5 #include <fstream>
6 using namespace std;
7 ifstream in("fadema.in");
8 ofstream out("fadema.out");
9
10 short n,m,a[1001][1001],mr1[1001][1001],mr2[1001][1001],
11 mc[1001][1001],mc1[1001][1001],mc2[1001][1001];
12
13 int main()
14 {
15 in>>n>>m;
16 for(int i=1;i<=n;i++)
17 {
18 for(int j=1;j<=m;j++)
19 {
20 in>>a[i][j];
21 }
22 }
23
24 for(int i=1;i<=n;i++)
25 {
26 for(int j=m;j>0;j--)
27 {
28 if(j==m)
29 {
30 mr2[i][j]=1;
31 }
32 else
33 {
34 if(a[i][j]+a[i][j+1]==1)
35 {
36 mr2[i][j]=mr2[i][j+1]+1;
37 }
38 else
39 {
40 mr2[i][j]=1;
CAPITOLUL 3. OJI 2018 71

41 }
42 }
43 }
44 }
45
46 for(int i=1;i<=n;i++)
47 {
48 for(int j=1;j<=m;j++)
49 {
50 if(j==1)
51 {
52 mr1[i][j]=1;
53 }
54 else
55 {
56 if(a[i][j]+a[i][j-1]==1)
57 {
58 mr1[i][j]=mr1[i][j-1]+1;
59 }
60 else
61 {
62 mr1[i][j]=1;
63 }
64 }
65 }
66 }
67
68 for(int i=1;i<=n;i++)
69 {
70 for(int j=1;j<=m;j++)
71 {
72 if(i==1)
73 {
74 mc1[i][j]=mr1[i][j];
75 mc2[i][j]=mr2[i][j];
76 mc[i][j]=1;
77 }
78 else
79 {
80 if(a[i][j]+a[i-1][j]==1)
81 {
82 mc1[i][j]=min(mc1[i-1][j],mr1[i][j]);
83 mc2[i][j]=min(mc2[i-1][j],mr2[i][j]);
84 mc[i][j]=mc[i-1][j]+1;
85 }
86 else
87 {
88 mc1[i][j]=mr1[i][j];
89 mc2[i][j]=mr2[i][j];
90 mc[i][j]=1;
91 }
92 }
93 }
94 }
95
96 int M=1;
97 for(int i=1;i<=n;i++)
98 {
99 for(int j=1;j<=m;j++)
100 {
101 if(M<mc[i][j]*(mc1[i][j]+mc2[i][j]-1))
102 M=mc[i][j]*(mc1[i][j]+mc2[i][j]-1);
103 }
104 }
105
106 out << M << endl;
107 return 0;
108 }

3.2.3 *Rezolvare detaliat 


CAPITOLUL 3. OJI 2018 72

3.3 tnia
Problema 3 - tnia 100 de puncte
Se d  o matrice binar  cu n coloane ³i m linii. Coloanele sunt numerotate de la stânga la
dreapta cu valori de la 1 la n, iar liniile sunt numerotate de jos în sus cu valori de la 1 la m.
Matricea dat  are o form  particular , astfel c  pentru ecare coloan  i de la 1 la n toate
elementele matricei de pe coloana respectiv  au valoarea 1 pentru toate liniile cuprinse în intervalul
1, hi ³i în rest valoarea 0. Valorile hi sunt numere naturale date în ordine cresc toare
(hi  1 & hi, 1 & i & n).

Cerinµe
S  se r spund  la q întreb ri de forma: dându-se numerele A, B , C , D se cere suma elementelor
din submatricea determinat  de zona dreptunghiular  având colµul stânga-jos în coloana A ³i linia
B , iar colµul dreapta-sus în coloana C ³i linia D.

Date de intrare
Fi³ierul de intrare este tnia.in.
a pe prima linie se g sesc dou  numere naturale n ³i m desp rµite printr-un spaµiu, cu semni-
caµia de mai sus;
a pe a doua linie sunt cele n elemente hi ale vectorului desp rµite prin câte un spaµiu;
a pe a treia linie este un num r natural q ce reprezint  num rul de întreb ri;
a pe urm toarele q linii se g sesc câte 4 numere A, B , C , D cu semnicaµia de mai sus,
desp rµite prin câte un spaµiu.

Date de ie³ire
Fi³ierul de ie³ire tnia.out va conµine q linii reprezentând r spunsul pentru ecare întrebare.

Restricµii ³i preciz ri
a 0 & hi & m, 1 & n & 100000
a 1 & q & 100000, 1 & m & 1000000000
a Pentru 15 puncte: n, m, q & 100
a Pentru alte 16 puncte: n, m, q & 3000
a Pentru alte 16 puncte: n & 100000, m & 1000000000, q & 100
a Pentru rezolvarea corect  a cerinµei se acord  90 de puncte
a Din ociu se acord  10 puncte.

Exemple
tnia.in tnia.out Explicaµii
5 10 30 Zona dreptunghiular  având colµul stânga-jos la coloana 1 ³i
2 3 7 8 10 6 linia 1 ³i colµul dreapta-sus la coloana 5 ³i linia 10 are suma
5 5 elementelor 30.
1 1 5 10 0 Analog, pentru celelalte patru întreb ri, r spunsurile corecte
2 5 4 7 6 sunt: 6, 5, 0 ³i 6
3 2 3 6
3 8 3 10
3 2 3 10

Timp maxim de executare/test: 0.7 secunde


Memorie: total 132 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

3.3.1 Indicaµii de rezolvare


Daniel Posd r scu - Universitatea Bucure³ti

Solutie pentru 15 puncte:


CAPITOLUL 3. OJI 2018 73

Putem construi iniµial întreaga matrice. Pentru ecare query (întrebare), parcurgem subma-
tricea ³i facem suma elementelor.
2
Complexitate: O N ˜ Q

Soluµie pentru 31 de puncte:


Construim matricea ³i facem sume parµiale pe aceasta:
suma_partialaij  = suma numerelor din dreptunghiul determinat de colµul stânga-jos 1, 1
³i dreapta-sus i, j . Aceasta se poate calcula: suma_partialaij  suma_partialai  1j  
suma_partialaij  1  suma_partialai  1j  1  valoare_matriceij 
Pentru ecare query putem folosi aceste sume parµiale pentru a r spunde în O 1.
2
Complexitate: O N  Q

Soluµie pentru 47 de puncte:


Parcurgem ecare coloan  din intervalul submatricei cerut  în query. Dac  în lµimea este prea
mica ³i se a  sub submatrice, suma pe coloana respectiv  este 0. Daca în lµimea este prea mare
³i cuprinde întreaga submatrice, ecare element din acea coloan  este 1 (deci D  B  1). Dac 
în lµimea se a  undeva în submatrice (în interior), adun m la r spuns doar intervalul de linii cu
valoarea 1 (heightcoloana  B  1).
Complexitate: O Q ˜ N 

Soluµie pentru 100 de puncte:


Analizând soluµia în O Q ˜ N  observ m c  avem 3 tipuri de în lµimi:
a în lµimi care se a  sub matricea din query: acestea contribuie cu 0 la sum 
a în lµimi care acoper  complet matricea din query: acestea contribuie complet pe intervalul
B, D 
a în lµimi care contribuie parµial.
Din moment ce inalµimile sunt date în ordine cresc toare, putem determina cu ajutorul a
dou  c ut ri binare, intervalul inalµimilor mici care nu contribuie cu nimic, intervalul înalµimilor
mari la care ne intereseaz  doar câte sunt ³i adun m D  B  1 ˜ cate_sunt, respectiv intervalul
înalµimilor medii. R mâne de v zut cum calcul m suma cu care contribuie înalµimile intervalului
parµial. Din moment ce toate aceste în lµimi se a  în intervalul B, D, observ m c  r spunsul
este suma heighti  B  1, pentru ecare i din acest interval. Aceast  sum  se poate scrie ³i ca
suma heighti  B  1 ˜ nr, unde nr este num rul elementelor din interval. suma heighti
se poate calcula cu ajutorul unor sume parµiale pe vectorul de in lµimi.
Complexitate: O N LogN 

3.3.2 Cod surs 

Listing 3.3.1: bruteQN2.cpp


1 #include<stdio.h>
2 #include<algorithm>
3
4 using namespace std;
5
6 #define NMAX 105
7 #define x first
8 #define y second
9
10 int n, m, v[NMAX], matrix[NMAX][NMAX], q;
11
12 pair<int,int> downL, topR;
13
14 int main ()
15 {
16
17 freopen("tnia.in","r",stdin);
18 freopen("tnia.out","w",stdout);
19
20 scanf("%d%d",&n,&m);
21 for(int i = 1; i <= n; i++)
22 {
23 scanf("%d",&v[i]);
24 for(int j = 1; j <= v[i]; j++)
25 matrix[i][j] = 1;
CAPITOLUL 3. OJI 2018 74

26 }
27
28 scanf("%d",&q);
29 for(int i = 1; i <= q; i++)
30 {
31 scanf("%d%d%d%d",&downL.x,&downL.y,&topR.x,&topR.y);
32
33 long long answer = 0;
34 for(int line = downL.x; line <= topR.x; line++)
35 for(int colum = downL.y; colum <= topR.y; colum++)
36 answer += matrix[line][colum];
37
38 printf("%d\n", answer);
39 }
40
41 return 0;
42 }

Listing 3.3.2: bruteN2+Q.cpp


1 // O(Q + N ^ 2)
2
3 #include<stdio.h>
4 #include<algorithm>
5
6 using namespace std;
7
8 #define NMAX 3005
9 #define x first
10 #define y second
11
12 int n, m, height[NMAX], matrix[NMAX][NMAX];
13 int partial_sums[NMAX][NMAX], q;
14
15 pair<int,int> downL, topR;
16
17 int main ()
18 {
19
20 freopen("tnia.in","r",stdin);
21 freopen("tnia.out","w",stdout);
22
23 scanf("%d%d",&n,&m);
24 for(int i = 1; i <= n; i++)
25 {
26 scanf("%d",&height[i]);
27 for(int j = 1; j <= height[i]; j++)
28 {
29 matrix[i][j] = 1;
30 }
31 }
32
33 for(int i = 1; i <= n; i++)
34 for(int j = 1; j <= m; j++)
35 {
36 partial_sums[i][j] = partial_sums[i - 1][j] +
37 partial_sums[i][j - 1] -
38 partial_sums[i - 1][j - 1] +
39 matrix[i][j];
40 }
41
42 scanf("%d",&q);
43 for(int i = 1; i <= q; i++)
44 {
45 scanf("%d%d%d%d", &downL.x, &downL.y, &topR.x, &topR.y);
46
47 printf("%d\n", partial_sums[topR.x][topR.y] -
48 partial_sums[downL.x - 1][topR.y] -
49 partial_sums[topR.x][downL.y - 1] +
50 partial_sums[downL.x - 1][downL.y - 1]);
51 }
52
53 return 0;
54 }
CAPITOLUL 3. OJI 2018 75

Listing 3.3.3: bruteQN.cpp


1 // O(N * Q)
2 #include<stdio.h>
3 #include<algorithm>
4
5 using namespace std;
6
7 #define NMAX 100005
8 #define x first
9 #define y second
10
11 int n, m, height[NMAX], q;
12 pair<int,int> downL, topR;
13
14 int main ()
15 {
16
17 freopen("tnia.in","r",stdin);
18 freopen("tnia.out","w",stdout);
19
20 scanf("%d%d",&n,&m);
21 for(int i = 1; i <= n; i++)
22 scanf("%d",&height[i]);
23
24 scanf("%d", &q);
25 for(int i = 1; i <= q; i++)
26 {
27 scanf("%d%d%d%d", &downL.x, &downL.y, &topR.x, &topR.y);
28 long long answer = 0;
29
30 for(int j = downL.x; j <= topR.x; j++)
31 if(height[j] >= topR.y)
32 answer += topR.y - downL.y + 1;
33 else if(height[j] >= downL.y)
34 answer += height[j] - downL.y + 1;
35
36 printf("%lld\n", answer);
37 }
38
39 return 0;
40 }

Listing 3.3.4: sursa-dani-NlogN.cpp


1 // O(N Log N)
2
3 #include<stdio.h>
4 #include<algorithm>
5 using namespace std;
6
7 #define x first
8 #define y second
9 #define NMAX 100005
10
11 int height[NMAX], n, m, q;
12 long long partial_sums[NMAX];
13
14 pair<int, int> downL, topR;
15
16 inline int binarySearch(int left, int right, int value)
17 {
18 int mid, answer = left - 1;
19
20 while(left <= right)
21 {
22 mid = (left + right) / 2;
23 if(height[mid] < value)
24 {
25 answer = mid;
26 left = mid + 1;
27 }
28 else
29 right = mid - 1;
30 }
CAPITOLUL 3. OJI 2018 76

31 return answer;
32 }
33
34 int main ()
35 {
36
37 freopen("tnia.in","r",stdin);
38 freopen("tnia.out","w",stdout);
39
40 scanf("%d%d",&n,&m);
41 for(int i = 1; i <= n; i++)
42 scanf("%d",&height[i]);
43
44 for(int i = 1; i <= n; i++)
45 partial_sums[i] = partial_sums[i - 1] + height[i];
46
47 scanf("%d",&q);
48 for(int i = 1; i <= q; i++)
49 {
50 scanf("%d%d%d%d", &downL.x, &downL.y, &topR.x, &topR.y);
51
52 int poz1 = binarySearch(downL.x, topR.x, downL.y);
53 int poz2 = binarySearch(downL.x, topR.x, topR.y);
54
55 // printf("am obtinut %d %d\n", poz1, poz2);
56
57 printf("%lld\n", partial_sums[poz2] -
58 partial_sums[poz1] -
59 ((long long)poz2 - poz1) * (downL.y - 1) +
60 ((long long)topR.x - poz2) * (topR.y - downL.y + 1));
61 }
62
63 return 0;
64 }

3.3.3 *Rezolvare detaliat 


Capitolul 4

OJI 2017

4.1 Ace
Problema 1 - Ace 100 de puncte
Pe o zon  în form  de dreptunghi cu laturile de lungimi N ³i M se g sesc N  M p trate de
latur  1. În centrul ec rui p trat se g se³te înpt câte un ac de grosime neglijabil . Fiecare
ac este descris de în lµimea sa. Aceast  zon  se poate reprezenta ca un tablou bidimensional de
dimensiuni N ³i M , iar ecare element din matrice reprezint  în lµimea (num r natural nenul)
ec rui ac. În centrul p tratului N, M  exist  o camer  de luat vederi de ultim  generaµie,
o
mobil , care se poate roti cu 360 în orice plan, situat  la nivelul solului. Dimensiunile camerei
sunt neglijabile. De exemplu, dac  avem zona sub forma:

Figura 4.1: Ace - Fig1 & Fig2

Pentru direcµia N , camera va vedea acul de coordonatele 3, 4 - în totalitate, iar acul 2, 4


se va vedea doar parµial . Acul 1, 4 nu se vede pentru c  este acoperit total de 2, 4.
În direcµia V , camera va vedea doar acul 4, 3, deoarece 4, 2 ³i 4, 1 sunt acoperite total de
4, 3.
Pentru celelalte direcµii se vor vedea parµial sau în totalitate acele 3, 3, 3, 2, 3, 1, 2, 3,
1, 3, 2, 2, 2, 1, 1, 2. Acul 1, 1 nu se vede din cauza acului 2, 2, care îl acoper  total.
Acul 2, 2 se vede doar parµial, pentru c  o parte din el este acoperit de acul 3, 3.

Cerinµe
1. Câte ace vede camera de luat vederi dac  se poate roti în plan vertical, doar în direcµiile N
³i V ?
2. Câte ace vede camera de luat vederi dac  se poate roti în orice plan ³i în orice direcµii?

Date de intrare
Fi³ierul de intrare ace.in conµine pe prima linie num rul P care poate  1 sau 2, pentru prima,
respectiv a doua cerinµ . Pe a doua linie se g sesc numerele N , M reprezentând dimensiunile
tabloului, apoi pe urm toarele N linii câte M numere naturale, desp rµite prin câte un spaµiu,
reprezentând în lµimile acelor.

77
CAPITOLUL 4. OJI 2017 78

Date de ie³ire
Fi³ierul de ie³ire ace.out va conµine pe prima linie num rul de ace v zute pentru cerinµ 
indicat  de valoarea num rului P .

Restricµii ³i preciz ri
a2 & N & 1000
a2 & M & 1000
a Elementele matricei sunt numere naturale nenule mai mici decât 1000, cu excepµia num rului
de pe linia N ³i coloana M care este 0.
a Pentru rezolvarea corect  a cerinµei 1 se acord  20 puncte, pentru rezolvarea corect  a cerinµei
2 se acord  70 de puncte, iar din ociu se acord  10 de puncte.
a Pentru cerinµa 2 exist  teste în valoare de 20 puncte cu N, M & 50.
a Pentru cerinµa 2 exist  teste în valoare de 45 puncte cu N, M & 100.

Exemple
ace.in ace.out Explicaµii
1 3 Pentru cerinµa 1 avem direcµiile N ³i V:
4 4 Pentru direcµia N, camera va vedea acul de coordonatele (3,4) -
8 5 4 7 în totalitate, iar acul (2,4) se va vedea doar parµial. Acul (1,4)
2 7 4 6 nu se va vedea pentru c  este acoperit total de (2,4).
5 5 3 2 în direcµia V, camera va vedea doar acul (4,3), deoarece acele
6 6 3 0 (4,2) ³i (4,1) sunt acoperite total de acul (4,3).
2 11 Pentru cerinµa 2 camera va vedea cele 3 ace din direcµiile N
4 4 ³i V(vezi mai sus) ³i 8 pentru celelalte direcµii se vor vedea
8 5 4 7 parµial sau în totalitate acele (3,3), (3,2), (3,1), (2,3), (1,3),
2 7 4 6 (2,2), (2,1),(1,2). Acul (1,1) nu se vede din cauza celui de pe
5 5 3 2 (2,2) care il acoper  total. Acul (2,2) se vede doar parµial, pentru
6 6 3 0 c  o parte din el este acoperit de acul (3,3).
2 8 Pentru cerinµa 2 camera va vedea în N (3,3), (2,3), în V va
4 3 vedea (4,2). în celelalte direcµii camera va vedea parµial sau în
5 4 7 totalitate acele (3,2), (3,1), (2,2), (1,2), (1,1).
6 4 6
5 3 2
6 3 0

Timp maxim de executare/test: 1.0 secunde


Memorie: total 32 MB
Dimensiune maxim  a sursei: 10 KB

4.1.1 Indicaµii de rezolvare


prof. Octavian Dumitra³cu - Colegiul Naµional Dinicu Golescu, Câmpulung

Soluµia se bazeaz  pe câteva observaµii matematice.


Dac  suntem în punctul N ³i M va vedea acul de coordonate x ³i y daca cmmdc N  x, M  y 
1 - în acest caz vede acul în totalitate altfel poate vedea varful acului x, y dac  pe direcµia
determinat  de cele dou  puncte nu se gaseste un ac "prea înalt" pentru cele doua puncte.
Pentru acest caz se veric  cu asem narea triunghiurilor o relaµie care determin  posibilitatea
de a  v zut sau nu.
Pentru o abordare brut  a acestor idei nu se obµine punctaj maxim.
Punctajul maxim se obµine parcurgând matricea de la punctul N , M ³i mergând dup  un
³ablon l, c (adica N, M  À
N  l, M  c ÀN  2l, M  2c À..., etc) ³i menµinând la
ecare moment o înalµime maxim  care determin  posibilitatea vizualiz rii sau nu.
Soluµia optim  are complexitate O N ˜ M .

4.1.2 Cod surs 


CAPITOLUL 4. OJI 2017 79

Listing 4.1.1: ace_LS.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <stdio.h>
4
5 using namespace std;
6
7 /*
8 ifstream in("ace.in");
9 ofstream out("ace.out);
10 */
11 FILE *in = fopen("ace.in","r");
12 FILE *out = fopen("ace.out","w");
13
14 int p,n,m,a[1001][1001],i,j,viz[1001][1001];
15 //viz[i][j] = 1 daca acul a mai fost vazut de camera
16
17 int db;
18 long long x1, y1, x2, y2;
19
20 bool directie(long long x1, long long y1, long long x2, long long y2)
21 {//produs incrucisat
22 return x1 * y2 - x2 * y1 > 0;
23 }
24
25 int main()
26 {
27 //in >> p >> n >> m;
28 fscanf(in,"%d%d%d",&p,&n,&m);
29
30 for(i = 1; i <= n; i++)
31 for(j = 1; j <= m; j++)
32 fscanf(in,"%d",&a[i][j]);
33 //in >> a[i][j];
34
35 //nord
36 db = 1;//vede primul ac
37 x1 = 1;
38 y1 = a[n-1][m];//coordonatele varfului primului ac
39
40 //fprintf(out, "%d %d\n",n-1,m);
41 for(i = n - 2; i >= 1; i--)
42 if(directie(x1,y1,n - i, a[i][m]))
43 {//daca produsul incrucisat > 0 vede acul
44 db = db + 1;
45 x1 = n - i; y1 = a[i][m];
46 //fprintf(out, "%d %d\n",i,m);
47 }
48
49 //vest
50 db = db + 1;//vede primul ac
51 x1 = 1; y1 = a[n][m - 1];//coordonatele varfului primului ac spre V
52 //fprintf(out, "%d %d\n",n,m-1);
53 for(i = m - 2; i >= 1; i--)
54 {
55 if(directie(x1, y1, m - i, a[n][i]))
56 {
57 db = db + 1;
58 x1 = m - i; y1 = a[n][i];
59 //fprintf(out, "%d %d\n",n,i);
60 }
61 }
62
63 if(p == 1)
64 fprintf(out,"%d", db);//out << db;
65 else
66 {
67 n = n - 1;
68 m = m - 1;//elimin ultima linie si coloana, deoarece am calculat deja
69
70 //inversez matricea, pt a calcula mai usor coordonatele
71 for(i = 1; i <= n / 2; i++)
72 for(j = 1; j <= m ;j++)
73 swap(a[i][j], a[n - i + 1][j]);
74
CAPITOLUL 4. OJI 2017 80

75 for(j = 1; j <= m / 2; j++)


76 for(i = 1; i <= n; i++)
77 swap(a[i][j],a[i][m - j + 1]);
78
79 for(i = 1; i <= n; i++)
80 {
81 for(j = 1; j <= m; j++)
82 {
83 x1 = 0;
84 y1 = 0;
85 for(int d=0; i+d*i <= n and j+d*j <= m; d++)
86 {//toate acele care sunt pe directia (0,0) -> (i,j)
87 if(viz[i + i * d][j + j * d] == 0)
88 {//daca nu a mai fost vazut dintr-o alta directie
89 if((x1 == 0 and y1 == 0) or
90 directie(x1, y1, j*d+j, a[i+i*d][j*d+j]))
91 {
92 db = db + 1;
93 x1 = j + d*j; y1 = a[i+d*i][j+d*j];
94 viz[i+d*i][j+d*j] = 1;
95 }
96 }
97 }
98
99 for(int d = 0; i + d*i <= n and j + d * j <= m; d++)
100 {
101 viz[i + i * d][j + j * d] = 1;//unghiul e deja vizitat
102 //fprintf(out, "(%d, %d) ",i + i * d, j + j * d);
103 }
104 }
105 }
106
107 fprintf(out,"%d", db);//out << db;
108 }
109
110 return 0;
111 }

Listing 4.1.2: ace_MirelaM.cpp


1 #include<cstdio>
2 #include<fstream>
3
4 using namespace std;
5
6 FILE*fin=fopen("ace.in","r");
7 ofstream fout("ace.out");
8
9 int a[1002][1002],n,m,ct,xmax,ymax,k,rez_a;
10
11 void mai_mare(int x, int y)
12 {
13 //determin tg unghiului pt a determina unghiul maxim
14 if(x*ymax<y*xmax)
15 {
16 xmax=x;
17 ymax=y;
18 k++;//numarul de ace vizibile de camera
19 }
20 }
21
22 int rezolva_1()//verificat ok
23 {
24 //NORD
25 int i,j;
26 k=2;
27 xmax=1;
28 ymax=a[n-1][m];
29 for(i=n-2,j=2;i>=1;i--,j++)
30 mai_mare(j,a[i][m]);
31
32 //VEST
33 xmax=1;
34 ymax=a[n][m-1];
35 for(i=m-2,j=2;i>=1;i--,j++)
CAPITOLUL 4. OJI 2017 81

36 mai_mare(j,a[n][i]);
37
38 return k;
39 }
40
41 void rezolva_2()
42 {
43 //parcurg matricea din coltul dreapta_jos spre 1,1
44 // si merg pe "diagonale"
45 int i,j,difx,dify,l,c;
46 for(i=n-1;i>=1;i--)
47 for(j=m-1;j>=1;j--)
48 if(a[i][j]!=-1)
49 {
50 xmax=m-j;
51 ymax=a[i][j];
52 k++;
53 difx=n-i;
54 dify=m-j;
55 a[i][j]=-1;
56 for(l=i-difx,c=j-dify;l>=1&&c>=1;l-=difx,c-=dify)
57 if(a[l][c]!=-1)
58 {
59 mai_mare(m-c,a[l][c]);
60 a[l][c]=-1;
61 }
62 }
63
64 fout<<k<<’\n’;
65 }
66
67 void citire()
68 {
69 int i,j;
70 fscanf(fin,"%d",&ct);
71 fscanf(fin,"%d",&n);
72 fscanf(fin,"%d",&m);
73
74 for(i=1;i<=n;i++)
75 for(j=1;j<=m;j++)
76 fscanf(fin,"%d",&a[i][j]);
77
78 rez_a=rezolva_1();
79 if(ct==1)
80 {
81 fout<<rez_a<<’\n’;
82 return;
83 }
84
85 rezolva_2();
86 }
87
88 int main()
89 {
90 citire();
91 return 0;
92 }

Listing 4.1.3: ace_priv.cpp


1 #include<fstream>
2 #include<iostream>
3
4 using namespace std;
5
6 ifstream fin("ace.in");
7 ofstream fout("ace.out");
8
9 int P,N,M,a[1002][1002];
10 char b[1002][1002];
11
12 int main()
13 {
14 int i,j,r,i1,j1,i2,j2,ii,jj,l1,k1;
15 fin>>P>>N>>M;
CAPITOLUL 4. OJI 2017 82

16
17 for(i=N;i>=1;i--)
18 {
19 for(j=M;j>=1;j--)
20 {
21 fin>>a[i][j];
22 }
23 }
24
25 if(P==1)
26 {
27 r=1;
28 i=2;
29 for(j=i+1;j<=M;j++)
30 {
31 if(a[1][j]*(i-1)>a[1][i]*(j-1))
32 {
33 r++;
34 i=j;
35 }
36 }
37
38 r++;
39 j=2;
40 for(i=j+1;i<=N;i++)
41 {
42 if(a[i][1]*(j-1)>a[j][1]*(i-1))
43 {
44 r++;
45 j=i;
46 }
47 }
48
49 fout<<r;
50 }
51
52 if(P==2)
53 {
54 r=0;
55 for(i=1;i<=N;i++)
56 {
57 for(j=1;j<=M;j++)
58 {
59 if(i>1 || j>1)
60 {
61 if(b[i][j]==0)
62 {
63 b[i][j]=1; r++;
64 i1=i;j1=j;
65 ii=i;jj=j;
66 l1=i1-1;k1=j1-1;
67 while(ii+l1<=N && jj+k1<=M)
68 {
69 i2=ii+l1; j2=jj+k1;
70 long long v1=(long long)a[i2][j2]*a[i2][j2]*
71 ((i1-1)*(i1-1)+(j1-1)*(j1-1));
72 long long v2=(long long)a[i1][j1]*a[i1][j1]*
73 ((i2-1)*(i2-1)+(j2-1)*(j2-1));
74 if(v1>v2)
75 {
76 r++;
77 i1=i2;j1=j2;
78 b[i2][j2]=1;
79 }
80 else
81 {
82 b[i2][j2]=2;
83 }
84
85 ii=i2;jj=j2;
86 }
87 }
88 }
89 }
90 }
91
CAPITOLUL 4. OJI 2017 83

92 fout<<r;
93 }
94
95 fout.close();
96 return 0;
97 }

Listing 4.1.4: ace_prmc.cpp


1 #include <vector>
2 #include <fstream>
3 #include <algorithm>
4
5 using namespace std;
6
7 bool isVisible(vector<vector<int>> &A, int p, int q, int i, int j)
8 {
9 int M = A.size(), N = A[0].size();
10 int H = abs(A[p][q]);
11 int h = abs(A[i][j]);
12 int D = max(M-1-p, N-1-q);
13 int d = max(M-1-i, N-1-j);
14 return (D == 0 || H*d < D*h);
15 }
16
17 int countOnDirectionAndMark(vector<vector<int>> &A, int di, int dj)
18 {
19 int M = A.size(), N = A[0].size();
20 int cnt = 0, highestI = M-1, highestJ = N-1;
21
22 for (int i = M-1-di, j = N-1-dj;
23 i >= 0 && j >= 0;
24 A[i][j] *= -1, i -= di, j -= dj)
25 if (isVisible(A, highestI, highestJ, i, j))
26 {
27 cnt++; highestI = i; highestJ = j;
28 }
29
30 return cnt;
31 }
32
33 int main()
34 {
35 ifstream f("ace.in");
36 ofstream g("ace.out");
37 int T, M, N; f >> T >> M >> N;
38
39 vector<vector<int>> A(M, vector<int>(N));
40
41 for (int i = 0; i < M; i++)
42 for (int j = 0; j < N; j++)
43 f >> A[i][j];
44
45 if (T == 1)
46 {
47 g << countOnDirectionAndMark(A, 1, 0) +
48 countOnDirectionAndMark(A, 0, 1);
49 }
50 else
51 {
52 int ans = 0;
53 for (int i = M-1; i >= 0; i--)
54 for (int j = N-1; j >= 0; j--)
55 if (A[i][j] >= 0 && (i != M-1 || j != N-1))
56 ans += countOnDirectionAndMark(A, M-1-i, N-1-j);
57 g << ans;
58 }
59
60 return 0;
61 }

Listing 4.1.5: ace_tavi.cpp


1 //sursa problema ace - prof Tavi Dumitrascu
CAPITOLUL 4. OJI 2017 84

2 #include <bits/stdc++.h>
3
4 using namespace std;
5 int pp, N, M, i, j, x, y, l, c, h, hmax, p, q, nr, vede, pozl, pozc, min1;
6 int a[1005][1005], b[1005][1005];
7
8 int main()
9 {
10 freopen("ace.in", "r", stdin);
11 freopen("ace.out", "w", stdout);
12
13 scanf("%d\n", &pp);
14 scanf("%d%d\n", &N, &M );
15
16 min1=100004;
17 x = N;
18 y = M;
19 for ( i = 1; i<= N; i++)
20 for ( j =1; j<=M; j++)
21 scanf("%d",&a[i][j]);
22
23 vede = 0;
24 nr=0;
25 hmax = 0;
26 for (i = 1;i < x; i++)
27 if (nr == 0)
28 {
29 hmax = a[x-i][M];
30 b[x-i][M] = 2;
31 nr = 1;
32 pozl = i;
33 vede++;
34 }
35 else
36 {
37 if (a[x-i][M] * pozl > i * hmax)
38 {
39 pozl = i;
40 hmax = a[x - i][M];
41 b[x-i][M] = 2;
42 vede++;
43 }
44 }
45
46 nr=0;
47 hmax = 0;
48 for (i = 1;i < y; i++)
49 if (nr == 0)
50 {
51 hmax = a[N][y - i];
52 nr = 1;
53 pozl = i;
54 vede++;
55 }
56 else
57 {
58 if (a[N][y-i] * pozl > i * hmax)
59 {
60 pozl = i;
61 hmax = a[N][y - i];
62 vede++;
63 }
64 }
65
66 if (pp == 1)
67 {
68 printf("%d\n",vede);
69 return 0;
70 }
71
72 for (l=1; l<x; l++)
73 for (c=1; c<y; c++)
74 {
75 h = 0;
76 p = x;
77 q = y;
CAPITOLUL 4. OJI 2017 85

78 nr = 0;
79 for (i = 1; i*l < x&& i*c <y ; i++)
80 {
81 if (b[p-i*l][q-i*c]==0)
82 {
83 b[p-i*l][q-i*c]=1;
84 if (nr==0)
85 {
86 hmax = a[p-i*l][q-i*c];
87 pozl = i;
88 vede++;
89 b[p-i*l][q-i*c]=2;
90 nr++;
91 }
92 else
93 if ((a[p-i*l][q-i*c]-h) * (pozl) > i * (hmax -h))
94 {
95 pozl = i;
96 hmax = a[p - i*l][q - i*c];
97 vede++;
98 b[p-i*l][q-i*c]=2;
99 }
100 }
101 }
102 }
103
104 printf("%d\n",vede);
105 return 0;
106 }

4.1.3 *Rezolvare detaliat 

4.2 Admitere
Problema 2 - Admitere 100 de puncte
S  ne imagin m faptul c  la un anumit liceu exist  doar dou  clase per generaµie: una de Real
³i una de Uman. În prezent au loc înscrierile pentru clasa a IX-a. Cele dou  clase au ecare câte
M locuri disponibile, atât la Real, cât ³i la Uman. Dac  lista de elevi înscri³i la o anumit  clas 
conµine mai mult de M elevi, vor  admi³i acei M elevi care au notele cele mai mari. Ambele clase
au deja M elevi înscri³i, iar pentru ecare se ³tie nota cu care a fost înscris la clasa respectiv .
Mai exist  îns  N elevi, singurii înc  neînscri³i, care sunt privilegiaµi în acest proces (indc 
au terminat gimnaziul la acest liceu). Privilegiul lor const  în urm torul fapt: ei se pot înscrie
acum, dup  ce înscrierile publice au fost încheiate, ³i se cunosc notele de înscriere la ambele clase.
Fiecare din cei N elevi are câte dou  note: nota cu care ar  înscris la Real ³i nota cu care ar
 înscris la Uman (acestea pot  diferite, deoarece examenele de admitere de la cele dou  clase
difer ). Fiecare din cei N elevi va alege s  se înscrie în maxim o clas . Ei î³i vor coordona alegerile
astfel încât s  maximizeze num rul de elevi admi³i. Deoarece calculele devin destul de complicate,
ace³tia s-ar putea folosi de ajutorul vostru. Ei doresc r spunsul la urm toarele dou  întreb ri:

Cerinµe
(1) Care este num rul maxim de elevi privilegiaµi care pot  admi³i dac  se pune restricµia
suplimentar  ca toµi elevii privilegiaµi admi³i s  e admi³i la aceea³i clas ?
(2) Care este num rul maxim de elevi privilegiaµi care pot  admi³i dac  ace³tia se pot înscrie
la clase diferite?

Date de intrare
Fi³ierul de intrare admitere.in conµine pe primul rând o valoare egal  cu 1 sau 2, reprezentând
cerinµa ce urmeaz  a  rezolvat . Urm toarea linie conµine cele dou  numere N ³i M . Pe al treilea
rând se a  M numere, separate prin câte un spaµiu, reprezentând notele cu care au fost înscri³i
elevii care formeaz  momentan clasa de Real. Pe al patrulea rând se a  M numere, separate
prin câte un spaµiu, reprezentând notele cu care au fost înscri³i elevii care formeaz  momentan
clasa de Uman. Urm toarele N linii vor conµine câte o pereche de numere Ri, U i, separate
CAPITOLUL 4. OJI 2017 86

prin câte un spaµiu, reprezentând nota cu care al i-lea elev privilegiat s-ar înscrie la clasa de Real,
respectiv la clasa de Uman.

Date de ie³ire
Fi³ierul de ie³ire admitere.out va conµine pe prima linie valoarea M AX : num rul maxim de
¬ ¬ ¬ ¬ ¬ ¬
elevi privilegiaµi admi³i. A doua linie va conµine un ³ir de N caractere din mulµimea r R , U , X x,
care va descrie scenariul optim. Dac  al i-lea elev va  înscris la Real, al i-lea caracter va  egal
¬ ¬ ¬ ¬
cu R . Dac  al i-lea elev va  înscris la Uman, al i-lea caracter va  egal cu U . Dac  acesta nu
¬ ¬
va  înscris nic ieri, al i-lea caracter va  egal cu X .
Deoarece elevii nu vor s  depun  efort inutil, un elev privilegiat care nu va  admis în scenariul
optim nu se va înscrie la nicio clas . Cu alte cuvinte, pentru ca scenariul descris s  e considerat
¬ ¬
corect este necesar ca exact M AX caractere din ³ir s  e diferite de X .

Restricµii ³i preciz ri
a 1 & N, M & 2000
a Teste în valoare total  de 25 de puncte vor solicita rezolvarea cerinµei (1), iar restul de 65
de puncte vor solicita rezolvarea cerinµei 2. Din ociu sunt acordate 10 puncte.
a Pentru cerinµa 2, teste în valoare total  de 45 de puncte vor avea 1 & N, M & 150
a Toate cele N  M note pentru clasa de Real sunt distincte dou  câte dou . Acela³i lucru
este valabil ³i în cazul notelor pentru clasa de Uman.
a Toate notele sunt numere naturale din intervalul 1, 4000.
a Notele elevilor deja înscri³i de la clasa de Real, respectiv Uman vor  date în ordine cresc -
toare.
a în cazul în care exist  mai multe soluµii corecte, este acceptat  oricare dintre acestea.

Exemple
admitere.in admitere.out Explicaµii
1 1 Nu este posibil ca ambii elevi s  e admi³i la aceea³i clas .
23 XR Exist  mai multe soluµii în care un singur elev este admis:
246 XR, XU, RX. Oricare din acestea este corect .
678
35
12 14
2 2 Deoarece acum rezolv m cerinµa (2), ne este permis s 
23 RU înscriem elevii la clase diferite. Exist  o soluµie în care
246 ambii elevi sunt admi³i, iar aceasta este unic : cea în care
678 elevul 1 este înscris la Real (el nu putea  admis la Uman
35 indiferent de decizia celui de-al doilea elev), iar cel de-al
12 14 doilea elev este înscris la Uman.

Timp maxim de executare/test: 1.5 secunde


Memorie: total 32 MB
Dimensiune maxim  a sursei: 10 KB

4.2.1 Indicaµii de rezolvare


Mihai Calancea, student Universitatea Bucure³ti
Cerinµa 1
Vom încerca s  plas m toµi elevii privilegiaµi la clasa de Real. Vom sorta ³irul celor N  M note
de la Real ³i vom num ra câµi elevi privilegiaµi se a  printre primii M elevi în ordinea notelor.
S  numim acest num r K . Suntem tentaµi s  consider m aceast  valoare ca ind optim , dar este
indicat s  investig m mai în detaliu aceast  ipotez . Din algoritmul descris rezult  c  ace³ti K
elevi admi³i sunt cei care au cele mai mari note dintre cei N . Este oare posibil ca p strând unii
din elevii de top înafara clasei, s  obµinem mai mulµi elevi admi³i în total?
R spunsul este nu: eliminând un elev dintre cei mai buni K , facem loc maxim unui alt elev
privilegiat neadmis pân  acum. Astfel, la ecare eliminare a unui elev deja admis, num rul total
de elevi admi³i poate doar s  r mân  egal cu cel precedent sau s  scad . Valoarea K este deci
num rul maxim de elevi pe care-l putem inscrie la clasa de Real.
CAPITOLUL 4. OJI 2017 87

Vom face acelasi lucru pentru clasa de Uman si vom pastra maximul dintre cele doua valori.

Cerinµa 2
În primul rând, s  not m c  prima cerinµ  trebuie luat  în calcul în soluµia celei de a doua.
Astfel, în continuare vom presupune c  exist  cel puµin un elev privilegiat admis în ambele clase.
La modul informal, dicultatea problemei const  în faptul c  avem de f cut multe alegeri,
iar acestea se inuenµeaz  reciproc. în asemenea situaµii poate  util s  ne restrângem opµiunile,
încercând s  rezolv m o problem  mai particular . S  analiz m, spre exemplu, cum putem rezolva
urm toarea variant  a problemei:
(1) Este posibil s -i admitem pe toµi cei N elevi privilegiaµi?
Este clar c  soluµia problemei originale trebuie s  trateze implicit sau explicit aceast  întrebare
(r psunsul pozitiv ar indica o soluµie clar optim ).
În ce fel este mai u³oar  aceast  problem ? în problema original  exista un factor de nesigu-
ranµ  în îns ³i decizia de a încerca s  admitem un anumit elev (f r  a decide ³i clasa la care am
face acest lucru). Poate c  în toate soluµiile optime respectivul elev ar  r mas înafara liceului.
în aceast  variant  trebuie s -i admitem pe toµi, iar acest lucru ne reduce din opµiuni.
Acum putem intui, în termeni informali, c  dicultatea în a-i admite pe toµi cei N elevi st  în
a-i admite pe cei mai slabi dintre ei. Ce ar însemna totu³i ca un elev s  e mai slab decât altul?
Putem argumenta credibil c  elevul cu notele (4, 4) este mai slab decât elevul cu notele (10, 10),
dar se pot compara elevii cu notele (4, 10), respectiv (10, 4)? Faptul c  elevii sunt bidimensionali
pare s  ne pun  dicult µi, a³a c  poate  o idee bun  s  restrângem din nou problema.
(2) Este posibil s -i admitem pe toµi cei N elevi privilegiaµi ³tiind c  ecare dintre ace³tia are
nota de înscriere de la Uman egal  cu cea de la Real?
Beneciul acestei constrângeri este c  acum avem un cel mai slab elev! Este clar c  indiferent
de clasa la care va  admis, el va  ultimul elev privilegiat admis la clasa respectiv . Vom încerca
pe rând s -l admitem la Real, respectiv Uman.
S  presupunem c  el va  admis la Real. Analizând cele M note care exist  deja la Real,
not m c  exist  P note mai mari decât a sa. Dac  P M , el nu poate  admis la Real indiferent
de alegerile celorlalµi privilegiaµi. Dac  P $ M , el poate  admis la Real ³i, mai important, putem
admite înc  M  P  1 elevi privilegiaµi la Real f r  a-l elimina pe elevul cel mai slab. S  numim
valoarea M  P  1 SPATIU_REAL.
Este optim s  aducem cât mai mulµi elevi la Real, respectând acest  limit  de SPATIU_REAL,
deoarece astfel u³ur m situaµia elevilor r ma³i care vor încerca s  e admi³i la Uman. întreba-
rea este acum pe care SPATIU_REAL dintre cei N  1 elevi este optim s -i admitem la Real?
Urm rind din nou s  u³ur m situaµia celor care vor r mâne la Uman, îi vom admite la Real pe
cei SPATIU_REAL cu cele mai mici note. Pentru a argumenta c  aceast  decizie cre³te ³ansele
elevilor care se vor înscrie la Uman ne amintim de la cerinµa 1 c  dac  exist  în general o soluµie
cu K elevi admi³i la o clas , exist  ³i una în care ace³ti K sunt cei cu cele mai mari note dintre
cei disponibili. în acela³i timp, nu risc m nimic prin a aduce elevi cu note mici la Real, deoarece
³tim c  am avut grij  s  r mân  admis acolo cel mai slab elev, fapt care ne asigur  c  orice alt
elev înscris la Real va  de-asemenea admis.
Dac  acest algoritm nu reu³e³te s  admit  toµi elevii, vom încerca s  admitem elevul cel mai
slab la clasa de Uman. Dac  nici în acest fel nu se g se³te soluµie, ea nu exist  (cel mai slab
elev trebuie s  e admis undeva, iar în ambele scenarii am luat decizii optime care s  faciliteze
admiterea celorlalµi).
Ce complexitate are acest algoritm? Detaliile variaz , dar exist  implement ri simple cu
complexitate O N ˜ M  N ˜ N . Este util  (dar nu strict necesar ) o sortare a celor N elevi,
care poate  f cut  în complexitate O N ˜ N . Este de-asemenea util s  ³tim pentru ecare elev
³i ecare clas  câte dintre cele M note deja înscrise sunt mai mari decât nota elevului respectiv.
Acest lucru se poate calcula u³or în O N ˜ M . Simularea algoritmului descris mai sus ³i vericarea
faptului c  toµi elevii sunt admi³i poate  f cut  combinând aceste dou  informaµii precalculate.
Astfel, avem o rezolvare satisf c toare ca timp de execuµie pentru subproblema (2). Vom
încerca acum s  folosim ideile de la (2) pentru a rezolva subproblema (1). Dicultatea este
bineînµeles c  nu mai avem neaparat un cel mai slab elev. Totu³i, vom avea înc  un ultim elev
privilegiat admis în clasa de Real. Cine va  acesta în soluµia optim ? Nu ³tim, dar putem
presupune, pe rând, despre ecare dintre cei N elevi c  va lua aceast  poziµie. Având elevul X
xat ca ind ultimul privilegiat admis la Real, suntem prezentati cu un scenariu similar celui din
(2). ³tim c  mai putem admite SPATIU_REAL(X ) (aceast  valoare variaz  în funcµie de X ) elevi
la clasa de Real f r  s -l elimin m pe X . Din nou, este de dorit s  admitem cât mai mulµi elevi
la Real (luând în calcul aceast  limit ). Trebuie îns  s  m mai atenµi în a analiza pe care din cei
N  1 elevi s -i admitem. în primul rând, ace³tia trebuie s  aib  nota de la Real strict mai mare
CAPITOLUL 4. OJI 2017 88

decât nota lui X , indc  altfel invalid m poziµia sa de ultim admis. Filtrând dup  acest criteriu,
are sens în continuare s  alegem elevii cu notele cele mai mici la Uman, din acela³i raµionament
de a cre³te ³ansele celor înscri³i la Uman de a  admi³i.
Care este complexitatea acestei rezolv ri? Principala schimbare faµ  de algoritmul pentru (2)
este c  vom varia ultimul elev de la real. Exist  N variante pe care le lu m în calcul. Astfel,
pentru a menµine o complexitate p tratic  este necesar s  calcul m în O N  componenµa optim 
a claselor, având un ultim elev xat. Acest lucru se poate face folosind acelea³i precalcul ri de la
(2).
Avem astfel o rezolvare pentru subproblema (1). Trecerea c tre soluµia problemei originale se
face acum foarte u³or. R mâne înc  adev rat c  în orice soluµie optim  vom avea un ultim elev
înscris la Real ³i c  putem presupune care este acesta. Mai mult, modalitatea de a alege clasa
pentru ceilalµi N  1 elevi având acest ultim elev xat este înc  optim . Se poate observa din
argumentele aduse în cadrul discuµiei subproblemei (1) c  dac  metoda e³ueaz  în a-i admite pe
toµi cei N  1 elevi r ma³i, reu³e³te totu³i s  admit  num rul de elevi maxim posibil.
R mân de soluµionat unele detalii de implementare necesare pentru ca algoritmul s  aib 
complexitate p tratic . Pentru indicii în acest sens puteµi consulta sursa ocial .
Este util acum s  analiz m procesul prin care am ajuns la soluµie. Observ m c  ideea critic 
a problemei este cea de a varia ultimul elev admis la o anumit  clas . Aceast  idee ne-a putut 
util  din dou  motive:
- Exist  un num r restrâns de opµiuni pe care le putem alege (N ), deci le putem încerca pe
toate, f r  a face vreo speculaµie riscant  în leg tur  cu cea optim .
- Odat  aleas  o opµiune, multe alte decizii ale algoritmului sunt forµate, iar cele r mase pot 
soluµionate u³or.
Pentru a ajunge la aceast  idee, am simplicat problema (impunându-ne constrângeri noi) pâna
la punctul în care exista un cel mai slab elev, iar acesta trebuia neaparat s  e admis. Acesta este,
bineînµeles, doar un r de gândire din multe posibile, dar autorul îl consider  educativ în vederea
abord rii problemelor de acest tip.

4.2.2 Cod surs 

Listing 4.2.1: mihai100.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 int main()
6 {
7 ifstream cin("admitere.in");
8 ofstream cout("admitere.out");
9
10 int tip, n, m; cin >> tip >> n >> m;
11
12 vector<vector<int>> fixedGrades(2, vector<int> (m, 0));
13 vector<vector<int>> currGrades(n, vector<int> (2, 0));
14
15 for(int i = 0; i < 2; ++i)
16 for(int j = 0; j < m; ++j)
17 cin >> fixedGrades[i][j];
18
19 for(int i = 0; i < n; ++i)
20 cin >> currGrades[i][0] >> currGrades[i][1];
21
22 vector<vector<int>> frontAlready(2, vector<int> (n, 0));
23 vector<vector<int>> p(2, vector<int> (n, 0));
24
25 for(int c = 0; c < 2; ++c)
26 {
27 for(int i = 0; i < n; ++i)
28 p[c][i] = i;
29
30 sort(p[c].begin(), p[c].end(), [&] (int a, int b) {
31 return currGrades[a][c] < currGrades[b][c];
32 });
33 }
34
CAPITOLUL 4. OJI 2017 89

35 for(int c = 0; c < 2; ++c)


36 for(int i = 0; i < n; ++i)
37 for(int j = 0; j < m; ++j)
38 if(fixedGrades[c][j] > currGrades[i][c])
39 frontAlready[c][i]++;
40
41 int bestAns = 0;
42 string bestConfig(n, ’X’);
43 string label = "RU";
44
45 auto realityCheck = [&] (string config) -> string {
46 for(int c = 0; c < 2; ++c)
47 {
48 int greaterFriends = 0;
49 for(int i = n - 1; i >= 0; --i)
50 {
51 if(config[p[c][i]] != label[c])
52 continue;
53 if(greaterFriends + frontAlready[c][p[c][i]] >= m)
54 config[p[c][i]] = ’X’;
55 greaterFriends++;
56 }
57 }
58
59 return config;
60 };
61
62 for(int c = 0; c < 2; ++c)
63 {
64 string config(n, label[c]);
65 config = realityCheck(config);
66
67 int temp = 0;
68 for(int i = 0; i < n; ++i)
69 if(config[i] != ’X’)
70 ++temp;
71
72 if(temp > bestAns)
73 {
74 bestAns = temp;
75 bestConfig = config;
76 }
77 }
78
79 if(tip == 1)
80 {
81 cout << bestAns << "\n";
82 cout << bestConfig << "\n";
83 return 0;
84 }
85
86 const int REAL = 0, UMAN = 1;
87
88 for(int minFirst = 0; minFirst < n; ++minFirst)
89 {
90 string config(n, ’X’);
91 config[minFirst] = label[REAL];
92
93 int space = m - 1;
94 for(int i = 0; i < m; ++i)
95 if(fixedGrades[REAL][i] > currGrades[minFirst][REAL])
96 space--;
97
98 for(int i = 0; i < n; ++i)
99 {
100 if(p[UMAN][i] == minFirst)
101 continue;
102 if(currGrades[p[UMAN][i]][REAL] > currGrades[minFirst][REAL] &&
103 space > 0)
104 {
105 config[p[UMAN][i]] = label[REAL];
106 space--;
107 }
108 else
109 {
110 config[p[UMAN][i]] = label[UMAN];
CAPITOLUL 4. OJI 2017 90

111 }
112 }
113
114 config = realityCheck(config);
115 int temp = 0;
116 for(int i = 0; i < n; ++i)
117 if(config[i] != ’X’)
118 temp++;
119
120 if(temp > bestAns)
121 {
122 bestAns = temp;
123 bestConfig = config;
124 }
125 }
126
127 cout << bestAns << "\n";
128 cout << bestConfig << "\n";
129 }

Listing 4.2.2: mihai100comments.cpp


1 /*
2 Complexitate O(N * N + N * M)
3 Sursa foloseste elemente de STL si C++11,
4 dar acestea nu sunt critice in rezolvarea problemei.
5 Pentru detalii despre anumite functii sau tipuri de date,
6 puteti consulta documentatia pe cplusplus.com.
7 */
8
9 #include <bits/stdc++.h>
10
11 using namespace std;
12
13 int main()
14 {
15 ifstream cin("admitere.in");
16 ofstream cout("admitere.out");
17
18 int tip, n, m; cin >> tip >> n >> m;
19
20 vector<vector<int>> fixedGrades(2, vector<int> (m, 0));
21 vector<vector<int>> currGrades(n, vector<int> (2, 0));
22
23 // 0 = REAL, 1 = UMAN
24
25 for(int i = 0; i < 2; ++i)
26 for(int j = 0; j < m; ++j)
27 cin >> fixedGrades[i][j]; // cele M note deja existente
28 // la fiecare clasa
29
30 for(int i = 0; i < n; ++i)
31 cin >> currGrades[i][0] >> currGrades[i][1]; // notele celor N elevi
32 // privilegiati
33
34 vector<vector<int>> frontAlready(2, vector<int> (n, 0));
35 vector<vector<int>> p(2, vector<int> (n, 0));
36
37 // Precalculam p[C][index] = care este al index-lea elev privilegiat
38 // in ordine
39 // crescatoare, daca ii sortam dupa nota pentru clasa C
40
41 for(int c = 0; c < 2; ++c)
42 {
43 for(int i = 0; i < n; ++i)
44 p[c][i] = i;
45
46 sort(p[c].begin(), p[c].end(), [&] (int a, int b) {
47 return currGrades[a][c] < currGrades[b][c];
48 });
49 }
50
51 // Precalculam frontAlready[C][E] = cate din cele M note deja existente
52 //la clasa C
53 // sunt mai mari decat nota elevului privilegiat cu numarul E la clasa C
CAPITOLUL 4. OJI 2017 91

54
55 for(int c = 0; c < 2; ++c)
56 for(int i = 0; i < n; ++i)
57 for(int j = 0; j < m; ++j)
58 if(fixedGrades[c][j] > currGrades[i][c])
59 frontAlready[c][i]++;
60
61 int bestAns = 0;
62 string bestConfig(n, ’X’);
63 string label = "RU";
64
65 // Functia realityCheck preia un string cu optiunile de inscriere
66 // ale celor N elevi
67 // si il "aduce la realitate", punand ’X’ in dreptul elevilor care
68 // nu sunt admisi
69 // la clasa aleasa. Complexitatea functiei este O(N).
70
71 auto realityCheck = [&] (string config) -> string {
72 for(int c = 0; c < 2; ++c)
73 {
74 int greaterFriends = 0;
75 for(int i = n - 1; i >= 0; --i)
76 { // parcurgem elevii privilegiati inscrisi la clasa c
77 // in ordine descrescatoare dupa nota
78 if(config[p[c][i]] != label[c])
79 continue;
80
81 // greaterFriends = cati elevi privilegiati inscrisi
82 // la clasa c au fost admisi deja
83 // frontAlready[..] = cati elevi din cei M initiali
84 // aveau note mai mari decat cel curent
85 // pozitia elevului curent este data de suma acestor
86 // doua valori + 1
87 // daca este >= m, elevul este respins
88
89 if(greaterFriends + frontAlready[c][p[c][i]] >= m)
90 config[p[c][i]] = ’X’;
91 greaterFriends++;
92 }
93 }
94 return config;
95 };
96
97 // Cerinta 1
98
99 for(int c = 0; c < 2; ++c)
100 {
101 string config(n, label[c]); // toti elevii se inscriu la clasa c
102 config = realityCheck(config); // config ne spune acum care elevi
103 // au ramas efectiv admisi la clasa c
104
105 int temp = 0;
106 for(int i = 0; i < n; ++i)
107 if(config[i] != ’X’)
108 ++temp;
109
110 if(temp > bestAns)
111 { // actualizam raspunsul cel mai bun
112 bestAns = temp;
113 bestConfig = config;
114 }
115 }
116
117 if(tip == 1)
118 {
119 cout << bestAns << "\n";
120 cout << bestConfig << "\n";
121 return 0;
122 }
123
124
125 //Cerinta 2
126
127 const int REAL = 0, UMAN = 1;
128
129 for(int minReal = 0; minReal < n; ++minReal)
CAPITOLUL 4. OJI 2017 92

130 { // fixam ultimul privilegiat admis la Real


131 string config(n, ’X’);
132 config[minReal] = label[REAL];
133
134 int space = m - 1;
135 for(int i = 0; i < m; ++i)
136 if(fixedGrades[REAL][i] > currGrades[minReal][REAL])
137 // mai putem pune maxim "space" privilegiati la Real
138 space--;
139
140 for(int i = 0; i < n; ++i)
141 { // parcurgem ceilalti N - 1 elevi in ordine crescatoare
142 // dupa nota de la UMAN
143 if(p[UMAN][i] == minReal)
144 continue;
145 if(currGrades[p[UMAN][i]][REAL] > currGrades[minReal][REAL] &&
146 space > 0)
147 { // daca elevul are nota de Real > minimul fixat
148 // si mai avem loc, il punem la real.
149 config[p[UMAN][i]] = label[REAL];
150 space--;
151 }
152 else
153 { // altfel se inscrie la uman
154 config[p[UMAN][i]] = label[UMAN];
155 }
156 }
157
158 config = realityCheck(config); // aducem configuratia la realitate
159
160 int temp = 0;
161 for(int i = 0; i < n; ++i)
162 if(config[i] != ’X’)
163 temp++;
164
165 if(temp > bestAns)
166 {
167 bestAns = temp;
168 bestConfig = config;
169 }
170 }
171
172 cout << bestAns << "\n";
173 cout << bestConfig << "\n";
174 }

Listing 4.2.3: priv100.cpp


1 ///O(N*(N+M))
2 #include<fstream>
3 #include<cstdlib>
4 #include<algorithm>
5 #include<iostream>
6
7 using namespace std;
8
9 ifstream fin("admitere.in");
10 ofstream fout("admitere.out");
11
12 int pozmin(int a[], int n, int v)
13 {
14 int p1=1, p2=n, mij;
15 while(p1<=p2)
16 {
17 mij=p1+(p2-p1)/2;
18 if(v>=a[mij])
19 p1=mij+1;
20 else
21 p2=mij-1;
22 }
23
24 return p2;
25 }
26
27 void citire_sort(int a[], int n)
CAPITOLUL 4. OJI 2017 93

28 {
29 for(int i=1;i<=n;i++)
30 fin>>a[i];
31
32 sort(a+1,a+n+1);
33 }
34
35 int V, M, N, u[4005], r[4005], sr[4005], su[4005],
36 pozu[4005], upoz[4005], pozr[4005], rpoz[4005], vr, vu;
37
38 char x[4005], y[4005];
39
40 int main()
41 {
42 int i,j,k,l,p,rez,aux,c,cr,cu;
43
44 fin>>V>>N>>M;
45
46 citire_sort(r,M);
47 citire_sort(u,M);
48
49 vr=2000000000;
50 vu=2000000000;
51 for(i=1;i<=N;i++)
52 {
53 fin>>sr[i]>>su[i];
54 vr=min(vr,sr[i]);
55 vu=min(vu,su[i]);
56 pozu[i]=i;
57 pozr[i]=i;
58 x[i]=’X’;
59 y[i]=’X’;
60 }
61
62 for(i=1;i<=N-1;i++)
63 for(j=i+1;j<=N;j++)
64 if(sr[pozr[i]]>sr[pozr[j]])
65 {
66 aux=pozr[i];
67 pozr[i]=pozr[j];
68 pozr[j]=aux;
69 }
70 ///for(i=1;i<=N;i++) rpoz[pozr[i]]=i;
71
72 for(i=1;i<=N-1;i++)
73 for(j=i+1;j<=N;j++)
74 if(su[pozu[i]]>su[pozu[j]])
75 {
76 aux=pozu[i];
77 pozu[i]=pozu[j];
78 pozu[j]=aux;
79 }
80
81 for(i=1;i<=N;i++)
82 upoz[pozu[i]]=i;
83
84 for(i=1;i<=N;i++)
85 y[i]=’X’;
86
87 i=N;
88 j=M;
89 k=0;
90 cr=0;
91 pozr[0]=0;
92 sr[0]=0;
93 r[0]=0;
94
95 for(k=M;k>=1;k--)
96 {
97 if(sr[pozr[i]]>r[j])
98 {
99 y[pozr[i]]=’R’;
100 cr++; i--;
101 }
102 else
103 {
CAPITOLUL 4. OJI 2017 94

104 j--;
105 }
106 }
107
108 rez=cr;
109
110 for(i=1;i<=N;i++)
111 x[i]=’X’;
112
113 i=N;
114 j=M;
115 k=0;
116 cu=0;
117 pozu[0]=0;
118 su[0]=0;
119 u[0]=0;
120 for(k=M;k>=1;k--)
121 {
122 if(su[pozu[i]]>u[j])
123 {
124 x[pozu[i]]=’U’;
125 cu++; i--;
126 }
127 else
128 {
129 j--;
130 }
131 }
132
133 if(cu>rez)
134 {
135 rez=cu;
136 for(i=1;i<=N;i++)y[i]=x[i];
137 }
138
139 if(V==1)
140 {
141 fout<<rez<<"\n";
142 fout<<y+1;
143 }
144
145 for(i=1;i<=N;i++)
146 {///O(N)
147 p=pozmin(u,M,su[i]);///log(M)
148 if(p>=1)
149 {
150 for(j=1;j<=N;j++)
151 x[j]=’X’;///O(N)
152
153 c=1;
154 x[i]=’U’;
155 for(j=1;j<=N;j++)///O(N)
156 if(upoz[pozr[j]]>upoz[i] && c<p)
157 {
158 x[pozr[j]]=’U’;
159 c++;
160 }
161
162 l=N;
163 j=M;
164 k=0;
165 pozr[0]=0;
166 sr[0]=0;
167 r[0]=0;
168 for(k=M;k>=1;k--)
169 {///O(M)
170 while(l>0 && x[pozr[l]]!=’X’)
171 l--;
172
173 if(sr[pozr[l]]>r[j])
174 {
175 x[pozr[l]]=’R’;
176 c++; l--;
177 }
178 else
179 {
CAPITOLUL 4. OJI 2017 95

180 j--;
181 }
182 }
183
184 if(c>rez)
185 {
186 rez=c;
187 for(j=1;j<=N;j++)
188 {
189 y[j]=x[j];
190 }
191 }
192 }
193 }
194
195 if(V==2)
196 {
197 fout<<rez<<"\n";
198 fout<<y+1;
199 }
200
201 fin.close();
202 fout.close();
203 return 0;
204 }

4.2.3 *Rezolvare detaliat 

4.3 Roboµi
Problema 3 - Roboµi 100 de puncte
“tefan a împlinit 15 ani. Fiind un pasionat membru al Clubului de Robotic , familia i-a d ruit
de ziua lui foarte mulµi roboµi, ecare dotat cu o arm  de o anumit  putere. El a a³ezat toµi roboµii
în jurul s u, pe circumferinµa unui cerc imaginar, în sensul acelor de ceasornic. Aceste dispozitive
inteligente pot comunica între ele, unindu-³i puterile armelor.

Cerinµe
Cunoscând num rul de roboµi, precum ³i puterea ec ruia, s  se scrie un program care deter-
min :
1. Dimensiunea celei mai lungi secvenµe de roboµi pentru care puterile armelor lor formeaz 
un ³ir strict cresc tor.
2. O aranjare a roboµilor pe cerc, astfel încât suma produselor de câte dou  puteri vecine s 
e maxim . Dac  exist  mai multe modalit µi de aranjare astfel încât s  se obµin  aceea³i sum 
maxim , se va determina cea minim  din punct de vedere lexicograc.

Date de intrare
Pe prima linie a ³ierului de intrare roboti.in se g se³te un num r natural v a c rui valoare
poate  doar 1 sau 2.
Pe a doua linie a ³ierului de intrare se g se³te un singur num r natural n reprezentând
num rul de roboµi.
Pe a treia linie a ³ierului de intrare se g sesc n numere naturale p1 , p2 , ..., pn , separate prin
câte un spaµiu, pi reprezentând puterea armei robotului i.

Date de ie³ire
Dac  valoarea lui v este 1, atunci ³ierul de ie³ire roboti.out va conµine pe prima linie un
singur num r natural reprezentând dimensiunea celei mai lungi secvenµe de roboµi pentru care
puterile armelor lor formeaz  un ³ir strict cresc tor.
Dac  valoarea lui v este 2, atunci ³ierul de ie³ire va conµine pe prima linie n numere naturale
separate prin câte un spaµiu, reprezentând puterile celor n roboµi a³ezaµi pe cerc astfel încât suma
produselor de câte dou  puteri vecine s  e maxim , iar a³ezarea s  e minim  din punct de
vedere lexicograc.
CAPITOLUL 4. OJI 2017 96

Restricµii ³i preciz ri
a 2 & n & 100000
a Pentru cerinµa 1, secvenµa de lungime maxim  se alege pe cerc în sensul acelor de ceasornic.
a Dac  avem dou  ³iruri de numere a1 , a2 , ..., an ³i b1 , b2 , ..., bn ³i exist  1 & k & n, cea mai
mic  poziµie, pentru care are loc a1 b1 , a2 b2 , ..., ak1 bk1 ³i ak $ bk , atunci spunem c 
³irul a este mai mic lexicograc decât ³irul b.
a Pentru rezolvarea corect  a cerinµei 1 se acord  30 puncte, pentru rezolvarea corect  a cerinµei
2 se acord  60 de puncte, iar din ociu se acord  10 puncte.
a Pentru cerinµa 2, dac  soluµia a³at  nu este minim  lexicograc, dar produce suma maxim 
corect  se acord  50% din punctajul testului respectiv.
a Pentru cerinµa 2, teste în valoare total  de 36 puncte vor avea n & 1000.
a 1 & p1 , p2 , ..., pn & 1000.

Exemple

roboti.in roboti.out Explicaµii


1 4 v = 1, deci se va rezolva DOAR prima cerinµ .
7 4726513
4726513 Cea mai lung  secvenµ  strict cresc toare este 1 3 4 7 ³i are
lungimea 4.
2 1 3 9 12 5 v = 2, deci se va rezolva DOAR a doua cerinµ .
5 1*3+3*9+9*12+12*5+5*1=203,
3 9 1 12 5 ³i este suma maxim  care se poate obµine.
Aceast  aranjare nu este singura pentru care se obµine suma
maxim , dar este cea mai mic  lexicograc.
2 1112 v = 2, deci se va rezolva DOAR a doua cerinµ .
4 1*1+1*1+1*2+2*1=6,
1211 ³i este suma maxim  care se poate obµine.
Aceast  aranjare nu este singura pentru care se obµine suma
maxim , dar este cea mai mic  lexicograc.
Tabela 4.3: roboti

Timp maxim de executare/test: 0.6 secunde


Memorie: total 32 MB
Dimensiune maxim  a sursei: 10 KB

4.3.1 Indicaµii de rezolvare


prof. Liliana Colin, Colegiul National "UNIREA", Foc³ani

Solutia 1 ³i demonstraµia propuse de Mihail-Cosmin Piµ-Rada


[0] Pentru usurinµa comunic rii vom deni puterea unui ³ir prin suma produselor tuturor
elementelor adiacente.
[1] Soluµia optim  va avea cea mai mic  valoare a ³irului original pe prima poziµie.
Sa presupunem prin absurd ca soluµia optim  are pe prima poziµie un element mai mare strict
decât minimul ³irului.
Prin rotaµii circulare puterea ³irului se conserv . Astfel putem roti circular a³a-numita soluµie
optim , pân  când elementul minim ajunge pe prima poziµie. Astfel ajungem la contradicµie,
întrucat noul ³ir are aceea³i putere, îns  este lexicograc mai mic.
[2] Vom încerca s  facem o demonstraµie constructiv . Evident vom pune cea mai mic  valoare
pe prima poziµie.
Vom ad uga elementele r mase în ordine cresc toare, încercând s  le ghicim poziµia optim .
Este util s  ne imagin m c  elementul de pe prima poziµie este o zon  contigu , de un element,
pentru început, ce se învecineaz  cu poziµiile 2 ³i N .
A ? ? ? ... ? ? ?  a³a arat  ³irul dup  a³ezarea minimului pe prima poziµie, ? reprezentând
poziµii libere
CAPITOLUL 4. OJI 2017 97

[3] Sa reprezent m mai general ³irul:


... * * * A ? ? ? ... ? ? ? B * * * ...  unde * reprezint  poziµii ocupate
Nu am demonstrat înc  forma de mai sus, îns  putem observa c  ³irul de pornire (cel de la
[2]) satisface regula. Astfel putem porni de la forma aceasta ³i tot ce demonstr m se poate aplica
inductiv, f r  nicio restricµie.
[4] Lem : Fie ³irul: ... * * * A X * ... * Y B * * * ...
(1) dac  A < B ³i X > Y ³irul nu poate  o soluµie optim 
(2) daca A > B ³i X < Y ³irul nu poate  o soluµie optim 
(3) daca A = B ³i X > Y ³irul nu poate  o soluµie optim 
S  oglindim secvenµa X * ... * Y: ... * * * A Y * ... * X B * * * ...
Fie P puterea sirului din enuntul lemei ³i P' puterea ³irului obµinut dup  oglindire.
P - P' = A*X + B*Y - A*Y - B*X = A*(X - Y) + B*(Y - X) = (A - B)*(X - Y)
Cazurile (1) ³i (2) conduc la P - P' < 0, ceea ce implic  faptul c  ultimul ³ir are o putere mai
mare.
În consecinµ  ³irul iniµial nu poate reprezenta o soluµie optim .
In cazul (3) deducem c  P = P', îns  cum X > Y ³irul obµinut dup  oglindire este lexicograc
mai mic.
A³adar ³i în acest caz ³irul iniµial nu are proprietatea de optimalitate.
[5] S  continu m cu pattern-ul de la [3], încercând s  ad ug m un nou num r, în ordinea lor
cresc toare.
Distingem posibilit µile:
(1) ... * * * A C ? ? ... ? ? ? B * * * ...
(2) ... * * * A ? ? ? ... ? ? C B * * * ...
(3) ... * * * A ? ? ? .C. ? ? ? B * * * ...
Întrucât avem duplicate, pot exista mai multe c i c tre soluµia optim .
Vom arata c  putem ajunge prin c ile (1) ³i (2) la soluµia optim .
Presupunem prin absurd c  soluµia optim  se poate atinge doar prin (3), soluµie ce va ar ta:
... * * * A X * ... * Y C * ... * B * * * ...
A, B <= C < X , Y (avem C < X pentru c  altfel ar rezulta C == X ³i putem obµine soluµia
³i prin (1))
Aplic m lema (1) pentru A X * ... * Y C ³i obµinem contradicµie.
Am exclus din discuµie cazul (3). R mâne s  vedem în ce condiµii vom amplasa C conform (1)
sau (2).
Distingem cazurile:
(i) A < B:
În acest caz putem rejecta cazul (2). Dac  prin absurd, soluµia optim  se poate obµine doar
prin (2), e aceast  soluµie: ... * * * A X * ... * C B * * * ...
Mai avem A, B <= C < X. Obµinem contradicµie conform lemei (1).
(ii) A > B:
În acest caz putem rejecta cazul (1). Dac  prin absurd, soluµia optim  se poate obµine doar
prin (1), e aceast  soluµie: ... * * * A C * ... * X B * * * ...
Mai avem A, B <= C < X. Obµinem contradicµie conform lemei (2).
(iii) A = B:
În acest caz putem rejecta cazul (2). Dac  prin absurd, soluµia optim  se poate obµine doar
prin (2), e aceast  soluµie: ... * * * A X * ... * C A * * * ...
Mai avem A <= C < X. Obµinem contradicµie conform lemei (3).
[6] Din cele de mai sus reiese algoritmul urm tor:
(0) se sorteaz  ³irul
(1) se amplaseaz  elementul minim pe prima poziµie
(2) se monitorizeaz  capetele zonei contigue (mai sus, reprezentate de A ³i B) ³i se alipe³te un
nou element de cap tul cel mai mic, iar în caz de egalitate, se alipe³te de cap tul stâng.

Soluµia 2 - propusa de prof. Piµ-Rada Ionel-Vasile, Colegiul National TRAIAN, Drobeta


Turnu Severin
Presupunem c  vectorul p[] este sortat cresc tor, p[1]<=p[2]<=...<=p[n] ³i au fost construite
perechile (valoare, frecvenµ ) (r[i], f[i]) cu r[1]<r[2]<...<r[nr].
Asez m consecutiv valoarea r[nr] de f[nr] ori ³i obµinem astfel secvenµa q[m], ..., q[M], unde
M-m+1==f[nr]. Este evident c  celelalte valori, care vor urma descresc tor, se vor a³eza la stânga
sau la dreapta acestei secvenµe.
CAPITOLUL 4. OJI 2017 98

Pentru ecare i cu nr>i>1


(1) dac  num rul de apariµii pentru r[i] este cel puµin 2, atunci vom ad uga câte o valoare
r[i] la ecare din capetele m ³i M, pentru obµinerea unei sume de produse maxime, apoi celelalte
f[i]-2 valori egale cu r[i] le vom ad uga la cap tul stâng m, pentru conservarea ordinii lexicograce
minime.
(2) dac  num rul de apariµii pentru r[i] este 1, atunci putem presupune c  avem o secvenµ 
1=f[i]=f[i-1]=...=f[j] de valori unicat (cu num rul de apariµii egal cu 1)
(a) dac  avem capete egale q[m]==q[M], atunci toate valorile unicat trebuie ad ugate alter-
nativ la cele dou  capete astfel încât ultima (cea mai mic ) s  e adaugat  la cap tul m (din
stânga), pentru suma maxim  de produse ³i conservarea ordinii lexiograce minime
(b) dac  avem capete cu valori diferite , atunci valorile unicat trebuie ad ugate alternativ la
cele dou  capete începand cu cap tul unde se a  cea mai mare, deoarece suma maxim  este
obiectivul principal de urm rit
(3) La nal valoarea minim  se va ad uga la cap tul m, conform cu num rul ei de apariµii.
Se va a³a apoi secvenµa q[m], q[m+1],...,q[M]

4.3.2 Cod surs 

Listing 4.3.1: roboti_luci.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <algorithm>
4
5 using namespace std;
6
7 int main()
8 {
9 ifstream in("roboti.in");
10 ofstream out("roboti.out");
11
12 long p[100000],a[100000],v,x,ok;
13 long n,i,max=1,s,d,ls=1,ns,nd;
14 int serie,cs,antdr;
15
16 in>>v>>n;
17 for(i=0;i<n;i++)
18 in>>p[i];
19
20 if(v==1) //cerinta 1
21 {
22 i=1;
23 x=p[0];
24 ok=0;
25 while(i<n && ok<2)
26 {
27 if(p[i]>p[i-1])
28 ls++;
29 else
30 {
31 if(ls>max)
32 max=ls;
33 ls=1;
34 }
35
36 i++;
37 //ciclarea cautarii
38 if(i==n)
39 {
40 if(x>p[i-1])
41 {
42 ls++;
43 i=1;
44 }
45 ok++; //numai inca o ciclare
46 }
47 }
48 out<<max;
49 }
50 else //cerinta 2
CAPITOLUL 4. OJI 2017 99

51 {
52 sort(p, p+n);
53
54 //aranjare
55 a[0]=p[0];
56 s=0; d=n;
57 serie=0;
58 ns=0;nd=0;
59 antdr=0;
60 for(i=1;i<=n-1;i++)
61 {
62 // daca este cap de serie,
63 // adica primuldintr-o serie de nr. care se repeta
64 if(i<n-1 && p[i]==p[i+1] && p[i-1]!=p[i])
65 cs=1;
66 else
67 cs=0;
68
69 //daca am pus mai putini sau egal in st si dr sau
70 // este intr-o serie de egale sau
71 // cel dinaintealui l-am pus in dreapta dar nu a fost cap de serie
72 if(ns<=nd || serie || (antdr && !cs) )//merge stanga
73 {
74 s++;
75 a[s]=p[i];
76 ns++;
77 antdr=0;
78 }
79 else //merge dreapta
80 {
81 d--;
82 a[d]=p[i];
83 nd++;
84 antdr=1;
85 }
86
87 if(p[i]==p[i-1])
88 serie++;
89 else
90 serie=0;
91 }
92
93 for(i=0;i<n;i++)
94 out<<a[i]<<’ ’;
95 }
96
97 in.close();
98 out.close();
99 return 0;
100 }

Listing 4.3.2: roboti_luci_wosort.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 long p[1001];
7
8 int main()
9 {
10 ifstream in("roboti.in");
11 ofstream out("roboti.out");
12
13 int a[100000],v,x,ok,serie,antdr;
14 long n,i,j,max=1,s,d,ls=1,ns,nd;
15
16 in>>v>>n;
17
18 if(v==1) //cerinta 1
19 {
20 for(i=0;i<n;i++)
21 in>>a[i];
22
CAPITOLUL 4. OJI 2017 100

23 i=1;
24 x=a[0];
25 ok=0;
26 while(i<n && ok<2)
27 {
28 if(a[i]>a[i-1])
29 ls++;
30 else
31 {
32 if(ls>max)
33 max=ls;
34 ls=1;
35 }
36
37 i++;
38 //ciclarea cautarii
39 if(i==n)
40 {
41 if(x>a[i-1])
42 {
43 ls++;
44 i=1;
45 }
46
47 ok++; //numai inca o ciclare
48 }
49 }
50
51 out<<max;
52 }
53 else //cerinta 2
54 {
55 for(i=0;i<n;i++)
56 {
57 in>>d;
58 p[d]++;
59 }
60
61 //aranjare
62 i=1;
63 s=0;
64 d=n;
65 ns=0;
66 nd=0;
67 while(p[i]==0)
68 i++;
69 //cout<<i<<":"<<p[i]<<endl;
70
71 a[0]=i;
72 p[i]--;
73 serie=0;
74 for(j=1;j<=p[i];j++)
75 {
76 s++;
77 a[s]=i;
78 ns++;
79 serie=1;
80 }
81
82 antdr=0;
83 i++;
84 do
85 {
86 while(p[i]==0 && i<=1000)
87 i++;
88
89 // daca este cap de serie,
90 // adica primul dintr-o serie de nr. care se repeta
91 // capul de serie merge dr restul din serie merg st
92 if(p[i]>1)
93 {
94 d--;
95 a[d]=i;
96 p[i]--;
97 nd++;
98 serie=1;
CAPITOLUL 4. OJI 2017 101

99 antdr=0;
100 for(j=1;j<=p[i];j++)
101 {
102 s++;
103 a[s]=i;
104 ns++;
105 }
106 }
107 else
108 {
109 if(ns<=nd || serie || antdr)
110 {
111 s++;
112 a[s]=i;
113 ns++;
114 serie=0;
115 antdr=0;
116 }
117 else
118 {
119 d--;
120 a[d]=i;
121 nd++;
122 antdr=1;
123 }
124 }
125
126 i++;
127 } while(ns+nd<n-1);
128
129 for(i=0;i<n;i++)
130 out<<a[i]<<’ ’;
131 }
132
133 in.close();
134 out.close();
135 return 0;
136 }

Listing 4.3.3: roboti_priv.cpp


1 #include<fstream>
2
3 using namespace std;
4
5 ifstream fin("roboti.in");
6 ofstream fout("roboti.out");
7
8 int v, n, p[200005], q[200005], r[200005], f[200005];
9
10 int main()
11 {
12 int i,j,lmax,k,nr,m,M;
13
14 fin>>v>>n;
15 for(i=1;i<=n;i++)
16 fin>>p[i];
17
18 if(v==1)
19 {
20 for(i=1;i<=n;i++)
21 p[n+i]=p[i];
22
23 lmax=0;
24 j=1;
25 for(i=2;i<=2*n;i++)
26 if(p[i-1]>=p[i])
27 {
28 lmax=max(i-j,lmax);
29 j=i;
30 }
31
32 fout<<lmax;
33 }
34 else
CAPITOLUL 4. OJI 2017 102

35 {
36 for(i=1;i<=1000;i++)
37 q[i]=0;
38
39 for(i=1;i<=n;i++)
40 q[p[i]]++;
41
42 nr=0;
43 for(i=1;i<=1000;i++)
44 {
45 if(q[i]>0)
46 {
47 nr++;
48 f[nr]=q[i];
49 r[nr]=i;
50 q[i]=0;
51 }
52 }
53
54 m=n+1;
55 for(i=1;i<=f[nr];i++)
56 q[--m]=r[nr];
57
58 M=n;
59 for(i=nr-1;i>=2;i--)
60 {
61 if(f[i]>1)
62 {
63 q[++M]=r[i];
64 for(j=1;j<f[i];j++)
65 q[--m]=r[i];
66 }
67 else
68 {
69 j=i;
70 while(j-1>=2 && f[j-1]==1)
71 j--;
72
73 if(q[m]==q[M])
74 {
75 if((i-j+1)%2==1)
76 q[--m]=r[i--];
77
78 for(k=i;k>=j;k=k-2)
79 {
80 q[++M]=r[k];
81 q[--m]=r[k-1];
82 }
83 }
84 else
85 {
86 if(q[m]>q[M])
87 {
88 for(k=0;k<=i-j;k++)
89 {
90 if(k%2==0)
91 q[--m]=r[i+k];
92 else
93 q[++M]=r[i+k];
94 }
95 }
96 else
97 {
98 for(k=0;k<=i-j;k++)
99 {
100 if(k%2==1)
101 q[--m]=r[i+k];
102 else
103 q[++M]=r[i+k];
104 }
105 }
106 }
107
108 i=j;
109 }
110 }
CAPITOLUL 4. OJI 2017 103

111
112 for(i=1;i<=f[1];i++)
113 q[--m]=r[1];
114
115 for(i=m;i<=M;i++)
116 fout<<q[i]<<" ";
117 }
118
119 fout.close();
120 return 0;
121 }

Listing 4.3.4: roboti_prmc.cpp


1 #include <vector>
2 #include <fstream>
3 #include <algorithm>
4
5 using namespace std;
6
7 int main()
8 {
9 ifstream f("roboti.in");
10 ofstream g("roboti.out");
11
12 int T, N;
13
14 f >> T >> N;
15
16 int A [N + N + 1];
17
18 for (int i = 0; i < N; i++)
19 {
20 f >> A[i];
21 A[i+N] = A[i];
22 }
23
24 if (T == 1)
25 {
26 int k, maxLen = 1;
27
28 for (int i = 0; i < N; i = k + 1)
29 {
30 for (k = i;
31 k + 1 < N + N && A[k] < A[k + 1];
32 k++);
33
34 maxLen = max(maxLen, k + 1 - i);
35 }
36
37 g << maxLen;
38 }
39 else
40 {
41 int B [N + 1];
42 int C [1001];
43 int i, j, k;
44 int p = 0;
45 int q = N - 1;
46
47 for( i = 1 ; i <= 1000 ; i++ )
48 C[i]=0;
49
50 for( i = 0 ; i < N ; i++ )
51 C[ A[i] ]++;
52
53 k = 0;
54 for( i = 1 ; i <= 1000 ; i++ )
55 for( j = 1 ; j <= C[i] ; j++ )
56 A[k++] = i;
57
58 B[p++] = A[0];
59 for (int i = 1; i < N; i++)
60 if (B[(q + 1) % N] < B[p - 1])
61 B[q--] = A[i];
CAPITOLUL 4. OJI 2017 104

62 else
63 B[p++] = A[i];
64
65 for (int i = 0; i < N; i++)
66 g << B[i] << " ";
67 }
68
69 g.close();
70 return 0;
71 }

4.3.3 *Rezolvare detaliat 


Capitolul 5

OJI 2016

5.1 Cifre
Problema 1 - Cifre 100 de puncte

Un indicator numeric este un dispozitiv de a³aj electronic destinat a³ rii unei


cifre zecimale.
Acesta conµine 7 segmente notate cu a, b, c, d, e, f, g , ca în gura al turat .
A³area unei cifre se face prin aprinderea unei combinaµii de segmente conform
tabelului:
Figura 5.1:
Cifra 0 1 2 3 4 5 6 7 8 9 Cifre
Segmente a,b,c, b,c a,b,d, a,b,c, b,c, a,c,d, a,c,d, a,b,c a,b,c, a,b,c,
aprinse d,e,f e,g d,g f,g f,g e,f,g d,e,f,g d,f,g
Tabela 5.1: cifre

Cerinµe
Cunoscând un num r natural N a³at cu ajutorul mai multor indicatoare numerice, s  se scrie
un program care determin :
1. Num rul de segmente aprinse pentru a³area num rului N .
2. Num rul de numere distincte mai mari decât N ,ce se pot forma prin aprinderea a cel
puµin unui segment în plus, faµ  de cele utilizate pentru a³area num rului N ,f r  a folosi alte
indicatoare numerice, ³i f r  a stinge nici un segment dintre cele deja aprinse.

Date de intrare
Fi³ierul de intrare este cifre.in
Pe prima linie a ³ierului de intrare se g se³te num rul natural V a c rui valoare poate  doar
1 sau 2.
Pe a doua linie a ³ierului de intrare se g se³te num rul natural N .

Date de ie³ire
Fi³ierul de ie³ire este cifre.out
Dac  valoarea lui V este 1 atunci ³ierul de ie³ire va conµine pe prima linie un singur num r
natural ce reprezint  num rul de segmente aprinse pentru a³area num rului N .
Dac  valoarea lui V este 2 atunci ³ierul de ie³ire va conµine pe prima linie un singur num r
natural reprezentând num rul de numere distincte mai mari decât N , ce se pot forma prin aprin-
derea a cel puµin unui segment în plus, faµ  de cele utilizate pentru a³area num rului N , f r  a
folosi alte indicatoare numerice.

Restricµii ³i preciz ri
a 10 & N & 1019
a 20% din teste vor avea valoarea V 1, iar 80% din teste vor avea valoarea V 2.

105
CAPITOLUL 5. OJI 2016 106

Exemple
cifre.in cifre.out Explicaµii
1 17 V = 1, deci se rezolv  NUMAI prima cerinµ .
823 N = 823;
Pentru a³area cifrei 8 s-au aprins 7 segmente, pentru cifra 2 s-au
aprins 5 segmente ³i pentru cifra 3 tot 5 segmente. în total s-au
aprins 17 segmente.
2 5 V = 2, deci se rezolv  NUMAI a doua cerinµ .
823 N = 823;
Din cifra 8 nu se mai pot obµine alte cifre prin aprinderea de noi
segmente.
Din cifra 2 se poate obµine cifra 8 iar din cifra 3 se pot obµine cifrele 8
³i 9 prin aprinderea de noi segmente. A³adar, se pot obµine 5 numere
mai mari ca 823: 828, 829, 883, 888, 889.

Timp maxim de executare/test: 0.1 secunde


Memorie: total 2 MB
Dimensiune maxim  a sursei: 5 KB

5.1.1 Indicaµii de rezolvare


prof. Che³c  Ciprian - Liceul Tehnologic Costin Neniµesc, Buz u

Varianta 1
Pentru prima cerinµ  trebuie determinate câte segmente sunt aprinse pentru a³area num rului
N.
Se prelucreaz  num rul N cifr  cu cifr  ³i se calculeaz  num rul segmentelor aprinse.
Num rul de segmente aprinse pentru a³area cifrelor este dat în tabelul de mai jos :
Cifr  0 1 2 3 4 5 6 7 8 9
Num r segmente aprinse 6 2 5 5 4 5 6 3 7 6
Pentru a doua cerinµ  o variant  mai puµin ecient  este urm toarea :
a Se parcurg toate numerele de la N  1 pân  la cel mai mare num r cu acela³i num r de cifre
ca N .
a Pentru ecare dintre numerele de mai sus, se determin  pentru ecare cifr  dac  aceasta
poate  construit  din cifra lui N de pe aceea³i poziµie prin aprinderea suplimentar  a unui sau
mai multor segmente.
a Dac  pentru ecare cifr  condiµia de mai sus este adevarat  se contorizeaz  înc  o soluµie.
Aceast  soluµie obµine aproximativ jum tate din punctaj ³i ordinul s u de complexitate este
nrcif re N 
O 10  1  N .
Varianta 2 (optimizare matematic )
Pentru a determina un algoritm mai ecient folosim una din metodele de num rare prezentate
în manualul de clasa a IX-a de matematic  ³i anume regula produsului. Aceast  regul  arm 
c  dac  avem A1 , A2 , ...., Ak operaµii succesive, prima putând  efectuat  în n1 moduri, a doua
în n2 moduri, ..., ultima în nk moduri, atunci succesiunea celor k operaµii poate  efectuat  în
n1 n2 ... nk moduri.
În acest scop construim urm toarele structuri de date:
a) Num rul de cifre ce se pot forma dintr-o cifr  dat  prin ad ugarea de noi segmente (inclusiv
cifra curent ):
Cifr  0 1 2 3 4 5 6 7 8 9
Num rul cifrelor ce se pot realiza din cifra curent  2 7 2 3 3 4 2 5 1 2
prin ad ugare de noi segmente (inclusiv cifra curent )
b) Num rul de cifre, strict mai mari decât o cifr  dat , ce se pot forma prin ad ugarea de noi
segmente:
Cifr  0 1 2 3 4 5 6 7 8 9
Num rul cifrelor strict mai mari decât cifra curent  1 5 1 2 2 3 1 2 0 0
ce se pot forma dintr-o cifr  dat  prin ad ugarea de
noi segmente:
Având în vedere aceste date vom parcurge cifrele lui N de la cifra cea mai semnicativ  la cea
mai puµin semnicativ  ³i la ecare cifr  proced m astfel:
Calcul m produsul dintre numerele:
a câte cifre strict mai mari exist  decât cifra curent  ³i
CAPITOLUL 5. OJI 2016 107

a câte cifre se pot forma prin ad ugarea de noi segmente pentru ecare din cifrele urm toare,
nu neap rat mai mari, decât cifrele de pe poziµii similare ale lui N .
Num rul de numere distincte mai mari decât N reprezint  suma produselor de mai sus.
Aceast  soluµie obµine maximum de punctaj iar ordinul s u de complexitate este
2
O nrcif re N  .

5.1.2 Cod surs 

Listing 5.1.1: CC1_cifre.cpp


1 // Chesca Ciprian - sursa 100 p
2 // O((nrcifre(N)^2))
3
4 #include <fstream>
5
6 using namespace std;
7
8 int main()
9 {
10 unsigned long long n,N,S,P;
11
12 // cate segmente are fiecare cifra
13 int s[10]={6,2,5,5,4,5,6,3,7,6};
14
15 // cate cifre strict mai mari se pot obtine din cifra curenta
16 // prin adaugare
17 int a[10]={1,5,1,2,2,3,1,2,0,0};
18
19 // cate cifre se pot obtine din cifra curenta prin adaugare,
20 // inclusiv cifra curenta
21 int b[10]={2,7,2,3,3,4,2,5,1,2};
22
23 int c,k,i,j,w[25],aux,v;
24
25 ifstream f("cifre.in");
26 ofstream g("cifre.out");
27
28 f>>v>>n;
29
30 if (v==1)
31 {
32 N=n;S=0;
33 while (N>0)
34 {
35 c=N%10;
36 S=S+s[c];
37 N=N/10;
38 }
39
40 g<<S<<"\n";
41 }
42
43 if (v==2)
44 {
45 // duc cifrele intr-un vector
46 N=n;k=0;
47 while (N>0)
48 {
49 w[++k]=N%10;
50 N=N/10;
51 }
52
53 // reversez vectorul
54 for(i=1,j=k;i<j;i++,j--)
55 {
56 aux=w[i];
57 w[i]=w[j];
58 w[j]=aux;
59 }
60
61 S=0;
62 for(i=1;i<=k;i++)
CAPITOLUL 5. OJI 2016 108

63 {
64 if (a[w[i]])
65 {
66 P=a[w[i]];
67 for(j=i+1;j<=k;j++)
68 P=P*b[w[j]];
69 S=S+P;
70 }
71 }
72
73 g<<S<<"\n";
74 }
75
76
77 f.close();
78 g.close();
79
80 return 0;
81 }

Listing 5.1.2: CP_cifre.cpp


1 // prof. Carmen Popescu - CNGL Sibiu - 100 puncte
2
3 #include <fstream>
4
5 using namespace std;
6
7 ifstream f("cifre.in");
8 ofstream g("cifre.out");
9
10 int main()
11 {
12 unsigned long long n,t,s,p;
13
14 // in cate cifre se poate transforma o cifra prin adaugare de bete,
15 // inclusiv in cifre mai mici, inclusiv numarul neschimbat
16 int c1[10] = {2, 7, 2, 3, 3, 4, 2, 5, 1, 2};
17
18 // in cate cifre se poate transforma o cifra prin adaugare de bete,
19 // cifre mai mari ca ea
20 int c2[10] = {1, 5, 1, 2, 2, 3, 1, 2, 0, 0};
21
22 // numarul de segmente aprinse
23 int c3[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
24
25 int v[25]; // cifrele numarului
26 f>>t;
27 f>>n;
28
29 s=0;
30 int nr_cif=0;
31
32 while (n>0)
33 {
34 int c=n%10;
35 v[nr_cif++]=c;
36 s=s+c3[c];
37 n=n/10;
38 }
39
40 if (t==1)
41 {
42 g<<s<<"\n";
43 }
44 else
45 {
46 s=0;
47 for (int i=nr_cif-1;i>=0;i--)
48 {
49 // marim cifra curenta, restul pot ramane neschimbate,
50 // sau le modificam in orice prin adaugarea de bete
51 p=c2[v[i]];
52 for (int j=i-1; j>=0 && p>0 ;j--)
53 p*=c1[v[j]];
CAPITOLUL 5. OJI 2016 109

54 s=s+p;
55 }
56
57 g<<s<<"\n";
58 }
59
60 return 0;
61 }

Listing 5.1.3: MLT_cifre.cpp


1 // prof. Mircea Lupse-Turpan - 100 puncte
2
3 #include <fstream>
4 using namespace std;
5
6 ifstream fin("cifre.in");
7 ofstream fout("cifre.out");
8
9 int Segments[] = {6,2,5,5,4,5,6,3,7,6};
10 int Up[] = {1,5,1,2,2,3,1,2,0,0};
11 int Total[] = {2,7,2,3,3,4,2,5,1,2};
12
13 int Solve1(unsigned long long N)
14 {
15 int S = 0;
16 while(N)
17 {
18 S += Segments[N%10];
19 N /= 10;
20 }
21 return S;
22 }
23
24 unsigned long long Solve2(unsigned long long N)
25 {
26 int X[25];
27 unsigned long long Sol = 0;
28 int k = 0;
29
30 while(N)
31 {
32 X[++k] = N % 10;
33 N /= 10;
34 }
35
36 for(int i = 1; i <= k; ++i)
37 {
38 unsigned long long P = Up[X[i]];
39 for(int j = 1; j < i; ++j)
40 P = P * Total[X[j]];
41 Sol += P;
42 }
43
44 return Sol;
45 }
46
47 int main()
48 {
49 unsigned long long V,N;
50
51 fin>>V>>N;
52
53 if(V==1)
54 fout<<Solve1(N)<<"\n";
55
56 if(V==2)
57 fout<<Solve2(N)<<"\n";
58
59 return 0;
60 }
CAPITOLUL 5. OJI 2016 110

5.1.3 *Rezolvare detaliat 

5.2 pic
Problema 2 - pic 100 de puncte

Alex s-a angajat în vacanµa de var  ca barman. Pentru c  îi place s 


transforme munca la bar într-un spectacol, uneori aranjeaz  mai multe
pahare identice ca form  ³i dimensiune, dar de capacit µi diferite, sub
forma unei stive.
Un pahar din stiv , cu excepµia celor de la baz , se sprijin  pe exact
dou  pahare din rândul de mai jos. Paharele sunt numerotate ca în
imaginea al turat . Nivelurile din stiv  sunt deasemenea numerotate,
începând cu 1, de la vârf, adic  paharul 1 se a  pe nivelul 1, paharele Figura 5.2: pic
2 ³i 3 pe nivelul 2, paharele 4, 5 ³i 6 sunt pe nivelul 3, ³.a.m.d.
Alex toarn  în ecare secund  câte un mililitru de ap  (o pic tur ) în paharul num rul 1.
Paharele au o proprietate ciudat  atunci când sunt pline: primul mililitru care ajunge într-un
pahar plin se va scurge instantaneu în paharul aat imediat în stânga sa pe rândul de dedesubt,
urm torul mililitru se va scurge instantaneu în paharul aat imediat în dreapta sa pe rândul de
dedesubt ³i tot a³a, alternativ câte o pic tur  în cele dou  pahare.
De exemplu când paharul 2 este plin, primul mililitru ce va ajunge în el se va scurge în paharul
4, urm torul mililitru se scurge în paharul 5, al treilea mililitru se va scurge din nou în paharul 4,
³.a.m.d.
Atunci când într-un pahar plin aat la baza stivei ajunge un nou mililitru de ap , acesta se
scurge instantaneu pe mas .
Cerinµe
Cunoscând num rul de pahare din rândul de la baza stivei ³i faptul c  stiva este complet 
(toate rândurile conµin num rul maxim de pahare ce se pot a³eza dup  regula de mai sus, iar pe
cel mai de sus rând se g se³te un singur pahar), s  se scrie un program care determin :
1. Care este nivelul minim (cel mai de sus) care are suma capacit µilor tuturor paharelor de
pe nivel maxim ?
2. Care este num rul minim de secunde necesar pentru a umple toate paharele folosind pro-
cedeul descris mai sus ³i câµi mililitri de ap  se risipesc (se scurg pe mas ) în acest caz?
Date de intrare
Pe prima linie a ³ierului de intrare pic.in se g se³te un num r natural V a c rui valoare poate
 doar 1 sau 2.
Pe a doua linie a ³ierului de intrare se g se³te un singur num r natural N reprezentând
num rul de pahare din rândul de la baza stivei.
Pe a treia linie a ³ierului de intrare se g sesc M N ˜ N 1©2 numere naturale C1 , C2 , ..., CM
separate prin câte un spaµiu, Ci reprezentând capacitatea (în mililitri) a paharului num rul i din
stiv .
Date de ie³ire
Dac  valoarea lui V este 1 atunci ³ierul de ie³ire pic.out va conµine pe prima linie un singur
num r natural ce reprezint  num rul de ordine al nivelului minim (cel mai de sus) care are suma
capacit µilor tuturor paharelor de pe nivel maxim .
Dac  valoarea lui V este 2 atunci ³ierul de ie³ire va conµine pe prima linie dou  numere
naturale separate printr-un singur spaµiu reprezentând num rul minim de secunde scurse pân 
când toate paharele din stiv  sunt pline ³i respectiv num rul de mililitri de ap  risipiµi (ajun³i pe
mas ) în acel moment.
Restricµii ³i preciz ri
a 2 & N & 50
a 20% din teste vor avea valoarea V 1, iar 80% din teste vor avea valoarea V 2.
a 35% din teste vor avea N 17, iar 65% din teste vor avea N % 17.
a 1 & C1 , C2 , ..., CM & 25
CAPITOLUL 5. OJI 2016 111

Exemple
pic.in pic.out Explicaµii
1 2 V = 1, deci se rezolv  NUMAI prima cerinµ .
3 Pe nivelul 1 se g se³te un pahar de capacitate 2.
242123 Pe nivelul 2 se g sesc dou  pahare de capacit µi 4 ³i 2.
Pe nivelul 3 se g sesc trei pahare de capacit µi 1, 2 ³i 3 .
Suma capacit µilor paharelor este: 2 pe nivelul 1, 6 pe nivelul 2
³i 6 pe nivelul 3, deci cel mai de sus nivel cu suma maxim  - 6,
este nivelul 2.
2 18 4 V = 2, deci se rezolv  NUMAI a doua cerinµ .
3 Dup  10 secunde conguraµia paharelor este urm toarea:
242123 Pahar Numar picaturi Observatii
1 2 Plin
2 4 Plin
3 2 Plin
4 0
5 1
6 1
Cea de a unsprezecea pic tur  se scurge din paharul 1 în paharul
2 ³i mai departe în paharul 4.

Timp maxim de executare/test: 0.5 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 5 KB

5.2.1 Indicaµii de rezolvare


prof. Popescu Carmen - Colegiul Naµional Gheorghe Laz r, Sibiu

Pentru rezolvarea primei cerinµe se calculeaz , simultan cu citirea datelor, suma capacit µilor
de pe ecare nivel ³i se determin  maximul acestor sume, reµinându-se ³i primul nivel pentru care
se atinge acest maxim.
Rezolvarea celei de-a doua cerinµe se bazeaz  pe o simulare a curgerii picurilor.

Varianta 1 - 50 pct (20 puncte pentru cerinµa 1 + 30 puncte pentru cerinµa 2)


Presupune simularea pic cu pic, la ecare pahar reµinând direcµia în care va urma s  curg 
urm toarea pic tur .

Varianta 2 - 100 pct


soluµie propus  de prof. Ciprian Che³c  ³i prof. Marcel Dr gan
Se face o simulare plecând cu un set întreg de pic turi. La un anumit pahar, dac  num rul K
de pic turi ajunse la el este mai mare decât capacitatea C a acestuia, cele K  C pic turi se vor
scurge în cele dou  pahare de dedesubt, K  C  1©2 se vor scurge în paharul din stânga iar
restul de K  C ©2 pic turi se scurg în paharul din dreapta.
Pentru g sirea setului exact de pic turi care este necesar pentru a umple toate paharele se
realizeaz  o c utare binar  în intervalul 0, U LLON G_M AX  simulând în mod repetat curgerea
unui set înterg de pic turi.
2
Complexitatea este O N logU LLON G_M AX , unde N este num rul de pahare de pe ultimul
rând, iar U LLON G_M AX este valoarea maxim  a num rul de pic turi necesar umplerii tuturor
paharelor.
ATEN•IE!!!
Soluµiile la a doua cerinµ  dep ³esc int, este necesar  folosirea tipului de date long long.

5.2.2 Cod surs 

Listing 5.2.1: pic_CP1.cpp


1 // prof. Carmen Popescu - Colegiul National "Gheorghe Lazar" Sibiu
2 // 100 pct
3
CAPITOLUL 5. OJI 2016 112

4 #include <fstream>
5 #define NMAX 55
6
7 using namespace std;
8
9 ifstream f("pic.in");
10 ofstream g("pic.out");
11
12 long long ctot,p[NMAX][NMAX];
13 long long c[NMAX][NMAX],d[NMAX][NMAX];
14 long long x,m,k,s,mx=0,ss;
15 int i,j,umpl,v,lin,n;
16
17 int main()
18 {
19 f>>v>>n;
20 ctot=0;
21
22 for (i=1; i<=n; i++)
23 {
24 m=0;
25 ss=0;
26 for (j=1; j<=i; j++)
27 {
28 f>>x;
29
30 ss+=x;
31 c[i][j]=x;
32 if (x>m) m=x;
33 }
34
35 ctot += ss;
36 if (ss>mx)
37 {
38 mx=ss;
39 lin=i;
40 }
41
42 }
43
44 if (v==1)
45 g<<lin<<"\n";
46 else
47 {
48 s=(1<<n); // 2^n, cantitate inca insuficienta pt a umple n nivele
49 // daca paharele ar avea toate capacitate 1
50
51 do
52 {
53 // turnam dintr-o data s picaturi in paharul 1
54
55 for (i=1; i<=n; i++) // initializam nr de picaturi turnate
56 // in fiecare pahar
57 for (j=1; j<=n; j++)
58 p[i][j]=0;
59 i=1;
60 j=1;
61 p[1][1]=s;
62 for (i=1; i<n; i++)
63 for (j=1; j<=i; j++)
64 {
65 x = p[i][j]-c[i][j];
66 p[i][j] = x;
67 p[i+1][j] += (x+1)/2;
68 p[i+1][j+1] += x/2;
69 }
70
71 // aflam cate pahare neumplute avem
72 k=0;
73 for (j=1; j<=n; j++)
74 {
75 p[n][j] -= c[n][j];
76 if (p[n][j]<0) k++;
77 }
78
79 if (k>0) // mai sunt pahare neumplute
CAPITOLUL 5. OJI 2016 113

80 s *= 2;
81 } while (k>0);
82
83 // s umple toate paharele,
84 // s/2 nu umple toate paharele
85
86 // numarul de picaturi necesare este in intervalul [s/2, s] facem
87 // o cautare binara a cantitatii necesare
88 // pt a ramane un singur pahar gol
89
90 long long l=s/2;
91 s=s-l/2; // s e jumatatea intervalului [s/2,s]
92
93 while (l>0)
94 {
95 for (i=1; i<=n; i++) // initializam nr de picaturi turnate
96 // in fiecare pahar
97 for (j=1; j<=n; j++)
98 p[i][j]=0;
99
100 i=1;
101 j=1;
102 p[1][1]=s;
103 for (i=1; i<n; i++)
104 for (j=1; j<=i; j++)
105 {
106 x = p[i][j]-c[i][j];
107 p[i][j] = x;
108 p[i+1][j] += (x+1)/2;
109 p[i+1][j+1] += x/2;
110 }
111
112 k=0;
113 for (j=1; j<=n; j++)
114 {
115 p[n][j] -= c[n][j];
116 if (p[n][j]<0) k++; // pahar neumplut
117 }
118
119 l=l/2;
120 if (k>0) // cel putin un pahar neumplut
121 s=s+l/2;
122 else
123 s=s-l/2;
124 }
125
126 if (k>0)
127 s++;
128
129 g<<s<<" "<<s-ctot<<"\n";
130 }
131
132 return 0;
133 }

Listing 5.2.2: picMD.cpp


1 // prof. Marcel Dragan
2 // Colegiul National "Samuel von Brukenthal" Sibiu
3 // 100 pct
4
5 #include <fstream>
6 #include <climits>
7
8 using namespace std;
9
10 ifstream fi("pic.in");
11 ofstream fo("pic.out");
12
13 long long p[101][101],pTemp[101][101];
14 unsigned long long pt,pl,mx,l;
15 int v,n,dp[101][101],dpTemp[101][101];
16
17 int main()
18 {
CAPITOLUL 5. OJI 2016 114

19 fi>>v>>n;
20 for(int i=1;i<=n;i++)
21 {
22 pl=0;
23 for(int j=1;j<=i;j++)
24 {
25 fi>>p[i][j];
26 pl+=p[i][j];
27 }
28
29 pt+=pl;
30 if (pl>mx)
31 {
32 mx=pl;
33 l=i;
34 }
35 }
36
37 if(v==1)
38 {
39 fo<<l<<"\n";
40 }
41 else
42 {
43 // copie stiva pahare
44 for(int i=1;i<=n+1;i++)
45 {
46 for(int j=1;j<=n+1;j++)
47 {
48 pTemp[i][j]=p[i][j];
49 }
50 }
51
52 // cautare binara intre 0 si ULLONG_MAX
53 unsigned long long p2=ULLONG_MAX/2+1,d;
54 d=p2;
55 int c=1;
56 while(d>0)
57 {
58 // simularea trecerii a p2 picaturi
59 p[1][1]-=p2;
60 for(int i=1;i<=n-1;i++)
61 {
62 for(int j=1;j<=i;j++)
63 {
64 if(p[i][j]<0)
65 {
66 p[i+1][j]+=p[i][j]/2;
67 p[i+1][j+1]+=p[i][j]/2;
68 if(p[i][j]%2==-1)
69 p[i+1][j]--;
70 p[i][j]=0;
71 }
72 }
73 }
74
75 // verificarea paharelor de pe ultimul rand
76 c=0;
77 for(int j=1;j<=n;j++)
78 if(p[n][j]>0)
79 c=1;
80 d=d/2;
81 if(c==1)
82 p2=p2+d;
83 else
84 p2=p2-d;
85
86 // refacere stiva pahare
87 for(int i=1;i<=n+1;i++)
88 {
89 for(int j=1;j<=n+1;j++)
90 {
91 p[i][j]=pTemp[i][j];
92 }
93 }
94 }
CAPITOLUL 5. OJI 2016 115

95
96 if(c==1)
97 p2++;
98
99 fo<<p2<<" "<<p2-pt<<"\n";
100 }
101
102 return 0;
103 }

Listing 5.2.3: picMLT1.cpp


1 // prof. Mircea Lupse-Turpan - 100 puncte
2 #include <fstream>
3 #include <cstring>
4
5 using namespace std;
6
7 ifstream fin("pic.in");
8 ofstream fout("pic.out");
9
10 const int NMax = 55;
11 const long long oo = 1LL << 62;
12 int V, N, Sum;
13 int C[NMax][NMax];
14 long long F[NMax][NMax];
15
16 void Read()
17 {
18 fin >> V >> N;
19 for(int i = 1; i <= N; ++i)
20 for(int j = 1; j <= i; ++j)
21 {
22 fin >> C[i][j];
23 Sum += C[i][j];
24 }
25 }
26
27 int Solve1()
28 {
29 int Max = 0,Level = 1;
30 for(int i = 1; i <= N; ++i)
31 {
32 int S = 0;
33 for(int j = 1; j <= i; ++j)
34 S += C[i][j];
35
36 if(S > Max)
37 {
38 Max = S;
39 Level = i;
40 }
41 }
42
43 return Level;
44 }
45
46 bool Check(long long Val)
47 {
48 memset(F,0,sizeof(F));
49
50 F[1][1] = Val;
51
52 for(int i = 1; i <= N; ++i)
53 for(int j = 1; j <= i; ++j)
54 {
55 F[i+1][j] += (F[i][j] - C[i][j] + 1) / 2;
56 F[i+1][j+1] += (F[i][j] - C[i][j]) / 2;
57 }
58
59 for(int i = 1; i <= N; ++i)
60 if (F[N][i] < C[N][i])
61 return 0;
62
63 return 1;
CAPITOLUL 5. OJI 2016 116

64 }
65
66 long long Solve2()
67 {
68 long long Sol = -1;
69 long long Left = Sum, Right = oo;
70 while(Left <= Right)
71 {
72 long long Mid = (Left + Right) / 2;
73 if(Check(Mid))
74 {
75 Sol = Mid;
76 Right = Mid - 1;
77 }
78 else
79 Left = Mid + 1;
80 }
81
82 return Sol;
83 }
84
85 int main()
86 {
87 Read();
88
89 if(V == 1)
90 fout<<Solve1()<<"\n";
91
92 if(V == 2)
93 {
94 long long T = Solve2();
95 fout<<T<<" "<<T - Sum<<"\n";
96 }
97
98 return 0;
99 }

5.2.3 *Rezolvare detaliat 


Capitolul 6

OJI 2015

6.1 arc
Problema 1 - arc 100 de puncte
Irinuca a descoperit un nou joc pe calculator. Pe ecran sunt plasate pe o linie n bile colorate.
Culorile bilelor sunt codicate cu numere naturale. Un sub³ir de bile al turate având toate aceea³i
culoare se nume³te secvenµ . O secvenµ  va conµine num rul maxim de bile al turate având aceea³i
culoare. Lungimea unei secvenµe este egal  cu num rul de bile din care este compus .
Irinuca are la dispoziµie un arc special. Tr gând cu arcul asupra unei bile, dac  aceasta face
parte dintr-o secvenµ  de lungime cel puµin egal  cu 3, întreaga secvenµ  va  eliminat , iar bilele
din dreapta secvenµei se vor deplasa spre stânga pentru a umple golul l sat de bilele eliminate.
Dac  imediat în stânga ³i în dreapta secvenµei eliminate se g seau dou  secvenµe având aceea³i
culoare ³i dac  secvenµa obµinut  din unirea acestora dup  eliminare are o lungime cel puµin egal 
cu 3, atunci va  ³i ea eliminat  la rândul ei. Procesul continu  pân  când secvenµele din stânga
³i dreapta unei secvenµe tocmai eliminate au culori diferite sau pân  când lungimea secvenµei
obµinute prin al turare este mai mic  decât 3 sau pân  când în stânga ori în dreapta unei secvenµe
eliminate nu se mai g sesc bile sau pân  sunt eliminate toate bilele de pe ecran.
Scopul jocului este de a elimina cât mai multe bile de pe ecran. Cum Irinuca înc  nu se pricepe
prea bine la acest joc ³i-a stabilit o strategie. Va trage cu arcul întotdeauna asupra unei bile ce
face parte din secvenµa de lungime maxim  de pe ecran. Dac  sunt mai multe astfel de secvenµe,
ea va alege cea mai din stânga secvenµ  de lungime maxim . Dac  toate secvenµele de pe ecran
au lungimi mai mici decât 3, Irinuca nu va mai putea elimina nici una din ele ³i jocul se încheie.
De exemplu, dac  ³irul iniµial de bile este
513322223115644447
Irinuca va acµiona asupra unei bile de culoare 2. Prin eliminare se obµine ³irul de bile
51333115644447
din care se elimin  ³i secvenµa de bile de culoare 3 obµinându-se ³irul de bile
51115644447
din care se elimin  ³i secvenµa de culoare 1.
55644447
Cum secvenµa de bile de culoare 5 nu este sucient de lung , aceasta nu se mai elimin . Acum
Irinuca trage asupra unei bile de culoare 4 ³i obµine
5567
dar cum în stânga ³i în dreapta secvenµei eliminate sunt secvenµe de culori diferite, nu se va
mai elimina nici o secvenµ . Jocul se încheie deoarece nu mai exist  nici o secvenµ  de lungime cel
puµin 3 asupra c reia s  se poat  trage.

Cerinµe
Cunoscând num rul de bile ³i culorile ec rei bile de pe ecran se cere s  se determine:
1. num rul de secvenµe de bile care se aau iniµial pe ecran;
2. num rul de bile care r mân neeliminate de pe ecran ³i culorile bilelor r mase în ordine pe
ecran la nalul jocului.

Date de intrare

117
CAPITOLUL 6. OJI 2015 118

Fi³ierul de intrare arc.in conµine pe prima linie un num r natural V . Pentru toate testele de
intrare, num rul V poate avea doar valoarea 1 sau 2.
A doua linie conµine un num r natural n reprezentând num rul de bile, iar a treia linie conµine
n numere naturale c1 , c2 , ..., cn separate prin câte un spaµiu, reprezentând culorile celor n bile de
pe ecran.

Date de ie³ire
Dac  valoarea lui V este 1, se va rezolva numai punctul 1 din cerinµ .
În acest caz, în ³ierul de ie³ire arc.out se va scrie un singur num r natural n1 , reprezentând
num rul de secvenµe de bile aate iniµial pe ecran.
Dac  valoarea lui V este 2, se va rezolva numai punctul 2 din cerinµ .
În acest caz, în ³ierul de ie³ire arc.out se va scrie pe prima linie un singur num r natural
n2 , reprezentând num rul de bile care r mân neeliminate de pe ecran la nalul jocului, iar pe
urm toarele n2 linii se va scrie câte un num r natural reprezentând în ordine culorile bilelor
r mase neeliminate la nalul jocului.
Dac  la nalul jocului nu mai r mâne nici o bil  neeliminat , ³ierul de ie³ire va conµine pe
prima sa linie valoarea 0.

Restricµii ³i preciz ri
a 1 & n & 10000
a 1 & c1 , c2 , ..., cn & 100000
a Pentru rezolvarea corect  a primei cerinµe se acord  20 de puncte, iar pentru cerinµa a doua
se acord  80 de puncte.

Exemple
arc.in arc.out Explicaµii
1 10 V=1
18 Atenµie!
513322223115644 Pentru acest test se rezolv  doar cerinµa 1.
447 Secvenµele sunt (5), (1), (3, 3), (2,2,2,2), (3), (1,1),
(5), (6), (4,4,4,4), (7)
2 4 V=2
18 5 Atenµie!
513322223115644 5 Pentru acest test se rezolv  doar cerinµa 2.
447 6
7
2 0 V=2
15 Atenµie!
122221133344443 Pentru acest test se rezolv  doar cerinµa 2.

Timp maxim de executare/test: 0.2 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 10 KB

6.1.1 Indicaµii de rezolvare


prof. Carmen Popescu - Colegiul Naµional "Gheorghe Laz r" Sibiu

La citire se vor forma doi vectori:


x - memorând culorile ecarei secvenµe
c - num rul de elemenete din ecare secvenµ 

Cerinµa 1.
Practic la citirea unei noi valori din ³ier, dac  aceasta este egal  cu ultima valoare memorat 
în x vom incrementa contorul c corespunz tor, altfel o vom memora în urm toarea component 
din vectorul x iar contorul corespunzator îl iniµializ m cu 1.
Num rul efectiv de valori din cei doi vectori, m, este r spunsul la primul punct.

Cerinta 2.
Vom simula elimin rile. Determin m maximul din vectorul c, e acesta max cp.
CAPITOLUL 6. OJI 2015 119

Cat timp max >= 3


- determin m cea mai mare valoare i pentru care
xp  j  xp  j  pt orice j 1, 2, ..., i (secvenµele din stânga ³i dreapta secvenµei eliminate
au aceea³i culoare)
cp  j   cp  j  % 3 (prin unire se obµine o secvenµ  de lungime cel puµin 3)
p  j % 1 ³i p  j $ m (nu ie³im din vector)
- elimin m din ambii vectori toate componentele xk , ck  cu k pi, pi1, ..., p, p1, ..., pi
prin deplasarea spre stânga cu 2i  1 poziµii a tuturor elementelor de dup  poziµia p  i.
- determin m urm toarea secvenµ  maxim  din vectorul c (max cp)
sf cât timp

6.1.2 Cod surs 

Listing 6.1.1: arc1.cpp


1 // prof. Carmen Popescu - Colegiul National "Gheorghe Lazar" Sibiu
2 #include <fstream>
3
4 using namespace std;
5
6 int p;
7 int c[10000],x[100000],m,n;
8
9 ifstream f("arc.in");
10 ofstream g("arc.out");
11
12 void citire()
13 {
14 int k,i;
15 f>>p;
16 f>>n;
17 f>>c[1];
18
19 x[1]=1;
20 m=1;
21 for (i=2;i<=n;i++)
22 {
23 f>>k;
24 if (k==c[m])
25 x[m]++;
26 else
27 {
28 m++;
29 c[m]=k;
30 x[m]=1;
31 }
32 }
33 }
34
35 int maxim()
36 {
37 int mx,k,i;
38 mx=x[1];
39 k=1;
40 for (i=1;i<=m;i++)
41 if (x[i]>mx)
42 {
43 mx=x[i]; k=i;
44 }
45 return k;
46 }
47
48 void elim(int k)
49 {
50 int i,j,m1;
51 if (x[k]>2)
52 {
53 n=n-x[k];
54 i=k-1;
55 j=k+1;
56 while (c[i]==c[j] && i>=1 && j<=m && x[i]+x[j]>2)
CAPITOLUL 6. OJI 2015 120

57 {
58 n=n-x[i]-x[j];
59 i--; j++;
60 }
61
62 if (j>m)
63 m=i;
64 else
65 {
66 if (i>=1 && j<=m && c[i]==c[j])
67 {
68 x[i]=x[i]+x[j];
69 j++;
70 }
71
72 m1=m-j+i+1;
73 i=i+1;
74 while (j<=m)
75 {
76 x[i]=x[j]; c[i]=c[j];
77 i++; j++;
78 }
79
80 m=m1;
81 }
82 }
83 }
84
85 int main()
86 {
87 int k;
88
89 citire();
90
91 if (p==1)
92 g<<m<<"\n";
93 else
94 {
95 k=maxim();
96 while (n>0 && x[k]>2)
97 {
98 elim(k);
99 if (k>0)
100 k=maxim();
101 }
102
103 if (n>0)
104 {
105 g<<n<<"\n";
106 for (int i=1;i<=m;i++)
107 for (int j=1;j<=x[i];j++)
108 g<<c[i]<<"\n";
109 }
110 else
111 g<<"0\n";
112 }
113
114 g.close();
115 return 0;
116 }

Listing 6.1.2: arc2.cpp


1 /* prof. Constantin Galatan
2 Colegiul National "Liviu Rebreanu" Bistrita
3 */
4 #include <iostream>
5 #include <fstream>
6
7 using namespace std;
8
9 ifstream fin("arc.in");
10 ofstream fout("arc.out");
11
12 int p, n, n1, n2;
CAPITOLUL 6. OJI 2015 121

13
14 struct Secv
15 {
16 int v, cnt;
17 short pos, prev, next;
18 };
19
20 const int MaxN = 10001;
21 Secv s[MaxN];
22 int first, last;
23
24 void Add(int val);
25 void Print();
26 int GetMax();
27 void DelSecv(int p);
28
29 int main()
30 {
31 int x;
32 fin >> p >> n;
33
34 for (int i = 1; i <= n; ++i)
35 {
36 fin >> x;
37 Add(x);
38 }
39
40 if (p == 1)
41 fout << n1 << ’\n’;
42 else
43 {
44 n2 = n;
45 while (true)
46 {
47 int p = GetMax(), p1, p2;
48
49 if ( !p || s[p].cnt <= 2 )
50 break;
51
52 n2 -= s[p].cnt;
53 p1 = s[p].prev;
54 p2 = s[p].next;
55
56 DelSecv(p);
57
58 while ( (p1 && p2) && (s[p1].v == s[p2].v) )
59 {
60 s[p1].cnt += s[p2].cnt;
61 DelSecv(p2);
62
63 if ( s[p1].cnt > 2 )
64 {
65 n2 -= s[p1].cnt;
66 int q = p1;
67 p1 = s[p1].prev;
68 DelSecv(q);
69 }
70
71 if ( p1 )
72 p2 = s[p1].next;
73 }
74 }
75
76 Print();
77 }
78
79 fin.close();
80 fout.close();
81 }
82
83 void Add(int val)
84 {
85 if ( last && s[last].v == val )
86 {
87 s[last].cnt++;
88 return;
CAPITOLUL 6. OJI 2015 122

89 }
90
91 Secv s1 {0, 0, 0, 0, 0 };
92
93 s1.v = val;
94 s1.cnt++; n1++;
95
96 if ( !first )
97 {
98 s1.prev = s1.next = 0;
99 first = last = 1;
100 s[first] = s1;
101 }
102 else
103 {
104 s[last].next = last + 1;
105 s1.prev = last;
106 s1.next = 0;
107 last++;
108 s[last] = s1;
109 }
110 }
111
112 void DelSecv(int p)
113 {
114 if ( p == first )
115 {
116 if ( p == last )
117 {
118 first = last = 0;
119 return;
120 }
121
122 first = s[p].next;
123 s[first].prev = 0;
124
125 return;
126 }
127
128 if ( p == last )
129 {
130 last = s[p].prev;
131 s[last].next = 0;
132 return;
133 }
134
135 int p1 = s[p].prev, p2 = s[p].next;
136 s[p1].next = p2;
137 s[p2].prev = p1;
138 }
139
140 int GetMax()
141 {
142 int pMax(0);
143 int cntMax = 0;
144
145 for (int p = first; p; p = s[p].next)
146 if ( s[p].cnt > cntMax )
147 cntMax = s[p].cnt, pMax = p;
148
149 return pMax;
150 }
151
152 void Print()
153 {
154 if ( !first )
155 fout << "0\n";
156 else
157 {
158 fout << n2 << ’\n’;
159
160 for (int p = first; p; p = s[p].next)
161 while ( s[p].cnt--)
162 fout << s[p].v << ’\n’;
163 }
164 }
CAPITOLUL 6. OJI 2015 123

Listing 6.1.3: arc3.cpp


1 /* prof. Sofia Vitelaru
2 Colegiul National "Fratii Buzesti" Craiova
3 */
4 #include <fstream>
5
6 using namespace std;
7
8 ifstream f("arc.in");
9 ofstream g("arc.out");
10
11 struct secv
12 {
13 int c,st,nr;
14 } v[10001];
15
16 int n,i,j,p,x,y,Max,poz,nr,t;
17
18 int main()
19 {
20 f>>p>>n;
21 f>>x;nr=1;
22
23 v[1].c=x;
24 v[1].st=1;
25 for(i=2;i<=n;i++)
26 {
27 f>>y;
28 if(x!=y)
29 {
30 v[nr].nr=i-v[nr].st;
31 nr++;
32 v[nr].c=y;
33 v[nr].st=i;
34 }
35
36 x=y;
37 }
38
39 v[nr].nr=n-v[nr].st+1;
40 if(p==1)
41 {
42 g<<nr;
43 return 0;
44 }
45
46 Max=0;
47 poz=0;
48 for(i=1;i<=nr;i++)
49 {
50 if(v[i].nr>=3&&v[i].nr>Max)
51 {
52 Max=v[i].nr;
53 poz=i;
54 }
55
56 t=t+v[i].nr;
57 }
58
59 while(1<2)
60 {
61 if(poz==0)
62 break;
63
64 if(poz>1 && poz<nr && v[poz-1].c==v[poz+1].c &&
65 v[poz-1].nr+v[poz+1].nr>=3)
66 {
67 v[poz-1].nr+=v[poz+1].nr;
68
69 for(j=poz+2;j<=nr;j++)
70 v[j-2]=v[j];
71
72 nr-=2;
73 poz--;
74 continue;
CAPITOLUL 6. OJI 2015 124

75 }
76
77 if(poz>1&& poz<nr && v[poz-1].c==v[poz+1].c)
78 {
79 v[poz-1].nr+=v[poz+1].nr;
80
81 for(j=poz+2;j<=nr;j++)
82 v[j-2]=v[j];
83
84 nr-=2;
85 }
86 else
87 {
88 for(j=poz+1;j<=nr;j++)
89 v[j-1]=v[j];
90
91 nr--;
92 }
93
94 Max=0;
95 poz=0;
96 t=0;
97 for(i=1;i<=nr;i++)
98 {
99 if(v[i].nr>=3&&v[i].nr>Max)
100 {
101 Max=v[i].nr;
102 poz=i;
103 }
104
105 t=t+v[i].nr;
106 }
107
108 }
109
110 g<<t<<’\n’;
111
112 for(i=1;i<=nr;i++)
113 for(j=1;j<=v[i].nr;j++)
114 g<<v[i].c<<"\n";
115
116 return 0;
117 }

Listing 6.1.4: arc4.cpp


1 ///prof. Gorea Claudiu-Cristian
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream fin ("arc.in");
7 ofstream fout("arc.out");
8
9 int caz,n,u,secv,x,i, v[10001],f[10001],st,dr,l,cauta;
10
11 int main()
12 {
13 fin>>caz;
14 fin>>n;
15 u=0;
16 secv=0;
17 for(i=1;i<=n;i++)
18 {
19 fin>>x;
20
21 if (u!=x)
22 {
23 secv++;
24 v[secv]=x;
25 f[secv]=1;
26 }
27 else
28 f[secv]++;
29
CAPITOLUL 6. OJI 2015 125

30 u=x;
31 }
32
33 if (caz==1)
34 fout<<secv<<endl;
35
36 if (caz==2)
37 {
38 l=0;u=0;
39 for(i=1;i<=secv;i++)
40 {
41 //fout<<v[i]<<" "<<f[i]<<endl;
42 if(f[i]>l) l=f[i],u=i;
43 }
44
45 i=u; /// plec de la cea mai lunga secventa...
46 while(l>=3)
47 {
48 if(f[i]>=3)
49 {
50 ///o pot elimina
51 v[i]=0;
52 n=n-f[i]; ///cate se sterg
53 f[i]=0;
54
55 st=i-1;
56 dr=i+1;
57 while(v[st]==0 && st>1)
58 st--;
59 while(v[dr]==0 && dr<secv)
60 dr++;
61
62 if (v[st]==v[dr]) ///verific lipirea
63 {
64 f[dr]=f[dr]+f[st];
65 f[st]=0;
66 v[st]=0;
67 i=dr;///ce incerc sa scot
68 cauta=0;
69 }
70 else
71 cauta=1;
72 }
73 else
74 cauta=1;
75
76 if (cauta)
77 {
78 ///caut noua secv maxima
79 l=0;
80 u=0;
81 for(i=1;i<=secv;i++)
82 {
83 //fout<<v[i]<<" "<<f[i]<<endl;
84 if(f[i]>l) l=f[i],u=i;
85 }
86
87 i=u; /// plec de la cea mai lunga secventa...
88 }
89 }
90
91 fout<<n<<endl;
92
93 for(i=1;i<=secv;i++)
94 for(x=1;x<=f[i];x++)
95 fout<<v[i]<<endl;
96 }
97
98 return 0;
99 }

6.1.3 *Rezolvare detaliat 


CAPITOLUL 6. OJI 2015 126

6.2 defrag
Problema 2 - defrag 90 de puncte
Discul dur (hard disk) este un dispozitiv utilizat pentru stocarea datelor. Stocarea se face pe
o suprafaµ  magnetic  dispus  pe platane rotunde metalice. Pe un platan, datele sunt organizate
în piste ³i sectoare, iar zona aat  la intersecµia dintre o pist  ³i un sector poart  denumirea de
cluster.
Un cluster poate avea dou  st ri: liber, dac  nu conµine date, sau ocupat, atunci când conµine
date.
Un platan se nume³te defragmentat dac  toµi clusterii ocupaµi de pe ecare pist  sunt a³ezaµi
în ordine consecutiv . Defragmentarea se realizeaz  prin mutarea unor clusteri ocupaµi ³i are rolul
de a mic³ora timpul de acces la date. Mutarea unui cluster reprezint  transferul datelor de la un
cluster ocupat c tre un cluster liber de pe aceea³i pist .

Figura 6.1: defrag

Cerinµe
Cunoscând num rul de piste P ³i de sectoare S al unui platan, num rul ³i poziµia clusterilor
ocupaµi, s  se scrie un program care determin :
1. num rul de piste care au toµi clusterii liberi;
2. num rul minim de mut ri de clusteri, pentru ecare pist  în parte, astfel încât platanul s 
devin  defragmentat.
Date de intrare
Pe prima linie a ³ierului de intrare defrag.in se g se³te num rul natural V a c rui valoare
poate  doar 1 sau 2.
Pe a doua linie a ³ierului de intrare se g sesc dou  numere naturale P ³i S , separate printr-un
spaµiu, cu semnicaµia din enunµ.
A treia linie conµine un num r natural C reprezentând num rul total de clusteri ocupaµi de pe
platan, iar pe ecare din urm toarele C linii se g se³te câte o pereche de valori pi ³i si , 1 & i & C ,
separate printr-un spaµiu, reprezentând pista, respectiv sectorul unde se a  ecare cluster ocupat.
Date de ie³ire
Fi³ierul de ie³ire este defrag.out.
Dac  valoarea lui V este 1 atunci ³ierul de ie³ire va conµine pe prima linie un num r natural
ce reprezint  num rul de piste care au toµi clusterii liberi.
Dac  valoarea lui V este 2 atunci ³ierul de ie³ire va conµine pe prima linie P numere naturale
notate Mi , 1 & i & P , separate prin câte un singur spaµiu, unde Mi reprezint  num rul minim de
mut ri de clusteri, dintre cei aaµi pe pista i, astfel încât pe pista i clusterii ocupaµi s  se g seasc 
într-o ordine consecutiv .
Restricµii ³i preciz ri
a 1 & P & 100
a 1 & S & 360
a 1&C &P ˜S
a pistele sunt numerotate de la 1 la P începând cu pista exterioar ;
a sectoarele sunt numerotate de la 1 la S în sensul acelor de ceasornic începând cu sectorul 1;
a dac  o pist  are toµi clusterii liberi, atunci valoarea cerut  la a doua cerinµ  este 0;
a 20% din teste vor avea valoarea V 1, iar 80% din teste vor avea valoarea V 2.
CAPITOLUL 6. OJI 2015 127

Exemple
defrag.in defrag.out Explicaµii
1 1 Datele corespund gurilor anterioare:
48 V = 1, deci se rezolv  NUMAI prima cerinµ .
10 a Num rul de piste P = 4 , num rul de sectoare S = 8
11 a Num rul total de clusteri ocupaµi este C = 10 (cei marcaµi cu
13 negru)
15 a Pe prima pist  sunt 4 clusteri ocupaµi, în sectoarele 1, 3, 5 si
17 7.
45 a Pe a doua pist  sunt 2 clusteri ocupaµi, în sectoarele 2 ³i 4.
41 a Pe a treia pist  nu sunt clusteri ocupaµi.
46 a Pe a patra pist  sunt 4 clusteri ocupaµi, în sectoarele 1, 5, 6
48 ³i 8.
22 O singur  pist  are toµi clusterii liberi, pista num rul 3, deci
24 valoarea cerut  este 1;
2 2101 Datele corespund gurilor anterioare:
48 V = 2, deci se rezolv  NUMAI a doua cerinµ .
10 a Pe prima pist  sunt necesare minim dou  mut ri de clusteri
11 pentru ca toµi clusterii ocupaµi s  se g seasc  într-o ordine con-
13 secutiv , deci valoarea cerut  este 2.
15 a Pe a doua pist  este sucient  o singur  mutare de cluster,
17 pentru ca toµi clusterii ocupaµi s  se g seasc  într-o ordine con-
45 secutiv , deci valoarea cerut  este 1.
41 a Pe a treia pist  nu sunt clusteri ocupaµi, deci valoarea cerut 
46 este 0.
48 a Pe a patra pist  este sucient  o singur  mutare de cluster,
22 pentru ca toµi clusterii ocupaµi s  se g seasc  într-o ordine con-
24 secutiv , deci valoarea cerut  este 1.

Timp maxim de executare/test: 0.2 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 10 KB

6.2.1 Indicaµii de rezolvare


prof. Che³c  Ciprian - Liceul Tehnologic Costin Neniµescu Buz u

Pentru rezolvarea primei cerinµe se determin  câµi clusteri sunt ocupaµi pe ecare pist . Se
poate utiliza în acest scop o matrice având num rul de linii egal cu num rul de piste ³i num rul
de coloane egal cu num rul de sectoare al platanului. La ecare citire de cluster ocupat se seteaz 
în matrice poziµia dat  de pista ³i sectorul clusterului pe 1 (iniµial ind pe 0). în acela³i timp se
contorizeaz  în primul element de pe ecare linie num rul de clusteri ocupaµi. Prin parcurgerea
elementelor de pe prima coloan  a matricei se determin  valoarea cerut  la prima cerinµ , prin
sc dere faµ  de num rul total de sectoare.
Pentru a doua cerinµ  se procedeaz  astfel:
Imaginându-ne c  s-a realizat mutarea unor clusteri ³i s-a obµinut o zon  contigu , aceast 
zon  va avea lungimea egal  cu num rul de clusteri ocupaµi pe ecare pist . Aceast  zon  poate
începe în orice poziµie a pistei (de la 1 la ns), îns  numai unele dintre aceste secvenµe au un num r
minim de clusteri liberi.
A³adar se preia num rul de clusteri ocupaµi pe ecare linie, calculat anterior, pe care s -l
not m cu ncoi (num r clusteri ocupaµi pe pista i) ³i se analizeaz  toate secvenµele de câte ncoi
elemente, începând cu poziµia 1 pân  la o poziµie în care zona se termin  pe ultimul sector, ³i se
determin  num rul minim de clusteri de 0 pentru toate secvenµele. Aceast  valoare este valoarea
cerut  la cerinµa 2.
O soluµie care implementeaz  algoritmul descris mai sus obµine aproximativ jumatate din
punctaj, deoarece nu µine cont de circularitatea a³ez rii clusterilor.
Pentru a rezolva ³i circularitatea a³ez rii clusterilor se realizeaz  o copie a matricii clusterilor pe
„orizontal  prin dublarea num rului de sectoare. în acest mod se poate testa ³i dac  secvenµele
cu clusteri consecutivi pot începe chiar de pe ultimul sector, formând astfel o zon  contigu  cu
clusteri aaµi în primele sectoare.
CAPITOLUL 6. OJI 2015 128

Pentru optimizarea algoritmului descris anterior, se poate proceda astfel: se determin  num rul
de clusteri liberi din secvenµa care începe pe primul sector ³i are lungimea ncoi, apoi la ecare
nou  secvenµa de aceea³i lungime mutat  cu o poziµie c tre dreapta (sau stânga),se adun  sau
se scade câte un element în funcµie de ce se întâmpl  la capete, deoarece celelalte elemente din
secvenµ  nu se modic .

prof. Carmen Popescu - Colegiul Naµional "Gheorghe Laz r" Sibiu

prof. Gorea Claudiu - Colegiul National „Al.Papiu - Ilarian Tg. Mure³

La citirea datelor se construie³te o matrice cu p linii ³i s coloane cu aij  1 dac  clusterul


j din pista i este ocupat aij  0 în caz contrar
În plus în ai0 memor m câte clustere sunt ocupate pe pista i (ai0 = suma elementelor
din linia i)
R spunsul la punctul a) se obµine simplu num rând pentru câte linii avem ai0 0
Pentru punctul b) pentru ecare linie practic dup  defragmentare vom obµine o zona compact 
de lungime k ai0, nu ne intereseaza unde este aceasta poziµionat . Aceast  zon  este normal
s  se aleag  în acea zona din pista unde sunt cele mai multe clustere ocupate. De aceea vom calcula
sumele

b1 ai1  ai2  ...  aik 


b2 ai2  ai3  ...  aik  1
...
bs  k  1 ais  k  1  ais  k  2  ...  ain

³i

c1 ais  k  2  ais  k  3  ...  ais  ai1


c2 ais  k  3  ais  k  4  ...  ais  ai1  ai2
...
ck  1 ais  ai1  ai2  ...  aik  1

Se observ  c 
bj  bj  1  aij  1  aij  k  1 pentru j 1, 2, ...., s  k  1
³i
c1 bs  k  1  ais  k  1  ai1
cj  cj  1  ais  k  j   aij 
pentru j 2, 3, ..., k  1
Se poate observa u³or c  nu avem nevoie de vectorii b ³i c, ci doar de o variabila simpl  în care
memor m suma curent  ³i pe care o actualiz m la ecare pas.
R spunsul la a doua cerinµ  este k - maximul dintre rb1, b2, ..., bs  k  1, c1, c2, ..., ck  1x

6.2.2 Cod surs 

Listing 6.2.1: defrag1.cpp


1 // prof. Chesca Ciprian
2 // Liceul Tehnologic "Costin Nenitescu" Buzau
3
4 #include <iostream>
5 #include <fstream>
6
7 #define pmax 101
8 #define smax 361
9
10 using namespace std;
11
12 short hdd[pmax][2*smax];
13
14 // ns = numar sectoare
15 // np = numar piste
16 // nso = numar sectoare ocupate
17 // xs = sector curent citit
CAPITOLUL 6. OJI 2015 129

18 // xp = pista curenta citita


19 // po = piste ocupate
20
21 int main()
22 {
23 int v,c,np,ns,xp,xs,i,j,k,po,min,s,nso;
24
25 ifstream f("defrag.in");
26 ofstream g("defrag.out");
27
28 // citesc configuratia HDD
29 f>>v>>np>>ns;
30
31 // citesc cate clustere sunt ocupate
32 f>>c;
33
34 // citesc clusterele ocupate
35 for(i=1;i<=c;i++)
36 {
37 f>>xp>>xs;
38 hdd[xp][xs]=1;
39
40 // extindere matrice
41 hdd[xp][xs+ns]=1;
42
43 hdd[xp][0]++;
44 }
45
46 if (v==1)
47 {
48 // cate piste nu au clustere ocupate
49 po=0;
50 for(i=1;i<=np;i++)
51 if (hdd[i][0])
52 po++;
53 g<<np-po;
54 }
55
56 if (v==2)
57 {
58 // determin numarul minim de mutari de clusteri, pe fiecare pista,
59 // pentru a obtine zone contigue
60 for(i=1;i<=np;i++)
61 {
62 min=ns;
63 nso=hdd[i][0];
64
65 // j = pozitie inceput de secventa
66 for(j=1;j<=ns;j++)
67 {
68 s=0;
69 for(k=j;k<j+nso;k++)
70 if (hdd[i][k]==0) s++;
71
72 if (s<min)
73 min=s;
74 }
75
76 g<<min<<" ";
77 }
78 }
79
80 g<<"\n";
81 f.close();
82 g.close();
83
84 return 0;
85 }

Listing 6.2.2: defrag2.cpp


1 // prof. Chesca Ciprian
2 // Liceul Tehnologic "Costin Nenitescu" Buzau
3
4 #include <iostream>
CAPITOLUL 6. OJI 2015 130

5 #include <fstream>
6
7 #define pmax 101
8 #define smax 361
9
10 using namespace std;
11
12 short hdd[pmax][2*smax];
13
14 // ns = numar sectoare
15 // np = numar piste
16 // nso = numar sectoare ocupate
17 // xs = sector curent citit
18 // xp = pista curenta citita
19 // po = piste ocupate
20
21 int main()
22 {
23 int v,c,np,ns,xp,xs,i,j,po,min,s,nso;
24
25 ifstream f("defrag.in");
26 ofstream g("defrag.out");
27
28 // citesc configuratia HDD
29 f>>v>>np>>ns;
30
31 // citesc cate clustere sunt ocupate
32 f>>c;
33
34 // citesc clusterele ocupate
35 for(i=1;i<=c;i++)
36 {
37 f>>xp>>xs;
38 hdd[xp][xs]=1;
39
40 // extindere matrice
41 hdd[xp][xs+ns]=1;
42
43 hdd[xp][0]++;
44 }
45
46 if (v==1)
47 {
48 // cate piste nu au clustere ocupate
49 po=0;
50 for(i=1;i<=np;i++)
51 if (hdd[i][0])
52 po++;
53
54 g<<np-po;
55 }
56
57 if (v==2)
58 {
59 // determin numarul minim de mutari de clusteri, pe fiecare pista,
60 // pentru a obtine zone contigue
61 for(i=1;i<=np;i++)
62 {
63 min=ns;
64 nso=hdd[i][0];
65
66 // j = pozitie inceput de secventa
67 s=0;
68 for(j=1;j<=nso;j++)
69 if (hdd[i][j]==0)
70 s++;
71
72 if (s<min) min=s;
73
74 for(j=2;j<=ns;j++)
75 {
76 if (hdd[i][j-1]==0) s--;
77 if (hdd[i][j+nso-1]==0) s++;
78 if (s<min) min=s;
79 }
80
CAPITOLUL 6. OJI 2015 131

81 g<<min<<" ";
82 }
83 }
84
85 g<<"\n";
86 f.close();
87 g.close();
88
89 return 0;
90 }

Listing 6.2.3: defrag3.cpp


1 // prof. Galatan Constantin
2 // Colegiul National "Liviu Rebreanu" Bistrita
3
4 #include <iostream>
5 #include <fstream>
6 #include <vector>
7 #include <algorithm>
8 #include <bitset>
9 #include <cassert>
10
11 using namespace std;
12
13 ifstream fin("defrag.in");
14 ofstream fout("defrag.out");
15
16 const int Pmax = 100, Smax = 360;
17
18 int V, P, S, C;
19
20 bitset<Smax> d[Pmax];
21 bitset<Pmax> pista;
22
23 int cnt[Pmax], sp[Smax];
24 int pisteLibere;
25
26 int main()
27 {
28 fin >> V;
29 fin >> P >> S >> C;
30
31 pisteLibere = P;
32
33 int p, s;
34 for (int c = 0; c < C; ++c )
35 {
36 fin >> p >> s;
37 // assert(p >= 1);
38 // assert(s >= 1);
39 p--;
40 s--;
41 cnt[p]++;
42 d[p][s] = true;
43 if ( !pista[p] )
44 {
45 pista[p] = true;
46 pisteLibere--;
47 }
48 }
49
50 if (V == 1)
51 fout << pisteLibere << ’\n’;
52 else
53 {
54 int k, sum;
55 for (int p = 0; p < P; ++p)
56 {
57 k = cnt[p];
58 if ( k == 0 )
59 {
60 fout << 0 << ’ ’;
61 continue;
62 }
CAPITOLUL 6. OJI 2015 132

63
64 sum = 0;
65 for (int i = 0; i < k; ++i)
66 sum += d[p][i];
67
68 sp[k - 1] = sum;
69 for (int i = k; i < S + k; ++i )
70 sp[i % S] = sp[(i - 1)% S] + d[p][i % S] - d[p][i - k];
71
72 int maxim = 0;
73 for (int i = 0; i < S; ++i)
74 maxim = max(maxim, sp[i]);
75
76 fout << k - maxim << ’ ’;
77 }
78 }
79
80 fin.close();
81 fout.close();
82 }

Listing 6.2.4: defrag4.cpp


1 ///prof. Gorea Claudiu-Cristian
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream fin ("defrag.in");
7 ofstream fout("defrag.out");
8
9 long long caz,piste,sect,c,i,viz[101],a[101][722],gol,x,y,j,cel,mut;
10
11 int main()
12 {
13 fin>>caz;
14 fin>>piste>>sect>>c;
15
16 for(i=1;i<=c;i++)
17 {
18 fin>>x>>y;
19 a[x][y]=1;
20 viz[x]++;
21 }
22
23 gol=0;
24 for(i=1;i<=piste;i++)
25 gol=gol+(viz[i]==0);
26
27 if (caz==1) fout<<gol<<endl;
28
29 if (caz==2)
30 {
31 ///obtinem nr consecutive prin mutare
32 for(x=1;x<=piste;x++)
33 if (viz[x]<=1 || viz[x]>=sect-1)
34 fout<<"0 ";
35 else
36 {
37 i=x;
38 cel=viz[x];
39 for(j=1;j<=sect;j++)
40 {
41 a[i][j+sect]=a[i][j];
42 a[i][j]=a[i][j-1]+a[i][j];//, fout<<a[i][j]<<"_";
43 }
44
45 for(j=sect+1;j<=2*sect;j++)
46 a[i][j]=a[i][j-1]+a[i][j];//, fout<<a[i][j]<<"_";
47
48 mut=0;
49 for(j=1;j<2*sect-cel;j++)
50 if(a[i][j+cel]-a[i][j]>mut ) mut=a[i][j+cel]-a[i][j];
51
52 fout<<cel-mut<<’ ’;
CAPITOLUL 6. OJI 2015 133

53 }
54 }
55
56 return 0;
57 }

Listing 6.2.5: defrag5.cpp


1 // prof. Carmen Popescu
2 // Colegiul National "Gheorghe Lazar" Sibiu
3
4 #include <fstream>
5
6 using namespace std;
7
8 ifstream f("defrag.in");
9 ofstream g("defrag.out");
10
11 int a[101][361];
12
13 int main()
14 {
15 int v,p,s,c,x,y,i,j,m,b,k;
16
17 f>>v;
18 f>>p>>s;
19 f>>c;
20
21 for (i=1;i<=c;i++)
22 {
23 f>>x>>y;
24 a[x][y]=1;
25 a[x][0]++;
26 }
27
28 if (v==1)
29 {
30 j=0;
31 for (i=1;i<=p;i++)
32 if (a[i][0]==0)
33 j++;
34 g<<j<<"\n";
35 g.close();
36 }
37 else
38 for (i=1;i<=p;i++)
39 {
40 k=a[i][0];
41 if (k==0 || k==1 || k==s || k==s-1)
42 g<<0<<" ";
43 else
44 {
45 b=0;
46 for (j=1;j<=k;j++)
47 b=b+a[i][j];
48
49 m=b;
50 for (j=2;j<=s-k+1;j++)
51 {
52 b=b-a[i][j-1]+a[i][j+k-1];
53 if (b>m)
54 m=b;
55 }
56
57 // zonele de la final de pista care se completeaza
58 // la inceptul pistei
59 for (j=1;j<=k-1;j++)
60 {
61 b=b-a[i][s-k+j]+a[i][j];
62 if (b>m)
63 m=b;
64 }
65
66 g<<k-m<<" ";
67 }
CAPITOLUL 6. OJI 2015 134

68
69 }
70
71 g<<"\n";
72 g.close();
73 return 0;
74 }

Listing 6.2.6: defrag6.cpp


1 // prof. Sofia Vitelaru
2 // Colegiul National "Fratii Buzesti" Craiova
3 #include <fstream>
4
5 using namespace std;
6
7 ifstream f("defrag.in");
8 ofstream g("defrag.out");
9
10 int a[101][801],fr[101],n,m,i,j,v,p,s,x,y,c,nr;
11
12 int main()
13 {
14 f>>v>>p>>s>>c;
15 for(i=1;i<=c;i++)
16 {
17 f>>x>>y;
18 a[x][y]=1;
19 a[x][y+s]=1;
20
21 fr[x]++;
22 }
23
24 for(i=1;i<=p;i++)
25 for(j=1;j<=2*s;j++)
26 {
27 a[i][j]+=a[i][j-1];
28
29 }
30
31 nr=0;
32 if(v==1)
33 {
34 for(i=1;i<=p;i++)
35 if(fr[i]==0)
36 nr++;
37 g<<nr;
38 return 0;
39 }
40
41 for(i=1;i<=p;i++)
42 {
43 nr=0;
44 if(fr[i]==0)
45 {
46 g<<0<<" ";
47 continue;
48 }
49
50 int min=fr[i];
51 for(j=1;j<=2*s-fr[i]-1;j++)
52 if(fr[i]-(a[i][j+fr[i]-1]-a[i][j-1])<min)
53 min=fr[i]-(a[i][j+fr[i]-1]-a[i][j-1]);
54
55 g<<min<<" ";
56 }
57
58 return 0;
59 }

6.2.3 *Rezolvare detaliat 


Capitolul 7

OJI 2014

7.1 cool
Problema 1 - cool 100 de puncte
Se consider  un ³ir A format din N elemente naturale nenule. Numim secvenµ  de lungime K
a ³irului A orice succesiune de elemente consecutive din ³ir de forma Ai , Ai1 , ..., AiK 1 .
O secvenµ  o numim secvenµ  cool dac  elementele care o compun sunt distincte ³i pot 
rearanjate astfel încât s  alc tuiasc  o secvenµ  continu  de numere consecutive.
De exemplu, considerând ³irul A 3, 1, 6, 8, 4, 5, 6, 7, 4, 3, 4, atunci secvenµa 8, 4, 5, 6, 7 este
o secvenµ  cool deoarece conµine elemente distincte ce pot  rearanjate astfel încât s  alc tuiasc 
³irul de numere consecutive 4, 5, 6, 7, 8, pe când secvenµele 4, 3, 4, 6, 7, 4, 3 nu sunt considerate
secvenµe cool.
Cerinµe
Fiind dat un ³ir de N numere naturale nenule se cer urm toarele:
1. Pentru o valoare dat  K s  se verice dac  secvenµa A1 , A2 , ..., AK este secvenµ  cool.
Dac  secvenµa este cool, atunci se va a³a cea mai mare valoare ce aparµine secvenµei. Dac 
secvenµa nu este cool, atunci se va a³a num rul elementelor distincte din secvenµa A1 , A2 , ...,
AK , adic  num rul elementelor care apar o singur  dat .
2. Lungimea maxim  a unei secvenµe cool ³i num rul secvenµelor cool de lungime maxim .

Date de intrare
Fi³ierul de intrare cool.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, desp rµite
printr-un spaµiu, dou  numere naturale N K . Pe urm toarea linie se g sesc N numere întregi,
separate prin câte un spaµiu, ce reprezint  elementele ³irului.

Date de ie³ire
Dac  valoarea lui p este 1, atunci se va rezolva numai punctul 1 din cerinµ . În acest caz, ³ierul
de ie³ire cool.out va conµine pe prima linie un num r natural, num r ce reprezint  conform cerinµei
1, maximul secvenµei A1,A2, ...,AK , dac  secvenµa este secvenµ  cool, sau num rul elementelor
distincte din secvenµ , dac  aceasta nu este secvenµ  cool.
Dac  valoarea lui p este 2, se va rezolva numai punctul 2 din cerinµ . În acest caz, ³ierul de
ie³ire cool.out va avea dou  linii. Prima linie va conµine un num r natural nenul ce reprezint 
lungimea maxim  a unei secvenµe cool, iar urm toarea linie un num r natural nenul ce reprezint 
num rul de secvenµe cool care au lungimea maxim .

Restricµii ³i preciz ri
a 1 & N & 5000
a 2 & K & 1000
a 1 & Ai & 1000, 1 & i & N
a Pentru 30% dintre teste N & 1000
a Pentru rezolvarea primei cerinµe se acord  20% din punctaj, iar pentru cerinµa a doua se
acord  80% din punctaj.

135
CAPITOLUL 7. OJI 2014 136

Exemple
col.in col.out Explicaµii
1 7 Atenµie! Pentru acest test se rezolv  doar cerinµa 1.
7 4 Secvenµa 6 4 5 7 este cool.
6 457835 Valoarea maxim  din secvenµ  este 7
1 2 Atenµie! Pentru acest test se rezolv  doar cerinµa 1.
7 6 Secvenµa 6 4 5 7 5 4 nu este secvenµ  cool. Num rul
6 457543 valorilor distincte din secvenµ  este 2. Valorile distincte
sunt: 6,7
2 5 Atenµie! Pentru acest test se rezolv  doar cerinµa 2.
11 4 2 Cele dou  secvenµe cool de lungime maxim  5 sunt:
74568457432 74568
68457

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

7.1.1 Indicaµii de rezolvare


prof. Eugen Nodea - Colegiul Naµional Tudor Vladimirescu, Târgu Jiu

Cerinµa 1. ( 20 puncte)
Printr-o simpl  vericare a frecvenµei de apariµie a primelor k numere se obµine r spunsul
a³teptat.
Cerinµa 2.
O soluµie brute-force obµine 25 p.
O soluµie bazat  pe set(stl) obµine 40  50 p.
Soluµia 1. ( 80 puncte) (Eugen Nodea)
Rezolvarea cerinµei necesit  câteva observaµii.
Fie secvenµa ce conµine k numere consecutive distincte:
x  1, x  2, ..., x  k
Observ m c :
1) min x  1, max x  k => max  min k1
2) elementele sunt distincte
3) relaµia 1) este adev rat  indiferent de ordinea elementelor
A³adar,
- pentru orice secvenµa care începe pe poziµia i si se termina pe poziµia j se va determina
minimul si maximul
- secvenµa este cool daca respecta relaµiile 1) si 2)
Astfel, complexitatea rezolv rii subpunctului b) poate  redus  la O N ˜ k  amortizat.
Soluµia 2. ( 80 puncte) (Constantin Galatan)
Pentru secvenµa curent  cuprins  între poziµiile i ³i j , se menµine un ³ir de contoare pentru
valorile din secvenµ . La ad ugarea unui nou element aj  în secvenµ  se incrementeaz  valoarea
contorului corespunz tor: cntaj   , iar la eliminarea unui element ai din secvenµ , contorul
se decrementeaz : cntai  . Se reµin de asemenea maximul ³i minimul valorilor din secvenµ 
în amax ³i amin. La ecare pas se veric  dac  amax - amin == j - i ³i c  nicio valoare din
2
secvenµ  nu are contorul mai mare decât 1. Complexitatea soluµiei este O n 

7.1.2 Cod surs 

Listing 7.1.1: CC2_cool.cpp


1 // sursa de 100 puncte cu sume partiale si urmatoarele verificari :
2 // - daca primul element este egal cu ultimul element din secventa curenta
3 // - daca numerele din secventa se repeta de mai multe ori
4 // prof. Ciprian Chesca
5
6 #include <fstream>
7 #define nmax 5001
8 #define lmax 1001
CAPITOLUL 7. OJI 2014 137

9
10 using namespace std;
11
12 int w[nmax],v[lmax];
13
14 int main()
15 {
16 int n,a[nmax],ok,nrsec=0,lsec=1,min,max,i,j,s,k,test,p,d;
17
18 ifstream f("cool.in");
19 ofstream g("cool.out");
20
21 f>>test;
22 f>>n>>p;
23 for(i=1;i<=n;i++)
24 f>>a[i];
25
26 // subpunctul a
27 if (test==1)
28 {
29 max=a[1];min=a[1];
30 for(i=1;i<=p;i++)
31 {
32 if (a[i]>max) max=a[i];
33 if (a[i]>max) max=a[i];
34 v[a[i]]=0;
35 }
36
37 for(i=1;i<=p;i++)
38 v[a[i]]++;
39 d=0;
40 for(i=1;i<=p;i++)
41 if (v[a[i]]==1)
42 d++;
43
44 if (d==p&&max-min==p-1)
45 g<<max<<"\n";
46 else
47 g<<d<<"\n";
48 }
49
50 // subpunctul 2
51 if (test==2)
52 {
53 w[1]=a[1];
54 for(i=2;i<=n;i++)
55 w[i]=w[i-1]+a[i];
56
57 for(i=1;i<=n-1;i++)
58 {
59 min=a[i];
60 max=a[i];
61 for(j=i+1;j<=n;j++)
62 {
63 if (a[j]<min) min=a[j];
64 if (a[j]>max) max=a[j];
65 if (a[j]==a[i]) break;
66 if (j-i==max-min)
67 {
68 s=w[j]-w[i-1];
69 if (s==max*(max+1)/2-min*(min-1)/2)
70 ok=1;
71 else
72 ok=0;
73
74 if (ok)
75 {
76 for(k=i;k<=j;k++)
77 v[a[k]]=0;
78
79 for(k=i;k<=j;k++)
80 {
81 v[a[k]]++;
82 if (v[a[k]]>1)
83 {
84 ok=0;
CAPITOLUL 7. OJI 2014 138

85 break;
86 }
87 }
88
89 // daca este secventa cool verific lungimea ei
90 if (ok)
91 {
92 if (max-min+1>lsec)
93 {
94 lsec=max-min+1;
95 nrsec=1;
96 }
97 else
98 if (max-min+1==lsec)
99 nrsec++;
100 }
101 }
102 }
103 }
104 }
105
106 g<<lsec<<"\n"<<nrsec<<"\n";
107 }
108
109 f.close();
110 g.close();
111
112 return 0;
113 }

Listing 7.1.2: cool_eugen_fs.cpp


1 // solutie 100 p - Eugen Nodea
2 # include <fstream>
3 # include <cstring>
4
5 # define NMax 5003
6 # define Nmax 1003
7
8 using namespace std;
9
10 ifstream f("cool.in");
11 ofstream g("cool.out");
12
13 int a[NMax], ap[Nmax];
14 int n, nr, Max, k;
15
16 void cool()
17 {
18 int i, j, k, min, max;
19 for (i=1; i < n; ++i)
20 {
21 min = a[i];
22 max = a[i];
23 ap[a[i]] = 1;
24 for (j=i+1; j<=n; ++j)
25 {
26 if (ap[a[j]]) break;
27 ap[a[j]] = 1;
28 if(a[j] < min) min = a[j];
29 if(a[j] > max) max = a[j];
30 k = j - i + 1;
31 if(max - min == k - 1)
32 {
33 if (k > Max) Max = k, nr = 1;
34 else if (k == Max) ++nr;
35 }
36 }
37
38 memset(ap, 0, sizeof(ap));
39 }
40 }
41
42 int main()
43 {
CAPITOLUL 7. OJI 2014 139

44 int i, p, Min = 1001, nr_dist = 0;


45
46 f >> p >> n >> k;
47 for (i=1; i<=n; ++i)
48 f >> a[i];
49
50 if (p == 1)
51 { //a
52 for (i=1; i<=k; ++i)
53 {
54 ap[a[i]]++;
55 if (a[i] < Min) Min = a[i];
56 if (a[i] > Max) Max = a[i];
57 }
58
59 for (i=Min; i<=Max; ++i)
60 if (ap[i] == 1) ++nr_dist;
61
62 if (nr_dist == k)
63 g << Max << "\n";
64 else
65 g << nr_dist << "\n";
66 }
67 else
68 { //b
69 cool();
70 g << Max << "\n" << nr <<"\n";
71 }
72
73 return 0;
74 }

Listing 7.1.3: cool_eugen_std.cpp


1 // solutie 100 p - Eugen Nodea
2 # include <cstdio>
3 # include <cstring>
4
5 # define NMax 5003
6 # define Nmax 1003
7
8 using namespace std;
9
10 int a[NMax], ap[Nmax];
11 int n, Max, nr;
12
13 void cool()
14 {
15 int i, j, k, min, max;
16 for (i=1; i < n; ++i)
17 {
18 min = a[i];
19 max = a[i];
20 ap[a[i]] = 1;
21 for (j=i+1; j<=n; ++j)
22 {
23 if (ap[a[j]]) break;
24 ap[a[j]] = 1;
25 if(a[j] < min) min = a[j];
26 if(a[j] > max) max = a[j];
27 k = j - i + 1;
28 if(max - min == k - 1)
29 {
30 if (k > Max) Max = k, nr = 1;
31 else if (k == Max) ++nr;
32 }
33 }
34
35 memset(ap, 0, sizeof(ap));
36 }
37 }
38
39 int main()
40 {
41 int i, k, p, Min = 1001, nr_dist = 0;
CAPITOLUL 7. OJI 2014 140

42
43 freopen ("cool.in", "r", stdin);
44 freopen ("cool.out","w", stdout);
45
46 scanf("%d", &p);
47 scanf("%d %d", &n, &k);
48 for (i=1; i<=n; ++i)
49 scanf("%d", &a[i]);
50
51 if (p == 1)
52 { //a
53 for (i=1; i<=k; ++i)
54 {
55 ap[a[i]]++;
56 if (a[i] < Min) Min = a[i];
57 if (a[i] > Max) Max = a[i];
58 }
59
60 for (i=Min; i<=Max; ++i)
61 if (ap[i] == 1) ++nr_dist;
62
63 if (nr_dist == k)
64 printf("%d\n", Max);
65 else
66 printf("%d\n", nr_dist);
67 }
68 else
69 { //b
70 cool();
71 printf("%d\n%d\n", Max, nr);
72 }
73
74 return 0;
75 }

Listing 7.1.4: cool_n2.cpp


1 /* Solutie 100 p
2 Complexitate O(n * n)
3 prof. Constantin Galatan
4 */
5 #include <fstream>
6 #include <cstring>
7 #include <algorithm>
8
9 using namespace std;
10
11 #define NMAX 1003
12 #define VMAX 5001
13
14 ifstream fin("cool.in");
15 ofstream fout("cool.out");
16
17 int cnt[NMAX], a[5001], nr[VMAX];
18 int T, K, n, maxK, minK = VMAX;
19 int L, Lmax = 1, nMaxCool, amin, amax, bad;
20
21 // bad - nr de valori cu contor > 1 in intervalul[i, j]
22
23 int main()
24 {
25 fin >> T >> n >> K;
26 for (int i = 0; i < n; ++i)
27 {
28 fin >> a[i];
29 if ( i < K )
30 {
31 maxK = max(maxK, a[i]), minK = min(minK, a[i]);
32 nr[a[i]]++;
33 }
34 }
35
36 if ( T == 1 )
37 {
38 bool cool = true; int distincte = 0;
CAPITOLUL 7. OJI 2014 141

39 for ( int x = minK; x <= maxK; ++x )


40 {
41 if ( nr[x] == 0 || nr[x] > 1 )
42 cool = false;
43 if ( nr[x] == 1 ) distincte++;
44 }
45
46 if ( cool )
47 fout << maxK << ’\n’;
48 else
49 fout << distincte << ’\n’;
50 }
51 else
52 {
53 for ( int i = 0; i < n; ++i )
54 {
55 cnt[a[i]]++, bad = 0;
56 amin = amax = a[i];
57 for ( int j = i + 1; j < n; ++j )
58 {
59 cnt[a[j]]++, L = j - i + 1;
60 if ( cnt[a[j]] > 1 ) bad++;
61
62 if ( amax < a[j] ) amax = a[j];
63 if ( amin > a[j] ) amin = a[j];
64
65 if ( bad || amax - amin != j - i )
66 continue;
67 if ( L > Lmax )
68 Lmax = L, nMaxCool = 1;
69 else
70 if ( L == Lmax ) nMaxCool++;
71 }
72
73 cnt[a[i]]--;
74 memset(cnt, 0, sizeof(cnt));
75 }
76
77 fout << Lmax << ’\n’ << nMaxCool << ’\n’;
78 }
79
80 return 0;
81 }

7.1.3 *Rezolvare detaliat 

7.2 pseudobil
Problema 2 - pseudobil 100 de
puncte
Suprafaµa plan  a unei mese de pseudo-biliard este format  din n  n celule p tratice cu
lungimea laturii egal  cu 1 (o unitate), lipite, dispuse pe n linii numerotate de la 1 la n ³i n
coloane, numerotate de la 1 la n. Pe mas  se a³eaz  K bile, ecare bil  g sindu-se în centrul unei
anumite celule a mesei. Un juc tor dore³te s  plaseze pe suprafaµa mesei un cadru p tratic având
lungimea diagonalei egal  cu D unit µi.
El trebuie s  r spund  la m întreb ri de forma: x y . Fiecare întrebare are semnicaµia: câte
bile se g sesc în interiorul sau pe laturile cadrului ?
Cadrul se plaseaz  astfel încât ecare colµ s  e poziµionat în centrul unei celule, colµurile
opuse s  se g seasc  pe aceea³i coloan , respectiv pe aceea³i linie, iar colµul de sus s  e plasat
în centrul celulei aat  pe linia x ³i coloana y .
Cerinµe
Cunoscând lungimea n a laturilor mesei, num rul m de întreb ri, num rul K de bile a³ezate
pe mas , poziµiile lor ³i lungimea D a diagonalei cadrului p tratic, se cere:
1. Num rul de celule care se vor g si în întregime în interiorul cadrului, dac  acesta se a³eaz 
pe suprafaµa mesei, conform descrierii de mai sus.
CAPITOLUL 7. OJI 2014 142

2. Câte un r spuns pentru ecare dintre cele m întreb ri.

Date de intrare
Fi³ierul de intrare pseudobil.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, K ³i D separate prin câte un spaµiu.
Pe ecare dintre urm toarele K linii, se g sesc câte dou  numere a ³i b (a, b & n) reprezentând
linia ³i coloana celulei în centrul c reia va  a³ezat  o bil .
Pe linia K  3 se g se³te un num r natural m.
Urm toarele m linii conµin câte dou  numere naturale x ³i y , reprezentând linia ³i coloana
celulei în centrul c reia se va plasa colµul de sus al cadrului.

Date de ie³ire
Dac  valoarea lui p este 1, se va rezolva numai punctul 1 din cerinµ . În acest caz, în ³ierul
de ie³ire pseudobil.out se va scrie un singur num r natural n1 , reprezentând num rul de celule
care se vor g si în întregime în interiorul cadrului.
Dac  valoarea lui p este 2, se va rezolva numai punctul 2 din cerinµ . În acest caz, ³ierul de
ie³ire pseudobil.out va conµine m linii. Pe ecare linie i se va scrie câte un num r natural n2 ,
reprezentând r spunsul pentru întrebarea i.

Restricµii ³i preciz ri
a 3 & n & 1500
a 1 & K & 55000
a 2 & D & n  1, D - num r par
a 1 & m & 100000
a Poziµiile cadrului sunt distince.
a Se garanteaz  pentru x ³i y valori pentru care cadrul este plasat în interiorul suprafeµei mesei
de pseudo-biliard.
a Pentru rezolvarea corect  a primei cerinµe se acord  20 de puncte, iar pentru cerinµa a doua
se acord  80 de puncte.
a Pentru primele 35% dintre testele care veric  cerinµa 2, m & 1000 ³i n & 500
a Pentru primele 75% din testele care veric  cerinµa 2, m & 10000 ³i n & 1000

Exemple

Figura 7.1: Pseudobil


CAPITOLUL 7. OJI 2014 143

Figura 7.2: Pseudobil

Timp maxim de executare/test: 1.0 secunde


Memorie: total 64 MB din care pentru stiv  16 MB
Dimensiune maxim  a sursei: 10 KB

7.2.1 Indicaµii de rezolvare


prof. Constantin G l µan, Colegiul Naµional Liviu Rebreanu, Bistriµa

Cerinµa 1 ( 20 puncte)
Se parcurg segmentele orizontale care conµin numai celule în interiorul p tratului.
Se observ  c  segmentul de lungime maxim  are nr D  1 celule. Segmentul aat imediat
de deasupra ³i cel de dedesupt au ecare câte nr  2 celule. Soluµia va 
nr1 nr  2 ˜ nr  2  2 ˜ nr  4  ...  2 ˜ 1

Cerinµa 2 ( 80 de puncte)
Soluµie O m ˜ n2 
Pentru ecare interogare, se parcurg orizontal toate segmentele formate din celule interioare
sau traversate pe diagonal  de c tre laturile p tratului.
Acest  soluµie obµine 30 de puncte din punctajul cerinµei 2.
Soluµie O m ˜ K  (Ciprian Che³c )
Se ³tie c  o dreapt  de ecuaµie ax  by  c 0 împarte planul în dou  semiplane cu proprietatea
c  toate punctele dintr-un semiplan veric  relaµia ax  by  c % 0 sau ax  by  c $ 0.
Notând vârfurile cadrului cu ABCD laturile acestuia formeaz  4 drepte : AB , CD respectiv
AD, BC . Putem considera c  o bila se g se³te în interiorul cadrului dac  bila se g se³te în
regiunea pozitiv  a semiplanului AB ³i regiunea negativ  a semiplanului CD ³i analog dac  bila
se g se³te în regiunea pozitiv  a semiplanului AD ³i regiunea negativ  a semiplanului BC .
Calculând în ce semiplan se g se³te ecare bil  în funcµie de poziµia cadrului se poate deduce
dac  bila este în interiorul cadrului sau pe una din laturi.
Acest  soluµie obµine 30 de puncte din punctajul cerinµei 2.
Soluµie O n2  m ˜ n
Pentru reducerea complexit µii la O n per interogare, se reµin sumele parµiale pentru ecare
coloan  a matricei.
Fie deci matricea a, cu aij  - num rul de bile aate pe coloana j , pân  la linia i.
Se parcurge ecare segment vertical aat în interiorul cadrului ³i se calculeaz  num rul de bile
ca ind ai2 j   ai1 j  unde i2 este linia pe care se g se³te ultima celul  (cea mai de jos), iar
i1 este linia anterioar  primei celule a segmentului.
Acest  soluµie obµine 60 de puncte din punctajul cerinµei 2.
CAPITOLUL 7. OJI 2014 144

Figura 7.3: Pseudobil

Soluµie O n2  m (Constantin G l µan)


Se poate r spunde la ecare întrebare în O 1 dac  se precalculeaz  în O n ˜ n sumele parµiale
în raport cu diagonalele.
În continuare vom numi diagonal  secundar  oricare diagonal  care e paralel  cu diagonala
secundar  ³i vom numi diagonal  principal  oricare diagonal  care e paralel  cu diagonala princi-
pal .
Denim matricile: su, sd, pu, pd cu semnicaµiile:
suij  - num rul de bile aate deasupra ³i pe diagonala secundar  i, pe coloanele 1..j . (up)
sdij  - num rul de bile aate sub ³i pe diagonala secundar  i, pe coloanele 1..j . (down)
spij  - num rul de bile aate deasupra ³i pe diagonala principal  i, pe coloanele 1..j . (up)
sdij  - num rul de bile aate sub ³i pe diagonala secundar  i, pe coloanele 1..j . (down)
Diagonalele secundare se numeroteaz  1, 2, ...2 ˜ n  1 începând cu cea de la celula 1, 1
Diagonalele principale se numeroteaz  1, 2, ...2 ˜ n  1 începând cu cea de la celula n, 1
În exemplele de mai jos, su54, pd54, sd75, pu75 reprezint  num rul de bile care
se g sesc în interiorul zonelor ha³urate.

Figura 7.4: Pseudobil

Figura 7.5: Pseudobil

Pentru a aa num rul de bile din interiorul cadrului plasat cu colµul superior în centrul celulei
CAPITOLUL 7. OJI 2014 145

x, y , se fac sume ³i diferenµe de sume parµiale. V  puteµi gândi la arii ³i la principiul includerii
³i excluderii.
Prin urmare, aria poligonului acoperit de cadru (num rul de bile acoperite) va  egal  cu aria
întregii suprafeµe, din care se scad ariile poligoanelor marcate cu ro³u, verde ³i albastru:

Figura 7.6: Pseudobil

Aceast  soluµie obµine - 80 de puncte, punctajul maxim pentru cerinµa 2.


Soluµie O n2  m (Eugen Nodea)
Cum putem transforma problema într-o problem  clasic ?
Iniµial avem o matrice ³i un cadru p tratic (romb) paralel cu diagonalele matricei.
o
Dar dac  rotim cu 45 matricea iniµial , atunci vom avea un romb în care este inclus un
cadru p trat.
S  privim gura al turat  ce descrie rotaµia:

Figura 7.7: Pseudobil

Matricea nou creat  are 2 ˜ N  1 ˜ 2 ˜ N  1 elemente.


Astfel problema se reduce la o problem  clasic :
Pentru o matrice p tratic  binar  (conµine valori de 0 ³i 1) s  se determine num rul de valori
de 1 aate în p tratul (rombul) de latura D, p trat pentru care se cunoa³te colµul dreapta sus.
Astfel, pentru ecare interogare se va r spunde în O 1.

7.2.2 Cod surs 

Listing 7.2.1: pseudobil.cpp


1 /* Solutie 100 puncte
2 Complexitate: O(m + n * n)
3 prof. Constantin Galatan
4 */
5 #include <iostream>
6 #include <fstream>
CAPITOLUL 7. OJI 2014 146

7 #include <vector>
8
9 using namespace std;
10
11 const int DIM = 1501;
12
13 ifstream fin("pseudobil.in");
14 ofstream fout("pseudobil.out");
15
16 typedef unsigned short M[2 * DIM][DIM];
17
18 M su, sd, pu, pd; // sec up, sec down, princ up, princ down
19 M a, b; // a[d][j] = 1 daca pe diagonala secundara d si coloana j
20 // este 1, b - acelasi lucru pentru diagonala principala d
21
22 int m, n, K, L, D, T, n1, n2;
23 unsigned short sc[DIM], ss[DIM], s[DIM]; // sume partiale pe coloane,
24 // suma dreptunghiului pana la col j
25 unsigned short sp1[DIM * 2], sp2[DIM * 2]; // suma pe diagonala curenta
26 // pana la coloana j
27
28 int main()
29 {
30 fin >> T >> n >> K >> D;
31 if ( T == 1 ) // rezolv numai cerinta 1)
32 {
33 int nr = D - 1;
34 n1 = nr;
35 while ( true )
36 {
37 nr -=2;
38 if ( nr < 0 ) break;
39 n1 += 2 * nr;
40 }
41
42 fout << n1 << ’\n’;
43 }
44 else // rezolv numai cerinta 2)
45 {
46 L = D / 2 + 1;
47 int d;
48 int x, y;
49
50 for ( int i = 0; i < K; ++i )
51 {
52 fin >> x >> y;
53 b[n + y - x][y] = 1;
54 a[x + y - 1][y] = 1;
55 s[y]++;
56 }
57
58 for ( int j = 1; j <= n; ++j )
59 ss[j] = ss[j - 1] + s[j];
60
61 for ( int d = 1; d < 2 * n; ++d )
62 {
63 for (int j = 0; j <= n; ++j )
64 sp1[j] = 0, sp2[j] = 0;
65
66 for ( int j = max(d - n + 1, 1); j <= min(d, n) ; ++j )
67 {
68 sp1[j] = sp1[j - 1] + a[d][j];
69 su[d][j] = su[d - 1][j] + sp1[j];
70
71 if ( j >= d ) su[d][j] += su[d - 1][j - 1];
72
73 sd[d][j] = ss[j] - su[d][j] + sp1[j];
74
75 sp2[j] = sp2[j - 1] + b[d][j];
76 pd[d][j] = pd[d - 1][j] + sp2[j];
77
78 if ( j >= d )
79 pd[d][j] += pd[d - 1][j - 1];
80
81 pu[d][j] = ss[j] - pd[d][j] + sp2[j];
82 }
CAPITOLUL 7. OJI 2014 147

83 }
84
85 int A, B, C, D, E, F;
86 fin >> m;
87
88 for ( int i = 0; i < m; ++i )
89 {
90 fin >> x >> y;
91
92 A = ss[y - L];
93 B = K - ss[y + L - 1];
94 if ( y > x + y - 2 )
95 C = su[x + y - 2][y - 1];
96 else
97 C = su[x + y - 2][y];
98
99 C -= su[x + y - 2][y - L];
100 D = pu[n - x + y + 1][y + L - 1] - pu[n - x + y + 1][y];
101
102 int X = x + 2 * (L - 1);
103 if ( y > n - X + y - 1)
104 E = pd[n - X + y - 1][y - 1];
105 else
106 E = pd[n - X + y - 1][y];
107
108 E -= pd[n - X + y -1][y - L];
109 F = sd[X + y ][y + L - 1] - sd[X + y ][y];
110 n2 = K - (A + B + C + D + E + F);
111
112 fout << n2 << ’\n’;
113 }
114 }
115
116 fin.close();
117 fout.close();
118 return 0;
119 }

Listing 7.2.2: pseudobil_EN_100.cpp


1 /* Solutie 100 puncte
2 Complexitate: O(n^2 + m)
3 prof. Eugen Nodea
4 */
5 # include <fstream>
6
7 # define NMax 1503
8
9 using namespace std;
10
11 ifstream f("pseudobil.in");
12 ofstream g("pseudobil.out");
13
14 int n, m, k, D, sol;
15 bool a[2*NMax-1][2*NMax-1];
16 int nr[2*NMax-1][2*NMax-1]; //suma patratelor de coord (1,1)-(i,j)
17
18 int main()
19 {
20 int i, j, x, y, T, x1, y1;
21
22 f >> T >> n >> k >> D;
23
24 for (i=1; i<=k; ++i)
25 f >> x >> y, a[x+y-1][n-x+y] = 1;
26
27 for (i=1; i<=2*n-1; ++i)
28 for (j=1; j<=2*n-1; ++j)
29 nr[i][j] = nr[i-1][j] + nr[i][j-1] - nr[i-1][j-1] + a[i][j];
30
31 if ( T == 1 )
32 { //1)
33 x = D / 2 - 1;
34 g << 2 * x * x + (D - 1) <<"\n";
35 }
CAPITOLUL 7. OJI 2014 148

36 else
37 { //2.
38 f >> m;
39 while (m--)
40 {
41 f >> x >> y;
42 x1 = x+y-1; y1 = n-x+y;
43 sol = nr[x1+D][y1] - nr[x1+D][y1-D-1] -
44 nr[x1-1][y1] + nr[x1-1][y1-D-1];
45
46 g << sol <<"\n";
47 }
48 }
49
50 return 0;
51 }

Listing 7.2.3: pseudobil_LS_100.cpp


1 //prof. Liliana Schiopu - C.N.F.B. Craiova
2
3 #include <stdio.h>
4 #include<stdlib.h>
5
6 using namespace std;
7
8 FILE *f=fopen("pseudobil.in","r");
9 FILE *g=fopen("pseudobil.out","w");
10
11 int k,d,n,m,x,y,x1,y1,i,j,n1,n2,p,t,x2,y2,x3,y3,x4,y4;
12 short a[3010][3010],op;
13
14 int main()
15 {
16 fscanf(f,"%d%d%d%d",&op,&n,&k,&d);
17 /*pentru cerinta 1. observam ca avem o progresie aritmetica
18 1,3,5,...,d-2,d-1,d-2,...,5,3,1 a carei suma este d^2/2-d+1
19 */
20 n1=(d*d)/2-d+1;
21 if(op==1)
22 {
23 fprintf(g,"%d\n",n1);
24 }
25 else
26 if(op==2)
27 {
28 for(i=1;i<=k;i++)
29 {
30 fscanf(f,"%d%d",&x,&y);
31 x1=n-y+x;
32 y1=x+y-1;
33 a[x1][y1]=1;
34 }
35
36 n1=2*n-1;
37 t=n;
38 p=0;
39 for(i=1;i<=n1;i++)
40 {
41 for(j=t-p;j<=t+p;j++)
42 a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
43
44 if(i<=n)
45 p++;
46 else
47 p--;
48 }
49
50 fscanf(f,"%d",&m);
51
52 for(i=1;i<=m;i++)
53 {
54 fscanf(f,"%d%d",&x,&y);
55 x1=n-y+x;
56 y1=x+y-1;
CAPITOLUL 7. OJI 2014 149

57 x2=n-(y+d/2)+x+d/2;
58 y2=x+d/2+y+d/2-1;
59 x3=n-y+x+d;
60 y3=x+d+y-1;
61 x4=n-(y-d/2)+x+d/2;
62 y4=x+d/2+(y-d/2)-1;
63 n2=a[x3][y3]-a[x2-1][y2]-(a[x4][y4-1]-a[x1-1][y1-1]);
64 fprintf(g,"%d\n",n2);
65 }
66 }
67
68 fclose(f);
69 fclose(g);
70 return 0;
71 }

Listing 7.2.4: pseudobil2.cpp


1 /* Solutie 100 puncte
2 Complexitate: O(n * n + m )
3 prof. Constantin Galatan
4 */
5 #include <fstream>
6
7 using namespace std;
8
9 ifstream fin("pseudobil.in");
10 ofstream fout("pseudobil.out");
11
12 typedef unsigned short M[2 * 1501][1501];
13
14 M su, sd, pu, pd;
15 M a, b;
16
17 unsigned sc[1501], ss[1501], s[1501];
18
19 int m, n, K, L, D, n1, n2, T;
20
21 inline void SumPart(M su, M sd, M a);
22
23 int main()
24 {
25 fin >> T >> n >> K >> D;
26 if ( T == 1 ) // rezolv numai cerinta a)
27 {
28 int nr = D - 1;
29 n1 = nr;
30 while ( true )
31 {
32 nr -=2;
33 if ( nr < 0 ) break;
34 n1 += 2 * nr;
35 }
36 fout << n1 << ’\n’;
37 }
38 else
39 {
40 int x, y;
41 for ( int i = 0; i < K; ++i )
42 {
43 fin >> x >> y;
44 b[n + y - x][y] = 1;
45 a[x + y - 1][y] = 1;
46 s[y]++;
47 }
48
49 for ( int j = 1; j <= n; ++j )
50 ss[j] = ss[j - 1] + s[j];
51 L = D / 2 + 1;
52
53 SumPart(su, sd, a);
54 SumPart(pd, pu, b);
55 int A, B, C, D, E, F;
56 fin >> m;
57
CAPITOLUL 7. OJI 2014 150

58 for ( int i = 0; i < m; ++i )


59 {
60 fin >> x >> y;
61
62 A = ss[y - L];
63 B = K - ss[y + L - 1];
64 if ( y > x + y - 2 )
65 C = su[x + y - 2][y - 1];
66 else
67 C = su[x + y - 2][y];
68
69 C -= su[x + y - 2][y - L];
70 D = pu[n - x + y + 1][y + L - 1] - pu[n - x + y + 1][y];
71 int X = x + 2 * (L - 1);
72 if ( y > n - X + y - 1)
73 E = pd[n - X + y - 1][y - 1];
74 else
75 E = pd[n - X + y - 1][y];
76
77 E -= pd[n - X + y -1][y - L];
78 F = sd[X + y ][y + L - 1] - sd[X + y ][y];
79 n2 = K - (A + B + C + D + E + F);
80
81 fout << n2 << ’\n’;
82 }
83 }
84
85 fin.close();
86 fout.close();
87 return 0;
88 }
89
90 void SumPart(M su, M sd, M a)
91 {
92 int sp[1501];
93 for ( int d = 1; d < 2 * n; ++d )
94 {
95 for (int j = 0; j <= n; ++j )
96 sp[j] = 0;
97 for ( int j = max(d - n + 1, 1); j <= min(d, n) ; ++j )
98 {
99 sp[j] = sp[j - 1] + a[d][j];
100 su[d][j] = su[d - 1][j] + sp[j];
101 if ( j >= d )
102 su[d][j] += su[d - 1][j - 1];
103 sd[d][j] = ss[j] - su[d][j] + sp[j];
104 }
105 }
106 }

7.2.3 *Rezolvare detaliat 


Capitolul 8

OJI 2013

8.1 betasah
Problema 1 - betasah 100 de puncte
Jocul beta³ah se joac  folosindu-se doar piese asem n toare damelor clasicului ³ah, numite
N N 1
tot dame. Suprafaµa de joc are o form  triunghiular  ³i este format  din 2
p trate identice
dispuse pe N rânduri ³i N coloane. Rândurile se numeroteaz  de sus în jos, de la 1 la N . Coloanele
se numeroteaz  de la stânga la dreapta, de la 1 la N . Primul rând conµine un singur p trat, al
doilea rând conµine dou  p trate al turate, ..., al N -lea rând conµine N p trate al turate, ca în
N N 1
suprafeµele de joc cu N 6 din gurile de mai jos. Din cele 2
p trate, K sunt gri, iar restul
sunt albe. Poziµia ec rui p trat de pe suprafaµa de joc este dat  de rândul ³i coloana în care
acesta este situat.

Figura 8.1: betasah

Pe suprafaµa de joc sunt a³ezate D dame în D p trate albe distincte, ocupându-le. Într-un
p trat alb poate  a³ezat  o singur  dam , iar într-un p trat gri nu poate  a³ezat  nicio dam .
Poziµia unei dame pe suprafaµa de joc este dat  de poziµia p tratului alb în care este a³ezat 
dama.
Damele pot accesa orice p trat alb neocupat situat pe direcµiile: vertical , orizontal  sau
diagonal , numerotate de la 1 la 8 în gura b). Accesul pe o direcµie se face trecând din p trat alb
în p trat alb (doar p trate albe neocupate) pân  la întâlnirea unui p trat gri sau a unui p trat
alb ocupat de o alt  dam  sau pân  la terminarea suprafeµei de joc.
Numim p trat accesibil orice p trat alb neocupat (de pe suprafaµa de joc) care ar putea 
accesat de cel puµin una din cele D dame.
De exemplu, pentru suprafaµa de joc din gura c) num rul de p trate accesibile (marcate cu
X ) de pe suprafaµ  este 11; pentru suprafaµa de joc cu N 6, D 3 ³i K 4 din gura d)
num rul de p trate accesibile de pe suprafaµ  este 13. În gura e) sunt marcate cu X p tratele
accesibile ec rei dame de pe suprafaµa de joc din gura d).

151
CAPITOLUL 8. OJI 2013 152

Figura 8.2: betasah

Cerinµe
Scrieµi un program care s  citeasc  numerele naturale N , D, K , poziµiile damelor ³i ale p tra-
telor gri pe suprafaµa de joc ³i care s  determine:
a) num rul maxim M de p trate albe conµinute de un rând al suprafeµei de joc;
b) num rul P de p trate accesibile de pe suprafaµa de joc.
Date de intrare
Fi³ierul de intrare betasah.in conµine:
- pe prima linie cele trei numere naturale N , D ³i K , separate prin câte un spaµiu, cu semni-
caµia din enunµ;
- pe linia i  1 dou  numere naturale nenule xi ³i yi , separate printr-un singur spaµiu, repre-
zentând poziµia damei i pe suprafaµa de joc (rândul xi ³i coloana yi ), pentru i 1, 2, 3, ..., D;
- pe linia D  1  j dou  numere naturale nenule zj ³i tj , separate printr-un singur spaµiu,
reprezentând poziµia p tratului gri j pe suprafaµa de joc (rândul zj ³i coloana tj ), pentru j
1, 2, 3, ..., K .
Date de ie³ire
Fi³ierul de ie³ire betasah.out va conµine pe prima linie num rul natural M ³i pe a doua linie
num rul natural P , cu semnicaµia din enunµ.
Restricµii ³i preciz ri
a 2 & N & 1000;
a 1 & D & 100;
a 1 & K & 50;

a DK &
N n1
2
;
a 1 & yi & xi & N pentru i 1, 2, 3, ..., D;
a 1 & tj & zj N pentru j 1, 2, 3, ..., K ;
a num rul M se va scrie obligatoriu pe prima linie a ³ierului de ie³ire betasah.out;
a num rul P se va scrie obligatoriu pe a doua linie a ³ierului de ie³ire betasah.out;
a pentru rezolvarea corect  a cerinµei a) se acord  20% din punctaj, iar pentru rezolvarea
corect  a cerinµei b) se acord  80% din punctaj.

Exemple

Figura 8.3: betasah


CAPITOLUL 8. OJI 2013 153

Timp maxim de executare/test: 0.1 secunde


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

8.1.1 Indicaµii de rezolvare


prof. Carmen Minc  - Colegiul Naµional de Informatic  Tudor Vianu, Bucure³ti

Pentru punctul a), se poate utiliza un vector v cu N componente care, iniµial, memoreaz  în
ecare v i num rul maxim de p trate albe de pe rândul i: v i i pentru i 1, 2, 3, ..., N .
La citirea rândului i ³i a coloanei j pentru ecare p trat gri se decrementeaz  valoarea com-
ponentei v i.
Num rul maxim M de p trate albe care pot  conµinute de un rând al suprafeµei de joc este
egal cu maximul valorilor componentelor vectorului v , M max v 1, v 2, ..., v N 
Pentru punctul b), o soluµie se poate obµine utilizând o matrice A cu N linii si N coloane.
Partea inferioar  a matricei, format  din elementele situate sub diagonala principal  inclusiv
diagonala principal , vor reprezenta p tratele din suprafaµa de joc.
Iniµial, matricea va avea toate elementele cu valoarea 0, adic  toate p tratele sunt albe.
Elementele matricei care corespund poziµiilor damelor vor avea valoarea 1, iar cele corespun-
z toare p tratelor gri vor avea valoarea 2. Pentru ecare dam  se va simula deplasarea pe cele
8 direcµii. Dac  dama se a  în p tratul din poziµia l, c atunci aceasta se poate deplasa în
p tratele accesibile ale c ror poziµii corespund elementelor cu valoarea 0 din partea inferioar  a
matricei ³i sunt situate în direcµia:
- 1, prin mic³orarea indicelui de linie: l  1, c, l  2, c, l  3, c etc.
- 5, prin m rirea indicelui de linie: l  1, c, l  2, c, l  3, c etc.
- 7, pe aceea³i linie, la stânga, prin mic³orarea indicelui de coloan :
l, c  1, l, c  2, l, c  3 etc.
- 3, pe aceea³i linie, la dreapta, prin m rirea indicelui de coloan : l, c 
1, l, c  2, l, c  3 etc.
- 8, diagonal , prin mic³orarea indicilor de linie ³i coloan : l  1, c 
1, l  2, c  2, l  3, c  3 etc. Figura 8.4: betasah
- 4, diagonal , prin m rirea indicilor de linie ³i coloan : l  1, c  1, l  2, c  2, l  3, c  3
etc.
- 2, diagonal , prin mic³orarea indicelui de linie ³i m rirea celui de coloan : l  1, c  1, l 
2, c  2, l  3, c  3 etc.
- 6, diagonal , prin m rirea indicelui de linie ³i mic³orarea celui de coloan : l  1, c  1, l 
2, c  2, l  3, c  3 etc.
Elementele matricei ce corespund p tratelor accesibile vor memora valoarea 3.
Pentru a se evita ie³irea din suprafaµa de joc în timpul c ut rii poziµiilor accesibile, se poate
borda partea inferioar  a matricei cu valori de 2 sau se vor verica indicii ec rei poziµii accesibile
x, y : 1 $ x $ N ³i 1 $ y $ x.
Dup  stabilirea p tratelor accesibile pentru ecare dam , partea inferioar  a matricei va avea
elemente cu valoarea 3 ce corespund acestor p trate. Parcurgând partea inferioar  a matricei, se
vor num ra aceste valori de 3. Num rul lor va reprezenta num rul P de p trate accesibile de pe
suprafaµa de joc.
Observaµie: o soluµie care const  în parcurgerea integral  a suprafeµei de joc ³i vericarea
pentru ecare p trat alb dac  pe una din cele 8 direcµii exist  vreo dam  care s -l acceseze va
obµine un punctaj de aprox. 55p din cauza dep ³irii timpului de executare.

8.1.2 Cod surs 

Listing 8.1.1: beta_CM1.cpp


1 //prof. Carmen Minca
2 //Colegiul National de Informatica Tudor Vianu Bucuresti
3 #include <fstream>
4
5 using namespace std;
6
7 int n,d,k,pozdama[103][3], albe[1003];
8 char a[1003][1003];
CAPITOLUL 8. OJI 2013 154

9
10 ifstream f("betasah.in");
11 ofstream g("betasah.out");
12
13 int main()
14 {
15 //citire date
16 int i,l,c,j,i1;
17 f>>n>>d>>k;
18 for(i=1;i<=d;i++)
19 {
20 f>>l>>c;
21 a[l][c]=1;//codific cu 1 pozitia damei din joc
22 pozdama[i][1]=l;pozdama[i][2]=c;
23 }
24
25 for(i=1;i<=n;i++) albe[i]=i;
26 for(i=1;i<=k;i++)
27 {
28 f>>l>>c;
29 a[l][c]=2;//codific cu 2 patratele gri din joc
30 albe[l]--;//scad patratele gri din nr total de patrate de pe linia l
31 }
32
33 int M=albe[1]; //a)
34 for(i=2;i<=n;i++)M=max(albe[i],M);
35
36 //bordare matrice. Incadrez suprafata de joc cu patrate gri
37 //pentru a nu iesi din suprafata de joc
38 for(i=0;i<=n+1;i++)
39 a[i][0]=a[i][i+1]=a[i][i+2]=2;
40 for(j=0;j<=n+2;j++)
41 a[n+1][j]=2;
42
43 //determin pentru fiecare dama patratele accesibile
44 for(i=1;i<=d;i++)
45 {
46 l=pozdama[i][1];
47 c=pozdama[i][2];
48
49 //pe aceeasi linie
50 j=c-1;
51 while(a[l][j]!=2)//directia 7
52 {
53 if(a[l][j]==0)a[l][j]=3;
54 j--;
55 }
56 j=c+1;
57 while(a[l][j]!=2)//directia 3
58 {
59 if(a[l][j]==0)a[l][j]=3;
60 j++;
61 }
62
63 //pe aceeasi coloana
64 i1=l-1;
65 while(a[i1][c]!=2)//directia 1
66 {
67 if(a[i1][c]==0)a[i1][c]=3;
68 i1--;
69 }
70 i1=l+1;
71 while(a[i1][c]!=2)//directia 5
72 {
73 if(a[i1][c]==0)a[i1][c]=3;
74 i1++;
75 }
76
77 //pe diagonala 8-4
78 i1=l-1;
79 j=c-1;
80 while (a[i1][j]!=2)//directia 8
81 {
82 if(a[i1][j]!=1)
83 a[i1][j]=3;//marchez cu 3 patratele accesibile
84 i1--;
CAPITOLUL 8. OJI 2013 155

85 j--;
86 }
87
88 i1=l+1;
89 j=c+1;
90 while (a[i1][j]!=2)//directia 4
91 {
92 if(a[i1][j]!=1)
93 a[i1][j]=3;
94 i1++; j++;
95 }
96
97 //pe diagonala 2-6
98 i1=l-1;
99 j=c+1;
100 while (a[i1][j]!=2)//spre 2
101 {
102 if(a[i1][j]!=1) a[i1][j]=3;
103 i1--;
104 j++;
105 }
106
107 i1=l+1;
108 j=c-1;
109 while (a[i1][j]!=2)//spre 6
110 {
111 if(a[i1][j]!=1)
112 a[i1][j]=3;
113 i1++;
114 j--;
115 }
116 }
117
118 //numarul patratelor accesibile = nr valori de 3 din matrice
119 int P=0;
120 for(i=1;i<=n;i++)
121 for(j=1;j<=i;j++)
122 if(a[i][j]==3)
123 P++;
124
125 g<<M<<endl<<P<<endl;
126
127 return 0;
128 }

Listing 8.1.2: beta_CM2.cpp


1 //autor prof. Carmen Minca
2 //Colegiul National de Informatica Tudor Vianu Bucuresti
3 #include <fstream>
4
5 using namespace std;
6
7 int n,d,k,pozdama[103][3],v[1003];
8 char a[1003][1003];
9
10 ifstream f("betasah.in");
11 ofstream g("betasah.out");
12
13 void citeste()
14 {
15 int i,l,c;
16 f>>n>>d>>k;
17 for(i=1;i<=d;i++)
18 {
19 f>>l>>c;
20 a[l][c]=1;//codific cu 1 pozitia damei din joc
21 pozdama[i][1]=l;pozdama[i][2]=c;
22 }
23
24 for(i=1;i<=n;i++ )v[i]=i;
25 for(i=1;i<=k;i++)
26 {
27 f>>l>>c; v[l]--;
28 a[l][c]=2;//codific cu 2 patratele gri din joc
CAPITOLUL 8. OJI 2013 156

29 }
30 }
31
32 void bordare()//incadrez suprafata de joc cu patrate gri
33 //pentru a nu iesi din suprafata de joc
34 {
35 int i,j;
36 for(i=0;i<=n+1;i++)
37 a[i][0]=a[i][i+1]=a[i][i+2]=2;
38 for(j=0;j<=n+2;j++)
39 a[n+1][j]=2;
40 }
41
42 void linie(int l,int c) //deplasare dama pe aceeasi linie
43 {
44 int j=c-1;
45 while(a[l][j]!=2)//directia 7
46 {
47 if(a[l][j]==0)a[l][j]=3;
48 j--;
49 }
50
51 j=c+1;
52 while(a[l][j]!=2)//directia 3
53 {
54 if(a[l][j]==0)a[l][j]=3;
55 j++;
56 }
57 }
58
59 void coloana(int l,int c)//deplasare dama pe aceeasi coloana
60 {
61 int i=l-1;
62 while(a[i][c]!=2)//directia 1
63 {
64 if(a[i][c]==0)a[i][c]=3;
65 i--;
66 }
67
68 i=l+1;
69 while(a[i][c]!=2)//directia 5
70 {
71 if(a[i][c]==0)a[i][c]=3;
72 i++;
73 }
74 }
75
76 void diag1(int l,int c)//deplasare dama pe directia 8-4
77 {
78 int i,j;
79 i=l-1;
80 j=c-1;
81 while (a[i][j]!=2)//directia 8
82 {
83 if(a[i][j]!=1) a[i][j]=3;//marchez cu 3 patratele accesibile
84 i--;
85 j--;
86 }
87
88 i=l+1;
89 j=c+1;
90 while (a[i][j]!=2)//directia 4
91 {
92 if(a[i][j]!=1)
93 a[i][j]=3;
94 i++;
95 j++;
96 }
97 }
98
99 void diag2(int l,int c)//deplasare dama pe directia 2-6
100 {
101 int i,j;
102 i=l-1;
103 j=c+1;
104 while (a[i][j]!=2)//directia 2
CAPITOLUL 8. OJI 2013 157

105 {
106 if(a[i][j]!=1) a[i][j]=3;
107 i--;
108 j++;
109 }
110
111 i=l+1;
112 j=c-1;
113 while (a[i][j]!=2)//directia 6
114 {
115 if(a[i][j]!=1)
116 a[i][j]=3;
117 i++;
118 j--;
119 }
120 }
121
122 int numara()
123 {
124 //numarul patratelor accesibile = nr valori de 3 din matrice
125 int i,j,P=0;
126 for(i=1;i<=n;i++)
127 for(j=1;j<=i;j++)
128 if(a[i][j]==3)
129 P++;
130 return P;
131 }
132
133 int main()
134 {
135 int i,l,c,M;
136
137 citeste();
138 bordare();
139
140 M=v[1];
141 for(i=2;i<=n;i++) M=max(M,v[i]);
142 for(i=1;i<=d;i++)
143 {
144 l=pozdama[i][1];
145 c=pozdama[i][2];
146 linie(l,c);
147 coloana(l,c);
148 diag1(l,c);
149 diag2(l,c);
150 }
151
152 g<<M<<endl<<numara()<<endl;
153
154 return 0;
155 }

Listing 8.1.3: beta_CM3.cpp


1 //prof. Carmen Minca, Colegiul National de Informatica Tudor Vianu Bucuresti
2 #include <fstream>
3
4 using namespace std;
5
6 int main()
7 {
8 int n,d,k,pozdama[103][3], albe[1003], P=0, M;
9 char a[1003][1003];
10
11 ifstream f("betasah.in");
12 ofstream g("betasah.out");
13
14 //citire date
15 int i,l,c,j,i1;
16 f>>n>>d>>k;
17
18 //initializez partea inferioara a matricei cu 0
19 for(i=1;i<=n;i++)
20 for(j=1;j<=i;j++)a[i][j]=0;
21
CAPITOLUL 8. OJI 2013 158

22 for(i=1;i<=d;i++)
23 {
24 f>>l>>c;
25 a[l][c]=1;//codific cu 1 pozitia damei din joc
26 pozdama[i][1]=l;pozdama[i][2]=c;
27 }
28
29 for(i=1;i<=n;i++)albe[i]=i;
30 for(i=1;i<=k;i++)
31 {
32 f>>l>>c;
33 a[l][c]=2;//codific cu 2 patratele gri din joc
34 albe[l]--;//scad patratele gri din nr total de patrate de pe linia l
35 }
36
37 M=albe[1]; //a)
38 for(i=2;i<=n;i++) M=max(albe[i],M);
39 g<<M<<endl;
40
41 //bordare matrice. Incadrez suprafata de joc cu patrate gri
42 //pentru a nu iesi din suprafata de joc
43 for(i=0;i<=n+1;i++)
44 a[i][0]=a[i][i+1]=a[i][i+2]=2;
45 for(j=0;j<=n+2;j++)
46 a[n+1][j]=2;
47
48 //determin pentru fiecare dama patratele accesibile
49 for(i=1;i<=d;i++)
50 {
51 l=pozdama[i][1];
52 c=pozdama[i][2];
53 //pe aceeasi linie
54 j=c-1;
55
56 while(a[l][j]!=2)//directia 7
57 {
58 if(a[l][j]==0) a[l][j]=3;
59 j--;
60 }
61
62 j=c+1;
63 while(a[l][j]!=2)//directia 3
64 {
65 if(a[l][j]==0) a[l][j]=3;
66 j++;
67 }
68
69 //pe aceeasi coloana
70 i1=l-1;
71 while(a[i1][c]!=2)//directia 1
72 {
73 if(a[i1][c]==0) a[i1][c]=3;
74 i1--;
75 }
76
77 i1=l+1;
78 while(a[i1][c]!=2)//directia 5
79 {
80 if(a[i1][c]==0) a[i1][c]=3;
81 i1++;
82 }
83
84 //pe diagonala 8-4
85 i1=l-1;
86 j=c-1;
87 while (a[i1][j]!=2)//directia 8
88 {
89 if(a[i1][j]!=1) a[i1][j]=3;//marchez cu 3 patratele accesibile
90 i1--;
91 j--;
92 }
93
94 i1=l+1;
95 j=c+1;
96 while (a[i1][j]!=2)//directia 4
97 {
CAPITOLUL 8. OJI 2013 159

98 if(a[i1][j]!=1) a[i1][j]=3;
99 i1++;
100 j++;
101 }
102
103 //pe diagonala 2-6
104 i1=l-1;
105 j=c+1;
106 while (a[i1][j]!=2)//directia 2
107 {
108 if(a[i1][j]!=1) a[i1][j]=3;
109 i1--;
110 j++;
111 }
112
113 i1=l+1;
114 j=c-1;
115 while (a[i1][j]!=2)//directia 6
116 {
117 if(a[i1][j]!=1) a[i1][j]=3;
118 i1++;
119 j--;
120 }
121 }
122
123 //numarul patratelor accesibile = nr valori de 3 din matrice
124 for(i=1;i<=n;i++)
125 for(j=1;j<=i;j++)
126 if(a[i][j]==3) P++;
127 g<<P<<endl;
128 return 0;
129 }

Listing 8.1.4: beta_GB.cpp


1 //prof. Georgeta Balacea Galati - C.N."V.Alecsandri"
2 #include <fstream>
3 #include<cstdio>
4 #include<cmath>
5
6 using namespace std;
7
8 int T[1003][1003]; int Dame[2][1001];
9 int D,N,K,M,P,V[1001];
10
11 int main()
12 {
13 int x,y,i,j,l,c;
14 FILE *f,*g;
15
16 f=fopen("betasah.in","r");
17 g=fopen("betasah.out","w");
18
19 fscanf(f,"%d%d%d",&N,&D,&K);
20
21 for (i=1;i<=D;i++)
22 {
23 fscanf(f,"%d%d",&x,&y);
24 T[x][y]=1;
25 Dame[0][i]=x;Dame[1][i]=y;
26
27 }
28
29 for(i=1;i<=N;i++) V[i]=i;
30 for (i=1;i<=K;i++)
31 {
32 fscanf(f,"%d%d",&x,&y);
33 T[x][y]=-1;
34 V[x]--;
35 }
36
37 for(i=1;i<=N;i++)
38 M=(M+V[i]+(int)abs((double)M-V[i]) )/2;
39
40 for(i=0;i<=N+1;i++)
CAPITOLUL 8. OJI 2013 160

41 {
42 T[i][0]=-1;
43 T[N+1][i]=-1;
44 T[0][i]=-1;
45 }
46
47 for (i=1;i<=D;i++)
48 {
49 l=Dame[0][i];
50 c=Dame[1][i];
51 T[l][c]=0;
52
53 // directia NORD
54 while (l>=c && (abs)((float)T[l][c])!=1)
55 {
56 T[l][c]=3;
57 --l;
58 }
59
60 l=Dame[0][i];
61 c=Dame[1][i];
62
63 // directia Nord Est
64 while (l>=c &&(abs)((float)T[l][c])!=1)
65 {
66 T[l][c]=3;
67 --l;
68 ++c;
69 }
70
71 l=Dame[0][i];
72 c=Dame[1][i];
73
74 // directia Est
75 while (l>=c && (abs)((float)T[l][c])!=1)
76 {
77 T[l][c]=3;c++;
78 }
79
80 l=Dame[0][i];
81 c=Dame[1][i];
82
83 // directia Sud Est
84 while ((abs)((float)T[l][c])!=1)
85 {
86
87 T[l][c]=3;
88 l++;
89 c++;
90 }
91
92 l=Dame[0][i];
93 c=Dame[1][i];
94
95 // directia Sud
96 while ((abs)((float)T[l][c])!=1)
97 {
98 T[l][c]=3;
99 l++;
100 }
101
102 l=Dame[0][i];
103 c=Dame[1][i];
104
105 // directia Sud Vest
106 while ( (abs)((float)T[l][c])!=1)
107 {
108 T[l][c]=3;
109 ++l;
110 --c;
111 }
112
113 l=Dame[0][i];
114 c=Dame[1][i];
115
116 // directia Vest
CAPITOLUL 8. OJI 2013 161

117 while ((abs)((float)T[l][c])!=1)


118 {
119 T[l][c]=3;
120 --c;
121 }
122
123 l=Dame[0][i];
124 c=Dame[1][i];
125
126 // directia Nord Vest
127 while ((abs)((float)T[l][c])!=1)
128 {
129 T[l][c]=3;
130 --c;
131 --l;
132 }
133
134 l=Dame[0][i];
135 c=Dame[1][i];
136 T[l][c]=1;
137 }
138
139 for(i=1;i<=N;i++)
140 for(j=1;j<=i;j++)
141 if(T[i][j]==3) P++;
142
143 P+=(T[i][j]==3);
144
145 fprintf(g,"%d\n%d\n",M,P);
146
147 fclose(f);
148 fclose(g);
149 return 0;
150 }

Listing 8.1.5: beta_ZS.cpp


1 // profesor Szabo Zoltan ISJ Mures
2 #include <fstream>
3
4 using namespace std;
5 int a[1003][1003]; // declar tabla de sah variabila globala,
6 // ca sa fie initializata cu 0
7 int main()
8 {
9 int N,D,K,d[2][100],i,j,maxalbe,albe,x,y,atacate,l,c;
10
11 ifstream fin("betasah.in");
12 ofstream fout("betasah.out");
13
14 fin>>N>>D>>K; // dimensiunea tablei N, numarul damelor D
15 //si numarul patratelor gri K
16 for (i=0;i<D;++i)
17 {
18 fin>>x>>y; // citesc pozitiile damelor
19 d[0][i]=x;
20 d[1][i]=y;
21 a[x][y]=1; // 1 pe tabla de sah va insemna dama
22 }
23
24 for (i=0;i<K;++i)
25 {
26 fin>>x>>y; // citesc pozitiile patratelor gri
27 a[x][y]=-1; // -1 pe tabla de sah va insemna patrat gri
28 }
29
30 maxalbe=0; // maxalbe contine numarul maxim de patrate albe
31 // de pe un rand
32 for (i=1;i<=N;++i)
33 {
34 albe=0; // in "albe" vom numara numarul patratelor albe
35 // de pe randul i
36 for(j=1;j<=i;++j)
37 albe+=(a[i][j]!=-1); // daca a[i][j] difera de -1,
38 // atunci e patrat alb,
CAPITOLUL 8. OJI 2013 162

39 // deci conditia are valoarea 1


40 if (albe>maxalbe) maxalbe=albe;
41 }
42
43 atacate=0; // numarul campurilor atacate de dame
44 for (i=0;i<D;++i)
45 {
46 l=d[0][i];
47 c=d[1][i];
48
49 // directia verticala-sus
50 while (l>=c && a[l][c]!=-1)
51 {
52 if (a[l][c]==0)
53 {
54 ++atacate; // se numara orice camp nenumarat
55 a[l][c]=1; //
56 }
57 --l;
58 }
59
60 l=d[0][i];
61 c=d[1][i];
62
63 // directia oblic dreapta-sus
64 while (l>=c && a[l][c]!=-1)
65 {
66 if (a[l][c]==0)
67 {
68 ++atacate; // se numara orice camp nenumarat
69 a[l][c]=1; //
70 }
71 --l;
72 ++c;
73 }
74
75 l=d[0][i];
76 c=d[1][i];
77
78 // directia orizontala-dreapta
79 while (l>=c && a[l][c]!=-1)
80 {
81 if (a[l][c]==0)
82 {
83 ++atacate; // se numara orice camp nenumarat
84 a[l][c]=1; //
85 }
86 ++c;
87 }
88
89 l=d[0][i];
90 c=d[1][i];
91
92 // directia oblic dreapta-jos
93 while (l<=N && a[l][c]!=-1)
94 {
95 if (a[l][c]==0)
96 {
97 ++atacate; // se numara orice camp nenumarat
98 a[l][c]=1; //
99 }
100 ++l;
101 ++c;
102 }
103
104
105 l=d[0][i];
106 c=d[1][i];
107
108 // directia verticala-jos
109 while (l<=N && a[l][c]!=-1)
110 {
111 if (a[l][c]==0)
112 {
113 ++atacate; // se numara orice camp nenumarat
114 a[l][c]=1; //
CAPITOLUL 8. OJI 2013 163

115 }
116 ++l;
117 }
118
119 l=d[0][i];
120 c=d[1][i];
121
122 // directia oblic stanga-jos
123 while (l<=N && c>0 && a[l][c]!=-1)
124 {
125 if (a[l][c]==0)
126 {
127 ++atacate; // se numara orice camp nenumarat
128 a[l][c]=1; //
129 }
130 ++l;
131 --c;
132 }
133
134 l=d[0][i];
135 c=d[1][i];
136
137 // directia orizontala-dreapta
138 while (c>0 && a[l][c]!=-1)
139 {
140 if (a[l][c]==0)
141 {
142 ++atacate; // se numara orice camp nenumarat
143 a[l][c]=1; //
144 }
145 --c;
146 }
147
148 l=d[0][i];
149 c=d[1][i];
150
151 // directia oblic-stanga-sus
152 while (c>0 && a[l][c]!=-1)
153 {
154 if (a[l][c]==0)
155 {
156 ++atacate; // se numara orice camp nenumarat
157 a[l][c]=1; //
158 }
159 --c;
160 --l;
161 }
162 }
163
164 fout <<maxalbe<<endl<<atacate<< endl;
165
166 fin.close();
167 fout.close();
168 return 0;
169 }

8.1.3 *Rezolvare detaliat 

8.2 clepsidru
Problema 2 - clepsidru 100 de
puncte
O clepsidr  este un dispozitiv folosit pentru a m sura timpul. Clepsidra este
alc tuit  din dou  incinte de sticl , conectate printr-un tub n. Una dintre incinte este
umplut  cu nisip, acesta scurgându-se în cea de-a doua incint , cu o vitez  constant .
Clepsidra poate  întoars , pentru a m sura o alt  perioad  de timp.
Arheologii au descoperit un dispozitiv, pe care l-au denumit clepsidru, format
din n clepsidre identice, suprapuse, numerotate de la 1 la n, prin care nisipul poate Figura 8.5:
circula de la o clepsidr  la alta datorit  forµei gravitaµionale. clepsidru
CAPITOLUL 8. OJI 2013 164

Studiind acest obiect, arheologii au constatat c :


- dispozitivul poate  utilizat atât în poziµia 1, când clepsidrele sunt în ordinea 1, 2, ..., n cu
clepsidra n a³ezat  pe sol, cât ³i în poziµia 2, când clepsidrele sunt în ordinea n, n  1, ..., 1 cu
clepsidra 1 a³ezat  pe sol;
- viteza de trecere a nisipului de la o incint  la alta, a aceleia³i clepsidre, este de 1 bob de
nisip/secund , pentru toate clepsidrele, indiferent de poziµie;
- trecerea clepsidrului dintr-o poziµie în alta presupune r sturnarea acestuia ³i rea³ezarea boa-
belor de nisip;
- timpul de trecere a boabelor de nisip de la o clepsidr  la alta este 0.
Arheologii studiaz  comportarea clepsidrului realizând dou  experimente diferite, dup  cum
urmeaz :
a) Se a³eaz  clepsidrul în poziµia 1, se introduc în incinta de sus a clepsidrei 1 un num r b de
boabe de nisip ³i se determin  dup  câte secunde vor ajunge toate boabele de nisip în incinta de
jos a ultimei clepsidre;
b) Se a³eaz  clepsidrul în poziµia 1, se introduc în incinta de sus a clepsidrei 1 un num r b
de boabe de nisip, apoi se a³eaz  clepsidrul în k st ri consecutive, o stare ind caracterizat  de
valorile si ³i pi , 1 & i & k , ce reprezint  num rul de secunde, respectiv poziµia, în care este
menµinut nemi³cat clepsidrul, iar la nal se determin  num rul de boabe de nisip din incintele
ec rei clepsidre.
Spre exemplu, dac  clepsidrul
este format din n 2 clepsidre, iar
în incinta de sus a primei clepsidre se
introduc b 3 boabe de nisip, la pri-
mul experiment se va obµine valoarea
4.
La al doilea experiment se a³eaz 
clepsidrul în k 2 st ri, caracteri-
zate prin s1 3, p1 1; s2 1,
p2 2. Num rul de boabe de nisip
din clepsidre va evolua ca în gura
al turat . Figura 8.6: clepsidru
Cerinµe
S  se scrie un program care cite³te valorile n ³i b, precum ³i valorile k , si , pi , 1 & i & k, ³i
calculeaz  valorile obµinute de arheologi la realizarea celor dou  experimente.

Date de intrare
Prima linie a ³ierului de intrare clepsidru.in conµine dou  numere naturale nenule n ³i b,
separate printr-un singur spaµiu, cu semnicaµia din enunµ; a doua linie conµine num rul natural
nenul k având semnicaµia din enunµ, iar urm toarele k linii conµin ecare câte o pereche de valori
si ³i pi , 1 & i & k , separate printr-un singur spaµiu, cu semnicaµia din enunµ.

Date de ie³ire
Fi³ierul de ie³ire clepsidru.out va conµine pe prima linie un num r natural ce reprezint 
valoarea obµinut  la primul experiment, iar pe urm toarele n linii va conµine câte o pereche de
numere naturale, separate printr-un singur spaµiu, ce reprezint  cantit µile de boabe de nisip din
incintele de sus ³i de jos ale celor n clepsidre, scrise în ordinea de la 1 la n a clepsidrelor, dup 
realizarea celui de-al doilea experiment.

Restricµii ³i preciz ri
a 1 & n & 1000;
a 1 & b & 1000000000;
a 1 & k & 1000;
a 1 & si & 1000, 1 & i & k ;
a pi " 1, 2, 1 & i & k ;
a pentru rezolvarea corect  a primei cerinµe se acord  25% din punctaj, iar pentru rezolvarea
corect  a celei de-a doua cerinµe se acord  75% din punctaj.
a acordarea punctajului pentru a doua cerinµ  se face numai dac  în ³ierul de ie³ire exist  un
r spuns pentru prima cerinµ , indiferent de corectitudinea acestuia.
CAPITOLUL 8. OJI 2013 165

Exemple
CAPITOLUL 8. OJI 2013 166

clepsidru.in clepsidru.out Explicaµii


23 4 - Clepsidrul este format din n=2 clepsidre ³i în incinta de sus
2 11 a primei clepsidre se introduc b=3 boabe de nisip.
31 01 - Toate boabele de nisip vor ajunge în incinta de jos a ultimei
12 clepsidre dup  4 secunde.
- Dup  ce clepsidrul este a³ezat 3 secunde în poziµia 1 ³i 1
secund  în poziµia 2, în clepsidre se vor g si câte (1,1), (0,1)
boabe de nisip.

Timp maxim de executare/test: 0.5 secunde


Memorie: total 64 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

8.2.1 Indicaµii de rezolvare

Varianta nr. 1
autor prof. Che³c  Ciprian - Liceul Tehnologic Costin Neniµescu Buz u

<
Soluµie în O n si  - C++
Valoarea cerut  de primul experiment, adic  dup  câte secunde ajung toate boabele de nisip
în incinta de jos a ultimei clepsidre, se determin  cu formula n  b  1. Justicarea este urm toarea:
la ecare secund  câte un bob de nisip cade în clepsidra urm toare, a³adar ultimul bob de nisip
din incinta de sus a primei clepsidre întârzie b  1 secunde pân  începe s  cad , iar pentru a c dea
are nevoie de n secunde, câte clepsidre sunt.
Pentru a r spunde cerinµei celui de-al doilea experiment, adic  pentru a preciza num rul de
boabe de nisip din incintele clepsidrelor, dup  cele k a³ez ri consecutive, am f cut o simulare
la nivel de secund . La ecare secund  am simulat curgerea boabelor de nisip într-unul sau în
cel lalt sens de curgere, în funcµie de poziµie. La citirea unei noi poziµii am simulat, dac  era
cazul, r sturnarea clepsidrului ³i rea³ezarea boabelor de nisip.

Varianta nr. 2
prof. Szabo Zoltan - Inspectoratul ³colar Judeµean Mure³

Soluµie de 100 puncte (rezolvare în O n  k ) - C++


În starea iniµial  toate boabele de nisip sunt în incinta de SUS. “tim,
c  în ecare secund , un bob de nisip trece printr-un oriciu, indiferent
de starea 1 sau 2.
Vom observa:
- Nisipul în interiorul clepsidrului curge liniar, în orice poziµie dife-
rit  de extremit µi avem un singur bob de nisip (sau 0, dac  înc  nu a
curs).
- Cantitate mai mare de nisip se poate acumula doar la cele dou 
extremit µi, iar între extremit µi, în ecare incint  avem câte un bob
de nisip.
Fie n num rul de clepsidre, b num rul de boabe de nisip.
Asociind un vector pentru memorarea clepsidrului, acest vector a
va avea n  1 elemente numerotate a0 , a1 , a2 , ..., an .
Figura
Incinta de sus este a0 , iar incinta de jos este an . Incintele intermediare sunt 8.7:incinte
dou  clepsidru
lipite
(ha³urate în imagine).
Noi deci trebuie s  memor m doar poziµia SU S (iniµial 0), poziµia JOS (iniµial 0), num rul
boabelor de nisip sus BSU S (iniµial b) ³i num rul boabelor de nisip jos BJOS (iniµial nu conteaz ,
dar pentru prelucrarea problemei este indicat s  e 1).
Ne vom folosi de faptul c  în T unit µi de timp din incinta de SU S vor curge T boabe de nisip
la o distanµ  SU S  T .
Astfel dup  T unit µi de timp noile valori vor  SU S , BSU S  T , JOS  T ³i BJOS 1.
Dac  observ m, c  JOS  T a dep ³it dimensiunea clepsidrului, atunci aplic  corecµii de
cumul pentru valorile lui JOS ³i BJOS . Iar dac  observ m, c  SU S num rul BSU S de boabe
este num r negativ, vom aplica de asemenea corecµii pentru valorile lui SU S ³i BSU S .
CAPITOLUL 8. OJI 2013 167

Dac  clepsidra este în starea 2, procedeul este identic, dar cu direcµie invers .
La tip rire vom avea grij  s  vedem ultima stare a clepsidrului, indc  în incintele intermediare
vom avea 0  1 sau 1  0 boabe de nisip.

Varianta nr.3
prof. Rodica Moldovan - C.N. Gheorghe ³incai Baia Mare
2
Soluµie în O k ˜ n  - C++
Vom lucra într-un vector stare1001, în care vom p stra num rul de boabe din ecare clepsidr 
la un moment dat. Iniµializ m staren b;
Vom folosi urm toarele notaµii:
l - nivelul de pe care pornesc boabele,
k - nivelul pe care ajung boabele
s - num r de secunde într-o anumit  stare
Pentru ecare pereche citit  (secunde poziµie) vom actualiza (vectorul stare) num rul de boabe
din clepsidre (nu la nivel de secund  ci funcµie de l, k , s)
La ecare actualizare, num rul de boabe b se iniµializeaz  cu num rul de boabe aate în
recipientul de pornire b starel
Se disting urm toarele situaµii:
transformareJos(int l, int k, int s)
a Dac  (l 0) && (k 0)
atunci suntem pe nivelul inferior ³i boabele nu coboar , deci poziµia din vectorul stare se va
actualiza cu b
a Dac (l k)
atunci num rul de boabe r mase în recipientul iniµial va  max 0, b  s;
a Dac  (k 0)
atunci num rul de boabe adunate în recipientul inferior va  max 0, min b, s  l  k   1;
a Dac  (s $ l  k )
atunci timpul este mai mic decât timpul necesar s  ajung  o boab  în recipient ³i vom actualiza
cu 0
a Dac  (s ' b  l  k )
atunci boabele au trecut deja prin recipient ³i vom actualiza cu 0
a Altfel boaba se aa în tranziµie prin recipient ³i vom actualiza cu 1

analog pentru transformareSus(int l, int k , int s)


a Dac  (l n) && (k n)
atunci suntem pe nivelul superior ³i boabele nu urc , deci poziµia din vectorul stare se va
actualiza cu b
a Dac  (l k)
atunci num rul de boabe r mase în recipientul iniµial va  max 0, b  s;
a Dac  (k n)
atunci num rul de boabe adunate în recipientul superior va  max 0, min b, s  k  l  1;
a Daca (s $ k  l)
atunci timpul este mai mic decât timpul necesar sa ajung  o boaba in recipient ³i vom actualiza
cu 0
a Daca (s ' b  k  l)
atunci boabele au trecut deja prin recipient ³i vom actualiza cu 0
a Altfel boaba se aa în tranziµie prin recipient ³i vom actualiza cu 1

Varianta nr. 4
prof. Popa Daniel - C.N. Aurel Vlaicu Or ³tie

Soluµie de 100 puncte (rezolvare în O n  k ) - Pascal


Ideea de rezolvare: Boabele stau în ³ir indian tot timpul, poziµiile ambelor capete ind
singurele date ce merit  memorate. La deplasarea ³irului trebuie s  avem grij  ca limita superioar 
(ls) s  nu treac  de n în jos ³i limita de jos (lj ) s  nu treac  de 0 în sus.
CAPITOLUL 8. OJI 2013 168

8.2.2 Cod surs 

Listing 8.2.1: CC_clepsidru.cpp


1 // sursa - prof. Chesca Ciprian
2
3 #include <fstream>
4 #define nmax 1001
5
6 using namespace std;
7
8 int u[nmax],d[nmax]; // u(i) - incinta de sus d(i) - incinta de jos
9 int sens,sensv=0,n,b,k;
10
11 ifstream f("clepsidru.in");
12 ofstream g("clepsidru.out");
13
14 int main()
15 {
16
17 int i,j,y,t,p;
18
19 // citesc n si b
20 f>>n>>b;
21
22 // citesc k
23 f>>k;
24
25 // afisez prima cerinta
26 g<<n+b-1<<"\n";
27
28 u[1]=b;
29 for(i=1;i<=k;i++)
30 {
31 f>>y>>p;
32 if (p==1) sens=1;
33 if (p==2) sens=-1;
34
35 // simulez rasturnarea, daca este cazul
36 if (sens!=sensv&&i!=1)
37 {
38 if (sensv==-1)
39 for(j=1;j<=n-1;j++)
40 if (d[j]>0)
41 {
42 u[j+1]+=1;
43 d[j]=0;
44 }
45
46 if (sensv==1)
47 for(j=n;j>=2;j--)
48 if (u[j]>0)
49 {
50 d[j-1]+=1;
51 u[j]=0;
52 }
53 }
54
55 // simulare la nivel de 1 secunda
56 // (adica ce se intampla din sec. in sec.)
57 for(t=1;t<=y;t++)
58 {
59 // clepsidru in pozitia 1
60 if (sens==1)
61 {
62 if(u[n]>0)
63 {
64 u[n]--;
65 d[n]++;
66 }
67
68 for(j=n-1;j>=1;j--)
69 if (u[j]>0)
70 {
CAPITOLUL 8. OJI 2013 169

71 u[j]--;
72 u[j+1]++;
73 }
74 }
75
76 // clepsidru in pozitia 2
77 if (sens==-1)
78 {
79 if (d[1]>0)
80 {
81 d[1]--;
82 u[1]++;
83 }
84
85 for(j=2;j<=n;j++)
86 if (d[j]>0)
87 {
88 d[j]--;
89 d[j-1]++;
90 }
91 }
92
93 }
94
95 sensv=sens;
96 }
97
98 // afisez a doua cerinta
99 for(i=1;i<=n;i++)
100 g<<u[i]<<" "<<d[i]<<"\n";
101
102 f.close();
103 g.close();
104
105 return 0;
106 }

Listing 8.2.2: MR_clepsidru.cpp


1 // sursa - prof. Moldovan Rodica
2 #include <iostream>
3 #include <fstream>
4
5 using namespace std;
6
7 int stare[1001], n;
8
9 // Nivelul de pe care pornesc boabele,
10 // nivelul pe care ajung boabele, secunde
11
12 int transformareJos(int l, int k, int s)
13 {
14 int b = stare[l];
15 if ((l == 0) && (k == 0)) //De pe nivelul inferior boabele nu coboara
16 return b;
17
18 if (l == k) //Boabe ramase in recipientul initial
19 return max(0, b - s);
20
21 if (k == 0) //Boabe adunate in recipientul inferior
22 return max(0, min(b, s - (l - k) + 1));
23
24 if (s < l - k) // Timpul este mai mic decat timpul necesar
25 // sa ajunga o boaba in recipient
26 return 0;
27
28 if (s >= b + l - k) //Boabele au trecut deja prin recipient
29 return 0;
30
31 return 1; //O boaba se afla in tranzitie prin recipient
32 }
33
34 // Nivelul de pe care pornesc boabele,
35 // nivelul pe care ajung boabele, secunde
36
CAPITOLUL 8. OJI 2013 170

37 int transformareSus(int l, int k, int s)


38 {
39 int b = stare[l];
40 //printf("ts(%d %d %d %d) = ", l, k, s, b);
41 if ((l == n) && (k == n)) // Boabele nu urca
42 return b;
43
44 if (l == k) // Boabe ramase in recipientul initial
45 return max(0, b - s);
46
47 if (k == n) // Boabe adunate in recipientul superior
48 return max(0, min(b, s - (k - l) + 1));
49
50 if (s < (k - l)) // Timpul este mai mic decat timpul necesar
51 // sa ajunga o boaba in recipient
52 return 0;
53
54 if (s >= b + (k - l)) // Boabele au trecut deja prin recipient
55 return 0;
56
57 return 1; // O boaba se afla in tranzitie prin recipient
58 }
59
60 void updateStare(int secunde, int pozitie)
61 {
62 int stareNoua[1001] = {0};
63
64 if (pozitie == 1)
65 {
66 for (int i = 0; i <= n; i++)
67 if (stare[i] > 0)
68 for (int j = 0; j <= i; j++)
69 stareNoua[j] += transformareJos(i, j, secunde);
70 }
71 else
72 {
73 for (int i = 0; i <= n; i++)
74 if (stare[i] > 0)
75 for (int j = i; j <= n; j++)
76 stareNoua[j] += transformareSus(i, j, secunde);
77 }
78
79 for (int i = 0; i <= n; i++)
80 {
81 stare[i] = stareNoua[i];
82 }
83 }
84
85 int main()
86 {
87 int b, k, secunde, pozitie;
88
89 ifstream fin ("clepsidru.in");
90 ofstream fout("clepsidru.out");
91
92 fin>>n>>b;
93 fin>>k;
94
95 stare[n] = b;
96 for (int i = 0; i < k; i++)
97 {
98 fin>>secunde>>pozitie;
99 updateStare(secunde, pozitie);
100 }
101
102 fout<<n+b-1<<"\n";
103
104 if (pozitie == 1)
105 {
106 for (int i = n; i >= 2; i--)
107 fout<<stare[i]<<" "<<0<<"\n";
108 fout<<stare[1]<<" "<< stare[0]<<"\n";
109 }
110 else
111 {
112 fout<<stare[n]<<" "<< stare[n-1]<<"\n";
CAPITOLUL 8. OJI 2013 171

113 for (int i = n - 2; i >= 0; i--)


114 fout<<0<<" "<< stare[i]<<"\n";
115 }
116
117 fin.close();
118 fout.close();
119 return 0;
120 }

Listing 8.2.3: PD_clepsidru.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 long ls,lj,n,k,b,a0,an,s,p,i;
7
8 void p1(int x)
9 {
10 if(ls<n)
11 {
12 ls+=x;
13 lj+=x;
14 if(ls>=n){ls=n;lj=n+b-1;}
15 }
16 }
17
18 void p2(int x)
19 {
20 if(lj>0)
21 {
22 lj-=x;
23 ls-=x;
24 if(lj<=0){lj=0;ls=1-b;}
25 }
26 }
27
28 int main()
29 {
30 ifstream fin("clepsidru.in");
31 ofstream fout("clepsidru.out");
32
33 fin>>n>>b;
34 lj=a0=an=0;
35 ls=1-b;
36 fout<<n+b-1<<"\n";
37
38 fin>>k;
39 for(i=1;i<=k;i++)
40 {
41 fin>>s>>p;
42 if(p==1)
43 p1(s);
44 else
45 p2(s);
46 }
47
48 if(ls<=0) a0=1-ls;
49 if(lj>=n) an=lj-n+1;
50 fout<<a0<<" ";
51
52 for(i=1;i<n;i++)
53 if((i>=ls)&&(i<=lj))
54 if(p==1)
55 fout<<"0\n1 ";
56 else
57 fout<<"1\n0 ";
58 else
59 fout<<"0\n0 ";
60
61 fout<<an<<"\n";
62
63 fin.close();
64 fout.close();
CAPITOLUL 8. OJI 2013 172

65 return 0;
66 }

Listing 8.2.4: ZS_clepsidru.cpp


1 // sursa prof. Zoltan Szabo - 100 puncte
2 #include <fstream>
3 #include<iostream>
4
5 using namespace std;
6
7 int c[1003][2]; // c si c1 memoreaza doua stari consecutive ale clepsidrului
8
9 int main()
10 {
11 int n,b,i,k,timp,stare,sus,jos,bsus,bjos;
12
13 ifstream fin ("clepsidru.in");
14 ofstream fout("clepsidru.out");
15
16 fin>>n>>b;
17 fout<<n+b-1<<"\n";
18
19 sus=0; // pozitia celui mai de sus bob
20 bsus=b; // boabe sus
21 jos=0; // pozitia celui mai de jos bob
22 bjos=1; // valoare initiala din oficiu atatea boabe sunt jos,
23 // cand nu s-a ajuns la fundul clepsidrului
24 fin>>k;
25 for (i=0;i<k;++i)
26 {
27 fin>>timp>>stare;
28
29 if (stare==1)
30 {
31 jos=jos+timp;
32 if (jos<n) //in acest caz boabele nu au timp sa ajunga pana jos
33 {
34 bjos=1;
35 bsus=b-jos; // restul a ramas sus
36
37 if (bsus<=0) // iar daca acest numar e negativ
38 { sus=1-bsus; //corectam nivelul de sus
39 bsus=1; // numarul de boabe va fi 1
40 }
41 }
42 else // in acest caz boabele se acumuleaza jos
43 {
44 bjos=bjos+jos-n; // numarul boabelor calculate
45 jos=n; // boabele se acumuleaza la nivelul n
46
47 if (bjos>=b) // daca s-a calculat mai mult decat exista
48 {
49 sus=n; // atunci
50 bjos=b; // toate boabele sunt jos
51 bsus=1; // valoare din oficiu
52 }
53 else
54 {
55 sus=0;
56 bsus=b-bjos-n+1; // atatea boabe au ramas sus
57
58 if (bsus<=0) // iar daca acest numar e negativ
59 { sus=1-bsus; //corectam pozitia
60 bsus=1; // numarul de boabe va fi 1
61 }
62 }
63 }
64 }
65 else
66 {
67 sus=sus-timp;
68 if (sus>0) // comentariile la fel c si la stare==1
69 {
70 bsus=1;
CAPITOLUL 8. OJI 2013 173

71 bjos=b-n+sus; // atatea boabe sunt jos


72
73 if (bjos<=0) // iar daca acest numar e negativ
74 { jos=n+bjos-1; //corectam nivelul de jos
75 bjos=1; // numarul de boabe va fi 1
76 }
77 }
78 else
79 {
80 bsus=bsus-sus;
81 sus=0; // boabele se acumuleaza la nivelul n
82
83 if (bsus>=b) // daca s-a calculat mai mult decat exista
84 {
85 jos=0; // atunci
86 bsus=b; // toate boabele sunt sus
87 bjos=1; // valoare din oficiu
88 }
89 else
90 {
91 jos=n;
92 bjos=b-bsus-n+1; // atatea boabe au ramas sus
93
94 if (bjos<=0) // iar daca acest numar e negativ
95 {
96 jos=n+bjos-1; //corectam pozitia
97 bjos=1; // numarul de boabe va fi 1
98 }
99 }
100 }
101
102 }
103 }
104
105 if (sus==0 && jos==0)
106 c[1][1]=bsus;
107
108 if (sus==n && jos==n)
109 c[n][2]=bjos;
110
111 if (sus==0 && jos==n && stare==1)
112 {
113 c[1][1]=bsus;
114 c[n][2]=bjos;
115 for (i=2;i<=n;i++)
116 c[i][1]=1;
117 }
118
119 if (sus==0 && jos==n && stare==2)
120 {
121 c[1][1]=bsus;
122 c[n][2]=bjos;
123 for (i=1;i<=n-1;i++)
124 c[i][2]=1;
125 }
126
127 if(sus==0 && jos<n && stare==1)
128 {
129 c[1][1]=bsus;
130 c[jos+1][1]=1;
131 for (i=2;i<=jos;i++)
132 c[i][1]=1;
133 }
134
135 if(sus==0 && jos<n && stare==2)
136 {
137 c[1][1]=bsus;
138 for (i=1;i<=jos;i++)
139 c[i][2]=1;
140 }
141
142 if (sus>0 && jos==n && stare==1)
143 {
144 c[n][2]=bjos;
145 for(i=sus+1;i<=jos;++i)
146 c[i][1]=1;
CAPITOLUL 8. OJI 2013 174

147 }
148
149 if (sus>0 && jos==n && stare==2)
150 {
151 c[n][2]=bjos;
152 for(i=sus;i<jos;++i)
153 c[i][2]=1;
154 }
155
156 if (sus>0 && jos<n && stare==1)
157 {
158 for(i=sus+1;i<=jos+1;++i)
159 c[i][1]=1;
160 }
161
162 if (sus>0 && jos<n && stare==2)
163 {
164 for(i=sus;i<=jos;++i)
165 c[i][2]=1;
166 }
167
168 for (i=1;i<=n;++i)
169 fout<<c[i][1]<<" "<<c[i][2]<<"\n";
170
171 fin.close();
172 fout.close();
173
174 return 0;
175 }

8.2.3 *Rezolvare detaliat 


Capitolul 9

OJI 2012

9.1 elicop
Problema 1 - elicop 100 de puncte
Un teren de fotbal este folosit pentru aterizarea elicopterelor. Gazonul de pe stadion este
parcelat în p tr µele de aceea³i dimensiune, cu laturile paralele cu marginile terenului. Liniile cu
p tr µele de gazon sunt numerotate de sus în jos cu numerele 1, 2, ..., m, iar coloanele cu p tr µele
de gazon sunt numerotate de la stânga la dreapta cu numerele 1, 2, ..., n.
Din cauza tipului diferit de iarb  se ³tie care dintre p tr µele de gazon sunt afectate sau nu
de umbr . Acest lucru este precizat printr-un tablou bidimensional a cu m linii ³i n coloane, cu
elemente 0 ³i 1 (aij 0 înseamn  c  p tr µelul aat pe linia i ³i coloana j este afectat de umbr ,
iar aij 1 înseamn  c  p tr µelul aat pe linia i ³i coloana j nu este afectat de umbr ).
Fiecare elicopter are 3 roµi pe care se sprijin . Roµile ec rui elicopter determin  un triunghi
dreptunghic isoscel. Elicopterele aterizeaz , astfel încât triunghiurile formate s  e cu catetele
paralele cu marginile terenului. în exemplul urm tor avem patru elicoptere.

Figura 9.1: Puncte

Pentru a preciza poziµia unui elicopter pe teren este sucient s  cunoa³tem linia ³i coloana
v rfurilor ipotenuzei ³i poziµia vârfului deasupra (codicat  prin 1) sau dedesubtul ipotenuzei
(codicat  prin 1). Pentru exemplu, elicopterul din stânga sus este dat prin 1, 1, 3, 3 ³i 1,
cel din dreapta sus prin 1, 9, 5, 5 ³i 1, cel din stânga jos prin 5, 1, 6, 2 ³i 1, iar cel din
dreapta jos prin 5, 9, 6, 8 ³i 1.
Un elicopter se consider  c  a aterizat gre³it, dac  triunghiul format sub el (denit mai sus)
are mai mult de jum tate din p tr µele afectate de umbr .
Administratorul terenului de fotbal dore³te s  determine num rul N 1 de elicoptere, care nu
afecteaz  nici un p tr µel din teren ³i numerele de ordine al elicopterelor, care au aterizat gre³it în
ordine cresc toare: e1 , e2 , ..., eN 2 , ³tiind c  exist  k elicoptere codicate prin numerele 1, 2, ..., k .

Cerinµe
Scrieµi un program care s  determine, pentru ³ierul cu datele din enunµ: num rul de elicoptere
N 1, care nu afecteaz  nici un p tr µel din teren ³i numerele de ordine al elicopterelor, care au
aterizat gre³it în ordine cresc toare, precedate de num rul lor N 2.

Date de intrare
Prima linie a ³ierului de intrare elicop.in conµine dou  numere naturale m ³i n, separate
printr-un spaµiu, cu semnicaµia din enunµ.

175
CAPITOLUL 9. OJI 2012 176

Urm toarele m linii conµin câte n numere 0 sau 1, separate prin câte un spaµiu cu semnicaµia
0 - p tr µel de gazon care este afectat de umbr , respectiv 1 - p tr µel care nu este afectat de
umbr .
Pe linia m  2 se a  num rul de elicoptere k , iar pe urm toarele k linii (în ordinea codic rii
lor 1, 2, ..., k ) câte cinci numere separate prin cate un spaµiu, pentru liniile ³i coloanele ipotenuzelor
³i poziµia vârfului (1 sau 1), triunghiurilor dreptunghice asociate elicopterelor:
L1 C1 L2 C2 p

Date de ie³ire
Fi³ierul de ie³ire elicop.out va conµine dou  linii: prima linie num rul N 1 de elicoptere, care
nu afecteaz  nici un p tr µel din teren, a doua linie cu numerele naturale N 2, e1 , e2 , ..., e  N 2
separate prin câte un spaµiu, în ordine cresc toare.

Restricµii ³i preciz ri
a2 & m, n & 100; 1 & k & 40
aNu exist  suprapuneri de triunghiuri asociate la dou  elicoptere. Triunghiurile asociate
elicopterelor conµin cel puµin trei p tr µele.
a Pentru determinarea corect  a valorilor N 1 se obµine 40% din punctajul unui test, iar pentru
determinarea corect  a valorilor N 2, e1 , e2 , ..., eN 2 se obµine 60% din punctajul unui test.

Exemple
elicop.in elicop.out Explicaµii
7 9 2 Elicopterele 2 ³i 4 nu afecteaz  niciun p tr µel de gazon.
1 1 1 1 1 1 1 1 1 213
0 0 0 0 1 1 1 1 0 Elicopterele 1 ³i 3 afecteaz  ecare mai mult de jum tate din
0 0 1 0 1 1 1 0 0 num rul p tr µelelor asociate triunghiurilor dreptunghice ³i
1 1 1 0 1 1 0 1 1 deci aterizeaz  gre³it. Elicopterul 1 face umbr  la 6 p tr µele,
0 0 1 1 1 1 0 1 1 din care afectate sunt 4.
1 1 1 1 1 1 0 1 1
1 1 1 1 1 1 0 0 1 Elicopterul 3 face umbr  la 3 p tr µele, din care afectate sunt
4 dou .
1 1 3 3 -1
1 9 5 5 1
5 1 6 2 1
5 9 6 8 1

Timp maxim de executare/test: 1.0 secunde


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 5 KB

9.1.1 Indicaµii de rezolvare


Prof. Doru Anastasiu Popescu

Pentru tabloul a cu elementele ai, j , 1 & i & m, 1 & j & n, un elicopter dat prin L1, C1, L2, C2
³i p poate avea  în una din poziµiile:

Poziµia 1 (L1 $ L2 si C2 $ C1, p 1)


aL1, C2 aL1, C2  1 . . . aL1, C1  1 aL1, C1
aL1  1, C2 aL1  1, C2  1 . . . aL1  1, C1  1
. . . . . .
aL2, C2

Poziµia 2 (L1 $ L2 si C1 $ C2, p 1 )


aL1, C1
. . . . . .
aL2  1, C1 aL2  1, C1  1 . . . aL2  1, C2  1
aL2, C1 aL2, C1  1 . . . aL2, C2  1 aL1, C2
CAPITOLUL 9. OJI 2012 177

Poziµia 3 (L1<L2 si C1<C2, p= 1)


aL1, C1 aL1, C1  1 . . . aL1, C2  1 aL1, C2
aL1  1, C1  1 . . . aL1  1, C2  1 aL1  1, C2
. . . . . .
aL2, C2

Poziµia 4 (L1 $ L2 si C2 $ C1, p 1 )


aL1, C1
aL1  1, C1  1 aL1  1, C1
. . . . . .
aL2  1, C2  1 . . . aL2  1, C1  1 aL2  1, C1
aL2, C2 aL2, C2  1 . . . aL2, C1  1 aL2, C1

OBSERVAµIE: Dac  L1>L2 interschimb m perechile (L1,C1) cu (L2,C2).


Pentru ecare dintre aceste triunghiuri se calculeaz  numarul de 0 în N o1 ³i num rul de 1 în
N o2. în funcµie de aceste valori se rezolv  cele dou  cerinµe. în plus, pentru a doua cerinµ  se
reµine numerele ce codic  elicopterele într-un vector, care apoi se a³eaz  precedat de num rul
de elemente.

9.1.2 Cod surs 

Listing 9.1.1: elicopCC.cpp


1 // sursa de 100 p Chesca Ciprian
2 #include <fstream>
3
4 #define nmax 101
5 #define kmax 41
6
7 using namespace std;
8
9 short modul(short x)
10 {
11 if (x<0) return -x;
12 else return x;
13 }
14
15 short max(short x,short y)
16 {
17 if (x>=y) return x;
18 else return y;
19 }
20
21 short min(short x,short y)
22 {
23 if (x>=y) return y;
24 else return x;
25 }
26
27 int main()
28 {
29
30 short t[nmax][nmax],sol[nmax],m,n,k,x1,y1,x2,y2,v,i,j,pg,ne=0,l,c,tip,d;
31
32 ifstream f("elicop.in");
33 ofstream g("elicop.out");
34
35 //initializez vectorul de solutii
36 for(i=0;i<kmax;i++) sol[i]=0;
37
38 // citesc datele de intrare
39 f>>m>>n;
40 for(i=1;i<=m;i++)
41 for(j=1;j<=n;j++)
42 f>>t[i][j];
43 f>>k;
44
45 for(i=1;i<=k;i++)
46 {
CAPITOLUL 9. OJI 2012 178

47 f>>x1>>y1>>x2>>y2>>v;
48
49 d=(x2-x1)*(y2-y1);
50
51 if (d>0&&v==-1) tip=1;
52 if (d>0&&v==1) tip=2;
53 if (d<0&&v==1) tip=3;
54 if (d<0&&v==-1) tip=4;
55
56 pg=0;
57 switch (tip)
58 {
59 case 1 :{
60 for(l=1;l<=max(x1,x2)-min(x1,x2)+1;l++)
61 for(c=1;c<=l;c++)
62 if (t[l+min(x1,x2)-1][c+min(y1,y2)-1]==0)
63 pg++;
64 break;
65 }
66 case 2 :{
67 for(l=1;l<=max(x1,x2)-min(x1,x2)+1;l++)
68 for(c=l;c<=max(y1,y2)-min(y1,y2)+1;c++)
69 if (t[l+min(x1,x2)-1][c+min(y1,y2)-1]==0)
70 pg++;
71 break;
72 }
73 case 3 :{
74 for(l=1;l<=max(x1,x2)-min(x1,x2)+1;l++)
75 for(c=1;c<=modul(x2-x1)+2-l;c++)
76 if (t[l+min(x1,x2)-1][c+min(y1,y2)-1]==0)
77 pg++;
78 break;
79 }
80 case 4 :{
81 for(l=1;l<=max(x1,x2)-min(x1,x2)+1;l++)
82 for(c=modul(x2-x1)+2-l;
83 c<=max(y1,y2)-min(y1,y2)+1;
84 c++)
85 if (t[l+min(x1,x2)-1][c+min(y1,y2)-1]==0)
86 pg++;
87 break;
88 }
89 }
90
91 if (pg==0)
92 ne++;
93 else
94 if (pg>(modul(x2-x1)+1)*(modul(x2-x1)+2)/4)
95 sol[++sol[0]]=i;
96 }
97
98 g<<ne<<"\n";
99 g<<sol[0]<<" ";
100 for(i=1;i<=sol[0];i++)
101 g<<sol[i]<<" ";
102
103 f.close();
104 g.close();
105 return 0;
106 }

Listing 9.1.2: elicopDPA.CPP


1 #include<fstream>
2 #include<iostream>
3 #include<math.h>
4
5 using namespace std;
6
7 ifstream fin("elicop.in");
8 ofstream fout("elicop.out");
9
10 int a[110][110],m,n,i,j,k,tip[1000],
11 L1[1000],C1[1000],L2[1000],C2[1000],N1,p[1000],np,temp,t;
12
CAPITOLUL 9. OJI 2012 179

13 void interschimba(int &i, int &j)


14 {
15 int aux;
16 aux=i;
17 i=j;
18 j=aux;
19 }
20
21 //-------------------------
22 int triunghicu1(int i)
23 {
24 int L11,L22,C11,C22,t,aux,p,q,no=0;
25
26 L11=L1[i];
27 L22=L2[i];
28 C11=C1[i];
29 C22=C2[i];
30 t=tip[i];
31
32 if(L11>L22)
33 {
34 aux=L11;
35 L11=L22;
36 L22=aux;
37 aux=C11;
38 C11=C22;
39 C22=aux;
40 }
41
42 if(C11>C22)
43 if(t==1)
44 {
45 aux=0;
46 for(p=L11;p<=L22;p++)
47 {
48 for(q=C22;q<=C11-aux;q++)
49 if(a[p][q]==1)
50 no++;
51 aux++;
52 }
53 }
54 else
55 {
56 aux=0;
57 for(p=L22;p>=L11;p--)
58 {
59 for(q=C22+aux;q<=C11;q++)
60 if(a[p][q]==1)
61 no++;
62 aux++;
63 }
64 }
65
66 if(C11<C22)
67 if(t==1)
68 {
69 aux=0;
70 for(p=L11;p<=L22;p++)
71 {
72 for(q=C11+aux;q<=C22;q++)
73 if(a[p][q]==1)
74 no++;
75 aux++;
76 }
77 }
78 else
79 {
80 aux=0;
81 for(p=L11;p<=L22;p++)
82 {
83 for(q=C11;q<=C11+aux;q++)
84 if(a[p][q]==1)
85 no++;
86 aux++;
87 }
88 }
CAPITOLUL 9. OJI 2012 180

89
90 return no;
91 }
92
93 //---------------------
94
95 void afis()
96 {
97 int i;
98 fout<<N1<<"\n"<<np<<" ";
99 for(i=1;i<=np;i++)
100 fout<<p[i]<<" ";
101 fout.close();
102 }
103
104 int main()
105 {
106 fin>>m>>n;
107
108 for(i=1;i<=m;i++)
109 for(j=1;j<=n;j++)
110 fin>>a[i][j];
111 fin>>k;
112
113 for(i=1;i<=k;i++)
114 fin>>L1[i]>>C1[i]>>L2[i]>>C2[i]>>tip[i];
115
116 fin.close();
117
118 np=0;
119 for(i=1;i<=k;i++)
120 {
121 t=triunghicu1(i);
122 //cout<<"Q "<<t<<"\n";
123
124 temp=abs(L1[i]-L2[i]);
125 temp=(temp+1)*(temp+2)/2;
126 //cout<<"*"<<L1[i]<<" "<<L2[i]<<" "<<temp<<"\n";
127
128 if(t==temp)
129 N1++;
130 if(t<temp/2.0)
131 {
132 np++;
133 p[np]=i;
134 }
135 }
136
137 afis();
138 return 0;
139 }

Listing 9.1.3: elicopGB.cpp


1 #include<cstdio>
2 #include<cmath>
3 #include<algorithm>
4
5 using namespace std;
6
7 int m,n,Teren[101][101],E_Sol[101],Elicop[41][5],k,nre,Caz,nr_umbra;
8
9 FILE *f,*g;
10
11 void citire()
12 {
13 fscanf(f,"%d%d",&m,&n);
14
15 for(int i=1;i<=m;i++)
16 for(int j=1;j<=n;j++)
17 fscanf(f,"%d",&Teren[i][j]);
18
19 fscanf(f,"%d",&k);
20
21 for(int i=1;i<=k;i++)
CAPITOLUL 9. OJI 2012 181

22 fscanf(f,"%d%d%d%d%d",&Elicop[i][1],&Elicop[i][2],
23 &Elicop[i][3],&Elicop[i][4],&Elicop[i][0]);
24 fclose(f);
25 }
26
27 int main()
28 {
29 f=fopen("elicop.in","r");
30 g=fopen("elicop.out","w");
31
32 nre=0;
33 citire();
34 int nr1;
35
36 for(int i=1;i<=k;i++)
37 {
38 if((Elicop[i][3]-Elicop[i][1])*(Elicop[i][4]-Elicop[i][2])>0 &&
39 Elicop[i][0]==-1)
40 Caz=1;
41 else
42 if((Elicop[i][3]-Elicop[i][1])*(Elicop[i][4]-Elicop[i][2])>0 &&
43 Elicop[i][0]==1)
44 Caz=2;
45 else
46 if((Elicop[i][3]-Elicop[i][1])*(Elicop[i][4]-Elicop[i][2])<0&&
47 Elicop[i][0]==-1)
48 Caz=3;
49 else
50 Caz=4;
51 nr1=0;
52
53 int J=max(Elicop[i][1],Elicop[i][3])-min(Elicop[i][1],Elicop[i][3])+1;
54 int Lx1=min(Elicop[i][1],Elicop[i][3]);
55 int Ly1=min(Elicop[i][2],Elicop[i][4]);
56 int Ly2=max(Elicop[i][2],Elicop[i][4]);
57
58 switch(Caz)
59 {
60 case 1: {
61 for(int j=1;j<=J;j++)
62 for(int l=1;l<=j;l++)
63 nr1+=!(Teren[j+Lx1-1][l+Ly1-1]);
64 break;
65 }
66 case 2: {
67 for(int j=1;j<=J;j++)
68 for(int l=j;l<=Ly2-Ly1+1;l++)
69 nr1+=!(Teren[j+Lx1-1][l+Ly1-1]);
70 break;
71 }
72 case 3: {
73 for(int j=1;j<=J;j++)
74 for(int l=(int)abs(Elicop[i][1]-Elicop[i][3])+2-j;
75 l<=Ly2-Ly1+1;
76 l++)
77 nr1+=!(Teren[j+Lx1-1][l+Ly1-1]);
78 break;
79 }
80 case 4: {
81 for(int j=1;j<=J;j++)
82 for(int l=1;
83 l<=(int)abs(Elicop[i][1]-Elicop[i][3])+2-j;
84 l++)
85 nr1+=!(Teren[j+Lx1-1][l+Ly1-1]);
86 break;
87 }
88 }
89
90 nre+=!nr1;
91 if(nr1 > (abs(Elicop[i][1]-Elicop[i][3])+1)*
92 (abs(Elicop[i][1]-Elicop[i][3])+2)/4)
93 E_Sol[++nr_umbra]=i;
94 }
95
96 fprintf(g,"%d\n%d ",nre,nr_umbra);
97
CAPITOLUL 9. OJI 2012 182

98 for(int i=1;i<=nr_umbra;i++)
99 fprintf(g,"%d ",E_Sol[i]);
100
101 fclose(g);
102 return 0;
103 }

9.1.3 *Rezolvare detaliat 

9.2 roata
Problema 2 - roata 90 de puncte
Una dintre atracµiile celebrului parc de distracµii Prater din Viena este
Marea Roat  Vienez . Din ea se poate admira priveli³tea întregii Viene.
Roata are n cabine, numerotate de la 1 la n în sens orar ³i dispuse simetric
pe circumferinµa roµii. îmbarcarea clienµilor se face în cabina în care roata
este tangent  cu solul, iar rotirea începe cu cabina 1 aat  în poziµia de
îmbarcare ³i se face în sens antiorar. Un client pl te³te pentru o rotire 1
EUR ³i poate cump ra un num r oarecare de rotiri. Figura 9.2: roata
Cei p clienµi care doresc utilizarea roµii trebuie s  respecte urm toarea procedur : clientul cu
num rul de ordine i î³i cump r  un bilet pe care sunt înscrise num rul s u de ordine ³i num rul
de rotiri ci, 1 & i & p, apoi se a³eaz  la rând. Când în poziµia de îmbarcare este o cabin  liber 
sau se elibereaz  o cabin , roata se opre³te ³i urc  urm torul clientul. Un client coboar  dup  ce
se efectueaz  num rul de rotiri înscris pe bilet.

Cerinµe
S  se scrie un program care, cunoscând num rul n de cabine al roµii, num rul p de clienµi,
precum ³i num rul de rotiri cump rate de ecare client, ci , 1 & i & p, s  calculeze:
a suma total  încasat  de administratorul roµii de la clienµi;
a ordinea în care coboar  clienµii din roat ;
a num rul cabinei din care coboar  ultimul client.

Date de intrare
Fi³ierul de intrare roata.in conµine pe primul rând num rul natural n, pe al doilea rând
num rul natural p iar pe al treilea rând numerele naturale ci , 1 & i & p, separate printr-un spaµiu,
cu semnicaµiile de mai sus.

Date de ie³ire
Fi³ierul de ie³ire roata.out va conµine pe prima linie suma total  încasat , pe a doua linie
numerele de ordine ale clienµilor, în ordinea coborârii, separate printr-un spaµiu, iar pe a treia linie
num rul cabinei din care va coborî ultimul client.

Restricµii ³i preciz ri
a2 & n & 360
a1 & p & 100000
a 1 & ci & 100000
a pentru rezolvarea primei cerinµe se acord  20% din punctaj, iar pentru celelalte dou  cerinµe
se acord  câte 40% din punctaj ecare.

Exemple
roata.in roata.out Explicaµii
CAPITOLUL 9. OJI 2012 183

4 29 Roata are n 4 cabine ³i num rul de clienµi este p 7.


7 3524176 Primul client cump r  6 rotiri, al doilea 4 rotiri , ... , iar al
6415283 3 ³aptelea client cump r  3 rotiri. Suma total  încasat  este de 29
EUR.
Dup  ce primii 4 clienµi se urc  în roat  ³i se efectueaz  o rotire
complet , primul care coboar  este clientul al 3-lea ³i imediat se
urc  clientul al 5-lea. Dup  înc  2 rotiri, clientul al 5-lea coboar 
³i se urc  clientul al 6-lea. Dup  înc  o rotire coboar  clientul al
2-lea ³i se urc  al 7-lea client. Ultimii 4 clienµi coboar  în ordinea
4, 1, 7, 6.
Cabina din care coboar  ultimul client este cabina cu num rul 3.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 5 KB

9.2.1 Indicaµii de rezolvare


Varianta 1
Prof. Che³c  Ciprian - Grup ³colar Costin Neniµescu Buz u

În cazul în care n ' p se încarc  num rul de rotiri într-un vector ³i se determin  suma total 
încasat  ca ind suma valorilor din vector, apoi se determin  poziµia maximului dintre elementele
vectorului, care reprezint  cabina din care va coborî ultimul client.
Pentru a determina ordinea în care coboar  clienµii, se determin  poziµia minimul dintre valorile
vectorului ³i se a³eaz , apoi se elimin  acest minim ³i se reia operaµia anterioar .
În cazul în care n $ p se încarc  într-un vector c i 1 & i & p num rul de rotiri ³i se iniµializeaz 
un vector cu num rul de ordine al clienµilor o i 1 & i & n.
Se calculeaz  ³i se a³eaz  suma total  încasat  ca ind suma valorilor din vector.
Considerând c  primii n clienµi sunt urcaµi în roat , pentru restul clienµilor cu numere de
ordine de la n  1 la p se procedeaz  astfel :
- se determin  poziµia minimului dintre primele n valori ale vectorului c i ³i se a³eaz  num rul
de ordine al acestuia;
- se încarc  un nou client în roat  ad ugând pe poziµia minimului calculat precedent datele
noului client (num rul de rotiri ³i num rul s u de ordine);
De remarcat c  dup  ce a fost calculat  poziµia unui minim, pentru a-l elimina, e sucient s 
adun m pe poziµia acestuia num rul de rotiri ale urm torului client ³i s  atribuim vectorului de
ordine num rul de ordine al clientului; aceast  metod  evit  înc  o parcurgerea a vectorului c i
în vederea prelucr rii informaµiilor clienµilor la o coborare, îns  are dezavantajul c  prin adun ri
repetate se poate ajunge la valori mari care trebuie gestionate cu tipuri de date corect alese.
- dup  prelucrarea tuturor clienµilor cu numerele de ordine de la n  1 la p se intr  pe secvenµa
descris  în cazul n ' p.

Varianta 2
prof. Florentina Ungureanu - Colegiul Naµional de Informatic  Piatra Neamµ

Vectorul roata reµine în poziµia i num rul de ordine al clientului din cabina i, respectiv p+1
dac  aceast  cabin  este liber . (Iniµializat cu 1, 2, ..., n pentru n & p, respectiv 1, 2, ..., p, p  1, p 
1, ...).
Vectorul ture conµine pentru început num rul de ture pl tite de ecare client, dar va  actuali-
zat indicând la nal, pentru ecare client, num rul total de ture f cute de roat  pân  la momentul
coborârii acestuia.
Rezolvare:
1. Suma valorilor de pe a treia linie a ³ierului furnizeaz  prima valoare cerut .
2. Determinarea ordinii coborârii:
Etapa I coboar -urc  (n $ p):
a. Pentru ecare client i (i % n) r mas în a³teptare se determin  num rul de ordine k al cabinei
care se elibereaz  - cea al c rei client are num r minim de ture pân  la coborâre ³i se a³eaz 
num rul de ordine al clientului.
CAPITOLUL 9. OJI 2012 184

b. Clientul i urc  în cabina k , iar num rul de ture pl tit de acesta se actualizeaz  devenind
egal cu num rul total de ture f cute de roat  pân  când acest client va coborî.
Etapa II coboar :
a. Se determin  num rul de ordine k al cabinei care se elibereaz  - cea al c rei client are num r
minim de ture pân  la coborâre.
b. Dac  num rul de ture corespunz tor este mai mic decât inf (mai exist  clienµi în roat ):
a se a³eaz  num rul de ordine al clientului;
a se completeaz  cabina k cu valoarea p  1 - (cabin  goal );
a se reia pasul a.
c. Când nu mai sunt clienµi în roat  se a³eaz  num rul cabinei din care a coborât ultimul
client.

9.2.2 Cod surs 

Listing 9.2.1: roataCC.cpp


1 // sursa de 100 p - prof. Chesca Ciprian
2 #include <fstream>
3
4 #define nmax 361
5 #define pmax 100001
6
7 using namespace std;
8
9 ifstream f("roata.in");
10 ofstream g("roata.out");
11
12 // n = numarul de cabine, p = numarul de persoane
13
14 int main()
15 {
16 // o - ordinea la urcare, c - costul biletului
17
18 int n,p,i,k,pozmax,pozmin,o[nmax],c[pmax];
19 long long s=0;
20
21 f>>n>>p;
22
23 // initializez numerele de ordine ale primilor n clienti
24 for(i=1;i<=n;i++)
25 o[i]=i;
26
27 // citesc numarul de rotiri si calculez suma totala incasata
28 for(i=1;i<=p;i++)
29 {
30 f>>c[i];
31 s+=c[i];
32 }
33
34 // afisez suma totala incasata
35 g<<s<<"\n";
36
37 if (n<p)
38 for(k=n+1;k<=p;k++)
39 {
40 // determin clientul care urmeaza sa coboare
41 pozmin=1;
42 for(i=2;i<=n;i++)
43 if (c[i]<c[pozmin])
44 pozmin=i;
45
46 // afisez ordinea la coborare
47 g<<o[pozmin]<<" ";
48
49 // resetez toate valorile lui c[i] pentru ca sunt prea mari
50 if (c[pozmin]>10000000)
51 for(i=1;i<=n;i++)
52 c[i]-=10000000;
53
54 c[pozmin]+=c[k]; // o mica smecherie - in loc sa scad
55 // din toate minimul, am adunat
CAPITOLUL 9. OJI 2012 185

56 o[pozmin]=k;
57 }
58 else
59 n=p;
60
61 // determin numarul cabinei din care va cobora ultimul client
62 pozmax=1;
63 for(i=2;i<=n;i++)
64 if (c[i]>=c[pozmax])
65 pozmax=i;
66
67 for(k=1;k<=n;k++)
68 {
69 // determin clientul care urmeaza sa coboare
70 pozmin=1;
71 for(i=2;i<=n;i++)
72 if (c[i]<c[pozmin]) pozmin=i;
73
74 // afisez ordinea la coborare
75 g<<o[pozmin]<<" ";
76
77 c[pozmin]+=100001;
78 }
79
80 // afisez numarul cabinei din care va cobora ultimul client
81 g<<"\n"<<pozmax<<"\n";
82
83 f.close();
84 g.close();
85 return 0;
86 }

Listing 9.2.2: roataPC.cpp


1 // sursa 100p - prof. Carmen Popescu - implementare folosind set din stl
2
3 #include <fstream>
4 #include <set>
5 #include <iostream>
6
7 using namespace std;
8
9 ifstream f("roata.in");
10 ofstream g("roata.out");
11
12 int n,p;
13 long long s;
14 int a[100001];
15
16 struct nd
17 {
18 int cab,cl;
19 long long rot;
20 };
21
22
23 bool comp (nd lhs, nd rhs)
24 {
25 return lhs.rot<rhs.rot || (lhs.rot==rhs.rot && lhs.cab<rhs.cab);
26 }
27
28 bool( *p_comp)(nd,nd) = comp;
29
30 set<nd,bool( * )(nd,nd)> m(p_comp);
31 set<nd,bool( * )(nd,nd)>::iterator it;
32
33 int main ()
34 {
35 nd c;
36 int i;
37 set<nd,bool( * )(nd,nd)>::iterator it;
38
39 f>>n>>p;
40 s=0;
41 for (i=0;i<p;i++)
CAPITOLUL 9. OJI 2012 186

42 {
43 f>>a[i]; s+=a[i];
44 }
45
46 g<<s<<"\n";
47 if (p<=n)
48 {
49 for (i=0;i<p;i++)
50 {
51 c.cab=i+1;
52 c.cl=i+1;
53 c.rot=a[i];
54 m.insert(c);
55 }
56
57 it=m.begin();
58 c=*it;
59 g<<c.cl;
60 it++;
61 while (it!=m.end())
62 {
63 c=*it;
64 g<<" "<<c.cl;
65 it++;
66 }
67
68 g<<"\n";
69 g<<c.cab<<"\n";
70 }
71 else
72 {
73 for (i=0;i<n;i++)
74 {
75 c.cab=i+1;
76 c.cl=i+1;
77 c.rot=a[i];
78 m.insert(c);
79 }
80
81 int pr=0;
82 for (i=n;i<p;i++)
83 {
84 c=*m.begin();
85
86 if (pr!=0)
87 g<<" ";
88 else
89 pr=1;
90
91 g<<c.cl;
92
93 c.rot+=a[i];
94 c.cl=i+1;
95
96 m.erase(m.begin());
97 m.insert(c);
98 }
99
100 for (it=m.begin();it!=m.end();it++)
101 {
102 c=*it;
103 if (pr!=0)
104 g<<" ";
105 else
106 pr=1;
107 g<<" "<<c.cl;
108 }
109
110 g<<"\n"<<c.cab<<"\n";
111 }
112
113 g.close();
114 }
CAPITOLUL 9. OJI 2012 187

9.2.3 *Rezolvare detaliat 


Capitolul 10

OJI 2011

10.1 vase
Problema 1 - vase 100 de puncte
Speciali³tii chimi³ti au reu³it crearea în laborator a unei game diversicate
de substanµe lichide nemiscibile (care nu se amestec  între ele), de aceea³i
densitate ³i de culori diferite.
Acest rezultat a fost utilizat de c tre speciali³tii zicieni pentru studiul
principiului vaselor comunicante. Conform acestui principiu într-un sistem de
vase comunicante nivelul lichidului este acela³i, indiferent de forma vaselor.
Experimentele zicienilor se desf ³oar  astfel: Figura 10.1:
Într-un sistem cu dou  vase comunicante, gradat identic pe ecare ramur  vase
cu 0, 1, 2, 3, ..., zicienii introduc un num r de n lichide, pe ramura din stânga sau pe ramura din
dreapta. Volumele introduse din ecare lichid, notate cu Vi (1 & i & n), sunt numere naturale
nenule pare astfel încât, la echilibru, orice lichid se va a³eza între dou  gradaµii de aceea³i parte a
unei ramuri sau pe cele dou  ramuri ale sistemului de vase comunicante. Lichidele sunt identicate
prin intermediul culorii acestora, culori numerotate cu 1, 2, 3, ..., n.
Introducerea lichidelor în sistemul cu dou  vase comunicante se face în ordinea cresc toare a
numerelor culorilor, începând cu lichidul de culoare 1.
Scopul experimentului este de a determina gradaµia maxim 
la care se ridic  lichidele în sistemul cu dou  vase comunicante,
precum ³i între ce gradaµii se g se³te un lichid de culoare x, dintre
cele introduse.
De exemplu, dac  în sistemul cu dou  vase comunicante se
introduc n 3 lichide în ordinea: V1 4 lichid de culoare 1
introdus prin ramura din dreapta (operaµie codicat  4D), V2
4 lichid de culoare 2 introdus prin ramura din stânga (operaµie
codicat  4S ) ³i V3 2 lichid de culoare 3 introdus prin ramura
din stânga (operaµie codicat  2S ) atunci gradaµia maxim  la care
se ridic  nivelul lichidelor în sistemul cu dou  vase comunicante
este 5, iar lichidul de culoare x 2 se g se³te între gradaµiile: 3 pe
ramura din stânga 3S  ³i 1 pe ramura din dreapta 1D, conform
gurii al turate.

Cerinµe Figura 10.2: vase

S  se scrie un program care cunoscând num rul n de lichide


introduse în sistemul cu dou  vase comunicante, volumul Vi ³i
ramura prin care se face introducerea lichidului de culoare i (1 & i & n), precum ³i culoarea x, s 
calculeze gradaµia maxim  la care se ridic  lichidele în acest sistem la echilibru ³i între ce gradaµii
se g se³te lichidul de culoare x.

Date de intrare
Prima linie a ³ierului de intrare vase.in conµine un singur num r natural nenul n, cu sem-
nicaµia de mai sus. Fiecare linie, din urm toarele n, conµine câte dou  valori separate printr-un
spaµiu: un num r natural nenul par ³i o liter  mare, S sau D, reprezentând volumul introdus

188
CAPITOLUL 10. OJI 2011 189

din lichidul de culoare i, respectiv ramura (S pentru ramura din stânga ³i D pentru ramura din
dreapta) prin care se face introducerea acestuia. Linia n  2 a ³ierului de intrare conµine un
singur num r nenul x ce reprezint  culoarea lichidului c utat.

Date de ie³ire
Fi³ierul de ie³ire vase.out va conµine pe prima linie un num r natural nenul ce reprezint  gra-
daµia maxim  la care se ridic  lichidele în sistemul de vase comunicante la echilibru. Urm toarele
dou  linii vor conµine ecare câte dou  valori separate printr-un spaµiu: un num r natural ³i o
liter  mare (S sau D), reprezentând gradaµia ³i ramura între care se a³eaz  lichidul c utat.

Restricµii ³i preciz ri
a 1 & x & n & 100000
a 2 & V i & 100000 pentru 1 & i & n
a sistemul de vase este gradat în acelea³i unit µi de m sur  în care sunt exprimate volumele
de lichid;
a dac  lichidul c utat, de culoare x, se a³eaz  pe aceea³i ramur  se va a³a întâi gradaµia
superioar  ³i apoi cea inferioar ;
a dac  lichidul c utat, de culoare x, se a³eaz  pe ramuri diferite se va a³a întâi gradaµia de
pe ramura din stânga ³i apoi cea de pe ramura din dreapta;
a dac  una dintre gradaµiile între care se situeaz  lichidul c utat, de culoare x, este 0 atunci
se consider  c  aceast  gradaµie se g se³te pe aceea³i ramur  cu cealalt  gradaµie;
a pentru rezolvarea primei cerinµe se acord  20% din punctaj, iar pentru a doua cerinµ  80%
din punctaj.

Exemple
vase.in vase.out Explicaµii
3 5 Se introduc 3 lichide în sistemul de dou  vase comunicante:
4D 3S - primul cu volumul 4, se introduce prin dreapta ³i are culoarea 1;
4S 1D - al doilea cu volumul 4, se introduce prin stânga ³i are culoarea 2;
2S - al treilea cu volumul 2, se introduce prin stânga ³i are culoarea 3;
2 Se caut  gradaµiile ce corespund lichidului de culoare 2.
Gradaµia maxim  la care ajunge nivelul lichidului este 5.
Lichidul de culoare 2 se a³eaz  între gradaµiile 3 pe ramura din stânga
³i 1 pe ramura din dreapta.

Timp maxim de executare/test: 0.5 secunde


Memorie: total 4 MB din care pentru stiv  3.5 MB
Dimensiune maxim  a sursei: 5 KB

10.1.1 Indicaµii de rezolvare


prof. Che³c  Ciprian - Grup ³colar Costin Neniµescu Buz u

O prim  abordare a problemei pleac  de la ideea c  înc  de la citirea datelor despre lichidele
introduse în sistemul de vase comunicante se face separarea acelora introduse pe ramura stâng  faµ 
de cele introduse pe ramura dreapt  în dou  structuri de date identice (tablouri unidimensionale),
câte un tablou pentru volume ³i respectiv culoare. În acela³i timp se calculeaz  suma tuturor
volumelor în vederea determin rii echilibrului ³i deci a gradaµiei maxime la care se ridic  lichidele.
Pentru rezolvarea celei de-a doua cerinµe se face o c utare secvenµial  în funcµie de culoare pe
cele dou  ramuri, actualizând în permanenµ  gradaµiile între care se g se³te lichidul curent. Se
analizeaz  cu atenµie a³area în ³ierul de ie³ire a rezultatelor, în funcµie de preciz rile problemei
referitoare la modul de a³are (întâi gradaµia superioar  ³i apoi cea inferioar , înt i ramura stâng 
³i apoi cea dreapt , s.a.m.d.)
Aceast  abordare necesit  îns  mai mult  memorie deoarece se reµine ³i culoarea lichidului
introdus, întrucât s-a facut separarea pe ramuri a lichidelor.
O a doua abordare a problemei se poate face prin preluarea datelelor despre lichidele introduse
în ordinea culorilor (a citirii datelor), adic  în dou  tablouri unidimensionale (unul pentru volume
³i altul pentru ramur ) de la 1 la n.
CAPITOLUL 10. OJI 2011 190

Apoi se calculeaz  nivelul la care se ajunge pân  la lichidul c utat, se determin  intervalul
iniµial al lichidului c utat ³i se actualizeaz  intervalul ocupat de lichidul c utat, în funcµie de
direcµia curent  pentru lichidele care au mai r mas de ad ugat.
Aceast  soluµie necesit  mai puµin  memorie deoarece memorarea culorilor s-a f cut natural
prin indicele vectorilor utilizaµi.

10.1.2 Cod surs 

Listing 10.1.1: v1_vase.cpp


1 //prof. Silviu Candale - 100 p
2 #include <fstream>
3 #include <iostream>
4
5 #define NN 100001
6
7 using namespace std;
8
9 int main()
10 {
11 int n,x,v[NN];
12 long long int s=0, // cantitatea totala
13 st, dr; //intervalul ocupat de lichidul x
14 char d[NN];
15
16 ifstream fin("vase.in");
17
18 fin>>n;
19 for(int i=1; i<=n; ++i)
20 fin>>v[i]>>d[i], s+=v[i];
21 fin>>x;
22
23 //calculez nivelul la care se ajunge pana la lichidul x
24 long long ss=0;
25 for(int i=1; i<x; ++i)
26 ss += v[i];
27 ss /= 2;
28
29 //determin intervalul initial al lichidului x
30 int i=x;
31 if(d[i]==’S’)
32 st = -ss-v[i]/2, dr = -ss + v[i]/2;
33 else
34 st = ss-v[i]/2, dr = ss + v[i]/2;
35
36 for(i = x+1 ; i<=n ; ++i)
37 {
38 //actualizez intervalul ocupat de lichidul x,
39 // in functie de directia curenta
40 if(d[i]==’S’)
41 st = st+v[i]/2, dr = dr + v[i]/2;
42 else
43 st = st-v[i]/2, dr = dr - v[i]/2;
44 }
45
46 ofstream fout("vase.out");
47
48 fout<<s/2<<endl;
49
50 if(st>=0)
51 fout<<dr<<" D\n"<<st<<" D\n";
52 else
53 if(dr<=0)
54 fout<< -st <<" S\n"<< -dr <<" S\n";
55 else
56 fout<< -st <<" S\n"<< dr <<" D\n";
57
58 return 0;
59 }

Listing 10.1.2: v2_vase.cpp


CAPITOLUL 10. OJI 2011 191

1 //prof.Carmen Minca - 100 p


2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("vase.in");
7 ofstream g("vase.out");
8
9 long s[100001][2], d[100001][2],ps=-1,pd=-1,x;
10
11 int main()
12 {
13 long long ns=0, nd=0, fin,i,c,n,echi,dif,ss,sd,es,ed;
14 char a;
15
16 f>>n;
17 for(i=1;i<=n;i++)
18 {
19 f>>c>>a;
20 if (a==’S’)
21 {
22 ps++;
23 s[ps][1]=c;
24 s[ps][0]=i;
25 ns=ns+c;
26 }
27 else
28 {
29 pd++;
30 d[pd][1]=c;
31 d[pd][0]=i;
32 nd=nd+c;
33 }
34 }
35
36 f>>x;
37
38 echi=(ns+nd)/2;
39 es=-1;
40 ss=0;
41 for(i=0;i<=ps;i++)
42 if(s[i][0]==x)
43 {
44 es=i;
45 break;
46 }
47 else
48 ss+=s[i][1];
49
50 g<<echi<<endl;
51
52 ed=-1;
53 sd=0;
54 for(i=0;i<=pd;i++)
55 if(d[i][0]==x)
56 {
57 ed=i;
58 break;
59 }
60 else
61 sd+=d[i][1];
62
63 if(ns==echi)
64 {
65 if(es==-1)
66 g<<sd+d[i][1]<<" D"<<endl<<sd<<" D";
67 else
68 g<<ss+s[i][1]<<" S"<<endl<<ss<<" S";
69 }
70 else
71 if(es>-1)
72 if(ns<echi)
73 {
74 dif=echi-ns;
75 ss+=dif;
CAPITOLUL 10. OJI 2011 192

76 fin=ss+s[es][1];
77 g<<fin<<" S"<<endl<<ss<<" S";
78 }
79 else
80 {
81 dif=-echi+ns;
82 ss-=dif;
83 fin=ss+s[es][1];
84 if (ss<0)
85 if(fin==0)
86 g<<-ss<<" D"<<endl<<0<<" D";
87 else
88 if(fin<0)
89 {
90 fin=-fin;
91 ss=-ss;
92 if(fin>ss)
93 g<<fin<<" D"<<endl<<ss<<" D";
94 else
95 g<<ss<<" D"<<endl<<fin<<" D";
96 }
97 else
98 g<<fin<<" S"<<endl<<-ss<<" D";
99 else
100 g<<fin<<" S"<<endl<<ss<<" S";
101 }
102 else
103 if(nd<echi)
104 {
105 dif=echi-nd;
106 sd+=dif;
107 fin=sd+d[ed][1];
108 g<<fin<<" D"<<endl<<sd<<" D";
109 }
110 else
111 {
112 dif=-echi+nd;
113 sd-=dif;
114 fin=sd+d[ed][1];
115 if (sd<0)
116 if(fin<=0)
117 g<<-sd<<" S"<<endl<<-fin<<" S";
118 else
119 g<<-sd<<" S"<<endl<<fin<<" D";
120 else
121 g<<fin<<" D"<<endl<<sd<<" D";
122 }
123
124 return 0;
125 }

Listing 10.1.3: v3_vase.cpp


1 //prof. Chesca Ciprian - 100 p
2 #include <fstream>
3
4 #define nmax 100001
5
6 using namespace std;
7
8 struct lichid
9 {
10 long cant; // cantitatea de substanta;
11 char ram; // ramura pe unde se toarna
12 long cul; //culoarea substantei
13 };
14
15 lichid st[nmax],dr[nmax];
16 long long vst=0,vdr=0;
17
18 ifstream f("vase.in");
19 ofstream g("vase.out");
20
21 int main()
22 {
CAPITOLUL 10. OJI 2011 193

23 long long i,n,x,z,mij,l1=0,l2=0,h1,h2,gasit,s=0;


24 char c,l1r=’ ’,l2r=’ ’;
25
26 f>>n;
27
28 // citesc datele sub forma a 2 stive st si dr si calculez suma totala
29 for(i=1;i<=n;i++)
30 {
31 f>>x>>c;
32 s+=x;
33 switch (c)
34 {
35 case ’S’:
36 st[++vst].cant=x;
37 st[vst].ram=c;
38 st[vst].cul=i;
39 break;
40 case ’D’:
41 dr[++vdr].cant=x;
42 dr[vdr].ram=c;
43 dr[vdr].cul=i;
44 break;
45 }
46 }
47
48 // culoarea pe care o caut
49 f>>z;
50
51 //calculez punctul de echilibru
52 mij=s/2;
53
54 h1=mij;
55 gasit=0;
56 while(h1>-mij&&vst>0&&!gasit)
57 {
58 if (st[vst].cul==z)
59 {
60 if (h1<0)
61 {
62 l1=-h1;
63 l1r=’D’;
64 }
65 else
66 { l1=h1;
67 l1r=’S’;
68 }
69
70 if (h1-st[vst].cant<0)
71 {
72 l2=-(h1-st[vst].cant);
73 l2r=’D’;
74 }
75 else
76 {
77 l2=h1-st[vst].cant;
78 l2r=’S’;
79 }
80
81 gasit=1;
82 }
83
84 h1-=st[vst--].cant;
85 }
86
87 h2=mij;
88 while(h2>-mij&&vdr>0&&!gasit)
89 {
90 if (dr[vdr].cul==z)
91 {
92 if (h2<0)
93 {
94 l1=-h2;
95 l1r=’S’;
96 }
97 else
98 {
CAPITOLUL 10. OJI 2011 194

99 l1=h2;
100 l1r=’D’;
101 }
102
103 if (h2-dr[vdr].cant<0)
104 {
105 l2=-(h2-dr[vdr].cant);
106 l2r=’S’;
107 }
108 else
109 {
110 l2=h2-dr[vdr].cant;
111 l2r=’D’;
112 }
113
114 gasit=1;
115 }
116
117 h2-=dr[vdr--].cant;
118 }
119
120 if (l1==0) l1r=l2r;
121 if (l2==0) l2r=l1r;
122
123 // scriu rezultatele in fisier
124 g<<mij<<"\n";
125 if (l1r==’D’&&l2r==’S’)
126 {
127 g<<l2<<" "<<l2r<<"\n";
128 g<<l1<<" "<<l1r<<"\n";
129 }
130 else
131 if (l1r==’S’&&l2r==’D’)
132 {
133 g<<l1<<" "<<l1r<<"\n";
134 g<<l2<<" "<<l2r<<"\n";
135 }
136 else
137 if (l1>l2)
138 {
139 g<<l1<<" "<<l1r<<"\n";
140 g<<l2<<" "<<l2r<<"\n";
141 }
142 else
143 {
144 g<<l2<<" "<<l2r<<"\n";
145 g<<l1<<" "<<l1r<<"\n";
146 }
147
148 f.close();
149 g.close();
150 return 0;
151 }

10.1.3 *Rezolvare detaliat 

10.2 cri
Problema 2 - cri 100 de puncte
Furnicuµa ³i-a construit un depozit pentru gr unµe pe o suprafaµ  de teren dreptunghiular 
³i l-a compartimentat în N ˜ M camere identice, de form  p tratic , dispuse câte M pe direcµia
Ox ³i câte N pe direcµia Oy . Din ecare camer  se poate intra în orice camer  învecinat  cu ea
(camer  care are un perete comun cu aceasta).
În ecare camer , identicat  prin coordonatele sale, ca în
desenul al turat în care N 5 ³i M 4, furnica a depozitat
o cantitate de gr unµe. De exemplu, în camera de coordonate
I, J  este depozitat  cantitatea CIJ de gr unµe.
CAPITOLUL 10. OJI 2011 195

Atât intrarea cât ³i ie³irea din depozit se poate face doar


prin cele patru camere din colµurile depozitului, adic  cele de
coordonate 1, 1, 1, M , N, 1 ³i N, M  care comunic  cu
exteriorul.
Pentru a asigura circulaµia aerului în depozit, furnica a
montat un sistem de ventilaµie în camera de coordonate X, Y .
V zând ce multe gr unµe are furnica pentru iarn , vecinul
ei, lene³ul greiera³ Cri, s-a hot rât s  fure din ele.
Cri s-a gândit s  intre în depozit prin sistemul de ventilaµie din camera de coordonate X, Y 
³i s  ias  prin una din cele 4 camere din colµurile depozitului care comunic  cu exteriorul.
A studiat planul depozitului ³i a împ rµit camerele în patru zone:
a prima zon , numerotat  cu 1, conµine toate camerele de cordonate I, J  cu 1 & I & X ³i
1 & J & Y , cu ie³irea prin camera de coordonate 1, 1
a a doua zon , numerotat  cu 2, conµine toate camerele de cordonate I, J  cu 1 & I & X ³i
Y & J & M , cu ie³irea prin camera de coordonate 1, M 
a a treia zon , numerotat  cu 3, conµine toate camerele de cordonate I, J  cu X & I & N ³i
1 & J & Y , cu ie³irea prin camera de coordonate N, 1
a a patra zon , numerotat  cu 4, conµine toate camerele de cordonate I, J  cu X & I & N ³i
Y & J & M , cu ie³irea prin camera de coordonate (N,M)
Cri va intra doar într-una din cele patru zone ³i va fura gr unµele doar din camerele conµinute
de zona aleas . Pentru a nu declan³a alarma furnicuµei, el va trebui s  treac  cel mult o dat  prin
ecare camer  din zon , s  fure întreaga cantitate de gr unµe din aceasta ³i s  ias  din depozit
prin camera ce comunic  cu exteriorul, corespunz toare zonei alese.
Cri va trebui s  aleag  zona în care va intra astfel încât cantitatea total  T de gr unµe furate
s  e maxim , iar num rul K de camere prin care va trece s  e minim.
Cerinµe
Scrieµi un program care s  determine numerele naturale Z , T ³i K , unde Z reprezint  num rul
zonei pe care va trebui s-o aleag  Cri astfel încât cantitatea total  T de gr unµe furate s  e
maxim , iar num rul K de camere prin va trece s  e minim.
Date de intrare
Fi³ierul cri.in conµine:
N M X Y - pe prima linie cele patru numere naturale nenule N M X Y , separate
C11 C12 ... C1M prin câte un spaµiu, cu semnicaµia din enunµ
C21 C22 ... C2M - pe ecare din urm toarele N linii câte M numere naturale nenule,
........... separate prin câte un spaµiu, reprezentând cantitatea de gr unµe CIJ
CN 1 CN 2 ... CN M depozitat  în camera de coordonate I, J  pentru 1 & I & N ³i 1 & J &
M.
Date de ie³ire
Fi³ierul de ie³ire cri.out va conµine, pe o singur  linie, cele trei numere naturale Z , T ³i K
determinate de program, separate prin câte un spaµiu, în aceast  ordine.
Restricµii ³i preciz ri
a 3 & N & 500; 3 & M & 500
a 2 & X $ N; 2 & Y $ M
a M , N , X ³i Y sunt numere naturale
a Z " r1, 2, 3, 4x
a 1 & CIJ & 8000 ( 1 & I & N ³i 1 & J & M )
a CIJ sunt numere naturale (1 & I & N ³i 1 & J & M )
a Dac  exist  zone pentru care se obµine aceea³i cantitate total  maxim  T de gr unµe ³i se
trece prin acela³i num r minim K de camere, se va alege zona numerotat  cu num rul cel mai
mic.
a Se acord :
- 20% din punctaj pentru determinarea corect  a num rului Z
- 40% din punctaj pentru determinarea corect  a num rului T
- 40% din punctaj pentru determinarea corect  a num rului K

Exemple
CAPITOLUL 10. OJI 2011 196

cri.in cri.out Explicaµii


5 423 2 45 3 Camera de pornire are coordonatele (2,3), iar N=5 ³i M=4.
1 2 3 33 Zona 1 conµine camerele de coordonate: (1,1), (1,2), (1,3), (2,1), (2,2),
5 439 (2,3). Cantitatea maxim  de gr unµe pe care o poate fura Cri este 18
2 13 4 15 trecând prin 6 camere.
1 233 Zona 2 conµine camerele de coordonate: (1,3), (1,4), (2,3), (2,4). Canti-
1 526 tatea maxim  de gr unµe pe care o poate fura Cri este 45 trecând prin 3
camere.
Zona 3 conµine camerele de coordonate: (2,1), (2,2), (2,3), (3,1), (3,2),
(3,3), (4,1), (4,2), (4,3), (5,1), (5,2), (5,3). Cantitatea maxim  de gr unµe
pe care o poate fura Cri este 45 trecând prin 12 camere.
Zona 4 conµine camerele de coordonate: (2,3), (2,4), (3,3), (3,4), (4,3),
(4,4), (5,3), (5,4). Cantitatea maxim  de gr unµe pe care o poate fura Cri
este 43 trecând prin 7 camere.
Astfel, Cri va intra în zona Z=2, va fura cantitatea maxim  de gr unµe
T=45 trecând prin num rul K=3 minim de camere.

Timp maxim de executare/test: 0.7 secunde


Memorie: total 2 MB din care pentru stiv  1.5 MB
Dimensiune maxim  a sursei: 10 KB

10.2.1 Indicaµii de rezolvare


prof. Carmen Minc  - Colegiul Naµional de Informatic  Tudor Vianu, Bucure³ti

O soluµie a problemei se poate obµine astfel:


Camera de plecare pentru toate zonele este cea de coordonate X, Y .
Ie³irea din zona cu num rul 1 se face prin camera de coordonate 1, 1.
Pentru zona cu num rul 1, asociem camerelor din aceast  zon  o matrice A cu X linii ³i Y
coloane în care AIJ CX I 1,Y J 1 pentru 1 & I & X ³i 1 & J & Y . Astfel A11 CXY ³i
AXY C11 . Fie S suma tuturor cantit µilor de gr unµe depozitate în aceast  zon .

Cazul I
Dac  X ³i Y sunt ambele numere pare, atunci num rul de camere prin care va trece Cri este
egal cu X ˜ Y  1. Dac  ar trece prin toate camerele, el nu ar mai putea s  ias  din zon  f r  s 
treac  de dou  ori printr-o camer , lucru nepermis.
Fie Ai0j0 cea mai mic  valoare din matricea A cu proprietatea c  suma indicilor i0  j0 este
un num r impar. Atunci, Cri nu va trece prin camera de coordonate X  i0  1, Y  j0  1 iar
cantitatea total  maxim  de gr unµe pe care Cri poate s-o fure în acest caz este egal  cu S  Ai0j0 .

Cazul al II-lea
Dac  X este impar sau Y este impar, sau ambele sunt impare, atunci num rul de camere prin
care va trece Cri este egal cu X ˜ Y . Astfel, cantitatea total  maxim  de gr unµe pe care Cri
poate s-o fure în acest caz este egal  cu S .
Se procedeaz  analog pentru celelalte trei zone, ³i apoi se alege zona pentru care se obµine
cantitatea total  maxim  de gr unµe prin traversarea unui num r minim de camere.
Implementarea soluµiei se poate realiza prin utilizarea unui tablou bidimensional ce memoreaz 
cantit µile de gr unµe (sursele: cripas1.pas, cricpp1.cpp) sau se poate realiza f r  tablouri (sursele:
cripas2.pas, cricpp2.cpp, cricpp3.cpp).
Complexitatea soluµiei propuse este O N ˜ M 
Pentru exemplul dat în enunµul problemei, traseul pe care l-ar putea str bate Cri în ecare
zon  este:
CAPITOLUL 10. OJI 2011 197

Figura 10.4: cri

Zona aleas  de Cri este zona cu num rul 2.

10.2.2 Cod surs 

Listing 10.2.1: cri_fstream-100.cpp


1 //prof.Carmen Minca - solutie problema cri - implementare fara tablouri
2
3 #include <fstream>
4 #include <iostream>
5
6 using namespace std;
7
8 ifstream f("cri.in");
9 ofstream g("cri.out");
10
11 int main()
12 {
13 int a,i0,j0,n,m,i,j,s1=0,s2=0,s3=0,s4=0,ies=1,k1,k2,k3,k4;
14 int min1,min2,min3,min4;
15
16 min1=min2=min3=min4=2000000000;
17
18 f>>n>>m>>i0>>j0;
19
20 for(i=1;i<=n;i++)
21 for(j=1;j<=m;j++)
22 {
23 f>>a;
24 if((i<=i0)&&(j<=j0))
25 {
26 s1+=a;
27 if((i0-i+j0-j)%2!=0)
28 if(min1>a)
29 min1=a;
30 }
31
32 if((i<=i0)&&(j>=j0))
33 {
34 s2+=a;
35 if((i0-i+j-j0)%2!=0)
36 if(min2>a)
37 min2=a;
38 }
39
40 if((i>=i0)&&(j<=j0))
41 {
42 s3+=a;
43 if((i-i0+j0-j)%2!=0)
44 if(min3>a)
45 min3=a;
46 }
47
48 if((i>=i0)&&(j>=j0))
49 {
CAPITOLUL 10. OJI 2011 198

50 s4+=a;
51 if((i-i0+j-j0)%2!=0)
52 if(min4>a)
53 min4=a;
54 }
55 }
56
57 k1=i0*j0;
58 k2=i0*(m-j0+1);
59 k3=(n-i0+1)*j0;
60 k4=(n-i0+1)*(m-j0+1);
61
62 if((i0%2==0)&&(j0%2==0))
63 {
64 k1--;
65 s1=s1-min1;
66 }
67
68 if((i0%2==0)&&((m-j0+1)%2==0))
69 {
70 k2--;
71 s2=s2-min2;
72 }
73
74 if(((n-i0+1)%2==0)&&(j0%2==0))
75 {
76 k3--;
77 s3=s3-min3;
78 }
79
80 if(((n-i0+1)%2==0)&&((m-j0+1)%2==0))
81 {
82 k4--;
83 s4=s4-min4;
84 }
85
86 if(s2>s1)
87 {
88 ies=2;
89 k1=k2;s1=s2;
90 }
91 else
92 if((s2==s1)&&(k1>k2))
93 {
94 ies=2;
95 k1=k2;
96 }
97
98 if(s3>s1)
99 {
100 ies=3;
101 k1=k3;
102 s1=s3;
103 }
104 else
105 if((s3==s1)&&(k1>k3))
106 {
107 ies=3;
108 k1=k3;
109 }
110
111 if(s4>s1)
112 {
113 ies=4;
114 k1=k4;
115 s1=s4;
116 }
117 else
118 if((s4==s1)&&(k1>k4))
119 {
120 ies=4;
121 k1=k4;
122 }
123
124 g<<ies<<’ ’<<s1<<’ ’<<k1;
125
CAPITOLUL 10. OJI 2011 199

126 g.close();
127 return 0;
128 }

Listing 10.2.2: cricpp1-100.cpp


1 //prof.Carmen Minca-solutie problema cri - implementare cu tablou bidimensional
2
3 #include <stdio.h>
4
5 int a[501][501],n,m,nb,mb;
6 int x,y;
7
8 void citire()
9 {
10 freopen("cri.in","r",stdin);
11 scanf("%d%d%d%d",&n,&m,&x,&y);
12
13 int i,j;
14 for(i=1;i<=n;i++)
15 for(j=1;j<=m;j++)
16 scanf("%d",&a[i][j]);
17 }
18
19 int zona1( int &nr)
20 {
21 int i,j,s=0,min=2000000000;
22
23 nb=0;
24 for(i=x; i>=1;i--)
25 {
26 nb++;
27 mb=0;
28 for(j=y;j>=1;j--)
29 {
30 mb++;
31 s=s+a[i][j];
32 if(((nb+mb)%2!=0)&&(a[i][j]<min))
33 min=a[i][j];
34 }
35 }
36
37 nr=nb*mb;
38 if(mb%2==0 && nb%2==0)
39 {
40 nr=nr-1;
41 s=s-min;
42 }
43
44 return s;
45 }
46
47 int zona2( int &nr)
48 {
49 int i,j,s=0,min=2000000000;
50
51 nb=0;
52 for(i=x; i>=1;i--)
53 {
54 nb++;
55 mb=0;
56 for(j=y;j<=m;j++)
57 {
58 mb++;
59 s=s+a[i][j];
60 if(((nb+mb)%2!=0)&&(a[i][j]<min))
61 min=a[i][j];
62 }
63 }
64
65 nr=nb*mb;
66 if(mb%2==0 && nb%2==0)
67 {
68 nr=nr-1;
69 s=s-min;
CAPITOLUL 10. OJI 2011 200

70 }
71
72 return s;
73 }
74
75 int zona3( int &nr)
76 {
77 int i,j,s=0,min=2000000000;
78
79 nb=0;
80 for(i=x; i<=n;i++)
81 {
82 nb++;
83 mb=0;
84 for(j=y;j>=1;j--)
85 {
86 mb++;
87 s=s+a[i][j];
88 if(((nb+mb)%2!=0)&&(a[i][j]<min))
89 min=a[i][j];
90 }
91 }
92
93 nr=nb*mb;
94 if(mb%2==0 && nb%2==0)
95 {
96 nr=nr-1;
97 s=s-min;
98 }
99
100 return s;
101 }
102
103 int zona4( int &nr)
104 {
105 int i,j,s=0,min=2000000000;nb=0;
106
107 for(i=x; i<=n;i++)
108 {
109 nb++;
110 mb=0;
111 for(j=y;j<=m;j++)
112 {
113 mb++;
114 s=s+a[i][j];
115 if(((nb+mb)%2!=0)&&(a[i][j]<min))
116 min=a[i][j];
117 }
118 }
119
120 nr=nb*mb;
121 if((mb%2==0) && (nb%2==0))
122 {
123 nr--;
124 s=s-min;
125 }
126
127 return s;
128 }
129
130 int main()
131 {
132 int nr,s,k=0,smax=0,ies;
133
134 citire();
135
136 smax=zona1(k);
137 ies=1;
138 s=zona2(nr);
139 if(s>smax)
140 {
141 smax=s;
142 k=nr;ies=2;
143 }
144 else
145 if(s==smax)
CAPITOLUL 10. OJI 2011 201

146 if(nr<k)
147 {
148 k=nr;
149 ies=2;
150 }
151
152 s=zona3(nr);
153 if(s>smax)
154 {
155 smax=s;
156 k=nr;ies=3;
157 }
158 else
159 if(s==smax)
160 if(nr<k)
161 {
162 k=nr;
163 ies=3;
164 }
165
166 s=zona4(nr);
167 if(s>smax)
168 {
169 smax=s;
170 k=nr;ies=4;
171 }
172 else
173 if(s==smax)
174 if(nr<k)
175 {
176 k=nr;
177 ies=4;
178 }
179
180 freopen("cri.out","w",stdout);
181 printf("%d %d %d",ies,smax,k);
182 return 0;
183 }

Listing 10.2.3: cricpp2-100.cpp


1 //prof.Carmen Minca - solutie problema cri - implementare fara tablouri
2
3 #include<stdio.h>
4
5 int main()
6 {
7 int a,i0,j0,n,m,i,j,s1=0,s2=0,s3=0,s4=0,ies=1,k1,k2,k3,k4;
8 int min1,min2,min3,min4;
9
10 min1=min2=min3=min4=2000000000;
11
12 freopen("cri.in","r",stdin);
13 freopen("cri.out","w",stdout);
14
15 scanf("%d%d%d%d",&n,&m,&i0,&j0);
16
17 for(i=1;i<=n;i++)
18 for(j=1;j<=m;j++)
19 {
20 scanf("%d",&a);
21 if((i<=i0)&&(j<=j0))
22 {
23 s1+=a;
24 if((i0-i+j0-j)%2!=0)
25 if(min1>a)
26 min1=a;
27 }
28
29 if((i<=i0)&&(j>=j0))
30 { s2+=a;
31 if((i0-i+j-j0)%2!=0)
32 if(min2>a)min2=a;
33 }
34 if((i>=i0)&&(j<=j0))
CAPITOLUL 10. OJI 2011 202

35 {
36 s3+=a;
37 if((i-i0+j0-j)%2!=0)
38 if(min3>a)
39 min3=a;
40 }
41
42 if((i>=i0)&&(j>=j0))
43 {
44 s4+=a;
45 if((i-i0+j-j0)%2!=0)
46 if(min4>a)
47 min4=a;
48 }
49 }
50
51 k1=i0*j0;
52 k2=i0*(m-j0+1);
53 k3=(n-i0+1)*j0;
54 k4=(n-i0+1)*(m-j0+1);
55
56 if((i0%2==0)&&(j0%2==0))
57 {
58 k1--;
59 s1=s1-min1;
60 }
61
62 if((i0%2==0)&&((m-j0+1)%2==0))
63 {
64 k2--;
65 s2=s2-min2;
66 }
67
68 if(((n-i0+1)%2==0)&&(j0%2==0))
69 {
70 k3--;
71 s3=s3-min3;
72 }
73
74 if(((n-i0+1)%2==0)&&((m-j0+1)%2==0))
75 {
76 k4--;
77 s4=s4-min4;
78 }
79
80 if(s2>s1)
81 {
82 ies=2;
83 k1=k2;
84 s1=s2;
85 }
86 else
87 if((s2==s1)&&(k1>k2))
88 {
89 ies=2;
90 k1=k2;
91 }
92
93 if(s3>s1)
94 {
95 ies=3;
96 k1=k3;
97 s1=s3;
98 }
99 else
100 if((s3==s1)&&(k1>k3))
101 {
102 ies=3;
103 k1=k3;
104 }
105
106 if(s4>s1)
107 {
108 ies=4;
109 k1=k4;
110 s1=s4;
CAPITOLUL 10. OJI 2011 203

111 }
112 else
113 if((s4==s1)&&(k1>k4))
114 {
115 ies=4;
116 k1=k4;
117 }
118
119 printf("%d %d %d",ies,s1,k1);
120
121 return 0;
122 }

Listing 10.2.4: cricpp3-100.cpp


1 // sursa 100p - problema cri
2 // prof.Silviu Candale
3
4 #include <fstream>
5 #include <cstdio>
6 //#define STDIO
7 //#define DEBUG
8
9 using namespace std;
10
11 int main()
12 {
13 printf("cri\n");
14
15 int n,m,x,y;
16 int s1,s2,s3,s4;
17 int m1,m2,m3,m4;
18
19 #ifndef STDIO
20 ifstream fin("cri.in");
21 fin>>n>>m>>x>>y;
22 #else
23 freopen("cri.in","r",stdin);
24 scanf("%d%d%d%d",&n,&m,&x,&y);
25 #endif
26
27 s1=s2=s3=s4=0;
28 m1=m2=m3=m4=10000;
29
30 int kk1,kk2,kk3,kk4;
31
32 kk1=kk2=kk3=kk4=0;
33
34 for(int i=1;i<=n;++i)
35 for(int j=1;j<=m;++j)
36 {
37 int q;
38 #ifndef STDIO
39 fin>>q;
40 #else
41 scanf("%d",&q);
42 #endif
43
44 if(i<=x && j<=y)
45 {
46 s1 += q;
47 kk1++;
48 if( (i!=x || j!=y) && (i!=1 || j!=1) &&
49 (i+j)%2!=(1+1)%2 && q<m1)
50 m1=q;
51 }
52
53 if(i<=x && j>=y)
54 {
55 s2 += q;
56 kk2++;
57 if( (i!=x || j!=y ) && (i!=1 || j!=m) &&
58 (i + j)%2!=(m+1)%2 && q<m2 )
59 m2 = q;
60 }
CAPITOLUL 10. OJI 2011 204

61
62 if(i>=x && j<=y)
63 {
64 s3 += q;kk3++;
65 if( (i!=x || j!=y) && (i!=n || j!=1) &&
66 (i+j)%2 != (n+1)%2 && q<m3 )
67 m3 = q;
68 }
69
70 if(i>=x && j>= y)
71 {
72 s4 += q;kk4++;
73 if((i!=x || j!=y) && (i!=n || j!=m) &&
74 (i+j)%2 != (n+m)%2 && q<m4 )
75 m4 = q;
76 }
77 }
78
79 int k1=x*y, k2=x*(m-y+1), k3=(n-x+1)*y, k4=(n-x+1)*(m-y+1);
80
81 if(x%2==0 && y%2==0)
82 s1-=m1, k1--;
83
84 if(x%2==0 && (m-y+1)%2==0)
85 s2-=m2, k2--;
86
87 if((n-x+1)%2==0 && y%2==0)
88 s3-=m3, k3--;
89
90 if((n-x+1)%2==0 && (m-y+1)%2==0)
91 s4-=m4, k4--;
92
93 #ifdef DEBUG
94 printf("%d %d %d %d\n",kk1,kk2,kk3,kk4);
95 printf(" zona suma min k\n");
96 printf("%d %d %d %d\n",1,s1,m1,k1);
97 printf("%d %d %d %d\n",2,s2,m2,k2);
98 printf("%d %d %d %d\n",3,s3,m3,k3);
99 printf("%d %d %d %d\n",4,s4,m4,k4);
100 #endif
101
102 int z=1,k=k1;
103 int s=s1;
104
105 if(s2>s)
106 z=2,s=s2,k=k2;
107 else
108 if (s2==s && k2<k)
109 z=2,s=s2,k=k2;
110
111 if(s3>s)
112 z=3,s=s3,k=k3;
113 else
114 if (s3==s && k3<k)
115 z=3,s=s3,k=k3;
116
117 if(s4>s)
118 z=4,s=s4,k=k4;
119 else
120 if (s4==s && k4<k)
121 z=4,s=s4,k=k4;
122
123 #ifndef STDIO
124 ofstream fout("cri.out");
125 fout<<z<<" "<<s<<" "<<k<<endl;
126 #else
127 freopen("cri.out","w",stdout);
128 printf("%d %d %d\n",z,s,k);
129 #endif
130
131 return 0;
132 }
CAPITOLUL 10. OJI 2011 205

10.2.3 *Rezolvare detaliat 


Capitolul 11

OJI 2010

11.1 livada
Problema 1 - livada 100 de puncte
Norocosul Gigel tocmai a primit în dar de la bunicul s u, Nelu, o imens  plantaµie de pomi
fructiferi. Fost profesor de geometrie, Nelu a plantat în mod riguros pomii fructiferi pe m rânduri
paralele, iar pe ecare rând a plantat exact câte n pomi fructiferi. Îns , din motive mai mult sau
mai puµin obiective, domnul Nelu nu a plantat pe ecare rând toµi pomii de acela³i soi, ci din
mai multe soiuri diferite. Soiurile de pomi plantaµi în livad  sunt codicate cu numere naturale
cuprinse între 1 ³i p.
Cuprins de febra rigurozit µii matematice ³i de cea a statisticii, Gigel a denit noµiunea de soi
majoritar astfel: dac  pe un rând k format din n pomi fructiferi avem cel puµin n©2  1 pomi de
acela³i soi x, atunci spunem c  soiul x este soi majoritar pe rândul k (prin y  se înµelege partea
întreag  a num rului real y ).

Cerinµe
Cunoscând numerele m, n ³i p, precum ³i soiul ec rui pom de pe ecare rând al plantaµiei,
ajutaµi-l pe Gigel s  determine:
1. pe câte rânduri din livad  exist  un soi majoritar;
2. care este cel mai mare num r de pomi de acela³i soi plantaµi în poziµii consecutive pe un
rând.

Date de intrare
Fi³ierul de intrare livada.in conµine pe prima linie trei numere naturale m, n ³i p cu semni-
caµia din enunµ, iar pe ecare dintre urm toarele m linii se g sesc câte n numere, desp rµite prin
câte un spaµiu, reprezentând soiurile pomilor de pe rândul respectiv.

Date de ie³ire
Fi³ierul de ie³ire livada.out va conµine dou  linii:
1. pe prima linie se va scrie un num r natural reprezentând num rul de rânduri din livad  pe
care exist  un soi majoritar;
2. pe a doua linie se va scrie un num r natural reprezentând cel mai mare numar de pomi de
acelasi soi plantaµi în poziµii consecutive pe un rând.

Restricµii ³i preciz ri
a 1 & m & 100
a 1 & n & 700.000
a 1 & m ˜ n & 700.000
a 1 & p & 998.560.000
a Pe ecare rând diferenµa dintre valoarea maxim  ³i cea minim  este cel mult 250.000.
a Dac  doar valoarea de pe prima linie este corect , se acord  40% din punctaj. Dac  doar
valoarea de pe a doua linie este corect , se acord  60% din punctaj. Dac  ambele valori sunt
corecte, se acord  100% din punctajul testului respectiv.

206
CAPITOLUL 11. OJI 2010 207

Exemple
livada.in livada.out Explicaµii
4 7 9 2 Plantaµia este format  din m=4 rânduri, iar pe ecare rând
2 1 23 8 2 2 3 avem câte n=7 pomi.
4 7 24 9 7 4 Pentru ca un soi sa e majoritar pe un rând trebuie ca pe acel
5 5 25 5 5 7 rând s  existe cel puµin [7/2]+1 = 4 pomi din soiul respectiv.
2 3 23 2 3 1 Exist  soiuri majoritare pe dou  rânduri: primul ³i al treilea.
Pe randul al treilea exista 3 poziµii consecutive în care se a 
pomi din acela³i soi (soiul 5).

Timp maxim de executare/test: 1.0 secunde


Memorie: total 8 MB din care pentru stiv  4 MB
Dimensiune maxim  a sursei: 20 KB

11.1.1 Indicaµii de rezolvare


... ??? ...

Evident, cele dou  cerinµe se rezolv  în paralel, pentru ecare rând.


Pentru rezolvarea primei cerinµe am folosit un contor rsm ³i apoi, pentru ecare rând, am
vericat dac  el are soi majoritar sau nu folosind urm torul algoritm:
- consider m prima valoare de pe rândul respectiv ca ind posibilul soi majoritar (variabila
sm) ³i iniµializ m un contor cnt (care num r  de câte ori se g se³te posibilul soi majoritar de pe
acel rând cu 1;
- parcurgem restul rândului ³i ori de câte ori g sim un soi egal cu sm cre³tem valoarea lui cnt
cu 1 (cre³te probabilitatea ca sm s  e soi majoritar) ³i ori de câte ori g sim un soi diferit sc dem
valoarea lui cnt cu 1 (scade probabilitatea ca sm s  e soi majoritar).
Dac  la un moment dat cnt devine 0, atunci reiniµializ m soiul majoritar cu soiul curent, iar
cnt devine 1.
- dup  ce termin m de parcurs rândul curent veric m dac  valoarea lui cnt este 0 sau nu.
în caz armativ înseamn  c  nu avem soi majoritar pe rândul respectiv. Altfel, dac  valoarea lui
cnt este diferit  de 0, înseamn  c  în sm avem cel mai bun candidat pentru soiul majoritar ³i
veric m dac  el este într-adev r soi majoritar, reparcurgând rândul respectiv. Acest lucru este
obligatoriu, pentru c  în cazul rândului 1, 3, 1, 2, 3, 3, 3 contorul cnt va  3, iar soiul majoritar este
într-adev r, soiul 3. în schimb, în cazul rândului 1, 2, 1, 2, 3, 3, 3 contorul va  tot 3, f r  ca soiul
3 s  e majoritar!
- dac  rândul curent are soi majoritar, cre³tem valoarea contorului rsm cu 1.
Pentru rezolvarea celei de-a doua cerinµe am folosit un contor max care va p stra maximul
lungimilor celor mai lungi secvenµe ce au proprietatea cerut  pe ecare rând. Algoritmul folosit
este unul clasic, în care se folosesc doi indec³i (j ³i k ) cu care se parcurge ecare rând, element
cu element. Dac  valoarea lui v k  este egal  cu valoarea lui v j , atunci se cre³te valoarea lui
k . În momentul în care se termin  o secvenµa format  din copaci de acela³i soi (v j  j v k ), se
veric  dac  lungimea ultimei secvenµe este mai mare decât valoarea maxim  obµinut  pân  în
acel moment (variabila rmax) sau nu.
Fiecare dintre cele dou  cerinµe a fost rezolvat  folosind algoritmi cu complexitatea O n, deci
complexitatea soluµiei este O m ˜ n.
Observaµi c  aceast  comlexitate nu depinde de valoarea lui p!
Exist  ³i alte posibilit µi de rezolvare ale acestei probleme, care pot obµine punctaje parµiale
sau chiar 100 de puncte:
1. ³ierul "livada_v1.pas" conµine o variant  de 100 de puncte bazat  pe vectori de contorizare,
alocaµi ecient (din preciz rile din enunµul problemei se deduce u³or c  un vector de contorizare
poate avea cel mult 250.000 de elemente)
2. ³ierul "livada_v2.cpp" conµine o soluµie bazat  pe sortarea unui rând (folosind funcµia
predenit  qsort) ³i vericare faptului ca elementul din mijlocul rândului este soi majoritar sau
nu (se observ  c  dac  soiurile sunt sortate cresc tor, atunci soi majoritar nu poate  decât cel
aat pe poziµia din mijloc);
3. se pot da soluµii de peste 50  70 de puncte folosind un algoritm asem n tor sort rii prin
num rare în care s  se numere de câte ori apare ecare soi pe un rând.
CAPITOLUL 11. OJI 2010 208

11.1.2 Cod surs 

Listing 11.1.1: livada.cpp


1 // varianta optima - O(m*n)
2
3 #include<stdio.h>
4
5 int v[700000];
6
7 int main()
8 {
9 int m,n,p;
10
11 FILE *fin=fopen("livada.in","r");
12 FILE *fout=fopen("livada.out","w");
13
14 fscanf(fin,"%d %d %d",&m,&n,&p);
15
16 int max=0;
17 int rsm=0;
18
19 for(int i=0;i<m;i++)
20 {
21 for(int j=0;j<n;j++)
22 fscanf(fin,"%d",&v[j]);
23
24 int rmax=0;
25 int j=0;
26 while(j<n-1)
27 {
28 int k=j+1;
29 while((k<n)&&(v[k]==v[j]))
30 k++;
31 if(k-j>rmax)
32 rmax=k-j;
33 j=k;
34 }
35
36 if(rmax>max) max=rmax;
37
38 int sm=v[0];
39 int cnt=1;
40
41 for(int j=1;j<n;j++)
42 if(cnt==0)
43 {
44 sm=v[j];
45 cnt=1;
46 }
47 else
48 if (v[j]==sm)
49 cnt++;
50 else
51 cnt--;
52
53 if(cnt==0)
54 sm=0;
55 else
56 {
57 cnt=0;
58 for(int j=0;j<n;j++)
59 if(v[j]==sm) cnt++;
60
61 if(cnt<n/2+1) sm=0;
62 }
63
64 if(sm!=0) rsm++;
65 }
66
67 fprintf(fout,"%d\n%d",rsm,max);
68
69 fclose(fin);
70 fclose(fout);
CAPITOLUL 11. OJI 2010 209

71
72 return 0;
73 }

Listing 11.1.2: livada_v2.cpp


1 // se sorteaza crescator taboul v (folosind functia predefinita qsort) si
2 // se verifica daca elementul din mijloc este soi majoritar sau nu
3 // - O(n*log n)
4
5 #include<stdio.h>
6 #include<stdlib.h>
7
8 int v[700000];
9
10 int compare(const void* a, const void* b)
11 {
12 int* arg1 = (int*) a;
13 int* arg2 = (int*) b;
14
15 if( *arg1 < *arg2 )
16 return -1;
17 else
18 if( *arg1 == *arg2 )
19 return 0;
20 else
21 return 1;
22 }
23
24 int main()
25 {
26 int m,n,p;
27
28 FILE *fin=fopen("livada.in","r");
29 FILE *fout=fopen("livada.out","w");
30
31 fscanf(fin,"%d %d %d",&m,&n,&p);
32
33 int max=0;
34 int rsm=0;
35
36 for(int i=0;i<m;i++)
37 {
38 for(int j=0;j<n;j++)
39 fscanf(fin,"%d",&v[j]);
40
41 int rmax=0;
42 int j=0;
43 while(j<n-1)
44 {
45 int k=j+1;
46 while((k<n)&&(v[k]==v[j]))
47 k++;
48 if(k-j>rmax)
49 rmax=k-j;
50 j=k;
51 }
52
53 if(rmax>max) max=rmax;
54
55 qsort(v, n, sizeof(int), compare);
56
57 int sm=v[(n-1)/2];
58 int cnt=0;
59
60 for(int j=0;j<n;j++)
61 if(v[j]==sm)
62 {
63 cnt++;
64 if(cnt>=n/2+1) break;
65 }
66
67 if(cnt<n/2+1) sm=0;
68
69 if(sm!=0) rsm++;
CAPITOLUL 11. OJI 2010 210

70 }
71
72 fprintf(fout,"%d\n%d",rsm,max);
73
74 fclose(fin);
75 fclose(fout);
76
77 return 0;
78 }

11.1.3 *Rezolvare detaliat 

11.2 numar
Problema 2 - numar 100 de
puncte
Se d  un num r raµional strict pozitiv q , sub form  de fracµie zecimal .

Cerinµe
S  se determine dou  numere naturale a ³i b astfel încât q a
b
iar modulul diferenµei dintre a
³i b s  e minim.

Date de intrare
Fi³ierul numar.in conµine
- pe prima linie dou  valori naturale ni ³i nz . ni reprezint  num rul de cifre care formeaz 
partea întreag  a lui q iar nz reprezint  num rul de cifre care formeaz  partea fracµionara a lui q .
- pe a doua linie, ni cifre care reprezint  partea întreag  a lui q . Între dou  cifre se a  câte
un caracter spaµiu.
- pe a treia linie, nz cifre care reprezint  partea zecimal  a lui q . Între dou  cifre se a  câte
un caracter spaµiu.

Date de ie³ire
³ierul numar.out va conµine:
- pe prima linie un num r natural n1 care reprezint  num rul de cifre din care este alc tuit
num rul a;
- pe a doua linie, cifrele num rului a, f r  spaµiu între ele.
- pe a treia linie un num r natural n2 care reprezint  num rul de cifre din care este alc tuit
num rul b;
- pe a patra linie, cifrele numarului b, f r  spaµiu între ele.

Restricµii ³i preciz ri
- 1 & n1, n2 $ 2000
- 1 & n1  n2 & 2000
- Cifrele din care este alc tuit q sunt cele din sistemul zecimal.
- Pentru 20% dintre teste, n1  n2 & 9; pentru alte 15% dintre teste, 10 & n1  n2 & 16

Exemple
numar.in numar.out Explicaµii
13 1 q 0.375 83
0 3 Exist  ³i alte perechi de valori naturale x, y astfel încât
375 1 x
y
0.375, dar pentru oricare alt  pereche, ¶x  y ¶ % ¶3  8¶
8 (¶...¶ reprezint  modulul)
37 7
012 1234567 q 12.34567 1234567
100000
3456700 6
100000
CAPITOLUL 11. OJI 2010 211

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 20 KB

11.2.1 Indicaµii de rezolvare


... ??? ...

Se ia iniµial
- a = num rul format din cifrele lui q din care se scoate punctual zecimal
- b = 1 urmat de atâtea cifre de 0 câte cifre are partea zecimal  (fracµionar ).
Evident, a ³i b trebuie implementate ca numere mari - ecare cifr  ca element al unui vector.
Apoi simplic m fracµia format  din a ³i b.
Matematic, simplicarea ar trebui facut  cu cmmmdc-ul dintre a ³i b, dar nu are rost s 
calcul m acest num r deoarece a ³i b sunt numere mari ³i acest calcul s-ar face prin sc deri
repetate sau implementarea imparµirii pe numere mari, ceea ce e complicat ³i nu garanteaz 
încadrarea în timp.
De fapt, cele dou  numere a ³i b nu se pot simplica decât cu o putere a lui 2 sau a lui 5,
având în vedere c  b este o putere a lui 10 - a³a c  tot ce trebuie s  facem este s  implement m
împarµirea unui num r mare la un num r de o singur  cifr .
Pentru punctaje parµiale, putem lucra cu variabile numerice:
- dac  folosim tipuri pe 16 biµi (integer în Pascal) obµinem 10 de puncte
- dac  folosim tipuri pe 36 biµi (longint în Pascal respective long sau int în C/C++) obµinem
20 de puncte
- dac  folosim tipuri pe 64 biµi (int64 în Pascal respective long long în C/C++) obµinem 35
de puncte
Mai putem prinde 2 teste în care partea întreag  ³i/sau partea zecimal  au mai mult de 16
cifre, îns  ele conµin multe 0-uri redundante, pe care trebuie s  nu le includem în numerele pe care
le construim.
Putem lucra ³i cu tipuri reale (dar care s  ne permit  precizii cat mai bune: extended în Pascal
sau long double în C) conform unuia dintre urm torii algoritmi, ceea ce ne permite obµinerea unor
punctaje parµiale (de exemplu al doilea dintre algoritmii descri³i mai jos prind 6 teste)
citim valoarea q într-o variabil  real 

a q ˜ 10
nz


b 10
nz


c cmmdc a, b

a a©c, b b©c 
sau
citim valoarea q într-o variabil  real 

pentru i 1, valoare arbitrar  foarte mare
dac  q ˜ i este valoare natural  atunci

a q˜i

b i
break

11.2.2 Cod surs 

Listing 11.2.1: numar_v1.cpp


1 //implementare de 100 de puncte - ciprian chesca
2 #include <fstream>
3
4 using namespace std;
5
6 typedef unsigned long vector[2000];
7
8 ifstream f("numar.in");
9 ofstream g("numar.out");
10
11 unsigned long prim[]={2,5};
CAPITOLUL 11. OJI 2010 212

12
13 void initializare(vector v)
14 {
15 v[0] = 0;
16 }
17
18 void atribuire(vector v, unsigned long x)
19 {
20 v[0] = 0;
21 while (x)
22 {
23 ++v[0];
24 v[v[0]] = x % 10;
25 x /= 10;
26 }
27 }
28
29 void inmultire(vector v, unsigned long x) /* v <- v*x */
30 {
31 unsigned long i,t=0;
32
33 for (i=1;i<=v[0];i++)
34 {
35 v[i]=v[i]*x+t;
36 t=v[i]/10;
37 v[i]=v[i]%10;
38 }
39
40 while (t) /* Cat timp exista transport */
41 {
42 v[++v[0]]=t%10;
43 t/=10;
44 }
45 }
46
47 void adunare(vector a, vector b) /* a <- a+b */
48 {
49 unsigned long i,t=0;
50
51 if (b[0]>a[0])
52 {
53 for (i=a[0]+1;i<=b[0];)
54 a[i++]=0;
55
56 a[0]=b[0];
57 }
58 else
59 for (i=b[0]+1;i<=a[0];)
60 b[i++]=0;
61
62 for (i=1;i<=a[0];i++)
63 {
64 a[i]+=b[i]+t;
65 t=a[i]/10;
66 a[i]%=10;
67 }
68
69 if (t)
70 a[++a[0]]=t;
71 }
72
73
74 void scriere(vector v)
75 {
76 unsigned long i;
77
78 for(i=v[0];i>=1;i--)
79 g<<v[i];
80
81 g<<endl;
82 }
83
84 void shl(vector a, int p) /* H <- H*10ACount */
85 {
86 long i;
87
CAPITOLUL 11. OJI 2010 213

88 /* Shifteaza vectorul cu Count pozitii */


89 for (i=a[0];i;i--)
90 a[i+p]=a[i];
91
92 /* Umple primele Count pozitii cu 0 */
93 for (i=1;i<=p;)
94 a[i++]=0;
95
96 /* Incrementeaza numarul de cifre */
97 a[0]+=p;
98 }
99
100 void shr(vector a, int p) /* H <- H/10ACount */
101 {
102 unsigned long i;
103
104 /* Shifteaza vectorul cu Count pozitii */
105 for (i=p+1;i<=a[0];i++)
106 a[i-p]=a[i];
107
108 /* Decrementeaza numarul de cifre */
109 a[0]-=p;
110 }
111
112 unsigned long divide(vector a, unsigned long x) /* A <- A/X si intoarce A%X */
113 {
114 unsigned long i;
115 unsigned long R=0;
116
117 for (i=a[0];i;i--)
118 {
119 a[i]=(R=10*R+a[i])/x;
120 R%=x;
121 }
122
123 while (!a[a[0]] && a[0]>1)
124 a[0]--;
125
126 return R;
127 }
128
129 unsigned long mod(vector a, unsigned long x) /* Intoarce A%X */
130 {
131 unsigned long i;
132 unsigned long R=0;
133
134 for (i=a[0];i;i--)
135 R=(10*R+a[i])%x;
136
137 return R;
138 }
139
140 int main()
141 {
142 unsigned long n,m,i;
143
144 vector v; // numarator
145 vector s; // numitor
146
147 initializare(v);
148 initializare(s);
149
150 f>>n>>m;
151
152 // citesc partea intreaga
153 for(i=1;i<=n;i++)
154 f>>v[n+m-i+1];
155
156 // citesc partea fractionara
157 for(i=1;i<=m;i++)
158 f>>v[m-i+1];
159
160 v[0]=n+m;
161 if (v[n+m]==0) v[0]--;
162
163 atribuire(s,1);
CAPITOLUL 11. OJI 2010 214

164 shl(s,m);
165 if (m==0)
166 {
167 g<<n<<"\n";
168 scriere(v);
169
170 g<<1<<"\n";
171 scriere(s);
172
173 return 0;
174 }
175
176 for(i=0;i<=1;i++)
177 while (mod(v,prim[i])==0&&mod(s,prim[i])==0)
178 {
179 divide(v,prim[i]);
180 divide(s,prim[i]);
181 }
182
183 g<<v[0]<<"\n";
184 scriere(v);
185 g<<s[0]<<"\n";
186 scriere(s);
187
188 f.close();
189 g.close();
190
191 return 0;
192 }

11.2.3 *Rezolvare detaliat 


Capitolul 12

OJI 2009

12.1 expresie
Problema 1 - expresie 100 de puncte
Costel are de rezolvat o tem  grea la matematic : având la dispoziµie N numere naturale nenule
trebuie s  a³eze între acestea 2 operaµii de înmulµire ³i N  3 operaµii de adunare, astfel încât
rezultatul calculelor s  e cel mai mare posibil. Nu este permis  modicarea ordinii numerelor
date.
De exemplu, dac  N 5 ³i numerele sunt 4, 7, 1, 5, 3, operaµiile pot  a³ezate 4  7 ˜ 1  5 ˜ 3,
4 ˜ 7 ˜ 1  5  3 e.t.c

Cerinµe
Scrieµi un program care s  a³eze dou  operaµii de înmulµire ³i N  3 operaµii de adunare între
cele N valori date astfel încât valoarea expresiei obµinute s  e maxim .

Date de intrare
Fi³ierul de intrare expresie.in are urm toarea structur :
Pe prima linie se a  un num r natural N , reprezentând num rul elementelor date.
Pe urm toarele linii se a  cele N numere naturale date, ecare pe câte o linie.

Date de ie³ire
Fi³ierul de ie³ire expresie.out va conµine, pe prima linie, valoarea maxim  obµinut  prin
evaluarea expresiei.

Restricµii ³i preciz ri
4 & N & 1000
Numerele date sunt numere naturale între 1 ³i 10000

Exemple
expresie.in expresie.out Explicaµii
5 44 Valoarea maxim  se obµine prin a³ezarea operaµiilor sub forma:
4 4˜715˜3
7
1
5
3

Timp maxim de executare/test: 1.0 secunde

215
CAPITOLUL 12. OJI 2009 216

12.1.1 Indicaµii de rezolvare


.......... ?????????? ..........

Memor m cele N numere naturale date în tabloul unidimensional T . Calcul m suma celor N
valori, deoarece presupunem c , iniµial, avem în expresia noastr  doar operaµii de adunare.
Exist  dou  cazuri posibile pentru a introduce dou  operaµii de înmulµire în expresie:
Cazul 1. Cele dou  înmulµiri sunt consecutive. Obµinem valoarea maxim  a expresiei în
variabila suma1, luând pe rând toate tripletele de numere consecutive ti, ti  1, ti  2 ³i
alegând tripletul pentru care suma ti  ti  1  ti  2  ti ˜ ti  1 ˜ ti  2 este maxim .
Cazul 2. Cele dou  operaµii de înmulµire nu sunt a³ezate consecutiv. Obµinem valoarea maxim 
a expresiei în variabila suma1, luând pe rand toate perechile (ti, ti  1) ³i (tj , tj  1) ³i
alegând combinaµia pentru care suma ti  ti  1  tj   tj  1  ti ˜ ti  1  tj  ˜ tj  1
este maxim .
Deoarece numerele date sunt între 1 ³i 10000, valoarea expresiei poate dep ³i tipul de date
longint (long int).
Pentru exemplul din enunt, avem:
1 2 3 4 5
T: 4 7 1 5 3
Suma iniµial  este suma=20.
Cazul 1. Avem expresiile posibile:
4*7*1+5+3 = 36
4+7*1*5+3 = 42
4+7+1*5*3 = 26
deci valoarea maxim  de pân  acum este 42
Cazul 2 Avem expresiile posibile:
4*7+1*5+3 = 28+5+3 = 36
4*7+1+5*3 = 28+1+15= 44
4+7*1+5*3 = 4+7+15=26
deci valoarea maxim  este 44

12.1.2 *Cod surs 

12.1.3 *Rezolvare detaliat 

12.2 placare
Problema 2 - placare 100 de puncte
O suprafaµ  dreptunghiular  de în lµime N ³i l µime M unit µi trebuie acoperit  perfect (pla-
cat ) prin utilizarea unor pl ci de form  dreptunghiular  de dimensiune 1  P sau P  1, unde P
este un num r natural nenul. Suprafaµa dat  poate  privit  ca un caroiaj cu N  M p tr µele
egale cu unitatea.
O placare corect  a suprafeµei iniµiale se memoreaz  într-un ³ier text folosind urm toarele
convenµii de codicare:
a pe prima linie se precizeaz  dimensiunile N ³i M ale suprafeµei;
a o plac  dreptunghiular  de laµime P este codicat  prin num rul natural P , iar o plac  de
înalµime P se codic  prin num rul întreg P ;
a convenim ca placa având ambele dimensiuni egale cu unitatea s  se codice cu valoarea 1;
a pe ecare din cele N linii ale codic rii se a  câte un ³ir de valori întregi reprezentând, în
ordine de la stânga la dreapta, codurile pl cilor care se g sesc amplasate începând de la respectiva
linie;
a codul P strict mai mare ca 1 al unei placi orizontale apare o singur  dat  pe linia corespun-
z toare pe care se a  placa, iar codul P al unei placi verticale va apare o singur  dat  ³i anume
pe prima linie de la care placa respectiv  este amplasat  în jos pe o anumita coloan  a suprafeµei;
a Dac  pe o anumit  linie a suprafeµei nu exist  astfel de coduri de pl ci, atunci pe respectiva
linie din ³ier este o singur  valoare de 0.
CAPITOLUL 12. OJI 2009 217

Folosind codicarea unei plac ri a suprafeµei iniµiale, se poate determina imaginea acestei
plac ri sub forma unui tablou bidimensional A, cu N linii ³i M coloane, unde Aij = valoarea
absolut  a codului pl cii care se suprapune peste p tr µelul de pe linia i ³i coloana j.

Cerinµe
Cunoscând codicarea unei plac ri corecte a suprafeµei date s  se obµin  imaginea acestei
plac ri (matricea de valori corespunz toare codic rii suprafeµei).

Date de intrare
Fi³ierul de intrare placare.in are urm toarea structur :
- pe prima linie valorile naturale N M , separate printr-un spaµiu, unde N este în lµimea
suprafeµei, M este l µimea suprafeµei.
- pe ecare din urm toarele N linii se a  un ³ir de valori întregi, separate prin câte un spaµiu,
reprezentând codicarea respectivei linii a plac rii.

Date de ie³ire
În ³ierul de ie³ire placare.out se va tip ri tabloul bidimensional ce reprezint  imaginea
plac rii, compus din N linii, pe ecare dintre ele aându-se M valori naturale separate prin câte
un spaµiu, cu semnicaµia din enunµ.

Restricµii ³i preciz ri
- 1 & N, M & 100 pentru 80% din teste;
- 100 $ N, M & 300 pentru 20% din teste
- dimensiunea P sau P a unei pl ci este aleas  astfel încât acoperirea obµinut  s  nu dep -
³easc  în lµimea N sau l µimea M a suprafeµei.
- datele din ³ierul de intrare sunt corecte în sensul c  reprezint  codicarea unei acoperiri a
zonei dreptunghiulare de dimensiuni N ³i M .

Exemple
placare.in placare.out Explicaµii
44 4 1 1 1 Valoarea 4 codic  o plac  de în lµime 4 ³i laµime 1 plasat 
-4 1 1 1 4 1 2 2 începând din p tratul de coordonate 1, 1 ³i pîn  în p tratul
12 4 2 2 1 de coordonate 4, 1
21 4 3 3 3 Valoarea 3 de pe ultima linie a codic rii desemneaz  o plac  de
3 l µime 3 ³i în lµime 1, plasat  orizontal, începând din p tr µelul
de coordonate 4, 2.
32 32
-3 -2 32
0 31
1

Timp maxim de executare/test: 1.0 secunde

12.2.1 Indicaµii de rezolvare


.......... ?????????? ..........

Se citesc datele de intrare folosind un vector A, care va reµine, succesiv, câte o linie a ³ierului
de intrare.
Construim soluµia printr-o parcurgere a datelor de intrare.
Folosim ca memorie auxiliara o matrice REST U RI cu dou  linii ³i 300 coloane.
Pentru ecare poziµie J , REST U RI 1, J  va memora valoarea asociat  unei pl ci întâlnite
pe una din liniile anterioare, iar REST U RI 2, J  va memora num rul r mas de linii pe care se
a³eaz  placa respectiv .
Pe o linie oarecare I , a codic rii, sunt posibile trei situaµii:
- dac  exist  plac  vertical  pe coloana curent , ce ocup  spaµiu ³i pe linia I , se va memora
valoarea corespunz toare acelei pl ci sc zându-se cu o unitate valoarea de pe linia 2 a matricei
CAPITOLUL 12. OJI 2009 218

auxiliare REST U RI .
- dac  nu, se cite³te urm toarea valoare din vectorul A de pe coloana I ³i se a³eaz  placa
gasit  pe linia I , orizontal dac  valoarea este pozitiv  sau vertical dac  valoarea este negativ ; în
acela³i timp se actualizeaz  matricea REST U RI , în cazul în care placa este asezat  pe vertical ;
- în al treilea caz, singura valoare de pe linia curent  este 0, aceasta însemnând c  pe aceast 
linie a plac rii exist  doar placi verticale plasate pe liniile anterioare; pentru aceasta e sucient s 
plas m valorile memorate în matricea REST U RI , actualizând REST U RI 2, J .
Procedeul se repet  pentru ecare linie a codic rii.
Utilizarea unor tablouri bidimensionale pentru construirea imaginii plac rii determin  obµine-
rea a 80 de puncte, datorit  dimensiunilor prea mari ale acestora.
Observaµie: Algorimul nu necesit  tehnici de programare ³i este insprirat din deniµia tabelelor
HT M L, în care o celul  se poate extinde pe mai multe linii sau coloane. Programul simuleaz 
modul in care tabelul este interpretat de browser ³i asat pe ecran ca pagin  web.

12.2.2 *Cod surs 

12.2.3 *Rezolvare detaliat 


Capitolul 13

OJI 2008

13.1 Concurs
Problema 1 - concurs 100 de puncte
La Olimpiada Naµional  de Informatic  particip  elevi din mai multe judeµe, ecare judeµ
ind identicat în mod unic printr-un num r natural. Elevii din ecare judeµ au asociat câte
un num r natural care permite identicarea în mod unic a elevului în cadrul judeµului. Astfel,
orice participant la olimpiada poate  identicat prin dou  numere: identicatorul judeµului ³i
identicatorul elevului în cadrul judeµului.
Pentru a repartiza elevii la calculatoare, organizatorii au nevoie de o list  care s  respecte
urm toarele condiµii:
- lista conµine toµi elevii participanµi la olimpiad ;
- oricare doi elevi consecutivi în list  sunt din judeµe diferite;
- elevii din orice judeµ apar în list  în ordinea cresc toare a numerelor de identicare.

Cerinµe
Scrieµi un program care s  genereze lista necesar  organizatorilor.

Date de intrare
Fi³ierul de intrare concurs.in conµine pe prima linie un num r natural P reprezentând num -
rul total de participanµi la ONI. Pe urm toarele P linii este descris  lista participanµilor, câte un
participant pe o linie. Pentru ecare participant sunt scrise dou  numere naturale separate prin
spaµiu J E , unde J reprezint  identicatorul judeµului, iar E reprezint  identicatorul elevului în
cadrul judeµului.

Date de ie³ire
Fi³ierul de ie³ire concurs.out va conµine pe prima linie un num r natural N J , reprezentând
num rul de judeµe din care exist  participanµi la olimpiad . Pe cea de a doua linie sunt scrise
N J numere naturale nenule separate prin câte un spaµiu reprezentând (în ordinea cresc toare a
numerelor de identicare a judeµelor) num rul de participanµi din ecare judeµ. Pe urm toarele P
linii este descris  lista necesar  organizatorilor, câte un elev pe o linie. Pentru ecare elev este scris
mai întâi identicatorul judeµului din care face parte, urmat de un spaµiu, apoi de identicatorul
elevului în cadrul judeµului.

Restricµii ³i preciz ri
Identicatorii judeµelor sunt numere naturale cuprinse între 1 ³i 50.
Identicatorii elevilor în cadrul judeµelor sunt numere naturale cuprinse între 1 ³i 1000.
Num rul total de elevi participanµi la olimpiad  nu dep ³e³te 500.
Pentru datele de test exist  întotdeauna soluµie, nu neap rat unic .
Pentru determinarea corect  a num rului de judeµe se acord  20% din punctaj. Pentru deter-
minarea corect  a num rului de judeµe, precum ³i a num rului de participanµi din ecare judeµ
se acord  30% din punctaj. Punctajul se acord  integral pentru rezolvarea tuturor celor 3 cerinµe
(num r de judeµe, num r de participanµi din ecare judeµ ³i lista necesar  organizatorilor).

219
CAPITOLUL 13. OJI 2008 220

Exemple
concurs.in concurs.out
7 3
1 3 4 12
2 4 1 2
1 2 5 2
5 2 1 3
5 3 5 3
1 6 1 6
1 9 2 4
1 9

Timp maxim de executare/test: 1.0 secunde

13.1.1 Indicaµii de rezolvare


prof. Radu Vi³inescu, C.N. "I. L. Caragiale" Ploie³ti

Vom utiliza o matrice A cu maxim 50 de linii. Pe linia i vor  memorate numerele de identicare
ale elevilor din judeµul i.
Mai exact:
Ai0 reprezint  num rul de elevi din judeµul i;
Ai1, Ai2, ..., AiAi0 sunt elevii din judeµul i.
Pentru a num ra judeµele din care exist  participanµi este sucient s  parcurgem coloana 0 a
matricei ³i s  num r m liniile i pentru care Ai0 j 0.
Pentru a rezolva cerinµa 2, vom parcurge din nou coloana 0 (începând de la 1) ³i vom a³a
valorile nenule Ai0.
Pentru a construi lista necesar  organizatorilor:
1. vom sorta cresc tor elevii din ecare judeµ;
2. vom repartiza elevii astfel:
- la ecare pas determin m dou  judeµe cu num r maxim de participanµi la ONI
- plasez în list  câte un elev din ecare dintre cele dou  judeµe (astfel judeµele vor alterna).
Pentru a p stra ordinea cresc toare a elevilor în cadrul judeµelor vom reµine pentru ecare
judeµ poziµia elevului curent (cel care urmeaz  a  plasat în list ).
Când plas m în list  un elev dintr-un judeµ avem grij  s  decrement m num rul de elevi
disponibili din judeµul respectiv (pentru a putea calcula la ecare pas care sunt judeµele cu num r
maxim de elevi disponibili).
Când num rul total de elevi este impar trebuie s  m atenµi la ultimul pas, indc  nu trebuie
s  mai select m doi elevi, ci unul singur.

13.1.2 Cod surs 

Listing 13.1.1: concurs.cpp


1 //Emanuela Cerchez, 100 puncte
2 #include <stdio.h>
3 #define JMax 50
4 #define NMax 501
5
6 int P, nrJ;
7 int a[JMax+1][NMax];
8 int lista[NMax][2];
9
10 void read();
11 void sortare(int);
12
13 int main()
14 {
15 int i, j, max1, pozmax1, k, max2, pozmax2;
16 int inc[JMax+1];
17 FILE * fout;
18
19 read();
20 for (i=1; i<=JMax; i++)
CAPITOLUL 13. OJI 2008 221

21 if (a[i][0])
22 nrJ++;
23
24 fout=fopen("concurs.out","w");
25 fprintf(fout,"%d\n",nrJ);
26
27 for (i=1; i<=JMax; i++)
28 if (a[i][0])
29 fprintf(fout,"%d ",a[i][0]);
30 fprintf(fout,"\n");
31
32 for (i=1; i<=JMax; i++)
33 if (a[i][0])
34 sortare(i);
35
36 for (j=1; j<=JMax; j++) inc[j]=1;
37
38 for (k=1; k<=P; i++)
39 {
40 //determina max1 si max2
41 max1=max2=0;
42 pozmax1=pozmax2=0;
43
44 for (j=1; j<=JMax; j++)
45 if (a[j][0]>max1)
46 {
47 max2=max1;
48 pozmax2=pozmax1;
49 max1=a[j][0];
50 pozmax1=j;
51 }
52 else
53 if (a[j][0]>max2)
54 {
55 max2=a[j][0];
56 pozmax2=j;
57 }
58
59 //distribuie primii 2 elevi in lista
60 lista[k][0]=pozmax1;
61 lista[k][1]=a[pozmax1][inc[pozmax1]];
62 if (max2)
63 {
64 lista[k+1][0]=pozmax2;
65 lista[k+1][1]=a[pozmax2][inc[pozmax2]];
66 }
67 k+=2;
68
69 //elimina elevii repartizati
70 a[pozmax1][0]--;
71 a[pozmax2][0]--;
72 inc[pozmax1]++;
73 inc[pozmax2]++;
74 }
75
76 for (i=1; i<=P; i++)
77 fprintf(fout,"%d %d\n", lista[i][0], lista[i][1]);
78
79 fclose(fout);
80 return 0;
81 }
82
83 void read()
84 {
85 int i, el, jud;
86 FILE * fin = fopen("concurs.in", "rt");
87
88 fscanf(fin, "%d", &P);
89 for (i=1; i<=P; i++)
90 {
91 fscanf (fin,"%d %d",&jud, &el);
92 a[jud][0]++;
93 a[jud][a[jud][0]]=el;
94 }
95
96 fclose(fin);
CAPITOLUL 13. OJI 2008 222

97 }
98
99 void sortare (int i)
100 {
101 int ok, j, aux;
102
103 do
104 {
105 ok=1;
106 for (j=1; j<a[i][0]; j++)
107 if (a[i][j]>a[i][j+1])
108 {
109 aux=a[i][j];
110 a[i][j]=a[i][j+1];
111 a[i][j+1]=aux;
112 ok=0;
113 }
114 } while (!ok);
115 }

13.1.3 *Rezolvare detaliat 

13.2 Pluricex
Problema 2 - Pluricex 100 de
puncte
Anul acesta se organizeaz  prima ediµie a Olimpiadei Pluridisciplinare pentru Centrele de
Excelenµ , PluriCEX. Fiecare Centru de Excelenµ  din µar  va trimite la concurs o echip  format 
din k membri (toµi participanµi la Centrul de Excelenµ ). Echipa va trebui s  rezolve probleme
interdisciplinare, disciplinele vizate ind cele de la Centrul de Excelenµ  (D discipline, pe care le
vom considera numerotate de la 1 la D).
Directorul CEX Ia³i a f cut o list  cu primii n cei mai buni elevi de la CEX, apoi a numerotat
elevii de la 1 la n, în ordinea apariµiei lor în list . Pentru ecare elev, directorul a notat disciplinele
la care el particip  la CEX.

Cerinµe
Scrieµi un program care s  determine toate echipele ce pot  formate din k dintre cei n elevi
de pe lista directorului, cu condiµia ca pentru ecare disciplin  s  existe în echip  cel puµin un
membru care s  studieze la CEX disciplina respectiv .

Date de intrare
Fi³ierul de intrare pluricex.in conµine pe prima linie trei numere naturale n k D (cu semni-
caµia din enunµ). Urmeaz  n linii care descriu particip rile la CEX ale celor n elevi de pe lista
directorului. Mai exact, pe linia i  1 este descris  participarea elevului i astfel:
nr d1 d2 ... dnr
Primul num r de pe linie (nr) indic  num rul de discipline la care particip  elevul i. Urm -
toarele nr numere reprezint  disciplinele la care particip  elevul i. Numerele scrise pe aceea³i linie
sunt separate prin spaµiu.

Date de ie³ire
Fi³ierul de ie³ire pluricex.out va conµine toate echipele ce se pot forma respectând condiµiile
din enunµ, câte o echip  pe o linie. Membrii unei echipe vor  scri³i în ordine cresc toare, separaµi
prin câte un spaµiu. Echipele vor  scrise în ordine lexicograc .

Restricµii ³i preciz ri
a 0 $ n & 22
a 0$k&8
a 0 $ D & 10
a Pentru datele de test problema admite întotdeauna soluµie, num rul de soluµii ind $ 20000.
CAPITOLUL 13. OJI 2008 223

a Spunem c  vectorul x1 , x2 , ..., xn  preced  lexicograc vectorul y1 , y2 , ..., yn  dac  exist 


un indice i astfel încât xj yj , pentru orice 1 & j $ i, iar xi $ yi.
a Pentru 20% din teste soluµia este unic .

Exemple
pluricex.in pluricex.out
6 3 5 234
1 2 345
2 1 4
3 2 43
1 5
2 3 1
1 3

Timp maxim de executare/test: 1.0 secunde

13.2.1 Indicaµii de rezolvare


prof. Emanuela Cerchez - Liceul de Informatic  "Grigore Moisil" Ia³i

Vom genera în ordine lexicograc  toate submulµimile de k elemente ale mulµimii r1, 2, ..., nx
(combin ri de n luate câte k ) printr-un algoritm de tip succesor.
Pentru ecare submulµime vom verica dac  pentru ecare disciplin  exist  cel puµin un mem-
bru al echipei care s  studieze la CEX disciplina respectiv  (în caz armativ, soluµia va  a³at ).
Generare combin ri
Fie n ³i k dou  numere naturale, 1 & k & n & 22.
S  se genereze în ordine lexicograc  toate submulµimile formate din k elemente ale mulµimii
r1, 2, ..., nx.

Soluµie
Vom reprezenta o submulµime ca un vector C cu k elemente (elementele submulµimii).
Evident, 1 & C i & n.
Deoarece ordinea elementelor într-o submulµime nu conteaz , pentru a nu genera de mai multe
ori aceea³i submulµime vom conveni c  plas m elementele în submulµime în ordine cresc toare:
C i $ C i  1, pentru orice 1 & i $ k .
Vom genera submuµimile printr-un algoritm de tip succesor.
Cea mai mic  submulµime (din pdv lexicograc) este 1, 2, ..., k.
Cea mai mare submulµime este: n-k+1,..., n-1, n
Intrebare: care este cea mai mare valoare care poate  plasat  pe poziµia i:
Poziµie k k  1 k  2 ... i ... 1
Valoare maxim  n n  1 n  2 ... ? ... n  k  1
Observ m c  atunci când poziµia scade cu 1, valoarea maxim  scade cu 1, deci diferenµa dintre
V aloarea_maxima ³i P ozitie este constant :
V aloare_maxima  P ozitie n  k ?  i.
Deducem c  valoarea maxim  care poate  plasat  pe poziµia i este n  k  i.
Pas 1. Iniµializ m vectorul C (C i 1, pentru orice 1 & i & k )
Pas 2. Cât timp este posibil (mai exist  succesor)
- a³ m submulµimea curent  (dac  veric  condiµiile din enunµ);
- gener m submulµimea urm toare; în acest scop c ut m prima component  (începând din
dreapta c tre stânga) care poate  m rit  (adic  C i $ n  k  i; dac  g sim o astfel de
component  o m rim, ³i repunem pe cea mai mic  valoare posibil  toate componentele urm toare
(C j  C j  1  1, i $ j & k ); dac  nu g sim o astfel de component  deducem c  generarea s-a
încheiat, acesta a fost cea mai mare submulµime din punct de vedere lexicograc.
Generare submulµimi
O alt  soluµie posibil  este de a genera toate submulµimile mulµimii r1, 2, ..., nx ³i de a verica
corectitudinea acelor soluµii care au k elemente. O astfel de abordare ar obµine în principiu 80 de
puncte (70 sau 90 ind punctaje posibile, funcµie de calculatorul de evaluare ³i ecienµa veric rii
condiµiilor).
CAPITOLUL 13. OJI 2008 224

13.2.2 Cod surs 

Listing 13.2.1: pluricex.cpp


1 //Emanuela Cerchez; 100 puncte
2 #include <stdio.h>
3
4 #define NMax 25
5 #define DMax 11
6
7 int n, k, D;
8 int a[NMax][DMax];
9 int sol[NMax];
10 int uz[DMax];
11
12 FILE *fout;
13 void read();
14 void solve();
15 void write();
16
17 int main()
18 {
19 fout=fopen("pluricex.out","w");
20 read();
21 solve();
22 fclose(fout);
23 return 0;
24 }
25
26 int verif()
27 {
28 int i, j;
29
30 for (i=1; i<=D; i++) uz[i]=0;
31
32 for (i=1; i<=k; i++)
33 for (j=1; j<=D; j++)
34 if (a[sol[i]][j]) uz[j]=1;
35
36 for (i=1; i<=D; i++)
37 if (!uz[i]) return 0;
38
39 return 1;
40 }
41
42 void solve()
43 {
44 int i;
45
46 for (i=1; i<=k; i++) sol[i]=i;
47
48 while (1)
49 {
50 if (verif()) write();
51 for (i=k; i>0 && sol[i]==n-k+i; i--);
52 if (!i) break;
53 sol[i]++;
54 for (i++; i<=k; i++) sol[i]=sol[i-1]+1;
55 }
56 }
57
58 void read()
59 {
60 int i, nr, d, j;
61 FILE * fin = fopen("pluricex.in", "rt");
62
63 fscanf(fin, "%d %d %d", &n, &k, &D);
64
65 for (i=1; i<=n; i++)
66 {
67 fscanf (fin,"%d",&nr);
68 for (j=1; j<=nr; j++)
69 {
70 fscanf (fin, "%d",&d);
CAPITOLUL 13. OJI 2008 225

71 a[i][d]=1;
72 }
73 }
74
75 fclose(fin);
76 }
77
78 void write()
79 {
80 int i;
81 for (i=1; i<k; i++)
82 fprintf(fout,"%d ",sol[i]);
83
84 fprintf(fout,"%d\n",sol[k]);
85 }

13.2.3 *Rezolvare detaliat 


Capitolul 14

OJI 2007 clasa a IX-a

14.1 Cartele
În sediul unei rme 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, veric  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 ecare î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 rmei are mai multe cartele. Pe unele dintre acestea a fost imprimat noul cod
de intrare, iar pe altele sunt coduri mai vechi. Pentru a aa care sunt cartelele care-i permit
accesul în sediul rmei angajatul este nevoit s  le verice pe toate, introduc ndu-le pe rând, în
toate modurile pe care le consider  necesare, în fanta cititorului de cartele.

Sablon Cartela 1 Cartela 2

Figura 14.1: Cartele1

Cerinµ 
Scrieµi un program care determin  care dintre cartele permite accesul în sediul rmei.
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 car-
telelelor. C reprezint  num rul de cartele. Urmeaz  C  1 blocuri de câte N linii ecare. Primul
bloc de N linii codic  ³ablonul. Urm toarele C blocuri de câte N linii codic  ecare câte o
cartel . Pe ecare 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.

226
CAPITOLUL 14. OJI 2007 CLASA A IX-A 227

Date de ie³ire
În ³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
3 2 1 Datele de intrare corespund situaµiei din gur .
0 1 0 0 Cartela 1 se potrive³te perfect ³ablonului, dac 
0 0 1 se rote³te în sens trigonometric cu 90 de grade.
1 0 0 Cartela 2 nu se potrive³te ³ablonului, indiferent
1 0 0 de modul în care se introduce în fant .
0 0 1
0 1 0
0 0 1
0 0 1
0 1 0
Timp maxim de execuµie/test: 1 secund 

14.1.1 Indicaµii de rezolvare

Pentru ecare 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 aseaz 
1 ³i se trece la prelucrarea urm toarei cartele.
Dac  nici dup  pasul 8 nu s-a gasit o potrivire exact , se a³eaz  0 ³i se trece la prelucrarea
urm toarei cartele.

14.1.2 Cod surs 

Listing 14.1.1: carteleC.cpp


1 #include <fstream>
2 #include <iomanip>
3
4 using namespace std;
5
6 #define DIM 51
7
8 ifstream fin("cartele.in");
9 ofstream fout("cartele.out");
10
11 int a[DIM][DIM], b[DIM][DIM]; // sablonul, cartela
12 int aux[DIM][DIM];
13 int n, C;
14
15 int Egale();
16 void Inverseaza(); // intoarce cartela pe partea cealalta
17 // (oglindire fata de linia i = n/2)
CAPITOLUL 14. OJI 2007 CLASA A IX-A 228

18 void Roteste(); // rotire 90 grade in sens trigonometric


19 void Rezolva();
20
21 int main()
22 {
23 Rezolva();
24 fin.close();
25 fout.close();
26 return 0;
27 }
28
29 void Rezolva()
30 {
31 fin >> n >> C;
32 int i, j, r;
33 int identice = 1;
34
35 for (i = 1; i <= n; i++)
36 for (j = 1; j <= n; j++)
37 fin >> a[i][j];
38
39 for (int k = 1; k <= C; k++)
40 {
41 identice = 1;
42 for (i = 1; i <= n; i++)
43 for (j = 1; j <= n; j++)
44 {
45 fin >> b[i][j];
46 if ( b[i][j] != a[i][j] ) identice = 0;
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
52 // se revine la matricea initiala
53 {
54 Roteste(); // cu 90 in sens trigonometric
55 if ( Egale() ) identice = 1;
56 }
57
58 if ( !identice ) Inverseaza();
59 }
60
61 if ( identice )
62 fout << 1 << ’\n’;
63 else
64 fout << 0 << ’\n’;
65 }
66 }
67
68 int Egale()
69 {
70 for (int i = 1; i <= n; i++)
71 for (int j = 1; j <= n; j++)
72 if ( a[i][j] != b[i][j] )
73 return 0;
74 return 1;
75 }
76
77 void Inverseaza()
78 {
79 int i, j, temp;
80 for (i = 1; i <= n / 2; i++)
81 for (j = 1; j <= n; j++)
82 temp = b[i][j], b[i][j] = b[n-i+1][j], b[n-i+1][j] = temp;
83 }
84
85 void Roteste()
86 {
87 int i, j;
88 for (i = 1; i <= n; i++)
89 for (j = 1; j<= n; j++)
90 aux[n-j+1][i] = b[i][j];
91
92 for (i = 1; i <= n; i++)
93 for (j = 1; j <= n; j++)
CAPITOLUL 14. OJI 2007 CLASA A IX-A 229

94 b[i][j] = aux[i][j];
95 }

14.1.3 Rezolvare detaliat 


(j-1) j direct 90 i (i-1) 180 270
(i-1) (j-1)
i j i j
(i-1) (j-1)
j (j-1) (i-1) i
invers j (j-1) 90 180 (i-1) i 270
(i-1) (j-1)
i j i j
(j-1) (i-1)
i (i-1) (j-1) j

Figura 14.2: Cartele2

Varianta 1:

Listing 14.1.2: cartele1.java


1
2 import java.io.*;
3 class cartele
4 {
5 static final int DIM=51;
6
7 static StreamTokenizer st;
8 static PrintWriter out;
9
10 static int[][] a=new int[DIM][DIM]; // sablonul
11 static int[][] b=new int[DIM][DIM]; // cartela
12 static int[][] aux=new int[DIM][DIM];// auxiliar
13
14 static int n, c;
15
16 public static void main(String[] args) throws IOException
17 {
18 st=new StreamTokenizer(new BufferedReader(new FileReader("cartele.in")));
19 out=new PrintWriter(new BufferedWriter(new FileWriter("cartele.out")));
20
21 rezolva();
22 out.close();
23 }// main(...)
24
25 static void rezolva() throws IOException
26 {
27 int i, j, k, r;
28 boolean identice;
29
30 st.nextToken(); n=(int)st.nval;
31 st.nextToken(); c=(int)st.nval;
32
33 for(i=1;i<=n;i++)
34 for(j=1;j<=n;j++)
35 {
36 st.nextToken(); a[i][j]=(int)st.nval;
37 }
38
39 for(k=1;k<=c;k++)
40 {
41 identice=true;
42 for(i=1;i<=n;i++)
43 for(j=1;j<=n;j++)
CAPITOLUL 14. OJI 2007 CLASA A IX-A 230

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:

Listing 14.1.3: cartele2.java


1 import java.io.*;
2 class cartele
3 {
4 static final int DIM=51;
5
6 static StreamTokenizer st;
7 static PrintWriter out;
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 boolean ok;
18
19 st=new StreamTokenizer(new BufferedReader(new FileReader("cartele.in")));
20 out=new PrintWriter(new BufferedWriter(new FileWriter("cartele.out")));
21
22 st.nextToken(); n=(int)st.nval;
23 st.nextToken(); c=(int)st.nval;
24
25 for(i=1;i<=n;i++) // citesc sablonul
26 for(j=1;j<=n;j++)
27 {
CAPITOLUL 14. OJI 2007 CLASA A IX-A 231

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:

Listing 14.1.4: cartele3.java


1 import java.io.*;
2 class cartele
3 {
4 static final int DIM=51;
5
6 static StreamTokenizer st;
7 static PrintWriter out;
CAPITOLUL 14. OJI 2007 CLASA A IX-A 232

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

14.2 Paritate
În vederea asigur rii unei transmiteri cât mai exacte a informaµiilor pe reµea, transmiterea se
efectueaz  caracter cu caracter, ecare caracter ind dat prin codul s u ASCII, adic  o grup  de
8 biµi (octet). Pentru ecare 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 stnga) egal cu 0. Pe aceast 
poziµie va  pus bitul de paritate, economisind astfel câte un bit pentru ecare caracter transmis.
CAPITOLUL 14. OJI 2007 CLASA A IX-A 233

De exemplu, dac  mesajul care trebuie trasmis conµine caracterele "Paritate", succesiunea de biµi
transmis  va :

01010000 11100001 01110010 01101001 01110100 11100001 01110100 01100101

Î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  verice 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 ind 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 ³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 ³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 ³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  trebuit s  existe
un num r impar de cifre 1, ceea ce este fals. Deci caracterul nu
a fosttransmis corect. Acela³i lucru se veric  ³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 14. OJI 2007 CLASA A IX-A 234

14.2.1 Indicaµii de rezolvare

Se utilizeaz  un tablou de caractere care va conµine:


- caracterul corect transmis sau
- caracterul #0 în cazul în care transmisia nu s-a efectual corect
În acela³i timp variabila Eroare va conµine ultima poziµie a unui cod eronat sau 0 dac  nu sunt
erori la transmiterea mesajului.
Deoarece suntem asiguraµi de faptul c  num rul de biµi 0/1 transmi³i este multiplu de 8, nu
mai fac aceast  vericare ³i tratez ecare grup  de 8 biµi astfel:
- citesc primul caracter separat (este bitul de paritate)
- îl transform în cifra 0/1
- citesc pe rând ceilalµi 7 biµi si formez codul ASCII corect num rând în acela³i timp biµii egali
cu 1
- dac  bitul de paritate este corect (adic  am un num r par de cifre 1) pun pe poziµia co-
respunz toare din tablou caracterul al c rui cod îl am - în caz contrar pun pe poziµia respectiv 
valoarea #0 ³i reµin în variabila Eroare poziµia caracterului eronat
Dup  terminarea acestui proces nu am decât s  veric variabila Eroare:
- în cazul în care are valoarea 0 (transmisie f r  eroare), a³ez 'DA' în prima linie a ³ierului
de ie³ire, apoi parcurg vectorul caracter cu caracter ³i îl scriu în ³ierul de ie³ire, având grij  ca
în cazul întâlnirii caracterului #10 (cod de linie nou ) s  trec la o nou  linie
- în cazul în care are o valoare >0 (transmisie cu erori) asez 'NU' în prima linie a ³ierului de
ie³ire, apoi parcurg vectorul caracter cu caracter ³i, în cazul întâlnirii valorii #0 (caracter eronat)
a³ez indicele respectiv.

14.2.2 Cod surs 

Listing 14.2.1: PARITATE.C


1 #include <stdio.h>
2
3 #define MAX 60000
4
5 char a[MAX]; //0: eroare; Caracter: corect
6 char c;
7 long i, j;
8 int BitP, Cod, Bit, Nr1;
9 long Eroare; //va contine ultima pozitie
10 //a unui cod eronat sau 0 daca nu sunt erori
11 FILE *f, *g;
12
13 int main()
14 {
15 f=fopen("paritate.in", "rt");
16 g=fopen("paritate.out", "wt");
17
18 i=-1; Eroare=0; c=0;
19 fscanf(f, "%c", &c);
20
21 while (c!=’\n’)
22 {
23 i++; //pozitie caracter
24 BitP=c-’0’; //bitul de paritate
25 Cod=0; //aici formez codul
26 Nr1=0; //cati de 1
27
28 for (j=1; j<=7; j++) //citesc ceilalti 7 biti
29 {
30 fscanf(f, "%c", &c); //citesc bit
31 Bit=c-’0’;
32 if (Bit==1) Nr1++; //daca e 1 il numar
33 Cod=Cod*2+Bit; //formez codul
34 }
35
36 if ((Nr1+BitP)%2==0) //daca cod corect
37 a[i]=Cod; //pun caracterul in vector
38 else //altfel
39 {
CAPITOLUL 14. OJI 2007 CLASA A IX-A 235

40 a[i]=1; //pun 1
41 Eroare=i; //si retin pozitia
42 }
43 fscanf(f, "%c", &c);
44 }
45
46 if (Eroare==0) //daca nu sunt erori
47 { //scrie DA si
48 fprintf(g, "DA\n");
49 for (j=0; j<=i; j++) //afiseaza cele i+1 caractere
50 if (a[j]==10) //avand grija la caracterul cu codul 10
51 fprintf(g, "\n");
52 else //altfel
53 fprintf(g, "%c", a[j]); //scrie caracterul
54 // fprintf(g, "\n");
55 }
56 else //eroare!!!
57 {
58 fprintf(g, "NU\n"); //scrie NU si
59 for (j=0; j<Eroare; j++)
60 if (a[j]==1) //cauta erorile - cod 01
61 fprintf(g, "%ld ", j); //si afiseaza pozitia lor
62 fprintf(g, "%ld\n", Eroare); //afiseaza pozitia ultimei erori
63 }
64
65 fclose(g);
66 return 0;
67 }

14.2.3 Rezolvare detaliat 

Varianta 1: Versiunea este o prelucrare a variantei ociale; comentariile din sursa au r mas
nemodicate pentru c  sunt un exemplu bun!

Listing 14.2.2: Paritate.java


1 import java.io.*;
2 class paritate
3 {
4 static final int MAX=60000;
5 static int[] a=new int[MAX]; // 0=eroare
6
7 public static void main(String[] args) throws IOException
8 {
9 int c;
10 int i,j,k;
11 int BitP, Cod, Bit, Nr1;
12 int Eroare; // ultima pozitie a unui cod eronat sau 0 daca nu sunt erori
13
14 //BufferedReader br0=new BufferedReader(new InputStreamReader(System.in));
15 BufferedReader br=new BufferedReader(new FileReader("paritate.in"));
16 PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter("paritate.out")));
17
18 i=-1;
19 Eroare=0;
20 c=0;
21 String s=br.readLine();
22 //System.out.println("s = "+s);
23 k=0;
24 while(k<s.length()-1)
25 {
26 c=s.charAt(k);
27 //System.out.println(k+" : "+c);
28 //br0.readLine();
29 i++; // pozitie caracter
30 BitP=c-’0’; // bitul de paritate
31 Cod=0; // aici formez codul
32 Nr1=0; // cati de 1
33 for(j=1;j<=7;j++) // citesc ceilalti 7 biti
34 {
35 c=s.charAt(++k); // citesc bit
36 //System.out.println(k+" : "+c);
37 //br0.readLine();
CAPITOLUL 14. OJI 2007 CLASA A IX-A 236

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 15

OJI 2006 clasa a IX-a

15.1 Flori
Cristina Bohm
Fetiµele din grupa mare de la gr diniµ  culeg ori ³i vor s  împleteasc  coroniµe pentru festivi-
tatea de premiere. În gr din  sunt mai multe tipuri de ori. Fiecare dintre cele n fetiµe culege un
buchet având acela³i num r de ori, î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 oare 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 ori dintr-un buchet, s  se determine grupele care se formeaz .
Date de intrare
Fi³ierul de intrare ori.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 ori din ecare buchet. Fiecare
dintre urm toarele n linii conµine, pentru ecare fetiµ , câte k valori separate prin câte un spaµiu
reprezentând tipurile de ori culese.
Date de ie³ire
Fi³ierul de ie³ire ori.out va conµine pe ecare 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 ori 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 ³ierul de ie³ire grupele vor  a³ate în ordinea cresc toare a num rului de ordine al
primei fetiµe din grup .
Exemplu
ori.in ori.out Explicaµie
5 4 134 Fetiµele 1 ³i 3 au cules amândou  ori de tipul 1,
1 2 3 4 2 iar fetiµele 1 ³i 4 au cules amândou  ori de tipurile
5 6 9 6 5 2,3 ³i 4, deci toate cele trei fetiµe (1, 3, 4) se vor aa
1 1 1 1 în aceea³i grup . Fetiµele 2 ³i 5 vor forma ecare câte
2 4 4 3 o grup  deoarece nu au cules ori de acela³i tip cu
7 7 7 7 nici una dintre celelalte fetiµe.
Timp de rulare/test: 1 secund 

15.1.1 Indicaµii de rezolvare

Soluµia comisiei

- citesc n - num rul de fetiµe ³i k - num rul de ori dintr-un buchet

237
CAPITOLUL 15. OJI 2006 CLASA A IX-A 238

- construiesc matricea a denita astfel : pe linia i sunt tipurile distincte de ori 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  ecare fetiµ  e în grup  doar ea cu ea;

- apelul irelj(i,j) veric  dac  i e în relaµie cu j : caut  pe linia i ³i j un tip de oare


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 

- a³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
a³ate pentru a nu le mai relua o dat .

15.1.2 Cod surs 

Listing 15.1.1: FLORI.CPP


1 #include<iostream>
2 #include<stdio.h>
3
4 using namespace std;
5
6 FILE *f=fopen("Flori.In","r");
7 FILE *g=fopen("Flori.Out","w");
8
9 int n,k,a[150][150];
10
11 int irelj(int i,int j) // verifica daca fata i e in relatie cu j
12 // (au cel putin o floare in comun)
13 {
14 int u,v;
15 for(u=1;u<=a[i][0];u++) //a[i][0] e numarul de elemente pe linia i
16 for(v=1;v<=a[j][0];v++)
17 if (a[i][u]==a[j][v])
18 return 1;
19 return 0;
20 }
21
22 int apartine(int val,int linie) //caut val in multimea de pe linia linie
23 {
24 int j,lg=a[linie][0];
25
26 for(j=1;j<=lg;j++)
27 if (val==a[linie][j])
28 return 1;
29 return 0;
30 }
31
32 void reuneste(int i,int j) //reuneste in linia i linia j
33 {
34 int u;
35 for(u=1;u<=a[j][0];u++)
36 if(!apartine(a[j][u],i))
37 {
38 a[i][0]++;
39 a[i][ a[i][0] ]=a[j][u];
40 }
41 }
42
CAPITOLUL 15. OJI 2006 CLASA A IX-A 239

43 int main()
44 {
45 int viz[150],i,j,val,ok;
46 fscanf(f,"%d %d",&n,&k);
47
48 for(i=1;i<=n;i++)
49 for(j=1;j<=k;j++)
50 {
51 fscanf(f,"%d",&val);
52 if(!apartine(val,i))
53 {
54 a[i][0]++; // pe prima coloana am nr. de tipuri distincte
55 // de flori
56 a[i][ a[i][0] ]=val; // in multimea de pe linia i am tipurile
57 // distincte de flori al fetitei i
58 }
59 }
60
61 for(i=1;i<=n;i++)
62 viz[i]=i; //initial exista n grupe
63
64 for(i=1;i<=n;i++)
65 {
66 ok=0;
67 if(a[i][0])
68 {
69 for(j=i+1;j<=n;j++)
70 if(irelj(i,j))
71 {
72 viz[j]=viz[i]; //j trebuie sa ajunga in grupa cu i
73 reuneste(i,j); //reunesc in linia i linia j
74 a[j][0]=0;//consider ca in multimea j am 0 elemente acuma
75 ok=1;
76 }
77 }
78
79 if (ok) i--;// faptul ca am reunit in i cel putin o multime j implica
80 // sa continui cu aceeasi linie i
81 // daca as lasa i sa se incrementeze conform for-ului,
82 // ar gresi in sensul ca
83 // pt. i rel j si i nu e in rel cu k si j rel k
84 // ar pune j in grupa i dar k ar ajunge in alta grupa
85 }
86
87 for(i=1;i<=n;i++)
88 if(viz[i])
89 {
90 fprintf(g,"%d ",i);
91 for(j=i+1;j<=n;j++)
92 if(viz[i]==viz[j])
93 {
94 fprintf(g,"%d ",j);
95 viz[j]=0; //ca sa nu mai fie prelucrat
96 }
97
98 fprintf(g,"\n");
99 }
100
101 fclose(g);
102 return 0;
103 }

15.1.3 Rezolvare detaliat 

Variant  iterativ :

Listing 15.1.2: ori1.java


1 import java.io.*;
2 class Flori1
3 {
4 static int n,k;
CAPITOLUL 15. OJI 2006 CLASA A IX-A 240

5 static int[][] a=new int[151][101];


6 static int[] gf=new int[151];
7 static int[] fgc=new int[101];
8
9 public static void main(String[] args) throws IOException
10 {
11 int i,j,ii,ng,ok,gasit;
12 StreamTokenizer st=new StreamTokenizer(
13 new BufferedReader(new FileReader("flori.in")));
14 PrintWriter out=new PrintWriter(
15 new BufferedWriter(new FileWriter("flori.out")));
16
17 st.nextToken(); n=(int)st.nval;
18 st.nextToken(); k=(int)st.nval;
19
20 for(i=1;i<=n;i++)
21 for(j=1;j<=k;j++) { st.nextToken(); a[i][j]=(int)st.nval; }
22
23 ng=0;
24 for(i=1;i<=n;i++)
25 {
26 if(gf[i]!=0) continue;
27 ng++;
28 for(j=0;j<=100;j++) fgc[j]=0;
29 for(j=1;j<=k;j++) fgc[a[i][j]]=1;
30
31 ok=1;
32 while(ok==1)
33 {
34 ok=0;
35 for(ii=i+1;ii<=n;ii++)
36 {
37 if(gf[ii]!=0) continue;
38 gasit=0;
39 for(j=1;j<=k;j++) if(fgc[a[ii][j]]==1)
40 {
41 gasit=1;
42 break;
43 }
44
45 if(gasit==1)
46 {
47 for(j=1;j<=k;j++) fgc[a[ii][j]]=1;
48 ok=1;
49 gf[ii]=ng;
50 }
51 }//for ii
52 }//while
53
54 out.print(i+" ");
55 for(j=1;j<=n;j++) if(gf[j]==ng) out.print(j+" ");
56 out.println();
57 }//for i
58
59 out.close();
60 }// main
61 }// class

Variant  recursiv :
Listing 15.1.3: ori2.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 15. OJI 2006 CLASA A IX-A 241

14 new BufferedWriter(new FileWriter("flori.out")));


15
16 st.nextToken(); n=(int)st.nval;
17 st.nextToken(); k=(int)st.nval;
18
19 for(i=1;i<=n;i++)
20 for(j=1;j<=k;j++) { st.nextToken(); fij=(int)st.nval; a[i][fij]=1;}
21
22 ng=0;
23 for(i=1;i<=n;i++)
24 {
25 if(gf[i]!=0) continue;
26 ng++;
27 fata(i);
28 }
29
30 for(i=1;i<=ng;i++)
31 {
32 for(j=1;j<=n;j++) if(gf[j]==i) out.print(j+" ");
33 out.println();
34 }
35
36 out.close();
37 }// main
38
39 static void fata(int i)
40 {
41 int j,ii;
42
43 gf[i]=ng;
44 for(j=0;j<=100;j++)
45 {
46 if(a[i][j]==0) continue;
47 for(ii=1;ii<=n;ii++)
48 {
49 if(ii==i) continue;
50 if(a[ii][j]==1)
51 if(gf[ii]==0) fata(ii);
52 }
53 }
54 }// fata(...)
55 }// class

15.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 identicare pe care soldaµii le poart  la gât. Pe aceste pl cuµe
sunt scrise numere care pot identica ecare soldat ³i plutonul din care acesta face parte. Astfel,
soldaµii din acela³i pluton au num rul de identicare format din acelea³i cifre, dispuse în alt 
ordine ³i numerele de identicare sunt unice. De exemplu, numerele de identicare 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 identicare, 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
ecare dintre urm toarele n linii câte un num r de identicare 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 15. OJI 2006 CLASA A IX-A 242

componenµa unui astfel de pluton, cu num r maxim de soldaµi recuperaµi, numerele de identicare
ale soldaµilor din componenµ  ind 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 identicare $ 2.000.000.000
Observaµii
Deoarece linia a patra conµine numerele de identicare 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 ind î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 ind 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 

15.2.1 Indicaµii de rezolvare

Soluµia comisiei
Soluµia 1:
 în timpul citirii creez un nou vector care conµine pe poziµiile corespunz toare numerele de
identicare din vectorul iniµial în ordinea descrescatoare a cifrelor
 în etapa a doua se parcurge vectorul nou format grupând toate numerele de identicare
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 identicare în vectorul a[]

 construiesc 2 vectori ajut tori

a vectorul b[] care va conµine num rul de cifre al ec rui element


a vectorul c[] care va conµine num rul de cifre distincte a ec rui element

 ordonez cei trei vectori cresc tor dup  num rul de cifre distincte (dup  c[]) ³i dup  num rul
de cifre, deci cheia de sortare va  un num r construit dup  formula c[i]*10+b[i]

 formez ³i num r plutoanele; plutoanele le voi reµine pe linii distincte a matricei G[MAX][2]

a în ecare linie, elementul G[i][0] va conµine num rul de elemente din pluton


a elementul G[i][1] va conµine reprezentantul plutonului, primul care apare în a[]
a repet pân  când toate elementele din a au fost vericate
reµinem primul element nepus înc  din pluton cu caracteristicile lui
veric elementele cu acelea³i caracteristici s  fac  parte din acela³i pluton cu primul
element din pluton pe care l-am reµinut
CAPITOLUL 15. OJI 2006 CLASA A IX-A 243

testez dac  are acelea³i cifre


· dac  nu are acelea³i cifre trec mai departe
· altfel îl numar (înc  face parte din acela³i pluton)

 detectez num rul maxim de elemente ale unui pluton ³i reµin maximul

 a³are cerinµele 1 2 3 folosind valorile aate

 la cerinµa 4

caut in a[] reprezentantul unui pluton numeros


a³ez cele maxe elemente - în vectorul sortat elementele cu acelea³i caracteristici (b ³i c)
sunt unul dup  altul, dar mai trebuie vericat s  aib  caracteristicile reprezentantului
(ex. 1212 ³i 3434 au acelea³i caracteristici (4,2), (4,2), dar nu fac parte din acela³i
pluton)

Soluµia 3:
 se utilizeaza notiunea de lista: dintr-o lista fac parte toti membrii unui pluton

 se construieste un vector ajutator care retine pentru ecare 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

 asarea se face utilizand informatiile din vetorul urm

15.2.2 Cod surs 

Listing 15.2.1: PLUTONC.C


1 #include <stdio.h>
2
3 #define MAX 4001
4 #define TRUE 1
5 #define FALSE 0
6
7 FILE *Fin, *Fout;
8
9 long a[MAX];
10 unsigned char b[MAX], c[MAX], Pus[MAX];
11 long G[MAX][2];
12 unsigned char cifre[10], cifreTest[10];
13 long n, Nre, NrG, maxe, btest, ctest;
14 unsigned char OK;
15
16 int ToatePuse(void)
17 {
18 int i, x;
19
20 x = TRUE;
21 for (i=1; i<=n; i++)
22 x*=Pus[i];
23 return x;
24 }
25
26 void Cifrele(long x, unsigned char c[10])
27 {
28 int j;
29
30 for (j=0; j<=9; j++) c[j]=0;
31 while (x)
32 {
33 c[x%10]++;
34 x/=10;
35 }
36 }
37
CAPITOLUL 15. OJI 2006 CLASA A IX-A 244

38 void Citire(void)
39 {
40 int i, j;
41 long x;
42
43 fscanf(Fin, "%ld", &n);
44 for (i=1; i<=n; i++)
45 {
46 fscanf(Fin, "%ld", &a[i]);
47 /*construiesc 2 vectori ajutatori
48 - vectorul b care va contine numarul de cifre al fiecarui element
49 - vectorul c care va contine numarul de cifre distincte a fiecarui element*/
50 for (j=0; j<=9; j++) cifre[j]=0;
51 x=a[i];
52 while (x)
53 {
54 b[i]++; //numar total de cifre
55 cifre[x%10]=1;
56 x/=10;
57 }
58 for (j=0; j<=9; j++)
59 c[i]+=cifre[j]; //numar de cifre distincte
60 }
61 }
62
63 void Sortare(void)
64 {
65 int i, j;
66 long min, pm, x, aux;
67
68 /*ordonez cei trei vectori crescator dupa numarul de cifre (dupa b)
69 si dupa numarul de cifre distincte, deci cheia de sortare va fi un numar
70 construit dupa formula b[i]*10+c[i] */
71 for (i=1; i<=n-1; i++)
72 {
73 min=b[i]*10+c[i]; pm=i;
74 for (j=i+1; j<=n; j++)
75 {
76 x=b[j]*10+c[j];
77 if (x<min)
78 {
79 min=x; pm=j;
80 }
81 }
82
83 aux=c[i]; c[i]=c[pm]; c[pm]=aux;
84 aux=b[i]; b[i]=b[pm]; b[pm]=aux;
85 aux=a[i]; a[i]=a[pm]; a[pm]=aux;
86 }
87 }
88
89 void FormarePlutoane(void)
90 {
91 int i, j;
92
93 /*formez si numar plutoanele
94 - plutoanele le voi retine pe linii distincte a matricii G
95 - in fiecare linie, elementul 0 va contine numarul de elemente din pluton
96 iar elementul 1 reprezentantul plutonului, primul care apare in a*/
97 while (!ToatePuse())
98 {
99 i=1;
100 while (Pus[i]) i++; //caut primul nepus inca
101
102 //retinem primul element nepus din pluton cu caracteristicile lui
103 btest=b[i]; ctest=c[i];
104 NrG=NrG+1; G[NrG][0]=1;
105 G[NrG][1]=a[i]; Pus[i]=TRUE; //il pun in pluton
106 Cifrele(a[i], cifreTest); //retin cifrele in cifreTest[]
107
108 //verific elementele cu aceleasi caracteristici daca fac parte din
109 //acelasi pluton cu primul element pe care l-am retinut
110 i++;
111 while ((b[i]==btest) && (c[i]==ctest) && (i<=n))
112 { //ar putea face parte din acelasi pluton
113 Cifrele(a[i], cifre);//aflu cifrele in cifre[]
CAPITOLUL 15. OJI 2006 CLASA A IX-A 245

114 OK=TRUE; //testez daca are aceleasi cifre


115 for (j=0; j<=9; j++)
116 if (cifreTest[j]!=cifre[j]) OK=FALSE;
117 if (OK) //are aceleasi cifre
118 {
119 G[NrG][0]++; //un nou soldat in pluton
120 Pus[i]=TRUE; //l-am pus
121 }
122 i++;
123 }
124 }
125 }
126
127 void Afisare(void)
128 {
129 int i, j;
130 long x;
131
132 fprintf(Fout, "%ld\n", NrG); //cerinta 1
133
134 maxe=0; //determin numarul maxim de elemente ale unui pluton
135 for (i=1; i<=n; i++)
136 if (G[i][0]>maxe)
137 {
138 maxe=G[i][0];
139 x=G[i][1]; //retin reprezentantul plutonului numeros
140 }
141
142 fprintf(Fout, "%ld\n", maxe); //cerinta 2
143
144 Nre=0;
145 for (i=1; i<=NrG; i++)
146 if (G[i][0]==maxe)
147 Nre++;
148
149 fprintf(Fout, "%ld\n", Nre); //cerinta 3
150
151 i=1;
152 while (x!=a[i]) i++; //il caut in a
153 fprintf(Fout, "%ld ", a[i]); //scriu reprezentantul plutonului
154 btest=b[i]; ctest=c[i]; //retin caracteristicile
155 Cifrele(a[i], cifreTest); //aflu cifrele
156
157 i++; //cerinta 4
158
159 while ((b[i]==btest) && (c[i]==ctest) && (i<=n))
160 { //ar putea face parte din acelasi pluton
161 Cifrele(a[i], cifre); //aflu cifrele
162 OK=TRUE; //testez daca are aceleasi cifre
163 for (j=0; j<=9; j++)
164 if (cifreTest[j]!=cifre[j]) OK=FALSE;
165 if (OK) //are aceleasi cifre
166 fprintf(Fout, "%ld ", a[i]); //il afisez
167 i++;
168 }
169
170 fprintf(Fout, "\n");
171 }
172
173 int main(void)
174 {
175 Fin=fopen("pluton.in", "rt"); //deschid fisierul ’pluton.in’ pentru citire
176 Fout=fopen("pluton.out", "wt");//deschid fisierul ’pluton.out’ pentru scriere
177 Citire();
178 Sortare();
179 FormarePlutoane();
180 Afisare();
181 fclose(Fin);
182 fclose(Fout);
183 return 0;
184 }

Listing 15.2.2: PLUTCARM.CPP


1 #include <fstream>
CAPITOLUL 15. OJI 2006 CLASA A IX-A 246

2 #include <conio.h>
3 #include <cstring>
4
5 using namespace std;
6
7 int verif(long int a,long int b)
8
9 // verifica daca a si b au aceleasi cifre
10 { int x[10],y[10],i;
11
12 memset(x,0,sizeof(x)); // x[i] retine numarul de cifre de i din a
13 memset(y,0,sizeof(y)); // y[i] retine numarul de cifre de i din b
14
15 while (a)
16 { x[a%10]++;
17 a/=10;
18 }
19
20 while (b)
21 { y[b%10]++;
22 b/=10;
23 }
24
25 for (i=0;i<10;i++)
26 if (x[i]!=y[i])
27 return 0;
28
29 return 1;
30 }
31
32 int main()
33 { long int x[4000],t;
34 int y[4000],z[4000],a[4000],b[4000],urm[4000],max,q,i1,j,p;
35
36 memset(x,0,sizeof(x)); // numerele de identificare ale soldatilor
37 memset(y,0,sizeof(y)); // bitul j, j=0,1,...,9, a lui y[i] este 1 daca
38 // x[i] contine cifra j
39 memset(z,0,sizeof(z)); // z[i]=numarul de cifre (distincte sau nu) din x[i]
40 memset(a,0,sizeof(a)); // a[i] = numaarum plutonului din care face parte i
41
42 // soldatii i si j fac parte din acelasi pluton daca:
43 // 1. y[i]=y[j], adica au aceleasi cifre distincte
44 // 2. z[i]=z[j], adica au acelasi numar total de cifre
45 // dar aceste doua conditii nu sunt suficiente (vezi de exemplu 112 si 122)
46 // 3. verif(x[i],y[j])=1, adica au exact aceleasi cifre
47
48 memset(b,0,sizeof(b));
49 memset(urm,0,sizeof(urm)); // urm[i] memoreaza indicele urmatorului soldat
50 // din acelasi pluton cu i
51
52 int n,i,k;
53
54 ifstream f("pluton.in");
55
56 f>>n; // numarul de soldati
57
58 for (i=0;i<n;i++)
59 { f>>x[i];
60 k=0;
61 t=x[i];
62 y[i]=0; z[i]=0;
63 while (t)
64 { y[i]=y[i] | (1<<(t%10));
65 t=t/10; z[i]++;
66 }
67 }
68
69 t=0; // t = numarul total de plutoane
70 max=0; // max = numarul maxim de soldati dintr-un pluton
71 q=0; // p = indicele primului soldat din plutonul cu
72 // numar maxim de soldati
73 // q = numarul de putoane cu numar maxim de soldati
74 for (i=0;i<n;i++)
75 if (a[i]==0)
76 { i1=i; t++;
77 k=1; a[i]=t;
CAPITOLUL 15. OJI 2006 CLASA A IX-A 247

78 for (j=i+1;j<n;j++)
79 if ((a[j]==0)&&(y[i]==y[j])&&(z[i]==z[j]))
80 { if (verif(x[i],x[j]))
81 { a[j]=t;
82 urm[i1]=j;
83 i1=j;
84 k++;
85 }
86 }
87
88 if (k>max) { max=k; p=i; q=1; }
89 else
90 if (k==max)
91 q++;
92 }
93
94 ofstream g("pluton.out");
95 g<<t<<endl;
96 g<<max<<endl;
97 g<<q<<endl;
98
99 i=p;
100 do
101 { g<<x[i]<<" ";
102 i=urm[i];
103 } while (i!=0);
104
105 g.close();
106 f.close();
107
108 return 0;
109 }

Listing 15.2.3: PLUTRADU.CPP


1 #include<fstream>
2
3 using namespace std;
4
5 long Nr[4001];//Nr[k]=nr de identificare al soldatului k
6
7 long NrSort[4001];//NrSort[k]=nr de identificare al soldatului k
8 //avand cifrele asezate in ordine descrescatoare - cu ajutorul
9 //acestui vector se va verifica rapid daca doi soldati i si j fac
10 //parte din acelasi pluton (adica au numerele de identificare
11 //Nr[i] si Nr[j] formate din acelasi cifre, insa in alta ordine)
12 //deoarece este suficient sa testam daca NrSort[i]=NrSort[j]
13
14 long n; //numarul de soldati
15 long nrp; //numarul de plutoane refacute
16 long maxx; //numarul maxim de soldati dintr-un pluton refacut
17 long nrmax;//numarul de plutoane refacute cu numar maxim de soldati (max)
18
19 long SortareCifre(long x) //functia intoarce numarul obtinut prin asezarea
20 { //in ordine descrescatoare a cifrelor numarului x
21 long aux,c,y; //de exemplu SortareCifre(2810831)=8832110
22
23 aux=x; //in aux se salveaza valoarea initiala a numarului x
24 y=0;
25 for(c=9;c>=0;c--) //se considera pe rand fiecare cifra c de la 9 la 0
26 {
27 while(x) //se ia pe rand fiecare cifra din numarul x
28 { //si daca ea este egala cu c se alipeste la
29 if(x%10==c) y=y*10+x%10; //un alt numar y - astfel numarul y va avea
30 x=x/10; //la sfarsit aceleasi cifre ca si numarul x,
31 } //insa asezate in ordine descrescatoare
32 x=aux; //se reface valoarea initiala a lui x
33 }
34 return y;
35 }
36
37 int main()
38 {
39 long i,j,p,pmax;
40 long aux;
CAPITOLUL 15. OJI 2006 CLASA A IX-A 248

41 fstream f;
42
43 f.open("pluton.in",ios::in);
44 f>>n; //se citesc din fisierul pluton.in numarul n si
45 for(i=1;i<=n;i++) //elementele vectorului Nr si se completeaza valorile
46 { //corespunzatoare in vectorul NrSort
47 f>>Nr[i];
48 NrSort[i]=SortareCifre(Nr[i]);
49 }
50 f.close();
51
52 i=1; //pozitia curenta din vectori Nr si NrSort(soldatul curent i)
53 nrp=nrmax=maxx=0;
54 while(i<=n)
55 {
56 p=i; //se salveaza pozitia curenta i in variabila p
57 j=i+1; //se cauta soldati j din acelasi pluton cu soldatul curent i
58 while(j<=n) //adica soldati pentru care NrSort[j]=NrSort[i]
59 {
60 if(NrSort[i]==NrSort[j]) //in cazul in care se gaseste un astfel de soldat
61 { //acesta este adus langa soldatul curent i
62 i++;
63 aux=Nr[i]; Nr[i]=Nr[j]; Nr[j]=aux;
64 aux=NrSort[i]; NrSort[i]=NrSort[j]; NrSort[j]=aux;
65 }
66 j++;
67 }
68
69 i++; //s-au adus langa soldatul i toti posibilii sai colegi de pluton
70 nrp++; //deci s-a mai refacut un pluton intre pozitiile p si i-1 din
71 //cei doi vectori Nr si NrSort
72
73 if(i-p>maxx) //daca numarul de soldati din ultimul pluton refacut (adica i-p)
74 { //este mai mare strict decat max se actualizeaza valorile
75 maxx=i-p; //variabilelor max si nrmax
76 nrmax=1;
77 pmax=p; //pmax retine pozitia la care incepe in vectorul Nr plutonul
78 } //cu numar maxim de soldati
79 else
80 if(i-p==maxx) nrmax++; //daca numarul de soldati din ultimul pluton refacut
81 } //este egal cu max se actualizeaza valoarea lui nrmax
82
83 f.open("pluton.out",ios::out); //se scriu in fisierul pluton.out rezultatele
84 f<<nrp<<endl<<maxx<<endl<<nrmax<<endl;
85 for(i=pmax;i<pmax+maxx;i++) f<<Nr[i]<<" ";
86 f.close();
87
88 return 0;
89 }

15.2.3 Rezolvare detaliat 


Solutie incorect  pentru Borland C++ 3.1 dar care ia 100 puncte !!!

Listing 15.2.4: pluton1.java


1 import java.io.*;
2 class Pluton1
3 {
4 static int n,np;
5 static long[] ni=new long[4001];
6 static long[] nid=new long[4001];
7 static int[] nip=new int[4001];
8 static int[] z=new int[4001];
9 static int[] fc=new int[10];
10 static int[] x=new int[11];
11
12 static long cifre(long nr) // 1230456789 --> 9.876.543.210 !!!
13 {
14 int i,j,k,nc=0;
15 long nrcd=0; // nr cu cifre descrescatoare
16
17 for(i=0;i<=9;i++) fc[i]=0;
18 for(i=0;i<=10;i++) x[i]=0;
CAPITOLUL 15. OJI 2006 CLASA A IX-A 249

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

Soluµie corect  ³i pentru Borland C++ 3.1


Listing 15.2.5: pluton2.java
1 import java.io.*;
2 class Pluton2
3 {
4 static int n,np;
5 static int[] ni=new int[4001];
6 static int[] nid1=new int[4001];
7 static int[] nid2=new int[4001];
8 static int[] nip=new int[4001];
9 static int[] z=new int[4001];
10 static int[] fc=new int[10];
11 static int[] x=new int[11];
CAPITOLUL 15. OJI 2006 CLASA A IX-A 250

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 16

OJI 2005 clasa a IX-a

16.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 ind toate numerele naturale distincte de la 1 la n . Pentru a
verica programul pe care l-a scris îi trebuie un ³ier care s  conµin  tabloul respectiv. Dup  ce a
creat acest ³ier, fratele s u, pus pe ³otii îi umbl  în ³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  ³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 ³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  modic rile f cute de fratele
lui Mircea.
Date de ie³ire
În ³ierul numere.out se va scrie pe un singur rând cu un singur spaµiu între ele numerele
cerute (primul ind cel mai mic).
Restricµii ³i preciz ri
a 0 $ n & 500.
a Fratele lui Mircea schimb  cel puµin un num r în ³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 ³ierul de intrare au fost înlocuite cu 0
507 numerele 2, 3, 4.
001
698
Timp maxim de execuµie: 1 secund /test

16.1.1 Indicaµii de rezolvare

Soluµia ocial 
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 ³ier, com-
ponentele corespunz toare din x se schimb  în 1 (xv  1).

251
CAPITOLUL 16. OJI 2005 CLASA A IX-A 252

Dup  citirea numerelor din ³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µi-
nerea astfel a sumei secvenµei de numere consecutive ³terse de Mircea. Din p cate sumele sunt
prea mari ³i dep ³esc tipurile predenite. 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 sucient s  utiliz m un ³ir de biµi care vor indica dac 
un num r apare sau nu în ³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 nal 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.

16.1.2 Cod surs 

Listing 16.1.1: numere.pas


1 program numere;
2
3 var f:text;
4 n,i,j,nr0,x:longint;
5 min,max,s,n1,nr1,m:longint;
6 v:array[0..60000]of byte;
7
8 begin
9 assign(f,’numere.in’);
10 reset(f);
11 s:=0;
12 nr0:=0;
13 readln(f,n);
14 for i:=1 to n do
15 begin
16 for j:=1 to n do
17 begin
18 read(f,x);
19 if (x<>0)and(x<=60000) then v[x]:=1;
20 end;
21 readln(f);
22 end;
23 close(f);
24 {for i:=1 to n*n do write(v[i],’ ’);
25 readln; }
26 if n*n>=60000 then m:=60000 else m:=n*n;
27 i:=1;
28 while (v[i]=1)and(i<=m) do inc(i);
29 min:=i;
30 i:=m;
31 while (v[i]=1)and(i>0) do dec(i);
32 max:=i;
33 assign(f,’numere.out’);
34 rewrite(f);
35 writeln(f,min,’ ’,max);
36 close(f);
37 end.

16.1.3 *Rezolvare detaliat 

Listing 16.1.2: numere.java


1 import java.io.*;
2 class Numere
3 {
4 static byte[] x=new byte[600001];
CAPITOLUL 16. OJI 2005 CLASA A IX-A 253

5 static int n,min=0,max=0;


6
7 public static void main(String[] args) throws IOException
8 {
9 int i,j;
10 long t1,t2;
11 t1=System.currentTimeMillis();
12 StreamTokenizer st=new StreamTokenizer(
13 new BufferedReader(
14 new FileReader("numere.in")));
15 st.nextToken(); n=(int)st.nval;
16 for(i=1;i<=n*n;i++)
17 {
18 st.nextToken(); j=(int)st.nval;
19 if(j<=60000) x[j]=1;
20 }
21 min=1;
22 for(i=1;i<=60000;i++) if(x[i]==0) {min=i; break;}
23 max=min;
24 for(i=min+1;i<=60000;i++)
25 if(x[i]==0) max++; else break;
26
27 PrintWriter out=new PrintWriter(
28 new BufferedWriter(
29 new FileWriter("numere.out")));
30 out.println(min+" "+max);
31 out.close();
32 t2=System.currentTimeMillis();
33 System.out.println("Timp = "+(t2-t1));
34 }
35 }

16.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 semnicaµ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 16. OJI 2005 CLASA A IX-A 254

maxd.in maxd.out Explicaµie


200 200 200 12 1 200 are 12 divizori iar in intervalul 200, 200
exist  un singur num r cu aceast  proprietate
maxd.in maxd.out Explicaµie
2 10 643 6 este cel mai mic num r din interval care are
maxim de divizori egal cu 4 ³i sunt 3 astfel de
numere 6, 8, 10
Timp maxim de execuµie: 1 sec/test

16.2.1 Indicaµii de rezolvare


Soluµia ocial 
Pentru un num r natural n cuprins în intervalul a, b, s  consider m descompunerea în
factori primi:
e e e e
n f1 1 ˜ f2 2 ˜ f3 3 ˜ ... ˜ fk k
Num rul de divizori ai lui n este dat de formula:
e1  1 ˜ e2  1 ˜ e3  1 ˜ ... ˜ ek  1
Se obµine un timp de execuµie mai bun dac  pentru factori (fk ) se realizeaz  un tablou unidi-
mensional format de numerele prime din domeniul int, iar n este vericat - legat de proprietatea
de divizibilitate - doar pe valori numere prime din acest tablou.
Se determin  pentru ecare valoare x din intervalul a, b num rul s u de divizori, se reµine
acea valoare ce are num r maxim de divizori; în caz de valori cu acela³i num r maxim de divizori
se incrementeaz  contorul ce reµine num rul acestor valori.
Soluµie prezentat  în GInfo nr. 15/3
Pentru ecare num r în intervalul dat, vom determina num rul divizorilor acestuia.
Pentru aceasta vom determina descompunerea în factori primi a ec rui num r.
Pentru un num r n, descompunerea în factori primi are forma:
e e e e
n f1 1 f2 2 f3 3 ... fk k ,
iar num rul divizorilor va  dat de valoarea
e1  1 e2  1 e3  1... ek  1.
Pentru ecare valoare x din intervalul a, b se determin  num rul s u de divizori, se reµine
acea valoare care are num r maxim de divizori; în cazul identic rii unor valori cu acela³i num r
maxim de divizori, se incrementeaz  contorul care reµine num rul acestor valori.
În cazul identic rii unei valori cu num r mai mare de divizori, valoarea contorului devine 1
³i se actualizeaz  variabila care conµine num rul cu cei mai mulµi divizori.
În nal se va a³a prima valoare x care are cel mai mare num r de divizori, precum ³i valoarea
contorului.

16.2.2 *Cod surs 

16.2.3 *Rezolvare detaliat 

Varianta 1:

Listing 16.2.1: MaxD1.java


1 import java.io.*; //timp = 5600
2 class MaxD
3 {
4 static int[] x;
5 static int a,b;
6
7 public static void main(String[] args) throws IOException
8 {
9 int i;
10 long t1,t2;
11 t1=System.currentTimeMillis();
12 StreamTokenizer st=new StreamTokenizer(
13 new BufferedReader(new FileReader("maxd.in")));
14 st.nextToken(); a=(int)st.nval;
CAPITOLUL 16. OJI 2005 CLASA A IX-A 255

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:

Listing 16.2.2: MaxD2.java


1 import java.io.*;
2 class MaxD
3 {
4 public static void main(String[] args) throws IOException
5 {
6 int i;
7 int a,b;
8
9 long t1,t2;
10 t1=System.currentTimeMillis();
11
12 StreamTokenizer st=new StreamTokenizer(
13 new BufferedReader(new FileReader("maxd.in")));
14
15 st.nextToken(); a=(int)st.nval;
16 st.nextToken(); b=(int)st.nval;
17
18 int max=-1;
19 int imax=-1;
20 int nrmax=0;
21 int nd;
22
23 for(i=a;i<=b;i++)
24 {
25 nd=nrDiv(i);
26 if(nd>max) {max=nd; imax=i; nrmax=1;}
27 else if(nd==max) nrmax++;
28 }
29
CAPITOLUL 16. OJI 2005 CLASA A IX-A 256

30 PrintWriter out=new PrintWriter(


31 new BufferedWriter(new FileWriter("maxd.out")));
32 out.println(imax+" "+max+" "+nrmax);
33 out.close();
34 t2=System.currentTimeMillis();
35 System.out.println("Timp = "+(t2-t1));
36 }// main(...)
37
38 static int nrDiv(int nr)
39 {
40 int d;
41 int nrd;
42 int p=1;
43
44 d=2;
45 nrd=0;
46 while(nr%d==0) { nrd++; nr=nr/d; }
47 p=p*(nrd+1);
48
49 d=3;
50 while(d*d<=nr)
51 {
52 nrd=0;
53 while(nr%d==0) { nrd++; nr=nr/d; }
54 p=p*(nrd+1);
55
56 d=d+2;
57 }
58
59 if(nr>1) p*=2;
60 return p;
61 }// nrDiv(...)
62 }// class
Capitolul 17

OJI 2004 clasa a IX-a

17.1 Expresie
Se d  un ³ir de n numere naturale nenule x1 , x2 , ..., xn ³i un num r natural m.
Cerinµ 
Ó
S  se verice dac  valoarea expresiei m x1 ...xn exte un num r natural.
În caz armativ s  se a³eze acest num r descompus în factori primi.
Date de intrare
În ³ierul exp.in se a  pe prima linie m, pe linia a doua n, iar pe linia a treia numerele
x1 , x2 , ..., xn separate între ele prin câte un spaµiu.
Date de ie³ire
În ³ierul exp.out se va scrie pe prima linie cifra 0, dac  valoarea expresiei nu este un num r
natural, respectiv 1 dac  este un num r natural. Dac  valoarea expresiei este un num r natural pe
urm toarele linii se vor scrie perechi de forma p e (p este factor prim care apare în descompunere
la puterea e ' 1). Aceste perechi se vor scrie în ordine cresc toare dup  primul num r (adic  p).
Restricµii ³i preciz ri
a n - num r natural nenul $ 5000
a xi - num r natural nenul $ 30000, i " 1, 2, ..., n
a m - poate  una din cifrele 2, 3, 4
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

17.1.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 14/4


Rezolvarea problemei const  în descompunerea în factori primi a numerelor date ³i prelu-
crarea acestor factori.
Concret, se poate p stra un vector P de la 1 la 30000, în care Pi este 0 dac  i nu este prim,
respectiv puterea la care apare i în descompunerea în factori primi a produsului dac  i este prim.
Valorile din P nu pot dep ³i n log2 30000, deci vor  mai mici sau egale cu 5000 14 70.000;
astfel, elementele vectorului vor  de tip longint.
Fiecare din cele n numere este descompus în factori primi. În momentul în care se determin 
un factor prim F al unui num r din cele date, se incrementeaz  PF cu puterea la care F apare
în descompunerea num rului respectiv. Nu este necesar  memorarea separat  a descompunerii
ec rui num r.
În nal, pentru ecare element din P se veric  dac  este multiplu de m.

257
CAPITOLUL 17. OJI 2004 CLASA A IX-A 258

Dac  toate elementele îndeplinesc aceast  condiµie, expresia este un num r natural ³i se trece
la a³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, vericarea dac  expresia este un num r natural ³i a³area au ordinul de com-
plexitate O n.
În concluzie, ordinul de complexitate
Ó al algoritmului de rezolvare a acestei probleme este O n,
dac  facem abstracµie de constanta 30000.

17.1.2 Cod surs 

Listing 17.1.1: EXP.PAS


1 Program Exp;
2
3 type vector = array[1..6000] of integer;
4
5 var x, p, y, f: vector;
6 m, n, dp, i, k, j: integer;
7 sw: boolean;
8 ff: text;
9
10 procedure cit;
11 var i: integer;
12 f: text;
13 begin
14 assign(f, ’exp.in’); reset(f);
15 readln(f, m);
16 readln(f, n);
17 for i:=1 to n do
18 read(f, x[i]);
19 close(f);
20 end;
21
22 function prim(k: integer): boolean;
23 var i: integer;
24 begin
25 prim := true;
26 for i := 2 to trunc(sqrt(k)) do
27 if k mod i=0 then
28 begin
29 prim := false;
30 exit;
31 end;
32 end;
33
34 procedure prime;
35 var i, j: integer;
36 begin
37 p[1] := 2;
38 p[2] := 3;
39 dp := 2;
40 i := 5;
41 while i<30000 do
42 begin
43 if prim(i) then
44 begin
45 inc(dp);
46 p[dp] := i;
47 end;
48 i := i+2;
CAPITOLUL 17. OJI 2004 CLASA A IX-A 259

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.

17.1.3 Rezolvare detaliat 

Prima variant :

Listing 17.1.2: Expresie1.java


1 import java.io.*;
2 class Expresie1
3 {
4 static int[] p=new int[30000];
5 static int m,n;
6
7 public static void main(String[] args) throws IOException
8 {
9 int i;
10 StreamTokenizer st=new StreamTokenizer(
11 new BufferedReader(new FileReader("expresie.in")));
12 st.nextToken(); m=(int)st.nval;
13 st.nextToken(); n=(int)st.nval;
14 int[] x=new int[n+1];
15 for(i=1;i<=n;i++) { st.nextToken(); x[i]=(int)st.nval; }
16 for(i=1;i<=n;i++) descfact(x[i]);
17 int ok=1;
18 for (i=2;i<30000;i++)
19 if (p[i]%m==0) p[i]=p[i]/m;
20 else { ok=0; break; }
21 PrintWriter out=new PrintWriter(
22 new BufferedWriter(new FileWriter("expresie.out")));
23 if(ok==0) out.println(0);
24 else
CAPITOLUL 17. OJI 2004 CLASA A IX-A 260

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 :

Listing 17.1.3: Expresie2.java


1 import java.io.*;
2 class Expresie2 // pentru valori < 30.000 sunt 3245 prime 2...29.989
3 { // sunt ~10% numere prime (+/- 5%)
4 static final int valmax=30000; // valoare maxima pentru x_i
5 static int m,n,nnp; // nnp=nr numere prime < valmax
6 static int[] x;
7 static int[] p=new int[3246]; // numere prime
8 static int[] e=new int[3246]; // exponenti corespunzatori
9 static boolean ok;
10
11 public static void main(String[] args) throws IOException
12 {
13 int i,j;
14 StreamTokenizer st=new StreamTokenizer(
15 new BufferedReader(
16 new FileReader("expresie.in")));
17 PrintWriter out=new PrintWriter(
18 new BufferedWriter(
19 new FileWriter("expresie.out")));
20 st.nextToken(); m=(int)st.nval;
21 st.nextToken(); n=(int)st.nval;
22 x=new int[n+1];
23 for(i=1;i<=n;i++) { st.nextToken(); x[i]=(int)st.nval; }
24 sol();
25 if(!ok) out.println(0);
26 else
27 {
28 out.println(1);
29 for(i=1;i<=nnp;i++)
30 if(e[i]>0) out.println(p[i]+" "+e[i]);
31 }
32 out.close();
33 }// main
34
35 static void sol()
36 {
37 int i,j;
38 prime(); // afisv(p);
39 for(i=1;i<=nnp;i++) e[i]=0; // era initializat implicit !!!
40 for(i=1;i<=n;i++)
41 {
42 j=1; // pozitie in lista numerelor prime p[]
43 while(x[i]!=1)
44 {
45 while(x[i]%p[j]==0) { e[j]++; x[i]/=p[j]; }
46 j++;
47 }
CAPITOLUL 17. OJI 2004 CLASA A IX-A 261

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 :

Listing 17.1.4: Expresie3.java


1 import java.io.*;
2 class Expresie3 // pentru "peste 5.000 de factori"
3 {
4 static int m,n,nf=0,nfc;
5 static int[] x;
6 static int[] f={}, e={};
7 static int[] fc=new int[6]; // 2*3*5*7*11*13=30030 > 30000 pentru x[i]
8 static int[] ec=new int[6]; // exponenti curenti corespunzatori
9 static boolean ok;
10
11 public static void main(String[] args) throws IOException
12 {
13 int i,j;
14 StreamTokenizer st=new StreamTokenizer(
15 new BufferedReader(
16 new FileReader("Expresie.in")));
17 PrintWriter out=new PrintWriter(
18 new BufferedWriter(
19 new FileWriter("Expresie.out")));
20 st.nextToken(); m=(int)st.nval;
21 st.nextToken(); n=(int)st.nval;
22 x=new int[n];
23 for(i=0;i<n;i++) { st.nextToken(); x[i]=(int)st.nval; }
24 sol();
25 if(!ok) out.println(0);
26 else
27 {
28 out.println(1);
29 for(i=0;i<nf;i++) out.println(f[i]+" "+e[i]);
30 }
31 out.close();
32 }// main
33
34 static void sol()
35 {
36 int i;
37 for(i=0;i<n;i++)
38 {
39 if(x[i]==1) continue;
40 descfact(x[i]); interclasare();
41 }
42 ok=true;
43 for(i=0;i<nf;i++)
44 if(e[i]%m==0) e[i]=e[i]/m; else {ok=false; break;}
45 }
46
CAPITOLUL 17. OJI 2004 CLASA A IX-A 262

47 static void interclasare() // (f cu ff) SI (e cu ee)


48 {
49 int i;
50 if(nf==0)
51 {
52 int[] ff=new int[nfc], ee=new int[nfc];
53 for(i=0;i<nfc;i++) {ff[i]=fc[i]; ee[i]=ec[i];}
54 f=ff; e=ee; nf=nfc; return;
55 }
56 int j, nft=nf+nfc,n;
57 int[] ff=new int[nft], ee=new int[nft];
58 i=0; j=0; n=0; // primul indice in care incarc este 0=zero !!!
59 while((i<nf)&&(j<nfc))
60 {
61 n++;
62 if(f[i]<fc[j])
63 {
64 ff[n-1]=f[i];
65 ee[n-1]=e[i];
66 i++;
67 }
68 else if(f[i]>fc[j])
69 {
70 ff[n-1]=fc[j];
71 ee[n-1]=ec[j];
72 j++;
73 }
74 else
75 {
76 ff[n-1]=f[i];
77 ee[n-1]=e[i]+ec[j];
78 i++; j++;
79 }
80 }
81 while(i<nf) {n++; ff[n-1]=f[i]; ee[n-1]=e[i]; i++;}
82 while(j<nfc) {n++; ff[n-1]=fc[j]; ee[n-1]=ec[j]; j++;}
83 if(n==nft) {f=ff; e=ee; nf=n;}
84 else
85 {
86 int[] fff=new int[n], eee=new int[n];
87 for(i=0;i<n;i++) {fff[i]=ff[i]; eee[i]=ee[i];}
88 f=fff; e=eee; nf=n;}
89 }
90
91 static void descfact(int nr)
92 {
93 // if((nr==0)||(nr==1)) return nr;
94 nfc=0;
95 int d=2;
96 if(nr%d==0)
97 {
98 nfc++; fc[nfc-1]=d; ec[nfc-1]=0;
99 while(nr%d==0) {ec[nfc-1]++; nr=nr/d;}}
100 d=3;
101 while((d*d<=nr)&&(nr!=1))
102 {
103 if(nr%d==0)
104 {
105 nfc++;
106 fc[nfc-1]=d;
107 ec[nfc-1]=0;
108 while(nr%d==0) {ec[nfc-1]++; nr=nr/d;}
109 }
110 d=d+2;
111 }
112 if(nr!=1) {nfc++; fc[nfc-1]=nr; ec[nfc-1]=1;}
113 }// descfact
114 }//class

17.2 Reactivi
Într-un laborator de analize chimice se utilizeaz  N reactivi.
CAPITOLUL 17. OJI 2004 CLASA A IX-A 263

Se ³tie c , pentru a evita accidentele sau deprecierea reactivilor, ace³tia trebuie s  e stocaµi
în condiµii de mediu speciale. Mai exact, pentru ecare reactiv x, se precizeaz  intervalul de
temperatur  minx , maxx  în care trebuie s  se încadreze temperatura de stocare a acestuia.
Reactivii vor  plasaµi în frigidere.
Orice frigider are un dispozitiv cu ajutorul c ruia putem stabili temperatura (constant ) care
va  î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 ecare dintre urm toarele N linii se a  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.

Restricµii ³i preciz ri
a 1&N & 8000
a 100 & minx & maxx & 100 (numere întregi, reprezentând grade Celsius), pentru orice x de
la 1 la N
a un frigider poate conµine un num r nelimitat de reactivi

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

17.2.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 14/4


Problema se poate rezolva prin metoda greedy.
O variant  mai explicit  a enunµului este urm toarea:
"Se consider  N intervale pe o ax . S  se aleag  un num r minim de puncte
astfel încât ecare interval s  conµin  cel puµin unul dintre punctele alese."

Facem o prim  observaµie: pentru orice soluµie optim  exist  o soluµie cu acela³i num r de
puncte (frigidere), în care ecare punct s  e sfâr³itul unui interval. Aceasta se poate obµine
mutând ecare punct spre dreapta, pân  când ar ajunge la sfâr³itul intervalului care se termin 
cel mai repede, dintre intervalele care îl conµin. Se observ  c  noua soluµie respect  restricµiile din
enunµ.
În continuare ne vom concentra pe g sirea unei soluµii de acest tip.
Sort m reactivii dup  sfâr³itul intervalului. Pentru intervalul care se termin  cel mai repede,
alegem ultimul punct al s u ca temperatur  a unui frigider. Se observ  c  aceast  alegere este
cea mai bun , dintre toate alegerile unui punct în intervalul respectiv, în sensul c  mulµimea
intervalelor care conµin punctul este mai mare (conform relaµiei de incluziune), decât mulµimea
corespunz toare oric rei alte alegeri. Acest fapt este adev rat, deoarece mutarea punctului mai la
stânga nu duce la selectarea unor noi intervale.
Intervalele care conµin punctul respectiv sunt eliminate (reactivii corespunz tori pot  plasaµi
într-un frigider), iar procesul continu  cu intervalele r mase, în acela³i mod.
Analiza complexit µii
CAPITOLUL 17. OJI 2004 CLASA A IX-A 264

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 ecare 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 .
A³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 ind O N D.

17.2.2 Cod surs 

Listing 17.2.1: REACT_QS.PAS


1 Program Reactivi;
2 {NU l-am dat pe asta ca sursa deoarece intr-a IX-a INCA nu se face QuickSort}
3 Type Frigider = Record
4 min, max: ShortInt
5 End;
6 Var f : Array[1..10000] Of Frigider;
7 r : Array[1..10000] Of Frigider;
8 Fisier : Text;
9 Cate, i, j, N: Integer;
10 min, max : ShortInt;
11
12 Function Maxim(x, y: ShortInt): ShortInt;
13 Begin
14 If x>y Then
15 Maxim := x
16 Else
17 Maxim := y
18 End;
19
20 Function Minim(x, y: ShortInt): ShortInt;
21 Begin
22 If x<y Then
23 Minim := x
24 Else
25 Minim := y
26 End;
27
28 Function Cauta(min, max: ShortInt): Integer;
29 Var i: Integer;
30 Begin
31 Cauta := -1;
32 For i := 1 To Cate Do
33 Begin
34 If (f[i].max>=min) And (f[i].max<=max) Then
35 Begin Cauta := i; Break End
36 Else
37 If (f[i].min>=min) And (f[i].min<=max) Then
38 Begin Cauta := i; Break End
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
CAPITOLUL 17. OJI 2004 CLASA A IX-A 265

54 procedure QuickSortCresc(Lo, Hi: Integer);


55
56 procedure Sort(Stanga, Dreapta: Integer);
57 var i, j, x: integer;
58 y: Frigider;
59 begin
60 i := Stanga; j := Dreapta; x := r[(Stanga+Dreapta) DIV 2].min;
61 repeat
62 while r[i].min < x do i := i + 1;
63 while x < r[j].min do j := j - 1;
64 if i <= j then
65 begin
66 y := r[i]; r[i] := r[j]; r[j] := y;
67 i := i + 1; j := j - 1;
68 end;
69 until i > j;
70 if Stanga < j then Sort(Stanga, j);
71 if i < Dreapta then Sort(i, Dreapta);
72 end;
73
74 begin {QuickSort};
75 Sort(Lo,Hi);
76 end;
77
78 procedure QuickSortDescresc(Lo, Hi: Integer);
79
80 procedure Sort(Stanga, Dreapta: Integer);
81 var i, j, x: integer;
82 y: Frigider;
83 begin
84 i := Stanga; j := Dreapta; x := r[(Stanga+Dreapta) DIV 2].max;
85 repeat
86 while r[i].max > x do i := i + 1;
87 while x > r[j].max do j := j - 1;
88 if i <= j then
89 begin
90 y := r[i]; r[i] := r[j]; r[j] := y;
91 i := i + 1; j := j - 1;
92 end;
93 until i > j;
94 if Stanga < j then Sort(Stanga, j);
95 if i < Dreapta then Sort(i, Dreapta);
96 end;
97
98 begin {QuickSort};
99 Sort(Lo,Hi);
100 end;
101
102 Begin
103 Assign(Fisier, ’reactivi.in’); Reset(Fisier);
104 ReadLn(Fisier, N);
105 For i := 1 To N Do
106 ReadLn(Fisier, r[i].min, r[i].max);
107 Close(Fisier);
108
109 QuickSortCresc(1, N);
110
111 i := 1; j := i+1;
112 While j<=N Do
113 Begin
114 While (r[j].min=r[i].min) And (j<=N) Do Inc(j);
115 QuickSortDescresc(i, j-1);
116 i := j; j := i+1
117 End;
118
119 f[1].min := r[1].min; f[1].max := r[1].max; Cate := 1;
120 For i := 2 To N Do
121 Begin
122 min := r[i].min; max := r[i].max;
123 j := Cauta(min, max);
124 If j>0 Then
125 Intersectie(j, min, max)
126 Else
127 Begin
128 Inc(Cate);
129 f[Cate].min := min;
CAPITOLUL 17. OJI 2004 CLASA A IX-A 266

130 f[Cate].max := max


131 End
132 End;
133 Assign(Fisier, ’reactivi.out’); ReWrite(Fisier);
134 WriteLn(Fisier, Cate);
135 Close(Fisier)
136 End.

Listing 17.2.2: REACTIVI.C


1 #include <stdio.h>
2
3 struct Frigider
4 {
5 char min;
6 char max;
7 } f[10001], r[10001];
8
9 int Cate, i, j, N;
10 char min, max;
11
12 FILE* Fisier;
13
14 char Maxim(char x, char y)
15 {
16 if (x>y) return x;
17 else return y;
18 }
19
20 char Minim(char x, char y)
21 {
22 if (x<y) return x;
23 else return y;
24 }
25
26 int Cauta(char min, char max)
27 {
28 int i, Unde;
29 for (i=1; i<= Cate; i++)
30 {
31 if ((f[i].max>=min) && (f[i].max<=max)) Unde=i;//am gasit frigiderul i
32 if ((f[i].min>=min) && (f[i].min<=max)) Unde=i;//am gasit frigiderul i
33 if ((f[i].min<=min) && (f[i].max>=max)) Unde=i;//am gasit frigiderul i
34 if ((f[i].max<min) || (f[i].min>max)) Unde=-1; //nu am gasit frigider
35 }
36 return Unde;
37 }
38
39 void Intersectie(int j, char min, char max)
40 {
41 f[j].min=Maxim(min, f[j].min);//modific temperatura minima din frigider
42 f[j].max=Minim(max, f[j].max);//modific temperatura maxima din frigider
43 }
44
45 void Ordoneaza(void)
46 { //sortare prin selectie
47 int i, j, ptmin;
48 char tmax, tmin;
49 for (i=1; i<=N-1; i++)
50 {
51 tmin=r[i].min; tmax=r[i].max; ptmin=i;
52 for (j=i+1; j<=N; j++)
53 if (r[j].min<tmin) //crescator dupa temperatura minima
54 { tmax=r[j].max; tmin=r[j].min; ptmin=j; }
55 else
56 if (r[j].min==tmin)//pentru temperatura minima egala
57 if (r[j].max>tmax) //descrescator dupa cea maxima
58 { tmax=r[j].max; tmin=r[j].min; ptmin=j; }
59 r[ptmin].min=r[i].min; r[ptmin].max=r[i].max;
60 r[i].min=tmin; r[i].max=tmax;
61 }
62 }
63
64 int main()
65 {
CAPITOLUL 17. OJI 2004 CLASA A IX-A 267

66 char x, y;
67 Fisier=fopen("reactivi.in", "rt");
68 fscanf(Fisier, "%d", &N);
69 for (i=1; i<=N; i++)
70 fscanf(Fisier, "%d %d", &r[i].min, &r[i].max, &x, &y);
71 Ordoneaza(); //ordonez intervalele de temperatura
72 //crescator dupa temperatura minima si
73 //descrescator dupa temperatura maxima
74
75 //pun primul reactiv (deci cel cu intervalul cel mai mare)
76 //in primul frigider
77 f[1].min=r[1].min; f[1].max=r[1].max; Cate=1;
78 for (i=2; i<= N; i++) //pentru toate celelalte
79 {
80 min=r[i].min; max=r[i].max;
81 j=Cauta(min, max); //caut un frigider in care a mai fost pus ceva
82 if (j>0) //daca gasesc
83 Intersectie(j, min, max);//ajustez temperatura din frigider
84 else //altfel
85 {
86 Cate++; //"deschid" un frigider nou
87 f[Cate].min=min; //si pun aici reactivul
88 f[Cate].max=max;
89 }
90 }
91
92 fclose(Fisier);
93
94 Fisier=fopen("reactivi.out", "wt");
95 fprintf(Fisier, "%d\n", Cate);
96 fclose(Fisier);
97
98 return 0;
99 }

Listing 17.2.3: reactivp.pas


1 Program Reactivi;
2
3 Type Frigider = Record
4 min, max: ShortInt
5 End;
6 Var f : Array[1..10000] Of Frigider;
7 r : Array[1..10000] Of Frigider;
8 Fisier : Text;
9 Cate, i, j, N: Integer;
10 min, max : ShortInt;
11
12 Function Maxim(x, y: ShortInt): ShortInt;
13 Begin
14 If x>y Then
15 Maxim := x
16 Else
17 Maxim := y
18 End;
19
20 Function Minim(x, y: ShortInt): ShortInt;
21 Begin
22 If x<y Then
23 Minim := x
24 Else
25 Minim := y
26 End;
27
28 Function Cauta(min, max: ShortInt): Integer;
29 Var i: Integer;
30 Begin
31 Cauta := -1;
32 For i := 1 To Cate Do
33 Begin
34 If (f[i].max>=min) And (f[i].max<=max) Then
35 Begin Cauta := i; Break End
36 Else
37 If (f[i].min>=min) And (f[i].min<=max) Then
38 Begin Cauta := i; Break End
CAPITOLUL 17. OJI 2004 CLASA A IX-A 268

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.

17.2.3 Rezolvare detaliat 

Listing 17.2.4: reactivi.java


1 import java.io.*;
2 class Reactivi
3 {
CAPITOLUL 17. OJI 2004 CLASA A IX-A 269

4 static int n; // n=nr reactivi


5 static int ni=0; // ni=nr interschimbari in quickSort
6 static int nf=0; // n=nr frigidere
7 static int[] ngf; // ng=nr grade in frigider
8 static int[] x1,x2; // limite inferioara/superioara
9
10 public static void main(String[] args) throws IOException
11 {
12 int i,j;
13 StreamTokenizer st=new StreamTokenizer(
14 new BufferedReader(new FileReader("Reactivi.in")));
15 PrintWriter out=new PrintWriter(
16 new BufferedWriter(new FileWriter("Reactivi.out")));
17 st.nextToken(); n=(int)st.nval;
18 x1=new int[n+1];
19 x2=new int[n+1];
20 ngf=new int[n+1];
21 for(i=1;i<=n;i++)
22 {
23 st.nextToken(); x1[i]=(int)st.nval;
24 st.nextToken(); x2[i]=(int)st.nval;
25 }
26 sol();
27 out.println(nf);
28 out.close();
29 }// main
30
31 static void sol()
32 {
33 int i;
34 quickSort(1,n);
35 i=1; nf=1; ngf[nf]=x2[i];
36 i++;
37 while(i<n)
38 {
39 while((i<=n) && (x1[i]<=ngf[nf])) i++;
40 if(i<n) ngf[++nf]=x2[i++];
41 }
42 }
43
44 static void quickSort(int p, int u)
45 {
46 int i,j,aux;
47 i=p; j=u;
48 while(i<j)
49 {
50 while((i<j) && ((x2[i]<x2[j])||
51 ((x2[i]==x2[j]) && (x1[i]>=x1[j])))) i++;
52 if(i!=j)
53 {
54 aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
55 aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
56 }
57 while((i<j) && ((x2[i]<x2[j])||
58 ((x2[i]==x2[j]) && (x1[i]>=x1[j])))) j--;
59 if(i!=j)
60 {
61 aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
62 aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
63 }
64 }
65 if(p<i-1) quickSort(p,i-1);
66 if(i+1<u) quickSort(i+1,u);
67 }
68 }
Capitolul 18

OJI 2003 clasa a IX-a

18.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 ec rui
paragraf din text la stânga ³i la dreapta. Pentru aceasta el va trebui s  împart  ecare paragraf
în linii separate de lungime Max (ecare linie terminat  cu Enter).
Împ rµirea se realizeaz  punând num rul maxim posibil de cuvinte pe ecare 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 ecare linie, astfel încât ultimul caracter de pe linie s  e diferit de spaµiu, iar
num rul total de caractere de pe linie s  e egal cu Max. Excepµie face numai ultima linie din
paragraf, care r mâne aliniat  la stânga (cuvintele ind separate printr-un singur spaµiu, chiar
dac  linia nu este plin ).
În general, este puµin probabil ca alinierea s  e 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  e 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
uri
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.

270
CAPITOLUL 18. OJI 2003 CLASA A IX-A 271

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 ecare paragraf r mâne aliniat  la stânga, cuvintele consecutive
ind separate printr-un singur spaµiu.
Timp maxim de executare: 1 secund /test.

18.1.1 Indicaµii de rezolvare

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 a³eaz  aceast  linie. Se continu  prelucrarea
vectorului începând cu poziµia i1  1, ³i a³a mai departe!

18.1.2 Cod surs 

Listing 18.1.1: TEXT.CPP


1 #include <fstream>
2 #include <string.h>
3
4 using namespace std;
5
6 #define InFile "text.in"
7 #define OutFile "text.out"
8 #define NMax 1001
9 #define LgMax 26
10
11 char prg[NMax];
12
13 ifstream fin(InFile);
14 ofstream fout(OutFile);
15
16 char cuv[NMax][LgMax];
17 int Max, nrl, LgLinie;
18 void afiseaza_paragraf(void);
19 void afiseaza_linie(int);
20
21 int main()
22 {
23 fin>>Max;
24 fin.get();
25
26 int gata=0;
27 while (!gata)
28 {
29 fin.getline(prg,NMax);
30 if (!fin.good())
31 gata=1;
32 else
33 afiseaza_paragraf();
34 }
35
36 fin.close();
37 fout.close();
38 return 0;
39 }
40
41 void afiseaza_paragraf()
42 {
43 char *p;
44 int LgCuv;
CAPITOLUL 18. OJI 2003 CLASA A IX-A 272

45 LgLinie=-1;
46 nrl=0;
47 p=strtok(prg," ");
48
49 while (p)
50 {
51 LgCuv=strlen(p);
52 if (LgLinie+LgCuv+1<=Max)
53 {
54 strcpy(cuv[nrl],p);
55 nrl++;
56 LgLinie+=LgCuv+1;
57 }
58 else
59 {
60 afiseaza_linie(0);
61 nrl=1;
62 LgLinie=LgCuv;
63 strcpy(cuv[0],p);
64 }
65
66 p=strtok(NULL," ");
67 }
68
69 afiseaza_linie(1);
70 }
71
72 void afiseaza_linie(int u)
73 {
74 int i, j, cate, rest, nrsp;
75
76 if (u)
77 for (i=0; i<nrl-1; i++)
78 fout<<cuv[i]<<’ ’;
79 else
80 {
81 cate=Max-LgLinie;
82 rest=cate%(nrl-1);
83 nrsp=cate/(nrl-1);
84
85 for (i=0; i<nrl-1;i++)
86 {
87 fout<<cuv[i];
88 for (j=0; j<=nrsp;j++)
89 fout<<’ ’;
90 if (rest)
91 {
92 fout<<’ ’;
93 rest--;
94 }
95 }
96 }
97
98 if (nrl>0)
99 fout<<cuv[nrl-1]<<endl;
100 else
101 fout<<endl;
102 }

18.1.3 Rezolvare detaliat 

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 18. OJI 2003 CLASA A IX-A 273

Sfâr³itul de ³ier (EOF) este marcat prin 1A.


Sfâr³itul de linie (EOL) este marcat prin 0D0A.

Listing 18.1.2: text.java


1 import java.io.*;
2 class Text
3 {
4 static int Max, nc;
5 static String[] s=new String[501]; // cuvintele din paragraf
6 static int[] lgc=new int[501]; // numarul caracterelor cuvintelor
7 static int[] nsp=new int[501]; // numarul spatiilor dupa cuvant
8 static int cs=0, cd=0; // cs=cuvant stanga, cd=cuvant
dreapta
9 static int lglin=0;
10 static PrintWriter out;
11
12 public static void main(String[] args) throws IOException
13 {
14 out=new PrintWriter(new BufferedWriter(new FileWriter("text.out")));
15 StreamTokenizer st=new StreamTokenizer(
16 new BufferedReader(new FileReader("text.in")));
17 st.eolIsSignificant(true);
18
19 st.nextToken(); Max=(int)st.nval;
20 st.nextToken(); // preia EOL-ul existent dupa Max
21 while(st.nextToken()!=StreamTokenizer.TT_EOF)
22 {
23 nc=0;
24 do
25 {
26 s[++nc]=st.sval.toString();
27 } while(st.nextToken()!=StreamTokenizer.TT_EOL);
28 rezolva();
29 }
30 out.close();
31 }
32
33 static void rezolva() throws IOException
34 {
35 cs=0; // primul cuvant din linie (din stanga)
36 cd=0; // ultimul cuvant din linie (din stanga)
37 while(cd<nc)
38 {
39 cs=cd+1;
40 cd=cs;
41 lglin=s[cs].length();
42 while((cd+1<=nc)&&(lglin+1+s[cd+1].length()<=Max))
43 {
44 cd++;
45 lglin=lglin+1+s[cd].length();
46 }
47 if(cd<nc) unRand(); else ultimulRand();
48 }
49 }
50
51 static void unRand() throws IOException
52 {
53 int i,j;
54 int ntsr; // ntsr=nr total de spatii ramase de distribuit
55 int nsr; // nsr=nr spatii ramase de distribuit pentru primele cuvinte
56 int nsf; // nr spatii de adaugat dupa fiecare cuvant cs ... cd-1
57
58 ntsr=Max-lglin;
59 if(cs!=cd)
60 {
61 nsf=ntsr/(cd-cs);
62 nsr=ntsr%(cd-cs);
63 for(i=cs;i<cd;i++) nsp[i]=1+nsf;
64 for(i=1;i<=nsr;i++) nsp[cs+i-1]++;
65 for(i=cs;i<=cd-1;i++)
66 {
67 out.print(s[i]);
68 for(j=1;j<=nsp[i];j++) out.print(" ");
69 }
70 out.println(s[cd]);
CAPITOLUL 18. OJI 2003 CLASA A IX-A 274

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 }

18.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 ecare 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 a  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 a  elementele primei submulµimi, iar pe linia a treia se a  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

18.2.1 Indicaµii de rezolvare

Num rul mic al numerelor (numai 10 numere distincte) permite generarea tuturor submulµi-
milor, vericarea condiµiilor din problem  pentru ecare pereche de submulµimi ³i determinarea
informaµiilor cerute.
CAPITOLUL 18. OJI 2003 CLASA A IX-A 275

18.2.2 Cod surs 

Listing 18.2.1: numere.cpp


1 #include <fstream>
2 #include <stdlib.h>
3
4 using namespace std;
5
6 int a[11], s1max[11], s2max[11], solutii=0, Smax=0, UzMax=0;
7
8 ifstream f("numere.in");
9 ofstream g("numere.out");
10
11 void citire()
12 {
13 int i;
14 for (i=1; i<=10; i++) f>>a[i];
15 f.close();
16 }
17
18 void rezolva()
19 {
20 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};
21 int i, ii, j, k, x, gasit, si, sj, jj, disjuncti;
22 int cati1;
23
24 for(ii=1; ii<=1022; ii++)
25 {
26 i=10;
27 while(vi[i]) vi[i--]=0; //adun 1 la vi
28 vi[i]=1;
29
30 si=0;
31 for (i=1; i<=10; i++)
32 if (vi[i]) si+=a[i];
33 for (i=1; i<=10; i++) vj[i]=vi[i];
34 for (jj=ii+1; jj<=1022; jj++)
35 {
36 j=10;
37 while(vj[j]) vj[j--]=0; //adun 1 la vj
38 vj[j]=1;
39
40 sj=0;
41 for(j=1; j<=10; j++)
42 if(vj[j])
43 sj+=a[j];
44
45 if(si==sj)
46 {
47 disjuncti=1;
48
49 for(k=1; k<=10; k++)
50 if((vi[k]+vj[k])==2)
51 disjuncti=0;
52
53 if(disjuncti)
54 {
55 solutii++;
56 cati1=0;
57 for (i=1; i<=10; i++)
58 cati1+=(vi[i]+vj[i]);
59
60 if (cati1>UzMax)
61 if (si>Smax)
62 {
63 UzMax=cati1;
64 Smax=si;
65 for (i=1; i<=10; i++)
66 s1max[i]=vi[i];
67 for (i=1; i<=10; i++)
68 s2max[i]=vj[i];
69 }
70 }
CAPITOLUL 18. OJI 2003 CLASA A IX-A 276

71 }
72 }
73 }
74 }
75
76 void afisare()
77 {
78 int i;
79
80 g<<solutii<<" "<<Smax<<endl;
81
82 for(i=1; i<=10; i++)
83 if (s1max[i])
84 g<<a[i]<<’ ’;
85 g<<endl;
86
87 for(i=1; i<=10; i++)
88 if (s2max[i])
89 g<<a[i]<<’ ’;
90 g<<endl;
91 g.close();
92 }
93
94 int main()
95 {
96 citire();
97 rezolva();
98 afisare();
99 return 0;
100 }

18.2.3 Rezolvare detaliat 

Listing 18.2.2: numere.java


1 import java.io.*;
2 class Numere
3 {
4 static int[] x=new int[10];
5 static PrintWriter out;
6
7 public static void main(String[] args) throws IOException
8 {
9 int i, ia, ib, nsol=0, smax=-1, iamax=123,ibmax=123, sumaia=-1;
10 long t1,t2;
11 t1=System.currentTimeMillis();
12 StreamTokenizer st=new StreamTokenizer(
13 new BufferedReader(new FileReader("numere.in")));
14 out=new PrintWriter(new BufferedWriter(new FileWriter("numere.out")));
15 for(i=0;i<10;i++) {st.nextToken(); x[i]=(int)st.nval;}
16 for(ia=1;ia<=1022;ia++)
17 for(ib=ia+1;ib<=1022;ib++) // fara ordine intre A si B
18 if((ia&ib)==0)
19 {
20 sumaia=suma(ia);
21 if(sumaia==suma(ib))
22 {
23 nsol++;
24 if(sumaia>smax)
25 {
26 smax=sumaia;
27 iamax=ia;
28 ibmax=ib;
29 }
30 }
31 }
32 out.println(nsol+" "+smax);
33 afis(iamax);
34 afis(ibmax);
35 out.close();
36 t2=System.currentTimeMillis();
37 System.out.println(t2-t1);
CAPITOLUL 18. OJI 2003 CLASA A IX-A 277

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 19

OJI 2002 clasa a IX-a

19.1 Poarta
Se consider  harta universului ca ind o matrice cu 250 de linii ³i 250 de coloane. În ecare
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 a  î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 ecare echipaj ind precizate po-
ziµia iniµial  ³i poziµia nal , determinaµi num rul minim de deplas ri necesare pentru ca toate
echipajele s  ajung  din poziµia iniµial  în cea nal .
Datele de intrare
Se citesc din ³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
nale a aceluia³i echipaj (linie coloan ).
Datele de ie³ire
Pe prima linie a ³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 nale 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 nale ale celor p echipaje sunt distincte dou  câte dou .
Exemplu
poarta.in poarta.out
3 4
1234
6539
3412

278
CAPITOLUL 19. OJI 2002 CLASA A IX-A 279

Figura 19.1: Poarta

Timp maxim de executare: 1 secund /test

19.1.1 Indicaµii de rezolvare

Fie NrStationare num rul echipajelor staµionare (care au poziµiile iniµiale ³i nale 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 nal  a echipajului i coincide cu
poziµia iniµial  a echipajului j .
Atunci NrMinDeplasari=p+NrCircuite-NrStationare.

19.1.2 *Cod surs 

19.1.3 *Rezolvare detaliat 

Listing 19.1.1: poarta.java


1 import java.io.*;
2 class Poarta
3 {
4 static int p,nmd,nc=0,ns=0;
5 static int[] xi,yi,xf,yf;
6 static boolean[] ea; // EsteAnalizat deja
7
8 public static void main(String[] args) throws IOException
9 {
10 StreamTokenizer st=new StreamTokenizer(
11 new BufferedReader(new FileReader("poarta.in")));
12
13 st.nextToken(); p=(int)st.nval;
14 xi=new int[p+1];
15 yi=new int[p+1];
16 xf=new int[p+1];
17 yf=new int[p+1];
18 ea=new boolean[p+1]; // implicit este false
19 int i;
20 for(i=1;i<=p;i++)
21 {
22 st.nextToken(); xi[i]=(int)st.nval;
23 st.nextToken(); yi[i]=(int)st.nval;
24 st.nextToken(); xf[i]=(int)st.nval;
25 st.nextToken(); yf[i]=(int)st.nval;
26 }
27
28 for(i=1;i<=p;i++)
29 {
30 if(ea[i]) continue;
31 if((xf[i]==xi[i])&&(yf[i]==yi[i])) { ea[i]=true; ns++;}
32 else if(circuit(i)) nc++;
33 }
34 PrintWriter out=new PrintWriter(
35 new BufferedWriter(new FileWriter("poarta.out")));
36 nmd=p+nc-ns;
CAPITOLUL 19. OJI 2002 CLASA A IX-A 280

37 System.out.println(p+" "+nc+" "+ns+" "+nmd);


38 out.print(nmd);
39 out.close();
40 }
41
42 static boolean circuit(int i)
43 {
44 int j=succesor(i);
45 while((j!=-1)&&(j!=i))
46 {
47 ea[j]=true;
48 j=succesor(j);
49 }
50 if(j==i) return true; else return false;
51 }
52
53 static int succesor(int j) // j --> k
54 {
55 int k;
56 for(k=1;k<=p;k++)
57 if((xf[j]==xi[k])&&(yf[j]==yi[k])) return k;
58 return -1;
59 }
60 }

19.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 ecare c m ruµ , câte n numere pe ecare linie,
separate prin spaµii. Toate valorile din ³ier sunt numere naturale între 1 ³i 100.
Datele de ie³ire
În ³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 1 1
3412 2 1
2 2
1 2
1 3
1 4
2 4
Explicaµie
CAPITOLUL 19. OJI 2002 CLASA A IX-A 281

Figura 19.2: Mouse

Timp maxim de executare: 1 secund /test

19.2.1 Indicaµii de rezolvare

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 a  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.

19.2.2 *Cod surs 

19.2.3 Rezolvare detaliat 

Listing 19.2.1: mouce.java


1 import java.io.*;
2 class Mouse
3 {
4 static int m,n,imin,jmin,min,s;
5 static int [][]a;
6 static PrintWriter out;
7
8 public static void main(String[] args) throws IOException
9 {
10 int i,j;
11 StreamTokenizer st=new StreamTokenizer(
12 new BufferedReader(new FileReader("mouse.in")));
13 out=new PrintWriter(new BufferedWriter(new FileWriter("mouse.out")));
14
15 st.nextToken();m=(int)st.nval;
16 st.nextToken();n=(int)st.nval;
17 a=new int[m+1][n+1];
18
19 for(i=1;i<=m;i++)
20 for(j=1;j<=n;j++) {st.nextToken(); a[i][j]=(int)st.nval;}
21
22 s=0;
23 for(i=1;i<=m;i++)
24 for(j=1;j<=n;j++) s=s+a[i][j];
25
26 if(m%2==1) mimpar();
27 else if(n%2==1) nimpar();
28 else mnpare();
29 out.close();
30 }//main
31
32 static void mimpar()
33 {
34 int i,j;
35 out.println(m*n+" "+s);
CAPITOLUL 19. OJI 2002 CLASA A IX-A 282

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 19. OJI 2002 CLASA A IX-A 283

112 j++;
113 }
114 }
115 }//class
Partea II

ONI - Olimpiada naµional  de


informatic 

284
Capitolul 20

ONI 2020

20.1 ***
Problema 1 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

Exemple:

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

20.1.1 *Indicaµii de rezolvare

20.1.2 *Cod surs 

20.1.3 *Rezolvare detaliat 

20.2 ***
Problema 2 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

285
CAPITOLUL 20. ONI 2020 286

Exemple:
CAPITOLUL 20. ONI 2020 287

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

20.2.1 *Indicaµii de rezolvare

20.2.2 *Cod surs 

20.2.3 *Rezolvare detaliat 

20.3 ***
Problema 3 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

Exemple:

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

20.3.1 *Indicaµii de rezolvare

20.3.2 *Cod surs 

20.3.3 *Rezolvare detaliat 

20.4 ***
Problema 4 - ... 100 de puncte

Cerinµe
CAPITOLUL 20. ONI 2020 288

Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

Exemple:

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

20.4.1 *Indicaµii de rezolvare

20.4.2 *Cod surs 

20.4.3 *Rezolvare detaliat 

20.5 ***
Problema 5 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

Exemple:

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

20.5.1 *Indicaµii de rezolvare

20.5.2 *Cod surs 


CAPITOLUL 20. ONI 2020 289

20.5.3 *Rezolvare detaliat 

20.6 ***
Problema 6 - ... 100 de puncte

Cerinµe
Date de intrare
Date de ie³ire
Restricµii ³i preciz ri

Exemple:

***.in ***.out Explicaµii

Timp maxim de executare/test: 0.5 secunde pe Windows, 0.5 secunde pe Linux


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

20.6.1 *Indicaµii de rezolvare

20.6.2 *Cod surs 

20.6.3 *Rezolvare detaliat 


Capitolul 21

ONI 2019

21.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
nalizarea 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 modic .

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 ³ierul amat.in, care are urm toarea structur :
a pe prima linie se a  num rul natural C , care poate  egal cu 1 sau 2, în funcµie de cerinµa
ce trebuie rezolvat ;
a pe linia urm toare se a  dou  numerele naturale N ³i M cu semnicaµia din enunµ;
a pe urm toarele N linii se g sesc elementele matricei A.
a dac  C 2 atunci ³ierul de intrare mai conµine:
- pe linia N  2 numerele naturale Q K cu semnicaµiile din enunµ;
- pe urm toarele Q linii descrierea submatricelor asupra c rora se efectueaz  operaµii de
upgrade de forma: x1 y1 x2 y2 V

Date de ie³ire
Datele de ie³ire se vor scrie în ³ierul amat.out, astfel:
Dac  C 1 se vor scrie, separate prin spaµiu patru numere naturale nenule x1 y1 x2 y2 ce
reprezint  coordonatele colµului stânga-sus, respectiv colµului dreapta-jos unde este plasat  piesa
cu num r maxim de elemente înainte de upgrade. Dac  exist  mai multe astfel de piese, atunci
vor  scrise coordonatele piesei pentru care coordonatele colµului stânga-sus are valoarea liniei cea
mai mare, iar la linii egale, se va alege piesa cu coordonata coloanei cea mai mare.
Dac  C 2 se va scrie num rul natural nenul N R ce reprezint  num rului minim de operaµii
de upgrade dup  care toate elementele matricei au valoarea mai mare sau egal  cu K .

290
CAPITOLUL 21. ONI 2019 291

Restricµii ³i preciz ri
a 2 & N, M & 1000; 1 & Q & 100000; 1 & V & 1000
a 1000 & elementele matricei A înainte de upgrade & 1000
a Operaµiile de upgrade se efectueaz  obligatoriu în ordinea citirii
a Pentru teste în valoare de 30 de puncte, C 1
a Pentru teste în valoare de 30 de puncte, C 2 ³i N, M, Q & 250
a Pentru teste în valoare de 50 de puncte, C 2 ³i Q & 4000
a Pentru teste în valoare de 70 de puncte, C 2.

Exemple

Figura 21.1: amat

amat.in amat.out Explicaµii


2 2 Se rezolv  cerinµa 2.
4 6 Matricea iniµial  construit  este cea prezentat  mai sus.
1 1 1 3 2 2 Dorel efectueaz  3 operaµii de upgrade.
1 1 1 3 2 2 Matricea obµinut  dup  efectuarea primului upgrade:
6 4 4 4 2 2 666322
6 4 4 4 5 7 666322
3 6 11 9 9 4 2 2
1 1 335 644457
1 2 465 Matricea obµinut  dup  efectuarea celui de-al doilea upgrade:
4 1 431 6 11 11 8 7 7
6 11 11 8 7 7
11 14 14 9 7 7
6 9 9 9 10 12
Matricea obµinut  dup  efectuarea celui de-al treilea upgrade:
6 11 11 8 7 7
6 11 11 8 7 7
11 14 14 9 7 7
7 10 10 9 10 12
La nal tuturor operaµiilor de upgrade, matricea are toate valorile
mai mari sau egale cu 6.
Se observ  c  sunt suciente primele dou  operaµii de upgrade pentru
c  toate elementele matricei s  e mai mari sau egale cu 6.

Timp maxim de executare/test: Windows: 1.5 secunde, Linux: 0.5 secunde


Memorie: total 64 MB
Dimensiune maxim  a sursei: 10 KB

21.1.1 Indicaµii de rezolvare


Prof. Eugen Nodea - Colegiul Naµional Tudor Vladimirescu, Tg-Jiu

Soluµie cerinµa 1 30p


CAPITOLUL 21. ONI 2019 292

Folosim un vector de frecvenµe. Cum indexarea vectorului pleac  de la 0, pentru determinarea


frecvenµei de apariµii a elementelor negative vom deplasa intervalul valorilor de intrare:
1000, 1000 % 0, 2000;
Complexitatea acestei soluµii este O N M  ³i ar trebui s  obµin  30 de puncte.
Soluµie cerinµa 2 20p
Pentru ecare operaµie în parte, vom aduna valoarea operaµiei pe ecare element al submatricei.
La ecare pas, veric m dac  exist  un element din matrice strict mai mic decât K . În caz contrar,
ne oprim.
Aceast  soluµie are complexitate O N M Q ³i ar trebui s  obµin  minimum 20 de puncte.
Soluµie cerinµa 2 50p
Deoarece valorile matricei pot doar s  creasc  dup  ecare operaµie, se observ  c  r spunsul
poate  g sit folosind c utare binar .
Pentru a verica dac , dup  un num r de operaµii, valorile matricei sunt toate mai mari sau
egale cu K , vom calcula efectiv matricea dup  simularea operaµiilor.
S  consider m ecare linie independent. Fiecare operaµie de upgrade va consta în O(N) operaµii
de ad ugare de interval pentru ecare vector linie. Pentru a simula operaµiile de ad ugare mai
rapid, ne putem folosi de ideea c  trebuie doar s  a m valorile nale.
S  consider m o matrice B unde B i, 1 A i, 1 ³i B i, j  A i, j   A i, j  1. O operaµie
de ad ugare cu valoarea x pe intervalul a, b pe vectorul-linie A i, ˜ se poate simula rapid pe
matricea B prin operaµiile B i, a x ³i B i, b  1 x. La nal, putem reconstitui matricea
A calculând sumele parµiale pentru ecare vector-linie B i, ˜.
Aceast  soluµie are complexitate O Q  M N log Q ³i ar trebui s  obµin  minimum 50 de
puncte.
Soluµie cerinµa 2 70p
Pentru a obµine punctajul maxim alocat cerinµei 2, vom extinde ideea de mai sus pentru cazul
bidimensional.
Astfel, vom crea o matrice C astfel încât C i, j  A i, j   A i  1, j   A i, j  1  A i  1, j  1.
În acest caz, o operaµie de ad ugare cu valoarea x pe submatricea i1, j1, i2, j2 se va simula prin
operaµiile C i1, j1 x C i1, j2  1 x C i2  1, j1 x ³i C i2  1, j2  1 x.
În nal, pentru a reconstitui matricea A, vom calcula sumele parµiale 2D pe matricea C .
Aceast  soluµie are complexitate O Q  M N log Q ³i ar trebui s  obµin  70 de puncte.

21.1.2 Cod surs 

Listing 21.1.1: alex-nnlog.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <assert.h>
4
5 #define x1 first.first
6 #define y1 first.second
7
8 #define x2 second.first
9 #define y2 second.second.first
10
11 #define val second.second.second
12
13 using namespace std;
14
15 ifstream fin("amat.in");
16 ofstream fout("amat.out");
17
18 int init[1002][1002], dp[1002][1002];
19
20 pair< pair<int,int>, pair<int, pair<int,int> > > q[250001];
21
22 int qq,k,n,m;
23
24 int main()
25 {
26 int optiune;
27 fin >> optiune >> n >> m;
CAPITOLUL 21. ONI 2019 293

28
29 for(int i = 1; i <= n; i++)
30 for(int j = 1; j <= m; j++)
31 fin >> init[i][j];
32
33 fin >> qq >> k;
34
35 for(int i = 1; i <= qq; i++)
36 fin >> q[i].x1 >> q[i].y1 >> q[i].x2 >> q[i].y2 >> q[i].val;
37
38 int st = 1;
39 int dr = qq;
40 int rasp = 0;
41 while(st <= dr)
42 {
43 int mij = (st + dr) / 2;
44 int ok = 1;
45 for(int i = 1; i <= mij; i++)
46 dp[q[i].x1][ q[i].y1 ] += q[i].val,
47 dp[q[i].x2 + 1][ q[i].y2 + 1] += q[i].val,
48 dp[q[i].x1][ q[i].y2 + 1 ] -= q[i].val,
49 dp[q[i].x2 + 1][ q[i].y1] -= q[i].val;
50
51 for(int i = 1; i <= n; i++)
52 for(int j = 1; j <= m; j++)
53 {
54 dp[i][j] += dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1];
55 if(dp[i][j] + init[i][j] < k)
56 ok = 0;
57 }
58
59 for(int i = 1; i <= n; i++)
60 for(int j = 1; j <= m; j++)
61 dp[i][j] = 0;
62
63 if(ok)
64 rasp = mij, dr = mij - 1;
65 else
66 st = mij + 1;
67 }
68
69 fout << rasp << ’\n’;
70 return 0;
71 }

Listing 21.1.2: alex-nqlog.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <assert.h>
4
5 #define x1 first.first
6 #define y1 first.second
7
8 #define x2 second.first
9 #define y2 second.second.first
10
11 #define val second.second.second
12
13 using namespace std;
14
15 ifstream fin("amat.in");
16 ofstream fout("amat.out");
17
18 int init[1002][1002], dp[1002][1002];
19
20 pair< pair<int,int>, pair<int, pair<int,int> > > q[250001];
21
22 int qq,k,n,m;
23
24 int main()
25 {
26 int optiune;
27 fin >> optiune >> n >> m;
28
CAPITOLUL 21. ONI 2019 294

29 for(int i = 1; i <= n; i++)


30 for(int j = 1; j <= m; j++)
31 fin >> init[i][j];
32
33 fin >> qq >> k;
34
35 for(int i = 1; i <= qq; i++)
36 fin >> q[i].x1 >> q[i].y1 >> q[i].x2 >> q[i].y2 >> q[i].val;
37
38 int st = 1;
39 int dr = qq;
40 int rasp = 0;
41 while(st <= dr)
42 {
43 int mij = (st + dr) / 2;
44 int ok = 1;
45 for(int i = 1; i <= mij; i++)
46 for(int j = q[i].x1; j <= q[i].x2; j++)
47 dp[j][ q[i].y1 ] += q[i].val,
48 dp[j][ q[i].y2 + 1] -= q[i].val;
49
50 for(int i = 1; i <= n; i++)
51 for(int j = 1; j <= m; j++)
52 {
53 dp[i][j] += dp[i][j - 1];
54 if(dp[i][j] + init[i][j] < k)
55 ok = 0;
56 }
57
58 for(int i = 1; i <= n; i++)
59 for(int j = 1; j <= m; j++)
60 dp[i][j] = 0;
61
62 if(ok)
63 rasp = mij, dr = mij - 1;
64 else
65 st = mij + 1;
66 }
67
68 fout << rasp << ’\n’;
69 return 0;
70 }

Listing 21.1.3: amat_eugen.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 ifstream f("amat.in");
6 ofstream g("amat.out");
7
8 struct submatrix
9 {
10 int x1, y1, x2, y2;
11 int k;
12 } ap[2003];
13
14 struct qry
15 {
16 int x1, y1, x2, y2, w;
17 } Q[100001];
18
19 const int NM = 1002;
20
21 int a[NM][NM], A[NM][NM];
22 int B[NM][NM];
23 int n, m, q, c, K;
24
25 bool verif(int nr)
26 {
27 int x1, y1, x2, y2, w, x;
28
29 memset(A, 0, sizeof(A));
30
CAPITOLUL 21. ONI 2019 295

31 for(int i = 1; i <= nr; ++i)


32 {
33 x1 = Q[i].x1; y1 = Q[i].y1;
34 x2 = Q[i].x2; y2 = Q[i].y2;
35 w = Q[i].w;
36 A[x1][y1] += w;
37 A[x1][y2 + 1] -= w;
38 A[x2 + 1][y1] -= w;
39 A[x2 + 1][y2 + 1] += w;
40 }
41
42 for(int i = 1; i <= n; ++i)
43 for(int j = 1; j <= m; ++j)
44 {
45 A[i][j] = A[i][j] + A[i-1][j] + A[i][j-1] - A[i-1][j-1];
46 x = A[i][j] + a[i][j];
47 if (x < K) return 0;
48 }
49
50 return 1;
51 }
52
53 int main()
54 {
55 int i, j, x, x1, y1, x2, y2;
56
57 f >> c >> n >> m;
58 for(i = 1; i <= n; ++i)
59 for(j = 1; j <= m; ++j)
60 f >> a[i][j];
61
62 if (c == 1)
63 {
64 for(i = 0; i <= 2000; ++i)
65 ap[i].y1 = ap[i].x1 = 2003;
66
67 for(i = 1; i <= n; ++i)
68 for(j = 1; j <= m; ++j)
69 {
70 x = a[i][j];
71 x += 1000;
72 ap[x].x1 = min(ap[x].x1, i);
73 ap[x].y1 = min(ap[x].y1, j);
74 ap[x].x2 = max(ap[x].x2, i);
75 ap[x].y2 = max(ap[x].y2, j);
76 ap[x].k++;
77 }
78
79 int Max_arie = 0;
80 for(i = 0; i <= 2000; ++i)
81 if (ap[i].k > Max_arie)
82 {
83 Max_arie = ap[i].k;
84 x1 = ap[i].x1;
85 x2 = ap[i].x2;
86 y1 = ap[i].y1;
87 y2 = ap[i].y2;
88 }
89 else
90 if (ap[i].k == Max_arie)
91 {
92 if (ap[i].x1 > x1 || ap[i].x1 == x1 && ap[i].y1 > y1)
93 {
94 x1 = ap[i].x1;
95 x2 = ap[i].x2;
96 y1 = ap[i].y1;
97 y2 = ap[i].y2;
98 }
99 }
100
101 g << x1 << " " << y1 << " " << x2 << " " << y2 << "\n";
102
103 }
104 else
105 {
106
CAPITOLUL 21. ONI 2019 296

107 f >> q >> K;


108 for(i = 1; i <= q; ++i)
109 {
110 f >> x1 >> y1 >> x2 >> y2 >> x;
111 Q[i] = {x1, y1, x2, y2, x};
112 }
113
114 i = 1, j = q;
115 while (i <= j)
116 {
117 int mij = (i+j) >> 1;
118 int k = verif(mij);
119 if (k)
120 j = mij - 1;
121 else
122 i = mij + 1;
123 }
124
125 g << i << "\n";
126 }
127
128 return 0;
129 }

Listing 21.1.4: brut.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <assert.h>
4
5 #define x1 first.first
6 #define y1 first.second
7
8 #define x2 second.first
9 #define y2 second.second.first
10
11 #define val second.second.second
12
13 using namespace std;
14
15 ifstream fin("amat.in");
16 ofstream fout("amat.out");
17
18 int init[1002][1002], dp[1002][1002];
19
20 pair< pair<int,int>, pair<int, pair<int,int> > > q[250001];
21
22 int qq,k,n,m;
23
24 int main()
25 {
26 int optiune;
27 fin >> optiune >> n >> m;
28
29 for(int i = 1; i <= n; i++)
30 for(int j = 1; j <= m; j++)
31 fin >> init[i][j];
32
33 fin >> qq >> k;
34
35 for(int i = 1; i <= qq; i++)
36 fin >> q[i].x1 >> q[i].y1 >> q[i].x2 >> q[i].y2 >> q[i].val;
37
38 int rasp = 0;
39 for(int mij = 1; mij <= qq; mij++)
40 {
41 int ok = 1;
42
43 for(int i = q[mij].x1; i <= q[mij].x2; i++)
44 for(int j = q[mij].y1; j <= q[mij].y2; j++)
45 init[i][j] += q[mij].val;
46
47 for(int i = 1; i <= n && ok; i++)
48 for(int j = 1; j <= m && ok; j++)
49 {
CAPITOLUL 21. ONI 2019 297

50 if(init[i][j] < k)
51 ok = 0;
52 }
53
54 if(ok)
55 {
56 fout << mij << ’\n’;
57 return 0;
58 }
59 }
60
61 return 0;
62 }

Listing 21.1.5: brut_IQ_9000


1 #include <iostream>
2 #include <fstream>
3 #include <assert.h>
4
5 #define x1 first.first
6 #define y1 first.second
7
8 #define x2 second.first
9 #define y2 second.second.first
10
11 #define val second.second.second
12
13 using namespace std;
14
15 ifstream fin("amat.in");
16 ofstream fout("amat.out");
17
18 int init[1002][1002], dp[1002][1002];
19
20 pair< pair<int,int>, pair<int, pair<int,int> > > q[250001];
21
22 int qq,k,n,m;
23 int bad;
24
25 int main()
26 {
27 int optiune;
28 fin >> optiune >> n >> m;
29
30 for(int i = 1; i <= n; i++)
31 for(int j = 1; j <= m; j++)
32 fin >> init[i][j];
33
34 fin >> qq >> k;
35
36 for(int i = 1; i <= qq; i++)
37 fin >> q[i].x1 >> q[i].y1 >> q[i].x2 >> q[i].y2 >> q[i].val;
38
39 for(int i = 1; i <= n; i++)
40 for(int j = 1; j <= m; j++)
41 if(init[i][j] < k)
42 bad++;
43
44 int rasp = 0;
45 for(int mij = 1; mij <= qq; mij++)
46 {
47 for(int i = q[mij].x1; i <= q[mij].x2; i++)
48 for(int j = q[mij].y1; j <= q[mij].y2; j++)
49 {
50 if(init[i][j] < k && init[i][j] + q[mij].val >= k )
51 bad--;
52 init[i][j] += q[mij].val;
53 }
54
55 if(bad == 0)
56 {
57 fout << mij << ’\n’;
58 return 0;
59 }
CAPITOLUL 21. ONI 2019 298

60 }
61
62 return 0;
63 }

Listing 21.1.6: chiorean_amat.cpp


1 // student Tudor Chiorean - Universitatea Tehnica din Cluj - 100 puncte
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 #define NMAX 1005
8 #define VALMAX 1000000000
9 #define VALMIN -1000
10
11 ifstream fin("amat.in");
12 ofstream fout("amat.out");
13
14 struct query
15 {
16 int x1, y1, x2, y2, v;
17 };
18
19 struct subtaskAnswer
20 {
21 int x1, y1, x2, y2;
22 };
23
24 int n, m, i, j, q, k, c;
25 int mat[NMAX + 5][NMAX + 5], aux[NMAX + 5][NMAX + 5];
26 query queries[100000 + 5];
27
28 void DEBUG_endmatrix()
29 {
30 for (i = 1 ; i <= n ; i++)
31 {
32 for (j = 1 ; j <= m ; j++)
33 {
34 cout << aux[i][j] << ’ ’;
35 }
36 cout << ’\n’;
37 }
38 }
39
40 void subtask()
41 {
42 int crt, cnt, maxCnt = 0, i, j, lin, col;
43 subtaskAnswer subAns;
44
45 for (i = 1 ; i <= n ; i++)
46 {
47 for (j = 1 ; j <= m ; j++)
48 {
49 if (aux[i][j] == 1) continue;
50
51 crt = mat[i][j];
52 cnt = 0;
53 for (lin = i ; lin <= n ; lin++)
54 {
55 if (mat[lin][j] != crt) break;
56 for (col = j ; col <= m ; col++)
57 {
58 if (mat[lin][col] != crt) break;
59 aux[lin][col] = 1;
60
61 cnt++;
62 }
63 }
64
65 if (cnt >= maxCnt)
66 {
67 subAns.x1 = i;
68 subAns.y1 = j;
CAPITOLUL 21. ONI 2019 299

69 subAns.x2 = lin - 1;
70 subAns.y2 = col - 1;
71
72 maxCnt = cnt;
73 }
74 }
75 }
76
77 fout << subAns.x1 << ’ ’ << subAns.y1 << ’ ’ <<
78 subAns.x2 << ’ ’ << subAns.y2 << ’\n’;
79 }
80
81 int solve(int nrQ)
82 {
83 int val;
84 memset(aux, 0, sizeof(aux));
85
86 for (i = 1 ; i <= nrQ ; i++)
87 {
88 val = queries[i].v;
89 aux[queries[i].x1][queries[i].y1] += val;
90 aux[queries[i].x1][queries[i].y2 + 1] += -val;
91 aux[queries[i].x2 + 1][queries[i].y1] += -val;
92 aux[queries[i].x2 + 1][queries[i].y2 + 1] += val;
93 }
94
95 for (i = 1 ; i <= n ; i++)
96 for (j = 1 ; j <= m ; j++)
97 aux[i][j] += aux[i][j - 1];
98
99 for (j = 1 ; j <= m ; j++)
100 for (i = 1 ; i <= n ; i++)
101 aux[i][j] += aux[i - 1][j];
102
103 /**
104 cout << nrQ << ’\n’;
105 DEBUG_endmatrix();
106 cout << "-----------------\n";
107 */
108
109 for (i = 1 ; i <= n ; i++)
110 for (j = 1 ; j <= m ; j++)
111 if (aux[i][j] + mat[i][j] < k) return 0;
112
113 return 1;
114 }
115
116 void maintask()
117 {
118 int ls = 1, ld = q, mij, best = q;
119
120 while (ls <= ld)
121 {
122 mij = (ls + ld) / 2;
123
124 if (solve(mij))
125 {
126 best = mij;
127 ld = mij - 1;
128 }
129 else
130 {
131 ls = mij + 1;
132 }
133 }
134
135 fout << best;
136 }
137
138 int main()
139 {
140 fin >> c;
141 fin >> n >> m;
142
143 assert(c == 1 || c == 2);
144 assert(2 <= n && n <= NMAX);
CAPITOLUL 21. ONI 2019 300

145 assert(2 <= m && m <= NMAX);


146
147 for (i = 1 ; i <= n ; i++)
148 for (j = 1 ; j <= m ; j++)
149 {
150 fin >> mat[i][j];
151
152 assert(VALMIN <= mat[i][j] && mat[i][j] <= 1000);
153 }
154
155 if (c == 1)
156 {
157 subtask();
158 }
159 else
160 {
161 fin >> q >> k;
162 assert(VALMIN <= k && k <= VALMAX);
163 assert(1 < q && q <= 100000);
164
165 for (i = 1 ; i <= q ; i++)
166 {
167 fin >> queries[i].x1 >> queries[i].y1 >>
168 queries[i].x2 >> queries[i].y2 >> queries[i].v;
169
170 assert(1 <= queries[i].x1 && queries[i].x1 <= n);
171 assert(1 <= queries[i].y1 && queries[i].y1 <= m);
172 assert(1 <= queries[i].x2 && queries[i].x2 <= n);
173 assert(1 <= queries[i].y2 && queries[i].y2 <= m);
174 assert(queries[i].x1 <= queries[i].x2 &&
175 queries[i].y1 <= queries[i].y2);
176 }
177
178 maintask();
179 }
180
181 return 0;
182 }

Listing 21.1.7: solution.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 int main()
6 {
7 ifstream cin("amat.in");
8 ofstream cout("amat.out");
9
10 int task; cin >> task;
11 int n, m; cin >> n >> m;
12
13 vector<vector<int>> a(n, vector<int>(m));
14
15 for (int i = 0; i < n; ++i)
16 {
17 for (int j = 0; j < m; ++j)
18 {
19 cin >> a[i][j];
20 }
21 }
22
23 if (task == 1)
24 {
25 const int kMax = 5000;
26 vector<int> lt(kMax, -1), rt(kMax, -1), up(kMax, -1), dw(kMax, -1);
27
28 for (int i = 0; i < n; ++i)
29 {
30 for (int j = 0; j < m; ++j)
31 {
32 int val = a[i][j] + 2000;
33 if (lt[val] == -1 || lt[val] > j) lt[val] = j;
34 if (rt[val] == -1 || rt[val] < j) rt[val] = j;
CAPITOLUL 21. ONI 2019 301

35 if (up[val] == -1 || up[val] > i) up[val] = i;


36 if (dw[val] == -1 || dw[val] < i) dw[val] = i;
37 }
38 }
39
40 tuple<int, int, int, int> best = make_tuple(-1, -1, -1, -1);
41
42 for (int i = 0; i < kMax; ++i)
43 {
44 tuple<int, int, int, int> now = make_tuple(
45 (rt[i] - lt[i] + 1) * (dw[i] - up[i] + 1), up[i], lt[i], i);
46
47 best = max(best, now);
48 }
49
50 int col = get<3>(best);
51 cout << up[col] + 1 << " " << lt[col] + 1 << " " <<
52 dw[col] + 1 << " " << rt[col] + 1 << endl;
53
54 }
55 else
56 {
57 int q, k; cin >> q >> k;
58
59 vector<tuple<int, int, int, int, int>> ops;
60
61 for (int i = 0; i < q; ++i)
62 {
63 int u, l, d, r, x; cin >> u >> l >> d >> r >> x;
64 ops.emplace_back(u, l, d, r, x);
65 }
66
67 int sol = 0;
68 for (int step = 1, adv = 1; step; adv ? step *= 2 : step /= 2)
69 {
70 if (sol + step > q)
71 {
72 adv = 0;
73 }
74 else
75 {
76 vector<vector<int>> mars(n + 1, vector<int>(m + 1, 0));
77
78 for (int i = sol; i < sol + step; ++i)
79 {
80 int u, l, d, r, x; tie(u, l, d, r, x) = ops[i];
81 mars[u - 1][l - 1] += x;
82 mars[u - 1][r] -= x;
83 mars[d][l - 1] -= x;
84 mars[d][r] += x;
85 }
86
87 bool bad = false;
88 for (int i = 0; i < n; ++i)
89 {
90 for (int j = 0; j < m; ++j)
91 {
92 if (i > 0) mars[i][j] += mars[i - 1][j];
93 if (j > 0) mars[i][j] += mars[i][j - 1];
94 if (i > 0 && j > 0) mars[i][j] -= mars[i - 1][j - 1];
95
96 if (mars[i][j] + a[i][j] < k)
97 {
98 bad = true;
99 }
100 }
101 }
102 /*
103 cerr << sol + step << endl;
104 for (int i = 0; i < n; ++i) {
105 for (int j = 0; j < m; ++j)
106 cerr << a[i][j] + mars[i][j] << " ";
107 cerr << endl;
108 }
109 */
110 if (!bad)
CAPITOLUL 21. ONI 2019 302

111 {
112 adv = 0;
113 continue;
114 }
115
116 for (int i = 0; i < n; ++i)
117 {
118 for (int j = 0; j < m; ++j)
119 {
120 a[i][j] += mars[i][j];
121 }
122 }
123
124 sol += step;
125 }
126 }
127
128 assert(sol < q);
129 cout << sol + 1 << endl;
130 }
131
132 return 0;
133 }

Listing 21.1.8: sursa_test.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 #define NMAX 1005
6 #define VALMAX 1000000000
7 #define VALMIN -1000
8
9 ifstream fin("amat.in");
10 ofstream fout("amat.out");
11
12 struct query
13 {
14 int x1, y1, x2, y2, v;
15 };
16
17 struct subtaskAnswer
18 {
19 int x1, y1, x2, y2;
20 };
21
22 int n, m, i, j, q, k, c;
23 int mat[NMAX + 5][NMAX + 5], aux[NMAX + 5][NMAX + 5];
24 query queries[100000 + 5];
25
26 void DEBUG_endmatrix()
27 {
28 for (i = 1 ; i <= n ; i++)
29 {
30 for (j = 1 ; j <= m ; j++)
31 {
32 cout << aux[i][j] << ’ ’;
33 }
34 cout << ’\n’;
35 }
36 }
37
38 void subtask()
39 {
40 int crt, cnt, maxCnt, i, j, lin, col;
41 subtaskAnswer subAns;
42
43 for (i = 1 ; i <= n ; i++)
44 {
45 for (j = 1 ; j <= m ; j++)
46 {
47 if (aux[i][j] == 1) continue;
48
49 crt = mat[i][j];
CAPITOLUL 21. ONI 2019 303

50 cnt = 0;
51 for (lin = i ; lin <= n ; lin++)
52 {
53 if (mat[lin][j] != crt) break;
54 for (col = j ; col <= m ; col++)
55 {
56 if (mat[lin][col] != crt) break;
57 aux[lin][col] = 1;
58
59 cnt++;
60 }
61 }
62
63 if (cnt >= maxCnt)
64 {
65 subAns.x1 = i;
66 subAns.y1 = j;
67 subAns.x2 = lin - 1;
68 subAns.y2 = col - 1;
69
70 maxCnt = cnt;
71 }
72 }
73 }
74
75 fout << subAns.x1 << ’ ’ << subAns.y1 << ’ ’ <<
76 subAns.x2 << ’ ’ << subAns.y2 << ’\n’;
77 }
78
79 int solve(int nrQ)
80 {
81 int val;
82 memset(aux, 0, sizeof(aux));
83
84 for (i = 1 ; i <= nrQ ; i++)
85 {
86 val = queries[i].v;
87 aux[queries[i].x1][queries[i].y1] += val;
88 aux[queries[i].x1][queries[i].y2 + 1] += -val;
89 aux[queries[i].x2 + 1][queries[i].y1] += -val;
90 aux[queries[i].x2 + 1][queries[i].y2 + 1] += val;
91 }
92
93 for (i = 1 ; i <= n ; i++)
94 for (j = 1 ; j <= m ; j++)
95 aux[i][j] += aux[i][j - 1];
96
97 for (j = 1 ; j <= m ; j++)
98 for (i = 1 ; i <= n ; i++)
99 aux[i][j] += aux[i - 1][j];
100
101 /**
102 cout << nrQ << ’\n’;
103 DEBUG_endmatrix();
104 cout << "-----------------\n";
105 */
106
107 for (i = 1 ; i <= n ; i++)
108 for (j = 1 ; j <= m ; j++)
109 if (aux[i][j] + mat[i][j] < k) return 0;
110
111 return 1;
112 }
113
114 void maintask()
115 {
116 int ls = 1, ld = q, mij, best = q;
117 while (ls <= ld)
118 {
119 mij = (ls + ld) / 2;
120
121 if (solve(mij))
122 {
123 best = mij;
124 ld = mij - 1;
125 }
CAPITOLUL 21. ONI 2019 304

126 else
127 {
128 ls = mij + 1;
129 }
130 }
131
132 fout << best;
133 }
134
135 int main()
136 {
137 fin >> c;
138 fin >> n >> m;
139
140 assert(c == 1 || c == 2);
141 assert(2 <= n && n <= NMAX);
142 assert(2 <= m && m <= NMAX);
143
144 for (i = 1 ; i <= n ; i++)
145 for (j = 1 ; j <= m ; j++)
146 {
147 fin >> mat[i][j];
148
149 assert(VALMIN <= mat[i][j] && mat[i][j] <= 1000);
150 }
151
152 if (c == 1)
153 {
154 subtask();
155 }
156 else
157 {
158 fin >> q >> k;
159 assert(VALMIN <= k && k <= VALMAX);
160 assert(1 < q && q <= 100000);
161
162 for (i = 1 ; i <= q ; i++)
163 {
164 fin >> queries[i].x1 >> queries[i].y1 >>
165 queries[i].x2 >> queries[i].y2 >> queries[i].v;
166
167 assert(1 <= queries[i].x1 && queries[i].x1 <= n);
168 assert(1 <= queries[i].y1 && queries[i].y1 <= m);
169 assert(1 <= queries[i].x2 && queries[i].x2 <= n);
170 assert(1 <= queries[i].y2 && queries[i].y2 <= m);
171 assert(queries[i].x1 <= queries[i].x2 &&
172 queries[i].y1 <= queries[i].y2);
173 }
174
175 maintask();
176 }
177
178 return 0;
179 }

21.1.3 *Rezolvare detaliat 

21.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 21. ONI 2019 305

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 a  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 aa K numere naturale distincte, în ordine strict cresc toare,
reprezentând ³irul propriu-zis.

Restricµii ³i preciz ri
a Toate valorile din ³ierul de intrare sunt numere naturale nenule mai mici sau egale cu
100000.
a Pentru teste în valoare de 15 puncte, toate valorile din ³ierul de intrare sunt mai mici sau
egale cu 20.
a Pentru teste în valoare de 50 de puncte, toate valorile din ³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
comun.in comun.out Explicaµii
5 3 1 = cmmdc(6, 7) = cmmdc(4, 6, 7).
12467 467 2 = cmmdc(4, 6).
Se poate demonstra c  orice alt ³ir cu proprietatea cerut  are
m car 3 elemente.
4 4 Nu exist  niciun ³ir de mai puµin de 4 elemente cu proprietatea
2 4 8 16 2 4 8 16 cerut .

Timp maxim de executare/test: Windows: 0.5 secunde, Linux: 0.5 secunde


Memorie: total 64 MB
Dimensiune maxim  a sursei: 10 KB

21.2.1 Indicaµii de rezolvare


Stud. Lucian Bicsi - Universitatea din Bucure³ti

Soluµie 15p
Exist  o multitudine de soluµii care se încadreaz  în aceste limite. în continuare va  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 sucient s  consider m
doar perechi de câte dou  numere.
CAPITOLUL 21. ONI 2019 306

Un algoritm este urm torul: pentru ecare 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  veric m ecare 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 .

21.2.2 Cod surs 

Listing 21.2.1: comun_back.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 int gcd(int a, int b)
6 {
7 while (b)
8 {
9 int r = a % b;
10 a = b;
11 b = r;
12 }
13 return a;
14 }
15
16 int main()
17 {
18 ifstream cin("comun.in");
19 ofstream cout("comun.out");
20
21 int n;
22 cin >> n;
23
24 vector<int> v(n);
25
26 int maxx = 0;
27 for (int i = 0; i < n; ++i)
28 {
29 cin >> v[i];
30 maxx = max(maxx, v[i]);
31 }
32
33 long long ap = 0;
34 for (auto x : v)
35 ap |= (1LL << (x - 1));
36
37 vector<int> calc(ap + 1, 0);
38
39 for (int i = 1; i <= ap; ++i)
40 {
41 for (int j = 0; j < maxx; ++j)
42 {
43 if (i & (1LL << j))
CAPITOLUL 21. ONI 2019 307

44 calc[i] = gcd(calc[i], j + 1);


45 }
46 --calc[i];
47 }
48
49 for (int found = 1; found >= 0; --found)
50 {
51 for (int msk = 1; msk <= ap; ++msk)
52 {
53 if ((msk & ap) != msk) continue;
54 int gc = calc[msk];
55 if ((ap & (1LL << gc)) && !(msk & (1LL << gc)))
56 {
57 ap ^= (1LL << gc);
58 found = 1;
59 break;
60 }
61 }
62 }
63
64 vector<int> ans;
65
66 for (int i = 0; i < maxx; ++i)
67 {
68 if (ap & (1 << i))
69 ans.push_back(i + 1);
70 }
71
72 cout << ans.size() << endl;
73
74 for (auto x : ans)
75 cout << x << " ";
76
77 cout << endl;
78
79 return 0;
80 }

Listing 21.2.2: comun_chiorean.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 #define NMAX 250005
6
7 ifstream fin("comun.in");
8 ofstream fout("comun.out");
9
10 int n, i, j, x, valmax, g_c_d;
11 int v[NMAX];
12
13 int main()
14 {
15 fin >> n;
16 for (i = 1 ; i <= n ; i++)
17 {
18 fin >> x;
19 v[x] = 1;
20 valmax = max(valmax, x);
21 }
22
23 for (i = valmax - 1 ; i > 0 ; i--)
24 {
25 if (!v[i] || i + i > valmax) continue;
26
27 g_c_d = 0;
28
29 for (j = i + i ; j <= valmax ; j += i)
30 if (v[j] == 1)
31 {
32 if (g_c_d == 0)
33 g_c_d = j;
34 else
35 g_c_d = __gcd(g_c_d, j);
CAPITOLUL 21. ONI 2019 308

36 }
37
38 if (g_c_d == i)
39 v[i] = 0, n--;
40 }
41
42 fout << n << ’\n’;
43 for (i = 1 ; i <= valmax ; i++)
44 {
45 if (v[i] == 1) fout << i << ’ ’;
46 }
47
48 return 0;
49 }

Listing 21.2.3: comun_eugen.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 const int N = 250000;
6
7 int n;
8 int a[N + 5], ap[N + 5];
9
10 vector <int> v;
11
12 inline int cmmdc(int x, int y)
13 {
14 if(x == -1) return y;
15 while(y > 0){
16 int r = x % y;
17 x = y; y = r;
18 }
19 return x;
20 }
21
22 int main()
23 {
24 freopen("comun.in", "r", stdin);
25 freopen("comun.out", "w", stdout);
26
27 scanf("%d", &n);
28 for(int i = 1; i <= n ; ++i)
29 scanf("%d", &a[i]), ++ap[a[i]]; ///citesc sirul si vad ce numere
30 ///apar in el
31
32 for(int i = 1; i <= n ; ++i){
33 ///un numar a[i] apare in sirul final doar daca cmmdc-ul tuturor
34 ///multiplilor lui
35 ///care apar in sirul initial e diferit de a[i]
36 int nr = 0, cm = -1;
37 for(int j = 2; j * a[i] <= N ; ++j)
38 if(ap[j * a[i]]) cm = cmmdc(cm, j * a[i]);
39
40 if(cm != a[i]) v.push_back(a[i]);
41 }
42
43 printf("%d\n", v.size());
44 for(auto it : v)
45 printf("%d ", it);
46
47 return 0;
48 }

Listing 21.2.4: comun_io.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 int gcd(int a, int b)
6 {
CAPITOLUL 21. ONI 2019 309

7 while (b)
8 {
9 int r = a % b;
10 a = b;
11 b = r;
12 }
13 return a;
14 }
15
16 int main()
17 {
18 ifstream cin("comun.in");
19 ofstream cout("comun.out");
20
21 int n; cin >> n;
22
23 vector<int> v(n);
24
25 int maxx = 0;
26 for (auto& x : v)
27 {
28 cin >> x;
29 maxx = max(maxx, x);
30 }
31
32 vector<bool> w(maxx + 1);
33 for (auto x : v)
34 w[x] = true;
35
36 for (int x = maxx; x >= 1; --x)
37 {
38 if (w[x] == false) continue;
39
40 for (int pos = 0; pos < (int)v.size(); ++pos)
41 {
42 int y = v[pos];
43 int gc = gcd(x, y);
44 if (gc < x && gc < y)
45 w[gc] = false;
46 }
47 }
48
49 v.clear();
50
51 for (int i = 1; i <= maxx; ++i)
52 if (w[i])
53 v.push_back(i);
54
55 cout << v.size() << endl;
56
57 for (auto x : v)
58 cout << x << " ";
59 cout << endl;
60
61 return 0;
62 }

Listing 21.2.5: comun_nlog_sub.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 int gcd(int a, int b)
6 {
7 while (a && b)
8 {
9 if (a > b) a -= b;
10 else b -= a;
11 }
12 return a + b;
13 }
14
15 vector<bool> Sparsify(vector<bool> vals)
16 {
CAPITOLUL 21. ONI 2019 310

17 for (int i = 1; i < (int)vals.size(); ++i)


18 {
19 if (!vals[i]) continue;
20 int all_gcd = 0;
21 for (int j = i + i; j < (int)vals.size(); j += i)
22 {
23 if (vals[j])
24 all_gcd = all_gcd ? gcd(all_gcd, j % all_gcd) : j;
25 }
26 if (all_gcd == i)
27 vals[i] = false;
28 }
29 return vals;
30 }
31
32 int main()
33 {
34 ifstream cin("comun.in");
35 ofstream cout("comun.out");
36
37 int n;
38 cin >> n;
39
40 vector<bool> v(100001, false);
41
42 for (int i = 0; i < n; ++i)
43 {
44 int x; cin >> x; v[x] = true;
45 }
46
47 v = Sparsify(v);
48 vector<int> out;
49
50 for (int i = 0; i < (int)v.size(); ++i)
51 if (v[i])
52 out.push_back(i);
53
54 cout << out.size() << endl;
55 for (auto x : out)
56 cout << x << " ";
57 cout << endl;
58
59 return 0;
60 }

Listing 21.2.6: comun_sol.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 int gcd(int a, int b)
6 {
7 while (b)
8 {
9 int r = a % b;
10 a = b;
11 b = r;
12 }
13 return a;
14 }
15
16 vector<bool> Sparsify(vector<bool> vals)
17 {
18 for (int i = 1; i < (int)vals.size(); ++i)
19 {
20 if (!vals[i]) continue;
21 int all_gcd = 0;
22
23 for (int j = i + i; j < (int)vals.size(); j += i)
24 {
25 if (vals[j])
26 all_gcd = gcd(all_gcd, j);
27 }
28
CAPITOLUL 21. ONI 2019 311

29 if (all_gcd == i)
30 vals[i] = false;
31 }
32 return vals;
33 }
34
35 int main()
36 {
37 ifstream cin("comun.in");
38 ofstream cout("comun.out");
39
40 int n;
41 cin >> n;
42
43 vector<bool> v(100001, false);
44
45 for (int i = 0; i < n; ++i)
46 {
47 int x; cin >> x; v[x] = true;
48 }
49
50 v = Sparsify(v);
51 vector<int> out;
52
53 for (int i = 0; i < (int)v.size(); ++i)
54 if (v[i])
55 out.push_back(i);
56
57 cout << out.size() << endl;
58 for (auto x : out)
59 cout << x << " ";
60 cout << endl;
61
62 return 0;
63 }

Listing 21.2.7: sol_nlog.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 int gcd(int a, int b)
6 {
7 while (b)
8 {
9 int r = a % b;
10 a = b;
11 b = r;
12 }
13 return a;
14 }
15
16 vector<bool> Sparsify(vector<bool> vals)
17 {
18 for (int i = 1; i < (int)vals.size(); ++i)
19 {
20 if (!vals[i]) continue;
21 int all_gcd = 0;
22
23 for (int j = i + i; j < (int)vals.size(); j += i)
24 {
25 if (vals[j])
26 all_gcd = gcd(all_gcd, j);
27 }
28
29 if (all_gcd == i)
30 vals[i] = false;
31 }
32 return vals;
33 }
34
35 int main()
36 {
37 ifstream cin("comun.in");
CAPITOLUL 21. ONI 2019 312

38 ofstream cout("comun.out");
39
40 int n;
41 cin >> n;
42 vector<bool> v(100001, false);
43
44 for (int i = 0; i < n; ++i)
45 {
46 int x; cin >> x; v[x] = true;
47 }
48
49 v = Sparsify(v);
50 vector<int> out;
51 for (int i = 0; i < (int)v.size(); ++i)
52 if (v[i])
53 out.push_back(i);
54
55 cout << out.size() << endl;
56 for (auto x : out)
57 cout << x << " ";
58 cout << endl;
59
60 return 0;
61 }

21.2.3 *Rezolvare detaliat 

21.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
S  se determine cardinalul mulµimii P .

Date de intrare
Fi³ierul de intrare pro3.in conµine 3 linii.
Pe linia i, 1 & i & 3 se vor g si câte 3 numere naturale ai , ri , ni , separate prin câte un spaµiu,
ce reprezint  în aceast  ordine primul termen, raµia ³i num rul de termeni ai progresiei P i.

Date de ie³ire
Fi³ierul de ie³ire pro3.out va conµine pe prima linie cardinalul mulµimii P .

Restricµii ³i preciz ri
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
pro3.in pro3.out Explicaµii
2 2 10 24 Prima progresie are primul termen 2, raµia 2 ³i 10 termeni.
348 A doua progresie are primul termen 3, raµia 4 ³i 8 termeni.
1 3 12 A treia progresie are primul termen 1, raµia 3 ³i 12 termeni.
A³adar:
P1 r2, 4, 6, 8, 10, 12, 14, 16, 18, 20x
P2 r3, 7, 11, 15, 19, 23, 27, 31x
P3 r1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34x
Reuniunea termenilor celor trei progresii este mulµimea
P r1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23,
CAPITOLUL 21. ONI 2019 313

Timp maxim de executare/test: Windows: 0.5 secunde, Linux: 0.5 secunde


Memorie: total 64 MB
Dimensiune maxim  a sursei: 10 KB

21.3.1 Indicaµii de rezolvare


prof. Ciprian Che³c  - Liceul Tehnologic Grigore C. Moisil Buz u

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  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 ecare 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 ecient 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), vericâ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  veric m
ecare 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.

21.3.2 Cod surs 

Listing 21.3.1: pro3_AT1.cpp


1 // student Alex Turdean - Universitatea Tehnica Cluj
2 #include <iostream>
3 #include <fstream>
4 #include <assert.h>
5
6 using namespace std;
7
8 ifstream fin("pro3.in");
9 ofstream fout("pro3.out");
10
11 long long rez, a[4], n[4],r[4];
12
13 long long gcd(int x, int y)
14 {
15 int r = x%y;
CAPITOLUL 21. ONI 2019 314

16 while(r)
17 {
18 x = y;
19 y = r;
20 r = x%y;
21 }
22 return y;
23 }
24
25 long long inter2(int x, int y)
26 {
27 long long maxi = min(a[x] + (n[x] - 1) * r[x], a[y] + (n[y] - 1) * r[y]);
28 for(int i = 0; i < 1000000; i++)
29 {
30 long long termen = a[x] + i * r[x];
31 if(termen > maxi)
32 return 0;
33 if( (termen - a[y]) % r[y] == 0 && termen >= a[y])
34 {
35 return (maxi - termen) / (r[x] * r[y] / gcd(r[y], r[x])) + 1;
36 }
37 }
38 return 0;
39 }
40
41 long long inter3()
42 {
43 long long maxi = min(a[1] + (n[1] - 1) * r[1], a[2] + (n[2] - 1) * r[2]);
44 long long termen;
45 long long ratie = r[1] * r[2] / gcd(r[1], r[2]);
46 int i;
47
48 for(i = 0; i < 1000000; i++)
49 {
50 termen = a[1] + i * r[1];
51 if(termen > maxi)
52 return 0;
53 if( (termen - a[2]) % r[2] == 0 )
54 {
55 break;
56 }
57 }
58
59 maxi = min(maxi, a[3] + (n[3] - 1) * r[3]);
60
61 if(termen > maxi || i == 1000000)
62 return 0;
63
64 for(i = 0; i < 1000000; i++)
65 {
66 if(i)
67 termen += ratie;
68 if(termen > maxi)
69 return 0;
70 if( (termen - a[3]) % r[3] == 0 )
71 {
72 return (maxi - termen) / (ratie * r[3] / gcd(r[3], ratie)) + 1;
73 }
74 }
75
76 return 0;
77 }
78
79 int main()
80 {
81 for(int i = 1; i <= 3; i++)
82 fin >> a[i] >> r[i] >> n[i];
83 fout << n[1] + n[2] + n[3] - inter2(1, 2) - inter2(1, 3) -
84 inter2(2, 3) + inter3() << ’\n’;
85
86 return 0;
87 }

Listing 21.3.2: pro3_CC1.cpp


CAPITOLUL 21. ONI 2019 315

1 // prof. Chesca Ciprian


2 // teoria numerelor cu simplificarea ratiei
3
4 #include <fstream>
5
6 using namespace std;
7
8 long long cmmdc(long long a, long long b)
9 {
10 long long d=a,i=b,r=d%i;
11 while (r)
12 {
13 d=i;
14 i=r;
15 r=d%i;
16 }
17 return i;
18 }
19
20 int main()
21 {
22 ifstream f("pro3.in");
23 ofstream g("pro3.out");
24
25 long long a1,r1,a2,r2,n1,n2,a3,r3,n3,a12,a13,a23,
26 r12,r13,r23,n12,n13,n23,r123,a123,n123;
27 long long an1,an2,an3,an12;
28
29 long long i,j,t,x0,y0,d;
30
31 f >> a1 >> r1 >> n1;
32 f >> a2 >> r2 >> n2;
33 f >> a3 >> r3 >> n3;
34
35 an1 = a1 + r1*(n1 - 1);
36 an2 = a2 + r2*(n2 - 1);
37 an3 = a3 + r3*(n3 - 1);
38
39 //-------------------------------------------------------------------
40 // determin cate elemente sunt comune sirurilor 1 si 2
41 // ecuatia r1*x - r2*y = (a2 - r2) - (a1 - r1)
42 // ecuatia are solutii daca cmmdc(r1,r2) divide (a2 - r2) - (a1 - r1)
43
44 d=cmmdc(r1,r2);
45 t = (a2 - r2) - (a1 - r1);
46 if (t%d==0) // ecuatia are solutii
47 {
48 //determin primul element comun celor doua siruri
49 i=1;j=1;x0=0;y0=0;
50 while (i<=n1 && j<=n2)
51 {
52 if (a1+r1*(i-1) < a2+r2*(j-1)) i++;
53 if (a1+r1*(i-1) > a2+r2*(j-1)) j++;
54 if (a1+r1*(i-1) == a2+r2*(j-1)) {x0 = i; y0 = j; break;}
55 }
56
57 if (x0 == 0) {a12 = 0; r12 = 0; n12 = 0;}
58 else
59 {
60 a12 = a1 + r1*(x0 - 1);
61 r12 = (r1*r2)/d;
62
63 if (an1 < an2)
64 n12 = (an1 - a12)/r12 + 1;
65 else
66 n12 = (an2 - a12)/r12 + 1;
67
68 an12 = a12 + r12*(n12 - 1);
69 //g<< x0<<" "<< y0 <<"\n";
70 }
71 }
72 else // ecuatia nu are solutii
73 {a12 = 0;r12 = 0;n12 = 0;}
74
75 //-------------------------------------------------------------------
76 // determin cate elemente sunt comune sirurilor 1 si 3
CAPITOLUL 21. ONI 2019 316

77 // ecuatia r1*x - r3*y = (a3 - r3) - (a1 - r1)


78 // ecuatia are solutii daca cmmdc(r1,r3) divide (a3 - r3) - (a1 - r1)
79
80 d=cmmdc(r1,r3);
81 t = (a3 - r3) - (a1 - r1);
82 if (t%d==0) // ecuatia are solutii
83 {
84 //determin primul element comun celor doua siruri
85 i=1;j=1;x0=0;y0=0;
86 while (i<=n1 && j<=n3)
87 {
88 if (a1+r1*(i-1) < a3+r3*(j-1)) i++;
89 if (a1+r1*(i-1) > a3+r3*(j-1)) j++;
90 if (a1+r1*(i-1) == a3+r3*(j-1)) {x0 = i; y0 = j; break;}
91 }
92 if (x0 == 0) {a13 = 0; r13 = 0; n13 = 0;}
93 else
94 {
95 a13 = a1 + r1*(x0 - 1);
96 r13 = (r1*r3)/d;
97 if (an1 < an3)
98 n13 = (an1 - a13)/r13 + 1;
99 else
100 n13 = (an3 - a13)/r13 + 1;
101
102 //g<< x0<<" "<< y0 <<"\n";
103 }
104 }
105 else // ecuatia nu are solutii
106 {a13 = 0;r13 = 0;n13 = 0;}
107
108 //-------------------------------------------------------------------
109 // determin cate elemente sunt comune sirurilor 2 si 3
110 // ecuatia r2*x - r3*y = (a3 - r3) - (a2 - r2)
111 // ecuatia are solutii daca cmmdc(r2,r3) divide (a3 - r3) - (a2 - r2)
112
113 d=cmmdc(r2,r3);
114 t = (a3 - r3) - (a2 - r2);
115 if (t%d==0) // ecuatia are solutii
116 {
117 //determin primul element comun celor doua siruri
118 i=1;j=1;x0=0;y0=0;
119 while (i<=n2 && j<=n3)
120 {
121 if (a2+r2*(i-1) < a3+r3*(j-1)) i++;
122 if (a2+r2*(i-1) > a3+r3*(j-1)) j++;
123 if (a2+r2*(i-1) == a3+r3*(j-1)) {x0 = i; y0 = j;break;}
124 }
125 if (x0 == 0) {a23 = 0; r23 = 0; n23 = 0;}
126 else
127 {
128 a23 = a2 + r2*(x0 - 1);
129 r23 = (r2*r3)/d;
130 if (an2 < an3)
131 n23 = (an2 - a23)/r23 + 1;
132 else
133 n23 = (an3 - a23)/r23 + 1;
134
135 //g<< x0<<" "<< y0 <<"\n";
136 }
137 }
138 else // ecuatia nu are solutii
139 {a23 = 0;r23 = 0;n23 = 0;}
140
141 //---------------------------------------------------
142 // determin cate elemente comune au cele 3 siruri
143 if (n12 && n23 && n13)
144 {
145 // prima progresie este a12,r12,n12 si a doua este a3,r3,n3
146 d=cmmdc(r12,r3);
147 t = (a3 - r3) - (a12 - r12);
148
149 if (t%d==0) // ecuatia are solutii
150 {
151 //determin primul element comun celor doua progresii
152 i=1;j=1;x0=0;y0=0;
CAPITOLUL 21. ONI 2019 317

153 while (i<=n12 && j<=n3)


154 {
155 if (a12+r12*(i-1) < a3+r3*(j-1)) i++;
156 if (a12+r12*(i-1) > a3+r3*(j-1)) j++;
157 if (a12+r12*(i-1) == a3+r3*(j-1)) {x0 = i; y0 = j; break;}
158 }
159
160 if (x0 == 0) {a123 = 0; r123 = 0; n123 = 0;}
161 else
162 {
163 // g << a12 << " " << r12 << " " << x0 <<" " << a123 << "\n";
164 a123 = a12 + r12*(x0 - 1);
165 r123 = (r12*r3)/d;
166 if (an12 < an3)
167 n123 = (an12 - a123)/r123 + 1;
168 else
169 n123 = (an3 - a123)/r123 + 1;
170 //g<< x0<<" "<< y0 <<"\n";
171 }
172 }
173 else
174 {a123 = 0; r123 = 0; n123 = 0;}
175 }
176 else
177 {a123 = 0; r123 = 0; n123 = 0;}
178
179 // aplic PINEX
180 //g<< " ------------------------------------------"<<"\n";
181
182 g << n1 + n2 + n3 - n12 - n13 - n23 + n123;
183
184 f.close();
185 g.close();
186
187 return 0;
188 }

Listing 21.3.3: pro3_CC2.cpp


1 // prof. Chesca Ciprian
2 // Teoria numerelor
3
4 #include <fstream>
5 #define int long long
6
7 using namespace std;
8
9 struct pro
10 {
11 long long a,r,n,an;
12 };
13
14
15 long long cmmdc(long long a, long long b)
16 {
17 long long d=a,i=b,r=d%i;
18 while (r)
19 {
20 d=i;
21 i=r;
22 r=d%i;
23 }
24 return i;
25 }
26
27 pro progresie(pro x, pro y)
28 {
29 // determin cate elemente sunt comune progresiilor x si y
30 // ecuatia r1*x - r2*y = (a2 - r2) - (a1 - r1)
31 // ecuatia are solutii daca cmmdc(r1,r2) divide (a2 - r2) - (a1 - r1)
32 // ratia progresie comune este r1*r2/cmmdc(r1,r2)
33
34 pro z;
35
36 long long d,t,i,j,x0;
CAPITOLUL 21. ONI 2019 318

37
38 d=cmmdc(x.r,y.r);
39 t = (y.a - y.r) - (x.a - x.r);
40 if (t%d==0) // ecuatia are solutii
41 {
42 //determin primul element comun celor doua siruri
43 i=1;j=1;x0=0;
44 while (i<=x.n && j<=y.n)
45 {
46 if (x.a+x.r*(i-1) < y.a+y.r*(j-1)) i++;
47 if (x.a+x.r*(i-1) > y.a+y.r*(j-1)) j++;
48 if (x.a+x.r*(i-1) == y.a+y.r*(j-1)) {x0 = i; break;}
49 }
50
51 if (x0 == 0) {z.a = 0; z.r = 0; z.n = 0;}
52 else
53 {
54 z.a = x.a + x.r*(x0 - 1);
55 z.r = (x.r*y.r)/d;
56
57 x.an=x.a+x.r*(x.n-1);
58 y.an=y.a+y.r*(y.n-1);
59
60 if (x.an < y.an)
61 z.n = (x.an - z.a)/z.r + 1;
62 else
63 z.n = (y.an - z.a)/z.r + 1;
64
65 z.an = z.a + z.r*(z.n - 1);
66 }
67 }
68 else // ecuatia nu are solutii
69 {z.a = 0;z.r = 0;z.n = 0;}
70
71 return z;
72 }
73
74 int32_t main()
75 {
76 ifstream f("pro3.in");
77 ofstream g("pro3.out");
78
79 pro x,y,z,xy,xz,yz,xyz;
80
81 f >> x.a >> x.r >> x.n;
82 f >> y.a >> y.r >> y.n;
83 f >> z.a >> z.r >> z.n;
84
85 xy = progresie(x,y);
86 xz = progresie(x,z);
87 yz = progresie(y,z);
88 xyz = progresie(xy,z);
89
90 // aplic PINEX
91 g << x.n + y.n + z.n - xy.n - xz.n - yz.n + xyz.n;
92
93 f.close();
94 g.close();
95
96 return 0;
97 }

Listing 21.3.4: pro3_LB1.cpp


1 // student Lucian Bicsi - Universitatea din Bucuresti - 100 puncte
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 struct Prog
8 {
9 long long a, r, n;
10 };
11
CAPITOLUL 21. ONI 2019 319

12 long long gcd(long long a, long long b)


13 {
14 while (b)
15 {
16 long long r = a % b;
17 a = b;
18 b = r;
19 }
20 return a;
21 }
22
23 Prog Merge(Prog p1, Prog p2)
24 {
25 if (p1.n == 0) return p1;
26 long long new_r = p1.r * p2.r / gcd(p1.r, p2.r);
27 for (int i = 0; i < p2.a + p2.r && i < p1.n; ++i)
28 {
29 long long x = p1.a + p1.r * i;
30 x -= p2.a;
31 if (x % p2.r == 0)
32 {
33 x /= p2.r;
34 if (x >= 0 && x < p2.n)
35 {
36 long long maxx = min(p1.a + p1.r * (p1.n - 1),
37 p2.a + p2.r * (p2.n - 1));
38 maxx -= p1.a + p1.r * i;
39 if (maxx < 0) break;
40 maxx /= new_r;
41 return {p1.a + p1.r * i, new_r, maxx + 1};
42 }
43 }
44 }
45 return {0, 0, 0};
46 }
47
48 ostream& operator<<(ostream& out, Prog prog)
49 {
50 out << "(" << prog.a << " + " << prog.r << " x " << prog.n << ")";
51 return out;
52 };
53
54 long long Calc(vector<Prog> progs)
55 {
56 // for (auto x : progs) cerr << x << " ";
57 Prog ret{0, 1, (long long)2e18};
58
59 for (auto prog : progs)
60 {
61 ret = Merge(ret, prog);
62 }
63 // cerr << " -> " << ret << endl;
64 return ret.n;
65 }
66
67 int main()
68 {
69 ifstream cin("pro3.in");
70 ofstream cout("pro3.out");
71
72 auto read = [&](){
73 Prog ret;
74 cin >> ret.a >> ret.r >> ret.n;
75 return ret;
76 };
77
78 Prog a = read(), b = read(), c = read();
79
80 cout << Calc({a}) + Calc({b}) + Calc({c}) -
81 Calc({a, b}) - Calc({a, c}) - Calc({b, c})
82 + Calc({a, b, c}) << endl;
83
84 return 0;
85 }
CAPITOLUL 21. ONI 2019 320

21.3.3 *Rezolvare detaliat 

21.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 aa 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
aaµ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 va-
15 3 5 15 7 63 21 125 loare 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

21.4.1 Indicaµii de rezolvare


Prof. Gh. Manolache - Colegiul Naµional de Informatic , Piatra Neamµ

Vom calcula exponenµii valorilor 2, 3, 5 ³i 7 din descompunerea în factori primi a ec 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 ecare 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 ecare cod câte perechi de poziµii cu acela³i cod exist . Pentru
ecare 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.
CAPITOLUL 21. ONI 2019 321

21.4.2 Cod surs 

Listing 21.4.1: cub_chiorean.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 ifstream fin("cub.in");
6 ofstream fout("cub.out");
7
8 int i, j, nr, n, x, mul, crtState[4], states[100];
9 long long sol;
10 const int divs[] = {2, 3, 5, 7};
11
12 int convert()
13 {
14 return crtState[3] * 3 * 3 * 3 + crtState[2] * 3 * 3 +
15 crtState[1] * 3 + crtState[0];
16 }
17
18 int main()
19 {
20 fin >> n;
21 for (i = 1 ; i <= n ; i++)
22 {
23 fin >> x;
24
25 for (j = 0 ; j < 4 ; j++)
26 {
27 nr = 0;
28 while (x % divs[j] == 0)
29 {
30 nr++;
31 x /= divs[j];
32 crtState[j]++;
33 }
34
35 crtState[j] %= 3;
36 }
37
38 states[convert()]++;
39 }
40 states[0]++;
41
42 for (i = 0 ; i < 81 ; i++)
43 sol += states[i] * (states[i] - 1) / 2;
44
45 fout << sol;
46
47 return 0;
48 }

Listing 21.4.2: cub_chiorean_brut.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 ifstream fin("cub.in");
6 ofstream fout("cub.out");
7
8 int i, j, nr, n, x, mul, crtState[4], v[1000000];
9 long long sol;
10 const int divs[] = {2, 3, 5, 7};
11
12 int convert()
13 {
14 return crtState[3] * 3 * 3 * 3 + crtState[2] * 3 * 3 +
15 crtState[1] * 3 + crtState[0];
16 }
17
18 int main()
CAPITOLUL 21. ONI 2019 322

19 {
20 fin >> n;
21 for (i = 1 ; i <= n ; i++)
22 {
23 fin >> x;
24
25 for (j = 0 ; j < 4 ; j++)
26 {
27 nr = 0;
28 while (x % divs[j] == 0)
29 {
30 nr++;
31 x /= divs[j];
32 crtState[j]++;
33 }
34
35 crtState[j] %= 3;
36 }
37
38 v[i] = convert();
39 }
40
41 for (i = 0 ; i < n ; i++)
42 for (j = i + 1 ; j <= n ; j++)
43 if (v[i] == v[j])
44 sol++;
45
46 fout << sol;
47
48 return 0;
49 }

Listing 21.4.3: cub_chiorean_n3.cpp


1 /**
2 Chiorean Tudor-Octavian
3 cub O(N^3)
4 */
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 ifstream fin("cub.in");
10 ofstream fout("cub.out");
11
12 int i, j, k, t, ok, nr, n, x, mul, state[4][1000000], crtState[4];
13 long long sol;
14 const int divs[] = {2, 3, 5, 7};
15
16 int main()
17 {
18 fin >> n;
19 for (i = 1 ; i <= n ; i++)
20 {
21 fin >> x;
22
23 for (j = 0 ; j < 4 ; j++)
24 {
25 nr = 0;
26 while (x % divs[j] == 0)
27 {
28 nr++;
29 x /= divs[j];
30 state[j][i]++;
31 }
32
33 state[j][i] %= 3;
34 }
35 }
36
37 for (i = 1 ; i < n ; i++)
38 for (j = i ; j <= n ; j++)
39 {
40 for (k = i ; k <= j ; k++)
41 {
CAPITOLUL 21. ONI 2019 323

42 for (t = 0 ; t < 4 ; t++)


43 {
44 crtState[t] += state[t][k];
45 }
46 ok = 1;
47 }
48
49 for (t = 0 ; t < 4 ; t++)
50 {
51 if (crtState[t] % 3 != 0)
52 {
53 ok = 0;
54 break;
55 }
56 }
57 sol += ok;
58
59 for (t = 0 ; t < 4 ; t++)
60 {
61 crtState[t] = 0;
62 }
63 }
64
65 fout << sol;
66
67 return 0;
68 }

Listing 21.4.4: cub100.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 ifstream f("cub.in");
6 ofstream g("cub.out");
7
8 unsigned long long e2,e3,e5,e7,v[1000002];
9 unsigned long long cod,sol,viz[82],x;
10 unsigned long long n,i,j,k,r,i1,j1,k1,r1,d[22],t[15],c[11],s[10],p,w;
11 unsigned long long ex2[1000002],ex3[1000002],ex5[1000002],ex7[1000002];
12
13 int main()
14 {
15 d[0]=1;
16 i=0;
17 while(d[i]<=1000000)
18 {
19 i++;
20 d[i]=d[i-1]*2;
21 }
22
23 i--;
24 t[0]=1;
25 j=0;
26 while(t[j]<=1000000)
27 {
28 j++;
29 t[j]=t[j-1]*3;
30 }
31
32 j--;
33 c[0]=1;
34 k=0;
35 while(c[k]<=1000000)
36 {
37 k++;
38 c[k]=c[k-1]*5;
39 }
40 k--;
41 s[0]=1;r=0;
42 while(s[r]<=1000000)
43 {
44 r++;
45 s[r]=s[r-1]*7;
CAPITOLUL 21. ONI 2019 324

46 }
47
48 r--;
49 //g<<"i2="<<i<<" i3="<<j<<" i5="<<k<<" i7="<<r<<"\n"; //19 12 8 7
50 // calculez numerele mai mici decat 1000000 ce sunt divizibile
51 // doar cu 2, 3 ,5 sau 7
52 for(i1=0;i1<=i;i1++)
53 for(j1=0;j1<=j;j1++)
54 for(k1=0;k1<=k;k1++)
55 for(r1=0;r1<=r;r1++)
56 {
57 p=d[i1]*t[j1]*c[k1]*s[r1];
58 if(p<=1000000)
59 {
60 ex2[p]=i1;
61 ex3[p]=j1;
62 ex5[p]=k1;
63 ex7[p]=r1;
64 //w++;
65 }
66 }
67
68 //g<<"nr numere ="<<w<<"\n";// 1273
69 // citesc numerele si formez exponentii cumulati
70 // ai lui 2, 3 ,5 si 7
71 e2=0;e3=0;e5=0;e7=0;
72 f>>n;
73 for(i=1;i<=n;i++)
74 f>>v[i];
75 for(i=1;i<=n;i++)
76 {
77 x=v[i]; ;
78 e2=(e2+ex2[x])%3;
79 e3=(e3+ex3[x])%3;
80 e5=(e5+ex5[x])%3;
81 e7=(e7+ex7[x])%3;
82 cod=e2*27+e3*9+e5*3+e7;//scriu in baza 3
83 viz[cod]++;
84 }
85
86 sol=viz[0];
87 for(i=0;i<=80;i++)
88 sol=sol+viz[i]*(viz[i]-1)/2;
89
90 g<<sol<<’\n’ ;
91 g.close();
92 return 0;
93 }

Listing 21.4.5: cub100_int.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 ifstream f("cub.in");
6 ofstream g("cub.out");
7
8 int e2,e3,e5,e7,v[1000002];
9 int cod,viz[82],x;
10 int n,i,j,k,r,i1,j1,k1,r1,d[22],t[15],c[11],s[10];
11 unsigned long long p,sol;
12 int ex2[1000002],ex3[1000002],ex5[1000002],ex7[1000002];
13
14 int main()
15 {
16 d[0]=1;
17 i=0;
18 while(d[i]<=1000000)
19 {
20 i++;
21 d[i]=d[i-1]*2;
22 }
23
24 i--;
CAPITOLUL 21. ONI 2019 325

25 t[0]=1;
26 j=0;
27 while(t[j]<=1000000)
28 {
29 j++;
30 t[j]=t[j-1]*3;
31 }
32
33 j--;
34 c[0]=1;
35 k=0;
36 while(c[k]<=1000000)
37 {
38 k++;
39 c[k]=c[k-1]*5;
40 }
41
42 k--;
43 s[0]=1;
44 r=0;
45 while(s[r]<=1000000)
46 {
47 r++;
48 s[r]=s[r-1]*7;
49 }
50 r--;
51 //g<<"i2="<<i<<" i3="<<j<<" i5="<<k<<" i7="<<r<<"\n"; //19 12 8 7
52 // calculez numerele mai mici decat 1000000 ce sunt divizibile
53 // doar cu 2, 3 ,5 sau 7
54 for(i1=0;i1<=i;i1++)
55 for(j1=0;j1<=j;j1++)
56 for(k1=0;k1<=k;k1++)
57 for(r1=0;r1<=r;r1++)
58 {
59 p=1ll*d[i1]*t[j1]*c[k1]*s[r1];
60 if(p<=1000000)
61 {
62 ex2[p]=i1;
63 ex3[p]=j1;
64 ex5[p]=k1;
65 ex7[p]=r1;
66 //w++;
67 }
68 }
69 //g<<"nr numere ="<<w<<"\n";// 1273
70 // citesc numerele si formez exponentii cumulati
71 // ai lui 2, 3 ,5 si 7
72
73 e2=0; e3=0; e5=0; e7=0;
74 f>>n;
75 for(i=1;i<=n;i++)
76 f>>v[i];
77
78 for(i=1;i<=n;i++)
79 {
80 x=v[i]; ;
81 e2=(e2+ex2[x])%3;
82 e3=(e3+ex3[x])%3;
83 e5=(e5+ex5[x])%3;
84 e7=(e7+ex7[x])%3;
85 cod=e2*27+e3*9+e5*3+e7;//scriu in baza 3
86 viz[cod]++;
87 }
88
89 sol=viz[0];
90 for(i=0;i<=80;i++)
91 sol=sol+viz[i]*(viz[i]-1)/2;
92
93 g<<sol<<’\n’ ;
94 g.close();
95 return 0;
96 }
CAPITOLUL 21. ONI 2019 326

21.4.3 *Rezolvare detaliat 

21.5 bofrac
Problema 5 - bofrac 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
Fi³ierul de intrare bofrac.in conµine pe prima linie num rul N .

Date de ie³ire
Fi³ierul de ie³ire bofrac.out va conµine pe prima linie num rul F ,cu semnicaµia de mai sus.

Restricµii ³i preciz ri
a Pentru teste în valoare de 24 puncte, 0 $ N $ 80
a Pentru teste în valoare de 40 puncte, 0 $ N $ 1101
a Pentru teste în valoare de 56 puncte, 0 $ N $ 50001
a Pentru teste în valoare de 100 puncte, 0 $ N $ 1000000
a Dou  fracµii ireductibile a©b ³i c©d sunt diferite dac  a j c sau b j d.
0&F $2
63
a

Exemple
bofrac.in bofrac.out Explicaµii
7 14 N=7; Primii 7 termeni ai ³irului Fibonacci sunt: 1, 1, 2, 3, 5, 8,
13 Se pot forma 14 fracµii diferite ireductibile subunitare:
, , , , 1 , 2, 2, 2 , 3, 3, 3 , 5, 5 , 8 .
1 1 1 1
2 3 5 8 13 3 5 13 5 8 13 8 13 13
2019 1547722 Se pot forma 1547722 fracµii diferite ireductibile subunitare uti-
lizând primii 2019 termeni ai ³irului Fibonacci.
500000 94988288219 Se pot forma 94988288219 fracµii diferite ireductibile subunitare
utilizând primii 500000 termeni ai ³irului Fibonacci.

Timp maxim de executare/test: Windows: 0.1 secunde, Linux: 0.1 secunde


Memorie: total 64 MB
Dimensiune maxim  a sursei: 10 KB

21.5.1 Indicaµii de rezolvare


Prof. Che³c  Ciprian - Liceul Tehnologic “Grigore C. Moisil Buz u

Soluµia 1
Gener m termenii ³irului Fibonacci ³i form m toate perechile posibile F a©F b cu F a $ F b.
Pentru ecare 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  e 1, rezult  c  F a, b poate  F 1 sau F 2, pentru c  F 1 1
CAPITOLUL 21. ONI 2019 327

³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  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 .

21.5.2 Cod surs 

Listing 21.5.1: bofrac_CC1.cpp


1 // Brute force
2 // O(n^2)
3
4 #include <fstream>
5
6 using namespace std;
7
8 long long fib[1000001];
9
10 long long cmmdc(long long a, long long b)
11 {
12 long long d=a,i=b,r=d%i;
13 while (r)
14 {
15 d=i;
16 i=r;
17 r=d%i;
18 }
19 return i;
20 }
21
22 int main()
23 {
24 long long i,j,n,sol;
25
26 ifstream f("fibofrac.in");
27 ofstream g("fibofrac.out");
28
29 f >> n;
30
31 fib[1]=1;fib[2]=1;
32 for(i=3;i<=n;i++)
33 fib[i]=fib[i-1]+fib[i-2];
34
35 sol=0;
36 for(i=2;i<n;i++)
37 for(j=i+1;j<=n;j++)
38 if (cmmdc(fib[i],fib[j])==1) sol++;
39 g<<sol<<" ";
40
41 f.close();
42 g.close();
43
44 return 0;
45 }

Listing 21.5.2: bofrac_CC2.cpp


1 // prof. Chesca Ciprian
2 // Complexitate O(nlog(n))
CAPITOLUL 21. ONI 2019 328

3
4 #include <fstream>
5 #define nmax 1000003
6
7 using namespace std;
8
9 int n,i,j;
10
11 long long T=0;
12 int fi[nmax];
13
14 ifstream f("fibofrac.in");
15 ofstream g("fibofrac.out");
16
17 int main()
18 {
19
20 f>>n;
21
22 // calculez fi(i)
23 for (i=1;i<=n;i++)
24 fi[i] = i-1;
25
26 fi[1]=1;
27 for (i=2;i<=n;i++)
28 for (j=i+i;j<=n;j+=i)
29 fi[j] -= fi[i];
30
31 for(i=1;i<=n/2;i++)
32 T+=2*fi[i];
33
34 for(i=n/2+1;i<=n;i++)
35 T+=fi[i];
36
37 g<<T - (n-2) - 3<<"\n";
38
39 f.close();
40 g.close();
41
42 return 0;
43 }

Listing 21.5.3: bofrac_CC3.cpp


1 // Brute force
2 // O(n^2) fara generarea termenilor sirului Fibonacci
3
4 #include <fstream>
5
6 using namespace std;
7
8 long long cmmdc(long long a, long long b)
9 {
10 long long d=a,i=b,r=d%i;
11 while (r)
12 {
13 d=i;
14 i=r;
15 r=d%i;
16 }
17 return i;
18 }
19
20 int main()
21 {
22 long long i,j,n,sol;
23 ifstream f("fibofrac.in");
24 ofstream g("fibofrac.out");
25 f >> n;
26
27 sol=0;
28 for(i=2;i<n;i++)
29 for(j=i+1;j<=n;j++)
30 if (cmmdc(i,j)==1 || cmmdc(i,j)==2) sol++;
31 g<<sol<<" ";
CAPITOLUL 21. ONI 2019 329

32
33 f.close();
34 g.close();
35
36 return 0;
37 }

Listing 21.5.4: bofrac_CC4.cpp


1 // prof. Chesca Ciprian
2 // O(nsqrt(n))
3
4 #include <fstream>
5 #define nmax 1000003
6
7 using namespace std;
8
9 long long T=0;
10 int fi[nmax],t;
11
12 char v[nmax];
13 int prime[nmax/5],nr=0;
14 int b[nmax/5],nf=0;
15
16 // Ciurul lui Eratostene
17 void ciur(int n)
18 {
19 int i, j;
20 for (i = 2; i <= n; ++i)
21 {
22 if (v[i] == 0)
23 {
24 prime[++nr]=i;
25 for (j = i + i; j <= n; j += i)
26 {
27 v[j] = 1;
28 }
29 }
30 }
31 }
32
33 //descompun in factori primi
34 void desc(int n)
35 {
36 int i=1,j=0,f;
37 while (n>1)
38 {
39 f=0;
40 while (n%prime[i]==0)
41 {
42 f++;
43 n=n/prime[i];
44 }
45
46 if (f)
47 {
48 b[++j]=prime[i];
49 }
50 else
51 i++;
52 }
53 nf=j;
54 }
55
56 int main()
57 {
58 ifstream f("fibofrac.in");
59 ofstream g("fibofrac.out");
60
61 int n,i,j;
62 f>>n;
63 ciur(n);
64
65 // calculez fi(i) in O(sqrt(n))
66 for(i=1;i<=n;i++)
CAPITOLUL 21. ONI 2019 330

67 {
68 fi[i]=i;
69 desc(i);
70 for(j=1;j<=nf;j++)
71 {
72 fi[i]=fi[i]/b[j];
73 fi[i]=fi[i]*(b[j]-1);
74 }
75 }
76
77 for(i=1;i<=n/2;i++)
78 T+=2*fi[i];
79
80 for(i=n/2+1;i<=n;i++)
81 T+=fi[i];
82
83 g<<T - (n-2) - 3<<"\n";
84
85 f.close();
86 g.close();
87
88 return 0;
89 }

Listing 21.5.5: bofrac_CC5.cpp


1 // prof. Chesca Ciprian
2 // Complexitate O(nlog(n))
3
4 #include <fstream>
5 #include <cassert>
6 #define nmax 1000003
7
8 using namespace std;
9
10 int n,i,j;
11
12 long long T=0;
13 int fi[nmax];
14
15 ifstream f("fibofrac.in");
16 ofstream g("fibofrac.out");
17
18 int main()
19 {
20 f>>n;
21 assert(n>0 && n < 10000001);
22
23 // calculez fi(i)
24 for (i=1;i<=n;i++)
25 fi[i] = i-1;
26
27 fi[1]=1;
28 for (i=2;i<=n;i++)
29 for (j=i+i;j<=n;j+=i)
30 fi[j] -= fi[i];
31
32 for(i=1;i<=n/2;i++)
33 T+=2*fi[i];
34
35 for(i=n/2+1;i<=n;i++)
36 T+=fi[i];
37
38 g<<T - (n-2) - 3<<"\n";
39
40 f.close();
41 g.close();
42
43 return 0;
44 }

Listing 21.5.6: bofrac_TC1.cpp


1 // student Tudor Chiorean - Universitatea Tehnica din Cluj
CAPITOLUL 21. ONI 2019 331

2 // 100 puncte
3
4 #include <bits/stdc++.h>
5 using namespace std;
6
7 #define NMAX 1000005
8
9 ifstream fin("fibofrac.in");
10 ofstream fout("fibofrac.out");
11
12 int phi[NMAX], n, i, j;
13 long long ans;
14
15 int main()
16 {
17 fin >> n;
18 for (int i = 1; i <= n; ++i)
19 phi[i] = i-1;
20 for (int i = 2; i <= n; ++i)
21 for (int j = 2 * i; j <= n; j += i)
22 phi[j] -= phi[i];
23
24 for (i = 1 ; i <= n ; i++)
25 ans += ((i <= (n / 2)) ? 2 : 1 ) * phi[i];
26
27 ans -= n - 1;
28
29 fout << ans;
30
31 return 0;
32 }

21.5.3 *Rezolvare detaliat 

21.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  r.
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, ecare 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 ecare 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  modicat 
pe parcursul jocului. Staµiile sunt conectate la aceea³i surs  de alimentare care are B unit µi de
energie.
î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  ae 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 a  un alt copil.
Cerinµe
CAPITOLUL 21. ONI 2019 332

1. Care este durata minim  a jocului, dac  Dorel nu ia parte la joc?


2. Care este durata minim  a jocului, dac  Dorel ia parte la joc ³i se poziµioneaz  în mod
optim pentru a minimiza durata jocului?

Date de intrare
Fi³ierul de intrare telefon.in conµine pe prima linie dou  numere naturale N ³i B cu semni-
caµia din enunµ. Pe cea de-a doua linie se a  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.

Restricµii ³i preciz ri
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

a Pentru alte teste în valoare de 35 puncte N & 10 , B & 10


3 4

a Pentru alte teste în valoare de 20 puncte N & 10 , B & 10


5 5

a Pentru alte teste în valoare de 30 puncte N & 10 , B & 10


5 9

Exemple
telefon.in telefon.out Explicaµii
6 15 86 N=6, B=15
7 9 12 16 21 27 X[1-6]=[7,9,12,16,21,27]

1.Dac  Dorel nu particip  la joc atunci copiii vor alege raza


de acµiune R=5 ³i al 2-lea, al 3-lea ³i al 4-lea copil vor folosi
sistemul de comunicare. în consecinµ  durata jocului va  (9-
7)+(27-21)= 2+6 = 8
2.Dac  Dorel particip  la joc se va poziµiona la distanµa 26 faµ 
de origine.
în aceast  situaµie copiii vor alege raza de acµiune R tot 5 ³i al 3-
lea, al 4-lea ³i al 5-lea copil vor folosi sistemul de comunicare. în
consecinµ  durata jocului va  (9-7)+(12-9)+(27-26) = 2+3+1
=6

Timp maxim de executare/test: Windows: 1.0 secunde, Linus: 0.2 secunse


Memorie: total 64 MB
Dimensiune maxim  a sursei: 10 KB

21.6.1 Indicaµii de rezolvare


student Alex Turdean - Universitatea Tehnic  din Cluj Napoca

Pentru a rezolva cerinµa 1 e sucient s  consider m c  R poate lua valori doar din mulµimea
distanµelor dintre oricare 2 copii.
CAPITOLUL 21. ONI 2019 333

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 xat  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 ecare valoare a lui R în
intervalul 1, B  c ut m cea mai mare distanµ  mai mic  sau egal  cu R ³i vom verica 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 xat  a lui R este sucient 
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 ecare dat  când incrementam pointerul din dreapta vom verica 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 
Soluµie - 100 puncte
Continuând soluµia anterioar , algoritmul optim nu va lua în considerare
pentru R toate valorile din intervalul 1, B  ci doar valorile din mulµimea
r1, 2, 3, 4, 5, ...., sqrt B , B, B ©2, B ©3, B ©4, B ©5, .., B ©sqrt B x. Astfel reducând cardinalul
mulµimii de valori pentru R de la B la 2 ˜ sqrt B .
Complexitate: O N ˜ log N   sqrt B 
Soluµie alternativ  - 100 puncte
O mic  optimizare a soluµiei anterioare const  în observaµia c  nu sunt necesare decât cei mai
mari N candidaµi dintre cei 2sqrt B  (în esenµ , B , B ©2, B ©3, ..., B ©N ). Astfel se obµine o soluµie
de complexitate O N log N , independent  de B .

21.6.2 Cod surs 

Listing 21.6.1: alex.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <algorithm>
4 #include <vector>
5 #define int long long
6
7 using namespace std;
8
9 vector<long long> v,dist,vals;
10
11 long long total,n,m,current,best,nokid;
12
13 int32_t main()
14 {
15 ifstream cin("telefon.in");
CAPITOLUL 21. ONI 2019 334

16 ofstream cout("telefon.out");
17
18 cin >> n >> m;
19 for(int i = 1; i <= n; i++)
20 {
21 int a;
22 cin >> a;
23 v.push_back(a);
24 }
25
26 for(int i = 0; i < v.size() - 1; i++)
27 {
28 dist.push_back(v[i + 1] - v[i]);
29 vals.push_back(v[i + 1] - v[i]);
30 total += v[i + 1] - v[i];
31 }
32
33 sort(dist.begin(), dist.end());
34
35 int i;
36 for(i = 1; i*i <= m; i++)
37 vals.push_back(i),vals.push_back(m/i);
38
39 vals.push_back(i), vals.push_back(i + 1);
40
41 sort(vals.begin(), vals.end());
42
43 int first = 0;
44 int semnal = 0;
45 int second = 0;
46 int extra = 0;
47 current = dist[0];
48
49 while(first < dist.size())
50 {
51 while(semnal + 1 < vals.size() && vals[semnal] < dist[first])
52 semnal++;
53
54 if(vals[semnal] < dist[first])
55 break;
56
57 long long delta = vals[semnal];
58
59 while((first - second + 1) * delta > m)
60 current -= dist[second], second++;
61
62 while(extra+1 < dist.size() && dist[extra+1] - delta <= delta)
63 extra++;
64
65 if(extra == first && extra + 1 < dist.size())
66 extra++;
67
68 int aux = second;
69 long long size = (first-second+1) * delta;
70 if(extra != first)
71 {
72 long long z = current;
73 long long plus = dist[extra] - delta;
74
75 if(size + delta <= m)
76 size += delta, z += delta;
77 else
78 if(aux <= first)
79 z += delta - dist[aux], aux++;
80
81 if(size + delta <= m)
82 size += plus, z += delta;
83 else
84 if(aux <= first && dist[aux] < plus)
85 z += plus - dist[aux];
86
87 best = max(z, best);
88 }
89
90 nokid = max(current, nokid);
91 best = max(current, best);
CAPITOLUL 21. ONI 2019 335

92 if(first + 1 == dist.size())
93 first++;
94 else
95 if(semnal+1 < vals.size() && vals[semnal+1] < dist[first+1])
96 semnal++;
97 else
98 first++,current+= dist[first];
99 }
100
101 long long z = 0;
102 cout<<max(z,total-nokid) << ’ ’;
103 cout<<max(z,total-best) << ’\n’;
104 }

Listing 21.6.2: telefon-adrian-100.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <vector>
4 #include <algorithm>
5 #include <climits>
6
7 #define int int64_t
8
9 using namespace std;
10
11 int32_t main()
12 {
13 ifstream cin("telefon.in");
14 ofstream cout("telefon.out");
15
16 int N, B; cin >> N >> B;
17
18 vector<int> X(N);
19
20 for (int i = 0; i < N; ++i)
21 {
22 cin >> X[i];
23 }
24
25 for (int i = N - 1; i > 0; --i)
26 X[i] -= X[i - 1];
27
28 X.erase(X.begin());
29 --N;
30
31 sort(X.begin(), X.end());
32
33 vector<int> partial(X.size());
34
35 for (int i = 0; i < int(X.size()); ++i)
36 {
37 partial[i] += X[i];
38 if (i > 0)
39 partial[i] += partial[i - 1];
40 }
41
42 int answer = numeric_limits<int>::max();
43
44 for (int i = 0; i < int(X.size()); ++i)
45 {
46 // this is max
47 int can_take = B / X[i];
48 int need = partial.back() - partial[i];
49 if (can_take <= i)
50 need += partial[i - can_take];
51 answer = min(answer, need);
52 }
53
54 cout << answer << " ";
55
56 answer = numeric_limits<int>::max();
57 N = X.size();
58 for (int cover = N + 1, end = -1, magic = 0; cover > 0; --cover)
59 {
CAPITOLUL 21. ONI 2019 336

60 int max_cover = B / cover;


61 // 3 ways
62 // we cover cover of already existant values
63 // we cover cover - 1 of already existent values + some part
64 // of the biggest
65 // we cover cover - 2 of already + the biggest <= 2 * max_cover
66
67 while (end + 1 < N && X[end + 1] <= max_cover)
68 {
69 ++end;
70 }
71
72 if (end == -1)
73 continue;
74
75 while (magic + 1 < N && X[magic + 1] <= 2 * max_cover)
76 ++magic;
77
78 // ignore Dorel
79 int now = partial[N - 1] - partial[end];
80 if (end - cover >= 0)
81 now += partial[end - cover];
82
83 answer = min(answer, now);
84
85 // Dorel once
86 if (cover > 0)
87 {
88 now = partial[N - 1] - partial[end];
89 if (end - (cover - 1) >= 0)
90 now += partial[end - (cover - 1)];
91
92 if (end < N - 1)
93 now -= min(max_cover, X[N - 1]);
94
95 answer = min(answer, now);
96 }
97
98 // Dorel twice
99 if (cover > 1)
100 {
101 now = partial[N - 1] - partial[end];
102 if (end - (cover - 2) >= 0)
103 now += partial[end - (cover - 2)];
104
105 if (end < magic)
106 now -= X[magic];
107
108 answer = min(answer, now);
109 }
110 }
111
112 cout << answer << "\n";
113 }

Listing 21.6.3: telefon-bicsi-100.cpp


1 #include <bits/stdc++.h>
2
3 #define int long long
4
5 using namespace std;
6
7 pair<long long, long long> Solve(int v, vector<int> dists, set<string> opts)
8 {
9 int n = dists.size();
10 sort(dists.begin(), dists.end());
11 long long sum = 0;
12 for (auto x : dists)
13 sum += x;
14
15
16 int magic = sqrt(v) + 2;
17 vector<int> cands = dists;
18
CAPITOLUL 21. ONI 2019 337

19 if (!opts.count("cands_are_dists"))
20 {
21 cands.clear();
22 for (int i = 1; i <= magic && i <= v; ++i)
23 {
24 cands.push_back(i);
25 cands.push_back(v / i);
26 }
27
28 sort(cands.rbegin(), cands.rend());
29
30 for (auto& x : cands)
31 x = v / x;
32 }
33 else
34 {
35 for (auto x : dists)
36 if (x > 1)
37 cands.push_back(x / 2);
38
39 sort(cands.begin(), cands.end());
40 }
41
42 vector<long long> bests(3, 0);
43
44 long long now = 0;
45 int l = 0, r = 0, p = -1;
46
47 for (auto delta : cands)
48 {
49 // bests[0] = bests[1] = bests[2] = 0;
50
51 int phones = v / delta;
52 while (r < n && dists[r] <= delta)
53 {
54 now += dists[r];
55 ++r;
56 }
57
58 while (r - l > phones)
59 {
60 now -= dists[l];
61 ++l;
62 }
63
64 bests[0] = max(bests[0], now);
65 if (phones >= 1 && r < n)
66 {
67 long long xnow = now + delta;
68 if (r - l == phones) xnow -= dists[l];
69 bests[1] = max(bests[1], xnow);
70 }
71
72 while (p + 1 < n && dists[p + 1] <= 2 * delta)
73 ++p;
74
75 if (phones >= 2 && p >= r)
76 {
77 long long xnow = now + dists[p];
78 if (r - l >= phones - 1) xnow -= dists[l];
79 if (r - l == phones) xnow -= dists[l + 1];
80 bests[2] = max(bests[2], xnow);
81 }
82
83 /*
84 for (auto x : dists) cerr << x << " "; cerr << endl;
85 cerr << "delta: " << delta << endl;
86 cerr << "(phones: " << phones << ")" << endl;
87 cerr << "bests: " << bests[0] << " " << bests[1]
88 << " " << bests[2] << endl;
89 cerr << endl;
90 */
91 }
92
93 long long ans = 0;
94
CAPITOLUL 21. ONI 2019 338

95 ans = max(ans, bests[0]);


96 ans = max(ans, bests[1]);
97 ans = max(ans, bests[2]);
98
99 return make_pair(sum - bests[0], sum - ans);
100 }
101
102 int32_t main()
103 {
104 ifstream cin("telefon.in");
105 ofstream cout("telefon.out");
106
107 int n, v; cin >> n >> v;
108 int last = -1;
109
110 vector<int> dists;
111
112 for (int i = 0; i < n; ++i)
113 {
114 int x; cin >> x;
115 if (i) dists.push_back(x - last);
116 last = x;
117 }
118
119 auto p = Solve(v, dists, {});
120 cout << p.first << " " << p.second << endl;
121
122 return 0;
123 }

Listing 21.6.4: tudor_telefon_v2.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 #define int long long
6
7 ifstream fin("telefon.in");
8 ofstream fout("telefon.out");
9
10 int n, b, i, x, y, total;
11
12 vector<int> d, signals;
13
14 void subtask()
15 {
16 int ls = 0, ld = 0, best = 0, saved = 0;
17 while (ld < d.size() && d[ld] <= b)
18 {
19 saved += d[ld++];
20 while ( (ld - ls) * d[ld - 1] > b )
21 saved -= d[ls++];
22
23 best = max(best, saved);
24 }
25
26 fout << total - best << ’ ’;
27 }
28
29 void solve()
30 {
31 int ls = 0, ld = 0, split = 0, bonus[3], saved = 0, best = 0;
32
33 for (int k = 0 ; k < signals.size() ; k++) {
34 int signalSize = signals[k];
35
36 while (ld < d.size() && d[ld] <= signals[k])
37 ld++; /// Fixate ld
38 ls = max(0LL, ld - (b / signals[k])); /// Fixate ls
39
40 split = ld + 1;
41 while (split < d.size() && d[split] <= 2 * signals[k])
42 split++; /// Fixate split
43
CAPITOLUL 21. ONI 2019 339

44 bonus[0] = bonus[1] = 0;
45 if (split > 0 && ld != d.size())
46 {
47 bonus[0] = min(signals[k], d[split - 1]);
48 bonus[1] = max(d[split - 1] - signals[k], 0LL);
49 if (bonus[1] > signalSize) bonus[1] = 0;
50 }
51
52 saved = 0;
53 int spaceLeft = b / signalSize - (ld - ls);
54 if (spaceLeft <= 0)
55 {
56 for (int r = ls ; r < ld ; r++)
57 {
58 if (r - ls == 0 || r - ls == 1)
59 saved += (bonus[r - ls] > d[r] ? bonus[r - ls] : d[r]);
60 else
61 saved += d[r];
62 }
63 }
64 else if (spaceLeft == 1)
65 {
66 for (int r = ls ; r < ld ; r++)
67 {
68 if (r - ls == 0)
69 saved += (bonus[1] > d[r] ? bonus[1] : d[r]);
70 else
71 saved += d[r];
72 }
73
74 saved += bonus[0];
75 }
76 else
77 {
78 for (int r = ls ; r < ld ; r++)
79 saved += d[r];
80
81 saved += bonus[0] + bonus[1];
82 }
83
84 best = max(best, saved);
85 }
86
87 fout << total - best;
88 }
89
90 int32_t main()
91 {
92 fin >> n >> b;
93 fin >> x;
94
95 for (i = 2 ; i <= n ; i++)
96 {
97 fin >> y;
98 d.push_back(y - x);
99 total += y - x;
100
101 x = y;
102 }
103
104 sort(d.begin(), d.end());
105 for (i = 0 ; i < d.size() ; i++)
106 {
107 cout << d[i] << ’ ’;
108 }
109
110 for (i = 1 ; i <= sqrt(b) ; i++)
111 signals.push_back(i);
112
113 for (i = int32_t(sqrt(b)) ; i >= 1 ; i--)
114 signals.push_back(b / i);
115
116 subtask();
117 solve();
118
119 return 0;
CAPITOLUL 21. ONI 2019 340

120 }

21.6.3 *Rezolvare detaliat 


Capitolul 22

ONI 2018

22.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  descompus cu ajutorul numerelor factoriale astfel:

N 1!f1  2!f2  3!f3  ...  m!fm

unde coecienµ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  descompu-
nere î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
Fi³ierul de intrare este bazaf.in
Acesta conµine pe primul rând un num r natural V care poate avea doar valorile 1 sau 2 cu
urm toarea semnicaµie:
a dac  valoarea lui V este 1, pe a doua linie a ³ierului de intrare se g se³te un num r natural
X cu semnicaµia de mai sus;
a dac  valoarea lui V este 2, pe a doua linie a ³ierului de intrare se g se³te o descompunere
a unui num r Y sub forma unui ³ir de valori naturale în care primul termen este m, urmat de m
valori fi , care respect  condiµiile fi ' 0, cu 1 & i $ m ³i fm j 0, desp rµite prin câte un spaµiu, cu
semnicaµia de mai sus

Date de ie³ire
Fi³ierul de ie³ire este bazaf.out
Dac  valoarea V este 1 atunci ³ierul de ie³ire va conµine descompunerea în baza factorial  a
num rului X iar dac  valoarea V este 2 atunci ³ierul de ie³ire va conµine descompunerea în baza
factorial  a num rului Y .
Descompunerea în baz  factorial  presupune scrierea în ³ierul de ie³ire a unei singure linii
sub forma unui ³ir de valori naturale în care primul termen este m, urmat de m valori fi , care
respect  condiµiile 0 & fi & i, cu 1 & i $ m ³i 0 $ fm & m, desp rµite prin câte un spaµiu, având
semnicaµia de mai sus.

Restricµii ³i preciz ri

341
CAPITOLUL 22. ONI 2018 342

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:
bazaf.in bazaf.out Explicaµii
1 3122 V 1, deci se rezolv  doar prima cerinµ 
17 X 17
Descompunerea num rului X 17 în baz  factorial  conµine 3
termeni ³i este format  din coecienµii 1, 2, 2 (17 = 1! 1 + 2!
2 + 3! 2);
2 2 10 5 3013 V 2, deci se rezolv  doar a doua cerinµ 
Descompunerea 2 10 5 este o descompunere cu 2 termeni având
coecienµii 10, 5 ³i corespunde num rului Y 20.
Descompunerea în baz  factorial  a num rului
Y = 20 va  3 0 1 3 (20 = 1! 0 + 2! 1 + 3! 3)
Timp maxim de executare/test: 0.8 secunde
Memorie: total 128 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

22.1.1 Indicaµii de rezolvare

Prof. Che³c  Ciprian - Liceul Tehnologic Grigore C. Moisil Buz u


Student Posd r scu Daniel, Universitatea Bucure³ti

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  coecientul acelui termen, se scade din num rul dat termenul respectiv,
se memoreaz  în vectorul de descompuneri coecientul 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 coecient este mai mare decât limita impus  vom calcula cât din valoarea
respectiv  va  transportat  c tre urm torul coecient.
Se actualizeaz  astfel atât coecientul curent cât ³i cel urm tor ³i în cazul c nd ultimul coe-
cient este mai mare decât limita impus  se adaug  noi coecienµ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

22.1.2 Cod surs 

Listing 22.1.1: 1_bazaf.cpp


1 // student Posdarascu Daniel
2
3 #include<stdio.h>
CAPITOLUL 22. ONI 2018 343

4 #include<algorithm>
5 #include<vector>
6 #include<cassert>
7
8 using namespace std;
9
10 #define NMAX 100005
11
12 long long n;
13 int v[NMAX], p;
14
15 void findBazaFactoriala(long long n)
16 {
17 long long fact = 1, k;
18 for(k = 1; ; k++)
19 {
20 if(fact * (k + 1) > n)
21 break;
22 fact *= (k + 1);
23 }
24
25 vector<int> answer;
26
27 for(; k >= 1; k--)
28 {
29 answer.push_back(n / fact);
30 n %= fact;
31 fact /= k;
32 }
33
34 int lim = answer.size();
35 printf("%d ", lim);
36
37 for(int i = lim - 1; i >= 0; i--)
38 printf("%d ", answer[i]);
39 printf("\n");
40 }
41
42 void solve1()
43 {
44 scanf("%lld",&n);
45 assert(1 <= n && n <= 1LL * 1000000000000000);
46 findBazaFactoriala(n);
47 }
48
49 void solve2()
50 {
51 scanf("%lld",&n);
52 assert(1 <= n && n <= 100000);
53 for(int i = 1; i <= n; i++)
54 {
55 assert(0 <= v[i] && v[i] <= 1000000000);
56 scanf("%d",&v[i]);
57 }
58
59 for(long long i = 1; i <= n || v[i] > 0; i++)
60 {
61 v[i + 1] += v[i] / (i + 1);
62 v[i] %= (i + 1);
63 n = max(n, i);
64 }
65
66 printf("%lld ", n);
67 for(int i = 1; i <= n; i++)
68 {
69 printf("%d ", v[i]);
70 }
71 printf("\n");
72 }
73
74 int main ()
75 {
76 freopen("bazaf.in","r",stdin);
77 freopen("bazaf.out","w",stdout);
78
79 scanf("%d", &p);
CAPITOLUL 22. ONI 2018 344

80 assert(1 <= p && p <= 2);


81 if(p == 1)
82 {
83 solve1();
84 }
85 else
86 {
87 solve2();
88 }
89
90 return 0;
91 }

Listing 22.1.2: 2_bazaf.cpp


1 // prof. Chesca Ciprian
2
3 #include<fstream>
4 #include<iostream>
5
6 using namespace std;
7
8 ifstream fin("bazaf.in");
9 ofstream fout("bazaf.out");
10
11 #define ll long long
12
13 int nx,m,i;
14 ll v,x[21],fi[100005];
15 ll f[21],p,c,r;
16
17 int main()
18 {
19 f[0]=1;
20 for (i=1;i<=20;i++)
21 {
22 f[i]=f[i-1]*i;
23 }
24
25 fin>>p;
26
27 if (p==1)
28 {
29
30 fin >> v;
31 nx=0;
32 for(int j=20;j>=1;j--)
33 {
34 x[j]=v/f[j];
35 if(x[j]>0 && nx==0)
36 {
37 nx=j;
38 }
39 v=v%f[j];
40 }
41
42 fout<<nx;
43 for(int j=1;j<=nx;j++)
44 {
45 fout<<" "<<x[j];
46 }
47 }
48
49 if (p==2)
50 {
51 fin >> m;
52
53 for(i=1;i<=m;i++)
54 fin >> fi[i];
55
56 for(i=1;i<=m-1;i++)
57 {
58 c=fi[i]/(i+1);
59 r=fi[i]%(i+1);
60 fi[i]=r;
CAPITOLUL 22. ONI 2018 345

61 fi[i+1]+=c;
62 }
63
64 while (fi[m]>m)
65 {
66 c=fi[m]/(m+1);
67 r=fi[m]%(m+1);
68 fi[m]=r;
69 fi[++m]+=c;
70 }
71
72 fout << m <<" ";
73 for(i=1;i<=m;i++)
74 fout << fi[i]<<" ";
75 fout << "\n";
76 }
77
78 fout.close();
79 fin.close();
80 return 0;
81 }

Listing 22.1.3: 3_bazaf.cpp


1 // student Posdarascu Daniel - brute force
2 #include<stdio.h>
3 #include<cassert>
4 #include<algorithm>
5 #include<vector>
6
7 using namespace std;
8
9 #define NMAX 100005
10
11 long long n;
12 long long v[NMAX], p;
13
14 void findBazaFactoriala(long long n)
15 {
16 long long fact = 1, k;
17 for(k = 1; ; k++)
18 {
19 if(fact * (k + 1) > n)
20 break;
21 fact *= (k + 1);
22 }
23
24 vector<int> answer;
25
26 for(; k >= 1; k--)
27 {
28 answer.push_back(n / fact);
29 n %= fact;
30 fact /= k;
31 }
32
33 long long lim = answer.size();
34 printf("%d ", lim);
35 for(int i = lim - 1; i >= 0; i--)
36 printf("%d ", answer[i]);
37 printf("\n");
38 }
39
40 void solve1()
41 {
42 scanf("%lld",&n);
43 assert(1 <= n && n <= 1LL * 1000000000000000);
44 findBazaFactoriala(n);
45 }
46
47 void solve2()
48 {
49 long long fact = 1, value = 0;
50
51 scanf("%lld",&n);
CAPITOLUL 22. ONI 2018 346

52 assert(1 <= n && n <= 100000);


53 for(int i = 1; i <= n; i++)
54 {
55 scanf("%d",&v[i]);
56 assert(0 <= v[i] && v[i] <= 1000000000);
57 fact *= i;
58 value += v[i] * fact;
59 }
60
61 findBazaFactoriala(value);
62 }
63
64 int main ()
65 {
66 freopen("bazaf.in","r",stdin);
67 freopen("bazaf.out","w",stdout);
68
69 scanf("%d", &p);
70 assert(1 <= p && p <= 2);
71
72 if(p == 1)
73 solve1();
74 else
75 solve2();
76
77 return 0;
78 }

Listing 22.1.4: 4_bazaf.cpp


1 // prof. Chesca Ciprian
2 #include<fstream>
3 #include<iostream>
4
5 using namespace std;
6
7 ifstream fin("bazaf.in");
8 ofstream fout("bazaf.out");
9
10 #define ll long long
11
12 int nx,m,i,j;
13 ll v,x[21],fi[100005];
14 ll f[21],p,c,r;
15
16 int main()
17 {
18 f[0]=1;
19 for (i=1;i<=20;i++)
20 {
21 f[i]=f[i-1]*i;
22 }
23
24 fin>>p;
25
26 if (p==1)
27 {
28
29 fin >> v;
30 nx=0;
31 for(int j=20;j>=1;j--)
32 {
33 x[j]=v/f[j];
34 if(x[j]>0 && nx==0)
35 {
36 nx=j;
37 }
38 v=v%f[j];
39 }
40 fout<<nx;
41
42 for(int j=1;j<=nx;j++)
43 fout<<" "<<x[j];
44 }
45
CAPITOLUL 22. ONI 2018 347

46 if (p==2)
47 {
48 fin >> m;
49 long long s=0;
50 for(i=1;i<=m;i++)
51 {
52 fin >> fi[i];
53 s+=fi[i]*f[i];
54 }
55
56 v=s;
57 nx=0;
58 for(int j=20;j>=1;j--)
59 {
60 x[j]=v/f[j];
61 if(x[j]>0 && nx==0)
62 {
63 nx=j;
64 }
65 v=v%f[j];
66 }
67 fout<<nx;
68
69 for(int j=1;j<=nx;j++)
70 {
71 fout<<" "<<x[j];
72 }
73
74 fout << "\n";
75 }
76
77 fout.close();
78 fin.close();
79 return 0;
80 }

22.1.3 *Rezolvare detaliat 

22.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 denim mex-ul acesteia ca ind 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 semnicaµia din enunµ.
Pe ecare dintre urm toarele N linii se a  câte M numere naturale nenule, desp rµite prin
câte un spaµiu, reprezentând valorile matricei.

Date de ie³ire
Fi³ierul mexitate.out va conµine un singur num r natural reprezentând produsul mex-urilor
tuturor submatricelor având K linii ³i L coloane ale matricei modulo 1 000 000 007.

Restricµii ³i preciz ri
CAPITOLUL 22. ONI 2018 348

a 1 & N ˜ M & 400 000


a 1&K&N
a 1&L&M
a 1 & Aij  & N ˜ M , 1 & i & N , 1 & j & M
a Pentru 20% din punctajul total exist  teste cu 1 & N, M & 50
a Pentru alte 20% din punctajul total exist  teste cu 1 & N, M & 630

Exemple:
mexitate.in mexitate.out Explicaµii
3 4 2 3 400 N = 3 ³i M = 4
1 2 3 2 K = 2 ³i L = 3
2 3 1 4 Submatricile cu 2 linii ³i 3 coloane sunt:
1 1 2 6
1 2 3 cu mex-ul 4
231

2 3 2 cu mex-ul 5
314

2 3 1 cu mex-ul 4
112

3 1 4 cu mex-ul 5
126

Produsul tuturor mex-urilor este:


4 5 4 5 = 400
400 % 1 000 000 007 = 400
Timp maxim de executare/test: 3.0 secunde
Memorie: total 128 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

22.2.1 Indicaµii de rezolvare

Posdarascu Eugenie Daniel - Universitatea din Bucure³ti

Soluµie 20 de puncte:
Pentru ecare submatrice de K * L, se parcurg elementele ³i se pun într-un vector de frecvenµ .
Pentru a determina r spunsul (primul element lips ), este sucient s  parcurgem vectorul de
frecvent  si s  a m prima poziµie cu frecvenµa 0.

Soluµie 100 de puncte:


Soluµia se construie³te folosind urm toarele 3 optimiz ri:
1. Presupunem c  avem vectorul de frecvenµ  pentru o submatrice. În momentul în care mut m
matricea la dreapta (de exemplu), vectorul de frecvenµ  se schimb  cu foarte puµine elemente. Mai
exact, coloana din stânga dispare ³i apare noua coloan  din dreapta. În total, avem doar 2 * k
elemente pe care trebuie s  le modic m. Analog, când mutâm matricea în jos, avem 2 * l elemente
pe care trebuie s  le modic m.
Putem parcurge submatricele în urm torul mod:

- pornesc cu matricea din colµul (1,1)


- matricea se mut  din stânga în dreapta pân  ajunge în cap t
- coborâm matricea cu o unitate
- matricea se mut  din dreapta în stânga pân  ajunge în cap t
- coborâm matricea cu o unitate
- revenim la pasul 2
CAPITOLUL 22. ONI 2018 349

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, comple-
xitatea 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
nal  va : 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
modic  (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 frec-
venµ , ecare bucat  având marcat câte elemente apar cel puµin o dat . Vectorul de frecvenµ 
³i informaµia pentru ecare bucat  de radical se pot menµine u³or la update în O(1). La query,
pentru ecare bucat  de radical putem s  o s rim dac  toate elementele apar în acel interval,
respectiv 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
element cu element intr-un interval de lungime radical. Astfel, vom putea determina valoarea
mex-ului în maxim sqrt(VMAX) pa³i.

22.2.2 Cod surs 

Listing 22.2.1: mexitate1.cpp


1 /* Eugenie Daniel Posdarascu
2 100 de puncte
3 */
4 #include<stdio.h>
5 #include<vector>
6 #include<stdlib.h>
7 #include<time.h>
8
9 using namespace std;
10
11 #define MOD 1000000007
12 #define NMAX 400005
13
14 int n, m, k, l, f[NMAX], fb[NMAX];
15 int bucket = 100, indexB[NMAX];
16 long long answer = 1;
17 long long ans = 0;
18
19 vector<int> matrix[NMAX], matrix2[NMAX];
20
21 void deleteElements(int a, int b, int c, int d)
22 {
23 for(int i = a; i <= c; i++)
24 for(int j = b; j <= d; j++)
25 {
26 int value = matrix[i][j];
27 f[value]--;
28 if(!f[value])
29 fb[indexB[value]]--;
30
CAPITOLUL 22. ONI 2018 350

31 }
32 }
33
34 void addElements(int a, int b, int c, int d)
35 {
36 for(int i = a; i <= c; i++)
37 for(int j = b; j <= d; j++)
38 {
39 int value = matrix[i][j];
40 f[value]++;
41 if(f[value] == 1)
42 fb[indexB[value]]++;
43 }
44 }
45
46 void query()
47 {
48 for(int buc = 1; ; buc++)
49 {
50 if(fb[buc] == bucket)
51 continue;
52 for(int i = (buc - 1) * bucket + 1; ; i++)
53 {
54 if(!f[i])
55 {
56 answer *= i;
57 ans += i;
58 if(answer >= MOD)
59 answer %= MOD;
60 break;
61 }
62 }
63 break;
64 }
65 }
66
67 void initializeaza()
68 {
69 addElements(1,1,k,l);
70 query();
71 }
72
73 int main ()
74 {
75 int value, sign;
76
77 srand(time(0));
78
79 freopen("mexitate.in","r",stdin);
80 freopen("mexitate.out","w",stdout);
81
82 scanf("%d%d%d%d",&n,&m,&k,&l);
83
84 for(int i = 1; i <= n; i++)
85 {
86 matrix2[i].push_back(0);
87 for(int j = 1; j <= m; j++)
88 {
89 scanf("%d",&value);
90 matrix2[i].push_back(value);
91 }
92 }
93 if(k > l)
94 { // linia devine coloana // m linia 1 m - 1 2
95 for(int i = 1; i <= m; i++)
96 for(int j = 0; j <= n; j++)
97 matrix[i].push_back(0);
98
99 for(int i = 1; i <= n; i++)
100 for(int j = 1; j <= m; j++)
101 matrix[m - j + 1][i] = matrix2[i][j];
102
103 int aux = n;
104 n = m;
105 m = aux;
106
CAPITOLUL 22. ONI 2018 351

107 aux = l;
108 l = k;
109 k = aux;
110 }
111 else
112 {
113 for(int i = 1; i <= n; i++)
114 {
115 matrix[i].push_back(0);
116 for(int j = 1; j <= m; j++)
117 {
118 matrix[i].push_back(matrix2[i][j]);
119 }
120 }
121 }
122
123 int last = 1;
124 for(int i = 1; i <= n * m; i++)
125 {
126 indexB[i] = last;
127 if(i % bucket == 0)
128 last++;
129 }
130
131 initializeaza();
132 for(int i = 1; i <= n - k + 1; i++)
133 {
134 sign = ((i&1) ? +1 : -1);
135 if(sign == 1)
136 {
137 for(int j = 2; j <= m - l + 1; j++)
138 {
139 deleteElements(i, j - 1, i + k - 1, j - 1);
140 addElements(i, j + l - 1, i + k - 1, j + l - 1);
141 query();
142 }
143
144 if(i <= n - k)
145 {
146 deleteElements(i, m - l + 1, i, m);
147 addElements(i + k, m - l + 1, i + k, m);
148 query();
149 }
150 }
151 else
152 {
153 for(int j = m - l; j >= 1; j--)
154 {
155 deleteElements(i, j + l, i + k - 1, j + l);
156 addElements(i, j, i + k - 1, j);
157 query();
158 }
159
160 if(i <= n - k)
161 {
162 deleteElements(i, 1, i, l);
163 addElements(i + k, 1, i + k, l);
164 query();
165 }
166 }
167 }
168
169 printf("%lld\n", answer);
170
171 return 0;
172 }

Listing 22.2.2: mexitate2.cpp


1 /*
2 Narcis Gemene
3 */
4 #include <iostream>
5 #include <cstdio>
6 #include <vector>
CAPITOLUL 22. ONI 2018 352

7 #include <cassert>
8
9 using namespace std;
10
11 const int MOD = 1000000007;
12
13 void Update(vector<int>&frecv, vector<int>&buckets, int bucket, int i, int val)
14 {
15 if (frecv[i] == 0)
16 buckets[i/bucket]++;
17 frecv[i] += val;
18 if (frecv[i] == 0)
19 buckets[i/bucket]--;
20 }
21
22 int Query(vector<int>&frecv,vector<int>&buckets, int bucket)
23 {
24 int i;
25 for(i = 0; buckets[i] == bucket; i++);
26 for(i = bucket*i;frecv[i] != 0; i++);
27 return i;
28 }
29
30 int main()
31 {
32 freopen("mexitate.in", "r", stdin);
33 freopen("mexitate.out", "w", stdout);
34
35 int n, m, k, l;
36
37 cin >> n >> m >> k >> l;
38 assert(1 <= n*m && n*m <= 400000);
39
40 vector<vector<int>>a(n, vector<int>(m,0));
41 vector<int>frecv(n * m + 5), buckets(n * m + 5);
42
43 int bucket = 1;
44 for(;bucket*bucket < n*m ;bucket++);
45
46 Update(frecv, buckets, bucket, 0, 1);
47
48 for(auto&line: a)
49 for(auto &elem: line)
50 {
51 cin >> elem;
52 assert(1 <= elem && elem <= n * m);
53 }
54
55 if(k > l)
56 {
57 vector<vector<int>> b = a;
58
59 swap(n, m);
60 swap(k, l);
61
62 a = vector<vector<int>>(n, vector<int>(m,0));
63
64 for (int i = 0; i < n; i++)
65 for (int j = 0; j < m; j++)
66 a[i][j] = b[j][i];
67 }
68
69 for(int i = 0; i < k; i++)
70 {
71 for (int j = 0;j < l;j ++)
72 {
73 Update(frecv, buckets, bucket, a[i][j], 1);
74 }
75 }
76
77 const int left[] = {0, m - 1};
78 const int right[] = {m, -1};
79 long long sol = 1;
80
81 auto mat = vector<vector<int>>(n, vector<int>(m,0));
82
CAPITOLUL 22. ONI 2018 353

83 for(int i = 0; i + k - 1 < n; i++)


84 {
85 int dir = 1;
86 if (i&1)
87 dir = -1;
88
89 for(int j = left[i&1]; j + dir * (l - 1) != right[i&1]; j += dir)
90 {
91 int mex = Query(frecv, buckets, bucket);
92
93 mat[i][min(j, j + dir * (l - 1))] = mex;
94 sol = 1LL*sol*mex%MOD;
95
96 int nextCol = j + dir * l;
97
98 if(0 <= nextCol && nextCol < m)
99 {
100 for(int step = 0; step < k; step++)
101 {
102 Update(frecv, buckets, bucket, a[i+step][j], -1);
103 Update(frecv, buckets, bucket, a[i+step][nextCol], 1);
104 }
105 }
106 }
107
108 if (i + k < n)
109 {
110 for(int step = 0 ; step < l; step++)
111 {
112 int col = left[(i & 1) xor 1] + (-1) *dir * step;
113 Update(frecv, buckets, bucket, a[i][col], -1);
114 Update(frecv, buckets, bucket, a[i + k][col], 1);
115 }
116 }
117 }
118
119 cout << sol <<"\n";
120 return 0;
121 }

22.2.3 *Rezolvare detaliat 

22.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 ecare 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 ecare dintre aceste K zile va ploua sau va  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 zic, 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 22. ONI 2018 354

Pe ecare dintre urm toarele K linii se a  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.

Restricµii ³i preciz ri
a 1 & N & 1 000 000 000
a 1 & K & 100 000
a 1 & t1 , t2 , ..., tk & 100 000
a 1 & z1 $ z2 $ ... $ zk & N
a 2 & T & 1 000 000
a Pentru 20% din punctajul total exist  teste cu 1 & N, K & 1 000
a Pentru 65% din punctajul total exist  teste cu 1 & N, K & 100 000

Exemple:
plaja.in plaja.out Explicaµii
313 8 În ziua 1 timpul de plaj  este limitat la 2 unit µi de timp. În
12 ziua a doua timpul maxim de plaj  este 2 + 3, iar în ziua a
treia, timpul maxim este 2 + 3 + 3 = 8 unit µi de timp.
5 2 11 16 În ziua 2 timpul de plaj  este limitat la 2 unit µi de timp, iar în
22 zilele 1 ³i 3 nu exist  limitare. Atunci timpul maxim de plaj 
45 pentru zilele 1 sau 3 este 2 + 11 = 13.
În ziua 4 timpul de plaj  este limitat la 5 unit µi de timp, iar în
ziua 5 nu exist  limitare. Deci în ziua 5 timpul maxim de plaj 
este 5 + 11 = 16.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 128 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

22.3.1 Indicaµii de rezolvare

prof. Constantin G l µan - C. N. Liviu Rebreanu Bistriµa

Soluµie O(N * log N * log T)


R spunsul se caut  binar. Fie t0 o valoare de test în c utarea binar . Pentru ecare zi d[i]
dintre cele cu limit  superioar  pentru timpul de plaj , se reµine un interval de forma [d[i] - x, d[i]
+ x], astfel încât pentru orice zi din acest interval, nu este posibil s  se ating  timpul t0, datorit 
restricµiei T din enunµul problemei.
x = (t0 - t[i] - 1) este distanµa în zile dintre ziua d[i] ³i cea mai îndep rtat  zi pentru care nu
se poate atinge valoarea T. Avem: x = (t0 - t[i] - 1) / T.
Cele K intervale conµin doar zile pentru care nu se poate ajunge la valoarea t0. Aceste intervale
se pot intersecta sau nu.
Dac  exista cel puµin o zi neacoperit  de nici un interval, atunci valoarea T poate  atins 
sau eventual dep ³it . Capetele intervalelor se pot reµine într-un ³ir cu valorile +1 pentru cap tul
stânga, respectiv -1 pentru cel din dreapta. Se ordoneaz  ³irul ³i se însumeaz  valorile. În
momentul în care se atinge valoarea 0, suntem în afara oric rui interval dintre cele de mai sus.
Punctaj acordat: aproximativ 90 de puncte.
Soluµie O(N * log T)
Se procedeaz  ca mai sus, doar c  pentru determinarea unei sume parµiale de valoare 0, se pot
face updateuri cu ajutorul unui algoritm de tip mars . Dezavantajul metodei este c  necesit 
CAPITOLUL 22. ONI 2018 355

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 ecare valoare t[i] vom reduce valoarea
t[i + 1] la o nou  valoare posibil a  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 ecare pereche de zile de forma (d[i],
d[i + 1]), iar vericarea 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 aa r spunsul între oricare dou  zile consecutive în O(1). Având cele
dou  zile (X - în stanga, Y - în dreapta), ³tim c  ecare 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
aa poziµia de valoare maxim  (P) în funcµie de paritatea secvenµei de zile r mase. Punctaj: 100
de puncte

22.3.2 Cod surs 

Listing 22.3.1: plaja_N+K.cpp


1 /*
2 Narcis Gemene
3 O(N + K)
4 */
5 #include <iostream>
6 #include <cstdio>
7 #include <vector>
8 #include <cassert>
9
10 using namespace std;
11
12 const int KMAX = 1000005;
13 long long z[KMAX];
14 long long timp[KMAX];
15
16 int main(int argc, const char * argv[])
17 {
18 freopen("plaja.in", "r", stdin);
19 freopen("plaja.out", "w", stdout);
20
21 int n, k, T;
22
23 cin >> n >> k >> T;
24 for (int i = 1; i<= k; i++)
25 cin >> z[i] >> timp[i];
26
27 for(int i = 2;i <= k; i++)
28 timp[i] = min(timp[i], timp[i-1] + T*(z[i] - z[i-1]));
29
30 for(int i = k-1;i >= 1; i--)
31 timp[i] = min(timp[i], timp[i+1] + T*(z[i+1] - z[i]));
32
33 long long ans = max(1LL*timp[1]+T*(z[1]-1), 1LL*timp[k]+T*(n-z[k]));
34
CAPITOLUL 22. ONI 2018 356

35 for (int i = 2; i <= k; i++)


36 {
37 for(int j = z[i-1]; j <= z[i]; j++)
38 ans = max(ans,
39 min(1LL*timp[i-1] + 1LL*T*(j-z[i-1]),
40 1LL*timp[i] + 1LL*T*(z[i] - j)));
41 }
42
43 cout << ans << "\n";
44 return 0;
45 }

Listing 22.3.2: plaja_ternara.cpp


1 /*
2 Narcis Gemene
3 cautare ternara
4 */
5 #include <iostream>
6 #include <cstdio>
7 #include <vector>
8 #include <cassert>
9
10 using namespace std;
11
12 const int KMAX = 1000005;
13 long long z[KMAX];
14 long long timp[KMAX], T;
15
16 long long F(int x, int i)
17 {
18 return min(1LL*timp[i-1] + 1LL*T*(x-z[i-1]), timp[i] + 1LL*T*(z[i]-x));
19 }
20
21 int main()
22 {
23 freopen("plaja.in", "r", stdin);
24 freopen("plaja.out", "w", stdout);
25
26 int n, k;
27 cin >> n >> k >> T;
28 for (int i = 1; i<= k; i++)
29 cin >> z[i] >> timp[i];
30
31 for(int i = 2;i <= k; i++)
32 timp[i] = min(timp[i], timp[i-1] + T*(z[i] - z[i-1]));
33
34 for(int i = k-1;i >= 1; i--)
35 timp[i] = min(timp[i], timp[i+1] + T*(z[i+1] - z[i]));
36
37 long long ans = max(1LL*timp[1]+T*(z[1]-1), 1LL*timp[k]+T*(n-z[k]));
38
39 for (int i = 2; i <= k; i++)
40 {
41 int l = z[i-1];
42 int r = z[i];
43 while(r - l > 3)
44 {
45 int mid1 = l + (r-l)/3;
46 int mid2 = r - (r-l)/3;
47 if(F(mid1, i) > F(mid2, i))
48 r = mid2;
49 else
50 l = mid1;
51 }
52
53 for(; l <= r; l++)
54 ans = max(ans, F(l, i));
55 }
56
57 cout << ans << "\n";
58 return 0;
59 }
CAPITOLUL 22. ONI 2018 357

Listing 22.3.3: plaja1.cpp


1 /*
2 Andrei Baltatu
3 Solutie O(K)
4 */
5
6 #include <bits/stdc++.h>
7 #define int long long
8
9 using namespace std;
10
11 struct point_wall
12 {
13 int pos, val;
14 point_wall(int _pos, int _val)
15 {
16 pos = _pos;
17 val = _val;
18 }
19 };
20
21 int n, k, t;
22 vector<point_wall> restr;
23
24 vector<point_wall> Normalize(vector<point_wall> A)
25 {
26 vector<point_wall> res(A.begin(), A.end());
27
28 // from left
29 point_wall mn = A.front();
30 for (int i = 0; i < A.size(); i++)
31 {
32 // check if present element is a better restriction
33 if (1LL * t * abs(A[i].pos - mn.pos) + mn.val > A[i].val)
34 mn = A[i];
35
36 // change element on this position in result
37 res[i].val = min(res[i].val,
38 1LL * t * abs(res[i].pos - mn.pos) + mn.val);
39 }
40
41 // from right
42 mn = A.back();
43 for (int i = A.size() - 1; i >= 0; i--)
44 {
45 // check if present element is a better restriction
46 if (1LL * t * abs(A[i].pos - mn.pos) + mn.val > A[i].val)
47 mn = A[i];
48
49 // change element on this position in result
50 res[i].val = min(res[i].val,
51 1LL * t * abs(res[i].pos - mn.pos) + mn.val);
52 }
53
54 return res;
55 }
56
57 int Local_maxima(point_wall x, point_wall y)
58 {
59 int mx = max(x.val, y. val);
60
61 // now look between the walls
62 if (y.val < x.val) swap(x, y);
63
64 int dif = abs(y.pos - x.pos) - 1;
65 int steps = (y.val - x.val) / t;
66
67 if (!dif || dif < steps) return mx;
68
69 dif -= steps;
70 x.val += 1LL * steps * t;
71
72 if (!dif) return mx;
73 if (dif & 1) // odd
74 {
CAPITOLUL 22. ONI 2018 358

75 int length = (dif / 2) + 1;


76 mx = max(mx, x.val + 1LL * length * t);
77 }
78 else
79 {
80 int length = dif / 2;
81 mx = max(mx, y.val + 1LL * length * t);
82 }
83
84 return mx;
85 }
86
87 int32_t main()
88 {
89 freopen("plaja.in", "r", stdin);
90 freopen("plaja.out", "w", stdout);
91
92 cin.sync_with_stdio(false);
93
94 cin >> n >> k >> t;
95 for (int i = 1; i <= k; i++)
96 {
97 int x, y;
98 cin >> x >> y;
99 restr.push_back(point_wall(x, y));
100 }
101
102 if (restr.back().pos < n)
103 restr.push_back(point_wall(n, 1LL * n * t + (1 << 30)));
104
105 if (restr.front().pos > 1)
106 restr.insert(restr.begin(), point_wall(1, 1LL * n * t + (1 << 30)));
107
108 restr = Normalize(restr);
109
110 int mx = 0;
111 for (int i = 0; i < restr.size() - 1; i++)
112 mx = max(mx, Local_maxima(restr[i], restr[i + 1]));
113
114 cout << mx << "\n";
115 return 0;
116 }

Listing 22.3.4: plaja2.cpp


1 /*
2 prof. Constantin Galatan
3 solutie O(K * log T)
4 */
5 #include <fstream>
6 #include <vector>
7 #include <deque>
8 #include <algorithm>
9
10 using namespace std;
11
12 ifstream fin("plaja.in");
13 ofstream fout("plaja.out");
14
15 using LL = long long;
16 using VLL = vector<LL>;
17
18 int N, K, T;
19 VLL d, t;
20
21 bool Try(LL t0, int i);
22
23 int main()
24 {
25 fin >> N >> K >> T;
26 d = t = VLL(K + 1);
27
28 for (int i = 1; i <= K; ++i)
29 fin >> d[i] >> t[i];
30
CAPITOLUL 22. ONI 2018 359

31 for (int i = 1; i < K; ++i)


32 t[i + 1] = min(t[i + 1], t[i] + (d[i + 1] - d[i]) * T);
33
34 for (int i = K - 1; i >= 1; --i)
35 t[i] = min(t[i], t[i + 1] + (d[i + 1] - d[i]) * T);
36
37 LL res = 0, l, r, m, tmp;
38
39 for (int i = 1; i < K; ++i)
40 {
41 l = max(t[i], t[i + 1]), r = 1LL * N * T;
42 while (l <= r)
43 {
44 m = (l + r) / 2;
45 if (Try(m, i))
46 {
47 l = m + 1;
48 tmp = m;
49 }
50 else
51 r = m - 1;
52 }
53 res = max(res, tmp);
54 }
55 res = max(res, max(t[K] + (N - d[K]) * T, t[1] + (d[1] - 1) * T));
56 fout << res;
57
58 }
59
60 bool Try(LL t0, int i)
61 {
62 return d[i+1] - d[i] >= (t0 - t[i] + T-1) / T + (t0 - t[i+1] + T-1) / T;
63 }

Listing 22.3.5: plaja3.cpp


1 // O(n * log n * log T)
2 #include <fstream>
3 #include <vector>
4 #include <algorithm>
5 #include <iostream>
6
7 using namespace std;
8
9 ifstream fin("plaja.in");
10 ofstream fout("plaja.out");
11
12 struct Entry
13 {
14 int day, sgn;
15 bool operator < (const Entry& e) const
16 {
17 return day < e.day || ( day == e.day && sgn > e.sgn);
18 }
19 };
20
21 int N, K, T;
22 vector<long long> d, t;
23
24 bool Try(long long H);
25
26 int main()
27 {
28 fin >> N >> K >> T;
29 int day, time;
30
31 for (int i = 0; i < K; ++i)
32 {
33 fin >> day >> time;
34 d.push_back(day);
35 t.push_back(time);
36 }
37
38 long long l = 0, r = (1LL << 35), m;
39 long long res = 0;
CAPITOLUL 22. ONI 2018 360

40
41 while (l <= r)
42 {
43 m = (l + r) / 2;
44 if (Try(m))
45 {
46 res = m;
47 l = m + 1;
48 }
49 else
50 r = m - 1;
51 }
52
53 fout << res;
54 }
55
56 bool Try(long long t0)
57 {
58 vector <Entry> e;
59
60 e.push_back( {1, 0} );
61 e.push_back({N + 1, -1});
62
63 int a, b;
64 for (int i = 0; i < K; i++)
65 if (t[i] < t0)
66 {
67 long long x = (t0 - t[i] - 1) / T;
68 a = max(d[i] - x, 1LL);
69 b = min(d[i] + x + 1, (long long)(N + 1));
70
71 e.push_back({a, 1});
72 e.push_back({b, -1});
73 }
74
75 sort(e.begin(), e.end());
76
77 long long cnt = 0;
78 for (int i = 0; i < e.size(); i++)
79 {
80 cnt += e[i].sgn;
81 if (cnt <= 0 && e[i].day <= N)
82 return true;
83 }
84
85 return false;
86 }

Listing 22.3.6: plaja4.cpp


1 /*
2 prof. Constantin Galatan
3 solutie O(N * log T)
4 */
5 #include <fstream>
6 #include <vector>
7 #include <algorithm>
8 #include <iostream>
9
10 using namespace std;
11
12 ifstream fin("plaja.in");
13 ofstream fout("plaja.out");
14
15 int N, K, T;
16 vector<long long> d, t;
17
18 bool Try(long long H);
19
20 int main()
21 {
22 fin >> N >> K >> T;
23 int day, time;
24 for (int i = 0; i < K; ++i)
25 {
CAPITOLUL 22. ONI 2018 361

26 fin >> day >> time;


27 d.push_back(day);
28 t.push_back(time);
29 }
30
31 long long l = 0, r = (1LL * N * T), m;
32 long long res = 0;
33
34 while (l <= r)
35 {
36 m = (l + r) / 2;
37 if (Try(m))
38 {
39 res = m;
40 l = m + 1;
41 }
42 else
43 r = m - 1;
44 }
45
46 fout << res;
47 }
48
49 bool Try(long long t0)
50 {
51 vector <int> e(N + 2);
52 e[1] = 0;
53 e[N + 1] = -1;
54 int a, b;
55
56 for (int i = 0; i < K; i++)
57 if (t[i] < t0)
58 {
59 long long x = (t0 - t[i] - 1) / T;
60 a = max(d[i] - x, 1LL);
61 b = min(d[i] + x + 1, (long long)(N + 1));
62
63 e[a]++;
64 e[b]--;
65 }
66
67 long long cnt = 0;
68 for (int i = 1; i <= N; i++)
69 {
70 cnt += e[i];
71 if (cnt <= 0)
72 return true;
73 }
74 return false;
75 }

22.3.3 *Rezolvare detaliat 

22.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 aat î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 22. ONI 2018 362

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 a³a soluµia minim lexicograc .
Dac  nu exist  soluµie se va a³a valoarea -1.
Un vector V 1 se consider  mai mic lexicograc 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:

a X - valoarea pe care o c ut m binar în vector


a M - num rul de iteraµii în algoritmul de c utare binar 
a a, b - intervalul în care se aplic  algoritmul de c utare binar  (perechea (a, b) este consi-
derat  prima iteraµie în algoritm)
a M  1 perechi de indici reprezentând valorile a³ate de algoritmul de c utare binar  în urma
ec rei iteraµii

Date de intrare
Fi³ierul bsrec.in conµine pe prima linie o valoare T reprezentând num rul de teste ce vor 
efectuate.
Pentru ecare 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 semnicaµ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 a³aµi în urma ec 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 a³a N numere naturale nenule strict cresc toare ce vor reprezenta valorile
vectorului v .
Deoarece exist  mai multe soluµii, se va a³a soluµia minim lexicograc . Dac  testul NU
admite soluµie, se va a³a -1.

Restricµii ³i preciz ri
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 lexicograc 
admite valori pân  în 20
a Se garanteaz  c  M & 15

Exemple:

bsrec.in bsrec.out Explicaµii


CAPITOLUL 22. ONI 2018 363

2 1 3 4 25 26 Fi³ierul conµine 2 teste.


53 -1 Primul test are 3 query-uri:
34 a Primul query caut  binar valoarea 3 în intervalul [1, 5] ³i face
15 4 iteraµii
12 a Cel de al doilea query caut  binar valoarea 30 pe intervalul
22 [2, 4] ³i face 3 iteraµii
21 a Cel de al treilea query caut  binar valoarea 25 pe intervalul
30 3 [4, 5] ³i face 2 iteraµii
24 Cel de al doilea test are 3 query-uri, dar NU admite soluµie.
44
54
25 2
45
43
53
30 4
15
12
22
21
33
24
44
54
52
45
43

Timp maxim de executare/test: 0.3 secunde


Memorie: total 128 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

22.4.1 Indicaµii de rezolvare

Posdarascu Eugenie Daniel - Universitatea din Bucure³ti

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  cond itia v[mid] < x este adevarat ,
deci v[mid] <= x - 1
În urma tuturor acestor relaµii, putem deduce c  pentru ecare 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  lexicograc, 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  maximul dintre soli  1 ³i downi  1. La nal, r mâne de vericat dac  soli
<= upi pentru ecare i de la 1 la N . Dac  toate restricµiile se respect , avem soluµie ³i o as m.
Altfel, r spunsul e -1.

22.4.2 Cod surs 


CAPITOLUL 22. ONI 2018 364

Listing 22.4.1: bsrec.cpp


1 #include<stdio.h>
2 #include<algorithm>
3
4 using namespace std;
5
6 #define NMAX 1005
7 #define INF 1000000007
8
9 int tests, up[NMAX], down[NMAX], n, q;
10 int left[NMAX], right[NMAX], sol[NMAX];
11
12 void readAndRestrict()
13 {
14 int value, steps;
15
16 scanf("%d%d", &value, &steps);
17 for(int i = 1; i <= steps; i++)
18 {
19 scanf("%d%d", &left[i], &right[i]);
20 }
21
22 for(int i = 1; i < steps; i++)
23 {
24 if(left[i] == left[i + 1])
25 down[right[i + 1] + 1] = max(down[right[i + 1] + 1], value);
26 else
27 up[left[i + 1] - 1] = min(up[left[i + 1] - 1], value - 1);
28 }
29 }
30
31 void setRestrictions()
32 {
33 for(int i = 1; i <= n; i++)
34 {
35 down[i] = 0;
36 up[i] = INF;
37 }
38 }
39
40 int main ()
41 {
42
43 freopen("bsrec.in","r",stdin);
44 freopen("bsrec.out","w",stdout);
45
46 scanf("%d",&tests);
47 for(; tests; tests--)
48 {
49 scanf("%d%d",&n,&q);
50 setRestrictions();
51
52 for(int i = 1; i <= q; i++)
53 {
54 readAndRestrict();
55 }
56
57 int have_sol = 1;
58 for(int i = 1; i <= n && have_sol; i++)
59 {
60 sol[i] = max(sol[i - 1] + 1, down[i]);
61 if(sol[i] > up[i]) {
62 have_sol = 0;
63 }
64 }
65
66 if(!have_sol)
67 {
68 printf("-1\n");
69 }
70 else
71 {
72 for(int i = 1; i <= n; i++)
73 printf("%d ", sol[i]);
74 printf("\n");
CAPITOLUL 22. ONI 2018 365

75 }
76 }
77
78 return 0;
79 }

Listing 22.4.2: bsrec2.cpp


1 #include <fstream>
2 #include <cstdio>
3 #include <vector>
4 #include <cassert>
5
6 using namespace std;
7
8 int main()
9 {
10 ifstream fin("bsrec.in");
11 ofstream fout("bsrec.out");
12
13 int t;
14 fin >> t;
15
16 while(t--)
17 {
18 int n, q;
19 fin >> n >> q;
20
21 vector<int> maxx(n + 1, 1<<30);
22 vector<int> minn(n + 1, 1);
23
24 while(q--> 0)
25 {
26 int x, mid, m;
27 fin >> x >> m;
28 for(int i = 1;i <= m;i++)
29 {
30 int l, r;
31 fin >> l >> r;
32 if (i > 1)
33 {
34 if(l == mid+1)
35 {
36 maxx[mid] = min(maxx[mid], x - 1);
37 }
38 else
39 {
40 minn[mid] = max(minn[mid], x);
41 }
42 }
43
44 mid = (l+r)/2;
45 }
46 }
47
48 vector<int>sol;
49 int x = 0;
50
51 for(int i = 1; i <= n;i++)
52 {
53 x = max(minn[i], x + 1);
54 if(x > maxx[i])
55 break;
56 sol.push_back(x);
57 }
58
59 if(sol.size() == n)
60 {
61 for(auto x: sol)
62 fout << x << " ";
63 fout <<"\n";
64 }
65 else
66 fout << "-1\n";
67 }
CAPITOLUL 22. ONI 2018 366

68
69 return 0;
70 }

22.4.3 *Rezolvare detaliat 

22.5 numinum
Problema 5 - numinum 100 de puncte
Se consider  urm toarea structur  de date:

a în vârful structurii se g se³te fracµia 11 .


p
a Din ecare vârf în care se g se³te fracµia q
se formeaz  alte dou  fracµii trasând câte 2
p pq
segmente de dreapt  astfel: c tre stânga fracµia pq
³i c tre dreapta fracµia q
.

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 ³ierului de intrare numinum.in se g se³te un num r natural N .
Pe ecare 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

Restricµii ³i preciz ri
a 1 & N & 10 000
a 1 & xi , yi , ai , bi & 1 000 000 000, 1 & i & N

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 43 cu fracµia 25 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 22. ONI 2018 367

22.5.1 Indicaµii de rezolvare

prof. Ciprian Che³c  - Liceul Tehnologic Grigore C. Moisil Buz u

Soluµia 1 O max a1 , b1 ©min a1 , b1   max a2 , b2 ©min a2 , b2 


Pentru ecare dintre cele dou  fracµii se determin  o secvenµ  de 1 ³i 0 obµinut  prin aplicarea
algoritmului lui Euclid prin sc deri repetate între num r tor ³i numitor.
Spre exemplu, pentru fracµia 3/4 se efectueaz  operaµiile 3/4 3/1 2/1 1/1 ³i se va
obµine secvenµa 011.
Se determin , de la dreapta la stânga, subsecvenµa comun  celor dou  secvenµe determinate
anterior ³i se calculeaz  suma num rului de cifre din subsecvenµele necomune.
Soluµia are dezavantajul c  necesit  mult  memorie, având în vedere c  arborele poate avea
9
10 nivele. Gestionând atent memoria disponibil , se pot obµine aproximativ 40 puncte.

Soluµia 2 O log min a1, b1  log min a2, b2


Pentru a avea un model vizual asociat acestei soluµii se completeaz  pentru început structura
cu fracµia 0/1 a³ezat  deasupra fracµiei 1/1 ³i se consider  c  se ajunge de la fracµia 0/1 la fracµia
1/1 plecând c tre dreapta.
Se aplic  algoritmul lui Euclid prin împ rµiri repetate ³i se construie³te o secvenµ  de numere
21
formate din câturile obµinute la ecare pas al aplic rii algoritmului Euclid .
Semnicaµia unei astfel de secvenµe este urm toarea:
citit  de la sfâr³it câtre început secvenµa arat  drumul (³i lungimea) spre o anumit  fracµie.
De exemplu pentru fracµia 5/3 se obµine secvenµa [1,1,2] care arat  c  se porne³te din 0/1, se
parcurg dou  segmente c tre dreapta apoi unul c tre stânga ³i apoi unul c tre dreapta ³i ajungem
la fracµia 5/3.
Dac  se traseaz  o ax  de simetrie (virtual) prin fracµia 1/1 ³i se împarte structura în dou 
zone similare dar în care fracµiile sunt r sturnate, se poate observa c  toate fracµiile din stânga
vor avea în secvenµ  un num r par de termeni, iar cele din dreapta un num r impar de termeni.
În cazul în care o secvenµ  conµine un num r par de termeni se va ad uga un termen de 1
la sf r³itul secvenµei ³i se va decrementa ultimul termen. Aceast  operaµie nu modic  lungimea
drumului dar permite realizarea unei diferenµe între drumurile ce pleac  din 0/1 în 1/1 ³i apoi
merg c tre dreapta structurii, faµ  de cele care pleac  din 0/1 în 1/1 ³i apoi merg c tre stânga
structurii.
Ca ³i în soluµia anterioar  se determin  apoi subsecvenµa comun  a secvenµelor obµinute pentru
cele dou  fracµii ³i se calculeaz  suma elementelor din subsecvenµele necomune.
Spre exemplu pentru fracµiile 2/5 ³i 5/3 secvenµele asociate asociate sunt: [0,2,2] ³i respectiv
[1,1,2]. Analizate de la dreapta la stânga se observ  c  cele dou  secvenµe au în comun 2 ³i apoi
înc  un element. Suma elementelor din subsecvenµele necomune este 0 + 1 (din prima fracµie) ³i
1 (din a doua fracµie), deci în total 2.
Aceast  soluµie obµine 100 puncte.

Soluµia 3: student Daniel Posd r scu, Universitatea Bucure³ti


Observ m c  pentru orice pereche (a,b), p rintele acestei perechi este o pereche (c,d), pereche
care se determin  în urma aplic rii algoritmului de cel mai mic divizor comun prin sc deri repetate.
O soluµie naiv  în O N  (în O(rezultat) mai exact) presupune ca la ecare moment s  select m
perechea cu una din cele 2 valori maxime ³i aplicarea sc derii acelei perechi.
De exemplu:
Dac  avem 2 perechi: (2, 17) ³i (13, 25)
Pasul 1: 25 este valoarea maxim  deci sc dem 13 din 25.
Perechea devine (13,12).
Pasul 2: 17 este valoarea maxim  deci sc dem 2 din 17.
Perechea devine (2,15)
etc.
În momentul în care perechile devin egale, ne oprim.
Dup  cum bine ³tim, o optimizare a algoritmului de cmmdc prin sc deri repetate este algoritmul
lui Euclid prin împ rµiri care face O log  pa³i.
Din p cate, în cazul acestei probleme, aplicarea algoritmului lui Euclid poate s  sar  peste
punctul de întâlnire a celor 2 perechi.
21
în matematic  o astfel de secvenµ  poart  denumirea de fracµie continu 
CAPITOLUL 22. ONI 2018 368

Ce putem deduce în schimb este faptul c  dac  am determina prima pereche din cele comune
celor 2 perechi, aceasta pereche ar  o pereche care se poate determina în mod direct din punctul
de întâlnire.
Denim 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 modic m doar b,
ori b = d ³i modicam 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 ecare 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 ecare pas veric 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.

22.5.2 Cod surs 

Listing 22.5.1: 1_numinum_100.cpp


1 // prof. Chesca Ciprian
2 // sursa 100 p, O(log(min(a1,b1))+log(min(a2,b2)))
3
4 #include <fstream>
5 #include <cassert>
6
7 #define nmax 1000
8
9 using namespace std;
10
11 int main()
12 {
13 long long a1,b1,a2,b2,T;
14 long long w1[nmax],w2[nmax];
15 long long d, i, j, k, c, r; // teorema impartirii cu rest
16 long long k1,k2,ok1,ok2,s;
17
18 ifstream fin("numinum.in");
19 ofstream fout("numinum.out");
20 fin >> T;
21 assert(T>=1 && T<=10000);
22
23 for(k=1;k<=T;k++)
24 {
25 fin >> a1 >> b1 >> a2 >> b2;
26 assert(a1>=1 && a1<=1000000000);
CAPITOLUL 22. ONI 2018 369

27 assert(b1>=1 && b1<=1000000000);


28 assert(a2>=1 && a2<=1000000000);
29 assert(b2>=1 && b2<=1000000000);
30
31 // prima fractie.......................................................
32 // determin fractia continua atasata primei fractii
33 d = a1;i = b1; r = d%i; k1 = 0;
34 if (r==0) w1[++k1] = d/i;
35 while (r)
36 {
37 r = d%i;
38 c = d/i;
39 w1[++k1] = c;
40 d = i;
41 i = r;
42 }
43
44 // daca lungimea secventei este un numar par mai adaug un termen de 1
45 if (k1%2==0)
46 {w1[++k1]=1;w1[k1-1]--;}
47
48 //calculez suma elementelor vectorului fractiei continue
49 ok1=0;
50 for(i=1;i<=k1;i++)
51 ok1+=w1[i];
52
53
54 // fractia a doua..........................................
55 // determin fractia continua atasata cele de-a doua fractii
56 d = a2;i = b2; r = d%i; k2 = 0;
57 if (r==0) w2[++k2] = d/i;
58 while (r)
59 {
60 r = d%i;
61 c = d/i;
62 w2[++k2] = c;
63 d = i;
64 i = r;
65 }
66
67 // daca lungimea secventei este un numar par mai adaug un termen de 1
68 if (k2%2==0)
69 {
70 w2[++k2]=1;
71 w2[k2-1]--;
72 }
73
74 //calculez suma elementelor vectorului fractiei continue
75 ok2=0;
76 for(i=1;i<=k2;i++)
77 ok2+=w2[i];
78
79 // determin secventa comuna celor doi vectori ai fractiilor continue
80 i=k1;j=k2;s=0;
81 while (i>=1 and j>=1)
82 {
83 if (w1[i]==w2[j])
84 {
85 s=s+2*w1[i];
86 i--;
87 j--;
88 }
89 else
90 {
91 if (w1[i]<w2[j])
92 s=s+2*w1[i];
93 else
94 s=s+2*w2[j];
95
96 break;
97 }
98 }
99
100 // afisez rezultatul
101 fout << ok1 + ok2 - s << "\n";
102 }
CAPITOLUL 22. ONI 2018 370

103
104 fin.close();
105 fout.close();
106
107 return 0;
108 }

Listing 22.5.2: 2_numinum_100.cpp


1 // student Daniel Posdarascu
2
3 #include<stdio.h>
4 #include<vector>
5 #include<algorithm>
6
7 using namespace std;
8
9 #define x first.first
10 #define y first.second
11 #define dist second
12
13 int a, b, c, d, T;
14 vector< pair<pair<int,int>, int> > mylist[3];
15
16 void getList(int index, int a, int b)
17 {
18 if(a == 1 && b == 1)
19 {
20 mylist[index].push_back(make_pair(make_pair(a, b), 0));
21 return ;
22 }
23
24 int c;
25 if(a < b)
26 {
27 if(a > 1)
28 {
29 c = b / a;
30 getList(index, a, b % a);
31 }
32 else
33 {
34 c = b - 1;
35 getList(index, 1, 1);
36 }
37 }
38 else
39 {
40 if(b > 1)
41 {
42 c = a / b;
43 getList(index, a % b, b);
44 }
45 else
46 {
47 c = a - 1;
48 getList(index, 1, 1);
49 }
50 }
51
52 mylist[index].push_back(make_pair(make_pair(a, b), c));
53 }
54
55 void clearAll()
56 {
57 mylist[1].clear();
58 mylist[2].clear();
59 }
60
61 inline int modul(int value)
62 {
63 return (value < 0 ? -value : value);
64 }
65
66 int main ()
CAPITOLUL 22. ONI 2018 371

67 {
68
69 freopen("numinum.in","r",stdin);
70 freopen("numinum.out","w",stdout);
71
72 scanf("%d",&T);
73
74 for(int k=1;k<=T;k++)
75 {
76
77 scanf("%d%d%d%d",&a,&b,&c,&d);
78
79 getList(1,a,b);
80 getList(2,c,d);
81
82 int lca=0, lim1=mylist[1].size(), lim2=mylist[2].size();
83 while(lca < lim1 && lca < lim2 &&
84 mylist[1][lca] == mylist[2][lca])
85 lca++;
86
87
88 int ans = 0;
89 for(int i = lim1 - 1; i > lca; i--)
90 ans += mylist[1][i].dist;
91
92 for(int i = lim2 - 1; i > lca; i--)
93 ans += mylist[2][i].dist;
94
95 if(lca == lim1)
96 {
97 int A = mylist[2][lca].x;
98 int B = mylist[2][lca].y;
99 int C;
100 if(A < B)
101 {
102 C = mylist[2][lca - 1].x;
103 ans += (B - C) / A;
104 }
105 else
106 {
107 C = mylist[2][lca - 1].y;
108 ans += (A - C) / B;
109 }
110
111 printf("%d\n",ans);
112 clearAll();
113 continue;
114 }
115
116 if(lca == lim2)
117 {
118 int A = mylist[1][lca].x;
119 int B = mylist[1][lca].y;
120 int C;
121
122 if(A < B)
123 {
124 C = mylist[1][lca - 1].x;
125 ans += (B - C) / A;
126 }
127 else
128 {
129 C = mylist[1][lca - 1].y;
130 ans += (A - C) / B;
131 }
132
133 printf("%d\n", ans);
134 clearAll();
135 continue;
136 }
137
138 int A = mylist[1][lca].x;
139 int B = mylist[1][lca].y;
140
141 int C = mylist[2][lca].x;
142 int D = mylist[2][lca].y;
CAPITOLUL 22. ONI 2018 372

143
144 if(A < B)
145 { // C > D || A == C
146 if(A == C)
147 ans += modul(B - D) / A;
148 else
149 ans += (C - A) / D + (B - D) / A;
150 }
151 else
152 {// A > B && C < D || B == D
153 if(B == D)
154 ans += modul(A - C) / B;
155 else
156 ans += (A - C) / B + (D - B) / C;
157 }
158
159 printf("%d\n", ans);
160 clearAll();
161 }
162
163 return 0;
164 }

22.5.3 *Rezolvare detaliat 

22.6 rosii mici


Problema 6 - rosii mici 100 de puncte
Dan este un mare pasionat al fructelor, printre preferatele sale ind strugurii ³i pepenii. îns 
recent ³i-a descoperit ³i pasiunea pentru legume, în special pentru ro³ii, dar mai ales ro³iile mici.
Spre norocul lui, gr dina bunicului este plin  de ro³ii.
Gr dina are forma unei matrice cu N linii ³i M coloane cu elemente numere naturale, nu
neap rat distincte, unde ecare element din matrice reprezint  dimensiunea unei ro³ii. Matricea
are proprietatea c  oricare coloan  are valorile ordonate cresc tor de sus în jos, adic  de la prima
spre ultima linie.
Bunicul s u îi cere s  rezolve Q sarcini. Pentru ecare sarcin , Dan prime³te un num r
natural x ³i trebuie s  g seasc  o submatrice de arie maxim  care începe de pe linia 1 a matricei
care reprezint  gr dina ³i are toate elementele mai mici sau egale decât x. Pentru determinarea
submatricei cerute, Dan are voie s  mute toate valorile unei coloane în faµa oric rei alte coloane.
De asemenea, îi este permis s  fac  oricâte mut ri de tipul acesta.

Cerinµe
S  se calculeze aria maxim  a unei submatrice care respect  specicaµiile din enunµ, pentru
ecare 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 semnicaµia din enunµ.
Pe ecare dintre urm toarele N linii se a  câte M numere naturale desp rµite prin câte un
spaµiu, reprezentând valorile matricei.
Pe urm toarele Q linii se a  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 ecare sarcin , în ordinea în care acestea apar în ³ierul de intrare.

Restricµii ³i preciz ri
CAPITOLUL 22. ONI 2018 373

a 1 & N, M & 1 000


a 1 & Q & 100 000
a 1 & Aij  & N ˜ M , 1 & i & N , 1 & j & M
a 1&x&N ˜M
a Pentru 30% din punctajul total exist  teste cu 1 & N, M, Q & 50
a Pentru alte 20% din punctajul total exist  teste cu 1 & M & 100

Exemple:

rosiimici.in rosiimici.out Explicaµii


343 4 Pentru rezolvarea primei sarcini Dan mut  prima coloan  în faµa
1962 9 celei de a patra obµinând matricea:
1 10 10 4 6
7 15 10 6
6
10
9
Alege apoi submatricea cu colµul stânga sus în (1,3) ³i colµul
dreapta jos în (2, 4). Aria acesteia este 4. Pentru rezolvarea
celei de a doua sarcini, Dan mut  prima coloan  în faµa celei de
a treia obµinând matricea:

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.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 128 MB din care pentru stiv  32 MB
Dimensiune maxim  a sursei: 10 KB

22.6.1 Indicaµii de rezolvare

Gemene Narcis-Gabriel - Universitatea din Bucure³ti

Rezolvare 30 de puncte:
Pentru 30 de puncte putem precalcula r spunsul pentru ecare 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 ind coloana care maximizeaz  expresia de mai sus.
O abordare similar  este calcularea vectorului inaltime pentru ecare 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  ecare coloan  este sortat .
Astfel, vom folosi algoritmul descris în problema bsrec pentru calcularea în m*log(n) pa³i a
vectorui inaltime.
CAPITOLUL 22. ONI 2018 374

O alt  aborare este parcurgerea matricei în ordinea sortat  a elementelor ³i menµinerea vec-
torului inaltime sortat la ecare pas, deoarece ad ugarea elementului duce la modicarea 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

Rezolvare 100 de puncte:


Pentru 100 de puncte vom optimizara soluµia precedent  folosindu-ne de observaµia c  pentru
a menµine vectorul sortat putem cauta binar poziµia care se modic  sau folosirea unui vector de
frecvenµ  care s  ne spun  num rul de elemente mai mari sau egale cu o valoare i din vectorul de
în lµimi.

22.6.2 Cod surs 

Listing 22.6.1: rosiimici.cpp


1 #include <cstdio>
2 #include <vector>
3 #include <fstream>
4 #include <cassert>
5
6 using namespace std;
7
8 int main()
9 {
10 ifstream fin("rosiimici.in");
11 ofstream fout("rosiimici.out");
12
13 int n, m, q;
14 fin >> n >> m >> q;
15
16 assert(1 <= n && n <= 1000);
17 assert(1 <= m && m <= 1000);
18 assert(1 <= q && q <= 100000);
19
20 vector<vector<int>> positions(n * m + 5);
21 vector<int> minn(m, 0);
22
23 for (int i = 0; i < n; ++i)
24 {
25 for (int j = 0; j < m; ++j)
26 {
27 int x;
28 fin >> x;
29 assert(minn[j] <= x);
30 minn[j] = x;
31 assert(1 <= x && x <= n*m);
32 positions[x].push_back(j);
33 }
34 }
35
36 vector<int>height(m + 1, 0), sum(n + 1, 0);
37 vector<int>ans(n * m + 5, 0);
38
39 int sol = 0;
40 for (int val = 1;val <= n*m; ++val)
41 {
42 for(auto j: positions[val])
43 {
44 height[j]++;
45 sum[height[j]]++;
46 sol = max(sol,sum[height[j]] * height[j]);
47 }
48 ans[val] = sol;
49 }
50
51 for (int i = 1; i <= n*m; i++)
52 ans[i] = max(ans[i-1], ans[i]);
53
54 while(q-- > 0)
55 {
56 int x;
CAPITOLUL 22. ONI 2018 375

57 fin >> x;
58 assert(1 <= x && x <= n*m);
59 fout << ans[x] << "\n";
60 }
61
62 return 0;
63 }

Listing 22.6.2: rosiimici2.cpp


1 /*
2 Fac inaltimile elementelor pe parcurs, sortez coloanele in O(log(M))
3 si actualizez maximul
4 */
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 const int VALMAX = 1000005;
11 const int NMAX = 1005;
12 const int QMAX = 100005;
13
14 int n, m, q;
15 int a[NMAX][NMAX];
16 int rasp[QMAX];
17 int column_height[NMAX], which_column[NMAX], actual_pos[NMAX];
18
19 vector< pair<int, int> > pos, queries;
20
21 void Swap(int poza, int pozb)
22 {
23 swap(column_height[poza], column_height[pozb]);
24 swap(actual_pos[which_column[poza]], actual_pos[which_column[pozb]]);
25 swap(which_column[poza], which_column[pozb]);
26 }
27
28 void Prep()
29 {
30 for (int i = 1; i <= m; i++)
31 {
32 column_height[i] = 0;
33 which_column[i] = actual_pos[i] = i;
34 }
35
36 int i = 0, j = 0;
37 int mx = 0;
38 while (i < pos.size() && j < queries.size())
39 {
40 pair<int, int> x = pos[i];
41 pair<int, int> y = queries[j];
42
43 if (a[x.first][x.second] <= y.first)
44 {
45 // binary search
46 int st=1, dr = actual_pos[x.second], ans = actual_pos[x.second];
47 while (st <= dr)
48 {
49 int mij = (st + dr) / 2;
50 if(column_height[mij] == column_height[actual_pos[x.second]])
51 {
52 ans = mij;
53 dr = mij - 1;
54 }
55 else
56 st = mij + 1;
57 }
58
59 Swap(actual_pos[x.second], ans);
60
61 // add to the column
62 column_height[actual_pos[x.second]]++;
63
64 mx = max(mx,
65 actual_pos[x.second] *
CAPITOLUL 22. ONI 2018 376

66 column_height[actual_pos[x.second]]);
67 i++;
68 }
69 else
70 {
71 rasp[y.second] = mx;
72 j++;
73 }
74 }
75
76 while (j < queries.size())
77 {
78 rasp[queries[j].second] = mx;
79 j++;
80 }
81 }
82
83 bool cmp(pair<int, int> A, pair<int, int> B)
84 {
85 return a[A.first][A.second] < a[B.first][B.second];
86 }
87
88 int main()
89 {
90 freopen("rosiimici.in", "r", stdin);
91 freopen("rosiimici.out", "w", stdout);
92 cin.sync_with_stdio(false);
93
94 cin >> n >> m >> q;
95 for (int i = 1; i <= n; i++)
96 for (int j = 1; j <= m; j++)
97 {
98 cin >> a[i][j];
99 pos.push_back(make_pair(i, j));
100 }
101
102 for (int i = 1; i <= q; i++)
103 {
104 int x;
105 cin >> x;
106 queries.push_back(make_pair(x, i));
107 }
108
109 sort(queries.begin(), queries.end());
110 sort(pos.begin(), pos.end(), cmp);
111
112 Prep();
113
114 for (int i = 1; i <= q; i++)
115 cout << rasp[i] << "\n";
116 return 0;
117 }

22.6.3 *Rezolvare detaliat 


Capitolul 23

ONI 2017

23.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 ecare 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 a  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, ind oameni practici, doresc construirea unui far-bibliotec  (a³ezat pe o platform  1
 1), într-o zon  acoperit  de ap . Poziµia platformei va  aleas  într-o celul  C astfel încât suma
distanµelor dintre toate insulele ³i C s  e minim .
Distanµa dintre o celul  C ³i o insul  este denit  ca ind minimul dintre distanµele Manhattan
dintre C ³i ecare 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 aate pe linia x1 ³i coloana y1, respectiv pe linia x2 ³i
coloana y2, este denit  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 semnicaµ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

377
CAPITOLUL 23. ONI 2017 378

Fi³ierul de ie³ire arhipelag.out va conµine o pereche de numere naturale, reprezentând linia


si coloana celulei alese de ionieni pentru construcµie. Dac  exist  mai multe soluµii posibile, se va
alege cea care are linia minim . Dac  în continuare exist  mai multe soluµii, se va alege cea care
are coloana minim .

Restricµii ³i preciz ri
a Pentru teste în valoare de 15 puncte, 1 & N, M & 50
a Pentru alte teste în valoare de 20 de puncte, 1 & N, M & 300, iar num rul de insule din
arhipelag nu dep ³e³te 300
a Pentru alte teste în valoare de 20 de puncte, 1 & N, M & 300
a Pentru restul de teste, 1 & N, M & 1000
a Se garanteaz  c  exist  cel puµin o zon  acoperit  de ap 

Exemple:
arhipelag.in arhipelag.out Explicaµii
7 7 23
Notând cu D(x1, y1, x2, y2) insula determinat  de dreptunghiul
0 1 0 1 0 1 1având colµul stânga sus în (x1, y1) ³i colµul dreapta jos în (x2,
0 1 0 1 0 1 1y2), arhipelagul conµine urm toarele insule: D1(1, 2, 2, 2), D2(1,
0 0 0 1 0 0 04, 7, 4), D3(1, 6, 2, 7), D4(6, 1, 7, 2) ³i D5(6, 6, 7, 7). Notând
0 0 0 1 0 0 0cu dist(D) distanµa dintre celula (2, 3) ³i insula D, distanµele
0 0 0 1 0 0 0sunt urm toarele:
1 1 0 1 0 1 1dist(D1) = min |2 - 1| + |3 - 2|, |2 - 2| + |3 - 2| = 1, dist(D2)
1 1 0 1 0 1 1= 1, dist(D3) = 3, dist(D4) = 5 ³i dist(D5) = 7.
4 4 12 Pentru ecare dintre celulele (1, 2), (2, 2), (3, 2), (4, 3) si (4, 4),
0
11 0 distanµa dintre celul  ³i singura insul  existent  în acest exemplu
0
11 0 este aceea³i. Se va alege cea care are linia minim , iar în caz de
0
11 0 egalitate se va alege cea care are coloana minim . Astfel, celula
0
00 0 (1, 2) reprezint  soluµia.
Timp maxim de executare/test: 0.3 secunde
Memorie: total 32 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 15 KB

23.1.1 Indicaµii de rezolvare

În descrierea soluµiei, C x, y  reprezint  o celul  aat  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 ecare 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 23. ONI 2017 379

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 ecare insul 
D x1, y1, x2, y2, se adaug  la total_dxx valoarea dx, denit  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 ecare pas µinând minte 2 variabile:

` a - câte intervale x1, x2 cu proprietatea c  x2 $ x exist 


` b - câte intervale x1, x2 cu proprietatea c  x $ x1 exist 
Pentru x % 1, total_dxx total_dxx  1  b  a (folosind valoarea b de la pasul precedent
³i a-ul curent; intuitiv, cele b intervale de la dreapta se aproprie cu 1 fata de x-ul curent, deci se
scad b ˜ 1 din costul total, iar cele a aate la stanga, inclusiv cele care tocmai au ramas în spatele
lui x - de aceea  a-ul curent - se departeaz  cu 1, deci se adaug  a ˜ 1 la costul total).
Valorile a ³i b se pot calcula simplu, în O 1 la ecare pas, folosind ni³te vectori în care marc m
unde începe, respectiv unde se termin , un interval.
Complexitate: O N ˜ M 

23.1.2 Cod surs 

Listing 23.1.1: arhipelag_100p_1.cpp


1 // Mihai Enache
2 #include <iostream>
3 #include <fstream>
4 using namespace std;
5
6 const int MAX_N = 1002;
7
8 int N, M, xs, ys;
9 int A[MAX_N][MAX_N], cx[MAX_N], cy[MAX_N], startX[MAX_N],
10 endX[MAX_N], startY[MAX_N], endY[MAX_N];
11
12 void computeCostForCoordinate(int n, int c[], int startHere[], int endHere[])
13 {
14 int toRight = 0;
15 int toLeft = 0;
16 for(int i = 2; i <= n; ++i)
17 {
18 c[1] += (i - 1) * startHere[i];
19 toRight += startHere[i];
20 }
21
22 for(int i = 2; i <= n; ++i)
23 {
24 toLeft += endHere[i - 1];
25 c[i] = c[i - 1] - toRight + toLeft;
26
27 toRight -= startHere[i];
28 }
29 }
30
31 int main() {
32 ifstream cin("arhipelag.in");
CAPITOLUL 23. ONI 2017 380

33 ofstream cout("arhipelag.out");
34
35 cin >> N >> M;
36 for(int i = 1; i <= N; ++i)
37 for(int j = 1; j <= M; ++j)
38 cin >> A[i][j];
39
40 for(int i = 1; i <= N; ++i)
41 {
42 for(int j = 1; j <= M; ++j)
43 {
44 if(A[i][j] == 1)
45 {
46 int xl = i;
47 int xr = i;
48
49 while(A[xr + 1][j])
50 ++xr;
51
52 int yl = j;
53 int yr = j;
54 while(A[i][yr + 1])
55 ++yr;
56
57 for(int k = xl; k <= xr; ++k)
58 for(int h = yl; h <= yr; ++h)
59 A[k][h] = -1;
60
61 ++startX[xl];
62 ++endX[xr];
63
64 ++startY[yl];
65 ++endY[yr];
66 }
67 }
68 }
69
70 computeCostForCoordinate(N, cx, startX, endX);
71 computeCostForCoordinate(M, cy, startY, endY);
72
73 int bestTotalDistance = 0x3f3f3f3f;
74 for(int i = 1; i <= N; ++i)
75 {
76 for(int j = 1; j <= M; ++j)
77 {
78 if(!A[i][j] && cx[i] + cy[j] < bestTotalDistance)
79 {
80 bestTotalDistance = cx[i] + cy[j];
81 xs = i;
82 ys = j;
83 }
84 }
85 }
86
87 cout << xs << " " << ys << "\n";
88 // printf("Best total disance = %d\n", bestTotalDistance);
89
90 return 0;
91 }

Listing 23.1.2: arhipelag_100p_2.cpp


1 // Alexandru Velea
2 #include <fstream>
3 #include <iostream>
4 #include <vector>
5
6 using namespace std;
7
8 const int kMaxN = 1e3+5;
9
10 int el[kMaxN][kMaxN];
11
12 struct Rectangle
13 {
CAPITOLUL 23. ONI 2017 381

14 int x, y, lx, ly;


15 Rectangle() : x(0), y(0), lx(0), ly(0) { }
16 };
17
18 vector<Rectangle> rectangles;
19
20 void AddRectangle(int x, int y)
21 {
22 Rectangle r;
23 r.x = x;
24 r.y = y;
25
26 while (el[x][y + r.ly] == 1)
27 r.ly += 1;
28
29 while (el[x + r.lx][y] == 1)
30 r.lx += 1;
31
32 rectangles.push_back(r);
33 }
34
35 int lazy_x[kMaxN];
36 int lazy_y[kMaxN];
37
38 int X[kMaxN];
39 int Y[kMaxN];
40
41 int main()
42 {
43 ifstream cin("arhipelag.in");
44 ofstream cout("arhipelag.out");
45
46 int n, m;
47 cin >> n >> m;
48 for (int i = 1; i <= n; i += 1)
49 for (int j = 1; j <= m; j += 1)
50 cin >> el[i][j];
51
52 for (int i = 1; i <= n; i += 1)
53 for (int j = 1; j <= m; j += 1)
54 if (el[i][j] and el[i - 1][j] == 0 and el[i][j - 1] == 0)
55 AddRectangle(i, j);
56
57 int sum_x = 0, sum_y = 0;
58 int lazy_x_sum = 0;
59 int lazy_y_sum = 0;
60
61 for (auto itr : rectangles)
62 {
63 lazy_x_sum -= 1;
64 lazy_x[itr.x + 1] += 1;
65 sum_x += itr.x;
66
67 lazy_x[itr.x + itr.lx] += 1;
68
69 lazy_y_sum -= 1;
70 lazy_y[itr.y + 1] += 1;
71 sum_y += itr.y;
72 lazy_y[itr.y + itr.ly] += 1;
73 }
74
75 for (int i = 1; i <= n; i += 1)
76 {
77 lazy_x_sum += lazy_x[i];
78 sum_x += lazy_x_sum;
79 X[i] = sum_x;
80 }
81
82 for (int i = 1; i <= m; i += 1)
83 {
84 lazy_y_sum += lazy_y[i];
85 sum_y += lazy_y_sum;
86 Y[i] = sum_y;
87 }
88
89 int mn = 1e9;
CAPITOLUL 23. ONI 2017 382

90 int rx = -1, ry = -1;


91 for (int i = 1; i <= n; i += 1)
92 {
93 for (int j = 1; j <= m; j += 1)
94 {
95 if (el[i][j] == 1)
96 continue;
97
98 if (X[i] + Y[j] < mn)
99 {
100 mn = X[i] + Y[j];
101 rx = i;
102 ry = j;
103 }
104 }
105 }
106
107 cout << rx << ’ ’ << ry << ’\n’;
108 return 0;
109 }

23.1.3 *Rezolvare detaliat 

23.2 mirror
Problema 2 - mirror 100 de puncte
Numim oglinda num rului natural nenul a, num rul b, obµinut prin modicarea ec 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 a³a, separate prin câte un spaµiu, toate repre-
zent 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 ecare pas orice termen cu
oglinda sa. Asupra termenilor care devin zero nu se vor mai efectua alte operaµii. Dup  efec-
tuarea 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 a³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 a  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 ³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 ³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  C 1.
CAPITOLUL 23. ONI 2017 383

Exemple:
mirror.in mirror.out Explicaµii
1 330111 7 10 111 2 ; 8 10 1000 2 ; 2 10 10 2 ; 11 10 1011 2 ;
42 Sirul format este: 1111000101011 ³i grupate câte 2 avem nume-
7 8 2 11 rele:
11 2 3 10 ; 11 2 3 10 ; 00 2 0 10 ; 01 2 1 10 ; 01 2
1 10 ; 01 2 1 10 ;
2 3 Dup  o transformare numerele în baza 2 sunt:
51 13
37 72 101 50
116

Cea mai lung  secvenµ  este de lungime 3, ind format  din


numerele 37, 72, 101 care începe pe poziµia 1 ³i se termin  pe
poziµia 3. Mai exist  înc  o astfel de secvenµa (101, 50, 116) dar
se alege cea mai
Timp maxim de executare/test: 1.0 secunde
Memorie: total 32 MB
Dimensiune maxim  a sursei: 10 KB

23.2.1 Indicaµii de rezolvare


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 semnicativ  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 a 
0 1
respectiv pe aceea³i poziµie, ca putere a lui doi (2 , 2 , ...)

23.2.2 Cod surs 

Listing 23.2.1: mirror_100p_1.cpp


1 #include <fstream>
2 #include<cstdio>
3 #include<climits>
4 #include <stdlib.h>
5 #include<algorithm>
6
7 using namespace std;
8
9 FILE *fin=fopen("mirror.in","r");
10 ofstream fout("mirror.out");
11
12 int v[1000005],n,ct,k;
13
14 void rezolva1()
15 {
16 int masca=0,i,y=1<<(k-1),nr,x=0;
17 for(i=1; i<=n; i++)
18 {
19 nr=v[i];
20 if(masca==0)
21 masca=1;
22
23 while(masca<nr)
CAPITOLUL 23. ONI 2017 384

24 masca<<=1;
25
26 if(masca>nr)masca>>=1;
27
28 while(masca)
29 {
30 if(masca&nr)
31 x=x+y;
32 y/=2;
33 if(y==0)
34 {
35 fout<<x<<" ";
36 y=1<<k-1;
37 x=0;
38 }
39 masca>>=1;
40 }
41 }
42 fout<<’\n’;
43 }
44
45 int lungime(int &st, int &dr)
46 {
47 int ok,l,lmax=0,masca=1,ddr,i;
48 do
49 {
50 ok=0;//pp ca nu mai sunt cifre binare de 1
51 l=0;
52 for(i=st; i<=dr; i++)
53 {
54 if(masca<v[i])
55 ok=1;
56 if(masca&v[i])
57 l++;
58 else
59 {
60 if(l>lmax)
61 {
62 lmax=l;
63 ddr=i-1;
64 }
65 l=0;
66 }
67 }
68
69 if(l>lmax)
70 {
71 lmax=l;
72 ddr=dr;
73 }
74 masca<<=1;
75 }
76
77 while(ok==1);
78 dr=ddr;
79 st=ddr-lmax+1;
80 return lmax;
81 }
82
83 void secventa()
84 {
85 int st=0,dr,i,lung,lmax=0,sst,ddr;
86 i=1;
87
88 while(v[i]==0&&i<=n)
89 i++;
90
91 if(i<=n)
92 for( ; i<=n; i++)
93 if(v[i]!=0)
94 if(st==0)
95 st=dr=i;
96 else
97 dr++;
98 else
99 {
CAPITOLUL 23. ONI 2017 385

100 if(st)
101 {
102 lung=lungime(st,dr);
103 if(lung>lmax)
104 {
105 lmax=lung;
106 sst=st;
107 ddr=dr;
108 }
109 st=0;
110 }
111 }
112
113 if(st)
114 {
115 lung=lungime(st,dr);
116 if(lung>lmax)
117 {
118 lmax=lung;
119 sst=st;
120 ddr=dr;
121 }
122 }
123
124 fout<<lmax<<’\n’<<sst<<" "<<ddr<<’\n’;
125 }
126
127 int main()
128 {
129 int x=1,i,j,masca;
130 fscanf(fin,"%d%d%d",&ct,&n,&k);
131 for(i=1; i<=n; i++)
132 fscanf(fin,"%d",&v[i]);
133
134 if(ct==1)
135 {
136 rezolva1();
137 return 0;
138 }
139
140 // transformare cautand cifrele care se modifica si tinand cont
141 // de paritatea lui k
142 int u[33],nr,p,l,r;
143 if(k)
144 for(r=1; r<=n; r++)
145 {
146 masca=1;
147 masca<<=30;
148 while((masca&v[r])==0)
149 masca>>=1;
150 nr=0;
151
152 //pun cifrele binare in vector
153 while(masca)
154 {
155 if(masca&v[r])
156 u[++nr]=1;
157 else
158 u[++nr]=0;
159
160 masca>>=1;
161 }
162
163 int cont=0;//numara de cate ori se schimba cifrele binare
164 int cifbin=1;
165 for(j=1; j<=nr; j++)
166 if(u[j]!=cifbin)
167 {
168 cont++;
169 if(cifbin==1)
170 cifbin=0;
171 else
172 cifbin=1;
173 }
174
175 if(cont<k)
CAPITOLUL 23. ONI 2017 386

176 v[r]=0;
177 else
178 {
179 // modific toate cifrele odata daca k impar,
180 // altfel cifrele raman la fel
181 if(k%2==1)
182 for(j=1; j<=nr; j++)
183 if(u[j]==0)
184 u[j]=1;
185 else
186 u[j]=0;
187
188 //elimin cifrele care se pierd la transformare
189 i=1;
190 cont=0;
191 while(cont<k&&i<=nr)
192 {
193 i++;
194 if(u[i]!=u[i-1])
195 cont++;
196 }
197
198 if(i>nr)
199 v[r]=0;
200 else
201 {
202 p=1;
203 x=0;
204 for(j=nr; j>=i; j--)
205 {
206 x=x+p*u[j];
207 p<<=1;
208 }
209
210 v[r]=x;
211 }
212 }
213 }
214
215 secventa();
216 return 0;
217 }

Listing 23.2.2: mirror_100p_2.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 char A[1000000][32],x2[32];
7
8 int main()
9 {
10 ifstream in("mirror.in");
11 ofstream out("mirror.out");
12
13 int c,k,a[30][33],s,nc,max=0;
14 long n,x,r,p2,p,lmax,psf,psfmin,nr,i,j,l;
15
16 in>>c; in>>n>>k;
17
18 if(c==1)
19 { //cer 1
20 p2=1;
21 for(i=1;i<=k-1;i++)
22 p2=p2*2;
23
24 nr=0;
25 r=0;
26 p=p2;
27 nc=0;
28 while(nr<n)
29 {
30 s=0; i=0;
CAPITOLUL 23. ONI 2017 387

31 while(s<k && nr<n)


32 {
33 in>>x; nr++; j=0;
34 while(x!=0)
35 {
36 a[i][++j]=x%2;
37 x=x/2;
38 }
39
40 a[i][0]=j;
41 s=s+j;
42 i++;
43 }
44
45 x=r;
46 for(l=0;l<i;l++)
47 for(j=a[l][0];j>0;j--)
48 {
49 x=x+a[l][j]*p;
50 p=p/2;
51 nc++;
52 if(nc==k)
53 {
54 out<<x<<" ";
55 x=0;
56 p=p2;
57 nc=0;
58 }
59 }
60 r=x;
61 }
62 }
63 else
64 { //cer 2
65 for(i=0;i<n;i++)
66 {
67 in>>x; nc=0;
68 if(x==0)
69 nc=1;
70
71 while(x!=0)
72 {
73 x2[nc++]=x%2;
74 x=x/2;
75 }
76
77 for(j=1;j<=k;j++)
78 {
79 r=j%2;
80 nc--;
81 if(nc>=1)
82 while(x2[nc-1]==r && nc>0)
83 nc--;
84 }
85
86 if(nc>max)
87 max=nc;
88
89 for(j=0;j<nc;j++)
90 {
91 if(k%2==1)
92 A[i][j]=!x2[j];
93 else
94 A[i][j]=x2[j];
95 }
96 }
97
98 //det secv de 1 cu lung max pe col si poz inc si sf
99 lmax=0;
100 psf=1;
101 psfmin=n;
102 for(j=0;j<max;j++)
103 {
104 l=0;
105 for(i=0;i<n;i++)
106 {
CAPITOLUL 23. ONI 2017 388

107 if(A[i][j]==1)
108 l++;
109 else
110 {
111 if(l>lmax)
112 {
113 lmax=l;
114 psf=i;
115 psfmin=i;
116 }
117
118 if(l==lmax)
119 psf=i;
120
121 if(psf<psfmin)
122 psfmin=psf;
123
124 l=0;
125 }
126 }
127
128 i=i-1;
129 if(A[i][j]==1 && l>lmax)
130 {
131 lmax=l;
132 psf=i+1;
133 psfmin=psf;
134 }
135 }
136
137 out<<lmax<<’\n’; out<<psfmin+1-lmax<<’ ’<<psfmin<<’\n’;
138 }
139
140 in.close();
141 out.close();
142 return 0;
143 }

Listing 23.2.3: mirror_100p_3.cpp


1 // Autor: Mihai Enache
2 #include <iostream>
3 #include <fstream>
4
5 using namespace std;
6
7 const int MAX_N = 1000002;
8
9 int C, N, K;
10 int v[MAX_N];
11
12 int main()
13 {
14 ifstream cin("mirror.in");
15 ofstream cout("mirror.out");
16
17 cin >> C >> N >> K;
18 for(int i = 1; i <= N; ++i)
19 cin >> v[i];
20
21 if(C == 1)
22 {
23 int currentValue = 0;
24 int nrBits = 0;
25
26 for(int i = 1; i <= N; ++i)
27 {
28 int maxBit = 0;
29 for(int bit = 31; bit >= 0; --bit)
30 {
31 if(v[i] & (1 << bit))
32 {
33 maxBit = bit;
34 break;
35 }
CAPITOLUL 23. ONI 2017 389

36 }
37
38 for(int bit = maxBit; bit >= 0; --bit)
39 {
40 if(v[i] & (1 << bit))
41 currentValue = currentValue * 2 + 1;
42 else
43 currentValue = currentValue * 2;
44
45 ++nrBits;
46
47 if(nrBits == K)
48 {
49 cout << currentValue << " ";
50
51 currentValue = 0;
52 nrBits = 0;
53 }
54 }
55 }
56
57 cout << "\n";
58 }
59 else
60 {
61 for(int i = 1; i <= N; ++i)
62 {
63 for(int j = 1; j <= K; ++j)
64 {
65 if(!v[i])
66 break;
67
68 int maxBit = 0;
69 for(int bit = 31; bit >= 0; --bit)
70 {
71 if(v[i] & (1 << bit))
72 {
73 maxBit = bit;
74 break;
75 }
76 }
77
78 v[i] ^= (1 << (maxBit + 1)) - 1;
79 }
80 }
81
82 int startPos = -1;
83 int endPos = -1;
84 int maxLength = 0;
85
86 for(int bit = 0; bit < 32; ++bit)
87 {
88 int currentLength = 0;
89 for(int i = 1; i <= N; ++i)
90 {
91 if(v[i] & (1 << bit))
92 ++currentLength;
93 else
94 currentLength = 0;
95
96 if(currentLength > maxLength ||
97 (currentLength == maxLength &&
98 i - currentLength + 1 < startPos))
99 {
100 maxLength = currentLength;
101 startPos = i - currentLength + 1;
102 endPos = i;
103 }
104 }
105 }
106
107 cout << maxLength << "\n";
108 cout << startPos << " " << endPos << "\n";
109 }
110
111 return 0;
CAPITOLUL 23. ONI 2017 390

112 }

Listing 23.2.4: mirror_100p_4.cpp


1 #include<iostream>
2 #include <fstream>
3 #include<cstdio>
4 #include<climits>
5 #include <stdlib.h>
6
7 using namespace std;
8
9 FILE *fin=fopen("mirror.in","r");
10 ofstream fout("mirror.out");
11
12 int v[1000005],n,ct,k;
13 int w[33];
14
15 int cautare_binara(int x)
16 {
17 int st=1,dr=31,mij;
18
19 if(x>=w[31])
20 return w[31];
21 if(x<=w[1])
22 return w[1];
23
24 while(st<=dr)
25 {
26 mij=(st+dr)>>1;
27 if(w[mij]==x)
28 return x;
29 if(x>w[mij] && x<=w[mij+1])
30 return w[mij+1];
31 if(x<w[mij])
32 dr=mij-1;
33 else
34 st=mij+1;
35 }
36
37 return -1;
38 }
39
40 int transforma(int x)
41 {
42 int y=cautare_binara(x);
43 y=(x^y);
44 return y;
45 }
46
47 void rezolva1()
48 {
49 int masca=0,i,j,y=1<<k-1,nr,x=0;
50 for(i=1; i<=n; i++)
51 {
52 nr=v[i];
53 if(masca==0)
54 masca=1;
55 while(masca<nr)
56 masca<<=1;
57 if(masca>nr)masca>>=1;
58 while(masca)
59 {
60 if(masca&nr)
61 x=x+y;
62 y/=2;
63 if(y==0)
64 {
65 fout<<x<<" ";
66 y=1<<k-1;
67 x=0;
68 }
69 masca>>=1;
70 }
71 }
CAPITOLUL 23. ONI 2017 391

72 fout<<’\n’;
73 }
74
75 int lungime(int &st, int &dr)
76 {
77 int ok,l,lmax=0,masca=1,ddr,i;
78 do
79 {
80 ok=0;//pp ca nu mai sunt cifre binare de 1
81 l=0;
82 for(i=st; i<=dr; i++)
83 {
84 if(masca<=v[i])
85 ok=1;
86 if(masca&v[i])
87 l++;
88 else
89 {
90 if(l>lmax)
91 {
92 lmax=l;
93 ddr=i-1;
94 }
95 l=0;
96 }
97 }
98 if(l>lmax)
99 {
100 lmax=l;
101 ddr=dr;
102 }
103 masca<<=1;
104 } while(ok==1);
105
106 dr=ddr;
107 st=ddr-lmax+1;
108 return lmax;
109 }
110
111 void secventa()
112 {
113 int st=0,dr=0,i,lung,lmax=0,sst,ddr;
114 i=1;
115 while(v[i]==0&&i<=n)
116 i++;
117
118 if(i<=n)
119 for( ; i<=n; i++)
120 if(v[i]!=0)
121 if(st==0)
122 st=dr=i;
123 else
124 dr++;
125 else
126 {
127 if(st)
128 {
129 lung=lungime(st,dr);
130 if(lung>lmax)
131 {
132 lmax=lung;
133 sst=st;
134 ddr=dr;
135 }
136 st=0;
137 }
138 }
139
140 if(dr)
141 {
142 lung=lungime(st,dr);
143 if(lung>lmax)
144 {
145 lmax=lung;
146 sst=st;
147 ddr=dr;
CAPITOLUL 23. ONI 2017 392

148 }
149 }
150
151 fout<<lmax<<’\n’<<sst<<" "<<ddr<<’\n’;
152 }
153
154 int main()
155 {
156 int x=1,i,j,masca;
157 for(i=1; i<=31; i++)
158 {
159 x=x<<1;
160 w[i]=x-1;
161 }
162 fscanf(fin,"%d%d%d",&ct,&n,&k);
163 for(i=1; i<=n; i++)
164 fscanf(fin,"%d",&v[i]);
165
166 if(ct==1)
167 {
168 rezolva1();
169 return 0;
170 }
171
172 //transformare cu cautare binara- timp mai mare?
173 for(i=1; i<=n; i++)
174 {
175 for(j=1; j<=k&&v[i]; j++)
176 v[i]=transforma(v[i]);
177 // fout<<v[i]<<" ";
178 }
179
180 secventa();
181 return 0;
182 }

Listing 23.2.5: mirror_100p_5.cpp


1 #include<fstream>
2 #include<cstdio>
3
4 using namespace std;
5
6 FILE *fin;
7 ofstream fout("mirror.out");
8
9 int C, N, K, pos[33], posL[33], L[33], maxL[33], v[33],z;
10 long long x, y, d[33];
11
12 int posd(int x)
13 {
14 ///d[i] este cea mai mica putere a lui 2 care depaseste pe x
15 int i=0;
16 while(d[i]<=x)
17 i++;
18 return i;
19 }
20
21 void afisareK(long long &x, int &p)
22 {
23 while(p>=K)
24 {
25 p=p-K;
26 fout<<x/d[p]<<" ";
27 x=x%d[p];
28 }
29 }
30
31 int main()
32 {
33 int i,q,p,j,jmax,l,pmax;
34
35 d[0]=1;
36 for(i=1;i<=32;i++)
37 {
CAPITOLUL 23. ONI 2017 393

38 d[i]=d[i-1]*2;
39 }
40
41 fin=fopen("mirror.in","rt");
42 fscanf(fin,"%d %d %d",&C,&N,&K);
43
44 if(C==1)
45 {
46 fscanf(fin,"%d",&z);
47 x=z;
48 p=posd(x);
49 afisareK(x,p);
50 for(i=2;i<=N;i++)
51 {
52 fscanf(fin,"%d",&z);
53 y=z;
54 q=posd(y);
55 x=x*d[q]+y;
56 p+=q;
57 afisareK(x,p);
58 }
59 }
60 else
61 {
62 for(j=0;j<33;j++)
63 {
64 posL[j]=0;
65 maxL[j]=0;
66 }
67 pmax=0;
68 for(i=1;i<=N;i++)
69 {
70 fscanf(fin,"%d",&z);
71 x=z;
72 for(j=0;j<=31;j++)
73 {
74 v[j]=x%2;
75 x=x/2;
76 if(v[j]==1){
77 p=j;
78 }
79 }
80 if(p>pmax)pmax=p;
81 for(l=1;l<=K && p>=0;l++)
82 {
83 while(p>=0 && v[p]==l%2)
84 {
85 v[p]=0;
86 p--;
87 }
88 }
89 for(j=0;j<=pmax;j++)
90 {
91 q=v[j];
92 if(K%2) q=1-q;
93 if(j>p) q=0;
94
95 if(q==1)
96 {
97 L[j]++;
98 if(L[j]==1)pos[j]=i;
99 if(L[j]>maxL[j])
100 {
101 maxL[j]=L[j];
102 posL[j]=pos[j];
103 }
104 }
105 else
106 {
107 L[j]=0;
108 }
109 }
110 }
111
112 jmax=0;
113 for(j=0;j<=pmax;j++)
CAPITOLUL 23. ONI 2017 394

114 {
115 if((maxL[j]>maxL[jmax])||
116 (maxL[j]==maxL[jmax] && posL[j]<posL[jmax]))
117 {
118 jmax=j;
119 }
120 }
121
122 fout<<maxL[jmax]<<"\n";
123 fout<<posL[jmax]<<" "<<posL[jmax]+maxL[jmax]-1<<"\n";
124 }
125
126 fclose(fin);
127 fout.close();
128 return 0;
129 }

Listing 23.2.6: mirror_100p_6.cpp


1 #include <fstream>
2 #include <iostream>
3 #include <vector>
4
5 using namespace std;
6
7 int c, n, k;
8 vector<int> el;
9
10 void Solve1()
11 {
12 vector<bool> bits;
13 for (auto itr : el)
14 {
15 int p = 30;
16 for (; p >= 0; p -= 1)
17 if ((1 << p) & itr)
18 break;
19
20 if (p == -1)
21 p = 0;
22
23 for (; p >= 0; p -= 1)
24 bits.push_back(!!((1 << p) & itr));
25 }
26
27 ofstream cout("mirror.out");
28
29 for (int i = 0; i + k <= (int)bits.size(); i += k)
30 {
31 int x = 0;
32 for (int j = 0; j < k; j += 1)
33 {
34 x *= 2;
35 x += bits[i + j];
36 }
37
38 cout << x << ’ ’;
39 }
40 cout << ’\n’;
41
42 return;
43 }
44
45 void Mirror(int& a, int k)
46 {
47 int p = 30;
48 for (; p >= 0; p -= 1)
49 if ((1 << p) & a)
50 break;
51
52 if (p == -1)
53 p = 0;
54
55 vector<bool> bits;
56 for (int i = 0; i <= p; i += 1)
CAPITOLUL 23. ONI 2017 395

57 bits.push_back(!!((1 << i) & a));


58
59 for (int i = 0; i < k and bits.size(); i += 1)
60 {
61 int last = bits.back();
62 while (bits.size() and bits.back() == last)
63 bits.pop_back();
64 }
65
66 k = k & 1;
67
68 int x = 0;
69 while (bits.size())
70 {
71 x *= 2;
72 x += k ^ bits.back();
73 bits.pop_back();
74 }
75
76 a = x;
77 }
78
79 void Solve2()
80 {
81 int mx = -1;
82 int where = 0;
83
84 vector<int> bits(31, 0);
85 int right = 1;
86 for (auto itr : el)
87 {
88 Mirror(itr, k);
89 for (int p = 0; p <= 30; p++)
90 {
91 if ((1 << p) & itr)
92 {
93 bits[p]++;
94 if (bits[p] > mx)
95 {
96 mx = bits[p];
97 where = right;
98 }
99 }
100 else
101 bits[p] = 0;
102 }
103
104 right++;
105 }
106
107 ofstream cout("mirror.out");
108
109 cout << mx << ’\n’;
110 cout << where - mx + 1 << ’ ’ << where << ’\n’;
111
112 return;
113 }
114
115 int main()
116 {
117 ifstream cin("mirror.in");
118 cin >> c >> n >> k;
119 el.resize(n);
120
121 for (int i = 0; i < n; i += 1)
122 cin >> el[i];
123
124 if (c == 1)
125 Solve1();
126 else
127 Solve2();
128
129 return 0;
130 }
CAPITOLUL 23. ONI 2017 396

23.2.3 *Rezolvare detaliat 

23.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 a³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 a³eze cel mai mic num r
okcpp care este mai mare sau egal cu N .
Date de intrare
Fi³ierul de intrare okcpp.in conµine pe primul rând num rul C .
Dac  C 1, atunci pe al doilea rând se vor aa scrise, separate prin spaµiu, numerele naturale
K , P , A ³i B .
Dac  C 2, atunci pe al doilea rând se vor aa scrise, separate prin spaµiu, numerele naturale
K , P ³i N .

Date de ie³ire
Dac  C 1, atunci în ³ierul de ie³ire okcpp.out se va scrie num rul de numere okcpp din
intervalul A; B .
Dac  C 2, atunci în ³ierul de ie³ire okcpp.out se va scrie cel mai mic num r natural okcpp
care este mai mare sau egal cu N .

Restricµii ³i preciz ri
a 1 & P & 10
a P & K & num rul de cifre al lui N & 18
a Pentru 20% din teste cerinµa va  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

23.3.1 Indicaµii de rezolvare

autor - prof. Pit-Rada Ionel-Vasile


CAPITOLUL 23. ONI 2017 397

Soluµie 1 Pit-Rada Mihail-Cosmin


Pentru a verica proprietatea "okcpp" a unui num'u ar putem proceda astfel:
- dac  P 1, atunci num rul are proprietatea "okcpp"
- dac  P % 1, atunci putem calcula suma S a celor mai mari P  1 frecvenµe de apariµie ale
cifrelor din num r ³i dac  S $ K ³i exist  cel puµin P cifre distincte în num rul dat, atunci
num rul are proprietatea "okcpp", iar în caz contrar nu are proprietatea cerut 
Cerinta C=1
Se poate verica ecare num r din intervalul A, B  ³i se contorizeaz  numerele care au pro-
prietatea "okcpp"
Cerinµa C=2
Presupunem c  N are M cifre.
Dac  num rul N veric  proprietatea, atunci va  a³at, iar algoritmul se opre³te.
Daca N nu veric  proprietatea, atunci pentru P ' 2 se încearc  p strarea unui prex cât mai
lung a lui N .
Fie L lungimea prexului curent
(a) se încearc  înlocuirea cifrei din N aat  la poziµia L  1 cu o cifr  mai mare astfel încât
construcµia s  aib  proprietatea "okcpp" ³i apoi se completeaz  construcµia la poziµiile L  2, L  3,
..., M completând lexicograc minim ³i corect pân  la obµinerea lungimii M ; se pastreaz  soluµia
g sit  ³i se opreste algoritmul;
(b) dac  cu prexul de lungime L nu este posibil  construirea unei soluµii, atunci se continu 
c utarea cu un prex mai scurt;
Dac  pe parcurs a fost reµinut  o soluµie aceasta se a³eaz .
În caz contrar se construie³te o soluµie de lungime M  1 minim  lexicograc ³i cu proprietatea
"okcpp".
Se garanteaz  c  exist  sigur soluµie.

23.3.2 Cod surs 

Listing 23.3.1: okcpp_97p.cpp


1 #include<fstream>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdlib>
5
6 using namespace std;
7
8 ifstream fin("okcpp.in");
9 ofstream fout("okcpp.out");
10
11 int C,T,K,P;
12 long long A,B,X,N;
13 int v[10002], nv, x[10002], nx;
14
15 int verificare(int a[], int na)
16 {
17 int f[11]={0}, i, ok, aux, w[11]={0},nw, s1, s2, s3, h, r, z;
18 for(i=1;i<=na;i++)f[a[i]]++;
19 nw=0;
20 for(i=0;i<=9;i++)
21 if(f[i]>0)
22 nw++;w[nw]=f[i];
23
24 do
25 {
26 ok=1;
27 for(i=1;i<nw;i++)
28 if(w[i]<w[i+1])
29 ok=0; aux=w[i]; w[i]=w[i+1]; w[i+1]=aux;
30 } while(!ok);
31
32 s1=0;
33 for(i=1;i<=P-1;i++) s1=s1+w[i];
34 if((nw>=P-1 && s1<=K-1)||(nw<P-1 && s1+P-1-nw<=K-1))
35 {
36 s2=0;
CAPITOLUL 23. ONI 2017 398

37 for(i=P;i<=9;i++) s2=s2+w[i];
38
39 w[0]=1000;
40 s3=s1;
41 i=min(nw+1,P-1);
42 h=w[i];
43 while(i>=1 && (K-1-s3)/(P-i)+h>=w[i-1])
44 {
45 r=(K-1-s3)/(P-i);
46 if(r+h>w[i-1]) r=w[i-1]-h;
47 s3=s3+r*(P-i);
48 i--;
49 h=w[i];
50 }
51
52 r=(K-1-s3)/(P-i);
53 if(r+h>w[i-1]) r=w[i-1]-h;
54 h=h+r;
55 s3=s3+r*(P-i);
56 z=(K-1-s3)%(P-i)+s3+h*(10-(P-1));
57 if(z<nv) return 0;
58 return 1;
59 }
60 else
61 return 0;
62 }
63
64 int main()
65 {
66 int i,j,l,ok,r,i1,i2,ok1,aux,c;
67 fin>>C;
68
69 if(C==1)
70 {
71 fin>>K>>P>>A>>B;
72 c=0;
73 for(N=A;N<=B;N++)
74 {
75 X=N;
76 nv=0;
77 do
78 {
79 v[++nv]=X%10;
80 X=X/10;
81 } while(X);
82
83 for(i=1;i<=nv/2;i++)
84 {
85 aux=v[i];
86 v[i]=v[nv+1-i];
87 v[nv+1-i]=aux;
88 }
89
90 if(P==1 || verificare(v,nv)==1)
91 c++;
92 }
93
94 fout<<c;
95 }
96
97 if(C==2)
98 {
99 fin>>K>>P>>N;
100 X=N;
101 nv=0;
102 do
103 {
104 v[++nv]=X%10;
105 X=X/10;
106 } while(X);
107
108 for(i=1;i<=nv/2;i++)
109 {
110 aux=v[i];
111 v[i]=v[nv+1-i];
112 v[nv+1-i]=aux;
CAPITOLUL 23. ONI 2017 399

113 }
114
115 ok=0;
116 if(P==1 || verificare(v,nv)==1)
117 {
118 fout<<N<<"\n";
119 ok=1;
120 }
121
122 if(ok==0)
123 {
124 nx=0;
125 for(i=1;i<=nv;i++)
126 if(v[i]<9)
127 nx++;x[nx]=i;
128
129 i1=1; i2=nx;
130 while(i1<=i2)
131 {
132 i=(i1+i2)/2;
133 ///pastrez prefix de lungime i, crescand a i-a cifra
134 ok1=0;
135 aux=v[x[i]];
136 for(l=v[x[i]]+1;l<=9 && 0==ok1;l++)
137 {
138 v[x[i]]=l;
139 if(verificare(v,x[i])==1)
140 ok1=1;
141 }
142
143 v[x[i]]=aux;
144 if(ok1==1)
145 i1=i+1;
146 else
147 i2=i-1;
148 }
149
150 i=x[i2];
151 for(l=v[i]+1;l<=9 && i>=1 && 0==ok;l++)
152 {
153 v[i]=l;
154 if(verificare(v,i)==1)
155 {
156 for(r=i+1;r<=nv;r++)
157 {
158 v[r]=0;
159 while(verificare(v,r)==0)
160 v[r]++;
161 }
162
163 ok=1;
164 for(j=1;j<=nv;j++)
165 fout<<v[j];
166
167 fout<<"\n";
168 }
169 }
170 }
171
172 if(ok==0)
173 {
174 v[1]=1;
175 if(verificare(v,1)==1)
176 {
177 nv++;
178 for(r=2;r<=nv;r++)
179 {
180 v[r]=0;
181 while(verificare(v,r)==0)
182 v[r]++;
183 }
184 ok=1;
185 for(j=1;j<=nv;j++)
186 fout<<v[j];
187
188 fout<<"\n";
CAPITOLUL 23. ONI 2017 400

189 }
190 }
191 ///if(ok==0) fout<<-1<<"\n";
192 }
193
194 fout.close();
195 fin.close();
196 return 0;
197 }

Listing 23.3.2: okcpp_100p_1_doar_linux.cpp


1 // doar linux
2 #include <vector>
3 #include <string>
4 #include <fstream>
5 #include <numeric>
6 #include <algorithm>
7
8 using namespace std;
9
10 bool isGoodFrequency(vector<int> C, int K, int P)
11 {
12 sort(C.begin(), C.end(), greater<int>());
13 if (C[P - 1] == 0) return false;
14 return (accumulate(C.begin(), C.begin() + P - 1, 0) < K);
15 }
16
17 bool isGood(string &S, int K, int P)
18 {
19 vector<int> C(10);
20 for (char d : S) C[d - ’0’]++;
21 return isGoodFrequency(C, K, P);
22 }
23
24 bool canFill(vector<int> C, int K, int P, int N)
25 {
26 while (N--) ( *min_element(C.begin(), C.end()))++;
27 return isGoodFrequency(C, K, P);
28 }
29
30 string findNext(string S, int K, int P)
31 {
32 if (isGood(S, K, P)) return S;
33
34 vector<int> C(10);
35 for (char d : S) C[d - ’0’]++;
36
37 int i = S.length() - 1;
38 bool found = false;
39 for (; i >= 0; i--)
40 {
41 C[S[i] - ’0’]--;
42 for (char d = S[i] + 1; d <= ’9’; d++)
43 {
44 C[d - ’0’]++;
45 if (canFill(C, K, P, S.length() - i - 1))
46 {
47 found = true;
48 S[i] = d;
49 break;
50 }
51 C[d - ’0’]--;
52 }
53
54 if (found) break;
55 }
56
57 if (i < 0)
58 {
59 S = string(S.length() + 1, ’0’);
60 S[0] = ’1’;
61 i = 0;
62 C[1]++;
63 if (!canFill(C, K, P, S.length() - 1)) return "-1";
CAPITOLUL 23. ONI 2017 401

64 }
65
66 for (i++; i < S.length(); i++)
67 for (char d = ’0’; d <= ’9’; d++)
68 {
69 C[d - ’0’]++;
70 if (canFill(C, K, P, S.length() - i - 1))
71 {
72 S[i] = d;
73 break;
74 }
75 C[d - ’0’]--;
76 }
77
78 return S;
79 }
80
81 int main()
82 {
83 ifstream f("okcpp.in");
84 ofstream g("okcpp.out");
85
86 int C, K, P, ns;
87 long long A, B, X;
88 string S;
89
90 f >> C ;
91 if(C == 1)
92 {
93 f >> K >> P >> A >> B;
94 ns = 0;
95 for( X = A; X <= B ; X ++)
96 {
97 S = to_string(X);
98 if(isGood(S, K, P))
99 {
100 ns ++;
101 }
102 g << ns << endl;
103 }
104 }
105 else
106 {
107 f >> K >> P >> S;
108 g << findNext(S, K, P) << endl;
109 }
110
111 return 0;
112 }

Listing 23.3.3: okcpp_100p_2.cpp


1 // Alexandru Velea
2
3 #include <cassert>
4 #include <algorithm>
5 #include <iostream>
6 #include <vector>
7
8 using namespace std;
9
10 typedef long long int64;
11
12 string AddStuffAtEnd(string start, vector<int> initial_digits,
13 vector<int> final_digits)
14 {
15 for (int i = 0; i < 10; i += 1)
16 final_digits[i] -= initial_digits[i];
17
18 for (int i = 0; i < 10; i += 1)
19 for (int j = 0; j < final_digits[i]; j += 1)
20 start += char(’0’ + i);
21
22 return start;
23 }
CAPITOLUL 23. ONI 2017 402

24
25 vector<int> ans;
26
27 bool IsOk(vector<int> digits, int k, int p)
28 {
29 sort(digits.begin(), digits.end());
30 reverse(digits.begin(), digits.end());
31
32 for (int i = 0; i + 1 < p; i += 1)
33 k -= digits[i];
34
35 if (k > 0)
36 return true;
37 else
38 return false;
39 }
40
41 bool Possible(vector<int> digits, int k, int p, int remaining)
42 {
43 for (int i = 0; i < remaining; i += 1)
44 ( *(min_element(digits.begin(), digits.end())))++;
45
46 return IsOk(digits, k, p);
47 }
48
49 void SolveThis(vector<int> digits, int digit, int k, int p,
50 int remaining, bool& ok)
51 {
52 if (digit == 10)
53 {
54 if (remaining != 0)
55 return;
56
57 ok = true;
58 ans = digits;
59 return ;
60 }
61
62 for (int current = remaining; current >= 0; current -= 1)
63 {
64 digits[digit] += current;
65 if (Possible(digits, k, p, remaining - current))
66 {
67 SolveThis(digits, digit + 1, k, p, remaining - current, ok);
68 auto d = digits;
69 if (ok)
70 return;
71 }
72 digits[digit] -= current;
73 }
74
75 return;
76 }
77
78 void SolveFixed(vector<int> digits, int last_digit, int k, int p,
79 int remaining, bool& ok, int& digit)
80 {
81 for (int current_digit = last_digit + 1;
82 current_digit < 10;
83 current_digit += 1)
84 {
85 digits[current_digit] += 1;
86 SolveThis(digits, 0, k, p, remaining - 1, ok);
87 digits[current_digit] -= 1;
88
89 if (ok)
90 {
91 digit = current_digit;
92 return;
93 }
94 }
95
96 return;
97 }
98
99 string Solve(int k, int p, string n)
CAPITOLUL 23. ONI 2017 403

100 {
101 vector<int> digits(10, 0);
102 for (char itr : n)
103 digits[itr - ’0’] += 1;
104
105 if (IsOk(digits, k, p))
106 return n;
107
108 int to_match = 0;
109 while (n.size())
110 {
111 to_match += 1;
112 digits[n.back() - ’0’] -= 1;
113
114 bool ok = false;
115 int digit = 0;
116 SolveFixed(digits, n.back() - ’0’, k, p, to_match, ok, digit);
117 n.pop_back();
118
119 if (ok)
120 {
121 digits[digit] += 1;
122 n += char(’0’ + digit);
123 return AddStuffAtEnd(n, digits, ans);
124 }
125 }
126
127 digits = vector<int>(10, 0);
128 digits[1] += 1;
129
130 bool ok = false;
131 int digit = 0;
132 SolveFixed(digits, -1, k, p, to_match, ok, digit);
133
134 if (ok)
135 {
136 digits[digit] += 1;
137 n = "1";
138 n += char(’0’ + digit);
139 return AddStuffAtEnd(n, digits, ans);
140 }
141 else
142 return "-1";
143 }
144
145 bool IsOk(int64 n, int k, int p)
146 {
147 vector<int> digits(10, 0);
148 do
149 {
150 digits[n % 10]++;
151 n /= 10;
152 } while (n);
153
154 return IsOk(digits, k, p);
155 }
156
157 int Solve1(int k, int p, int64 left, int64 right)
158 {
159 int a = 0;
160 for (int64 i = left; i <= right; i += 1)
161 a += IsOk(i, k, p);
162
163 return a;
164 }
165
166 int main()
167 {
168 freopen("okcpp.in", "r", stdin);
169 freopen("okcpp.out", "w", stdout);
170
171 int c;
172 cin >> c;
173
174 if (c == 2)
175 {
CAPITOLUL 23. ONI 2017 404

176 int k, p;
177 string n;
178 cin >> k >> p >> n;
179 cout << Solve(k, p, n) << ’\n’;
180 }
181 else
182 {
183 int k, p;
184 int64 left, right;
185 cin >> k >> p >> left >> right;
186 cout << Solve1(k, p, left, right) << ’\n’;
187 }
188
189 return 0;
190 }

23.3.3 *Rezolvare detaliat 

23.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 ec 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  e cât mai mic posibil.

Cerinµe
Determinaµi:
1. Punctajul primului elev care nu ar mai putea  repartizat în prima clas  în condiµiile în
care toµi elevii î³i doresc s  e 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  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
Dac  C 1, atunci în ³ierul de ie³ire adlic.out se va scrie soluµia cerinµei 1.
Dac  C 2, atunci în ³ierul de ie³ire adlic.out se va scrie soluµia cerinµei 2.

Restricµii ³i preciz ri
a Punctajele elevilor vor avea cel mult ³ase cifre
a 1 & N & 1 000 000
a Pentru cerinµa 1 se garanteaz  existenµa soluµiei
a Pentru 20% din punctaj cerinµa va  C 1
a Pentru alte 20% din punctaj cerinµa va  C 2 ³i N & 1000
a Pentru restul testelor C 2 ³i N & 1000 000

Exemple:
CAPITOLUL 23. ONI 2017 405

adlic.in adlic.out Explicaµii


1 9 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 
2 9 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

23.4.1 Indicaµii de rezolvare

1. Se parcurge ³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 ³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.

23.4.2 Cod surs 

Listing 23.4.1: adlic_100p_1.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("adlic.in");
7 ofstream g("adlic.out");
8
9 int v[1000001], n=0;
10
11 int Bins(int l,int h, int w)
12 {
13 int m;
14 while(l<h)
15 {
16 m=(l+h)/2;
17 if (w>=v[m])
18 h=m;
19 else l=m+1;
20 }
21 if(l>h or w<v[h])
22 return h+1;
23 else
24 return h;
25 }
26 int main()
27 {
28 int c,w,k,uw=0,i,m;
29 f>>c>>m;
30 if(c==1)
31 {
32 for(i=1;i<=m;i++)
33 {
34 f>>w;
35 if(w>=uw)
36 uw=w;
37 else
38 g<<w;i=m;
39 }
40 }
41 else
42 for(i=1;i<=m;i++)
43 {
CAPITOLUL 23. ONI 2017 406

44 f>>w;
45 k=Bins(1,n,w);
46
47 if(k==n+1)
48 v[++n]= w;
49 else
50 v[k]=w;
51
52 g<<n;
53 }
54
55 return 0;
56 }

Listing 23.4.2: adlic_100p_2.cpp


1 // Autor: Mihai Enache
2 #include <fstream>
3
4 using namespace std;
5
6 const int MAX_N = 1000002;
7
8 int N, cerinta;
9 int punctaje[MAX_N], ultim[MAX_N];
10
11 int main()
12 {
13 ifstream f("adlic.in");
14 ofstream g("adlic.out");
15
16 f >> cerinta >> N;
17 for(int i = 1; i <= N; ++i)
18 f >> punctaje[i];
19
20 if(cerinta == 1)
21 {
22 int maxP = 0;
23 int ans = 0;
24 for(int i = 1; i <= N; ++i)
25 if(punctaje[i] < punctaje[i - 1])
26 {
27 ans = punctaje[i];
28 break;
29 }
30
31 g << ans << "\n";
32 }
33 else
34 {
35 int nrClase = 0;
36 for(int i = 1; i <= N; ++i)
37 {
38 int l = 1;
39 int r = nrClase;
40 int clasa = 0;
41 while(l <= r)
42 {
43 int m = (l + r) / 2;
44
45 if(ultim[m] <= punctaje[i])
46 {
47 clasa = m;
48 r = m - 1;
49 }
50 else
51 l = m + 1;
52 }
53
54 if(clasa)
55 ultim[clasa] = punctaje[i];
56 else
57 {
58 ++nrClase;
59 ultim[nrClase] = punctaje[i];
CAPITOLUL 23. ONI 2017 407

60 }
61 }
62
63 g << nrClase << "\n";
64 }
65
66 f.close();
67 g.close();
68
69 return 0;
70 }

Listing 23.4.3: adlic_100p_3.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 long mx[1000000];
7
8 int main()
9 {
10 ifstream in("adlic.in");
11 ofstream out("adlic.out");
12
13 int V,rep;
14 long p,i=0,k,st,dr, max, m; //j;
15 in>>V;
16 if(V==1) // cerinta 1
17 {
18 in>>p; max=p; k=1;
19 while(!in.eof())
20 {
21 in>>p;
22 if(p>=max)
23 max=p;
24 else
25 {
26 out<<p;
27 break;
28 }
29 }
30
31 //cout<<k;
32 }
33 else // cerinta 2
34 {
35 k=0;
36 while(in>>p)
37 {
38 st=1;dr=k;
39 while(st<=dr)
40 {
41 m=(st+dr)/2;
42 if(p>=mx[m])
43 dr=m-1;
44 else
45 st=m+1;
46 }
47
48 if(st>k)
49 {
50 k++;
51 mx[k]=p;
52 }
53 else
54 {
55 mx[st]=p;
56 }
57 }
58
59 out<<k;
60 // cout<<k;
61 }
CAPITOLUL 23. ONI 2017 408

62
63 in.close();
64 out.close();
65 return 0;
66 }

23.4.3 *Rezolvare detaliat 

23.5 bomboane
Problema 5 - bomboane 100 de puncte
Zeno are n cutii cu bomboane, iar în ecare 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 ecare 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 ecare cutie se împart în mod egal între cei k colegi, ace³tia primind un
num r nenul de bomboane;
- în nal în ecare 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 ecare 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 ecare 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 ecare 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 ecare 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 ecare 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
ecare cutie.

Date de ie³ire
Dac  p 1 se va rezolva numai punctul a) din cerinµ . În acest caz ³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
ecare 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 .

Restricµii ³i preciz ri
CAPITOLUL 23. ONI 2017 409

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 a³a oricare.
a Pentru rezolvarea ec 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 pot
14 23 17 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 ecare coleg s  primeasc  un num r diferit de bomboane, iar
diferenµele dintre bomboanele primite de doi colegi consecutivi
s  e 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

23.5.1 Indicaµii de rezolvare

a) împ rµirea fr µeasc 
e 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 veric  dac  num rul de bomboane
din ecare 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 ecare cutie este cuprins între 0 ³i
min(a[i]).
Complexitate O(N*min(a[i])
Metoda 3 - 100% din punctaj
e a[i] - numarul de bomboane din cutia i
Trebuie determinat un numar k astfel incat ecare 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  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  e 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 23. ONI 2017 410

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 a³a o împ rtire este
1 ci 2 ci-1 3 ci-2 .... (de exemplu pt 10 bomboane 1, 4, 2, 3)
La a³area primei perechi trebuie avut în vedere faptul c  dac  ci * (ci + 1) < a[i] atunci se
a³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

l <- 1 //pornim de la marginea stanga


r <- ci //pornim de la marginea dreapta

cat timp l <= r executa


daca suntem la prima pereche
afiseaza l si k + a[i] - ci * (ci + 1) / 2
altfel
afiseaz l si r

l <- l + 1//trecem la urmatoarele valori


r <- r - 1
sf cat timp

Datorit  faptului c  la ecare pas diferenµa între 2 elemente consecutive scade, se respect 
cerinµa ca elementele consecutive s  aib  diferenµa distinct .

23.5.2 Cod surs 

Listing 23.5.1: bomboane_100p_1.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream in("bomboane.in");
7 ofstream out("bomboane.out");
8
9 int n,p,a[10002],i,k;
10
11 int cmmdc(int a, int b)
12 {
13 int r;
14 while(b > 0)
15 {
16 r = a % b;
17 a = b;
18 b = r;
19 }
20 return a;
21 }
22
23 int nr_max_col(int n)
24 {
25 //determinam valoarea maxima pentru k, a.i k * (k + 1) / 2 <= n
26 int k;
27 for (k = 1; ; k++)
28 if (k * (k + 1) / 2 > n)
29 return k-1;
30 }
31
32 int main()
33 {
34 in >> p >> n;
CAPITOLUL 23. ONI 2017 411

35 for(i = 1; i <= n; i++) in >> a[i];


36
37 if(p == 1)
38 {
39 int vmin = a[1];
40
41 //determinam vmin = min(a[1], a[2] ... a[n])
42 for(i = 1; i <= n; i++)
43 if(vmin > a[i]) vmin = a[i];
44
45 //scadem vmin din elementele vectorului a
46 for(i = 1; i <= n; i++) a[i] = a[i] - vmin;
47
48 //dereminam k = cmmdc(a[1], a[2] ... a[n])
49 k = 0;
50 for(i = 1; i <= n; i++)
51 k = cmmdc(a[i], k);
52
53 out << k;
54 }
55 else
56 {
57 int l, r;
58 for(i = 1; i <= n; i++){
59 k = nr_max_col(a[i]); // determinam valoarea maxima pentru k,
60 // a.i k * (k + 1) / 2 <= a[i]
61
62 out << k << " ";
63 // cea mai simpla modalitate este sa se afiseze 1 k 2 k-1 3 k-2
64 // etc
65 // la afisarea primei prechi trebuie avut in vedere ca daca
66 // k * (k + 1) < n
67 //atunci se afiseaza 1 si k + diferenta (a[i] - k * (k+1) / 2
68
69 l = 1; // se porneste cu 2 indici l = 1
70 r = k; // si r = k si se afiseaza alternativ
71
72 //dupa fiecare afisare l creste, iar r scade
73 while(l <= r)
74 {
75 if(l != r)
76 {
77 out << l << " ";
78 if(r == k) out << r + a[i] - k * (k+1) / 2<< " ";
79 else out << r << " ";
80 }
81 else
82 out << l << " ";
83
84 l = l + 1;
85 r = r - 1;
86 }
87
88 out << "\n";
89 }
90 }
91
92 return 0;
93 }

Listing 23.5.2: bomboane_100p_2.cpp


1 #include <iostream>
2 #include <fstream>
3 #include <math.h>
4
5 using namespace std;
6
7 long cmmdc(long a, long b)
8 {
9 while(a!=b)
10 if(a>b)
11 a=a-b;
12 else
13 b=b-a;
CAPITOLUL 23. ONI 2017 412

14 return a;
15 }
16
17 int main()
18 {
19 ifstream in("bomboane.in");
20 ofstream out("bomboane.out");
21
22 int p,n,i,k=0,j,nb;
23 long b[10001], c[10001], min, s, r, st, dr;
24 in>>p>>n;
25 // cout<<n<<endl;
26 if(p==1) //cerinta 1
27 {
28 in>>b[1]; min=b[1];
29 for(i=2;i<=n;i++)
30 {
31 in>>b[i];
32 if(b[i]<min)
33 min=b[i];
34 }
35
36 for(i=1;i<=n;i++)
37 b[i]=b[i]-min;
38 i=1;
39 while(b[i]==0)
40 i++;
41 k=b[i];
42 for(i=2;i<=n;i++)
43 if(b[i]!=0)
44 k=cmmdc(k,b[i]);
45 out<<k;
46 }
47 else //cerinta 2
48 {
49 for(i=1;i<=n;i++)
50 {
51 in>>b[i];
52 c[i]=sqrt(2*b[i]);
53 if(c[i]*(c[1]+1)>2*b[i])
54 c[i]--;
55
56 s=0;
57 j=0;
58 do
59 {
60 j++;
61 s=s+j;
62 } while(s<=b[i]);
63
64 if(s==b[i])
65 r=j;
66 else
67 {
68 s=s-j;
69 j--;
70 r=j+b[i]-s;
71 }
72
73 st=1;
74 dr=j;
75 p=1;
76 out<<j<<’ ’;
77 while(st<=dr)
78 {
79 if(p==1)
80 out<<1<<’ ’<<r<<’ ’;
81 else
82 if(st==dr)
83 out<<st<<’ ’;
84 else
85 out<<st<<’ ’<<dr<<’ ’;
86
87 st++;
88 dr--;
89 p++;
CAPITOLUL 23. ONI 2017 413

90 }
91
92 out<<endl;
93 }
94 }
95
96 in.close();
97 out.close();
98 return 0;
99 }

Listing 23.5.3: bomboane_100p_3.cpp


1 #include<fstream>
2 #include<cmath>
3
4 using namespace std;
5
6 ifstream fin("bomboane.in");
7 ofstream fout("bomboane.out");
8
9 int p,n,a[10005];
10
11 int cmmdc(int a, int b)
12 {
13 if(b==0)
14 return a;
15 return cmmdc(b,a%b);
16 }
17
18 void impf()
19 {
20 int amin=a[1], i, k;
21 for(i=2;i<=n;i++)
22 amin=min(amin,a[i]);
23 k=a[1]-amin;
24 for(i=2;i<=n;i++)
25 k=cmmdc(k,a[i]-amin);
26 fout<<k;
27 }
28
29 void d(int s)
30 {
31 int a,i,r=(sqrt(8*s+1)-1)/2;
32 a=(r+1)/2;
33 fout << r << " " << a << " ";
34 s=s-a;
35 for(i=1;i<=(r-2)/2;i++,s=s-2*a)
36 {
37 fout << a+i << " " << a-i << " ";
38 }
39
40 if(r%2==0)
41 fout << s << "\n";
42 else
43 fout << s-1 << " 1\n";
44 }
45
46 void impd()
47 {
48 for(int i=1;i<=n;i++)
49 d(a[i]);
50 }
51
52 int main()
53 {
54 fin>>p>>n;
55 for(int i=1;i<=n;i++)
56 fin>>a[i];
57 if(p==1)
58 impf();
59 else
60 impd();
61
62 fout.close();
CAPITOLUL 23. ONI 2017 414

63 return 0;
64 }

23.5.3 *Rezolvare detaliat 

23.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 ecare drum, se cunoa³te lungimea lui, exprimat  în metri ³i viteza cu care se poate
parcurge, exprimat  în metri pe secund .

Cerinµe
Jupânul trebuie s  ajung  din ora³ul 0 în ora³ul N .
Acesta ³tie c  poate îmbun t µi un drum, m rindu-i viteza de la V metri pe secund  la V +
1 metri pe secund , cu costul de 1 dolar. Acesta poate îmbun t µi un drum de mai multe ori.
Jupânul are un buget de X dolari ³i ar vrea s -i foloseasc  pentru a mic³ora timpul în care
ajunge din ora³ul 0 în ora³ul N .

Date de intrare
Fi³ierul de intrare orase.in are urm toarea structur :
a Pe prima linie se a  num rul T , reprezentând tipul de restricµii pe care îl respect  testul.
a Pe a doua linie, se a  2 numere naturale N ³i X .
a Pe a treia linie se a  N numere naturale, al i-lea num r reprezentând distanµa între ora³ele
i  1 ³i i.
a Pe a patra linie se a  N numere naturale, al i-lea num r reprezentând viteza între ora³ele
i  1 ³i i.

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 .

Restricµii ³i preciz ri
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 nale vor  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 23. ONI 2017 415

orase.in orase.out Explicaµii


1 3 Timpul minim este 3.65, iar rezultatul este [3.65]=3
3 5 Vitezele nale vor  4, 3, 5
5 37 5 / 4 + 3 / 3 + 7 / 5 = 3.65
2 14
1 4 Timpul minim este 4.321, iar rezultatul este [4.321]=4
4 6 Vitezele nale vor  4, 7, 7, 5
3 8 10 5 3 / 4 + 8 / 7 + 10 / 7 + 5 / 5 = 4.32142857
4 373
1 4 Timpul minim este 4.65, iar rezultatul este [4.65]=4
5 6 Vitezele nale vor  5, 4, 3, 3, 3
2 5324 2 / 5 + 5 / 4 + 3 / 3 + 2 / 3 + 4 / 3 = 4.65
5 1213
Timp maxim de executare/test: 1.0 secunde
Memorie: total 32 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 15 KB

23.6.1 Indicaµii de rezolvare

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 ocial  - 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, ecare î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 drumu-
rilor.
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 ind 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  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 ecare 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 ecare pas prin toate drumurile ³i s  îl alegem pe cel care salveaz  cât mai
mult timp, putem itera prin distanµele drumurilor, a 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 xat  (este posibil s  e mai multe astfel de drumuri)
Astfel, ecare îmbun t µire se poate realiza în 200 de iteraµii, nu în N (50 000).
CAPITOLUL 23. ONI 2017 416

Pentru a aa drumul de vitez  minim  pentru o distanµ  xat , 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 ind necesar  doar la calcularea rezultatului.
O soluµie ar  s  µinem un ³ir de frecvenµe pentru ecare 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 aa 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 nal, î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 ocial 
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  e < 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

Dac  vrem V minim, ajungem la ecuaµia


CAPITOLUL 23. ONI 2017 417

0 = V * V + V - D / T
delta = 1 + 4 * D / T
V1,2 = (-1 +- sqrt(1 + 4 * D / T)) / 2

cum V e pozitiv, valoarea lui va 


V = -0.5 + sqrt(1 + 4 * D / T) / 2
V trebuie rotunjit în sus, deoarece dac  V = 3.3 înseamn  c  pentru V = 3 înc  îmbun t µirea
de la 3 la 4 este mai bun  decât T.
Astfel, putem calcula în O(N) de câte îmbun t µiri am avea nevoie pentru un timp de îmbu-
n t µire xat.
C utarea binar  se va face pe numere reale, la fel ca cea pe întregi, doar c  pe reale.
În loc de stanga < dreapta se vor face 40 de iteraµii de exemplu.
La nal aducem toate drumurile s  respecte valoarea c utat  binar.
Dac  ³tim c  pentru 1.5000000 avem nevoie de 10 îmbun t µiri, dar pentru 1.5000001 avem
nevoie de 15 îmbun t µiri, având 13 îmbun t µiri posibile, putem spune c  o s  mai îmbun t µim
3 drumuri, ecare drum salvând 1.5000001 secunde.
Nu conteaz  exact ce drumuri îmbun t µim. “tim c  ele exist , ³i c  sunt x 5 care au acea
proprietate.
Acest lucru se întâmpl  când avem multe drumuri cu aceea³i distanµ , sau cu distanµe propor-
µionale.
Timp nal: O N logX 
Memorie: O N 
Dac  nu facem abstracµie de faptul c  funcµia sqrt este înceat , ³i c  în practic  face tot O log ,
timpul este O N logXlog _de_la_sqrt

23.6.2 Cod surs 

Listing 23.6.1: orase_100p.cpp


1 // Problema Orase - Oni clasa a 9-a
2 // sursa Velea Alexandru
3 // time O(n * log2(x))
4 // 100 points
5
6 #include <cmath>
7
8 #include <iomanip>
9 #include <fstream>
10 #include <iostream>
11 #include <queue>
12 #include <vector>
13
14 using namespace std;
15
16 typedef long long int64;
17
18 const int kAcceptedRemaining = 2e4;
19
20 struct Road
21 {
22 int distance;
23 int speed;
24 int id;
25
26 long double SavedTime(int bonus_time = 0) const
27 {
28 return 1.0*distance / (speed+bonus_time) / (speed+1+bonus_time);
29 }
30
31 long double Time(int bonus_time = 0) const
32 {
33 return 1.0 * distance / (speed + bonus_time);
34 }
35
36 bool operator<(const Road& rhs) const
37 {
38 return SavedTime() < rhs.SavedTime();
CAPITOLUL 23. ONI 2017 418

39 }
40 };
41
42 int64 x;
43 vector<Road> roads;
44
45 int Read()
46 {
47 ifstream cin("orase.in");
48 int test_type;
49 cin >> test_type;
50
51 int n;
52 cin >> n >> x;
53
54 roads.resize(n);
55
56 for (int i = 0; i < n; i += 1)
57 roads[i].id = i;
58
59 for (auto& itr : roads)
60 cin >> itr.distance;
61
62 for (auto& itr : roads)
63 cin >> itr.speed;
64
65 return 1;
66 }
67
68 int is_read = Read();
69
70 int64 UpgradeRoad(int index, long double saved_time)
71 {
72 return max(0LL,
73 int64(1.0 + -0.5 +
74 sqrt(1.0+4.0*roads[index].distance / saved_time) / 2.0 +
75 1e-10) -
76 roads[index].speed);
77 }
78
79 vector<int64> GetUpgrades(long double saved_time)
80 {
81 vector<int64> answer(roads.size(), 0);
82
83 for (int i = 0; i < (int)roads.size(); i += 1)
84 answer[i] = UpgradeRoad(i, saved_time);
85
86 return answer;
87 }
88
89 int64 GetNumOperations(long double saved_time)
90 {
91 auto upgrades = GetUpgrades(saved_time);
92
93 int64 answer = 0;
94 for (auto itr : upgrades)
95 answer += itr;
96
97 return answer;
98 }
99
100 long double EstimateSolution()
101 {
102 long double mx_save = 0;
103 for (auto& itr : roads)
104 mx_save = max(mx_save, itr.SavedTime());
105
106 long double left = 0, right = mx_save;
107
108 int64 mx_upgrades = 0;
109 long double mx_where = 0;
110
111 int64 mn_upgrades = 1e9;
112 long double mn_where = 0;
113
114 for (int i = 0; i < 100; i += 1)
CAPITOLUL 23. ONI 2017 419

115 {
116 long double mid = (right + left) / 2;
117 int64 num_operations = GetNumOperations(mid);
118
119 if (num_operations == x)
120 {
121 mx_upgrades = num_operations;
122 mx_where = mid;
123 break;
124 }
125
126 if (num_operations > x)
127 {
128 left = mid;
129
130 if (mn_upgrades >= num_operations)
131 {
132 mn_upgrades = num_operations;
133 mn_where = mid;
134 }
135 }
136 else
137 {
138 right = mid;
139
140 if (mx_upgrades <= num_operations)
141 {
142 mx_upgrades = num_operations;
143 mx_where = mid;
144 }
145 }
146 }
147
148 auto upgrades = GetUpgrades(mx_where);
149
150 int64 sum = 0;
151 for (int i = 0; i < (int)roads.size(); i += 1)
152 {
153 roads[i].speed += upgrades[i];
154 sum += upgrades[i];
155 }
156
157 x -= sum;
158
159 double answer = 0;
160 for (auto& itr : roads)
161 answer += itr.Time();
162
163 answer -= 1.0 * mx_where * x;
164
165 return answer;
166 }
167
168 long double Solve()
169 {
170 return EstimateSolution();
171 }
172
173 int main()
174 {
175 long double answer = Solve();
176
177 ofstream cout("orase.out");
178 cout << int64(answer) << ’\n’;
179
180 return 0;
181 }

23.6.3 *Rezolvare detaliat 


Capitolul 24

ONI 2016

24.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 ecare an cartiere de câte un hectar
ecare î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 ecare 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 a  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.

420
CAPITOLUL 24. ONI 2016 421

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 ³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 ³ierului conµine dou  numere naturale x ³i y reprezentând dimensiunile h rµii.
A treia linie a ³ierului conµine num rul natural t.
A patra linie a ³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
Dac  valoarea lui p este 1, atunci se va rezolva numai prima cerinµ .
În acest caz, în ³ierul de ie³ire civilizatie.out se va scrie un singur num r natural, reprezen-
tând aria suprafeµei (în hectare) unui ora³ dup  t ani, dac  nu ar întâlni nici un alt ora³ ³i nici nu
ar ajunge la marginea h rµii.
Dac  valoarea lui p este 2 atunci, se va rezolva numai a doua cerinµ .
În acest caz, ³ierul de ie³ire va conµine pe prima linie un num r natural reprezentând aria
suprafeµei din hart  r mas  neocupat  dup  ce toate cele N ora³e ³i-au încetat expansiunea, iar
pe a doua linie un num r natural reprezentând timpul scurs pân  când ultimul ora³ s-a oprit din
expansiune.

Restricµii ³i preciz ri
a 1 & N & 2000
a 1 & x, y, t & 100 000
a Pentru 30% din teste se garanteaz  faptul c  x, y & 500
a Pentru rezolvarea corect  a primei cerinµe se acord  20 de puncte, iar pentru rezolvarea
corect  a celei de-a doua cerinµe se acord  80 de puncte.

Exemple:
civilizatie.in civilizatie.out Explicaµii
1 145 p = 1, în ³ier se va scrie aria suprafeµei ce ar putea  ocupat 
79 de un ora³ în timp de 9 ani.
9 Atenµie! Pentru acest test se rezolv  doar cerinµa 1).
2
32
46
2 33
79 4
5
2
32
46
2 97 p=2, deci se rezolv  doar cerinµa 2
10 10 1 în acest caz, cele 3 civilizaµii nu se vor putea extinde deloc, deci
5 celelalte 97 de hectare r mân neocupate.
3
22
24
32
Timp maxim de executare/test: 3.0 secunde
Memorie: total 128 MB
Dimensiune maxim  a sursei: 15 KB
CAPITOLUL 24. ONI 2016 422

24.1.1 Indicaµii de rezolvare


prof. Marcel Dr gan

Cerinµa 1 - Soluµia 1
Consider m un tablou bidimensional, în care ecare 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 ecare 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 ecare 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  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  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.

24.1.2 Cod surs 

Listing 24.1.1: civilizatie_BICSI.cpp


1 #include <fstream>
2 #include <vector>
3 #include <cmath>
4 #include <utility>
5 #include <iostream>
CAPITOLUL 24. ONI 2016 423

6
7 #define cin fin
8
9 using namespace std;
10
11 typedef pair<int, int> Pair;
12
13 const int MAXN = 2005;
14 const int MAXD = 1e6 + 1;
15
16 int Stopped[MAXN], X[MAXN], Y[MAXN], T[MAXN][MAXN];
17 vector<Pair> Meets[MAXD];
18
19 int maxx, maxy, n, now;
20 long long Extend[MAXD];
21 vector<int> ToStop;
22
23 int getTime(int i, int j)
24 {
25 if(j == 0)
26 return min(min(X[i], Y[i]), min(maxx - X[i] + 1, maxy - Y[i] + 1));
27
28 int d = abs(X[i] - X[j]) + abs(Y[i] - Y[j]);
29 if(Stopped[j] >= 0)
30 return d - Stopped[j] + 1;
31 return (1 + d) / 2;
32 }
33
34 void addEvent(int i, int j)
35 {
36 int t = getTime(i, j);
37 Meets[t].push_back(make_pair(i, j));
38 }
39
40 int main()
41 {
42 int p, t;
43
44 ifstream cin("civilizatie.in");
45 ofstream cout("civilizatie.out");
46
47 cin >> p;
48 cin >> maxx >> maxy;
49 Extend[1] = 1;
50 for(int i = 2; i < MAXD; ++i)
51 Extend[i] = Extend[i - 1] + 4 * i - 4;
52
53 cin >> t;
54 if(p == 1)
55 {
56 cout << Extend[t];
57 return 0;
58 }
59 else
60 {
61 cin >> n;
62
63 for(int i = 1; i <= n; ++i)
64 {
65 cin >> X[i] >> Y[i];
66 Stopped[i] = -1;
67 }
68
69 for(int i = 1; i <= n; ++i)
70 for(int j = n; j >= 0; --j)
71 if(i != j)
72 addEvent(i, j);
73
74 int cnt = n;
75 for(now = 1; cnt; ++now)
76 {
77 for(int z = 0; z < Meets[now].size(); ++z)
78 {
79 Pair p = Meets[now][z];
80 if(Stopped[p.first] == -1 &&
81 now == getTime(p.first, p.second))
CAPITOLUL 24. ONI 2016 424

82 {
83 ToStop.push_back(p.first);
84 }
85 }
86
87 for(int i = 0; i < ToStop.size(); ++i)
88 {
89 int no = ToStop[i];
90 if(Stopped[no] >= 0) continue;
91
92 Stopped[no] = now;
93 --cnt;
94
95 for(int j = 1; j <= n; ++j)
96 if(Stopped[j] == -1)
97 addEvent(j, no);
98 }
99
100 ToStop.clear();
101 }
102
103 long long ans = 1LL * maxx * maxy;
104 for(int i = 1; i <= n; ++i)
105 ans -= Extend[Stopped[i]];
106 }
107
108 return 0;
109 }

Listing 24.1.2: civilizatie100p.cpp


1 #include <fstream>
2 #include <cmath>
3
4 using namespace std;
5
6 ifstream fin("civilizatie.in");
7 ofstream fout("civilizatie.out");
8
9 #define nrCiv 2001 // numarul maxim de civilizatii
10
11 int p // tipul de cerinta
12 ,n // mumarul de civilizatii
13 ,nx,ny // dimensiunile hartii
14 ,x[nrCiv],y[nrCiv] // coordonatele initiale ale oraselor
15 ,dxyMin[nrCiv] // timpul pana la atingerea marginilor
16 ,dMin[nrCiv] // timpul pana la oprire
17 ,difMin[nrCiv][nrCiv] // distata dintre oricare doua civilizatii
18 ,difMin2[nrCiv][nrCiv] // timpul minim dintre oricare doua civilizatii
19 ,lMin[nrCiv] // minimele pe linii
20 ,jlMin[nrCiv]; // pozitia minimului pe linii
21
22 unsigned long long t // timpul pt cerinta 1
23 ,ct[nrCiv]; // suprafata ocupata de orase
24 int main()
25 {
26 fin>>p>>nx>>ny>>t>>n;
27 if(p==1)
28 {
29 unsigned long long ct=ct+2*t*(t-1)+1;;
30 fout<<ct<<’\n’;
31 }
32 else
33 {
34 for(int i=1;i<=n;i++)
35 fin>>x[i]>>y[i];
36
37 // minime pana la margini
38 for(int i=1;i<=n;i++)
39 {
40 int dxMin=min(nx-x[i]+1,x[i]),dyMin=min(ny-y[i]+1,y[i]);
41 dxyMin[i]=min(dxMin,dyMin);
42 }
43
44 // calcul distantei initiale pana la celelalte civilizatii
CAPITOLUL 24. ONI 2016 425

45 for(int i=1;i<=n;i++)
46 {
47 lMin[i]=nx+ny;
48 for(int j=1;j<=n;j++)
49 {
50 difMin[i][j]=abs(x[i]-x[j])+abs(y[i]-y[j])+1;
51 difMin2[i][j]=difMin[i][j]/2;
52 if(difMin2[i][j]<lMin[i] && i!=j)
53 {
54 lMin[i]=difMin2[i][j];jlMin[i]=j;
55 }
56 }
57 }
58
59 // selectare orase in ordinea opriri expansiunii
60 int nr=0;
61 while(nr<n)
62 {
63 // cautare minim
64 int mMin=nx+ny,iMin,jMin,im=0;
65 for(int i=1;i<=n;i++)
66 {
67 if(mMin>lMin[i] && !dMin[i])
68 {
69 mMin=lMin[i];iMin=i;jMin=jlMin[i];im=0;
70 }
71 if(mMin>dxyMin[i] && !dMin[i])
72 {
73 mMin=dxyMin[i];im=i;
74 }
75 }
76
77 // calcul timp de oprire oras selectat si ajustare timpi
78 // pentru celelalte
79 int i=iMin,j=jMin;
80 if(im!=0)
81 {
82 i=im;dMin[i]=dxyMin[i];
83 }
84 else
85 {
86 if(dMin[i])
87 { // civilizatia i verificata deja
88 i=jMin;j=iMin;
89 }
90
91 dMin[i]=difMin2[i][j];
92 }
93
94 nr++;
95 lMin[i]=nx+ny;
96 for(int k=1;k<=n;k++) // pt toate elementele de pe col i
97 if(!dMin[k] || !dMin[i])
98 if(difMin2[k][i]!=dMin[i])
99 {
100 difMin2[k][i]=difMin2[i][k]=difMin[k][i]-dMin[i];
101 if(difMin2[k][i]<lMin[i] &&
102 k!=i &&
103 (!dMin[k] || !dMin[i]))
104 {
105 lMin[i]=difMin2[k][i];jlMin[i]=k;
106 }
107 if(jlMin[k]==i)
108 {
109 lMin[k]=nx+ny;
110 for(int ik=1;ik<=n;ik++) // pt toate elementele
111 // de pe linia k
112 if(difMin2[k][ik]<lMin[k] && k!=ik )
113 {
114 lMin[k]=difMin2[k][ik];jlMin[k]=ik;
115 }
116 }
117 }
118 }
119
120 // calcul suprafete
CAPITOLUL 24. ONI 2016 426

121 for(int i=1;i<=n;i++)


122 {
123 t=dMin[i];
124 ct[i]=2*t*(t-1)+1;
125 }
126
127 unsigned long long tot=0;
128 int dMax=0;
129 for(int i=1;i<=n;i++)
130 {
131 tot=tot+ct[i];
132 if(dMax<dMin[i])
133 dMax=dMin[i];
134 }
135
136 fout<<(unsigned long long)nx*ny-tot<<’\n’;
137 fout<<dMax<<’\n’;
138 }
139
140 return 0;
141 }

24.1.3 *Rezolvare detaliat 

24.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 a  cele k numere din ³ir care dau cel mai mare divizor comun
maxim.

Restricµii ³i preciz ri
a 1 & n & 1 000 000
a 2 & k & 100 000
a k&n
a 1 & ai & 1 000 000, i 1..n
a Valorile din ³ir se pot repeta.

Exemple:
cmmdc.in cmmdc.out Explicaµii
63 3 Cel mai mare divizor comun care se poate obµine dintr-un grup
6 9 8 10 15 3 15 9 6 de 3 numere este 3, iar cele 3 numere care dau suma maxim ,
ordonate descresc tor, sunt 15, 9 ³i 6.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 64 MB
Dimensiune maxim  a sursei: 15 KB
CAPITOLUL 24. ONI 2016 427

24.2.1 Indicaµii de rezolvare

prof. Stelian Ciurea, prof. Dan Pracsiu

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 e egale
cu x, e sunt multiplii ai numarului x.
Astefel se parcurge cu o variabila x descrescator intervalul 1 000 000 2 si pentru ecare
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:

max a max a max 3 max a max a


   ...  x  ... 
1 2 1 max a

Rezolv ri alternative se pot face prin generare de submultimi:


- una dintre surse genereaza o submultime de k elemente, ii calculeaza cmmdc-ul si o reµine
daca are cmmdc-ul maxim - 25 puncte
- celalta e optimizat  în sensul c  se calculeaz  cmmdc-ul dup  ecare element ad ugat la
submulµime ³i dac  acesta e mai mic decât maximul de pân  atunci se trece la alegerea altui
element - 40 puncte

În ambele surse alternative se sorteaz  elementele descresc tor.

24.2.2 Cod surs 

Listing 24.2.1: cmmdc_BICSI.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 const int MAXN = 2e6;
7
8 int Freq[MAXN], FreqFact[MAXN];
9
10 int main()
11 {
12 ifstream fin("cmmdc.in");
13 ofstream fout("cmmdc.out");
14
15 int n, val, k, maxx = 0;
16
17 fin >> n >> k;
18 for(int i = 1; i <= n; ++i)
19 {
20 fin >> val;
21 Freq[val] += 1;
22 maxx = max(maxx, val);
23 }
24
25 for(int i = 1; i <= maxx; ++i)
26 for(int j = i; j <= maxx; j += i)
27 FreqFact[i] += Freq[j];
28
29 for(int i = maxx; i; --i)
CAPITOLUL 24. ONI 2016 428

30 if(FreqFact[i] >= k)
31 {
32 fout << i << endl;
33 for(int j = maxx / i * i; j; j -= i)
34 while(Freq[j] > 0)
35 {
36 fout << j << " ";
37 if(--k == 0)
38 return 0;
39 --Freq[j];
40 }
41 }
42
43 return 0;
44 }

24.2.3 *Rezolvare detaliat 

24.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 ecare rând aâ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  culeas , iar pentru alµii aceasta poate  determinat  pe baza unei formule.
Quinto ³i-a propus s  recolteze C kg de fructe din pomii aaµi în livada lui. Acesta folose³te un
utilaj modern pentru culesul fructelor. Utilajul poate  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 re-
coltarea 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 dicil
s  culeag  x 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
Pe prima linie a ³ierului livada.in se a  4 numere naturale N , M , C , R cu semnicaµia din
enunµ.
Pe a doua linie din ³ierul de intrare se a  5 numere naturale x, y , z , w, u, separate printr-un
spaµiu.
Dac  not m cu Aij  cantitatea de fructe (exprimat  în kg) din cel de-al j -lea pom de pe
linia i, atunci:
Linia a treia din ³ierul de intrare conµine M valori A1i, 1 & i & M , separate printr-un
spaµiu
Linia a patra din ³ierul de intrare conµine N  1 valori Ai1, 2 & i & N , separate printr-un
spaµiu
Celelalte valori Aij , 2 & i & N , 2 & j & M , se calculeaz  conform formulei:

Aij  x ˜ Ai  1j   y ˜ Aij  1  z ˜ Ai  1j  1  w%u.

Date de ie³ire
CAPITOLUL 24. ONI 2016 429

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 
e respectate toate restricµiile problemei.

Restricµii ³i preciz ri
a 1 & R & N & 100
a 1 & M & 25 000
a 0 & x, y, z, w, u & 10
9

a 0 & Aij  & 10


9

a Atenµie la determinarea ec 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

a Se garanteaz  c  pentru toate testele problema are soluµie


a Pentru 30% din teste se garanteaz  faptul c  1 & M & 100 ³i 1 & Aij  & 100
a Pentru 70% din teste se garanteaz  faptul c  1 & M & 4 000

Exemple:
livada.in livada.out Explicaµii
5 6 18 4 3 6 5 4 Sunt 5 rânduri cu câte 6 pomi pe ecare 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 gura 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  e 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

24.3.1 Indicaµii de rezolvare

prof. Mircea Lup³e-Turpan - Liceul Teoretic Grigore Moisil Timisoara

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 veric  pe rând valori posibile ³i se
alege valoarea minim  care respect  restricµiile problemei).
Soluµii parµiale 30 / 70 puncte
Odat  xat  valoarea pentru cantitatea maxim  de kg de fructe culese dintr-un singur pom,
se determin  cantitatea maxim  de fructe care poate  culeas  din întreaga livad  astfel încât s 
e respectate restricµiile problemei.
Aceast  valoare poate  aat  dac  se determin  pentru ecare rând cantitatea maxim  de
fructe care poate  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 xat , ³i se memoreaz  aceste valori într-un vector,
2
care este apoi sortat descresc tor. Este sucient 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  adunate din livad .
Dup  determinarea cantit µii maxime care poate  culeas  din întreaga livad , se veric  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 ind O N ˜ M ˜ M axV al, unde N e num rul de rânduri,
M e num rul de pomi de pe ecare 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 ind O N ˜ M ˜ log M axV al.
CAPITOLUL 24. ONI 2016 430

Soluµie 100 puncte


Este necesar  precalcularea unor sume parµiale ³i maxime parµiale pentru ecare rând.
Vom calcula pentru ecare rând i:
Sumij  - Cantitatea de fructe din pomii numerotaµi de la 1 la j , de pe rândul i
M axij  - Cea mai mare cantitate de fructe dintr-unul din pomii numerotaµi de la 1 la j de
pe rândul i
Folosind aceste sume parµiale, cantitatea maxim  de fructe care poate  culeas  de pe un rând
i poate  determinat  prin c utarea binar  a celei mai mari valori mai mici decat valoarea xat 
de pe linia i a matricei M ax (se observ  c  pe orice linie din matricea M ax valorile sunt în ordine
cresc toare) ³i reµinerea valorii din linia i a matricei Sum.
În acest fel, operaµia de vericare a unei valori xate pentru soluµia problemei are complexitatea
O N ˜ log M , complexitatea determin rii soluµiei devenind O N ˜ log M ˜ log M axV al, dar
complexitatea algoritmului este totu³i O N ˜ M  din cauza gener rii matricei.

24.3.2 Cod surs 

Listing 24.3.1: livada_dan.cpp


1 #include <bits/stdc++.h>
2
3 #define inFile "livada.in"
4 #define outFile "livada.out"
5
6 #define Nmax 101
7 #define Mmax 25001
8
9 using namespace std;
10
11 int a[Nmax][Mmax], mx[Nmax][Mmax];
12 long long s[Nmax][Mmax];
13 long long t[Mmax];
14 int n, m, R;
15 long long C;
16
17 void Citire()
18 {
19 int i, j, x, y, z, w, u;
20 ifstream fin(inFile);
21 fin >> n >> m >> C >> R;
22 fin >> x >> y >> z >> w >> u;
23 for (i = 1; i <= m; ++i)
24 fin >> a[1][i];
25 for (i = 2; i <= n; ++i)
26 fin >> a[i][1];
27 for (i = 2; i <= n; ++i)
28 for (j = 2; j <= m; ++j)
29 a[i][j]=(1LL*x*a[i-1][j] + 1LL*y*a[i][j-1] +
30 1LL*z*a[i-1][j-1] + w) % u;
31 fin.close();
32 }
33
34 void Procesare()
35 {
36 int i, j;
37 for (i = 1; i <= n; ++i)
38 for (j = 1; j <= m; ++j)
39 {
40 mx[i][j] = max(mx[i][j-1], a[i][j]);
41 s[i][j] = s[i][j-1] + a[i][j];
42 }
43 }
44
45 /// returneaza cantitatea maxima care se poate culege
46 /// de pe linia k daca pot culege din copacii care
47 /// au cel mult cantitatea cant
48 long long Linia(int k, int cant)
49 {
50 if (cant < mx[k][1]) return 0;
51 if (cant >= mx[k][m]) return s[k][m];
CAPITOLUL 24. ONI 2016 431

52 int st, dr, mij, sol;


53 sol = 0;
54 st = 1; dr = m;
55 while (st <= dr)
56 {
57 mij = (st + dr) / 2;
58 if (mx[k][mij] > cant)
59 dr = mij - 1;
60 else
61 {
62 sol = mij;
63 st = mij + 1;
64 }
65 }
66 return s[k][sol];
67 }
68
69 void CautBin()
70 {
71 ofstream fout(outFile);
72 int st, dr, sol, mij, i;
73 long long suma;
74 sol = 0;
75 st = 1;
76 dr = 1000000000;
77 while (st <= dr)
78 {
79 mij = (st + dr) / 2;
80 for (i = 1; i <= n; ++i)
81 t[i] = Linia(i, mij);
82 sort(t + 1, t + n + 1);
83 suma = 0;
84 for (i = n; i > n - R; i--)
85 suma += t[i];
86 if (suma >= C)
87 {
88 sol = mij;
89 dr = mij - 1;
90 }
91 else
92 st = mij + 1;
93 }
94
95 fout << sol << "\n";
96 fout.close();
97 }
98
99 int main()
100 {
101 Citire();
102 Procesare();
103 CautBin();
104 return 0;
105 }

Listing 24.3.2: Livada100.cpp


1 #include <fstream>
2 #include <algorithm>
3
4 using namespace std;
5
6 ifstream fin("livada.in");
7 ofstream fout("livada.out");
8
9 const int MaxVal = 1000000000;
10 const int NMax = 100;
11 const int MMax = 25000;
12
13 int A[NMax+5][MMax+5],Max[NMax+5][MMax+5];
14 long long Sum[NMax+5][MMax+5];
15 int N,M,R,Sol;
16 long long C;
17
18 void Read()
CAPITOLUL 24. ONI 2016 432

19 {
20 long long x,y,z,w,u;
21
22 fin>>N>>M>>C>>R;
23 fin>>x>>y>>z>>w>>u;
24
25 for(int i = 1; i<=M; i++)
26 fin>>A[1][i];
27
28 for(int i = 2; i<=N; i++)
29 fin>>A[i][1];
30
31 for(int i = 2; i <= N; ++i)
32 for(int j = 2; j <= M; ++j)
33 A[i][j] = (x * A[i - 1][j] +
34 y * A[i][j - 1] +
35 z * A[i - 1][j - 1] +
36 w) % u;
37
38 }
39
40 void Precalculate()
41 {
42 for(int i = 1; i <= N; ++i)
43 for(int j = 1; j <= M; ++j)
44 {
45 Sum[i][j] = A[i][j] + Sum[i][j-1];
46 Max[i][j] = max(Max[i][j-1],A[i][j]);
47 }
48 }
49
50 long long Find(int i, int Value)
51 {
52 long long Crop = 0;
53
54 int Left = 1, Right = M;
55
56 while(Left <= Right)
57 {
58 int Mid = (Left+Right) / 2;
59 if(Max[i][Mid] <= Value)
60 {
61 Crop = Sum[i][Mid];
62 Left = Mid + 1;
63 }
64 else
65 Right = Mid - 1;
66 }
67 return Crop;
68 }
69
70 bool Check(int Value)
71 {
72 long long V[NMax],k = 0;
73
74 for(int i = 1; i <= N; i++)
75 V[++k] = Find(i,Value);
76
77 sort(V+1,V+k+1,greater<long long>());
78
79 long long Total = 0;
80
81 for(int i = 1; i<=R && i<=k; i++)
82 Total+=V[i];
83
84 return (Total >= C);
85 }
86
87 void Solve()
88 {
89 int Left = 1, Right = MaxVal;
90 while(Left <= Right)
91 {
92 int Mid = (Left + Right) / 2;
93
94 if(Check(Mid))
CAPITOLUL 24. ONI 2016 433

95 {
96 Sol = Mid;
97 Right = Mid - 1;
98 }
99 else
100 Left = Mid + 1;
101 }
102 }
103
104 void Print()
105 {
106 fout<<Sol<<"\n";
107 }
108
109 int main()
110 {
111 Read();
112 Precalculate();
113 Solve();
114 Print();
115 return 0;
116 }

24.3.3 *Rezolvare detaliat 

24.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 , ..., Yn˜n  cu n ˜ n elemente obµinute din toate produsele de câte dou  elemente din
³irul X (ecare 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  diferenµa dintre valorile aate pe poziµiile p1
³i p2 în ³irul ordonat descresc tor. Sandu a g sit rapid valorile d1 ³i d2 ³i, pentru a le verica, v 
roag  s  le determinaµi ³i voi.

Cerinµe
Dându-se ³irul X cu n elemente ³i valorile p1 ³i p2 , determinaµi valorile d1 ³i d2 .

Date de intrare
Fi³ierul de intrare dif2.in va conµine pe prima linie un num r natural C care poate  doar
1 sau 2. Dac  C 1, atunci pe linia a doua se va aa num rul natural n. Dac  C 2, atunci
pe linia a doua se vor aa numerele naturale n, p1 ³i p2 separate prin câte un spaµiu. în ambele
cazuri, pe urm toarele n linii se vor aa elementele ³irului X , câte un num r natural pe ecare
linie a ³ierului.

Date de ie³ire
În cazul C 1, ³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 ³ierul de ie³ire va conµine
pe prima linie un num r natural d2 reprezentând diferenµa dintre valorile aate pe poziµiile p1 ³i
p2 din ³irul Y , presupunând c  ar  ordonat descresc tor.

Restricµii ³i preciz ri
a 3 $ n $ 300 000
a 1 & p1 $ p2 & n2
a 1 & Xi $ 300 000, i 1..n
CAPITOLUL 24. ONI 2016 434

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:
dif2.in dif2.out Explicaµii
1 32 Atenµie, C 1, deci se rezolv  doar cerinµa 1!
4 Valoarea maxim  d1 va  32 ³i se obµine efectuând diferenµa
3 dintre 6*6 ³i 2*2.
5
2
6
2 8 Atenµie, C 2, deci se rezolv  doar cerinµa 2!
4 5 11 Se obµin în Y urm toarele 16 valori: 3*3, 3*5, 3*2, 3*6, 5*3,
3 5*5, 5*2, 5*6, 2*3, 2*5, 2*2, 2*6, 6*3, 6*5, 6*2, 6*6.
5 Valoarea d2 va  8, deoarece dac  vom considera ³irul Y ordonat
2 descresc tor (36, 30, 30, 25, 18, 18, 15, 15, 12, 12, 10, 10, 9, 6,
6 6, 4), atunci Y[5]-Y[11]=18-10=8
Timp maxim de executare/test: 2.0 secunde
Memorie: total 16 MB
Dimensiune maxim  a sursei: 15 KB

24.4.1 Indicaµii de rezolvare

Gheorghe Manolache, Colegiul Naµional de Informatic , Piatra Neamµ

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  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 aa pe ce poziµie va  un produs p, vom utiliza ³irul c precalculat ³i vom num r 
pentru ecare 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 pe acest vector Sumi.
prexe
Pentru a aa produsul aat pe o poziµie Z , c ut m binar produsul, iar pentru un produs P
xat trebuie sa vedem câte perechi (a, b) exista cu a ˜ b & P .
Pentru asta vom parcurge ecare valoare posibila, iar pentru o valoare A xata 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 .
Solutie O N log2 V :
Similar cu soluµia anterioara, doar ca in loc sa µinem un vector de frecventa, sortam vectorul
iniµial.
CAPITOLUL 24. ONI 2016 435

Cu Z si P xate (ca in soluµia anterioara), pentru a num r  perechile vom face:


Pentru o poziµie i din vector, trebuie sa g sim j maxim astfel încât v i ˜ v j  & P .
Odat  g sit, v i, v 1, ..., v i, j  toate vor  perechi posibile.
Acest j poate  c utat binar, deoarece vectorul este sortat.
Soluµie O(N logV):
Plecând de la soluµia anterioar , observ m c :
Suntem la pasul i ³i ³tim j pentru acest pas.
Când trecem la pasul i  1, j poate doar sa scad .
(Deoarece v i  1 ' v i)
A³adar în loc s  c ut m binar vom µine doi pointeri cu care vom parcurge vectorul.

24.4.2 Cod surs 

Listing 24.4.1: dif2_bicsi.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 #define int long long
6 const int MAXN = 300000 + 10;
7 int V[MAXN], n;
8
9 long long countSmall(long long x)
10 {
11 long long ans = 0;
12
13 for(int i = 1, j = n; i <= n; ++i)
14 {
15 while(1LL * V[i] * V[j] > x)
16 --j;
17 ans += j;
18 }
19
20 return ans;
21 }
22
23
24 long long kth(long long pos)
25 {
26 long long ans = 0;
27 for(long long i = (1LL << 40); i; i >>= 1LL)
28 {
29 int x = countSmall(ans + i);
30 if(x < pos)
31 ans += i;
32 }
33 return ans + 1;
34 }
35
36 int32_t main()
37 {
38 ifstream cin("dif2.in");
39 ofstream cout("dif2.out");
40
41 int t;
42 long long a, b;
43
44 cin >> t >> n;
45
46 if(t == 2) cin >> a >> b;
47
48 for(int i = 1; i <= n; ++i)
49 cin >> V[i];
50
51 sort(V + 1, V + n + 1);
52
53 if(t == 1)
54 cout << 1LL * V[n] * V[n] - 1LL * V[1] * V[1] << endl;
55 else
CAPITOLUL 24. ONI 2016 436

56 cout << kth(1LL * n * n - a + 1) - kth(1LL * n * n - b + 1) << endl;


57
58 return 0;
59 }

Listing 24.4.2: dif2_eric.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 typedef long long LL;
6 #define int LL
7
8 const int MAX_V = 300000 + 1;
9 const int MAX_N = 300000 + 1;
10
11 int v[MAX_N];
12 int f[MAX_V];
13 LL S[MAX_V];
14 int n;
15
16 LL p1, p2;
17
18 LL test1()
19 {
20 return 1LL * v[n] * v[n] - 1LL * v[1] * v[1];
21 }
22
23 LL before(LL prod)
24 {
25 LL ret = 0;
26 for(int i = min(1LL * v[n], prod); i >= 1; --i)
27 {
28 LL other = prod / i;
29
30 if(other <= v[n])
31 ret += 1LL * S[other] * f[i];
32 else
33 ret += 1LL * S[v[n]] * f[i];
34 }
35
36 return ret;
37 }
38
39 LL query(LL poz)
40 {
41 LL i, step;
42 LL mx = 1LL * v[n] * v[n];
43 for(step = 1; step < mx; step *= 2);
44
45 for(i = 0; step; step /= 2)
46 if(i + step <= mx && before(i + step) < poz)
47 i += step;
48
49 return i + 1;
50 }
51
52 int32_t main()
53 {
54 ifstream in("dif2.in");
55 int c;
56 in >> c;
57 assert(c == 1 || c == 2);
58
59 in >> n;
60 assert(3 < n && n < MAX_N);
61
62 if(c == 2)
63 in >> p1 >> p2;
64
65
66 for(int i = 1; i <= n; ++i)
67 {
68 in >> v[i];
CAPITOLUL 24. ONI 2016 437

69 assert(1 <= v[i] && v[i] < MAX_V);


70 f[v[i]]++;
71 }
72
73 for(int i = 1; i < MAX_V; ++i)
74 S[i] = S[i - 1] + f[i];
75
76 sort(v + 1, v + n + 1);
77
78 ofstream out("dif2.out");
79
80 if(c == 1)
81 {
82 out << test1() << "\n";
83 return 0;
84 }
85 else
86 {
87 p2 = 1LL * n * n - p2 + 1;
88 p1 = 1LL * n * n - p1 + 1;
89 out << query(p1) - query(p2) << "\n";
90 }
91
92 return 0;
93 }

Listing 24.4.3: dif2_N_log2.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 const int MAXN = 500000;
6 int V[MAXN], n;
7
8 long long countSmall(long long x)
9 {
10 long long ans = 0;
11
12 for(int i = 1; i <= n; ++i)
13 ans += upper_bound(V + 1,
14 V + n + 1,
15 min((long long)V[n], x / V[i])) - V - 1;
16
17 return ans;
18 }
19
20 long long kth(long long pos)
21 {
22 long long ans = 0;
23 for(long long i = (1LL << 40); i; i >>= 1LL)
24 {
25 int x = countSmall(ans + i);
26 if(x < pos)
27 ans += i;
28 }
29
30 return ans + 1;
31 }
32
33 template<typename T>
34
35 void Read(T &a)
36 {
37 char c;
38 for(c = getchar(); !isdigit(c); c = getchar());
39
40 for(a = 0; isdigit(c); c = getchar())
41 a = a * 10 + c - ’0’;
42 }
43
44 int main()
45 {
46 freopen("dif2.in", "r", stdin);
47 freopen("dif2.out", "w", stdout);
CAPITOLUL 24. ONI 2016 438

48
49 int t;
50 long long a, b;
51
52 Read(t);
53 Read(n);
54
55 if(t == 2)
56 {
57 Read(a);
58 Read(b);
59 }
60
61 for(int i = 1; i <= n; ++i)
62 Read(V[i]);
63
64 sort(V + 1, V + n + 1);
65
66 if(t == 1)
67 cout << 1LL * V[n] * V[n] - 1LL * V[1] * V[1] << endl;
68 else
69 cout << kth(1LL * n * n - a + 1) - kth(1LL * n * n - b + 1) << endl;
70
71 return 0;
72 }

24.4.3 *Rezolvare detaliat 

24.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 ec rui
led produce modicarea 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 modic  st rile ledurilor i  1, i ³i i  1. Dac  se atinge ledul 1, atunci
se modic  st rile ledurilor 1 ³i 2, iar dac  se atinge ledul N , atunci se modic  st rile ledurilor
N  1 ³i N . Vreau s  modic 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µ.

Restricµii ³i preciz ri
a 1 & N & 100 000
a Se garanteaz  c  pentru toate testele exist  soluµie.
a Pentru teste valorând 30 de puncte, N va  cel mult 20
CAPITOLUL 24. ONI 2016 439

Exemple:
leduri.in leduri.out Explicaµii
4 2 O soluµie posibil  este:
1010 Se apas  mai întâi al doilea led: 1 0 1 0 0100
0111 Se apas  ultimul led: 0 1 0 0 0111
Timp maxim de executare/test: 0.5 secunde
Memorie: total 64 MB
Dimensiune maxim  a sursei: 15 KB

24.5.1 Indicaµii de rezolvare

prof. Ionel-Vasile Piµ-Rada, Colegiul Naµional Traian, Drobeta Turnu Severin


student Lucian Bicsi, Universitatea Bucure³ti

În primul rând, se observa becurile pot  atinse in orice ordine, f r  a produce schimb ri de
efect în nal. O a doua observaµie util  în rezolvarea problemei este c , pentru a transforma
conguraµia iniµial  în cea nala, orice bec ajunge sa e 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 conguraµ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 nal, 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 nal , unde singura variant  posibil 
de a rezolva aceast  problem  este s  atingem becul 2 (deoarece becul 1 nu mai poate  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 modica înapoi starea becului 1)
Inductiv, se demonstreaz  c  atingerile tuturor celor n becuri sunt unic determinate.
În nal, 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 conguraµie care difer  de cea nal  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 sucient ³i necesar s  aducem ambele con-
guraµ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 ecare 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 24. ONI 2016 440

Analiz m în funcµie de resturile lui n la împ rµirea cu 3:


0) n 3k : Putem din starea (S1) s  atingem becurile 1, 2, 4, 5, 7, 8, ..., 3k-2, 3k-1 ³i ajungem
în (S0)
1) n 3k  1: Putem din starea (S1) s  atingem becurile 2, 3, 5, 6, ..., 3k - 1, 3k ³i ajungem
în (S0)
2) n 3k  2: Acesta este cazul interesant. în acest caz demonstr m imediat c  din (S0) nu
se poate ajunge în (S1) (sau invers)
Pentru cazurile în care n = 3k sau n = 3k + 1 rezult  imediat c  se poate ajunge din orice
stare în oricare alta (deoarece din orice stare se poate ajunge în (S0) sau (S1) ³i din (S0) se poate
ajunge în (S1) ).
n
Cum dintr-o stare x se poate ajunge în toate cele 2 st ri diferite, iar num rul de operaµii
n
posibile este 2 , rezult  imediat c  modul prin care se ajunge din x în y este unic, pentru orice y
(s  presupunem c  exist  dou  submulµimi care îl transform  pe x în y ; conform principiului lui
k k
Dirichlet, exist  o stare care nu poate  atins , pentru c  exist  2 st ri diferite ³i 2 transform ri
diferite, iar dou  dintre ele duc în aceea³i stare => contradicµie cu ce am demonstrat mai sus).
În concluzie, pentru n 3k sau n 3k  1, soluµia este unic  ³i este obµinut  prin combinarea
(prin sau exclusiv) a celor dou  ³iruri rezultate din algoritmul descris.
Pentru n 3k  2, s  consider m starea (S0) ³i s  presupunem prin absurd c  putem ajunge în
(S1). Atunci, în mod evident, putem ajunge din (S0) în orice alt  stare. Folosind un raµionament
asem n tor cu cel din demonstraµia precedent , rezult  c  exist  un singur mod de a transforma
(S0) într-o stare x. Ori, exist  dou  moduri de a ajunge de la (S0) la (S0): una este setul vid,
iar alta este setul de operaµii 1, 2, 4, 5, 7, 8, ..., 3k + 1, 3k + 2 (se demonstreaz  prin inducµie).
Deci presupunerea f cut  este fals . Mai mult, se demonstreaz  c  acest set de operaµii nevid este
unicul mod de a ajunge din (S0) în (S0).
Not : Acela³i set de operaµii transform  ³irul (S1) în (S1).
De aici se observ  c  pentru a ajunge dintr-o stare iniµial  (A) într-o stare nal  (B) este
sucient ³i necesar ca:
1) Din st rile (A), (B) folosind algoritmul din demonstraµia lemei s  se ajung  la aceea³i stare
2) Setul de operaµii s  e format din combinarea celor doua seturi de operaµii din algoritmul
respectiv, sau prin combinarea celor dou  cu "³irul special" 1, 2, 4, 5, ..., 3k + 1, 3k + 2
În concluzie, soluµia se poate obµine ³i prin aducerea celor doua ³iruri la o form  intermediar 
(tehnica cunoscut  ³i ca "meet in the middle"), combinarea celor dou  seturi de operaµii ³i,
eventual, tratarea cazului special n 3k  2 (singurul caz în care exist  mai multe soluµii ale
problemei).
Complexitate nal : O n

24.5.2 Cod surs 

Listing 24.5.1: leduri_bicsi.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 const int MAXN = 1000000;
6 int A0[MAXN], A1[MAXN], B[MAXN];
7 int n;
8
9 int transform(int A[])
10 {
11 int ops = 0;
12
13 for(int i = 1; i < n; ++i)
14 {
15 if(A[i] != B[i])
16 {
17 A[i] ^= 1;
18 A[i + 1] ^= 1;
19 A[i + 2] ^= 1;
20 ops += 1;
21 }
22 }
23 return ops;
24 }
CAPITOLUL 24. ONI 2016 441

25
26 int main()
27 {
28 freopen("leduri.in", "r", stdin);
29 freopen("leduri.out", "w", stdout);
30
31 cin >> n;
32
33 for(int i = 1; i <= n; ++i)
34 {
35 cin >> A0[i];
36 A1[i] = A0[i];
37 }
38
39 for(int i = 1; i <= n; ++i)
40 cin >> B[i];
41
42 A1[1] ^= 1;
43 A1[2] ^= 1;
44
45 int a = transform(A0);
46 int b = transform(A1);
47
48 int ans = 1000000;
49 if(A0[n] == B[n]) ans = min(ans, a);
50 if(A1[n] == B[n]) ans = min(ans, b + 1);
51
52 if(ans == 1000000)
53 ans = -1;
54
55 cout << ans << endl;
56
57 return 0;
58 }

Listing 24.5.2: leduri_eric.cpp


1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 const int MAX_N = 1e5 + 10;
6
7 int n;
8 bool v[MAX_N];
9 bool init[MAX_N];
10 bool fin[MAX_N];
11
12 void apply(int x)
13 {
14 v[x] ^= 1;
15 if(x - 1 >= 1)
16 v[x - 1] ^= 1;
17 if(x + 1 <= n)
18 v[x + 1] ^= 1;
19 }
20
21 const int INF = (1 << 29) - 1;
22
23 int go()
24 {
25 int cnt = 0;
26
27 for(int i = 2; i <= n; ++i)
28 {
29 if(v[i - 1] != fin[i - 1])
30 {
31 cnt++;
32 apply(i);
33 }
34 }
35
36 for(int i = 1; i <= n; ++i)
37 if(v[i] != fin[i])
38 return INF;
CAPITOLUL 24. ONI 2016 442

39
40 return cnt;
41 }
42
43 ofstream out("leduri.out");
44
45 int main()
46 {
47 ifstream in("leduri.in");
48 in >> n;
49 assert(1 <= n && n <= 100000);
50
51 for(int i = 1; i <= n; ++i)
52 {
53 in >> v[i];
54 init[i] = v[i];
55 assert(v[i] == 0 || v[i] == 1);
56 }
57
58 for(int i = 1; i <= n; ++i)
59 {
60 in >> fin[i];
61 assert(fin[i] == 0 || fin[i] == 1);
62 }
63
64 int s1 = go();
65
66 for(int i = 1; i <= n; ++i)
67 v[i] = init[i];
68
69 v[1] ^= 1;
70 v[2] ^= 1;
71
72 int s2 = go() + 1;
73
74 if(s1 >= INF && s2 >= INF)
75 {
76 out << "-1\n";
77 return 0;
78 }
79 else
80 out << min(s1, s2) << "\n";
81
82 return 0;
83 }

24.5.3 *Rezolvare detaliat 

24.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 ind:
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
S  se determine câte submatrice nevide omogene exist .
CAPITOLUL 24. ONI 2016 443

Date de intrare
Fi³ierul omogene.in conµine pe prima linie numerele naturale L ³i C . Pe urm toarele L linii
se a  câte C numere naturale separate prin spaµii reprezentând câte o linie din matrice.

Date de ie³ire
Fi³ierul omogene.out va conµine pe prima linie un singur num r natural reprezentând num rul
submatricelor nevide omogene.

Restricµii ³i preciz ri
a 2 & L & C & 5 000
a 4 & L ˜ C & 65 536
a Atenµie, o submatrice este format  dintr-o secvenµ  continu  de linii ³i coloane, deci, de
exemplu, dac  se aleg dintr-o matrice liniile 1, 2 ³i 5, atunci acestea nu formeaz  o submatrice.
9
a Num rul submatricelor omogene va  mai mic decât 2 ˜ 10
a întreaga matrice poate  submatrice omogen 

Exemple:
omogene.in omogene.out Explicaµii
2 4 6 Cele ³ase submatrice au fost menµionate în enunµ.
0 1 20
1 2 01
3 3 3
0 1 2
0 2 2
0 1 1
Timp maxim de executare/test: 3.0 secunde
Memorie: total 64 MB
Dimensiune maxim  a sursei: 15 KB

24.6.1 Indicaµii de rezolvare

prof. Dan Pracsiu, Liceul Teoretic Emil Racoviµ  Vaslui

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 aa num rul perechilor de triplete egale, se poate sorta ³irul tripletelor. în ³irul
sortat, tripletele identice sunt al turate. Dac  identic 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 24. ONI 2016 444

Revenind la problema bidimensional : Din restricµii, L & C ³i L ˜ C &65 536. De unde rezult 

L & sqrt 65536 256.
Deci num rul de linii din matrice nu poate  mai mare de 256.
Construim dou  matrice de sume parµiale:
z[i][j] = num rul de valori de 0 aate pe coloana j, începând de la poziµia (i,j) în sus.
u[i][j] = num rul de valori de 1 aate 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  a 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 aate 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
aate 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  deci O L ˜ L ˜ C ˜ logC 

24.6.2 Cod surs 

Listing 24.6.1: omogene_eric.cpp


1 #include <fstream>
2 #include <iostream>
3 #include <vector>
4 #include <algorithm>
5
6 using namespace std;
7
8 typedef long long LL;
9
10 const int MAX_N = 320 + 10;
11 const int MAX_M = 10000 + 10;
12
13 int v[MAX_N][MAX_M];
14 int n, m;
15
16 int s[MAX_N][MAX_M][3];
17
18 LL ans = 0;
19
20 int vect[MAX_M][3];
21 //vect[i][j] -> partial sum of j over the i prefix
22
23 struct state
24 {
25 int v[3];
26 //v[0] -> cnt[0] - cnt[1]
27 //v[1] -> cnt[0] - cnt[2]
28 //v[2] -> cnt[1] - cnt[2]
29
30 bool operator==(const state &other)
31 {
32 for(int k = 0; k < 3; ++k)
33 if(v[k] != other.v[k])
34 return 0;
35 return 1;
36 }
37 };
38
39 state good = {0, 0, 0};
40
41 bool comp(const state &a, const state &b)
42 {
43 int k = 0;
44 while(k < 2 && a.v[k] == b.v[k])
45 k++;
46 return a.v[k] < b.v[k];
47 }
48
49 state sts[MAX_M];
50
CAPITOLUL 24. ONI 2016 445

51 state getState(int i)
52 {
53 state ret;
54 ret.v[0] = vect[i][0] - vect[i][1];
55 ret.v[1] = vect[i][0] - vect[i][2];
56 ret.v[2] = vect[i][1] - vect[i][2];
57 return ret;
58 }
59
60 LL cnt(int l1, int l2)
61 {
62 for(int i = 1; i <= m; ++i)
63 {
64 for(int k = 0; k < 3; ++k)
65 vect[i][k] = vect[i - 1][k] + s[l2][i][k] - s[l1 - 1][i][k] -
66 s[l2][i - 1][k] + s[l1 - 1][i - 1][k];
67 }
68
69 for(int i = 1; i <= m; ++i)
70 sts[i] = getState(i);
71
72 sort(sts + 1, sts + m + 1, comp);
73
74 LL ret = 0;
75
76 for(int i = 1, j; i <= m; i = j)
77 {
78 j = i + 1;
79 while(j <= m && sts[i] == sts[j])
80 j++;
81
82 LL diff = j - i;
83 ret += diff * (diff - 1) / 2;
84 }
85
86 for(int i = 1; i <= m; ++i)
87 ret += sts[i] == good;
88
89 return ret;
90 }
91
92 int main()
93 {
94 ifstream in("omogene.in");
95 ofstream out("omogene.out");
96
97 in >> n >> m;
98 for(int i = 1; i <= n; ++i)
99 for(int j = 1; j <= m; ++j)
100 in >> v[i][j];
101
102
103 for(int i = 1; i <= n; ++i)
104 for(int j = 1; j <= m; ++j)
105 {
106 for(int k = 0; k < 3; ++k)
107 {
108 s[i][j][k] += s[i - 1][j][k] + s[i][j - 1][k]
109 - s[i - 1][j - 1][k];
110 }
111 s[i][j][v[i][j]]++;
112 }
113
114 for(int i = 1; i <= n; ++i)
115 for(int j = i; j <= n; ++j)
116 ans += cnt(i, j);
117
118 out << ans << "\n";
119 return 0;
120 }

Listing 24.6.2: omogene_n_4.cpp


1 #include <bits/stdc++.h>
2
CAPITOLUL 24. ONI 2016 446

3 #define MAXC 10003


4 #define MAXL 320
5 #define inFile "omogene.in"
6 #define outFile "omogene.out"
7
8 using namespace std;
9
10 int z[MAXL][MAXC];
11 int u[MAXL][MAXC];
12 int d[MAXL][MAXC];
13
14 int L, C;
15 long long sol;
16
17 void NrSubmat()
18 {
19 int i, j, i2, j2, zero, unu, doi;
20 for (i = 1; i <= L; ++i)
21 for (j = 1; j <= C; ++j)
22 for (i2 = i; i2 <= L; ++i2)
23 for (j2 = j; j2 <= C; ++j2)
24 {
25 zero=z[i2][j2]-z[i-1][j2]-z[i2][j-1]+z[i-1][j-1];
26 unu=u[i2][j2]-u[i-1][j2]-u[i2][j-1]+u[i-1][j-1];
27 doi=d[i2][j2]-d[i-1][j2]-d[i2][j-1]+d[i-1][j-1];
28 if (zero == unu && zero == doi)
29 sol++;
30 }
31 }
32
33 void Citire()
34 {
35 int i, j, x;
36 ifstream fin(inFile);
37 fin >> L >> C;
38 for (i = 1; i <= L; ++i)
39 for (j = 1; j <= C; ++j)
40 {
41 fin >> x;
42 z[i][j] = z[i-1][j]+z[i][j-1]-z[i-1][j-1]+(x == 0);
43 u[i][j] = u[i-1][j]+u[i][j-1]-u[i-1][j-1]+(x == 1);
44 d[i][j] = d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(x == 2);
45 }
46 fin.close();
47 }
48
49 void Afisare()
50 {
51 ofstream fout(outFile);
52 fout << sol << "\n";
53 fout.close();
54 }
55
56 int main()
57 {
58 Citire();
59 NrSubmat();
60 Afisare();
61 return 0;
62 }

Listing 24.6.3: omogeneMG.cpp


1 #include<fstream>
2 #include<algorithm>
3
4 using namespace std;
5
6 ifstream f("omogene.in");
7 ofstream g("omogene.out");
8
9 struct nod
10 {
11 int d01,d12,d20;
12 };
CAPITOLUL 24. ONI 2016 447

13
14 inline bool test(const nod &t1,const nod &t2)
15 {
16 return t1.d01<t2.d01 ||
17 (t1.d01==t2.d01 && t1.d12<t2.d12) ||
18 (t1.d01==t2.d01 && t1.d12==t2.d12 && t1.d20<t2.d20);
19 }
20
21 nod v[10001];
22 int a[318][10001];
23 int z[318][10001],u[318][10001];
24 int t0,t1,t2,st0,st1,st2;
25 long long sol;
26 int n,m;
27
28 void citire()
29 {
30 f>>n>>m;
31 for(int i=1;i<=n;++i)
32 for(int j=1;j<=m;++j)
33 f>>a[i][j];
34 f.close();
35 }
36
37 void prelucrare()
38 {
39 for(int i=1;i<=n;++i)
40 for(int j=1;j<=m;++j)
41 {
42 z[i][j]=z[i-1][j]+(a[i][j]==0);
43 u[i][j]=u[i-1][j]+(a[i][j]==1);
44 }
45
46 for(int k=1;k<=n;++k)
47 for(int p=k;p<=n;++p)
48 {
49 int d=p-k+1;
50 st0=st1=st2=0;
51 v[0].d01=v[0].d12=v[0].d20=0;
52 for(int j=1;j<=m;++j)
53 {
54 t0=z[p][j]-z[k-1][j];st0+=t0;
55 t1=u[p][j]-u[k-1][j];st1+=t1;
56 t2=d-t0-t1;st2+=t2;
57 v[j].d01=st0-st1;
58 v[j].d12=st1-st2;
59 v[j].d20=st2-st0;
60 }
61
62 sort(v,v+m+1,test);
63
64 int x=1;
65 for(int i=1;i<=m;++i)
66 if(v[i-1].d01==v[i].d01 &&
67 v[i-1].d12==v[i].d12 &&
68 v[i-1].d20==v[i].d20)
69 x++;
70 else
71 {
72 sol=sol+1ll*x*(x-1)/2;
73 x=1;
74 }
75
76 sol=sol+1ll*x*(x-1)/2;
77 }
78 }
79
80 int main()
81 {
82 citire();
83 prelucrare();
84 g<<sol<<’\n’;
85 return 0;
86 }
CAPITOLUL 24. ONI 2016 448

24.6.3 *Rezolvare detaliat 


Capitolul 25

ONI 2015

25.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 ecare 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
Cunoscând latura N a cubului, s  se umple cubul cu tubul luminos
(becurile ind legate cresc tor), apoi s  se determine:
1. Coordonatele x, y, z  ale becului cu num rul V . (x-linia, y -coloana, z -în lµimea)
2. Num rul de becuri luminoase situate pe ecare faµ  a cubului.

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 ³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 ³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 .

449
CAPITOLUL 25. ONI 2015 450

a Dac  valoarea lui p este 2, se va rezolva numai cerinµa 2. În acest caz, ³ierul de ie³ire
cub.out va conµine 4 linii. Pe ecare linie i, se va scrie câte un num r natural , reprezentând
num rul de becuri inscripµionate cu numere prime de pe faµa i.

Restricµii ³i preciz ri
a 1 & N & 200
1&V &N
3
a
a Pentru rezolvarea corect  a primei cerinµe se acord  20 de puncte, iar pentru cerinµa a doua
se acord  80 de puncte.
a Pentru 20% dintre teste: 1 & N & 20
a Pentru 30% dintre teste: 21 & N & 100
a Pentru 50% dintre teste: 101 & N & 200

Exemple:
cub.in cub.out Explicaµ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

25.1.1 Indicaµii de rezolvare

prof. Claudiu-Cristian Gorea - C. N. Al. Papiu Ilarian Târgu-Mure³

Cubul e format din N matrici p tratice de N*N elemente.


Primul nivel se completeaz  circular spre interior în sensul acelor de ceasornic:
a plecând din (1,1) : linia 1, coloana N, linia N, coloana 1.
a plecând din (2,2) : linia 2 (necompletat ), coloana N-1, linia N-1, coloana 2. ³.a.m.d.

Dac  N este un num r par, se vor folosi la ecare plecare 4 direcµii matricea ind completat 
astfel.
Dac  N este impar elementul din centru va  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 ecare 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 25. ONI 2015 451

- linia 1 pentru faµa 1 (spate)


- coloana N pentru faµa 2 (dreapta)
- linia N pentru faµa 3 (frontal)
- coloana1 pentru faµa 4 (stânga),

la care vom aduna pentru ecare nivel nou 2*N*N.


Pentru testarea numerelor prime se poate folosi ciurul lui Eratostene.
Complexitate O n ˜ n - 100 puncte

25.1.2 Cod surs 

Listing 25.1.1: cubul_cp1.cpp


1 // prof. Carmen Popescu
2 // colegiul National "Gheorghe Lazar" Sibiu
3
4 #include <fstream>
5 #include <bitset>
6
7 using namespace std;
8
9 ifstream f("cub.in");
10 ofstream g("cub.out");
11
12 int m=2;
13 bitset<8000005> pr;
14
15 void prim(int n)
16 {
17 int i=1,j;
18 for (i=1;i<=n;i++)
19 pr[i]=true;
20 pr[1]=false;
21 for (i=2;i<=n/2;i++)
22 if (pr[i])
23 {
24 j=i+i;
25 while (j<=n)
26 {
27 pr[j]=false;
28 j+=i;
29 }
30 }
31 }
32
33 int main()
34 {
35 int v,n,n2,k,z,p,i,j,f1=0,f2=0,f3=0,f4=0;
36 m=2; pr[1]=2; pr[2]=3;
37 f>>v;
38 f>>n>>k;
39 n2=n*n;
40 prim(n2*n);
41 if (v==1)
42 {
43 z=1;
44 v=0;
45 while (k-v>n2)
46 {
47 z++;
48 v=v+n2;
49 }
50
51 if (z%2==1)
52 {
53 for (p=1;;p++)
54 {
55 for (j=p;j<=n-p+1;j++)
56 {
57 v++;
58 if (v==k)
59 {
CAPITOLUL 25. ONI 2015 452

60 g<<p<<" "<<j<<" "<<z<<"\n";


61 g.close();
62 return 0;
63 }
64 }
65 for (i=p+1;i<=n-p+1;i++)
66 {
67 v++;
68 if (v==k)
69 {
70 g<<i<<" "<<n-p+1<<" "<<z<<"\n";
71 g.close();
72 return 0;
73 }
74 }
75 for (j=n-p;j>=p;j--)
76 {
77 v++;
78 if (v==k)
79 {
80 g<<n-p+1<<" "<<j<<" "<<z<<"\n";
81 g.close();
82 return 0;
83 }
84 }
85 for (i=n-p;i>=p+1;i--)
86 {
87 v++;
88 if (v==k)
89 {
90 g<<i<<" "<<p<<" "<<z<<"\n";
91 g.close();
92 return 0;
93 }
94 }
95 }
96 }
97 else
98 {
99 v=v+n2+1;
100 for (p=1;;p++)
101 {
102 for (j=p;j<=n-p+1;j++)
103 {
104 v--;
105 if (v==k)
106 {
107 g<<p<<" "<<j<<" "<<z<<"\n";
108 g.close();
109 return 0;
110 }
111 }
112 for (i=p+1;i<=n-p+1;i++)
113 {
114 v--;
115 if (v==k)
116 {
117 g<<i<<" "<<n-p+1<<" "<<z<<"\n";
118 g.close();
119 return 0;
120 }
121 }
122 for (j=n-p;j>=p;j--)
123 {
124 v--;
125 if (v==k)
126 {
127 g<<n-p+1<<" "<<j<<" "<<z<<"\n";
128 g.close();
129 return 0;
130 }
131 }
132 for (i=n-p;i>=p+1;i--)
133 {
134 v--;
135 if (v==k)
CAPITOLUL 25. ONI 2015 453

136 {
137 g<<i<<" "<<p<<" "<<z<<"\n";
138 g.close();
139 return 0;
140 }
141 }
142 }
143
144 }
145 }
146 else
147 {
148 for (z=1;z<=n;z++)
149 if (z%2==1)
150 {
151 p=1; v=(z-1)*n2;
152
153 // coltul st-sus
154 v++;
155 if (pr[v]) { f1++; f4++; }
156
157 for (j=p+1; j<=n-p; j++)
158 {
159 v++;
160 if (pr[v]) f1++;
161 }
162
163 // coltul dreapta sus
164 v++;
165 if (pr[v]) { f1++; f2++; }
166
167 for (i=p+1; i<=n-p; i++)
168 {
169 v++;
170 if (pr[v]) f2++;
171 }
172
173 // coltul dreapta jos
174 v++;
175 if (pr[v]) { f2++; f3++; }
176
177 for (j=n-p; j>=p+1; j--)
178 {
179 v++;
180 if (pr[v]) f3++;
181 }
182
183 // coltul stanga-jos
184 v++;
185 if (pr[v]) { f3++; f4++; }
186
187 for (i=n-p; i>=p+1; i--)
188 {
189 v++;
190 if (pr[v]) f4++;
191 }
192
193 v=v+n2-(4*n-4);
194 }
195 else
196 {
197 p=1;
198 v=v+n2-(4*n-4);
199 for (i=p+1; i<=n-p; i++)
200 {
201 v++;
202 if (pr[v]) f4++;
203 }
204
205 // coltul stanga jos
206 v++;
207 if (pr[v]) { f4++; f3++; }
208
209 for (j=p+1; j<=n-p; j++)
210 {
211 v++;
CAPITOLUL 25. ONI 2015 454

212 if (pr[v]) f3++;


213 }
214
215 // coltul dreapta jos
216 v++;
217 if (pr[v]) { f3++; f2++; }
218
219 for (i=n-p; i>=p+1; i--)
220 {
221 v++;
222 if (pr[v]) f2++;
223 }
224
225 // coltul dreapta sus
226 v++;
227 if (pr[v]) {f2++; f1++; }
228
229 for (j=n-p; j>=p+1; j--)
230 {
231 v++;
232 if (pr[v]) f1++;
233 }
234
235 // coltul stanga sus
236 v++;
237 if (pr[v]) { f1++; f4++; }
238 }
239
240 g<<f1<<"\n"<<f2<<"\n"<<f3<<"\n"<<f4<<"\n";
241 g.close();
242
243 }
244 return 0;
245 }

Listing 25.1.2: cubul_cp2.cpp


1 // prof. Carmen Popescu
2 // colegiul National "Gheorghe Lazar" Sibiu
3
4 #include <fstream>
5
6 using namespace std;
7
8 ifstream f("cub.in");
9 ofstream g("cub.out");
10
11 int pr[1000000],m=2;
12
13 int prim(int x)
14 {
15 int i=1;
16 if (x==1) return 0;
17 if (x==2 || x==3)
18 return 1;
19
20 while (pr[i]<=x/2)
21 {
22 if (x%pr[i]==0)
23 return 0;
24 i++;
25 }
26
27 m++;
28 pr[m]=x;
29 return 1;
30 }
31
32 int main()
33 {
34 int v,n,n2,k,x,y,z,p,i,j,f1=0,f2=0,f3=0,f4=0;
35 m=2;
36 pr[1]=2;
37 pr[2]=3;
38 f>>v;
CAPITOLUL 25. ONI 2015 455

39 f>>n>>k;
40 n2=n*n;
41 if (v==1)
42 {
43 z=1;
44 v=0;
45 while (k-v>n2)
46 {
47 z++;
48 v=v+n2;
49 }
50 if (z%2==1)
51 {
52 for (p=1;;p++)
53 {
54 for (j=p;j<=n-p+1;j++)
55 {
56 v++;
57 if (v==k)
58 {
59 g<<p<<" "<<j<<" "<<z<<"\n";
60 g.close();
61 return 0;
62 }
63 }
64 for (i=p+1;i<=n-p+1;i++)
65 {
66 v++;
67 if (v==k)
68 {
69 g<<i<<" "<<n-p+1<<" "<<z<<"\n";
70 g.close();
71 return 0;
72 }
73 }
74 for (j=n-p;j>=p;j--)
75 {
76 v++;
77 if (v==k)
78 {
79 g<<n-p+1<<" "<<j<<" "<<z<<"\n";
80 g.close();
81 return 0;
82 }
83 }
84 for (i=n-p;i>=p+1;i--)
85 {
86 v++;
87 if (v==k)
88 {
89 g<<i<<" "<<p<<" "<<z<<"\n";
90 g.close();
91 return 0;
92 }
93 }
94 }
95 }
96 else
97 {
98 v=v+n2+1;
99 for (p=1;;p++)
100 {
101 for (j=p;j<=n-p+1;j++)
102 {
103 v--;
104 if (v==k)
105 {
106 g<<p<<" "<<j<<" "<<z<<"\n";
107 g.close();
108 return 0;
109 }
110 }
111 for (i=p+1;i<=n-p+1;i++)
112 {
113 v--;
114 if (v==k)
CAPITOLUL 25. ONI 2015 456

115 {
116 g<<i<<" "<<n-p+1<<" "<<z<<"\n";
117 g.close();
118 return 0;
119 }
120 }
121 for (j=n-p;j>=p;j--)
122 {
123 v--;
124 if (v==k)
125 {
126 g<<n-p+1<<" "<<j<<" "<<z<<"\n";
127 g.close();
128 return 0;
129 }
130 }
131 for (i=n-p;i>=p+1;i--)
132 {
133 v--;
134 if (v==k)
135 {
136 g<<i<<" "<<p<<" "<<z<<"\n";
137 g.close();
138 return 0;
139 }
140 }
141 }
142
143 }
144 }
145 else
146 {
147 for (z=1;z<=n;z++)
148 if (z%2==1)
149 {
150 p=1; v=(z-1)*n2;
151
152 // coltul st-sus
153 v++;
154 k=prim(v);
155 if (k) { f1++; f4++; }
156
157 for (j=p+1; j<=n-p; j++)
158 {
159 v++;
160 k=prim(v);
161 if (k) f1++;
162 }
163
164 // coltul dreapta sus
165 v++;
166 k=prim(v);
167 if (k) { f1++; f2++; }
168
169 for (i=p+1; i<=n-p; i++)
170 {
171 v++;
172 k=prim(v);
173 if (k) f2++;
174 }
175
176 // coltul dreapta jos
177 v++;
178 k=prim(v);
179 if (k) { f2++; f3++; }
180
181 for (j=n-p; j>=p+1; j--)
182 {
183 v++;
184 k=prim(v);
185 if (k) f3++;
186 }
187
188 // coltul stanga-jos
189 v++;
190 k=prim(v);
CAPITOLUL 25. ONI 2015 457

191 if (k) { f3++; f4++; }


192
193 for (i=n-p; i>=p+1; i--)
194 {
195 v++;
196 k=prim(v);
197 if (k) f4++;
198 }
199
200 for (i=1; i<=n2-(4*n-4); i++)
201 {
202 v++;
203 prim(v);
204 }
205 }
206 else
207 {
208 p=1;
209 for (i=1; i<=n2-(4*n-4); i++)
210 {
211 v++;
212 prim(v);
213 }
214
215
216 for (i=p+1; i<=n-p; i++)
217 {
218 v++;
219 k=prim(v);
220 if (k) f4++;
221 }
222
223 // coltul stanga jos
224 v++;
225 k=prim(v);
226 if (k) { f4++; f3++; }
227
228 for (j=p+1; j<=n-p; j++)
229 {
230 v++;
231 k=prim(v);
232 if (k) f3++;
233 }
234
235 // coltul dreapta jos
236 v++;
237 k=prim(v);
238 if (k) { f3++; f2++; }
239
240 for (i=n-p; i>=p+1; i--)
241 {
242 v++;
243 k=prim(v);
244 if (k) f2++;
245 }
246
247 // coltul dreapta sus
248 v++;
249 k=prim(v);
250 if (k) {f2++; f1++; }
251
252 for (j=n-p; j>=p+1; j--)
253 {
254 v++;
255 k=prim(v);
256 if (k) f1++;
257 }
258
259 // coltul stanga sus
260 v++;
261 k=prim(v);
262 if (k) { f1++; f4++; }
263 }
264 g<<f1<<"\n"<<f2<<"\n"<<f3<<"\n"<<f4<<"\n";
265 g.close();
266
CAPITOLUL 25. ONI 2015 458

267 }
268 return 0;
269 }

Listing 25.1.3: cubul_gcc_2matrici.cpp


1 ///prof. Gorea Claudiu-Cristian
2 ///C.N. Al. Papiu Ilarian Tg-Mures
3 #include <fstream>
4
5 using namespace std;
6
7 ifstream fin ("cub.in");
8 ofstream fout("cub.out");
9
10 int a[201][201],b[201][201];
11 int p,n,v,i,j,x,k,sum,f1,f2,f3,f4,nr;
12
13 int prim(int m)
14 {
15 int d;
16 if (m<2 ||(m>2 && m%2==0)) return 0;
17 for(d=2;d*d<=m;d++)
18 if(m%d==0) return 0;
19 return 1;
20 }
21
22 void afisare(int mat[201][201])
23 {
24 int l,c;
25 for(l=1;l<=n;l++)
26 {
27 for(c=1;c<=n;c++)
28 fout<<mat[l][c]<<" ";
29 fout<<endl;
30 }
31 fout<<"---------------------\n";
32 }
33
34 int main()
35 {
36 fin>>p;
37 fin>>n>>v;
38
39 ///nivel 1 - matrice A
40 x=1;
41 k=0;
42 while(x<=n/2)
43 {
44 ///spre stanga - linia x
45 for(j=x;j<=n-x;j++)
46 a[x][j]=++k;
47
48 ///spre jos - coloana n-x+1
49 for(i=x;i<=n-x;i++)
50 a[i][n-x+1]=++k;
51
52 ///spre stanga - linia n-x+1
53 for(j=n-x+1;j>x;j--)
54 a[n-x+1][j]=++k;
55
56 ///spre sus - coloana x
57 for(i=n-x+1;i>x;i--)
58 a[i][x]=++k;
59 x++;
60 }
61
62 if (n%2==1) a[n/2+1][n/2+1]=++k;
63 //afisare(a);
64
65 ///nivel 2 - matrice B
66 sum=2*n*n+1;
67 for(i=1;i<=n;i++)
68 for(j=1;j<=n;j++)
69 b[i][j]=sum-a[i][j];
CAPITOLUL 25. ONI 2015 459

70 //afisare(b);
71 if (p==1)
72 {
73 ///cerinta 1
74 x=v;
75 int nivel=0;
76 while(x>2*n*n)
77 x=x-2*n*n, nivel+=2;
78 int stop=0;
79 for(i=1;i<=n && stop==0; i++)
80 for(j=1;j<=n && stop==0; j++)
81 {
82 if(a[i][j]==x)
83 {
84 fout<<i<<" "<<j<<" "<<nivel+1<<endl;
85 stop=1;
86 }
87 if(b[i][j]==x)
88 {
89 fout<<i<<" "<<j<<" "<<nivel+2<<endl;
90 stop=1;
91 }
92 }
93 }
94 else
95 {
96 ///cerinta 2
97 f1=f2=f3=f4=0;
98 ///fata 1 - linia 1 A,B,.... 200*200 elemente=40.000 nr de testat
99 for(j=1;j<=n;j++)
100 {
101 nr=a[1][j];
102 while(nr<n*n*n)
103 {
104 if (prim(nr)) f1++;
105 nr+=2*n*n;
106 }
107 nr=b[1][j];
108 while(nr<n*n*n)
109 {
110 if (prim(nr)) f1++;
111 nr+=2*n*n;
112 }
113 }
114 ///fata 2 - coloana n A,B,....
115 for(i=1;i<=n;i++)
116 {
117 nr=a[i][n];
118 while(nr<n*n*n)
119 {
120 if (prim(nr)) f2++;
121 nr+=2*n*n;
122 }
123 nr=b[i][n];
124 while(nr<n*n*n)
125 {
126 if (prim(nr)) f2++;
127 nr+=2*n*n;
128 }
129 }
130 ///fata 3 - linia n A,B,....
131 for(j=1;j<=n;j++)
132 {
133 nr=a[n][j];
134 while(nr<n*n*n)
135 {
136 if (prim(nr)) f3++;
137 nr+=2*n*n;
138 }
139 nr=b[n][j];
140 while(nr<n*n*n)
141 {
142 if (prim(nr)) f3++;
143 nr+=2*n*n;
144 }
145 }
CAPITOLUL 25. ONI 2015 460

146 ///fata 4 - coloana 1 A,B,....


147 for(i=1;i<=n;i++)
148 {
149 nr=a[i][1];
150 while(nr<n*n*n)
151 {
152 if (prim(nr)) f4++;
153 nr+=2*n*n;
154 }
155 nr=b[i][1];
156 while(nr<n*n*n)
157 {
158 if (prim(nr)) f4++;
159 nr+=2*n*n;
160 }
161 }
162 fout<<f1<<’\n’;
163 fout<<f2<<’\n’;
164 fout<<f3<<’\n’;
165 fout<<f4<<’\n’;
166 }
167
168 return 0;
169 }

Listing 25.1.4: cubul_gcc_umplere_ciur.cpp


1 ///prof. Gorea Claudiu-Cristian
2 ///C.N. Al. Papiu Ilarian Tg-Mures
3 #include <cstdio>
4
5 using namespace std;
6
7 int a[101][101][101],n,v,l,c,x,y,z,nivel,i,j,k,colt,lim,p;
8 bool ciur[1000001];
9 int f1,f2,f3,f4;
10
11 void afisare()
12 {
13 int i,j;
14 for(i=1;i<=n;i++)
15 {
16 for(j=1;j<=n;j++)
17 printf("%4d",a[i][j][nivel]);
18 printf("\n");
19 }
20 printf("\n");
21 }
22
23 int main()
24 {
25 freopen ("cub.in","r",stdin);
26 freopen ("cub.out","w",stdout);
27
28 scanf("%d",&p);
29 scanf("%d %d",&n,&v);
30
31 nivel=0;
32 k=0;
33 ciur[1]=ciur[0]=true; /// adica nu e prim;
34 for(i=2;i<=n*n*n;i++)
35 if (ciur[i]==false)
36 for(j=2;j<=n*n*n/i;j++)
37 ciur[i*j]=true;
38
39 f1=f2=f3=f4=0;
40 /// SE COMPLETEAZA INTREGUL CUB
41 while(nivel<n)
42 {
43 nivel++;
44 ///nivel impar - plec din 1,1
45 l=c=1;
46 while(l<=n/2)
47 {
48 ///dir1
CAPITOLUL 25. ONI 2015 461

49 for(j=c;j<=n-c;j++)
50 {
51 k++;
52 a[l][j][nivel]=k;
53 if (k==v && p==1) printf("%d %d %d\n",l,j,nivel);
54 }
55 ///dir2
56 for(i=l;i<=n-l;i++)
57 {
58 k++;
59 a[i][n-c+1][nivel]=k;
60 if (k==v && p==1) printf("%d %d %d\n",i,n-c+1,nivel);
61 }
62 ///dir3
63 for(j=n-c+1;j>c;j--)
64 {
65 k++;
66 a[n-l+1][j][nivel]=k;
67 if (k==v && p==1) printf("%d %d %d\n",n-l+1,j,nivel);
68 }
69 ///dir4
70 for(i=n-l+1;i>l;i--)
71 {
72 k++;
73 a[i][c][nivel]=k;
74 if (k==v && p==1) printf("%d %d %d\n",i,c,nivel);
75 }
76 l++;
77 c++;
78 }
79 if (n%2==1)
80 {
81 k++;
82 a[n/2+1][n/2+1][nivel]=k;
83 if (k==v && p==1) printf("%d %d %d\n",n/2+1,n/2+1,nivel);
84 }
85 // afisare();
86 if (nivel<n)
87 {
88 colt=a[1][1][nivel];
89 nivel++; /// nivelul par
90 lim=colt+n*n*2;
91 for(i=1;i<=n;i++)
92 for(j=1;j<=n;j++)
93 {
94 a[i][j][nivel]=(colt-1)+lim-a[i][j][nivel-1];
95 if (a[i][j][nivel]==v && p==1)
96 printf("%d %d %d\n",i,j,nivel);
97 }
98 k+=n*n;
99 // afisare();
100 }
101 }
102 ///fata 1
103 for(nivel=1;nivel<=n;nivel++)
104 for(j=1;j<=n;j++)
105 if(ciur[a[1][j][nivel]]==false)
106 {
107 // printf("%d ",a[1][j][nivel]);
108 f1++;
109 }
110 if (p==2) printf("%d\n",f1);
111 ///fata 2
112 for(nivel=1;nivel<=n;nivel++)
113 for(i=1;i<=n;i++)
114 if(ciur[a[i][n][nivel]]==false)
115 {
116 // printf("%d ",a[i][n][nivel]);
117 f2++;
118 }
119 if (p==2) printf("%d\n",f2);
120 ///fata 3
121 for(nivel=1;nivel<=n;nivel++)
122 for(j=1;j<=n;j++)
123 if(ciur[a[n][j][nivel]]==false)
124 {
CAPITOLUL 25. ONI 2015 462

125 // printf("%d ",a[n][j][nivel]);


126 f3++;
127 }
128 if (p==2) printf("%d\n",f3);
129 ///fata 4
130 for(nivel=1;nivel<=n;nivel++)
131 for(i=1;i<=n;i++)
132 if(ciur[a[i][1][nivel]]==false)
133 {
134 // printf("%d ",a[i][1][nivel]);
135 f4++;
136 }
137 if (p==2) printf("%d\n",f4);
138 return 0;
139 }

Listing 25.1.5: cubul_mot_e.cpp


1 // cubul - E.M. - O(n^2*log n)
2 #include <fstream>
3 #include <cmath>
4
5 using namespace std;
6
7 const int Npr=430;
8 int p[Npr]=
9 {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,
10 101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,
11 193,197,199,
12 211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,
13 307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,
14 401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,
15 503,509,521,523,541,547,557,563,569,571,577,587,593,599,
16 601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,
17 701,709,719,727,733,739,743,751,757,761,769,773,787,797,
18 809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,
19 907,911,919,929,937,941,947,953,967,971,977,983,991,997,
20 1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,
21 1097,
22 1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,
23 1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,
24 1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,
25 1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,
26 1493,1499,
27 1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,
28 1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699,
29 1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,
30 1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,1889,
31 1901,1907,1913,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999,
32 2003,2011,2017,2027,2029,2039,2053,2063,2069,2081,2083,2087,2089,2099,
33 2111,2113,2129,2131,2137,2141,2143,2153,2161,2179,
34 2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297,
35 2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399,
36 2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,
37 2503,2521,2531,2539,2543,2549,2551,2557,2579,2591,2593,
38 2609,2617,2621,2633,2647,2657,2659,2663,2671,2677,2683,2687,2689,2693,2699,
39 2707,2711,2713,2719,2729,2731,2741,2749,2753,2767,2777,2789,2791,2797,
40 2801,2803,2819,2833,2837,2843,2851,2857,2861,2879,2887,2897,
41 2903,2909,2917,2927,2939,2953,2957,2963,2969,2971,2999};
42
43 int prim(int x)
44 {
45 int i,L;
46 if (x<2) return 0;
47
48 L=(int)sqrt((double)x);
49 for (i=0;p[i]<=L;i++)
50 if(x%p[i]==0)
51 return 0;
52 return 1;
53 }
54
55 int a[202][202];
56 int dx[4]={0,1,0,-1}, dy[4]={1,0,-1,0};
57
CAPITOLUL 25. ONI 2015 463

58 int main()
59 {
60 ifstream fi("cub.in"); ofstream fo("cub.out");
61 int i,j,k,d,p,N,V;
62 fi>>p>>N>>V;
63
64 if (p==1)
65 {
66 for(i=0;i<=N+1;i++)
67 a[0][i]=a[N+1][i]=a[i][0]=a[i][N+1]=1;
68
69 i=j=1;
70 d=0;
71 for(k=1;k<=N*N;k++)
72 {
73 a[i][j]=k;
74 if(!a[i+dx[d]][j+dy[d]])
75 {
76 i+=dx[d];
77 j+=dy[d];
78 }
79 else
80 {
81 d=(d+1)%4;
82 i+=dx[d];
83 j+=dy[d];
84 }
85 }
86
87 k=V/(N*N);
88 V%=(N*N);
89 if (k&1)
90 V=N*N+1-V;
91
92 for (i=1;i<=N;i++)
93 for (j=1;j<=N;j++)
94 if (a[i][j]==V)
95 {
96 fo<<i<<" "<<j<<" "<<k+1<<"\n";
97 break;
98 }
99 }
100 else
101 {
102 int f[5],t;
103 f[1]=f[2]=f[3]=f[4]=0;
104 for (i=1;i<=N;i++)
105 for (j=1;j<=N;j++)
106 for (t=0;t<4;t++)
107 {
108 if (i&1)
109 k=j+(i-1)*N*N+t*(N-1);
110 else
111 k=i*N*N-j+1-t*(N-1);
112
113 if (t==3 && j==N)
114 k=(i&1)*(1+(i-1)*N*N);
115
116 f[t+1]+=prim(k);
117 }
118 for (t=1;t<=4;t++)
119 fo<<f[t]<<"\n";
120 }
121
122 return 0;
123 }

25.1.3 *Rezolvare detaliat 


CAPITOLUL 25. ONI 2015 464

25.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
persoanelor 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  ecare 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 aate în dreapta persoanei
celei mai înalte vor  mai înalte decât cele aate în stânga persoanei cele mai înalte. În plus, înal-
µimile persoanelor vor  cresc toare pân  la poziµia k a persoanei celei mai înalte ³i descresc toare
dup  poziµia k .
Mai exact: dac  h1 , h2 , ..., hn sunt în lµimile persoanelor dup  nalizarea operaµiilor de
mutare, atunci: exist  o poziµie k , cu 1 & k & n astfel încât

h1 $ h2 $ ...hk 1 $ hk % hk 1 % ... % hn 1 % hn
  

³i în plus hi $ hj pentru oricare i $ k ³i k $ j .


Deoarece o asemenea logic  este greu de comb tut, iar agenµii nu au aerul c  vor s  glumeasc ,
persoanele care a³teapt  la coad  vor accepta toate mut rile impuse de c tre ace³tia.

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 ³ierului de intrare risc.in se g se³te num rul natural p a c rui valoare poate
 doar 1 sau 2.
Pe a doua linie a ³ierului de intrare se a  num rul natural n cu semnicaµ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
Fi³ierul de ie³ire este risc.out.
a Dac  valoarea lui p este 1 atunci se va rezolva numai cerinµa 1. în acest caz, ³ierul de ie³ire
va conµine pe prima linie un num r natural poz ce reprezint  poziµia persoanei celei mai înalte în
rândul iniµial. Dac  rândul iniµial nu respect  condiµiile de risc minim de securitate, atunci poz
este -1.
a Dac  valoarea lui p este 2 se va rezolva numai cerinµa 2. în acest caz ³ierul de ie³ire va
conµine pe prima linie un num r natural m, reprezentând num rul minim de mut ri necesare
pentru a obµine risc minim de securitate.

Restricµii ³i preciz ri
a 1 & n & 100 000
a 1 & h1 , h2 , ...hn & 100 000
a Persoana cea mai înalt  într-o conguraµie cu risc minim de securitate poate  plasat  pe
oricare dintre poziµiile 1...n
a Pentru 50% din teste, n & 2 000, iar pentru alte 40% din teste, n & 10 000
a Pentru rezolvarea corect  a primei cerinµe se acord  20 de puncte, iar pentru cerinµa a doua
se acord  80 de puncte.

Exemple:
CAPITOLUL 25. ONI 2015 465

risc.in risc.out Explicaµii


1 4 35 20 10 1 p = 1, deci se rezolv  numai cerinµa 1.
1 Rândul îndepline³te condiµiile de risc minim de securitate.
Persoana cea mai înalt  se g seste pe poziµia poz = 1.
1 6 1 8 12 20 -1 p = 1, deci se rezolv  numai cerinµa 1.
15 10 Rândul NU îndepline³te condiµiile de risc minim de securitate.
³irul în lµimilor nu respect  condiµia ca toate valorile în lµimilor
din dreapta poziµiei persoanei celei mai înalte s  e mai mari
decât toate valorile în lµimilor din stânga acesteia.
Deci poz = -1.
2 5 2 8 4 20 1 p = 2, deci se rezolv  numai cerinµa 2.
16 Se mut  persoana de în lµime 8 la sfâr³itul rândului.
Deci m = 1
2 6 3 19 7 30 2 p = 2, deci se rezolv  numai cerinµa 2.
10 25 Prima operaµie: se mut  persoana de în lµime 19 la sfâr³itul
rândului.
Rândul devine: 3 7 30 10 25 19
A doua operaµie: se mut  persoana de în lµime 10 la sfâr³itul
rândului.
Rândul devine: 3 7 30 25 19 10
Deci m = 2
Timp maxim de executare/test: 0.2 secunde
Memorie: total 4 MB
Dimensiune maxim  a sursei: 10 KB

25.2.1 Indicaµii de rezolvare

prof. Constantin G l µan - C. N. Liviu Rebreanu Bistriµa index[person]Constantin G l µan

Cerinµa 1. 20 puncte
Se determin  înalµimea hmax ³i poziµia pmax a celei mai înalte persoane.
Pentru ecare poziµie 2 & i & pmax se veric  dac  h[i] > h[i -1] ³i similar, pentru ecare
poziµie i % pmax se veric  dac  pentru persoana i aat  în dreapta acesteia se veric  dac 
hi $ hi  1. Pentru a determina dac  toate persoanele aate în dreapta poziµiei pmax sunt
mai înalte decât cele aate în stânga se veric  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  în dreapta maximului dup  mut ri
Pentru ecare î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  mutate.
Vom muta apoi la sfâr³it toate valorile aate în dreapta maximului care nu respect  cerinµa
a doua de mai sus, deci pentru ecare î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 aau
iniµial în dreapta maximului ³i nu au fost mutate s  e 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 25. ONI 2015 466

Soluµia 2 - Complexitate O n ˜ log n prof. Constantin G l µan, C. N. Liviu Rebreanu


Bistriµa
Persoanele care nu se mut  trebuie alese în a³a fel încât s  existe certitudinea c  celelalte
persoane pot  mutate la cap tul din dreapta al rândului. Astfel, va trebui s  g sim lungimea
celui mai lung sub³ir cresc tor, format din cele mai mici numere din ³ir. De asemenea, începând
cu poziµia pmax a maximului în lµimilor, vom determina un num r Desc reprezentând lungimea
celui mai lung sub³ir descresc tor format din cele mai mari numere din ³ir.
Atenµie, nu se aplica algoritmul clasic de programare dinamica!
Se face o copie c a sirului h al în ltimilor ³i se sorteaz  copia. Apoi, pentru ecare poziµie i
din sirul h, se contorizeaz  num rul de valori întâlnite în parcurgerea sirului h de la inceput spre
sfar³it ³i care sunt cele mai mici valori ³i totodat  consecutive în sirul c.
Deci pentru ecare poziµie i, în sirul h, se determin  num rul Asci, reprezentand num rul
de valori aate în ordine cresc toare, din intervalul de poziµii 1..i, astfel încât acestea s  e cele
mai mici valori din sir. Pornind de la poziµia pmax, mergând c tre sfâr³itul ³irului se determin 
un singur num r: Desc, reprezentând num rul de valori din intervalul de poziµii pmax, n, aate
în ordine descrescatoare astfel încât acestea s  e cele mai mari valori din ³ir.
Fie M num rul de elemente care trebuie mutate. Dac  Ascpmax  Desc % Ascn, atunci
maximul nu se mut  la cap tul randului ³i M Ascpmax  Desc.
Un caz special este acela în care lungimea Ascn a celui mai lung sub³ir cresc tor format din
cele mai mici numere din ³ir, este mai mare decât Ascpmax  Desc. În acest caz, r mân pe loc
toate valorile din acest sub³ir ³i se mut  toate celelalte, inclusiv maximul.
În concluzie num rul maxim de persoane care nu se mut  este: max Ascn, Ascpmax 
Desc, iar
M n  max Ascn, Ascpmax  Desc
S  lu m un prim exemplu:

Cu ro³u s-au reprezentat cele mai mici numere din ³ir aate î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
gurate cele mai mari numere din ³ir aate î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  e mutate. Ordinea mut rii poate  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 exemplicat  mai jos:
CAPITOLUL 25. ONI 2015 467

Se observ  c  pmax 6, Asc6  Desc 3  3 % Ascn 5.


A³adar ³i în acest caz M n  Ascpmax  desc 11  6 5
Un ultim exemplu:

În acest caz, pmax 5, Asc5  Desc 2  2 $ Ascn 5, iar M n  Ascn.


A³adar, pentru toate situaµiile: M n  max Ascpmax  desc, Ascn
Pentru determinarea ³irurilor Asc ³i a variabilei Desc, este necesar  copierea ³irului h ³i sor-
tarea copiei, urmate de parcurgerea ³irului h.
Determinarea valorii num rului M de persoane care trebuie mutate se face în timp constant
conform relaµiei de mai sus.
Complexitatea algoritmului depinde de sortare, deci O n ˜ logn.
O asemenea soluµie obµine 70 de puncte din 80 posibile.
Soluµia 3 - Complexitate O n
Soluµia este de fapt identic  cu Soluµia 2, dar se coboar  la O n complexitatea algoritmului
de sortare cu o sortare prin num rare.
O asemenea soluµie obµine 80 de puncte din 80 posibile.

25.2.2 Cod surs 

Listing 25.2.1: risc.cpp


1 /*
2 Constantin Galatan
3 Complexitate: O(n)
4 */
5
6 #include <fstream>
7 #include <vector>
8 #include <algorithm>
9
10 using namespace std;
11 using VI = vector<int>;
12
CAPITOLUL 25. ONI 2015 468

13 VI a, b, c; // sirul sirul inaltimilor, sirul sortat, sir auxiliar


14 int n, V;
15
16 int main()
17 {
18 ifstream fin("risc.in");
19 ofstream fout("risc.out");
20
21 int hmax(0), pmax(-1);
22 fin >> V >> n;
23 a = VI(n);
24 for (int i = 0; i < n; ++i)
25 {
26 fin >> a[i];
27 if ( a[i] > hmax )
28 hmax = a[i], pmax = i;
29 }
30
31 if ( V == 1 )
32 {
33 bool ok = true;
34 for (int i = 0; i < n && ok; ++i)
35 {
36 if ( i && i <= pmax && a[i - 1] > a[i] )
37 ok = false;
38
39 if ( i > pmax && a[i - 1] < a[i] )
40 ok = false;
41 }
42
43 if ( ok && pmax && a[pmax - 1] > a[n - 1])
44 ok = false;
45 if ( ok )
46 fout << pmax + 1 << ’\n’;
47 else
48 fout << -1 << ’\n’;
49 }
50 else
51 {
52 b = VI(n); c = VI(hmax + 1);
53 for (int i = 0; i < n; i++)
54 c[a[i]]++; // c[i] - numarul elem egale cu i (frecventele)
55
56 for (int i = 1; i <= hmax; i++) // O(n)
57 c[i] += c[i-1]; // c[i] - numarul elem mai mici sau egale cu i
58
59 for (int i = 0; i < n; i++)
60 b[c[a[i]] - 1] = a[i];
61
62 int cntAsc(0), cntDesc(0), k(0);
63 for (int i(0), j(0); i < n; ++i) // partea crescatoare
64 {
65 if (a[i] == b[j] )
66 cntAsc++, j++;
67
68 if ( i == pmax )
69 k = cntAsc;
70 }
71
72 for (int i(pmax), j(n - 1); i < n; ++i) // partea descrescatoare
73 if (a[i] == b[j] )
74 cntDesc++, j--;
75
76 fout << n - max(cntAsc, cntDesc + k) << ’\n’;
77 }
78
79 fin.close();
80 fout.close();
81 return 0;
82 }

Listing 25.2.2: risc_n2.cpp


1 // prof. Carmen Popescu - Colegiul National "Gh. Lazar" Sibiu
2 // O(n^2)
CAPITOLUL 25. ONI 2015 469

3 #include <iostream>
4 #include <fstream>
5 #include <algorithm>
6 #include <bitset>
7
8 using namespace std;
9
10 int a[100001];
11 int n,mx,p;
12
13 ifstream f("risc.in");
14 ofstream g("risc.out");
15
16 bitset<100001> b;
17 bitset<100001> c;
18
19 int main()
20 {
21 int v,i,j,k,mn;
22 f>>v>>n;
23 for (i=1;i<=n;i++)
24 {
25 f>>a[i];
26 if (a[i]>mx)
27 {
28 mx=a[i]; p=i;
29 }
30 }
31
32 if (v==1)
33 {
34 // ordinea din stanga
35 for (i=1;i<p;i++)
36 if (a[i]>a[i+1])
37 {
38 g<<-1<<"\n";
39 return 0;
40 }
41
42 // ordinea din dreapta
43 for (i=p+1;i<n;i++)
44 if (a[i]<a[i+1])
45 {
46 g<<-1<<"\n";
47 return 0;
48 }
49
50 // in stanga sunt numere mai mici decat minimul din dreapta
51 if (a[p-1]>a[n] || a[p+1]<a[p-1])
52 {
53 g<<-1<<"\n";
54 return 0;
55 }
56 g<<p<<"\n";
57 }
58 else
59 {
60 // CAZUL 1: mutam ce nu e ok din stanga si dreapta
61
62 // mn = minimul valorilor ce vor fi in dreapta dupa mutari
63 mn=a[p];
64
65 // marcam nr din stanga ce nu respecta ordinea si vor fi
66 // mutate la sfarsit
67 k=0;
68 mx=-1; // mx= maximul valorilor ce se muta la sfarsit
69 for (i=2;i<p;i++) // pt fiecare valoare din stanga
70 for (j=i-1;j>=1;j--) // mutam (marcam) numerele din fata sa
71 // care sunt mai mari, adica ele
72 // vor trebui mutate
73 if (a[j]>a[i] && !b[j])
74 {
75 b[j]=true;
76 k++;
77 if (a[j]>mx) mx=a[j];
78 if (a[j]<mn) mn=a[j];
CAPITOLUL 25. ONI 2015 470

79 }
80
81 // marcam valorile din dreapta care nu respecta ordinea si
82 // vor fi mutate la sfarsit
83 for (i=p+2;i<=n;i++)
84 {
85 if (a[i]<mn)
86 mn=a[i];
87 for (j=i-1;j>p;j--)
88 if (a[j]<a[i] && !b[j])
89 {
90 b[j]=true;
91 k++;
92 if (a[j]>mx) mx=a[j];
93 }
94 }
95
96 // marcam valorile care au ramas in stanga (nemutate) care sunt
97 // mai mari decat minimul din dreapta si care vor
98 // fi mutate la sfarsit
99 for (i=1;i<p;i++)
100 if (!b[i] && a[i]>mn)
101 {
102 b[i]=true;
103 k++;
104 if (a[i]>mx) mx=a[i];
105 }
106
107 // verificam valorile din dreapta care au ramas nemutate si care
108 // sunt mai mici decat maximul celor mutate, ele trebuie mutate
109 // la sfarsit
110 for (i=n;i>p;i--)
111 if (!b[i] && a[i]<mx)
112 k++;
113
114 // CAZUL 2:
115 // mutam maximul la sfarsit pe urma mutam ce nu respecta ordinea
116 // mn = minimul valorilor ce se vor afla in dreapta
117 mn=a[p];
118 int k1=1; // am mutat maximul la sfarsit
119 for (i=2;i<=n;i++)
120 if (i!=p)
121 for (j=i-1;j>=1;j--)
122 if (j!=p && a[j]>a[i] && !c[j])
123 {
124 c[j]=true;
125 k1++;
126 if (a[j]<mn) mn=a[j];
127 }
128 // cautam valorile ramase nemutate si care sunt mai mari decat
129 // minimul celor mutate si ele vor fi mutate
130 for (i=1;i<=n;i++)
131 if (i!=p && !c[i] && a[i]>mn)
132 k1++;
133
134 // solutia e cea din cazul cel mai favorabil
135 if (k<k1)
136 g<<k<<"\n";
137 else
138 g<<k1<<"\n";
139 }
140
141 return 0;
142 }

Listing 25.2.3: risc_nlogn.cpp


1 /*
2 Constantin Galatan
3 Complexitate: O(n*log n)
4 */
5 #include <fstream>
6 #include <vector>
7 #include <algorithm>
8
CAPITOLUL 25. ONI 2015 471

9 using namespace std;


10 using VI = vector<int>;
11
12 VI a, b; // sirul inaltimilor, sirul sortat
13 int n, V;
14
15 int main()
16 {
17 ifstream fin("risc.in");
18 ofstream fout("risc.out");
19
20 int hmax(0), pmax(-1);
21 fin >> V >> n;
22 a = VI(n);
23 for (int i = 0; i < n; ++i)
24 {
25 fin >> a[i];
26 if ( a[i] > hmax )
27 hmax = a[i], pmax = i;
28 }
29
30 if ( V == 1 )
31 {
32 bool ok = true;
33 for (int i = 0; i < n && ok; ++i)
34 {
35 if ( i && i <= pmax && a[i - 1] > a[i] )
36 ok = false;
37
38 if ( i > pmax && a[i - 1] < a[i] )
39 ok = false;
40 }
41
42 if ( ok && pmax && a[pmax - 1] > a[n - 1])
43 ok = false;
44 if ( ok )
45 fout << pmax + 1 << ’\n’;
46 else
47 fout << -1 << ’\n’;
48 }
49 else
50 {
51 b = a;
52 sort(b.begin(), b.end());
53 int cntAsc(0), cntDesc(0), k(0);
54
55 for (int i(0), j(0); i < n; ++i) // partea crescatoare
56 {
57 if (a[i] == b[j] )
58 cntAsc++, j++;
59
60 if ( i == pmax )
61 k = cntAsc;
62 }
63
64 for (int i(pmax), j(n - 1); i < n; ++i) // partea descrescatoare
65 if (a[i] == b[j] )
66 cntDesc++, j--;
67
68 fout << n - max(cntAsc, cntDesc + k) << ’\n’;
69 }
70
71 fin.close();
72 fout.close();
73 return 0;
74 }

25.2.3 *Rezolvare detaliat 


CAPITOLUL 25. ONI 2015 472

25.3 roboti
Problema 3 - roboti 100 de puncte
O rm  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, ecare robot având baza
un p trat de latur  L. Suprafaµa defri³at  de ecare 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 ecare
interogare de aceast  form  va trebui determinat  latura minim  a unui robot astfel încât s  e
necesari pentru defri³are cel mult k roboµi.

Date de intrare
Fi³ierul de intrare roboti.in conµine:
a Pe prima linie un num r natural p reprezentând varianta cerinµei de rezolvare. Pentru toate
testele de intrare, num rul p poate avea doar valoarea 1 sau valoarea 2.
a Pe a doua linie se a  3 numere naturale N , M , T separate prin câte un spaµiu reprezen-
tând num rul liniilor, num rul coloanelor terenului dreptunghiular, respectiv num rul copacilor
plantaµi.
a Urm toarele T linii conµin ecare câte dou  numere naturale x, y separate prin câte un
spaµiu, reprezentând linia, respectiv coloana parcelei în care este plantat un copac.
a În cazul cerinµei 1, ultima linie conµine un singur num r natural L, reprezentând latura unui
robot.
a În cazul cerinµei 2, penultima linie va conµine un num r natural Q, iar ultima linie Q numere
naturale k1 , k2 , ..., kQ separate prin câte un spaµiu, reprezentând num rul maxim de roboµi ce pot
 utilizaµi în ecare dintre cele Q interog ri.

Date de ie³ire
a Dac  valoarea lui p este 1, se va rezolva numai cerinµa 1. În acest caz, în ³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 ³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  e utilizaµi cel mult ki roboµi.

Restricµii ³i preciz ri
a 2 & N, M, L & 150
a 1 & Q & 150
a 1 & ki & 150, 1 & i & Q
a 1 & T & 1000
a Latura robotului nu poate  mai mare decât dimensiunile terenului
a Pe tot parcursul deplas rii, ecare robot se va aa în interiorul suprafeµei terenului.
a în orice moment în interiorul suprafeµei terenului se va aa cel mult un robot.

Exemple:

roboti.in roboti.out Explicaµii


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.
CAPITOLUL 25. ONI 2015 473

2 688415 41 p 2
3 352655 Prima valoare din ³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  e necesar un singur robot, conform primei interog ri.
A doua valoare din ³ierul de ie³ire reprezint  latura minim  pe
care o pot avea roboµii astfel încât pentru defri³area întregului
teren s  e necesari cel mult trei roboµi, conform celei de-a doua
interog ri.
Atenµie! Pentru acest test se rezolv  doar cerinµa 2.

Timp maxim de executare/test: 0.1 secunde


Memorie: total 8 MB
Dimensiune maxim  a sursei: 10 KB

25.3.1 Indicaµii de rezolvare

Autor: prof. Soa Viµelaru - C. N. Fratii Buzesti Craiova

Cerinµa 1. 50 puncte prof. Constantin G l µan, C. N. Liviu Rebreanu Bistrita


Soluµia 1 - Complexitate O(n* T*log T)
Se aplica o tehnic  greedy. Se sorteaz  coordonatele copacilor cresc tor dup  linie ³i pentru
aceea³i linie, cresc tor dup  coloan . Se plaseaz  robotul curent cu colµul dreapta sus pe primul
copac (cel mai de sus ³i din stânga). Din acest moment se simuleaz  mi³carea.
Se parcurg punctele în ordinea sortata. Pentru ecare copac întâlnit pot exista situaµiile: este
acoperit de robot (copac tip A), se a  în dreapta ³i mai jos faµ  de colµul dreapta sus al robotului
(copac de tip B), se a  sub latura de jos a robotului (copac de tip C) sau se a  la stânga
robotului (copac de tip D). Copacii de tip A ³i de tip C se ³terg din ³irul de copaci, copacii de tip
D se pierd ³i va trebui s  e t iaµi de urm torii roboµi, iar în cazul în care apare un copac de tip
D, robotul se mut  cu colµul stânga sus pe acel robot ³i îl va t ia.
Când robotul curent atinge colµul stânga jos al terenului, urma torul robot va parcurge ³irul
de copaci r ma³i în ³ir. Algoritmul se opre³te când toµi copacii din ³ir au fost t iaµi (³ter³i din ³ir)
Soluµia 2
Din desenul de mai jos se observ  c  numarul total de roboµi necesari t ierii tuturor copacilor
este egal cu lungimea maxim  a unei secvenµe de copaci în cazul din exemplu patru) care sunt
plasaµi descrescator dup  linie ³i la aceeasi linie cresc tor dupa coloan , astfel încat distanµa pe
orizontal  între doi roboµi al turaµi în secvenµ  s  e mai mare sau egal  decat latura robotului.
Toµi ceilalti roboµi de pe teren pot  t iaµi de unul dintre ace³ti roboµi.

Se parcurg copacii de sus în jos ³i de la dreapta la stânga.


în funcµie de metoda aleas  pentru determinarea celei mai lungi secvenµe de copaci care res-
pect  condiµia menµionat  mai sus se pot obµine diverse complexit µi (de exemplu, O n ˜ n cu
programare dinamic  respectiv O T ˜ log2n cu AIB 2D).

Cerinµa 2. 50 de puncte
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 
CAPITOLUL 25. ONI 2015 474

25.3.2 Cod surs 

Listing 25.3.1: roboti_aib.cpp


1 /*
2 prof Constantin Galatan
3 Complexitate: O(Q * T * log(n)^3)
4 */
5 #include <fstream>
6 #include <algorithm>
7 #include <cassert>
8
9 using namespace std;
10
11 struct Cell
12 {
13 int i, j;
14 bool operator < (const Cell& c) const
15 {
16 if (i != c.i )
17 return i < c.i;
18 return j > c.j;
19 }
20 };
21
22 int n, m;
23 vector<Cell> cl;
24 int mx[1002][1002];
25
26 int Query(int i, int j);
27 void Update(int i, int j, int val);
28 void Reset(int i, int j);
29 int CountRobots(int L); // O(T * log(n)^2)
30
31 int main()
32 {
33 ifstream fin("roboti.in");
34 ofstream fout("roboti.out");
35
36 int x, y, p, T, L;
37
38 fin >> p >> n >> m >> T;
39
40 for (int i = 0; i < T; ++i)
41 {
42 fin >> x >> y;
43 // assert(x <= n && y <= m);
44 cl.push_back({x, y});
45 }
46
47 sort(cl.begin(), cl.end());
48
49 if (p == 1) // O(T * log(n)^2)
50 {
51 fin >> L;
52 fout << CountRobots(L) << ’\n’;
53 }
54 else // O(Q * T * log(n)^3)
55 {
56 int Q, k, lo, hi, Lmin;
57
58 for (fin >> Q; Q--; fout << Lmin << ’\n’)
59 {
60 fin >> k;
61 lo = 1, hi = n / k + 1;
62 while (lo <= hi)
63 {
64 L = (lo + hi) / 2;
65 if (CountRobots(L) <= k)
66 {
67 Lmin = L;
68 hi = L - 1;
69 }
70 else
CAPITOLUL 25. ONI 2015 475

71 lo = L + 1;
72 }
73 }
74 }
75
76 fin.close();
77 fout.close();
78 }
79
80 int CountRobots(int L) // O(T * log(n)^2)
81 {
82 int cnt(0), cntUp(0);
83
84 for (const auto& c : cl)
85 {
86 cntUp = 1;
87 if (c.i > L && c.j <= m - L)
88 cntUp += Query(c.i - L, m - c.j - L + 1);
89
90 Update(c.i, m - c.j + 1, cntUp);
91 cnt = max(cnt, cntUp);
92 }
93
94 for (const auto& c : cl)
95 Reset(c.i, m - c.j + 1);
96
97 return cnt;
98 }
99
100 void Reset(int ic, int jc)
101 {
102 for (int i = ic; i <= n; i += i & -i)
103 for (int j = jc; j <= m; j += j & -j)
104 mx[i][j] = 0;
105 }
106
107 void Update(int ic, int jc, int val)
108 {
109 for (int i = ic; i <= n; i += i & -i)
110 for (int j = jc; j <= m; j += j & -j)
111 mx[i][j] = max(mx[i][j], val);
112 }
113
114 int Query(int ic, int jc)
115 {
116 int cnt(0);
117 for (int i = ic; i >= 1; i -= i & -i)
118 for (int j = jc; j >= 1; j -= j & -j)
119 cnt = max(cnt, mx[i][j]);
120 return cnt;
121 }

Listing 25.3.2: roboti_dp.cpp


1 /*
2 prof. Constantin Galatan
3 O(Q * (n^2) * log n)
4 */
5 #include <fstream>
6 #include <algorithm>
7 #include <vector>
8 #include <bitset>
9
10 using namespace std;
11 using VI = vector<int>;
12
13 const int MaxN = 155;
14 bitset<MaxN> b[MaxN];
15 vector<VI> a;
16 int n, m;
17
18 int CountRobots(int L);
19
20 ifstream fin("roboti.in");
21 ofstream fout("roboti.out");
CAPITOLUL 25. ONI 2015 476

22
23 int main()
24 {
25 int x, y, p, T, L;
26
27 fin >> p >> n >> m >> T;
28
29 for (int i = 1; i <= T; ++i)
30 {
31 fin >> x >> y;
32 b[x][y] = 1;
33 }
34
35 if ( p == 1 )
36 {
37 fin >> L;
38 fout << CountRobots(L) << ’\n’;
39 }
40 else
41 {
42 int Q, k, lo, hi, Lmin;
43
44 for (fin >> Q; Q--; fout << Lmin << ’\n’)
45 {
46 fin >> k;
47 lo = 1, hi = n / k + 1;
48 while (lo <= hi)
49 {
50 L = (lo + hi) / 2;
51
52 if (CountRobots(L) <= k)
53 {
54 Lmin = L;
55 hi = L - 1;
56 }
57 else
58 lo = L + 1;
59 }
60 }
61 }
62
63 fin.close();
64 fout.close();
65 }
66
67 int CountRobots(int L)
68 {
69 int cnt = 0;
70 a = vector<VI>(n + 1, VI(m + 2));
71
72 for (int i = 1; i <= n; ++i)
73 for (int j = m; j >= 1; --j)
74 {
75 if ( b[i][j] )
76 {
77 if ( i > L && j <= m - L )
78 a[i][j] = 1 + a[i - L][j + L];
79 else
80 a[i][j] = 1;
81 }
82 else
83 a[i][j] = max(a[i][j + 1], a[i - 1][j]);
84
85 cnt = max(cnt, a[i][j]);
86 }
87
88 return cnt;
89 }

Listing 25.3.3: roboti_greedy.cpp


1 /*
2 prof. Constantin Galatan
3 O(Q * T * log n)
4 */
CAPITOLUL 25. ONI 2015 477

5 #include <iostream>
6 #include <fstream>
7 #include <algorithm>
8 #include <deque>
9
10 using namespace std;
11 using PII = pair<int, int>;
12
13 #define I q1.front().first
14 #define J q1.front().second
15
16 deque<PII> q, q1, q2, q3;
17 int n, m;
18
19 int CountRobots(int L);
20
21 int main()
22 {
23 ifstream fin("roboti.in");
24 ofstream fout("roboti.out");
25
26 int x, y, p, T, L;
27
28 fin >> p >> n >> m >> T;
29 for (int i = 1; i <= T; ++i)
30 {
31 fin >> x >> y;
32 q.push_back({x, y});
33 }
34
35 sort(q.begin(), q.end());
36
37 if ( p == 1 )
38 {
39 fin >> L;
40 fout << CountRobots(L) << ’\n’;
41 }
42 else
43 {
44 int Q, k, lo, hi, Lmin;
45
46 for (fin >> Q; Q--; fout << Lmin << ’\n’)
47 {
48 fin >> k;
49 lo = 1, hi = n / k + 1;
50 while (lo <= hi)
51 {
52 L = (lo + hi) / 2;
53
54 if ( CountRobots(L) <= k)
55 {
56 Lmin = L;
57 hi = L - 1;
58 }
59 else
60 lo = L + 1;
61 }
62 }
63 }
64
65 fin.close();
66 fout.close();
67 }
68
69 int CountRobots(int L)
70 {
71 int lastJ, lastI, i1, i2, j1, cnt = 0;
72 bool first(true);
73 for (q1 = q; !q1.empty(); q1.swap(q2))
74 {
75 cnt++;
76 lastJ = J, lastI = I, first = true;
77
78 for ( ; !q1.empty(); )
79 {
80 // cobor cat pot pe verticala
CAPITOLUL 25. ONI 2015 478

81 if (!q1.empty() && J <= lastJ && J > lastJ - L)


82 {
83 q1.pop_front();
84 continue;
85 }
86
87 // daca e un nou robot si vin din stanga, atunci el ia
88 // tot ce robotul pe orizontala
89 if (first && !q1.empty() && J <= lastJ && I <= lastI + L - 1 )
90 {
91 q1.pop_front();
92 continue;
93 }
94
95 // se pierde - va fi luat de urmatorul robot
96 if ( !q1.empty() && J <= lastJ - L )
97 {
98 q2.push_back(q1.front());
99 q1.pop_front();
100 continue;
101 }
102
103 if ( J > lastJ )
104 {
105 i1 = I, i2 = min(n, i1 + L - 1), j1 = max(J, L);
106
107 while ( !q1.empty() && I >= i1 && I <= i2 )
108 {
109
110 // dreptunghiul pana sub copacul pe care a fost
111 // plasat robotul anterior
112 if ( !q1.empty() && J <= j1 && J >= lastJ - L + 1 )
113 {
114 q1.pop_front();
115 continue;
116 }
117
118 // suntem inca in intervalul de culisare
119 // (penultimul robot) spre stanga
120 if (first && !q1.empty() && J <= lastJ && I <= lastI + L - 1 )
121 {
122 q1.pop_front();
123 continue;
124 }
125
126 // ramane neacoperit in stanga robotului
127 if ( !q1.empty() && J <= lastJ - L )
128 {
129 q2.push_back(q1.front());
130 q1.pop_front();
131 continue;
132 }
133
134 // ce-i in dreapta ÈŹi jos fata de copacull de la
135 // coloana J pun in alt sir
136 if ( !q1.empty() && J > j1 )
137 {
138 q3.push_back(q1.front());
139 q1.pop_front();
140 continue;
141 }
142 }
143
144 for (; !q3.empty(); q3.pop_back())
145 q1.push_front(q3.back());
146
147 lastJ = j1;
148 lastI = i1;
149 first = false;
150 }
151 }
152 }
153
154 return cnt;
155 }
CAPITOLUL 25. ONI 2015 479

25.3.3 *Rezolvare detaliat 

25.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  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  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  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 con-
strucµie.

Date de intrare
Fi³ierul casa.in conµine:
a Pe prima linie un num r natural p reprezentând cerinµa care trebuie rezolvat . Pentru toate
testele de intrare, num rul p poate avea valoarea 1 sau 2.
a Dac  valoarea lui p este 1 atunci liniile urm toare conµin descrierea unei case prin construcµie
astfel: pe linia a doua un num r natural N reprezentând num rul de camere ale casei, iar pe ecare
din urm toarele N  1 linii câte dou  valori separate prin câte un spaµiu - un num r natural ³i un
caracter, cu semnicaµia de mai sus.
a Dac  valoarea lui p este 2 atunci liniile urm toare conµin descrierea unei case prin matrice
minimal  astfel: pe linia a doua dou  numere naturale nenule M , N reprezentând dimensiunile
matricei, iar pe urm toarele M linii câte N numere 0 sau 1 separate prin câte un spaµiu.

Date de ie³ire
Dac  valoarea lui p este 1 atunci se va rezolva numai cerinµa 1. În acest caz ³ierul casa.out
va conµine pe prima linie dou  numere naturale M ³i N , separate prin câte un singur spaµiu repre-
zentâ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 ³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
coloane care conµine valoarea 1 în prima linie). Urm toarele N r  1 linii vor conµine ecare 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  numerotate începând de la 1, iar dac  exist  mai
multe soluµii va  a³at  cea mai mic  în ordine lexicograc : perechea k, l va  a³at  înaintea
perechii k, l dac  k $ k sau dac  k k ³i l $ l (adic  E < N < S < V).
CAPITOLUL 25. ONI 2015 480

Restricµii ³i preciz ri
a Matricea minimal  a unei case are maximum 100 000 elemente.

Exemple:
casa.in casa.out Explicaµii
1 23
5 111
1 E 011
2 E
2 S
3 S
2 51
2 3 1E
1 11 1S
1 01 2E
4S
Timp maxim de executare/test: 0.1 secunde
Memorie: total 4 MB
Dimensiune maxim  a sursei: 10 KB

25.4.1 Indicaµii de rezolvare

prof. Nistor Moµ - Colegiul Naµional Nicolae B lcescu Br ila

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).

25.4.2 Cod surs 

Listing 25.4.1: casa.cpp


1 // Casa O(m*n)
2 #include <fstream>
3
4 using namespace std;
5
6 #define M 100001
7 int p[M][4],t[M];
8
9 int main()
10 {
11 ifstream fi("casa.in");
12 ofstream fo("casa.out");
13
14 int a[M],m,n,i,j,k,v,nc,op;
15 char c[M],pct[7]="xENSV";
16
17 fi>>op;
18 if (op==1)
19 {
CAPITOLUL 25. ONI 2015 481

20 fi>>nc;
21 for (i=1;i<nc;i++)
22 fi>>a[i]>>c[i];
23
24 int max_e=0,max_v=0,max_n=0,max_s=0;
25 for (i=1;i<=nc;i++)
26 {
27 for(j=0;j<4;j++)
28 p[i+1][j]=p[a[i]][j];
29
30 switch(c[i])
31 {
32 case ’N’:
33 p[i+1][2]--;
34 p[i+1][1]++;
35 if(p[i+1][1]>max_n)
36 max_n=p[i+1][1];
37 break;
38 case ’S’:
39 p[i+1][1]--;
40 p[i+1][2]++;
41 if(p[i+1][2]>max_s)
42 max_s=p[i+1][2];
43 break;
44 case ’E’:
45 p[i+1][3]--;
46 p[i+1][0]++;
47 if(p[i+1][0]>max_e)
48 max_e=p[i+1][0];
49 break;
50 case ’V’:
51 p[i+1][0]--;
52 p[i+1][3]++;
53 if(p[i+1][3]>max_v)
54 max_v=p[i+1][3];
55 break;
56 }
57 }
58
59 m=max_n+max_s+1;
60 n=max_e+max_v+1;
61 fo<<m<<" "<<n<<"\n";
62 p[1][1]=max_n+1;
63 p[1][2]=max_v+1;
64
65 for (i=1;i<nc;i++)
66 {
67 switch(c[i])
68 {
69 case ’N’:
70 p[i+1][1]=p[a[i]][1]-1;
71 p[i+1][2]=p[a[i]][2];
72 break;
73 case ’S’:
74 p[i+1][1]=p[a[i]][1]+1;
75 p[i+1][2]=p[a[i]][2];
76 break;
77 case ’E’:
78 p[i+1][1]=p[a[i]][1];
79 p[i+1][2]=p[a[i]][2]+1;
80 break;
81 case ’V’:
82 p[i+1][1]=p[a[i]][1];
83 p[i+1][2]=p[a[i]][2]-1;
84 break;
85 }
86 }
87
88 for (i=1;i<=nc;i++)
89 t[(p[i][1]-1)*n+p[i][2]-1]=1;
90
91 for (i=1;i<=m;i++)
92 {
93 for (j=1;j<=n;j++)
94 fo<<t[(i-1)*n+j-1]<<" ";
95 fo<<"\n";
CAPITOLUL 25. ONI 2015 482

96 }
97 }
98
99 if (op==2)
100 {
101 fi>>m>>n;
102 nc=0;
103 k=0;
104
105 for (j=1;j<=n;j++)
106 {
107 fi>>t[j-1];
108 nc+=t[j-1];
109 if (k==0 && t[j-1]==1)
110 k=j;
111 }
112
113 for (i=2;i<=m;i++)
114 for (j=1;j<=n;j++)
115 {
116 fi>>t[(i-1)*n+j-1];
117 nc+=t[(i-1)*n+j-1];
118 }
119
120 fo<<nc<<" "<<k<<"\n";
121 p[1][0]=1;
122 p[1][1]=k;
123 t[k-1]=2;
124 k=1;
125 v=1;
126
127 while (v<nc)
128 {
129 i=p[k][0];
130 j=p[k][1];
131
132 if (j<n && t[(i-1)*n+j]==1)
133 {
134 v++;
135 t[(i-1)*n+j]=v+1;
136 p[v][0]=i;
137 p[v][1]=j+1;
138 p[v][2]=1;
139 }
140
141 if (i>1 && t[(i-2)*n+j-1]==1)
142 {
143 v++;
144 t[(i-2)*n+j-1]=v+1;
145 p[v][0]=i-1;
146 p[v][1]=j;
147 p[v][2]=2;
148 }
149
150 if (i<m && t[i*n+j-1]==1)
151 {
152 v++;
153 t[i*n+j-1]=v+1;
154 p[v][0]=i+1;
155 p[v][1]=j;
156 p[v][2]=3;
157 }
158
159 if (j>1 && t[(i-1)*n+j-2]==1)
160 {
161 v++;
162 t[(i-1)*n+j-2]=v+1;
163 p[v][0]=i;
164 p[v][1]=j-1;
165 p[v][2]=4;
166 }
167
168 k++;
169 }
170
171 for (k=2;k<=nc;k++)
CAPITOLUL 25. ONI 2015 483

172 {
173 i=p[k][0];
174 j=p[k][1];
175 switch(p[k][2])
176 {
177 case 1: j--; break;
178 case 2: i++; break;
179 case 3: i--; break;
180 case 4: j++; break;
181 }
182
183 fo<<t[(i-1)*n+j-1]-1<<" "<<pct[p[k][2]]<<"\n"; }
184 }
185
186 return 0;
187 }

Listing 25.4.2: casa_slow.cpp


1 ///prof. Gorea Claudiu-Cristian
2 ///Colegiul National Al. Papiu Ilarian Tg-Mures
3 #include <fstream>
4
5 using namespace std;
6
7 ifstream fin ("casa.in");
8 ofstream fout("casa.out");
9
10 struct casa
11 {
12 int x,y;
13 } coada[100002];
14
15
16
17 int dx[4]={ 0,-1,+1, 0};
18 int dy[4]={+1, 0, 0,-1};
19
20 int caz,t,leg,lin,col,lmax,cmax,i,j,n,m,st,dr,k;
21 char c;
22
23 int main()
24 {
25 fin>>caz;
26
27 if (caz==1)
28 {
29 fin>>t;
30 coada[1].x=coada[1].y=1;
31 lmax=1;
32 cmax=1;
33
34 for(i=2;i<=t;i++)
35 {
36 fin>>leg>>c;
37 lin=coada[leg].x;
38 col=coada[leg].y;
39 if (c==’E’) col++;
40 if (c==’V’) col--;
41 if (c==’N’) lin--;
42 if (c==’S’) lin++;
43 coada[i].x=lin;
44 coada[i].y=col;
45 // a[lin][col]=1;
46 if (lin>lmax) lmax=lin;
47 if (col>cmax) cmax=col;
48 }
49 fout<<lmax<<" "<<cmax<<endl;
50
51 bool a[lmax+2][cmax+2];
52 ///initializare
53 for(i=1;i<=lmax;i++)
54 for(j=1;j<=cmax;j++)
55 a[i][j]=0;
56 ///copiere din coada
CAPITOLUL 25. ONI 2015 484

57 a[1][1]=1;
58 for(i=2;i<=t;i++) a[coada[i].x][coada[i].y]=1;
59
60 ///afisare
61 for(i=1;i<=lmax;i++)
62 {
63 for(j=1;j<cmax;j++)
64 fout<<a[i][j]<<" ";
65 fout<<a[i][j]<<endl; ///ultimul de pe linie
66 }
67 }
68
69 if (caz==2)
70 {
71 fin>>n>>m;
72 bool a[n+2][m+2]; ///definire locala de matrice
73
74 ///initializare bordura
75 for(i=0;i<=n+1;i++) a[i][0]=a[i][m+1]=0;
76 for(j=1;j<=m+1;j++) a[0][j]=a[n+1][j]=0;
77
78 ///prelucrare
79 t=0;
80 lmax=0;
81 cmax=0;
82 for(i=1;i<=n;i++)
83 for(j=1;j<=m;j++)
84 {
85 fin>>a[i][j];
86 t+=a[i][j];
87 if ((lmax+cmax==0) && (a[i][j]==1)) lmax=i,cmax=j;
88 }
89 fout<<t<<" "<<cmax<<"\n";
90
91 st=dr=1;
92 coada[1].x=lmax;
93 coada[1].y=cmax;
94 a[lmax][cmax]=0;
95 while(st<=dr)
96 {
97 // st - nr camerei vecina
98 lmax=coada[st].x;
99 cmax=coada[st].y;
100 for(k=0;k<4;k++)
101 if (a[lmax+dx[k]][cmax+dy[k]]==1)
102 {
103 fout<<st;
104 if (k==0) fout<<" E\n";
105 if (k==1) fout<<" N\n";
106 if (k==2) fout<<" S\n";
107 if (k==3) fout<<" V\n";
108 a[lmax+dx[k]][cmax+dy[k]]=0;
109 dr++;
110 coada[dr].x=lmax+dx[k];
111 coada[dr].y=cmax+dy[k];
112 }
113 st++;
114 }
115 }
116
117 return 0;
118 }

Listing 25.4.3: casa2.cpp


1 //Prof. Vitelaru Sofia
2 #include <fstream>
3 #include<algorithm>
4
5 using namespace std;
6
7 ifstream f("casa.in");
8 ofstream g("casa.out");
9
10 struct matrice
CAPITOLUL 25. ONI 2015 485

11 {
12 int l,c;
13 } q[100010];
14
15 int a[100010];
16 int dx[4]={0,1,-1,0}, dy[4]={1,0,0,-1},dx1[]={0,-1,1,0},dy1[]={-1,0,0,1};
17 char dir1[]="ESNV";
18 int var,i,j,n,lmax,cmax,cmin,v,m,nr,lmin;
19 char ch;int dir[256];
20
21 int cmp(matrice a, matrice b)
22 {
23 if(a.l==b.l)
24 return a.c<b.c;
25 return a.l<b.l;
26 }
27
28 int main()
29 {
30 f>>var;
31 dir[’E’]=0;
32 dir[’S’]=1;
33 dir[’N’]=2;
34 dir[’V’]=3;
35
36 if(var==1)
37 {
38 f>>n;
39 q[1].l=1;
40 q[1].c=1;
41 cmin=lmin=1000000000;
42
43 for(i=2;i<=n;i++)
44 {
45 f>>v>>ch;
46 q[i].l=q[v].l+dx[dir[ch]];
47 q[i].c=q[v].c+dy[dir[ch]];
48
49 if(q[i].l>lmax)
50 lmax=q[i].l;
51
52 if(q[i].l<lmin)
53 lmin=q[i].l;
54
55 if(q[i].c>cmax)
56 cmax=q[i].c;
57
58 if(q[i].c<cmin)
59 cmin=q[i].c;
60 }
61
62 if(cmin<=0)
63 {
64 cmax+=(-cmin)+1;
65 for(i=1;i<=n;i++)
66 q[i].c+=(-cmin)+1;
67 }
68
69 if(lmin<=0)
70 {
71 lmax+=(-lmin);
72 for(i=1;i<=n;i++)
73 q[i].l+=(-lmin)+1;
74 }
75
76 g<<lmax<<" "<<cmax<<’\n’;
77
78 sort(q+1,q+n+1,cmp);
79
80 int k=1;
81 for(i=1;i<=lmax;i++)
82 {
83 for(j=1;j<=cmax;j++)
84 if(i==q[k].l&&j==q[k].c)
85 {
86 g<<1<<" ";k++;
CAPITOLUL 25. ONI 2015 486

87 }
88 else
89 g<<0<<" ";
90
91 g<<’\n’;
92 }
93
94 return 0;
95 }
96
97 f>>n>>m;
98 int x,c1;
99 for(i=1;i<=n;i++)
100 for(j=1;j<=m;j++)
101 {
102 f>>x;
103 if(x==1)
104 {
105 nr++;
106 if(nr==1)
107 c1=j;
108
109 a[(i-1)*m+j]=-1;
110 }
111 }
112
113 g<<nr<<" "<<c1<<’\n’;
114
115 q[1].l=1;
116 q[1].c=c1;
117 a[c1]=1;
118 int p=1,u=1;
119 nr=1;
120
121 while(p<=u)
122 {
123 for(int k=0;k<4;k++)
124 {
125 int l=q[p].l+dx[k];
126 int c=q[p].c+dy[k];
127 int t=(l-1)*m+c;
128 if(l>=1&&l<=n&&c>=1&&c<=m&&a[t]==-1)
129 {
130 nr++;
131 g<<a[(q[p].l-1)*m+q[p].c]<<" "<<dir1[k]<<’\n’;
132 a[t]=nr;
133 u++;
134 q[u].l=l;
135 q[u].c=c;
136 }
137 }
138
139 p++;
140 }
141
142 return 0;
143 }

25.4.3 *Rezolvare detaliat 

25.5 lenes
Problema 5 - lenes 100 de puncte
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
CAPITOLUL 25. ONI 2015 487

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 ec 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
Fi³ierul lenes.in conµine:
a Pe prima linie un num r natural p reprezentând cerinµa de rezolvare. Pentru toate testele
de intrare num rul p poate avea doar valoarea 1 sau 2.
a Pe linia a doua sunt 4 numere naturale M , N , k1, k2, separate prin câte un spaµiu cu
semnicaµia de mai sus.
a Pe urm toarele M linii se g sesc câte N numere naturale separate prin câte un spaµiu,
reprezentând eforturile de traversare a ec rei zone a terenului.

Date de ie³ire
a Dac  valoarea lui p este 1, se va rezolva numai cerinµa 1. în acest caz ³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 ³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.

Restricµii ³i preciz ri
a 3 & M, N & 500
a 0 & k1, k2 & M
a Valorile din matrice sunt numere naturale din intervalul 1, 1000.
a Lene³ul poate s  fac  popasuri pe aceea³i linie în ambele celule din stânga ³i din dreapta
coloanei parcurse.
a Deplasarea între ultima zon  a drumului parcurs de la Nord la Sud ³i prima zon  a drumului
parcurs de la Sud la Nord la întoarcere se face cu efort 0.

Exemple:

lenes.in lenes.out Explicaµii


1 12 p 1
4723 Lene³ul traverseaz  terenul de la Nord la Sud pe coloana a 5-a
99 1 33 9 2 4 7 cu popas în zonele (2, 6) ³i (4, 6).
99 1 44 8 1 2 3 Atenµie! Pentru acest test se rezolv  doar cerinµa 1.
98 1 55 8 2 3 2
97 1 66 4 3 2 1
2 35 p 2
4732 Lene³ul traverseaz  terenul de la Nord la Sud pe coloana a 7-a
99 1 33 9 2 4 7 cu popasuri în zonele (3, 6), (1, 6), (4, 6), iar de la Sud la Nord
99 1 44 8 1 2 3 pe coloana a 5 - a, cu popas în zonele (4, 4) ³i (2, 6).
98 1 55 8 2 2 2 Atenµie! Pentru acest test se rezolv  doar cerinµa 2.
97 1 66 4 3 2 1
2 19 p 2
3722 Lene³ul traverseaz  terenul de la Nord la Sud pe coloana a 6-a
2 1 33 9 99 47 cu popasuri în zonele (2, 7), (3, 7), iar de la Sud la Nord pe
1 1 44 9 99 23 coloana a 2 - a, cu popasuri în zonele (3, 1) ³i (2, 1). Efortul
2 1 55 9 99 22 de deplasare între zonele (3, 6) ³i (3, 2) este nul.
Atenµie! Pentru acest test se rezolv  doar cerinµa 2.
CAPITOLUL 25. ONI 2015 488

Timp maxim de executare/test: 0.1 secunde


Memorie: total 4 MB
Dimensiune maxim  a sursei: 10 KB

25.5.1 Indicaµii de rezolvare

prof. Nistor Moµ - Colegiul Naµional Nicolae B lcescu Br ila

Dup  citirea datelor se vor sorta elementele de pe ecare coloan 


cresc tor, ³i se vor calcula sumele de pe ecare coloan .
Cerinµa 1
Pentru ecare i, se determin suma celor mai mici k1 numere din
coloanele i  1 ³i i  1, la care adun m ³i suma elementelor din coloana
i. Pentru prima ³i ultima coloan , cele k1 popasuri se fac doar pe
coloanele 2 ³i n  1.
Cerinµa 2
Se disting trei cazuri distincte: Figura 25.1: lenes
a) drumurile sunt adiacente: coloanele i si i  1, atunci se iau in considerare pentru popas cele
mai mici k1 valori din coloana i  1³i cele mai mici k2 valori din coloana i  2 sau invers.
b) drumurile au cel puµin dou  coloane între ele, atunci sumele se calculeaz  independent, ca
la cerinµa 1.
c) între drumuri exist  o singur  coloan : drumurile sunt pe coloanele i ³i i  2, atunci vom
alege cele mai mici k1 valori din coloana i  1 ³i respectiv cele mai mici k2 valori din coloana i  3,
incercând apoi s  înlocuim cele mai mari dintre aceste volori cu cele mai mici valori din coloana
i  1.

25.5.2 Cod surs 

Listing 25.5.1: lenes.cpp


1 /**
2 * Problema: Lenes O(n^2)
3 * Stud. Popescu Silviu-Emil
4 * Automatica si Calculatoare
5 * Facultatea Politehnica Bucurest
6 */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <algorithm>
11
12 #define NMax 1010
13 #define VMax 1010
14 #define oo 0x3f3f3f3f
15
16 using namespace std;
17
18 const char IN[] = "lenes.in", OUT[] = "lenes.out";
19
20 int Case, N, M, k1, k2;
21 int Mat[NMax][NMax];
22 int Sum[NMax], V[NMax], left[NMax], right[NMax];
23
24 int get_line_sum( int lin, int k, bool up = true, bool down = true )
25 {
26 int res = Sum[lin];
27
28 static int v[VMax];
29 memset(v, 0, sizeof(v));
30
31 for ( int i = 1; i <= M; ++ i )
32 {
33 if ( up )
34 ++ v[ Mat[lin - 1][i] ];
CAPITOLUL 25. ONI 2015 489

35 if ( down )
36 ++ v[ Mat[lin + 1][i] ];
37 }
38
39 for ( int i = 0; i < VMax && k ; ++ i )
40 if ( k >= v[i] )
41 {
42 res += i * v[i];
43 k -= v[i];
44 }
45 else if ( v[i] )
46 {
47 res += k * i;
48 k = 0;
49 }
50
51 return res;
52 }
53
54 int case1()
55 {
56
57 int ret = get_line_sum(1, k1);
58
59 for ( int i = 1; i <= N; ++ i )
60 ret = min( ret, get_line_sum(i, k1));
61
62 return ret;
63 }
64
65 int case2()
66 {
67
68 int ret = oo;
69
70 for ( int i = 1; i <= N; ++ i )
71 V[i] = get_line_sum(i, k2);
72
73 left[1] = V[1];
74 for ( int i = 2; i <= N; ++ i )
75 left[i] = min(V[i], left[i - 1]);
76
77 right[N] = V[N];
78 for ( int i = N - 1; i > 0; -- i )
79 right[i] = min(V[i], right[i + 1]);
80
81
82 // computing distant lines
83 for ( int i = 1; i <= N; ++ i )
84 {
85 int r = get_line_sum(i, k1);
86 int other = oo;
87
88 if ( i > 3 ) other = min(other, left[i - 3]);
89 if ( i < N - 2) other = min(other, right[i + 3]);
90
91 ret = min(ret, r + other);
92 }
93
94 // computing adiacent lines
95 for ( int i = 1; i < N; ++ i )
96 {
97
98 int r1 = get_line_sum(i, k1 , true, false);
99 int r2 = get_line_sum(i + 1, k2, false, true);
100
101 ret = min( ret, r1 + r2 );
102
103 r1 = get_line_sum(i, k2 , true, false);
104 r2 = get_line_sum(i + 1, k1, false, true);
105
106 ret = min( ret, r1 + r2 );
107 }
108
109 //fprintf(stderr, "%d\n", ret);
110 // computing common margin lines
CAPITOLUL 25. ONI 2015 490

111
112 bool parity = true;
113
114 for ( int i = 1; i < N - 1; ++ i )
115 {
116
117 int sum = Sum[i] + Sum[i + 2];
118 int up = i - 1;
119 int mid = i + 1;
120 int down = i + 3;
121 int index_up = 1;
122 int index_down = 1;
123
124 for ( int j = 1; j <= M && j <= k1 + k2; ++ j )
125 sum += Mat[mid][j];
126
127 // adding extra elements
128 for ( int j = M + 1; j <= k1 + k2; ++ j )
129 {
130 if ( index_up <= k1 &&
131 Mat[up][index_up] <= Mat[down][index_down] ||
132 index_down > k2 )
133 {
134 sum += Mat[up][index_up];
135 ++ index_up;
136 }
137 else
138 {
139 sum += Mat[down][index_down];
140 ++ index_down;
141 }
142 }
143
144 ret = min(ret, sum);
145 // fprintf(stderr, "begining with: (%d, %d, %d)\n",
146 // index_up - 1, min(M, k1 + k2), index_down - 1);
147 for ( int j = min(M, k1 + k2); j > 0; -- j )
148 {
149 sum -= Mat[mid][j];
150 if ( index_up <= k1 &&
151 Mat[up][index_up] <= Mat[down][index_down] ||
152 index_down > k2 )
153 {
154 sum += Mat[up][index_up];
155 ++ index_up;
156 }
157 else
158 {
159 sum += Mat[down][index_down];
160 ++ index_down;
161 }
162
163 ret = min( ret, sum );
164 if ( ret == sum )
165 {
166 // fprintf(stderr, "(up: %d, mid: %d, down: %d -> %d\n",
167 // index_up - 1, j - 1, index_down - 1, ret);
168 }
169 }
170
171 if ( parity )
172 {
173 parity = false;
174 -- i;
175 }
176 else
177 {
178 parity = true;
179 }
180
181 swap( k1, k2 );
182 }
183
184 return ret;
185 }
186
CAPITOLUL 25. ONI 2015 491

187 int ( *Work[] )() = { case1, case2 };


188
189 int main()
190 {
191 freopen(IN, "r", stdin);
192 freopen(OUT, "w", stdout);
193
194 scanf("%d%d%d%d%d", &Case, &N, &M, &k1, &k2);
195
196 for ( int i = 1; i <= N; ++ i)
197 for ( int j = 1; j <= M; ++ j )
198 scanf("%d", &Mat[j][i]);
199
200 swap(N, M);
201
202 for ( int i = 1; i <= N; ++ i )
203 for ( int j = 1; j <= M; ++ j )
204 Sum[i] += Mat[i][j];
205
206 for ( int i = 1; i <= M; ++ i )
207 Mat[0][i] = Mat[N + 1][i] = VMax - 1;
208
209 for ( int i = 1; i <= N; ++ i )
210 sort( Mat[i] + 1, Mat[i] + M + 1);
211
212 printf("%d\n", Work[Case - 1]());
213
214 return 0;
215 }

Listing 25.5.2: lenes2.cpp


1 // prof. Carmen Popescu - Colegiul National "Gh. Lazar" Sibiu
2 #include <fstream>
3 #include <climits>
4
5 using namespace std;
6
7 int a[502][502],sum[502],m,n;
8 int mn=INT_MAX;
9
10 ifstream f("lenes.in");
11 ofstream g("lenes.out");
12
13 int calc(int c,int k)
14 {
15 int s=0,i1=1,i2=1,i;
16 for (i=1;i<=k;i++)
17 if (a[i1][c-1]<a[i2][c+1])
18 {
19 s+=a[i1][c-1];
20 i1++;
21 }
22 else
23 {
24 s+=a[i2][c+1];
25 i2++;
26 }
27 s+=sum[c];
28 return s;
29 }
30
31 int main()
32 {
33 int i,j,i1,i2,i3,ok,j1,k1,k2,k3,k4,p;
34 int s1,s2,s;
35 f>>p;
36 f>>m>>n>>k1>>k2;
37
38 for (i=1;i<=m;i++)
39 {
40 for (j=1;j<=n;j++)
41 {
42 f>>a[i][j];
43 sum[j] += a[i][j];
CAPITOLUL 25. ONI 2015 492

44 }
45 a[i][0]=INT_MAX;
46 a[i][n+1]=INT_MAX;
47 }
48
49 for (j=1;j<=n;j++)
50 {
51 a[0][j]=INT_MAX;
52 a[m+1][j]=INT_MAX;
53 // sortam coloana j
54 for (i=1;i<m;i++)
55 for (i1=i+1;i1<=m;i1++)
56 if (a[i][j]>a[i1][j])
57 {
58 s=a[i][j];
59 a[i][j]=a[i1][j];
60 a[i1][j]=s;
61 }
62
63 }
64
65 if (p==1)
66 {
67 for (j=1;j<n;j++)
68 {
69 s=calc(j,k1);
70 if (s<mn) mn=s;
71 }
72 g<<mn<<"\n";
73 }
74 else
75 {
76 int cc;
77 // cazul 1: doua coloane alaturate (j si j+1)
78 for (j=2;j<n-1;j++)
79 {
80 for (int g=1;g<=2;g++)
81 {
82 s=0;
83 for (i=1;i<=k1;i++)
84 s=s+a[i][j-1];
85 for (i=1;i<=k2;i++)
86 s+=a[i][j+2];
87
88 s=s+sum[j]+sum[j+1];
89 if (s<mn) mn=s;
90 s1=k1; k1=k2; k2=s1;
91 }
92 }
93
94 // cazul 2: coloanele j si j+2 (o coloana goala intre ele)
95 for (j=2;j<n-2;j++)
96 for (int g=1; g<=2; g++)
97 {
98 s1=0;
99 for (i=1; i<=k1; i++)
100 s1+=a[i][j-1];
101 for (i=1; i<=k2; i++)
102 s1+=a[i][j+3];
103
104 // incercam sa inlocuim o parte din popasuri cu
105 // popasuri pe coloana din mijloc, j+1
106 i1=k1; i2=1; i3=k2;
107 s=s1;
108 do
109 {
110 ok=0;
111 if (i1>0 &&
112 a[i1][j-1]>=a[i3][j+3] &&
113 a[i1][j-1]>a[i2][j+1]) // inlocuim un popas de pe
114 // coloana j-1 cu popas pe
115 {
116 // coloana j+1
117 s=s+a[i2][j+1]-a[i1][j-1];
118 i1--;
119 i2++;
CAPITOLUL 25. ONI 2015 493

120 ok=1;
121 }
122 else
123 if (i3>0 &&
124 a[i3][j+3]>=a[i1][j-1] &&
125 a[i3][j+3]>a[i2][j+1]) // inlocuim un popas de pe
126 // coloana j-1 cu popas pe
127 {
128 // coloana j+1
129 s=s+a[i2][j+1]-a[i3][j+3];
130 i3--;
131 i2++;
132 ok=1;
133 }
134 } while (ok==1);
135
136 s+=sum[j]+sum[j+2];
137 if (s<mn)
138 mn=s;
139
140 s=k1; k1=k2; k2=s;
141 }
142
143 // caz particular la cazul 2: coloana 1 si coloana 3
144 for (int g=1;g<=2;g++)
145 {
146 s1=0;
147 for (i=1;i<=k1;i++)
148 s1+=a[i][2];
149
150 i1=k1+1;
151 i2=1;
152 for (i=1;i<=k2;i++)
153 if (i1<=m && a[i1][2]<a[i2][4])
154 {
155 s1+=a[i1][2]; i1++;
156 }
157 else
158 {
159 s1+=a[i2][4]; i2++;
160 }
161
162 s1+=sum[1]+sum[3];
163 if (s1<mn)
164 mn=s1;
165
166 s1=k1;
167 k1=k2;
168 k2=s1;
169 }
170
171 // caz particular la cazul 2: coloana n-2 si coloana n
172 for (int g=1;g<=2;g++)
173 {
174 s1=0;
175 for (i=1;i<=k1;i++)
176 s1+=a[i][n-1];
177
178 i1=k1+1;
179 i2=1;
180 for (i=1;i<=k2;i++)
181 if (i1<=m && a[i1][n-1]<a[i2][n-3])
182 {
183 s1+=a[i1][n-1]; i1++;
184 }
185 else
186 {
187 s1+=a[i2][n-3]; i2++;
188 }
189
190 s1+=sum[n]+sum[n-2];
191 if (s1<mn)
192 mn=s1;
193
194 s1=k1;
195 k1=k2;
CAPITOLUL 25. ONI 2015 494

196 k2=s1;
197 }
198
199 // cazul 3: doua linii total independente
200 for (j=1;j<=n-3;j++)
201 {
202 s1=calc(j,k1); // dus pe j intrs pe j1
203 s2=calc(j,k2); // dus pe j1 intors pe j
204 for (j1=j+3;j1<=n;j1++)
205 {
206 i1=s1+calc(j1,k2);
207 i2=s2+calc(j1,k1);
208 if (i1<mn)
209 mn=i1;
210 if (i2<mn)
211 mn=i2;
212 }
213 }
214
215 g<<mn<<"\n";
216 }
217 }

Listing 25.5.3: lenes3.cpp


1 // prof. Carmen Popescu - Colegiul National "Gh. Lazar" Sibiu
2 #include <fstream>
3 #include <climits>
4 #include <algorithm>
5
6 using namespace std;
7
8 int sum[502][502],a[502][502],m,n;
9 int mn=INT_MAX;
10
11 ifstream f("lenes.in");
12 ofstream g("lenes.out");
13
14 int calc(int i,int k1)
15 {
16 int mn=INT_MAX,s=0,j,s1;
17
18 if (i>1 && i<n)
19 {
20 s=sum[i][m];
21 for (j=0; j<=k1; j++)
22 {
23 s1 = sum[i-1][k1-j];
24 s1 += sum[i+1][j];
25 s1 += s;
26 if (s1<mn) mn=s1;
27 }
28 }
29 else
30 if (i==1)
31 mn=sum[1][m]+sum[2][k1];
32 else
33 mn=sum[n][m]+sum[n-1][k1];
34 return mn;
35 }
36
37 int main()
38 {
39 int p,k1,k2,i,j,s,s1,s2,k,i1,i2,i3,gg;
40 int mn=INT_MAX;
41 f>>p;
42 f>>m>>n>>k1>>k2;
43
44 for (i=1;i<=m;i++)
45 for (j=1;j<=n;j++)
46 f>>a[j][i];
47
48 for (j=1;j<=n;j++)
49 {
50 sort(a[j]+1,a[j]+m+1);
CAPITOLUL 25. ONI 2015 495

51 sum[j][1]=a[j][1];
52 for (i=2;i<=m;i++)
53 sum[j][i]=a[j][i]+sum[j][i-1];
54 }
55
56 if (p==1)
57 {
58 for (i=1;i<=n;i++)
59 {
60 s=calc(i,k1);
61 if (s<mn) mn = s;
62 }
63 g<<mn<<’\n’;
64 }
65 else
66 {
67 // cazul 1: doua coloane consecutive
68 for (i=2;i<n-1;i++)
69 {
70 s = sum[i-1][k1] + sum[i][m] + sum[i+1][m] + sum[i+2][k2];
71 if (s<mn) mn=s;
72
73 s = sum[i-1][k2] + sum[i][m] + sum[i+1][m] + sum[i+2][k1];
74 if (s<mn) mn=s;
75 }
76
77 // cazul 2: 2 coloane cu una intre ele
78 for (int tt=1;tt<=2;tt++)
79 {
80 for (i=2;i<=n-3;i++)
81 {
82
83 i1=k1; i2=0; i3=k2;
84 s = sum[i][m] + sum[i+2][m];
85 do
86 {
87 gg=0;
88 if (i1>=1 && i2<=m &&
89 a[i+1][i2+1]<a[i-1][i1] && a[i+3][i3]<=a[i-1][i1])
90 {
91 i1--; i2++; gg=1;
92 }
93 else
94 if (i3>=1 && i2<=m &&
95 a[i+1][i2+1]<a[i+3][i3] && a[i-1][i1]<=a[i+3][i3])
96 {
97 i3--; i2++; gg=1;
98 }
99 } while (gg==1);
100 s1 = s + sum[i-1][i1] + sum[i+1][i2] + sum[i+3][i3];
101 if (s1<mn) mn=s1;
102 }
103
104 // coloanele 1 si 3
105 s = sum[1][m] + sum[3][m];
106
107 if (n>3)
108 {
109 for (j=0;j<=k2;j++)
110 if (k1+j<=m && k2>j)
111 {
112 s1 = sum[2][k1+j];
113 s1 += sum[4][k2-j];
114 s1 += s;
115 if (s1<mn) mn=s1;
116 }
117 }
118 else
119 {
120 s=s+sum[2][k1+k2];
121 if (s<mn) mn=s;
122 }
123
124 // coloanele n-2 si n
125 if (n>3)
126 {
CAPITOLUL 25. ONI 2015 496

127 s = sum[n-2][m] + sum[n][m];


128
129 for (j=0;j<=k1;j++)
130 if (k2+j<=m && k1>j)
131 {
132 s1 = sum[n-3][k1-j];
133 s1 += sum[n-1][k2+j];
134 s1 += s;
135 if (s1<mn) mn=s1;
136 }
137 }
138 i1=k1; k1=k2; k2=i1;
139 }
140
141 // cazul 3: doua coloane oarecare
142 for (i=1; i<=n-3; i++)
143 {
144 s1 = calc(i,k1);
145 s2 = calc(i,k2);
146 for (i1=i+3;i1<=n;i1++)
147 {
148 s = s1 + calc(i1,k2);
149 if (s<mn) mn=s;
150
151 s = s2 + calc(i1,k1);
152 if (s<mn) mn=s;
153 }
154 }
155 g<<mn<<’\n’;
156 }
157 }

25.5.3 *Rezolvare detaliat 

25.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 
e g sit  ³i conscat  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 ind 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 ind
s  se obµin  o împ rµire în care num rul monedelor r mase nedistribuite s  e 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  distribuite conform regulilor stabilite, au fost donate bisericii.

Cerinµe
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.
CAPITOLUL 25. ONI 2015 497

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
³ierului sipet.in. Dac  exist  mai multe soluµii corecte, este acceptat  oricare dintre ele.

Restricµii ³i preciz ri
a 1 & N & 10 000 000
a 2 & p1 $ p2 $ p3 & N
a 1 & T & 10 - în ³ierul de intrare nu vor  mai mult de 10 perechi de numere N p1

Exemple:
sipet.in sipet.out Explicaµii
3 33000 - num rul maxim de sipete este 3, toate cu câte 3 monede;
15 5 21010 - sau: 2 0 2 0 0 (1*3+1*7=2*5=10); (ambele soluµii sunt co-
10 3 31110 recte!)
41 11 - num rul maxim de sipete este 3; 1 sipet cu 11, unul cu 13 ³i
unul cu 17 monede.
Timp maxim de executare/test: 1.5 secunde
Memorie: total 128 MB
Dimensiune maxim  a sursei: 10 KB

25.6.1 Indicaµii de rezolvare

prof. Budai István - Lic. Teor. Nagy Mózes Târgu Secuiesc

Soluµia O n3  - 60 puncte - prof. Claudiu Cristian Gorea - C. N. Al. Papiu Ilarian


Târgu-Mure³
Pentru ecare pereche N p1, aplic m urm torul algoritm:
Asociem problemei urm toarea ecuaµie:
x p1  y p2  z p3  r N
Se caut  prima soluµie cu restul r cel mai mic din intervalul [0..p1-1].
în cadrul acestei soluµii x trebuie s  e cât mai mare.
Valoarea N  r  x ˜ p1 va  distribuit  în y gr mezi cu p2 monede.
Valoarea N  r  x ˜ p1  y ˜ p2 va  distribuit  în z gr mezi cu p3 monede.
Parcurgând descresc tor valorile posibile pentru x ³i y , vom obµine o sum  cu valoare maxim .
Ne oprim la soluµia în care este îndeplinit  condiµia: N  r  x ˜ p1  y ˜ p2  z ˜ p3 0.
în funcµie de algoritmul de testare/generare a valorilor prime p2 ³i p3, dar ³i a utiliz rii eciente
a structurilor repetitive, se pot obµine maxim 60 puncte.
Solutie O N  - 100p stud. Popescu Silviu-Emil, Univ. Politehnic  Bucure³ti
Se observ  ca problema se reduce la rezolvarea egalit µii:
a ˜ p1  b ˜ p2  c ˜ p3 N  p, cu a, b, c, p naturale.
Vom folosi o strategie de tip Meet In The Middle astfel:
Rescriem ecuaµia în forma: a ˜ p1  b ˜ p2 N  c ˜ p3  p
Aplic m urm toarea strategie:
1) calcul m toate valorile posibile ale expresiei: a ˜ p1  b ˜ p2
2) calcul m toate valorile posibile ale expresiei: N  c ˜ p3  p
3) veric 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  O N ©p1 ˜ p1 O N 
Num rul de valori posibile din a doua expresie va  O p1 ˜ p1 O N 
Specic m c  vericarea egalit µii se va face folosind un vector de apariµii în O 1
CAPITOLUL 25. ONI 2015 498

25.6.2 Cod surs 

Listing 25.6.1: sipet.cpp


1 /**
2 * Problema: Sipet O( N )
3 * Stud. Popescu Silviu Emil
4 * Automatica si Calculatoare
5 * Universitatea Politehnica Bucuresti
6 */
7
8 #include <stdio.h>
9 #include <assert.h>
10 #define NMax 10000010
11
12 const char IN[] = "sipet.in", OUT[] = "sipet.out";
13
14 struct pereche
15 {
16 int a, b;
17 };
18
19 int Tes, N, L, p[4];
20 int A, B, C, R;
21 pereche v[NMax];
22 bool bb[NMax];
23
24 bool isPrime( int x )
25 {
26
27 for ( int d = 2; d * d <= x; ++ d )
28 if ( x % d == 0 )
29 return false;
30 return true;
31
32 }
33
34 int sqr( int x )
35 {
36 return x * x;
37 }
38
39 int main()
40 {
41
42 freopen(IN, "r", stdin);
43 freopen(OUT, "w", stdout);
44
45 scanf("%d", &Tes);
46
47 while ( Tes -- )
48 {
49 scanf("%d%d", &N, &p[1]);
50
51 assert(isPrime(p[1]));
52 for ( int i = 2; i <= 3; ++ i )
53 for ( p[i] = p[i - 1] + 1; !isPrime(p[i]); ++ p[i] );
54
55 // fprintf(stderr, "%d %d %d\n", p[1], p[2], p[3]);
56 for ( int a = 0; a * p[1] <= N; ++ a )
57 for ( int b = 0; a * p[1] + b * p[2] <= N && b < p[1]; ++ b )
58 {
59 int pos = a * p[1] + b * p[2];
60 v[pos].a = a;
61 v[pos].b = b;
62 bb[pos] = true;
63 }
64
65 bool found = false; R = -1;
66 for ( int r = 0; r <= p[1] && R == -1; ++ r )
67 for ( int c = 0; r + c * p[3] <= N && c <= p[1]; ++ c )
68 {
69 int pos = c * p[3] + r;
70 if ( bb[N - pos] &&
CAPITOLUL 25. ONI 2015 499

71 (R == -1 || R != -1 &&
72 A + B + C < v[N - pos].a + v[N - pos].b + c ))
73 {
74 found = true;
75 A = v[N - pos].a;
76 B = v[N - pos].b;
77 C = c;
78 R = r;
79 }
80 }
81
82 for ( int a = 0; a * p[1] <= N; ++ a )
83 for ( int b = 0; a * p[1] + b * p[2] <= N && b < p[1]; ++ b )
84 {
85 int pos = a * p[1] + b * p[2];
86 bb[pos] = false;
87 }
88
89 printf("%d %d %d %d %d\n", A + B + C, A, B, C, R);
90
91 }
92
93 return 0;
94 }

25.6.3 *Rezolvare detaliat 


Capitolul 26

ONI 2014

26.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, cartograi au constatat c  toate cele k cl diri au
forma unor dreptunghiuri distincte. Imaginea poate  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.
Cartograi sunt interesaµi ca pe aceast  hart  s  e 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 codic  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 codic  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  ecare 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 ecare 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 ecare 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 ³ierul de
ie³ire harta.out se vor scrie cele dou  numere S ³i C având semnicaµ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, ³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 ecare 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.

500
CAPITOLUL 26. ONI 2014 501

Restricµii ³i preciz ri
a3 & n, m & 1500
a1 & i1 & i2 & n
a 1 & j1 & j2 & m
a 1 & k & 1000
a 1 & Lmax & 50 (Lmax - latura maxim  a unui dreptunghi)
a Se garanteaz  c  exist  soluµie pentru ambele cerinµe, pentru toate datele de test.
a Pentru rezolvarea corect  a primei cerinµe se acord  20 de puncte, iar pentru cerinµa a doua
se acord  80 de puncte.

Exemple:

harta.in harta.out Explicaµii


1 16 2 Atenµie! Pentru acest test se rezolv  doar cerinµa 1.
7 7 4 Cl direa de coordonate 1 1 4 4
1 1 4 4 este cel mai mare p trat ³i ocup 
6 2 6 4 S = 4 x 4 = 16 celule.
3 6 3 6 Cl dirile de coordonate 3 6 3 6 ³i 6 6 7 7
6 6 7 7 încap în interiorul cl dirii 1 1 4 4
f r  s  se suprapun 
peste celulele sale marginale.
Deci C = 2.
2 01110000 Atenµie! Pentru acest test se rezolv  doar cerinµa 2.
10 11 4 01010010
1244 01010010
8989 01110000
7395 00000000
2939 00111000
00101010
00111000
00000000

Timp maxim de executare/test: 1.0 secunde


Memorie: total 16 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 10 KB

26.1.1 Indicaµii de rezolvare

prof. Constantin G l µan - C. N. Liviu Rebreanu Bistriµa

Cerinµa a) - 20 puncte
Se determin  latura Lmax a celei mai mari cl diri p tratice. Apoi, pentru ecare 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 26. ONI 2014 502

Se reµin la fel ca în soluµia anterioar  numerele de ordine ale linilor ³i coloanelor care trebuie
³terse. Pentru ecare 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 a³are.
Pentru a evita operaµiile de ³tergere de linii/coloane se marcheaz  liniile/coloanele care trebuie
sa e ³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 a³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 ecare 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 ecare valoare din ³irul xa, se caut  binar
poziµia i a acesteia în ³irul x, iar pentru ecare 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 .

26.1.2 Cod surs 

Listing 26.1.1: harta.cpp


1 /* 100 pucte
2 Constantin Galatan
3 */
4
5 #include <fstream>
6 #include <vector>
7 #include <algorithm>
8
9 using namespace std;
10
11 #define pb push_back
12 #define DIM 1501
13
14 typedef vector<int> VI;
15
16 ifstream fin("harta.in");
17 ofstream fout("harta.out");
18
19 struct Dr
20 {
21 int i1, j1, i2, j2;
22 Dr(int _i1, int _j1, int _i2, int _j2)
23 : i1(_i1), j1(_j1), i2(_i2), j2(_j2) {
24 }
25 };
26
27 vector<Dr> d;
28
29 int imax, jmax;
30 VI xa, ya, x, y, c1, c2, b1, b2;
31 int k, N, M, n, m, Lmax, L, H, T, nr_dr;
32
33 bool A[DIM][DIM], s1[DIM], s2[DIM];
34
35 void WriteMatr(bool a[][DIM], int N, int M);
36 int GetPos(VI, int val);
37
CAPITOLUL 26. ONI 2014 503

38 int main()
39 {
40 fin >> T >> N >> M >> k;
41 int i1, j1, i2, j2;
42 x.pb(0), y.pb(0);
43 s1[0] = true;
44 s2[0] = true;
45 for ( int i = 0; i < k; ++i )
46 {
47 fin >> i1 >> j1 >> i2 >> j2;
48 d.pb(Dr(i1, j1, i2, j2));
49 L = i2 - i1 + 1, H = j2 - j1 + 1;
50 if ( L == H && L > Lmax )
51 Lmax = L;
52
53 for ( int i = i1; i <= i2; ++i)
54 {
55 xa.pb(i), ya.pb(j1), xa.pb(i), ya.pb(j2);
56 if ( !s1[i] )
57 x.pb(i), s1[i] = true;
58 }
59
60 for ( int j = j1; j <= j2; ++j )
61 {
62 xa.pb(i1), ya.pb(j);
63 xa.pb(i2), ya.pb(j);
64 if ( !s2[j] )
65 y.pb(j), s2[j] = true;
66 }
67
68 if ( !s1[i1 - 1] )
69 x.pb(i1 - 1), s1[i1 - 1] = true;
70 if ( !s2[j1 - 1] )
71 y.pb(j1 - 1), s2[j1 - 1] = true;
72 imax = max(imax, i2);
73 jmax = max(jmax, j2);
74 }
75
76 if ( T == 1 )
77 {
78 for ( int i = 0; i < k; ++i )
79 {
80 L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
81 if ( L < Lmax - 1 && H < Lmax - 1 )
82 nr_dr++;
83 }
84 fout << Lmax * Lmax << ’ ’ << nr_dr << ’\n’;
85 }
86 else
87 {
88 c1 = VI(imax + 1);
89 c2 = VI(jmax + 1);
90
91 for (int i = 0; i < x.size(); ++i )
92 c1[x[i]]++;
93 for (int i = 0; i < y.size(); ++i )
94 c2[y[i]]++;
95
96 for (int i = 1; i <= imax; ++i )
97 c1[i] += c1[i - 1];
98 for (int i = 1; i <= jmax; ++i )
99 c2[i] += c2[i - 1];
100
101 b1 = VI(x.size()); // aici - x sortat
102 b2 = VI(y.size());
103
104 for ( int i = 0; i < x.size(); ++i )
105 b1[c1[x[i]] - 1] = x[i], c1[x[i]]--;
106
107 for ( int i = 0; i < y.size(); ++i )
108 b2[c2[y[i]] - 1] = y[i], c2[y[i]]--;
109
110 n = 0, m = 0; int i, j;
111 for (size_t k = 0; k < xa.size(); ++k )
112 {
113 i= GetPos(b1, xa[k]);
CAPITOLUL 26. ONI 2014 504

114 j = GetPos(b2, ya[k]);


115 n = max(n, i), m = max(m, j);
116 A[i][j] = 1;
117 }
118
119 if ( n < M ) n++;
120 if ( m < M ) m++;
121 WriteMatr(A, n, m);
122 }
123
124 fin.close();
125 fout.close();
126 return 0;
127 }
128
129
130 int GetPos(VI v, int val)
131 {
132 int i, p2, n = v.size();
133 int lo = 0, hi = n, mid;
134
135 while ( lo <= hi )
136 {
137 mid = lo + (hi - lo) / 2;
138 if ( v[mid] == val )
139 return mid;
140 if ( val < v[mid] )
141 hi = mid - 1;
142 else
143 lo = mid + 1;
144 }
145
146 return 0;
147 }
148
149 void WriteMatr(bool a[][DIM], int N, int M)
150 {
151 for ( int i = 1; i <= N; ++i )
152 {
153 for ( int j = 1; j <= M; ++j )
154 fout << a[i][j] << ’ ’;
155 fout << ’\n’;
156 }
157 }

Listing 26.1.2: harta_Adriana.cpp


1 //100 puncte
2 // prof. Adriana Simulescu
3
4 # include <fstream>
5 # include <cstdio>
6
7 using namespace std;
8
9 int n,m,Lmax,p,k,S,s,C;
10 int x[1501],linii[1501],coloane[1501];
11
12 FILE *fout = fopen("harta.out","wt");
13
14 struct dreptunghi
15 {
16 int i1,i2,j1,j2;
17 };
18
19 dreptunghi d[1001],dmax,d2[1001],d1[1001];
20
21 void read ()
22 {
23 FILE *fin=fopen("harta.in","rt");
24 fscanf(fin,"%d%d%d%d",&p,&n,&m,&k);
25 for(int i=1;i<=k;++i)
26 {
27 fscanf(fin,"%d%d%d%d",&d[i].i1,&d[i].j1,&d[i].i2,&d[i].j2);
28 d1[i]=d2[i]=d[i];
CAPITOLUL 26. ONI 2014 505

29 }
30 fclose(fin);
31 }
32
33 void solve()
34 {
35 int a,b;
36 if(p==1)
37 {
38 for(int i=1;i<=k;i++)
39 {
40 a=(d[i].i2-d[i].i1+1);
41 b=(d[i].j2-d[i].j1+1);
42 if(a==b)
43 {
44 s=a*b;
45 if(s>S)
46 {
47 S=s;
48 dmax=d[i];
49 }
50 }
51 }
52
53 for(int i=1;i<=k;i++)
54 {
55 if(d[i].i2-d[i].i1+1<=dmax.i2-dmax.i1-1&&
56 d[i].j2-d[i].j1+1<=dmax.j2-dmax.j1-1)
57 C++;
58 }
59 fprintf(fout,"%d %d\n",S,C);
60
61 }
62 else
63 {
64 int i,j,di=0,dj=0,ix=1,iy,xx=1,yy=1;
65 dreptunghi aux;
66 for(i=1;i<k;i++)
67 for(j=i+1;j<=k;j++)
68 {
69 if(d[i].i1>d[j].i1)
70 {
71 aux=d[i];
72 d[i]=d[j];
73 d[j]=aux;
74 }
75
76 if(d2[i].i2>d2[j].i2)
77 {
78 aux=d2[i];
79 d2[i]=d2[j];
80 d2[j]=aux;
81 }
82
83 if(d1[i].j1>d1[j].j1)
84 {
85 aux=d1[i];
86 d1[i]=d1[j];
87 d1[j]=aux;
88 }
89 }
90
91 for(i=1;i<=k;i++)
92 {
93 if(d[i].i1-2>=xx)
94 for(int ix=xx;ix<=d[i].i1-2;ix++)
95 linii[ix]=1;
96
97 if(d[i].i2>=xx)
98 xx=d[i].i2+1;
99
100 if(d1[i].j1-2>=yy)
101 for(int iy=yy;iy<=d1[i].j1-2;iy++)
102 coloane[iy]=1;
103
104 if(d1[i].j2>=yy)
CAPITOLUL 26. ONI 2014 506

105 yy=d1[i].j2+1;
106 }
107
108 if(n-xx>=1)
109 for(int ix=xx;ix<=n-1;ix++)
110 linii[ix]=1;
111
112 if(m-yy>=1)
113 for(int iy=yy;iy<=m-1;iy++)
114 coloane[iy]=1;
115
116 int ii=1,iii=1;
117 i=1;
118 while(i<=n)
119 {
120 if(!linii[i])
121 {
122 while(d[ii].i1==i&&ii<=k)
123 {
124 x[d[ii].j1]=x[d[ii].j2]=d[ii].i2-d[ii].i1+1;
125
126 for(int jj=d[ii].j1+1;jj<d[ii].j2;jj++)
127 x[jj]=1;
128 ii++;
129 }
130
131 while(d2[iii].i2==i&&iii<=k)
132 {
133 for(int jj=d2[iii].j1+1;jj<d2[iii].j2;jj++)
134 x[jj]=1;
135 iii++;
136 }
137
138 for(j=1;j<=m;j++)
139 if(!coloane[j])
140 if(!x[j])
141 fprintf(fout,"0 ");
142 else
143 {
144 fprintf(fout,"1 ");
145 x[j]--;
146 }
147 fprintf(fout,"\n");
148 }
149
150 i++;
151 }
152 }
153 }
154
155 int main()
156 {
157 read();
158 solve();
159 fclose(fout);
160 return 0;
161 }

Listing 26.1.3: harta_brut_1.cpp


1 /* 50 puncte
2 Constantin Galatan
3 */
4 #include <fstream>
5 #include <algorithm>
6
7 using namespace std;
8
9 #define DIM 1501
10 ifstream fin("harta.in");
11 ofstream fout("harta.out");
12
13 struct Drept
14 {
15 int i1, j1, i2, j2;
CAPITOLUL 26. ONI 2014 507

16 } d[DIM];
17
18 int sc[DIM], sl[DIM], a[DIM][DIM];
19 int k, N, M, Lmax, L, H, T, nr_dr;
20
21 void WriteMatr(int a[][DIM], int N, int M);
22 void DeleteLine(int L);
23 void DeleteColumn(int C);
24
25 int main()
26 {
27 fin >> T >> N >> M >> k;
28 int i1, j1, i2, j2;
29
30 for ( int i = 0; i < k; ++i )
31 {
32 fin >> i1 >> j1 >> i2 >> j2;
33 d[i].i1 = i1, d[i].j1 = j1, d[i].i2 = i2, d[i].j2 = j2;
34 L = i2 - i1 + 1, H = j2 - j1 + 1;
35
36 if ( L == H && L > Lmax )
37 Lmax = L;
38
39 for ( int i = i1; i <= i2; ++i)
40 a[i][j1] = a[i][j2] = 1, sl[i] += 2;
41
42 for ( int j = j1; j <= j2; ++j )
43 a[i1][j] = a[i2][j] = 1, sc[j] += 2;
44 }
45
46 if ( T == 1 )
47 {
48 for ( int i = 0; i < k; ++i )
49 {
50 L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
51 if ( L < Lmax - 1 && H < Lmax - 1 )
52 nr_dr++;
53 }
54
55 fout << Lmax * Lmax << ’ ’ << nr_dr << ’\n’;
56 }
57 else
58 {
59 for ( int col = 1; col <= M; ++col ) // pt fiec coloana O(M)
60 {
61 int k = 0;
62 while ( col + k <= M && !sc[col + k] )
63 k++;
64
65 if ( k > 1 )
66 {
67 for ( int j = col + 1; j < col + k; ++j )
68 a[1][j] = 2;
69
70 col += k - 1;
71 }
72 }
73
74 for ( int j = 1; j <= M; ++j )
75 if ( a[1][j] == 2 )
76 DeleteColumn(j), --j;
77
78 for ( int lin = 1; lin <= N; ++lin ) // pt fiec linie O(N)
79 {
80 int k = 0;
81 while ( lin + k <= N && !sl[lin + k] )
82 k++;
83
84 if ( k > 1 )
85 {
86 for ( int i = lin + 1; i < lin + k; ++i )
87 a[i][1] = 2;
88
89 lin += k - 1;
90 }
91 }
CAPITOLUL 26. ONI 2014 508

92
93 for ( int i = 1; i <= N; ++i )
94 if ( a[i][1] == 2 )
95 DeleteLine(i), --i;
96
97 WriteMatr(a, N, M);
98 }
99
100 fin.close();
101 fout.close();
102 return 0;
103 }
104
105 void DeleteLine(int L)
106 {
107 for ( int j = 1; j <= M; ++j )
108 for ( int i = L; i < N; ++i )
109 a[i][j] = a[i + 1][j];
110 N--;
111 }
112
113
114 void DeleteColumn(int C)
115 {
116 for ( int i = 1; i <= N; ++i )
117 for ( int j = C; j < M; ++j )
118 a[i][j] = a[i][j + 1];
119 M--;
120 }
121
122 void WriteMatr(int a[][DIM], int N, int M)
123 {
124 for ( int i = 1; i <= N; ++i )
125 {
126 for ( int j = 1; j <= M; ++j )
127 fout << a[i][j] << ’ ’;
128 fout << ’\n’;
129 }
130 }

Listing 26.1.4: harta_brut_2.cpp


1 /* 75 puncte
2 Constantin Galatan
3 */
4 #include <iostream>
5 #include <fstream>
6 #include <algorithm>
7
8 using namespace std;
9
10 #define DIM 1501
11
12 ifstream fin("harta.in");
13 ofstream fout("harta.out");
14
15 struct Drept
16 {
17 int i1, j1, i2, j2;
18 } d[DIM];
19
20
21 int sc[DIM], sl[DIM], a[DIM][DIM];
22 int k, N, M, Lmax, L, H, T, nr_dr;
23
24 void WriteMatr(int a[][DIM], int N, int M);
25 void DeleteKLines(int L, int k);
26 void DeleteKColumns(int C, int k);
27
28 int main()
29 {
30 fin >> T >> N >> M >> k;
31 int i1, j1, i2, j2;
32
33 for ( int i = 0; i < k; ++i )
CAPITOLUL 26. ONI 2014 509

34 {
35 fin >> i1 >> j1 >> i2 >> j2;
36 d[i].i1 = i1, d[i].j1 = j1, d[i].i2 = i2, d[i].j2 = j2;
37 L = i2 - i1 + 1, H = j2 - j1 + 1;
38 if ( L == H && L > Lmax )
39 Lmax = L;
40
41 for ( int i = i1; i <= i2; ++i)
42 a[i][j1] = a[i][j2] = 1, sl[i] += 2;
43
44 for ( int j = j1; j <= j2; ++j )
45 a[i1][j] = a[i2][j] = 1, sc[j] += 2;
46 }
47
48 if ( T == 1 )
49 {
50 for ( int i = 0; i < k; ++i )
51 {
52 L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
53 if ( L < Lmax - 1 && H < Lmax - 1 )
54 nr_dr++;
55 }
56
57 fout << Lmax * Lmax << ’ ’ << nr_dr << ’\n’;
58 }
59 else
60 {
61 for ( int col = 1; col <= M; ++col ) // pt fiec coloana O(M)
62 {
63 int k = 0;
64 while ( col + k <= M && !sc[col + k] )
65 k++;
66
67 if ( k > 1 )
68 {
69 for ( int j = col + 1; j < col + k; ++j )
70 a[1][j] = 2;
71
72 col += k - 1;
73 }
74 }
75
76 for ( int j = 1; j <= M; ++j )
77 {
78 int k = 0;
79 while ( j + k <= M && a[1][j + k] == 2 )
80 ++k;
81
82 if ( k ) DeleteKColumns(j, k), --j;
83 }
84
85 for ( int lin = 1; lin <= N; ++lin ) // pt fiec linie O(N)
86 {
87 int k = 0;
88 while ( lin + k <= N && !sl[lin + k] )
89 k++;
90
91 if ( k > 1 )
92 {
93 for ( int i = lin + 1; i < lin + k; ++i )
94 a[i][1] = 2;
95 lin += k - 1;
96 }
97 }
98
99 for ( int i = 1; i <= N; ++i )
100 {
101 int k = 0;
102 while ( i + k <= N && a[i + k][1] == 2 )
103 ++k;
104
105 if ( k ) DeleteKLines(i, k), --i;
106 }
107
108 WriteMatr(a, N, M);
109 }
CAPITOLUL 26. ONI 2014 510

110
111 fin.close();
112 fout.close();
113 return 0;
114 }
115
116 void DeleteKLines(int L, int k)
117 {
118 for ( int j = 1; j <= M; ++j )
119 for ( int i = L; i + k <= N; ++i )
120 a[i][j] = a[i + k][j];
121
122 N -= k;
123 }
124
125 void DeleteKColumns(int C, int k)
126 {
127 for ( int i = 1; i <= N; ++i )
128 for ( int j = C; j + k <= M; ++j )
129 a[i][j] = a[i][j + k];
130
131 M -= k;
132 }
133
134 void WriteMatr(int a[][DIM], int N, int M)
135 {
136 for ( int i = 1; i <= N; ++i )
137 {
138 for ( int j = 1; j <= M; ++j )
139 fout << a[i][j] << ’ ’;
140
141 fout << ’\n’;
142 }
143 }

Listing 26.1.5: harta_eugen.cpp


1 /* 100 puncte
2 prof. Eugen Nodea
3 */
4 # include <cstdio>
5
6 using namespace std;
7
8 struct cladire
9 {
10 short x1, y1, x2, y2;
11 int l, L;
12 } C[1002];
13
14 short n, m, k;
15 bool A[1502][1502];
16 short l[1502], c[1502];
17
18 int main()
19 {
20 int i, j, p, Max, a, nr;
21 freopen("harta.in", "r", stdin);
22 freopen("harta.out","w", stdout);
23 scanf("%d", &p);
24 scanf("%hd %hd %hd", &n, &m, &k);
25 for (i=1; i<=k; ++i)
26 scanf("%hd %hd %hd %hd", &C[i].x1, &C[i].y1, &C[i].x2, &C[i].y2);
27
28 if (p == 1)
29 {
30 Max = 0;
31 for (i=1; i<=k; ++i)
32 {
33 C[i].l = C[i].x2 - C[i].x1 + 1;
34 C[i].L = C[i].y2 - C[i].y1 + 1;
35 if (C[i].l == C[i].L)
36 if (C[i].l > Max)
37 Max = C[i].l;
38 }
CAPITOLUL 26. ONI 2014 511

39
40 a = (Max - 1) * (Max - 1);
41 nr = 0;
42 for (i=1; i<=k; ++i)
43 if (C[i].l * C[i].L <= a &&
44 C[i].l < Max - 1 &&
45 C[i].L < Max - 1)
46 ++nr;
47
48 printf("%d %d\n", Max * Max, nr);
49 }
50 else
51 {
52 for (; k>0; --k)
53 {
54 for (i=C[k].x1; i<=C[k].x2; ++i)
55 {
56 A[i][C[k].y1] = A[i][C[k].y2] = 1;
57 l[i] = 1;
58 }
59
60 for (i=C[k].y1; i<=C[k].y2; ++i)
61 {
62 A[C[k].x1][i] = A[C[k].x2][i] = 1;
63 c[i] = 1;
64 }
65 }
66
67 for (i=1; i<=m; ++i)
68 if (c[i] == 0)
69 {
70 j = i + 1;
71 while (c[j] == 0 && j <= m)
72 c[j++] = 2;
73 }
74
75 for (i=1; i<=n; ++i)
76 if (l[i] == 0)
77 {
78 j = i + 1;
79 while (l[j] == 0 && j <= n)
80 l[j++] = 2;
81 }
82
83 for (i=1; i<=n; ++i)
84 if (l[i] < 2)
85 {
86 for (j=1; j<=m; ++j)
87 if (c[j] < 2) printf("%d ", (int) A[i][j]);
88 printf("\n");
89 }
90 }
91
92 return 0;
93 }

Listing 26.1.6: harta_pit.cpp


1 /* 100 puncte
2 prof. Ionel-Vasile Pit-Rada
3 */
4 #include<fstream>
5
6 using namespace std;
7
8 ifstream fin("harta.in");
9 ofstream fout("harta.out");
10
11 int p,n,m,k;
12 int lung,lat,lx;
13 int patrate[52],lmax;
14 char a[1502][1502];
15 int i1,j1,i2,j2,i;
16 int l1,c1;
17 int S,C;
CAPITOLUL 26. ONI 2014 512

18
19 int main()
20 {
21 fin>>p>>n>>m>>k;
22 if(p==1)
23 {
24 for(i=1;i<=k;i++)
25 {
26 fin>>i1>>j1>>i2>>j2;
27 lat=i2-i1+1;
28 lung=j2-j1+1;
29 lx=lat;
30 if(lung>lx)lx=lung;
31 patrate[lx]++;
32 if(lat==lung)
33 if(lat>lmax)
34 lmax=lat;
35 }
36
37 S=lmax*lmax;
38 C=0;
39 for(i=1;i<=lmax-2;i++)
40 C=C+patrate[i];
41
42 fout<<S<<" "<<C;
43 }
44 else
45 {
46 for(i=1;i<=k;i++)
47 {
48 fin>>i1>>j1>>i2>>j2;
49 for(l1=i1;l1<=i2;l1++)
50 {
51 a[l1][j1]=1;
52 a[l1][j2]=1;
53 a[l1][0]=1;//marcat linie care ramane
54 }
55
56 for(c1=j1;c1<=j2;c1++)
57 {
58 a[i1][c1]=1;
59 a[i2][c1]=1;
60 a[0][c1]=1;//marcat coloana care ramane
61 }
62 }
63
64 for(l1=1;l1<=n-1;l1++)
65 if(a[l1][0]==0 && a[l1+1][0]==0)
66 a[l1][0]=2;//linia l1 se va sterge
67
68 for(c1=1;c1<=m-1;c1++)
69 if(a[0][c1]==0 && a[0][c1+1]==0)
70 a[0][c1]=2;//coloana c1 se va sterge
71
72 //vom pastra doar liniile si coloanele marcate diferit de 2
73 for (l1=1;l1<=n;l1++)
74 {
75 if(a[l1][0]!=2)
76 {
77 for(c1=1;c1<=m;c1++)
78 if(a[0][c1]!=2)
79 fout<<(int)a[l1][c1]<<" ";
80
81 fout<<"\n";
82 }
83 }
84 }
85
86 fout.close();
87 fin.close();
88 return 0;
89 }

Listing 26.1.7: harta1.cpp


CAPITOLUL 26. ONI 2014 513

1 /* 100 puncte
2 Constantin Galatan
3 */
4 #include <fstream>
5 #include <vector>
6 #include <algorithm>
7
8 using namespace std;
9
10 #define pb push_back
11 #define DIM 1501
12
13 ifstream fin("harta.in");
14 ofstream fout("harta.out");
15
16 struct Dr
17 {
18 int i1, j1, i2, j2;
19 Dr(int _i1, int _j1, int _i2, int _j2)
20 : i1(_i1), j1(_j1), i2(_i2), j2(_j2) {
21 }
22 };
23
24 typedef vector<int> VI;
25
26 VI xa, ya, x, y;
27 vector<Dr> d;
28 int imax, jmax;
29 int T, N, M, n, m, k, L, H, Lmax;
30 bool A[DIM][DIM], s1[DIM], s2[DIM];
31
32 void One(), Two(), WriteMatr(bool a[][DIM], int N, int M);
33
34 int main()
35 {
36 fin >> T >> N >> M >> k;
37 int i1, j1, i2, j2;
38 x.pb(0), y.pb(0);
39 s1[0] = true, s2[0] = true;
40 for ( int i = 0; i < k; ++i )
41 {
42 fin >> i1 >> j1 >> i2 >> j2;
43 d.pb(Dr(i1, j1, i2, j2));
44 L = i2 - i1 + 1, H = j2 - j1 + 1;
45 if ( L == H && L > Lmax )
46 Lmax = L;
47
48 for ( int i = i1; i <= i2; ++i)
49 {
50 xa.pb(i), ya.pb(j1), xa.pb(i), ya.pb(j2);
51 if ( !s1[i] ) x.pb(i), s1[i] = true;
52 }
53
54 for ( int j = j1; j <= j2; ++j )
55 {
56 xa.pb(i1), ya.pb(j); xa.pb(i2), ya.pb(j);
57 if ( !s2[j] ) y.pb(j), s2[j] = true;
58 }
59
60 if ( !s1[i1 - 1] )
61 x.pb(i1 - 1), s1[i1 - 1] = true;
62 if ( !s2[j1 - 1] )
63 y.pb(j1 - 1), s2[j1 - 1] = true;
64 imax = max(imax, i2); jmax = max(jmax, j2);
65 }
66
67 if ( T == 1 )
68 One();
69 else
70 Two();
71
72 fin.close();
73 fout.close();
74 return 0;
75 }
76
CAPITOLUL 26. ONI 2014 514

77 void One()
78 {
79 int nr_dr(0);
80 for ( int i = 0; i < k; ++i )
81 {
82 L = d[i].i2 - d[i].i1 + 1, H = d[i].j2 - d[i].j1 + 1;
83 if ( L < Lmax - 1 && H < Lmax - 1)
84 nr_dr++;
85 }
86 fout << Lmax * Lmax << ’ ’ << nr_dr << ’\n’;
87 }
88
89 void Two()
90 {
91 sort(x.begin(), x.end());
92 sort(y.begin(), y.end());
93
94 x.erase(unique(x.begin(), x.end()), x.end());
95 x.erase(unique(x.begin(), x.end()), x.end());
96 y.erase(unique(y.begin(), y.end()), y.end());
97
98 int i, j;
99 for (size_t k = 0; k < xa.size(); ++k )
100 {
101 i = lower_bound(x.begin(), x.end(), xa[k]) - x.begin();
102 j = lower_bound(y.begin(), y.end(), ya[k]) - y.begin();
103 n = max(n, i), m = max(m, j);
104 A[i][j] = 1;
105 }
106
107 if ( n < M ) n++;
108 if ( m < M ) m++;
109 WriteMatr(A, n, m);
110 }
111
112 void WriteMatr(bool a[][DIM], int N, int M)
113 {
114 for ( int i = 1; i <= N; ++i )
115 {
116 for ( int j = 1; j <= M; ++j )
117 fout << a[i][j] << ’ ’;
118 fout << ’\n’;
119 }
120 }

26.1.3 *Rezolvare detaliat 

26.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, ecare
vector având un num r precizat de elemente.

Cerinµe
S  se r spund  la Q întreb ri de tipul:
a) 1 i j
cu semnicaµia: care este minimul dintre modulele diferenµelor oric ror dou  elemente, pri-
mul element aparµinând vectorului numerotat cu i, iar cel de al doilea element aparµinând
vectorului numerotat cu j ?
b) 2 i j
cu semnicaµia: care este valoarea ce se g se³te pe poziµia median  în vectorul obµinut prin
interclasarea vectorilor având numerele de ordine i, i  1, ...,j (i $ j ).

Date de intrare
CAPITOLUL 26. ONI 2014 515

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 ecare 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 ecare 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 semnicaµ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 aat  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
3 3 13 Prima întrebare este de tipul 2. Vectorul nou obµinut prin
7 1 4 5 8 11 18 19 3 interclasarea vectorilor numerotaµi cu 2 ³i cu 3 este urm -
6 2 4 5 10 21 29 10 torul: 2, 4, 5, 10, 13, 14, 15, 15, 21, 29 ³i conµine 6+4=10
4 13 14 15 15 elemente, valoarea elementului median este 13.
2 23 A doua întrebare este de tipul 1. Diferenµa minim  se
1 23 obµine pentru perechea (10,13), unde valoarea 10 aparµine
2 13 vectorului numerotat cu 2, iar valoarea 13 aparµine vecto-
rului numerotat cu 3.
A treia întrebare este de tipul 2. Poziµia median  în vecto-
rul 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

26.2.1 Indicaµii de rezolvare

prof. Eugen Nodea - C. N. Tudor Vladimirescu Tg. Jiu


Aparent este o problema de implementare (oare?).
Rezolvarea intreb rilor de tip 1:
De³i pare o problem  de c utare, rezolvarea optim  este asem n toare unui algoritm de inter-
clasare:

subrutina q1(int a[], int b[], int n, int m)


{
i = 1, j = 1 , Min = inf;
Cat timp (i<=n && j <=m)
{
Min = minim(abs(a[i]-b[j]), Min)
i++, j++;
}
return Min
}
CAPITOLUL 26. ONI 2014 516

Rezolvarea întreb rilor tipului 2:


De³i pare o problema de interclasare a mai mulµi vectori, totu³i vom folosi c utarea binar .
Astfel, vom c uta ³i num ra în toµi vectorii cuprin³i între i ³i j câte valori sunt mai mici sau
egale cu valoarea c utat  x (valoarea aat  pe poziµia median ).
O soluµie brute-force obµine 20 p.

26.2.2 Cod surs 

Listing 26.2.1: qvect_eugen_bf.cpp


1 # include <fstream>
2 # include <algorithm>
3 # include <cmath>
4
5 # define inf 1000000001
6
7 using namespace std;
8
9 ifstream f("qvect.in");
10 ofstream g("qvect.out");
11
12 int a[102][5002];
13 int z[500002];
14
15 int q1(int x, int y)
16 {
17 int i, j, n = a[x][0], m=a[y][0], z, Min;
18 Min = 2 * inf;
19 for (i=1; i<=n; ++i)
20 for (j=1; j<=m; ++j)
21 {
22 z = abs(a[x][i] - a[y][j]);
23 if (z < Min) Min = z;
24 }
25 return Min;
26 }
27
28 int q2(int x, int y)
29 {
30 int i, k = 0, nr=0;
31
32 for (k=x; k<=y; ++k)
33 for (i=1; i<=a[k][0]; ++i)
34 z[++nr] = a[k][i];
35
36 nth_element(z+1, z+(nr/2), z+nr+1);
37
38 return z[nr/2];
39 }
40 int main()
41 {
42 int n, q, op, i, j, x, y;
43
44 f >> n >> q;
45 for (i=1; i<=n; ++i)
46 {
47 f >> x;
48 a[i][0] = x;
49 for (j=1; j<=x; ++j)
50 f>> a[i][j];
51 }
52
53 while ( q-- )
54 {
55 f >> op >> x >> y;
56 switch (op)
57 {
58 case 1: {
59 g << q1(x, y) << "\n";
60 break;
61 };
62 case 2: {
CAPITOLUL 26. ONI 2014 517

63 g << q2(x, y) << "\n";


64 break;
65 };
66 }
67 }
68 return 0;
69 }

Listing 26.2.2: qvect_eugen_fs.cpp


1 # include <fstream>
2 # include <algorithm>
3 # define inf 1000000000
4
5 using namespace std;
6
7 ifstream f("qvect.in");
8 ofstream g("qvect.out");
9
10 int a[101][5001];
11 int nr[101];
12
13 int q1(int x, int y)
14 {
15 int i = 1, j = 1, n = a[x][0], m = a[y][0], Min = inf, z;
16 while ( i <= n && j <= m )
17 {
18 if (a[x][i] > a[y][j])
19 z = a[x][i] - a[y][j];
20 else
21 z = a[y][j] - a[x][i];
22
23 if (z == 0) return 0;
24 if (z < Min) Min = z;
25
26 if (a[x][i] < a[y][j])
27 ++i;
28 else
29 ++j;
30 }
31
32 return Min;
33 }
34
35 int q2(int x, int y, int p)
36 {
37 int i, j, s, d, val, nr, k, ok, nr0, rez, sol;
38 s = -inf, d = inf;
39 while ( s <= d )
40 {
41 val = (s + d) >> 1 ;
42 nr = 0;
43 ok = 0;
44 for (k = x; k<=y; ++k)
45 {
46 i = 1; j = a[k][0];
47 nr0 = upper_bound(a[k] + 1, a[k] + j + 1, val) - a[k] - 1;
48 if (a[k][nr0] == val)
49 {
50 sol = val; ok = 1;
51 }
52 nr += nr0;
53 }
54
55 if (nr >= p)
56 {
57 if (ok)
58 {
59 rez = sol;
60 if (nr == p) return sol;
61 }
62 d = val - 1;
63 }
64 else
65 s = val + 1;
CAPITOLUL 26. ONI 2014 518

66 }
67
68 return rez;
69 }
70
71 int main()
72 {
73 int n, q, op, i, j, x, y, m;
74
75 f >> n >> q;
76 for (i=1; i<=n; ++i)
77 {
78 f >> x;
79 a[i][0] = x;
80 nr[i] = nr[i-1] + x;
81 for (j=1; j<=x; ++j)
82 f>> a[i][j];
83 }
84
85 while ( q-- )
86 {
87 f >> op >> x >> y;
88 switch (op)
89 {
90 case 1:
91 {
92 g << q1(x, y) << "\n";
93 break;
94 };
95 case 2:
96 {
97 m = (nr[y] - nr[x-1]) / 2;
98 g << q2(x, y, m) << "\n";
99 break;
100 };
101 }
102 }
103
104 return 0;
105 }

Listing 26.2.3: qvect_eugen_std.cpp


1 # include <cstdio>
2 # include <algorithm>
3
4 # define inf 1000000000
5
6 using namespace std;
7
8 int a[102][5002];
9 int nr[102];
10
11 int q1(int x, int y)
12 {
13 int i = 1, j = 1, n = a[x][0], m = a[y][0], Min = 2*inf, z;
14 while ( i <= n && j <= m )
15 {
16 if (a[x][i] > a[y][j])
17 z = a[x][i] - a[y][j];
18 else
19 z = a[y][j] - a[x][i];
20
21 if (z == 0) return 0;
22 if (z < Min) Min = z;
23
24 if (a[x][i] < a[y][j])
25 ++i;
26 else
27 ++j;
28 }
29
30 return Min;
31 }
32
CAPITOLUL 26. ONI 2014 519

33 int q2(int x, int y, int p)


34 {
35 int i, j, s, d, val, nr, k, ok, nr0, rez, sol;
36 s = -inf, d = inf;
37 while ( s <= d )
38 {
39 val = (s + d) >> 1 ;
40 nr = 0; ok = 0;
41 for (k = x; k<=y; ++k)
42 {
43 i = 1; j = a[k][0];
44 nr0 = upper_bound(a[k] + 1, a[k] + j + 1, val) - a[k] - 1;
45 if (a[k][nr0] == val)
46 {
47 sol = val; ok = 1;
48 }
49
50 nr += nr0;
51 }
52
53 if (nr >= p)
54 {
55 if (ok)
56 {
57 rez = sol;
58 if (nr == p) return sol;
59 }
60 d = val - 1;
61 }
62 else
63 s = val + 1;
64 }
65
66 return rez;
67 }
68
69 int main()
70 {
71 int n, q, op, i, j, x, y, m;
72
73 freopen("qvect.in", "r", stdin);
74 freopen("qvect.out","w", stdout);
75
76 scanf("%d%d", &n, &q);
77 for (i=1; i<=n; ++i)
78 {
79 scanf("%d", &x);
80 a[i][0] = x;
81 nr[i] = nr[i-1] + x;
82 for (j=1; j<=x; ++j)
83 scanf("%d", &a[i][j]);
84 }
85
86 while ( q-- )
87 {
88 scanf("%d%d%d", &op, &x, &y);
89 switch (op)
90 {
91 case 1: {
92 printf("%d\n", q1(x, y));
93 break;
94 };
95 case 2: {
96 m = (nr[y] - nr[x-1]) >> 1;
97 printf("%d\n", q2(x, y, m));
98 break;
99 };
100 }
101 }
102
103 return 0;
104 }

Listing 26.2.4: qvect_inter.cpp


CAPITOLUL 26. ONI 2014 520

1 #include <algorithm>
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("qvect.in");
7 ofstream g("qvect.out");
8
9 int a[101][5005], poz[101], n, q,nr;
10 int b[250005],c[250005];
11
12 void citeste()
13 {
14 f>>n>>q;
15 for(int i=1;i<=n;i++)
16 {
17 f>>a[i][0];
18 for(int j=1;j<=a[i][0];j++)
19 f>>a[i][j]; poz[i]=1;
20 }
21 }
22
23 int abs(int x)
24 {
25 return max(x,-x);
26 }
27
28 int cerinta1(int n1, int n2)
29 {
30 int i=1,j=1,m=0,nr1,nr2, dif=1000000005,x,y;
31 nr1=a[n1][0]; nr2=a[n2][0];
32 while(i<=nr1 && j<=nr2)
33 {
34 if(a[n1][i]<a[n2][j])
35 {
36 x=a[n1][i];i++;
37 if(m==2)
38 dif=min(dif, abs(x-y));
39 m=1;
40 }
41 else
42 if(a[n1][i]>a[n2][j])
43 {
44 y=a[n2][j];j++;
45 if(m==1)
46 dif=min(dif, abs(x-y));
47 m=2;
48 }
49 else
50 if(a[n1][i]==a[n2][j])
51 return 0;
52 }
53
54 if(i<=nr1)
55 {
56 if(m==2)
57 dif=min(dif, abs(a[n1][i]-y));
58 }
59 else
60 {
61 if(m==1)
62 dif=min(dif, abs(x-a[n2][j]));
63 }
64
65 return dif;
66 }
67
68 int interc(int k, int p,int nr)
69 {
70 int i=1,j=1,n=0;
71 while(i<=k && j<=a[p][0] && n<nr)
72 if(b[i]<=a[p][j])
73 c[++n]=b[i++];
74 else
75 c[++n]=a[p][j++];
76
CAPITOLUL 26. ONI 2014 521

77 while(j<=a[p][0] && n<=nr)


78 c[++n]=a[p][j++];
79
80 while(i<=k && n<=nr)
81 c[++n]=b[i++];
82
83 for(i=1;i<=n;i++)
84 b[i]=c[i];
85
86 return n;
87 }
88
89 int cerinta2(int n1, int n2)
90 {
91 int i,j,nr=0,k;
92 for(i=n1;i<=n2;i++)nr+=a[i][0];
93 nr/=2;
94 k=min(nr,a[n1][0]);
95 for(i=1;i<=k;i++)
96 b[i]=a[n1][i];
97
98 for(i=n1+1;i<=n2;i++)
99 k=interc(k,i,nr);
100
101 return b[nr];
102 }
103
104 int main()
105 {
106 citeste();
107 int k,t,i,j;
108 for(k=1;k<=q;k++)
109 {
110 f>>t>>i>>j;
111 if(t==1)
112 g<<cerinta1(i,j)<<endl;
113 else
114 g<<cerinta2(i,j)<<endl;
115 }
116
117 return 0;
118 }

Listing 26.2.5: qvect_mink.cpp


1 #include <fstream>
2 using namespace std;
3
4 ifstream f("qvect.in");
5 ofstream g("qvect.out");
6
7 int a[101][5005], poz[101], n, q,nr;
8
9 void citeste()
10 {
11 f>>n>>q;
12 for(int i=1;i<=n;i++)
13 {
14 f>>a[i][0];
15 for(int j=1;j<=a[i][0];j++)
16 f>>a[i][j];
17
18 poz[i]=1;
19 }
20 }
21
22 int abs(int x)
23 {
24 return max(x,-x);
25 }
26
27 int cerinta1(int n1, int n2)
28 {
29 int i=1,j=1,m=0,nr1,nr2, dif=1000000005,x,y;
30 nr1=a[n1][0];
CAPITOLUL 26. ONI 2014 522

31 nr2=a[n2][0];
32 while(i<=nr1 && j<=nr2)
33 {
34 if(a[n1][i]<a[n2][j])
35 { x=a[n1][i];i++;
36 if(m==2)
37 dif=min(dif, abs(x-y));
38 m=1;
39 }
40 else
41 if(a[n1][i]>a[n2][j])
42 {
43 y=a[n2][j];j++;
44 if(m==1)
45 dif=min(dif, abs(x-y));
46 m=2;
47 }
48 else
49 if(a[n1][i]==a[n2][j])
50 return 0;
51 }
52
53 if(i<=nr1)
54 {
55 if(m==2)
56 dif=min(dif, abs(a[n1][i]-y));
57 }
58 else
59 {
60 if(m==1)
61 dif=min(dif, abs(x-a[n2][j]));
62 }
63
64 return dif;
65 }
66
67 int minim(int n1, int n2)
68 {
69 int p=0,i, mi=1000000005,j,k;
70
71 for(i=n1;i<=n2;i++)
72 {
73 j=poz[i];
74 if(j<=a[i][0] && a[i][j]<mi)
75 {
76 mi=a[i][j];
77 p=i;
78 }
79 }
80
81 poz[p]++;
82 return mi;
83 }
84
85 int cerinta2(int n1, int n2)
86 {
87 int i,med,nr=0;
88 for(i=1;i<=n;i++)
89 poz[i]=1;
90
91 for(i=n1;i<=n2;i++)
92 nr+=a[i][0];
93
94 nr/=2;
95 for(i=1;i<=nr;i++)
96 med=minim(n1,n2);
97
98 return med;
99 }
100
101 int main()
102 {
103 citeste();
104
105 int k,t,i,j;
106
CAPITOLUL 26. ONI 2014 523

107 for(k=1;k<=q;k++)
108 {
109 f>>t>>i>>j;
110 if(t==1)
111 g<<cerinta1(i,j)<<endl;
112 else
113 g<<cerinta2(i,j)<<endl;
114 }
115
116 return 0;
117 }

Listing 26.2.6: qvect_qsort.cpp


1 #include <algorithm>
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("qvect.in");
7 ofstream g("qvect.out");
8
9 int a[101][5005], poz[101], n, q,nr;
10 int b[500005];
11
12 void citeste()
13 {
14 f>>n>>q;
15 for(int i=1;i<=n;i++)
16 {
17 f>>a[i][0];
18 for(int j=1;j<=a[i][0];j++)
19 f>>a[i][j];
20
21 poz[i]=1;
22 }
23 }
24
25 int abs(int x)
26 {
27 return max(x,-x);
28 }
29
30 int cerinta1(int n1, int n2)
31 {
32 int i=1,j=1,m=0,nr1,nr2, dif=1000000005,x,y;
33 nr1=a[n1][0];
34 nr2=a[n2][0];
35
36 while(i<=nr1 && j<=nr2)
37 {
38 if(a[n1][i]<a[n2][j])
39 {
40 x=a[n1][i];i++;
41 if(m==2)
42 dif=min(dif, abs(x-y));
43 m=1;
44 }
45 else
46 if(a[n1][i]>a[n2][j])
47 {
48 y=a[n2][j];
49 j++;
50 if(m==1)
51 dif=min(dif, abs(x-y));
52 m=2;
53 }
54 else
55 if(a[n1][i]==a[n2][j])
56 return 0;
57 }
58
59 if(i<=nr1)
60 {
61 if(m==2)
CAPITOLUL 26. ONI 2014 524

62 dif=min(dif, abs(a[n1][i]-y));
63 }
64 else
65 {
66 if(m==1)
67 dif=min(dif, abs(x-a[n2][j]));
68 }
69
70 return dif;
71 }
72
73 int cerinta2(int n1, int n2)
74 {
75 int i,j,nr=0,k;
76 for(i=n1;i<=n2;i++)
77 nr+=a[i][0];
78
79 nr/=2;
80 k=min(nr,a[n1][0]);
81 for(i=1;i<=k;i++)
82 b[i]=a[n1][i];
83
84 for(i=n1+1;i<=n2;i++)
85 {
86 for(j=1;j<=a[i][0] && j<=nr;j++)
87 b[++k]=a[i][j];
88
89 sort(b+1,b+k+1);
90 k=min(k,nr);
91 }
92
93 return b[k];
94 }
95
96 int main()
97 {
98 citeste();
99 int k,t,i,j;
100 for(k=1;k<=q;k++)
101 {
102 f>>t>>i>>j;
103 if(t==1)
104 g<<cerinta1(i,j)<<endl;
105 else
106 g<<cerinta2(i,j)<<endl;
107 }
108
109 return 0;
110 }

Listing 26.2.7: qvect_qsortscanf.cpp


1 #include <algorithm>
2 #include <cstdio>
3
4 using namespace std;
5
6 int a[101][5005], poz[101], n, q,nr;
7 int b[500005];
8
9 void citeste()
10 {
11 freopen("qvect.in", "r", stdin);
12
13 scanf("%d%d", &n, &q);
14 for(int i=1;i<=n;i++)
15 {
16 scanf("%d",&a[i][0]);
17 for(int j=1;j<=a[i][0];j++)
18 scanf("%d",&a[i][j]);
19
20 poz[i]=1;
21 }
22 }
23
CAPITOLUL 26. ONI 2014 525

24 int abs(int x)
25 {
26 return max(x,-x);
27 }
28
29 int cerinta1(int n1, int n2)
30 {
31 int i=1,j=1,m=0,nr1,nr2, dif=1000000005,x,y;
32 nr1=a[n1][0];
33 nr2=a[n2][0];
34
35 while(i<=nr1 && j<=nr2)
36 {
37 if(a[n1][i]<a[n2][j])
38 {
39 x=a[n1][i];i++;
40 if(m==2)
41 dif=min(dif, abs(x-y));
42 m=1;
43 }
44 else
45 if(a[n1][i]>a[n2][j])
46 {
47 y=a[n2][j];
48 j++;
49 if(m==1)
50 dif=min(dif, abs(x-y));
51 m=2;
52 }
53 else
54 if(a[n1][i]==a[n2][j])
55 return 0;
56 }
57
58 if(i<=nr1)
59 {
60 if(m==2)
61 dif=min(dif, abs(a[n1][i]-y));
62 }
63 else
64 {
65 if(m==1)
66 dif=min(dif, abs(x-a[n2][j]));
67 }
68
69 return dif;
70 }
71
72
73 int cerinta2(int n1, int n2)
74 {
75 int i,j,nr=0,k;
76 for(i=n1;i<=n2;i++)
77 nr+=a[i][0];
78
79 nr/=2;
80 k=min(nr,a[n1][0]);
81 for(i=1;i<=k;i++)
82 b[i]=a[n1][i];
83
84 for(i=n1+1;i<=n2;i++)
85 {
86 for(j=1;j<=a[i][0] && j<=nr;j++)
87 b[++k]=a[i][j];
88
89 sort(b+1,b+k+1);
90 k=min(k,nr);
91 }
92
93 return b[k];
94 }
95
96 int main()
97 {
98 citeste();
99 freopen("qvect.out","w", stdout);
CAPITOLUL 26. ONI 2014 526

100 int k,t,i,j;


101
102 for(k=1;k<=q;k++)
103 {
104 scanf("%d%d%d", &t, &i, &j);
105 if(t==1)
106 printf("%d\n",cerinta1(i,j));
107 else
108 printf("%d\n",cerinta2(i,j));
109 }
110
111 return 0;
112 }

Listing 26.2.8: qvect_vs.cpp


1 #include<fstream>
2
3 using namespace std;
4
5 ifstream fin("qvect.in");
6 ofstream fout("qvect.out");
7
8 int a[102][5003] ,N ,b[102] ,Q , v[500009], n, w[500009];
9 int inf=2100000000;
10
11 int dif_min(int i1, int j1)
12 {
13 int i,j,d,dmin;
14 i=1;
15 j=1;
16 dmin=inf;
17 while(i<=b[i1] && j<=b[j1])
18 {
19 d=a[i1][i]-a[j1][j];
20
21 if(d<0)d=-d;
22 if(d<dmin)dmin=d;
23 if(dmin==0)return 0;
24
25 if(a[i1][i]<a[j1][j])
26 i++;
27 else
28 j++;
29 }
30
31 return dmin;
32 }
33
34 int maxim(int a, int b)
35 {
36 return ((a>b)?a:b);
37 }
38
39 int cautbin(int i, int v)
40 {
41 int p,q,m;
42 a[i][b[i]+1]=maxim(v+1,a[i][b[i]]);
43 p=1; q=b[i];
44 while(p<=q)
45 {
46 m=p+(q-p)/2;
47 if(v<a[i][m])
48 q=m-1;
49 else
50 p=m+1;
51 }
52
53 return q;
54 }
55
56 int cautbin2(int i, int v)
57 {
58 int p,q,m,r;
59 p=1;
CAPITOLUL 26. ONI 2014 527

60 q=b[i];
61 r=-1;
62 while(p<=q && r==-1)
63 {
64 m=p+(q-p)/2;
65 if(v==a[i][m])
66 return m;
67
68 if(v<a[i][m])
69 q=m-1;
70 else
71 p=m+1;
72 }
73
74 return -1;
75 }
76
77 void sortare()
78 {
79 int i,j,k,p,q,r,s,ok;
80
81 //sterg elementele suplimentare
82 k=1;
83 for (i=2;i<=n;i++)
84 {
85 if(v[k]!=v[i])
86 v[++k]=v[i];
87 }
88
89 n=k;
90 ok=0;
91 while(ok==0)
92 {
93 r=0;
94 s=0;
95 do
96 {
97 p=r+1;
98 q=p;
99 while(q+1<=n && v[q]<=v[q+1])
100 q++;
101
102 s++;
103 r=q+1;
104 while(r+1<=n && v[r]<=v[r+1])
105 r++;
106
107 if(p<=q && q<r && r<=n)
108 {
109 s++;
110 i=p;
111 j=q+1;
112 k=0;
113 while(i<=q && j<=r)
114 {
115 if(v[i]<v[j])
116 w[++k]=v[i++];
117 else
118 w[++k]=v[j++];
119 }
120
121 while(i<=q)
122 w[++k]=v[i++];
123
124 while(j<=r)
125 w[++k]=v[j++];
126
127 i=p;
128 for(j=1;j<=k;j++)
129 v[i++]=w[j];
130 }
131 } while(r<n);
132
133 if(s==1) ok=1;
134 }
135
CAPITOLUL 26. ONI 2014 528

136 //sterg elementele suplimentare


137 k=1;
138 for (i=2;i<=n;i++)
139 {
140 if(v[k]!=v[i])
141 v[++k]=v[i];
142 }
143 n=k;
144 }
145
146 int mmse(int i1, int j1, int v)
147 {
148 //aflam fata de cate valori din secventa i1..j1 este v mai mare sau egala
149 int s,i;
150 s=0;
151 for(i=i1;i<=j1;i++)
152 {
153 if(a[i][b[i]]<=v)
154 s=s+b[i];
155 else
156 if(a[i][1]<=v)
157 s=s+cautbin(i,v);
158 }
159
160 return s;
161 }
162
163 int exista(int i1, int j1, int v)
164 {
165 int s,i;
166 for(i=i1;i<=j1;i++)
167 {
168 if(a[i][1]<=v && v<=a[i][b[i]])
169 {
170 s=cautbin2(i,v);
171 if(s!=-1) return 1;
172 }
173 }
174
175 return -1;
176 }
177
178 int mediana(int i1, int j1)
179 {
180 int p,q,m,s,nr;
181 nr=0;
182 for(p=i1;p<=j1;p++)
183 nr=nr+b[p];
184
185 p=1;
186 q=n;
187 while(p<=q)
188 {
189 m=p+(q-p)/2;
190 s=mmse(i1,j1,v[m]);
191 if(s<nr/2)
192 p=m+1;
193 else
194 q=m-1;
195 }
196
197 while(exista(i1,j1,v[p])==0)
198 p++;
199
200 return v[p];
201 }
202
203 int main()
204 {
205 int i,j,t,i1,j1;
206 fin>>N>>Q;
207 n=0;
208 for (i=1;i<=N;i++)
209 {
210 fin>>b[i];
211 for (j=1;j<=b[i];j++)
CAPITOLUL 26. ONI 2014 529

212 {
213 fin>>a[i][j];
214 n++;
215 v[n]=a[i][j];
216 }
217 }
218
219 sortare();
220
221 for (i=1;i<=Q;i++)
222 {
223 fin>>t>>i1>>j1;
224 if(t==1)
225 fout<<dif_min(i1,j1);
226 else
227 fout<<mediana(i1,j1);
228
229 fout<<"\n";
230 }
231
232 fout.close();
233 fin.close();
234 return 0;
235 }

26.2.3 *Rezolvare detaliat 

26.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
S  se determine num rul tripletelor geometrice limitate de num rul natural N .

Date de intrare
Fi³ierul de intrare tg.in conµine pe prima linie un num r natural N .

Date de ie³ire
În ³ierul de ie³ire tg.out se va scrie num rul tripletelor geometrice limitate de N .

Restricµii ³i preciz ri
a 4&N & 4 000 000

Exemple:
tg.in tg.out Explicaµii
8 2 Cele dou  triplete sunt 1, 2, 4 ³i 2, 4, 8
Timp maxim de executare/test: 0.1 secunde
Memorie: total 32 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 5 KB
CAPITOLUL 26. ONI 2014 530

26.3.1 Indicaµii de rezolvare

prof. Piµ-Rada Ionel-Vasile - C. N. Traian Drobeta-Tr. Severin

Complexitate O N ˜ N 
Pentru ecare a din mulµimea r1, 2, ..., N  2x ³i pentru ecare c din mulµimea a  2, a  3, ..., N
se veric  dac  a ˜ c este p trat perfect ³i în caz armativ se calculeaz  b sqrt a ˜ c ³i se
contorizeaz  rezultatul.
Complexitate O N ˜ sqrt N 
Pentru ecare 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  e multiplu al lui a ³i în acela³i timp ar trebui
s  e 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  e 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  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 ecare
Õ a din mulµimea r 1, 2, ..., N  2x vom parcurge k din mulµimea

r x  1 ,  x  2 , ...,  x x ³i astfel vom obµine toate tripletele geometrice c utate, care sunt
a a a
Ó 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 ecare 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 .

26.3.2 Cod surs 

Listing 26.3.1: tg_100p.cpp


1 #include <fstream>
2 #include <algorithm>
3 using namespace std;
4
5 ifstream fin("tg.in");
6 ofstream fout("tg.out");
7
8 int N;
9 bool ok[4000010];
10 long long sol;
11
12 int main()
13 {
14 fin >> N;
15 for (int i = 1; i <= N; ++i)
16 if (!ok[i])
17 for (int j = 1; i * j * j <= N; ++j)
18 {
19 sol += j - 1;
20 ok[i * j * j] = true;
21 }
22
23 fout << sol << ’\n’;
CAPITOLUL 26. ONI 2014 531

24
25 fin.close();
26 fout.close();
27 }

Listing 26.3.2: tg_on.cpp


1 //nr triplete 1<=p<q<r<=n aflate in progresie geometrica
2 #include<fstream>
3 #include<cmath>
4
5 using namespace std;
6
7 ifstream fin("tg.in");
8 ofstream fout("tg.out");
9
10 int v[1000010];
11 int main()
12 {
13 long long n,a,i,k,s,p,n4,n4a;
14 fin>>n;
15 n4=n/4;
16 s=0;
17 for(a=1;a<=n4;a++)
18 {
19 if (v[a]==0)
20 {
21 n4a=n4/a;
22 for(i=1;i*i<=n4a;i++)
23 {
24 v[a*i*i]=a;
25 }
26 k=(int)sqrt(n/a);
27 if(k%2==0)
28 p=k/2*(k-1);
29 else
30 p=(k-1)/2*k;
31 s=s+p;
32 }
33 }
34
35 fout<<s<<endl;
36 fout.close();
37 fin.close();
38 return 0;
39 }

Listing 26.3.3: tg_on_v2.cpp


1 //nr triplete 1<=p<q<r<=n aflate in progresie geometrica
2 #include<fstream>
3 #include<cmath>
4
5 using namespace std;
6
7 ifstream fin("tg.in");
8 ofstream fout("tg.out");
9
10 int v[4000010];
11
12 int main()
13 {
14 long long n,s,a,x,k1,k2,t,d,c,b,i,k,s1,p,r;
15 fin>>n;
16 s=0;
17 t=0;
18 for (a=1;a<=n-2;a++)
19 {
20 if(v[a]==0)//i are toti factorii primi cu exponenti impari
21 {
22 b=(n-2)/a;
23 for(i=1;i*i<=b;i++)
24 v[a*i*i]=a;
25 }
CAPITOLUL 26. ONI 2014 532

26
27 x=v[a];
28 k1=sqrt(a/x);
29 k2=sqrt(n/x);
30 s=s+k2-k1;
31 }
32
33 fout<<s<<endl;
34 fout.close();
35 fin.close();
36 return 0;
37 }

Listing 26.3.4: tg_on2.cpp


1 //nr triplete 1<=p<q<r<=n aflate in progresie geometrica
2 #include<fstream>
3 #include<cmath>
4
5 using namespace std;
6
7 ifstream fin("tg.in");
8 ofstream fout("tg.out");
9
10 int main()
11 {
12 int a,c,s,n,ua,uc,uac;
13 long long bb;
14 double r;
15 fin>>n;
16 s=0;
17 for(a=1;a<=n-2;a++)
18 {
19 ua=a%10;
20 for (c=a+2;c<=n;c++)
21 {
22 uc=c%10;
23 uac=(ua*uc)%10;
24 if(uac==0 || uac==1 || uac==4 ||
25 uac==5 || uac==6 || uac==9)
26 {
27 bb=a;
28 bb=bb*c;
29 r=sqrt(bb);
30 if(r==(int)r)
31 s++;
32 }
33 }
34 }
35
36 fout<<s;
37 fout.close();
38 fin.close();
39 return 0;
40 }

Listing 26.3.5: tg_onsqrtn.cpp


1 //nr triplete 1<=p<q<r<=n aflate in progresie geometrica
2 #include<fstream>
3 #include<cmath>
4
5 using namespace std;
6
7 ifstream fin("tg.in");
8 ofstream fout("tg.out");
9
10 int main()
11 {
12 long long n,s,a,x,k1,k2,t,d,c;
13 fin>>n;
14 s=0;
15 for (a=1;a<=n-2;a++)
16 {
CAPITOLUL 26. ONI 2014 533

17 x=1;
18 t=a;
19 d=2;
20 while(d*d<=t)
21 {
22 c=0;
23 while(t%d==0)
24 {
25 c++;
26 t=t/d;
27 }
28 if(c%2==1) x=x*d;
29 d++;
30 }
31
32 if(t>1) x=x*t;
33 k1=sqrt(a/x);
34 k2=sqrt(n/x);
35 s=s+k2-k1;
36 }
37
38 fout<<s<<endl;
39 fout.close();
40 fin.close();
41 return 0;
42 }

26.3.3 *Rezolvare detaliat 

26.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  e 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 semnicaµia de
mai sus.
Date de ie³ire
În ³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 26. ONI 2014 534

Timp maxim de executare/test: 0.1 secunde


Memorie: total 32 MB din care pentru stiv  16 MB
Dimensiune maxim  a sursei: 5 KB

26.4.1 Indicaµii de rezolvare

prof. Che³c  Ciprian - Liceul Tehnologic Costin Neniµescu Buz u

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 ind 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

(*) oricare dou  secvenµe adiacente din ³irul v[] au sume diferite, astfel:
- primii trei termeni sunt 1, 2, 1
- al patrulea termen nu poate  1, 2, 3, 4 deci va  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  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  ale³i identici cu primii obµinându-se un ³ir cu 2 ˜ i  1 termeni
³i procesul va  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 26. ONI 2014 535

26.4.2 Cod surs 

Listing 26.4.1: CC2_progresie.cpp


1 // Solutie Chesca Ciprian - 95 p
2 // Toate numere care in baza 3 nu contin cifra 2
3
4 #include <fstream>
5 #include <math.h>
6
7 using namespace std;
8
9 int main()
10 {
11 ifstream f("progresie.in");
12 ofstream g("progresie.out");
13
14 int n,i,s,x,ok,c;
15
16 f>>n;
17
18 s=0;
19 for(i=1;i<=2*n*sqrt(n);i++)
20 {
21 x=i;
22 ok=1;
23 do
24 {
25 c=x%3;
26 if (c==2) ok=0;
27 x=x/3;
28 } while (x>0&&ok);
29
30 if (ok)
31 if (s<n)
32 {
33 s++;
34 g<<i<<" ";
35 }
36 else
37 break;
38
39 }
40
41 g<<"\n";
42
43 f.close();
44 g.close();
45 return 0;
46 }

Listing 26.4.2: CC3_progresie.cpp


1 // Solutie Chesca Ciprian - 100 p
2 // Algoritm constructiv - inductie matematica
3
4 #include <fstream>
5 #include <math.h>
6 #include <iostream>
7
8 #define nmax 100009
9
10 using namespace std;
11
12 int a[nmax];
13
14 int main()
15 {
16 ifstream f("progresie.in");
17 ofstream g("progresie.out");
18
19 long long k,p;
20 int n,i,j,t,un,x;
CAPITOLUL 26. ONI 2014 536

21
22 f>>n;
23
24 a[1]=1;a[2]=2;k=2;
25
26 i=1;
27 un=0;
28 p=3;
29 while (!un)
30 {
31 t=k;
32 for(j=1;j<=k;j++)
33 {
34 x=a[j]+p;
35 if (x<=3*n*sqrt(n)) a[++t]=x;
36 else un=1;
37
38 }
39
40 if (!un)
41 k=2*k;
42 else
43 k=t;
44 i++;
45 p=p*3;
46 }
47 // cout << t ;
48
49 for(i=1;i<=n;i++)
50 g<<a[i]<<" ";
51
52 g<<"\n";
53 f.close();
54 g.close();
55 return 0;
56 }

Listing 26.4.3: CM_progresie.cpp


1 #include <fstream>
2
3 using namespace std;
4
5 int v[2200002];
6
7 int main()
8 {
9 int n,j,u,i,x;
10
11 ifstream f("progresie.in") ;
12 ofstream g("progresie.out");
13
14 f>>n;
15 g<<1<<" "<<2;
16 v[1]=v[2]=1;
17 u=2;
18 for(i=3;i<=n;i++)
19 {
20 x=2*u;
21 for(j=u-1;j>=1;j--)
22 if(v[j]==1 && v[x-j]!=1)
23 v[x-j]=2;
24 u++;
25 while(v[u]) u++;
26 v[u]=1;
27 g<<" "<<u;
28 }
29
30 g<<endl;
31 return 0;
32 }

Listing 26.4.4: EN_progresie.cpp


CAPITOLUL 26. ONI 2014 537

1 # include <cstdio>
2 # include <cmath>
3 using namespace std;
4
5 int N, K, i;
6 int a[100003];
7
8 int main()
9 {
10 freopen("progresie.in", "r", stdin);
11 freopen("progresie.out","w",stdout);
12
13 scanf("%d", &N);
14
15 a[0] = 1;
16 for (i=0; i<=N; ++i)
17 {
18 a[2*i] = 3 * a[i] - 2;
19 a[2*i+1] = 3 * a[i] - 1;
20 }
21
22 for (i=0; i<N; ++i)
23 printf("%d ", a[i]);
24
25 return 0;
26 }

Listing 26.4.5: PIT_progresie.cpp


1 //Solutie - Pit Rada Vasile Ionel - 100 p
2
3 #include<fstream>
4
5 using namespace std;
6
7 ifstream fin ("progresie.in");
8 ofstream fout("progresie.out");
9
10 int N,K,s,a,i,v[220009],j;
11 long long p;
12
13 int main()
14 {
15 fin>>N;
16 s=0;
17 a=1;
18 fout<<a+s<<" ";
19 p=1;
20
21 for (i=1;i<N;i++)
22 {
23 if(i==p)
24 {
25 p=p*2;
26 v[i]=s+1;
27 s=s+v[i];
28 j=1;
29 }
30 else
31 {
32 v[i]=v[j];
33 s=s+v[i];
34 j++;
35 }
36
37 fout<<a+s<<" ";
38 }
39
40 fout.close();
41 fin.close();
42 return 0;
43 }

Listing 26.4.6: SP_progresie.cpp


CAPITOLUL 26. ONI 2014 538

1 #include <stdio.h>
2 #include <math.h>
3
4 #define NMax 10002
5
6 int N, Limit;
7 bool b[2 * NMax * NMax];
8 int sol[NMax];
9
10 bool bkt( int x, int val )
11 {
12 if ( x == N + 1 )
13 return true;
14
15 for ( int v = val; v <= Limit; ++ v )
16 if ( b[v]==false )
17 {
18 for ( int i = 1; i < x; ++ i)
19 b[ 2 * v - sol[i] ]=true;
20
21 sol[x] = v;
22
23 if ( bkt( x + 1, v + 1) )
24 return true;
25
26 for ( int i = 1; i < x; ++ i)
27 b[ 2 * v - sol[i] ]=false;
28 }
29
30 return false;
31 }
32
33 int main()
34 {
35 freopen("progresie.in", "r", stdin);
36 freopen("progresie.out", "w", stdout);
37
38 scanf("%d", &N);
39 Limit = 2 * N * sqrt(N);
40 if ( bkt( 1, 1 ) )
41 {
42 for ( int i = 1; i <= N; ++ i)
43 printf("%d ", sol[i]);
44
45 printf("\n");
46 }
47 else
48 return 0;
49 }

26.4.3 *Rezolvare detaliat 

26.5 reex
Problema 5 - reex 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
ind numerotate de la 1 la 4, colµul cu num rul 1 ind cel din
stânga jos apoi restul ind 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 26. ONI 2014 539

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 un-
ghiul de incidenµ  este egal cu cel de reexie.

Cerinµe
Se cere s  se determine:
a) dup  câte secunde ³i prin ce colµ al ringului va ie³i robotul;
b) de câte ori se ciocne³te robotul de pereµii orizontali ³i verticali, rezultând o schimbare de
direcµie, pân  la ie³irea din ring.

Date de intrare
Fi³ierul de intrare reex.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 reex.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, ³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.

Restricµii ³i preciz ri
a 3 & N, M & 2 000 000 000
a Pentru rezolvarea corect  a unei singure cerinµe se acord  50% din punctaj, iar pentru
rezolvarea corect  a ambelor cerinµe se acord  100% din punctaj.

Exemple:
reex.in reex.out Explicaµii
36 11 4 4 1
Pân  la ie³ire se parcurg 11 p trate,
ie³irea se produce pe la colµul 4. Se
produc 4 ciocniri cu pereµii orizontali
³i o ciocnire cu pereµii verticali.

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).

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 5 KB

26.5.1 Indicaµii de rezolvare

prof. Silaghi Lucian - Lic. Tehnologic Lucian Blaga Reghin

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 veric  ciocnirea cu un petete orizontal (y 1 sau y N ) sau cu un perete vertical (x 1 sau
x M ). Se veric  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 26. ONI 2014 540

- Colµul 3 (dreapta sus) cu x M ³i y N ;


- Colµul 4 (stânga sus) cu x 1 ³i y N ;

În urma ec 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
ind 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 ind colµul 4 .

Colµul exact se determin  prin combinarea posibilit tilor rezultate în funtie de paritatea lui H
³i V .

Figura 26.1: reex

26.5.2 Cod surs 

Listing 26.5.1: reex_eugen.cpp


1 # include <fstream>
2
3 using namespace std;
4
5 ifstream f("reflex.in");
6 ofstream g("reflex.out");
7
8 long long cmmmc, n, m, a, b, r, v, h, c;
9
10 int main()
CAPITOLUL 26. ONI 2014 541

11 {
12 f >> n >> m;
13 --n, --m;
14
15 //cmmdc
16 a = n;
17 b = m;
18 while(b)
19 {
20 r = a % b;
21 a = b; b = r;
22 }
23
24 //cmmmc
25 cmmmc = n * m / a;
26
27 v = n / a;
28 h = m / a;
29 c = 1;
30 if (v % 2 == 0)
31 c = 4;
32 else
33 if (h % 2 == 0)
34 c = 2;
35 else
36 c = 3;
37
38 g << cmmmc + 1 << " " << c << "\n";
39 g << h - 1 << " " << v - 1 << "\n";
40 return 0;
41 }

Listing 26.5.2: reex_LS_brut.cpp


1 #include <iostream>
2 #include <iomanip>
3 #include <fstream>
4
5 using namespace std;
6
7 struct punct
8 {
9 long x,y;
10 };
11
12 int main()
13 {
14 long long n,m,x=1,N, S, E, W;
15 int dx,dy,colt=0;
16 punct c1,c2,c3,c4,b;
17
18 ifstream inFile;
19 ofstream outFile;
20
21 // citire date din fis
22 inFile.open("reflex.in");
23 outFile.open("reflex.out");
24
25 N=0;
26 S=0;
27 E=0;
28 W=0;
29 inFile >> n >> m;
30
31 c1.x=1; c1.y=1;
32 c2.x=m; c2.y=1;
33 c3.x=m; c3.y=n;
34 c4.x=1; c4.y=n;
35 b=c1;
36
37 dx=1;
38 dy=1;
39 do
40 {
41 x++;
CAPITOLUL 26. ONI 2014 542

42 b.x+=dx;
43 b.y+=dy;
44 //cout << "B("<<b.y<<"."<<b.x<<")\n ";
45 if (b.x == m) {dx= -1; E++;} //lat E
46 if (b.y == n) {dy= -1; N++;} //lat N
47 if (b.x == 1) {dx = 1; W++;} //lat W
48 if (b.y == 1) {dy = 1; S++;} //lat S
49 if ((b.x==c1.x) && (b.y==c1.y)) {colt=1; S--; W--;}
50 if ((b.x==c2.x) && (b.y==c2.y)) {colt=2; S--; E--;}
51 if ((b.x==c3.x) && (b.y==c3.y)) {colt=3; N--; E--;}
52 if ((b.x==c4.x) && (b.y==c4.y)) {colt=4; N--; W--;}
53
54 } while (!colt);
55
56 outFile << x << " " << colt << "\n";
57 outFile << N + S << " " << E + W << "\n";
58
59 inFile.close();
60 outFile.close();
61 return 0;
62 }

Listing 26.5.3: reex_LS_Euclid_100p.cpp


1 #include <iostream>
2 #include <iomanip>
3 #include <fstream>
4
5 using namespace std;
6
7 long cmmdc(long a, long b)
8 {
9 long r;
10 while (b)
11 {
12 r=a%b;
13 a=b;
14 b=r;
15 }
16 return a;
17 }
18
19 int main()
20 {
21 long n,m,N,M,CMMDC,nl,nh;
22 long long s;
23 int colt=0;
24
25 ifstream inFile;
26 ofstream outFile;
27
28 // citire date din fis
29 inFile.open("reflex.in");
30 outFile.open("reflex.out");
31 inFile >> n >> m;
32 N=n-1;
33 M=m-1;
34 CMMDC=cmmdc(N,M);
35
36 //CMMMC = N * M / CMMDC;
37 //nl=CMMMC/M;
38 //nh=CMMMC/N;
39
40 nl=N/CMMDC;
41 nh=M/CMMDC;
42 if (nl%2==0)
43 colt=4;
44 else
45 if (nh%2==0)
46 colt=2;
47 else
48 colt=3;
49
50 s = nl;
51 s = s * M + 1;
CAPITOLUL 26. ONI 2014 543

52
53 outFile << s << " " << colt << "\n";
54 outFile << nh -1 << " " << nl -1 << "\n";
55
56 inFile.close();
57 outFile.close();
58 return 0;
59 }

Listing 26.5.4: reex_LS_med.cpp


1 #include <iostream>
2 #include <iomanip>
3 #include <fstream>
4
5 using namespace std;
6
7 struct punct
8 {
9 long long x,y;
10 };
11
12 int main()
13 {
14 long long n,m,N, S, E, W;
15 long long s=1;
16
17 int dx,dy,colt=0;
18 punct c1,c2,c3,c4,b;
19
20 ifstream inFile;
21 ofstream outFile;
22
23 // citire date din fis
24 inFile.open("reflex.in");
25 outFile.open("reflex.out");
26
27 N=0, S=0, E=0, W=0;
28 inFile >> n >> m;
29
30 c1.x=1; c1.y=1;
31 c2.x=m; c2.y=1;
32 c3.x=m; c3.y=n;
33 c4.x=1; c4.y=n;
34 b=c1;
35
36 if (n>m)
37 dx=m-1;
38 else
39 dx=n-1;
40
41 dy=dx;
42 do
43 {
44 //cout <<"dx="<<dx<< " dy="<<dy<<"\n";
45 if ( (b.x+dx <= m) && (b.y+dy <= n) && (b.x+dx >=1) && (b.y+dy>=1) )
46 {
47 b.x+=dx;
48 b.y+=dy;
49 }
50 else
51 {
52 if (b.x+dx > m)
53 {
54 if (dy > 0)
55 b.y+=m-b.x;
56 else
57 b.y-=m-b.x;
58
59 b.x=m;
60 }
61 else
62 if (b.y+dy > n)
63 {
64 if (dx > 0)
CAPITOLUL 26. ONI 2014 544

65 b.x+=n-b.y;
66 else
67 b.x-=n-b.y;
68
69 b.y=n;
70 }
71 else
72 if (b.x+dx < 1)
73 {
74 if (dy > 0)
75 b.y+=b.x-1;
76 else
77 b.y-=b.x-1;
78
79 b.x=1;
80 }
81 else
82 if (b.y+dy < 1)
83 {
84 if (dx > 0)
85 b.x+=b.y-1;
86 else
87 b.x-=b.y-1;
88
89 b.y=1;
90 }
91 }
92
93 //cout << "B("<<b.y<<"."<<b.x<<")\n ";
94 //cin >> ch;
95 if (b.x == m) {dx= -dx; E++; s+=m-1;} //lat E
96 if (b.y == n) {dy= -dy; N++;} //lat N
97 if (b.x == 1) {dx = -dx; W++; s+=m-1;} //lat W
98 if (b.y == 1) {dy = -dy; S++;} //lat S
99 if ((b.x==c1.x) && (b.y==c1.y)) {colt=1; S--; W--;}
100 if ((b.x==c2.x) && (b.y==c2.y)) {colt=2; S--; E--;}
101 if ((b.x==c3.x) && (b.y==c3.y)) {colt=3; N--; E--;}
102 if ((b.x==c4.x) && (b.y==c4.y)) {colt=4; N--; W--;}
103 } while (!colt);
104
105 outFile << s << " " << colt << "\n";
106 outFile << N + S << " " << E +W << "\n";
107
108 inFile.close();
109 outFile.close();
110 return 0;
111 }

Listing 26.5.5: reex_LS_Sr_100p.cpp


1 #include <iostream>
2 #include <iomanip>
3 #include <fstream>
4
5 using namespace std;
6
7 long cmmdc(long a, long b)
8 {
9 while (a!=b)
10 {
11 if (a>b)
12 a=a-b;
13 else
14 b=b-a;
15 }
16 return a;
17 }
18
19 int main()
20 {
21 long n,m,N,M,CMMDC,nl,nh;
22 long long s;
23 int colt=0;
24
25 ifstream inFile;
CAPITOLUL 26. ONI 2014 545

26 ofstream outFile;
27
28 // citire date din fis
29 inFile.open("reflex.in");
30 outFile.open("reflex.out");
31 inFile >> n >> m;
32
33 N=n-1;
34 M=m-1;
35 CMMDC=cmmdc(N,M);
36 // CMMMC = N * M / CMMDC;
37 nl=N/CMMDC;
38 nh=M/CMMDC;
39 if (nl%2==0)
40 colt=4;
41 else
42 if (nh%2==0)
43 colt=2;
44 else
45 colt=3;
46
47 s = nl;
48 s = s * M + 1;
49
50 outFile << s << " " << colt << "\n";
51 outFile << nh -1 << " " << nl -1 << "\n";
52
53 inFile.close();
54 outFile.close();
55 return 0;
56 }

Listing 26.5.6: reex_vs.cpp


1 #include <iostream>
2 #include <iomanip>
3 #include <fstream>
4
5 using namespace std;
6
7 long cmmdc(long a, long b)
8 {
9 while (a!=b)
10 {
11 if (a>b)
12 a=a-b;
13 else
14 b=b-a;
15 }
16 return a;
17 }
18
19 int main()
20 {
21 long n,m,N,M,CMMDC,nl,nh;
22 long long s;
23 int colt=0;
24
25 ifstream inFile;
26 ofstream outFile;
27
28 // citire date din fis
29 inFile.open("reflex.in");
30 outFile.open("reflex.out");
31 inFile >> n >> m;
32
33 N=n-1;
34 M=m-1;
35 CMMDC=cmmdc(N,M);
36 // CMMMC = N * M / CMMDC;
37 nl=N/CMMDC;
38 nh=M/CMMDC;
39 if (nl%2==0)
40 colt=4;
41 else
CAPITOLUL 26. ONI 2014 546

42 if (nh%2==0)
43 colt=2;
44 else
45 colt=3;
46
47 s = nl;
48 s = s * M + 1;
49
50 outFile << s << " " << colt << "\n";
51 outFile << nh -1 << " " << nl -1 << "\n";
52
53 inFile.close();
54 outFile.close();
55 return 0;
56 }

26.5.3 *Rezolvare detaliat 

26.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 ec 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, ecare camer  având drept eti-
chet  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 a  precum ³i eticheta camerei în care
buc tarul hotelului depoziteaz  alimente.
Studiind hotelul, ³oricelul a constatat c  pe ecare etaj, din orice camer  poate intra în toate
camerele care au un perete comun cu aceasta (existând un mic oriciu pentru aerisire).
De asemenea, ³oricelul a constatat c  din ecare 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
Se cere s  se determine:
a) num rul de camere prin care trece cel mai scurt traseu al ³oricelului de la camera lui la
camera cu alimente (inclusiv camera lui ³i camera cu alimente);
b) etichetele camerelor prin care trece traseul determinat la punctul a).

Date de intrare
Fi³ierul traseu.in conµine:
a pe prima linie, dou  numere naturale N ³i M separate printr-un spaµiu, N cu semnicaµia
din enunµ iar M reprezentând num rul de camere ocupate de clienµii hotelului;
a pe a doua linie, trei numere naturale K1 L1 C1, separate prin câte un spaµiu, reprezentând
eticheta camerei în care se a  ³oricelul;
a pe a treia linie, trei numere naturale K2 L2 C2, separate prin câte un spaµiu, reprezentând
eticheta camerei în care sunt depozitate alimentele;
a pe ecare dintre urm toarele M linii, câte trei numere naturale X Y Z , separate prin câte
un spaµiu, reprezentând etichetele celor M camere ocupate de clienµi.
CAPITOLUL 26. ONI 2014 547

Date de ie³ire
Fi³ierul de ie³ire traseu.out va conµine pe prima linie un num r natural T reprezentând
num rul de camere prin care trece cel mai scurt traseu al ³oricelului de la camera lui la camera
cu alimente determinat la punctul a).
Pe ecare din urm toarele T linii, se vor scrie câte trei numere naturale X Y Z , separate prin
câte un spaµiu, reprezentând etichetele camerelor prin care trece traseul determinat la punctul
a), în ordinea în care sunt parcurse camerele de c tre ³oricel pentru a ajunge din camera lui în
camera cu alimente.

Restricµii ³i preciz ri
a 2 & N & 100; 1 & M & 5000 ³i M $ N ˜ N  2
a “oricelul nu intr  decât în camere neocupate de clienµi.
a Camera ³oricelului este o camer  neocupat  de clienµi.
a Dac  exist  mai multe trasee ale ³oricelului de la camera lui la camera de alimente care trec
prin exact T camere, atunci traseul a³at va  cel mai mic traseu din punct de vedere lexicograc.
a Eticheta (X1 Y1 Z1 ) se consider  strict mai mic  în sens lexicograc ca eticheta (X2 Y2 Z2 )
dac  este satisf cut  doar una dintre condiµiile:
1) X1 $ X2 2) X1 X2 ³i Y1 $ Y2 3) X1 X2 ³i Y1 Y2 ³i Z  1 $ Z2
a Eticheta X1 Y1 Z1 se consider  egal  cu eticheta X2 Y2 Z2 dac  X1 X2 ³i Y1 Y2 ³i
Z1 Z2 . Vom scrie egalitatea lor astfel: (X1 Y1 Z1 ) = (X2 Y2 Z2 ).
a Traseul ce trece (în aceast  ordine) prin camerele cu etichetele (X1 Y1 Z1 ), (X2 Y2 Z2 ), ...,
(XT YT ZT ) este mai mic din punct de vedere lexicograc decât traseul (A1 B1 C1 ), (A2 B2 C2 ),
..., (AT BT CT ) dac  exist  un indice J (1 & J & T ) astfel încât (X1 Y1 Z1 ) = (A1 B1 C1 ), (X2
Y2 Z2 ) = (A2 B2 C2 ), ..., (XJ 1 YJ 1 ZJ 1 ) = (AJ 1 BJ 1 CJ 1 ) iar eticheta (XJ YJ ZJ ) este
strict mai mic  decât eticheta (AJ BJ CJ ).
a Se acord : 40% din punctaj pentru determinarea corect  a num rului T ³i 100% din punctaj
pentru rezolvarea corect  a ambelor cerinµe.
a Se garanteaz  c  exist  soluµie pentru ambele cerinµe, pentru toate datele de test.

Exemple:
traseu.in traseu.out Explicaµii
3 4 7 Hotelul are trei etaje (1,2 ³i 3). Pe ecare etaj sunt 3*3 camere.
1 1 1 1 1 1 ³oricelul se a  în camera cu eticheta 1 1 1 iar camera cu alimente
3 3 3 1 1 2 are eticheta 3 3 3.
3 3 1 1 1 3 Sunt 4 camere ocupate de cli-
2 1 1 1 2 3 enµi. Acestea au etichetele : 3
3 1 1 1 3 3 3 1, 2 1 1, 3 1 1, 3 1 3.
3 1 3 2 3 3 Traseul cel mai scurt trece
3 3 3 prin T=7 camere.
Sunt mai multe astfel de tra-
see. 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 lexicograc) 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

26.6.1 Indicaµii de rezolvare

prof. Carmen Minc , Colegiul Naµional de Informatic  Tudor Vianu, Bucure³ti

O soluµie se poate obµine astfel:


CAPITOLUL 26. ONI 2014 548

- Vom utiliza un masiv tridimensional A cu N*N*N componente (hotelul cu N*N*N camere),


care iniµial are toate elementele A[x][y][z]=0.
- Se poate borda masivul cu -1 pentru a se evita ie³irea în afara poziµiilor camerelor hotelului
- Marc m cu 1 poziµia camerei ³oricelului A[K1][L1][C1]=1.
- Pentru ecare camer  accesibil  (e x y z eticheta acestei camere) studiem cele maximum 6
camere vecine în care ar putea intra ³oricelul:

Figura 26.2: traseu

- Poate  accesibil  oricare din cele 6 camere cu dac  elemntul masivului corespunz tor eti-
chetei camerei este 0 sau este mai mare ca A[x][y][z]
- Vizit m ³i marc m ecare 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 nalul cozii poziµiile ec rei
camere accesibile, în ordinea vizit rii. Trecem la poziµia p+1 din coad  ³i acest procedeu.
- La nal, 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 lexicograc. 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
a³a în ordine invers .
3
Complexitate O N 

26.6.2 Cod surs 

Listing 26.6.1: traseu_carmen_nerec.cpp


1 #include <fstream>
2 #include <iostream>
3
4 using namespace std;
5
6 int a[105][105][105], p, u,i;
7 short int n,m,k1,l1,c1,k2,l2,c2,x,y,z,xc,yc,zc;
8 struct camera
9 {
10 short int x,y,z;
11 } c[1000005];
CAPITOLUL 26. ONI 2014 549

12 short int dx[]={-1,0,0,0,0,1}, dy[]={0,-1,0,0,1,0},dz[]={0,0,-1,1,0,0};


13
14 ofstream g("traseu.out");
15
16 void citeste()
17 {
18 ifstream f("traseu.in");
19 f>>n>>m;
20 f>>k1>>l1>>c1>>k2>>l2>>c2;
21 for(i=1;i<=m;i++)
22 {
23 f>>x>>y>>z;
24 a[x][y][z]=-1;
25 }
26 }
27
28 void bordare()
29 {
30 for(int i=0;i<=n+1;i++)
31 for(int j=0;j<=n+1;j++)
32 {
33 a[0][i][j]=-1;
34 a[n+1][i][j]=-1;
35 a[i][0][j]=-1;
36 a[i][n+1][j]=-1;
37 a[i][j][0]=-1;
38 a[i][j][n+1]=-1;
39 }
40 }
41
42 void coada()
43 {
44 p=u=1;
45 c[1].x=k1;
46 c[1].y=l1;
47 c[1].z=c1;
48 a[k1][l1][c1]=1;
49
50 while((p<=u)&& (a[k2][l2][c2]==0))
51 {
52 x=c[p].x;
53 y=c[p].y;
54 z=c[p++].z;
55 for(i=0;i<6;i++)
56 {
57 xc=x+dx[i];
58 yc=y+dy[i];
59 zc=z+dz[i];
60 if (a[xc][yc][zc]==0)
61 {
62 u++;
63 a[xc][yc][zc]=a[x][y][z]+1;
64 c[u].x=xc;
65 c[u].y=yc;
66 c[u].z=zc;
67 }
68 }
69 }
70 }
71
72 void drum(int val)
73 {
74 while(val>0)
75 {
76 if (a[x-1][y][z]==val-1)
77 --x;
78 else
79 if (a[x][y-1][z]==val-1)
80 --y;
81 else
82 if (a[x][y][z-1]==val-1)
83 --z;
84 else
85 if (a[x][y][z+1]==val-1)
86 ++z;
87 else
CAPITOLUL 26. ONI 2014 550

88 if (a[x][y+1][z]==val-1)
89 ++y;
90 else
91 if (a[x+1][y][z]==val-1)
92 ++x;
93 --val;
94 c[val].x=x;
95 c[val].y=y;
96 c[val].z=z;
97 }
98 }
99
100 int main()
101 {
102 citeste();
103 bordare();
104 coada();
105
106 int val=a[k2][l2][c2];
107 g<<val<<endl;
108 c[val].x=k2;
109 c[val].y=l2;
110 c[val].z=c2;
111 drum(val);
112
113 for(int i=1;i<=val; i++)
114 g<<c[i].x<<’ ’<<c[i].y<<’ ’<<c[i].z<<endl;
115
116 g.close();
117 return 0;
118 }

Listing 26.6.2: traseu_carmen_rec.cpp


1 #include <fstream>
2 #include <iostream>
3
4 using namespace std;
5
6 int a[105][105][105], p, u,i;
7 short int n,m,k1,l1,c1,k2,l2,c2,x,y,z,xc,yc,zc;
8 struct camera
9 {
10 short int x,y,z;
11 } c[1000005];
12 short int dx[]={-1,0,0,0,0,1}, dy[]={0,-1,0,0,1,0},dz[]={0,0,-1,1,0,0};
13
14 ofstream g("traseu.out");
15
16 void citeste()
17 {
18 ifstream f("traseu.in");
19 f>>n>>m;
20 f>>k1>>l1>>c1>>k2>>l2>>c2;
21 for(i=1;i<=m;i++)
22 {
23 f>>x>>y>>z;
24 a[x][y][z]=-1;
25 }
26 }
27
28 void bordare()
29 {
30 for(int i=0;i<=n+1;i++)
31 for(int j=0;j<=n+1;j++)
32 {
33 a[0][i][j]=-1;
34 a[n+1][i][j]=-1;
35 a[i][0][j]=-1;
36 a[i][n+1][j]=-1;
37 a[i][j][0]=-1;
38 a[i][j][n+1]=-1;
39 }
40 }
41
CAPITOLUL 26. ONI 2014 551

42 void coada()
43 {
44 p=u=1;
45 c[1].x=k1;
46 c[1].y=l1;
47 c[1].z=c1;
48 a[k1][l1][c1]=1;
49 while((p<=u)&&(a[k2][l2][c2]==0))
50 {
51 x=c[p].x;
52 y=c[p].y;
53 z=c[p++].z;
54 for(i=0;i<6;i++)
55 {
56 xc=x+dx[i];
57 yc=y+dy[i];
58 zc=z+dz[i];
59 if (a[xc][yc][zc]==0)
60 {
61 u++;
62 a[xc][yc][zc]=a[x][y][z]+1;
63 c[u].x=xc;
64 c[u].y=yc;
65 c[u].z=zc;
66 }
67 }
68 }
69 }
70
71 void drum(short int x, short int y, short int z, int val)
72 {
73 if(val>1)
74 {
75 if (a[x-1][y][z]==val-1) drum(x-1,y,z,val-1);
76 else
77 if (a[x][y-1][z]==val-1) drum(x,y-1,z,val-1);
78 else
79 if (a[x][y][z-1]==val-1) drum(x,y,z-1,val-1);
80 else
81 if (a[x][y][z+1]==val-1) drum(x,y,z+1,val-1);
82 else
83 if (a[x][y+1][z]==val-1) drum(x,y+1,z,val-1);
84 else
85 if (a[x+1][y][z]==val-1) drum(x+1,y,z,val-1);
86
87 g<<x<<’ ’<<y<<’ ’<<z<<endl;
88 }
89 }
90
91 int main()
92 {
93 citeste();
94 bordare();
95 coada();
96
97 int val=a[k2][l2][c2];
98 g<<val<<endl;
99 g<<k1<<’ ’<<l1<<’ ’<<c1<<endl;
100 drum(k2,l2,c2,val);
101
102 //cout<<val;
103 g.close();
104 return 0;
105 }

Listing 26.6.3: traseu_eugen.cpp


1 # include <fstream>
2 # include <queue>
3 # define inf 20000003
4
5 using namespace std;
6
7 ifstream f("traseu.in");
8 ofstream g("traseu.out");
CAPITOLUL 26. ONI 2014 552

9
10 struct cel
11 {
12 short k, l, c;
13 };
14
15 short dx[]={-1, 0, 1, 0, 0, 0};
16 short dy[]={ 0, 1, 0,-1, 0, 0};
17 short dz[]={ 0, 0, 0, 0, 1,-1};
18 bool a[102][102][102];
19 int M[102][102][102], T[102][102][102];
20 short n, m, k1, l1, c1, k2, l2, c2;
21
22 queue <cel> q;
23
24 void lee()
25 {
26 short k;
27 cel x, y;
28 x.k = k1; x.l = l1; x.c = c1;
29 M[x.k][x.l][x.c] = 1;
30 T[x.k][x.l][x.c] = -1;
31 q.push(x);
32
33 while(!q.empty())
34 {
35 x = q.front(); q.pop();
36 for (k=0; k<6; ++k)
37 {
38 y.k = x.k + dz[k];
39 y.l = x.l + dx[k];
40 y.c = x.c + dy[k];
41 if (a[y.k][y.l][y.c] == 0)
42 if (M[x.k][x.l][x.c] + 1 < M[y.k][y.l][y.c])
43 {
44 if (T[y.k][y.l][y.c] == 0)
45 T[y.k][y.l][y.c] = x.k*1000000 + x.l*1000 + x.c;
46 else
47 {
48 if(T[y.k][y.l][y.c] > x.k*1000000+x.l*1000+x.c)
49 T[y.k][y.l][y.c] = x.k*1000000+x.l*1000+x.c;
50 }
51
52 M[y.k][y.l][y.c] = M[x.k][x.l][x.c] + 1;
53 q.push(y);
54 }
55 else
56 if (M[x.k][x.l][x.c] + 1 == M[y.k][y.l][y.c])
57 {
58 if (T[y.k][y.l][y.c] > x.k*1000000 + x.l*1000 + x.c)
59 T[y.k][y.l][y.c] = x.k*1000000 + x.l*1000 + x.c;
60 }
61 }
62 }
63 }
64
65 void afis(short k, short l, short c)
66 {
67 short k2, l2, c2;
68 int x;
69 if (k == k1 && l == l1 && c == c1)
70 g << k << " " << l << " " << c << "\n";
71 else
72 {
73 x = T[k][l][c];
74 k2 = x / 1000000;
75 l2 = x / 1000 % 1000;
76 c2 = x % 1000;
77 afis(k2, l2, c2);
78 g << k << " " << l << " " << c << "\n";
79 }
80 }
81
82 int main()
83 {
84 short k, l, c;
CAPITOLUL 26. ONI 2014 553

85 f >> n >> m;
86 for (k=0; k<=n+1; ++k)
87 for (l=0; l<=n+1; ++l)
88 for (c=0; c<=n+1; ++c)
89 {
90 M[k][l][c] = inf;
91 if (k==0 || l==0 || c==0)
92 a[k][l][c] = 1;
93 if (k==n+1 || l==n+1 || c==n+1)
94 a[k][l][c] = 1;
95 }
96
97 f >> k1 >> l1 >> c1;
98 f >> k2 >> l2 >> c2;
99
100 while (m--)
101 {
102 f >> k >> l >> c;
103 a[k][l][c] = 1;
104 }
105
106 lee();
107
108 g << M[k2][l2][c2] << "\n";
109 afis(k2, l2, c2);
110 return 0;
111 }

Listing 26.6.4: traseu_vspit.cpp


1 #include<fstream>
2
3 using namespace std;
4
5 ifstream fin("traseu.in");
6 ofstream fout("traseu.out");
7
8 int N, M, K1, L1, C1, K2, L2, C2, K, L, C, k, l, c, T;
9 int A[103][103][103];
10 char dc[6]={ 0, 0,-1,+1, 0, 0};
11 char dl[6]={ 0,-1, 0, 0,+1, 0};
12 char dk[6]={-1, 0, 0, 0, 0,+1};
13
14 struct eticheta
15 {
16 char K, L, C;
17 };
18
19 eticheta tail[1000001],poz;
20 int tf, tl, tlen, i, j;
21
22 int main()
23 {
24 fin>>N>>M>>K1>>L1>>C1>>K2>>L2>>C2;
25 for(i=1;i<=M;i++)
26 {
27 fin>>K>>L>>C;
28 A[K][L][C]=-1;
29 }
30
31 A[K1][L1][C1]=1*10;
32 tail[0].K=K1;
33 tail[0].L=L1;
34 tail[0].C=C1;
35 tf=0;
36 tl=0;
37 tlen=1;
38 while(A[K2][L2][C2]==0 && tlen>0)
39 {
40 k=tail[tf].K;
41 l=tail[tf].L;
42 c=tail[tf].C;
43 for (j=0;j<=5;j++)
44 {
45 K=k+dk[j];
CAPITOLUL 26. ONI 2014 554

46 L=l+dl[j];
47 C=c+dc[j];
48 if(1<=K && K<=N && 1<=L && L<=N &&
49 1<=C && C<=N && A[K][L][C]==0)
50 {
51 A[K][L][C]=j + (1 + A[k][l][c] / 10) * 10;
52 tl++;
53 tail[tl].K=K;
54 tail[tl].L=L;
55 tail[tl].C=C;
56 tlen++;
57 if(K==K2 && L==L2 && C==C2)break;
58 }
59 }
60
61 tf++;
62 tlen--;
63 }
64
65 T=A[K2][L2][C2]/10;
66 K=K2;
67 L=L2;
68 C=C2;
69
70 for(i=T;i>=1;i--)
71 {
72 tail[i].K=K;
73 tail[i].L=L;
74 tail[i].C=C;
75 j=A[K][L][C]%10;
76 j=5-j;
77 K=K+dk[j];
78 L=L+dl[j];
79 C=C+dc[j];
80 }
81
82 fout<<T<<"\n";
83 for (i=1;i<=T;i++)
84 {
85 fout<<(int)tail[i].K<<" "<<(int)tail[i].L<<" "<<(int)tail[i].C<<"\n";
86 }
87
88 fout.close();
89 fin.close();
90 return 0;
91 }

26.6.3 *Rezolvare detaliat 


Capitolul 27

ONI 2013

27.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 cres-
c 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 a  pe poziµia i, iar sticluµa fi se a 
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 ec 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 ecare linie se vor aa dou 
numere diferite x ³i y din intervalul 1, 2 ˜ N , semnicând faptul c  Marian trebuie s  interschimbe
sticluµa de pe poziµia x cu sticluµa de pe poziµia y .

Restricµii ³i preciz ri
a 2 & N & 100 000
a Dac  exist  mai multe soluµii, se poate a³a oricare dintre ele.
a Soluµia nu trebuie s  fac  neap rat num rul minim de operaµii.
a Pentru 15% din teste se garanteaz  c  N & 13.
a Pentru alte 25% din teste se garanteaz  c  N & 300.
a Pentru alte 30% din teste se garanteaz  c  N & 1000.

555
CAPITOLUL 27. ONI 2013 556

Exemple:
aranjare.in aranjare.out Explicaµii
3 24 În explicaµia de mai jos, ecare 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

27.1.1 Indicaµii de rezolvare

stud. Cosmin Mihai Tutunaru, Universitatea Babe³ Bolyai Cluj

Se observ  c  valorile sticluµelor din ³ierul de intrare nu inuenµeaz  cu nimic rezolvarea


problemei, deoarece ele trebuie rearanjate doar dup  etichet , deci atât conguraµia iniµial  cât ³i
cea nal  se pot deduce cunoscând doar valoarea N .
Conguraµie iniµial : r1 r2 r3 ...rN f1 f2 f3 ...fN
Conguraµie nal : r1 f1 r2 f2 r3 f3 ...rN fN
Putem calcula pentru orice sticluµ  poziµia nal  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 a  sticluµa ce trebuie a³ezat  pe poziµia i

La ecare 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 conguraµia iniµial  la conguraµia nal .
Complexitate: O N 

27.1.2 Cod surs 

Listing 27.1.1: aranjare.cpp


1 # include <fstream>
2 # include <iostream>
3
4 # define DIM 100003
5
6 using namespace std;
7
8 int n, v[2*DIM], p[2*DIM];
9
10 void read ()
11 {
12 ifstream fin ("aranjare.in");
13 fin>>n;
14 }
15
16 int main()
17 {
18 read ();
19 ofstream fout ("aranjare.out");
20
21 for(int i=1;i<=2*n;++i)
22 p[i]=i,
23 v[i]=i;
24
25 for(int i=1;i<n;++i)
26 {
CAPITOLUL 27. ONI 2013 557

27 if (v[2*i]!=n+i)
28 {
29 v[p[n+i]]=v[2*i];
30 p[v[2*i]]=p[n+i];
31 fout<<2*i<<" "<<p[n+i]<<"\n";
32 }
33
34 if (v[2*i+1]!=i+1)
35 {
36 v[p[i+1]]=v[2*i+1];
37 p[v[2*i+1]]=p[i+1];
38 fout<<2*i+1<<" "<<p[i+1]<<"\n";
39 }
40 }
41
42 return 0;
43 }

27.1.3 *Rezolvare detaliat 

27.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 ecare. Orice pom fructifer putea  identicat
cunoscând num rul rândului pe care se a  ³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 ec ruia,
determinaµi num rul maxim de pomi dintr-o gr din  p trat  de latur  K ³i num rul modurilor
în care poate  amplasat  gr dina cu num rul maxim de pomi.

Date de intrare
Fi³ierul gradina.in conµine:
- pe prima linie numerele naturale N , P ³i K , separate prin câte un spaµiu, cu semnicaµia din
enunµ;
- pe urm toarele P linii câte 2 numere naturale Lin ³i Col, separate printr-un spaµiu, repre-
zentând num rul rândului, respectiv poziµia în rând a ec rui pom din livad .

Date de ie³ire
Fi³ierul gradina.out va conµine:
- pe prima linie num rul maxim de pomi fructiferi dintr-o gr din  p trat  cu latura de K
metri;
- pe a doua linie num rul de posibilit µi de a amplasa gr dina astfel încât s  conµin  num rul
maxim de pomi determinat.

Restricµii ³i preciz ri
a 2 & N & 1000
1&P &N
2
a
a 1&K&N

Exemple:
CAPITOLUL 27. ONI 2013 558

gradina.in gradina.out Explicaµii


12 10 5 5 Gr dina lui P cal  poate avea maximum 5 pomi fructiferi.
43 5 Ea poate  amplasat  în 5 moduri, având colµul stânga-sus de
55 coordonate:
68 (5, 3), (5, 4), (5, 5), (6, 6), (7, 3).
73
77
88
93
96
10 10
11 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

27.2.1 Indicaµii de rezolvare

prof. Alin Burµa - C.N. B.P. Hasdeu Buz u

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 ecare 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:

npomi S ij   S i  k j   S ij  k   S i  k j  k 

Pe m sur  ce calcul m, actualiz m num rul maxim de pomi ³i num rul de soluµii.


2
Complexitatea algoritmului este O N .
Se pot utiliza ³i alte idei de rezolvare de complexitate mai mare. De exemplu, putem contoriza
3
num rul pomilor din ecare p trat de latur  K , îns  algoritmul are complexitatea O N  ³i va
obµine cel mult 60p.
Dac  se folose³te c utarea brut  a gradinii cerute, determinând pentru ecare poziµie i, j 
4
num rul pomilor din gr dina ce are colµul stânga-sus în aceast  poziµie, complexitatea va  O N ,
iar algoritmul va obµine cel mult 30p.

27.2.2 Cod surs 

Listing 27.2.1: gradina.cpp


1 # include <fstream>
2 # include <cstdio>
3
4 # define DIM 1003
5
6 using namespace std;
7
8 int n, p, k, a[DIM][DIM], pmax, nsol;
9
10 void read ()
11 {
12 FILE *fin=fopen("gradina.in","rt");
13 fscanf(fin,"%d%d%d",&n,&p,&k);
14
15 for(int i=1;i<=p;++i)
16 {
17 int x, y;
18 fscanf(fin,"%d%d",&x,&y);
19 a[x][y]=1;
CAPITOLUL 27. ONI 2013 559

20 }
21
22 fclose(fin);
23 }
24
25 void solve ()
26 {
27 for(int i=1;i<=n;++i)
28 for(int j=1;j<=n;++j)
29 a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];
30
31 for(int i=k;i<=n;++i)
32 for(int j=k;j<=n;++j)
33 {
34 int np = a[i][j]-a[i-k][j]-a[i][j-k]+a[i-k][j-k];
35 if (np>pmax)
36 {
37 pmax=np;
38 nsol=1;
39 }
40 else
41 if (np==pmax)
42 ++nsol;
43 }
44 }
45
46 int main ()
47 {
48 read ();
49 solve ();
50
51 FILE *fout = fopen("gradina.out","wt");
52 fprintf(fout,"%d\n%d\n",pmax,nsol);
53 fclose(fout);
54 return 0;
55 }

27.2.3 *Rezolvare detaliat 

27.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 ecare secvenµ  s  conµin  cel puµin
dou  elemente. Mai exact, se identic  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 ecare secvenµ  se determin 
costul ei ca ind 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  e 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 a  trei numere naturale i, j ³i k , separate
prin câte un spaµiu, cu semnicaµia din enunµ.

Restricµii ³i preciz ri
CAPITOLUL 27. ONI 2013 560

8 & N & 5 000


a
0 & ai & 100 000 000, pentru orice i 1..N
a
a O secvenµ  poate avea costul 0 (valoarea maxim  egal  cu valoarea minim )
a Dac  exist  mai multe soluµii cu aceea³i sum  maxim , atunci se va alege soluµia cu i minim.
Dac  exist  mai multe soluµii cu acela³i i minim, se alege aceea cu j minim, iar dac  exist  mai
multe soluµii cu acela³i i ³i j minim, se alege aceea cu k minim.

Exemple:
split.in split.out Explicaµ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

27.3.1 Indicaµii de rezolvare

prof. Dan Pracsiu - Liceul ³tefan Procopiu Vaslui

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 ecare j (4 & j & n  4):
avom c uta poziµia i plecând de la j spre stânga ³i determinând la ecare 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 ecare 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 .

27.3.2 Cod surs 

Listing 27.3.1: split.cpp


1 /*
2 Complexitate O (N x N)
3 */
4 #include<fstream>
5
6 #define inFile "split.in"
7 #define outFile "split.out"
8 #define Dim 10005
9
10 using namespace std;
11
12 int a[Dim], minst[Dim], mindr[Dim], maxdr[Dim], maxst[Dim], n;
13
14 int main()
15 {
16 int i, j, k, x, di, dk, maxim, minim, xi, xj, xk;
CAPITOLUL 27. ONI 2013 561

17 int solst, soldr, solmax;


18
19 // citire
20 ifstream fin(inFile);
21 fin >> n;
22 for (i = 1; i <= n; i++)
23 fin >> a[i];
24 fin.close();
25
26 //min max stg
27 minst[1] = maxst[1] = a[1];
28 for (i = 2; i <= n; i++)
29 {
30 if (a[i] > maxst[i-1])
31 maxst[i] = a[i];
32 else
33 maxst[i] = maxst[i-1];
34
35 if (a[i] < minst[i-1])
36 minst[i] = a[i];
37 else
38 minst[i] = minst[i-1];
39 }
40
41 //min max drp
42 mindr[n] = maxdr[n] = a[n];
43 for (i = n - 1; i >= 1; i--)
44 {
45 if (a[i] > maxdr[i+1])
46 maxdr[i] = a[i];
47 else
48 maxdr[i] = maxdr[i+1];
49
50 if (a[i] < mindr[i+1])
51 mindr[i] = a[i];
52 else
53 mindr[i] = mindr[i+1];
54 }
55
56 // calcul
57 solmax = 0;
58 di = dk = 0;
59 xi = 2; xj = 4; xk = 6;
60 for (j = 4; j <= n - 3; j++)
61 {
62 // maximul din stanga: i=2..j-2
63 if (a[j] > a[j - 1])
64 {
65 maxim = a[j];
66 minim = a[j - 1];
67 }
68 else
69 {
70 maxim = a[j - 1];
71 minim = a[j];
72 }
73
74 solst = maxim - minim + maxst[j-2] - minst[j-2];
75 di = j - 2;
76 for (i = j - 2; i > 2; i--)
77 {
78 if (a[i] > maxim) maxim = a[i];
79 if (a[i] < minim) minim = a[i];
80 x = maxim - minim + maxst[i-1] - minst[i-1];
81 if (solst <= x)
82 {
83 solst = x;
84 di = i-1;
85 }
86 }
87
88 //maximul din dreapta k=j+2..n-2
89 if (a[j+1] > a[j+2])
90 {
91 maxim = a[j+1];
92 minim = a[j+2];
CAPITOLUL 27. ONI 2013 562

93 }
94 else
95 {
96 maxim = a[j+2];
97 minim = a[j+1];
98 }
99
100 soldr = maxim - minim + maxdr[j+3] - mindr[j+3];
101 dk = j + 2;
102 for (k = j + 3; k <= n - 2; k++)
103 {
104 if (a[k] > maxim) maxim = a[k];
105 if (a[k] < minim) minim = a[k];
106 x = maxim - minim + maxdr[k+1] - mindr[k+1];
107 if (soldr < x)
108 {
109 soldr = x;
110 dk = k;
111 }
112 }
113
114 x = solst + soldr;
115 if (x > solmax)
116 {
117 solmax = x;
118 xi = di;
119 xj = j;
120 xk = dk;
121 }
122 }
123
124 ofstream fout(outFile);
125 fout << solmax << "\n";
126 fout << xi << " " << xj << " " << xk << "\n";
127 fout.close();
128
129 return 0;
130 }

27.3.3 *Rezolvare detaliat 

27.4 momente
Problema 4 - momente 100 de puncte
G are un ceas digital care a³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 a³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 a³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 aate 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  ae câte astfel de momente sunt într-un interval de timp dat.
Un interval de timp este situat pe parcursul anului 2013 ind 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 a³at  de ceasul digital al lui G.

Cerinµe
Determinaµi câte momente palidromice au loc în k intervale de timp date.
CAPITOLUL 27. ONI 2013 563

Date de intrare
Fi³ierul de intrare momente.in conµine pe prima linie num rul natural k cu semnicaµia din
enunµ. Pe ecare dintre urm toarele k linii se a  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 aa un singur
num r care va reprezenta num rul de momente palindromice din intervalul i.

Restricµii ³i preciz ri
a k & 100 000
a data de început precede data de sfâr³it pentru ecare interval de timp;
a în anul 2013 luna februarie are 28 zile;
a pentru 50% dintre teste vom avea k 1;
a se nume³te palindrom un num r care citit de la stânga la dreapta sau de la dreapta la stânga
are aceea³i valoare.
a dac  intervalul de timp considerat începe sau se termin  cu un moment palindromic, acesta
este num rat.

Exemple:
momente.in momente.out Explicaµii
1 24 Fi³ierul de intrare conµine un singur interval de timp, intre
2 28 23 44 32 28 februarie ora 23, 44 minute ³i 32 secunde ³i 1 martie
3 1 0 02 02 ora 0, 2 minute ³i 2 secunde.
In acest interval de timp sunt 24 momente palindomice
dupa cum urmeaza:
- în data de 28 februarie la orele 23 44 32 ³i 23 55 32;
- în data de 1 martie la orele 0 00 00, 0 00 01, 0 00 02, 0
Timp maxim de executare/test: 1.2 secunde
Memorie: total 16 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 5 KB

27.4.1 Indicaµii de rezolvare

prof. Stelian Ciurea, Universitatea Lucian Blaga, Sibiu

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 ecare 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 );

2 câte momente palindromice sunt în prima zi a intervalului dat;

3 câte momente palindromice sunt în ultima zi intervalului dat.

Pentru 2 ³i 3 putem folosi:

- un tablou precalculat în care memor m num rul de palindromuri pentru ecare 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 27. ONI 2013 564

- 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 a³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 ec rui interval din secund  în secund ) obµin
cel mult 20 puncte.

27.4.2 Cod surs 

Listing 27.4.1: momente.pas


1 { momente palindromice varianta 2: k intervale;
2 solutie "oficiala-oficiala": pentru fiecare interval
3 determina cate zile complete sunt cu 699 momente /zi
4 precalculeaza numarul de palindroame in fiecare moment al zilei
5 apoi determina cate momente sunt in prima zi de la momentul initial
6 pana la 23:59:59 si in ultima de la 0:00:00 pana la momentul final
7 }
8
9 const zifin : array[1..12] of integer = (31,28,31,30,31,30,31,31,30,31,30,31);
10
11 var ora1,min1,sec1,luna1,zi1,ora2,min2,sec2,luna2,zi2 : longint;
12 zi,luna,ora,min,sec : longint;
13 st,dr,mj,i,k,nr,ctz,ct1,ct2,ct3 : longint;
14 fi,fo : text;
15 mom : array[-1..90000] of longint;
16
17
18 function inv(nr : longint) : longint;
19 begin
20 inv := 0;
21 while nr>0 do
22 begin
23 inv := inv*10+nr mod 10;
24 nr := nr div 10
25 end;
26 end;
27
28
29 procedure precalc;
30 var ora,min,sec,i:longint;
31
32 begin
33 ora := 0;
34 min := 0;
35 sec := 1;
36
37 for i := 1 to 24*3600-1 do
38 begin
39
40 nr := ora*10000 + min*100 + sec;
41 if inv(nr) = nr then
42 mom[i] := mom[i-1]+1
43 else
44 mom[i] := mom[i-1];
45
46 inc(sec);
47 if sec=60 then
48 begin
49 sec := 0;
50 inc(min)
51 end;
52 if min=60 then
53 begin
54 min := 0;
55 inc(ora);
56 end;
57 end;
58 end;
CAPITOLUL 27. ONI 2013 565

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.

27.4.3 *Rezolvare detaliat 

27.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 27. ONI 2013 566

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
S  se determine L1 , L2 , ..., LN .

Date de intrare
Fi³ierul secvente.in conµine pe prima linie num rul natural N . Pe ecare din urm toarele N
linii se g se³te câte un num r natural, deci pe linia i  1 se va aa elementul ai , pentru i 1...N .

Date de ie³ire
Fi³ierul secvente.out conµine exact N linii. Pe linia i (i 1...N ) se va a³a valoarea Li .

Restricµii ³i preciz ri
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

27.5.1 Indicaµii de rezolvare

stud. Cosmin Mihai Tutunaru, Universitatea Babe³ Bolyai Cluj

Soluµie 35 pct
O prim  idee ar  s  avem un vector de vizite pentru a ³ti la ecare pas dac  un element se
a  în secvenµa curent  (care începe pe poziµia 1). Un algoritm de rezolvare este urm torul:

ˆ best = 0, lungimea secvenµei de valori consecutive pentru secvenµa vid 

ˆ pentru ecare i de la 1 la N , unde i este cap tul drept al secvenµei

` 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 27. ONI 2013 567

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:

ˆ 0, dac  elementul x nu se a  în secvenµa curent 


ˆ poziµia unde se termin  secvenµa, dac  x este primul element din secvenµ 
ˆ poziµia unde începe secvenµa, dac  x este ultimul element din secvenµ 

Pentru valorile x care aparµin unei secvenµe de valori consecutive dar nu se a  pe marginile
secvenµei, se observ  c  posx este irelevant.
Noul algoritm de rezolvare este:

ˆ best = 0
ˆ pentru ecare 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 a  deja într-o secvenµ 
t begin posv i  1, actualiz m începutul secvenµei în care se a  elementul v i
` dac  posv i  1! 0, adic  elementul v i  1 se a  deja într-o secvenµ 
t end posv i  1, actualiz m sfâr³itul secvenµei în care se a  elementul v i
` deci elementul v i se a  î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

Complexitate: O N  V M AX , unde V M AX este valoarea maxim  din ³ir

27.5.2 Cod surs 

Listing 27.5.1: secvente.cpp


1 #define TuTTy "Cosmin-Mihai Tutunaru"
2 #include <cstdio>
3 #include <cassert>
4 #include <vector>
5 #include <algorithm>
6
7 #define infile "secvente.in"
8 #define outfile "secvente.out"
9 #define nMax 200013
10 #define vMax 1000013
11
12 using namespace std;
13
14 bool vz[vMax];
15
16 int v[nMax];
17 int pos[vMax];
18 int n;
19
20 void read()
21 {
22
23 scanf("%d\n", &n);
24
CAPITOLUL 27. ONI 2013 568

25 for (int i = 0; i < n; ++i)


26 {
27 scanf("%d", &v[i+1]);
28
29 assert(vz[v[i+1]] == false);
30 vz[v[i+1]] = true;
31 }
32 }
33
34 void solve()
35 {
36
37 int best = 0;
38
39 for (int i = 1; i <= n; ++i)
40 {
41 int begin = v[i], end = v[i];
42
43 if (pos[v[i]-1]) begin = pos[v[i]-1];
44 if (pos[v[i]+1]) end = pos[v[i]+1];
45
46 pos[begin] = end;
47 pos[end] = begin;
48
49 best = max(best, end - begin + 1);
50 printf("%d\n", best);
51 }
52 }
53
54 int main()
55 {
56 freopen(infile, "r", stdin);
57 freopen(outfile, "w", stdout);
58
59 read();
60 solve();
61
62 fclose(stdin);
63 fclose(stdout);
64 return 0;
65 }

27.5.3 *Rezolvare detaliat 

27.6 spider
Problema 6 - spider 100 de puncte
Omul p ianjen (Spiderman) sare de pe o cl dire pe alta, aat  î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 ecare 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 27. ONI 2013 569

Date de ie³ire
Fi³ierul de ie³ire spider.out va conµine, pe prima linie, un singur num r natural k , repre-
zentând num rul maxim de s rituri. Urm toarele k linii vor conµine câte dou  numere naturale,
separate printr-un spaµiu, reprezentând coordonatele cl dirilor care formeaz  drumul maxim, în
ordinea parcurgerii. Dac  exist  mai multe soluµii, în ³ierul de ie³ire se va scrie drumul care
porne³te de la poziµia i, j  cu i minim, iar dac  exist  mai multe soluµii cu acela³i i minim, se va
scrie în ³ier soluµia cu i ³i j de valoare minim .

Restricµii ³i preciz ri
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:
spider.in spider.out Explicaµii
55 8 Spiderman porne³te de pe blocul de 90 de metri aat în
35 3842 40 50 54 poziµia (5, 4), face 8 s rituri ³i ajunge în poziµia (1, 4), de
34 3830 75 50 53 unde nu mai are posibilit µi de a s ri.
70 7888 86 30 43
39 9088 23 25 33
35 8089 90 34 34
24
25
15
14
Timp maxim de executare/test: 3.0 secunde
Memorie: total 64 MB din care pentru stiv  8 MB
Dimensiune maxim  a sursei: 5 KB

27.6.1 Indicaµii de rezolvare

Budai István, Liceul Teoretic Nagy Mózes

Soluµie 35 pct
Se denesc 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 veric  î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  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 denesc 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 27. ONI 2013 570

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 ³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 ecare 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 semnicaµ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, ecare valoare semnicând
una din cele 4 direcµii posibile).
Complexitate: O N ˜ M 

27.6.2 Cod surs 

Listing 27.6.1: spider.cpp


1 #define TuTTy "Cosmin-Mihai Tutunaru"
2 #include <cstdio>
3 #include <vector>
4 #include <algorithm>
5
6 #define infile "spider.in"
7 #define outfile "spider.out"
8 #define nMax 1013
9
10 using namespace std;
11
12 #define point pair <int, int>
13
14 point outside = make_pair(-1, -1);
15
16 const int ii[] = {-1, 0, 1, 0};
17 const int jj[] = {0, 1, 0, -1};
18
19 int best[nMax][nMax][5];
20 int h[nMax][nMax];
21 int n, m;
22
23 point sol;
24 int steps;
25
26 inline bool valid(point p)
27 {
28 if (p.first < 1 || p.first > n) return false;
29 if (p.second < 1 || p.second > m) return false;
30 return true;
31 }
32
33 inline int getHeight(point p)
34 {
35
36 if (p == outside)
37 {
38 return -1;
39 }
40
41 return h[p.first][p.second];
42 }
43
44 pair<point, int> getNextPosition(point crt, point prv)
45 {
46
CAPITOLUL 27. ONI 2013 571

47 point ret = outside;


48 int dir = -1;
49
50 for (int t = 0; t < 4; ++t)
51 {
52 point nxt = make_pair(crt.first + ii[t], crt.second + jj[t]);
53 if (nxt != prv && valid(nxt) &&
54 getHeight(ret) < getHeight(nxt) &&
55 getHeight(nxt) <= getHeight(crt))
56 {
57 ret = nxt;
58 dir = t;
59 }
60 }
61
62 return make_pair(ret, dir);
63 }
64
65 void read()
66 {
67
68 scanf("%d %d\n", &n, &m);
69
70 for (int i = 0; i < n; ++i)
71 {
72 for (int j = 0; j < m; ++j)
73 {
74 scanf("%d", &h[i+1][j+1]);
75 }
76 }
77 }
78
79 int solve(int x, int y, int d)
80 {
81
82 if (best[x][y][d])
83 {
84 return best[x][y][d];
85 }
86
87 best[x][y][d] = 1;
88 point prv;
89
90 if (d == 4)
91 {
92 prv = outside;
93 }
94 else
95 {
96 prv = make_pair(x + ii[d], y + jj[d]);
97 }
98
99 pair<point, int> nxt = getNextPosition(make_pair(x, y), prv);
100
101 if (nxt.first != outside)
102 {
103 best[x][y][d] +=
104 solve(nxt.first.first, nxt.first.second, (nxt.second + 2) % 4);
105 }
106
107 return best[x][y][d];
108 }
109
110 void solve()
111 {
112
113 for (int i = 0; i < n; ++i)
114 {
115 for (int j = 0; j < m; ++j)
116 {
117 int dist = solve(i+1, j+1, 4);
118 if (dist > steps)
119 {
120 steps = dist;
121 sol = make_pair(i+1, j+1);
122 }
CAPITOLUL 27. ONI 2013 572

123 }
124 }
125
126 }
127
128 void write()
129 {
130
131 printf("%d\n", steps - 1);
132
133 point prv = outside;
134
135 while(sol != outside)
136 {
137 printf("%d %d\n", sol.first, sol.second);
138 point aux = getNextPosition(sol, prv).first;
139
140 prv = sol;
141 sol = aux;
142 }
143 }
144
145 int main()
146 {
147 freopen(infile, "r", stdin);
148 freopen(outfile, "w", stdout);
149
150 read();
151 solve();
152 write();
153
154 fclose(stdin);
155 fclose(stdout);
156 return 0;
157 }

27.6.3 *Rezolvare detaliat 


Capitolul 28

ONI 2012

28.1 7segmente
Problema 1 - 7segmente 100 de puncte
Un indicator cu 7 segmente este un dispozitiv de a³aj electronic destinat
a³ rii unei cifre zecimale. Aceste dispozitive sunt utilizate pe scar  larg  în
ceasuri digitale, contoare electronice ³i alte aparate, pentru a³area informaµiilor
numerice. Cele 7 segmente au fost notate cu literele a, b, c, d, e, f, g, dup 
modelul din gura al turat . A³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 a³aj trebuie s  µin  cont ³i de puterea necesar  pentru
a³area unei cifre. Pentru aprinderea unui segment este necesar  o putere de 1 mW. Astfel, în
funcµie de cifra a³at , dispozitivul necesit  o putere egal  cu num rul de segmente aprinse la
a³area cifrei respective. Puterea necesar  pentru a³area unui num r natural este egal  cu suma
puterilor necesare a³ rii ec 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 a³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 a³are o putere de cel mult p mW .

Date de intrare
Prima linie a ³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 semnicaµ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 semnicaµia de
mai sus.

Restricµii ³i preciz ri
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:

573
CAPITOLUL 28. ONI 2012 574

7segmente.in 7segmente.outExplicaµii
7654 12 18 7511 Num rul n este 7654; puterea necesar  pentru a³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 a³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

28.1.1 Indicaµii de rezolvare

prof. Che³c  Ciprian - Grup ³colar Costin Neniµescu Buz u

Num rul de dispozitive de tip 7 segmente necesare pentru a³area num rului n este egal cu
num rul de cifre al lui n. Puterea necesar  pentru a³area num rului n se calculeaz  ca suma
puterilor necesare pentru a³area ec rei cifre în parte, în funcµie de num rul de segmente utilizat
la a³area ec 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 ind 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 aat, 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 semnicativ  c tre cifra cea mai puµin semnicativ ) 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  e 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 ecare 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  e mai
mici decât cifrele lui n de pe poziµiile corespunz toare.

28.1.2 Cod surs 

Listing 28.1.1: PIT_7segmente.c


1 // sursa 100 p - PIT-RADA VASILE
2
3 #include<stdio.h>
4 #include<string.h>
5
6 char s[30];
7 int p,w,pc,i,prest,j,ok,ok2;
8 int x[10]={0,2,5,5,4,5,6,3,7,6};
9
10 int main()
11 {
12 freopen("7segmente.in","r",stdin);
13 freopen("7segmente.out","w",stdout);
14
15 scanf("%s %d",s,&p);
16 w=strlen(s);
17
18 pc=0;
19 for (i=0;i<w;i++)
20 pc+=x[s[i]-’0’];
21 printf("%d ",pc);
CAPITOLUL 28. ONI 2012 575

22
23 prest=p;
24 ok=0;
25 ok2=1;
26 for (i=0;i<w;i++)
27 {
28 for (j=(s[i]-’0’)*ok2+9*(1-ok2);j>0;j--)
29 if (x[j]+(w-i-1)*2<=prest)
30 break;
31
32 if (j>0)
33 {
34 ok=1;
35 printf("%d",j);
36 }
37 if (j==0 && ok==1)
38 printf("%d",j);
39 if (j<s[i]-’0’)
40 ok2=0;
41 prest-=x[j];
42 }
43
44 fclose(stdin);
45 fclose(stdout);
46 return 0;
47 }

Listing 28.1.2: CC_7segmente.cpp


1 // sursa 100 puncte - Chesca Ciprian
2
3 #include <fstream>
4 #include <math.h>
5
6 using namespace std;
7
8 typedef unsigned short cifra;
9
10 ifstream f("7segmente.in");
11 ofstream g("7segmente.out");
12
13 // v = vector in care obtin cifrele lui n
14 // t = vector in care formez numarul t
15 cifra v[20],t[20],p=0;
16
17
18 // vectorul puterilor necesare la afisarea unei cifre
19 // (desi este prezenta - nu folosesc cifra 0)
20 cifra pw[10]={6,2,5,5,4,5,6,3,7,6};
21
22 unsigned long long n,cc=1;
23
24 int main()
25 {
26
27 cifra pc=0,i=0,nc=0,aux=0,dmin=0,dmax=0,cifra,egalcif=1;
28
29 f>>n>>p;
30
31 v[0]=0;
32 i=0;
33 while (n>0)
34 {
35 v[++i]=n%10;
36 switch (n%10)
37 {
38 case 1 : pc=pc+2;break;
39 case 2 : pc=pc+5;break;
40 case 3 : pc=pc+5;break;
41 case 4 : pc=pc+4;break;
42 case 5 : pc=pc+5;break;
43 case 6 : pc=pc+6;break;
44 case 7 : pc=pc+3;break;
45 case 8 : pc=pc+7;break;
46 case 9 : pc=pc+6;break;
CAPITOLUL 28. ONI 2012 576

47 }
48
49 n=n/10;
50 }
51
52 v[0]=i;
53 g<<pc<<" ";
54
55 // oglindesc cifrele
56 for(i=1;i<=v[0]/2;i++)
57 {
58 aux=v[i];
59 v[i]=v[v[0]+1-i];
60 v[v[0]+1-i]=aux;
61 }
62
63 // calculez numarul de cifre(nc) maxim pe care-l poate avea t
64 nc=p/2;
65 if (nc>=v[0]) nc=v[0];
66
67 // setez toate cifrele la 1
68 for(i=1;i<=nc;i++)
69 t[i]=1;
70
71 // din puterea data scad puterea utilizata pentru cifrele de 1
72 p=p-2*nc;
73
74 t[0]=nc;cc=1;
75 egalcif=1;
76 while (p>0&&cc<=t[0])
77 {
78 dmin=10;cifra=1;dmax=0;
79
80 // caut cifra corespunzatoare
81 for(i=2;i<=9;i++)
82 {
83 if (t[0]==v[0]&&egalcif==1)
84 if (i<=v[cc]&&abs(i-v[cc])<=dmin&&p-pw[i]+pw[1]>=0)
85 {
86 cifra=i;
87 dmin=abs(i-v[cc]);
88 }
89
90 if (t[0]<v[0]||egalcif==0)
91 if (i>=dmax&&p-pw[i]+pw[1]>=0)
92 {
93 cifra=i;
94 dmax=i;
95 }
96 }
97
98 // daca am setat o cifra mai mica, restul cifrelor pot fi mai mari
99 if (v[cc]-cifra!=0)
100 egalcif=0;
101
102 // asez cifra pe pozitia corecta si recalculez puterea
103 p+=pw[1];
104 p-=pw[cifra];
105 t[cc++]=cifra;
106 }
107
108 for(i=1;i<=t[0];i++)
109 g<<t[i];
110
111 g<<"\n";
112
113 f.close();
114 g.close();
115 return 0;
116 }

Listing 28.1.3: RH_7segmente.cpp


1 //sursa 100 p - Robert Hasna
2
CAPITOLUL 28. ONI 2012 577

3 #include <cstdio>
4
5 using namespace std;
6
7 static const int NMAX = 20;
8
9 int N, P;
10 int x[] = { 0, 2, 5, 5, 4, 5, 6, 3, 7, 6 };
11 int v[NMAX];
12
13 int cer1;
14 int rez[NMAX], nrCifre;
15
16 int main(int argc, char **argv)
17 {
18 freopen("7segmente.in", "r", stdin);
19 freopen("7segmente.out", "w", stdout);
20
21 char c = ’1’;
22 while (1)
23 {
24 scanf("%c", &c);
25 if (c == ’ ’)
26 break;
27 v[N++] = c - ’0’;
28 }
29 scanf("%d", &P);
30
31 //cerinta 1
32 for (int i = 0; i < N; cer1 += x[v[i++]]);
33 printf("%d ", cer1);
34
35 //cerinta 2
36 nrCifre = (P / 2) < N ? (P / 2) : N;
37
38 if (nrCifre < N)
39 {
40 printf("%c", P & 1 ? ’7’ : ’1’);
41 for (int i = 0; i < nrCifre - 1; printf("1"), ++i)
42 ;
43 return 0;
44 }
45
46 P -= N << 1;
47 bool miss = false;
48 int i;
49 for (i = 0; i < N; ++i)
50 {
51 P += 2;
52 int j = miss ? 9 : v[i];
53 while (x[j] > P)
54 --j;
55
56 rez[i] = j;
57 P -= x[j];
58
59 if (rez[i] != v[i])
60 miss = true;
61 }
62
63 for (int i = 0; i < N; ++i)
64 printf("%d", rez[i]);
65 printf("\n");
66
67 return 0;
68 }

28.1.3 *Rezolvare detaliat 


CAPITOLUL 28. ONI 2012 578

28.2 copaci
Problema 2 - copaci 100 de puncte
Se consider  n copaci de diferite în lµimi, aaµi în linie dreapt  la distanµe egale, numerotaµi
de la 1 la n. Pentru ecare copac se cunoa³te în lµimea sa Hi . Cum ³i copacii simt nevoia s 
socializeze, ecare dintre ei are prieteni printre ceilalµi copaci.
Prietenii oric rui copac i se pot aa atât la stânga, cât ³i la dreapta sa. Relaµiile de prietenie
sunt denite în felul urm tor: pentru ecare 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 denesc 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 ecare 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 ecare 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 gura de mai jos sunt reprezentaµi 7 copaci, ecare 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 ind 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 copacului 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, e 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µ.

Restricµii ³i preciz ri
CAPITOLUL 28. ONI 2012 579

a 1 & n & 200.000;


a 1 & Hi & 200;
a nu vor exista 2 copaci al turaµi cu aceea³i în lµime;
a dou  triplete de copaci se consider  distincte dac  exist  cel puµin un copac din primul triplet
care nu se a  ³i în al doilea triplet;
a pentru 30% din teste, 1 & n & 200.

Exemple:
copaci.in copaci.out Explicaµ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

28.2.1 Indicaµii de rezolvare


Vlad Ionescu

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  x m copacul B ca ind 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 x m copacul B, iar A ³i C le stabilim ca ind 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  veric m concret
dac  A ³i C sunt prieteni reciproci. Pentru aceasta folosim 2 vectori ST i ³i DRi care semnic 
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 ecient, cu o stiv  în complexitate O N . Ambele metode intr  în
timp.
Complexitate nal : O N ˜ Hmax sau O N .

28.2.2 Cod surs 

Listing 28.2.1: PIT_copaci.c


1 #include<stdio.h>
2
3 unsigned char h[200005];
4 int n,i,j,k;
5 int nr=0;
6
7 int main()
8 {
9 freopen("copaci.in","r",stdin);
10 freopen("copaci.out","w",stdout);
11
12 scanf("%d",&n);
13 for (i=1;i<=n;i++) scanf("%d",&h[i]);
14
15 for (j=2;j<n;j++)
16 {
17 /*caut in stanga primul i cu h[i]>=h[j]*/
18 i=j-1;
19 while (0<i && h[i]<h[j])i--;
20 /*caut in dreapta primul k cu h[j]<=h[k]*/
CAPITOLUL 28. ONI 2012 580

21 k=j+1;
22 while (k<=n && h[j]>h[k])k++;
23 if (i>0 && k<=n &&
24 h[i]>h[j] &&
25 h[j]<h[k]) // trebuie sa avem h[i]!=h[j] si h[j]!=h[k],
26 // putem avea insa h[i]==h[k]
27 nr++;
28 }
29
30 printf("%d",nr);
31 fclose(stdout);
32 fclose(stdin);
33 return 0;
34 }

Listing 28.2.2: RH_copaci.cpp


1 #include <cstdio>
2 #include <stack>
3 #include <utility>
4
5 using namespace std;
6
7 static const int NMAX = 200001;
8
9 int N, rez;
10 int h[NMAX], st[NMAX], dr[NMAX];
11
12 stack< pair< int, int > > S;
13
14 int main(int argc, char **argv)
15 {
16 freopen("copaci.in", "r", stdin);
17 freopen("copaci.out", "w", stdout);
18
19 scanf("%d\n", &N);
20 for (int i = 0; i < N; scanf("%d ", h + i), ++i);
21
22 for (int i = 0; i < N; ++i)
23 {
24 while (!S.empty() && h[i] > S.top().first)
25 {
26 dr[S.top().second] = 1;
27 S.pop();
28 }
29
30 if (!S.empty() && h[i] == S.top().first)
31 S.pop();
32 else
33 S.push(make_pair(h[i], i));
34 }
35
36 while (!S.empty()) S.pop();
37
38 for (int i = N-1; i >=0; --i)
39 {
40 while (!S.empty() && h[i] > S.top().first)
41 {
42 st[S.top().second] = 1;
43 S.pop();
44 }
45
46 if (!S.empty() && h[i] == S.top().first)
47 S.pop();
48 else
49 S.push(make_pair(h[i], i));
50 }
51
52 for (int i = 0; i < N; ++i)
53 rez += st[i] && dr[i] ? 1 : 0;
54
55 printf("%d\n", rez);
56
57 return 0;
58 }
CAPITOLUL 28. ONI 2012 581

Listing 28.2.3: VI_copaci.cpp


1 #include <iostream>
2 #include <algorithm>
3 #include <cstdio>
4 #include <cstring>
5
6 using namespace std;
7
8 #define maxn 200010
9 #define valmax 210
10
11 int N, sol;
12 int ST[maxn], DR[maxn], H[maxn];
13 int val[valmax];
14
15 /** functii calc_st() si calc_dr() se pot implementa eficient
16 folosind o stiva **/
17 void calc_st()
18 {
19 memset(val, 0, sizeof(val));
20 int i, j, p;
21
22 for(i=1; i<=N; i++)
23 {
24 p = 0;
25 for(j=H[i]; j<valmax; j++)
26 {
27 p = max(p, val[j]);
28 }
29
30 ST[i] = p;
31 val[ H[i] ] = i;
32 }
33 }
34
35 void calc_dr()
36 {
37 memset(val, 0, sizeof(val));
38 int i, j, p;
39
40 for(i=N; i>=1; i--)
41 {
42 p = N+1;
43 for(j=H[i]; j<valmax; j++)
44 {
45 if(val[j]) p = min(p, val[j]);
46 }
47
48 DR[i] = p;
49 val[ H[i] ] = i;
50 }
51 }
52
53 int main()
54 {
55 FILE *f1=fopen("copaci.in", "r"), *f2=fopen("copaci.out", "w");
56 int i, j, p, q;
57
58 fscanf(f1, "%d\n", &N);
59 for(i=1; i<=N; i++)
60 {
61 fscanf(f1, "%d", &H[i]);
62 }
63
64 calc_st();
65 calc_dr();
66
67 for(i=2; i<=N-1; i++)
68 {
69 // consideram copacul i ca fiind de inaltime minima
70 // din triunghiul (A, i, B)
71 // poate exista cel mult un triunghi centrat in i
72 p = ST[i], q = DR[i];
73 if(p == 0 || q == N+1) continue;
74
CAPITOLUL 28. ONI 2012 582

75 //trebuie sa verificam daca p vede pe i, daca q vede pe i


76 //si daca p si q se vad reciproc
77 if(ST[i] <= p && DR[i] >= q && DR[p] >= q && ST[q] <= p)
78 {
79 //cout<<p<<" "<<i<<" "<<q<<endl;
80 sol ++;
81 }
82 }
83
84 fprintf(f2, "%d\n", sol);
85
86 fclose(f1); fclose(f2);
87 return 0;
88 }

28.2.3 *Rezolvare detaliat 

28.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 seg-
mente de dreapt  cu cele patru colµuri ale dreptunghiului. Unele
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 gura 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
ecare 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 (reprezen-
tând dimensiunile dreptunghiului) ³i n. Urm toarele n linii conµin câte un num r natural xi cu
semnicaµia de mai sus.

Date de ie³ire
Fi³ierul de ie³ire intersectii.out va conµine n linii. Pe ecare linie i va  scris num rul de
segmente care trec prin exact xi p trate 2-intersectate, obµinute dup  plasarea punctului P în
ecare colµ al unui p trat de latur  1 din interiorul dreptunghiului ABCD.

Restricµii ³i preciz ri
a 2 & w, h & 2 000 numere naturale pare;
a 2 & n & 100 000;
a punctul P se alege doar în interiorul dreptunghiului;
a pentru 40% din teste 2 & w, n, h & 500.

Exemple:
CAPITOLUL 28. ONI 2012 583

intersectii.in intersectii.out Explicaµii


46235 12 4 Se pot obµine 12 segmente care trec prin exact 3 p trate
2-intersectate ³i 4 segmente care trec prin exact 3 p trate
2-intersectate.
Timp maxim de executare/test: 0.2 secunde
Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 5 KB

28.3.1 Indicaµii de rezolvare

prof. Gheorghe Manolache - Colegiul Naµional de Informatic  , Piatra Neamµ

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 x, deci putem studia doar cazul colµului A iar la
nal 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  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 ecare
test în timp 1.
Deci complexitatea algoritmului devine O h ˜ w  n. .

28.3.2 Cod surs 

Listing 28.3.1: PIT1_intersectii.c


1 #include<stdio.h>
2
3 int s[5000],W,H,Q,i,j,x,y,r,k,H12,W12,H2,W2;
4
5 int main()
6 {
7 freopen("intersectii.in","r",stdin);
8 freopen("intersectii.out","w",stdout);
9 scanf("%d%d%d",&W,&H,&Q);
10
11 W2=W/2;
12 H2=H/2;
13 H12=(H-1)/2;
14 W12=(W-1)/2;
15 for (i=1;i<=W12;i++)
16 for (j=1;j<=H12;j++)
17 {
18 x=i;
19 y=j;
20 while (y){r=x%y; x=y; y=r;}
21 s[i+j-x]+=4;
22 x=W-i;
23 y=j;
24 while (y){r=x%y; x=y; y=r;}
25 s[W-i+j-x]+=4;
26 x=i;
27 y=H-j;
28 while (y){r=x%y; x=y; y=r;}
29 s[i+H-j-x]+=4;
CAPITOLUL 28. ONI 2012 584

30 x=W-i;
31 y=H-j;
32 while (y){r=x%y; x=y; y=r;}
33 s[W-i+H-j-x]+=4;
34 }
35
36 if (W%2==0)
37 for (j=1;j<=H12;j++)
38 {
39 x=W2;
40 y=j;
41 while (y){r=x%y; x=y; y=r;}
42 s[W2+j-x]+=4;
43
44 x=W2;
45 y=H-j;
46 while (y){r=x%y ;x=y; y=r;}
47 s[W2+H-j-x]+=4;
48 }
49
50 if (H%2==0)
51 for (i=1;i<=W12;i++)
52 {
53 x=i;
54 y=H2;
55 while (y){r=x%y; x=y; y=r;}
56 s[i+H2-x]+=4;
57
58 x=W-i;
59 y=H2;
60 while (y){r=x%y; x=y; y=r;}
61 s[W-i+H2-x]+=4;
62 }
63
64 if (W%2==0 && H%2==0)
65 {
66 x=W2;
67 y=H2;
68 while (y){r=x%y; x=y; y=r;}
69 s[W2+H2-x]+=4;;
70 }
71
72 for (i=1;i<=Q;i++)
73 {
74 scanf("%d",&k);
75 printf("%d\n",s[k]);
76 }
77
78 fclose(stdout);
79 fclose(stdin);
80 return 0;
81 }

Listing 28.3.2: PIT2_intersectii.c


1 #include<stdio.h>
2
3 int s[5000],W,H,Q,i,j,x,y,r,k;
4
5 int main()
6 {
7 freopen("intersectii.in","r",stdin);
8 freopen("intersectii.out","w",stdout);
9 scanf("%d%d%d",&W,&H,&Q);
10
11 for (i=1;i<=W-1;i++)
12 {
13 if (i<W && i<H)s[i]++;
14 for (j=i+1;j<=H-1;j++)
15 {
16 x=i; y=j;
17 while (y){r=x%y; x=y; y=r;}
18 s[i+j-x]++;
19 if(i<H && j<W)
20 s[i+j-x]++;
CAPITOLUL 28. ONI 2012 585

21 }
22 }
23
24 for (i=1;i<=Q;i++)
25 {
26 scanf("%d",&k);
27 printf("%d\n",4*s[k]);
28 }
29
30 fclose(stdout);
31 fclose(stdin);
32 return 0;
33 }

Listing 28.3.3: brut_intersectii.cpp


1 #include<cstdio>
2 #include<bitset>
3
4 using namespace std;
5
6 int H,W,Q,i,j,n,I,J,N,cnt[4010],poz,m1,m2,m3,m4;
7
8 int euclid(int a, int b)
9 {
10 int c;
11 while(b)
12 {
13 c=a%b;
14 a=b;
15 b=c;
16 }
17 return a;
18 }
19
20 int main()
21 {
22 freopen("intersectii.in","r",stdin);
23 freopen("intersectii.out","w",stdout);
24 scanf("%d%d%d",&W,&H,&Q);
25
26 for(i=1;i<W;++i)
27 for(j=1;j<H;++j)
28 {
29 m1=i+j-euclid(i,j);
30 m2=i+H-j-euclid(i,H-j);
31 m3=W-i+H-j-euclid(W-i,H-j);
32 m4=W-i+j-euclid(W-i,j);
33 cnt[m1]++;
34 cnt[m2]++;
35 cnt[m3]++;
36 cnt[m4]++;
37 }
38
39 poz=W+H;
40 //for(i=1;i<=poz;++i) cnt[i]+=cnt[i-1];
41 for(;Q;Q--)
42 {
43 scanf("%d",&i);
44 if(i>poz)
45 printf("0\n");
46 else
47 printf("%d\n",cnt[i]);
48 }
49
50 return 0;
51 }

Listing 28.3.4: GM_intersectii.cpp


1 #include<cstdio>
2 #include<bitset>
3
4 using namespace std;
CAPITOLUL 28. ONI 2012 586

5
6 int H,W,NT,i,j,n,I,J,N,c[4110],poz;
7
8 bitset<2110> v[2110];
9
10 int main()
11 {
12 freopen("intersectii.in","r",stdin);
13 freopen("intersectii.out","w",stdout);
14 scanf("%d%d%d",&W,&H,&NT);
15
16 for(i=1;i<W;i++)
17 for(j=1;j<H;j++)
18 if(!v[i][j])
19 {
20 n=i+j-1;
21 for(I=i,J=j,N=n;I<W && J<H;I+=i,J+=j,N+=n)
22 {
23 v[I][J]=1;
24 c[N]++;
25 if(poz<N) poz=N;
26 }
27 }
28
29 //for(i=1;i<=poz;++i) c[i]+=c[i-1];
30 for(;NT;NT--)
31 {
32 scanf("%d",&i);
33 if(i>poz)
34 printf("0\n");
35 else
36 printf("%d\n",4*c[i]);
37 }
38
39 return 0;
40 }

Listing 28.3.5: VI_intersectii.cpp


1 #include <iostream>
2 #include <algorithm>
3 #include <cstdio>
4 #include <cstring>
5
6 using namespace std;
7
8 #define maxn 2048
9
10 int W, H, T, N;
11 int res[maxn], viz[maxn], d[maxn], a[maxn];
12
13 int main()
14 {
15 FILE *f1=fopen("intersectii.in", "r"), *f2=fopen("intersectii.out", "w");
16 int i, j, p, q, k;
17
18 fscanf(f1, "%d %d %d\n", &W, &H, &T);
19
20 N = max(W, H) + 1;
21
22 for(i=1; i<=N; i++)
23 d[i] = 1;
24
25 for(i=2; i<=N; i++)
26 {
27 if(!viz[i])
28 {
29 for(j=i; j<=N; j+=i)
30 {
31 viz[j] = 1;
32 d[j] = i;
33 }
34 }
35 }
36
CAPITOLUL 28. ONI 2012 587

37 for(i=1; i<H; i++)


38 {
39 memset(a, 0, sizeof(a));
40
41 for(j=1; j<W; j++)
42 {
43 if(d[j] == 1 || d[i] == 1)
44 a[j] = 1;
45
46 p = j / d[j];
47 q = i / a[p];
48
49 if(q % d[j] == 0)
50 a[j] = d[j] * a[p];
51 else
52 a[j] = a[p];
53
54 if(i == 1)
55 {
56 p = j;
57 res[p] += 4;
58 }
59 else
60 if(j == 1)
61 {
62 p = i;
63 res[p] += 4;
64 }
65 else
66 if(i == j)
67 {
68 p = i;
69 res[p] += 4;
70 }
71 else
72 {
73 p = i + j - a[j];
74 res[p] += 4;
75 }
76 }
77 }
78
79 while(T--)
80 {
81 fscanf(f1, "%d\n", &p);
82 fprintf(f2, "%d\n", res[p]);
83 }
84
85 fclose(f1);
86 fclose(f2);
87 return 0;
88 }

28.3.3 *Rezolvare detaliat 

28.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  Tna-
map. Locuitorii acestei µ ri puteau s  aplice instantaneu transform ri asupra cifrelor unui num r,
utilizând un tablou de corespondenµe T.
O cifr  c a unui num r poate  înlocuit  cu
cifra corespunz toare ei, T c.
Dalv ³i Sogard, doi indivizi speciali ai acestei societ µi ciudate se aau în drum spre INO când
au con³tientizat c  pot transforma instantaneu, folosind num r minim de transform ri de cifre,
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?
CAPITOLUL 28. ONI 2012 588

Cerinµe
Cunoscând valorile T0 , T1 , ..., T9 , num rul ce urmeaz  a  transformat N ³i num rul K
(divizorul palindromului), determinaµi:
1. Num rul maxim care se poate obµine aplicând transform ri succesive num rului N dat.
2. Cel mai mare dintre palindromurile divizibile cu K , ce se pot obµine din num rul N ,
efectuând un num r minim de transform ri asupra cifrelor num rului dat, respectiv asupra cifrelor
numerelor obµinute pe parcurs.

Date de intrare
Pe prima linie a ³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.

Restricµii ³i preciz ri
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:
palindrom.in palindrom.out Explicaµii
0465127893 4994
1234 4224 1234 4234 4634 4734 4834 4934 4954
3 4924 4964 4974 4984 4994
Num rul N trece prin urm toarele st ri înainte de a de-
veni palindrom cu valoarea maxim , divizibil cu 3: 1234
4234 4254 4224.
Timp maxim de executare/test: 0.4 secunde
Memorie: total 256 MB din care pentru stiv  64 MB
Dimensiune maxim  a sursei: 10 KB

28.4.1 Indicaµii de rezolvare

student Robert Hasna


student Vlad Ionescu
student Cosmin Tutunaru
student Vlad Duµ 
student Drago³ Opric 
student Alexandru Cazacu

Se precalculeaz  elementele matricei ajunge, dup  relaµia


ajungeij  = num rul minim de transform ri ce pot  efectuate pornind de la cifra i pentru
a obtine cifra j .
Având aceasta matrice calculat , primul subpunct este trivial, r mâne de v zut pentru ecare
pereche de cifre din ³irul iniµial i, N  i  1, cu i de la 1 la N ©2 (unde N = lungimea ³irului)
care este cea mai mare cifr  care se poate obµine prin transform ri repetate, atât din Ai, cât ³i
din AN  i  1 (am notat cu A ³irul iniµial de lungime N ).
CAPITOLUL 28. ONI 2012 589

Pentru al doilea subpunct vom folosi o recurenµ  începând de la jum tatea ³irului spre început,
Dij  = num rul minim de aplic ri pentru care ³irul cuprins intre poziµiile i ³i N  i  1 este
palindrom ³i d  restul j la împ rµirea prin K . Dac  pe poziµiile i, N  i  1 punem cifra q , atunci
restul obµinut la pasul anterior S devine S  q ˜ 10 N  i  10 i  1%K . Astfel se deduce
relaµia de recurenµ :

if(ajunge[ A[i] ][p] >= 0 && ajunge[ A[N-i+1] ][p] >= 0) {


//se poate ajunge la cifra p
//vechiul rest este j
int nou_rest = (j + p * (put_zece[N-i] + put_zece[i-1])) % K;
D[i][nou_rest] = min(D[i][nou_rest], D[i+1][j] + ajunge[cif1][p]
+ ajunge[cif2][p]);
}

unde i ia valori de la N ©2 la 1, j ia valori de la 1 la K ³i p ia valori de la 1 la 9. Astfel vom avea


în D10 num rul minim de aplic ri.
Pentru a reconstitui palindromul maxim divizibil cu K , repet m procedeul plecând de la
început spre mijloc (pentru a ne asigura c  se obµine cel mai mare palindrom), reµinând poziµia
pe care ne a m ³i restul corespunz tor secvenµei de palindrom r mase.
Complexitatea algoritmului: O N ˜ K .

28.4.2 Cod surs 

Listing 28.4.1: CTpalindrom.cpp


1 #define TuTTy "Cosmin-Mihai Tutunaru"
2 #include<cstdio>
3 #include<cstring>
4 #include<algorithm>
5
6 #define infile "palindrom.in"
7 #define outfile "palindrom.out"
8 #define nMax 1000013
9 #define kMax 23
10 #define sigma 10
11 #define inf (nMax * kMax)
12
13 using namespace std;
14
15 int p[sigma];
16 char s[nMax];
17
18
19 int co[sigma][sigma];
20 int dp[nMax][kMax];
21
22 char chr[nMax][kMax];
23 char prv[nMax][kMax];
24
25 char sol[nMax];
26
27 int t[nMax];
28 int n, k;
29
30 void read()
31 {
32 for(int i = 0; i < sigma; ++i)
33 {
34 scanf("%d", &p[i]);
35 }
36
37 scanf("\n");
38 scanf("%s\n", s + 1);
39 scanf("%d", &k);
40 }
41
42 void init()
43 {
44 n = strlen(s + 1);
CAPITOLUL 28. ONI 2012 590

45
46 t[0] = 1;
47 for(int i = 1; i <= n; ++i)
48 {
49 t[i] = (t[i-1] * 10) % k;
50 s[i] -= ’0’;
51 }
52
53 for(int i = 0; i < sigma; ++i)
54 {
55 for(int j = 0; j < sigma; ++j)
56 co[i][j] = inf;
57
58 int crt = i, cost = 0;
59 while(cost < co[i][crt])
60 {
61 co[i][crt] = cost++;
62 crt = p[crt];
63 }
64 }
65
66 for(int i = 1; i < k; ++i)
67 dp[(n>>1) + 1][i] = inf;
68 }
69
70 void solve()
71 {
72 for(int i = (n>>1); i >= 1; --i)
73 {
74 int first = i;
75 int last = n - i + 1;
76
77 for(int j = 0; j < k; ++j)
78 {
79 dp[i][j] = inf;
80
81 for(int d = sigma - 1; d >= 0 + (i == 1); --d)
82 {
83 int cost = co[s[first]][d] + co[s[last]][d];
84 int div = (d * t[n - first] + d * t[n - last]) % k;
85 int prvDiv = (j - div + k) % k;
86
87 if(cost >= inf || dp[i-1][prvDiv] >= inf)
88 continue;
89
90 cost += dp[i+1][prvDiv];
91 if(cost < dp[i][j])
92 {
93 dp[i][j] = cost;
94 chr[i][j] = d;
95 prv[i][j] = prvDiv;
96 }
97 }
98
99 //if(dp[i][j] != inf) printf("%d %d = %d\n", i, j, dp[i][j]);
100 }
101 }
102 }
103
104 void write()
105 {
106 printf("%d\n", dp[1][0]);
107 int last = 0;
108 for(int i = 1; i <= (n>>1); ++i)
109 {
110 sol[i] = sol[n - i + 1] = chr[i][last] + ’0’;
111 last = prv[i][last];
112 }
113
114 printf("%s\n", sol + 1);
115 }
116
117 int main()
118 {
119 freopen(infile, "r", stdin);
120 freopen(outfile, "w", stdout);
CAPITOLUL 28. ONI 2012 591

121
122 read();
123 init();
124 solve();
125 write();
126
127 fclose(stdin);
128 fclose(stdout);
129 return 0;
130 }

Listing 28.4.2: RHpalindrom.cpp


1 #include <stdio.h>
2
3 #define NMAX 1000001
4 #define KMAX 21
5
6 int P[10], K, x[NMAX], N;
7 int cost[10][10];
8 int compar[KMAX][KMAX];
9 int costMin[2][KMAX];
10 int rez[NMAX][KMAX];
11 int back[NMAX][KMAX];
12 int putere[NMAX];
13 int partial[KMAX][2];
14 int rezFinal[NMAX];
15 int cnt, k;
16
17 void citire()
18 {
19 freopen("palindrom.in", "r", stdin);
20 freopen("palindrom.out", "w", stdout);
21
22 for (int i = 0; i < 10; scanf("%d ", P + i), ++i);
23 while (true)
24 {
25 char c;
26 scanf("%c", &c);
27 if (c >= ’0’ && c<=’9’)
28 x[N++] = c - ’0’;
29 else
30 {
31 scanf("%d", &K);
32 break;
33 }
34 }
35 }
36
37 void preprocesareCosturiTranzitie()
38 {
39 int uz[10];
40
41 for (int i = 0; i < 10; ++i)
42 {
43 for (int j = 0; j < 10; uz[j] = 0, cost[i][j++] = -1);
44 int j = i;
45 int cc = 0;
46
47 cost[i][i] = 0;
48 uz[i] = 1;
49
50 while (uz[P[j]] == 0)
51 {
52 uz[P[j]] = 1;
53 cost[i][P[j]] = ++cc;
54 j = P[j];
55 }
56 }
57 }
58
59 void preprocesarePuteri10()
60 {
61 putere[0] = 1;
62 for (int i = 1; i < N; ++i)
CAPITOLUL 28. ONI 2012 592

63 putere[i] = (putere[i - 1] * 10) % K;


64 }
65
66 void cerintaA()
67 {
68 for (int i = 0; i < N; ++i)
69 {
70 int j = 9;
71 while (cost[x[i]][j] == -1)
72 --j;
73 printf("%d", j);
74 }
75
76 printf("\n");
77 }
78
79 void cerintaB()
80 {
81 preprocesarePuteri10();
82
83 costMin[1][0] = 0;
84 for (int i = 1; i < K; ++i)
85 costMin[1][i] = -1;
86
87 for (int i = 0, L = N / 2; i < L; ++i)
88 {
89 int st = x[i];
90 int dr = x[N - i - 1];
91 k = i & 1;
92
93 for (int ii = 0; ii < K; ++ii)
94 partial[ii][0] = partial[ii][1] =
95 costMin[k][ii] = rez[i][ii] = back[i][ii] = -1;
96
97 for (int j = 9; i == 0 ? j >= 1 : j >= 0; --j)
98 {
99 if (cost[st][j] != -1 && cost[dr][j] != -1)
100 {
101 int costTranf = cost[st][j] + cost[dr][j];
102 int restAdaugat = (j * putere[i] + j * putere[N-1-i]) % K;
103
104 if (partial[restAdaugat][0] == -1
105 || costTranf < partial[restAdaugat][0])
106 {
107 partial[restAdaugat][0] = costTranf;
108 partial[restAdaugat][1] = j;
109 }
110 }
111 }
112
113 for (int j = 0; j < K; ++j)
114 printf("part %d: %d %d\n", j, partial[j][0], partial[j][1]);
115
116 for (int j = 0; j < K; ++j)
117 {
118 for (int l = 0; l < K; ++l)
119 {
120 if (partial[l][0] == -1 || costMin[1-k][j] == -1)
121 continue;
122
123 int restTarget = (j + l) % K;
124
125 if (costMin[k][restTarget] == -1
126 || costMin[k][restTarget]
127 > costMin[1 - k][j] + partial[l][0])
128 {
129
130 costMin[k][restTarget] = costMin[1-k][j] + partial[l][0];
131 rez[i][restTarget] = partial[l][1];
132 back[i][restTarget] = j;
133 }
134
135 if (costMin[k][restTarget]
136 == costMin[1 - k][j] + partial[l][0])
137 {
138 if (compar[j][restTarget] == 0)
CAPITOLUL 28. ONI 2012 593

139 {
140 if (rez[i][restTarget] < partial[l][1])
141 {
142 costMin[k][restTarget] = costMin[1 - k][j]
143 + partial[l][0];
144 rez[i][restTarget] = partial[l][1];
145 back[i][restTarget] = j;
146 }
147 }
148 else
149 if (compar[j][restTarget] == 1)
150 {
151 costMin[k][restTarget] = costMin[1 - k][j]
152 + partial[l][0];
153 rez[i][restTarget] = partial[l][1];
154 back[i][restTarget] = j;
155 }
156 }
157 }
158 }
159
160 for (int j = 0; j < K; ++j)
161 printf("%d : %d %d %d\n", j,costMin[k][j],rez[i][j],back[i][j]);
162
163 //refac comparatiile
164 for (int j = 0; j < K; ++j)
165 for (int l = 0; l < K; ++l)
166 if (compar[j][l] == 0)
167 if (rez[i][j] > rez[i][l])
168 compar[j][l] = 1;
169 else if (rez[i][j] < rez[i][l])
170 compar[j][l] = -1;
171 }
172
173 int poz = 0;
174 int i = N / 2 - 1;
175 while (i >= 0)
176 {
177 rezFinal[cnt++] = rez[i][poz];
178 poz = back[i][poz];
179 --i;
180 }
181
182 for (int i = N / 2 - 1; i >= 0; --i)
183 printf("%d", rezFinal[i]);
184 for (int i = 0; i < N / 2; ++i)
185 printf("%d", rezFinal[i]);
186 printf("\n");
187 printf("%d\n", costMin[k][0]);
188 }
189
190 int main(int argc, char *argv)
191 {
192 citire();
193
194 preprocesareCosturiTranzitie();
195
196 cerintaA();
197 //cerintaB();
198
199 return 0;
200 }

Listing 28.4.3: VIpalindrom.cpp


1 #include <iostream>
2 #include <algorithm>
3 #include <cstdio>
4 #include <cstring>
5 #include <vector>
6
7 using namespace std;
8
9 #define maxn 100010
10 #define maxk 22
CAPITOLUL 28. ONI 2012 594

11 #define maxcifre 10
12 #define inf 99999999
13 #define pii pair<int, int>
14 #define pb push_back
15 #define mkp make_pair
16
17 int N, K;
18 char A[maxn], sol[maxn];
19 int perm[maxcifre], put[maxn];
20 int ajunge[maxcifre][maxcifre];
21 int D[maxn][maxk];
22
23 void preproc_puteri()
24 {
25 int i;
26
27 put[0] = 1;
28 for(i=1; i<=N; i++)
29 put[i] = (put[i-1] * 10) % K;
30 }
31
32 void preproc_ajunge()
33 {
34 int i, j, p;
35
36 for(i=0; i<maxcifre; i++)
37 for(j=0; j<maxcifre; j++)
38 ajunge[i][j] = -1;
39
40 for(i=0; i<maxcifre; i++)
41 {
42 ajunge[i][i] = 0;
43 p = i;
44 for(j=1; j<=100; j++)
45 {
46 p = perm[p];
47 if(p == i) break;
48 ajunge[i][p] = j;
49 }
50 }
51 }
52
53 int main()
54 {
55 FILE *f1=fopen("palindrom.in", "r"), *f2=fopen("palindrom.out", "w");
56 int i, j, p, q;
57
58 for(i=0; i<=9; i++)
59 fscanf(f1, "%d ", &perm[i]);
60
61 fscanf(f1, "%s\n", A+1);
62 N = strlen(A+1);
63
64 fscanf(f1, "%d\n", &K);
65
66 preproc_puteri();
67 preproc_ajunge();
68
69 int jumatate = N / 2;
70
71 //prima cerinta
72 for(i=jumatate; i>=1; i--)
73 {
74 int cif1 = A[i] - ’0’;
75 int cif2 = A[N-i+1] - ’0’;
76
77 int finish = 0;
78 if(i == 1) finish = 1;
79
80 for(q=9; q>=1; q--)
81 {
82 if(ajunge[cif1][q] >= 0 && ajunge[cif2][q] >= 0)
83 {
84 sol[i] = (q + ’0’);
85 break;
86 }
CAPITOLUL 28. ONI 2012 595

87 }
88 }
89
90 for(i=1; i<=jumatate; i++)
91 fprintf(f2, "%c", sol[i]);
92
93 for(i=jumatate; i>=1; i--)
94 fprintf(f2, "%c", sol[i]);
95
96 fprintf(f2, "\n");
97
98 //a doua cerinta
99 for(i=jumatate+1; i>=1; i--)
100 for(j=0; j<K; j++)
101 D[i][j] = inf;
102
103 D[jumatate+1][0] = 0;
104
105 for(i=jumatate; i>=1; i--)
106 {
107 int cif1 = A[i] - ’0’;
108 int cif2 = A[N-i+1] - ’0’;
109
110 for(j=0; j<K; j++)
111 {
112 int start = 0;
113 if(i == 1) start = 1; //prima cifra a palindromului nu poate fi 0
114
115 for(p=start; p<=9; p++)
116 if(ajunge[cif1][p] >= 0 && ajunge[cif2][p] >= 0)
117 {
118 int nou_rest = (j + p * (put[N-i] + put[i-1])) % K;
119 D[i][nou_rest] = min(D[i][nou_rest], D[i+1][j] +
120 ajunge[cif1][p] + ajunge[cif2][p]);
121 }
122 }
123 }
124
125 int cost = D[1][0];
126 int rest = 0;
127
128 if(cost >= N * 10)
129 {
130 fprintf(f2, "-1\n");
131 fclose(f1);
132 fclose(f2);
133 return 0;
134 }
135
136 for(i=1; i<=jumatate; i++)
137 {
138 //calculez sol[i]
139 int cif1 = A[i] - ’0’;
140 int cif2 = A[N-i+1] - ’0’;
141
142 int finish = 0;
143 if(i == 1) finish = 1;
144
145 for(q=9; q>=finish; q--)
146 {
147 if(ajunge[cif1][q] >= 0 && ajunge[cif2][q] >= 0)
148 {
149 p = (q * (put[N-i] + put[i-1])) % K;
150 int nou_rest = rest - p;
151 if(nou_rest < 0) nou_rest += K;
152
153 if(D[i+1][nou_rest]+ajunge[cif1][q]+ajunge[cif2][q] == cost)
154 {
155 //este ok ok ok
156 sol[i] = (q + ’0’);
157
158 cost -= (ajunge[cif1][q] + ajunge[cif2][q]);
159 rest = nou_rest;
160
161 break;
162 }
CAPITOLUL 28. ONI 2012 596

163 }
164 }
165 }
166
167 for(i=1; i<=jumatate; i++)
168 fprintf(f2, "%c", sol[i]);
169
170 for(i=jumatate; i>=1; i--)
171 fprintf(f2, "%c", sol[i]);
172
173 fprintf(f2, "\n");
174
175 fclose(f1);
176 fclose(f2);
177 return 0;
178 }

28.4.3 *Rezolvare detaliat 

28.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  aplicate, în acela³i condiµii, ³i asupra numerelor rezultate dup 
ecare î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, neind 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
Fi³ierul de ie³ire sstabil.out va conµine pe o linie num rul sstabil maxim obµinut.

Restricµii ³i preciz ri
a 1 & n & 1 000 000

Exemple:
sstabil.in sstabil.out Explicaµii
510451 191 10451 1091 191
552832 785
52832 7832 785
Timp maxim de executare/test: 1.0 secunde
Memorie: total 16 MB din care pentru stiv  4 MB
Dimensiune maxim  a sursei: 5 KB
CAPITOLUL 28. ONI 2012 597

28.5.1 Indicaµii de rezolvare

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: ...

28.5.2 Cod surs 

Listing 28.5.1: PRIVsstabil_1.cpp


1 #include<stdio.h>
2
3 int a[1000005],b[1000005];
4 int n,i,j,r,p,k,s,t;
5
6 int main()
7 {
8 freopen("sstabil.in","r",stdin);
9 freopen("sstabil.out","w",stdout);
10
11 scanf("%d\n",&n);
12 for (i=1;i<=n;i++)
13 scanf("%d",&a[i]);
14
15 k=0;
16 r=n;
17 while (r>0)
18 {
19 j=r;
20 s=0;
21 do
22 {
23 s=s+a[j];
24 j--;
25 } while (j>0 && s<10);
26
27 k++;
28 if (s<10)
29 {
30 b[k]=s;
31 r=j;
32 }
33 else
34 if (k==1)
35 {
36 p=r; t=a[p];
37 while (s-t>=10)
38 {
39 p--;
40 t=t+a[p];
41 }
42 b[k]=t;
43 r=p-1;
44 }
45 else
46 {
47 p=r; t=a[p];
48 while (s-t>9 || t+b[k-1]<10)
49 {
50 p--;
51 t=t+a[p];
52 }
53 b[k]=t;
54 r=p-1;
55 }
56 }
57
58 for (i=k;i>=1;i--)
59 printf("%d",b[i]);
60 fclose(stdout);
CAPITOLUL 28. ONI 2012 598

61 fclose(stdin);
62 return 0;
63 }

Listing 28.5.2: PRIVsstabil_2.cpp


1 #include<stdio.h>
2
3 int a[1000005],b[1000005];
4 int n,i,j,r,p,k,s,t;
5
6 int main()
7 {
8 freopen("sstabil.in","r",stdin);
9 freopen("sstabil.out","w",stdout);
10
11 scanf("%d\n",&n);
12 for (i=1;i<=n;i++)
13 scanf("%d",&a[i]);
14
15 a[0]=9;
16 a[n+1]=9;
17 b[0]=9;
18 k=0;
19 r=n;
20 while (r>0)
21 {
22 j=r; s=0;
23 do
24 {
25 s=s+a[j];
26 j--;
27 } while (s<10);
28
29 p=r;
30 t=a[p];
31
32 while (s-t>9 || t+b[k]<10)
33 {
34 p--;
35 t=t+a[p];
36 }
37
38 k++;
39 b[k]=t;
40 r=p-1;
41 }
42
43 for (i=k;i>=1;i--)
44 printf("%d",b[i]);
45
46 fclose(stdout);
47 fclose(stdin);
48 return 0;
49 }

Listing 28.5.3: VI_sstabil.cpp


1 #include<stdio.h>
2
3 int a[1000005],b[1000005];
4 int n,i,j,r,p,k,s,t;
5
6 int main()
7 {
8 freopen("sstabil.in","r",stdin);
9 freopen("sstabil.out","w",stdout);
10
11 scanf("%d\n",&n);
12 for (i=1;i<=n;i++)
13 scanf("%d",&a[i]);
14
15 a[0]=9;
16 a[n+1]=9;
CAPITOLUL 28. ONI 2012 599

17 b[0]=9;
18 k=0;
19 r=n;
20 while (r>0)
21 {
22 j=r; s=0;
23 do
24 {
25 s=s+a[j];
26 j--;
27 } while (s<10);
28
29 p=r;
30 t=a[p];
31
32 while (s-t>9 || t+b[k]<10)
33 {
34 p--;
35 t=t+a[p];
36 }
37
38 k++;
39 b[k]=t;
40 r=p-1;
41 }
42
43 for (i=k;i>=1;i--)
44 printf("%d",b[i]);
45
46 fclose(stdout);
47 fclose(stdin);
48 return 0;
49 }

28.5.3 *Rezolvare detaliat 

28.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, aate 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 ec rei 1-secvenµe s  e
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
Fi³ierul de ie³ire unuzero.out va conµine pe prima linie num rul natural K cerut.

Restricµii ³i preciz ri
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:
CAPITOLUL 28. ONI 2012 600

unuzero.in unuzero.out Explicaµii


523 8 0000110
0001100
0001110
0011000
0011100
0110000
0110110
0111000
Timp maxim de executare/test: 0.2 secunde
Memorie: total 16 MB din care pentru stiv  16 MB
Dimensiune maxim  a sursei: 5 KB

28.6.1 Indicaµii de rezolvare

prof. Gheorghe Manolache - Colegiul Naµional de Informatic  , Piatra Neamµ

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  evident 0.
Fie U i, num rul de conguraµii corecte de lungime i, terminate cu 1 ³i Z i, num rul de
conguraµii corecte de lungime i, terminate cu 0.
Atunci, avem relaµia de recurenµ  urm toare:
ia
U i = Z j 
j ib

Iniµial Z 0 1, Z 1 1 iar U 1 1 doar dac  a este 1;


2
Se obµine un algoritm de complexitate O n  care poate obµine 60 de puncte.
Pentru punctaj maxim algoritmul trebuie optimizat folosind sume parµiale ³i se obµine astfel
complexitate O n.

28.6.2 Cod surs 

Listing 28.6.1: GMunuab_100.cpp


1 // O(n)
2 #include <fstream>
3
4 using namespace std;
5
6 int mod=666013,a,b;
7 int s[1000100],u[1000100],z[1000100],n;
8
9 int main()
10 {
11 ifstream in("unuzero.in");
12 ofstream out("unuzero.out");
13
14 in>>n>>a>>b;
15 z[0]=1;
16 s[0]=1;
17 for(int i=1;i<=n;++i)
18 {
19 z[i]=z[i-1]+u[i-1]; // daca punem 0
20 z[i]%=mod;
21
22 if(i-a>=0)
23 if(i-b-1>=0)
24 u[i]=s[i-a]-s[i-b-1];
CAPITOLUL 28. ONI 2012 601

25 else
26 u[i]=s[i-a];
27
28 if(u[i]<0) u[i]+=mod;
29 u[i]%=mod;
30
31 s[i]=s[i-1]+z[i];
32 s[i]%=mod;
33 }
34
35 out<<(z[n]+u[n]-1+(z[n]+u[n]-1<0?mod:0))%mod<<’\n’;
36 out.close();
37 return 0;
38 }

Listing 28.6.2: GMunuab_back.cpp


1 #include <fstream>
2 using namespace std;
3
4 int n,a,b,sol;
5 int v[100100];
6
7 ifstream in("unuzero.in");
8 ofstream out("unuzero.out");
9
10 inline void verif()
11 {
12 int ok=0;
13 for(int i=1;i<=n;)
14 {
15 if(v[i]==1)
16 {
17 ok=1;
18 int sv=i,nr=0;
19 while(v[i]==v[sv] && i<=n) ++i,++nr;
20 if(!(nr>=a && nr<=b)) return ;
21 }
22 else ++i;
23 }
24
25 if(!ok) return;
26
27 sol++;
28 /*for(int i=1;i<=n;++i)
29 {
30 out<<v[i];
31 }
32 out<<’\n’;*/
33 }
34
35 void back(int k)
36 {
37 if(k>n)
38 verif();
39 else
40 for(int i=0;i<=1;++i)
41 {
42 v[k]=i;
43 back(k+1);
44 }
45 }
46
47 int main()
48 {
49 in>>n>>a>>b;
50 back(1);
51 out<<sol%666013<<’\n’;
52 out.close();
53 return 0;
54 }

Listing 28.6.3: GMunuab_n patrat.cpp


CAPITOLUL 28. ONI 2012 602

1 // O(n^2)
2 #include <fstream>
3
4 using namespace std;
5
6 int mod=666013,a,b;
7 int u[1000100],z[1000100],n;
8
9 int main()
10 {
11 ifstream in("unuzero.in");
12 ofstream out("unuzero.out");
13
14 in>>n>>a>>b;
15 z[0]=1;
16 z[1]=1;
17 if(a==1) u[1]=1;
18 for(int i=2;i<=n;++i)
19 {
20 z[i]=z[i-1]+u[i-1]; // daca punem 0
21 z[i]%=mod;
22 for(int j=i-b;j<=i-a;++j) // daca punem 1
23 {
24 if(j>=0)
25 {
26 u[i]+=z[j];
27 u[i]%=mod;
28 }
29 }
30 }
31
32 out<<(z[n]+u[n]-1+(z[n]+u[n]-1<0?mod : 0))%mod<<’\n’;
33 out.close();
34 return 0;
35 }

Listing 28.6.4: VI_unuzero.cpp


1 #include <iostream>
2 #include <algorithm>
3 #include <cstdio>
4 #include <cstring>
5
6 using namespace std;
7
8 #define maxn 1000010
9 #define mod 666013
10
11 int N, A, B;
12 int s[maxn], n[maxn];
13
14 int main()
15 {
16 FILE *f1=fopen("unuzero.in", "r"), *f2=fopen("unuzero.out", "w");
17 int i, p;
18
19 fscanf(f1, "%d %d %d\n", &N, &A, &B);
20 N ++;
21
22 n[0] = s[0] = 1;
23 for(i=1; i<=N; i++)
24 {
25 n[i] = n[i-1];
26 // negru pe pozitia i
27 // (i-1, i-2, ..., i-A, i-A-1, ..., i-B
28 if(i-A-1 >= 0)
29 {
30 if(i-B-2 >= 0)
31 {
32 n[i] += (s[i-A-1] - s[i-B-2] + mod) % mod;
33 }
34 else
35 {
36 n[i] += (s[i-A-1]) % mod;
37 }
CAPITOLUL 28. ONI 2012 603

38
39 n[i] %= mod;
40 }
41
42 s[i] = (s[i-1] + n[i]) % mod;
43 }
44
45 fprintf(f2, "%d\n", (mod + n[N] - 1) % mod);
46
47 fclose(f1);
48 fclose(f2);
49 return 0;
50 }

28.6.3 *Rezolvare detaliat 


Capitolul 29

ONI 2011

29.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 aate 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 aate pe
direcµia de tragere.

Cerinµe
“tiind c  tunul laser se g se³te în originea sistemului de
coordonate, s  se scrie un program care s  determine: num -
rul de µinte, num rul minim de lovituri de tun laser necesare
pentru a distruge toate µintele precum ³i num rul de µinte
doborâte la ecare lovitur .
Spre exemplu, dac  avem n 6 distanµe (5, 7, 10, 13, 15,
17) pentru care se încearc  plasarea µintelor, atunci în poligon
se vor plasa 10 µinte, va  nevoie de 6 lovituri pentru a doborî
toate µintele iar la ecare lovitur  se vor doborî respectiv 1,
1, 3, 3, 1, 1 µinte.

Date de intrare
Fi³ierul de intrare poligon.in conµine pe prima linie num rul n de distanµe la care vor 
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 ecare lovitur ,
separate printr-un spaµiu, în ordinea cresc toare a unghiurilor direcµiilor cu axa OX .

Restricµii ³i preciz ri
a 1 & n & 1000
a 1 & Ri & 1000
a pentru ecare set de date de intrare, în poligon va exista cel puµin o µint .
a se acord :
- 20% din punctaj pentru determinarea corect  a num rului de µinte.
- 40% din punctaj pentru determinarea corect  a num rului minim de lovituri.
- 40% din punctaj pentru determinarea corect  a num rului de µinte doborâte la ecare lovi-
tur .

604
CAPITOLUL 29. ONI 2011 605

Exemple:
poligon.in poligon.out Explicaµii
6 10 Avem 6 distante: 5,10,15,7,13,17.
5 10 15 7 13 17 6 În poligon vor  plasate 10 µinte (punctele negre marcate pe
113311 gur ) care pot  doborâte din 6 lovituri iar la ecare 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

29.1.1 Indicaµii de rezolvare

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 ecare 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 a³eaz  rezultatele.

29.1.2 Cod surs 

Listing 29.1.1: poligon.cpp


1 // sursa Pit-Rada Vasile - 100 p
2 #include<fstream>
3 #include<math.h>
4
5 using namespace std;
6
7 ifstream f1("poligon.in");
8 ofstream f2("poligon.out");
9
10 int n,i,c,s,aux,r[1001],j,k;
11 int x[10000],y[10000];
12
13
14 int main()
15 {
16 f1>>n;
17 c=0;
18 for (i=1;i<=n;i++)
19 {
20 f1>>r[i];
21 for (j=1;j<=r[i]-1;j++)
22 {
23 k=(int)sqrt((double)r[i]*r[i]-j*j);
24 if (k*k+j*j==r[i]*r[i])
25 {
26 c++;
27 x[c]=j; y[c]=k;
28
29 }
30 }
31 }
32
33 f2<<c<<"\n";
34 for (i=1;i<c;i++)
35 for (j=i+1;j<=c;j++)
36 if (x[i]*y[j]>x[j]*y[i])
CAPITOLUL 29. ONI 2011 606

37 {
38 aux=x[i]; x[i]=x[j]; x[j]=aux;
39 aux=y[i]; y[i]=y[j]; y[j]=aux;
40 }
41
42 x[0]=0; y[0]=1;
43 s=0;
44 for (i=1;i<=c;i++)
45 {
46 if (x[i-1]*y[i]<x[i]*y[i-1])
47 s++;
48 }
49
50 f2<<s<<"\n";
51 s=1;
52 for (i=2;i<=c;i++)
53 {
54 if (x[i-1]*y[i]==x[i]*y[i-1])
55 {
56 s++;
57 }
58 else
59 {
60 f2<<s<<" ";
61 s=1;
62 }
63 }
64
65 f2<<s;
66 f2.close();
67 f1.close();
68 return 0;
69 }

29.1.3 *Rezolvare detaliat 

29.2 stalpi
Problema 2 - stalpi 100 de puncte
Între doi stâlpi verticali aaµi pe malurile unui râu (de o parte ³i de alta a râului) se a 
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 gura de mai jos.
Pe cabluri exist  desenate câte n puncte colorate cu diverse culori, culorile ind codicate prin
numerele 1, 2, 3,..., k . Poziµia punctelor pe ecare cablu este dat  prin distanµa faµ  de stâlpul
A pentru ecare punct. Punctele de pe ecare cablu sunt numerotate cu 1, 2, 3 ,..., n. Pe ecare
cablu exist  cel puµin un punct colorat cu ecare 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 ecare culoare s  existe o singur  pereche de puncte între care s  e leg tur ;
- lungimea total  de sârm  folosit  s  e minim .

Cerinµe
S  se scrie un program care determin  lungimea minim  a sârmei ce va  folosit  pentru
rezolvarea problemei ³i o mulµime de perechi de puncte ce urmeaz  a  legate pentru a obµine
acest minim.

Date de intrare
Fi³ierul de intrare stalpi.in va conµine:
- pe prima linie numerele naturale nenule n, d separate printr-un spaµiu;
- pe a doua linie n perechi de numere, formate din distanµa faµ  de stâlpul A la ecare punct
³i culoarea asociat  punctului, separate prin câte un spaµiu, aate pe cablul 1;
CAPITOLUL 29. ONI 2011 607

- pe a treia linie n perechi de numere, formate din distanµa faµ  de stâlpul A la ecare punct
³i culoarea asociat  punctului, separate prin câte un spaµiu, aate 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 toa-
rele k linii numerele de ordine ale punctelor ce vor  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.

Restricµii ³i preciz ri
a 1 & n & 10 000
a 1 & k & 100
a 1 & d & 1 000
a Distanµa dintre cei doi stâlpi A ³i B este 30 000.
a Distanµele de la stâlpul A la puncte sunt numere naturale.
a Distanµa minim  va  a³at  trunchiat  la primele 3 zecimale.
a Toate punctele de pe un cablu sunt distincte.
a Se acord  40% din punctaj pentru determinarea corect  a minimului din cerinµ .

Exemple:
stalpi.in stalpi.out Explicaµii
3 100 211.803 Sunt n 3 perechi de puncte, k 2 culori, codicate 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

29.2.1 Indicaµii de rezolvare

prof. Doru Anastasiu Popescu, C. N. Radu Greceanu, Slatina

Folosind datele de intrare, pentru ecare culoare c din mulµimea r1, 2, ..., k x se construiesc doi
vectori:
a a0 , a1 , ..., a30000 , unde ai este codul punctului aat 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 aat 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
daca 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 29. ONI 2011 608

Õ
S d2  xa  xb2
Introducem xa ³i xb în doi vectori x ³i y .
A³ m s ³i elementele vectorilor x ³i y .
În algoritm am folosit:

M N =minim, dac  M Q=minim, pentru c  P Q d, care este constant .


Din M Q ¶xP  xM ¶, P Q d ³i teorema lui Pitagora obµinem:
Õ
MN d2  xP  xM 2

O alt  solutie de complexitate O k ˜ n este urm toarea :


prof. Pit-Rada Ionel Vasile, Colegiul National Traian, Drobeta Turnu Severin

Se construiesc tablourile cablu1[ ] ³i cablu2[ ], ecare de câte 30 000 de elemente de tip int.
Pentru ecare punct de pe ecare cablu, poziµia i din ³irul de date citit ³i perechea (distanµ ,
culoare) specice ec 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 ind valoarea maxim  a valorilor culorilor citite.
Pentru ecare 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 ecare cablu, preluând
distanµa faµ  de cap tul stâng al cablului pe care se a  ³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 aate 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 nal am a³at cu trei zecimale exacte valoarea dmin ³i apoi cele k perechi xi, y i,
1 & i & k.

29.2.2 Cod surs 

Listing 29.2.1: stalpi.cpp


1 // Sursa Pit-Rada Vasile - 100 p
2 #include<fstream>
3 #include<math.h>
4
5 using namespace std;
6
7 int cablu1[30001],cablu2[30001];
8 int x1[10001],x2[10001],z1[10001],z2[10001],x[101],y[101];
9 int n1,n2,n,i,d,k,poz,cul,d1,d2,j,dif,dift,i1,i2,w1,w2,w,m1,m2;
CAPITOLUL 29. ONI 2011 609

10 double dmin;
11
12 ifstream f1("stalpi.in");
13 ofstream f2("stalpi.out");
14
15 int main()
16 {
17
18 f1>>n>>d;
19 k=0;
20 d1=0;
21 for (i=1;i<=n;i++)
22 {
23 f1>>poz>>cul;
24 cablu1[poz]=i*100+cul-1;
25 if (cul>k) k=cul;
26 if (poz>d1)d1=poz;
27 }
28
29 d2=0;
30 for (i=1;i<=n;i++)
31 {
32 f1>>poz>>cul;
33 cablu2[poz]=i*100+cul-1;
34 if (poz>d2)d2=poz;
35 }
36
37 dmin=0;
38 for (cul=1;cul<=k;cul++)
39 {
40 n1=0;
41 m1=0;
42 for (i=0;i<=d1;i++)
43 {
44 if (cablu1[i])
45 {
46 m1++;
47 if (cablu1[i]%100+1 == cul)
48 {
49 n1++;
50 x1[n1]=i;
51 z1[n1]=cablu1[i]/100;
52 }
53 }
54 }
55
56 n2=0;
57 m2=0;
58 for (i=0;i<=d2;i++)
59 {
60 if (cablu2[i])
61 {
62 m2++;
63 if (cablu2[i]%100+1 == cul)
64 {
65 n2++;
66 x2[n2]=i;
67 z2[n2]=cablu2[i]/100;
68 }
69 }
70 }
71
72 i=1;
73 j=1;
74 dift=x1[i]-x2[j];
75 i1=z1[i];
76 i2=z2[j];
77 if (dift<0) dift=-dift;
78 while (i<=n1 && j<=n2 && dift!=0)
79 {
80 dif=x1[i]-x2[j];
81 if (dif<0)dif=-dif;
82 if (dif<dift)
83 {
84 dift=dif;
85 i1=z1[i];
CAPITOLUL 29. ONI 2011 610

86 i2=z2[j];
87 }
88
89 if (x1[i]<x2[j])
90 i++;
91 else
92 j++;
93 }
94
95 x[cul]=i1;
96 y[cul]=i2;
97 dmin=dmin+sqrt((double)dift*dift+d*d);
98 }
99
100 w1=(int)floor(dmin);
101 w2=(int)floor((dmin-w1)*1000);
102 f2<<w1<<".";
103 if (w2<10)
104 f2<<"00"<<w2;
105 else
106 if (w2<100)
107 f2<<"0"<<w2;
108 else
109 f2<<w2;
110
111 f2<<"\n";
112 for (cul=1;cul<=k;cul++)
113 {
114 f2<<x[cul]<<" "<<y[cul]<<"\n";
115 }
116
117 f2.close();
118 f1.close();
119 return 0;
120 }

29.2.3 *Rezolvare detaliat 

29.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 ecare p trat aâ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,
codicate cu N, E, S, V. Gigel ind mare amator de c p³uni vrea s  taie cele k felii astfel încât
num rul c p³unilor din aceste felii s  e 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  conform gurii 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 29. ONI 2011 611

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 ³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 ³ierului tort.out va conµine num rul maxim de c p³uni care poate  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.

Restricµii ³i preciz ri
a 2 & m, n & 500
a 1 & k $ min m, n

Exemple:
tort.in tort.out Explicaµii
6 6 3 10 Tortul este format dintr-un caroiaj cu m 6 linii ³i n 6
0 1 1 1 0 1 5 coloane ³i se pot t ia k 3 felii.
1 0 0 0 0 1 Se pot obµine maxim 10 c p³uni.
0 0 0 1 0 0 Cele 5 posibilit µi de a t ia cele 3 felii sunt: NNS, NSE, NSV,
0 1 0 1 0 1 VEV ³i NEV
1 0 0 0 0 0
1 1 1 0 0 1
Timp maxim de executare/test: 0.2 secunde
Memorie: total 4 MB
Dimensiune maxim  a sursei: 5 KB

29.3.1 Indicaµii de rezolvare

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 ecare 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 ,
sucient  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 29. ONI 2011 612

daca S<min min=S; np=1


altfel daca S=min np=np+1 sf.
sf.
sf.
sf.
sf.
scrie min, np

29.3.2 Cod surs 

Listing 29.3.1: tort.c


1 // sursa 100 p - Nistor Mot
2 #include <stdio.h>
3 #define M 502
4
5 int a[M][M];
6
7 int main()
8 {
9 FILE *fi,*fo;
10 int i,j,m,n,k,k1,min,s,np;
11
12 fi=fopen("tort.in","rt");
13 fscanf(fi,"%d %d %d",&m,&n,&k);
14
15 for(i=1;i<=m;i++)
16 for(j=1;j<=n;j++)
17 fscanf(fi,"%d",&a[i][j]);
18 fclose(fi);
19
20 min=m*n+1;
21 for(i=2;i<=m;i++)
22 a[i][1]+=a[i-1][1];
23
24 for(j=2;j<=n;j++)
25 a[1][j]+=a[1][j-1];
26
27 for(i=2;i<=m;i++)
28 for(j=2;j<=n;j++)
29 a[i][j]+=(a[i-1][j]+a[i][j-1]-a[i-1][j-1]);
30
31 for(k1=0;k1<=k;k1++)
32 for(i=m-k1;i<=m;i++)
33 for(j=n-k+k1;j<=n;j++)
34 {
35 s=a[i][j]-a[i-m+k1][j]-a[i][j-n+k-k1]+a[i-m+k1][j-n+k-k1];
36 if(s<min)
37 {
38 min=s;
39 np=1;
40 }
41 else
42 if(s==min) np++;
43 }
44
45 fo=fopen("tort.out","wt");
46 fprintf(fo,"%d\n%d\n",a[m][n]-min,np);
47 fclose(fo);
48 return 0;
49 }

29.3.3 *Rezolvare detaliat 

29.4 ape
Problema 4 - ape 100 de puncte
CAPITOLUL 29. ONI 2011 613

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 a  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 ociul 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 ecare ap , mergând
pe conturul acesteia. Pasul lui are lungimea de 1 metru. La ecare 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  ecare pas Mihai actualizeaz  ³i num rul de pa³i pe care i-a f cut.

Cerinµe
Se dore³te s  se ae, pentru ecare traseu:
a) Dimensiunile pe direcµiile Vest-Est ³i respectiv Nord-Sud ale unei suprafeµe dreptunghiulare
de arie minim  care cuprinde în interior sau pe margini suprafaµa apei.
b) Sensul în care a fost parcurs traseul: 0- pentru sens orar, respectiv 1- pentru sens invers
orar;
c) Aria suprafeµei apei înconjurate, din interiorul traseului.

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 a 
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: pri-
mul num r reprezint  dimensiunea pe direcµia Vest-Est ³i al doilea num r reprezint  dimensiunea
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.

Restricµii ³i preciz ri
a 1 & P & 10 000
a Pentru 30% din teste traseele vor avea lungimea maxim  2 000.
a Dac  dimensiunile suprafeµei dreptunghiulare de arie minim  sunt corecte, atunci se va
acorda 10% din punctaj/test.
a Dac  sensul traseului este determinat corect, atunci se vor primi 10% puncte/test.
a Aria suprafeµei apei este un num r natural nenul.

Exemple:
ape.in ape.out Explicaµii
16 3315 C suµele marcate cu '-' reprezint  traseul, iar p tratele
NNVVSVSSESEEENNV negre reprezint  apa.
Zona m rginit  de linia îngro³at 
reprezint  suprafaµa dreptunghiu-
lar  minimal  ce conµine p tratele
negre, care reprezint  apa.
C suµa marcat  cu * este poziµia
de pornire.
Timp maxim de executare/test: 0.1 secunde
Memorie: total 2 MB
Dimensiune maxim  a sursei: 5 KB

29.4.1 Indicaµii de rezolvare

prof. Piµ-Rada Ionel-Vasile, Colegiul Naµional Traian, Drobeta Turnu Severin


CAPITOLUL 29. ONI 2011 614

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  ecare 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 ecare 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  nemodicat .
Complexitate O p

29.4.2 Cod surs 

Listing 29.4.1: ape.cpp


1 // Sursa 100 p - Pit-Rada Ionel Vasile
2 #include<fstream>
3
4 using namespace std;
5
6 int P,i,x=0,y=0,s=0,xm=0,xM=0,ym=0,yM=0,sens;
7 char a,b,c;
8
9 int main()
10 {
11 ifstream f1("ape.in",ios::in);
12 ofstream f2("ape.out",ios::out);
13 f1>>P;
14 f1>>a;
15 c=a;
16 for (i=2;i<=P+1;i++)
17 {
18 if (i>P)
19 b=c;
20 else
21 f1>>b;
22
23 if (a==’N’)
24 switch (b)
25 {
26 case ’N’ : {s=s-x-1; y++; break;}
27 case ’E’ : {x++; break;}
28 case ’V’ : {s=s-x-1; x--; break;}
29 }
30 else
31 if (a==’V’)
32 switch (b)
33 {
34 case ’N’ : {y++; break;}
35 case ’V’ : {x--; break;}
36 case ’S’ : {s=s+x; y--; break;}
37 }
38 else
39 if (a==’S’)
40 switch (b)
41 {
42 case ’S’ : {s=s+x; y--; break;}
43 case ’V’ : {x--; break;}
CAPITOLUL 29. ONI 2011 615

44 case ’E’ : {s=s+x; x++; break;}


45 }
46 else
47 switch (b)
48 {
49 case ’S’ : {y--; break;}
50 case ’E’ : {x++; break;}
51 case ’N’ : {s=s-x-1; y++; break;}
52 }
53
54 if (x>xM) xM=x;
55 if (x<xm) xm=x;
56 if (y>yM) yM=y;
57 if (y<ym) ym=y;
58 a=b;
59 }
60
61 f2<<(xM-1-xm)<<" "<<(yM-1-ym)<<" ";
62
63 if (s<0)
64 {
65 sens=1;
66 s=-s;
67 s=s-P;
68 }
69 else
70 sens=0;
71
72 f2<<sens<<" "<<s<<endl;
73 f1.close();
74 f2.close();
75 return 0;
76 }

29.4.3 *Rezolvare detaliat 

29.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 ecare 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 ecare 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 re-
prezentând num rul de ecuaµii de tipul I, respectiv num rul de ecuaµii de tipul I care au exact o
r d cin , aat  în tablou, iar pe a doua linie tot dou  numere separate printr-un spaµiu repre-
zentâ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 29. ONI 2011 616

Restricµii ³i preciz ri
a 1 & n & 500
a 1 & k & 1000
a Elementele tabloului sunt numere întregi cu maxim 4 cifre ecare;
a La ecare ecuaµie de tipul I a, b, c vor  precizate, chiar dac  acestea au valoarea 0 sau 1,
(de exemplu x  2 3 va apare 1x  2 3).
a La ecare ecuaµie de tipul II a, b, c, d vor  precizate, chiar dac  acestea au valoarea 0 sau 1,
2
(de exemplu x2  1 3 va apare 1x  0x  1 3).
a Pentru ecuaµiile de tipul I a, b, c sunt numere naturale cu maxim 4 cifre;
a Pentru ecuaµiile de tipul II a, b, c, d sunt numere naturale cu maxim 4 cifre;
a Se va acorda: 10% din punctaj pentru num rul de ecuaµii de tipul I, 30% din punctaj pentru
câte dintre ele au exact o r d cin  în tablou, 20% din punctaj pentru num rul de ecuaµii de tipul
II ³i 40% din punctaj pentru câte dintre ele au exact ambele r d cini în tablou.

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.
20x2+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.
2x2+1x+4=0 A patra ecuaµie este de tipul II ³i nu are r d cinile în tablou.
1x2+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

29.5.1 Indicaµii de rezolvare

prof. Doru Anastasiu Popescu, C.N. Radu Greceanu, Slatina

1. Se citesc numerele din tablou ³i se construie³te un vector cu componentele distincte: x


x1 , x2 , ..., xp .
2. Se ordoneaz  cresc tor vectorul x.
3. Se citesc pe rând caracterele, pentru câte o ecuaµie, se veric  dac  este ecuaµie de tipul I sau
II ³i se construiesc coecienµii (de tip numeric), apoi se determin  r d cinile: x=(c-d)/a, dac 
a j 0, respectiv x1=(-b+sqrt(b2-4ac))/(2a), x2=(-b-sqrt(b2-4ac))/(2a), dac  b  4ac ' 0.
2

Se trateaza cazurile speciale in functie de coecienti


4. În funcµie de tipul de ecuaµie se m re³te cu 1 num rul de ecuaµii de gradul I, sau cel de
gradul II.
5. Pentru r d cinile determinate anterior, acestea se caut  în vectorul x, folosind un algoritm
ecient. În cazul armativ se m re³te num rul de ecuaµii cu soluµii în tablou.
6. Se a³eaz  numerele determinate.

29.5.2 Cod surs 

Listing 29.5.1: ec.cpp


1 // Sursa 100 p - Robert Hasna
2 #include <cstdio>
3 #include <cstring>
4 #include <cmath>
5
6 #define Lmax 100
7 #define Nmax 20000
CAPITOLUL 29. ONI 2011 617

8
9 using namespace std;
10
11 int N, M, L, nr_ec1, nr_ec2, sol_ec1, sol_ec2;
12 int v[Nmax];
13 char line[Lmax];
14
15 inline bool is_int(long double x)
16 {
17 return abs((int)x - x) < 0.000000000001L;
18 }
19
20 int main()
21 {
22 freopen("ec.in", "r", stdin);
23 freopen("ec.out", "w", stdout);
24
25 scanf("%d %d\n", &N, &M);
26
27 L = N*N;
28 for (int i = 0; i < L; ++i)
29 {
30 int x;
31 scanf("%d ",&x);
32 v[x+10000] = 1;
33 }
34
35
36 int coef[4], nr_coef;
37 for (int i = 0; i < M; ++i)
38 {
39 scanf("%s\n", line);
40 nr_coef = 0;
41 int val = 0;
42 for (int j = 0, size = strlen(line); j < size; ++j)
43 {
44 if (line[j] <= ’9’ && line[j] >= ’0’)
45 val = val * 10 + line[j] - ’0’;
46
47 if (line[j] == ’x’)
48 {
49 coef[nr_coef++] = val;
50 val = 0;
51 if (line[j + 1] == ’^’)
52 j+=3;
53 else
54 ++j;
55 }
56
57 if (line[j] == ’=’)
58 {
59 coef[nr_coef++] = val;
60 val = 0;
61 }
62 }
63
64 coef[nr_coef++] = val;
65
66 long double sol;
67
68 if (nr_coef == 3)
69 {
70 //ec gr1
71 ++nr_ec1;
72 if (coef[0] == 0) continue;
73 sol = ((long double)(coef[2] - coef[1])) / coef[0];
74 if (is_int(sol) && v[(int)sol+10000])
75 ++sol_ec1;
76 }
77 else
78 {
79 //ec gr 2
80 ++nr_ec2;
81 bool ok = true;
82 int delta = coef[1]*coef[1] - 4*coef[0]*(coef[2] - coef[3]);
83 if (coef[0] == 0)
CAPITOLUL 29. ONI 2011 618

84 {
85 if (coef[1] == 0)
86 continue;
87 sol = ((long double)(coef[3] - coef[2])) / coef[1];
88 if (is_int(sol) && v[(int)sol+10000])
89 ++sol_ec2;
90 continue;
91 }
92
93 if (delta < 0) continue;
94
95 sol = (long double)(-coef[1] + sqrtl(delta)) / (2*coef[0]);
96 if (!(is_int(sol) && v[(int)sol+10000]))
97 ok = false;
98
99 sol = (long double)(-coef[1] - sqrtl(delta)) / (2*coef[0]);
100 if (is_int(sol) && v[(int)sol+10000] && ok)
101 ++sol_ec2;
102 }
103 }
104
105 printf("%d %d\n%d %d\n", nr_ec1, sol_ec1, nr_ec2, sol_ec2);
106 return 0;
107 }

29.5.3 *Rezolvare detaliat 

29.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 denit 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 a  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 a  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 .

Restricµii ³i preciz ri
a 1 $ L $ 10 000 000
a 0 $ N $ 100 000
a 0 & pozi & L
CAPITOLUL 29. ONI 2011 619

Exemple:
furnici.in furnici.out Explicaµii
10 3 8 Bara are lungimea de 10 cm ³i pe bar  vor  plasate 3 furnici
4D la distanµele de 4, 8, 1 faµ  de cap tul din stânga al barei ³i vor
8S avea urm toarele sensuri de deplasare: dreapta, stânga respectiv
1S stânga.
Primele dou  furnici se vor întâlni dup  2 secunde în punctul
de coordonat  6 ³i î³i vor schimba sensul de deplasare.
Dup  schimbarea de sens a doua furnic  va parcurge înc  4cm
c tre dreapta ³i va c dea, în timp ce prima furnic  dup  schim-
barea de sens va parcurge înc  6 cm c tre stânga, pân  la c dere.
În acest timp a treia furnic  a c zut de pe bara dup  1 secund .
Pe bar  nu vor mai  furnici dup  8 secunde.

Timp maxim de executare/test: 0.2 secunde


Memorie: total 2 MB
Dimensiune maxim  a sursei: 5 KB

29.6.1 Indicaµii de rezolvare

student Robert Hasna Universitatea Bucuresti

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 aa 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 sucient 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 a  la coordonata k va  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 a  iniµial pe bar ,
furnica aat  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 a  ecare 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 29. ONI 2011 620

Se observ  c  dac  la pasul curent nu este nici o furnic  orientat  spre exteriorul barei, la pasul
viitor cel puµin una va  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 aa î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
specicat î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 

29.6.2 Cod surs 

Listing 29.6.1: furnici.cpp


1 /*
2 Problema Furnici
3 Complexitate timp O(N)
4 Complexitate spatiu O(1)
5 sursa 100 p - Robert Hasna
6 */
7
8 #include <cstdio>
9
10 using namespace std;
11
12 int L, N, rez, x;
13 char dir;
14
15 inline int max(int x, int y)
16 {
17 return x > y ? x : y;
18 }
19
20 int main()
21 {
22 freopen("furnici.in", "r", stdin);
23 freopen("furnici.out", "w", stdout);
24
25 scanf("%d %d\n", &L, &N);
26 for (int i = 0; i < N; ++i)
27 {
28 scanf("%d %c\n", &x, &dir);
29 if (dir == ’S’)
30 rez = max(rez, x);
31 else
32 rez = max(rez, L - x);
CAPITOLUL 29. ONI 2011 621

33 }
34
35 printf("%d\n", rez);
36 return 0;
37 }

29.6.3 *Rezolvare detaliat 


Capitolul 30

ONI 2010

30.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 zicii parti-
culelor 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 gur  cu 1, 2, 3. Traiectoria unei particule elementare
porne³te din unul din punctele marcate pe gur  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 gura 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 ³ierului de intrare cern.in conµine un caracter p ce indic  punctul de plecare al
particulei.
A doua linie a ³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 ³ierului de ie³ire cern.out se vor scrie dou  numere naturale g ³i c, se-
parate printr-un spaµiu, ce reprezint  num rul de grade, în sens trigonometric, respectiv cercul,
corespunz toare poziµiei nale unde se va g si particula dup  trecerea celor t unit µi de timp.

Restricµii ³i preciz ri

622
CAPITOLUL 30. ONI 2010 623

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 nal  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 nal  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

30.1.1 Indicaµii de rezolvare

prof. Sichim Cristina - Colegiul Naµional Ferdinand I Bac u


prof. Che³c  Ciprian - Grup ³colar Costin Neniµescu Buz u

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  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).

30.1.2 Cod surs 

Listing 30.1.1: cern100v1.cpp


1 // Sursa 100 puncte numai cu if-uri prof. Cristina Sichim - Bacau
2 # include <fstream>
3
4 using namespace std;
5
6 ifstream fi("cern.in");
CAPITOLUL 30. ONI 2010 624

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 30. ONI 2010 625

83 if(t>60) t=t-60,p=’G’; else fo<<300+t<<" 2\n",t=0;


84 else
85 if(cerc==3)
86 if(t>60) t=t-60,p=’I’; else fo<<120-t<<" 3\n",t=0;
87 else
88 if(t>300) t=t-300,p=’G’; else fo<<(300-t)%360<<" 2\n",t=0;
89
90 cerc=1;
91 break;
92
93 case ’I’:
94 if(s==1)
95 if(cerc==1)
96 if(t>300)t=t-300,p=’G’; else fo<<(240+t)%360<<" 1\n",t=0;
97 else
98 if(t>60) t=t-60,p=’H’; else fo<<60+t<<" 3\n",t=0;
99 else
100 if(cerc==1)
101 if(t>60) t=t-60,p=’G’; else fo<<240-t<<" 1\n",t=0;
102 else
103 if(t>300) t=t-300,p=’H’; else fo<<(420-t)%360<<" 3\n",t=0;
104 cerc=2;
105 break;
106 }
107 s=-s;
108 }
109
110 fi.close();
111 fo.close();
112 return 0;
113 }

Listing 30.1.2: cern100v2.cpp


1 //ciprian chesca
2 #include <fstream>
3
4 using namespace std;
5
6 ifstream f("cern.in");
7 ofstream g("cern.out");
8
9 int main()
10 {
11 char p;
12 int s,c=1,pp=0;
13 long long t;
14
15 // citire date de intrare
16 f>>p;
17 f>>s;f>>t;
18
19 switch (p)
20 {
21 case ’A’:pp=0;c=1;break;
22 case ’B’:pp=60;c=1;break;
23 case ’C’:pp=120;c=2;break;
24 case ’D’:pp=180;c=2;break;
25 case ’E’:pp=240;c=3;break;
26 case ’F’:pp=300;c=3;break;
27 }
28
29 int i,pozitie=pp,sens=s,cerc=c;
30 for(i=1;i<=t%(3*360);i++)
31 {
32 switch (pozitie)
33 {
34 case 180 : if(sens==1&&cerc==1)
35 {
36 pozitie=360;
37 sens=-1;
38 cerc=2;
39 }
40 if (sens==-1&&cerc==1)
41 {
CAPITOLUL 30. ONI 2010 626

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 }

30.1.3 *Rezolvare detaliat 

30.2 cmmmc
Problema 2 - cmmmc 100 de puncte
CAPITOLUL 30. ONI 2010 627

Denim noµiunea de pereche ordonat , perechea de numere naturale x, y  cu x & y . Denim


cel mai mic multiplu comun al unei perechi ordonate ca ind cel mai mic multiplu comun al
numerelor care formeaz  perechea.
Se dau k numere naturale n1 , n2 , ..., nk .

Cerinµe
S  se determine pentru ecare dintre numerele ni (i = 1, 2, ..., k ):
a) câte perechi ordonate au cel mai mic multiplu comun egal cu ni .
b) dintre acestea, perechea ordonat  care are suma minim .

Date de intrare
Prima linie a ³ierului cmmmc.in conµine un num r natural k . Urm toarele k linii din acest
³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 ecare dintre acestea se vor aa 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 , a³ate în ordine cresc toare.

Restricµii ³i preciz ri
a 1 & k & 100
a 1 & ni & 2 000 000 000
a Pentru 20% dintre teste, k & 100 ³i ni & 1 000
a Fiecare dintre cele k linii ale ³ierului cmmmc.out trebuie s  conµin  exact trei numere
separate prin câte un spaµiu; în caz contrar, soluµia se consider  gre³it  ³i se obµin 0 puncte
pentru testul respectiv. Rezolvarea corect  a cerinµei a) valoreaz  40% din punctajul unui test iar
rezolvarea corect  a cerinµei b) 60%.

Exemple:
cmmmc.in cmmmc.out Explicaµii
2 525 Exist  cinci perechi distincte care au cel mai mic multiplu comun
10 2 1 11 egal cu 10: (1,10), (2,10), (5,10), (2,5) (10,10). Dintre acestea
11 perechea cu cea mai mic  sum  este (2,5).
Pentru n=11 exist  dou  perechi ordonate care au cel mai mic
multiplu comun 11: (1,11), (11,11). Dintre acestea perechea cu
cea mai mic  sum  este (1,11).
Timp maxim de executare/test: 0.2 secunde
Memorie: total 2 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

30.2.1 Indicaµii de rezolvare

Stelian Ciurea, P tca³ Csaba


q q q
Fie n p11 ˜ p22 ˜ ... ˜ pkk scrierea în descompunere de factori primi a num rului n.
Pentru ca dou  numere a ³i b s  aib  cel mai mare multiplu comun egal cu n, pentru ecare
factor prim pi avem dou  posibilit µi:
pi apare la puterea qi în a, ³i la o putere oarecare de la 0 la qi în b,
sau apare la puterea qi în b, ³i la o putere oarecare de la 0 la qi în a.
În total avem deci qi  1  qi  1 2 ˜ qi  2 variante, dar am num rat de dou  ori varianta,
când pi apare în ambele numere la puterea qi , deci num rul corect al variantelor va  2 ˜ qi  1.
A³adar num rul total al perechilor, care au cel mai mare divizor comun egal cu n este 2 ˜
q1  1 ˜ 2 ˜ q2  1 ˜ ... ˜ 2 ˜ qk  1.
CAPITOLUL 30. ONI 2010 628

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  consi-
der m ecare 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 vericarea pentru ecare
pereche de divizori, dac  are cel mai mare divizor comun egal cu n. Pentru a determina divizorii
unui num r este de ajuns s  veric 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.

30.2.2 Cod surs 

Listing 30.2.1: cmmmc60.cpp


1 //Code by Patcas Csaba
2 //Time complexity: O(sqrt(n) + nDivisors ^ 2)
3 //Space complexity: O(nDivisors)
4 //Method: Brute force
5 //Implementation time: 15 minutes
6
7 #include <vector>
8 #include <string>
9 #include <set>
10 #include <map>
11 #include <queue>
12 #include <bitset>
13 #include <stack>
14 #include <list>
15
16 #include <numeric>
17 #include <algorithm>
18
19 #include <cstdio>
20 #include <fstream>
21 #include <iostream>
22 #include <sstream>
23 #include <iomanip>
24
25 #include <cctype>
26 #include <cmath>
27 #include <ctime>
28 #include <cassert>
29
30 using namespace std;
31
32 #define LL long long
33 #define PII pair <int, int>
34 #define PLL pair <LL, LL>
35 #define VB vector <bool>
36 #define VI vector <int>
37 #define VD vector <double>
38 #define VS vector <string>
39 #define VLL vector <LL>
40 #define VPII vector <pair <int, int> >
41 #define VVI vector < VI >
42 #define VVB vector < VB >
43
44 #define FORN(i, n) for(int i = 0; i < (n); ++i)
45 #define FOR(i, a, b) for(int i = (a); i <= (b); ++i)
CAPITOLUL 30. ONI 2010 629

46 #define REPEAT do{


47 #define UNTIL(x) }while(!(x));
48
49 #define SZ size()
50 #define BG begin()
51 #define EN end()
52 #define CL clear()
53 #define X first
54 #define Y second
55 #define RS resize
56 #define PB push_back
57 #define MP make_pair
58 #define ALL(x) x.begin(), x.end()
59
60 #define IN_FILE "cmmmc.in"
61 #define OUT_FILE "cmmmc.out"
62 #define MAX 2000000000
63
64 ifstream fin(IN_FILE);
65 ofstream fout(OUT_FILE);
66
67 int test, nSol;
68 LL n;
69
70 LL Gcd(LL x, LL y)
71 {
72 while (y)
73 {
74 LL z = x % y;
75 x = y;
76 y = z;
77 }
78 return x;
79 }
80
81 PLL Solve()
82 {
83 PLL ans = MP(n, n);
84 LL gyokN = sqrt(double(n));
85 vector<LL> divisors;
86 FOR(i, 1, gyokN - 1)
87 if (!(n % i)) divisors.PB(i), divisors.PB(n / i);
88 if (!(n % gyokN))
89 if (gyokN * gyokN == n) divisors.PB(gyokN);
90 else divisors.PB(gyokN), divisors.PB(n / gyokN);
91 nSol = 1;
92 LL minSum = (LL)MAX + MAX + 1;
93 FORN(i, divisors.SZ - 1)
94 FOR(j, i + 1, divisors.SZ - 1)
95 {
96 LL aux1 = divisors[i], aux2 = divisors[j], gcd = Gcd(aux1, aux2);
97 if (aux1 / gcd * aux2 == n)
98 {
99 ++nSol;
100 if (minSum > aux1 + aux2)
101 {
102 minSum = aux1 + aux2;
103 ans = MP(aux1, aux2);
104 }
105 }
106 }
107 return ans;
108 }
109
110 int main()
111 {
112 fin >> test;
113 FORN(t, test)
114 {
115 fin >> n;
116 PII sol = Solve();
117 fout << nSol << " " << sol.X << " " << sol.Y << endl;
118 }
119 fin.close();
120 fout.close();
121
CAPITOLUL 30. ONI 2010 630

122 return 0;
123 }

Listing 30.2.2: cmmmc100.cpp


1 //Code by Patcas Csaba
2 //Time complexity: O(MAX log log MAX + sqrt(n) + 2 ^ nFactors)
3 //Space complexity: O(sqrt(MAX))
4 //Method: Eratostenes sieve + factorization + brute force
5 //Implementation time: 45 minutes
6
7 #include <vector>
8 #include <string>
9 #include <set>
10 #include <map>
11 #include <queue>
12 #include <bitset>
13 #include <stack>
14 #include <list>
15
16 #include <numeric>
17 #include <algorithm>
18
19 #include <cstdio>
20 #include <fstream>
21 #include <iostream>
22 #include <sstream>
23 #include <iomanip>
24
25 #include <cctype>
26 #include <cmath>
27 #include <ctime>
28 #include <cassert>
29
30 using namespace std;
31
32 #define LL long long
33 #define PII pair <int, int>
34 #define PLL pair <LL, LL>
35 #define VB vector <bool>
36 #define VI vector <int>
37 #define VD vector <double>
38 #define VS vector <string>
39 #define VLL vector <LL>
40 #define VPII vector <pair <int, int> >
41 #define VVI vector < VI >
42 #define VVB vector < VB >
43
44 #define FORN(i, n) for(int i = 0; i < (n); ++i)
45 #define FOR(i, a, b) for(int i = (a); i <= (b); ++i)
46 #define REPEAT do{
47 #define UNTIL(x) }while(!(x));
48
49 #define SZ size()
50 #define BG begin()
51 #define EN end()
52 #define CL clear()
53 #define X first
54 #define Y second
55 #define RS resize
56 #define PB push_back
57 #define MP make_pair
58 #define ALL(x) x.begin(), x.end()
59
60 #define IN_FILE "cmmmc.in"
61 #define OUT_FILE "cmmmc.out"
62 #define MAX 2000000000
63
64 ifstream fin(IN_FILE);
65 ofstream fout(OUT_FILE);
66
67 int test, nSol;
68 LL n;
69 VPII factors;
70 VB isPrime;
CAPITOLUL 30. ONI 2010 631

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;
146 }
CAPITOLUL 30. ONI 2010 632

147 fin.close();
148 fout.close();
149
150 return 0;
151 }

30.2.3 *Rezolvare detaliat 

30.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 gura 1 este simetric , iar
cea din gura 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, denim submatricea de vârfuri l1 , c1  ³i l2 , c2 ,


cu 1 & l1 & l2 & M ³i 1 & c1 & c2 & N , ca ind 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 ecare 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 ³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.
Restricµii ³i preciz ri
a 2 & M, N & 400
a Elementele matricii sunt numere naturale cuprinse între 1 ³i 30 000

Exemple:
simetric.in simetric.out Explicaµii
4 5 20 Exist  20 de submatrici simetrice de latur  1 (ecare celul  este
5 1 3 6 9 3 considerat  submatrice), 3 submatrici simetrice de latur  2 ³i 2
1 6 2 8 9 2 de latur  3. Submatricile simetrice de latur  3 sunt:
3 2 7 5 1
9 8 5 3 8 5 1 3 6 2 8
1 6 2 2 7 5
3 2 7 8 5 3
CAPITOLUL 30. ONI 2010 633

Timp maxim de executare/test: 0.5 secunde


Memorie: total 2 MB din care pentru stiv  2 MB
Dimensiune maxim  a sursei: 15 KB

30.3.1 Indicaµii de rezolvare

Filip Cristian Buruian , Universitatea Politehnic  Bucure³ti


Marius Dumitran, Universitatea Bucure³ti
5
Rezolvarea trivial  are complexitatea O N : pentru orice vârf i, j  din matricea dat  ³i
pentru orice dimensiune x posibil  test m dac  submatricea de vârfuri i, j  ³i i  x  1, j  x  1
este simetric . Pasul de testare se face parcurgând efectiv submatricea. Când am întâlnit o
matrice simetric  de latur  x, increment m num rul de soluµii pentru aceast  latur . În plus,
compar m x cu maximul g sit anterior ³i eventual actualiz m acest maxim. Aceast  abordare
obµine 20 de puncte.
4
Putem obµine un algoritm de complexitate O N  observând c  dup  ce am xat vârful i, j ,
increment m x doar cât timp submatricea de vârfuri i, j  ³i i  x  1, j  x  1 este simetric .
La pasul de vericare nu se mai parcurge efectiv toat  submatricea, ci se testeaz  doar egalitatea
ultimei linii ad ugate cu ultima coloan  adaugat  în submatrice, deoarece submatricea de latur 
x  1 care începe în i, j  este sigur simetric . Aceast  abordare obµine 40-50 de puncte.
3
Complexitatea optim  de rezolvare este îns  O N , ³i exist 
mai multe soluµii cu aceast  complexitate.
Prima se bazeaz  ³i pe ideea algoritmului de mai sus: pentru
un colµ xat i, j , determin m submatricea simetric  de latur 
maxim  cu colµul stanga-sus în i, j . Toate celelalte submatrici
cu latura mai mic  sau egal  decât acest maxim vor  simetrice,
iar cele cu latura mai mare sigur nu vor  simetrice. Pentru a
determina latura maxim  din i, j  test m cât ne putem deplasa
maxim pe linii ³i pe coloane: pornim cu k = 0, ³i cât timp Ai,j k
Aik,j (dup  cum se vede ³i în desen), increment m k . Pentru ca o submatrice din i, j  s  e
simetric , ³i cea din i  1, j  1 trebuie s  e simetric . Vom compara deci k -ul cu latura maxim 
gasit  pentru colµul i  1, j  1, ³i vom reµine minimul dintre ele (atât este valoarea maxim  cu
care ne putem extinde pentru a forma o submatrice simetric  din i, j ).
3
O alta soluµie care are tot complexitatea O N  este urm toarea: pre-
supunând c  matricea dat  este patratic , alegem ecare semidiagonal  pa-
ralel  cu diagonala principal  ³i determin m câte submatrici simetrice au
diagonala principal  pe semidiagonala selectat . Pentru o semidiagonal 
selectat  trebuie s  vedem din orice punct al ei cât ne putem extinde pe
semidiagonale paralele cu diagonala secundar . Determinarea unei subma-
trici simetrice se poate face astfel liniar. Pentru matrici iniµiale care nu sunt
p tratice abordarea este similar .

30.3.2 Cod surs 

Listing 30.3.1: simetric20.cpp


1 #include<stdio.h>
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 ] )
CAPITOLUL 30. ONI 2010 634

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 }

Listing 30.3.2: simetric40.cpp


1 #include<stdio.h>
2 #include<string.h>
3
4 #define maxn 128 * 4
5
6 short int N, V[ maxn ][ maxn ], Max[ maxn][ maxn ], M;
7 int nr[ maxn];
8
9 int main()
10 {
11 freopen("simetric.in","r",stdin);
12 freopen("simetric.out","w",stdout);
13
14 memset( nr, 0, sizeof( nr ));
15 memset( Max, 0, sizeof( Max ));
16
17 scanf("%d %d",&N, &M);
18
19 for( int i = 1; i <= N; ++i)
20 for( int j = 1; j <= M; ++j)
21 scanf("%d", &V[ i ][ j ]);
22
23 int optim = 0;
24 for( int i = 1; i <= N; ++i)
25 for( int j = 1; j <= M; ++j)
26 {
27 int ok = 1;
28 for( int l = 1; i+l-1 <= N && l+j-1 <= M; l++)
29 {
30 for( int k = 1; k <= l; ++k)
31 if( V[i+l-1][j+k-1] != V[i+k-1][j+l-1] )
32 { ok = 0; break;}
33 if( ok == 0) break;
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;
CAPITOLUL 30. ONI 2010 635

45 }

Listing 30.3.3: simetric100.cpp


1 #include <stdio.h>
2
3 #define minim(a, b) ((a < b) ? a : b)
4 #define NMax 405
5
6 int M, N, A[NMax][NMax], bst[NMax][NMax];
7 int Res, cnt[NMax];
8
9 int main()
10 {
11 int i, j, k;
12
13 freopen("simetric.in", "r", stdin);
14 freopen("simetric.out", "w", stdout);
15
16 scanf("%d %d", &M, &N);
17 for (i = 1; i <= M; ++i)
18 for (j = 1; j <= N; ++j)
19 scanf("%d", &A[i][j]);
20
21 for (i = M; i; --i)
22 for (j = N; j; --j)
23 {
24 for (k = 1; i+k <= M && j+k <= N && A[i+k][j] == A[i][j+k]; ++k);
25 bst[i][j] = minim(bst[i+1][j+1]+1, k);
26 if (bst[i][j] > Res)
27 Res = bst[i][j];
28 ++cnt[bst[i][j]];
29 }
30
31 for (i = Res-1; i; --i)
32 cnt[i] += cnt[i+1];
33 for (i = 1; i <= Res; ++i)
34 printf("%d\n", cnt[i]);
35
36 return 0;
37 }

30.3.3 *Rezolvare detaliat 

30.4 pesti
Problema 4 - pesti 100 de puncte
Nicu³or trebuie s  aib  grij , pe perioada vacanµei, de cei n pe³ti aaµ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.
Astfel, în prima zi, Nicu³or formeaz  dou  subgrupe de acvarii.
În subgrupa din stânga a³eaz , în ordine, pe³tii din acvariile aate pe poziµii impare în grup 
(primul acvariu din grup , al treilea, al cincilea etc).
În subgrupa din dreapta
a³eaz , în ordine, pe³tii din
acvariile aate pe poziµii pare
în grup  (al doilea acvariu din
grup , al patrulea, al ³aselea
etc).
CAPITOLUL 30. ONI 2010 636

În ecare dintre urm toarele


zile, Nicu³or aplic  operaµia des-
cris  anterior pentru ecare su-
bgrup  format  în ziua prece-
dent .
Activitatea lui Nicu³or se încheie în ziua în care ecare dintre grupe este format  din cel mult
dou  acvarii.
Exemplu. Pentru n 9, la nalul celei de-a treia zi, pe³ti³orii sunt a³ezaµi în 5 grupe, conform
gurii al turate.

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.

Restricµii ³i preciz ri
a 3 & n & 2 000 000 000;
a 1 & x & n.
a Dac  un pe³te nu este mutat deloc atunci r spunsul la a treia cerinµ  este 1.
a Evaluare: dac  se r spunde corect la prima cerinµ  se obµine 20% din punctaj. Dac  se
r spunde corect la primele dou  cerinµe se obµine 60% din punctaj. Dac  se r spunde corect la
toate cele trei cerinµe se obµine 100% din punctaj.

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 aa î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

30.4.1 Indicaµii de rezolvare

prof. Cristina Sichim, Colegiul Naµional Ferdinand I Bac u


asist. drd. P tca³ Csaba, Universitatea Babe³-Bolyai, Cluj-Napoca

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 veric  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 nal  a pe³ti³orului cu num rul x, trebuie s  reµinem la ecare pas
în ce grup  ³i în ce acvariu se a .
CAPITOLUL 30. ONI 2010 637

Dac , la un moment dat pe³tele se a  î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 a  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 sucient s  utiliz m o singur  variabil  pe care s  o actualiz m de ecare 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 codica starea pentru ziua
curent , iar cealalt  pentru ziua anterioar . Menµion m c , num rul de elemente dintr-o linie a
matricii groupSize se modic  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 ecare din zile, memoria folosit  se modic  la O n log n ³i se pot
obµine 40-50 puncte.

30.4.2 Cod surs 

Listing 30.4.1: pesti.cpp


1 # include <fstream>
2 # include <math.h>
3
4 using namespace std;
5
6 ifstream f("pesti.in");
7 ofstream g("pesti.out");
8
9 unsigned long n,x,a,b,c,z,u,p,zi=1;
10 long double r;
11
12 int main()
13 {
14 f>>n>>x;
15
16 //cerinta a
17 r=logl(n)/logl(2);
18 if(r==floorl(r)) z=r-1; else z=r;
19
20 //cerintele b si c
21 a=1;
22 b=n;
23 u=0;
24 do
25 {
26 p=x;
27 if((x-a)%2==0) // grupa din stanga
28 {
29 b=(a+b)/2;
30 x=a+(x-a)/2;
31 }
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’;
CAPITOLUL 30. ONI 2010 638

46 f.close();
47 g.close();
48 return 0;
49 }

30.4.3 *Rezolvare detaliat 

30.5 plaja
Problema 5 - plaja 100 de puncte
Prim ria ora³ului Constanµa reamenajeaz  plaja din staµiunea Mamaia. Aceasta este repre-
zentat  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
 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 a  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 veric  dac  zonele din suprafaµa favorit  sunt
libere (neacoperite de prosoape a³ezate de turi³ti sau de umbrele).

Cerinµe
Scrieµi un program care s  determine:
a num rul de turi³ti care au reu³it s  î³i a³eze prosoapele pe plaj ;
a num rul de localnici ale c ror zone favorite sunt libere.

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 semnicaµ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 ³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 a  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
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.

Restricµii ³i preciz ri
a Colµul din stânga sus al zonei dreptunghiulare are coordonatele (1,1)
a 3 & a, b & 2 000
a 0 & u & 100
a 3 & m, n & 100 000
a Un prosop descris de x1 , y1 , x2 , y2  va avea 1 & x1 & x2 & a ³i 1 & y1 & y2 & b
O suprafaµ  favorit  descris  de x1 , y1 , x2 , y2  va avea 1 & x1 & x2 & a ³i 1 & y1 & y2 & b
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬
a
CAPITOLUL 30. ONI 2010 639

a Pentru lucrul în C/C++, se recomand  citirea folosind scanf sau fscanf, deoarece sunt mai
rapide decât cin.
a Evaluare: dac  se r spunde corect la prima cerinµ  se obµine 40% din punctaj. Dac  se
r spunde corect la ambele cerinµe se obµine 100% din punctaj.

Exemple:
plaja.in plaja.out Explicaµii
12 13 1 32 Ultimul turist nu î³i poate a³eza prosopul.
6 11
4
3477
5688
9 2 10 3
5 10 8 12
3
1 8 3 13
10 3 12 4
2 10 5 12
Zona favorit  al celui de-al 2-lea localnic nu este liber .
Timp maxim de executare/test: 1.0 secunde
Memorie: total 64 MB din care pentru stiv  1 MB
Dimensiune maxim  a sursei: 10 KB

30.5.1 Indicaµii de rezolvare

prof. Sichim Cristina - Colegiul Naµional Ferdinand I Bac u


Filip Cristian Buruian , Universitatea Politehnic  Bucure³ti
Marius Dumitran, Universitatea Bucure³ti

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 ecare 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 ecient ³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 ecare 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:
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 nal  este O N ˜ U  A ˜ B  M  N .
CAPITOLUL 30. ONI 2010 640

30.5.2 Cod surs 

Listing 30.5.1: plaja.cpp


1 #include <stdio.h>
2
3 #define NMax 2004
4
5 int M, N, A, B, U, X[128], Y[128];
6 int S[NMax][NMax], Res;
7
8 int main()
9 {
10 int i, j, x1, y1, x2, y2;
11 bool ok = false;
12
13 freopen("plaja.in", "r", stdin);
14 freopen("plaja.out", "w", stdout);
15
16 scanf("%d %d %d", &A, &B, &U);
17 for (i = 1; i <= U; ++i)
18 scanf("%d %d", &X[i], &Y[i]);
19 scanf("%d", &N);
20 for (i = 1; i <= N; ++i)
21 {
22 scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
23
24 for (j = 1, ok = true; j <= U && ok; ++j)
25 if (x1 <= X[j] && X[j] <= x2 && y1 <= Y[j] && Y[j] <= y2)
26 ok = false;
27 if (!ok)
28 continue;
29 ++Res;
30 ++S[x1][y1];
31 ++S[x2+1][y2+1];
32 --S[x1][y2+1];
33 --S[x2+1][y1];
34 }
35
36 printf("%d ", Res);
37
38 for (i = 1; i <= A; ++i)
39 for (j = 1; j <= B; ++j)
40 S[i][j] += S[i-1][j] + S[i][j-1] - S[i-1][j-1];
41
42 for (i = 1; i <= U; ++i)
43 S[X[i]][Y[i]] = 1;
44
45 for (i = 1; i <= A; ++i)
46 for (j = 1; j <= B; ++j)
47 S[i][j] = S[i-1][j] + S[i][j-1] - S[i-1][j-1] + (S[i][j] != 0);
48
49 Res = 0;
50 for (scanf("%d", &M); M; --M)
51 {
52 scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
53 Res += (S[x2][y2] - S[x2][y1-1] - S[x1-1][y2] + S[x1-1][y1-1] == 0);
54 }
55 printf("%d\n", Res);
56
57 return 0;
58 }

30.5.3 *Rezolvare detaliat 

30.6 tango
Problema 6 - tango 100 de puncte
CAPITOLUL 30. ONI 2010 641

Un tango este format din fraze muzicale, ecare 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 guri. Succesiunea de guri efectuate în timpul dansului formeaz  o
coregrae. Dou  coregrai se consider  diferite dac  succesiunea gurilor care le alc tuiesc este
diferit . O coregrae frumoas  asociat  unui tango are particularitatea urm toare: atunci când
se termin  o fraz  muzical  trebuie s  se termine ³i o gur .
D ³i S se preg tesc pentru primul lor concurs de dans ³i ei lucreaza momentan la coregraa
de tango. Chiar dac  va  primul lor concurs, ei deja ³tiu n guri de dans ³i au calculat pentru
ecare dintre aceste guri câµi timpi muzicali dureaz . Fiindc  le place foarte mult s  danseze
împreun , ei vor s  preg teasc  o coregrae frumoas  pentru o pies  care dureaz  exact k timpi
muzicali.

Cerinµe
Determinaµi num rul coregrailor 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 guri cunoscute
de D ³i S (mai este prea puµin timp pân  la concurs, ca ei s  inveµe ³i guri noi).

Date de intrare
Pe prima linie a ³ierului de intrare tango.in se a  numerele naturale nenule n ³i k , separate
printr-un singur spaµiu. Pe a doua linie se a  exact n numere separate prin câte un spaµiu,
reprezintând lungimile gurilor.

Date de ie³ire
În ³ierul de ie³ire tango.out se va a³a num rul de coregrai posibile modulo 999983.

Restricµii ³i preciz ri
a n & 100 000
a k & 2 000 000 000
a k va  întotdeauna divizibil cu 8
a 1 & lungimea unei guri & 8
a pentru 30% din teste va exista o singur  gur  de o anumit  lungime
a pentru 50% din teste n & 30
a pentru 70% din teste lungimile gurilor vor  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 coregrae frumoas  se va dansa pe
118 16 / 8 = 2 fraze muzicale.
Dac  not m gurile cu litere, avem gura A de lungime 1, gura B
de lungime 1 ³i gura C de lungime 8. Prima fraz  muzical  poate
 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 coregrai frumoase posibile.
Cum 66049 modulo 999983 = 66049, se obµine rezultatul 66049.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 32 MB din care pentru stiv  16 MB
Dimensiune maxim  a sursei: 25 KB
CAPITOLUL 30. ONI 2010 642

30.6.1 Indicaµii de rezolvare

asist. drd. P tca³ Csaba, Universitatea Babe³-Bolyai Cluj-Napoca


Marius Dumitran, Universitatea Bucure³ti

Primul pas va  s  calcul m num rul de posibilit µi pentru a dansa guri care acoper  exact
o fraz  muzical . Trat m cazul în care gurile 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 guri.
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 guri 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  exemplic m c  avem
2 guri de lungime 2 ³i 3 de lungime 6. Atunci pentru cazul 1) vor  24 moduri de a  dansat
(pentru ecare gur  de lungime 2 avem 2 posibilit µi), iar pentru cazurile 4) ³i 7) vor  6 moduri
de a  dansate (2 * 3).
S  extindem la cazul în care gurile au ³i timpi impari. în acest caz sunt mai multe moduri
de a descompune o fraz  în guri:
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  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
ocial ). Alte metode mai elegante pentru a genera num rul de posibilit µi sunt programarea
dinamic  specic  problemei rucsacului, sau metoda backtracking.
Al doilea pas este s  calcul m num rul de moduri de a dansa întreg tangoul.
Pentru a aa 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 .

30.6.2 Cod surs 

Listing 30.6.1: tango.cpp


1 #include<stdio.h>
2
3 #define prim 999983
4
5 int nr[20];
6
7 int main()
8 {
9 int n=0, k=0;
10 long long sol = 0;
11
12 freopen("tango.in","r",stdin);
13 freopen("tango.out","w",stdout);
14 scanf("%d %d", &n, &k);
15
16 //printf("%d %d\n",n,k);
17 //return 0;
18
CAPITOLUL 30. ONI 2010 643

19 for( int i = 1; i <= n; ++i)


20 {
21 int x;
22 scanf("%d", &x);
23 nr[ x ]++;
24 }
25
26 nr[ 0 ] = 1;
27 for( int i1 = 1; i1 <= 8; i1++)
28 {
29 if( nr[ i1 ] == 0) continue;
30 for( int i2 = 0; i1 + i2 <= 8; i2++)
31 {
32 if( i1 + i2 != 8 && i2 == 0) continue;
33 if( nr[ i2 ] == 0) continue;
34 for( int i3 = 0; i1 + i2 + i3 <= 8; i3++)
35 {
36 if( nr[ i3 ] == 0) continue;
37 if( i1 + i2 + i3 != 8 && i3 == 0) continue;
38 for( int i4 = 0; i1 + i2 + i3 + i4 <= 8; i4++)
39 {
40 if( nr[ i4 ] == 0) continue;
41 if( i1 + i2 + i3 + i4 != 8 && i4 == 0) continue;
42 for( int i5 = 0; i1 + i2 + i3 + i4 +i5 <= 8; i5++)
43 {
44 if( nr[ i5 ] == 0) continue;
45 if( i1 + i2 + i3 + i4 +i5 != 8 && i5 == 0) continue;
46 for( int i6 = 0; i1+i2+i3+i4+i5+i6 <= 8; i6++)
47 {
48 if( nr[ i6 ] == 0) continue;
49 if( i1+i2+i3+i4+i5+i6 != 8 && i6 == 0) continue;
50 for( int i7 = 0; i1+i2+i3+i4+i5+i6+i7 <= 8; i7++)
51 {
52 if( nr[ i7 ] == 0) continue;
53 if( i1+i2+i3+i4+i5+i6+i7 != 8 && i7 == 0)
54 continue;
55 for( int i8 = 0;
56 i1+i2+i3+i4+i5+i6+i7+i8 <= 8;
57 i8++)
58 {
59 if( i1+i2+i3+i4+i5+i6+i7+i8 == 8 )
60 {
61 long long temp =
62 ((long long ) nr[ i1 ] *
63 nr[ i2 ] * nr[ i3 ] ) % prim;
64 temp *=
65 ((long long )nr[ i4 ] * nr[ i5 ] *
66 nr[ i6 ] ) % prim;
67 temp %= prim;
68 temp = (long long)temp *
69 nr[ i7 ] * nr[ i8 ] % prim;
70 sol = ( sol + temp) % prim;
71 }
72 if( nr[ i8 ] == 0) continue;
73 }
74 }
75 }
76 }
77 }
78 }
79 }
80 }
81
82 int p = k / 8;
83 long long rez = 1;
84 long long put = sol;
85 while( p )
86 {
87 if ( p & 1 ) rez = (put * rez) % prim;
88 put = ( put * put ) % prim;
89 p >>= 1;
90 }
91
92 printf("%d\n",(int) rez);
93 //fcloseall();
94 return 0;
CAPITOLUL 30. ONI 2010 644

95 }

30.6.3 *Rezolvare detaliat 


Capitolul 31

ONI 2009

31.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, e egal cu 0, e 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 denire a codului: num rul 9999 codic  o înc pere conµinând
o comoar , iar num rul 0 o înc pere conµinând o capcan .
În etapa 1 ecare 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 nalul
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 depla-
sare î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 ec rui concurent, ³i codu-
rile 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 ³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 ecare înc pere.

Date de ie³ire
Prima linie a ³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.

Restricµii ³i preciz ri
a 1 & n & 400
a 1 & m & 900
a Pentru toate testele de intrare se garanteaz  c  exist  exact un câ³tig tor.

645
CAPITOLUL 31. ONI 2009 646

Exemple:
joc.in joc.out Explicaµii
48 2571Câ³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 nal 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

31.1.1 Indicaµii de rezolvare

Se citesc din ³ier numerele n (num rul de juc tori) ³i m (num rul de înc peri pentru ecare
concurent). Pentru ecare 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 ecare înc pere la care a ajuns, se marcheaz  înc perea ca ind vizitat , iar valoarea
din înc pere poate :
` 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 innit 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
³ier numerele cerute.

31.1.2 Cod surs 

Listing 31.1.1: joc.cpp


1 #include<fstream>
2 #include<iostream>
3
4 using namespace std;
5
6 int a[1001],n,m,t[401],s[401],v[401];
7
8 ifstream f("joc.in");
9 ofstream g("joc.out");
10
11 int main()
12 {
13 int i,j,x,castigator=0,nrp=0,viz[1001],l[1001];
14 long etapa,e_min=320000000,c_min,i_min;
15 f>>n>>m;
16 for(i=1;i<=n;i++)
17 {
18 for(j=1;j<=m;j++)
19 {
20 f>>a[j];
21 viz[j]=0;
CAPITOLUL 31. ONI 2009 647

22 }
23
24 etapa=1;
25 j=1;
26 viz[1]=1;
27 castigator=0;
28 while(!castigator&&t[i]!=-1)
29 {
30 x=a[j];
31 viz[j]=1;
32
33 if(x==0)
34 {
35 t[i]=-1;
36 v[i]=etapa;
37 }
38 else
39 if(x==9999)
40 {
41 if(etapa<e_min)
42 {
43 c_min=i;
44 e_min=etapa;
45 i_min=j;
46 }
47 castigator=1;
48 }
49 else
50 {
51 if(j==x/10) break;
52 j=x/10;
53 if(viz[j]==1)
54 break;
55 else
56 etapa=etapa+x%10+1;
57 }
58 }
59 }
60
61 nrp=0;
62 for(i=1;i<=n;i++)
63 if(t[i]==-1&&v[i]<=e_min)
64 nrp++;
65 g<<c_min<<" "<<i_min<<" "<<e_min<<" "<<nrp<<endl;
66 f.close();
67 g.close();
68 return 0;
69 }

31.1.3 *Rezolvare detaliat 

31.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 denesc trei tipuri de operaµii codicate 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  foarte mare se cere
restul împ rµirii sale la 13007.
CAPITOLUL 31. ONI 2009 648

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
Fi³ierul perspic.out va conµine restul împ rµirii la 13007 al num rului minim determinat.

Restricµii ³i preciz ri
a 1 & N & 100
a 1 & M & 10.000
a Pentru 60% din teste num rul minim de aplic ri ale setului de operaµii necesare va  mai
mic decât 2.000.000.000.

Exemple:
perspic.in perspic.out Explicaµii
22 2 Matricea iniµial :
C12 12
R12 34
Matricea dup  prima operaµie:
21
43
Matricea dup  a doua operaµie (terminarea primului set):
43
21
Matricea dup  a treia operaµie:
34
12
Matricea dup  a patra operaµie(terminarea celui de al doilea set):
12
34
33 4
E1122
R12
C23
Timp maxim de executare/test: 0.1 secunde
Memorie: total 2 MB din care pentru stiv  1 MB

31.2.1 Indicaµii de rezolvare

Vom considera o matrice iniµial  I:


1 2 ... N
N+1 N+2 ... 2*N
...
(N-1)*N+1 (N-1)*N+2 ... N*N.
Se vor simula toate cele M operaµii asupra matricei, obµinând în nal o matrice A.
În continuare putem liniariza matricea (concaten m liniile în ordine) ³i vom avea o corespon-
denµ  între poziµiile iniµiale ale ecarui num r ³i poziµiile în care acestea se vor aa dupa aplicarea
setului de operaµii. Vom reµine aceste corespondenµe într-un vector P (P i va reprezenta poziµia
pe care se a  i dup  aplicarea setului de M operaµii).
Pentru ecare numar i de la 1 la N ˜ N vom determina un ciclu în felul urmator:

i, P i, P P i, P P P i...

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 aa din nou în poziµia
iniµial . Pentru orice multiplu al lui k acest lucru va  de asemenea adevarat.
CAPITOLUL 31. ONI 2009 649

Vom avea
H i - num rul minim de aplic ri ale setului de operaµii pentru care i s  se ae 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

cmmmc H 1, H 2, ...H N ˜ N 

Pentru calcularea cmmmc se poate folosi pur ³i simplu formula

cmmmc a, b a ˜ b©cmmdc a, b,

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 ecare 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
e divizibil cu p . Soluµia se va înmulµi cu p , obµinând în nal cmmmc dorit.

31.2.2 Cod surs 

Listing 31.2.1: perspic.c


1 #include <stdio.h>
2 #include <string.h>
3
4 #define MAXN 100
5 #define MOD 13007
6 #define pos(i, j) ((i) * n + (j))
7 #define sw(x, y) (aux = A[x], A[x] = A[y], A[y] = aux)
8
9 int A[MAXN * MAXN], n, m;
10 char B[MAXN * MAXN];
11 int P[MAXN * MAXN], np = 0;
12 short E[MAXN * MAXN];
13
14 void dump()
15 {
16 int i, j;
17 printf("%d %d\n", n, m);
18 for (i = 0; i < n; ++i)
19 {
20 for (j = 0; j < n; ++j)
21 printf(" %d", A[pos(i, j)]);
22 printf("\n");
23 }
24 }
25
26 void read_eval()
27 {
28 int i, j, z, a, b, c, d, aux;
29 char op[10];
30 scanf("%d%d", &n, &m);
31
32 for (i = 0; i < n * n; ++i)
33 A[i] = i;
34
35 for (z = 0; z < m; ++z)
36 {
37 scanf("%s%d%d", op, &a, &b);
38 // printf("I: %s %d %d\n", op, a, b);
39 --a, --b;
40 switch (op[0])
41 {
42 case ’R’:
43 for (j = 0; j < n; ++j)
44 sw(pos(a, j), pos(b, j));
45 break;
46 case ’C’:
47 for (i = 0; i < n; ++i)
48 sw(pos(i, a), pos(i, b));
CAPITOLUL 31. ONI 2009 650

49 break;
50 case ’E’:
51 scanf("%d%d", &c, &d);
52 --c, --d;
53 sw(pos(a, b), pos(c, d));
54 break;
55 default:
56 //printf("ERROR: %s\n", op);
57 break;
58 }
59 }
60 }
61
62 void cycles()
63 {
64 int i, len, j, d, pow, x;
65
66 memset(B, 0, MAXN * MAXN);
67 memset(E, 0, MAXN * MAXN);
68
69 for (i = 0; i < n * n; ++i)
70 {
71 for (len = 0, j = i; !B[j]; ++len)
72 {
73 // printf("c: \t%d\n", j);
74 B[j] = 1;
75 j = A[j];
76 }
77
78 if (len)
79 {
80 // printf("<<< %d\n", len);
81 for (x=len, d=0; x>1 && P[d]*P[d] <= x && d < np; ++d)
82 {
83 // printf("e: %d, %d, %d\n", d, P[d], x);
84 for (pow = 0; x % P[d] == 0; ++pow)
85 x /= P[d];
86 if (E[P[d]] < pow)
87 E[P[d]] = pow;
88 }
89
90 if (x > 1 && !E[x])
91 E[x] = 1;
92 // dump_exp();
93 }
94 }
95 }
96
97 void gen_primes()
98 {
99 int a, j, i;
100 P[np++] = 2;
101 for (a = 3; a < n * n; a += 2)
102 {
103 char prim = 1;
104 for (j = 0; prim && (P[j] * P[j] <= a) && j < np; ++j)
105 prim = ((a % P[j]) != 0);
106 if (prim)
107 P[np++] = a;
108 }
109 }
110
111 void dump_exp()
112 {
113 int i;
114 printf("exp:\n");
115 for (i = 0; i < n * n; ++i)
116 if (E[P[i]])
117 printf(" %d^%d", P[i], E[P[i]]);
118
119 printf("\n");
120 }
121
122 void dump_primes()
123 {
124 int i;
CAPITOLUL 31. ONI 2009 651

125 printf("primes:\n");
126 for (i = 0; i < np; ++i)
127 printf(" %d", P[i]);
128
129 printf("\n");
130 }
131
132 int cmmmc()
133 {
134 int i, res, j;
135 for (res = 1, i = 1; i < n * n; ++i)
136 for (j = 0; j < E[i]; ++j)
137 res = (((long long)res * (i % MOD)) % MOD);
138
139 return res;
140 }
141
142 int main(void)
143 {
144 freopen("perspic.in", "rt", stdin);
145 freopen("perspic.out", "wt", stdout);
146
147 read_eval();
148 // dump();
149 gen_primes();
150 // dump_primes();
151 cycles();
152 printf("%d\n", cmmmc());
153 return 0;
154 }

31.2.3 *Rezolvare detaliat 

31.3 rafturi
Problema 3 - rafturi 100 de puncte
Într-o bibliotec  se a  C dulapuri identice a³ezate unul lâng  altul pe peretele unei înc peri,
dulapurile ind 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 ec rui dulap ind
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  e 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 ³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, repre-
zentâ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 31. ONI 2009 652

Restricµii ³i preciz ri
a 1 & C & 10.000
a 1 & N & 50.000

Exemple:
rafturi.in rafturi.out Explicaµii
10 4 11 Bibliotecara urc  astfel:
54 -pe dulapul 1 la raftul 1
11 -pe dulapul 4 la raftul 8
62 -pe dulapul 6 la raftul 2
38
Timp maxim de executare/test: 0.1 secunde
Memorie: total 2 MB din care pentru stiv  1 MB

31.3.1 Indicaµii de rezolvare

La citirea datelor reµinem în vectorul carti, pentru ecare dulap, în lµimea cea mai mare de
pe care trebuie luat  cartea.
Construim un vector inaltimi în care la ecare 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]

Pentru un dulap i ne putem aa în una din urmatoarele situaµii:

- cartea de pe dulapul i se ia impreuna cu cartea de pe dulapul precedent


- cartea se ia independent de grupul ultimelor trei carti
- cartea de pe dulapul i se ia impreuna cu cartile luate de pe ultimele doua dulapuri
~
„ max cartii, cartii  1  inaltimii  2
„
„ min inaltimii, cartii  inaltimii  1
inaltimii ‚
„
„
„ min inaltimii, max max cartii  2, cartii  1, cartii  inaltimei  3
€
Dac  avem cartii 0 atunci: inaltimii min inaltimii, inaltimii  1;

31.3.2 Cod surs 

Listing 31.3.1: rafturi.cpp


1 #include <stdio.h>
2
3 #define MAXN 10000
4 #define MAXV 2000000000
5
6 int C;
7 long N;
8 int carti[MAXN+1];
9 long inaltime[MAXN+1];
10
11 void afisare(long a[MAXN+1])
12 {
13 int i;
14 for(i=1;i<=C;i++)
15 printf("%ld",a[i]);
16
17 printf("\n");
18 }
19
20 void citire(void)
CAPITOLUL 31. ONI 2009 653

21 {
22 FILE *f;
23 int x,y;
24 long i;
25
26 f=fopen("rafturi.in","r");
27 fscanf(f,"%d%ld",&C,&N);
28 for (i=1;i<=N;i++)
29 {
30 fscanf(f,"%d%d",&y,&x);
31 if (carti[y]<x) carti[y]=x;
32 }
33 carti[0]=0;
34 // afisare(carti);
35 fclose(f);
36 }
37
38 void calcul(void)
39 {
40 long h;
41 int i,j;
42 inaltime[0]=0;
43 for (i=1;i<=C;i++)
44 {
45 inaltime[i]=MAXV+1;
46 h=carti[i];
47 if (inaltime[i-1]+h<inaltime[i])
48 inaltime[i]=inaltime[i-1]+h;
49 for (j=i-1; j>i-3 && j>0 ;j--)
50 {
51 if (h<carti[j])
52 h=carti[j];
53 if (inaltime[j-1]+h<inaltime[i])
54 inaltime[i]=inaltime[j-1]+h;
55 }
56 }
57 }
58
59 void afisare(void)
60 {
61 FILE *g;
62 g=fopen("rafturi.out","w");
63 fprintf(g,"%ld\n",inaltime[C]);
64 //printf("%ld\n",inaltime[C]);
65 fclose(g);
66 }
67
68 int main()
69 {
70 citire();
71 calcul();
72 afisare();
73 return 0;
74 }

31.3.3 *Rezolvare detaliat 

31.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 ecare
prieten i se cunoa³te Ci - costul berii lui preferate. Din când în când, câte un prieten, e el k ,
cump r  câte o bere pentru o secvenµ  de prieteni aaµ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 31. ONI 2009 654

Se cere num rul de beri pe care le va cump ra ecare 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 ³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 ³ierului de intrare conµine N numere naturale C1 , C2 ..., CN , separate prin câte
un spaµiu, reprezentând costurile berilor preferate de ecare 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, ecare 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.

Restricµii ³i preciz ri
a 1 & N & 15.000
a 1 & T & 10.000
a 1 & Ci & 100
a 1 & ki & N
a 1 & xi & 3.000.000
a Un program corect, care se încadreaz  în timp pentru T & 4000, va obµine cel puµin 30 de
puncte
a Un program corect, care se încadreaz  în timp pentru N & 2000, va obµine cel puµin 60 de
puncte
a Prietenii beau numai berea lor preferat .

Exemple:
br.in br.out Explicaµ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

31.4.1 Indicaµii de rezolvare

O soluµie pentru 40 de puncte


Pentru ecare pereche ki , xi  iterezi prin ³irul de costuri pân  când suma lor dep ³e³te xi .
Complexitate: O N T 
O soluµie pentru 60 de puncte
Sortezi perechile ki , xi  ³i, pentru ecare ki unic, iterezi prin ³irul de costuri ca s  obµii
r spunsurile pentru toate cererile cu acela³i ki .
2
Complexitate: O N 
CAPITOLUL 31. ONI 2009 655

O soluµie pentru 100 de puncte


Calculezi ³irul de sume parµiale Si C1  C2 ...  Ci . Acest ³ir iµi permite s  calculezi suma
costurilor pe orice interval de prieteni i, j : Ci  Ci1  ...  Cj Sj  Si1
Pentru ecare pereche ki xi faci c utare binar  pentru indicele q pân  la care se pot cump ra
beri începând din k . Folose³ti S ca s  determini dac  suma costurilor de la poziµia k la poziµia q
dep ³e³te x.
Intrucât ³irul este circular, trebuie s  decizi în ce interval cauµi soluµia q : e în intervalul k, n,
e în intervalul 1, k  1. A doua variant  este posibil  numai dac  k % 1 ³i Sn  Sk1  C1 & x.
Complexitate: O T log N 

31.4.2 Cod surs 

Listing 31.4.1: br.cpp


1 #include <stdio.h>
2
3 #define nmax 15006
4 #define FOR(i,s,d) for(i=(s);i<(d);++i)
5 #define pt(i) (1<<(i))
6
7 int n,m,k,x,sum[nmax];
8
9 int main()
10 {
11 freopen("br.in","r",stdin);
12 freopen("br.out","w",stdout);
13 int i,j,ii,rez;
14 scanf("%d %d",&n,&m);
15 FOR(i,1,n+1)
16 {
17 scanf("%d",&sum[i]);
18 sum[i]+=sum[i-1];
19 }
20
21 FOR(ii,0,m)
22 {
23 scanf("%d %d",&k,&x);
24 rez = 0;
25
26 if(sum[n]-sum[k-1] <=x)
27 {
28 rez += n-k+1;
29 x -= sum[n] - sum[k-1];
30 k = 0;
31 }
32 else
33 k--;
34
35 rez += n * (x / sum[n]);
36 x %= sum[n];
37
38 for(j=k,i=21;i>=0;--i)
39 if(pt(i)+j <= n && sum[pt(i)+j] - sum[k] <=x)
40 j += pt(i);
41
42 rez += j - k;
43 if(rez > n) rez = n;
44 printf("%d\n",rez);
45 }
46
47 return 0;
48 }

31.4.3 *Rezolvare detaliat 


CAPITOLUL 31. ONI 2009 656

31.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 aate 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 aate 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  ind 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
Fi³ierul de intrare origami.in are urm toarea structur :
- prima linie a ³ierului conµine un num r natural N , reprezentând dimensiunea iniµial  a
hârtiei;
- a doua linie conµine un num r natural K , reprezentând num rul îndoiturilor;
- pe urm toarele K linii se g sesc perechi de numere naturale nenule, A B , separate printr-un
spaµiu, reprezentând tipul îndoiturii (A este 1 dac  se realizeaz  o îndoitur  vertical  sau
A este 2 dac  se realizeaz  o îndoitur  orizontal ), respectiv la ce distanµ  se realizeaz 
îndoitura;

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.

Restricµii ³i preciz ri
a 2 & N & 170
a 1 & K & 2N  2
a A 1 sau A 2
a 1 & B $ în lµimea sau l µimea hârtiei la momentul respectiv (funcµie de tipul îndoiturii)

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 nal, 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 31. ONI 2009 657

31.5.1 Indicaµii de rezolvare

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 repre-
zenta grosimea foii în zona p tr µelei de pe linia i ³i coloana j .
Matricea F va reecta modic rile aduse foii iniµiale dup  ecare îndoitur  din ³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 toa-
rele 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 .

31.5.2 Cod surs 

Listing 31.5.1: origami.cpp


1 #include <fstream>
2 #include <iostream>
3
4 using namespace std;
5
6 #define Fin "origami.in"
7 #define Fou "origami.out"
8 #define Nmax 171
9
10 typedef int Foaie[Nmax][Nmax];
11 Foaie F; //foaia de hartie
12
13 void indoaie(int &N, int &M, int Tip, int Lin)
14 {
15 int i,j, k ,CatIndoi, Rest, tmp;
16 if(Tip==1)
17 {
18 // daca indoitura depaseste cumva marginea dreapta a foii
19 if(2*Lin>M)
20 {
21 CatIndoi = M - Lin; //numarul de coloane ale indoiturii
22 Rest = Lin - CatIndoi; //cate coloane mut la sfarsit
23 }
24 else
25 {
26 CatIndoi = Lin;
27 Rest = 0;
28 }
29
30 // copiez primele Rest coloane, oglindite, la sfarsitul tabloului,
31 // deplasand elem la stanga
32 for(k=Rest;k>=1;k--)
33 for(i=1;i<=N;i++)
34 {
35 tmp = F[i][k];
36 for(j=k;j<M;j++) F[i][j] = F[i][j+1];
37 F[i][M] = tmp;
38 }
CAPITOLUL 31. ONI 2009 658

39
40 // indoi
41 for(j=1;j<=CatIndoi;j++)
42 for(i=1;i<=N;i++) F[i][2*CatIndoi-j+1] += F[i][j];
43
44 // mut elementele tabloului cu CatIndoi elemente la stanga
45 for(i=1;i<=N;i++)
46 for(j=1;j<=M-CatIndoi;j++) F[i][j] = F[i][j+CatIndoi];
47 M = M - CatIndoi;
48 }
49 else
50 {
51 //daca indoitura depaseste cumva marginea de jos a foii
52 if(2*Lin>N)
53 {
54 CatIndoi = N - Lin; // numarul de coloane ale indoiturii
55 Rest = Lin - CatIndoi; // cate coloane mut la sfarsit
56 }
57 else
58 {
59 CatIndoi = Lin;
60 Rest = 0;
61 }
62
63 // copiez primele Rest linii, oglindite, la sfarsitul tabloului,
64 // deplasand liniile in sus
65 for(k=Rest;k>=1;k--)
66 for(i=1;i<=M;i++)
67 {
68 tmp = F[k][i];
69 for(j=k;j<N;j++) F[j][i] = F[j+1][i];
70 F[N][i] = tmp;
71 }
72
73 // indoi
74 for(i=1;i<=CatIndoi;i++)
75 for(j=1;j<=M;j++) F[2*CatIndoi-i+1][j] += F[i][j];
76
77 // mut elementele tabloului cu CatIndoi elemente in sus
78 for(i=1;i<=N-CatIndoi;i++)
79 for(j=1;j<=M;j++)F[i][j] = F[i+CatIndoi][j];
80 N = N - CatIndoi;
81 }
82 }
83
84 int main()
85 {
86 ifstream f(Fin);
87 int N; // dimensiunea initiala a hartiei
88 int M; // latimea hartiei, initial = N
89 int Nr; // numarul indoiturilor
90
91 int Tip; // Tipul indoiturii: 1 verticala spre dreapta;
92 // 2 - orizontal de sus in jos
93 int Lin; // Linia de-a lungul careia facem indoitura
94 int Max; // Grosimea maxima
95 int i,j;
96
97 //citesc datele de intrare si indoi
98 f>>N>>Nr;
99 M = N; // initial hartia e patrata, pe urma nu
100
101 //initializez hartia
102 for(i=1;i<=N;i++) for(j=1;j<=M;j++) F[i][j] = 1;
103 for(i=1;i<=Nr;i++)
104 {
105 f>>Tip>>Lin;
106 indoaie(N,M,Tip,Lin);
107 }
108 f.close();
109
110 // aflu grosimea maxima si afisez datele de iesire
111 Max = 0;
112 for(i=1;i<=N;i++)
113 for(j=1;j<=M;j++)
114 if(F[i][j]>Max) Max = F[i][j];
CAPITOLUL 31. ONI 2009 659

115
116 ofstream g(Fou);
117 g<<N<<" "<<M<<" "<<Max<<’\n’;
118 /*
119 for(i=1;i<=N;i++)
120 {
121 for(j=1;j<=M;j++) g<<F[i][j]<<" ";
122 g<<’\n’;
123 }
124 */
125 g.close();
126 return 0;
127 }

31.5.3 *Rezolvare detaliat 

31.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  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 ³ierului de intrare patrate.in conµine dou  numere naturale n ³i p separate
printr-un spaµiu, având semnicaµia de mai sus.

Date de ie³ire
Prima linie a ³ierului de ie³ire patrate.out va conµine num rul natural m c utat.
Urmeaz  n  p  1 linii. Linia i a ³ierului, pentru i 2, 3, ..., n  p  2, va conµine p  i  2
numere naturale separate prin câte un spaµiu, cu proprietatea c  suma p tratelor acestora este m.

Restricµii ³i preciz ri
a2 & n & 1000
a2&p&n
a Soluµia nu este unic , se va accepta orice soluµie corect ;
a Un program corect, care se încadreaz  în timp pentru n & 30, va obµine cel puµin 30 de
puncte.
a Un program corect, care se încadreaz  în timp pentru n & 150, va obµine cel puµin 70 de
puncte.

Exemple:
patrate.in patrate.out Explicaµii
2 2 2
43 18 18 1  1  4
2 2 2 2
114 18 2  1  2  3
2123
Timp maxim de executare/test: 0.4 secunde
Memorie: total 16 MB din care pentru stiv  1 MB
CAPITOLUL 31. ONI 2009 660

31.6.1 Indicaµii de rezolvare

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  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  e minim.

31.6.2 Cod surs 

Listing 31.6.1: patrate.cpp


1 #include <stdio.h>
2 #include <math.h>
3
4 #define FOR(i,s,d) for(i=(s);i<(d);++i)
5 #define nmax 100111
6
7 int n, A[nmax], B[nmax],C[nmax], p,a,b,c;
8
9 int test(int x)
10 {
11 int i,j;
12 FOR(i, 1, 10000)
13 {
14 if(x < i*i) break;
15 j = int(sqrt(x - i * i));
16 if(j*j + i*i != x) continue;
17 a = i;
18 b = j;
19 return 1;
20 }
21 return 0;
22 }
23
24 int doit(int x)
25 {
26 int i;
27 FOR(i, 1, 1000)
28 if(test(x + i * i))
CAPITOLUL 31. ONI 2009 661

29 {
30 c = i;
31 return x + i * i;
32 }
33 return -1;
34 }
35
36 int main()
37 {
38 int i,j,l,k;
39 freopen("patrate.in","r",stdin);
40 freopen("patrate.out","w",stdout);
41
42 scanf("%d", &n);
43 scanf("%d", &p);
44
45 j = 2;
46 A[0] = B[0] = 1;
47 C[0] = 0;
48 l = 1;
49 FOR(i, 3, n + 1)
50 {
51 j = doit(j);
52 A[l] = a ;
53 B[l] = b ;
54 C[l] = c ;
55 l++;
56 }
57 printf("%d\n", j);
58
59 FOR(i,p,n+1)
60 {
61 k = n - i;
62 printf("%d %d",A[k],B[k]);
63 FOR(j,k+1, l) printf(" %d",C[j]);
64 printf("\n");
65 }
66
67 return 0;
68 }

31.6.3 *Rezolvare detaliat 


Capitolul 32

ONI 2008

32.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
Fi³ierul de intrare ab.in va conµine pe prima linie numerele N A B .

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 a³a cea minim  din punct de vedere lexicograc.

Restricµii ³i preciz ri
a 1 & N, A, B & 30.000
a Se garanteaz  c  mereu exist  soluµie pentru datele de intrare
a Se nume³te sub³ir al ³irului X x1 , x2 , ..., xN , un ³ir Y xi1 , xi2 , ..., xiM  cu proprietatea
1 & i1 $ i2 $ ... $ iM & N
a Un ³ir x1 , x2 , ..., xK  este mai mic din punct de vedere lexicograc decat un alt ³ir
y1 , y2 , ..., yK  dac  exist  o poziµie p & K , astfel încat xp $ yp ³i x1 y1 , x2 y2 , ..., xp1 yp1
a Pentru un test se va acorda 70% din punctaj dac  permutarea a³at  are cel mai lung sub³ir
cresc tor de lungime A ³i cel mai lung sub³ir descresc tor de lungime B , dar nu este minim 
lexicograc.

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 lexicograc.
Timp maxim de executare/test: 0.1 secunde

662
CAPITOLUL 32. ONI 2008 663

32.1.1 Indicaµii de rezolvare

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
ecare. Deasemenea, trebuie s  existe cel puµin un grup de lungime x B .
2) Se inverseaz  elementele din ecare grup
Orice sub³ir descresc tor va face parte dintr-un grup, iar cum lungimea unui grup este maxim
B (³i exist  unul cu x B ), cel mai lung sub³ir descresc tor va avea lungime B .
Orice sub³ir cresc tor va  format cu câte un element din ecare grup. Cum sunt A grupuri,
cel mai lung sub³ir cresc tor va avea lungime A.
Pentru a obµine o soluµie minim  lexicograc trebuie ca grupurile de la început s  e cât mai
mici ca m rime.
Exemplu pentru N 10, A 4, B 3
Se împarte în 4 grupuri, ecare de lungime maxim 3 astfel încât grupurile de la început s  e
cât mai mici:
1 | 2 3 4 | 5 6 7 | 8 9 10
Se inverseaz  elementele din ecare grup ³i se obµine permutarea:
1 4 3 2 7 6 5 10 9 8

32.1.2 Cod surs 

Listing 32.1.1: ab.cpp


1 #include <stdio.h>
2
3 #define MAX_A 30005
4 #define FIN "ab.in"
5 #define FOUT "ab.out"
6 #define min(a, b) ((a) < (b) ? (a) : (b))
7
8 int N, A, B, size[MAX_A];
9
10 int main(void)
11 {
12 int i, j, n;
13
14 freopen(FIN, "r", stdin);
15 freopen(FOUT, "w", stdout);
16
17 scanf("%d %d %d", &N, &A, &B);
18
19 if (N < A+B-1 || N > A*B)
20 {
21 printf("Fara solutie\n");
22 return 0;
23 }
24
25 n = N;
26 for (i = A-1; i >= 0; --i)
27 {
28 size[i] = min(n-i, B);
29 n -= size[i];
30 }
31 for (i = 0; i < A; ++i)
32 {
33 for (j = n+size[i]; j > n; --j)
34 printf("%d ", j);
35 n += size[i];
36 }
37 printf("\n");
38
39 return 0;
40 }
CAPITOLUL 32. ONI 2008 664

32.1.3 *Rezolvare detaliat 

32.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  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 ecare 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 aate 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 ecare 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 codicate 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 codic  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 nal  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 ³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 ³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 codicat  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
codicarea 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 32. ONI 2008 665

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 nale a iepura³ului.
A doua linie a ³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 ³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 nal .

Restricµii ³i preciz ri
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 nit 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 aate 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:

Timp maxim de executare/test: 0.2 secunde pe Windows, 0.1 secunde pe Linux

32.2.1 Indicaµii de rezolvare

prof. Carmen Popescu

Pentru ecare celul  în care intr  iepura³ul se veric , î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 32. ONI 2008 666

- dac  celula conµine o bomb  iepura³ul devine îngera³


- dac  într-una din celulele vecine exist  o bomb  se disting urm toarele situaµii
t este prima trecere prin vecin tatea acestei bombe, atunci în matricea ce memoreaz 
harta se va înlocui valoarea 6 cu alt  valoare diret  de 0, 1, ..., 9.
t dac  iepura³ul a trecut a doua oar  pe lâng  aceast  bomba (valoarea e diferit  de
0,...9) atunci iepura³ul devine îngera³
- dac  s-au cules toµi morcovii iepura³ul este fericit.
- dac  celula conµine o trap  se ³terg zidurile curente (poziµiile marcate cu 8 în matrice) ³i
se marcheaz  tot cu 8 poziµiile noilor ziduri.
- dac  celula conµine o s geat  se schimb  sensul de deplasare al iepura³ului (1  3, 2 4)
³i se ³terge s geata (se pune 0 în matrice în celula respectiv )
- dac  urm toarea celul  de pe direcµia de deplasare a iepura³ului conµine un pom sau un zid
atunci se schimb  sensul de deplasare al iepura³ului (1  
3, 2 4)

32.2.2 Cod surs 

Listing 32.2.1: iepuras.cpp


1 #include <iostream>
2 #include <fstream>
3
4 using namespace std;
5
6 int m,n;
7 char a[201][201];
8 int di[4]={-1,0,1,0};
9 int dj[4]={0,-1,0,1};
10
11 struct zid
12 {
13 int i,j;
14 };
15 zid z1[50],z2[50];
16
17 int afara(int i,int j)
18 {
19 if (i<1 || i>m) return 1;
20 if (j<1 || j>n) return 1;
21 return 0;
22 }
23
24 int explod(int i,int j)
25 {
26 int k,i1,j1;
27 if (a[i][j]==’6’ || a[i][j]==’*’)
28 return 1;
29
30 for (k=0;k<4;k++)
31 {
32 i1=i+di[k];
33 j1=j+dj[k];
34
35 if (i1>=0 && i1<=m && j1>=1 && j1<=n)
36 if (a[i1][j1]==’6’)
37 a[i1][j1]=’*’;
38 else
39 if (a[i1][j1]==’*’)
40 return 1;
41 }
42
43 return 0;
44 }
45
46 main()
47 {
48 int k,i0,j0,d,i,j,n1=0,n2=0,pz=1;
49 long nrm,p=0,q=0;
50
51 ifstream f("iepuras.in");
52 ofstream g("iepuras.out");
CAPITOLUL 32. ONI 2008 667

53
54 nrm=0;
55 f>>m>>n;
56 f>>i0>>j0>>d;
57
58 for (i=1;i<=m;i++)
59 for (j=1;j<=n;j++)
60 {
61 f>>a[i][j];
62 if (a[i][j]==’7’) nrm++;
63 if (a[i][j]==’8’)
64 {
65 z1[n1].i=i;
66 z1[n1].j=j;
67 n1++;
68 }
69 }
70
71 f>>n2;
72 for (i=0;i<n2;i++)
73 f>>z2[i].i>>z2[i].j;
74 f.close();
75
76 i=i0;
77 j=j0;
78 while (1)
79 {
80 i=i+di[d-1];
81 j=j+dj[d-1];
82 p++;
83
84 if (i==3 && j==190)
85 cout<<"bla";
86
87 // afara
88 if (afara(i,j))
89 {
90 g<<"RATACIT"<<endl;
91 g<<i-di[d-1]<<" "<<j-dj[d-1]<<endl;
92 g<<q<<" "<<p;
93 break;
94 }
95
96 // morcov
97 if (a[i][j]==’7’)
98 {
99 q++;
100 a[i][j]=’0’;
101 }
102
103 // e bomba sau in vecinatatea unei bombe pregatita sa explodeze
104 if (explod(i,j))
105 {
106 g<<"INGERAS"<<endl;
107 g<<i<<" "<<j<<endl;
108 g<<q<<" "<<p;
109 break;
110 }
111
112 // a cules toti morcovii
113 if (q==nrm)
114 {
115 g<<"FERICIT"<<endl;
116 g<<i<<" "<<j<<endl;
117 g<<q<<" "<<p;
118 break;
119 }
120
121 if (a[i][j]==’9’)
122 {
123 if (pz==1)
124 {
125 for (k=0;k<n1;k++)
126 a[z1[k].i][z1[k].j]=’0’;
127
128 for (k=0;k<n2;k++)
CAPITOLUL 32. ONI 2008 668

129 a[z2[k].i][z2[k].j]=’8’;
130
131 pz=2;
132 }
133 else
134 {
135 for (k=0;k<n2;k++)
136 a[z2[k].i][z2[k].j]=’0’;
137
138 for (k=0;k<n1;k++)
139 a[z1[k].i][z1[k].j]=’8’;
140
141 pz=1;
142 }
143 }
144
145 // sageata, schimba directia
146 if (a[i][j]==’1’) { d=1; a[i][j]=’0’; }
147 if (a[i][j]==’2’) { d=2; a[i][j]=’0’; }
148 if (a[i][j]==’3’) { d=3; a[i][j]=’0’; }
149 if (a[i][j]==’4’) { d=4; a[i][j]=’0’; }
150
151 // pom sau zid, schimba directia
152 if (a[i+di[d-1]][j+dj[d-1]]==’5’ || a[i+di[d-1]][j+dj[d-1]]==’8’)
153 if (d==1)
154 d=3;
155 else
156 if (d==2)
157 d=4;
158 else
159 if (d==3)
160 d=1;
161 else
162 d=2;
163 }
164
165 g.close();
166 }

32.2.3 *Rezolvare detaliat 

32.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  e 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  e 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  e
palindrom. De asemenea aaµi ³i num rul de ³iruri nale distincte pe care le poate obµine efectuând
acest numar minim de operaµii.

Date de intrare
Fi³ierul de intrare palind.in va conµine pe prima linie num rul T , reprezentând num rul de
seturi de date de intrare care urmeaz . în continuare urmeaz  seturile de date de intrare, ecare
pe câte dou  linii. Pe prima linie a unui set de date se a  num rul N , având semnicaµia din
enunµ. Pe urm toarea linie se a  elementele ³irului iniµial, separate prin câte un spaµiu.
CAPITOLUL 32. ONI 2008 669

Date de ie³ire
Fi³ierul de ie³ire palind.out va conµine T linii, pe linia i aâ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 nale care se pot obµine efectuând num rul minim de
operaµii.

Restricµii ³i preciz ri
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:
palind.in palind.out Explicaµii
2 23 Pentru primul test, cele trei ³iruri posibile sunt:
3 01 1 2 1, 2 2 2 si 3 2 3.
123 Pentru al doilea test, singurul ³ir posibil este format din
1 elementul 3
3
Timp maxim de executare/test: 0.5 secunde pe Windows, 0.2 secunde pe Linux

32.3.1 Indicaµii de rezolvare

Adrian Airinei

Pentru ca orice subsecvenµ  de lungime impar  s  e palindrom ³irul trebuie s  e de forma


XY XY XY XY...XY .
Astfel putem rezolva independent problema pentru poziµiile pare ³i impare.
Dac  x 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 xa ecare 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  x 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  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.

32.3.2 Cod surs 

Listing 32.3.1: palind.c


1 #include <stdio.h>
2 #include <string.h>
3
4 #define INF 2000000000
5 #define MAXN 7001
6 #define ABS(x) ((x) < 0 ? (-(x)) : (x))
7
8 int N, A[2][MAXN], cnt[MAXN];
CAPITOLUL 32. ONI 2008 670

9 int sol[2][2];
10
11 void baga(int t)
12 {
13 int i, nr, val, x, mmin = INF;
14 memset(cnt, 0, sizeof(cnt));
15 for(val = nr = 0, i = 1; i < MAXN; i++)
16 val += nr, val += A[t][i-1], nr += A[t][i-1], cnt[i] += val;
17 for(nr = val = 0, i = MAXN-2; i >= 1; i--)
18 val += nr, val += A[t][i+1], nr += A[t][i+1], cnt[i] += val;
19 for(nr = 0, i = 1; i < MAXN; i++)
20 {
21 if(cnt[i] == mmin) nr++;
22 if(cnt[i] < mmin) mmin = cnt[i], nr = 1;
23 }
24 sol[t][0] = mmin, sol[t][1] = nr;
25 }
26
27 void solve(void)
28 {
29 int i, val, x;
30
31 memset(A, 0, sizeof(A));
32
33 scanf("%d", &N);
34 for(i = 1; i <= N; i++)
35 scanf("%d", &x), A[i&1][x]++;
36
37 baga(0), baga(1);
38 printf("%d %d\n", sol[0][0]+sol[1][0], sol[0][1]*sol[1][1]);
39 }
40
41 #define VMAX 7000
42
43 void brute(void)
44 {
45 int p[2][2], i, j, x, nr, val, t, mmin;
46
47 for(t = 0; t <= 1; t++)
48 {
49 nr = 0, mmin = INF;
50 for(i = 1; i <= VMAX; i++)
51 {
52 for(val = 0, j = 1; j <= VMAX; j++)
53 val += ABS(j-i)*A[t][j];
54 if(val == mmin) nr++;
55 if(val < mmin) mmin = val, nr = 1;
56 }
57 p[t][0] = mmin, p[t][1] = nr;
58 }
59 fprintf(stderr, "%d %d\n", p[0][0]+p[1][0], p[0][1]*p[1][1]);
60 }
61
62 int main(void)
63 {
64 freopen("palind.in", "rt", stdin);
65 freopen("palind.out", "wt", stdout);
66
67 int teste;
68
69 scanf("%d ", &teste);
70 while(teste--)
71 solve();//, brute();
72
73 return 0;
74 }

Listing 32.3.2: palindvxn.c


1 #include <stdio.h>
2 #include <string.h>
3
4 #define INF 2000000000
5 #define MAXN 7001
6 #define ABS(x) ((x) < 0 ? (-(x)) : (x))
CAPITOLUL 32. ONI 2008 671

7
8 int N, A[2][MAXN], cnt[2];
9 int sol[2][2];
10
11 void solve(void)
12 {
13 int i, val, t, x, j, nr, mmin = INF;
14
15 memset(A, 0, sizeof(A)), cnt[0] = cnt[1] = 0;
16
17 scanf("%d", &N);
18 for(i = 1; i <= N; i++)
19 scanf("%d", &x), A[i&1][ ++cnt[i&1] ] = x;
20
21 for(t = 0; t <= 1; t++)
22 {
23 nr = 0, mmin = INF;
24 for(i = 1; i < MAXN; i++)
25 {
26 for(val = 0, j = 1; j <= cnt[t]; j++)
27 val += ABS(i-A[t][j]);
28 if(val == mmin) nr++;
29 if(val < mmin) mmin = val, nr = 1;
30 }
31 sol[t][0] = mmin, sol[t][1] = nr;
32 }
33
34 printf("%d %d\n", sol[0][0]+sol[1][0], sol[0][1]*sol[1][1]);
35 }
36
37 int main(void)
38 {
39 freopen("palind.in", "rt", stdin);
40 freopen("palind.out", "wt", stdout);
41
42 int teste;
43
44 scanf("%d ", &teste);
45 while(teste--)
46 solve();//, brute();
47
48 return 0;
49 }

Listing 32.3.3: palindvxv.c


1 #include <stdio.h>
2 #include <string.h>
3
4 #define INF 2000000000
5 #define MAXN 7001
6 #define ABS(x) ((x) < 0 ? (-(x)) : (x))
7
8 int N, A[2][MAXN], cnt[MAXN];
9 int sol[2][2];
10
11 #define VMAX 500
12
13 void brute(void)
14 {
15 int p[2][2], i, j, x, nr, val, t, mmin;
16
17 memset(A, 0, sizeof(A));
18 scanf("%d", &N);
19 for(i = 1; i <= N; i++)
20 scanf("%d ", &x), A[i&1][x]++;
21
22 for(t = 0; t <= 1; t++)
23 {
24 nr = 0, mmin = INF;
25 for(i = 1; i <= VMAX; i++)
26 {
27 for(val = 0, j = 1; j <= VMAX; j++)
28 val += ABS(j-i)*A[t][j];
29 if(val == mmin) nr++;
CAPITOLUL 32. ONI 2008 672

30 if(val < mmin) mmin = val, nr = 1;


31 }
32
33 p[t][0] = mmin, p[t][1] = nr;
34 }
35
36 printf("%d %d\n", p[0][0]+p[1][0], p[0][1]*p[1][1]);
37 }
38
39 int main(void)
40 {
41 freopen("palind.in", "rt", stdin);
42 freopen("palind.out", "wt", stdout);
43
44 int teste;
45
46 scanf("%d ", &teste);
47 while(teste--)
48 brute();
49
50 return 0;
51 }

32.3.3 *Rezolvare detaliat 

32.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
ecare punct de acces exist  containere pentru colectarea de³eurilor, toate containerele au aceea³i
capacitate ³i în ecare punct de acces pot  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 tracul ³i din acest motiv trebuie ca la
ecare acces pe autostrad  s  e 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  e adunate toate contai-
nerele.

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, ecare pe cate dou 
linii. Pe prima linie a unui set se a  num rul N , având semnicaµia din enunµ. Pe urm toarea
linie se a  N numere naturale separate printr-un spaµiu, reprezentând num rul de containere din
ecare punct de acces.

Date de ie³ire
Fi³ierul de ie³ire auto.out va conµine T linii, pe linia i aându-se r spunsul pentru al i-lea set
de date de intrare. Valorile posibile pentru K se vor a³a în ordine crescatoare, separate printr-un
spaµiu.

Restricµii ³i preciz ri
CAPITOLUL 32. ONI 2008 673

a 2 & T & 30
a 2 & N & 9 000
a 1&K&N
a 0 & num rul de containere din ecare punct de acces & 10 000

Exemple:
auto.in auto.out Explicaµii
2 12
8 13
12342000
3
111
Timp maxim de executare/test: 0.8 secunde pe Windows, 0.2 secunde pe Linux

32.4.1 Indicaµii de rezolvare

Pentru un K xat, 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 .

32.4.2 Cod surs 

Listing 32.4.1: auto.c


1 #include <stdio.h>
2 #include <time.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <assert.h>
6
7 #define MAXN 9001
8 #define MAXDIV 770
9
10 int N, NR, A[MAXN], st[MAXN], pos[MAXN], sol[MAXDIV];
11 long steps = 0;
12
13 int check(int K)
14 {
15 int i, j, t = 1, p = 0, val = 0;
16
17 for(i = 1; i <= N; i++)
18 {
19 steps++;
20 while(t <= p && pos[t] <= i-K) val -= st[t++];
21 if(A[i] < val || (i > N-K+1 && A[i] != val)) return 0;
22 if(A[i] > val) pos[++p] = i, st[p] = A[i]-val, val = A[i];
23 }
24
25 return 1;
26 }
27
28 void read_and_solve(void)
29 {
30 int i, j, k; long d = 0;
31
32 scanf("%d\n", &N), NR = 0;
33 assert(N >= 1 && N <= 9000);
CAPITOLUL 32. ONI 2008 674

34
35 for(i = 1; i <= N; i++) scanf("%d ", &A[i]), d += (long)A[i],
36 assert(A[i] >= 0 && A[i] <= 10000);
37
38 for(i = 1; i <= N; i++)
39 if(d % i == 0 && check(i)) sol[++NR] = i;
40
41 for(i = 1; i <= NR; i++)
42 for(j = i+1; j <= NR; j++)
43 if(sol[i] > sol[j]) k = sol[i], sol[i] = sol[j], sol[j] = k;
44
45 for(i = 1; i <= NR; i++) printf("%d ", sol[i]);
46 printf("\n");
47 }
48
49 int main(void)
50 {
51 int teste, start, end;
52
53 freopen("auto.in", "rt", stdin);
54 freopen("auto.out", "wt", stdout);
55
56 start = clock();
57
58 scanf("%d ", &teste);
59 while(teste--)
60 read_and_solve();
61
62 fprintf(stderr, "%ld\n", steps);
63
64 end = clock();
65
66 fprintf(stderr, "%lf\n", (double)(end-start)/CLOCKS_PER_SEC);
67
68 return 0;
69 }

Listing 32.4.2: autonk.c


1 #include <stdio.h>
2 #include <string.h>
3
4 #define MAXN 9001
5 #define MAXDIV 770
6
7 int N, NR, A[MAXN], st[MAXN], pos[MAXN], sol[MAXDIV];
8 long steps;
9
10 int check(int K)
11 {
12 int i, j, t = 1, p = 0, val = 0;
13
14 for(i = 1; i <= N; i++)
15 {
16 steps++;
17 while(t <= p && pos[t] <= i-K) val -= st[t++];
18 if(A[i] < val || (i > N-K+1 && A[i] != val)) return 0;
19 if(A[i] > val) pos[++p] = i, st[p] = A[i]-val, val = A[i];
20 }
21
22 return 1;
23 }
24
25 void read_and_solve(void)
26 {
27 int i, j, k; long d = 0;
28
29 scanf("%d\n", &N), NR = 0;
30 for(i = 1; i <= N; i++) scanf("%d ", &A[i]), d += (long)A[i];
31
32 for(i = 1; i <= N; i++)
33 if(check(i)) sol[++NR] = i;
34
35 for(i = 1; i <= NR; i++) printf("%d\n", sol[i]);
36 }
CAPITOLUL 32. ONI 2008 675

37
38 int main(void)
39 {
40 int teste;
41
42 freopen("auto.in", "rt", stdin);
43 freopen("auto2.out", "wt", stdout);
44
45 scanf("%d ", &teste);
46 while(teste--)
47 read_and_solve();
48
49 fprintf(stderr, "%ld\n", steps);
50
51 return 0;
52 }

32.4.3 *Rezolvare detaliat 

32.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  ³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.

Restricµii ³i preciz ri
a 2 & M & 30000
a N are cel mult 5 000 cifre
a prima cifr  a lui N este nenul 
a o secvenµ  este format  din cifre aate pe poziµii consecutive în num rul N .

Exemple:
div.in div.out Explicaµii
2 1 10 ...
3333333333
7 00 ...
33332222
7 56 ...
3333322222
Timp maxim de executare/test: 1.0 secunde pe Windows, 0.3 secunde pe Linux
CAPITOLUL 32. ONI 2008 676

32.5.1 Indicaµii de rezolvare

prof. Nistor Moµ

Fie c1 c2 ...cn num rul dat. Se construiesc vectorii p, u, z ,


pi = restul împ rµirii lui c1 c2 ...ci la M ,
ui = restul împ rµirii lui cni1 ...cn la M
i
z i = restul împ rµirii lui 10 la M
folosind relaµiile de recurenµ  evidente.
Apoi se testeaz  toate numerele obµinute prin eliminarea a 0, 1, 2, ..., n cifre:

Pentru i=0,n (i fiind numarul de cifre sterse)


pentru j=1,n-i+1 j fiind pozitia de inceput)
daca p[j-1]*z[n-i-j+1] mod M = 0 si numarul maxim
memoreaza i,j
E necesar  o funcµie de comparare a dou  ³iruri de cifre

32.5.2 Cod surs 

Listing 32.5.1: div_rares.cpp


1 // https://infoarena.ro/job_detail/2580532?action=view-source
2 // Created by Eusebiu Rares on 13/03/2020.
3
4 #include <iostream>
5
6 FILE *in = fopen("div.in", "r"), *out = fopen("div.out", "w") ;
7
8 const int MV = 5e3 ;
9
10 int digits[MV + 1] ;
11 int putere[MV + 1] ;
12 int psm[MV + 1] ;
13 int suff[MV + 1] ;
14 int n ;
15
16 int main(int argc, const char * argv[])
17 {
18 int m ;
19 char digit ;
20
21 fscanf(in, "%d\n", &m) ;
22
23 for (digit = fgetc(in) ; digit != ’\n’ ; digit = fgetc(in))
24 digits[++ n] = digit - ’0’ ;
25
26 putere[0] = 1 ;
27
28 for (int i = 1 ; i <= MV ; ++ i)
29 {
30 putere[i] = putere[i - 1] * 10 ;
31 putere[i] %= m ;
32 }
33
34 for (int i = 1 ; i <= n ; ++ i)
35 {
36 psm[i] = psm[i - 1] * 10 + digits[i] ;
37 psm[i] %= m ;
38 }
39
40 if (psm[n] == 0)
41 {
42 fprintf(out, "0 0") ;
43 return 0 ;
44 }
45
CAPITOLUL 32. ONI 2008 677

46 for (int i = n ; i >= 1 ; -- i)


47 {
48 suff[i] = suff[i + 1] + putere[n - i] * digits[i] ;
49 suff[i] %= m ;
50 }
51
52 bool found(false) ;
53 int ansi(1), ansj(n) ;
54
55 for (int i = 1 ; i <= n && !found; ++ i)
56 {
57 for (int j = 1 ; j <= n - i + 1 ; ++ j)
58 {
59 int interval = (psm[j-1]*putere[n-i-j+1]+suff[j+i]) % m ;
60 if (interval == 0 && !(j == 1 && digits[i + j] == 0))
61 {
62 found = true ;
63 if (ansi == 1 && ansj == n)
64 {
65 ansi = j ;
66 ansj = j + i - 1 ;
67 }
68 else
69 {
70 for (int idx1 = 1, idx2 = 1 ; idx1 <= n && idx2 <= n ;)
71 {
72 while (ansi <= idx1 && idx1 <= ansj)
73 idx1 ++ ;
74
75 while (j <= idx2 && idx2 <= j + i - 1)
76 idx2 ++ ;
77
78 if (digits[idx1] < digits[idx2])
79 {
80 ansi = j ;
81 ansj = j + i - 1 ;
82 break ;
83 }
84
85 idx1 ++ ;
86 idx2 ++ ;
87 }
88 }
89 }
90 }
91 }
92
93 fprintf(out, "%d %d", ansi, ansj) ;
94
95 }

32.5.3 *Rezolvare detaliat 

32.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 gurant, 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 ecare 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 gurant, deci pentru ecare pies  în care joac , Alina trebuie s  îmbrace un singur
costum, acela asociat piesei respective. Costumele de guranµi sunt identicate 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 32. ONI 2008 678

Cunoscând costumul asociat ec 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 identic  costumul de gurant care trebuie îmbr cat în spectacolul din ziua i.

Date de ie³ire
În ³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  î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 a³eaz  cea c reia îi corespunde o zi de început mai aproape de începutul stagiunii.

Restricµii ³i preciz ri
a 1 & Z & 55 000
a 1 & C & 26
a C&Z
a Pentru 20% dintre teste Z & 350

Exemple:
teatru.in teatru.out Explicaµii
10 3 5 ...
XKUFKFEGXG KUFKF
15 4 6 ...
IAJRAMRCZJJCDNS AJRAMR
25 5 8 ...
LDSDGIFAURLPTZLDLPNLEGFRN LPTZLDLP
Timp maxim de executare/test: 0.2 secunde Windows, 0.1 secunde Linux

32.6.1 Indicaµii de rezolvare

prof. Constantin G l µan

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.

32.6.2 Cod surs 

Listing 32.6.1: teatru.cpp


1 #include <stdio.h>
2
3 char s[55001];
4 long imax, jmax, Lmax, n, k;
5 long f[91];
6
7 void Citeste();
8 void Afis();
CAPITOLUL 32. ONI 2008 679

9 void Calculeaza();
10
11 int main()
12 {
13 freopen("teatru.in", "r", stdin);
14 freopen("teatru.out", "w", stdout);
15 Citeste();
16 Calculeaza();
17 Afis();
18 return 0;
19 }
20
21 void Citeste()
22 {
23 scanf("%d%d", &n, &k);
24 scanf("%c", &s[0]); // citeste ’\n’
25 for (long int i = 0; i < n; i++ )
26 scanf("%c", s + i);
27 }
28
29 void Afis()
30 {
31 printf("%d\n", Lmax);
32 for (long int i = imax; i <= jmax; i++ )
33 printf("%c", s[i]);
34 printf("\n");
35 }
36
37 void Calculeaza()
38 {
39 long int i = 0, j = 0, nr = 0;
40 f[s[0]]++; nr++;
41 do
42 {
43 while (nr <= k && j < n)
44 {
45 j++;
46 if ( j >= n ) break;
47 if (f[s[j]] == 0)
48 {
49 f[s[j]]++;
50 nr++;
51 }
52 else f[s[j]]++;
53 }
54
55 if (j - i > Lmax)
56 {
57 Lmax = j - i;
58 imax = i; jmax = j - 1;
59 }
60
61 while ( nr > k )
62 {
63 f[s[i]]--;
64 if ( f[s[i]] == 0 ) nr--;
65 i++;
66 }
67
68 } while (i < n && j < n);
69 }

Listing 32.6.2: teatru_n2.cpp


1 #include <stdio.h>
2 #include <string.h>
3
4 #define MAX_N 55005
5 #define FIN "teatru.in"
6 #define FOUT "teatru.out"
7
8 int N, C, bst_len, start;
9 char S[MAX_N], cnt[26];
10
11 int main(void)
CAPITOLUL 32. ONI 2008 680

12 {
13 int i, j, k;
14
15 freopen(FIN, "r", stdin);
16 freopen(FOUT, "w", stdout);
17
18 scanf("%d %d %s", &N, &C, S);
19
20 for (i = 0; i < N; ++i)
21 {
22 memset(cnt, 0, sizeof(cnt));
23 for (j = i, k = 0; j < N && k <= C; ++j)
24 {
25 if (!cnt[S[j]-’A’]) { ++k; cnt[S[j]-’A’] = 1; }
26 if (k == C && bst_len < j-i+1)
27 {
28 bst_len = j-i+1;
29 start = i;
30 }
31 }
32 }
33
34 printf("%d\n", bst_len);
35 for (i = start; i < start+bst_len; ++i)
36 printf("%c", S[i]);
37 printf("\n");
38
39 return 0;
40 }

Listing 32.6.3: teatru_n3.cpp


1 #include <stdio.h>
2
3 char s[55001];
4 long imax, jmax, Lmax, n, k;
5 long f[91];
6
7 void Citeste();
8 void Afis();
9 void Calculeaza();
10
11 int main()
12 {
13 freopen("teatru.in", "r", stdin);
14 freopen("teatru.out", "w", stdout);
15 Citeste();
16 Calculeaza();
17 Afis();
18 return 0;
19 }
20
21 void Citeste()
22 {
23 scanf("%d%d", &n, &k);
24 scanf("%c", &s[0]); // citeste ’\n’
25 for (long int i = 0; i < n; i++ )
26 scanf("%c", s + i);
27 }
28
29 void Afis()
30 {
31 printf("%d\n", Lmax);
32 for (long int i = imax; i <= jmax; i++ )
33 printf("%c", s[i]);
34 printf("\n");
35 }
36
37 void Calculeaza()
38 {
39 long int i = 0, j = 0, nr = 0, p;
40
41 for ( i = 0; i < n; i++ )
42 for ( j = i; j < n; j++ )
43 {
CAPITOLUL 32. ONI 2008 681

44 for ( p = ’A’; p <= ’Z’; p++ )


45 f[p] = 0;
46
47 for ( p = i; p <= j; p++ )
48 f[s[p]] = 1;
49
50 nr = 0;
51 for ( p = ’A’; p <= ’Z’; p++ )
52 if ( f[p] ) nr++;
53
54 if ( nr == k && Lmax < j - i + 1)
55 {
56 Lmax = j - i + 1;
57 imax = i; jmax = j;
58 }
59 }
60 }

32.6.3 *Rezolvare detaliat 


Capitolul 33

ONI 2007

Figura 33.1: Sigla ONI 2007

33.1 Agitaµie
O rm  produc toare de software organizeaz  un interviu pentru ocuparea unui post de pro-
gramator, la care s-au prezentat N candidaµi. Ace³tia sunt ordonaµi în funcµiie de momentul la
care ³i-au trimis CV-ul ³i numerotaµi cu numere consecutive de la 1 la N .
Fiec rui candidat i-a fost realizat în prealabil un prol psihologic ³i i s-a determinat nivelul
de agitaµie provocat de interviul care urmeaz  s  aib  loc, precum ³i un sens (cresc tor sau
descresc tor) de modicare a acestui nivel. Astfel, la ora la care s-a anunµat începerea interviului
(pe care o vom considera momentul 0), ecare candidat are un nivel de agitaµie iniµial. Pentru
ecare unitate de timp dup  momentul 0 ³i pân  în momentul în care candidatul este invitat
pentru interviu (pân  atunci el trebuind s  a³tepte), nivelul s u de agitaµie se modic  cu 1:
pentru o parte din candidaµi nivelul cre³te cu 1 unitate, iar pentru ceilalµi nivelul scade cu 1
unitate. Dac  nivelul de agitaµie a unui candidat ajunge la 0, din acel moment, pentru ecare
unitate de timp urm toare, nivelul de agitaµie va cre³te cu 1 (se schimb  sensul de modicare a
nivelului de agitaµie).
Firma va invita candidaµii la interviu în grupuri, în ordinea numerot rii (toµi candidaµii având
numere de ordine mai mici decât un candidat K vor  invitaµi într-un grup anterior sau în acela³i
grup cu candidatul K ). Înainte de a invita un grup, comisia ce conduce interviul poate decide
s  a³tepte un num r întreg de unit µi de timp (zero sau mai multe). Pentru un grup, durata
interviului se consider  neglijabil  (ecare candidat trebuie doar s  r spund  la câteva întreb ri
de tip gril ). Din momentul în care un candidat este invitat la interviu, nivelul de agitaµie a
acestuia r mâne constant.
Deoarece rma dore³te ca, indiferent de rezultatul interviului, toµi candidaµii s  r mân  cu o
p rere bun , comisia va forma grupurile ³i va alege timpii de a³teptare în a³a fel încât suma total 
a nivelelor de agitaµie a candidaµilor la sfâr³itul interviului s  e minim .
Cerinµ 
S  se scrie un program care s  determine suma total  minim  a nivelelor de agitaµie a candi-
daµilor la sfâr³itul interviului.

682
CAPITOLUL 33. ONI 2007 683

Date de intrare
Fi³ierul de intrare agitatie.in are pe prima linie num rul natural N , reprezentând num rul
de candidaµi. Pe urm toarele N linii se a  câte dou  numere întregi A ³i B , separate printr-un
spaµiu. A reprezint  nivelul iniµial de agitaµie a candidatului, iar B reprezint  sensul de modicare
a agitaµiei pentru ecare unitate de timp în care acesta a³teapt  (dac  B este 1, atunci nivelul
de agitaµie cre³te, iar dac  B este 1, nivelul de agitaµie scade). Linia k  1 din ³ier va conµine
valorile corespunz toare candidatului cu num rul k .
Date de ie³ire
Fi³ierul de ie³ire agitatie.out va conµine pe prima (³i singura) linie suma total  minim  a
nivelelor de agitaµie a candidaµilor la sfâr³itul interviului.
Restricµii ³i preciz ri
a 1 & N & 3000
a 1 & nivelul iniµial de agitaµie a ec rui candidat & 3000
Exemplu
agitatie.in agitatie.out
6 23
10 1
3 -1
2 -1
1 -1
91
6 -1
Explicaµie:
Suma total  minim  este 23.
O posibil  soluµie este urm toarea: Se formeaz  3 grupuri.
Primul grup este format doar din candidatul 1 ³i a³teapt 0 unit µi de timp. Prin urmare,
nivelul nal de agitaµie a candidatului 1 va  10.
Al doilea grup va a³tepta 2 unit µi de timp din momentul în care a terminat interviul primul
grup (deci va începe interviul la momentul 2), iar din grup vor face parte candidaµii 2, 3, 4 ³i 5.
Nivelele nale de agitaµie a acestor candidaµi vor : 1, 0, 1 ³i 11. Observaµi c  nivelul de agitaµie
a candidatului 4 a sc zut întâi pân  la 0, apoi a crescut la 1.
Al 3-lea grup va mai a³tepta 4 unit µi de timp (deci va începe interviul la momentul 6), iar
din grup va face parte doar candidatul 6. Nivelul nal de agitaµie a acestuia va  0.
Timp maxim de execuµie/test(Windows/Linux): 0.6 secunde

33.1.1 Indicaµii de rezolvare

Soluµia 1 (As. Mugurel Ionuµ Andreica)


Se va calcula o matrice SM IN i, t, reprezentând suma minim  a nivelelor nale de agitaµie
a candidaµilor i, i  1, .., N , dac  interviul primilor i  1 candidaµi s-ar termina la momentul t.
Pentru calculul lui SM IN i, t avem 2 variante:
a s  nu a³tept m deloc înainte de a începe grupul ce-l conµine pe candidatul i (timp de a³teptare
egal cu 0 implic  apartenenµa la grupul anterior); deci, SM IN i, t ar putea  egal cu: (nivelul
de agitatie al candidatului i la momentul t) SM IN i  1, t
a se a³teapt  cel puµin 1 unitate de timp (se începe un grup nou de la candidatul i încolo)
deci, SM IN i, t ar putea  egal cu SM IN i, t  1
Pentru SM IN i, t se alege minimul din cele 2 variante. Rezultatul dorit se aa în
SM IN 1, 0.
Complexitatea soluµiei este O N ˜ T M AX , unde ambele valori sunt mai mici sau egale cu
3000. Pentru a se încadra în limita de memorie, observ m c  pentru calculul matricei SM IN
avem nevoie, la orice moment, doar de 2 linii (liniile i ³i i  1) ale matricii SM IN ; prin urmare,
cantitatea necesar  de memorie este O T M AX  ³i nu O N ˜ T M AX .
Soluµia 2 (stud. Adrian Paul Diaconu)
Se calculeaz  vectorul SU M i ca ind suma sensurilor de modicare a agitaµiei pentru can-
didaµii i, i  1, .., N . Se alege valoarea minim  din acest vector.
CAPITOLUL 33. ONI 2007 684

S  presupunem c  aceast  valoare se obµine pentru poziµia k din vector. Dac  SU M k  este mai
mic  decât 0, atunci introducând o perioad  de a³teptare mai mare decât 0 înaintea candidatului
k , suma total  a nivelelor de agitaµie va sc dea.
Perioada de timp cu care se m re³te timpul de a³teptare dinaintea candidatului k este m rit 
cu valoarea minim  a nivelului de agitaµie dintre toµi candidaµii j ' k având sensuri negative de
modicare a agitaµiei. Se modic  corespunz tor valorile nivelelor de agitaµie a tuturor candi-
daµilor j ' k , iar pentru toµi candidaµii care ating nivelul de agitaµie 0, se schimb  ³i sensul de
modicare a nivelului de agitaµie (din 1 în 1).
Acest algoritm se repet  pân  în momentul în care valoarea minim  din vectorul SU M este
2
mai mare sau egal  cu 0. Complexitatea acestei soluµii este O N .

33.1.2 *Cod surs 

33.1.3 Rezolvare detaliat 

Listing 33.1.1: agitatie.java


1 import java.io.*;
2 class agitatie
3 {
4 static StreamTokenizer st;
5 static PrintWriter out;
6 static int n,tmax=-1;
7 static int[] a;
8 static int[] b;
9 static int[][] smin;
10
11 public static void main(String[] args) throws IOException
12 {
13 int i,t,na;
14 st=new StreamTokenizer(new BufferedReader(new FileReader("agitatie.in")));
15 out=new PrintWriter(new BufferedWriter(new FileWriter("agitatie.out")));
16 st.nextToken(); n=(int)st.nval;
17 a=new int[n+1];
18 b=new int[n+1];
19 for(i=1;i<=n;i++)
20 {
21 st.nextToken(); a[i]=(int)st.nval;
22 st.nextToken(); b[i]=(int)st.nval;
23 if(a[i]>tmax) tmax=a[i];
24 }
25 smin=new int[n+2][tmax+2];
26
27 for(i=0;i<=n;i++) smin[i][tmax+1]=9999999;
28
29 for(i=n;i>=1;i--)
30 {
31 for(t=tmax;t>=0;t--)
32 {
33 if(b[i]==1) na=a[i]+t;
34 else
35 {
36 if(t<=a[i]) na=a[i]-t; else na=t-a[i];
37 }
38 smin[i][t]=min(na+smin[i+1][t],smin[i][t+1]);
39 }// for t
40 }// for i
41 //afissmin();
42 System.out.println(smin[1][0]);
43 out.println(smin[1][0]);
44 out.close();
45 }// main
46
47 static int min(int a, int b)
48 {
49 if(a<b) return a; else return b;
50 }
51
52 static void afissmin()
CAPITOLUL 33. ONI 2007 685

53 {
54 int i,t;
55 for(i=0;i<=n;i++)
56 {
57 for(t=0;t<=tmax;t++) System.out.print(smin[i][t]+"\t");
58 System.out.println();
59 }
60 System.out.println();
61 }// afissmin(...)
62 }// class

33.2 Coduri
Întorcându-se de la ³coal  în ziua în care a aat cum se face înmulµirea numerelor, Gigel a
auzit la televizor urm toarea armaµie: "Pentru a face avere, nu trebuie s  aduni bani în viaµ ,
ci trebuie s-i înmulµe³ti".
Toate acestea l-au pus pe gânduri, a³a c  s-a hot rât s  inventeze propriul "sistem de codicare"
pentru numere reale mai mari decât 0 care s  aib  urm toarele propriet µi:
- ecare num r va  codicat sub forma unui ³ir de valori întregi (pozitive ³i/sau negative)
- dac  un num r real x are codul cx ³i un num r real y are codul cy , atunci num rul real
rezultat prin înmulµirea lui x ³i y trebuie s  aib  codul obµinut prin "adunarea" codurilor c  x ³i
cy .
- dac  un num r real x se poate scrie ca produs de numere y1 , y2 , ..., yk , atunci codul lui x se
obµine prin "adunarea" codurilor numerelor y1 , y2 , ..., yk .
Consider m un cod c1 format din n1 valori an1 ...a1 ³i un cod c2 format din n2 valori bn2 ...b1,
atunci codul c3 obµinut prin "adunarea" codurilor c1 ³i c2 va avea n3 valori dn3 ...d1 , cu propriet µile
urm toare:
an3 este maximul dintre n1 ³i n2

~
„ai  bi pentru i 1, ..., min n1 , n2 
„
„
„
adi ‚
„ai pentru i minim n1 , n2   1, ..., n1 dac  minim n1 , n2  n2
„
„
„bi pentru i minim n1 , n2   1, ..., n2 dac  minim n1 , n2  n1
€

Cerinµ 
Dându-se N numere reale mai mari strict decât 0, s  se scrie codicarea acestora în sistemul
inventat de Gigel.
Date de intrare
Fi³ierul de intrare coduri.in va conµine:
- pe prima linie din ³ier se a  num rul N de numere reale
- pe urm toarele N linii cele N numere reale, ecare pe câte o linie.
Date de ie³ire
Fi³ierul de ie³ire coduri.out va conµine N linii:
- pe linia i (i între 1 ³i N ) : num rul de valori folosite pentru codicarea num rului cu indicele
i din ³ierul de intrare, urmat de un spaµiu ³i apoi valorile ce alc tuiesc codul num rului, separate
dou  câte dou  printr-un singur spaµiu.
Restricµii ³i preciz ri
2 & N & 18
a
Separatorul între partea întreag  ³i partea zecimal  este virgula.
a
a Orice num r are dup  virgul  cel mult 5 cifre.
a Valorile din codurile numerelor din ³ierele de test trebuie s  e cuprinse în intervalul
106, 106.
a Partea întreag  a ec rui num r real este o valoare mai mic  sau egal  cu 20000.
a Toate numerele din ³ierele de test sunt strict pozitive ³i distincte dou  câte dou .
a Num rul maxim de valori utilizat pentru codicarea unui num r este 2500.
a Dac  exist  mai multe soluµii de codicare, se va a³a una singur .
a Nu trebuie s  existe dou  numere diferite cu aceea³i codicare.
CAPITOLUL 33. ONI 2007 686

a 40% din teste vor conµine numai numere întregi, 30% din teste vor conµine numere întregi ³i
numere reale f r  perioad  ³i 30% din teste vor conµine numere întregi ³i numere reale cu ³i f r 
perioad .
Exemplu
coduri.in coduri.out Explicaµii
8 2 11
10 3 -1 0 1 10=2*5 iar suma codurilor pentru 2 ³i 5
2 3 110 determin  codul lui 10
5 3 210
0,3 3 -1 2 1 2,1=7*0,3 iar suma codurilor pentru 7 ³i 0,3
7 3 131 determin  codul lui 2,1
2,1 2 1 11
1,(7) 2 12
1,2(34)
Timp maxim de execuµie/test(Windows/Linux): 0.2 secunde

33.2.1 Indicaµii de rezolvare

Descrierea soluµiei (Prof. Roxana Tîmplaru)


O soluµie de codicare porne³te de la ideea c  orice num r întreg se descompune în factori
k k k k k
primi, sub forma 2 1 3 2 5 3 7 4 ...a p . Codicarea acestui num r va avea kp valori, iar acestea
vor  coecienµii la care apar factorii primi în descompunerea num rului, adic  kp ...k3 k2 k1 .
Num rul rezultat prin înmulµirea a dou  numere va avea coecienµii în descompunere suma
coecienµilor celor dou  numere, deci codicarea num rului care va rezulta ca produs a celor dou ,
va rezulta din suma codurilor acestor numere.
Pentru numerele cu virgul , se folose³te scrierea sub form  de fracµie, se descompune num r -
torul ³i numitorul, iar codicarea se va obµine sc zând din coecienµii de la num r tor pe cei de
la numitor.

33.2.2 *Cod surs 

33.2.3 Rezolvare detaliat 


Rezultatele nu corespund cu cele date în exemplu dar ... pare corect aici!

Listing 33.2.1: coduri.java


1 import java.io.*;
2 class coduri
3 {
4 static BufferedReader br;
5 static PrintWriter out;
6 static int n;
7 static String nr;
8 static int[] p=new int[9593]; // nr prime
9 static int[] f1=new int[9593];
10 static int[] f2=new int[9593];
11 static int iuf, iuf1, iuf2;
12
13 public static void main(String[] args) throws IOException
14 {
15 int i,j,k;
16 int nr1,nr2,nr3,aux,n10,x,y;
17
18 br=new BufferedReader(new FileReader("coduri.in"));
19 out=new PrintWriter(new BufferedWriter(new FileWriter("coduri.out")));
20
21 nrprime();
22 n=Integer.parseInt(br.readLine());
23 for(i=1;i<=n;i++)
24 {
25 nr=br.readLine();
CAPITOLUL 33. ONI 2007 687

26 nr1=0;
27 for(j=0;j<nr.length();j++)
28 if(nr.charAt(j)==’,’) break;
29 else nr1=nr1*10+nr.charAt(j)-’0’;
30
31 //System.out.print(i+" : "+nr+" --> ");
32
33 if(j==nr.length()) // intreg
34 {
35 x=nr1;
36 y=1;
37
38 //System.out.println("x = "+x+" y = "+y);
39
40 if(x==1) // 1 nu se mai desxcompune ... are codul "0"
41 {
42 out.println("1 0");
43 continue;
44 }
45
46 descfact(x,f1);
47 iuf1=iuf;
48
49 out.print(iuf1+" ");
50 for(k=iuf1;k>=2;k--) out.print(f1[k]+" ");
51 out.println(f1[1]);
52
53 continue;
54 }// if intreg
55
56 nr2=0;
57 for(k=j+1;k<nr.length();k++)
58 if(nr.charAt(k)==’(’) break;
59 else nr2=nr2*10+nr.charAt(k)-’0’;
60
61 if(k==nr.length()) // real neperiodic
62 {
63 x=nr1;
64 y=1;
65 aux=nr2;
66 while(aux!=0) { x*=10; y*=10; aux/=10;}
67 x+=nr2;
68
69 descfact(x,f1);
70 iuf1=iuf;
71
72 descfact(y,f2);
73 iuf2=iuf;
74
75 iuf=max(iuf1,iuf2);
76 out.print(iuf+" ");
77 for(k=iuf;k>=2;k--)
78 {
79 if(k<=iuf1 && k<=iuf2) out.print((f1[k]-f2[k])+" "); else
80 if(k<=iuf1)
81 out.print(f1[k]+" ");
82 else
83 out.print(-f2[k]+" ");
84 }
85 out.println(f1[1]-f2[1]);
86
87 continue;
88 }// if real neperiodic
89
90 // real periodic (nr1-nr2)/nr3
91 aux=nr2;
92 n10=1;
93 while(aux!=0) {nr1*=10; aux/=10; n10=n10*10;}
94 nr1+=nr2;
95 nr2=nr1;
96 nr3=0;
97 for(j=k+1;j<nr.length()-1;j++)
98 {
99 nr1=nr1*10+nr.charAt(j)-’0’;
100 nr3=nr3*10+9;
101 }
CAPITOLUL 33. ONI 2007 688

102 nr3=nr3*n10;
103 x=nr1-nr2;
104 y=nr3;
105
106 descfact(x,f1);
107 iuf1=iuf;
108 descfact(y,f2);
109 iuf2=iuf;
110
111 iuf=max(iuf1,iuf2);
112 out.print(iuf+" ");
113 for(k=iuf;k>=2;k--)
114 {
115 if(k<=iuf1 && k<=iuf2) out.print((f1[k]-f2[k])+" "); else
116 if(k<=iuf1)
117 out.print(f1[k]+" ");
118 else
119 out.print(-f2[k]+" ");
120 }
121 out.println(f1[1]-f2[1]);
122
123 }// for i
124
125 out.close();
126 }// main(...)
127
128 static void descfact(int nr, int[] f)
129 {
130 int i;
131 i=1;
132 f[1]=0;
133 while(nr>1)
134 {
135 while(nr%p[i]==0) {f[i]++; nr/=p[i];}
136 i++;
137 f[i]=0;
138 }
139 iuf=i-1;
140 }// descfact(...)
141
142 static void nrprime()
143 {
144 int i,j;
145 boolean ok;
146 int k;
147 p[1]=2; p[2]=3; p[3]=5; p[4]=7; p[5]=11;
148 p[6]=13; p[7]=17; p[8]=19; p[9]=23; // gata ...
149 k=9;
150 for(i=29;i<=99999;i=i+2)
151 {
152 ok=true;
153 for(j=2; j<=k && p[j]*p[j]<=i && ok; j++)
154 if(i%p[j]==0) ok=false;
155 if(ok)
156 {
157 p[++k]=i;
158 }
159 }// for
160 }// nrprime(...)
161
162 static int max(int a, int b)
163 {
164 if(a>b) return a; else return b;
165 }// max(...)
166 }// class

33.3 Lacuri
Pe un teren de form  p trat  sunt zone de uscat ³i lacuri. Harta terenului este memorat 
într-un tablou bidimensional n ˜ n cu valori 1 (ap ) sau 0 (uscat). Liniile sunt numerotate de la
1 la n, de sus în jos ³i coloanele de la 1 la n, de la stânga la dreapta. Fiecare lac este înconjurat
CAPITOLUL 33. ONI 2007 689

de o suprafaµ  de teren uscat. Excepµie fac doar lacurile situate la marginea terenului care sunt
înconjurate de uscat doar prin interiorul terenului nu ³i prin afara acestuia.
Cerinµ 
Se dore³te s  se traverseze terenul pe uscat, de la poziµia 1, 1 la poziµia n, n, pe un drum
de lungime minim , mergând pas cu pas. La un pas, se ajunge de la un p tr µel la altul învecinat
cu el spre nord, est, sud sau vest.
S  se scrie un program care:
a Determin  num rul lacurilor care sunt de form  p trat  ³i în acela³i timp sunt "pline de
1".
b În cazul în care toate lacurile sunt de form  p trat  ³i în acela³i timp "pline de 1", deter-
minaµi un drum cu proprietatea de mai sus.
Date de intrare
Fi³ierul de intrare lacuri.in are pe prima linie num rul n, iar pe urm toarele n linii este harta
terenului (ecare linie are n valori 0 sau 1, separate de câte un spaµiu).
Date de ie³ire
Fi³ierul de ie³ire lacuri.out va conµine:
a pe prima linie: num rul de lacuri ce sunt de form  p trat  ³i în acela³i timp "pline de 1";
a în cazul în care toate lacurile sunt de form  p trat  ³i în acela³i timp "pline de 1", urmeaz 
liniile ce descriu drumul de lungime minim  ales. Fiecare linie din ³ier conµine câte o pereche de
coordonate ale poziµiilor succesive prin care trece drumul (linia ³i coloana, separate cu un spaµiu),
începând cu 1, 1 ³i terminând cu n, n.
Restricµii ³i preciz ri
a 2 & n & 100
a Poziµiile 1, 1 ³i n, n sunt sigur libere (cu valoarea 0).
a Dac  exist  mai multe soluµii se va a³a oricare dintre ele.
a Pot  cel mult 100 de lacuri ³i cel puµin unul; dac  un lac este de form  p trat , atunci
1 & latura & n  1.
a Indiferent de forma lor, lacurile sunt sigur "izolate", adic  nu se "ating" deloc de alt lac. De
exemplu, dac  un lac conµine poziµia 3, 3, atunci un alt lac nu poate conµine vreuna din poziµiile
învecinate cu 3, 3, adic : 2, 3, 2, 4, 3, 4, 4, 4, 4, 3, 4, 2, 3, 2 ³i 2, 2.
a Pentru cerinµa a se acord  30% din punctaj, iar pentru cerinµa b) se acord  70% din punctaj.

Exemplu
lacuri.in lacuri.out
6 4
0 0 0 0 0 0 1 1
0 1 0 1 1 1 1 2
0 0 0 1 1 1 1 3
0 0 0 1 1 1 2 3
1 1 0 0 0 0 3 3
1 1 0 0 1 0 4 3
5 3
5 4
5 5
5 6
6 6
Explicaµie:
a Prima linie conµine 4 (sunt 4 lacuri de form  p trat  ³i "pline de 1")
b Deoarece toate cele 4 lacuri sunt de form  p trat  ³i "pline de 1", se scrie ³i drumul ales:
de la 1, 1, 1, 2, 1, 3, 2, 3, 3, 3, ...., (6, 6.
Observaµii:
1 Dac  în poziµia 3, 5 ar  fost un 0, atunci lacul cu latura 3 nu ar mai  fost "plin de 1" ³i
atunci prima linie ar  conµinut doar valoarea 3 (ar  fost doar 3 lacuri p trate ³i "pline de 1").
2 în exemplul iniµial, dac  în poziµia 6, 1 ar  fost valorea 0, atunci nu ar  fost toate lacurile
p trate (cel din stânga-jos nu ar  fost p trat) ³i s-ar  a³at doar un 3 în ?ierul de ie³ire.
3 în exemplul iniµial, dac  în poziµia 5, 2 ar  fost valoarea 0, atunci s-ar  a³at doar un
3, pentru c  lacul din stânga-jos nu ar  un lac p trat ³i "plin de 1".
CAPITOLUL 33. ONI 2007 690

Timp maxim de execuµie/test: 0.25 secunde

33.3.1 Indicaµii de rezolvare

Descrierea soluµiei (prof. Dan Grigoriu)


a. Prima cerinµ : se parcurge tabloul pe linii ³i pentru ecare valoare 1 întâlnit  se veric 
dac  este colµul stânga-sus al unui p trat ; dac  da, atunci se num r  ³i se ³terge acel p trat; dac 
nu, atunci se reµine acest lucru. Apoi se reia c utarea unei eventuale alte valori 1.
b. A doua cerinµ : dac  toate lacurile sunt p trate ³i pline de 1, atunci se încearc  un drum
apropiat de diagonal  de la poziµia 1, 1 la poziµia n, n; sigur va exista un asemenea drum
deoarece lacurile sunt izolate ³i pot  ocolite.
Fie poziµia curent  i, i. Cazuri:
1. poziµia i  1, i  1 este ocupat .
În acest caz, se va c uta poziµia liber  imediat urm toare de pe diagonal . Fie ea j, j .
Drumul de la poziµia i, i la poziµia j, j  este sigur liber  (pentru c  lacurile sunt sub form 
p trat  ³i sunt izolate) e pe "deasupra" lacului întâlnit, e pe sub acesta. Veric m una dintre
rute ³i dac  e liber , atunci o folosim, altfel o alegem pe cealalt . Observaµie: în nal, dac  j n,
atunci se termin  algoritmul.
2. poziµia i  1, i  1 este liber .
În acest caz, se veric  dac  se poate ajunge la poziµia i  1, i  1 pe "deasupra" ³i atunci
se trece prin poziµia intermediar  i, i  1, iar dac  aceast  poziµie intermediar  este ocupat , se
va ajunge la poziµia i  1, i  1 prin poziµia intermediar  i  1, i, care este liber  sigur (tot din
cauz  c  lacurile sunt izolate).

33.3.2 *Cod surs 

33.3.3 Rezolvare detaliat 

Listing 33.3.1: lacuri.java


1 import java.io.*; // margine ... ??? = T6, T7
2 class lacuri
3 {
4 static StreamTokenizer st;
5 static PrintWriter out;
6 static int n;
7 static int np=0;
8 static int[][] a;
9 static int[][] b;
10
11 static final int dimq=10000;
12 static int[] q=new int[dimq]; // coada circulara
13
14 public static void main(String[] args) throws IOException
15 {
16 st=new StreamTokenizer(new BufferedReader(new FileReader("lacuri.in")));
17 out=new PrintWriter(new BufferedWriter(new FileWriter("lacuri.out")));
18
19 int i,j,k;
20
21 st.nextToken(); n=(int)st.nval;
22 a=new int[n+2][n+2];
23 b=new int[n+2][n+2];
24
25 for(i=1;i<=n;i++)
26 for(j=1;j<=n;j++)
27 {
28 st.nextToken(); a[i][j]=b[i][j]=(int)st.nval;
29 }
30
31 if(!suntnumaipatrate())
32 {
33 System.out.println("NU ... "+np);
34 out.println(np);
35 }
CAPITOLUL 33. ONI 2007 691

36 else
37 {
38 out.println(np);
39 System.out.println("DA ... "+np);
40 out.println("1 1");
41 i=1; // casuta pusa
42 j=2; // prima casuta mai departe (libera sau ocupata)
43 while(j<=n)
44 {
45 while(b[j][j]==1) j++;
46 System.out.println("i = "+i+" j = "+j);
47
48 if(j==i+1) // casute apropiate
49 {
50 if(b[i][i+1]==0)
51 out.println(i+" "+(i+1));
52 else
53 out.println((i+1)+" "+i);
54 out.println(j+" "+j);
55 i=j;
56 j=i+1;
57 }
58 else // casute departate i=pus j=nepus
59 {
60 if(b[i+1][i]==0) // in jos, apoi dreapta
61 {
62 for(k=i+1;k<=j;k++) out.println(k+" "+i);
63 for(k=i+1;k<=j;k++) out.println(j+" "+k);
64 }
65 else // dreapta, apoi in jos
66 {
67 for(k=i+1;k<=j;k++) out.println(i+" "+k);
68 for(k=i+1;k<=j;k++) out.println(k+" "+j);
69 }
70 i=j;
71 j=j+1;
72 }// else casute departate
73 }// while
74 }// else, toate patratele sunt pline de 1
75 out.close();
76 }// main
77
78 static boolean suntnumaipatrate()
79 {
80 int i,j,i0,j0,i1,j1;
81 boolean ok,okfin;
82
83 okfin=true;
84 for(i0=1;i0<=n;i0++)
85 for(j0=1;j0<=n;j0++)
86 if(a[i0][j0]==1)
87 {
88 ok=true;
89 for(j1=j0+1;j1<=n+1;j1++) if(a[i0][j1]==0) break;
90 j1--;
91 for(i1=i0+1;i1<=n+1;i1++) if(a[i1][j0]==0) break;
92 i1--;
93 System.out.print("patrat posibil: "+i0+" "+j0+" "+i1+" "+j1);
94
95 // verificare interior
96 for(i=i0+1;i<=i1-1 && ok;i++)
97 for(j=j0+1;j<=j1-1 && ok;j++)
98 if(a[i][j]==0) ok=false;
99
100 // verificare stanga
101 for(i=i0;i<=i1 && ok;i++)
102 if(a[i][j0-1]==1) ok=false;
103
104 // verificare dreapta
105 for(i=i0;i<=i1 && ok;i++)
106 if(a[i][j1+1]==1) ok=false;
107
108 // verificare sus
109 for(j=j0;j<=j1 && ok;j++)
110 if(a[i0-1][j]==1) ok=false;
111
CAPITOLUL 33. ONI 2007 692

112 // verificare jos


113 for(j=j0;j<=j1 && ok;j++)
114 if(a[i1+1][j]==1) ok=false;
115
116 fill(i0,j0);
117 if(!ok) okfin=false; else np++;
118
119 if(ok) System.out.println(" DA"); else System.out.println(" NU");
120 }// for i0 j0 if ...
121
122 return okfin;
123 }// suntnumaipatrate(...)
124
125 static void fill(int i0, int j0)
126 {
127 // recursiv depaseste stiva ... in Java !!!
128 int ic,sc,k,i,j;
129 a[i0][j0]=0;
130 k=(i0-1)*n+j0;
131 q[0]=k;
132 ic=0;
133 sc=1;
134 while(ic!=sc)
135 {
136 k=q[ic];
137 ic=(ic+1)%dimq;
138 i=(k+n-1)/n;
139 j=k%n;
140 if(j==0) j=n;
141 //System.out.println(n+" "+k+" "+i+" "+j);
142 if(a[i-1][j]==1) {a[i-1][j]=0; q[sc]=(i-2)*n+j; sc=(sc+1)%dimq;}
143 if(a[i+1][j]==1) {a[i+1][j]=0; q[sc]=(i)*n+j; sc=(sc+1)%dimq;}
144 if(a[i][j-1]==1) {a[i][j-1]=0; q[sc]=(i-1)*n+j-1; sc=(sc+1)%dimq;}
145 if(a[i][j+1]==1) {a[i][j+1]=0; q[sc]=(i-1)*n+j+1; sc=(sc+1)%dimq;}
146 }// while
147 }// fill(...)
148 }// class

33.4 Secv
Se d  un ³ir de N numere întregi A1 , A2 , ..., AN . Asupra acestui ³ir se poate efectua urm toarea
operaµie: se împarte ³irul în 3 secvenµe nevide, se calculeaz  valoarea maxim  din ecare secvenµ 
³i apoi se face suma acestor valori. Cu alte cuvinte se aleg doi indici 0 $ i $ j $ N ³i se calculeaz 
valorile
X maxrAk ¶1 & k & ix,
Y maxrAk ¶i  1 & k & j x,
Z maxrAk ¶j  1 & k & N x
³i suma S X  Y  Z .
Cerinµ 
Calculaµi valoarea minim  a lui S care se poate obµine în urma unei astfel de operaµii ³i
determinaµi cei doi indici care separ  secvenµele pentru a obµine aceast  valoare.
Date de intrare
Prima linie a ³ierului de intrare secv.in conµine un num r natural N reprezentând num rul
de elemente al ³irului de intrare, iar a doua linie conµine numerele întregi A1 , A2 , ..., AN separate
prin câte un spaµiu.
Date de ie³ire
Fi?³erul de ie³ire secv.out va conµine:
- pe prima linie: valoarea minim  a sumei;
- pe a doua linie: dou  numere naturale i, j separate printr-un spaµiu, reprezentând indicii
pentru care se obµine valoarea minim  pentru S prin aplicarea operaµiei descrise mai sus.
Restricµii ³i preciz ri
a 3 & N & 30000
a A1 , A2 , ..., AN sunt numere întregi din intervalul 10000, 10000
CAPITOLUL 33. ONI 2007 693

a în cazul în care exist  mai multe soluµii se poate a³a oricare dintre ele.
Exemplu
secv.in secv.out Explicaµii
7 10 Prima secvenµ  : 3 2 - maximul este 3
3215632 23 A doua secvenµ  : 1 - maximul este 1
A treia secvenµ  : 5 6 3 2 - maximul este 6
Suma: 10
Timp maxim de execuµie/test: 0.1 secunde

33.4.1 Indicaµii de rezolvare

Descrierea soluµiei (Adrian Diaconu)


Vom nota cu X maximul elementelor din ³ir. X va ap rea ca maxim într-una din cele 3
secvenµe. Se disting trei cazuri:
a X aparµine primei secvenµe. Acum se poate observa c  a doua secvenµ  se poate forma
dintr-un singur element pe cazul optim deoarece, în caz contrar celealte elemente se pot lipi
primei secvenµe (lucru care nu ar m ri costul). De aceea vom considera c  ultima secvenµ  este
pe rând format  din ultimele i elemente, secvenµa 2 este format  din elementul N  i ³i prima
secvenµ  din primele N  i  1 ³i alegem minimul dintre toate aceste posibilit µi de secc tionare.
b X aparµine celei de a doua secvenµe, caz în care prima ³i ultima secvenµ  le vom considera
formate dintr-un singur element dintr-un raµionament asem n tor cu punctul a.
c X aparµine celei de a treia secvenµe caz simetric cu cel de la punctul a.
Se alege minim global al tuturor celor trei cazuri.
Complexitate: O n

33.4.2 *Cod surs 

33.4.3 Rezolvare detaliat 

Listing 33.4.1: secv.java


1 import java.io.*;
2 class secv
3 {
4 static StreamTokenizer st;
5 static PrintWriter out;
6 static int n;
7 static int[] a;
8 static final int oo=300000001;
9
10 public static void main(String[] args) throws IOException
11 {
12 int i,j,x,y,z,max,imax=0,smin;
13 int s1=oo,s2=oo,s3=oo;
14 int i1=0,j1=0,i2=0,j2=0,i3=0,j3=0;
15
16 st=new StreamTokenizer(new BufferedReader(new FileReader("10-secv.in")));
17 out=new PrintWriter(new BufferedWriter(new FileWriter("secv.out")));
18
19 st.nextToken(); n=(int)st.nval;
20 a=new int[n+1];
21
22 max=-oo;
23 for(i=1;i<=n;i++)
24 {
25 st.nextToken(); a[i]=(int)st.nval;
26 if(a[i]>max) { max=a[i]; imax=i;}
27 }
28
29 // max in prima secventa (daca imax <= n-2)
30 if(imax<=n-2)
31 {
32 x=max;
CAPITOLUL 33. ONI 2007 694

33 s1=oo; // minim
34 for(i=imax+1;i<=n-1;i++)
35 {
36 y=a[i];
37 z=-oo;
38 for(j=i+1;j<=n;j++) if(a[j]>z) { z=a[j]; j1=j;}
39 if(x+y+z<s1) { s1=x+y+z; i1=i; j1=i1+1;}
40 }// for
41 }// if
42
43 // x in a doua secventa
44 if((imax>1)&&(imax<n))
45 {
46 s2=max+a[1]+a[n];
47 i2=1;
48 j2=n-1;
49 }
50
51 // max in a treia secventa
52 if(imax>=3)
53 {
54 z=max;
55 s3=oo; // minim
56 for(j=2;j<=imax-1;j++)
57 {
58 y=a[j];
59 x=0;
60 for(i=1;i<=j;i++) if(a[i]>x) {x=a[i]; i3=i;}
61 if(x+y+z<s3) { s3=x+y+z; j3=j;}
62 }// for
63 i3=j3-1;
64 }// if
65
66 smin=min(min(s1,s2),s3);
67 out.println(smin);
68 if(smin==s1) out.println(i1+" "+j1); else
69 if(smin==s2) out.println(i2+" "+j2); else
70 out.println(i3+" "+j3);
71 out.close();
72 }// main
73
74 static int min(int a, int b)
75 {
76 if(a<b) return a; else return b;
77 }// min(...)
78 }// class

33.5 “otron
Pe asfalt este desenat cu cret  un ³otron, caroiaj format din n ˜ n c suµe având acelea³i
dimensiuni (câte n c suµe pe ecare din cele n rânduri).
În ecare c suµ  este scris câte un num r întreg din intervalul 100, 100. Fiecare juc tor are
câte o piatr  pe care o arunc  într-o c suµ  a ³otronului, ³i s rind într-un picior, împinge piatra
din c suµ  în c suµ , pe un anumit traseu astfel încât punctajul obµinut din suma numerelor de
pe traseul parcurs s  e cât mai mare.
Numerele din c suµele ³otronului sunt scrise cu dou  culori albastru ³i alb, astfel încât s  nu
existe dou  c suµe al turate (pe cele patru direcµii Nord, Est, Sud, Vest) având numere scrise cu
aceea³i culoare. Întotdeauna, prima c suµ  din primul rând al ³otronului are înscris un num r de
culoare albastr .
Se stabilesc apoi, urm toarele reguli ale jocului:
- la începutul jocului, piatra poate  aruncat  în oricare c suµ  a ³otronului. Din poziµia
respectiv  juc torul î³i conduce piatra pân  la sfâr³itul traseului stabilit de el;
- dintr-o c suµ  în care num rul este scris cu albastru, piatra poate  deplasat  doar în c suµa
vecin  pe direcµia Nord;
- dintr-o c suµ  în care num rul este scris cu alb, piatra poate  deplasat  doar în c suµa
vecin  pe direcµia Est;
- juc torul poate alege orice c suµ  (inclusiv cea în care a aruncat piatra) pentru a încheia
jocul, atâta timp cât piatra nu iese din ³otron.
CAPITOLUL 33. ONI 2007 695

Figura 33.2: Sotron1

Cerinµ 
S  se scrie un program care s  determine cel mai mare punctaj care se poate obµine jucând
³otron dup  regulile stabilite.
Date de intrare
Fi³ierul de intrare sotron.in are pe prima linie dimensiunea n a ³otronului, iar pe urm toarele
n linii câte n numere separate de câte un spaµiu, reprezentând numerele scrise în ³otron.
Date de ie³ire
Fi³ierul de ie³ire sotron.out va conµine pe prima linie un singur num r reprezentând punctajul
maxim care se poate obµine jucând ³otron.
Restricµii ³i preciz ri
1 & n & 240
Exemplu
secv.in secv.out Explicaµii
5 21
0 -6 -5 -17 2 Punctajul obµinut este
1 -4 7 10 5 3+(-2)+3+7+10=21
-3 -2 3 -8 -8
-20 3 5 3 -5
-10 -15 2 2 -4
Timp maxim de execuµie/test(Windows/Linux): 0.1 secunde

33.5.1 Indicaµii de rezolvare

Descrierea soluµiei (prof. Cristina B rbieru)


Se deplaseaz  piatra în ³otron din c suµ  în c suµ , conform regulilor din enunµ, identicându-
se astfel n trasee(în form  de zig-zag) de lungime (num r de c suµe) maxim  posibil . Aceste
trasee pornesc din ecare c suµ  în care e scris un num r cu albastru, de pe prima coloan  a
³otronului sau dintr-o c suµ  cu num r scris cu alb de pe ultima linie a ³otronului.

Figura 33.3: Sotron2

Pentru a calcula punctajul maxim obµinut de un juc tor, se calculeaz  pentru ecare astfel
de traseu suma maxim  a numerelor unei succesiuni de c suµe vecine. Numerele scrise într-un
traseu determinant la un moment dat se pot memora într-un vector, astfel, acest  problem  este
echivalent  cu determinarea unei secvenµe de sum  maxim  dintr-un vector.
CAPITOLUL 33. ONI 2007 696

33.5.2 *Cod surs 

33.5.3 Rezolvare detaliat 


Listing 33.5.1: sotron.java
1 import java.io.*;
2 class Sotron
3 {
4 static StreamTokenizer st;
5 static PrintWriter out;
6 static int n,smaxsol=-101;
7 static int[][] a;
8 static int[] x;
9
10 public static void main(String[] args) throws IOException
11 {
12 int i,j,ii,jj,nc,smaxc;
13
14 st=new StreamTokenizer(new BufferedReader(new FileReader("sotron.in")));
15 out=new PrintWriter(new BufferedWriter(new FileWriter("sotron.out")));
16
17 st.nextToken(); n=(int)st.nval;
18 a=new int[n+1][n+1];
19 x=new int[2*n];
20
21 for(i=1;i<=n;i++)
22 for(j=1;j<=n;j++)
23 {
24 st.nextToken(); a[i][j]=(int)st.nval;
25 }
26
27 // prima coloana
28 for(i=1;i<=n;i++)
29 {
30 if(i%2==0) continue;
31
32 nc=0;
33 x[++nc]=a[i][1];
34 ii=i-1;
35 jj=1;
36 while(ii>=1)
37 {
38 x[++nc]=a[ii][jj]; // sus
39 x[++nc]=a[ii][++jj]; // dreapta
40 ii--;
41 }
42 smaxc=secvsummax(nc);
43 if(smaxc>smaxsol) smaxsol=smaxc;
44 }// for i
45
46 // ultima linie
47 for(j=1;j<=n;j++)
48 {
49 if(n%2==0 && j%2==0) continue;
50 if(n%2==1 && j%2==1) continue;
51
52 nc=0;
53 x[++nc]=a[n][j];
54 ii=n;
55 jj=j+1;
56 while(jj<=n)
57 {
58 x[++nc]=a[ii][jj]; // dreapta
59 x[++nc]=a[--ii][jj]; // sus
60 jj++;
61 }
62 smaxc=secvsummax(nc);
63 if(smaxc>smaxsol) smaxsol=smaxc;
64 }// for j
65
66 out.println(smaxsol);
67 out.close();
CAPITOLUL 33. ONI 2007 697

68 }// main(...)
69
70 static int secvsummax(int n)
71 {
72 int i,i1,i2,ic,sc,smax;
73 i1=i2=ic=1;
74 smax=sc=x[1];
75 for(i=2;i<=n;i++)
76 {
77 if(sc<=0) { ic=i; sc=x[i]; continue; } else sc=sc+x[i];
78 if(sc>=smax) { smax=sc; i1=ic; i2=i; }
79 }
80 //System.out.println("smax = "+smax+" x["+i1+"..."+i2+"]");
81 return smax;
82 }// secvsummax(...)
83 }// class

33.6 Triunghi
În comuna Triunghi din România sunt n µ rani codicaµi prin numerele 1, 2, ..., n. Dup 
anul 1990 a început retrocedarea suprafeµelor de p mânt deµinute înainte de colectivizare. Fiecare
µ ran are un document prin care dovede³te c  este proprietar pe o singur  suprafaµ  de teren de
form  triunghiular . Din p cate, documentele dau b taie de cap primarului (care se ocup  de
retrocedarea suprafeµelor de p mânt), pentru c  sunt porµiuni din suprafeµele de p mânt care se
reg sesc pe mai multe documente.
În aceast  comun  exist  o fântân  cu ap , ind posibil ca ea s  e revendicat  de mai mulµi
µ rani. O suprafaµ  de p mânt este dat  prin coordonatele celor trei colµuri, iar fântâna este
considerat  punctiform  ³i dat  prin coordonatele punctului.
Cerinµ 
S  se scrie un program care s  determine:
a Codurile µ ranilor care au documente cu suprafeµe de p mânt ce conµin în interior sau pe
frontier  fântâna.
b Codul µ ranului ce deµine un document cu suprafaµa de teren, care include toate celelalte
suprafeµe.
Date de intrare
Fi³ierul de intrare triunghi.in are pe prima linie num rul n de µ rani, pe urm toarele n linii
câte 6 valori numere întregi separate prin câte un spaµiu, în formatul: x1 y1 x2 y2 x3 y3, ce
reprezint  coordonatele celor trei colµuri ale suprafeµei triunghiulare deµinute de un µ ran (x1, x2,
x3 abscise, iar y1, y2, y3 ordonate). Pe linia i  1 se a  coordonatele colµurilor suprafeµei de
teren triunghiulare deµinute de µ ranul i, i 1, 2, ..., n. Ultima linie a ³ierului (linia n  2) va
conµine coordonatele fântânii în formatul x y , cu un spaµiu între ele (x abscis , iar y ordonat ).
Date de ie³ire
Fi³ierul de ie³ire triunghi.out va conµine pe prima linie r spunsul de la punctul a, adic :
num rul de µ rani care îndeplinesc condiµia din cerinµ  ³i apoi codurile lor (în ordine cresc toare),
cu un spaµiu între ele. Dac  nu exist  µ rani cu condiµia din cerinµ , pe prima linie se va scrie cifra
0. Pe linia a doua se va scrie r spunsul de la punctul b, adic : codul µ ranului cu proprietatea
cerut , sau cifra 0, dac  nu exist  un astfel de µ ran.
Restricµii ³i preciz ri
a 2 & n & 65
a coordonatele colµurilor suprafeµelor de p mânt ³i ale fântânii sunt numere întregi din inter-
valul 3000, 3000
a cele trei colµuri ale ec rei suprafeµe de p mânt sunt distincte ³i necoliniare
a nu exist  doi µ rani care s  deµin  aceea³i suprafaµ  de p mânt
a nu se acord  punctaje parµiale.

Exemplu
CAPITOLUL 33. ONI 2007 698

secv.in secv.out
3 212
10 0 0 10 10 10 2
0 100 100 0 -100 0
0 0 10 0 0 10
10 5
Explicaµie:
La punctul a, sunt doi µ rani care deµin suprafeµe de p mânt ce au în interior sau pe frontier 
fântâna, cu codurile 1 ³i 2.
La punctul b, µ ranul cu codul 2 deµine o suprafaµ  de teren care include, suprafeµele de
p mânt deµinute de ceilalµi µ rani (cu codurile 1 ³i 3).
Timp maxim de execuµie/test: 0.1 secunde

33.6.1 Indicaµii de rezolvare

Descrierea soluµiei (prof. Doru Popescu Anastasiu)


Not m cu T1 , T2 , ..., Tn triunghiurile corespunz toare suprafeµelor ³i cu I punctul unde se
g se³te fântâna.
Ti Ai Bi Ci , i 1, 2, ..., n.
a
nr 0
Pentru i 1, ..., n veric m dac  I este interior sau pe frontiera lui Ti , în caz armativ
nr nr  1 ³i solnr i. A³ m nr ³i vectorul sol.
Pentru a verica dac  I este interior sau pe frontiera unui triunghi Ti este sucient s  veric m
dac :
aria Ai Bi Ci  aria IAi Bi   aria IAi Ci   aria IBi Ci 
O alt  variant  ar  s  folosim poziµia unui punct faµ  de o dreapt .
b
Dac  exist  un asemenea triunghi atunci el este de arie maxim . Astfel determin m triunghiul
p de arie maxim . Pentru acest triunghi veric m dac  toate celelalte n  1 triunghiuri sunt
interioare sau pe frontiera lui Tp (adic  dac  au toate vârfurile în interiorul sau pe frontiera lui
Tp ). În caz armativ se a³eaz  p, altfel 0.

33.6.2 *Cod surs 


33.6.3 Rezolvare detaliat 

Listing 33.6.1: triunghi.java


1 import java.io.*;
2 class Triunghi
3 {
4 static int n,x0,y0;
5 static int smax=-1,imax=-1,nr=0;
6 static int[] x1=new int[66];
7 static int[] y1=new int[66];
8 static int[] x2=new int[66];
9 static int[] y2=new int[66];
10 static int[] x3=new int[66];
11 static int[] y3=new int[66];
12 static int[] sol=new int[66];
13
14 public static void main(String[] args) throws IOException
15 {
16 int i,j,s,s1,s2,s3;
17 boolean ok;
18
19 StreamTokenizer st=new StreamTokenizer(
20 new BufferedReader(new FileReader("14-triunghi.in")));
21 PrintWriter out=new PrintWriter(
22 new BufferedWriter(new FileWriter("triunghi.out")));
23 st.nextToken(); n=(int)st.nval;
CAPITOLUL 33. ONI 2007 699

24 for(i=1;i<=n;i++)
25 {
26 st.nextToken(); x1[i]=(int)st.nval;
27 st.nextToken(); y1[i]=(int)st.nval;
28 st.nextToken(); x2[i]=(int)st.nval;
29 st.nextToken(); y2[i]=(int)st.nval;
30 st.nextToken(); x3[i]=(int)st.nval;
31 st.nextToken(); y3[i]=(int)st.nval;
32 }
33 st.nextToken(); x0=(int)st.nval;
34 st.nextToken(); y0=(int)st.nval;
35 for(i=1;i<=n;i++)
36 {
37 s=aria(x1[i],y1[i],x2[i],y2[i],x3[i],y3[i]);
38 if(s>smax) {smax=s; imax=i;}
39 s1=aria(x1[i],y1[i],x2[i],y2[i],x0,y0);
40 s2=aria(x2[i],y2[i],x3[i],y3[i],x0,y0);
41 s3=aria(x1[i],y1[i],x3[i],y3[i],x0,y0);
42 if(s==s1+s2+s3) {nr++; sol[nr]=i;}
43 //System.out.println("i = "+i+" --> "+s+" "+s1+" "+s2+" "+s3);
44 }
45 if(nr>0)
46 {
47 out.print(nr+" ");
48 for(i=1;i<=nr;i++)
49 if(i!=nr) out.print(sol[i]+" "); else out.println(sol[i]);
50 }
51 else out.println(0);
52
53 //System.out.println("imax = "+imax);
54 ok=true;
55 for(i=1;i<=n;i++)
56 {
57 if(i==imax) continue;
58
59 s1=aria(x1[imax],y1[imax],x2[imax],y2[imax],x1[i],y1[i]);
60 s2=aria(x2[imax],y2[imax],x3[imax],y3[imax],x1[i],y1[i]);
61 s3=aria(x1[imax],y1[imax],x3[imax],y3[imax],x1[i],y1[i]);
62 if(smax!=s1+s2+s3) { ok=false; break; }
63
64 s1=aria(x1[imax],y1[imax],x2[imax],y2[imax],x2[i],y2[i]);
65 s2=aria(x2[imax],y2[imax],x3[imax],y3[imax],x2[i],y2[i]);
66 s3=aria(x1[imax],y1[imax],x3[imax],y3[imax],x2[i],y2[i]);
67 if(smax!=s1+s2+s3) { ok=false; break; }
68
69 s1=aria(x1[imax],y1[imax],x2[imax],y2[imax],x3[i],y3[i]);
70 s2=aria(x2[imax],y2[imax],x3[imax],y3[imax],x3[i],y3[i]);
71 s3=aria(x1[imax],y1[imax],x3[imax],y3[imax],x3[i],y3[i]);
72 if(smax!=s1+s2+s3) { ok=false; break; }
73 }
74 if(ok) out.println(imax); else out.println(0);
75 out.close();
76 }// main(...)
77
78 static int aria(int x1, int y1, int x2, int y2, int x3, int y3) // dubla ...
79 {
80 int s=x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1;
81 if(s<0) s=-s;
82 return s;
83 }
84 }// class
Capitolul 34

ONI 2006

Figura 34.1: Sigla ONI 2006

34.1 Factorial
autor Mânz Victor Liceul
teoretic "C. Brancoveanu" Bucuresti
Pentru un num r natural nenul, denim factorialul s u ca ind produsul tuturor numerelor
naturale nenule mai mici sau egale decât el ³i îl not m N ! (adic  N ! 1 ˜ 2 ˜ ... ˜ N ). Pentru
o baz  de numeraµie B ³i un num r natural nenul N , se cere determinarea ultimei cifre nenule a
scrierii în baza B a lui N !.
Cerinµ 
Se citesc 5 perechi de forma Ni , Bi , unde 1 & i & 5. Pentru ecare din cele 5 perechi citite,
aaµi ultima cifr  nenul  a scrierii în baza Bi a factorialului num rului Ni .
Date de intrare
Fi³ierul de intrare fact.in conµine 5 linii, pe ecare dintre ele ind scrise câte dou  numere
naturale nenule Ni ³i Bi , scrise în baza 10, desp rµite printr-un spaµiu.
Date de ie³ire
Fi³ierul de ie³ire fact.out va conµine 5 linii. Pe linia i se va aa cifra corespunz toare unei
perechi Ni , Bi , citit  de pe linia i din ³ierul de intrare.
Restricµii ³i preciz ri
ˆ 1 & Ni & 1000000, pentru 1 & i & 5;
ˆ 2 & Bi & 36, pentru 1 & i & 5;
ˆ în cazul în care Bi % 10, cifrele mai mari decât 9 vor  reprezentate prin litere mari ale
¬ ¬ ¬ ¬ ¬ ¬
alfabetului englez (10 A , 11 B ,...,35 Z );

ˆ un test va  punctat doar dac  toate cele 5 rezultate cerute sunt corecte.

700
CAPITOLUL 34. ONI 2006 701

Exemplu
.in .out Explicaµie
5 10 2 5! 120, în baza 10, deci ultima cifr  nenul  este 2
7 10 4 7! 5040, în baza 10, deci ultima cifr  nenul  este 4
7 20 C 7! CC0, în baza 20, deci ultima cifr  nenul  este C
8 16 8 8! 9D80, în baza 16, deci ultima cifr  nenul  este 8
9 8 6 9! 1304600, în baza 8, deci ultima cifr  nenul  este 6
Timp maxim de execuµie/test: 1 secund  (Windows), 0.3 secunde (Linux)

34.1.1 Indicaµii de rezolvare

Soluµia comisiei
Se descompune mai întâi B în produs de factori primi. Retinem în di0 factorul prim
cu numarul de ordine i ³i în di1 puterea la care apare acesta în descompunerea lui B .
Apoi se parcurg numerele de la 1 la N ³i din ecare dintre acestea se elimin  toµi factorii primi
ai lui B , reµinuµi în di0. Se p streaz  totodat  în di2 puterea la care ar  aparut ace³tia
în descompunerea lui N !. Odat  cu eliminarea factorilor primi ai lui B din N !, se calculeaz  ³i
restul la împ rµirea cu B al produsului (notat p).
Se calculeaz  apoi num rul de zerouri consecutive care se vor aa la sfâr³itul lui N !, reprezentat
în baza B : minimul rapoartelor dintre di2 ³i di1, notat min. Se face diferenta între di2
³i min ˜ di1, adic  num rul de elemente di0 care mai trebuie înmulµite cu p ³i se fac aceste
înmulµiri moduloB . Acesta este rezultatul c utat.

34.1.2 Cod surs 

Listing 34.1.1: fact.cpp


1 #include<stdio.h>
2 #define lmax 10000
3
4 FILE *fin,*fout;
5
6 void multiply(long x[lmax],long y,int b)
7 {
8 long p[lmax],i,j,t,aux;
9
10 p[0]=x[0];
11 for(i=1;i<lmax;i++) p[i]=0;
12 t=0;
13 for(i=1;i<=x[0];i++)
14 {
15 p[i]=x[i]*y+t;
16 t=p[i]/b;
17 p[i]=p[i]%b;
18 }
19 while(t)
20 {
21 p[i]=t%b;
22 t=t/b;
23 i++;
24 }
25 p[0]=i-1;
26 for(i=0;i<=p[0];i++) x[i]=p[i];
27 }
28
29 void print(long x[lmax])
30 {
31 int i;
32
33 i=1;
34 while(!x[i]) i++;
35 if(x[i]<10) fprintf(fout,"%ld\n",x[i]);
36 else fprintf(fout,"%c\n",’A’+x[i]-10);
37 }
38
39 int main()
CAPITOLUL 34. ONI 2006 702

40 {
41 long n,b,i,j;
42 long x[lmax];
43
44 fin=fopen("fact.in","r");
45 fout=fopen("fact.out","w");
46 for(i=1;i<=5;i++)
47 {
48 fscanf(fin,"%ld %ld",&n,&b);
49 x[0]=x[1]=1;
50 for(j=1;j<=n;j++)
51 multiply(x,j,b);
52 print(x);
53 for(j=0;j<lmax;j++) x[j]=0;
54 }
55 fclose(fin);
56 fclose(fout);
57 return 0;
58 }

Listing 34.1.2: factok.cpp


1 #include<stdio.h>
2
3 #define T 5
4
5 long n,b,d[10][3],nr;
6
7 FILE *fin=fopen("fact.in","r");
8 FILE *fout=fopen("fact.out","w");
9
10 void desc(long b)
11 {
12 nr=0;
13 for(long i=2;b>1;i++)
14 if(!(b%i))
15 {
16 d[nr][0]=i;
17 d[nr][1]=0;
18 while(!(b%i))
19 {
20 d[nr][1]++;
21 b/=i;
22 }
23 nr++;
24 }
25 }
26
27 long cifra()
28 {
29 long i,j,p=1,aux;
30 for(j=0;j<nr;j++)
31 d[j][2]=0;
32 for(i=1;i<=n;i++)
33 {
34 aux=i;
35 for(j=0;j<nr;j++)
36 {
37 while(!(aux%d[j][0]))
38 {
39 d[j][2]++;
40 aux/=d[j][0];
41 }
42 }
43
44 p=(p*(aux%b))%b;
45 }
46
47 return p;
48 }
49
50 long minim()
51 {
52 long min=d[0][2]/d[0][1];
53 for(long i=1;i<nr;i++)
CAPITOLUL 34. ONI 2006 703

54 if(d[i][2]/d[i][1]<min)
55 min=d[i][2]/d[i][1];
56 return min;
57 }
58
59 long calcul()
60 {
61 long i,j,p=cifra();
62 long min=minim();
63 for(i=0;i<nr;i++)
64 for(j=0;j<d[i][2]-d[i][1]*min;j++)
65 p=(p*d[i][0])%b;
66 return p;
67 }
68
69 void scrie()
70 {
71 long x=calcul();
72 if(x<10)
73 fprintf(fout,"%ld\n",x);
74 else
75 fprintf(fout,"%c\n",’A’+(x-10));
76 }
77
78 int main()
79 {
80 for(int i=0;i<T;i++)
81 {
82 fscanf(fin,"%ld%ld",&n,&b);
83 desc(b);
84 scrie();
85 }
86
87 fclose(fin);
88 fclose(fout);
89 return 0;
90 }

34.1.3 Rezolvare detaliat 


Listing 34.1.3: fact.java
1 import java.io.*;
2 class Factorial
3 {
4 static int n,b,m,p;
5 static int[] fb=new int[10];
6 static int[] eb=new int[10];
7 static int[] en=new int[10];
8
9 static void descf(int nr)
10 {
11 int d;
12 m=0;
13
14 d=2;
15 if(nr%d==0) {m++; fb[m]=d; eb[m]=0;}
16 while(nr%d==0) {eb[m]++; nr/=d; }
17
18 d=3;
19 while(d*d<=nr)
20 {
21 if(nr%d==0) {m++; fb[m]=d; eb[m]=0;}
22 while(nr%d==0) {eb[m]++;nr/=d; }
23 d=d+2;
24 }
25 if(nr!=1) {m++; fb[m]=nr; eb[m]=1;}
26 }//descf(...)
27
28 static void calcul()
29 {
30 int i,ii,j,min;
31 descf(b);
32 p=1;
CAPITOLUL 34. ONI 2006 704

33 for(j=1;j<=m;j++) en[j]=0;
34 for(i=2;i<=n;i++) // n!
35 {
36 ii=i;
37 for(j=1;j<=m;j++) while(ii%fb[j]==0) { en[j]++; ii/=fb[j]; }
38 p=(p*(ii%b))%b;
39 }
40
41 min=en[1]/eb[1];
42 for(j=2;j<=m;j++) if(en[j]/eb[j]<min) min=en[j]/eb[j];
43 for(j=1;j<=m;j++) en[j]=en[j]-min*eb[j];
44 for(j=1;j<=m;j++) for(i=1;i<=en[j];i++) p=(p*(fb[j]%b))%b;
45 }// calcul()
46
47 public static void main(String[] args) throws IOException
48 {
49 int k;
50 StreamTokenizer st=new StreamTokenizer(
51 new BufferedReader(new FileReader("fact.in")));
52 PrintWriter out=new PrintWriter(
53 new BufferedWriter(new FileWriter("fact.out")));
54
55 for(k=1;k<=5;k++)
56 {
57 st.nextToken(); n=(int)st.nval;
58 st.nextToken(); b=(int)st.nval;
59 calcul();
60 if(p<=9) out.println(p); else out.println((char)(p-10+’A’));
61 }
62 out.close();
63 }// main(...)
64 }// class

34.2 Limbaj
autor Domsa Ovidiu
Universitatea 1 Decembrie 1918 Alba Iulia
Denim un limbaj de programare, cu instrucµiuni de atribuire ³i de decizie. Sintaxa instruc-
µiunilor este:
Instrucµiunea de atribuire

variabila=variabila
Instrucµiunea de decizie

if
semn variabila variabila
{da
instrucµiuni }
{ nu
instrucµiuni }

Variabilele limbajului sunt identicate printr-un singur caracter, liter  mic , din alfabetul
englez. Valorile variabilelor sunt de tip întreg.
semn este unul din caracterele $ , % sau
¬ ¬ ¬ ¬ ¬ ¬
.
Liniile instrucµiunilor nu conµin spaµii.
Instrucµiunile din { } pot lipsi.
Cerinµ 
Dac  se dau valorile iniµiale ale tuturor variabilelor (de la a la z ), în ordine alfabetic  începând
cu a, se cer valorile tuturor variabilelor de la a la z , dup  execuµia secvenµei de program.
Date de intrare
Fi³ier de intrare: limbaj.in
Linia 1: Va Vb ... Vz - Valorile iniµiale ale variabilelor, de la a la z , separate prin câte un
spaµiu.
Linia 2: linie de program - Urm toarele linii, pân  la sfâr³itul ³ierului conµin linii de program,
CAPITOLUL 34. ONI 2006 705

Linia 3: linie de program cu instrucµiuni corecte din punct de vedere sintactic.


....
Date de ie³ire
Fi³ier de ie³ire: limbaj.out
Linia 1: Va Vb ... Vz - ³irul valorilor variabilelor, de la a la z , în ordine alfabetic , pe un rând,
separate prin câte un spaµiu.
Restricµii ³i preciz ri
ˆ 30000 $ a, b, .., z $ 30000
ˆ Num rul liniilor de program este mai mic decât 10000
ˆ Limbajul este case sensitive (se folosesc doar litere mici de la a la z ).

Exemplu
limbaj.in
13574000000000000000000000
if
=ab
nu
if
$ab
da
b=d

f=d


limbaj.out
17574070000000000000000000
Timp maxim de execuµie/test: 0.5 secunde (Windows), 0.1 secunde (Linux)

34.2.1 Indicaµii de rezolvare

Soluµia comisiei
Soluµia problemei const  în parcurgerea liniar  a instrucµiunilor linie cu linie ³i p strarea
valorilor logice ale condiµiilor ³i respectiv a ramurilor da ³i nu într-un ³ir.
Semnicaµia poziµiei din ³ir este nivelul de imbricare la care ne a m. Practic parcurgem, f r 
a evalua, acele instrucµiuni care nu aparµin nivelului ³i ramurii pe care ne a m.
În acest sens vom evalua instrucµiunile de atribuire doar cele care îndeplinesc condiµiile de a
se aa pe nivelul corespunz tor ³i pentru care valorile condiµiilor ³i respectiv a ramurilor sunt în
aceal³i timp adevarate.

34.2.2 Cod surs 

Listing 34.2.1: limbaj.cpp


1 #include <stdio.h>
2 #include <string.h>
3
4 int to_lower(char *string)
5 {
6 int i=0;
7 for(i=0;i<strlen(string);i++)
8 {
9 if ((string[i]<91) && (string[i]>=65))
10 string[i]+=32;
11 }
12 return 0;
13 }
14
15 int main()
CAPITOLUL 34. ONI 2006 706

16 {
17 freopen("limbaj.in","rt",stdin);
18 freopen("limbaj.out","wt",stdout);
19
20 int v[26];
21 int nivel[2500];
22
23 memset(nivel,0,2500*sizeof(int));
24
25 int niv=0;
26 for(int i=0;i<26;i++)
27 scanf("%d",&v[i]);
28
29 int flag=0;
30 char string[200];
31
32 while(scanf("%s",string)>0)
33 {
34 to_lower(string);
35
36 if(flag=1 && strlen(string)==3 && string[1]==’=’)
37 {
38 v[string[0]-97]=v[string[2]-97];
39 continue;
40 }
41
42 if(!strcmp(string,"if"))
43 {
44 scanf("%s",string);
45 if(string[0]==’=’)
46 if(v[string[1]-97]==v[string[2]-97])
47 nivel[niv]=1;
48
49 if(string[0]==’>’)
50 if(v[string[1]-97]>v[string[2]-97])
51 nivel[niv]=1;
52
53 if(string[0]==’<’)
54 if(v[string[1]-97]<v[string[2]-97])
55 nivel[niv]=1;
56
57 niv++;
58 continue;
59 }
60
61 if(!strcmp(string,"fi"))
62 {
63 nivel[niv-1]=0;
64 niv--;
65 }
66
67 //sar peste toate liniile din da, daca asta e fals
68 if((!strcmp(string,"da")) && (nivel[niv-1]==0))
69 {
70 flag=0;
71 char string1[10];
72 int niv1=0;
73 while(flag==0)
74 {
75 scanf("%s",string1);
76 if(!strcmp(string1,"if")) niv1++;
77 if(!strcmp(string1,"fi")&&(niv1>0)) niv1--;
78 if(!strcmp(string1,"fi")&&(niv1==0)) flag=1;
79 if(!strcmp(string1,"nu")&&(niv1==0)) flag=1;
80 }
81
82 continue;
83 }
84
85 //sar peste toate liniile din nu, daca asta e fals
86 if((!strcmp(string,"nu")) && (nivel[niv-1]==1))
87 {
88 flag=0;
89 char string1[10];
90 int niv1=0;
91 while(flag==0)
CAPITOLUL 34. ONI 2006 707

92 {
93 scanf("%s",string1);
94 if(!strcmp(string1,"if")) niv1++;
95 if(!strcmp(string1,"fi")&&(niv1>0)) niv1--;
96 if(!strcmp(string1,"fi")&&(niv1==0)) flag=1;
97 }
98 continue;
99 }
100 }
101
102 //afisarea
103 for(int j=0;j<26;j++)
104 printf("%d ",v[j]);
105 printf("\n");
106
107 return 0;
108 }

34.2.3 Rezolvare detaliat 


Variant  cu mesaje:

Listing 34.2.2: limbaj1.java


1 import java.io.*; // test 25 g,h= gresit la ei!!!
2 class LimbajMesaje // test 26 ==> liniile 321 si 400 si 479= "fiif" ... ???
3 { // test 28 ==> multe linii cu "fiif" ... ???
4 static int[] x=new int[26];
5 static char[] a=new char[4];
6 static String s;
7
8 static BufferedReader brin;
9 static StreamTokenizer stin;
10 static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
11
12 static void afisx()
13 {
14 int i;
15 for(i=0;i<26;i++) System.out.print(x[i]+" ");
16 System.out.println();
17 }// afisx()
18
19 static void afisa()
20 {
21 int i;
22 for(i=0;i<a.length;i++) System.out.print(a[i]+" ");
23 System.out.println();
24 }// afisa()
25
26 static boolean esteif()
27 {
28 if((a[0]==’i’)&&(a[1]==’f’)) return true; else return false;
29 } // esteif()
30
31 static boolean estefi()
32 {
33 if((a[0]==’f’)&&(a[1]==’i’)) return true; else return false;
34 }// estefi()
35
36 static boolean esteda()
37 {
38 if((a[0]==’d’)&&(a[1]==’a’)) return true; else return false;
39 }// esteda()
40
41 static boolean estenu()
42 {
43 if((a[0]==’n’)&&(a[1]==’u’)) return true; else return false;
44 }// estenu()
45
46 static void blocda(boolean ok) throws IOException
47 {
48 System.out.println(" --> blocDA: "+s+" ok="+ok); //br.readLine();
49 bloc(ok);
50 System.out.println(" <-- blocDA: "+s+" ok="+ok); //br.readLine();
CAPITOLUL 34. ONI 2006 708

51 }// blocda(...)
52
53 static void blocnu(boolean ok) throws IOException
54 {
55 System.out.println(" --> blocNU: "+s+" ok="+ok); //br.readLine();
56 bloc(ok);
57 System.out.println(" <-- blocNU: "+s+" ok="+ok); //br.readLine();
58 }// blocnu(...)
59
60 static void blocif(boolean ok) throws IOException
61 {
62 System.out.println(" --> blocif: "+s+" ok="+ok); //br.readLine();
63 boolean oke=false;
64
65 // urmeaza expresia logica (conditia din IF)
66 s=brin.readLine(); a=s.toCharArray();
67 System.out.println("evaluare expresie: "+s+" --> oke = "+oke+" ok="+ok);
68 //br.readLine();
69 afisx();
70
71 if(a[0]==’=’ && x[a[1]-’a’] == x[a[2]-’a’]) oke=true; else
72 if(a[0]==’<’ && x[a[1]-’a’] < x[a[2]-’a’]) oke=true; else
73 if(a[0]==’>’ && x[a[1]-’a’] > x[a[2]-’a’]) oke=true;
74
75 // urmeaza "DA" sau "NU"
76 s=brin.readLine(); a=s.toCharArray();
77
78 System.out.println(" ? bloc DA ? s= "+s);
79 if(esteda())
80 {
81 s=brin.readLine(); a=s.toCharArray();
82 blocda(ok && oke);
83 }
84
85 System.out.println(" ? bloc NU ? s= "+s);
86 if(estenu())
87 {
88 s=brin.readLine(); a=s.toCharArray();
89 blocnu(ok && !oke);
90 }
91
92 s=brin.readLine(); if(s!=null) a=s.toCharArray();
93 System.out.println(" <-- blocif: "+s+" ok="+ok); //br.readLine();
94 }// blocif(...)
95
96 static void blocatr(boolean ok) throws IOException
97 {
98 System.out.println(" --> blocatr: "+s+" ok="+ok); //br.readLine();
99 if(ok) x[a[0]-’a’]=x[a[2]-’a’];
100
101 // mai sunt si alte atribuiri consecutive ...
102 s=brin.readLine(); if(s!=null) a=s.toCharArray();
103 while((s!=null)&&(a[1]==’=’))
104 {
105 System.out.println(" "+s+" ok="+ok); //br.readLine();
106 if(ok) x[a[0]-’a’]=x[a[2]-’a’];
107 s=brin.readLine(); if(s!=null) a=s.toCharArray();
108 }
109 System.out.println(" <-- blocatr: "+s); //br.readLine();
110 }// blocatr(...)
111
112 static void blocelem(boolean ok) throws IOException
113 {
114 if(estefi()||esteda()||estenu()) return;
115 System.out.println(" --> blocelem: "+s); //br.readLine();
116
117 if(a[1]==’=’) blocatr(ok);
118 if(esteif()) blocif(ok);
119
120 System.out.println(" <-- blocelem: "+s); //br.readLine();
121 }// blocelem(...)
122
123 static void bloc(boolean ok) throws IOException // blocElementar + blocElementar
+...
124 {
125 System.out.println("--> bloc: "+s); //br.readLine();
CAPITOLUL 34. ONI 2006 709

126 while(s!=null&&!estefi()&&!esteda()&&!estenu())
127 {
128 blocelem(ok);
129 }
130 System.out.println("<-- bloc: "+s); //br.readLine();
131 }// bloc(...)
132
133 public static void main(String[] args) throws IOException
134 {
135 int k;
136 brin=new BufferedReader(new FileReader("limbaj.in"));
137 stin=new StreamTokenizer(brin);
138 PrintWriter out=new PrintWriter(
139 new BufferedWriter(new FileWriter("limbaj.out")));
140
141 int i;
142 for(i=0;i<26;i++) { stin.nextToken(); x[i]=(int)stin.nval; }
143 brin.readLine();
144 afisx();
145
146 s=brin.readLine();
147 if(s!=null) a=s.toCharArray();
148
149 bloc(true);
150
151 for(i=0;i<26;i++) out.print(x[i]+" ");
152 out.println();
153 out.close();
154 }// main(...)
155 }// class

Variant  f r  mesaje:

Listing 34.2.3: limbaj2.java


1 import java.io.*; // test 25 g,h= gresit la ei!!!
2 class Limbaj // test 26 ==> liniile 321 si 400 si 479= "fiif" ... ???
3 { // test 28 ==> multe linii cu "fiif" ...
4 static int[] x=new int[26];
5 static char[] a=new char[4];
6 static String s;
7
8 static BufferedReader brin;
9 static StreamTokenizer stin;
10
11 static boolean esteif()
12 {
13 if((a[0]==’i’)&&(a[1]==’f’)) return true; else return false;
14 }// esteif()
15
16 static boolean estefi()
17 {
18 if((a[0]==’f’)&&(a[1]==’i’)) return true; else return false;
19 }// esteif()
20
21 static boolean esteda()
22 {
23 if((a[0]==’d’)&&(a[1]==’a’)) return true; else return false;
24 }// esteda()
25
26 static boolean estenu()
27 {
28 if((a[0]==’n’)&&(a[1]==’u’)) return true; else return false;
29 }// estenu()
30
31 static void blocif(boolean ok) throws IOException
32 {
33 boolean oke=false;
34
35 // urmeaza expresia logica (conditia din IF)
36 s=brin.readLine(); a=s.toCharArray();
37
38 if(a[0]==’=’ && x[a[1]-’a’] == x[a[2]-’a’]) oke=true; else
39 if(a[0]==’<’ && x[a[1]-’a’] < x[a[2]-’a’]) oke=true; else
40 if(a[0]==’>’ && x[a[1]-’a’] > x[a[2]-’a’]) oke=true;
CAPITOLUL 34. ONI 2006 710

41
42 // urmeaza "DA" sau "NU"
43 s=brin.readLine(); a=s.toCharArray();
44
45 if(esteda())
46 {
47 s=brin.readLine(); a=s.toCharArray();
48 bloc(ok && oke);
49 }
50
51 if(estenu())
52 {
53 s=brin.readLine(); a=s.toCharArray();
54 bloc(ok && !oke);
55 }
56
57 s=brin.readLine(); if(s!=null) a=s.toCharArray();
58 }// blocif(...)
59
60 static void blocatr(boolean ok) throws IOException
61 {
62 if(ok) x[a[0]-’a’]=x[a[2]-’a’];
63
64 // mai sunt si alte atribuiri consecutive ...
65 s=brin.readLine(); if(s!=null) a=s.toCharArray();
66 while((s!=null)&&(a[1]==’=’))
67 {
68 if(ok) x[a[0]-’a’]=x[a[2]-’a’];
69 s=brin.readLine(); if(s!=null) a=s.toCharArray();
70 }
71 }// blocatr(...)
72
73 static void blocelem(boolean ok) throws IOException
74 {
75 if(estefi()||esteda()||estenu()) return;
76 if(a[1]==’=’) blocatr(ok);
77 if(esteif()) blocif(ok);
78 }// blocelem(...)
79
80 static void bloc(boolean ok) throws IOException// blocElementar + blocElementar +...
81 {
82 while(s!=null&&!estefi()&&!esteda()&&!estenu()) blocelem(ok);
83 }// bloc(...)
84
85 public static void main(String[] args) throws IOException
86 {
87 int k;
88 brin=new BufferedReader(new FileReader("limbaj.in"));
89 stin=new StreamTokenizer(brin);
90 PrintWriter out=new PrintWriter(
91 new BufferedWriter(new FileWriter("limbaj.out")));
92
93 int i;
94 for(i=0;i<26;i++) { stin.nextToken(); x[i]=(int)stin.nval; }
95 brin.readLine();
96
97 s=brin.readLine();
98 if(s!=null) a=s.toCharArray();
99
100 bloc(true);
101
102 for(i=0;i<26;i++) out.print(x[i]+" ");
103 out.println();
104 out.close();
105 }// main(...)
106 }// class

34.3 Panouri
autor G l µan Constantin, ISJ
Bistrita-Nasaud
Pe autostrada "Soarele Estului" sunt a³ezate de-a lungul ³oselei, la distanµe egale, panouri
publicitare ale unor rme. Aceea³i rm , poate s  aib  mai multe panouri publicitare ³i ecare
CAPITOLUL 34. ONI 2006 711

panou poate s  apar  în mai multe locuri. Panourile se identic  prin numere naturale, num rul
total de panouri ind N .
Firma "X Corporation" are panouri de T tipuri diferite. Firma a primit aprobarea construirii
unui mare complex turistic în apropierea autostr zii; de aceea, pentru alegerea locului, este inte-
resat  ³i de urm torul aspect: care este lungimea minim  de ³osea, în care se pot întâlni, toate
cele T tipuri de panouri publicitare ale rmei, indiferent de ordinea acestora, ³i indiferent dac 
între ele se mai interpun sau nu panouri ale altor rme.
Cerinµ 
Cunoscând N - num rul total de panouri de la marginea autostr zii ³i ordinea amplas rii lor,
ca ³i cele T tipuri de panouri amplasate de rm , determinaµi num rul minim de intervale dintre
dou  panouri între care rma "X Corporation" î³i reg se³te toate panourile sale.
Date de intrare
Fi³ierul de intrare panouri.in are pe prima linie numerele N ³i T .
Pe urm toarele N linii, sunt N numere naturale, nu neaparat diferite, câte unul pe linie,
reprezentând panourile, iar începând cu linia N  2, câte unul pe linie, cele T tipuri de panouri
diferite al rmei.
Date de ie³ire
Fi³ierul de ie³ire panouri.out va conµine pe prima linie un singur num r întreg pozitiv L,
reprezentând num rul cerut, sau 1 în caz c  nu exist  soluµie.
Restricµii ³i preciz ri
ˆ 1&N & 15000
ˆ 1&T & 1000
ˆ Toate numerele reprezentând panouri sunt numere naturale din intervalul 1..1000.

Exemple
panouri.in panouri.out Explicaµie
62 2 Sunt N 6 panouri : 1 2 3 5 3 1.
1 Firma are T 2 tipuri de panouri: 5 ³i 1.
2
3 Cel mai scurt interval care conµine elementele
5 5 ³i 1, este între panourile al 4 - lea
3 ³i al 6 -lea, ³i conµine 2 intervale.
1
5
1
83 4 Sunt N 8 panouri de tipurile: 5 1 3 3 5 4 2 1.
5 Firma are T 3 tipuri de panouri: 3, 1 ³i 4.
1
3 Cel mai scurt interval care conµine elementele
3 1, 3 ³i 4, este între al 2 lea ³i al 6-lea panou,
5 ³i conµine 4 intervale.
4
2
1
3
1
4

Timp maxim de execuµie/test: 1 secund  (Windows) 0.1 secunde (Linux)

34.3.1 Indicaµii de rezolvare

Soluµia comisiei
Reformul m problema: ind dat un ³ir a1..n ³i o multime B cu m elemente, s  se gaseasc 
dou  poziµii start ³i f inal, astfel încât toate elementele mulµimii B s  e conµinute în subsecvenµa
astart, ..., f inal, cu proprietatea c  diferenµa f inal  start are valoare minim .
CAPITOLUL 34. ONI 2006 712

Cu alte cuvinte, s  se gaseasc  subsecvenµa de lungime minim  astart..f inal care conµine
toate elementele mulµimii B .
Având în vedere valorile mici pentru tipurile de panouri (1 & tip & 1000), pentru operaµia
de c utare în O 1 a unui element în multimea B , denim ³irul b, cu bi 1 dac  i aparµine
mulµimii B .
Denim de asemenea ³rul frecvenµelor f r, cu proprietatea c  f ri x daca i aparµine lui B
³i i apare de x ori în subsecvenµa astart, ..f inal.
Fix m start ³i f inal la valoarea 1 ³i increment m poziµia f inal pân  când toate elementele
mulµimii B se a  în intervalul astart, ..f inal. Apoi incrementam start pân  la valoarea maxim 
la care astart, ..f inal mai conµine înc  toate elementele mulµimii B .
În continuare, pentru ecare incrementare a poziµiei f inal, m rim cât se poate de mult poziµia
start, cu respectarea restricµiilor. În acest fel ne asigur m c  pentru ecare poziµie f inal, avem o
subsecvenµ  de lungime minim  care conµine multimea B .
Algoritmul are complexitatea O n.

34.3.2 Cod surs 

Listing 34.3.1: panouri.cpp


1 // Complexitate O(n)
2 #include <fstream>
3 #include <iostream>
4 #include <stdlib.h>
5
6 using namespace std;
7
8 #define IN "panouri.in"
9 #define OUT "panouri.out"
10 #define DIM1 15001
11 #define DIM2 1001
12
13 unsigned int a[DIM1];
14 unsigned int b[DIM2], fr[DIM1];
15 /*
16 b[i] = 1 => panoul de tip i apartine firmei.
17 frecventa, fr[i] = x, daca b[i] = 1
18 (panoul de tip i apartine firmei si daca numarul i apare
19 de x ori in subsecventa a[s..f])
20 */
21 unsigned int n, m, s, f, i, nr, best, x;
22 unsigned int minx, maxx;
23
24 ofstream fout(OUT);
25
26 void ReadData();
27 void Solve();
28
29 int main()
30 {
31 ReadData();
32 Solve();
33 return 0;
34 }
35
36 void ReadData()
37 {
38 ifstream fin(IN);
39 fin >> n >> m;
40 for (i = 1; i <= n; i++ )
41 fin >> a[i];
42 for (i = 1; i <= m; i++ )
43 {
44 fin >> x;
45 b[x] = 1;
46 }
47 fin.close();
48 }
49
50 void Solve()
51 {
CAPITOLUL 34. ONI 2006 713

52 s = 1; f = 1;
53 nr = 0;
54 if ( b[a[s]] == 1 )
55 fr[a[s]] = 1, nr = 1;
56
57 while (f < n && nr < m) // cautam cel mai mic al 2-lea indice f
58 // astfel ca in a[s..f]
59 { // sa fie continute toate numerele din b[]
60 f++;
61 if (b[a[f]] == 1) // face parte din b[]
62 {
63 if ( fr[a[f]] == 0 ) // o singura aparitie in secventa
64 nr++;
65 fr[a[f]]++;
66 }
67 }
68
69 if ( nr < m ) // nu exista solutie
70 {
71 // fout << nr << ’\n’;
72 fout << -1 << ’\n’;
73 fout.close();
74 return;
75 }
76
77 while (b[a[s]] == 0 || (b[a[s]] == 1 && fr[a[s]] > 1))
78 --fr[a[s++]]; // marim s cat se poate astfel ca in secventa a[s..f]
79 // sa apara toate numerele din b
80 best = DIM1;
81 if (best > f - s)
82 best = f - s, minx = s, maxx = f;
83
84 for (f = f + 1; f <= n; f++ )
85 {
86 if (b[a[f]] == 1)
87 fr[a[f]]++;
88
89 while (b[a[s]] == 0 || (b[a[s]] == 1) && (fr[a[s]] > 1))
90 --fr[a[s++]]; // la fiecare incrementare a indicelui f incercam
91 // sa marim pe cat posibil si indicele s
92
93 if (best > f - s)
94 best = f - s, minx = s, maxx = f;
95 }
96
97 fout << best << ’\n’;
98 // fout << min << " " << max << ’\n’;
99 fout.close();
100 }

34.3.3 Rezolvare detaliat 

Variant  care dep ³este timpul pentru ultimele 4 teste:

Listing 34.3.2: panouri1.java


1 import java.io.*; // OK ... dar .. depasire timp executie pentru
2 class Panouri1 // t7=1.07sec t8=2.5sec t9=4.9sec t10=6.6sec
3 {
4 static int n,t,min;
5 static int[] x=new int[15001];
6 static int[] pf=new int[1001];
7 static int[] px=new int[1001];
8 static int[] pu=new int[1001];
9
10 static void calcul()
11 {
12 int i,j,npu,imin,jmin;
13
14 min=15123;
15 for(i=1;i<=n-t+1;i++)
16 {
CAPITOLUL 34. ONI 2006 714

17 imin=i;
18 if(pf[x[i]]==0) continue;
19
20 for(j=1;j<=1000;j++) pu[j]=0;
21
22 npu=1;
23 pu[x[i]]=1;
24 jmin=-1;
25 for(j=i+1;j<=n;j++)
26 {
27 if( (pf[x[j]]==1) && (pu[x[j]]==0) )
28 {
29 npu++;
30 pu[x[j]]=1;
31 if(npu==t) {jmin=j; break;}
32 }
33 }
34 if(npu==t)
35 if((jmin-imin)<min) min=jmin-imin;
36 }
37 }// calcul()
38
39 public static void main(String[] args) throws IOException
40 {
41 int i,tipp;
42 long t1,t2;
43 t1=System.currentTimeMillis();
44 StreamTokenizer st=new StreamTokenizer(
45 new BufferedReader(new FileReader("panouri.in")));
46 PrintWriter out=new PrintWriter(
47 new BufferedWriter(new FileWriter("panouri.out")));
48
49 st.nextToken(); n=(int)st.nval;
50 st.nextToken(); t=(int)st.nval;
51 for(i=1;i<=n;i++) { st.nextToken(); x[i]=(int)st.nval; px[x[i]]=1; }
52 for(i=1;i<=t;i++) { st.nextToken(); tipp=(int)st.nval; pf[tipp]=1; }
53
54 min=0;
55 for(i=1;i<=1000;i++)
56 if((px[i]==0)&&(pf[i]==1)) {min=-1; break;}
57
58 if(min==0)
59 if(t>1) calcul();
60
61 out.println(min);
62 out.close();
63 t2=System.currentTimeMillis();
64 System.out.println("Time = "+(t2-t1));
65 }// main(...)
66 }// class

Variant  care se încadreaz  în timp pentru toate testele:

Listing 34.3.3: panouri2.java


1 import java.io.*; // OK ... fara depasire timp executie pentru
2 class Panouri2 // t7=.13sec t8=0.15sec t9=0.18sec t10=0.19sec
3 {
4 static int n,t,min;
5 static int[] x=new int[15001];
6 static int[] pf=new int[1001]; // panou firma
7 static int[] px=new int[1001];
8 static int[] pu=new int[1001]; // panou utilizat
9
10 static void calcul()
11 {
12 int i,j,npu;
13
14 i=1;
15 while((i<=n)&&(pf[x[i]]==0)) i++; // i --> primul panou de firma
16 pu[x[i]]++; // panou utilizat (nr de ori)
17 npu=1; // nr panouri utilizate
18
19 j=i+1;
20 while((j<=n-t+1)&&(npu<t)) // ... toate panourile firmei ...
CAPITOLUL 34. ONI 2006 715

21 {
22 while(pf[x[j]]==0) j++;
23 if(pu[x[j]]==0) npu++;
24 pu[x[j]]++; // panou utilizat (nr de ori)
25 if(npu<t) j++;
26 }
27 min=j-i; // intre i ... j sunt toate ...
28
29 while(j<=n)
30 {
31 while(npu==t)
32 {
33 while(pf[x[i]]==0) i++; // i -->
34 if(pu[x[i]]==1) break; else {pu[x[i]]--; i++;}
35 }
36 if(j-i<min) min=j-i;
37
38 // il scot pe i ...
39 pu[x[i]]=0;
40 npu=t-1;
41 i++;
42
43 j++; // j --> pana refac nr panouri = t
44 while((j<=n)&&(npu<t))
45 {
46 while(pf[x[j]]==0) j++; // ma plasez pe panou firma
47 if(pu[x[j]]==0) npu++;
48 pu[x[j]]++; // panou utilizat (nr de ori)
49 if(npu<t) j++;
50 }
51 }
52 }// calcul()
53
54 public static void main(String[] args) throws IOException
55 {
56 int i,tipp;
57 long t1,t2;
58 t1=System.currentTimeMillis();
59 StreamTokenizer st=new StreamTokenizer(
60 new BufferedReader(new FileReader("panouri.in")));
61 PrintWriter out=new PrintWriter(
62 new BufferedWriter(new FileWriter("panouri.out")));
63
64 st.nextToken(); n=(int)st.nval;
65 st.nextToken(); t=(int)st.nval;
66 for(i=1;i<=n;i++) { st.nextToken(); x[i]=(int)st.nval; px[x[i]]=1; }
67 for(i=1;i<=t;i++) { st.nextToken(); tipp=(int)st.nval; pf[tipp]=1; }
68
69 min=0;
70 for(i=1;i<=1000;i++)
71 if((px[i]==0)&&(pf[i]==1)) {min=-1; break;}
72
73 if(min==0)
74 if(t>1) calcul();
75
76 out.println(min);
77 out.close();
78 t2=System.currentTimeMillis();
79 System.out.println("Time = "+(t2-t1));
80 }// main(...)
81 }// class

34.4 Pereµi
autor Dom³a Ovidiu,
Universitatea 1 Decembrie 1918 Alba Iulia
Localitatea Târgovi³te este în plin  modernizare. Prim ria decide s  inventarieze toate cl dirile
din ora³ pentru a renova faµadele acestora. În acest sens analizeaz  harta ora³ului ³i constat  c 
toµi pereµii sunt a³ezaµi doar pe direcµia Nord Sud sau Est Vest. Pereµii vizibili de c tre turi³ti
sunt doar aceia la care se poate ajunge din exteriorul ora³ului prin deplasarea pe cele dou  direcµii
date, în oricare din cele 4 sensuri (N , E , S , V ). Harta ora³ului este întocmit  pe un caroiaj format
din p trate cu latur  1.
CAPITOLUL 34. ONI 2006 716

Cerinµ 
Cunoscându-se harta ora³ului, determinaµi lungimea pereµilor vizibili ce urmeaz  a  zugr viµi.
Date de intrare
Fi³ierul de intrare pereti.in are pe prima linie dimensiunile m (num rul de linii), n (num rul
de coloane) ale h rµii. Pe ecare dintre urm toarele m linii exist  n numere naturale de la 0 la
15, separate prin câte un spaµiu, cu semnicaµia:
- reprezentarea binar  a num rului pe 4 cifre semnic , începând de la stânga spre dreapta,
existenµa unui perete spre direcµiile N , E , S , V (1 - exist  perete, 0 - nu exist  perete;
explicaµii în gura de mai jos).
De exemplu valoarea 13 se reprezint  în binar 1101, deci în mod corespunz tor, de la stânga
spre dreapta, vom avea pereµi spre N, E ³i V.

N
0 6 13 1
4 15 5 1
V 0 14 1 E
4 15 0
0 12 5 7

Figura 34.2: Pereµi

Date de ie³ire
Fi³ierul de ie³ire pereti.out va conµine pe prima linie num rul natural k reprezentând lungimea
pereµilor ce vor  zugr viµi.
Restricµii ³i preciz ri
ˆ 1 & m, n & 100
ˆ Pereµii aaµi la marginea h rµii sunt pereµi vizibili.
ˆ Datele de intrare sunt considerate corecte.

Exemplu
pereti.in pereti.out Explicaµie
5 4 22 Pentru poziµiile (5, 2) ³i (5, 3) peretele dintre ele
0 6 13 1 va  zugr vit pe ambele feµe.
4 15 5 1
0 14 7 1 Peretele dinspre Nord al poziµiei (1,3) este perete
4 15 9 0 exterior, chiar dac  se a  pe marginea h rµii.
0 12 5 7
Timp maxim de execuµie/test: 1 secund  (Windows), 0.5 secunde (Linux)

34.4.1 Indicaµii de rezolvare

Soluµia comisiei
Se bordeaz  matricea cu valorile corespunz toare, astfel încât toat  harta se înconjor  cu ziduri
exterioare. Se determin  toate poziµiile (celulele) din matrice accesibile din pozitia 0, 0 care este
exterioar  astfel:
Se construie³te un ³ir de perechi, i, j  care vor marca pe rând poziµiile din matrice pe care
le-am parcurs deja.
Se porne³te din 0, 0, se determin  toate poziµiile din matrice vecine cu acestea, în care se
poate ajunge, ³i se adaug  la ³irul construit. Odat  ad ugate, se trece la urm toarea poziµie din
CAPITOLUL 34. ONI 2006 717

³ir ³i se adaug  din nou toµi vecinii accesibili din aceasta dar care nu gureaz  în ³irul nostru.
Procedura continu  pân  când marc m toµi vecinii celulelor ad ugate în ³ir.
Pentru a marca elementele din matrice deja ad ugate în sir adun m 16 ec rei "celule". În
acela³i timp ad ug m num rul de pereµi vizibili din exterior, respectiv 1, 2 sau 3 în funcµie de
valoarea celulei.
Vericarea vecinilor se realizeaz  f r  a folosi descompunerea binar  a valorilor. Se folose³te
operaµia AND între valorile numerice întregi (de exemplu, 8 and 11 = 8 deci exist  perete spre
N).

34.4.2 Cod surs 

Listing 34.4.1: pereti.pas


1 type mat=array[0..101,0..101] of byte;
2 dir=array[1..4] of integer;
3 elem=record
4 i,j:byte;
5 end;
6 sir=array[1..11000] of elem;
7
8 const d:dir=(8,4,2,1);
9 dx:dir=(-1,0,1,0);
10 dy:dir=(0,1,0,-1);
11
12 var a:mat;
13 x:sir;
14 m,n:byte;
15 f:text;
16 k,l:integer;
17 nr:longint;
18
19 Procedure citire(var a:mat;var m,n:byte);
20 var i,j:byte;
21 f:text;
22 begin
23 assign(f,’pereti.in’);
24 reset(f);
25 readln(f,m,n);
26 for i:=1 to m do
27 begin
28 for j:=1 to n do
29 read(f,a[i,j]);
30 readln(f);
31 end;
32 close(f);
33 end;
34
35 procedure bordare(var a:mat;m,n:byte);
36 var i:byte;
37 begin
38 for i:=0 to n+1 do
39 begin
40 a[0,i]:=8+ (a[1,i] and 8) div 4;
41 a[m+1,i]:=2+ (a[m,i] and 2)* 4 ;
42 end;
43 for i:=0 to m+1 do
44 begin
45 a[i,0]:=a[i,0]+1+(a[i,1] and 1) * 4;
46 a[i,n+1]:=a[i,n+1]+4+(a[i,n] and 4) div 4;
47 end;
48 end;
49
50 Function numara(var a:mat;m,n:byte):longint;
51 var nr:longint;
52 i,j:byte;
53 begin
54 nr:=0;
55 k:=1;
56 l:=1;
57 x[l].i:=0;
58 x[l].j:=0;
CAPITOLUL 34. ONI 2006 718

59 a[0,0]:=a[0,0]+16;
60 while k<=l do
61 begin
62 for i:=1 to 4 do
63 if ((a[x[k].i,x[k].j] and d[i])<>d[i])and (a[x[k].i+dx[i],x[k].j+dy[i]]<16) then
64 begin
65 inc(l);
66 x[l].i:=x[k].i+dx[i];
67 x[l].j:=x[k].j+dy[i];
68 a[x[l].i,x[l].j]:=a[x[l].i,x[l].j]+16;
69 end;
70 case a[x[k].i,x[k].j] -16 of
71 1,2,4,8: nr:=nr+1;
72 3,5,6,9,10,12: nr:=nr+2;
73 7,11,13,14: nr:=nr+3;
74 end;
75 inc(k);
76 end;
77 numara:=nr;
78 end;
79
80 begin
81 citire(a,m,n);
82 bordare(a,m,n);
83 assign(f,’pereti.out’);
84 rewrite(f);
85 writeln(f,numara(a,m,n)-2*m-2*n-8);
86 close(f);
87 end.

Listing 34.4.2: peretiok.pas


1 type mat=array[0..101,0..101] of byte;
2 dir=array[1..4] of integer;
3
4 const d:dir=(8,4,2,1);
5 dx:dir=(-1,0,1,0);
6 dy:dir=(0,1,0,-1);
7
8 var a:mat;
9 m,n:byte;
10 f:text;
11 nr:longint;
12
13 Procedure citire(var a:mat;var m,n:byte);
14 var i,j:byte;
15 f:text;
16 begin
17 assign(f,’pereti.in’);
18 reset(f);
19 readln(f,m,n);
20 for i:=1 to m do
21 begin
22 for j:=1 to n do
23 read(f,a[i,j]);
24 readln(f);
25 end;
26 close(f);
27 end;
28
29 procedure bordare(var a:mat;m,n:byte);
30 var i:byte;
31 begin
32 for i:=0 to n+1 do
33 begin
34 a[0,i]:=8+ (a[1,i] and 8) div 4;
35 a[m+1,i]:=2+ (a[m,i] and 2)* 4 ;
36 end;
37 for i:=0 to m+1 do
38 begin
39 a[i,0]:=a[i,0]+1+(a[i,1] and 1) * 4;
40 a[i,n+1]:=a[i,n+1]+4+(a[i,n] and 4) div 4;
41 end;
42 end;
43
CAPITOLUL 34. ONI 2006 719

44 Procedure fill(x,y:byte);
45 var i:byte;
46 begin
47 a[x,y]:=a[x,y]+16;
48 for i:=1 to 4 do
49 if (a[x,y] and d[i]<>d[i]) and (a[x+dx[i],y+dy[i]]<16) then
50 fill(x+dx[i],y+dy[i]);
51 end;
52
53 Function numara(var a:mat;m,n:byte):longint;
54 var nr:longint;
55 i,j:byte;
56 begin
57 nr:=0;
58 for i:=1 to m do
59 for j:=1 to n do
60 begin
61 if a[i,j]>16 then
62 case a[i,j]-16 of
63 1,2,4,8: nr:=nr+1;
64 3,5,6,9,10,12: nr:=nr+2;
65 7,11,13,14: nr:=nr+3;
66 end;
67 end;
68 for i:=1 to n do
69 begin
70 if a[0,i]<>24 then inc(nr);
71 if a[m+1,i]<>18 then inc(nr);
72 end;
73 for i:=1 to m do
74 begin
75 if a[i,0]<>17 then inc(nr);
76 if a[i,n+1]<>20 then inc (nr);
77 end;
78 numara:=nr;
79 end;
80
81 begin
82 citire(a,m,n);
83 bordare(a,m,n);
84 fill(0,0);
85 assign(f,’pereti.out’);
86 rewrite(f);
87 writeln(f,numara(a,m,n));
88 close(f);
89 end.

34.4.3 Rezolvare detaliat 

Listing 34.4.3: pereti.java


1 import java.io.*;
2 class Pereti
3 {
4 static final int nord=8, est=4, sud=2, vest=1;
5 static int m,n,np,nz;
6 static int[][] a=new int[101][101];
7 static int[][] b=new int[101][101];
8
9 static void fill(int i, int j)
10 {
11 b[i][j]=nz;
12
13 if((a[i][j] & nord)!=0) np++;
14 if((a[i][j] & est) !=0) np++;
15 if((a[i][j] & sud) !=0) np++;
16 if((a[i][j] & vest)!=0) np++;
17
18 if((i-1>=1)&&(b[i-1][j]==0)&&((a[i][j]&nord)==0)) fill(i-1,j);// nord
19 if((i+1<=m)&&(b[i+1][j]==0)&&((a[i][j]&sud)==0)) fill(i+1,j);// sud
20 if((j-1>=1)&&(b[i][j-1]==0)&&((a[i][j]&vest)==0)) fill(i,j-1);// vest
21 if((j+1<=n)&&(b[i][j+1]==0)&&((a[i][j]&est)==0)) fill(i,j+1);// est
CAPITOLUL 34. ONI 2006 720

22 }// fill(...)
23
24 static void intra()
25 {
26 int i,j;
27 for(i=1;i<=m;i++) // de la vest
28 if((b[i][1]==0)&&((a[i][1]&vest)==0)) { nz++; fill(i,1); }
29 for(i=1;i<=m;i++) // de la est
30 if((b[i][n]==0)&&((a[i][n]&est)==0)) { nz++; fill(i,n); }
31 for(j=1;j<=n;j++) // de la nord
32 if((b[1][j]==0)&&((a[1][j]&nord)==0)) { nz++; fill(1,j); }
33 for(j=1;j<=n;j++) // de la sud
34 if((b[m][j]==0)&&((a[m][j]&sud)==0)) { nz++; fill(m,j); }
35 }// intra(...)
36
37 static void exterior()
38 {
39 int i,j;
40 for(i=1;i<=m;i++) if((a[i][1]&vest)!=0) np++; // vest
41 for(i=1;i<=m;i++) if((a[i][n]&est) !=0) np++; // est
42 for(j=1;j<=n;j++) if((a[1][j]&nord)!=0) np++; // nord
43 for(j=1;j<=n;j++) if((a[m][j]&sud) !=0) np++; // sud
44 }// exterior(...)
45
46 public static void main(String[] args) throws IOException
47 {
48 int i,j;
49 StreamTokenizer st=new StreamTokenizer(
50 new BufferedReader(new FileReader("pereti.in")));
51 PrintWriter out=new PrintWriter(
52 new BufferedWriter(new FileWriter("pereti.out")));
53 st.nextToken(); m=(int)st.nval;
54 st.nextToken(); n=(int)st.nval;
55 for(i=1;i<=m;i++)
56 for(j=1;j<=n;j++) { st.nextToken(); a[i][j]=(int)st.nval; }
57 np=0; // nr pereti
58 nz=0; // nr zone (fill!)
59 intra();
60 exterior();
61 out.println(np);
62 out.close();
63 }// main(...)
64 }// class

34.5 “anµ
autor “erban Marinel, Liceul de
Informatica "Gr. C. Moisil" Iasi
Cei n deµinuµi ai unei închisori, numerotaµi de la 1 la n, trebuie s  sape un ³anµ dispus în linie
dreapt  între dou  puncte A ³i B , situate la distanµa de 250 km unul de cel lalt, pe care exist 
251 borne kilometrice numerotate de la 0 la 250. Fiecare deµinut are îns  o pretenµie, "e doar
democraµie, nu?": el dore³te s  sape doar undeva în zona dintre borna x ³i borna y . Pe lâng 
aceste pretenµii închisoarea se confrunt  ³i cu o alt  problem : nu are sucienµi paznici angajaµi.
Cerinµ 
Cunoscându-se num rul n de deµinuµi ³i pretenµiile lor, s  se determine locul (locurile) unde
vor  pu³i deµinuµii s  sape într-o zi de munc , respectându-se pretenµiile lor, astfel încât num rul
de paznici necesari pentru a p zi cei n deµinuµi s  e minim. Intervalul în care poate p zi un
paznic nu poate conµine dou  sau mai multe zone disjuncte dintre cele exprimate de deµinuµi în
preferinµele lor.
Date de intrare
Fi³ierul de intrare sant.in are pe prima linie num rul n de deµinuµi. Pe ecare dintre urm -
toarele n linii exist  câte dou  numere naturale, ai bi , separate printr-un spaµiu (ai & bi ), care
reprezint  pretenµia deµinutului. Mai exact pe linia i  1 (1 & i & n) este descris  pretenµia
deµinutului cu num rul de ordine i.
Date de ie³ire
Fi³ierul de ie³ire sant.out va conµine pe prima linie num rul natural k reprezentând num rul
minim de paznici necesari pentru paza celor n deµinuµi sco³i la lucru. Pe urm toarele 2k linii vor
CAPITOLUL 34. ONI 2006 721

 descrise locurile unde vor  pu³i s  sape deµinuµii, astfel: ecare pereche de linii 2j, 2j  1
va conµine pe linia 2j trei numere naturale p xj yj , separate prin câte un spaµiu reprezentând
num rul de ordine al paznicului ³i bornele kilometrice xj c si yj unde va p zi paznicul p, iar pe
linia 2j  1 vor  scrise numerele de ordine ale deµinuµilor care sap  în aceast  zon , separate prin
câte un spaµiu, ordonate cresc tor.
Restricµii ³i preciz ri
ˆ 1 & n & 10000

ˆ 0 & ai & bi & 250, pentru orice i, 1 & i & n


ˆ 0 & xj & yj & 250, pentru orice j , 1 & j & k
ˆ un deµinut poate s  sape ³i într-un singur punct ("în dreptul bornei kilometrice numerotat 
cu x")

ˆ în cazul în care exist  mai multe soluµii se va a³a una singur 

ˆ numerele de ordine ale paznicilor vor  scrise în ³ier în ordine cresc toare

ˆ numerotarea paznicilor începe de la 1

Exemplu
.in .out Explicaµie
3 2 sunt necesari 2 paznici: paznicul 1 va p zi între
0 20 1 8 13 borna 8 ³i borna 13, iar deµinuµii p ziµi sunt 1 ³i 2;
8 13 1 2 paznicul 2 va p zi între borna 30 ³i borna 60, iar
30 60 2 30 60 deµinutul p zit este 3
3
4 3 sunt necesari 3 paznici: paznicul 1 va p zi între
10 203 1 10 20 borna 10 ³i borna 20, iar deµinutul p zit este 1;
2 53 1 paznicul 2 va p zi la borna 5, iar deµinuµii p ziµi
30 403 2 55 sunt 2 ³i 4; paznicul 3 va p zi între borna 30 ³i
5 7 33 2 4 borna 40, iar deµinutul p zit este 3
3 30 40
3
5 2 sunt necesari 2 paznici: paznicul 1 va p zi la
10 30 1 30 30 borna 30, iar deµinuµii p ziµi sunt 1, 2, 3 ³i 4;
30 32 1 234 paznicul 2 va p zi între borna 27 ³i borna 28,
0 30 2 27 28 iar deµinutul p zit este 5
27 30 5
27 28 !Soluµia nu este unic !

Timp maxim de execuµie/test: 1 secund  (Windows), 0.5 secunde (Linux)

34.5.1 Indicaµii de rezolvare

Soluµia comisiei
Problema cere, de fapt, determinarea num rului minim de intersecµii între segmentele deter-
minate de kilometrul minim ³i maxim între care sap  un deµinut.
Pentru a le determina procedez astfel:

ˆ ordonez intervalele cresc tor dup  kilometrul minim ³i descresc tor dup  kilometrul maxim

ˆ pun primul deµinut (deci cel cu intervalul de s pare cel mai mare) în grija primului paznic

ˆ pentru toate celelalte

 caut un paznic care mai p ze³te deµinuµi ³i care poate p zi ³i acest deµinut (adic 
intersecµia segmentelor s  e nevid )
 dac  g sesc
 ajustez intervalul de s pare ca s  poat  s pa ³i acest deµinut
CAPITOLUL 34. ONI 2006 722

 altfel (dac  nu g sesc)


 mai am nevoie de un paznic ³i îl dau în grija acestuia
Datorit  faptului c  intervalele sunt sortate, cresc tor dup  cap tul inferior, în momentul când
un interval nu se mai intersecteaz  cu cel deja determinat, este sigur c  nici un alt interval ce îl
urmeaz  nu se mai intersecteaz , lucru ce asigur  determinarea num rului minim de paznici.

34.5.2 Cod surs 

Listing 34.5.1: sant.c


1 #include <stdio.h>
2
3 #define MAXD 10001
4 #define MAXP 252
5
6 struct Detinut //detinut
7 {
8 unsigned char NrOrd; //numarul de ordine
9 unsigned char min, max; //pretentie
10 unsigned char NrP; //paznicul care il pazeste
11 } D[MAXD];
12
13 struct MinMax //zona unde pazeste paznicul i
14 {
15 unsigned char min, max;
16 } P[MAXP]; //posibil MAXP Paznici
17
18 int NrPaznici, N;
19
20 FILE* Fisier;
21
22 void Afiseaza(void)
23 {
24 int i, j;
25
26 Fisier=fopen("sant.out", "wt");
27 fprintf(Fisier, "%d\n", NrPaznici);
28
29 for (i=1; i<=NrPaznici; i++)
30 {
31 fprintf(Fisier, "%d %d %d\n", i, P[i].min, P[i].max);
32 for(j=1;j<=N;j++)
33 if(D[j].NrP==i)
34 fprintf(Fisier, "%d ", D[j].NrOrd);
35 fprintf(Fisier, "\n");
36 }
37 fclose(Fisier);
38 }
39
40 void Citeste(void)
41 {
42 int i;
43
44 Fisier=fopen("sant.in", "rt");
45 fscanf(Fisier, "%d", &N);
46 for (i=1; i<=N; i++)
47 {
48 fscanf(Fisier, "%d %d", &D[i].min, &D[i].max);
49 D[i].NrOrd=i;
50 }
51 fclose(Fisier);
52 }
53
54 int Cauta(unsigned char min, unsigned char max)
55 {
56 int i, Unde=-1;
57
58 for (i=1; i<= NrPaznici; i++)
59 {
60 if ((P[i].max>=min) && (P[i].max<=max)) Unde=i;//am gasit Paznicul i
61 if ((P[i].min>=min) && (P[i].min<=max)) Unde=i;//am gasit Paznicul i
CAPITOLUL 34. ONI 2006 723

62 if ((P[i].min<=min) && (P[i].max>=max)) Unde=i;//am gasit Paznicul i


63 }
64 return Unde;
65 }
66
67 unsigned char Maxim(unsigned char x, unsigned char y)
68 {
69 if (x>y) return x;
70 return y;
71 }
72
73 unsigned char Minim(unsigned char x, unsigned char y)
74 {
75 if (x<y) return x;
76 return y;
77 }
78
79 void Intersectie(int j, unsigned char min, unsigned char max)
80 {
81 P[j].min=Maxim(min, P[j].min);//modific kilometrul minim unde pazeste
82 P[j].max=Minim(max, P[j].max);//modific kilometrul maxim unde pazeste
83 }
84
85 void PunePaznici(void)
86 {
87 int i, j;
88 unsigned char min, max;
89
90 P[1].min=D[1].min;
91 P[1].max=D[1].max;
92 NrPaznici=1;
93 D[1].NrP=NrPaznici;
94 for (i=2; i<= N; i++) //pentru toti ceilalti detinuti
95 {
96 min=D[i].min; max=D[i].max;
97 j=Cauta(min, max); //caut un Paznic care mai pazeste detinuti
98 if (j>0) //daca gasesc
99 {
100 Intersectie(j, min, max);//ajustez kilometri din Paznic ca sa
101 D[i].NrP=j; //pazeasca si noul detinut
102 }
103 else //altfel
104 {
105 NrPaznici++; //iau un Paznic nou
106 P[NrPaznici].min=min;
107 P[NrPaznici].max=max;
108 D[i].NrP=NrPaznici; //si ii dau in grija detinutul
109 }
110 }
111 }
112
113 int main()
114 {
115 Citeste();
116 PunePaznici();
117 Afiseaza();
118 return 0;
119 }

Listing 34.5.2: sant1.c


1 #include <stdio.h>
2
3 #define MAXD 10001
4 #define MAXP 252
5
6 struct Detinut //detinut
7 {
8 int NrOrd; //numarul de ordine
9 unsigned char min, max; //pretentie
10 unsigned char NrP; //paznicul care il pazeste
11 } D[MAXD];
12
13 struct MinMax //zona unde pazeste paznicul i
14 {
CAPITOLUL 34. ONI 2006 724

15 unsigned char min, max;


16 } P[MAXP]; //posibil MAXP Paznici
17
18 int NrPaznici, N;
19
20 FILE* Fisier;
21
22 unsigned char Maxim(unsigned char x, unsigned char y)
23 {
24 if (x>y) return x;
25 return y;
26 }
27
28 unsigned char Minim(unsigned char x, unsigned char y)
29 {
30 if (x<y) return x;
31 return y;
32 }
33
34 int Cauta(unsigned char min, unsigned char max)
35 {
36 int i, Unde;
37
38 for (i=1; i<= NrPaznici; i++)
39 {
40 if ((P[i].max>=min) && (P[i].max<=max)) Unde=i;//am gasit Paznicul i
41 if ((P[i].min>=min) && (P[i].min<=max)) Unde=i;//am gasit Paznicul i
42 if ((P[i].min<=min) && (P[i].max>=max)) Unde=i;//am gasit Paznicul i
43 if ((P[i].max<min) || (P[i].min>max)) Unde=-1; //nu am gasit Paznic
44 }
45 return Unde;
46 }
47
48 void Intersectie(int j, unsigned char min, unsigned char max)
49 {
50 P[j].min=Maxim(min, P[j].min);//modific kilometrul minim unde pazeste
51 P[j].max=Minim(max, P[j].max);//modific kilometrul maxim unde pazeste
52 }
53
54 void Ordoneaza(void)
55 { //sortare prin selectie
56 int i, j, pKMmin;
57 unsigned char KMmax, KMmin;
58 struct Detinut Aux;
59
60 for (i=1; i<=N-1; i++)
61 {
62 KMmin=D[i].min;
63 KMmax=D[i].max;
64 pKMmin=i;
65
66 for (j=i+1; j<=N; j++)
67 if (D[j].min<KMmin) //crescator dupa kilometrul minim
68 { KMmax=D[j].max; KMmin=D[j].min; pKMmin=j; }
69 else
70 if (D[j].min==KMmin)//pentru kilometrul minim egal
71 if (D[j].max>KMmax) //descrescator dupa cel maxim
72 { KMmax=D[j].max; KMmin=D[j].min; pKMmin=j; }
73
74 Aux=D[pKMmin];
75 D[pKMmin]=D[i];
76 D[i]=Aux;
77 }
78 }
79
80 void Sorteaza(int p, int u)
81 {
82 int i, j, pozm;
83 int min;
84 struct Detinut Aux;
85
86 for (i=p; i<u; i++)
87 {
88 min=D[i].NrOrd;
89 pozm=i;
90
CAPITOLUL 34. ONI 2006 725

91 for (j=i+1; j<=u; j++)


92 if (D[j].NrOrd<min)
93 {
94 min=D[j].NrOrd;
95 pozm=j;
96 }
97
98 Aux=D[i];
99 D[i]=D[pozm];
100 D[pozm]=Aux;
101 }
102 }
103
104 void Citeste(void)
105 {
106 int i, x, y;
107
108 Fisier=fopen("sant.in", "rt");
109 fscanf(Fisier, "%d", &N);
110
111 for (i=1; i<=N; i++)
112 {
113 fscanf(Fisier, "%d %d", &x, &y);
114 D[i].min=x;
115 D[i].max=y;
116 D[i].NrOrd=i;
117 }
118
119 fclose(Fisier);
120 }
121
122 void PunePaznici(void)
123 {
124 int i, j;
125 char min, max;
126
127 //pun primul detinut (deci cel cu intervalul cel mai mare sau)
128 //la primul Paznic
129 P[1].min=D[1].min;
130 P[1].max=D[1].max;
131 NrPaznici=1;
132 D[1].NrP=NrPaznici;
133
134 for (i=2; i<= N; i++) //pentru toti ceilalti detinuti
135 {
136 min=D[i].min; max=D[i].max;
137 j=Cauta(min, max); //caut un Paznic care mai pazeste detinuti
138 //si poate sa-l pazeasca si pe asta
139 if (j>0) //daca gasesc
140 {
141 Intersectie(j, min, max);//ajustez kilometri din Paznic ca sa
142 D[i].NrP=NrPaznici; //pazeasca si noul detinut
143 }
144 else //altfel
145 {
146 NrPaznici++; //iau un Paznic nou
147 P[NrPaznici].min=min;
148 P[NrPaznici].max=max;
149 D[i].NrP=NrPaznici; //si ii dau in grija detinutul
150 }
151 }
152 }
153
154 void Afiseaza(void)
155 {
156 int i, j, p, u;
157
158 Fisier=fopen("sant.out", "wt");
159 fprintf(Fisier, "%d\n", NrPaznici);
160
161 p=1;
162 u=1;
163 for (i=1; i<=NrPaznici; i++)
164 {
165 fprintf(Fisier, "%d %d %d\n", i, P[i].min, P[i].max);
166
CAPITOLUL 34. ONI 2006 726

167 while (D[u].NrP==i&&u<=N)


168 u++;
169
170 Sorteaza(p, u-1);
171
172 for (j=p; j<u-1; j++)
173 fprintf(Fisier, "%d ", D[j].NrOrd);
174
175 fprintf(Fisier, "%d\n", D[u-1].NrOrd);
176 p=u;
177 }
178
179 fclose(Fisier);
180 }
181
182 int main(void)
183 {
184 Citeste();
185 Ordoneaza(); //ordonez intervalele de kilometri
186 //crescator dupa kilometrul minim si
187 //descrescator dupa kilometrul maxim
188 PunePaznici();
189 Afiseaza();
190 return 0;
191 }

34.5.3 Rezolvare detaliat 


Listing 34.5.3: sant.java
1 import java.io.*;
2 class Sant
3 {
4 static int n;
5 static int[] x1=new int[10001];
6 static int[] x2=new int[10001];
7 static int[] o=new int[10001];
8
9 static void qsort(int li,int ls)
10 {
11 int val,aux,i,j;
12
13 i=li;
14 j=ls;
15 val=x2[(i+j)/2];
16
17 while(i<j)
18 {
19 while(x2[i]<val) i++;
20 while(x2[j]>val) j--;
21 if(i<=j)
22 {
23 aux=x1[i]; x1[i]=x1[j]; x1[j]=aux;
24 aux=x2[i]; x2[i]=x2[j]; x2[j]=aux;
25 aux=o[i]; o[i]=o[j]; o[j]=aux;
26 i++;
27 j--;
28 }
29 }// while
30
31 if(li<j) qsort(li,j);
32 if(i<ls) qsort(i,ls);
33 }// qsort(...)
34
35 public static void main(String[] args) throws IOException
36 {
37 int k,np,xp;
38 StreamTokenizer st=new StreamTokenizer(
39 new BufferedReader(new FileReader("sant.in")));
40 PrintWriter out=new PrintWriter(
41 new BufferedWriter(new FileWriter("sant.out")));
42
43 st.nextToken(); n=(int)st.nval;
44 for(k=1;k<=n;k++)
CAPITOLUL 34. ONI 2006 727

45 {
46 st.nextToken(); x1[k]=(int)st.nval;
47 st.nextToken(); x2[k]=(int)st.nval;
48 o[k]=k;
49 }
50
51 qsort(1,n);
52
53 np=1;
54 xp=x2[1];
55 for(k=2;k<=n;k++) if(x1[k]>xp) { np++; xp=x2[k]; }
56 out.println(np);
57
58 // inca o data pentru ...!
59 np=1;
60 xp=x2[1];
61 out.println(np+" "+xp+" "+xp);
62 out.print(o[1]+" ");
63 for(k=2;k<=n;k++)
64 {
65 if(x1[k]>xp)
66 {
67 out.println();
68 np++;
69 xp=x2[k];
70 out.println(np+" "+xp+" "+xp);
71 out.print(o[k]+" ");
72 }
73 else out.print(o[k]+" ");
74 }// for k
75 out.close();
76 }// main(...)
77 }// class

34.6 Zumzi
autor Popescu Carmen, Colegiul
National "Gh. Lazar" Sibiu
Albinuµa zumzi locuie³te într-un stup format din N celule de form  hexagonal . Cele N celule
numerotate de la 1 la N sunt dispuse sub form  de spiral  (ca în gur ).

15
14 16
13 5 17
4 6
12 1 18
3 7
11 2 19
10 8
9 20
23 21
22

Figura 34.3: Zumzi1

Adic , celula din centrul stupului este numerotat  cu 1. Plecând de la aceast  celul  spre sud
³i apoi în spiral , în sensul acelor de ceasornic, sunt numerotate celelalte celule.
Iniµial zumzi se g se³te în celula din centru (cea numerotat  cu 1), ³i dore³te s  ajung ,
trecând din celul  în celul , la celula cu num rul de ordine X , unde se g se³te prietenul ei. zumzi
se poate deplasa dintr-o celul  în oricare dintre celulele vecine, f r  a p r si îns  stupul.
Dou  celule sunt vecine dac  au o latur  comun .
Unele celule ale stupului sunt ocupate de alte albine ³i de aceea zumzi nu poate s  treac  prin
ele.
Cerinµ 
Problema v  cere s  determinaµi câte variante are zumzi ca dup  exact K pa³i s  ajung  la
prietenul ei.
CAPITOLUL 34. ONI 2006 728

Date de intrare
Fi³ierul de intrare zumzi.in conµine pe prima sa linie valorile naturale N , M , K ³i X separate
printr-un spaµiu, având urm toarea semnicaµie:
ˆ N - num rul total de celule din stup;
ˆ M - num rul de celule din stup ocupate de alte albine
ˆ K - num rul de pa³i pe care îi are la dispoziµie zumzi
ˆ X - num rul de ordine a celulei în care se g se³te prietenul lui zumzi.
Urm toarea linie a ³ierului de intrare conµine M numere naturale separate printr-un spaµiu
reprezentând numerele de ordine ale celulelor ocupate din stup.
Date de ie³ire
Fi³ierul text zumzi.out va conµine pe prima sa linie un singur num r natural reprezentând
num rul de variante pe care le are zumzi la dispoziµie de a ajunge la prietenul ei.
Restricµii ³i preciz ri
ˆ 1&M $ N & 300
ˆ X j1
ˆ K & 100
ˆ zumzi nu are posibilitatea de a p r si stupul, iar în plus odat  ajuns  la prietenul ei nu îl
va mai p r si.
ˆ zumzi nu este o albin  foarte inteligent  de aceea ea poate trece de mai multe ori printr-o
celul , cu excepµia celulei nale, în care se a  prietenul ei, celul  în care va intra o singur 
dat  ³i nu o mai p r se³te.

Exemplu
zumzi.in zumzi.out Explicaµie
12 4 3 9 4 Variantele sunt:
11 4 6 8 1-2-10-9
1-3-2-9
1-3-10-9
1-7-2-9
12 4 4 2 9 Variantele avute la dispoziµie sunt:
11 4 6 8 1-3-10-9-2
1-7-1-3-2
1-5-1-7-2
etc.

Figura 34.4: Zumzi2

Timp maxim de execuµie/test: 1 secund 

34.6.1 Indicaµii de rezolvare

Soluµia comisiei
Pentru memorarea "stupului" vom folosi o matrice p tratic  T de ordinul 2 ˜ k  1, unde
valoarea lui k este data de relaµia:

3 ˜ k  1 ˜ k  1 $ n & 3 ˜ k ˜ k  1  1
CAPITOLUL 34. ONI 2006 729

adic  k reprezint  num rul de cercuri concentrice din stup (f r  a num ra celula 1 ca un cerc).

T k  1, k  1 1 adic  reprezint  celula din centrul stupului.

Celulele vecine ale celulei de la coordonatele i, j ) din matricea T vor , în ordine:

i  1, j , i  1, j  1, i, j  1, i  1, j , i  1, j  1, i, j  1.

De exemplu pentru N 12 matricea T va ar ta astfel:


0 0 0 0 0
0 0 5 6 0
0 4 1 7 0
12 3 2 8 0
11 10 9 0 0
iar pentru N 35:
0 0 0 31 32 33 34
0 0 30 15 16 17 35
0 29 14 5 6 18 0
28 13 4 1 7 19 0
27 12 3 2 8 20 0
26 11 10 9 21 0 0
25 24 23 22 0 0 0
Vom mai folosi un vector A cu M componente, în care Ai este 0 dac  celula cu num rul de
ordine i este liber , ³i respectiv 1 dac  celula este ocupat .
Se vor mai folosi doi vectori p1 ³i p2 cu semnicaµia c  la pasul j , p1i reprezint  num rul de
drumuri de lungime j  1 existente pân  la celula i, iar p2i reprezint  num rul de drumuri de
lungime j existente pân  la celula i. Se observ  c  p2i va  suma valorilor p1k  pentru toate
celulele k vecine cu i.
Întrucât valorile solicitate pot  foarte mari se va lucra pe numere mari.

34.6.2 Cod surs 

Listing 34.6.1: zumzi.pas


1 program probl;
2 const di:array[1..6] of -1..1=(1,1,0,-1,-1,0);
3 dj:array[1..6] of -1..1=(0,-1,-1,0,1,1);
4
5 CONST MAXCELULE = 304;
6 MAX = 21;
7 fin=’zumzi.in’;
8 fout=’zumzi.out’;
9
10 type fractie=record x,y:integer;
11 end;
12 nrmare= string[100];
13
14 var t:array[0..MAX,0..MAX] of integer;
15 a:array[0..MAXCELULE] of integer;
16 p1,p2:array[0..MAXCELULE] of nrmare;
17 f:text;
18 s1,s,t1,nrp,i,j,m,n,i1,j1,y,k:integer;
19 poz : array[0..MAXCELULE] of fractie;
20
21 function zero(x:nrmare):boolean;
22 begin
23 if (x=’0’) or (length(x)=0) then zero:=true
24 else
25 zero:=false;
26 end;
27
28 procedure adun(x,y:nrmare;var z:nrmare);
29 var c,t,i,zero:integer;
30 begin
31 zero:=ord(’0’);
CAPITOLUL 34. ONI 2006 730

32 while length(x)>length(y) do
33 y:=y+’0’;
34 while length(y)>length(x) do
35 x:=x+’0’;
36 z:=’’;
37 for i:=1 to length(x) do
38 z:=z+’0’;
39 t:=0;
40 for i:=1 to length(x) do
41 begin
42 c:=ord(x[i])+ord(y[i])-2*zero+t;
43 t:=c div 10;
44 c:=c mod 10;
45 z[i]:=chr(c+zero);
46 end;
47 if t>0 then
48 z:=z+chr(t+zero);
49 end;
50
51 procedure compl;
52 var c,i,j,s:integer;
53 begin
54 i:=k+1; j:=k+1; s:=1;
55 t[i,j]:=s; poz[s].x:=i; poz[s].y:=j;
56 s:=s+1;
57 for c:=1 to k do
58 begin
59 i:=i+1;
60 while i<=k+c+1 do
61 begin
62 t[i,j]:=s;
63 poz[s].x:=i; poz[s].y:=j;
64 s:=s+1;
65 if s>n then exit;
66 i:=i+1; j:=j-1;
67 end;
68 i:=i-1;
69 while j>=k-c+1 do
70 begin
71 t[i,j]:=s;
72 poz[s].x:=i; poz[s].y:=j;
73 s:=s+1;
74 if s>n then exit;
75 j:=j-1;
76 end;
77 j:=j+1; i:=i-1;
78 while i>=k+1 do
79 begin
80 t[i,j]:=s;
81 poz[s].x:=i; poz[s].y:=j;
82 s:=s+1;
83 if s>n then exit;
84 i:=i-1;
85 end;
86 j:=j+1;
87 while i>=k-c+1 do
88 begin
89 t[i,j]:=s;
90 poz[s].x:=i; poz[s].y:=j;
91 s:=s+1;
92 if s>n then exit;
93 i:=i-1; j:=j+1;
94 end;
95 i:=i+1;
96 while j<=k+c+1 do
97 begin
98 t[i,j]:=s;
99 poz[s].x:=i; poz[s].y:=j;
100 s:=s+1;
101 if s>n then exit;
102 j:=j+1;
103 end;
104 j:=j-1; i:=i+1;
105 while i<=k+1 do
106 begin
107 t[i,j]:=s;
CAPITOLUL 34. ONI 2006 731

108 poz[s].x:=i; poz[s].y:=j;


109 s:=s+1;
110 if s>n then exit;
111 i:=i+1;
112 end;
113 i:=i-1;
114 end;
115 end;
116
117 procedure scrie(x:nrmare);
118 var i:integer;
119 begin
120 if length(x)=0 then
121 writeln(f,0)
122 else
123 begin
124 for i:=length(x) downto 1 do
125 write(f,x[i]);
126 writeln(f);
127 end;
128 end;
129
130 begin
131 assign(f,fin); reset(f);
132 readln(f,n,m,nrp,y); {n = numarul de celule, m = numarul de celule ocupate
133 nrp = numarul de pasi efectuati de ZUMZI
134 y = pozitia prietenului lui ZUMZI}
135 k:=1; s:=1; s1:=s+k*6;
136 while s1<n do
137 begin
138 s:=s1; k:=k+1;
139 s1:=s+k*6;
140 end;
141 a[0]:=1;
142 {a[i]=1 casuta i este ocupata
143 0 casuta i este libera}
144 for i:=1 to m do begin read(f,j); a[j]:=1; end;
145 close(f);
146 fillchar(p1,sizeof(p1),0);
147 fillchar(p2,sizeof(p2),0);
148
149 compl;
150
151 p1[1]:=’1’;
152 for i:=1 to nrp do
153 begin
154 fillchar(p2,sizeof(p2),0);
155 for j:=1 to n do
156 if not zero(p1[j]) then
157 begin
158 i1:=poz[j].x; j1:=poz[j].y;
159 for m:=1 to 6 do
160 begin
161 t1:=t[i1+di[m],j1+dj[m]];
162 if a[t1]=0 then {poate sari in celula t1}
163 adun(p1[j],p2[t1],p2[t1])
164 end;
165 end;
166 p1:=p2;
167 fillchar(p1[y],sizeof(p1[y]),0);
168 end;
169 assign(f,fout); rewrite(f);
170 scrie(p2[y]);
171 close(f);
172 end.

34.6.3 Rezolvare detaliat 


CAPITOLUL 34. ONI 2006 732

0,1 0,3 0,5 0,7

0,0 0,2 0,4 0,6 0,8

1,1 1,3 1,5 1,7

1,0 1,2 1,4 1,6 1,8

2,1 2,3 2,5 2,7

2,0 2,2 2,4 2,6 2,8

3,1 3,3 3,5 3,7

3,0 3,2 3,4 3,6 3,8

4,1 4,3 4,5 4,7

4,0 4,2 4,4 4,6 4,8

Figura 34.5: Zumzi3

i-1, j
N

i-(j%2) , j-1 i-(j%2) , j+1


NV NE

i,j

SV SE
i+1-(j%2) , j-1 i+1-(j%2) , j+1
S
i+1, j

Figura 34.6: Zumzi4

Variant  f r  numere mari:

Listing 34.6.2: zumzi1.java


1 import java.io.*;
2 class Zumzi1
3 {
4 static int n,m,np,x; // np=nr pasi
5 static int nv=0,nc=0;
6
7 static int[] a=new int[301];
8 static int[][] s=new int[23][23]; // stup
9 static int[] is=new int[301]; // pozitie in stup: linia
10 static int[] js=new int[301]; // pozitie in stup: coloana
11
12 static int[][] nd=new int[301][2]; // nr drumuri
13
14 static int i0=11, j0=11; // centrul stupului
15
16 static int in(int i,int j) { return i-1; } // i nord
17 static int jn(int i,int j) { return j; } // j nord
18 static int is(int i,int j) { return i+1; } // i sud
19 static int js(int i,int j) { return j; } // j sud
20
21 static int ine(int i,int j) { return i-j%2; } // i nord_est
22 static int jne(int i,int j) { return j+1; } // j nord_est
23 static int ise(int i,int j) { return i+1-j%2; } // i sud_est
24 static int jse(int i,int j) { return j+1; } // j sud_est
25
26 static int inv(int i,int j) { return i-j%2; } // i nord_vest
27 static int jnv(int i,int j) { return j-1; } // j nord_vest
28 static int isv(int i,int j) { return i+1-j%2; } // i sud_vest
29 static int jsv(int i,int j) { return j-1; } // j sud_vest
30
CAPITOLUL 34. ONI 2006 733

31 static void stupul()


32 {
33 int i,j,ic,jc,np,k; // k=nr celule
34 k=1;
35 s[i0][j0]=1; is[1]=i0; js[1]=j0; // zumzi (linie, coloana)
36 i=i0;
37 j=j0;
38 while(k<n)
39 {
40 nc++;
41 // la sud 1 pas
42 ic=i; jc=j;
43 i=is(ic,jc); j=js(ic,jc);
44 s[i][j]=++k; is[k]=i; js[k]=j;
45 if(k==n) break;
46
47 // sud_vest nc-1 pasi
48 for(np=1;np<=nc-1&&k<n;np++)
49 {
50 ic=i; jc=j;
51 i=isv(ic,jc); j=jsv(ic,jc);
52 s[i][j]=++k; is[k]=i; js[k]=j;
53 }
54 if(k==n) break;
55
56 // nord_vest nc pasi
57 for(np=1;np<=nc&&k<n;np++)
58 {
59 ic=i; jc=j;
60 i=inv(ic,jc); j=jnv(ic,jc);
61 s[i][j]=++k; is[k]=i; js[k]=j;
62 }
63 if(k==n) break;
64
65 // nord nc pasi
66 for(np=1;np<=nc&&k<n;np++)
67 {
68 ic=i; jc=j;
69 i=in(ic,jc); j=jn(ic,jc);
70 s[i][j]=++k; is[k]=i; js[k]=j;
71 }
72 if(k==n) break;
73
74 // nord_est nc pasi
75 for(np=1;np<=nc&&k<n;np++)
76 {
77 ic=i; jc=j;
78 i=ine(ic,jc); j=jne(ic,jc);
79 s[i][j]=++k; is[k]=i; js[k]=j;
80 }
81 if(k==n) break;
82
83 // sud_est nc pasi
84 for(np=1;np<=nc&&k<n;np++)
85 {
86 ic=i; jc=j;
87 i=ise(ic,jc); j=jse(ic,jc);
88 s[i][j]=++k; is[k]=i; js[k]=j;
89 }
90 if(k==n) break;
91
92 // sud nc pasi
93 for(np=1;np<=nc&&k<n;np++)
94 {
95 ic=i; jc=j;
96 i=is(ic,jc); j=js(ic,jc);
97 s[i][j]=++k; is[k]=i; js[k]=j;
98 }
99 }// while
100 }// stupul()
101
102 static void calcul()
103 {
104 int i,k,v; // v=vecin
105 nd[1][0]=1; // zumzi
106 for(k=1;k<=np;k++) // pasul k
CAPITOLUL 34. ONI 2006 734

107 {
108 for(i=1;i<=n;i++) // celula i
109 {
110 nd[i][k%2]=0; // este suma vecinilor
111
112 if(a[i]==1) continue; // este ocupata
113 if((i==x)&&(k<np)) continue; // nu mai pleaca !
114
115 v=s[in(is[i],js[i])][jn(is[i],js[i])]; // vecin la nord
116 if(a[v]==0) nd[i][k%2]+=nd[v][(k+1)%2]; // celula libera
117
118 v=s[is(is[i],js[i])][js(is[i],js[i])]; // vecin la sud
119 if(a[v]==0) nd[i][k%2]+=nd[v][(k+1)%2]; // celula libera
120
121 v=s[ine(is[i],js[i])][jne(is[i],js[i])]; // vecin la nord_est
122 if(a[v]==0) nd[i][k%2]+=nd[v][(k+1)%2]; // celula libera
123
124 v=s[ise(is[i],js[i])][jse(is[i],js[i])]; // vecin la sud_est
125 if(a[v]==0) nd[i][k%2]+=nd[v][(k+1)%2]; // celula libera
126
127 v=s[inv(is[i],js[i])][jnv(is[i],js[i])]; // vecin la nord_vest
128 if(a[v]==0) nd[i][k%2]+=nd[v][(k+1)%2]; // celula libera
129
130 v=s[isv(is[i],js[i])][jsv(is[i],js[i])]; // vecin la sud_vest
131 if(a[v]==0) nd[i][k%2]+=nd[v][(k+1)%2]; // celula libera
132 }// for i
133 }// for k
134 }// calcul()
135
136 public static void main(String[] args) throws IOException
137 {
138 int i,j;
139 StreamTokenizer st=new StreamTokenizer(
140 new BufferedReader(new FileReader("zumzi.in")));
141 PrintWriter out=new PrintWriter(
142 new BufferedWriter(new FileWriter("zumzi.out")));
143 st.nextToken(); n=(int)st.nval;
144 st.nextToken(); m=(int)st.nval;
145 st.nextToken(); np=(int)st.nval;
146 st.nextToken(); x=(int)st.nval;
147
148 for(i=1;i<=m;i++) { st.nextToken(); j=(int)st.nval; a[j]=1; }
149
150 stupul(); // numerotare celule
151 calcul();
152
153 out.println(nd[x][np%2]);
154 out.close();
155 }// main(...)
156 }// class

Variant  cu numere mari:

Listing 34.6.3: zumzi2.java


1 import java.io.*; // test2 = ? aici = 18697967389042507036635337140
2 class Zumzi2 // in 2-zumzi.ok = 18709929980883486610943359969
3 {
4 static int n,m,np,xz; // np=nr pasi
5 static int nv=0,nc=0;
6
7 static int[] a=new int[301];
8 static int[][] s=new int[23][23]; // stup
9 static int[] is=new int[301]; // pozitie in stup: linia
10 static int[] js=new int[301]; // pozitie in stup: coloana
11
12 static int[][][] nd=new int[301][2][1]; // nr drumuri
13
14 static int i0=11, j0=11; // centrul stupului
15
16 static int in(int i,int j) { return i-1; } // i nord
17 static int jn(int i,int j) { return j; } // j nord
18 static int is(int i,int j) { return i+1; } // i sud
19 static int js(int i,int j) { return j; } // j sud
20
CAPITOLUL 34. ONI 2006 735

21 static int ine(int i,int j) { return i-j%2; } // i nord_est


22 static int jne(int i,int j) { return j+1; } // j nord_est
23 static int ise(int i,int j) { return i+1-j%2; } // i sud_est
24 static int jse(int i,int j) { return j+1; } // j sud_est
25
26 static int inv(int i,int j) { return i-j%2; } // i nord_vest
27 static int jnv(int i,int j) { return j-1; } // j nord_vest
28 static int isv(int i,int j) { return i+1-j%2; } // i sud_vest
29 static int jsv(int i,int j) { return j-1; } // j sud_vest
30
31 static void stupul()
32 {
33 int i,j,ic,jc,np,k; // k=nr celule
34 k=1;
35 s[i0][j0]=1; is[1]=i0; js[1]=j0; // zumzi (linie, coloana)
36 i=i0;
37 j=j0;
38 while(k<n)
39 {
40 nc++;
41 // la sud 1 pas
42 ic=i; jc=j;
43 i=is(ic,jc); j=js(ic,jc);
44 s[i][j]=++k; is[k]=i; js[k]=j;
45 if(k==n) break;
46
47 // sud_vest nc-1 pasi
48 for(np=1;np<=nc-1&&k<n;np++)
49 {
50 ic=i; jc=j;
51 i=isv(ic,jc); j=jsv(ic,jc);
52 s[i][j]=++k; is[k]=i; js[k]=j;
53 }
54 if(k==n) break;
55
56 // nord_vest nc pasi
57 for(np=1;np<=nc&&k<n;np++)
58 {
59 ic=i; jc=j;
60 i=inv(ic,jc); j=jnv(ic,jc);
61 s[i][j]=++k; is[k]=i; js[k]=j;
62 }
63 if(k==n) break;
64
65 // nord nc pasi
66 for(np=1;np<=nc&&k<n;np++)
67 {
68 ic=i; jc=j;
69 i=in(ic,jc); j=jn(ic,jc);
70 s[i][j]=++k; is[k]=i; js[k]=j;
71 }
72 if(k==n) break;
73
74 // nord_est nc pasi
75 for(np=1;np<=nc&&k<n;np++)
76 {
77 ic=i; jc=j;
78 i=ine(ic,jc); j=jne(ic,jc);
79 s[i][j]=++k; is[k]=i; js[k]=j;
80 }
81 if(k==n) break;
82
83 // sud_est nc pasi
84 for(np=1;np<=nc&&k<n;np++)
85 {
86 ic=i; jc=j;
87 i=ise(ic,jc); j=jse(ic,jc);
88 s[i][j]=++k; is[k]=i; js[k]=j;
89 }
90 if(k==n) break;
91
92 // sud nc pasi
93 for(np=1;np<=nc&&k<n;np++)
94 {
95 ic=i; jc=j;
96 i=is(ic,jc); j=js(ic,jc);
CAPITOLUL 34. ONI 2006 736

97 s[i][j]=++k; is[k]=i; js[k]=j;


98 }
99 }// while
100 }// stupul()
101
102 static int[] nrv(int nr)
103 {
104 int[] x;
105 if(nr==0) { x=new int[1]; x[0]=0; }
106 else
107 {
108 int nc, nrrez=nr;
109 nc=0;
110 while(nr!=0) {nc++; nr=nr/10;}
111
112 x=new int[nc];
113 nr=nrrez;
114 nc=0;
115 while(nr!=0) { x[nc]=nr%10; nc++; nr=nr/10;}
116 }
117 return x;
118 }// nrv(...)
119
120 static int[] suma(int[] x, int[] y)
121 {
122 int[] z=new int[(x.length>y.length) ? (x.length+1) : (y.length+1)];
123 int k,t=0;
124 for(k=0;k<=z.length-2;k++)
125 {
126 z[k]=t;
127 if(k<x.length) z[k]+=x[k];
128 if(k<y.length) z[k]+=y[k];
129 t=z[k]/10;
130 z[k]=z[k]%10;
131 }
132 z[z.length-1]=t;
133 if(z[z.length-1]!=0) return z;
134 else
135 {
136 int[] zz=new int[z.length-1];
137 for(k=0;k<zz.length;k++) zz[k]=z[k];
138 return zz;
139 }
140 }// suma(...)
141
142 static void calcul()
143 {
144 int i,k,v; // v=vecin
145 nd[1][0]=nrv(1); // zumzi
146 for(k=1;k<=np;k++) // pasul k
147 {
148 for(i=1;i<=n;i++) // celula i
149 {
150 nd[i][k%2]=nrv(0); // este suma vecinilor
151
152 if(a[i]==1) continue; // este ocupata
153 if((i==xz)&&(k<np)) continue; // nu mai pleaca !
154
155 v=s[in(is[i],js[i])][jn(is[i],js[i])]; // vecin la nord
156 if(a[v]==0) nd[i][k%2]=suma(nd[i][k%2],nd[v][(k+1)%2]);
157
158 v=s[is(is[i],js[i])][js(is[i],js[i])]; // vecin la sud
159 if(a[v]==0) nd[i][k%2]=suma(nd[i][k%2],nd[v][(k+1)%2]);
160
161 v=s[ine(is[i],js[i])][jne(is[i],js[i])]; // vecin la nord_est
162 if(a[v]==0) nd[i][k%2]=suma(nd[i][k%2],nd[v][(k+1)%2]);
163
164 v=s[ise(is[i],js[i])][jse(is[i],js[i])]; // vecin la sud_est
165 if(a[v]==0) nd[i][k%2]=suma(nd[i][k%2],nd[v][(k+1)%2]);
166
167 v=s[inv(is[i],js[i])][jnv(is[i],js[i])]; // vecin la nord_vest
168 if(a[v]==0) nd[i][k%2]=suma(nd[i][k%2],nd[v][(k+1)%2]);
169
170 v=s[isv(is[i],js[i])][jsv(is[i],js[i])]; // vecin la sud_vest
171 if(a[v]==0) nd[i][k%2]=suma(nd[i][k%2],nd[v][(k+1)%2]);
172 }// for i
CAPITOLUL 34. ONI 2006 737

173 }// for k


174 }// calcul()
175
176 public static void main(String[] args) throws IOException
177 {
178 int i,j;
179 StreamTokenizer st=new StreamTokenizer(
180 new BufferedReader(new FileReader("zumzi.in")));
181 PrintWriter out=new PrintWriter(
182 new BufferedWriter(new FileWriter("zumzi.out")));
183 st.nextToken(); n=(int)st.nval;
184 st.nextToken(); m=(int)st.nval;
185 st.nextToken(); np=(int)st.nval;
186 st.nextToken(); xz=(int)st.nval;
187
188 for(i=1;i<=m;i++) { st.nextToken(); j=(int)st.nval; a[j]=1; }
189
190 stupul(); // numerotare celule
191 calcul();
192
193 for(i=nd[xz][np%2].length-1;i>=0;i--) out.print(nd[xz][np%2][i]);
194 out.println();
195 out.close();
196 }// main(...)
197 }// class
Capitolul 35

ONI 2005

Figura 35.1: Sigla ONI 2005

35.1 Bifo
Autor: Silviu G nceanu
Pentru a-³i vindeca rana provocat  de Spânul cel Negru, prinµul Algorel are nevoie de
leacul miraculos aat în posesia vr jitoarei din p durea întunecat .
Aceasta i-a promis leacul dac  îi rezolv  urm toarea problem , la care ea s-a gândit zadarnic o
mie de ani: pornind de la dou  cuvinte iniµiale A1 ³i A2 ³i aplicând "formula bifo" An An2 An1
pentru n ' 3, se obµin cuvintele A3 , A4 , A5 , ³.a.m.d.
Prin An2 An1 înµelegem concatenarea cuvintelor An2 ³i An1 în aceast  ordine.
Toate aceste cuvinte (A1 , A2 , A3 , ³.a.m.d), sunt la rândul lor concatenate, în ordine, formând
un ³ir de caractere innit denumit ³ir magic.
Formula leacului miraculos are M caractere, pe care vr jitoarea nu le ³tie. Se ³tiu îns  cele M
poziµii din ³irul magic în care apar, în ordine, caracterele din formul .
Cerinµ 
Cu toat  inteligenµa lui, Algorel nu poate rezolva aceast  problem . Ajutaµi-l pe prinµ s  ias 
din încurc tur  aând formula leacului magic.
Date de intrare
Primele dou  linii ale ³ierului bifo.in conµin ecare câte un ³ir de cel mult 100 de caractere
reprezentând cuvintele A1 (pe prima linie) ³i respectiv A2 (pe a doua linie).
A treia linie conµine un num r întreg M , reprezentând num rul de caractere din formula
leacului miraculos.
Urmeaz  M linii descriind, în ordine, poziµiile din ³irul magic unde se g sesc caracterele din
formul .
Date de ie³ire

738
CAPITOLUL 35. ONI 2005 739

Fi³ierul de ie³ire bifo.out va conµine pe prima linie un ³ir de M caractere reprezentând formula
leacului miraculos.
Restricµii ³i preciz ri
a 1 & M & 100;
a A1 ³i A2 conµin doar litere mici ale alfabetului englez;
a Numerotarea poziµiilor din ³irul innit începe cu 1;
a Cele M poziµii vor  numere întregi (nu neap rat distincte) de maxim 100
de cifre;
a Pentru 60% din teste poziµiile vor  numere întregi între 1 ³i 1.000.000.000;
a Fiecare linie din ³ierul de intrare ³i din ³ierul de ie³ire se termin  cu
marcaj de sfâr³it de linie;
Exemplu
bifo.in bifo.out
ab xdb
cdx
3
10
4
15
Explicaµie: Primele 5 ³iruri obµinute folosind "formula bifo" sunt:
ab, cdx, abcdx, cdxabcdx, abcdxcdxabcdx
Concatenând aceste ³iruri se obµine ³irul magic:
abcdxabcdxcdxabcdxabcdxcdxabcdx...
Timp maxim de execuµie/test: 1 sec sub Windows ³i 1 sec sub Linux

35.1.1 Indicaµii de rezolvare

Soluµia ocial , Silviu G nceanu


Utilizând "formula bifo" se obµine un ³ir de cuvinte A1, A2, A3, ³.a.m.d, care este practic un
³ir Fibonacci de cuvinte. Fie Li lungimea cuvântului Ai din acest ³ir. Pentru a aa ce caracter se
a  pe poziµia X din ³irul magic se procedeaz  în modul urm tor :
pas1  se g se³te cel mai mic K astfel încât L1  L2  ...  LK ' X (termenul
din ³irul de cuvinte unde se g se³te poziµia X )
pas2  se scade din valoarea X suma L1  L2  ...  LK 1 pentru a aa poziµia
din termenul K unde este caracterul din poziµia X
pas3 
1. dac  K $ 3 se a³eaz  caracterul de pe poziµia X din cuvântul corespunz tor
2. altfel, ³tiind lungimile cuvintelor AK 2 ³i AK 1 ³i ³tiind c  AK se obµine
prin concatenarea acestora, se decide în care din aceste cuvinte se va aa
caracterul c utat în modul urm tor:
a dac  LK 2 $ X atunci caracterul va  în cuvântul AK 1 ³i vom sc dea
K cu o unitate, iar X cu LK 2 ³i revenim la pas3
a altfel caracterul c utat se a  în cuvântul AK 2 ³i vom sc dea din K
dou  unit µi iar X va r mâne neschimbat ³i se revine la pas3
Preciz m c  termenii ³irului (Fibonacci) de cuvinte nu se obµin explicit ci doar se lucreaz  cu
lungimile acestora. Soluµia presupune utilizarea numerelor mari din moment ce poziµiile din ³irul
magic pot dep ³i orice tip de date predenit în compilatoarele utilizate.
Datele de test au fost create astfel încât:
a Implementarea unei soluµii imediate, care calculeaz  termenii ³irului
(Fibonacci) necesari a rii caracterelor de pe poziµiile din ³ierul de
intrare, s  e punctat  cu aproximativ 30 pct
a Implementarea soluµiei corecte f r  folosirea numerelor mari s  obµin 
aproximativ 60 pct
a Implementarea soluµiei corecte folosind numere mari (implementate de
concurent) s  obµin  100 pct
CAPITOLUL 35. ONI 2005 740

35.1.2 *Cod surs 

35.1.3 Rezolvare detaliat 


Testarea prelu rii datelor de intrare.

Listing 35.1.1: bifo0.java


1 import java.io.*;
2 class Bifo1
3 {
4 static int m;
5 static char[] a1;
6 static char[] a2;
7 static char[] f;// formula
8 static int[] p; // pozitii
9
10 static PrintWriter out;
11 static BufferedReader br;
12 static StreamTokenizer st;
13
14 public static void main(String[] args) throws IOException
15 {
16 citire();
17 rezolvare();
18 afisare();
19 }
20
21 static void citire() throws IOException
22 {
23 int i,j;
24 br=new BufferedReader(new FileReader("bifo.in"));
25 st=new StreamTokenizer(br);
26 st.nextToken(); a1=st.sval.toCharArray();
27 st.nextToken(); a2=st.sval.toCharArray();
28 st.nextToken(); m=(int)st.nval;
29 f=new char[m+1];
30 p=new int[m+1];
31 for(i=1;i<=m;i++)
32 {
33 st.nextToken();
34 p[i]=(int)st.nval;
35 }
36
37 afisv(a1);
38 afisv(a2);
39 System.out.println(m);
40 afisv(p);
41 }
42
43 static void afisv(char[] a)
44 {
45 int i;
46 for(i=0;i<a.length;i++) System.out.print(a[i]);
47 System.out.println(" "+a.length);
48 }
49
50 static void afisv(int[] a)
51 {
52 int i;
53 for(i=1;i<=m;i++) System.out.println(a[i]);
54 System.out.println();
55 }
56
57 static void rezolvare()
58 {
59
60 }
61
62 static void afisare() throws IOException
63 {
64 int i;
65 out=new PrintWriter(new BufferedWriter(new FileWriter("bifo.out")));
CAPITOLUL 35. ONI 2005 741

66 for(i=1;i<=m;i++) out.print(f[i]);
67 out.println();
68 out.close();
69 }
70 }

Variant  f r  numere mari.

Listing 35.1.2: bifo1.java


1 import java.io.*;
2 class Bifo2
3 {
4 static int m;
5 static char[] a1;
6 static char[] a2;
7 static char[] f;// formula
8 static int[] p; // pozitii
9 static int[] lg;// lungimile cuvintelor a[i]=a[i-2]a[i-1]
10
11 static PrintWriter out;
12 static BufferedReader br;
13 static StreamTokenizer st;
14
15 public static void main(String[] args) throws IOException
16 {
17 int i;
18 citire();
19 for(i=1;i<=m;i++) rezolvare(i);
20 afisare();
21 }
22
23 static void citire() throws IOException
24 {
25 int i,j;
26 br=new BufferedReader(new FileReader("bifo.in"));
27 st=new StreamTokenizer(br);
28 st.nextToken(); a1=st.sval.toCharArray();
29 st.nextToken(); a2=st.sval.toCharArray();
30 st.nextToken(); m=(int)st.nval;
31 f=new char[m+1];
32 p=new int[m+1];
33 lg=new int[101];
34 for(i=1;i<=m;i++)
35 {
36 st.nextToken();
37 p[i]=(int)st.nval;
38 }
39 }
40
41 static void rezolvare(int i)
42 {
43 int x,k,sk1,sk,pk;
44 lg[1]=a1.length;
45 lg[2]=a2.length;
46
47 x=p[i];
48 if(x<=lg[1]){ f[i]=a1[x-1]; return; }
49 if(x<=lg[1]+lg[2]){ f[i]=a2[x-lg[1]-1]; return; }
50
51 sk1=lg[1];
52 sk=sk1+lg[2];
53 k=2; // k=cuvantul unde se gaseste caracterul de pe pozitia x
54 while(sk<x)
55 {
56 k++;
57 lg[k]=lg[k-2]+lg[k-1];
58 sk1=sk;
59 sk=sk1+lg[k];
60 }
61 x=x-sk1;
62
63 while(k>2) // a[k]=a[k-2]a[k-1]
64 {
65 if(lg[k-2]>=x) // caracterul este in a[k-2] stanga
66 {
CAPITOLUL 35. ONI 2005 742

67 k=k-2;
68 }
69 else // caracterul este in a[k-1] dreapta
70 {
71 x=x-lg[k-2];
72 k--;
73 }
74 }
75 if(k==1) f[i]=a1[x-1];
76 if(k==2) f[i]=a2[x-1];
77 }
78
79 static void afisare() throws IOException
80 {
81 int i;
82 out=new PrintWriter(new BufferedWriter(new FileWriter("bifo.out")));
83 for(i=1;i<=m;i++) out.print(f[i]);
84 out.println();
85 out.close();
86 }
87 }

Variant  cu numere mari.

Listing 35.1.3: bifo2.java


1 import java.io.*; // Fibo[481]=101 cifre
2 class Bifo3
3 {
4 static int m;
5 static char[] a1;
6 static char[] a2;
7 static char[] f; // formula
8 static char[][] p; // pozitii
9 static char[][] lg=new char[482][1];// lungimile cuvintelor a[i]=a[i-2]a[i-1]
10
11 static PrintWriter out;
12 static BufferedReader br;
13 static StreamTokenizer st;
14
15 public static void main(String[] args) throws IOException
16 {
17 int i;
18 citire();
19 for(i=1;i<=m;i++) rezolvare(i);
20 afisare();
21 }
22
23 static void citire() throws IOException
24 {
25 int i,j;
26 char aux;
27 br=new BufferedReader(new FileReader("bifo.in"));
28 st=new StreamTokenizer(br);
29 st.nextToken(); a1=st.sval.toCharArray();
30 st.nextToken(); a2=st.sval.toCharArray();
31 st.nextToken(); m=(int)st.nval;
32 f=new char[m+1];
33 p=new char[m+1][1];
34
35 br.readLine(); // citeste CR LF adica 0D 0A adica 13 10
36 for(i=1;i<=m;i++)
37 {
38 p[i]=br.readLine().toCharArray();
39 for(j=0;j<p[i].length/2;j++)
40 {
41 aux=p[i][j];
42 p[i][j]=p[i][p[i].length-j-1];
43 p[i][p[i].length-j-1]=aux;
44 }
45 for(j=0;j<p[i].length;j++)
46 p[i][j]=(char)(p[i][j]-0x30); // coduri cifre --> cifre reale
47 }
48 }
49
50 static void rezolvare(int i)
CAPITOLUL 35. ONI 2005 743

51 {
52 char[] x,sk1,sk,pk;
53 int k;
54 lg[1]=nr2v(a1.length);
55 lg[2]=nr2v(a2.length);
56
57 x=suma(nr2v(0),p[i]); // improvizatie pentru x=p[i]; !!!
58 if(compar(x,lg[1])<=0){f[i]=a1[v2nr(x)-1];return;}
59 if(compar(x,suma(lg[1],lg[2]))<=0){f[i]=a2[v2nr(x)-v2nr(lg[1])-1];return;}
60
61 sk1=suma(nr2v(0),lg[1]);
62 sk=suma(sk1,lg[2]);
63 k=2; // k=cuvantul unde se gaseste caracterul de pe pozitia x
64 while(compar(sk,x)<0)
65 {
66 k++;
67 lg[k]=suma(lg[k-2],lg[k-1]);
68 sk1=suma(nr2v(0),sk);
69 sk=suma(sk1,lg[k]);
70 }
71 x=scade(x,sk1);
72 while(k>2) // a[k]=a[k-2]a[k-1]
73 {
74 if(compar(lg[k-2],x)>=0) // caracterul este in a[k-2] stanga
75 {
76 k=k-2;
77 }
78 else // caracterul este in a[k-1] dreapta
79 {
80 x=scade(x,lg[k-2]);
81 k--;
82 }
83 }
84 if(k==1) f[i]=a1[v2nr(x)-1];
85 if(k==2) f[i]=a2[v2nr(x)-1];
86 }
87
88 static void afisare() throws IOException
89 {
90 int i;
91 out=new PrintWriter(new BufferedWriter(new FileWriter("bifo.out")));
92 for(i=1;i<=m;i++) out.print(f[i]);
93 out.println();
94 out.close();
95 }
96
97 static int compar(char[] a, char[] b) //-1, 0, 1 ... a < = > b
98 {
99 int na=a.length;
100 int nb=b.length;
101 if(na>nb) return 1; else if(na<nb) return -1;
102
103 int i=na-1;
104 while((i>=0)&&(a[i]==b[i])) i--;
105 if(i==-1) return 0;
106 else if(a[i]>b[i]) return 1; else return -1;
107 }
108
109 static char[] scade(char[] x,char[] y) // z=x-y unde x>=y
110 {
111 int nx=x.length;
112 int ny=y.length;
113 int nz=nx;
114 int i,s;
115 char t;
116 char[] z=new char[nz];
117 char[] yy=new char[nz];
118 for(i=0;i<ny;i++) yy[i]=y[i];
119 t=0;
120 for(i=0;i<nz;i++)
121 {
122 s=x[i]-yy[i]-t; // poate fi negativ ==> nu merge tipul char !!!
123 if(s<0) {z[i]=(char)(s+10); t=1;} else {z[i]=(char)s; t=0;}
124 }
125 if(z[nz-1]!=0) return z;
126 else
CAPITOLUL 35. ONI 2005 744

127 {
128 int poz=nz-1; // de exemplu 123-121=002 ==>
129 while((int)z[poz]==0) poz--;// pot fi mai multe zero-uri la inceput
130 char[] zz=new char[poz+1];
131 for(i=0;i<=poz;i++) zz[i]=z[i];
132 return zz;
133 }
134 }
135
136 static char[] suma(char[] x,char[] y)
137 {
138 int nx=x.length;
139 int ny=y.length;
140 int nz;
141 if(nx>ny) nz=nx+1; else nz=ny+1;
142 int t,i;
143 char[] z=new char[nz];
144 char[] xx=new char[nz];
145 char[] yy=new char[nz];
146 for(i=0;i<nx;i++) xx[i]=x[i];
147 for(i=0;i<ny;i++) yy[i]=y[i];
148 t=0;
149 for(i=0;i<nz;i++)
150 {
151 z[i]=(char)(xx[i]+yy[i]+t);
152 t=z[i]/10;
153 z[i]=(char)(z[i]%10);
154 }
155 if(z[nz-1]!=0) return z;
156 else
157 {
158 char[] zz=new char[nz-1];
159 for(i=0;i<nz-1;i++) zz[i]=z[i];
160 return zz;
161 }
162 }
163
164 static char[] nr2v(int nr)
165 {
166 int nrr=nr,nc=0,i;
167 while(nr!=0) { nc++; nr=nr/10; }
168 char[] nrv=new char[nc];
169 nr=nrr;
170 for(i=0;i<nc;i++) { nrv[i]=(char)(nr%10); nr=nr/10; }
171 return nrv;
172 }
173
174 static int v2nr(char[] x)
175 {
176 int nr=0,i;
177 for(i=x.length-1;i>=0;i--) nr=nr*10+x[i];
178 return nr;
179 }
180 }

35.2 Romeo
Autor: prof. Dan Grigoriu
Ora³ul Julietei este de form  p trat  ³i are str zi doar pe direcµiile Nord-Sud ³i Est-Vest,
toate la distanµe egale ³i numite ca în desen: strada vertical  0, 1, 2, 3, ..., respectiv strada
orizontal  0, 1, 2, 3... . Julieta locuie³te la intersecµia str zilor: vertical  x ³i orizontalu a y
(poziµia x, y ).
Romeo se a  în colµul de Sud-Vest al ora³ului (poziµia (0,0)) ³i dore³te s  ajung  la Julieta,
nu ³tim exact de ce, dar este treaba lui. Peste toate necazurile cunoscute ale bietului b iat, mai
apar ³i altele:
 ora³ul urc  în pant  spre Nord, ceea ce îngreuneaz  mersul în acea direcµie;
 nu poate merge decât spre Nord sau spre Est, pentru c  dac  "ea" l-ar vedea mergând spre
Vest sau spre Sud, atunci ar crede c  el se îndep rteaz  denitiv de ea.
Numim segment elementar distanµa dintre dou  str zi paralele al turate.
CAPITOLUL 35. ONI 2005 745

Dac  Romeo merge spre Est, atunci el consum  1J (J = joule = o unitate de energie) pentru
ecare segment elementar parcurs.
Din cauza pantei, dac  merge spre Nord k segmente elementare consecutive, consum  1  2 
3  ...  k J .
Romeo vrea s  ajung  la Julieta (mergând în condiµiile de mai sus) cu un consum minim
de energie.
De exemplu, dac  datele sunt:
x 4 ³i y 3,
atunci desenul al turat prezint  un drum posibil (dar nu cu consum minim de energie).

Figura 35.2: Romeo

În desen, avem
a un prim segment elementar orizontal (consum = 1J ), apoi
a spre Nord dou  segmente elementare (consum: 1  2 3J )
a urmeaz  3 segmente spre Est (consum: 1  1  1 3J ) ³i
a ultima porµiune de un segment vertical (consum: 1J ).
Total consum energie: 1  3  3  1 8J .
Cerinµ 
Scrieµi un program care cite³te x ³i y ³i care a³eaz  num rul minim de J consumaµi pentru
tot drumul de la poziµia 0, 0 la poziµia x, y , mergând doar în direcµiile precizate.
Date de intrare
Fi³ierul de intrare romeo.in conµine numerele x ³i y pe prima linie, separate de un spaµiu.
Date de ie³ire
Fi³ierul de ie³ire romeo.out conµine o singur  linie cu num rul de J consumaµi pentru distanµa
total  parcurs  din poziµia de plecare pân  în cea nal .

Restricµii ³i preciz ri
a x ³i y sunt numere naturale;
a 0 & x, y & 40000
a Fiecare linie din ³ierul de intrare ³i din ³ierul de ie³ire se încheie cu marcaj de sfâr³it de
linie.
Exemplu
romeo.in romeo.out
32 5

Figura 35.3: Romeo

Explicaµie
Datele de intrare indic  un ora³ ca în desen.
CAPITOLUL 35. ONI 2005 746

Un drum posibil (el nu este unic) este dat de linia îngro³at .


Primul segment vertical consum  1J, porµiunea orizontal  consum  3J ³i ultimul segment
vertical (cel din dreapta), înc  1J, deci vom a³a num rul 5, care reprezint  1J+3J+1J=5J.
Timp maxim de execuµie/test: 1 sec sub Windows ³i 1 sec sub Linux

35.2.1 Indicaµii de rezolvare

Soluµia ocial , profesor Dan Grigoriu


Soluµia descris  este realizat  didactic ³i se bazeaz  pe un calcul algebric simplu; soluµia nu
conµine structuri repetive.
Energia pentru segmentele orizontale este în toate cazurile aceea³i: X.
Problema r mâne pentru energia consumat  cu segmentele verticale.
Analiz m dou  cazuri:
1) Dac  X ' Y  1, atunci se poate adopta un drum cu consum minim de energie mergând
pe un zig-zag cu segmente elementare, începând cu direcµa spre Nord, pân  la strada orizontal  a
destinaµiei, dup  care (dac  este cazul), se merge pe acea strad  pân  la destinaµie, pe orizontal ,
ca în desenul alaturat.

Figura 35.4: Romeo1

Drumul prezentat nu este unic.


Bilanµul pentru un astfel de drum (în exemplu: X 4 ³i Y 3) este: X  Y J .
2) Dac  X $ Y  1, atunci vom avea ³i porµiuni verticale mai mari decât un segment elementar
pe vertical . Cu cât o asemenea porµiune este mai lung , cu atât energia consumat  este mai
mare. De aceea, vom încerca s  avem porµiuni verticale de lungimi cât mai mici, chiar dac  sunt
mai multe, lungimea lor total  ind aceea³i: Y .
Num rul de porµiuni verticale va  X  1, adic  vom merge vertical pe ecare strad  vertical 
o porµiune, pentru a avea cât mai multe porµiuni verticale (deci ³i mai mici).
Fie Z Y © X  1 lungimea unei porµiuni verticale (num rul de segmente elementare).
Porµiunile verticale vor avea toate lungimea Z , dac  X  1¶Y , sau Z ³i Z  1, în caz contrar.
Pentru acest caz, e M num rul de segmente de m rime Z ³i N num rul de segmente de
m rime Z  1.
Se va rezolva sistemul:
M N X 1 (1)
M ˜ Z  N ˜ Z  1 Y (2)
Semnicaµia ecuaµiilor:
(1): (num rul de porµiuni verticale de lungime Z ) + (num rul de porµiuni verticale de lungime
Z  1) = (num rul total de porµiuni verticale, adica X  1).
(2): (lungimea total  a porµiunilor de lungime Z ) + (lungimea total  a poµiunilor de lungime
Z  1) = (distanµa total  de parcurs pe vertical , adic  Y ).
Odat  obµinute M ³i N , energia pe vertical  se va calcula ca ind suma dintre E1 energia
pentru cele M porµiuni de lungime Z ³i E2 energia pentru cele N porµiuni de lungime Z  1.
E1  E2 M ˜ 1  2  3  ...  Z   N ˜ 1  2  3  ...  Z  1.
CAPITOLUL 35. ONI 2005 747

Figura 35.5: Romeo2

Mai jos, avem conform desenului:


X 4 ³i Y 12.
Atunci: Z Y © X  1 12© 4  1 12©5 2
Sistemul devine:
(1) M  N 4  1
(2) M ˜ 2  N ˜ 2  1 12,
adic 
M N 5
M ˜ 2  N ˜ 2  1 12
cu soluµia:
M 3 ³i N 2
Cu acestea, E1 ³i E2 devin:
E1 M ˜ 1  ..  Z  3 ˜ 1  2 9J
E2 N ˜ 1  ...  Z  1 2 ˜ 1  2  3 12J
Bilanµ:
Total energie pe vertical : E1  E2 9  12 21J .
Total energie pe orizontal : X 4J
Total energie pentru tot drumul: 21  4 25J
Observaµie
Ora³ul este de form  p trat , dar în desen apare doar o parte din el (cea interesant  pentru
noi).

35.2.2 *Cod surs 

35.2.3 Rezolvare detaliat 


y
Rezolvarea sistemului conduce la n y  x  1z ³i m x  1  y  x  1z unde z  x1 
iar energia total  este
z z  1 z  1 z  2
m n  x.
2 2

Listing 35.2.1: romeo.java


1 import java.io.*;
2 class Romeo
3 {
4 public static void main(String[] args) throws IOException
5 {
6 int x,y;
7 int z,m,n,e;
8 PrintWriter out = new PrintWriter(
9 new BufferedWriter(new FileWriter("romeo.out")));
CAPITOLUL 35. ONI 2005 748

10 StreamTokenizer st=new StreamTokenizer(


11 new BufferedReader(new FileReader("romeo.in")));
12 st.nextToken(); x=(int)st.nval;
13 st.nextToken(); y=(int)st.nval;
14 if(x>=y-1) e=x+y;
15 else
16 {
17 z=y/(x+1);
18 n=y-(x+1)*z;
19 m=x+1-y+(x+1)*z;
20 e=m*z*(z+1)/2+n*(z+1)*(z+2)/2+x;
21 }
22 out.println(e);
23 out.close();
24 }
25 }

35.3 Seceta
lect. Ovidiu Dom³a
Gr dinile roditoare ale B r ganului sufer  anual pierderi imense din cauza secetei. C u-
t torii de ap  au g sit n fântâni din care doresc s  alimenteze n gr dini. Fie Gi , Fi , i 1, ..., n
puncte în plan reprezentând puncte de alimentare ale gr dinilor ³i respectiv punctele în care se
a  fântânile. Pentru ecare punct se dau coordonatele întregi x, y  în plan.
Pentru a economisi materiale, leg tura dintre o gr din  ³i o fântân  se realizeaz  printr-o
conduct  în linie dreapt . Fiecare fântân  alimenteaz  o singur  gr din . Consiliul Judeµean
Galaµi pl te³te investiµia cu condiµia ca lungimea total  a conductelor s  e minim .
Fiecare unitate de conduct  cost  100 lei noi (RON).
Cerinµ 
S  se determine m, costul minim total al conductelor ce leag  ecare gr din  cu exact o fântân .
Date de intrare
Fi³ierul de intrare seceta.in va conµine:
Pe prima linie se a  num rul natural n, reprezentând num rul gr dinilor ³i al fântânilor.
a
a Pe urm toarele n linii se a  perechi de numere întregi Gx Gy , separate printr-un spaµiu,
reprezentând coordonatele punctelor de alimentare ale gr dinilor.
a Pe urm toarele n linii se a  perechi de numere întregi Fx Fy , separate printr-un spaµiu,
reprezentând coordonatele punctelor fântânilor.
Date de ie³ire
Fi³ierul de ie³ire seceta.out va conµine:
m  un num r natural reprezentând partea întreag  a costului minim total al conductelor.
Restricµii ³i preciz ri
a 1 $ n $ 13
a 0 & Gx, Gy, F x, F y & 200
a Nu exist  trei puncte coliniare, indiferent dac  sunt gr dini sau fântâni
a Orice linie din ³ierele de intrare ³i ie³ire se termin  prin marcajul de sfâr³it de linie.
Exemplu
seceta.in seceta.out Explicaµie
3 624 Costul minim este [6.24264 * 100]=624
1 4 prin legarea perechilor:
3 3 Gradini Fantani
4 7 14 23
2 3 33 31
2 5 47 25
3 1
Timp maxim de execuµie/test: 1 sec sub Windows ³i 0.5 sec sub Linux.

35.3.1 Indicaµii de rezolvare


CAPITOLUL 35. ONI 2005 749

Soluµia ocial , lect. Ovidiu Dom³a


Num rul mic al punctelor permite generarea tuturor posibilit µilor de a conecta o gr din  cu
o fântân  neconectat  la un moment dat.
Pentru ecare astfel de combinaµie g sit  se calculeaz  suma distanµelor Gi, F j , în li-
nie dreapta, folosindÔ formula distanµei dintre dou  puncte în plan, studiat  la geometrie.
(d A x, y , B z, t x  z 2  y  t2 ).
Acest  soluµie implementat  corect asigur  60  70 de puncte.
Pentru a obµine punctajul maxim se tine cont de urm toarele aspecte:
1. Se construie³te în prealabil matricea distanµelor d i, j  cu semnicaµia distanµei dintre
gr dina i ³i fântâna j . Aceasta va reduce timpul de calcul la variantele cu peste 9 perechi.
2. Pentru a elimina cazuri care nu pot constitui soluµii optime se folose³te proprietatea pa-
trulaterului c  suma a doua laturi opuse (condiµie care asigur  unicitatea conect rii unei singure
fântâni la o singur  gr din ) este mai mic  decât suma diagonalelor. De aceea nu se vor lua în
considerare acele segmente care se intersecteaz . Condiµia de intersecµie a dou  segmente care
au capetele în punctele de coordonate A a1, a2, B b1, b2, C c1, c2, D d1, d2 este ca luând
segmentul AB , punctele C ³i D s  se ae de aceea³i parte a segmentului AB ³i respectiv pentru
segmentul CD, punctele A ³i B s  se ae de aceea³i parte (se înlocuie³te în ecuaµia dreptei ce
trece prin dou  puncte, studiat  în clasa a 9-a).
Observaµie: Pentru cei interesaµi, problema are soluµie ³i la un nivel superior, folosind algorit-
mul de determinare a unui ux maxim de cost minim.

35.3.2 *Cod surs 

35.3.3 Rezolvare detaliat 


Variant  cu determinarea intesecµiei segmentelor.

Listing 35.3.1: seceta1.java


1 import java.io.*; // cu determinarea intesectiei segmentelor
2 class Seceta1 // Java este "mai incet" decat Pascal si C/C++
3 { // test 9 ==> 2.23 sec
4 static int nv=0;
5 static int n;
6 static int[] xg, yg, xf, yf, t, c;
7 static int[] a; // permutare: a[i]=fantana asociata gradinii i
8 static double costMin=200*1.42*12*100;
9 static double[][] d;
10 static PrintWriter out;
11 static StreamTokenizer st;
12
13 public static void main(String[] args) throws IOException
14 {
15 long t1,t2;
16 t1=System.currentTimeMillis();
17 citire();
18 rezolvare();
19 afisare();
20 t2=System.currentTimeMillis();
21 System.out.println("Timp = "+(t2-t1)+" ms");
22 }
23
24 static void citire() throws IOException
25 {
26 int k;
27 st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.in")));
28 st.nextToken(); n=(int)st.nval;
29
30 xg=new int[n+1];
31 yg=new int[n+1];
32 xf=new int[n+1];
33 yf=new int[n+1];
34 a=new int[n+1];
35 d=new double[n+1][n+1];
36
37 for(k=1;k<=n;k++)
38 {
CAPITOLUL 35. ONI 2005 750

39 st.nextToken(); xg[k]=(int)st.nval;
40 st.nextToken(); yg[k]=(int)st.nval;
41 }
42 for(k=1;k<=n;k++)
43 {
44 st.nextToken(); xf[k]=(int)st.nval;
45 st.nextToken(); yf[k]=(int)st.nval;
46 }
47 }
48
49 static void rezolvare() throws IOException
50 {
51 int i,j;
52 int s;
53 for(i=1;i<=n;i++) // gradina i
54 for(j=1;j<=n;j++) // fantana j
55 {
56 s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]);
57 d[i][j]=Math.sqrt(s);
58 }
59 f(1); // generez permutari
60 }
61
62 static void f(int k)
63 {
64 boolean ok;
65 int i,j;
66 for(i=1;i<=n;i++)
67 {
68 ok=true; // k=1 ==> nu am in stanga ... for nu se executa !
69 for(j=1;j<k;j++) if(i==a[j]) {ok=false; break;}
70 if(!ok) continue;
71 for(j=1;j<k;j++)
72 if(seIntersecteaza(xg[k],yg[k],xf[i], yf[i],
73 xg[j],yg[j],xf[a[j]],yf[a[j]]))
74 {
75 ok=false;
76 break;
77 }
78 if(!ok) continue;
79 a[k]=i;
80 if(k<n) f(k+1); else verificCostul();
81 }
82 }
83
84 static void verificCostul()
85 {
86 int i;
87 double s=0;
88 for(i=1;i<=n;i++) s=s+d[i][a[i]];
89 if(s<costMin) costMin=s;
90 }
91
92 // de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp)
93 static int s(int xp,int yp,int xa,int ya,int xb,int yb)
94 {
95 double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya;
96 if(s<-0.001) return -1; // in zona "negativa"
97 else if(s>0.001) return 1; // in zona "pozitiva"
98 else return 0; // pe dreapta suport
99 }
100
101 // testeaza daca segmentul[P1,P1] se intersecteaza cu [P3,P4]
102 static boolean seIntersecteaza(int x1, int y1, int x2, int y2,
103 int x3, int y3, int x4, int y4)
104 {
105 double x,y;
106 if((x1==x2)&&(x3==x4)) // ambele segmente verticale
107 if(x1!=x3) return false;
108 else if(intre(y1,y3,y4)||intre(y2,y3,y4)) return true;
109 else return false;
110
111 if((y1==y2)&&(y3==y4)) // ambele segmente orizontale
112 if(y1!=y3) return false;
113 else if(intre(x1,x3,x4)||intre(x2,x3,x4)) return true;
114 else return false;
CAPITOLUL 35. ONI 2005 751

115
116 if((y2-y1)*(x4-x3)==(y4-y3)*(x2-x1)) // au aceeasi panta (oblica)
117 if((x2-x1)*(y3-y1)==(y2-y1)*(x3-x1)) // au aceeasi dreapta suport
118 if(intre(x1,x3,x4)||intre(x2,x3,x4)) return true;
119 else return false;
120 else return false;// nu au aceeasi dreapta suport
121 else // nu au aceeasi panta (macar unul este oblic)
122 {
123 x=(double)((x4-x3)*(x2-x1)*(y3-y1)-
124 x3*(y4-y3)*(x2-x1)+
125 x1*(y2-y1)*(x4-x3))/
126 ((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1));
127 if(x2!=x1) y=y1+(y2-y1)*(x-x1)/(x2-x1); else y=y3+(y4-y3)*(x-x3)/(x4-x3);
128 if(intre(x,x1,x2)&&intre(y,y1,y2)&&intre(x,x3,x4)&&intre(y,y3,y4))
129 return true; else return false;
130 }
131 }
132
133 static boolean intre(int c, int a, int b) // c este in [a,b] ?
134 {
135 int aux;
136 if(a>b) {aux=a; a=b; b=aux;}
137 if((a<=c)&&(c<=b)) return true; else return false;
138 }
139
140 static boolean intre(double c, int a, int b) // c este in [a,b] ?
141 {
142 int aux;
143 if(a>b) {aux=a; a=b; b=aux;}
144 if((a<=c)&&(c<=b)) return true; else return false;
145 }
146
147 static void afisare() throws IOException
148 {
149 int k;
150 out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out")));
151 out.println((int)(costMin*100));
152 out.close();
153 }
154 }

Variant  cu cu determinarea pozitiei punctelor in semiplane ³i mesaje pentru depanare.

Listing 35.3.2: seceta2.java


1 import java.io.*; // cu determinarea pozitiei punctelor in semiplane
2 class Seceta2 // cu mesaje pentru depanare !
3 {
4 static int nv=0;
5 static int n;
6 static int[] xg, yg, xf, yf, t, c;
7 static int[] a; // permutare: a[i]=fantana asociata gradinii i
8 static double costMin=200*1.42*12*100;
9 static double[][] d;
10 static PrintWriter out;
11 static StreamTokenizer st;
12
13 public static void main(String[] args) throws IOException
14 {
15 long t1,t2;
16 t1=System.currentTimeMillis();
17 citire();
18 rezolvare();
19 afisare();
20 t2=System.currentTimeMillis();
21 System.out.println("Timp = "+(t2-t1)+" ms");
22 }
23
24 static void citire() throws IOException
25 {
26 int k;
27 st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.in")));
28 st.nextToken(); n=(int)st.nval;
29 xg=new int[n+1];
30 yg=new int[n+1];
31 xf=new int[n+1];
CAPITOLUL 35. ONI 2005 752

32 yf=new int[n+1];
33 a=new int[n+1];
34 d=new double[n+1][n+1];
35
36 for(k=1;k<=n;k++)
37 {
38 st.nextToken(); xg[k]=(int)st.nval;
39 st.nextToken(); yg[k]=(int)st.nval;
40 }
41 for(k=1;k<=n;k++)
42 {
43 st.nextToken(); xf[k]=(int)st.nval;
44 st.nextToken(); yf[k]=(int)st.nval;
45 }
46 }
47
48 static void rezolvare() throws IOException
49 {
50 int i,j;
51 int s;
52 for(i=1;i<=n;i++) // gradina i
53 for(j=1;j<=n;j++) // fantana j
54 {
55 s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]);
56 d[i][j]=Math.sqrt(s);
57 }
58 f(1); // generez permutari
59 }
60
61 static void f(int k)
62 {
63 boolean ok;
64 int i,j;
65 for(i=1;i<=n;i++)
66 {
67 ok=true; // k=1 ==> nu am in stanga ... for nu se executa !
68 for(j=1;j<k;j++) if(i==a[j]) {ok=false; break;}
69 if(!ok) continue;
70
71 for(j=1;j<k;j++)
72 if((s(xg[k],yg[k],xg[j],yg[j],xf[a[j]],yf[a[j]])*
73 s(xf[i],yf[i],xg[j],yg[j],xf[a[j]],yf[a[j]])<0)&&
74 (s(xg[j], yg[j], xg[k],yg[k],xf[i],yf[i])*
75 s(xf[a[j]],yf[a[j]],xg[k],yg[k],xf[i],yf[i])<0))
76 {
77 afisv(k-1);// pe pozitia k(gradina) vreau sa pun i(fantana)
78 System.out.print(i+" ");// pe pozitia j(gradina) e pus a[j](fantana)
79 System.out.print(k+""+i+" "+j+""+a[j]);
80 System.out.print(" ("+xg[k]+","+yg[k]+") "+" ("+xf[i]+","+yf[i]+") ");
81 System.out.println(" ("+xg[j]+","+yg[j]+") "+" ("+xf[a[j]]+","+yf[a[j]]+") "
);
82
83 ok=false;
84 break;
85 }
86 if(!ok) continue;
87
88 a[k]=i;
89 if(k<n) f(k+1); else verificCostul();
90 }
91 }
92
93 static void verificCostul()
94 {
95 int i;
96 double s=0;
97 for(i=1;i<=n;i++) s=s+d[i][a[i]];
98 if(s<costMin) costMin=s;
99 afisv(n); System.out.println(" "+s+" "+costMin+" "+(++nv));
100 }
101
102 static void afisv(int nn)
103 {
104 int i;
105 for(i=1;i<=nn;i++) System.out.print(a[i]);
106 }
CAPITOLUL 35. ONI 2005 753

107
108 // de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp)
109 static int s(int xp,int yp,int xa,int ya,int xb,int yb)
110 {
111 double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya;
112 if(s<-0.001) return -1; // in zona "negativa"
113 else if(s>0.001) return 1; // in zona "pozitiva"
114 else return 0; // pe dreapta suport
115 }
116
117 static void afisare() throws IOException
118 {
119 int k;
120 out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out")));
121 out.println((int)(costMin*100));
122 out.close();
123 }
124 }

Variant  cu cu determinarea pozitiei punctelor in semiplane, f r mesaje pentru depanare.

Listing 35.3.3: seceta3.java


1 import java.io.*; // cu determinarea pozitiei punctelor in semiplane
2 class Seceta3 // Java este "mai incet" decat Pascal si C/C++
3 { // test 9 ==> 2.18 sec
4 static int n;
5 static int[] xg, yg, xf, yf, t, c;
6 static int[] a; // permutare: a[i]=fantana asociata gradinii i
7 static double costMin=200*1.42*12*100;
8 static double[][] d;
9 static PrintWriter out;
10 static StreamTokenizer st;
11
12 public static void main(String[] args) throws IOException
13 {
14 long t1,t2;
15 t1=System.currentTimeMillis();
16 citire();
17 rezolvare();
18 afisare();
19 t2=System.currentTimeMillis();
20 System.out.println("Timp = "+(t2-t1)+" ms");
21 }
22
23 static void citire() throws IOException
24 {
25 int k;
26 st=new StreamTokenizer(new BufferedReader(
27 new FileReader("seceta.in")));
28 st.nextToken(); n=(int)st.nval;
29 xg=new int[n+1];
30 yg=new int[n+1];
31 xf=new int[n+1];
32 yf=new int[n+1];
33 a=new int[n+1];
34 d=new double[n+1][n+1];
35
36 for(k=1;k<=n;k++)
37 {
38 st.nextToken(); xg[k]=(int)st.nval;
39 st.nextToken(); yg[k]=(int)st.nval;
40 }
41 for(k=1;k<=n;k++)
42 {
43 st.nextToken(); xf[k]=(int)st.nval;
44 st.nextToken(); yf[k]=(int)st.nval;
45 }
46 }
47
48 static void rezolvare() throws IOException
49 {
50 int i,j;
51 int s;
52 for(i=1;i<=n;i++) // gradina i
53 for(j=1;j<=n;j++) // fantana j
CAPITOLUL 35. ONI 2005 754

54 {
55 s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]);
56 d[i][j]=Math.sqrt(s);
57 }
58 f(1); // generez permutari
59 }
60
61 static void f(int k)
62 {
63 boolean ok;
64 int i,j;
65 for(i=1;i<=n;i++)
66 {
67 ok=true; // k=1 ==> nu am in stanga ... for nu se executa !
68 for(j=1;j<k;j++) if(i==a[j]) {ok=false; break;}
69 if(!ok) continue;
70 for(j=1;j<k;j++)
71 if((s(xg[k], yg[k], xg[j],yg[j],xf[a[j]],yf[a[j]])*
72 s(xf[i], yf[i], xg[j],yg[j],xf[a[j]],yf[a[j]])<0)&&
73 (s(xg[j], yg[j], xg[k],yg[k],xf[i], yf[i])*
74 s(xf[a[j]],yf[a[j]],xg[k],yg[k],xf[i], yf[i])<0))
75 {
76 ok=false;
77 break;
78 }
79 if(!ok) continue;
80 a[k]=i;
81 if(k<n) f(k+1); else verificCostul();
82 }
83 }
84
85 static void verificCostul()
86 {
87 int i;
88 double s=0;
89 for(i=1;i<=n;i++) s=s+d[i][a[i]];
90 if(s<costMin) costMin=s;
91 }
92
93 //de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp)
94 static int s(int xp,int yp,int xa,int ya,int xb,int yb)
95 {
96 double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya;
97 if(s<-0.001) return -1; // in zona "negativa"
98 else if(s>0.001) return 1; // in zona "pozitiva"
99 else return 0; // pe dreapta suport
100 }
101
102 static void afisare() throws IOException
103 {
104 int k;
105 out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out")));
106 out.println((int)(costMin*100));
107 out.close();
108 }
109 }

Varianta 4:

Listing 35.3.4: seceta4.java


1 import java.io.*; // gresit (!) dar ... obtine 100p ... !!!
2 class Seceta4 // test 9 : 2.18 sec --> 0.04 sec
3 {
4 static int n;
5 static int[] xg, yg, xf, yf, t, c;
6 static int[] a; // permutare: a[i]=fantana asociata gradinii i
7 static double costMin=200*1.42*12*100;
8 static double[][] d;
9 static boolean[] epus=new boolean[13];
10
11 static PrintWriter out;
12 static StreamTokenizer st;
13
14 public static void main(String[] args) throws IOException
15 {
CAPITOLUL 35. ONI 2005 755

16 long t1,t2;
17 t1=System.currentTimeMillis();
18
19 citire();
20 rezolvare();
21 afisare();
22
23 t2=System.currentTimeMillis();
24 System.out.println("Timp = "+(t2-t1)+" ms");
25 }// main(...)
26
27 static void citire() throws IOException
28 {
29 int k;
30 st=new StreamTokenizer(new BufferedReader(new FileReader("seceta.in")));
31 st.nextToken(); n=(int)st.nval;
32 xg=new int[n+1];
33 yg=new int[n+1];
34 xf=new int[n+1];
35 yf=new int[n+1];
36 a=new int[n+1];
37
38 d=new double[n+1][n+1];
39
40 for(k=1;k<=n;k++)
41 {
42 st.nextToken(); xg[k]=(int)st.nval;
43 st.nextToken(); yg[k]=(int)st.nval;
44 }
45 for(k=1;k<=n;k++)
46 {
47 st.nextToken(); xf[k]=(int)st.nval;
48 st.nextToken(); yf[k]=(int)st.nval;
49 }
50 }// citire(...)
51
52 static void rezolvare() throws IOException
53 {
54 int i,j;
55 int s;
56 for(i=1;i<=n;i++) // gradina i
57 for(j=1;j<=n;j++) // fantana j
58 {
59 s=(xg[i]-xf[j])*(xg[i]-xf[j])+(yg[i]-yf[j])*(yg[i]-yf[j]);
60 d[i][j]=Math.sqrt(s);
61 }
62 f(1); // generez permutari
63 }// rezolvare(...)
64
65 static void f(int k)
66 {
67 int i,j;
68 boolean seIntersecteaza;
69 for(i=1;i<=n;i++)
70 {
71 if(epus[i]) continue;
72 seIntersecteaza=false;
73 for(j=1;j<=k-1;j++)
74 if(d[k][i]+d[j][a[j]]>d[j][i]+d[k][a[j]])
75 {
76 seIntersecteaza=true;
77 break;
78 }
79
80 if(seIntersecteaza) continue;
81
82 a[k]=i;
83
84 epus[i]=true;
85 if(k<n) f(k+1); else verificCostul();
86 epus[i]=false;
87 }// for i
88 }// f(...)
89
90 static void verificCostul()
91 {
CAPITOLUL 35. ONI 2005 756

92 int i;
93 double s=0;
94 for(i=1;i<=n;i++) s=s+d[i][a[i]];
95 if(s<costMin) costMin=s;
96 }// verificCostul(...)
97
98 static void afisare() throws IOException
99 {
100 int k;
101 out=new PrintWriter(new BufferedWriter(new FileWriter("seceta.out")));
102 out.println((int)(costMin*100));
103 out.close();
104 }// afisare(...)
105 }// class

35.4 Biblos
Maria ³i Adrian Niµ 
Din dorinµa de a realiza un fond de carte cât mai voluminos, ocialit µile ora³ului Ga-
laµi, au modernizat pentru început, o sal  pentru depozitarea c rµilor ³i l-au numit pe Biblos
coordonatorul acestei biblioteci.
Achiziµionarea de carte s-a realizat în mai multe etape.
De ecare dat  c rµile achiziµionate au fost depozitate pe câte un stativ construit special de
Biblos.
Pentru a avea spaµiu de depozitare Biblos a construit mai multe stative decât i-ar  fost
necesare, unele putând r mâne f r  c rµi.
Dup  mai multe etape de achiziµionare, Biblos a constatat c  spaµiul alocat bibliotecii este
prea mic.
Primind un alt spaµiu mai înc p tor, mut  primul stativ cu toate c rµile conµinute de acesta
³i se opre³te deoarece î³i dore³te s  mute acele stative care nu sunt a³ezate unul lâng  cel lalt ³i
care fac ca fondul de carte din noua sal  s  e cât mai mare posibil.
Cerinµ 
Scrieµi un program care, cunoscând num rul stativelor, precum ³i num rul de volume de carte
de pe ecare stativ, determin  care este num rul maxim de volume care pot  mutate în noua
sal , ³tiind c  primul stativ a fost deja mutat iar celelalte se aleg astfel încât s  nu e a³ezate unul
lâng  cel lalt. Dac  exist  stative care nu au c rµi acestea nu vor  mutate în a doua sal .
Date de intrare
Fi³ierul de intrare biblos.in conµine
a pe prima linie o valoare n, num r natural cu semnicaµia num rul de stative,
a pe a doua linie n numere naturale, x1 , x2 , ..., xn separate prin câte un spaµiu cu semnicaµia
xi = num rul de volume de carte existente pe ecare stativ.
Date de ie³ire
Fi³ierul de ie³ire biblos.out va conµine o singur  linie unde se a  un num r natural cu
semnicaµia: num rul maxim de volume ce au fost transferate.
Restricµii ³i preciz ri
a 1 & n & 30000
a 0 & xi & 32767, unde i 1, ..., n iar xi reprezint  num rul de c rµi de pe stativul i.
a Pentru 70% dintre teste n & 1000
a Fiecare linie din ³ierul de intrare ³i din ³ierul de ie³ire se termin  cu marcaj de sfâr³it de
linie.
Exemple
biblos.in biblos.out
7 16
1362584
Explicaµie: Suma maxim  se obµine din mutarea stativelor 1 (obligatoriu), 3, 5, 7 (nu pot 
stative al turate)
CAPITOLUL 35. ONI 2005 757

biblos.in biblos.out
15 836
3 1 84 9 89 55 135 49 176 238 69 112 28 175 142
Explicaµie: Suma maxim  obµinut  din mutarea stativelor 1, 3, 5, 7, 10, 12, 14

biblos.in biblos.out
8 32
7 1 4 12 9 9 12 4
Explicaµie: Suma maxim  obµinut  din mutarea stativelor 1, 3, 5, 7, sau din mutarea stativelor
1, 4, 6, 8.
Timp maxim de execuµie/test: 0.5 sec sub Windows ³i 0.1 sec sub Linux

35.4.1 Indicaµii de rezolvare

Soluµia ocial , Maria ³i Adrian Niµ 


Problema cere determinarea unei sume maxime format  din numerele citite, astfel încât s  se
aleag  doar valori care nu sunt pe poziµii consecutive.
Structurile folosite sunt:
a carti 1, carti 2, ... carti n
unde carti i = num rul de volume existente pe stativul i (i 1, ..., n).
a cmax 1, cmax 2, ..., cmax n
unde cmax i = num rul maxim de volume ce pot  transferate utilizând c rµile existente pe
stativele de la 1 la i  2, astfel încât s  e selectat ³i raftul i.
Primul stativ ind întotdeauna transferat atunci realizarea lui cmax se poate face astfel:

cmax 1 carti 1;

Pentru toate celelalte valori cmax i, se observ  c  se poate folosi formula:

cmax i carti i  maximrcmax k  unde k 1, ..., i  2x

Prin alegerea maximului dintre valorile lui cmax, se obµine rezultatul cerut.
Pentru optimizare, se poate face observaµia c  dintre valorile lui cmax, intereseaz  doar cele
de pe poziµiile i  3 ³i i  2, deci:
dac  cmax i  2 % cmax i  3
atunci cmax i cmax i  2  carti i
altfel cmax i cmax i  3  carti i/

35.4.2 *Cod surs 

35.4.3 Rezolvare detaliat 


Variant  pentru depanare.

Listing 35.4.1: biblos1.java


1 import java.io.*;
2 class Biblos1
3 {
4 static int n;
5 static int[] x;
6 static int[] smax;
7 static int[] p; // predecesor, pentru depanare
8
9 public static void main(String []args) throws IOException
10 {
11 long t1,t2;
12 t1=System.currentTimeMillis();
13
14 int i,j,max,jmax,ji;
15 StreamTokenizer st=new StreamTokenizer(
16 new BufferedReader(new FileReader("biblos.in")));
CAPITOLUL 35. ONI 2005 758

17 PrintWriter out = new PrintWriter (


18 new BufferedWriter( new FileWriter("biblos.out")));
19
20 st.nextToken();n=(int)st.nval;
21 x=new int[n+1];
22 smax=new int[n+1];
23 p=new int[n+1];
24
25 for(i=1;i<=n;i++) { st.nextToken(); x[i]=(int)st.nval;}
26
27 smax[1]=x[1];
28 for(i=2;i<=n;i++)
29 {
30 max=0; jmax=0;
31 if(i==2) ji=0; else ji=i-3;
32 for(j=ji;j<=i-2;j++)
33 if(smax[j]>=max) { max=smax[j]; jmax=j; }
34 if(max!=0) { smax[i]=max+x[i]; p[i]=jmax;} else {smax[i]=0; p[i]=0;}
35 }
36
37 if(smax[n]>smax[n-1]) {max=smax[n]; jmax=n;}
38 else {max=smax[n-1]; jmax=n-1;}
39 out.println(max);
40 out.close();
41
42 t2=System.currentTimeMillis();
43 drum(jmax);
44 System.out.println("Timp = "+(t2-t1)+" ms");
45 }//main
46
47 static void drum(int j)
48 {
49 if(p[j]!=0) drum(p[j]);
50 System.out.println(j+" "+x[j]+" "+smax[j]);
51 }
52 }//class

Variant  cu vectori.

Listing 35.4.2: biblos2.java


1 import java.io.*;
2 class Biblos2
3 {
4 static int n;
5 static int[] x;
6 static int[] smax;
7
8 public static void main(String []args) throws IOException
9 {
10 int i,j,max,jmax,ji;
11 StreamTokenizer st=new StreamTokenizer(
12 new BufferedReader(new FileReader("biblos.in")));
13 PrintWriter out = new PrintWriter (
14 new BufferedWriter( new FileWriter("biblos.out")));
15
16 st.nextToken();n=(int)st.nval;
17 x=new int[n+1];
18 smax=new int[n+1];
19
20 for(i=1;i<=n;i++) { st.nextToken(); x[i]=(int)st.nval;}
21
22 smax[1]=x[1];
23 for(i=2;i<=n;i++)
24 {
25 max=0; jmax=0;
26 if(i==2) ji=0; else ji=i-3;
27 for(j=ji;j<=i-2;j++)
28 if(smax[j]>=max) { max=smax[j]; jmax=j; }
29 if(max!=0) smax[i]=max+x[i]; else smax[i]=0;
30 }
31
32 if(smax[n]>smax[n-1]) max=smax[n]; else max=smax[n-1];
33 out.println(max);
34 out.close();
35 }//main
CAPITOLUL 35. ONI 2005 759

36 }//class

Variant  f r  vectori (optimizat spaµiul de memorie folosit).

Listing 35.4.3: biblos3.java


1 import java.io.*; // s-a renuntat la vectorii x si smax
2 class Biblos3
3 {
4 static int n;
5 static int xi;
6 static int si,si1,si2,si3;
7
8 public static void main(String []args) throws IOException
9 {
10 int i,j,max,jmax,ji;
11 StreamTokenizer st=new StreamTokenizer(
12 new BufferedReader(new FileReader("biblos.in")));
13 PrintWriter out = new PrintWriter (
14 new BufferedWriter( new FileWriter("biblos.out")));
15
16 st.nextToken();n=(int)st.nval;
17 st.nextToken(); xi=(int)st.nval;
18 si3=0;
19 si2=xi;
20 si1=0;
21 st.nextToken(); xi=(int)st.nval; // citesc x[2] si il neglijez !
22
23 for(i=3;i<=n;i++)
24 {
25 st.nextToken(); xi=(int)st.nval;
26 if(si2>si3) si=si2+xi; else si=si3+xi;
27 si3=si2;
28 si2=si1;
29 si1=si;
30 }
31
32 if(si1>si2) out.println(si1); else out.println(si2);
33 out.close();
34 }//main
35 }//class

35.5 Joc
Cristina Luca
Pe o tabl  p trat  de dimensiune n  n se deseneaz  o secvenµ  de triunghiuri dreptunghic
isoscele.
Fiecare triunghi are vârfurile numerotate cu 1, 2 ³i 3 (2 corespunde unghiului drept iar ordinea
1, 2, 3 a vârfurilor este în sens invers acelor de ceasornic - vezi gura). Triunghiurile au catetele
paralele cu marginile tablei.
Primul triunghi, având lungimea catetei Lg , are vârful 1 pe linia L ³i coloana C ³i este orientat
ca în gur .
3
a
a a
a a a
1 a a a a 2
Jocul const  în alipirea câte unui nou triunghi la unul din vârfurile 2 sau 3 ale triunghiului
curent. Dac  se al tur  colµului 2, noul triunghi se a³eaz  cu vârful 1 în prelungirea laturii 1, 2 a
triunghiului curent, iar dac  se al tur  colµului 3 se a³eaz  cu vârful 1 în prelungirea laturii 2, 3.
Iniµial noul triunghi este orientat ca ³i cel anterior. El se poate plasa pe tabl  dac  nu sunt
dep ³ite marginile acesteia sau nu se suprapune peste un alt triunghi. În caz contrar, se face o
o
singur rotaµie cu 90 spre stânga, obµinându-se o nou  orientare a triunghiului. Dac  nici în acest
caz noul triunghi nu poate  plasat, jocul se opre³te.
Zona ocupat  de primul triunghi se completez  cu litera 'a'; zona celui de-al doilea se comple-
tez cu litera 'b', ³.a.m.d. Când literele mici ale alfabetului englez sunt epuizate, se reîncepe de la
'a'.
CAPITOLUL 35. ONI 2005 760

Cerinµ 
Cunoscându-se dimensiunea tablei, poziµia primului triunghi (linie, coloan ) ³i lungimea catetei
precum ³i o secvenµ  de triunghiuri care se doresc a  alipite se cere s  se genereze matricea
rezultat  în nalul jocului.
Jocul se termin  dac  un triunghi nu mai poate  alipit sau au fost plasate toate triunghiurile
descrise în secvenµ .
Date de intrare
În ³ierul de intrare joc.in, pe prima linie se a  n (dimensiunea tablei). Pe a doua linie
separate prin câte un spaµiu se a : L (linia), C (coloana) ³i Lg (lungimea catetei) corespunz toare
primului triunghi. Urm toarele linii, pân  la sfâr³itul ³ierului, conµin câte dou  numere naturale
separate prin câte un singur spaµiu reprezentând colµul triunghiului curent la care va  alipit
triunghiul urm tor ³i dimensiunea catetei triunghiului urm or.
Date de ie³ire
În ³ierul de ie³ire joc.out va  a³at  matricea rezultat . Celulele tablei care nu sunt com-
pletate cu litere ale alfabetului vor  completate cu '.'.
Restricµii ³i preciz ri
a 1 & n & 100; 1 & C , L & n; 2 & Lg & n
a Fiecare linie din ³ierul de intrare ³i din c serul de iec sre se termin  cu marcaj de sfâr³it
de linie.
Exemplu
joc.in joc.out
20 . . . . . . . . . . . . . . . . . . . .
16 8 4 . . . . . . . . . . . . . . . . . . . .
35 . . . . . . . . . . f f f f f e e e . .
23 . . . . . . . . . . f f f f . . e e . .
34 . . . . . . . . . . f f f . . . . e . .
23 . . . . . . . . . . f f . . d d d d . .
35 . . . . . . . . . . f . . . . d d d . .
33 . . . . . . h h g g g . . . b . d d . .
22 . . . . . . h . g g . . . b b . . d . .
34 j j j i i i i . g . . . b b b . . c . .
23 j j . i i i . . . . . b b b b . c c . .
33 j . . i i . . . . . b b b b b c c c . .
32 k . . i . . . . . . a . . . . . . . . .
33 k k . . . . . . . a a . . . . . . . . .
33 k k k l . . . . a a a . . . . . . . . .
24 . . . l l m . a a a a . . . . . . . . .
. . . . . m m . . . . . . . . . . . . .
. . . . . m m m n . . . . . . . . . . .
. . . . . . . . n n . . . . . . . . . .
. . . . . . . . n n n . . . . . . . . .

Explicaµii
 Triunghiul 'a' este plasat în linia 16 coloana 8 ³i are latura 4.
 Triunghiul 'b' se alipe³te în colµul 3 ³i are lungimea 5.
 Triunghiul 'c' se alipe³te în colµul 2 ³i are lungimea 3.
 Tringhiurile 'a', 'b' ³i 'c' p streaz  aceea³i aranjare.
 Triunghiul 'd' nu se poate alipi în aceea³i aranjare colµului 3 deoarece are cateta de lungimea
o
4 ³i dep ³e³te tabla. Rotim triunghiul cu 90 spre stânga ³i obµinem o nou  aranjare.
 Triunghiul 'e' se alipe³te în colµul 2 ³i are cateta de lungime 3 în aranjarea curent .
 Triunghiul 'f' nu se poate alipi în aceea³i aranjare cu 'e' în colµul 3 deoarece are cateta de
o
lungimea 5 ³i dep ³e³te tabla. Rotim triunghiul cu 90 spre stânga ³i obµinem o nou  aranjare.
Triunghiul 'f' se alipe³te în colµul 3, are lungimea 5 ³i o nou  aranjare.
 Algoritmul continu  pân  la al 14-lea triunghi, 'n'.
 Al 15-lea triunghi nu se mai poate plasa.

Timp maxim de execuµie/test: 0.2 sec în Windows ³i 0.2 sec sub Linux
CAPITOLUL 35. ONI 2005 761

35.5.1 Indicaµii de rezolvare

Soluµia ocial , Cristina Luca


Soluµia problemei const  în gestionarea spaµiului unei matrice folosind propriet µi ale indicilor,
parcurgerea pe linii, pe coloane, pe diagonale, ecare în dou  sensuri.
Plasarea triunghiurilor pe tabla de joc presupune vericarea urm toarelor dou  situaµii:
1. cel puµin un element al zonei triunghiului care urmeaz  s  e plasat se a  în afara tablei;
a. în exemplul de mai jos situaµiile triunghiurilor 'd', 'f', 'k', triunghiuri care nu pot  plasate
în aceea³i orientare cu cele care le preced deoarece lungimile catetelor dep ³esc marginile dreapt ,
sus, respectiv stâang  ale matricei;
b. dac  triunghiul 'k' ar  plasat în poziµia 2 faµ  de triunghiul 'j' ar avea vârful în afara tablei;
2. cel puµin un element al zonei triunghiului care urmeaz  s  e plasat se suprapune peste un
element al unui triunghi deja plasat.
o
Vericarea celor dou  situaµi se face ³i dac  se încearc  rotirea cu 90 a triunghiului. De
exemplu, dac  triunghiul 'd' nu este rotit apare situaµia 1.a. De aceea el este rotit obµinându-se
noua orientare în care nu apar situaµiile 1 sau 2.
Exist  dou  situaµii în care jocul se opre³te:
a se epuizeaz  secvenµa de triunghiuri descrise în ³ier;
a triunghiul nu poate  plasat nici direct nici dup  o singur  rotire; restul triunghiurilor nu
vor mai  luate în considerare: în exemplul de mai jos ultimul triunghi, 24, nu va mai  plasat.
Exemplu
joc.in joc.out
20 . . . . . . . . . . . . . . . . . . . .
16 8 4 . . . . . . . . . . . . . . . . . . . .
35 . . . . . . . . . . f f f f f e e e . .
23 . . . . . . . . . . f f f f . . e e . .
34 . . . . . . . . . . f f f . . . . e . .
23 . . . . . . . . . . f f . . d d d d . .
35 . . . . . . . . . . f . . . . d d d . .
33 . . . . . . h h g g g . . . b . d d . .
22 . . . . . . h . g g . . . b b . . d . .
34 j j j i i i i . g . . . b b b . . c . .
23 j j . i i i . . . . . b b b b . c c . .
33 j . . i i . . . . . b b b b b c c c . .
32 k . . i . . . . . . a . . . . . . . . .
33 k k . . . . . . . a a . . . . . . . . .
33 k k k l . . . . a a a . . . . . . . . .
24 . . . l l m . a a a a . . . . . . . . .
. . . . . m m . . . . . . . . . . . . .
. . . . . m m m n . . . . . . . . . . .
. . . . . . . . n n . . . . . . . . . .
. . . . . . . . n n n . . . . . . . . .

35.5.2 *Cod surs 

35.5.3 Rezolvare detaliat 

Listing 35.5.1: joc.java


1 import java.io.*;
2 class Joc
3 {
4 static int n;
5 static int[][] a;
6 static int val; // numar/caracter de umplere: 0,1,...,25 ==> (val+1)%26
7 static int dir; // directia: 0,1,2,3 circular ==> (dir+1)%4
8
9 public static void main(String[] args) throws IOException
10 {
CAPITOLUL 35. ONI 2005 762

11 long t1,t2;
12 t1=System.currentTimeMillis();
13 int v,d,i,j;
14 int l1, c1, d1;
15 boolean ok=true;
16 StreamTokenizer st=new StreamTokenizer(
17 new BufferedReader(new FileReader("joc.in")));
18 PrintWriter out=new PrintWriter(
19 new BufferedWriter(new FileWriter("joc.out")));
20 st.nextToken(); n=(int)st.nval;
21
22 a=new int[n+1][n+1];
23 for(i=1;i<=n;i++) for(j=1;j<=n;j++) a[i][j]=-1;
24
25 st.nextToken(); l1=(int)st.nval; // linia varful 1
26 st.nextToken(); c1=(int)st.nval; // coloana varful 1
27 st.nextToken(); d1=(int)st.nval; // latura
28
29 val=0;
30 dir=0;
31 if(esteGol0(l1,c1,d1)) umple0(l1,c1,d1);
32 else if(esteGol1(l1,c1,d1)) {dir=(dir+1)%4; umple1(l1,c1,d1);}
33
34 while(ok&&(st.nextToken()!=StreamTokenizer.TT_EOF))
35 {
36 v=(int)st.nval;
37 st.nextToken(); d=(int)st.nval;
38 val=(val+1)%26;
39
40 if(v==2)
41 switch(dir)
42 {
43 case 0: if(esteGol0(l1,c1+d1,d)) // direct
44 {
45 c1=c1+d1; d1=d; umple0(l1,c1,d1);
46 }
47 else if(esteGol1(l1,c1+d1,d)) // rotit
48 {
49 c1=c1+d1; d1=d;
50 dir=(dir+1)%4;
51 umple1(l1,c1,d1);
52 }
53 else ok=false;
54 break;
55 case 1: if(esteGol1(l1-d1,c1,d)) // direct
56 {
57 l1=l1-d1; d1=d; umple1(l1,c1,d1);
58 }
59 else if(esteGol2(l1-d1,c1,d)) // rotit
60 {
61 l1=l1-d1; d1=d;
62 dir=(dir+1)%4;
63 umple2(l1,c1,d1);
64 }
65 else ok=false;
66 break;
67 case 2: if(esteGol2(l1,c1-d1,d)) // direct
68 {
69 c1=c1-d1; d1=d; umple2(l1,c1,d1);
70 }
71 else if(esteGol3(l1,c1-d1,d)) // rotit
72 {
73 c1=c1-d1;d1=d;
74 dir=(dir+1)%4;
75 umple3(l1,c1,d1);
76 }
77 else ok=false;
78 break;
79 case 3: if(esteGol3(l1+d1,c1,d)) // direct
80 {
81 l1=l1+d1;d1=d; umple3(l1,c1,d1);
82 }
83 else if(esteGol0(l1+d1,c1,d)) // rotit
84 {
85 l1=l1+d1; d1=d;
86 dir=(dir+1)%4;
CAPITOLUL 35. ONI 2005 763

87 umple0(l1,c1,d1);
88 }
89 else ok=false;
90 break;
91 default: System.out.println("Ciudat!!!");
92 }
93
94 if(v==3)
95 switch(dir)
96 {
97 case 0: if(esteGol0(l1-d1,c1+d1-1,d)) // direct
98 {
99 c1=c1+d1-1; l1=l1-d1; d1=d; umple0(l1,c1,d1);
100 }
101 else if(esteGol1(l1-d1,c1+d1-1,d)) // rotit
102 {
103 l1=l1-d1; c1=c1+d1-1; d1=d;
104 dir=(dir+1)%4;
105 umple1(l1,c1,d1);
106 }
107 else ok=false;
108 break;
109 case 1: if(esteGol1(l1-d1+1,c1-d1,d)) // direct
110 {
111 l1=l1-d1+1; c1=c1-d1; d1=d; umple1(l1,c1,d1);
112 }
113 else if(esteGol2(l1-d1+1,c1-d1,d)) // rotit
114 {
115 l1=l1-d1+1; c1=c1-d1; d1=d;
116 dir=(dir+1)%4;
117 umple2(l1,c1,d1);
118 }
119 else ok=false;
120 break;
121 case 2: if(esteGol2(l1+d1,c1-d1+1,d)) // direct
122 {
123 c1=c1-d1+1; l1=l1+d1; d1=d; umple2(l1,c1,d1);
124 }
125 else if(esteGol3(l1+d1,c1-d1+1,d)) // rotit
126 {
127 c1=c1-d1+1; l1=l1+d1; d1=d;
128 dir=(dir+1)%4;
129 umple3(l1,c1,d1);
130 }
131 else ok=false;
132 break;
133 case 3: if(esteGol3(l1+d1-1,c1+d1,d)) // direct
134 {
135 l1=l1+d1-1;c1=c1+d1; d1=d; umple3(l1,c1,d1);
136 }
137 else if(esteGol0(l1+d1-1,c1+d1,d)) // rotit
138 {
139 l1=l1+d1-1;c1=c1+d1; d1=d;
140 dir=(dir+1)%4;
141 umple0(l1,c1,d1);
142 }
143 else ok=false;
144 break;
145 default: System.out.println("Ciudat!!!");
146 }
147 }
148
149 for(i=1;i<=n;i++)
150 {
151 for(j=1;j<=n;j++)
152 if(a[i][j]==-1) out.print("."); else out.print((char)(a[i][j]+’a’));
153 out.println();
154 }
155 out.println();
156 out.close();
157
158 t2=System.currentTimeMillis();
159 System.out.println("Timp = "+(t2-t1)+" ms");
160 }
161
162 static void umple0(int lin, int col, int d) // 12=sd==>dir=0
CAPITOLUL 35. ONI 2005 764

163 {
164 int i,j,k;
165 for(k=0;k<d;k++)
166 {
167 i=lin-k;
168 for(j=col+k;j<col+d;j++) a[i][j]=val;
169 }
170 }
171
172 static boolean esteGol0(int lin, int col, int d)
173 {
174 int i,j,k;
175 if((lin-0>n)||(lin-d+1<1)||(col<1)||(col+d-1>n)) return false;
176 boolean gol=true;
177 for(k=0;k<d;k++)
178 {
179 i=lin-k;
180 for(j=col+k;j<col+d;j++) if(a[i][j]!=-1){ gol=false;break;}
181 }
182 return gol;
183 }
184
185 static void umple1(int lin, int col, int d) // 12=js==>dir=1
186 {
187 int i,j,k;
188 for(k=0;k<d;k++)
189 {
190 i=lin-k;
191 for(j=col-k;j<=col;j++) a[i][j]=val;
192 }
193 }
194
195 static boolean esteGol1(int lin, int col, int d)
196 {
197 int i,j,k;
198 if((lin-0>n)||(lin-d+1<1)||(col-d+1<1)||(col>n)) return false;
199 boolean gol=true;
200 for(k=0;k<d;k++)
201 {
202 i=lin-k;
203 for(j=col-k;j<=col;j++) if(a[i][j]!=-1) {gol=false; break;}
204 }
205 return gol;
206 }
207
208 static void umple2(int lin, int col, int d) // 12=ds==>dir=2
209 {
210 int i,j,k;
211 for(k=0;k<d;k++)
212 {
213 i=lin+k;
214 for(j=col-d+1;j<=col-k;j++) a[i][j]=val;
215 }
216 }
217
218 static boolean esteGol2(int lin, int col, int d)
219 {
220 int i,j,k;
221 if((lin<1)||(lin+d-1>n)||(col-d+1<1)||(col>n)) return false;
222 boolean gol=true;
223 for(k=0;k<d;k++)
224 {
225 i=lin+k;
226 for(j=col-d+1;j<=col-k;j++) if(a[i][j]!=-1){ gol=false;break;}
227 }
228 return gol;
229 }
230
231 static void umple3(int lin, int col, int d) // 12=sj==>dir=3
232 {
233 int i,j,k;
234 for(k=0;k<d;k++)
235 {
236 i=lin+k;
237 for(j=col;j<=col+k;j++) a[i][j]=val;
238 }
CAPITOLUL 35. ONI 2005 765

239 }
240
241 static boolean esteGol3(int lin, int col, int d)
242 {
243 int i,j,k;
244 if((lin<1)||(lin+d-1>n)||(col<1)||(col+d-1>n)) return false;
245 boolean gol=true;
246 for(k=0;k<d;k++)
247 {
248 i=lin+k;
249 for(j=col;j<=col+k;j++) if(a[i][j]!=-1) {gol=false; break;}
250 }
251 return gol;
252 }
253 }

35.6 Pal
Autor: Silviu G nceanu
Prinµul Algorel este în încurc tur  din nou: a fost prins de Spânul cel Negru în încercarea
sa de a o salva pe prinµes  ³i acum este închis în Turnul cel Mare.
Algorel poate evada dac  g se³te combinaµia magic  cu care poate deschide poarta turnului.
Prinµul ³tie cum se formeaz  aceast  combinaµie magic : trebuie s  utilizeze toate cifrele scrise
pe u³a turnului pentru a obµine dou  numere palindroame, astfel încât suma lor s  e minim , iar
aceast  sum  este combinaµia magic  ce va deschide u³a.
Primul num r palindrom trebuie s  aib  cel puµin L cifre, iar cel de-al doilea poate avea orice
lungime diferit  de 0. Numerele palindroame formate nu pot începe cu cifra 0. Acum interveniµi
dumneavoastr  în poveste, ind prietenul s u cel mai priceput în algoritmi.
Prin noul super-telefon al s u, prinµul transmite num rul de apariµii a ec rei cifre de pe u³a
turnului precum ³i lungimea minim  L a primului num r, iar dumneavoastr  trebuie s -i trimiteµi
cât mai repede numerele cu care poate obµine combinaµia magic .
Cerinµ 
Având datele necesare, aaµi dou  numere palindroame cu care se poate obµine combinaµia
magic .
Date de intrare
Prima linie a ³ierului pal.in conµine un num r întreg L reprezentând lungimea minim  a
primului num r. Urmeaz  10 linii: pe linia i  2 se va aa un num r întreg reprezentând num rul
de apariµii ale cifrei i, pentru i cu valori de la 0 la 9.
Date de ie³ire
Prima linie a ³ierului de ie³ire pal.out conµine primul num r palidrom, iar cea de-a doua
linie conµine cel de-al doilea num r palindrom. Dac  exist  mai multe soluµii se va scrie doar una
dintre ele.
Restricµii ³i preciz ri
În total vor  cel mult 100 de cifre
a
1 & L $ 100 ³i L va  mai mic decât num rul total de cifre
a
a Pentru datele de test va exista întotdeauna soluµie: se vor putea forma din cifrele scrise pe
u³a turnului dou  numere care încep cu o cifr  diferit  de 0, iar primul num r s  aib  cel puµin L
cifre
a Un num r este palindrom dac  el coincide cu r sturnatul s u. De exemplu 12321 ³i 7007
sunt numere palindroame, în timp ce 109 ³i 35672 nu sunt.
a Pentru 30% dintre teste, num rul total de cifre va  cel mult 7; pentru alte 40% din teste
num rul total de cifre va  cel mult 18, iar pentru restul de 30% din teste num rul total de cifre
va  mai mare sau egal cu 30.
a Fiecare linie din ³ierul de intrare ³i din ³ierul de ie³ire se termin  cu marcaj de sfâr³it de
linie.
Exemplu
CAPITOLUL 35. ONI 2005 766

pal.in pal.out Explicaµie


5 10001 Pentru acest exemplu avem L 5,
3 222 3 cifre de 0, 2 cifre de 1³i 3 cifre de 2.
2 Cifrele de la 3 la 9 lipsesc
3 de pe u³a turnului.
0
0 Cele dou  palindroame cu care
0 se genereaz  combinaµia magic 
0 sunt 10001 ³i 222.
0 Combinaµia magic  va  suma acestora
0 ³i anume 10223 (care este suma minim 
0 pe care o putem obµine).
Timp maxim de execuµie/test: 1 sec sub Windows ³i 1 sec sub Linux

35.6.1 Indicaµii de rezolvare

Soluµia ocial , Silviu G nceanu


Problema se rezolv  utilizând tehnica greedy. Not m num rul total de cifre cu N C . Se observ 
c , pentru ca suma celor dou  numere palindroame s  e minim , trebuie ca lungimea maxim  a
celor dou  numere s  e cât mai mic . A³adar, pentru început, se stabile³te lungimea exact  a
primului num r (care va  cel mai lung), în funcµie de L în modul urm tor:
1. dac  L $ N C ©2, atunci lungimea primului palindrom va  aleas  astfel încât cele dou 
numere s  e cât mai apropiate ca lungime
2. dac  L % N C ©2 apar cazuri particulare ³i se stabile³te dac  lungimea primului palindrom
cre³te sau nu cu o unitate.
Având lungimile numerelor stabilite putem merge mai departe utilzând strategia greedy, obser-
vând c  cifrele mai mici trebuie s  ocupe poziµii cât mai semnicative. Pentru a realiza acest lucru
se vor completa în paralel cele dou  numere cu cifrele parcurse în ordine cresc toare stabilind în
care din cele dou  numere se vor poziµiona. De asemenea, trebuie avut în vedere ca niciunul din
cele dou  numere s  nu înceap  cu cifra 0.
Datele de test au fost construite astfel încât ³i soluµii neoptime s  obµin  puncte, gradat, în
funcµie de optimiz rile efectuate asupra implement rii.

35.6.2 *Cod surs 

35.6.3 Rezolvare detaliat 

Listing 35.6.1: pal.java


1 import java.io.*; // la inceput cifre mici in p1
2 class Pal // apoi in ambele in paralel!
3 {
4 static int L;
5 static int NC=0;
6 static int nc1,nc2;
7 static int ncfi=0; // nr cifre cu frecventa impara
8 static int[] fc=new int[10]; // frecventa cifrelor
9 static int[] p1; // palindromul 1
10 static int[] p2; // palindromul 2
11
12 public static void main(String []args) throws IOException
13 {
14 int i;
15 StreamTokenizer st=new StreamTokenizer(
16 new BufferedReader(new FileReader("pal.in")));
17 PrintWriter out = new PrintWriter (
18 new BufferedWriter( new FileWriter("pal.out")));
19
20 st.nextToken();L=(int)st.nval;
21 for(i=0;i<=9;i++) { st.nextToken(); fc[i]=(int)st.nval;}
22
23 for(i=0;i<=9;i++) NC+=fc[i]; // nr total cifre
CAPITOLUL 35. ONI 2005 767

24 for(i=0;i<=9;i++) ncfi=ncfi+(fc[i]%2); // nr cifre cu frecventa impara


25
26 nc1=L;
27 nc2=NC-nc1;
28 while(nc1<nc2) {nc1++; nc2--;}
29 if((ncfi==2)&&(nc1%2==0)) {nc1++; nc2--;}
30
31 p1=new int[nc1];
32 p2=new int[nc2];
33 switch(ncfi)
34 {
35 case 0: impare0(); break;
36 case 1: impare1(); break;
37 case 2: impare2(); break;
38 default: System.out.println("Date de intrare eronate!");
39 }
40 corectez(p1);
41 corectez(p2);
42
43 for(i=0;i<p1.length;i++) out.print(p1[i]);
44 out.println();
45 for(i=0;i<p2.length;i++) out.print(p2[i]);
46 out.println();
47 int[] p12=suma(p1,p2);// pentru ca in teste rezultat = suma!
48 for(i=p12.length-1;i>=0;i--) out.print(p12[i]);
49 out.println();
50 out.close();
51 }//main
52
53 static void impare0() // cea mai mare cifra la mijloc
54 {
55 int i,k1,k2;
56 int imp1=nc1/2, imp2=nc2/2;
57
58 if(nc1%2==1) // numai daca nc1 si nc2 sunt impare !
59 for(i=9;i>=0;i--)
60 if(fc[i]>0)
61 {
62 p1[imp1]=i; fc[i]--;
63 p2[imp2]=i; fc[i]--;
64 break;
65 }
66 k1=0;
67 k2=0;
68 while(k1<nc1-nc2) {incarcPozitia(k1,p1); k1++;}// incarc numai p1
69 while((k1<imp1)||(k2<imp2))
70 {
71 if(k1<imp1) {incarcPozitia(k1,p1); k1++;}
72 if(k2<imp2) {incarcPozitia(k2,p2); k2++;}
73 }
74 }
75
76 static void impare1()
77 {
78 int i,k1,k2;
79 int imp1=nc1/2, imp2=nc2/2;
80
81 for(i=0;i<=9;i++)
82 if(fc[i]%2==1) { p1[imp1]=i; fc[i]--; break;}
83 for(i=0;i<=9;i++)
84 if(fc[i]%2==1) { p2[imp2]=i; fc[i]--; break;}
85
86 k1=0;
87 k2=0;
88 while(k1<nc1-nc2) {incarcPozitia(k1,p1); k1++;} // incarc numai p1
89 while((k1<imp1)||(k2<imp2))
90 {
91 if(k1<imp1) {incarcPozitia(k1,p1); k1++;}
92 if(k2<imp2) {incarcPozitia(k2,p2); k2++;}
93 }
94 }
95
96 static void impare2()
97 {
98 int i,k1,k2;
99 int imp1=nc1/2, imp2=nc2/2;
CAPITOLUL 35. ONI 2005 768

100
101 for(i=0;i<=9;i++)
102 if(fc[i]%2==1) { p1[imp1]=i; fc[i]--; break;}
103 for(i=0;i<=9;i++)
104 if(fc[i]%2==1) { p2[imp2]=i; fc[i]--; break;}
105
106 k1=0;
107 k2=0;
108 while(k1<nc1-nc2) {incarcPozitia(k1,p1); k1++;}// incarc numai p1
109 while((k1<imp1)||(k2<imp2))
110 {
111 if(k1<imp1) { incarcPozitia(k1,p1); k1++; }
112 if(k2<imp2) { incarcPozitia(k2,p2); k2++; }
113 }
114 }
115
116 static void corectez(int[] x)
117 {
118 int pozdif0,val, n=x.length;
119 pozdif0=0;
120 while(x[pozdif0]==0) pozdif0++;
121 if(pozdif0>0)
122 {
123 val=x[pozdif0];
124 x[0]=x[n-1]=val;
125 x[pozdif0]=x[n-pozdif0-1]=0;
126 }
127 }
128
129 static void incarcPozitia(int k, int[] x)
130 {
131 int i;
132 int n=x.length;
133 for(i=0;i<=9;i++)
134 if(fc[i]>0)
135 {
136 x[k]=i; fc[i]--;
137 x[n-k-1]=i; fc[i]--;
138 break;
139 }
140 }
141
142 static int[] suma(int[] x, int[] y)
143 {
144 int[] z=new int[(x.length>y.length) ? (x.length+1) : (y.length+1)];
145 int k,t=0;
146 for(k=0;k<=z.length-2;k++)
147 {
148 z[k]=t;
149 if(k<x.length) z[k]+=x[k];
150 if(k<y.length) z[k]+=y[k];
151 t=z[k]/10;
152 z[k]=z[k]%10;
153 }
154 z[z.length-1]=t;
155 if(z[z.length-1]!=0) return z;
156 else
157 {
158 int[] zz=new int[z.length-1];
159 for(k=0;k<zz.length;k++) zz[k]=z[k];
160 return zz;
161 }
162 }
163 }//class
Capitolul 36

ONI 2004

Figura 36.1: Sigla ONI 2004

36.1 Coduri
Un detectiv particular are de rezolvat un caz special.
Este vorba de o deturnare de fonduri. Pentru a putea rezolva cazul trebuie s  g seasc  un ³ir
cu n coduri distincte. Fiecare cod este un num r natural scris în baza 10.
Din p cate lucrurile nu sunt simple, pentru c  din cercet rile efectuate a obµinut dou  infor-
maµii. Prima informaµie este legat  de faptul c  suma p tratelor codurilor este un cub perfect,
iar a doua spune c  suma cuburilor codurilor este un p trat perfect.
Cerinµ 
Ajutaµi detectivul s  g sesc  un ³ir de coduri x1 , x2 , ..., xn , care veric  condiµiile din enunµ ³i
xi & n14 , pentru orice i cu 1 & i & n.
Date de intrare
Fi³ierul de intrare coduri.in conµine pe prima linie num rul natural n.
Date de ie³ire
Fi³ierul de ie³ire coduri.out va conµine n linii, câte una pentru ecare cod din ³ir, în ordine
cresc toare.
Restricµii
a 1 & n & 20
Exemplu
coduri.in coduri.out
2 625
1250
Timp maxim de execuµie: 1 sec/test

36.1.1 Indicaµii de rezolvare

k s ,1&k & n.
4
Fie s n n  1 2n  1©6. Se pot considera codurile xk

769
CAPITOLUL 36. ONI 2004 770

36.1.2 Cod surs 

Listing 36.1.1: COD_OK.PAS


1 program coduri_cu_numere_mari;
2 type ve=array[1..300]of byte;
3
4 var n,i,m,j:longint;
5 f:text;
6 x,y,z,u,s,ii:ve;
7 ds,dx,dy,dz,di:byte;
8 er:integer;
9 sm,si:string;
10
11 procedure suma(x:ve;dx:byte;y:ve;dy:byte;var z:ve;var dz:byte);
12 var i,mi,dt,tr:byte;t:ve;
13 begin
14 if dx>dy then
15 begin
16 for i:=1 to dx-dy do
17 t[i]:=0;
18 dt:=dx-dy;
19 for i:=1 to dy do
20 begin
21 inc(dt);
22 t[dt]:=y[i];
23 end;
24 for i:=1 to dt do
25 y[i]:=t[i];
26 dy:=dx;
27 end
28 else
29 if dy>dx then
30 begin
31 for i:=1 to dy-dx do
32 t[i]:=0;
33 dt:=dy-dx;
34 for i:=1 to dx do
35 begin
36 inc(dt);
37 t[dt]:=x[i];
38 end;
39 for i:=1 to dt do
40 x[i]:=t[i];
41 dx:=dy;
42 end;
43 dt:=0;
44 mi:=0;
45 for i:=dx downto 1 do
46 begin
47 inc(dt);
48 tr:=x[i]+y[i]+mi;
49 t[dt]:=tr mod 10;
50 mi:=tr div 10;
51 end;
52 if mi>0 then
53 begin
54 inc(dt);
55 t[dt]:=mi;
56 end;
57 for i:=1 to dt do
58 z[i]:=t[dt+1-i];
59 dz:=dt;
60 end;{suma}
61
62 procedure produs(x:ve;dx:byte;y:ve;dy:byte;var z:ve;var dz:byte);
63 var i,j,k,dt,du:byte;t,u:ve;
64 begin
65 dz:=1;z[1]:=0;
66 for i:=1 to dy do
67 begin
68 t[1]:=0;dt:=1;
69 for j:=1 to y[dy-i+1] do
70 begin
CAPITOLUL 36. ONI 2004 771

71 suma(x,dx,t,dt,u,du);
72 dt:=du;
73 for k:=1 to du do t[k]:=u[k];
74 end;
75 for j:=1 to i-1 do
76 begin
77 inc(dt);
78 t[dt]:=0;
79 end;
80 suma(z,dz,t,dt,u,du);
81 dz:=du;
82 for j:=1 to du do z[j]:=u[j];
83 end;
84 end;{produs}
85
86 begin
87 assign(f,’coduri.in’);
88 reset(f);
89 readln(f,n);
90 close(f);
91 m:=n*(n+1)*(2*n+1) div 6;
92 str(m,sm);
93 ds:=length(sm);
94 for i:=1 to ds do
95 val(sm[i],s[i],er);
96 produs(s,ds,s,ds,x,dx);
97 s:=x;ds:=dx;
98 produs(s,ds,s,ds,x,dx);
99 s:=x;ds:=dx;
100
101 assign(f,’coduri.out’);
102 rewrite(f);
103 for i:=1 to n do
104 begin
105 str(i,si);
106 di:=length(si);
107 for j:=1 to di do
108 val(si[j],ii[j],er);
109 produs(ii,di,s,ds,x,dx);
110 for j:=1 to dx do
111 write(f,x[j]);
112 writeln(f);
113 end;
114 close(f);
115 end.

Listing 36.1.2: CODURI.PAS


1 program coduri;
2 var n,i:integer;
3 s:extended;
4 f:text;
5
6 begin
7 assign(f,’coduri.in’);
8 reset(f);
9 readln(f,n);
10 close(f);
11 s:=n*(n+1)*(2*n+1)/6;
12 s:=s*s*s*s;
13 assign(f,’coduri1.out’);
14 rewrite(f);
15 for i:=1 to n do
16 writeln(f,s*i:0:0);
17 close(f);
18 end.

36.1.3 Rezolvare detaliat 

Codurile x1 , x2 , ..., xn trebuie s  îndeplineasc  urm toarele condiµii:


2 2 2 3 3 3 3 2
x1  x2  ...  xn a ³i x1  x2  ...  xn b
CAPITOLUL 36. ONI 2004 772

unde a ³i b sunt ni³te numere naturale.


Aceste relaµii ne fac s  ne gândim la formulele:

2 2 2 n n  1 2n  1 3 3 3 n n  1 2
1  2  ...  n ³i 1  2  ...  n  
6 2

³i ne sugereaz  s  consider m codurile sub forma xk α k.


Pentru suma cuburilor obµinem

3 3 3 3 n n  1 2
x1  x2  ...  xn α  
2
2
³i pentru a obµine un p trat perfect vom considera α β , deci codurile vor  considerate sub
2
forma xk β k .
Acum suma cuburilor
3 3 3 6 n n  1 2
x1  x2  ...  xn β  
2
este un p trat perfect.
Suma p tratelor codurilor este

2 2 2 4n n  1 2n  1 3 n n  1 2n  1
x1  x2  ...  xn β β β
6 6
³i pentru a obµine un cub perfect putem considera

n n  1 2n  1 2
β   .
6

Astfel, putem considera codurile sub forma

n n  1 2n  1 4
xk k   .
6

Listing 36.1.3: coduri.java


1 import java.io.*;
2 class Coduri
3 {
4 public static void main(String[] args) throws IOException
5 {
6 int n;
7 int k;
8 long s;
9 long[] x;
10 PrintWriter out = new PrintWriter(
11 new BufferedWriter(new FileWriter("coduri.out")));
12 StreamTokenizer st=new StreamTokenizer(
13 new BufferedReader(new FileReader("coduri.in")));
14 st.nextToken(); n=(int)st.nval;
15 x=new long[n+1];
16 s=n*(n+1)*(2*n+1)/6;
17 s=s*s*s*s;
18 for(k=1;k<=n;k++) x[k]=k*s;
19 for(k=1;k<=n;k++) out.println(x[k]);
20 out.close();
21 }
22 }

36.2 Logic
Demonstrarea automat  a teoremelor ³i vericarea satisabilit µii unei formule constituie dou 
capitole importante în cadrul logicii matematice.
Formulele propoziµionale sunt alc tuite din variabile propoziµionale (variabile care pot lua doar
dou  valori: sau adev rat sau fals) ³i din operatorii logici ³i, sau, negaµie, echivalent, implic .
CAPITOLUL 36. ONI 2004 773

Iat  câteva exemple de formule propoziµionale:


~p& q $ % p % q
p¶q $ % ~p&~q
p
p % q % a % t % ~p
În acest exemplu, p ³i q sunt variabilele propoziµionale, ~ este operatorul unar negaµie, & este
operatorul binar ³i, ¶ este operatorul binar sau, % este implicaµia logic  (³i apare numai în acest
sens, nu ³i $ ), iar $ % este echivalenµa logic .
În plus, într-o formul  propoziµional  pot s  apar  ³i paranteze care stabilesc ordinea operaµi-
ilor. În lipsa parantezelor, operatorii în ordinea priorit µii lor sunt & ¶ % $ %.
În formulele de forma "A1 opA2 op...bf opAK " asociaµiile se fac de la dreapta la stânga (adic 
"A1 op A2 op ...opAK ..."), unde op este unul dintre &, ¶, % sau $ % ³i Ai sunt formule
propoziµionale, cu i de la 1 la K .
În general, o formul  propoziµional  se dene³te astfel:
 orice variabil  propoziµional  este formul  propoziµional 
 dac  A ³i B sunt formule propoziµionale, atunci A, ~A, A&B , A¶B , A % B, A $ % B
sunt formule propoziµionale.
Dac  înlocuim într-o formul  propoziµional  toate variabilele cu valori de adev r (adev rat sau
fals),
obµinem o armaµie.
Valoarea de adev r a unei armaµii este dat  de urm toarea deniµie:
 dac  armaµia const  dintr-o singur  valoare de adev r,
armaµia ia valoarea respectiv 
 dac  A ³i B sunt armaµii, atunci:
a A este adev rat  dac  ³i numai dac  valoarea sa de adev r
este adev rat
a A este adev rat  dac  ³i numai dac  este adev rat  A
a ~A este fals  dac  ³i numai dac  A este adev rat 
a A&B este adev rat  dac  ³i numai dac  atât A cât ³i B
sunt adev rate
a A¶B este fals  dac  ³i numai dac  A este fals ³i B este fals
a A %B este adev rat  dac  ³i numai dac  ~A¶B este adev rat 
a A$ %B este adev rat  dac  ³i numai dac  A % B & B % A
este adev rat .
Se nume³te soluµie a formulei propoziµionale P (formul  în care apar numai variabilele propo-
ziµionale distincte A1 , A2 , ..., AN ) orice N -uplu
v1 , v2 , ..., vN 
(cu vi valori de adev r) pentru care înlocuind ecare variabil  Ai cu valoarea vi , armaµia rezultat 
este adev rat .
Cerinµ 
Logica ind un obiect nesuferit de studenµii de la informatic , ei apeleaz  la informaticienii din
clasa a IX-a pentru a-i ajuta s  numere câte soluµii distincte are o formul  propoziµional  dat .
Date de intrare
În ³ierul de intrare logic.in se g se³te o formul  propoziµional  unde variabilele propoziµionale
sunt reprezentate de litere mici ale alfabetului englez.
Date de ie³ire
În ³ierul de ie³ire logic.out se va a³a num rul de soluµii pentru formula propoziµional  din
³ierul de intrare, urmat de caracterul sfâr³it de linie.
Restricµii ³i preciz ri
a La intrare se va da întotdeauna o formul  propoziµional  corect  sintactic
a Formula are mai puµin de 232 de caractere
a În formul  nu apar mai mult de 10 litere mici ale alfabetului latin
Exemplu:
logic.in logic.out
p¶q $ % p& q 4
A 1
CAPITOLUL 36. ONI 2004 774

Timp execuµie: 1 sec/test.

36.2.1 Indicaµii de rezolvare

Informaµia "în formul  nu apar mai mult de 10 litere mici ale alfabetului latin" ne conduce
10
la ideea de a genera toate conguraµiile (sunt cel mult 2 1024) ³i a calcula valoarea de
adev r a formulei pentru ecare conguraµie. Se folose³te recursivitatea µinând cont de priorit µile
operatorilor.

36.2.2 Cod surs 

Listing 36.2.1: logic.c


1 #include <stdio.h>
2 #include <string.h>
3
4 char e[256];
5
6 int nv, pos;
7 char c[26];
8 int t[256];
9
10 int echivalenta(void);
11
12 int not(void)
13 {
14 if (e[pos] == ’~’)
15 {
16 pos++;
17 return !not();
18 }
19 else if (e[pos] >= ’a’ && e[pos] <= ’z’)
20 {
21 return t[(int)e[pos++]];
22 }
23 else if (e[pos] == ’(’)
24 {
25 int v;
26
27 pos++;
28 v = echivalenta();
29 pos++;
30 return v;
31 }
32
33 return 0; // SHOULD NEVER HAPPEN; KEEPS COMPILER HAPPY
34 }
35
36 int si(void)
37 {
38 int value = not(), v;
39
40 while (e[pos] == ’&’)
41 {
42 pos++;
43 v = not();
44 value = value && v;
45 }
46 return value;
47 }
48
49 int sau(void)
50 {
51 int value = si(), v;
52
53 while (e[pos] == ’|’)
54 {
55 pos++;
56 v = sau();
57 value = value || v;
CAPITOLUL 36. ONI 2004 775

58 }
59 return value;
60 }
61
62 int implicatie(void)
63 {
64 int value = sau(), v;
65
66 while (e[pos] == ’=’)
67 {
68 pos += 2;
69 v = implicatie();
70 value = (!value) || v;
71 }
72 return value;
73 }
74
75 int echivalenta(void)
76 {
77 int value = implicatie(), v;
78
79 while (e[pos] == ’<’)
80 {
81 pos += 3;
82 v = echivalenta();
83 value = value == v;
84 }
85 return value;
86 }
87
88 int main(void)
89 {
90 FILE *fi, *fo;
91 int len, result;
92 int i, j;
93 int a[256];
94
95 memset(e, 0, sizeof(e));
96 fi = fopen("logic.in", "r");
97 fscanf(fi, "%s", e);
98 len = strlen(e);
99 fclose(fi);
100
101 memset(a, 0, sizeof(a));
102 for (i = 0; i < len; i++) a[(int)e[i]]++;
103 nv = 0;
104 for (i = ’a’; i <= ’z’; i++)
105 if (a[i]) c[nv++] = i;
106
107 result = 0;
108 for (i = 0; i < (1 << nv); i++)
109 {
110 for (j = 0; j < nv; j++)
111 t[(int)c[j]] = i & (1 << j) ? 1 : 0;
112 pos = 0;
113 result += echivalenta();
114 }
115
116 fo = fopen("logic.out", "w");
117 fprintf(fo, "%d\n", result);
118 fclose(fo);
119
120 return 0;
121 }

36.2.3 Rezolvare detaliat 

O vizualizare a codurilor operatorilor:

Listing 36.2.2: logic0.java


1 import java.io.*;
2 class Logic0
CAPITOLUL 36. ONI 2004 776

3 {
4 static char[] e; // expresia
5
6 public static void main(String[]args) throws IOException
7 {
8 int i,n;
9 PrintWriter out = new PrintWriter(
10 new BufferedWriter(new FileWriter("logic.out")));
11 BufferedReader br=new BufferedReader(new FileReader("logic.in"));
12
13 e=br.readLine().toCharArray();
14 n=e.length;
15 for(i=0;i<n;i++) System.out.print(e[i]);
16 System.out.println();
17
18 System.out.println("~ "+(int)’~’);
19 System.out.println("& "+(int)’&’);
20 System.out.println("| "+(int)’|’);
21 System.out.println("< "+(int)’<’);
22 System.out.println("= "+(int)’=’);
23 System.out.println("> "+(int)’>’);
24 System.out.println("a "+(int)’a’);
25 System.out.println("z "+(int)’z’);
26 out.close();
27 }//main
28 }//class

Soluµia cu mesaje de urm rire a execuµiei:

Listing 36.2.3: logic1.java


1 import java.io.*; // prioritati: variabila ( ~ & | => <=> (descrescator!)
2 class Logic1 // la egalitate de la dreapta la stanga (recursivitate)
3 {
4 static char[] e; // expresia
5 static char[] c=new char[10]; // codurile variabilelor din expresie
6 static boolean[] v=new boolean[127]; // valoarea logica a variabilelor
7 static int nv=0; // nr variabile in expresie
8 static int poz; // poz in expresie
9 static int n; // lungime expresie
10
11 public static void main(String[]args) throws IOException
12 {
13 int resultat=0, i, j;
14 int[] f=new int[127]; // frecventa caracterelor
15
16 PrintWriter out = new PrintWriter(
17 new BufferedWriter(new FileWriter("logic.out")));
18 BufferedReader br=new BufferedReader(new FileReader("logic.in"));
19 e=br.readLine().toCharArray();
20 n=e.length;
21 for(i=0;i<n;i++) f[e[i]]++;
22 for(i=’a’;i<=’z’;i++) if(f[i]>0) c[nv++]=(char)i;
23 for(i=0;i<(1<<nv);i++) // 1<<nv este 2^{nv}
24 {
25 for(j=0;j<nv;j++) v[(int)c[j]]=((i&(1<<j))>0)?true:false;
26 poz=0;
27 resultat+=(formula()?1:0);
28 System.out.println("----------------- ");
29 }
30 out.println(resultat);
31 out.close();
32 }//main
33
34 static boolean formula()
35 {
36 System.out.println(" --> formula "+poz+" "+e[poz]);
37 boolean val;
38 val=echivalenta();
39 if(poz<n) System.out.println("<-- formula "+poz+" "+e[poz]);
40 else System.out.println("<-- formula "+poz);
41 return val;
42 }
43
44 static boolean echivalenta()
45 {
CAPITOLUL 36. ONI 2004 777

46 System.out.println(" --> echivalenta "+poz+" "+e[poz]);


47 boolean a,b,val;
48 a=implicatie();
49 val=a;
50 if((poz<n)&&(e[poz]==’<’)) {poz+=3; b=formula(); val=(a==b);}
51 if(poz<n) System.out.println("<-- echivalenta "+poz+" "+e[poz]);
52 else System.out.println("<-- echivalenta "+poz);
53 return val;
54 }
55
56 static boolean implicatie()
57 {
58 System.out.println(" --> implicatie "+poz+" "+e[poz]);
59 boolean a, b, val;
60 a=sau();
61 val=a;
62 if((poz<n)&&(e[poz]==’=’)) {poz+=2; b=implicatie(); val=(!a)||b;}
63 if(poz<n) System.out.println("<-- implicatie "+poz+" "+e[poz]);
64 else System.out.println("<-- implicatie "+poz);
65 return val;
66 }
67
68 static boolean sau()
69 {
70 System.out.println(" --> sau "+poz+" "+e[poz]);
71 boolean a, b, val;
72 a=si();
73 val=a;
74 if((poz<n)&&(e[poz]==’|’)) {poz++; b=sau(); val=(a||b);}
75 if(poz<n) System.out.println("<-- sau "+poz+" "+e[poz]);
76 else System.out.println("<-- sau "+poz);
77 return val;
78 }
79
80 static boolean si()
81 {
82 System.out.println(" --> si "+poz+" "+e[poz]);
83 boolean a, b, val;
84 a=not();
85 val=a;
86 if((poz<n)&&(e[poz]==’&’)) {poz++; b=si(); val=(a&&b);}
87 if(poz<n) System.out.println("<-- si "+poz+" "+e[poz]);
88 else System.out.println("<-- si "+poz);
89 return val;
90 }
91
92 static boolean not()
93 {
94 boolean val;
95 System.out.println(" --> not "+poz+" "+e[poz]);
96 if(e[poz]==’~’) {poz++; val=!not();}
97 else val=paranteza();
98 if(poz<n) System.out.println("<-- not "+poz+" "+e[poz]);
99 else System.out.println("<-- not "+poz);
100 return val;
101 }
102
103 static boolean paranteza()
104 {
105 System.out.println(" --> paranteza "+poz+" "+e[poz]);
106 boolean val;
107 if(e[poz]==’(’) {poz++;val=formula(); poz++;}
108 else if(e[poz] == ’)’) val=false;
109 else val=variabila();
110 if(poz<n) System.out.println("<-- paranteza "+poz+" "+e[poz]);
111 else System.out.println("<-- paranteza "+poz);
112 return val;
113 }
114
115 static boolean variabila()
116 {
117 System.out.println(" --> variabila "+poz+" "+e[poz]);
118 boolean val;
119 if((poz<n)&&(e[poz]>=’a’)&&(e[poz]<=’z’)) val=v[(int)e[poz++]];
120 else val=formula();
121 if(poz<n) System.out.println("<-- variabila "+poz+" "+e[poz]);
CAPITOLUL 36. ONI 2004 778

122 else System.out.println("<-- variabila "+poz);


123 return val;
124 }
125 }//class

Listing 36.2.4: logic2.java


1 import java.io.*; // prioritati: variabila ( ~ & | => <=> (descrescator!)
2 class Logic1 // la egalitate de la dreapta la stanga (recursivitate)
3 {
4 static char[] e; // expresia
5 static char[] c=new char[10]; // codurile variabilelor din expresie
6 static boolean[] v=new boolean[127]; // valoarea logica a variabilelor
7 static int nv=0; // nr variabile in expresie
8 static int poz; // poz in expresie
9 static int n; // lungime expresie
10
11 public static void main(String[]args) throws IOException
12 {
13 int resultat=0, i, j;
14 int[] f=new int[127]; // frecventa caracterelor
15
16 PrintWriter out = new PrintWriter(
17 new BufferedWriter(new FileWriter("logic.out")));
18 BufferedReader br=new BufferedReader(new FileReader("logic.in"));
19 e=br.readLine().toCharArray();
20 n=e.length;
21 for(i=0;i<n;i++) f[e[i]]++;
22 for(i=’a’;i<=’z’;i++) if(f[i]>0) c[nv++]=(char)i;
23 for(i=0;i<(1<<nv);i++) // 1<<nv este 2^{nv}
24 {
25 for(j=0;j<nv;j++) v[(int)c[j]]=((i&(1<<j))>0)?true:false;
26 poz=0;
27 resultat+=(formula()?1:0);
28 }
29 out.println(resultat);
30 out.close();
31 }//main
32
33 static boolean formula()
34 {
35 boolean val;
36 val=echivalenta();
37 return val;
38 }
39
40 static boolean echivalenta()
41 {
42 boolean a,b,val;
43 a=implicatie();
44 val=a;
45 if((poz<n)&&(e[poz]==’<’)) {poz+=3; b=formula(); val=(a==b);}
46 return val;
47 }
48
49 static boolean implicatie()
50 {
51 boolean a, b, val;
52 a=sau();
53 val=a;
54 if((poz<n)&&(e[poz]==’=’)) {poz+=2; b=implicatie(); val=(!a)||b;}
55 return val;
56 }
57
58 static boolean sau()
59 {
60 boolean a, b, val;
61 a=si();
62 val=a;
63 if((poz<n)&&(e[poz]==’|’)) {poz++; b=sau(); val=(a||b);}
64 return val;
65 }
66
67 static boolean si()
68 {
CAPITOLUL 36. ONI 2004 779

69 boolean a, b, val;
70 a=not();
71 val=a;
72 if((poz<n)&&(e[poz]==’&’)) {poz++; b=si(); val=(a&&b);}
73 return val;
74 }
75
76 static boolean not()
77 {
78 boolean val;
79 if(e[poz]==’~’) {poz++; val=!not();}
80 else val=paranteza();
81 return val;
82 }
83
84 static boolean paranteza()
85 {
86 boolean val;
87 if(e[poz]==’(’) {poz++;val=formula(); poz++;}
88 else if(e[poz] == ’)’) val=false;
89 else val=variabila();
90 return val;
91 }
92
93 static boolean variabila()
94 {
95 boolean val;
96 if((poz<n)&&(e[poz]>=’a’)&&(e[poz]<=’z’)) val=v[(int)e[poz++]];
97 else val=formula();
98 return val;
99 }
100 }//class

36.3 Poligon
Se d  un caroiaj de M  N în care sunt plasate K puncte. Fiecare punct poate  legat de
vecinul s u direct pe maxim opt direcµii (N , N E , E , SE , S , SV , V , N V ).
Cerinµ 
Determinaµi patrulaterele având vârfurile în punctele date iar laturile formate din leg turi între
dou  sau mai multe puncte coliniare.
Date de intrare
Fi³ierul de intrare poligon.in conµine
a pe prima linie trei numere naturale nenule, separate prin câte un spaµiu,
M N K
reprezentând dimensiunile M , N ale caroiajului ³i K num rul de puncte, iar
a pe urm toarele K linii câte trei numere naturale separate printr-un spaµiu,
Ii Ji Vi
reprezentând coordonatele punctului i, 1 & i & K respectiv direcµiile
spre care este legat de vecini direcµi.
Codicarea direcµiilor se face printr-un num r cuprins între 0 ³i 255.
Reprezentarea binar  a acestuia pe 8 cifre reprezint , începând de la stânga
spre dreapta, leg tur  pe direcµiile (1 - legatur , 0 - nu ):
N N E E SE S SV V N V .
De exemplu: 1 0 0 0 0 1 1 0 = 134 deci leg turi spre N , SV , V
Date de ie³ire
Fi³ierul de ie³ire poligon.out conµine numai num rul natural
npol
reprezentând num rul patrulaterelor.
Restricµii
1 $ M, N & 100
4 & K & 50
CAPITOLUL 36. ONI 2004 780

Exemplu
POLIGON.IN POLIGON.OUT
449 6
1 1 24
2 1 184
2 2 43
2 3 22
3 1 136
3 2 213
345
4 1 192
4 3 65

Figura 36.2: Poligon2

Timp de execuµie: 2 sec/test

36.3.1 Indicaµii de rezolvare

Se genereaz  toate combin rile de câte 4 puncte ³i se veric  dac  acestea pot porma un
patrulater (tinând cont de direcµii).

36.3.2 Cod surs 

Listing 36.3.1: poligon.pas


1 type mat=array[0..100,0..100] of byte;
2 sir=array[0..50] of integer;
3 dir=array[1..8] of byte;
4 vecini=array[1..8] of byte;
5
6 const d:dir=(128,64,32,16,8,4,2,1);
7 x1:vecini=(1,2,3,4,1,0,0,0);
8 x2:vecini=(1,2,4,3,1,0,0,0);
9 x3:vecini=(1,3,2,4,1,0,0,0);
10
11 var n,m,k,l,npol,nr:integer;
12 x,e:sir;
13 a:mat;
14 f:text;
15 time:longint absolute $000:$046C;
16 timeinit:longint;
17
18 Procedure citire(var a:mat;var m,n,l:integer);
19 var i,j,k,v:byte;
20 f:text;
21 begin
22 assign(f,’poligon.in’);
23 reset(f);
24 readln(f,m,n,l);
25 for k:=1 to l do
26 begin
27 readln(f,i,j,v);
CAPITOLUL 36. ONI 2004 781

28 a[i,j]:=v;
29 e[k]:=(i-1)*m+j;
30 end;
31 close(f);
32 end;
33
34 Function traseu(vi,vf,i1,c1,i2:byte;c2:integer;c:byte):boolean;
35 var aux,i:byte;
36 begin
37 if vi>vf then begin aux:=vi;vi:=vf;vf:=aux end;
38 traseu:=true;
39 for i:=vi to vf-1 do
40 if a[i1+c1*i,i2+c2*i] and d[c] <> d[c] then
41 traseu:=false
42 end;
43
44 Function verif(var x:sir; p:integer):boolean;
45 var i,j,l1,c1,l2,c2,t,nc,nl,ndp,nds:byte;
46 begin
47 if p=1 then verif:=true
48 else
49 begin
50
51 verif:=true;
52 nc:=0;nl:=0;ndp:=0;nds:=0;
53 l1:=(e[x[p]]-1) div n +1;
54 c1:=(e[x[p]]-1) mod n +1;
55 for i:=1 to p-1 do
56 begin
57 l2:=(e[x[i]]-1) div n +1;
58 c2:=(e[x[i]]-1) mod n +1;
59 if (c1=c2) then inc(nc);
60 if (l1=l2) then inc(nl);
61 if l1-l2=c1-c2 then inc(ndp);
62 if l1-l2=c2-c1 then inc(nds);
63 end;
64 if (nc>1) or (nl>1) or (ndp>1) or (nds>1) then verif:=false;
65 end;
66 end;
67
68 Function este(y:vecini):boolean;
69 var i,l1,c1,l2,c2,min,max:byte;
70 cod:boolean;
71 begin
72 este:=true;
73 for i:=1 to k do
74 begin
75 cod:=false;
76 l1:=(e[x[y[i+1]]]-1) div n +1;
77 c1:=(e[x[y[i+1]]]-1) mod n +1;
78 l2:=(e[x[y[i]]]-1) div n +1;
79 c2:=(e[x[y[i]]]-1) mod n +1;
80 min:=trunc(((l1+l2)-abs(l1-l2))/2);
81 if (c1=c2) and traseu(l1,l2,0,1,c1,0,5) then cod:=true;
82 if (l1=l2) and traseu(c1,c2,l1,0,0,1,3) then cod:=true;
83 if (l1-l2=c1-c2) and
84 traseu(0,abs(c1-c2),min,1,trunc(((c1+c2)-abs(c1-c2))/2),1,4)
85 then cod:=true;
86 if (l1-l2=c2-c1)
87 and traseu(0,abs(c1-c2),min,1,trunc(((c1+c2)+abs(c1-c2))/2),-1,6)
88 then cod:=true;
89 if not cod then este:=false;
90 end;
91 end;
92
93 Procedure Poligon(x:sir;n:integer);
94 var i:byte;
95 begin
96 if este(x1) or este(x2) or este(x3) then
97 begin
98 inc(nr);
99 {for i:=1 to n do
100 write(f,e[x[i]],’,’);
101 writeln(f);}
102 end;
103 end;
CAPITOLUL 36. ONI 2004 782

104
105 procedure comb (p:integer);
106 var i:integer;
107 begin
108 for i:=x[p-1]+1 to l do
109 begin
110 x[p]:=i;
111 if verif(x,p) then
112 if p=k then
113 Poligon(x,k)
114 else
115 comb(p+1);
116 end;
117 end;
118
119 begin
120 {writeln(’Incepem’);
121 timeinit:=time;
122 writeln(time);}
123 nr:=0;
124 assign(f,’poligon.out’);
125 rewrite(f);
126 k:=4;
127 citire(a,m,n,l);
128 comb(1);
129 writeln(f,nr);
130 close(f);
131 {writeln(time,’/’,(timeinit-time)/18:4:2); }
132 end.

36.3.3 Rezolvare detaliat 

Listing 36.3.2: poligon.java


1 import java.io.*;
2 class Poligon
3 {
4 static int m,n,k,nsol=0;
5 static int[] x, y, d;
6 static int[] a=new int[5];
7 static int[][] ePunctIn=new int[101][101]; // =i ==> e punctul i
8 // =0 ==> nu e punct acolo
9 public static void main(String[] args) throws IOException
10 {
11 int i,j,ymax;
12 long t1,t2;
13 t1=System.nanoTime();
14 PrintWriter out=new PrintWriter(
15 new BufferedWriter(new FileWriter("poligon.out")));
16 StreamTokenizer st=new StreamTokenizer(
17 new BufferedReader(new FileReader("poligon.in")));
18 st.nextToken(); m=(int)st.nval;
19 st.nextToken(); n=(int)st.nval;
20 st.nextToken(); k=(int)st.nval;
21
22 x=new int[k+1];
23 y=new int[k+1];
24 d=new int[k+1];
25
26 for(i=1;i<=k;i++)
27 {
28 st.nextToken(); y[i]=(int)st.nval; // linia
29 st.nextToken(); x[i]=(int)st.nval; // coloana
30 st.nextToken(); d[i]=(int)st.nval; // directia
31 }
32
33 ymax=y[1];
34 for(i=2;i<=k;i++) if(y[i]>ymax) ymax=y[i];
35 for(i=1;i<=k;i++) y[i]=ymax-y[i]+1; // sa fie "normal"!
36
37 for(i=1;i<=k;i++) ePunctIn[y[i]][x[i]]=i;
38
CAPITOLUL 36. ONI 2004 783

39 generezCombinariPePozitia(1);
40
41 out.println(nsol);
42 out.close();
43 t2=System.nanoTime();
44 System.out.println("Timp = "+((double)(t2-t1))/1000000000);
45 }
46
47 static void generezCombinariPePozitia(int j)
48 {
49 int i;
50 for(i=a[j-1]+1;i<=k-4+j;i++)
51 {
52 a[j]=i;
53 if(j<4) generezCombinariPePozitia(j+1);
54 else if(ePoligonOK()) nsol++;
55 }
56 }
57
58 static boolean ePoligonOK()
59 {
60 if(coliniare3Puncte()) return false;
61 if(ePoligon(a[1],a[2],a[3],a[4])) return true;
62 if(ePoligon(a[1],a[2],a[4],a[3])) return true;
63 if(ePoligon(a[1],a[3],a[2],a[4])) return true;
64 if(ePoligon(a[1],a[3],a[4],a[2])) return true;
65 if(ePoligon(a[1],a[4],a[2],a[3])) return true;
66 if(ePoligon(a[1],a[4],a[3],a[2])) return true;
67 return false;
68 }
69
70 static boolean coliniare3Puncte()
71 {
72 if(coliniare(a[1],a[2],a[3])) return true;
73 if(coliniare(a[1],a[2],a[4])) return true;
74 if(coliniare(a[1],a[3],a[4])) return true;
75 if(coliniare(a[2],a[3],a[4])) return true;
76 return false;
77 }
78
79 static boolean coliniare(int p1, int p2, int p3)
80 {
81 int s;
82 s=x[p1]*y[p2]+x[p2]*y[p3]+x[p3]*y[p1];
83 s=s-y[p1]*x[p2]-y[p2]*x[p3]-y[p3]*x[p1];
84 if(s==0) return true; else return false;
85 }
86
87 static boolean ePoligon(int p1, int p2, int p3, int p4)
88 {
89 if(!eLinie(p1,p2)) return false;
90 if(!eLinie(p2,p3)) return false;
91 if(!eLinie(p3,p4)) return false;
92 if(!eLinie(p4,p1)) return false;
93 if(eNedegenerat(p1,p2,p3,p4)) return true; else return false;
94 }
95
96 static boolean eLinie(int p1, int p2) // trece prin coordonate intregi!
97 {
98 if(Math.abs(x[p1]-x[p2])==Math.abs(y[p1]-y[p2])) return eLinieOkOblica(p1,p2);
99 else if(x[p1]==x[p2]) return eLinieOkVerticala(p1,p2);
100 else if(y[p1]==y[p2]) return eLinieOkOrizontala(p1,p2);
101 else return false;
102 }
103
104 static boolean eLinieOkOrizontala(int p1, int p2)
105 {
106 int i;
107 if(x[p1]>x[p2]) {i=p1;p1=p2;p2=i;} // p1 ... p2
108
109 for(i=x[p1]+1; i<=x[p2]; i++)
110 {
111 if(ePunctIn[y[p1]][i]==0) return false;
112 else if((d[ePunctIn[y[p1]][i]]&(1<<1))==0) // linie spre V
113 return false;
114 }
CAPITOLUL 36. ONI 2004 784

115 return true;


116 }
117
118 static boolean eLinieOkVerticala(int p1, int p2)
119 {
120 int i;
121 if(y[p1]>y[p2]) {i=p1;p1=p2;p2=i;} // p1 ... p2
122
123 for(i=y[p1]+1; i<=y[p2]; i++)
124 if(ePunctIn[i][x[p1]]==0) return false;
125 else if((d[ePunctIn[i][x[p1]]]&(1<<3))==0) // linie spre S
126 return false;
127 return true;
128 }
129
130 static boolean eLinieOkOblica(int p1, int p2)
131 {
132 int i,j,pasy,dir;
133 if(x[p1]>x[p2]) {i=p1;p1=p2;p2=i;} // p1 ... p2
134
135 if(y[p1]>y[p2]) {pasy=-1; dir=0;} // NV
136 else {pasy=+1; dir=2;} // SV
137
138 i=y[p1];
139 for(j=x[p1]+1; j<=x[p2]; j++)
140 {
141 i=i+pasy;
142 if(ePunctIn[i][j]==0) return false;
143 else if((d[ePunctIn[i][j]]&(1<<dir))==0) // linie spre SV sau NV
144 return false;
145 }
146 return true;
147 }
148
149 static boolean eNedegenerat(int p1, int p2, int p3, int p4)
150 {
151 // daca nu se intersecteaza p1p4 cu p2p3
152 if(!seIntersecteaza(x[p1],y[p1],x[p4],y[p4],x[p2],y[p2],x[p3],y[p3]))
153 return true; else return false;
154 }
155
156 static boolean seIntersecteaza( int x1,int y1,int x2,int y2,
157 int x3,int y3,int x4,int y4)
158 {
159 // daca se intersecteaza segmentele [p1p2] cu [p3p4]
160 // p3 si p4 sunt in semiplane diferite fata de dreapta (p1p2) si
161 // p1 si p2 sunt in semiplane diferite fata de dreapta (p3p4)
162 return (s(x3,y3,x1,y1,x2,y2)*s(x4,y4,x1,y1,x2,y2)<0) &&
163 (s(x1,y1,x3,y3,x4,y4)*s(x2,y2,x3,y3,x4,y4)<0);
164 }
165
166 static int s(int xp,int yp,int xa,int ya,int xb,int yb)
167 {
168 //de ce parte a dreptei ((xa,ya);(xb,yb)]) se afla (xp,yp)
169 double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya;
170 if(s<-0.001) return -1;
171 else if(s>0.001) return 1;
172 else return 0;
173 }
174 }

36.4 “ablon
Gigel ³i Vasilic  imagineaz  un mod de a transmite mesaje pe care nimeni s  nu le poat 
descifra. Mesajul este ascuns într-un text care are N linii ³i pe ecare linie sunt exact N caractere
- litere mari ale alfabetului englez, cifre, semne de punctuaµie ³i caracterul spaµiu.
Decodicarea se face cu ajutorul unui ³ablon, de acelea³i dimensiuni ca ³i textul, care are
câteva g uri.
Suprapunând ³ablonul peste text r mân vizibile câteva caractere.
Acestea se citesc în ordinea liniilor, de sus în jos, iar pe aceea³i linie de la stânga la dreapta.
CAPITOLUL 36. ONI 2004 785

o
Apoi hârtia cu textul se rote³te spre stânga, în sens trigonometric, cu 90 , ³ablonul r mânând
x. Alte caractere devin vizibile ³i acestea se citesc în acela³i mod.
o o
Operaµia se repet  de înc  dou  ori (rotire cu 180 , respectiv cu 270 ), pân  când textul
o
ajunge, printr-o nou  rotaµie cu 90 , din nou în poziµia iniµial .
Din p cate, ³ablonul pentru codicare/decodicare s-a pierdut. În schimb a r mas la Gigel
mesajul iniµial iar la Vasilic  a ajuns textul care conµine mesajul.
Cerinµ 
S  se reconstituie ³ablonul care a fost folosit la codicare.
Date de intrare
Fi³ierul de intrare sablon.in conµine
a pe prima linie, mesajul iniµial
a pe linia a doua a ³ierului de intrare se g se³te valoarea N
a urm toarele N linii conµin textul care ascunde mesajul.

Date de ie³ire
Fi³ierul de ie³ire sablon.out conµine N linii a câte N caractere. Caracterele sunt 'O' (pentru
reprezentarea unei g uri) ³i 'X'.
Restricµii ³i
a prin rotirea textului nici una din g uri nu se va suprapune peste nici una
din poziµiile ocupate de o gaur  în poziµiile precedente ale textului
a 1 & N & 50
a mesajul are maxim 1000 caractere ³i se încheie cu un caracter diferit de
spaµiu
a în cazul în care exist  mai multe soluµii, a³aµi una dintre ele

Exemplu
sablon.in sablon.out
CODIFICARE CU SABLON XXXXOXXXXX
10 XXOXXXXXXX
ABCDCEFAGH OXXXXXXXXX
IJOKLEMNOP XXXOXXXXXX
DQRSTUVWCX XOXXXXXXXX
YZAIBCRDEF XXXXXXXXXX
GFHIJKLMNI XXXXXXXXXX
AJKLMNOPSQ XXXXXXXXXX
RSTOUV WXY XXXXXXXXXX
ZBABCDEFGU XXXXXXXXXX
HIJKNLMCNO
PQLRS TUVW
Timp de execuµie: 1 sec/test

36.4.1 Indicaµii de rezolvare

Soluµia ocial 
Problema se rezolv  relativ simplu µinând cont de urm toarele observaµii:
1. Deoarece întregul mesaj a fost codicat prin 4 rotiri ale textului, este clar c  la o poziµionare a
textului sub ³ablon pot  citite Lung M esaj ©4 caractere, deci întregul mesaj are 4˜N umarGauri
caractere
2. Ca urmare a observaµiei de la punctul 1, mesajul poate  împartit exact în 4 ³iruri de
lungimi egale M esaj1, ..., M esaj4
3. Dac  o gaur  se a  în poziµia T i, j  din ³ablon, ei îi corespund poziµiile

ˆ T j, N  i  1 la rotire cu 90 grade

ˆ T N  i  1, N  j  1 la rotire cu 180 grade


ˆ T N  j  1, i la rotire cu 270 grade
de unde deducem c  nu e nevoie s  rotim textul!!!
CAPITOLUL 36. ONI 2004 786

4. Dac  lungimea unui ³ir este L4 (vezi în surs ), este sucient s  parcurgem numai primul
din cele 4 ³iruri cu un Index. Atunci, parcurgând textul care ascunde mesajul, în poziµia i, j 
exist  o gaur  în ³ablon dac  ³i numai dac  toate cele 4 caractere

M esaj1Index, M esaj2Index, M esaj3Index, M esaj4Index

coincid cu cele 4 caractere obµinute prin rotire (vezi observaµia 3)


5. "Cel mai bun pseudocod este... PASCAL-ul", deci: ... (urmeaz  sursa în Pascal).

36.4.2 Cod surs 

Listing 36.4.1: sablon.pas


1 Program DeterminareSablon;
2 Const DimMax = 50; { lungimea maxima a textului }
3 Type Gauri = Array [1..DimMax, 1..DimMax] Of Boolean;
4 Textul = Array [1..DimMax, 1..DimMax] Of Char;
5
6 Var G, OK : Gauri;
7 T : Textul;
8 Mesaj : Array [1..4] Of String;
9 N, L4 : Integer;
10 NrGauri: Integer;
11
12 Procedure Afisare;
13 Var i, j: Integer;
14 f : Text;
15 Begin
16 Assign(f, ’sablon.out’); ReWrite(f);
17 If NrGauri<L4 Then
18 FillChar(G, SizeOf(G), False);
19 For i := 1 To N Do
20 Begin
21 For j := 1 To N Do
22 If G[i,j] Then
23 Write(f, ’O’)
24 Else
25 Write(f, ’X’);
26 Writeln(f);
27 End;
28 Close(f);
29 End;
30
31 Procedure Prelucrare;
32 Var Index, i, j, Lung, Cadran, N2: Integer;
33 Begin
34 FillChar(G, SizeOf(G), False);
35 FillChar(OK, SizeOf(OK), True);
36 NrGauri := 0;
37 Index := 1;
38 For i := 1 To N Do
39 {parcurg cu Index-ul Textul pana la length(mesaj)/4 - pentru 4 rotiri}
40 For j := 1 To N Do
41 Begin
42 If OK[i][j] Then
43 If (Mesaj[1][Index] = T[i,j]) And {primul caracter}
44 (Mesaj[2][Index] = T[j,N-i+1]) And {rotit cu 90Ãÿ}
45 (Mesaj[3][Index] = T[N-i+1,N-j+1]) And {rotit cu 180Ãÿ}
46 (Mesaj[4][Index] = T[N-j+1,i]) {rotit cu 270Ãÿ}
47 Then
48 Begin
49 Inc(NrGauri);
50 G[i,j] := True; OK[i,j] := False; OK[j,N-i+1] := False;
51 OK[N-i+1,N-j+1] := False; OK[N-j+1,i] := False;
52 Inc(Index);
53 If Index > L4 Then exit {div 4 pentru 4 rotiri}
54 End;
55 End
56 End;
57
58 Procedure Citire;
59 Var f : Text;
CAPITOLUL 36. ONI 2004 787

60 i, j: Integer;
61 Lung: Integer;
62 M : Array[1..1000] Of Char;
63 Begin
64 Assign(f, ’sablon.in’); reset (f);
65 Lung := 0;
66 FillChar(M, SizeOf(M), #0);
67 While Not Eoln(f) Do {citesc mesajul in sirul de caractere}
68 Begin {determinandu-i lungimea}
69 Inc(Lung);
70 Read(f, M[Lung]);
71 End;
72 ReadLn(f, N); {citesc N}
73 For i := 1 To N Do {citesc textul in T}
74 Begin
75 For j := 1 To N Do
76 Read(f, T[i,j]);
77 Readln(f);
78 End;
79 Close(f);
80 If Lung Mod 4<>0 Then
81 WriteLn(’Mesajul nu are lungimea corecta’)
82 Else
83 Begin
84 L4 := Lung Div 4; {determin numarul de caractere la o rotire}
85 For i := 1 To L4 Do {si impart mesajul in 4 parti egale}
86 Begin
87 Mesaj[1][i] := M[i];
88 Mesaj[2][i] := M[L4+i];
89 Mesaj[3][i] := M[2*L4+i];
90 Mesaj[4][i] := M[3*L4+i]
91 End
92 End
93 End;
94
95 Begin
96 Citire;
97 Prelucrare;
98 Afisare;
99 End.

36.4.3 Rezolvare detaliat 


Vericarea prelu rii corecte a datelor de intrare:

Listing 36.4.2: sablon0.java


1 import java.io.*;
2 class Sablon0
3 {
4 static int n;
5 static String mesaj; // mesajul initial
6 static char[][] text; // text (codificarea)
7 static PrintWriter out;
8
9 public static void main(String[] args) throws IOException
10 {
11 int i,j;
12 out=new PrintWriter(new BufferedWriter(new FileWriter("sablon.out")));
13 BufferedReader br=new BufferedReader(new FileReader("sablon.in"));
14 StreamTokenizer st=new StreamTokenizer(br);
15
16 mesaj=br.readLine(); System.out.println(mesaj);
17 st.nextToken(); n=(int)st.nval; System.out.println(n);
18 text=new char[n][n];
19 br.readLine(); // citeste CR LF adica 0D 0A adica 13 10
20 for(i=0;i<n;i++) text[i]=br.readLine().toCharArray();
21 for(i=0;i<n;i++)
22 {
23 for(j=0;j<n;j++) System.out.print(text[i][j]);
24 System.out.println();
25 }
26 out.close();
27 }
CAPITOLUL 36. ONI 2004 788

28 }

Listing 36.4.3: sablon1.java


1 import java.io.*;
2 class Sablon1
3 {
4 static int n;
5 static String mesajInitial; // mesajul initial
6 static char[][] text; // text (codificarea)
7 static boolean[][] gaura;
8
9 static PrintWriter out;
10 static BufferedReader br;
11 static StreamTokenizer st;
12
13 public static void main(String[] args) throws IOException
14 {
15 citire();
16 rezolvare();
17 afisare();
18 }
19
20 static void citire() throws IOException
21 {
22 int i,j;
23 br=new BufferedReader(new FileReader("sablon.in"));
24 st=new StreamTokenizer(br);
25 mesajInitial=br.readLine();
26 st.nextToken(); n=(int)st.nval;
27 text=new char[n][n];
28 gaura=new boolean[n][n];
29 br.readLine(); // citeste CR LF adica 0D 0A adica 13 10
30 for(i=0;i<n;i++) text[i]=br.readLine().toCharArray();
31 }
32
33 static void rezolvare()
34 {
35 int i,j,k;
36 int nrGauri=mesajInitial.length()/4;
37 char[][] mesajPartial=new char[4][nrGauri]; // 4 mesaje partiale
38 for(i=0;i<nrGauri;i++) // impart mesajul in 4 parti egale
39 {
40 mesajPartial[0][i]=mesajInitial.charAt(i);
41 mesajPartial[1][i]=mesajInitial.charAt(nrGauri+i);
42 mesajPartial[2][i]=mesajInitial.charAt(2*nrGauri+i);
43 mesajPartial[3][i]=mesajInitial.charAt(3*nrGauri+i);
44 }
45
46 k=0; // gaurile 0, 1, ..., nrGauri-1
47 for(i=0;i<n;i++)
48 for(j=0;j<n;j++)
49 if( (mesajPartial[0][k]==text[i][j]) && // primul caracter
50 (mesajPartial[1][k]==text[j][n-i-1]) && // rotit cu 90 grade
51 (mesajPartial[2][k]==text[n-i-1][n-j-1]) && // rotit cu 180 grade
52 (mesajPartial[3][k]==text[n-j-1][i])) // rotit cu 270 grade
53 { // daca toate 4 caractere coincid
54 gaura[i][j]=true;
55 k++;
56 if(k>=nrGauri) return;
57 }
58 }
59
60 static void afisare() throws IOException
61 {
62 int i,j;
63 out=new PrintWriter(new BufferedWriter(new FileWriter("sablon.out")));
64 for(i=0;i<n;i++)
65 {
66 for(j=0;j<n;j++)
67 if(gaura[i][j]) out.print(’O’); else out.print(’X’);
68 out.println();
69 }
70 out.close();
71 }
CAPITOLUL 36. ONI 2004 789

72 }

36.5 “ir
Gigel se distreaz  construind ³iruri cresc toare de numere din mulµimea r1, 2, ..., nx. La un
moment dat observ  c  unele ³iruri, de cel puµin k termeni (k ' 3), au o proprietate mai aparte:
diferenµa dintre doi termeni consecutivi este constant . Iat  câteva exemple de astfel de ³iruri
pentru n ' 21:
2,3,4
1,5,9,13
7,10,13,16,19,21
Cerinµ 
Dându-se num rul natural n ajutaµi-l pe Gigel s  numere câte astfel de ³iruri poate s  con-
struiasc .
Date de intrare
În ³ierul de intrare sir.in se g se³te, pe prima linie, num rul n.
Date de ie³ire
În ³ierul de ie³ire sir.out se va a³a, pe prima linie, num rul cerut urmat de caracterul sfâr³it
de linie.
Restricµii:
a 3 & n & 20000
a 3&k&n
Exemple:
sir.in sir.out
3 1
4 3
5 7
Timp execuµie: 1 sec/test

36.5.1 Indicaµii de rezolvare

Soluµia ocial 
Notând cu r diferenta dintre doi tereni consecutivi constat m c  pentru r 1 se pot construi
urmatoarele submulµimi, ³iruri cu proprietea cerut , de lungime 3:

r1, 2, 3x, r2, 3, 4x, ..., rn  2, n  1, nx.

Cele de lungime superioar  se construiesc ad ugând elemente pe cele deja obµinute. Num rul
<
n2
lor va  i 1 i.
Similar pentru r 2 obµinem urm toarele submulµimi, ³iruri de lungime 3:

r1, 3, 5x, r2, 4, 6x, ..., rn  4, n  2, nx sau rn  5, n  3, n  1x functie de paritatea lui n .

Cele de lungime superioar  se construiesc ad ugând elemente pe acestea. Numarul lor este o
sum  te tipul precedent.
Se continu  astfel pân  la r n©2, valoarea maxim  a lui r.

36.5.2 Cod surs 

Listing 36.5.1: sir.pas


1 program sir;
2 var f,g:text;
3 r,n,i:longint;
4 s:longint;
CAPITOLUL 36. ONI 2004 790

5 begin
6 assign(f,’sir.in’);
7 assign(g,’sir.out’);
8 reset(f);rewrite(g);
9 s:=0;
10 readln(f,n);
11 for r:= 1 to n div 2 do
12 begin
13 i:= n-2*r;
14 while i>0 do
15 begin
16 s:=s+i;
17 i:= i-r;
18 end;
19 end;
20 writeln(g,s);
21 Close(g)
22 end.

36.5.3 Rezolvare detaliat 

Listing 36.5.2: sir.java


1 import java.io.*;
2 class sir
3 {
4 public static void main(String []args) throws IOException
5 {
6 int ns=0,n=19999,r,k,i;
7 StreamTokenizer st=new StreamTokenizer(
8 new BufferedReader(new FileReader("sir.in")));
9 PrintWriter out=new PrintWriter(new BufferedWriter(
10 new FileWriter("sir.out")));
11 st.nextToken(); n=(int)st.nval;
12 ns=0;
13 for(r=1;r<=(n-1)/2;r++)
14 for(k=3;k<=(n-1+r)/r;k++)
15 ns=ns+n-(k-1)*r;
16 System.out.println(ns);
17 out.println(ns);
18 out.close();
19 }
20 }

36.6 Snipers
Se spune c  în timpul r zboiului cu gnomii, trolii au trimis n tr g ori de elit  s  lichideze cele
n c petenii inamice.
Din fericire c peteniile inamice erau plasate în câmp deschis, iar tr g torii au reu³it s  se
plaseze în zon  f r  s  e observaµi.
Când s  e dat  comanda de tragere s-a constatat c  nu se transmisese ec rui tr g tor ce
c petenie s  împu³te, iar dac  doi tr g tori ar  tras în aceea³i c petenie sau traiectoriile razelor
uciga³e s-ar  intersectat, atunci ar  sc pat cel puµin o c petenie care ar  putut duce r zboiul
pân  la cap t, iar trolii ar  fost învin³i.
Deoarece c peteniile aveau capacitatea de a deveni invizibile oricând doreau (pe o perioad 
nelimitat ), trebuiau lichidate simultan, altfel ...
Istoria ne spune c  trolii au învins deoarece comandantul lor a reu³i ca în mai puµin de o
secund  s  transmit  ec rui tr g tor în ce c petenie s  trag .
Voi puteµi face asta?
Cerinµ 
Scrieµi un program care, citind poziµiile tr g torilor ³i a c peteniilor, determin  c petenia în
care trebuie s  trag  ecare tr g tor.
Date de intrare
CAPITOLUL 36. ONI 2004 791

Fi³ierul de intrare snipers.in conµine


a pe prima sa linie num rul n
a pe urm toarele n linii se a  perechi de numere întregi, separate prin spaµiu, ce reprezint 
coordonatele tr g torilor urmate de
a alte n perechi de numere întregi ce reprezint  coordonatele c peteniilor (abscis  ³i ordonat ).

Date de ie³ire
Fi³ierul de ie³ire snipers.out conµine n linii.
Pe linia i a ³ierului se a  num rul c peteniei µintite de tr g torul i
(i 1...n).
Restricµii ³i preciz ri
a 0 $ n $ 200
a Coordonatele sunt numere întregi din intervalul [0, 50000]
a Raza uciga³  a oric rei arme se opre³te în µinta sa.
a În datele de intrare nu vor exista trei persoane aate în puncte coliniare.
Exemple
snipers.in snipers.out snipers.in snipers.out
2 1 5 2
1 3 2 6 6 5
1 1 4 12 1
3 4 2 8 3
3 1 9 4 4
5 2
6 11
9 7
3 9
1 4
7 3
Timp de execuµie: 1 sec/test

36.6.1 Indicaµii de rezolvare

Soluµia ocial 
La început ec rui tragator i îi asociem capetenia i, dup  care vom lua în considerare toate
perechile tr g tor-c petenie ³i vom elimina încruci³ rile razelor, la câte dou  perechi prin in-
terschimbarea µintelor tr g torilor.

Figura 36.3: Snipers1s

Se vor face interschimb ri între câte dou  perechi tr g tor-µint  pâna când nu vor mai exista
intersecµii.
Se observ  c  la eliminarea unei intersecµii suma distanµelor dintre tr g tori ³i µinte scade, ceea
ce asigur  nitudinea algoritmului.

36.6.2 Cod surs 

Listing 36.6.1: snipers.pas


1 program snipers;
2 const nmax=1000;
3 type coord=record
CAPITOLUL 36. ONI 2004 792

4 x,y,k:longint;{coord. si pe cine kill-ere}


5 end;
6 var t,c:array[1..nmax] of coord;{tragatori si capetenii}
7 n:integer;
8
9 procedure citire;
10 var f:text;i:integer;
11 begin
12 fillchar(t,sizeof(t),0);
13 fillchar(c,sizeof(c),0);
14 assign(f,’snipers.in’);reset(f);
15 readln(f,n);
16 for i:=1 to n do
17 with t[i] do readln(f,x,y);
18 for i:=1 to n do
19 with c[i] do readln(f,x,y);
20 close(f);
21 end;
22
23 procedure scrie;
24 var f:text;i:integer;
25 begin
26 assign(f,’snipers.out’);rewrite(f);
27 for i:=1 to n do
28 writeln(f,t[i].k);
29 close(f);
30 end;
31
32 {ne spune de ce parte a dreptei ce trece prin a, b se afla punctul p}
33 function s(p,a,b:coord):real;
34 begin
35 s:=1.0*p.y*(b.x-a.x)-1.0*p.x*(b.y-a.y)+1.0*a.x*b.y-1.0*b.x*a.y;
36 end;
37
38 {testeaza daca segmentul[a1,b1] se inersecteaza cu [a2,b2]}
39 function inter(a1,b1,a2,b2:coord):boolean;
40 begin
41 inter:=(s(a2,a1,b1)*s(b2,a1,b1)<0)and
42 (s(a1,a2,b2)*s(b1,a2,b2)<0);
43 {a2 si b2 se afla de o parte si de alta a lui [a1,b1]
44 si
45 a1 si b1 se afla de o parte si de alta a lui [a2,b2] }
46 end;
47
48 procedure rezolva;
49 var i,j,aux:word; e_int,ok:boolean;
50 begin
51 for i:=1 to n do t[i].k:=i;
52
53 {tragatorul i trage in capetenia i, pentru inceput}
54 if n>1 then
55 repeat
56 ok:=true;{am gasit o combinatie valida tragator-capetenie}
57 i:=1;
58
59 repeat
60 j:=i;
61
62 repeat
63 j:=j+1;
64 {verifica daca traiectoria de la sniper i la tinta
65 sa nu se intersecteaza
66 cu traiectoria de la sniper j la tinta sa }
67 e_int:=inter(t[i],c[t[i].k],t[j],c[t[j].k]);
68 {daca da le interschimbam tintele,
69 si notam ca nu avem o combinatie buna}
70 if e_int then
71 begin
72 aux:=t[i].k;
73 t[i].k:=t[j].k;
74 t[j].k:=aux;
75 ok:=false;
76 end;
77 until (j=n)or e_int;
78
79 if not e_int then inc(i);
CAPITOLUL 36. ONI 2004 793

80 {daca nu jeneaza pe numeni trecem al urmatorul sniper}


81
82 until i=n;{pana terminam lista de tragatori}
83
84 until ok;
85 end;
86
87 BEGIN
88 citire;
89 rezolva;
90 scrie;
91 END.

36.6.3 Rezolvare detaliat 

Atenµie: valoarea expresiei yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya poate dep ³i capaci-


tatea de înregistrare a tipului int.

Listing 36.6.2: snipers.java


1 import java.io.*;
2 class Snipers
3 {
4 static int n;
5 static int[] xt, yt, xc, yc, t, c;
6
7 static PrintWriter out;
8 static StreamTokenizer st;
9
10 public static void main(String[] args) throws IOException
11 {
12 citire();
13 rezolvare();
14 afisare();
15 }
16
17 static void citire() throws IOException
18 {
19 int k;
20 st=new StreamTokenizer(new BufferedReader(new FileReader("snipers.in")));
21 st.nextToken(); n=(int)st.nval;
22 xt=new int[n+1];
23 yt=new int[n+1];
24 xc=new int[n+1];
25 yc=new int[n+1];
26 t=new int[n+1]; // tragator --> capetenie
27
28 for(k=1;k<=n;k++)
29 {
30 st.nextToken(); xt[k]=(int)st.nval;
31 st.nextToken(); yt[k]=(int)st.nval;
32 }
33 for(k=1;k<=n;k++)
34 {
35 st.nextToken(); xc[k]=(int)st.nval;
36 st.nextToken(); yc[k]=(int)st.nval;
37 }
38 }
39
40 static void rezolvare() throws IOException
41 {
42 int i,j,aux;
43 boolean seIntersecteaza_ij,ok;
44 for(i=1;i<=n;i++) t[i]=i; // tragatorul i trage in capetenia i
45 if(n==1) return;
46 do
47 {
48 ok=true; // am gasit o combinatie valida tragator-capetenie
49 i=1;
50 do
51 {
52 j=i;
53 do
CAPITOLUL 36. ONI 2004 794

54 {
55 j=j+1;
56 seIntersecteaza_ij=seIntersecteaza(xt[i],yt[i],xc[t[i]],yc[t[i]],
57 xt[j],yt[j],xc[t[j]],yc[t[j]]);
58 if(seIntersecteaza_ij)
59 {
60 aux=t[i];t[i]=t[j];t[j]=aux; // interschimbam tintele
61 ok=false; // nu avem combinatie buna
62 }
63 } while ((j!=n) && !seIntersecteaza_ij);
64 if(!seIntersecteaza_ij) i++; // trecem la urmatorul sniper
65 } while(i!=n); // pana terminam lista de tragatori
66 } while(!ok);
67 }
68
69 //de ce parte a dreptei [(xa,ya);(xb,yb)] se afla (xp,yp)
70 static int s(int xp,int yp,int xa,int ya,int xb,int yb)
71 {
72 double s=(double)yp*(xb-xa)-xp*(yb-ya)+xa*yb-xb*ya;
73 if(s<-0.001) return -1;
74 else if(s>0.001) return 1;
75 else return 0;
76 }
77
78 // testeaza daca segmentul[a1,b1] se intersecteaza cu [a2,b2]
79 static boolean seIntersecteaza(int xa1,int ya1,int xb1,int yb1,
80 int xa2,int ya2,int xb2,int yb2)
81 {
82 // a2 si b2 se afla de o parte si de alta a lui [a1,b1] si
83 // a1 si b1 se afla de o parte si de alta a lui [a2,b2]
84 return (s(xa2,ya2,xa1,ya1,xb1,yb1)*s(xb2,yb2,xa1,ya1,xb1,yb1)<0) &&
85 (s(xa1,ya1,xa2,ya2,xb2,yb2)*s(xb1,yb1,xa2,ya2,xb2,yb2)<0);
86 }
87
88 static void afisare() throws IOException
89 {
90 int k;
91 out=new PrintWriter(new BufferedWriter(new FileWriter("snipers.out")));
92 for(k=1;k<=n;k++) out.println(t[k]);
93 out.close();
94 }
95 }
Capitolul 37

ONI 2003

Figura 37.1: Sigla ONI 2003

37.1 Seti
Cercet torii ce lucreaz  la programul SETI au recepµionat dou  transmisii de date foarte
ciudate, date care ar putea veni din partea unor civilizaµii extraterestre. Primul set de date
este format din 10 caractere distincte, date în ordinea lor lexicograc , ce formeaz  alfabetul
extraterestru. A doua transmisie conµine cuvinte din exact 4 caractere.
Cerinµ 
Cercet torii trebuie s  ordoneze lexicograc cuvintele primite în a doua transmisie (conform
alfabetului extraterestru).
Date de intrare
Fi³ierul de intrare seti.in conµine pe prima linie cele 10 caractere ale alfabetului, iar pe ecare
din urm toarele linii câte un cuvânt.
Date de ie³ire
Fi³ierul de ie³ire seti.out va conµine cuvintele ordonate, câte unul pe linie.
Restricµii ³i preciz ri
a În ³ier nu sunt mai mult de 200.000 de cuvinte, iar caracterele sunt literele mici ale alfabe-
tului englez.
a Datele de intrare se presupun ca ind corecte.

Exemplu
seti.in seti.out
abcdefghij aaaa
aaaa aabc
fgaa fgaa
aabc iihf
iihf
Timp de execuµie: 1 sec/test

795
CAPITOLUL 37. ONI 2003 796

37.1.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 13/6


Rezolvarea problemei const  în realizarea unei metode de trecere de la cuvintele de 4 litere
la numerele de cel mult 4 cifre. Având o astfel de metod , toate cuvintele se vor transforma în
numere cuprinse între 0 ³i 9999.
Numerele nu vor  memorate, în schimb se va folosi un tablou cu 10.000 de elemente, ecare
element reprezentând num rul de apariµii al unui cuvânt din ³ier.
În nal, pentru ecare num r de la 0 la 9999, se va a³a cuvântul reprezentat de atâtea ori de
câte indic  num rul s u de apariµii.
Pentru a trece de la cuvânt la num r ³i invers se poate folosi ca etap  intermediar  un tablou
de patru cifre, transform rile realizându-se foarte u³or.
Analiza complexit µii
Fie N num rul de cuvinte din ³ier care poate avea valoarea maxim  200.000.
Operaµia de citirea a datelor are ordinul de complexitate O N .
Metodele care transform  cuvintele în numere ³i invers au ordinul de complexitate O 1.
Actualizarea tabloului care contorizeaz  num rul de apariµii se face concomitent cu citirea,
deci aceast  operaµie are ordinul de complexitate O N .
Operaµia de a³area a datelor are ordinul de complexitate O N .
În nal, ordinul de complexitate al algoritmului de rezolvare este O N O N O N  O N .

37.1.2 Cod surs 

Listing 37.1.1: seti.pas


1 program seti;
2 var c:array[’a’..’z’] of byte;
3 n:array[0..9] of char;
4 s:array[0..9999] of longint;
5 f:text;i:byte;p:word;
6 x:string;k:longint;
7
8 begin
9 assign(f,’seti.in’);
10 reset(f);
11 readln(f,x);
12 for i:=0 to 9 do
13 begin
14 c[x[i+1]]:=i;
15 n[i]:=x[i+1];
16 end;
17 while not eof(f) do
18 begin
19 readln(f,x);
20 p:=c[x[1]]*1000+c[x[2]]*100+c[x[3]]*10+c[x[4]];
21 inc(s[p]);
22 end;
23 close(f);
24 assign(f,’seti.out’);
25 rewrite(f);
26 for p:=0 to 9999 do
27 for k:=1 to s[p] do
28 writeln(f,n[p div 1000]+n[(p div 100) mod 10]+
29 n[(p div 10) mod 10]+n[p mod 10]);
30 close(f);
31 end.

37.1.3 Rezolvare detaliat 

Versiune cu mesaje de vericare!

Listing 37.1.2: seti0java


1 import java.io.*;
CAPITOLUL 37. ONI 2003 797

2 class Seti1
3 {
4 static int[] codd=new int[10];
5 static int[] codi=new int[26];
6 static char[] caractercd=new char[10];
7 static int[] f=new int[10000];
8
9 public static void main(String[]args) throws IOException
10 {
11 String alfabet,cuvant;
12 int i,nr;
13 long t1,t2;
14 t1=System.currentTimeMillis();
15 PrintWriter out = new PrintWriter(
16 new BufferedWriter(new FileWriter("seti.out")));
17 StreamTokenizer st=new StreamTokenizer(
18 new BufferedReader(new FileReader("seti.in")));
19
20 st.nextToken(); alfabet=st.sval.toString();
21 System.out.println(alfabet);
22
23 for(i=0;i<10;i++)
24 {
25 codd[i]=alfabet.charAt(i)-’a’;
26 caractercd[i]=alfabet.charAt(i);
27 }
28 for(i=0;i<10;i++)
29 System.out.println(i+" : "+codd[i]+" "+caractercd[i]);
30 System.out.println();
31
32 for(i=’a’;i<=’z’;i++) codi[i-’a’]=-1;
33 for(i=0;i<10;i++) codi[codd[i]]=i;
34
35 for(i=0;i<=’z’-’a’;i++)
36 System.out.println(i+" : "+codi[i]+" "+(char)(i+’a’));
37 System.out.println();
38
39 while(st.nextToken()!=st.TT_EOF) // preluarea cuvantului
40 {
41 cuvant=st.sval.toString(); // conversia in String
42 nr=0;
43 for(i=0;i<4;i++) nr=nr*10+codi[cuvant.charAt(i)-’a’];
44 System.out.println(cuvant+" "+nr);
45 f[nr]++;
46 }
47
48 for(i=0;i<10000;i++)
49 if(f[i]>0)
50 {
51 cuvant=cuvantul(i);
52 for(nr=1;nr<=f[i];nr++) out.println(cuvant);
53 }
54 out.close();
55 t2=System.currentTimeMillis();
56 System.out.println("Timp = "+(t2-t1));
57 }//main
58
59 static String cuvantul(int nr)
60 {
61 int r,i;
62 char[] c=new char[4];
63 for(i=0;i<4;i++) c[i]=caractercd[0];
64
65 i=3;
66 while(nr!=0)
67 {
68 r=nr%10;
69 c[i]=caractercd[r];
70 nr=nr/10;
71 i--;
72 }
73 String s=new String(c);
74 return s;
75 }
76 }//class
CAPITOLUL 37. ONI 2003 798

Versiune nala!
Listing 37.1.3: seti1.java
1 import java.io.*;
2 class Seti2
3 {
4 static int[] codd=new int[10];
5 static int[] codi=new int[26];
6 static char[] caractercd=new char[10];
7 static int[] f=new int[10000];
8
9 public static void main(String[]args) throws IOException
10 {
11 String alfabet,cuvant;
12 int i,nr;
13 long t1,t2;
14 t1=System.currentTimeMillis();
15 PrintWriter out = new PrintWriter(
16 new BufferedWriter(new FileWriter("seti.out")));
17 StreamTokenizer st=new StreamTokenizer(
18 new BufferedReader(new FileReader("seti.in")));
19
20 st.nextToken(); alfabet=st.sval.toString();
21
22 for(i=0;i<10;i++)
23 {
24 codd[i]=alfabet.charAt(i)-’a’;
25 caractercd[i]=alfabet.charAt(i);
26 }
27
28 for(i=’a’;i<=’z’;i++) codi[i-’a’]=-1;
29 for(i=0;i<10;i++) codi[codd[i]]=i;
30
31 while(st.nextToken()!=st.TT_EOF) // preluarea cuvantului
32 {
33 cuvant=st.sval.toString(); // conversia in String
34 nr=0;
35 for(i=0;i<4;i++) nr=nr*10+codi[cuvant.charAt(i)-’a’];
36 f[nr]++;
37 }
38
39 for(i=0;i<10000;i++)
40 if(f[i]>0)
41 {
42 cuvant=cuvantul(i);
43 for(nr=1;nr<=f[i];nr++) out.println(cuvant);
44 }
45 out.close();
46 t2=System.currentTimeMillis();
47 System.out.println("Timp = "+(t2-t1));
48 }//main
49
50 static String cuvantul(int nr)
51 {
52 int i;
53 char[] c=new char[4];
54 for(i=0;i<4;i++) c[i]=caractercd[0];
55
56 i=3;
57 while(nr!=0)
58 {
59 c[i]=caractercd[nr%10];
60 nr=nr/10;
61 i--;
62 }
63 String s=new String(c);
64 return s;
65 }
66 }//class

37.2 Scaune
Se consider  ns scaune numerotate de la 1 la ns, aranjate în cerc.
CAPITOLUL 37. ONI 2003 799

Exemplu pentru ns 20 a³ezarea scaunelor este dat  în gur .


20 19 18 17 16 15 14 13 12 11
        
 
1 2 3 4 5 6 7 8 9 10
Pe ecare din aceste scaune este a³ezat un copil. Primul copil st  pe scaunul 1, iar ultimul
pe scaunul ns. Pe lâng  cele ns scaune deja ocupate, alµi nc copii (1 & nc & ns) a³teapt  s  se
elibereze un loc.
La un moment dat un singur copil se ridic  de pe scaun ³i pleac . Atunci, cât timp în dreptul
scaunului liber nu exist  un copil, toµi copiii aaµi în a³teptare se mi³c  în sens invers mi³c rii
acelor ceasornicului, câte o poziµie, pân  când unul din ei ocup  locul liber.
Condiµii:
 la început toate scaunele sunt ocupate;
 ecare copil aat în a³teptare se a  iniµial în dreptul unui scaun ocupat;
 când un copil avanseaz  cu n poziµii spre un loc pe scaun, toµi cei care a³teapt  avanseaz 
tot cu n poziµii. Deoarece mi³carea este circular , avansarea cu 4 poziµii de la poziµia 18, semnic 
o deplasare în dreptul poziµiei 2;
Cerinµ 
Dac  se d  o secvenµ  a numerelor de ordine a copiilor care pleac  la ecare moment, s  se
scrie un program care s  a³eze num rul scaunului pe care s-a a³ezat ecare copil care a³teapt ,
dac  acest lucru este posibil.
Date de intrare
a Pe prima linie a ³ierului text de intrare scaune.in se a  dou  numere, separate prin spaµiu,
reprezentând num rul de scaune, ns ³i respectiv num rul copiilor care stau în a³teptare nc.
a Pe urm toarele nc linii vor  date poziµiile copiilor aaµi în a³teptare.
a În continuare pân  la sfâr³itul ³ierului sunt linii ce descriu numerele de ordine ale copiilor
care se ridic  unul câte unul de pe scaune ³i p r sesc jocul.
Date de ie³ire
Fi³ierul de ie³ire scaune.out conµine nc linii, ecare linie conµinând poziµia iniµial  de a³tep-
tare a copilului ³i pozii a ocupat , separate printr-un spaµiu.
Liniile de ie³ire trebuie s  e în aceea³i ordine ca cele din ³ierul de intrare.
În cazul în care un copil nu are nici o posibilitate s  se a³eze, în dreptul s u se va scrie 0 în
³ierul de ie³ire.
Restricµii
a 1 & ns & 200
a nc & ns

Exemplu
scaune.in scaune.out
20 5 6 16
6 19 3
19 17 0
17 13 20
13 11
1
1
3
20
16
Timp maxim de execuµie: 1 secund /test

37.2.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 13/6


Rezolvarea acestei probleme const  în simularea deplas rii copiilor, conform regulilor din enunµ.
CAPITOLUL 37. ONI 2003 800

Deoarece limita pentru num rul de scaune este destul de mic , avansarea copiilor se poate
realiza pas cu pas; astfel, în momentul în care un scaun se elibereaz , ecare copil avanseaz  câte
o poziµie pân  când unul dintre ei ajunge în dreptul scaunului ³i îl ocup .
O metod  mai rapid  const  în calcularea, pentru ecare copil, a num rului de mut ri pân 
la ajungerea la scaunul liber.
Fie un copil situat în poziµia C , iar scaunul liber în poziµia S ; atunci copilul va avansa S  C
poziµii, dac  C S , respectiv N S  S  C poziµii, dac  C % S .
Astfel, la ecare ridicare a unui copil se poate aa cel mai apropiat copil, se calculeaz  câte
poziµii se va deplasa acesta (s  spunem P ), apoi toµi copiii vor avansa P poziµii. Un copil care
avanseaz  P poziµii din poziµia C va ajunge în poziµia C  P (dac  C  P N S ), respectiv
C  P  N S (dac  C  P % N S ).
Analiza complexit µii
Deoarece sunt N C copii în a³teptare, vor  analizate cel mult N C ridic ri de pe scaun, indi-
ferent dac  mai urmeaz  ceva în ³ierul de intrare.
Dac  sunt mai puµin de N C copii care se ridic , atunci o parte dintre cei care a³teapt  vor
r ­mâne în picioare.
La ecare moment în care se ridic  un copil, avansarea pas cu pas a copiilor aaµi în a³teptare
pân  la a³zarea pe scaun a unuia dintre ei nu poate necesita mai mult de N S pa³i; ecare pas
necesit  avansarea tuturor copiilor, deci a cel mult N C copii.
Complexitatea ocup rii unui scaun prin avansare pas cu pas este deci O N S N C .
În cazul metodei mai rapide, calcularea distanµei pentru toµi copiii pân  la scaunul liber are
complexitatea O N C . Aceea³i complexitate au ³i operaµiile de alegere a minimului P dintre
aceste distanµe, respectiv de avansare a tuturor copiilor cu P poziµii.
Ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O N C N S N C 
pentru prima variant , respectiv O N C N C  pentru cea de-a doua.

37.2.2 Cod surs 

Listing 37.2.1: scaune.cpp


1 #include<stdio.h>
2 #define MaxPoz 202
3
4 const char *InputFile ="scaune.in";
5 const char *OutputFile="scaune.out";
6
7 typedef int vector[MaxPoz];
8
9 /*
10 PozIn[i] = n, cu semnificatia scaunul n este in lista initiala pe pozitia i;
11 PozLoc[i]= 0, daca in dreptul pozitiei i nu este nici un scaun
12 n, daca in dreptul pozitiei i este scaunul n,
13 pentru a ocupa locul i
14 Loc[i] = 0, daca pe pozitia i nu a fost pus nici un scaun
15 n, numarul scaunului pus pe pozitia i
16 Muta [] = vector auxiliar in care se vor roti scaunele dupa asezarea unuia
17 dintre ele, vecto ce va fi copiat in PozLoc.
18 */
19
20 vector PozIn, PozLoc, Loc, Muta;
21
22 int n, poza, locp, liber, nrmut;
23 int gasitloc, faraloc;
24
25 FILE *fin, *fout;
26
27 void init()
28 { int i;
29 for(i=0;i<MaxPoz;i++)
30 PozIn[i]=PozLoc[i]=Loc[i]=0;
31 }
32
33 void prelucrare()
34 { int n, poza, locp, liber, nrmut, ns;
35 int gasitloc, faraloc;
36 int i, ok;
37 fin = fopen(InputFile,"r");
CAPITOLUL 37. ONI 2003 801

38 fout= fopen(OutputFile, "w");


39 fscanf(fin,"%d %d",&ns, &n);
40 for(i=1;i<=n;i++)
41 { fscanf(fin,"%d",&poza);
42 PozLoc[poza]=i;
43 PozIn[i]=poza;
44 }
45 faraloc=0;
46 while(!feof(fin)&&!faraloc)
47 { fscanf(fin,"%d",&liber);
48 if(!feof(fin))
49 {
50 gasitloc=0;
51 i=liber;
52 while(i>=1&&!gasitloc)
53 { if(PozLoc[i]) { locp=PozLoc[i];
54 nrmut=liber-i;
55 gasitloc=1;
56 }
57 else i--;
58 }
59 if(!gasitloc) { i=ns;
60 while((i>=liber+1)&&!gasitloc)
61 { if(PozLoc[i]) { locp=PozLoc[i];
62 nrmut=ns-i+liber;
63 gasitloc=1;
64 }
65 else i--;
66 }
67 }
68 if(!gasitloc) faraloc=1;
69 else { Loc[locp]=liber;
70 PozLoc[i]=0;
71 for(i=1;i<=ns;i++)
72 if(i+nrmut<=ns) Muta[i+nrmut]=PozLoc[i];
73 else Muta[i+nrmut-ns]=PozLoc[i];
74 for(i=1;i<=ns;i++)
75 PozLoc[i]=Muta[i];
76 }
77 }
78 }
79
80 ok=1;
81 for(i=1;i<=n;i++)
82 if(Loc[i]) { fprintf(fout,"%d %d \n",PozIn[i],Loc[i]); ok=0; }
83 else { fprintf(fout,"%d %d\n",PozIn[i],0); ok=0; }
84 if(ok&&faraloc) fprintf(fout,"%d",0);
85 fclose(fin);
86 fclose(fout);
87 }
88
89 int main()
90 { init();
91 prelucrare();
92 return 0;
93 }

37.2.3 Rezolvare detaliat 


Diferenµa de timp între cele dou  variante este mic !

Listing 37.2.2: scaune1.java


1 import java.io.*;
2 class Scaune1
3 {
4 static int ns, nc;
5 static int[] pozInitiala;
6 static int[] pozFinala;
7 static boolean[] asezat;
8
9 public static void main(String[]args) throws IOException
10 {
11 int i,j,k,nca=0; // nca=nr copii asezati
CAPITOLUL 37. ONI 2003 802

12 long t1,t2;
13 t1=System.currentTimeMillis();
14 PrintWriter out = new PrintWriter(
15 new BufferedWriter(new FileWriter("scaune.out")));
16 StreamTokenizer st=new StreamTokenizer(
17 new BufferedReader(new FileReader("scaune.in")));
18 st.nextToken(); ns=(int)st.nval;
19 st.nextToken(); nc=(int)st.nval;
20 asezat=new boolean[nc+1];
21 pozInitiala=new int[nc+1];
22 pozFinala=new int[nc+1];
23
24 for(k=1;k<=nc;k++)
25 {
26 st.nextToken();
27 pozInitiala[k]=pozFinala[k]=(int)st.nval;
28 }
29 while(st.nextToken()!=st.TT_EOF)
30 {
31 k=(int)st.nval; // scaunul k este liber
32 i=esteLaPozitia(k);
33 while((nca<nc)&&(i==0)) { misca(); i=esteLaPozitia(k); }
34 pozFinala[i]=k;
35 asezat[i]=true;
36 nca++;
37 }
38
39 for(j=1;j<=nc;j++)
40 if(asezat[j]) out.println(pozInitiala[j]+" "+pozFinala[j]);
41 else out.println(pozInitiala[j]+" "+0);
42 out.close();
43 t2=System.currentTimeMillis();
44 System.out.println("Timp = "+(t2-t1));
45 }//main
46
47 static void misca()
48 {
49 int i;
50 for(i=1;i<=nc;i++)
51 if(!asezat[i])
52 {
53 pozFinala[i]++;
54 if(pozFinala[i]==ns+1) pozFinala[i]=1;
55 }
56 }
57
58 static int esteLaPozitia(int k)
59 {
60 int i,copil=0;
61 for(i=1;i<=nc;i++)
62 if(!asezat[i]&&(pozFinala[i]==k)) { copil=i; break; }
63 return copil;
64 }
65 }//class

Pentru a doua varianta se calculeaz  distanµele pân  la scaunul liber pentru ecare copil r mas
nea³ezat.

Listing 37.2.3: scaune2.java


1 import java.io.*;
2 class Scaune2
3 {
4 static int ns, nc;
5 static int[] pozInitiala;
6 static int[] pozFinala;
7 static boolean[] asezat;
8 static int[] d;
9
10 public static void main(String[]args) throws IOException
11 {
12 int i,j,k;
13 long t1,t2;
14 t1=System.currentTimeMillis();
15 PrintWriter out = new PrintWriter(
16 new BufferedWriter(new FileWriter("scaune.out")));
CAPITOLUL 37. ONI 2003 803

17 StreamTokenizer st=new StreamTokenizer(


18 new BufferedReader(new FileReader("scaune.in")));
19 st.nextToken(); ns=(int)st.nval;
20 st.nextToken(); nc=(int)st.nval;
21 asezat=new boolean[nc+1];
22 d=new int[nc+1];
23 pozInitiala=new int[nc+1];
24 pozFinala=new int[nc+1];
25
26 for(k=1;k<=nc;k++)
27 {
28 st.nextToken();
29 pozInitiala[k]=pozFinala[k]=(int)st.nval;
30 }
31 while(st.nextToken()!=st.TT_EOF)
32 {
33 k=(int)st.nval; // scaunul k este liber
34 i=esteLaPozitia(k);
35 pozFinala[i]=k;
36 asezat[i]=true;
37 }
38
39 for(j=1;j<=nc;j++)
40 if(asezat[j]) out.println(pozInitiala[j]+" "+pozFinala[j]);
41 else out.println(pozInitiala[j]+" "+0);
42 out.close();
43 t2=System.currentTimeMillis();
44 System.out.println("Timp = "+(t2-t1));
45 }//main
46
47 static int esteLaPozitia(int k)
48 {
49 int i,min,imin;
50 for(i=1;i<=nc;i++)
51 if(!asezat[i])
52 if(pozFinala[i]<=k) d[i]=k-pozFinala[i];
53 else d[i]=k+ns-pozFinala[i];
54
55 imin=0; min=ns+1;
56 for(i=1;i<=nc;i++)
57 if(!asezat[i])
58 if(d[i]<min){ min=d[i]; imin=i; }
59
60 for(i=1;i<=nc;i++)
61 if(!asezat[i])
62 {
63 pozFinala[i]=pozFinala[i]+min;
64 if(pozFinala[i]>ns) pozFinala[i]=pozFinala[i]-ns;
65 }
66 return imin;
67 }
68 }//class

37.3 Circular
Unele numere naturale sunt formate doar din cifre distincte nenule.
Dintre acestea, unele, numite numere circulare, au urm toarea proprietate: pornind de la
prima cifr  ³i num rând spre dreapta, dup  cifr , atâtea cifre cât indic  aceasta, se determin  o
nou  cifr . Procedând la fel ³i pentru aceasta ³i pentru toate cele care urmeaz  se va ajunge din
nou la prima cifr .
Dac  toate cifrele au fost vizitate exact o dat , num rul se nume³te circular.
De exemplu num rul
1894256
este num r circular deoarece:
a are numai cifre distincte
a nu conµine cifra 0
a pornind de la 1 obµinem, pe rând: 8, 9, 2, 6, 5, 4, 1

Cerinµ 
CAPITOLUL 37. ONI 2003 804

Scrieµi un program care, pentru un N dat, determin  câte numere circulare sunt mai mici sau
egale cu N, precum ³i cel mai mare num r circular mai mic sau egal cu N.
Date de intrare
Pe prima linie a ³ierului de intrare circular.in se a  num rul natural N.
Date de ie³ire
Fi³ierul de ie³ire circular.out conµine o singur  linie, pe care se a  num rul de numere
circulare mai mici ca N precum ³i num rul circular maxim cerut, separate printr-un spaµiu.
Dac  nu exist  nici un num r circular mai mic ca N, în ³ierul de ie³ire se vor a³a dou  valori
0 separate printr-un spaµiu.
Restricµii
10 & N $ 10.000.000
Exemplu
circular.in circular.out Semnicaµie
1894250 347 1849625 Exist  347 numere circulare mai mici dacât
1894250 cel mai mare dintre acestea ind
num rul 1849625
Timp de execuµie: 1 sec/test

37.3.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 13/6


O rezolvare foarte bun  a acestei probleme se bazeaz  pe faptul c  exist  foarte puµine numere
circulare între 10 ³i 10.000.000. Acestea pot  generate în timpul concursului, obµinându-se un
³ier cu toate numerele circulare, câte 10 pe linie ³i separate prin virgul . Acest ³ier poate 
transformat u³or într-un vector de constante care poate  folosit de un program Pascal sau C/C++
care, astfel conµine toate numerele circulare. Acest program va citi valoarea lui N , apoi va rezolva
cerinµele parcurgând vectorul de constante. Deci, concurentul va elabora dou  programe: cel de
generare a numerelor circulare ³i cel care, folosindu-le, rezolv  cerinµele problemei.
Pentru generarea numerelor circulare se poate folosi un ciclu for pân  la 10.000.000, în cadrul
c ruia se testeaz , pe rând, pentru ecare num r dac  este circular. Un mod simplu de a realiza
aceast  testare este de a transforma num rul într-un vector de cifre, prin împ rµiri succesive la 10
cu salvarea restului.
Se observ  c  se vor obµine cifrele num rului în ordine invers ; este foarte u³or s  se resta-
bileasc  ordinea corect  prin inversarea primei cifre cu ultima, a celei de-a doua cu penultima
etc.
Pentru un num r reprezentat ca un vector de cifre se poate verica foarte u³or dac  este format
din cifre distincte ³i nu conµine cifra 0; pentru condiµia r mas  se va num ra spre dreapta, iar
poziµiile pe care se opre³te num rarea vor  marcate cu 0. Dac  la un moment dat se va ajunge
pe o poziµie marcat  cu 0, f r  a se  marcat în prealabil toate poziµiile, atunci num rul nu este
circular.
O metod  mai bun  ³i mai rapid  ar consta în generarea tuturor vectorilor de cifre distincte
nenule de cel mult 7 cifre, folosind metoda backtracking.
O alt  metod , ³i mai rapid , ar consta în generarea direct  a numerelor circulare, folosind o
alt  variant  a metodei backtracking. Astfel, dup  încercarea de a folosi cifra C1 pe prima poziµie,
se va trece la poziµia 1  C1 (de fapt se vor face C1 avans ri spre dreapta) ³i se va continua de
acolo; dac  acolo se va folosi cifra C2 , se avanseaz  înc  C2 poziµii spre dreapta etc.
La ecare pas din backtracking se folosesc cifrele cuprinse între 1 ³i 9, care nu au fost folosite
anterior; dac  avansarea spre dreapta duce într-o poziµie deja completat  ³i mai sunt poziµii libere,
num rul în construcµie nu poate  circular ³i se revine la cifra precedent .
Analiza complexit µii
Pentru aceast  problem , complexitatea rezolv rii este dat  de num rul M de numere circulare,
deci soluµia ruleaz  instantaneu, deci ordinul de complexitate al acesteia ar  O log2 M , în cazul
în care se folose³te o c utare binar .
Deoarece M este o constant , ordinul de complexitate al soluµiei este O 1.
Complexitatea metodei de generare a numerelor circulare depinde de metoda folosit .
CAPITOLUL 37. ONI 2003 805

37.3.2 Cod surs 

Listing 37.3.1: ALLCIRC.C


1 #include <stdio.h>
2
3 FILE *f, *g;
4
5 unsigned long N, nn;
6 int c[100], vc[11], i, circular, nc;
7
8 int CIRC(void)
9 {
10 int i, j, k, CORECT;
11
12 for (i=1; i<=nc; i++)
13 if (!c[i]) return 0; //cifre nenule
14 for (i=1; i<=nc; i++)
15 for (j=i+1; j<=nc; j++)
16 if (c[i]==c[j]) return 0; //numarul nu are cifre distincte
17 for (i=1; i<=nc; i++)
18 for (j=1; j<=10; j++) //pun numarul de 10 ori in vector
19 c[j*nc+i]=c[i];
20 for (i=0; i<11; i++) vc[i]=0; //vectorul caracteristic pe 0
21 i=1; vc[1]=1; CORECT=1; j=1; //initializari
22 while (CORECT) //parcurg cifrele pana gasesc una de 2 ori
23 {
24 k=1;
25 while (k<=c[i])
26 {
27 j++;
28 k++;
29 }
30 if (j%nc==0) //sunt chiar pe ulrima cifra
31 k=nc;
32 else
33 k=j%nc; //sunt pe o cifra distincta de ultima
34 vc[k]++; //marchez in vectorul caracteristic
35 if (vc[k]>1) CORECT=0; //am ajuns aici a doua oara
36 i=k; //noua cifra
37 }
38 for (i=2; i<=nc; i++) //am "trecut" prin toate cifrele? sau
39 if ((!vc[i])||(vc[i]>1))//ciclul s-a incheiat la alta cifra decat prima?
40 return 0;
41 return 1; //este OK!!!
42 }
43
44 int main()
45 {
46 // f=fopen("circular.in", "rt");
47 // fscanf(f, "%U", &N); fclose(f);
48 g=fopen("allcirc.out", "wt");
49 for (N=10; N<=10000000; N++) //4294967295
50 {
51 // circular=0;
52 // while (!circular)
53 {
54 nn=N;
55 nc=0;
56 while (nn) //determin numarul de cifre
57 {
58 nc++;
59 nn/=10;
60 }
61 nn=N;
62 for (i=nc; i>=1; i--) //pun cifrele in vector corect
63 { //Obs. daca un numar este circular
64 c[i]=nn%10; // nu rezulta ca inversul este
65 nn/=10; // si el circular
66 }
67 if (CIRC())
68 {
69 // circular=1;
70 fprintf(g, "%ld\n", N);
CAPITOLUL 37. ONI 2003 806

71 }
72 // N++;
73 }
74 }
75 fclose(g);
76 return 0;
77 }

Listing 37.3.2: Circback.c


1 #include <stdio.h>
2
3 FILE *f, *g;
4
5 unsigned long N, nn, n, contor, gata, nc1, NCircM, y;
6 int c[100], vc[11], i, nc, x[10], uz[10], c1, cifra;
7
8 int CIRC(void)
9 {
10 int i, j, k, CORECT;
11
12 for (i=1; i<=nc; i++)
13 for (j=1; j<=10; j++) //pun numarul de 10 ori in vector
14 c[j*nc+i]=c[i];
15 for (i=0; i<11; i++) vc[i]=0; //vectorul caracteristic pe 0
16 i=1; vc[1]=1; CORECT=1; j=1; //initializari
17 while (CORECT) //parcurg cifrele pana gasesc una de 2 ori
18 {
19 k=1;
20 while (k<=c[i])
21 {
22 j++;
23 k++;
24 }
25 if (j%nc==0) //sunt chiar pe ultima cifra
26 k=nc;
27 else
28 k=j%nc; //sunt pe o cifra distincta de ultima
29 vc[k]++; //marchez in vectorul caracteristic
30 if (vc[k]>1) CORECT=0; //am ajuns aici a doua oara
31 i=k; //noua cifra
32 }
33 for (i=2; i<=nc; i++) //am "trecut" prin toate cifrele? sau
34 if ((!vc[i])||(vc[i]>1))//ciclul s-a incheiat la alta cifra decat prima?
35 return 0;
36 return 1; //este OK!!!
37 }
38
39 void prelucreaza(void)
40 {
41 int i;
42 for (i=1; i<=n; i++) c[i]=x[i];
43 nc=n;
44 if (CIRC())
45 {
46 y=0; //formez numarul
47 for (i=1; i<=n; i++) y=y*10+x[i];
48 if (!gata) //
49 {
50 contor++;
51 NCircM=y;
52 }
53 else
54 if (y<=N)
55 {
56 contor++;
57 NCircM=y;
58 }
59
60 }
61 }
62
63 void back(int k)
64 {
65 int i;
CAPITOLUL 37. ONI 2003 807

66 if (k==n+1)
67 prelucreaza();
68 else
69 for (i=1; i<=9; i++)
70 if (!uz[i])
71 {
72 uz[i]=1;
73 x[k]=i;
74 back(k+1);
75 uz[i]=0;
76 }
77 }
78
79 int main(void)
80 {
81 f=fopen("circular.in", "rt");
82 g=fopen("circular.out", "wt");
83 fscanf(f, "%U", &N); fclose(f);
84 nn=N;
85 nc=0;
86 while (nn) //determin numarul de cifre
87 {
88 nc++;
89 nn/=10;
90 }
91
92 nn=N;
93 for (i=nc; i>=1; i--) //pun cifrele in vector corect
94 { //Obs. daca un numar este circular
95 c[i]=nn%10; // nu rezulta ca inversul este
96 nn/=10; // si el circular
97 }
98 nc1=nc;
99 c1=c[1]; gata=0;
100 contor=0;
101 for (n=2; n<nc1; n++) back(1);
102 gata=1;
103 for (cifra=1; cifra<=c1; cifra++)
104 {
105 x[1]=cifra;
106 uz[cifra]=1;
107 back(2);
108 uz[cifra]=0;
109 }
110
111 fprintf(g, "%ld %ld\n", contor, NCircM);
112 fclose(g);
113
114 return 0;
115 }

Listing 37.3.3: CIRCULAR.C


1 #include <stdio.h>
2
3 FILE *f, *g;
4
5 unsigned long N, nn, CircM, contor=0;
6 int c[100], vc[11], i, circular, nc;
7
8 int CIRC(void)
9 {
10 int i, j, k, CORECT;
11
12 for (i=1; i<=nc; i++)
13 if (!c[i]) return 0; //cifre nenule
14 for (i=1; i<=nc; i++)
15 for (j=i+1; j<=nc; j++)
16 if (c[i]==c[j]) return 0; //numarul nu are cifre distincte
17 for (i=1; i<=nc; i++)
18 for (j=1; j<=10; j++) //pun numarul de 10 ori in vector
19 c[j*nc+i]=c[i];
20 for (i=0; i<11; i++) vc[i]=0; //vectorul caracteristic pe 0
21 i=1; vc[1]=1; CORECT=1; j=1; //initializari
22 while (CORECT) //parcurg cifrele pana gasesc una de 2 ori
CAPITOLUL 37. ONI 2003 808

23 {
24 k=1;
25 while (k<=c[i])
26 {
27 j++;
28 k++;
29 }
30 if (j%nc==0) //sunt chiar pe ultima cifra
31 k=nc;
32 else
33 k=j%nc; //sunt pe o cifra distincta de ultima
34 vc[k]++; //marchez in vectorul caracteristic
35 if (vc[k]>1) CORECT=0; //am ajuns aici a doua oara
36 i=k; //noua cifra
37 }
38 for (i=2; i<=nc; i++) //am "trecut" prin toate cifrele? sau
39 if ((!vc[i])||(vc[i]>1))//ciclul s-a incheiat la alta cifra decat prima?
40 return 0;
41 return 1; //este OK!!!
42 }
43
44 int main()
45 {
46 f=fopen("circular.in", "rt");
47 fscanf(f, "%U", &N); fclose(f);
48 CircM=0;
49 while (N>10)
50 {
51 nn=N;
52 nc=0;
53 while (nn) //determin numarul de cifre
54 {
55 nc++;
56 nn/=10;
57 }
58 nn=N;
59 for (i=nc; i>=1; i--) //pun cifrele in vector corect
60 { //Obs. daca un numar este circular
61 c[i]=nn%10; // nu rezulta ca inversul este
62 nn/=10; // si el circular
63 }
64 if (CIRC())
65 {
66 contor++;
67 if (N>CircM) CircM=N;
68 }
69 N--;
70 }
71 g=fopen("circular.out", "wt");
72 fprintf(g, "%ld %ld\n", contor, CircM);
73 fclose(g);
74 return 0;
75 }

37.3.3 Rezolvare detaliat 

Listing 37.3.4: circular1.java


1 import java.io.*; // Timp = 18sec pentru n=9.999.999 (448 --> 9.682.415)
2 class Circular1 // Timp = 1.8sec pentru n=1.000.000
3 { // Timp = 0.9sec pentru n= 555.555 (3 teste peste
!!!)
4 static int n, nnc=0,ncmax=0;
5 static int[] a=new int[7]; // vectorul cifrelor
6 static int[] aa=new int[7]; // traseu!
7 static int[] fc=new int[10];// frecventa cifrelor
8 static int nc;
9
10 public static void main(String[]args) throws IOException
11 {
12 int k;
13 long t1,t2;
CAPITOLUL 37. ONI 2003 809

14 t1=System.currentTimeMillis();
15 PrintWriter out = new PrintWriter(
16 new BufferedWriter(new FileWriter("circular.out")));
17 StreamTokenizer st=new StreamTokenizer(
18 new BufferedReader(new FileReader("circular.in")));
19 st.nextToken(); n=(int)st.nval;
20
21 for(k=12;k<=n;k++)
22 if(esteCircular(k)) { nnc++; ncmax=k; }
23
24 out.println(nnc+" "+ncmax);
25 out.close();
26 t2=System.currentTimeMillis();
27 System.out.println("Timp = "+(t2-t1));
28 }//main
29
30 static boolean esteCircular(int k)
31 {
32 int i,j;
33 int c; // cifra
34 nc=0;
35 for(i=0;i<=9;i++) fc[i]=0;
36 while(k!=0) {c=k%10; a[nc]=c; fc[c]++; k=k/10; nc++;}
37 if(fc[0]>0) return false;
38 for(i=1;i<=9;i++) if(fc[i]>1) return false;
39 for(i=0;i<nc/2;i++) {c=a[i]; a[i]=a[nc-1-i]; a[nc-1-i]=c;}
40 for(i=0;i<nc;i++) aa[i]=a[i];
41
42 j=0;
43 for(i=1;i<=nc;i++)
44 if(aa[j]==0) return false; else { aa[j]=0; j=(j+a[j])%nc; }
45 if(j==0) return true; else return false;
46 }
47 }//class

Listing 37.3.5: circular2.java


1 import java.io.*;
2 class Circular2
3 {
4 static int n, nnc=0,ncmax=0;
5 static int[] x=new int[7];
6 static int[] a=new int[7];
7 static int[] aa=new int[7];
8 static int ncn; // nr cifrelor lui n
9 static int nc; // nc=2, ..., ncn (lg numerelor generate)
10
11 public static void main(String[] args) throws IOException
12 {
13 long t1,t2;
14 t1=System.currentTimeMillis();
15 PrintWriter out = new PrintWriter(
16 new BufferedWriter(new FileWriter("circular.out")));
17 StreamTokenizer st=new StreamTokenizer(
18 new BufferedReader(new FileReader("circular.in")));
19 st.nextToken(); n=(int)st.nval;
20 int k=n;
21 ncn=0;
22 while(k!=0) {k=k/10; ncn++;}
23
24 for(nc=2;nc<=ncn;nc++) f(0);
25
26 out.println(nnc+" "+ncmax);
27 out.close();
28 t2=System.currentTimeMillis();
29 System.out.println("Timp = "+(t2-t1));
30 }
31
32 static void f(int k)
33 {
34 boolean ok;
35 int i,j;
36 for(i=1;i<=9;i++) // pun valoarea i pe pozitia k in vectorul a
37 {
38 ok=true;
CAPITOLUL 37. ONI 2003 810

39 if(k>0)
40 for(j=0;j<k;j++) if(i==x[j]) {ok=false; break;}
41
42 if(!ok) continue;
43 x[k]=i;
44 if(k<nc-1) f(k+1); else afisv();
45 }
46 }
47
48 static void afisv()
49 {
50 if(!esteCircular(x)) return;
51 if(numar(x)>n) return;
52 nnc++;
53 ncmax=numar(x);
54 //System.out.print(nnc+" : ");
55 //for(int i=0; i<=nc-1; i++) System.out.print(x[i]);
56 //System.out.println();
57 }
58
59 static int numar(int[] x)
60 {
61 int nx=x[0];
62 for(int i=1;i<=nc-1;i++) nx=nx*10+x[i];
63 return nx;
64 }
65
66 static boolean esteCircular(int[] x)
67 {
68 int i,j;
69 for(i=0;i<nc;i++) a[i]=aa[i]=x[i];
70
71 j=0;
72 for(i=1;i<=nc;i++)
73 if(aa[j]==0) return false; else { aa[j]=0; j=(j+a[j])%nc; }
74 if(j==0) return true; else return false;
75 }
76 }

37.4 Criptare
Mircea ³i Vasilic  vor s -³i trimit  mesaje pe care alµii s  nu le înµeleag . Au citit ei despre
spioni ³i modalit µi de a scrie mesaje ³i, în nal, au imaginat un mod de criptare a unui mesaj
care folose³te "cuvânt cheie" (le-a pl cut lor denumirea asta :-) ).
Alegându-³i un cuvânt cheie format numai din litere distincte, ei num r  literele acestuia ³i
împart mesajul în grupe de lungime egal  cu num rul de litere ale cuvântului cheie, ³i le a³eaz 
una sub alta. Desigur, se poate întâmpla ca ultima grup  s  e incomplet , a³a c  o completeaz 
cu spaµii.
Apoi numeroteaz  literele cuvântului cheie în ordinea apariµiei lor în alfabetul englez.
În nal, rescriu mesajul astfel: coloana de sub litera numerotat  cu 1, urmat  de coloana de
sub litera numerotat  cu 2, etc. înlocuind totodat  ³i spaµiile cu caracterul '*' (asterisc).
Exemplu:
cuvântul cheie: criptam
mesaj de criptat: Incercam sa lucram cu coduri si criptari.
cuvântul cheie criptam are 7 litere
numerotare: 2635714
deoarece, avem, în ordine
a b c defgh i jkl m no p q r s t uvwxzy
1 2 3 4 5 6 7
împ rµire în grupe: Incerca¶m sa lu¶cram cu¶ coduri¶ si cri¶ptari.

codicare:
CAPITOLUL 37. ONI 2003 811

2 6 3 5 7 1 4
I n c e r c a
m * s a * l u
c r a m * c u
* c o d u r i
* s i * c r i
p t a r i . *
mesaj criptat: clcrr.Imc**pcsaoiaauuii*eamd*rn*rcstr**uci
clcrr. Imc**p csaoia auuii* eamd*r n*rcst r**uci
col1 col2 col3 col4 col5 col6 col7
Cerinµ 
Fiind date un cuvânt cheie ³i un mesaj criptat, decodicaµi mesajul trimis de Mircea pentru
Vasilic .
Date de intrare
Fi³ierul de intrare criptare.in conµine pe prima linie mesajul criptat iar pe linia a doua
cuvântul cheie.
Date de ie³ire
Fi³ierul de intrare criptare.out conµine pe prima linie mesajul decriptat.
Restricµii
a lungimea mesajului este de minim 20 ³i maxim 1000 caractere
a cuvântul cheie are minim 5 ³i maxim 20 de caractere
a cuvântul cheie conµine numai litere mici ale alfabetului
Exemplu
criptare.in
clcrr.Imc**pcsaoiaauuii*eamd*rn*rcstr**uci
criptam

criptare.out
Incercam sa lucram cu coduri si criptari.
Timp maxim de execuµie: 1 secund /test

37.4.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 13/6


Pentru decriptarea mesajului se utilizeaz  algoritmul de criptare descris, în sens invers.
Se determin  ordinea literelor din cuvântul cheie. Considerând c  acest cuvânt are lungimea
L, iar textul are lungimea N , el va  împ rµit în L grupe de lungimea N ©L.
Grupele sunt a³ezate într-o matrice cu L coloane pe coloanele corespunz toare ordinii literelor
din cuvântul cheie.
În nal, matricea este parcurs  pe linii ³i se a³eaz  mesajul decriptat.
Analiza complexit µii
Operaµiile de citire ³i a³are a datelor au ordinul de complexitate O N .
Deoarece lungimea L a cuvântului cheie este mic , operaµiile cu acesta (determinarea ordinii
literelor) pot  neglijate la calculul complexit µii.
Scrierea cuvântului în matrice are ordinul de complexitate O N .
În nal, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O N  
O N  O N .

37.4.2 Cod surs 

Listing 37.4.1: criptare.cpp


1 #include <fstream.h>
2 #include <string.h>
3
4 char mesaj[1001], cheie[21], c;
CAPITOLUL 37. ONI 2003 812

5 int lgm, lgc, l, i, j, apare, cate, col, gasit, k;


6 int cod[201][21], alfabet[27];
7
8 ifstream f("criptare.in");
9 ofstream g("criptare.out");
10
11 void citire()
12 {
13 f.getline(mesaj, 1001);
14 f.getline(cheie, 21);
15 f.close();
16 }
17
18 void decriptare()
19 {
20 lgm=strlen(mesaj);
21 lgc=strlen(cheie);
22 cate=lgm/lgc;
23 apare=0;
24 for (c=’a’; c<=’z’; c++)
25 {
26 for (i=0; i<lgc; i++)
27 if (cheie[i]==c)
28 {
29 apare++;
30 alfabet[c-’a’+1]=apare;
31 }
32 }
33 l=0;
34 for (i=0; i<lgc; i++)
35 for (j=1; j<=26; j++)
36 if (alfabet[j]==i+1) //am gasit litera a i-a (ca numerotare in cheie)
37 {
38 //caut pe ce pozitie in cheie este ca sa determin coloana
39 col=0; gasit=0;
40 while (!gasit)
41 {
42 if (cheie[col]==(char)(j+’a’-1))
43 gasit=1;
44 col++;
45 }
46 for (k=1; k<=cate; k++) //pun in coloana col
47 cod[k][col]=mesaj[l++];
48 }
49 for (i=1; i<=cate; i++)
50 for (j=1; j<=lgc; j++)
51 if ((char)cod[i][j]!=’*’)
52 g<<(char)cod[i][j];
53 else
54 g<<’ ’;
55 g<<endl;
56 g.close();
57 }
58
59 int main()
60 {
61 citire();
62 decriptare();
63 return 0;
64 }

37.4.3 Rezolvare detaliat 


Listing 37.4.2: criptare1.java
1 import java.io.*;
2 class Criptare1
3 {
4 public static void main(String[]args)throws IOException
5 {
6 int i,j,k;
7 BufferedReader br=new BufferedReader(
8 new FileReader("criptare.in"));
9 String mesaj=br.readLine();
CAPITOLUL 37. ONI 2003 813

10 String cheia=br.readLine();
11 br.close();
12
13 System.out.println("mesajul este: "+mesaj+" "+mesaj.length());
14 System.out.println("cheia este: "+cheia+" "+cheia.length());
15
16 int m=mesaj.length()/cheia.length(); // nr linii matrice
17 int n=cheia.length(); // nr coloane matrice
18
19 int[] p=new int[n];
20 // sortare prin numarare
21 for(j=1; j<=n-1; j++)
22 for(i=0;i<=j-1;i++)
23 if(cheia.charAt(i)<cheia.charAt(j)) ++p[j]; else ++p[i];
24 afisv(p);
25 int[] pp=new int[n]; // permutarea inversa
26 for(i=0;i<n;i++) pp[p[i]]=i;
27 afisv(pp);
28
29 char a[][]=new char[m][n];
30
31 k=0;
32 for(j=0;j<n;j++)
33 for(i=0;i<m;i++)
34 {
35 a[i][pp[j]]=mesaj.charAt(k);
36 k++;
37 }
38
39 System.out.println("Matricea a[][] este:");
40 for(i=0;i<m;i++)
41 {
42 for(j=0;j<n;j++) System.out.print(a[i][j]+" ");
43 System.out.println();
44 }
45
46 for(i=0;i<m;i++)
47 for(j=0;j<n;j++)
48 if(a[i][j]!=’*’) System.out.print(a[i][j]);
49 else System.out.print(" ");
50 System.out.println();
51 }//main
52
53 static void afisv(int[] x)
54 {
55 int n=x.length, i;
56 for(i=0;i<n;i++) System.out.print(x[i]+" ");
57 System.out.println();
58 }
59 }//class:

Listing 37.4.3: criptare2.java


1 import java.io.*;
2 class Criptare2
3 {
4 public static void main(String[]args)throws IOException
5 {
6 int i,j,k;
7 BufferedReader br=new BufferedReader(
8 new FileReader("criptare.in"));
9 PrintWriter out=new PrintWriter(
10 new BufferedWriter(
11 new FileWriter("criptare.out")));
12 String mesaj=br.readLine();
13 String cheia=br.readLine();
14 br.close();
15
16 int m=mesaj.length()/cheia.length(); // nr linii matrice
17 int n=cheia.length(); // nr coloane matrice
18
19 int[] p=new int[n];
20 // sortare prin numarare
21 for(j=1; j<=n-1; j++)
22 for(i=0;i<=j-1;i++)
CAPITOLUL 37. ONI 2003 814

23 if(cheia.charAt(i)<cheia.charAt(j)) ++p[j]; else ++p[i];


24 int[] pp=new int[n]; // permutarea inversa
25 for(i=0;i<n;i++) pp[p[i]]=i;
26
27 char a[][]=new char[m][n];
28
29 k=0;
30 for(j=0;j<n;j++)
31 for(i=0;i<m;i++)
32 {
33 a[i][pp[j]]=mesaj.charAt(k);
34 k++;
35 }
36
37 for(i=0;i<m;i++)
38 for(j=0;j<n;j++)
39 if(a[i][j]!=’*’) out.print(a[i][j]); else out.print(" ");
40 out.close();
41 }
42 }

37.5 Ma³ina
O µar  are 3 & N & 30000 ora³e, numerotate de la 1 la N , dispuse pe un cerc.
PAM tocmai ³i-a luat carnet de conducere ³i vrea s  viziteze toate ora³ele µ rii. Lui PAM îi
este fric  s  conduc  prin locuri aglomerate a³a c  ea ³i-a propus s  mearg  numai pe ³oselele
unde tracul este mai redus.
Exist  ³osele de leg tur  între oricare dou  ora³e al turate: între ora³ul 1 ³i ora³ul 2, ... , între
ora³ul i ³i ora³ul i  1, iar ora³ul N este legat de ora³ulul 1.
Ca s  nu se r t ceasc , PAM ³i-a propus s -³i aleag  un ora³ de început ³i s  mearg  pe ³oselele
respective în sens trigonometric pân  ajunge înapoi în ora³ul de unde a plecat. Dac  PAM pleac 
din ora³ul K , atunci traseul ei va : K, K  1, ..., N, 1, 2, ..., K .
Ma³ina lui PAM are un rezervor foarte mare (în care poate pune oricât de mult  benzin ). În
ecare ora³, PAM ia toat  cantitatea de benzin  existent  în ora³, iar parcurgerea ec rei ³osele
necesit  o anumit  cantitate de benzin .
Cerinµ 
“tiind c  PAM are, la începutul c l toriei, doar benzina existent  în ora³ul de plecare, ³i c ,
atunci când ajunge într-un ora³, ea va lua toat  cantitatea de benzin  disponibil  în acel ora³, s 
se g seasc  un ora³ din care PAM î³i poate începe excursia astfel încât s  nu r mân  f r  benzin .
Se consider  c  PAM a r mas f r  benzin  dac  în momentul plec rii dintr-un ora³, nu are
sucient  benzin  pentru a parcurge ³oseaua care duce la ora³ul urm tor. Dac  benzina îi ajunge
la x (adic  la plecare are tot atâta benzin  cât  îi trebuie) se consider  c  PAM poate ajunge
pân  în ora³ul urm tor.
Date de intrare
Fi³ierul de intrare masina.in conµine
a pe prima linie num rul N ,
a pe cea de-a doua linie se g sesc N numere naturale a1, a2, ..., aN , separate prin câte
un spaµiu, unde ai reprezint  cantitatea de benzin  disponibil  în ora³ul i.
a linia a treia conµine un ³ir de N numere naturale b1, b2, ..., bN , separate prin câte un
spaµiu, unde bi reprezint  cantitatea de benzin  necesar  str baterii ³oselei dintre ora³ele i ³i
i  1 (sau N ³i 1, dac  i N ).
Date de ie³ire
Fi³ierul de ie³ire masina.out va conµine un singur num r s care reprezint  un ora³ din care,
dac  PAM î³i începe c l toria, poate completa turul µ rii f r  a face pana prostului.
Restricµii ³i preciz ri
a Dac  exist  mai multe soluµii, se cere una singur .
a 0 & ai & 30000
a 1 & bi & 30000
Exemplu
CAPITOLUL 37. ONI 2003 815

masina.in masina.out
6 4
0 3 2 5 10 5
783214
Timp maxim de execuµie: 0.3 sec/test

37.5.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 13/6


Având în vedere c  ora³ele sunt a³ezate ³i parcurse circular, putem dubla ora³ele, iar apoi
putem s  le consider m a³ezate liniar:
an1 a1 , an2 a2 , ..., ank ak , ..., a2n an
bn1 b1 , bn2 b2 , ..., bnk bk , ..., b2n bn .
Cantitatea de benzin  câ³tigat  în urma opririi în ora³ul i ³i parcurgerii drumului c tre ora³ul
i  1 este ci ai  bi . Evident, aceast  cantitate poate  negativ .
Denim ³irul sumelor parµiale ale vectorului c astfel:
si < i
k 1 si .
Pentru ca PAM s  poat  efectua turul, este necesar ca suma cantit µii de benzin  acumulate
s  e mai mare sau egal  cu 0, deci sn = 0.
Rezolvarea problemei const  în a g si un x astfel încât
cx ' 0, cx  cx1 ' 0, ..., cx  cxn1 ' 0,
adic 
sx  sx1 ' 0, sx1  sx1 ' 0,..., sxn1  sx1 ' 0.
Un candidat viabil este poziµia m & n pentru care sm este minim.
În continuare se demonstreaz  c  aceast  alegere conduce la o soluµie.
Fie m $ k & n; avem sk ' sm (din deniµia lui m).
Fie n $ k ; avem
sk c1  ...  cn  cn1  ...  ck sn  c1  ...  ck sn  sk ' sk ' sm
(am folosit faptul c  sn ' 0).
În concluzie x m  1 este o soluµie pentru problem .
Implementarea const  în citirea datelor, calcularea ³irului sumelor parµiale ³i g sirea minimului
dintre s1 , s2 , ..., sn .
Analiza complexit µii
Operaµia de citire a datelor are ordinul de complexitate O n.
Calculul ³irului sumelor parµiale are ordinul de complexitate O n pentru c  si si1  ai .
Minimul din acest ³ir se calculeaz  cu aceea³i complexitate.
Operaµia de scriere a rezultatului are ordinul de complexitate O 1.
În nal, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O n 
O n  O 1 O n.

37.5.2 *Cod surs 

37.5.3 Rezolvare detaliat 

Listing 37.5.1: masina.java


1 import java.io.*;
2 class Masina
3 {
4 public static void main(String[] args) throws IOException
5 {
6 int i,rez;
7 int j,n;
8 StreamTokenizer st=new StreamTokenizer(
9 new BufferedReader(new FileReader("masina.in")));
10 PrintWriter out=new PrintWriter(
11 new BufferedWriter(new FileWriter("masina.out")));
12
13 st.nextToken();n=(int)st.nval;
CAPITOLUL 37. ONI 2003 816

14 int[] a=new int[n+1];


15 int[] b=new int[n+1];
16
17 for (j=1;j<=n;j++){ st.nextToken();a[j]=(int)st.nval;}
18 for (j=1;j<=n;j++){ st.nextToken();b[j]=(int)st.nval;}
19
20 for(i=1;i<=n;i++)
21 {
22 rez=a[i]-b[i];
23 j=i;
24 j++; if(j==n+1) j=1;
25 while((rez>=0)&&(j!=i))
26 {
27 rez=rez+a[j]-b[j];
28 j++;if(j==n+1) j=1;
29 }
30 if(rez>=0) {out.println(i); break;}
31 }
32 out.close();
33 }
34 }

37.6 Operaµii
k
Not m cu c ³i r câtul ³i respectiv restul împ rµirii unui numar nr la 2 , unde k este un num r
natural nenul.
Asupra num rului putem efectua succesiv urm toarele operaµii:
k
a O1 nr, k  reprezint  transformarea num rului nr în num rul 2 2c  1  r pentru orice rest
r, sau
c  r doar dac  r $ 2k  1.
k1
a O2 nr, k  reprezint  transformarea num rului nr în num rul 2

Cerinµ 
Se dau m ³i n dou  numere naturale nenule.
Efectuaµi asupra numerelor m ³i n operaµii succesive, O1 sau O2, pentru valori alese ale lui k ,
astfel încât dup  un num r nit de operaµii cele dou  numere s  devin  egale, iar valoarea astfel
obµinut  s  e minim .
Date de intrare
Fi³ierul de intrare operatii.out conµine pe o linie m n  dou  numere naturale nenule, separate
printr-un spaµiu, reprezentând cele dou  numere date.
Date de ie³ire
Fi³ierul de ie³ire operatii.out conµine pe cele i  j  3 linii urm toarele:
a nmin  num rul natural nenul nmin, reprezentând valoarea minim  obµinut  din m ³i n
prin aplicarea unor succesiuni de operaµii O1 sau O2,
a i  num rul operaµiilor efectuate asupra primului num r m, pe a doua linie
a op1 k1  pe urm toarele i linii: perechi de numere
a ...  reprezentând operaµia (1 sau 2) ³i respectiv valoarea lui k
a opi ki  pentru operaµia respectiv , separate printr-un spaµiu.
a j  num rul operaµiilor efectuate asupra celui de al doilea num r n, pe linia i+2
a op1 k1  pe urm toarele j linii: perechi de numere
a ...  reprezentând operaµia (1 sau 2) ³i respectiv valoarea lui k
a opj kj  pentru operaµia respectiv , separate printr-un spaµiu

Restricµii
1 $ m, n & 2.000.000.000
Exemplu
OPERATII.IN OPERATII.OUT
11 45 15
2
23
12
2
22
24
CAPITOLUL 37. ONI 2003 817

Timp maxim de execuµie: 1 sec/test

37.6.1 Indicaµii de rezolvare

Soluµie prezentat  de Mihai Stroe, GInfo nr. 13/6


Pentru rezolvarea problemei este necesar  descompunerea în binar a celor dou  numere ³i
înµelegerea efectului pe care îl au cele dou  operaµii asupra acestei descompuneri.
Se observ  c  operaµia O1 insereaz  o cifr  1 în descompunere, pe o anumit  poziµie, în timp
ce operaµia O2 ³terge un 0, la alegere.
Privind astfel cele dou  operaµii, algoritmul de rezolvare începe cu descompunerea numerelor
în baza 2 ³i ³tergerea tuturor cifrelor 0 din ambele numere. Se vor obµine astfel dou  numere, nu
neap rat egale, ale c ror descompuneri în baza 2 sunt formate numai din cifre 1 (deci numerele
sunt de forma 2 P  1).
Cum cifrele 1 nu pot  ³terse, singura variant  de a ajunge la acela³i num r const  în inserarea
de cifre 1 în cadrul num rului mai mic.
La efectuarea operaµiilor O2 pe ecare num r, acestea se memoreaz  pentru a  a³ate.
Pentru operaµiile O1 nu este necesar  memorarea; ele se vor efectua numai asupra num rului
care r mâne mai mic, valoarea pentru K putând  1 pentru toate aceste operaµii.
Analiza complexit µii
Operaµia de citire a datelor are ordinul de complexitate O 1.
De asemenea, ordinul de complexitate al operaµiilor de descompunere a numerelor în baza
2, de detectare a zerourilor din descompunere, de memorare a poziµiilor operaµiilor de tip 2, de
detectare a num rului mai mic ³i a³area mut rilor este O 1.
În nal, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O 1.

37.6.2 *Cod surs 

37.6.3 Rezolvare detaliat 

Listing 37.6.1: operatii.java


1 import java.io.*;
2 class Operatii
3 {
4 static int m, n;
5 static int[] a=new int[64]; // vectorul cifrelor binare ale lui m
6 static int[] b=new int[64]; // vectorul cifrelor binare ale lui n
7 static int nca,ncb, nc1a, nc0a, nc1b,nc0b, nc1sol,nminsol;
8
9 public static void main(String[]args) throws IOException
10 {
11 int i,j,k,nr;
12 PrintWriter out = new PrintWriter(
13 new BufferedWriter(new FileWriter("operatii.out")));
14 StreamTokenizer st=new StreamTokenizer(
15 new BufferedReader(new FileReader("operatii.in")));
16 st.nextToken(); m=(int)st.nval;
17 st.nextToken(); n=(int)st.nval;
18
19 nr=m;
20 k=0;
21 while(nr!=0) { a[k]=nr%2; k++; nr=nr/2;}
22 nca=k;
23 nc1a=0;
24 for(k=0;k<nca;k++) if(a[k]==1) nc1a++;
25 nc0a=nca-nc1a;
26
27 nr=n;
28 k=0;
29 nc1b=0;
30 while(nr!=0) { b[k]=nr%2; k++; nr=nr/2;}
31 ncb=k;
32 for(k=0;k<ncb;k++) if(b[k]==1) nc1b++;
33 nc0b=ncb-nc1b;
CAPITOLUL 37. ONI 2003 818

34
35 if(nc1a<nc1b) nc1sol=nc1b; else nc1sol=nc1a;
36 nminsol=1;
37 for(k=1;k<=nc1sol;k++) nminsol=nminsol*2;
38 nminsol=nminsol-1;
39 out.println(nminsol);
40
41 out.println(nc1sol-nc1a+nc0a);
42 j=0;
43 for(k=1;k<=nc0a;k++)
44 {
45 while(a[j]!=0) j++;
46 out.println(2+" "+(j-k+2)); // sterg 0 de pe pozitia j
47 j++;
48 }
49 for(k=nc1a;k<nc1sol;k++) out.println("1 1");
50
51 out.println(nc1sol-nc1b+nc0b);
52 j=0;
53 for(k=1;k<=nc0b;k++)
54 {
55 while(b[j]!=0) j++;
56 out.println(2+" "+(j-k+2)); // sterg 0 de pe pozitia j
57 j++;
58 }
59 for(k=nc1b;k<nc1sol;k++) out.println("1 1");
60
61 out.close();
62 }//main
63 }//class
Capitolul 38

ONI 2002

Figura 38.1: Sigla ONI 2002

38.1 Pentagon
lect. univ. Ovidiu Dom³a, Alba Iulia
În urma bombardamentelor din 11 septembrie 2001, cl direa Pentagonului a suferit daune
la unul din pereµii cl dirii. Imaginea codicat  a peretelui avariat se reprezint  sub forma unei
matrice cu m linii ³i n coloane, ca în gura de mai jos:
1110000111 unde 1 reprezint  zid intact
1100001111 0 zid avariat
1000000011
1111101111
1110000111
Sumele alocate de Bin Laden pentru refacerea Pentagonului vor  donate celor care vor ajuta
americanii s  refac  aceast  cl dire prin plasarea, pe vertical , a unor blocuri de în lµimi k ,
k 1, ..., m, ³i l µime 1, în locurile avariate.
Cerinµ :
Pentru o structur  dat  a unui perete din cl direa Pentagonului, determinaµi num rul minim
al blocurilor, de în lµimi k 1, k 2, ..., k m, necesare refacerii cl dirii.
Date de intrare:
Fi³ierul de intrare PENTAGON.IN conµine pe prima linie dimensiunile m ³i n ale peretelui
cl dirii, iar pe urm toarele m linii câte o secvenµ  de caractere 1 sau 0 de lungime n.
Date de ie³ire:
Fi³ierul PENTAGON.OUT va conµine pe câte o linie, ordonate cresc tor dup  k , secvenµe:
k nr
unde k este înalµimea blocului,
iar nr este num rul de blocuri de în lµime k , separate prin câte un spaµiu.
Restricµii ³i preciz ri
a 1 & m, n & 200
a nu se vor a³a blocurile de în lµime k , a c ror num r este zero (0).

819
CAPITOLUL 38. ONI 2002 820

Exemplu
PENTAGON.IN PENTAGON.OUT
5 10 17
1110000111 21
1100001111 32
1000000011 51
1111101111
1110000111
Timp maxim de execuµie: 1 secund /test

38.1.1 Indicaµii de rezolvare

Soluµie prezentat  în GInfo 12/6


Rezolvarea acestei probleme este foarte simpl , ea neimplicând decât parcurgerea pe coloane a
unei matrice, contorizarea unor secvenµe de zerouri ³i p strarea unei statistici referitoare la aceste
secvenµe.
Pentru a determina blocurile necesare, vom determina blocurile pe ecare coloan  (datorit 
faptului c  l µimea unui bloc este întotdeauna 1 ³i toate blocurile sunt dispuse pe vertical , un
bloc poate ocupa o singur  coloan ).
Pentru a determina blocurile de pe o coloan  va trebui s  determin m secvenµele de zerouri
de pe coloana respectiv . Vom lua în considerare doar secvenµele de lungime maxim  pentru a
minimiza num rul total de blocuri. De exemplu, dac  avem ³ase zerouri consecutive, am putea
folosi un bloc de lungime 6, dar ³i dou  blocuri de lungime 5 ³i 1, 4 ³i 2 etc. Evident, este
obligatoriu s  folosim un singur bloc pentru ca num rul total al blocurilor utilizate s  e minim.
A³adar, pentru ecare coloan  vom determina lungimile secvenµelor de zerouri. O secvenµ  de
zerouri poate începe e pe prima linie a coloanei, e în momentul în care întâlnim o linie pe care
se a  un element cu valoarea 0 în timp ce pe linia anterioar  se a  un element cu valoarea 1.
Secvenµa se va termina e la terminarea parcurgerii coloanei (se ajunge pe ultima linie a acesteia),
e în momentul în care întâlnim o linie pe care se a  un element cu valoarea 1 în timp ce pe linia
anterioar  se a  un element cu valoarea 0.
În momentul detect rii termin rii unei secvenµe (presupunem c  lungimea acesteia este x),
num rul blocurilor de lungime x este incrementat. Pentru p strarea num rului de blocuri se
utilizeaz  un ³ir a, unde ax indic  num rul blocurilor de lungime x.
La sfâr³it, vom a³a statistica cerut  pe baza datelor p strate în ³irul a. Vor  a³ate toate
perechile de forma i ai care respect  condiµia ai j 0.
Analiza complexit µii
Datorit  faptului c  num rul de elemente care compun o secvenµ  de zerouri poate  determinat
pe m sur  ce este parcurs  secvenµa, întregul algoritm const  într-o singur  traversare a matricei.
Ordinul de complexitate al unei astfel de travers ri este O m n, unde m reprezint  num rul de
linii ale matricei, iar n reprezint  num rul de coloane.
Pentru citirea datelor, matricea este parcurs  o singur  dat , deci ordinul de complexitate al
acestei operaµii este tot O m n.
Pentru a³area datelor se parcurge o singur  dat  ³irul a; acesta nu poate conµine mai mult de
m elemente deoarece nu pot  folosite blocuri mai înalte decât în lµimea zidului. A³adar, scrierea
soluµiei are ordinul de complexitate O m.
În concluzie, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O m
n  O m n  O m O m n.

38.1.2 Cod surs 

Listing 38.1.1: pentagon.pas


1 type corp=array[1..200] of string[200];
2 var c:corp;
3 f:text;
4 s:array[1..200] of byte;
5 m,n,i:byte;
6
7 Procedure numara;
CAPITOLUL 38. ONI 2002 821

8 var i,j:byte;
9 cod:boolean;
10 nr:byte;
11 begin
12 for j:=1 to n do
13 begin
14 nr:=0;
15 cod:=c[1,j]=’1’;
16 for i:=1 to m do
17 if c[i,j]=’1’ then
18 begin
19 if not cod then
20 begin
21 cod:=true;
22 inc(s[nr]);
23 nr:=0;
24 end
25 end
26 else
27 begin
28 if cod then
29 begin
30 nr:=1;
31 cod:=false;
32 end
33 else
34 inc(nr);
35 end;
36 if not cod then
37 begin
38 cod:=true;
39 inc(s[nr]);
40 end
41 end;
42 end;
43
44 begin
45 assign(f,’pentagon.in’);
46 reset(f);
47 readln(f,m,n);
48 for i:=1 to m do
49 readln(f,c[i]);
50 close(f);
51 numara;
52 assign(f,’pentagon.out’);
53 rewrite(f);
54 for i:=1 to m do
55 if s[i]<>0 then
56 writeln(f,i,’ ’,s[i]);
57 close(f);
58 end.

38.1.3 Rezolvare detaliat 


Listing 38.1.2: pentagon.java
1 import java.io.*;
2 class Pentagon
3 {
4 static int m,n;
5 static String[] a;
6 static int[] f;
7
8 public static void main(String[]args) throws IOException
9 {
10 int i,j,k;
11 BufferedReader br=new BufferedReader(new FileReader("pentagon.in"));
12 StreamTokenizer st=new StreamTokenizer(br);
13
14 st.nextToken();m=(int)st.nval;
15 st.nextToken();n=(int)st.nval;
16
17 a=new String[m];
18 f=new int[n+1]; // frecventa lungimilor blocurilor
CAPITOLUL 38. ONI 2002 822

19
20 br.readLine(); // altfel nu merge !!!
21
22 for(i=0;i<m;i++) a[i]=br.readLine();
23
24 for(j=0;j<n;j++)
25 {
26 i=0;
27 while(i<m)
28 {
29 while((i<m)&&(a[i].charAt(j)==’1’)) i++;
30 k=0;
31 while((i<m)&&(a[i].charAt(j)==’0’)) {k++; i++;}
32 f[k]++;
33 }
34 }
35
36 PrintWriter out=new PrintWriter(
37 new BufferedWriter(new FileWriter("pentagon.out")));
38 for(k=1;k<=n;k++) if(f[k]>0) out.println(k+" "+f[k]);
39 out.close();
40 }
41 }

38.2 Pod
prof. Marinel “erban, Ia³i
Între dou  maluri ale unei v i adânci s-a construit un pod suspendat format din N buc µi
de scândur , legate cu liane.
Vom considera c  scândurile sunt numerotate de la 1 la N , începând de pe malul pe care ne
a m.
În timp unele buc µi de scândur  s-au deteriorat, iar altele chiar au disp rut.
Pentru traversarea podului se ³tie c :
 se pot face pa³i doar de lungime 1, 2 sau 3;
 scândurile deteriorate sunt nesigure, deci pe ele ³i de pe ele se pot face doar pa³i de lungime
1.
 evident, nu se poate p ³i pe o scândur  care lipse³te.

Cerinµ :
Scrieµi un program care s  determine num rul de modalit µi de traversare a podului (mai exact,
de a ajunge pe cel lalt mal), precum ³i o soluµie de traversare, dac  o astfel de soluµie exist .
Date de intrare:
Fi³ierul de intrare POD.IN are structura:
POD.IN Semnicaµie
N Num rul total de scânduri
k s1 s2 ... sk Num rul de scânduri lips  ³i numerele lor de ordine
h d1 d2 ... dh Num rul de scânduri deteriorate ³i numerele lor de ordine
Date de ie³ire:
Fi³ierul de ie³ire POD.OUT va conµine pe prima linie valoarea -1 dac  nu este posibil s 
travers m podul, respectiv num rul de posibilit µi de a traversa podul, dac  aceasta este posibil.
În cazul în care exist  soluµii, pe cea de a doua linie va  a³at  o astfel de soluµie, prin
indicarea, în ordine, a scândurilor pe care se p ³e³te, sub forma:
POD.OUT Semnicaµie
Nr Num rul total de posibilit µi
p1 p2 ... pm Soluµia determinat , prin indicarea în ordine a scândurilor
pe care se p ³e³te
Restricµii ³i preciz ri:
a 3 & N & 300
a 0 & k, h & N
a rs1 , s2 , ..., sk x N r2, ...N x,
a rd1 , d2 , ..., dh x N r1, 2, ...N x;
a rs1 , s2 , ..., sk x = rd1 , d2 , ..., dh x o
CAPITOLUL 38. ONI 2002 823

a N r are cel mult 80 de cifre.


Exemple:
pod.in pod.out pod.in pod.out pod.in pod.out
5 24 10 48 6 -1
0 3 227 368 224
0 15 13
Timp maxim de execuµie: 1 secund /test

38.2.1 Indicaµii de rezolvare

Soluµie prezentat  în GInfo 12/6


Rezolvarea problemei se bazeaz  pe o variant  simpl  a metodei program rii dinamice. Se
observ  foarte u³or c  num rul de posibilit µi de a ajunge pe cea de-a i-a scândur  depinde doar
de num rul de posibilit µi de a ajunge pe cele trei scânduri aate în faµa ei.
Vom nota cu ti num rul de posibilit µi de a ajunge pe cea de-a i-a scândur . Vom considera
malul opus ca ind cea de-a N  1-a scândur , unde N este num rul scândurilor care formeaz 
podul.
Soluµia problemei va  dat  de valoarea tN 1 .
În cazul în care cea de-a i-a scândur  lipse³te, pe ea nu se poate ajunge, deci vom avea ti 0.
În cazul în care aceast  scândur  exist , dar este deteriorat , pe ea se poate ajunge doar de
pe scândura precedent , deci vom avea ti ti1 .
În cazul în care scândura exist  ³i nu este deteriorat , pe ea se poate ajunge de pe scândura
anterioar  (chiar dac  este deteriorat ) sau de pe oricare dintre precedentele dou  (dac  nu sunt
deteriorate). Pentru a exprima relaµia matematic  pentru ti , vom folosi funcµia si denit  prin
si ti dac  a i-a scândur  exist  ³i nu este deteriorat  ³i si 0 în caz contrar.
Folosind aceast  funcµie, relaµia este ti si3  si2  ti1 .
Iniµial, vom avea t0 1, deoarece se poate spune c  exist  o singur  posibilitate de a ajunge
pe malul pe care ne a m.
Datorit  faptului c  valorile ti pot avea pân  la 80 de cifre, este necesar  simularea operaµiei
de adunare pentru numere mari.
Determinarea unei soluµii corecte se poate realiza destul de u³or dac , la ecare pas, p str m
indicele unei scânduri anterioare de pe care se poate trece pe scândura curent . Acest indice
va  e cel al scândurii anterioare (dac  aceasta exist  ³i num rul posibilit µilor de a ajunge la
ea este nenul), e al uneia dintre precedentele dou  (dac  exist , nu este deteriorat  ³i num rul
posibilit µilor de a ajunge la ea este nenul).
Dac  valoarea tN 1 este nenul , atunci exist  cu siguranµ  cel puµin o soluµie.
În nal, modalitatea de traversare va  generat  cu ajutorul unei tehnici recursive foarte simple.
Analiza complexit µii
Operaµia de adunare a numerelor mari are ordinul de complexitate O N Cif , unde N Cif este
num rul de cifre al celui mai mare dintre numerele care se adun . Deoarece N Cif este cel mult 80,
ordinul de complexitate al operaµiei de adunare poate  considerat a  O 80 80 O 1 O 1.
Trebuie efectuate cel mult 2 N astfel de adun ri, deci operaµia de determinare a valorilor ti are
ordinul de complexitate O N  O 1 O N .
Pentru reconstituirea drumului, la ecare pas trebuie p strat indicele unei scânduri precedente
de pe care se poate ajunge pe scândura curent . Exist  doar trei posibilit µi de a ajunge pe
scândura curent , deci ordinul de complexitate al acestei operaµii este O 1. Pentru determina-
rea scândurii anterioare corespunz toare ec rei scânduri din componenµa podului sunt efectuate
O N  astfel de operaµii, deci ordinul de complexitate al operaµiei de determinare a unei modalit µi
de traversare este O N .
Citirea datelor de intrare se realizeaz  într-un timp cu ordinul de compelexitate O N  deoarece
pot exista cel mult N  1 scânduri care lipsesc ³i cel mult N care sunt deteriorate.
Scrierea datelor de ie³ire const  în generarea unei modalit µi de traversare care are lungimea cel
mult N ³i a num rului modalit µilor de traversare. Deoarece determinarea scândurii precedente
se realizeaz  pe baza unor indici p straµi pentru ecare scândur  în parte, ordinul de complexitate
al acestei operaµii este O 1. Datorit  faptului c  vor  cel mult N astfel de determin ri, ordinul
de complexitate al operaµiei de determinare a modalit µii de traversare este O N . Scrierea
num rului posibilit µilor de traversare poate  considerat a  o operaµie elementar , deci are
ordinul de complexitate O 1. A³adar, ordinul de complexitate al operaµiei de scriere a datelor de
ie³ire este O(N) + O(1) = O(N).
CAPITOLUL 38. ONI 2002 824

În concluzie, algoritmul de rezolvare al acestei probleme are ordinul de complexitate O N  


O N   O N   O N  O N .

38.2.2 Cod surs 

Listing 38.2.1: PODBACK.CPP


1 #include <fstream>
2
3 using namespace std;
4
5 #define NMax 1001
6 #define nfout "9.b"
7 #define nfin "9.in"
8
9 int n, uz[NMax], s[NMax], p[NMax], sol[NMax], lgsol;
10 long NrSol;
11
12 void Afisare();
13 void Gen(int);
14 void Citire();
15
16 int main()
17 {
18 Citire();
19 p[0]=1;
20 Gen(1);
21 if (!s[2]) {p[0]=2; Gen(1);}
22 if (!s[3]) {p[0]=3; Gen(1);}
23 Afisare();
24 return 0;
25 }
26
27 void Afisare()
28 {
29 ofstream fout(nfout);
30 if (!NrSol) fout<<-1<<endl;
31 else
32 {fout<<NrSol<<endl;
33 for (int i=0; i<lgsol; i++)
34 fout<<sol[i]<<’ ’;
35 fout<<endl;
36 }
37 fout.close();
38 }
39
40 void Citire()
41 {
42 ifstream f(nfin);
43 int x, k, h;
44 f>>n>>k;
45 for(int i=0;i<k;i++) {f>>x; s[x]=1;}
46 //s[x]=1 daca scandura x lipseste si 0 altfel
47 f>>h;
48 for(int i=0;i<h;i++) {f>>x; uz[x]=1;}
49 //uz[x]=1 daca scandura x este deteriorata si 0 altfel
50 f.close();
51 }
52
53 void GasitSol(int k)
54 {
55 NrSol++;
56 if (NrSol==1)
57 {lgsol=k;
58 for (int i=0; i<lgsol; i++) sol[i]=p[i];}
59 }
60
61 void Gen(int k)
62 {
63 if (p[k-1]==n) GasitSol(k);
64 else
65 if (p[k-1]==n-1)
66 {if (!uz[n-1]) GasitSol(k);
CAPITOLUL 38. ONI 2002 825

67 if (!s[n]) {p[k]=n; Gen(k+1);}


68 }
69 else
70 if (p[k-1]==n-2)
71 {
72 if (!uz[n-2]) GasitSol(k); //sar direct
73 if (!s[n-1]) {p[k]=n-1; Gen(k+1);}
74 if (!s[n] && !uz[n] && !uz[n-2]) {p[k]=n; Gen(k+1);}
75 }
76 else
77 {//mai sar
78 if (!s[p[k-1]+1])
79 {
80 p[k]=p[k-1]+1;
81 Gen(k+1);
82 }
83 if (!s[p[k-1]+2] && !uz[p[k-1]+2] &&!uz[p[k-1]])
84 {
85 p[k]=p[k-1]+2;
86 Gen(k+1);
87 }
88 if (!s[p[k-1]+3] && !uz[p[k-1]+3] &&!uz[p[k-1]])
89 {
90 p[k]=p[k-1]+3;
91 Gen(k+1);
92 }
93 }
94 }

Listing 38.2.2: PODDBIG.CPP


1 #include <fstream>
2 #include <string.h>
3
4 using namespace std;
5
6 #define NMax 500
7 #define LgMax 101
8 #define nfout "9.out"
9 #define nfin "9.in"
10
11 int n, uz[NMax], s[NMax], poz[NMax];
12 typedef char Big[LgMax];
13 Big Nr[NMax];
14
15 void Afisare();
16 void Rezolva();
17 void Citire();
18 void Sum (Big &, Big &);
19
20 int main()
21 {
22 Citire();
23 Rezolva();
24 Afisare();
25 return 0;
26 }
27
28 void Afisare()
29 {
30 Big Total="0";
31 int d[NMax], final, lg;
32
33 if (!s[n]) {Sum(Total,Nr[n]); final=n;}
34 if (strcmp(Nr[n-1],"-1") && !uz[n-1]) {Sum(Total,Nr[n-1]); final=n-1;}
35 if (strcmp(Nr[n-2],"-1") && !uz[n-2]) {Sum(Total,Nr[n-2]); final=n-2;}
36
37 ofstream fout(nfout);
38
39 if (strcmp(Total,"0"))
40 fout<<-1<<endl;
41 else
42 {fout<<strrev(Total)<<endl;
43 d[0]=final; lg=1;
44 while (poz[final]!=0) {d[lg++]=poz[final]; final=poz[final];}
CAPITOLUL 38. ONI 2002 826

45 for (final=lg-1; final>=0; final--)


46 fout<<d[final]<<’ ’;
47 fout<<endl;
48 }
49
50 fout.close();
51 }
52
53 void Citire()
54 {
55 ifstream f(nfin);
56 int x, k, h;
57 f>>n>>k;
58 for(int i=0;i<k;i++) {f>>x; s[x]=1;}
59 //s[x]=1 daca scandura x lipseste si 0 altfel
60 f>>h;
61 for(int i=0;i<h;i++) {f>>x; uz[x]=1;}
62 //uz[x]=1 daca scandura x este deteriorata si 0 altfel
63 f.close();
64 }
65
66 void Rezolva()
67 {
68 strcpy(Nr[1],"1"); poz[1]=0;
69 if (s[2]) strcpy(Nr[2],"-1");
70 else {strcpy(Nr[2],"2"); poz[2]=0;}
71 if (s[3]) strcpy(Nr[3],"-1");
72 else
73 if (s[2]) {strcpy(Nr[3],"2"); poz[3]=0;}
74 else {strcpy(Nr[3],"4"); poz[3]=0;}
75 for (int i=4; i<=n; i++)
76 if (s[i]) strcpy(Nr[i],"-1");
77 else
78 if (uz[i]) { strcpy(Nr[i],Nr[i-1]); poz[i]=i-1;}
79 else
80 {strcpy(Nr[i],"0");
81 if (strcmp(Nr[i-1],"-1"))
82 {strcpy(Nr[i],Nr[i-1]);poz[i]=i-1;}
83 if (strcmp(Nr[i-2],"-1") && !uz[i-2])
84 {Sum(Nr[i],Nr[i-2]);poz[i]=i-2;}
85 if (strcmp(Nr[i-3],"-1") && !uz[i-3])
86 {Sum(Nr[i],Nr[i-3]);poz[i]=i-3;}
87 if (!strcmp(Nr[i],"0"))
88 strcpy(Nr[i],"-1");}
89 }
90
91 void Sum (Big & A, Big & B)
92 {
93 //adun pe B la A
94 int t=0, cifra, i;
95 for (i=0; A[i] && B[i]; i++)
96 {cifra = (A[i]-’0’+B[i]-’0’+t);
97 A[i]=cifra%10+’0’;
98 t=cifra/10;}
99 for (; A[i]; i++)
100 {cifra = (A[i]-’0’+t);
101 A[i]=cifra%10+’0’;
102 t=cifra/10;}
103 for (; B[i]; i++)
104 {cifra = (B[i]-’0’+t);
105 A[i]=cifra%10+’0’;
106 t=cifra/10;}
107 A[i]=NULL;
108 if (t) {A[i]=t+’0’; A[i+1]=NULL;}
109 }

Listing 38.2.3: PODIN.CPP


1 #include <fstream>
2
3 using namespace std;
4
5 #define NMax 1000
6 #define nfout "9.o"
7 #define nfin "9.in"
CAPITOLUL 38. ONI 2002 827

8
9 int n, uz[NMax], s[NMax], poz[NMax];
10 long Nr[NMax];
11
12 void Afisare();
13 void Rezolva();
14 void Citire();
15
16 int main()
17 {
18 Citire();
19 Rezolva();
20 Afisare();
21 return 0;
22 }
23
24 void Afisare()
25 {
26 long Total=0;
27 int d[NMax], final, lg;
28 if (!s[n]) {Total+=Nr[n]; final=n;}
29 if (Nr[n-1]!=-1 && !uz[n-1]) {Total+=Nr[n-1]; final=n-1;}
30 if (Nr[n-2]!=-1 && !uz[n-2]) {Total+=Nr[n-2]; final=n-2;}
31 if (!Total) Total=-1;
32 ofstream fout(nfout);
33 fout<<Total<<endl;
34 if (Total!=-1)
35 {d[0]=final; lg=1;
36 while (poz[final]!=0) {d[lg++]=poz[final]; final=poz[final];}
37 for (final=lg-1; final>=0; final--)
38 fout<<d[final]<<’ ’;
39 fout<<endl;
40 }
41 fout.close();
42 }
43
44 void Citire()
45 {
46 ifstream f(nfin);
47 int x, k, h, i;
48 f>>n>>k;
49 for(int i=0;i<k;i++) {f>>x; s[x]=1;}
50 //s[x]=1 daca scandura x lipseste si 0 altfel
51 f>>h;
52 for(i=0;i<h;i++) {f>>x; uz[x]=1;}
53 //uz[x]=1 daca scandura x este deteriorata si 0 altfel
54 f.close();
55 }
56
57 void Rezolva()
58 {
59 Nr[1]=1; poz[1]=0;
60 if (s[2]) Nr[2]=-1;
61 else {Nr[2]=2; poz[2]=0;}
62 if (s[3]) Nr[3]=-1;
63 else
64 if (s[2]) {Nr[3]=2; poz[3]=0;}
65 else {Nr[3]=4; poz[3]=0;}
66 for (int i=4; i<=n; i++)
67 if (s[i]) Nr[i]=-1;
68 else
69 if (uz[i]) { Nr[i]=Nr[i-1]; poz[i]=i-1;}
70 else
71 {Nr[i]=0;
72 if (Nr[i-1]!=-1) {Nr[i]=Nr[i-1];poz[i]=i-1;}
73 if (Nr[i-2]!=-1 && !uz[i-2]) {Nr[i]+=Nr[i-2];poz[i]=i-2;}
74 if (Nr[i-3]!=-1 && !uz[i-3]) {Nr[i]+=Nr[i-3];poz[i]=i-3;}
75 if (!Nr[i]) Nr[i]=-1;}
76 }

38.2.3 Rezolvare detaliat 

Listing 38.2.4: pod1.java


CAPITOLUL 38. ONI 2002 828

1 import java.io.*;
2 class Pod1
3 {
4 static final int buna=0, deteriorata=1, lipsa=2;
5 static int ns, nsl,nsd;
6 static int nsol;
7 static int[] x;
8 static int[] y;
9 static int[] p;
10
11 public static void main(String[] args) throws IOException
12 {
13 int i,j;
14 StreamTokenizer st=new StreamTokenizer(new BufferedReader(
15 new FileReader("pod.in")));
16 PrintWriter out=new PrintWriter(new BufferedWriter(
17 new FileWriter("pod.out")));
18
19 st.nextToken(); ns=(int)st.nval;
20 x=new int[ns+2];
21 y=new int[ns+2]; // nsol[i] ajunge in i
22 p=new int[ns+2]; // predecesor
23
24 st.nextToken();nsl=(int)st.nval; // lipsa
25 for(i=1;i<=nsl;i++)
26 {
27 st.nextToken(); j=(int)st.nval;
28 x[j]=lipsa;
29 }
30
31 st.nextToken();nsd=(int)st.nval; // deteriorate
32 for(i=1;i<=nsd;i++)
33 {
34 st.nextToken(); j=(int)st.nval;
35 x[j]=deteriorata;
36 }
37 afisv(x); System.out.println();
38
39 y[1]=0;
40 if(x[1]!=lipsa) y[1]+=1; // 1 pas de pe mal
41 afisv(y);
42
43 y[2]=0;
44 if(x[2]==deteriorata) y[2]+=y[1]; // 1 pas din 1
45 else if(x[2]==buna) y[2]+=y[1]+1; // 1 pas din 1 si 2 pasi de pe mal
46 if(y[2]>0) if(y[1]>0) p[2]=1;
47 else p[2]=0;
48 afisv(y);
49
50 y[3]=0;
51 if(x[3]==deteriorata)
52 {
53 if(x[2]!=lipsa) y[3]=y[2]+1; // 1 pas din 2
54 if(x[1]==buna) y[3]=y[3]+1; // 2 pasi din 1
55 }
56 else if(x[3]==buna)
57 {
58 y[3]+=1; // 3 pasi de pe mal;
59 if(x[1]==buna) y[3]+=y[1]; // 2 pasi din 1
60 if(x[2]!=lipsa) y[3]+=y[2]; // 1 pas din 2
61 }
62 afisv(y); System.out.println();
63
64 for(i=4;i<=ns+1;i++)
65 {
66 switch(x[i])
67 {
68 case lipsa:
69 y[i]=0; break;
70 case deteriorata:
71 if(x[i-1]!=lipsa) y[i]=y[i-1]; // 1 pas din i-1
72 break;
73 case buna:
74 y[i]=0; // pentru suma
75 if(x[i-3]==buna) y[i]+=y[i-3]; // 3 pasi din i-3
CAPITOLUL 38. ONI 2002 829

76 if(x[i-2]==buna) y[i]+=y[i-2]; // 2 pasi din i-2


77 if(x[i-1]!=lipsa) y[i]+=y[i-1]; // 1 pas din i-1
78 }
79 afisv(y);
80 }
81 }// main
82
83 static void afisv(int[] a)
84 {
85 int i;
86 for(i=1;i<=ns+1;i++) System.out.print(a[i]+" ");
87 System.out.println();
88 }
89 }// class

Listing 38.2.5: pod2.java


1 import java.io.*;
2 class Pod2
3 {
4 static final int buna=0, deteriorata=1, lipsa=2;
5 static int ns, nsl,nsd;
6 static int nsol;
7 static int[] x;
8 static int[] y;
9 static int[] p;
10
11 public static void main(String[] args) throws IOException
12 {
13 int i,j;
14 StreamTokenizer st=new StreamTokenizer(new BufferedReader(
15 new FileReader("pod.in")));
16 PrintWriter out=new PrintWriter(new BufferedWriter(
17 new FileWriter("pod.out")));
18
19 st.nextToken();ns=(int)st.nval;
20 x=new int[ns+2];
21 y=new int[ns+2]; // nsol[i] ajunge in i
22 p=new int[ns+2]; // predecesor
23
24 st.nextToken();nsl=(int)st.nval; // lipsa
25 for(i=1;i<=nsl;i++)
26 {
27 st.nextToken(); j=(int)st.nval;
28 x[j]=lipsa;
29 }
30
31 st.nextToken();nsd=(int)st.nval; // deteriorate
32 for(i=1;i<=nsd;i++)
33 {
34 st.nextToken(); j=(int)st.nval;
35 x[j]=deteriorata;
36 }
37 afisv(x); System.out.println();
38
39 y[1]=0;
40 if(x[1]!=lipsa) y[1]+=1; // 1 pas de pe mal
41 afisv(y);
42
43 y[2]=0;
44 if(x[2]==deteriorata) y[2]+=y[1]; // 1 pas din 1
45 else if(x[2]==buna) y[2]+=y[1]+1; // 1 pas din 1 si 2 pasi de pe mal
46 if(y[1]>0) p[2]=1;
47 afisv(y);
48
49 y[3]=0;
50 if(x[3]==deteriorata)
51 {
52 if(x[2]!=lipsa) y[3]=y[2]+1; // 1 pas din 2
53 if(x[1]==buna) y[3]=y[3]+1; // 2 pasi din 1
54 }
55 else if(x[3]==buna)
56 {
57 y[3]+=1; // 3 pasi de pe mal;
58 if(x[1]==buna) y[3]+=y[1]; // 2 pasi din 1
CAPITOLUL 38. ONI 2002 830

59 if(x[2]!=lipsa) y[3]+=y[2]; // 1 pas din 2


60 }
61 afisv(y); System.out.println();
62
63 if(y[1]>0) p[3]=1;
64 else if(y[2]>0) p[3]=2;
65
66 for(i=4;i<=ns+1;i++)
67 {
68 switch(x[i])
69 {
70 case lipsa:
71 y[i]=0; break;
72 case deteriorata:
73 if(x[i-1]!=lipsa) y[i]=y[i-1];// 1 pas din i-1
74 if(y[i-1]>0) p[i]=i-1;
75 break;
76 case buna:
77 y[i]=0; // pentru suma
78 if(x[i-3]==buna) y[i]+=y[i-3]; // 3 pasi din i-3
79 if(x[i-2]==buna) y[i]+=y[i-2]; // 2 pasi din i-2
80 if(x[i-1]!=lipsa) y[i]+=y[i-1]; // 1 pas din i-1
81 if(y[i-3]>0) p[i]=i-3;
82 else if(y[i-2]>0) p[i]=i-2;
83 else if(y[i-1]>0) p[i]=i-1;
84 }
85 afisv(y);
86 }
87
88 System.out.println();
89 drum(ns+1);
90 System.out.println();
91 }// main
92
93 static void drum(int j)
94 {
95 if(p[j]!=0) drum(p[j]);
96 System.out.print(j+" ");
97 }
98
99 static void afisv(int[] a)
100 {
101 int i;
102 for(i=1;i<=ns+1;i++) System.out.print(a[i]+" ");
103 System.out.println();
104 }
105 }// class

Listing 38.2.6: pod3.java


1 import java.io.*;
2 class Pod3
3 {
4 static final int buna=0, deteriorata=1, lipsa=2;
5 static int ns, nsl,nsd;
6 static int nsol;
7 static int[] x; // tip scandura
8 static int[][] y; // y[i]=nr variante de a ajunge in i
9 static int[] p; // p[i]=predecesorul lui i
10 static PrintWriter out;
11
12 public static void main(String[] args) throws IOException
13 {
14 int i,j;
15 StreamTokenizer st=new StreamTokenizer(new BufferedReader(
16 new FileReader("pod.in")));
17 out=new PrintWriter(new BufferedWriter(
18 new FileWriter("pod.out")));
19
20 st.nextToken();ns=(int)st.nval;
21 x=new int[ns+2];
22 y=new int[ns+2][1]; // nsol[i] ajunge in i
23 p=new int[ns+2]; // predecesor
24
25 st.nextToken();nsl=(int)st.nval; // lipsa
CAPITOLUL 38. ONI 2002 831

26 for(i=1;i<=nsl;i++)
27 {
28 st.nextToken(); j=(int)st.nval;
29 x[j]=lipsa;
30 }
31
32 st.nextToken();nsd=(int)st.nval; // deteriorate
33 for(i=1;i<=nsd;i++)
34 {
35 st.nextToken(); j=(int)st.nval;
36 x[j]=deteriorata;
37 }
38
39 if(x[1]!=lipsa) y[1]=nrv(1); // 1 pas de pe mal
40
41 y[2]=nrv(0);
42 if(x[2]==deteriorata) y[2]=suma(y[2],y[1]); // 1 pas din 1
43 else if(x[2]==buna)
44 y[2]=suma(y[2],suma(y[1],nrv(1)));
45 if(ok(y[1])) p[2]=1;
46
47 y[3]=nrv(0);
48 if(x[3]==deteriorata)
49 {
50 if(x[2]!=lipsa) y[3]=suma(y[2],nrv(1)); // 1 pas din 2
51 if(x[1]==buna) y[3]=suma(y[3],nrv(1)); // 2 pasi din 1
52 }
53 else if(x[3]==buna)
54 {
55 y[3]=suma(y[3],nrv(1)); // 3 pasi de pe mal;
56 if(x[1]==buna) y[3]=suma(y[3],y[1]); // 2 pasi din 1
57 if(x[2]!=lipsa) y[3]=suma(y[3],y[2]); // 1 pas din 2
58 }
59
60 if(ok(y[1])) p[3]=1;
61 else if(ok(y[2])) p[3]=2;
62
63 for(i=4;i<=ns+1;i++)
64 {
65 switch(x[i])
66 {
67 case lipsa:
68 y[i]=nrv(0); break;
69 case deteriorata:
70 if(x[i-1]!=lipsa) y[i]=suma(nrv(0),y[i-1]);// 1 pas din i-1
71 if(ok(y[i-1])) p[i]=i-1;
72 break;
73 case buna:
74 y[i]=nrv(0); // pentru
suma
75 if(x[i-3]==buna) y[i]=suma(y[i],y[i-3]); // 3 pasi din i-3
76 if(x[i-2]==buna) y[i]=suma(y[i],y[i-2]); // 2 pasi din i-2
77 if(x[i-1]!=lipsa) y[i]=suma(y[i],y[i-1]); // 1 pas din i-1
78 if(ok(y[i-3])) p[i]=i-3;
79 else if(ok(y[i-2])) p[i]=i-2;
80 else if(ok(y[i-1])) p[i]=i-1;
81 }
82 }
83 afisv(y[ns+1]);
84 drum(ns+1);
85 out.println();
86 out.close();
87 }// main
88
89 static boolean ok(int[] z)
90 {
91 int i;
92 for(i=0;i<z.length;i++) if(z[i]!=0) return true;
93 return false;
94 }
95
96 static void drum(int j)
97 {
98 if(p[j]!=0) drum(p[j]);
99 out.print(j+" ");
100 }
CAPITOLUL 38. ONI 2002 832

101
102 static void afisv(int[] x)
103 {
104 int nx=x.length;
105 int i;
106 for(i=nx-1;i>=0;i--) out.print(x[i]);
107 out.println();
108 }
109
110 static int[] nrv(int nr)
111 {
112 int nc;
113 int nrrez=nr;
114 nc=0;
115 while(nr!=0) {nc++; nr=nr/10;}
116 int[] x=new int[nc];
117 nr=nrrez;
118 nc=0;
119 while(nr!=0) { x[nc]=nr%10; nc++; nr=nr/10; }
120 return x;
121 }
122
123 static int[] suma(int[] x,int[] y)
124 {
125 int k,s,t;
126 int nx=x.length;
127 int ny=y.length;
128 int nz;
129 if(nx>ny)nz=nx+1; else nz=ny+1;
130 int[] z=new int[nz];
131 t=0;
132 for(k=0;k<nz;k++)
133 {
134 s=t;
135 if(k<nx) s=s+x[k];
136 if(k<ny) s=s+y[k];
137 z[k]=s%10;
138 t=s/10;
139 }
140 if(z[nz-1]!=0)return z;
141 else
142 {
143 int[] zz=new int[nz-1];
144 for(k=0;k<nz-1;k++) zz[k]=z[k];
145 return zz;
146 }
147 }
148 }// class

38.3 Suma
Florin Gheµu, Bucure³ti
Fie ³irul tuturor numerelor naturale de la 1 la un num r oarecare N .
Considerând asociate câte un semn (+ sau -) ec rui num r ³i adunând toate aceste numere
cu semn se obµine o sum  S .
Problema const  în a determina pentru o sum  dat  S , num rul minim N pentru care, printr-o
asociere de semne tuturor numerelor de la 1 la N , se poate obµine S .
Cerinµ :
Pentru un S dat, g siµi valoarea minim  N ³i asocierea de semne numerelor de la 1 la N pentru
a obµine S în condiµiile problemei.
Restricµii:
a 0$S & 100.000.
Date de intrare
În ³ierul SUMA.IN se va aa pe prima linie un întreg pozitiv S reprezentând suma ce trebuie
obµinut .

Date de ie³ire
CAPITOLUL 38. ONI 2002 833

În ³ierul SUMA.OUT se va scrie, pe prima linie num rul minim N pentru care se poate obµine
suma S iar pe urm toarele linii, pân  la sfâr³itul ³ierului, numerele care au semn negativ, câte
unul pe linie.
Ordinea de a³are a numerelor nu are importanµ .
Celelalte numere care nu apar în ³ier se consider  pozitive.
Dac  exist  mai multe soluµii se cere doar una.
Exemplu:
SUMA.IN SUMA.OUT
12 7
1
7
Deci suma 12 se poate obµine din minimum 7 termeni astfel:
12 1  2  3  4  5  6  7.
Nu este singura posibilitate de asociere de semne termenilor de la 1 la 7.
Timpul de execuµie: 1 secund /test

38.3.1 Indicaµii de rezolvare

Soluµie prezentat  în GInfo 12/6


Pentru ca num rul termenilor s  e minim, trebuie ca suma termenilor care apar cu semn
negativ s  e cât mai mic  posibil.
Iniµial vom presupune c  nu va trebui s  sc dem nici o valoare ³i ne propunem s  g sim cel
mai mic num r N pentru care
sN < i 1 i ' S.
N

Valoarea N se obµine rezolvând ecuaµia de gradul II SN S (vom avea SN & S  N ) ³i


rotunjind prin adaos r d cina pozitiv  obµinut . Avem:Ó
N N 1
2
S 2
N N 2 S 0
Ó
N 18 S 1
2
A³adar, valoarea N este - 3.
Datorit  faptului c  N este cea mai mic  valoare pentru
18 S 1
2
care suma atinge sau dep ³e³te valoarea S , soluµia problemei nu poate  dat  de nici un num r
N $ N.
¬

În cele ce urmeaz  vom demonstra c  soluµia problemei este dat  de N , N  1 sau N  2.


Se observ  c  numerele SN , SN 1 ³i SN 2 nu pot avea toate aceea³i paritate, deoarece N  1 ³i
N  2 au parit µi diferite. Vom nota prin DN diferenµa dintre valoarea SN ³i valoarea care trebuie
obµinut  (S ). Deoarece se scade aceea³i valoare din SN , SN 1 ³i SN 2 este evident c  DN , DN 1
³i DN 2 nu pot avea toate aceea³i paritate.
Soluµia problemei este dat  de indicele celui mai mic num r par dintre aceste trei numere; e
¬
acest indice N .
Suma total  a elementelor c rora trebuie s  le e modicat semnul este DN ¬ ©2. Prin mo-
dicarea semnului unui element, valoarea expresiei scade cu dublul acestui element, deci avem
SN ¬  DN ¬ ©2 2 S .
Fie x DN ¬ ©2 (suma total  a elementelor care trebuie sc zute). Deoarece N $ N  2, obµinem
¬

SN ¬ & SN  N  1  N  2. Folosind relaµia SN & S  N , se obµine relaµia SN ¬ & S  3 N  3.


¬
Cu alte cuvinte, avem x & 3 N23 & 3 2N . A³adar, valoarea x poate  obµinut  alegând doar dou 
¬
numere cuprinse între 1 ³i N .
În cazul în care x % N vom sc dea N ³i N  x, iar în caz contrar vom sc dea doar x.
¬ ¬ ¬

Analiza complexit µii
Valoarea N poate  obµinut  folosind o simpl  formul  matematic , deci ordinul de complexi-
tate al acestei operaµii este O 1.
Valorile SN , SN 1 , SN 2 , DN , DN 1 , DN 2 sunt calculate tot cu ajutorul unor formule ma-
tematice simple, a³adar ordinul de complexitate al operaµiei de determinare a acestora este tot
O 1.
¬
Identicarea valorii N se face pe baza veric rii parit µii a cel mult trei numere, deci ³i aceast 
operaµie are ordinul de complexitate O 1.
Determinarea valorii x ³i a celor cel mult dou  numere c rora li se va modica semnul este
realizat  tot pe baza unor calcule simple al c ror ordin de complexitate este O 1.
În concluzie, ordinul de complexitate al unui algoritm ecient de rezolvare a acestei probleme
este O 1  O 1  O 1  O 1 O 1.
CAPITOLUL 38. ONI 2002 834

38.3.2 Cod surs 

Listing 38.3.1: suma.pas


1 const fi=’suma.in’; fo=’suma.out’;
2 var s,n,d:longint;
3
4 procedure sol(n,i:longint);
5 begin
6 writeln(n);
7 if i>0 then
8 while i>0 do
9 if i<=n then begin
10 writeln(i); i:=0;
11 end else begin
12 writeln(n); dec(i,n); dec(n);
13 end;
14 end;
15
16 begin
17 assign(input,fi); reset(input);
18 read(s); close(input);
19 assign(output,fo); rewrite(output);
20 n:=trunc((sqrt(1+8*s)-1)/2);
21 if (n*(n+1))div 2<s then inc(n);
22 d:=(n*(n+1))div 2-s;
23 if d mod 2=0 then sol(n,d div 2)
24 else begin
25 inc(n);
26 d:=(n*(n+1))div 2-s;
27 if d mod 2=0 then sol(n,d div 2)
28 else begin
29 inc(n);
30 d:=(n*(n+1))div 2-s;
31 sol(n,d div 2);
32 end;
33 end; close(output);
34 end.

38.3.3 Rezolvare detaliat 

Listing 38.3.2: suma.java


1 import java.io.*;
2 class Suma
3 {
4 public static void main(String[] args) throws IOException
5 {
6 int n=0,suma=0,s;
7 int np;
8 StreamTokenizer st=new StreamTokenizer(
9 new BufferedReader(new FileReader("suma.in")));
10 PrintWriter out=new PrintWriter(
11 new BufferedWriter(new FileWriter("suma.out")));
12 st.nextToken(); s=(int)st.nval;
13
14 while((suma<s)||((suma-s)%2==1))
15 {
16 n++;
17 suma=suma+n;
18 }
19 out.println(n);
20 np=(suma-s)/2;
21 if(np>0)
22 if(np<=n) out.println(np);
23 else out.println((np-n)+"\n"+n);
24 out.close();
25 }
26 }
27 \end{verbatim}
CAPITOLUL 38. ONI 2002 835

28
29 %-----------------------------------------------
30
31
32 \section{Becuri}
33 \hspace{6cm} \hfill {\it prof. Cristina B\u arbieru, Timi\c soara}
34
35 \hspace{0.66cm}
36 Un panou publicitar, de form\u a dreptunghiular\u a con\c tine becuri, unul l\^ang\u a
altul, aliniate pe linii \c si coloane.
37
38 Fiecare linie \c si fiecare coloan\u a are un comutator care schimb\u a starea tuturor
becurilor de pe acea linie sau coloan\u a, din starea \^in care se afl\u a \^in
starea opus\u a (stins/aprins, aprins/stins). Asupra unui bec nu se poate ac\c tiona
individual. Ini\c tial panoul are toate becurile stinse.
39
40 \vspace{0.2cm}
41 {\bf Cerin\c t\u a:}
42
43 S\u a se realizeze un program care ac\c t ion\^and asupra unui num\u ar minim de linii \
c si coloane aduce panoul din starea ini\c tial\u a, la o configura\c tie dat\u a,
dac\u a acest lucru este posibil.
44
45 \vspace{0.2cm}
46 {\bf Datele de intrare}
47
48 se vor citi din fi\c sierul BECURI.IN, care are urm\u atoarea configura\c tie:
49
50 $\bullet$ pe prima linie dou\u a numere naturale, $m$ \c si $n$, separate prin spa\c tiu
, repre\-zen\-t\^and num\u arul de linii \c si de coloane ale panoului;
51
52 $\bullet$ pe urm\u atoarele $m$ linii c\^ate $n$ coloane, formate din elementele $0$ sau
$1$, se\-pa\-rate prin spa\c tiu, reprezent\^and configura\c tia final\u a a
panoului. $1$ este codificarea unui bec aprins iar $0$ a unui bec stins.
53
54 \vspace{0.2cm}
55 {\bf Datele de ie\c sire}
56
57 se va afi\c sa o solu\c tie \^in fi\c sierul BECURI.OUT astfel:
58
59 $\bullet$ pe prima linie a fi\c sierului indicii coloanelor asupra c\u arora s-a ac\c
tionat, separa\c ti prin spa\c tiu.
60
61 $\bullet$ pe a doua linie a fi\c sierului indicii linilor asupra c\u arora s-a ac\c
tionat, separa\c ti prin spa\c tiu.
62
63 Dac\u a nu se ac\c tioneaz\u a asupra liniilor sau coloanelor se va afi\c sa $0$ pe
linia corespunz\u atoare.
64
65 Numerotarea liniilor, respectiv coloanelor \^incepe de la 1.
66
67 Dac\u a problema nu are solu\c tie, pe prima linie a fi\c sierului se va afi\c sa $-1$.
68
69 \vspace{0.2cm}
70 {\bf Restrictii}
71
72 $\bullet$ $m \le 100, n \le 100$
73
74 \vspace{0.2cm}
75 {\bf Exemplu:}
76
77 \begin{tabular}{|l|l|}
78 \hline
79 BECURI.IN & BECURI.OUT \\
80 \hline
81 5 6 & 2 5 \\
82 1 0 1 1 0 1 & 1 2 3 \\
83 1 0 1 1 0 1 & \\
84 1 0 1 1 0 1 & \\
85 0 1 0 0 1 0 & \\
86 0 1 0 0 1 0 & \\
87 \hline
88 \end{tabular}
89
90 \vspace{0.2cm}
91 {\bf Timp maxim de execu\c tie:} 1 secund\u a/test
CAPITOLUL 38. ONI 2002 836

92
93
94 \subsection{Indica\c tii de rezolvare}
95 \hspace{0.66cm}
96
97 {\it Solu\c tie prezentat\u a \^in GInfo 12/6}
98
99 Este evident c\u a pentru a ob\c tine prima linie a matricei nu putem dec\^at fie s\u a
comut\u am toate coloanele pe care trebuie s\u a se afle becuri aprinse \c si s\u a
nu comut\u am prima linie, fie s\u a comut\u am prima linie \c si s\u a comut\u am
toate coloanele pe care trebuie s\u a se afle becuri stinse.
100
101 Analog, pentru prima coloan\u a putem fie s\u a comut\u am toate liniile pe care trebuie
s\u a se afle becuri aprinse \c si s\u a nu comut\u am prima coloan\u a, fie s\u a
comut\u am prima coloan\u a \c si s\u a comut\u am toate liniile pe care trebuie s\u
a se afle becuri stinse.
102
103 Astfel avem patru posibilit\u a\c ti de a ob\c tine configura\c tia cerut\u a:
104
105 $\bullet$ comut\u am liniile c\u arora le corespund becuri aprinse pe prima coloan\u a \
c si coloanele c\u arora le corespund becuri aprinse pe prima linie;
106
107 $\bullet$ comut\u am liniile c\u arora le corespund becuri aprinse pe prima coloan\u a \
c si coloanele c\u arora le corespund becuri stinse prima linie;
108
109 $\bullet$ comut\u am liniile c\u arora le corespund becuri stinse pe prima coloan\u a \c
si coloanele c\u arora le corespund becuri aprinse pe prima linie;
110
111 $\bullet$ comut\u am liniile c\u arora le corespund becuri stinse pe prima coloan\u a \c
si coloanele c\u arora le corespund becuri stinse pe prima linie.
112
113 Vom verifica fiecare dintre aceste patru variante \c si apoi o vom alege pe cea care
114 implic\u a cele mai pu\c tine comut\u ari de linii \c si coloane.
115
116 Dac\u a nici una dintre cele patru variante nu duce la ob\c tinerea configura\c tiei
cerute, putem trage concluzia c\u a aceasta nu poate fi ob\c tinut\u a prin
comutarea unor linii \c si a unor coloane.
117
118 {\bf Analiza complexit\u a\c tii}
119
120 Citirea datelor de intrare implic\u a parcurgerea unei matrice p\u atratice, deci
ordinul de complexitate al acestei opera\c tii este $O(N^2)$.
121
122 Verificarea fiec\u areia dintre cele patru posibilit\u a\c ti de a ob\c tine solu\c tia
implic\u a parcurgerea primei linii \c si a primei coloane; ordinul de complexitate
al unei astfel de parcurgere este $O(N) + O(N) = O(N)$.
123
124 Pentru fiecare element este posibil ca linia/coloana corespunz\u atoare s\u a fie
comutat\u a. Opera\c tia de comutare implic\u a traversarea liniei sau coloanei, a\c
sadar are ordinul de complexitate $O(N)$. Ca urmare, ordinul de complexitate al
fiec\u areia dintre cele patru
125 posibilit\u a\c ti este $O(N) \cdot O(N) = O(N^2)$. \^Intreaga opera\c tie de
determinare a solu\c tiei are ordinul de complexitate $4 \cdot O(N^2) = O(N^2)$.
126
127 Scrierea datelor de ie\c sire implic\u a traversarea \c sirurilor care indic\u a dac\u a
o linie sau coloan\u a a fost comutat\u a; ordinul de complexitate al opera\c tiei
este $O(N)$.
128
129 \^In concluzie, algoritmul de rezolvare al acestei probleme are ordinul de complexitate
$O(N^2) + O(N^2) + O(N) = O(N^2)$.
130
131
132
133 %-----------------------------------------------
134
135 \subsection{Cod surs\u a}
136 \hspace{0.66cm}
137
138 \begin{lstlisting}[language=C++,commentstyle=\color{purple}, caption={\hspace{5mm}
becbun.cpp}]
139 #include <iostream>
140 #include <conio.h>
141 #include <stdio.h>
142
143 using namespace std;
144
CAPITOLUL 38. ONI 2002 837

145 int a[101][101],m,n,k,l0,l1,c0,c1,x,y;


146 int s[4];
147 FILE *g;
148
149 int generare(int lin, int col)
150 {
151 int ax[101][101],i,j,ok;
152 for(i=1;i<=m;i++)
153 for(j=1;j<=n;j++)
154 ax[i][j]=0;
155 for(j=1;j<=n;j++)//pun coloanele care trebuie pe 1
156 if(a[1][j]==lin)
157 for(i=1;i<=m;i++)
158 ax[i][j]=1;
159 for(i=1;i<=m;i++)//comut liniile care trebuie
160 if(a[i][1]==col)
161 for(j=1;j<=n;j++){
162 if(ax[i][j]==0) ax[i][j]=1;
163 else ax[i][j]=0;
164 }
165 //verificare
166 ok=1;
167 for(i=1;i<=m;i++)
168 for(j=1;j<=n;j++){
169 if(ax[i][j]!=a[i][j])
170 ok=0;
171 }
172 return ok;
173 }
174
175 void comutare(int lin,int col)
176 { int i,j;
177 //cout<<"\nmodific coloanele "<<endl;
178 int sw=1;
179 for(j=1;j<=n;j++) if (a[1][j]==lin)
180 { fprintf(g,"%d ",j);sw=0;
181 //solc[jj++]=j;
182 }
183 if (sw) fprintf(g,"0");
184 //cout<<"\nmodific liniile "<<endl;
185 fprintf(g,"\n");
186 sw=1;
187 for(i=1;i<=m;i++)
188 if (a[i][1]==col)
189 {fprintf(g,"%d ",i);
190 sw=0;}
191 if (sw) fprintf(g,"0");
192 fprintf(g,"\n");
193 }
194
195 int date_valide()
196 {
197 int sir[101],i,j,egale,opuse;
198 for(i=1;i<=m;i++) sir[i]=0;
199 sir[1]=1;
200 for(i=2;i<=m;i++)
201 {
202 egale=1;//presupun ca sunt egale
203 for (j=1;j<=n;j++)
204 if (a[1][j]!=a[i][j])
205 {
206 egale=0;
207 j=n+1;
208 }
209
210 if (egale==0)//verific daca sunt opuse
211 {
212 opuse=1;//presupun ca sunt opuse
213 for (j=1;j<=n;j++)
214 if (!(a[1][j]^a[i][j]))
215 {
216 opuse=0;
217 j=n+1;
218 }
219 }
220 if (egale==1 || opuse==1) sir[i]=1;
CAPITOLUL 38. ONI 2002 838

221 else sir[i]=0;


222 }
223 for(i=1;i<=m;i++)
224 if (sir[i]==0) return 0;
225 return 1;
226 }
227
228 void citire_fis(){
229 FILE *f;
230 int i,j;
231 f=fopen("becuri.in","r");
232 fscanf(f,"%d %d",&m,&n);
233 for(i=1;i<=m;i++)
234 for(j=1;j<=n;j++){
235 fscanf(f,"%d",&a[i][j]);
236 if(a[i][j]) k++;
237 }
238 fclose(f);
239 }
240
241 int main(){
242 int test,i,j,min,k1=0,k2=0,k3=0,k4=0;
243 g=fopen("becuri.out","w");
244 citire_fis();
245 test=date_valide();
246 if (!test)
247 fprintf(g,"-1");
248 for(j=1;j<=n;j++)
249 if(a[1][j]) l1++;
250 l0=n-l1;
251 for(i=1;i<=m;i++)
252 if(a[i][1]) c1++;
253 c0=m-c1;
254 i=0;
255 if (generare(0,0)) {i++;s[i]=l0+c0;k1=1;}
256 if (generare(0,1)) {i++; s[i]=l0+c1;k2=1;}
257 if (generare(1,0)) {i++;s[i]=l1+c0;k3=1;}
258 if (generare(1,1)) {i++;s[i]=l1+c1;k4=1;}
259
260 if (s[1]<s[2]) min=s[1];
261 else min=s[2];
262
263 if (min==l0+c0 && k1) comutare(0,0);
264 else
265 if (min==l0+c1 && k2) comutare(0,1);
266 else
267 if (min==l1+c0 && k3) comutare(1,0);
268 else
269 if (min==l1+c1 && k4) comutare(1,1);
270 fclose(g);
271 return 0;
272 }

38.3.4 Rezolvare detaliat 


Prima faz : pun zero pe prima linie ³i veric.

Listing 38.3.3: becuri1.java


1 import java.io.*;
2 class Becuri1
3 {
4 static int m,n;
5 static byte[][] a;
6 static byte[][] b;
7 static byte[] colc;
8 static byte[] linc;
9 static byte[] colcsol;
10 static byte[] lincsol;
11 static int ncolcsol=234, nlincsol=234;
12
13 public static void main(String[]args) throws IOException
14 {
15 int i,j,k;
CAPITOLUL 38. ONI 2002 839

16 BufferedReader br=new BufferedReader(new FileReader("becuri.in"));


17 StreamTokenizer st=new StreamTokenizer(br);
18
19 st.nextToken(); m=(int)st.nval;
20 st.nextToken(); n=(int)st.nval;
21
22 a=new byte[m+1][n+1];
23 b=new byte[m+1][n+1];
24 colc=new byte[n+1];
25 colcsol=new byte[n+1];
26 linc=new byte[m+1];
27 lincsol=new byte[m+1];
28
29 for(i=1;i<=m;i++)
30 for(j=1;j<=n;j++)
31 { st.nextToken(); a[i][j]=(byte)st.nval; }
32
33 fac0Linia1();
34 // .... fac1Linia1();
35
36 System.out.println(ncolcsol+" "+nlincsol);
37
38 PrintWriter out=new PrintWriter(
39 new BufferedWriter(new FileWriter("becuri.out")));
40 out.close();
41 }
42
43 static void fac0Linia1()
44 {
45 int i,j;
46 int nlc=0, ncc=0; // nr linii/coloane comutate
47 boolean ok=true;
48
49 for(j=1;j<=n;j++) colc[j]=0; // coloane necomutate inca
50 for(i=1;i<=m;i++) linc[i]=0; // linii necomutate inca
51 for(i=1;i<=m;i++) for(j=1;j<=n;j++) b[i][j]=a[i][j]; // copie
52
53 for(j=1;j<=n;j++)
54 if(b[1][j]==1)
55 {
56 comutaColoana(j);
57 colc[j]=1; // coloana j este comutata;
58 ncc++;
59 }
60 afisb();
61 for(i=2;i<=m;i++)
62 if(!okLinia(i)) {ok=false; break;}
63 else if(b[i][1]==1) { linc[i]=1; nlc++; }
64 if(ok&&(nlc+ncc<nlincsol+ncolcsol))
65 {
66 nlincsol=nlc;
67 ncolcsol=ncc;
68 for(i=1;i<=m;i++) lincsol[i]=linc[i];
69 for(j=1;j<=n;j++) colcsol[j]=colc[j];
70 }
71 }
72
73 static boolean okLinia(int i)
74 {
75 int j;
76 for(j=2;j<=n;j++) if(b[i][j]!=b[i][1]) return false;
77 return true;
78 }
79
80 static void comutaColoana(int j)
81 {
82 int i;
83 for(i=1;i<=m;i++) b[i][j]=(byte)((1+b[i][j])%2); // 0<-->1
84 }
85
86 static void comutaLinia(int i)
87 {
88 int j;
89 for(j=1;j<=n;j++) b[i][j]=(byte)((1+b[i][j])%2); // 0<-->1
90 }
91
CAPITOLUL 38. ONI 2002 840

92 static void afisb()


93 {
94 int i,j;
95 for(i=1;i<=m;i++)
96 {
97 for(j=1;j<=n;j++) System.out.print(b[i][j]+" ");
98 System.out.println();
99 }
100 }
101 }

A doua faz : pun 1 pe prima linie ³i veric. Constat m c  se poate folosi aceea³i metot  dar
cu un parametru pentru a transmite valoarea 0 sau 1.

Listing 38.3.4: becuri2.java


1 import java.io.*;
2 class Becuri2
3 {
4 static int m,n;
5 static byte[][] a;
6 static byte[][] b;
7 static byte[] colc;
8 static byte[] linc;
9 static byte[] colcsol;
10 static byte[] lincsol;
11 static int ncolcsol=234, nlincsol=234;
12
13 public static void main(String[]args) throws IOException
14 {
15 int i,j,k;
16 BufferedReader br=new BufferedReader(new FileReader("becuri.in"));
17 StreamTokenizer st=new StreamTokenizer(br);
18
19 st.nextToken();m=(int)st.nval;
20 st.nextToken();n=(int)st.nval;
21
22 a=new byte[m+1][n+1];
23 b=new byte[m+1][n+1];
24 colc=new byte[n+1];
25 colcsol=new byte[n+1];
26 linc=new byte[m+1];
27 lincsol=new byte[m+1];
28
29 for(i=1;i<=m;i++)
30 for(j=1;j<=n;j++) { st.nextToken(); a[i][j]=(byte)st.nval; }
31
32 facLinia1((byte)0);
33 facLinia1((byte)1);
34
35 System.out.println(ncolcsol+" "+nlincsol);
36
37 PrintWriter out=new PrintWriter(
38 new BufferedWriter(new FileWriter("becuri.out")));
39 out.close();
40 }
41
42 static void facLinia1(byte val)
43 {
44 int i,j;
45 int nlc=0, ncc=0; // nr linii/coloane comutate
46 boolean ok=true;
47
48 for(j=1;j<=n;j++) colc[j]=0; // coloane necomutate inca
49 for(i=1;i<=m;i++) linc[i]=0; // linii necomutate inca
50 for(i=1;i<=m;i++) for(j=1;j<=n;j++) b[i][j]=a[i][j]; // copie
51
52 for(j=1;j<=n;j++)
53 if(b[1][j]!=val)
54 {
55 comutaColoana(j);
56 colc[j]=1; // coloana j este comutata;
57 ncc++;
58 }
59 afisb();
60 for(i=1;i<=m;i++) if(!okLinia(i)) { ok=false; break; }
CAPITOLUL 38. ONI 2002 841

61 else if(b[i][1]==1) { linc[i]=1; nlc++; }


62 if(ok&&(nlc+ncc<nlincsol+ncolcsol))
63 {
64 nlincsol=nlc;
65 ncolcsol=ncc;
66 for(i=1;i<=m;i++) lincsol[i]=linc[i];
67 for(j=1;j<=n;j++) colcsol[j]=colc[j];
68 }
69 }
70
71 static boolean okLinia(int i)
72 {
73 int j;
74 for(j=2;j<=n;j++) if(b[i][j]!=b[i][1]) return false;
75 return true;
76 }
77
78 static void comutaColoana(int j)
79 {
80 int i;
81 for(i=1;i<=m;i++) b[i][j]=(byte)((1+b[i][j])%2); // 0<-->1
82 }
83
84 static void comutaLinia(int i)
85 {
86 int j;
87 for(j=1;j<=n;j++) b[i][j]=(byte)((1+b[i][j])%2); // 0<-->1
88 }
89
90 static void afisb()
91 {
92 int i,j;
93 for(i=1;i<=m;i++)
94 {
95 for(j=1;j<=n;j++) System.out.print(b[i][j]+" ");
96 System.out.println();
97 }
98 System.out.println();
99 }
100 }

Analog pentru prima coloan . în plus, se nalizeaz  scrierea rezultatelor în ³ier.

Listing 38.3.5: becuri3.java


1 import java.io.*;
2 class Becuri3
3 {
4 static int m,n;
5 static byte[][] a;
6 static byte[][] b;
7 static byte[] colc;
8 static byte[] linc;
9 static byte[] colcsol;
10 static byte[] lincsol;
11 static int ncolcsol=234, nlincsol=234;
12
13 public static void main(String[]args) throws IOException
14 {
15 int i,j,k;
16 BufferedReader br=new BufferedReader(new FileReader("becuri.in"));
17 StreamTokenizer st=new StreamTokenizer(br);
18
19 st.nextToken();m=(int)st.nval;
20 st.nextToken();n=(int)st.nval;
21
22 a=new byte[m+1][n+1];
23 b=new byte[m+1][n+1];
24 colc=new byte[n+1];
25 colcsol=new byte[n+1];
26 linc=new byte[m+1];
27 lincsol=new byte[m+1];
28
29 for(i=1;i<=m;i++)
30 for(j=1;j<=n;j++) { st.nextToken(); a[i][j]=(byte)st.nval; }
31
CAPITOLUL 38. ONI 2002 842

32 facLinia1((byte)0);
33 facLinia1((byte)1);
34 facColoana1((byte)0);
35 facColoana1((byte)1);
36
37 PrintWriter out=new PrintWriter(
38 new BufferedWriter(new FileWriter("becuri.out")));
39
40 if(nlincsol+ncolcsol>n+m) out.println(-1);
41 else
42 {
43 if(ncolcsol==0) out.print(0);
44 else for(j=1;j<=n;j++) if(colcsol[j]==1) out.print(j+" ");
45 out.println();
46 if(nlincsol==0) out.print(0);
47 else for(i=1;i<=m;i++) if(lincsol[i]==1) out.print(i+" ");
48 out.println();
49 }
50 out.close();
51 }
52
53 static void facLinia1(byte val)
54 {
55 int i,j;
56 int nlc=0, ncc=0; // nr linii/coloane comutate
57 boolean ok=true;
58
59 for(j=1;j<=n;j++) colc[j]=0; // coloane necomutate inca
60 for(i=1;i<=m;i++) linc[i]=0; // linii necomutate inca
61 for(i=1;i<=m;i++) for(j=1;j<=n;j++) b[i][j]=a[i][j]; // copie
62
63 for(j=1;j<=n;j++)
64 if(b[1][j]!=val)
65 {
66 comutaColoana(j);
67 colc[j]=1; // coloana j este comutata;
68 ncc++;
69 }
70 for(i=1;i<=m;i++)
71 if(!okLinia(i)) {ok=false; break;}
72 else if(b[i][1]==1) { linc[i]=1; nlc++; }
73 if(ok&&(nlc+ncc<nlincsol+ncolcsol))
74 {
75 nlincsol=nlc;
76 ncolcsol=ncc;
77 for(i=1;i<=m;i++) lincsol[i]=linc[i];
78 for(j=1;j<=n;j++) colcsol[j]=colc[j];
79 }
80 }
81
82 static void facColoana1(byte val)
83 {
84 int i,j;
85 int nlc=0, ncc=0; // nr linii/coloane comutate
86 boolean ok=true;
87
88 for(j=1;j<=n;j++) colc[j]=0; // coloane necomutate inca
89 for(i=1;i<=m;i++) linc[i]=0; // linii necomutate inca
90 for(i=1;i<=m;i++) for(j=1;j<=n;j++) b[i][j]=a[i][j]; // copie
91
92 for(i=1;i<=m;i++)
93 if(b[i][1]!=val)
94 {
95 comutaLinia(i);
96 linc[i]=1; // linia i este comutata;
97 nlc++;
98 }
99 for(j=1;j<=n;j++)
100 if(!okColoana(j)) { ok=false; break;}
101 else if(b[1][j]==1) { colc[j]=1; ncc++; }
102 if(ok&&(nlc+ncc<nlincsol+ncolcsol))
103 {
104 nlincsol=nlc;
105 ncolcsol=ncc;
106 for(i=1;i<=m;i++) lincsol[i]=linc[i];
107 for(j=1;j<=n;j++) colcsol[j]=colc[j];
CAPITOLUL 38. ONI 2002 843

108 }
109 }
110
111 static boolean okLinia(int i)
112 {
113 int j;
114 for(j=2;j<=n;j++) if(b[i][j]!=b[i][1]) return false;
115 return true;
116 }
117
118 static boolean okColoana(int j)
119 {
120 int i;
121 for(i=2;i<=m;i++) if(b[i][j]!=b[1][j]) return false;
122 return true;
123 }
124
125 static void comutaColoana(int j)
126 {
127 int i;
128 for(i=1;i<=m;i++) b[i][j]=(byte)((1+b[i][j])%2); // 0<-->1
129 }
130
131 static void comutaLinia(int i)
132 {
133 int j;
134 for(j=1;j<=n;j++) b[i][j]=(byte)((1+b[i][j])%2); // 0<-->1
135 }
136 }

38.4 Discuri
Florin Gheµu, Bucure³ti
Se dau N numere reale considerate ca ind razele a N discuri.
Consider m c  a³ez m un disc în sistemul xOy dac  îl plas m la o coordonat  x pozitiv 
sucient de mare, tangent cu axa Ox ³i deasupra ei, apoi îl împingem spre Oy pân  când devine
tangent cu Oy sau cu primul disc întâlnit, a³ezat anterior.
Dup  a³ezarea tuturor discurilor în ordinea dat  unele dintre ele pot  considerate dispensabile,
pentru c  prin eliminarea lor nu se modic  l µimea total  a gurii rezultate, adic  nici un disc
nu se mai poate deplasa spre stânga.
Cerinµ :
Identicaµi toate discurile dispensabile dintr-o conguraµie dat .
Date de intrare:
Din ³ierul de intrare DISCURI.IN veµi citi de pe prima linie num rul N de discuri, iar de
pe urm toarele N linii, N numere reale reprezentând razele discurilor în ordinea de a³ezare, câte
unul pe linie.
Date de iesire:
În ³ierul DISCURI.OUT veµi scrie pe prima linie num rul K de discuri dispensabile, iar pe
urm toarele K linii, K întregi reprezentând numerele de ordine ale discurilor considerate dispen-
sabile, câte unul pe linie.
Restricµii:
a N & 1000.
CAPITOLUL 38. ONI 2002 844

Figura 38.2: Discuri

Exemplu (gura de mai sus; discurile ha³urate sunt dispensabile)


DISCURI.IN DISCURI.OUT
7 3
4 2
0.1 3
0.5 5
3
0.5
4
1
Timpul maxim de execuµie: 1 secund /test

38.4.1 Indicaµii de rezolvare

Soluµie prezentat  în GInfo 12/6


Pentru identicarea discurilor dispensabile va trebui s  determin m atingerile dintre discurile
care inuenµeaz  l µimea gurii. De exemplu, dac  avem ³ase discuri, cu raze de 1000, 1, 2,
3, 1000, respectiv 500, atunci atingerile care inuenµeaz  l µimea gurii sunt între primul ³i al
cincilea disc, respectiv între al cincilea ³i al ³aselea.
Pentru ecare disc i vom determina, pe rând, coordonatele orizonatale pe care le-ar avea discul
dac  ar atinge unul dintre discurile anterioare.
În nal, vom alege discul (sau axa Oy ) pentru care coordonata orizontal  a centrului noului
disc este maxim  ³i putem arma c  discul i ajuge în aceast  poziµie.
Dac  discul i va atinge un disc anterior j , atunci discurile cu numerele de ordine cuprinse între
j  1 ³i i  1 sunt dispensabile.
Dup  ce vom lua în considerare toate cele N discuri, vom putea determina numerele de ordine
ale tuturor discurilor dispensabile.
În nal vom verica dac  exist  discuri introduse la sfâr³it care sunt dispensabile. Pentru
aceasta vom determina l µimea gurii ³i ultimul disc care o inuenµeaz . Toate discurile introduse
dup  acest disc sunt dispensabile.
Pentru determinarea coordonatei orizontale xi a centrului unui disc i care atinge un disc j
avem nevoie de coordonata orizontal  xj a centrului discului j , precum ³i de razele ri ³i rj ale
celor dou  discuri. Dac  aceste trei valori sunt cunoscute, se poate folosi urm toarea formul 
pentru a determina coordonata
Õ orizontal  a centrului discului j :
xj xi  ri  rj   ri  rj 2 .
2

Analiza complexit µii
Pentru ecare disc i care este introdus, se determin  posibila coordonat  xi datorat  atingerii
cu toate cele i  1 discuri inserate anterior. Pentru cele N discuri se vor determina, în total,
0  1  2  ...  N  1 N N  1©2 coordonate, deci ordinul de complexitate al acestei operaµii
2
este O N .
La ecare pas, pot  marcate ca dispensabile cel mult toate discurile inserate anterior, a³adar
2
ordinul de complexitate al acestei operaµii este tot O N .
CAPITOLUL 38. ONI 2002 845

Determinarea l µimii gurii ³i a cercurilor dispensabile de la sfâr³itul secvenµei necesit  a


singur  parcurgere a ³irului care p streaz  coordonatele centrelor discurilor, ceea ce implic  ordinul
de complexitate O N .
A³area cercurilor dispensabile precum ³i citirea razelor cercurilor sunt operaµii care se efec-
tueaz  în timp liniar (O N ), necesitând o simpl  parcurgere a unor ³iruri.
În concluzie, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este
2 2 2
O N   O N   O N   O N   O N  O N .

38.4.2 Cod surs 

Listing 38.4.1: discuri.pas


1 const fi=’discuri.in’; fo=’discuri.out’;
2 type vect=array[1..10000]of real;
3 var n,i,j,nr,k:integer;
4 sw:boolean;
5 dd,dp:real;
6 d,x:^vect;
7 vc:array[1..10000]of boolean;
8
9 function dist(i,j:integer):real;
10 var r,rr:real;
11 begin
12 r:=d^[i]; rr:=d^[j];
13 dist:=x^[i]+sqrt((r+rr)*(r+rr)-(r-rr)*(r-rr));
14 end;
15
16 begin
17 new(d); new(x);
18 assign(input,fi); reset(input);
19 readln(n);
20 for i:=1 to n do readln(d^[i]);
21 close(input);
22 fillchar(vc,sizeof(vc),true);
23 for i:=1 to n do begin
24 dd:=d^[i]; k:=0;
25 for j:=1 to i-1 do begin
26 dp:=dist(j,i);
27 if dd<=dp then begin
28 dd:=dp; k:=j;
29 end;
30 end; x^[i]:=dd;
31 for j:=k+1 to i-1 do vc[j]:=false;
32 end; k:=0; dd:=0;
33 for i:=1 to n do if dd<x^[i]+d^[i] then begin
34 dd:=x^[i]+d^[i]; k:=i;
35 end;
36 for i:=k+1 to n do vc[i]:=false;
37 nr:=0;
38 for i:=1 to n do if not vc[i] then inc(nr);
39 assign(output,fo); rewrite(output);
40 writeln(nr);
41 for i:=1 to n do if not vc[i] then writeln(i);
42 close(output); dispose(d); dispose(x);
43 end.

38.4.3 Rezolvare detaliat 

Listing 38.4.2: discuri.java


1 import java.io.*;
2 class Discuri
3 {
4 static int ultimulDisc;
5 static double latime;
6 static boolean[] dispensabil;
7 static double[] r;
8 static double[] x;
9 static int n,k;
CAPITOLUL 38. ONI 2002 846

10
11 public static void main(String args[]) throws IOException
12 {
13 citeste();
14 calcul();
15 scrie();
16 }
17
18 static void citeste() throws IOException
19 {
20 BufferedReader br=new BufferedReader(new FileReader("discuri.in"));
21 StreamTokenizer st=new StreamTokenizer(br);
22 st.nextToken(); n=(int)st.nval;
23 r=new double[n+1];
24 x=new double[n+1];
25 dispensabil=new boolean[n+1]; // implicit este false
26 for(int i=1; i<=n; i++){st.nextToken();r[i]=st.nval;}
27 br.close();
28 }
29
30 static void calcul()
31 {
32 int i;
33 for(i=1; i<=n; i++) plaseazaCerc(i);
34 for(i=ultimulDisc+1; i<=n; i++) dispensabil[i]=true;
35 }
36
37 static void plaseazaCerc(int i)
38 {
39 int j, discContact=0;
40 double contactOx;
41 x[i]=r[i]; // initializare contact cu axa Oy
42 for(j=1; j<i; j++)
43 {
44 contactOx=coord(x[j],r[j],r[i]);
45 if(contactOx>x[i]) {x[i]=contactOx; discContact=j;}
46 }
47 for(j=discContact+1; j<i; j++) dispensabil[j]=true;
48 if(latime<x[i]+r[i]) {latime=x[i]+r[i]; ultimulDisc=i;}
49 }
50
51 static double coord(double xj,double rj, double ri)
52 {
53 return(xj+Math.sqrt((ri+rj)*(ri+rj)-(ri-rj)*(ri-rj)));
54 }
55
56 static void scrie() throws IOException
57 {
58 int i;
59 k=0;
60 for(i=1; i<=n; i++) if(dispensabil[i]) k++;
61 PrintWriter out=new PrintWriter(
62 new BufferedWriter(new FileWriter("discuri.out")));
63 out.println(k);
64 for(i=1; i<=n; i++) if(dispensabil[i]) out.println(i);
65 out.close();
66 }
67 }

38.5 Cod
lect. univ. Ovidiu Dom³a, Alba Iulia
Transmiterea ³i memorarea informaµiilor necesit  diverse sisteme de codicare în vederea
utiliz rii optime a spaµiilor disponibile. Un sistem foarte des întâlnit este acela prin care unei
secvenµe de caractere i se asociaz  un num r.
Se consider  cuvintele formate numai cu literele mici ale alfabetului englez a, b, c, ..., z (26 de
caractere). Din toate aceste cuvinte le consider m doar pe cele ale c ror caractere sunt în ordine
strict lexicograc  (caracterul de pe orice poziµie este strict mai mic decât orice caracter urm tor).
Sistemul de codicare se obµine astfel:
a Se ordoneaz  cuvintele în ordinea cresc toare a lungimilor lor.
CAPITOLUL 38. ONI 2002 847

a Cuvintele de aceea³i lungime se ordoneaz  lexicograc (în ordinea alfabetic  a cuvintelor


dintr-un dicµionar).
a Codic m aceste cuvinte prin numerotarea lor începând cu a, dup  cum urmeaz :
a  1
b  2
...
z  26
ab  27
...
az  51
bc  52
...
vwxzy  83681
...
Cerinµ :
Dac  se d  un cuvânt s  se precizeze dac  poate  codicat conform sistemului de codicare.
În caz armativ s  se precizeze codul s u.
Date de intrare
Fi³ierul de intrare COD.IN conµine pe o linie un cuvânt.
Date de ie³ire
Fi³ierul COD.OUT va conµine codul cuvântului ce trebuie codicat, sau 0 în cazul în care
cuvântul nu poate  codicat.
Restricµii ³i preciz ri
a Num rul maxim de litere ale unui cuvânt este 10
a Num rul de caractere din alfabetului englez este 26
Exemple
COD.IN COD.OUT COD.IN COD.OUT COD.IN COD.OUT
bf 55 aab 0 vwxyz 83681
Timp maxim de execuµie: 2 secunde/test

38.5.1 Indicaµii de rezolvare

Soluµie prezentat  în GInfo 12/6


Din condiµiile precizate în enunµ rezult  c , pe baza unei mulµimi de litere distincte, se poate
construi un singur cuvânt care respect  condiµiile date, ³i anume cel care conµine literele ordonate
lexicograc. A³adar, oric rei mulµimi de cel mult zece litere distincte îi corespunde un cuvânt care
respect  condiµiile din enunµ.
Se poate arma c  un cuvânt este o submulµime a mulµimii literelor; de aici rezult  c  num rul
k
cuvintelor formate din k litere este C26 . Mai mult, num rul cuvintelor formate din k dintre
k
ultimele n litere ale alfabetului este Cn .
Num rul de ordine al cuvântului dat este mai mare decât cel al cuvintelor formate din mai
multe cifre. Din aceste motive, pentru un cunvânt format din k litere, vom avea un num r mai
< k
mare decât i 1 C26 .
i

În continuare, pentru prima liter  a cuvântului, va trebui s  g sim num rul cuvintelor care
încep cu o liter  mai mic  (din punct de vedere lexicograc).
k1 k1
În cazul în care cuvântul are k litere, vor exista C25 cuvinte valide care k încep cu 'a', C25
cuvinte valide care încep cu 'b' etc.
k1
În general, vor exista C26i cuvinte valide care încep cu a i-a liter  a alfabetului.
<
Dac  prima liter  a cuvântului este cea de-a n-a liter  a alfabetului, vom avea i 1 C26i
n k1

cuvinte valide de care încep cu o liter  mai mic .


În acest moment ³tim num rul de ordine minim al unui cuvânt care începe cu prima liter  a
cuvântului dat.
Pentru a doua liter  vom proceda într-o manier  asem n toare. Singura diferenµ  este dat  de
faptul c  a doua liter  trebuie s  e strict mai mare decât prima. A³adar, dac  prima liter  este
< m1 k2
cea de-a n-a a alfabetului, iar a doua este cea de-a m-a, atunci vom avea i n1 C26i cuvinte
care au pe prima poziµia aceea³i liter , iar pe cea de-a doua poziµie o liter  mai mic .
CAPITOLUL 38. ONI 2002 848

Procedeul va continua pentru ecare liter  în parte.


În cazul în care litera curent  este cea de-a p-a a cuvântului, este a m-a a alfabetului, iar litera
anterioar  este a n-a a alfabetului, num rul de cuvite care au pe primele p  1 poziµii acelea³i
<m1
litere ca ³i cuvântul dat, iar pe cea de-a p-a o liter  mai mic  este dat de formula i n1 C26i .
kp

Adunând toate valorile obµinute pe parcurs vom obµine num rul cuvintelor care se a  înaintea
cuvântului dat. Adunând 1 la aceast  valoare, vom obµine num rul de ordine al cuvântului.
Analiza complexit µii
Pentru a analiza complexitatea acestui algoritm va trebui s  preciz m faptul c  num rul lite-
relor din alfabet este constant, deci nu poate interveni în exprimarea ordinului de complexitate.
Acesta va  stabilit doar în funcµie de lungimea k a cuvântului.
< k i
Iniµial se calculeaz  suma i 1 C26 , operaµie realizabil  în timp liniar, avân în vedere observaµia
anterioar . A³adar, primul pas al algoritmului are ordinul de complexitate O k .
< m1 k2
Pentru ecare liter  a cuvântului se calculeaz  suma i n1 C26i , unde variabilele au sem-
nicaµia prezentat  anterior. Num rul de litere este implicat în determinarea valorii combin rii.
A³adar, calculul combin rii se realizeaz  în timp liniar. Num rul de combin ri calculate nu de-
pinde de lungimea cuvântului, deci ordinul de complexitate al calcul rii acestei sume este O k .
În total vor  calculate k astfel de sume, deci ordinul de complexitate al celui de-al doilea pas
2
al algoritmului este O k  O k  O k .
Citirea datelor de intrare ³i scrierea celor de ie³ire se realizeaz  cu ajutorul unor operaµii
elementare, deci putem considera c  au ordinul de complexitate O 1.
În concluzie, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este
2 2
O k   O k  O k O k  având în vedere c  num rul literelor din alfabetul englez este
constant.

38.5.2 Cod surs 

Listing 38.5.1: cod.pas


1 var x,cuv:string;
2 l:byte;
3 nr:longint;
4 f:text;
5
6 Function verifcuv(x:string):boolean;
7 var cod:boolean;
8 i:byte;
9 begin
10 i:=2;
11 COD:=TRUE;
12 while (i<=length(x)) and cod do
13 begin;
14 cod:=x[i]>x[i-1];
15 inc(i);
16 end;
17 verifcuv:=cod;
18 end;
19
20 Procedure Sol(x:string);
21 begin
22 if x=cuv then
23 begin
24 WriteLn(f,nr);
25 close(f);
26 Halt(0);
27 end;
28 end;
29
30 Procedure Back(i:byte);
31 var j,aa:char;
32 begin
33 if i=1 then aa:=’a’ else aa:=succ(x[i-1]);
34 for j:=aa to ’z’ do
35 begin
36 x:=X+j;
37 if i<l then Back(i+1)
38 else begin
39 nr:=nr+1;
CAPITOLUL 38. ONI 2002 849

40 sol(x);
41 end;
42 delete(x,i,1);
43 end;
44 end;
45
46 begin
47 assign(f,’cod.in’);
48 reset(f);
49 ReadLn(f,cuv);
50 close(f);
51 assign(f,’cod.out’);
52 rewrite(f);
53 nr:=0;
54 if verifcuv(cuv) then
55 for l:=1 to length(cuv) do
56 Back(1)
57 else
58 writeln(f,0);
59 close(f);
60 end.

Listing 38.5.2: codcomb.pas


1 {$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+,Y+}
2 {$M 16384,0,655360}
3 const fin=’cod.in’;
4 fou=’cod.out’;
5 var s:string;
6 i,j,p,t,ss:longint;
7 n,d:real;
8 f:text;
9
10 begin
11 assign(f,fin);
12 reset(f);
13 readln(f,s);
14 close(f);
15 for i:=1 to length(s)-1 do begin
16 d:=1;n:=1;
17 for j:=1 to i do begin
18 n:=n*(27-j);
19 d:=d*j;
20 end;
21 n:=n / d;
22 ss:=ss+trunc(n);
23 end;
24 for t:=1 to length(s) do begin
25 if t>1 then p:=ord(s[t-1])-96;
26 for i:=p+1 to ord(s[t])-97 do begin
27 d:=1;n:=1;
28 for j:=1 to length(s)-t do begin
29 n:=n*(27-j-i);
30 d:=d*j;
31 end;
32 n:=n/ d;
33 ss:=ss+trunc(n);
34 end;
35 end;
36 assign(f,fou);
37 rewrite(f);
38 writeln(f,ss+1);
39 close(f);
40 end.

38.5.3 Rezolvare detaliat 


Listing 38.5.3: cod.java
1 import java.io.*;
2 class Cod
3 {
4 static String cuvant;
CAPITOLUL 38. ONI 2002 850

5 static long codcuvant;


6
7 public static void main(String args[]) throws IOException
8 {
9 cuvant=citeste();
10 codcuvant=calcul();
11 scrie();
12 }
13
14 static String citeste() throws IOException
15 {
16 BufferedReader br=new BufferedReader(new FileReader("cod.in"));
17 String s=br.readLine();
18 br.close();
19 return(s);
20 }
21
22 static long calcul()
23 {
24 int k=cuvant.length();
25 int i,j,m,n=0;
26 long nr=1;
27 for(i=1; i<k; i++) nr+=comb(26,i);
28 for(i=0; i<k; i++)
29 {
30 if(i>0)
31 {
32 n=cuvant.charAt(i-1)-’a’+1;
33 if(cuvant.charAt(i)<=cuvant.charAt(i-1)) return 0;
34 }
35 m=cuvant.charAt(i)-’a’+1;
36 for(j=n+1; j<m; j++) nr+=comb(26-j,k-i-1);
37 }
38 return nr;
39 }
40
41 static void scrie() throws IOException
42 {
43 PrintWriter out=new PrintWriter(
44 new BufferedWriter(new FileWriter("cod.out")));
45 out.print(codcuvant);
46 out.close();
47 }
48
49 static long comb(int n,int k)
50 {
51 int i,j,d;
52 if(k>n/2) k=n-k;
53 int[] x=new int[k+1];
54 int[] y=new int[k+1];
55 for(i=1;i<=k;i++) x[i]=n-k+i;
56 for(j=1;j<=k;j++) y[j]=j;
57 for(j=2;j<=k;j++)
58 for(i=1;i<=k;i++)
59 {
60 d=cmmdc(x[i],y[j]);
61 x[i]=x[i]/d;
62 y[j]=y[j]/d;
63 if(y[j]==1) break;
64 }
65 long p=1;
66 for(i=1;i<=k;i++) p=p*x[i];
67 return p;
68 }
69
70 static int cmmdc(int a, int b)
71 {
72 int d,i,c,r;
73 if (a>b) {d=a; i=b;} else {d=b; i=a;}
74 while (i != 0){ c=d/i; r=d%i; d=i; i=r; }
75 return d;
76 }
77 }
Capitolul 39

ONI 2001

Figura 39.1: Sigla ONI 2001

39.1 Ferma
prof. Maria Niµ  ³i prof. Adrian Niµ , Oradea
Un fermier are un teren care are forma unui tablou dreptunghiular lung de M unit µi ³i
lat de N unit µi. Pe teren sunt plantaµi din loc în loc copaci, pe care fermierul nu dore³te s -i
taie. Dorind s -³i supravegheze cultura, fermierul realizeaz  un mic robot de form  p trat  având
latura de 3 unit µi pe care îl poate teleghida prin ferm , parcurgând unitate cu unitate o anumit 
suprafaµ .
Robotul se poate mi³ca pe vertical  ³i pe orizontal  dar, nu poate trece peste copaci, nu îi poate
distruge, nu se poate roti ³i are nevoie pentru mi³care de o suprafaµ  corespunz toare dimensiunii
lui.
Cerinµ 
Ajutaµi-l pe fermier s  determine suprafaµa maxim  pe care o poate urm ri, folosind acest
sistem.
Date de intrare
Fi³ier de intrare: FERMA.IN
a Linia 1: N M - dou  numere naturale nenule, separate pritr-un spaµiu, reprezentând num rul
de linii (N ), respectiv num rul de coloane (M );
a Liniile 2..N+1: C1 C2 ...CM - aceste N linii codic  ferma; ecare linie conµine câte M
caractere (f r  s  e separate prin spaµii) având semnicaµia:
'.' - teren liber;
'+' - locul în care este plantat un copac;
'R' - centrul robotului.
Date de ie³ire
Fi³ier de ie³ire: FERMA.OUT
a Liniile 1..N: C1 C2 ...CM - aceste N linii codic  modul în care fermierul poate s -³i utilizeze
robotul pe terenul s u; ecare linie conµine câte M caractere (f r  s  e separate prin spaµii)
având semnicaµia:

851
CAPITOLUL 39. ONI 2001 852

'.' - teren neacoperit de robot;


'*' - teren ce poate  vericat de robot;
'+' - loc în care a r mas copacul.
Restricµii
1 & N, M & 50
Exemplu
FERMA.IN
12 11
. . . . . . . . . . .
. . . + . . . . . + .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . + . . . . . . .
. + . . . R . . . . .
. . . . . . . . . + .
. . + . . . . . . . +
. . . . . . + . . . .
. . . . . . . . . . .
. . . . . . + . . . .
FERMA.OUT
. . . . * * * * * . .
. . . + * * * * * + .
. . * * * * * * * * *
. . * * * * * * * * *
. + * * * * * * * * *
. . . + * * * * * * *
. + . * * * * * * * *
. . . * * * * * * + .
. . + * * * * * * . +
* * * * * * + . . . .
* * * * * * . . . . .
* * * * * * + . . . .
Timp maxim de executare/test: 3 secunde

39.1.1 Indicaµii de rezolvare


Se folose³te un algoritm de tip ll (umplere) folosind recursivitatea, plecând din poziµia
iniµial  a robotului ³i µinând cont de restricµiile problemei.

39.1.2 *Cod surs 

39.1.3 Rezolvare detaliat 

Listing 39.1.1: ferma1.java


1 import java.io.*;
2 class Ferma1
3 {
4 static int n,m;
5 static int[][] a=new int[51][51]; // ferma
6 static PrintWriter out;
7
8 public static void main(String[] args) throws IOException
9 {
10 int i,j,k;
11 int ic,sc;
12 out=new PrintWriter(new BufferedWriter(new FileWriter("ferma.out")));
CAPITOLUL 39. ONI 2001 853

13 BufferedReader br=new BufferedReader(new FileReader("ferma.in"));


14 StreamTokenizer st=new StreamTokenizer(br);
15
16 st.nextToken(); n=(int)st.nval;
17 st.nextToken(); m=(int)st.nval;
18 System.out.println("n="+n+" m="+m);
19
20 br.readLine();// citeste LF adica 0A adica 10
21 for(i=1;i<=n;i++)
22 {
23 for(j=1;j<=m;j++)
24 {
25 a[i][j]=br.read();
26 System.out.print(a[i][j]+" ");
27 }
28 br.readLine(); // citeste CR LF adica 0D 0A adica 13 10
29 System.out.println();
30 }
31
32 System.out.println();
33 for(i=1;i<=n;i++)
34 {
35 for(j=1;j<=m;j++) System.out.print((char) a[i][j]+" ");
36 System.out.println();
37 }
38 out.close();
39 }
40 }

Listing 39.1.2: ferma2.java


1 import java.io.*;
2 class Ferma2
3 {
4 static int n,m;
5 static int[][] a=new int[51][51]; // ferma codificata
6 static PrintWriter out;
7 static final int liber=(int)’.’,copac=(int)’+’,robot=(int)’*’;
8
9 public static void main(String[] args) throws IOException
10 {
11 int i,j,ir=0,jr=0;
12 int ic,sc;
13 out=new PrintWriter(new BufferedWriter(new FileWriter("ferma.out")));
14 BufferedReader br=new BufferedReader(new FileReader("ferma.in"));
15 StreamTokenizer st=new StreamTokenizer(br);
16
17 st.nextToken(); n=(int)st.nval;
18 st.nextToken(); m=(int)st.nval;
19
20 br.readLine();// citeste LF = 0x0A =10
21 for(i=1;i<=n;i++)
22 {
23 for(j=1;j<=m;j++) a[i][j]=br.read();
24 br.readLine(); // citeste CR LF adica 0D 0A adica 13 10
25 }
26
27 for(i=1;i<=n;i++)
28 for(j=1;j<=m;j++)
29 if(a[i][j]==(int)’R’) {ir=i; jr=j; break;};
30 for(i=-1;i<=1;i++)
31 for(j=-1;j<=1;j++) a[ir+i][jr+j]=robot;
32
33 deplasareDin(ir,jr);
34 afism();
35 out.close();
36 }
37
38 static void deplasareDin(int ir, int jr)
39 {
40 if(ir-2>=1) // sus
41 if((a[ir-2][jr-1]!=copac)&&(a[ir-2][jr]!=copac)&&(a[ir-2][jr+1]!=copac))
42 if((a[ir-2][jr-1]==liber)||(a[ir-2][jr]==liber)||(a[ir-2][jr+1]==liber))
43 {
44 a[ir-2][jr-1]=a[ir-2][jr]=a[ir-2][jr+1]=robot;
CAPITOLUL 39. ONI 2001 854

45 deplasareDin(ir-1,jr);
46 }
47
48 if(ir+2<=n) // jos
49 if((a[ir+2][jr-1]!=copac)&&(a[ir+2][jr]!=copac)&&(a[ir+2][jr+1]!=copac))
50 if((a[ir+2][jr-1]==liber)||(a[ir+2][jr]==liber)||(a[ir+2][jr+1]==liber))
51 {
52 a[ir+2][jr-1]=a[ir+2][jr]=a[ir+2][jr+1]=robot;
53 deplasareDin(ir+1,jr);
54 }
55
56 if(jr-2>=1) // stanga
57 if((a[ir-1][jr-2]!=copac)&&(a[ir][jr-2]!=copac)&&(a[ir+1][jr-2]!=copac))
58 if((a[ir-1][jr-2]==liber)||(a[ir][jr-2]==liber)||(a[ir+1][jr-2]==liber))
59 {
60 a[ir-1][jr-2]=a[ir][jr-2]=a[ir+1][jr-2]=robot;
61 deplasareDin(ir,jr-1);
62 }
63
64 if(jr+2<=m) // dreapta
65 if((a[ir-1][jr+2]!=copac)&&(a[ir][jr+2]!=copac)&&(a[ir+1][jr+2]!=copac))
66 if((a[ir-1][jr+2]==liber)||(a[ir][jr+2]==liber)||(a[ir+1][jr+2]==liber))
67 {
68 a[ir-1][jr+2]=a[ir][jr+2]=a[ir+1][jr+2]=robot;
69 deplasareDin(ir,jr+1);
70 }
71 }
72
73 static void afism()
74 {
75 int i,j;
76 for(i=1;i<=n;i++)
77 {
78 for(j=1;j<=m;j++) out.print((char) a[i][j]);
79 out.println();
80 }
81 out.println();
82 }
83 }

39.2 Fracµii
prof. Ovidiu Dom³a, Alba Iulia
O proprietate interesant  a fracµiilor ireductibile este c  orice fracµie se poate obµine dup 
urm toarele reguli:
a pe primul nivel se a  fracµia 1©1;
a pe al doilea nivel, în stânga fracµiei 1©1 de pe primul nivel, plas m fracµia 1©2 iar în dreapta
ei fracµia 2©1;
nivelul 1: 1©1
nivelul 2: 1©2 2©1
a pe ecare nivel k se plaseaz  sub ecare fracµie i©j de pe nivelul de deasupra, fracµia i© i  j 
în stânga, iar fracµia i  j ©j în dreapta.
nivelul 1: 1©1
nivelul 2: 1©2 2©1
nivelul 3: 1©3 3©2 2©3 3©1
Cerinµ 
Dându-se o fracµie oarecare prin num r torul ³i numitorul s u, determinaµi num rul nivelului
pe care se a  fracµia sau o fracµie echivalent  (având aceea³i valoare) cu aceasta.
Date de intrare
Fi³ier de intrare: FRACTII.IN
a Linia 1: N M - dou  numere naturale nenule, separate printr-un spaµiu, reprezentând
num r torul ³i numitorul unei fracµii.
Date de ie³ire
Fi³ier de ie³ire: FRACTII.OUT
CAPITOLUL 39. ONI 2001 855

a Linia 1: niv - num r natural nenul, reprezentând num rul nivelului care corespunde fracµiei.
Restricµii
1 $ N, M & 2.000.000.000
Exemple
FRACTII.IN FRACTII.OUT FRACTII.IN FRACTII.OUT
13 8 6 12 8 3
Timp maxim de execuµie: 1 secund /test

39.2.1 Indicaµii de rezolvare


Se aduce fracµia la o fracµie echivalent  ireductibil . Nu se coboar  în arbore ci se urc  din
poziµia fracµiei echivalente pân  la fracµia 1/1 din vârful arborelui de fracµii.

39.2.2 *Cod surs 

39.2.3 Rezolvare detaliat 

Listing 39.2.1: fractii.java


1 import java.io.*;
2 class Fractii
3 {
4 public static void main(String[] args) throws IOException
5 {
6 int n,m,k,d;
7 StreamTokenizer st=new StreamTokenizer(new BufferedReader(
8 new FileReader("fractii.in")));
9 PrintWriter out=new PrintWriter(new BufferedWriter(
10 new FileWriter("fractii.out")));
11 st.nextToken();n=(int)st.nval;
12 st.nextToken();m=(int)st.nval;
13 k=0;
14 d=cmmdc(n,m);
15 n=n/d;
16 m=m/d;
17 while((n!=1)||(m!=1))
18 {
19 k++;
20 if(n>m) n=n-m; else m=m-n;
21 }
22 k++;
23 out.println(k);
24 out.close();
25 }
26
27 static int cmmdc(int a, int b)
28 {
29 int d,i,c,r;
30 if(a>b) {d=a; i=b;} else {d=b; i=a;}
31 while(i!=0) {c=d/i; r=d%i; d=i; i=r;}
32 return d;
33 }
34 }

39.3 Tablou
prof. Rodica Pintea, Bucure³ti
Generaµi un tablou bidimensional cu propriet µile:
a conµine N linii ³i N coloane;
a elementele sale sunt numere naturale nenule;
a suma elementelor este egal  cu num rul natural nenul S;
a pe nici o linie ³i pe nici o coloan  nu exist  dou  elemente identice;
a diferenµa dintre cel mai mare ³i cel mai mic element ale tabloului este minim .
CAPITOLUL 39. ONI 2001 856

Date de intrare
Fi³ier de intrare: TABLOU.IN
a Linia 1: N S - dou  numere naturale nenule, separate printr-un spaµiu, reprezentând num rul
de linii ³i de coloane ale tabloului, respectiv valoarea sumei tuturor elementelor din tablou;
Date de ie³ire
Fi³ier de ie³ire: TABLOU.OUT
a Linia 1..N: nr11 nr12 ... nr1N
nr21 nr22 ... nr2N
.........................
nrN 1 nrN 2 ... nrN N
pe aceste N linii se vor scrie elementele tabloului, câte o linie din tablou pe o linie din ³ier;
elementele se vor separa prin câte un spaµiu.
Restricµii ³i preciz ri
a 1 $ N & 100
0$S&2
31
a
a Dac  problema nu are soluµie, în ³ierul de ie³ire se va scrie cifra 0.
a Dac  problema are mai multe soluµii, în ³ier se va scrie una singur .
Exemplu
TABLOU.IN TABLOU.OUT
3 51 4 6 7
7 4 5
5 7 6
Timp maxim de execuµie: 1 sec/test

39.3.1 Indicaµii de rezolvare


Se determin  cel mai mare num r natural x cu proprietatea n x  x  1  ...  x  n  1 $ s
³i se plaseaz  cele n numere x, x  1, ..., x  n  1 pe linii, permutându-le circular de la o linie la
alta. Astfel, pe ecare linie ³i ecare coloan  vor  numere diferite. Se m resc cu câte o unitate,
dac  este cazul, cele mai mari numere din matrice pân  când suma tuturor elementelor matricei
este egal  cu s.

39.3.2 *Cod surs 

39.3.3 Rezolvare detaliat 

Listing 39.3.1: tablou1.java


1 import java.io.*; // max --> [i][j] cu (i+j)%n==n-1
2 class Tablou1 // min --> [i][j] cu (i+j)%n==0
3 { // (i+j)%n=k ==> j=?=(n-i+k-1)%n
4 static int n,s,x,sp,p; // p=puncte ramase < n*n
5 static int[][] a;
6
7 public static void main(String[] args) throws IOException
8 {
9 int i,j,k;
10 StreamTokenizer st=new StreamTokenizer(new BufferedReader(
11 new FileReader("tablou.in")));
12 PrintWriter out=new PrintWriter(new BufferedWriter(
13 new FileWriter("tablou.out")));
14 st.nextToken();n=(int)st.nval;
15 st.nextToken();s=(int)st.nval;
16 a=new int[n][n];
17
18 x=(2*s-n*n*n+n*n)/(2*n*n);
19 System.out.println("x="+x);
20 sp=0;
21 for(i=0;i<n;i++)
22 for(j=0;j<n;j++)
CAPITOLUL 39. ONI 2001 857

23 {
24 a[i][j]=x+(i+j)%n;
25 sp+=a[i][j];
26 }
27 p=s-sp;
28 System.out.println("sp="+sp+" s="+s+" p="+p);
29
30 System.out.println(); afism();
31 k=x+n-1;
32 while(p>0)
33 {
34 i=0;
35 while((p>0)&&(i<n)) // k=valoarea pe care o maresc cu 1
36 {
37 j=(n-i+k-1)%n;
38 System.out.println("k="+k+" i="+i+" j="+j+" p="+p);
39 a[i][j]++;
40 i++; p--;
41 }
42 System.out.println(); afism();
43 k--;
44 }
45 System.out.println(); afism();
46 out.close();
47 }
48
49 static void afism()
50 {
51 int i,j;
52 for(i=0;i<n;i++)
53 {
54 for(j=0;j<n;j++) System.out.print(a[i][j]+" ");
55 System.out.println();
56 }
57 }
58 }

Listing 39.3.2: tablou2.java


1 import java.io.*; // max --> [i][j] cu (i+j)%n==n-1
2 class Tablou2 // min --> [i][j] cu (i+j)%n==0
3 { // (i+j)%n=k ==> j=?=(n-i+k-1)%n
4 static int n,s,x,sp,p; // p=puncte ramase < n*n
5 static int[][] a;
6
7 public static void main(String[] args) throws IOException
8 {
9 int i,j,k;
10 StreamTokenizer st=new StreamTokenizer(new BufferedReader(
11 new FileReader("tablou.in")));
12 PrintWriter out=new PrintWriter(new BufferedWriter(
13 new FileWriter("tablou.out")));
14 st.nextToken();n=(int)st.nval;
15 st.nextToken();s=(int)st.nval;
16 a=new int[n][n];
17
18 x=(2*s-n*n*n+n*n)/(2*n*n);
19 sp=0;
20 for(i=0;i<n;i++)
21 for(j=0;j<n;j++)
22 {
23 a[i][j]=x+(i+j)%n;
24 sp+=a[i][j];
25 }
26 p=s-sp;
27 k=x+n-1;
28 while(p>0)
29 {
30 i=0;
31 while((p>0)&&(i<n)) // k=valoarea pe care o maresc cu 1
32 {
33 j=(n-i+k-1)%n;
34 a[i][j]++;
35 i++; p--;
36 }
CAPITOLUL 39. ONI 2001 858

37 k--;
38 }
39 for(i=0;i<n;i++)
40 {
41 for(j=0;j<n;j++) out.print(a[i][j]+" ");
42 out.println();
43 }
44 out.close();
45 }
46 }

39.4 Competiµie dicil 


Angel Proorocu, Bucure³ti
La o competiµie au participat N concurenµi. Fiecare dintre ei a primit un num r de
concurs astfel încât s  nu existe concurenµi cu acela³i num r.
Numerele de concurs aparµin mulµimii r1, 2, ..., N x.
Din p cate, clasamentul nal a fost pierdut, iar comisia î³i poate aduce aminte doar câteva
relaµii între unii participanµi (de genul "participantul cu num rul 3 a ie³it înaintea celui cu num rul
5").
Cerinµ 
“eful comisiei are nevoie de un clasament nal ³i v  cere s -l ajutaµi determinând primul
clasament în ordine lexicograc  ce respect  relaµiile pe care ³i le aminte³te comisia.
Date de intrare
Fi³ier de intrare: COMPET.IN
a Linia 1: N M - dou  numere naturale nenule, reprezentând num rul concurenµilor, respectiv
num rul relaµiilor pe care ³i le aminte³te comisia;
a Liniile 2..M+1: i j - pe ecare din aceste M linii se a  câte dou  numere naturale nenule
i ³i j , având semnicaµia: concurentul cu num rul de concurs i a fost în clasament înaintea
concurentului cu num rul de concurs j .
Date de ie³ire
Fi³ier de ie³ire: COMPET.OUT
a Linia 1: nr1 nr2 ... nrN - pe aceast  linie se va scrie clasamentul sub forma unui ³ir de numere
naturale nenule, separate prin câte un spaµiu, reprezentând numerele de concurs ale concurenµilor,
în ordine de la primul clasat la ultimul.
Restricµii ³i preciz ri
a 1 $ N & 1000
a se garanteaz  corectitudinea datelor de intrare ³i faptul c  exist  totdeauna o soluµie.
Exemple
COMPET.IN COMPET.OUT COMPET.IN COMPET.OUT
31 123 42 2134
12 21
34
Timp maxim de execuµie: 1 secund /test

39.4.1 Indicaµii de rezolvare


Pentru ecare concurent se determin  num rul concurenµilor care se g sesc în clasamentul
nal în faµa lor. Dintre concurenµii care au în clasamentul nal în faµa lor 0 (zero) concurenµi se
alege cel cu num rul de concurs cel mai mic. Acesta este primul concurent din soluµie. Tuturor
concurenµilor care îl au în faµa lor pe concurentul plasat în soluµie li se scade câte o unitate din
num rul concurenµilor pe care îi au în faµa lor. Se repet  acest algoritm pân  când toµi concurenµii
au fost plasaµi în soluµie.

39.4.2 *Cod surs 


CAPITOLUL 39. ONI 2001 859

39.4.3 Rezolvare detaliat 

Listing 39.4.1: competitie.java


1 import java.io.*;
2 class competitie
3 {
4 static int n,m;
5 static int[] a;
6 static int[] b;
7 static int[] c;
8 static int[] inainte;
9 static boolean[] finalizat;
10
11 public static void main(String[] args) throws IOException
12 {
13 int i,j,igasit=-1;
14 StreamTokenizer st=new StreamTokenizer(new BufferedReader(
15 new FileReader("compet.in")));
16 PrintWriter out=new PrintWriter(new BufferedWriter(
17 new FileWriter("compet.out")));
18 st.nextToken();n=(int)st.nval;
19 st.nextToken();m=(int)st.nval;
20 a=new int[m+1];
21 b=new int[n+1];
22 c=new int[n+1]; // solutia
23 inainte=new int[n+1];
24 finalizat=new boolean[n+1];
25 for(i=1;i<=m;i++)
26 {
27 st.nextToken();a[i]=(int)st.nval; // a[i] < b[i]
28 st.nextToken();b[i]=(int)st.nval;
29 }
30
31 for(i=1;i<=m;i++) inainte[b[i]]++;
32 for(j=1;j<=n;j++)
33 {
34 for(i=1;i<=n;i++)
35 if((!finalizat[i])&&(inainte[i]==0))
36 {
37 finalizat[i]=true;
38 c[j]=i;
39 igasit=i;
40 break;
41 }
42 for(i=1;i<=m;i++)
43 if(a[i]==igasit) inainte[b[i]]--;
44 }
45
46 for(i=1;i<=n;i++) out.print(c[i]+" ");
47 out.close();
48 }
49 }

39.5 Cuvinte
prof. Maria Niµ  ³i prof. Adrian Niµ , Oradea
Se consider  o list  având un num r cunoscut de cuvinte. Din acest  list  s-au ales
dou  cuvinte oarecare. Se dore³te transformarea primului cuvânt în cel de-al doilea, trecând
prin cuvinte intermediare, existente în lista dat . Trecerea dintr-un cuvânt în altul se poate face
folosind urm toarele operaµii: schimbarea, ad ugarea sau ³tergerea unui singur caracter.
Cerinµ 
Dându-se o list  de cuvinte ³i dou  cuvinte din aceasta, g siµi cea mai scurt  secvenµ  de
operaµii care transform  primul cuvânt în cel de-al doilea folosind operaµiile permise.
Date de intrare
Fi³ierul de intrare: CUVINTE.IN
a Linia 1: N P Q - trei numere naturale nenule, reprezentând num rul cuvintelor din list 
(N ), poziµia primului cuvânt în list  (P ), respectiv poziµia celui de-al doilea cuvânt în list  (Q);
CAPITOLUL 39. ONI 2001 860

a Liniile 2..N+1: cuvânt - aceste N linii conµin ecare câte un cuvânt, aparµinând listei.
Date de ie³ire
Fi³ier de ie³ire: CUVINTE.OUT
a Linia 1: M - num r natural, reprezentând num rul minim de pa³i necesari pentru a ajunge
de la primul cuvânt la al doilea;
a Liniile 2..M+2: cuvânti - pe aceste linii apar în ordine cuvintele dintr-o secvenµ  ce respect 
cerinµa problemei (câte un cuvânt pe linie), inclusiv primul cuvânt al secvenµei.
Restricµii ³i preciz ri
a 2 & N & 100
a 1 & M, P, Q & 100
a num rul maxim de caractere dintr-un cuvânt este 100;
a dac  nu exist  soluµie, în ³ierul de ie³ire se va scrie num rul 0 (zero);
a dac  sunt mai multe secvenµe de lungime minim , în ³ier se va scrie una singur .

Exemple
CUVINTE.IN CUVINTE.OUT CUVINTE.IN CUVINTE.OUT
715 2 716 0
car car car
cer mar cer
cerc mare cerc
mar mar
mare mare
rosu rosu
inrosit inrosit
Timp maxim de execuµie: 2 secunde/test

39.5.1 Indicaµii de rezolvare


Folosim un tablou bidimensional aij 1&i,j &n cu urm toarea semnicaµie: aij  1
dac  se poate transforma cuvântul i în cuvântul j , ³i aij  0 altfel. Se parcurge în l µime
graful neorientat asociat, plecând de la primul cuvânt, pân  când se întâlne³te al doilea cuvânt
sau nu mai sunt cuvinte care pot  atinse prin aceast  parcurgere.

39.5.2 *Cod surs 

39.5.3 Rezolvare detaliat 

Listing 39.5.1: cuvinte.java


1 import java.io.*;
2 class Cuvinte
3 {
4 static int n,p,q;
5 static String[] cuvant=new String[101]; // cuvintele
6 static int[] coada=new int[101];
7 static int[] analizat=new int[101];
8 static int[] d=new int[101]; // distante catre p
9 static int[] pr=new int[101]; // predecesori
10 static int[][] a=new int[101][101]; // matrice de adiacenta
11 static PrintWriter out;
12
13 public static void main(String[] args) throws IOException
14 {
15 int i,j,k;
16 int ic,sc;
17 out=new PrintWriter(new BufferedWriter(new FileWriter("cuvinte.out")));
18 StreamTokenizer st=new StreamTokenizer(
19 new BufferedReader(new FileReader("cuvinte.in")));
20 st.nextToken(); n=(int)st.nval;
CAPITOLUL 39. ONI 2001 861

21 st.nextToken(); p=(int)st.nval;
22 st.nextToken(); q=(int)st.nval;
23 for(i=1;i<=n;i++) { st.nextToken(); cuvant[i]=st.sval.toString(); }
24 for(i=1;i<n;i++)
25 for(j=i+1;j<=n;j++)
26 a[i][j]=a[j][i]=trece(cuvant[i],cuvant[j]);
27
28 // drum minim intre p si q
29 ic=0; sc=1; coada[ic]=p; // ic==sc ==> coada vida!
30 analizat[p]=1;
31 while(ic!=sc)
32 {
33 i=coada[ic]; ic++;
34 for(j=1;j<=n;j++)
35 {
36 if((analizat[j]==0)&&(a[i][j]==1))
37 {
38 coada[sc]=j; sc++;
39 analizat[j]=1;
40 d[j]=d[i]+1;
41 pr[j]=i;
42 if(j==q) break;
43 }
44 }
45 if(analizat[q]==1) break;
46 }
47 out.println(d[q]);
48 drum(q);
49 out.close();
50 }
51
52 static void drum(int j)
53 {
54 if(pr[j]!=0) drum(pr[j]);
55 out.println(cuvant[j]);
56 }
57
58 static int trece(String x, String y) // lg egale sau diferenta=1
59 {
60 int k,dif;
61 if(Math.abs(x.length()-y.length())>1) return 0;
62 String xy;
63 if(x.length()<y.length()) {xy=x; x=y; y=xy;} // lg.x >= lg.y
64
65 int nx=x.length(), ny=y.length();
66 if(nx==ny)
67 {
68 dif=0;
69 for(k=0;k<nx;k++) if(x.charAt(k)!=y.charAt(k)) dif++;
70 if(dif==1) return 1; else return 0;
71 }
72 else // nx>ny
73 {
74 k=0;
75 while((k<ny)&&(x.charAt(k)==y.charAt(k))) k++;
76 if(k==ny) return 1;
77 else
78 {
79 k++;
80 while((k<ny)&&(x.charAt(k)==y.charAt(k-1))) k++;
81 if(k==ny) return 1; else return 0;
82 }
83 }
84 }
85 }

39.6 Grup
prof. Emanuela Cerchez ³i prof. Marinel “erban, Ia³i
Administratorul reµelei cu N calculatoare de la SRI împarte, din motive strategice, aceste
calculatoare în mai multe grupuri. De fapt, important este doar num rul de grupuri ³i num rul de
calculatoare din ecare grup, a³a c  împ rµirea este descris  prin ³irul numerelor de calculatoare
din ecare grup, ordonat cresc tor.
CAPITOLUL 39. ONI 2001 862

Periodic, el procedeaz  la o nou  împ rµire pe grupe a calculatoarelor.


Dintre toate împ rµirile posibile ale calculatoarelor în grupuri putem alege ca urm toare îm-
p rµire doar aceea a c rei descriere precede sau succede lexicograc imediat împ rµirii curente.
Not :
Spunem c  ³irul x1 x2 ... xp precede lexicograc ³irul y1 y2 ... yk dac :
a) exist  un indice j , astfel încât xi yi pentru toµi indicii i $ j ³i xj $ yj
sau
b) p $ k ³i xi yi pentru toµi indicii i & p
Exemple
a) 3 7 2 5 precede lexicograc 3 7 4 1 6 2
b) 1 2 3 precede lexicograc 2
Cerinµ 
Dându-se o împ rµire în grupe a celor N calculatoare, determinaµi cele dou  variante candidate
pentru împ rµirea urm toare.
Date de intrare
Fi³ier de intrare: GRUP.IN
a Linia 1: N k - numere naturale nenule, reprezentând num rul total (N ) al calculatoarelor
din reµea ³i num rul (k ) de grupe.
a Linia 2: g1 g2 . . . gk - num rul calculatoarelor din ecare grup .

Date de ie³ire
Fi³ier de ie³ire: GRUP.OUT
a p - num rul de grupe din împ rµirea care precede lexicograc imediat împ rµirea dat ;
a h1 h2 ... hp - num rul de calculatoare din cele p grupe ale împ rµirii precedente;
a u - num rul de grupe din împ rµirea care succede lexicograc imediat împ rµirea dat ;
a t1 t2 ... tu - num rul de calculatoare din cele u grupe ale împ rµirii urm toare;

Restricµii ³i preciz ri
a 2 & N & 1000
a g1  g2  ...  gk h1  h2  ...  hp t1  t2  ...  tu N
a 1 & g1 & g2 & ... & gk ; 1 & h1 & h2 & ... & hp ; 1 & t1 & t2 & ... & tu ;
a 1$k$N
a 1 & p, u & N
Exemplu
GRUP.IN GRUP.OUT
14 3 3
266 257
2
2 12
Timp maxim de execuµie: 1 secund /test

39.6.1 Indicaµii de rezolvare


Fie g i num rul calculatoarelor din grupul i (1 & i & k .
Pentru secvenµa precedent  (lexicograc):
Dac  g k  1 $ g k ©2 atunci secvenµa are k  1 grupe. Grupul g k  se împarte în dou 
grupuri (prima jum tate se plaseaz  pe poziµia k iar a doua jum tate se plaseaz  pe poziµia k  1
(dac  g k  este impar, grupul de pe poziµia k  1 va avea cu un calculator mai mult decât grupul
de pe poziµia k ).
Dac  g k  1 % g k ©2 atunci se determin  cel mai mare indice i astfel încât g i  1 ' g i  1.
Secvenµa va avea i  1 grupe. Primele i  1 grupe de calculatoare r mân neschimbate. Pe poziµia
i plas m g i  1 calculatoare iar pe poziµia i  1 restul calculatoarelor.
Pentru secvenµa urm toare (lexicograc):
Dac  g k  1  1 % g k   1 atunci secvenµa are k  1 grupe. Primele k  2 grupe r mân
neschimbate iar pe poziµia k  1 se pun împreun  calculatoarele de pe poziµiile k  1 ³i k (din
secvenµa iniµial ).
Dac  g k  1  1 & g k   1 atunci primele k  2 grupuri r mân neschimbate iar calculatoarele
din grupurile k  1 ³i k (din secvenµa iniµial ) se vor distribui în grupuri de g k  1  1 calculatoare
CAPITOLUL 39. ONI 2001 863

(cu excepµia ultimului grup, eventual, care va avea cel puµin g k  1  1 calculatoare dar nu mai
mult de 2g k  1  1 calculatoare).

39.6.2 *Cod surs 

39.6.3 Rezolvare detaliat 

Listing 39.6.1: grup.java


1 import java.io.*;
2 class Grup
3 {
4 static int n,k;
5 static int[] g;
6
7 public static void main(String[] args) throws IOException
8 {
9 int i,j,s,nge;
10 StreamTokenizer st=new StreamTokenizer(
11 new BufferedReader(new FileReader("grup.in")));
12 PrintWriter out=new PrintWriter(
13 new BufferedWriter(new FileWriter("grup.out")));
14 st.nextToken(); n=(int)st.nval;
15 st.nextToken(); k=(int)st.nval;
16 g=new int[k+1];
17 for(i=1;i<=k;i++) { st.nextToken(); g[i]=(int)st.nval; }
18
19 if(g[k-1]<=g[k]/2)
20 {
21 out.println(k+1); // nr grupe din precedenta
22 for(j=1;j<=k-1;j++) out.print(g[j]+" ");
23 out.println((g[k]/2)+" "+(g[k]+1)/2);
24 }
25 else
26 {
27 s=g[k];
28 i=k-1;
29 while(g[i]-1<g[i-1]) {s+=g[i]; i--;}
30 out.println(i+1); // nr grupe din precedenta
31 for(j=1;j<i;j++) out.print(g[j]+" ");
32 out.println((g[i]-1)+" "+(s+1));
33 }
34
35 if(g[k-1]+1<=g[k]-1)
36 {
37 nge=(g[k]-1)/(g[k-1]+1);// nr grupe egale la sfarsit
38 out.println(k+nge-1); // nr grupe din urmatoarea
39 for(j=1;j<=k-2;j++) out.print(g[j]+" ");
40 for(j=1;j<=nge;j++) out.print((g[k-1]+1)+" ");
41 out.println(g[k-1]+1+(g[k]-1)%(g[k-1]+1));
42 }
43 else
44 {
45 out.println(k-1); // nr grupe din urmatoarea
46 for(j=1;j<=k-2;j++) out.print(g[j]+" ");
47 out.println((g[k-1]+g[k]));
48 }
49 out.close();
50 }
51 }
Capitolul 40

ONI 2000

Figura 40.1: Sigla ONI 2000

40.1 Algoritm
prof. Roxana Tîmplaru, Liceul "“tefan Odobleja", Craiova
Georgel scrie un algoritm care conµine structuri de atribuire, alternative, de selecµie,
repetitive ³i compuse. Dup  scrierea algoritmului vrea s -l testeze pentru toate cazurile posibile.
Pentru aceasta trebuie s  cunoasc  num rul minim de date de test necesare.
Pentru descrirea structurilor se utilizeaz  urm toarele cuvinte cheie:
- pentru atribuire ATRIB
- pentru alternativ  DACA
ATUNCI
ALTFEL unde ALTFEL poate lipsi
- pentru selecµie ALEGE
CAZ
CAZ_IMPLICIT unde CAZ_IMPLICIT poate lipsi
- pentru repetitive 1) CAT_TIMP
2) REPETA
PANA_CAND
3) PENTRU
- pentru compus  INCEPUT
SFARSIT
Nu se face diferenµ  între literele mari ³i literele mici. Un algoritm începe cu cuvântul cheie
INCEPUT, se termin  cu SFARSIT ³i nu conµine structuri vide. De asemenea o structur  compus 
începe cu cuvântul cheie INCEPUT ³i se termin  cu SFARSIT.
Cerinµ 
S  se calculeze num rul minim de date de test necesare pentru vericarea algoritmului.
Date de intrare:
Din ³ierul text ALGOR.IN se cite³te algoritmul.
Fi³ierul conµine pe ecare linie câte un singur cuvânt cheie.
Nu exist  linii vide.
Algoritmul respect  principiile program rii structurate ³i este scris corect.
Date de ie³ire:
În ³ierul ALGOR.OUT se va scrie pe prima linie num rul minim de date de test necesare
pentru vericarea algoritmului. Vericarea se bazeaz  pe principiul "cutiei transparente", ceea ce

864
CAPITOLUL 40. ONI 2000 865

înseamn  c  testele se compun astfel încât s  e posibil  executarea algoritmului pe toate ramurile
posibile.
De exemplu, în cazul unei structuri repetitive CAT_TIMP care conµine în corpul s u un singur
ATRIB, un test vizeaz  o execuµie f r  s  se intre în corpul structurii, altul pentru a trece cel
puµin o dat  ³i prin corpul acestuia.
În mod similar se trateaz  ³i structura PENTRU.
Restricµii ³i preciz ri
a Dup  cuvintele cheie
ATUNCI, ALTFEL,
CAZ, CAZ_IMPLICIT,
CAT_TIMP, REPETA, PENTRU
trebuie s  existe obligatoriu o structur  de atribuire, alternativ , de decizie, repetitiv  sau
compus .
Exemplul 1:
ALGOR.IN ALGOR.OUT
INCEPUT 2
atrib
DACA
atunci
ATRIB
SFARSIT
Exemplul 2:
ALGOR.IN ALGOR.OUT OBS.
INCEPUT 1 REPETA se execut  cel puµin o dat 
ATRIB
REPETA
inceput
atrib
atrib
SFARSIT
pana_cand
SFARSIT
Exemplul 3:
ALGOR.IN ALGOR.OUT OBS.
INCEPUT 3 - se execut  ATRIB de la primul CAZ
ATRIB - se execut  ATRIB de la al doilea CAZ
ALEGE - nu se execut  nici primul CAZ,
CAZ nici al doilea
ATRIB
CAZ
INCEPUT
ATRIB
ATRIB
SFARSIT
SFARSIT
Exemplul 4:
ALGOR.IN ALGOR.OUT OBS.
INCEPUT 3 - se execut  ATRIB
ATRIB de la primul CAZ
ALEGE - se execut  ATRIB
CAZ de la al doilea CAZ
ATRIB - se execut  ATRIB
CAZ de la CAZ_IMPLICIT
ATRIB
CAZ_IMPLICIT
ATRIB
SFARSIT
Exemplul 5:
CAPITOLUL 40. ONI 2000 866

ALGOR.IN ALGOR.OUT OBS.


INCEPUT 4 - se execut  ATUNCI ³i PENTRU
atrib - se execut  ATUNCI
DACA ³i nu se execut  PENTRU
ATUNCI - se execut  ALTFEL ³i PENTRU
ATRIB - se execut  ALTFEL
ALTFEL ³i nu se execut  PENTRU
ATRIB În total 4 teste.
pentru
atrib
SFARSIT
Timp maxim de execuµie pe test: 1 secund 

40.1.1 Indicaµii de rezolvare


Metoda de rezolvare este Divide et Impera. Problema se descompune în instrucµiuni elemen-
tare folosind recursivitatea.

40.1.2 Cod surs 

Listing 40.1.1: Algoritm-oc.pas


1 program Testare;
2 var
3 fin, fout: text;
4 atom: string;
5
6 { Analizor lexical }
7 procedure AnaLex;
8 var cuv: string;
9 ch: char;
10 begin
11 { eof }
12 if eof(fin) then atom := ’eof’
13 else begin
14 { citeste si codifica }
15 cuv := ’’;
16 repeat
17 read(fin, ch);
18 if ch in [’A’..’Z’] then ch := chr(ord(ch)+32);
19 if ch in [’a’..’z’, ’_’] then cuv := cuv + ch
20 else break;
21 until eoln(fin);
22 readln(fin);
23 atom := cuv;
24 end
25 end;
26
27 { Calculeaza numar cai }
28 function Instr: longint;
29 var nr, nrAtunci, nrAltfel, nrCaz, nrTemp: longint;
30 contor: integer;
31 implicit: boolean;
32 begin
33 nr := 1;
34 if atom = ’atrib’ then begin
35 AnaLex;
36 nr := 1;
37 end
38 else if atom = ’daca’ then begin
39 { Atunci }
40 AnaLex;
41 AnaLex;
42 nrAtunci := Instr;
43 { Altfel }
44 nrAltfel := 1;
45 if atom = ’altfel’ then begin
46 AnaLex;
47 nrAltfel := Instr;
CAPITOLUL 40. ONI 2000 867

48 end;
49 nr := nrAtunci + nrAltfel;
50 end
51
52 else if atom = ’alege’ then begin
53 AnaLex;
54 implicit := false;
55 nr := 0;
56 while (atom = ’caz’) or (atom = ’caz_implicit’) do begin
57 if (atom = ’caz_implicit’) then implicit := true;
58 AnaLex;
59 nrCaz := Instr;
60 nr := nr + nrCaz;
61 end;
62 if not implicit then inc(nr);
63 end
64
65 else if atom = ’cat_timp’ then begin
66 AnaLex;
67 nr := Instr+1;
68 end
69
70 else if atom = ’repeta’ then begin
71 AnaLex;
72 nr := Instr;
73 AnaLex;
74 end
75
76 else if atom = ’pentru’ then begin
77 AnaLex;
78 nr := Instr+1;
79 end
80
81 else if atom = ’inceput’ then begin
82 contor := 0;
83 repeat
84 if atom = ’inceput’ then begin
85 AnaLex;
86 inc(contor)
87 end
88 else if atom = ’sfarsit’ then begin
89 AnaLex;
90 dec(contor);
91 end
92 else begin
93 nrTemp := Instr;
94 nr := nr * nrTemp;
95 end
96 until contor = 0;
97 end
98
99 else nr := 1;
100
101 Instr := nr;
102 end;
103
104 begin
105 { Deschidere fisiere }
106 assign(fin, ’Algor.in’);
107 reset(fin);
108 assign(fout, ’Algor.out’);
109 rewrite(fout);
110
111 { Calcul }
112 AnaLex;
113 writeln(fout,Instr);
114 close(fout);
115 close(fin);
116 end.

40.1.3 Rezolvare detaliat 

Listing 40.1.2: algoritm1.java


CAPITOLUL 40. ONI 2000 868

1 import java.io.*;
2 class Algoritm1
3 {
4 static String atom;
5 static String fin="algor.in";
6 static String fout="algor.out";
7 static StreamTokenizer st;
8 static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
9 static String pauza;
10
11 public static void main(String[] args) throws IOException
12 {
13 st=new StreamTokenizer(
14 new BufferedReader( new FileReader(fin)));
15 st.wordChars((int)’_’,(int)’_’);
16
17 PrintWriter out=new PrintWriter(
18 new BufferedWriter(new FileWriter(fout)));
19
20 readAtom();
21 out.println(nrTeste()); // "inceput" bloc
22 out.close();
23 }// main
24
25 static int nrTeste() throws IOException
26 {
27 int nr, nrTesteAtunci, nrTesteAltfel, nrTesteCaz;
28 int nrBlocuriDeschise; // inceput
29 boolean implicit;
30
31 String atomr=new String(atom);
32 System.out.println(" ***> "+atomr);
33
34 if(atom.equals("inceput")) // inceput bloc
35 {
36 nr=1; // initializare pentru produsul: nr=nr*nrTeste();
37 nrBlocuriDeschise=1;
38 readAtom();
39 while(nrBlocuriDeschise!=0)
40 {
41 if(atom.equals("inceput")) {nrBlocuriDeschise++; readAtom();}
42 else if(atom.equals("sfarsit")) {nrBlocuriDeschise--; readAtom();}
43 else nr=nr*nrTeste();
44 }
45 }
46 else if(atom.equals("atrib")) { readAtom(); nr=1; }
47 else if(atom.equals("daca"))
48 {
49 readAtom(); // citeste "atunci"
50 readAtom(); nrTesteAtunci=nrTeste();
51 nrTesteAltfel=1;
52 if(atom.equals("altfel")) {readAtom(); nrTesteAltfel=nrTeste();}
53 nr=nrTesteAtunci+nrTesteAltfel;
54 }
55 else if(atom.equals("alege"))
56 {
57 implicit=false;
58 nr=0;
59 readAtom();
60 while(atom.equals("caz")||atom.equals("caz_implicit"))
61 {
62 if(atom.equals("caz_implicit")) implicit = true;
63 readAtom(); nrTesteCaz=nrTeste();
64 nr=nr+nrTesteCaz;
65 }
66 if(!implicit) nr++;
67 }
68 else if(atom.equals("cat_timp")) {readAtom(); nr=nrTeste()+1;}
69 else if(atom.equals("repeta"))
70 {
71 readAtom(); nr=nrTeste();
72 readAtom(); // citeste ce urmeaza dupa "pana_cand"
73 }
74 else if(atom.equals("pentru")) {readAtom(); nr=nrTeste()+1;}
75 else nr=1; // la eof
CAPITOLUL 40. ONI 2000 869

76
77 System.out.println(" <*** "+atomr);
78 return nr;
79 }
80
81 static void readAtom() throws IOException
82 {
83 if(st.nextToken()==StreamTokenizer.TT_EOF) atom="eof";
84 else atom=st.sval.toString().toLowerCase();
85
86 System.out.println("readAtom() "+atom);
87 pauza=br.readLine();
88 }
89 }// class

Listing 40.1.3: algoritm.java


1 import java.io.*;
2 class Algoritm
3 {
4 static String atom;
5 static String fin="algor.in";
6 static String fout="algor.out";
7 static StreamTokenizer st;
8
9 public static void main(String[] args) throws IOException
10 {
11 st=new StreamTokenizer(
12 new BufferedReader(new FileReader(fin)));
13 st.wordChars((int)’_’,(int)’_’);
14
15 PrintWriter out=new PrintWriter(
16 new BufferedWriter(new FileWriter(fout)));
17
18 readAtom(); // citeste "inceput" bloc program
19 out.println(nrTeste());
20 out.close();
21 }// main
22
23 static int nrTeste() throws IOException
24 {
25 int nr, nrTesteAtunci, nrTesteAltfel, nrTesteCaz;
26 int nrBlocuriDeschise; // inceput
27 boolean implicit;
28
29 if(atom.equals("inceput")) // inceput bloc
30 {
31 nr=1; // initializare pentru produsul: nr=nr*nrTeste();
32 nrBlocuriDeschise=1;
33 readAtom();
34 while(nrBlocuriDeschise!=0)
35 {
36 if(atom.equals("inceput")) {nrBlocuriDeschise++; readAtom();}
37 else if(atom.equals("sfarsit")) {nrBlocuriDeschise--; readAtom();}
38 else nr=nr*nrTeste();
39 }
40 }
41 else if(atom.equals("atrib")) { readAtom(); nr=1; }
42 else if(atom.equals("daca"))
43 {
44 readAtom(); // citeste "atunci"
45 readAtom(); nrTesteAtunci=nrTeste();
46 nrTesteAltfel=1;
47 if(atom.equals("altfel")) {readAtom(); nrTesteAltfel=nrTeste();}
48 nr=nrTesteAtunci+nrTesteAltfel;
49 }
50 else if(atom.equals("alege"))
51 {
52 implicit=false;
53 nr=0;
54 readAtom();
55 while(atom.equals("caz")||atom.equals("caz_implicit"))
56 {
57 if(atom.equals("caz_implicit")) implicit = true;
58 readAtom(); nrTesteCaz=nrTeste();
CAPITOLUL 40. ONI 2000 870

59 nr=nr+nrTesteCaz;
60 }
61 if(!implicit) nr++;
62 }
63 else if(atom.equals("cat_timp")) {readAtom(); nr=nrTeste()+1;}
64 else if(atom.equals("repeta"))
65 {
66 readAtom(); nr=nrTeste();
67 readAtom(); // citeste ce urmeaza dupa "pana_cand"
68 }
69 else if(atom.equals("pentru")) {readAtom(); nr=nrTeste()+1;}
70 else nr=1; // pentru "eof"
71
72 return nr;
73 }
74
75 static void readAtom() throws IOException
76 {
77 if(st.nextToken()==StreamTokenizer.TT_EOF) atom="eof";
78 else atom=st.sval.toString().toLowerCase();
79 }
80 }// class

40.2 Cod de identicare


prof. Eugen Ionescu, Liceul "Tiberiu Popoviciu", Cluj-Napoca
Pentru a concura cu num rul de serie de la procesoarele Intel Pentium III, Advanced
Micro Devices (AMD) a stabilit un sistem de identicare pentru noile procesoare cu numele de
cod Thunderbird. Fiecare rm  distribuitoare prime³te o mulµime de litere (de exemplu: ra, m, xx)
din care va trebui s -³i formeze codurile proprii de identicare.
Firmelor li se impune exact de câte ori trebuie s  apar  ecare liter  în aceste coduri. De
exemplu, o rm  trebuie s  formeze identicatori care s  conµin  exact 3 litere a, 2 litere m ³i 1
liter  x.
Cerinµ 
Scrieµi un program care, cunoscând un anumit cod dat, determin  urm torul cod corect în
ordine lexicograc , dac  exist  un astfel de cod urm tor.
Date de intrare
Singura linie a ³ierului de intrare conµine un cod.

Date de ie³ire
Fi³ierul de ie³ire COD.OUT va conµine o singur  linie pe care se va aa codul urm tor; dac 
nu exist  un astfel de cod, atunci în ³ier se va scrie "Este ultimul cod."
Restricµii ³i preciz ri
a Codurile sunt formate din cel mult 100 de litere mici ale alfabetului latin.
Exemple:
COD.IN COD.OUT COD.IN COD.OUT
amaaxm amamax xmmaaa Este ultimul cod.
Timp de execuµie: 1 secund /test.

40.2.1 Indicaµii de rezolvare

Se determin  cel mai mare indice i pentru care cod[i]<cod[i+1]. Caracterele de pe ultimile
poziµii din cod, incepând cu poziµia i inclusiv, se scriu în noul cod în ordine cresc toare.

40.2.2 Cod surs 

Listing 40.2.1: cod2.pas


1 program cod_prog;
CAPITOLUL 40. ONI 2000 871

2 label e;
3 var
4 cod,cod_temp:string;
5 i,j:byte;
6 t:text;
7
8 function nedes(s:string):boolean;
9 var
10 i:byte;
11 begin
12 nedes:=false;
13 for i:=1 to length(s)-1 do
14 if s[i]<s[i+1] then
15 begin
16 nedes:=true;
17 break;
18 end;
19 end;
20
21 procedure next(pos:byte);
22 var
23 i,start,j:byte;
24 next_min:byte;
25 ch:char;
26
27 begin
28 for i:=pos+1 to length(cod) do if cod[i]>cod[pos] then
29 begin
30 next_min:=i;
31 break;
32 end;
33 if next_min<length(cod) then
34 begin
35 start:=next_min+1;
36 for i:=start to length(cod) do
37 if (cod[i]>cod[pos]) and (cod[i]<cod[next_min]) then next_min:=i;
38 ch:=cod[pos];
39 cod[pos]:=cod[next_min];
40 cod[next_min]:=ch;
41 if pos+1<length(cod) then
42 for i:=pos+1 to length(cod)-1 do
43 begin
44 start:=i;
45 for j:=i+1 to length(cod) do
46 if cod[start]>cod[j] then start:=j;
47 ch:=cod[i];
48 cod[i]:=cod[start];
49 cod[start]:=ch;
50 end;
51 end
52 else
53 begin
54 ch:=cod[length(cod)];
55 cod[length(cod)]:=cod[length(cod)-1];
56 cod[length(cod)-1]:=ch;
57 end;
58 end;
59
60 begin
61 assign(t,’cod.in’);
62 reset(t);
63 read(t,cod);
64 close(t);
65 assign(t,’cod.out’);
66 rewrite(t);
67 if nedes(cod)=true then
68 begin
69 for i:=length(cod)-1 downto 1 do
70 begin
71 for j:=i to length(cod) do cod_temp:=cod_temp+cod[j];
72 if nedes(cod_temp) then
73 begin
74 next(i);
75 writeln(t,cod);
76 goto e;
77 end;
CAPITOLUL 40. ONI 2000 872

78 end;
79 if i=1 then
80 begin
81 writeln(t,’Este ultimul cod’);
82 goto e;
83 end;
84 end
85 else writeln(t,’Este ultimul cod’);
86 e:close(t);
87 end.

Listing 40.2.2: cod-oc.pas


1 Program Coduri;
2 const MaxN=100;
3 var cod,urm:string;
4 nr,n,i:Word;
5 fi,fo:Text;
6
7 procedure calcul;
8 var nr_lit:array[’a’..’z’] of Word;
9 i:Word;
10 x:Char;
11 ok:Boolean;
12 begin
13 for x:=’a’ to ’z’ do nr_lit[x]:=0;
14 i:=n;
15 ok:=False;
16 while i>0 do
17 begin
18 Inc(nr_lit[cod[i]]);
19 for x:=Succ(cod[i]) to ’z’ do
20 if nr_lit[x]>0
21 then begin ok:=true; Break end;
22 if ok then Break;
23 Dec(i)
24 end;
25
26 if i=0
27 then urm:=’Este ultimul cod.’
28 else
29 begin
30 Dec(nr_lit[x]);
31 cod[i]:=x;
32 for x:=’a’ to ’z’ do
33 begin
34 while nr_lit[x]>0 do
35 begin
36 Inc(i);
37 cod[i]:=x;
38 Dec(nr_lit[x])
39 end
40 end;
41 urm:=cod
42 end
43 end;
44
45 Begin
46 Assign(fi,’COD.IN’); Reset(fi);
47 Assign(fo,’COD.OUT’); Rewrite(fo);
48 Readln(fi,cod);
49 n:=Length(cod);
50 calcul;
51 Writeln(fo,urm);
52 Close(fi);
53 Close(fo)
54 End.

40.2.3 Rezolvare detaliat 


O prim  încercare ca antrenament cu string-uri!

Listing 40.2.3: cod1.java


CAPITOLUL 40. ONI 2000 873

1 import java.io.*;
2 class Cod1
3 {
4 static String cod;
5 static int n;
6 static int[] nrl=new int[26];
7
8 public static void main(String[] args) throws IOException
9 {
10 int i;
11 BufferedReader br=new BufferedReader(new FileReader("cod.in"));
12 PrintWriter out=new PrintWriter(
13 new BufferedWriter(new FileWriter("cod.out")));
14 cod=br.readLine();
15 n=cod.length();
16
17 System.out.println(cod);
18 for(i=0;i<n;i++) System.out.print(cod.charAt(i)+"\t ");
19 System.out.println();
20 for(i=0;i<n;i++) System.out.print((byte)cod.charAt(i)+"\t ");
21 System.out.println();
22 for(i=0;i<n;i++) System.out.print(((byte)cod.charAt(i)-’a’)+"\t ");
23 System.out.println();
24 for(i=0;i<n;i++) nrl[(byte)cod.charAt(i)-’a’]++;
25 System.out.println();
26 for(i=0;i<26;i++)
27 if(nrl[i]>0) System.out.println(i+"\t"+(char)(i+’a’)+"\t"+nrl[i]);
28 out.close();
29 }
30 }

Totu³i, merit  lucrat cu vector de caractere!

Listing 40.2.4: cod2.java


1 import java.io.*;
2 class Cod2
3 {
4 static char[] cod;
5 static int n;
6 static int[] nrl1=new int[26];
7 static int[] nrl2=new int[26]; // pentru a evita o sortare !
8
9 public static void main(String[] args) throws IOException
10 {
11 int i,j,k,kk;
12 BufferedReader br=new BufferedReader(new FileReader("cod.in"));
13 PrintWriter out=new PrintWriter(
14 new BufferedWriter(new FileWriter("cod.out")));
15 cod=br.readLine().toCharArray();
16 n=cod.length;
17
18 for(k=0;k<n;k++) System.out.print(cod[k]);
19 System.out.println();
20
21 for(i=0;i<n;i++) nrl1[(byte)cod[i]-’a’]++;
22 for(i=0;i<26;i++) nrl2[i]=nrl1[i];
23
24 i=n-2; // cod[0] ... cod[i] cod[i+1] ... cod[n-2] cod[n-1]
25 while((i>=0)&&(cod[i]>=cod[i+1]))
26 {
27 j=(byte)cod[i+1]-’a’;
28 nrl2[j]--;
29 i--;
30 }
31 j=(byte)cod[i+1]-’a’;
32 nrl2[j]--;
33
34 for(k=0;k<i;k++) System.out.print(cod[k]);
35 System.out.println();
36
37 if(i<0) out.println("Este ultimul cod.");
38 else
39 {
40 j=(byte)cod[i]-’a’; // j = "codul" caracterului cod[i]
CAPITOLUL 40. ONI 2000 874

41 nrl2[j]--; // caractere de pus la sfarsit!


42
43 for(k=0;k<26;k++)
44 if(nrl2[k]!=nrl1[k])
45 System.out.println((char)(k+’a’)+" "+nrl2[k]+" "+nrl1[k]);
46
47 for(k=j+1;k<26;k++) // caut primul caracter > cod[i]
48 if(nrl2[k]<nrl1[k]) // si il pun pe pozitia i
49 {
50 cod[i]=(char)(k+’a’);
51 nrl2[k]++;
52 break;
53 }
54
55 for(k=0;k<=i;k++) System.out.print(cod[k]);
56 System.out.println();
57
58 for(k=0;k<26;k++)
59 if(nrl2[k]!=nrl1[k])
60 System.out.println((char)(k+’a’)+" "+nrl2[k]+" "+nrl1[k]);
61
62 i++;
63 for(k=0;k<26;k++)
64 if(nrl2[k]<nrl1[k]) // poate lipsi !
65 for(j=nrl2[k];j<nrl1[k];j++)
66 {
67 cod[i]=(char)(k+’a’);
68 for(kk=0;kk<=i;kk++) System.out.print(cod[kk]);
69 System.out.println();
70 i++;
71 }
72 for(k=0;k<n;k++) out.print(cod[k]);
73 out.println();
74 }
75 out.close();
76 }
77 }

Eliminând mesajele de urm rire a execuµiei obµinem versiunea nal .

Listing 40.2.5: cod3.java


1 import java.io.*;
2 class Cod3
3 {
4 static char[] cod;
5 static int n;
6 static int[] nrl1=new int[26];
7 static int[] nrl2=new int[26]; // pentru a evita o sortare !
8
9 public static void main(String[] args) throws IOException
10 {
11 int i,j,k,kk;
12 BufferedReader br=new BufferedReader(new FileReader("cod.in"));
13 PrintWriter out=new PrintWriter(
14 new BufferedWriter(new FileWriter("cod.out")));
15 cod=br.readLine().toCharArray();
16 n=cod.length;
17
18 for(i=0;i<n;i++) nrl1[(byte)cod[i]-’a’]++;
19 for(i=0;i<26;i++) nrl2[i]=nrl1[i];
20
21 i=n-2; // cod[0] ... cod[i] cod[i+1] ... cod[n-2] cod[n-1]
22 while((i>=0)&&(cod[i]>=cod[i+1]))
23 {
24 j=(byte)cod[i+1]-’a’;
25 nrl2[j]--;
26 i--;
27 }
28 j=(byte)cod[i+1]-’a’; // trebuie si el!
29 nrl2[j]--;
30
31 if(i<0) out.println("Este ultimul cod.");
32 else
33 {
34 j=(byte)cod[i]-’a’; // j = "codul" caracterului cod[i]
CAPITOLUL 40. ONI 2000 875

35 nrl2[j]--; // caractere de pus la sfarsit!


36
37 for(k=j+1;k<26;k++) // caut primul caracter > cod[i]
38 if(nrl2[k]<nrl1[k]) // si il pun pe pozitia i
39 {
40 cod[i]=(char)(k+’a’);
41 nrl2[k]++;
42 break;
43 }
44
45 i++;
46 for(k=0;k<26;k++)
47 if(nrl2[k]<nrl1[k]) // poate lipsi !
48 for(j=nrl2[k];j<nrl1[k];j++)
49 {
50 cod[i]=(char)(k+’a’);
51 i++;
52 }
53 for(k=0;k<n;k++) out.print(cod[k]);
54 out.println();
55 }
56 out.close();
57 }
58 }

40.3 Comoara
Mihai Stroe, student, Universitatea Politehnica, Bucure³ti
Cei K membri ai unui grup de c ut tori de comori se a  într-un complex dreptunghiular
format din camere p trate de latur  1.
În mod normal toate camerele ar trebui s  e deschise, dar o parte dintre ele sunt închise ³i
pot  deschise doar cu ajutorul unor cartele speciale. Astfel, ecare camer  ³i ecare cartel  are
asociat un num r între 0 ³i 20; o camer  cu num rul asociat 0 este deschis  de la început, în
timp ce una cu num rul asociat diferit de 0 este iniµial închis , poate  deschis  din interior sau
din exterior dintr-o camer  vecin  cu o cartel  cu num rul corespunz tor ³i va r mâne deschis 
ulterior.
Cartelele cu num rul asociat 0 nu au nici o întrebuinµare practic . Un num r poate  asociat
mai multor cartele, respectiv camere. Dintr-o camer  se poate trece în alta numai dac  ele sunt
vecine ³i ambele sunt deschise. Dou  camere se consider  vecine dac  au un perete (deci o latur )
în comun.
În ecare camer  se a  comori cu o valoare între 0 ³i 10000. De asemenea, în ecare camer 
se a  exact o cartel  de acces (cu num rul asociat între 0 ³i 20); un c ut tor de comori poate
ridica ³i folosi cartelele din camerele prin care trece, dar nu le poate da unui alt c ut tor decât
dac  respectivul se a  în aceea³i camer  cu el.
Cerinµ 
Cunoscându-se conguraµia complexului ³i poziµiile iniµiale ale c ut torilor de comori, s  se
determine valoarea maxim  a comorilor adunate de ace³tia.
Datele de intrare
se citesc din ³ierul COMOARA.IN care are urm torul format:
 pe prima linie dimensiunile complexului, m ³i n
 pe urm toarele m linii numerele asociate ec rei camere
 pe urm toarele m linii valorile comorilor din ecare camer 
 pe urm toarele m linii numerele asociate cartelelor din ecare camer 
 pe urm toarea linie num rul K al membrilor grupului
 pe urm toarele K linii poziµiile iniµiale ale membrilor grupului în complex

Datele de ie³ire:
În ³ierul COMOARA.OUT se va a³a valoarea total  a comorilor care pot  strânse de cei
K membri ai grupului.
Restricµii ³i preciz ri
a m, n & 40, k & 10
Exemple
CAPITOLUL 40. ONI 2000 876

COMOARA.IN COMOARA.OUT
33 23909
0 0 11
14 0 10
19 0 0
5162 4331 1390
5230 1955 9796
5507 6210 1021
000
19 0 0
000
2
32
21
Observaµie: al doilea c utator de comori nu poate p trunde în camera 3, 1, de³i are cartela
corespunz toare, pentru c  nu poate p r si camera 2, 1 în care este plasat iniµial.
Timp maxim de execuµie pe test: 1 secund 

40.3.1 Indicaµii de rezolvare

Se folose³te un algoritm de tip ll (umplere) pentru ecare c ut tor de comori, în mod repetat,
pân  când nu mai apar îmbun t µiri ale soluµiei. Pe parcurs, unele camere devin ³i r mân deschise.
Pentru ecare c ut tor de comori se p streaz  informaµii referitoare la cheile pe care le are la un
moment dat (în momentul în care poate p trunde într-o camer , el ia cheia existent  în acea
camer ).

40.3.2 Cod surs 

Listing 40.3.1: comoara-oc.pas


1 {$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S+,T-,V+,X+,Y+}
2 {$M 65500,0,655360}
3 var ii,i,j,k,l,m,n:longint;
4 fi,fo:text;
5 d,p,a,key,v:array[0..51,0..51]of integer;
6 x,y:array[1..10]of longint;
7 s,suma:longint;
8 chei:array[1..10]of set of 1..20;
9 ok:array[1..10,0..51,0..51]of byte;
10 gata:boolean;
11
12 procedure readdata;
13 begin
14 assign(fi,’comoara.in’);
15 assign(fo,’comoara.out’);
16 reset(fi);
17 readln(fi,m,n);
18 for i:=0 to m+1 do
19 for j:=0 to n+1 do
20 a[i,j]:=99;
21 for i:=1 to m do
22 begin
23 for j:=1 to n do
24 read(fi,a[i,j]);
25 readln(fi);
26 end;
27 for i:=1 to m do
28 begin
29 for j:=1 to n do
30 read(fi,v[i,j]);
31 readln(fi);
32 end;
33 for i:=1 to m do
34 begin
35 for j:=1 to n do
CAPITOLUL 40. ONI 2000 877

36 read(fi,key[i,j]);
37 readln(fi);
38 end;
39 readln(fi,k);
40 for i:=1 to k do
41 readln(fi,x[i],y[i]);
42 close(fi);
43 end;
44
45 procedure init;
46 begin
47 s:=0;
48 for ii:=1 to k do
49 begin
50 ok[ii,x[ii],y[ii]]:=1;
51 chei[ii]:=[0];
52 end;
53 for i:=1 to m do
54 for j:=1 to n do
55 suma:=suma+v[i,j];
56 end;
57
58 procedure fill(x,i,j:integer);
59
60 begin
61 if (ok[x,i,j]=0)and(not(a[i,j] in chei[x])) then exit;
62 if(i<0)or(i>m)or(j<0)or(j>n) then exit;
63 if p[i,j]=1 then exit;
64 p[i,j]:=1;
65 if ok[x,i,j]=0 then gata:=false;
66 ok[x,i,j]:=1;
67 chei[x]:=chei[x]+[key[i,j]];
68 if a[i,j] in chei[x] then
69 a[i,j]:=0;
70 s:=s+v[i,j];
71 v[i,j]:=0;
72 if a[i,j] in chei[x] then
73 begin
74 fill(x,i-1,j);
75 fill(x,i+1,j);
76 fill(x,i,j-1);
77 fill(x,i,j+1);
78 end;
79 end;
80
81 procedure expand;
82 begin
83 gata:=true;
84 for ii:=1 to k do
85 begin
86 fillchar(p,sizeof(p),0);
87 fill(ii,x[ii],y[ii]);
88 end;
89
90 end;
91
92 procedure dump;
93 begin
94 for i:=1 to m do
95 for j:=1 to n do
96 begin
97 l:=0;
98 for ii:=1 to k do
99 if ok[ii,i,j]=1 then
100 l:=1;
101 if l=1 then
102 d[i,j]:=1;
103 end;
104 for i:=1 to m do
105 begin
106 for j:=1 to n do
107 write(fo,d[i,j],’ ’);
108 writeln(fo);
109 end;
110
111 end;
CAPITOLUL 40. ONI 2000 878

112
113 procedure solve;
114 begin
115 init;
116 gata:=false;
117 while not gata do
118 expand;
119 rewrite(fo);
120 writeln(fo,s);
121 { dump;}
122 close(fo);
123 end;
124
125 begin
126 readdata;
127 solve;
128 end.

40.3.3 Rezolvare detaliat 


Listing 40.3.2: comoara.java
1 import java.io.*;
2 class Comoara
3 {
4 static int m,n;
5 static int[][] codCamera; // cod camera
6 static int[][] valc; // valoare comoara in camera
7 static int[][] cheiaDinCamera; // cheia din camera
8 static int k; // nr persoane in grup
9 static int[] lin; // linia initiala persoana
10 static int[] col; // coloana initiala persoana
11
12 static boolean[][] traseu;
13 static boolean[][][] amaifost;
14 static boolean[][] arecheia; // ce chei are
15 static int s; // suma comorilor
16 static boolean gata; // =true daca nu apar imbunatatiri
17
18 static PrintWriter out;
19 static StreamTokenizer st;
20
21 public static void main(String[] args) throws IOException
22 {
23 citire();
24 rezolvare();
25 afisare();
26 }
27
28 static void citire() throws IOException
29 {
30 int i,j;
31 st=new StreamTokenizer(
32 new BufferedReader(new FileReader("comoara.in")));
33 st.nextToken(); m=(int)st.nval;
34 st.nextToken(); n=(int)st.nval;
35 codCamera=new int[m+1][n+1];
36 valc=new int[m+1][n+1];
37 cheiaDinCamera=new int[m+1][n+1];
38 traseu=new boolean[m+1][n+1];
39
40 for(i=1;i<=m;i++)
41 for(j=1;j<=n;j++) {st.nextToken(); codCamera[i][j]=(int)st.nval;}
42 for(i=1;i<=m;i++)
43 for(j=1;j<=n;j++) {st.nextToken(); valc[i][j]=(int)st.nval;}
44 for(i=1;i<=m;i++)
45 for(j=1;j<=n;j++) {st.nextToken(); cheiaDinCamera[i][j]=(int)st.nval;}
46
47 st.nextToken(); k=(int)st.nval;
48 arecheia=new boolean[k+1][21];
49 lin=new int[k+1];
50 col=new int[k+1];
51 amaifost=new boolean[k+1][m+1][n+1];
52
CAPITOLUL 40. ONI 2000 879

53 for(i=1;i<=k;i++)
54 {
55 st.nextToken(); lin[i]=(int)st.nval;
56 st.nextToken(); col[i]=(int)st.nval;
57 }
58 }
59
60 static void rezolvare()
61 {
62 int i;
63 s=0;
64 for(i=1;i<=k;i++) arecheia[i][0]=true;
65 for(i=1;i<=k;i++) amaifost[i][lin[i]][col[i]]=true;
66
67 gata=false;
68 while(!gata) alteCautari();
69 }
70
71 static void alteCautari()
72 {
73 int i;
74 gata=true;
75 for(i=1;i<=k;i++)
76 {
77 curatTraseu();
78 cauta(i,lin[i],col[i]);
79 }
80 }
81
82 static void curatTraseu()
83 {
84 int i,j;
85 for(i=1;i<=m;i++) for(j=1;j<=n;j++) traseu[i][j]=false;
86 }
87
88 static void cauta(int x, int i, int j) // alg "fill" (de umplere)
89 {
90 if((i<1)||(i>m)||(j<1)||(j>n)) return;
91 if(traseu[i][j]) return; // a mai trecut pe aici
92 if((!amaifost[x][i][j])&& // nu a mai fost aici
93 (!arecheia[x][codCamera[i][j]])) return; // nu are cheia de intrare aici
94
95 traseu[i][j]=true;
96 if(!amaifost[x][i][j]) gata=false; // face o imbunatatire
97 amaifost[x][i][j]=true;
98
99 arecheia[x][cheiaDinCamera[i][j]]=true; // ia cheia din camera
100 s=s+valc[i][j]; // ia valorile
101 valc[i][j]=0; // raman valori zero
102
103 if(arecheia[x][codCamera[i][j]]) // daca are cheia camerei
104 {
105 codCamera[i][j]=0; // camera ramane deschisa
106 cauta(x,i-1,j);
107 cauta(x,i+1,j);
108 cauta(x,i,j-1);
109 cauta(x,i,j+1);
110 }
111 }
112
113 static void afisare() throws IOException
114 {
115 out=new PrintWriter(
116 new BufferedWriter(new FileWriter("comoara.out")));
117 out.println(s);
118 out.close();
119 }
120 }

40.4 Cuburi
prof. Ovidiu Dom³a, Colegiul "Horea, Clo³ca ³i Cri³an", Alba Iulia
Un joc nou este format din n cuburi identice, numerotate de la 1 la n. Fiecare cub are
CAPITOLUL 40. ONI 2000 880

toate cele ³ase feµe adezive (cu "scai").


Jocul const  în realizarea unui obiect din toate cele n cuburi.
Obiectul iniµial este cubul 1. Un obiect se obµine din obiectul anterior prin alipirea unui nou
cub la un cub aat în obiect, pe una din feµele acestuia.
Pentru alipirea unui cub nou, se extrage un bileµel cu num rul cubului la care se va alipi acesta
³i se arunc  un zar care va indica unde va  lipit cubul nou (sus, jos, la stânga, la dreapta, în faµ 
sau în spate, poziµii codicate respectiv cu 1, 2, 3, 4, 5, 6, ale cubului din obiect).
Cerinµ 
a Dându-se o succesiune de alipire a cuburilor, vericaµi dac  aceasta este valid 
b Dac  succesiunea este valid , precizaµi dac  obiectul obµinut are forma unui paralelipiped
plin, specicând dimesiunile acestuia.

Date de intrare
Fisierul CUBURI.IN conµine pe prima linie valoarea lui n.
Pe urm toarele n  1 linii, se a  o succesiune de a³ezare a cuburilor, pentru ecare cub
specicând:
num r_cub biletel zar
valori separate prin câte un spatiu.
Date de ie³ire
Fi³ierul de iesire CUBURI.OUT va conµine pe dou  linii rezultatele de la punctul a ³i respectiv
b dup  cum urmeaz :
a Dac  succesiunea este valid , prima linie va contine valoarea 0. În caz contrar, prima linie
va contine num rul cubului ce nu poate  alipit, num rul cubului ³i num rul feµei la care nu se
poate alipi, precum si codul de eroare:
1 pentru alipire la cub inexistent;
2 pentru alipire pe o faµ  ocupat .
b Dac  obiectul nu este paralelipiped plin, sau dac  succesiunea nu este valid , a doua linie a
³ierului va conµine valoarea 0.
Dac  obiectul este paralelipiped plin, a doua linie a ³ierului va conµine dimensiunile acestuia,
m surate în num r de cuburi, în ordinea:
num r de cuburi pe directiile sus-jos, stânga-dreapta, faµ -spate
separate prin câte un spatiu.
Restricµii ³i precizri
a 2 & n & 1000;
a pe o dimensiune se alipesc cel mult 10 cuburi.
Exemple
CUBURI.IN CUBURI.OUT CUBURI.IN CUBURI.OUT
6 0 4 0
211 321 211 0
324 421
514 324
631
463
CUBURI.IN CUBURI.OUT CUBURI.IN CUBURI.OUT
8 6361 8 8732
211 0 211 0
514 426
636 324
762 636
324 762
426 516
873 873
Timp maxim de execuµie: 1 secund /test

40.4.1 Indicaµii de rezolvare


CAPITOLUL 40. ONI 2000 881

Identic m poziµia cubului în obiect prin coordonatele sale din (stanga, jos, faµ ). De aseme-
nea, pentru c  pe o dimensiune se alipesc cel mult 10 cuburi, folosim un tablou tridimensional
pentru a reµine num rul de ordine al cubului care este plasat în oricare punct al spaµiului în care
poate s  e construit corpul respectiv.

40.4.2 Cod surs 

Listing 40.4.1: cuburi-oc.pas


1 var a:array[1..21,1..21,1..21] of integer;
2 l,c,d,i,j,k,ll,mini,maxi,minj,maxj,mink,maxk,pi,pj,pk:byte;
3 fis,fis1:string;f,g:text;
4 n:integer;cod:boolean;
5
6 Procedure afiscub;
7 begin
8 for pk:=mink to maxk do
9 begin
10 for pi:=mini to maxi do
11 begin
12 for pj:=minj to maxj do
13 write(a[pi,pj,pk],’,’);
14 writeln;
15 end;
16 writeln;
17 writeln;
18 end;
19 end;
20
21 begin
22 {readln(fis);
23 readln(fis1);}
24 assign(f,’cuburi.in’);
25 assign(g,’cuburi.out’);
26 reset(f);
27 rewrite(g);
28 readln(f,n);
29 i:=11;
30 j:=11;
31 k:=11;
32 a[i,j,k]:=1;
33 mini:=11;maxi:=11;
34 minj:=11;maxj:=11;
35 mink:=11;maxk:=11;
36 for l:=2 to n do
37 begin
38 readln(f,ll,c,d);
39 cod:=true;
40 for pi:=mini to maxi do
41 for pj:=minj to maxj do
42 for pk:=mink to maxk do
43 if a[pi,pj,pk]=c then
44 begin
45 i:=pi;j:=pj;k:=pk;
46 cod:=FALSE;
47 end;
48 if cod then
49 begin
50 writeln(g,ll,’ ’,c,’ ’,d,’ 1’);
51 writeln(g,0);
52 CLOSE(g);
53 CLOSE(F);
54 halt(1);
55 end
56 else
57
58 case d of
59 1: begin
60 i:=i-1;
61 if i<mini then
62 mini:=i;
63 end;
64 2: begin
CAPITOLUL 40. ONI 2000 882

65 i:=i+1;
66 if i>maxi then
67 maxi:=i;
68 end;
69 3: begin
70 j:=j-1;
71 if j<minj then
72 minj:=j;
73 end;
74 4: begin
75 j:=j+1;
76 if j>maxj then
77 maxj:=j;
78 end;
79 5: begin
80 k:=k-1;
81 if k<mink then
82 mink:=k;
83 end;
84 6: begin
85 k:=k+1;
86 if k>maxk then maxk:=k;
87 end;
88 end;
89 if a[i,j,k]=0 then
90 begin
91 a[i,j,k]:=ll;
92 end
93 else
94 begin
95 writeln(g,ll,’ ’,c,’ ’,d,’ 2’ );
96 writeln(g,0);
97 { afiscub;}
98 CLOSE(g);
99 CLOSE(F);
100 halt(1);
101 end;
102 end;
103 if (maxi-mini+1)*(maxj-minj+1)*(maxk-mink+1)=n then
104 begin
105 { afiscub;}
106 writeln(g,0);
107 write(g,(maxi-mini+1),’ ’,(maxj-minj+1),’ ’,(maxk-mink+1))
108 end
109 else
110 begin
111 writeln(g,0);
112 writeln(g,0);
113 {afiscub}
114 end;
115 close(f);
116 CLOSE(g);
117 end.

40.4.3 Rezolvare detaliat 


Listing 40.4.2: cuburi.java
1 import java.io.*;
2 class Cuburi
3 {
4 static int n;
5 static int[][][] ecubul=new int[21][21][21]; // aici e cubul ...
6 static int[] x; // x[i]=coordonata x a cubului i
7 static int[] y; // y[i]=coordonata y a cubului i
8 static int[] z; // z[i]=coordonata z a cubului i
9 static boolean[] eplasat;
10 static int minx, maxx, miny, maxy, minz, maxz; // coordonate obiect
11 static final int sus=1, jos=2, stanga=3, dreapta=4, fata=5, spate=6;
12
13 public static void main(String[] args) throws IOException
14 {
15 int i;
16 int cubnou, cubvechi, fatacubvechi;
CAPITOLUL 40. ONI 2000 883

17 boolean ok=true;
18
19 StreamTokenizer st=new StreamTokenizer(
20 new BufferedReader(new FileReader("cuburi.in")));
21 PrintWriter out=new PrintWriter(
22 new BufferedWriter(new FileWriter("cuburi.out")));
23
24 st.nextToken(); n=(int)st.nval;
25 x=new int[n+1];
26 y=new int[n+1];
27 z=new int[n+1];
28 eplasat=new boolean[n+1];
29
30 x[1]=11; y[1]=11; z[1]=11; // coordonatele cubului 1
31 ecubul[x[1]][y[1]][z[1]]=1; // aici e cubul 1
32 eplasat[1]=true; // cubul 1 este plasat in obiect
33 minx=maxx=x[1];
34 miny=maxy=y[1];
35 minz=maxz=z[1];
36
37 for(i=2;i<=n;i++)
38 {
39 st.nextToken(); cubnou=(int)st.nval;
40 st.nextToken(); cubvechi=(int)st.nval;
41 st.nextToken(); fatacubvechi=(int)st.nval;
42 if(!eplasat[cubvechi]) // alipire la cub inexisent
43 {
44 out.println(cubnou+" "+cubvechi+" "+fatacubvechi+" 1");
45 out.println("0"); // succesiunea nu e valida
46 ok=false;
47 break; // iese din for ==> nu mai pun "else" !
48 }
49
50 x[cubnou]=x[cubvechi];
51 y[cubnou]=y[cubvechi];
52 z[cubnou]=z[cubvechi];
53 switch(fatacubvechi)
54 {
55 case sus: z[cubnou]++; if(z[cubnou]>maxz) maxz++; break;
56 case jos: z[cubnou]--; if(z[cubnou]<minz) minz--; break;
57 case stanga: x[cubnou]--; if(x[cubnou]<minx) minx--; break;
58 case dreapta: x[cubnou]++; if(x[cubnou]>maxx) maxx++; break;
59 case fata: y[cubnou]++; if(y[cubnou]>maxy) maxy++; break;
60 case spate: y[cubnou]--; if(y[cubnou]<miny) miny--; break;
61 default: System.out.println("Date de intrare eronate!");
62 }
63 if(ecubul[x[cubnou]][y[cubnou]][z[cubnou]]!=0)// fata ocupata
64 {
65 out.println(cubnou+" "+cubvechi+" "+fatacubvechi+" 2");
66 out.println("0"); // succesiunea nu e valida
67 ok=false;
68 break; // iese din for ==> nu mai pun "else" !
69 }
70 ecubul[x[cubnou]][y[cubnou]][z[cubnou]]=cubnou;
71 eplasat[cubnou]=true;
72 }
73 if(ok)
74 if((maxx-minx+1)*(maxy-miny+1)*(maxz-minz+1)==n)
75 {
76 out.println("0");
77 out.println((maxz-minz+1)+" "+(maxx-minx+1)+" "+(maxy-miny+1));
78 }
79 else
80 {
81 out.println("0");
82 out.println("0");
83 }
84 out.close();
85 }
86 }

40.5 Fibo
prof. Marinel “erban, Liceul de Informatic , Ia³i
CAPITOLUL 40. ONI 2000 884

Consider m ³irul lui Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21, ...


Dat ind un num r natural (n " N), scrieµi acest num r sub form  de sum  de elemente
neconsecutive din ³irul Fibonacci, ecare element putând s  apar  cel mult o dat , astfel încât
num rul de termeni ai sumei s  e minim.
Date de intrare
Din ³ierul FIB.IN se cite³te de pe prima linie num rul natural n.
Datele de ie³ire
În ³ierul de ie³ire FIB.OUT se vor a³a termeni ai ³irului Fibonacci, câte unul pe linie, a
c ror sum  este n.
Restricµii ³i preciz ri
a n poate avea maxim 80 de cifre
Exemple:
FIB.IN FIB.OUT FIB.IN FIB.OUT
20 2 8 8
5
13
Timp maxim de execuµie pe test: 1 secund 

40.5.1 Indicaµii de rezolvare

Se determin  cel mai mare num r din ³irul Fibonacci care este mai mic sau egal cu num rul
dat. Acesta este primul num r din soluµie. Se determin  cel mai mare num r din ³irul Fibonacci
care este mai mic sau egal cu diferenµa dintre num rul dat ³i primul num r din soluµie. Acesta
este al doilea num r din soluµie. Se repet  acest algoritm pân  când num rul r mas devine zero.
Se folosesc operaµiile de adunare, sc dere ³i comparare cu numere mari.

40.5.2 Cod surs 

Listing 40.5.1: sb.pas


1 program Suma_Fibonacci;
2 const NMax = 15000;
3 type Indice = 0 .. NMax;
4 Sir = array[Indice] of Longint;
5 var n, nr: longint;
6 F: Sir;
7
8 procedure Citire;
9 var f:text;
10 begin
11 assign (f, ’5.in’); reset(f);
12 readln (f, n);
13 close(f);
14 end;
15
16 procedure Fibonacci;
17 var i: LongInt;
18 begin
19 F[0]:=0; F[1]:=1;
20 i:=1;
21 while (F[i] <= n) And (F[i-1]<=F[i]) do
22 begin
23 inc(i);
24 F[i]:=F[i-1]+F[i-2];
25 WriteLn(i:5,F[i]:20)
26 end;
27 If F[i-1]>F[i] Then
28 Nr := i-1
29 Else
30 Nr:=i;
31 end;
32
33 procedure Determina;
CAPITOLUL 40. ONI 2000 885

34 var fout:text;
35 i:LongInt;
36 begin
37 assign (fout, ’fib.out’); rewrite(fout);
38 i:=Nr;
39 repeat
40 while F[i]>n do dec(i);
41 writeln (fout, F[i]);
42 n:=n-F[i];
43 until n=0;
44 close(fout);
45 end;
46
47 begin
48 Citire;
49 Fibonacci;
50 Determina;
51 end.

Listing 40.5.2: sbmare.pas


1 Program Suma_Fibonacci;
2 Const DimMax = 500;
3 Type Indice = 0 .. DimMax;
4 Numar = String[80];
5 Sir = Array[Indice] Of Numar;
6 Var n: Numar;
7 Nr: Integer;
8 F: Sir;
9
10 Procedure Citire;
11 Var f: Text;
12 Begin
13 Assign(f, ’FIB.IN’); Reset(f);
14 ReadLn(f, n);
15 Close(f);
16 End;
17
18 Function Cifra(c: Char): Byte;
19 Begin
20 Cifra := Ord(c) - 48;
21 End;
22
23 Function Adunare(a, b: Numar): Numar;
24 Var t, Cif, i: byte;
25 s: Numar;
26 begin
27 While Length(a) < Length(b) Do a := ’0’+a;
28 While Length(a) > Length(b) Do b := ’0’+b;
29 t := 0; s := ’’;
30 For i := Length(a) DownTo 1 Do
31 Begin
32 Cif := (t + Cifra(a[i]) + cifra(b[i])) Mod 10;
33 t := (t + Cifra(a[i]) + Cifra(b[i])) Div 10;
34 s := Chr(Cif+48) + s;
35 End;
36 If t>0 Then s := Chr(t+48) + s;
37 Adunare := s;
38 End;
39
40 Function Compara(a, b: Numar): Integer;
41 Begin
42 If Length(a) > Length(b) Then Compara := 1
43 Else
44 If Length(b) > Length(a) Then Compara := -1
45 Else
46 If a > b Then Compara := 1
47 Else
48 If a < b Then Compara := -1
49 Else Compara := 0;
50 End;
51
52 Procedure Fibonacci;
53 Var i: Integer;
54 Begin
CAPITOLUL 40. ONI 2000 886

55 F[0] := ’0’; F[1] := ’1’;


56 i := 1;
57 While Compara(F[i], n) <= 0 Do
58 Begin
59 Inc(i);
60 F[i] := Adunare(F[i-1], F[i-2]);
61 End;
62 Nr := i;
63 End;
64
65 Function Scadere(a, b: Numar): Numar;
66 Var c: Numar;
67 i: Byte;
68 t, Cif: Integer;
69 Begin
70 t := 0; c := ’’;
71 While Length(a) > Length(b) Do b := ’0’ + b;
72 For i := Length(a) DownTo 1 Do
73 Begin
74 If t + Cifra(a[i])-Cifra(b[i]) >= 0 Then
75 Begin
76 Cif := t + Cifra(a[i])-Cifra(b[i]);
77 t := 0;
78 End
79 Else
80 Begin
81 Cif := 10 + t + Cifra(a[i])-Cifra(b[i]);
82 t := -1;
83 End;
84 c := Chr(cif+48) + c;
85 End;
86 While (c<>’’) And (c[1]=’0’) Do Delete(c, 1, 1);
87 If c=’’ Then c := ’0’;
88 Scadere := c;
89 End;
90
91 Procedure Determina;
92 Var Fout: Text;
93 i: Integer;
94 Begin
95 Assign(Fout, ’FIB.OUT’); ReWrite(Fout);
96 i := Nr;
97 Repeat
98 While Compara(F[i], n)>0 Do Dec(i);
99 WriteLn(Fout, F[i]);
100 n := Scadere(n, F[i]);
101 Until n=’0’;
102 Close(Fout);
103 End;
104
105 Begin
106 Citire;
107 Fibonacci;
108 Determina;
109 End.

40.5.3 Rezolvare detaliat 


Listing 40.5.3: bo.java
1 import java.io.*;
2 class Fibo
3 {
4 static int nf=385; // 81 cifre Fibo
5 static int[][] f=new int[nf+1][1];
6 static int[] x;
7 static PrintWriter out;
8
9 public static void main(String[] args) throws IOException
10 {
11 int k,i,iy,y;
12 BufferedReader br=new BufferedReader(new FileReader("fib.in"));
13 out=new PrintWriter(new BufferedWriter(new FileWriter("fib.out")));
14
CAPITOLUL 40. ONI 2000 887

15 char[] a=br.readLine().toCharArray();
16 int nx=a.length;
17 x=new int[nx];
18
19 for(i=0;i<a.length;i++) x[nx-1-i]=a[i]-’0’;
20
21 f[0]=nr2v(0);
22 f[1]=nr2v(1);
23 f[2]=nr2v(1);
24 for(k=3;k<=nf;k++) f[k]=suma(f[k-1],f[k-2]);
25
26 while(compar(x,nr2v(0))>0)
27 {
28 iy=maxFibo(x);
29 afisv(f[iy]);
30 x=scade(x,f[iy]);
31 }
32 out.close();
33 }
34
35 static int maxFibo(int[] nr)
36 {
37 int k;
38 for(k=1;k<=nf;k++) if (compar(f[k],nr)>0) break;
39 return k-1;
40 }
41
42 static int compar(int[] a, int[] b) //-1, 0, 1 ... a < = > b
43 {
44 int na=a.length;
45 int nb=b.length;
46 if(na>nb) return 1; else if(na<nb) return -1;
47
48 int i=na-1;
49 while((i>=0)&&(a[i]==b[i])) i--;
50 if(i==-1) return 0;
51 else if(a[i]>b[i]) return 1; else return -1;
52 }
53
54 static int[] scade(int[] x,int[] y) // z=x-y unde x>=y
55 {
56 int nx=x.length;
57 int ny=y.length;
58 int nz=nx;
59 int t,i;
60 int[] z=new int[nz];
61 int[] yy=new int[nz];
62 for(i=0;i<ny;i++) yy[i]=y[i];
63 t=0;
64 for(i=0;i<nz;i++)
65 {
66 z[i]=x[i]-yy[i]-t;
67 if(z[i]<0) {z[i]+=10; t=1;} else t=0;
68 }
69 if(z[nz-1]!=0) return z;
70 else
71 {
72 int[] zz=new int[nz-1];
73 for(i=0;i<nz-1;i++) zz[i]=z[i];
74 return zz;
75 }
76 }
77
78 static int[] suma(int[] x,int[] y)
79 {
80 int nx=x.length;
81 int ny=y.length;
82 int nz;
83 if(nx>ny) nz=nx+1; else nz=ny+1;
84 int t,i;
85 int[] z=new int[nz];
86 int[] xx=new int[nz];
87 int[] yy=new int[nz];
88 for(i=0;i<nx;i++) xx[i]=x[i];
89 for(i=0;i<ny;i++) yy[i]=y[i];
90 t=0;
CAPITOLUL 40. ONI 2000 888

91 for(i=0;i<nz;i++)
92 {
93 z[i]=xx[i]+yy[i]+t;
94 t=z[i]/10;
95 z[i]=z[i]%10;
96 }
97 if(z[nz-1]!=0) return z;
98 else
99 {
100 int[] zz=new int[nz-1];
101 for(i=0;i<nz-1;i++) zz[i]=z[i];
102 return zz;
103 }
104 }
105
106 static int[] nr2v(int nr)
107 {
108 int nrr=nr,nc=0,i;
109 while(nr!=0) { nc++; nr=nr/10; }
110 int[] nrv=new int[nc];
111 nr=nrr;
112 for(i=0;i<nc;i++) { nrv[i]=nr%10; nr=nr/10; }
113 return nrv;
114 }
115
116 static void afisv(int[] x)
117 {
118 int i;
119 for(i=x.length-1;i>=0;i--) out.print(x[i]);
120 out.println();
121 }
122 }

40.6 Kommando
prof. Marinel “erban, Liceul de Informatic , Ia³i
Într-o cl dire cu h etaje sunt deµinuµi, la parter, câµiva prizonieri de c tre T terori³ti.
Fiecare etaj al cl dirii are m  n camere identice. Fiecare camer  are un cod numeric (nu neap rat
unic) exprimat printr-un num r din intervalul 0  255.
O trup  de komando, format  din K speciali³ti în luptele antitero, trebuie s  elibereze prizo-
nierii. Trupa de komando este para³utat  pe cl dire ³i încearc  s  ajung  la parter. Se cunoa³te
locul x, y  unde ecare membru al trupei a aterizat pe acoperi³.
Greutatea ec rui membru al trupei este exprimat  în unit µi din intervalul 1  255. Un
membru al trupei poate trece "în jos" printr-o camer  a cl dirii doar dac  "greutatea lui trece
prin camera respectiv ", conform urm toarei deniµii.
Deniµie: Spunem c  "a trece prin b" (a %% b) dac , în reprezentare binar , num rul de cifre
1 a lui a este mai mic sau egal cu num rul de cifre 1 a lui b ³i cifrele 1 ale lui a sunt comune cu
unele cifre 1 ale lui b.
Exemplu: "44 trece prin 174" (44 %% 174) deoarece
44 = 00101100
174 = 10101110
Pentru detectarea unei camere prin care s  poat  trece, un membru al trupei de komando se
poate, eventual, deplasa cu un "pas" în cele 8 direcµii al turate poziµiei curente în care a ajuns prin
aterizare sau trecerea "în jos". Prin "pas"-ul respectiv se ajunge la una din cele 8 camere vecine.
Prizonierii pot  eliberaµi doar dac  la parter ajung minim T membri ai trupei de komando.
Cerinµ :
S  se determine dac  prizonierii pot  eliberaµi sau nu, precum ³i num rul de membri ai trupei
de komando care pot s  ajung  la parter.
Date de intrare
Fi³ierul text KOMMANDO.IN are structura urm toare:
a pe prima linie valorile m, n, h, K , T desp rµite prin câte un spaµiu, cu semnicaµiile descrise
mai sus;
a urm toarele h linii reprezint  codurile celor m  n camere ale unui etaj, desp rµite prin câte
un spaµiu;
CAPITOLUL 40. ONI 2000 889

a ultimele K linii ale ³ierului conµin greutatea ³i coordonatele x ³i y a poziµiei de aterizare pe


acoperi³ ale celor K membri ai trupei de komando, pentru ecare pe câte o linie, desp rµite prin
câte un spaµiu:
m n h K T
c111 c112 ... c11n c121 c122 ... c12n ... c1m1 ... c1mn
...
ch11 ch12 ... ch1n ch21 ch22 ... ch2n ... chm1 ... chmn
G1 x1 y1
...
GK xK yK
Datele de ie³ire:
Fi³ierul text KOMMANDO.OUT cu structura:
a DA sau NU - pe prima linie;
a num rul de membri ai trupei de komando ajun³i la parter - pe linia a doua.

Restricµii ³i preciz ri
a 2 & m, n, h & 35 1 & xi & m 1 & yi &n
a 1 & T, K, Gi & 255
a 0 & cijk & 255
a Toate valorile sunt întregi.
Exemplu:
KOMMANDO.IN KOMMANDO.OUT
55532 DA
0 0 0 0 0 0 0 33 0 0 0 02 0000030 00000 2
00000 01 0000 44 2 0 0 0 0 0 3 0 0 0 0 0 0
0 0 0 0 0 11 0 0 0 0 0 0 2 22 0 0 0 0 0 3 0 0 0 0 0
00000 00 0000 66 2 0 0 0 0 0 7 0 15 0 0 0 0
00000 00 0000 00 0000000 00000
122
233
344
Timp maxim de execuµie pe test: 5 secunde

40.6.1 Indicaµii de rezolvare

În ³ierul de intrare etajele sunt de sus în jos (primul etaj este lâng  acoperi³)!
Pentru ecare soldat se face o parcurgere în adâncime (folosind recursivitatea) sau o parcurgere
în l µime (eventual folosind o structur  de tip coad ) pentru un algoritm de tip ll (umplere).

40.6.2 Cod surs 

Listing 40.6.1: KMIHAI.pas


1 {$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}
2 {$M 16384,0,655360}
3 Program Eliberarea_Prizonierilor;
4
5 Const DimMax = 35;
6 MaxK = 200;
7 Dx: Array[1..9] Of -1..1 = (0, 0, 1,1,1,0,-1,-1,-1);
8 Dy: Array[1..9] Of -1..1 = (0,-1,-1,0,1,1, 1, 0,-1);
9
10 Type Dim = 0..DimMax+1;
11 Komando = Record
12 x, y: Dim; {locul de pornire de pe acoperis}
13 Greu: Byte; {greutatea}
14 Ajuns: Boolean; {initial FALSE pentru toti}
15 End;
16 Cladire = Array[Dim, Dim, Dim] Of Byte;
17
18 Var c : Cladire;
CAPITOLUL 40. ONI 2000 890

19 Com : Array[1..MaxK] Of Komando;


20 ContorAjunsi: Byte;
21 m, n, h : Byte;
22 Kapa, K, T : Byte;
23 g : Text;
24 p:array[1..dimmax,1..dimmax] of set of 1..dimmax;
25
26 Procedure Citeste_Date;
27 Var f: Text;
28 i, j, l: Byte;
29 Begin
30 Assign(f, ’m’); Reset(f);
31 ReadLn(f, m, n, h, K, T);
32 For i := 1 To h Do {citeste un etaj}
33 Begin
34 For j := 1 To m Do {citeste m linii}
35 For l := 1 To n Do {citeste n coloane}
36 Read(f, c[j, l, i]);
37 ReadLn(f)
38 End;
39 For i := 1 To K Do {citeste cei K Komandisti}
40 With Com[i] Do
41 Begin
42 ReadLn(f, Greu, x, y);
43 Ajuns := False
44 End;
45 Close(f)
46 End;
47
48 Procedure Trece(Kapa: Byte; xx, yy, Nivel: Dim);
49 Var i: 1..9;
50 Begin
51 if yy in p[nivel,xx] then exit;
52 p[nivel,xx]:=p[nivel,xx]+[yy];
53
54 If Not Com[Kapa].Ajuns Then
55 If Nivel = h Then
56 Begin
57 Com[Kapa].Ajuns := True; {Kapa a ajuns !!! la parter}
58 Exit
59 End
60 Else
61 For i := 1 To 9 Do {incearca in cele 9 posibile directii}
62 If c[xx+Dx[i], yy+Dy[i], Nivel] and Com[Kapa].greu= Com[Kapa].greu
63 { then
64 if not(yy+dy[i] in p[nivel+1,xx+dx[i]])
65 } Then {daca trece prin aceasta camera}
66 Trece(Kapa, xx+Dx[i], yy+Dy[i], Nivel+1)
67 {incearca la nivelul urmator}
68 End;
69
70 Begin
71 Citeste_Date;
72 Assign(g, ’KOMMANDO.OUT’); ReWrite(g);
73 For Kapa := 1 To K Do {pentru fiecare din cei K Komandisti}
74 begin
75 if kapa mod 10=0 then
76 writeln(kapa);
77 fillchar(p,sizeof(p),0);
78 Trece(Kapa, Com[Kapa].x, Com[Kapa].y, 1); {plec de pe nivelul 1}
79 end;
80 For Kapa := 1 To K Do {contorizez cei ajunsi}
81 If Com[Kapa].Ajuns Then Inc(ContorAjunsi);
82 If ContorAjunsi >= T Then WriteLn(g, ’DA’)
83 Else WriteLn(g, ’NU’);
84 WriteLn(g, ContorAjunsi);
85 Close(g);
86 End.

Listing 40.6.2: KOM_MEU.pas


1 Program Eliberarea_Prizonierilor;
2
3 Const DimMax = 35;
4 MaxK = 200;
CAPITOLUL 40. ONI 2000 891

5 Dx: Array[1..9] Of -1..1 = (0, 0, 1,1,1,0,-1,-1,-1);


6 Dy: Array[1..9] Of -1..1 = (0,-1,-1,0,1,1, 1, 0,-1);
7
8 Type Dim = 0..DimMax+1;
9 Komando = Record
10 x, y: Dim; {locul de pornire de pe acoperis}
11 Greu: Byte; {greutatea}
12 Ajuns: Boolean; {initial FALSE pentru toti}
13 End;
14 Cladire = Array[Dim, Dim, Dim] Of Byte;
15
16 Var c : Cladire;
17 Com : Array[1..MaxK] Of Komando;
18 ContorAjunsi: Byte;
19 m, n, h : Byte;
20 Kapa, K, T : Byte;
21 g : Text;
22
23 Procedure Citeste_Date;
24 Var f: Text;
25 i, j, l: Byte;
26 Begin
27 Assign(f, ’8’); Reset(f);
28 ReadLn(f, m, n, h, K, T);
29 For i := 1 To h Do {citeste un etaj}
30 Begin
31 For j := 1 To m Do {citeste m linii}
32 For l := 1 To n Do {citeste n coloane}
33 Read(f, c[j, l, i]);
34 ReadLn(f)
35 End;
36 For i := 1 To K Do {citeste cei K Komandisti}
37 With Com[i] Do
38 Begin
39 ReadLn(f, Greu, x, y);
40 Ajuns := False
41 End;
42 Close(f)
43 End;
44
45 Procedure Trece(Kapa: Byte; xx, yy, Nivel: Dim);
46 Var i: 1..9;
47 Begin
48 If Not Com[Kapa].Ajuns Then
49 If Nivel = h Then
50 Begin
51 Com[Kapa].Ajuns := True; {Kapa a ajuns !!! la parter}
52 Exit
53 End
54 Else
55 For i := 1 To 9 Do {incearca in cele 9 posibile directii}
56 If c[xx+Dx[i], yy+Dy[i], Nivel] and Com[Kapa].greu= Com[Kapa].greu
57 Then {daca trece prin aceasta camera}
58 Trece(Kapa, xx+Dx[i], yy+Dy[i], Nivel+1)
59 {incearca la nivelul urmator}
60 End;
61
62 Begin
63 Citeste_Date;
64 Assign(g, ’KOMMANDO.OUT’); ReWrite(g);
65 For Kapa := 1 To K Do {pentru fiecare din cei K Komandisti}
66 Trece(Kapa, Com[Kapa].x, Com[Kapa].y, 1); {plec de pe nivelul 1}
67 For Kapa := 1 To K Do {contorizez cei ajunsi}
68 If Com[Kapa].Ajuns Then Inc(ContorAjunsi);
69 If ContorAjunsi >= T Then WriteLn(g, ’DA’)
70 Else WriteLn(g, ’NU’);
71 WriteLn(g, ContorAjunsi);
72 Close(g);
73 End.

Listing 40.6.3: Kommando-oc.pas


1 {$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}
2 {$M 16384,0,655360}
3 Program Eliberarea_Prizonierilor;
CAPITOLUL 40. ONI 2000 892

4
5 Const DimMax = 35;
6 MaxK = 200;
7 Dx: Array[1..9] Of -1..1 = (0, 0, 1,1,1,0,-1,-1,-1);
8 Dy: Array[1..9] Of -1..1 = (0,-1,-1,0,1,1, 1, 0,-1);
9
10 Type Dim = 0..DimMax+1;
11 Komando = Record
12 x, y: Dim; {locul de pornire de pe acoperis}
13 Greu: Byte; {greutatea}
14 Ajuns: Boolean; {initial FALSE pentru toti}
15 End;
16 Cladire = Array[Dim, Dim, Dim] Of Byte;
17
18 Var c : Cladire;
19 Com : Array[1..MaxK] Of Komando;
20 ContorAjunsi: Byte;
21 m, n, h : Byte;
22 Kapa, K, T : Byte;
23 g : Text;
24 a, b : Array[0..DimMax+1, 0..DimMax+1] Of Byte;
25
26 Procedure Citeste_Date;
27 Var f: Text;
28 i, j, l: Byte;
29 Begin
30 Assign(f, ’kommando.in’); Reset(f);
31 ReadLn(f, m, n, h, K, T);
32 For i := 1 To h Do {citeste un etaj}
33 Begin
34 For j := 1 To m Do {citeste m linii}
35 For l := 1 To n Do {citeste n coloane}
36 Read(f, c[j, l, i]);
37 ReadLn(f)
38 End;
39 For i := 1 To K Do {citeste cei K Komandisti}
40 With Com[i] Do
41 Begin
42 ReadLn(f, Greu, x, y);
43 Ajuns := False
44 End;
45 Close(f)
46 End;
47
48 Procedure Trece;
49 Var Niv, i, j, xx: Byte;
50 Begin
51 FillChar(a, SizeOf(a), 0);
52 a[Com[Kapa].x, Com[Kapa].y] := 1;
53 For Niv := 2 To h Do
54 Begin
55 FillChar(b, SizeOf(b), 0);
56 For i := 1 To m Do
57 For j := 1 To n Do
58 If a[i, j] = 1 Then
59 For xx := 1 To 9 Do
60 If c[i+Dx[xx], j+Dy[xx], Niv-1] and
61 Com[Kapa].greu = Com[Kapa].greu
62 Then
63 b[i+Dx[xx], j+Dy[xx]] := 1;
64 a := b;
65 End;
66 For i := 1 To m Do
67 For j := 1 To n Do
68 If a[i, j] = 1 Then
69 Com[Kapa].Ajuns := true;
70 End;
71
72 Begin
73 Citeste_Date;
74 Assign(g, ’KOMMANDO.OUT’); ReWrite(g);
75 For Kapa := 1 To K Do {pentru fiecare din cei K Komandisti}
76 Trece; {plec de pe nivelul 1}
77 For Kapa := 1 To K Do {contorizez cei ajunsi}
78 If Com[Kapa].Ajuns Then Inc(ContorAjunsi);
79 If ContorAjunsi >= T Then WriteLn(g, ’DA’)
CAPITOLUL 40. ONI 2000 893

80 Else WriteLn(g, ’NU’);


81 WriteLn(g, ContorAjunsi);
82 Close(g);
83 End.

Listing 40.6.4: VERKOMM.pas


1 program eval_permanent;
2 {
3 Parametrii :
4 1 - fisier iesire comisie
5 2 - fisier iesire concurent
6 3 - rezultate evaluare
7 }
8
9 var pt:real;
10 s1,s2:string;
11 s3,s4:longint;
12 fe,fc:text;
13 i:1..2;
14
15 procedure eroare(s:string; p:real);
16 var f:text;
17 begin
18 assign(f,paramstr(3)); rewrite(f);
19 writeln(f,s);
20 close(f);
21 halt;
22 end;
23
24 procedure citire_concurent;
25 var i:integer;
26 gasit:boolean;
27 c:char;
28 s:string;
29 begin
30 assign(fe,paramstr(2));
31 {$I-}
32 reset(fe);
33 {$I+}
34 if ioresult<>0 then eroare(’Fisier de iesire inexistent!’, 0);
35 {$I-}
36 readln(fe, s1);
37 {$I+}
38 if ioresult<>0 then eroare(’Fisier de iesire cu format incorect!’, 0);
39 end;
40
41 procedure citire_comisie;
42 var x,y,z:integer;
43 begin
44 assign(fc, paramstr(1)); reset(fc);
45 readln(fc, s2);
46 end;
47
48 begin
49 citire_comisie;
50 citire_concurent;
51 for i:=1 to 2 do s2[i]:=upcase(s2[i]);
52 if s1<>s2 then eroare(’Gresit!!!’, 0)
53 else
54 begin
55 ReadLn(fc, s3);
56 if seekeof(fe) then Eroare(’Fisier incomplet!!! ’,0)
57 else
58 begin
59 ReadLn(fe, s4);
60 if s3=s4 then Eroare(’Numar corect!!!’, 10)
61 else eroare(’Numar gresit!!!’, 0);
62 close(fe);
63 end;
64 close(fc)
65 end;
66 end.
CAPITOLUL 40. ONI 2000 894

40.6.3 Rezolvare detaliat 


Listing 40.6.5: kommando1.java
1 import java.io.*; // 35*35*35=42875
2 class Kommando1 // parcurgere in adancime
3 {
4 static int m,n,h,K,T;
5 static int[][][] codCamera; // cod camera
6 static int[] xk; // x acoperis soldat k
7 static int[] yk; // y acoperis soldat k
8 static int[] G; // greutatea
9 static int ns=0; // nr soldati ajunsi la parter
10 static boolean aajunslaparter;
11
12 static PrintWriter out;
13 static StreamTokenizer st;
14
15 public static void main(String[] args) throws IOException
16 {
17 citire();
18 rezolvare();
19 afisare();
20 }
21
22 static void citire() throws IOException
23 {
24 int x,y,etaj,k;
25 st=new StreamTokenizer(
26 new BufferedReader(new FileReader("0.in")));
27 st.nextToken(); m=(int)st.nval;
28 st.nextToken(); n=(int)st.nval;
29 st.nextToken(); h=(int)st.nval;
30 st.nextToken(); K=(int)st.nval;
31 st.nextToken(); T=(int)st.nval;
32
33 codCamera=new int[h+1][m+1][n+1];
34 xk=new int[K+1];
35 yk=new int[K+1];
36 G=new int[K+1];
37
38 for(etaj=1;etaj<=h;etaj++)
39 for(x=1;x<=m;x++)
40 for(y=1;y<=n;y++)
41 {
42 st.nextToken();
43 codCamera[etaj][x][y]=(int)st.nval;
44 }
45
46 for(k=1;k<=K;k++)
47 {
48 st.nextToken(); G[k]=(int)st.nval;
49 st.nextToken(); xk[k]=(int)st.nval;
50 st.nextToken(); yk[k]=(int)st.nval;
51 }
52 }
53
54 static void rezolvare() throws IOException
55 {
56 int soldat;
57 for(soldat=1;soldat<=K;soldat++)
58 {
59 System.out.println(soldat);
60 aajunslaparter=false;
61 coboara(soldat,1,xk[soldat],yk[soldat]);
62 if(aajunslaparter) ns++;
63 }
64 }
65
66 static void coboara(int soldat, int etaj, int x, int y)
67 {
68 System.out.println(soldat+" "+etaj+" "+x+" "+y);
69 if((x<1)||(x>m)||(y<1)||(y>n)) return;
70 if(aajunslaparter) return;
71 if(etaj==h) {aajunslaparter=true; return;}
72 int i,j;
CAPITOLUL 40. ONI 2000 895

73 for(i=-1;i<=1;i++)
74 for(j=-1;j<=1;j++)
75 if((x+i>=1)&&(x+i<=m)&&(y+j>=1)&&(y+j<=n))
76 if(trece(G[soldat],codCamera[etaj][x+i][y+j]))
77 coboara(soldat, etaj+1, x+i, y+j);
78 }
79
80 static boolean trece(int a, int b) // a trece prin b ?
81 {
82 int k;
83 for(k=0;k<=7;k++)
84 if(((1<<k)&a)!=0) // pozitia k in a este 1
85 if(((1<<k)&b)==0) // pozitia k in b este 0
86 return false;
87 return true;
88 }
89
90 static void afisare() throws IOException
91 {
92 out=new PrintWriter(
93 new BufferedWriter(new FileWriter("kommando.out")));
94 if(ns>=T) out.println("DA"); else out.println("NU");
95 out.println(ns);
96 out.close();
97 }
98 }

Listing 40.6.6: kommando2.java


1 import java.io.*; // optimizare prin inregistrarea traseului
2 class Kommando2 // totusi, test 10 ==> 13.5 sec ???
3 {
4 static int m,n,h,K,T;
5 static int[][][] codCamera; // cod camera
6 static int[] xk; // x acoperis soldat k
7 static int[] yk; // y acoperis soldat k
8 static int[] G; // greutatea
9 static int ns=0; // nr soldati ajunsi la parter
10 static boolean aajunslaparter;
11 static boolean[][][] traseu; // traseul soldatului curent
12
13 static PrintWriter out;
14 static StreamTokenizer st;
15
16 public static void main(String[] args) throws IOException
17 {
18 long t1,t2;
19 t1=System.currentTimeMillis();
20
21 citire();
22 rezolvare();
23 afisare();
24
25 t2=System.currentTimeMillis();
26 System.out.println("Timp = "+(t2-t1)+" ms");
27 }
28
29 static void citire() throws IOException
30 {
31 int x,y,etaj,k;
32 st=new StreamTokenizer(
33 new BufferedReader(new FileReader("10.in")));
34 st.nextToken(); m=(int)st.nval;
35 st.nextToken(); n=(int)st.nval;
36 st.nextToken(); h=(int)st.nval;
37 st.nextToken(); K=(int)st.nval;
38 st.nextToken(); T=(int)st.nval;
39
40 codCamera=new int[h+1][m+1][n+1];
41 traseu=new boolean[h+1][m+1][n+1];
42
43 xk=new int[K+1];
44 yk=new int[K+1];
45 G=new int[K+1];
46
CAPITOLUL 40. ONI 2000 896

47 for(etaj=1;etaj<=h;etaj++)
48 for(x=1;x<=m;x++)
49 for(y=1;y<=n;y++)
50 {
51 st.nextToken();
52 codCamera[etaj][x][y]=(int)st.nval;
53 }
54
55 for(k=1;k<=K;k++)
56 {
57 st.nextToken(); G[k]=(int)st.nval;
58 st.nextToken(); xk[k]=(int)st.nval;
59 st.nextToken(); yk[k]=(int)st.nval;
60 }
61 }
62
63 static void rezolvare() throws IOException
64 {
65 int soldat;
66 for(soldat=1;soldat<=K;soldat++)
67 {
68 curatTraseu();
69 aajunslaparter=false;
70 coboara(soldat,1,xk[soldat],yk[soldat]);
71 if(aajunslaparter) ns++;
72 }
73 }
74
75 static void curatTraseu()
76 {
77 int etaj, x, y;
78 for(etaj=1;etaj<=h;etaj++)
79 for(x=1;x<=m;x++)
80 for(y=1;y<=n;y++)
81 traseu[etaj][x][y]=false;
82 }
83
84 static void coboara(int soldat, int etaj, int x, int y)
85 {
86 if((x<1)||(x>m)||(y<1)||(y>n)) return;
87 if(aajunslaparter) return;
88 if(etaj==h) {aajunslaparter=true; return;}
89 if(traseu[etaj][x][y]) return; // a mai trecut pe aici
90 traseu[etaj][x][y]=true;
91
92 int i,j;
93 for(i=-1;i<=1;i++)
94 for(j=-1;j<=1;j++)
95 if((x+i>=1)&&(x+i<=m)&&(y+j>=1)&&(y+j<=n))
96 if(trece(G[soldat],codCamera[etaj][x+i][y+j]))
97 coboara(soldat, etaj+1, x+i, y+j);
98 }
99
100 static boolean trece(int a, int b) // a trece prin b ?
101 {
102 int k;
103 for(k=0;k<=7;k++)
104 if(((1<<k)&a)!=0) // pozitia k in a este 1
105 if(((1<<k)&b)==0) // pozitia k in b este 0
106 return false;
107 return true;
108 }
109
110 static void afisare() throws IOException
111 {
112 out=new PrintWriter(
113 new BufferedWriter(new FileWriter("kommando.out")));
114 if(ns>=T) out.println("DA"); else out.println("NU");
115 out.println(ns);
116 out.close();
117 }
118 }

Listing 40.6.7: kommando3.java


CAPITOLUL 40. ONI 2000 897

1 import java.io.*; // parcurgere in latime


2 class Kommando3 // totusi, test 10 ==> 10.5 sec ???
3 {
4 static int m,n,h,K,T;
5 static int[][][] codCamera; // cod camera
6 static int[] xk; // x acoperis soldat k
7 static int[] yk; // y acoperis soldat k
8 static int[] G; // greutatea
9 static int ns=0; // nr soldati ajunsi la parter
10 static boolean aajunslaparter;
11 static boolean[][][] ok; // soldat poate trece prin camera
12
13 static PrintWriter out;
14 static StreamTokenizer st;
15
16 public static void main(String[] args) throws IOException
17 {
18 long t1,t2;
19 t1=System.currentTimeMillis();
20
21 citire();
22 rezolvare();
23 afisare();
24
25 t2=System.currentTimeMillis();
26 System.out.println("Timp = "+(t2-t1)+" ms");
27 }
28
29 static void citire() throws IOException
30 {
31 int x,y,etaj,k;
32 st=new StreamTokenizer(
33 new BufferedReader(new FileReader("0.in")));
34 st.nextToken(); m=(int)st.nval;
35 st.nextToken(); n=(int)st.nval;
36 st.nextToken(); h=(int)st.nval;
37 st.nextToken(); K=(int)st.nval;
38 st.nextToken(); T=(int)st.nval;
39
40 codCamera=new int[h+1][m+1][n+1];
41 ok=new boolean[h+1][m+1][n+1];
42
43 xk=new int[K+1];
44 yk=new int[K+1];
45 G=new int[K+1];
46
47 for(etaj=1;etaj<=h;etaj++)
48 for(x=1;x<=m;x++)
49 for(y=1;y<=n;y++)
50 {
51 st.nextToken();
52 codCamera[etaj][x][y]=(int)st.nval;
53 }
54
55 for(k=1;k<=K;k++)
56 {
57 st.nextToken(); G[k]=(int)st.nval;
58 st.nextToken(); xk[k]=(int)st.nval;
59 st.nextToken(); yk[k]=(int)st.nval;
60 }
61 }
62
63 static void rezolvare() throws IOException
64 {
65 int soldat,etaj,x,y;
66 for(soldat=1;soldat<=K;soldat++)
67 {
68 System.out.println(soldat);
69 curatTraseu();
70 aajunslaparter=false;
71 etaj=1;
72 x=xk[soldat];
73 y=yk[soldat];
74 coboara(soldat,etaj,x,y); // coboara din (x,y) si vecini
75 for(etaj=2;etaj<=h;etaj++)
76 for(x=1;x<=m;x++)
CAPITOLUL 40. ONI 2000 898

77 for(y=1;y<=n;y++)
78 if(ok[etaj][x][y]) coboara(soldat,etaj,x,y);
79 if(aajunslaparter) ns++;
80 }
81 }
82
83 static void coboara(int soldat, int etaj, int x, int y)
84 {
85 if(etaj==h) {aajunslaparter=true; return;}
86 int i,j;
87 for(i=-1;i<=1;i++)
88 for(j=-1;j<=1;j++)
89 if((x+i>=1)&&(x+i<=m)&&(y+j>=1)&&(y+j<=n))
90 if(trece(G[soldat],codCamera[etaj][x+i][y+j]))
91 ok[etaj+1][x+i][y+j]=true;
92 }
93
94 static boolean trece(int a, int b) // a trece prin b ?
95 {
96 int k;
97 for(k=0;k<=7;k++)
98 if(((1<<k)&a)!=0) // pozitia k in a este 1
99 if(((1<<k)&b)==0) // pozitia k in b este 0
100 return false;
101 return true;
102 }
103
104 static void curatTraseu()
105 {
106 int etaj, x, y;
107 for(etaj=1;etaj<=h;etaj++)
108 for(x=1;x<=m;x++)
109 for(y=1;y<=n;y++)
110 ok[etaj][x][y]=false;
111 }
112
113 static void afisare() throws IOException
114 {
115 out=new PrintWriter(
116 new BufferedWriter(new FileWriter("kommando.out")));
117 if(ns>=T) out.println("DA"); else out.println("NU");
118 out.println(ns);
119 out.close();
120 }
121 }

Listing 40.6.8: kommando4.java


1 import java.io.*; // parcurgere in latime
2 class Kommando4 // test 10 ==> 4.9 sec
3 { // se poate si mai bine folosind traseu="coada" !!!
4 static int m,n,h,K,T;
5 static int[][][] codCamera; // cod camera
6 static int[] xk; // x acoperis soldat k
7 static int[] yk; // y acoperis soldat k
8 static int[] G; // greutatea
9 static int ns=0; // nr soldati ajunsi la parter
10 static boolean aajunslaparter;
11 static boolean[][][] ok; // soldat poate trece prin camera
12 static boolean[][][] traseu;// soldat a mai fost aici
13
14 static PrintWriter out;
15 static StreamTokenizer st;
16
17 public static void main(String[] args) throws IOException
18 {
19 long t1,t2;
20 t1=System.currentTimeMillis();
21
22 citire();
23 rezolvare();
24 afisare();
25
26 t2=System.currentTimeMillis();
27 System.out.println("Timp = "+(t2-t1)+" ms");
CAPITOLUL 40. ONI 2000 899

28 }
29
30 static void citire() throws IOException
31 {
32 int x,y,etaj,k;
33 st=new StreamTokenizer(
34 new BufferedReader(new FileReader("10.in")));
35 st.nextToken(); m=(int)st.nval;
36 st.nextToken(); n=(int)st.nval;
37 st.nextToken(); h=(int)st.nval;
38 st.nextToken(); K=(int)st.nval;
39 st.nextToken(); T=(int)st.nval;
40
41 codCamera=new int[h+1][m+1][n+1];
42 ok=new boolean[h+1][m+1][n+1];
43 traseu=new boolean[h+1][m+1][n+1];
44
45 xk=new int[K+1];
46 yk=new int[K+1];
47 G=new int[K+1];
48
49 for(etaj=1;etaj<=h;etaj++)
50 for(x=1;x<=m;x++)
51 for(y=1;y<=n;y++)
52 {
53 st.nextToken();
54 codCamera[etaj][x][y]=(int)st.nval;
55 }
56
57 for(k=1;k<=K;k++)
58 {
59 st.nextToken(); G[k]=(int)st.nval;
60 st.nextToken(); xk[k]=(int)st.nval;
61 st.nextToken(); yk[k]=(int)st.nval;
62 }
63 }
64
65 static void rezolvare() throws IOException
66 {
67 int soldat,etaj,x,y;
68 for(soldat=1;soldat<=K;soldat++)
69 {
70 curatTraseu();
71 aajunslaparter=false;
72 etaj=0;
73 x=xk[soldat];
74 y=yk[soldat];
75 coboara(soldat,etaj,x,y);
76 for(etaj=1;etaj<=h;etaj++)
77 for(x=1;x<=m;x++)
78 for(y=1;y<=n;y++)
79 if(ok[etaj][x][y]) coboara(soldat,etaj,x,y);
80 if(aajunslaparter) ns++;
81 }
82 }
83
84 static void coboara(int soldat, int etaj, int x, int y)
85 {
86 if(etaj==h) {aajunslaparter=true; return;}
87 int i,j;
88 for(i=-1;i<=1;i++)
89 for(j=-1;j<=1;j++)
90 if((x+i>=1)&&(x+i<=m)&&(y+j>=1)&&(y+j<=n))
91 if(!traseu[etaj+1][x+i][y+j])
92 {
93 traseu[etaj+1][x+i][y+j]=true;
94 if(trece(G[soldat],codCamera[etaj+1][x+i][y+j]))
95 ok[etaj+1][x+i][y+j]=true;
96 }
97 }
98
99 static boolean trece(int a, int b) // a trece prin b ?
100 {
101 int k;
102 for(k=0;k<=7;k++)
103 if(((1<<k)&a)!=0) // pozitia k in a este 1
CAPITOLUL 40. ONI 2000 900

104 if(((1<<k)&b)==0) // pozitia k in b este 0


105 return false;
106 return true;
107 }
108
109 static void curatTraseu()
110 {
111 int etaj, x, y;
112 for(etaj=0;etaj<=h;etaj++)
113 for(x=1;x<=m;x++)
114 for(y=1;y<=n;y++)
115 {
116 ok[etaj][x][y]=false;
117 traseu[etaj][x][y]=false;
118 }
119 }
120
121 static void afisare() throws IOException
122 {
123 out=new PrintWriter(
124 new BufferedWriter(new FileWriter("kommando.out")));
125 if(ns>=T) out.println("DA"); else out.println("NU");
126 out.println(ns);
127 out.close();
128 }
129 }
901
Anexa A

Un pic de matematic !

A.1 ...

A.1.1 Prezentare general 

...

A.1.2 Exemple

...

902
Anexa B

Un pic de programare!

B.1 ...

B.1.1 Prezentare general 

...

B.1.2 Exemple

...

903
Glosar

19
10 , 573 directie, 80
'¯n', 13 distanµa Manhattan, 377
=, 442 doi pointeri, 435
³ir al frecvenµelor, 678
³ir circular, 655 emplace_back, 53
empty(), 478, 581
1LL, 358
end(), 53, 186, 335, 336, 338, 358, 360, 404,
1LL $$ 62, 13
475, 478, 514
accumulate, 401 erase, 186, 336, 514
aib, 475 exponenµiere logaritmic , 642
AIB 2D, 473 rst, 53, 76, 338, 478, 581
algorimul lui Euclid, 649 fracµie continu , 367
algoritm de interclasare, 515 freopen, 11
algoritm de tip mars, 354 front(), 358, 478
algoritm de tip greedy, 473
algoritmul lui Eratostene, 534 gcd, 307
algoritmul lui Euclid, 367 getchar(), 438
assert, 59 greedy, 478
auto, 41, 307, 309, 338, 366, 382, 395
heap, 25
auto&, 309, 338, 419
I.d.k.:
back(), 336, 358, 404, 478 I don't know who the author is., v
begin(), 53, 90, 186, 335, 336, 338, 358, 360, ifndef, 204
404, 475, 478, 514 ifstream, 13
binarySearch, 76 inline, 150
bitset, 132, 454, 476 insert, 186, 358
bool, 186, 359, 360, 395, 445, 514 int32_t, 335, 336, 358
bool operator, 475 int64_t, 336
brute-force, 349 isdigit, 438
BUF_SIZE 1 $$ 17, 25 iterator, 186

c utare binar , 3, 26, 27, 113, 115, 354, 374, lambda, 90, 319
405, 416, 429, 434, 473, 502, 516, lazy, 53
564, 655 liniarizare matrice, 648
char, 401, 404, 438 liniarizarea matricei, 480
ciclu, 648 long long, 13
cin vs scanf, 11 lower_bound, 41, 514
ciurul lui Eratostene, 330, 427, 451, 454, make_pair, 39, 581
530, 583, 628 matrice, 372
clear(), 338 mediana ³irului, 669
cmmdc, 413, 414 memset, 116, 139, 296
const, 360, 382, 419, 445, 475 min, 438
const auto&, 475 min_element, 401, 404
count, 338 multiplu comun, 649
cout vs ofstream, 16
cout vs printf, 11 nth_element, 517
num rul de divizori, 254
deque, 478 numeric_limits, 336
descompunere în factori primi, 254, 330 <int>::max(), 336

904
GLOSAR 905

ofstream, 13 stiv , 579, 673


ofstream vs cout, 16 strategie de tip Meet In The Middle, 497
operator, 360, 419, 445 strategie Greedy, 342, 363
or (operator alternativ), 92 strategie greedy, 415
ordine lexicograc , 479 string, 338, 401, 404
struct, 13, 123, 124, 186, 296, 358, 360, 382,
pair, 53, 74, 76, 338, 478, 581 419, 445, 475, 514
pop(), 581 subsecvenµa de sum  maxim , 9
pop_back(), 404, 478 sume parµiale, 430, 434, 558, 600, 655
pop_front(), 478 swap, 80, 478
prexe, 434 sync_with_stdio, 358
principiul lui Dirichlet, 440
printf vs cout, 11 tehnica meet in the middle, 440
programare dinamic , 473, 476 tehnica greedy, 473
push, 581 template, 438
push_back, 307, 309, 335, 338, 351, 358, tie, 59
360, 382, 475, 478, 514 ignore, 59
push_front, 478 top(), 581
rbegin(), 59, 338 trage cu tunul, iv
read() ... fast, 23 true, 514
recurenµ , 589 tuple, 59
relaµie de recurenµ , 600 typedef, 150, 514
rend(), 59 typename, 438
resize, 419
reverse, 404 unique, 41, 514
unsigned, 150
sau exclusiv, 440 upper_bound, 438, 518
scanf vs cin, 11 using, 359
second, 53, 76, 338, 478, 581
SegmTree, 53 vector, 41, 53, 59, 90, 307, 309, 335, 336,
set, 186, 338 338, 351, 358, 360, 382, 395, 401,
size, 335 404, 419, 475, 476, 514
size(), 307, 309, 335, 336, 338, 358, 360, 404, back(), 395
419 begin(), 41, 401
sizeof, 139 emplace_back, 59
sort, 41, 53, 59, 90, 335, 336, 338, 360, 401, end(), 41, 401
438, 445, 475, 478, 514 erase, 41
greater, 401 pop_back(), 395
sortare, 373, 429, 467, 473, 488, 669, 673 push_back, 41, 395
sortare prin num rare, 467 resize, 395
Sqrt, 41 size(), 59, 395
srand, 351 vector de frecvenµ , 348, 374, 434
stack, 581 vector de vizitare, 583
statistici de ordine, 669 vectorul frecvenµelor, 669
Bibliograe
[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

[6] Calude C.; Teoria algoritmilor, Ed. Universit µii Bucure³ti, 1987

[7] Cerchez, E., “erban, M.; Informatic  - manual pentru clasa a X-a., Ed. Polirom, 2000

[8] Cerchez, E.; Informatic  - Culegere de probleme pentru liceu, Ed. Polirom, 2002

[9] Cerchez, E., “erban, M.; Programarea în limbajul C/C++ pentru liceu, Ed. Polirom, 2005

[10] Cori, R.; Lévy, J.J.; Algorithmes et Programmation, Polycopié, version 1.6;
http://w3.edu.polytechnique.fr/informatique/
[11] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Introducere în Algoritmi, Ed Agora, 2000

[12] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Pseudo-Code Language, 1994

[13] Cristea, V.; Giumale, C.; Kalisz, E.; Paunoiu, Al.; Limbajul C standard, Ed. Teora, Bucure³ti,
1992

[14] Erickson J.; Combinatorial Algorithms; http://www.uiuc.edu/~jeffe/

[15] Flanagan, D.; Java in a Nutshell, O'Reilly, 1997.

[16] Giumale C., Negreanu L., C linoiu S.; Proiectarea ³i analiza algoritmilor. Algoritmi de sortare,
Ed. All, 1997

[17] Halim S., Halim F., Competitive programming, 2013

[18] Knuth, D.E.; Arta program rii calculatoarelor, vol. 1: Algoritmi fundamentali, Ed. Teora,
1999.

[19] Knuth, D.E.; Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici, Ed. Teora,
2000.

[20] Knuth, D.E.; Arta programarii calculatoarelor, vol. 3: Sortare ³i c utare, Ed. Teora, 2001.

[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.

906
BIBLIOGRAFIE 907

[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

[31] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/


2007/Info/Rezolvari_C10.pdf
[32] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/
2007/Info/Rezolvari_C11.pdf

[33] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/


2007/Info/Rezolvari_Baraj.pdf
[34] Skiena S.S., Revilla M.A.; Programming challenges - The Programming Contest Training
Manual, Springer, 2003

[35] Tomescu, I.; Probleme de combinatoric  ³i teoria grafurilor, Editura Didactic  ³i Pedagogic ,
Bucure³ti, 1981

[36] Tomescu, I.; Leu, A.; Matematic  aplicat  în tehnica de calcul, Editura Didactic  ³i Pedago-
gic , Bucure³ti, 1982

[37] Tudor, S.; Informatic  - prolul real intensiv, varianta C++; Editura L&S, Bucure³ti, 2004

[38] Tudor, S.; Hutanu, V,; Informatic  intensiv; Editura L&S, Bucure³ti, 2006

[39] V duva, C.M.; Programarea in JAVA. Microinformatica, 1999

[40] Vi³inescu, R.; Vi³inescu, V.; Programare dinamic  - teorie ³i aplicaµii; GInfo nr. 15/4 2005

[41] Vlada, M.; Conceptul de algoritm - abordare modern , GInfo, 13/2,3 2003

[42] Vlada, M.; Grafuri neorientate ³i aplicaµii. Gazeta de Informatic , 1993

[43] Vlada, M.; Gândirea Algoritmic  - O Filosoe Modern  a Matematicii ³i Informaticii, CNIV-
2003, Editura Universit µii din Bucure³ti, 2003

[44] Weis, M.A.; Data structures and Algorithm Analysis, Ed. The Benjamin/Cummings Pu-
blishing Company. Inc., Redwoods City, California, 1995.

[45] Winston, P.H., Narasimhan, S.; On to JAVA, Addison-Wesley, 1996

[46] Wirth N.; Algorithms + Data Structures = Programs, Prentice Hall, Inc 1976

[47] *** - Gazeta de Informatic , Editura Libris, 1991-2005

[48] *** - https://github.com/DinuCr/CS/blob/master/Info/stuff%20stuff/Re


zolvari_C09.pdf
[49] *** - https://dokumen.tips/documents/rezolvaric09.html

[50] *** - https://www.scribd.com/doc/266218102/Rezolvari-C09

[51] *** - https://www.scribd.com/document/396362669/Rezolvari-C10

[52] *** - https://www.scribd.com/document/344769195/Rezolvari-C11

[53] *** - https://www.scribd.com/document/364077679/Rezolvari-C11-pdf

[54] *** - https://needoc.net/rezolvari-c11-pdf


BIBLIOGRAFIE 908

[55] *** - https://vdocumente.com/algoritmi-i-structuri-de-date.html

[56] *** - https://pdfslide.net/documents/algoritmi-si-structuri-de-date-


1-note-de-cuprins-1-oji-2002-clasa-a-ix-a-1-11.html
[57] *** - https://www.infoarena.ro/ciorna

[58] *** - https://infoarena.ro/olimpici

[59] *** - https://www.infogim.ro/

[60] *** - https://www.pbinfo.ro/

[61] *** - http://www.cplusplus.com/

[62] *** - http://www.cplusplus.com/doc/tutorial/operators/

[63] *** - http://www.info1cup.com/

[64] *** - http://www.olimpiada.info/

[65] *** - http://www.usaco.org/

[66] *** - http://algopedia.ro/

[67] *** - http://campion.edu.ro/

[68] *** - http://varena.ro/

[69] *** - http://rmi.lbi.ro/rmi_2019/

[70] *** - https://codeforces.com/

[71] *** - https://cpbook.net/

[72] *** - https://csacademy.com/

[73] *** - https://gazeta.info.ro/revigoram-ginfo/

[74] *** - https://oj.uz/problems/source/22

[75] *** - https://profs.info.uaic.ro/~infogim/2019/index.html

[76] *** - https://wandbox.org/

[77] *** - https://en.cppreference.com/w/cpp/language/operator_alternative

[78] *** - https://en.cppreference.com/w/cpp/algorithm

[79] *** - https://www.ejoi2019.si/

[80] *** - https://en.wikipedia.org/wiki/Algorithm

[81] *** - https://en.wikipedia.org/wiki/List_of_algorithms

[82] *** - https://en.wikipedia.org/wiki/List_of_data_structures


Lista autorilor
problemelor ³i indicaµiilor

Adrian Airinei, 669 Marcel Dr gan, 26, 422


Adrian Diaconu, 693 Maria Niµ , 253, 757
Adrian Niµ , 253, 757 Maria Pandele, 56
Alin Burµa, 558 Marinel “erban, 241, 861, 883
Marius Dumitran, 633, 639
Budai István, 497, 569 Marius Nicoli, 3
Mihai Calancea, 86
Carmen Minc , 153, 196, 547
Mihai Stroe, 257, 796, 875
Carmen Popescu, 118, 665
Mircea Lup³e-Turpan, 429
Che³c  Ciprian, 46, 106, 183, 189, 313, 342,
Mircea Pa³oi, 663
534, 574, 623
Mugurel Ionuµ Andreica, 683
Claudiu-Cristian Gorea, 450
Constantin G l µan, 67, 143, 354, 501, 678 Nistor Moµ, 37, 480, 676
Cosmin Mihai Tutunaru, 556, 566
Cristian Toncea, 64 Octavian Dumitra³cu, 78
Cristina B rbieru, 695 Ovidiu Dom³a, 749, 854, 879
Cristina Bohm, 237
Cristina Luca, 761 P tca³ Csaba, 627, 636, 642
Cristina Sichim, 636, 639 Piµ-Rada Ionel-Vasile, 97, 439, 530, 613
Piµ-Rada Mihail-Costin, 96
Dan Grigoriu, 690, 746 Popa Daniel, 167
Dan Pracsiu, 427, 443, 560 Popescu Carmen, 111
Daniel Posd r scu, 72 Posd r scu Daniel, 342
Doru Popescu Anastasiu, 176, 251, 698
Radu Vi³inescu, 220
Emanuela Cerchez, 223 Robert Hasna, 619
Emanuela Cherchez, 861 Rodica Moldovan, 167
Eugen Ionescu, 870 Roxana Tîmplaru, 686, 864
Eugen Nodea, 136, 291, 515
Sichim Cristina, 623
Filip Cristian Buruian , 639 Silaghi Lucian, 539
Gemene Narcis-Gabriel, 373 Silviu G nceanu, 739, 766
Soa Viµelaru, 473
Iordache Ioan-Bogdan, 9 Stelian Ciurea, 427, 563, 627
Szabo Zoltan, 166
Liliana Colin, 96
Lucian Bicsi, 305, 439 Turdean Alexandru, 332

Manolache Gheorghe, 320, 434, 583 Vlad Ionescu, 579

909

S-ar putea să vă placă și