Sunteți pe pagina 1din 598

Informatică

Gimnaziu + EJOI

2023-10
ALGORITMI ELEMENTARI
+
PROBLEME DE INFORMATICĂ
date la olimpiade

EJOI
ı̂n

**** **** 2022 2021 2020


2019 2018 2017 **** ****

Dacă ştii să rezolvi problemele date ı̂n ultimii 10-20 de ani
atunci vei şti să rezolvi problemele care se vor da ı̂n acest an!

(European Junior Olympiad in Informatics)


... draft (ciornă) ...
*** Nobody is perfect ***

Adrian Răbâea, Ph.D.

https://www.scribd.com/user/552245048/Adi-Rabaea

2023-10-13
Figura 1: Descompunerea canonică a lui f

”O imagine valorează
cât 1000 de cuvinte!” 1

x1 x1
x3 x3
x2 x2
elevii f = aleg ”şeful” elevi
ı̂n clasă ı̂n clasă
fac echipe echipei
y1 y1
y3 y3
y2 y2

p : proiecţia canonică (se fac listele) i : injecţia canonică


(şefii revin)
(ı̂n clasă)
xr x1

liste cu elevi f¯ = predau lista şefi de echipe


”şefului” echipei Im f 
yr y1

Figura 2: Un desen ... i ` f¯ ` p f

1
I.d.k.: ”I don’t know who the author is.”
Dedication

I would like to dedicate this book ...

a ”to myself” ...


2
3
in a time when ...
4
I will not be able ... ”to be”
5
That is because ...
6
”When I Die Nobody Will Remember Me”

a to people impacted by the book


a to my nephew Adam.

( in ascending order! )

2
https://www.femalefirst.co.uk/books/carol-lynne-fighter-1034048.html
3
https://otiliaromea.bandcamp.com/track/dor-de-el
4
https://en.wikipedia.org/wiki/To_be,_or_not_to_be
5
Everyone will go to his star and the stars are very far from each other!
6
https://www.youtube.com/watch?v=eMtcDkSh7fU

ii
Prefaţă

Stilul acestor cărţi este ... ca şi cum aş vorbi cu nepoţii mei (şi chiar cu mine ı̂nsumi!) ... ı̂ncercând
ı̂mpreună să găsim o rezolvare cât mai bună pentru o problemă dată la olimpiadă.
Ideea, de a scrie aceste culegeri de probleme date la olimpiadele de informatică, a apărut acum
câţiva ani când am ı̂ntrebat un student (care nu reuşea să rezolve nişte probleme foarte simple):
”Ce te faci dacă un elev, care ştie că eşti student şi că studiezi şi informatică, te roagă, din când ı̂n
când, să-l ajuţi să rezolve câte o problemă de informatică dată la gimnaziu la olimpiadă, sau pur
şi simplu ca temă de casă, şi tu, aproape de fiecare dată, nu ı̂l poţi ajuta? Eu cred că nu este prea
bine şi poate că ... te faci ... de râs!” Pe vremea mea (!), când eram elev de gimnaziu, un student
era ca un fel de ... zeu! Cu trecerea anilor am ı̂nţeles că nu este chiar aşa! Şi ı̂ncă ceva: nu am
reuşit să ı̂nţeleg de ce, atunci când cineva nu poate să rezolve corect o problemă de informatică
de clasa a 6-a, dată la olimpiada de informatică sau ca temă de casă, foloseşte această scuză: ”eu
nu am făcut informatică ı̂n liceu!” şi acest cineva este ”zeul” sau ”zeiţa” despre care vorbeam!.
7
Sunt convins că este important să studiem cu atenţie cât mai multe probleme rezolvate! . Cred
că sunt utile şi primele versiuni ale acestor cărţi ... ı̂n care sunt prezentate numai enunţurile şi
indicaţiile ”oficiale” de rezolvare. Acestea se găsesc ı̂n multe locuri; aici ı̂ncerc să le pun pe ”toate
la un loc”! Fiecare urcă spre vârf ... cât poate! Sunt multe poteci care duc spre vârf iar această
carte este ... una dintre ele!
Limbajul de programare se alege ı̂n funcţie de problema pe care o avem de rezolvat. Cu nişte
ani ı̂n urmă alegerea era mai simplă: dacă era o problemă de calcul se alegea Fortran iar dacă era
o problemă de prelucrarea masivă a datelor atunci se alegea Cobol. Acum alegerea este ceva mai
8 9
dificilă! :-) Vezi, de exemplu, IOI2020 şi IOI2019 , IOI2015 . (Numai GCC a fost mereu!)
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
10
de rezolvare!) .
Am ı̂nceput câteva cărţi (pentru clasele de liceu) cu mai mulţi ani ı̂n urmă, pentru
perioada 2000-2007 ([29] - [33]), cu anii ı̂n ordine crescătoare!). A urmat o pauză de
câţiva ani (destul de mulţi!). Am observat că acele cursuri s-au ”ı̂mprăştiat” un pic
”pe net” ([48] - [56])! Încerc acum să ajung acolo unde am rămas ... plecând mereu
din prezent ... până când nu va mai fi posibil ... aşa că, de această dată, anii sunt
ı̂n ordine ... descrescătoare! :-)
”Codurile sursă” sunt cele ”oficiale” (publicate pe site-urile olimpiadelor) sau publicate pe alte
site-uri (dacă mi s-a părut că sunt utile şi se poate ı̂nvăţa câte ceva din ele).
Pentru liceu perioada acoperită este de ”azi” (până când va exista acest ”azi” pentru mine!)
până ı̂n anul 2000 (aveam deja perioada 2000-2007!).
Pentru gimnaziu perioada acoperită este de ”azi” până ı̂n anul 2010 (nu am prea mult timp
11
disponibil şi, oricum, calculatoarele folosite la olimpiade ı̂nainte de 2010 erau ceva mai ’slabe’ şi

... restricţiile de memorie, din enunţurile problemelor, par ’ciudate’ acum!).


7
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’ ...
8
IOI2019 şi IOI2020 au a permis utilizarea limbajelor de programare C++ şi Java
9
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python şi Ruby
10
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema ”avârcolaci” - ONI2014 clasa a 11-a
11
https://en.wikipedia.org/wiki/Computer

iii
În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: ı̂n noiembrie 2017
12
ı̂n China, ı̂n noiembrie 2019 ı̂n SUA şi ı̂n iunie 2020 ı̂n Japonia. În iunie 2022 ”Frontier”
13
depăşeşte pragul ”exascale”! (1.102 Exaflop/s). ”Peta” a fost depăşit ı̂n 2008, ”tera” ı̂n 1997,
14
”giga” ı̂n 1972. . Pentru ce a fost mai ı̂nainte, vezi https://en.wikipedia.org/wiki/Li
st_of_fastest_computers.
15
O mică observaţie: ı̂n 2017 a fost prima ediţie a olimpiadei EJOI ı̂n Bulgaria şi ... tot
16
ı̂n Bulgaria a fost şi prima ediţie a olimpiadei IOI ı̂n 1989. Dar ... prima ediţie a olimpiadei
17
IMO (International Mathematical Olympiad) a fost ı̂n România ı̂n 1959. Tot ı̂n România s-au
ţinut ediţiile din anii 1960, 1969, 1978, 1999 şi 2018. Prima ediţie a olimpiadei BOI (Balkan
Olympiad in Informatics) a fost ı̂n România ı̂n 1993 la Constanţa. Prima ediţie a olimpiadei
CEOI (Central-European Olympiad in Informatics) a fost ı̂n România ı̂n 1994 la Cluj-Napoca.
Revenind la ... “culegerile noastre” ... mai departe, probabil, va urma completarea unor
informaţii ı̂n ”Rezolvări detaliate” ... pentru unele probleme numai (tot din cauza lipsei timpului
necesar pentru toate!). Prioritate vor avea problemele de gimnaziu (nu pentru că sunt mai ’uşoare’
ci pentru că ... elevii de liceu se descurcă şi singuri!). Acum, ı̂n martie 2022, am ı̂nceput şi
redactarea unei culegeri de probleme date la bacalaureat ı̂n ultimii câţiva ani (câţi voi putea!).
Îmi aduc aminte că exista o interesantă vorbă de duh printre programatorii din generaţia mea:
”nu se trage cu tunul ı̂ntr-o muscă” . Sensul este: nu se scrie un cod complicat dacă se
poate scrie un cod simplu şi clar! Asta ı̂ncerc eu ı̂n ”Rezolvări detaliate”.
Vom ı̂ncerca, ı̂mpreună, şi câteva probleme de ... IOI ... dar asta este o treabă ... nu prea
uşoară! Cred totuşi că este mai bine să prezint numai enunţuri ale problemelor date la IOI ı̂n
ultimii câţiva ani! (asta aşa, ca să vedem cum sunt problemele la acest nivel!). Cei care ajung
acolo sau vor să ajungă acolo (la IOI) sigur nu au nevoie de ajutorul meu! Se descurcă singuri!
”ALGORITMI utili la olimpiadele de informatică”, separat pentru gimnaziu şi liceu, sper să
fie de folos, aşa cum cred că sunt [1] - [28], [34] - [47], [57] - [83], ... şi multe alte cărţi şi site-uri!.
Ar fi interesant să descoperim noi ı̂nşine cât mai mulţi algoritmi ... ı̂n loc să-i ı̂nvăţăm pur şi
simplu!
O altă mică observaţie: ce am strâns şi am scris ı̂n aceste
cărţi se adresează ”ı̂ncepătorilor” interesaţi de aceste teme! Nu se
adresează ”avansaţilor” şi nici cârcotaşilor! Sunt evidente sursele
”de pe net” (şi locurile ı̂n care au fost folosite); cred că nu sunt
necesare ”ghilimele anti-plagiat” şi precizări la fiecare pas!
Şi un ultim gând: criticile şi sfaturile sunt utile dacă au valoare
reală! Dacă sunt numai aşa ... cum critică lumea la un meci de
fotbal ... sau cum, pe bancă ı̂n parc, unul ”ı̂şi dă cu părerea”
despre rezolvarea problemelor economice ale ţării ... atunci ... !!!
18
”I’m only responsible for what I say, not for what you understand.”
Adrese interesante (rezultatele elevilor români):
https://stats.ioinformatics.org/halloffame/
https://stats.ioinformatics.org/tasks/
http://stats.ioinformatics.org/results/ROU

Adresele acestor cursuri:


https://www.scribd.com/user/550183580/Adrian-Rabaea
https://www.scribd.com/user/552245048/Adi-Rabaea
https://drive.google.com/drive/folders/1hC5PZuslCdS95sl37SW46H-qy59GRDGZ

Bistriţa, 13th October 2023 Adrian Răbâea

12
https://www.top500.org/lists/top500/2022/06/
13
https://en.wikipedia.org/wiki/Metric_prefix/
14
https://en.wikipedia.org/wiki/Computer_performance_by_orders_of_magnitude
15
https://ejoi.org/about/
16
https://stats.ioinformatics.org/olympiads/
17
https://en.wikipedia.org/wiki/International_Mathematical_Olympiad
18
https://www.facebook.com/johnwayne/photos/a.156450431041410/2645523435467418/?type=3
”Acknowledgements”

”I want to thank God most of all because


without God
I wouldn’t be able to do any of this.” 19

and

I thank everyone for their support 20

and/or
suggestions!

19
I.d.k.: ”I don’t know who the author is.”
20
donation/donaţie:
. name: RABAEA AUREL ADRIAN, IBAN: RO22BRDE060SV11538970600, SWIFT: BRDEROBUXXX

v
Despre autor

21
nume: Răbâea Aurel-Adrian, 18.03.1953 - ...

telefon: +40 728 zz ll aa +40 363 xx 25 xx


email: adrian1803@gmail.com skype: adrian.r53
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)
https://dmi.cunbm.utcluj.ro/
Discipline predate (1992-2018):
Algoritmi şi structuri de date, Algoritmi ı̂n teoria opţiunilor financiare, Bazele matematice
ale calculatoarelor, Bazele tehnologiei informaţiei, Birotică, Capitole speciale de inteligenţă
artificială, 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):


Instituţia: Academia de Studii Economice, Bucureşti;
22
Titlul tezei: Algoritmi paraleli şi aplicaţii pe maşini virtual paralele
23
Conducător ştiinţific: Prof. dr. ing. Gheorghe Dodescu
Teme studiate: utilizarea algoritmilor paraleli ı̂n teoria opţiunilor financiare
Studii de specializare ı̂n informatică - Certificat 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
24
Conducător ştiinţific: 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ă
25
Conducător ştiinţific: 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 Infor-
26
matică, Departamentul de Informatică
27
- (1992-1979) Centrul de Informatică şi organizare CINOR, Bucureşti
28
Olimpiade: (fiind elev la Liceul Militar ”Dimitrie Cantemir” - Breaza, PH)
- 1971: Olimpiada Naţională de matematică: participare (fără rezultat notabil)
- 1970: Olimpiada Naţională de matematică: participare (fără rezultat notabil)
21
https://dmi.cunbm.utcluj.ro/?page_id=2
22
http://opac.biblioteca.ase.ro/opac/bibliographic_view/149021
23
http://www.ionivan.ro/2015-PERSONALITATI/Dodescu.htm
24
http://old.fmi.unibuc.ro/ro/prezentare/promotii/promotia1978informatica_10ani/
25
https://ro.wikipedia.org/wiki/Ion_V%C4%83duva
26
https://fmi.univ-ovidius.ro/
27
https://www.cinor.ro/index.htm
28
https://www.cantemircml.ro/

vi
Dhawan Sanjeev, Kulvinder Singh, Eduard-Marius Craciun, Adrian Răbâea, Amit Batra;
Next-Cart Recommendation by Utilizing Personalized Item Frequency Information in Online Web
Portals, Neural Processing Letters, 2023; https://doi.org/10.1007/s11063-023-11207-2

Sanjeev Dhawan, Kulvinder Singh, Adrian Răbâea, Amit Batra;


ImprovedGCN: An efficient and accurate recommendation system employing lightweight graph con-
volutional networks in social media; Electronic Commerce Research and Applications, 2022, Vol.
55, Pages 101191-101207; DOI: https://doi.org/10.1016/j.elerap.2022.101191

Sanjeev Dhawan, Kulvinder Singh, Adrian Răbâea, Amit Batra;


Session centered Recommendation Utilizing Future Contexts in Social Media; Analele Ştiinţifice
ale Universităţii Ovidius Constanţa - Seria Matematică, Vol. 29 (3), 2021, 91-104; DOI:
https://doi.org/10.2478/auom-2021-0036

E.M. Craciun, A. Răbâea, S. Das;


Cracks Interaction in a Pre-Stressed and Pre-Polarized Piezoelectric Material;
Journal of Mechanics, 36(2), 2020, 177-182; DOI: https://doi.org/10.1017/jmech.2019.57
https://www.cambridge.org/core/journals/journal-of-mechanics/article/abs/cracks-interaction-
in-a-prestressed-and-prepolarized-piezoelectric-material/3938BF0AD79D5BE6B81BC4FB6BE80208

EM Craciun, A Răbâea, MF Popa, CI Mihailov;


Crack propagation in the human bone. Mode I of fracture; Analele Ştiinţifice ale Univ. Ovidius
Constanţa - Seria Matematică, Vol. 26(2), 2018, 59-70; DOI: https://doi.org/10.2478/auom-2018-
0018

EM Craciun, M Marin, A Răbâea;


Anti-plane crack in human bone. I. Mathematical modelling; Analele Ştiinţifice ale Univer-
sităţii Ovidius Constanţa - Seria Matematică, Volume 26, Issue 1, 2018, Pages 81-90; DOI:
https://doi.org/10.2478/auom-2018-0004

T. Sadowski, E. M. Craciun, A. Răbâea, L. Marsavina;


Mathematical modeling of three equal collinear cracks in an orthotropic solid; Meccanica 51, 329-
339 (2016); https://doi.org/10.1007/s11012-015-0254-5

E.M. Craciun, T. Sadowski, A. Răbâea;


Stress concentration in an anisotropic body with three equal collinear cracks in Mode II of frac-
ture. I. Analytical study; ZAMM Journal of Applied Mathematics and Mechanics / Zeitschrift
fr Angewandte Mathematik und Mechanik, Vol. 94 (9); September 2014, Pages 721-729;
https://doi.org/10.1002/zamm.201200293

E-M Craciun, T. Sadowski, L. Marsavina, A. Răbâea;


Mathematical Aspects Regarding Cracks Behaviour in Wood Composites; Key Engineering Mate-
rials 601:108-111, March 2014; DOI: 10.4028/www.scientific.net/KEM.601.108

ƒ Probleme de informatică date la bacalaureat


ƒ Probleme de informatică date la olimpiade - I.O.I.
ƒ Probleme de informatică date la olimpiade - liceu
ƒ Probleme de informatică date la olimpiade - gimnaziu
ƒ Algoritmi elementari + olimpiade EJOI gimnaziu
ƒ Algoritmi şi structuri de date + olimpiade EGOI liceu
ƒ Programa pentru bacalaureat - informatică liceu
ƒ Programa pentru olimpiadă - informatică gimnaziu + liceu

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
Cuprins

Prefaţă iii

Cuprins viii

Lista figurilor xv

Lista tabelelor xviii

Lista programelor xix

1 Noţiuni elementare 1
1.1 Numărare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Conversii ale datelor numerice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Bazele de numeraţie 2 şi 16 . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 Conversia din baza 10 ı̂n baza 2 . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.3 Conversia din baza 2 ı̂n baza 10 . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.4 Conversia din baza 2 ı̂n baza 16 şi invers . . . . . . . . . . . . . . . . . . . . 7
1.3 Programe ciudate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.3.1 Un program ciudat ı̂n Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.2 Un program ciudat ı̂n C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.3 Un program ciudat ı̂n Java . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3.4 De ce au apărut rezultate ciudate? . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.5 Verificări cu “calculator.exe” . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3.6 Câteva valori ı̂n reprezentarea BYTE . . . . . . . . . . . . . . . . . . . . . 14
1.3.7 Reprezentarea numerelor 100 şi -100 pe BYTE . . . . . . . . . . . . . . . . 16
1.4 Codificarea numerelor ı̂ntregi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.4.1 O reprezentare grafică pentru codul complementar . . . . . . . . . . . . . . 17
1.4.2 Codificarea numerelor ı̂ntregi cu semn . . . . . . . . . . . . . . . . . . . . . 18
1.5 Coduri ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.5.1 Caractere de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.5.2 Litere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Litere mici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Litere mari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.3 Cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.4 ’Whitespace’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

2 Programare C+++ 21
2.1 Structura unui program in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.1.1 Şablon simplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.1.2 namespace std; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.3 Pregătire suport pentru fişiere . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.4 Acces la fişiere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.5 Citire şi scriere folosind fişiere . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.1.6 Comentarii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.1.7 Reprezentarea internă a datelor ı̂n fişierele .txt . . . . . . . . . . . . . . . . 34
2.1.8 Două citiri din fişier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.1.9 Câteva explicaţii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.1.10 Variabilă neiniţializată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.1.11 Alegerea identificatorilor (numelor) . . . . . . . . . . . . . . . . . . . . . . . 39
2.2 Alfabetul limbajului . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

viii
2.2.1 Un program util . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.2.2 Coduri de control şi spaţii . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.2.3 Caracterele printabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.2.4 Codurile alfanumerice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.2.5 Simboluri ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.3 Alocarea variabilelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.3.1 Puţină istorie! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.3.2 ’Big endian’ sau ’Little endian’ . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.3.3 Afişare ı̂n ’hexa’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.3.4 Afişare ı̂n ’binar’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.3.5 Alocarea variabilelor ı̂n spaţiul de memorie . . . . . . . . . . . . . . . . . . 47
2.3.6 MSB şi LSB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.4 Instrucţiuni de decizie - structuri elementare de control . . . . . . . . . . . . . . . 52
2.4.1 Instrucţiunea if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.4.2 Instrucţiunea case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.4.3 Operatorul condiţional (ternar) ? : . . . . . . . . . . . . . . . . . . . . . . 52
2.5 Instrucţiuni repetitive - structuri elementare de control . . . . . . . . . . . . . . . . 52
2.5.1 Instrucţiunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Instrucţiunea for + if + continue . . . . . . . . . . . . . . . . . . . . . . 53
Instrucţiunea for + if + break . . . . . . . . . . . . . . . . . . . . . . . . 53
2.5.2 Instrucţiunea while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.5.3 Instrucţiunea do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.6 Instrucţiuni de salt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.6.1 Instrucţiunea continue; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.6.2 Instrucţiunea break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7 Funcţii utile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.1 getchar(); . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.2 system(“PAUSE”); . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

3 Clasa a V-a 55
3.1 Algoritmi elementari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.1.1 Tipuri simple de date. Tipul ı̂ntreg (pe 4 octeţi), tipul logic . . . . . . . . . 55
3.1.2 Structura liniară, alternativă şi repetitivă . . . . . . . . . . . . . . . . . . . 55
3.1.3 Prelucrarea cifrelor numerelor naturale scrise ı̂n baza 10 . . . . . . . . . . . 58
3.1.4 Divizorii numerelor naturale . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.1.5 Numere prime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.1.6 Descompunere ı̂n factori primi . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.1.7 Numărul divizorilor - calcul cu formulă . . . . . . . . . . . . . . . . . . . . 71
3.1.8 Algoritmul lui Euclid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.1.9 cmmmc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3.1.10 Simplificarea fracţiilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.1.11 Calculul factorialului . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.1.12 Ridicare la putere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.2 Generări de şiruri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.2.1 Progresii aritmetice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.2.2 Progresii geometrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
3.2.3 Şirul lui Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
3.3 Fişiere text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.3.1 Citire număr fix de elemente din fişiere text . . . . . . . . . . . . . . . . . . 84
3.3.2 Scriere ı̂n fişiere text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
3.3.3 Citire număr variabil de intregi din fişiere text . . . . . . . . . . . . . . . . 85
3.3.4 Citire număr variabil de caractere/cifre din fişiere text . . . . . . . . . . . . 86
cls 5 OJI 2016 Problema 2 - Palindrom . . . . . . . . . . . . . . . . . . . . 86
3.4 Prelucrări de şiruri de numere citite succesiv, fără memorarea lor . . . . . . . . . . 88
3.4.1 Determinare maxim/minim . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.4.2 Determinare primele două maxime . . . . . . . . . . . . . . . . . . . . . . . 89
3.4.3 p40 Subsecventa crescatoare, fara stocare . . . . . . . . . . . . . . . . . . . 90
3.4.4 p041 Numararea secventelor, fara stocare . . . . . . . . . . . . . . . . . . . 91
˜
3.5 Tablouri unidimensionale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.5.1 p042 Parcurgere vector p042 . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.5.2 p043 Inversare ı̂n vector p043 . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.5.3 p044 Căutare liniara in vector p044 . . . . . . . . . . . . . . . . . . . . . . 95
3.5.4 p045 verificare in vector p045 . . . . . . . . . . . . . . . . . . . . . . . . . . 96
3.5.5 p046 Copiere spre dreapta ı̂n afara vectorului p046 . . . . . . . . . . . . . . 98
3.5.6 p047 Copiere un pic spre dreapta p047 . . . . . . . . . . . . . . . . . . . . . 100
3.5.7 p048 Inserare vector ı̂n vector p048 . . . . . . . . . . . . . . . . . . . . . . . 101
3.5.8 p049 Permutare circulară ı̂n vector p049 . . . . . . . . . . . . . . . . . . . . 104
3.5.9 p050 Interclasarea a doi vectori sortaţi p050 . . . . . . . . . . . . . . . . . . 106
3.5.10 p051 Vectori caracteristici / de frecvenţă p051 . . . . . . . . . . . . . . . . 108
3.6 Sortări . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.6.1 p052 Sortare cu functia din biblioteca p052 . . . . . . . . . . . . . . . . . . 110
3.6.2 p053 Sortare prin selecţie p053 . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.6.3 p054 Sortare prin inserţie p054 . . . . . . . . . . . . . . . . . . . . . . . . . 112
3.6.4 p055 Sortare prin metoda bulelor p055 . . . . . . . . . . . . . . . . . . . . . 113
3.6.5 p056 Sortare cu vector de frecvenţă p056 . . . . . . . . . . . . . . . . . . . 114
3.6.6 p057 Sortare prin numărare p057 . . . . . . . . . . . . . . . . . . . . . . . . 116

I EJOI - The European Junior Olympiad in Informatics 118


4 EJOI 2023 119
4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
4.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

5 EJOI 2022 121


5.1 adjacent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
5.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
5.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
5.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
5.2 root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
5.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
5.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.3 tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
5.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
5.4 game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
5.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
5.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
5.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.5 permutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
5.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
5.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
5.6 unfriendly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
5.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
5.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
5.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
6 EJOI 2021 159
6.1 AddK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
6.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
6.1.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
6.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.2 KPart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.2.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
6.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
6.3 XCopy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
6.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
6.3.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
6.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
6.4 BinSearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
6.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
6.4.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
6.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
6.5 Dungeons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
6.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
6.5.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
6.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
6.6 Waterfront . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
6.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
6.6.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

7 EJOI 2020 176


7.1 fountain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
7.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
7.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
7.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
7.2 triangulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
7.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
7.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
7.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
7.3 exam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
7.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
7.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
7.4 xorsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
7.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
7.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
7.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
7.5 cards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
7.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
7.5.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
7.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
7.6 game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
7.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
7.6.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
7.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229

8 EJOI 2019 230


8.1 Hanging rack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
8.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
8.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
8.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
8.2 XORanges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
8.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
8.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
8.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
8.3 T - Covering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
8.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
8.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
8.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
8.4 Adventure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
8.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
8.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
8.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
8.5 Tower . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
8.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
8.5.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
8.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
8.6 Colouring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
8.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
8.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
8.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291

9 EJOI 2018 292


9.1 Hills . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
9.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
9.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
9.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
9.2 Passports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
9.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
9.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
9.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
9.3 AB-Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
9.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
9.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
9.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
9.4 Prime Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
9.4.1 *Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
9.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
9.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
9.5 Cycle sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
9.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
9.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
9.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416

10 EJOI 2017 417


10.1 Magic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
10.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
10.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
10.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
10.2 Particles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
10.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
10.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
10.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
10.3 Six . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
10.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
10.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
10.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
10.4 Camel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
10.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
10.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
10.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484
10.5 Experience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484
10.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
10.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
10.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
10.6 Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
10.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
10.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
10.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513

Appendix A Programa olimpiadei - gimnaziu 515


A.1 Clasa a V-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
A.2 Clasa a VI-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
A.3 Clasa a VII-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
A.4 Clasa a VIII-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
A.5 Barajul de selecţie a lotului naţional lărgit . . . . . . . . . . . . . . . . . . . . . . . 517
A.6 Note . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517

Appendix B ”Instalare” C++ 518


B.1 Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
B.1.1 Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
B.1.2 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
B.1.3 Utilizare Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
B.1.4 Setări Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
B.1.5 Multe surse ı̂n Code Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
B.2 winlibs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
B.2.1 GCC şi MinGW-w64 pentru Windows . . . . . . . . . . . . . . . . . . . . . 528
B.2.2 PATH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
B.2.3 CodeBlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531

Appendix C main(), cin, cout, fin, fout 541


C.1 Funcţia main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541

Appendix D ”Vecini” ... 543


D.1 Direcţiile N, E, S, V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543

Appendix E Diagonale ı̂n matrice 545


E.1 Diagonale ı̂n matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
E.2 Simetric faţă de diagonale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
E.2.1 Simetric faţă de diagonala principală . . . . . . . . . . . . . . . . . . . . . . 546
E.2.2 Simetric faţă de diagonala secundară . . . . . . . . . . . . . . . . . . . . . . 546

Appendix F Parcurgere matrice 547


F.1 Parcurgere NE spre SV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547

Appendix G Exponenţiere rapidă 548


G.1 Analogie baza 2 cu baza 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
G.2 Notaţii, relaţii şi formule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
G.3 Pregătire pentru scrierea codului! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
G.4 Codul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
G.5 Chiar este rapidă? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
G.6 Rezumat intuitiv! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554

Appendix H Căutare binară 556


H.1 Mijlocul = ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
H.2 Poziţie oarecare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
H.2.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
H.2.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
H.3 Poziţia din stânga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
H.3.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
H.3.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
H.4 Poziţia din dreapta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
H.4.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
H.4.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563

Index 564

Bibliografie 568
Lista autorilor 571
Lista figurilor

1 Descompunerea canonică a lui f . . . . . . . . . . . . . . . . . . . . . . . . . . . . i


2 Un desen ... i ` f¯ ` p f . . . . . . . . . . . . . . . . . . . . . . . . . . . i

1.1 Monede: 8, 4, 2, 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Monede: 11 = 8 + 2 + 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Un program ciudat ı̂n Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4 Un program ciudat ı̂n C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5 Un program ciudat ı̂n Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.6 Calculator Standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.7 Selectare ”Programmer” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.8 50 000 in bazele 16, 10, 8 şi 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.9 QWORD = 64 biţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.10 DWORD = 32 biţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.11 WORD = 16 biţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.12 BYTE = 8 biţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.13 QWORD ultimul byte din 50 000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.14 Click pe 0 l-a schimbat ı̂n 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.15 DWORD valoare 208 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.16 WORD valoare 208 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.17 BYTE valoare -48 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.18 QWORD valoare 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.19 BYTE valoare 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.20 BYTE valoare 127 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.21 BYTE valoare -1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.22 Click pe poziţia indicată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.23 -128 se transformă ı̂n -120 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.24 BYTE 100 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.25 BYTE -100 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.26 Cercul reprezentării ı̂n cod complementar . . . . . . . . . . . . . . . . . . . . . . . . 17

2.1 Cartele cu 80 de coloane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23


2.2 Maşină de ’scris’ cartele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.3 Bandă perforată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4 Cod sursă pe hârtie de imprimantă . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.5 Pachet cu cartele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.6 p01.cpp compilare (build) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.7 p01.cpp execuţie (run) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.8 p01.cpp rezultat execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.9 Style Allman (ANSI) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.10 Style Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.11 TAB size in spaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.12 Compiler warnings: [-Wall] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.13 Optimize even more (for speed) [-O2] . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.14 p05 cod sursă şi fereastra de execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.15 Fişierul cu datele de intrare: f05.in . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.16 f05.in vizualizat cu WinHex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.17 fişierul cu datele de ieşire f05.out şi rezultatul afişat pe ecran . . . . . . . . . . . . 33
2.18 fişierul f05.in ı̂n WinHex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.19 Dimensiuni fişiere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

xv
2.20 Execuţie p05a.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.21 Alocarea spaţiului de memorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.22 Poziţia capului de citire după prima citire . . . . . . . . . . . . . . . . . . . . . . . 37
2.23 Poziţia capului de citire după a doua citire . . . . . . . . . . . . . . . . . . . . . . 38
2.24 Poziţia capului de citire după a treia citire . . . . . . . . . . . . . . . . . . . . . . . 38
2.25 Execuţie p05a.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.26 isctrl+isspace+isblank . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.27 isprint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.28 isalnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.29 simboluri ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.30 Big or Little endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.31 Big si Little endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.32 Afişare hexa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.33 Afişare binar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.34 Plimbare prin biţi! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.35 MSB+LSB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.36 MSB endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.37 LSBendian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.38 LSBendian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

3.1 Divizorii lui n 24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63


3.2 divizorii lui n 100 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.3 Divizorii lui n 24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.4 Numărul divizorilor - calcul cu formulă . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.5 şirul Fibonacci fără vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
3.6 citire număr neprecizat de numere ı̂ntregi . . . . . . . . . . . . . . . . . . . . . . . 86
3.7 parcurgere vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.8 inversare ı̂n vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
3.9 verificare proprietati ı̂n vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
3.10 Copiere spre dreapta ı̂n afara vectorului . . . . . . . . . . . . . . . . . . . . . . . . 99
3.11 Copiere ”un pic” spre dreapta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
3.12 Inserare vector ı̂n vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3.13 permutare circulara ı̂n vector - v1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
3.14 permutare circulara ı̂n vector - v2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
3.15 interclasarea a doi vectori sortaţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
3.16 sortare cu functia din biblioteca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.17 sortare prin selectie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.18 sortare prin insertie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
3.19 sortare prin metoda bulelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

B.1 Fişierele din Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518


B.2 CodeBlocks & C++ Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
B.3 Ce conţine C:¯ OJI ¯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
B.4 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
B.5 New -¿ Text document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
B.6 Schimbare nume fişier şi nume extensie fişier . . . . . . . . . . . . . . . . . . . . . . 521
B.7 Confirmare schimbare extensie ı̂n .cpp . . . . . . . . . . . . . . . . . . . . . . . . . 522
B.8 Pregătit pentru Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
B.9 Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . 522
B.10 Primul cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . . . . . . . . . . 523
B.11 Build - compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
B.12 0 error(s), 0 warning(s) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
B.13 Run - execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
B.14 Executat corect: a făcut “nimic” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
B.15 Settings  % Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
B.16 Toolchain executables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
B.17 Unde sunt acele programe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
B.18 Multe surse ı̂n Code Blocks - setări . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
B.19 Multe surse in Code Blocks - exemple . . . . . . . . . . . . . . . . . . . . . . . . . 527
B.20 mingw64 pe D: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
B.21 search path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
B.22 System properties –¿ Advanced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
B.23 Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
B.24 Edit Environment Variables –¿ New . . . . . . . . . . . . . . . . . . . . . . . . . . 530
B.25 Calea şi versiunea pentru gcc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
B.26 Settings –¿ Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
B.27 Toolchain executables –¿ Auto-detect . . . . . . . . . . . . . . . . . . . . . . . . . . 532
B.28 New –¿ Text Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
B.29 New text Document.txt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
B.30 Schimbare nume şi extensie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
B.31 Moore apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
B.32 Look for another app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
B.33 Cale pentru codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
B.34 Selectare codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
B.35 Editare test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
B.36 Compilare test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
B.37 Mesaje după compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
B.38 Execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
B.39 Rezultat execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
B.40 Fişiere apărute după compilare! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
B.41 Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿ . . . . . . . . . . . . . . . . . . 538
B.42 Lista programelor de utilizat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
B.43 Selectare Code::Blocks IDE pentru fişierele .cpp . . . . . . . . . . . . . . . . . . 539
B.44 Editare+Compilare+Execuţie pentru test02 . . . . . . . . . . . . . . . . . . . . . 539
B.45 Selectare tab ce conţine test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 540

D.1 ”vecini” ı̂n matrice şi sistem Oxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543


D.2 Toţi ”pereţii” sunt la N sau la V ı̂n matricea ”bordată” . . . . . . . . . . . . . . . 543
D.3 Toţi ”pereţii” sunt codificaţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 544

E.1 diagonale ı̂n matrice - start = 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545


E.2 diagonale ı̂n matrice - start = 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
E.3 simetric faţă de diagonala principală . . . . . . . . . . . . . . . . . . . . . . . . . . 546
E.4 simetric faţă de diagonala secundară . . . . . . . . . . . . . . . . . . . . . . . . . . 546

F.1 Interclasarea vectorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547

G.1 Analogie B2 cu B10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548

H.1 căutare binară: mijlocul zonei ı̂n vector . . . . . . . . . . . . . . . . . . . . . . . . 556


Lista tabelelor

1.1 Cifrele sistemului hexazecimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7


1.2 Două numere ... destul de mici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3 Suma celor două numere ... destul de mici . . . . . . . . . . . . . . . . . . . . . . . 10
1.4 Două numere ... destul de mari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.5 Suma celor două numere ... destul de mari . . . . . . . . . . . . . . . . . . . . . . . 11
1.6 Două numere ... şi mai mari! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.7 Suma celor două numere ... prea mari! . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.8 Caractere de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.9 ’white-space-characters’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

7.1 Triangulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190


7.2 Card Trick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

8.1 XOR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242

xviii
Lista programelor

1.3.1 ciudat.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.2 ciudat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.3 ciudat.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5.1 ascii litere mici.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.2 ascii litere mari.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.3 ascii cifre.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.4 whitespace.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.0.1 p00.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.0.2 p00.asm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.0.3 secvenţă de cod ’cpp’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.1.1 p01 structura unui program C++ . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.1.2 p02 namespace std; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.3 p03 fstream şi iostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.4 p04 ifstream şi ofstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.5 p05 fin şi fout şi cout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.1.6 p05a citire dublă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.1.7 p00x1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.1.8 p05b variabilă neiniţializată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.1.9 p06 nume pentru variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.2.1 ascii comentat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.3.1 Big or Little endian.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.3.2 afisare hexa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.3.3 afisare binar.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.3.4 adrese in memorie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.3.5 show bytes in memory.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.4.1 p07 instrucţiunea if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.5.1 p08 instrucţiunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.5.2 p09 instrucţiunea while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.5.3 p10 instrucţiunea do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.6.1 p11 instrucţiunea continue; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.6.2 p12 instrucţiunea break; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.1 p13 instrucţiunea getchar(); . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.2 p14 instrucţiunea system(“PAUSE”); . . . . . . . . . . . . . . . . . . . . . . . 54
3.1.1 p001 tipul intreg.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.1.2 p002 tipul logic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.1.3 p003 structura liniara.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.1.4 p004 structura alternativa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.1.5 p005 structura repetitiva for.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.1.6 p006 structura repetitiva for.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.1.7 p007 structura repetitiva for.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.1.8 p008 structura repetitiva while.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.1.9 p009 numarul si suma cifrelor.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.1.10 p010 plasare cifra la sfarsit.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.1.11 p011 plasare cifra la inceput.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.1.12 p012 inversare numar.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.1.13 p013 verificare palindrom.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.1.14 p014 modificarea cifrei de la mijloc.cpp . . . . . . . . . . . . . . . . . . . . . . . 60
3.1.15 p015 modificarea mijlocului numarului.cpp . . . . . . . . . . . . . . . . . . . . . 62
3.1.16 p016 lista divizorilor.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

xix
3.1.17 p017 numarul divizorilor.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.1.18 p018 suma divizorilor.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.1.19 p019 lista divizorilor optim.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.1.20 p020 numere prime - verificare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 67
3.1.21 p021 numere prime - ciurul lui Eratostene.cpp . . . . . . . . . . . . . . . . . . . 68
3.1.22 p022 descompunere in factori primi.cpp . . . . . . . . . . . . . . . . . . . . . . . 69
3.1.23 p023 numarul divizorilor cu formula si vectori.cpp . . . . . . . . . . . . . . . . . 71
3.1.24 p024 numarul divizorilor cu formula fara vectori . . . . . . . . . . . . . . . . . . 72
3.1.25 p025 cmmdc algoritmul lui Euclid . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.1.26 p026 cmmmmc cu Euclid.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3.1.27 p027 cmmmmc cu descompunere in factori primi.cpp . . . . . . . . . . . . . . . . 75
3.1.28 p028 simplificarea fractiei folosind cmmdc.cpp . . . . . . . . . . . . . . . . . . . . 79
3.1.29 p029 calcul factorial.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.1.30 p030 calcul putere.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.2.1 p031 progresii aritmetice.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.2.2 p032 progresii geometrice.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
3.2.3 p033 sirul lui Fibonacci.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
3.2.4 p033 sirul lui Fibonacci.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
3.3.1 p035 fisier text citire numar precizat de intregi.cpp . . . . . . . . . . . . . . . . . 84
3.3.2 p036 fisier text citire numar neprecizat de intregi.cpp . . . . . . . . . . . . . . . 85
3.3.3 p037 fisier text citire numar neprecizat de caractere.cpp . . . . . . . . . . . . . . 87
3.4.1 p038 determinare maxim fara stocarte.cpp . . . . . . . . . . . . . . . . . . . . . . 88
3.4.2 p039 primele doua maxime distincte fara stocare.cpp . . . . . . . . . . . . . . . . 89
3.4.3 p040 subsecventa crescatoare fara stocare.cpp . . . . . . . . . . . . . . . . . . . . 90
3.4.4 p041 numararea secventelor fara stocare.cpp . . . . . . . . . . . . . . . . . . . . 91
3.5.1 p042 parcurgere vector.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.5.2 p043 inversare vector.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
3.5.3 p045 cautare liniara in vector.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
3.5.4 p044 verificare in vector.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
3.5.5 p046 copiere in afara vectorului.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 99
3.5.6 p047 copiere un pic spre dreapta.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 100
3.5.7 p048 inserare vector in vector.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3.5.8 p049 permutare circulara in vector - v1.cpp . . . . . . . . . . . . . . . . . . . . . 104
3.5.9 p049 permutare circulara in vector - v2.cpp . . . . . . . . . . . . . . . . . . . . . 105
3.5.10 p050 interclasare vectori sortati.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 106
3.5.11 p051 vector de frecventa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
3.6.1 p052 sortare cu functia din biblioteca.cpp . . . . . . . . . . . . . . . . . . . . . . 110
3.6.2 p053 sortare prin selectie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.6.3 p054 sortare prin insertie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
3.6.4 p055 sortare prin metoda bulelor.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 113
3.6.5 p056 sortare prin frecventa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
3.6.6 p057 sortare prin numarare.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
5.1.1 EJOI2022 Perechi ModelSolution.cpp . . . . . . . . . . . . . . . . . . . . . . . . 123
5.1.2 EJOI2022 Perechi check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
5.2.1 EJOI2022 Root rw 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
5.2.2 EJOI2022 Root check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
5.3.1 EJOI2022 Tree mhq.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.3.2 EJOI2022 Tree check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.4.1 EJOI2022 game fast power 2 m.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 142
5.4.2 EJOI2022 game check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
5.5.1 EJOI2022 permutations solution.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 148
5.5.2 EJOI2022 permutations check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 151
5.6.1 EJOI2022 LCS 16774096.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
5.6.2 EJOI2022 LCS check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
7.1.1 checkerFountain.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
7.1.2 Fountain-979362.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
7.1.3 Fountain-981117.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
7.1.4 Fountain-981322.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
7.1.5 Fountain-983271.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
7.1.6 Fountain-985016.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
7.1.7 Fountain-992243.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
7.1.8 Fountain-992296.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
7.1.9 Fountain-994277.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
7.2.1 triangulation-981145.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
7.2.2 triangulation-981153.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
7.2.3 triangulation-981926.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
7.2.4 triangulation-983329.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
7.2.5 triangulation-985775.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
7.3.1 exam-981110.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.3.2 exam-982347.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
7.3.3 exam-984751.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
7.3.4 exam-985120.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
7.3.5 exam-986828.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
7.3.6 exam-992381.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
7.4.1 checkerXorSort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
7.4.2 xorsort-980965.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
7.4.3 xorsort-982930.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
7.4.4 xorsort-986772.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
8.1.1 216711-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
8.1.2 217942-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
8.1.3 229739-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
8.1.4 232150-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
8.1.5 232196-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
8.1.6 237579-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
8.1.7 237607-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
8.1.8 237667-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
8.1.9 237671-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
8.1.10 240337-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
8.1.11 242103-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
8.1.12 246139-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
8.2.1 218063-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
8.2.2 220350-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
8.2.3 229737-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
8.2.4 236349-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
8.2.5 237576-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
8.2.6 237584-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
8.2.7 242110-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
8.2.8 246138-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
8.2.9 246793-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
8.2.10 checkerXORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
8.3.1 229752-covering.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
8.3.2 248792-covering.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
8.4.1 218070-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
8.4.2 218553-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
8.4.3 226630-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
8.4.4 226699-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
8.4.5 229759-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
8.4.6 232209-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
8.4.7 240339-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
8.4.8 242112-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
8.4.9 244272-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
8.6.1 218172-colouring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
8.6.2 226737-colouring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
8.6.3 250194-colouring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
9.1.1 ds n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
9.1.2 ds n2 other.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
9.1.3 gr ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
9.1.4 ig n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
9.1.5 isaf ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
9.1.6 11899245-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
9.1.7 56159794-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
9.1.8 81469343-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
9.1.9 88103111-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
9.1.10 Hills1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
9.1.11 checkerHills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
9.2.1 passports pkun.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
9.2.2 41334650-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
9.2.3 41335815-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
9.2.4 48114130-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
9.2.5 48115887-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
9.2.6 52626176-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
9.2.7 56188505-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
9.2.8 79747607-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
9.2.9 checkerPassports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
9.3.1 rg wa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
9.3.2 rg wa2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
9.3.3 solve.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
9.3.4 ds ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
9.3.5 gr ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
9.3.6 isaf ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
9.3.7 rg ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
9.3.8 11899245-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
9.3.9 56157022-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
9.3.10 86481283-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
9.3.11 86488789-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
9.3.12 86816257-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
9.3.13 86816797-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
9.3.14 87021285-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
9.3.15 87201594-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
9.3.16 87466173-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
9.3.17 87522420-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
9.3.18 88013224-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
9.3.19 88040747-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
9.3.20 chem1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
9.3.21 checkChemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
9.4.1 ds diams.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
9.4.2 ds zhfs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
9.4.3 full random.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
9.4.4 many random.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
9.4.5 prime tree pashka.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
9.4.6 print id.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
9.4.7 sol 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
9.4.8 solve is.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
9.4.9 solve is no local.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
9.4.10 solve qoo2p5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
9.4.11 checkPrime-tree.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
9.5.1 cycle sort ad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
9.5.2 gr ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
9.5.3 ig ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
9.5.4 isaf ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
9.5.5 isaf ok slowio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
9.5.6 40973023-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
9.5.7 41148878-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
9.5.8 43081231-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
9.5.9 67068459-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
9.5.10 75463698-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
9.5.11 87786210-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
9.5.12 checkCycle-sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
10.1.1 482109.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
10.1.2 483136-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
10.1.3 484114-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
10.1.4 484759-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
10.1.5 484817-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
10.1.6 1703927-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
10.1.7 1748766-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
10.1.8 2119774-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
10.1.9 2210797-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
10.2.1 482015-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
10.2.2 482536-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
10.2.3 482782-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
10.2.4 483295-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
10.2.5 484328-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
10.2.6 485011-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
10.2.7 1497611-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
10.2.8 1628810-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
10.2.9 checkerParticles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
10.3.1 481436-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
10.3.2 482515-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
10.3.3 505367-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
10.3.4 1275534-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
10.3.5 1633337-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
10.3.6 1709178-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
10.3.7 2123900-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
10.3.8 2219732-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
10.4.1 486658-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
10.4.2 486713-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
10.4.3 502953-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
10.4.4 2220355-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
10.4.5 2659930-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
10.4.6 checkerCamel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
10.5.1 486102-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
10.5.2 486224-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
10.5.3 486736-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
10.5.4 487126-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
10.5.5 489869-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
10.5.6 501765-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
10.5.7 1082259-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
10.5.8 1549152-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
10.5.9 1573243-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
10.5.10 1593395-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
10.5.11 1696625-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
10.5.12 checkerExperience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
10.6.1 486962-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
10.6.2 487418-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
10.6.3 487442-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
10.6.4 488403-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
10.6.5 488741-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
10.6.6 490435-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
10.6.7 507893-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
10.6.8 594280-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
10.6.9 1173310-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510
10.6.10 1275946-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
10.6.11 checkerGame.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512
C.1.1 sss1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
C.1.2 sss2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
G.4.1exponentiere rapida1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
G.4.2exponentiere rapida2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
G.4.3exponentiere rapida3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
G.4.4exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
G.5.1exponentiere naiva MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
G.5.2exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
G.6.1secventa cod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
H.2.1cautare binara-v1-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
H.2.2cautare binara-v1-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
H.3.1cautare binara-v2-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
H.3.2cautare binara-v2-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
H.4.1cautare binara-v3-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
H.4.2cautare binara-v3-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
Capitolul 1

Noţiuni elementare

1.1 Numărare
Să numărăm: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ... Asta ı̂n baza 10, baza noastră de numeraţie!
Să notăm cu B10 mulţimea cifrelor pe care le folosim.
Deci
B10 r0, 1, 2, 3, 4, 5, 6, 7, 8, 9x
cifrele fiind scrise ı̂n ordine crescătoare.
Cifra cea mai mică este 0 (zero).
Cifra cea mai mare este 9, iar baza noastră este 10.
Observaţia 1. Cifra cea mai mare este egală cu ”baza-1”.
Să continuăm numărarea: 13, 14, 15, 16, 17, ...
Observaţia 2. Numai ultima cifră se modifică (se măreşte).
Să continuăm să numărăm: 18, 19, 20, ...
Observaţia 3. Dacă ultima cifă (poziţia din dreapta) este 9 (cea mai mare cifră) urmează 0 (zero
pe poziţia respectivă) şi se măreşte cu 1 cifra din stânga (poziţia zecilor, ı̂n cazul nostru!)
Este ca şi cum am avea 3 cutii:

cutia 2 cutia 1 cutia 0


aici se pun bancnote de 100 lei aici se pun bancnote de 10 lei aici se pun bancnote de 1 leu

*** 0 bancnote *** *** 1 bancnotă *** *** 9 bancnote ***

şi punem bancnotele ı̂n cutiile corecte!


”Continuăm să numărăm” este ca şi cum am primi ı̂n continuare o bancnotă de 1 leu pe care
trebuie să o punem ı̂n cutia corectă. Nu avem voie să avem 10 bancnote ı̂ntr-o cutie! Dacă s-au
făcut totuşi 10 bancnote de 1 leu ... luăm toate cele 10 bancnote din ”cutia 0” ... cutia rămâne
goală ... deci 0 (zero) acolo, ... ne ducem la bancă şi schimbăm cu o bancnotă de 10 lei ... venim
acasă şi punem bancnota de 10 lei ı̂n cutia corectă!

cutia 2 cutia 1 cutia 0


aici se pun bancnote de 100 lei aici se pun bancnote de 10 lei aici se pun bancnote de 1 leu

*** 0 bancnote *** *** 2 bancnote *** *** 0 bancnote ***

1
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.1. NUMĂRARE 2

Cam aşa este această poveste cu numărarea!


Să mai continuăm ”sărind” un pic peste nişte numere: 198, 199, 200, ...
Observaţia 4. Deci ... după 9 vine 0 şi se măreşte poziţia din stânga ... dar aici este 9 ... aşa
că vom pune zero pe acea poziţie şi vom mări poziţia din stânga!

Şi aşa mai departe ...


Este ca şi cum conţinutul cutiilor este:

cutia 2 cutia 1 cutia 0


aici se pun bancnote de 100 lei aici se pun bancnote de 10 lei aici se pun bancnote de 1 leu

*** 1 bancnotă *** *** 9 bancnote *** *** 9 bancnote ***

şi primim o bancnotă de 1 leu pe care trebuie să o punem ı̂n cutia corectă. Dar ... acolo sunt
deja 9 bancnote şi cu bancnota asta se fac 10 bancnote!
Luăm toate cele 10 bancnote de 1 leu ... cutia 0 rămn̂e goală ... deci 0 (zero) acolo ... mergem
la bancă şi schimbăm cele 10 bancnote de 1 leu ı̂n o bancnotă de 10 lei ... venim acasă şi punem
bancnota de 10 lei ı̂n cutia corectă. Dar ... ı̂n cutia 1 erau deja 9 bancnote de 10 lei şi cu această
bancnotă se fac ... 10 bancnote de 10 lei.
Suntem obligaţi să le luăm pe toate cele 10 bancnote ... cutia 1 rămâne goală (la fel este şi
cutia 0, să nu uităm!) ... deci 0 (zero) acolo ... mergem la bancă şi schimbăm cele zece bancnote
de 10 lei ı̂ntr-o bancnotă de 100 de lei ... venim acasă şi punem bancnota de 100 lei ı̂n cutia
corectă. Astfel, cutia 2 conţine acum două bancnote de câte 100 de lei, cutia 1 conţine acum zero
bancnote de câte 10 de lei şi cutia 0 conţine acum zero bancnote de câte 1 leu.

cutia 2 cutia 1 cutia 0


aici se pun bancnote de 100 lei aici se pun bancnote de 10 lei aici se pun bancnote de 1 leu

*** 2 bancnote *** *** 0 bancnote *** *** 0 bancnote ***

Ultima observaţie pe această temă:

Observaţia 5. La fiecare pas (când mai primim câte 1 leu) vom ı̂ncerca să mărim cifra din
dreapta. Dacă reuşim ... o mărim şi gata ... dar ... dacă acolo este cea mai mare cifră atunci
vom pune zero ı̂n locul ei şi ı̂ncercăm să mărim cifra/poziţia imediat la stânga ... şi tot aşa mai
departe ... până când reuşim (ı̂n stânga numărului sunt ... multe zerouri ... care nu se scriu când
nu avem nevoie de ele dar ... sunt acolo atunci când avem nevoie de ele)!

Aceste observaţii sunt simple şi valabile ı̂n orice bază! De exemplu ı̂n B2 şi B16 ... (ı̂nlocuind
10 cu 2, 16, ...).
Evident, atunci când vrem să luăm o bancnotă, contează foarte mult care este poziţia cutiei
din care vom lua acea bancnotă! Acesta este motivul pentru care se spune: ”Sistemul zecimal este
29
un sistem de numeraţie poziţional, având baza 10” .
29
https://ro.wikipedia.org/wiki/Sistem_zecimal
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.2. CONVERSII ALE DATELOR NUMERICE3

1.2 Conversii ale datelor numerice


1.2.1 Bazele de numeraţie 2 şi 16
30
Făcând analogie cu sistemul de numeraţie ı̂n baza 10, ı̂n care se folosesc cifrele

B10 r0, 1, 2, 3, 4, 5, 6, 7, 8, 9x
31
vom ı̂nţelege uşor de ce ı̂n sistemul de numeraţie ı̂n baza 2 se folosesc cifrele

B2 r0, 1x
32
şi ı̂n sistemul de numeraţie ı̂n baza 16 se folosesc ”cifrele”

B16 r0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15x.

La baza 16 apare o problemă: de exemplu, ı̂n scrierea 112 pot să ı̂nteleg că este vorba de alipirea
cifrelor 1, 1, 2 sau 1, 12 sau 11, 2. Pentru a elimina această situaţie, cifrele considerate pentru
sistemul de numeraţie ı̂n baza 16 au fost notate astfel:

B16 r0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F x.

unde A 10, B 11, C 12, D 13, E 14, F 15.

1.2.2 Conversia din baza 10 ı̂n baza 2


33 34
Noi folosim sistemul zecimal de numeraţie iar calculatoarele folosesc sistemul binar. Pentru
uşurinţa citirii informaţiilor binare se foloseşte, de cele mai multe ori, sistemul hexazecimal de
35 36
numeraţie. Deşi apare ı̂n limbajele de programare, sistemul octal de numeraţie se foloseşte
foarte putin!
37
Pentru ı̂nceput să ne ’jucăm’, un pic, astfel: presupunem că avem monede ı̂n număr suficient
de mare din fiecare tip dintre cele din figura Figura ??:

Figura 1.1: Monede: 8, 4, 2, 1

Jocul 1: Să presupunem că, iniţial, avem 11 monede de câte 1 ban.


’Joaca’ noastră este prezentată ı̂n următoarea figură:
30
https://ro.wikipedia.org/wiki/Baz%C4%83_de_numera%C8%9Bie
31
https://en.wikipedia.org/wiki/Binary_number
32
https://ro.wikipedia.org/wiki/Sistem_hexazecimal
33
https://ro.wikipedia.org/wiki/Sistem_zecimal
34
https://ro.wikipedia.org/wiki/Sistem_binar
35
https://ro.wikipedia.org/wiki/Sistem_hexazecimal
36
https://ro.wikipedia.org/wiki/Sistem_octal
37
https://ro.wikipedia.org/wiki/Moned%C4%83
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.2. CONVERSII ALE DATELOR NUMERICE4

Figura 1.2: Monede: 11 = 8 + 2 + 1

De fiecare dată vom parcurge şirul de monede de la stânga spre dreapta!


ˆ În prima etapă (notată E1 ı̂n figură) vom schimba două monede alăturate de 1 ban, cu o

monedă de 2 bani, de fiecare dată când le ı̂ntâlnim ı̂n parcurgerea monedelor de la stânga
la dreapta.
ˆ În a doua etapă (notată E2 ı̂n figură) vom schimba două monede alăturate de 2 bani, cu o

monedă de 4 bani, de fiecare dată când le ı̂ntâlnim ı̂n parcurgerea monedelor de la stânga
la dreapta.
ˆ În a treia etapă (notată E3 ı̂n figură) vom schimba două monede alăturate de 4 bani, cu o

monedă de 8 bani, de fiecare dată când le ı̂ntâlnim ı̂n parcurgerea monedelor de la stânga
la dreapta.
ˆ ...

ˆ Vom proceda ı̂n acest mod până când nu mai avem de făcut schimbări de monede!

k1
Algoritm: pentru etapa cu numărul k vom schimba două monede alăturate de 2 bani, cu o
k
monedă de 2 bani, de fiecare dată când le ı̂ntâlnim ı̂n parcurgerea monedelor de la stânga la
dreapta, pentru k 1, 2, 3, ... până când nu mai avem de făcut schimbări de monede!
Observaţie: Orice monedă poate apărea cel mult o dată la sfârşitul acestui ’joc’ ! În exemplul
de mai sus nu a apărut, ı̂n finalul jocului, moneda de 4 bani. Întotdeauna, la finalul acestui ’joc’,
numărul de apariţii este 0 sau 1 pentru orice tip de monedă!
ˆ Numărul de apariţii al monedei de 8 bani este 1

ˆ Numărul de apariţii al monedei de 4 bani este 0

ˆ Numărul de apariţii al monedei de 2 bani este 1

ˆ Numărul de apariţii al monedei de 1 ban este 1

Putem scrie aceste numere (’de apariţie’) de la stânga spre dreapta astfel: 1011 2 (este chiar
scrierea binară a lui 11 10 (unsprezece ı̂n baza 10). Cel mai din stânga este numărul de apariţii
al monedei cu valoarea cea mai mare (aceasta sigur apare, deci sigur scrierea ı̂ncepe cu 1). Cel
mai din dreapta este numărul de apariţii al monedei cu valoarea cea mai mică (poate fi 1 sau 0).
Iniţial am avut 11 monede (valoarea a fost de 11 bani) iar la final numai 3 monede (dar valoarea
38
ı̂n bani nu s-a schimbat!). Se pare că scopul acestui joc (de fapt este un mic algoritm ) a fost
să obţinem un număr cât mai mic de monede (fără să modificăm valoarea banilor pe care i-am
39
avut iniţial)! Pentru că monedele nu au fost alese oricum, ci sunt puteri consecutive ale lui 2,
rezultatul acestui ’joc’-’algoritm’ este, de fapt, conversia unui număr din baza 10 ı̂n baza 2. Un
40
mic secret: strategia folosită ı̂n acest joc se numeşte strategie ’bottom-up’.
38
https://ro.wikipedia.org/wiki/Algoritm
39
https://ro.wikipedia.org/wiki/Putere_(matematic%C4%83)
40
https://en.wikipedia.org/wiki/Top-down_and_bottom-up_design
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.2. CONVERSII ALE DATELOR NUMERICE5

Pentru că este foarte greu să folosim desene şi pentru monedele de 16 bani, 32 de bani, 64 de
bani, ..., vom folosi numai valorile lor, adică: 1, 2, 4, 8, 16, 32, 64, 128, ...

Jocul 2: Să presupunem că, iniţial, avem 111 monede de câte 1 ban şi vrem să le schimbăm
cu monede de valori cât mai mari (astfel vom avea cât mai puţine monede dar valoarea lor va fi
aceeaşi cu cea iniţială, 111 bani).
Ce monedă să folosim când avem 111 bani? Desigur 128 este prea mare dar 64 se poate folosi.
Mai rămân de schimbat 111-64 = 47 de bani.
Ce monedă să folosim când avem 47 de bani? Desigur 64 este prea mare dar 32 se poate folosi.
Mai rămân de schimbat 47-32 = 15 bani.
Ce monedă să folosim când avem 15 bani? Desigur 16 este prea mare dar 8 se poate folosi.
Mai rămân de schimbat 15-8 = 7 bani.
Ce monedă să folosim când avem 7 bani? Desigur 8 este prea mare dar 4 se poate folosi.
Mai rămân de schimbat 7-4 = 3 bani.
Ce monedă să folosim când avem 3 bani? Desigur 4 este prea mare dar 2 se poate folosi.
Mai rămân de schimbat 3-1 = 1 ban.
Ce monedă să folosim când avem 1 ban? Desigur 2 este prea mare dar 1 se poate folosi.
Mai rămân de schimbat 1-1 = 0 bani.
Ce monedă să folosim când avem 0 bani? Desigur ... niciuna!
Însumând valorile folosite (64, 32, 8, 4, 2, 1) obţinem 111.
Într-un tabel cu 2 linii şi mai multe coloane (!) vom pune valorile monedelor şi ı̂n dreptul
fiecăreia vom pune 1 dacă acea monedă a fost folosită şi 0 dacă nu a fost folosită. Pentru exemplul
anterior, tabelul este:
7 6 5 4 3 2 1 0
moneda: 128=2 64=2 32=2 16=2 8=2 4=2 2=2 1=2
apariţie: 0 1 1 0 1 1 1 1
Este evident că, ı̂ntr-o astfel de reprezentare, se ı̂nsumează numai valorile monedelor care au
1 la ’apariţie’. Secvenţa 01101111 2 este reprezentarea ı̂n baza 2 a numărului 111 (din baza 10).
41
Valorile monedelor fiind puteri, le putem identifica numai prin exponentul la care apare 2:
exponent: 7 6 5 4 3 2 1 0
apariţie: 0 1 1 0 1 1 1 1

Jocul 3: Conversia se poate face prin ı̂mpărţiri succesive.


Fie x 11 numărul ı̂n baza 10. Secvenţa de ı̂mpărţiri este:

(1) se ı̂mparte 11 la 2 şi se obţine câtul 5 şi restul 1


(2) se ı̂mparte ”câtul” 5 la 2 şi se obţine câtul 2 şi restul 1
(3) se ı̂mparte ”câtul” 2 la 2 şi se obţine câtul 1 şi restul 0
(4) se ı̂mparte ”câtul” 1 la 2 şi se obţine câtul 0 şi restul 1
(5) nu mai are rost să se ı̂mpartă ”câtul” 0 la 2 ...

În urma acestor ı̂mpărţiri obţinem că 11 din baza 10 se scrie ı̂n baza 2 astfel: 1011 (resturile
obţinute ... scrise ı̂n ordine inversă).

Această afirmaţie se notează pe scurt astfel: 1110 10112 . Când nu punem ı̂n evidenţă baza,
vom considera că este vorba de baza 10. Putem să nu punem ı̂n evidenţă baza ı̂n notaţia numărului
şi atunci când este foarte clar despre ce bază este vorba!

Primul rest este ultima cifră ı̂n reprezentarea lui 11 ı̂n baza 2. Al doilea rest este penultima
cifră, şi aşa mai departe. Altfel spus, cifrele ı̂n baza 2 se obţin ı̂n ordine inversă.
41
https://www.mathsisfun.com/exponent.html
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.2. CONVERSII ALE DATELOR NUMERICE6

Ca să ”acceptăm” mai uşor acest algoritm, să ı̂ncercăm să facem o conversie din baza 10 ı̂n
baza 10 (da, nu este nicio greşeală, este tot baza 10). Mai precis, vom ı̂ncerca să obţinem cifrele
numărului.
42
Fie x 1395 numărul ı̂n baza 10 (acest număr ne aminteşte de ... Mircea cel Bătrân ).
Secvenţa de ı̂mpărţiri este:

(1) se ı̂mparte 1395 la 10 şi se obţine câtul 139 şi restul 5


(2) se ı̂mparte ”câtul” 139 la 10 şi se obţine câtul 13 şi restul 9
(3) se ı̂mparte ”câtul” 13 la 10 şi se obţine câtul 1 şi restul 3
(4) se ı̂mparte ”câtul” 1 la 10 şi se obţine câtul 0 şi restul 1
(5) nu mai are rost să se ı̂mpartă ”câtul” 0 la 10 ...

În urma acestor ı̂mpărţiri obţinem că 1395 din baza 10 are cifrele 5, 9, 3, 1 dar acestea sunt
ı̂n ordine inversă (adică, dacă vrem să obţinem numărul corect, trebuie să le scriem ı̂n ordinea
inversă faţă de ordinea ı̂n care le-am obţinut).

1.2.3 Conversia din baza 2 ı̂n baza 10


Tot o analogie, cu ceea ce ştim deja din baza 10, ne va ajuta.
Să ne uităm iar la numărul 1395.
ordinul bazei 1000 100 10 1
cifra 1 3 9 5

1395 = 1 mie + 3 sute + 9 zeci + 5 unităţi


adică
1395 = 1 * 1000 + 3 * 100 + 9 * 10 + 5
adică
1395 = 1 * (10*10*10) + 3 * (10*10) + 9 * (10) + 5
adică
3 2 1 0
1395 = 1 * 10 + 3 * 10 + 9 * 10 + 5*10

În tabelul anterior putem să notăm, ı̂n loc de 1000, numai de câte ori apare 10, ı̂n
1000=10*10*10, adică (exponentul puterii):

indicele ordinului bazei 3 2 1 0


ordinul bazei 1000 100 10 1
cifra 1 3 9 5

Valoarea numărului se obţine făcând suma produselor dintre fiecare ”cifră” şi ”ordinul bazei”
corespunzătoare acelei cifre. În exemplul din tabel trebuie să facem produsele:
cifra 1 cu ordinul corespunzător 1000
cifra 3 cu ordinul corespunzător 100
cifra 9 cu ordinul corespunzător 10
cifra 5 cu ordinul corespunzător 1

La fel vom face şi când vom avea baza 2 sau altă bază (nu baza 10 ca acum).
Oricum, aceste lucruri sunt şi ı̂n manualele de matematică de clasa a 5-a, de exemplu:
https://manuale.edu.ro/manuale/Clasa%20a%20V-a/Matematica/ART/#book/u1-p50-51

Observaţie simplă dar importantă: Zerourile din faţa numărului NU contează!


Putem să scriem 001395 dar valoarea tot 1395 este. La fel, vom accepta acest lucru, şi ı̂n baza 2
şi ı̂n oricare altă bază, nu numai ı̂n baza 10.

Să considerăm un număr a cărui reprezentare ı̂n baza 2 este:


42
https://ro.wikipedia.org/wiki/B%C4%83t%C4%83lia_de_la_Rovine
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 7

indicele ordinului bazei 11 10 9 8 7 6 5 4 3 2 1 0


ordinul bazei 2048 1024 512 256 128 64 32 16 8 4 2 1
cifra 0 1 0 1 0 1 1 1 0 0 1 1
Valoarea numărului este:
0*2048 + 1*1024 + 0*512+ 1*256 + 0*128 + 1*64 + 1*32 + 1*16 + 0*8 + 0*4 + 1*2 + 1*1.
Pentru că produsele ı̂n care apare ca factor 0 au valoarea 0, valoarea totală a numărului se
poate calcula făcând numai suma acelor puteri ale bazei care au cifra corespunzătoare 1.
În cazul exemplului nostru, valoarea numărului este:
1024+256+64+32+16+2+1 = 1280+96+18+1 = 1376+19 = 1395.

1.2.4 Conversia din baza 2 ı̂n baza 16 şi invers


Să reprezentăm cifrele B16 r0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15x ı̂n bazele 10, 16 şi 2:

ı̂n baza 10 ı̂n baza 16 ı̂n baza 2


0 0 0000
1 1 0001
2 2 0010
3 3 0011
4 4 0100
5 5 0101
6 6 0110
7 7 0111
8 8 1000
9 9 1001
10 A 1010
11 B 1011
12 C 1100
13 D 1101
14 E 1110
15 F 1111
Tabelul 1.1: Cifrele sistemului hexazecimal

Pentru conversia din baza 2 ı̂n baza 16 se ı̂nlocuiesc grupuri de câte 4 cifre binare cu simbolul
corespunzător din baza 16 din tabel. Dacă numărul de cifre binare nu este multiplu de 4 se
completează ı̂n stânga numărului cu atâtea 0-uri câte sunt necesare ca numărul de cifre să fie
multiplu de 4.
De exemplu: 1011011010 À 10 1101 1010 À 0010 1101 1010 À 2 D A À 2DA
Invers, pentru conversia din baza 16 ı̂n baza 2, se ı̂nlocuieţe fiecare cifră cu câte un grup de 4
cifre binare corespunzător cifrei hexazecimale din tabel.
De exemplu: ABAC À 1010 1011 1010 1100 À 1010101110101100
1.3 Programe ciudate
Atunci când dorim să participăm la olimpiadele de informatică, nu are prea mare importanţă dacă
limbajul de programare pe care ı̂l folosim este Java, C/C++, Pascal sau alt limbaj de programare.
Oricare ar fi limbajul de programare, este bine să ştim cum se reprezintă numerele ı̂n memoria
calculatorului. Altfel putem avea surprize ciudate!
Dacă nu suntem atenţi la valorile pe care le pot lua variabilele cu care lucrăm, putem obţine
rezultate greşite chiar dacă modalitatea de rezolvare a problemei este corectă. Iată astfel de situaţii
ı̂n Pascal, C/C++ şi Java.
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 8

1.3.1 Un program ciudat ı̂n Pascal


Iată un program Pascal ı̂n care dorim să calculăm suma 20.000  30.000.

Listing 1.3.1: ciudat.pas


1 var x,y,z:integer;
2
3 BEGIN
4 x:=20000;
5 y:=30000;
6 z:=x+y;
7 write(x,’+’,y,’=’,z);
8 END.

Deşi ne aşteptam să apară ca rezultat 50.000, surpriza este că pe ecran apare

20000+30000=-15536

Figura 1.3: Un program ciudat ı̂n Pascal

1.3.2 Un program ciudat ı̂n C++


Iată şi un program ı̂n C++ ı̂n care dorim să calculăm suma 20.000  30.000.

Listing 1.3.2: ciudat.cpp


#include<iostream.h>
int main()
{
int x,y,z;
x=20000;
y=30000;
z=x+y;
cout << x <<"+"<<y<<"="<<z;
return 0;
}

Deşi ne aşteptam să apară ca rezultat 50.000, surpriza este că pe ecran apare din nou

20000+30000=-15536
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 9

Figura 1.4: Un program ciudat ı̂n C++

Şi mai ciudat pare a fi faptul că a apărut acelaşi rezultat greşit care a apărut la programul ı̂n

Pascal! Ce se ı̂ntâmplă pe aici?

1.3.3 Un program ciudat ı̂n Java

Nici ı̂n Java nu scăpăm de astfel de ”necazuri”!


Iată un program ı̂n Java ı̂n care dorim să calculăm produsul 200.000 ˜ 300.000.

Listing 1.3.3: ciudat.java


1 class Ciudat
2 {
3 public static void main(String args[])
4 {
5 int x,y,z;
6 x=200000;
7 y=300000;
8 z=x*y;
9 System.out.println(x+"*"+y+"="+z);
10 }
11 }

Pe ecran apare: 200000*300000=-129542144

Figura 1.5: Un program ciudat ı̂n Java


CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 10

1.3.4 De ce au apărut rezultate ciudate?

Numerele sunt reprezentate ı̂n baza 2 ı̂n calculator (nu ı̂n baza 10).
Dar noi suntem obişnuiţi cu ... baza 10.
Dacă reuşim să facem analogii cu baza 10, vom reuşi să ı̂nţelegem mai uşor şi mai repede atunci

când trebuie să folosim baza 2 sau baza 16.


Atunci când valorile obţinute din calcule depăşesc marginile permise de tipul variabilelor im-
plicate ı̂n respectivele calcule, se pot obţine rezultate eronate.
Să presupunem că scriem pe un caiet de matematică, cu pătrăţele, şi avem o zonă de 5 căsuţe
ı̂n care să ne facem calculele (nişte adunări). Prima căsuţă este pentru semn şi următoarele 4
căsuţe sunt pentru cifrele numerelor.
Semnele + (plus) şi - (minus) sunt codificate prin cifrele 0 şi respectiv 1.
Numerele sunt aliniate la dreapta şi se pun zerouri ı̂n faţa numărului dacă este necesar să se
completeze zona alocată cifrelor numărului. De exemplu: 89 se scrie 0089 (au fost alocate 4 poziţii
pentru cifrele numerelor!).

1 2 3 4 5 6 7

nefolosit semn cifrele numărului (pe 4 poziţii)


... ... ... ... ... ... ...
... ... 0 0 0 8 9 +
... ... 0 0 7 5 4
... ... =
... ... ... ... ... ... ...
Tabelul 1.2: Două numere ... destul de mici

Şi acum se face adunarea:

1 2 3 4 5 6 7

nefolosit semn cifrele numărului (pe 4 poziţii)


... ... ... ... ... ... ...
... ... 0 0 0 8 9 +
... ... 0 0 7 5 4
... ... 0 0 8 4 3 =
... ... ... ... ... ... ...
Tabelul 1.3: Suma celor două numere ... destul de mici

Să alegem nişte numere un pic mai mari:

1 2 3 4 5 6 7

nefolosit semn cifrele numărului (pe 4 poziţii)


... ... ... ... ... ... ...
... ... 0 0 8 9 1 +
... ... 0 7 5 4 2
... ... =
... ... ... ... ... ... ...
Tabelul 1.4: Două numere ... destul de mari

Şi acum se face adunarea:


CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 11

1 2 3 4 5 6 7

nefolosit semn cifrele numărului (pe 4 poziţii)


... ... ... ... ... ... ...
... ... 0 0 8 9 1 +
... ... 0 7 5 4 2
... ... 0 8 4 3 3 =
... ... ... ... ... ... ...
Tabelul 1.5: Suma celor două numere ... destul de mari

Să alegem nişte numere şi mai mari:

1 2 3 4 5 6 7

nefolosit semn cifrele numărului (pe 4 poziţii)


... ... ... ... ... ... ...
... ... 0 6 8 9 1 +
... ... 0 7 5 4 2
... ... =
... ... ... ... ... ... ...
Tabelul 1.6: Două numere ... şi mai mari!

Şi acum se face adunarea:

1 2 3 4 5 6 7

nefolosit semn cifrele numărului (pe 4 poziţii)


... ... ... ... ... ... ...
... ... 0 6 8 9 1 +
... ... 0 7 5 4 2
... ... 1 4 4 3 3 =
... ... ... ... ... ... ...
Tabelul 1.7: Suma celor două numere ... prea mari!

Poziţia de semn este 1 şi asta ı̂nseamnă că numărul este negativ! Deci rezultatul este - 4433.
Iată cum apare ”ciudăţenia”!
Cam asta s-a ı̂ntâmplat ı̂n cele 3 programe ”ciudate”!

Trebuie să fim atenţi la câte ”pătrăţele” cerem calculatorului pentru numerele noastre!

1.3.5 Verificări cu “calculator.exe”


Pentru a ı̂nţelege, despre ce este vorba, vom folosi programul ”Calculator” (ı̂n figurile următoare
este cel din Windows 10):

Figura 1.6: Calculator Standard Figura 1.7: Selectare ”Programmer”


CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 12

Iniţial calculator.exe oferă interfaţa ”Standard” (Figura 1.6) dar aceasta se poate schimba
ı̂n interfaţa ”Programmer” (Figura 1.7). Aceasta din urmă ne va ajuta să ı̂nţelegem ceea ce s-a
ı̂ntâmplat ı̂n programele anterioare.
Să introducem valoarea 50 000, direct cu ajutorul tastaturii calculatorului/laptopului pe care
lucrăm sau, cu ajutorul mouse-lui, făcând ”click” pe cifrele corespunzătoare din tastatura oferită
de interfaţa programului ”calculator.exe”.

Figura 1.8: 50 000 in bazele 16, 10, 8 şi 2 Figura 1.9: QWORD = 64 biţi

În Figura 1.8, ı̂n partea stângă-sus, apar reprezentările numărului 50 000 ı̂n bazele 16 (”HEX”),
10 (”DEC”), 8 (”OCT”) şi 2 (”BIN”).
În baza 16 reprezentarea numărului 50 000 este C350.
În baza 8 reprezentarea numărului 50 000 este 141 520.
În baza 16 reprezentarea numărului 50 000 este 1100 0011 0101 0000.
Obs: Spaţiile folosite ı̂ntre grupuri de cifre sunt numai pentru a se citi mai uşor!
Un click pe ’cerculeţul galben’ (Bit Toggling keypad) din Figura 1.8 ne va conduce la Figura
43 44
1.9 ı̂n care avem reprezentarea pe biţi ı̂n format QWORD (4 * 16 = 64 biţi, QUAD WORD).
Biţii din această reprezentare sunt numerotaţi de la 0 la 63, de la dreapta spre stânga şi de jos
ı̂n sus. Pentru o ”vizibilitate” mai bună, apar notaţi numai biţii care sunt multiplii de 4.
Bitul 63 este bit de semn (0 pentru +, 1 pentru -).
Un click pe ’cerculeţul galben’ (’QWORD’) din Figura 1.9 ne va conduce la Figura 1.10 ı̂n care
avem reprezentarea pe biţi ı̂n format DWORD (2 * 16 = 32 biţi, DOUBLE WORD).

Figura 1.10: DWORD = 32 biţi Figura 1.11: WORD = 16 biţi

În Figura 1.10 biţii sunt numerotaţi de la 0 la 31, de la dreapta spre stânga şi de jos ı̂n sus.
43
https://en.wikipedia.org/wiki/Bit
44
https://en.wikipedia.org/wiki/Word_(computer_architecture)
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 13

Bitul 31 este bit de semn (ca de obicei, 0 pentru +, 1 pentru -).


Până acum zona cifrelor semnificative nu a fost afectată pentru că s-a renunţat la o zonă cu
0-uri şi bitul de semn a juns să fie pe o poziţie ı̂n care este cifra 0.
Astfel, valoarea ı̂n baza 10 a rămas 50 000.
Un click pe ’cerculeţul galben’ (’DWORD’) din Figura 1.10 ne va conduce la Figura 1.11 ı̂n
care avem reprezentarea pe biţi ı̂n format WORD (1 * 16 = 16 biţi, WORD).
În Figura 1.11 biţii sunt numerotaţi de la 0 la 15, de la dreapta spre stânga. Bitul 15 este bit
de semn (tot ca de obicei, 0 pentru +, 1 pentru -).
De această dată bitul de semn a ajuns pe o poziţie ı̂n care este cifra 1, deci valoarea numărului
este negativă. Mai precis, valoarea numărului ı̂n baza 10 este -15 536 (Figura 1.11).

Iată că a apărut şi numărul nostru “ciudat”.


Un click pe ’cerculeţul galben’ (’WORD’) din Figura 1.11 ne va conduce la Figura 1.12 ı̂n care
45
avem reprezentarea pe biţi ı̂n format BYTE (1 * 8 = 8 biţi, BYTE).

Figura 1.12: BYTE = 8 biţi Figura 1.13: QWORD ultimul byte din 50 000

În Figura 1.12 biţii sunt numerotaţi de la 0 la 7, de la dreapta spre stânga.


Bitul 7 este bit de semn (tot ca de obicei, 0 pentru +, 1 pentru -).
De această dată bitul de semn a ajuns pe o poziţie ı̂n care este cifra 0, deci valoarea numărului
este pozitivă. Zona la care s-a renunţat (biţii 8-15) conţinea şi cifre de 1 (ne aşteptăm să avem o
valoare mai mică ı̂n valoare absolută).
Mai precis, valoarea numărului ı̂n baza 10 este 80 (Figura 1.12).
Un click pe ’cerculeţul galben’ (’BYTE’) din Figura 1.12 ne va conduce la Figura 1.13 ı̂n care
avem din nou reprezentarea pe biţi ı̂n format QWORD (4 * 16 = 64 biţi, QUAD WORD) dar, de
această dată a numărului 80.
Un click pe o cifră va schimba valoarea existentă acolo astfel: 0 este schimbat ı̂n 1 iar 1 este
schimbat ı̂n 0.
Un click pe poziţia bitului 7, marcat cu cerculeţ roşu şi săgeată roşie ı̂n Figura 1.13, ne va
conduce la Figura 1.14 ı̂n care avem tot reprezentarea pe biţi ı̂n format QWORD dar ... valoarea
numărului a devenit 208.
45
https://en.wikipedia.org/wiki/Byte
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 14

Figura 1.14: Click pe 0 l-a schimbat ı̂n 1 Figura 1.15: DWORD valoare 208

Un click pe ’QWORD’ din Figura 1.14 ne va conduce la Figura 1.15. Prin acest ’click’ s-a
renunţat la jumătate din cifrele numărului (cele 32 din stânga; mai precis, s-a renunţat la cifrele
din zona poziţiilor 32-63).
Un click pe ’DWORD’ din Figura 1.15 ne va conduce la Figura 1.16. Prin acest ’click’ s-a
renunţat la jumătate din cifrele numărului (cele 16 din stânga; mai precis, s-a renunţat la cifrele
din zona poziţiilor 16-31).

Figura 1.16: WORD valoare 208 Figura 1.17: BYTE valoare -48

Un click pe ’WORD’ din Figura 1.16 ne va conduce la Figura 1.17. Prin acest ’click’ s-a
renunţat la jumătate din cifrele numărului (cele 8 din stânga; mai precis, s-a renunţat la cifrele
din zona poziţiilor 8-15).
În Figura 1.17 bitul de semn este pe poziţia 7 si are valoarea 1, deci valoarea numărului este
negativă. Mai precis, valoarea este -48.

1.3.6 Câteva valori ı̂n reprezentarea BYTE


Ne putem ”juca” cu programul calculator.exe construind numere ı̂n forma binară. Paşii sunt:

1. : calculator

2. Click pe aplicaţia oferită

3. Click pe (Open Navigation) şi selectare

4. Click pe (Bit toggling keypad)


CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 15

Ajungem astfel ı̂n situaţia din Figura 1.18. Un click pe ’QWORD’, apoi un click pe ’DWORD’
şi ı̂n final un click pe ’WORD’ ne duc situaţia din ı̂n Figura 1.19.

Figura 1.18: QWORD valoare 0 Figura 1.19: BYTE valoare 0

Un click pe poziţia 0 va schimba valoarea 0 ı̂n 1. Procedăm la fel pentru poziţiile 1-6 (fără
poziţia 7) şi obţinem Figura 1.20.

Figura 1.20: BYTE valoare 127 Figura 1.21: BYTE valoare -1

Bitul 7 este bit de semn şi valoarea este +127 (aceasta este valoarea maximă care se poate
obţine pe BYTE).
Dacă facem acum un click pe poziţia 7, vom schimba bitul de semn din 0 ı̂n 1 şi ... surpriza
este că valoarea se schimbă din 127 ı̂n -1 (vezi Figura 1.21). Altfel spus, valoarea -1 se reprezintă
ı̂n calculator ca ... ”1 peste tot”! Iar acest fapt nu se ı̂ntâmplă numai pentru BYTE ci şi pentru
WORD, DWORD şi QWORD.
Nu trebuie să ţinem minte acest fapt! Cred că vom ţine minte fără să vrem mai târziu, când
vom mai ı̂nvăţa câte ceva!
Să lăsăm acum 1 pe bitul de semn (poziţia 7) şi să schimbăm poziţiile 0-6 din 1 ı̂n 0. Adică, 1
pe poziţia de semn şi 0 ı̂n rest. Obţinem Figura 1.22.
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.3. PROGRAME CIUDATE 16

Figura 1.22: Click pe poziţia indicată Figura 1.23: -128 se transformă ı̂n -120

Valoarea -128 este cea mai mică valoare care se poate ı̂nregistra pe BYTE. Adică, ”1 pe poziţia
de semn şi ı̂n rest 0” generează cea mai mică valoare negativă care se poate ı̂nregistra pe BYTE.
La fel se ı̂ntâmplă şi pentru WORD, DWORD şi QWORD numai că vor fi alte valori.
Continuând joaca noastră, dacă facem click pe poziţia 3 vom schimba valoarea poziţiei din 0
ı̂n 1 iar valoare pe BYTE se schimbă din -128 ı̂n -120 (vezi Figura 1.23).
Poziţia 3 corespunde valorii 8=2*2*2 şi există o legătură uşor de observat ı̂ntre valoarea veche
-128, valoarea nouă -120 şi acest 8.

1.3.7 Reprezentarea numerelor 100 şi -100 pe BYTE

Figura 1.24: BYTE 100 Figura 1.25: BYTE -100

Din cele două figuri se poate observa uşor cum se poate trece de la 100 la -100 ı̂n reprezentarea
46
binară (ı̂n cod complementar ):

1. se parcurge reprezentarea lui 100 de la dreapta spre stânga până se ı̂ntâlneşte cifra 1 (marcat
cu o săgeată roşie ı̂n figurile de mai sus)
2. această zonă “10...0” (formată din un 1 urmat de mai multe 0-uri) se lasă neschimbată
3. cifrele care urmează, spre stânga zonei “10...0” se vor schimba, toate (cifra 0 se schimbă ı̂n
1, iar cifra 1 se schimbă ı̂n 0), inclusiv cifra de semn care este pe prima poziţie din stânga!
46
https://en.wikipedia.org/wiki/Signed_number_representations
https://en.wikipedia.org/wiki/Two%27s_complement
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.4. CODIFICAREA NUMERELOR ÎNTREGI
17

1.4 Codificarea numerelor ı̂ntregi


1.4.1 O reprezentare grafică pentru codul complementar
Pentru o imagine intuitivă interesantă, să plasăm numerele 0, 1, ..., 15 pe un cerc (aşa cum sunt
pe ceas numerele 1, 2, ..., 12).

Figura 1.26: Cercul reprezentării ı̂n cod complementar

Între cele două cercuri sunt trecute reprezentările binare (pe 4 biţi) corespuzatoare numerelor
0, 1, ..., 15 (care sunt scrise ı̂n exteriorul cercului mare).
Ne propunem să considerăm că avem numere pozitive ı̂n dreapta lui 0 şi numere negative ı̂n
stânga lui 0. Asta presupune să “rupem” cercul pe undeva (ı̂n partea de jos pare mai bine!). În
figura de mai sus cercul este “rupt” ı̂ntre numerele 7 şi 8 (din exteriorul cercului mare!).
Primul numâr din stânga lui 0 ar trebui să fie -1, al doilea număr din stânga lui 0 ar trebui să
fie -2, şi aşa mai departe! Pe figură, ı̂n dreptul lui 15 este scris cu roşu -1, ı̂n dreptul lui 14 este
scris cu roşu -2, şi aşa mai departe!
O “justificare” pentru tăietura cercului ı̂ntre 7 şi 8 ar putea fi următoare:

1. de la 0 la 15 sunt 16 numere
2. considerăm jumătate pozitive şi jumătate negative

3. 0 (zero) pare mai mult pozitiv decât negativ ... !


4. 0, 1, ...7 sunt cele 8 numere pozitive, deci vom face tăietura după 7 ...
5. restul ... sunt negative ... !

O ı̂ntrebare: Ce reprezintă “1000”?

Răspuns: “1000” reprezintă:

a) ca reprezentare binară ı̂n cod direct pe 4 biţi, reprezintă: +8

b) ca reprezentare binară ı̂n cod complementar pe 4 biţi, reprezintă: -8

c) ca reprezentare ı̂n baza 10, reprezintă: 1000 = “o mie”


CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.5. CODURI ASCII 18

1.4.2 Codificarea numerelor ı̂ntregi cu semn


Toate compilatoarele C++ folosesc reprezentarea numerelor ı̂ntregi cu semn ı̂n complement faţă
47
de 2 (sau cod complementar) . La unele calculatoare s-a folosit şi reprezentarea numerelor ı̂ntregi
48 49
ı̂n complement faţa de 1 dar aceasta se va abandona complet ı̂ncepând cu standardul C++20.
Pentru informaţii suplimentare:
https://en.wikipedia.org/wiki/Computer_number_format
https://en.wikipedia.org/wiki/Integer_(computer_science)

1.5 Coduri ASCII


50
ASCII = American Standard Code for Information Interchange

1.5.1 Caractere de control


Caracterele de control sunt cele din zona 0-31 şi 127. Ele permit trimiterea de comenzi către
imprimantă sau către calculatorul pe care lucrăm. Vom folosi foarte puţine dintre ele!

Dec Hex Abbreviation C++ Name


0 0 NULL NUL \0 Null
1 1 SOM SOH Start of Heading
2 2 EOA STX Start of Text
3 3 EOM ETX End of Text
4 4 EOT End of Transmission
5 5 WRU ENQ Enquiry
6 6 RU ACK Acknowledgement
7 7 BELL BEL \a Bell
8 8 FE0 BS \b kspace
9 9 HT/SK HT \t Horizontal Tab
10 0A LF \n Line Feed
11 0B VTAB VT \v Vertical Tab
12 0C FF \f Form Feed
13 0D CR \r Carriage Return
14 0E SO Shift Out
15 0F SI Shift In
16 10 DC0 DLE Data Link Escape
17 11 DC1 Device Control 1 (often XON)
18 12 DC2 Device Control 2
19 13 DC3 Device Control 3 (often XOFF)
20 14 DC4 Device Control 4
21 15 ERR NAK Negative Acknowledgement
22 16 SYNC SYN Synchronous Idle
23 17 LEM ETB End of Transmission Block
24 18 S0 CAN Cancel
25 19 S1 EM End of Medium
26 1A S2 SUB Substitute
27 1B S3 ESC \e Escape
28 1C S4 FS File Separator
29 1D S5 GS Group Separator
30 1E S6 RS Record Separator
31 1F S7 US Unit Separator
127 7F DEL Delete
Tabelul 1.8: Caractere de control

47
https://en.wikipedia.org/wiki/Two%27s_complement
48
https://en.wikipedia.org/wiki/Ones%27_complement
49
https://en.wikipedia.org/wiki/C%2B%2B20
50
https://en.wikipedia.org/wiki/ASCII
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.5. CODURI ASCII 19

1.5.2 Litere
Literele au coduri ı̂n zona 65-90 şi 97-122.

Litere mici
Literele mici au coduri ı̂n zona 97-122. În hexazecimal zona este 0x61-0x7A
Listing 1.5.1: ascii litere mici.cpp
#include<iostream>

using namespace std;

int main()
{
for(int i = 97; i <= 122; i++)
//cout<<i<<" : "<<(char)i<<’\n’;
cout<<(char)i<<’ ’;

return 0;
}

Litere mari
Literele mari au coduri ı̂n zona 65-90. În hexazecimal zona este 0x41-0x5A
Listing 1.5.2: ascii litere mari.cpp
#include<iostream>

using namespace std;

int main()
{
for(int i = 65; i <= 90; i++)
//cout<<i<<" : "<<(char)i<<’\n’;
cout<<(char)i<<’ ’;

return 0;
}

1.5.3 Cifre
Cifrele au coduri ı̂n zona 48-57. În hexazecimal zona este 0x30-0x39
Listing 1.5.3: ascii cifre.cpp
#include<iostream>

using namespace std;

int main()
{
for(int i = 48; i <= 57; i++)
//cout<<i<<" : "<<(char)i<<’\n’;
cout<<(char)i<<’ ’;

return 0;
}
CAPITOLUL 1. NOŢIUNI ELEMENTARE 1.5. CODURI ASCII 20

1.5.4 ’Whitespace’
51 52
Spaţiile albe (white-spaces ) sunt caracterele de separare ı̂ntre entităţile limbajului.
Acestea sunt:

cod cod secvenţa efect


hexazecimal zecimal ’escape’
0x09 9 ’\t’ Horizontal Tab
0x0A 10 ’\n’ Line Feed (New Line)
0x0B 11 ’\v’ Vertical Tab
0x0C 12 ’\f’ Form Feed (New
Page)
0x0D 13 ’\r’ Carriage Return
0x20 32 ’ ’ Space
Tabelul 1.9: ’white-space-characters’

53
Ele sun folosite pentru ’tabularea’ informaţiilor.

Listing 1.5.4: whitespace.cpp


#include<iostream>

using namespace std;

int main()
{
char ch;

for(ch=1; ch<=125; ch++)


if (isspace(ch))
{
cout<<(int)ch<<"\n";
//getchar();
}

return 0;
}

54
Pe ecran apar codurile (ı̂n baza 10) din tabela ASCII ale caracterelor white-space:

51
https://docs.microsoft.com/en-us/cpp/c-language/white-space-characters?view=msvc-160
52
C++ “sare” peste ele!
53
https://en.wikipedia.org/wiki/Tab_key
54
https://en.wikipedia.org/wiki/ASCII
Capitolul 2

Noţiuni elementare de programare


C++

Multe lucruri se pot face “ı̂n fel şi chip” şi la fel se ı̂ntâmplă şi cu programarea ı̂n C++!
Vom spune de multe ori că “se face aşa ...” deşi ... se poate face ı̂n multe alte feluri!
Pentru ı̂nceput (şi mai ales pentru ı̂ncepători!) este mai bine să folosim variante standard,
simple, uşor de ı̂nţeles şi de ţinut minte (asta se realizează prin muncă, prin repetare, aşa cum fac
şi sportivii la antrenamente!).

Listing 2.0.1: p00.cpp


// p00 ...

#include<iostream>

using namespace std;

int main()
{
int a, b, sum;

cin >> a;
cin >> b;

sum = a + b;
cout << sum << endl;

return 0;
}

Calculatorul ı̂nţelege numai codul să maşină iar programul anterior este scris ı̂n C++ care este
un limbaj de programare evoluat. Există programe speciale care transformă codul sursă scris de
programator ı̂n C++ (sau ı̂n alt limbaj de programare: Java, Pascal, ...) ı̂n cod maşină care poate
55
fi executat pe calculator.
În limbaj de asamblare acest cod arată astfel:

Listing 2.0.2: p00.asm


1 main:
2 push rbp
3 mov rbp, rsp
4 sub rsp, 16
5 lea rax, [rbp-8]
6 mov rsi, rax
7 mov edi, OFFSET FLAT:_ZSt3cin
8 call std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
9 lea rax, [rbp-12]
10 mov rsi, rax
11 mov edi, OFFSET FLAT:_ZSt3cin
12 call std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
13 mov edx, DWORD PTR [rbp-8]
14 mov eax, DWORD PTR [rbp-12]
15 add eax, edx
55
https://ro.wikipedia.org/wiki/Limbaj_de_asamblare

21
CAPITOLUL 2. PROGRAMARE C+++ 22

16 mov DWORD PTR [rbp-4], eax


17 mov eax, DWORD PTR [rbp-4]
18 mov esi, eax
19 mov edi, OFFSET FLAT:_ZSt4cout
20 call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
21 mov esi, OFFSET FLAT:
_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
22 mov rdi, rax
23 call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::
basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char,
std::char_traits<char> >&))
24 mov eax, 0
25 leave
26 ret
27 __static_initialization_and_destruction_0(int, int):
28 push rbp
29 mov rbp, rsp
30 sub rsp, 16
31 mov DWORD PTR [rbp-4], edi
32 mov DWORD PTR [rbp-8], esi
33 cmp DWORD PTR [rbp-4], 1
34 jne .L5
35 cmp DWORD PTR [rbp-8], 65535
36 jne .L5
37 mov edi, OFFSET FLAT:_ZStL8__ioinit
38 call std::ios_base::Init::Init() [complete object constructor]
39 mov edx, OFFSET FLAT:__dso_handle
40 mov esi, OFFSET FLAT:_ZStL8__ioinit
41 mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
42 call __cxa_atexit
43 .L5:
44 nop
45 leave
46 ret
47 _GLOBAL__sub_I_main:
48 push rbp
49 mov rbp, rsp
50 mov esi, 65535
51 mov edi, 1
52 call __static_initialization_and_destruction_0(int, int)
53 pop rbp
54 ret

Se poate folosi https://godbolt.org/ pentru a verifica acest exemplu sau pentru a vedea
alte coduri proprii.
Este util şi acest link: https://www.geeksforgeeks.org/convert-cc-code-to-a
ssembly-language/
Secvenţa

Listing 2.0.3: secvenţă de cod ’cpp’


int a, b, sum;

cin >> a;
cin >> b;

sum = a + b;
cout << sum << endl;

56
este reprezentată ı̂n cod maşină astfel:
00000 10011110
00001 11110100
00010 10011110
00011 11010100
00100 10111111
00101 00000000
Ca să fie tacâmul complet ... vreau să spun că, ı̂n studenţie, scriam programele pe cartele:
56
http://www.cplusplus.com/doc/tutorial/introduction/
CAPITOLUL 2. PROGRAMARE C+++ 23

Figura 2.1: Picture from: https://upload.wikimedia.org/wikipedia/commons/f/fe/Used_Punchca


rd_%285151286161%29.jpg

cu ajutorul unor maşini de scris care arătau cam aşa:

Figura 2.2: Picture from: https://twobithistory.org/2018/06/23/ibm-029-card-punch.html

Mai ı̂nainte, prin liceu (la cercul de informatică), foloseam bandă perforată care arăta cam aşa:

Figura 2.3: Picture from: https://www.pinterest.es/pin/125115695869461978/

Rezultatul programelor (şi codul sursă) ı̂l primeam pe hârtie de imprimanta care arăta cam aşa:
CAPITOLUL 2. PROGRAMARE C+++ 24

Figura 2.4: Picture from: https://www.pinterest.es/pin/178032991496703596/

Programele noastre aveau instrucţiuni pe mai multe cartele pe care le legam cu elastic, cam aşa:

Figura 2.5: (Picture from: https://upload.wikimedia.org/wikipedia/commons/2/26/Punched_ca


rd_program_deck.agr.jpg )
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 25

2.1 Structura unui program in C++


2.1.1 Şablon simplu
57
Un program ”gol” dar care arată care este structura unui program ı̂n C++.

Listing 2.1.1: p01 structura unui program C++


// p01 - structura unui program C++

int main()
{

return 0;
}

Figura 2.6: p01.cpp compilare (build)

57
exemplul clasic este: http://www.cplusplus.com/doc/tutorial/program_structure/
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 26

Figura 2.7: p01.cpp execuţie (run)

Figura 2.8: p01.cpp rezultat execuţie

Scrierea codului pe linii este complet liberă! Se pot scrie toate instrucţiunile (tot programul)
58 59
pe un singur rând! Totuşi, un cod lizibil este de preferat! Indentarea standard oferită de
Code::Blocks ajută la scrierea unui cod lizibil!
60
Fiecare editează şi programează cum vrea dar ... este util, din mai multe puncte de vedere,
61 62
să citim câte ceva despre “best practice” ı̂n domeniul programării ı̂n C++.
58
DEX - lizibil: care poate fi citit uşor
59
DEX - indentare: plasare a programelor pe linii, pentru scrierea cât mai clară a acestora
60
https://www.learncpp.com/cpp-tutorial/whitespace-and-basic-formatting/
61
https://en.wikipedia.org/wiki/Best_practice
62
http://web.mit.edu/6.s096/www/standards.html
https://www.learncpp.com/cpp-tutorial/why-functions-are-useful-and-how-to-use-them-eff
ectively/
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 27

Eu prefer să las setările implicite! Până la urmă ... ele au fost stabilite aşa de către specialişti

... cei care au făcut Code::Blocks!


Pentru formatul codului sursă, ı̂n Code::Blocks, setarea implicită pentru Bracket style este:
Allman (ANSI). Totuşi, mulţi preferă Bracket style: Java.

Figura 2.9: Style Allman (ANSI)

Figura 2.10: Style Java


CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 28

Pentru indentarea codului sursă, ı̂n Code::Blocks, setarea implicită pentru TAB este: 4. Mulţi
preferă TAB size in spaces: 2.

Figura 2.11: TAB size in spaces

Mulţi folosesc opţiunea Enable all common compiler warnings [-Wall]

Figura 2.12: Compiler warnings: [-Wall]


CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 29

De asemenea, trebuie să menţionez, mulţi folosesc opţiunea Optimize even more (for
speed) [-O2] dar ... eu cred că adevărata “viteză” se obţine cu ajutorul ... algoritmului!

Figura 2.13: Optimize even more (for speed) [-O2]

Câteva observaţii pentru primul program ı̂n C++ din figurile (2.6 - 2.8):

ˆ folderul de lucru este D:¯Programare C++ (Figura 2.8)

63
ˆ este butonul build pentru complilare (Figura 2.6)

ˆ este butonul run pentru execuţie (Figura 2.7)

Despre liniile de cod din p01.cpp: ce este cu roşu este “şablon” (nu le schimbăm niciodată!).

Linia 1: // comentariu despre program


Linia 2: ... aici vor apărea ..., variabile globale, funcţii, ...
Linia 3: int main()
Linia 4: {
Linia 5: ... aici vor apărea ..., variabile locale, instrucţiuni de executat, ...
Linia 6: return 0;
Linia 7: }
Linia 8: rând gol sau poate apărea ¯* ... alt comentariu ... *¯
63
http://www.cplusplus.com/doc/tutorial/introduction/
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 30

2.1.2 namespace std;

Listing 2.1.2: p02 namespace std;


// p02 ... namespace

using namespace std;

int main()
{

return 0;
}

Am adăugat o instrucţiune ı̂n şablon! Este acelaşi program care face ... “nimic” ... şi care era

funcţional şi fără using namespace std; dar ... este mai bine aşa! ... Oricum, are şi ea
rolul ei şi este utilă!

2.1.3 Pregătire suport pentru fişiere

Listing 2.1.3: p03 fstream şi iostream


// p03 ... fstream + iostream

#include<fstream>
#include<iostream>

using namespace std;

int main()
{

return 0;
}

Am mai adăugat două instrucţiuni ı̂n şablon! #include¡fstream¿ ne permite să accesăm
fişiere iar #include¡iostream¿ ne permite să accesăm tastatura şi ecranul.
64
In a C program, all lines that start with # are processed by preprocessor which is a special
program invoked by the compiler. In a very basic term, preprocessor takes a C program and
produces another C program without any #.
The C preprocessor or cpp is the macro preprocessor for the C, Objective-C and C++ computer
programming languages. The preprocessor provides the ability for the inclusion of header files,
macro expansions, conditional compilation, and line control.
In many C implementations, it is a separate program invoked by the compiler as the first part
of translation.
The language of preprocessor directives is only weakly related to the grammar of C, and so is
sometimes used to process other kinds of text files.[1]
The following are some interesting facts about preprocessors in C.
1) When we use include directive, the contents of included header file (after preprocessing)
are copied to the current file. Angular brackets ¡ and ¿ instruct the preprocessor to look in the
standard folder where all header files are held. Double quotes and instruct the preprocessor to
65
look into the current folder (current directory).
2) When we use define for a constant, the preprocessor produces a C program where the
66
defined constant is searched and matching tokens are replaced with the given expression.

2.1.4 Acces la fişiere

Listing 2.1.4: p04 ifstream şi ofstream


// p04 ... ifstream + ofstream
64
https://en.wikipedia.org/wiki/C_preprocessor
65
https://www.geeksforgeeks.org/interesting-facts-preprocessors-c/
66
https://www.geeksforgeeks.org/interesting-facts-preprocessors-c/
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 31

#include<fstream>
#include<iostream>

using namespace std;

ifstream fin("f04.in");
ofstream fout("f04.out");

int main()
{

return 0;
}

Am mai adăugat două instrucţiuni ı̂n şablon!


ifstream fin(”f04.in”); ne permite să citim/preluăm, din interiorul programului nostru .cpp,
date din fişierul f04.in care trebuie să fie ı̂n folderul de lucru. Numele fişierului pe hard disk este
f04.in dar ı̂n codul programului nostru numele este fin (aici, numele vine de la file input dar ...

fiecare este liber să-l boteze cum vrea!).


ofstream fin(”f04.out”); ne permite să scriem, din interiorul programului nostru .cpp, date
ı̂n fişierul f04.out care va fi creat ı̂n folderul de lucru. Numele fişierului pe hard disk va fi f04.out
dar ı̂n codul programului nostru numele este fout (aici, numele vine de la file output dar ...

fiecare este liber să-l boteze cum vrea!).


Cam acesta este şablonul final pe care ı̂l vom folosi!

2.1.5 Citire şi scriere folosind fişiere

Listing 2.1.5: p05 fin şi fout şi cout


// p05 ... fin + fout + cout

#include<fstream>
#include<iostream>

using namespace std;

ifstream fin("f05.in"); // Atentie: instructiunile se termina cu " ; "


ofstream fout("f05.out");

int main()
{
int n;

fin >> n;
fout << n*n << "\n";
cout << "nˆ2 = " << n*n << "\n";

return 0;
}

În acest program (p05.cpp) pe linia 13 instructiunea int n; declară variabila n. Este
aproape ca la matematică atunci când folosim o ’necunoscută’ n sau ceva notat cu n. Aici
’variabila este caracterizată de:
ˆ nume (aici este n)

ˆ tip (aici este int)

ˆ adresa din memorie (aici nu este afişată dar o putem afla!)

ˆ valoare (aici valoarea este preluată de la tastatură!)


CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 32

Figura 2.14: p05 cod sursă şi fereastra de execuţie

Figura 2.15: Fişierul cu datele de intrare: f05.in

Fişierul de intrare f05.in conţine 3 numere: 12345, 67 şi 89. Să observăm că ı̂ntre 12345 şi 67
67
există un spaţiu. Acesta are codul 20 ı̂n tabela de coduri ASCII. Să mai observăm că 89 este pe
rândul următor faţă de 67. Este normal să “bănuim” că ı̂ntre 67 şi 89 trebuie să fie o comandă
pentru a “trece pe rândul următor, la ı̂nceput de rând”! O vom vedea ... imediat!
Capul de citire din fişier este poziţionat la ı̂nceputul fişierului.

Figura 2.16: f05.in vizualizat cu WinHex

68
Numerele sunt despărţite de nişte “separatori” care se numesc “caractere whitespace”.
67
https://en.wikipedia.org/wiki/ASCII
68
https://en.cppreference.com/w/cpp/string/byte/isspace
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 33

Noi vom folosi frecvent:

space (0x20, ’ ’)
line feed (0x0a, ’\n’)
carriage return (0x0d, ’\r’)
horizontal tab (0x09, ’\t’)

Instrucţiunea int n; (rândul 13) generează alocarea spaţiului de memorie pentru variabila n şi
69
precizează (prin cuvântul cheie rezervat int) cum să fie prelucrat acel spaţiu de memorie.
Cât spaţiu se alocă pentru n? Depinde!
70
Depinde, ı̂n primul rând, de cuvântul scris ı̂n faţa lui n (se numeşte tipul lui n, iar ı̂n
71
exemplul nostru este int). Dar ... mai depinde şi de compilatorul de C++ pe care ı̂l folosim,
72 73
de standardul pe care ı̂l utilizează acest compilator şi de sistemul de operare pe care ı̂l avem
74
pe calculatorul nostru (de obicei Windows sau Linux dar ... unii au Mac ). Apropo de sistem
de operare, compilatoare, etc.: la Olimpiada Internaţională de Informatică din septembrie 2020
s-a folosit sistemul de operare Ubuntu 20.04 LTS, compilatoarele folosite au fost OpenJDK
75 76 77
11.0.7 şi Gcc 9.3.0 , iar interpretoarele folosite au fost Ruby 2.7 şi Python 3.8.2. (adică,
limbajele de programare folosite au fost Java, C++, Ruby şi Python).
Instrucţiunea fin >> n; (de pe rândul 15) citeşte din fişier, preia valoarea 12345, depozitează
această valoare la adresa lui n şi poziţionează capul de citire după 12345 şi ı̂nainte de 67.

Figura 2.17: fişierul cu datele de ieşire f05.out şi rezultatul afişat pe ecran

Instrucţiunea fout << n*n << "\n"; (de pe rândul 16) scrie ı̂n fout valoarea calculată a
expresiei n*n = 12345*12345 = 152399025 şi apoi execută comanda "\n" (trece pe rândul
următor, la ı̂nceput de rând).
Poate că aceste instrucţiuni par “grele” la ı̂nceput (pentru ı̂ncepători) dar ele sunt sugestive!
Simbolul >> sugerează o deplasare de la stânga spre dreapta!
Simbolul << sugerează o deplasare de la dreapta spre stânga!
fin >> n; ı̂nseamnă că se deplasează ceva de la fin spre n.
Ce se poate deplasa din fişierul fin? Evident ... ce este acolo!
fout << n*n << "\n"; ı̂nseamnă că se deplasează n*n şi "\n" spre fout.
69
https://en.cppreference.com/w/cpp/keyword
70
https://www.tutorialspoint.com/cplusplus/cpp_data_types.htm
71
http://www.cplusplus.com/doc/tutorial/introduction/
72
https://en.wikipedia.org/wiki/C%2B%2B
. https://isocpp.org/std/status
73
https://en.wikipedia.org/wiki/Operating_system
74
https://en.wikipedia.org/wiki/Macintosh
75
https://gcc.gnu.org/releases.html
76
https://en.wikipedia.org/wiki/Interpreter_(computing)
77
https://ioi2020.sg/contestantpc/
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 34

Primul care ajunge la fout este n*n şi apoi "\n". Expresia n*n se calculează şi numai
rezultatul ei “va pleca” spre fout. Apoi “va pleca” spre fout şi "\n" care este o comandă pentru
78
trecerea pe rândul următor.
cout, din linia de cod 17, este numele pe care trebuie să-l folosim atunci când ne referim la
ecranul calculatorului nostru.
linia de cod 17: cout << "nˆ2 = " << n*n << "\n";
cout << ... ı̂nseamnă că vine ceva spre ecran!
79
Ce este scris ı̂ntre ghilimele se scrie exact aşa cum este scris ı̂ntre ghilimele!
n*n este o expresie aritmetică, ea se evaluează şi numai rezultatul “se duce” spre cout.
80
"\n" este un caracter de control (generează trecerea pe rândul următor la ı̂nceput de rând).

Încet-ı̂ncet lucrurile devin mai clare si mai simple!


Când rezultatele sunt simple şi suntem ı̂n faza de scriere şi depanare a programului, de multe
ori vom scrie rezultatele şi pe ecran, nu numai ı̂n fişierul de ieşire!
Aşa am făcut şi acum, pe linia 17 din codul programului.
După ce obţinem un program corect, putem să ştergem instrucţiunea de afişare pe ecran sau,
şi mai bine (!), să o facem un simplu “comentariu” şi astfel ea nu mai este folosită ı̂n program.

2.1.6 Comentarii
Instrucţiunea “comentată” arată astfel:
// cout << "nˆ2 = " << n*n << "\n";
deci, cu două slash-uri ı̂n faţa ei.
Devine comentariu, până la sfârşitul liniei, tot ce este scris ı̂n dreapta celor două slash-uri.
81
Dacă trebuie să comentăm mai multe linii consecutive vom folosi acest model :

/*
...
*/

2.1.7 Reprezentarea internă a datelor ı̂n fişierele .txt


Pe hard disc fişierul f05.in nu este chiar aşa cum ı̂l arată Notepad ı̂n Figura 2.15 ci mai degrabă
aşa cum ı̂l arată WinHex ı̂n figura următoare:

Figura 2.18: fişierul f05.in ı̂n WinHex

82
30 este codul ASCII pentru 0.
78
https://www.w3schools.com/cpp/cpp_new_lines.asp
79
se numeşte “string”: https://www.w3schools.com/cpp/cpp_strings.asp
80
https://www.ascii-code.com/
81
https://www.w3schools.com/cpp/cpp_comments.asp
30 16 3 ˜ 16  0 48 10 , vezi: https://en.wikipedia.org/wiki/ASCII
82
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 35

31 este codul ASCII pentru 1.


...
39 este codul ASCII pentru 9.
20 este codul ASCII pentru “spaţiu”.
0D este codul ASCII pentru CR (comandă: trece cursorul la ı̂nceputul rândului curent).
0A este codul ASCII pentru LF (comandă: trece cursorul pe rândul următor pe verticală).

07 este codul ASCII pentru BEL (comandă: produce un beep - semnalizare sonoră ).
0D 0A este combinaţia care realizează trecere cursorului pe rândul următor la ı̂nceput de rând.
Asta se ı̂ntâmplă pe Windows! Pe Linux este altfel!
Fişierul f05.in, care este un fişier de tip txt, are dimensiunea de 12 octeţi: 10 caractere
obişnuite (9 cifre şi un spaţiu, codificate cu 31, 32, 33, 34, 35, 36, 37, 38, 39 şi 20) şi 2 caractere
care sunt de fapt comenzi (0D şi 0A).
Acelaşi fişier ı̂n Word ar avea 11,687 de octeţi. Sunt foarte multe comenzi inserate!

Programul nostru executabil p05.exe are 15,584,734 octeţi. Asta da dimensiune mare!

Figura 2.19: Dimensiuni fişiere

2.1.8 Două citiri din fişier


În programul următor este modificată puţin instrucţiunea de citire de pe rândul 15:

Listing 2.1.6: p05a citire dublă


// p05a ...

#include<fstream>
#include<iostream>

using namespace std;

ifstream fin("f05.in");
ofstream fout("f05.out");

int main()
{
int n;

fin >> n >> n;


fout << n*n << "\n";
cout << "nˆ2 = " << n*n << "\n";

return 0;
}

Fereastra de execuţie arată astfel:


CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 36

Figura 2.20: Execuţie p05a.exe

Instrucţiunea de citire fin >> n >> n; are următorul efect: din fişierul fin se trimite primul
număr către primul n şi al doilea număr către a doua variabilă, care este tot n (apare astfel o
suprascriere, o depozitare peste o valoare existentă; rămâne numai ultima valoare scrisă la locaţia
respectivă de memorie).
Fişierul fin (f05.in) avea următorul conţinut: 12345 67 89. Instrucţiunea de citire preia 12345
şi-l depozitează la adresa de memorie a lui n, apoi preia 67 şi-l depozitează tot la adresa de
memorie a lui n. După citire n va rămâne cu valoarea 67 ı̂nregistrată la adresa sa de memorie.
Pe ecran va apărea valoarea n*n = 67*67 = 4489.

2.1.9 Câteva explicaţii


Putem să ne imaginăm că memoria calculatorului este ca un rând de pătrăţele, ca ı̂n caietul de
83
matematică, şi că aceste pătrăţele sunt numerotate ı̂ncepând cu 0.
Dacă avem 4G de memorie, la calculatorul nostru, atunci ultima pătrăţică ar avea numărul de
ordine 4*1024*1024*1024-1 = 4,294,967,295 (sunt 4 miliarde şi ceva de pătrăţele!).
84 85
O astfel de pătrăţică se numeşte octet sau byte. şi conţine fiecare câte 8 cifre de 0 sau 1.
În funcţie de cum ne este mai uşor, putem să ne imaginăm că şirul de căsuţe este numerotat
de la stânga spre dreapta sau invers, de la dreapta spre stânga!
De asemenea, putem să ne imaginăm că este o coloană de pătrăţele numerotate de jos ı̂n sus,

sau invers, de sus ı̂n jos! Importantă este numai adresa!


Fiecare pătrăţică are 8 poziţii ı̂n care sunt numai cifre de 0 sau 1. Să nu uităm că “pătrăţica”

se numeşte “octet” sau “byte” şi ... aşa ı̂i vom spune mai departe!

Listing 2.1.7: p00x1.cpp


// p00x01 ...

#include<iostream>

using namespace std;

int a1, a2;


int a3=1, a4=1;

int main()
{
int b1, b2;
int b3=1, b4=1;

cout << "&a1 = " << &a1 << "\n";


cout << "&a2 = " << &a2 << "\n";
cout << "&a3 = " << &a3 << "\n";
cout << "&a4 = " << &a4 << "\n\n";

cout << "&b1 = " << &b1 << "\n";


cout << "&b2 = " << &b2 << "\n";
cout << "&b3 = " << &b3 << "\n";
cout << "&b4 = " << &b4 << "\n";

return 0;
}
/*
83
https://en.wikipedia.org/wiki/Byte_addressing
84
https://en.wikipedia.org/wiki/Byte
85
https://en.wikipedia.org/wiki/Bit
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 37

&a1 = 0x51e030 --> 5,365,808


&a2 = 0x51e034 --> 5,365,812
&a3 = 0x4ed010 --> 5,165,072
&a4 = 0x4ed014 --> 5,165,076

&b1 = 0x2accfe1c --> 718,077,468


&b2 = 0x2accfe18 --> 718,077,464
&b3 = 0x2accfe14 --> 718,077,460
&b4 = 0x2accfe10 --> 718,077,456

Process returned 0 (0x0) execution time : 0.154 s


Press any key to continue.
*/

86
Când se declară o variabilă, “ţineva - nu spui ţine ... persoană ı̂nsemnată ...” alocă o zonă
de memorie pentru acea variabilă, la o anumită adresă din memorie. Astfel, “toată lumea” ştie la

ce adresă este acea ... variabilă!


Sau, putem să ne imaginăm că este ca o bandă lungă, pe care pot ı̂nregistra, diverse persoane,
câte o fotografie! Când se declară o variabilă, “ţineva - nu spui ţine ... persoană ı̂nsemnată ...”
alocă o zonă de bandă pentru acea variabilă (pentru acea persoană care poate să depoziteze acolo
numai o fotografie), la o anumită adresă de pe bandă (din memorie). Astfel, “toată lumea” ştie

la ce adresă este acea ... variabilă! Dacă persoana vizualizează spaţiul alocat fără să fi pus
fotografia lui acolo, va vedea o fotografie veche a altei persoane (vechiul conţinut al memoriei).
Este important să folosim zona alocată numai după ce am depozitat noi ceva acolo!

Figura 2.21: Alocarea spaţiului de memorie

În Figura 2.21 este considerată situaţia ı̂n care ı̂n zona alocată pentru n există ı̂nregistrat 555.
Dacă afişăm n, fără să depozităm noi ceva acolo, pe ecran va apărea 555. După citirea din fişier a
primului număr, dacă am afişa n ar apărea 12345. Dacă am citi din nou din fişier şi am depozita
tot ı̂n n, la afişare ar apărea 67 (pntru că s-a suprascris 67 peste 12345 la adresa lui).
Instrucţiunea fin >> n; (de pe rândul 15) citeşte primul număr din fin şi depozitează valoarea
citită ı̂n spatiul de memorie alocat variabilei n. Se spune pe scurt: citeşte n din fin. În spaţiul de
memerie alocat lui n se află acum valoarea 12345.
Capul de citire din fişierul fin se află acum după 12345 şi ı̂nainte de 67. (Figura 2.22)

Figura 2.22: Poziţia capului de citire după prima citire

86
https://ro.wikisource.org/wiki/O_scrisoare_pierdut%C4%83
CAPITOLUL 2. PROGRAMARE C+++ 2.1. STRUCTURA UNUI PROGRAM IN C++ 38

După a doua citire, capul de citire din fişierul fin se află după 67 şi ı̂nainte de 89. (Figura 2.23)

Figura 2.23: Poziţia capului de citire după a doua citire

După o eventuală a treia citire, capul de citire din fişierul fin s-ar fi aflat după 89. (Figura 2.24)

Figura 2.24: Poziţia capului de citire după a treia citire

2.1.10 Variabilă neiniţializată


Să presupunem că ... am uitat să citim n din fişier!
Ştim deja că instrucţiunea int n; (de pe rândul 13) alocă spaţiu de memorie pentru variabila
n, dar acolo, ı̂n acel spaţiu de memorie, poate fi deja ceva ı̂nregistrat (rămăşiţe mai vechi de la
alte programe, care nu au fost “curăţate”). O instrucţiune de afişare a variabilei n va afişa ce este

la adresa de memorie a lui n, iar acolo ... poate fi orice “rămăşiţă”!

Listing 2.1.8: p05b variabilă neiniţializată


// p05b ...

#include<fstream>
#include<iostream>

using namespace std;

ifstream fin("f05.in");
ofstream fout("f05.out");

int main()
{
int n;

fout << n*n << "\n";


cout << "nˆ2 = " << n*n << "\n";

return 0;
}

De aceea e mai bine ca, după declararea variabilei (care alocă spaţiu pentru variabilă), să
suprascriem ı̂n acea zonă de memorie o valoare care să fie “valoarea noastră” (să nu mai fie o
“rămăşiţă” de valoare despre care nu ştim “cât” este!).
CAPITOLUL 2. PROGRAMARE C+++ 2.2. ALFABETUL LIMBAJULUI 39

În cazul nostru, fereastra de execuţie arată aşa:

Figura 2.25: Execuţie p05a.exe

Înţelegem că la adresa de memorie a lui n a fost 0.


Variabilele globale (cele care sunt declarate ı̂n exteriorul oricărei funcţii) sunt iniţializate cu
0 de către compilator.
Variabilele locale (cele care sunt declarate ı̂n interiorul unei funcţii) nu sunt iniţializate de
către compilator.
Cel mai bine este să le iniţializăm noi pe toate, şi pe cele globale şi pe cele locale, cu ce valoare

vrem noi!

2.1.11 Alegerea identificatorilor (numelor)


Fiecare este liber să-şi “boteze” variabilele cum vrea!
De preferat este să alegem nume care să fie sugestive şi să ne ajute atunci când programul
nostru nu ne-a “ieşit” şi ne chinuim să-l depanăm!
În programul următor am schimbat numele fişierelor de intrare şi de ieşire fin şi fout ı̂n finput
şi foutput.

Listing 2.1.9: p06 nume pentru variabile


// p06 ... numele variabilelor

#include <fstream> // ifstream, ofstream


#include <iostream> // cout ... !!!

using namespace std;

ifstream finput("f06.in");
ofstream foutput("f06.out");

int main()
{
int n;

finput >> n;

cout << "patratul lui n este: " << n*n <<"\n";


foutput << "patratul lui n este: " << n*n <<"\n";

return 0;
}

2.2 Alfabetul limbajului


O mică observaţie: mulţi vor spune că “ne dăm mari” spunând că folosim limbajul de programare
C++ şi nu spunem că folosim mai mult limbajul de programare C dar ... deşi, ı̂ntr-un fel, au

dreptate (!) ... noi vom merge mai departe spunând aşa!
Am văzut, ı̂n exemplele anterioare, că ı̂n C++ se folosesc litere, cifre, semne de punctuaţie,
87
simbolul adunării “+”, etc. Acestea se numesc caractere.
Totalitatea caracterelor permise ı̂n limbaj se numeşte “alfabetul” limbajului.
87
caracter: 1. Semn săpat (ı̂n piatră, ı̂n metal) sau scris (https://dexonline.ro/definitie/caracter)
CAPITOLUL 2. PROGRAMARE C+++ 2.2. ALFABETUL LIMBAJULUI 40

88
În acest alfabet sunt 96 de caractere (standard pentru limbajul C) :
litere mici: a b c d e f g h i j k l m n o p q r s t u v w x y z
litere mari: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
cifre: 0 1 2 3 4 5 6 7 8 9
caractere speciale:
_ { } [ ] # ( ) < > % : ; . ? * + - / ˆ & | ˜ ! = , \ " ’

Observaţie: 26 + 26 + 10 + 29 = 91
Parcă lipsesc 5 caractere!
89
Dar ... iată-le (White Spaces) :
1. caracterul HT (horizontal tab) \t cu codul ASCII 9 (0x09) ***
2. caracterul LF (new line / line feed) \n cu codul ASCII 10 (0x0A) ***
3. caracterul VT (vertical tab) \v cu codul ASCII 11 (0x0B) ***
4. caracterul FF (form feed / new page) \f cu codul ASCII 12 (0x0C) ***
5. caracterul CR (carriage return) \r cu codul ASCII 13 (0x0D)
6. caracterul space cu codul ASCII 32 (0x20) ***

90
Sunt chiar 6 caractere!
În
https://publications.gbdirect.co.uk/c_book/chapter2/alphabet_of_c.html
şi ”Sailing Through C: A Complete C Programming Guide” - De Farheen Siddiqui,
erau numai 5.

2.2.1 Un program util


Iată un mic program care ne permite să vedem despre ce este vorba:

Listing 2.2.1: ascii comentat.cpp


#include<iostream>
#include<iomanip>

using namespace std;

int main()
{
int ch;
int nrv;

// ------------------------------------

cout<<"iscntrl: \n";
nrv=1;
for(ch=0; ch<=255; ch++)
if (iscntrl(ch))
{
cout<<setw(3)<<(int)ch<<" ";
if((nrv%10)==0) cout<<"\n";
++nrv;
//getchar();
}
cout<<"\n\n";

// ------------------------------------

cout<<"isspace: ";
for(ch=0; ch<=255; ch++)
if (isspace(ch))
{
cout<<(int)ch<<" ";
//getchar();
88
https://publications.gbdirect.co.uk/c_book/chapter2/alphabet_of_c.html
89
https://www.techiedelight.com/remove-whitespaces-string-cpp/
90
https://www.techiedelight.com/remove-whitespaces-string-cpp/
CAPITOLUL 2. PROGRAMARE C+++ 2.2. ALFABETUL LIMBAJULUI 41

}
cout<<"\n\n";

// ------------------------------------

cout<<"isblank: ";
for(ch=0; ch<=255; ch++)
if (isblank(ch))
{
cout<<(int)ch<<" ";
//getchar();
}
cout<<"\n\n";

// ------------------------------------

cout<<"isprint: \n";
for(ch=0; ch<=255; ch++)
if (isprint(ch))
{
cout<<setw(3)<<(int)ch<<" ";
if((ch%16)==15) cout<<"\n";
//getchar();
}
cout<<"\n\n";

for(ch=0; ch<=255; ch++)


if (isprint(ch))
{
cout<<setw(3)<<(char)ch<<" ";
if((ch%16)==15) cout<<"\n";
//getchar();
}
cout<<"\n\n";
// ------------------------------------

cout<<"isalnum: \n";
nrv=1;
for(ch=0; ch<=255; ch++)
if (isalnum(ch))
{
cout<<setw(3)<<(int)ch<<" ";
if((nrv%16)==0) cout<<"\n";
++nrv;
//getchar();
}
cout<<"\n\n";

nrv=1;
for(ch=0; ch<=255; ch++)
if (isalnum(ch))
{
cout<<setw(3)<<(char)ch<<" ";
if((nrv%16)==0) cout<<"\n";
++nrv;
//getchar();
}
cout<<"\n\n";

// ------------------------------------

cout<<"Simboluri ASCII: \n";


nrv=1;
for(ch=0; ch<=127; ch++)
if (!iscntrl(ch) &&
!isspace(ch) &&
!isblank(ch) &&
//isprint(ch) &&
!isalnum(ch))
{
cout<<setw(3)<<(char)ch<<" ";
if((nrv%16)==0) cout<<"\n";
++nrv;
//getchar();
}
cout<<"\n\n";
CAPITOLUL 2. PROGRAMARE C+++ 2.2. ALFABETUL LIMBAJULUI 42

// ------------------------------------

cout<<"Simboluri ASCII extins: \n";


nrv=1;
for(ch=128; ch<=255; ch++)
if (!iscntrl(ch) &&
!isspace(ch) &&
!isblank(ch) &&
//isprint(ch) &&
!isalnum(ch))
{
cout<<setw(3)<<(char)ch<<" ";
if((nrv%16)==0) cout<<"\n";
++nrv;
//getchar();
}
cout<<"\n\n";

return 0;
}

2.2.2 Coduri de control şi spaţii

Figura 2.26: isctrl+isspace+isblank

Caracterele de control sunt ı̂n zona codurilor 0-31 (0x00-0x1F ı̂n hexazecimal).
91
Codul 127 (0x7F ı̂n hexazecimal) este pentru DEL.
Codurile 9 (HT ’horizontal tabulation’) şi 32 (’space’) sunt considerate şi ’space’ şi ’blank’.

2.2.3 Caracterele printabile

Figura 2.27: isprint

Caracterele printabile sunt cele din zona 32-126 (0x20-0x7E).


Codul 127 (care pare că lipseşte de aici!) este DEL ... ı̂n zona caracterelor de control!.
91
https://en.wikipedia.org/wiki/ASCII
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 43

2.2.4 Codurile alfanumerice

Figura 2.28: isalnum

Codurile alfanumerice conţin codurile cifrelor şi codurile literelor (mari şi mici).

2.2.5 Simboluri ASCII


Ce a mai rămas din zona caracterelor ASCII, zona 0-127 (00-7F ı̂n hexa), sunt simbolurile:

Figura 2.29: simboluri ASCII

Nu sunt trecute aici simbolurile din zona codurilor ASCII extins, zona 128-255 (80-FF ı̂n hexa).

2.3 Alocarea variabilelor ı̂n memorie


2.3.1 Puţină istorie!
http://ioi.te.lv/history.shtml
Calculatoarelor folosite la olimpiade sunt mult mai puternice decât erau ı̂nainte!
92
La EJOI2018 s-au folosit calculatoare cu următoarea configuraţie:
Hardware:
ˆ Laptop HP 250 G5

ˆ CPU: Intel Core i5-6200U 2.3GHz

ˆ RAM: 8GB DDR4

ˆ Disk: 256 GB SSD

93
Configuraţiile calculatoarelor care s-au folosit la I.O.I. cu mai mulţi ani ı̂n urmă au fost:
94
La IOI2000 s-au folosit calculatoare cu următoarea configuraţie:
’Competition Equipment’:
ˆ ”The competition computers will be PCs, each with an Intel Pentium III CPU running at

366MHz, 32MB RAM, a standard US keyboard, a mouse and a color monitor.”

95
La IOI1995 s-au folosit calculatoare cu următoarea configuraţie:
’Competition Equipment’:
ˆ ”The computer is a Tulip Vision Line with a 75 MHz Pentium processor, a standard U.S.

keyboard, and a mouse. It runs MS-DOS 6.22 (U.S. version). ”


92
https://ejoi2018.org/environment
93
https://en.wikipedia.org/wiki/International_Olympiad_in_Informatics
94
http://ioi.te.lv/locations/ioi00/contest/c-rules.txt
95
http://olympiads.win.tue.nl/ioi95/contest/c-rules.html
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 44

96
La IOI1994 s-au folosit calculatoare cu următoarea configuraţie:
’Competition Equipment’:
ˆ ”The computer is an AST with a 33 MHz Intel 486 processor running MS-DOS”

97
La IOI1993 s-au folosit calculatoare cu următoarea configuraţie:
’Tools’:
ˆ ”For the Olympiad each participant will be provided with a personal computer with a USA

style, 101-key, QWERTY keyboard and the necessary software. MS-DOS V. 5.0 will be
provided.”

Din 1989 (de când au ı̂nceput olimpiadele de informatică) până acum, puterea calculatoarelor
personale folosite la olimpiade a crescut de peste 1 000 de ori, atât din punct de vedere al vitezei
de calcul cât şi din punct de vedere al memoriei folosite pentru stocarea informaţiilor necesare
rezolvării problemelor date ı̂n concurs!
În general, la olimpiadele de informatică, s-au folosit calculatoare personale cu procesoare
destul de noi! Caracteristicile acestora sunt:
http://www.cpu-collection.de/?tn=1&l0=cl&l1=80186/188&l2=Intel
https://en.wikipedia.org/wiki/List_of_Intel_processors#80186
https://en.wikipedia.org/wiki/Intel_80286

2.3.2 ’Big endian’ sau ’Little endian’

Listing 2.3.1: Big or Little endian.cpp


#include<iostream>

using namespace std;

int main()
{
unsigned int i1 = 0x87654321; // se aloca 4 octeti
unsigned int i2 = 0x21436587; // bitii din octet nu se inverseaza

char* c1 = (char*) &i1; // ce este la primul octet al lui i1


char* c2 = (char*) &i2; // ce este la primul octet al lui i2

if(*c1 == 0x10)
{
//printf("calculatorul tau este ... ’Little endian’ ... !!!\n\n");
cout<<"calculatorul tau este ... ’Little endian’ ... !!!\n\n";
}
else
{
//printf("calculatorul tau este ... ’Big endian’ ... !!!\n\n");
cout<<"calculatorul tau este ... ’Big endian’ ... !!!\n\n";
}

cout << "i1 = 0x87654321; *(c1+0) --> "


<< (int)((unsigned char)(*(c1+0))) << " "
<< std::hex << " hex: " << (int)((unsigned char)(*(c1+0))) << "\n";

cout<< std::dec;
cout << "i1 = 0x87654321; *(c1+3) --> "
<< (int)((unsigned char)(*(c1+3))) << " "
<< std::hex << " hex: " << (int)((unsigned char)(*(c1+3))) << "\n";

cout<<"\n";

cout<< std::dec;
cout << "i2 = 0x21436587; *(c2+0) --> "
<< (int)((unsigned char)(*(c2+0))) << " "
<< std::hex << " hex: " << (int)((unsigned char)(*(c2+0))) << "\n";

cout<< std::dec;
cout << "i2 = 0x21436587; *(c2+3) --> "
<< (int)((unsigned char)(*(c2+3))) << " "
96
http://olympiads.win.tue.nl/ioi/ioi94/contest/
97
http://ioi.te.lv/locations/ioi93/rules.txt
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 45

<< std::hex << " hex: " << (int)((unsigned char)(*(c2+3))) << "\n";

return 0;
}

Pe calculatorul meu ... apare:

Figura 2.30: Big or Little endian

Observaţie:
ˆ 21 ı̂n baza 16 este de fapt 33 ı̂n baza 10 pentru că 2*16+1=33
ˆ 87 ı̂n baza 16 este de fapt 135 ı̂n baza 10 pentru că 8*16+7=135

Ce ı̂nseamnă asta?
Povestea este cam aşa: să presupunem că cineva cere (de la primărie!) un teren pentru 4 case
(’casa1’, ’casa2’, ’casa3’ şi ’casa4’). Primăria ı̂i dă terenul solicitat pe strada ’Memorie’ la numerele
consecutive 123, 124, 125 şi 126 (deci, grupul de case ı̂ncepe la numărul 123 şi se termină la
numărul 126). Trebuie spus că strada ’Memorie’ are case numai pe o parte a străzii ... deci ... pe
acea parte sunt şi numerele cu soţ şi numerele fără soţ! Casele vor fi construite una după alta.
Trebuie să mai ştim ceva important: casele au valori diferite! Cea mai valoroasă este ’casa4’
şi valorile lor descresc spre ’casa1’. Un exemplu fictiv este: ’casa4’ valorează 10 000, ’casa3’
valorează 1 000, ’casa2’ valorează 100 şi ’casa1’ valorează 10.
Iar ı̂ntrebarea este: la ce număr se află cea mai valoroasă casa? La 123 sau la 126?

Figura 2.31: Big si Little endian

2.3.3 Afişare ı̂n ’hexa’


Uneori este util să vedem reprezentarea numerelor ı̂n ’hexa’. Iată un exemplu:

Listing 2.3.2: afisare hexa.cpp


#include<iostream> // cout
#include <iomanip> // setw

using namespace std;

int main()
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 46

{
unsigned int i = 0x04030201; // se aloca 4 octeti

cout<< "1. i = "<<setw(8)<<i<<"\n";


cout<< "2. i = " << setw(8)<< std::hex << i << ’\n’;
cout<< "3. i = "<<setw(8)<<i<<"\n";
cout << std::dec; // revenire la "decimal"
cout<< "4. i = "<< setw(8)<<i<<"\n";

return 0;
}

Pe R8 (rândul 8 din programul sursă) 0x ı̂nseamnă că urmează scrierea numărului ı̂n ‘hexa’
(baza 16).
Pe R10 setw(8) ı̂nseamnă că urmează afişarea numărului pe 8 poziţii (aliniat la dreapta!).
Pe R11 std::hex ı̂nseamnă că urmează afişarea numărului ı̂n ’hexa’. Afişările următoare se
vor face tot ı̂n ’hexa’ până când se va seta afişarea ı̂n ’dec’ (’decimal’, ı̂n baza 10) aşa cum este
făcut ı̂n R13.
R14 este o afişare de verificare că mai departe se vor face afişări ı̂n maza 10.

Figura 2.32: Afişare hexa

2.3.4 Afişare ı̂n ’binar’


Uneori este util să vedem reprezentarea numerelor ı̂n ’binar’. Iată un exemplu:

Listing 2.3.3: afisare binar.cpp


#include<iostream> // cout
#include <bitset> // bitset

using namespace std;

int main()
{
unsigned int x = 1234567890; // se aloca 4 octeti

cout<< "1. x = "<<x<<"\n";


cout<< "2. x = "<< std::bitset<32>(x)<<"\n";

cout<< "3. x = ";


for (unsigned int i = 1 << 31; i>0; i = i / 2)
{
if((x&i) > 0) // trebuie intre paranteze x&i
cout<<"1";
else
cout<<"0";
}
cout<<"\n";

return 0;
}

Pe ecran apare:
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 47

Figura 2.33: Afişare binar

Putem folosi ’bitset’ sau putem calcula noi!


Pe R11 este folosit ’bitset’ şi apare reprezentarea binară ı̂n 2..
Pe rândurile R14-R20 sunt calculaţi şi afişati (ı̂n 3.) biţii din reprezentarea lui x.
Reprezentările afişate la 2. şi 3. sunt identice! Pe R16 trebuie un pic de prudenţă din cauza
98
priorităţii operatorilor & şi ¿. O scriere de forma if(x&i > 0) ar fi generat rezultate greşite!
(& are prioritate 7 iar ¿ are prioritate 9 ... şi din această cauză expresia este evaluată astfel:
x & (i > 0). Nouă ne trebuie această evaluare: (x & i) > 0)). Dacă nu suntem siguri ...
mai bine punem paranteze!
Oricum ... ı̂n memoria calculatorului x este reprezentat ı̂n binar! Deci, problema noastră este
... să vedem dacă pe poziţia unei cifre (binare!) este 0 sau 1. Şi nu numai pentru o poziţie ci
pentru toate poziţiile cifrelor lui x (din programul nostru!).
Acest lucru ı̂l putem face ’plimbându-ne’ cu un 1 prin x.

Figura 2.34: Plimbare prin biţi!

L1: poziţia cifrelor binare ı̂n x


L2: cifrele binare din x
L3: numărul 1 (spre stânga lui sunt numai cifre 0 dar ... nu sunt scrise aici ... pentru a se
vedea mai uşor!). Acest 1 este deplasat spre stânga cu 31 de poziţii şi astfel ajunge pe poziţia 31.
L4: numărul 1 a ajuns pe poziţia 31 (spre dreapta lui sunt numai cifre 0 dar ... nu sunt scrise
31
aici ... pentru a se vedea mai uşor!). Valoarea numărului scris pe linia L4 este 2 dar acest lucru
nu ne este util acum!
L5: prin ı̂mpărţire cu 2, cifra 1 se deplasează spre dreapta cu o poziţie. Să presupunem că,
prin câteva ı̂mparţiri prin 2, a ajuns pe poziţia 23. Pe poziţia 23 este 1 dar, chiar dacă nu apar ı̂n
desen, spre stânga şi spre dreapta sunt cifre 0. Operatorul & se numeşte ”şi pe biţi” şi funcţionează
ca ı̂nmulţirea obişnuită: 0&0 = 0 şi 0&1=0 şi 1&1=1. Dacă vom calcula L5&x se va obţine ca
23
rezultat L5 (pe poziţia 23 este 1 şi ı̂n x şi ı̂n L5) şi valoarea rezultată este 2 care este sigur ¿
23
0 (aici 2 chiar este destul de mare!). Deci, dacă rezultatul operaţiei L5&x este un număr ¿ 0
atunci x are pe poziţia 23 cifra 1 (nu 0).
L6: Să presupunem că, prin alte câteva ı̂mparţiri prin 2, 1 a ajuns pe poziţia 13. Pe poziţia
13 este 1 dar, chiar dacă nu apar ı̂n desen, spre stânga şi spre dreapta sunt cifre 0. Dacă vom
calcula L6&x se va obţine ca rezultat o linie plină cu 0 (pe poziţia 13: este 0 ı̂n x şi 1 ı̂n L6 dar
... 0&1=0) şi valoarea rezultată este = 0. Deci, dacă rezultatul operaţiei L6&x este = 0 atunci x
are pe poziţia 13 cifra 0 (nu 1).

2.3.5 Alocarea variabilelor ı̂n spaţiul de memorie

https://en.wikipedia.org/wiki/DOS_memory_management
98
https://en.cppreference.com/w/cpp/language/operator_precedence
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 48

https://en.wikipedia.org/wiki/Conventional_memory
https://en.wikipedia.org/wiki/High_memory_area
https://en.wikipedia.org/wiki/Upper_memory_area

Listing 2.3.4: adrese in memorie.cpp


#include<iostream>

using namespace std;

int x1g=12;
int x2g=23;
int x3g=34;

void functie()
{
int xx1f=1234;
int xx2f=2345;
int xx3f=3456;

cout<<"In zona ’FUNCTIE’ adresele se aloca consecutiv descrescator\n";


cout << "&xx1f = " << &xx1f << " xx1f = " << xx1f <<"\n";
cout << "&xx2f = " << &xx2f << " xx2f = " << xx2f<<"\n";
cout << "&xx23 = " << &xx3f << " xx3f = " << xx3f<<"\n";
}

void stiva1(int xx1m, int xx2m, int xx3m) // prin valoare


{
cout<<"In zona ’STIVA1’ variabilele ’copie’ se aloca crescator\n";
cout << "&xx1m = " << &xx1m << " xx1m = " << xx1m << "\n";
cout << "&xx2m = " << &xx2m << " xx2m = " << xx2m << "\n";
cout << "&xx3m = " << &xx3m << " xx3m = " << xx3m << "\n";
}

void stiva2(int xx1g, int xx2g, int xx3g) // prin valoare


{
cout<<"In zona ’STIVA2’ variabilele ’copie’ se aloca crescator\n";
cout << "&xx1g = " << &xx1g << " xx1g = " << xx1g<<"\n";
cout << "&xx2g = " << &xx2g << " xx2g = " << xx2g<<"\n";
cout << "&xx3g = " << &xx3g << " xx3g = " << xx3g<<"\n";
}

void stiva3(int &xx1m, int &xx2m, int &xx3m) // prin adresa


{
cout<<"In zona ’STIVA3’ adresele sunt cele reale din zona MAIN\n";
cout << "&xx1m = " << &xx1m << " xx1m = " << xx1m <<"\n";
cout << "&xx2m = " << &xx2m << " xx2m = " << xx2m <<"\n";
cout << "&xx3m = " << &xx3m << " xx3m = " << xx3m <<"\n";
}

void stiva4(int &xx1g, int &xx2g, int &xx3g) // prin adresa


{
cout<<"In zona ’STIVA4’ adresele sunt cele reale din zona GLOBALA\n";
cout << "&xx1g = " << &xx1g << " xx1g = " << xx1g <<"\n";
cout << "&xx2g = " << &xx2g << " xx2g = " << xx2g <<"\n";
cout << "&xx3g = " << &xx3g << " xx3g = " << xx3g <<"\n";
}

int main()
{
int x1m=123;
int x2m=234;
int x3m=345;

cout<<"In zona ’MAIN’ adresele se aloca consecutiv descrescator\n";


cout<< "&x1m = " << &x1m <<" x1m = " << x1m <<"\n";
cout<< "&x2m = " << &x2m <<" x2m = " << x2m <<"\n";
cout<< "&x3m = " << &x3m <<" x3m = " << x3m <<"\n";

cout<<"\n";

cout<<"In zona ’GLOBALA’ adresele se aloca consecutiv crescator\n";


cout << "&x1g = " << &x1g << " x1g = " << x1g <<"\n";
cout << "&x2g = " << &x2g << " x2g = " << x2g <<"\n";
cout << "&x3g = " << &x3g << " x3g = " << x3g <<"\n";
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 49

cout<<"\n";

functie(); cout<<"\n";

stiva1(x1m,x2m,x3m); cout<<"\n";
stiva2(x1g,x2g,x3g); cout<<"\n";
stiva3(x1m,x2m,x3m); cout<<"\n";
stiva4(x1g,x2g,x3g);

return 0;
}

Pe ecran apare:
In zona ’MAIN’ adresele se aloca consecutiv descrescator
&x1m = 0x61fdec x1m = 123
&x2m = 0x61fde8 x2m = 234
&x3m = 0x61fde4 x3m = 345

In zona ’GLOBALA’ adresele se aloca consecutiv crescator


&x1g = 0x404020 x1g = 12
&x2g = 0x404024 x2g = 23
&x3g = 0x404028 x3g = 34

In zona ’FUNCTIE’ adresele se aloca consecutiv descrescator


&xx1f = 0x61fdac xx1f = 1234
&xx2f = 0x61fda8 xx2f = 2345
&xx23 = 0x61fda4 xx3f = 3456

In zona ’STIVA1’ variabilele ’copie’ se aloca crescator


&xx1m = 0x61fdc0 xx1m = 123
&xx2m = 0x61fdc8 xx2m = 234
&xx3m = 0x61fdd0 xx3m = 345

In zona ’STIVA2’ variabilele ’copie’ se aloca crescator


&xx1g = 0x61fdc0 xx1g = 12
&xx2g = 0x61fdc8 xx2g = 23
&xx3g = 0x61fdd0 xx3g = 34

In zona ’STIVA3’ adresele sunt cele reale din zona MAIN


&xx1m = 0x61fdec xx1m = 123
&xx2m = 0x61fde8 xx2m = 234
&xx3m = 0x61fde4 xx3m = 345

In zona ’STIVA4’ adresele sunt cele reale din zona GLOBALA


&xx1g = 0x404020 xx1g = 12
&xx2g = 0x404024 xx2g = 23
&xx3g = 0x404028 xx3g = 34

Process returned 0 (0x0) execution time : 0.106 s


Press any key to continue.

Stiva1 a eliberat memoria şi Stiva2 a plasat variabilele pe aceleaşi adrese!


https://www.learncpp.com/cpp-tutorial/the-stack-and-the-heap/
10.10 The stack and the heap By Alex on August 10th, 2007 — last modified by Alex on
December 21st, 2020
The memory that a program uses is typically divided into a few different areas, called segments:
The code segment (also called a text segment), where the compiled program sits in memory.
The code segment is typically read-only.
The bss segment (also called the uninitialized data segment), where zero-initialized global and
static variables are stored.
The data segment (also called the initialized data segment), where initialized global and static
variables are stored.
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 50

The heap, where dynamically allocated variables are allocated from.


The call stack, where function parameters, local variables, and other function-related informa-
tion are stored.
For this lesson, well focus primarily on the heap and the stack, as that is where most of the
interesting stuff takes place.
...

2.3.6 MSB şi LSB

Listing 2.3.5: show bytes in memory.cpp


// https://www.geeksforgeeks.org/little-and-big-endian-mystery/

#include <stdio.h>

/* function to show bytes in memory, from location start to start+n*/


void show_mem_rep(char *start, int n)
{
int i;
for (i = 0; i < n; i++)
//printf(" %.2x", start[i]);
printf(" %.8x : %.2x \n", &start[i], start[i]);

//printf("\n");
}

/*Main function to call above function for 0x01234567*/


int main()
{
int i = 0x01234567;

printf(" %.8x : %.2x \n\n", &i, i);

show_mem_rep((char *)&i, sizeof(i));


//getchar();
return 0;
}
/*
When above program is run on little endian machine,
gives "67 45 23 01" as output ,
while if it is run on big endian machine,
gives "01 23 45 67" as output.
*/

Pe ecran apare:
0061fdec : 1234567

0061fdec : 67
0061fded : 45
0061fdee : 23
0061fdef : 01

Process returned 0 (0x0) execution time : 0.016 s


Press any key to continue.

Figura 2.35: MSB+LSB

99
MSB (most-significant byte)
99
https://en.wikipedia.org/wiki/Bit_numbering
CAPITOLUL 2. PROGRAMARE C+++ 2.3. ALOCAREA VARIABILELOR 51

LSB (least-significant byte)

Figura 2.36: MSB endian

Ordinea octeţilor (ordinea biţilor nu se schimbă ı̂n ı̂n interiorul octeţilor). De exemplu pentru
numărul 23 MSB=2 şi LSB=3. plasează ordinul unităţilor (cifra 3) la adresă mare şi ordinul
zecilor (cifra 2) la adresă mică. Deci, ordin mic la adresă mare, ordin mare la adresă mică - ı̂n
reprezentarea Big Endian!

Figura 2.37: LSBendian

Ordinea octeţilor (ordinea biţilor nu se schimbă ı̂n ı̂n interiorul octeţilor). De exemplu pentru
numărul 23 LSB plasează ordinul unităţilor (cifra 3) la adresă mică şi ordinul zecilor (cifra 2) la
adresă mare. Deci, ordin mic la adresă mică, ordin mare la adresă mare - ı̂n reprezentarea Little
Endian!!
”Picture from:” https://aticleworld.com/little-and-big-endian-importance/
O reprezentare ”pe verticală” ... cred că produce mai puţină ı̂ncurcătură!

Figura 2.38: LSBendian

”Picture from:” https://en.wikipedia.org/wiki/Endianness


Adrese utile:
https://medium.com/@nonuruzun/little-big-endian-dc9abe36270
http://www.firmcodes.com/write-c-program-convert-little-endian-big
-endian-integer/
https://www.geeksforgeeks.org/bit-manipulation-swap-endianness-of-a
-number/
CAPITOLUL 2. PROGRAMARE C+++ 2.4. INSTRUCŢIUNI DE DECIZIE -
STRUCTURI ELEMENTARE DE CONTROL 52

https://www.geeksforgeeks.org/little-and-big-endian-mystery/
https://en.cppreference.com/w/cpp/types/endian
https://aticleworld.com/little-and-big-endian-importance/ ... OK !!!
https://en.wikipedia.org/wiki/Endianness

2.4 Instrucţiuni de decizie - structuri elementare de control


2.4.1 Instrucţiunea if

Listing 2.4.1: p07 instrucţiunea if


// p07 ... if

#include <fstream> // ifstream, ofstream


#include <iostream> // cout ... !!!

using namespace std;

ifstream fin("f07.in");
ofstream fout("f07.out");

int main()
{
int a, b;

fin >> a;
fin >> b;

if(a > b)
{
cout << " a este mai mare decat b";
}
else
{
cout << " a NU este mai mare decat b";
}

cout << "\n\n";

cout << " a = " << a << "\n";


cout << " b = " << b << "\n";

return 0;
}

Obs: if(x=3) ... in loc de x==3 ... ??? de dat exemplu ... Atenţie ... !!!

2.4.2 Instrucţiunea case


2.4.3 Operatorul condiţional (ternar) ? :
100 101
Operatorul condiţional (ternar ) ? : are următoarea sintaxă:
condition ? value if true : value if false
şi ste folosit (de obicei!) astfel:
variable = condition ? value if true : value if false

2.5 Instrucţiuni repetitive - structuri elementare de control


2.5.1 Instrucţiunea for

Listing 2.5.1: p08 instrucţiunea for


// p08 ... for descrescator

100
https://ro.wikipedia.org/wiki/Operator_ternar
101
https://en.wikipedia.org/wiki/%3F:
CAPITOLUL 2. PROGRAMARE C+++ 2.5. INSTRUCŢIUNI REPETITIVE -
STRUCTURI ELEMENTARE DE CONTROL 53

#include <fstream> // ifstream, ofstream


#include <iostream> // cout ... !!!

using namespace std;

ifstream finput("fa08.in");
ofstream foutput("fa08.out");

int a, b;
int k;

int main()
{
finput >> a;
finput >> b;

cout << " a = " << a << "\n";


cout << " b = " << b << "\n";
//cout << "\n";

for(k=b ; k >= a ; k--)


{
cout << k << " : " << k*k << "\n";
}

//cout<<"k = "<<k;

return 0;
}

Instrucţiunea for + if + continue


Instrucţiunea for + if + break

2.5.2 Instrucţiunea while

Listing 2.5.2: p09 instrucţiunea while


// p09 ... while

#include <fstream> // ifstream, ofstream


#include <iostream> // cout ... !!!

using namespace std;

ifstream finput("f09.in");
ofstream foutput("f09.out");

int a, b;

int main()
{
finput >> a;
finput >> b;

cout << " a = " << a << "\n";


cout << " b = " << b << "\n";
//cout << "\n";

int k=a;

while(k <= b)
{
cout << k << " : " << k*k << "\n";
k = k+1;
}

return 0;
}

2.5.3 Instrucţiunea do
CAPITOLUL 2. PROGRAMARE C+++ 2.6. INSTRUCŢIUNI DE SALT 54

Listing 2.5.3: p10 instrucţiunea do


// p10 ...

2.6 Instrucţiuni de salt


2.6.1 Instrucţiunea continue;

Listing 2.6.1: p11 instrucţiunea continue;


// p11 ...

2.6.2 Instrucţiunea break

Listing 2.6.2: p12 instrucţiunea break;


// p12 ...

2.7 Funcţii utile (... ı̂n depanare!)


2.7.1 getchar();

Listing 2.7.1: p13 instrucţiunea getchar();


// p13 ...
#include<stdio.h> // getchar(); // getc(stdin);
#include<iostream> // cout

using namespace std;

int main()
{
for(int i=1; i<23; i++)
{
cout<<i<<" ";
if(i%5==0) getchar(); // getc(stdin);
}

return 0;
}

2.7.2 system(“PAUSE”);

Listing 2.7.2: p14 instrucţiunea system(“PAUSE”);


// p14 ...
#include<stdio.h> // getchar(); // getc(stdin);
#include<iostream> // cout

using namespace std;

int main()
{
for(int i=1; i<23; i++)
{
cout<<i<<" ";
if(i%5==0) getchar(); // getc(stdin);
}

return 0;
}
Capitolul 3

Clasa a V-a

3.1 Algoritmi elementari


3.1.1 Tipuri simple de date. Tipul ı̂ntreg (pe 4 octeţi), tipul logic

Listing 3.1.1: p001 tipul intreg.cpp


#include<iostream> // cout

using namespace std;

int a1, a2; // tipul intreg; valori: -2 mialiarde ... 2 miliarde

int main()
{
a1=3;
a2=a1*a1;

cout<<"a1 = "<<a1<<" a2 = "<<a2<<endl;

return 0;
}
/*
a1 = 3 a2 = 9
*/

Listing 3.1.2: p002 tipul logic.cpp


#include<iostream> // cout

using namespace std;

bool b1, b2; // tipul logic; valori: 1 --> true; 0 --> false
bool c1, c2;

int main()
{
b1=1;
b2=true;

c1=0;
c2=false;

cout<<"b1 = "<<b1<<" b2 = "<<b2<<endl;


cout<<"c1 = "<<c1<<" c2 = "<<c2<<endl;
return 0;
}
/*
b1 = 1 b2 = 1
c1 = 0 c2 = 0
*/

3.1.2 Structura liniară, alternativă şi repetitivă

55
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 56

Listing 3.1.3: p003 structura liniara.cpp


#include<iostream> // cout

using namespace std;

int a1, a2, a3;

int main()
{
a2=2;
a3=3;
a1=a3/a2;

cout<<"a1 = "<<a1<<" a2 = "<<a2<<" a3 = "<<a3<<endl;

return 0;
}
/*
a1 = 1 a2 = 2 a3 = 3
*/

Listing 3.1.4: p004 structura alternativa.cpp


#include<iostream> // cout

using namespace std;

int a1, a2, a3;

int main()
{
a1=1;
a2=2;

if(a1 < a2)


{
a3=a1;// cel mai mic
}
else
if(a1 > a2)
{
a3=a2;// cel mai mic
}
else
//if(a1 == a2) // ara clar ca asta era ultima varianta!
{
a3=a1+a2; // = 2 a1 = 2 a2
}

cout<<"a1 = "<<a1<<" a2 = "<<a2<<" a3 = "<<a3<<endl;

return 0;
}
/*
a1 = 1 a2 = 2 a3 = 1
*/

Listing 3.1.5: p005 structura repetitiva for.cpp


#include<iostream> // cout

using namespace std;

int a1, a2;


int i;

int main()
{
a1=10;
a2=20;

for(i = a1; i <= a2; i++)


{
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 57

cout<<i<<" ";
}
cout<<endl;

return 0;
}
/*
10 11 12 13 14 15 16 17 18 19 20
*/

Listing 3.1.6: p006 structura repetitiva for.cpp


#include<iostream> // cout

using namespace std;

int a1, a2;


int i;

int main()
{
a1=10;
a2=20;

i=a1;
for( ; i <= a2; i++)
{
cout<<i<<" ";
}
cout<<endl;

return 0;
}
/*
10 11 12 13 14 15 16 17 18 19 20
*/

Listing 3.1.7: p007 structura repetitiva for.cpp


#include<iostream> // cout

using namespace std;

int a1, a2;


int i;

int main()
{
a1=10;
a2=20;

i=a1;
for( ; i <= a2; )
{
cout<<i<<" ";
i++; // i = i+1;
}
cout<<endl;

return 0;
}
/*
10 11 12 13 14 15 16 17 18 19 20
*/

Listing 3.1.8: p008 structura repetitiva while.cpp


#include<iostream> // cout

using namespace std;

int a1, a2;


int i;
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 58

int main()
{
a1=10;
a2=20;

i=a1;
while( i <= a2 )
{
cout<<i<<" ";
i++; // i = i+1;
}
cout<<endl;

return 0;
}
/*
10 11 12 13 14 15 16 17 18 19 20
*/

3.1.3 Prelucrarea cifrelor numerelor naturale scrise ı̂n baza 10

Listing 3.1.9: p009 numarul si suma cifrelor.cpp


#include<iostream> // cout

using namespace std;

int nr; // numar natural cu cel mult 9 cifre


int nrcif; // numarul cifrelor lui nr
int sumacif; // suma cifrelor lui nr

int main()
{
nr=123456789;
nrcif=0;
sumacif=0;

int nrc=nr; // copie a lui nr, pentru afisare in cout

while(nr != 0)
{
nrcif++;
sumacif=sumacif + (nr%10); // nr%10 este ultima cifra din nr
nr = nr/10; // ii "sterge" ultima cifra
}

cout<<"numarul cifrelor lui "<<nrc<<" este "<<nrcif<<endl;


cout<<"suma cifrelor lui "<<nrc<<" este "<<sumacif<<endl;

return 0;
}
/*
numarul cifrelor lui 123456789 este 9
suma cifrelor lui 123456789 este 45
*/

Listing 3.1.10: p010 plasare cifra la sfarsit.cpp


#include<iostream> // cout

using namespace std;

int nr1; // numar natural cu cel mult 8 cifre


int cif; // cifra de pus la sfarsitul lui nr
int nr2; // nr1 cu cifra cif la sfarsit

int main()
{
nr1=34567;
cif=1;

nr2 = nr1*10 + cif;


CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 59

cout<<"nr1 = "<<nr1<<" cif = "<<cif<<" nr2 = "<<nr2<<endl;

return 0;
}
/*
nr1 = 34567 cif = 1 nr2 = 345671
*/

Listing 3.1.11: p011 plasare cifra la inceput.cpp


#include<iostream> // cout

using namespace std;

int nr1; // numar natural cu cel mult 8 cifre


int cif; // cifra de pus la inceputul lui nr
int nr2; // nr1 cu cifra cif la sfarsit

int p10; // puteri ale lui 10


int p10c; // p10c = p10*cif
int nr1c; // copie a lui nr1, pentru afisare in cout

int main()
{
nr1=34567;
cif=9;

nr1c=nr1;
p10=1;
while(nr1c != 0)
{
p10=p10*10;
nr1c = nr1c/10;
}
cout<<"nr1 = "<<nr1<<endl;
cout<<"p10 = "<<p10<<endl; // p10 = 100000 zero-uri cate cifre are nr1

p10c = p10*cif;

cout<<"p10c = "<<p10c<<endl; // p10c = cif00000 zero-uri cate cifre are nr1

nr2=nr1+p10c;
cout<<"nr2 = "<<nr2<<endl;

return 0;
}
/*
nr1 = 34567
p10 = 100000
p10c = 900000
nr2 = 934567
*/

Listing 3.1.12: p012 inversare numar.cpp


#include<iostream> // cout

using namespace std;

int nr1; // numar natural cu cel mult 8 cifre


int nr2; // nr1 cu cifrele inversate

int nr1c; // copie a lui nr1

int main()
{
nr1=123456789;
nr2=0;

nr1c=nr1;
while(nr1c != 0)
{
nr2=nr2*10 + (nr1c % 10); // nr1c%10 este ultima cifra a lui nr1c
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 60

nr1c = nr1c/10; // nr1c%10 este restul impartirii lui nr1c la 10


}
cout<<"nr1 = "<<nr1<<endl;
cout<<"nr2 = "<<nr2<<endl;

return 0;
}
/*
nr1 = 123456789
nr2 = 987654321
*/

Listing 3.1.13: p013 verificare palindrom.cpp


#include<iostream> // 12344321 este palindrom; 123454321 este palindrom

using namespace std;

int nr1; // numar natural cu cel mult 9 cifre


int nr2; // numar natural cu cel mult 9 cifre

int nr1i; // nr1 cu cifrele inversate


int nr2i; // nr2 cu cifrele inversate

int nr1c; // copia lui nr1


int nr2c; // copia lui nr2

int main()
{
nr1=12344321;
nr2=123451234;

nr1c=nr1;
nr1i=0;
while(nr1c != 0)
{
nr1i=nr1i*10 + (nr1c % 10);
nr1c = nr1c/10;
}

nr2c=nr2;
nr2i=0;
while(nr2c != 0)
{
nr2i=nr2i*10 + (nr2c % 10);
nr2c = nr2c/10;
}

if(nr1 == nr1i)
{
cout<<"nr1 = "<<nr1<<" este palindrom"<<endl;
}
else
{
cout<<"nr1 = "<<nr1<<" NU este palindrom"<<endl;
}

if(nr2 == nr2i)
{
cout<<"nr2 = "<<nr2<<" este palindrom"<<endl;
}
else
{
cout<<"nr2 = "<<nr2<<" NU este palindrom"<<endl;
}

return 0;
}
/*
nr1 = 12344321 este palindrom
nr2 = 123451234 NU este palindrom
*/

Listing 3.1.14: p014 modificarea cifrei de la mijloc.cpp


CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 61

#include<iostream> // presupun ca numarul are numai cifre nenule


// scad 1 din cifra/cifrele de la mijloc: 55377 --> 55277 si 553477 --> 552377
#include<iomanip> // pentru afisari pe lungime fixa (iese mai frumos!)

using namespace std;

int nr1; // numar natural cu cel mult 9 cifre


int nr2; // numar natural cu cel mult 9 cifre

int nr1cif; // numarul cifrelor lui nr1


int nr2cif; // numarul cifrelor lui nr2

int nr1c; // copie a lui nr1


int nr2c; // copie a lui nr2

int nr1m; // nr1 modificat


int nr2m; // nr2 modificat

int p10; // puterile lui 10

int i; // de "plimbat" prin for

int main()
{
nr1=55377;
nr2=553477;

nr1cif=0;
nr2cif=0;

nr1c=nr1;
nr2c=nr2;

// determin numarul cifrelor lui nr1


while(nr1c != 0)
{
nr1cif++;
nr1c = nr1c/10; // "sterg" ultima cifra
}
cout<<"numarul cifrelor lui "<<nr1<<" este "<<nr1cif<<endl;

// plasez p10 la jumatatea lui nr1


p10=1;
for(i=1; i <= nr1cif/2; i++)
{
p10=p10*10;
}
cout<<"nr1 = "<<setw(9)<<nr1<<endl;
cout<<"p10 = "<<setw(9)<<p10<<endl;

nr1m=nr1-p10;
cout<<"nr1m = "<<setw(9)<<nr1m<<endl;

cout<<endl;

// determin numarul cifrelor lui nr2 (aproape la fel ca la nr1)


// la numar par de cifre, la mijloc sut doua cifre, nu numai o cifra!
while(nr2c != 0)
{
nr2cif++;
nr2c = nr2c/10; // "sterg" ultima cifra
}
cout<<"numarul cifrelor lui "<<nr2<<" este "<<nr2cif<<endl;

// daca facem la fel ca la nr1, obtinem:


// plasez p10 la jumatatea lui nr1
p10=1;
for(i=1; i <= nr2cif/2; i++)
{
p10=p10*10;
}
cout<<"nr2 = "<<setw(9)<<nr2<<endl;
cout<<"p10 = "<<setw(9)<<p10<<endl;

nr2m=nr2-p10;
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 62

cout<<"nr2m = "<<setw(9)<<nr2m<<endl;

// trebuie sa mai scadem 1 din cifra de "mai la dreapta"


p10=p10/10;
nr2m=nr2m-p10;
cout<<"nr2m = "<<setw(9)<<nr2m<<endl;

return 0;
}
/*
numarul cifrelor lui 55377 este 5
nr1 = 55377
p10 = 100
nr1m = 55277

numarul cifrelor lui 553477 este 6


nr2 = 553477
p10 = 1000
nr2m = 552477
nr2m = 552377
*/

Listing 3.1.15: p015 modificarea mijlocului numarului.cpp


#include<iostream> // presupun ca numarul are numai cifre nenule
// scad 1 din cifra/cifrele de la mijloc: 55377 --> 55277 si 553477 --> 552377
#include<iomanip> // pentru afisari pe lungime fixa (iese mai frumos!)

using namespace std;

int nr; // numar natural cu cel mult 9 cifre


int nrcif; // numarul cifrelor lui nr
int nrc; // copie a lui nr
int nrm; // nr modificat

int p10; // puterile lui 10


int p10c; // p10*cif

int i; // de "plimbat" prin for

int main()
{
nr=55377;
//nr=553477;

nrcif=0;
nrc=nr;

// determin numarul cifrelor lui nr


while(nrc != 0)
{
nrcif++;
nrc = nrc/10; // "sterg" ultima cifra
}
cout<<"numarul cifrelor lui "<<nr<<" este "<<nrcif<<endl;

// plasez p10 la jumatatea lui nr1


p10=1;
for(i=1; i <= nrcif/2; i++)
{
p10=p10*10;
}
cout<<"nr = "<<setw(9)<<nr<<endl;
cout<<"p10 = "<<setw(9)<<p10<<endl;

nrm=nr-p10;
cout<<"nrm = "<<setw(9)<<nrm<<endl;

// daca nrcif=par trebuie sa mai scadem 1 din cifra de "mai la dreapta"


if(nrcif%2 == 0) // restul impartirii la 2 este 0 (adica numarul este par)
{
p10=p10/10;
nrm=nrm-p10;
cout<<"nrm = "<<setw(9)<<nrm<<endl;
}
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 63

return 0;
}
/*
numarul cifrelor lui 55377 este 5
nr = 55377
p10 = 100
nrm = 55277
*/

3.1.4 Divizorii numerelor naturale


Definiţia 1. Numerele 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ... se numesc numere naturale.

Definiţia 2. Numărul natural b divide numărul natural a dacŭ există numărul natural c, astfel
ı̂ncât a b c.

scriem citim
b¶a b divide pe a, sau
b este divizor al lui a
ab a se divide cu b, sau
a este divizibil cu b, sau
a este multiplu al lui b

La matematică aţi studiat:

ˆ Proprietăţi ale divizibilităţii


– Fie a, b, c " N. Dacă a şi b se divid cu c, atunci şi a  b se divide cu c.
– Fie a, b, n " N. Dacă a se divide cu b, atunci şi n a se divide cu b.
ˆ Divizori comuni
Definiţia 3. Un divizor comun al numerelor a, b " N este un număr d " N, cu proprietatea
că d ¶ a şi d ¶ b.
ˆ Multipli comuni
Definiţia 4. Un multiplu comun al numerelor a, b " N este un număr m " N, cu proprietatea
că ma şi mb.
ˆ Criterii de divizibilitate cu 10 (ı̂n produs de numere: un 2 şi un 5)
ˆ Criterii de divizibilitate cu 100 (ı̂n produs de numere: doi de 2 şi doi de 5)
ˆ Criterii de divizibilitate cu 1000 (ı̂n produs de numere: trei de 2 şi trei de 5)
ˆ Criteriul de divizibilitate cu 5 (ultima cifră este divizibilă cu 5)
ˆ Criteriul de divizibilitate cu 2 (ultima cifră este divizibilă cu 2)
ˆ Criteriul de divizibilitate cu 9 (suma cifrelor este multiplu de 9)
ˆ Criteriul de divizibilitate cu 3 (suma cifrelor este multiplu de 3)
ˆ Numere prime
Definiţia 5. Un număr natural mai mare decât 1, care are numai doi divizori: pe 1 şi pe
el ı̂nsuşi, se numeşte număr prim.
ˆ Numere compuse
Definiţia 6. Un număr natural diferit de 1, care are mai mult de doi divizori, se numeşte
număr compus.

Figura 3.1: Divizorii lui n 24


CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 64

Dincolo de 12 (jumătatea lui n) nu mai există niciun divizor al lui n (cu excepţia lui n, desigur!).
Explicaţia este simplă: dacă ar exista un divizor d % n2 al lui n atunci d ı̂nmulţit cu vreun număr
ar trebui să fie egal cu n. Cel mai mic număr posibil else 2. Dar şi el, cel mai mic, ı̂l face pe d să-l
depăşească pe n, pentru că

d%
n
2
¼2 ˜ d%n

În concluzie, atunci când trebuie să găsim divizorii unui numar natural n ı̂i vom căuta ı̂n zona
numerelor 2, 3, ..., n2 , dar să nu uităm de 1 şi de n care sunt divizori şi ei!

Listing 3.1.16: p016 lista divizorilor.cpp


#include<iostream> // pentru cout

using namespace std;

int n; // numar natural cu cel mult 9 cifre

int i; // de "plimbat" prin for

int main()
{
n=2*3*3*5; // n > 1

cout<<"n = "<<n<<endl;

for(i=1; i<=n; i++)


{
if(n%i == 0) // restul impartirii prin i
{
cout<<i<<" ";

}// sf if
}// sf for

// lista ... optim!


cout<<endl;
cout<<"Optim:"<<endl;
cout<<1<<" ";
for(i=2; i<=n/2; i++)
{
if(n%i == 0) // restul impartirii prin i
{
cout<<i<<" ";

}// sf if
}// sf for
cout<<n;

return 0;
}// sf main
/*
n = 90
1 2 3 5 6 9 10 15 18 30 45 90
Optim:
1 2 3 5 6 9 10 15 18 30 45 90
*/

Listing 3.1.17: p017 numarul divizorilor.cpp


#include<iostream> // pentru cout

using namespace std;

int n; // numar natural cu cel mult 9 cifre


int nrdiv1; // nr divizori
int nrdiv2; // nr divizori - "optim"!

int i; // de "plimbat" prin for

int main()
{
n=2*3*3*5; // n > 1
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 65

cout<<"n = "<<n<<endl;

nrdiv1=0;
for(i=1; i<=n; i++)
{
if(n%i == 0) // restul impartirii prin i
{
nrdiv1++;

}// sf if
}// sf for
cout<<"nrdiv1 = "<<nrdiv1<<endl;

// calcul ... optim!


cout<<endl;
cout<<"Optim:"<<endl;
nrdiv2=2; // pentru 1 si n
for(i=2; i<=n/2; i++)
{
if(n%i == 0) // restul impartirii prin i
{
nrdiv2++;

}// sf if
}// sf for
cout<<"nrdiv2 = "<<nrdiv2<<endl;

return 0;
}// sf main
/*
n = 90
nrdiv1 = 12

Optim:
nrdiv2 = 12
*/

Listing 3.1.18: p018 suma divizorilor.cpp


#include<iostream> // pentru cout

using namespace std;

int n; // numar natural cu cel mult 9 cifre


int sdiv1; // suma divizori
int sdiv2; // suma divizori - "optim"!

int i; // de "plimbat" prin for

int main()
{
n=2*3*3*5; // n > 1

cout<<"n = "<<n<<endl;

sdiv1=0;
for(i=1; i<=n; i++)
{
if(n%i == 0) // restul impartirii prin i
{
sdiv1=sdiv1+i;

}// sf if
}// sf for
cout<<"sdiv1 = "<<sdiv1<<endl;

// calcul ... optim!


cout<<endl;
cout<<"Optim:"<<endl;
sdiv2=1+n; // pentru 1 si n
for(i=2; i<=n/2; i++)
{
if(n%i == 0) // restul impartirii prin i
{
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 66

sdiv2=sdiv2+i;

}// sf if
}// sf for
cout<<"sdiv2 = "<<sdiv2<<endl;

return 0;
}// sf main
/*
n = 90
sdiv1 = 234

Optim:
sdiv2 = 234
*/

3.1.5 Numere prime


Pentru a verifica dacă un număr n este număr prim trebuie să verificăm
dacă are vreun divizor ı̂n afară de 1 şi de n. Deci ar trebuie să verificăm
dacă n este divizibil cu vreunul dintre numerele 2, 3, ..., n-1, adică să folosim
instrucţiunea
for(i=2; i <= n-1; i++)
Dar, se poate ceva mai bine!
Hai să analizăm divizorii lui 100 ca să vedem cum arată aceşti divizori!
100
Fie d1 un divizor al lui 100 şi d2 d1
divizorul ”pereche”!
Figura
Sunt nişte divizori mai mici : 1, 2, 4, 5, 10 şi nişte divizori mai mari : 10, 3.2:50, divi-
20, 25, 100.
Aceşti divizori sunt ”ı̂n pereche”, un divizor mic cu un divizor mare! zorii lui n 100
Pe acest exemplu este clar că nu are rost să depăşim numărul 10 pentru că am regăsi numai
divizorii mici (pe care deja i-am găsit ı̂ncercând numerele 1, 2, 3, 4, 5, 6, 7, 8, 9, 10). Când am
ajuns la 11 ... ne oprim! ... pentru că nu are rost să căutăm mai departe! De ce? Pentru că mai

departe i-am găsi numai pe cei mici !


Dar ce semnificaţie are numărul 10 din acest exemplu, unde ne-am oprit căutarea? De fapt,
este mai interesant să vedem de ce nu l-am luat pe 11 iar pe 10 l-am luat ı̂n considerare pentru
verificare!
Pe 11 nu l-am luat ı̂n considerare şi ne-am oprit căutarea pentru că 11 11 % 100.
În concluzie, pentru determinarea divizorilor unui număr n, ı̂ncepem căutarea divizorilor mici
n
d1 (obţinem şi divizorul pereche d2 ) verificând, pe rând, numerele d=2, 3, 4, 5, 6, 7, ... şi
d1
când d d % n ne oprim.

Figura 3.3: Divizorii lui n 24

De exemplu, pentru n 24 ne oprim cu verificarea la d 5 pentru că 5 5 25 % 24.


Când ne interesează numai dacă numărul n este număr prim, vom verifica, pe rând, numerele
d=2, 3, 5, 7, 9, 11, ... şi când d d % n ne oprim. De ce nu mai verificăm numerele 4, 6, 8, 10, ...?
Răspunsul ı̂l avem la verificarea lui 2.

Listing 3.1.19: p019 lista divizorilor optim.cpp


#include<iostream> // pentru cout
#include<iomanip>

using namespace std;

int n; // numar natural cu cel mult 9 cifre


CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 67

int i; // de "plimbat" prin for

int main()
{
n=100; // n > 1

cout<<"n = "<<n<<endl;

for(i=1; i<=n; i++)


{
if(n%i == 0) // restul impartirii prin i
{
cout<<i<<" ";

}// sf if
}// sf for

// lista ... optim!


cout<<endl;
cout<<"Optim:"<<endl;
for(i=1; i * i <= n; i++)
{
if(n%i == 0) // restul impartirii prin i
{
cout<<setw(3)<<i<<" "<<setw(3)<< n/i <<endl;

}// sf if
}// sf for

return 0;
}// sf main
/*
n = 100
1 2 4 5 10 20 25 50 100
Optim:
1 100
2 50
4 25
5 20
10 10
*/

102
Pentru a găsi numerele prime, putem folosi ”ciurul lui Eratostene” . El constă ı̂n a elimina
numerele compuse pe axa numerelor naturale
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ...
Primul număr prim este 2. Îl ”ı̂ncercuim” (aici, colorăm cu verde) şi ”tăiem” (aici, colorăm cu
rosu) toţi multiplii lui 2.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ...
Spre dreapta lui 2, primul număr ”netăiat” (necolorat ı̂n roşu) este 3. Acesta este număr prim
şi repetăm procedeul de la numărul 2.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ...
Numerele 6, 12, 18, 24, 30, ... erau deja ”tăiate” (colorate cu roşu) de când am eliminat
multiplii lui 2.
Repetăm această procedură pentru 5, 7, 11, 13, 17, 19, 23, ... care au ”scăpat netăiate”

(neı̂nroşite) până acum şi vor scăpa ı̂n continuare! Numărul 25 ”nu scapă” din cauza lui 5.

Listing 3.1.20: p020 numere prime - verificare.cpp


#include<iostream> // pentru cout

int n;

bool esteprim; // esteprim=1 (true) --> n este prim;


int i; // esteprim=0 (false) --> n NU este prim;

102
https://ro.wikipedia.org/wiki/Ciurul lui Eratostene
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 68

using namespace std;

int main()
{
n=123;
//n=97;

cout<<"n = "<<n<<endl;

esteprim=1; // presupun ca nr este prim

if(n%2 == 0) // n este numar par


{
esteprim=0;
}
else
for(i=3; i*i <=n; i=i+2) // nr impare; optimizare !!!
//for(i=3; i<=n-1; i=i+2)
{
if(n%i == 0) // restul impartirii prin i
{
// n are divizor pe i, deci nu este prim
esteprim=0;
cout<<n<<" = "<<i<<" * "<< n/i <<endl;
break; // iese brusc din for
}// sf if
}// sf for

// aici nu stiu daca s-a afisat mesajul din for, dar pot verifica
if(esteprim == 1)
{
cout<<n<<" este numar prim"<<endl;
}
else
if(esteprim == 0) // nu era necesar, dar ...ca sa fie mai clar !
{
cout<<n<<" NU este numar prim"<<endl;
}

return 0;
}
/*
n = 123
123 = 3 * 41
123 NU este numar prim
*/

Listing 3.1.21: p021 numere prime - ciurul lui Eratostene.cpp


#include<iostream> // pentru cout
#include<iomanip>

int n; // afisez numerele prime din zona 2, ..., n

int i, j; // de utilizat in for si vectorul ciur[]

// numere prime din intervalul [2, ..., 1.000.000]


bool ciur[1000001]; // initializat automat cu 0 = false
// ciur[i]=false ==> i = NU este numar prim;
// ciur[i]=true ==> i = este numar prim;

using namespace std;

void ciurEratostene()
{
cout<<"--> ciurEratostene()\n";

for(i=2; i<=n; i++) ciur[i]=true; // era initializat automat cu false!

// j=2 ramane nr prim


for (int j = 4; j <= n; j = j+2) // marchez nr pare, 2 este prim
{
ciur[j] = false; // j NU este nr prim ...!
}
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 69

//for (int i = 3; i*i <= n; i += 2) // optimizare !!!


for (int i = 3; i <= n; i += 2) // analizez numai numerele impare
{
if (ciur[i]==true) // daca i ESTE nr prim
{
//cout<<"\ni = "<<i<<" : -------------------------\n";

for (j = 2 * i; j <= n; j = j + i) // marchez multiplii lui i


{
ciur[j] = false; // j NU este nr prim ...

} // for j
} // sf if
else
if (ciur[i]==false) // daca i NU ESTE nr prim
{
// NU am ce sa fac aici pentru ca i este deja marcat!
}
} // sf for i

cout<<"<-- ciurEratostene()\n";
//cout<<"Apasa <Enter>";
//getchar();
}// ciurEratostene()

int main()
{
n=200; // ciur pana la n=200

cout<<"n = "<<n<<endl;

ciurEratostene();

for(i=2; i<=n; i++)


{
if(ciur[i]==true)
{
cout<<setw(3)<<i<<" ";
}
}
cout<<endl;

int n1=97;
int n2=123;

if(ciur[n1]==true) cout<<n1<<" este numar prim\n";


else cout<<n1<<" NU este numar prim\n";

if(ciur[n2]==true) cout<<n2<<" este numar prim\n";


else cout<<n2<<" NU este numar prim\n";

return 0;
}
/*
n = 200
--> ciurEratostene()
<-- ciurEratostene()
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 101 103 107 109 113
127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199
97 este numar prim
123 NU este numar prim
*/

3.1.6 Descompunere ı̂n factori primi

Listing 3.1.22: p022 descompunere in factori primi.cpp


#include<iostream> // pentru cout
#include<iomanip>

int n; // nr de descompus in factori primi


int i; // indice
int nrfp; // numarul factorilor primi
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 70

int d; // divizor

int f[30]; // f[i] = factor prim


int e[30]; // exponentul factorului prim f[i]

using namespace std;

int main()
{
n=2*2*2*3*3*5*7*7;

d=2;
nrfp=0;
if(n%d == 0) // daca n este numar par
{
nrfp=1; // 2 ar fi primul factor prim
f[1]=2;
while(n%d == 0) // scot 2-urile din n
{
e[1]++;
n=n/2;
}
} // sf if(n%d == 0)

d=3; // incerc numerele impare ...


while(d*d <= n) // optimizarea pe care am mai folosit-o !
{
if(n%d == 0) // daca d = divizor (sigur e factor prim!)
{
nrfp++; // alt factor prim;
f[nrfp]=d;
while(n%d == 0) // scot d-urile din n
{
e[nrfp]++;
n=n/d;
}
} // if(n%d == 0)

d=d+2; // trec la urmatorul numar impar


} // sf while(d*d <= n)

if(n > 1) // a ramas ultimul factor prim la puterea 1


{
nrfp++;
f[nrfp]=n;
e[nrfp]=1;
}
else
if(n==1) // ca sa fie mai clar ...
{
// nu am ce sa fac aici!
}

// afisez factorii primi si exponentii lor

cout<<"f[i]: ";
for(i=1; i<=nrfp; i++) cout<<setw(3)<<f[i];

cout<<endl;

cout<<"e[i]: ";
for(i=1; i<=nrfp; i++) cout<<setw(3)<<e[i];

return 0;
}
/*
n=2*2*2*3*3*5*7*7;
f[i]: 2 3 5 7
e[i]: 3 2 1 2
*/
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 71

3.1.7 Numărul divizorilor - calcul cu formulă

Figura 3.4: Numărul divizorilor - calcul cu formulă

Listing 3.1.23: p023 numarul divizorilor cu formula si vectori.cpp


#include<iostream> // pentru cout
#include<iomanip>

int n; // nr de descompus in factori primi


int i; // indice
int nrfp; // numarul factorilor primi
int nrdiv; // numarul divizorilor
int d; // divizor

int f[30]; // f[i] = factor prim


int e[30]; // exponentul factorului prim f[i]

using namespace std;

int main()
{
//n=2*2*2*3*3*5*7*7;
n=24;

cout<<"n = "<<n<<endl;
d=2;
nrfp=0;
if(n%d == 0) // daca n este numar par
{
nrfp=1; // 2 ar fi primul factor prim
f[1]=2;
while(n%d == 0) // scot 2-urile din n
{
e[1]++;
n=n/2;
}
} // sf if(n%d == 0)

d=3; // incerc numerele impare ...


while(d*d <= n) // optimizarea pe care am mai folosit-o !
{
if(n%d == 0) // daca d = divizor (sigur e factor prim!)
{
nrfp++; // alt factor prim;
f[nrfp]=d;
while(n%d == 0) // scot d-urile din n
{
e[nrfp]++;
n=n/d;
}
} // if(n%d == 0)

d=d+2; // trec la urmatorul numar impar


} // sf while(d*d <= n)

if(n > 1) // a ramas ultimul factor prim la puterea 1


{
nrfp++;
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 72

f[nrfp]=n;
e[nrfp]=1;
}
else
if(n==1) // ca sa fie mai clar ...
{
// nu am ce sa fac aici!
}

// afisez factorii primi si exponentii lor

cout<<"f[i]: ";
for(i=1; i<=nrfp; i++) cout<<setw(3)<<f[i];

cout<<endl;

cout<<"e[i]: ";
for(i=1; i<=nrfp; i++) cout<<setw(3)<<e[i];

nrdiv=1;
for(i=1; i<=nrfp; i++)
{
nrdiv=nrdiv*(1+e[i]);
}

cout<<endl;
cout<<"nrdiv = "<<nrdiv<<endl;

return 0;
}
/*
n=2*2*2*3*3*5*7*7;
f[i]: 2 3 5 7
e[i]: 3 2 1 2
*/

Listing 3.1.24: p024 numarul divizorilor cu formula fara vectori


#include<iostream> // pentru cout
#include<iomanip>

int n; // nr pentru care calculez nr divizorilor


int efp; // exponentul factorului prim
int nrdiv; // numarul divizorilor lui n
int d; // divizor

using namespace std;

int main()
{
n=24;
//n=2*2*2*3*3*5*7*7;

cout<<"n = "<<n<<endl;

nrdiv=1; // initializez produsul nrdiv=(1+e1)*(1+e2)*...*(1+ek)


d=2;
if(n%d == 0) // daca n este numar par
{
efp=0; // 2 ar fi primul factor prim
while(n%d == 0) // scot 2-urile din n
{
efp++;
n=n/2;
}
} // sf if(n%d == 0)
nrdiv=nrdiv*(1+efp);

d=3; // incerc numerele impare ...


while(d*d <= n) // optimizarea pe care am mai folosit-o !
{
if(n%d == 0) // daca d = divizor (sigur e factor prim!)
{
efp=0; // alt factor prim;
while(n%d == 0) // scot d-urile din n
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 73

{
efp++;
n=n/d;
}
} // if(n%d == 0)
nrdiv=nrdiv*(1+efp);

d=d+2; // trec la urmatorul numar impar


} // sf while(d*d <= n)

if(n > 1) // a ramas ultimul factor prim la puterea 1


{
nrdiv=nrdiv*(1+1);
}
else
if(n==1) // ca sa fie mai clar ...
{
// nu am ce sa fac aici!
}

//cout<<endl;
cout<<"nrdiv = "<<nrdiv<<endl;

return 0;
}
/*
n = 24
nrdiv = 8

n = 17640
nrdiv = 72
*/

3.1.8 Algoritmul lui Euclid

Listing 3.1.25: p025 cmmdc algoritmul lui Euclid


#include<iostream> // pentru cout
#include<iomanip>

using namespace std;

int a, b; // cmmdc(a,b) = ?
int ac, bc; // cmmdc(a,b) = ?

int c; // catul impartirii a/b


int r; // restul impartirii a/b
int cmmdcab;// cmmdc(a,b)

int main()
{
a=2*3*3*5;
b=2*2*3*7;

ac=a; // copie pentru afisarea finala!


bc=b; // copie pentru afisarea finala!

cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;

while(b != 0) // fac impartirea a/b


{
c=a/b; // nu se foloseste dar ... ca sa fie mai clar ...
r=a%b;

cout<<"a = "<<setw(2)<<a
<<" b = "<<setw(2)<<b
<<" c = "<<setw(2)<<c
<<" r = "<<setw(2)<<r<<endl;

a=b;
b=r;

cout<<"a = "<<setw(2)<<a
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 74

<<" b = "<<setw(2)<<b<<endl;

//getchar();
} // sf while(r != 0)

cmmdcab=a;

cout<<"cmmdc("<<ac<<","<<bc<<") = "<<cmmdcab<<endl;

return 0;
}// sf main
/*
a = 90
b = 84
a = 90 b = 84 c = 1 r = 6
a = 84 b = 6
a = 84 b = 6 c = 14 r = 0
a = 6 b = 0
cmmdc(90,84) = 6
*/

3.1.9 cmmmc

Listing 3.1.26: p026 cmmmmc cu Euclid.cpp


#include<iostream> // pentru cout
#include<iomanip>

using namespace std;

int a, b; // cmmdc(a,b) = ?
int ac, bc; // pentru afisare

int c; // catul impartirii a/b


int r; // restul impartirii a/b

int cmmdcab;// cmmdc(a,b)


int cmmmmcab;// cmmmmc(a,b)

int main()
{
a=2*3*3*5;
b=2*2*3*7;

ac=a; // copie pentru afisarea finala!


bc=b; // copie pentru afisarea finala!

cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;

while(b != 0) // fac impartirea a/b


{
c=a/b; // nu se foloseste dar ... ca sa fie mai clar ...
r=a%b;

cout<<"a = "<<setw(2)<<a
<<" b = "<<setw(2)<<b
<<" c = "<<setw(2)<<c
<<" r = "<<setw(2)<<r<<endl;

a=b;
b=r;

cout<<"a = "<<setw(2)<<a
<<" b = "<<setw(2)<<b<<endl;

//getchar();
} // sf while(r != 0)

cmmdcab=a;
cout<<"cmmdc("<<ac<<","<<bc<<") = "<<cmmdcab<<endl;

cmmmmcab=ac*bc/cmmdcab; // apar probleme daca ac*ab este prea mare !


cout<<"cmmmmc("<<ac<<","<<bc<<") = "<<cmmmmcab<<endl;
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 75

return 0;
}// sf main
/*
a = 90
b = 84
a = 90 b = 84 c = 1 r = 6
a = 84 b = 6
a = 84 b = 6 c = 14 r = 0
a = 6 b = 0
cmmdc(90,84) = 6
cmmmmc(90,84) = 1260
*/

Listing 3.1.27: p027 cmmmmc cu descompunere in factori primi.cpp


#include<iostream> // fara vectori nu are rost ...
#include<iomanip> // pentru ca vectorii folositi sunt "mici"

int a, b; // cmmmmc(a,b) = ?
int ac, bc; // pentru afisare

int cmmmmc; // cmmmmc(a,b)

int nrfpa; // numarul factorilor primi a


int nrfpb; // numarul factorilor primi b
int nrfpc; // numarul factorilor primi c

int i, j, k; // indici
int d; // divizor

int fa[30]; // f[i] = factor prim


int ea[30]; // exponentul factorului prim f[i]

int fb[30]; // f[i] = factor prim


int eb[30]; // exponentul factorului prim f[i]

int fc[30]; // f[i] = factor prim


int ec[30]; // exponentul factorului prim f[i]

using namespace std;

int main()
{
a=2*3*3*5;
b=2*2*3*7;

ac=a; // copia lui a


bc=b; // copia lui b

//-----------------------------------------------------

// descompunere in factori primi pentru a


d=2;
nrfpa=0;
if(a%d == 0) // daca a este numar par
{
nrfpa=1; // 2 ar fi primul factor prim
fa[1]=2;
while(a%d == 0) // scot 2-urile din a
{
ea[1]++;
a=a/2;
}
} // sf if(a%d == 0)

d=3; // incerc numerele impare ...


while(d*d <= a) // optimizarea pe care am mai folosit-o !
{
if(a%d == 0) // daca d = divizor (sigur e factor prim!)
{
nrfpa++; // alt factor prim;
fa[nrfpa]=d;
while(a%d == 0) // scot d-urile din n
{
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 76

ea[nrfpa]++;
a=a/d;
}
} // if(a%d == 0)

d=d+2; // trec la urmatorul numar impar


} // sf while(d*d <= a)

if(a > 1) // a ramas ultimul factor prim la puterea 1


{
nrfpa++;
fa[nrfpa]=a;
ea[nrfpa]=1;
}
else
if(a==1) // ca sa fie mai clar ...
{
// nu am ce sa fac aici!
}

// afisez factorii primi si exponentii lor


cout<<"a = "<<ac<<" = "; // o afisare mai draguta ... !!!
cout<<fa[1]; // urmeaza * fa[i] de ea[i]-1 ori ... !!!
for(j=2; j<=ea[1]; j++) cout<<" * "<<fa[1];
for(i=2; i<=nrfpa; i++)
{
for(j=1; j<=ea[i]; j++) cout<<" * "<<fa[i];
}

cout<<endl;
cout<<"fa[i]: ";
for(i=1; i<=nrfpa; i++) cout<<setw(3)<<fa[i];

cout<<endl;

cout<<"ea[i]: ";
for(i=1; i<=nrfpa; i++) cout<<setw(3)<<ea[i];

//-----------------------------------------------------

// descompunere in factori primi pentru b


d=2;
nrfpb=0;
if(b%d == 0) // daca b este numar par
{
nrfpb=1; // 2 ar fi primul factor prim
fb[1]=2;
while(b%d == 0) // scot 2-urile din a
{
eb[1]++;
b=b/2;
}
} // sf if(b%d == 0)

d=3; // incerc numerele impare ...


while(d*d <= b) // optimizarea pe care am mai folosit-o !
{
if(b%d == 0) // daca d = divizor (sigur e factor prim!)
{
nrfpb++; // alt factor prim;
fb[nrfpb]=d;
while(b%d == 0) // scot d-urile din n
{
eb[nrfpb]++;
b=b/d;
}
} // if(b%d == 0)

d=d+2; // trec la urmatorul numar impar


} // sf while(d*d <= b)

if(b > 1) // a ramas ultimul factor prim la puterea 1


{
nrfpb++;
fb[nrfpb]=b;
eb[nrfpb]=1;
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 77

}
else
if(b==1) // ca sa fie mai clar ...
{
// nu am ce sa fac aici!
}

// afisez factorii primi si exponentii lor


cout<<endl<<endl;
cout<<"b = "<<bc<<" = "; // o afisare mai draguta ... !!!
cout<<fb[1]; // urmeaza * fpb[i] de eb[i]-1 ori ... !!!
for(j=2; j<=eb[1]; j++) cout<<" * "<<fb[1];
for(i=2; i<=nrfpb; i++)
{
for(j=1; j<=eb[i]; j++) cout<<" * "<<fb[i];
}

cout<<endl;
cout<<"fb[i]: ";
for(i=1; i<=nrfpb; i++) cout<<setw(3)<<fb[i];

cout<<endl;

cout<<"eb[i]: ";
for(i=1; i<=nrfpb; i++) cout<<setw(3)<<eb[i];

//-----------------------------------------------------

// acum calculez cmmmmc ...

i=1; // fa[i[, ea[i]


j=1; // fb[j[, eb[j]
k=1; // fc[k[, ec[k]

while((i <= nrfpa) && (j <= nrfpb))


{
if(fa[i] < fb[j]) // pun fa[i] in fc[k] cu ec[k]=ea[i]
{
fc[k]=fa[i];
ec[k]=ea[i];
i++;
k++;
}
else
if(fa[i] > fb[j]) // pun fb[j] in fc[k] cu ec[k]=eb[j]
{
fc[k]=fb[i];
ec[k]=eb[i];
j++;
k++;
}
else// am pus if(fa[i] == fb[j]) ca sa fie ... mai clar ... !!!
if(fa[i] == fb[j]) // pun max(fa[i],fb[j]) in fc[k]
{
fc[k]=fa[i];
if(ea[i] > eb[j]) // o msingura data la puterea cea mai mare
{
ec[k]=ea[i];
}
else
{
ec[k]=eb[j];
}
i++;
j++;
k++;
}
} // sf while((i <= nrfpa) && (j <= nrfpb))

// o mica verificare in faza de testare ... !!!


cout<<endl<<endl;
cout<<"i = "<<i<<" nrfpa = "<<nrfpa<<endl;
cout<<"j = "<<j<<" nrfpb = "<<nrfpb<<endl;

// verific daca a ramas vreun factor de pus in c


if(i<=nrfpa) // au ramas factori primi din a de plasat in c
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 78

{
while(i<=nrfpa)
{
fc[k]=fa[i];
ec[k]=ea[i];
i++;
k++;
}
} // sf if(i<=nrfpa)
else
if(j<=nrfpb) // au ramas factori primi din b de plasat in c
{
while(j<=nrfpb)
{
fc[k]=fb[j];
ec[k]=eb[j];
j++;
k++;
}
} // if(j<=nrfpb)

// o alta mica verificare in faza de testare ... !!!


cout<<endl;
cout<<"i = "<<i<<" nrfpa = "<<nrfpa<<endl;
cout<<"j = "<<j<<" nrfpb = "<<nrfpb<<endl;

nrfpc=k-1;
cout<<"k = "<<k<<" nrfpc = "<<nrfpc<<endl;

//-----------------------------------------------------

// calculez cmmmc
cmmmmc=1; // initializare pentru un produs ... !!!
for(i=1; i<=nrfpc; i++)
{
for(j=1; j<=ec[i]; j++)
{
cmmmmc = cmmmmc * fc[i];
}
}

//-----------------------------------------------------

// afisez factorii primi si exponentii lor din cmmmmc


cout<<endl;
cout<<"cmmmmc = "<<cmmmmc<<" = "; // o afisare mai draguta ... !!!
cout<<fc[1]; // urmeaza * fpc[i] de ec[i]-1 ori ... !!!
for(j=2; j<=eb[1]; j++) cout<<" * "<<fb[1];
for(i=2; i<=nrfpc; i++)
{
for(j=1; j<=ec[i]; j++) cout<<" * "<<fc[i];
}

cout<<endl;
cout<<"fc[i]: ";
for(i=1; i<=nrfpc; i++) cout<<setw(3)<<fc[i];

cout<<endl;

cout<<"ec[i]: ";
for(i=1; i<=nrfpc; i++) cout<<setw(3)<<ec[i];
//-----------------------------------------------------

return 0;
}
/*
a = 90 = 2 * 3 * 3 * 5
fa[i]: 2 3 5
ea[i]: 1 2 1

b = 84 = 2 * 2 * 3 * 7
fb[i]: 2 3 7
eb[i]: 2 1 1

i = 4 nrfpa = 3
j = 3 nrfpb = 3
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 79

i = 4 nrfpa = 3
j = 4 nrfpb = 3
k = 5 nrfpc = 4

cmmmmc = 1260 = 2 * 2 * 3 * 3 * 5 * 7
fc[i]: 2 3 5 7
ec[i]: 2 2 1 1
*/

3.1.10 Simplificarea fracţiilor


Cu simplificare prin cmmdc obţinut cu algoritmul lui Euclid:

Listing 3.1.28: p028 simplificarea fractiei folosind cmmdc.cpp


#include<iostream> // pentru cout
#include<iomanip>

using namespace std;

int a, b; // de simplificat fractia a/b


int ac, bc; // pentru afisare

int c; // catul impartirii a/b


int r; // restul impartirii a/b

int cmmdcab;// cmmdc(a,b)

int main()
{
a=2*3*3*5; // a/b = ...
b=2*2*3*7;

ac=a; // copie pentru afisarea finala!


bc=b; // copie pentru afisarea finala!

cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;

while(b != 0) // fac impartirea a/b


{
c=a/b; // nu se foloseste dar ... ca sa fie mai clar ...
r=a%b;

a=b;
b=r;
} // sf while(r != 0)

cmmdcab=a;
cout<<"cmmdc("<<ac<<","<<bc<<") = "<<cmmdcab<<endl;

cout<< ac << " / " << bc << " = "


<< ac/cmmdcab << " / " << bc/cmmdcab <<endl;

return 0;
}// sf main
/*
a = 90
b = 84
cmmdc(90,84) = 6
90 / 84 = 15 / 14
*/

Se poate folosi şi descompunerea ı̂n factori, dar ... este prea mult de scris (vezi Listing 3.1.27).

3.1.11 Calculul factorialului

Listing 3.1.29: p029 calcul factorial.cpp


#include<iostream>
#include<iomanip>
CAPITOLUL 3. CLASA A V-A 3.1. ALGORITMI ELEMENTARI 80

using namespace std;

int n; // numar natural cu cel mult 9 cifre

long long nf; // nf = 1*2*3*...*n

int i; // de "plimbat" prin for

int main()
{
n=21; // n > 1

nf=1LL;
for(i=1; i<=n; i++)
{
nf=nf*i;
cout<<i<<"! = "<<setw(20)<<nf<<endl;
}// sf for

return 0;
}// sf main
/*
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 355687428096000
18! = 6402373705728000
19! = 121645100408832000
20! = 2432902008176640000
21! = -4249290049419214848 eronat!
22! = 51090942171709440000 corect!
*/

3.1.12 Ridicare la putere


La matematică aţi studiat:
ˆ Puterea unui număr natural
ˆ Ordinea operaţiilor ı̂n calcule cu puteri
ˆ Reguli de calcul cu puteri
ˆ Pătratul unui număr natural
ˆ Baze de numeraţie

Listing 3.1.30: p030 calcul putere.cpp


#include<iostream>
#include<iomanip>

using namespace std;

int a;
int n; // numar natural cu cel mult 9 cifre

long long an; // an = aˆn

int i; // de "plimbat" prin for

int main()
{
CAPITOLUL 3. CLASA A V-A 3.2. GENERĂRI DE ŞIRURI 81

a=8;
n=22; // n > 1

an=1LL;
for(i=1; i<=n; i++)
{
an=an*a;
cout<<a<<"ˆ"<<setw(2)<<i<<" = "<<setw(20)<<an<<endl;

}// sf for

return 0;
}// sf main
/*
8ˆ 1 = 8
8ˆ 2 = 64
8ˆ 3 = 512
8ˆ 4 = 4096
8ˆ 5 = 32768
8ˆ 6 = 262144
8ˆ 7 = 2097152
8ˆ 8 = 16777216
8ˆ 9 = 134217728
8ˆ10 = 1073741824
8ˆ11 = 8589934592
8ˆ12 = 68719476736
8ˆ13 = 549755813888
8ˆ14 = 4398046511104
8ˆ15 = 35184372088832
8ˆ16 = 281474976710656
8ˆ17 = 2251799813685248
8ˆ18 = 18014398509481984
8ˆ19 = 144115188075855872
8ˆ20 = 1152921504606846976
8ˆ21 = -9223372036854775808 eronat!
8ˆ22 = 0 eronat!
8ˆ21 = 9223372036854775808 corect!
8ˆ22 = 73786976294838206464 corect!
*/

3.2 Generări de şiruri


3.2.1 Progresii aritmetice

Listing 3.2.1: p031 progresii aritmetice.cpp


#include<iostream> // a[i] = a[i-1] + r
#include<iomanip> // a[i] = "vecinul din stanga" + ceva fix

using namespace std;

int a1; // termenul de start


int r; // "ratia", pasul spre urmatorul numar
int n; // numarul termenilor de calculat

int a[101]; // a[0], a[1], a[2], ..., a[99], a[100]

int i; // de "plimbat" prin for

int main()
{
a1=7; // startul sirului de numere
r=3; // ratia ... pasul spre urmatorul numar
n=20; // numarul termenilor

a[1]=a1;
for(i=2; i<=n; i++)
{
a[i]=a[i-1]+r;
}// sf for

for(i=1; i<=n; i++)


{
CAPITOLUL 3. CLASA A V-A 3.2. GENERĂRI DE ŞIRURI 82

cout<<setw(2)<<i<<" ";
}// sf for
cout<<endl;

for(i=1; i<=n; i++)


{
cout<<setw(2)<<a[i]<<" ";
}// sf for
cout<<endl;

return 0;
}// sf main
/*
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
7 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64
*/

3.2.2 Progresii geometrice

Listing 3.2.2: p032 progresii geometrice.cpp


#include<iostream> // a[i] = a[i-1] * q
#include<iomanip> // a[i] = "vecinul din stanga" * ceva fix

using namespace std;

int a1; // primul element, de start


int q; // "ratia", pasul spre urmatorul numar
int n; // numarul termenilor de calculat

int a[101]; // a[0], a[1], a[2], ..., a[99], a[100]

int i; // de "plimbat" prin for

int main()
{
a1=1; // startul sirului de numere
q=2; // ratia ... pasul spre urmatorul numar
n=14; // numarul termenilor

a[1]=a1;
for(i=2; i<=n; i++)
{
a[i]=a[i-1]*q;
}// sf for

for(i=1; i<=n; i++)


{
cout<<setw(4)<<i<<" ";
}// sf for
cout<<endl;

for(i=1; i<=n; i++)


{
cout<<setw(4)<<a[i]<<" ";
}// sf for
cout<<endl;

return 0;
}// sf main
/*
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192
*/

3.2.3 Şirul lui Fibonacci

Listing 3.2.3: p033 sirul lui Fibonacci.cpp


#include<iostream> // f[i] = a[i-1] + f[i-2]; f[0]=0, f[1]=1;
#include<iomanip> // f[i] = suma celor doi vecini din stanga
CAPITOLUL 3. CLASA A V-A 3.2. GENERĂRI DE ŞIRURI 83

using namespace std;

int n; // pana la ce termen calculez!

int f[101]; // a[0], a[1], a[2], ..., a[99], a[100]

int i; // de "plimbat" prin for

int main()
{
n=16;

f[0]=0;
f[1]=1;
for(i=2; i<=n; i++)
{
f[i]=f[i-1]+f[i-2];
}// sf for

for(i=0; i<=n; i++)


{
cout<<setw(3)<<i<<" ";
}// sf for
cout<<endl;

for(i=0; i<=n; i++)


{
cout<<setw(3)<<f[i]<<" ";
}// sf for
cout<<endl;

return 0;
}// sf main
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
*/

Figura 3.5: şirul Fibonacci fără vector

Listing 3.2.4: p033 sirul lui Fibonacci.cpp


#include<iostream> // f[i] = a[i-1] + f[i-2]; f[0]=0, f[1]=1;
#include<iomanip> // f[i] = suma celor doi vecini din stanga

// notez fi=f[i]; fi1=f[i-1]; fi2=f[i-2] ==> fi=fi1+fi2;

using namespace std;

int n; // pana la ce termen calculez!

int fi; // f[i]


int fi1; // f[i-1]
int fi2; // f[i-2]

int i; // de "plimbat" prin for

int main()
CAPITOLUL 3. CLASA A V-A 3.3. FIŞIERE TEXT 84

{
n=16;

for(i=0; i<=n; i++)


{
cout<<setw(3)<<i<<" ";
}// sf for
cout<<endl;

fi2=0;
fi1=1;

cout<<setw(3)<<fi2<<" ";
cout<<setw(3)<<fi1<<" ";
for(i=2; i<=n; i++)
{
fi=fi1+fi2;
cout<<setw(3)<<fi<<" ";

fi2=fi1; // ordinea conteaza !!!


fi1=fi;
}// sf for

return 0;
}// sf main
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
*/

3.3 Fişiere text


3.3.1 Citire număr fix de elemente din fişiere text
OJI 2023 problema ”Castel”:

Date de intrare

Fişierul castel.in conţine:

ˆ pe prima linie două numere naturale C şi N , ı̂n această ordine, despărţite printr-un spaţiu,
unde C reprezintă numărul cerinţei şi poate avea valorile 1, 2 sau 3, iar N are semnificaţia
din enunţ;
ˆ pe a doua linie, N numere naturale despărţite prin câte un spaţiu, reprezentând numerele
scrise pe cuburile galbene, ı̂n ordinea ı̂n care sunt preluate.

Exemple:

castel.in castel.out Explicaţii


1 12 6 C=1 şi sunt 6 cuburi pe care sunt scrise
17 5 11 2 17 17 4 2 2 5 34 88 numere de o singură cifră.
2 12 45 Exemplul corespunde imaginii din enunt,
17 5 11 2 17 17 4 2 2 5 34 88 şi C=2. Cubul din vârful castelului este pe
rândul 4 şi pe el este scris 5.
3 12 6 110 Exemplul corespunde imaginii din enunt,
17 5 11 2 17 17 4 2 2 5 34 88 şi C=3. Sunt 6 cuburi albastre ı̂n castel şi
suma numerelor scrise pe acestea este 110.

Listing 3.3.1: p035 fisier text citire numar precizat de intregi.cpp


#include<iostream> // cls5-oji2023-castel
#include<fstream>
CAPITOLUL 3. CLASA A V-A 3.3. FIŞIERE TEXT 85

using namespace std;

ifstream fin("castel.in");
ofstream fout("castel.out");

int C; // cerinta
int N; // nr de zboruri

int ncg; // numarul scris pe cubul galben citit din fisier


int nrcg1; // nr cuburi galbene de 1 cifra
int rvarf; // randul varfului
int nrca; // nr cuburi albastre

int i, j; // folosit pentru numarat ...

int calcul1() // C=1


{
for(int i=1; i<=N; i++)
{
fin>>ncg;
// ...
}
// ...
fout<<nrcg1;

return 0;
} // sf calcul1()

int calcul2() // C=2


{
// ...
return 0;
} // sf calcul2()

int calcul3() // C=2


{
// ...
return 0;
} // sf calcul3()

int main()
{
fin>>C;
fin>>N;

if(C==1)
calcul1();
else
if(C==2)
calcul2();
else
if(C==3)
calcul3();

fin.close();
fout.close();

return 0;
}

3.3.2 Scriere ı̂n fişiere text


Vezi Listing 3.3.1.

3.3.3 Citire număr variabil de intregi din fişiere text

Listing 3.3.2: p036 fisier text citire numar neprecizat de intregi.cpp


#include<iostream> // 2021bac-iunie-III-3
#include<fstream>
CAPITOLUL 3. CLASA A V-A 3.3. FIŞIERE TEXT 86

using namespace std;

int x;

ifstream fin("2021bac-iunie-III-3.txt");
/*
210
3445 210 893210 1245 1210 3210 15210 67120 20210 12
*/

int main()
{
int val, valv;
int i;
int n;

int ai,ai1; // a[i], a[i+1]

fin>>x;
fin>>valv;

ai=-123;
ai1=-12345;
while(fin>>val)
{

if(val%1000 == x && valv%1000 ==x)


{
ai=valv;
ai1=val;
}
valv=val;
}

if(ai<0)
cout<<"nu exista";
else
cout<<ai<<" "<<ai1;

return 0;
}

Figura 3.6: citire număr neprecizat de numere ı̂ntregi

3.3.4 Citire număr variabil de caractere/cifre din fişiere text


cls 5 OJI 2016 Problema 2 - Palindrom
Date de intrare

Fişierul de intrare palindrom.in conţine pe prima linie un număr C. Pentru toate testele, C
poate lua numai valorile 1, 2 sau 3. Pe a doua linie se află numărul n, iar pe a treia linie cele n
numere naturale despărţite prin câte un spaţiu.

Exemple

palindrom.in palindrom.out
1 5
7
1221 500 53635 505 7 4004 1410
CAPITOLUL 3. CLASA A V-A 3.3. FIŞIERE TEXT 87

Listing 3.3.3: p037 fisier text citire numar neprecizat de caractere.cpp


#include <fstream> // palindrom_Marius
#include <cstring> // cls 5 OJI 2016 Problema 2 "Palindrom"

using namespace std;

char s[12];
int t, n, i, j, k, t1, t2, nr;

int main ()
{
ifstream fin("palindrom.in");
ofstream fout("palindrom.out");

fin>>t>>n;

for (i=1;i<=n;i++)
{
fin>>s;
int nr = 0;
for (j=0, k=strlen(s)-1; j<k; j++, k--)
{
if (s[j] != s[k]) nr++;
}
if (nr == 0) t1++;
if (nr == 1) t2++;

if (t == 3)
{
int number = 0;
int noua = 1;
for (j=0;s[j]!=0;j++)
{
if (s[j] != ’9’) { noua = 0; }
number = number*10 + s[j] - ’0’;
}
if (noua)
{
fout<<1;
for (j=0;s[j+1]!=0;j++) fout<<0;
fout<<"1 ";
continue;
}

if (strlen(s) % 2 == 0)
{
int left = 0;
for (j=0;j<strlen(s)/2;j++)
left = left * 10 + s[j] - ’0’;
int auxleft = left;
int rez = left;
while (left != 0)
{
rez = rez * 10 + left % 10;
left /= 10;
}
if (rez > number)
{
fout<<rez<<" ";
continue;
}
else
{
left = auxleft + 1;
rez = left;
while (left != 0)
{
rez = rez * 10 + left % 10;
left /= 10;
}
fout<<rez<<" ";
continue;
}
}
else
CAPITOLUL 3. CLASA A V-A 3.4. PRELUCRĂRI DE ŞIRURI DE NUMERE CITITE
SUCCESIV, FĂRĂ MEMORAREA LOR 88
{
int left = 0;
for (j=0;j<=strlen(s)/2;j++)
left = left * 10 + s[j] - ’0’;
int auxleft = left;
int rez = left;
left /= 10;
while (left != 0)
{
rez = rez * 10 + left % 10;
left /= 10;
}
if (rez > number)
{
fout<<rez<<" ";
continue;
}
else
{
left = auxleft + 1;
rez = left;
left /= 10;
while (left != 0)
{
rez = rez * 10 + left % 10;
left /= 10;
}
fout<<rez<<" ";
continue;
}
}
}
}

if (t == 1) fout<<t1<<"\n";
if (t == 2) fout<<t2<<"\n";
return 0;
}

3.4 Prelucrări de şiruri de numere citite succesiv, fără


memorarea lor
3.4.1 Determinare maxim/minim

Listing 3.4.1: p038 determinare maxim fara stocarte.cpp


#include<iostream>
#include<fstream>

using namespace std;

ifstream fin("p038 maxim.txt");

/*
3445 210 893210 1245 1210 3210 15210 67120 20210 12
*/

int main()
{
int valc; // valoarea citita
int pozc; // pozitia curenta

int valmax; // valoarea maxima pana la momentul curent


int pozmax; // pozitia maximului in sirul de numere

fin>>valmax; // deocamdata valmax este primul element citit


pozmax=1;
pozc=1;
//cout<<"pozc = "<<pozc<<" valmax = "<<valmax<<endl;

while(fin>>valc)
{
CAPITOLUL 3. CLASA A V-A 3.4. PRELUCRĂRI DE ŞIRURI DE NUMERE CITITE
SUCCESIV, FĂRĂ MEMORAREA LOR 89
pozc++;
//cout<<"pozc = "<<pozc<<" valc = "<<valc<<endl;
if(valc > valmax)
{
valmax=valc;
pozmax=pozc;
}
}

cout<<"valmax = "<<valmax<<" pozmax = "<<pozmax<<endl;

return 0;
}

3.4.2 Determinare primele două maxime

Listing 3.4.2: p039 primele doua maxime distincte fara stocare.cpp


#include<iostream> // fisierul contine numere naturale nenule
#include<fstream> // axista cel putin doua valori distincte in fisier
#include<iomanip>

using namespace std;

ifstream fin("p039 doua maxime.txt");


/*
3445 210 893210 1245 1210 3210 15210 67120 20210 12
*/

int main()
{
int valc; // valoarea citita
int pozc; // pozitia curenta

int valmax1=0; // valoarea maxima1 pana la momentul curent


int pozmax1=0; // pozitia maximului1 in sirul de numere

int valmax2=0; // valoarea maxima2 pana la momentul curent


int pozmax2=0; // pozitia maximului2 in sirul de numere

fin>>valmax1; // deocamdata valmax este primul element citit


pozmax1=1;
pozc=1;

cout<<"valmax1 = "<<valmax1<<" pozmax1 = "<<pozmax1<<endl;


cout<<"valmax2 = "<<valmax2<<" pozmax2 = "<<pozmax2<<endl;
getchar();

while(fin>>valc)
{
pozc++;
cout<<"pozc = "<<pozc<<" valc = "<<valc<<endl;

if(valmax2 == 0) // valmax2 necompletat !


{
if(valc > valmax1)
{
valmax2=valc;
pozmax2=pozc;
}
else
if(valc < valmax1)
{
valmax2=valmax1;
pozmax2=pozmax1;

valmax1=valc;
pozmax1=pozc;
}
else
if(valc == valmax1)
{
// aici nu am ce sa fac !
}
CAPITOLUL 3. CLASA A V-A 3.4. PRELUCRĂRI DE ŞIRURI DE NUMERE CITITE
SUCCESIV, FĂRĂ MEMORAREA LOR 90
}
else // aici este completat si valmax2
{
// unde se plaseaza valc ?
if(valc > valmax2)
{
valmax1=valmax2;
pozmax1=pozmax2;

valmax2=valc;
pozmax2=pozc;
}
else // aici valc <= valmax2
if((valc > valmax1) && (valc < valmax2))
{
valmax1=valc;
pozmax1=pozc;;
}
else // aici valc <= valmax1 sai valc=valmax2
{
// aici nu am ce sa modific!
}
}

cout<<"valmax1 = "<<valmax1<<" pozmax1 = "<<pozmax1<<endl;


cout<<"valmax2 = "<<valmax2<<" pozmax2 = "<<pozmax2<<endl;
getchar();
} // while(fin>>valc)

cout<<"valmax1 = "<<valmax1<<" pozmax1 = "<<pozmax1<<endl;


cout<<"valmax2 = "<<valmax2<<" pozmax2 = "<<pozmax2<<endl;

return 0;
}

3.4.3 p40 Subsecventa crescatoare, fara stocare

Listing 3.4.3: p040 subsecventa crescatoare fara stocare.cpp


#include<iostream> // fisierul contine numere naturale nenule si distincte
#include<fstream> // axista cel putin doua valori distincte in fisier
#include<iomanip> // ? = cea mai lunga subsecventa crescatoare

using namespace std;

ifstream fin("p040 secventa crescatoare.in");

int main()
{
int vala; // valoarea adiacenta stanga cu valoarea curenta valc
int valc; // valoarea curenta citita

int lgmax=0; // lungimea secventei maxime


int lgc=0; // lungimea secventei curente

int poz1max=0; // capatul din stanga al secventei crescatoare maxime


int poz2max=0; // capatul din dreapta al secventei crescatoare maxime

int pozc=0; // pozitia lui valc


int poz1c=0; // capatul din stanga al secventei crescatoare curente
int poz2c=0; // capatul din dreapta al secventei crescatoare curente

fin>>vala;
pozc=1;
//cout<<"pozc = "<<pozc<<" vala = "<<vala<<endl;
cout<<vala<<" ";

poz1c=1;
poz2c=1;

poz1max=1;
poz2max=1;

while(fin>>valc)
CAPITOLUL 3. CLASA A V-A 3.4. PRELUCRĂRI DE ŞIRURI DE NUMERE CITITE
SUCCESIV, FĂRĂ MEMORAREA LOR 91
{
pozc++;
cout<<valc<<" ";
//cout<<"pozc = "<<pozc
// <<" valc = "<<valc
// <<" vala = "<<vala<<endl;
//getchar();

if( valc > vala) // se lungeste secventa


{
poz2c=pozc;
//cout<<"lungesc secventa: poz1c = "
// <<poz1c<<" poz2c = "<<poz2c<<"\n";
}
else // aici este completat si valmax2
if( valc < vala) // s-a terminat secventa
{
poz2c=pozc-1;
lgc=poz2c-poz1c+1;

if(lgc > lgmax)


{
lgmax=lgc;
poz1max=poz1c;
poz2max=poz2c;
//cout<<"poz1max = "<<poz1max
// <<" poz2max = "<<poz2max
// <<" lgmax = "<<lgmax<<endl;
}

// incepe o noua secventa


poz1c=pozc;
poz2c=pozc;
//cout<<"incepe o noua secventa "
// <<" poz1c = "<<poz1c<<" poz2c = "<<poz2c<<endl;;
}

vala=valc; // actualizare valoare anterioara


//cout<<"--------------------------------------------\n";
//getchar();
} // while(fin>>valc)
cout<<endl;

// daca NU verific ultima secventa ... !!!


//cout<<"poz1max = "<<poz1max
// <<" poz2max = "<<poz2max
// <<" lgmax = "<<lgmax<<endl;

//cout<<"poz1c = "<<poz1c<<" poz2c = "<<poz2c<<endl;

if(poz2c - poz1c + 1 > lgmax)


{
poz1max=poz1c;
poz2max = poz2c;
lgmax = poz2c-poz1c+1;
}
// daca VERIFIC ULTIMA ultima secventa ... !!!
cout<<"poz1max = "<<poz1max
<<" poz2max = "<<poz2max
<<" lgmax = "<<lgmax<<endl;

return 0;
}
/*
345 210 8210 125 12 321 1521 671 20210 11 12 13 14 15 16 17
poz1max = 10 poz2max = 16 lgmax = 7
*/

3.4.4 p041 Numararea secventelor, fara stocare

Listing 3.4.4: p041 numararea secventelor fara stocare.cpp


#include<iostream> // fisierul contine numere naturale nenule si distincte
#include<fstream> // axista cel putin doua valori distincte in fisier
CAPITOLUL 3. CLASA A V-A 3.4. PRELUCRĂRI DE ŞIRURI DE NUMERE CITITE
SUCCESIV, FĂRĂ MEMORAREA LOR 92
#include<iomanip> // ? = numarul subsecventelor crescatoare cu lungime >= 3

using namespace std;

ifstream fin("p041 numarul secventelor crescatoare.in");

int main()
{
int vala; // valoarea adiacenta stanga cu valoarea curenta valc
int valc; // valoarea curenta citita

int lgmax=0; // lungimea secventei maxime


int lgc=0; // lungimea secventei curente

int poz1max=0; // capatul din stanga al secventei crescatoare maxime


int poz2max=0; // capatul din dreapta al secventei crescatoare maxime

int pozc=0; // pozitia lui valc


int poz1c=0; // capatul din stanga al secventei crescatoare curente
int poz2c=0; // capatul din dreapta al secventei crescatoare curente

int nrsc3=0; // numarul secventelor crescatoare cu lg >=3

fin>>vala;
pozc=1;
//cout<<"pozc = "<<pozc<<" vala = "<<vala<<endl;
cout<<vala<<" ";

poz1c=1;
poz2c=1;

poz1max=1;
poz2max=1;

while(fin>>valc)
{
pozc++;
cout<<valc<<" ";
//cout<<"pozc = "<<pozc
// <<" valc = "<<valc
// <<" vala = "<<vala<<endl;
//getchar();

if( valc > vala) // se lungeste secventa


{
poz2c=pozc;
//cout<<"lungesc secventa: poz1c = "
// <<poz1c<<" poz2c = "<<poz2c<<"\n";
}
else // aici este completat si valmax2
if( valc < vala) // s-a terminat secventa
{
poz2c=pozc-1;
lgc=poz2c-poz1c+1;

if(lgc >= 3)
{
nrsc3++;
}

// incepe o noua secventa


poz1c=pozc;
poz2c=pozc;
//cout<<"lgc= = "<<lgc<<" nrsc3 = "<<nrsc3<<endl;
//cout<<"incepe o noua secventa "
// <<" poz1c = "<<poz1c<<" poz2c = "<<poz2c<<endl;;
}

vala=valc; // actualizare valoare anterioara


//cout<<"--------------------------------------------\n";
//getchar();
} // while(fin>>valc)
cout<<endl;

// daca NU verific ultima secventa


cout<<"daca NU verific ultima secventa ... nrsc3 = "<<nrsc3<<endl;
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 93

// daca VERIFIC ULTIMA secventa ... !!!


//cout<<"poz1 = "<<poz1c<<" poz2c = "<<poz2c<<endl;
if(poz2c-poz1c+1 >= 3) nrsc3++;

cout<<"daca VERIFIC ULTIMA secventa ... nrsc3 = "<<nrsc3<<endl;

return 0;
}
/*
145 210 893210 1245 1210 3210 15210 67120 20210 12 13 14
daca NU verific ultima secventa ... nrsc3 = 2
daca VERIFIC ULTIMA secventa ... nrsc3 = 3
*/

˜
3.5 Tablouri unidimensionale
3.5.1 p042 Parcurgere vector p042
Problema: Câte valori simetrice sunt ı̂n vector?
Rezolvare: Parcurg vectorul de la stânga spre dreapta,
Figura 3.7:
până la jumătatea vectorului, şi verific dacă poziţiile simetrice parcurgere
sunt egale, vector
adică
if(a[i] == a[n-i+1]).

Listing 3.5.1: p042 parcurgere vector.cpp


#include<iostream> // cate perechi de pozitii simetrice sunt egale
#include<fstream> // n = nr par

using namespace std;

ifstream fin("p042.in");

int a[101];
int n;

int i;
int nrperechi=0; // este initializat automat dar ...

int main()
{
// citire
fin>>n;
cout<<"n = "<<n<<endl; // de control ...

for(i=1; i<=n; i++)


{
fin>>a[i];
cout<<a[i]<<" ";
}
cout<<endl;

for(i=1; i<=n/2; i++)


{
if(a[i] == a[n-i+1]) nrperechi++;
}

cout<<"nrperechi = "<<nrperechi<<endl;

return 0;
}
/*
n = 10
1 2 3 4 5 1 4 5 2 1
nrperechi = 3
*/

3.5.2 p043 Inversare ı̂n vector p043


˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 94

Problema: Să se inverseze ordinea de parcurgere ı̂ntre valoarea


minimă şi valoarea maximă dintr-un vector cu elemente numere
naturale distincte.
Rezolvare: Parcurg vectorul şi determin valoarea minimă amin
şi valoarea maximă amax, dar şi poziţiile acestora (pozmin şi
Figura 3.8:Parcurgerea
pozmax). Parcurg vectorul ı̂ntre cele două poziţii şi inversez elementele. inversare ı̂nse
vector
face
numai până la jumătatea distanţei; altfel elementele iar s-ar ainversa şi ar reveni la locul lor!
Temă: Aceeaşi problemă dar elementele nu sunt distincte!!

Listing 3.5.2: p043 inversare vector.cpp


#include<iostream> // sa se inverseze elementele dintre minim si maxim
#include<fstream> // valorile sunt numere naturale distincte ... !

using namespace std;

ifstream fin("p043.in");

int n;

int a[101];

int i; // de plimbat prin vector


int aux; // pentru permutare valori
int amin; // valoarea minima din vectorul a[ ]
int amax; // valoarea minima din vectorul a[ ]

int pozmin; // pozitia valorii minime


int pozmax; // pozitia valorii maxime

int poz1; // poz1 = min(pozmin,pozmax)


int poz2; // poz2 = max(pozmin,pozmax)

int main()
{
fin>>n;
cout<<"n = "<<n<<endl; // de control ...

fin>>a[1];
amin=a[1]; amax=a[1];
pozmin=1;
pozmax=1;

for(i=2; i<=n; i++)


{
fin>>a[i];
if(a[i] < a[pozmin])
{
pozmin=i;
continue; // nu mai am ce sa mai verific aici
}

if(a[i] >= a[pozmax])


{
pozmax=i;
continue; // nu mai am ce sa mai verific aici
}

// aici amin <= a[i] < amax; nu am ce sa fac aici


}

amin=a[pozmin];
amax=a[pozmax]; // numai ca sa fie mai clar ... !

cout<<"amin = a["<<pozmin<<"] = "<<amin<<endl;


cout<<"amax = a["<<pozmax<<"] = "<<amax<<endl<<endl;

for(i=1; i<=n; i++) // afisare vector initial


{
cout<<a[i]<<" ";
}
cout<<endl;
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 95

if(pozmin < pozmax)


{
poz1=pozmin;
poz2=pozmax;
}
else
{
poz1=pozmax;
poz2=pozmin;
}
// transformare vector
for(i=1; i < (poz1+poz2)/2; i++)
{
aux=a[poz1+i-1];
a[poz1+i-1]=a[poz2-i+1];
a[poz2-i+1]=aux;
}

for(i=1; i<=n; i++) // afisare vector transformat


{
cout<<a[i]<<" ";
}
cout<<endl<<endl;
return 0;
}
/*
n = 9
amin = a[7] = 1
amax = a[2] = 9

7 9 5 4 3 2 1 6 8
7 1 2 3 4 5 9 6 8
*/

3.5.3 p044 Căutare liniara in vector p044


Problema: Să se determine valoarea minimă amin şi valoarea maximă amax dintr-ul vector.
Rezolvare: Iniţializez amin=a[1] şi amax=a[1]. Parcurg vectorul, de la poziţia 2 până la
sfârşitul lui, şi actualizez amin şi amax.

Listing 3.5.3: p045 cautare liniara in vector.cpp


#include<iostream> // cautare liniara in vector
#include<fstream> // cautare valoare minima si valoare maxima

using namespace std;

ifstream fin("p044.in");

int a[101];
int n;

int i;
int amin; // valoarea minima
int amax; // valoarea maxima

int main()
{
// citire
fin>>n;
cout<<"n = "<<n<<endl; // de control ...

for(i=1; i<=n; i++)


{
fin>>a[i];
cout<<a[i]<<" ";
}
cout<<endl;

amin=a[1];
amax=a[1];
for(i=2; i<=n; i++)
{
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 96

if(a[i] < amin)


{
amin=a[i];
}
else
if(a[i]>amax)
{
amax=a[i];
}
else
{
// nu am ce sa fac aici !
}
}

cout<<"amin = "<<amin<<" amax = "<<amax<<endl;

return 0;
}
/*
n = 9
7 9 5 9 3 2 8 2 3
amin = 2 amax = 9
*/

3.5.4 p045 verificare in vector p045


Problema: Să se determine distanţa cea mai mare dintre o valoare minimă şi o valoare maximă,
ı̂ntr-un vector cu elemente numere naturale.
Rezolvare: Considerăm un exemplu de lucru: 795239823
103
Ca să nu păţim ca ı̂n Caragiale cu număratul steagurilor (”Pristanda: Două la prefectură
... paişpce... Tipătescu: (râzând) Le-ai mai numărat pe ale de la prefectură.”) vom analiza ı̂n

mod ”organizat” poziţiile posibile!


Considerăm fixate cele două poziţii ale maximului (cu cel mai mic şi cel mai mare indice).
Apoi, fixăm cel mai mic indice de la minim si analizăm toate poziţiile pe care le poate avea
cel mai mare indice de la minim.

Figura 3.9: verificare proprietati ı̂n vector

Listing 3.5.4: p044 verificare in vector.cpp


#include<iostream> // sa se determine distanta cea mai mare
#include<fstream> // dintre o valoare minima si o valoare maxima

using namespace std;

ifstream fin("p045.in");

int n;

int a[101];

int i; // de plimbat prin vector


int aux; // pentru permutare valori
int amin; // valoarea minima din vectorul a[ ]
int amax; // valoarea minima din vectorul a[ ]

103
http://www.romanianvoice.com/poezii/teatru/ilc-scrisoare pierduta-actul 1.php
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 97

int pozmin1; // pozitia primei valori minime


int pozmin2; // pozitia ultimei valori minime

int pozmax1; // pozitia primei valori maxime


int pozmax2; // pozitia ultimei valori int pozmax; // pozitia valorii maximemaxime

int distmax; // distanta maxima dintre amin si amax

int main()
{
fin>>n;
cout<<"n = "<<n<<endl; // de control ...

fin>>a[1];

amin=a[1];
amax=a[1];

pozmin1=1;
pozmax1=1;

pozmin2=1;
pozmax2=1;

for(i=2; i<=n; i++)


{
fin>>a[i];

if(a[i] < amin)


{
amin=a[i];
pozmin1=i;
pozmin2=i;
continue; // nu mai am ce sa mai verific aici
}

if(a[i] == amin)
{
pozmin2=i;
continue; // nu mai am ce sa mai verific aici
}

if(a[i] > amax)


{
amax=a[i];
pozmax1=i;
pozmax2=i;
continue; // nu mai am ce sa mai verific aici
}

if(a[i] == amax)
{
pozmax2=i;
continue; // nu mai am ce sa mai verific aici
}

// aici nu am ce sa schimb
} // sf for(i=2; i<=n; i++)

for(i=1; i<=n; i++) // aisare vector initial


{
cout<<a[i]<<" ";
}
cout<<endl;

cout<<"amin = "<<amin
<<" pozmin1 = "<<pozmin1<<" pozmin2 = "<<pozmin2<<endl;

cout<<"amax = "<<amax
<<" pozmax1 = "<<pozmax1<<" pozmax2 = "<<pozmax2<<endl;

if(pozmin1<=pozmax1) // cazul 1.
{
if(pozmin2 <= pozmax1) // cazul 1.1
{
distmax = pozmax2-pozmin1;
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 98

cout<<"distmax = "<<distmax<<": "<<pozmin1<<" ... "<<pozmax2<<endl;


}
else
if((pozmin2 >= pozmax1) && (pozmax2 <= pozmax2)) // cazul 1.2
{
distmax = pozmax2-pozmin1;
cout<<"distmax = "<<distmax<<": "<<pozmin1<<" ... "<<pozmax2<<endl;
}
else
if(pozmin2 >= pozmax2) // cazul 1.3
{
if(pozmax2-pozmin1 >= pozmin2-pozmax1)
{
distmax=pozmax2-pozmin1;
cout<<"distmax = "<<distmax<<": "<<pozmin1<<" ... "<<pozmax2<<endl;
}
else
{
distmax = pozmin2-pozmax1;
cout<<"distmax = "<<distmax<<": "<<pozmax1<<" ... "<<pozmin2<<endl;
}
}
} // sf if(pozmin1<=pozmax1) // cazul 1.
else
if((pozmin1 >= pozmax1) && (pozmin1 <= pozmax2)) // cazul 2.
{
if(pozmin2 <= pozmax2) // cazul 2.1
{
if(pozmax2-pozmin1 >= pozmin2-pozmax1)
{
distmax=pozmax2-pozmin1;
cout<<"distmax = "<<distmax<<": "<<pozmin1<<" ... "<<pozmax2<<endl;
}
else
{
distmax = pozmin2-pozmax1;
cout<<"distmax = "<<distmax<<": "<<pozmax1<<" ... "<<pozmin2<<endl;
}
} // sf if(pozmin2 <= pozmax2) // cazul 2.1
else // if ... ca sa fie mai clar ... !
if(pozmin2 >= pozmax2) // cazul 2.2
{
distmax=pozmin2-pozmax1;
cout<<"distmax = "<<distmax<<": "<<pozmax1<<" ... "<<pozmin2<<endl;
}
} // sf if((pozmin1 >= pozmax1) && (pozmin1 <= pozmax2)) // cazul 2.
else // if ca sa fie mai clar ... !
if(pozmin1>=pozmax2) // cazul 3.
{
distmax=pozmin2-pozmax1;
cout<<"distmax = "<<distmax<<": "<<pozmax1<<" ... "<<pozmin2<<endl;
}

distmax = distmax+1; // numarul valorilor inclusiv capetele ... !!!

cout<<"distmax = "<<distmax<<endl;
return 0;
}
/*
n = 9
7 9 5 2 3 9 8 2 3
amin = 2 pozmin1 = 4 pozmin2 = 8
amax = 9 pozmax1 = 2 pozmax2 = 6
distmax = 6: 2 ... 8
distmax = 7
*/

3.5.5 p046 Copiere spre dreapta ı̂n afara vectorului p046


Problema: Un vector are spaţiu suficient de mare, dar elemntele din el sunt mai puţine decât
jumătatea lui. De exemplu: vectorul are 20 de poziţii disponibile, dar valori există numai ı̂n
primele 7 poziţii (vezi figura următoare!). Să se copieze elementele ı̂ncărcate, spre dreapta zonei
ocupate deja, fără să se suprapună şi fără să iasă din spaţiul vectorului!
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 99

Rezolvare: Folosind exemplul din Figura 3.10 stabilim ce relaţie (formulă!) există ı̂ntre o poziţie
i, 1 & i & 7 na şi poziţia ı̂n care trebuie să ajungă valoarea a[i]. Apoi parcurgem vectorul
crescător de la 1 la 7 sau descrescător de la 7 la 1 (aici nu contează, pentru că zonele sunt disjuncte,
dar ... dacă zonele nu sunt disjuncte ... contează!) şi folosim formula găsită.

Figura 3.10: Copiere spre dreapta ı̂n afara vectorului

Listing 3.5.5: p046 copiere in afara vectorului.cpp


#include<iostream>
#include<fstream>
#include<iomanip>

using namespace std;

ifstream fin("p046.in");

int a[101];
int na;
int nrpoz=11; // numarul pozitiilor de deplasare spre dreapta

int main()
{
fin>>na;
cout<<"na = "<<na<<" nrpoz = "<<nrpoz<<endl;

for(int i=1; i <= na; i++)


{
fin>>a[i];
}
cout<<endl;

// afisarea vectorului initial


for(int i=1; i <= na; i++)
{
cout<<setw(2)<<i<<" ";
}
cout<<endl;
for(int i=1; i <= na; i++)
{
cout<<setw(2)<<a[i]<<" ";
}
cout<<endl;

// deplasarea vectorului spre dreapta cu nrpoz pozitii


for(int i=na; i >=1; i--)
{
a[i+nrpoz]=a[i];
}

// este ok si crescator ... aici


//for(int i=1; i <= na; i++)
//{
// a[i+nrpoz]=a[i];
//}
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 100

// afisarea vectorului deplasat


cout<<endl;
for(int i=1; i <= na+nrpoz; i++)
{
cout<<setw(2)<<i<<" ";
}
cout<<endl;
for(int i=1; i <= na+nrpoz; i++)
{
cout<<setw(2)<<a[i]<<" ";
}
cout<<endl;

return 0;
}
/*
na = 7 nrpoz = 11

1 2 3 4 5 6 7
1 2 3 4 5 6 7

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
1 2 3 4 5 6 7 0 0 0 0 1 2 3 4 5 6 7
*/

3.5.6 p047 Copiere un pic spre dreapta p047


Problema: Un vector are spaţiu suficient de mare. Să se copieze elementele ı̂ncărcate, spre
dreapta, câteva poziţii (mai puţine decât numărul elementelor deja ı̂ncărcate).
Rezolvare: Folosind exemplul din Figura 3.11 stabilim ce relaţie (formulă!) există ı̂ntre o poziţie
i, 1 & i & 7 na şi poziţia ı̂n care trebuie să ajungă valoarea a[i]. Apoi parcurgem vectorul
descrescător de la 7 la 1 (aici trebuie neapărat descrescător!) şi folosim formula găsită.

Figura 3.11: Copiere ”un pic” spre dreapta

Listing 3.5.6: p047 copiere un pic spre dreapta.cpp


#include<iostream>
#include<fstream>
#include<iomanip>

using namespace std;

ifstream fin("p047.in");

int a[101];
int na;
int nrpoz=3; // numarul pozitiilor de deplasare spre dreapta

int main()
{
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 101

fin>>na;
cout<<"na = "<<na<<" nrpoz = "<<nrpoz<<endl;

for(int i=1; i <= na; i++)


{
fin>>a[i];
}
cout<<endl;

// afisarea vectorului initial


for(int i=1; i <= na; i++)
{
cout<<setw(2)<<i<<" ";
}
cout<<endl;
for(int i=1; i <= na; i++)
{
cout<<setw(2)<<a[i]<<" ";
}
cout<<endl;

// deplasarea vectorului spre dreapta cu nrpoz pozitii


for(int i=na; i >=1; i--)
{
a[i+nrpoz]=a[i];
}

//crescator ... NU este ok !!!


//for(int i=1; i <= na; i++)
//{
// a[i+nrpoz]=a[i];
//}

// afisarea vectorului deplasat


cout<<endl;
for(int i=1; i <= na+nrpoz; i++)
{
cout<<setw(2)<<i<<" ";
}
cout<<endl;
for(int i=1; i <= na+nrpoz; i++)
{
cout<<setw(2)<<a[i]<<" ";
}
cout<<endl;

return 0;
}
/*
na = 7 nrpoz = 3

1 2 3 4 5 6 7
1 2 3 4 5 6 7

1 2 3 4 5 6 7 8 9 10
1 2 3 1 2 3 4 5 6 7
*/

3.5.7 p048 Inserare vector ı̂n vector p048


Problema: Un vector a[] are spaţiu suficient de mare. Să se insereze, ı̂n acest vector, elementele
vectorului b[], ı̂ncepând cu o poziţie poz (vezi Figura 3.12).
Rezolvare: Folosim exemplul din Figura 3.12. Deplasăm elementele vectorului a[], ı̂ncepând
cu poziţia poz, spre dreapta cu nb poziţii (nb este numărul de elemente din vectorul b[]). Apoi
copiem elementele vectorului b[] ı̂n vectorul a[] ı̂ncepând din poziţia poz din vectorul a[].
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 102

Figura 3.12: Inserare vector ı̂n vector

Listing 3.5.7: p048 inserare vector in vector.cpp


#include<iostream> // sa se insereze b[] in a[] pe pozitia poz
#include<fstream> //
#include<iomanip> // pentru afisare ...

using namespace std;

ifstream fin("p048.in");

int a[101];
int b[101];

int na;
int nb;

int poz; // pozitia de plasat, a lui b[ ] in a[ ]

int main()
{
fin>>na>>nb>>poz;
cout<<"na = "<<na<<" nb = "<<nb<<" poz = "<<poz<<endl<<endl;

for(int i=1; i <= na; i++)


{
fin>>a[i];
}
for(int i=1; i <= na; i++)
{
fin>>b[i];
}

cout<<"i: ";
for(int i=1; i <= na; i++)
{
cout<<setw(3)<<i<<" ";
}
cout<<endl;
cout<<"a: ";
for(int i=1; i <= na; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl<<endl;
//getchar();

cout<<"i: ";
for(int i=1; i <= nb; i++)
{
cout<<setw(3)<<i<<" ";
}
cout<<endl;
cout<<"b: ";
for(int i=1; i <= nb; i++)
{
cout<<setw(3)<<b[i]<<" ";
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 103

}
cout<<endl<<endl;
//getchar();

// deplasare elemente spre dreapta in a[ ]


// nb pozi\c tii, \incepand cu pozitia poz din a[]
for(int i=na; i>=poz; i--)
{
a[i+nb]=a[i];
}

cout<<"dupa deplasare in a[]:\n";


cout<<"i: ";
for(int i=1; i <= na+nb; i++)
{
cout<<setw(3)<<i<<" ";
}
cout<<endl;
cout<<"a: ";
for(int i=1; i <= na+nb; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl<<endl;
//getchar();

// plasare elementele lui b[] in a[ ]


for(int i=1; i<=nb; i++)
{
a[poz+i-1]=b[i];
}

cout<<"dupa inserare b[] in a[]:\n";


cout<<"i: ";
for(int i=1; i <= na+nb; i++)
{
cout<<setw(3)<<i<<" ";
}
cout<<endl;
cout<<"a: ";
for(int i=1; i <= na+nb; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl<<endl;

return 0;
}
/*
na = 7 nb = 5 poz = 4

i: 1 2 3 4 5 6 7
a: 1 2 3 4 5 6 7

i: 1 2 3 4 5
b: 8 8 8 8 8

dupa deplasare in a[]:


i: 1 2 3 4 5 6 7 8 9 10 11 12
a: 1 2 3 4 5 6 7 0 4 5 6 7

dupa inserare b[] in a[]:


i: 1 2 3 4 5 6 7 8 9 10 11 12
a: 1 2 3 8 8 8 8 8 4 5 6 7
*/
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 104

3.5.8 p049 Permutare circulară ı̂n vector p049

Figura 3.13: permutare circulara ı̂n vector - v1

Listing 3.5.8: p049 permutare circulara in vector - v1.cpp


#include<iostream> // sa se deplaseze circular spre dreapta
#include<fstream> // numerele 1 2 3 ... n, cu nrpasi pozitii
#include<iomanip> // afisare ...

using namespace std;

int a[101];
int n=9;
int nrpasi=4; // nr pasilor spre dreapta (circular)

void afisa()
{
cout<<"i: "; // inainte de inserare b[ ]
for(int i=1; i <= n; i++)
{
cout<<setw(3)<<i<<" ";
}
cout<<endl;
cout<<"a: "; // inainte de inserare b[ ]
for(int i=1; i <= n; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl<<endl;
}

int main()
{
for(int i=1; i <= n; i++)
{
a[i]=i;
}
afisa();

// permutare cate un pas


for(int k=1; k<=nrpasi; k++)
{
int aux=a[n]; // copie a lui a[n]
for(int i=n; i>=2; i--)
{
a[i]=a[i-1]; // un pas spre dreapta
}
a[1]=aux;
afisa();
}

return 0;
}
/*
i: 1 2 3 4 5 6 7 8 9
a: 1 2 3 4 5 6 7 8 9

i: 1 2 3 4 5 6 7 8 9
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 105

a: 9 1 2 3 4 5 6 7 8

i: 1 2 3 4 5 6 7 8 9
a: 8 9 1 2 3 4 5 6 7

i: 1 2 3 4 5 6 7 8 9
a: 7 8 9 1 2 3 4 5 6

i: 1 2 3 4 5 6 7 8 9
a: 6 7 8 9 1 2 3 4 5
*/

Figura 3.14: permutare circulara ı̂n vector - v2

Listing 3.5.9: p049 permutare circulara in vector - v2.cpp


#include<iostream> // sa se deplaseze circular spre dreapta
#include<fstream> // numerele 1 2 3 ... n, cu nrpasi pozitii
#include<iomanip> // afisare ...

using namespace std;

int a[101];
int n=9;
int nrpasi=4; // nr pasilor spre dreapta (circular)
int lgafis; // lungimea de afisare a vectorului a[]

void afisa()
{
cout<<"i: "; // inainte de inserare b[ ]
for(int i=1; i <= lgafis; i++)
{
cout<<setw(3)<<i<<" ";
}
cout<<endl;
cout<<"a: "; // inainte de inserare b[ ]
for(int i=1; i <= lgafis; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl<<endl;
}

int main()
{
cout<<"n = "<<n<<" nrpasi = "<<nrpasi<<endl;
for(int i=1; i <= n; i++)
{
a[i]=i;
}
lgafis=n;
afisa();

for(int i=n; i>=1; i--)


{
cout<<i<<" --> "<<i+nrpasi<<endl;
a[i+nrpasi]=a[i]; // un pas spre dreapta
}
cout<<endl;
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 106

lgafis=n+nrpasi;
afisa();

// plasez zona n+1..n+nrpasi im zona 1.. nrpasi


for(int i=1; i<=nrpasi; i++) a[i]=a[n+i];
lgafis=n;
afisa();

return 0;
}
/*
n = 9 nrpasi = 4
i: 1 2 3 4 5 6 7 8 9
a: 1 2 3 4 5 6 7 8 9

9 --> 13
8 --> 12
7 --> 11
6 --> 10
5 --> 9
4 --> 8
3 --> 7
2 --> 6
1 --> 5

i: 1 2 3 4 5 6 7 8 9 10 11 12 13
a: 1 2 3 4 1 2 3 4 5 6 7 8 9

i: 1 2 3 4 5 6 7 8 9
a: 6 7 8 9 1 2 3 4 5
*/

3.5.9 p050 Interclasarea a doi vectori sortaţi p050

Figura 3.15: interclasarea a doi vectori sortaţi

Listing 3.5.10: p050 interclasare vectori sortati.cpp


#include<iostream>
#include<algorithm> // pentru sort
#include<iomanip> // setw pentru afisare mai buna!

#include<fstream>

using namespace std;

ifstream fin("p050 interclasare vectori.in");


˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 107

int a[21];
int b[21];
int c[44];

int na, nb, nc; // nr elementelor din a[], b[], c[]

void afisa()
{
cout<<"a: ";
for(int i=1; i<=na; i++)
{
cout<<setw(2)<<a[i]<<" ";
}
cout<<endl;
}

void afisb()
{
cout<<"b: ";
for(int i=1; i<=nb; i++)
{
cout<<setw(2)<<b[i]<<" ";
}
cout<<endl;
}

void afisc()
{
cout<<"c: ";
for(int i=1; i<=nc; i++)
{
cout<<setw(2)<<c[i]<<" ";
}
cout<<endl;
}

void citire()
{
fin>>na>>nb;

for(int i=1; i<=na; i++) fin>>a[i];


for(int i=1; i<=nb; i++) fin>>b[i];

return;
}

int main()
{
int i,j,k; // i pentru a[i], j pentru b[j], k pentru c[k]

citire();

afisa();
afisb();

nc=na+nb;

i=1;
j=1;
k=1; // aici VOI PUNE a[i] sau b[j]

while((i <=na) && (j <= nb))


{
if(a[i] < b[j]) // plasez a[i] in c[k]
{
c[k]=a[i];;
//cout<<"c["<<k<<"] = a["<<i<<"] = "<<c[k];
k=k+1;
i=i+1;
//cout<<" i = "<<i<<" j = "<<j<<" k = "<<k<<endl;
}
else
if(a[i] > b[j]) // plasez b[j] in c[k]
{
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 108

c[k]=b[j];;
//cout<<"c["<<k<<"] = b["<<j<<"] = "<<c[k];
k=k+1;
j=j+1;
//cout<<" i = "<<i<<" j = "<<j<<" k = "<<k<<endl;
}
else // nu este necesar if-ul urmator, dar ... ca sa fie mai clar!
if(a[i] == b[j]) // plasez a[i] in c[k]
{
c[k]=a[i];
//cout<<"c["<<k<<"] = a["<<i<<"] = "<<c[k];
k=k+1;
i=i+1;
//cout<<" i = "<<i<<" j = "<<j<<" k = "<<k<<endl;
} // while((i <=na) && (j <= nb))
} // sf while

cout<<endl;
cout<<"na = "<<na<<" nb = "<<nb<<" nc = "<<nc<<endl;
cout<<"i = "<<i<<" j = "<<j<<" k = "<<k<<endl;
cout<<endl;

// verific daca a ramas ceva neplasat din a[ ] sau din b[ ]


if(k <= nc) // a ramas ceva nepus din a sau din b[]
{
if(i > na) // a ramas ceva din b[ ]
{
// copiez restul din b[] in c[]
while(j<=nb)
{
c[k]=b[j];
k++;
j++;
}
}
else
if(j > nb) // a ramas ceva din a[ ]
{
// copiez restul din a[] in c[]
while(i<=na)
{
c[k]=a[i];
k++;
i++;
}
}
else
{
// nu se ajunge aici ... !!!
}

afisc();

return 0;
}
/*
a: 3 5 5 7 8 9 9
b: 1 2 2 7 7 8 8 9

na = 7 nb = 8 nc = 15
i = 8 j = 8 k = 15

c: 1 2 2 3 5 5 7 7 7 8 8 8 9 9 9
*/

3.5.10 p051 Vectori caracteristici / de frecvenţă p051


Vectorii de frecvenţă au fost necesari, ı̂n clasa a V-a, la:
ONI 2010 max,
OJI 2011 magic,
ONI 2013 onigim,
˜
CAPITOLUL 3. CLASA A V-A 3.5. TABLOURI UNIDIMENSIONALE 109

ONI 2015 mesaj,


ONI 2018 mostenire,
ONI 2018 pyk,
ONI 2021 iepuras,
ONI 2022 joc,
ONI 2023 patinaj.
Vectorii de frecvenţă sunt utili atunci când numărul valorilor distincte sunt foarte puţine (100,
1.000, chiar şi 1.000.000 uneori!) ı̂n comparaţie cu numărul informaţiilor din fişierul de intrare.
Prelucrarea lor se face ca la orice vector obişnuit!

Listing 3.5.11: p051 vector de frecventa.cpp


// ONI 2023 patinaj - secventa din sursa oficiala

#include <fstream>

#include<iostream> // cout ... pentru afisare pe ecran


#include<iomanip> // setw ... pentru afisare mai buna pe ecran

using namespace std;

const int VMAX = 81;

int nr_a[1 + VMAX], nr_f[1 + VMAX], nr_b[1 + VMAX];

int main()
{
ifstream in("patinaj-test-03.in");
ofstream out("patinaj.out");

int c, n;
int maxsc=0;

in >> c >> n;

cout<<"c = "<<c<<" n = "<<n<<endl;


//getchar();

cout<<" x : ";
for (int i = 0; i < n; i++)
{
int x;
in >> x;
cout<<x<<" ";

int sc = 0;
do
{
sc += x % 10;
x /= 10;
}
while (x != 0);

nr_a[sc]++;
if(sc > maxsc) maxsc=sc;
}
cout<<endl;
//getchar();

cout<<" i : ";
for(int i=0; i <= maxsc; i++)
{
cout<<setw(2)<<i<<" ";
}
cout<<endl;
//getchar();

cout<<"sc[i]: ";
for(int i=0; i <= maxsc; i++)
{
cout<<setw(2)<<nr_a[i]<<" ";
}
cout<<endl;
CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 110

// ...

if (c == 1)
{
// ...
}
else // c == 2
{

} // c == 2

in.close();
out.close();

return 0;
}
/*
c = 1 n = 20
x : 6 1 2 6 2 6 6 1 2 1 1 6 6 6 6 6 2 2 1 6
i : 0 1 2 3 4 5 6
sc[i]: 0 5 5 0 0 0 10
*/

3.6 Sortări
Sortări au fost necesare, ı̂n clasa a V-a, la:
ONI 2014 iepurasi,
ONI 2016 perechi,
ONI 2019 trio.

3.6.1 p052 Sortare cu functia din biblioteca p052


Se poate folosi, chiar şi la clasa a V-a, funcţia de sortare din biblioteca C++, dar ... sunt bune

pentru ”antrenament” cele care urmează aici!

Figura 3.16: sortare cu functia din biblioteca

În Figura 3.16 este o mică ”explicaţie” grafică pentru instrucţiunea de sortare care apare ı̂n
codul sursă: sort(a+1, a+n+1);// sortare crescatoare ...

Listing 3.6.1: p052 sortare cu functia din biblioteca.cpp


#include<iostream>
#include<algorithm> // pentru sort
#include<iomanip> // setw pentru afisare mai buna!
#include<fstream>

using namespace std;

ifstream fin("p052 sortare cu functia din biblioteca.in");

int n;
int a[101];

void afisa()
{
CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 111

cout<<"a: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl;

return;
}

int main()
{
fin>>n;
for(int i=1; i<=n; i++) fin>>a[i];

cout<<"inainte de sortare:"<<endl;
afisa();

sort(a+1, a+n+1);// sortare crescatoare ...

afisa();
cout<<"dupa sortare"<<endl;

return 0;
}
/*
inainte de sortare:
a: 7 1 3 5 2 9 8 2 4
a: 1 2 2 3 4 5 7 8 9
dupa sortare
*/

3.6.2 p053 Sortare prin selecţie p053

Listing 3.6.2: p053 sortare prin selectie.cpp


#include<iostream>
#include<algorithm> // pentru sort
#include<iomanip> // setw pentru afisare mai buna!
#include<fstream>

using namespace std;

ifstream fin("p053 sortare prin selectie.in");

int n;
int a[101];

void afisa()
{
cout<<"a: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl;

return;
}

int main()
{
fin>>n;
for(int i=1; i<=n; i++) fin>>a[i];

cout<<"inainte de sortare:"<<endl;
afisa();

// sortare prin selectie minim spre dreapta


int valmin,k,aux;
for(int i=1;i<=n-1;i++) Figura 3.17: sortare prin se-
{ lectie
valmin=a[i];
k=i; // pozitia minimului
CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 112

for(int j=i+1;j<=n;j++)
{
if(a[j]<valmin)
{
valmin=a[j];
k=j;
}//if
}//forj
aux=a[k];
a[k]=a[i];
a[i]=aux;// plasez min a[j] pe pozitia lui a[i]
afisa();
} // for i

afisa();
cout<<"dupa sortare"<<endl;

return 0;
}
/*
inainte de sortare:
a: 7 1 3 5 2 9 8 2 4
a: 1 7 3 5 2 9 8 2 4
a: 1 2 3 5 7 9 8 2 4
a: 1 2 2 5 7 9 8 3 4
a: 1 2 2 3 7 9 8 5 4
a: 1 2 2 3 4 9 8 5 7
a: 1 2 2 3 4 5 8 9 7
a: 1 2 2 3 4 5 7 9 8
a: 1 2 2 3 4 5 7 8 9
a: 1 2 2 3 4 5 7 8 9
dupa sortare
*/

3.6.3 p054 Sortare prin inserţie p054

Listing 3.6.3: p054 sortare prin insertie.cpp


#include<iostream>
#include<algorithm> // pentru sort
#include<iomanip> // setw pentru afisare mai buna!

#include<fstream>

using namespace std;

ifstream fin("p054 sortare prin insertie.in");

int n;
int a[101];

void afisa()
{
cout<<"a: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl;

return;
}

int main()
{
int i, j; // indici de plimbat prin vector!
int aux; // pentru permutare valori

fin>>n;
for(int i=1; i<=n; i++) fin>>a[i];

cout<<"inainte de sortare:"<<endl;
afisa();

Figura 3.18: sortare


prin insertie
CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 113

// sortare prin insertie


for (i = 2; i <= n; i++)
{
aux = a[i]; // valoarea de plasat pe pozitia corecta
for (j=i; j > 0 && a[j-1] > aux; j--)
{
a[j]=a[j-1]; // deplasarea elementelor in vector
}
a[j] = aux;

afisa();
}

afisa();
cout<<"dupa sortare"<<endl;

return 0;
}
/*
inainte de sortare:
a: 7 1 3 5 2 9 8 2 4
a: 1 7 3 5 2 9 8 2 4
a: 1 3 7 5 2 9 8 2 4
a: 1 3 5 7 2 9 8 2 4
a: 1 2 3 5 7 9 8 2 4
a: 1 2 3 5 7 9 8 2 4
a: 1 2 3 5 7 8 9 2 4
a: 1 2 2 3 5 7 8 9 4
a: 1 2 2 3 4 5 7 8 9
a: 1 2 2 3 4 5 7 8 9
dupa sortare
*/

3.6.4 p055 Sortare prin metoda bulelor p055

Listing 3.6.4: p055 sortare prin metoda bulelor.cpp


#include<iostream>
#include<algorithm> // pentru sort
#include<iomanip> // setw pentru afisare mai buna!
#include<fstream>

using namespace std;

ifstream fin("p055 sortare prin metoda bulelor.in");

int n;
int a[101];

void afisa()
{
cout<<"a: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl;

return;
}

int main()
{
bool ok; // ok=true --> vectorul este sortat deja
int i, k; // indici de plimbat prin vector
int aux; // pentru permutat valori
Figura 3.19: sortare prin
fin>>n;
for(int i=1; i<=n; i++) fin>>a[i]; metoda bulelor

cout<<"inainte de sortare:"<<endl;
afisa();

// sortare prin metoda bulelor


CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 114

ok=false; // presupun ca sirul nu este sortat (oricum trebuie sa verific!)


while(ok==false)
{
ok=true; // presupun ca ar fi deja sortat
for(i=1; i<=n-1; i++)
{
if(a[i] > a[i+1])
{
aux=a[i];
a[i]=a[i+1];
a[i+1]=aux;
ok=false; // interschimbarea --> nu a fost deja sortat
}// sf if
} // sf for

afisa();
} // sf while

afisa();
cout<<"dupa sortare"<<endl;

return 0;
}
/*
inainte de sortare:
a: 7 1 3 5 2 9 8 2 4
a: 1 3 5 2 7 8 2 4 9
a: 1 3 2 5 7 2 4 8 9
a: 1 2 3 5 2 4 7 8 9
a: 1 2 3 2 4 5 7 8 9
a: 1 2 2 3 4 5 7 8 9
a: 1 2 2 3 4 5 7 8 9
a: 1 2 2 3 4 5 7 8 9
dupa sortare
*/

3.6.5 p056 Sortare cu vector de frecvenţă p056


Sortare prin numărare se realizează folosind vecor de frecvenţă şi, desigur, se foloseşter atunci
când există o plajă mică de valori (raportat la numerele din fişierul de intrare!).

Listing 3.6.5: p056 sortare prin frecventa.cpp


#include<iostream> // Counting Sort
#include<algorithm> // pentru sort
#include<iomanip> // setw pentru afisare mai buna!
#include<fstream>

using namespace std;

ifstream fin("p056 sortare prin frecventa.in");

int n;
int a[101];
int fr[101]; // vectorul de frecventa; este initializat automat cu 0

int valmina; // valoarea minima din a[]


int valmaxa; // valoarea axima din a[]

void afisa()
{
cout<<"a: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl;

return;
}

void afisfr() // afisare vector de frecvente


{
CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 115

cout<<" i: ";
for(int i=valmina; i<=valmaxa; i++)
{
cout<<setw(2)<<i<<" ";
}
cout<<endl;
cout<<"fr: ";
for(int i=valmina; i<=valmaxa; i++)
{
cout<<setw(2)<<fr[i]<<" ";
}
cout<<endl;
return;
}

int main()
{
fin>>n;
fin>>a[1];

valmina=a[1];
valmaxa=a[1];
fr[a[1]]++;

for(int i=2; i<=n; i++)


{
fin>>a[i];
if(a[i] < valmina)
{
valmina=a[i];
}
else
if(a[i] > valmaxa)
{
valmaxa=a[i];
}
else
{
// aici nu am nimic de facut!
}

fr[a[i]]++;
} // for i

cout<<"valmina = "<<valmina<<" valmaxa = "<<valmaxa<<endl<<endl;;

cout<<"inainte de sortare:"<<endl;
afisa();

cout<<endl;
afisfr();
cout<<endl;

// sortare prin numarare (reconstruire vector a[ ] sortat


int poz=1; // pozitia pentru plasat valoare in vector
for(int i=valmina;i<=valmaxa;i++)
{
//cout<<"i = "<<i<<" j = ";
for(int j=1; j<=fr[i]; j++)
{
a[poz]=i;
poz++;
//cout<<j<<" ";
} // for j
//cout<<endl;
} // for i

afisa();
cout<<"dupa sortare"<<endl;

return 0;
}
/*
valmina = 2 valmaxa = 7

inainte de sortare:
CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 116

a: 7 2 3 5 2 7 6 2 7

i: 2 3 4 5 6 7
fr: 3 1 0 1 1 3

a: 2 2 2 3 5 6 7 7 7
dupa sortare
*/

3.6.6 p057 Sortare prin numărare p057


Sortare prin numărare (Counting Sort) se realizează folosind ...

Listing 3.6.6: p057 sortare prin numarare.cpp


#include<iostream> // tot Counting Sort se nuymeste !
#include<algorithm> // pentru sort
#include<iomanip> // setw pentru afisare mai buna!
#include<fstream>

/*
Fie vectorul a. Strategia consta in a numara pentru fiecare
a[i] cate elemente mai mici decat el exista; numerele astfel
obtinute le vom memora in vectorul c. Pe baza acestui vector,
rearanjam elementele lui a in vectorul b.
*/

using namespace std;

ifstream fin("p057 sortare prin numarare.in");

int n;
int a[101]; // bvectorul a nesortat
int b[100]; // vectorul a[] sortat
int c[100]; // c[i] = cate elemente sunt mai mici decat a[i]

void afisi()
{
cout<<"i: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<i<<" ";
}
cout<<endl;

return;
}

void afisa()
{
cout<<"a: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<a[i]<<" ";
}
cout<<endl;

return;
}

void afisb()
{
cout<<"b: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<b[i]<<" ";
}
cout<<endl;

return;
}

void afisc()
{
CAPITOLUL 3. CLASA A V-A 3.6. SORTĂRI 117

cout<<"c: ";
for(int i=1; i<=n; i++)
{
cout<<setw(3)<<c[i]<<" ";
}
cout<<endl;

return;
}

int main()
{
int i, j; //indici de plimbat prin vector

fin>>n;
for(int i=1; i<=n; i++) fin>>a[i];

cout<<"inainte de sortare:"<<endl;
afisi();

afisa();

for(int i=1; i<=n; i++) c[i]=1; // pozitia lui a[i] in vectorul sortat

for(i=1;i<=n;i++)
{
for(j=1;j<=i-1;j++)
{
//cout<<"i = "<<i<<" j = "<<j<<" --> "<<a[i]<<" "<<a[j]<<endl;
//getchar();
if(a[j]<a[i])
c[i]++;
else
c[j]++;
} // sf for j
} // sf for i
afisc();

for(i=1;i<=n;i++)
{
b[c[i]]=a[i]; //
}

afisb(); // aste a[] dupa sortare


cout<<"dupa sortare"<<endl;

return 0;
}
/*
inainte de sortare:
i: 1 2 3 4 5 6 7 8 9
a: 7 1 3 5 2 9 8 2 4
c: 7 1 4 6 3 9 8 2 5
b: 1 2 2 3 4 5 7 8 9
dupa sortare
*/
Part I

Olimpiada Europeană de
104
Informatică pentru juniori

104
Este organizată după regulile şi standardele Olimpiadei Internaţionale de Informatică (IOI).
Potrivit regulamentului, pot fi ı̂nscrişi elevi din ı̂nvăţământul secundar, cu vârsta de până la 15 ani, fiecare echipă
având doi profesori coordonatori şi maximum 4 elevi.

118
Capitolul 4
105
EJOI 2023

4.1
Problema 1 - 100 de puncte

Date de intrare

Date de ieşire

Exemplu
Intrare:

Ieşire:

Explicaţie

Punctaj

4.1.1 Indicaţii de rezolvare

4.1.2 Coduri sursă

4.1.3 *Rezolvare detaliată

4.2
Problema 2 - 100 de puncte

Exemplu
Explicaţii

Punctaj
105
aur: Iorgulescu Andrei Paul, Liceul Teoretic Internaţional de Informatică, Bucureşti
. argint: Petrean Roland, Şcoala Gimnazială ”Simion Bărnuţiu”, Zalău
. argint: Mureşan Luca Valentin, Colegiul Naţional ”Mihai Eminescu”, Satu Mare
. argint: Neacşu Matei, Liceul Teoretic Internaţional de Informatică, Bucureşti

119
CAPITOLUL 4. EJOI 2023 4.3. 120

4.2.1 Indicaţii de rezolvare

4.2.2 Coduri sursă

4.2.3 *Rezolvare detaliată

4.3
Problema 3 - 100 de puncte

Date de intrare
Date de ieşire

Exemplu
Date de intrare:
...

Date de ieşire:
...

Punctaj

4.3.1 Indicaţii de rezolvare

Author:
Developer:
Editorialist:

4.3.2 Coduri sursă

4.3.3 *Rezolvare detaliată


Capitolul 5
106
EJOI 2022

5.1 adjacent
Problema 1 - Perechi adiacente 100 de puncte
Vom numi un şir b1 , b2 , ..., bm bun, dacă bi j bi1 pentru orice i cu 1 & i & m  1.
Se dă un şir bun de n numere ı̂ntregi pozitive a1 , a2 , a3 , ..., an .
Puteţi efectua următoarea operaţie asupra acestui şir:
Alegeţi un indice i (1 & i & n) şi un număr x (1 & x & 10 ). Apoi atribuiţi lui a valoarea x.
9
ˆ

După această operaţie şirul trebuie să rămână bun.


Trebuie să efectuaţi un număr de asemenea operaţii astfel ı̂ncât şirul rezultat să conţină exact două
valori distincte. Determinaţi numărul minim de operaţii necesare pentru a obţine acest lucru.
Date de intrare
Datele de intrare conţin mai multe scenarii de test. Prima linie conţine numărul ı̂ntreg t
(1 & t & 10), reprezentând numărul de teste. Descrierea unui test urmează:
Prima linie a fiecărui test conţine un singur număr ı̂ntreg n (2 & n & 2 10 ), reprezentând
5

lungimea şirului.
A doua linie a fiecărui test conţine n numere ı̂ntregi a1 , a2 , ..., an (1 & ai & n), reprezentând
elementele şirului. Se garantează că ai j ai1 pentru 1 & i & n  1 (adică şirul dat este bun).
5
Se garantează că suma valorilor lui n pentru toate testele nu depăşeşte 2 10 .
Date de ieşire
Pentru fiecare test, afişaţi un singur număr ı̂ntreg - cel mai mic număr de operaţii necesare
pentru a obţine un şir ı̂n care sunt exact două valori distincte.
Exemplu
Intrare:

2
5
4 5 2 4 5
2
1 2

Ieşire:

3
0
106
aur: SAVU Ştefan Cătălin, Colegiul Naţional ”I.L. Caragiale”/CJEX, Ploieşti
. aur: VOICU Mihai Valeriu, Liceul Teoretic Internaţional de Informatică, Bucureşti
. argint: CHENGLIN Shang, Liceul Teoretic Internaţional de Informatică, Bucureşti
. argint: REBENGIUC Mircea, C.N. de Informatică ”Tudor Vianu”’, Bucureşti
. argint: DUMITRU Alexandru, C.N. ”I.L. Caragiale” / CJEX, Ploieşti
. argint: DIMA Alexandru, Şcoala Gimnazială Internaţională ”‘Spectrum”’, Cluj Napoca
. btonz: GUBA Daniel, Liceul Teoretic Internaţional de Informatică, Bucureşti
. menţiune de onoare: MATEESCU Alexandru, Liceul Teoretic Internaţional de Informatică, Bucureşti

121
CAPITOLUL 5. EJOI 2022 5.1. ADJACENT 122

Explicaţie
Pentru primul test, o secvenţă optimă de operaţii este:
(4, 5, 2, 4, 5) (2, 5, 2, 4, 5) (2, 5, 2, 4, 2) (2, 5, 2, 5, 2).
Pentru al doilea test, şirul deja conţine doar două valori distincte, deci răspunsul este 0.
Punctaj
1. (20 puncte): Suma valorilor lui n pentru toate testele nu depăşeşte 100
2. (10 puncte): Suma valorilor lui n pentru toate testele nu depăşeşte 500
3. (25 puncte): Suma valorilor lui n pentru toate testele nu depăşeşte 4000
4. (45 puncte): Fără restricţii suplimentare

5.1.1 Indicaţii de rezolvare

Author: Jeroen Op de Beek


Developer: Jeroen Op de Beek
Editorialist: Jeroen Op de Beek

Subtask 1. Let’s call an array good if all adjacent pairs of elements are different. At the beginning
the array is good, and after each operation the array will stay good. So the ending array must
have the form: [c, d, c, d, ...]; c j d . This means that there are O n  possible ending states.
2

(For any ending state with c or d not in r1, 2, ..., nx, it doesn’t matter what the value is exactly,
so there are only O n interesting options for those cases).
For this subtask we can try all pairs.
So we fix some c and d. Let’s call the final array b  c, d, c, d, ....
Then we greedily make moves, trying to change elements in a to b. It could happen that a j b
but no greedy move is possible:
a 1, 2, 3, 1, 2
b 1, 2, 1, 2, 1
The last three elements of the array cannot be changed greedily, because this would cause
adjacent equal valued elements.
Such a blockage is always caused by some subarray that is of the form d, c, d, c, ... which is
the desired pattern, but with the wrong parities. Let’s call subarrays which have values d and c at
indices of the wrong parity and that can’t be extended further bad subarrays. It turns out that
9
changing the second element of any bad subarray to 10 is optimal (for the proof, see subtask 2).
So a simple algorithm will try to find a greedy move, if there’s no greedy move, it finds any
starting point of a bad subarray and changes the second element.
This can be implemented to run in O n per iteration, and there are at most O n iterations
4
per pair of c and d, so this will run in O n , with a small constant.
Subtask 2. Instead of simulating the process of converting the array we can calculate the
number of moves needed more directly.
Firstly, for each c and d we find all bad subarrays, with a single for loop.
For a bad subarray of size k it’s optimal to first do k ©2$ extra moves, where we place the
9
value 10 on positions 2, 4, 6, ... of the subarray. After this, the whole subarray can be finished
greedily.
To show that this is the best we can do, let’s consider all possible moves we can make on this
subarray.
When a value in the subarray is replaced, we are always left with two bad subarrays of sizes
l and r, such that k l  r  1. Notice that for a bad subarray of size 1, there’s no problem.
Because it is maximal, the elements around it cannot be equal to c and d, but this means the
only element of the subarray can be greedily changed. By induction on the size of the subarray,
and basecases 0 and 1 the lowerbound of k ©2$ extra moves can be proven. The formula for the
number of moves needed will be:
#moves n  # of i, such that ai bi   = k ©2$
bad subarrays
CAPITOLUL 5. EJOI 2022 5.1. ADJACENT 123

3
Now the time per pair is reduced to O n, and the total complexity is O n .

Subtask 3. It’s intuitively clear that values that appear more frequent are better candidates
for c or d.
2
We can show that instead of O n  pairs, we can only examine the O n best pairs. For all
pairs c and d, calculate

#greedy moves n # of i, such that ai bi 

and sortsort them increasingly on this quantity. To calculate this value in O 1 per pair, we can
count the number of occurrences of x (1 & x & n) at odd and even indices as a precomputation
step.
Notice that the total number of bad subarrays over all pairs c and d is O n (here we ignore bad
subarrays of length 1, because they don’t change the answer). This is because two bad subarrays
cannot overlap by more than 1 element. So there are only O n pairs of c and d that cannot
immediately be finished by greedy moves.
So by the pigeonhole principle, if we examine more pairs than this, we will always have at least
one pair with no bad subarrays.
After this point no other pairs in the increasing order of greedy moves can give a better answer,
so we can break the loop early.
With this observation we only need to check O n pairs, and the time complexity becomes
2 2
O n  or O n log n, depending on the implementation.

Subtask 4. Instead of finding bad subarrays for each pair c and d independently, all bad
subarrays can be found at once, with a single pass through array a, with some simple logic. We
can use a map to store c, d extra moves needed, and add  bad subarray size / 2 $ to the map
appropriately.
Now for finding the best c, d pair, iterate through all possible c, the final value of all odd
indices of the array.
For the value of d, we can loop through all d in decreasing order of occeven d (the number of
occurrences of d in even positions). By reusing the same observation as subtask 3, we can break
this loop over d as soon as c, d can be solved with only greedy moves (i.e. it isn’t in the map).
The total number of iterations of the inner for loop is bounded by (size of the map) + n = O n.
This gives an O n log n algorithm, because of the sorting of d and the map. By changing
out the map to a hash map, and changing the sorting algorithm to counting sort, O n is also
possible.
For deterministic linear time, with clever use of a global array that is reused between the
iterations of c, the hashmap is no longer needed. These optimizations were not needed to obtain
the full score.

5.1.2 Coduri sursă

Listing 5.1.1: EJOI2022 Perechi ModelSolution.cpp


#include "bits/stdc++.h"

using namespace std;

#define all(x) begin(x),end(x)

//template<typename A, typename B> ostream& operator<<(ostream &os, const pair<A, B> &p)
// { return os << ’(’ << p.first << ", " << p.second << ’)’; }

//template<typename T_container, typename T = typename enable_if<!is_same<T_container,


string>::value,
// typename T_container::value_type>::type> ostream& operator<<(ostream &os, const
T_container &v)
// { string sep; for (const T &x : v) os << sep << x, sep = " "; return os; }
CAPITOLUL 5. EJOI 2022 5.1. ADJACENT 124

//#define debug(a) cerr << "(" << #a << ": " << a << ")\n";

//typedef long long ll;


typedef vector<int> vi;
//typedef vector<vi> vvi;
typedef pair<int,int> pi;

const int mxN = 1e5+1, oo = 1e9;

int main()
{
//-----------------------------------------------------
std::clock_t c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();

auto t1 = clock();

std::freopen("../tests/186", "r", stdin) ;


std::freopen("perechi.out", "w", stdout) ;
//-----------------------------------------------------

int t;
cin >> t;

while(t--)
{
int n;
cin >> n;

vector<int> a(n);
for(int& i : a)
cin >> i, --i;

int ans=n; // just change the numbers to the pattern 10**9-1, 10**9 ...
array<vi,2> cnt = {vi(n),vi(n)};
for(int i=0;i<n;++i)
{
cnt[i%2][a[i]]++;
}

// handle case where one of the two distinct is 10**9


for(int rep=0;rep<2;++rep)
{
ans = min(ans,n- *max_element(all(cnt[rep])));
}

vi ord(n);
iota(all(ord),0);
sort(all(ord),[&](int i,int j) {return cnt[1][i]>cnt[1][j];});

// handle case where both are in the array


map<pi,int> cost; // cost for if pair (c,d) is used (with a on the even parities
(0,2,4,6), b on the odd parities (1,3,5))
for(int i=0;i+1<n;)
{
int c = a[i], d = a[i+1];
if(i%2==0) swap(c,d);
int j=i+2;
while(j<n and a[j]==a[j-2]) ++j;
cost[{c,d}]+=(j-i)/2;
i=j-1;
}
vi evenCnt(n);
for(int i=0;i<n;i+=2) evenCnt[a[i]]++;
for(int c=0;c<n;++c)
{
for(auto d : ord) if(c!=d)
{
int cur = n-cnt[0][c]-cnt[1][d];
if(!cost.count({c,d}))
{
ans = min(ans,cur);
break;
}
else
{
CAPITOLUL 5. EJOI 2022 5.1. ADJACENT 125

ans = min(ans,cur+cost[{c,d}]);
}
}
// the second for loop runs only O(n) times total (the number of pairs in
the cost map + n iterations)
}
cout << ans << ’\n’;
}

// ----------------------------------------------------------------
auto t2 = clock();
std::clock_t c_end = std::clock();
auto t_end = std::chrono::high_resolution_clock::now();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;

std::cout << std::fixed << std::setprecision(2)


<< "\nCPU time used: "
<< 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
<< "Wall clock time passed: "
<< std::chrono::duration<double, std::milli>(t_end-t_start).count()
<< " ms\n";

return 0;
// ----------------------------------------------------------------
}

Listing 5.1.2: EJOI2022 Perechi check.cpp

#include "testlib.h"
#include <sstream>

using namespace std;

int main()
//int main(int argc, char * argv[])
{
//-----------------------------------------------------------------------
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/186", // input
(char*)"../tests/186.a", // rezultat corect
(char*)"perechi.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

//registerChecker("perechi", argc, argv);


//-----------------------------------------------------------------------

setName("compare ordered sequences of signed int%d numbers", 8 * int(sizeof(long


long)));

registerTestlibCmd(argc, argv);

int n = 0;
string firstElems;

while (!ans.seekEof() && !ouf.seekEof())


{
n++;
long long j = ans.readLong();
long long p = ouf.readLong();
if (j != p)
quitf(_wa, "%d%s numbers differ - expected: ’%s’, found: ’%s’", n,
CAPITOLUL 5. EJOI 2022 5.2. ROOT 126

englishEnding(n).c_str(), vtos(j).c_str(), vtos(p).c_str());


else
if (n <= 5)
{
if (firstElems.length() > 0)
firstElems += " ";
firstElems += vtos(j);
}
}

int extraInAnsCount = 0;

while (!ans.seekEof())
{
ans.readLong();
extraInAnsCount++;
}

int extraInOufCount = 0;

while (!ouf.seekEof())
{
ouf.readLong();
extraInOufCount++;
}

if (extraInAnsCount > 0)
quitf(_wa, "Answer contains longer sequence [length = %d], but output contains %
d elements", n + extraInAnsCount, n);

if (extraInOufCount > 0)
quitf(_wa, "Output contains longer sequence [length = %d], but answer contains %
d elements", n + extraInOufCount, n);

if (n <= 5)
quitf(_ok, "%d number(s): \"%s\"", n, compress(firstElems).c_str());
else
quitf(_ok, "%d numbers", n);
}
/* la verificare apare:
argc = 4
checker
../tests/186
../tests/186.a
perechi.out
----------------------
ok 1 number(s): "199390"

Process returned 0 (0x0) execution time : 0.079 s


Press any key to continue.
*/

5.1.3 *Rezolvare detaliată

5.2 root
Problema 2 - Unde este rădăcina? 100 de puncte
Aceasta este o problemă interactivă.
Vi se dă un arborearbore cu n noduriU̇n arbore este un graf ı̂n care există un singur drum ı̂ntre
oricare două noduri. De asemenea, se garantează că există cel puţin un nod care este conectat
direct prin câte o muchie de cel puţin alte 3 noduri. Unul dintre noduri este rădăcina arborelui,
iar obiectivul vostru este să găsiţi această rădăcină. Pentru a rezolva problema, veţi putea pune
interogări de forma:

ˆ Pentru o mulţime de noduri a1 , a2 , ..., am , verifică dacă cel mai apropiat strămoş comun al
nodurilor din mulţime se află de asemenea ı̂n mulţime.
Un nod v este un strămoş comun al unei mulţimi de noduri S dacă drumurile de la fiecare nod
CAPITOLUL 5. EJOI 2022 5.2. ROOT 127

din S la rădăcină trec prin nodul v. Cel mai apropiat strămoş comun al mulţimii de noduri S este
strămoşul comun al mulţimii S aflat la distanţă maximă faţă de rădăcină.

Interacţiune
Porniţi interacţiunea prin citirea unui singur număr ı̂ntreg n (4 & n & 500) - numărul de
noduri.
Citiţi apoi următoarele n  1 linii. A i-a linie va conţine doi ı̂ntregi a, b (1 & a, b & n), care
semnifică existenţa unei muchii ı̂ntre nodurile a şi b din arbore.
Se garantează că cele n  1 muchii formează un arbore şi că există cel puţin un nod care este
conectat direct prin câte o muchie de cel puţin alte 3 noduri.
Pentru a trimite o interogare, afişaţi mai ı̂ntâi caracterul ” ? ”, apoi numărul ı̂ntreg m, apoi
m numere ı̂ntregi distincte a1 , a2 , ..., am (1 & m & n, 1 & a & n, toate a distincte) - mulţimea de
noduri pentru care vreţi să verificaţi dacă cel mai apropiat strămoş comun se află printre ele.
Ca răspuns, interactorul va afişa ” YES ” dacă cel mai apropiat strămoş comun al mulţimii
interogate este unul dintre nodurile a1 , a2 , ..., am , sau ” NO ” ı̂n caz contrar.
Puteţi pune cel mult 1000 de interogări, dar veţi fi punctaţi diferit ı̂n funcţie de numărul lor.
Afişarea răspunsului final nu se va număra ca interogare. Citiţi cu atenţia secţiunea Punctaj de
mai jos.
Atunci când aţi identificat rădăcina, afişaţi caracterul ” ! ” urmat de un număr ı̂ntreg v
(1 & v & n) - rădăcina. Apoi terminaţi execuţia programului vostru.
După afişarea unei interogări nu uitaţi să afişaţi caracterul pentru final de linie (newline) şi să
goliţi (flush) outputul. Pentru aceasta, folosiţi:

ˆ fflush(stdout) sau cout.flush() pentru C++;


ˆ stdout.flush() pentru Python;

Se garantează că pentru toate testele arborele şi rădăcina sa sunt fixate ı̂nainte de ı̂nceperea
interacţiunii. Cu alte cuvinte, interactorul nu este adaptiv.
Exemplu

Input:
7
4 1
1 2
4 3
3 5
3 6
4 7
Output:
? 2 5 6
Input:
NO
Output:
? 3 6 3 5
Input:
YES
Output:
? 2 1 7
Input:
NO
Output:
? 2 4 6
Input:
YES
Output:
! 4

Explicaţii
CAPITOLUL 5. EJOI 2022 5.2. ROOT 128

Rădăcina ascunsă este nodul 4.


Pentru prima interogare, cel mai apropiat strămoş comun al nodurilor 5 şi 6 este nodul 3 care
nu se află printre nodurile 5 şi 6, deci răspunsul este ” NO ”.
Pentru a doua interogare, cel mai apropiat strămoş comun al nodurilor 3, 5 şi 6 este nodul 3,
deci răspunsul este ” YES ”.
Pentru a treia interogare, cel mai apropiat strămoş comun al nodurilor 1 şi 7 este nodul 4, deci
răspunsul este ” NO ”.
Pentru a patra interogare, cel mai apropiat strămoş comun al nodurilor 4 şi 6 este nodul 4,
deci răspunsul este ” YES ”.
După aceea, putem ghici că rădăcina este nodul 4, care este şi răspunsul corect.
Punctaj
1. (7 puncte): n & 9
2. (10 puncte): n & 30
3. (cel mult 83 puncte): n & 500
În primele două subtaskuri puteţi pune cel mult 1000 de interogări pentru punctajul maxim.
În al treilea subtask, fie k numărul maxim de interogări puse ı̂n cadrul testelor. Dacă k9, veţi
ln k6
primi 83 de puncte. Altfel, veţi primi max 10, 83 1  7 & puncte.
Cod ı̂n C++ care calculează numărul de puncte:

((k <= 9) ? 83: max(10, int(83 * (1 - log(k - 6.0) / 7))))

5.2.1 Indicaţii de rezolvare

Author: Ray Bai


Developer: Roman Bilyi
Editorialist: Anton Trygub

Subtask 1. You can just ask about each possible subset of nodes. As we will show later, for
each two root candidates there is a query for which their answers would be different, so we can
n
determine the root uniquely. It’s enough to ask 2  1 queries.
CAPITOLUL 5. EJOI 2022 5.2. ROOT 129

Subtask 2. Let’s ask a query for each pair of nodes. Clearly, if root is v, then the answer to
each query r; v  for v j r is YES. If there is only one node that gives YES for each query, it must
be the root. Suppose that some node r1 j r also gives YES for each query in which it’s included.
If r is not a lead, then there is a node v j r1 ; v j r such that LCA r1 ; v  r, contradiction. So,
r must be a leaf.
Suppose that there is another leaf r1 that gives YES for all queries. Then consider any leaf
v different from r, r1 (in a graph in which at least one degree is at least 3, there are at least 3
leaves). Then, LCA r1 , v  can’t be equal to r1 and v, so the answer would be NO. Contradiction.
So, if there is only one such node, it’s the root, otherwise it’s the unique leaf among the nodes
n n
which gave YES for each query in which they were involved. It’s enough to ask 2 queries.
Subtask 3. There are many different approaches that achieve various bounds. We will describe
the solutions which work for n 500 in 10 queries, and in 9 queries.
10 queries: Suppose that r is the root. First, let’s ask a query about all leaves. Their LCA
has to be r (if r is a leaf, it’s clear; otherwise, there is a leaf in each of the subtrees of r). So, we
can determine if r is a leaf with this query.
Now if r is not a leaf, let’s ask queries for sets, which contain all leaves. We will get YES iff
we have included r into the set. So, we can do binary search on the non-leaf nodes, discarding
roughly half at each time. This way, we will need at most 9 queries.
Suppose now that r is a leaf. If we ask a question about some subset of the leaves of size '
2, the answer will be YES if and only if r is in the chosen set. So, we can once again do binary
search on the leaves, until we get at most 2 candidates r1 , r2 . At that point, we can ask a query
for nodes r2 , r3 , where r3 is any other leaf, to determine if r2 is the root. This way, we will also
need at most 9 queries.
Adding the initial query about all leaves, we are done in 10 queries.
9 queries: Our algorithm loses one query at the first step: it might be the case that the
number of remaining candidates is n - some small number (when the root is a leaf, and almost all
nodes are leaves, for example). Let’s try to solve this issue.
Let’s arrange our nodes in the following fashion: first all leaves, then all non-leaves. What
happens if we ask a query about the some prefix of this order of length at least 2? Let’s consider
two cases.

ˆ Our prefix contains all the leaves, and, maybe, some other nodes. Then their LCA is r, and
we will get YES if and only if r is in that prefix.
ˆ Our prefix is some subset of the leaves. Then, if r is among them, we will get YES, otherwise
their LCA would be some node different from any of them, so we will get NO. Once again,
we will get YES if and only if r is in that prefix.

So, we can do binary search on this order of nodes! The only exception is when we end up
with a prefix of length 2. Then, as in previous subtask, we should ask one of these leaves with
one of other leaves.
This solves the problem in 9 queries.

5.2.2 Coduri sursă

Listing 5.2.1: EJOI2022 Root rw 100.cpp


#include <bits/stdc++.h>

using namespace std;

#define FOR(i, a, b) for (int i = (a); i < (b); i++)


#define RFOR(i, b, a) for (int i = (b)-1; i >= (a); --i)
#define FILL(A, value) memset(A, value, sizeof(A))

#define ALL(V) V.begin(), V.end()


CAPITOLUL 5. EJOI 2022 5.2. ROOT 130

#define SZ(V) (int)V.size()


#define PB push_back
#define MP make_pair

typedef long long Int;


typedef unsigned long long UInt;
typedef vector<int> VI;
typedef pair<int, int> PII;
typedef pair<Int, Int> PLL;
typedef pair<double, double> PDD;
typedef complex<double> base;

const int INF = 1000000000;


const int MOD = 998244353;
const double Pi = acos(-1.);

const int MAX = 507;

int deg[MAX];
VI G[MAX];
int P[MAX];
bool T[MAX];

void dfs(int v, int p) {


P[v] = p;
for(auto to: G[v]) {
if (to == p)
continue;
dfs(to, v);
}
}

int main(int argc, char **argv)


{
//-----------------------------------------------------
std::clock_t c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();

auto t1 = clock();

std::freopen("../tests/145", "r", stdin) ;


std::freopen("root.out", "w", stdout) ;
//-----------------------------------------------------
int n;
cin >> n;
FOR(i,0,n - 1) {
int a, b;
cin >> a >> b;
-- a; -- b;
G[a].push_back(b);
G[b].push_back(a);
deg[a] ++;
deg[b] ++;
}

VI A, B;
FOR(i,0,n) {
if (deg[i] == 1)
A.push_back(i);
else
B.push_back(i);
}

for(auto x: B)
A.push_back(x);

int L = 1;
int R = n;

FOR(i,0,50) {
cout << "? 1 1" << endl;
string res;
cin >> res;
}

while (R - L > 1) {
CAPITOLUL 5. EJOI 2022 5.2. ROOT 131

int M = (L + R) / 2;
VI Q;
FOR(i,0,M) {
Q.push_back(A[i]);
}

cout << "? " << SZ(Q);


for(auto x: Q) {
cout << " " << x + 1;
}
cout << endl;
string res;
cin >> res;
if (res == "YES") {
R = M;
} else {
L = M;
}
}

if (L == 1) {
dfs(A[0], -1);
int v = A[1];
while (v != -1) {
T[v] = true;
v = P[v];
}
cout << "? 2 " << A[0] + 1 << ’ ’;
FOR(i,0,n) {
if (!T[i]) {
cout << i + 1 << endl;
break;
}
}
string res;
cin >> res;
if (res == "YES")
R = 1;
}
cout << "! " << A[R - 1] + 1 << endl;

// ----------------------------------------------------------------
auto t2 = clock();
std::clock_t c_end = std::clock();
auto t_end = std::chrono::high_resolution_clock::now();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;

std::cout << std::fixed << std::setprecision(2)


<< "\nCPU time used: "
<< 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
<< "Wall clock time passed: "
<< std::chrono::duration<double, std::milli>(t_end-t_start).count()
<< " ms\n";

return 0;
// ----------------------------------------------------------------
}

Listing 5.2.2: EJOI2022 Root check.cpp


#include <bits/stdc++.h>
#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
//int main(int argc, char **argv)
{
//-----------------------------------------------------------------------
int argc=4;
CAPITOLUL 5. EJOI 2022 5.3. TREE 132

char* argv[] =
{
(char*)"checker",
(char*)"../tests/145", // input
(char*)"../tests/145.a", // rezultat corect
(char*)"root.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

//registerChecker("root", argc, argv);


//-----------------------------------------------------------------------

registerTestlibCmd(argc, argv);

int n = inf.readInt();
int v = inf.readInt();
int g = inf.readInt();

int q = ouf.readInt();

if (g == 0) {
quitf(_ok, "OK");
}
if (g == 1) {
quitf(_ok, "OK");
}
if (g == 2) {
quitf(_ok, "OK");
}
int max_points = 83;
int points = ((q <= 9) ? 83: max(10, int(83 * (1 - log(q - 6.0) / 7))));
if (points == 83) {
quitf(_ok, "OK");
} else {
quitp(points, "OK");
}
}

5.2.3 *Rezolvare detaliată

5.3 tree
Problema 3 - Arbore de acoperire limitat 100 de puncte
Vi se dă un graf neorientat, conex, cu muchii ponderate cu n vârfuri şi m muchii. În acest
graf nu există bucle proprii (adică nu există muchie care să unească un vârf cu el ı̂nsuşi), dar pot
exista mai multe muchii ı̂ntre anumite perechi de vârfuri.
Prietenul tău ţi-a spus următoarele despre acest graf:
ˆ Toate ponderile muchiilor sunt numere ı̂ntregi distincte din intervalul 1, m. Cu alte cuvinte,
ele trebuie să formeze o permutare a numerelor ı̂ntregi de la 1 la m.
ˆ Ponderea celei de-a i-a muchii este din intervalul l, r pentru fiecare i de la 1 la m.
ˆ Muchiile cu indicii 1, 2, ..., n  1 (primele n  1 muchii din datele de intrare) formează un
arbore de acoperire minim al acestui graf.
Vreţi să ştiţi dacă această configuraţie este posibilă. Determinaţi dacă există astfel de atribuiri
de ponderi ale muchiilor pentru care aceste condiţii sunt valabile şi, dacă da, găsiţi una dintre ele.
Reamintim că, un arbore de acoperire al unui graf este orice submulţime de muchii care
formează un arbore (graf conex cu n noduri şi n  1 muchii).
Arborele de acoperire minim al unui graf este orice arbore de acoperire cu cea mai mică sumă
de ponderi dintre toţi arborii de acoperire ai grafului.
CAPITOLUL 5. EJOI 2022 5.3. TREE 133

Date de intrare
Prima linie conţine un singur număr ı̂ntreg t ( 1 & t & 10 ) - numărul de teste.
5

Prima linie a fiecărui test conţine două numere ı̂ntregi n şi m (1 & n1 & m & 5 5
10 ) -
numărul de vârfuri şi, respectiv, numărul de muchii.
Linia i dintre următoarele m linii conţine patru numere ı̂ntregi u, v, l, r (1 & u $ v & n,
1 & l & r & m) - indicând că există o muchie care leagă nodurile u , v şi că ponderea acesteia ar
trebui să fie ı̂n intervalul l, r.
Se garantează că, pentru fiecare test, muchiile cu indicii 1, 2, ..., n  1 formează un arbore de
acoperire al grafului dat.
5
Se garantează că suma valorilor m din toate testele nu depăşeşte 5 10 .
Date de ieşire
Pentru fiecare test, dacă nu există un vector de ponderi ale muchiilor care să ı̂ndeplinească
condiţiile, se afişează ” NO ” pe prima linie.
În caz contrar, pe prima linie se afişează ” YES ”. Pe a doua linie, se afişează m numere ı̂ntregi
w1 , w2 , ..., wm (1 & w & m, toate valorile wi sunt distincte) ce reprezintă ponderile muchiilor
(unde wi este ponderea atribuită celei de-a i-a muchii din datele de intrare).
În cazul ı̂n care există mai multe răspunsuri, se afişează oricare dintre ele.
Pentru afişarea datelor de ieşire puteţi utiliza litere mari şi/sau litere mici (de exemplu, ” YES
”, ” Yes ”, ” yes ”, ” yEs ”, ” yEs ” vor fi considerate ca răspunsuri corecte).

Exemplu
Date de intrare:

3
4 6
1 2 1 3
1 3 2 6
3 4 1 2
1 4 2 5
2 3 2 4
2 4 4 6
4 4
1 2 2 2
2 3 3 3
3 4 4 4
1 4 1 4
5 6
1 2 1 1
2 3 1 2
3 4 2 4
4 5 6 6
1 4 4 6
1 4 5 6

Date de ieşire:

YES
2 3 1 5 4 6
NO
YES
1 2 3 6 4 5

Punctaj
1. (4 puncte): l r (1 & i & m)
2. (6 puncte): Suma valorilor m din toate testele nu depăşeşte 10.
3. (10 puncte): Suma valorilor m din toate testele nu depăşeşte 20.
CAPITOLUL 5. EJOI 2022 5.3. TREE 134

4. (10 puncte): m n  1, suma valorilor m din toate testele nu depăşeşte 500.


5. (7 puncte): m n  1.
6. (20 puncte): m n
7. (11 puncte): Suma valorilor m pentru toate testele nu depăşeşte 5000.
8. (8 puncte): u i, v i  1 (1 & i & n  1).
5
9. (12 puncte): Suma valorilor m din toate testele nu depăşeşte 10 .
10. (12 puncte): Fără restricţii suplimentare.

5.3.1 Indicaţii de rezolvare

Author: Ihor Barenblat, Matvii Aslandukov


Developer: Ihor Barenblat
Editorialist: Ihor Barenblat

Subtask 1. For each edge, its weight is known. There is need to check that edges can have
different weights from the range 1, m and that edges with indices 1, 2, ..., n  1 form a minimum
spanning tree of the given graph. The last can be done using any suitable algorithm. Time
complexity: O m log m or O m.

Subtask 2. Just try all possible assignments for the weights of the edges and for each one check
compliance with the rules described in the statement. Time complexity: O m! m.

Subtask 3. Use dynamic programming on subsets of the edges. Lets denote dpS  as a boolean
variable indicating that there is assignment of the weights 1, 2, ..., ¶S ¶ to the edges from S, such
that there is no rules were broken. Here by ”breaking mst rule” we mean assigning to a non-
spanning-tree edge such value that this edge will be taken into minimum spanning tree when
m
considering it in the process of execution of Kruskal’s algorithm. Time complexity: O 2 m.

Subtask 4. The same idea as in the Subtask 5, but with worse algorithm complexity. Also
3
possible to solve using ”matching in bipartite graph” approach. Time complexity: O m  or
2
O m .

Subtask 5. We see that for this subtask there is no ”mst rule”. So we just need to find an
assignment of the edge weights using integers from the range 1, m. It is possible to do this
using greedy approach: set 1 to the edge that have li 1 with the minimum possible ri . This is
true, since any valid assignment can be changed without losing validity to be compatible with the
mentioned greedy assignment. So the overall greedy algorithm is the following: for each i from 1
to m set i as edge weight for the edge with lj & i with the minimum possible rj , between all edges
with unset weight. Time complexity: O m log m.

Subtask 6. Lets call the non-spanning-tree edge as ”special”. After the assigning a value for the
special edge we need to be sure that there exist possibility of assigning for spanning-tree edges
weights from the range 1, m (except the already assigned one). Lets find all suitable possible
values that satisfy this condition, choose the greatest one from the range of the special edge and
update rj for the edges on the cycle to be sure that they are smaller that the value we assigned
to the special edge.
For finding all suitable possible values that satisfy mentioned condition we can use Hall’s
107
marriage theorem on the bipartite graph (first part consists of vertices corresponding to the
edges and second part consists of vertices corresponding to possible edge weights): value k is
suitable if and only if there is no L, R such that L & k & R and R & L  1 & (# i such that
1 & i & n  1 and L & li & ri & R. Time complexity: O m log m.

Subtask 7. Lets call pair of edges (etree , enon tree ) interesting (here etree is a spanning-tree
edge and enon tree is a non-spanning-tree edge) if etree lies on the simple path in the expected
minimum spanning tree between vertices that connects enon tree . It is easy to see that for each
107
https://en.wikipedia.org/wiki/Hall%27s_marriage_theorem
CAPITOLUL 5. EJOI 2022 5.3. TREE 135

interesting pair of edges we can do the following update:

etree .r min etree .r, enon tree .r  1,


eenon tree .l max enon tree .l; etree .l  1.

With such modifications, any assignment found by the greedy algorithm from the Subtask 5
will satisfy ”mst rule”. Time complexity: O mcdotn.

Subtask 8. The same idea as in the Subtask 7, but with better algorithm complexity. You can
use segment tree to do mentioned updates. Time complexity: O m log n.

Subtask 9. The same idea as in the Subtask 7, but with better algorithm complexity. You can
108 2
use heavy-light decomposition to do mentioned updates. Time complexity: O m log n.

Subtask 10. The same idea as in the Subtask 7, but with better algorithm complexity. You can
109
use binary lifting approach or non-trivial heavy-light decomposition approach to do mentioned
updates. Time complexity: O m log n.

5.3.2 Coduri sursă

Listing 5.3.1: EJOI2022 Tree mhq.cpp


#ifdef DEBUG
#define _GLIBCXX_DEBUG
#endif
//#pragma GCC optimize("O3")

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef long double ld;

int n, m;

const int maxN = 5e5 + 10;


vector<pair<int,int>> g[maxN];

int u[maxN], v[maxN], l[maxN], r[maxN];

const int LOG = 20;


int mn[LOG][maxN];
//<= r[i]
int up[LOG][maxN];
int id_edge[maxN];
int mx[LOG][maxN];
int h[maxN];

void dfs(int v, int p)


{
for (auto& it : g[v]) {
if (it.first == p) continue;
id_edge[it.first] = it.second;
mn[0][it.first] = r[it.second];
mx[0][it.first] = l[it.second];
up[0][it.first] = v;
h[it.first] = h[v] + 1;
dfs(it.first, v);
}
108
https://en.wikipedia.org/wiki/Heavy_path_decomposition
. https://usaco.guide/plat/hld?lang=cpp
. https://www.geeksforgeeks.org/heavy-light-decomposition-set-1-introduction/
. https://cp-algorithms.com/graph/hld.html
109
https://www.geeksforgeeks.org/lca-in-a-tree-using-binary-lifting-technique/
. https://codeforces.com/blog/entry/100826
CAPITOLUL 5. EJOI 2022 5.3. TREE 136

}
void update(int u, int v, int val)
{
if (h[u] < h[v]) swap(u, v);
for (int k = LOG - 1; k >= 0; k--) {
if (h[u] - (1 << k) >= h[v]) {
mn[k][u] = min(mn[k][u], val);
u = up[k][u];
}
}
if (u == v) return;
for (int k = LOG - 1; k >= 0; k--) {
if (up[k][u] != up[k][v]) {
mn[k][u] = min(mn[k][u], val);
mn[k][v] = min(mn[k][v], val);
u = up[k][u];
v = up[k][v];
}
}
assert(up[0][u] == up[0][v]);
mn[0][u] = min(mn[0][u], val);
mn[0][v] = min(mn[0][v], val);
}

int get(int u, int v)


{
int d = 0;
if (h[u] < h[v]) swap(u, v);
for (int k = LOG - 1; k >= 0; k--) {
if (h[u] - (1 << k) >= h[v]) {
d = max(mx[k][u], d);
u = up[k][u];
}
}
if (u == v) return d;
for (int k = LOG - 1; k >= 0; k--) {
if (up[k][u] != up[k][v]) {
d = max(mx[k][u], d);
d = max(mx[k][v], d);
u = up[k][u];
v = up[k][v];
}
}
assert(up[0][u] == up[0][v]);
d = max(mx[0][u], d);
d = max(mx[0][v], d);
return d;
}

vector<int> ev[maxN];
int ans[maxN];

void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) {
g[i].clear();
}
for (int i = 1; i <= m; i++) {
ev[i].clear();
}
for (int k = 0; k < LOG; k++) {
for (int i = 0; i <= n; i++) {
up[k][i] = mx[k][i] = 0;
mn[k][i] = m;
}
}
for (int i = 1; i <= m; i++) {
cin >> u[i] >> v[i] >> l[i] >> r[i];
if (i < n) {
g[u[i]].emplace_back(v[i], i);
g[v[i]].emplace_back(u[i], i);
}
}
dfs(1, -1);
CAPITOLUL 5. EJOI 2022 5.3. TREE 137

for (int k = 0; k + 1 < LOG; k++) {


for (int i = 1; i <= n; i++) {
up[k + 1][i] = up[k][up[k][i]];
mx[k + 1][i] = max(mx[k][i], mx[k][up[k][i]]);
}
}
for (int i = n; i <= m; i++) {
update(u[i], v[i], r[i]);
l[i] = max(l[i], get(u[i], v[i]));
// cout << " ? " << l[i] << " " << r[i] << endl;
}
for (int k = LOG - 1; k > 0; k--) {
for (int i = 1; i <= n; i++) {
mn[k - 1][i] = min(mn[k - 1][i], mn[k][i]);
mn[k - 1][up[k - 1][i]] = min(mn[k - 1][up[k - 1][i]], mn[k][i]);
}
}
for (int i = 2; i <= n; i++) {
r[id_edge[i]] = min(r[id_edge[i]], mn[0][i]);
}
for (int i = 1; i <= m; i++) {
// cout << l[i] << " " << r[i] << " ??? " << endl;
if (l[i] > r[i]) {
cout << "No\n";
return;
}
ev[l[i]].emplace_back(i);
}
set<pair<int,int>> s;
for (int i = 1; i <= m; i++) {
for (auto& it : ev[i]) {
s.insert({r[it], it});
}
if (s.empty()) {
cout << "No\n";
return;
}
auto it = s.begin();
if (it->first < i) {
cout << "No\n";
return;
}
ans[it->second] = i;
s.erase(it);
}
assert(s.empty());
cout << "Yes\n";
for (int i = 1; i <= m; i++) {
cout << ans[i] << " ";
}
cout << ’\n’;

int main()
{
//-----------------------------------------------------
std::clock_t c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();

auto t1 = clock();

std::freopen("../tests/238", "r", stdin) ;


std::freopen("tree.out", "w", stdout) ;
//-----------------------------------------------------

ios_base::sync_with_stdio(false);
cin.tie(nullptr);
#ifdef DEBUG
freopen("input.txt", "r", stdin);
#endif
int tst;
cin >> tst;
while (tst--) {
solve();
CAPITOLUL 5. EJOI 2022 5.3. TREE 138

}
return 0;
}

Listing 5.3.2: EJOI2022 Tree check.cpp


#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;

struct edge {
int u, v;
int l, r;

edge() {
u = v = 0;
l = r = 0;
}
};

int n,m;
vector<edge> edges;
vector<int> p;

int f(int v) {
return p[v] == v ? v : p[v] = f(p[v]);
}

string upper(string s) {
for(char &c : s) {
c = toupper(c);
}
return s;
}

bool check(InStream &in) {


string result = upper(in.readToken("[yY][eE][sS]|[nN][oO]", "Yes/No"));
if (result == "NO") {
return false;
} else {
vector<int> edge_with_value(m, -1);
for (int i = 0; i < m; ++i) {
int p = in.readInt(1, m, "p_i");
if (p < edges[i].l || edges[i].r < p) {
in.quitf(_wa, "p_i is not in [l_i;r_i]");
}

--p;
if (edge_with_value[p] != -1) {
in.quitf(_wa, "p is not a permutation");
}
edge_with_value[p] = i;
}

p.resize(n);
iota(p.begin(), p.end(), 0);
for (int i = 0; i < m; ++i) {
int edge_id = edge_with_value[i];
int u = f(edges[edge_id].u);
int v = f(edges[edge_id].v);

if (u == v && edge_id < (n - 1)) {


in.quitf(_wa, "edge from the spanning tree is not taken");
}
p[u] = v;
}

return true;
}
}

bool part_but_not_jury = false;


bool jury_but_not_part = false;
CAPITOLUL 5. EJOI 2022 5.4. GAME 139

void check_test() {
n = inf.readInt();
m = inf.readInt();
edges.resize(m);
for (int i = 0; i < m; ++i) {
edges[i].u = inf.readInt();
edges[i].v = inf.readInt();
edges[i].l = inf.readInt();
edges[i].r = inf.readInt();

--edges[i].u;
--edges[i].v;
}

bool jury_out = check(ans);


bool part_out = check(ouf);

if (!jury_out && part_out) {


part_but_not_jury = true;
}
if (jury_out && !part_out) {
jury_but_not_part = true;
}
}

int main()
//int main(int argc, char *argv[])
{
//-----------------------------------------------------------------------
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/238", // input
(char*)"../tests/238.a", // rezultat corect
(char*)"tree.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

//registerChecker("perechi", argc, argv);


//-----------------------------------------------------------------------
registerTestlibCmd(argc, argv);

int tests = inf.readInt();


for (int test = 0; test < tests; ++test) {
setTestCase(test + 1);
check_test();
}

if (part_but_not_jury) {
quitf(_fail, "participant found the answer but jury didn’t");
} else if (jury_but_not_part) {
quitf(_wa, "jury found the answer but participant didn’t");
} else {
quitf(_ok, "all is ok");
}
}

5.3.3 *Rezolvare detaliată

5.4 game
Problema 4 - Joc cu numere 100 de puncte
Doi jucători joacă un joc. Ei au primit un şir a1 , a2 , ..., an , precum şi un şir b1 , b2 , ..., bm .
Jocul constă din m runde. Jucătorii participă la runde alternativ. Pe parcursul celei de-a i-a
CAPITOLUL 5. EJOI 2022 5.4. GAME 140

runde (pentru i de la 1 la m) jucătorul corespunzător (primul jucător, dacă i este impar, şi al
doilea dacă i este par) are de făcut exact una dintre următoarele:
ˆ şterge toate elementele din şirul a care sunt divizibile cu bi ,
ˆ şterge toate elementele din şirul a care nu sunt divizibile cu bi .
Primul jucător doreşte să minimizeze suma elementelor rămase ı̂n şirul a după toate cele m
runde, iar al doilea doreşte să o maximizeze. Aflaţi suma elementelor rămase ı̂n şirul a după toate
cele m runde, dacă ambii jucători joacă optim.

Date de intrare
Prima linie conţine doi ı̂ntregi n, m (1 &n&2 4
10 , 1 &m&2 5
10 ) - lungimea şirului a şi
numărul de runde ale jocului.
A doua linie conţine n ı̂ntregi a1 , a2 , ..., an (4 10
14
& ai & 4 14
10 ) - elementele şirului a.
A treia linie conţine m ı̂ntregi b1 , b2 , ..., bm (1 & bi &4 14
10 ) - elementele şirului b.

Date de ieşire
Afişaţi un singur ı̂ntreg - suma elementelor rămase ı̂n şirul a după toate cele m runde dacă
ambii jucători joacă optim.

Exemple
Date de intrare 1:

6 2
2 2 5 2 2 7
2 5

Date de ieşire 1:

Date de intrare 2:

5 1
-5000111000 -5000222000 -15 5 2
5

Date de ieşire 2:

-10000333010

Explicaţii
În primul exemplu, o posibilă desfăşurare a jocului este următoarea:
ˆ Runda 1: primul jucător şterge din a toate elementele divizibile cu 2. şirul a devine (5, 7).
ˆ Runda 2: al doilea jucător şterge din a toate elementele divizibile cu 5. şirul a devine (7).
Dacă el ar şterge din a toate elementele nedivizibile cu 5, a ar deveni (5), care ar avea o
sumă mai mică a elementelor şi prin urmare nu ar fi de dorit pentru al doilea jucător.

Punctaj
1. (3 puncte): m 1
2. (6 puncte): bi1 bi (1 & i $ m), adică toate elementele şirului b sunt egale
3. (15 puncte): bi1 mod bi 0 (1 & i $ m)
4. (9 puncte): 1 & m & 7
5. (11 puncte): 1 & m & 20
6. (15 puncte): 1 & m & 100
(18 puncte): 1 & ai , bi & 10
9
7.
8. (11 puncte): m mod 2 0, b2i1 b2i (1 & i & m 2
)
9. (12 puncte): Fără restricţii suplimentare
CAPITOLUL 5. EJOI 2022 5.4. GAME 141

5.4.1 Indicaţii de rezolvare

Author: Matvii Aslandukov Developer: Matvii Aslandukov Editorialist: Matvii Aslandukov

Author: Ray Bai


Developer: Roman Bilyi
Editorialist: Anton Trygub

Subtask 1. In the first subtask m 1 so you can find two sums s1 and s2 : the sum of all
elements that are divisible by b1 and the sum of all elements that are not divisible by b1 . Then
the answer is min s1 , s2  because the first player wants to minimize the sum of the remaining
elements. Time complexity: O n.
Lemma 1. Before describing the solution for each individual subtask let’s prove the following
lemma: if both players can remove all the elements from the array a using only their own rounds,
then the answer is 0. Indeed, in such a case both players can make the sum of the remaining
elements in the array a equal to 0 regardless of the actions of the second player. The first player
wants to minimize the sum, therefore the answer is not bigger than zero. At the same time, the
second player wants to maximize the sum, therefore the answer is not less than zero. Thus the
only possible answer is 0.

Subtask 2. In the second subtask two own rounds are enough for the first player to remove
all the elements: he can remove all the numbers that are divisible by b1 and are not divisible
by b3 . At the same time, only one own round is enough for the second player to remove all the
elements: he can make an opposite operation to the first player due to the condition b1 b2 . Thus
by the lemma 1 the answer is 0 for m % 2. For m 2 the answer is ©max 0, min s1 , s2 . Time
complexity: O n.

Subtask 3. In the third subtask two own rounds are enough for each player to remove all the
elements: the first player can remove all the numbers that are divisible by b1 and are not divisible
by b3 , and the second player can do the same using b2 and b4 . Thus by the lemma 1 the answer is
0 for m % 3. For m & 3 you can either solve the problem recursively by considering each possible
game scenario or consider up to 8 cases manually. Time complexity: O n.

Subtask 4. In the fourth subtask no additional observations are required. The entire game
can be simulated recursively: you can represent the state of the game as a pair operations, pos,
where operations is an array with chosen operations and pos is a current round. Then at each
state of the recursion you can try to make all two possible operations and select the best one. The
m
total number of states is O 2  and for each final state with pos m you can calculate the sum
m
of the remaining elements in O nm. Therefore such a solution works in O 2 nm.

Subtask 5. In the fifth subtask you can modify recursion in the following way: instead of
representing the state of the game as a pair operations, pos you can represent it as a pair a, pos,
where a is an array with remaining elements and pos is a current round. Then at each state of
the recursion you can try to make all two possible operations and select the best one. The total
m
number of states is O 2  but the total size of all arrays a across all states is only O nm due to
the fact that each initial element of the array a is contained in exactly m states. Therefore such
m
a solution works in O 2  nm. See the following code on Python for a better understanding.
def solve(a, pos):
if pos == len(b):
return sum(a)
na = [[], []]
for x in a:
na[(x % b[pos]) == 0].append(x)
return [min,max][pos % 2](solve(na[0],pos+1),solve(na[1],pos+1))
n, m = map(int, input().split())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
print(solve(a, 0))
m
Subtask 6. In the sixth subtask you can notice that the majority of O 2  states inside the
recursion have an empty array a that allows to immediately return 0 as a result:
CAPITOLUL 5. EJOI 2022 5.4. GAME 142

def solve(a, pos):


if len(a) == 0:
return 0
...
Such optimization gives time complexity O nm which is enough for the sixth subtask.

Subtask 7. In the seventh subtask ai ' 1 that means that the final sum of the remaining
elements is always non-negative. It simplifies the proof of the lemma 1 because now the goal of the
first player is to remove all the elements from the array a. Also it can be proven that the answer
is equal to 0 when mge19 under the constraint ai & 10 . Such proof is left as an exercise for the
9

reader (see bonus section). Such a fact means that the only difference from the fifth subtask is
that we can just output 0 when m % 20.

Subtask 8. In the eighth subtask the second player can always remove all the elements by
making the opposite second operation. It means that the answer is always non-negative. When the
answer is positive the only strategy for the second player is to repeat the corresponding operations
m©2
of the first player. It allows us to speed up the solution from the fifth subtask to O 2  nm
which is ok for the m & 40. See the solution for the next subtask to understand what to do in
case m % 40.

Subtask 9. For the full solution you can notice that O log n own rounds are enough for
each player to remove all the elements. Indeed, at each round one of the two possible operations
removes at least half of all remaining elements, therefore *log2 n0 rounds are always enough to
remove all the elements. It means that the only difference from the sixth subtask is that we can
just output 0 when m % 100 by the lemma 1.

Bonus. What is the maximum value of m where the answer is not equal to zero?

5.4.2 Coduri sursă

Listing 5.4.1: EJOI2022 game fast power 2 m.cpp


#include <bits/stdc++.h>

using namespace std;

long long solve(const vector<long long> &a, const vector<long long> &b, int pos) {
if (pos == b.size()) {
return accumulate(a.begin(), a.end(), 0LL);
}
vector<long long> na[2];
for (long long x : a) {
na[(x % b[pos]) == 0].push_back(x);
}
if (pos % 2) {
return max(solve(na[0], b, pos + 1), solve(na[1], b, pos + 1));
}
return min(solve(na[0], b, pos + 1), solve(na[1], b, pos + 1));
}

int main()
{
//-----------------------------------------------------
std::clock_t c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();

auto t1 = clock();

std::freopen("../tests/161", "r", stdin) ;


std::freopen("game.out", "w", stdout) ;
//-----------------------------------------------------

ios_base::sync_with_stdio(0);
cin.tie(0);
int n, m;
CAPITOLUL 5. EJOI 2022 5.4. GAME 143

cin >> n >> m;


vector<long long> a(n), b(m);
for (long long &x : a) {
cin >> x;
}
for (long long &x : b) {
cin >> x;
}
if (m >= 28) {
cout << 0 << endl;
return 0;
}
cout << solve(a, b, 0) << endl;

// ----------------------------------------------------------------
auto t2 = clock();
std::clock_t c_end = std::clock();
auto t_end = std::chrono::high_resolution_clock::now();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;

std::cout << std::fixed << std::setprecision(2)


<< "\nCPU time used: "
<< 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
<< "Wall clock time passed: "
<< std::chrono::duration<double, std::milli>(t_end-t_start).count()
<< " ms\n";

return 0;
// ----------------------------------------------------------------
}

Listing 5.4.2: EJOI2022 game check.cpp


#include "testlib.h"

#include <string>

using namespace std;

pattern pnum("0|-?[1-9][0-9]*");

bool isNumeric(const string& p)


{
return pnum.matches(p);
}

int main()
//int main(int argc, char * argv[])
{
//-----------------------------------------------------------------------
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/161", // input
(char*)"../tests/161.a", // rezultat corect
(char*)"game.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

setName("compare two signed huge integers");


registerTestlibCmd(argc, argv);

string ja = ans.readWord();
string pa = ouf.readWord();
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 144

if (!isNumeric(ja))
quitf(_fail, "%s is not a valid integer", compress(ja).c_str());

if (!ans.seekEof())
quitf(_fail, "expected exactly one token in the answer file");

if (!isNumeric(pa))
quitf(_pe, "%s is not a valid integer", compress(pa).c_str());

if (ja != pa)
quitf(_wa, "expected ’%s’, found ’%s’", compress(ja).c_str(), compress(pa).c_str
());

quitf(_ok, "answer is ’%s’", compress(ja).c_str());


}

5.4.3 *Rezolvare detaliată

5.5 permutations
Problema 5 - LCS al permutărilor 100 de puncte
Pentru două şiruri x şi y, definim LCS x, y  ca lungimea celui mai lung subşir comun al
acestora.
Se dau 4 numere ı̂ntregi n, a, b, c. Determinaţi dacă există 3 permutări p, q, r de numere
ı̂ntregi de la 1 la n, astfel ı̂ncât:
ˆ LCS p, q  a
ˆ LCS p, r b
ˆ LCS q, r c
Dacă există astfel de permutări, determinaţi orice triplet de permutări care respectă cerinţa.
O permutare p a numerelor ı̂ntregi de la 1 la n este un şir de lungime n cu proprietatea că
toate elementele sunt ı̂ntregi distincţi din intervalul 1, n. Spre exemplu, (2, 4, 3, 5, 1) este o
permutare a numerelor ı̂ntregi de la 1 la 5, ı̂n timp ce (1, 2, 1, 3, 5) şi (1, 2, 3, 4, 6) nu sunt.
Un şir c este un subşir al şirului d dacă c poate fi obţinut din d prin ştergerea câtorva (posibil,
zero sau toate) elemente. Spre exemplu, (1, 3, 5) este un subşir al şirului (1, 2, 3, 4, 5), ı̂n timp
ce (3, 1) nu este.
Cel mai lung subşir comun al şirurilor x şi y este cel mai lung şir z care este subşir atât
pentru x, cât şi pentru y. Spre exemplu, cel mai lung subşir comun al şirurilor x 1, 3, 2, 4, 5 şi
y 5, 2, 3, 4, 1 este z 2, 4 ı̂ntrucât este subşir al ambelor şiruri şi este cel mai lung astfel de
subşir. LCS x, y  reprezintă lungimea celui mai lung subşir comun, care ı̂n cazul de mai devreme
este 2.
Date de intrare
Datele de intrare conţin mai multe scenarii de test. Prima linie a datelor de intrare conţine un
singur număr ı̂ntreg t (1 & t & 10 ) - numărul de teste. După care urmează descrierea testelor.
5

Fiecare test este descris printr-o singură linie şi conţine 5 numere ı̂ntregi n, a, b, c, output
(1 & a & b & c & n & 2 10 , 0 & output & 1).
5

Dacă output 0, determinaţi doar dacă există astfel de permutări. Dacă output 1, trebuie,
de asemenea, să determinaţi un astfel de triplet de permutări, dacă există.
5
Se garantează că suma valorilor n pentru toate scenariile de test nu depăşeşte 2 10 .
Date de ieşire
Pentru fiecare test, afişaţi pe o singură linie ” YES ”, dacă există astfel de permutări p, q, r,
şi ” NO ” ı̂n caz contrar. Dacă output 1, şi există astfel de permutări, afişaţi ı̂ncă trei linii:
Pe prima linie afişaţi n numere ı̂ntregi p1 , p2 , ..., pn (1 & p & n, toate pi distincte) - elementele
permutării p.
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 145

Pe a doua linie afişaţi n numere ı̂ntregi q1 , q2 , ..., qn (1 & q & n, toate q distincte) - elementele
permutării q.
Pe a treia linie afişaţi n numere ı̂ntregi r  1, r2 , ..., rn (1 & r & n, toate r distincte) - elementele
permutării r.
Dacă există mai multe triplete corecte, afişaţi oricare dintre acestea.
Puteţi afişa răspunsul cu litere mici sau mari (de exemplu, ”YES”, ”Yes”, ”yes”, ”yEs”, ”yEs”
vor fi considerate răspunsuri corecte).
Exemple
Date de intrare:

8
1 1 1 1 1
4 2 3 4 1
6 4 5 5 1
7 1 2 3 1
1 1 1 1 0
4 2 3 4 0
6 4 5 5 0
7 1 2 3 0

Date de ieşire:

YES
1
1
1
NO
YES
1 3 5 2 6 4
3 1 5 2 4 6
1 3 5 2 4 6
NO
YES
NO
YES
NO

Explicaţii
În primul test, LCS 1, 1 este 1.
În al doilea test, se poate demonstra că astfel de permutări nu există.
În al treilea test, unul dintre exemple este p 1, 3, 5, 2, 6, 4, q 3, 1, 5, 2, 4, 6, r
1, 3, 5, 2, 4, 6. Este uşor să observăm că:

ˆ LCS p, q  4 (unul dintre cele mai lungi subşiruri este 1, 5, 2, 6)


ˆ LCS p, r 5 (unul dintre cele mai lungi subşiruri este 1, 3, 5, 2, 4)
ˆ LCS q, r 5 (unul dintre cele mai lungi subşiruri este 3, 5, 2, 4, 6)

În al patrulea test, se poate demonstra că astfel de permutări nu există.


Punctaj
1. (3 puncte): a b 1, c n, output 1
2. (8 puncte): n & 6, output 1
3. (10 puncte): c n, output 1
4. (17 puncte): a 1, output 1
5. (22 puncte): output 0
6. (40 puncte): output 1
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 146

5.5.1 Indicaţii de rezolvare

Author: Anton Trygub


Developer: Anton Trygub
Editorialist: Anton Trygub

Subtask 1. For a b 1, c n, there always is a solution. It’s enough to take p 1, 2, ..., n


and q r n, n  1, ..., 1.

Subtask 2. Let’s notice the simple property:


Let a, b be any sequences of length n consisting of integers from 1 to n, and p be any permutation
of integers from 1 to n. Then, we have the following property:
ˆ LCS a, b LCS pa1 , pa2 , ..., pan , pb1 , pb2 , ..., pbn 
Indeed, we don’t care about which element is larger than which. We only care about which
element is equal to which.
With that in mind, we might notice that if there is such a triple of permutations p, q, r, then
there also is such a triple with p 1, 2, ..., n. This allows us to fit the brute force in time.
2
Let’s try all candidates for permutations q, r (there are n! such pairs), and for each pair
2
of candidates, candidate pairwise LCSs (we can find the LCS of two permutations in O n ,
for example). For each triple a, b, c that we saw, memorize any triple of permutations p, q, r
2
that produced it. This allows us to solve the subtask with preprocessing, taking O 6!6 , which
passes easily.

Subtask 3. Note that if LCS q, r n, we must have q r, so we also must have a b. We are
still considering p 1, 2, ..., n. Also, note that LCS 1, 2, ..., n, q  is just equal to the length of
the longest increasing subsequence of q.
Now, we need to check if there exists a permutation with LIS q  a (from now on, by LIS q 
I will denote the length of the longest increasing subsequence of q). It turns out that it exists for
each 1 & a & n.
Indeed, it’s enough to consider q n, n  1, ..., a  2, a  1, 1, 2, ..., a.

Subtask 4. If LCS p; q  1, then q must be the reverse of p. In our case, we would have
p 1, 2, ..., n, q n, n  1, ..., 1. Note that LCS n, n  1, ..., 1, r LDS r for any
permutation, where by LDS r we denote the length of the longest decreasing subsequence of r.
So, we just need to determine if there exists a permutation of length n with LIS b and
LDS c. Here, the ErdősSzekeres theorem might be useful. It states that in any sequence with
length r  1 s  1, there is an increasing subsequence of length r, or a decreasing subsequence
of length s. We will use it in the following form: LIS pLDS p ' n (indeed, if LIS pLDS p &
n  1, then the subsequence of length n would have an increasing subsequence of length LIS p  1,
or a decreasing subsequence of length LDS p  1.
So, we must have bc ' n. Is this condition sufficient? Sadly, no. Consider b c n, for
example. We would have to have p q r, but p j q (for n % 1).
Then, we might notice the second condition: LCS p  LDS p & n  1 for any permutation
p of length n. Indeed, any increasing subsequence may have at most 1 common element with any
decreasing one, so at most one of n elements can contribute to both LCS and LDS, and others
can contribute to at most one of them.
So, we get: b  c & n  1, bc ' n. Are these conditions sufficient? Turns out, yes. Consider
permutation c, c  1, ..., 1, 2c, 2c  1, ..., c  1, 3c, ..., bc, bc  1, ..., bc  c  1 of integers from 1 to
bc. It’s easy to see that its LIS is b, and its LDS is c. (For example, the argument for LDS: it
clearly has an increasing subsequence c, 2c, ..., bc, but also is split into b decreasing blocks, none
of which can contain more than one element from LIS).
Consider some subsequence of this permutation, which contains elements c, 2c, ..., bc, as well as
elements c, c  1, ..., 1 (c in written twice, yes) n  1 elements in total. Any such subsequence will
have LIS b and LDS c. As b  c  1 & n & bc, we can take some extra n  b  c  1 elements
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 147

of this permutation, and obtain a sequence with LIS b and LDS c of length n. Then, we can
just ”compress” the sequence to the permutation by mapping different values to 1, 2, ..., n in the
relative order.

Subtask 5. In some sense, this subtask was included so that participants would be able to check
if their criteria were correct, basically guessing before getting to the actual construction. That’s
what we are going to do here, leaving the proof and the construction for the subtask 6.
Let’s try to guess these criteria. We already know some criteria for the case a 1. Maybe we
can generalize them somehow?
First, let’s try to get something similar to LIS p  LDS q  & n  1. Consider common
subsequences of p, r and q, r. If their lengths are b, c correspondingly, they must have at least
b  c  n elements in common. These common elements would form a common subsequence of p, q
so b  c  n & a, or b  c & a  n.
Now, let’s try to get something similar to LIS pLDS p ' n. I claim, that
LCS p, q LCS q, rLCS p, r ' n. Proof: suppose that abc & n. As p 1, 2, ..., n, we get that
LIS q  a 
LDS q  ' n2 ' bc  1. Consider some decreasing subsequence of q of length bc  1.
Let’s look at how the elements of this subsequence are situated in r. No b  1 of these elements
can form an increasing subsequence in r (as then we would have LCS p, r ' b  1. No c  1 of
these elements can form a decreasing subsequence in r (as then we would have LCS q, r ' c  1.
110
But at least one of these has to hold, by the Erdős-Szekeres theorem ! Contradiction.
So, we found two necessary conditions:
ˆ bc&an
ˆ abc & n
It turns out that these conditions are actually sufficient. We will prove this in the next section.

Subtask 6. Let’s prove that for such a, b, c, n there always exists such a triple of permutations.
We will prove this by induction. We already know this is the case if a 1, and it’s clear for n 1.
Now, suppose that it’s true for all tuples a1 , b1 , c1 , n1  with a1 & a, b1 & b, c1 & c, n1 & n.
Suppose that b  c & a  n and abc ' n. If a % 1 and a  1 b  1 c  1 ' n  1, then we know
that there is such a triple of permutations for tuple a  1, b  1, c  1, n  1 as well. Consider
these permutations p1 , q1 , r1 . Let’s append n to each of them. Clearly, the LCS of each pair will
increase by precisely 1, so we would get the desired outcome.
Now, let’s provide a construction for the case abc n. Let’s take:
ˆ p 1, 2, ..., abc
ˆ q abc  a  1, abc  a  2, ..., abc, abc  2a  1, abc  2a  2, ..., abc  a, ..., 1, 2, ..., a (bc
increasing blocks, each of length a)
ˆ r ac, ac  1, ..., 1, 2ac, 2ac  1, ..., ac  1, ..., abc, abc  1, ..., abc  ac  1 (b decreasing blocks,
each of length ac)

It’s easy to see that LIS q  a and LIS r b, it only remains to prove that LCS q; r c.
Sequence ac, a  1c, ..., 2c, c is a subsequence of both. Suppose that some sequence of length c  1
is a subsequence of both. If some two elements x $ y of it are from different blocks of length ac
(here I mean blocks ac, ac  1, ..., 1, 2ac, 2ac  1, ..., ac  1, ..., abc, abc  1, ..., abc  ac  1,
then in q x goes after y, and in r before, which is impossible. So, they all must be from the same
block, say, kac, kac  1, ..., k  1ac  1.
Then, this subsequence must be decreasing. However, the elements kac, kac1, ..., k 1ac1
in q go in order (kac  a  1, kac  a  2, ..., kac, kac  2a  1; kac  2a  2, ..., kac  a, ...,
 k  1ac  1, k  1ac  2, ..., k  1ac  a) - that is, c increasing blocks, each of size a. So, it
can’t have decreasing subsequence of length c  1 (as some two elements would have to be in the
same block). Contradiction.
How to use this construction for n abc to get construction for smaller n, the same way as we
did in the case a 1? Let’s select elements 1, 2, ..., a, 1, ac  1, 2ac  1, ..., b  1ac  1, ac  c  1,
ac  2c  1, ..., 1 (1 appears in all 3 of these sequences). We want to take some subset of size n
of integers from 1 to abc, containing all the a  b  c  2 elements above, and remove from each
110
https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93Szekeres_theorem
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 148

of p, q, r all elements not contained in this subset (and later ”compress” by mapping k-th largest
number among selected to k). If we do this, we will get precisely LCS p, q  a, LCS p, r b,
LCS q, r c). We can do this when a  b  c  2 & n.
If we haven’t succeeded, we have the following conditions:
ˆ a  1  b  1  c  1 ' n
ˆ a  1 b  1 c  1 & n  2

When is xyz & x  y  z  2 possible in general for positive integers x & y & z?
If x ' 2, we get xyz ' 4z % x  y  z % x  y  z  2, so we must have x 1, or yz & y  z  1.
For y ' 2, we get yz ' 2z ' y  z % y  z  1, so we must have y 1. In this case, we
get a b 2, and c  1 ' n, c  1 & n  2, implying c n  1. So, the only remaining case is
2, 2, n  1, n (when n ' 3).
For this case, there is a simple construction:
ˆ p 1, 2, ..., n
ˆ q n, n  1, ..., 5, 4, 3, 1, 2
ˆ r n, n  1, ..., 5, 4, 1, 3, 2
We have covered all cases, and provided an algorithm how to construct such permutations, so
we are done.

5.5.2 Coduri sursă

Listing 5.5.1: EJOI2022 permutations solution.cpp


#include <ext/pb_ds/assoc_container.hpp> // rezultat corect dar eroare in executie !!!
#include <ext/pb_ds/tree_policy.hpp>

#include <bits/stdc++.h>

#pragma GCC target ("avx2")


#pragma GCC optimization ("O3")
#pragma GCC optimization ("unroll-loops")

using namespace __gnu_pbds;


using namespace std;

using ll = long long;


using ld = double;

typedef tree<
pair<int, int>,
null_type,
less<pair<int, int>>,
rb_tree_tag,
tree_order_statistics_node_update>
ordered_set;

#define mp make_pair

int MOD = 998244353;

int mul(int a, int b) {


return (1LL * a * b) % MOD;
}

int add(int a, int b) {


int s = (a+b);
if (s>=MOD) s-=MOD;
return s;
}

int sub(int a, int b) {


int s = (a+MOD-b);
if (s>=MOD) s-=MOD;
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 149

return s;
}

int po(int a, ll deg)


{
if (deg==0) return 1;
if (deg%2==1) return mul(a, po(a, deg-1));
int t = po(a, deg/2);
return mul(t, t);
}

int inv(int n)
{
return po(n, MOD-2);
}

mt19937 rnd(time(0));

const int LIM = 1000005;

vector<int> facs(LIM), invfacs(LIM), invs(LIM);

void init()
{
facs[0] = 1;
for (int i = 1; i<LIM; i++) facs[i] = mul(facs[i-1], i);
invfacs[LIM-1] = inv(facs[LIM-1]);
for (int i = LIM-2; i>=0; i--) invfacs[i] = mul(invfacs[i+1], i+1);

for (int i = 1; i<LIM; i++) invs[i] = mul(invfacs[i], facs[i-1]);


}

int C(int n, int k)


{
if (n<k) return 0;
if (n<0 || k<0) return 0;
return mul(facs[n], mul(invfacs[k], invfacs[n-k]));
}

struct DSU
{
vector<int> sz;
vector<int> parent;
void make_set(int v) {
parent[v] = v;
sz[v] = 1;
}

int find_set(int v) {
if (v == parent[v])
return v;
return find_set(parent[v]);
}

void union_sets(int a, int b) {


a = find_set(a);
b = find_set(b);

if (a != b) {
if (sz[a] < sz[b])
swap(a, b);
parent[b] = a;
sz[a] += sz[b];
}
}

DSU (int n)
{
parent.resize(n);
sz.resize(n);
for (int i = 0; i<n; i++) make_set(i);
}
};
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 150

void print(vector<int> a)
{
for (auto it: a) cout<<it<<’ ’;
cout<<endl;
}

void print(vector<bool> a)
{
for (auto it: a) cout<<it<<’ ’;
cout<<endl;
}

void print(vector<pair<int, int>> a)


{
for (auto it: a) cout<<it.first<<’ ’<<it.second<<"| ";
cout<<endl;
}

void solve()
{
int n; cin>>n;
vector<int> a(n); for (int i = 0; i<n; i++) cin>>a[i];

map<int, int> last_pos;


set<int> pos;

int answer = 1;

vector<vector<int>> prv(n);
vector<vector<int>> ans(n);

for (int i = 0; i<n; i++)


{
//Pushing last occurences

if (!pos.empty())
{
auto pt = pos.end();
int cnt = 5;
while (cnt && pt!=pos.begin())
{
pt = prev(pt); prv[i].push_back(*pt); cnt--;
}
}

//Updating

if (last_pos.find(a[i])!=last_pos.end())
{
pos.erase(last_pos[a[i]]);
}

last_pos[a[i]] = i;
pos.insert(i);
}

/*for (int i = 0; i<n; i++)


{
cout<<i<<": "; print(prv[i]);
}*/

for (int i = 0; i<n; i++)


{
for (auto x: prv[i])
{
if (a[x] == a[i])
{
ans[i].push_back(1); continue;
}
int best = 2;
for (int j = 0; j<prv[x].size(); j++)
{
if (a[prv[x][j]] != a[x] && a[prv[x][j]] != a[i]) best = max(best, 1 +
ans[x][j]);
}
ans[i].push_back(best);
CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 151

answer = max(answer, best);


}
}

cout<<answer<<endl;
}

int main()
{
//-----------------------------------------------------
std::clock_t c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();

auto t1 = clock();

std::freopen("../tests/306", "r", stdin) ;


std::freopen("permutations.out", "w", stdout) ;
//-----------------------------------------------------
ios_base::sync_with_stdio(0);
cin.tie(nullptr);

int t;
cin>>t;

while (t--) solve();

// ----------------------------------------------------------------
auto t2 = clock();
std::clock_t c_end = std::clock();
auto t_end = std::chrono::high_resolution_clock::now();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;

std::cout << std::fixed << std::setprecision(2)


<< "\nCPU time used: "
<< 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
<< "Wall clock time passed: "
<< std::chrono::duration<double, std::milli>(t_end-t_start).count()
<< " ms\n";

return 0;
// ----------------------------------------------------------------
}

Listing 5.5.2: EJOI2022 permutations check.cpp


#include "testlib.h"
#include <sstream>

using namespace std;

int main()
//int main(int argc, char * argv[])
{
//-----------------------------------------------------------------------
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/306", // input
(char*)"../tests/306.a", // rezultat corect
(char*)"permutations.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

//registerChecker("perechi", argc, argv);


CAPITOLUL 5. EJOI 2022 5.5. PERMUTATIONS 152

//-----------------------------------------------------------------------

setName("compare ordered sequences of signed int%d numbers", 8 * int(sizeof(long


long)));

registerTestlibCmd(argc, argv);

int n = 0;
string firstElems;

while (!ans.seekEof() && !ouf.seekEof())


{
n++;
long long j = ans.readLong();
long long p = ouf.readLong();
if (j != p)
quitf(_wa, "%d%s numbers differ - expected: ’%s’, found: ’%s’", n,
englishEnding(n).c_str(), vtos(j).c_str(), vtos(p).c_str());
else
if (n <= 5)
{
if (firstElems.length() > 0)
firstElems += " ";
firstElems += vtos(j);
}
}

int extraInAnsCount = 0;

while (!ans.seekEof())
{
ans.readLong();
extraInAnsCount++;
}

int extraInOufCount = 0;

while (!ouf.seekEof())
{
ouf.readLong();
extraInOufCount++;
}

if (extraInAnsCount > 0)
quitf(_wa, "Answer contains longer sequence [length = %d], but output contains %
d elements", n + extraInAnsCount, n);

if (extraInOufCount > 0)
quitf(_wa, "Output contains longer sequence [length = %d], but answer contains %
d elements", n + extraInOufCount, n);

if (n <= 5)
quitf(_ok, "%d number(s): \"%s\"", n, compress(firstElems).c_str());
else
quitf(_ok, "%d numbers", n);
}
/* la verificare apare:
argc = 4
checker
../tests/306
../tests/306.a
permutations.out
----------------------
ok 40000 numbers

Process returned 0 (0x0) execution time : 0.078 s


Press any key to continue.
*/
CAPITOLUL 5. EJOI 2022 5.6. UNFRIENDLY 153

5.5.3 *Rezolvare detaliată

5.6 unfriendly
Problema 6 - Cel mai lung subşir neprietenos 100 de puncte
Numim şirul b1 , b2 , ..., bm neprietenos, dacă următoarea condiţie este ı̂ndeplinită:
ˆ dacă 1 & i $ j & m şi j  i & 2, atunci bi j bj .
Cu alte cuvinte, un şir este neprietenos dacă oricare două elemente aflate la distanţă cel mult
2 sunt distincte.
Vi se dă un şir a1 , a2 , ..., an . Găsiţi lungimea celui mai lung subşir neprietenos al acestuia.
Un şir c se numeşte subşirul unui şir d dacă c poate fi obţinut din d prin ştergerea unui număr
de elemente (posibil zero sau chiar toate). De exemplu, (1, 3, 5) este un subşir al şirului (1, 2, 3,
4, 5), ı̂n timp ce (3, 1) nu este.

Date de intrare
Datele de intrare conţin mai multe scenarii de test. Prima linie conţine un singur număr ı̂ntreg
t ( 1 & t & 10 ) - numărul de teste. Urmează descrierea celor t teste.
5

Prima linie a fiecărui test conţine un singur număr ı̂ntreg n (1 & n & 2 10 ) - lungimea şirului.
5

A doua linie a fiecărui test conţine n ı̂ntregi a1 , a2 , ..., an (1 & ai & 109 ) - elementele şirului a.
Se garantează că suma valorilor n pentru toate scenariile de test din datele de intrare nu
5
depăşeşte 2 10 .

Date de ieşire
Pentru fiecare test, afişaţi un singur număr ı̂ntreg - lungimea celui mai lung subşir neprietenos
al şirului a.

Exemple
Date de intrare:

3
5
1 2 1 2 1
7
1 2 3 2 1 2 3
8
1 10 10 1 1 100 100 1

Date de ieşire:

2
6
4

Explicaţii
În primul test, cele mai lungi subşiruri neprietenoase sunt (1, 2) şi (2, 1). Subşirul (1, 2, 1),
de exemplu, nu este neprietenos, ı̂ntrucât primul element şi al treilea element sunt egale.
În al doilea test, cel mai lung subşir neprietenos este (1, 2, 3, 1, 2, 3). Este evident că subşirul
corespunzător ı̂ntregului şir nu este neprietenos, aşadar răspunsul este 6.
În al treilea test, cel mai lung subşir neprietenos este (1, 10, 100, 1).

Punctaj
1. (3 puncte): ai & ai1
CAPITOLUL 5. EJOI 2022 5.6. UNFRIENDLY 154

2. (6 puncte): n & 8
3. (8 puncte): Suma valorilor n din toate scenariile de test nu depăşeşte 500
4. (10 puncte): ai & 3
5. (10 puncte): ai & 10
6. (20 puncte): Suma valorilor n din toate scenariile de test nu depăşeşte 10 000
7. (43 puncte): Fără restricţii suplimentare

5.6.1 Indicaţii de rezolvare

Author: Anton Trygub


Developer: Anton Trygub
Editorialist: Anton Trygub

Subtask 1. Any subsequence of such a sequence is nondecreasing. Unfriendly sequences, however,


do not allow equality of two adjacent elements, so any unfriendly subsequence of this sequence
has to be strictly increasing. This means, that each value will appear in such a subsequence at
most once.
But then we can delete duplicates and take a subsequence containing precisely one occurrence
of each element that appears in a. As all elements of this subsequence are distinct, it’s unfriendly.
So, the answer for this subtask is just the number of distinct elements in a. We can find it in
O n.
n
Subtask 2. We can just consider all possible 2  1 nonempty subsequences of a, check each for
n
unfriendliness in O n time, and output the length of the longest unfriendly. This takes O 2 n
for n & 8.
5 111
time. As tle10 , this easily fits in TL

Subtask 3. Clearly, for n 1 answer is 1, and for n ' 2 it’s ' 2 (as any subsequence of length
exactly 2 is unfriendly).
Let’s use dynamic programming. Let dpij  for 1 & i $ j & n denote the length of the longest
unfriendly subsequence of a, in which the last element is aj , and the second last is ai . If ai aj ,
dp[i][j] = 0. Otherwise, dpij  max 2, max1&k$i dpk i  1 over k for which ak j ai and
ak j aj . We can calculate this dp table in O n  for a single test case, which is fast enough.
3

Subtask 4. Let’s look at any unfriendly sequence b1 , b2 , ..., bm such that for all i 1 & bi & 3.
Each 3 consecutive elements of b are distinct, therefore bi , bi1 , bi2 are some permutation of
1, 2, 3 for 1 & i & n  2. Then, however, bi1 , bi2 , bi3 also are such a permutation. As bi and
bi3 both differ from two distinct values bi1 , bi2 , they must be equal. So, bi bi3 for each i;
b has to be periodic with period 3.
Then, just try each possible start of the subsequence b p1 , p2 , p3 - every permutation of 1, 2, 3.
For each of them, take elements p1 , p2 , p3 , p1 , p2 , ... as soon as you see them. Output the largest
answer over these 6 options.

Subtask 5. Let’s go through our sequence a from left to right and keep the following dynamic
programming table: let dpxy  denote the length of the longest unfriendly subsequence of a up
to this moment, whose last element is y, and second last element is x.
9
Initially, we can set each value in this table to IN F (where IN F 10 , for example). Let’s
also keep track of what elements have already appeared in our sequence.
It turns out that it’s easy to update this table: when we are at position i, we just need
to update the values of dpxai  for each x j ai . If x hasn’t appeared before, there is no
subsequence ending with x, ai , otherwise, do dpxai  max dpxai , 2. Then, we need to
do dpxai  max dpxai , dpy x  1 over all y j x, ai . Updating this table after seeing
2 2
the next element takes O M AX , with overall complexity O M AX n per test case, which fits
easily.
111
time limit
CAPITOLUL 5. EJOI 2022 5.6. UNFRIENDLY 155

Subtask 6. Let’s modify our algorithm from Subtask 5 a little. Clearly, we can assume that
elements are in the range 1, n (just map k-th smallest value to k, we don’t care about the exact
values of elements, we only care about which elements are equal to which). Now, again, let’s keep
dpxy  for x j y: the length of the longest unfriendly subsequence of a up to this moment which
ends with x, y . The difficulty lies in updating dpxai  max dpxai , dpy x  1 over all
y j x, ai : this can take O n , which for n 10 000 has no chance of passing.
3

But let’s note that we don’t actually need all the values dpy x to update this table. We
need the largest value among the ones for which y j ai . Then, for each y let’s keep two values
x1 j y; x2 j y, such that the values dpx1 y , dpx2 y  are the largest among all dpxy .
Then, we would just have 2 (at most) candidates to check. After we do this for each y, we will
recalculate the best choices for the previous element for ai .
2
This way, processing new element takes O n, and the entire algorithm runs in O n  time,
which passes easily.

Subtask 7. For this subtask, we will have to analyze the structure of the longest unfriendly
subsequence a bit more.
Consider the longest unfriendly subsequence of a. Suppose that it contains ai . What could be
the previous element before ai , if there is any? Clearly, if it’s some value x, it’s optimal to take
the last occurrence of x before ai .
What we did in previous subtasks was going through all possible candidates for x. However, as
it turns out, we don’t need that many. Among all last occurrences of elements before ai , consider 5
rightmost (if there are at least 5). Suppose that we don’t take any of those as our x. Then, I claim,
we can extend our unfriendly subsequence by inserting one of these rightmost 5 last occurrences
into it.
Indeed, two (or less, if there are less than two) elements to the left of ai in this subsequence,
ai , and the element to the right, if there is any. They are the only prohibited values for the x
(if we want to insert x right before ai in this subsequence). Then one of those 5 last occurrences
would not be prohibited, and the subsequence wouldn’t be the longest possible.
So, for each ai , we know the set of at most 5 possible candidates for the previous element in
the longest unfriendly subsequence. Therefore, we can once again use dynamic programming of
form candlast, indicating the length of the longest possible unfriendly subsequence, ending in
acand , alast . For each last, we have at most 5 cand. So, when processing new last, we need to
2
do just M AGIC checks (where M AGIC 5).
We can keep this dp in maps, and keep the last occurrence of each element with a simple set.
2
The total complexity is O n 5  log n.

5.6.2 Coduri sursă

Listing 5.6.1: EJOI2022 LCS 16774096.cpp


// https://www.cnblogs.com/shrshrshr/p/16774096.html

#include <bits/stdc++.h>

typedef long long ll;

using namespace std;

vector<int> p,q,r;

bool solve(int n,int a,int b,int c){


if(b+c>a+n||1ll*a*b*c<n) return 0;
if(!n) return 1;
if(solve(n-1,a-1,b-1,c-1)){
p.push_back(n);q.push_back(n);r.push_back(n);
return 1;
}
if(a+b+c-2>n){
for(int i=1;i<=n;i++) p.push_back(i);
CAPITOLUL 5. EJOI 2022 5.6. UNFRIENDLY 156

for(int i=1;i<=n-3;i++) q.push_back(n-i+1),r.push_back(n-i+1);


q.insert(q.end(),{3,1,2});
r.insert(r.end(),{1,3,2});
return 1;
}
set<ll> sp;
auto posq=[&](ll x) {return make_pair(-(x-1)/a,x);};
auto cmpq=[&](ll x,ll y) {return posq(x)<posq(y);};
set<ll,decltype(cmpq)> sq(cmpq);
auto posr=[&](ll x) {return make_pair((x-1)/a/c,-x);};
auto cmpr=[&](ll x,ll y) {return posr(x)<posr(y);};
set<ll,decltype(cmpr)> sr(cmpr);
auto add=[&](ll x) {sp.insert(x);sq.insert(x);sr.insert(x);};
for(int i=1;i<=a;i++) add(i);
for(int i=1;i<b;i++) add(1ll*i*a*c+1);
for(int i=1;i<c;i++) add(1ll*i*a+1);
for(int i=2,s=a+b+c-2;s<n;i++)
if(!sp.count(i)) add(i),s++;
map<ll,int> id;
int t=0;
for(ll x:sp) p.push_back(id[x]=++t);
for(ll x:sq) q.push_back(id[x]);
for(ll x:sr) r.push_back(id[x]);
return 1;
}

int main()
{
//-----------------------------------------------------
std::clock_t c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();

auto t1 = clock();

std::freopen("../tests/110", "r", stdin) ;


std::freopen("LCS.out", "w", stdout) ;
//-----------------------------------------------------
int tot;scanf("%d",&tot);
for(int n,a,b,c,op;tot--;){
p.clear();q.clear();r.clear();
scanf("%d%d%d%d%d",&n,&a,&b,&c,&op);
if(solve(n,a,b,c)){
puts("YES");
if(!op) continue;
for(int x:p) printf("%d ",x);
puts("");
for(int x:q) printf("%d ",x);
puts("");
for(int x:r) printf("%d ",x);
puts("");
}else puts("NO");
}

// ----------------------------------------------------------------
auto t2 = clock();
std::clock_t c_end = std::clock();
auto t_end = std::chrono::high_resolution_clock::now();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;

std::cout << std::fixed << std::setprecision(2)


<< "\nCPU time used: "
<< 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
<< "Wall clock time passed: "
<< std::chrono::duration<double, std::milli>(t_end-t_start).count()
<< " ms\n";

return 0;
// ----------------------------------------------------------------
}

Listing 5.6.2: EJOI2022 LCS check.cpp


CAPITOLUL 5. EJOI 2022 5.6. UNFRIENDLY 157

#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;

int n, a, b, c, output;

int lis(vector<int> const& a) {


int n = a.size();
const int INF = 1e9;
vector<int> d(n+1, INF);
d[0] = -INF;

for (int i = 0; i < n; i++) {


int j = upper_bound(d.begin(), d.end(), a[i]) - d.begin();
if (d[j-1] < a[i] && a[i] < d[j])
d[j] = a[i];
}

int ans = 0;
for (int i = 0; i <= n; i++) {
if (d[i] < INF)
ans = i;
}
return ans;
}

int lcs(vector<int> p, vector<int> q)


{
vector<int> invp(n);
for (int i = 0; i<n; i++) {p[i]--; invp[p[i]] = i;}
for (int i = 0; i<n; i++) {q[i]--; q[i] = invp[q[i]];}
return lis(q);
}

string upper(string s) {
for(char &c : s) {
c = toupper(c);
}
return s;
}

bool check_perm(vector<int> a)
{
set<int> guys(a.begin(), a.end());
return (guys.size() == n);
}

bool checkAns(InStream& stream) {


string yesno = upper(stream.readToken("[yY][eE][sS]|[nN][oO]", "yes/no"));

if(yesno == "NO") return false;


if (output == 0) return true;

vector<int> p = stream.readInts(n, 1, n);


if (!check_perm(p)) stream.quitf(_wa, "p is not a permutation");

vector<int> q = stream.readInts(n, 1, n);


if (!check_perm(q)) stream.quitf(_wa, "q is not a permutation");

vector<int> r = stream.readInts(n, 1, n);


if (!check_perm(r)) stream.quitf(_wa, "r is not a permutation");

if (lcs(p, q)!=a) stream.quitf(_wa, "LCS of p and q is not a");


if (lcs(p, r)!=b) stream.quitf(_wa, "LCS of p and r is not b");
if (lcs(q, r)!=c) stream.quitf(_wa, "LCS of q and r is not c");

return true;
}

int main()
//int main(int argc, char* argv[])
{
//-----------------------------------------------------------------------
int argc=4;
CAPITOLUL 5. EJOI 2022 5.6. UNFRIENDLY 158

char* argv[] =
{
(char*)"checker",
(char*)"../tests/110", // input
(char*)"../tests/110.a", // rezultat corect
(char*)"LCS.out", // rezultat de verificat si acordat punctaj
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

//registerChecker("perechi", argc, argv);


//-----------------------------------------------------------------------
registerTestlibCmd(argc, argv);

int t = inf.readInt();

for (int test = 1; test<=t; test++)


{
setTestCase(test);
n = inf.readInt();
a = inf.readInt();
b = inf.readInt();
c = inf.readInt();
output = inf.readInt();

bool juryAns = checkAns(ans);


bool partAns = checkAns(ouf);

if (output==0)
{
if (juryAns!=partAns) quitf(_wa, "Wrong answer");
}
else
{
if (juryAns && (!partAns)) quitf(_wa, "Jury found the answer but participant
didn’t");
if ((!juryAns) && partAns) quitf(_fail, "Participant found the answer but
jury didn’t");
}
}
quitf(_ok, "Correct");
}
/*
Apare la executie:

argc = 4
checker
../tests/110
../tests/110.a
LCS.out
----------------------
ok Correct (50000 test cases)

Process returned 0 (0x0) execution time : 5.539 s


Press any key to continue.
*/

5.6.3 *Rezolvare detaliată


Capitolul 6
112
EJOI 2021

6.1 AddK
Problema 1 - AddK 100 de puncte
Se consideră un şir A cu N elemente numere naturale A1 , ..., AN şi un număr natural K. Se
cere să se proceseze Q cerinţe de următoarele două tipuri:
ˆ 1 i1 i2 . . . iK : se permută circular la stânga elementele şirului Ai1 , ..., AiK . Astfel noile
valori ale elementelor Ai1 , Ai2 , ..., A  iK 1 , AiK vor fi Ai2 , Ai3 , ..., AiK , Ai1 . Remarcaţi că
i1 , ..., ik sunt distincte şi nu neapărat in ordine crescătoare.
ˆ 2 l r m: se cere calculul sumei elementelor tuturor subsecvenţelor continue de lungime m
din secvenţa Al , Al1 , ..., Ar1 , Ar . Remarcaţi că elementele care apar ı̂n mai multe secvenţe
vor fi adunate de mai multe ori.

Date de intrare
Prima linie a fisierului de intrare standard conţine două numere ı̂ntregi, N şi K. A doua linie
conţine N numere ı̂ntregi: elementele vectorului A. A treia linie conţine un ı̂ntreg Q, numărul de
cerinţe, şi apoi Q linii conţinând cerinţele, care pot fi din cele două tipuri descrise mai sus.
Date de ieşire
Fişierul standard de ieşire trebuie să conţină răspunsurile la cerinţele de tip 2, câte unul pe linie.
Restricţii
# Punctaj Restricţii
0 & Ai & 10
6
1 36 1 & N, Q & 10 000, K 1
1&l&r&N
10 001 & N, Q & 100 000, K 1
1&m&rl1
2 56
3 8 1 & N, Q & 100 000, 2 & K & 10
Example

Fişier de intrare Fişier de ieşire


8 3 52
7 2519346 50
3
2 274
1 258
2 273

Explicaţii
Prima cerinţă este de tip 2 şi trebuie să calculăm suma elementelor tuturor subsecvenţelor de
lungime m 4 din secvenţa 2, 5, 1, 9, 3, 4. Aceste subsecvenţe sunt (2, 5, 1, 9), (5, 1, 9, 3), (1,
9, 3, 4), iar suma elementelor lor este 52.
112
aur: Ştefan Cătălin Savu, cls7, Colegiul Naţional ”I.L. Caragiale”, Ploieşti
. argint: Iulian George Arsenoiu, cls8, Şcoala Gimnazială ”Ing. Gh. Pănculescu”, Vălenii de Munte
. argint: Rareş Felix Tudose, cls8, Colegiul Naţional ”Alexandru Papiu Ilarian”, Târgu Mureş
. bronz: Tudor Ştefan Muşat, cls8, Colegiul Naţional de Informatică ”Tudor Vianu”, Bucureşti

159
CAPITOLUL 6. EJOI 2021 6.1. ADDK 160

A doua cerinţă este de tip 1 şi are ca efect permutarea circulară a elementelor din şirul A,
situate pe poziţiile 2, 5, 8. Astfel, şirul A devine 7, 9, 5, 1, 6, 3, 4, 2.
A treia cerinţă este de tip 2 şi trebuie să calculăm suma elementelor tuturor subsecvenţelor de
lungime m 3 din secvenţa 9, 5, 1, 6, 3, 4. Aceste subsecvenţe sunt (9, 5, 1), (5, 1, 6), (1, 6, 3),
(6, 3, 4), iar suma elementelor lor este 50.

6.1.1 Indicaţii de rezolvare

(Proposed by Mihai Bunget)


Subtask 1 A type 1 requirement is not processed because the array A does not change.
For query of type 2 we calculate the sum S Al  Al1  ...  Alm1 , which is added to the
result, then for every i from l  m to r S S  Aim  Ai is updated, which is added to the result.
Subtask 2 A type 1 requirement is not processed because the array A does not change.
For the type 2 requirement we observe the following two cases:

ˆ If the length of the sequence Al , Al1 , ..., Ar1 , Ar , ie r  l  1, is at least 2 m, then the sum
of the elements of all subsequences of length m in this sequence will be S S1  S2  S3 ,
where
S1 1 Al  2 Al1  ...  m  1 Alm2 ,
S2 r  l  1  2 m  1 Alm  Alm1  ...  Arm1 ,
S3 m  1 Arm2  ...  2 Ar1  1 Ar .
ˆ If the length of the sequence Al , Al1 , ..., Ar1 , Ar is less than 2 m, then the sum of the
elements of all subsequences of length m in this sequence will be S S1  S2  S3 , where
S1 1 Al  2 Al1  ...  rlm Arm1 ,
S2 r  l  2  m Arm  Arm1  ...  Alm1 ,
S3 rl  m  1 Alm  ...  2 Ar1  1 Ar .

For this we construct the arrays of partial sums B and C, so that


Bi A1  A2  ...  Ai and
Ci 1 A1  2 A2  ...  i Ai ,
for any i from 1 to N .
These being constructed, for the first case above we will have
S1 Clm2  Cl1  l  1 Blm2  Bl1 ,
S2 r  l  1  2 m  1 Brm1  Blm1 ,
S3 r  1 Br  Brm1   Cr  Crm1 .
Subtask 3 Because in this case we also have the update operation on array A we will use the
binary indexed tree structure for arrays B and C defined above.
Thus for the type 1 requirement we will update these BIT’s (binary indexed trees), for each of
the elements Ai1 , Ai2 , ..., AiK .
For the type 2 requirement we will use two interval calculation functions for arrays B and C, using
the BIT’s associated with these arrays. The calculation formulas are the same from subtask 2.

6.1.2 *Coduri sursă

***
CAPITOLUL 6. EJOI 2021 6.2. KPART 161

6.1.3 *Rezolvare detaliată

6.2 KPart
Problema 2 - KPart 100 de puncte
Virgil tocmai şi-a propus să studieze proprietăţi ale şirurilor. Astfel, el defineşte un K-şir ca
fiind orice şir de numere naturale nenule care are proprietatea că orice subsecvenţă a sa de lungime
K se poate partiţiona ı̂n două subşiruri disjuncte, nu neapărat subsecvenţe, având suma egală.
De exemplu 1, 2, 1, 3 e un 3-şir, căci 1, 2, 1 poate fi partiţionat ı̂n 1, 1 şi 2, şi 2, 1, 3 poate fi
partiţionat ı̂n 2, 1 şi 3. Nu este 2-şir căci 1, 2 nu poate fi partiţionat ı̂n două subşiruri cu sumă
egală. Totodată nu este 4-şir.
Pentru T şiruri de numere naturale nenule A, Virgil doreşte să afle toate valorile K, pentru
care şirul A poate fi numit K-şir.
Date de intrare
Pe prima linie se află numărul T . Urmează apoi T şiruri. Fiecare şir este dat prin două linii.
Prima linie conţine valoarea lui N . A doua linie conţine elementele şirului separate prin câte un
spaţiu.
Date de ieşire
Afişaţi răspunsurile pentru fiecare şir ı̂n ordine. Pentru fiecare şir afişaţi câte o linie care
conţine mai ı̂ntâi numărul de valori K pentru care şirul este K-şir şi apoi, ı̂n ordine crescătoare,
acele valori K pentru care şirul este K-şir.
Restricţii

ˆ 1&T & 20.


ˆ Fie< A suma valorilor dintr-un şir (nu suma valorilor tuturor şirurilor).
Atunci 1 & < A & 100 000.

# Punctaj Restricţii
1 10 1 & N & 30
2 20 31 & N & 120
3 70 121 & N & 1 000

Example

Fişier de intrare Fişier de ieşire


2 246
7 236
7351335
6
123583

Explicaţii
Primul şir, cel de lungime 7 este 4-şir şi 6-şir, deoarece fiecare secvenţă de lungime 4, respectiv
6, conţin câte două subşiruri disjuncte cu suma egală care partiţionează secvenţa. Al doilea şir,
cel de lungime 6 este 3-şir şi 6-şir, deoarece fiecare secvenţă de lungime 3 şi fiecare secvenţa de
lungime 6, conţin câte două subşiruri disjuncte cu suma egală care partiţionează secvenţa.

6.2.1 Indicaţii de rezolvare

(Proposed by Ionel-Vasile Piţ-Rada)


We will further note by ksir A, N, pos, kV alues the context of the solution for the string
A1 , A2 , ..., AN . The kV alues string contains, in ascending order, all the values K for which A is
CAPITOLUL 6. EJOI 2021 6.3. XCOPY 162

K-string. The pos string is defined by poss= the highest index, between 1, 2, 3, ..., N , for which
there is a sub-sequence that starts with Aposs and has the amount s. The values poss will
be equal to -1 for the amounts s, 1 & s & 50 000, which have not yet been reached. The value
pos0 is initially equal to 0.
Suppose we have solved the problem ksir A, N  1, pos, kV alues and we want to get the
solution for the problem ksir A, N, pos, kV alues. How can we proceed?
The newly appeared value is AN , so we will update in a first step the string pos by going
through decreasing all the positive amounts s already reached and updating the poss  AN 
each time s  AN  & 50 000. The update will be made using the expression poss  AN 
max poss  AN , poss. Then it will update posAN  with N .
In the second stage we will go through the string kV alues and update it. Note that the values
kV aluesj , 1 & j & length kV alues are all already validated for the sub-string A1 , A2 , ..., AN 1 .
What we are interested in now is to check/validate the sub-strings AN kV aluesj 1 ...AN , check-
ing if the amounts s AN kV aluesj 1  AN kV aluesj 2  ...  AN are even and poss©2 '
N  kV aluesj   1, where 1 & j & length kV alues, which ensures that the amounts s©2 can
be obtained using only elements of a sub-sequence inside the verified sub-string. The sub-string
A1 ...AN is checked/validated separately using the same idea. Obviously, all valid kV aluesj 
values will be kept.
Initially it will start with the context of the ksir A, 0, pos, kV alues in which A is the empty
string, length kV alues 0, pos0 0 and posj  1 for 1 & j & 50 000, and then step
by step the contexts will be updated ksir A, i, pos, kV alues, for 1 & i & N . The complexity is
O T N S , where S & 50 000.

6.2.2 *Coduri sursă

***

6.2.3 *Rezolvare detaliată

6.3 XCopy
Problema 3 - XCopy 100 de puncte
Astăzi, la finalul orei de informatică, profesorul a dat ca temă pentru acasă o problemă foarte
dificilă, aşa că elevii s-au hotărât să copieze unii de la alţii. Vor trebui totuşi să lucreze cât mai
deştept pentru a nu fi prinşi că au copiat.
Clasa este alcatuită din N  M elevi, aşezaţi ı̂n bănci pe N rânduri şi M coloane. Spunem că
doi elevi sunt vecini dacă se află ı̂n bănci adiacente fie pe rânduri, fie pe coloane. Tema fiecărui
copil constă ı̂n găsirea unui număr natural. Pentru ca elevii să nu fie prinşi că au copiat, toate
temele acestora vor trebui să fie distincte. Mai mult, elevii sunt foarte leneşi, aşa că ı̂şi vor modifica
tema foarte puţin faţă de tema vecinilor. Mai exact, tema oricarui elev diferă prin exact un bit ı̂n
scrierea ı̂n baza 2 faţă de tema oricărui vecin al său. De exemplu 3 şi 2 diferă prin exact un bit,
pe când 2 şi 4 diferă prin doi biţi.
Pentru a nu ridica suspiciuni prea mari, elevii doresc să creeze temele astfel ı̂ncât cea mai mare
valoare a unei teme să fie cât mai mică posibil. Fiind date dimensiunile clasei, N şi M , contruiţi
o configuraţie a valorilor temelor fiecarui elev din clasă astfel ı̂ncât profesorul să nu ı̂şi dea seama
că aceştia au copiat.
Date de intrare
Pe prima şi singura linie a datelor de intrare se vor afla 2 numere separate printr-un singur
spaţiu, N şi M cu semnificaţia de mai sus.
Date de ieşire
Datele de ieşire constau ı̂n afişarea configuraţiei temelor fiecărui elev. În fişier se vor afla N
rânduri, iar pe fiecare rând se vor afla M numere naturale separate printr-un spaţiu. Acestea
reprezintă răspunsurile copiilor, corespunzătoare poziţiei lor ı̂n clasă.
CAPITOLUL 6. EJOI 2021 6.3. XCOPY 163

Restricţii

ˆ 1 & N, M & 2 000

# Punctaj Restricţii
1 7 N 1.
2 9 N, M sunt puteri de 2.
3 14 N este o putere de 2.
4 70 Nicio restricţie suplimentară.

Punctare
Această problemă acceptă şi soluţii parţiale, astfel se va acorda punctaj parţial pentru fiecare
test, ı̂n funcţie de cât de aproape de răspunsul optim este soluţia dată folosind următoarea formulă:
Ø
G
 1
S max 

1 
O
, 0


3
 

unde:
a S este punctajul testului,
a G este răspunsul dat,
a O este răspunsul optim.
Atenţie! O soluţie care nu respectă toate cerinţele problemei pentru un anumit test (toate
numerele să fie distincte şi oricare două numere adiacente să difere printr-un singur bit) va fi
punctata cu 0 pe acel test.
Example

Fişier de intrare Fişier de ieşire


33 546
102
9 8 10

Explicaţii
În această secţiune, indicele poziţionat in dreapta-jos a numărului reprezintă baza ı̂n care este
scris. Spre exemplu, opt poate fi scris drept 810 10002 .
Unul dintre răspunsurile optime pentru copii sunt date ı̂n urmatorul tabel:

01012 510 01002 410 01102 610


00012 110 00002 010 00102 210
10012 910 10002 810 10102 1010

Putem observa că ı̂ntre oricare două bănci adiacente numerele elevilor din acele bănci diferă cu
exact un bit. Valoarea maximă a soluţiei este 10 si este răspunsul optim. Este evident că există
şi alte răspunsuri optime - spre exemplu, soluţia propusa dar oglindită vertical sau orizontal.
Una dintre soluţiile parţiale posibile ı̂n care maximul este 15 este:

01102 01112 01012


11102 11112 11012
10102 10112 10012

Această soluţie ar fi punctată, conform formulei de punctare, cu 59,1% din punctajul testului.
CAPITOLUL 6. EJOI 2021 6.3. XCOPY 164

6.3.1 Indicaţii de rezolvare

(Proposed by Tulba-Lecu Theodor-Gabriel & Pop Ioan Cristian)


Disclaimer The subtasks were designed to give contestants a way to solve the task step by
step. Each subtask adds a new level of difficulty and brings you closer to the optimal solution. The
proofs for all the observations can be found at the end of the editorial in the Appendix section.
1 Solution for subtask 1
When we set N 1, the task is reduced to finding an array such that any two consecutive
numbers differ by exactly one bit. This is well known as a Gray code.
We can greedily generate a partial Gray code of length M that contains all the elements from 0
to M  1 exactly once. For the rest of the editorial we will refer to this as a ”compact Gray code”.
A constructive algorithm can be found in the section A of the Appendix.
2 Solution for subtask 2
For this subtask we need to worry if and how the bits that change when moving on a row,
influence those that change when moving on a column. It turns out that the two sets of bits are
completely independent of each other (no bit can be influenced by both moving on a row or by
moving on a column). A proof for this can be found in the section B of the Appendix.
Since both N and M are powers of 2, the Gray codes are complete and thus we only need to
concatenate the Gray code for the rows, and columns. An example for such a test would be N 4
and M 8. A possible optimal answer would be:

0 1 3 2 6 7 5 4
8 9 11 10 14 15 13 12
24 25 27 26 30 31 29 28
16 17 19 18 22 23 21 20

3 Solution for subtask 3


The third subtask is a combination of the first two subtasks. You need to compute a ”compact
Gray code” for the column set of bits. And then concatenate the bits of the two sets in a way to
allow an optimal answer.
4 Complete solution
The last observation we need to make in order to solve the problem is how to combine the two
sets of bits to obtain a minimal value. Just concatenating doesn’t necessarily produce an optimal
outcome. For example for N, M 6 if we generate the following ”compact Gray code” for both
sets of bits:
5 101 2 , 1 001 2 , 3 011 2 , 2 010 2 , 0 000 2 , 4 100 2
and concatenate the answers we would obtain the following matrix:

101101 101001 101011 101010 101000 101100


001101 001001 001011 001010 001000 001100
011101 011001 011011 011010 011000 011100
010101 010001 010011 010010 010000 010100
000101 000001 000011 000010 000000 000100
001101 001001 001011 001010 001000 001100

with a maximum value of 45 101101. Whereas if we look at the maximum value (5 101 2 ),
we can observe that we can merge the two 5s in the following way: 43 101011. Thus we can
minimize the maximum value of the matrix to 43 instead of 45 and obtain the following matrix:

1010 11 1000 11 1001 11 1001 10 1000 10 1010 10


0010 11 0000 11 0001 11 0001 10 0000 10 0010 10
0110 11 0100 11 0101 11 0101 10 0100 10 0110 10
0110 01 0100 01 0101 01 0101 00 0100 00 0110 00
0010 01 0000 01 0001 01 0001 00 0000 00 0010 00
1010 01 1000 01 1001 01 1001 00 1000 00 1010 00
CAPITOLUL 6. EJOI 2021 6.3. XCOPY 165

The merging can be done in various ways, one of which would be using dynamic programming:
dpi,j = the minimum value that can be obtained using the first i bits of the maximum of the
first set and the first j bits of the maximum of the second set.

A Compact Gray code constructive algorithm


n
Gray codes of power-of-two length. Note that a Gray code of length 2 can be created
by setting element i (indexed from 0) to i h idiv2. This Gray code can also be rotated (thus if
g1 , ..., g2n is a Gray code, so is gk , ..., g2n , g1 , ..., gk1 . We leave as an exercise to the reader proving
that this works.
Gray codes of arbitrary length. Observe the following facts:

n n
ˆ We can create a Gray code of length 2 starting with any number from 0 to 2  1, using
the previous construction suitably rotated.
ˆ Any integer is a sum of distinct powers of 2.

These facts suggest immediately an algorithm: create Gray codes of lengths equal to the powers
of 2 that compose the length of the desired Gray code, and put them together in some way. We
will show how this is done by example, for length 7.
Note that Gray codes for length 1, 2 and 4 respectively are 0, 1, 3, 2, 0, 1 and 0 respectively.
We now ”add in” the high order bit in the second and third Gray codes to get 0, 1, 3, 2, 4, 5, 6.
Now we need to rotate the Gray codes so that they can be adjacent, from right to left. 6 can only
be adjacent to 4, so the array ends with 5, 4, 6. 5 can only be adjacent to 1, so the full array is
3, 2, 0, 1, 5, 4, 6.

B The row-column independence


First let’s note that if we fix the elements at the indexes i, j , i  1, j  and i, j  1, then the
element at the index i  1, j  1 is uniquely determined by the following formula (h represents
the bitwise XOR operation):

M i  1j  1 M ij  1 h M i  1j  h M ij 

More than that, we can recursively extend this formula to:

M ij  M 0j  h M i0 h M 00

Now let’s suppose that there exists a bit such that it is changed both on a row and a column.
That means that there exists a bit k and a row i and column j such that:
M i  1x M ix h 2 , ¾x " r0, 1, ..., M  1x
k

M xj  1 M xj  h 2 , ¾x " r0, 1, ..., N  1x


k

From the previous relation we get the following result:


M i  1j  1 M ij  1 h M i  1j  h M ij 
k k
M i  1j  1 M ij  h 2 h M ij  h 2 h M ij 
M i  1j  1 M ij 
In conclusion, if there would exist a bit that is influenced both by a line and by a column, then
the condition of distinctness would not be satisfied.

6.3.2 *Coduri sursă

***
CAPITOLUL 6. EJOI 2021 6.4. BINSEARCH 166

6.3.3 *Rezolvare detaliată

6.4 BinSearch
Problema 4 - BinSearch 100 de puncte

bool binary_search(int n, int p[], int target ){


int left = 1, right = n;
while(left < right ){
int mid = (left + right) / 2;
if(p[mid] == target)
return true;
else if(p[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
if(p[left] == target) return true;
else return false;
}

Este bine ştiut faptul că dacă p este sortat, atunci codul returnează true dacă şi numai dacă
target apare ı̂n p. Pe de altă parte, acest lucru poate să nu se ı̂ntâmple dacă p nu este sortat.
Vi se dă un număr natural n şi o secvenţă b1 , ..., bn " rtrue, f alsex. Se garantează că există
k
un număr natural k pentru care n 2  1. Trebuie să generaţi o permutare p a elementelor
r1, ..., nx care ı̂ndeplineşte anumite condiţii. Fie S p numărul de indici i " r1, ..., nx pentru care
binary search n, p, i nu returnează bi . Trebuie sa alegeţi p astfel ı̂ncât S p este mic (aşa cum
este detaliat ı̂n secţiunea ”Restricţii”).
(Notă: o permutare a mulţimii of {1, . . . , n} este o secvenţa de n numere naturale care
conţine fiecare numar natural de la 1 la n fix odată.)
Date de intrare
Prima linie conţine T , numărul de teste. Urmează apoi testele.
Prima linie a unui test conţine numărul natural n. Pe cea de-a doua linie se găseşte un şir de
n caractere ce conţine doar caracterele ’0’ şi ’1’. Aceste caractere nu sunt separate prin spaţii.
Dacă cel de-al i-lea caracter este ’1’, atunci bi true, iar dacă este ’0’, atunci bi f alse.
Date de ieşire
Răspunsul pentru un test va fi o permutare p.
Restricţii

ˆ Fie < n suma tuturor valorilor n ı̂ntr-un singur fişier.


ˆ 1 & < n & 100 000.
ˆ 1&T & 7 000.
k
ˆ Există un număr natural k pentru care n 2  1.

ˆ Dacă S p & 1 pentru toate testele dintr-un subtask, atunci se primesc 100% din punctele
alocate acelui subtask.
În caz contrar, dacă 0 & S p & *log2 n0 (adică 1 & 2 & n  1) pentru toate testele
S p
ˆ

dintr-un subtask, atunci se primesc 50% din punctele alocate acelui subtask.

# Punctaj Restricţii
1 3 bi true.
2 4 bi f alse.
3 16 1 & n & 7.
4 25 1 & n & 15.
16
5 22 n 2 1 şi fiecare bi este generat uniform aleator din mulţimea rtrue, f alsex.
6 30 Fără restricţii suplimentare.
CAPITOLUL 6. EJOI 2021 6.4. BINSEARCH 167

Example

Fişier de intrare Fişier de ieşire


43 1 2 3
111 1 2 34567
7 3 2 1
1111111 7 6 54321
3
000
7
000000000
23 321
010 7315246
7
0010110

Explicaţii
Exemplul 1. În primele două teste avem S p 0.
În cel de-al treilea test, avem S p 1.
Acest lucru se ı̂ntâmplă deoarece binary search n, p, 2 returnează true, deşi b2 f alse.
În cel de-al patrulea test, avem S p 1.
Acest lucru se ı̂ntâmplă deoarece binary search n, p, 4 returnează true, deşi b4 f alse.
Exemplul 2. Avem S p 0 pentru ambele teste.

6.4.1 Indicaţii de rezolvare

(Proposed by Maria-Alexa Tudose)


Let us call the value n2 1 ”the middle value” and the position n2 1 (in the permutation p) ”the
middle position”.
Subtask 1. When bi true for all i, we can achieve S p 0 by setting p to be the increasing
permutation.
Subtask 2. When bi f alse for all i, we can achieve S p 1 in the following way:
ˆ Place the middle value on the middle position.
ˆ Place all values smaller than the middle value on positions n2 1  1, ..., n, in any order.

ˆ Place all values bigger than the middle value on positions 1, ..., n2 1  1, in any order.
In particular, note that the decreasing permutation works.
Subtask 3. In this subtask N 7 holds. We can use the fact that 7! 5040 is small. This
allows us to generate all the possible permutations of size n, and for each such permutation we
can run binary search on all values from 1 to n and compare the returned result with the desired
value in b. We calculate S p for all permutations p, and print any permutation p that achieves
S p1.
Subtask 4. This subtask encourages solutions with sub-optimal (but polynomial) time com-
plexities. For example, a poor implementation of the ”Solution 1” presented below might have a
2
time complexity of O n  instead of the optimal O n.
Subtask 5. In this subtask, the sequence b is guaranteed to be generated randomly. This
allows us to design solutions which use different facts, such as:
ˆ The numbers of ones and zeros in b should be approximately equal.
ˆ There are not many consecutive equal entries in b.
CAPITOLUL 6. EJOI 2021 6.4. BINSEARCH 168

Subtask 6. The problem admits a wide variety of full solutions which promote different types
of thinking. We present only a few of them.
Solution 1. We try to place, in turn, each possible value on the middle position. Let X be
our current try. Our aim is to find p for which bi binary search n, p, i for all values i j X.
This would give S p 0 if bX 1 and would give S p 1 if bX 0.
We define two sets L and R:
ˆ L ri¶bi true, i $ X x < ri¶bi f alse, i % X x
ˆ R ri¶bi true, i % X x < ri¶bi f alse, i $ X x

We also define two sets A and B:


ˆ A ri¶bi truex
ˆ B ri¶bi f alsex

Lemma. If ¶L¶ ¶R¶ n2 1 for some X, then we can find a good permutation.
Proof. One way to build such a permutation is:
ˆ First place the values in L in increasing order
ˆ Then place the values in R in increasing order

Lemma. There exists an X such that ¶L¶ ¶R¶ n2 1 .


Proof. We aim to achieve ¶L¶ n2 1 .
When we change X to X  1, ¶L¶ either stays constant, increases by 1, or decreases by 1.
For X 1, we have ¶L¶ ¶B  r1x¶. Also, when we set X n, we have ¶L¶ ¶A  rnx¶.
Therefore, ¶L¶ & n2 1 for X 1 or for X n, and ¶L¶ ' n2 1 for X 1 or for X n. Combining
these observations, we get the conclusion.

Solution 2. If A o, then we return the decreasing permutation.


From now on, assume that A j o.
We choose X to be any value from A for which ¶L = A¶, ¶R = A¶ & n2 1 .
Intuitively, this means that we place on the middle position any value from A for which all
remaining values from A ”fit properly” in the remaining halves.
There are 2 cases now: ¶L¶ ' ¶R¶ and ¶L¶ $ ¶R¶. We will treat only the first case, because the
second one can be solved symmetrically.
Assuming ¶L¶ ' ¶R¶, let W be a set of size ¶L¶  n2 1 such that W N L = B. We will place the
values in L  W in the first half of the permutation in increasing order and the values in R < W
in the second half (in an order to be determined).
All elements in the first half will get the required result when calling binary search. The value
on the middle position also gets the required result.
We therefore need to arrange the elements in the second half such that we get at most one
position i for which bi j binary search n, p, i. We can solve this recursively.
Solution 3. (This solution is due to Tamio-Vesa Nakajima.) We will create a procedure
solve A, B  which creates a sequence p for which S p & 1 if A is the set of indices i with
bi true and B is the set of indices i with bi f alse. This will be a recursive procedure, using
the following cases:

ˆ If A o then we are in subtask 2 - output B sorted in decreasing order.


ˆ If ¶A¶  ¶B ¶ & 3 then the answer can be easily found by hand (there are only 8 cases).
ˆ Now suppose A j o and ¶A¶  ¶B ¶ ' 7. Thus at least one element should be ”found”. We
have two further cases:

– Suppose ¶A¶ % ¶B ¶. Suppose there are 2  1 elements overall. Let A contain the first
k
k
2 1 elements of A in increasing order. Then output A in increasing order first, followed
by solve A  A, B . For example if A r1, 3, 5, 6, 7x, B r2, 4x, then A r1, 3, 5, 6x
and our output starts with 1, 3, 5, 6 followed by the result of solve A  A r7x, B
r2, 4x.
CAPITOLUL 6. EJOI 2021 6.5. DUNGEONS 169

– Suppose ¶A¶ $ ¶B ¶. Suppose ¶A¶  ¶B ¶ 2  1. (Note that the case ¶A¶ ¶B ¶ is


k
k1
impossible as ¶A¶  ¶B ¶ 2  1 is odd.) Since ¶B ¶ % ¶A¶ we deduce that ¶B ¶ ' 2  1.
k
k2
Let t 2  1. Let X be the first t elements of B in increasing order and Y be the
last t elements of B in increasing order. Since ¶B ¶ ' 2t we deduce that X = Y o i.e.
X and Y have no common elements. Let b be an arbitrary element from B  X  Y .
There are two cases: either b $ min A or b % min A - we will assume without loss of
generality that b $ minA, since the other case is treated symmetrically.
We construct our array as follows:
* The first t elements of the result are Y (in any order).
* The next element of the result is b.
* The next t elements of the result are X (in any order).
* The next element (in fact the middle element) should be a min A.
* The second half of the result should be solve A  rax, B  X  Y  rbx.
Observe that:
* All of the elements in Y are greater than b, and thus are not found.
* b $ min A and thus is not found.
* All of the elements in X are less than b and thus are not found.
* min A is immediately found.
* By the correctness of solve the elements in the second half contribute at most 1 to
S p.

As an example, suppose A r3, 4x, B r1, 2, 5, 6, 7x. Then X r1x, Y r7x, b 2, and
2 b $ min A 3. Thus the array begins with 7, 2, 1, 3 followed by the result of ({4}, {5, 6}),
which can be 5, 4, 6 for instance. Thus the result is 7, 2, 1, 3, 5, 4, 6, with S p 1.

6.4.2 *Coduri sursă

***

6.4.3 *Rezolvare detaliată

6.5 Dungeons
Problema 5 - Dungeons 100 de puncte
Dungeon Crawl: Paper Soup tocmai a devenit cel mai popular joc, iar tu eşti pe cale să ı̂l
ı̂ncerci. Jocul se desfăşoară pe un teren dreptunghiular cu N linii şi M coloane, unde fiecare
celulă este de unul dintre cele cinci tipuri descrise mai jos:
ˆ celulă liberă ’.’;
ˆ perete ’#’;
ˆ celulă cu monedă ’o’;
ˆ celulă cu mină explozivă ’X’;
ˆ celulă de start ’S’;
Se garantează că pe prima, respectiv ultima linie şi coloană se află pereţi (de precizat că este
imposibilă deplasarea prin pereţi). Terenul poate conţine una sau mai multe celule de start. În
momentul ı̂n care jocul ı̂ncepe, jucătorul va fi poziţionat iniţial ı̂ntr-una dintre celulele de start,
marcate cu ’S’. Deoarece jocul se desfăşoară ı̂ntr-un sistem de peşteri cu vizibilitate redusă (vezi
numele jocului), jucătorul nu poate vedea toată harta, ci doar o zona de vizibilitate restrânsă,
reprezentată de un pătrat de 3  3 centrat ı̂n poziţia sa curentă. Mai mult, ı̂n această zonă de
vizibilitate minele şi celulele de start apar drept celule libere (sunt invizibile pentru jucător).
La fiecare pas, jucătorul poate să se mişte pe direcţiile nord, sud, est sau vest. Dacă acesta
ajunge pe o poziţie cu o monedă, colectează moneda, iar aceasta dispare de pe hartă. Dacă acesta
CAPITOLUL 6. EJOI 2021 6.5. DUNGEONS 170

ajunge pe o poziţie cu mină, sistemul de peşteri se prăbuşeste, jucătorul pierde toate monezile
colectate până ı̂n acel moment, iar jocul se termină.
Din fericire, urmărind diverse ghiduri pe internet ai aflat harta exactă a terenului, ı̂nsă nu ştii
ı̂n care dintre punctele de start vei fi repartizat - este garantat ı̂nsă că vei porni dintr-o celulă de
start. Considerând că vei adopta cea mai bună strategie, care este numărul maxim de monezi pe
care ı̂l poţi obţine garantat, indiferent de unde vei fi poziţionat la ı̂nceput?
Date de intrare
Pe prima linie ı̂n fişierul de intrare se vor găsi valorile N şi M : dimensiunile terenului pe care
se va desfăşura jocul, conform ghidurilor de pe internet. Următoarele N linii conţin fiecare câte
un şir de caractere de lungime M , reprezentând harta, conform codificării descrise ı̂n enunţ.
Date de ieşire
În fişierul de ieşire se va afişa un singur număr natural, numărul maxim de monezi care se
poate obţine garantat pe acel teren.
Restricţii

ˆ Fie S numărul de celule de start ce se află pe hartă.


ˆ N & 400, M le400, S & 60.

# Punctaj Restricţii
1 3 S 1. Nu există mine. Cu excepţia primei
şi ultimei linii şi coloane, nu există pereţi.
2 7 N 3
3 12 S 1
4 23 S 2
5 41 1 & N, M & 250, 1 & S & 12
6 14 Nicio
restricţie su-
plimentară

Example

Fişier de intrare Fişier de ieşire


37 4
#######
#Soooo#
#######
38 1
########
#SoXooS#
########
7 18 0
##################
#................#
#.o...SX.......o.#
#.o...X..X.....o.#
#.o.....XS.....o.#
#................#
##################
CAPITOLUL 6. EJOI 2021 6.5. DUNGEONS 171

7 18 6
##################
#....#...........#
#.o...SX.......o.#
#.o...X..X.....o.#
#.o.....XS.....o.#
#.........#......#
##################
7 18 1
##################
#......X..S....oo#
##################
#..o..S.X......o.#
##########X#######
#o.....S...X.....#
##################

Explicaţii
Exemplul 1 Există o singură poziţie de start, deci ştim exact de unde va porni jucătorul. În
acest caz, jucătorul poate colecta toate monezile.
Exemplul 2 Sunt două poziţii de start, iar jucătorul poate deduce unde este poziţionat pe
baza zonei de vizibilitate (@ este poziţia jucătorului):
### ###
#@o o@#
### ###
Numărul maxim de monezi pe care le poate colecta jucătorul dacă porneşte din partea stângă
este 1, respectiv 2 dacă porneşte din partea dreaptă. Deci, pe cel mai rău caz, putem colecta o
monedă.
Exemplul 3 ı̂n orice direcţie s-ar mişca iniţial jucătorul, pe caz nefavorabil se va afla o mină
ı̂n direcţia respectivă. Zona de vizibilitate iniţială este:
...
.@.
...

Exemplul 4 Jucătorul ı̂şi poate da seama ı̂n care celulă a fost plasat iniţial, analizând zona
de vizibilitate. Mai exact, acesta se uită dacă iniţial vede un perete ı̂n stânga-sus sau dreapta-jos;
astfel, ştie exact cum să se poziţioneze pentru a evita minele. Zonele de vizibilitate iniţiale aferente
celor două poziţii sunt:
#.. ...
.@. .@.
... ..#

Exemplul 5 Jucătorul se mută 2 paşi la stânga. Dacă vede o monedă la stânga, deduce că
este ı̂n zona din mijloc, o culege şi termină jocul. Dacă nu, ştie că nu este ı̂n zona din mijloc, aşa
că se mută la dreapta 4 paşi.
Observând mai apoi dacă există spaţiu liber ı̂n dreapta-sus (minele sunt văzute drept spaţii
libere), poate deduce dacă se află ı̂n zona de sus sau zona de jos. În ambele cazuri, este liber să
colecteze moneda/monezile corespunzătoare. În cel mai nefavorabil caz, ı̂nsă, va putea colecta o
singură monedă.
Se poate observa că jucătorul nu se putea muta iniţial spre dreapta, deoarece ar fi riscat să
atingă mina din centru pe caz nefavorabil.
CAPITOLUL 6. EJOI 2021 6.5. DUNGEONS 172

6.5.1 Indicaţii de rezolvare

(Proposed by Lucian Bicsi. Primarily prepared by Teodor-Gabrial Tulba-Lecu. We thank Tamio-


Vesa Nakajima for this editorial.)
To solve this problem, first note that the dungeon explorer’s ”mental state” consists of:
ˆ The current position relative to the starting position.
ˆ The set of possible starting positions.
Thus initially the state consists of position (+0, +0) relative to the starting position, and the
set S r1, ..., S x of starting position (represented by their indices).
Observe that if we know that we started at some subset of starting positions S, then we will
never go to a relative position that corresponds to a mine relative to any starting position from
S. This is because doing this would risk getting 0 coins. Other than this we can visit any relative
position.
Thus, we can imagine the following algorithm for the explorer:

ˆ Consider the current set of starting positions that we could have started at S.
ˆ Visit all possible squares that do not require us to visit a relative position that could corre-
spond to a bomb for any of the starting positions in S.
ˆ Check if we see any wall or coin that only appears in some proper subset of starting positions
S L S.
ˆ If such a position exists, continue the search from that subset.
ˆ Otherwise give up with the coins we can collect at this point.
How can we simulate this algorithm in our case? It is not difficult to make a version that does
O N M S  complexity for each starting set (simply do a breadth first search, checking if a position
is visitable, or respectively is a wall or a coin that only appears for some starting positions, by
iterating over the current starting positions).
To optimise this to use O N M  time for each set of starting positions that we check, store
the map ”relative to each starting position” in a bit mask. Thus we will have several matrices
wall/bomb/coin that, at position i, j  will contain a bit mask that have bit k equal to 1 if and
only if position i, j  relative to starting position k contains a wall/bomb/coin. This allows us to
check
(a) if a certain relative position contains a bomb for a subset of starting positions, and
(b) if a certain relative position contains a wall/coin in some starting positions but not others.
These can be done by representing the set of starting positions with a bit mask, and the using
a bitwise ”and” operation.
Then for (a) we check if the result is nonzero, and for (b) we check if it is neither zero or equal
to the bit mask that represents the set of starting positions.
Thus with this approach we can find, in O N M , for any set of starting positions:
ˆ The number of coins we can safely collect.
ˆ If any wall or coin is visible for only some subset of starting positions.
ˆ What that subset of starting positions is.
Now suppose f S  gives us the result for some set of starting positions S. The full result will
be f r1, ..., S x. To compute f S  use the following algorithm:

ˆ Check if some coin or wall is visible only in some subset S L S.


ˆ If so, then return min f S , f S  S .

ˆ Otherwise return the number of coins safely collectable if we would start at S.

It can be proved that this does at most S matrix traversals. Thus the final complexity is
O N M S .
CAPITOLUL 6. EJOI 2021 6.6. WATERFRONT 173

6.5.2 *Coduri sursă

***

6.5.3 *Rezolvare detaliată

6.6 Waterfront
Problema 6 - Waterfront 100 de puncte
Pe faleza râului Prahova primarul oraşului Ploieşti a plantat un şir de N arbuşti ornamentali
de diverse soiuri, fiecare arbust i având iniţial ı̂nălţimea heighti, 1 & i & N . În funcţie de solul
ı̂n care este plantat şi de vreme, arbustul i creşte zilnic cu ı̂nălţimea dailyGrowthi.
În fiecare zi grădinarul primăriei ajustează, prin tăiere cu o foarfecă, ı̂nălţimea arbuştilor.
Totuşi, grădinarul este limitat de detaliile tehnice ale foarfecii. Astfel, acesta poate tăia la o
tăietură exact x centimetri din ı̂nălţimea unui arbust dacă ı̂nălţimea este cel puţin x (de notat
faptul că arbustul poate ajunge la ı̂nălţimea 0 după o tăietură). Pentru a nu se obosi, grădinarul
poate să efectueze ı̂ntr-o zi cel mult k tăieturi. Grădinarul poate să efectueze mai multe tăieturi
asupra unui arbust ı̂ntr-o zi.
Primarul organizează după M zile un eveniment artistic şi doreşte să aflaţi care este ı̂nălţimea
minimă a celui mai ı̂nalt arbust după cele M zile.
Atenţie! În fiecare zi arbustul ı̂ntâi creşte şi apoi se fac tăierile.
Date de intrare
Fişierul de intrare conţine pe prima linie numerele naturale N , M , k şi x. Pe următoarele N
linii se află câte două numere naturale heighti şi dailyGrowthi, separate prin spaţiu.
Date de ieşire
Afişaţi un număr nenegativ reprezentând ı̂nălţimea minimă a celui mai ı̂nalt arbust după cele
M zile.
Restricţii

ˆ 1&k & 1 000


ˆ 1 & x & 10 000
ˆ 0 & heighti & 10 000
ˆ 0 & dailyGrowthi & 10 000

# Punctaj Restricţii
1 8 N & 100, M 1, k 1, x 1, heighti ' 1, dailyGrowthi 0
2 22 1 & N, M & 500
3 43 1 & N, M & 5 000
4 27 1 & N, M & 10 000

Example

Fişier de intrare Fişier de ieşire


4 343 8
2 5
3 2
0 4
2 8
CAPITOLUL 6. EJOI 2021 6.6. WATERFRONT 174

Explicaţii
Grădinarul taie arbuştii ı̂n 3 zile, ı̂n fiecare zi făcând câte 4 tăieturi. La fiecare tăietură poate
elimina câte 3 cm din ı̂nălţimea arbustului. Următorul tabel ilustrează modul optim de efectuare
a tăierilor:

Ziua Co- Operaţii


pac
5 3
1 1 2 7 4
2
2 3 5
3 4
0 4
4 8 3 3 3
2 10 7 4 1
5 3 3
2 1 4 9 6 3
2
2 5 7
3 4
4 8
4 8 3 3
1 9 6 3
5
3 1 3 8
2 3
2 7 9 6
3 4 3 3
8 12 9 6
4 8 3
3 11 8

6.6.1 Indicaţii de rezolvare

(Proposed by Eugen Nodea. We thank Tamio-Vesa Nakajima for this editorial.)


We will describe the solution in several stages, starting from a brute-force solution, and then
gradually optimising it.
Brute force solution. A simple brute-force solution is the following:
ˆ Find the tree which will be the tallest at the end of the M days.
ˆ Find the first moment at which this tree can be cut, if such a moment exists.
ˆ Cut the tree at that moment, if it exists.
ˆ If not, output it’s height.
To see (broadly) why this is correct, suppose that we try to see if the solution can be at most
S. Then, based on the heights at the end of the M days, we know how many times each tree
must be cut (according to the formula *S  f inalHeight©x0). Each cut can then be done in some
suffix of days. We then need to assign cuts to days in some way. This is an example of an activity
selection problem. We have M days in which we can do k activities daily, and each activity can
be done in some suffix of days. It is well known that such problems can be solved using a greedy
approach - which is exactly what we do.
The previous remarks justify our brute force algorithm. If the real solution is S, then we note
that the set of tree cuts done by our brute force algorithm are precisely the tree cuts necessary
for solution all solutions greater or equal to S in decreasing order - thus stopping at precisely
the correct solution. Furthermore, we can see that our greedy solution is correct, since the set of
assigned activities will be the same as if we only considered those cuts (even if certain cuts may
be done in different days).
Optimisation 1. First we optimise the part of the solution where we find the first point at
which a cut can be made. Note that it is easy to check using arithmetic and some bookkeeping
the first point in time in which a certain tree is tall enough to be cut. We now need to find the
first day in which it can be cut. If we model the days as an array v 1, ..., v M , where v i is
the number of cuts allowed in day i, then we want to find the first non-zero value in some suffix
v a, ..., v M . Then we want to decrement that value (that is where the cut is done).
CAPITOLUL 6. EJOI 2021 6.6. WATERFRONT 175

How do we do this? Suppose we consider a artition of indices 1, ..., M where all adjacent
indices i where v i 0 are joined into the same partition. In this case to find the first nonzero
value to the right of index a, we find the set to which a belongs, we find the rightmost index r in
this partition, and:

ˆ if v r 0, then by the definition of the partition the next nonzero index if r  1;
ˆ otherwise r a and a itself can be decremented.

This partition can be efficiently maintained in log ˜M complexity using a disjoint set disjoint
set.
Optimisation 2. We now optimise the way of choosing the tree to cut. We previously said
that we always choose the tree that is tallest at the end of the M days. In essence this can be
simulated using a priority queue. The keys are the heights at the end of the M days, and the
values are the indices of the trees.
Optimisation 3. The idea from the previous paragraph can be optimised further. Note that
we actually have the following operations on our priority queue:

ˆ Finding the maximum key-value pair.


ˆ Decrementing the maximum key by a constant, x.

This type of priority queue can be implemented in constant time for each operation, with
n log n precalculation time. To do this, maintain two data structures, a stack S and a queue Q.
Insert all the key-value pairs into S in increasing order (so that the maximum value is at the
top of the stack). When trying to get the maximum key-value pair, take the maximum of the
key-value pair from among the top of S and the front of Q. To decrement it’s key, remove it from
S or Q respectively, and add it into Q (at the back), with a decremented key. We leave the proof
of correctness as an exercise.

6.6.2 *Coduri sursă

***

6.6.3 *Rezolvare detaliată


Capitolul 7
113
EJOI 2020

7.1 fountain
Problema 1 - Fountain 100 de puncte
O fântână nouă constă din N rezervoare de apă circulare, aliniate pe verticală şi numerotate
de sus ı̂n jos cu numere ı̂ntregi ı̂ncepând cu 1, ca mai jos:

Fiecare rezervor este caracterizat prin diametru, capacitate şi printr-un robinet care poate goli
orice cantitate de apă din el.
Ori de câte ori volumul de apă depăşeşte capacitatea rezervorului, excesul de apă se revarsă
pe la margini şi ajunge mai jos, ı̂n cel mai apropiat rezervor care are diametrul strict mai mare
sau ı̂n canalizare, dacă nu există un astfel de rezervor.
Ai de răspuns la Q ı̂ntrebări independente de tipul următor: care este numărul rezervorului
unde se termină debitul de apă dacă eliberaţi Vi litri de apă din al Ri -lea robinet? Dacă debitul
de apă se termină ı̂n canalizare, răspunsul va fi 0.
Input
Prima linie a intrării standard conţine doi ı̂ntregi: N şi Q.
Următoarele N linii conţin câte doi ı̂ntregi Di şi Ci , respectiv diametrul şi capacitatea celui
de-al i-lea rezervor.
Următoarele Q linii conţin fircare câte doi ı̂ntregi, Ri şi Vi .
Output
113
aur: Alexandru-Raul Todoran, clasa a 9-a, Liceul Teoretic “Aurel Vlaicu”, Orăştie,
. argint: Alexandru Dobleagă, clasa a 8-a, C. N. “I. L. Caragiale” din Ploieşti.

176
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 177

Scrie Q linii, cu câte un ı̂ntreg fiecare, reprezentând răspunsurile la ı̂ntrebări, ı̂n ordinea dată.
Constraints

ˆ 2&N & 105


1 & Q & 2 10
5
ˆ

ˆ 1 & Ci & 1000


ˆ 1 & Di , Vi & 109
ˆ 1 & Ri &N

Subtasks

1. (30 puncte): N & 1000; Q & 2000


2. (30 puncte): Diametrele sunt ı̂n ordine strict crescătoare de sus ı̂n jos (Di $ Di1 )
3. (40 puncte): Fără alte restricţii

Example
Input Output
65 5
4 10 0
68 5
35 4
4 14 2
10 9
4 20
1 25
6 30
58
3 13
28
Comment
Primele două ı̂ntrebări sunt ilustrate ı̂n imaginea de mai sus.
Întrucât interogările sunt independente, pentru a treia ı̂ntrebare, la al cincilea rezervor nu se
va revărsa apa.
Timp maxim de executare/test: 1.5 secunde
Memorie: total 512 MB

7.1.1 Indicaţii de rezolvare

We will describe how to solve the last subtask which was enough to achieve a full score.
Subtask 3:
The solution consists of three parts: building the graph (which is a tree), preprocessing the tree
and answering the queries.

ˆ For each reservoir we determine the next one (i.e. where the water flows into when the given
reservoir overflows). To do this efficiently, we can go from bottom to top and maintain the
stack of the reservoirs in ascending order of their diameters. Each time a new reservoir is
processed we remove smaller or equal reservoirs from the top of the stack and insert the
current reservoir, determining the next reservoir along the way. This can be done in O N .
After that we connect each reservoir to the next one and we end up with a directed tree with
a root in node 0 (that corresponds to the waterways).
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 178

ˆ We use the method called ”binary lifting, most commonly used in LCA algorithm, where
k
for each node we determine its 1st ancestor, 2nd ancestor, 4th ancestor, ..., 2 -th ancestor.
Additionally we precompute the total amount of water that the reservoirs up to the corre-
sponding ancestor can hold. Since k is of the order of log N , we will need O N log N  time
and O N log N  memory for this part.
ˆ For each query we keep moving to the farthest precomputed ancestor while the given amount
of water doesn’t exceed the sum of the capacities of the nodes traversed and find the answer.
This step has O Q log N  complexity.

7.1.2 Coduri sursă

Listing 7.1.1: checkerFountain.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../Fountain-tests/input.3_09",
(char*)"../Fountain-tests/output.3_09",
(char*)"fountain.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("fountain", argc, argv);


compareRemainingLines();
}

Listing 7.1.2: Fountain-979362.cpp


// https://loj.ac/s/979362 663 ms 16.2 M execution time : 0.871 s

#include <bits/stdc++.h>

#define LOG(FMT...) fprintf(stderr, FMT)

#define fir first


#define sec second

using namespace std;

typedef long long ll;


typedef unsigned long long ull;
typedef vector<int> vi;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<class T>
istream &operator>>(istream &is, vector<T> &v)
{
for (T &x : v)
is >> x;

return is;
}
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 179

ostream &operator<<(ostream &os, const pair<char, int> &unit)


{
return os << unit.first << "ˆ" << unit.second;
}

template<class T>
ostream &operator<<(ostream &os, const vector<T> &v)
{
if (!v.empty())
{
os << v.front();

for (int i = 1; i < v.size(); ++i)


os << ’ ’ << v[i];
}

return os;
}

const int N = 100010, L = 20;


const int INF = 1e9 + 10;

int d[N];
int f[N][L];
int s[N][L];

int main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(nullptr);

int n, q;
cin >> n >> q;

for (int i = 1; i <= n; ++i)


cin >> d[i] >> s[i][0];

d[n + 1] = numeric_limits<int>::max();
stack<int> stk;
stk.push(n + 1);

for (int i = n; i; --i)


{
while (d[stk.top()] <= d[i])
stk.pop();

f[i][0] = stk.top();
stk.push(i);
}

fill_n(s[n + 1], L, INF);


fill_n(f[n + 1], L, n + 1);

for (int i = n; i; --i)


for (int j = 1; j != L; ++j)
{
f[i][j] = f[f[i][j - 1]][j - 1];
s[i][j] = min(INF, s[i][j - 1] + s[f[i][j - 1]][j - 1]);
}

while (q--)
{
int r, v;
cin >> r >> v;

for (int i = L - 1; i >= 0; --i)


CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 180

if (s[r][i] < v)
{
v -= s[r][i];
r = f[r][i];
}

if (r == n + 1)
r = 0;

cout << r << ’\n’;


}

return 0;
}

Listing 7.1.3: Fountain-981117.cpp


// https://loj.ac/s/981117 621 ms 14.2 M execution time : 1.156 s

#include <cstdio>
#include <stdlib.h> /* exit, EXIT_FAILURE */

int n, q, d[100010], c[100010];


int stk[100010], top, fa[100010][17], sum[100010][17];

int main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);

scanf("%d%d", &n, &q);

for (int i = 1; i <= n; i++)


scanf("%d%d", d + i, c + i);

++n;
d[n] = 0x7f7f7f7f, c[n] = 1000000000;

for (int i = n; i; i--)


{
while (top && d[stk[top]] <= d[i])
--top;

fa[i][0] = stk[top];
sum[i][0] = c[i];
stk[++top] = i;
}

for (int j = 1; j <= 16; j++)


for (int i = 1; i <= n; i++)
fa[i][j] = fa[fa[i][j - 1]][j - 1],
sum[i][j] = sum[i][j - 1] + sum[fa[i][j - 1]][j - 1];

for (int i = 1, r, v; i <= q; i++)


{
scanf("%d%d", &r, &v);

for (int j = 16; ˜j; --j)


if (sum[r][j] < v)
{
v -= sum[r][j], r = fa[r][j];
}

printf("%d\n", r == n ? 0 : r);
}
}
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 181

Listing 7.1.4: Fountain-981322.cpp


// https://loj.ac/s/981322 511 ms 8.5 M execution time : 1.122 s

#include <cstdio>
#include <stdlib.h> /* exit, EXIT_FAILURE */

using namespace std;


const int N = 1e5 + 5;
const int logN = 18;

int n, Q, D[N], C[N];


int fa[N][logN], sum[N];

void init()
{
static int stk[N], top;
top = 0;

for (int i = n; i; i--)


{
for (; top && D[stk[top]] <= D[i]; --top);

fa[i][0] = !top ? 0 : stk[top];

sum[i] = C[i] + sum[fa[i][0]];


stk[++top] = i;
}

for (int i = 1; i < logN; i++)


for (int j = 1; j <= n; j++)
fa[j][i] = fa[fa[j][i - 1]][i - 1];
}

signed main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);

scanf("%d%d", &n, &Q);

for (int i = 1; i <= n; i++)


scanf("%d%d", D + i, C + i);

init();

while (Q--)
{
int R, V;
scanf("%d%d", &R, &V);

if (sum[R] < V)
{
printf("0\n");
}
else
{
for (int i = logN - 1; ˜i; --i)
if (fa[R][i] && V > sum[R] - sum[fa[R][i]])
V -= sum[R] - sum[fa[R][i]], R = fa[R][i];

if (V > C[R])
R = fa[R][0];

printf("%d\n", R);
}
}

return 0;
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 182

Listing 7.1.5: Fountain-983271.cpp


// https://loj.ac/s/983271 611 ms 21.9 M execution time : 0.656 s

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>

#include <stdlib.h> /* exit, EXIT_FAILURE */

#define setIO(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)


#define closefile fclose(stdin),fclose(stdout)
#define see(x) cerr<<x<<" "
#define seeln(x) cerr<<x<<endl
#define out(x) cerr<<#x<<" = "<<x<<" "
#define outln(x) cerr<<#x<<" = "<<x<<endl
#define outarr(x,l,r) cerr<<#x"["<<l<<"-"<<r<<"] = "; \
for (int _i=l;_i<=r;++_i) cerr<<x[_i]<<" "; cerr<<endl;
#define m_p make_pair
#define sz(x) (int)x.size()

using namespace std;

typedef long long ll;


typedef pair<int, int> pii;
typedef unsigned long long ull;

template <class T> void read(T &x)


{
x = 0;
char c = getchar();
int flag = 0;

while (c < ’0’ || c > ’9’)


flag |= (c == ’-’), c = getchar();

while (c >= ’0’ && c <= ’9’)


x = (x << 3) + (x << 1) + (c ˆ 48), c = getchar();

if (flag)
x = -x;
}

template <class T> T _max(T a, T b)


{
return b > a ? b : a;
}

template <class T> T _min(T a, T b)


{
return b < a ? b : a;
}

template <class T> bool checkmax(T &a, T b)


{
return b > a ? a = b, 1 : 0;
}

template <class T> bool checkmin(T &a, T b)


{
return b < a ? a = b, 1 : 0;
}
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 183

const int N = 100005, LOG = 17;

int n, q, d[N], c[N], b[N], top = 0;


int nxt[LOG + 1][N];
ll g[LOG + 1][N];

void init()
{
read(n);
read(q);

for (int i = 1; i <= n; ++i)


{
read(d[i]);
read(c[i]);
}

for (int i = 1; i <= n; ++i)


{
while (top && d[b[top]] < d[i])
nxt[0][b[top]] = i, --top;

b[++top] = i;
}

for (int i = 1; i <= n; ++i)


g[0][i] = c[nxt[0][i]];

for (int i = 1; i <= LOG; ++i)


for (int j = 1; j <= n; ++j)
nxt[i][j] = nxt[i - 1][nxt[i - 1][j]],
g[i][j] = g[i - 1][j] + g[i - 1][nxt[i - 1][j]];
}

void solve()
{
int pos, val;

while (q--)
{
read(pos);
read(val);

if (val <= c[pos])


{
printf("%d\n", pos);
continue;
}

val -= c[pos];

for (int i = LOG; i >= 0; --i)


{
if (nxt[i][pos] && val >= g[i][pos])
{
val -= g[i][pos];
pos = nxt[i][pos];
}
}

printf("%d\n", val ? nxt[0][pos] : pos);


}
}

int main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 184

init();
solve();
return 0;
}

Listing 7.1.6: Fountain-985016.cpp


// https://loj.ac/s/985016 1751 ms 29.6 M execution time : 5.938 s

// Problem: #3373. eJOI2020


// Contest: LibreOJ
// URL: https://loj.ac/problem/3373
// Memory Limit: 512 MB
// Time Limit: 1500 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <cmath>
#include <iostream>

#define INF 0x3f3f3f3f

using namespace std;

int d[100005], c[100005], maxd[100005][25];

int findmax(int l, int r)


{
int k = log2(r - l + 1);
return max(maxd[l][k], maxd[r - (1 << k) + 1][k]);
}

int f[100005][25], g[100005][25];

int main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);

int n, q;
cin >> n >> q;

for (int i = 1; i <= n; i++)


cin >> d[i] >> c[i];

d[++n] = INF;
c[n] = INF;

for (int i = 1; i <= n; i++)


maxd[i][0] = d[i];

for (int j = 1; (1 << j) <= n; j++)


for (int i = 1; i <= n; i++)
maxd[i][j] = max(maxd[i][j-1], maxd[min(n, i+(1 << (j-1)))][j-1]);

for (int i = 1; i < n; i++)


{
int l = i, r = n;

while (l <= r)
{
int mid = (l + r) >> 1;

if (findmax(i, mid) > d[i])


{
f[i][0] = mid;
r = mid - 1;
CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 185

}
else
l = mid + 1;
}

g[i][0] = c[f[i][0]];
}

for (int j = 1; j <= 20; j++)


for (int i = 1; i <= n; i++)
{
f[i][j] = f[f[i][j - 1]][j - 1];
g[i][j] = g[i][j - 1] + g[f[i][j - 1]][j - 1];
}

while (q--)
{
int r, v;
cin >> r >> v;
v -= c[r];

for (int i = 20; i >= 0; i--)


if (v >= g[r][i])
v -= g[r][i], r = f[r][i];

if (v > 0)
r = f[r][0];

if (r == n)
cout << 0 << endl;
else
cout << r << endl;
}

return 0;
}

Listing 7.1.7: Fountain-992243.cpp


// https://loj.ac/s/992243 507 ms 16.9 M execution time : 0.594 s

#include <cctype>
#include <cstdio>

#include <stdlib.h> /* exit, EXIT_FAILURE */

using namespace std;

inline int readint()


{
int x = 0;
bool f = 0;
char c = getchar();

while (!isdigit(c) && c != ’-’)


c = getchar();

if (c == ’-’)
{
f = 1;
c = getchar();
}

while (isdigit(c))
{
x = x * 10 + c - ’0’;
c = getchar();
}

return f ? -x : x;
}

const int maxn = 1e5 + 5;

int n, q, d[maxn], c[maxn];


CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 186

int st[maxn], top = 0;


int f[maxn][20], s[maxn][20];

int main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);

n = readint();
q = readint();

for (int i = 1; i <= n; i++)


{
d[i] = readint();
c[i] = readint();
}

st[0] = n;

for (int i = 0; i < 20; i++)


f[n][i] = n;

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


{
while (top && d[st[top]] <= d[i])
top--;

f[i][0] = st[top];
s[i][0] = c[i];

for (int j = 1; j < 20; j++)


{
f[i][j] = f[f[i][j - 1]][j - 1];
s[i][j] = s[i][j - 1] + s[f[i][j - 1]][j - 1];
}

st[++top] = i;
}

while (q--)
{
int r, v;
r = readint();
v = readint();

for (int i = 19; i >= 0; i--)


if (v > s[r][i])
{
v -= s[r][i];
r = f[r][i];
}

printf("%d\n", r < n ? r : 0);


}

return 0;
}

Listing 7.1.8: Fountain-992296.cpp


// https://loj.ac/s/992296 491 ms 16.9 M execution time : 0.578 s

#include <cstdio>
#include <algorithm>

using namespace std;

inline int R()


CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 187

{
char c;
int re;

while ((c = getchar()) > ’9’ || c < ’0’);

re = c - 48;

while ((c = getchar()) >= ’0’ && c <= ’9’)


re = re * 10 + c - 48;

return re;
}

const int maxn = 100007;


const int J = 19;

int D[maxn], V[maxn];


int N, Q;
int pre[maxn][J + 1], sum[maxn][J + 1];
int stk[maxn], top;

int find(int pos, int val)


{
for (int i = J; i >= 0; i--)
{
if (val > sum[pos][i])
{
val -= sum[pos][i];
pos = pre[pos][i];
}
}

return pos;
}

int main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);

N = R();
Q = R();

for (int i = 1; i <= N; i++)


D[i] = R(), V[i] = R();

stk[top] = N;

for (int i = 0; i <= J; i++)


pre[N][i] = N;

for (int i = N; i > 0; i--)


{
while (top && D[i] >= D[stk[top]])
--top;

pre[i][0] = stk[top];
sum[i][0] = V[i];

for (int j = 1; j <= J; j++)


pre[i][j] = pre[pre[i][j - 1]][j - 1],
sum[i][j] = sum[i][j - 1] + sum[pre[i][j - 1]][j - 1];

stk[++top] = i;
}

int pos, val, ans;


CAPITOLUL 7. EJOI 2020 7.1. FOUNTAIN 188

while (Q--)
{
pos = R();
val = R();
ans = find(pos, val);

if (ans < N)
printf("%d\n", ans);
else
puts("0");
}

return 0;
}

Listing 7.1.9: Fountain-994277.cpp


// https://loj.ac/s/994277 561 ms 8.8 M execution time : 1.102 s

#include <cstdio>

#include <stdlib.h> /* exit, EXIT_FAILURE */

using namespace std;


const int N = 1e5 + 5;
const int logN = 18;

int n, Q, D[N], C[N];


int fa[N][logN], sum[N];

void init()
{
static int stk[N], top;
top = 0;

for (int i = n; i; i--)


{
for (; top && D[stk[top]] <= D[i]; --top);

fa[i][0] = !top ? 0 : stk[top];


sum[i] = C[i] + sum[fa[i][0]];
stk[++top] = i;
}

for (int i = 1; i < logN; i++)


for (int j = 1; j <= n; j++)
fa[j][i] = fa[fa[j][i - 1]][i - 1];
}

signed main()
{
if (freopen("../Fountain-tests/input.3_09", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.3_09", "r", stdin);


std::freopen("fountain.out", "w", stdout);

scanf("%d%d", &n, &Q);

for (int i = 1; i <= n; i++)


scanf("%d%d", D + i, C + i);

init();

while (Q--)
{
int R, V;
scanf("%d%d", &R, &V);

if (sum[R] < V)
{
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 189

printf("0\n");
}
else
{
for (int i = logN - 1; ˜i; --i)
if (fa[R][i] && V > sum[R] - sum[fa[R][i]])
V -= sum[R] - sum[fa[R][i]],
R = fa[R][i];

if (V > C[R])
R = fa[R][0];

printf("%d\n", R);
}
}

return 0;
}

7.1.3 *Rezolvare detaliată

7.2 triangulation
Problema 2 - Triangulation 100 de puncte
Anna a desenat un poligon cu n vârfuri numerotate de la 0 la n  1 ı̂n ordinea acelor de
ceasornic. Mai târziu a triangulat poligonul desenând n  3 diagonale care nu se intersectează cu
excepţia posibilă a vârfurilor. O diagonala este o linie dreapta ı̂ntre două vârfuri diferite care nu
au o latura ı̂ntre ele.
Să definim mai ı̂ntâi distanţa de la vârful A la diagonala D. Să
zicem că ı̂ncepem la vârful A şi ne deplasăm la următorul vârf ı̂n
ordinea acelor de ceasornic pâna ajungem la unul dintre vârfurile
lui D.
Numărul de muchii traversate il numim left distance.
Analog, right distance este numărul de muchii traversate
dacă ı̂ncepem de la A şi ne deplasăm ı̂n direcţia opusă acelor
ceasului până ajungem la D. Distanţa de la A la D este maximul
dintre left distance şi right distance.
În exemplul din imagine distanţa de la vârful 0 la diagonala (1, 5) este 2 cu left distance
egal cu 1 şi right distance egal cu 2. Pentru diagonala (0, 5) distanţ de la vârful 0 este 5, cu
left distance=5 şi right distance=2.
Anna vrea sa facă din acest lucru o provocare pentru Jacob. Jacob nu ştie diagonalele desenate.
El ştie doar valoarea lui N , dar o poate ı̂ntreba pe Anna de mai multe ori desepre perechi de vârfuri
şi ea ı̂i va spune dacă se găseşte o muchie ı̂ntre vârfurile acelea. Scopul lui Jacob este să găsească
cea mai apropiata (cu noţiunea de distanţă de mai sus) diagonala desenata faţă de vârful 0. Îl
veţi ajuta să ı̂şi ı̂ndeplinească scopul ı̂ntrebând-o pe Anna un număr limitat de ı̂ntrebări.
Constraints

ˆ 5 & n & 100

Implementation Details
Trebuie să implementaţi următoarea funcţie ı̂n submisia voastră:

int solve(int n)

ˆ Funcţia se apelează exact odată de către evaluator.


ˆ n: numărul de vârfuri din poligon.
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 190

ˆ Funcţia ar trebui să returneze diagonala ı̂ntre nişte vârfuri a şi b ca un ı̂ntreg cu valoarea
a nb
ˆ Dacă sunt mai multe diagonale cu aceeaşi distanţă minimă, puteţi să returnaţi oricare.

Funcţia de mai sus poate apela următoarea funcţie:

int query(int x, int y)

ˆ x: indicele primului vârf

ˆ y: indicele celui de al doilea vârf


ˆ 0 & x, y &n
ˆ returnează 1 dacă există o diagonală ı̂ntre x şi y şi 0 altfel.

Sample interaction
Urmează un exemplu de input pentru evaluator şi apelurile de funcţii corespunzătoare. Inputul
este cel desenat in imaginea de mai sus.
Singura linie din input corespunde lui n.
Evaluatorul va afişa fiecare apel al lui query la stdout şi trebuie să ı̂i răspundeţi manual cu 1
sau 0.

Sample Input Sample Calls


to grader Calls Returns Calls Returns
7 solve(7)
query(0, 3)
query returns 0
query(0, 5)
query returns 1
query(1, 5)
query returns 1
solve returns 1 7  5 12
Correct!
Tabelul 7.1: Triangulation

Scoring
Fie q numărul de interogări ce le faceţi pe un singur test. Totodată fie w f racn n  32.

ˆ Dacă faci o interogare invalidă sau ghiciţi răspunsul incorect veţi primi 0% din puncte.
ˆ Dacă w $ q veţi primi 0% din puncte pentru test.
wq
ˆ Dacă n $ q & w veţi primi 10  60 wn
% din puncte pentru un test.
ˆ Dacă q & n veţi primi 100% din puncte pe test.

Subtasks
Va fi un singur subtask iar scorul vostru este suma scorurilor pe testele individuale. Dar, pe
parcursul concursului veţi putea vedea scorurile doar pe jumătate din teste (ce valorează 50 de
puncte). Celelalte scoruri se vor afişa după concurs. Scorul final va fi scorul maxim dintre
toate submisiile.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 512 MB
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 191

7.2.1 Indicaţii de rezolvare

The trivial solution, checking every diagonal was getting 10 points.


One way to improve it was to sort all the diagonals in ascending order of distance and query
them in that order until a drawn one is found. This solution would get around 55 points.
In order to get 100 points it was enough to ask which diagonals are drawn from vertex 0 (n  3
questions in total). After that one could determine the answer: it is either one of the confirmed
diagonals, or the one that connects the endpoints of two of the confirmed diagonals, those who
are the closest to the opposite of vertex 0 from both sides.

7.2.2 Coduri sursă

Listing 7.2.1: triangulation-981145.cpp


// https://loj.ac/s/981145 83 ms 260 K

// ----------------------------------------------------

//#include "triangulation.h"
#include <bits/stdc++.h>

using namespace std;

// ----------------------------------------------------

#define MAXN 105

int n, w, qryCnt, mndis;


char s[MAXN][MAXN];

inline void fadd(int &x, int y)


{
x += y;
if (x >= n) x -= n;
return ;
}

inline int getDis(int x, int y)


{
int ldis = 0, rdis = 0, now = 0;
do
{
++ldis;
fadd(now, 1);
} while (now != x && now != y);

now = 0;
do
{
++rdis;
fadd(now, n - 1);
} while (now != x && now != y);

return ldis > rdis ? ldis : rdis;


}

inline int checkValid(int x, int y)


{
if (!(0 <= x && x < n)) return 0;
if (!(0 <= y && y < n)) return 0;
return 1;
}

inline int query(int x, int y)


{
++qryCnt;
if (qryCnt > w) printf("Query limit exceed");
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 192

if(!checkValid(x, y)) return 0;

return (s[x][y] - ’0’);


}

// ----------------------------------------------------

#define pb push_back
#define eb emplace_back
#define mp make_pair
#define Fast_IO ios::sync_with_stdio(false);
#define DEBUG fprintf(stderr,"Running on Line %d in Function %s\n",\
__LINE__,__FUNCTION__)
//mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
#define fir first
#define sec second
#define mod 998244353
#define ll long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f

typedef pair<int, int> pii;

void print(vector<int> x)
{
for (int i = 0; i < (int)x.size(); i++)
printf("%d%c", x[i], " \n"[i == (int)x.size() - 1]);
}

int solve(int n)
{
if (query(1, n - 1))
return n + n - 1;

vector<int> v;
v.pb(1);

for (int i = 2; i <= n - 2; i++)


if (query(0, i))
v.pb(i);

v.pb(n - 1);
int ax = 0, ay = 0, mind = inf;

for (int i = 1; i < (int)v.size(); i++)


{
int D = max(v[i - 1], n - v[i]);

if (v[i - 1] + 1 != v[i] && D < mind)


ax = v[i - 1], ay = v[i], mind = D;
}

for (int i = 1; i < (int)v.size() - 1; i++)


{
int D = max(v[i], n - v[i]);

if (D < mind)
ax = 0, ay = v[i], mind = D;
}

return ax * n + ay;
}

// ----------------------------------------------------

int main()
{
//FILE* fin = fopen("../Triangulation-tests/input.0_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.1_0", "r");
//FILE* fin = fopen("../Triangulation-tests/input.2_1", "r");
//FILE* fin = fopen("../Triangulation-tests/input.3_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.4_0", "r");
FILE* fin = fopen("../Triangulation-tests/input.5_29", "r");

fscanf(fin, "%d", &n);


w = n * (n - 3) / 2;
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 193

for (int i = 0; i < n; i++)


fscanf(fin, "%s", s[i]); // char s[MAXN][MAXN];
fscanf(fin, "%d", &mndis);
fclose(fin);

printf("n = %d\n", n);


printf("w = %d\n", w);

int sol=solve(n);

printf("sol = %d\n", sol); // (a,b) --> a*n+b

int a=sol/n;
int b=sol%n;
printf("a = %d\n", a);
printf("b = %d\n", b);

int dist=getDis(a,b);

printf("dist = %d\n", dist);


printf("mndis = %d\n", mndis);
if(mndis != dist)
{
printf("Wrong answer");
return 0;
}

printf("qryCnt = %d\n", qryCnt);


if (qryCnt > n)
{
printf("score = %lf\n", (float)(10.0 + 60.0 * (w-qryCnt) / (w-n)));
}
else
{
printf("score = 100\n");
}

return 0;
}

Listing 7.2.2: triangulation-981153.cpp


// https://loj.ac/s/981153 98 ms 260 K

// Author -- xyr2005

// ----------------------------------------------------

#include <bits/stdc++.h>
//#include "triangulation.h"

// ----------------------------------------------------

#define MAXN 105

int n, w, qryCnt, mndis;


char s[MAXN][MAXN];

inline int checkValid(int x, int y)


{
if (!(0 <= x && x < n)) return 0;
if (!(0 <= y && y < n)) return 0;
return 1;
}

inline int query(int x, int y)


{
++qryCnt;
if (qryCnt > w) printf("Query limit exceed");

if(!checkValid(x, y)) return 0;

return (s[x][y] - ’0’);


}
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 194

// ----------------------------------------------------

#define lowbit(x) ((x)&(-(x)))


#define DEBUG fprintf(stderr,"Running on Line %d in Function %s\n",\
__LINE__,__FUNCTION__)
#define SZ(x) ((int)x.size())
#define mkpr std::make_pair
#define pb push_back

typedef long long ll;


typedef unsigned int uint;
typedef unsigned long long ull;
typedef std::pair<int, int> pi;
typedef std::pair<ll, ll> pl;
using std::min;
using std::max;

const int inf = 0x3f3f3f3f, Inf = 0x7fffffff;


const ll INF = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(std::chrono::steady_clock::now().time_since_epoch().count());

template <typename _Tp>_Tp gcd(const _Tp &a, const _Tp &b)


{
return (!b) ? a : gcd(b, a % b);
}

template <typename _Tp>inline _Tp abs(const _Tp &a)


{
return a >= 0 ? a : -a;
}

template <typename _Tp>inline void chmax(_Tp &a, const _Tp &b)


{
(a < b) &&(a = b);
}

template <typename _Tp>inline void chmin(_Tp &a, const _Tp &b)


{
(b < a) &&(a = b);
}

template <typename _Tp>inline void read(_Tp &x)


{
char ch(getchar());
bool f(false);

while (!isdigit(ch))
f |= ch == 45, ch = getchar();

x = ch & 15, ch = getchar();

while (isdigit(ch))
x = (((x << 2) + x) << 1) + (ch & 15), ch = getchar();

f &&(x = -x);
}

template <typename _Tp, typename... Args>


inline void read(_Tp &t, Args &...args)
{
read(t);
read(args...);
}

inline int read_str(char *s)


{
char ch(getchar());

while (ch == ’ ’ || ch == ’\r’ || ch == ’\n’)


ch = getchar();

char *tar = s;
*tar = ch, ch = getchar();
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 195

while (ch != ’ ’ && ch != ’\r’ && ch != ’\n’ && ch != EOF)


*(++tar) = ch, ch = getchar();

return tar - s + 1;
}

int _;
inline int getdis(int x, int y)
{
return max(x == 0 ? y : x, _ - y);
}

int solve(int n)
{
::_ = n;
int ans = inf, res = 0;
int last = 1;

for (int i = 2; i + 1 < n; ++i)


{
int tag = query(0, i);

if (tag)
{
if (getdis(0, i) < ans)
{
ans = getdis(0, i);
res = 0 * n + i;
}

if (last + 1 != i && getdis(last, i) < ans)


{
ans = getdis(last, i);
res = last * n + i;
}

last = i;
}
}

int i = n - 1;

if (last + 1 != i && getdis(last, i) < ans)


{
ans = getdis(last, i);
res = last * n + i;
}

return res;
}

// ----------------------------------------------------

int main()
{
//FILE* fin = fopen("../Triangulation-tests/input.0_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.1_0", "r");
//FILE* fin = fopen("../Triangulation-tests/input.2_1", "r");
//FILE* fin = fopen("../Triangulation-tests/input.3_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.4_0", "r");
FILE* fin = fopen("../Triangulation-tests/input.5_29", "r");

fscanf(fin, "%d", &n);


w = n * (n - 3) / 2;

for (int i = 0; i < n; i++)


fscanf(fin, "%s", s[i]); // char s[MAXN][MAXN];
fscanf(fin, "%d", &mndis);
fclose(fin);

printf("n = %d\n", n);


printf("w = %d\n", w);

int sol=solve(n);

printf("sol = %d\n", sol); // (a,b) --> a*n+b


CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 196

int a=sol/n;
int b=sol%n;
printf("a = %d\n", a);
printf("b = %d\n", b);

//int dist=getDis(a,b);
int dist=getdis(a,b);

printf("dist = %d\n", dist);


printf("mndis = %d\n", mndis);
if(mndis != dist)
{
printf("Wrong answer");
return 0;
}

printf("qryCnt = %d\n", qryCnt);


if (qryCnt > n)
{
printf("score = %lf\n", (float)(10.0 + 60.0 * (w-qryCnt) / (w-n)));
}
else
{
printf("score = 100\n");
}

return 0;
}

Listing 7.2.3: triangulation-981926.cpp


// https://loj.ac/s/981926 73 ms 168 K

#include <utility>
#include <vector>

#include <cstdio>
#include <cstdlib>

using namespace std;

#define MAXN 105

int n, w, qryCnt, mndis;


char s[MAXN][MAXN];

inline void fadd(int &x, int y)


{
x += y;
if (x >= n) x -= n;
return ;
}

inline int getDis(int x, int y)


{
int ldis = 0, rdis = 0, now = 0;
do
{
++ldis;
fadd(now, 1);
} while (now != x && now != y);

now = 0;
do
{
++rdis;
fadd(now, n - 1);
} while (now != x && now != y);

return ldis > rdis ? ldis : rdis;


}

inline int checkValid(int x, int y)


CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 197

{
if (!(0 <= x && x < n)) return 0;
if (!(0 <= y && y < n)) return 0;
return 1;
}

inline int query(int x, int y)


{
++qryCnt;
if (qryCnt > w) printf("Query limit exceed");

if(!checkValid(x, y)) return 0;

return (s[x][y] - ’0’);


}

// ----------------------------------------------------

int solve(int n)
{
vector<int> rec;
rec.push_back(1);

for (int i = 2; i < n - 1; i++)


if (query(0, i))
rec.push_back(i);

rec.push_back(n - 1);

pair<int, int> ans = make_pair(1e9, 0);

for (int i = 1; i < (int)rec.size(); i++)


{
int a = rec[i - 1], b = rec[i];

if (a + 1 == b)
continue;

ans = min(ans, make_pair(max(rec[i-1], n-rec[i]), a*n+b));


}

for (int i = 1; i < (int)rec.size() - 1; i++)


ans = min(ans, make_pair(max(rec[i], n-rec[i]), rec[i]));

return ans.second;
}

// ----------------------------------------------------

int main()
{
//FILE* fin = fopen("../Triangulation-tests/input.0_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.1_0", "r");
//FILE* fin = fopen("../Triangulation-tests/input.2_1", "r");
//FILE* fin = fopen("../Triangulation-tests/input.3_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.4_0", "r");
FILE* fin = fopen("../Triangulation-tests/input.5_29", "r");

fscanf(fin, "%d", &n);


w = n * (n - 3) / 2;

for (int i = 0; i < n; i++)


fscanf(fin, "%s", s[i]); // char s[MAXN][MAXN];
fscanf(fin, "%d", &mndis);
fclose(fin);

printf("n = %d\n", n);


printf("w = %d\n", w);

int sol=solve(n);

printf("sol = %d\n", sol); // (a,b) --> a*n+b

int a=sol/n;
int b=sol%n;
printf("a = %d\n", a);
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 198

printf("b = %d\n", b);

int dist=getDis(a,b);

printf("dist = %d\n", dist);


printf("mndis = %d\n", mndis);
if(mndis != dist)
{
printf("Wrong answer");
return 0;
}

printf("qryCnt = %d\n", qryCnt);


if (qryCnt > n)
{
printf("score = %lf\n", (float)(10.0 + 60.0 * (w-qryCnt) / (w-n)));
}
else
{
printf("score = 100\n");
}

return 0;
}

Listing 7.2.4: triangulation-983329.cpp


// https://loj.ac/s/983329 93 ms 260 K

// ----------------------------------------------------

#include <bits/stdc++.h>
//#include "triangulation.h"

using namespace std;

// ----------------------------------------------------

#define MAXN 105

int n, w, qryCnt, mndis;


char s[MAXN][MAXN];

inline int checkValid(int x, int y)


{
if (!(0 <= x && x < n)) return 0;
if (!(0 <= y && y < n)) return 0;
return 1;
}

inline int query(int x, int y)


{
++qryCnt;
if (qryCnt > w) printf("Query limit exceed");

if(!checkValid(x, y)) return 0;

return (s[x][y] - ’0’);


}

inline void fadd(int &x, int y)


{
x += y;
if (x >= n) x -= n;
return ;
}

inline int getDis(int x, int y)


{
int ldis = 0, rdis = 0, now = 0;
do
{
++ldis;
fadd(now, 1);
} while (now != x && now != y);
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 199

now = 0;
do
{
++rdis;
fadd(now, n - 1);
} while (now != x && now != y);

return ldis > rdis ? ldis : rdis;


}

// ----------------------------------------------------

int sta[105];
int Min = 200, ans = 0;
int query(int a, int b);

void check(int u, int v, int n)


{
if (u > v)
swap(u, v);

int tmp = (u == 0 ? max(v, n - v) : max(u, n - v));

if (Min > tmp)


{
Min = tmp, ans = u * n + v;
}
}

int solve(int n)
{
int top = 0;
sta[++top] = 1;

for (int i = 2; i <= n - 2; ++i)


{
if (query(0, i))
{
sta[++top] = i;
check(0, i, n);
}
}

sta[++top] = n - 1;

for (int i = 1; i < top; ++i)


{
if (sta[i] + 1 == sta[i + 1])
continue;

check(sta[i], sta[i + 1], n);


}

return ans;
}

// ----------------------------------------------------

int main()
{
//FILE* fin = fopen("../Triangulation-tests/input.0_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.1_0", "r");
//FILE* fin = fopen("../Triangulation-tests/input.2_1", "r");
//FILE* fin = fopen("../Triangulation-tests/input.3_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.4_0", "r");
FILE* fin = fopen("../Triangulation-tests/input.5_29", "r");

fscanf(fin, "%d", &n);


w = n * (n - 3) / 2;

for (int i = 0; i < n; i++)


fscanf(fin, "%s", s[i]); // char s[MAXN][MAXN];
fscanf(fin, "%d", &mndis);
fclose(fin);
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 200

printf("n = %d\n", n);


printf("w = %d\n", w);

int sol=solve(n);

printf("sol = %d\n", sol); // (a,b) --> a*n+b

int a=sol/n;
int b=sol%n;
printf("a = %d\n", a);
printf("b = %d\n", b);

int dist=getDis(a,b);

printf("dist = %d\n", dist);


printf("mndis = %d\n", mndis);
if(mndis != dist)
{
printf("Wrong answer");
return 0;
}

printf("qryCnt = %d\n", qryCnt);


if (qryCnt > n)
{
printf("score = %lf\n", (float)(10.0 + 60.0 * (w-qryCnt) / (w-n)));
}
else
{
printf("score = 100\n");
}

return 0;
}

Listing 7.2.5: triangulation-985775.cpp


// https://loj.ac/s/985775 74 ms 196 K

// ----------------------------------------------------

//#include "triangulation.h"
#include <algorithm>

using namespace std;

// ----------------------------------------------------

#include <bits/stdc++.h>

#define MAXN 105

int n, w, qryCnt, mndis;


char s[MAXN][MAXN];

inline void fadd(int &x, int y)


{
x += y;
if (x >= n) x -= n;
return ;
}

inline int getDis(int x, int y)


{
int ldis = 0, rdis = 0, now = 0;
do
{
++ldis;
fadd(now, 1);
} while (now != x && now != y);

now = 0;
do
{
++rdis;
CAPITOLUL 7. EJOI 2020 7.2. TRIANGULATION 201

fadd(now, n - 1);
} while (now != x && now != y);

return ldis > rdis ? ldis : rdis;


}

inline int checkValid(int x, int y)


{
if (!(0 <= x && x < n)) return 0;
if (!(0 <= y && y < n)) return 0;
return 1;
}

inline int query(int x, int y)


{
++qryCnt;
if (qryCnt > w) printf("Query limit exceed");

if(!checkValid(x, y)) return 0;

return (s[x][y] - ’0’);


}

// ----------------------------------------------------

int solve(int n)
{
int lst = 1, ans1 = -1, ans2 = -1;
auto val = [&n](int a, int b)
{
return max(a ? a : b, n - b);
};

for (int i = 2; i < n - 1; i++)


if (query(0, i))
{
if (ans1 < 0 || val(0, i) < val(ans1, ans2))
{
ans1 = 0;
ans2 = i;
}

if (lst < i - 1 && (ans1 < 0 || val(lst, i) < val(ans1, ans2)))


{
ans1 = lst;
ans2 = i;
}

lst = i;
}

if (lst < n - 2 && (ans1 < 0 || val(lst, n - 1) < val(ans1, ans2)))


{
ans1 = lst;
ans2 = n - 1;
}

return ans1 * n + ans2;


}

// ----------------------------------------------------

int main()
{
//FILE* fin = fopen("../Triangulation-tests/input.0_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.1_0", "r");
//FILE* fin = fopen("../Triangulation-tests/input.2_1", "r");
//FILE* fin = fopen("../Triangulation-tests/input.3_2", "r");
//FILE* fin = fopen("../Triangulation-tests/input.4_0", "r");
FILE* fin = fopen("../Triangulation-tests/input.5_29", "r");

fscanf(fin, "%d", &n);


w = n * (n - 3) / 2;

for (int i = 0; i < n; i++)


fscanf(fin, "%s", s[i]); // char s[MAXN][MAXN];
CAPITOLUL 7. EJOI 2020 7.3. EXAM 202

fscanf(fin, "%d", &mndis);


fclose(fin);

printf("n = %d\n", n);


printf("w = %d\n", w);

int sol=solve(n);

printf("sol = %d\n", sol); // (a,b) --> a*n+b

int a=sol/n;
int b=sol%n;
printf("a = %d\n", a);
printf("b = %d\n", b);

int dist=getDis(a,b);

printf("dist = %d\n", dist);


printf("mndis = %d\n", mndis);
if(mndis != dist)
{
printf("Wrong answer");
return 0;
}

printf("qryCnt = %d\n", qryCnt);


if (qryCnt > n)
{
printf("score = %lf\n", (float)(10.0 + 60.0 * (w-qryCnt) / (w-n)));
}
else
{
printf("score = 100\n");
}

return 0;
}

7.2.3 *Rezolvare detaliată

7.3 exam
Problema 3 - Exam 100 de puncte
N elevi sunt aliniaţi, dând un examen. Sunt numerotaţi de la stânga la dreapta, cu numere
ı̂ntregi ı̂ncepând de la 1. E bine de ştiut cât de bine lucrează fiecare elev: al i-lea elev va face
exact Ai puncte.
Din când ı̂n când proctorul iese din sală pentru a lua o pauză, şi atunci elevii pot trişa: doi
sau mai mulţi elevi consecutivi se pot aduna şi să copieze lucrarea cea mai buna a unora dintre
ei. Drept urmare, scorurile lor devin egale cu scorul maxim din intervalul acela. Trişatul se poate
ı̂ntâmpla un număr oarecare de ori (inclusiv 0).
Pentru a trece examenul elevul al i-lea trebuie sa facă exact Bi puncte. Determinaţi numărul
maxim de elevi care pot să treacă examenul.
Input
Primul rând conţine numărul ı̂ntreg N .
Al doilea rând conţine N numere ı̂ntregi: A1 , A2 , ..., AN .
Al treilea rând conţine N numere ı̂ntregi: B1 , B2 , ..., BN .
Output
Afişaţi un singur număr ı̂ntreg: numărul maxim de elevi.
Constraints

ˆ 2&N
CAPITOLUL 7. EJOI 2020 7.3. EXAM 203

ˆ 1 & Ai & 109


ˆ 1 & Bi & 109

Subtasks

1 (14 puncte): N & 10


2 (12 puncte): N & 105 , Toate elementele lui B sunt egale (B1 B2 ... BN )
3 (13 puncte): N & 5000, A este strict crescător (A1 $ A2 $ ... $ AN )
4 (23 puncte): N & 105 , Elementele lui A sunt distincte.
5 (16 puncte): N & 200
6 (22 puncte): N & 5000

Example
Input Output
3 2
123
222
4 3
10 1 9 1
10 9 10 9

În primul exemplu primii doi elevi pot trişa dupa care scorurile devin 2, 2, 3 şi amândoi trec
examenul.
În al doilea exemplu elevii 2 şi 3 pot să treacă examenul, dar nu simultan.
Observaţi că acest test nu poate fi prezent ı̂n subtaskurile 2, 3 sau 4.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 512 MB

7.3.1 Indicaţii de rezolvare

To achieve a full score one could solve subtasks 2, 4 and 6 separately.


Subtask 2:
Go over the consecutive intervals of A with works that aren’t better than B1 and accumulate
the lengths of those intervals that contain at least one work equal to B1 .
Since only one pass is needed it works in O N .
To solve other two subtasks we will need the following observations:

ˆ The works in the final state will appear in the same relative order as they were originally
ˆ student X may end up with student’s Y ’s work if and only if in the interval between X and
Y (inclusive) there were no works that were better than X’s work

Any state that satisfies above requirements is reachable.


For each student X we can find the interval containing X and extending on both sides as much
as possible that doesn’t contain works better than X’s. This can be done in multiple ways:
2
ˆ Simple seek in each direction until we find better work, in O N  which is good enough for
most subtasks
ˆ Using data structures (RMQ / Segment tree), in O N log N 
CAPITOLUL 7. EJOI 2020 7.3. EXAM 204

ˆ Single pass in each direction maintaining sorted stack, in O N , similar to the one described
in the task Fountain

Using these precomputed intervals we can answer whether student Y can end up with student
X’s work in O 1 and doing these checks at appropriate times will be needed in both subtasks.
Subtask 4:
We have to find the longest subsequence in B such that corresponding values in A are in
non-decreasing order. This can be done by solving a well known Longest Increasing Subsequence
(LIS) problem with O N log N  solution.
Subtask 6:
2
We have to match the most values from B to A in the same order, which can be done in O N 
using the standard 2D dynamic programming similar to the one that solves Longest Common
Subsequence (LCS) problem.

7.3.2 Coduri sursă

Listing 7.3.1: exam-981110.cpp


// https://loj.ac/s/981110 832 ms 6.6 M

#include <set>
#include <cstdio>
#include <algorithm>

std::set<int>set;

struct point
{
int v, ord;
} num[200001];

bool cmp(point a, point b)


{
return a.v > b.v || (a.v == b.v && a.ord > b.ord);
}

int n, a[100001], b[100001], L[100001], R[100001], c[100001];

void update(int ind, int num)


{
for (; ind <= n; ind += ind & -ind)
c[ind] = std::max(c[ind], num);
}

int query(int ind)


{
int tot = 0;

for (; ind; ind -= ind & -ind)


tot = std::max(tot, c[ind]);

return tot;
}

int main()
{
if (freopen("../Exam-tests/input.4_e", "r", stdin) == NULL) // 1206
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.4_e", "r", stdin);


//std::freopen("exam.out", "w", stdout);
CAPITOLUL 7. EJOI 2020 7.3. EXAM 205

scanf("%d", &n);

for (int i = 1; i <= n; i++)


scanf("%d", a + i), num[i] = (point) { a[i], i };

for (int i = 1; i <= n; i++)


scanf("%d", b + i), num[i + n] = (point) { b[i], -i };

std::sort(num + 1, num + (n << 1) + 1, cmp);

set.insert(0);

set.insert(n + 1);

for (int i = 1; i <= n << 1; i++)


if (num[i].ord > 0)
{
set.insert(num[i].ord);
// printf("insert %d\n",num[i].ord);
}
else
{
// printf("query %d\n",-num[i].ord);
int x = *set.lower_bound(-num[i].ord);

if (a[x] == num[i].v)
R[-num[i].ord] = x;

x = *--set.upper_bound(-num[i].ord);

if (a[x] == num[i].v)
L[-num[i].ord] = x;
}

for (int i = 1; i <= n; i++)


{
// printf("%d %d\n",L[i],R[i]);
int x1 = query(L[i]), x2 = query(R[i]);

if (L[i])
update(L[i], x1 + 1);

if (R[i])
update(R[i], x2 + 1);
}

printf("%d\n", query(n));
}

OBS1: Pentru x = *--set.upper_bound(-num[i].ord); http://www.cplusplu


s.com/forum/beginner/118449/
OBS2: Pentru C++ Operator Precedence https://en.cppreference.com/w/cp
p/language/operator_precedence

Listing 7.3.2: exam-982347.cpp


// https://loj.ac/s/982347 777 ms 6.6 M

#include <algorithm>
#include <cstdio>
#include <set>
#include <vector>

using namespace std;


const int N = 1e5 + 5;

int n;
int A[N], B[N];
int L[N], R[N];

int tr[N];

inline void upd(int p, int v)


{
for (; p <= n; p += p & -p)
CAPITOLUL 7. EJOI 2020 7.3. EXAM 206

tr[p] = max(tr[p], v);


}

inline int get(int p)


{
int v = 0;

for (; p; p -= p & -p)


v = max(tr[p], v);

return v;
}

signed main()
{
if (freopen("../Exam-tests/input.4_e", "r", stdin) == NULL) // 1206
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.4_e", "r", stdin);


//std::freopen("exam.out", "w", stdout);

scanf("%d", &n);

for (int i = 1; i <= n; i++)


scanf("%d", A + i);

for (int i = 1; i <= n; i++)


scanf("%d", B + i);

vector<pair<int, int>> tmp(n * 2);


set<int> rec({0, n + 1});

for (int i = 1; i <= n; i++)


tmp[i - 1] = {A[i], i};

for (int i = 1; i <= n; i++)


tmp[i + n - 1] = {B[i], -i};

sort(tmp.begin(), tmp.end(), greater<pair<int, int>>());

for (auto it : tmp)


{
if (it.second < 0)
{
int l = *rec.lower_bound(-it.second);

if (A[l] == it.first)
R[-it.second] = l;

int r = *--rec.upper_bound(-it.second);

if (A[r] == it.first)
L[-it.second] = r;
}
else
rec.insert(it.second);
}

for (int i = 1; i <= n; i++)


{
int l = get(L[i]), r = get(R[i]);

if (L[i])
upd(L[i], l + 1);

if (R[i])
upd(R[i], r + 1);
}

printf("%d\n", get(n));

return 0;
CAPITOLUL 7. EJOI 2020 7.3. EXAM 207

Listing 7.3.3: exam-984751.cpp


// https://loj.ac/s/9847511034 ms 11.4 M

#include <bits/stdc++.h>

using namespace std;

const int maxn = 201001;


int a[maxn], n, b[maxn];

struct ddata
{
int val, ty, pos;

ddata(int x = 0, int y = 0, int z = 0)


{
val = x, ty = y, pos = z;
}

friend bool operator<(const ddata &a, const ddata &b)


{
return a.val == b.val ?
(a.ty == b.ty ? a.pos < b.pos : a.ty < b.ty) :
a.val > b.val;
}

} p[maxn << 1];

int lp[maxn], rp[maxn];

struct SGT
{
int tr[maxn << 2];

void upd(int i, int l, int r, int x, int k)


{
if (l == r)
return tr[i] = k, void();

//int mid = l + r >> 1;


int mid = (l + r) >> 1;

if (x <= mid)
upd(i << 1, l, mid, x, k);
else
upd(i << 1 | 1, mid + 1, r, x, k);

tr[i] = max(tr[i << 1], tr[i << 1 | 1]);


}

int qry(int i, int l, int r, int x, int y)


{
if (y < l || r < x)
return 0;

if (x <= l && r <= y)


return tr[i];

//int mid = l + r >> 1, ans = 0;


int mid = (l + r) >> 1, ans = 0;

if (x <= mid)
ans = max(ans, qry(i << 1, l, mid, x, y));

if (y > mid)
ans = max(ans, qry(i << 1 | 1, mid + 1, r, x, y));

return ans;
}
} sgt[2];
CAPITOLUL 7. EJOI 2020 7.3. EXAM 208

int main()
{
if (freopen("../Exam-tests/input.4_e", "r", stdin) == NULL) // 1206
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.4_e", "r", stdin);


//std::freopen("exam.out", "w", stdout);

cin >> n;

for (int i = 1; i <= n; i++)


{
scanf("%d", &a[i]);
p[i] = ddata(a[i], 0, i);
}

for (int i = 1; i <= n; i++)


{
scanf("%d", &b[i]);
p[i + n] = ddata(b[i], 1, i);
}

sort(p + 1, p + (n << 1) + 1);


set<int>cur;
cur.insert(0), cur.insert(n + 1);
a[0] = a[n + 1] = 2e9;

for (int i = 1; i <= n * 2; i++)


{
if (p[i].ty == 0)
cur.insert(p[i].pos);
else
{
int lpos = *--cur.upper_bound(p[i].pos);
int rpos = *cur.lower_bound(p[i].pos);

if (p[i].val == a[lpos])
lp[p[i].pos] = lpos;

if (p[i].val == a[rpos])
rp[p[i].pos] = rpos;
}
}

for (int i = 1; i <= n; i++)


{
int Ln = max(sgt[0].qry(1, 1, n, 1, lp[i]),
sgt[1].qry(1, 1, n, 1, lp[i]));
int Rn = max(sgt[0].qry(1, 1, n, 1, rp[i]),
sgt[1].qry(1, 1, n, 1, rp[i]));

if (lp[i])
sgt[0].upd(1, 1, n, lp[i], Ln + 1);

if (rp[i])
sgt[1].upd(1, 1, n, rp[i], Rn + 1);
}

cout << max(sgt[0].qry(1, 1, n, 1, n), sgt[1].qry(1, 1, n, 1, n));

return 0;
}

Listing 7.3.4: exam-985120.cpp


// https://loj.ac/s/985120 636 ms 6.7 M

#include <bits/stdc++.h>

using namespace std;


CAPITOLUL 7. EJOI 2020 7.3. EXAM 209

const int buffer_size = 1e5 + 5;


char buf[buffer_size], *S, *T;
bool flag_EOF;

inline char read_char()


{
if (S == T)
T = (S = buf) + fread(buf, 1, buffer_size, stdin);

return S != T ? *(S++) : EOF;


}

inline int read_int()


{
int flag = 1;
char c = read_char();

while (c < ’0’ || c > ’9’)


{
if (c == EOF)
{
flag_EOF = true;
return 0;
}

flag = (c == ’-’ ? -1 : flag);


c = read_char();
}

int x = 0;

while (c >= ’0’ && c <= ’9’)


{
x = x * 10 + (c ˆ 48);
c = read_char();
}

return x * flag;
}

inline void Write(int x)


{
if (!x)
{
putchar(’0’);
return;
}

if (x < 0)
{
putchar(’-’);
x = -x;
}

static char st[13];


int head = 0;

while (x)
{
st[++head] = x % 10 + ’0’;
x /= 10;
}

while (head > 0)


putchar(st[head--]);
}

inline int _max(int a, int b)


{
return a < b ? b : a;
}

int n;
const int max_n = 1e5 + 5;
int a[max_n], b[max_n];
typedef pair<int, int> P;
CAPITOLUL 7. EJOI 2020 7.3. EXAM 210

P tmp[max_n << 1];


int c[max_n];

inline void modify(int k, int v)


{
if (k > 0)
{
for (int i = k; i <= n; i += i & (-i))
c[i] = _max(c[i], v);
}
}

inline int query(int k)


{
int res = 0;

for (int i = k; i > 0; i -= i & (-i))


res = _max(res, c[i]);

return res;
}

set<int> id;
set<int>::iterator it;
int L[max_n], R[max_n];

int main()
{
if (freopen("../Exam-tests/input.4_e", "r", stdin) == NULL) // 1206
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.4_e", "r", stdin);


//std::freopen("exam.out", "w", stdout);

n = read_int();

for (int i = 1; i <= n; ++i)


{
tmp[i].first = a[i] = read_int();
tmp[i].second = i;
}

for (int i = 1; i <= n; ++i)


{
tmp[i + n].first = b[i] = read_int();
tmp[i + n].second = -i;
}

sort(tmp + 1, tmp + (n << 1) + 1, greater<P>());

for (int i = 1; i <= (n << 1); ++i)


{
if (tmp[i].second < 0)
{
int x = -tmp[i].second;
it = id.lower_bound(x);

if (it != id.end() && a[ *it ] == tmp[i].first)


R[x] = *it;

it = id.upper_bound(x);

if (it != id.begin() && a[ *(--it) ] == tmp[i].first)


L[x] = *it;
}
else
id.insert(tmp[i].second);
}

for (int i = 1; i <= n; ++i)


{
int l = query(L[i]), r = query(R[i]);
CAPITOLUL 7. EJOI 2020 7.3. EXAM 211

if (L[i])
modify(L[i], l + 1);

if (R[i])
modify(R[i], r + 1);
}

printf("%d", query(n));

return 0;
}

Listing 7.3.5: exam-986828.cpp


// https://loj.ac/s/986828 652 ms 6.7 M

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
#define setIO(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
#define closefile fclose(stdin),fclose(stdout)
#define see(x) cerr<<x<<" "
#define seeln(x) cerr<<x<<endl
#define out(x) cerr<<#x<<" = "<<x<<" "
#define outln(x) cerr<<#x<<" = "<<x<<endl
#define outarr(x,l,r) cerr<<#x"["<<l<<"-"<<r<<"] = "; \
for (int _i=l;_i<=r;++_i) cerr<<x[_i]<<" "; cerr<<endl;
#define m_p make_pair
#define sz(x) (int)x.size()

using namespace std;

typedef long long ll;


typedef pair<int, int> pii;
typedef unsigned long long ull;

template <class T> void read(T &x)


{
x = 0;
char c = getchar();
int flag = 0;

while (c < ’0’ || c > ’9’)


flag |= (c == ’-’), c = getchar();

while (c >= ’0’ && c <= ’9’)


x = (x << 3) + (x << 1) + (c ˆ 48), c = getchar();

if (flag)
x = -x;
}

template <class T> T _max(T a, T b)


{
return b > a ? b : a;
}

template <class T> T _min(T a, T b)


{
return b < a ? b : a;
}

template <class T> bool checkmax(T &a, T b)


{
CAPITOLUL 7. EJOI 2020 7.3. EXAM 212

return b > a ? a = b, 1 : 0;
}

template <class T> bool checkmin(T &a, T b)


{
return b < a ? a = b, 1 : 0;
}

const int N = 200005, inf = 0x3f3f3f3f;


int n, a[N], b[N];
pii sta[N];
int top = 0;

set<int> S;

int l[N], r[N];


int f[N], g[N];
int C[N];

void update(int x, int num)


{
for (; x <= n; x += (x & (-x)))
checkmax(C[x], num);
}

int query(int x)
{
int ans = 0;

for (; x; x -= (x & (-x)))


checkmax(ans, C[x]);

return ans;
}

void init()
{
read(n);

for (int i = 1; i <= n; ++i)


read(a[i]);

for (int i = 1; i <= n; ++i)


read(b[i]);

for (int i = 1; i <= n; ++i)


sta[++top] = m_p(a[i], i), sta[++top] = m_p(b[i], -i);

sort(sta + 1, sta + top + 1);


reverse(sta + 1, sta + top + 1);

S.insert(-inf);
S.insert(inf);

for (int i = 1; i <= top; ++i)


{
int x = sta[i].second;

if (x > 0)
S.insert(x);
else
{
x = -x;
l[x] = *(--S.upper_bound(x));
r[x] = *S.lower_bound(x);

if (l[x] != -inf && a[l[x]] != b[x])


l[x] = -inf;

if (r[x] != inf && a[r[x]] != b[x])


r[x] = inf;

//out(x); out(l[x]); outln(r[x]);


}
}
}
CAPITOLUL 7. EJOI 2020 7.3. EXAM 213

void solve()
{
for (int i = 1; i <= n; ++i)
{
int L = 0, R = 0;

if (l[i] != -inf)
L = query(l[i]);

if (r[i] != inf)
R = query(r[i]);

if (r[i] != inf)
update(r[i], R + 1);

if (l[i] != -inf)
update(l[i], L + 1);
}

printf("%d\n", query(n));
}

int main()
{
if (freopen("../Exam-tests/input.4_e", "r", stdin) == NULL) // 1206
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.4_e", "r", stdin);


//std::freopen("exam.out", "w", stdout);

init();
solve();
return 0;
}

Listing 7.3.6: exam-992381.cpp


// https://loj.ac/s/992381 1009 ms 6.1 M

#include <algorithm>
#include <set>
#include <cctype>
#include <cstdio>

using namespace std;

inline int readint()


{
int x = 0;
bool f = 0;
char c = getchar();

while (!isdigit(c) && c != ’-’)


c = getchar();

if (c == ’-’)
{
f = 1;
c = getchar();
}

while (isdigit(c))
{
x = x * 10 + c - ’0’;
c = getchar();
}

return f ? -x : x;
}
CAPITOLUL 7. EJOI 2020 7.3. EXAM 214

const int maxn = 1e5 + 5;


int n, a[maxn], b[maxn];

bool cmp(int x, int y)


{
int vx = x > 0 ? a[x] : b[-x], vy = y > 0 ? a[y] : b[-y];

if (vx == vy)
return x > y;

return vx > vy;


}

int ord[maxn * 2];


int pre[maxn], suc[maxn];
int c[maxn];

inline int lowbit(int x)


{
return x & -x;
}

void modify(int x, int k)


{
while (x <= n)
{
c[x] = max(c[x], k);
x += lowbit(x);
}
}

int query(int x)
{
int s = 0;

while (x)
{
s = max(s, c[x]);
x -= lowbit(x);
}

return s;
}

int main()
{
if (freopen("../Exam-tests/input.4_e", "r", stdin) == NULL) // 1206
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../Fountain-tests/input.4_e", "r", stdin);


//std::freopen("exam.out", "w", stdout);

n = readint();

for (int i = 1; i <= n; i++)


a[i] = readint();

for (int i = 1; i <= n; i++)


b[i] = readint();

for (int i = 1; i <= n; i++)


ord[i] = i;

for (int i = 1; i <= n; i++)


ord[i + n] = -i;

sort(ord + 1, ord + n * 2 + 1, cmp);


set<int> s;

s.insert(0);
s.insert(n + 1);
CAPITOLUL 7. EJOI 2020 7.4. XORSORT 215

for (int i = 1; i <= n * 2; i++)


if (ord[i] > 0)
s.insert(ord[i]);
else
{
int x = -ord[i];
pre[x] = *--s.upper_bound(x);

if (a[pre[x]] != b[x])
pre[x] = 0;

suc[x] = *s.lower_bound(x);

if (a[suc[x]] != b[x])
suc[x] = n + 1;
}

for (int i = 1; i <= n; i++)


{
if (suc[i] <= n)
modify(suc[i], query(suc[i]) + 1);

if (pre[i] > 0 && pre[i] < i)


modify(pre[i], query(pre[i]) + 1);
}

printf("%d\n", query(n));

return 0;
}

7.3.3 *Rezolvare detaliată

7.4 xorsort
Problema 4 - XOR Sort 100 de puncte
Vi se dă un număr ı̂ntreg S şi un şir A de N numere ı̂ntregi mai mari sau egale cu 0, indexat
de la 1. Aveţi voie să aplicaţi următoarea operaţie pe el: alegeţi un indice i (1 & i & N ), alegeţi
un vecin j (1 & j & N , fie j i  1 sau j i  1) şi ı̂l ı̂nlocuiţi pe Ai cu (Ai h Aj ) unde h este
XOR-ul pe biţi al numerelor. Definim XOR-ul pe biţi la finalul enunţului.
Scopul vostru este să ı̂l sortaţi pe A:
ˆ dacă S 1 atunci se cere ca şirul final să fie crescător, i.e. Ai $ Ai1 pentru 1 & i $ N
ˆ dacă S 2 atunci se cere ca şirul final să fie nedescrescător, i.e. Ai & Ai1 pentru 1 & i $ N

Găsiţi orice secvenţă de operaţii care ı̂ndeplineşte scopul.


Nu este neapărat să minimizaţi numărul de operaţii, cât timp numărul lor nu trece de 40 000.
Input
Primul rând conţine două valori ı̂ntregi: N şi S.
Al doilea rând conţine N numere ı̂ntregi: elementele lui A.
Output
Primul rând al outputului ar trebui să conţină un număr ı̂ntreg K (0 & K & 40 000) - numărul
de operaţii.
Următoarele K linii ar trebui să conţină două numere ı̂ntregi fiecare, descriind operaţiile ı̂n
ordine cronologică: primul număr este un indice i al elementului care se modifică, iar al doilea
număr este un indice j al celuilalt număr implicat ı̂n operaţie.
Constraints

ˆ 1&S &2
ˆ 2 & N & 1000
CAPITOLUL 7. EJOI 2020 7.4. XORSORT 216

ˆ 0 & Ai $ 220

Subtasks

1 (25 puncte) 2 & N & 150, S 1, Elementele lui A sunt distincte


2 (35 puncte) 2 & N & 200, S 1, Elementele lui A sunt distincte
3 (40 puncte) 2 & N & 1000, S 2

Example
Input Output
51 3
32841 12
43
54
52 3
44201 32
43
54
Explicaţie la primul output:
[3, 2, 8, 4, 1] -¿ [1, 2, 8, 4, 1] -¿ [1, 2, 8, 12, 1] -¿ [1, 2, 8, 12, 13]
Explicaţie la al doilea output:
[4, 4, 2, 0, 1] -¿ [4, 4, 6, 0, 1] -¿ [4, 4, 6, 6, 1] -¿ [4, 4, 6, 6, 7]
XOR-ul dintre biţii a şi b este 0 dacă a b şi 1 altfel.
XOR-ul pe biţi ı̂ntre numerele ı̂ntregi a şi b este operaţia XOR făcută bit cu bit asupra biţilor
lui a şi b:
75 h 29 = 86
1001011 h 0011101 = 1010110
În C/C++/Java se foloseşte operatorul ˆ pentru a face XOR.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 512 MB

7.4.1 Indicaţii de rezolvare

Subtask 1 (25 points):


The main idea is to simulate bubble sort, where the array is sorted by swapping adjacent
members of an array. We can perform a swap of two adjacent members of the array Ai and Aj
by three-step operation (Where h is XOR):

1. Aj h Ai
2. Ai h Aj
3. Aj h Ai

Array will transform like this:

Ai , Aj  Ai , Aj h Ai  Aj , Aj h Ai  Aj , Ai .

n˜ n1
Since the bubble sort can take at most 2
swaps to sort, our method will finish in at most
n˜ n1
˜ 3 & 40 000 operations.
2
Subtask 2 (35 points):
Here, since swapping is not a viable option, it’s likely that we should rearrange the array with
transformed values and then recover original values.
CAPITOLUL 7. EJOI 2020 7.4. XORSORT 217

Recovering the original values is important, because the resulting array should not have equal
members and preserving original array elements is one way to ensure that.
Our approach can be divided into n stages. In each stage, we will swap the largest element to
the end of the unsorted part of the array. Consider the array A a, b, c, d, e, f , where c is the
maximal element.
Firstly, we transform our array to the following one in n  1 operations:

a h b, b h c, c h d, d h e, e h f, f ,

after that, we will make following operations:

Ac1 , Ac , Ac2 , Ac1 , ..., An , An1 ,

so the array will change to


a h b, b h c, c h d, c h e, c h f, c.

After that, we can change left part of A same way:

a h c, b h c, c h d, c h e, c h f, c.

Last two steps took maximum of n  1 (when the maximum element is the first one) operations
in total, i.e. each stage can take 2 ˜ n  2 operations.
As we can see, all the elements, apart from the last, are the original elements XOR-ed with c.
Now we can move on to the next stage, but we have to pick the maximum from the original
array, not the transformed one. For example, if the second maximum is e, we will move fourth
element of A to fifth position (as c already holds the last) even if some element became bigger
after the previous transformations.
Suppose that A would be increasingly sorted as follows: b, f, a, d, e, c. Then, after completing
all the stages, which takes n ˜ n  1 operations, A will be like this:

b h f, f h a, a h d, d h e, e h c, c

(for example, like in the first stage, at the end of the second stage, last (fifth) element would be
e h c, while all the elements before fifth would be XOR-ed with e h c and a h c h e h c a h e, so
we would have a similar situation as at the end of the first stage).
In the final sweep, we can recover original elements of A doing following operations:

An1 , An, An2 , An1 , ..., A1 , A2 .


2
Finally, array is strictly increasingly sorted and we took total of n  1 operations, which just
below the limit when n 200.
Subtask 3 (40 points):
This subtask does not require distinct numbers, which gives us more freedom. We can sort any
array A in O n log n operations in log n sweeps. In each sweep we find the leftmost element in A
with the largest bit in its binary representation and then drag it to the end of the array. Suppose
that such element is Ai . We can perform the following operations:
1. If Ai1 does not contain Ai -s maximum bit, then Ai1 h Ai , after which Ai1 will contain
that bit
2. Ai h Ai1 which removes maximal bit from Ai . In this way, we will drag this bit to the end
in at most 2 ˜ n operations.
It’s easy to see that after each sweep the maximal bit we can find in unsorted part of A will
decrease and since Ai $ 2 , after 20 sweeps only zeros will be left besides the elements to which
20

we already dragged some maximal bits, which is non-decreasing, thus the array is sorted. In total,
this method takes 2 ˜ n ˜ 20 operations, which is exactly 40 000 when n 1000.
CAPITOLUL 7. EJOI 2020 7.4. XORSORT 218

7.4.2 Coduri sursă

Listing 7.4.1: checkerXorSort.cpp


// Author: HeRaNO

#include "testlib.h" //https://github.com/MikeMirzayanov/testlib

using namespace std;

#define MAXN 1005

int a[MAXN];

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../XorSort-tests/input.3_14",
(char*)"../XorSort-tests/output.3_14",
(char*)"xorsort.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerTestlibCmd(argc, argv);

int n = inf.readInt();
int S = inf.readInt();

for (int i = 1; i <= n; i++)


a[i] = inf.readInt();

int k = ouf.readInt(0, 40000);

for (int i = 1; i <= k; i++)


{
int l, r;
l = ouf.readInt(1, n);
r = ouf.readInt(1, n);
if (abs(l - r) != 1)
quitf(_wa, "Opration %d, index wrong: %d %d", i, l, r);
a[l] ˆ= a[r];
}

if (S == 1)
{
for (int i = 1; i < n; i++)
if (!(a[i] < a[i + 1]))
quitf(_wa, "doesn\’t meet requirement. A[%d] = %d, A[%d] = %d",
i, a[i], i + 1, a[i + 1]);
}
else
{
for (int i = 1; i < n; i++)
if (!(a[i] <= a[i + 1]))
quitf(_wa, "doesn\’t meet requirement. A[%d] = %d, A[%d] = %d",
i, a[i], i + 1, a[i + 1]);
}

quitf(_ok, "OK");

return 0;
}

OBS: Pentru #include "testlib.h": fişierul se găseşte la https://github.com/Mik


eMirzayanov/testlib
CAPITOLUL 7. EJOI 2020 7.4. XORSORT 219

Listing 7.4.2: xorsort-980965.cpp


// https://loj.ac/s/980965 182 ms 4.5 M

#include <cstdio>
#include <vector>
#include <cstring>
#include <cassert>

int a[1001], n, S, Base[20];


std::vector<std::pair<int, int>>ope;

void Xor(int i, int j)


{
ope.push_back(std::make_pair(i, j));
a[i] ˆ= a[j];
}

void rev(int l, int r)


{
if (l == r)
return;

for (int j = l; j <= r; j++)


{
for (int i = r; i > j; i--)
{
Xor(i, i - 1);
Xor(i - 1, i);
}
}

for (int j = l; j < r; j++)


Xor(j + 1, j);
}

int Do[1048576];

void dfs(int l, int r, int x, int stat)


{
if (l > r)
{
Do[x] = stat;
return;
}

dfs(l + 1, r, x, stat);
dfs(l + 1, r, x ˆ a[l], stat | (1 << (l - n - 1)));
}

void get(int r, int l)


{
for (int j = l + 1; j < r; j++)
Xor(j - 1, j), Xor(j, j - 1);

Xor(r, r - 1);

for (int j = r - 1; j > l; j--)


Xor(j, j - 1), Xor(j - 1, j);
}

int main()
{
if (freopen("../XorSort-tests/input.3_14", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../XorSort-tests/input.3_14", "r", stdin);


std::freopen("xorsort.out", "w", stdout);

scanf("%d%d", &n, &S);

for (int i = 1; i <= n; i++)


CAPITOLUL 7. EJOI 2020 7.4. XORSORT 220

scanf("%d", a + i);

if (S == 2)
{
for (int i = 19; ˜i; i--)
{
for (int j = 1; j < n; j++)
if (a[j] & (1 << i))
if (!(a[j + 1] & (1 << i)))
Xor(j + 1, j);

for (int j = 1; j < n; j++)


if (a[j] & (1 << i))
Xor(j, j + 1);

if (n && (a[n] & (1 << i)))


--n;
}

printf("%d\n", (int)ope.size());

for (int i = 0; i < (int)ope.size(); i++)


printf("%d %d\n", ope[i].first, ope[i].second);
}
else
{
int cnt = 0;

for (int i = 19; ˜i; i--)


{
for (int j = 1; j < n; j++)
if (a[j] & (1 << i))
if (!(a[j + 1] & (1 << i)))
Xor(j + 1, j);

for (int j = 1; j < n; j++)


if (a[j] & (1 << i))
Xor(j, j + 1);

if (n && (a[n] & (1 << i)))


--n, ++cnt;
}

int old = cnt;


cnt = std::min(cnt, 8);
memset(Do, -1, sizeof Do);
dfs(n + 1, n + cnt, 0, 0);
int now = n + cnt;

for (int i = 1048575; (˜i) && now > cnt; i--)


if (˜Do[i])
{
bool have = 0;

for (int j = now - cnt + 1; j <= now; j++)


if (i == a[j])
have = 1;

if (!have)
{
for (int j = now - cnt; j < now; j++)
Xor(j, j + 1), Xor(j + 1, j);

for (int j = 0; j < cnt; j++)


if (Do[i] & (1 << j))
{
get(now, now - cnt + j);
}

--now;
}
}

for (int j = cnt; j; j--)


{
int tem = j;
CAPITOLUL 7. EJOI 2020 7.4. XORSORT 221

while (tem < n + cnt && a[tem] > a[tem + 1])


{
Xor(tem, tem + 1);
Xor(tem + 1, tem);
Xor(tem, tem + 1);
++tem;
}
}

for (int j = n + cnt + 1; j <= n + old; j++)


{
int tem = j;

while (tem && a[tem] < a[tem - 1])


{
Xor(tem - 1, tem);
Xor(tem, tem - 1);
Xor(tem - 1, tem);
--tem;
}
}

printf("%d\n", (int)ope.size());

for (int i = 0; i < (int)ope.size(); i++)


printf("%d %d\n", ope[i].first, ope[i].second);
}
}

OBS: Pentru max_element: http://www.cplusplus.com/reference/algorithm/


max_element/ sau https://en.cppreference.com/w/cpp/algorithm/max_element

Listing 7.4.3: xorsort-982930.cpp


// https://loj.ac/s/986772 163 ms 676 K

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 2000000;


int c[2000005];

void add(int x, int k)


{
while (x <= N)
{
c[x] += k;
x += x & -x;
}
}

int getSum(int x)
{
int res = 0;

while (x > 0)
{
res += c[x];
x -= x & -x;
}

return res;
}

int n, s;
int a[40005];

int getN()
{
int ans = 0;

for (int i = n; i >= 1; i--)


CAPITOLUL 7. EJOI 2020 7.4. XORSORT 222

{
ans += getSum(a[i] - 1);
add(a[i], 1);
}

return ans;
}

vector<int>v1, v2;
int times = 40000;

void XOR(int x, int y)


{
a[x] ˆ= a[y];
v1.push_back(x);
v2.push_back(y);
}

void Do(int id, int l, int r)


{
if (id < 0)
return ;

if (l >= r)
return ;

int ok = 0;

for (int i = l; i <= r; i++)


if (((a[i] >> id) & 1))
ok = 1;

if (!ok)
{
Do(id - 1, l, r);
return ;
}

for (int i = l; i < r; i++)


{
if (((a[i] >> id) & 1) && ((a[i + 1] >> id) & 1))
{
XOR(i, i + 1);
}
else
if (((a[i] >> id) & 1) && !((a[i + 1] >> id) & 1))
{
XOR(i + 1, i);
XOR(i, i + 1);
}
}

Do(id - 1, l, r - 1);
}

int p[40005];

void Do1(int l, int r, int x)


{
if (l >= r)
return ;

int mxi = l;

for (int i = l; i <= r; i++)


if ((a[i]ˆx) > (a[mxi]ˆx))
mxi = i;

int ji = a[mxi] ˆ x;

for (int i = mxi + 1; i <= r; i++)


XOR(i, i - 1);

for (int i = mxi - 2; i >= l; i--)


XOR(i, i + 1);
CAPITOLUL 7. EJOI 2020 7.5. CARDS 223

Do1(l, r - 1, ji);
}

int main()
{
if (freopen("../XorSort-tests/input.3_14", "r", stdin) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
// returneaza 0 daca nu exista fisierul de intrare ... !!!

//std::freopen("../XorSort-tests/input.3_14", "r", stdin);


std::freopen("xorsort.out", "w", stdout);

scanf("%d%d", &n, &s);

for (int i = 1; i <= n; i++)


scanf("%d", &a[i]), p[i] = a[i];

if (s == 2)
Do(19, 1, n);
else
{
for (int i = 1; i < n; i++)
XOR(i, i + 1);

for (int i = n; i > 1; i--)


{
int mxi = 1;

for (int j = 1; j <= i; j++)


if (p[j] > p[mxi])
mxi = j;

for (int j = mxi; j < i; j++)


p[j] = p[j + 1];

for (int j = mxi; j < i; j++)


XOR(j + 1, j);

for (int j = max(1, mxi - 1); j < i; j++)


XOR(j, j + 1);
}

printf("%d\n", (int)v1.size());

for (int i = 0; i < (int)v1.size(); i++)


printf("%d %d\n", v1[i], v2[i]);

return 0;
}

Listing 7.4.4: xorsort-986772.cpp

7.4.3 *Rezolvare detaliată

7.5 cards
Problema 5 - Card Trick 100 de puncte
Doi jucători doresc să realizeze un truc folosind un pachet standard cu 52 de cărţi de joc.
Pentru comoditate, valorile cărţilor sunt distincte, cuprinse ı̂ntre 0 şi 51.
Iniţial cărţile sunt aşezate pe o masă, formând un singur rând, cu faţa ı̂n sus (cu valorile
vizibile), ı̂ntr-o anumită ordine necunoscută de cei doi.
CAPITOLUL 7. EJOI 2020 7.5. CARDS 224

Primul jucător merge la masă, priveşte cărţile şi realizează cel mult S interscimbări. Fiecare
interschimbare constă ı̂n alegerea a două cărţi de pe poziţiile i şi j (i şi j pot fi egale) şi duce
cartea de pe poziţia i ı̂n locul celei de pe poziţia j, şi invers.
După aceasta primul jucător pleacă fără a comunică cu primul şi toate cărţile vor fi ı̂ntoarse
(deci nu se mai vede ce conţin), fără să li se schimbe ordinea. Al doilea jucător este invitat la
masă şi este ı̂ntrebat unde se găseşte o anumită carte cu o anume valoare ţintă permiţându-i-se să
ı̂ntoarcă cel mult T cărţi, una câte una. Dacă una dintre cărţile ı̂ntoarse este cea ţintă, atunci cei
doi jucători câştigă. Scopul tău este să scrii două programe care să simuleze acţiunile celor doi şi
să câştige jocul.
Implementation Details
Vi se vor furniza două programe - FirstPlayer şi SecondPlayer, ı̂mpreună cu un exemplu
de evaluator.
În FirstPlayer ai de implementat următoarea funcţie:

void swapCards(int cards[], int S, int T)

ˆ Funcţia este apelată exact o dată de către evaluator


ˆ cards: un tablou conţinând iniţial valorile de la stanga la dreapta, cu exact 52 de elemente,
indexat de la 0 la 51
ˆ S: numărul de interschimbări
ˆ T : numărul de ghiciri permise

swapCards poate apela următoarea funcţie:

void doSwap(int i, int j)

ˆ i: indicele primei cărţi de interschimbat, 0 & i $ 52


ˆ j: celei de-a doua cărţi de interschimbat, 0 & j $ 52
ˆ doSwap poate fi apelată de cel mult S ori

În SecondPlayer ai de implementat funcţia următoare:

void guessCard(int S, int T, int target)

ˆ S: numărul de interschimbări
ˆ T : number de ghiciri permise
ˆ target: cartea a cărei valori trebuie descoperită

guessCard poate apela funcţia următoare:

int guess(int idx)

ˆ idx: indicele ghicit, 0 & idx $ 52


ˆ Returnează valoarea cărţii de indicele i.
ˆ guess poate fi apelată de cel mult T ori.
ˆ când apare ghicirea, evaluarea se termină cu succes

Sample interaction
Mai jos este un exemplu de input pentru evaluatorul ataşat.
Prima linie trebuie să conţină doi ı̂ntregi: S şi T .
A doua linie trebuie să conţină 52 de numere. Al i-lea reprezintă valoarea cărţii i.
A treia linie conţine un ı̂ntreg ţinta.
CAPITOLUL 7. EJOI 2020 7.5. CARDS 225

Exemplu Exemple de apeluri


de intrare
pentru apeluri subapeluri Returnări
evaluator
1 51 swapCards([0,1,...], 1, 51)
0123456 doSwap(0, 1)
7 8 9 10 11 Interschimbă cărţile de la indicii 0 şi 1.
12 13 14 15 swapCards finishes.
16 17 18 19 guessCard(1, 51, 1)
20 21 22 23 guess(5)
24 25 26 27 guess returns 5
28 29 30 31 guess(1)
32 33 34 35 guess returns 0
36 37 38 39 guess(0)
40 41 42 43 Correct!
44 45 46 47
48 49 50 51
1
Tabelul 7.2: Card Trick

Constraints

ˆ 1&S & 52
ˆ 1&T & 51
ˆ 0 & ţinta $ 52

Subtasks

1 (16 puncte): S 52, T 1


2 (20 puncte): S  T 52
3 (22 puncte): S 13, T 27
4 (18 puncte): S 1, T 26
5 (24 puncte): Există strategie de câştig pentru S şi T

Timp maxim de executare/test: 0.5 secunde


Memorie: total 512 MB

7.5.1 Indicaţii de rezolvare

Subtask 1 (16 points):


In this subtask we can sort the cards in 51 swaps, so the second player can choose target-th
card and be sure that the guess is correct.
Subtask 2 (20 points):
Here, since we have S swaps and N  S guesses, the first player can put first S cards on
corresponding indexes and left all others in arbitrary places, if target & S, the second player will
reveal target-th card and guess first try. Otherwise in N  S guesses the unsorted part can be
fully searched.
Subtask 3 (22 points):
In this subtask we have n4 swaps and n2 1 guesses. We can divide our deck in two predetermined
parts (for example, odd and even indexes or first and second halves).
CAPITOLUL 7. EJOI 2020 7.6. GAME 226

For simplicity, we take odd and even indexes.


Now we should observe the deck and see how many odd-valued cards are in even indexes (it’s
easy to see that this number will be equal to the number of even-valued cards on odd indexes).
It’s obvious, that either that number will be less or equal to n4 , or the number of even-valued cards
on even indexes.
So, in at most n4 swaps, we can either put all the even-valued cards, or all the odd-valued cards
on even indexes (after that, all the values of the odd indexed cards will have the same parity too).
On the first guess, the second player should check if odd indexes hold odd-valued cards by
revealing the first card and after that, we have n2 tries and n2 possible locations of target, so the
problem can be solved by full search in corresponding half.
Subtask 4 (18 points):
In this subtask we have 1 swap and n2 guesses. If we interpret our deck as a graph, where i-th
card is connected to the vi -th one (where vi is the value of the i-th card), we will get a graph,
where every card is a part of exactly one simple cycle.
If we follow the cycle of card t by first revealing target-th card, then vt -th, vvt -th and so on,
we will find the card target in exactly st guesses (where st is the size of that cycle), because the
last card of the cycle should point to index target, which means it has a value of target.
The main issue of this solution is that we can have cycles larger than n2 , but we can’t have
more than one such cycle for obvious reasons. If such cycle exists, we can swap n2  1-th and the
first element of cycle (you can enumerate the elements of the cycle any way you want as long as
i-th card points to i  1-th and the last one points to the first).
After the swap, n2 -th card will still point to the index of n2  1-th card, but now that card
points to the second one, so now this means that the size of one of newly formed cycle is exactly
n
2
, while the size of remaining cycle is si  n2 & n2 , so now the second player can definitely find the
desired card in time.
Subtask 5 (24 points):
This subtask is just a generalization of the last one. If we have S swaps and T guesses, on each
swap we can take one of the cycles with size greater than T (if one exists) and cut the T -sized
cycle out of it. Algorithm of the second player remains exactly the same.

7.5.2 *Coduri sursă

***

7.5.3 *Rezolvare detaliată

7.6 game
Problema 6 - Dots and Boxes 100 de puncte
Tamta şi Anna sunt surori la care le place să joace ”Dots and Boxes”. Jocul ı̂ncepe cu un grid
gol de N  1 pe M  1 puncte (şi un grid corespunzător de N pe M pătraţele).
Jucătorii joaca alternativ adăugând o singură muchie orizontală sau verticală ı̂ntre două puncte
neunite adiacente (două puncte sunt adiacente dacă distanţa dintre ele este 1).
Dacă un jucător completează a patra latură a unui pătrăţel de 1  1 atunci jucătorul câştigă
un punct şi ia o tură ı̂n plus, altfel tura alternează la celălalt jucător. Jocul se termină când nu
se mai pot adăuga muchii.
Trei ture posibile ı̂ntr-un grid cu =2,=3 (liniile punctate sunt mutările jucătorilor):
CAPITOLUL 7. EJOI 2020 7.6. GAME 227

Anna şi Tamta au jucat o vreme şi ai observat că ı̂n starea curentă
fiecare pătrăţel are exact zero sau două laturi neunite şi ca Anna
e la mutare (puteţi vedea un exemplu ı̂n imaginea din dreapta. Observaţi
că imaginea de mai sus nu corespunde acestor restricţii).
Scorul jocului este SA  ST unde SA este numărul de puncte ce ı̂l face
Anna de acum ı̂nainte şi ST este numărul de puncte pe care ı̂l face Tamta.
Evident, Anna vrea să maximizeze scorul şi Tamta ı̂ncearcă să ı̂l minimizeze.
Calculaţi scorul final ştiind că amândoi jucătorii joacă optim.
Input
Prima linie conţine două numere ı̂ntregi N şi M care reprezintă numărul de rânduri şi de
coloane ı̂n gridul de pătrăţele.
Fiecare dintre următoarele N  1 linii conţine M cifre, acestea fiind unu sau zero (neseparate
prin spaţii), cel de al j-lea număr ı̂n al i-lea rând fiind unu dacă şi nu mai dacă exista o muchie
orizontală ı̂ntre punctele cu coordonatele i, j  şi i, j  1.
Fiecare dintre următoarele N linii conţine M  1 cifre, ı̂n acelaşi format, cel de al j-lea număr ı̂n
al i-lea rând fiind unu dacă şi numai dacă exista o muchie verticală ı̂ntre punctele cu coordonatele
i, j  şi i  1, j .
Output
Outputul conţine un singur rând cu un singur număr ı̂ntreg, scorul final.
Constraints

ˆ 3 & N, M & 20
ˆ Fiecare pătrăţel are exact zero sau două laturi neunite.

Subtasks
Definim o componentă ca fiind un set maximal de pătrăţele neluate de
nimeni pe un grid astfel ı̂ncât te poţi deplasă de la oricare pătrăţel la oricare
altul traversând muchii care nu sunt ı̂ncă desenate.

1 (20 puncte): Există o singură componentă rămasă.


2 (20 puncte): N M & 12
3 (20 puncte): Au mai rămas două componente.
4 (20 puncte): N & 7, M & 7
5 (20 puncte): Fară restricţii suplimentare.

Example
CAPITOLUL 7. EJOI 2020 7.6. GAME 228

Input Output
33 -5
000
111
011
55 6
00100
10100
11010
00100
01000
11100
011111
001011
101011
110111
100111
Primul exemplu, cu una dintre secvenţele optime de mutări, sunt desenate mai jos (numerele
pe muchii reprezintă ordinea mutării, culoarea roşie e folosită de Anna, cea albastră de Tamta).
Al doilea exemplu este cel din imaginile de mai sus.

Timp maxim de executare/test: 0.5 secunde


Memorie: total 512 MB

7.6.1 Indicaţii de rezolvare

Subtask 1 (20 points):


Since there is only one component the second player is going to claim it. It was enough to
calculate the size of that component which can be done by counting the number of already claimed
boxes and subtracting from the total number.
Subtask 2 (20 points):
#unjoined sides
The number of positions players could get (which is 2 ) was small enough to
try and determine the optimal strategy from each possible state using dynamic programming or
memoization.
Subtask 3 (20 points):
The general strategy should be about claiming the boxes whenever possible but it is important
to understand that sometimes when you have the opportunity to take a whole chain of length n
it is better to claim n  2 boxes and leave the last 2 to the other player by making a move that
doesn’t complete any of those 2 boxes.
Using this you are forcing the opponent to make the bad move later that will give the current
player an even bigger component back. With this in mind, one could find the components (whose
amount is at most 2) and find the answer using simple casework.
Subtask 4 (20 points):
Due to the constraints each component will be either a chain or a loop. The state of the
game can be described as the lengths of the remaining chains and loops. There won’t be many
CAPITOLUL 7. EJOI 2020 7.6. GAME 229

components in this subtask and one could solve it by solving each state, similar to subtask 2, this
#of components
time the number of states being O 2 .

Subtask 5 (20 points):


Notice that if one player has to give up the component of a certain kind (chain or loop), it
is better to give up the smallest one. Using this the number of states that optimal solutions can
reach can be reduced significantly to some polynomial amount and this time dynamic program-
ming/memoization approach can be used to achieve the full score.

7.6.2 *Coduri sursă

***

7.6.3 *Rezolvare detaliată


Capitolul 8
114
EJOI 2019

8.1 Hanging rack


Problema 1 - Hanging Rack 100 de puncte
A hanging rack is composed of n levels of connected rods. Level i (for i " r0, 1, ..., n  1x)
i
consists of 2 rods. The midpoint of the rod at level 0 is fixed to the wall. At all other levels, the
midpoint of the j-th rod (for j " r1, ..., 2 x) is fixed to the left endpoint of the *j ©20-th rod at the
i

previous level if j is odd and to the right endpoint of the same rod if j is even. At the last level,
there is a hook for hanging a coat on both endpoints of each rod. The hooks are numbered from
n
1 to 2 in the left-to-right order.
For example, the rack for n 3 looks as follows:

Mojca would like to hang all her coats on the rack. Every coat weighs exactly 1 unit. To avoid
breaking the delicate structure, she has to hang them in such an order that the difference between
the total weight wl placed on the left endpoint of any given rod and the total weight wr placed
on the right endpoint of the same rod is either 0 or 1 (wl  wr " r0, 1x). (By the laws of physics,
the difference could also be -1, but a right-leaning rack looks really ugly to Mojca.) The rods are
so thin that their weight can be neglected.
Having heard about your problem-solving proficience, Mojca asks you for help. Write a pro-
9
gram that reads the integer n and an integer k and prints the sequential number (modulo 10  7)
of the hook on which Mojca has to hang her k-th coat.
Input
The input consists of a single line, which contains the integers and , separated by a space.
Output
Print the number (modulo ) of the hook to be used in the -th step.
Constraints
n " 1, 10 ,
6
ˆ

114
aur: Alexandru Luchianov, clasa a 8-a, Liceul Internaţional de Informatică din Bucureşti,
. aur: Luca Perju-Verzotti, clasa a 8-a, Colegiul Naţional de Informatică ”Tudor Vianu” din Bucureşti,
. aur: Alexandru Raul Todoran, clasa a 7-a, Liceul Teoretic ”Aurel Vlaicu” din Orăştie, jud. Hunedoara,
. argint: Mihnea Andreescu, clasa a 8-a, Liceul Teoretic Internaţional de Informatică din Bucureşti.

230
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 231

ˆ k " 1, minr2n , 1018 x.


Subtasks

ˆ 20 points: n " 1, 10.


ˆ 20 points: n " 1, 20.
ˆ 60 points: no additional constraints.

Example 1
Input

32

Output

Comment
In this case, the hooks should be used in the following order: 1, 5, 3, 7, 2, 6, 4, 8. In the second
step, Mojca thus has to hang her coat on the hook with number 5.
Example 2
Input

5 10

Output

19

Comment
Here, the order of hooks is 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, etc.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 100 MB

8.1.1 Indicaţii de rezolvare

Let’s look on the level 0. First coat should go in the left subtree, second should go in the right
subtree, and so on. So we can calculate, in which subtree is k-th coat by looking on the parity
of number k. Now we know, which subtree we need, let’s calculate how many coats are in this
subtree. Now have the same task for the subtree, do it recursively. We have n layers of recursion,
and on each step we solve problem in O 1 time, so the total time will be O n.

8.1.2 Coduri sursă

Listing 8.1.1: 216711-rack.cpp


//https://oj.uz/submission/216711

#include <bits/stdc++.h>

using namespace std;

int MOD = 1e9 + 7;

int main()
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 232

{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

long long n, k;
cin>>n>>k;

vector<long long> pow_two;


pow_two.push_back(1);

long long at=1;


for(int i=1;i<n;i++)
{
at*=2;
at %= MOD;
if(at < 0)
return 0;
pow_two.push_back(at);
}

long long rez=1;


long long step=1;
while(k!=1)
{
if(k%2==0)
{
rez+=pow_two[n-step];
rez %= MOD;
if(rez < 0)
return 0;
k-=k/2;
step++;
}
else
{
step++;
k-=k/2;
}
}

cout<<rez<<endl;
return 0;
}

Listing 8.1.2: 217942-rack.cpp


// https://oj.uz/submission/217942
/*
ID: Sho10
LANG: C++
*/
115
#include <bits/stdc++.h> //Andrei Alexandru a.k.a Sho10
#define ll long long int
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#define all(a) (a).begin(), (a).end()
#define sz size
#define f first
#define s second
#define pb push_back
#define er erase
#define in insert
#define mp make_pair
#define pi pair
#define rc(s) return cout<<s,0
#define endl ’\n’
#define mod 1000000007
#define PI 3.14159265359
#define CODE_START ios_base::sync_with_stdio(false); cin.tie(0);cout.tie(0);

using namespace std;

ll n,k;
115
https://oj.uz/profile/Sho10
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 233

ll pw(ll a,ll b)
{
if(b==0) return 1;
ll n=pw(a,b/2);
if(b%2==1)
{
return (((n*n)%mod)*a)%mod;
}
else
return (n*n)%mod;
}

int32_t main()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

CODE_START;
cin>>n>>k;

ll ans=1,s1=1;
while(k!=1)
{
if(k%2==0)
{
ans=ans+pw(2LL,n-s1);
ans=ans%mod;
s1++;
ll val=k/2;
k=k-val;
}
else
{
s1++;
ll val=k/2;
k=k-val;
}
}

cout<<ans<<endl;
}

Listing 8.1.3: 229739-rack.cpp


// https://oj.uz/submission/229739

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;

#define SZ(x) (int)(x.size())


#define F0(i,n) for(int i=0;i<n;i++)
#define F1(i,n) for(int i=1;i<=n;i++)
#define CL(a,x) memset(x, a, sizeof(x));
#define PR(x) cerr << #x << "=" << (x) << endl;

const int MOD = 1000000007;


const double pi = atan(1.0)*4.0;
const double eps = 1e-8;
const int N = 200001;

int i, j, k, m, n;
int a[N];

int pw[1000001];

int main()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

ll k;
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 234

cin >> n >> k;


k--;

pw[0] = 1;
F1(i, n) pw[i] = pw[i - 1] * 2 % MOD;

int ret = 0;
for (int i = n - 1; i >= 0; i--)
{
if (k % 2 == 1)
{
ret = (ret + pw[i]) % MOD;
}
k /= 2;
}

cout << ret + 1 << endl;

return 0;
}

Listing 8.1.4: 232150-rack.cpp


// https://oj.uz/submission/232150

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

ll expo(ll x, ll n, ll m)
{
if(n==0)return 1;
ll u=expo(x,n/2,m);
u=(u*u)%m;
if(n%2)u=(u*x)%m;
return u;
}

int main ()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

long long n,k;


cin>>n>>k;
n--;
long long start;

if(n>59)
start=1;
else
{
ll temp=1e18;
ll res=expo(2,n,temp);
if(k<=res)
start=1;
else
{
start=2;
k-=res;
}
}
//cout<<start<<endl;

int mod=1e9+7;

while(k>(ll)1)
{

if(k%2)
{
k/=2;
k++;
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 235

}
else
{
k/=2;
start+=expo(2,n,mod);
start%=mod;
}

n--;
}

cout<<start<<endl;
}

Listing 8.1.5: 232196-rack.cpp


// https://oj.uz/submission/232196
//
// main.cpp
// Hanging Rack
//
// Created by Panagiotis Hadjicostas on 16/05/2020.
// Copyright 2020 Panagiotis Hadjicostas. All rights reserved.
//

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ll;


#define MOD ll(1e9+7)
ll din[1000001];

ll reverse1(ll num,ll n)
{
ll b=0;
for (ll i = n - 1; i >= 0&&num; i--)
{
if (num % 2 == 1)
{
b=(b+din[i])%MOD;
//cout<<b<<endl;
}
num/=2;
}
return b;
}

int main()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

ll n,k;
cin>>n>>k;
din[0]=1;
for(ll i=1;i<1000001;i++)
{
din[i]=(din[i-1]*2)%MOD;
}
//cout<<’h’;
//k--;
cout<<(reverse1(k-1, n)+1)%MOD<<endl;
return 0;
}

Listing 8.1.6: 237579-rack.cpp


// https://oj.uz/submission/237579

#include <iostream>
#include <vector>
#include <set>
#include <map>
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 236

#include <algorithm>
#include <cmath>

using namespace std;

typedef vector<int> vi;


typedef pair<int,int> pi;
typedef long long ll;

#define loop(i,a,b) for (int i = a; i <= b; i++)

#define INF ((unsigned) ˜0) // ˜ bit inversion


#define F first
#define S second
#define PB push_back
#define MP make_pair

const ll MOD = 1000000000 + 7;

int main()
{
//cout<<"INF = "<<INF<<"\n";
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

int n;
ll k;
cin >> n >> k;
k--;
ll res = 1;
ll e;
ll p;
for(e = n - 1, p = 1; e >= 0; e--, p = (p * 2) % MOD)
if(e < 60)
if(k & ((ll)1<<e))
res = (res + p) % MOD;
cout << res << "\n";
}

OBS: 116
Listing 8.1.7: 237607-rack.cpp
// https://oj.uz/submission/237607

#include <bits/stdc++.h>

using namespace std;

#define ll long long


#define MOD 1000000007
#define MAXN 100005

typedef pair<int, int> pii;

ll fast(ll base, ll exp)


{
if(exp==0) return 1%MOD;
ll u = fast(base,exp/2);
u=(u*u)%MOD;
if (exp%2==1) u=(u*base)%MOD;
return u;
}

ll solve(ll n, ll k)
{
if(n==1&&k==1)return 1%MOD;
if(n==1&&k==2)return 2%MOD;

ll ans = fast(2,n-1);

if(k%2==0)
return (solve(n-1,k/2)+ans)%MOD;
else
116
http://www.cplusplus.com/doc/tutorial/operators/
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 237

return solve(n-1,(k+1)/2)%MOD;
}

int main()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

ll n,k,ans;
cin>>n>>k;

ans=solve(n,k)%MOD;

if(ans<0) ans+=MOD;

cout<<ans<<’\n’;

return 0;
}

Listing 8.1.8: 237667-rack.cpp


// https://oj.uz/submission/237667

#include <iostream>

using namespace std;

typedef unsigned long long ll;

const ll modulo = 1000000007;


ll n, k;
bool ans[1000005];

void recursiva(ll level, ll casacos)


{
if (level == n)
return;

if (casacos % 2 == 0)
recursiva(level + 1, casacos / 2);
else
{
ans[level] = true;
recursiva(level + 1, (casacos - 1) / 2);
}
}

int main()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

cin >> n >> k;


recursiva(0, k - 1);
ll res = 0;

for (int i = 0; i < n; i++)


res = ((res << 1) | ans[i]) % modulo;

cout << res + 1 << endl;


return 0;
}

Listing 8.1.9: 237671-rack.cpp


// https://oj.uz/submission/237671

#include <bits/stdc++.h>

using namespace std;

const int64_t MOD = 1000000007;


CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 238

int64_t fast_pow(int64_t x, int64_t y)


{
x %= MOD;
if (x == 0)
{
return 0;
}

int64_t res = 1;
while (y > 0)
{
if (y % 2 == 1)
{
res = (res * x) % MOD;
}

y >>= 1;
x = (x * x) % MOD;
}

return res;
}

int64_t solve(int64_t n, int64_t k)


{
if (n == 0)
{
if (k != 0)
{
abort();
}

return 1;
}

if (k % 2 == 0)
{
return solve(n - 1, k / 2);
}
else
{
int64_t k2 = 0;
if (k > 1)
{
k2 = k / 2;
}

return (fast_pow(2, n - 1) + solve(n - 1, k2)) % MOD;


}
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994


//std::freopen("rack.out", "w", stdout);

int64_t n, k;
cin >> n >> k;
cout << solve(n, k - 1) << endl;
}

117
OBS: int64 t,¡cstdint¿

Listing 8.1.10: 240337-rack.cpp


// https://oj.uz/submission/240337

#include <bits/stdc++.h>

using namespace std;

117
http://www.cplusplus.com/reference/cstdint/
CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 239

typedef long long lo;


typedef pair< lo,lo > PII;

#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define int long long
#define fio() ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL)
#define FOR for(int i=1;i<=n;i++)
#define mid ((start+end)/2)
#define ort ((bas+son)/2)

const lo MAX = -1000000000000000000;


const lo MIN = 1000000000000000000;
const lo inf = 1000000000;
const lo KOK = 100000;
const lo LOG = 30;
const lo li = 500005;
const lo mod = 1000000007;

int n,m,b[li],a[li],k,flag,t;
int cev;
string s;
vector<int> v;

inline int mul(int x,int y)


{
return (x%mod)*(y%mod)%mod;
}

inline int add(int x,int y)


{
if(x+y>=mod) return x+y-mod;
return x+y;
}

inline int fp(int x,int y)


{
if(y==0)return 1;
if(y==1)return x;
int tt=fp(x,y/2);
tt=mul(tt,tt);
if(y%2==1) tt=mul(tt,x);
return tt;
}

main(void)
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

scanf("%lld %lld",&n,&m);
while(m>1)
{
//˜ m--;
v.pb((m%2));
//˜ if(m/2==0)break;
m=(m+1)/2;
}

int bas=1;
int say=n-1;
//˜ reverse(v.begin(),v.end());
for(int i=0;i<(int)v.size();i++)
{
if(v[i]==0) bas=add(bas,fp(2,say));
say--;
//˜ cout<<bas<<" "<<son<<endl;
}

printf("%lld\n",bas%mod);
return 0;
}

Listing 8.1.11: 242103-rack.cpp


CAPITOLUL 8. EJOI 2019 8.1. HANGING RACK 240

// https://oj.uz/submission/242103

#include<bits/stdc++.h>

using namespace std;

#define int long long

const int M = 1e9 + 7;

int bin_pow(int val, int n)


{
int res = 1;
while(n > 0)
{
if(n & 1)
{
res *= val;
res %= M;
}

val *= val;
val %= M;
n /= 2;
}

return res;
}

signed main()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(0);

int n, k;
cin >> n >> k;
k--;
int ans = 1;
for(int i = 0; i < n; i++)
{
if(k & 1)
{
ans += bin_pow(2, n - i - 1);
ans %= M;
}

k /= 2;
}

cout << ans;


return 0;

Listing 8.1.12: 246139-rack.cpp


// https://oj.uz/submission/246139

#include <bits/stdc++.h>

#define endl "\n"


#define int long long
#define see(x) cerr<<#x<<"="<<x<<endl;

using namespace std;

const int N = 1e6 + 10;


const int MOD = 1e9 + 7;

int bp (int n)
{
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 241

if (!n) return 1;
if (n & 1) return 2ll * bp(n-1) % MOD;
int b = bp(n>>1);
return b * b % MOD;
}

int n, k, ans = 1;

int32_t main ()
{
std::freopen("../rack-tests/input.test_03_05", "r", stdin); // 751564994
//std::freopen("rack.out", "w", stdout);

ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

cin >> n >> k;


k --;

for (int i = n - 1; i >= 0; -- i)


if (k & 1)
{
ans += bp(i);
ans %= MOD;
k >>= 1;
}
else k >>= 1;

cout << ans << endl;

return 0;
}

118
OBS: ios base::sync with stdio(0);

8.1.3 *Rezolvare detaliată

8.2 XORanges
Problema 2 - XORanges 100 de puncte
Janez loves oranges! So he made a scanner for oranges. With a cameras and a Raspberry Pi
3b+ computer, he started creating 3D images of oranges. His image processor is not a very good
one, so the only output he gets is a 32-bit integer, which holds information about the holes on the
peel. A 32- bit integer is represented as a sequence of 32 digits (bits) each of which is one or zero. If
we start from 0 we can obtain D by adding for every i-th bit that is equal to one. More formally the
31 30 1 0
number D is represented by the sequence d31 , d30 , ..., d0 when D d31 2 d30 2 ...d1 2 d0 2 .
For example, 13 is represented as 0, ..., 0, 1, 1, 0, 1.
Janez scanned n oranges; however, sometimes he decides to rescan one of the oranges (i-th
orange) during the execution of your program. This means that from this scan on, he uses the
updated value for the i-th orange.
Janez wants to analyse those oranges. He finds exclusive or (XOR) operation very interesting,
so he decides to make some calculations. He selects a range of oranges from l to u (where l & u)
and wants to find out the value of XOR of all elements in that range, all pairs of consecutive
elements in that range, all sequences of 3 consecutive elements and so on up to the sequence of
u  l  1 consecutive elements (all elements in the range).
I.e. If l 2 and u 4 and there is an array of scanned values A, program should return the
value of a2 h a3 h a4 h a2 h a3  h a3 h a4  h a2 h a3 h a4 , where h represents the XOR and
ai represents the i-th element in array A.
Let XOR operation be defined as:
118
https://www.geeksforgeeks.org/fast-io-for-competitive-programming/
. https://codeforces.com/blog/entry/18093
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 242

If the i-th bit of the first value is the same as the i-th bit of the second value, the i-th bit of
the result is 0; If the i-th bit of the first value is different as the i-th bit of the second value, the
i-th bit of the result is 1.

x y xhy
0 0 0
0 1 1
1 0 1
1 1 0
Tabelul 8.1: XOR

For example, 13 h 23 26.

13 = 0...0 0 1 1 0 1
23 = 0...0 1 0 1 1 1
13 h 23 = 0...0 1 1 0 1 0

Input
In the first line of the input there are 2 positive integers n and q (total number of rescans and
queries - actions).
In the next line, there are n space-separated non-negative integers, which represent values of
the array A (scan results for oranges). Element ai contains the value for i-th orange. Index i
starts with 1.
Actions are described in the next q lines with three space-separated positive integers.
If the action type is 1 (rescan), the first integer equals 1 and is followed by i (index of an
orange that Janez wants to rescan) and j (the result of the rescan of the i-th orange).
If the action type is 2 (query), the first integer equals 2 and is followed by l and u.
Output
You should print exactly one integer for each query with the matching result for the query.
You should print every value in a new line. Note that the i-th line of the output should match
the result of the i-th query.
Constraints

ˆ ai & 109
0 $ n, q & 2
5
ˆ 10

Subtasks
1. [12 points]: 0 $ n, q & 100
2. [18 points]: 0 $ n, q & 500 and there are no rescans (actions of type 1)
3. [25 points]: 0 $ n, q & 5000

4. [20 points]: 0 $ n, q &2 5


10 and there are no rescans (actions of type 1)
5. [25 points]: No additional constraints.

Examples
Example 1
Input

3 3
1 2 3
2 1 3
1 1 3
2 1 3
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 243

Output

2
0

Comment
At the begining, A 1, 2, 3. The first query is on the full range. The result of the analysis
is 1 h 2 h 3 h 1 h 2 h 2 h 3  1 h 2 h 3 2.
Then the value of the first orange is updated to 3. This leads to a change on the same query
(on a range 1, 3) 3 h 2 h 3 h 3 h 2 h 2 h 3  3 h 2 h 3 0.
Example 2
Input

5 6
1 2 3 4 5
2 1 3
1 1 3
2 1 5
2 4 4
1 1 1
2 4 4

Output

2
5
4
4

Timp maxim de executare/test: 1.0 secunde


Memorie: total 64 MB

8.2.1 Indicaţii de rezolvare

Since x h x 0, after simplification of the final h-sum each element will be presented at most once.
To determine if the element is presented in the final h- sum, we need to calculate the number of
segments it belongs to. If this number is odd, then the element will be in the final h-sum.
The element i of segment l; r belongs to i  l  1 r  i  1 subsegments. This number is
odd if both i  l  1 and r  i  1 are odd. If l and r has different parity, then it is impossible,
so the final h-sum will be 0, else i  l  1 r  i  1 will be odd for i l, l  2, l  4, ..., r. Now we
need data structure to calculate sums like xl h xl  2 h xl  4 h ... h xr. We can use two segment
trees (or Fenwick trees), one for elements with odd indices, another for element with even indices.
So, the queries can be answered in O log n time.

8.2.2 Coduri sursă

Listing 8.2.1: 218063-XORanges.cpp


// https://oj.uz/submission/218063

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 244

#include <unordered_map>
#include <queue>
#include <cassert>
#include <string>
#include <cstring>
#include <bitset>
#include <random>
#include <chrono>

#define FOR(i, a, b) for(int i = a; i < (int) b; i++)


#define F0R(i, a) FOR (i, 0, a)
#define ROF(i, a, b) for(int i = a; i >= (int) b; i--)
#define R0F(i, a) ROF(i, a, 0)
#define GO(i, a) for (auto i : a)

#define rsz resize


#define eb emplace_back
#define pb push_back
#define sz(x) (int) x.size()
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define f first
#define s second

using namespace std;

typedef pair<int, int> pii;


typedef vector<int> vi;
typedef vector<pii> vpii;
typedef vector<vi> vvi;
typedef vector<vpii> vvpii;
typedef long long i64;
typedef vector<i64> vi64;
typedef vector<vi64> vvi64;
typedef pair<i64, i64> pi64;
typedef vector<pi64> vpi64;

const int N = 2e5 + 5;


i64 bit [N][2];

void update (int idx, int delta, int where)


{
for (; idx < N; idx += idx & (- idx))
{
bit[idx][where] ˆ= delta;
}
}

i64 query (int idx, int where)


{
i64 tot = 0;
for (; idx > 0; idx -= idx & (- idx))
{
tot ˆ= bit[idx][where];
}
return tot;
}

i64 range_sum (int lo, int hi, int where)


{
return query (hi, where) ˆ query (lo - 1, where);
}

int main ()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);

int n, q;
cin >> n >> q;

vi a (n);
FOR (i, 1, n + 1)
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 245

{
cin >> a[i];
update (i, a[i], i & 1);
}

while (q--)
{
int t, lo, hi;
cin >> t >> lo >> hi;
if (t == 1)
{
// Update
update (lo, a[lo], lo & 1);
update (lo, hi, lo & 1);
a[lo] = hi;
}
else
{
// Query
const int T = hi - lo + 1;
if ((T & 1) == 0)
{
cout << 0 << ’\n’;
continue;
}
cout << range_sum (lo, hi, lo & 1) << ’\n’;
}
}

return 0;
}
/*
argc = 4
checker
../xoranges-tests/input.s5t5
../xoranges-tests/output.s5t5
xoranges.out
----------------------
1
Correct

Process returned 0 (0x0) execution time : 0.721 s


Press any key to continue.
*/

Listing 8.2.2: 220350-XORanges.cpp


// https://oj.uz/submission/220350

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <cmath>

using namespace std;

const int INF = 2e9;


const int N = 2e5;

int n, q;
int v[5 + N];

class AIB
{
private:
int a[5 + N];

int lsb(int i)
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 246

{
return (i & (-i));
}

public:
void Build(int pos, int val)
{
for(int i = pos; i <= n; i += lsb(i))
a[i] = a[i] ˆ val;
}

void Update(int pos, int val)


{
for(int i = pos; i <= n; i += lsb(i))
a[i] = a[i] ˆ v[pos] ˆ val;
v[pos] = val;
}

int Query(int pos)


{
int s(0);
for(int i = pos; i >= 1; i -= lsb(i))
s ˆ= a[i];

return s;
}
};
AIB FenwOdd, FenwEven;

int main()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

scanf("%d%d", &n, &q);


for(int i = 1; i <= n; i++)
{
scanf("%d", &v[i]);
if(i & 1)
FenwOdd.Build(i, v[i]);
else
FenwEven.Build(i, v[i]);
}

while(q--)
{
int t, x, y;
scanf("%d%d%d", &t, &x, &y);
if(t == 1)
{
if(x & 1)
FenwOdd.Update(x, y);
else
FenwEven.Update(x, y);
}
else
{
if((x - y + 1) & 1)
{
if(y & 1)
printf("%d\n", FenwOdd.Query(y) ˆ FenwOdd.Query(x-2));
else
printf("%d\n", FenwEven.Query(y) ˆ FenwEven.Query(x-2));
}
else
printf("0\n");
}
}

return 0;
}

Listing 8.2.3: 229737-XORanges.cpp


// https://oj.uz/submission/229737
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 247

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;

#define SZ(x) (int)(x.size())


#define F0(i,n) for(int i=0;i<n;i++)
#define F1(i,n) for(int i=1;i<=n;i++)
#define CL(a,x) memset(x, a, sizeof(x));
#define PR(x) cerr << #x << "=" << (x) << endl;

const int inf = 1000000009;


const double pi = atan(1.0)*4.0;
const double eps = 1e-8;
const int N = 200001;

int i, j, k, m, n;
int a[N];

int h[2][N];

void add(int ind, int x, int val)


{
while (x <= n/2+1)
{
h[ind][x] ˆ= val;
x = (x|(x-1)) + 1;
}
}

int get(int ind, int x)


{
int ret = 0;
while (x)
{
ret ˆ= h[ind][x];
x &= (x-1);
}
return ret;
}

int main()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

int q, t;
cin >> n >> q;
F1(i, n)
{
scanf("%d", &a[i]);
add(i % 2, (i + 1) / 2, a[i]);
}

while (q--)
{
scanf("%d%d%d", &t, &i, &j);
if (t == 1)
{
add(i % 2, (i + 1) / 2, a[i]);
a[i] = j;
add(i % 2, (i + 1) / 2, a[i]);
}
else
{
int len = j - i + 1;
int ret = 0;
if (len % 2)
{
ret = get(j % 2, (j + 1) / 2) ˆ get(i % 2, (i - 1) / 2);
}
printf("%d\n", ret);
}
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 248

return 0;
}

Listing 8.2.4: 236349-XORanges.cpp


// https://oj.uz/submission/236349

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

#define F first
#define S second
//#define endl ’\n’
#define fastio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define maxp 22
#define mod (int)1e9+7
#define N (int)1e5*2+1

struct ano
{
int even;
int odd;
};

int arr[N];
ano seg[4*N];

ano join(ano a, ano b, bool even)


{
ano res;
if(even)
{
res.even=a.evenˆb.even;
res.odd=a.oddˆb.odd;
}
else
{
res.even=a.evenˆb.odd;
res.odd=a.oddˆb.even;
}

return res;
}

void build(int s, int e, int idx)


{
if(s==e)
{
seg[idx].odd=arr[s];
// cout<<"leaf "<<s<<" "<<e<<" "<<seg[idx].odd<<endl;
return;
}

int mid=(s+e)/2;
build(s,mid,idx*2);
build(mid+1,e,idx*2+1);

bool even=true;
if((mid-s+1)%2)
even=false;
seg[idx]=join(seg[idx*2],seg[idx*2+1],even);
// cout<<"up "<<s<<" "<<e<<" "<<seg[idx].odd<<" "<<seg[idx].even<<endl;
}

void update(int s,int e, int idx, int k, int u)


{
if(s==e && s==k)
{
seg[idx].odd=u;
return;
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 249

if(s>k || e<k)
return;

int mid=(s+e)/2;
update(s,mid,idx*2,k,u);
update(mid+1,e,idx*2+1,k,u);

bool even=true;
if((mid-s+1)%2)
even=false;
seg[idx]=join(seg[idx*2],seg[idx*2+1],even);
}

ano query(int s, int e, int idx, int qs, int qe)


{
if(qs<=s && e<=qe)
{
//cout<<"init "<<s<<" "<<e<<" "<<seg[idx].odd<<" "<<seg[idx].even<<endl;
return seg[idx];
}

if(s>qe || e<qs)
return {0,-1};

int mid=(s+e)/2;
ano p1,p2;
p1=query(s,mid,idx*2,qs,qe);
p2=query(mid+1,e,idx*2+1,qs,qe);

// cout<<s<<" "<<e<<endl;
// cout<<p1.odd<<" "<<p1.even<<" "<<p2.odd<<" "<<p2.even<<endl;

if(p1.odd==-1)
return p2;

if(p2.odd==-1)
return p1;

bool even=true;
if((min(mid,qe)-max(s,qs)+1)%2)
even=false;
return join(p1,p2,even);
}

int main ()
{
//fastio;

std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994


std::freopen("xoranges.out", "w", stdout);

int n,q;
cin>>n>>q;

int i;
for(i=1;i<=n;i++)
cin>>arr[i];

build(1,n,1);
while(q--)
{
int task,x,y;
cin>>task>>x>>y;
if(task==1)
update(1,n,1,x,y);
else
if((x-y+1)%2)
cout<<query(1,n,1,x,y).odd<<endl;
else
cout<<0<<endl;
}
}

Listing 8.2.5: 237576-XORanges.cpp


CAPITOLUL 8. EJOI 2019 8.2. XORANGES 250

// https://oj.uz/submission/237576

#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <cmath>

using namespace std;

typedef vector<int> vi;


typedef pair<int,int> pi;
typedef long long ll;

#define loop(i,a,b) for (int i = a; i <= b; i++)

#define INF ((unsigned) ˜0)


#define F first
#define S second
#define PB push_back
#define MP make_pair

const int MXN = 200000;


int n, q;
int st[2*MXN];

int gi(int i)
{
if(i % 2 == 0)
return i / 2;
return (n + i) / 2;
}

int rxq(int l, int r)


{
int res = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1)
{
if (l&1) res ˆ= st[l++];
if (r&1) res ˆ= st[--r];
}

return res;
}

int main()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

cin >> n >> q;


for(int i = 0; i < n; i++)
cin >> st[gi(i) + n];

for(int i = n - 1; i > 1; i--)


st[i] = st[i<<1] ˆ st[i<<1|1];

while(q--)
{
int tp, a, b;
cin >> tp >> a >> b;

a--;
if(tp == 1)
{
a = gi(a) + n;
st[a] = b;
while(a >= 1)
{
a = a >> 1;
st[a] = st[a<<1] ˆ st[a<<1|1];
}
}
else
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 251

{
b--;
if(a % 2 == b % 2)
{
cout << rxq(gi(a), gi(b) + 1) << "\n";
}
else
{
cout << "0\n";
}
}
}
}

Listing 8.2.6: 237584-XORanges.cpp


// https://oj.uz/submission/237584

#include <iostream>

using namespace std;

typedef unsigned int ui;

int n, q;
ui oranges[200005];
ui st_par[400005];
ui st_impar[400005];

void update_par(int index)


{
st_par[n + index] = oranges[index];

for (int i = n + index; i > 1; i >>= 1)


st_par[i >> 1] = st_par[i] ˆ st_par[i ˆ 1];
}

void update_impar(int index)


{
st_impar[n + index] = oranges[index];

for (int i = n + index; i > 1; i >>= 1)


st_impar[i >> 1] = st_impar[i] ˆ st_impar[i ˆ 1];
}

ui query_par(int l, int r)
{
ui res = 0;

for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1)


{
if (l & 1)
res ˆ= st_par[l++];

if (r & 1)
res ˆ= st_par[--r];
}

return res;
}

ui query_impar(int l, int r)
{
ui res = 0;

for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1)


{
if (l & 1)
res ˆ= st_impar[l++];

if (r & 1)
res ˆ= st_impar[--r];
}

return res;
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 252

int main()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

cin >> n >> q;

for (int i = 0; i < n; i++)


cin >> oranges[i];

for (int i = 0; i < n; i++)


st_par[n + i] = (i % 2 == 0 ? oranges[i] : 0);

for (int i = n - 1; i > 0; i--)


st_par[i] = st_par[i << 1] ˆ st_par[i << 1 | 1];

for (int i = 0; i < n; i++)


st_impar[n + i] = (i % 2 == 0 ? 0 : oranges[i]);

for (int i = n - 1; i > 0; i--)


st_impar[i] = st_impar[i << 1] ˆ st_impar[i << 1 | 1];

while (q--)
{
int aux;
cin >> aux;

if (aux == 1)
{
//rescan
int index;
cin >> index;
cin >> oranges[index - 1];

if ((index - 1) % 2 == 0)
update_par(index - 1);
else
update_impar(index - 1);
}
else
{
//query
int l, u;
cin >> l >> u;

if ((u - l + 1) % 2 == 0)
cout << 0 << ’\n’;
else
if ((l - 1) % 2 == 0)
cout << query_par(l - 1, u - 1) << ’\n’;
else
cout << query_impar(l - 1, u - 1) << ’\n’;
}
}

cout.flush();

return 0;
}

Listing 8.2.7: 242110-XORanges.cpp


// https://oj.uz/submission/242110

#include<bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2e5 + 7;

int t[2][N];
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 253

void upd(int ind, int val)


{
for(int i = ind + 1; i < N; i += i & -i)
{
t[ind & 1][i] ˆ= val;
}
}

int get(int par, int ind)


{
int res = 0;
for(; ind > 0; ind -= ind & -ind)
{
res ˆ= t[par][ind];
}
return res;
}

int get(int par, int l, int r)


{
return get(par, r) ˆ get(par, l);
}

int n, q, a[N];

signed main()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> q;

for(int i = 0; i < n; ++i)


{
cin >> a[i];
upd(i, a[i]);
}

while(q--)
{
int type;
cin >> type;

if(type == 1)
{
int ind, val;
cin >> ind >> val;
ind--;
upd(ind, a[ind] ˆ val);
a[ind] = val;
}

if(type == 2)
{
int l, r;
cin >> l >> r;
l--;
r--;
int ans = 0;
if((l & 1) & (r & 1))
{
ans ˆ= get(1, l, r + 1);
}

if(((l & 1) ˆ 1) & ((r & 1) ˆ 1))


{
ans ˆ= get(0, l, r + 1);
}

cout << ans << ’\n’;


}
}
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 254

return 0;
}

Listing 8.2.8: 246138-XORanges.cpp


// https://oj.uz/submission/246138

#include <bits/stdc++.h>

#define endl "\n"


#define int long long
#define see(x) cerr<<#x<<"="<<x<<endl;

using namespace std;

const int N = 2*1e5 + 10;

int n, q, a[N], t1[4*N], t2[4*N];

void build (int x, int l, int r)


{
if (l == r)
{
if (l % 2)
t1[x] = a[l];
else
t2[x] = a[l];
return;
}

int m = (l + r) / 2;
build(2*x+1, l, m);
build(2*x+2, m+1, r);
t1[x] = t1[2*x+1] ˆ t1[2*x+2];
t2[x] = t2[2*x+1] ˆ t2[2*x+2];
}

void update (int x, int l, int r, int i, int v)


{
if (l == r)
{
if (i % 2)
t1[x] = v;
else
t2[x] = v;
return;
}

int m = (l + r) / 2;
if (i > m)
update(2*x+2, m+1, r, i, v);
else
update(2*x+1, l, m, i, v);

t1[x] = t1[2*x+1] ˆ t1[2*x+2];


t2[x] = t2[2*x+1] ˆ t2[2*x+2];
}

int get (int x, int lx, int rx, int l, int r, int type)
{
if (lx > r or rx < l) return 0;
if (lx >= l and rx <= r)
{
if (type == 1)
return t1[x];
else
return t2[x];
}

int m = (lx + rx) / 2;


int g1 = get(2*x+1, lx, m, l, r, type);
int g2 = get(2*x+2, m+1, rx, l, r, type);

return g1 ˆ g2;
}
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 255

int32_t main ()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);

cin >> n >> q;

for (int i = 0; i < n; ++ i)


cin >> a[i];

build(0, 0, n-1);

while (q --)
{
int t, i, j;
cin >> t >> i >> j;

if (t == 1)
update(0, 0, n-1, i-1, j);
else
if ((j - i) % 2)
cout << "0\n";
else
if (i % 2)
cout << get(0, 0, n-1, i-1, j-1, 2) << endl;
else
cout << get(0, 0, n-1, i-1, j-1, 1) << endl;
}

return 0;
}

Listing 8.2.9: 246793-XORanges.cpp


// https://oj.uz/submission/246793

#include <bits/stdc++.h> //Andrei Alexandru a.k.a Sho10

#define ll long long int


#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#define all(a) (a).begin(), (a).end()
#define sz size
#define f first
#define s second
#define pb push_back
#define er erase
#define in insert
#define mp make_pair
#define pi pair
#define rc(s) return cout<<s,0
#define endl ’\n’
#define mod 1000000007
#define PI 3.14159265359
#define MAXN 100005
#define INF 1000000005
#define LINF 1000000000000000005
#define CODE_START ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);

using namespace std;

ll n,q,a[200005],bit[3][200005];

void build(ll s1,ll pos,ll val)


{
for(;pos<=n;pos+=pos&(-pos))
{
bit[s1][pos]ˆ=val;
}
}
CAPITOLUL 8. EJOI 2019 8.2. XORANGES 256

void update(ll s1,ll pos,ll val)


{
ll m=pos;
for(;pos<=2*n;pos+=pos&(-pos))
{
bit[s1][pos]=bit[s1][pos]ˆa[m]ˆval;
}

a[m]=val;
}

ll query(ll s1,ll pos)


{
ll res=0;
if(pos<=0)
{
return 0;
}

for(;pos;pos-=pos&(-pos))
{
resˆ=bit[s1][pos];
}

return res;
}

int32_t main()
{
std::freopen("../xoranges-tests/input.s5t5", "r", stdin); // 751564994
std::freopen("xoranges.out", "w", stdout);

CODE_START;
cin>>n>>q;

for(ll i=1;i<=n;i++)
{
bit[1][i]=0;
bit[0][i]=0;
}

//cout<<"DA"<<endl;
for(ll i=1;i<=n;i++)
{
cin>>a[i];
if(i%2==0)
{
build(1,i,a[i]);
}
else
{
build(0,i,a[i]);
}
}
//cout<<"DA"<<endl;

while(q--)
{
ll t,x,y;
cin>>t>>x>>y;
//cout<<"DA"<<endl;

if(t==1)
{
if(x%2==0)
{
update(1,x,y);
}
else
update(0,x,y);
}
else
{
if((x-y+1)%2==0)
{
cout<<0<<endl;
CAPITOLUL 8. EJOI 2019 8.3. T - COVERING 257

}
else
{
if(y%2==1)
{
cout<<(query(0,y)ˆquery(0,x-2))<<endl;
}
else
cout<<(query(1,y)ˆquery(1,x-2))<<endl;
}
}
}
}

Listing 8.2.10: checkerXORanges.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../xoranges-tests/input.s5t5",
(char*)"../xoranges-tests/output.s5t5",
(char*)"xoranges.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("xoranges", argc, argv);


compareRemainingLines();
}

119
OBS: #include ”testlib.h”

8.2.3 *Rezolvare detaliată

8.3 T - Covering
Problema 3 - T - Covering 100 de puncte
If you have ever played Tetris, you might know that one of the figures looks as follows:

We will call this figure a T-tetromino; a tetromino is just a fancy word for a connected geometric
figure composed of four cells. The cell marked with  will be called the center cell.
Manca draws a rectangular grid with m rows and n columns and writes a number into each
cell. The rows of the table are numbered from 0 to m  1 and the columns are numbered from 0
to n  1.
119
https://github.com/MikeMirzayanov/testlib
CAPITOLUL 8. EJOI 2019 8.3. T - COVERING 258

She also marks some cells as special, e.g., by painting them red. After that, she asks Nika, a
friend of hers, to place T-tetrominoes on the grid in such a way that the following conditions are
met:

ˆ The number of T-tetrominoes has to be the same as the number of special cells. For each
Ttetromino, its center cell has to lie on some special cell.
ˆ No pair of T-tetrominoes may overlap.
ˆ All T-tetrominoes have to completely lie on the grid.

ãáà
Note that there are four possible orientations of each T-tetromino ( , , , and ). â
If the conditions cannot be satisfied, Nika should answer N o; if they can, she has to find such a
placement of T-tetrominoes that the sum of the numbers in the cells covered by the T-tetrominoes
is maximum possible. In this case, she has to tell Manca the maximum sum.
Write a program to help Nika solve the riddle.
Input
Each line contains a sequence of integers separated by a single space.
The first line of the input contains the integers m and n. Each of the following m lines contains
n integers from the interval 0, 1000. The j-th integer in the i-th line represents the number
written in the j-th cell of the i-th row of the grid. The next line contains an integer k " r1, ..., mnx.
This line is followed by k more lines, each of which consists of integers ri " r0, ..., m  1x and
ci " r0, ..., n  1x, which represent the position (the row index and column index, respectively) of
the i-th special cell. The list of special cells does not contain any duplicates.
Output
Print the maximum possible sum of the numbers in the cells covered by the T-tetrominoes, or
No if no valid placement of T-tetrominoes exists.
Constraints

1 & mn & 10
6
ˆ

Subtasks

ˆ 5 points: k & 1000; for each pair of distinct special cells i and j, we have ¶ri  rj ¶ % 2 or
¶ci  cj ¶ % 2.

ˆ 10 points: k & 1000; for each pair of distinct special cells i and j, it holds that if ¶ri  rj ¶ & 2
and ¶ri  rj ¶ & 2, then and are adjacent by side, or more formally the following statement is
true (¶ri  rj ¶ 1 and ¶ci  cj ¶ 0) or (¶ri  rj ¶ 0 and ¶ci  cj ¶ 1).
ˆ 10 points: k & 1000; for each pair of distinct special cells i and j, it holds that if ¶ri  rj ¶ & 2
and ¶ci  cj ¶ & 2 then ¶ri  rj ¶ & 1 and ¶ci  cj ¶ & 1.
ˆ 10 points: k & 1000; all special cells lie in the same row.
ˆ 15 points: k & 10.
ˆ 20 points: k & 1000.
ˆ 30 points: no additional constraints.

Example 1
Input

5 6
7 3 8 1 0 9
4 6 2 5 8 3
1 9 7 3 9 5
2 6 8 4 5 7
3 8 2 7 3 6
CAPITOLUL 8. EJOI 2019 8.3. T - COVERING 259

31
1
2 2
3 4

Output

67

Comment
To achieve the maximum sum, Nika may place the tetrominoes as follows:

ˆ â on the cell (1, 1);


ˆ à on the cell (2, 2);
ˆ á on the cell (3, 4).
Example 2
Input

5 6
7 3 8 1 0 9
4 6 2 5 8 3
1 9 7 3 9 5
2 6 8 4 5 7
3 8 2 7 3 6
31
1
2 2
3 3

Output

No

Timp maxim de executare/test: 1.0 secunde


Memorie: total 100 MB

8.3.1 Indicaţii de rezolvare

Let’s look on some small examples first. If we have one special cell, and we can rotate the tetramino
in all 4 directions. Then the answer is sum all neighbouring cells except the cell with minimal
value.
If two special cells share a side, then there is unique way to rotate the tetraminos. If two special
cells share a corner, then there are two ways to rotate the tetraminos, but the set of covered cells
is the same.
If two special cells are 1 cell away from each other, then we have 7 neighbour cells, and we need
to cover 6 of them. It’s easy to see that we can cover any 6 of them, so if we need to maximize
sum, we need to cover all except the minimal one.
Now let’s look on the full task. Let’s build a graph. The vertices are the special cells, and
two special cells are connected if they share side, or corner, or if they are 1 cell away. Let’s find
connected components in this graph. If connected component contains k special cells, then there
are no more than 4n   n  1 3n  1 neighbour cells, that can be covered by the tetraminos,
and we need to cover 3n of them. So ow we have three cases:
1. If this number is less than 3n than it is obviously impossible to put all tetraminos.
CAPITOLUL 8. EJOI 2019 8.3. T - COVERING 260

2. If this number is 3n than it can be shown that it is always possible to place all tetraminos.
So we can just add their sum to the answer.
3. If this number is 3n + 1 than it can be shown that for each neighbour cell it is always
possible to place all tetraminos in such a way that all cells except selected one are covered.
So we can just add the sum of all elements except the minimal one to the answer.

Total time complexity of this solution is O mn.

8.3.2 Coduri sursă

Listing 8.3.1: 229752-covering.cpp


// https://oj.uz/submission/229752
// https://oj.uz/problems/source/489

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;

#define SZ(x) (int)(x.size())


#define F0(i,n) for(int i=0;i<n;i++)
#define F1(i,n) for(int i=1;i<=n;i++)
#define CL(a,x) memset(x, a, sizeof(x));
#define PR(x) cerr << #x << "=" << (x) << endl;

const int MOD = 1000000007;


const double pi = atan(1.0)*4.0;
const double eps = 1e-8;
const int N = 1000001;

int i, j, k, m, n;
int a[N];

int b[N], d[N], c[N];


vector<pii> v;
int clbest, clsum, cnt;
ll ans;

const int DX[]={-1,0,1,0,0};


const int DY[]={0,1,0,-1,0};

void nosol()
{
cout << "No" << endl;
exit(0);
}

bool Empty(int x, int y)


{
if (x < 0 || x >= m || y < 0 || y >= n) return 0;
return !c[x*n + y];
}

int GetA(int x, int y)


{
return a[x*n + y];
}

void Set(int x, int y, int val)


{
c[x*n + y] = val;
}

void Fill(int x, int y)


{
Set(x, y, 1);
ans += GetA(x, y);
CAPITOLUL 8. EJOI 2019 8.3. T - COVERING 261

void DFS(int x, int y)


{
F0(k, 4)
if (Empty(x + DX[k], y + DY[k]))
{
cnt++;
if (clbest == -1 || clbest > GetA(x + DX[k], y + DY[k]))
{
clbest = GetA(x + DX[k], y + DY[k]);
}

Fill(x + DX[k], y + DY[k]);


}

F0(k, 4)
{
int x2 = x + 2 * DX[k], y2 = y + 2 * DY[k];
if (Empty(x2, y2) && b[x2*n + y2])
{
Fill(x2, y2);
v.push_back(pii(x2, y2));
DFS(x2, y2);
}
}
}

int main()
{
std::freopen("../covering-tests/input.test_07_05", "r", stdin);// 100520160
//std::freopen("covering.out", "w", stdout);

cin >> m >> n;


F0(i, m * n) scanf("%d", &a[i]);

cin >> k;
while (k--)
{
scanf("%d%d", &i, &j);
b[i*n+j] = 1;
}

// find pairs
F0(i, m) F0(j, n)
if (b[i*n + j])
{
for (int dx = -1; dx <= 1; dx++)
{
for (int dy = -1; dy <= 1; dy++)
if (dx || dy)
{
int i2 = i + dx, j2 = j + dy;
if (i2 < 0 || i2 >= m || j2 < 0 || j2 >= n || !b[i2*n + j2])
continue;
if (d[i*n + j] && d[i2*n + j2]) continue;
if (d[i*n + j] || d[i2*n + j2]) nosol();

d[i*n + j] = 1;
d[i2*n + j2] = 1;

F0(k, 5)
if (!Empty(i + DX[k], j + DY[k]))
nosol();

F0(k, 5)
if (!Empty(i2 + DX[k], j2 + DY[k]))
nosol();

F0(k, 5)
if (Empty(i + DX[k], j + DY[k]))
Fill(i + DX[k], j + DY[k]);

F0(k, 5)
if (Empty(i2 + DX[k], j2 + DY[k]))
Fill(i2 + DX[k], j2 + DY[k]);
CAPITOLUL 8. EJOI 2019 8.3. T - COVERING 262

}
}
}

F0(i, m) F0(j, n) if (b[i*n + j] && !d[i*n + j])


{
if (!Empty(i, j)) nosol();
int found = 0;

F0(k, 4)
{
int good = 1;
F0(dir, 4) if (dir != k)
{
if (!Empty(i + DX[dir], j + DY[dir]))
{
good = 0;
break;
}
}

found = 1;
break;
}

if (!found) nosol();
}

F0(i, m)
F0(j, n)
if (b[i*n + j] && Empty(i, j))
{
clbest = -1;
cnt = 0;
v.clear();
v.push_back(pii(i, j));
Fill(i, j);
DFS(i, j);

//PR(cnt);
// PR(SZ(v));

if (cnt < 3*SZ(v))


nosol();
else
if (cnt == 3*SZ(v) + 1)
ans -= clbest;
}

cout << ans << endl;

return 0;
}

Listing 8.3.2: 248792-covering.cpp


// https://oj.uz/submission/248792
// https://oj.uz/problems/source/489

#include <bits/stdc++.h>

#define endl "\n"


#define see(x) cerr<<#x<<"="<<x<<endl;

using namespace std;

const int N = 1002;

int m, n, val[N*N], ans, k, cnt;


bool spec[N*N], used[N*N];
vector <int> g[N*N], values;

void dfs(int v)
{
used[v] = true;
CAPITOLUL 8. EJOI 2019 8.3. T - COVERING 263

if (spec[v])
++ cnt;
else
values.push_back(val[v]);

for (auto i: g[v])


if (!used[i])
dfs(i);
}

int32_t main ()
{
std::freopen("../covering-tests/input.test_07_05", "r", stdin); // 100520160
//std::freopen("covering.out", "w", stdout);

ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

cin >> m >> n;

int temp = 1;
for (int i = 0; i < m; ++ i)
for (int j = 0; j < n; ++ j)
{
int a;
cin >> a;
val[temp ++] = a;
}

cin >> k;

for (int i = 0; i < k; ++ i)


{
int x, y;
cin >> x >> y;
++ x;
++ y;

int t = (x-1)*n + y,
t1 = (x-2)*n + y,
t2 = (x-1)*n + y - 1,
t3 = x*n + y,
t4 = (x-1)*n + y + 1;
ans += val[t];
spec[t] = true;
if (x > 1)
{
g[t].push_back(t1);
g[t1].push_back(t);
}
if (y > 1)
{
g[t].push_back(t2);
g[t2].push_back(t);
}
if (x < m)
{
g[t].push_back(t3);
g[t3].push_back(t);
}
if (y < n)
{
g[t].push_back(t4);
g[t4].push_back(t);
}
}

temp = 1;
for (int i = 0; i < m; ++ i)
for (int j = 0; j < n; ++ j)
{
if (!used[temp])
{
values.clear();
cnt = 0;
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 264

dfs(temp);

sort(values.rbegin(), values.rend());

if (3*cnt > values.size())


{
cout << "No\n";
return 0;
}

for (int k = 0; k < 3*cnt; ++ k)


ans += values[k];
}

++ temp;
}

cout << ans << endl;

return 0;
}

8.3.3 *Rezolvare detaliată

8.4 Adventure
Problema 4 - Awesome Arrowland Adventure 100 de puncte
European Junior Olympiad in Informatics 2542 is held in Arrowland. Arrowland is shaped
like a grid with m rows (numbered 0 through m  1) and n columns (numbered 0 through n  1),
where each cell represents a city. Let r, c denote the cell in row r and column c. The contestants
are accommodated in the cell 0, 0, and the competition hall is in the cell m  1, n  1.
A strange tourist attraction of Arrowland is that some cities have giant arrows. Even stranger,
these arrows can only be rotated clockwise by 90 degrees at a time. Each arrow initially points
to either North, East, South, or West. Because of the host country’s name, the EJOI organizers
want to make use of the arrows.
The contestants will blindly follow the arrows, regardless of their current position. From each
city, they simply move to the adjacent city pointed to by the arrow. If they enter a city with no
arrow or if they leave Arrowland, they will just stay there and will never reach the competition
hall. Since the EJOI organizers want the contestants to arrive to the hall from their accomodation
(cell 0, 0), they might have to rotate some arrows. Help them determine the minimum number
of rotations required to achieve their goal, or tell them that the contestants cannot reach the hall,
regardless of the arrows’ orientation.
Input
The first line contains two integers, m and n, denoting the number of rows and columns,
respectively. The next m lines each contain n characters denoting the initial direction of the
arrows (N - north, E - east, S - south, W - west, X - no arrow in this cell). The last character
in the last row (i.e., the character corresponding to the competition hall) is guaranteed to be X.
In the input matrix, the directions North, East, South, and West have the same meaning as
they do on a standard map. Therefore, the character N means ”upwards”, E means ”to the
right”, S means ”downwards”, and W means ”to the left”.
Output
Output the minimum number of rotations that EJOI organizers have to perform. Output 1
if their task is impossible.
Constraints

ˆ 1 & m & 500


ˆ 1 & n & 500
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 265

ˆ Each cell contains one of these characters: N , E, S, W , X.

Subtasks

ˆ 10 points: m 1; each cell contains either a character E or a character X.


ˆ 12 points: m 1.
ˆ 12 points: m n 3.
ˆ 16 points: m 2.

ˆ 50 points: no additional constraints.

Example 1
Input

3 3
EES
SSW
ESX

Output

Comment
In this case, one possible optimal solution is to change W in the cell 1, 2 to S by rotating
the arrow three times.
Example 2
Input

3 3
EES
SSW
EEX

Output

Comment
Here, the EJOI organizers don’t have to change anything for the contestants to arrive to the
competition hall.
Example 3
Input

3 4
EXES
WSNS
XNNX

Output

Comment
Rotate the arrow in the cell 0, 0 once (to obtain S), the arrow in the cell 1, 0 twice (to
obtain E), and the arrow in the cell 2, 1 once (to obtain E).
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 266

Timp maxim de executare/test: 2.0 secunde


Memorie: total 100 MB

8.4.1 Indicaţii de rezolvare

Let’s change the task in the following way. Imagine that students go from cell 0; 0 to cell
m  1; n  1, and they rotate the arrows to match the direction of their movement. It’s easy to
see that in the optimal path they should not visit the same cell more than once, so we can build
the following directed graph: vertices of the graph are the cells, and we add edge from vertex v to
vertex u of weight w, if after w rotations, the arrow in cell v is directed to cell u.
Now we can simply find the shortest path in this graph. We can use Dijkstra algorithm with
priority queue, or even breath-first search, because all weights are small integers.
Time complexity will be O mn log mn or O mn.

8.4.2 Coduri sursă

Listing 8.4.1: 218070-adventure.cpp


// https://oj.uz/submission/218070

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <cassert>
#include <string>
#include <cstring>
#include <bitset>
#include <random>
#include <chrono>

#define FOR(i, a, b) for(int i = a; i < (int) b; i++)


#define F0R(i, a) FOR (i, 0, a)
#define ROF(i, a, b) for(int i = a; i >= (int) b; i--)
#define R0F(i, a) ROF(i, a, 0)
#define GO(i, a) for (auto i : a)

#define rsz resize


#define eb emplace_back
#define pb push_back
#define sz(x) (int) x.size()
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define f first
#define s second

using namespace std;

typedef pair<int, int> pii;


typedef vector<int> vi;
typedef vector<pii> vpii;
typedef vector<vi> vvi;
typedef vector<vpii> vvpii;
typedef long long i64;
typedef vector<i64> vi64;
typedef vector<vi64> vvi64;
typedef pair<i64, i64> pi64;
typedef vector<pi64> vpi64;

/*
const int dr[] = {+1, -1, +0, +0, +1, -1, +1, -1};
const int dc[] = {+0, +0, +1, -1, +1, -1, -1, +1};
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 267

*/
const int ms[] = {+31, +29, +31, 30, +31, +30, +31, +31, +30, +31, +30, +31};

const int dr[] = {-1, +0, +1, +0};


const int dc[] = {+0, +1, +0, -1};

const char dir[] = {’N’, ’E’, ’S’, ’W’};

const int N = 5e2 + 5;


char g [N][N];
int dist [N][N];

int main ()
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);

int r, c;
cin >> r >> c;
if (r == 1 && c == 1)
{
cout << 0 << ’\n’;
return 0;
}

F0R (i, r) F0R (j, c) cin >> g[i][j];


F0R (i, r) F0R (j, c) dist[i][j] = 1e9;

dist[0][0] = 0;

priority_queue < pair<int, pii> ,


vector<pair<int, pii> >,
greater < pair<int, pii> > > pq;

pq.push({0, {0, 0}});


auto Get = [&] (char cur)
{
F0R (i, 4)
if (dir[i] == cur) return i;
return -1;
};

while (!pq.empty())
{
int cost = pq.top().f;
int linha = pq.top().s.f;
int coluna = pq.top().s.s;

pq.pop();
if (cost > dist[linha][coluna]) continue;
int cur = Get (g[linha][coluna]);
if (cur == -1) continue;
F0R (i, 4)
{
if (linha + dr[i] < 0 || coluna + dc[i] < 0 ||
linha + dr[i] >= r || coluna + dc[i] >= c) continue;

int w = 0;
while ((cur + w) % 4 != i) ++w;

if (dist[linha + dr[i]][coluna + dc[i]] > dist[linha][coluna] + w)


{
dist[linha + dr[i]][coluna + dc[i]] = dist[linha][coluna] + w;
pq.push({dist[linha + dr[i]][coluna + dc[i]],
{linha + dr[i], coluna + dc[i]}});
}
}
}

cout << (dist[r - 1][c - 1] > 1e8 ? -1 : dist[r - 1][c - 1]) << ’\n’;

return 0;
}
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 268

Listing 8.4.2: 218553-adventure.cpp


// https://oj.uz/submission/218553
/*
ID: Sho10
LANG: C++
*/
#include <bits/stdc++.h> //Andrei Alexandru a.k.a Sho10
#define ll long long int
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#define all(a) (a).begin(), (a).end()
#define sz size
#define f first
#define s second
#define pb push_back
#define er erase
#define in insert
#define mp make_pair
#define pi pair
#define rc(s) return cout<<s,0
#define endl ’\n’
#define mod 1000000007
#define PI 3.14159265359
#define CODE_START ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);

using namespace std;

ll n,m;
char c[505][505];
vector<pair<pair<ll,ll>,ll>>v[505][505];
ll viz[505][505];
char directii[]={’N’,’E’,’S’,’W’,’N’,’E’,’S’,’W’};

void edge(ll i,ll j)


{
if(c[i][j]==’X’)
{
return;
}

ll pos=0;
while(directii[pos]!=c[i][j]) ++pos;

ll cnt=-1;
for(ll t=1;t<=4;t++)
{
++cnt;
if(directii[pos]==’E’||directii[pos]==’W’)
{
if(directii[pos]==’E’&&j<m)
{
v[i][j].pb({{i,j+1},cnt});
}
else
if(directii[pos]==’W’&&j>1)
{
v[i][j].pb({{i,j-1},cnt});
}
}
else
{
if(directii[pos]==’N’&&i>1)
{
v[i][j].pb({{i-1,j},cnt});
}
else
if(directii[pos]==’S’&&i<n)
{
v[i][j].pb({{i+1,j},cnt});
}
}

pos++;
}
}
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 269

ll sol()
{
ll x=1,y=1;
viz[1][1]=1;
priority_queue<pair<int,pair<int,int>>,
vector<pair<int,pair<int,int>>>,
greater<pair<int,pair<int,int>>>> q;

for(auto it:v[1][1])
{
q.push({it.s,{it.f.f,it.f.s}});
}

while(q.size()&&(q.top().s.f!=n || q.top().s.s!=m))
{
auto it=q.top();
ll x=it.s.f;
ll y=it.s.s;
if(viz[x][y]==1)
{
q.pop();
continue;
}
q.pop();
ll val=it.f;
for(auto it1:v[x][y])
{
if(viz[it1.f.f][it1.f.s]==0)
{
q.push({val+it1.s,{it1.f.f,it1.f.s}});
}
}
viz[x][y]=1;
}

if(q.empty()==true)
{
return -1;
}

return q.top().f;
}

int32_t main()
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

CODE_START;
cin>>n>>m;
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
{
cin>>c[i][j];
edge(i,j);
}

cout<<sol();
}

Listing 8.4.3: 226630-adventure.cpp


// https://oj.uz/submission/226630

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


ll INF = 1e18+7;
typedef pair<ll,ll> ii;
#define iii pair<ii,ll>

#define f(i,a,b) for(long long i = a;i < b;i++)


CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 270

#define rf(i,a,b) for(long long i=a;i>=b;i--)

#define fastio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);

#define w(t) while(t--)


#define c(n); cin>>n;
#define p(n) cout<<n;
#define pl(n) cout<<n<<"\n";
#define ps(n); cout<<n<<" ";

#define F first
#define S second

#define pb(a) push_back(a)


#define all(x) (x).begin(), (x).end()

#define ull unsigned long long


#define vll vector<ll>
#define vii vector<ii>

#define mkp make_pair


#define ld long double
#define arrin(a,n) f(i,0,n){cin>>a[i];}
#define arrout(a,n) f(i,0,n){cout<<a[i]<<" ";}

#define prllclock cerr<<"Time : "<<1000*(ld)clock()/(ld)CLOCKS_PER_SEC<<"ms\n";

#define PI (2LL*acos(0))

vll visit,taken;
ll dist[500 * 500 + 5];
vector<vll> adj;
vii adj2LL[500 * 500 + 5];

priority_queue<ii,vector<ii>, greater<ii> > pq;

void dijkstra(ll s)
{
pq.push(ii(0,s));
dist[s] = 0;
while(!pq.empty())
{
ii f = pq.top();
pq.pop();
ll w = f.F;
ll u = f.S;
if(w > dist[u]) {continue;}

for(auto v:adj2LL[u])
{
if(dist[u] + v.S < dist[v.F])
{
dist[v.F] = dist[u] + v.S;
pq.push(ii(dist[v.F],v.F));
}
}
}
}

int main(void)
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

fastio;
ll n,m;
c(n);
c(m);
char arr[n][m];
f(i,0,n)
{
f(j,0,m)
{
c(arr[i][j]);
}
}
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 271

swap(n,m);
f(i,0,m)
{
f(j,0,n)
{
if(arr[i][j] == ’N’)
{
if(i-1 >= 0)
adj2LL[j*m+i].pb(ii(j*m+i-1,0));

if(j+1 <= n-1)


adj2LL[j*m+i].pb(ii((j+1)*m+i,1));

if(i+1 <= m-1)


adj2LL[j*m+i].pb(ii(j*m+i+1,2));

if(j-1 >= 0)
adj2LL[j*m+i].pb(ii((j-1) *m + i,3));
}
else
if(arr[i][j] == ’E’)
{
if(i-1 >= 0)
adj2LL[j*m+i].pb(ii(j*m+i-1,3));

if(j+1 <= n-1)


adj2LL[j*m+i].pb(ii((j+1)*m+i,0));

if(i+1 <= m-1)


adj2LL[j*m+i].pb(ii(j*m+i+1,1));

if(j-1 >= 0)
adj2LL[j*m+i].pb(ii((j-1) *m + i,2));
}
else
if(arr[i][j] == ’S’)
{
if(i-1 >= 0)
adj2LL[j*m+i].pb(ii(j*m+i-1,2));

if(j+1 <= n-1)


adj2LL[j*m+i].pb(ii((j+1)*m+i,3));

if(i+1 <= m-1)


adj2LL[j*m+i].pb(ii(j*m+i+1,0));

if(j-1 >= 0)
adj2LL[j*m+i].pb(ii((j-1) *m + i,1));
}
else
if(arr[i][j] == ’W’)
{
if(i-1 >= 0)
adj2LL[j*m+i].pb(ii(j*m+i-1,1));

if(j+1 <= n-1)


adj2LL[j*m+i].pb(ii((j+1)*m+i,2));

if(i+1 <= m-1)


adj2LL[j*m+i].pb(ii(j*m+i+1,3));

if(j-1 >= 0)
adj2LL[j*m+i].pb(ii((j-1) *m + i,0));
}
}
}

f(i,0,n*m+5)
{
dist[i] = INF;
}

dijkstra(0);

ll i = m-1;
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 272

ll j = n-1;
if(dist[j*m + i] >= INF)
{
pl(-1);
return 0;
}

pl(dist[j*m + i]);
}

Listing 8.4.4: 226699-adventure.cpp


// https://oj.uz/submission/226699

#include <bits/stdc++.h>

#define MAXN 507

using namespace std;

int ind[MAXN][MAXN],dist[MAXN][MAXN],dx[4]={0,1,0,-1},dy[4]={-1,0,1,0},n,m;

void dijkstra()
{
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
dist[i][j]=-1;

priority_queue<pair<int,pair<int,int>>,
vector<pair<int,pair<int,int>> >,
greater<pair<int,pair<int,int> > > > pq;

pq.push(make_pair(0,make_pair(0,0)));
while(!pq.empty())
{
int x=pq.top().second.first,
y=pq.top().second.second,
c=pq.top().first;

pq.pop();
if(x<0 || x>=m || y<0 || y>=n || dist[y][x]!=-1) continue;

dist[y][x]=c;
if(ind[y][x]==-1) continue;

for(int i=0;i<4;i++)
pq.push(make_pair(c+i,
make_pair(x+dx[(ind[y][x]+i)%4],
y+dy[(ind[y][x]+i)%4])));
}
}

int main()
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

cin>>n>>m;
for(int i=0;i<n;i++) for(int j=0;j<m;j++)
{
char c; cin>>c;
if(c==’N’) ind[i][j]=0;
if(c==’E’) ind[i][j]=1;
if(c==’S’) ind[i][j]=2;
if(c==’W’) ind[i][j]=3;
if(c==’X’) ind[i][j]=-1;
}

dijkstra();

cout<<dist[n-1][m-1];
}

\index{dijkstra!priority\_queue}
\index{priority\_queue!top()}
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 273

\index{priority\_queue!pop()}
\index{priority\_queue!make\_pair}

Listing 8.4.5: 229759-adventure.cpp


// https://oj.uz/submission/229759

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;

#define SZ(x) (int)(x.size())


#define F0(i,n) for(int i=0;i<n;i++)
#define F1(i,n) for(int i=1;i<=n;i++)
#define CL(a,x) memset(x, a, sizeof(x));
#define PR(x) cerr << #x << "=" << (x) << endl;

const int MOD = 1000000007;


const double pi = atan(1.0)*4.0;
const double eps = 1e-8;
const int N = 501;

int i, j, k, m, n;
int a[N];

string s[N];

int DX[]={-1,0,1,0};
int DY[]={0,1,0,-1};
int d[N][N];
string H="NESW";

int main()
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

cin >> m >> n;


F0(i, m) cin >> s[i];

set<pair<int, pii>> S;

CL(-1, d);
d[0][0] = 0;
S.insert(make_pair(0, pii(0, 0)));
while (!S.empty())
{
pii p = S.begin()->second;
S.erase(S.begin());

if (s[p.first][p.second] == ’X’) continue;

F0(k, 4)
{
int x = p.first + DX[k],
y = p.second + DY[k];

if (x < 0 || x >= m || y < 0 || y >= n) continue;

int d0 = H.find(s[p.first][p.second]);
int cost = (k - d0 + 4) % 4 + d[p.first][p.second];

if (d[x][y] == -1 || d[x][y] > cost)


{
d[x][y] = cost;
S.insert(make_pair(cost, pii(x, y)));
}
}
}

cout << d[m-1][n-1] << endl;


CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 274

return 0;
}

Listing 8.4.6: 232209-adventure.cpp


// https://oj.uz/submission/232209

#include <bits/stdc++.h>

#define mp(a,b) make_pair(a,b)

using namespace std;


/*
struct ano
{
int c;
int d;

bool operator < (const ano &o) const


{
return d>o.d;
}
};
*/
vector<pair<int,int> > a[250000];

priority_queue<pair<int,int> ,
vector<pair<int,int> >,
greater<pair<int,int> > > q;

int d[250000];

int main (void)


{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

int n,m;
cin>>n>>m;

int i,j;
char c;

for(i=0;i<n*m;i++)
d[i]=1000000;

for(i=0;i<n;i++)
for(j=0;j<m;j++)
{
cin>>c;

if(c==’X’)
continue;

if(c==’N’)
{
if(i-1>=0)
a[i*m+j].push_back(mp(0,(i-1)*m+j));
if(j+1<m)
a[i*m+j].push_back(mp(1,i*m+j+1));
if(i+1<n)
a[i*m+j].push_back(mp(2,(i+1)*m+j));
if(j-1>=0)
a[i*m+j].push_back(mp(3,i*m+j-1));
}
else
if(c==’E’)
{
if(i-1>=0)
a[i*m+j].push_back(mp(3,(i-1)*m+j));
if(j+1<m)
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 275

a[i*m+j].push_back(mp(0,i*m+j+1));
if(i+1<n)
a[i*m+j].push_back(mp(1,(i+1)*m+j));
if(j-1>=0)
a[i*m+j].push_back(mp(2,i*m+j-1));
}
else
if(c==’S’)
{
if(i-1>=0)
a[i*m+j].push_back(mp(2,(i-1)*m+j));
if(j+1<m)
a[i*m+j].push_back(mp(3,i*m+j+1));
if(i+1<n)
a[i*m+j].push_back(mp(0,(i+1)*m+j));
if(j-1>=0)
a[i*m+j].push_back(mp(1,i*m+j-1));
}
else
{
if(i-1>=0)
a[i*m+j].push_back(mp(1,(i-1)*m+j));
if(j+1<m)
a[i*m+j].push_back(mp(2,i*m+j+1));
if(i+1<n)
a[i*m+j].push_back(mp(3,(i+1)*m+j));
if(j-1>=0)
a[i*m+j].push_back(mp(0,i*m+j-1));
}
}

q.push({0,0});
d[0]=0;

int start,w;

while(!q.empty())
{
start=q.top().second;
w=q.top().first;
q.pop();

if(d[start]<w)
continue;

for(auto x : a[start])
if(d[x.second]>d[start]+x.first)
{
d[x.second]=d[start]+x.first;
q.push(mp(d[x.second],x.second));
}
}

if(d[n*m-1]==1000000)
cout<<-1<<’\n’;
else
cout<<d[n*m-1]<<’\n’;
}

Listing 8.4.7: 240339-adventure.cpp


// https://oj.uz/submission/240339

#include <bits/stdc++.h>

using namespace std;

typedef long long lo;


typedef pair< lo,lo > PII;
typedef pair< lo,PII > PIII;

#define fi first
#define se second
#define mp make_pair
#define int long long
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 276

#define pb push_back
#define fio() ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL)
#define FOR for(int i=1;i<=n;i++)
#define mid ((start+end)/2)
#define ort ((bas+son)/2)

const lo MAX = -1000000000000000000;


const lo MIN = 1000000000000000000;
const lo inf = 1000000000;
const lo KOK = 100000;
const lo LOG = 30;
const lo li = 500005;
const lo mod = 1000000007;

int n,m,b[li],a[li],k,flag,t,vis[505][505],d[505][505];
int cev;
string s[li];
vector<int> v;

inline void sp()


{
priority_queue<PIII> pq;
pq.push(mp(0,mp(1,0)));
while(pq.size())
{
//˜ cout<<"( *(\n"<<endl;
int para=-pq.top().fi;
int satir=pq.top().se.fi;
int sutun=pq.top().se.se;

pq.pop();

if(satir<=0 || sutun<0 || satir>n || sutun>=m)continue ;


if(vis[satir][sutun]==1)continue ;
d[satir][sutun]=para;

//˜ if(s[satir][sutun]==’X’)continue ;
vis[satir][sutun]=1;
if(s[satir][sutun]==’N’)
{
pq.push(mp(-para,mp(satir-1,sutun)));
pq.push(mp(-(para+1),mp(satir,sutun+1)));
pq.push(mp(-(para+2),mp(satir+1,sutun)));
pq.push(mp(-(para+3),mp(satir,sutun-1)));
}

if(s[satir][sutun]==’E’)
{
pq.push(mp(-para,mp(satir,sutun+1)));
pq.push(mp(-(para+1),mp(satir+1,sutun)));
pq.push(mp(-(para+3),mp(satir-1,sutun)));
pq.push(mp(-(para+2),mp(satir,sutun-1)));
}

if(s[satir][sutun]==’S’)
{
pq.push(mp(-para,mp(satir+1,sutun)));
pq.push(mp(-(para+3),mp(satir,sutun+1)));
pq.push(mp(-(para+2),mp(satir-1,sutun)));
pq.push(mp(-(para+1),mp(satir,sutun-1)));
}

if(s[satir][sutun]==’W’)
{
pq.push(mp(-para,mp(satir,sutun-1)));
pq.push(mp(-(para+2),mp(satir,sutun+1)));
pq.push(mp(-(para+3),mp(satir+1,sutun)));
pq.push(mp(-(para+1),mp(satir-1,sutun)));
}
}
}

main(void)
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 277

memset(d,-1,sizeof(d));
scanf("%lld %lld",&n,&m);
FOR
{
cin>>s[i];
}

sp();
printf("%lld\n",d[n][m-1]);
return 0;
}

Listing 8.4.8: 242112-adventure.cpp


// https://oj.uz/submission/242112

#include<bits/stdc++.h>

using namespace std;

const int N = 505, INF = 1e9 + 5;


const int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int n, m;
char a[N][N];
vector<pair<pair<int, int>, int>> g[N][N];
int dist[N][N];

inline bool check(int x, int y)


{
return x >= 0 && x < n && y >= 0 && y < m;
}

int main()
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(0);

map<char, int> mp;


mp[’N’] = 0;
mp[’E’] = 1;
mp[’S’] = 2;
mp[’W’] = 3;
cin >> n >> m;
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
cin >> a[i][j];
if(a[i][j] != ’X’)
{
for(int k = 0; k < 4; k++)
{
int dst = k - mp[a[i][j]];
if(dst < 0)
{
dst += 4;
}

int x = i + dx[k], y = j + dy[k];


if(check(x, y))
{
g[i][j].push_back({{x, y}, dst});
}
}
}

dist[i][j] = INF;
}
}
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 278

dist[0][0] = 0;

set<pair<int, pair<int, int>>> st = {{0, {0, 0}}};

while(!st.empty())
{
auto v = st.begin()->second;
st.erase(st.begin());
for(auto to : g[v.first][v.second])
{
int dst = dist[v.first][v.second] + to.second;
if(dst < dist[to.first.first][to.first.second])
{
st.erase({dist[to.first.first][to.first.second], to.first});
dist[to.first.first][to.first.second] = dst;
st.insert({dist[to.first.first][to.first.second], to.first});
}
}
}

if(dist[n - 1][m - 1] == INF)


{
cout << -1;
}
else
{
cout << dist[n - 1][m - 1];
}

return 0;
}

Listing 8.4.9: 244272-adventure.cpp


// https://oj.uz/submission/244272

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <cmath>

using namespace std;

const int INF = 2e9;


const int N = 500;

int n, m;

vector <pair <int, int>> g[5 + N * N];


bool viz[5 + N * N];
int dp[5 + N * N];

class Min_Heap
{
private:
pair <int, int> h[5 + N * N];
int sz;

void Sift_Up(int pos)


{
int parent;
parent = pos >> 1;

while(h[pos].second < h[parent].second && parent > 0)


{
swap(h[pos], h[parent]);

pos = parent;
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 279

parent = pos >> 1;


}
}

void Sift_Down(int pos)


{
int child;
child = pos << 1;

if(child + 1 <= sz && h[child].second > h[child + 1].second)


child++;

while(child <= sz && h[child].second < h[pos].second)


{
swap(h[pos], h[child]);

pos = child;
child = pos << 1;

if(child + 1 <= sz &&


h[child].second > h[child + 1].second)
child++;
}
}

public:
void Clear()
{
sz = 0;
}

void Push(pair <int, int> val)


{
h[++sz] = val;
Sift_Up(sz);
}

void Pop()
{
swap(h[1], h[sz]);
sz--;
Sift_Down(1);
}

pair <int, int> Top()


{
return h[1];
}

int Size()
{
return sz;
}
};

Min_Heap heap;

void Dijkstra(int nodStart)


{
for(int i = 1; i <= n * m; i++) dp[i] = INF;

dp[nodStart] = 0;
heap.Push(make_pair(nodStart, 0));

while(heap.Size())
{
pair <int, int> top = heap.Top();
heap.Pop();

if(viz[top.first] == false)
{
viz[top.first] = true;

for(auto to : g[top.first])
{
if(dp[to.first] > to.second + dp[top.first])
CAPITOLUL 8. EJOI 2019 8.4. ADVENTURE 280

{
dp[to.first] = to.second + dp[top.first];
heap.Push(make_pair(to.first, dp[to.first]));
}
}
}
}
}

int main()
{
std::freopen("../adventure-tests/input.test_04_03", "r", stdin);// 260
//std::freopen("adventure.out", "w", stdout);

cin >> n >> m;

for(int i = 1; i <= n; i++)


{
string s;
cin >> s;

for(int j = 0; j < s.size(); j++)


{
int idx = (i - 1) * m + j + 1;
int idxn, idxe, idxs, idxw;
idxn = idx - m;
idxs = idx + m;
idxe = idx + 1;
idxw = idx - 1;

if(s[j] == ’N’)
{
if(i > 1) g[idx].push_back(make_pair(idxn, 0));
if(j < m - 1) g[idx].push_back(make_pair(idxe, 1));
if(i < n) g[idx].push_back(make_pair(idxs, 2));
if(j > 0) g[idx].push_back(make_pair(idxw, 3));
}
else
if(s[j] == ’E’)
{
if(i > 1) g[idx].push_back(make_pair(idxn, 3));
if(j < m - 1) g[idx].push_back(make_pair(idxe, 0));
if(i < n) g[idx].push_back(make_pair(idxs, 1));
if(j > 0) g[idx].push_back(make_pair(idxw, 2));
}
else
if(s[j] == ’S’)
{
if(i > 1) g[idx].push_back(make_pair(idxn, 2));
if(j < m - 1) g[idx].push_back(make_pair(idxe, 3));
if(i < n) g[idx].push_back(make_pair(idxs, 0));
if(j > 0) g[idx].push_back(make_pair(idxw, 1));
}
else
if(s[j] == ’W’)
{
if(i > 1) g[idx].push_back(make_pair(idxn, 1));
if(j < m - 1) g[idx].push_back(make_pair(idxe, 2));
if(i < n) g[idx].push_back(make_pair(idxs, 3));
if(j > 0) g[idx].push_back(make_pair(idxw, 0));
}
}
}

Dijkstra(1);

if(dp[n * m] == INF)
cout << -1 << ’\n’;
else
cout << dp[n * m] << ’\n’;

return 0;
}
CAPITOLUL 8. EJOI 2019 8.5. TOWER 281

8.4.3 *Rezolvare detaliată

8.5 Tower
Problema 5 - Tower 100 de puncte
Farmhand Jernej gets bored in the evenings, thus he invented a simple game. He wants to
build a tower from numbers on pieces of paper. He starts with a piece of paper and writes 1 on it.
Jernej can write another number on a piece of paper and place it on top of the tower. The new
value on the top of the tower must be a valid sum of numbers on consecutive papers comprising
the tower.
Let’s say there are currently n pieces of paper comprising the tower. He makes a sum of
numbers in the tower within arbitrary positions l, u, where 1 & l & u & n and puts the sum on
top.
Jernej wants to produce towers with desired numbers on top. Help him find out the required
steps. He also asks you to minimize the number of those steps.
Input
In the first line of input, you will be given a positive integer T (number of different towers
Jernej wants to produce).
In each of the next T lines, there will be one positive integer q, the value Jernej wants to
produce at the end. All games are independent.
Output
For each integer q:
ˆ print one line with number s (0 & s & 1000) - required steps to produce the desired value.
ˆ In the next lines, there should be 2 space-separated positive integers l and u, range bounds
to produce the desired value.

Constraints
ˆ 1&T & 1000
ˆ 1&q & 1018
Subtasks

1. [1 test case - 10 points]: T & 10 and q & 10


2. [1 test case - 10 points]: T & 20 and q & 20
3. [1 test case - 10 points]: T & 100 and q & 100
4. [1 test case - 10 points]: T & 1000 and q & 104
5. [1 test case - 10 points]: T & 1000 and q & 105
6. [1 test case - 10 points]: T & 1000 and q & 106
7. [1 test case - 10 points]: T & 1000 and q & 109
8. [1 test case - 10 points]: T & 1000 and q & 1012
9. [2 test cases - 20 points]: No additional constraints

Grading
There will be 10 test cases with T towers. For each test case, points will be calculated using
the following rules:
ˆ If the solution produces the desired value with minimal number of steps for all towers, you
get 10 points for the test case,
CAPITOLUL 8. EJOI 2019 8.5. TOWER 282

ˆ otherwise, your solution will be scored as the minimum of the score of all towers, where
towers are scored as 1  minimum steps
solution steps
7 rounded up to 2 decimal places.

ˆ If the solution for one of the towers is invalid, the solution gets 0 points.

All towers will have a valid solution.


Example
Input

3
2
3
7

Output

2
1 1
1 2
3
1 1
2 2
1 3
4
1 1
1 2
2 3
1 4

Explanation
In this example T 3.
Jernej wants to find out the required steps to get r2, 3, 7x. The current tower contains only
one piece of paper with number 1.
The first desired value is 2.
In the first step, he can only make a sum of numbers from the range 1, 1, so the number on
the next piece of paper can be only 1.
st
If Jernej wants to produce number 2, he should make a sum on the range 1, 2 (pick the 1
nd
and 2 number). This will sum into 2, which is the desired result.
The second desired value is 3. To produce 3, there are multiple ways. We can also produce 3
the following way:

1 1
1 2
2 3

Timp maxim de executare/test: 2.0 secunde


Memorie: total 100 MB

8.5.1 Indicaţii de rezolvare

First, let’s try to build maximal possible number on each step. We will have tower like this: 1,
i1
1, 2, 4, 8, 16, ... . On step i the maximal number we can produce is 2 . So it’s obvious that
number x cannot be produced in less than *log2 0  1 operations. We will show later that this
lower bound is actually achievable. But first let’s discuss partial solution that make about twice
more operations.
Let’s split given number x into sum of powers of two. Now, first spend about log2 n operations
to make all required powers of two, then spend another log2 n operations to copy the required
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 283

powers of two to the end of the list, and finally make operation that calculates sum of these powers
of two.
Now let’s discuss the optimal solution. First, let’s build the following sequence
1, 1, 2, 4, 8, 16, ..., 2 , where 2 is the smallest power of two such that 2 ' x.
k k k

Now let’s try to modify this sequence. We can decrease any element of the sequence by one
by shifting the left border of the segment from 1 to 2.
If we decrease i-th element by 1, then i  1-th element will be decreased by 1, i  2-th by 2,
ki1
i  3-th by 4, and so on. So the k-th element will be decreased by 2 .
k
Now let’s split the difference d 2  x into the sum of powers of two, and decrease the
corresponding elements of the sequence to make the last elements equal to x.
For example, if x 10, then first we make sequence 1, 1, 2, 4, 8, 16. Now we need to decrease
the last number by 6 = 4+2. First, let’s decrease it by 4. This can be done by decreasing the
second number from 2 to 1: 1, 1, 1, 3, 6, 12. Now we need to decrease it by 2. This can be done
by decreasing the third number from 3 to 2: 1, 1, 1, 2, 5, 10
This solution uses exactly *log2 n0  1 operations and it’s time complexity is O log n.

8.5.2 *Coduri sursă

***

8.5.3 *Rezolvare detaliată

8.6 Colouring
Problema 6 - Colouring a rectangle 100 de puncte
Srečko would like to paint a rectangular grid having m rows (numbered from 0 to m  1) and
n columns (numbered from 0 to n  1). Initially, all cells in the grid are white. In each step, he
chooses a diagonal and paints all of its cells using his favourite colour. However, some diagonals
may be more expensive to paint than others, regardless of their length. Given the cost of painting
each of the diagonals, write a program to tell Sreko the minimum total cost of painting all cells
in the grid.
Note that cells can be painted twice.
A rectangular grid with m rows and n columns has 2m  2n  2 diagonals. For instance, if
m 4 and n 3, there are 12 diagonals:

Input
The input consists of three lines.
The first line contains the numbers m and n.
The second line contains m  n  1 numbers that specify the costs of painting the diagonals
running in the  direction. The i-th number (for i " r1, ..., m  n  1x) refers to the diagonal in
which the difference between the row index and the column index is i  n. The first number thus
refers to the diagonal that consists only of the cell 0, n  1 (row 0, column n  1), the second
number refers to the diagonal comprising the cells 0, n  2 and 1, n  1, etc. The order of the
diagonals is thus the same as in the first row of the above figure.
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 284

The third line contains m  n  1 numbers that specify the costs of painting the diagonals
running in the  direction. The i-th number (for i " r1, ..., m  n  1x) refers to the diagonal in
which the sum of the row index and the column index is i  1. The first number thus refers to the
diagonal that consists only of the cell 0, 0, the second number refers to the diagonal comprising
the cells 1, 0 and 0, 1, etc. The order of the diagonals is thus the same as in the second row of
the above figure.
Output
Output the minimum cost of painting the grid.
Constraints
1 & m & 2 10
5
ˆ

1 & n & 2 10
5
ˆ

9
ˆ The costs are integers from the interval 1, 10 .

Subtasks
ˆ 10 points: m, n & 4.
ˆ 10 points: m, n & 10.
ˆ 10 points: m, n & 20.
ˆ 20 points: m, n & 2000.
1 and n & 2 10 .
5
ˆ 10 points: m
n & 2 10 .
5
ˆ 20 points: m
ˆ 20 points: no additional constraints.

Example 1
Input

2 2
1 3 1
1 3 1

Output

Comment
In this case, the following diagonals have to be painted to minimize the total cost:

Each of the selected diagonals costs 1, so the total cost is 4.


Example 2
Input

4 3
2 3 9 3 4 3
2 3 3 1 2 4

Output

14
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 285

Comment
This time, the following diagonals cover the grid at the minimum total cost:

The costs of painting the selected diagonals are 3, 2, 3, 3, 1, and 2 (in this order).
Timp maxim de executare/test: 2.0 secunde
Memorie: total 100 MB

8.6.1 Indicaţii de rezolvare


Let’s look on some diagonal of the first ( ) type. If we don’t paint this diagonal, we are forced to

paint all diagonals of the second ( ) type, that intersect our diagonal. Notice that these diagonals
always form a continuous segment in the list of diagonals.
Now the task can be modified in the following way. There are n  m  1 elements ai (corre-
sponding to diagonals of the first type), and n  m  1 elements bj (corresponding to diagonals of
the second type). For each i we need either to take element ai , or take all elements bj for all j in
li , ri  (values li and ri can be precalculated based on n and m).
Find the minimal cost of taken elements.
This task can be solved by dynamic programming. First, let’s sort elements by li . Now let’s
say di, j  is the minimal cost to satisfy elements a0..i  1 in such a way that elements bli ..j 
are taken.
The transition is: either take the element ai, or all elements bj..ri .
2
This solution works in O n  m  time.
It can be improved to O n  m log n  m by using the segment tree.

8.6.2 Coduri sursă

Listing 8.6.1: 218172-colouring.cpp


// https://oj.uz/submission/218172 - 10 p
// // https://oj.uz/problems/source/489
/*
ID: Sho10
LANG: C++
*/
#include <bits/stdc++.h> //Andrei Alexandru a.k.a Sho10

#define ll long long int

#pragma GCC optimize("O3")


#pragma GCC optimize("Ofast")

#define all(a) (a).begin(), (a).end()


#define sz size
#define f first
#define s second
#define pb push_back
#define er erase
#define in insert
#define mp make_pair
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 286

#define pi pair
#define rc(s) return cout<<s,0
#define endl ’\n’
#define mod 1000000007
#define PI 3.14159265359
#define CODE_START ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);

using namespace std;

ll n,m,a[800005];
int32_t main()
{
std::freopen("../colouring-tests/input.70", "r", stdin);// 296294320991251
//std::freopen("colouring.out", "w", stdout);

CODE_START;
cin>>n>>m;

ll ans=0;
for(ll i=0;i<2*n+2*m-2;i++)
cin>>a[i];

for(ll i=0;i<n+m-1;i++)
{
ans=ans+min(a[i],a[2*n+2*m-2-i-1]);
}
cout<<ans<<endl;
}

Această variantă a obţinut numai 10 puncte.

Listing 8.6.2: 226737-colouring.cpp


// https://oj.uz/submission/226737 - 10 p
// https://oj.uz/problems/source/489

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


ll INF = 1e9+7;
ll MOD = 998244353;
typedef pair<ll,ll> ii;

#define iii pair<ii,ll>


#define f(i,a,b) for(long long i = a;i < b;i++)
#define rf(i,a,b) for(long long i=a;i>=b;i--)
#define fastio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define w(t) while(t--)
#define c(n); cin>>n;
#define p(n) cout<<n;
#define pl(n) cout<<n<<"\n";
#define ps(n); cout<<n<<" ";
#define F first
#define S second
#define pb(a) push_back(a)
#define all(x) (x).begin(), (x).end()
#define ull unsigned long long
#define vll vector<ll>
#define vii vector<ii>
#define mkp make_pair
#define ld long double
#define arrin(a,n) f(i,0,n){cin>>a[i];}
#define arrout(a,n) f(i,0,n){cout<<a[i]<<" ";}
#define printclock cerr<<"Time : "<<1000*(ld)clock()/(ld)CLOCKS_PER_SEC<<"ms\n";
#define PI (2*acos(0))

const long long N = 1e6+5;

vll visited,dist,taken;

vector<vll> adj;
vector<vii> adj2;

priority_queue<ii,vector<ii>, greater<ii> > pq;


CAPITOLUL 8. EJOI 2019 8.6. COLOURING 287

ll bit[N]={0};
ll siz;
ll res = 0;

ll lcm(ll a,ll b){return (a * b) / __gcd(a,b);}


ll gcd(ll a,ll b){return __gcd(a,b);}

ll power(ll a,ll b)
{
if(b == 0) return 1;
if(b == 1) return a;
ll ans = power(a,b/2) % MOD;
ans *= ans;
ans %= MOD;
if(b % 2 == 1)ans *= a;
return ans%MOD;
}

ll inverse(ll x)
{
x%=MOD;
return power(x,MOD-2);
}

void BITup(ll k, ll x)
{
while(k <= siz)
{
bit[k]+=x;
k += k & -k;
}
}

ll BITq(ll k)
{
ll s=0;
while(k>=1)
{
s+=bit[k];
k -= k &-k;
}
return s;
}

struct node{ll lazy,val;} seg[4000000];


struct point{ll x,y;};

void dfs(ll v)
{
visited[v] = 1;
for(auto x:adj[v])
{
if(!visited[x])dfs(x);
}
}

void bfs(ll s)
{
visited[s] = 1;
queue<ll>q;
q.push(s);
while(!q.empty())
{
ll u = q.front();
ps(u);
q.pop();
for(auto x:adj[u])
{
if(!visited[x])
{
visited[x] = 1;
q.push(x);
}
}
}
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 288

void dijkstra(ll s)
{
pq.push(ii(0,s));
dist[s] = 0;
while(!pq.empty())
{
ii f = pq.top();
pq.pop();
ll w = f.F;
ll u = f.S;
if(w > dist[u]) {continue;}
for(auto v:adj2[u])
{
if(dist[u] + v.S < dist[v.F])
{
dist[v.F] = dist[u] + v.S;
pq.push(ii(dist[v.F],v.F));
}
}
}
}

void prim(ll edge)


{
taken[edge] = 1;
for(auto v:adj2[edge])
{
if (taken[v.first]==0) pq.push(ii(v.second, v.first));
}
}

ll mst(ll s)
{
taken.assign(N, 0);
prim(s);
ll cost = 0;
while(!pq.empty())
{
ii front = pq.top();
pq.pop();
ll w = front.first;
ll u = front.second;
if(taken[u]==0) cost += w;
prim(u);
}
return cost;
}

void YESNO(ll a){if(!!a){pl("Yes");}else{pl("No");}}


void filesin(void){freopen("tracing.in","r",stdin);}
void filesout(void){freopen("tracing.out","w",stdout);}

///I hope I will get uprating and don’t make mistakes


///I will never stop programming
///sqrt(-1) Love C++
///Please don’t hack me
///@TheofanisOrfanou Theo830
///Training

int main(void)
{
std::freopen("../colouring-tests/input.70", "r", stdin);// 296294320991251
//std::freopen("colouring.out", "w", stdout);

fastio;
ll m,n;
c(m);
c(n);
ll siz = m+n-1;
ll arr1[siz],arr2[siz];
ll ans = 0;
arrin(arr1,siz);
arrin(arr2,siz);
f(i,0,siz)
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 289

{
ans += min(arr1[i],arr2[siz-i-1]);
}
pl(ans);
}

Această variantă a obţinut numai 10 puncte dar are un mic avantaj: conţine câteva funcţii
care merită atenţia noastră!

Listing 8.6.3: 250194-colouring.cpp


// https://oj.uz/submission/250194
// https://oj.uz/problems/source/489

#include <bits/stdc++.h>

using namespace std;

int main()
{
std::freopen("../colouring-tests/input.70", "r", stdin);// 296294320991251
//std::freopen("colouring.out", "w", stdout);

int n, m;
cin >> n >> m;

long long sum = 0ll;


for (int i = 0ll; i < n + m - 1ll; ++i)
{
long long elem;
cin >> elem;
sum += elem;
}

long long sm = sum;


if (n == 2 && m == 2 && sm == 5)
{ cout << 4 << endl; }
if (n == 4 && m == 3 && sm == 24)
{ cout << 14 << endl; }
if (n == 4 && m == 4 && sm == 3931590351)
{ cout << 2127966803 << endl; }
if (n == 4 && m == 1 && sm == 2886496324)
{ cout << 1719990957 << endl; }
if (n == 2 && m == 4 && sm == 2260694372)
{ cout << 1551214357 << endl; }
if (n == 4 && m == 3 && sm == 5999999998)
{ cout << 5999999996 << endl; }
if (n == 4 && m == 4 && sm == 7000000000)
{ cout << 6000000000 << endl; }
if (n == 4 && m == 2 && sm == 3000000002)
{ cout << 6 << endl; }
if (n == 4 && m == 4 && sm == 2000000262)
{ cout << 2000000001 << endl; }
if (n == 10 && m == 10 && sm == 8873176026)
{ cout << 7335517282 << endl; }
if (n == 10 && m == 5 && sm == 6940361783)
{ cout << 4626278143 << endl; }
if (n == 10 && m == 1 && sm == 6007130444)
{ cout << 4101610701 << endl; }
if (n == 2 && m == 10 && sm == 4020762238)
{ cout << 2538247897 << endl; }
if (n == 3 && m == 9 && sm == 10999999938)
{ cout << 10999999934 << endl; }
if (n == 10 && m == 10 && sm == 19000000000)
{ cout << 18000000000 << endl; }
if (n == 10 && m == 4 && sm == 8000000020)
{ cout << 7000000137 << endl; }
if (n == 10 && m == 10 && sm == 8000000072)
{ cout << 6000000221 << endl; }
if (n == 9 && m == 10 && sm == 6000000268)
{ cout << 6000000268 << endl; }
if (n == 20 && m == 20 && sm == 19294294812)
{ cout << 16285613950 << endl; }
if (n == 20 && m == 10 && sm == 15750020066)
{ cout << 14393485246 << endl; }
if (n == 20 && m == 2 && sm == 12182711961)
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 290

{ cout << 8517381029 << endl; }


if (n == 19 && m == 20 && sm == 18969475895)
{ cout << 16397332736 << endl; }
if (n == 19 && m == 4 && sm == 21999999857)
{ cout << 21999999857 << endl; }
if (n == 20 && m == 20 && sm == 39000000000)
{ cout << 38000000000 << endl; }
if (n == 20 && m == 20 && sm == 18000000288)
{ cout << 12000000959 << endl; }
if (n == 3 && m == 20 && sm == 12000000010)
{ cout << 7000000018 << endl; }
if (n == 5 && m == 20 && sm == 11000001625)
{ cout << 9000004231 << endl; }
if (n == 7 && m == 20 && sm == 15677300097)
{ cout << 12766284044 << endl; }
if (n == 2000 && m == 2000 && sm == 1990318952507)
{ cout << 1964696750089 << endl; }
if (n == 2000 && m == 2000 && sm == 3998999998023)
{ cout << 3997999997963 << endl; }
if (n == 2000 && m == 1999 && sm == 2006651298491)
{ cout << 1950312824231 << endl; }
if (n == 1001 && m == 2000 && sm == 1507089691591)
{ cout << 1478241342048 << endl; }
if (n == 999 && m == 2000 && sm == 2997998492127)
{ cout << 2997998488299 << endl; }
if (n == 2000 && m == 1 && sm == 999000001867)
{ cout << 490000012923 << endl; }
if (n == 2000 && m == 2 && sm == 1009999999491)
{ cout << 674000037063 << endl; }
if (n == 2000 && m == 5 && sm == 1904341678648)
{ cout << 1903482049155 << endl; }
if (n == 2000 && m == 9 && sm == 969000001039)
{ cout << 890000001274 << endl; }
if (n == 23 && m == 2000 && sm == 990133120451)
{ cout << 963626263104 << endl; }
if (n == 1999 && m == 41 && sm == 1001670357123)
{ cout << 994663415039 << endl; }
if (n == 239 && m == 1999 && sm == 1140009843074)
{ cout << 1109454980646 << endl; }
if (n == 2000 && m == 97 && sm == 1104350799872)
{ cout << 1019193802511 << endl; }
if (n == 1 && m == 200000 && sm == 100006530571022)
{ cout << 66765921164653 << endl; }
if (n == 1 && m == 200000 && sm == 100037785404388)
{ cout << 66762533048313 << endl; }
if (n == 1 && m == 200000 && sm == 99980185255812)
{ cout << 66788334817629 << endl; }
if (n == 1 && m == 200000 && sm == 199999999900032)
{ cout << 199999999849896 << endl; }
if (n == 1 && m == 200000 && sm == 200000000000000)
{ cout << 200000000000000 << endl; }
if (n == 1 && m == 199999 && sm == 100561327567469)
{ cout << 50069284353350 << endl; }
if (n == 1 && m == 1 && sm == 1000000000)
{ cout << 1000000000 << endl; }
if (n == 200000 && m == 200000 && sm == 399999000000000)
{ cout << 399998000000000 << endl; }
if (n == 199999 && m == 199999 && sm == 199935831661609)
{ cout << 199879576186022 << endl; }
if (n == 200000 && m == 200000 && sm == 199803249629420)
{ cout << 199657162642704 << endl; }
if (n == 200000 && m == 200000 && sm == 399998999399992)
{ cout << 399997999399994 << endl; }
if (n == 200000 && m == 200000 && sm == 399998999800027)
{ cout << 399997999400089 << endl; }
if (n == 200000 && m == 200000 && sm == 199799000200200)
{ cout << 199581000200417 << endl; }
if (n == 200000 && m == 200000 && sm == 199721473454555)
{ cout << 199112815264354 << endl; }
if (n == 200000 && m == 2 && sm == 100157098321271)
{ cout << 66740345261896 << endl; }
if (n == 199999 && m == 4 && sm == 99949520445557)
{ cout << 80203800120306 << endl; }
if (n == 5 && m == 200000 && sm == 99809261496735)
{ cout << 93228490508230 << endl; }
if (n == 200000 && m == 6 && sm == 99838000100167)
CAPITOLUL 8. EJOI 2019 8.6. COLOURING 291

{ cout << 85567000130283 << endl; }


if (n == 200000 && m == 17 && sm == 190005094156490)
{ cout << 189998578276705 << endl; }
if (n == 22 && m == 199999 && sm == 99896276484268)
{ cout << 98302026738615 << endl; }
if (n == 200000 && m == 123 && sm == 100213000059807)
{ cout << 99536000266947 << endl; }
if (n == 199999 && m == 239 && sm == 99983272263566)
{ cout << 99853789478207 << endl; }
if (n == 1234 && m == 200000 && sm == 100694742428393)
{ cout << 100550376125295 << endl; }
if (n == 200000 && m == 10037 && sm == 104863555792502)
{ cout << 104826887141160 << endl; }
if (n == 33331 && m == 200000 && sm == 117021509288447)
{ cout << 116723795182436 << endl; }
if (n == 199999 && m == 55555 && sm == 127763665173769)
{ cout << 127763437646171 << endl; }
if (n == 199999 && m == 200000 && sm == 199950000200048)
{ cout << 199705000200293 << endl; }
if (n == 199999 && m == 199997 && sm == 199816977665152)
{ cout << 199572345201748 << endl; }
if (n == 200000 && m == 100000 && sm == 296295308795512)
{ cout << 296294320991251 << endl; }
}

Această sursă obţine 100 de puncte dar ... este o simplă ı̂nşiruire a rezultatelor date ı̂n fişierele
de test publicate după EJOI2019!

8.6.3 *Rezolvare detaliată


Capitolul 9
120
EJOI 2018

9.1 Hills
Problema 1 - Hills 100 de puncte
Welcome to Innopolis city. Throughout the whole year, Innopolis citizens suffer from everlast-
ing city construction.
From the window in your room, you see the sequence of n hills, where i-th of them has height
ai . The Innopolis administration wants to build some houses on the hills. However, for the sake of
city appearance, a house can be only built on the hill, which is strictly higher than neighbouring
hills (if they are present). For example, if the sequence of heights is 5, 4, 6, 2, then houses could
be built on hills with heights 5 and 6 only.
The Innopolis administration has an excavator, that can decrease the height of an arbitrary hill
by one in one hour. The excavator can only work on one hill at a time. It is allowed to decrease hills
up to zero height, or even to negative values. Increasing height of any hill is impossible. The city
administration wants to build k houses, so there must be at least k hills that satisfy the condition
above. What is the minimum time required to adjust the hills to achieve the administration’s
plan?
However, the exact value of k is not yet determined, so could you please calculate answers for
all k in range 1 & k & * n2 0? Here * n2 0 denotes n divided by two, rounded up.
Input
The first line of input contains the only integer n (1 & n & 5000)—the number of the hills in
the sequence.
Second line contains n integers ai (1 & ai & 100 000)—the heights of the hills in the sequence.
Output
Print exactly * n2 0 numbers separated by spaces. The i-th printed number should be equal to
the minimum number of hours required to level hills so it becomes possible to build i houses.
Constraints

ˆ ***

Subtasks

ˆ (7 points) n 3, ai & 100


ˆ (15 points) n & 10, ai & 100
ˆ (13 points) n & 100, ai & 100
ˆ (18 points) n & 100, ai & 2000
120
aur: Alexandru Luchianov, clasa a 7-a, Liceul Internaţional de Informatică din Bucureşti,
. argint: Albert-Antoniu Greaca, clasa a 8-a, Liceul Teoretic Internaional de Informatică din Bucureşti,
. argint: Andrei Moldovan, clasa a 7-a, la Şcoala gimnazială nr. 79 din Bucureşti,
. bronz: Luca Perju Verzotti, clasa a 7-a, Colegiul Nai̧onal de Informatică ”Tudor Vianu” din Bucureşti.

292
CAPITOLUL 9. EJOI 2018 9.1. HILLS 293

ˆ (22 points) n & 500


ˆ (25 points) n & 5000

Examples
standard input standard output
5 122
11111
3 02
123
5 013
12322
Comment/Note
In the first example, to get at least one hill suitable for construction, one can decrease the
second hill by one in one hour, then the sequence of heights becomes 1, 0, 1, 1, 1 and the first hill
becomes suitable for construction.
In the first example, to get at least two or at least three suitable hills, one can decrease the
second and the fourth hills, then the sequence of heights becomes 1, 0, 1, 0, 1, and hills 1, 3, 5
become suitable for construction.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 512 MB

9.1.1 Indicaţii de rezolvare

https://codeforces.com/blog/entry/60920
https://codeforces.com/topic/61265/en10
The problem’s short statement is: ”we allowed to decrease any element and should create at
least k local maximums, count the minimum number of operations for all k”.
Notice, that any set of positions, where no positions are adjacent could be made to be local
maximums - we just need to decrease the neighbouring hills to some value.
Let’s introduce the following dynamic programming:
dp[prefix][local maxs] - the minimum cost if we analyze only given prefix, have the specified
number of local maximums (”good hills to build on”) and we make a local maximum in the last
hill of this prefix.
2 4
The dumb implementation of this leads to O n  states and O n  time - in each state we can
brute force the previous position of local maximum (n) and then calculate the cost of patching
the segment from previous local maximum to current one.
3
A more attentive look says that it is, in fact O n  solution - on the segment only first and
last elements need to be decreased (possibly first and last elements are same).
2
To get the full solution in O n  we need to optimize dp a little bit. As we noticed in the
previous paragraph, there is one extreme situation, when the first and elements are same, let’s
handle this transition by hand in O 1 for each state.
Otherwise, funny fact, the cost of the segment strictly between local maximums is the cost of
it’s left part plus it’cost of it’s right part. Seems like something we can decompose, right?
Since our goal is to update state pref ix, local now the right part is fixed constant for all
such transitions. And we need to select minimum value of
dpilocal  1  cost i, i  1 where i & pref ix  3.
This can be done by calculating a supplementary dp during the primarily dp calculation - for
example we can calculate
f pref j  mindpij   cost i, i  1 for i & pref .

9.1.2 Coduri sursă


CAPITOLUL 9. EJOI 2018 9.1. HILLS 294

Listing 9.1.1: ds n2.cpp


// 2018, Sayutin Dmitry. // execution time : 5.670 s

#include <bits/stdc++.h>

using std::cin;
using std::cout;
using std::cerr;

using std::vector;
using std::map;
using std::array;
using std::set;
using std::string;

using std::pair;
using std::make_pair;

using std::min;
using std::abs;
using std::max;

using std::unique;
using std::sort;
using std::generate;
using std::reverse;
using std::min_element;
using std::max_element;

#ifdef LOCAL
#define LASSERT(X) assert(X)
#else
#define LASSERT(X) {}
#endif

template <typename T>


T input()
{
T res;
cin >> res;
LASSERT(cin);
return res;
}

template <typename IT>


void input_seq(IT b, IT e)
{
std::generate(b, e,
input<typename std::remove_reference<decltype( *b)>::type>);
}

#define SZ(vec) int((vec).size())


#define ALL(data) data.begin(),data.end()
#define RALL(data) data.rbegin(),data.rend()
#define TYPEMAX(type) std::numeric_limits<type>::max()
#define TYPEMIN(type) std::numeric_limits<type>::min()

#define pb push_back
#define eb emplace_back

const int max_n = 5000 + 10;


// (prefix, num_local_maxs, is_pre_last_max, is_last_max)
int dp[max_n + 1][max_n + 1][2][2];

int main()
{
std::freopen("../tests/76", "r", stdin);// ...
std::freopen("hills.out", "w", stdout);

std::iostream::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

// code here.
int n = input<int>();
CAPITOLUL 9. EJOI 2018 9.1. HILLS 295

vector<int> a(n);
input_seq(ALL(a));

for (int i = 0; i <= max_n; ++i)


for (int j = 0; j <= max_n; ++j)
for (int t = 0; t <= 1; ++t)
for (int l = 0; l <= 1; ++l)
dp[i][j][t][l] = TYPEMAX(int);

dp[0][0][0][0] = 0;
auto go = [&](int a, int b, int c, int d, int f)
{
dp[a][b][c][d] = min(dp[a][b][c][d], f);
};

auto get = [&](int i)


{
return (i >= 0 and i < n ? a[i] : 0);
};

for (int pref = 0; pref != max_n; ++pref)


for (int local = 0; local <= n; ++local)
for (int prelast = 0; prelast <= 1; ++prelast)
for (int last = 0; last <= 1; ++last)
{
if (dp[pref][local][prelast][last] == TYPEMAX(int))
continue;

auto cost = [&](bool a, bool b, bool c)


{
// [pref - 2, pref - 1, pref].

int orig = get(pref - 1);


int min_val = orig;
if (a)
min_val = min(min_val, get(pref - 2) - 1);
if (c)
min_val = min(min_val, get(pref) - 1);

return orig - min_val;


};

go(pref + 1, local, last, false,


dp[pref][local][prelast][last] + cost(prelast, last, false));
if (not last)
go(pref + 1, local + 1, last, true,
dp[pref][local][prelast][last] + cost(prelast, last, true));
}

vector<int> answers((n + 1) / 2 + 1, TYPEMAX(int));


for (int pref = n; pref <= n; ++pref)
for (int local = 1; local <= n; ++local)
for (int prelast = 0; prelast <= 1; ++prelast)
for (int last = 0; last <= 1; ++last)
{
if (dp[pref][local][prelast][last] == TYPEMAX(int))
continue;

int cost = (prelast == 1 ? max(0, get(n - 1) - (get(n - 2) - 1)) : 0);


answers[local]=min(answers[local],dp[pref][local][prelast][last]+cost);
}

for (int i = 1; i <= (n + 1) / 2; ++i)


cout << answers[i] << " ";
cout << "\n";

return 0;
}

Listing 9.1.2: ds n2 other.cpp


// 2018, Sayutin Dmitry. // execution time : 3.255 s

#include <bits/stdc++.h>
CAPITOLUL 9. EJOI 2018 9.1. HILLS 296

using std::cin;
using std::cout;
using std::cerr;

using std::vector;
using std::map;
using std::array;
using std::set;
using std::string;

using std::pair;
using std::make_pair;

using std::min;
using std::abs;
using std::max;

using std::unique;
using std::sort;
using std::generate;
using std::reverse;
using std::min_element;
using std::max_element;

#ifdef LOCAL
#define LASSERT(X) assert(X)
#else
#define LASSERT(X) {}
#endif

template <typename T>


T input()
{
T res;
cin >> res;
LASSERT(cin);
return res;
}

template <typename IT>


void input_seq(IT b, IT e)
{
std::generate(b, e,
input<typename std::remove_reference<decltype( *b)>::type>);
}

#define SZ(vec) int((vec).size())


#define ALL(data) data.begin(),data.end()
#define RALL(data) data.rbegin(),data.rend()
#define TYPEMAX(type) std::numeric_limits<type>::max()
#define TYPEMIN(type) std::numeric_limits<type>::min()

#define pb push_back
#define eb emplace_back

const int max_n = 5000;

// (prefix, local)
int dp[max_n][max_n];
int f[max_n][max_n];

int main()
{
std::freopen("../tests/76", "r", stdin);// ...
std::freopen("hills.out", "w", stdout);

std::iostream::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

// code here.
int n = input<int>();

vector<int> a(n);
input_seq(ALL(a));
CAPITOLUL 9. EJOI 2018 9.1. HILLS 297

for (int i = 0; i < n; ++i)


for (int j = 0; j <= n; ++j)
f[i][j] = dp[i][j] = TYPEMAX(int);

auto get = [&](int i)


{
return (i >= n or i <= -1 ? 0 : a[i]);
};

for (int i = 0; i < n; ++i)


{
dp[i][1] = max(0, get(i - 1) - (get(i) - 1));
for (int j = 2; j <= n; ++j)
{
if (i - 2 >= 0 and dp[i - 2][j - 1] != TYPEMAX(int))
{
dp[i][j] = min(dp[i][j],
dp[i-2][j-1]+
max(0,get(i-1)-
min(get(i-2)-1, get(i)-1)));
}

if (i - 3 >= 0 and f[i - 3][j - 1] != TYPEMAX(int))


{
dp[i][j] = min(dp[i][j],
f[i-3][j-1] + max(0, get(i-1) - (get(i)-1)));
}
}

for (int j = 1; j <= n; ++j)


{
if (i != 0)
f[i][j] = f[i - 1][j];

if (dp[i][j] != TYPEMAX(int))
f[i][j]=min(f[i][j],dp[i][j]+max(0, get(i+1)-(get(i)-1)));
}
}

vector<int> answers(1 + (n + 1) / 2, TYPEMAX(int));


for (int i = 0; i != n; ++i)
for (int j = 1; j <= n; ++j)
{
if (dp[i][j] == TYPEMAX(int))
continue;

int cost = 0;

if (i != n - 1)
cost = max(0, get(i + 1) - (get(i) - 1));

answers[j] = min(answers[j], dp[i][j] + cost);


}

for (int i = 1; i <= (n + 1) / 2; ++i)


cout << answers[i] << " ";
cout << "\n";

return 0;
}

Listing 9.1.3: gr ok.cpp


// ... // execution time : 2.022 s
#include <bits/stdc++.h>

using namespace std;

const int MX = 5007;


const int INF = 1000 * 1000 * 1000 + 7;

int dp[MX][MX / 2][3];


int a[MX];
CAPITOLUL 9. EJOI 2018 9.1. HILLS 298

int cost(int a, int b)


{
if (a > b)
{
return 0;
}
return b - a + 1;
}

int main()
{
std::freopen("../tests/76", "r", stdin);// ...
std::freopen("hills.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
int m = (n + 1) / 2;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 0; i <= n + 1; i++)
{
for (int j = 0; j <= m + 1; j++)
{
for (int k = 0; k < 3; k++)
{
dp[i][j][k] = INF;
}
}
}

dp[0][0][2] = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= m; j++)
{
for (int k = 0; k < 3; k++)
{
if (dp[i][j][k] == INF) continue;
int val = a[i];
if (k == 1)
{
val = min(val, a[i - 1] - 1);
}

{
// i + 1 is not a local maximum
int gok = min(k + 1, 2);
if (k != 0)
{
dp[i+1][j][gok] = min(dp[i+1][j][gok], dp[i][j][k]);
}
else
{
dp[i+1][j][gok] = min(dp[i+1][j][gok],
dp[i][j][k] + cost(a[i],
a[i + 1]));
}
}

{
// i + 1 is a local maximum
if (k != 0)
{
dp[i+1][j+1][0] = min(dp[i+1][j+1][0],
dp[i][j][k] + cost(a[i+1],
val));
}
}
}
}
}
CAPITOLUL 9. EJOI 2018 9.1. HILLS 299

for (int i = 1; i <= m; i++)


{
int best = INF;
for (int j = 0; j <= 2; j++)
{
best = min(best, dp[n][i][j]);
}
cout << best << " ";
}
cout << "\n";
}

Listing 9.1.4: ig n2.cpp


#include <cmath> // execution time : 9.628 s
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <set>
#include <map>
#include <list>
#include <time.h>
#include <math.h>
#include <random>
#include <deque>
#include <queue>
#include <cassert>
#include <unordered_map>
#include <unordered_set>
#include <iomanip>
#include <bitset>
#include <sstream>

using namespace std;

typedef long long ll;

mt19937 rnd(228);

const int N = 5000 + 1;

int dp[N][N][2][2];
int arr[N];

int n;

int cost(int i, int j)


{
if (min(i, j) < 0 || max(i, j) >= n)
{
return 0;
}
if (arr[i] <= arr[j])
{
return arr[j] - arr[i] + 1;
}
else
{
return 0;
}
}

int main()
{
std::freopen("../tests/76", "r", stdin);// ...
std::freopen("hills.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> arr[i];
CAPITOLUL 9. EJOI 2018 9.1. HILLS 300

}
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
for (int a = 0; a < 2; a++)
{
for (int b = 0; b < 2; b++)
{
dp[i][j][a][b] = 1e9;
}
}
}
}

dp[0][0][0][0] = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
for (int a = 0; a < 2; a++)
{
for (int b = 0; b < 2; b++)
{
dp[i+1][j][b][0] = min(dp[i+1][j][b][0], dp[i][j][a][b]);
if (!b)
{
int cst = cost(i, i - 1) + cost(i, i + 1);
if (!a)
{
dp[i+1][j+1][b][1] = min(dp[i+1][j+1][b][1],
dp[i][j][a][b]+cst);
}
else
{
dp[i+1][j+1][b][1] = min(dp[i+1][j+1][b][1],
dp[i][j][a][b]+cst-min(cost(i,i-1),
cost(i-2,i-1)));
}
}
}
}
}
}

for (int k = 1; k <= (n + 1) / 2; k++)


{
int ans = 1e9;
for (int a = 0; a < 2; a++)
{
for (int b = 0; b < 2; b++)
{
ans = min(ans, dp[n][k][a][b]);
}
}
cout << ans << ’ ’;
}
cout << ’\n’;
}

Listing 9.1.5: isaf ok.cpp


//#pragma GCC optimize("O3") // execution time : 1.140 s
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef long double ld;

#define TIME (clock() * 1.0 / CLOCKS_PER_SEC)

const ld eps = 1e-6;


const ld pi = acos(-1.0);
const int two = 2;
CAPITOLUL 9. EJOI 2018 9.1. HILLS 301

const int BIG = 1e9 + 239;


const ll INF = 1e18 + 239;
const ll MOD = 998244353;
const ll MOD2 = MOD * MOD;
const int th = 3;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};
const int alf = 26;
const int dig = 10;
const int T = (1 << 19);
const int M = (5010);
const int N = (M / 2);

int n, a[M], k;
int dp[two][N][M];

int main()
{
std::freopen("../tests/76", "r", stdin);// ...
std::freopen("hills.out", "w", stdout);

ios::sync_with_stdio(0);
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
k = (n + 1) / 2;
for (int i = 0; i <= k; i++)
for (int j = 0; j <= n; j++)
dp[0][i][j] = BIG, dp[1][i][j] = BIG;

dp[0][0][0] = 0;
for (int i = 1; i <= n; i++)
for (int x = 0; x <= (i + 1) / 2; x++)
{
if (i == 1)
{
dp[0][x][i] = 0;
dp[1][x][i] = 0;
if (x == 1) dp[0][1][1] = BIG;
continue;
}

dp[0][x][i] = dp[0][x][i - 1];


if (i>1) dp[0][x][i] = min(dp[0][x][i],
dp[1][x][i-1]+max(a[i-1]+1-a[i-2],0));
if (x > 0)
{
dp[1][x][i] = dp[0][x-1][i-2]+max(a[i-2]+1-a[i-1],0);
if (i>2) dp[1][x][i]=min(dp[1][x][i],
dp[1][x-1][i-2]+
max(a[i-2]+1-min(a[i-1],a[i-3]),0));
}
else
dp[1][x][i] = BIG;
}

for (int i = 1; i <= k; i++)


cout << min(dp[0][i][n], dp[1][i][n]) << " ";
cout << ’\n’;
return 0;
}

Listing 9.1.6: 11899245-hills.cpp


// https://www.cnblogs.com/KisekiPurin2019/p/11899245.html

#include<bits/stdc++.h> // execution time : 0.703 s

using namespace std;

typedef long long ll;

const int INF = 0x3f3f3f3f;


int h[5005];
int dp[5005][2505][2];
CAPITOLUL 9. EJOI 2018 9.1. HILLS 302

void test_case()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &h[i]);

memset(dp, INF, sizeof(dp));

dp[0][0][0] = 0;
dp[1][0][0] = 0;
dp[1][1][1] = 0;
for(int i = 2; i <= n; ++i)
{
for(int j = 0, c = (i + 1) / 2; j <= c; ++j)
dp[i][j][0] = min(dp[i - 1][j][0],
dp[i - 1][j][1] + max(0, h[i] - (h[i-1]-1)));
for(int j = 1, c = (i + 1) / 2; j <= c; ++j)
dp[i][j][1] = min(dp[i-2][j-1][0] + max(0, h[i-1]-(h[i]-1)),
dp[i-2][j-1][1] + max(0, max(h[i-1]-(h[i]-1),
h[i-1]-(h[i-2]-1))));
}

for(int k = 1, c = (n + 1) / 2; k <= c; ++k)


printf("%d%c", min(dp[n][k][0], dp[n][k][1]), " \n"[k == c]);
}

int main()
{
std::freopen("../tests/76", "r", stdin);
std::freopen("hills.out", "w", stdout);
int t = 1;
for(int ti = 1; ti <= t; ++ti)
{
//printf("Case #%d: ", ti);
test_case();
}
}

Listing 9.1.7: 56159794-hills.cpp


// https://codeforces.com/contest/1013/submission/56159794
// https://codeforces.com/contest/1013/status/E

#include<bits/stdc++.h> // execution time : 0.641 s

using namespace std;

int A[5001],dp[5001][2501][2],a,i,j;

int main()
{
std::freopen("../tests/76", "r", stdin);
std::freopen("hills.out", "w", stdout);

cin>>a, memset(dp,63,sizeof dp), dp[0][0][0]=dp[1][1][1]=0;

for(i=1;i<=a;i++)
{
scanf("%d",&A[i]),dp[i][0][0]=0;

if(i!=1)
for(j=1;j<=(i+1)>>1;j++)
dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1]+max(0,A[i]-A[i-1]+1)),
dp[i][j][1]=min(dp[i-2][j-1][0]+max(0,A[i-1]-A[i]+1),
dp[i-2][j-1][1]+max(0,A[i-1]-min(A[i],A[i-2])+1));
}

for(i=1;i<=(a+1)>>1;i++)
printf("%d ",min(dp[a][i][0],dp[a][i][1]));
}

Listing 9.1.8: 81469343-hills.cpp


CAPITOLUL 9. EJOI 2018 9.1. HILLS 303

// https://codeforces.com/contest/1013/submission/81469343
// https://codeforces.com/contest/1013/status/E

#include<bits/stdc++.h> // execution time : 1.109 s

#define N 5005

using namespace std;

int n,a[N],dp[N][N/2][2];
int main()
{
std::freopen("../tests/76", "r", stdin);
std::freopen("hills.out", "w", stdout);

scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);

memset(dp,127,sizeof(dp));

dp[0][0][0]=dp[1][1][1]=dp[1][0][0]=0;
for(int i=2;i<=n;i++)
{
dp[i][0][0]=0;
for (int j=1;j<=(n+1)>>1;j++)
{
dp[i][j][0]=min(dp[i-1][j][0],
dp[i-1][j][1]+max(0,a[i]-a[i-1]+1));
dp[i][j][1]=min(dp[i-2][j-1][1]+max(0,a[i-1]-min(a[i],a[i-2])+1),
dp[i-2][j-1][0]+max(0,a[i-1]-a[i]+1));
}
}

for (int i=1;i<=(n+1)>>1;i++)


printf("%d ",min(dp[n][i][0],dp[n][i][1]));
return 0;
}

Listing 9.1.9: 88103111-hills.cpp


// https://codeforces.com/contest/1012/submission/88103111
// https://codeforces.com/contest/1012/status

#pragma GCC optimize ("O3") // execution time : 1.203 s


#pragma GCC target ("sse4")

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef long double ld;
typedef vector<int> vi;
typedef pair<int, int> pi;

#define debug(x) cerr << #x << ": " << x << endl
#define debug2(x, y) debug(x), debug(y)
#define repn(i, a, b) for(int i = (int)(a); i < (int)(b); i++)
#define rep(i, a) for(int i = 0; i < (int)(a); i++)
#define all(v) v.begin(), v.end()
#define mp make_pair
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define fi first
#define se second
#define sq(x) ((x) * (x))

const int mxN = 5005;

template<class T>
T gcd(T a, T b)
{
return ((b == 0) ? a : gcd(b, a % b));
CAPITOLUL 9. EJOI 2018 9.1. HILLS 304

int dp[mxN][mxN];
int mn[mxN];

int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);

std::freopen("../tests/76", "r", stdin);


std::freopen("hills.out", "w", stdout);

int n;
cin >> n;
vi a(n);
rep(i, n) cin >> a[i];
if(n == 1)
{
cout << 0 << endl;
return 0;
}

rep(i, mxN) rep(j, mxN) dp[i][j] = 1e9;


rep(i, mxN) mn[i] = 1e9;
mn[0] = 0;
rep(i, mxN) dp[i][0] = 0;
dp[0][1] = max(0, a[1] - a[0] + 1);
repn(i, 1, n)
{
dp[i][1]=max(0,a[i-1]-a[i]+1)+
((i < (n-1)) ? max(a[i+1]-a[i]+1,0) : 0);
if(i > 1)
{
repn(j, 2, ((n + 1) / 2) + 1)
{
int nw = min(a[i - 1], a[i - 2] - 1);
dp[i][j] = min(dp[i][j],
dp[i-2][j-1] +
((i < (n - 1)) ? max(0, a[i+1]-a[i]+1) : 0) +
max(0, nw-a[i]+1));
}
}

if(i > 1)
{
repn(j, 1, ((n + 1) / 2) + 1)
{
dp[i][j] = min(mn[j - 1] +
max(0, a[i - 1] - a[i] + 1) +
(i < (n-1) ? max(a[i+1]-a[i]+1, 0) : 0),
dp[i][j]);
}

repn(j, 1, ((n + 1) / 2) + 1)
{
if(i > 1) mn[j] = min(mn[j], dp[i - 2][j]);
}
}
}

vi ans(((n + 1) / 2) + 1, 1e9);
rep(i, n)
{
repn(j, 1, ((n + 1) / 2) + 1)
{
ans[j] = min(ans[j], dp[i][j]);
}
}

repn(i, 1, ((n + 1) / 2) + 1) cout << ans[i] << " ";


cout << endl;

return 0;
}
/*
CAPITOLUL 9. EJOI 2018 9.1. HILLS 305

Things to look out for:


- Integer overflows
- Array bounds
- Special cases
Be careful!
*/

Listing 9.1.10: Hills1.cpp


// https://kaloronahuang.com/oi/codeforces-round-500/

#include <bits/stdc++.h> // execution time : 1.203 s

using namespace std;

const int MAX_N = 5050;


int n, ai[MAX_N], dp[MAX_N][MAX_N][3];

int main()
{
std::freopen("../tests/76", "r", stdin);
std::freopen("hills.out", "w", stdout);

scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &ai[i]);

memset(dp, 0x3f, sizeof(dp));

dp[0][0][0] = 0;
for (int i = 1; i <= n; i++)
{
dp[i][0][0] = 0;
for (int j = 1; j <= (i + 1) >> 1; j++)
{
if (ai[i] > ai[i - 1])
dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][0]);
if (i != 1)
{
if (ai[i] > ai[i - 2] - 1)
dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][2]);
else
dp[i][j][1] = min(dp[i][j][1],
dp[i-1][j-1][2]+ai[i-2]-1-ai[i]+1);
}
if (ai[i] <= ai[i - 1])
dp[i][j][1] = min(dp[i][j][1],
dp[i - 1][j - 1][0] + ai[i-1]-ai[i]+1);
// choose;
dp[i][j][0] = min(dp[i][j][0],
min(dp[i-1][j][2], dp[i-1][j][0]));
if (ai[i] < ai[i - 1])
dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][1]);
if (ai[i] >= ai[i - 1])
dp[i][j][2] = min(dp[i][j][2],
min(dp[i-1][j][1],
dp[i-1][j][0]) + ai[i] - ai[i-1] + 1);
}
}

for (int i = 1; i <= (n + 1) >> 1; i++)


printf("%d ", min(dp[n][i][0], min(dp[n][i][1], dp[n][i][2])));
puts("");

return 0;
}

Listing 9.1.11: checkerHills.cpp


#include "testlib.h"

using namespace std;

int main()
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 306

//int main(int argc, char * argv[])


{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/76",
(char*)"../tests/76.a",
(char*)"hills.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("hills", argc, argv);


compareRemainingLines();
}

OBS: testlib.h https://github.com/MikeMirzayanov/testlib

9.1.3 *Rezolvare detaliată

9.2 Passports
Problema 2 - Passports 100 de puncte
Gleb is a famous competitive programming teacher from Innopolis. He is planning a trip to
N programming camps in the nearest future. Each camp will be held in a different country. For
each of them, Gleb needs to apply for a visa.
For each of these trips Gleb knows three integers: the number of the first day of the trip si ,
the length of the trip in days leni , and the number of days ti this country’s consulate will take to
process a visa application and stick a visa in a passport. Gleb has P (1 & P & 2) valid passports
and is able to decide which visa he wants to put in which passport.
For each trip, Gleb will have a flight to that country early in the morning of the day si and
will return back late in the evening of the day si  leni  1.
To apply for a visa on the day d, Gleb needs to be in Innopolis in the middle of this day. So
he can’t apply for a visa while he is on a trip, including the first and the last days. If a trip starts
the next day after the end of the other one, Gleb can’t apply for a visa between them as well. The
earliest Gleb can apply for a visa is day 1.
After applying for a visa of country i on day d, Gleb will get his passport back in the middle
of the day d  ti . Consulates use delivery services, so Gleb can get his passport back even if he
is not in Innopolis on this day. Gleb can apply for another visa on the same day he received his
passport back, if he is in Innopolis this day.
Gleb will not be able to start his trip on day si if he doesn’t has a passport with a visa for
the corresponding country in the morning of day si . In particular, the passport should not be in
another country’s consulate for visa processing.
Help Gleb to decide which visas he needs to receive in which passport, and when he should
apply for each visa.
Input
In the first line of the input there are two integers N (1 & N & 22) and P (1 & P & 2)—the
number of trips and the number of passports Gleb has, respectively.
The next N lines describe Gleb’s trips. Each line contains three positive integers si , leni , ti
(1 & si , leni , ti & 10 )—the first day of the trip, the length of the trip and number of days the
9

consulate of this country needs to process a visa application. It is guaranteed that no two trips
intersect.
Output
If it is impossible to get all visas on time, just print “NO” (quotes for clarity). Otherwise,
print “YES” and N lines describing trips. For each trip, first print number of the passport Gleb
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 307

should put this country’s visa in, and then print number of the day he should apply for it. Print
trips in the same order as they appear in the input. Days are numbered from 1, starting with
tomorrow—the first day you can apply for a visa. Passports are numbered from 1 to P .
Constraints

ˆ ***

Subtasks

ˆ N & 2, si , leni , ti & 100, ti tj , P 1 (5 points)


ˆ N & 10, si , leni , ti & 100, ti tj , P 1 (8 points)
ˆ N & 10, si , leni , ti & 100, ti tj (7 points)

ˆ N & 16, si , leni , ti & 100, P 1 (12 points)


ˆ N & 16, si , leni , ti & 100 (13 points)
ˆ N & 18, si , leni , ti & 107 , P 1 (15 points)

ˆ N & 18, si , leni , ti & 107 (15 points)


ˆ N & 20 (15 points)
ˆ N & 22 (10 points)

Examples
standard input standard output
21 YES
311 11
611 14
31 YES
13 2 2 1 10
731 11
19 3 4 12
72 YES
15 1 1 2 13
14 1 1 11
18 1 1 1 16
21 1 1 1 19
946 12
22 2 5 2 16
543 21
31 NO
731
13 2 3
19 3 4
Comment backslash Note
Examples with answer “YES” are depicted below.
Each cell of the stripe represents a single day. Rectangles represent trips, each trip starts in
the morning and ends in the evening. Rectangles with angled corners represent visa applications.
Each application starts in the middle of a day and ends ti days after. The trip and the visa
application for the same country have the same color.
In examples with two passports, visa applications and trips depicted above the time stripe are
made using the first passport, visa applications and trips depicted below the time stripe are made
using the second passport.
Example 1:
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 308

✶ ✷ ✸ ✹ ✺ ✻ ✼

'✁✂✄ ☎ ✆✝✞✟ ✠ ✡☛☞✌ ✍ ✎✏✑✒ ✓

Example 2:

✶ ✷ ✸ ✹ ✺ ✻ ✼ ✽ ✾ )✁ ✂✄ ☎✆ ✝✞ ✟✠ ✡☛ ☞✌ ✍✎ ✏✑ ✒✓ ✔✕ ✖✗ ✘✙

✚✛✜✢ ✣ ✤✥✦✧ ★ ✩✪✫✬ ✭ ✮✯✰✱ ✲ ✳✴✵✿ ❀ ❁❂❃❄ ❅

Example 3:

♠♥♦♣‘’“” ✉

❏❑▲▼ ◆ ❝❞❡❢ ❣ ❤✐❥❦ ❧ ❊❋●❍ ■ ❚❯❱❲ ❳ ❖p◗❘ ❙ ❫❴❵❛ ❜ ❨❩❬❭ ❪

✶ ✷ ✸ ✹ ✺ ✻ ✼ ✽ ✾ )✁ ✂✄ ☎✆ ✝✞ ✟✠ ✡☛ ☞✌ ✍✎ ✏✑ ✒✓ ✔✕ ✖✗ ✘✙ ✚✛ ✜✢

❅❆❇❈ ❉ ❀❁❂❃ ❄ ★✩✪✫ ✬ ✣✤✥✦ ✧ ✲✳✴✵ ✿ ✭✮✯✰ ✱


✈✇①②③④⑤⑥ ⑦

Timp maxim de executare/test: 2.0 secunde


Memorie: total 512 MB

9.2.1 Indicaţii de rezolvare

https://codeforces.com/blog/entry/60920
https://codeforces.com/topic/61265/en10
Let’s solve the P 1 case first. We’ll use dynamic programming on subsets. Let’s try to
add visas to subset in order of application. Notice that if we only have one passport, every visa
processing segment should lie between some two consecutive trips. For convenience, let’s find all
these segments beforehand.
Define dpA as the minimum day, before which all visas from set A can be acquired. Try all
possible i as a visa which Gleb will apply for next. Now we have to find minimum possible day
of application d, such that d ' dpA, segment d, d  ti  does not intersect with any trip, and
d  ti $ si . Such d can be found in O n by a linear search over precalculated free segment. Relax
the value of with d  ti . If dpr1..nx $  ™ then there is a solution that can be restored using
n 2
standard techniques. Total time complexity is O 2 n , that can be too slow for n 22.
Let’s generalize this solution for P 2. Still, Gleb can apply for a visa only when he is in
Innopolis. However, the last day of visa processing can be during some trip, but only if all trips
between the day of visa application and the day of visa acquisition use another passport.
We will slightly change the definition of dpA: now this value is equal to the minimum possible
day, by which it’s possible to finish processing all visas from set A with one passport, assuming
all trips not from A use another passport.
By this definition, calculation of DP transition is a little bit different: when trying to add
visa i we have to find minimum d, such that d ' dpA, during day d Gleb is in Innopolis, and
segment d, d  ti  does not intersect with half-closed intervals sj , sj  lenj  for all. This can be
n 2
implemented similarly to the first part in O n. Total running time is O 2 n , that can pass
system tests with some optimizations.
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 309

n
We’ll optimize the solution down to O 2 n. To do that, we process all transition from set A
in O n total time. Sort all visas in order of increasing ti . Then the optimal visa application day
d will be increasing if visa processing time ti increases. Now we can apply two pointers technique
n
to get O n total processing time for one set and O 2 n for all sets.
After calculating dpA for all subsets, we have to try all partitions of r1..nx into two sets A
and B and check if both A and B can be done with one passport each. This is equivalent to
dpA $  ™. If there are two such sets that dpA $  ™ and dpB  $  ™, then we have
found the answer, otherwise there is no answer.

9.2.2 Coduri sursă

Listing 9.2.1: passports pkun.cpp


#include <cstdio> // execution time : 12.646 s
#include <vector>
#include <algorithm>
#include <cassert>

using namespace std;

struct trip
{
int l;
int len;
int t;
int id;
};

const int INF = 1e9;

int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

int n, p;
scanf("%d%d", &n, &p);
vector<trip> trips(n);

for (int i = 0; i < n; i++)


{
scanf("%d%d%d", &trips[i].l, &trips[i].len, &trips[i].t);
trips[i].id = i;
}

vector<trip> lorder = trips, torder = trips;


sort(lorder.begin(),
lorder.end(),
[](const trip& a, const trip &b) { return a.l < b.l;});
sort(torder.begin(),
torder.end(),
[](const trip& a, const trip &b) { return a.t < b.t;});

vector<int> dp(1 << n, INF);


vector<int> pj(1 << n, -1);
vector<int> pt(1 << n, -1);
dp[0] = 1;

for (int i = 0; i < (1 << n); i++)


{
if (dp[i] == INF) continue;
int c = dp[i];
auto ptr = lorder.begin();
auto ptr_same = lorder.begin();

for (auto j : torder)


{
int to = i | (1 << j.id);
if (to == i) continue;
//printf("can %d -> %d on %d?\n", i, to, c);
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 310

while (true)
{
while (ptr != lorder.end() && ptr->l + ptr->len <= c) ptr++;
while (ptr_same != lorder.end() &&
(ptr_same->l < c || ((i & (1 << ptr_same->id)) == 0)))
ptr_same++;
if (ptr != lorder.end() && ptr->l <= c)
{
c = ptr->l + ptr->len;
//printf("need wait till %d for end of trip %d\n", /
// c, ptr->id);
continue;
}

if (ptr_same != lorder.end() && c + j.t >= ptr_same->l)


{
c = ptr_same->l + ptr->len;
//printf("need wait till %d for end of trip %d, /
// visa back is too late\n", c, ptr_same->id);
continue;
}

break;
}

//printf("can %d -> %d on %d\n", i, to, c);

if (c + j.t < j.l)


{
if (dp[to] > c + j.t)
{
dp[to] = c + j.t;
pj[to] = j.id;
pt[to] = c;
}
}
}
}

vector<int> anst(n, -1);


vector<int> ansp(n, -1);

auto restore = [&](int mask, int p)


{
while (mask)
{
int last = pj[mask];
assert(last != -1 && (mask & (1 << last)));
anst[last] = pt[mask];
ansp[last] = p;
mask ˆ= (1 << last);
}
};

if (p == 1)
{
if (dp[(1 << n) - 1] != INF)
{
restore((1 << n) - 1, 1);
}
}
else
{
for (int i = 0; i < (1 << n) - 1; i++)
{
if (dp[i] != INF && dp[(1 << n) - 1 - i] != INF)
{
restore(i, 1);
restore((1 << n) - 1 - i, 2);
break;
}
}
}

if (anst[0] == -1)
{
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 311

printf("NO\n");
}
else
{
printf("YES\n");
for (int i = 0; i < n; i++)
{
printf("%d %d\n", ansp[i], anst[i]);
}
}

return 0;
}

Listing 9.2.2: 41334650-passports.cpp


// https://codeforces.com/contest/1012/submission/41334650
// https://codeforces.com/contest/1012/status/F // execution time : 8.188 s

#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <limits>
#include <functional>

using namespace std;

const int INF = numeric_limits<int>::max();

int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(0);

int n, p;
cin >> n >> p;

vector<int> s(n);
vector<int> len(n);
vector<int> t(n);
vector<int> os(n);
vector<int> ot(n);

for (int i = 0; i < n; i++)


{
cin >> s[i] >> len[i] >> t[i];
os[i] = ot[i] = i;
}

sort(os.begin(), os.end(),
[&](const int& a, const int& b) { return s[a] < s[b];});

sort(ot.begin(), ot.end(),
[&](const int& a, const int& b) { return t[a] < t[b]; });

vector<int> dp(1 << n, INF); // dp[a]: minumum day to complete all trips
// and visas in the bitmask a
vector< pair<int, int> > save(1 << n); // table to recover the solution

dp[0] = 1;
for (int mask = 0; mask < (1 << n); mask++)
{
if (dp[mask] < INF)
{
int i = 0;
int val = dp[mask];
for (auto& j : ot)
{
if (mask & (1 << j)) { continue; }
while (i < n && val + t[j] >= s[os[i]])
{
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 312

int cur = os[i];


i++;
if (mask & (1 << cur) || val >= s[cur])
{
val = max(val, s[cur] + len[cur]);
}
}

if (val + t[j] < s[j] && dp[mask + (1 << j)] > val + t[j])
{
dp[mask + (1 << j)] = val + t[j];
save[mask + (1 << j)] = {j, val};
}
}
}
}

vector<int> which(n);
vector<int> day(n);

function<void(int, int)> assign = [&](int mask, int pa)


{
if (mask == 0) { return; }
int cur = save[mask].first;
which[cur] = pa;
day[cur] = save[mask].second;
assign(mask ˆ (1 << cur), pa);
};

if (p == 1)
{
if (dp[(1 << n) - 1] == INF)
{
cout << "NO" << endl;
}
else
{
cout << "YES" << endl;
assign((1 << n) - 1, 1);
for (int i = 0; i < n; i++)
{
cout << which[i] << ’ ’ << day[i] << endl;
}
}
}
else
{
int sec = -1;
for (int mask = 0; mask < (1 << n); mask++)
{
if (dp[mask] < INF && dp[((1 << n) - 1) ˆ mask] < INF)
{
sec = mask;
break;
}
}

if (sec == -1)
{
cout << "NO" << endl;
}
else
{
cout << "YES" << endl;
assign(sec, 1);
assign(((1 << n) - 1) ˆ sec, 2);
for (int i = 0; i < n; i++)
{
cout << which[i] << ’ ’ << day[i] << endl;
}
}
}

return 0;
}
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 313

Listing 9.2.3: 41335815-passports.cpp


// https://codeforces.com/contest/1012/submission/41335815
// https://codeforces.com/contest/1012/status/F // execution time : 6.298 s

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>

using namespace std;

#define pb push_back
#define mp make_pair

typedef pair<int,int> pii;


typedef long long ll;
typedef double ld;
typedef vector<int> vi;

#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ]; \
void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;} \
void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ]; \
void ad_de(int a,int b,int c){++M; nxt[M]=fst[a]; fst[a]=M; \
vb[M]=b; vc[M]=c;} \
void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define SZ 666666

const int inf=2.01e9;

int n,p,s[SZ],len[SZ],t[SZ],id[SZ];
int f[1<<22]; pii so[1<<22],rs[SZ];

vector<int> ve,v2;

bool by_s(int a,int b) {return s[a]<s[b];}


bool by_t(int a,int b) {return t[a]<t[b];}

void prt(int g,int t)


{
if(!g) return;
int s=so[g].se;
rs[s]=pii(t,so[g].fi);
prt(gˆ(1<<s),t);
}

int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

scanf("%d%d",&n,&p);
for(int i=0;i<n;++i)
scanf("%d%d%d",s+i,len+i,t+i),
ve.pb(i),v2.pb(i);

sort(ve.begin(),ve.end(),by_s);
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 314

sort(v2.begin(),v2.end(),by_t);

for(int i=0;i<(1<<n);++i) f[i]=inf;

f[0]=1;
for(int i=0;i<(1<<n);++i) if(f[i]!=inf)
{
int _=0,g=f[i];
for(auto t:v2) if(!(i&(1<<t)))
{
for(;_<int(ve.size());++_)
{
int j=ve[_];
if(g+::t[t]<s[j]) break;
if(i&(1<<j))
{
if(g<s[j]+len[j]) g=s[j]+len[j];
}
else
if(g>=s[j]&&g<s[j]+len[j]) g=s[j]+len[j];
}

if(g+::t[t]>=s[t]) continue;
int w=g+::t[t];
if(w>=f[iˆ(1<<t)]) continue;
f[iˆ(1<<t)]=w;
so[iˆ(1<<t)]=pii(g,t);
}
}

if(p==1)
{
if(f[(1<<n)-1]>=inf)
{
puts("NO");
return 0;
}
puts("YES");
prt((1<<n)-1,1);
for(int i=0;i<n;++i)
printf("%d %d\n",rs[i].fi,rs[i].se);
return 0;
}

int g=-1;
for(int i=0;i<(1<<n);++i)
if(f[i]<inf&&f[((1<<n)-1)ˆi]<inf) g=i;

if(g==-1)
{
puts("NO");
return 0;
}

puts("YES");
prt(g,1);
prt(((1<<n)-1)ˆg,2);

for(int i=0;i<n;++i)
printf("%d %d\n",rs[i].fi,rs[i].se);
}

Listing 9.2.4: 48114130-passports.cpp


// https://codeforces.com/contest/1012/submission/48114130
// https://codeforces.com/contest/1012/status/F // execution time : 2.938 s

#include<bits/stdc++.h>

#define inf 0x3f3f3f3f

using namespace std;

template<typename T>
inline void read(T &x)
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 315

{
x=0;
bool f=0;

char ch=getchar();
for(; ch<’0’||ch>’9’; ch=getchar())
f|=ch==’-’; // f |= (ch==’-’);

for(; ch>=’0’&&ch<=’9’; ch=getchar())


x=(x¡¡1LL)+(x¡¡3LL)+ch-’0’;

x=f?-x:x;
}

template<typename T1,typename T2>


inline void updmax(T1 &x, T2 y){ x = x>y ? x : y;}

template<typename T1,typename T2>


inline bool chkmin(T1 &x, T2 y){ return x¿y ? x=y,1 : 0; }

int n,S,p,f[1<<22];

struct node
{
int l,r,t,id;
} a[25],b[25];

bool cmp1(node A,node B) {return A.l<B.l;}


bool cmp2(node A,node B) {return A.t<B.t;}

struct From
{
int id,st;
} from[1<<22],ans[25];

void putcol(int s,int id)


{
if(s)
ans[from[s].id]=(From){id,from[s].st},
putcol(sˆ(1<<from[s].id),id);
}

void solve1()
{
if(f[S-1]==inf){ puts("NO"); return; }
puts("YES"),putcol(S-1,1);

for(int i=0;i<n;i++)
printf("%d %d\n", ans[i].id, ans[i].st);
}

void solve2()
{
int s=0;
for(;s<S;s++)
if(f[s]<inf && f[(S-1)ˆs]<inf) break;

if(s==S){puts("NO");return;}

puts("YES"), putcol(s,1), putcol((S-1)ˆs,2);

for(int i=0;i<n;i++)
printf("%d %d\n", ans[i].id, ans[i].st);
}

int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

read(n),
read(p),
S=1<<n;

for(int i=1;i<=n;i++)
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 316

{
read(a[i].l),
read(a[i].r),
read(a[i].t);

a[i].r+=a[i].l-1,
a[i].id=i-1,
b[i]=a[i];
}

sort(a+1,a+1+n,cmp1),
sort(b+1,b+1+n,cmp2);

for(int i=1;i<S;i++) f[i]=inf;

f[0]=1;
for(int s=0,i,j,T;s<S;s++)
if(f[s]<inf)
{
for(i=1,j=1,T=f[s];i<=n;i++)
if(!(s&(1<<b[i].id)))
{
for(;j<=n&&T+b[i].t>=a[j].l;j++)
{
((s&(1<<a[j].id))||T>=a[j].l) ? updmax(T,a[j].r+1) : (void)0;
}

if(T+b[i].t<b[i].l && chkmin(f[s|(1<<b[i].id)],T+b[i].t))


from[s|(1<<b[i].id)] = (From){b[i].id,T};
}
}

p==1 ? solve1() : solve2();

return 0;
}

OBS1: (x¡¡1LL)+(x¡¡3LL) este echivalent cu x*2 + x*8 = x*10 care permite ”alocarea
unui unui spaţiu pentru o cifră”!
OBS2: return x¿y ? x=y,1 : 0; returnează 1 dacă x¿y, altfel returnează 0. În plus, dacă x¿y
se face şi atribuirea x=y. (http://www.cplusplus.com/doc/tutorial/operators/
”... When the set of expressions has to be evaluated for a value, only the right-most expression is
considered.”)
OBS3: (void)0; este un fel de ”instrucţiune vidă”. (https://stackoverflow.com/ques
tions/2198950/why-is-void-0-a-no-operation-in-c-and-c ”... (void)0 (+;) is
a valid, but ’does-nothing’ C++ expression, that’s everything. It doesn’t translate to the no-op
instruction of the target architecture, it’s just an empty statement as placeholder whenever the
language expects a complete statement ...”)

Listing 9.2.5: 48115887-passports.cpp


// https://codeforces.com/contest/1012/submission/48115887
// https://codeforces.com/contest/1012/status/F // execution time : 3.554 s

#include<bits/stdc++.h>

#define FIO "B"

using namespace std;

char st;

const int N=1<<22,INF=1e9;


int n,p,f[N],up,len[N],t[N],s[N],id[2][N],g[N][2],cho[23],date[23];
char ed;

inline void chkmax(int &a,int b) {if(b>a)a=b;}


inline void chkmin(int &a,int b) {if(b<a)a=b;}

inline int cmp1(const int &a,const int &b)


CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 317

{
return s[a]<s[b];
}

inline int cmp2(const int &a,const int &b)


{
return t[a]<t[b];
}

inline void dfs(int S,int id)


{
if(!S) return;

int i=g[S][0];
cho[i]=id;
date[i]=g[S][1];
dfs(Sˆ1<<i,id);
}

int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

scanf("%d%d",&n,&p);
up=1<<n;

for(int i=0;i<n;i++)
scanf("%d%d%d",&s[i],&len[i],&t[i]),
id[0][i]=id[1][i]=i;

sort(id[0],id[0]+n,cmp1);
sort(id[1],id[1]+n,cmp2);

for(int i=0;i<up;i++) f[i]=INF;

f[0]=1;
for(int S=0; S<up; S++)
if(f[S]ˆINF)
{
int ptr=0,val=f[S];
//"val apply date (noon)
for(int k=0,i=id[1][k]; k<n; k++,i=id[1][k])
if((!(S>>i&1)))
{
while(ptr<n&&val+t[i]>=s[id[0][ptr]])
{
//"apply visa i using this passort
int j=id[0][ptr++];
if((S>>j&1)||val>=s[j])
//"need this passort for trip j
//" or at least after trip j
chkmax(val,s[j]+len[j]);
}

if(val+t[i]<min(f[S|1<<i],s[i]))
{
chkmin(f[S|1<<i],val+t[i]);
g[S|1<<i][0]=i;
g[S|1<<i][1]=val;
}
}
}

if(p==1)
{
if(f[up-1]==INF) return puts("NO");
puts("YES");
dfs(up-1,1);
for(int i=0;i<n;i++) printf("1 %d\n",date[i]);
}
else
{
int S=0;
for(int i=1;i<up;i++)
if(f[i]ˆINF&&f[up-1-i]ˆINF) {S=i;break;}
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 318

if(f[S]==INF||f[up-1-S]==INF) return puts("NO");

puts("YES");
dfs(S,1);
dfs(up-1-S,2);
for(int i=0;i<n;i++) printf("%d %d\n",cho[i],date[i]);
}

return 0;
}

Listing 9.2.6: 52626176-passports.cpp


// https://codeforces.com/contest/1012/submission/52626176
// https://codeforces.com/contest/1012/status/F // execution time : 2.748 s

#include<bits/stdc++.h>

using namespace std;

#define pw(x) (1<<(x))

const int maxN=22;


const int inf=2000000000;

int n,P;
int Srt[maxN],Len[maxN],Nt[maxN];
int Is[maxN],It[maxN];
int F[pw(maxN)+10],From[pw(maxN)+10];

pair<int,int> Ans[maxN];

bool cmps(int a,int b);


bool cmpt(int a,int b);

int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

scanf("%d%d",&n,&P);
int N=1<<n;
for (int i=1;i<N;i++) F[i]=inf;

F[0]=1;
for (int i=0;i<n;i++)
scanf("%d%d%d",&Srt[i],&Len[i],&Nt[i]),
Is[i]=It[i]=i;

sort(&Is[0],&Is[n],cmps);
sort(&It[0],&It[n],cmpt);

for (int S=0;S<N;S++)


{
if (F[S]==inf) continue;
int lip=0,limt=F[S],above=0;
//cout<<"S:"<<S<<" "<<F[S]<<endl;
for (int i=0;i<n;i++)
if (!(S&pw(It[i])))
{
//cout<<limt+Nt[It[i]]<<" "<<Srt[Is[lip]]<<endl;
while (lip<n&&limt+Nt[It[i]]>=Srt[Is[lip]])
{
//cout<<"Arr:"<<Is[lip]<<endl;
if (!(S&pw(Is[lip])))
if (limt>=Srt[Is[lip]]&&limt<Srt[Is[lip]]+Len[Is[lip]])
limt=Srt[Is[lip]]+Len[Is[lip]];
else;
else
limt=max(limt,Srt[Is[lip]]+Len[Is[lip]]);

above|=pw(Is[lip]);
++lip;
}
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 319

//cout<<"fail at:"<<(above&pw(It[i]))<<" "<< \


(limt+Nt[It[i]]>=Srt[It[i]])<<endl;
if ((above&pw(It[i]))||(limt+Nt[It[i]]>=Srt[It[i]])) continue;

if (F[S|pw(It[i])]>limt+Nt[It[i]])
F[S|pw(It[i])]=limt+Nt[It[i]],
From[S|pw(It[i])]=It[i];
}
}

if (P==1)
{
if (F[N-1]==inf) puts("NO");
else
{
int S=N-1;
while (S)
Ans[From[S]].second=F[S]-Nt[From[S]],
Sˆ=pw(From[S]);
puts("YES");
for (int i=0;i<n;i++)
printf("1 %d\n",Ans[i].second);
}
}
else
{
for (int S=0;S<N;S++)
if (F[S]!=inf&&F[Sˆ(N-1)]!=inf)
{
int T=S;
while (T)
Ans[From[T]]=make_pair(1,F[T]-Nt[From[T]]),
Tˆ=pw(From[T]);

T=Sˆ(N-1);
while (T)
Ans[From[T]]=make_pair(2,F[T]-Nt[From[T]]),
Tˆ=pw(From[T]);

puts("YES");
for (int i=0;i<n;i++)
printf("%d %d\n",Ans[i].first,Ans[i].second);

return 0;
}

puts("NO");
}

return 0;
}

bool cmps(int a,int b)


{
return Srt[a]<Srt[b];
}

bool cmpt(int a,int b)


{
return Nt[a]<Nt[b];
}

Listing 9.2.7: 56188505-passports.cpp


// https://codeforces.com/contest/1012/submission/56188505
// https://codeforces.com/contest/1012/status/F // execution time : 2.406 s

#include<bits/stdc++.h>

using namespace std;

const int inf = 0x3f3f3f3f;

struct zt
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 320

{
int ss,ii;
} frm[1<<22],ans[25];

struct node
{
int l,r,t,id;
} a[25],b[25];

bool cmp1(node aa,node bb)


{
return aa.l < bb.l;
}

bool cmp2(node aa,node bb)


{
return aa.t < bb.t;
}

int N,P,S,f[1<<22];

void dfs(int s,int ii)


{
if(!s) return;
ans[frm[s].ii] = (zt){frm[s].ss,ii};
dfs(sˆ(1<<(frm[s].ii-1) ),ii);
}

void SLV1()
{
if(f[S]==inf)
{
puts("NO"); exit(0);
}
puts("YES");
dfs(S,1);
for(int i=1;i<=N;i++)
{
printf("%d %d\n",ans[i].ii,ans[i].ss);
}
}

void SLV2()
{
int s1 = 0; int s2 = -233;
for(;s1<=S;s1++)
{
if(f[s1]<inf&&f[Sˆs1]<inf)
{
s2 = ( S ˆ s1 );
break;
}
}

if(s2==-233)
{
puts("NO"); exit(0);
}

puts("YES");
dfs(s1,1); dfs(s2,2);

for(int i=1;i<=N;i++)
{
printf("%d %d\n",ans[i].ii,ans[i].ss);
}
}

int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

scanf("%d%d",&N,&P);
for(int i=1;i<=N;i++)
{
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 321

scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].t);
a[i].id = i; a[i].r += a[i].l-1;
b[i] = a[i];
}

sort(a+1,a+1+N,cmp1);
sort(b+1,b+1+N,cmp2);

S = (1<<N)-1;
memset(f,0x3f,sizeof f);

f[0] = 1;
for(int s=0;s<S;s++)
{
if(f[s]!=inf)
{
int tim = f[s];
int pit = 1;
for(int i=1;i<=N;i++)
{
// cerr<<i<<pit<<endl;
if( (s>>(b[i].id-1))&1 ) continue;
for(;pit<=N&&tim+b[i].t>=a[pit].l;pit++)
{
if(tim>=a[pit].l||( (s>>(a[pit].id-1))&1 ))
tim = max(tim,a[pit].r+1);
}

if(tim+b[i].t<b[i].l)
{
if(f[s|(1<<(b[i].id-1))] > tim+b[i].t )
{
f[s|(1<<(b[i].id-1))] = tim + b[i].t;
frm[s|(1<<(b[i].id-1))] = (zt) {tim,b[i].id};
}
}
}
}
}

if(P==1)
SLV1();
else
SLV2();
}

Listing 9.2.8: 79747607-passports.cpp


// https://codeforces.com/contest/1012/submission/79747607
// https://codeforces.com/contest/1012/status/F // execution time : 6.533 s

#include<bits/stdc++.h>

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)x.size())
#define ALL(x) x.begin(),x.end()
#define L(i,u) for (register int i=head[u]; i; i=nxt[i])
#define rep(i,a,b) for (register int i=(a); i<=(b); i++)
#define per(i,a,b) for (register int i=(a); i>=(b); i--)

using namespace std;

typedef long double ld;


typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> Pii;
typedef vector<int> Vi;

template<class T> inline void read(T &x)


{
x=0;
char c=getchar();
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 322

int f=1;
while (!isdigit(c))
{
if (c==’-’) f=-1;
c=getchar();
}

while (isdigit(c))
{
x=x*10+c-’0’;
c=getchar();
}
x*=f;
}

template<class T> T gcd(T a, T b){return !b?a:gcd(b,a%b);}


template<class T> inline void umin(T &x, T y){x=x<y?x:y;}
template<class T> inline void umax(T &x, T y){x=x>y?x:y;}

inline ui R()
{
static ui seed=416;
return seedˆ=seed>>5, seedˆ=seed<<17, seedˆ=seed>>13;
}

const int N = 66,inf=2.05e9;

int n,tp,t[N],bg[N],f[1<<22|3],rec[1<<22|3];
Pii ans[N];

struct seg
{
int l,r,t,idx;
} a[N];

void GG(){ puts("NO"); exit(0); }

void recover(int s, int kd)


{
while(s)
{
ans[rec[s]]=mp(kd,f[s]-t[rec[s]]);
sˆ=1<<rec[s]-1;
}
}
int main()
{
std::freopen("../tests/157", "r", stdin);
std::freopen("passports.out", "w", stdout);

read(n);
read(tp);

rep(i,1,n)
read(a[i].l),
read(a[i].r),
a[i].r+=a[i].l-1,
a[i].idx=i,
read(a[i].t),
t[i]=a[i].t,
bg[i]=a[i].l;

static int id[N];

rep(i,1,n) id[i]=i;

sort(id+1,id+n+1,[](int x, int y){return t[x]<t[y];});


sort(a+1,a+n+1,[](seg x, seg y){return x.l<y.l;});

rep(i,1,n-1)
if(a[i].r>=a[i+1].l) GG();

int all=(1<<n)-1;
rep(s,0,all) f[s]=inf;

f[0]=1;
CAPITOLUL 9. EJOI 2018 9.2. PASSPORTS 323

rep(s,0,all-1)
if(f[s]<inf)
{
int x=f[s],p=1,p2=1;
static Pii seg[N];

int sz=0;
rep(i,1,n)
if(s>>a[i].idx-1&1)
seg[++sz]=mp(a[i].l,a[i].r);

rep(j,1,n)
if((˜s>>id[j]-1&1))
{
while(1)
{
int orix=x,orip=p,orip2=p2;
while(p<=sz&&seg[p].se<x) p++;
if(p<=sz&&x+t[id[j]]>=seg[p].fi) x=seg[p].se+1,p++;
while(p2<=n&&a[p2].r<x) p2++;
if(p2<=n&&x>=a[p2].l&&x<=a[p2].r) x=a[p2].r+1;
if(orix==x&&orip==p&&orip2==p2) break;
}

if(f[s|1<<id[j]-1]>x+t[id[j]]&&x+t[id[j]]<bg[id[j]])
f[s|1<<id[j]-1]=x+t[id[j]],rec[s|1<<id[j]-1]=id[j];
}
// rep(i,1,n)if(x>=a[i].l&&x<=a[i].r)x=a[i].r+1;
// rep(i,1,n)if((˜s>>i-1&1)&&f[s|1<<i-1]>x+t[i]-1)
// f[s|1<<i-1]=x+t[i]-1,rec[s|1<<i-1]=i;
}

// cerr<<f[30]<<’:’<<f[30ˆall]<<endl;
if(tp==1)
{
if(f[all]>=inf)GG();
recover(all,1);
}
else
{
bool ok=0;
rep(s,0,all)
if(!ok&&f[s]<inf&&f[sˆall]<inf)
{
recover(s,1);recover(sˆall,2);ok=1;
}

if(!ok)GG();
}

puts("YES");rep(i,1,n)printf("%d %d\n",ans[i].fi,ans[i].se);

return 0;
}

Listing 9.2.9: checkerPassports.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/157",
(char*)"../tests/157.a",
(char*)"passports.out",
};

cout<<"argc = "<<argc<<"\n";
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 324

for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("passports", argc, argv);


compareRemainingLines();
}

9.2.3 *Rezolvare detaliată

9.3 AB-Strings
Problema 3 - AB-Strings 100 de puncte
There are two strings s and t, consisting only of letters a and b. You can make the following
operation several times: choose a prefix of s, a prefix of t and swap them. Prefixes can be empty,
also a prefix can coincide with a whole string.
Your task is to find a sequence of operations after which one of the strings consists only of a
letters and the other consists only of b letters. The number of operations should be minimized,
but solutions that find non-optimal sequences will still get some points. Read the scoring section
for more detailed information.
Input
The first line contains a string s (1 & ¶s¶ & 2 10 ).
5

The second line contains a string t (1 & ¶t¶ & 2 10 ).


5

Here ¶s¶ and ¶t¶ denote the lengths of s and t, respectively. It is guaranteed that at least one
of the strings contains at least one a letter and at least one of the strings contains at least one b
letter.
Output
The first line should contain a single integer n (0 & n & 5 10 )—the number of operations.
5

Each of the next n lines should contain two space-separated integers ai , bi —the lengths of
prefixes of s and t to swap, respectively.
If there are multiple possible solutions, you can print any of them.
Constraints

ˆ ***

Subtasks
Let n be the length of your sequence, and m be the length of some optimal sequence.

ˆ If for all tests of the group n m, you will get 100% of the score of this group.
ˆ If for all tests of the group n & m  2, you will get 70% of the score of this group (rounded
down to the nearest integer).
ˆ If for all tests of the group n & 2m  2, you will get 50% of the score of this group (rounded
down to the nearest integer).

If for all tests of the group n & 5 10 , you will get 30% of the score of this group (rounded
5
ˆ

down to the nearest integer).

If for at least one test you output n % 5 10 , you will get WA and 0 points for this group.
5
ˆ
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 325

Subtask Score Constraints Comment


0 0 — Tests from the statement
1 5 1 & ¶s¶, ¶t¶ & 6 s and t combined contain exactly one letter a
2 10 1 & ¶s¶, ¶t¶ & 6 —
3 20 1 & ¶s¶, ¶t¶ & 50 —
4 20 1 & ¶s¶, ¶t¶ & 250 —
5 20 1 & ¶s¶, ¶t¶ & 2000 —
1 & ¶s¶, ¶t¶ & 2 10
5
6 25 —

Examples
standard input standard output
bab 2
bb 10
13
bbbb 0
aaa
Comment ¯ Note
In the first example, you can solve the problem in two operations:

1. Swap the prefix of the first string with length 1 and the prefix of the second string with
length 0. After this swap, you’ll have strings ab and bbb.
2. Swap the prefix of the first string with length 1 and the prefix of the second string with
length 3. After this swap, you’ll have strings bbbb and a.

In the second example, the strings are already appropriate, so no operations are needed.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 512 MB

9.3.1 Indicaţii de rezolvare

https://codeforces.com/blog/entry/60920
https://codeforces.com/topic/61265/en10
The solution is basically like following:
Note that we can compress equal adjacent letters.
Now we can do a dynamic programming with params (first letter of s, length of s, first letter
of t, length of t).
However, the amount of transactions and even states is too large. But we can write a slow,
but surely correct solution, and examine the transactions, which are made in dp.
Basically, the most typical transaction is to just make a swap of first group in s with first group
in t. However there special cases, like when the first letters are the same or when the lengths are
very small.
Running a slow dynamic programming helps to get all the cases for the solution.
Formally, the correctness of this algorithm can be proven by induction and the large cases
analyses, which we skip for clarity.
Another approach is to consider different first operations, and then go a greedy after it algo-
rithm. See the second solution for the details. We don’t prove it.
The first solution: 40971595
(https://codeforces.com/contest/1012/submission/40971595)
and the second solution: 40971634
(https://codeforces.com/contest/1012/submission/40971634).
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 326

9.3.2 Coduri sursă

Listing 9.3.1: rg wa.cpp


#include <iostream> // execution time : 1.031 s
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
#include <cassert>
#include <queue>
#include <ctime>
#include <string>
#include <cstring>
#define mp make_pair
#define pb push_back
#define NAME ""
#define y1 y1_423
#define list lista

using namespace std;

typedef long long ll;


typedef long double ld;

const int nmax = 2000 * 1000;


const int inf = 2000 * 1000 * 1000;
const ll infl = 1000ll * 1000ll * 1000ll * 1000ll * 1000ll * 1000ll;
const int mod = 1000 * 1000 * 1000 + 7;
const ld pi = acos(-1.0);

void make(const string &s, vector<pair<char, int> > &a)


{
int cur = s[0];
int cnt = 0;
for (auto c : s)
{
if (c == cur)
{
cnt++;
}
else
{
a.pb(mp(cur, cnt));
cnt = 1;
cur = c;
}
}
a.pb(mp(cur, cnt));
}

string s, t;

vector<pair<int, int> > solve()


{
vector<pair<char, int> > a, b;
vector<pair<int, int> > answer;

make(s, a);
make(t, b);

reverse(a.begin(), a.end());
reverse(b.begin(), b.end());

while (a.size() > 1 || b.size() > 1)


{
while (a.size() >= 2 && a.back().first == a[(int)a.size() - 2].first)
{
a[(int)a.size() - 2].second += a.back().second;
a.pop_back();
}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 327

while (b.size() >= 2 && b.back().first == b[(int)b.size() - 2].first)


{
b[(int)b.size() - 2].second += b.back().second;
b.pop_back();
}

int ans1 = 0, ans2 = 0;


if (a.size() == 1 && a.back().first == ’b’)
{
ans1 = 1;
}
else
if (a.size() >= 2)
{
if (a.back().first == ’a’)
{
ans1 = 2;
}
else
{
ans1 = 1;
}
}

if (b.size() == 1 && b.back().first == ’a’)


{
ans2 = 1;
}
else
if (b.size() >= 2)
{
if (b.back().first == ’b’)
{
ans2 = 2;
}
else
{
ans2 = 1;
}
}

if (ans1 == 0 && ans2 == 0)


{
continue;
}

vector<pair<char, int> > cura, curb;


int sum1 = 0, sum2 = 0;
for (int i = 0; i < ans1; i++)
{
cura.pb(a.back());
sum1 += a.back().second;
a.pop_back();
}

for (int i = 0; i < ans2; i++)


{
curb.pb(b.back());
sum2 += b.back().second;
b.pop_back();
}

answer.pb(mp(sum1, sum2));
while (cura.size())
{
b.pb(cura.back());
cura.pop_back();
}

while (curb.size())
{
a.pb(curb.back());
curb.pop_back();
}
}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 328

if (a.size() == 1 && b.size() == 1)


{
if (a.back().first == ’b’ && b.back().first == ’a’)
{
swap(a, b);
answer.pb(mp(a.back().second, b.back().second));
}
else
if (a.back().first == ’b’)
{
answer.pb(mp(a.back().second, 0));
}
else
if (b.back().first == ’a’)
{
answer.pb(mp(0, b.back().second));
}
}

return answer;
}

int main()
{
std::freopen("../tests/146", "r", stdin);
std::freopen("ab-strings.out", "w", stdout);

getline(cin, s);
getline(cin, t);

vector<pair<int, int> > ans1 = solve();


swap(s, t);
vector<pair<int, int> > ans2 = solve();

if (ans1.size() > ans2.size())


{
swap(ans1, ans2);
for (auto &u : ans1)
{
swap(u.first, u.second);
}
}

printf("%d\n", (int)ans1.size());
for (auto u : ans1)
{
printf("%d %d\n", u.first, u.second);
}

return 0;
}

Listing 9.3.2: rg wa2.cpp


#include <iostream> // execution time : 1.062 s
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
#include <cassert>
#include <queue>
#include <ctime>
#include <string>
#include <cstring>
#define mp make_pair
#define pb push_back
#define NAME ""
#define y1 y1_423
#define list lista

using namespace std;


CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 329

typedef long long ll;


typedef long double ld;

const int nmax = 2000 * 1000;


const int inf = 2000 * 1000 * 1000;
const ll infl = 1000ll * 1000ll * 1000ll * 1000ll * 1000ll * 1000ll;
const int mod = 1000 * 1000 * 1000 + 7;
const ld pi = acos(-1.0);

void make(const string &s, vector<pair<char, int> > &a)


{
int cur = s[0];
int cnt = 0;
for (auto c : s)
{
if (c == cur)
{
cnt++;
}
else
{
a.pb(mp(cur, cnt));
cnt = 1;
cur = c;
}
}

a.pb(mp(cur, cnt));
}

string s, t;

vector<pair<int, int> > solve()


{
vector<pair<char, int> > a, b;
vector<pair<int, int> > answer;
make(s, a);
make(t, b);
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
vector<pair<char, int> > cur;
if (a.size() > b.size())
{
while (a.size() > b.size() + cur.size())
{
cur.pb(a.back());
a.pop_back();
}
int sum = 0;
while (cur.size())
{
sum += cur.back().second;
b.pb(cur.back());
cur.pop_back();
}
answer.pb(mp(sum, 0));
}
else
if (a.size() < b.size())
{
while (b.size() > a.size() + cur.size())
{
cur.pb(b.back());
b.pop_back();
}
int sum = 0;
while (cur.size())
{
sum += cur.back().second;
a.pb(cur.back());
cur.pop_back();
}
answer.pb(mp(0, sum));
}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 330

while (a.size() > 1 || b.size() > 1)


{
while (a.size() >= 2 && a.back().first == a[(int)a.size() - 2].first)
{
a[(int)a.size() - 2].second += a.back().second;
a.pop_back();
}

while (b.size() >= 2 && b.back().first == b[(int)b.size() - 2].first)


{
b[(int)b.size() - 2].second += b.back().second;
b.pop_back();
}

int ans1 = 0, ans2 = 0;


if (a.size() == 1 && a.back().first == ’b’)
{
ans1 = 1;
}
else
if (a.size() >= 2)
{
if (a.back().first == ’a’)
{
ans1 = 2;
}
else
{
ans1 = 1;
}
}

if (b.size() == 1 && b.back().first == ’a’)


{
ans2 = 1;
}
else
if (b.size() >= 2)
{
if (b.back().first == ’b’)
{
ans2 = 2;
}
else
{
ans2 = 1;
}
}

if (ans1 == 0 && ans2 == 0)


{
continue;
}

vector<pair<char, int> > cura, curb;


int sum1 = 0, sum2 = 0;
for (int i = 0; i < ans1; i++)
{
cura.pb(a.back());
sum1 += a.back().second;
a.pop_back();
}

for (int i = 0; i < ans2; i++)


{
curb.pb(b.back());
sum2 += b.back().second;
b.pop_back();
}

answer.pb(mp(sum1, sum2));
while (cura.size())
{
b.pb(cura.back());
cura.pop_back();
}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 331

while (curb.size())
{
a.pb(curb.back());
curb.pop_back();
}
}

if (a.size() == 1 && b.size() == 1)


{
if (a.back().first == ’b’ && b.back().first == ’a’)
{
swap(a, b);
answer.pb(mp(a.back().second, b.back().second));
}
else
if (a.back().first == ’b’)
{
answer.pb(mp(a.back().second, 0));
}
else
if (b.back().first == ’a’)
{
answer.pb(mp(0, b.back().second));
}
}

return answer;
}

int main()
{
std::freopen("../tests/146", "r", stdin);
std::freopen("ab-strings.out", "w", stdout);

cin >> s >> t;


vector<pair<int, int> > ans1 = solve();
swap(s, t);
vector<pair<int, int> > ans2 = solve();
if (ans1.size() > ans2.size())
{
swap(ans1, ans2);
for (auto &u : ans1)
{
swap(u.first, u.second);
}
}

printf("%d\n", (int)ans1.size());
for (auto u : ans1)
{
printf("%d %d\n", u.first, u.second);
}

return 0;
}

Listing 9.3.3: solve.cpp


#include <bits/stdc++.h> // execution time : 0.484 s

using namespace std;

vector<pair<char, int>> convert(const string &s)


{
vector<pair<char, int>> result;
for (int i = 0; i < (int) s.size(); i++)
{
int j = i;
while (j + 1 < (int) s.size() && s[j + 1] == s[j])
{
j++;
}
result.emplace_back(s[i], j - i + 1);
i = j;
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 332

}
reverse(result.begin(), result.end());
return result;
}

void add(vector<pair<char, int>> &v, pair<char, int> el)


{
if (!v.empty() && v.back().first == el.first)
{
v.back().second += el.second;
}
else
{
v.push_back(el);
}
}

void add(vector<pair<char, int>> &v, const vector<pair<char, int>> &els)


{
for (int i = (int) els.size() - 1; i >= 0; i--)
{
add(v, els[i]);
}
}

template<typename T> vector<T> extract(vector<T> &v, int cnt)


{
vector<T> result;
while (cnt--)
{
result.push_back(v.back());
v.pop_back();
}
return result;
}

int get_total_length(const vector<pair<char, int>> &v)


{
int result = 0;
for (const auto &it : v)
{
result += it.second;
}
return result;
}

int main()
{
std::freopen("../tests/146", "r", stdin);
std::freopen("ab-strings.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

string s, t;
cin >> s >> t;
auto x = convert(s);
auto y = convert(t);
bool swapped = false;
vector<pair<int, int>> answer;

auto query = [&] (int len_x, int len_y)


{
auto part_x = extract(x, len_x);
auto part_y = extract(y, len_y);
int length_x = get_total_length(part_x);
int length_y = get_total_length(part_y);
if (swapped)
{
swap(length_x, length_y);
}
answer.emplace_back(length_x, length_y);
add(x, part_y);
add(y, part_x);
};
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 333

while ((int) x.size() != 1 || (int) y.size() != 1)


{
int la = (int) x.size();
int lb = (int) y.size();
if (la < lb)
{
swap(la, lb);
swap(x, y);
swapped ˆ= true;
}

char a = x.back().first;
char b = y.back().first;

if (a != b)
{
if (lb <= 2)
{
if (la <= 3)
{
query(1, 1);
}
else
{
query(3, 1);
}
}
else
{
query(1, 1);
}
}
else
{
if (lb == 1)
{
if (la <= 3)
{
query(1, 0);
}
else
{
query(3, 0);
}
}
else
if (lb == 2)
{
if (la == 2)
{
query(1, 0);
}
else
{
query(2, 1);
}
}
else
{
query(3, 2);
}
}
}

cout << answer.size() << "\n";


for (auto &op : answer)
{
cout << op.first << " " << op.second << "\n";
}

return 0;
}
\end{lstlistng}

%---------------------------------------------------------------------------
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 334

\hspace{0.33cm}
\subsection{*Rezolvare detaliat\u a}
\vspace{3mm}

%=================================================================

\section{Chemistry}

{\bf Problema 4 - Chemical table \hfill 100 de puncte }


\vspace{2mm}

Innopolis University scientists continue to investigate the periodic table. There are $n
\cdot m$
known elements and they form a periodic table, a rectangle with $n$ rows and $m$
columns. Each element can be described by its coordinates $(r, c)$ ($1 \le r \le n$
, $1 \le c \le m$) in the table. Recently scientists discovered that for every four
different elements in this table that form a rectangle with sides parallel to
sides of the table, if they have samples of three of four elements, they can
produce a sample of the fourth element using nuclear fusion. So if we have elements
in positions $(r_1, c_1)$, $(r_1, c_2)$, $(r_2, c_1)$, where $r_1 \neq r_2$ and
$c_1 \neq c_2$, then we can produce element $(r_2, c_2)$.

\begin{figure}[H]
\centering
%\begin{center}
\includegraphics[width=0.63\textwidth]{img/ejoi2018_chemistry4.eps}
%\end{center}
%\caption{***}
\end{figure}

Original samples of elements as well as newly crafted elements can be used again in
future fusions.

Innopolis University scientists already have samples of $q$ elements. They want to
obtain samples of all $n \cdot m$ elements. To achieve that, they will purchase some
samples from other laboratories and then produce all remaining elements using
arbitrary number of nuclear fusions in some order. Help them find the minimal number
of elements they need to purchase.

\vspace{2mm}
\noindent
{\bf Input}
\vspace{2mm}

First line contains three integers $n$, $m$, $q$ ($1 \le n, m \le 200\,000$; $0 \le q \
le \min(n \cdot m, 200\,000)$)---chemical table dimensions and the number of
elements scientists already have. Following $q$ lines contain two integers $r_i$,
$c_i$ ($1 \le r_i \le n$, $1 \le c_i \le m$) each---descriptions of the elements
that scientists already have. All elements in the input are different.

\vspace{2mm}
\noindent
{\bf Output}
\vspace{2mm}

In the only line print $k$---the minimal number of elements to be purchased.

\vspace{2mm}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 335

\noindent
{\bf Constraints}
\vspace{2mm}

\begin{itemize}
\item ***
\end{itemize}

\vspace{2mm}
\noindent
{\bf Subtasks}
\vspace{2mm}

In the only line print $k$---the minimal number of elements to be purchased.

\medskip
\begin{center}
\begingroup
\renewcommand{\arraystretch}{1.5}
\begin{tabular}{|c|c|c|c|c|c|}
\hline
& & \multicolumn{3}{|c|}{Constraints} & \\
\cline{3-5}
\raisebox{2.25ex}[0cm][0cm]{Subtask}
& \raisebox{2.25ex}[0cm][0cm]{Score}
& $n$ & $m$ & $q$
& \raisebox{2.25ex}[0cm][0cm]{\parbox{3cm}{\centering Dependencies}}
\\
\hline
0 & 0 & --- & --- & --- & --- \\
\hline
1 & 10 & $n = 2$ & $m = 2$ & $0 \le q \le 4$ & --- \\
\hline
2 & 8 & $n = 1$ & $1 \le m \le 20$ & $0 \le q \le 20$ & --- \\
\hline
3 & 9 & $n = 2$ & $1 \le m \le 20$ & $0 \le q \le 40$ & 1 \\
\hline
4 & 8 & $1 \le n \le 20$ & $1 \le m \le 20$ & $q = 0$ & --- \\
\hline
5 & 20 & $1 \le n \le 20$ & $1 \le m \le 20$ & $0 \le q \le 400$ & 1---4 \\
\hline
6 & 10 & $1 \le n \le 100$ & $1 \le m \le 100$ & $0 \le q \le 10\,000$ & 1---5 \\
\hline
7 & 10 & $1 \le n \le 250$ & $1 \le m \le 250$ & $0 \le q \le 62\,500$ & 1---6 \\
\hline
8 & 10 & $1 \le n \le 10\,000$ & $1 \le m \le 10\,000$ & $0 \le q \le 100\,000$ & 1---7
\\
\hline
9 & 15 & $1 \le n \le 200\,000$ & $1 \le m \le 200\,000$ & $0 \le q \le 200\,000$ &
1---8 \\
\hline
\end{tabular}
\endgroup
\end{center}

% ---------------------------------------------

\vspace{2mm}
\noindent
{\bf Examples}
\vspace{2mm}

\begin{tabular}{|p{30mm}|p{30mm}|}
\hline
standard input & standard output \\ \hline
2 2 3 \newline
1 2 \newline
2 2 \newline
2 1
&
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 336

0 \\
\hline
1 5 3 \newline
1 3 \newline
1 1 \newline
1 5
&
2 \\
\hline
4 3 6 \newline
1 2 \newline
1 3 \newline
2 2 \newline
2 3 \newline
3 1 \newline
3 3
&
1 \\
\hline
\end{tabular}

\vspace{3mm}
\noindent
{\bf Comment $\backslash$ Note}
\vspace{2mm}

Pictures below explain examples.

The first picture for each example describes the initial set of element samples
available.
Black crosses represent elements available in the lab initially.

The second picture describes how remaining samples can be obtained.


Red dashed circles denote elements that should be purchased from another labs (optimal
solution should minimize number of red circles).
Blue dashed circles are elements which can be produced with nuclear fusion.
They are numbered starting in order in which they can be produced.

\textbf{Test 1}

We can use nuclear fusion and get the element from other three samples, so we don’t need
to purchase anything.

\begin{figure}[H]
\centering
%\begin{center}
\includegraphics[width=0.45\textwidth]{img/ejoi2018_chemistry1.eps}
%\end{center}
%\caption{***}
\end{figure}

\textbf{Test 2}

We cannot use any nuclear fusion at all as there is only one row, so we have to purchase
all missing elements.

\begin{figure}[H]
\centering
%\begin{center}
\includegraphics[width=0.63\textwidth]{img/ejoi2018_chemistry2.eps}
%\end{center}
%\caption{***}
\end{figure}

\textbf{Test 3}

Note that after purchasing one element it’s still not possible to produce the middle
element in the top row (marked as 4).
So we produce the element in the left-bottom corner first (marked as 1), and then use it
in future fusions.

\begin{figure}[H]
\centering
%\begin{center}
\includegraphics[width=0.5\textwidth]{img/ejoi2018_chemistry3.eps}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 337

%\end{center}
%\caption{***}
\end{figure}

% ---------------------------------------------

\vspace{3mm}

{\bf Timp maxim} de executare/test: 1.0 secunde

{\bf Memorie:} total 512 MB

\vspace{2mm}

% ---------------------------------------------

\vspace{3mm}
\subsection{Indica\c tii de rezolvare} % Chemical table
\vspace{3mm}

\url{https://codeforces.com/blog/entry/60920}

\noindent
\url{https://codeforces.com/topic/61265/en10}
\vspace{2mm}

One of the way to solve this problem is to interprete the cells in $2d$ matrix as an
edge in the {\it bipartite graph}\index{graph!bipartite}, that is a cell $(i, j )$
is an edge between $i$ of the left part and $j$ of the right part.

Note, that each fusion operation (we have edges $(r_1, c _ 1 )$, $(r_1, c _ 2 )$, $(r_2,
c _ 1 )$ and get edge $(r_2, c _ 2 )$) doesn’t change the {\it connected components}\
index{connected component} of the graph.

Moreover, we can prove, that we can obtain each edge in the connected component by
fusion.

Let’s examine example {\it edge}\index{edge} $(x, y )$, and some {\it path}\index{path}
between $x$ and $y$ (since they lay in one connected component). Since the graph is
bipartite, the length of this path must be odd, and it is $\ g e q 3 $ , otherwise the
edge already exists.

So we have this path. Take first three edges and make the corresponding fusion, replace
this three edges with the brand new fused edge. The length of the path decreased by
2. Repeat until the path is a mere edge.

This way, the number of edges to add (cells to buy) is just number of connected
components minus one.

\hspace{0.33cm}
\subsection{Coduri surs\u a} % ************************************
\hspace{0.99cm}

\begin{lstlisting}[language=C++,numbers=none,commentstyle=\color{purple}, caption={\
hspace{2mm} chemistry\_ad.cpp}]
#include <bits/stdc++.h> // execution time : 0.750 s

using namespace std;

#define se second
#define fi first
#define forn(i, n) for (int i = 0; i < n; i++)
#define sz(a) (int)a.size()
#define mp make_pair

int n, m, q, use[1000000], use2[1000000];

vector<int> nms[1000000], nmr[1000000];


vector<pair<int, int> > qq, ans;
vector<int> cq, pq;
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 338

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

iostream::sync_with_stdio(0), cin.tie(0);

cin >> n >> m >> q;


forn (i, q)
{
int x, y;
cin >> x >> y;
x--;
y--;
nms[x].push_back(y);
nmr[y].push_back(x);
}

if (q == 0)
{
ans.push_back(mp(0, 0));
nms[0].push_back(0);
nmr[0].push_back(0);
}

forn (i, n)
{
if (use[i] == 0 && sz(nms[i]))
{
qq.push_back(mp(i, nms[i][0]));
queue<int> vv;
vv.push(i);
use[i] = 1;
while (!vv.empty())
{
int nu = vv.front();
vv.pop();
for (int q : nms[nu])
{
if (use2[q] == 0)
{
for (int c : nmr[q])
{
if (use[c] == 0)
{
use[c] = 1;
vv.push(c);
}
}
use2[q] = 1;
}
}
}
}
else
{
if (use[i] == 0)
cq.push_back(i);
}
}

forn (i, m)
{
if (use2[i] == 0)
{
pq.push_back(i);
}
}

forn (i, sz(qq) - 1)


ans.push_back(mp(qq[i].fi, qq[i + 1].se));

forn (i, sz(pq))


ans.push_back(mp(qq[0].fi, pq[i]));

forn (i, sz(cq))


CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 339

ans.push_back(mp(cq[i], qq[0].se));

cout << sz(ans) << "\n";


}

Listing 9.3.4: ds ok.cpp


// 2018, Sayutin Dmitry,execution time : 1.562 s

#include <bits/stdc++.h>

using std::cin;
using std::cout;
using std::cerr;

using std::vector;
using std::map;
using std::array;
using std::set;
using std::string;

using std::pair;
using std::make_pair;

using std::min;
using std::abs;
using std::max;

using std::unique;
using std::sort;
using std::generate;
using std::reverse;
using std::min_element;
using std::max_element;

#ifdef LOCAL
#define LASSERT(X) assert(X)
#else
#define LASSERT(X) {}
#endif

template <typename T>


T input()
{
T res;
cin >> res;
LASSERT(cin);
return res;
}

template <typename IT>


void input_seq(IT b, IT e)
{
std::generate(b, e,
input<typename std::remove_reference<decltype( *b)>::type>);
}

#define SZ(vec) int((vec).size())


#define ALL(data) data.begin(),data.end()
#define RALL(data) data.rbegin(),data.rend()
#define TYPEMAX(type) std::numeric_limits<type>::max()
#define TYPEMIN(type) std::numeric_limits<type>::min()

void zhfs(const vector<vector<int>>& graph, vector<char>& used, int v)


{
used[v] = 1;
for (int u: graph[v])
if (not used[u])
zhfs(graph, used, u);
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 340

int n = input<int>();
int m = input<int>();

vector<vector<int>> graph(n + m);

for (int q = input<int>(); q != 0; --q)


{
int a = input<int>() - 1;
int b = input<int>() - 1;

graph[a].push_back(n + b);
graph[n + b].push_back(a);
}

int ans = -1;


vector<char> used(n + m, false);
for (int i = 0; i != n + m; ++i)
if (not used[i])
{
++ans;
zhfs(graph, used, i);
}

cout << ans << "\n";


}

Listing 9.3.5: gr ok.cpp


//execution time : 0.781 s
#include <bits/stdc++.h>

using namespace std;

const int MX = 2 * 1000 * 1000 + 7;

vector<int> g[MX];
bool was[MX];

void dfs(int v)
{
was[v] = true;
for (int to : g[v])
{
if (!was[to])
{
dfs(to);
}
}
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);

std::freopen("../tests/167", "r", stdin);


std::freopen("chemistry.out", "w", stdout);

int n, m, q;
cin >> n >> m >> q;

if (q == 0)
{
cout << n + m - 1 << "\n";
/*for (int i = 1; i <= n; i++) {
cout << i << " 1\n";
}
for (int i = 2; i <= m; i++) {
cout << "1 " << i << "\n";
}*/
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 341

return 0;
}

for (int i = 1; i <= q; i++)


{
int x, y;
cin >> x >> y;
g[x].push_back(n + y);
g[n + y].push_back(x);
}

int anyv = -1;


for (int i = 1; i <= n + m; i++)
{
if (g[i].size() != 0)
{
anyv = i;
break;
}
}

dfs(anyv);

int anyx = -1, anyy = -1;


for (int i = 1; i <= n; i++)
{
if (was[i])
{
anyx = i;
}
}

for (int i = n + 1; i <= n + m; i++)


{
if (was[i])
{
anyy = i - n;
}
}

vector<pair<int, int> > ans;


for (int i = 1; i <= n; i++)
{
if (!was[i])
{
ans.emplace_back(i, anyy);
dfs(i);
}
}

for (int i = n + 1; i <= n + m; i++)


{
if (!was[i])
{
ans.emplace_back(anyx, i - n);
}
}

cout << ans.size() << "\n";

//for (const auto& v : ans) {


// cout << v.first << " " << v.second << "\n";
//}

return 0;
}

Listing 9.3.6: isaf ok.cpp


//#pragma GCC optimize("O3") // execution time : 1.000 s
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 342

typedef long double ld;

#define TIME (clock() * 1.0 / CLOCKS_PER_SEC)

const ld eps = 1e-6;


const ld pi = acos(-1.0);
const int two = 2;
const int BIG = 1e9 + 239;
const ll INF = 1e18 + 239;
const ll MOD = 998244353;
const ll MOD2 = MOD * MOD;
const int th = 3;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};
const int alf = 26;
const int dig = 10;
const int T = (1 << 19);
const int M = 2e6 + 239;

vector<int> v[M];
vector<int> c[M];

bool used[M];
int n, m, q, r;

void dfs(int p)
{
used[p] = true;
c[r].push_back(p);
for (int i : v[p])
if (!used[i])
dfs(i);
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios::sync_with_stdio(0);
cin >> n >> m >> q;
for (int i = 0; i < q; i++)
{
int s, f;
cin >> s >> f;
s--, f--;
f += n;
v[s].push_back(f);
v[f].push_back(s);
}

memset(used, 0, sizeof(used));

r = 0;
for (int i = 0; i < (n + m); i++)
if (!used[i])
{
dfs(i);
r++;
}

cout << (r - 1) << "\n";

return 0;
}

Listing 9.3.7: rg ok.cpp


#include <iostream> // execution time : 1.203 s
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <set>
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 343

#include <map>
#include <cassert>
#include <queue>
#include <ctime>
#include <string>
#include <cstring>
#define mp make_pair
#define pb push_back
#define NAME ""
#define y1 y1_423
#define list lista

using namespace std;

typedef long long ll;


typedef long double ld;

const int nmax = 2000 * 1000;


const int inf = 2000 * 1000 * 1000;
const ll infl = 1000ll * 1000ll * 1000ll * 1000ll * 1000ll * 1000ll;
const int mod = 1000 * 1000 * 1000 + 7;
const ld pi = acos(-1.0);

bool used[nmax];
vector<int> a[nmax];
vector<pair<int, int> > ans;

void dfs(int v)
{
used[v] = true;
for (int u : a[v])
{
if (!used[u])
{
dfs(u);
}
}
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

int n, m, q;
cin >> n >> m >> q;
for (int i = 0; i < q; i++)
{
int x, y;
scanf("%d%d", &x, &y);
x--, y--;
a[x].pb(y + n);
a[y + n].pb(x);
}

dfs(n);

for (int i = 0; i < n; i++)


{
if (!used[i])
{
dfs(i);
ans.pb(mp(i + 1, 1));
}
}

for (int i = n; i < n + m; i++)


{
if (!used[i])
{
dfs(i);
ans.pb(mp(1, i + 1 - n));
}
}

printf("%d\n", (int)ans.size());
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 344

/*for (auto u : ans) {


printf("%d %d\n", u.first, u.second);
}*/
return 0;
}

Listing 9.3.8: 11899245-chemistry.cpp


// https://www.cnblogs.com/KisekiPurin2019/p/11899245.html

#include<bits/stdc++.h> // execution time : 1.047 s

using namespace std;

typedef long long ll;

bool vis[400005];
vector<int> G[400005];

void dfs(int u)
{
vis[u] = 1;
for(auto v : G[u])
{
if(!vis[v])
dfs(v);
}
}

void test_case()
{
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
while(q--)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(n + v);
G[n + v].push_back(u);
}

int cnt = 0;
for(int i = 1; i <= n + m; ++i)
{
if(!vis[i])
{
dfs(i);
++cnt;
}
}

printf("%d\n", cnt - 1);


}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

int t = 1;
for(int ti = 1; ti <= t; ++ti)
{
//printf("Case #%d: ", ti);
test_case();
}
}

Listing 9.3.9: 56157022-chemistry.cpp


// https://codeforces.com/contest/1013/submission/56157022
// https://codeforces.com/contest/1013/status/D

#include<bits/stdc++.h> // execution time : 0.688 s


CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 345

using namespace std;

int A[400001];

int Fx(int x)
{
return A[x]==x?x:A[x]=Fx(A[x]);
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

int a,b,c,i,x,y,w=0;
cin>>a>>b>>c;

for(i=1;i<=a+b;i++) A[i]=i;

for(i=1;i<=c;i++)
{
scanf("%d%d",&x,&y),
x=Fx(x),
y=Fx(y+a);

if(x!=y) A[x]=y;
}

for(i=1;i<=a+b;i++)
if(A[i]==i) w++;

cout<<w-1;
}

Listing 9.3.10: 86481283-chemistry.cpp


// https://codeforces.com/contest/1012/submission/86481283
// https://codeforces.com/contest/1012/status/page/4?order=BY_JUDGED_DESC

#include <iostream> //execution time : 0.672 s


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 400005;


int p[N];

int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

int n, m, q;
scanf("%d%d%d", &n, &m, &q);

for (int i = 1; i <= n + m; ++i) p[i] = i;

int r, c;
for (int i = 1; i <= q; ++i)
{
scanf("%d%d", &r, &c);
c += n;
p[find(r)] = find(c);
}

int res = 0;
for (int i = 1; i <= n + m; ++i)
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 346

{
if (p[i] == i)
++res;
}

printf("%d\n", res - 1);

return 0;
}

Listing 9.3.11: 86488789-chemistry.cpp


// https://codeforces.com/contest/1012/submission/86488789
// https://codeforces.com/contest/1012/status/page/3?order=BY_JUDGED_DESC

#include <bits/stdc++.h> // execution time : 0.266 s

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cmath>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define ull unsigned long long
#define PI acos(-1)
#define pb(x) push_back(x)
#define il inline
#define re register
#define IO ios::sync_with_stdio(0);cin.tie(0);
//#define ls (o<<1)
//#define rs (o<<1|1)
#define pii pair<int,int>

using namespace std;

const int maxn = 2e5+5;


const int maxm = 400010;
const int INF = 0x3f3f3f3f;
const ll LINF = 3e17+1;
const int mod = 1e9+7;

int n, r, m;

inline int read()


{
register int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch==’-’)f=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+ch-’0’;ch=getchar();}
return (f==1)?x:-x;
}

int fa[maxn<<1];

int getfa(int x)
{
return x == fa[x] ? x : fa[x] = getfa(fa[x]);
}

void join(int x, int y)


{
int xx = getfa(x), yy = getfa(y);
if (xx != yy)
fa[xx] = yy;
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

IO;
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 347

int t;
cin >> n >> m >> t;

int u,v;
for (int i = 1; i <= n + m; i++)
fa[i] = i;

for (int i = 1; i <= t; i++)


{
cin >> u >> v;
join(u,v+n);
}

int ans = 0;
int root = getfa(1);
for (int i = 2; i <= n + m; i++)
{
if (getfa(i) != root)
ans ++, fa[getfa(i)] = root;
}

cout << ans << endl;

return 0;
}

Listing 9.3.12: 86816257-chemistry.cpp


// https://codeforces.com/contest/1012/submission/86816257
// https://codeforces.com/contest/1012/status/page/3?order=BY_JUDGED_DESC

#include<bits/stdc++.h> //execution time : 0.391 s

using namespace std;

# define ll long long int

int parent[500008];
vector< pair<int,int > > v(500008);
int n,m,q;

int find_set(int v)
{
if(v==parent[v])
return v;
return parent[v]=find_set(parent[v]);
}

void union_sets(int a, int b)


{
a =find_set(a);
b=find_set(b);
if(a!=b)
{
parent[b]=a;

}
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios_base::sync_with_stdio(0);
cin.tie(0);

cin>>n>>m>>q;

for(int i=1;i<=q;i++)
{
int x,y;
cin>>x>>y;
v.push_back({x,y+n});
}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 348

for(int i=1;i<=n+m;i++)
{
parent[i]=i;
}

for(int i=0;i<v.size();i++)
{
int x=v[i].first;
int y=v[i].second;
if(find_set(x)==find_set(y)) continue;

union_sets(x,y);
}

int ans=0;
for(int i=1;i<=n+m;i++)
{
if(find_set(i)==i)ans++;
}

cout<<ans-1<<endl;

return 0;
}

Listing 9.3.13: 86816797-chemistry.cpp


// https://codeforces.com/contest/1012/submission/86816797
// https://codeforces.com/contest/1012/status/page/3?order=BY_JUDGED_DESC

#include <bits/stdc++.h> //execution time : 0.516 s

#pragma GCC optimize("Ofast")

using namespace std;

typedef long long ll;


typedef long double ld;

#define pll pair<ll, ll>


#define ppll pair<pair<ll, ll>, ll>
#define pb push_back

const ll maxn = ll(2e5) + 5;


const ll inf = ll(1e9) + 123;
const ll mod = ll(1e9) + 7;

ll n, m, q;
vector<pll> a;
vector<pll> ps[maxn];
vector<ll> cnt(maxn, 0);

vector<ll> par;
vector<ll> sz;

ll get_par(ll v)
{
if(par[v] == v) return v;
return (par[v] = get_par(par[v]));
}

void join(ll v, ll u)
{
v = get_par(v);
u = get_par(u);
if(v == u) return;
if(sz[v] < sz[u])
{
swap(v, u);
}
par[u] = v;
sz[v] += sz[u];
}
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 349

void solve()
{
cin >> n >> m >> q;
for(ll i = 0; i < q; i++)
{
ll row, col;
cin >> row >> col;
row--;
col--;
a.pb({row, col});
ps[row].pb({col, i});
cnt[col]++;
}

for(ll i = 0; i < maxn; i++)


{
par.pb(i);
sz.pb(1);
}

for(ll i = 0; i < n; i++)


{
for(ll j = 1; j < ps[i].size(); j++)
{
join(ps[i][j - 1].first, ps[i][j].first);
}
}

set<ll> roots;
ll notusedx = 0;
ll notusedy = 0;
for(ll i = 0; i < m; i++)
{
if(cnt[i] > 0) roots.insert(get_par(i));
if(cnt[i] == 0) notusedx++;
}

for(ll i = 0; i < n; i++)


{
if(ps[i].size() == 0) notusedy++;
}

cout << (ll(roots.size()) - 1) + notusedx + notusedy << endl;


}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

//ll start = clock();


solve();

//cout << fixed << setprecision(3) << \


ld(1.0) * (clock() - start) / CLOCKS_PER_SEC << endl;

return 0;
}

Listing 9.3.14: 87021285-chemistry.cpp


// https://codeforces.com/contest/1012/submission/87021285
// https://codeforces.com/contest/1012/status/page/3?order=BY_JUDGED_DESC

#include <bits/stdc++.h> //execution time : 0.297 s

using namespace std;

template<typename T>
void out(T x) { cout << x << endl; exit(0); }
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 350

#define watch(x) cout << (#x) << " is " << (x) << endl

using ll = long long;

const ll mod = 1e9+7;


const int maxn = 1e6 + 5;

struct dsu0
{
vector<int> par, siz;
int n;
int cc;
int largest;

void init(int n)
{
assert(n>0);
this->n=n;
cc=n;
par.resize(n+10);
siz.resize(n+10);
for (int i=0; i<n; i++)
par[i]=i,siz[i]=1;
largest=1;
}

int parent(int x)
{
assert(x>=0 && x<n);
return par[x]==x ? x : par[x]=parent(par[x]);
}

void join(int x, int y)


{
x=parent(x);
y=parent(y);
if (x==y) return;

cc--;
if (siz[x]<siz[y]) swap(x,y);
siz[x]+=siz[y];
par[y]=x;
largest=max(largest,siz[x]);
}
};

int n,m,q;

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);

cin>>n>>m>>q;
dsu0 dsu;
dsu.init(n+m);
while (q--)
{
int x,y;
cin>>x>>y;
--x;
--y;
dsu.join(x,y+n);
}

cout<<dsu.cc-1<<endl;
return 0;
}

OBS: DSU Disjoint Set Union https://cp-algorithms.com/data_structures/di


sjoint_set_union.html

Listing 9.3.15: 87201594-chemistry.cpp


CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 351

// https://codeforces.com/contest/1012/submission/87201594
// https://codeforces.com/contest/1012/status/page/2?order=BY_JUDGED_DESC

#include<iostream> // execution time : 0.500 s


#include<vector>

using namespace std;

typedef long long ll;

int n, m, q;
vector<int> v[400050];
bool visited[400050];

void dfs(int e)
{
visited[e] = 1;
for(int i = 0; i < v[e].size(); i++)
if (!visited[v[e][i]]) dfs(v[e][i]);
}

int main(void)
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(0);

int cnt = 0;
cin>>n>>m>>q;

while(q--)
{
int a, b;
cin>>a>>b;
v[a].push_back(b+n);
v[b+n].push_back(a);
}

for(int i = 1; i <= n+m; i++)


if (!visited[i])
dfs(i), cnt++;

cout<<cnt-1;

return 0;
}

Listing 9.3.16: 87466173-chemistry.cpp


// https://codeforces.com/contest/1012/status/page/2?order=BY_JUDGED_DESC
// https://codeforces.com/contest/1012/submission/87466173

#include <bits/stdc++.h> // execution time : 0.719 s

using namespace std;

//TO_STRING
template <typename A, typename B>
string to_string(pair<A, B> p);

template <typename A, typename B, typename C>


string to_string(tuple<A, B, C> p);

template <typename A, typename B, typename C, typename D>


string to_string(tuple<A, B, C, D> p);

string to_string(const string& s)


{
return ’"’ + s + ’"’;
}

string to_string(char ch)


{
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 352

string s;
s += ch;
return s;
}

string to_string(const char* s)


{
return to_string((string) s);
}

string to_string(bool b)
{
return (b ? "true" : "false");
}

string to_string(vector<bool> v)
{
bool first = true;
string res = "{";
for (int i = 0; i < static_cast<int>(v.size()); i++)
{
if (!first)
{
res += ", ";
}

first = false;
res += to_string(v[i]);
}

res += "}";
return res;
}

template <size_t N>


string to_string(bitset<N> v)
{
string res = "";
for (size_t i = 0; i < N; i++)
{
res += static_cast<char>(’0’ + v[i]);
}

return res;
}

template <typename A>


string to_string(A v)
{
bool first = true;
string res = "{";
for (const auto &x : v)
{
if (!first)
{
res += ", ";
}

first = false;
res += to_string(x);
}

res += "}";
return res;
}

template <typename A, typename B>


string to_string(pair<A, B> p)
{
return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
}

template <typename A, typename B, typename C>


string to_string(tuple<A, B, C> p)
{
return "(" + to_string(get<0>(p)) + ", " + to_string(get<1>(p)) + ", " +
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 353

to_string(get<2>(p)) + ")";
}

template <typename A, typename B, typename C, typename D>


string to_string(tuple<A, B, C, D> p)
{
return "(" + to_string(get<0>(p)) + ", " + to_string(get<1>(p)) + ", " +
to_string(get<2>(p)) + ", " + to_string(get<3>(p)) + ")";
}

//DEBUG
void debug_out() { cerr << endl; }

template <typename Head, typename... Tail>


void debug_out(Head H, Tail... T)
{
cerr << " " << to_string(H);
debug_out(T...);
}

#ifdef LOCAL //compile with -DLOCAL


#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

typedef long long ll;


typedef long double ld;
typedef pair<int, int> pi;
typedef pair<ll, ll> pll;

typedef vector<int> vi;


typedef vector<ll> vll;
typedef vector<bool> vb;
typedef vector<pi> vpi;
typedef vector<char> vch;
typedef vector<string> vstr;
typedef vector<vi> vvi;
typedef vector<vpi> vvpi;
typedef vector<pll> vpll;
typedef vector<vll> vvll;
typedef vector<vb> vvb;
typedef vector<vvi> vvvi;
typedef vector<vch> vvch;
typedef vector<vstr> vvstr;

#define all(x) x.begin(), x.end()


#define pb push_back
#define pf push_front
#define fi first
#define se second
#define sz(x) ((ll)x.size())

#define rep(i, a, b) for (int i = (int)a; i < (int)b; i++)


#define repi(i, a, b) for (int i = (int)a; i >= (int)b; i--)

void no()
{
cout << "NO\n";
exit(0);
}

void dfs(int node, auto &vis, auto &edges)


{
vis[node] = 1;
for (int child : edges[node])
{
if (!vis[child])
{
dfs(child, vis, edges);
}
}
}

void solve()
{
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 354

int n, m, q;
cin >> n >> m >> q;

int N = n + m;
vvi edges(N + 1);
rep(i, 0, q)
{
int a, b;
cin >> a >> b;
b += n;
edges[a].pb(b);
edges[b].pb(a);
}

vb vis(N + 1);
int compo = 0;

rep(i, 1, N + 1)
{
if (!vis[i])
{
++compo;
dfs(i, vis, edges);
}
}

cout << compo - 1 << "\n";


}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);

int t = 1;
// cin >> t;

for (int tc = 1; tc <= t; tc++)


{
solve();
}
}

Listing 9.3.17: 87522420-chemistry.cpp


// https://codeforces.com/contest/1012/submission/87522420
// https://codeforces.com/contest/1012/status/page/2?order=BY_JUDGED_DESC

#include <bits/stdc++.h> //execution time : 0.281 s

using namespace std;

#define forn(i, n) for(int i = 0; i < int(n); i++)


// #define fore(i, l, r) for(int i = int(l); i < int(r); i++)
// #define pb push_back
#define all(x) x.begin(), x.end()
// #define sz(a) int((a).size())
// typedef long long ll;
typedef vector<int> vi;
// typedef vector<vector<int>> vvi;
// typedef vector<ll> vl;
// typedef vector<vector<ll>> vvl;
// typedef vector<string> vs;
// typedef vector<bool> vb;
// typedef pair<int, int> pii;
// const int mod = 1e9 + 7;
// template<class T, class U> inline void add_self(T &a, U b)\
{a += b;if (a >= mod) a -= mod;if (a < 0) a += mod;}
// template<class T, class U> inline void min_self(T &x, U y)\
{ if (y < x) x = y; }
// template<class T, class U> inline void max_self(T &x, U y)\
{ if (y > x) x = y; }
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 355

// #define _deb(x) cout<<x;


// void _print() {cerr << "]\n";} template <typename T, typename... V>\
void _print(T t, V... v) {_deb(t); if (sizeof...(v)) \
cerr << ", "; _print(v...);}
// #define deb(x...) cerr << "[" << #x << "] = ["; _print(x)
// template <class T, class U> void print_m(const map<T,U> &m, int w=3)\
{if(m.empty()){cout<<"Empty"<<endl; return;}for(auto x: m)\
cout<<"("<<x.first<<": "<<x.second<<"),"<<endl;cout<<endl;}
// template<class T, class U>\
void debp(const pair<T, U> &pr, bool end_line=1){cout<<"{"<<\
pr.first<<" "<<pr.second<<"}"; cout<<(end_line ? "\n" : ", ");}
// template <class T> void print_vp(const T &vp, int sep_line=0)\
{if(vp.empty()){cout<<"Empty"<<endl; return;}if(!sep_line) \
cout<<"{ ";for(auto x: vp) debp(x,sep_line);if(!sep_line) \
cout<<"}\n";cout<<endl;}
// template <typename T>void print(const T &v, bool show_index = false)\
{int w = 2;if(show_index){for(int i=0; i<sz(v); i++)\
cout<<setw(w)<<i<<" ";cout<<endl;}for(auto &el: v) \
cout<<setw(w)<<el<<" ";cout<<endl;}
// template <typename T>void print_vv(const T &vv){if(sz(vv)==0) \
{cout<<"Empty"<<endl; return;} int w = 3;cout<<setw(w)<<" ";\
for(int j=0; j<sz( *vv.begin()); j++)cout<<setw(w)<<j<<" ";\
cout<<endl;int i = 0;for(auto &v: vv){cout<<i++<<" {";\
for(auto &el: v) cout<<setw(w)<<el<<" ";cout<<"},\n";}cout<<endl;}

const int nax = 2e5+10;


int n, m;
vi par;

int find par(int x)


{
if(x==par[x]) return x;
return par[x] = find_par(par[x]);
}

bool make set(int x, int y)


{
x = find_par(x);
y = find_par(y);
if(x!=y)
{
par[y] = x;
return true;
}

return false;
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);

while(cin>>n>>m)
{
par.resize(n+m+1);
iota(all(par),0);

int q;
cin>>q;
forn(i,q)
{
int x, y;
cin>>x>>y;
make_set(x,n+y);
}

int comps = 0;
for(int i=1; i<=n+m; ++i)
{
if(par[i]==i) ++comps;
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 356

cout<<comps-1<<"\n";
}

return 0;
}

Listing 9.3.18: 88013224-chemistry.cpp


// https://codeforces.com/contest/1012/submission/88013224
// https://codeforces.com/contest/1012/status //execution time : 0.625 s

#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <cmath>
#include <unordered_map>
#include <queue>
#include <map>
#include <functional>
#include <stack>
#include <cmath>
#include <cstring>
#include <random>
#include <chrono>
#include <memory>
#include <cassert>
#include <numeric>
#include <array>
#define int long long
#define pii pair<int, int>
#define pic pair<int, char>
#define pdd pair<double, double>
#define x first
#define y second
#define ll long long
#define ull unsigned long long
#define endl "\n"
#define all(a) (a).begin(), (a).end()

#pragma GCC optimize ("O3")

using namespace std;

const int N = 2e5 + 10;


const int inf = 1e16 + 10ll;
const int mod = 998244353;
const int eps = 1e-9;

mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());

using namespace std;

int n, m, q;
vector<int> g[N * 2];
bool used[2 * N];

void dfs(int v)
{
used[v] = true;

for (auto u : g[v])


if (!used[u])
dfs(u);
}

int32_t main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 357

ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout.precision(10);

cin >> n >> m >> q;

while (q--)
{
int v, u;
cin >> v >> u;
g[v].push_back(u + N);
g[u + N].push_back(v);
}

int ans = 0;

for(int i = 1; i <= n; ++i)


if (!used[i])
{
dfs(i);
++ans;
}

for(int i = 1; i <= m; ++i)


if (!used[N + i])
{
dfs(N + i);
++ans;
}

cout << (ans - 1) << endl;


}

121
mt19937

Listing 9.3.19: 88040747-chemistry.cpp


// https://codeforces.com/contest/1012/submission/88040747
// https://codeforces.com/contest/1012/status

#include<cstdio> //execution time : 0.625 s

const int maxn = 2E+5 + 5;

int n, m, q;
int fa[maxn * 2];

int Getfather(int x)
{
if(fa[x] != x) fa[x] = Getfather(fa[x]);
return fa[x];
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

scanf("%d%d%d", &n, &m, &q);

for(int i = 1; i <= n; ++i)


fa[i] = i;

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


fa[n + j] = n + j;

while(q--)
{
int x, y;

scanf("%d%d", &x, &y),


y += n;

121
http://www.cplusplus.com/reference/random/mt19937/
CAPITOLUL 9. EJOI 2018 9.3. AB-STRINGS 358

int fx = Getfather(x),
fy = Getfather(y);

if(fx != fy) fa[fy] = fx;


}

int ans = 0;
for(int i = 1; i <= n + m; ++i)
if(fa[i] == i) ++ans;

printf("%d", ans - 1);


}

Listing 9.3.20: chem1.cpp


// https://kaloronahuang.com/oi/codeforces-round-500/

#include <bits/stdc++.h> // execution time : 0.984 s

using namespace std;

const int MAX_N = 4e5 + 200;

typedef pair<int, int> pii;

int n, m, q, sumx[MAX_N], sumy[MAX_N], color[MAX_N];


pii pts[MAX_N];
vector<int> G[MAX_N];

void dfs(int u, int org)


{
color[u] = org;
for (auto v : G[u])
if (color[v] == 0)
dfs(v, org);
}

int main()
{
std::freopen("../tests/167", "r", stdin);
std::freopen("chemistry.out", "w", stdout);

scanf("%d%d%d", &n, &m, &q);


for (int i = 1, u, v; i <= q; i++)
scanf("%d%d", &u, &v),
G[u].push_back(v + n),
G[v + n].push_back(u);

int ans = 0;
for (int i = 1; i <= n + m; i++)
if (color[i] == 0)
dfs(i, ++ans);

printf("%d\n", ans - 1);


return 0;
}

Listing 9.3.21: checkChemistry.cpp


#include "testlib.h"
#include <sstream>
#include <iostream>

using namespace std;

int main()
//int main(int argc, char * argv[])
{
setName("compare ordered sequences of signed int%d numbers", \
8 * sizeof(long long));

int argc=4;

char* argv[] =
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 359

{
(char*)"checker",
(char*)"../tests/167",
(char*)"chemistry.out",
(char*)"../tests/167.a",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerTestlibCmd(argc, argv);

int n = 0;
string firstElems;

while (!ans.seekEof() && !ouf.seekEof())


{
n++;
long long j = ans.readLong();
long long p = ouf.readLong();

if (j != p)
quitf(_wa, "%d%s numbers differ - expected: ’%s’, found: ’%s’", n,\
englishEnding(n).c_str(), vtos(j).c_str(), vtos(p).c_str());
else
if (n <= 5)
{
if (firstElems.length() > 0)
firstElems += " ";
firstElems += vtos(j);
}
}

if (n <= 5)
quitf(_ok, "%d number(s): \"%s\"", n, compress(firstElems).c_str());
else
quitf(_ok, "%d numbers", n);
}

9.3.3 *Rezolvare detaliată

9.4 Prime Tree


Problema 5 - Prime Tree 100 de puncte
A tree is a connected undirected graph that has no cycles. Consider a tree with n vertices,
labeled with integers 1, 2, ..., n. Let’s call an edge u, v  bad if there is an integer d % 1 such that
the label of u and the label of v are both divisible by d. For example, in the tree below there are
three bad edges: 6, 4 are both divisible by 2, 2, 6 are both divisible by 2, and 3, 6 are both
divisible by 3:

4 1

6 3

2 5

Your goal is to relabel vertices in such a way that the number of bad edges is as small as
possible. For example, if you relabel vertices of the tree shown above in the following way, there
will be only one bad edge 3, 6:
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 360

1 2

6 3

5 4

The less bad edges your tree will have the more points you will get.
This is an output-only problem. There are 10 input files available in the PCMS.
You need to run your program locally and only submit the answer file for each input
file.
Input
Each input file contains several test cases.
The first line of the input file contains the number of test cases in this input file.
The first line of test case description contains a single integer n, the number of the vertices in
the tree.
Each of the following n  1 lines contains two integers u and v (1 & u, v & n), vertices connected
by the edge.
All trees in a single file have the same number of vertices.
Output
For each test case print one line containing exactly n different integers from 1 to n — labels
assigned to vertices 1, 2, . . . , n.
Constraints

ˆ ***

Subtasks
For each input file, let the total number of edges in all test cases of this input file be M , the
X
total number of bad edges in your solution be X, and R M
. Your score for the input file is
calculated as following:

ˆ if R % 0.4, your score will be 0;

ˆ if 0.33 $ R & 0.4, your score will be 1;


ˆ if 0.26 $ R & 0.33, your score will be 2;
ˆ if 0.19 $ R & 0.26, your score will be 3;
ˆ if 0.12 $ R & 0.19, your score will be 4;

ˆ if 0.05 $ R & 0.12, your score will be 5;


ˆ if 0.01 $ R & 0.05, your score will be 6;
ˆ if 0.005 $ R & 0.01, your score will be 7;

ˆ if 0.001 $ R & 0.005, your score will be 8;


ˆ if 0 $ R & 0.001, your score will be 9;
ˆ if R 0, your score will be 10.

Example
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 361

standard input standard output


2 253146
6 512346
13
35
36
64
62
6
12
13
14
15
16
Comment ¯ Note
First test case is shown in the problem statement above. There is one bad edge 6, 3 after
relabeling, because both 6 and 3 are divisible by 3.
In the second test case there will be edges 5, 1, 5, 2, 5, 3, 5, 4 and 5, 6. None of them
are bad.
There are 10 edges in the input file and 1 bad edge in the answer. Thus, M 10, X 1,
R 0.1. According to the scoring table, this answer would get 5 points.
Tests have the following structure:
ˆ Input file 1 contains three trees on 7 vertices, depicted below from the left to the right:

4 1
5
2 1
1
2 4
4 6
6 2
7 5 7
7
5 3 3 3

ˆ Input files 2 and 3 contain 100 random trees on 10 and 30 vertices respectively.
ˆ Input files 4 to 8 contain various randomly generated trees with some special structure (e.g.
trees with many leaves, binary trees etc). Distribution of different kinds of trees is roughly
the same for all inputs.
ˆ Input files 9 and 10 contain randomly generated trees of 50 000 and 100 000 vertices respec-
tively.
Initially, label of vertices of all trees in all input files are random.
Timp maxim de executare/test: 10.0 secunde
Memorie: total 256 MB

9.4.1 *Indicaţii de rezolvare

***

9.4.2 Coduri sursă


CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 362

Listing 9.4.1: ds diams.cpp


using namespace std;

#include <bits/stdc++.h> // execution time : 26.555 s

template <typename T>


T input()
{
T res;
cin >> res;
return res;
}

pair<int, int> dfs_get(vector<vector<int>>& graph,


vector<char>& used,
vector<int>& used_this,
int cur_t, int v)
{
pair<int, int> largest = {0, v};
used_this[v] = cur_t;

for (int u: graph[v])


if (not used[u] and used_this[u] != cur_t)
{
auto rs = dfs_get(graph, used, used_this, cur_t, u);

largest.first += 1;
largest = max(largest, rs);
}

return largest;
}

bool get_path(vector<vector<int>>& graph, vector<char>& used,


vector<int>& used_this, int cur_t,
vector<int>& path, int target)
{
if (path.back() == target)
return true;

int v = path.back();
used_this[v] = cur_t;
for (int u: graph[v])
if (not used[u] and used_this[u] != cur_t)
{
path.push_back(u);

if (get_path(graph, used, used_this, cur_t, path, target))


return true;

path.pop_back();
}

return false;
}

void solve()
{
int n;
cin >> n;

vector<vector<int>> graph(n);
for (int i = 1; i != n; ++i)
{
int v = input<int>() - 1;
int u = input<int>() - 1;

graph[v].push_back(u);
graph[u].push_back(v);
}

vector<char> used(n);

vector<int> used_this(n);
int cur_t = 0;
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 363

vector<int> perm(n);
int p_free = 1;

while (true)
{
int i;
for (i = 0; i != n and used[i]; ++i) {}

if (i == n)
break;

int farthest = dfs_get(graph, used, used_this, ++cur_t, i).second;

int f2 = dfs_get(graph, used, used_this, ++cur_t, farthest).second;

vector<int> path = {i};


get_path(graph, used, used_this, ++cur_t, path, f2);

for (int x: path)


{
used[x] = 1;
perm[x] = p_free++;
}
}

for (int i = 0; i != n; ++i)


cout << perm[i] << " ";
cout << "\n";
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

for (int t = input<int>(); t != 0; --t)


solve();
}

Listing 9.4.2: ds zhfs.cpp


#include <bits/stdc++.h> // execution time : 7.156 s

using namespace std;

template <typename T>


T input()
{
T res;
cin >> res;
return res;
}

#define ALL(A) A.begin(), A.end()

std::minstd_rand rnd(31031999);

void zhfs(const vector<vector<int>>& graph,


vector<int>& perm, int v, int& counter)
{
perm[v] = counter++;

for (int u: graph[v])


if (perm[u] == -1)
zhfs(graph, perm, u, counter);
}

void solve()
{
int n = input<int>();

vector<vector<int>> graph(n);
for (int i = 1; i != n; ++i)
{
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 364

int v = input<int>() - 1;
int u = input<int>() - 1;

graph[v].push_back(u);
graph[u].push_back(v);
}

for (int i = 0; i != n; ++i)


std::shuffle(ALL(graph[i]), rnd);

int the_v = -1;


int the_score = 1e9;

std::uniform_int_distribution<int> dist(0, n - 1);


for (int retry = 0; retry != 100; ++retry)
{
int v = dist(rnd);

vector<int> perm(n, -1);


int counter = 1;

zhfs(graph, perm, v, counter);

int sc = 0;
for (int v = 0; v != n; ++v)
for (int u: graph[v])
if (std::__gcd(perm[v], perm[u]) > 1)
sc += 1;

if (sc < the_score)


the_score = sc, the_v = v;
}

vector<int> perm(n, -1);


int counter = 1;

zhfs(graph, perm, the_v, counter);

for (int i = 0; i != n; ++i)


cout << perm[i] << " ";
cout << "\n";
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

for (int t = input<int>(); t != 0; --t)


solve();
}

122
minstd rand
123
shuffle
124
uniform int distribution
125
gcd
Listing 9.4.3: full random.cpp
#include <bits/stdc++.h> // execution time : 0.813 s

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); ++i)


#define fore(i, b, e) for (int i = (int)(b); i <= (int)(e); ++i)
#define ford(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(), (x).end()
122
http://www.cplusplus.com/reference/random/minstd_rand/
123
http://www.cplusplus.com/reference/algorithm/shuffle/
124
https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution
125
https://www.geeksforgeeks.org/stdgcd-c-inbuilt-function-finding-gcd/
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 365

typedef vector<int> vi;


typedef pair<int, int> pii;
typedef long long i64;
typedef unsigned long long u64;
typedef long double ld;
typedef long long ll;

const int maxn = 100500;

int gcd(int a, int b)


{
while (b)
{
a %= b;
swap(a, b);
}
return a;
}

int n;
vi e[maxn];
int l[maxn];
int sc;

void scan()
{
cin >> n;
forn(i, n) e[i].clear();
forn(i, n-1)
{
int u, v;
cin >> u >> v;
--u, --v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
forn(i, n) sort(all(e[i]));
forn(i, n) l[i] = i+1;
}

bool pr[maxn];
void erath()
{
pr[0] = pr[1] = false;
for (int i = 2; i < maxn; ++i) if (!pr[i])
{
if ((i64)i*i > maxn) break;
for (int j = i*i; j < maxn; j += i) pr[j] = true;
}
}

mt19937 rr;
// mt19937 rr{random_device{}()};

int getScore()
{
int sc = 0;
forn(v, n)
for (int to: e[v])
if (to < v && gcd(l[v], l[to]) > 1)
++sc;
return sc;
}

bool iter()
{
int x = rr() % n;
int y = x;
while (y == x) y = rr() % n;

int cur = sc;


swap(l[x], l[y]);
int nxt = getScore();

if (nxt < cur)


CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 366

{
sc = nxt;
return true;
}
else
{
swap(l[x], l[y]);
}

return false;
}

ld perTest;
ld limit;
bool TL()
{
static int iter = 0;
if (++iter == 100)
{
iter = 0;
if (1.0 * clock() / CLOCKS_PER_SEC > limit)
{
return true;
}
}

return false;
}

int T;

void solve()
{
shuffle(l, l+n, rr);
forn(i, n) cout << l[i] << " ";
cout << endl;
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

erath();

int t;
cin >> t;
T = t;
forn(i, t)
{
scan();
limit = 1.0 * clock() / CLOCKS_PER_SEC + perTest;
solve();
}
// fill();

#ifdef LOCAL
cerr << "Time elapsed: " << clock() / 1000 << " ms" << endl;
#endif
return 0;
}

126
emplace back

Listing 9.4.4: many random.cpp


#include <bits/stdc++.h> // execution time : 13.482 s

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); ++i)


#define fore(i, b, e) for (int i = (int)(b); i <= (int)(e); ++i)
#define ford(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define pb push_back
126
http://candcplusplus.com/c-difference-between-emplace_back-and-push_back-function
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 367

#define fi first
#define se second
#define all(x) (x).begin(), (x).end()

typedef vector<int> vi;


typedef pair<int, int> pii;
typedef long long i64;
typedef unsigned long long u64;
typedef long double ld;
typedef long long ll;

const int maxn = 100500;

int gcd(int a, int b)


{
while (b)
{
a %= b;
swap(a, b);
}
return a;
}

int n;
vi e[maxn];
int l[maxn];
int sc;

void scan()
{
cin >> n;
forn(i, n) e[i].clear();
forn(i, n-1)
{
int u, v;
cin >> u >> v;
--u, --v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
forn(i, n) sort(all(e[i]));
forn(i, n) l[i] = i+1;
}

bool pr[maxn];
void erath()
{
pr[0] = pr[1] = false;
for (int i = 2; i < maxn; ++i)
if (!pr[i])
{
if ((i64)i*i > maxn) break;
for (int j = i*i; j < maxn; j += i)
pr[j] = true;
}
}

mt19937 rr;
// mt19937 rr{random_device{}()};

int getScore()
{
int sc = 0;
forn(v, n)
for (int to: e[v])
if (to < v && gcd(l[v], l[to]) > 1)
++sc;
return sc;
}

bool iter()
{
int x = rr() % n;
int y = x;
while (y == x) y = rr() % n;
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 368

int cur = sc;


swap(l[x], l[y]);
int nxt = getScore();

if (nxt < cur)


{
sc = nxt;
return true;
}
else
{
swap(l[x], l[y]);
}
return false;
}

ld perTest;
ld limit;
bool TL()
{
static int iter = 0;
if (++iter == 100)
{
iter = 0;
if (1.0 * clock() / CLOCKS_PER_SEC > limit)
{
return true;
}
}
return false;
}

int T;

void solve()
{
shuffle(l, l+n, rr);
pair<int, vi> best(getScore(), vi(l, l+n));
while (!TL())
{
shuffle(l, l+n, rr);
best = min(best, pair<int, vi>{getScore(), vi(l, l+n)});
}

forn(i, n)
cout << l[i] << " ";
cout << endl;
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

erath();

int t;
cin >> t;
T = t;
perTest = 7.0 / T;
forn(i, t)
{
scan();
limit = 1.0 * clock() / CLOCKS_PER_SEC + perTest;
solve();
}
// fill();

#ifdef LOCAL
cerr << "Time elapsed: " << clock() / 1000 << " ms" << endl;
#endif
return 0;
}

Listing 9.4.5: prime tree pashka.cpp


CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 369

#include <cstdio> // execution time : 1.442 s


#include <iostream>
#include <iomanip>
#include <vector>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <random>

#define MIN_INT -2147483648


#define MAX_INT 2147483647
#define MIN_LONG -9223372036854775808L
#define MAX_LONG 9223372036854775807L
#define PI 3.141592653589793238462643383279502884L

#define long long long int

using std::vector;
using std::map;
using std::set;
using std::string;
using std::pair;
using std::cin;
using std::cout;
using std::cerr;

// @author: pashka

vector<vector<int>> g;
vector<int> p;

vector<int> d[2];

void dfs(int x, int p, int c)


{
d[c].push_back(x);
for (int i = 0; i < g[x].size(); i++)
{
if (g[x][i] == p) continue;
dfs(g[x][i], x, 1 - c);
}
}

int gcd(int a, int b)


{
while (b > 0)
{
int c = a % b;
a = b;
b = c;
}
return a;
}

long seed = 263172936192361732LL;

int rand()
{
seed = 1726391263971263 * seed + 1729316729316293162;
seed ˆ= seed >> 24;
return (int) (seed & 0x7fffffff);
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

std::ios::sync_with_stdio(false);

int tn;
cin >> tn;
for (int tt = 0; tt < tn; tt++)
{
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 370

int n;
cin >> n;
g.clear();
g.resize(n);
for (int i = 0; i < n - 1; i++)
{
int x, y;
cin >> x >> y;
x--;
y--;
g[x].push_back(y);
g[y].push_back(x);
}

d[0].clear();
d[1].clear();

dfs(rand() % n, -1, 0);

if (d[0].size() < d[1].size())


{
swap(d[0], d[1]);
}

d[0].insert(d[0].end(), d[1].begin(), d[1].end());


//cerr << d[0].size() << " " << d[1].size() << "\n";

int j = 0;
p.clear();
p.resize(n);
for (int i = 2; i <= n; i += 2)
{
p[d[0][j++]] = i;
}
for (int i = 1; i <= n; i += 2)
{
p[d[0][j++]] = i;
}

set<pair<int, int>> bd;


vector<int> w(n);
for (int x = 0; x < n; x++)
{
for (int y : g[x])
{
if (gcd(p[x], p[y]) > 1)
{
w[x]++;
}
}

if (w[x] > 0)
bd.insert({w[x], x});
}

//cerr << bd.size() << "\n";

int c = 0;
while (bd.size() > 0)
{
c++;
if (c > 100 * n)
{
break;
}
//if (c % 100 == 0)
//{
// cerr << bd.size() << "\n";
//}

int x = bd.rbegin()->second;
int xx = rand() % n;
while ((xx == x) || (p[x] % 2 != p[xx] % 2))
{
xx = rand() % n;
}
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 371

for (int v : {x, xx})


{
for (int u : g[v])
{
if (gcd(p[v], p[u]) > 1)
{
bd.erase({w[v], v});
bd.erase({w[u], u});
w[v]--;
w[u]--;
if (w[v] > 0) bd.insert({w[v], v});
if (w[u] > 0) bd.insert({w[u], u});
}
}
}

std::swap(p[x], p[xx]);
for (int v : {x, xx})
{
for (int u : g[v])
{
if (gcd(p[v], p[u]) > 1)
{
bd.erase({w[v], v});
bd.erase({w[u], u});
w[v]++;
w[u]++;
bd.insert({w[v], v});
bd.insert({w[u], u});
}
}
}
}

for (int i = 0; i < n; i++)


{
cout << p[i] << " ";
}
cout << "\n";
}

return 0;
}

127
insert

Listing 9.4.6: print id.cpp


#include <bits/stdc++.h> // execution time : 0.814 s

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); ++i)


#define fore(i, b, e) for (int i = (int)(b); i <= (int)(e); ++i)
#define ford(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(), (x).end()

typedef vector<int> vi;


typedef pair<int, int> pii;
typedef long long i64;
typedef unsigned long long u64;
typedef long double ld;
typedef long long ll;

const int maxn = 100500;

int gcd(int a, int b)


{
while (b)
{
a %= b;
127
http://www.cplusplus.com/reference/vector/vector/insert/
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 372

swap(a, b);
}
return a;
}

int n;
vi e[maxn];
int l[maxn];
int sc;

void scan()
{
cin >> n;
forn(i, n) e[i].clear();
forn(i, n-1)
{
int u, v;
cin >> u >> v;
--u, --v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
forn(i, n) sort(all(e[i]));
forn(i, n) l[i] = i+1;
}

bool pr[maxn];

void erath()
{
pr[0] = pr[1] = false;
for (int i = 2; i < maxn; ++i) if (!pr[i])
{
if ((i64)i*i > maxn) break;
for (int j = i*i; j < maxn; j += i) pr[j] = true;
}
}

mt19937 rr;
// mt19937 rr{random_device{}()};

int getScore()
{
int sc = 0;
forn(v, n) for (int to: e[v]) if (to < v && gcd(l[v], l[to]) > 1) ++sc;
return sc;
}

bool iter()
{
int x = rr() % n;
int y = x;
while (y == x) y = rr() % n;

int cur = sc;


swap(l[x], l[y]);
int nxt = getScore();

if (nxt < cur)


{
sc = nxt;
return true;
}
else
{
swap(l[x], l[y]);
}
return false;
}

ld perTest;
ld limit;
bool TL()
{
static int iter = 0;
if (++iter == 100)
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 373

{
iter = 0;
if (1.0 * clock() / CLOCKS_PER_SEC > limit)
{
return true;
}
}
return false;
}

int T;

void solve()
{
// shuffle(l, l+n, rr);
forn(i, n) cout << l[i] << " "; cout << endl;
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

erath();

int t;
cin >> t;
T = t;
forn(i, t)
{
scan();
limit = 1.0 * clock() / CLOCKS_PER_SEC + perTest;
solve();
}
// fill();

#ifdef LOCAL
cerr << "Time elapsed: " << clock() / 1000 << " ms" << endl;
#endif

return 0;
}

Listing 9.4.7: sol 100.cpp


#include <cstdio> // execution time : 1.396 s
#include <iostream>
#include <iomanip>
#include <vector>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <random>

#define MIN_INT -2147483648


#define MAX_INT 2147483647
#define MIN_LONG -9223372036854775808L
#define MAX_LONG 9223372036854775807L
#define PI 3.141592653589793238462643383279502884L

#define long long long int

using std::vector;
using std::map;
using std::set;
using std::string;
using std::pair;
using std::cin;
using std::cout;
using std::cerr;

// @author: pashka

vector<vector<int>> g;
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 374

vector<int> p;

vector<int> d[2];

void dfs(int x, int p, int c)


{
d[c].push_back(x);
for (int i = 0; i < g[x].size(); i++)
{
if (g[x][i] == p) continue;
dfs(g[x][i], x, 1 - c);
}
}

int gcd(int a, int b)


{
while (b > 0)
{
int c = a % b;
a = b;
b = c;
}
return a;
}

long seed = 263172936192361732LL;

int rand()
{
seed = 1726391263971263 * seed + 1729316729316293162;
seed ˆ= seed >> 24;
return (int) (seed & 0x7fffffff);
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

std::ios::sync_with_stdio(false);

int tn;
cin >> tn;
for (int tt = 0; tt < tn; tt++)
{

int n;
cin >> n;
g.clear();
g.resize(n);
for (int i = 0; i < n - 1; i++)
{
int x, y;
cin >> x >> y;
x--;
y--;
g[x].push_back(y);
g[y].push_back(x);
}

while (true)
{
d[0].clear();
d[1].clear();
dfs(rand() % n, -1, 0);
if (d[0].size() < d[1].size())
{
swap(d[0], d[1]);
}
d[0].insert(d[0].end(), d[1].begin(), d[1].end());
// cerr << d[0].size() << " " << d[1].size() << "\n";
int j = 0;
p.clear();
p.resize(n);
for (int i = 2; i <= n; i += 2)
{
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 375

p[d[0][j++]] = i;
}
for (int i = 1; i <= n; i += 2)
{
p[d[0][j++]] = i;
}

set<pair<int, int>> bd;


vector<int> w(n);
for (int x = 0; x < n; x++)
{
for (int y : g[x])
{
if (gcd(p[x], p[y]) > 1)
{
w[x]++;
}
}
if (w[x] > 0)
bd.insert({w[x], x});
}

int c = 0;
while (bd.size() > 0)
{
c++;
if (c > 500 * n)
{
break;
}
if (c % 100 == 0)
{
// cerr << bd.size() << "\n";
}
int x = bd.rbegin()->second;
int xx = rand() % n;
while ((xx == x) || (p[x] % 2 != p[xx] % 2))
{
xx = rand() % n;
}

for (int v : {x, xx})


{
for (int u : g[v])
{
if (gcd(p[v], p[u]) > 1)
{
bd.erase({w[v], v});
bd.erase({w[u], u});
w[v]--;
w[u]--;
if (w[v] > 0) bd.insert({w[v], v});
if (w[u] > 0) bd.insert({w[u], u});
}
}
}

std::swap(p[x], p[xx]);

for (int v : {x, xx})


{
for (int u : g[v])
{
if (gcd(p[v], p[u]) > 1)
{
bd.erase({w[v], v});
bd.erase({w[u], u});
w[v]++;
w[u]++;
bd.insert({w[v], v});
bd.insert({w[u], u});
}
}
}
}
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 376

if (!bd.empty())
{
cerr << "retry\n";
continue;
}

for (int i = 0; i < n; i++)


{
cout << p[i] << " ";
}
cout << "\n";
// cerr << bd.size() << std::endl;
break;
}
}

return 0;
}

Listing 9.4.8: solve is.cpp


#include <bits/stdc++.h> // execution time : 0.896 s

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); ++i)


#define fore(i, b, e) for (int i = (int)(b); i <= (int)(e); ++i)
#define ford(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(), (x).end()

typedef vector<int> vi;


typedef pair<int, int> pii;
typedef long long i64;
typedef unsigned long long u64;
typedef long double ld;
typedef long long ll;

const int maxn = 100500;

int gcd(int a, int b)


{
while (b)
{
a %= b;
swap(a, b);
}
return a;
}

int n;
vi e[maxn];
int l[maxn];
int sc;

void scan()
{
cin >> n;
forn(i, n) e[i].clear();
forn(i, n-1)
{
int u, v;
cin >> u >> v;
--u, --v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
forn(i, n) sort(all(e[i]));
forn(i, n) l[i] = i+1;
}

bool pr[maxn];
void erath()
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 377

{
pr[0] = pr[1] = false;
for (int i = 2; i < maxn; ++i) if (!pr[i])
{
if ((i64)i*i > maxn) break;
for (int j = i*i; j < maxn; j += i) pr[j] = true;
}
}

mt19937 rr;
// mt19937 rr{random_device{}()};

set<pii> bad;
vector<pii> badq;

void fillbad()
{
forn(v, n) for (int to: e[v]) if (to < v && gcd(l[v], l[to]) > 1)
{
bad.emplace(to, v);
badq.emplace_back(to, v);
}
}

bool iter2()
{
if (bad.empty())
{
assert(sc == 0);
return true;
}
int id = rr() % badq.size();
while (!bad.count(badq[id]))
{
swap(badq[id], badq.back());
badq.pop_back();
id = rr() % badq.size();
}

int x;
if (rr() % 2) x = badq[id].first;
else x = badq[id].second;

int v = badq[id].first ˆ badq[id].second ˆ x;

int y = x;
while (gcd(l[y], l[v]) > 1) y = rr() % n;

int cur = 0;
for (int to: e[x]) cur += gcd(l[x], l[to]) > 1;
for (int to: e[y]) cur += gcd(l[y], l[to]) > 1;
if (binary_search(all(e[x]), y)) cur -= gcd(l[x], l[y]) > 1;

int nxt = 0;
for (int to: e[x]) nxt += gcd(l[y], l[to]) > 1;
for (int to: e[y]) nxt += gcd(l[x], l[to]) > 1;
if (binary_search(all(e[x]), y)) nxt -= gcd(l[x], l[y]) > 1;

if (nxt >= cur) return false;

sc += nxt - cur;
for (int t: e[x]) if (gcd(l[x], l[t]) > 1)
bad.erase({min(t, x), max(t, x)});

for (int t: e[y]) if (gcd(l[y], l[t]) > 1)


bad.erase({min(t, y), max(t, y)});

swap(l[x], l[y]);

for (int t: e[x]) if (gcd(l[x], l[t]) > 1)


{
pii p{min(t, x), max(t, x)};
if (bad.emplace(p).second)
{
badq.push_back(p);
}
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 378

for (int t: e[y]) if (gcd(l[y], l[t]) > 1)


{
pii p{min(t, y), max(t, y)};
if (bad.emplace(p).second)
{
badq.push_back(p);
}
}

return true;
}

bool iter()
{
int x = rr() % n;
int y = x;
while (y == x) y = rr() % n;

int cur = 0;
for (int to: e[x]) cur += gcd(l[x], l[to]) > 1;
for (int to: e[y]) cur += gcd(l[y], l[to]) > 1;
if (binary_search(all(e[x]), y)) cur -= gcd(l[x], l[y]) > 1;

int nxt = 0;
for (int to: e[x]) nxt += gcd(l[y], l[to]) > 1;
for (int to: e[y]) nxt += gcd(l[x], l[to]) > 1;
if (binary_search(all(e[x]), y)) nxt -= gcd(l[x], l[y]) > 1;

if (nxt < cur)


{
sc += nxt - cur;
swap(l[x], l[y]);
return true;
}
return false;
}

int getScore()
{
int sc = 0;
forn(v, n) for (int to: e[v]) if (to < v && gcd(l[v], l[to]) > 1) ++sc;
return sc;
}

bool inq[maxn];

void fill()
{
memset(l, 0, sizeof l);
vi q;
forn(i, n) if (e[i].size() == 1) q.push_back(i), inq[i] = true;
shuffle(all(q), rr);

set<int> rem;
vi extra{1};
fore(i, 2, n)
{
if (!pr[i] && i > n/2) extra.push_back(i);
else rem.insert(i);
}

forn(i, n)
{
int v = q[i];
assert(l[v] == 0);
for (int to: e[v])
if (!inq[to])
q.push_back(to), inq[to] = true;

if (rem.empty())
{
l[v] = *extra.begin();
extra.erase(extra.begin());
continue;
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 379

bool done = false;


int cnt = 0;
forn(III, 1)
{
for (int x: rem)
{
++cnt;
bool ok = true;
for (int to: e[v]) if (l[to])
{
if (gcd(l[to], x) > 1) { ok = false; break; }
}

if (ok)
{
l[v] = x;
rem.erase(x);
done = true;
break;
}
}

if (done) break;
}

if (!done)
{
if (!extra.empty())
{
l[v] = *extra.begin();
extra.erase(extra.begin());
}
else
{
l[v] = *rem.begin();
rem.erase(rem.begin());
}
}
}

// forn(i, n) cout << l[i] << " "; cout << endl;
cerr << getScore() << endl;
// cerr << rem.size() << endl;
// cerr << set<int>(l, l+n).size() << endl;
}

int T;

void solve()
{
// fill();
// return;
shuffle(l, l+n, rr);
sc = getScore();
// fillbad();
// cerr << "n = " << n << endl;
// cerr << "start sc: " << sc << endl;

forn(i, 1000 / T)
//forn(i, 10000000 / T) // depaseste timpul !
{
if (iter())
{
// cerr << i << " " << sc << endl;
}
// if (iter2())
//{
// cerr << i << " " << sc << endl;
// }
if (sc == 0) break;
}
// cerr << "end sc: " << sc << endl;
// cerr << endl;
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 380

forn(i, n)
cout << l[i] << " "; cout << endl;
// cout << sc << endl;
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

erath();

int t;
cin >> t;
T = t;
forn(i, t)
{
scan();
solve();
}
// fill();

return 0;
}

Listing 9.4.9: solve is no local.cpp


#include <bits/stdc++.h> // execution time : 10.393 s

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); ++i)


#define fore(i, b, e) for (int i = (int)(b); i <= (int)(e); ++i)
#define ford(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(), (x).end()

typedef vector<int> vi;


typedef pair<int, int> pii;
typedef long long i64;
typedef unsigned long long u64;
typedef long double ld;
typedef long long ll;

const int maxn = 100500;

int gcd(int a, int b)


{
while (b)
{
a %= b;
swap(a, b);
}
return a;
}

int n;
vi e[maxn];
int l[maxn];
int sc;

void scan()
{
cin >> n;
forn(i, n) e[i].clear();
forn(i, n-1)
{
int u, v;
cin >> u >> v;
--u, --v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 381

forn(i, n) sort(all(e[i]));
forn(i, n) l[i] = i+1;
}

bool pr[maxn];

void erath()
{
pr[0] = pr[1] = false;
for (int i = 2; i < maxn; ++i) if (!pr[i])
{
if ((i64)i*i > maxn) break;
for (int j = i*i; j < maxn; j += i) pr[j] = true;
}
}

mt19937 rr;
// mt19937 rr{random_device{}()};

int getScore()
{
int sc = 0;
forn(v, n)
for (int to: e[v])
if (to < v && gcd(l[v], l[to]) > 1)
++sc;
return sc;
}

bool iter()
{
int x = rr() % n;
int y = x;
while (y == x) y = rr() % n;

int cur = sc;


swap(l[x], l[y]);
int nxt = getScore();

if (nxt < cur)


{
sc = nxt;
return true;
}
else
{
swap(l[x], l[y]);
}
return false;
}

ld perTest;
ld limit;

bool TL()
{
static int iter = 0;
if (++iter == 100)
{
iter = 0;
if (1.0 * clock() / CLOCKS_PER_SEC > limit)
{
return true;
}
}
return false;
}

int T;

void solve()
{
shuffle(l, l+n, rr);
sc = getScore();
while (!TL())
{
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 382

iter();
if (sc == 0) break;
}
forn(i, n)
cout << l[i] << " ";
cout << endl;
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

erath();

int t;
cin >> t;
T = t;
perTest = 7.0 / T;
forn(i, t)
{
scan();
limit = 1.0 * clock() / CLOCKS_PER_SEC + perTest;
solve();
}
// fill();

return 0;
}

Listing 9.4.10: solve qoo2p5.cpp


#include <bits/stdc++.h> //execution time : 24.297 s

using namespace std;

const int N = (int) 1e5 + 123;

mt19937 rnd(42);

int n;
vector<int> g[N];
int best_ans;
vector<int> ans;

void upd(vector<int> &v)


{
int bad = 0;
for (int i = 0; i < n; i++)
{
for (int j : g[i])
{
if (j < i)
{
continue;
}

if (__gcd(v[i], v[j]) > 1)


{
bad++;
}
}
}

if (bad < best_ans)


{
best_ans = bad;
ans = v;
}
}

void dfs(int v, vector<int> &res, int &ptr, int f = -1)


{
res[v] = ptr++;
for (int u : g[v])
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 383

{
if (u != f)
{
dfs(u, res, ptr, v);
}
}
}

void upd_dfs(int v)
{
for (int i = 0; i < n; i++)
{
shuffle(g[i].begin(), g[i].end(), rnd);
}
int ptr = 1;
vector<int> res(n);
dfs(v, res, ptr);
upd(res);
}

void upd_deg()
{
vector<int> divisors(n + 1);
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j += i)
{
divisors[j]++;
}
}

vector<int> ord(n), vert(n);


for (int i = 1; i <= n; i++)
{
ord[i - 1] = i;
vert[i - 1] = i - 1;
}

sort(ord.begin(), ord.end(),
[&] (int x, int y)
{
return make_pair(divisors[x], x) > make_pair(divisors[y], y);
});

sort(vert.begin(), vert.end(),
[&] (int x, int y)
{
return (int) g[x].size() > (int) g[y].size();
});

vector<int> res(n);
for (int v : vert)
{
res[v] = ord.back();
ord.pop_back();
}

upd(res);
}

void solve()
{
cin >> n;
for (int i = 0; i <= n; i++)
{
g[i].clear();
}

for (int i = 0; i < n - 1; i++)


{
int u, v;
cin >> u >> v;
u--;
v--;
g[u].push_back(v);
g[v].push_back(u);
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 384

ans.resize(n);
for (int i = 0; i < n; i++)
{
ans[i] = i + 1;
}
shuffle(ans.begin(), ans.end(), rnd);

best_ans = N;
upd(ans);
upd_dfs(0);
for (int iter = 0; iter < min(n, 300); iter++)
{
upd_dfs(rnd() % n);
}
upd_deg();

for (int i = 0; i < n; i++)


{
cout << ans[i] << " ";
}
cout << "\n";
}

int main()
{
std::freopen("../tests/11", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);

int t;
cin >> t;
while (t--)
{
solve();
}

return 0;
}

Listing 9.4.11: checkPrime-tree.cpp


#include "testlib.h"
#include <assert.h>
#include <vector>
#include <iostream>

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); ++i)

int gcd(int a, int b)


{
while (b)
{
a %= b;
swap(a, b);
}
return a;
}

int main()
//int main(int argc, char *argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/11",
(char*)"../tests/11.a",
CAPITOLUL 9. EJOI 2018 9.4. PRIME TREE 385

(char*)"prime-tree.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerTestlibCmd(argc, argv);

#ifdef PCMS2
// PCMS on EJOI needed some extra info in the input for partial scoring \
with groups
inf.readWord();
#endif

int tn = inf.readInt();
int m = 0;
int x = 0;

for (int tt = 0; tt < tn; tt++)


{
int n = inf.readInt();
vector<int> u(n - 1), v(n - 1);
forn(i, n-1)
{
u[i] = inf.readInt() - 1;
v[i] = inf.readInt() - 1;
}

vector<int> l(n);
map<int, int> occurred;
forn(i, n)
{
l[i] = ouf.readInt(1, n, format("test #%d, label of vertex #%d", tt + 1, i +
1));
if (occurred.count(l[i]))
{
quitf(_wa, "test #%d: label %d is duplicated on vertexes %d and %d",
tt + 1, l[i] + 1, occurred[l[i]] + 1, i + 1);
}
occurred[l[i]] = i;
}

forn(i, n-1)
{
x += gcd(l[u[i]], l[v[i]]) != 1;
}
m += n - 1;
}

double score = 1.0 * x / m;


vector<pair<double, int>> grades
{
{0, 10},
{0.001, 9},
{0.005, 8},
{0.01, 7},
{0.05, 6},
{0.12, 5},
{0.19, 4},
{0.26, 3},
{0.33, 2},
{0.4, 1},
{1, 0},
};

for (auto grade : grades)


{
if (score <= grade.first)
{
if (grade.second == 10)
{
quitf(_ok, "Full score, X=M=%d", x);
}
else
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 386

{
quitp(grade.second, "X=%d, M=%d, R=%.4f", x, m, score);
}
}
}

assert(false);
}

9.4.3 *Rezolvare detaliată

9.5 Cycle sort


Problema 6 - Cycle sort 100 de puncte
You are given an array of n positive integers a1 , a2 , . . . , an . You can perform the following
operation any number of times: select several distinct indices i1 , i2 , . . . , ik (1 & ij & n) and move
the number standing at the position i1 to the position i2 , the number at the position i2 to the
position i3 , . . . , the number at the position ik to the position i1 . In other words, the operation
cyclically shifts elements: i1 i2 . . . ik i1 .
For example, if you have n 4, an array a1 10, a2 20, a3 30, a4 40, and you choose
three indices i1 2, i2 3, i3 4, then the resulting array would become a1 10, a2 40, a3
20, a4 30.
Your goal is to make the array sorted in non-decreasing order with minimum number of opera-
tions. The additional constraint is that the sum of cycle lengths over all operations should be less
than or equal to a number s. If it’s impossible to sort the array while satisfying that constraint,
your solution should report that as well.
Input
The first line of the input contains two integers n and s (1 & n & 200 000, 0 & s & 200 000)—the
number of elements in the array and the upper bound on the sum of cycle lengths.
The next line contains n integers a1 , a2 , . . . , an —elements of the array (1 & ai & 10 ).
9

Output
If it’s impossible to sort the array using cycles of total length not exceeding s, print a single
number “-1” (quotes for clarity).
Otherwise, print a single number q— the minimum number of operations required to sort the
array.
On the next 2q lines print descriptions of operations in the order they are applied to the array.
The description of i-th operation begins with a single line containing one integer k (1 & k & n)—
the length of the cycle (that is, the number of selected indices). The next line should contain k
distinct integers i1 , i2 , . . . , ik (1 & ij & n)—the indices of the cycle.
The sum of lengths of these cycles should be less than or equal to s, and the array should be
sorted after applying these q operations.
If there are several possible answers with the optimal q, print any of them.
Constraints

ˆ ***

Subtasks

ˆ Subtask 1 (5 points) n, s & 2 and all elements of the array are either 1 or 2.
ˆ Subtask 2 (5 points) n & 5.
ˆ Subtask 3 (5 points) All elements of the array are either 1 or 2.
ˆ Subtask 4 (10 points) Array contains numbers from 1 to n only, each number appears
exactly once, s 2 n.
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 387

ˆ Subtask 5 (10 points) Array contains numbers from 1 to n only, each number appears
exactly once, n & 1000.
ˆ Subtask 6 (15 points) Array contains numbers from 1 to n only, each number appears
exactly once.

ˆ Subtask 7 (15 points) s 2 n.


ˆ Subtask 8 (15 points) n & 1000.
ˆ Subtask 9 (20 points) No additional constraints.

Example
standard input standard output
55 1
32311 5
14235
4 3 -1
2 143
2 0 0
2 2
6 9 2
6 54321 6
1 62534
3
3 21
68 3
654321 2
3 4
4
1 625
2
2 1
Comment ¯ Note
In the first example, it’s also possible to sort the array with two operations of total length 5:
first apply the cycle 1 4 1 (of length 2), then apply the cycle 2 3 t 2 (of length
3). However, it would be wrong answer as you’re asked to use the minimal possible number of
operations, which is 1 in that case.
In the second example, it’s possible to the sort the array with two cycles of total length 4
(1 2 1 and 3 4 3). However, it’s impossible to achieve the same using shorter cycles,
which is required by s 3.
In the third example, the array is already sorted, so no operations are needed. Total length of
empty set of cycles is considered to be zero.
Notice that examples 1 and 3 contain duplicate numbers, so they do not satisfy requirements
for subtasks 4, 5 and 6. Examples 2, 4, and 5 satisfy requirements for subtasks 5 and 6.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 512 MB

9.5.1 Indicaţii de rezolvare

https://codeforces.com/blog/entry/60920
https://codeforces.com/topic/61265/en10
Let’s solve an array if a is permutation and the sum of cycle sizes is unlimited.
128
It’s known that each permutation is composition of some non-intersecting cycles.
If permutation is sorted answer is 0.
128
https://en.wikipedia.org/wiki/Cyclic_permutation
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 388

If permutation is 1 cycle and some fixed indexes answer is 1, you should take this cycle to get
this answer.
If permutation is composition of ' 2 cycles answer should be ' 2, because it’s impossible to
use 1 cycle. In this case it’s possible to make the answer with 2 cycles.
Let’s define inversed permutation for our permutation as composition of this cycles p11
p12 ... p1k1 p11 , ..., pm1 pm2 ... pmkm pm1  (all cycles, except cycles with
length 1).
We can sort permutations using this 2 cycles: p11 p12 ... p1k1 p21 ... pm1
pm2 ... pmkm p11  (all cycles, written in a row) and pm1 p m11 ...p11 pm1  (first
elements of cycles in inversed order).
This solution used sum of cycle sizes t  m, there t - number of non-fixed elements and m -
number of cycles with size 1.
Now let’s solve problem for permutation, if we can use sum of cycle sizes & s.
Each solution should use sum of cycle sizes ' t, because we should shift each non-fixed element.
So if s $ t answer is 1.
Let’s call each cycle from m cycles bad, if each of it’s element shifted exactly 1 time in the
answer. It can be proved, that each bad cycle should be used in the answer.
Let’s define x as number of bad cycles and y number of other cycles. So x  y m.
Sum of cycles in the answer ' t  y.
So, t  y & s ==¿ y & s  t ==¿ x my ' m  t  s.
Number of cycles in the answer ' x  min y, 2.
It’s true, because each of x bad cycles are in answer and other y cycles can be sorted using
' min y, 2 cycles. We should take x as maximal as possible, because if we increase x by 1
sum min y, 2  x won’t increase. Minimal x, that we can use is max 0, m  t  s. So we get
y m  max 0, m  t  s m  min s  t  m, 0 min s  t, 0 s  t (because s ' t).
So answer in this case if max 0, m  t  s  min s  t, 2.
We can build it, if we use x max 0, m  t  s any of m cycles, and for other y s  t cycles
do same construction as in previous case.
Let’s solve problem for array.
Let’s define b as sorted array a and t as count of 1 & i & n, such that ai j bi .
Our solution will sort some permutation p, such that api bi , for all i.
Answer for permutation is max 0, m  t  s  min s  t, 2.
In this formula s and t are fixed. So to minimize the answer we should minimize m. So we
should sort array using array with as minimal as possible number of cycles. To do it let’s build
directed graph: vertexes is number in array, let’s add edge from ai to bi for all i, such that ai j bi .
It’s easy to see, that we should take euler cycle in each component of this graph and build
permutation with this cycles to make number of cycles in permutation as minimal as possible.
To code this solution without building graph let’s take first any sorting permutation. After
that, we can merge cycles. If ai aj and i and j in different cycles of permutation, we can merge
this cycles using swap pi , pj .
129
So let’s merge all possible cycles using DSU for components.
Time complexity: O n log n.
Jury’s solution (by isaf27): 40973023
(https://codeforces.com/contest/1012/submission/40973023)

9.5.2 Coduri sursă

Listing 9.5.1: cycle sort ad.cpp


#include <bits/stdc++.h> // execution time : 0.781 s

using namespace std;

#define se second
#define fi first
#define forn(i, n) for (int i = 0; i < n; i++)
#define sz(a) (int)a.size()
#define mp make_pair
129
https://cp-algorithms.com/data_structures/disjoint_set_union.html
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 389

int n, sm, use[500000];


vector<int> a, b, e[500000], ce[500000], head(500000, 0);
vector<vector<int> > vv;

vector<pair<int, int> > pq;

void dfs(int nu, int pp)


{
use[nu] = 1;
for (;head[nu] < sz(e[nu]);)
{
head[nu]++;
int prv = ce[nu][head[nu] - 1];
dfs(e[nu][head[nu] - 1], prv);
vv[sz(vv) - 1].push_back(prv);
}
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

iostream::sync_with_stdio(0);

cin >> n >> sm;


a.resize(n);
forn (i, n)
{
cin >> a[i];
pq.push_back(mp(a[i], i));
}

sort(pq.begin(), pq.end());

int m = -1;
forn (i, n)
{
if (i == 0 || pq[i].fi != pq[i - 1].fi)
{
m++;
}
a[pq[i].se] = m;
b.push_back(m);
}

forn (i, n)
{
if (a[i] != b[i])
{
e[a[i]].push_back(b[i]);
ce[a[i]].push_back(i);
}
}

int csum = 0;
forn (i, m + 1)
{
if (use[i] == 0 && sz(e[i]))
{
vv.push_back(vector<int> (0));
dfs(i, -1);
csum += sz(vv.back());
}
}

int cnt = sz(vv);


if (sz(vv) == 1) cnt--;

if (csum + cnt > sm)


{
if (csum <= sm)
{
int ct = sm - csum;
if (ct > 1)
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 390

cout << sz(vv) - ct + 2 << "\n";


else
cout << sz(vv) << "\n";

if (ct > 1)
{
int sm1 = 0;
forn (i, ct)
{
sm1 += sz(vv[i]);
}
cout << sm1 << "\n";
vector<int> cpq;
forn (i, ct)
{
for (int v : vv[i])
cout << v + 1 << " ";
cpq.push_back(vv[i][0]);
}
cout << "\n";
cout << sz(cpq) << "\n";
reverse(cpq.begin(), cpq.end());
for (int v : cpq) cout << v + 1 << " ";
}

if (ct > 1)
for (int j = ct; j < sz(vv); j++)
{
cout << sz(vv[j]) << "\n";
for (int q : vv[j]) cout << q + 1 << " ";
cout << "\n";
}
else
for (int j = 0; j < sz(vv); j++)
{
cout << sz(vv[j]) << "\n";
for (int q : vv[j]) cout << q + 1 << " ";
cout << "\n";
}

return 0;
}

cout << "-1";


return 0;
}

if (sz(vv) == 0)
{
cout << "0";
return 0;
}

if (sz(vv) == 1)
{
cout << "1\n";
}
else
cout << "2\n";

cout << csum << "\n";


forn (j, sz(vv))
for (int c : vv[j])
if (c != -1) cout << c + 1 << " ";

if (sz(vv) > 1)
{
cout << "\n" << sz(vv) << "\n";
vector<int> cpq;
forn (j, sz(vv)) cpq.push_back(vv[j][0]);
reverse(cpq.begin(), cpq.end());
for (int v : cpq) cout << v + 1 << " ";
}
}

Listing 9.5.2: gr ok.cpp


CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 391

#include <bits/stdc++.h> //execution time : 1.109 s

using namespace std;

const int MX = 500 * 1000 + 7;

int a[MX];
int b[MX];
int val[MX];
bool was[MX];
bool bad[MX];

vector<vector<int> > findCycs(int n)


{
memset(was, 0, sizeof(was));
vector<vector<int> > ans;
for (int i = 1; i <= n; i++)
{
if (!bad[i])
{
continue;
}
if (!was[i] && a[i] != i)
{
int v = i;
vector<int> cyc;
while (!was[v])
{
cyc.push_back(v);
was[v] = true;
v = a[v];
}
ans.push_back(cyc);
}
}
return ans;
}

void applyCyc(const vector<int>& cyc)


{
vector<int> vals;
for (int x : cyc)
{
vals.push_back(a[x]);
}
for (int i = 0; i < cyc.size(); i++)
{
a[cyc[i]] = vals[(i + cyc.size() - 1) % cyc.size()];
}
}

vector<int> gr[MX];

class TDisjointSetsUnion
{
public:
TDisjointSetsUnion(size_t size)
: parent(size, size)
, rank(size, 1)
{
}

bool AreInSameSet(size_t u, size_t v) const


{
if (u >= parent.size() || v >= parent.size())
{
throw std::out_of_range("DSU error!");
}

return GetParent(u) == GetParent(v);


}

void Join(size_t u, size_t v)


{
if (u >= parent.size() || v >= parent.size())
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 392

{
throw std::out_of_range("DSU error!");
}

u = GetParent(u);
v = GetParent(v);

if (u == v)
{
return;
}
if (rank[u] < rank[v])
{
std::swap(u, v); // smaller-to-larger heuristics
}
rank[u] += rank[v];
parent[v] = u;
}

private:
mutable std::vector<size_t> parent;
std::vector<size_t> rank;

size_t GetParent(size_t vertex) const noexcept


{
if (parent[vertex] == parent.size())
{
return vertex;
}

size_t newParent = GetParent(parent[vertex]); // compressing path


// heuristics
parent[vertex] = newParent;
return newParent;
}
};

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);

int n, k;
cin >> n >> k;
vector<pair<int, int> > perestanovocka;

vector<int> comp;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
comp.push_back(a[i]);
b[i] = a[i];
}

sort(b + 1, b + 1 + n);

vector<int> BADS;
for (int i = 1; i <= n; i++)
{
if (a[i] != b[i])
{
BADS.push_back(i);
bad[i] = true;
}
}

if (is_sorted(a + 1, a + 1 + n))
{
cout << 0 << "\n";
return 0;
}
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 393

sort(comp.begin(), comp.end());

comp.resize(unique(comp.begin(), comp.end()) - comp.begin());

for (int i = 1; i <= n; i++)


{
if (!bad[i]) continue;
a[i] = lower_bound(comp.begin(), comp.end(), a[i]) - comp.begin() + 1;
b[i] = a[i];
gr[a[i]].push_back(i);
perestanovocka.emplace_back(a[i], i);
}

sort(perestanovocka.begin(), perestanovocka.end());

for (int pozicia = 0; pozicia < perestanovocka.size(); pozicia++)


{
a[perestanovocka[pozicia].second] = BADS[pozicia];
}

TDisjointSetsUnion dsu(n + 1);


{
auto cycs = findCycs(n);
for (const auto& cyc : cycs)
{
for (int x : cyc)
{
dsu.Join(cyc[0], x);
}
}
}

for (int i = 1; i <= n; i++)


{
vector<int> cyc;
int last = -1;
for (int j = 0; j < gr[i].size(); j++)
{
int pos = gr[i][j];
if (a[pos] == pos)
{
continue;
}

if (last == -1)
{
cyc.push_back(pos);
}
else
if (!dsu.AreInSameSet(last, pos))
{
cyc.push_back(pos);
dsu.Join(last, pos);
}
last = pos;
}
applyCyc(cyc);
}

auto cycs = findCycs(n);


sort(cycs.begin(), cycs.end(),
[&](const auto& x, const auto& y) { return x.size() < y.size(); });

int more = k;
for (const auto& cyc : cycs)
{
more -= cyc.size();
}

if (more < 0)
{
cout << -1 << "\n";
return 0;
}

vector<vector<int> > ans;


CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 394

int tomerge = min(int(cycs.size()), more);


if (tomerge > 1)
{
vector<int> mergeCyc;
for (int i = 0; i < tomerge; i++)
{
mergeCyc.push_back(cycs[i][0]);
}
ans.push_back(mergeCyc);
applyCyc(mergeCyc);
}

cycs = findCycs(n);

for (const auto& cyc : cycs)


{
applyCyc(cyc);
ans.push_back(cyc);
}

cout << ans.size() << "\n";


for (const auto& cyc : ans)
{
cout << cyc.size() << "\n";
for (int x : cyc)
{
cout << x << " ";
}
cout << "\n";
}

return 0;
}

Listing 9.5.3: ig ok.cpp


#include <cmath> //execution time : 1.638 s
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <set>
#include <map>
#include <list>
#include <time.h>
#include <math.h>
#include <random>
#include <deque>
#include <queue>
#include <cassert>
#include <unordered_map>
#include <unordered_set>
#include <iomanip>
#include <bitset>
#include <sstream>

using namespace std;

typedef long long ll;

mt19937 rnd(228);

int ans(int n, int s, vector <int> a)


{
vector <bool> u(n);
vector <vector <int> > b;
for (int i = 0; i < n; i++)
{
if (i == a[i] || u[i])
{
continue;
}
vector <int> p;
int x = i;
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 395

while (!u[x])
{
p.push_back(x);
u[x] = 1;
x = a[x];
}
b.push_back(p);
}
int sum = 0;
for (auto c : b)
{
sum += c.size();
}
if (sum > s)
{
return 1e9;
}
s -= sum;
int hv = 0;
for (int i = 3; i <= (int) b.size(); i++)
{
if (s >= i)
{
hv = i;
}
}
if (hv == 0)
{
return b.size();
}
else
{
return 2 + (int) b.size() - hv;
}
}

void print(int n, int s, vector <int> a)


{
vector <bool> u(n);
vector <vector <int> > b;
for (int i = 0; i < n; i++)
{
if (i == a[i] || u[i])
{
continue;
}
vector <int> p;
int x = i;
while (!u[x])
{
p.push_back(x);
u[x] = 1;
x = a[x];
}
b.push_back(p);
}
int sum = 0;
for (auto c : b)
{
sum += c.size();
}
if (sum > s)
{
cout << -1 << ’\n’;
return;
}
s -= sum;
int hv = 0;
for (int i = 3; i <= (int) b.size(); i++)
{
if (s >= i)
{
hv = i;
}
}
if (hv == 0)
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 396

{
cout << b.size() << ’\n’;
for (auto c : b)
{
cout << c.size() << ’\n’;
for (int x : c)
{
cout << x + 1 << ’ ’;
}
cout << ’\n’;
}
}
else
{
cout << 2 + (int) b.size() - hv << ’\n’;
for (int i = hv; i < (int) b.size(); i++)
{
cout << b[i].size() << ’\n’;
for (int x : b[i])
{
cout << x + 1 << ’ ’;
}
cout << ’\n’;
}
int s = 0;
for (int i = 0; i < hv; i++) s += (int) b[i].size();
cout << s << ’\n’;
for (int i = 0; i < hv; i++)
{
for (int x : b[i])
{
cout << x + 1 << ’ ’;
}
cout << ’\n’;
}
cout << hv << ’\n’;
for (int i = hv - 1; i >= 0; i--)
{
cout << b[i][0] + 1 << ’ ’;
}
cout << ’\n’;
}
}

const int N = 5e5 + 7;

int par[N];

int get(int v)
{
if (v == par[v])
{
return v;
}
else
{
return par[v] = get(par[v]);
}
}

void uni(int u, int v)


{
par[get(u)] = get(v);
}

vector <pair <int, int> > g[N];


bool vis[N];
bool u[N];

vector <int> cyc;

void dfs(int v)
{
vis[v] = true;
while (!g[v].empty())
{
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 397

int to = g[v].back().first;
int ind = g[v].back().second;
g[v].pop_back();
if (u[ind])
{
continue;
}
else
{
u[ind] = 1;
dfs(to);
}
}
cyc.push_back(v);
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);
int n, s;
cin >> n >> s;
vector <int> a(n), h(n), ind(n);
vector <int> p;
for (int i = 0; i < n; i++)
{
par[i] = i;
cin >> a[i];
ind[i] = i;
p.push_back(a[i]);
}
sort(p.begin(), p.end());
p.resize(unique(p.begin(), p.end()) - p.begin());
sort(ind.begin(), ind.end(), [&] (int x, int y)
{
return a[x] < a[y];
});
for (int i = 0; i < n; i++)
{
a[i] = lower_bound(p.begin(), p.end(), a[i]) - p.begin();
}
vector <int> perm(n);
map <pair <int, int>, vector <int> > v;
for (int i = 0; i < n; i++)
{
int x = a[i], y = a[ind[i]];
if (x != y)
{
g[y].push_back({x, i});
v[{x, y}].push_back(i);
}
else
{
perm[i] = i;
}
}
for (int i = 0; i < (int) p.size(); i++)
{
if (!vis[i])
{
cyc.clear();
dfs(i);
cyc.pop_back();
vector <int> ret;
for (int i = 0; i < (int) cyc.size(); i++)
{
int x = cyc[i], y = cyc[(i + 1) % ((int) cyc.size())];
ret.push_back(v[{x, y}].back());
v[{x, y}].pop_back();
}
for (int i = 0; i < (int) ret.size(); i++)
{
int j = (i - 1 + (int) ret.size()) % ((int) ret.size());
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 398

perm[ret[i]] = ret[j];
}
}
}

print(n, s, perm);
}

Listing 9.5.4: isaf ok.cpp


#include <bits/stdc++.h> //execution time : 0.453 s

using namespace std;

const int M = 2e5 + 239;

int n, s, a[M], p[M];


pair<int, int> b[M];

int parent[M], r[M];

inline void init()


{
for (int i = 0; i < n; i++)
{
parent[i] = i;
r[i] = 0;
}
}

inline int find_set(int p)


{
if (parent[p] == p) return p;
return (parent[p] = find_set(parent[p]));
}

inline void merge_set(int i, int j)


{
i = find_set(i);
j = find_set(j);
if (i == j) return;
if (r[i] > r[j]) swap(i, j);
if (r[i] == r[j]) r[j]++;
parent[i] = j;
}

inline bool is_connect(int i, int j)


{
return (find_set(i) == find_set(j));
}

int t;
bool used[M];
vector<int> c[M];

void dfs(int x)
{
used[x] = true;
c[t].push_back(x);
if (!used[p[x]]) dfs(p[x]);
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

ios::sync_with_stdio(0);
cin >> n >> s;
for (int i = 0; i < n; i++)
{
cin >> a[i];
b[i] = make_pair(a[i], i);
}
sort(b, b + n);
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 399

for (int i = 0; i < n; i++) p[b[i].second] = i;


for (int i = 0; i < n; i++)
if (a[i] == b[i].first && p[i] != i)
{
p[b[i].second] = p[i];
b[p[i]].second = b[i].second;
p[i] = i;
b[i].second = i;
}
init();
for (int i = 0; i < n; i++) merge_set(p[i], i);
int ls = -1;
for (int i = 0; i < n; i++)
{
if (p[b[i].second] == b[i].second) continue;
if (ls >= 0 && a[ls] == a[b[i].second])
{
int x = ls;
int y = b[i].second;
if (is_connect(x, y)) continue;
merge_set(x, y);
swap(p[x], p[y]);
}
ls = b[i].second;
}
t = 0;
for (int i = 0; i < n; i++)
if (!used[i] && p[i] != i)
{
dfs(i);
t++;
}
int kol = 0;
for (int i = 0; i < t; i++)
kol += (int)c[i].size();
if (kol > s)
{
cout << "-1";
return 0;
}
s -= kol;
s = min(s, t);
if (s <= 1)
{
cout << t << "\n";
for (int i = 0; i < t; i++)
{
cout << c[i].size() << "\n";
for (int x : c[i])
cout << (x + 1) << " ";
cout << "\n";
}
return 0;
}
cout << (t - s + 2) << "\n";
for (int i = 0; i < t - s; i++)
{
cout << c[i + s].size() << "\n";
for (int x : c[i + s])
cout << (x + 1) << " ";
cout << "\n";
kol -= (int)c[i + s].size();
}
cout << kol << "\n";
for (int i = 0; i < s; i++)
for (int x : c[i])
cout << (x + 1) << " ";
cout << "\n";
cout << s << "\n";
for (int i = s - 1; i >= 0; i--)
cout << (c[i][0] + 1) << " ";
cout << "\n";
return 0;
}

Listing 9.5.5: isaf ok slowio.cpp


CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 400

#include <bits/stdc++.h> //execution time : 1.281 s

using namespace std;

const int M = 5e5 + 239;

int n, s, a[M], p[M];


pair<int, int> b[M];

int parent[M], r[M];

inline void init()


{
for (int i = 0; i < n; i++)
{
parent[i] = i;
r[i] = 0;
}
}

inline int find_set(int p)


{
if (parent[p] == p) return p;
return (parent[p] = find_set(parent[p]));
}

inline void merge_set(int i, int j)


{
i = find_set(i);
j = find_set(j);
if (i == j) return;
if (r[i] > r[j]) swap(i, j);
if (r[i] == r[j]) r[j]++;
parent[i] = j;
}

inline bool is_connect(int i, int j)


{
return (find_set(i) == find_set(j));
}

int t;
bool used[M];
vector<int> c[M];

void dfs(int x)
{
used[x] = true;
c[t].push_back(x);
if (!used[p[x]]) dfs(p[x]);
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

cin >> n >> s;


for (int i = 0; i < n; i++)
{
cin >> a[i];
b[i] = make_pair(a[i], i);
}
sort(b, b + n);
for (int i = 0; i < n; i++) p[b[i].second] = i;
for (int i = 0; i < n; i++)
if (a[i] == b[i].first && p[i] != i)
{
p[b[i].second] = p[i];
b[p[i]].second = b[i].second;
p[i] = i;
b[i].second = i;
}
init();
for (int i = 0; i < n; i++) merge_set(p[i], i);
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 401

int ls = -1;
for (int i = 0; i < n; i++)
{
if (p[b[i].second] == b[i].second) continue;
if (ls >= 0 && a[ls] == a[b[i].second])
{
int x = ls;
int y = b[i].second;
if (is_connect(x, y)) continue;
merge_set(x, y);
swap(p[x], p[y]);
}
ls = b[i].second;
}
t = 0;
for (int i = 0; i < n; i++)
if (!used[i] && p[i] != i)
{
dfs(i);
t++;
}
int kol = 0;
for (int i = 0; i < t; i++)
kol += (int)c[i].size();
if (kol > s)
{
cout << "-1";
return 0;
}
s -= kol;
s = min(s, t);
if (s <= 1)
{
cout << t << endl;
for (int i = 0; i < t; i++)
{
cout << c[i].size() << endl;
for (int x : c[i])
cout << (x + 1) << " ";
cout << endl;
}
return 0;
}
cout << (t - s + 2) << endl;
for (int i = 0; i < t - s; i++)
{
cout << c[i + s].size() << endl;
for (int x : c[i + s])
cout << (x + 1) << " ";
cout << endl;
kol -= (int)c[i + s].size();
}
cout << kol << endl;
for (int i = 0; i < s; i++)
for (int x : c[i])
cout << (x + 1) << " ";
cout << endl;
cout << s << endl;
for (int i = s - 1; i >= 0; i--)
cout << (c[i][0] + 1) << " ";
cout << endl;
return 0;
}

Listing 9.5.6: 40973023-cycle sort.cpp


// https://codeforces.com/contest/1012/submission/40973023
// https://codeforces.com/contest/1012/status/E

#include <bits/stdc++.h> //execution time : 0.438 s

using namespace std;

const int M = 2e5 + 239;


CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 402

int n, s, a[M], p[M];


pair<int, int> b[M];
int parent[M], r[M];

inline void init()


{
for (int i = 0; i < n; i++)
{
parent[i] = i;
r[i] = 0;
}
}

inline int find_set(int p)


{
if (parent[p] == p) return p;

return (parent[p] = find_set(parent[p]));


}

inline void merge_set(int i, int j)


{
i = find_set(i);
j = find_set(j);
if (i == j) return;
if (r[i] > r[j]) swap(i, j);
if (r[i] == r[j]) r[j]++;
parent[i] = j;
}

inline bool is_connect(int i, int j)


{
return (find_set(i) == find_set(j));
}

int t;
bool used[M];
vector<int> c[M];

void dfs(int x)
{
used[x] = true;
c[t].push_back(x);
if (!used[p[x]]) dfs(p[x]);
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

ios::sync_with_stdio(0);

cin >> n >> s;


for (int i = 0; i < n; i++)
{
cin >> a[i];
b[i] = make_pair(a[i], i);
}

sort(b, b + n);

for (int i = 0; i < n; i++) p[b[i].second] = i;

for (int i = 0; i < n; i++)


if (a[i] == b[i].first && p[i] != i)
{
p[b[i].second] = p[i];
b[p[i]].second = b[i].second;
p[i] = i;
b[i].second = i;
}

init();
for (int i = 0; i < n; i++) merge_set(p[i], i);
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 403

int ls = -1;
for (int i = 0; i < n; i++)
{
if (p[b[i].second] == b[i].second) continue;
if (ls >= 0 && a[ls] == a[b[i].second])
{
int x = ls;
int y = b[i].second;
if (is_connect(x, y)) continue;
merge_set(x, y);
swap(p[x], p[y]);
}

ls = b[i].second;
}

t = 0;
for (int i = 0; i < n; i++)
if (!used[i] && p[i] != i)
{
dfs(i);
t++;
}

int kol = 0;
for (int i = 0; i < t; i++)
kol += (int)c[i].size();

if (kol > s)
{
cout << "-1";
return 0;
}

s -= kol;
s = min(s, t);
if (s <= 1)
{
cout << t << "\n";
for (int i = 0; i < t; i++)
{
cout << c[i].size() << "\n";
for (int x : c[i])
cout << (x + 1) << " ";
cout << "\n";
}
return 0;

}
cout << (t - s + 2) << "\n";
for (int i = 0; i < t - s; i++)
{
cout << c[i + s].size() << "\n";
for (int x : c[i + s])
cout << (x + 1) << " ";
cout << "\n";
kol -= (int)c[i + s].size();
}

cout << kol << "\n";


for (int i = 0; i < s; i++)
for (int x : c[i])
cout << (x + 1) << " ";
cout << "\n";

cout << s << "\n";


for (int i = s - 1; i >= 0; i--)
cout << (c[i][0] + 1) << " ";
cout << "\n";

return 0;
}

Listing 9.5.7: 41148878-cycle sort.cpp


CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 404

// https://codeforces.com/contest/1012/submission/41148878
// https://codeforces.com/contest/1012/status/E

#include<bits/stdc++.h> //execution time : 0.484 s

#define fo(i,a,b) for(i=a;i<=b;i++)


#define fd(i,a,b) for(i=a;i>=b;i--)

using namespace std;

typedef pair<int,int> pa;

inline int read()


{
int n=0;char c;
for(c=getchar();c<’0’||c>’9’;c=getchar());
for(;c>=’0’&&c<=’9’;c=getchar()) n=n*10+c-48;
return n;
}

const int maxn=2e5+5;


int i,j,n,s,a[maxn],f[maxn],p[maxn],cnt,sum;
vector<int>an[maxn];bool bz[maxn];
pa b[maxn];

int get(int x)
{
return f[x]?f[x]=get(f[x]):x;
}

void merge(int x,int y)


{
x=get(x),
y=get(y);
if (x!=y) f[x]=y;
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

n=read(),
s=read();

fo(i,1,n)
a[i]=read(),
b[i]=make_pair(a[i],i);

sort(b+1,b+1+n);

fo(i,1,n) p[b[i].second]=i;

fo(i,1,n)
if (a[i]==b[i].first&&p[i]!=i)
{
p[b[i].second]=p[i];
b[p[i]].second=b[i].second;
p[i]=i,b[i].second=i;
}

fo(i,1,n)
if (p[i]!=i) merge(p[i],i);

int x=0;
fo(i,1,n)
{
int y=b[i].second;
if (p[y]==y) continue;
if (a[x]==a[y])
{
if (get(x)==get(y)) continue;
merge(x,y);
swap(p[x],p[y]);
}
x=y;
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 405

fo(i,1,n)
if (p[i]!=i&&!bz[i])
{
cnt++,x=i;
for(;!bz[x];x=p[x])
sum++,
bz[x]=1,
an[cnt].push_back(x);
}

if (s<sum)
return puts("-1"),0; // ... !!!

s-=sum;
s=min(s,cnt);
if (s<=2)
{
printf("%d\n",cnt);
fo(i,1,cnt)
{
printf("%d\n",an[i].size());
for(j=0;j<an[i].size();j++)
printf("%d ",an[i][j]);
puts("");
}
return 0;
}

printf("%d\n",cnt-s+2);
fo(i,1,cnt-s)
{
printf("%d\n",an[i].size());
for(j=0;j<an[i].size();j++)
printf("%d ",an[i][j]);
sum-=an[i].size();
puts("");
}

printf("%d\n",sum);
fo(i,cnt-s+1,cnt)
for(j=0;j<an[i].size();j++)
printf("%d ",an[i][j]);
puts("");
printf("%d\n",s);
fd(i,cnt,cnt-s+1)
printf("%d ",an[i][0]);
puts("");
}

Listing 9.5.8: 43081231-cycle sort.cpp


// https://codeforces.com/contest/1012/submission/43081231
// https://codeforces.com/contest/1012/status/E?order=BY_CONSUMED_TIME_ASC
// https://codeforces.com/contest/1012/status/E

#include<bits/stdc++.h> //execution time : 0.219 s

#define Rep(i,a,b) for(register int i=(a);i<=(b);++i)


#define Repe(i,a,b) for(register int i=(a);i>=(b);--i)
#define pb push_back
#define mp make_pair
#define Chkmax(a,b) a=a>b?a:b
#define Chkmin(a,b) a=a<b?a:b
#define mx(a,b) (a>b?a:b)
#define mn(a,b) (a<b?a:b)

typedef unsigned long long uint64;


typedef unsigned int uint32;
typedef long long ll;

using namespace std;

namespace IO
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 406

{
const uint32 Buffsize=1<<15,Output=1<<23;
static char Ch[Buffsize],*S=Ch,*T=Ch;

inline char getc()


{
return((S==T)&&(T=(S=Ch)+fread(Ch,1,Buffsize,stdin),S==T)?0:*S++);
}

static char Out[Output],*nowps=Out;

inline void flush(){fwrite(Out,1,nowps-Out,stdout);nowps=Out;}

template<typename T>inline void read(T&x)


{
x=0;
static char ch;
T f=1;
for(ch=getc();!isdigit(ch);ch=getc())
if(ch==’-’) f=-1;
for(;isdigit(ch);ch=getc())
x=x*10+(chˆ48);
x*=f;
}

template<typename T>inline void write(T x,char ch=’\n’)


{
if(!x)
*nowps++=’0’;
if(x<0)
*nowps++=’-’,
x=-x;

static uint32 sta[111],tp;

for(tp=0;x;x/=10)
sta[++tp]=x%10;
for(;tp;*nowps++=sta[tp--]ˆ48);

*nowps++ = ch;
}
}

using namespace IO;

inline void file()


{
#ifndef ONLINE_JUDGE
freopen("gen.in","r",stdin);
freopen("gen.out","w",stdout);
#endif
}

const int MAXN=2e5+7;


static int n,s;

static struct poi


{
int x,id;
friend bool operator<(poi a,poi b)
{
return a.x<b.x;
}
} a[MAXN];

namespace DSU
{
static int fa[MAXN];

inline void cls()


{
Rep(i,1,n)fa[i]=i;
}

inline int Find(int u)


{
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 407

return u==fa[u]?u:fa[u]=Find(fa[u]);
}

void merge(int u,int v)


{
fa[Find(v)]=Find(u);
}
}

static int ps[MAXN],nx[MAXN],pr[MAXN];

static int nm[MAXN],tt;

void dfs(int u,int fg=1)


{
if(fg==1)
{
int x=u,sm=0;
do
{
++sm;
x=nx[x];
} while(uˆx);

write(sm);
}
else
if(!fg)
{
int x=u;
do
{
++tt;
x=nx[x];
} while(uˆx);

return;
}

int x=u;
do
{
write(x,’ ’);
x=nx[x];
}while(uˆx);
}

inline void init()


{
read(n);
read(s);
s-=n;
DSU::cls();
Rep(i,1,n)
read(a[i].x),
a[i].id=nx[i]=i;
}

inline void solve()


{
sort(a+1,a+n+1);
Rep(i,1,n)
while(a[a[i].id].x==a[i].x && a[i].idˆi)
swap(a[i],a[a[i].id]);

Rep(i,1,n)
ps[a[i].id]=i;

static int u;

Rep(i,1,n)
if(nx[i]==i)
{
u=i;
while(1)
{
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 408

nx[u]=ps[u];
if(ps[u]==i)break;
u=ps[u];
DSU::merge(u,i);
}

if(nx[i]==i)
DSU::fa[i]=0,++s;
}

for(register int i=1,j;i<=n;i=j+1)


{
j=i;
u=DSU::Find(a[i].id);
if(!u) continue;

while(j<n&&a[j+1].x==a[i].x)
{
++j;
if(DSU::Find(a[j].id)==u||!DSU::fa[a[j].id])
continue;
swap(nx[a[i].id],nx[a[j].id]);
DSU::merge(a[i].id,a[j].id);
}
}

if(s<0)
return(void)puts("-1");

static int tmp;tmp=0;

Rep(i,1,n)
if(DSU::fa[i]==i)
nm[++tmp]=i;

write((s<=1||tmp<=1)?tmp:max(tmp+2-s,2));

if(s<=1||tmp<=1)
Rep(i,1,tmp)
dfs(nm[i]),
*nowps++=’\n’;
else
{
u=min(s,tmp);
Rep(i,1,u)
dfs(nm[i],0);
write(tt);
Rep(i,1,u)
dfs(nm[i],-1);
*nowps++ = ’\n’;
write(u);
Repe(i,u,1)
write(nm[i],i==1?’\n’:’ ’);
Rep(i,u+1,tmp)
dfs(nm[i]),
*nowps++ = ’\n’;
}

flush();
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

init();
solve();
return 0;
}

Listing 9.5.9: 67068459-cycle sort.cpp


// https://codeforces.com/contest/1012/submission/67068459
// https://codeforces.com/contest/1012/status/E?order=BY_CONSUMED_TIME_ASC
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 409

// https://codeforces.com/contest/1012/status/E

#include<bits/stdc++.h> //execution time : 0.484 s

#define rep(i,j,k) for(int i=j;i<=k;++i)


#define drp(i,j,k) for(int i=j;i>=k;--i)

using namespace std;

char buf[25];
const int maxn=200010;

struct node
{
int w,id;
} c[maxn],d[maxn];

vector<int>v[maxn];
int a[maxn],b[maxn],f[maxn],q[maxn];
bool p[maxn];
int n,m,s,ans;

int read()
{
int x=0,f=0;
char ch=getchar();
while(ch<’0’||ch>’9’)
{
if(ch==’-’) f=1;
ch=getchar();
}

while(ch>=’0’&&ch<=’9’)
{
x=(x<<1)+(x<<3)+(chˆ48);
ch=getchar();
}

return f?-x:x;
}

void write(int x)
{
if(!x){ putchar(’0’); return;}
if(x<0){putchar(’-’); x=-x;}

int cnt=0;
while(x)
{
buf[++cnt]=’0’+x%10;
x/=10;
}

drp(i,cnt,1)
putchar(buf[i]);
}

bool cmp(node x,node y)


{
return x.w<y.w;
}

int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}

void unions(int x,int y)


{
int l=find(x),r=find(y);
if(l!=r) f[l]=r;
}

int main()
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 410

{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

n=read();
s=read();
rep(i,1,n)
{
a[i]=b[i]=read();
}

sort(b+1,b+1+n);

rep(i,1,n)
if(a[i]!=b[i])
{
++m;
c[m]=d[m]=(node){a[i],i};
}

if(m>s)
{
puts("-1");
return 0;
}

sort(d+1,d+1+m,cmp);

rep(i,1,m)
f[c[i].id]=c[i].id;

rep(i,1,m)
unions(d[i].id,c[i].id);

rep(i,2,m)
if(d[i].w==d[i-1].w&&find(d[i].id)!=find(d[i-1].id))
{
swap(d[i],d[i-1]);
unions(d[i].id,d[i-1].id);
}

rep(i,1,m)
q[d[i].id]=c[i].id,
p[d[i].id]=true;

rep(i,1,m)
{
int x=c[i].id;
if(p[x])
{
++ans;
while(p[x])
{
p[x]=false;
v[ans].push_back(x);
x=q[x];
}
}
}

int num=min(ans,s-m);

if(num>2)
{
write(ans-(num-2));
putchar(’\n’);

int sum=0;
rep(i,1,num) sum+=v[i].size();

write(sum);
putchar(’\n’);

rep(i,1,num)
{
rep(j,0,v[i].size()-1)
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 411

{
write(v[i][j]);
if(i!=num||j!=v[i].size()-1)
putchar(’ ’);
else
putchar(’\n’);
}
}

write(num);putchar(’\n’);

drp(i,num,1)
{
write(v[i][0]);
if(i!=1)
putchar(’ ’);
else
putchar(’\n’);
}

rep(i,num+1,ans)
{
write(v[i].size());
putchar(’\n’);
rep(j,0,v[i].size()-1)
{
write(v[i][j]);
if(j!=v[i].size()-1)
putchar(’ ’);
else
putchar(’\n’);
}
}
}
else
{
write(ans);
putchar(’\n’);

rep(i,1,ans)
{
write(v[i].size());
putchar(’\n’);

rep(j,0,v[i].size()-1)
{
write(v[i][j]);
if(j!=v[i].size()-1)
putchar(’ ’);
else
putchar(’\n’);
}
}
}

return 0;
}

Listing 9.5.10: 75463698-cycle sort.cpp


// https://codeforces.com/contest/1012/submission/75463698
// https://codeforces.com/contest/1012/status/E

#include <bits/stdc++.h> //execution time : 0.906 s

using std::vector;

const int N=300005;


int a[N],b[N],nt[N],t[N],n,cnt,now,s;
vector<int> ans[N];

int nxt(int x)
{
while (nt[x]<t[x+1] && a[nt[x]]==x) nt[x]++;
return nt[x];
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 412

void dfs(int x)
{
while (nxt(x)<t[x+1])
{
int tt=nt[x];
dfs(a[nt[x]++]);
ans[now].push_back(tt);
}
}

void out(int i)
{
for (auto j:ans[i]) printf("%d ",j);
puts("");
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

scanf("%d%d",&n,&s);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),
b[i]=a[i];

std::sort(b+1,b+n+1);

for (int i=1;i<=n;i++)


if (b[i]!=b[i-1])
{
b[++cnt]=b[i];
t[cnt]=nt[cnt]=i;
}

for (int i=1;i<=n;i++)


a[i]=std::lower_bound(b+1,b+cnt+1,a[i])-b;

t[cnt+1]=n+1;
for (int i=1;i<=cnt;i++)
if (nxt(i)<t[i+1])
{
++now;
dfs(i);
s-=ans[now].size();
std::reverse(ans[now].begin(),ans[now].end());
}

if (s<0)
{
puts("-1");
return 0;
}

s=std::min(s,now);
if (s<2)
{
printf("%d\n",now);
for (int i=1;i<=now;i++)
{
printf("%d\n",ans[i].size());
out(i);
}
}
else
{
printf("%d\n",now-s+2);
int sum=0;
for (int i=1;i<=s;i++)
sum+=ans[i].size();
printf("%d\n",sum);

for (int i=1;i<=s;i++) out(i);


printf("%d\n",s);
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 413

for (int i=s;i>=1;i--) printf("%d ",ans[i][0]);


puts("");

for (int i=s+1;i<=now;i++)


{
printf("%d\n",ans[i].size());
out(i);
}
}
}

Listing 9.5.11: 87786210-cycle sort.cpp


// https://codeforces.com/contest/1012/submission/87786210
// https://codeforces.com/contest/1012/status/page/2?order=BY_JUDGED_DESC

#include<bits/stdc++.h> //execution time : 0.734 s

using namespace std;

typedef int ll;


typedef long long int li;
typedef pair<ll,ll> pii;

const ll MAXN=2e5+51;
vector<pii>g[MAXN];
vector<ll>res[MAXN];
ll n,m,len,p,tot,zkdxl,rr,cnt;
ll x[MAXN],y[MAXN],z[MAXN],vis[MAXN];

inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!=’-’)
{
ch=getchar();
}

if(ch==’-’)
{
neg=-1;
ch=getchar();
}

while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-’0’);
ch=getchar();
}

return num*neg;
}

inline void dfs(ll x)


{
pii bk;
vis[x]=1;
while(!g[x].empty())
{
bk=g[x].back(),
g[x].pop_back();

dfs(bk.first),
res[tot].push_back(bk.second);
}
}

int main()
{
std::freopen("../tests/180", "r", stdin);
std::freopen("prime-tree.out", "w", stdout);

n=read(),
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 414

m=read();

for(register int i=1;i<=n;i++)


{
x[i]=y[i]=read();
}

sort(y+1,y+n+1);

for(register int i=1;i<=n;i++)


{
z[i]=(len+=(y[i]!=y[i-1]));
}

len=unique(y+1,y+n+1)-y-1;

for(register int i=1;i<=n;i++)


{
x[i]=lower_bound(y+1,y+len+1,x[i])-y;
}

for(register int i=1;i<=n;i++)


{
if(x[i]!=z[i])
{
g[x[i]].push_back(make_pair(z[i],i)),
p++;
}
}

if(p>m)
{
return puts("-1"),0;
}

if(!p)
{
return puts("0"),0;
}

for(register int i=1;i<=len;i++)


{
if(!vis[i]&&!g[i].empty())
{
tot++,
dfs(i);
}
}

if(tot<=1||m-p<=1)
{
printf("%d\n",tot);
for(register int i=1;i<=tot;i++)
{
printf("%d\n",res[i].size());
for(register int j:res[i])
{
printf("%d ",j);
}

puts("");
}

return 0;
}

rr=min(m-p,tot),
printf("%d\n%d\n",zkdxl=tot+2-rr,rr);

for(register int i=1;i<=rr;i++)


{
printf("%d%c",res[i].back()," \n"[i==rr]),
cnt+=res[i].size();
}

reverse(res+1,res+rr+1),printf("%d\n",cnt);
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 415

for(register int i=1;i<=rr;i++)


{
for(register int j:res[i])
{
printf("%d ",j);
}
}

puts("");
for(register int i=rr+1;i<=tot;i++)
{
printf("%d\n",res[i].size());
for(register int j:res[i])
{
printf("%d ",j);
}

puts("");
}
}

Listing 9.5.12: checkCycle-sort.cpp


#include "testlib.h"
#include <vector>

using namespace std;

int n, s;
vector<int> a_original;

int readAns(InStream& stream)


{
int q = stream.readInt(-1, s, "q");
if (q == -1) return -1;

vector<int> b = a_original;
int sum = 0;
for (int op_id = 0; op_id < q; op_id++)
{
int k = stream.readInt(1, n, format("k[%d]", op_id + 1).c_str());
sum += k;
if (sum > s)
{
stream.quitf(_wa,
"sum of length of cycles should be <= %d, \
but it’s %d after cycle #%d",
s, sum, op_id + 1);
}
vector<int> id;
set<int> id_occurred;
for (int x = 0; x < k; x++)
{
int p = stream.readInt(1, n, format("p[%d][%d]", \
op_id + 1, x + 1).c_str());
p--;
if (id_occurred.count(p))
{
stream.quitf(_wa,
"p[%d][%d]=%d occurs twice in the cycle", \
op_id + 1, x + 1, p + 1);
}

id_occurred.insert(p);
id.push_back(p);
}

for (int i = k - 1; i >= 1; i--)


{
swap(b[id[i]], b[id[i - 1]]);
}
}
for (int i = 0; i < n - 1; i++)
if (b[i] > b[i + 1])
CAPITOLUL 9. EJOI 2018 9.5. CYCLE SORT 416

stream.quitf(_wa, "array isn’t sorted, \


because a[%d] = %d > a[%d] = %d", i+1, b[i], i+2, b[i+1]);
return q;
}

void readinput()
{
n = inf.readInt();
s = inf.readInt();
a_original.resize(n);
for (int &x : a_original) x = inf.readInt();
}

int main()
//int main(int argc, char* argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/180",
(char*)"../tests/180.a",
(char*)"prime-tree.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerTestlibCmd(argc, argv);

readinput();

int jans = readAns(ans);


int pans = readAns(ouf);

if (jans >= 0)
{
if (pans >= 0)
{
if (pans == jans) quitf(_ok, \
"OK, answer exists, pans = jans = %d", pans);
else
if (pans > jans) quitf(_wa, \
"jury found better answer, jans = %d < pans = %d",jans,pans);
else
if (pans < jans) quitf(_fail, \
"participant found better answer, jans = %d > pans = %d", \
jans, pans);
}
else
if (pans == -1)
quitf(_wa, "participant didn’t find answer, but answer exists");
}
else
{
if (pans >= 0)
quitf(_fail, "answer exists, but jury didn’t find");
else
quitf(_ok, "OK, no solution");
}

return 0;
}

9.5.3 *Rezolvare detaliată


Capitolul 10
130
EJOI 2017

10.1 Magic
Problema 1 - Magic 100 de puncte
Now is the English lesson in grade 9 with Mr. Daskalov. Our main character - Deni - is very
weak in English and she counts the number of flies in the room. This proves to be very boring
activity, so she looks at the board where the teacher has written some text. She ignores the spaces
between the words so the whole text seems to her as one big sequence of English letters with
length N . Let us denote the number of different characters in this sequence with K. Deni starts
to take up different substrings from this sequence and she writes down the number of times each
character occurs. When for all of the K letters these numbers are equal, she calls the current
substring magical.
Remarks: A substring is a part of a given string, which contains consecutively written charac-
ters.
During this English lesson she is able to check every substring of the sequence. Meanwhile
she has counted how many of the substrings are magical and in the end she is very happy with
the accomplished activity. Deni decides that she would like to do so in every English lesson. But
with every subsequent English lesson the text on the board written by Mr. Daskalov is becoming
longer and longer. So she asks for your help - you have to write a program which tells her the
number of magical substrings in a given sequence of N English letters.
Task
Write a program magic which counts the number of magical substrings in a given sequence
of N English letters. Substrings which are the same but on different positions are counted as
different
Input
From the first line of the standard input, your program has to read one integer N - the number
of characters in the sequence written by Mr. Daskalov. From the next line your program has to
read a string of N English letters. The letters can be lower- and uppercase. Note that the lower-
and uppercase forms of the same letter are considered to be different characters (A and a are
different characters).
Output
The program must print to the standard output the number of magical substrings in the given
string. Since this number may be quite large, you are required to print its remainder when divided
by 1 000 000 007.
Constraints

ˆ 2&N & 100 000


130
aur: Matei Tinca, Colegiul Naţional de Informatică “Tudor Vianu” din Bucureşti,
. argint: Tiberiu-Ioan Muşat, Colegiul Naţional de Informatică “Tudor Vianu” din Bucureşti,
. argint: George-Alexandru Rapeanu, Colegiul Naţional “Emil Racoviţă” din Cluj-Napoca,
. argint: Matei-Mihai Mirica, Liceul Teoretic Internaţional de Informatică din Bucureşti.

417
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 418

Subtasks
Subtask Points N Further constraints
1 10 & 100 There are no further constraints.
2 20 & 2000 There are no further constraints.
3 30 & 100 000 There are only two kinds of characters in the given string (K 2).
4 40 & 100 000 There are no further constraints.
Examples
Sample Input Sample Explanation
Output
8 4 The magical substrings are abc, cba, abc and abccba.
abccbabc Note that for example the substring ab is not magical
because the letter c is not in it.
7 1 Only the substring abcABC is magical (the letters
abcABCC a and A are different because a is a lowercase letter
and A is an uppercase letter).
20 22 The number of magical substrings is 22 and one of
SwSSSwwwwSwSwwSwwwwS them is SwSwwS.

10.1.1 Indicaţii de rezolvare

This task looks like a direct dynamic optimization problem but this is not correct. There have to
be made some important observations and after them the task becomes more straightforward.
The first subtask is for 10 points. No observations are needed to be made here - considering the
constraints it is enough for every substring to be viewed and to be counted how many times every
type of letter is met. Next the counts for every type of letter are checked and if they are all equal
3 2
then the current substring is counted in the answer. The expected complexity is O n  kn .
The second subtask is for 20 points. Here the idea is to optimize what we did before. We
will remove the inner cycle - we will calculate the count for every type of letter for a current
substring in constant complexity. There are different ways to do that. The author precomputes
the prefix counts for every type of letter and with this the count for every type of letter for a
current substring can be calculated as a subtraction of prefix counts. So the complexity here is
2
O kn .
The third subtask is for 30 points. We have to make the first observation in the task which is
for an easier variant - when we only have two types of letters. Let we do the following change of the
letters in the string. We replace the first type of letters with the number +1 and the second type -
with the number -1 (negative 1). In this way our string becomes an array of numbers. The needed
substrings are with equal number of letters from the first and the second type i.e. the sum of the
numbers in the array in these positions would be 0 which means we are looking for subintervals
with 0 sum in the array. This task is solved easier than the original. Let we calculate the prefix
sums in the array pref (we have a fictive prefix sum in the beginning which is 0). The sum of a
subinterval of the array from x to y is pref y   pref x  1. We want this to be 0 i.e. for a given
y the subintervals ending there with zero sum are with x-es for which pref x  1 pref y . To
solve the current subtask we can save how many prefix sums are there for every possible value of
a sum in the array cnt and for a current index ind the number of subintervals with the needed
property ending there are cntind. We add this to the value of the answer and then we need to
make the update: cntind  . The complexity of the described procedure is linear - O n but
the solution for the whole task isn’t so here it can be made a solution with complexity O n log2 n.
The fourth subtask is for 40 points. The previous subtask was useful because the idea can be
adapted for the whole task. Let we fix two types of letters - a and b (these are only fictive and the
letters can be different). Let we save the number values in the array nums1 . Again in the same
way we replace the letters of type a with +1, we replace the letters of type b with -1 but for every
letter which is from a different type we replace it with the number 0. Let we take the second type
of letters - b and a new type of letters - c and we will save the numbers in a new array nums2 .
After we make the replacements and save the numbers using the described scheme we take c and
another different type of letters (for example d) and we again fill in an array nums3 and we do
this while we still have more unused types of letters. A substring that fulfills the requirement in
the statement is such that the sums of the numbers in the corresponding subintervals in the arrays
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 419

nums1 , nums2 , ..., nums k1 are all zeroes. Now we can approach analogously as in the previous
subtask.
Here we have a problem - the array cnt which we used now has to have several numbers as a
parameter. We can use hashes which isn’t expected to be known by the competitors so the author’s
solution doesn’t use this despite providing complexity of O kn. We can use the data structure
map from STL but we have to predefine operator and there will be a big constant which will make
the solution slower. The author approaches in the following way. Let for every array numsi we
calculate the corresponding prefix array prefi . For every position i we can make this type of sets
Si rpref1 , pref2 , ..., prefn x. In reality we search for pairs of sets Sx and Sy (of course x $ y) for
which Sx  Sy . Let we sort the sets Si . Now the equivalent sets are adjacent. Let we view all
groups of equivalent sets and their number is p and every group’s length is numi . The number of
< p1
all substrings which fulfill the requirement from the statement is i 1 numi numi  1. This is
easy to be calculated and solves the task for 100 points. The complexity of the described procedure
is O kn log2 n - this is the complexity of the sorting of the sets Si which is the dominating in the
solution.
The thing which shouldn’t be forgotten is that we have to make the calculations by the modulo
in the statement. In reality it turns out that the answer can’t be too big (the more types of
letters the small it gets) - using a convenient sample the maximal number for a given n and k is
k n & k &1
n
n
k
& n  1 
k
2
which for the maximal constraints isn’t much bigger than the modulo.
The reason there is a modulo is for deception that the answer will be very big and to prevent the
competitors from doing cheat solutions.
The task is interesting because of the approach that the letters are replaced with numbers which
is the big step towards finding a solution with smaller complexity.

10.1.2 Coduri sursă

Listing 10.1.1: 482109.cpp


// https://csacademy.com/submission/482109/ execution time : 0.203 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>

#define rep(i,a,b) for(i=a;i<=b;i++)

using namespace std;

typedef unsigned long long ull;

const int mod1=1e9+9,mod2=1e9+7;


const int mod=1e9+7;
const int base=347;
const int N=100010,maxn=100000;

struct H
{
int x1,x2;ull x3;
H(){}
H(int x1,int x2,ull x3):x1(x1),x2(x2),x3(x3){}
H operator + (H a)
{
return H((x1+a.x1)%mod1,
(x2+a.x2)%mod2,
x3+a.x3);
}
H operator - (H a)
{
return H((x1-a.x1+mod1)%mod1,
(x2-a.x2+mod2)%mod2,
x3-a.x3);
}
H operator * (H a)
{
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 420

return H(1ll*x1*a.x1%mod1,
1ll*x2*a.x2%mod2,
x3*a.x3);}
bool operator == (H a)
{
return x1==a.x1&&x2==a.x2&&x3==a.x3;
}
bool operator < (H a)
{
if(x1!=a.x1) return x1<a.x1;
else
if(x2!=a.x2) return x2<a.x2;
else
return x3<a.x3;
}
};

int cnt[N];
H h[N],hsh[N],p[N];
H a[N];
int vis[N];
char s[N];
int ans;

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

int i,j,n;
int mx;int l,r;

scanf("%d",&n);
scanf("%s",s+1);

p[0]=H(1,1,1);
rep(i,1,maxn) p[i]=p[i-1]*H(base,base,base);
rep(i,1,n)
{
hsh[i]=hsh[i-1]+p[s[i]];
}

rep(i,1,n) vis[s[i]]=’1’;
rep(i,’A’,’z’)
if(vis[i]) h[1]=h[1]+p[i];
rep(i,2,n) h[i]=h[i-1]+h[1];

a[0]=H(0,0,0);
rep(i,1,n)
{
cnt[s[i]]++;
mx=n;
rep(j,’A’,’z’) if(vis[j]) mx=min(mx,cnt[j]);
a[i]=hsh[i]-h[mx];
}

sort(a,a+1+n);

for(l=0;l<=n;l=r)
{
r=l;
while(a[r]==a[l]&&r<=n) r++;
ans=(ans+1ll*(r-l)*(r-l-1)/2)%mod;
}

printf("%d",ans);
}

Listing 10.1.2: 483136-magic.cpp


// https://csacademy.com/submission/483136/ execution time : 0.109 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#include <string>
#include <iostream>
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 421

#include <algorithm>

using namespace std;

typedef unsigned long long ull;


int n, c[55];
bool vis[55];
ull v[100009];
string s, t;

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

cin >> n >> s;


for (int i = 0; i < n; i++)
{
int p = s[i] <= ’Z’ ? s[i] - 65 : s[i] - 71;
if (!vis[p])
{
vis[p] = true;
t += s[i];
}
}

for (int i = 0; i <= n; i++)


{
for (int j = 1; j < t.size(); j++)
v[i] = v[i] * 1777777777 + (c[j] - c[0] + 123456789);

if(i < n) c[t.find(s[i])]++;


}

sort(v, v + n + 1);

int l = 0;
long long ret = 0;
for (int i = 1; i <= n + 1; i++)
{
if (i == n + 1 || v[i] != v[i - 1])
ret += 1LL * (i - l) * (i - l - 1) / 2, l = i;
}

cout << ret % 1000000007 << endl;

return 0;
}

Listing 10.1.3: 484114-magic.cpp


// https://csacademy.com/submission/484114/ execution time : 0.781 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#include <bits/stdc++.h>

using namespace std;

#define int long long

constexpr int MOD = 1152921504606846883;


constexpr int B = 97;

int cnt[52], mp[128];


bitset<52> used;

bool flag;

int H()
{
int m = MOD;
for (int i = 0; i < 52; i++)
{
if (used[i])
m = min(m, cnt[i]);
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 422

if (m == 0)
flag = true;
int t = 0;
for (int i = 0; i < 52; i++)
{
if (used[i])
t = ((t * B) % MOD + cnt[i] - m) % MOD;
}

return t;
}

signed main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

int i = 0;
for (char c = ’A’; c <= ’Z’; c++)
mp[(int)c] = i++;

for (char c = ’a’; c <= ’z’; c++)


mp[(int)c] = i++;

unordered_map<int, int> m;

int N;
cin >> N;

string s;
cin >> s;

int total = 0;
for (char c : s)
used[mp[(int)c]] = true;

for (char c : s)
{
cnt[mp[(int)c]]++;
flag = false;
int tmp = H();
if (!tmp)
m[tmp]++;
if (!flag)
total = (total + m[tmp]) % 1000000007;
if (tmp)
m[tmp]++;
}

cout << total << endl;


}

Listing 10.1.4: 484759-magic.cpp


// https://csacademy.com/submission/484759/ execution time : 0.187 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#define _GNU_SOURCE // asprintf ...

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<stdbool.h>
#include<limits.h>

#include<iostream>

using namespace std;

typedef unsigned long long ull;


typedef long long ll;
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 423

int cmp(const void* pa, const void* pb)


{
ull*a=(ull*)pa;
ull*b=(ull*)pb;
return *a < *b ? -1 : 1;
}

char* concat(char* a, char b)


{
char* ptr=NULL;
asprintf(&ptr, "%s%c", a, b);
return ptr;
}

int n, c[55];
bool vis[55];
ull v[100009];
char* t;
char s[100000];

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

t = strdup("");
scanf("%d", &n);
scanf("%s", &s);

for (int i = 0; i < n; i++)


{
int p = s[i] <= ’Z’ ? s[i] - 65 : s[i] - 71;
if (!vis[p])
{
vis[p] = true;
t = concat(t, s[i]);
}
}

for (int i = 0; i <= n; i++)


{
int tsz = strlen(t);
for (int j = 1; j < tsz; j++)
v[i] = v[i] * 1777777777 + (c[j] - c[0] + 123456789);

if(i < n)
{
char*pidx = strchr(t, s[i]);
int idx;

if(pidx)
idx = pidx - t;
else
idx=0;

c[idx]++;
}
}

qsort(v, n + 1, sizeof(ull), cmp);

int l = 0;
ll ret = 0;
for (int i = 1; i <= n + 1; i++)
{
if (i == n + 1 || v[i] != v[i - 1])
ret += 1LL * (i - l) * (i - l - 1) / 2, l = i;
}

printf("%lld\n", ret % 1000000007 );

return 0;
}

Listing 10.1.5: 484817-magic.cpp


CAPITOLUL 10. EJOI 2017 10.1. MAGIC 424

// https://csacademy.com/submission/484817/ execution time : 1.625 s


// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#include<cstdio>
#include<string>
#include<algorithm>

using namespace std;

string s,t;

unsigned long long v[100005];


int c[60],viz[60];

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

int n,i,l=0,j,nr;
long long rasp=0;
char ch;

scanf("%d\n",&n);
for(i=1;i<=n;i++)
{
scanf("%c",&ch);
s=s+ch;
}

for(i=0;i<n;i++)
{
if (s[i]<=’Z’)
nr=s[i]-65;
else
nr=s[i]-71;

if (!viz[nr])
viz[nr]=1,t=t+s[i];
}

for(i=0;i<=n;i++)
{
for(j=1;j<t.size();j++)
v[i]=v[i]*1777777777+c[j]-c[0]+1LL*123456789;

if (i<n)
c[t.find(s[i])]++;
}

sort(v,v+n+1);

for(i=1;i<=n;i++)
if (v[i]!=v[i-1])
rasp=rasp+1LL*(i-l)*(i-l-1)/2,
l=i;

rasp=rasp+1LL*(n+1-l)*(n-l)/2;
printf("%lld\n",rasp%1000000007);

return 0;
}

Listing 10.1.6: 1703927-magic.cpp


// https://csacademy.com/submission/1703927/ execution time : 0.203 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

/// {{{ Author: Wang, Yen-Jen


// include
#include <bits/stdc++.h>

// using
using namespace std;
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 425

// types
typedef long long ll;
typedef pair<int,int> pii;

// macro
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin() , (x).end()
#define REP(i , n) for(int i = 0; i < int(n); i++)
#define REP1(i , a , b) for(int i = a; i <= int(b); i++)
#define F first
#define S second
#define MP make_pair
#define PB push_back
#define LC o<<1 , l , m
#define RC o<<1|1 , m + 1 , r
#define MS(x , v) memset(x , (v) , sizeof(x))

// input
inline bool SR(int &x)
{
return scanf("%d",&x) == 1;
}

inline bool SR(ll &x)


{
return scanf("%lld",&x) == 1;
}

inline bool SR(double &x)


{
return scanf("%lf",&x) == 1;
}

inline bool SR(char *s)


{
return scanf("%s",s) == 1;
}

inline bool RI()


{
return true;
}

template<typename I , typename... T>


inline bool RI(I &x , T&... tail)
{
return SR(x) && RI(tail...);
}

// output
inline void SP(const int x)
{
printf("%d",x);
}

inline void SP(const ll x)


{
printf("%lld",x);
}

inline void SP(const double x) {


printf("%.16lf",x);
}

inline void SP(const char *s)


{
printf("%s",s);
}

inline void PL()


{
puts("");
}

template<typename I , typename... T>


inline void PL(const I x , const T... tail)
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 426

{
SP(x);
if(sizeof...(tail)) putchar(’ ’);
PL(tail...);
}

// debug
#define WangYenJen

#ifdef WangYenJen
template<typename I>
void _DOING(const char *s , I&& x)
{
cerr << s << " = " << x << endl;
}

template<typename I , typename... T>


void _DOING(const char *s , I&& x , T&&... tail)
{
int c = 0;
while(*s != ’,’ || c != 0)
{
if(*s == ’(’ || *s == ’[’ || *s == ’{’) c++;
if(*s == ’)’ || *s == ’]’ || *s == ’}’) c--;
cerr << *s++;
}
cerr << " = " << x << " , ";
_DOING(s + 1 , tail...);
}

#define DEBUG(...) \
do {\
fprintf(stderr , "%s: Line %d - ",__PRETTY_FUNCTION__,__LINE__);\
_DOING(#__VA_ARGS__ , __VA_ARGS__);\
} while(0);

#else
#define DEBUG(...)

#endif

// constant number
const int INF = 0x3f3f3f3f;
const ll INF64 = 0x3f3f3f3f3f3f3f3fll;

// random function
inline int RAND()
{
static int x = 880301;
return (x = x * 0xdefaced + 1) % 0x7fffffff;
}

const int MAX_N = 100000 + 7;


const ll BASE = 880301;
const ll MOD = 0x7fffffff;

ll dp[MAX_N];
char S[MAX_N];
ll power[MAX_N];

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

int N;
RI(N);
SR(S + 1);

vector<char> rd;
REP1(i, 1, N) rd.PB(S[i]);

sort(ALL(rd));

rd.erase(unique(ALL(rd)), rd.end());
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 427

power[0] = 1;
REP1(i, 1, SZ(rd)) power[i] = power[i - 1] * BASE % MOD;

ll P = 0;
REP(i, SZ(rd)) P += power[i];

ll hv = 0, ans = 0;

unordered_map<ll, int> cnt;

cnt[hv]++;
REP1(i, 1, N)
{
int x = lower_bound(ALL(rd), S[i]) - rd.begin();
(hv += power[x]) %= P;
if (cnt.count(hv)) ans += cnt[hv];
cnt[hv]++;
}

PL(ans % (1000000000 + 7));

return 0;
}

OBS: “typename...” In a primary class template, the template parameter pack must be the
131
final parameter in the template parameter list.

Listing 10.1.7: 1748766-magic.cpp


// https://csacademy.com/submission/1748766/ execution time : 0.172 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#define _GNU_SOURCE // asprintf ...


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<stdbool.h>
#include<limits.h>

#include<iostream>

using namespace std;

typedef unsigned long long ull;


typedef long long ll;

int cmp(const void*pa, const void*pb)


{
ull*a=(ull*)pa;
ull*b=(ull*)pb;
return *a < *b ? -1 : 1;
}

char*concat(char*a, char b)
{
char*ptr=NULL;
asprintf(&ptr, "%s%c",a,b);
return ptr;
}

int n, c[55];
bool vis[55];
ull v[100009];
char*t;
char s[100000];

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

t = strdup("");
scanf("%d", &n);
131
https://en.cppreference.com/w/cpp/language/parameter_pack
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 428

scanf("%s", &s);
for (int i = 0; i < n; i++)
{
int p = s[i] <= ’Z’ ? s[i] - 65 : s[i] - 71;
if (!vis[p])
{
vis[p] = true;
t = concat(t, s[i]);
}
}

for (int i = 0; i <= n; i++)


{
int tsz = strlen(t);
for (int j = 1; j < tsz; j++)
v[i] = v[i] * 1777777777 + (c[j] - c[0] + 123456789);

if(i < n)
{
char*pidx = strchr(t, s[i]);
int idx;
// if(pidx)
idx = pidx - t;
//else
// idx=0;
c[idx]++;
}
}

qsort(v, n + 1, sizeof(ull), cmp);

int l = 0;
ll ret = 0;
for (int i = 1; i <= n + 1; i++)
{
if (i == n + 1 || v[i] != v[i - 1])
ret += 1LL * (i - l) * (i - l - 1) / 2, l = i;
}

printf("%lld\n", ret % 1000000007 );

return 0;
}

Listing 10.1.8: 2119774-magic.cpp


// https://csacademy.com/submission/2119774/ execution time : 0.203 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#include<bits/stdc++.h>

#define ff first
#define ss second

using namespace std;

int n,zl[205],minim;
long long hasz,ans,kand,ile;
const long long mod=1E9+123,mod2=1E9+7;
char zn[100005];

vector<char>v;
vector<long long>vec;

map<long long,int>m;

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

ios_base::sync_with_stdio(0);

cin>>n;
for(int i=1;i<=n;i++)
CAPITOLUL 10. EJOI 2017 10.1. MAGIC 429

{
cin>>zn[i];
zl[zn[i]]++;
}

for(int i=’a’;i<=’z’;i++)
{
if(zl[i]!=0)v.push_back(i);
}

for(int i=’A’;i<=’Z’;i++)
{
if(zl[i]!=0)v.push_back(i);
}
for(int i=0;i<v.size();i++) zl[v[i]]=0;

for(int i=1;i<=n;i++)
{
zl[zn[i]]++;
minim=INT_MAX;
for(int j=0;j<v.size();j++)
minim=min(minim,zl[v[j]]);
hasz=0;
for(int j=0;j<v.size();j++)
hasz=hasz*29+(zl[v[j]]-minim);
vec.push_back(hasz);
}

sort(vec.begin(),vec.end());

for(int i=0;i<vec.size();i++)
{
if(i==0)
{
kand=vec[i];
ile=1;
continue;
}

if(vec[i]!=vec[i-1])
{
if(kand!=0) ile--;
ans=(ans+((ile)*(ile+1))/2)%mod2;
kand=vec[i];
ile=1;
}
else
ile++;
}

if(kand!=0) ile--;
ans=(ans+((ile)*(ile+1))/2)%mod2;

cout<<ans;
}

Listing 10.1.9: 2210797-magic.cpp


// https://csacademy.com/submission/2210797/ execution time : 0.187 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/magic/statistics/

#define _GNU_SOURCE // asprintf ...

#include<bits/stdc++.h>

typedef long long ll;


typedef unsigned long long ull;

int cmp(const void* pa, const void* pb)


{
ull* a=(ull*)pa;
ull* b=(ull*)pb;
return *a < *b ? -1 : 1;
}
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 430

char*concat(char*a, char b)
{
char*ptr = NULL;
asprintf(&ptr, "%s%c", a, b);
return ptr;
}

int n, c[55];
bool vis[55];
ull v[100009];
char*t;
char s[100000];

int main()
{
std::freopen("../magic_tests/magic.20.in", "r", stdin); // 21636849
//std::freopen("magic.out", "w", stdout);

t = strdup("");
scanf("%d", &n);
scanf("%s", &s);

for (int i = 0; i < n; ++ i)


{
int p = s[i] <= ’Z’ ? s[i] - 65 : s[i] - 71;
if (!vis[p])
{
vis[p] = true;
t = concat(t, s[i]);
}
}

for (int i = 0; i <= n; ++ i)


{
int tsz = strlen(t);
for (int j = 1; j < tsz; j++)
v[i] = v[i] * 1777777777 + (c[j] - c[0] + 123456789);

if(i < n)
{
char*pidx = strchr(t, s[i]);
int idx;
if(pidx)
idx = pidx - t;
else
idx=0;

c[idx]++;
}
}

qsort(v, n + 1, sizeof(ull), cmp);

int l = 0;
ll ret = 0;
for (int i = 1; i <= n + 1; ++ i)
{
if (i == n + 1 || v[i] != v[i - 1])
ret += 1LL * (i - l) * (i - l - 1) / 2, l = i;
}

printf("%lld\n", ret % 1000000007 );

return 0;
}

10.1.3 *Rezolvare detaliată

10.2 Particles
Problema 2 - Particles 100 de puncte
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 431

Two linear particle accelerators A and B, placed opposite to each other at a distance L apart,
are propelling elementary particles. A is shooting x-particles, while B is shooting y-particles.
The two kinds of particles are flying one opposing the other, and when an x-particle meets a y-
particle, they collide and annihilate. One should be aware that an xparticle could overtake other
x-particles, as well as a y-particle could overtake other y-particles without any consequences for
the particles.
Like so, in a given moment of time, which we assume to be zero, a shooting of N x-particles
and N y-particles starts from the two accelerators. Each particle moves with its own constant
speed. The particles are numbered in the order of their shooting from 1 to N , this holds true for
both the x-particles and the y-particles.
Remark: For time t, a particle with speed v travels distance s vt.
The shooting time moments for the x-particles are 0 tx1 $ tx2 $ tx3 $ ... $ txN , and their
speeds are vx1 , vx2 , vx3 , ..., vxN .
Correspondingly, for the y-particles the moments are denoted by 0 ty1 $ ty2 $ ty3 $ ... $ tyN ,
and their speeds by vy1 , vy2 , vy3 , ..., vyN .
The shooting is executed in a way to guarantee the fulfillment of the following conditions:
- Each particle will collide a particle of the opposite type;
- When two particles collide, all other particles will be at a distance greater than or equal to
1 from the collision point. This is guarantee for the first collisions.

Task
Write a program particles to determine the first K collisions between particles of the two kinds.
Input
The three space separated positive integers N , L, and K are read from the first line of the
standard input.
The following N lines contain two space separated non-negative integers txi and vxi each: the
shooting moment and the speed of the corresponding x-particle.
The last N input lines contain, respectively, each the shooting moment tyi and the speed vyi
of the corresponding y-particle in the same format.
Output
The program must print to the standard output K lines, each containing two space sepa-
rated positive integers: the numbers of the x-particle and y-particle, which are involved in the
corresponding collision.
th
Lines are output increasingly by the order of collisions - from the first one to the K .
Constraints

ˆ 1&N & 50 000


ˆ In 30% of the tests N & 1000
1 & L & 10
9
ˆ

ˆ 1&K & 100, K & N


0 & txi , tyi & 10
9
ˆ

1 & vxi , vyi & 10


9
ˆ

Example
Sample input Sample output
4 100 2 42
01 24
23
32
6 10
05
3 10
51
7 20
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 432

10.2.1 Indicaţii de rezolvare

In order to be able to solve the problem we have to learn to find the exact moment in which two
particles collide. Let us try to find the moment in which the i-th x particle and the j-th y particle
collide. We can do that quite easily using a binary search on the moment of collision, but that
would have a complexity of O log. To find the moment of collision in O 1 let us look at the
formulas that describe the position of a certain particle in a specific moment. Let us imagine that
the x particles are being shot from point 0 rightwards on the number line, and the y particles are
being shot from point L leftwards on the number line. At a certain moment t, the position of the
two particles is:
P xi t  txi   vxi
P yj L  t  tyj   vyj
This formula could give negative values if we evaluate it for invalid times in which the particle
has not been shot yet, but that does not matter. We clearly have a collision if P xi P yj . That
is:
t  txi   vxi L  t  tyj   vyj
t  vxi  txi  vxi L  t  vyj  tyj  vyj
t  vxi  t  vyj L  tyj  vyj  txi  vxi
t L  tyj  vyj  txi  vxi  © vxi  vyj 
And hence we get a direct formula for the moment of collision. It is possible for the collision
to happen before 0 or after L on the number line, which means that this collision will surely not
happen (those particles will collide with some others before colliding with each other).
2
Solution with complexity O N  log N 
Since we can quickly find the moment of collision between two particles, we can find the
moment of collision for every pair of particles and order these collisions chronologically. The total
2 2
amount of collisions is O N , and sorting them takes O N  log N . After this we can iterate
over all collisions chronologically and keep which of the particles have already collided. If we reach
a pair in which both particles have not collided with anything yet, then we know that they will
collide with each other. In this way we can find not only the first K, but all collisions with the
same complexity.
Solution with complexity O N  K  log 
We will describe a solution that finds only the first collision. After finding it we can remove
the two particles that collided and repeat the whole process K times.
To find the first collision we will use binary search on the moment of the first collision. Let us
fix the time t. We want to know whether the first collision happened before or after t. To do this
we can find the furthest x particle and the furthest y particle (by ’furthest’ we mean the one that
travelled the largest distance, that is, using the formulas above, the x particle with the largest
position and the y particle with the smallest one).
Let the positions of the two particles, according to the formulas above, be respectively Px and
Py . Then:

ˆ If ¶Px  Py ¶ $ ε (where ε is a small enough constant) we can assume that t is the moment
of the first collision. The particles which collided are the furthest particles.
Else:
ˆ If Px $ Py , then the first collision has not happened yet and we have to look for a larger
value of t
ˆ If Px % Py , then the first collision already happened and we have to look for a smaller value
of t

This solution has a complexity of O N  log for finding the first collision. The bounds of the
binary search in which we search for t are from 0 to the time needed for any particle to be shot
and travel a distance of L (since at this moment the particle must have collided with some other
one).
Since we are doing a binary search on real values and not integers, it is a good idea to set a
limit on the amount of iterations the binary search can perform, since choosing a good ε may be
tricky. About 40-50 iterations should be ideal (more may trigger time limits and less could lead
to precision errors).
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 433

Using this procedure K times we get a solution with total complexity of O N  K  log.
The problem can be solved in O N  K , but this was not required.

10.2.2 Coduri sursă

Listing 10.2.1: 482015-particles.cpp


// https://csacademy.com/submission/482015/ execution time : 8.060 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include<stdio.h>
#include<algorithm>

#include<iostream>

#define ran 55555

using namespace std;

int n,L,k;
int tx[ran],vx[ran],ty[ran],vy[ran];
bool fx[ran],fy[ran];

int main()
{
std::freopen("../particles_tests/particles.20.in", "r", stdin);
std::freopen("particles.out", "w", stdout);

scanf("%d%d%d",&n,&L,&k);

for(int i=1; i<=n; i++)


{
scanf("%d%d",&tx[i],&vx[i]);
}

for(int i=1; i<=n; i++)


{
scanf("%d%d",&ty[i],&vy[i]);
}

while(k--)
{
double lo = 0, hi = 2e9;
while(true)
{
double mi = (lo + hi)/2;
double MX = 0;
int IDX = -1;
for(int i=1; i<=n; i++)
{
if(fx[i])continue;
if(tx[i] > mi)continue;
double pos = (mi - tx[i]) * vx[i];
if(pos >= MX)MX = pos,IDX = i;
}

double MY = L;
int IDY = -1;
for(int i=1; i<=n; i++)
{
if(fy[i])continue;
if(ty[i] > mi)continue;
double pos = L - (mi - ty[i]) * vy[i];
if(pos <= MY) MY = pos, IDY = i;
}

if(MX < MY - 0.05)


{
lo = mi;
}
else
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 434

if(MX > MY)


hi = mi;
else
{
printf("%d %d\n",IDX, IDY);
fx[IDX] = fy[IDY] = true;
break;
}
}
}

return 0;
}

Listing 10.2.2: 482536-particles.cpp


// https://csacademy.com/submission/482536/ execution time : 8.747 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <cmath>
#include <cstring>
#include <ctime>
#include <unordered_map>
#include <iomanip>

using namespace std;

#define fi first
#define se second
#define pb push_back
#define all(v) (v).begin(),(v).end()

typedef long long ll;


typedef long double ld;
typedef vector<int> vi;
typedef pair<int,int> pii;

const int maxn=50010;


int N,L,K,tx[maxn],vx[maxn],ty[maxn],vy[maxn];
bool px[maxn],py[maxn];
int id[2];

bool check(ld t)
{
ld l=0,r=0;

id[0]=id[1]=-1;

int i;
for (i=1; i<=N; i++)
if (!px[i] && tx[i]<=t && (id[0]==-1 || l<vx[i]*(t-tx[i])))
{
l=1LL*vx[i]*(t-tx[i]);
id[0]=i;
}

for (i=1; i<=N; i++)


if (!py[i] && ty[i]<=t && (id[1]==-1 || r<vy[i]*(t-ty[i])))
{
r=1LL*vy[i]*(t-ty[i]);
id[1]=i;
}

if (id[0]==-1 || id[1]==-1 || l<L-r) return 0;

return 1;
}
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 435

int main()
{
std::freopen("../particles_tests/particles.20.in", "r", stdin);
std::freopen("particles.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);
cin >> N >> L >> K;

int i,maxt=0;
for (i=1; i<=N; i++)
{
cin >> tx[i] >> vx[i];
maxt=max(maxt,tx[i]);
}

for (i=1; i<=N; i++)


{
cin >> ty[i] >> vy[i];
maxt=max(maxt,ty[i]);
}

ld t=1;
while (K)
{
ld l=t,r=L+maxt,mid;

for (i=1; i<=50; i++)


{
mid=l+(r-l)/2;
if (check(mid)) r=mid;
else l=mid;
}

cout << id[0] << " " << id[1] << "\n";
t=l;
px[id[0]]=1,py[id[1]]=1;
K--;
}

return 0;
}

Listing 10.2.3: 482782-particles.cpp


// https://csacademy.com/submission/4822782/ execution time : 8.253 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/

#include <stdio.h>
#include <iostream>
#include <vector>
#include <assert.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <sstream>
#include <memory.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 50000;


int n, L, k;
pair<int, int> a[N], b[N];

int main()
{
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 436

std::freopen("../particles_tests/particles.20.in", "r", stdin);


std::freopen("particles.out", "w", stdout);

scanf("%d%d%d", &n, &L, &k);

for (int i = 0; i < n; ++i)


scanf("%d%d", &a[i].first, &a[i].second);

for (int i = 0; i < n; ++i)


scanf("%d%d", &b[i].first, &b[i].second);

for (int i = 0; i < k; ++i)


{
double l = 0, r = 2e9, m, res;
pair<int, int> at;
for (int it = 0; it < 64; ++it)
{
m = (l + r) / 2;
double mxA = -1e18, mnB = 1e18;
for (int i = 0; i < n; ++i)
{
if (a[i].first != -1 && m >= a[i].first)
{
double loc = (m - a[i].first)*a[i].second;
if (mxA < loc)
{
mxA = loc;
at.first = i;
}
}

if (b[i].first != -1 && m >= b[i].first)


{
double loc = L - (m - b[i].first)*b[i].second;
if (mnB > loc)
{
mnB = loc;
at.second = i;
}
}
}

if (mxA > mnB)


r = m;
else
l = m;
}

a[at.first].first = -1;
b[at.second].first = -1;
printf("%d %d\n", at.first + 1, at.second + 1);
}

return 0;
}

Listing 10.2.4: 483295-particles.cpp


// https://csacademy.com/submission/483295/ execution time : 0.563 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#define _CRT_SECURE_NO_WARNINGS
#pragma comment(linker, "/STACK:256000000")
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 437

#include <set>
#include <queue>
#include <deque>
#include <bitset>
#include <unordered_map>
#include <unordered_set>

using namespace std;

typedef long long ll;

#define TASK ""

int solve();

int main()
{
std::freopen("../particles_tests/particles.20.in", "r", stdin);
std::freopen("particles.out", "w", stdout);

solve();
return 0;
}

const int MAXN = 50123;


vector<ll> t1, t2, v1, v2;
ll T1[MAXN], T2[MAXN], V1[MAXN], V2[MAXN];
vector<int> num1, num2;

int solve()
{
int n, l, k;
cin >> n >> l >> k;

for (int i = 0; i < n; i++) cin >> T1[i] >> V1[i];


for (int i = 0; i < n; i++) cin >> T2[i] >> V2[i];

for (int K = 0; K < k; K++)


{
t1.clear(), t2.clear(), v1.clear(), v2.clear(),
num1.clear(), num2.clear();

int prevV = 0;
for (int i = 0; i < n; i++)
{
int t = T1[i], v = V1[i];
if (v > prevV)
{
t1.push_back(t), v1.push_back(v);
num1.push_back(i + 1);
prevV = v;
}
}

prevV = 0;
for (int i = 0; i < n; i++)
{
int t = T2[i], v = V2[i];
if (v > prevV)
{
t2.push_back(t), v2.push_back(v);
prevV = v;
num2.push_back(i + 1);
}
}

double minT = 1e18;


int x = 0, y = 0;
for (int i = 0; i < (int)t1.size(); i++)
{
for (int j = 0; j < (int)t2.size(); j++)
{
double curT = 1.0*(l+v1[i]*t1[i]+v2[j]*t2[j])/(v1[i]+v2[j]);
if (minT > curT) { minT = curT, x = i, y = j; }
}
}
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 438

cout << num1[x] << ’ ’ << num2[y] << ’\n’;


t1.erase(t1.begin() + x);
v1.erase(v1.begin() + x);

t2.erase(t2.begin() + y);
v2.erase(v2.begin() + y);

V1[num1[x] - 1] = -1, V2[num2[y] - 1] = -1;


num1.erase(num1.begin() + x), num2.erase(num2.begin() + y);
}

return 0;
}

Listing 10.2.5: 484328-particles.cpp


// https://csacademy.com/submission/484328/ execution time : 5.332 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include <bits/stdc++.h>

using namespace std;

#define MAXN 50001


int n, l, k, t[2][MAXN], v[2][MAXN];

pair<double, pair<int, int>> calc(int pos)


{
double ans[2] = {2e19, 2e19};
int ind[2];
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < n; ++j)
{
double tm = t[i][j]+(double)pos/v[i][j];
if (tm < ans[i])
{
ans[i] = tm;
ind[i] = j;
}
}

pos = l-pos;
}

return {ans[1]-ans[0], {ind[0], ind[1]}};


}

pair<int, int> solve()


{
int lo = 0, hi = l;
while (lo+1<hi)
{
int mid = (hi+lo)/2;
if (calc(mid).first>0) lo = mid;
else hi = mid;
}

if (abs(calc(lo).first)<abs(calc(hi).first)) return calc(lo).second;

return calc(hi).second;
}

int main()
{
std::freopen("../particles_tests/particles.20.in", "r", stdin);
std::freopen("particles.out", "w", stdout);

scanf("%d%d%d", &n, &l, &k);


for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < n; ++j)
{
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 439

int tt, vv;


scanf("%d%d", &tt, &vv);
t[i][j] = tt;
v[i][j] = vv;
}
}

for (int i = 0; i < k; ++i)


{
pair<int, int> ans = solve();
printf("%d %d\n", ans.first+1, ans.second+1);
v[0][ans.first] = 1;
v[1][ans.second] = 1;
t[0][ans.first] = 1e9+1;
t[1][ans.second] = 1e9+1;
}

return 0;
}

Listing 10.2.6: 485011-particles.cpp


// https://csacademy.com/submission/485011/ execution time : 0.766 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include<cstdio>
#include<vector>

using namespace std;

struct tip
{
int first,second,nr;
};

int n,l,k;

double get(int i,int j,vector<tip>&a,vector<tip>&b)


{
if (a[i].first<b[j].first)
{
long long d=1LL*a[i].second*(b[j].first-a[i].first);
if (d>l)
return 1e18;
else
return b[j].first+(l-d)*1.0/(a[i].second+b[j].second);
}
else
{
long long d=1LL*b[j].second*(a[i].first-b[j].first);
if (d>l)
return 1e18;
else
return a[i].first+(l-d)*1.0/(a[i].second+b[j].second);
}
}

int main()
{
std::freopen("../particles_tests/particles.20.in", "r", stdin);
std::freopen("particles.out", "w", stdout);

int i,i1,j;
scanf("%d%d%d",&n,&l,&k);

vector<tip> a(n),b(n),c(n),d(n);

for(i=0;i<n;i++)
scanf("%d%d",&a[i].first,&a[i].second),a[i].nr=i;

for(i=0;i<n;i++)
scanf("%d%d",&b[i].first,&b[i].second),b[i].nr=i;

for(i1=0;i1<k;i1++)
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 440

{
c.clear(),d.clear();
for(i=0;i<n;i++)
{
if (c.size() && c.back().second>a[i].second)
continue;
c.push_back(a[i]);
c.back().nr=i;
}

for(i=0;i<n;i++)
{
if (d.size() && d.back().second>b[i].second)
continue;
d.push_back(b[i]);
d.back().nr=i;
}

double t=1e18;
int in1=-1,in2=-1;
for(i=0;i<(int)c.size();i++)
{
int r=(int)d.size()-1;
for(j=0;j<=r;j++)
{
double num=get(i,j,c,d);
if (t>num)
{
t=num;
in1=c[i].nr;
in2=d[j].nr;
}
}
}

printf("%d %d\n",a[in1].nr+1,b[in2].nr+1);
a.erase(a.begin()+in1);
b.erase(b.begin()+in2);
n--;
}

return 0;
}

Listing 10.2.7: 1497611-particles.cpp


// https://csacademy.com/submission/1497611/ execution time : 0.797 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/

#include <bits/stdc++.h>
#define err(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)

using namespace std;

typedef long long ll;

struct cond
{
int first, second, ind;
};

int n, L, k;

double get(int i, int j, vector<cond> &A, vector<cond> &B)


{
if (A[i].first < B[j].first)
{
ll d1 = 1ll * A[i].second * (B[j].first - A[i].first);
if (d1 > L) return 1e18;
return B[j].first + (L - d1) * 1.0 / (A[i].second + B[j].second);
}
else
{
ll d1 = 1ll * B[j].second * (A[i].first - B[j].first);
if (d1 > L) return 1e18;
CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 441

return A[i].first + (L - d1) * 1.0 / (A[i].second + B[j].second);


}
}

int main()
{
std::freopen("../particles_tests/particles.20.in", "r", stdin);
std::freopen("particles.out", "w", stdout);

scanf("%d %d %d", &n, &L, &k);


vector<cond> A(n), B(n), C(n), D(n);

for (int i = 0; i < n; i++)


{
scanf("%d %d", &A[i].first, &A[i].second);
A[i].ind = i;
}

for (int i = 0; i < n; i++)


{
scanf("%d %d", &B[i].first, &B[i].second);
B[i].ind = i;
}

for (int it = 0; it < k; it++)


{
C.clear(), D.clear();

for (int i = 0; i < n; i++)


{
if (C.size() && C.back().second > A[i].second) continue;
C.push_back(A[i]);
C.back().ind = i;
}

for (int i = 0; i < n; i++)


{
if (D.size() && D.back().second > B[i].second) continue;
D.push_back(B[i]);
D.back().ind = i;
}

double t = 1e18;
int ind1 = -1, ind2 = -1;

for (int i = 0; i < (int)C.size(); i++)


{
int l = 0, r = (int)D.size() - 1;

for (int j = l; j <= r; j++)


{
double tmp = get(i, j, C, D);
if (t > tmp)
{
t = tmp;
ind1 = C[i].ind;
ind2 = D[j].ind;
}
}
}

int a = A[ind1].ind;
int b = B[ind2].ind;

printf("%d %d\n", a + 1, b + 1);

A.erase(A.begin() + ind1);
B.erase(B.begin() + ind2);
n--;
}

return 0;
}

Listing 10.2.8: 1628810-particles.cpp


CAPITOLUL 10. EJOI 2017 10.2. PARTICLES 442

// https://csacademy.com/submission/1628810/ execution time : 4.358 s


// https://csacademy.com/contest/ejoi-2017-day-1/task/particles/statistics/

#include<bits/stdc++.h>

#define maxx 50005

using namespace std;

int n, lgth, K;
int tx[maxx], vx[maxx], ty[maxx], vy[maxx];
bool flag, fx[maxx], fy[maxx];

main()
{
std::freopen("../particles_tests/particles.20.in", "r", stdin);
std::freopen("particles.out", "w", stdout);

cin >> n >> lgth >> K;


for (int i=0; i<n; i++) cin >> tx[i] >> vx[i];
for (int i=0; i<n; i++) cin >> ty[i] >> vy[i];

for (int i=0; i<K; i++)


{
if(flag) cout<<endl;
flag=1;
double l = 0, r = INT_MAX;
int mx = -1, my = -1;
for (int j=0; j<40; j++)
{
double m =(l+r)/2, pl=0, pr=lgth;
for (int k = 0; k < n; k++)
{
if (!fx[k])
{
double ql = (m - tx[k])*vx[k];
if(pl<ql) pl=ql, mx=k;
}

if (!fy[k])
{
double qr = lgth-(m - ty[k])*vy[k];
if(pr>qr) pr=qr, my=k;
}

pl<pr ? l=m : r=m;


}

fx[mx]=fy[my]=1;

cout <<mx+1<<’ ’<<my+1;


}
}

Listing 10.2.9: checkerParticles.cpp


#include "testlib0.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../particles_tests/particles.20.in",
(char*)"particles.out",
(char*)"../particles_tests/particles.20.sol",
};
CAPITOLUL 10. EJOI 2017 10.3. SIX 443

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("particles", argc, argv);


compareRemainingLines();
}

10.2.3 *Rezolvare detaliată

10.3 Six
Problema 3 - Six 100 de puncte
Elly studies the properties of some given integer N . So far she has discovered that it has no
more than six distinct prime divisors. A prime number (or a prime) is a natural number greater
than 1 that has no positive divisors other than 1 and itself.
Now the girl spends her time in the following way. Starting with an empty list, she writes
divisors of N , greater than 1 (some divisors she may repeat several times). When adding a new
number to the list, she makes sure that it has common divisors greater than 1 with at most one
of the already written numbers.
For example, if the number N is 12156144, some of the many possible valid sequences the girl
can generate are (42), (616, 6, 91, 23), (91, 616, 6, 23), (66, 7), (66, 7, 7, 23, 299, 66), (143, 13,
66), and (42, 12156144). Examples for invalid sequences would be (5, 11), since 5 is not a divisor
of 12156144, or (66, 13, 143), since 143 has common divisors with both 13 and 66.
Now Elly is wondering how many different valid sequences of divisors of N exist. We consider
two sequences different if they have different length or there is a position, in which they have
different numbers.
Task
Write a program six that helps Elly to find the number of valid sequences of divisors of N .
Input
From the first line of the standard input your program has to read one integer N .
Output
On the standard output your program has to print one integer - the number of different
sequences of divisors of N , which could have been written by Elly. Since this number can be
rather large, you are required to print only its remainder when divided by 1 000 000 007.
Constraints

ˆ 1&N & 1015


ˆ In around 30% of the tests N will have at most 2 distinct prime divisors.
ˆ In around 60% of the tests N will have at most 4 distinct prime divisors.

ˆ In 100% of the tests N will have at most 6 distinct prime divisors.

Example
Sample Input Sample Output
6 28
203021 33628
60357056536 907882
12156144 104757552
Explanation: All 28 valid sequences in the first sample are: {(2), (2, 2), (2, 2, 3), (2, 2, 3, 3),
(2, 3), (2, 3, 2), (2, 3, 2, 3), (2, 3, 3), (2, 3, 3, 2), (2, 6), (2, 6, 3), (3), (3, 2), (3, 2, 2), (3, 2, 2, 3),
CAPITOLUL 10. EJOI 2017 10.3. SIX 444

(3, 2, 3), (3, 2, 3, 2), (3, 3), (3, 3, 2), (3, 3, 2, 2), (3, 6), (3, 6, 2), (6), (6, 2), (6, 2, 3), (6, 3), (6,
3, 2), (6, 6)}
In the last example the answer is 14104757650, but since you are required to print it modulo
1 000 000 007, the actual result is 14104757650 % 1000000007 = 104757552.

10.3.1 Indicaţii de rezolvare

The main thing that the contestants must have noticed in this problem was that N has at most
six distinct prime divisors. Note that these 6 primes will be not only in the factorization of N ,
but also in the factorization of all of its divisors - the numbers, Elly is writing.
The first thing the contestants should do before getting to any solution is find the (up to)
6 prime factors of N . This can be done quite trivially, using the O sqrt N  algorithm. Since
15
N was up to 10 , this would require around 32 million operations - a fraction of a second on a
processor manufactured in the past 10 years.
Now, let’s get to the main part of the problem. As with many other problems where the
question is ”how many” and the answer can be pretty large (thus, the modulus), here as well
the solution is based on dynamic programming. We’ll need to have a state, which tells us which
divisors of N we can write at any given time.
In order two numbers in the list to be coprime (to have no common divisors greater than or
equal to 2), they must share no prime number in their factorization. Thus, keeping track which
of the primes have not been used, which have been used once, and which have been used twice
should do the trick, right?
Well, almost. Unfortunately, it is not entirely enough and will most likely be a common
problem the competitors face. It is fairly intuitive to go this way (use a ternary mask for the
state), however we’ll show that this is actually incorrect. Luckily for them, the examples are quite
strong and reveal this!
The problem with the solution above is that it matters whether two primes (which were used
once) were used in the same number or in two different numbers. Consider the following sequences:
1. (2, 3, 30)
2. (6, 30)
The first one is invalid, because 30 has common divisors with both 2 and 3 (two different
numbers from the list). However, the second one is valid - 30 still has common divisors with 2
and 3, but this time they are used in the same number 6.
So, we need a state, which both:
ˆ Keeps track which primes were used 0, 1, and 2 times.
ˆ Keeps track which primes were used together.
We have several possible solutions. Let’s start with the easiest one.
Approach 1 (keep all occurrences of each factor)
To get to this approach we need to make the observation that the list Elly writes can have at
most 12 elements. This should be fairly obvious, as each prime can be used at most twice (and 6
* 2 = 12).
Instead of storing 0 (not used), 1 (used once), or 2 (used twice) for each prime, we can store
not used (no restrictions), used twice (cannot use) or the position, at which it was used. Knowing
the position where each of the primes was used helps us solve the problem we outlined above.
We will need 14 values for each prime: 0-11 will represent the position at which it was used,
12 will mean it was not used and 13 will mean it was already used twice. This means a DP with
6
14 = 7,529,536 states, which is okay from memory point of view (the answer fits into 4int, thus
we’ll need around 30 megabytes for the DP table).
In terms of time, for each state we have to choose which divisor of N to use. Since we have up
6
to 6 primes, the number of divisors is 2 64. This looks bad at first, since, if we do the math,
6 6
we’ll need around 14 ˜ 2 = 481,890,304 operations in the worst case - more than we can afford
for the given time limit.
However, we can easily see that many of the states are invalid (can never be reached), thus
shouldn’t be counted towards that (see below for explanation why).
CAPITOLUL 10. EJOI 2017 10.3. SIX 445

In fact, running the last example (which has six prime divisors, i.e., should reach as many
states as possible) reaches only 170,000 out of the 7,500,000 possible states. With that many
actual states, the 64 operations per state are quite okay - this solution runs quite quickly on any
input with these constraints, thus it gets 100 points.
Why are the valid states so few?
How can we deduce that many of the states will be invalid? Let’s assume that as the first
number in the list we use a divisor, which includes multiple factors. This drastically reduces the
maximal length of the list. For example, if the first element is N itself (thus, uses all 6 factors),
the length of the list is suddenly limited to 7.
Moreover, an average divisor of N has (1 + 6) / 2 = 3.5 (thus 3 or 4) prime factors. This
means that the average length of a valid list is only around 12 / 3.5 = 3.42 (thus 3 to 4) numbers!
From here we can conclude that, although there are valid sequences with 12 elements, most of the
sequences will have only few. If we assume that in most cases the length of the list is no more
6
than 6, our calculation for the number of reachable states would be 6  2 = 262,144 - much
closer to what we get empirically.
Approach 2 (map-based solution)
The contestants could exploit the low number of states, by using a map-based solution (in-
stead of figuring out the perfect state). Since we don’t care where in the list we have writ-
ten each of the previous numbers, only which numbers we have written, we can instead use
map <vector<int>, int> as a dynamic table. This approach is pretty simple and, as long as
the vector is sorted, also relatively fast - it would get 80 points.
Approach 3 (smart solution)
We can do another observation (this time a hard one) to get a better state. We already saw
that the number of reachable states is much lower than what our table covers. What if we make
the table smarter (less sparse)?
We don’t really care on which position each of the prime factors was used, only which pairs
of prime factors cannot be used together anymore. For example, if we’ve already written 2 and
3, we know that we cannot use a number, which is divisible by 6. If we’ve written the numbers 6
and 35, we know that we cannot use the pairs (2, 5), (2, 7), (3, 5), and (3, 7) - thus the numbers
10, 14, 15, and 21 cannot be divisors of any new number.
How many pairs there are? Since (2, 3) and (3, 2) are the same, we’ll only need information
about 6 * 7 / 2 = 21 pairs. And we can use a bitmask for that.
21
This brings the table to 2 = 2,097,152 states (slightly lower than the previous one). In fact,
many of those are also not reachable. For example, if we know that the pairs (2, 3) and (5, 7) are
forbidden, but (3, 5) is not, then we can conclude that (2, 5) should also be forbidden (because
3 and 5 were apparently used in the same number). Checking this empirically shows that only
slightly over 3000 states were actually reached! It turns out that this solution is not only better
in terms of memory efficiency, but also much faster as runtime as well. The number of states is
so low, that even a map-based solution with this idea gets a full score.
I’d be interested in what is the best state the contestants come up with, since even this approach
is very inefficient in terms of state.
Naive (bruteforce) approaches
Of course, the contestants could have tried other simpler approaches - for example bruteforce.
Depending on how smart the backtrack was written, they could have gotten quite a lot of points.
This was done on purpose, since the smart solutions are on the hard side. The trivial bruteforce,
for example, was supposed to get around 40 points. An optimized one could get around 60.

10.3.2 Coduri sursă

Listing 10.3.1: 481436-six.cpp


// https://csacademy.com/submission/481436/ execution time : 0.031 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/
CAPITOLUL 10. EJOI 2017 10.3. SIX 446

/*
ID: espr1t
TASK: Six
KEYWORDS: Hard, Dynamic Programming, Bitmasks
*/

#include <cstdio>
#include <vector>
#include <map>

using namespace std;

const int MOD = 1000000007;

vector <int> masks;


vector <long long> ways;
map <long long, int> dyn;

// Provides constant access to (a bitmask of) invalid pairs of divisors


long long cross(int mask1, int mask2)
{
long long ret = 0, bit = 0;
for (int i = 0; i < 6; i++)
{
for (int c = i; c < 6; c++)
{
if (((mask1 & (1 << i)) && (mask2 & (1 << c))) ||
((mask2 & (1 << i)) && (mask1 & (1 << c))))
ret |= (1LL << bit);
bit++;
}
}

return ret;
}

// used is bitmask of all previously used prime factors.


// It is uniquely identified by the forbidden pairs,
// so we don’t have to store it in the state
long long recurse(long long mask, int used)
{
if (dyn.find(mask) != dyn.end())
return dyn[mask];

long long ans = 1;


// For each subset of prime factors which doesn’t contain
// forbidden pair continue the sequence
for (int i = 0; i < (int)masks.size(); i++)
if ((mask & cross(masks[i], masks[i])) == 0)
ans = (ans + ways[i] * recurse(mask | cross(masks[i], used),
used | masks[i])) % MOD;

return dyn[mask] = ans;


}

void getDivisors(long long n)


{
// Get factors of N
vector < pair <long long, int> > facts;
for (long long d = 2; d * d <= n; d++)
{
if (n % d == 0)
{
facts.push_back(make_pair(d, 0));
while (n % d == 0)
n /= d, facts.back().second++;
}
}

if (n > 1)
facts.push_back(make_pair(n, 1));

// All subsets of factors of N yield a proper divisor


for (int mask = 1; mask < (1 << (int)facts.size()); mask++)
{
long long cnt = 1;
CAPITOLUL 10. EJOI 2017 10.3. SIX 447

for (int i = 0; i < (int)facts.size(); i++)


if (mask & (1 << i)) cnt = (cnt * facts[i].second) % MOD;

masks.push_back(mask),
ways.push_back(cnt);
}
}

int main(void)
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

long long n;
fscanf(stdin, "%lld", &n);

// Get the number N,


// all of its prime divisors and all their proper subsets
getDivisors(n);

long long ans = 0;


for (int idx1 = 0; idx1 < (int)masks.size(); idx1++)
{
// Sequence of only one number
ans = (ans + ways[idx1]) % MOD;

// Sequences of at least two numbers


for (int idx2 = 0; idx2 < (int)masks.size(); idx2++)
{
int used = masks[idx1] | masks[idx2];
long long mask = cross(masks[idx1], masks[idx2]);
ans = (ans +
(ways[idx1] * ways[idx2]) % MOD *
recurse(mask, used)) % MOD;
}
}

fprintf(stdout, "%lld\n", ans);

return 0;
}

Listing 10.3.2: 482515-six.cpp


// https://csacademy.com/submission/482515/ execution time : 0.031 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<time.h>
#include<algorithm>
#include<map>
#include<set>
#include<iostream>
#include<vector>

using namespace std;

#define BigInteger long long int

BigInteger FACTORLIST[50];
int EXPONENTLIST[50],BIGOMEGA;

int smallprimes[168]=
{
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,101,103,107,109,113,127,131,
137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,
227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,
313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,
419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,
509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,
CAPITOLUL 10. EJOI 2017 10.3. SIX 448

617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,
727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,
829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,
947,953,967,971,977,983,991,997
};
/* small table */

BigInteger powmod(BigInteger,BigInteger,BigInteger);
int strong_pseudo_primetest(BigInteger,int);
int isprime(BigInteger);
BigInteger gcd(BigInteger,BigInteger);
void factor_using_pollard_rho (BigInteger n,int);
void factor(BigInteger n);

BigInteger mulmod(const BigInteger&a, const BigInteger&b, const BigInteger&p)


{
long long int y = (long long int)((long double)a*(long double)b/p +
(long double)(0.5));
long long int r=a*b-y*p;
if (r<0) r = r+p;

return r;
// return a*b%p;
}

BigInteger powmod(BigInteger n,BigInteger exponent,BigInteger modulus)


{
/* return by (nˆexponent)%modulus, here exponent>=0 */

BigInteger result=1,
powres=n%modulus;

while(exponent>0)
{
if(exponent%2==1)
result=mulmod(result,powres,modulus);
powres=mulmod(powres,powres,modulus);
exponent/=2;
}

return result;
}

int strong_pseudo_primetest(BigInteger n,int base)


{
/* return by 1 if n is a strong pseudo prime for base, otherwise by 0 */

BigInteger n2=n-1,res;
int s;
s=0;

while(n2%2==0) n2/=2,s++;

res=powmod(base,n2,n);
if((res==1)||(res==n-1)) return 1;

s--;
while(s>=0)
{
res=mulmod(res,res,n);
if(res==n-1) return 1;
s--;
}

return 0;
}

int isprime(BigInteger n)
{
if(n<2) return 0;
if(n<4) return 1;

if(strong_pseudo_primetest(n,2)==0) return 0;
if(strong_pseudo_primetest(n,3)==0) return 0;
if(n<1373653LL) return 1;
if(strong_pseudo_primetest(n,5)==0) return 0;
CAPITOLUL 10. EJOI 2017 10.3. SIX 449

if(n<25326001LL) return 1;
if(strong_pseudo_primetest(n,7)==0) return 0;
if(n==3215031751LL) return 0;
if(n<25000000000LL) return 1;
if(strong_pseudo_primetest(n,11)==0) return 0;
if(n<2152302898747LL) return 1;
if(strong_pseudo_primetest(n,13)==0) return 0;
if(n<3474749660383LL) return 1;
if(strong_pseudo_primetest(n,17)==0) return 0;
if(n<341550071728321LL) return 1;
if(strong_pseudo_primetest(n,19)==0) return 0;
if(strong_pseudo_primetest(n,23)==0) return 0;
if(strong_pseudo_primetest(n,29)==0) return 0;
if(strong_pseudo_primetest(n,31)==0) return 0;
if(strong_pseudo_primetest(n,37)==0) return 0;
return 1;
/* are we sure that n is prime?!
Up to 2ˆ63 there can be some exceptions, but very few. */
}

BigInteger gcd(BigInteger a,BigInteger b)


{
BigInteger c;
if(a==0) return b;
if(b==0) return a;

while(b>0)
{
a%=b;
c=a;
a=b;
b=c;
}

return a;
}

void factor_using_pollard_rho (BigInteger n,int a)


{
/* factorization by Pollard method
x -> (xˆ2+a)%n is the sequence in the factorization */
BigInteger y,x,x1,g,P,t1;
int c,i,k,l,expo,count,err;
y=2;
x=2;
x1=2;
k=1;
l=1;
P=1;
c=0;

while(n!=1)
{
S2:
x=mulmod(x,x,n);
x=(x+a)%n;
t1=n+x1-x;
if(t1>=n)t1-=n;
P=mulmod(P,t1,n);

c++;
if(c==20)
{
c=0;
g=gcd(P,n);
if(g!=1) goto S4;
y=x;
}
S3:
k--;
if(k>0) goto S2;

g=gcd(P,n);
if(g!=1) goto S4;

x1=x;
CAPITOLUL 10. EJOI 2017 10.3. SIX 450

k=l;
l=2*l;
for(i=0;i<k;i++)
{
x=mulmod(x,x,n);
x=(x+a)%n;
}
y=x;
c=0;
goto S2;

S4:
err=0;
count=0;
do
{
if(count>l)
{
err=1;
break;
}
y=mulmod(y,y,n);
y=(y+a)%n;
t1=n+x1-y;
if(t1>=n) t1-=n;
g=gcd(t1,n);
count++;
} while(g==1);

if(err==1)
{
do
{
a=rand();
} while(a<4);

factor_using_pollard_rho(n,a);
break;
}
else
{
n=n/g;
if(isprime(g)==0)
{
do
{
a=rand();
} while(a<4);

factor_using_pollard_rho(g,a);
}
else
{
expo=1;
while(n%g==0) n/=g,expo++;
FACTORLIST[BIGOMEGA]=g;
EXPONENTLIST[BIGOMEGA]=expo;
BIGOMEGA++;
}
}

x=mulmod(x,x,n);
if(isprime(n)==1)
{
FACTORLIST[BIGOMEGA]=n;
EXPONENTLIST[BIGOMEGA]=1;
BIGOMEGA++;
n=1;
}
}

return;
}

void factor(BigInteger n)
{
CAPITOLUL 10. EJOI 2017 10.3. SIX 451

int i,expo;
/* trial divide by the first 168 primes (primes up to 1000) */
BIGOMEGA=0;
if(n<2) return;

for(i=0;(i<168)&&(smallprimes[i]*smallprimes[i]<=n);i++)
{
if(n%smallprimes[i]==0)
{
n/=smallprimes[i],expo=1;
while(n%smallprimes[i]==0) n/=smallprimes[i],expo++;
FACTORLIST[BIGOMEGA]=smallprimes[i];
EXPONENTLIST[BIGOMEGA]=expo;
BIGOMEGA++;
}
}

if(n==1) return;

if((n<1000000)||(isprime(n)==1))
{
FACTORLIST[BIGOMEGA]=n;
EXPONENTLIST[BIGOMEGA]=1;
BIGOMEGA++;
return;
}
/* at here we know n is composite */
factor_using_pollard_rho(n,3);
}

#define mod 1000000007


int add(int x,int y)
{
return (x+=y)<mod?x:x-mod;
}

int sub(int x,int y)


{
return (x-=y)>=0?x:x+mod;
}

int mul(int x,int y)


{
return (int)((long long int)x*y%mod);
}

int w,sz[64];

map<vector<int>,int> Map;

vector<vector<int> > Lis;


int aa[6],r[6],lr;

int to[3333][64];
int dp[2][3333];

void dfs(int i)
{
if(i == w)
{
vector<int> tmp;
for(int j=0; j<w; j++) tmp.push_back(aa[j]);
Map[tmp] = Lis.size();
Lis.push_back(tmp);
return;
}

aa[i] = -1;
dfs(i+1);
aa[i] = -2;
dfs(i+1);
r[lr++] = i;
aa[i] = i;
dfs(i+1);
lr--;
for(int j=0; j<lr; j++)
CAPITOLUL 10. EJOI 2017 10.3. SIX 452

{
aa[i] = r[j];
dfs(i+1);
}
}

int ace[6];
int ancestor(int x)
{
return x-ace[x]?ace[x]=ancestor(ace[x]):x;
}

void fin()
{
w = BIGOMEGA;
for(int i=0; i<(1<<w); i++)
{
sz[i] = 1;
for(int j=0; j<w; j++)if(i&(1<<j))sz[i] = mul(sz[i], EXPONENTLIST[j]);
}

Map.clear();
Lis.clear();
dfs(0);

int len = Lis.size();


for(int i=0; i<len; i++)for(int j=1; j<(1<<w); j++)
{
to[i][j] = -1;
bool flag = true;
for(int k=0; k<w; k++)
ace[k] = k;

for(int k=0; k<w; k++)


{
aa[k] = Lis[i][k];
if(aa[k] >= 0)
{
ace[ancestor(aa[k])] = ancestor(k);
}
}

int rr = -1;
int bad = 0;
for(int k=0; k<w; k++)
{
if(j&(1<<k))
{
if(aa[k] == -2)flag = false;else
if(aa[k] != -1)
{
aa[k] = -2;
if(bad == 0)
bad |= 1<<ancestor(k);
else
if(bad != (1<<ancestor(k)))
flag = false;
}
else
{
if(rr==-1)rr=k;
aa[k] = rr;
ace[ancestor(k)] = ancestor(rr);
}
}
}

if(!flag)continue;

vector<int> tmp;
for(int k=0; k<w; k++)if(aa[k] >= 0)
{
int rt;
for(rt=0; ancestor(rt) != ancestor(k) || aa[rt] < 0; rt++);
tmp.push_back(rt);
}
CAPITOLUL 10. EJOI 2017 10.3. SIX 453

else
tmp.push_back(aa[k]);

if(Map.count(tmp) == 0)
{
tmp.clear();
}

to[i][j] = Map[tmp];
}

memset(dp[0],0,sizeof(dp[0]));

dp[0][0] = 1;
int res = 0;
while(true)
{
memset(dp[1],0,sizeof(dp[1]));
for(int i=0; i<len; i++)
{
if(dp[0][i]==0) continue;

for(int j=1; j<(1<<w); j++)


if(to[i][j] != -1)
dp[1][to[i][j]] = add(dp[1][to[i][j]], mul(dp[0][i], sz[j]));
}

bool flag = true;


for(int i=0; i<len; i++)
{
dp[0][i] = dp[1][i];
if(dp[1][i] == 0)continue;
res = add(res, dp[1][i]);
flag = false;
}

if(flag)break;
}

printf("%d\n",res);
}

int main()
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

BigInteger n;
srand(time(0));

cin >> n;
factor(n);
// for(w=1; w<=6; w++)
fin();
return 0;
}

132
OBS1: strong pseudo prime
vspace2mm
133
OBS2: Pollard method

Listing 10.3.3: 505367-six.cpp


// https://csacademy.com/submission/505367/ execution time : 0.031 s
// https://csacademy.com/submission/2227515/ execution time : 0.031 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
132
https://en.wikipedia.org/wiki/Strong_pseudoprime
133
https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
CAPITOLUL 10. EJOI 2017 10.3. SIX 454

#include <queue>
#include <cstring>
#include <cmath>

using namespace std;

#define PB push_back
#define MP make_pair
#define SZ(v) ((int)(v).size())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define REP(i,n) FOR(i,0,n)
#define FORE(i,a,b) for(int i=(a);i<=(b);++i)
#define REPE(i,n) FORE(i,0,n)
#define FORSZ(i,a,b) FOR(i,a,SZ(v))
#define REPSZ(i,v) REP(i,SZ(v))

typedef long long ll;

const int MAXP=6;


const int MAGIC=100000;
const int MOD=1000000007;
const int PTESTCNT=12;
const int PTEST[PTESTCNT]={2,3,5,7,11,13,17,19,23,29,31,37};

ll N;

int pcnt[MAXP],np;

ll modmlt(ll a,ll b,ll mod)


{
ll ret=0;
for(;b>0;b>>=1,a=(a<<1)%mod)
if(b&1)
if((ret+=a)>=mod)
ret-=mod;
return ret;
}

ll modpw(ll x,ll n,ll mod)


{
ll ret=1;
while(true)
{
if(n&1) ret=modmlt(ret,x,mod);
if((n>>=1)==0) return ret;
x=modmlt(x,x,mod);
}
}

bool ismillerrabinprime(ll n)
{
if(n<2) return false;
ll d=n-1;
int r=0;
while(d%2==0) d/=2,++r;
REP(i,PTESTCNT)
{
int a=PTEST[i];
ll x=modpw(a,d,n);
if(x==1||x==n-1) continue;
REP(j,r-1)
{
x=modmlt(x,x,n);
if(x==1||x==n-1) break;
}
if(x!=n-1) return false;
}
return true;
}

void decompose()
{
ll x=N; np=0;
for(int i=2;i<=MAGIC;++i) if(x%i==0)
{
pcnt[np]=0;
CAPITOLUL 10. EJOI 2017 10.3. SIX 455

while(x%i==0) ++pcnt[np],x/=i;
++np;
}

if(x==1) return;
int y=(int)sqrt(1.0*x);
if((ll)y*y==x)
{
pcnt[np++]=2;
return;
}

if(!ismillerrabinprime(x))
{
pcnt[np++]=1;
pcnt[np++]=1;
return;
}

pcnt[np++]=1;
}

int mem[MAXP][2*MAXP+1][1<<(2*MAXP)];

int go(int at,int cnt,int mask)


{
if(at>=np) return 1;
int &ret=mem[at][cnt][mask];

if(ret==-1)
{
ret=go(at+1,cnt,mask);
REPE(a,2*cnt)
{
int acnt=cnt,amask=mask,apos;
if(a%2==1)
apos=(a-1)/2;
else
{
apos=a/2;
++acnt;
amask=(mask&((1<<apos)-1))|((mask>>apos)<<(apos+1));
}

int acur=go(at+1,acnt,amask);
ret=(ret+(ll)acur*pcnt[at])%MOD;

//if(mask==2) printf("\t%d -> %d,%d,%d\n",a,at+1,acnt,amask);


FORE(b,2*apos+2,2*acnt)
{
int bcnt=acnt,bmask=amask,bpos;
if(b%2==1)
{
bpos=(b-1)/2;
if(bmask&(1<<bpos))
continue;
else
bmask|=1<<bpos;
}
else
{
bpos=b/2;
++bcnt;
bmask=(amask&((1<<bpos)-1))|
((amask>>bpos)<<(bpos+1))|
(1<<bpos); }

int bcur=go(at+1,bcnt,bmask);
ret=(ret+(ll)bcur*pcnt[at]*pcnt[at])%MOD;

//if(mask==2) printf("\t%d,%d -> %d,%d,%d\n",a,b,at+1,bcnt,bmask);


}
}

REP(i,cnt) if(mask&(1<<i))
{
CAPITOLUL 10. EJOI 2017 10.3. SIX 456

int ccur=go(at+1,cnt,mask);
ret=(ret+(ll)ccur*pcnt[at]*pcnt[at])%MOD;
}

//printf("go(%d,%d,%d)=%d\n",at,cnt,mask,ret);
}

return ret;
}

int main()
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

scanf("%lld",&N);
decompose();

//printf("pcnt:"); REP(i,np) printf(" %d",pcnt[i]); puts("");

memset(mem,-1,sizeof(mem));
int ret=(go(0,0,0)+MOD-1)%MOD;

printf("%d\n",ret);
}

134
OBS: Miller-Rabin primality test

Listing 10.3.4: 1275534-six.cpp


// https://csacademy.com/submission/1275534/ execution time : 0.031 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int, int> ii;

#define X first
#define Y second
#define vi vector<int>
#define vvi vector< vi >
#define vii vector< ii >
#define mp make_pair
#define pb push_back

int ndiv;
vector<int> fdiv;
const int md = 1e9+7;

void add(int &a, int b)


{
a += b;
if(a>= md) a -=md;
}

int mul(int a, int b)


{
return (1LL*a*b)%md;
}

int ways[(1<<6) + 5];

map<ll, int> dp;

void proc(ll N)
{
for(ll i = 2; i*i<= N; i++)
{
if(N%i == 0)
{
134
https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
CAPITOLUL 10. EJOI 2017 10.3. SIX 457

fdiv.pb(0);
}
while(N%i == 0)
{
fdiv.back()++;
N /= i;
}
}

if(N> 1)
{
fdiv.pb(1);
}

ndiv = fdiv.size();
for(int i = 0; i< (1<<ndiv); i++)
{
ways[i] = 1;
for(int j = 0; j< ndiv; j++)
{
if((1<<j) & i)
{
ways[i] = mul(ways[i], fdiv[j]);
}
}
//printf("ways[%d] = %d\n", i, ways[i]);
}
}

ll cross(int x, int y)
{
ll res = 0;
for(int i = 0; i< ndiv; i++)
{
for(int j = 0; j< ndiv; j++)
{
if( ( ( (1<<i) & x ) and ( (1<<j) & y ) ) or
( ( (1<<j) & x ) and ( (1<<i) & y ) ) )
{
res |= (1ll<<(ndiv*i+j));
}
}
}

return res;
}

int go(ll mask, int cur)


{
if(dp.find(mask) != dp.end()) return dp[mask];

//system("pause");
//printf("arrived %lld %d\n", mask, cur);

int res = 1;
for(int i = 1; i< (1<<ndiv); i++)
{
int ok = 1;
for(int x1 = 0; x1< ndiv; x1++)
{
for(int x2 = 0; x2< ndiv; x2++)
{
if( ( i & (1<<x1) ) and
(i & (1<<x2) ) and
(mask & (1ll<<(x1*ndiv+x2) ) ) )
{
ok = 0; break;
}
}
}

if(ok)
{
//printf("go %d to (%lld, %d)\n", i, mask|cross(cur, i), cur|i);
add(res, mul(ways[i], go(mask|cross(cur, i), cur|i)));
}
CAPITOLUL 10. EJOI 2017 10.3. SIX 458

//printf("dp[%lld][%d] = %d\n", mask, cur, res);

return dp[mask] = res;


}

int main()
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

ll N;
scanf("%lld", &N);
proc(N);
int res = 0;
for(int i = 1; i< (1<<ndiv); i++)
{
//printf("%d:\n", i);
add(res, ways[i]);
//printf("%d\n", res);
for(int j = 1; j< (1<<ndiv); j++)
{
add(res, mul(mul(ways[i], ways[j]), go(cross(i, j), i|j)));
}
//printf("%d\n", res);
}

printf("%d\n", res);
}

Listing 10.3.5: 1633337-six.cpp


// https://csacademy.com/submission/1633337/ execution time : 0.760 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include <bits/stdc++.h>

#define ll long long


#define ull unsigned long long
#define inf 1000000007
#define inf16 0x3f3f3f3f
#define INF 1000000000000000007LL
#define VI vector<int>
#define VPII vector<pair<int, int> >
#define VLL vector<ll>
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
#define mp make_pair
#define eb emplace_back
#define endl ’\n’
#define ALL(c) (c).begin(), (c).end()
#define SIZE(x) (int)(x).size()

using namespace std;

const int MOD = 1e9+7;


ll n, res;
int f;

vector<pair<int,int> > fact;


ll ile[1<<6];

map<ll,ll> memo[1<<6];

ll solve(int s, ll p)
{
if(memo[s].find(p)!=memo[s].end())
return memo[s][p];

ll res = 1;
for(int i = 1; i < (1<<f); ++i)
{
CAPITOLUL 10. EJOI 2017 10.3. SIX 459

bool ok = 1;
for(int j = 0; j < f; ++j)
{
for(int k = j; k < f; ++k)
{
if((p&(1ll<<(6*j+k))) && (i&(1<<j)) && (i&(1<<k)))
{
ok = 0;
break;
}
}
}

if(ok)
{
ll nowe = 0;
for(int j = 0; j < f; ++j)
{
for(int k = 0; k < f; ++k)
{
if((s&(1<<j)) && (i&(1<<k)))
nowe |= (1ll<<(6*min(j, k)+max(j, k)));
}
}

res += solve(s|i, p|nowe)*ile[i];


res %= MOD;
}
}

memo[s][p] = res;

return res;
}

int main()
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

cin >> n;
ll curr = n;
for(ll i=2;i*i<=n;++i)
{
if(curr%i==0)
{
fact.eb(i,0);
while(curr%i==0)
{
curr /= i;
++fact.back().second;
}
}
}

if(curr>1)
fact.eb(curr, 1);

f = fact.size();
for(int i = 1; i < (1<<f); ++i)
{
ile[i] = 1;
for(int j = 0; j < f; ++j)
{
if(i&(1<<j))
ile[i] *= fact[j].second;
}
}

cout << (solve(0, 0)-1+MOD)%MOD << endl;

return 0;
}

Listing 10.3.6: 1709178-six.cpp


CAPITOLUL 10. EJOI 2017 10.3. SIX 460

// https://csacademy.com/submission/1709178/ execution time : 0.031 s


// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include <bits/stdc++.h>

#define pb push_back
#define mp make_pair

#define all(x) (x).begin(), (x).end()

#define fi first
#define se second

using namespace std;

typedef long long ll;

const int mod = (int)1e9 + 7;

map<vector<int>, int> mm;

int w[10005];

int k;

ll n;

void addMod(int &a, int b, int m = mod)


{
a += b;

if (m <= a)
{
a -= m;
}
}

int calc(vector<int> v)
{
if (mm.find(v) != mm.end())
{
return mm[v];
}

int ret = 0;

for (int m = 1; m < (1 << k); ++m)


{
if (v[m] == 2)
{
continue;
}

vector<int> nxt = v;

for (int i = 1; i < (1 << k); ++i)


{
if ((i & m) > 0)
{
nxt[i] = min(2, nxt[i] + 1);
}
}

addMod(ret, w[m] * (calc(nxt) + 1ll) % mod);


}

return mm[v] = ret;


}

void solve()
{
scanf("%lld", &n);
vector<pair<ll, int>> vv;
CAPITOLUL 10. EJOI 2017 10.3. SIX 461

for (ll i = 2; i * i <= n; ++i)


{
if (n % i == 0)
{
int cnt = 0;

while (n % i == 0)
{
++cnt;
n /= i;
}

vv.pb(mp(i, cnt));
}
}

if (n > 1)
{
vv.pb(mp(n, 1));
}

k = vv.size();

for (int m = 0; m < (1 << k); ++m)


{
w[m] = 1;

for (int i = 0; i < k; ++i)


{
if (m & (1 << i))
{
w[m] = (w[m] * 1ll * vv[i].se) % mod;
}
}
}

printf("%d\n", calc(vector<int>(1 << k, 0)));


}

int main()
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

int tt = 1;

while (tt--)
{
solve();
}

return 0;
}

Listing 10.3.7: 2123900-six.cpp


// https://csacademy.com/submission/2123900/ execution time : 0.031 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

/*************************************************************************
> File Name: Six.cpp
> Author: Roundgod
> Mail: wcysai@foxmail.com
> Created Time: 2018-12-27 17:54:48
************************************************************************/

#pragma GCC optimize(3)

#include<bits/stdc++.h>

#define MAXN 1000005


#define INF 1000000000
#define MOD 1000000007
#define F first
CAPITOLUL 10. EJOI 2017 10.3. SIX 462

#define S second

using namespace std;

typedef long long ll;


typedef pair<int,int> P;

void add(int &a,int b)


{
a+=b;
if(a>=MOD) a-=MOD;
}

ll mul_mod(ll a,ll i,ll n)


{
ll s=0;
while(i)
{
if(i&1) s=(s+a)%n;
a=(a+a)%n;
i>>=1;
}

return s;
}

ll pow_mod(ll a,ll i,ll n)


{
ll s=1;
while(i)
{
if(i&1) s=mul_mod(s,a,n);
a=mul_mod(a,a,n);
i>>=1;
}

return s;
}

bool test(ll n,ll a,ll d)


{
if(n==2) return true;
if(n==a) return true;
if((n&1)==0) return false;
while(!(d&1)) d=d>>1;
ll t=pow_mod(a,d,n);
while((d!=n-1)&&(t!=1)&&(t!=n-1))
{
t=mul_mod(t,t,n);
d=d<<1;
}

return(t==n-1||(d&1)==1);
}

bool isPrime(ll n)
{
if(n<2) return false;
ll a[]={2,3,5,7,11,13,17,19,23,29};

for(ll i=0;i<=9;++i)
if(!test(n,a[i],n-1)) return false;

return true;
}

int prime[MAXN],sz;
bool is_prime[MAXN];

int sieve(int n)
{
int p=0;
memset(is_prime,true,sizeof(is_prime));
is_prime[0]=is_prime[1]=false;
for(int i=2;i<=n;i++)
{
CAPITOLUL 10. EJOI 2017 10.3. SIX 463

if(is_prime[i]) prime[p++]=i;
for(int j=0;j<p;j++)
{
if(i*prime[j]>n) break;
is_prime[i*prime[j]]=false;
if(i%prime[j]==0) break;
}
}

return p;
}

ll n;
int tot=0;
vector<int> fact;

unordered_map<int,int> dp;

int bit[10][10],cc;
int cnt[1000];

int find_next(int mask,int fmask)


{
for(int i=0;i<sz;i++)
{
if(!(fmask&(1<<i))) continue;
for(int j=i;j<sz;j++)
{
if(!(fmask&(1<<j))) continue;
if(mask&(1<<bit[i][j])) return -1;
}
}

int nmask=mask;
for(int i=0;i<sz;i++)
{
if(!(fmask&(1<<i))) continue;
for(int j=i;j<sz;j++)
if(mask&(1<<j))
nmask|=(1<<bit[i][j]);
for(int j=0;j<i;j++)
if(mask&(1<<j))
nmask|=(1<<bit[j][i]);
}

nmask|=fmask;
return nmask;
}

//mask: number in base 3


//0: unchosen 1: chosen once 2:chosen twice

int dfs(int mask)


{
if(dp[mask]) return dp[mask];
int ans=1;
for(int fmask=1;fmask<(1<<sz);fmask++)
{
int to=find_next(mask,fmask);
if(to==-1) continue;
add(ans,1LL*cnt[fmask]*dfs(to)%MOD);
}

return dp[mask]=ans;
}

int main()
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

int p=sieve(100000);
scanf("%lld",&n);
for(int i=0;i<p;i++)
{
int cnt=0;
CAPITOLUL 10. EJOI 2017 10.3. SIX 464

while(n%prime[i]==0)
{
n=n/prime[i];
cnt++;
}

if(cnt) fact.push_back(cnt);
}

if(n>1)
{
if(isPrime(n)) fact.push_back(1);
else
{
int r=(int)sqrt(n);
bool f=false;
for(int i=r-10;i<=r+10;i++)
if(1LL*r*r==n)
{
f=true;
fact.push_back(2);
break;
}

if(!f)
{
fact.push_back(1);
fact.push_back(1);
}
}
}

sz=(int)fact.size();
for(int i=0;i<(1<<sz);i++)
{
cnt[i]=1;
for(int j=0;j<sz;j++)
if(i&(1<<j)) cnt[i]*=fact[j];
}

int cc=sz;
for(int i=0;i<sz;i++)
for(int j=i;j<sz;j++)
bit[i][j]=cc++;

printf("%d\n",(dfs(0)-1+MOD)%MOD);

return 0;
}

Listing 10.3.8: 2219732-six.cpp


// https://csacademy.com/submission/2219732/ execution time : 0.031 s
// https://csacademy.com/contest/ejoi-2017-day-1/task/six/statistics/
// https://csacademy.com/contest/ejoi-2017-day-1/

#include<bits/stdc++.h>
using namespace std;

int k, w[65] = {1};


typedef long long ll;
const int P = 1e9 + 7;

struct hash_pair
{
template<class T1, class T2>
size_t operator() (const pair<T1, T2> &p) const
{
return hash<T1>{}(p.first) ˆ hash<T2>{}(p.second);
}
};

unordered_map<pair<ll, ll>, int, hash_pair> mp;

int bt(ll msk1, ll msk2)


CAPITOLUL 10. EJOI 2017 10.4. CAMEL 465

{
if (mp.count({msk1, msk2}))
return mp[{msk1, msk2}];

for (int i = 1; i < 1 << k; i++)


{
int cnt = 0;
for (ll j = msk1, y = __builtin_ctzll(j & -j);
cnt < 2 && j;
j &= j - 1, y = __builtin_ctzll(j & -j))
if (i & y + 1)
cnt += (msk2 >> y & 1) + 1;

if (cnt < 2)
(mp[{msk1, msk2}] += (1LL +
bt(msk1 | 1LL << i - 1,
msk1 & 1ULL << i - 1 | msk2)) *
w[i] % P) %= P;
}

return mp[{msk1, msk2}];


}

int main()
{
std::freopen("../six_tests/six.25.in", "r", stdin); // 591780
//std::freopen("six.out", "w", stdout);

ll n;
scanf("%lld", &n);

for (int i = 2; 1LL * i * i <= n; i++)


if (!(n % i))
{
while (!(n % i))
w[1 << k]++, n /= i;

k++;
}

if (n > 1)
w[1 << k++]++;

for (int i = 1; i < 1 << k; i++)


w[i] = w[i ˆ i & -i] * w[i & -i];

printf("%d", bt(0, 0));


}

135
OBS: hash

10.3.3 *Rezolvare detaliată

10.4 Camel
Problema 4 - Camel 100 de puncte
Let’s describe a new ”chess” piece and call it ”camel-tone”. The piece
moves jumping: horizontally or vertically - over two chessboard squares, or
diagonally - over one square. The picture shows a part of the board with a
camel-tone, placed in the center and the positions (marked by x), where it
can go with one move. Of course, it cannot go outside the playing board,
which happens to be a big square, divided into N  N little squares. In this
task N is always divisible by 5.
The camel-tone starts at the square in the top-left corner of the board.
The game consists of making a sequence of moves on the board, visiting every square exactly
135
https://en.cppreference.com/w/cpp/utility/hash
. http://www.cplusplus.com/reference/functional/hash/
. https://www.geeksforgeeks.org/stdhash-class-in-c-stl/
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 466

2
once. Moreover, after N  1 moves the piece should be exactly one move away from its starting
position. This is a so-called ”camel-tonian cycle”!
Task
Write a program camel to find any possible way to play the game, or to report that the cycle
is impossible.
Input
A single line is read from the standard input, containing only one integer N .
Output
The program has to write to the standard output:

ˆ one line with the message NO, if you establish that the cycle is impossible or
ˆ N lines, each containing N space separated numbers, which are the different positive integers
2
between 1 and N inclusive. The first number in the first line is 1. The output represents the
playing board (N  N squares), where integers indicate the consecutive occupied positions.
See the example below.

Constraints

ˆ N is divisible by 5
ˆ 5&N & 1000
Grading

ˆ There is a test with N 5 that is worth 20% of the points for the task
ˆ The remaining 16 tests are worth 5% of the points each.

Example
Sample Input Sample Output
10 1 52 29 8 51 28 9 50 37 16
85 95 59 86 94 66 87 93 65 88
40 19 100 39 18 76 38 17 77 49
2 53 30 7 58 27 10 89 36 15
84 96 60 75 99 67 72 92 64 71
41 20 82 44 23 90 45 24 78 48
3 54 31 6 57 26 11 68 35 14
83 97 61 74 98 62 73 91 63 70
42 21 81 43 22 80 46 25 79 47
4 55 32 5 56 33 12 69 34 13
Explanation: The camel-tone starts at the top left position (row:1, column:1), numbered 1. The
second occupied position is (row:4, column:1), so it is numbered 2. The next position is (row:7,
column: 1), and it is numbered 3, and so on. The final (hundredth) occupied position is (row:3,
column:3), and it is at one move away from the starting position.
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 467

10.4.1 Indicaţii de rezolvare

The task concerns finding a Hamiltonian cycle in a graph, defined by some neighborhood on a
rectangular network. Generally, it is an NP-complete problem and its solution needs exhausting
algorithms. For small values of N this can be done here, too. For bigger values, some rule is to
be found to realize the tour.
The solution, if it exists, is even far not unique, which gives the chance for different rules to
be noticed. A usual way is to split the given graph into smaller subgraphs, each with existing
Hamiltonian path and after walking it, to provide a legal transition to the next subgraph. We use
this method here as well.
The idea is to be able to construct a Hamiltonian path for a square with side of length 5,
with given start and final positions. For to calculate quicker the first desired path using standard
backtracking, we can often diminish the final demands to only the desired row or column, setting
free the other dimension, as it, more often than not, does not matter. What counts is to get
correctly to the next subgraph - a 5  5 square. If we can solve subtasks of this kind, we can divide
the big board into smaller 5  5 squares, every one of which can be travelled without leaving its
boundaries, finishing the path in a place, which assures a correct move to the next 5  5 square.
If we denote the number of columns (and rows) of 5  5 squares with T , a Hamiltonian tour on it
is easy to be seen. In the tours shown below every square is in fact a 5  5 square:

It appears that we can use a small number of different Hamiltonian paths on a 5  5 square, less
than 20. This makes it possible to remember them and reuse the found ones. They are identified
by their start and end positions, as, once again, the final row or column usually do not matter.
Nothing remains but to take into consideration the necessity of leaving ”steps” in the top left
5  5 corner: one or two unvisited little squares, leading to the top left little square. This should
be the end of the path, transforming it into a cycle. One little square is enough for ”stepping”
when is even, and, if is odd, at least two little squares are needed. Of course, this is true for
”natural” walks like the one above. As one can guess, the number of suitable paths on the board
can be large.
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 468

10.4.2 Coduri sursă

Listing 10.4.1: 486658-camel.cpp


// https://csacademy.com/submission/486658/ execution time : 0.520 s
// https://csacademy.com/submission/486660/ execution time : 0.520 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/camel/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef long double ld;
typedef vector<vector<ll> > matrix;

matrix mul(matrix a, matrix b)


{
matrix c;
c.resize(a.size());

for (int i = 0; i < c.size(); i++)


c[i].resize(b[0].size(), 0);

for (int i = 0; i < c.size(); i++)


for (int j = 0; j < c[i].size(); j++)
for (int k = 0; k < b.size(); k++)
c[i][j] = (c[i][j] + a[i][k] * b[k][j]);

return c;
}

matrix Def;

matrix bpow(matrix a, ll st)


{
if (st == 0)
return Def;

if (st == 1)
return a;

matrix b = bpow(a, st >> 1);


b = mul(b, b);
if (st & 1)
b = mul(a, b);

return b;
}

ll AR = 19, BR = 13, CR = 23, XR = 228, YR = 322, MOD = 1e9 + 993;

ll myrand()
{
ll ZR = (XR * AR + YR * BR + CR) % MOD;
XR = YR;
YR = ZR;
return ZR;
}

ll sqr(ll x)
{
return x * x;
}

const ll llinf = 2e18;


const ld EPS = 1e-9;
const ld PI = atan2(0, -1);
const int maxn = 1e5 + 100,
maxw = 1e6 + 100,
inf = 1e9 + 100,
mod = 1e9 + 7,
sq = 100;
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 469

int q[1000][1000];

vector<int> p[10];
vector<int> DEFAULT;

int n;

void fill(int x, int y, int v, int id)


{
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
if (p[id][i * 5 + j] != 0)
q[x + i][j + y] = v + p[id][i * 5 + j];
}

void fill(int x, int y, int v, vector<int> &id)


{
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
if (id[i * 5 + j] != 0)
q[x + i][j + y] = v + id[i * 5 + j];
}

int main()
{
std::freopen("../camel_tests/camel.17.in", "r", stdin);
std::freopen("camel.out", "w", stdout);

ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
p[0] = {8, 21, 2, 7, 20, 16, 5, 10, 17, 4, 1, 13,
25, 22, 12, 9, 18, 3, 6, 19, 15, 23, 11, 14, 24};
p[1] = {7, 12, 1, 6, 11, 15, 4, 9, 14, 3, 21, 18,
25, 22, 19, 8, 13, 2, 5, 10, 16, 23, 20, 17, 24};
p[2] = {8, 16, 2, 9, 17, 13, 5, 19, 14, 4, 21, 10,
25, 22, 1, 7, 15, 3, 6, 18, 12, 23, 20, 11, 24};
p[3] = {14, 9, 20, 15, 10, 6, 17, 12, 7, 18, 21, 3,
25, 22, 2, 13, 8, 19, 16, 11, 5, 23, 1, 4, 24};
p[4] = {0, 21, 14, 11, 22, 16, 9, 24, 19, 8, 4, 12,
0, 5, 13, 25, 20, 15, 10, 23, 17, 6, 3, 18, 7};
DEFAULT = {1, 9, 18, 2, 10, 6, 15, 12, 7, 16, 21, 3,
25, 22, 19, 13, 8, 17, 14, 11, 5, 23, 20, 4, 24};

if (n == 5)
{
fill(0, 0, 0, DEFAULT);
}
else
if (n % 10 == 0)
{
q[0][0] = 1;
q[2][2] = 2;
for (int it = 0; it < n; it += 10)
{
if (it == 0)
fill(it, 5, 2, 0);
else
fill(it, 5, it * (n - 5) + 2, 1);
for (int i = 10; i < n; i += 5)
fill(it, i, it * (n - 5) + 2 + (i / 5 - 1) * 25, 0);
fill(it + 5, n - 5, it * (n - 5) + 2 + (n - 5) * 5, 1);
for (int i = 5; i < n - 5; i += 5)
fill(it + 5, i, it * (n-5) + 2 + (n-5) * 5 + 5 * (n-i-5), 2);
}

fill(n - 5, 0, 2 + (n - 5) * n, 2);
for (int i = n - 10; i > 0; i -= 5)
fill(i, 0, 2 + (n - 5) * n + (n - i - 5) * 5, 3);
fill(0, 0, n * n - 25, 4);
}
else
{
p[4] = {0, 8, 20, 23, 7, 13, 16, 5, 10, 15, 19, 22,
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 470

0, 18, 21, 25, 9, 14, 24, 6, 12, 17, 4, 11, 0};


p[5] = {4, 10, 16, 5, 11, 23, 1, 13, 8, 21, 17, 6,
25, 18, 15, 3, 9, 22, 2, 12, 24, 19, 14, 7, 20};
p[6] = {15, 10, 21, 16, 9, 6, 18, 13, 5, 19, 22, 3,
8, 23, 2, 14, 11, 20, 17, 12, 7, 24, 1, 4, 25};

q[0][0] = 1;
q[2][2] = 2;
q[4][4] = 3;

for (int i = 5; i < n - 10; i += 5)


fill(5, i, 3 + (i - 5) * 10, 5),
fill(0, i, 3 + (i - 5) * 10 + 25, 6);

fill(5, n - 10, 3 + (n - 15) * 10, 5);


fill(0, n - 10, 3 + (n - 15) * 10 + 25, 3);
fill(0, n - 5, 3 + (n - 10) * 10, 0);
fill(5, n - 5, 3 + (n - 10) * 10 + 25, 1);
fill(10, n - 5, 3 + (n - 5) * 10, 1);

for (int i = n - 10; i > 0; i -= 5)


fill(10, i, 3 + (n - 5) * 10 + 5 * (n - i - 5), 2);

for (int it = 15; it < n; it += 10)


{
fill(it, 5, it * (n - 5) + 3, 1);
for (int i = 10; i < n; i += 5)
fill(it, i, it * (n - 5) + 3 + (i / 5 - 1) * 25, 0);
fill(it + 5, n - 5, it * (n - 5) + 3 + (n - 5) * 5, 1);
for (int i = 5; i < n - 5; i += 5)
fill(it + 5, i, it * (n-5) + 3 + (n-5) * 5 + 5 * (n-i-5), 2);
}

fill(n - 5, 0, 3 + (n - 5) * n, 2);

for (int i = n - 10; i > 0; i -= 5)


fill(i, 0, 3 + (n - 5) * n + (n - i - 5) * 5, 3);

fill(0, 0, n * n - 25, 4);


}

for (int i = 0; i < n; i++)


{
for (int j = 0; j < n; j++)
cout << q[i][j] << ’ ’;
cout << ’\n’;
}
}

Listing 10.4.2: 486713-camel.cpp


// https://csacademy.com/submission/486713/ execution time : 1.047 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/camel/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<bits/stdc++.h>

using namespace std;

int a,s,d[1002][1002],f,g,h,j,k,l,i,n,m,d1,d2,s1,s2;
pair<int,int> p[8]={{-3,0},{-2,2},{0,3},{2,2},{3,0},{2,-2},{0,-3},{-2,-2}};

int init(int i1,int i2,int sv)


{
if(sv==25+(n==5) && i1==0 && i2==(n==5 ? 0 : 5)) {return 1;}
if(i1>4) return 0;
if(i2>4) return 0;
if(i1<0) return 0;
if(i2<0) return 0;
if(d[i1][i2]) return 0;
d[i1][i2]=sv;//cout<<i1<<" "<<i2<<" "<<sv<<endl;

for(int i=0;i<8;i++)
{
if(init(i1+p[i].first,i2+p[i].second,sv+1)) return 1;
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 471

d[i1][i2]=0;

return 0;
}

int dfs(int i1,int i2,int sv)


{
if(sv==k+25 && i1==d1 && i2==d2) {return 1;}
if(sv==k+25 && (i1!=d1 || i2!=d2)) {return 0;}
if(i1>s1+4) return 0;
if(i2>s2+4) return 0;//cout<<"*";
if(i1<s1) return 0;
if(i2<s2) return 0;
if(d[i1][i2]) return 0;
d[i1][i2]=sv;//cout<<i1<<" "<<i2<<" ("<<s1<<" "<<s2<<" , "<<d1<<" "<<d2<<")"<<sv<<"
"<<k+25<<endl;
for(int i=0;i<8;i++)
{
if(dfs(i1+p[i].first,i2+p[i].second,sv+1)) return 1;
}

d[i1][i2]=0;

return 0;
}

main()
{
std::freopen("../camel_tests/camel.17.in", "r", stdin);
std::freopen("camel.out", "w", stdout);

cin>>n;

if(n>5) d[2][2]=n*n;
init(0,0,1);
if(n>5)
{
k=25;
i=0;
for(a=5;a<n-5;a+=5)
{
d1=0,d2=a+5;
s1=i;
s2=a;

dfs(0,a,k);

k+=25;
}//cout<<a<<"%";

d1=5;
d2=n-1;
s1=i;
s2=a;

dfs(0,a,k);

k+=25;
l=-1;
int st2=5,st1=n-1;

for(a=n-1;a>=0;a-=5)
{
l*=-1;
swap(st1,st2);
if(a==9 && n%2==1)
{
s1=n-5;
s2=5;
d1=n-1;
d2=4;

dfs(i,a,k);
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 472

k+=25;
}

for(i=st1;i!=st2-4*l;i+=5*l)
{//cout<<i<<" "<<a<<" "<<st2<<endl;
if(a==9 && n%2==1)
{
s1=i-4;
s2=0;
//cout<<s1<<" "<<s2<<" "<<d1<<" "<<d2<<" "<<i<<" "<<a<<endl;
if(s1==5)
{
d1=2;d2=2;
dfs(i,a-5,k);
break;
}
d1=i-5;
d2=5;

dfs(i,a-5,k);

k+=25;

s1=i-9;
s2=5;
d1=i-5;
d2=4;

dfs(i-5,a-4,k);

k+=25;
continue;
}

d1=i+5*l;
d2=a;
s1=min(i,i+4*l);
s2=a-4;

if(i==st1)
dfs(i,a,k);
else
{
for(int i=s1;i<s1+5;i++)
{
for(int a=s2;a<s2+5;a++)
{//cout<<i<<" "<<a<<" "<<s1<<endl;
d[i][a]=d[i-5*l][a]+25;
}
}
}

k+=25;
}

if(a==9 && n%2==1)


{
s1=5;
s2=0;
d1=2;
d2=2;

dfs(i,a-5,k);

break;
//break;
}

d1=i+4*l;
d2=a-5;
s1=min(i,i+4*l);
s2=a-4;
if(s1==5 && s2==0) d1=2,d2=2;

dfs(i,a,k);
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 473

k+=25;
}
}/////

for(i=0;i<n;i++)
{
for(a=0;a<n;a++)
{
printf("%d ",d[i][a]);
}
cout<<endl;
}
}

Listing 10.4.3: 502953-camel.cpp


// https://csacademy.com/submission/502953/ execution time : 0.285 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/camel/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cstdio>
#include <numeric>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <list>
#include <cmath>
#include <bitset>
#include <cassert>
#include <queue>
#include <stack>
#include <deque>

using namespace std;

template<typename T1, typename T2>


inline void chkmin(T1 &x, T2 y)
{
if (x > y) x = y;
}

template<typename T1, typename T2>


inline void chkmax(T1 &x, T2 y)
{
if (x < y) x = y;
}

/** Interface */

inline int readChar();

template <class T = int>


inline T readInt();

template <class T>


inline void writeInt( T x, char end = 0 );

inline void writeChar( int x );


inline void writeWord( const char *s );

/** Read */

static const int buf_size = 4096;

inline int getChar()


{
static char buf[buf_size];
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 474

static int len = 0, pos = 0;


if (pos == len)
{
pos = 0,
len = fread(buf, 1, buf_size, stdin);
}

if (pos == len)
{
return -1;
}

return buf[pos++];
}

inline int readChar()


{
int c = getChar();
while (c <= 32)
{
c = getChar();
}

return c;
}

template <class T>


inline T readInt()
{
int s = 1, c = readChar();
T x = 0;
if (c == ’-’)
s = -1, c = getChar();

while (’0’ <= c && c <= ’9’)


x = x * 10 + c - ’0’, c = getChar();

return s == 1 ? x : -x;
}

/** Write */

static int write_pos = 0;


static char write_buf[buf_size];

inline void writeChar( int x )


{
if (write_pos == buf_size)
fwrite(write_buf, 1, buf_size, stdout),
write_pos = 0;

write_buf[write_pos++] = x;
}

template <class T>


inline void writeInt( T x, char end )
{
if (x < 0)
writeChar(’-’), x = -x;

char s[24];
int n = 0;
while (x || !n)
s[n++] = ’0’ + x % 10, x /= 10;

while (n--)
writeChar(s[n]);

if (end)
writeChar(end);
}

inline void writeWord( const char *s )


{
while (*s)
writeChar(*s++);
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 475

struct Flusher
{
˜Flusher()
{
if (write_pos)
fwrite(write_buf, 1, write_pos, stdout), write_pos = 0;
}
} flusher;

#define sz(c) (int)(c).size()


#define all(c) (c).begin(), (c).end()
#define rall(c) (c).rbegin(), (c).rend()
#define left left228
#define right right228
#define next next228
#define rank rank228
#define prev prev228
#define y1 y1228

const int MAXN = 1001;

int ans[MAXN][MAXN];
vector<int> p[10];
vector<int> base;
int n;

inline void paint(int x, int y, int sd, int id)


{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (p[id][i * 5 + j] != 0)
{
ans[x + i][j + y] = sd + p[id][i * 5 + j];
}
}
}
}

inline void paint(int x, int y, int sd, vector<int> &id)


{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (id[i * 5 + j] != 0)
{
ans[x + i][j + y] = sd + id[i * 5 + j];
}
}
}
}

int main()
{
std::freopen("../camel_tests/camel.17.in", "r", stdin);
std::freopen("camel.out", "w", stdout);

n = readInt();
p[0] = {8, 21, 2, 7, 20, 16, 5, 10, 17, 4, 1, 13, 25,
22, 12, 9, 18, 3, 6, 19, 15, 23, 11, 14, 24};
p[1] = {7, 12, 1, 6, 11, 15, 4, 9, 14, 3, 21, 18, 25,
22, 19, 8, 13, 2, 5, 10, 16, 23, 20, 17, 24};
p[2] = {8, 16, 2, 9, 17, 13, 5, 19, 14, 4, 21, 10, 25,
22, 1, 7, 15, 3, 6, 18, 12, 23, 20, 11, 24};
p[3] = {14, 9, 20, 15, 10, 6, 17, 12, 7, 18, 21, 3, 25,
22, 2, 13, 8, 19, 16, 11, 5, 23, 1, 4, 24};
p[4] = {0, 21, 14, 11, 22, 16, 9, 24, 19, 8, 4, 12,
0, 5, 13, 25, 20, 15, 10, 23, 17, 6, 3, 18, 7};
base = {1, 9, 18, 2, 10, 6, 15, 12, 7, 16, 21, 3, 25,
22, 19, 13, 8, 17, 14, 11, 5, 23, 20, 4, 24};
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 476

if (n == 5)
{
paint(0, 0, 0, base);
}
else
{
if (n % 10 == 0)
{
ans[0][0] = 1;
ans[2][2] = 2;
for (int it = 0; it < n; it += 10)
{
if (it == 0)
{
paint(it, 5, 2, 0);
}
else
{
paint(it, 5, it * (n - 5) + 2, 1);
}

for (int i = 10; i < n; i += 5)


{
paint(it, i, it * (n - 5) + 2 + (i / 5 - 1) * 25, 0);
}

paint(it + 5, n - 5, it * (n - 5) + 2 + (n - 5) * 5, 1);

for (int i = 5; i < n - 5; i += 5)


{
paint(it+5, i, it*(n-5)+2+(n-5)*5+5*(n-i-5), 2);
}
}

paint(n - 5, 0, 2 + (n - 5) * n, 2);

for (int i = n - 10; i > 0; i -= 5)


{
paint(i, 0, 2 + (n - 5) * n + (n - i - 5) * 5, 3);
}

paint(0, 0, n * n - 25, 4);


}
else
{
p[4] = {0, 8, 20, 23, 7, 13, 16, 5, 10, 15, 19, 22, 0,
18, 21, 25, 9, 14, 24, 6, 12, 17, 4, 11, 0};
p[5] = {4, 10, 16, 5, 11, 23, 1, 13, 8, 21, 17, 6, 25,
18, 15, 3, 9, 22, 2, 12, 24, 19, 14, 7, 20};
p[6] = {15, 10, 21, 16, 9, 6, 18, 13, 5, 19, 22, 3, 8,
23, 2, 14, 11, 20, 17, 12, 7, 24, 1, 4, 25};

ans[0][0] = 1;
ans[2][2] = 2;
ans[4][4] = 3;

for (int i = 5; i < n - 10; i += 5)


{
paint(5, i, 3 + (i - 5) * 10, 5),
paint(0, i, 3 + (i - 5) * 10 + 25, 6);
}

paint(5, n - 10, 3 + (n - 15) * 10, 5);


paint(0, n - 10, 3 + (n - 15) * 10 + 25, 3);
paint(0, n - 5, 3 + (n - 10) * 10, 0);
paint(5, n - 5, 3 + (n - 10) * 10 + 25, 1);
paint(10, n - 5, 3 + (n - 5) * 10, 1);

for (int i = n - 10; i > 0; i -= 5)


{
paint(10, i, 3 + (n - 5) * 10 + 5 * (n - i - 5), 2);
}

for (int it = 15; it < n; it += 10)


{
paint(it, 5, it * (n - 5) + 3, 1);
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 477

for (int i = 10; i < n; i += 5)


{
paint(it, i, it * (n - 5) + 3 + (i / 5 - 1) * 25, 0);
}

paint(it + 5, n - 5, it * (n - 5) + 3 + (n - 5) * 5, 1);

for (int i = 5; i < n - 5; i += 5)


{
paint(it+5, i, it*(n-5)+3+(n-5)*5 + 5 * (n-i-5), 2);
}
}

paint(n - 5, 0, 3 + (n - 5) * n, 2);

for (int i = n - 10; i > 0; i -= 5)


{
paint(i, 0, 3 + (n - 5) * n + (n - i - 5) * 5, 3);
}

paint(0, 0, n * n - 25, 4);


}
}

for (int i = 0; i < n; i++)


{
for (int j = 0; j < n; j++)
{
writeInt(ans[i][j], ’ ’);
}

writeChar(’\n’);
}

return 0;
}

Listing 10.4.4: 2220355-camel.cpp


// https://csacademy.com/submission/2220355/ execution time : 0.866 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/camel/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

using namespace std;

template <typename T> void read(T &t)


{
char ch=getchar();
int f=1;
t=0;
while (’0’>ch||ch>’9’)
{
if (ch==’-’) f=-1;
ch=getchar();
}

do
{
136
(t*=10)+=ch-’0’;
ch=getchar();
} while (’0’<=ch&&ch<=’9’);

t*=f;
}

int d[5][5]={
{1,11,18,4,10},
{16,22,8,13,23},
{19,5,25,20,6},
{2,12,17,3,9},
{15,21,7,14,24}
136
t=t*10+ch-’0’;
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 478

};

pair<int,int> to[1010][1010];
int n,ans[1010][1010];

int main()
{
std::freopen("../camel_tests/camel.17.in", "r", stdin);
std::freopen("camel.out", "w", stdout);

for (int i=0;i<5;i++)


for (int j=0;j<5;j++)
for (int k=0;k<5;k++)
for (int l=0;l<5;l++)
if (d[i][j]-1==d[k][l] || (d[i][j]==1 && d[k][l]==25))
to[k][l]=make_pair(i,j);

read(n);

for (int i=0;i<n;i++)


for (int j=0;j<n;j++)
{
to[i][j]=to[i%5][j%5];
to[i][j].first+=i-i%5;
to[i][j].second+=j-j%5;
}

for (int i=0;i<n-5;i+=5)


to[i+4][3]=make_pair(i+7,3),
to[i+7][0]=make_pair(i+4,0);

for (int i=0;i<n;i+=5)


for (int j=0;j<n-5;j+=5)
to[i+1][j+3]=make_pair(i+1,j+6),
to[i+4][j+6]=make_pair(i+4,j+3);

int x=1,y=1,cnt=0;
while (1)
{
ans[x][y]=++cnt;
pair<int,int> tmp=to[x][y];

x=tmp.first,
y=tmp.second;

if (x==1 && y==1) break;


}

for (int i=0;i<n;i++)


{
for (int j=0;j<n;j++)
printf("%d ",ans[i][j]);
printf("\n");
}

return 0;
}

Listing 10.4.5: 2659930-camel.cpp


// https://csacademy.com/submission/2659930/ execution time : 0.314 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/camel/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cstdio>
#include <numeric>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <set>
#include <map>
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 479

#include <unordered_map>
#include <unordered_set>
#include <list>
#include <cmath>
#include <bitset>
#include <cassert>
#include <queue>
#include <stack>
#include <deque>

using namespace std;


template<typename T1, typename T2>
inline void chkmin(T1 &x, T2 y) { if (x > y) x = y; }

template<typename T1, typename T2>


inline void chkmax(T1 &x, T2 y) { if (x < y) x = y; }

/** Interface */

inline int readChar();


template <class T = int> inline T readInt();
template <class T> inline void writeInt( T x, char end = 0 );
inline void writeChar( int x );
inline void writeWord( const char *s );

/** Read */

static const int buf_size = 4096;

inline int getChar()


{
static char buf[buf_size];
static int len = 0, pos = 0;
if (pos == len)
{
pos = 0, len = fread(buf, 1, buf_size, stdin);
}
if (pos == len)
{
return -1;
}
return buf[pos++];
}

inline int readChar()


{
int c = getChar();
while (c <= 32)
{
c = getChar();
}
return c;
}

template <class T>


inline T readInt()
{
int s = 1, c = readChar();
T x = 0;
if (c == ’-’)
s = -1, c = getChar();
while (’0’ <= c && c <= ’9’)
x = x * 10 + c - ’0’, c = getChar();
return s == 1 ? x : -x;
}

/** Write */

static int write_pos = 0;


static char write_buf[buf_size];

inline void writeChar( int x )


{
if (write_pos == buf_size)
fwrite(write_buf, 1, buf_size, stdout), write_pos = 0;
write_buf[write_pos++] = x;
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 480

template <class T>


inline void writeInt( T x, char end )
{
if (x < 0)
writeChar(’-’), x = -x;

char s[24];
int n = 0;
while (x || !n)
s[n++] = ’0’ + x % 10, x /= 10;
while (n--)
writeChar(s[n]);
if (end)
writeChar(end);
}

inline void writeWord( const char *s )


{
while (*s) writeChar(*s++);
}

struct Flusher
{
˜Flusher()
{
if (write_pos)
fwrite(write_buf, 1, write_pos, stdout), write_pos = 0;
}
} flusher;

#define sz(c) (int)(c).size()


#define all(c) (c).begin(), (c).end()
#define rall(c) (c).rbegin(), (c).rend()
#define left left228
#define right right228
#define next next228
#define rank rank228
#define prev prev228
#define y1 y1228

const int MAXN = 1001;

int ans[MAXN][MAXN];
vector<int> p[10];
vector<int> base;
int n;

inline void paint(int x, int y, int sd, int id)


{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (p[id][i * 5 + j] != 0)
{
ans[x + i][j + y] = sd + p[id][i * 5 + j];
}
}
}
}

inline void paint(int x, int y, int sd, vector<int> &id)


{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (id[i * 5 + j] != 0)
{
ans[x + i][j + y] = sd + id[i * 5 + j];
}
}
}
}
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 481

int main()
{
std::freopen("../camel_tests/camel.17.in", "r", stdin);
std::freopen("camel.out", "w", stdout);

n = readInt();
p[0] = {8, 21, 2, 7, 20, 16, 5, 10, 17, 4, 1, 13, 25,
22, 12, 9, 18, 3, 6, 19, 15, 23, 11, 14, 24};
p[1] = {7, 12, 1, 6, 11, 15, 4, 9, 14, 3, 21, 18, 25,
22, 19, 8, 13, 2, 5, 10, 16, 23, 20, 17, 24};
p[2] = {8, 16, 2, 9, 17, 13, 5, 19, 14, 4, 21, 10, 25,
22, 1, 7, 15, 3, 6, 18, 12, 23, 20, 11, 24};
p[3] = {14, 9, 20, 15, 10, 6, 17, 12, 7, 18, 21, 3, 25,
22, 2, 13, 8, 19, 16, 11, 5, 23, 1, 4, 24};
p[4] = {0, 21, 14, 11, 22, 16, 9, 24, 19, 8, 4, 12, 0,
5, 13, 25, 20, 15, 10, 23, 17, 6, 3, 18, 7};
base = {1, 9, 18, 2, 10, 6, 15, 12, 7, 16, 21, 3, 25,
22, 19, 13, 8, 17, 14, 11, 5, 23, 20, 4, 24};

if (n == 5)
{
paint(0, 0, 0, base);
}
else
{
if (n % 10 == 0)
{
ans[0][0] = 1;
ans[2][2] = 2;
for (int it = 0; it < n; it += 10)
{
if (it == 0)
{
paint(it, 5, 2, 0);
}
else
{
paint(it, 5, it * (n - 5) + 2, 1);
}

for (int i = 10; i < n; i += 5)


{
paint(it, i, it * (n - 5) + 2 + (i / 5 - 1) * 25, 0);
}

paint(it + 5, n - 5, it * (n - 5) + 2 + (n - 5) * 5, 1);
for (int i = 5; i < n - 5; i += 5)
{
paint(it + 5, i, it*(n-5)+2+(n-5)*5+5*(n-i-5), 2);
}
}

paint(n - 5, 0, 2 + (n - 5) * n, 2);
for (int i = n - 10; i > 0; i -= 5)
{
paint(i, 0, 2 + (n - 5) * n + (n - i - 5) * 5, 3);
}

paint(0, 0, n * n - 25, 4);


}
else
{
p[4] = {0, 8, 20, 23, 7, 13, 16, 5, 10, 15, 19, 22,
0, 18, 21, 25, 9, 14, 24, 6, 12, 17, 4, 11, 0};
p[5] = {4, 10, 16, 5, 11, 23, 1, 13, 8, 21, 17, 6,
25, 18, 15, 3, 9, 22, 2, 12, 24, 19, 14, 7, 20};
p[6] = {15, 10, 21, 16, 9, 6, 18, 13, 5, 19, 22, 3,
8, 23, 2, 14, 11, 20, 17, 12, 7, 24, 1, 4, 25};

ans[0][0] = 1;
ans[2][2] = 2;
ans[4][4] = 3;

for (int i = 5; i < n - 10; i += 5)


{
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 482

paint(5, i, 3 + (i - 5) * 10, 5),


paint(0, i, 3 + (i - 5) * 10 + 25, 6);
}

paint(5, n - 10, 3 + (n - 15) * 10, 5);


paint(0, n - 10, 3 + (n - 15) * 10 + 25, 3);
paint(0, n - 5, 3 + (n - 10) * 10, 0);
paint(5, n - 5, 3 + (n - 10) * 10 + 25, 1);
paint(10, n - 5, 3 + (n - 5) * 10, 1);

for (int i = n - 10; i > 0; i -= 5)


{
paint(10, i, 3 + (n - 5) * 10 + 5 * (n - i - 5), 2);
}

for (int it = 15; it < n; it += 10)


{
paint(it, 5, it * (n - 5) + 3, 1);
for (int i = 10; i < n; i += 5)
{
paint(it, i, it * (n - 5) + 3 + (i / 5 - 1) * 25, 0);
}

paint(it + 5, n - 5, it * (n - 5) + 3 + (n - 5) * 5, 1);
for (int i = 5; i < n - 5; i += 5)
{
paint(it+5, i, it*(n-5)+3+(n-5)*5+5*(n-i-5), 2);
}
}

paint(n - 5, 0, 3 + (n - 5) * n, 2);
for (int i = n - 10; i > 0; i -= 5)
{
paint(i, 0, 3 + (n - 5) * n + (n - i - 5) * 5, 3);
}
paint(0, 0, n * n - 25, 4);
}
}

for (int i = 0; i < n; i++)


{
for (int j = 0; j < n; j++)
{
writeInt(ans[i][j], ’ ’);
}

writeChar(’\n’);
}

return 0;
}

Listing 10.4.6: checkerCamel.cpp


#include <cstdio>
#include <cstring>

#include<iostream>

#define MAXN 1000

using namespace std;

typedef struct
{
int row,col;
bool vis;
} Place;

int N;
Place p[MAXN*MAXN+1];
Place offs[8]={
{0,3,0},
{0,-3,0},
{3,0,0},
CAPITOLUL 10. EJOI 2017 10.4. CAMEL 483

{-3,0,0},
{2,2,0},
{2,-2,0},
{-2,2,0},
{-2,-2,0}
};

void showPlace(const Place *p)


{
printf("(%d, %d)",p->row,p->col);
}

void showPlaces()
{
for (int i=1;i<=N*N;i++)
{
printf("%d:",i);
showPlace(&p[i]);
printf(" ");
}

printf("\n");
}

bool isCorr(int i,int j)


{
int diffR=p[i].row-p[j].row,
diffC=p[i].col-p[j].col;

for (int i=0;i<8;i++)


if (offs[i].row==diffR && offs[i].col==diffC)
return true;

return false;
}

int check()
{
for (int i=2;i<=N*N;i++)
{
if (p[i].vis) return 1;
if (!isCorr(i-1,i)) return 2;
p[i].vis=true;
}

if (!isCorr(N*N,1)) return 3;

return 0;
}

int main()
{
char b[512];
FILE *f;
f=fopen("../camel_tests/camel.17.in","r");//s[1] - in-file

if (!f)
{
fprintf(stderr,"In-file not found\n");
return 0;
}

fscanf(f,"%d",&N);
fclose(f);

f=fopen("camel.out","r"); //s[3] - result file

if (!f)
{
printf("0\nResult not found\n");
return 0;
}

if (N<5)
{
fscanf(f,"%s",b);
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 484

if (strcmp(b,"NO"))
printf("0\nIncorrect output\n");
else
printf("1\nCorrect\n");
fclose(f);

return 0;
}

for (int r=0;r<N;r++)


for (int c=0;c<N;c++)
{
if (feof(f))
{
printf("0\nIncorrect output format\n");
fclose(f);
return 0;
}

int k;
fscanf(f,"%d",&k);

if (k<1 || k>N*N)
{
printf("0\nIncorrect output value\n");
fclose(f);
return 0;
}

p[k].row=r;
p[k].col=c;
p[k].vis=false;
}

fclose(f);

switch (check())
{
case 0:{printf("1\nCorrect\n");break;}
case 1:{printf("0\nPlace visited twice\n");break;}
case 2:{printf("0\nInvalid move\n");break;}
case 3:printf("0\nIncorrect finish place\n");
}

return 0;
}

10.4.3 *Rezolvare detaliată

10.5 Experience
Problema 5 - Experience 100 de puncte
Company X has N employees. The company has a strict hierarchical tree-like structure - the
CEO (Chief Executive Officer) stands at the top (root of the tree), he has some number of direct
subordinates, who also have direct subordinates and so on, until we reach regular employees, who
have no subordinates (leaves of the tree).
The employees are numbered with integers from 1 to N . The CEO has number 1, but the
other numbers have nothing to do with the hierarchy. Each employee has some experience - the
i-th employee has experience, denoted by non-negative integer Wi .
The company has a large number of group projects to complete and the management has
decided to split all of the employees into different groups (teams), so that the following conditions
are satisfied:

ˆ Each team must consist of at least one person and each person must belong to exactly one
team.
ˆ Each team must consist only of people, who are consecutive subordinates of one another. A
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 485

group of employees j1 , j2 , j3 , j4 , ... is a valid team if j2 is directly subordinate of j1 , j3 is a


directly subordinate of j2 , j4 is directly subordinate of j3 and so on.
The management knows that after a group project is finished, the total experience of the group,
assigned to the project, increases by Wmax  Wmin , where Wmax is the maximal experience, and
Wmin is the minimal experience among the group members. The total experience increase for the
company is equal to the sum of the experience increases of all teams. The management wants to
maximize the total experience increase for the company, by splitting the employees into the best
possible teams’ configuration, following the two conditions mentioned above.
Task
Write a program experience to calculate the maximum possible experience increase for the
company.
Input
The first line of the standard input contains a single integer N - number of employees in the
company.
The second line contains N space separated non-negative integers W1 , W2 , ..., WN - the expe-
rience of each employee of the company.
Then N  1 lines follow, each containing space separated integers u and v in the mentioned
order. These numbers represent the subordinate relations in the company - the employee with
number v is a direct subordinate of the employee with number u.
Output
The program should print to the standard output one integer - the maximum total experience
increase for the company.
Constraints

ˆ 1&N & 100 000


0 & Wi & 10
9
ˆ

ˆ N & 20 in the tests that are worth 20% of the points for the task
ˆ N & 5000 in the tests that are worth 50% of the points for the task
ˆ Each employee has at most one direct subordinate in the tests that are worth 10% of the
points for the task

Example
Sample Input Sample Output
7 6
5536233
16
53
15
62
24
67

Explanation:
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 486

One possible configuration that maximizes the total experience increase is {1, 5, 3}, {6, 2, 4},
{7}. There is another configuration with the same maximal total experience increase - {1, 5}, {3},
{6, 2, 4}, {7}.

10.5.1 Indicaţii de rezolvare

1 Introduction
The problem statement in short is:
We have a it tree in which every vertex has a weight. We want to decompose the tree into
vertical chains, such that every vertex belongs to exactly one chain and the sum of ”values” of the
chains is maximum. The value of a chain is defined as Wmax  Wmin , where Wmax is the maximal
weight of a vertex in the chain and Wmin is the minimal weight.
We want our solution to be O N log N  or O N  in time, where N is the number of vertices
in the tree.
2 Observations
To solve the problem we first need to make some observations. The most important one is
that at least one of the following is true for every chain:

ˆ The chain should start from the vertex with the largest weight and finish on the vertex with
the smallest weight from this path.
ˆ The chain should start from the vertex with the smallest weight and finish on the vertex
with the largest weight from this path.
This is true, because if we have a path not satisfying one of the above conditions we can divide
this path into several smaller ones without decreasing the initial value.
The second observation is that there exists an optimal decomposition into chains such that the
weights of the vertices in each chain are either increasing or decreasing. This observation is again
true because if there was a chain not satisfying this, we could have divided it into several smaller
ones without decreasing its initial values.
3 Main part
Using the above observations we can solve the problem in linear time.
The authors solution uses only the first observation. Also there exists a solution which is easier
to be implemented but it uses both observations. Both solutions are linear in time.
To get partial points one could have implemented a brute force for 10 points. To get 40 points
one could implement a solution quadratic in time.
3.1 Solution for 40 points
We will solve the problem with dynamic programming.
Our state will be dpu which is the maximal sum of values of chains in a decomposition of
the subtree rooted at vertex u. We know that there is a chain starting at vertex u and finishing
in a vertex in its subtree. Lets denote this vertex as v and the set of vertices which belong to
the path from u to v as S. Also lets denote by Ci the sum of the dp values of the children of u
(Ci < x"children i dpx ). From here we get that for the chain starting at u and finishing at v, the
< <
value of dpu will be x"S Cx  x"Surux dpx  maxx"S Wx  minx"S Wx . And so we can find dpu
by checking this value for all vertices v in the subtree of u and then by getting the maximal value.
2
This solution is O N . If implemented well it can get even 50 points.
3.2 Author’s Solution
We will use dynamic programming to solve the problem. With each vertex we will associate 3
values:

ˆ dpu,0 - maximum sum of values to cover the subtree of u.


ˆ dpu,1 - maximum sum of values to cover the subtree of u, if we have an unfinished path,
which includes the root and a fixed maximum (the value of the maximum has been added
in advance).
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 487

ˆ dpu,2 - maximum sum of values to cover the subtree of u, if we have an unfinished path, which
includes the root and a fixed minimum (the value of the minimum has been substracted in
advance).

The answer to the problem will be dp1,0 .


We will use DFS, starting from the root (vertex number 1). After going through all of the
children of a given vertex, we will calculate the three values for the vertex. Let us firstly calculate
dpu,1 . The evaluation of dpu,2 will use an analogical approach as the one for dpu,1 .
When we calculate dpu,1 , we have two possibilities:
ˆ A new path starts from vertex u.
ˆ A path, which until now finished in one of the children of u, continues through u.
<
In the first case, the value will be Wu  v"children u dpv,0 .
In the second case, we need to choose a child of u, the path from which we want to continue.
Let S < v "children u dpv,0 . If the chosen child of u is p, the value of dpu,1 will be S  dpp,0 
dpp,1 .
This way we can find the maximum value for dpu,1 and dpu,2 .
Now in order to calculate dpu,0 , we once again have two possibilities:

ˆ To finish a path, where u is the vertex with maximum weight.


ˆ To finish a path, where u is the vertex with minimum weight.
So we get dpu,0 max dpu,1  Wu , dpu,2  Wu .
This solution has a complexity of O N  if it’s implemented by the described above way.
3.3 Second Solution (Encho Mishinev)
We will once again use dynamic programming. However, we will use the second observation
- that an optimal decomposition into paths exists, such that the values in each path are either
only increasing or only decreasing. If we decompose the tree in such fashion, we can say, that the
weight of a path is the sum of the absolute differences between each two consecutive vertices. For
increasing or decreasing paths this definition is equivalent to Wmax  Wmin . With each vertex we
will associate 2 values:
ˆ Fv,0 - maximum sum of values for the covering of the subtree of u, if the path starting from
u is with decreasing weights.
ˆ Fv,1 - maximum sum of values for the covering of the subtree of u, if the path starting from
u is with increasing weights.
The answer to the problem will then be max F1,0 , F1,1 .
The calculation of Fv,0 and Fv,1 is analogical, so we will focus only on the calculation of Fv,0 :
Let S < x"children v  max Fx,0 , Fx,1 .
Because a decreasing path starts from v, we have top chose to which child it continues. If this
child is u (Wu & Wv ), the value will be S  max Fu,0 , Fu,1   Wv  Wu   Fu,0 .
We have to remember the case, where v is a path, consisting only of one vertex and thus the
value is simply S.
This way with O 1 we can evaluate the value for a fixed child, which allows us to calculate
all values in the tree with a complexity of O N .
4 Additional information
You can try to solve a more difficult version of the problem. In it once again you need to
maximize the sum of the paths, where the value of a path is once again the difference between the
largest and smallest weight in the path, however we have one more condition - each path has to
have length & K, where K is a given integer.
This version of the problem can be solved with a complexity of O N log N , using a segment
tree, or with a complexity of O N log2 N , using the technique, described here: http://codefo
rces.com/blog/entry/44351.
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 488

10.5.2 Coduri sursă

Listing 10.5.1: 486102-experience.cpp


// https://csacademy.com/submission/486102/ execution time : 0.328 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

#define endl ’\n’

using namespace std;

const int MAXN = (int)1e5 + 42 + 17;

int n, W[MAXN];
vector<int> adj[MAXN];

void read()
{
cin >> n;
for(int i = 1; i <= n; i++)
cin >> W[i];

for(int i = 0; i < n - 1; i++)


{
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
}

int64_t dp[MAXN][3];

void dfs(int u, int pr = -1)


{
uint64_t S = 0;
for(int v: adj[u])
if(v != pr)
{
dfs(v, u);
S += dp[v][0];
}

dp[u][1] = S + W[u];
dp[u][2] = S - W[u];
for(int v: adj[u])
if(v != pr)
{
dp[u][1] = max(dp[u][1], (int64_t)(S + dp[v][1] - dp[v][0]));
dp[u][2] = max(dp[u][2], (int64_t)(S + dp[v][2] - dp[v][0]));
}

dp[u][0] = dp[u][1] - W[u];


dp[u][0] = max(dp[u][0], dp[u][2] + W[u]);
}

void solve()
{
dfs(1);
cout << dp[1][0] << endl;
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(NULL);

read();
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 489

solve();
return 0;
}

Listing 10.5.2: 486224-experience.cpp


// https://csacademy.com/submission/486224/ execution time : 0.250 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

using namespace std;

using ll = long long;


using ull = unsigned long long;
using ld = long double;

#define forn(i, a, n) for (int i = (int)(a); i < (int)(n); ++i)


#define ford(i, a, n) for (int i = (int)(n - 1); i >= (int)(a); --i)
#define fore(i, a, n) for (int i = (int)(a); i <= (int)(n); ++i)
#define all(a) (a).begin(), (a).end()
#define fs first
#define sn second
#define trace(a)\
for (auto i : a) cerr << i << ’ ’;\
cerr << ’\n’
#define eb emplace_back

//#ifndef M_PI
//const ld M_PI = acos(-1.0);
//#endif

template<typename T>
inline void setmax(T& a, T b)
{
if (a < b) a = b;
}

template<typename T>
inline void setmin(T& a, T b)
{
if (a > b) a = b;
}

const ld eps = 1e-9;


const int INF = 2000000000;
const ll LINF = 1ll * INF * INF;
const ll MOD = 1000000007;

const int N = 1e5;


int p[N];

vector<int> ch[N];

ll dp1[N], dp2[N];
int w[N];

void dfs(int u)
{
for (int v : ch[u])
dfs(v);

ll sum = 0;
for (int v : ch[u])
sum += max(dp1[v], dp2[v]);

dp1[u] = sum;
dp2[u] = sum;
for (int v : ch[u])
{
ll* dp = w[u] > w[v] ? dp1 : dp2;
setmax(dp[u], sum - max(dp1[v], dp2[v]) + abs(w[u] - w[v]) + dp[v]);
}
}
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 490

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

ios::sync_with_stdio(false);
cin.tie(0);

//srand((unsigned) chrono::high_resolution_clock::\
now().time_since_epoch().count());

int n;
cin >> n;

forn(i, 0, n) cin >> w[i];

forn(i, 1, n)
{
int u, v;
cin >> u >> v;

--u, --v;
p[v] = u;
ch[u].eb(v); // emplace_back
}

dfs(0);

cout << max(dp1[0], dp2[0]) << ’\n’;


}

Listing 10.5.3: 486736-experience.cpp


// https://csacademy.com/submission/486736/ execution time : 0.781 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> ii;


typedef long long ll;

#define X first
#define Y second

const int MOD=1e9+7;

const int N=2e5+10;


const ll inf=1e16;

int n,w[N];
vector <int> v[N];

int q[N],pa[N];
ll f[N][3];

void BFS()
{
int top=1,bot=1;
q[1]=1;
while (top<=bot)
{
int x=q[top++];
for(auto y:v[x])
if (y!=pa[x])
{
pa[y]=x;
q[++bot]=y;
}
}
}
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 491

ll solve()
{
BFS();
for(int i=n;i>=1;i--)
{
int x=q[i];
f[x][0]=f[x][1]=-inf;
f[x][2]=0;

for(auto y:v[x])
if (y!=pa[x])
f[x][2]+=*max_element(f[y],f[y]+3);

for(auto y:v[x])
if (y!=pa[x])
{
ll sub=*max_element(f[y],f[y]+3);
if (w[y]>=w[x])
f[x][0]=max(f[x][0],
f[x][2]-sub+max(f[y][0], f[y][2])+w[y]-w[x]);
else
f[x][1]=max(f[x][1],
f[x][2]-sub+max(f[y][1], f[y][2])+w[x]-w[y]);
}
// cout<<x<<" "<<f[x][0]<<" "<<f[x][1]<<" "<<f[x][2]<<’\n’;
}

return *max_element(f[1],f[1]+3);
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",w+i);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}

cout<<solve();
}

137
OBS: max element returnează iterator, *max element returnează valoare!

Listing 10.5.4: 487126-experience.cpp


// https://csacademy.com/submission/487126/ execution time : 0.250 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <numeric>
#include <iterator>
#include <cstring>

using namespace std;

int n;
int a[100005];
vector<int> e[100005];
long long dp[100005][2];

void solve(int x)
137
https://en.cppreference.com/w/cpp/algorithm/max_element
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 492

{
long long sum = 0;

for (int y : e[x])


{
solve(y);
sum += max(dp[y][0], dp[y][1]);
}

dp[x][0] = sum;
dp[x][1] = sum;

for (int y : e[x])


{
if (a[x] <= a[y])
{
dp[x][0] = max(dp[x][0],
sum - max(dp[y][0], dp[y][1]) + a[y] - a[x] + dp[y][0]);
}

if (a[x] >= a[y])


{
dp[x][1] = max(dp[x][1],
sum - max(dp[y][0], dp[y][1]) + a[x] - a[y] + dp[y][1]);
}
}
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cerr.tie(nullptr);

cin >> n;
for (int i=1; i<=n; i++)
{
cin >> a[i];
}

for (int i=1; i<n; i++)


{
int u, v;
cin >> u >> v;
e[u].push_back(v);
}

solve(1);

cout << max(dp[1][0], dp[1][1]);


}

Listing 10.5.5: 489869-experience.cpp


// https://csacademy.com/submission/489869/ execution time : 0.234 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

using namespace std;

int n, w[100000];
vector<int> g[100000];
long long dp[100000][2], ans[100000];

void dfs(int v)
{
long long sum = 0;
dp[v][0] = -w[v];
dp[v][1] = w[v];
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 493

for(int to : g[v])
{
dfs(to);
sum += ans[to];
dp[v][0] = max(dp[v][0], dp[to][0] - ans[to]);
dp[v][1] = max(dp[v][1], dp[to][1] - ans[to]);
}

dp[v][0] += sum;
dp[v][1] += sum;
ans[v] = max(dp[v][0] + w[v], dp[v][1] - w[v]);
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

ios::sync_with_stdio(0);

cin >> n;
for(int i = 0; i < n; i++)
{
cin >> w[i];
}

for(int i = 0; i < n - 1; i++)


{
int u, v;
cin >> u >> v;
g[u - 1].push_back(v - 1);
}

dfs(0);
cout << ans[0];
}

Listing 10.5.6: 501765-experience.cpp


// https://csacademy.com/submission/501765/ execution time : 0.578 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>

#define ll long long

using namespace std;

const int N=100005;

struct E
{
int to,next;
} mem[N<<1];

int n,num,x,y;
int w[N],head[N];
ll dp[N][2];

void add(int x,int y)


{
num++;
mem[num].to=y;
mem[num].next=head[x];
head[x]=num;
}

ll dfs(int k,int pre)


{
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 494

int u; ll ans,sum=0;
for (int j=head[k];j;j=mem[j].next)
{
u=mem[j].to;
if (u==pre) continue;
ans=dfs(u,k);
sum+=ans;
if (w[k]>w[u])
dp[k][0]=max(dp[k][0],dp[u][0]+1ll*w[k]-1ll*w[u]-ans);
if (w[k]<w[u])
dp[k][1]=max(dp[k][1],dp[u][1]+1ll*w[u]-1ll*w[k]-ans);
}

dp[k][0]+=sum;
dp[k][1]+=sum;

return max(dp[k][0],dp[k][1]);
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&w[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}

printf("%lld\n",dfs(1,0));

return 0;
}

Listing 10.5.7: 1082259-experience.cpp


// https://csacademy.com/submission/1082259/ execution time : 0.313 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <iostream>
#include <vector>

using namespace std;


typedef long long ll;

ll n,val[100123],i,a,b;
vector<ll> sus[100123];
ll dp[100123][2];

void dfs( ll par , ll cur )


{
if(sus[cur].size()==1 && cur!=1)return;
ll sum=0;
for(ll g=0;g<sus[cur].size();g++)
{
ll z=sus[cur][g];
if( z==par ) continue;
dfs( cur , z );
sum+=max( dp[z][0] , dp[z][1] );
}

dp[cur][1]=max( dp[cur][1] , sum );


dp[cur][0]=max( dp[cur][0] , sum );

for(ll g=0;g<sus[cur].size();g++)
{
ll z=sus[cur][g];
if(z==par)continue;
ll f=max( dp[z][0] , dp[z][1] );

dp[cur][1]=max( dp[cur][1] , val[cur]-val[z] + sum - f + dp[z][1] );


CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 495

dp[cur][0]=max( dp[cur][0] , val[z]-val[cur] + sum - f + dp[z][0] );


}
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin>>n;

for(i=1;i<=n;i++)
cin>>val[i];

for(i=1;i<n;i++)
{
cin>>a>>b;
sus[a].push_back(b);
sus[b].push_back(a);
}

dfs( -1 , 1 );

cout<<max( dp[1][0] , dp[1][1] )<<endl;

return 0;
}

Listing 10.5.8: 1549152-experience.cpp


// https://csacademy.com/submission/1549152/ execution time : 0.297 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<bits/stdc++.h>

using namespace std;

long long w[100001], c[100001], d[100001];


vector<int> adj[100001];

void dfs(int u, int from)


{
int v;
long long sum=0;
for(int i=0; i<adj[u].size(); i++)
{
v=adj[u][i];
if(v==from) continue;
dfs(v, u);
sum+=max(c[v], d[v]);
}

for(int i=0; i<adj[u].size(); i++)


{
v=adj[u][i];
if(v==from) continue;
d[u]=max(d[u], sum);
c[u]=max(c[u], sum);
if(w[v]<w[u])
{
d[u]=max(d[u], sum-max(c[v], d[v])+w[u]-w[v]+d[v]);
}
else
if(w[v]>w[u])
{
c[u]=max(c[u], sum-max(c[v], d[v])+w[v]-w[u]+c[v]);
}
}
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 496

std::freopen("experience.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(NULL);

int n, u, v;
cin >> n;

memset(c, 0, sizeof c);


memset(d, 0, sizeof d);

for(int i=1; i<=n; i++) cin >> w[i];

for(int i=1; i<n; i++)


{
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}

dfs(1, -1);

cout << max(d[1], c[1]);

return 0;
}

Listing 10.5.9: 1573243-experience.cpp


// https://csacademy.com/submission/1573243/ execution time : 0.375 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

#define sqr(x) ((x)*(x))


#define mp make_pair
#define uint unsigned

inline char gc()


{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2 &&
(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2) ? EOF : *p1++;
}

//#define gc getchar

inline int read()


{
int x = 0;
char ch = gc();
bool positive = 1;
for (; !isdigit(ch); ch = gc())
if (ch == ’-’) positive = 0;

for (; isdigit(ch); ch = gc())


x = x * 10 + ch - ’0’;

return positive ? x : -x;


}

inline void write(int a)


{
if(a>=10) write(a/10);
putchar(’0’+a%10);
}

inline void writeln(int a)


{
if(a<0)
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 497

{
a=-a;
putchar(’-’);
}

write(a);
puts("");
}

const int N=100005;


int n,w[N];
ll dp[N][2];
vector<int> v[N];

void dfs(int p,int fa)


{
ll sum=0;

for(unsigned i=0; i<v[p].size(); i++)


if(v[p][i]!=fa)
{
dfs(v[p][i],p);
ll zs=max(dp[v[p][i]][0],dp[v[p][i]][1]);
sum+=zs;
if(w[p]>=w[v[p][i]])
dp[p][0]=max(dp[p][0],dp[v[p][i]][0]+w[p]-w[v[p][i]]-zs);

if(w[p]<=w[v[p][i]])
dp[p][1]=max(dp[p][1],dp[v[p][i]][1]+w[v[p][i]]-w[p]-zs);
}

dp[p][0]+=sum;
dp[p][1]+=sum;
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

n=read();
for(int i=1;i<=n;i++)
w[i]=read();

for(int i=1;i<n;i++)
{
int s=read(),
t=read();
v[s].push_back(t);
v[t].push_back(s);
}

dfs(1,0);

cout<<max(dp[1][0],dp[1][1])<<endl;
}

Listing 10.5.10: 1593395-experience.cpp


// https://csacademy.com/submission/1593395/ execution time : 0.266 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

using namespace std;

struct state
{
long long v0, v1, v2;
};

void solve(int u,
vector<state>& mem,
const vector<int>& a,
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 498

const vector<vector<int>>& g)
{
if ((int) g[u].size() == 0)
{
return;
}

mem[u].v0 = mem[u].v1 = mem[u].v2 = 0ll;

for (auto& v : g[u])


{
solve(v, mem, a, g);
mem[u].v0 += max(mem[v].v0, max(mem[v].v1, mem[v].v2));
}

for (auto& v : g[u])


{
long long m = max(mem[v].v0, max(mem[v].v1, mem[v].v2));
if (a[v] <= a[u])
{
mem[u].v1 = max(mem[u].v1,
mem[u].v0 - m + mem[v].v1 +
(long long) a[u] - (long long) a[v]);
mem[u].v1 = max(mem[u].v1,
mem[u].v0 - m + mem[v].v0 +
(long long) a[u] - (long long) a[v]);
}

if (a[v] >= a[u])


{
mem[u].v2 = max(mem[u].v2,
mem[u].v0 - m + mem[v].v2 -
(long long) a[u] + (long long) a[v]);
mem[u].v2 = max(mem[u].v2,
mem[u].v0 - m + mem[v].v0 -
(long long) a[u] + (long long) a[v]);
}
}

return;
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(0);

int n;
cin >> n;

vector<int> a(n + 1);

for (int i = 1; i <= n; i++)


{
cin >> a[i];
}

vector<vector<int>> g(n + 1);


for (int i = 0; i < n - 1; i++)
{
int u, v;
cin >> u >> v;
g[u].emplace_back(v);
}

vector<state> ans(n + 1);


solve(1, ans, a, g);
int idx = 0;
#ifdef DEBUG
for (auto& x : ans) {
cout << idx++ << ": " << x.v0 << ’ ’ << x.v1 << ’ ’ << x.v2 << ’\n’;
}
#endif
CAPITOLUL 10. EJOI 2017 10.5. EXPERIENCE 499

cout << max(ans[1].v0, max(ans[1].v1, ans[1].v2)) << ’\n’;

return 0;
}

Listing 10.5.11: 1696625-experience.cpp


// https://csacademy.com/submission/1696625/ execution time : 0.234 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/experience/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<bits/stdc++.h>

using namespace std;

#define ll long long


#define pb push_back

const int maxn = 1e5 + 20;

vector<int> adj[maxn];

int a[maxn];

ll dp[maxn] , pd[maxn];

void dfs(int v)
{
ll sum = 0;

for(auto u : adj[v])
{
dfs(u);

sum += max(dp[u] , pd[u]);


}

dp[v] = pd[v] = sum;

for(auto u : adj[v])
{
ll k = max(dp[u] , pd[u]);

if(a[v] <= a[u])


dp[v] = max(dp[v] , dp[u] + a[u] - a[v] + sum - k);
if(a[v] >= a[u])
pd[v] = max(pd[v] , pd[u] + a[v] - a[u] + sum - k);
}
}

int main()
{
std::freopen("../experience_tests/experience.18.in", "r", stdin);
std::freopen("experience.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);

int n;
cin >> n;

for(int i = 0; i < n; i++)


cin >> a[i];

for(int i = 0; i < n - 1; i++)


{
int a , b;
cin >> a >> b;
a-- , b--;

adj[a].pb(b);
}
CAPITOLUL 10. EJOI 2017 10.6. GAME 500

dfs(0);

cout << max(dp[0] , pd[0]) << endl;


}

Listing 10.5.12: checkerExperience.cpp


#include "testlib0.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../experience_tests/experience.18.in",
(char*)"experience.out",
(char*)"../experience_tests/experience.18.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("experience", argc, argv);


compareRemainingLines();
}

10.5.3 *Rezolvare detaliată

10.6 Game
Problema 6 - Game 100 de puncte
Alice and Bob are playing the following game:
They are given a sequence of N positive integers with values less than or equal to N . The
elements of the sequence are numbered from 1 to N . Equal numbers may exist in the sequence.
A set S is created in the beginning of the game, containing the first P elements of the sequence.
Note that S may be a multiset - it may contain equal elements. The players take turns to play
and Alice is playing first. Each move is made as follows:
1) The player whose turn has come, selects one number from the set S and takes it away, adding
its value to his/her score (initially, the score of both players is 0).
2) The next number in the sequence, if any left at all, is added to the set S (if the sequence is
already empty, this action is skipped). This is to say, that after the first taking from S, the
number indexed with P  1 is added to the set, after the second one - the number indexed
with P  2 is added, etc.
The game continues, until the set S becomes empty. We assume that each player does their best
to maximize their own score. The game’s result is the number obtained by subtracting the points,
collected by Bob, from those, collected by Alice.
Task
Write a program game, which has to process K games on a given starting sequence.
Input
Two space separated positive integers N and K are read from the first line of the standard
input.
The second line consists of N space separated positive integers a1 , a2 , ..., aN , representing the
elements of the given sequence.
CAPITOLUL 10. EJOI 2017 10.6. GAME 501

The third line contains K space separated positive integers p1 , p2 , ..., pK , each defining the
starting set S, created from the given sequence (taking the first pi elements) and intended for the
i-th game, i 1, 2, ..., K.
Output
The program should print to the standard output K lines, each containing a single integer -
the corresponding game’s result. Line number i should contain the result of the game number i
(the games are numbered from 1 to K by the input).
Constraints

ˆ 1&N & 100 000


ˆ 1&K & 2 000
ˆ K &N
ˆ 1 & ai &N for i 1, 2, ..., N
ˆ 1 & pi &N for i 1, 2, ..., K
ˆ In 10% of the tests: 1 & N & 10
ˆ In 30% of the tests: 1 & N & 600
ˆ In 50% of the tests: 1 & N & 10 000, 1 & K & 1 000

Example
Sample input Sample output
52 2
24235 6
43

Explanation: The input data determines that your program will process two games. For both
games, the given sequence is the same, but for the first game P 4 and the starting multiset S is
{2, 4, 2, 3}, and for the second game, P 3 and S is {2, 4, 2}.

10.6.1 Indicaţii de rezolvare

Exponential solution
For very small constraints we can simulate the games with exponential complexity - we simply
try all possible ways the game could go. Since on every turn a player can have many different
choices, this solution runs very slowly. It would work for about N & 10, depending on the
implementation.
Polynomial solutions
The main observation in the problem is the following statement:
The optimal choice for the player whose turn it is is to take the largest number in the multiset.
This statement is obvious, since taking any other number does not obstruct the other player
in any way and only reduces the players own score.
In such case, we can just simulate all K games.
2
Solution for O N  K
On every turn, we have to find the largest number in the multiset. We can do this by simply
iterating through the multiset linearly. Since its size can be at most O N , the complexity for the
2 2
simulation of a single game is O N  and the total complexity of the solution is O N  K .
Solution for O N  K  log N 
We can improve our previous solution by keeping the elements that are in the multiset in
some structure that allows to quickly find the maximum number, as well as to remove the largest
element and to add new elements. There are many structures that support these operations with
logarithmic complexity per operation. The competitors can implement a structure of their own
CAPITOLUL 10. EJOI 2017 10.6. GAME 502

or use one of the STL structures - for example priority queue or set/map. The best complexity
that can be obtained using a structure of this kind is O N  log N  for the simulation of a single
game and hence a total complexity of O N  K  log N .
Solution for O N  K
In order to improve the efficiency of our simulation we have to make the following observation:
If in a certain moment the number that is being added to the multiset is the largest number in
the multiset, then this number will be taken on the very next turn.
This statement follows directly from the optimal playing strategy. In such case, let us define:
countx = the amount of times we have the number x in the multiset at a certain moment
Since all numbers are up to N , the size of count is also N .
The optimal move at a certain moment is to take the largest x, such that countx % 0. Adding
and removing numbers can be done in O 1 time by simply increasing or decreasing the respective
value in count with 1. The algorithm to quickly simulate a single game is as follows:
We begin with a pointer ptr N and we want to have countx 0 for every x % ptr at any
moment. To find the optimal choice at a certain moment we reduce the pointer until we reach
countptr % 0. When we are adding a new number A to the multiset, we have two options:
1. If A % ptr, then we remember that this number will be taken on the very next move. We
don’t have to update count at all.

2. If A & ptr then we can simply add the number and find the optimal choice using the pointer.
Since we reduce the pointer at most N times and process every addition in constant time,
the total amortized complexity for the simulation of a single game is O N . Thus we get a total
complexity of O N  K .

10.6.2 Coduri sursă

Listing 10.6.1: 486962-game.cpp


// https://csacademy.com/submission/486962/ execution time : 10.781 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <numeric>
#include <iterator>
#include <cstring>

using namespace std;

int n;
int a[100005], c[100005];

int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cerr.tie(nullptr);

int q;
cin >> n >> q;

for (int i=1; i<=n; i++)


{
cin >> a[i];
}
CAPITOLUL 10. EJOI 2017 10.6. GAME 503

while (q--)
{
int k, p = 0, e = 0;
cin >> k;
for (int i=1; i<=k; i++)
{
c[a[i]]++;
p = max(p, a[i]);
}

int j = k;
long long sum = 0;
for (int step=1; step<=n; step++)
{
if (e != 0)
{
if (step & 1)
{
sum += e;
}
else
{
sum -= e;
}

e = 0;
}
else
{
if (step & 1)
{
sum += p;
}
else
{
sum -= p;
}

c[p]--;
while (p > 0 && c[p] == 0)
{
p--;
}
}

if (step <= n-k)


{
int x = a[step + k];
if (x > p)
{
e = x;
}
else
{
c[x]++;
}
}
}

cout << sum << ’\n’;


}
}

Listing 10.6.2: 487418-game.cpp


// https://csacademy.com/submission/487418/ execution time : 11.678 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

#define err(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)

using namespace std;


CAPITOLUL 10. EJOI 2017 10.6. GAME 504

typedef long long ll;

const int dd = (int)1e5 + 1;


int pos;
int cnt[dd];

int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

int n, k;
cin >> n >> k;

vector<int> A(n);
for (int i = 0; i < n; i++) cin >> A[i];

for (int i = 0; i < k; i++)


{
int p;
cin >> p;
for (int j = 0; j < p; j++)
cnt[A[j]]++, pos = max(pos, A[j]);

ll ans = 0;
int sn = 1, last = -1;

for (int it = 0; it < n; it++)


{
if (last != -1)
{
ans += sn * last;
}
else
{
while (cnt[pos] == 0) pos--;
cnt[pos]--;
ans += pos * sn;
}

if (p + it < n)
{
if (A[p + it] > pos)
last = A[p + it];
else
last = -1, cnt[A[p + it]]++;
}
else
{
last = -1;
}

sn = -sn;
}

printf("%lld\n", ans);
}

return 0;
}

Listing 10.6.3: 487442-game.cpp


// https://csacademy.com/submission/487442/ execution time : 5.688 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<bits/stdc++.h>

using namespace std;

#define ll long long


#define fr first
#define sc second
CAPITOLUL 10. EJOI 2017 10.6. GAME 505

#define MAX ((ll)(1e9+100))


#define ARRS ((ll)(1e5+100))

ll a[ARRS];
pair<ll,ll> b[ARRS];

int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

ll n,p,m,q;
ll p1,p2,k;

cin>>n>>q;
for(int i=0; i<n; i++)
{
cin>>a[i];
b[i]={a[i],i};
}

sort(b,b+n);

while(q--)
{
cin>>k;
ll c=0,x=0;
while(c<k-1)
{
if(b[x].sc<k-1)c++;
x++;
}

x--;
ll d=1,p=0;
for(int i=k-1; i<n; i++)
{
//cout<<i<<" -> "<<x<<" "<<b[x].fr<<" "<<b[x].sc<<" "<<p<<endl;
if(a[i]>=b[x].fr)
p+=a[i]*d;
else
{
p+=b[x].fr*d;
x--;
while(x&&b[x].sc>i)x--;
}

d=d*-1;
}

while(x>=0)
{
p+=b[x].fr*d;
d*=-1;
x--;
}

cout<<p<<endl;
}

return 0;
}

Listing 10.6.4: 488403-game.cpp


// https://csacademy.com/submission/488403/ execution time : 12.156 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<cstdio>

int f[100005],v[100005];
int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
CAPITOLUL 10. EJOI 2017 10.6. GAME 506

std::freopen("game.out", "w", stdout);

int n,k,i,i1,poz,num,urmat=0,p,rand=1;
long long a=0,b=0;

scanf("%d%d",&n,&k);

for(i=1;i<=n;i++)
scanf("%d",&v[i]);

for(i1=1;i1<=k;i1++)
{
scanf("%d",&p);
for(i=1;i<=p;i++)
f[v[i]]++;

num=100000;
a=0;
b=0;
while(f[num]==0)
num--;

urmat=num;
rand=1;
poz=p+1;

while(num!=0)
{
if (rand==1)
a=a+1LL*urmat;
else
b=b+1LL*urmat;

f[urmat]--;
if (v[poz]>num && poz<=n)
urmat=v[poz],poz++;
else
{
if (poz<=n)
{
f[v[poz]]++;
poz++;
}

while(f[num]==0 && num>=1)


num--;

urmat=num;
}

rand=1-rand;
}

printf("%lld\n",a-b);
for(i=1;i<=100000;i++)
f[i]=0;
}

return 0;
}

Listing 10.6.5: 488741-game.cpp


// https://csacademy.com/submission/488741/ execution time : 6.516 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

#define maxN 100002


#define maxK 2002

using namespace std;

//FILE *fin = freopen("game.in", "r", stdin);


CAPITOLUL 10. EJOI 2017 10.6. GAME 507

//FILE *fout = freopen("game.out", "w", stdout);

int n, k, a[maxN];
int pos[maxN];

int cmp(const int x, const int y)


{
if (a[x] == a[y])
return x < y;
return a[x] < a[y];
}

int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

scanf("%d%d", &n, &k);


for (int i = 1; i <= n; ++ i)
{
scanf("%d", &a[i]);
pos[i] = i;
}

sort(pos + 1, pos + n + 1, cmp);

for (int i = 1; i <= k; ++ i)


{
int p;
scanf("%d", &p);
int cnt = 0, idx = 1;
long long ans = 0;
while (cnt < p - 1 && idx <= n)
{
if (pos[idx] < p)
++ cnt;
++ idx;
}

-- idx;
int t = 1;
for (int j = p; j <= n; ++ j)
{
if (pos[idx] > j || a[j] >= a[pos[idx]])
ans += t * a[j];
else
{
ans += t * a[pos[idx]];
-- idx;

while (idx > 0 && pos[idx] > j)


-- idx;
}

t = -t;
}

while (idx > 0)


{
ans += t * a[pos[idx]];
t = -t;

-- idx;
}

printf("%lld\n", ans);
}

return 0;
}

Listing 10.6.6: 490435-game.cpp


// https://csacademy.com/submission/490435/ execution time : 10.000 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
CAPITOLUL 10. EJOI 2017 10.6. GAME 508

// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<cstdio>

const int MAX_N = 100000;


int v[MAX_N], fr[1+MAX_N];

int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

int n, k, p;
scanf("%d%d", &n, &k);
for(int i = 0; i < n; ++i)
scanf("%d", &v[i]);

for(int q = 0; q < k; ++q)


{
int up = n, player = 1;
long long s = 0LL;
scanf("%d", &p);
for(int i = 0; i < p; ++i)
fr[v[i]]++;

for(int i = 0; i < n; ++i)


{
if(i >= 1 && i + p - 1 < n && v[i + p - 1] > up)
s = s + player * v[i + p - 1];
else
{
if(i >= 1 && i + p - 1 < n)
fr[v[i + p - 1]]++;

while(fr[up] == 0)
--up;

--fr[up];
s = s + player * up;
}

player = -player;
}

printf("%lld\n", s);
}

return 0;
}

Listing 10.6.7: 507893-game.cpp


// https://csacademy.com/submission/507893/ execution time : 7.281 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<utility>

using namespace std;

int n;
pair<int,int> a[111111];
bool f[111111];

void upd(long long int&res,int b,int x)


{
if(b%2)res += x;else
res -= x;
}

int main()
CAPITOLUL 10. EJOI 2017 10.6. GAME 509

{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

int q;
scanf("%d%d",&n,&q);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i].first);
a[i].second = i;
}

sort(a+1,a+n+1);
reverse(a+1,a+n+1);

while(q--)
{
int x;
scanf("%d",&x);
int pnt = x;
long long int res = 0;
memset(f,0,sizeof(f));

for(int i=1; i<=n; i++)


{
int pos = a[i].second, val = a[i].first;
while(pnt <= n && f[pnt])
pnt++;

if(pnt > n)
{
upd(res, i, val);
}
else
if(pos > pnt)
{
upd(res, pos - x + 1, val);
f[pos] = true;
}
else
{
upd(res, pnt - x + 1, val);
f[pnt] = true;
}
}

cout << res << endl;


}

return 0;
}

Listing 10.6.8: 594280-game.cpp


// https://csacademy.com/submission/594280/ execution time : 10.250 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

using namespace std;


typedef long long ll;
const int NMAX = 1e5 + 1;

int cnt[NMAX];

main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

int n, K;
scanf("%d%d", &n, &K);
int arr[n];
for (int i = 0; i < n; ++i)
CAPITOLUL 10. EJOI 2017 10.6. GAME 510

{
scanf("%d", &arr[i]);
}

for (int i = 0; i < K; ++i)


{
int k;
scanf("%d", &k);
int curmx = 0;
for (int j = 0; j < k - 1; ++j)
{
++cnt[arr[j]];
curmx = max(curmx, arr[j]);
}

ll co[] = {0, 0};


bool ind = 0;
for (int j = k - 1; j < n; ++j)
{
if (arr[j] >= curmx)
{
co[ind] += arr[j];
}
else
{
co[ind] += curmx;
++cnt[arr[j]];
--cnt[curmx];
while (curmx > 0 && cnt[curmx] == 0)
{
--curmx;
}
}

ind = !ind;
}

while (curmx > 0)


{
co[ind] += curmx;
--cnt[curmx];
while (curmx > 0 && cnt[curmx] == 0)
{
--curmx;
}

ind = !ind;
}

printf("%lld\n", co[0] - co[1]);


}
}

Listing 10.6.9: 1173310-game.cpp


// // https://csacademy.com/submission/1173310/ execution time : 14.783 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <stdio.h>
#include <queue>
#include <algorithm>

#include<iostream>

using namespace std;

int k,n,t[100001],nxt[100001],iz[100010];

struct pa
{
int ind,val;
};

bool cmp(pa a,pa b)


CAPITOLUL 10. EJOI 2017 10.6. GAME 511

{
return a.val>b.val;
}

pa a[100010];

long long simulate(int t)


{
long long score=0;
int ff=0;
fill(iz,iz+n,0);
for(int i=0;i<n;i++) nxt[i]=i;
for(int i=0;i<n;i++)
{
int x=a[i].ind;
x=(x>=t?x-t+1:0);
if(x<=ff)
{
while(iz[ff]) ff++;
nxt[x]=ff;
}
iz[nxt[x]]=a[i].val;
nxt[x]=nxt[nxt[x]+1];
if(x<=ff) ff=nxt[x];
}
for(int i=0;i<n;i++) score+=(i%2==0?iz[i]:-iz[i]);
return score;
}

int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

scanf("%d %d",&n,&k);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i].val);
a[i].ind=i;
}

sort(a,a+n,cmp);

for(int i=0;i<k;i++)
scanf("%d",&t[i]);
for(int i=0;i<k;i++)
printf("%lld\n",simulate(t[i]));
}

Listing 10.6.10: 1275946-game.cpp


// https://csacademy.com/submission/1275946/ execution time : 9.788 s
// https://csacademy.com/contest/ejoi-2017-day-2/task/game/statistics/
// https://csacademy.com/contest/ejoi-2017-day-2/task/

#include <bits/stdc++.h>

//#ifdef atom #else #endif


using namespace std;

typedef long long ll;


typedef pair<int, int> ii;

#define X first
#define Y second
#define vi vector<int>
#define vvi vector< vi >
#define vii vector< ii >
#define mp make_pair
#define pb push_back

int a[100005];
int b[100005];
vii foo;
int use[100005];
CAPITOLUL 10. EJOI 2017 10.6. GAME 512

void update(ll &res, int b, int p)


{
//printf("update %d %d\n", b, p);
if(b%2) res += p;
else res -= p;
}

int main()
{
std::freopen("../game_tests/game.20.in", "r", stdin);
std::freopen("game.out", "w", stdout);

int n, k;
scanf("%d %d", &n, &k);
for(int i = 0; i< n; i++)
{
scanf("%d", a+i);
foo.pb(ii(a[i], i));
}

sort(a, a+n);
reverse(a, a+n);
sort(foo.begin(), foo.end()); reverse(foo.begin(), foo.end());

for(int i = 0; i< n; i++)


{
b[foo[i].Y] = i;
}

while(k--)
{
int x; scanf("%d", &x);
for(int i = 0; i< x-1; i++)
{
int k = b[i];
use[k] = 1;
//printf("use %d\n", k);
}

int ptr = 0;
ll res = 0;
int cnt = 1;
while(cnt<= n)
{
if(x+cnt-2< n)
{
int nxt = b[x+cnt-2];
use[nxt] = 1;
//printf("turn on %d\n", nxt);
if(nxt< ptr)
{
update(res, cnt++, a[nxt]);
use[nxt] = 0;
continue;
}
}

while(ptr< n && use[ptr] == 0) ptr++;

use[ptr] = 0;
update(res, cnt++, a[ptr]);
}

printf("%lld\n", res);
}

return 0;
}

Listing 10.6.11: checkerGame.cpp


#include "testlib0.h"

using namespace std;


CAPITOLUL 10. EJOI 2017 10.6. GAME 513

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../game_tests/game.20.in",
(char*)"game.out",
(char*)"../game_tests/game.20.sol",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("game", argc, argv);


compareRemainingLines();
}

10.6.3 *Rezolvare detaliată


514
Appendix A

Programa olimpiadei - gimnaziu

A.1 Clasa a V-a


1. Algoritmi elementari
– Tipuri simple de date. Tipul ı̂ntreg (pe 4 octeţi), tipul logic
– Structura liniară, alternativă şi repetitivă
– Prelucrarea cifrelor numerelor naturale scrise ı̂n baza 10
– Divizibilitate (divizorii numerelor naturale, numere prime, determinarea cmmdc prin
algoritmul lui Euclid, cmmmc, numere prime ı̂ntre ele, simplificarea fracţiilor)
– Calculul unor expresii (de exemplu, factorial, ridicare la putere)
2. Generări de şiruri
– Generarea şirurilor pe baza unor reguli
– Şirul lui Fibonacci şi alte şiruri recurente
3. Fişiere text
4. Prelucrări de şiruri de numere citite succesiv, fără memorarea lor
– Prelucrări ce necesită stocarea ultimului element
(de exemplu, determinare maxim/minim, primele două maxime sau minime)
– Prelucrări ce necesită stocarea ultimelor p (p=2, 3, ..) elemente (de exemplu, cea mai
lungă subsecvenţă cu anumite proprietăţi, numărarea secvenţelor cu anumite pro-
prietăţi)

Doar pentru etapa naţională

5. Algoritmi elementari. Tablouri unidimensionale


– Prelucrări elementare (de exemplu, parcurgere, inversare, verificare proprietăţi)
– Căutarea liniară a unor valori
– Vectori caracteristici/ de frecvenţă
– Algoritmi de sortare ı̂n complexitate pătratică (sortarea prin selecţie, sortarea prin
inserţie, metoda bulelor)
– Sortare prin numărare (folosind vectori de frecvenţă)

A.2 Clasa a VI-a


1. Tipuri simple de date (ı̂ntregi, reale, char)
2. Sisteme de numeraţie şi reguli de conversie
3. Aritmetica modulară (adunări, scăderi, ı̂nmulţiri)
4. Divizibilitate
– Ciurul lui Eratostene
– Descompunerea numerelor naturale ı̂n factori primi
5. Tablouri unidimensionale (vectori)
– Operaţii cu mulţimi
– Căutare binară

515
APPENDIX A. PROGRAMA OLIMPIADEI - GIMNAZIU A.3. CLASA A VII-A 516

– Precalcularea unor informaţii pentru prefixe/sufixe ı̂n tablouri unidimensionale


(de exemplu sume parţiale, maxime parţiale)
– Probleme cu secvenţe de valori (de exemplu, determinarea unei secvenţe maximale cu o
anumită proprietate, numărarea secvenţelor, prelucrarea secvenţelor de lungime fixată
ce nu implică stive, cozi sau alte structuri de date avansate)
– Căutarea apariţiilor unei subsecvenţe ı̂ntr-o secvenţă de valori ı̂n timp pătratic
– Interclasarea tablourilor unidimensionale

Doar pentru etapa naţională

6. Tablouri bidimensionale
– Prelucrări elementare ale tablourilor bidimensionale (de exemplu, parcurgeri pe linii/-
coloane/diagonale/ı̂n spirală, generări, transpunere, bordare)
– Prelucrări specifice tablourilor bidimensionale pătratice (de exemplu, diagonale şi zone
determinate de diagonale)
– Căutări secvenţiale ı̂n tablouri bidimensionale (de exemplu, a unui element, a unei
secvenţe de valori, a unei submatrice)
– Utilizarea vectorilor de direcţie
7. Simulări
– reprezentarea sistemului de simulat, starea sistemului
– bucla de evenimente ce modifică starea sistemului

A.3 Clasa a VII-a


1. Funcţii
– Declarare, definire, apel
– Variabile locale, variabile globale
– Transmiterea parametrilor prin valoare şi prin referinţă
2. Tablouri
– Tehnica Two Pointers
– Tablouri de diferenţe - Difference Arrays
– Determinarea secvenţei de sumă maximă
– Determinarea elementului majoritar
– Precalcularea unor informaţii ı̂n tablouri bidimensionale (de exemplu sume parţiale
pe prefixe/sufixe de linii/coloane, suma elementelor dintr-o submatrice cu unul dintre
colţuri fixat ı̂n unul dintre colţurile matricei)
– Tablouri multidimensionale
3. Tipuri de date neomogene (struct)
4. Utilizarea funcţiilor din biblioteca STL pentru sortare şi căutare
5. Metoda Greedy

Doar pentru etapa naţională

6. Operaţii cu numere mari


– Adunarea numerelor mari
– Scăderea numerelor mari
– ı̂nmulţirea unui număr mare cu un număr natural
– ı̂mpărţirea cu rest a unui număr mare la un număr natural nenul
7. Algoritmul de exponenţiere rapidă
8. Stiva. Aplicaţii specifice

A.4 Clasa a VIII-a


1. Şiruri de caractere. Funcţii specifice
2. Generarea elementelor combinatoriale prin algoritmi de tip succesor
– Submulţimi
– Produs cartezian
APPENDIX A. PROGRAMA OLIMPIADEI - GIMNAZIU A.5. BARAJUL DE SELECŢIE
A LOTULUI NAŢIONAL LĂRGIT 517

– Permutări
– Combinări
– Aranjamente
– Utilizarea funcţiilor din biblioteca STL pentru permutări

Doar pentru etapa naţională

3. Coada. Aplicaţii specifice


4. Coada cu dublă prioritate (deque). Aplicaţii specifice
5. Elemente de geometrie
– sistemul de coordonate cartezian ı̂n plan
– puncte ı̂n planul cartezian
– distanţa dintre două puncte
– arii

A.5 Barajul de selecţie a lotului naţional lărgit


1. Operaţii pe biţi
2. Indicatorul lui Euler
3. Tablouri de diferenţe (Difference Arrays) 2D
4. Recursivitate
5. Algoritmul de fill
6. Algoritmul lui Lee
7. Tehnica Square root decomposition
8. Metoda programării dinamice

A.6 Note
ˆ Exceptând clasa a V-a, programa fiecărei clase include şi programele pentru toate clasele
precedente.
ˆ Barajul de selecţie a lotului naţional lărgit include programele pentru clasele V-VIII, precum
şi temele suplimentare specificate.
ˆ Pentru barajele de selecţie a echipelor reprezentative ale României vor fi abordate teme
suplimentare.
Appendix B

”Instalare” C++

Ca să putem ”lucra” cu C++ avem nevoie de

ˆ un compilator pentru C++, şi


ˆ un IDE (Integrated Development Enviroment) pentru C++.

B.1 Kit OJI 2017


Poate că cel mai uşor este să se descarce fişierul
http://www.cnlr.ro/resurse/download/Kit_OJI_2017.rar
https://cdn.kilonova.ro/p/WXbRLG/Kit_OJI_2017.rar
http://olimpiada.info/oji2018/Kit_OJI_2017.rar
https://www.liis.ro/Documents/download/Kit_OJI_2017.rar
folosit de către elevi la şcoală şi la olimpiade.

Fişierele din arhivă sunt:

Figura B.1: Fişierele din Kit OJI 2017

Se lansează ı̂n execuţie fişierul OJIkit 2017.exe.

Instalarea este foarte uşoară (este de tipul ”next -> next -> ... -> next”) iar pe in-
ternet sunt multe site-uri de ajutor. De exemplu:

https://www.pbinfo.ro/?pagina=intrebari-afisare&id=26
https://www.youtube.com/watch?v=CLkWRvAwLO8
https://infoas.ro/lectie/112/tutorial-instalare-codeblocks-usor-introducere-in-in
https://www.competentedigitale.ro/c/oji2019/Kit_OJI_2017.rar

Există numeroase alternative la CodeBlocks: Dev-C++, Microsoft Visual Studio, Eclipse,


NetBeans, CodeLite, CLion, KDevelop, etc.

518
APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 519

Kitul Kit_OJI_2017 se instalează implicit pe C:¯OJI¯


138
t IDE-ul Code::Blocks ,
t compilatorul pentru C: gcc.exe,
139
t compilatorul pentru C++: g++.exe
t make.exe
t gdb.exe
t şi... altele!

La sfârşitul instalării apar pe ecran două link-uri:

Figura B.2: CodeBlocks & C++ Reference

Figura B.3: Ce conţine C:¯ OJI ¯

B.1.1 Code::Blocks
Pentru versiuni mai noi, de Code::Blocks şi compilatoare, se poate accesa site-ul
http://www.codeblocks.org/downloads/binaries
de unde se poate descărca, de exemplu, codeblocks-20.03mingw-setup.exe.
Versiuni mai vechi, dar foarte bune, se pot descărca de la adresa
138
Code::Blocks este un IDE (integrated development environment) pentru C/C++, un fel de Notepad ... mai
sofisticat, cu multe butoane care lansează ı̂n execuţie diverse programe, de exeplu g++.exe
139
g++.exe este compilatorul de C++, programul care va verifica dacă instrucţiunile noastre sunt ok sau nu ...

şi care ne va supăra mereu cu erorile pe care ni le arată ... ... Este “o mică artă” să ı̂nţelegem mesajele de
eroare pe care le vedem pe ecran şi să fim ı̂n stare să “depanăm” programele noastre!
APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 520

http://www.codeblocks.org/downloads/source/5
Mai precis:
https://sourceforge.net/projects/codeblocks/files/Binaries/20.03/
http://sourceforge.net/projects/codeblocks/files/Binaries/17.12
http://sourceforge.net/projects/codeblocks/files/Binaries/16.01

B.1.2 Folder de lucru

Figura B.4: Folder de lucru

De preferat este să avem cel puţin două partiţii: C:¯, D:¯, ... şi să “lăsăm ı̂n pace” partiţia C:¯
pentru sistemul de operare şi programele instalate!

În figura de mai sus se poate observa că pe D:¯ există un folder de lucru pentru programele ı̂n
C++, folder care se numeşte Programe C++. Aici vom salva toate programele C++ pe care
le vom scrie, eventual organizate pe mai multe subfoldere.

Acum, să intrăm ı̂n folderul Programe C++.

Click-dreapta cu mouse-ul şi selectăm “New -¿ Text document” ca ı̂n figura următoare.
APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 521

Figura B.5: New -¿ Text document

Figura B.6: Schimbare nume fişier şi nume extensie fişier


APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 522

Figura B.7: Confirmare schimbare extensie ı̂n .cpp

Figura B.8: Pregătit pentru Code::Blocks

Dacă vom executa două click-uri pe numele fşierului p01.cpp sau un click pentru a marca
fişierul şi apoi un ¡Enter¿, se va declanşa Code::Blocks cu p01.cpp ı̂n fereastra de editare şi, ce
este şi mai important, cu Programe C++ ca folder curent de lucru pentru Code::Blocks.
Adică, aici vor apărea toate fişiere generate de Code::Blocks pentru p01.cpp.

B.1.3 Utilizare Code::Blocks

Figura B.9: Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks
APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 523

Figura B.10: Primul cod de program C++ ı̂n Code::Blocks

Figura B.11: Build - compilare


APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 524

Figura B.12: 0 error(s), 0 warning(s)

Figura B.13: Run - execuţie


APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 525

Figura B.14: Executat corect: a făcut “nimic”

B.1.4 Setări Code::Blocks


De preferat este să lăsăm setările implicite (sunt stabilite totuşi de nişte specialişti!) dar, dacă
vrem, putem să umblăm şi noi prin setări!

Figura B.15: Settings  % Compiler


APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 526

Figura B.16: Toolchain executables

Figura B.17: Unde sunt acele programe


APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 527

B.1.5 Multe surse ı̂n Code Blocks


Settings Environment: pe calculatorul meu setările sunt:

Figura B.18: Multe surse ı̂n Code Blocks - setări

Dacă avem fişierele p01.cpp, ..., p05.cpp, ı̂n folderul nostru de lucru, şi facem dublu-click pe
fiecare ... vor apărea toate ...

Figura B.19: Multe surse in Code Blocks - exemple


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 528

B.2 winlibs
B.2.1 GCC şi MinGW-w64 pentru Windows
Se descarcă de la
http://winlibs.com/#download-release
unul dintre fişierele:

ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.7z dimensiune fişier = 148 MB
ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-llvm-11.0.0-mingw-w64-8.0.0-r
3.zip dimensiune fişier = 324 MB

ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.7z dimensiune
fişier = 52.1 MB
ˆ winlibs-x86_64-posix-seh-gcc-10.2.0-mingw-w64-8.0.0-r3.zip dimensi-
une fişier = 141 MB

Se dezarhivează şi se mută folderul mingw64 pe C: sau D: sau ...


Eu l-am dezarhivat pe cel mai mic şi l-am pus pe D:

Figura B.20: mingw64 pe D:

B.2.2 PATH
Trebuie pusă ı̂n PATH calea pentru D:\mingw64\bin\. În “Type here to search” scrieţi: path
şi apoi click pe “Edit the system environment variables”, ca ı̂n figura următoare:
APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 529

Figura B.21: search path

Apare fereastra “System properties”:

Figura B.22: System properties –¿ Advanced

Fereastra este poziţionată pe tab-ul “Advanced”. Se selectează “Environment Variables”.


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 530

Figura B.23: Environment Variables

Se selecteaza “Path” şi click pe “Edit”. Apare fereastra “Edit Environment Variables”

Figura B.24: Edit Environment Variables –¿ New

Se selectează “New”, se scrie calea D:¯mingw64¯bin şi se finalizează cu click pe “OK”,


“OK”, ..., “OK” până la sfârşit!
Se verifică calea şi versiunea pentru “gcc”:

ˆ C:¯path
ˆ C:¯gcc –version (Atentie! sunt 2 caractere - consecutive)
APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 531

Figura B.25: Calea şi versiunea pentru gcc

Dacă totul este OK atunci se trece la instalarea IDE-ului preferat (Integrated Development
140
Environment ), de exemplu Code::Blocks 20.03 (sau Eclipse, Visual Studio Code, Dev C++,
NetBeans, şi altele).

Observatie: Pentru Windows 11

"Setting the path and variables in Windows 11

In the System > About window,


click the Advanced system settings link
at the bottom of the Device specifications section.

In the System Properties window,


click the Advanced tab,
then click the Environment Variables button
near the bottom of that tab."

B.2.3 CodeBlocks
CodeBlocks se poate descărca de la http://www.codeblocks.org/downloads/26. Sunt
mai multe variante dar eu am descărcat numai codeblocks-20.03-setup.exe care are 35.7 MB.
La instalare am lăsat totul implicit, deci ... Next, Next, ..., Next până la sfârşit!
La prima lansare ı̂n execuţie este necesară setarea locaţiei compilatorului de C++ (gcc-ul pe
care tocmai l-am instalat!).
140
https://en.wikipedia.org/wiki/Integrated_development_environment
https://ro.wikipedia.org/wiki/Mediu_de_dezvoltare
APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 532

Figura B.26: Settings –¿ Compiler

Figura B.27: Toolchain executables –¿ Auto-detect


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 533

Figura B.28: New –¿ Text Document

Figura B.29: New text Document.txt

Figura B.30: Schimbare nume şi extensie


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 534

Figura B.31: Moore apps

Figura B.32: Look for another app


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 535

Figura B.33: Cale pentru codeblocks.exe

Figura B.34: Selectare codeblocks.exe


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 536

Figura B.35: Editare test01.cpp

Figura B.36: Compilare test01


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 537

Figura B.37: Mesaje după compilare

Figura B.38: Execuţie test01


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 538

Figura B.39: Rezultat execuţie test01

Figura B.40: Fişiere apărute după compilare!

Figura B.41: Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 539

Figura B.42: Lista programelor de utilizat

Figura B.43: Selectare Code::Blocks IDE pentru fişierele .cpp

Figura B.44: Editare+Compilare+Execuţie pentru test02


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 540

Figura B.45: Selectare tab ce conţine test01.cpp


Appendix C

main(), cin, cout, fin, fout

C.1 Funcţia main()


Toţi suntem obişnuiţi cu structura unui program C/C++ pe care trebuie să-l scriem la olimpiadă!
141
El arată cam aşa :
,

Listing C.1.1: sss1.cpp


#include <iostream>
#include <fstream>

using namespace std;

int n, c, sum, L, sol1, sol2, x, k, sc, nr, i;

ifstream fin ("sss.in");


ofstream fout("sss.out");

int main()
{
fin>>c;
fin>>n;
// instructiuni

return 0;
}

Chiar dacă utilizarea funcţiilor definite de utilizator este prevăzută ı̂ncepând cu clasa a VII-a,
este bine să le folosim ı̂ncă din clasa a V-a! Asta pentru că utilizarea lor permite o mai bună
142
lizibilitate a programelor! Şi oricum ... participanţii la olimpiadă citesc mai mult decât este
143
prevăzut ı̂n programa olimpiadei! Vom folosi modelul de la funcţia main().
,

Listing C.1.2: sss2.cpp


#include <iostream>
#include <fstream>

using namespace std;

int n, c, sum, L, sol1, sol2, x, k, sc, nr, i;

ifstream fin ("sss.in");


ofstream fout("sss.out");

int calcul()
{
// instructiuni

141
https://sepi.ro/page/oni2022
142
https://ro.wikipedia.org/wiki/Lizibilitate
143
tot ce nu este interzis, este permis!

541
APPENDIX C. MAIN(), CIN, COUT, FIN, FOUT C.1. FUNCŢIA MAIN() 542

return 0;
}

int main()
{
fin>>c;
fin>>n;

calcul();

return 0;
}
Appendix D

”Vecini” ...

D.1 Direcţiile N, E, S, V

Figura D.1: ”vecini” ı̂n matrice şi sistem Oxy

În figura D.1 sunt două modele (notaţii) utile ı̂n probleme ı̂n care se folosesc directiile N, E, S şi
V (NE, NV, SE şi SV se deduc uşor!).

Figura D.2: Toţi ”pereţii” sunt la N sau la V ı̂n matricea ”bordată”

În figura D.2 ...

543
APPENDIX D. ”VECINI” ... D.1. DIRECŢIILE N, E, S, V 544

Figura D.3: Toţi ”pereţii” sunt codificaţi

În figura D.3 ...


Appendix E

Diagonale ı̂n matrice

E.1 Diagonale ı̂n matrice

Figura E.1: diagonale ı̂n matrice - start = 0

În figura E.1 sunt prezentate diagonale principală şi secundară ı̂mpreună cu zonele din matrice pe
care acestea le separă (indici plecând de la 0).

Figura E.2: diagonale ı̂n matrice - start = 1

În figura E.2 sunt prezentate diagonale principală şi secundară ı̂mpreună cu zonele din matrice
pe care acestea le separă (indici plecând de la 1).

545
APPENDIX E. DIAGONALE ÎN MATRICE E.2. SIMETRIC FAŢĂ DE DIAGONALE 546

E.2 Simetric faţă de diagonale


E.2.1 Simetric faţă de diagonala principală

Figura E.3: simetric faţă de diagonala principală

E.2.2 Simetric faţă de diagonala secundară

Figura E.4: simetric faţă de diagonala secundară


Appendix F

Parcurgere matrice

F.1 Parcurgere NE spre SV

Figura F.1: Interclasarea vectorilor

În figura F.1 este prezentată parcurgerea ı̂n matrice din NE spre SV.

547
Appendix G

Exponenţiere rapidă

G.1 Analogie baza 2 cu baza 10

Figura G.1: Analogie B2 cu B10

În figura G.1 este considerat numărul nr 234 care ı̂n baza 2 se scrie sub forma 11101010 (celula
C10).
Pe R3 (rândul 3) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 10: se ”şterge” ulima cifră (care este egală cu nr%10 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 10).
Pe R10 (rândul 10) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 2: se ”şterge” ulima cifră (care este egală cu nr%2 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 2, la fel cum se ı̂ntâmplă ı̂n baza 10).
Observaţia 6. Cifrele se obţin ı̂n ordine inversă. Prima cifră obţinută ca rest este de fapt ultima
cifră din număr (vezi R4 şi R11).
Pe R6 şi R16 sunt precizate puterile bazei.
Valoarea numărului nr este suma produselor dintre cifre şi puterile corespunzătoare:
ˆ ı̂n baza 10: 4*1 + 3*10 + 2*100 = 234 (R3 şi R6)
ˆ ı̂n baza 2: 0*1 + 1*2 + 0*4 + 1*8 + 0*16 + 1*32 + 1*64 + 1*128 = 234 (R13 şi R16)

548
APPENDIX G. EXPONENŢIERE RAPIDĂ G.2. NOTAŢII, RELAŢII ŞI FORMULE 549

G.2 Notaţii, relaţii şi formule


234 1˜1281˜641˜320˜161˜80˜41˜20˜1
a a
1˜128 1˜64 1˜32 0˜16 1˜8 0˜4 1˜2 0˜1
a ˜ a ˜ a ˜ a ˜ a ˜ a ˜ a ˜ a
128 1 64 1 32 1 16 0 8 1 4 0 2 1 1 0
a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  .
k
2
Dacă notăm ak a atunci
234 1 1 1 0 1 0 1 0
a a7  ˜ a6  ˜ a5  ˜ a4  ˜ a3  ˜ a2  ˜ a1  ˜ a0  .
2
Şirul a0 , a1 , a2 , ... se obţine uşor prin ridicări la putere ak ak1 .

2 2 2 4 2 8 2 16
a0 a a1 a0 a  a2 a1 a  a3 a2 a  a4 a3 a 
2 32 2 64 2 128
a5 a4 a  a6 a5 a  a7 a6 a 

Dacă notăm ek ”exponentul (puterea)” la care apare ak ı̂n produs, atunci putem observa
uşor că şirul e0 , e1 , e2 , e3 , ... se obţine prin ı̂mpărţiri succesive ale lui n prin 2 şi preluı̂nd restul
rezultatului. Pentru a folmaliza acest lucru vom considera şirul n0 , n1 , n2 , n3 , ... definit astfel:

n0 n,
w
nk nk1 ©2 (câtul ı̂mpărţirii!) k '1
Folosind aceste notaţii, putem scrie:

ek nk %2 , (restul ı̂mpărţirii!) k '0


ek 1 0
Dacă notăm şi produsul parţial pk ak  ˜ ... ˜ a1  ˜ a0 
obţinem ı̂n final relaţiile:

~
„n0 n;
„
„
„
„a0 a;
„
„
„
„
„
„e n0 %2 " r0, 1x;
„ 0
„
„
„ 1 ˜ a0 , dacă e0 1;
„
„p0
e
a00 sau, altfel scris: p0 w
„
„
„
„ 1, dacă e0 0;
„
„
„
‚
„ (G.2.1)
„
„
„
„
„nk nk1 ©2 k ' 1; nk1 j 0 desigur ! ... dar şi nk1 j 1 
„
„
„ ak1 , k ' 1;
2
„
„a k
„
„
„
„
„e k nk %2 " r0, 1x, k ' 0;
„
„
„ pk1 ˜ ak , k ' 0, dacă ek 1; ak ak modifică produsul pk1 
1
„
„
„
„p w
„ k k ' 0, dacă ek 0; ak 1 nu modifică produsul pk1 
0
€ pk1 ,

G.3 Pregătire pentru scrierea codului!


Relaţia nk nk1 ©2 ı̂nseamnă că nnou nvechi ©2 (sunt explicaţii pentru ı̂ncepători ... nu pentru
avansaţi!) şi se poate programa sub forma n=n/2;
2 2
Relaţia ak ak1 ı̂nseamnă că anou avechi şi se poate programa sub forma a=a*a;
Relaţia pk pk1 ˜ ak ı̂nseamnă că pnou pvechi ˜ anou şi se poate programa sub forma p=p*a;
Relaţia pk pk1 ı̂nseamnă că pnou pvechi şi se poate programa sub forma p=p; care
ı̂nseamnă că nu se schimbă p, deci ... mai bine nu mai scriem nicio instrucţiune!
APPENDIX G. EXPONENŢIERE RAPIDĂ G.4. CODUL 550

G.4 Codul
Codul pentru relaţiile (G.2.1) devine:

Listing G.4.1: exponentiere rapida1.cpp


1 #include<iostream> // actualizare p dupa actualizare a si n
2
3 using namespace std;
4
5 int exponentiere_rapida(int a, int n) // p=aˆn
6 {
7 int nk1, nk;
8 int ak1, ak;
9 int ek1, ek;
10 int pk1, pk;
11
12 int k=0;
13 nk1=n;
14 ak1=a;
15 ek1=nk1%2;
16 if(ek1==1)
17 pk1=ak1;
18 else
19 pk1=1;
20
21 while(nk1>1)
22 {
23 k++;
24 nk=nk1/2;
25 ak=ak1*ak1;
26 ek=nk%2;
27 if(ek==1)
28 pk=pk1*ak;
29 else
30 pk=pk1;
31
32 // devin valori "vechi" inainte de noua actualizare
33 nk1=nk;
34 ak1=ak;
35 ek1=ek;
36 pk1=pk;
37 }
38
39 return pk;
40 }
41
42 int main()
43 {
44 int a=2;
45 //int n=234; // aˆn = prea mare !!!
46 //int n=30;
47 //int n=6; // par: 2ˆ6 = 64 ... usor de verificat!
48 int n=5; // impar: 2ˆ5 = 32 ... usor de verificat!
49
50 int rez=exponentiere_rapida(a,n);
51
52 cout<<a<<"ˆ"<<n<<" = "<<rez<<"\n";
53
54 return 0;
55 }
56 /*
57 2ˆ30 = 1073741824
58
59 Process returned 0 (0x0) execution time : 0.076 s
60 Press any key to continue.
61 */

Observaţia 7. În acest cod actualizarea lui p se face după actualizările pentru a şi n.
Observaţia 8. În codul următor actualizarea lui p se face ı̂naintea actualizărilor pentru a şi n,
corespunzător relaţiilor (G.4.2).
APPENDIX G. EXPONENŢIERE RAPIDĂ G.4. CODUL 551

Listing G.4.2: exponentiere rapida2.cpp


1 #include<iostream> // actualizare p inainte de actualizare a si n
2
3 using namespace std;
4
5 int exponentiere_rapida(int a, int n) // p=aˆn
6 {
7 int nk1, nk;
8 int ak1, ak;
9 int ek1, ek;
10 int pk1, pk;
11
12 nk1=n;
13 ak1=a;
14 pk1=1;
15
16 while(nk1>0)
17 {
18 ek=nk1%2;
19 if(ek==1)
20 pk=pk1*ak1;
21 else
22 pk=pk1;
23 ak=ak1*ak1;
24 nk=nk1/2;
25
26 // devin valori "vechi" inainte de noua actualizare
27 nk1=nk;
28 ak1=ak;
29 pk1=pk;
30 }
31
32 return pk;
33 }
34
35 int main()
36 {
37 int a=2;
38 //int n=234; // aˆn = prea mare !!!
39 //int n=30;
40 int n=6; // par: 2ˆ6 = 64 ... usor de verificat!
41 //int n=5; // impar: 2ˆ5 = 32 ... usor de verificat!
42
43 int rez=exponentiere_rapida(a,n);
44
45 cout<<a<<"ˆ"<<n<<" = "<<rez<<"\n";
46
47 return 0;
48 }
49 /*
50 2ˆ30 = 1073741824
51
52 Process returned 0 (0x0) execution time : 0.076 s
53 Press any key to continue.
54 */

~
„iniţializări:
„
„
„
„
„
„p1 1;
„
„
„
„n1 n;
„
„
„
„
„
„a1 a;
„
„
„
„
„calcul pentru k ' 0:
„
„e
‚
„ k nk1 %2 " r0, 1x; k ' 0 (G.4.2)
„
„ pk1 ˜ ak1 , k ' 0, dacă ek 1; ak1
1
„
„ ak1 modifică produsul pk1 
„
„ k w
p
„ k ' 0, dacă ek 0; ak1
0
„
„ pk1 , 1 nu modifică produsul pk1 
„
„
„actualizări k ' 0:
„
„
„
„
„a ak1 , k ' 0;
2
„
„
„
„
k
„nk nk1 ©2 k ' 0; şi nk1 j 0 desigur ! 
„
€
APPENDIX G. EXPONENŢIERE RAPIDĂ G.4. CODUL 552

Observaţia 9. Instrucţiunile care sunt ”ı̂n plus” se pot elimina. Codul următor arată acest lucru:

Listing G.4.3: exponentiere rapida3.cpp


1 #include<iostream> // actualizare p inainte de actualizare a si n
2 // ... si simplificarea codului ...
3
4 using namespace std;
5
6 int exponentiere_rapida(int a, int n) // p=aˆn
7 {
8 int nk;
9 int ak;
10 int ek;
11 int pk;
12
13 nk=n;
14 ak=a;
15 pk=1;
16
17 while(nk>0)
18 {
19 ek=nk%2;
20 if(ek==1)
21 pk=pk*ak;
22 else
23 pk=pk; // nu are rost ... !!!
24 ak=ak*ak;
25 nk=nk/2;
26 }
27
28 return pk;
29 }
30
31 int main()
32 {
33 int a=2;
34 //int n=234; // aˆn = prea mare !!!
35 //int n=30;
36 int n=6; // par: 2ˆ6 = 64 ... usor de verificat!
37 //int n=5; // impar: 2ˆ5 = 32 ... usor de verificat!
38
39 int rez=exponentiere_rapida(a,n);
40
41 cout<<a<<"ˆ"<<n<<" = "<<rez<<"\n";
42
43 return 0;
44 }
45 /*
46 2ˆ30 = 1073741824
47
48 Process returned 0 (0x0) execution time : 0.076 s
49 Press any key to continue.
50 */

Observaţia 10. Produsul poate deveni foarte mare şi din cauza asta se cere rezultatul modulo un
număr prim. Codul următor arată acest lucru:

Listing G.4.4: exponentiere rapida MOD.cpp


1 #include<iostream>
2
3 using namespace std;
4
5 int MOD=1000;
6 int nropr; // numar operatii (inmultiri) la exponentiere rapida
7
8 int exponentiere_rapida(int a, int n) // p=aˆn
9 {
10 int p = 1;
11 while(n>0)
12 {
13 if(n % 2 == 1) p = (p * a)%MOD;
APPENDIX G. EXPONENŢIERE RAPIDĂ G.5. CHIAR ESTE RAPIDĂ? 553

14 a = (a * a)%MOD;
15 n = n / 2;
16 nropr=nropr+6; // n%2, p*a, a*a, (a * a)%MOD si n/2
17 }
18 return p;
19 }
20
21 int main()
22 {
23 int a=1234;
24 int n=1e+9; // 10ˆ9
25 int rezn; // rezultat exponentiere naiva
26
27 rezn=exponentiere_rapida(a,n);
28
29 cout<<"rezr = "<<rezn<<" nropr = "<<nropr<<"\n";
30
31 return 0;
32 }
33 /*
34 rezr = 376 nropr = 180
35
36 Process returned 0 (0x0) execution time : 0.021 s
37 Press any key to continue.
38 */

G.5 Chiar este rapidă?


De ce se numeşte ”rapidă”?
1 000 000 000
Să presupunem că vrem să calculăm 1234 şi pentru că rezultatul are foarte multe
cifre ... ne vom mulţumi, la fiecare calcul, cu ultimele 3 cifre!
Aceasta este metoda ”naivă”:

Listing G.5.1: exponentiere naiva MOD.cpp


1 #include<iostream>
2
3 using namespace std;
4
5 int MOD=1000;
6 int nropn=0; // numar operatii (inmultiri) la exponentiere naiva
7
8 int exponentiere_naiva(int a, int n) // p=aˆn
9 {
10 int p = 1;
11 for(int k=1; k<=n; k++)
12 {
13 p=(p*a)%MOD;
14 nropn=nropn+1;
15 }
16 return p;
17 }
18
19 int main()
20 {
21 int a=1234;
22 int n=1e+9; // 10ˆ9
23 int rezn; // rezultat exponentiere naiva
24
25 rezn=exponentiere_naiva(a,n);
26
27 cout<<"rezn = "<<rezn<<" nropn = "<<nropn<<"\n";
28
29 return 0;
30 }
31 /*
32 rezn = 376 nropn = 2000000000
33
34 Process returned 0 (0x0) execution time : 21.239 s
35 Press any key to continue.
36 */
APPENDIX G. EXPONENŢIERE RAPIDĂ G.6. REZUMAT INTUITIV! 554

Iar aceasta este metoda ”rapidă”:

Listing G.5.2: exponentiere rapida MOD.cpp


1 #include<iostream>
2
3 using namespace std;
4
5 int MOD=1000;
6 int nropr; // numar operatii (inmultiri) la exponentiere rapida
7
8 int exponentiere_rapida(int a, int n) // p=aˆn
9 {
10 int p = 1;
11 while(n>0)
12 {
13 if(n % 2 == 1) p = (p * a)%MOD;
14 a = (a * a)%MOD;
15 n = n / 2;
16 nropr=nropr+6; // n%2, p*a, ..%MODa*a, (a * a) ... %MOD si n/2
17 }
18 return p;
19 }
20
21 int main()
22 {
23 int a=1234;
24 int n=1e+9; // 10ˆ9
25 int rezn; // rezultat exponentiere naiva
26
27 rezn=exponentiere_rapida(a,n);
28
29 cout<<"rezr = "<<rezn<<" nropr = "<<nropr<<"\n";
30
31 return 0;
32 }
33 /*
34 rezr = 376 nropr = 180
35
36 Process returned 0 (0x0) execution time : 0.021 s
37 Press any key to continue.*/

Numărul de operaţii:
ˆ cu metoda naivă acest număr este 2 000 000 000
ˆ cu metoda rapidă este 180.

Timpul de execuţie (pe calculatorul pe care am testat eu!):


ˆ cu metoda naivă este 21.239 secunde
ˆ cu metoda rapidă este 0.021 secunde
deci ... cam de 1000 de ori mai rapidă!

Iar ca număr de operaţii ... una este să faci 2 miliarde de operaţii şi alta este să faci
numai 180 de operaţii de acelaşi tip (de fapt sunt numai 30 de paşi dar la fiecare pas se fac 5
sau 6 operaţii aritmetice)!

G.6 Rezumat intuitiv!


Revenind la relaţia
234 128 1 64 1 32 1 16 0 8 1 4 0 2 1 1 0
a a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a  ˜ a 

să privim cu atenţie ce este aici!


Putem calcula uşor acest produs de la dreapta spre stânga!
2 4 8 16 32 64 128
Secvenţa a , a , a , a , a , a , a se poate genera cu instrucţiunea a=a*a;
Puterile expresiilor (marcate cu roşu, cele care sunt ı̂ntre paranteze) se obţin (tot de la dreapta
spre stânga) prin ı̂mpărţiri succesive ale lui n la 2 şi preluând restul. Dacă restul este 0 atunci
0
puterea respectivă este 0 iar ... 1 ... deci, nu mai apare ı̂n produs!
APPENDIX G. EXPONENŢIERE RAPIDĂ G.6. REZUMAT INTUITIV! 555

Asta este tot!


Deci, secvenţa de cod este:

Listing G.6.1: secventa cod.cpp


1 int p = 1;
2 while(n>0)
3 {
4 if(n % 2 == 1) p = (p * a);
5 a = a * a;
6 n = n / 2;
7 }

144
şi nu mai trebuie ”atâtea formule matematice”!

144
Este o glumă!
Appendix H

Căutare binară

H.1 Mijlocul = ?
Care este poziţia (indicele) ”mijlocului” unei zone dintr-un vector?

Figura H.1: căutare binară: mijlocul zonei ı̂n vector

În figura H.1 sunt trei formule pentru determinarea mijlocului unei zone [i1, i2] din vector:
ˆ mij1 = (i1+i2)/2
ˆ mij2 = i1+(i2-i1)/2
ˆ mijdr = (i1+i2+1)/2
Toate trei dau rezultat corect dacă secvenţa [i1, ..., i2] are un număr impar de elemente.
Când secvenţa [i1, ..., i2] are un număr par de elemente apar două elemente la mijloc ... şi ...
primele două formule dau ”mijlocul din stânga” iar a treia formulă dă ”mijlocul din dreapta”.
Atunci când căutăm o valoare ı̂ntr-un vector avem la dispoziţie căutarea secvenţială. Dacă
ştim că vectorul este sortat (şir crescător de numere) atunci este bine să folosim căutarea binară
pentru că aceasta ”găseste” numărul căutat (sau ”spune” că acel număr nu este ı̂n şir)
ˆ ı̂n 10 paşi dacă şirul are 1000 de elemnte (de fapt 1024 dar ... aproximăm!),
ˆ ı̂n 20 de paşi dacă şirul are 1,000,000 de elemente,
ˆ ı̂n 30 de paşi dacă şirul are 1,000,000,000 de elemente,
ˆ ...
Explicaţia simplă este că noi tot ı̂njumătăţim intervalul de căutare până când acest interval
rămâne cu un singur element ... şi vedem dacă acel element este cel căutat (dacă elementul rămas
nu este cel căutat ... ı̂nseamnă că acel element ”căutat” nu există ı̂n şir).
Putem să ne gândim la drumul invers: plecăm de la o secvenţă cu un singur element şi dublăm
secvenţa (invers faţă de cum am făcut ı̂njumătăţind secvenţa) până ajungem la o secvenţă care
are atâtea elemente câte a avut şirul iniţial (ne oprim imediat dacă am depăşit acel număr!).
Lungimile secvenţelor sunt: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, ...

Observăm că ı̂n 10 paşi, plecând din 1, ajungem să depăşim 1000.

556
APPENDIX H. CĂUTARE BINARĂ H.2. POZIŢIE OARECARE 557

H.2 Poziţie oarecare


H.2.1 Varianta iterativă

Listing H.2.1: cautare binara-v1-iterativ.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste o pozitie oarecare din secventa x...x...x (a doua din stanga!)
3 // daca x nu exista --> v[dr] < x < v[st] unde dr=st-1 (initial st < dr)
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19 // ------------------------------------------------------------------
20 bool cautareBinaraIter1(int x, int v[], int st, int dr)
21 {
22 int mij;
23
24 while(st <= dr)
25 {
26 nrc++;
27 mij=(st+dr)/2; // actualizez zona de cautare
28 cout<<"caut "<<x<<" in ";
29 afisv(v,st,dr);
30 cout<<"\n\n";
31
32 if(x == v[mij])
33 {
34 cout<<"am gasit "<<x<<" pe pozitia mij = "<<mij
35 <<" st="<<st<<" dr="<<dr<<"\n";
36 return true;// l-a gasit pe x
37 }
38 else // aici x != v[mij] ...
39 {
40 if(x < v[mij])
41 dr=mij-1; // caut in stanga lui mij, st ramane neschimbat
42 else
43 st=mij+1; // caut in dreapta lui mij, dr ramane neschimbat
44 }
45
46 cout<<"v["<<st<<"] = "<<v[st]<<" v["<<dr<<"] = "<<v[dr]<<endl;
47 }
48
49 if(st>dr)
50 {
51 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
52 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
53 //return;
54 return false;
55 }
56
57 return true; // sau return false; ... oricum nu ajunge aici ... !!!
58 }
59 // ------------------------------------------------------------------
60
61 int main()
62 {
63 int x; // caut x in vectorul a[]
64
65 //x=1; // secventa pe primele pozitii
66 x=2; // nu exista 2 ... ultima cautare in (dr,st)
67 //x=3; // exista numai un 3
68 //x=4; // exista secventa de 4
69 //x=9; // secventa pe ultimele pozitii
APPENDIX H. CĂUTARE BINARĂ H.2. POZIŢIE OARECARE 558

70
71 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
72
73 int n=sizeof(a)/sizeof(int);
74
75 cout<<"prima pozitie in vector = 0\n";
76 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
77
78 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
79
80 cout<<endl;
81 cout<<"nr cautari = "<<nrc<<"\n";
82
83 return 0;
84 }

H.2.2 Varianta recursivă

Listing H.2.2: cautare binara-v1-recursiv.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste o pozitie oarecare din secventa x...x...x (a doua din stanga!)
3 // daca x nu exista --> v[dr] < x < v[st] unde dr=st-1 (initial st < dr)
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19 // ------------------------------------------------------------------
20 void cautareBinaraRec1(int x, int v[], int st, int dr)
21 {
22 nrc++; // o noua cautare,
23
24 cout<<"caut "<<x<<" in ";
25 afisv(v,st,dr);
26 cout<<"\n\n";
27
28 int mij=(st+dr)/2;
29
30 if(st>dr)
31 {
32 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
33 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
34 return;
35 //return false;
36 }
37
38 if(x == v[mij])
39 {
40 cout<<"am gasit "<<x<<" pe pozitia "<<mij<<"\n";
41 return;
42 //return true;
43 }
44 else // mai caut
45 {
46 if(x < v[mij])
47 cautareBinaraRec1(x,v,st,mij-1); // caut in stanga lui mij
48 else
49 cautareBinaraRec1(x,v,mij+1,dr); // caut in dreapta lui mij
50 }
51
52 return;
53 }
54 // ------------------------------------------------------------------
APPENDIX H. CĂUTARE BINARĂ H.3. POZIŢIA DIN STÂNGA 559

55 int main()
56 {
57 int x; // caut x in vectorul a[]
58
59 //x=1; // secventa pe primele pozitii
60 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
61 //x=3; // exista numai un 3
62 //x=4; // exista secventa de 4
63 //x=9; // secventa pe ultimele pozitii
64
65 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
66
67 int n=sizeof(a)/sizeof(int);
68
69 cout<<"prima pozitie in vector = 0\n";
70 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
71
72 cautareBinaraRec1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
73
74 cout<<endl;
75 cout<<"nr cautari = "<<nrc<<"\n";
76
77 return 0;
78 }

H.3 Poziţia din stânga


H.3.1 Varianta iterativă

Listing H.3.1: cautare binara-v2-iterativ.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din stanga din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre dreapta cu v[st] > x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19 // ------------------------------------------------------------------
20 bool cautareBinaraIter1(int x, int v[], int st, int dr)
21 {
22 int mij;
23
24 while(st != dr)
25 {
26 nrc++;
27 mij=(st+dr)/2; // actualizez zona de cautare
28
29 cout<<"caut "<<x<<" in ";
30 afisv(v,st,dr);
31 cout<<"\n\n";
32
33 if(x <= v[mij])
34 dr=mij; // caut in stanga
35 else
36 st=mij+1; // caut in dreapta
37
38
39
40 cout<<"v["<<st<<"] = "<<v[st]<<" v["<<dr<<"] = "<<v[dr]<<endl;
41 }
42
APPENDIX H. CĂUTARE BINARĂ H.3. POZIŢIA DIN STÂNGA 560

43 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
44 {
45 if(v[st]==x)
46 {
47 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
48
49 return true;
50 }
51
52 else
53 {
54 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
55 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
56
57 return false;
58 }
59 }
60
61 return true; // sau return false; ... oricum nu ajunge aici ... !!!
62 }
63
64 // ------------------------------------------------------------------
65
66 int main()
67 {
68 int x; // caut x in vectorul a[]
69
70 //x=1; // secventa pe primele pozitii
71 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
72 //x=3; // exista numai un 3
73 //x=4; // exista secventa de 4
74 //x=9; // secventa pe ultimele pozitii
75
76 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
77
78 int n=sizeof(a)/sizeof(int);
79
80 cout<<"prima pozitie in vector = 0\n";
81 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
82
83 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
84
85 cout<<endl;
86 cout<<"nr cautari = "<<nrc<<"\n";
87
88 return 0;
89 }

H.3.2 Varianta recursivă

Listing H.3.2: cautare binara-v2-recursiv.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din stanga din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre dreapta cu v[st] > x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 // ------------------------------------------------------------------
12
13 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
14 {
15 int i;
16 for(i=st; i<=dr; i++)
17 {
18 cout<<v[i]<<" ";
19 }
20 }
21
22 // ------------------------------------------------------------------
APPENDIX H. CĂUTARE BINARĂ H.4. POZIŢIA DIN DREAPTA 561

23 void cautareBinaraRec2(int x, int v[], int st, int dr)


24 {
25 nrc++; // o noua cautare,
26 cout<<"nrc = "<<nrc<<": caut "<<x<<" in zona indicilor "
27 <<st<<" ... "<<dr<<" : "<<endl;
28 afisv(v,st,dr);
29 cout<<"\n\n";
30 //getchar(); // while si recursivitatea ... pot "scapa" de sub control!
31
32 int mij=(st+dr)/2;
33
34 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
35 {
36 if(v[st]==x)
37 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
38 else
39 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
40 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
41 return;
42 //return false;
43 }
44
45 // mai caut
46 if(x <= v[mij])
47 cautareBinaraRec2(x,v,st,mij); // caut in stanga
48 else// aici x > v[mij]
49 cautareBinaraRec2(x,v,mij+1,dr); // caut in dreapta
50
51 return; // oricum nu ajunge aici !
52 }
53
54 // ------------------------------------------------------------------
55
56 int main()
57 {
58 int x; // caut x in vectorul a[]
59
60 //x=1; // secventa pe primele pozitii
61 //x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
62 //x=3; // exista numai un 3
63 x=4; // exista secventa de 4
64 //x=9; // secventa pe ultimele pozitii
65
66 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
67 int n=sizeof(a)/sizeof(int);
68
69 cout<<"prima pozitie in vector = 0\n";
70 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
71
72 cautareBinaraRec2(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
73
74 cout<<endl;
75 cout<<"nr cautari = "<<nrc<<"\n";
76
77 return 0;
78 }

H.4 Poziţia din dreapta


H.4.1 Varianta iterativă

Listing H.4.1: cautare binara-v3-iterativ.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din dreapta din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre stanga cu v[st] < x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
APPENDIX H. CĂUTARE BINARĂ H.4. POZIŢIA DIN DREAPTA 562

11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++)
15 {
16 cout<<v[i]<<" ";
17 }
18 }
19
20 // ------------------------------------------------------------------
21
22 bool cautareBinaraIter1(int x, int v[], int st, int dr)
23 {
24 int mij;
25
26 while(st != dr)
27 {
28 nrc++;
29 mij=(st+dr+1)/2; // actualizez zona de cautare
30
31 cout<<"caut "<<x<<" in ";
32 afisv(v,st,dr);
33 cout<<"\n\n";
34
35 if(x >= v[mij])
36 st=mij; // caut in dreapta
37 else
38 dr=mij-1; // caut in stanga
39
40 cout<<"v["<<st<<"] = "<<v[st]<<" v["<<dr<<"] = "<<v[dr]<<endl;
41 //getchar();
42 }
43
44 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
45 {
46 if(v[st]==x)
47 {
48 cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
49 return true;
50 }
51 else
52 {
53 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
54 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
55 return false;
56 }
57 }
58
59 return true; // sau return false; ... oricum nu ajunge aici ... !!!
60 }
61 // ------------------------------------------------------------------
62
63 int main()
64 {
65 int x; // caut x in vectorul a[]
66
67 //x=1; // secventa pe primele pozitii
68 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
69 //x=3; // exista numai un 3
70 //x=4; // exista secventa de 4
71 //x=9; // secventa pe ultimele pozitii
72
73 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
74 int n=sizeof(a)/sizeof(int);
75
76 cout<<"prima pozitie in vector = 0\n";
77 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
78
79 cautareBinaraIter1(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
80
81 cout<<endl;
82 cout<<"nr cautari = "<<nrc<<"\n";
83
84 return 0;
85 }
APPENDIX H. CĂUTARE BINARĂ H.4. POZIŢIA DIN DREAPTA 563

H.4.2 Varianta recursivă

Listing H.4.2: cautare binara-v3-recursiv.cpp


1 // are rost NUMAI daca vectorul este SORTAT (aici crescator ... !!!)
2 // gaseste pozitia din stanga din secventa x...x...x
3 // daca x nu exista --> st = dr = prima pozitie spre stanga cu v[st] < x
4
5 #include<iostream>
6
7 using namespace std;
8
9 int nrc=0; // numarul de cautari
10
11 void afisv(int v[], int st, int dr) // afisez v[] intre st si dr
12 {
13 int i;
14 for(i=st; i<=dr; i++) { cout<<v[i]<<" "; }
15 }
16
17 // ------------------------------------------------------------------
18
19 void cautareBinaraRec2(int x, int v[], int st, int dr)
20 {
21 nrc++; // o noua cautare,
22 cout<<"nrc = "<<nrc<<": caut "<<x<<" in zona indicilor "
23 <<st<<" ... "<<dr<<" : "<<endl;
24 afisv(v,st,dr);
25 cout<<"\n\n";
26 //getchar(); // while si recursivitatea ... pot "scapa" de sub control!
27
28 int mij=(st+dr+1)/2;
29 if(st==dr) // am terminat (gasit cel mai din stanga sau nu exista deloc)
30 {
31 if(v[st]==x) cout<<"am gasit "<<x<<" pe pozitia "<<st<<"\n";
32 else
33 cout<<x<<" nu exista in vector ...!"<<" st="<<st<<" dr="<<dr
34 <<" v[st]="<<v[st]<<" v[dr]="<<v[dr]<<"\n";
35 return;
36 //return false;
37 }
38
39 // mai caut
40 if(x >= v[mij])
41 cautareBinaraRec2(x,v,mij,dr); // caut in dreapta
42 else// aici x < v[mij]
43 cautareBinaraRec2(x,v,st,mij-1); // caut in stanga
44
45 return; // oricum nu ajunge aici !
46 }
47 // ------------------------------------------------------------------
48 int main()
49 {
50 int x; // caut x in vectorul a[]
51
52 //x=1; // secventa pe primele pozitii
53 x=2; // nu exista 2 ... ultima cautare in (dr,st) dr=st-1
54 //x=3; // exista numai un 3
55 //x=4; // exista secventa de 4
56 //x=9; // secventa pe ultimele pozitii
57
58 int a[]={1, 1, 1, 3, 3, 4, 4, 4, 4, 4, 4, 4, 7, 7, 8, 8, 9, 9, 9, 9};
59 int n=sizeof(a)/sizeof(int);
60
61 cout<<"prima pozitie in vector = 0\n";
62 cout<<"ultima pozitie in vector = "<<n-1<<endl<<endl;
63
64 cautareBinaraRec2(x,a,0,n-1); // caut x in vectorul a[] de dimensiune=n
65
66 cout<<endl;
67 cout<<"nr cautari = "<<nrc<<"\n";
68
69 return 0;
70 }
Index

15
10 , 443 auto &, 312
18
10 , 281
 bit inversion, 236 backtracking, 445, 467
((1 $$ n)-1)ˆmask, 312 Be careful
(c ˆ 48), 213 Array bounds, 305
(void)0;, 316 Integer overflows, 305
(x $$ 3) + (x $$ 1), 213 Special cases, 305
(x & (-x)), 213 begin(), 139
(x$$1LL)+(x$$3LL)+ch-’0’, 316 begin()), 151
*–, 205 best practice, 26
*max element, 125 biţi, 216
*set.lower bound(...), 205 Big Endian, 51
? :, 52 binary indexed tree, 160
[&], 125 binary lifting, 135, 178
#define, 30, 125, 151 binary representation, 217
#define GNU SOURCE, 423 binary search, 129, 167, 168, 432
asprintf, 423, 430 bipartite graph
#include, 30 matching, 134
#include ”testlib.h”, 257 bit, 36, 172, 216
#pragma GCC, 151 BIT - binary indexed trees, 160
&, 245, 312 bit mask, 172
&..., 427 bitmask, 445
&&, 312 bits, 241
&&..., 427 bitset, 422
ˆ, 251 bitwise and—hyperpage, 172
ˆ=, 245, 251 bool, 131, 139
builtin ctzll, 465 cmp, 205
gcd, 364 bool cmp(int x, int y), 215
şablonul final, 31 boolean
0x, 46 variable, 134
0x3f3f3f3f, 213, 316, 427 bottom-up, 4
0x3f3f3f3f3f3f3f3fll, 427 breadth first search, 172
0x7fffffff, 427 breath-first search, 266
2D dynamic programming, 204 brute force, 146
32 million operations, 444 brute-force, 174, 293, 445
32-bit integer, 241 bubble sort, 216
byte, 36
a$$1 ¶ 1, 251
AC - Accepted Code, 154 C++, 7
accumulate(a.begin(), a.end(), 0LL);, 143 C++ Operator Precedence, 205
alfabet, 39 căutare binară, 556
analogie cu baza 10, 3, 10 căutare secvenţială, 556
ancestor, 178, 453 caracter, 39
arbore, 132 cerr, 264, 274
de acoperire, 132 chain, 486
array, 125 char*, 423, 430
ascending order, 177 cicle, 226
ASCII, 32 CINOR, vi
auto, 125, 131, 138, 207, 275, 297 class, 394

564
INDEX INDEX 565

clear(), 264, 371, 398 f ¶= ch==’-’;, 316


clear();, 138 f[1$$22];, 316
clock(), 366 faster as runtime, 445
CLOCKS PER SEC, 366 fastIO
cod complementar, 16 buf size = 4096;, 482
cod OK1, 351, 358, 489 flusher, 477, 482
cod OK2, 354, 356, 490, 496 getChar(), 477, 482
compilare, 29 readChar(), 477, 482
concat, 423, 430 readInt(), 477, 482
connected components, 259 writeChar( int x ), 477, 482
const, 125 writeInt( T x, char end ), 477, 482
double, 131 writeWord( const char *s ), 477, 482
int, 131 Fenwick trees, 243
const auto&, 394 final state, 141
const vector¡int¿&, 394 find(a[i]), 151
const vector¡long long¿ &a, 143 flush, 127
const void*, 423, 430 for (char c : s), 422
constexpr, 422 friend bool operator, 208
coprime, 444 from memory point of view, 444
count, 125, 427, 465
gcd, 453
counting sort, 123
getchar(), 213
cout.precision(10);, 357
global array, 123
crescător, 215
graf, 126
CTE - Compile Time Error, 154
conex, 132
cum vrea, 26
neorientat, 132
cycle, 134
graph, 177, 226, 259
data structure bipartite, 134
map, 419 connected, 359
decompose(), 456 directed, 388
decreasing order, 174 edge, 388
define, 131 undirected, 359
degree, 129 vertex, 388
dfs, 131, 138, 264, 341, 342, 351, 357, 487, Gray code, 164
489 greedily, 122
dfs(int u, int from), 495 greedy, 134, 135, 325
Dijkstra, 272 greedy approach, 174
with priority queue, 266 Hall’s marriage theorem, 134
Min Heap, 280 Hamiltonian cycle, 467
directed graph, 266 Hamiltonian path, 467
directed tree, 177 hash, 419, 465
disjoint set, 175 hash map, 123
dp table, 154 hashmap, 123
DP transition, 308 heavy-light decomposition, 135
drum ı̂n arbore, 126
DSU, 350, 388 i$$1, 251
dynamic programming, 134, 154, 228, 229, I.d.k.:
285, 293, 308, 325, 444, 486 I don’t know who the author is., i, v
dynamic programming table, 154 indentare, 26
induction, 122, 147
edge, 266 inline bool, 427
emplace back, 366 insert, 138
empty(), 138, 151, 398 insert(i), 151
end(), 139, 151 instrucţiuni, 26
EOF, 211 break, 54
erase case, 52
unique, 427 continue;, 54
ErdősSzekeres theorem, 146 do, 53
euler cycle, 388 for, 52
exclusive or (XOR), 241 getchar();, 54
expo(...), 235 if, 52
INDEX INDEX 566

ifstream, 31 muchii ponderate, 132


ofstream, 31 multiset, 501
system(“PAUSE”);, 54
while, 53 nedescrescător, 215
int mod=1e9+7;, 235 newline, 127
int64 t, 238, 489 nod ı̂n arbore, 126
interactor neadaptiv, 127 NULL, 423, 430
inversed permutation, 388 număr compus, 63
ios base::sync with stdio(0);, 241 număr prim, 63
iota, 125, 356 number of states, 445
ismillerrabinprime, 456 numere naturale, 63
it.first, 151 numeric limits, 295, 312
it.second, 151
octet, 36
Java, 7 output, 127

p, 175
Kruskal, 134
p==1 ? solve1() : solve2();, 316
pair, 264, 371, 473, 478
lambda, 268, 297
pair of nodes, 129
LCA, 129
partial sums, 160
LCA algorithm, 178
partition, 175
lead, 129
partitions, 309
letters are replaced with numbers, 419
Pascal, 7
Liceul Militar Dimitrie Cantemir, vi
pattern, 122
limbajul de programare C, 39
periodic, 154
limbajul de programare C++, 39
permutare, 132, 144, 166
linear search, 308
permutation, 387
linear time, 123
Pi = acos(-1.);, 131
Little Endian, 51
pigeonhole principle, 123
lizibil, 26
pointer, 502
long double, 138
Pollard method, 453
long long, 138
pop back(), 398
Longest Common Subsequence, 204
pow mod, 464
Longest Increasing Subsequence, 204
mul mod, 464
lowbit, 215
precalculated, 285, 308
lower bound, 394, 398, 427
precalculation, 175
LSB, 51
precomputation, 123
LSB (least-significant byte), 51
precompute, 178, 418
precomputed, 204
map, 123, 125, 155, 278, 398, 453, 458, 461
map $vector$int%, int%, 445
prefix, 129
map$int, int%, 151
prefix sums, 418
preprocessing, 146, 177
matrice, 172
priority queue, 175
matrix, 264
key-value pair, 175
matrix bpow(matrix a, ll st), 470
priority queue, 268, 269, 272, 275, 277
matrix mul(matrix a, matrix b), 470
push back, 264, 371
max, 138
push back(*pt), 151
max element, 221, 491
push back(x), 143
memoization, 228, 229
putchar, 211
memory efficiency, 445
puteri ale lui 2, 4
memset, 264, 274, 302, 394, 427, 453
Python, 141
min, 125, 138
minimum spanning tree, 134 qsort, 423, 428
mst, 134 queue, 175
minstd rand, 364 front of queue, 175
modulo, 419 quickly find, 501
MSB, 50
MSB (most-significant byte), 50 r %% 1, 251
mt19937, 357, 398 r&1, 251
mt19937 rnd(time(0));, 151 rădăcina arborelui, 126
muchie ı̂n graf, 127 rbegin(), 264
INDEX INDEX 567

read(T &x), 213 strchr, 423, 430


read char(), 211 strdup, 423, 430
read int(), 211 string, 139, 274
readint(), 215 find, 274, 424
recursive procedure, 168 size(), 424
recursively, 141, 168 strlen, 423, 430
relax the value, 308 strong pseudo prime, 453
remainder, 417 struct, 139, 205, 208, 420, 465
rend(), 264 struct DSU, 151
resize, 356 subşir, 144, 153
unique, 398 subarray, 122
resize(n), 139, 371 subsequence, 155
resize(n);, 151 subset, 147
RMQ, 203 subtrees, 129
root, 177 suffix, 174
RTE - Run Time Error, 154 sum of powers of two, 282
swap, 125, 151, 272, 371
scanf(”%s”,s+1);, 420 sync with stdio(0);, 264
Segment tree, 203
segment tree, 135, 243, 285, 487 terms of time, 444
set, 155, 205, 207, 211, 213, 215, 274, 278, testlib.h, 218
371, 376 time limit, 444
erase, 274 TL - time limit, 154
insert, 205, 207, 211, 213, 215, 274, 371 TLE - Time Limit Exceed, 154
lower bound, 205, 215 top of the stack, 175, 177
rbegin(), 371 trage cu tunul, iv
upper bound, 205, 215 tree, 177, 359, 486
set a limit, 432 tuple, 147
set$int%, 151 two pointers technique, 309
set$pair$int,int%%, 138 typedef, 131, 151, 211
setmax, 489 pair, 125
setmin, 489 vector, 125
setw(8), 46 typename..., 427
shortest path, 266
show bytes in memory, 50 uniform int distribution, 364
shuffle, 364 unique, 394
sieve, 464 Universitatea Ovidius, vi
signed, 422 unordered map, 422, 427, 464, 465
sistem de numeraţie, 3 using, 151
size(), 264, 371
size t, 394 variabilă, 31
sizeof..., 427 variabile globale, 39
slow, 308 variabile locale, 39
sort, 125, 191, 205, 207, 264, 285, 309, 388, vector, 139, 207, 312
419, 421, 429 back(), 440
ascending order, 191 clear(), 440
greater$pair$int, int%%(), 207 erase, 441
lambda, 312, 394 insert, 371
sorted stack, 204 pair, 207
sorting, 123 push back, 357
speed up the solution, 142 struct, 440
stack, 175, 177 vector$int%, 138, 151
state, 444 vector$pair$int, int%% a, 151
state of the game, 141 vector$pair$int,int%%, 138
states, 325 vector$vector$int%%, 151, 371
std::hex, 46 vertices, 266
STL, 419
map, 502 WA - Wrong Answer, 154
priority queue, 502 whitespace, 32
set, 502
strămoş comun, 126 x%y ? x=y,1 : 0;, 316
Bibliografie

[1] Aho, A., Hopcroft, J., Ullman, J.D.; Data strutures and algorithms, Addison Wesley, 1983

[2] Andreica M.I.; Elemente de algoritmică - probleme şi soluţii, Cibernetica MC, 2011
[3] Andonie R., Gârbacea I.; Algoritmi fundamentali, o perspectivă C++, Ed. Libris, 1995
[4] Atanasiu, A.; Concursuri de informatică. Editura Petrion, 1995
[5] Bell D., Perr M.; Java for Students, Second Edition, Prentice Hall, 1999

[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, Bu-
cureş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.

568
BIBLIOGRAFIE BIBLIOGRAFIE 569

[26] Odăgescu, I., Smeureanu, I., Ştefănescu, I.; Programarea avansată a calculatoarelor person-
ale, 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 Peda-
gogică, Bucureşti, 1982
[37] Tudor, S.; Informatică - profilul 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 Filosofie 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 Pub-
lishing 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 BIBLIOGRAFIE 570

[55] *** - https://vdocumente.com/algoritmi-i-structuri-de-date.html


[56] *** - https://pdfslide.net/documents/algoritmi-si-structuri-de-dat
e-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_alternativ
e
[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
[83] *** - https://cs.pub.ro/images/NewsLabsImages/Teste-admitere-informa
tica-UPB-2020.pdf
[84] *** - https://www.viitoriolimpici.ro/

[85] *** - https://oni2023.ro/blog/


Lista autorilor
problemelor şi indicaţiilor

Andrei Alexandru, 232 Lucian Bicsi, 172


Anton Trygub, 128, 141, 146, 154
Maria-Alexa Tudose, 167
Constantin Tudor, vi Matvii Aslandukov, 134
Mihai Bunget, 160
Eugen Nodea, 174

Gheorghe Dodescu, vi Pop Ioan Cristian, 164

Ihor Barenblat, 134 Ray Bai, 128, 141


Ion Văduva, vi Roman Bilyi, 128, 141
Ionel-Vasile Piţ-Rada, 161
Tamio-Vesa Nakajima, 168, 172
Jeroen Op de Beek, 122 Theodor-Gabriel Tulba-Lecu, 164, 172

571
What’s the next?
ORNL’s Frontier First to Break the Exaflop Ceiling

H T T P S :// E N . W I K I P E D I A . O R G / W I K I /F R O N T I E R _( S U P E R C O M P U T E R )#/ M E D I A /F I L E :F R O N T I E R _S U P E R C O M P U T E R _(2). J P G

Over time
the following steps
will lead you to the value
you seek for yourself
now!

S-ar putea să vă placă și