Sunteți pe pagina 1din 543

Informatică

Gimnaziu + EJOI

2023-4
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-4-26
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”
That is because ...
5
”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
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!.
6
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
7 8
dificilă! :-) Vezi, de exemplu, IOI2020 şi IOI2019 , IOI2015 .
Cred că, de cele mai multe ori, este foarte greu să gândim ”simplu” şi să nu ”ne complicăm”
atunci când cautăm o rezolvare pentru o problemă dată la olimpiadă. Acesta este motivul pentru
care vom analiza cu foarte mare atenţie atât exemplele date ı̂n enunţurile problemelor cât şi
”restricţiile” care apar acolo (ele sigur ”ascund” ceva interesant din punct de vedere al algoritmului
9
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
10
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!).


În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: ı̂n noiembrie 2017
11
ı̂n China, ı̂n noiembrie 2019 ı̂n SUA şi ı̂n iunie 2020 ı̂n Japonia. În iunie 2022 ”Frontier”
6
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’ ...
7
IOI2019 şi IOI2020 au a permis utilizarea limbajelor de programare C++ şi Java
8
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python şi Rubi (...)
9
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema ”avârcolaci” - ONI2014 clasa a 11-a
10
https://en.wikipedia.org/wiki/Computer
11
https://www.top500.org/lists/top500/2022/06/

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

Bistriţa, 26th April 2023 Adrian Răbâea


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

18
”I want to thank God most of all because without God I wouldn’t be able to do any of this.”

Bistriţa, 26th April 2023

Adrian R.

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

v
Despre autor

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

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


email: adrian1803@gmail.com
Lector universitar - Universitatea Tehnică din Cluj Napoca - Centrul Universitar Nord din Baia
Mare, Facultatea de Ştiinţe, Str. Victoriei, nr. 76, Baia Mare, România, (pensionat: 01.10.2018)
https://stiinte.utcluj.ro/departamente.html
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;
20
Titlul tezei: Algoritmi paraleli şi aplicaţii pe maşini virtual paralele
21
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
22
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ă
23
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-
24
matică, Departamentul de Informatică
25
- (1992-1979) Centrul de Informatică şi organizare CINOR, Bucureşti
26
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)
19
https://dmi.cunbm.utcluj.ro/?page_id=2
20
http://opac.biblioteca.ase.ro/opac/bibliographic_view/149021
21
http://www.ionivan.ro/2015-PERSONALITATI/Dodescu.htm
22
http://old.fmi.unibuc.ro/ro/prezentare/promotii/promotia1978informatica_10ani/
23
https://ro.wikipedia.org/wiki/Ion_V%C4%83duva
24
https://fmi.univ-ovidius.ro/
25
https://www.cinor.ro/index.htm
26
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
https://link.springer.com/article/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
convolutional networks in social media; Electronic Commerce Research and Applications,
2022, Volume 55, Pages 101191-101207; DOI: https://doi.org/10.1016/j.elerap.2022.101191
https://www.sciencedirect.com/science/article/abs/pii/S1567422322000758

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 https://www.anstuocmath.ro/volume-xxix-2021-
fascicola-3.html

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 https://www.anstuocmath.ro/volume-xxvi-2018-fascicola-2.html

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


Anti-plane crack in human bone. I. Mathematical modelling; Analele Ştiinţifice ale Uni-
versităţii Ovidius Constanţa - Seria Matematică, Volume 26, Issue 1, 2018, Pages 81-90;
DOI: https://doi.org/10.2478/auom-2018-0004 https://www.anstuocmath.ro/volume-xxvi-2018-
fascicola-1-1.html

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


Mathematical modeling of three equal collinear cracks in an orthotropic
solid; Meccanica 51, 329339 (2016); https://doi.org/10.1007/s11012-015-0254-5
https://link.springer.com/article/10.1007/s11012-015-0254-5#article-info

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


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

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


Mathematical Aspects Regarding Cracks Behaviour in Wood Composites; Key Engineer-
ing Materials 601:108-111, March 2014; DOI: 10.4028/www.scientific.net/KEM.601.108
https://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

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 xiv

Lista tabelelor xvii

Lista programelor xviii

I Algoritmi şi programare 1


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
1.5.3 Cifre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5.4 ’Whitespace’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

2 Probleme elementare 21
2.1 Deplasarea cifrelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.1.1 Deplasarea cifrelor spre stânga . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.1.2 Deplasarea cifrelor spre dreapta . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2 Contor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.2 Mărire cu 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.3 Micşorare cu 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.4 Predecesor şi succesor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.3 Ordine lexicografică . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.3.1 Cel mai mic număr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.3.2 Cel mai mare număr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.3.3 Succesorul unui număr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

viii
2.3.4 Predecesorul unui număr . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

3 Programare C+++ 26
3.1 Structura unui program in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.1.1 Şablon simplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.1.2 namespace std; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1.3 Pregătire suport pentru fişiere . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1.4 Acces la fişiere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1.5 Citire şi scriere folosind fişiere . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.1.6 Comentarii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.1.7 Reprezentarea internă a datelor ı̂n fişierele .txt . . . . . . . . . . . . . . . . 39
3.1.8 Două citiri din fişier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.1.9 Câteva explicaţii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1.10 Variabilă neiniţializată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.1.11 Alegerea identificatorilor (numelor) . . . . . . . . . . . . . . . . . . . . . . . 44
3.2 Alfabetul limbajului . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.2.1 Un program util . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.2.2 Coduri de control şi spaţii . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.2.3 Caracterele printabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.2.4 Codurile alfanumerice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.2.5 Simboluri ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.3 Alocarea variabilelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.3.1 Puţină istorie! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.3.2 ’Big endian’ sau ’Little endian’ . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.3.3 Afişare ı̂n ’hexa’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.3.4 Afişare ı̂n ’binar’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.3.5 Alocarea variabilelor ı̂n spaţiul de memorie . . . . . . . . . . . . . . . . . . 52
3.3.6 MSB şi LSB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.4 Instrucţiuni de decizie - structuri elementare de control . . . . . . . . . . . . . . . 57
3.4.1 Instrucţiunea if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.4.2 Instrucţiunea case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.4.3 Operatorul condiţional (ternar) ? : . . . . . . . . . . . . . . . . . . . . . . 57
3.5 Instrucţiuni repetitive - structuri elementare de control . . . . . . . . . . . . . . . . 57
3.5.1 Instrucţiunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.5.2 Instrucţiunea while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.5.3 Instrucţiunea do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.6 Instrucţiuni de salt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.6.1 Instrucţiunea continue; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.6.2 Instrucţiunea break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.7 Funcţii utile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.7.1 getchar(); . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.7.2 system(“PAUSE”); . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.8 Tipuri predefinite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.8.1 Character types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.8.2 Numerical integer types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.8.3 Floating-point types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.8.4 Boolean type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

4 Masive de date ı̂n C++ 63


4.1 Vectori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.1.1 Implementare statică - array . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.1.2 Implementare dinamică - array . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.1.3 Implementare STL - vector¡***¿ . . . . . . . . . . . . . . . . . . . . . . . . 63
4.2 Şiruri de caractere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.2.1 Vector de caractere - array . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.2.2 Tipul ’string’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.3 Matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.1 Implementare statică - array . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.2 Implementare dinamică - array . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.3 Implementare STL - vector¡***¿ . . . . . . . . . . . . . . . . . . . . . . . . 65
4.4 Structuri complexe de tip vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.4.1 Stivă - ’Stacks’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.4.2 Coadă - ’Queues’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.4.3 Arbori binari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.4.4 Heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.5 Tipul ’struct’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.5.1 Vector-array de structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.5.2 Matrice-array de structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.5.3 STL vector¡struct¿ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.6 Alocarea ı̂n memorie pentru structurile ’mari’ . . . . . . . . . . . . . . . . . . . . . 66

5 Funcţii şi recursivitate 68


5.1 Funcţii - definite de utilizator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.2 Recursivitate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

II EJOI - The European Junior Olympiad in Informatics 69


6 EJOI 2022 70
6.1 adjacent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
6.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
6.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.2 root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
6.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.3 tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.4 game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.5 permutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.6 unfriendly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
6.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

7 EJOI 2021 108


7.1 AddK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
7.1.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
7.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.2 KPart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7.2.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
7.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
7.3 XCopy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
7.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
7.3.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
7.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
7.4 BinSearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
7.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
7.4.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
7.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
7.5 Dungeons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
7.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
7.5.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7.6 Waterfront . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
7.6.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
7.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

8 EJOI 2020 125


8.1 fountain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
8.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
8.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
8.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
8.2 triangulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
8.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
8.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
8.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.3 exam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
8.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
8.4 xorsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
8.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
8.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
8.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
8.5 cards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
8.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
8.5.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
8.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
8.6 game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
8.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
8.6.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
8.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178

9 EJOI 2019 179


9.1 Hanging rack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
9.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
9.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
9.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
9.2 XORanges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
9.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
9.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
9.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
9.3 T - Covering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
9.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
9.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
9.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
9.4 Adventure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
9.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
9.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
9.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
9.5 Tower . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
9.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
9.5.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
9.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
9.6 Colouring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
9.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
9.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
9.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
10 EJOI 2018 241
10.1 Hills . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
10.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
10.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
10.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
10.2 Passports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
10.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
10.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
10.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
10.3 AB-Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
10.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
10.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
10.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
10.4 Prime Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
10.4.1 *Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
10.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
10.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
10.5 Cycle sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
10.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
10.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
10.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365

11 EJOI 2017 366


11.1 Magic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
11.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
11.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
11.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
11.2 Particles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
11.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
11.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
11.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
11.3 Six . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
11.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
11.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
11.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
11.4 Camel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
11.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
11.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
11.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
11.5 Experience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
11.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
11.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
11.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
11.6 Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
11.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
11.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
11.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462

Appendix A Programa olimpiadei - gimnaziu 464


A.1 Clasa a V-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
A.2 Clasa a VI-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
A.3 Clasa a VII-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
A.4 Clasa a VIII-a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
A.5 Observaţie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
Appendix B ”Instalare” C++ 465
B.1 Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
B.1.1 Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
B.1.2 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
B.1.3 Utilizare Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
B.1.4 Setări Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
B.1.5 Multe surse ı̂n Code Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
B.2 winlibs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
B.2.1 GCC şi MinGW-w64 pentru Windows . . . . . . . . . . . . . . . . . . . . . 475
B.2.2 PATH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
B.2.3 CodeBlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478

Appendix C Exponenţiere rapidă 488


C.1 Analogie baza 2 cu baza 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
C.2 Notaţii, relaţii şi formule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
C.3 Pregătire pentru scrierea codului! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
C.4 Codul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
C.5 Chiar este rapidă? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
C.6 Rezumat intuitiv! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494

Appendix D Căutare binară 496


D.1 Mijlocul = ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
D.2 Poziţie oarecare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
D.2.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
D.2.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
D.3 Poziţia din stânga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
D.3.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
D.3.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
D.4 Poziţia din dreapta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
D.4.1 Varianta iterativă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
D.4.2 Varianta recursivă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503

Appendix E ”Vecini” ... 504


E.1 Direcţiile N, E, S, V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504

Appendix F Diagonale ı̂n matrice 506


F.1 Diagonale ı̂n matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
F.2 Simetric faţă de diagonale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
F.2.1 Simetric faţă de diagonala principală . . . . . . . . . . . . . . . . . . . . . . 507
F.2.2 Simetric faţă de diagonala secundară . . . . . . . . . . . . . . . . . . . . . . 507

Appendix G Parcurgere matrice 508


G.1 Parcurgere NE spre SV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508

Appendix H Interclasare 509


H.1 Interclasarea vectorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509

Index 510

Bibliografie 514

Lista autorilor 517


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 Determinare predecesor şi succesor - cazul 1 . . . . . . . . . . . . . . . . . . . . . . 23


2.2 Determinare predecesor şi succesor - cazul 2 . . . . . . . . . . . . . . . . . . . . . . 23
2.3 Determinare predecesor şi succesor - cazul 3 . . . . . . . . . . . . . . . . . . . . . . 24

3.1 Cartele cu 80 de coloane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28


3.2 Maşină de ’scris’ cartele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.3 Bandă perforată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4 Cod sursă pe hârtie de imprimantă . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.5 Pachet cu cartele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.6 p01.cpp compilare (build) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.7 p01.cpp execuţie (run) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.8 p01.cpp rezultat execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.9 Style Allman (ANSI) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.10 Style Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.11 TAB size in spaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.12 Compiler warnings: [-Wall] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.13 Optimize even more (for speed) [-O2] . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.14 p05 cod sursă şi fereastra de execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.15 Fişierul cu datele de intrare: f05.in . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

xiv
3.16 f05.in vizualizat cu WinHex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.17 fişierul cu datele de ieşire f05.out şi rezultatul afişat pe ecran . . . . . . . . . . . . 38
3.18 fişierul f05.in ı̂n WinHex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.19 Dimensiuni fişiere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.20 Execuţie p05a.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.21 Alocarea spaţiului de memorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.22 Poziţia capului de citire după prima citire . . . . . . . . . . . . . . . . . . . . . . . 42
3.23 Poziţia capului de citire după a doua citire . . . . . . . . . . . . . . . . . . . . . . 43
3.24 Poziţia capului de citire după a treia citire . . . . . . . . . . . . . . . . . . . . . . . 43
3.25 Execuţie p05a.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.26 isctrl+isspace+isblank . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.27 isprint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.28 isalnum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.29 simboluri ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.30 Big or Little endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.31 Big si Little endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.32 Afişare hexa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.33 Afişare binar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.34 Plimbare prin biţi! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.35 MSB+LSB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.36 MSB endian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.37 LSBendian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.38 LSBendian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

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


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

C.1 Analogie B2 cu B10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488

D.1 căutare binară: mijlocul zonei ı̂n vector . . . . . . . . . . . . . . . . . . . . . . . . 496

E.1 ”vecini” ı̂n matrice şi sistem Oxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504


E.2 Toţi ”pereţii” sunt la N sau la V ı̂n matricea ”bordată” . . . . . . . . . . . . . . . 504
E.3 Toţi ”pereţii” sunt codificaţi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505

F.1 diagonale ı̂n matrice - start = 0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506


F.2 diagonale ı̂n matrice - start = 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
F.3 simetric faţă de diagonala principală . . . . . . . . . . . . . . . . . . . . . . . . . . 507
F.4 simetric faţă de diagonala secundară . . . . . . . . . . . . . . . . . . . . . . . . . . 507

G.1 Interclasarea vectorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508

H.1 Interclasarea vectorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509


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

2.1 Deplasare spre stânga cu 1 poziţie . . . . . . . . . . . . . . . . . . . . . . . . . . . 21


2.2 Deplasare spre stânga cu 2 poziţii . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3 Deplasare spre stânga cu 3 poziţii . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.4 Deplasare spre dreapta cu 1 poziţie . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

8.1 Triangulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139


8.2 Card Trick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174

9.1 XOR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191

xvii
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
3.0.1 p00.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.0.2 p00.asm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.0.3 secvenţă de cod ’cpp’ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.1.1 p01 structura unui program C++ . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.1.2 p02 namespace std; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1.3 p03 fstream şi iostream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1.4 p04 ifstream şi ofstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1.5 p05 fin şi fout şi cout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.1.6 p05a citire dublă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.1.7 p00x1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1.8 p05b variabilă neiniţializată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.1.9 p06 nume pentru variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.2.1 ascii comentat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.3.1 Big or Little endian.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.3.2 afisare hexa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.3.3 afisare binar.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.3.4 adrese in memorie.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.3.5 show bytes in memory.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.4.1 p07 instrucţiunea if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.5.1 p08 instrucţiunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.5.2 p09 instrucţiunea while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.5.3 p10 instrucţiunea do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.6.1 p11 instrucţiunea continue; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.6.2 p12 instrucţiunea break; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.7.1 p13 instrucţiunea getchar(); . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.7.2 p14 instrucţiunea system(“PAUSE”); . . . . . . . . . . . . . . . . . . . . . . . 59
4.1.1 .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.1.2 .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.5.1 .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.5.2 .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.5.3 .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
6.1.1 EJOI2022 Perechi ModelSolution.cpp . . . . . . . . . . . . . . . . . . . . . . . . 72
6.1.2 EJOI2022 Perechi check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
6.2.1 EJOI2022 Root rw 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.2.2 EJOI2022 Root check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.3.1 EJOI2022 Tree mhq.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.3.2 EJOI2022 Tree check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.4.1 EJOI2022 game fast power 2 m.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.4.2 EJOI2022 game check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.5.1 EJOI2022 permutations solution.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.5.2 EJOI2022 permutations check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.6.1 EJOI2022 LCS 16774096.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

xviii
6.6.2 EJOI2022 LCS check.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
8.1.1 checkerFountain.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
8.1.2 Fountain-979362.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
8.1.3 Fountain-981117.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
8.1.4 Fountain-981322.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
8.1.5 Fountain-983271.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
8.1.6 Fountain-985016.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
8.1.7 Fountain-992243.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
8.1.8 Fountain-992296.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
8.1.9 Fountain-994277.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
8.2.1 triangulation-981145.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
8.2.2 triangulation-981153.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
8.2.3 triangulation-981926.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8.2.4 triangulation-983329.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
8.2.5 triangulation-985775.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
8.3.1 exam-981110.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
8.3.2 exam-982347.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
8.3.3 exam-984751.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
8.3.4 exam-985120.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
8.3.5 exam-986828.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
8.3.6 exam-992381.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
8.4.1 checkerXorSort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
8.4.2 xorsort-980965.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
8.4.3 xorsort-982930.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
8.4.4 xorsort-986772.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
9.1.1 216711-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
9.1.2 217942-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
9.1.3 229739-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
9.1.4 232150-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
9.1.5 232196-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
9.1.6 237579-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
9.1.7 237607-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
9.1.8 237667-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
9.1.9 237671-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
9.1.10 240337-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
9.1.11 242103-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
9.1.12 246139-rack.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
9.2.1 218063-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
9.2.2 220350-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
9.2.3 229737-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
9.2.4 236349-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
9.2.5 237576-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
9.2.6 237584-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
9.2.7 242110-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
9.2.8 246138-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
9.2.9 246793-XORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
9.2.10 checkerXORanges.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
9.3.1 229752-covering.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
9.3.2 248792-covering.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
9.4.1 218070-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
9.4.2 218553-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
9.4.3 226630-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
9.4.4 226699-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
9.4.5 229759-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
9.4.6 232209-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
9.4.7 240339-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
9.4.8 242112-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
9.4.9 244272-adventure.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
9.6.1 218172-colouring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
9.6.2 226737-colouring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
9.6.3 250194-colouring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
10.1.1 ds n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
10.1.2 ds n2 other.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
10.1.3 gr ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
10.1.4 ig n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
10.1.5 isaf ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
10.1.6 11899245-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
10.1.7 56159794-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
10.1.8 81469343-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
10.1.9 88103111-hills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
10.1.10 Hills1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
10.1.11 checkerHills.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
10.2.1 passports pkun.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
10.2.2 41334650-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
10.2.3 41335815-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
10.2.4 48114130-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
10.2.5 48115887-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
10.2.6 52626176-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
10.2.7 56188505-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
10.2.8 79747607-passports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
10.2.9 checkerPassports.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
10.3.1 rg wa.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
10.3.2 rg wa2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
10.3.3 solve.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
10.3.4 ds ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
10.3.5 gr ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
10.3.6 isaf ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
10.3.7 rg ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
10.3.8 11899245-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
10.3.9 56157022-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
10.3.10 86481283-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
10.3.11 86488789-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
10.3.12 86816257-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
10.3.13 86816797-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
10.3.14 87021285-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
10.3.15 87201594-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
10.3.16 87466173-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
10.3.17 87522420-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
10.3.18 88013224-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
10.3.19 88040747-chemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
10.3.20 chem1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
10.3.21 checkChemistry.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
10.4.1 ds diams.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
10.4.2 ds zhfs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
10.4.3 full random.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
10.4.4 many random.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
10.4.5 prime tree pashka.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
10.4.6 print id.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
10.4.7 sol 100.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
10.4.8 solve is.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
10.4.9 solve is no local.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
10.4.10 solve qoo2p5.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
10.4.11 checkPrime-tree.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
10.5.1 cycle sort ad.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
10.5.2 gr ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
10.5.3 ig ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
10.5.4 isaf ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
10.5.5 isaf ok slowio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
10.5.6 40973023-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
10.5.7 41148878-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
10.5.8 43081231-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
10.5.9 67068459-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
10.5.10 75463698-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
10.5.11 87786210-cycle sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
10.5.12 checkCycle-sort.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
11.1.1 482109.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
11.1.2 483136-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
11.1.3 484114-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
11.1.4 484759-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
11.1.5 484817-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
11.1.6 1703927-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
11.1.7 1748766-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
11.1.8 2119774-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
11.1.9 2210797-magic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
11.2.1 482015-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
11.2.2 482536-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
11.2.3 482782-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
11.2.4 483295-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
11.2.5 484328-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
11.2.6 485011-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
11.2.7 1497611-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
11.2.8 1628810-particles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
11.2.9 checkerParticles.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
11.3.1 481436-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
11.3.2 482515-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
11.3.3 505367-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
11.3.4 1275534-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
11.3.5 1633337-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
11.3.6 1709178-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
11.3.7 2123900-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
11.3.8 2219732-six.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
11.4.1 486658-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
11.4.2 486713-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
11.4.3 502953-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
11.4.4 2220355-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
11.4.5 2659930-camel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
11.4.6 checkerCamel.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
11.5.1 486102-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
11.5.2 486224-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
11.5.3 486736-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
11.5.4 487126-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
11.5.5 489869-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
11.5.6 501765-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
11.5.7 1082259-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
11.5.8 1549152-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
11.5.9 1573243-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
11.5.10 1593395-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
11.5.11 1696625-experience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
11.5.12 checkerExperience.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
11.6.1 486962-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
11.6.2 487418-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
11.6.3 487442-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
11.6.4 488403-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
11.6.5 488741-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
11.6.6 490435-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
11.6.7 507893-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
11.6.8 594280-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
11.6.9 1173310-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
11.6.10 1275946-game.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
11.6.11 checkerGame.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
C.4.1exponentiere rapida1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
C.4.2exponentiere rapida2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
C.4.3exponentiere rapida3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
C.4.4exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
C.5.1exponentiere naiva MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
C.5.2exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
C.6.1secventa cod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
D.2.1cautare binara-v1-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
D.2.2cautare binara-v1-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
D.3.1cautare binara-v2-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
D.3.2cautare binara-v2-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
D.4.1cautare binara-v3-iterativ.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
D.4.2cautare binara-v3-recursiv.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
Part I

Algoritmi şi programare

1
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
27
un sistem de numeraţie poziţional, având baza 10” .
27
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
28
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
29
vom ı̂nţelege uşor de ce ı̂n sistemul de numeraţie ı̂n baza 2 se folosesc cifrele

B2 r0, 1x
30
ş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


31 32
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
33 34
numeraţie. Deşi apare ı̂n limbajele de programare, sistemul octal de numeraţie se foloseşte
foarte putin!
35
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ă:
28
https://ro.wikipedia.org/wiki/Baz%C4%83_de_numera%C8%9Bie
29
https://en.wikipedia.org/wiki/Binary_number
30
https://ro.wikipedia.org/wiki/Sistem_hexazecimal
31
https://ro.wikipedia.org/wiki/Sistem_zecimal
32
https://ro.wikipedia.org/wiki/Sistem_binar
33
https://ro.wikipedia.org/wiki/Sistem_hexazecimal
34
https://ro.wikipedia.org/wiki/Sistem_octal
35
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
36
ı̂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
37
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
38
mic secret: strategia folosită ı̂n acest joc se numeşte strategie ’bottom-up’.
36
https://ro.wikipedia.org/wiki/Algoritm
37
https://ro.wikipedia.org/wiki/Putere_(matematic%C4%83)
38
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).
39
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ă.
39
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.
40
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:


40
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
41 42
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.
41
https://en.wikipedia.org/wiki/Bit
42
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
43
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.
43
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
44
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!
44
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ţă
45
de 2 (sau cod complementar) . La unele calculatoare s-a folosit şi reprezentarea numerelor ı̂ntregi
46 47
ı̂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


48
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

45
https://en.wikipedia.org/wiki/Two%27s_complement
46
https://en.wikipedia.org/wiki/Ones%27_complement
47
https://en.wikipedia.org/wiki/C%2B%2B20
48
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’
49 50
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’

51
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;
}

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

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

Probleme elementare (... utile!)

2.1 Deplasarea cifrelor


2.1.1 Deplasarea cifrelor spre stânga
Să presupunem că folosim numai numere pozitive şi zona pentru cifrele numerelor cu care lucrăm
are 5 “căsuţe”. Alinierea cifrelor se face spre dreapta!
Să considerăm că avem numărul 891. Pentru că avem 5 poziţii pentru cifre, numărul va apărea
scris sub forma 00891.

1 2 3 4 5 6 7

zonă nefolosită cifrele numărului pozitiv (pe 5 poziţii)


... ... ... ... ... ... ...
... ... 0 0 8 9 1 ¡¡ 1
... ... ... ... ... ... ...
... ... 0 8 9 1 0 =
... ... ... ... ... ... ...
Tabelul 2.1: Deplasare spre stânga cu 1 poziţie

Deplasare spre stânga cu 1 poziţie este echivalentă cu ı̂nmulţirea numărului cu 10, deci, 891 se
transformă ı̂n 8910.

1 2 3 4 5 6 7

zonă nefolosită cifrele numărului pozitiv (pe 5 poziţii)


... ... ... ... ... ... ...
... ... 0 0 8 9 1 ¡¡ 2
... ... ... ... ... ... ...
... ... 8 9 1 0 0 =
... ... ... ... ... ... ...
Tabelul 2.2: Deplasare spre stânga cu 2 poziţii

Deplasare spre stânga cu 2 poziţii este echivalentă cu ı̂nmulţirea numărului cu 10*10, deci, 891
se transformă ı̂n 89100.

1 2 3 4 5 6 7

zonă nefolosită cifrele numărului pozitiv (pe 5 poziţii)


... ... ... ... ... ... ...
... ... 0 0 8 9 1 ¡¡ 3
... ... ... ... ... ... ...
... ... 9 1 0 0 0 =
... ... ... ... ... ... ...
Tabelul 2.3: Deplasare spre stânga cu 3 poziţii

Deplasare spre stânga cu 3 poziţii este echivalentă cu ı̂nmulţirea numărului cu 10*10*10 şi

21
CAPITOLUL 2. PROBLEME ELEMENTARE 2.2. CONTOR 22

astfel 891 se transformă ı̂n 891000 dar ... cifra 8 ajunge ı̂n zona roşie (roz)! ... şi “se pierde” pur

şi simplu ... deci, 891 se transformă ı̂n 91000 prin ı̂nmulţire cu 1000.
Astfel de lucruri se ı̂ntâmplă şi ı̂n baza 2, ı̂n calculator, ı̂n C++, ı̂n Pascal, ı̂n Java, ...! Numerele
au alocate nişte căsuţe ı̂n care să fie scrise. Dacă ieşim din acele căsuţe atunci se pierd cifre şi

astfel se obţin rezultate greşite ... despre care noi am crezut că sunt numai ... “ciudate”!

2.1.2 Deplasarea cifrelor spre dreapta

1 2 3 4 5 6 7

zonă nefolosită cifrele numărului pozitiv (pe 5 poziţii)


... ... ... ... ... ... ...
... ... 0 0 8 9 1 ¿¿ 1
... ... ... ... ... ... ...
... ... 0 0 0 8 9 =
... ... ... ... ... ... ...
Tabelul 2.4: Deplasare spre dreapta cu 1 poziţie

Deplasare spre dreapta cu 1 poziţie este echivalentă cu ı̂mpărţirea numărului prin 10, deci 891 se
transformă ı̂n 89 pentru că cifra 1 “se pierde” pur şi simplu!
Acelaşi număr 891, dacă ar fi fost deplasat spre dreapta cu 2 poziţii ar fi devenit 8 iar dacă ar

fi fost deplasat spre dreapta cu 3 poziţii ar fi devenit 0. Desigur!

2.2 Numere generate de contor - produs cartezian!


2.2.1 Introducere
Mişcarea contorului electric este ceva normal şi simplu pentru noi! Dacă acum contorul arată
02174 după ce vom consuma 1 KW contorul va arăta 02175.
Indiferent de ce număr arată contorul nostru la un moment dat, noi ştim ce va arăta după
consumarea unui kilowat. Asta este sigur! Nu ştiu insă dacă ı̂nţelegem, de fapt, ce se ı̂ntâmplă
când se adaugă o unitate la un număr!

2.2.2 Mărire cu 1
Ce se ı̂ntâmplă când se măreşte valoarea unui număr ı̂ntreg cu o unitate?

1. Dacă se poate, se măreşte cu o unitate ultima cifră (cea din dreapta) şi ... gata!.
2. Dacă nu se poate mări ultima cifră (pentru că este ”9” = cea mai mare cifră permisă pe
poziţia verificată), se caută spre stânga prima poziţie care se poate mări, se măreşte acea
poziţie şi se pun pe ”0” toate poziţiile prin care s-a trecut. (”0” este cea mai mică cifră
permisă pe poziţia verificată).

Observaţie:
După mărire cu 1 a cifrei de pe poziţia posibilă, se pune cel mai mic număr pe zona cifrelor din
dreapta poziţiei mărite!

2.2.3 Micşorare cu 1
Ce se ı̂ntâmplă când se micşorează valoarea unui număr ı̂ntreg cu o unitate?

1. Dacă se poate, se micşorează cu o unitate ultima cifră (cea din dreapta) şi ... gata!.
2. Dacă nu se poate micşora ultima cifră (pentru că este ”0” = cea mai mică cifră permisă pe
poziţia verificată), se caută spre stânga prima poziţie care se poate micşora, se micşorează
acea poziţie şi se pun pe ”9” toate poziţiile prin care s-a trecut. (”9” este cea mai mare cifră
permisă pe poziţia verificată).
CAPITOLUL 2. PROBLEME ELEMENTARE 2.2. CONTOR 23

Observaţie:
După micşorare cu 1 a cifrei de pe poziţia posibilă, se pune cel mai mare număr pe zona cifrelor
din dreapta poziţiei micşorate!

2.2.4 Predecesor şi succesor


53
Următoarele 3 exemple ne permit să ne antrenăm gândirea algoritmică .[43]
Mai precis: creierul nostru ne spune imediat că după 23699 urmează 23700 dar ... este util să
ı̂ntelegem regulile şi paşii utilizaţi la “mişcarea contorului”, ı̂n exemplele care urmează!.

9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
8 8 8 8 8 8 8 8 8 88 8 8 8 8 8
7 7 7 7 77 7 7 7 7 7 7 7 7 7 7
6 6 66 6 6 6 6 66 6 6 6 6 66 6 6
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
3 33 3 33 3 3 33 3 33 3 3 33 3 33 3
22 2 2 2 2 22 2 2 2 2 22 2 2 2 2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 3 2 1 0 4 3 2 1 0 4 3 2 1 0

(a) (b) (c)


Predecesor Stare curentă Succesor

Figura 2.1: Determinare predecesor şi succesor - cazul 1

În cazul 1 ultima cifră a contorului ı̂n starea curentă este 8. Pentru succesor trebuie să mărim
8 cu 1 iar pentru predecesor trebuie să micşorăm 8 cu 1. Ambele cazuri sunt “uşoare” pentru că
restul cifrelor nu se schimbă!

9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
8 8 8 8 88 8 8 8 8 8 8 8 8 8 8
7 7 7 7 7 7 7 7 7 7 7 7 77 7 7
6 6 66 6 6 6 6 66 6 6 6 6 6 6 6
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
3 33 3 3 3 3 33 3 3 3 3 33 3 3 3
22 2 2 2 2 22 2 2 2 2 22 2 2 2 2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 3 2 1 0 4 3 2 1 0 4 3 2 1 0

(a) (b) (c)


Predecesor Stare curentă Succesor

Figura 2.2: Determinare predecesor şi succesor - cazul 2

În cazul 2 ultima cifră a contorului ı̂n starea curentă este 9.


Pentru succesor trebuie să mărim 9 cu 1 iar pentru predecesor trebuie să micşorăm 9 cu 1.
Pentru predecesor cazul este “uşor” pentru că restul cifrelor nu se schimbă!
Pentru succesor cazul este mai “greu” pentru că restul cifrelor se schimbă!.
9 este cea mai mare cifră care se poate folosi. Pentru că nu o putem mări, trebuie să căutăm,
spre stânga, prima poziţie care are o cifră ce se poate mări cu 1. Această poziţie din “starea
53
https://www.academia.edu/718368/G%C3%A2ndirea_Algoritmic%C4%83_O_Filosofie_Modern%C4
%83_a_Matematicii_%C5%9Fi_Informaticii
CAPITOLUL 2. PROBLEME ELEMENTARE 2.3. ORDINE LEXICOGRAFICĂ 24

curentă” este cea care conţine cifra 6. 6 va deveni 7 iar pe poziţiile din dreapta trebuie să punem
cele mai mici cifre!
Mai ı̂n glumă, mai ı̂n serios ... ne dăm seama că:
ˆ dacă ultima cifră este 9 atunci predecesorul se obţine uşor, dar succesorul ... mai greu!
ˆ dacă ultima cifră este 0 atunci succesorul se obţine uşor, dar predecesorul ... mai greu!
ˆ dacă ultima cifră este diferită de 9 şi 0 atunci ambele se obţin uşor (se modifică numai ultima
cifră)!

9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
6 6 6 6 6 6 6 66 6 6 6 6 66 6 6
5 5 55 5 5 5 5 5 5 5 5 5 5 5 5
4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
3 33 3 3 3 3 33 3 3 3 3 33 3 3 3
22 2 2 2 2 22 2 2 2 2 22 2 2 2 2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 11
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 3 2 1 0 4 3 2 1 0 4 3 2 1 0

(a) (b) (c)


Predecesor Stare curentă Succesor

Figura 2.3: Determinare predecesor şi succesor - cazul 3

În cazul 3 ultima cifră a contorului ı̂n starea curentă este 0.


Pentru succesor trebuie să mărim 0 cu 1 iar pentru predecesor trebuie să micşorăm 0 cu 1.
Pentru succesor cazul este “uşor” pentru că restul cifrelor nu se schimbă!
Pentru predecesor cazul este mai “greu” pentru că restul cifrelor se schimbă!.
0 este cea mai mică cifră care se poate folosi. Pentru că nu o putem micşora, trebuie să căutăm,
spre stânga, prima poziţie care are o cifră ce se poate micsora cu 1. Această poziţie din “starea
curentă” este cea care conţine cifra 6. 6 va deveni 5 iar pe poziţiile din dreapta trebuie să punem
cele mai mari cifre!
Observaţie:
Mai ı̂n glumă, mai ı̂n serios ... (din nou!) ne dăm seama că observaţiile făcute ı̂n cazurile 2 şi 3
sunt aproape identice; practic sunt numai interschimbate 0 cu 9 şi “mic” cu “mare”!

2.3 Numere formate cu cifre date - ordine lexicografică!


Să presupunem că avem câteva cifre (nu neapărat distincte) cu care formăm numere.
De exemplu: 2,3,5,3,7.

2.3.1 Cel mai mic număr


Cel mai mic număr care se poate forma cu cifrele 2, 3, 5, 3, 7 este 23357.
“Regula” este evidentă (pentru a obţine cel mai mic număr folosind nişte cifre date): se scriu
cifrele ı̂n ordine crescătoare!

2.3.2 Cel mai mare număr


Cel mai mare număr care se poate forma cu cifrele 2, 3, 5, 3, 7 este 75332.
“Regula” este evidentă (pentru a obţine cel mai mare număr folosind nişte cifre date): se
scriu cifrele ı̂n ordine descrescătoare!
CAPITOLUL 2. PROBLEME ELEMENTARE 2.3. ORDINE LEXICOGRAFICĂ 25

Observaţie:
Mai ı̂n glumă, mai ı̂n serios ... (din nou!) ne dăm seama că cele două “Reguli” sunt aproape
identice; practic sunt numai interschimbate “mic” cu “mare” şi “crescător” cu “descrescător”!
Să presupunem că avem la dispoziţie aceleaşi cifre ca ı̂n exemplele anterioare: 2,3,5,3,7.
Să presupunem că avem 3 numere “consecutive” a, b, c şi “numărul curent” este b 52733.
Cât este a? Cât este b?. Sau altfel spus: care sunt predecesorul şi succesorul lui 52733?

2.3.3 Succesorul unui număr


“Succesorul” unui număr nu este neapărat numărul curent +1. În cazul nostru b+1 = 52733 + 1
= 52734 dar acest număr nu este “valabil” pentru că, de exemplu, conţine cifra 4 (cifrele permise
erau: 2,3,5,3,7).
Intuitiv, primul număr mai mare decât b care se poate forma cu cifrele lui b este c 53237.
Vă gândiţi la o altă valoare pentru c?
Argumentul central este următorul: ı̂n b 52733 secvenţa de cifre 733 este cea mai lungă
secvenţă descrescătoare de la sfârşitul lui b, deci cel mai mare număr care sepoate forma cu cifrele
7, 3, 3 la sfârşitul lui b. Ca să mărim un pic b trebuie să mărim puţin cifra 2 (care este prima cifră
ı̂n stânga secvenţei 733).
Dar nu putem mări oricum pe 2 ci trebuie să folosim una dintre cifrele secvenţei 733.
Cea mai mică cifră mai mare decât 2 care este ı̂n secvenţa 733 este 3. Vom schimba ı̂ntre ele
cifrele, 2 cu 3 (cu primul 3 sau cu al doilea 3 din secvenţa 733 ... este altă poveste!).
Numărul mărit un pic arată aşa: 53*** unde ı̂n locul steluţelor trebuie să punem cifrele 2, 3,
7 (cifre provenite din 733 ı̂n care am luat un 3 şi am pus un 2).
Fiind 53*** ı̂n loc de 52***, deja este clar că numărul a fost mărit! Pentru că trebuie să-l
mărim numai un pic, vom pune ı̂n locul steluţelor cel mai mic număr care se poate forma cu cifrele
rămase 2,3,7.

Obţinem astfel c 53237.

2.3.4 Predecesorul unui număr


“Predecesorul” unui număr nu este neapărat numărul curent -1. În cazul nostru b-1 = 52733 - 1
= 52732 dar acest număr nu este “valabil” pentru că, de exemplu, conţine numai o cifra 3 (cifrele
permise erau: 2,3,5,3,7).
Intuitiv, primul număr mai mic decât b care se poate forma cu cifrele lui b este a 52373.
Vă gândiţi la o altă valoare pentru a?
Argumentul central este următorul: ı̂n b 52733 secvenţa de cifre 33 este cea mai lungă
secvenţă crescătoare de la sfârşitul lui b, deci cel mai mic număr care sepoate forma cu cifrele 3,
3 la sfârşitul lui b. Ca să micşorăm un pic b trebuie să micşorăm puţin cifra 7 (care este prima
cifră ı̂n stânga secvenţei 33).
Dar nu putem micşora oricum pe 7 ci trebuie să folosim una dintre cifrele secvenţei 33.
Cea mai mare a cifră mai mică decât 7 care este ı̂n secvenţa 33 este 3. Vom schimba ı̂ntre ele
cifrele, 7 cu 3 (cu primul 3 sau cu al doilea 3 din secvenţa 33 ... este altă poveste!).
Numărul micşorat un pic arată aşa: 523** unde ı̂n locul steluţelor trebuie să punem cifrele 3,
7 (cifre provenite din 33 ı̂n care am luat un 3 şi am pus un 7).
Fiind 523** ı̂n loc de 527**, deja este clar că numărul a fost micşorat! Pentru că trebuie să-l
micşorăm numai un pic, vom pune ı̂n locul steluţelor cel mai mare număr care se poate forma cu
cifrele rămase 3,7.

Obţinem astfel c 52373.


Observaţie:

Mai ı̂n glumă, mai ı̂n serios ... (iarăşi? ) ne dăm seama că cele două “Argumente cen-
trale” sunt aproape identice; practic sunt numai interschimbate “mic” cu “mare” şi “crescător”
cu “descrescător” (desigur, şi cifrele corespunzătoare!)!
Capitolul 3

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 3.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
54
fi executat pe calculator.
În limbaj de asamblare acest cod arată astfel:

Listing 3.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
54
https://ro.wikipedia.org/wiki/Limbaj_de_asamblare

26
CAPITOLUL 3. PROGRAMARE C+++ 27

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 3.0.3: secvenţă de cod ’cpp’


int a, b, sum;

cin >> a;
cin >> b;

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

55
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:
55
http://www.cplusplus.com/doc/tutorial/introduction/
CAPITOLUL 3. PROGRAMARE C+++ 28

Figura 3.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 3.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 3.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 3. PROGRAMARE C+++ 29

Figura 3.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 3.5: (Picture from: https://upload.wikimedia.org/wikipedia/commons/2/26/Punched_ca


rd_program_deck.agr.jpg )
CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 30

3.1 Structura unui program in C++


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

Listing 3.1.1: p01 structura unui program C++


// p01 - structura unui program C++

int main()
{

return 0;
}

Figura 3.6: p01.cpp compilare (build)

56
exemplul clasic este: http://www.cplusplus.com/doc/tutorial/program_structure/
CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 31

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

Figura 3.8: p01.cpp rezultat execuţie

Scrierea codului pe linii este complet liberă! Se pot scrie toate instrucţiunile (tot programul)
57 58
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!
59
Fiecare editează şi programează cum vrea dar ... este util, din mai multe puncte de vedere,
60 61
să citim câte ceva despre “best practice” ı̂n domeniul programării ı̂n C++.
57
DEX - lizibil: care poate fi citit uşor
58
DEX - indentare: plasare a programelor pe linii, pentru scrierea cât mai clară a acestora
59
https://www.learncpp.com/cpp-tutorial/whitespace-and-basic-formatting/
60
https://en.wikipedia.org/wiki/Best_practice
61
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 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 32

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 3.9: Style Allman (ANSI)

Figura 3.10: Style Java


CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 33

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

Figura 3.11: TAB size in spaces

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

Figura 3.12: Compiler warnings: [-Wall]


CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 34

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 3.13: Optimize even more (for speed) [-O2]

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

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

62
ˆ este butonul build pentru complilare (Figura 3.6)

ˆ este butonul run pentru execuţie (Figura 3.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 ... *¯
62
http://www.cplusplus.com/doc/tutorial/introduction/
CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 35

3.1.2 namespace std;

Listing 3.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ă!

3.1.3 Pregătire suport pentru fişiere

Listing 3.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.
63
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
64
look into the current folder (current directory).
2) When we use define for a constant, the preprocessor produces a C program where the
65
defined constant is searched and matching tokens are replaced with the given expression.

3.1.4 Acces la fişiere

Listing 3.1.4: p04 ifstream şi ofstream


// p04 ... ifstream + ofstream
63
https://en.wikipedia.org/wiki/C_preprocessor
64
https://www.geeksforgeeks.org/interesting-facts-preprocessors-c/
65
https://www.geeksforgeeks.org/interesting-facts-preprocessors-c/
CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 36

#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!

3.1.5 Citire şi scriere folosind fişiere

Listing 3.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 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 37

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

Figura 3.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
66
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 3.16: f05.in vizualizat cu WinHex

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

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
68
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!
69
Depinde, ı̂n primul rând, de cuvântul scris ı̂n faţa lui n (se numeşte tipul lui n, iar ı̂n
70
exemplul nostru este int). Dar ... mai depinde şi de compilatorul de C++ pe care ı̂l folosim,
71 72
de standardul pe care ı̂l utilizează acest compilator şi de sistemul de operare pe care ı̂l avem
73
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
74 75 76
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 3.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.
68
https://en.cppreference.com/w/cpp/keyword
69
https://www.tutorialspoint.com/cplusplus/cpp_data_types.htm
70
http://www.cplusplus.com/doc/tutorial/introduction/
71
https://en.wikipedia.org/wiki/C%2B%2B
. https://isocpp.org/std/status
72
https://en.wikipedia.org/wiki/Operating_system
73
https://en.wikipedia.org/wiki/Macintosh
74
https://gcc.gnu.org/releases.html
75
https://en.wikipedia.org/wiki/Interpreter_(computing)
76
https://ioi2020.sg/contestantpc/
CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 39

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
77
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!
78
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.
79
"\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.

3.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.
80
Dacă trebuie să comentăm mai multe linii consecutive vom folosi acest model :

/*
...
*/

3.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 3.15 ci mai degrabă
aşa cum ı̂l arată WinHex ı̂n figura următoare:

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

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

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 3.19: Dimensiuni fişiere

3.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 3.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 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 41

Figura 3.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.

3.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
82
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!).
83 84
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 3.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;
}
/*
82
https://en.wikipedia.org/wiki/Byte_addressing
83
https://en.wikipedia.org/wiki/Byte
84
https://en.wikipedia.org/wiki/Bit
CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 42

&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.
*/

85
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 3.21: Alocarea spaţiului de memorie

În Figura 3.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 3.22)

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

85
https://ro.wikisource.org/wiki/O_scrisoare_pierdut%C4%83
CAPITOLUL 3. PROGRAMARE C+++ 3.1. STRUCTURA UNUI PROGRAM IN C++ 43

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

Figura 3.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 3.24)

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

3.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 3.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 3. PROGRAMARE C+++ 3.2. ALFABETUL LIMBAJULUI 44

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

Figura 3.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!

3.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 3.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;
}

3.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,
86
simbolul adunării “+”, etc. Acestea se numesc caractere.
Totalitatea caracterelor permise ı̂n limbaj se numeşte “alfabetul” limbajului.
86
caracter: 1. Semn săpat (ı̂n piatră, ı̂n metal) sau scris (https://dexonline.ro/definitie/caracter)
CAPITOLUL 3. PROGRAMARE C+++ 3.2. ALFABETUL LIMBAJULUI 45

87
Î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!
88
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) ***

89
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.

3.2.1 Un program util


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

Listing 3.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();
87
https://publications.gbdirect.co.uk/c_book/chapter2/alphabet_of_c.html
88
https://www.techiedelight.com/remove-whitespaces-string-cpp/
89
https://www.techiedelight.com/remove-whitespaces-string-cpp/
CAPITOLUL 3. PROGRAMARE C+++ 3.2. ALFABETUL LIMBAJULUI 46

}
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 3. PROGRAMARE C+++ 3.2. ALFABETUL LIMBAJULUI 47

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

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;
}

3.2.2 Coduri de control şi spaţii

Figura 3.26: isctrl+isspace+isblank

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

3.2.3 Caracterele printabile

Figura 3.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!.
90
https://en.wikipedia.org/wiki/ASCII
CAPITOLUL 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 48

3.2.4 Codurile alfanumerice

Figura 3.28: isalnum

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

3.2.5 Simboluri ASCII


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

Figura 3.29: simboluri ASCII

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

3.3 Alocarea variabilelor ı̂n memorie


3.3.1 Puţină istorie!
http://ioi.te.lv/history.shtml
Calculatoarelor folosite la olimpiade sunt mult mai puternice decât erau ı̂nainte!
91
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

92
Configuraţiile calculatoarelor care s-au folosit la I.O.I. cu mai mulţi ani ı̂n urmă au fost:
93
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.”

94
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). ”


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

95
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”

96
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

3.3.2 ’Big endian’ sau ’Little endian’

Listing 3.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))) << " "
95
http://olympiads.win.tue.nl/ioi/ioi94/contest/
96
http://ioi.te.lv/locations/ioi93/rules.txt
CAPITOLUL 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 50

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

return 0;
}

Pe calculatorul meu ... apare:

Figura 3.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 3.31: Big si Little endian

3.3.3 Afişare ı̂n ’hexa’


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

Listing 3.3.2: afisare hexa.cpp


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

using namespace std;

int main()
CAPITOLUL 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 51

{
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 3.32: Afişare hexa

3.3.4 Afişare ı̂n ’binar’


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

Listing 3.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 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 52

Figura 3.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
97
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 3.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).

3.3.5 Alocarea variabilelor ı̂n spaţiul de memorie

https://en.wikipedia.org/wiki/DOS_memory_management
97
https://en.cppreference.com/w/cpp/language/operator_precedence
CAPITOLUL 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 53

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

Listing 3.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 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 54

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 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 55

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.
...

3.3.6 MSB şi LSB

Listing 3.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 3.35: MSB+LSB

98
MSB (most-significant byte)
98
https://en.wikipedia.org/wiki/Bit_numbering
CAPITOLUL 3. PROGRAMARE C+++ 3.3. ALOCAREA VARIABILELOR 56

LSB (least-significant byte)

Figura 3.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 3.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 3.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 3. PROGRAMARE C+++ 3.4. INSTRUCŢIUNI DE DECIZIE -
STRUCTURI ELEMENTARE DE CONTROL 57

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

3.4 Instrucţiuni de decizie - structuri elementare de control


3.4.1 Instrucţiunea if

Listing 3.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 ... !!!

3.4.2 Instrucţiunea case


3.4.3 Operatorul condiţional (ternar) ? :
99 100
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

3.5 Instrucţiuni repetitive - structuri elementare de control


3.5.1 Instrucţiunea for

Listing 3.5.1: p08 instrucţiunea for


// p08 ... for descrescator

99
https://ro.wikipedia.org/wiki/Operator_ternar
100
https://en.wikipedia.org/wiki/%3F:
CAPITOLUL 3. PROGRAMARE C+++ 3.5. INSTRUCŢIUNI REPETITIVE -
STRUCTURI ELEMENTARE DE CONTROL 58

#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

3.5.2 Instrucţiunea while

Listing 3.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;
}

3.5.3 Instrucţiunea do
CAPITOLUL 3. PROGRAMARE C+++ 3.6. INSTRUCŢIUNI DE SALT 59

Listing 3.5.3: p10 instrucţiunea do


// p10 ...

3.6 Instrucţiuni de salt


3.6.1 Instrucţiunea continue;

Listing 3.6.1: p11 instrucţiunea continue;


// p11 ...

3.6.2 Instrucţiunea break

Listing 3.6.2: p12 instrucţiunea break;


// p12 ...

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


3.7.1 getchar();

Listing 3.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;
}

3.7.2 system(“PAUSE”);

Listing 3.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. PROGRAMARE C+++ 3.8. TIPURI PREDEFINITE 60

3.8 Tipuri de date predefinite ı̂n C++


Aici este lista complet a tipurilor fundamentale n C ++:
http://www.cplusplus.com/doc/tutorial-ro/variables/
Grup Numele tipurilor* Observaii privind dimensiunea / precizia Tipuri caracter char Exact
un byte n dimensiune. Cel puin 8 bii.
char16 t Nu mai mic dect char. Cel puin 16 bii. char32 t Nu mai mic dect char16 t.Cel puin
32 bii.
wchar t Poate reprezenta cea mai mare mulime de caractere suportat. Tipuri ntregi cu
semn(signed) signed char Acceai dimensiune ca i char. Cel puin 8 bii. signed short int Nu
mai mic dect char. Cel puin 16 bii. signed int Nu mai mic dect short. Cel puin 16 bii. signed long
int Nu mai mic dect int. Cel puin 32 bii. signed long long int Nu mai mic dect long. Cel puin
64 bii. Tipuri ntregi fr semn (unsigned) unsigned char (aceeai dimensiune ca i corespondentul
cu semn) unsigned short int unsigned int unsigned long int unsigned long long int Tipuri pentru
numere zecimale float double Precizie nu mai mic dect float long double Precizie nu mai mic dect
double Tip boolean bool Tip vid void fr spaiu Pointer nul decltype(nullptr)
* The names of certain integer types can be abbreviated without their signed and int compo-
nents - only the part not in italics is required to identify the type, the part in italics is optional.
I.e., signed short int can be abbreviated as signed short, short int, or simply short; they all identify
the same fundamental type.
n fiecare dintre grupele de mai sus, diferena dintre tipuri este doar dimensiunea lor (de exemplu,
ct de mult spaiu ocup n memorie): primul tip din fiecare grup este cel mai mic, iar ultimul este
cel mai mare. Fiecare tip este cel puin la fel de mare ca i cel precedent din cadrul aceluiai grup,
excepie fcnd tipurile dintr-un grup care au aceleai proprieti.
Data type https://en.wikipedia.org/wiki/Data_type
Un tip este n mod esenial caracterizat prin:

(1) Mulimea valorilor creia i aparine o constant a tipului n cauz, respectiv mulimea valorilor pe
care le poate asuma o variabil, o expresie sau care pot figenerate de o funcie ncadrat n acel
tip
(2) Un anumit grad de structurare (organizare) a informaiei;
(3) Unset de operatori specifici.

Caracteristicile conceptului de tip sunt urmtoarele:

(1) Un tip de date determinmulimea valorilor creia i aparine o constant, sau pe care le poate
asuma o variabil sau o expresie, sau care pot fi generate de un operator sau o funcie.
(2) Tipul unei valori precizate de o constant, variabil sau expresie poate fi dedusdin forma sau
din declaraia sa, fr a fi necesar execuia unor procese de calcul.
(3) Fiecare operator sau funcie acceptargumente de un tip precizat i conduce la un rezultat de
un tip precizat.
(4) Un tip presupune un anumit nivel de structurare (organizare) a informaiei.

Tipurile de date de baz n C/C++ sunt:


ntregi:char,short int,int,long int
reale:float,double
Fiecrui tip de date ntreg i se poate specifica dac acesta este cu semn sau far semn
(signed,unsigned). n mod implicit, toate tipurile de date ntregi suntcu semn (signed), ne fiind
nevoie ca programatorul s specifice dect atuncicnd are nevoie de un numr fr semn (unsigned).
Diferena ntre tipurile de date ntregi este dat de spaiul pe care acel tipde date l ocup n memorie.
Tipul de date implicit esteint, i dimensiuneaacestuia variaz de la sistem la sistem.chariunsigned
charntotdeaunaocup 1 octet (byte). Faptul c un tip de date este cu sau fr semn nu schimbnumrul
de octei pe care acesta l ocup n memorie.
Pentru a afla exact ci octei (bytes) ocup un anumit tip de date se foloseteoperatorulsizeof().
https://www.cs.cmu.edu/˜mihaib/articole/tipuri/tipuri-html.html#SEC
TION00030000000000000000
CAPITOLUL 3. PROGRAMARE C+++ 3.8. TIPURI PREDEFINITE 61

Ce este un tip de date


Exist mai multe definiii posibile pentru tipurile de date, din mai multe perspective. O privire
global definete simultan att tipurile de date ct i operaiile care se pot face cu ele, pentru c acestea
sunt de fapt cele dou faete ale unei aceleiai monezi: nu pot exista una fr cealalt. Noi vom trata
cele dou subiecte pe rnd i n mod inegal, acordnd o oarecare prioritate tipurilor de date.
Astfel, n cea mai simpl accepiune posibil, un tip de date nu este altceva dect o mulime de
valori.
Ce este o valoare? Nu are nici o importan! Este un nume special dat elementelor care aparin
unui tip. Termenul “aparin” este justificat, pentru c, reinei, tipul este o mulime, n cel mai precis
sens matematic.
Exemple? n Pascal tipul boolean este o mulime cu dou elemente. Aceste dou elemente se
noteaz n Pascal cu false i true. False i true se numesc de aceea valori booleene. Tipul integer este
tot o mulime care include, printre altele, nite elemente (valori) care n Pascal se noteaz cu -2, -1, 0,
1, 2, 3 etc. Tipul integer este un tip interesant, pentru c i propune s mimeze mulimea matematica
Z a numerelor ntregi, dar nu reuete prea bine, pentru c mulimea integer este finit, iar Z nu! Care
sunt limitele mulimii integer nu se specific. n dialectul Turbo Pascal (cel mai rspndit pe PC-uri)
integer are 65536 de elemente, de la cel notat -32768 la cel notat 32767. Elementele tipului integer
sunt numite valori ntregi (sau mai precis integer values).
Dac ai citit textul de mai sus cu atenie ai observat c ne-am ferit s spunem ca false este un
element (valoare) boolean. Am spus false este o notaie (o reprezentare) a unui element boolean!
De aici ncolo nu vom mai fi att de scrupuloi; dealtfel cele dou noiuni (element i reprezentare) se
pot adesea interschimba fr ambiguitate.
Vom zice de aceea:
boolean = { false, true }
unde acoladele sunt notaia uzual pentru mulime.
Tipuri fundamentale
Limbajul Pascal (standard) conine patru tipuri gata construite. Pe acestea le numim tipuri
fundamentale. Ele sunt:
real, integer, char, boolean
Pentru boolean am vzut
boolean = { false, true }
Pentru integer am putea scrie ceva de genul

integer rnϵZ ¶ limitajos & n $ limitasus x


limitajos i limitasus depind de dialectul de Pascal folosit. Pentru Turbo ele sunt (dup cum am
zis) -32768, respectiv 32768.
Pentru char lucrurile sunt grele pentru c exist unele caractere care nu au o reprezentare evident!
Tot ce putem spune este c aceast mulime include mulimea
{’a’, ’b’, ...’A’, ’B’, ..., ’0’, ..., ’ !’, ...}
a caracterelor care pot fi tiprite.
Iar pentru real lucrurile sunt i mai nclcite, pentru c, dei real se dorete o imagine a mulimii R
a numerelor reale, n realitate real este finit i are elementele distribuite ntr-un mod foarte ciudat
pe axa real (nici mcar dou elemente ale ei nu sunt echidistante)! Aceasta mulime este subiectul
analizei numerice, care studiaz cum putem face calcule cnd putem manipula numai aproximri ale
valorilor.
In computer science and computer programming, a data type or simply type is an attribute
of data which tells the compiler or interpreter how the programmer intends to use the data.
Most programming languages support basic data types of integer numbers (of varying sizes),
floating-point numbers (which approximate real numbers), characters and Booleans. A data type
constrains the values that an expression, such as a variable or a function, might take. This data
type defines the operations that can be done on the data, the meaning of the data, and the way
values of that type can be stored. A data type provides a set of values from which an expression
(i.e. variable, function, etc.) may take its values.[1][2]
’tells the compiler or interpreter how the programmer intends to use the data’: cât spaţiu să
aloce, cum să codifice informaţia şi să o depună acolo, cum să decodifice ce este acolo, ce operaţii
se pot face cu ele, ...
The five types are:
Syntactic
CAPITOLUL 3. PROGRAMARE C+++ 3.8. TIPURI PREDEFINITE 62

A type is a purely syntactic label associated with a variable when it is declared. Such definitions
of ”type” do not give any semantic meaning to types.[clarification needed]
Representation
A type is defined in terms of its composition of more primitive typesoften machine types.
Representation and behaviour
A type is defined as its representation and a set of operators manipulating these representations.
Value space
A type is a set of possible values which a variable can possess. Such definitions make it possible
to speak about (disjoint) unions or Cartesian products of types.
Value space and behaviour
A type is a set of values which a variable can possess and a set of functions that one can apply
to these values.
The definition in terms of a representation was often done in imperative languages such as
ALGOL and Pascal, while the definition in terms of a value space and behaviour was used in
higher-level languages such as Simula and CLU.
Classes of data types
Primitive data types
Primitive data types are typically types that are built-in or basic to a language implementation.
Machine data types
Boolean type
Enumerations
Numeric types: integer, floating point, fixed point, Bignum
Composite types: array, record, union, set, object
String and text types: character, string,
Abstract data types:
Other types: Pointers and references, Function types, ...
Type systems:

3.8.1 Character types


3.8.2 Numerical integer types
3.8.3 Floating-point types
3.8.4 Boolean type
De conspectat din:
http://www.enseignement.polytechnique.fr/informatique/INF478/docs/C
pp/en/cpp/language/types.html ... Fundamental types
https://en.cppreference.com/w/cpp/language/types ... ASCII whitespaces ...
http://www.cplusplus.com/doc/tutorial/variables/ ... Fundamental data types
https://www.tutorialspoint.com/cplusplus/cpp_data_types.htm ... Primi-
tive Built-in Types
http://www.cplusplus.com/doc/tutorial/variables/ ... initialization of variables
https://en.cppreference.com/w/cpp/language/types ... C++20
https://isocpp.org/std/the-standard ... current standard (C++17)
Capitolul 4

Masive de date ı̂n C++

4.1 Vectori
https://www.geeksforgeeks.org/introduction-to-arrays/

4.1.1 Implementare statică - array

Listing 4.1.1: .cpp


// https://www.geeksforgeeks.org/advantages-of-vector-over-array-in-c/

#include <bits/stdc++.h>

using namespace std;

int main()
{
int array[100]; // Static Implementation

cout << sizeof(array) << "\n";


cout << sizeof(array[0]) << "\n";
cout << "Size of Array " << sizeof(array) / sizeof(array[0]) << "\n";

return 0;
}

4.1.2 Implementare dinamică - array

Listing 4.1.2: .cpp


// https://www.geeksforgeeks.org/advantages-of-vector-over-array-in-c/

#include <bits/stdc++.h>

using namespace std;

int main()
{
int* arr = new int[100]; // Dynamic Implementation

cout << sizeof(arr) << "\n";


cout << sizeof(*arr) << "\n";
cout << sizeof(arr) / sizeof(*arr) << "\n";
// Pointer cannot be used to get size of block pointed by it

delete[] arr; // array Explicitly deallocated


return 0;
}

4.1.3 Implementare STL - vector¡***¿


https://www.educative.io/edpresso/cpp-vector-vs-array

63
CAPITOLUL 4. MASIVE DE DATE ÎN C++ 4.2. ŞIRURI DE CARACTERE 64

Dynamic in nature. The size automatically changes as elements are added or removed.
Occupies a lot of memory, due to being dynamic in nature. A typical vector implementation
grows by doubling its allocated space rather than incrementing it by 1. Reallocating memory is
usually an expensive operation in vectors.
https://www.modernescpp.com/index.php/c-core-guidelines-std-array-a
nd-std-vector-are-your-friends
Okay, I can make it short today. Here is a rule of thumb: If you want to add elements to your
container or remove elements from your container, use a std::vector; if not, use a std::array.
std::array and std::vector offer the following advantages:
the fastest general-purpose access (random access, including being vectorization-friendly);
the fastest default access pattern (begin-to-end or end-to-begin is prefetcher-friendly);
the lowest space overhead (contiguous layout has zero per-element overhead, which is cache-
friendly).
Tipuri de date structurate.
Tipuri de date tablou unidimensional https://invat.online/lectia/priveste/988
Tipul tablou (Array )
array vs vector ...
https://towardsdatascience.com/8-common-data-structures-every-progr
ammer-must-know-171acf6a1a42

4.2 Şiruri de caractere


4.2.1 Vector de caractere - array
4.2.2 Tipul ’string’
https://en.cppreference.com/w/cpp/string/basic_string
https://www.geeksforgeeks.org/stdstring-class-in-c/
https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp9_String.html
https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/IO/Streams/st
ring
https://ro.wikipedia.org/wiki/Structur%C4%83_de_date
https://simple.wikipedia.org/wiki/Data_structure
https://www.geeksforgeeks.org/data-structures/
https://towardsdatascience.com/data-structures-in-c-part-1-b64613b
0138d
https://data-flair.training/blogs/data-structures-in-cpp/
A data structure is a particular way of organizing data in a computer so that it can be used
effectively.
For example, we can store a list of items having the same data-type using the array data
structure.
https://en.wikipedia.org/wiki/Data_structure
Not to be confused with ’data type’. https://en.wikipedia.org/wiki/Data_type
In computer science, a ’data structure’ is a data organization, management, and storage format
that enables efficient access and modification.[1][2][3] More precisely, a data structure is a collection
of ’data values’, the relationships among them, and the functions or operations that can be applied
to the data.[4]
CAPITOLUL 4. MASIVE DE DATE ÎN C++ 4.3. MATRICE 65

4.3 Matrice
4.3.1 Implementare statică - array
4.3.2 Implementare dinamică - array
4.3.3 Implementare STL - vector¡***¿
Liniarizare matrice si matrice rare

4.4 Structuri complexe de tip vector


4.4.1 Stivă - ’Stacks’
4.4.2 Coadă - ’Queues’
4.4.3 Arbori binari
Arbori binari de căutare ...
Parcurgerea arborilor: RSD, SRD, SDR

4.4.4 Heap

4.5 Tipul ’struct’


4.5.1 Vector-array de structuri

Listing 4.5.1: .cpp


// https://www.geeksforgeeks.org/store-data-triplet-vector-c/

// C++ program to store data triplet in a vector


// using user defined structure.
#include<bits/stdc++.h>
using namespace std;

struct Test
{
int x, y, z;
} v[3];

int main()
{
int n=3;

v[0]={11,12,13};
v[1]={21,22,23};
v[2]={31,32,33};
for (int i=0;i<n;i++)
{
cout << v[i].x << ", " << v[i].y<< ", " << v[i].z << endl;
}

return 0;
}

4.5.2 Matrice-array de structuri


4.5.3 STL vector¡struct¿

Listing 4.5.2: .cpp


// https://www.geeksforgeeks.org/store-data-triplet-vector-c/

// C++ program to store data triplet in a vector


// using user defined structure.
#include<bits/stdc++.h>
using namespace std;
CAPITOLUL 4. MASIVE DE DATE ÎN C++ 4.6. ALOCAREA ÎN MEMORIE PENTRU
STRUCTURILE ’MARI’ 66

struct Test
{
int x, y, z;
};

int main()
{
// Creating a vector of Test

vector<Test> myvec;

// Inserting elements into vector. First


// value is assigned to x, second to y
// and third to z.
myvec.push_back({2, 31, 102});
myvec.push_back({5, 23, 114});
myvec.push_back({9, 10, 158});

int s = myvec.size();
for (int i=0;i<s;i++)
{
// Accessing structure members using their
// names.
cout << myvec[i].x << ", " << myvec[i].y
<< ", " << myvec[i].z << endl;
}
return 0;
}

Listing 4.5.3: .cpp


// https://www.geeksforgeeks.org/store-data-triplet-vector-c/

// C++ program to store data triplet in a vector


// using pair class
#include<bits/stdc++.h>
using namespace std;

int main()
{
// We make a pair with first element as normal
// element and second element as another pair.
// therefore 3 elements simultaneously.
vector< pair<int, pair<int, int> > > myvec;

// For inserting element in pair use


// make_pair().
myvec.push_back(make_pair(2, make_pair(31, 102)));
myvec.push_back(make_pair(5, make_pair(23, 114)));
myvec.push_back(make_pair(9, make_pair(10, 158)));

int s = myvec.size();
for (int i=0; i<s; i++)
{
// The elements can be directly accessed
// according to first or second element
// of the pair.
cout << myvec[i].first << ", " << myvec[i].second.first
<< ", " << myvec[i].second.second << endl;
}
return 0;
}

4.6 Alocarea ı̂n memorie pentru structurile ’mari’


9 101
Time: 10 operations which mean 10 seconds
Memory: ... check amount of memory that is allowed ...
101
https://www.geeksforgeeks.org/overcoming-common-problems-in-competitive-programming
/?ref=rp
CAPITOLUL 4. MASIVE DE DATE ÎN C++ 4.6. ALOCAREA ÎN MEMORIE PENTRU
STRUCTURILE ’MARI’ 67
6 3 3
4 MB = integer array of size 10 (assuming int takes 4 bytes) or 2-d array of size 10 ˜ 10
Standard Memory limits in most of the problem are of the order of 256MB.
If you have to allocate a large array, then it is NOT a good idea to do allocation inside
a function as memory is allocated and released for every test case, and memory is allocated on
the function call stack (stack size is limited at many places). Thus if you have to make an
array of large size, make it global.
Capitolul 5

Funcţii şi recursivitate

5.1 Funcţii - definite de utilizator


5.2 Recursivitate

68
Part II

Olimpiada Europeană de
102
Informatică pentru juniori

102
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.

69
Capitolul 6
103
EJOI 2022

6.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
103
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

70
CAPITOLUL 6. EJOI 2022 6.1. ADJACENT 71

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

6.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 6. EJOI 2022 6.1. ADJACENT 72

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.

6.1.2 Coduri sursă

Listing 6.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 6. EJOI 2022 6.1. ADJACENT 73

//#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 6. EJOI 2022 6.1. ADJACENT 74

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 6.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 6. EJOI 2022 6.2. ROOT 75

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.
*/

6.1.3 *Rezolvare detaliată

6.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 6. EJOI 2022 6.2. ROOT 76

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 6. EJOI 2022 6.2. ROOT 77

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))))

6.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 6. EJOI 2022 6.2. ROOT 78

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.

6.2.2 Coduri sursă

Listing 6.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 6. EJOI 2022 6.2. ROOT 79

#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 6. EJOI 2022 6.2. ROOT 80

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 6.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 6. EJOI 2022 6.3. TREE 81

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");
}
}

6.2.3 *Rezolvare detaliată

6.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 6. EJOI 2022 6.3. TREE 82

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 6. EJOI 2022 6.3. TREE 83

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.

6.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
104
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
104
https://en.wikipedia.org/wiki/Hall%27s_marriage_theorem
CAPITOLUL 6. EJOI 2022 6.3. TREE 84

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
105 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
106
use binary lifting approach or non-trivial heavy-light decomposition approach to do mentioned
updates. Time complexity: O m log n.

6.3.2 Coduri sursă

Listing 6.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);
}
105
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
106
https://www.geeksforgeeks.org/lca-in-a-tree-using-binary-lifting-technique/
. https://codeforces.com/blog/entry/100826
CAPITOLUL 6. EJOI 2022 6.3. TREE 85

}
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 6. EJOI 2022 6.3. TREE 86

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 6. EJOI 2022 6.3. TREE 87

}
return 0;
}

Listing 6.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 6. EJOI 2022 6.4. GAME 88

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");
}
}

6.3.3 *Rezolvare detaliată

6.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 6. EJOI 2022 6.4. GAME 89

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 6. EJOI 2022 6.4. GAME 90

6.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 6. EJOI 2022 6.4. GAME 91

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?

6.4.2 Coduri sursă

Listing 6.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 6. EJOI 2022 6.4. GAME 92

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 6.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 6. EJOI 2022 6.5. PERMUTATIONS 93

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());


}

6.4.3 *Rezolvare detaliată

6.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 6. EJOI 2022 6.5. PERMUTATIONS 94

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 6. EJOI 2022 6.5. PERMUTATIONS 95

6.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 6. EJOI 2022 6.5. PERMUTATIONS 96

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.
107
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
107
https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93Szekeres_theorem
CAPITOLUL 6. EJOI 2022 6.5. PERMUTATIONS 97

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.

6.5.2 Coduri sursă

Listing 6.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 6. EJOI 2022 6.5. PERMUTATIONS 98

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 6. EJOI 2022 6.5. PERMUTATIONS 99

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 6. EJOI 2022 6.5. PERMUTATIONS 100

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 6.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 6. EJOI 2022 6.5. PERMUTATIONS 101

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

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 6. EJOI 2022 6.6. UNFRIENDLY 102

6.5.3 *Rezolvare detaliată

6.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 6. EJOI 2022 6.6. UNFRIENDLY 103

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

6.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 108
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.
108
time limit
CAPITOLUL 6. EJOI 2022 6.6. UNFRIENDLY 104

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.

6.6.2 Coduri sursă

Listing 6.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 6. EJOI 2022 6.6. UNFRIENDLY 105

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 6.6.2: EJOI2022 LCS check.cpp


CAPITOLUL 6. EJOI 2022 6.6. UNFRIENDLY 106

#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 6. EJOI 2022 6.6. UNFRIENDLY 107

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.
*/

6.6.3 *Rezolvare detaliată


Capitolul 7
109
EJOI 2021

7.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.
109
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

108
CAPITOLUL 7. EJOI 2021 7.1. ADDK 109

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.

7.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.

7.1.2 *Coduri sursă

***
CAPITOLUL 7. EJOI 2021 7.2. KPART 110

7.1.3 *Rezolvare detaliată

7.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.

7.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 7. EJOI 2021 7.3. XCOPY 111

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.

7.2.2 *Coduri sursă

***

7.2.3 *Rezolvare detaliată

7.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 7. EJOI 2021 7.3. XCOPY 112

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 7. EJOI 2021 7.3. XCOPY 113

7.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 7. EJOI 2021 7.3. XCOPY 114

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.

7.3.2 *Coduri sursă

***
CAPITOLUL 7. EJOI 2021 7.4. BINSEARCH 115

7.3.3 *Rezolvare detaliată

7.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 7. EJOI 2021 7.4. BINSEARCH 116

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.

7.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 7. EJOI 2021 7.4. BINSEARCH 117

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 7. EJOI 2021 7.5. DUNGEONS 118

– 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.

7.4.2 *Coduri sursă

***

7.4.3 *Rezolvare detaliată

7.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 7. EJOI 2021 7.5. DUNGEONS 119

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 7. EJOI 2021 7.5. DUNGEONS 120

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 7. EJOI 2021 7.5. DUNGEONS 121

7.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 7. EJOI 2021 7.6. WATERFRONT 122

7.5.2 *Coduri sursă

***

7.5.3 *Rezolvare detaliată

7.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 7. EJOI 2021 7.6. WATERFRONT 123

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

7.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 7. EJOI 2021 7.6. WATERFRONT 124

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.

7.6.2 *Coduri sursă

***

7.6.3 *Rezolvare detaliată


Capitolul 8
110
EJOI 2020

8.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
110
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.

125
CAPITOLUL 8. EJOI 2020 8.1. FOUNTAIN 126

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

8.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 8. EJOI 2020 8.1. FOUNTAIN 127

ˆ 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.

8.1.2 Coduri sursă

Listing 8.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 8.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 8. EJOI 2020 8.1. FOUNTAIN 128

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 8. EJOI 2020 8.1. FOUNTAIN 129

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 8.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 8. EJOI 2020 8.1. FOUNTAIN 130

Listing 8.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 8. EJOI 2020 8.1. FOUNTAIN 131

Listing 8.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 8. EJOI 2020 8.1. FOUNTAIN 132

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 8. EJOI 2020 8.1. FOUNTAIN 133

init();
solve();
return 0;
}

Listing 8.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 8. EJOI 2020 8.1. FOUNTAIN 134

}
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 8.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 8. EJOI 2020 8.1. FOUNTAIN 135

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 8.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 8. EJOI 2020 8.1. FOUNTAIN 136

{
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 8. EJOI 2020 8.1. FOUNTAIN 137

while (Q--)
{
pos = R();
val = R();
ans = find(pos, val);

if (ans < N)
printf("%d\n", ans);
else
puts("0");
}

return 0;
}

Listing 8.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 8. EJOI 2020 8.2. TRIANGULATION 138

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;
}

8.1.3 *Rezolvare detaliată

8.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 8. EJOI 2020 8.2. TRIANGULATION 139

ˆ 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 8.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 8. EJOI 2020 8.2. TRIANGULATION 140

8.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.

8.2.2 Coduri sursă

Listing 8.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 8. EJOI 2020 8.2. TRIANGULATION 141

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 8. EJOI 2020 8.2. TRIANGULATION 142

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 8.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 8. EJOI 2020 8.2. TRIANGULATION 143

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

#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 8. EJOI 2020 8.2. TRIANGULATION 144

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 8. EJOI 2020 8.2. TRIANGULATION 145

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 8.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 8. EJOI 2020 8.2. TRIANGULATION 146

{
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 8. EJOI 2020 8.2. TRIANGULATION 147

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 8.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 8. EJOI 2020 8.2. TRIANGULATION 148

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 8. EJOI 2020 8.2. TRIANGULATION 149

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 8.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 8. EJOI 2020 8.2. TRIANGULATION 150

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 8. EJOI 2020 8.3. EXAM 151

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;
}

8.2.3 *Rezolvare detaliată

8.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 8. EJOI 2020 8.3. EXAM 152

ˆ 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

8.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 8. EJOI 2020 8.3. EXAM 153

ˆ 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.

8.3.2 Coduri sursă

Listing 8.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 8. EJOI 2020 8.3. EXAM 154

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 8.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 8. EJOI 2020 8.3. EXAM 155

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 8. EJOI 2020 8.3. EXAM 156

Listing 8.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 8. EJOI 2020 8.3. EXAM 157

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 8.3.4: exam-985120.cpp


// https://loj.ac/s/985120 636 ms 6.7 M

#include <bits/stdc++.h>

using namespace std;


CAPITOLUL 8. EJOI 2020 8.3. EXAM 158

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 8. EJOI 2020 8.3. EXAM 159

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 8. EJOI 2020 8.3. EXAM 160

if (L[i])
modify(L[i], l + 1);

if (R[i])
modify(R[i], r + 1);
}

printf("%d", query(n));

return 0;
}

Listing 8.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 8. EJOI 2020 8.3. EXAM 161

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 8. EJOI 2020 8.3. EXAM 162

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 8.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 8. EJOI 2020 8.3. EXAM 163

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 8. EJOI 2020 8.4. XORSORT 164

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;
}

8.3.3 *Rezolvare detaliată

8.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 8. EJOI 2020 8.4. XORSORT 165

ˆ 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

8.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 8. EJOI 2020 8.4. XORSORT 166

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 8. EJOI 2020 8.4. XORSORT 167

8.4.2 Coduri sursă

Listing 8.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 8. EJOI 2020 8.4. XORSORT 168

Listing 8.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 8. EJOI 2020 8.4. XORSORT 169

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 8. EJOI 2020 8.4. XORSORT 170

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 8.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 8. EJOI 2020 8.4. XORSORT 171

{
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 8. EJOI 2020 8.5. CARDS 172

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 8.4.4: xorsort-986772.cpp

8.4.3 *Rezolvare detaliată

8.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 8. EJOI 2020 8.5. CARDS 173

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 8. EJOI 2020 8.5. CARDS 174

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 8.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

8.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 8. EJOI 2020 8.6. GAME 175

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.

8.5.2 *Coduri sursă

***

8.5.3 *Rezolvare detaliată

8.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 8. EJOI 2020 8.6. GAME 176

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 8. EJOI 2020 8.6. GAME 177

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

8.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 8. EJOI 2020 8.6. GAME 178

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.

8.6.2 *Coduri sursă

***

8.6.3 *Rezolvare detaliată


Capitolul 9
111
EJOI 2019

9.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
ˆ

111
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.

179
CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 180

ˆ 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

9.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.

9.1.2 Coduri sursă

Listing 9.1.1: 216711-rack.cpp


//https://oj.uz/submission/216711

#include <bits/stdc++.h>

using namespace std;

int MOD = 1e9 + 7;

int main()
CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 181

{
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 9.1.2: 217942-rack.cpp


// https://oj.uz/submission/217942
/*
ID: Sho10
LANG: C++
*/
112
#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;
112
https://oj.uz/profile/Sho10
CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 182

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 9.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 9. EJOI 2019 9.1. HANGING RACK 183

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 9.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 9. EJOI 2019 9.1. HANGING RACK 184

}
else
{
k/=2;
start+=expo(2,n,mod);
start%=mod;
}

n--;
}

cout<<start<<endl;
}

Listing 9.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 9.1.6: 237579-rack.cpp


// https://oj.uz/submission/237579

#include <iostream>
#include <vector>
#include <set>
#include <map>
CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 185

#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: 113
Listing 9.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
113
http://www.cplusplus.com/doc/tutorial/operators/
CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 186

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 9.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 9.1.9: 237671-rack.cpp


// https://oj.uz/submission/237671

#include <bits/stdc++.h>

using namespace std;

const int64_t MOD = 1000000007;


CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 187

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;
}

114
OBS: int64 t,¡cstdint¿

Listing 9.1.10: 240337-rack.cpp


// https://oj.uz/submission/240337

#include <bits/stdc++.h>

using namespace std;

114
http://www.cplusplus.com/reference/cstdint/
CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 188

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 9.1.11: 242103-rack.cpp


CAPITOLUL 9. EJOI 2019 9.1. HANGING RACK 189

// 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 9.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 9. EJOI 2019 9.2. XORANGES 190

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;
}

115
OBS: ios base::sync with stdio(0);

9.1.3 *Rezolvare detaliată

9.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:
115
https://www.geeksforgeeks.org/fast-io-for-competitive-programming/
. https://codeforces.com/blog/entry/18093
CAPITOLUL 9. EJOI 2019 9.2. XORANGES 191

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 9.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 9. EJOI 2019 9.2. XORANGES 192

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

9.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.

9.2.2 Coduri sursă

Listing 9.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 9. EJOI 2019 9.2. XORANGES 193

#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 9. EJOI 2019 9.2. XORANGES 194

{
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 9.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 9. EJOI 2019 9.2. XORANGES 195

{
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 9.2.3: 229737-XORanges.cpp


// https://oj.uz/submission/229737
CAPITOLUL 9. EJOI 2019 9.2. XORANGES 196

#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 9. EJOI 2019 9.2. XORANGES 197

return 0;
}

Listing 9.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 9. EJOI 2019 9.2. XORANGES 198

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 9.2.5: 237576-XORanges.cpp


CAPITOLUL 9. EJOI 2019 9.2. XORANGES 199

// 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 9. EJOI 2019 9.2. XORANGES 200

{
b--;
if(a % 2 == b % 2)
{
cout << rxq(gi(a), gi(b) + 1) << "\n";
}
else
{
cout << "0\n";
}
}
}
}

Listing 9.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 9. EJOI 2019 9.2. XORANGES 201

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 9.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 9. EJOI 2019 9.2. XORANGES 202

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 9. EJOI 2019 9.2. XORANGES 203

return 0;
}

Listing 9.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 9. EJOI 2019 9.2. XORANGES 204

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 9.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 9. EJOI 2019 9.2. XORANGES 205

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 9. EJOI 2019 9.3. T - COVERING 206

}
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 9.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();
}

116
OBS: #include ”testlib.h”

9.2.3 *Rezolvare detaliată

9.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.
116
https://github.com/MikeMirzayanov/testlib
CAPITOLUL 9. EJOI 2019 9.3. T - COVERING 207

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 9. EJOI 2019 9.3. T - COVERING 208

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

9.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 9. EJOI 2019 9.3. T - COVERING 209

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.

9.3.2 Coduri sursă

Listing 9.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 9. EJOI 2019 9.3. T - COVERING 210

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 9. EJOI 2019 9.3. T - COVERING 211

}
}
}

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 9.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 9. EJOI 2019 9.3. T - COVERING 212

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 9. EJOI 2019 9.4. ADVENTURE 213

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;
}

9.3.3 *Rezolvare detaliată

9.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 9. EJOI 2019 9.4. ADVENTURE 214

ˆ 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 9. EJOI 2019 9.4. ADVENTURE 215

Timp maxim de executare/test: 2.0 secunde


Memorie: total 100 MB

9.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.

9.4.2 Coduri sursă

Listing 9.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 9. EJOI 2019 9.4. ADVENTURE 216

*/
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 9. EJOI 2019 9.4. ADVENTURE 217

Listing 9.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 9. EJOI 2019 9.4. ADVENTURE 218

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 9.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 9. EJOI 2019 9.4. ADVENTURE 219

#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 9. EJOI 2019 9.4. ADVENTURE 220

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 9. EJOI 2019 9.4. ADVENTURE 221

ll j = n-1;
if(dist[j*m + i] >= INF)
{
pl(-1);
return 0;
}

pl(dist[j*m + i]);
}

Listing 9.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 9. EJOI 2019 9.4. ADVENTURE 222

\index{priority\_queue!pop()}
\index{priority\_queue!make\_pair}

Listing 9.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 9. EJOI 2019 9.4. ADVENTURE 223

return 0;
}

Listing 9.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 9. EJOI 2019 9.4. ADVENTURE 224

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 9.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 9. EJOI 2019 9.4. ADVENTURE 225

#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 9. EJOI 2019 9.4. ADVENTURE 226

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 9.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 9. EJOI 2019 9.4. ADVENTURE 227

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 9.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 9. EJOI 2019 9.4. ADVENTURE 228

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 9. EJOI 2019 9.4. ADVENTURE 229

{
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 9. EJOI 2019 9.5. TOWER 230

9.4.3 *Rezolvare detaliată

9.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 9. EJOI 2019 9.5. TOWER 231

ˆ 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

9.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 9. EJOI 2019 9.6. COLOURING 232

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.

9.5.2 *Coduri sursă

***

9.5.3 *Rezolvare detaliată

9.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 9. EJOI 2019 9.6. COLOURING 233

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 9. EJOI 2019 9.6. COLOURING 234

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

9.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.

9.6.2 Coduri sursă

Listing 9.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 9. EJOI 2019 9.6. COLOURING 235

#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 9.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 9. EJOI 2019 9.6. COLOURING 236

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 9. EJOI 2019 9.6. COLOURING 237

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 9. EJOI 2019 9.6. COLOURING 238

{
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 9.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 9. EJOI 2019 9.6. COLOURING 239

{ 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 9. EJOI 2019 9.6. COLOURING 240

{ 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!

9.6.3 *Rezolvare detaliată


Capitolul 10
117
EJOI 2018

10.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
117
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.

241
CAPITOLUL 10. EJOI 2018 10.1. HILLS 242

ˆ (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

10.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 .

10.1.2 Coduri sursă


CAPITOLUL 10. EJOI 2018 10.1. HILLS 243

Listing 10.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 10. EJOI 2018 10.1. HILLS 244

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 10.1.2: ds n2 other.cpp


// 2018, Sayutin Dmitry. // execution time : 3.255 s

#include <bits/stdc++.h>
CAPITOLUL 10. EJOI 2018 10.1. HILLS 245

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 10. EJOI 2018 10.1. HILLS 246

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 10.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 10. EJOI 2018 10.1. HILLS 247

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 10. EJOI 2018 10.1. HILLS 248

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 10.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 10. EJOI 2018 10.1. HILLS 249

}
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 10.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 10. EJOI 2018 10.1. HILLS 250

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 10.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 10. EJOI 2018 10.1. HILLS 251

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 10.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 10.1.8: 81469343-hills.cpp


CAPITOLUL 10. EJOI 2018 10.1. HILLS 252

// 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 10.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 10. EJOI 2018 10.1. HILLS 253

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 10. EJOI 2018 10.1. HILLS 254

Things to look out for:


- Integer overflows
- Array bounds
- Special cases
Be careful!
*/

Listing 10.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 10.1.11: checkerHills.cpp


#include "testlib.h"

using namespace std;

int main()
CAPITOLUL 10. EJOI 2018 10.2. PASSPORTS 255

//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

10.1.3 *Rezolvare detaliată

10.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 10. EJOI 2018 10.2. PASSPORTS 256

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 10. EJOI 2018 10.2. PASSPORTS 257

✶ ✷ ✸ ✹ ✺ ✻ ✼

'✁✂✄ ☎ ✆✝✞✟ ✠ ✡☛☞✌ ✍ ✎✏✑✒ ✓

Example 2:

✶ ✷ ✸ ✹ ✺ ✻ ✼ ✽ ✾ )✁ ✂✄ ☎✆ ✝✞ ✟✠ ✡☛ ☞✌ ✍✎ ✏✑ ✒✓ ✔✕ ✖✗ ✘✙

✚✛✜✢ ✣ ✤✥✦✧ ★ ✩✪✫✬ ✭ ✮✯✰✱ ✲ ✳✴✵✿ ❀ ❁❂❃❄ ❅

Example 3:

♠♥♦♣‘’“” ✉

❏❑▲▼ ◆ ❝❞❡❢ ❣ ❤✐❥❦ ❧ ❊❋●❍ ■ ❚❯❱❲ ❳ ❖p◗❘ ❙ ❫❴❵❛ ❜ ❨❩❬❭ ❪

✶ ✷ ✸ ✹ ✺ ✻ ✼ ✽ ✾ )✁ ✂✄ ☎✆ ✝✞ ✟✠ ✡☛ ☞✌ ✍✎ ✏✑ ✒✓ ✔✕ ✖✗ ✘✙ ✚✛ ✜✢

❅❆❇❈ ❉ ❀❁❂❃ ❄ ★✩✪✫ ✬ ✣✤✥✦ ✧ ✲✳✴✵ ✿ ✭✮✯✰ ✱


✈✇①②③④⑤⑥ ⑦

Timp maxim de executare/test: 2.0 secunde


Memorie: total 512 MB

10.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 10. EJOI 2018 10.2. PASSPORTS 258

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.

10.2.2 Coduri sursă

Listing 10.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 10. EJOI 2018 10.2. PASSPORTS 259

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 10. EJOI 2018 10.2. PASSPORTS 260

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 10.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 10. EJOI 2018 10.2. PASSPORTS 261

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 10. EJOI 2018 10.2. PASSPORTS 262

Listing 10.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 10. EJOI 2018 10.2. PASSPORTS 263

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 10.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 10. EJOI 2018 10.2. PASSPORTS 264

{
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 10. EJOI 2018 10.2. PASSPORTS 265

{
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 10.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 10. EJOI 2018 10.2. PASSPORTS 266

{
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 10. EJOI 2018 10.2. PASSPORTS 267

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 10.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 10. EJOI 2018 10.2. PASSPORTS 268

//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 10.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 10. EJOI 2018 10.2. PASSPORTS 269

{
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 10. EJOI 2018 10.2. PASSPORTS 270

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 10.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 10. EJOI 2018 10.2. PASSPORTS 271

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 10. EJOI 2018 10.2. PASSPORTS 272

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 273

for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("passports", argc, argv);


compareRemainingLines();
}

10.2.3 *Rezolvare detaliată

10.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 10. EJOI 2018 10.3. AB-STRINGS 274

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

10.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 10. EJOI 2018 10.3. AB-STRINGS 275

10.3.2 Coduri sursă

Listing 10.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 10. EJOI 2018 10.3. AB-STRINGS 276

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 10. EJOI 2018 10.3. AB-STRINGS 277

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 278

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 10. EJOI 2018 10.3. AB-STRINGS 279

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 10. EJOI 2018 10.3. AB-STRINGS 280

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 281

}
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 10. EJOI 2018 10.3. AB-STRINGS 282

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 10. EJOI 2018 10.3. AB-STRINGS 283

\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 10. EJOI 2018 10.3. AB-STRINGS 284

\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 10. EJOI 2018 10.3. AB-STRINGS 285

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 10. EJOI 2018 10.3. AB-STRINGS 286

%\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 10. EJOI 2018 10.3. AB-STRINGS 287

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 10. EJOI 2018 10.3. AB-STRINGS 288

ans.push_back(mp(cq[i], qq[0].se));

cout << sz(ans) << "\n";


}

Listing 10.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 10. EJOI 2018 10.3. AB-STRINGS 289

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 290

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 291

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 292

#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 10. EJOI 2018 10.3. AB-STRINGS 293

/*for (auto u : ans) {


printf("%d %d\n", u.first, u.second);
}*/
return 0;
}

Listing 10.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 10.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 10. EJOI 2018 10.3. AB-STRINGS 294

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 295

{
if (p[i] == i)
++res;
}

printf("%d\n", res - 1);

return 0;
}

Listing 10.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 10. EJOI 2018 10.3. AB-STRINGS 296

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 297

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 298

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 299

#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 10.3.15: 87201594-chemistry.cpp


CAPITOLUL 10. EJOI 2018 10.3. AB-STRINGS 300

// 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 10.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 10. EJOI 2018 10.3. AB-STRINGS 301

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 10. EJOI 2018 10.3. AB-STRINGS 302

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 10. EJOI 2018 10.3. AB-STRINGS 303

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 10.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 10. EJOI 2018 10.3. AB-STRINGS 304

// #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 10. EJOI 2018 10.3. AB-STRINGS 305

cout<<comps-1<<"\n";
}

return 0;
}

Listing 10.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 10. EJOI 2018 10.3. AB-STRINGS 306

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;


}

118
mt19937

Listing 10.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;

118
http://www.cplusplus.com/reference/random/mt19937/
CAPITOLUL 10. EJOI 2018 10.3. AB-STRINGS 307

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 10.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 10.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 10. EJOI 2018 10.4. PRIME TREE 308

{
(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);
}

10.3.3 *Rezolvare detaliată

10.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 10. EJOI 2018 10.4. PRIME TREE 309

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 10. EJOI 2018 10.4. PRIME TREE 310

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

10.4.1 *Indicaţii de rezolvare

***

10.4.2 Coduri sursă


CAPITOLUL 10. EJOI 2018 10.4. PRIME TREE 311

Listing 10.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 10. EJOI 2018 10.4. PRIME TREE 312

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 10.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 10. EJOI 2018 10.4. PRIME TREE 313

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();
}

119
minstd rand
120
shuffle
121
uniform int distribution
122
gcd
Listing 10.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()
119
http://www.cplusplus.com/reference/random/minstd_rand/
120
http://www.cplusplus.com/reference/algorithm/shuffle/
121
https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution
122
https://www.geeksforgeeks.org/stdgcd-c-inbuilt-function-finding-gcd/
CAPITOLUL 10. EJOI 2018 10.4. PRIME TREE 314

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 10. EJOI 2018 10.4. PRIME TREE 315

{
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;
}

123
emplace back

Listing 10.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
123
http://candcplusplus.com/c-difference-between-emplace_back-and-push_back-function
CAPITOLUL 10. EJOI 2018 10.4. PRIME TREE 316

#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 10. EJOI 2018 10.4. PRIME TREE 317

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 10.4.5: prime tree pashka.cpp


CAPITOLUL 10. EJOI 2018 10.4. PRIME TREE 318

#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 10. EJOI 2018 10.4. PRIME TREE 319

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 10. EJOI 2018 10.4. PRIME TREE 320

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;
}

124
insert

Listing 10.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;
124
http://www.cplusplus.com/reference/vector/vector/insert/
CAPITOLUL 10. EJOI 2018 10.4. PRIME TREE 321

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 10. EJOI 2018 10.4. PRIME TREE 322

{
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 10.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 10. EJOI 2018 10.4. PRIME TREE 323

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 10. EJOI 2018 10.4. PRIME TREE 324

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 10. EJOI 2018 10.4. PRIME TREE 325

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 10.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 10. EJOI 2018 10.4. PRIME TREE 326

{
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 10. EJOI 2018 10.4. PRIME TREE 327

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 10. EJOI 2018 10.4. PRIME TREE 328

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 10. EJOI 2018 10.4. PRIME TREE 329

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 10.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 10. EJOI 2018 10.4. PRIME TREE 330

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 10. EJOI 2018 10.4. PRIME TREE 331

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 10.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 10. EJOI 2018 10.4. PRIME TREE 332

{
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 10. EJOI 2018 10.4. PRIME TREE 333

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 10.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 10. EJOI 2018 10.4. PRIME TREE 334

(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 10. EJOI 2018 10.5. CYCLE SORT 335

{
quitp(grade.second, "X=%d, M=%d, R=%.4f", x, m, score);
}
}
}

assert(false);
}

10.4.3 *Rezolvare detaliată

10.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 10. EJOI 2018 10.5. CYCLE SORT 336

ˆ 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

10.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.
125
It’s known that each permutation is composition of some non-intersecting cycles.
If permutation is sorted answer is 0.
125
https://en.wikipedia.org/wiki/Cyclic_permutation
CAPITOLUL 10. EJOI 2018 10.5. CYCLE SORT 337

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 .
126
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)

10.5.2 Coduri sursă

Listing 10.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
126
https://cp-algorithms.com/data_structures/disjoint_set_union.html
CAPITOLUL 10. EJOI 2018 10.5. CYCLE SORT 338

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 10. EJOI 2018 10.5. CYCLE SORT 339

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 10.5.2: gr ok.cpp


CAPITOLUL 10. EJOI 2018 10.5. CYCLE SORT 340

#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 10. EJOI 2018 10.5. CYCLE SORT 341

{
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 10. EJOI 2018 10.5. CYCLE SORT 342

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 10. EJOI 2018 10.5. CYCLE SORT 343

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 10.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 10. EJOI 2018 10.5. CYCLE SORT 344

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 10. EJOI 2018 10.5. CYCLE SORT 345

{
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 10. EJOI 2018 10.5. CYCLE SORT 346

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 10. EJOI 2018 10.5. CYCLE SORT 347

perm[ret[i]] = ret[j];
}
}
}

print(n, s, perm);
}

Listing 10.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 10. EJOI 2018 10.5. CYCLE SORT 348

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 10.5.5: isaf ok slowio.cpp


CAPITOLUL 10. EJOI 2018 10.5. CYCLE SORT 349

#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 10. EJOI 2018 10.5. CYCLE SORT 350

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 10.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 10. EJOI 2018 10.5. CYCLE SORT 351

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 10. EJOI 2018 10.5. CYCLE SORT 352

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 10.5.7: 41148878-cycle sort.cpp


CAPITOLUL 10. EJOI 2018 10.5. CYCLE SORT 353

// 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 10. EJOI 2018 10.5. CYCLE SORT 354

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 10.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 10. EJOI 2018 10.5. CYCLE SORT 355

{
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 10. EJOI 2018 10.5. CYCLE SORT 356

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 10. EJOI 2018 10.5. CYCLE SORT 357

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 10.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 10. EJOI 2018 10.5. CYCLE SORT 358

// 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 10. EJOI 2018 10.5. CYCLE SORT 359

{
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 10. EJOI 2018 10.5. CYCLE SORT 360

{
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 10.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 10. EJOI 2018 10.5. CYCLE SORT 361

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 10. EJOI 2018 10.5. CYCLE SORT 362

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 10.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 10. EJOI 2018 10.5. CYCLE SORT 363

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 10. EJOI 2018 10.5. CYCLE SORT 364

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 10.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 10. EJOI 2018 10.5. CYCLE SORT 365

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;
}

10.5.3 *Rezolvare detaliată


Capitolul 11
127
EJOI 2017

11.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


127
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.

366
CAPITOLUL 11. EJOI 2017 11.1. MAGIC 367

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.

11.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 11. EJOI 2017 11.1. MAGIC 368

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.

11.1.2 Coduri sursă

Listing 11.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 11. EJOI 2017 11.1. MAGIC 369

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 11.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 11. EJOI 2017 11.1. MAGIC 370

#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 11.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 11. EJOI 2017 11.1. MAGIC 371

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 11.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 11. EJOI 2017 11.1. MAGIC 372

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 11.1.5: 484817-magic.cpp


CAPITOLUL 11. EJOI 2017 11.1. MAGIC 373

// 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 11.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 11. EJOI 2017 11.1. MAGIC 374

// 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 11. EJOI 2017 11.1. MAGIC 375

{
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 11. EJOI 2017 11.1. MAGIC 376

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
128
final parameter in the template parameter list.

Listing 11.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);
128
https://en.cppreference.com/w/cpp/language/parameter_pack
CAPITOLUL 11. EJOI 2017 11.1. MAGIC 377

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 11.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 11. EJOI 2017 11.1. MAGIC 378

{
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 11.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 11. EJOI 2017 11.2. PARTICLES 379

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;
}

11.1.3 *Rezolvare detaliată

11.2 Particles
Problema 2 - Particles 100 de puncte
CAPITOLUL 11. EJOI 2017 11.2. PARTICLES 380

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 11. EJOI 2017 11.2. PARTICLES 381

11.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 11. EJOI 2017 11.2. PARTICLES 382

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.

11.2.2 Coduri sursă

Listing 11.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 11. EJOI 2017 11.2. PARTICLES 383

if(MX > MY)


hi = mi;
else
{
printf("%d %d\n",IDX, IDY);
fx[IDX] = fy[IDY] = true;
break;
}
}
}

return 0;
}

Listing 11.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 11. EJOI 2017 11.2. PARTICLES 384

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 11.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 11. EJOI 2017 11.2. PARTICLES 385

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 11.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 11. EJOI 2017 11.2. PARTICLES 386

#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 11. EJOI 2017 11.2. PARTICLES 387

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 11.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 11. EJOI 2017 11.2. PARTICLES 388

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 11.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 11. EJOI 2017 11.2. PARTICLES 389

{
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 11.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 11. EJOI 2017 11.2. PARTICLES 390

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 11.2.8: 1628810-particles.cpp


CAPITOLUL 11. EJOI 2017 11.2. PARTICLES 391

// 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 11.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 11. EJOI 2017 11.3. SIX 392

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("particles", argc, argv);


compareRemainingLines();
}

11.2.3 *Rezolvare detaliată

11.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 11. EJOI 2017 11.3. SIX 393

(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.

11.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 11. EJOI 2017 11.3. SIX 394

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.

11.3.2 Coduri sursă

Listing 11.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 11. EJOI 2017 11.3. SIX 395

/*
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 11. EJOI 2017 11.3. SIX 396

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 11.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 11. EJOI 2017 11.3. SIX 397

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 11. EJOI 2017 11.3. SIX 398

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 11. EJOI 2017 11.3. SIX 399

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 11. EJOI 2017 11.3. SIX 400

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 11. EJOI 2017 11.3. SIX 401

{
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 11. EJOI 2017 11.3. SIX 402

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;
}

129
OBS1: strong pseudo prime
vspace2mm
130
OBS2: Pollard method

Listing 11.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>
129
https://en.wikipedia.org/wiki/Strong_pseudoprime
130
https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
CAPITOLUL 11. EJOI 2017 11.3. SIX 403

#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 11. EJOI 2017 11.3. SIX 404

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 11. EJOI 2017 11.3. SIX 405

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);
}

131
OBS: Miller-Rabin primality test

Listing 11.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)
{
131
https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
CAPITOLUL 11. EJOI 2017 11.3. SIX 406

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 11. EJOI 2017 11.3. SIX 407

//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 11.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 11. EJOI 2017 11.3. SIX 408

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 11.3.6: 1709178-six.cpp


CAPITOLUL 11. EJOI 2017 11.3. SIX 409

// 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 11. EJOI 2017 11.3. SIX 410

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 11.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 11. EJOI 2017 11.3. SIX 411

#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 11. EJOI 2017 11.3. SIX 412

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 11. EJOI 2017 11.3. SIX 413

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 11.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 11. EJOI 2017 11.4. CAMEL 414

{
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));


}

132
OBS: hash

11.3.3 *Rezolvare detaliată

11.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
132
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 11. EJOI 2017 11.4. CAMEL 415

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 11. EJOI 2017 11.4. CAMEL 416

11.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 11. EJOI 2017 11.4. CAMEL 417

11.4.2 Coduri sursă

Listing 11.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 11. EJOI 2017 11.4. CAMEL 418

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 11. EJOI 2017 11.4. CAMEL 419

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 11.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 11. EJOI 2017 11.4. CAMEL 420

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 11. EJOI 2017 11.4. CAMEL 421

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 11. EJOI 2017 11.4. CAMEL 422

k+=25;
}
}/////

for(i=0;i<n;i++)
{
for(a=0;a<n;a++)
{
printf("%d ",d[i][a]);
}
cout<<endl;
}
}

Listing 11.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 11. EJOI 2017 11.4. CAMEL 423

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 11. EJOI 2017 11.4. CAMEL 424

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 11. EJOI 2017 11.4. CAMEL 425

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 11. EJOI 2017 11.4. CAMEL 426

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 11.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
{
133
(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}
133
t=t*10+ch-’0’;
CAPITOLUL 11. EJOI 2017 11.4. CAMEL 427

};

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 11.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 11. EJOI 2017 11.4. CAMEL 428

#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 11. EJOI 2017 11.4. CAMEL 429

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 11. EJOI 2017 11.4. CAMEL 430

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 11. EJOI 2017 11.4. CAMEL 431

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 11.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 11. EJOI 2017 11.4. CAMEL 432

{-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 11. EJOI 2017 11.5. EXPERIENCE 433

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;
}

11.4.3 *Rezolvare detaliată

11.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 11. EJOI 2017 11.5. EXPERIENCE 434

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 11. EJOI 2017 11.5. EXPERIENCE 435

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}.

11.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 11. EJOI 2017 11.5. EXPERIENCE 436

ˆ 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 11. EJOI 2017 11.5. EXPERIENCE 437

11.5.2 Coduri sursă

Listing 11.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 11. EJOI 2017 11.5. EXPERIENCE 438

solve();
return 0;
}

Listing 11.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 11. EJOI 2017 11.5. EXPERIENCE 439

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 11.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 11. EJOI 2017 11.5. EXPERIENCE 440

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();
}

134
OBS: max element returnează iterator, *max element returnează valoare!

Listing 11.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)
134
https://en.cppreference.com/w/cpp/algorithm/max_element
CAPITOLUL 11. EJOI 2017 11.5. EXPERIENCE 441

{
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 11.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 11. EJOI 2017 11.5. EXPERIENCE 442

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 11.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 11. EJOI 2017 11.5. EXPERIENCE 443

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 11.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 11. EJOI 2017 11.5. EXPERIENCE 444

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 11.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 11. EJOI 2017 11.5. EXPERIENCE 445

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 11.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 11. EJOI 2017 11.5. EXPERIENCE 446

{
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 11.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 11. EJOI 2017 11.5. EXPERIENCE 447

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 11. EJOI 2017 11.5. EXPERIENCE 448

cout << max(ans[1].v0, max(ans[1].v1, ans[1].v2)) << ’\n’;

return 0;
}

Listing 11.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 11. EJOI 2017 11.6. GAME 449

dfs(0);

cout << max(dp[0] , pd[0]) << endl;


}

Listing 11.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();
}

11.5.3 *Rezolvare detaliată

11.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 11. EJOI 2017 11.6. GAME 450

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}.

11.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 11. EJOI 2017 11.6. GAME 451

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 .

11.6.2 Coduri sursă

Listing 11.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 11. EJOI 2017 11.6. GAME 452

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 11.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 11. EJOI 2017 11.6. GAME 453

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 11.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 11. EJOI 2017 11.6. GAME 454

#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 11.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 11. EJOI 2017 11.6. GAME 455

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 11.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 11. EJOI 2017 11.6. GAME 456

//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 11.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 11. EJOI 2017 11.6. GAME 457

// 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 11.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 11. EJOI 2017 11.6. GAME 458

{
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 11.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 11. EJOI 2017 11.6. GAME 459

{
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 11.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 11. EJOI 2017 11.6. GAME 460

{
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 11.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 11. EJOI 2017 11.6. GAME 461

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 11.6.11: checkerGame.cpp


#include "testlib0.h"

using namespace std;


CAPITOLUL 11. EJOI 2017 11.6. GAME 462

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();
}

11.6.3 *Rezolvare detaliată


463
Appendix A

Programa olimpiadei - gimnaziu

A.1 Clasa a V-a


- Elemente de bază ale limbajului de programare;
- Tipuri simple de date (ex.: int, long long, char);
- Structurile liniară, alternativă şi repetitivă;
- Algoritmi care prelucrează tipuri simple de date;
- Fişiere text;
- Tablouri unidimensionale (vectori) - numai pentru etapa naţională.

A.2 Clasa a VI-a


- Tipuri reale (ex.: float, double);
- Algoritmi care folosesc tipuri simple şi tablouri unidimensionale;
- Tablouri bidimensionale (matrice) - numai pentru etapa naţonală.

A.3 Clasa a VII-a


- Tipuri structurate;

- Algoritmi care prelucrează tipuri structurate de date;


- Prelucrarea şirurilor de caractere - numai pentru etapa naţională.

A.4 Clasa a VIII-a


- Funcţii (subprograme) definite de utilizator;
- Probleme de geometrie plană ı̂ntr-un sistem de coordonate carteziene;

- Probleme de combinatoric˘(ex.: permutări, submulţimi) - numai pentru etapa naţională.

A.5 Observaţie
Pentru clasele a VI-a - a VIII-a, programa de concurs corespunzătoare clasei se completează cu
programele claselor anterioare.

464
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.

465
APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 466

Kitul Kit_OJI_2017 se instalează implicit pe C:¯OJI¯


135
t IDE-ul Code::Blocks ,
t compilatorul pentru C: gcc.exe,
136
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
135
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
136
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 467

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 468

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 469

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 470

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 471

Figura B.12: 0 error(s), 0 warning(s)

Figura B.13: Run - execuţie


APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 472

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 473

Figura B.16: Toolchain executables

Figura B.17: Unde sunt acele programe


APPENDIX B. ”INSTALARE” C++ B.1. KIT OJI 2017 474

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 475

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 476

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 477

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 478

Figura B.25: Calea şi versiunea pentru gcc

Dacă totul este OK atunci se trece la instalarea IDE-ului preferat (Integrated Development
137
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!).
137
https://en.wikipedia.org/wiki/Integrated_development_environment
https://ro.wikipedia.org/wiki/Mediu_de_dezvoltare
APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 479

Figura B.26: Settings –¿ Compiler

Figura B.27: Toolchain executables –¿ Auto-detect


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 480

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 481

Figura B.31: Moore apps

Figura B.32: Look for another app


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 482

Figura B.33: Cale pentru codeblocks.exe

Figura B.34: Selectare codeblocks.exe


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 483

Figura B.35: Editare test01.cpp

Figura B.36: Compilare test01


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 484

Figura B.37: Mesaje după compilare

Figura B.38: Execuţie test01


APPENDIX B. ”INSTALARE” C++ B.2. WINLIBS 485

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 486

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 487

Figura B.45: Selectare tab ce conţine test01.cpp


Appendix C

Exponenţiere rapidă

C.1 Analogie baza 2 cu baza 10

Figura C.1: Analogie B2 cu B10

În figura C.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)

488
APPENDIX C. EXPONENŢIERE RAPIDĂ C.2. NOTAŢII, RELAŢII ŞI FORMULE 489

C.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;
„
„
„
‚
„ (C.2.1)
„
„
„
„
„nk nk1 ©2 k ' 1; nk1 j 0 desigur ! ... dar şi nk1 j 1 
„
„
„ ak1 , k ' 1;
2
„
„a k
„
„
„
„
„ek nk %2 " r0, 1x, k ' 0;
„
„
„ pk1 ˜ ak , k ' 0, dacă ek 1; ak ak modifică produsul pk1 
1
„
„
„
„p w
„ k k ' 0, dacă ek 0; ak 1 nu modifică produsul pk1 
0
€ pk1 ,

C.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 C. EXPONENŢIERE RAPIDĂ C.4. CODUL 490

C.4 Codul
Codul pentru relaţiile (C.2.1) devine:

Listing C.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 (C.4.2).
APPENDIX C. EXPONENŢIERE RAPIDĂ C.4. CODUL 491

Listing C.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 (C.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 C. EXPONENŢIERE RAPIDĂ C.4. CODUL 492

Observaţia 9. Instrucţiunile care sunt ”ı̂n plus” se pot elimina. Codul următor arată acest lucru:

Listing C.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 C.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 C. EXPONENŢIERE RAPIDĂ C.5. CHIAR ESTE RAPIDĂ? 493

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 */

C.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 C.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 C. EXPONENŢIERE RAPIDĂ C.6. REZUMAT INTUITIV! 494

Iar aceasta este metoda ”rapidă”:

Listing C.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)!

C.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 C. EXPONENŢIERE RAPIDĂ C.6. REZUMAT INTUITIV! 495

Asta este tot!


Deci, secvenţa de cod este:

Listing C.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 }

138
şi nu mai trebuie ”atâtea formule matematice”!

138
Este o glumă!
Appendix D

Căutare binară

D.1 Mijlocul = ?
Care este poziţia (indicele) ”mijlocului” unei zone dintr-un vector?

Figura D.1: căutare binară: mijlocul zonei ı̂n vector

În figura D.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.

496
APPENDIX D. CĂUTARE BINARĂ D.2. POZIŢIE OARECARE 497

D.2 Poziţie oarecare


D.2.1 Varianta iterativă

Listing D.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 D. CĂUTARE BINARĂ D.2. POZIŢIE OARECARE 498

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 }

D.2.2 Varianta recursivă

Listing D.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 D. CĂUTARE BINARĂ D.3. POZIŢIA DIN STÂNGA 499

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 }

D.3 Poziţia din stânga


D.3.1 Varianta iterativă

Listing D.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 D. CĂUTARE BINARĂ D.3. POZIŢIA DIN STÂNGA 500

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 }

D.3.2 Varianta recursivă

Listing D.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 D. CĂUTARE BINARĂ D.4. POZIŢIA DIN DREAPTA 501

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 }

D.4 Poziţia din dreapta


D.4.1 Varianta iterativă

Listing D.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 D. CĂUTARE BINARĂ D.4. POZIŢIA DIN DREAPTA 502

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 D. CĂUTARE BINARĂ D.4. POZIŢIA DIN DREAPTA 503

D.4.2 Varianta recursivă

Listing D.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 }
Appendix E

”Vecini” ...

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

Figura E.1: ”vecini” ı̂n matrice şi sistem Oxy

În figura E.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 E.2: Toţi ”pereţii” sunt la N sau la V ı̂n matricea ”bordată”

În figura E.2 ...

504
APPENDIX E. ”VECINI” ... E.1. DIRECŢIILE N, E, S, V 505

Figura E.3: Toţi ”pereţii” sunt codificaţi

În figura E.3 ...


Appendix F

Diagonale ı̂n matrice

F.1 Diagonale ı̂n matrice

Figura F.1: diagonale ı̂n matrice - start = 0

În figura F.1 sunt prezentate diagonale principală şi secundară ı̂mpreună cu zonele din matrice pe
care acestea le separă (indici plecând de la 0).

Figura F.2: diagonale ı̂n matrice - start = 1

În figura F.2 sunt prezentate diagonale principală şi secundară ı̂mpreună cu zonele din matrice
pe care acestea le separă (indici plecând de la 1).

506
APPENDIX F. DIAGONALE ÎN MATRICE F.2. SIMETRIC FAŢĂ DE DIAGONALE 507

F.2 Simetric faţă de diagonale


F.2.1 Simetric faţă de diagonala principală

Figura F.3: simetric faţă de diagonala principală

F.2.2 Simetric faţă de diagonala secundară

Figura F.4: simetric faţă de diagonala secundară


Appendix G

Parcurgere matrice

G.1 Parcurgere NE spre SV

Figura G.1: Interclasarea vectorilor

În figura H.1 este prezentată parcurgerea ı̂n matrice din NE spre SV.

508
Appendix H

Interclasare

H.1 Interclasarea vectorilor

Figura H.1: Interclasarea vectorilor

În figura H.1 este prezentată interclasarea a doi vectori a1[ ] şi a2[ ] ı̂n vectorul a12[ ].
Vom folosi doi indici i1 (care se ”plimbă” prin vectorul a1[ ]) şi i2 (care se ”plimbă” prin vectorul
a2[ ]). La ”momentul 0” cei doi indici sunt plasaţi pe poziţia 0 (fiecare ı̂n vectorul lui!). Aceşti
doi indici pot fi priviţi ca două persoane (i1 şi i2) care parcurg fiecare câte un şir de cutii (a1[ ] şi
a2[ ]) numerotate cu 0, 1, 2, ... ı̂n care sunt obiecte care au scris preţul lor pe ele. Ambele şiruri de
cutii conţin obiecte cu valori ı̂n ordine crescătoare. Aceste obiecte trebuie mutate ı̂n şirul cutiilor
goale a12[ ] dar trebuie să rămână tot ı̂n ordine crescătoare acolo!
Cele două persoane (i1 şi i2) cunosc ı̂n orice moment care este valoarea din cutia ı̂n faţa
căreia ”aşteaptă” celălalt indice (persoană!). La momentrul 0 ı̂ncepe să ”care obiectele”, pentru
depozitare ı̂n şirul a12[ ], cel care are ı̂n cutia din faţa lui valoarea cea mai mică ... iar dacă au

valori egale ı̂ncepe i1 (sau i2 dacă vrei neapărat! ).


În exemplul nostru ı̂ncepe i1 pentru că el este ı̂n faţa cutiei cu numărul 0 care conţine un
obiect de valoare 2 ... iar celălalt are ı̂n cutia lui (tot cu numărul 0) un obiect cu valoare 4 (mai
mare decât 2). Şi i1 ı̂ncepe să care obiecte! El se opreşte ı̂n dreptul cutiei cu numărul 5 pentru că
aceasta conţine un obiect cu valoare 5 (este o pură ı̂ntâmplare că este tot 5 ...), valoare care este
mai mare decât valoarea pe care o are i2 (i2 este ı̂n aşteptare ı̂n dreptul cutiei cu numărul 0 care
are un obiect cu valoarea 4).
În acest moment i2 ı̂ncepe să care obiecte iar i1 aşteaptă ”cuminte” ı̂n dreptul cutiei cu numărul
5 ... până când i2 va da peste o cutie cu o valoare strict mai mare decât cea a lui i1.

Şi tot aşa ... ! Până sunt cărate toate cutiile!

509
Index

15
10 , 392 auto &, 261
18
10 , 230
 bit inversion, 185 backtracking, 394, 416
((1 ¡¡ n)-1)ˆmask, 261 Be careful
(c ˆ 48), 162 Array bounds, 254
(void)0;, 265 Integer overflows, 254
(x ¡¡ 3) + (x ¡¡ 1), 162 Special cases, 254
(x & (-x)), 162 begin(), 88
(x¡¡1LL)+(x¡¡3LL)+ch-’0’, 265 begin()), 100
*–, 154 best practice, 31
*max element, 74 biţi, 165
*set.lower bound(...), 154 Big Endian, 56
? :, 57 binary indexed tree, 109
[&], 74 binary lifting, 84, 127
#define, 35, 74, 100 binary representation, 166
#define GNU SOURCE, 372 binary search, 78, 116, 117, 381
asprintf, 372, 379 bipartite graph
#include, 35 matching, 83
#include ”testlib.h”, 206 bit, 41, 121, 165
#pragma GCC, 100 BIT - binary indexed trees, 109
&, 194, 261 bit mask, 121
&..., 376 bitmask, 394
&&, 261 bits, 190
&&..., 376 bitset, 371
ˆ, 200 bitwise and—hyperpage, 121
ˆ=, 194, 200 bool, 80, 88
builtin ctzll, 414 cmp, 154
gcd, 313 bool cmp(int x, int y), 164
şablonul final, 36 boolean
0x, 51 variable, 83
0x3f3f3f3f, 162, 265, 376 bottom-up, 4
0x3f3f3f3f3f3f3f3fll, 376 breadth first search, 121
0x7fffffff, 376 breath-first search, 215
2D dynamic programming, 153 brute force, 95
32 million operations, 393 brute-force, 123, 242, 394
32-bit integer, 190 bubble sort, 165
byte, 41
a¡¡1 ¶ 1, 200
AC - Accepted Code, 103 C++, 7
accumulate(a.begin(), a.end(), 0LL);, 92 C++ Operator Precedence, 154
alfabet, 44 căutare binară, 496
analogie cu baza 10, 3, 10 căutare secvenţială, 496
ancestor, 127, 402 caracter, 44
arbore, 81 cerr, 213, 223
de acoperire, 81 chain, 435
array, 74 char*, 372, 379
ascending order, 126 cicle, 175
ASCII, 37 CINOR, vi
auto, 74, 80, 87, 156, 224, 246 class, 343

510
INDEX INDEX 511

clear(), 213, 320, 347 f ¶= ch==’-’;, 265


clear();, 87 f[1¡¡22];, 265
clock(), 315 faster as runtime, 394
CLOCKS PER SEC, 315 fastIO
cod complementar, 16 buf size = 4096;, 431
cod OK1, 300, 307, 438 flusher, 426, 431
cod OK2, 303, 305, 439, 445 getChar(), 426, 431
compilare, 34 readChar(), 426, 431
concat, 372, 379 readInt(), 426, 431
connected components, 208 writeChar( int x ), 426, 431
const, 74 writeInt( T x, char end ), 426, 431
double, 80 writeWord( const char *s ), 426, 431
int, 80 Fenwick trees, 192
const auto&, 343 final state, 90
const vector¡int¿&, 343 find(a[i]), 100
const vector¡long long¿ &a, 92 flush, 76
const void*, 372, 379 for (char c : s), 371
constexpr, 371 friend bool operator, 157
coprime, 393 from memory point of view, 393
count, 74, 376, 414
gândire algoritmică, 23
counting sort, 72
gcd, 402
cout.precision(10);, 306
getchar(), 162
crescător, 164
global array, 72
CTE - Compile Time Error, 103
graf, 75
cum vrea, 31
conex, 81
cycle, 83
neorientat, 81
data structure, 64 graph, 126, 175, 208
map, 368 bipartite, 83
data type, 60, 64 connected, 308
decompose(), 405 directed, 337
decreasing order, 123 edge, 337
define, 80 undirected, 308
degree, 78 vertex, 337
dfs, 80, 87, 213, 290, 291, 300, 306, 436, 438 Gray code, 113
dfs(int u, int from), 444 greedily, 71
Dijkstra, 221 greedy, 83, 84, 274
with priority queue, 215 greedy approach, 123
Min Heap, 229 Hall’s marriage theorem, 83
directed graph, 215 Hamiltonian cycle, 416
directed tree, 126 Hamiltonian path, 416
disjoint set, 124 hash, 368, 414
dp table, 103 hash map, 72
DP transition, 257 hashmap, 72
drum ı̂n arbore, 75 heavy-light decomposition, 84
DSU, 299, 337
dynamic programming, 83, 103, 177, 178, I.d.k.:
234, 242, 257, 274, 393, 435 I don’t know who the author is., i, v
dynamic programming table, 103 i¡¡1, 200
indentare, 31
edge, 215 induction, 71, 96
emplace back, 315 inline bool, 376
empty(), 87, 100, 347 insert, 87
end(), 88, 100 insert(i), 100
EOF, 160 instrucţiuni, 31
erase break, 59
unique, 376 case, 57
ErdősSzekeres theorem, 95 continue;, 59
euler cycle, 337 do, 58
exclusive or (XOR), 190 for, 57
expo(...), 184 getchar();, 59
INDEX INDEX 512

if, 57 muchie ı̂n graf, 76


ifstream, 36 muchii ponderate, 81
ofstream, 36 multiset, 450
system(“PAUSE”);, 59
while, 58 nedescrescător, 164
int mod=1e9+7;, 184 newline, 76
int64 t, 187, 438 nod ı̂n arbore, 75
interactor neadaptiv, 76 NULL, 372, 379
inversed permutation, 337 number of states, 394
ios base::sync with stdio(0);, 190 numeric limits, 244, 261
iota, 74, 305
octet, 41
ismillerrabinprime, 405
output, 76
it.first, 100
it.second, 100 p, 124
p==1 ? solve1() : solve2();, 265
Java, 7 pair, 213, 320, 422, 427
pair of nodes, 78
Kruskal, 83
partial sums, 109
partition, 124
lambda, 217, 246
partitions, 258
LCA, 78
Pascal, 7
LCA algorithm, 127
pattern, 71
lead, 78
periodic, 103
letters are replaced with numbers, 368
permutare, 81, 93, 115
Liceul Militar Dimitrie Cantemir, vi
permutation, 336
limbajul de programare C, 44
Pi = acos(-1.);, 80
limbajul de programare C++, 44
pigeonhole principle, 72
linear search, 257
pointer, 451
linear time, 72
Pollard method, 402
Little Endian, 56
pop back(), 347
lizibil, 31
pow mod, 413
long double, 87
mul mod, 413
long long, 87
precalculated, 234, 257
Longest Common Subsequence, 153
precalculation, 124
Longest Increasing Subsequence, 153
precomputation, 72
lowbit, 164
precompute, 127, 367
lower bound, 343, 347, 376
precomputed, 153
LSB, 56
prefix, 78
LSB (least-significant byte), 56
prefix sums, 367
preprocessing, 95, 126
map, 72, 74, 104, 227, 347, 402, 407, 410
priority queue, 124
map ¡vector¡int¿, int¿, 394
key-value pair, 124
map¡int, int¿, 100
priority queue, 217, 218, 221, 224, 226
matrice, 121
push back, 213, 320
matrix, 213
push back(*pt), 100
matrix bpow(matrix a, ll st), 419
push back(x), 92
matrix mul(matrix a, matrix b), 419
putchar, 160
max, 87
puteri ale lui 2, 4
max element, 170, 440
Python, 90
memoization, 177, 178
memory efficiency, 394 qsort, 372, 377
memset, 213, 223, 251, 343, 376, 402 queue, 124
min, 74, 87 front of queue, 124
minimum spanning tree, 83 quickly find, 450
mst, 83
minstd rand, 313 r ¿¿= 1, 200
modulo, 368 r&1, 200
MSB, 55 rădăcina arborelui, 75
MSB (most-significant byte), 55 rbegin(), 213
mt19937, 306, 347 read(T &x), 162
mt19937 rnd(time(0));, 100 read char(), 160
INDEX INDEX 513

read int(), 160 strdup, 372, 379


readint(), 164 string, 88, 223
recursive procedure, 117 find, 223, 373
recursively, 90, 117 size(), 373
relax the value, 257 strlen, 372, 379
remainder, 366 strong pseudo prime, 402
rend(), 213 struct, 88, 154, 157, 369, 414
resize, 305 struct DSU, 100
unique, 347 subşir, 93, 102
resize(n), 88, 320 subarray, 71
resize(n);, 100 subsequence, 104
RMQ, 152 subset, 96
root, 126 subtrees, 78
RTE - Run Time Error, 103 suffix, 123
sum of powers of two, 231
scanf(”%s”,s+1);, 369 swap, 74, 100, 221, 320
Segment tree, 152 sync with stdio(0);, 213
segment tree, 84, 192, 234, 436
set, 104, 154, 156, 160, 162, 164, 223, 227, terms of time, 393
320, 325 testlib.h, 167
erase, 223 time limit, 393
insert, 154, 156, 160, 162, 164, 223, 320 TL - time limit, 103
lower bound, 154, 164 TLE - Time Limit Exceed, 103
rbegin(), 320 top of the stack, 124, 126
upper bound, 154, 164 trage cu tunul, iv
set a limit, 381 tree, 126, 308, 435
set¡int¿, 100 tuple, 96
set¡pair¡int,int¿¿, 87 two pointers technique, 258
setmax, 438 typedef, 80, 100, 160
setmin, 438 pair, 74
setw(8), 51 vector, 74
shortest path, 215 typename..., 376
show bytes in memory, 55
shuffle, 313 uniform int distribution, 313
sieve, 413 unique, 343
signed, 371 Universitatea Ovidius, vi
sistem de numeraţie, 3 unordered map, 371, 376, 413, 414
size(), 213, 320 using, 100
size t, 343
sizeof..., 376 variabilă, 36
slow, 257 variabile globale, 44
sort, 74, 140, 154, 156, 213, 234, 258, 337, variabile locale, 44
368, 370, 378 vector, 88, 156, 261
ascending order, 140 back(), 389
greater¡pair¡int, int¿¿(), 156 clear(), 389
lambda, 261, 343 erase, 390
sorted stack, 153 insert, 320
sorting, 72 pair, 156
speed up the solution, 91 push back, 306
stack, 124, 126 struct, 389
state, 393 vector¡int¿, 87, 100
state of the game, 90 vector¡pair¡int, int¿¿ a, 100
states, 274 vector¡pair¡int,int¿¿, 87
std::hex, 51 vector¡vector¡int¿¿, 100, 320
STL, 368 vertices, 215
map, 451
priority queue, 451 WA - Wrong Answer, 103
set, 451 whitespace, 37
strămoş comun, 75
strchr, 372, 379 x¿y ? x=y,1 : 0;, 265
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, Bucureşti,
1992
[14] Erickson J.; Combinatorial Algorithms; http://www.uiuc.edu/˜jeffe/
[15] Flanagan, D.; Java in a Nutshell, O’Reilly, 1997.
[16] Giumale C., Negreanu L., Călinoiu S.; Proiectarea şi analiza algoritmilor. Algoritmi de
sortare, Ed. All, 1997
[17] Halim S., Halim F., Competitive programming, 2013
[18] Knuth, D.E.; Arta programării calculatoarelor, vol. 1: Algoritmi fundamentali, Ed. Teora,
1999.
[19] Knuth, D.E.; Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici, Ed. Teora,
2000.
[20] Knuth, D.E.; Arta programarii calculatoarelor, vol. 3: Sortare şi căutare, Ed. Teora, 2001.

[21] Knuth, D.E.; The art of computer programming, vol. 4A: Combinatorial algorithms, Part 1,
Addison Wesley, 2011.
[22] Lambert,K. A., Osborne,M.; Java. A Framework for Programming and Problem Solving,
PWS Publishing, 1999
[23] Laaksonen A.; Guide to competitive programming, Springer, 2017
[24] Livovschi, L.; Georgescu H.; Analiza şi sinteza algoritmilor. Ed. Enciclopedică, Bucureşti,
1986.
[25] Niemeyer, P., Peck J.; Exploring Java, O’Reilly, 1997.

514
BIBLIOGRAFIE BIBLIOGRAFIE 515

[26] Odăgescu, I., Smeureanu, I., Ştefănescu, I.; Programarea avansată a calculatoarelor personale,
Ed. Militară, Bucureşti 1993
[27] Odăgescu, I.; Metode şi tehnici de programare, Ed. Computer Lobris Agora, Cluj, 1998
[28] Popescu Anastasiu, D.; Puncte de articulaţie şi punţi ı̂n grafuri, Gazeta de Informatică nr.
5/1993
[29] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Lista_probleme_2000-2007.pdf
[30] Răbâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire
/2007/Info/Rezolvari_C09.pdf

[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 516

[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_alternative
[78] *** - https://en.cppreference.com/w/cpp/algorithm
[79] *** - https://www.ejoi2019.si/
[80] *** - https://en.wikipedia.org/wiki/Algorithm
[81] *** - https://en.wikipedia.org/wiki/List_of_algorithms
[82] *** - https://en.wikipedia.org/wiki/List_of_data_structures
[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, 181 Lucian Bicsi, 121


Anton Trygub, 77, 90, 95, 103
Maria-Alexa Tudose, 116
Constantin Tudor, vi Matvii Aslandukov, 83
Mihai Bunget, 109
Eugen Nodea, 123

Gheorghe Dodescu, vi Pop Ioan Cristian, 113

Ihor Barenblat, 83 Ray Bai, 77, 90


Ion Văduva, vi Roman Bilyi, 77, 90
Ionel-Vasile Piţ-Rada, 110
Tamio-Vesa Nakajima, 117, 121
Jeroen Op de Beek, 71 Theodor-Gabriel Tulba-Lecu, 113, 121

517
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