Sunteți pe pagina 1din 1103

Informatică

Olimpiada - I.O.I.

2023-4
PROBLEME DE INFORMATICĂ
date la olimpiade

I.O.I.
ı̂n

2019 2018 2017 2016 2015


2014 2013 2012 2011 2010

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!

Volumul 1
... 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 xvi

Lista programelor xvii

1 IOI 2019 1
1.1 Arranging Shoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.2 Split the Attractions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
1.3 Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
1.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
1.4 Broken Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
1.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
1.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.5 Vision Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.6 Sky Walking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

2 IOI 2018 113


2.1 Combo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
2.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
2.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
2.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
2.2 Seats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
2.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
2.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
2.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
2.3 Werewolf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
2.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
2.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
2.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
2.4 Mechanical Doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

viii
2.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
2.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
2.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
2.5 Highway Tolls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
2.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
2.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
2.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
2.6 Meetings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
2.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
2.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
2.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237

3 IOI 2017 238


3.1 Nowruz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
3.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
3.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
3.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
3.2 Wiring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
3.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
3.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
3.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
3.3 Toy Train . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
3.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
3.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
3.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
3.4 Big Prize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
3.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
3.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
3.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
3.5 Simurgh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
3.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
3.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
3.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
3.6 Ancient Books . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
3.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
3.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
3.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395

4 IOI 2016 396


4.1 Detecting molecules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
4.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
4.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
4.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
4.2 Roller Coaster Railroad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
4.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
4.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
4.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
4.3 Shortcut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
4.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
4.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
4.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
4.4 Paint By Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
4.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
4.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
4.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
4.5 Unscrambling a Messy Bug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
4.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
4.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
4.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
4.6 Aliens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
4.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 544
4.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
4.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591

5 IOI 2015 592


5.1 Boxes with souvenirs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
5.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 593
5.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
5.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602
5.2 Scales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602
5.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
5.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
5.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
5.3 Teams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
5.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
5.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
5.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
5.4 Horses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
5.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
5.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
5.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641
5.5 Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641
5.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
5.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
5.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
5.6 Towns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
5.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
5.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
5.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671

6 IOI 2014 672


6.1 Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
6.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
6.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
6.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
6.2 Rail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
6.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
6.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
6.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
6.3 Wall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
6.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
6.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
6.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
6.4 Friend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
6.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
6.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
6.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
6.5 Gondola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
6.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
6.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
6.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
6.6 Holiday . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
6.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 737
6.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 738
6.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749
7 IOI 2013 750
7.1 Art Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 750
7.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
7.1.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
7.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
7.2 Cave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
7.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
7.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
7.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
7.3 Dreaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
7.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
7.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
7.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
7.4 Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
7.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 783
7.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
7.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
7.5 Robots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
7.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
7.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
7.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
7.6 Wombats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
7.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
7.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
7.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823

8 IOI 2012 824


8.1 Pebbling odometer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
8.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
8.1.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
8.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
8.2 Parachute rings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
8.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
8.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
8.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850
8.3 Crayfish scrivener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850
8.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
8.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
8.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
8.4 Ideal city . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
8.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
8.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
8.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877
8.5 Last Supper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877
8.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
8.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
8.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891
8.6 Tournament . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891
8.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
8.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
8.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904

9 IOI 2011 905


9.1 Tropical Garden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 905
9.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907
9.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909
9.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
9.2 Race . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
9.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 919
9.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921
9.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 928
9.3 Rice Hub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 928
9.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 930
9.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931
9.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937
9.4 Crocodile’s Underground City . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937
9.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939
9.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
9.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948
9.5 Dancing Elephants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948
9.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950
9.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952
9.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960
9.6 Parrots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960
9.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963
9.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 966
9.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 976

10 IOI 2010 977


10.1 Cluedo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 977
10.1.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 978
10.1.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979
10.1.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 982
10.2 Hotter Colder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 982
10.2.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 984
10.2.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 987
10.2.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996
10.3 Quality of Living . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996
10.3.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997
10.3.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999
10.3.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1003
10.4 Languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1003
10.4.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005
10.4.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005
10.4.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1013
10.5 Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1013
10.5.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1014
10.5.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016
10.5.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1019
10.6 Traffic Congestion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1020
10.6.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1021
10.6.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1022
10.6.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1026
10.7 Maze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1026
10.7.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
10.7.2 *Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
10.7.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
10.8 Saveit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
10.8.1 Indicaţii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1031
10.8.2 Coduri sursă . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032
10.8.3 *Rezolvare detaliată . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1038

Appendix A ”Instalare” C++ 1040


A.1 Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1040
A.1.1 Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1041
A.1.2 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1042
A.1.3 Utilizare Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1044
A.1.4 Setări Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047
A.1.5 Multe surse ı̂n Code Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049
A.2 winlibs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050
A.2.1 GCC şi MinGW-w64 pentru Windows . . . . . . . . . . . . . . . . . . . . . 1050
A.2.2 PATH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050
A.2.3 CodeBlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053

Appendix B Exponenţiere rapidă 1063


B.1 Analogie baza 2 cu baza 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063
B.2 Notaţii, relaţii şi formule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064
B.3 Pregătire pentru scrierea codului! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064
B.4 Codul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1065
B.5 Chiar este rapidă? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
B.6 Rezumat intuitiv! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069

Index 1071

Bibliografie 1074

Lista autorilor 1077


Lista figurilor

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


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

1.1 Shoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Split1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.3 split2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.4 rectangular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
1.5 Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
1.6 vision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.7 walk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

2.1 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152


2.2 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
2.3 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
2.4 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
2.5 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
2.6 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
2.7 highway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
2.8 Meetings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219

A.1 Fişierele din Kit OJI 2017 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1040


A.2 CodeBlocks & C++ Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1041
A.3 Ce conţine C:¯ OJI ¯ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1041
A.4 Folder de lucru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1042
A.5 New -¿ Text document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043
A.6 Schimbare nume fişier şi nume extensie fişier . . . . . . . . . . . . . . . . . . . . . . 1043
A.7 Confirmare schimbare extensie ı̂n .cpp . . . . . . . . . . . . . . . . . . . . . . . . . 1044
A.8 Pregătit pentru Code::Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1044
A.9 Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . 1044
A.10 Primul cod de program C++ ı̂n Code::Blocks . . . . . . . . . . . . . . . . . . . . . 1045
A.11 Build - compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1045
A.12 0 error(s), 0 warning(s) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1046
A.13 Run - execuţie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1046
A.14 Executat corect: a făcut “nimic” . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047
A.15 Settings  % Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047
A.16 Toolchain executables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048
A.17 Unde sunt acele programe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048
A.18 Multe surse ı̂n Code Blocks - setări . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049
A.19 Multe surse in Code Blocks - exemple . . . . . . . . . . . . . . . . . . . . . . . . . 1049
A.20 mingw64 pe D: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050
A.21 search path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051
A.22 System properties –¿ Advanced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051
A.23 Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1052
A.24 Edit Environment Variables –¿ New . . . . . . . . . . . . . . . . . . . . . . . . . . 1052
A.25 Calea şi versiunea pentru gcc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053
A.26 Settings –¿ Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1054
A.27 Toolchain executables –¿ Auto-detect . . . . . . . . . . . . . . . . . . . . . . . . . . 1054
A.28 New –¿ Text Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055
A.29 New text Document.txt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055

xiv
A.30 Schimbare nume şi extensie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055
A.31 Moore apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1056
A.32 Look for another app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1056
A.33 Cale pentru codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057
A.34 Selectare codeblocks.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1057
A.35 Editare test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1058
A.36 Compilare test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1058
A.37 Mesaje după compilare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
A.38 Execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
A.39 Rezultat execuţie test01 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1060
A.40 Fişiere apărute după compilare! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1060
A.41 Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿ . . . . . . . . . . . . . . . . . . 1060
A.42 Lista programelor de utilizat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1061
A.43 Selectare Code::Blocks IDE pentru fişierele .cpp . . . . . . . . . . . . . . . . . . 1061
A.44 Editare+Compilare+Execuţie pentru test02 . . . . . . . . . . . . . . . . . . . . . 1061
A.45 Selectare tab ce conţine test01.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 1062

B.1 Analogie B2 cu B10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063


Lista tabelelor

2.1 Combo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114


2.2 highway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200

xvi
Lista programelor

1.1.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3


1.1.2 shoes.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3 shoes.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.5 shoes-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.6 shoes-model+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.7 shoes 143076+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.1.8 shoes 143080+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.1.9 shoes-ds ok+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.10 shoes-jonathanirvings+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.11 shoes-kuzey ok+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.2.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.2 split.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.3 split.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.5 split-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.6 split-kostka-full+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.2.7 split 144514+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.2.8 split koosaga+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.2.9 split-mahdi+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.2.10 split-maroon-accepted+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 36
1.3.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.3.2 rect.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.3.3 rect.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.3.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.3.5 rect-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.3.6 rectangle-mruxim-n2lg+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 46
1.3.7 rectangle 147888+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.3.8 rectangle145193+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
1.3.9 rectangle-peyman-n2lg-opt+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . 57
1.4.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.4.2 line.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.4.3 line.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.4.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.4.5 line-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.4.6 sol-ge-most-optimal+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.5.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.5.2 vision.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.5.3 vision.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.5.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.5.5 vision-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.6.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.6.2 walk.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.6.3 walk.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.6.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.6.5 walk-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.6.6 akm-full+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
1.6.7 maroon-full+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
1.6.8 mohammad-full-walk+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

xvii
1.6.9 walk 143257 Benq+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
1.6.10 walk 147051+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
2.1.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
2.1.2 combo.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
2.1.3 combo.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
2.1.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
2.1.5 combo-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
2.1.6 combo 75294.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
2.1.7 combo 75871.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
2.1.8 combo 76356.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
2.1.9 combo 77113.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
2.1.10 combo koosaga.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
2.2.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
2.2.2 seat.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
2.2.3 seat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
2.2.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
2.2.5 seat-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
2.2.6 seats 75159.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
2.2.7 seats 75485.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
2.2.8 seats 76357.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
2.2.9 seats 80434.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
2.2.10 seats 81209.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
2.3.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
2.3.2 werewolf.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
2.3.3 werewolf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
2.3.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
2.3.5 werewolf-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
2.3.6 werewolf 75105.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
2.3.7 werewolf 75296.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
2.3.8 werewolf 75793.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
2.3.9 werewolf 81140.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
2.3.10 werewolf 85439.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
2.4.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
2.4.2 dool.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
2.4.3 dool.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
2.4.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
2.4.5 doll 75123.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
2.4.6 dool 75619.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
2.4.7 dool 76623.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
2.4.8 dool 78782.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
2.5.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
2.5.2 highway.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
2.5.3 highway.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
2.5.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
2.5.5 highway 74962.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
2.5.6 highway 77105.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
2.5.7 highway 78948.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
2.6.1 compile cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
2.6.2 meetings.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
2.6.3 meetings.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
2.6.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
2.6.5 meetings-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
2.6.6 meetings 76204.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
2.6.7 meetings 120908.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
2.6.8 meetings 159115.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
3.1.1 nowruz.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
3.1.2 checker.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
3.1.3 addleaves.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
3.1.4 addleaves once.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
3.1.5 addleaves rand.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
3.1.6 dfs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
3.1.7 haircomb.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
3.1.8 nowruz1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
3.2.1 wiring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
3.2.2 wiring+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
3.2.3 wiring-haas-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
3.2.4 wiring-haas-ac-ternary.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
3.2.5 wiring-mahdi-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
3.2.6 wiring-malek-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
3.2.7 wiring-saeed-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
3.2.8 wiring-197505.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
3.2.9 wiring-200512.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
3.2.10 wiring-206573.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
3.2.11 wiring-216947.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
3.2.12 wiring-221801.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
3.2.13 wiring-227702.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
3.2.14 wiring-227979.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
3.3.1 train.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
3.3.2 train+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
3.3.3 checkerModificat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
3.3.4 train-malek-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
3.3.5 train-saeed-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
3.3.6 train-134142.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
3.3.7 train-146520.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
3.3.8 train-160375.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
3.3.9 train-163181.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
3.3.10 train-201183.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
3.3.11 train-221800.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
3.3.12 train-222137.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
3.4.1 prize.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
3.4.2 prize+graderpublic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
3.4.3 prize-169927.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
3.4.4 prize-187329.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
3.4.5 prize-206577.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
3.4.6 prize-208873.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
3.4.7 prize-221802.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
3.4.8 prize-229052.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
3.4.9 prize-232826.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
3.5.1 simurgh.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
3.5.2 simurgh-33904.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
3.5.3 simurgh-70374.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
3.5.4 simurgh-70729.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
3.5.5 simurgh-136567.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
3.5.6 simurgh-137723.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
3.6.1 books+graderpublic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
3.6.2 books-42759.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
3.6.3 books-51837.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
3.6.4 books-94567.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
3.6.5 books-122101.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
3.6.6 books-138862.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
3.6.7 books-152524.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
3.6.8 books-206638.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
4.1.1 molecules sk.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
4.1.2 molecules sk greedy 1 n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
4.1.3 molecules sk greedy 2 n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
4.1.4 molecules sk greedy 3 ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
4.1.5 molecules-20751.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
4.1.6 molecules-72936.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
4.1.7 molecules-93472.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
4.1.8 molecules-114550.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
4.1.9 molecules-159299.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
4.2.1 railroad mp nlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
4.2.2 railroad-103076.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
4.2.3 railroad-117891.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
4.2.4 railroad-135900.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
4.2.5 railroad-223610.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
4.2.6 railroad-233098.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
4.3.1 shortcut c.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
4.3.2 sol ge nlogd.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
4.3.3 sol ge nlogd fastio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
4.3.4 sol nk nlogd.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
4.3.5 shortcut-24626.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
4.3.6 shortcut-33932.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
4.3.7 shortcut-94572.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
4.3.8 shortcut-97033.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
4.3.9 shortcut-99074.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
4.3.10 shortcut-99075.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
4.3.11 shortcut-113364.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
4.3.12 shortcut-114144.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
4.3.13 shortcut-142952.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
4.3.14 shortcut-162764.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
4.3.15 shortcut-207049.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
4.3.16 shortcut-225819.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
4.4.1 paint c.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
4.4.2 paint iz.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
4.4.3 solve-correct-lc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
4.4.4 checkerPaintModificat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
4.4.5 paint-65961.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
4.4.6 paint-66328.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
4.4.7 paint-97040.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
4.4.8 paint-105782.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
4.4.9 paint-107399.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
4.4.10 paint-108251.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
4.4.11 paint-112136.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
4.4.12 paint-130510.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
4.4.13 paint-180292.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
4.4.14 paint-204502.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
4.5.1 checkerMessyModificat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
4.5.2 messy cpp ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
4.5.3 messy iz.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
4.5.4 messy tourist.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
4.5.5 messy-21910.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
4.5.6 messy-23726.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
4.5.7 messy-23992.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
4.5.8 messy-59231.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
4.5.9 messy-66964.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
4.5.10 messy-67587.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
4.5.11 messy-70792.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
4.5.12 messy-71456.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
4.5.13 messy-102062.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
4.6.1 checkerAliens.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
4.6.2 alien-bsearch.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
4.6.3 aliens ma nlogm.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
4.6.4 aliens ma nlogm double.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
4.6.5 alien-32012.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
4.6.6 alien-43618.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
4.6.7 alien-45993.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
4.6.8 alien-70398.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
4.6.9 alien-94585.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
4.6.10 alien-96357.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567
4.6.11 alien-97219.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
4.6.12 alien-121473.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571
4.6.13 alien-166476.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574
4.6.14 alien-171563.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
4.6.15 alien-172712.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
4.6.16 alien-173410.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
4.6.17 alien-224940.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
4.6.18 alien-225911.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
4.6.19 alien-228077.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
5.1.1 boxes-16533.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
5.1.2 boxes-64042.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
5.1.3 boxes-70567.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
5.1.4 checkerBoxes.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
5.2.1 graderlib.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
5.2.2 scales-45773.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607
5.2.3 scales-115418.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
5.2.4 scales-122639.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
5.2.5 checkerScales.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
5.3.1 teams-17286.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
5.3.2 teams-69885.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
5.3.3 teams-172439.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
5.3.4 checkerTeams.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
5.4.1 horses-91995.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
5.4.2 horses-102703.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634
5.4.3 horses-202434.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
5.4.4 checkerHorses.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
5.5.1 sorting-70140.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
5.5.2 sorting-114135.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
5.5.3 sorting-129522.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650
5.5.4 sorting-155927.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
5.5.5 sorting-225877.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
5.5.6 sorting-233228.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656
5.6.1 graderlib.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
5.6.2 towns-127541.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
5.6.3 towns-134867.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
5.6.4 towns-151462.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
5.6.5 checkerTowns.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
6.1.1 game-92195.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
6.1.2 game-117330.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
6.1.3 game-231330.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
6.1.4 game nˆ2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
6.1.5 checkerGame.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
6.2.1 rail-117000.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
6.2.2 rail-121219.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
6.2.3 rail-125767.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691
6.2.4 rail-139096.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
6.2.5 rail-173712.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
6.3.1 wall-39307.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
6.3.2 wall-93939.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
6.3.3 wall-173006.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
6.3.4 wall-232336.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
6.3.5 checkerWall.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
6.4.1 friend-16524.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
6.4.2 friend-115760.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
6.4.3 friend-119597.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
6.4.4 checkerFriend.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
6.5.1 gondola-7306.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
6.5.2 gondola-93790map.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728
6.5.3 gondola-102776.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730
6.5.4 gondola-153281.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
6.5.5 checkerGondola.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
6.6.1 holiday-120785.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
6.6.2 holiday-134641.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741
6.6.3 holiday-211797.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
6.6.4 holiday-229391.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
6.6.5 checkerHoliday.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
7.2.1 cave-1749.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
7.2.2 cave-16655.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
7.2.3 cave-170339.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 760
7.2.4 cave-231414.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763
7.2.5 checkerCave.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
7.3.1 dreaming-3168.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
7.3.2 dreaming-16636.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 772
7.3.3 dreaming-81476.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
7.3.4 dreaming-172308.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
7.3.5 dreaming-198762.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778
7.3.6 checkerDreaming.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
7.4.1 game-220385.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
7.4.2 game-224978.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
7.4.3 game-225963.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 792
7.4.4 checkerGame.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
7.5.1 robot-7026.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
7.5.2 robot-7037.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
7.5.3 robot-17227.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803
7.5.4 robot-96364.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
7.5.5 checkerRobot.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
7.6.1 wombats-108194.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
7.6.2 wombats-108196.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818
7.6.3 wombats-118871.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
7.6.4 checkerWombats.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
8.1.1 odometer.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
8.2.1 rings.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831
8.2.2 rings-13540.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835
8.2.3 rings-49004.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838
8.2.4 rings-62499.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
8.2.5 rings-223749.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
8.2.6 rings-233348.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846
8.2.7 checkerRings.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
8.3.1 scrivener.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
8.3.2 scrivener-7279.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854
8.3.3 scrivener-18725.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856
8.3.4 scrivener-230126.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858
8.3.5 checkerScrivener.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
8.4.1 city.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
8.4.2 city-16324.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
8.4.3 city-18847.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
8.4.4 city-32163.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
8.4.5 city-197022.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 874
8.4.6 checkerCity.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877
8.5.1 supper.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
8.5.2 supper-231107.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 887
8.6.1 tournament.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
8.6.2 tournament-2193.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 897
8.6.3 tournament-4657.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899
8.6.4 tournament-14774.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901
8.6.5 checkerTournament.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 903
9.1.1 garden-49449.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909
9.1.2 garden-49789.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 911
9.1.3 garden-116557.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 914
9.1.4 checkerGarden.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
9.2.1 race-28930.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921
9.2.2 race-112526.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 923
9.2.3 race-216556.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925
9.2.4 checkerRace.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 927
9.3.1 racehub-7321.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931
9.3.2 racehub-95096.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 932
9.3.3 racehub-113697.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 933
9.3.4 racehub-116283.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935
9.3.5 checkerRaceHub.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936
9.4.1 crocodile-10431.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 940
9.4.2 crocodile-16656.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942
9.4.3 crocodile-133704.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 944
9.4.4 crocodile-228833.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946
9.4.5 checkerCrocodile.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947
9.5.1 elephants-115857.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952
9.5.2 elephants-192442.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 954
9.5.3 elephants-207733.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
9.5.4 checkerElephants.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960
9.6.1 parrot-224033.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 966
9.6.2 parrots-235627.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971
10.1.1 cluedosol.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 978
10.1.2 cluedosol.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979
10.1.3 cluedo-218031.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979
10.1.4 cluedo-220599.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 980
10.1.5 cluedo-229322.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 981
10.2.1 coldersol1.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 986
10.2.2 coldersol2.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 986
10.2.3 hottercolder.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 987
10.2.4 hottercolder-173047.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 990
10.2.5 hottercolder-201977.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 992
10.3.1 rectanglesol.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998
10.3.2 quality-208958.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999
10.3.3 quality-234933.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001
10.3.4 checkerQuality.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1003
10.4.1 lang-8124.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005
10.4.2 lang-12891.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1007
10.4.3 lang-155338.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009
10.4.4 lang-235517.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1011
10.5.1 memory.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
10.5.2 memory.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
10.5.3 memory-206951.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016
10.5.4 memory-217254.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017
10.5.5 memory-232610.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
10.6.1 quality-229321.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1022
10.6.2 quality-232654.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023
10.6.3 quality-236364.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1024
10.6.4 checkerQuality.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1026
10.8.1 saveit-231998.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032
10.8.2 saveit-224546.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035
B.4.1exponentiere rapida1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1065
B.4.2exponentiere rapida2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1066
B.4.3exponentiere rapida3.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
B.4.4exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
B.5.1exponentiere naiva MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
B.5.2exponentiere rapida MOD.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069
B.6.1secventa cod.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1070
Capitolul 1
27
IOI 2019

1.1 Arranging Shoes


Problema 1 - Arranging Shoes 100 de puncte

Author: Danylo Mysak

Adnan este proprietarul celui mai mare magazin de ı̂ncălţăminte din Baku. O cutie ce conţine
n perechi de pantofi tocmai a sosit ı̂n magazin. Fiecare pereche este formată din doi pantofi de
aceeaşi mărime: stânga respectiv dreapta. Adnan a pus toţi cei 2n pantofi ı̂ntr-o linie formată din
2n poziţii numerotate de la 0 la 2n  1, de la stânga la dreapta.
Adnan doreşte să rearanjeze pantofii ı̂ntr-un aranjament valid. Un aranjament este valid
dacă şi numai dacă pentru orice i (0 & i & n  1), următoarele condiţii sunt respectate:
a Pantofii de pe poziţiile 2i şi 2i  1 au aceeaşi mărime.
a Pantoful de pe poziţia 2i este cel din stânga.
a Pantoful de pe poziţia 2i  1 este cel din dreapta.
Pentru a realiza acest lucru, Adnan poate efectua un şir de interschimbări. Într-o interschim-
bare, acesta selectează doi pantofi adiacenţi ı̂n acel moment şi ı̂i interschimbă (ı̂i ridică şi pune
fiecare pantof pe locul celuilalt). Doi pantofi sunt adiacenţi dacă diferenţa absolută a poziţiilor
este 1.
Determinaţi numărul minim de interschimbări ce Adnan trebuie să facă pentru a obţine un
aranjament valid de pantofi.

Detalii de implementare

Trebuie să implementaţi următoarea funcţie:


int64 count_swaps(int[] S)
a S: un vector cu 2n numere ı̂ntregi. Pentru fiecare i (0 & i & 2n  1), S i este o valoare
nenulă ce descrie pantoful iniţial aflat pe poziţia i. Valoarea absolută a lui S i este mărimea
pantofului. Mărimea pantofului nu depăşeşte n. Dacă S i $ 0, pantoful de pe poziţia i este
pantof stâng, altfel este pantof drept.
a Această funcţie trebuie să returneze numărul minim de interschimbări (de pantofi adiacenţi)
ce trebuie efectuate pentru a obţine un aranjament valid.

Exemple

Exemplul 1
Să considerăm următorul apel:
count_swaps([2, 1, -1, -2])
27
argint: Theodor Pierre Moroianu, ICHB (Bucureşti),
. argint: Bogdan Sitaru, Dinicu Golescu (Campulung),
. bronz: Laura Ioana Georgescu, ICHB (Bucureşti)
. bronz: Alexandru Petrescu, Tudor Vianu (Bucureşti).

1
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 2

Figura 1.1: Shoes

Adnan poate obţine un aranjament valid ı̂n 4 interschimbări.


De exemplu, el poate face prima interschimbare ı̂ntre pantofii 1 şi 1, apoi 1 şi 2, apoi 1 şi
2, şi ı̂n final 2 şi 2. El va obţine următorul aranjament valid: 2, 2, 1, 1.
Nu putem obţine un aranjament valid ı̂n mai puţin de 4 interschimbări. Prin urmare, funcţia
va returna 4.
Exemplul 2
În exemplul următor, toţi pantofii au aceeaşi mărime:
count_swaps([-2, 2, 2, -2, -2, 2])
Adnan poate schimba pantofii de pe poziţiile 2 şi 3 pentru a obşine un aranjament valid
2, 2, 2, 2, 2, 2, deci funcţia va returna 1.

Restricţii

1 & n & 100000


a
Pentru fiecare i (0 & i & 2n  1), 1 & ¶S i¶ & n. ¶x¶ reprezintă valoarea absolută a lui x.
a
a Se poate obţine un aranjament valid al pantofilor prin efectuarea unor secvenţe de inter-
schimbări

Subtaskuri

1. (10 puncte) n 1
2. (20 de puncte) n & 8
3. (20 de puncte) Toţi pantofii sunt de aceeaşi mărime.
4. (15 puncte) Toţi pantofii de pe poziţiile 0, ..., n  1 sunt de stânga, iar toţi pantofii de pe
poziţiile n, ..., 2n  1 sunt de dreapta. De asemenea, pentru fiecare i (0 & i & n  1), pantofii de
pe poziţiile i şi i  1 sunt de aceeaşi mărime.
5. (20 de puncte) n & 1000
6. (15 puncte) Nu există alte restricţii.

Exemplu de grader

Grader-ul citeşte datele de intrare ı̂n formatul următor:


CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 3

a linia 1: n
a linia 2: S 0 S 1 S 2 ... S 2n  1
Grader-ul returnează o singură linie ce conţine valoarea returnată de funcţia count_swaps
Timp maxim de executare/test: 1.0 secunde
Memorie: total 1024 MB

1.1.1 Indicaţii de rezolvare


28
Let’s consider the first (leftmost) shoe A in the line and its matching shoe (that is, the shoe,
that this one will be paired with in an optimal arrangement). Let’s suppose that the matching
shoe is initially at position k k ' 1). Each possible swap either involves moving at least one of
these two shoes or does not involve moving any of them. Swaps of the first kind do not affect the
relative ordering of all the other shoes. At the same time, swaps of the second kind do not affect
the two shoes in question, which means that in order to pair up these two shoes there should be
at least k  1 swaps of the first kind, in case the leftmost shoe is a left shoe, or k swaps, if it’s a
right shoe. Regardless of how swaps of the second kind are going to advance, we could initially
perform this set of k  1 or k swaps, moving the shoe initially located at position k to the left
until the pair occupies the two leftmost positions.
The greedy approach is optimal since the pair of shoes handled in this way will not interfere
with any further swaps. Moreover, it always makes sense to take the leftmost one among the
shoes matching shoe A, since otherwise the same intermediate arrangement with the two leftmost
positions occupied by a matching pair of shoes could be obtained by an even shorter sequence of
¬
swaps: if another matching shoe is at position k % k, then instead of moving k all the way to the
left, first move it until the shoe occupies position k  1, but then instead of moving it further,
simply start moving the shoe that was at position k.
The process can be repeated until the arrangement of the shoes becomes valid. This can
be modelled naively in quadratic time, which solves most of the subtasks, or more efficiently in
O n log n using a Fenwick tree or a segment tree.

1.1.2 Coduri sursă

Listing 1.1.1: compile cpp.sh


#!/bin/bash

problem=shoes

g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"

"grader.cpp" "${problem}.cpp"

Listing 1.1.2: shoes.h


#include <vector>

long long count_swaps(std::vector<int> S);

Listing 1.1.3: shoes.cpp


#include "shoes.h"

long long count_swaps(std::vector<int> s)


{
return 1;
}

28
Dacă cineva are nevoie de traducere, cred că a nimerit un pic aiurea pe aici!!!
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 4

Listing 1.1.4: grader.cpp


#include "shoes.h"
#include <cstdio>
#include <cassert>

using namespace std;

int main()
{
int n;
assert(1 == scanf("%d", &n));
vector<int> S(2 * n);
for (int i = 0; i < 2 * n; i++)
assert(1 == scanf("%d", &S[i]));
fclose(stdin);

long long result = count_swaps(S);

printf("%lld\n", result);
fclose(stdout);
return 0;
}

Listing 1.1.5: shoes-model.cpp


#include "shoes.h"

using namespace std;

class Fenwick
{
vector<int> a;

int count(int right)


{
int cnt = 0;
for (; right >= 0; right = (right & (right + 1)) - 1)
{
cnt += a[right];
}
return cnt;
}

public:
explicit Fenwick(int n)
{
a.assign(n, 0);
}

int count(int left, int right)


{
return count(right) - count(left - 1);
}

void put(int index)


{
for (; index < int(a.size()); index = index | (index + 1))
{
a[index]++;
}
}
};

vector<int> create_index(int n, vector<int> &S)


{
vector<int> index(2 * n + 1);
for (int i = 0; i < 2 * n; i++)
{
index[S[i] + n] = i;
}
return index;
}

long long count_adjacent(int n, vector<int> &S)


CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 5

{
vector<int> index = create_index(n, S);
Fenwick f = Fenwick(2 * n);
long long ans = 0;
for (int i = 0; i < 2 * n; i++)
{
if (S[i] != 0)
{
int pos = index[-S[i] + n];
ans += pos - i - f.count(i, pos) - (S[i] < 0 ? 1 : 0);
S[pos] = 0;
f.put(pos);
}
}
return ans;
}

vector<int> change(vector<int> v)
{
int n = (int)v.size() / 2;
vector<int> cnt(n, 0);
for(int i = 0; i < 2*n; i++)
if(v[i] > 0)
cnt[v[i] - 1]++;

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


cnt[i] += cnt[i-1];

vector<int> cntl = cnt, cntr = cnt;

for(int i = 0; i < 2*n; i++)


if(v[i] > 0)
v[i] = (--cntr[v[i] - 1]) + 1;
else
v[i] = -((--cntl[-v[i] - 1]) + 1);
return v;
}

long long count_swaps(vector<int> S)


{
S = change(S);
int n = S.size() / 2;
return count_adjacent(n, S);
}

Listing 1.1.6: shoes-model+grader.cpp


#include "shoes.h"

#include <cstdio>
#include <cassert>

#include <time.h> /* clock */


#include <iostream>

using namespace std;

class Fenwick
{
vector<int> a;

int count(int right)


{
int cnt = 0;
for (; right >= 0; right = (right & (right + 1)) - 1)
{
cnt += a[right];
}
return cnt;
}

public:
explicit Fenwick(int n)
{
a.assign(n, 0);
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 6

int count(int left, int right)


{
return count(right) - count(left - 1);
}

void put(int index)


{
for (; index < int(a.size()); index = index | (index + 1))
{
a[index]++;
}
}
};

vector<int> create_index(int n, vector<int> &S)


{
vector<int> index(2 * n + 1);
for (int i = 0; i < 2 * n; i++)
{
index[S[i] + n] = i;
}
return index;
}

long long count_adjacent(int n, vector<int> &S)


{
vector<int> index = create_index(n, S);
Fenwick f = Fenwick(2 * n);
long long ans = 0;
for (int i = 0; i < 2 * n; i++)
{
if (S[i] != 0)
{
int pos = index[-S[i] + n];
ans += pos - i - f.count(i, pos) - (S[i] < 0 ? 1 : 0);
S[pos] = 0;
f.put(pos);
}
}
return ans;
}

vector<int> change(vector<int> v)
{
int n = (int)v.size() / 2;
vector<int> cnt(n, 0);
for(int i = 0; i < 2*n; i++)
if(v[i] > 0)
cnt[v[i] - 1]++;

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


cnt[i] += cnt[i-1];

vector<int> cntl = cnt, cntr = cnt;

for(int i = 0; i < 2*n; i++)


if(v[i] > 0)
v[i] = (--cntr[v[i] - 1]) + 1;
else
v[i] = -((--cntl[-v[i] - 1]) + 1);
return v;
}

long long count_swaps(vector<int> S)


{
S = change(S);
int n = S.size() / 2;
return count_adjacent(n, S);
}

int read_int()
{
int x;
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 7

if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/6-15.in", "r", stdin) ;


std::freopen("shoes.out", "w", stdout) ;

int n;
//assert(1 == scanf("%d", &n));
n = read_int();

vector<int> S(2 * n);


for (int i = 0; i < 2 * n; i++)
//assert(1 == scanf("%d", &S[i]));
S[i] = read_int();

fclose(stdin);

auto t2 = clock();

long long result = count_swaps(S);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<result<<’\n’<<’\n’;
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.1.7: shoes 143076+grader.cpp


// https://oj.uz/submission/143076

#include <bits/stdc++.h>
#include "shoes.h"

using std::vector;
using std::pair;

#define SZ(vec) int((vec).size())

vector<int> change(vector<int> v)
{
int n = (int)v.size() / 2;
vector<int> cnt(n, 0);
for(int i = 0; i < 2*n; i++)
if(v[i] > 0)
cnt[v[i] - 1]++;

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


cnt[i] += cnt[i-1];

vector<int> cntl = cnt, cntr = cnt;

for(int i = 0; i < 2*n; i++)


CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 8

if(v[i] > 0)
v[i] = (--cntr[v[i] - 1]) + 1;
else
v[i] = -((--cntl[-v[i] - 1]) + 1);
return v;
}

long long count_swaps(vector<int> s)


{
s = change(s);
vector<pair<int, bool>> in(SZ(s));

vector<int> where(SZ(s) / 2, -1);

for (int i = 0; i != SZ(s); ++i)


{
in[i].first = abs(s[i]) - 1;
in[i].second = (s[i] < 0 ? 0 : 1);

if (where[in[i].first] == -1)
where[in[i].first] = i;
}

vector<int> fenw(SZ(in));
for (int i = 0; i != SZ(in); ++i)
fenw[i] = 1 + i - (i & (i+1));

long long ans = 0;


vector<char> dead(SZ(in), false);

for (int pos = SZ(in) - 1; pos >= 0; --pos)


if (not dead[pos])
{
dead[pos] = 1;

int i = where[in[pos].first];
dead[i] = 1;

int dist = 0;
for (int p = pos; p >= 0; p = (p & (p + 1)) - 1)
dist += fenw[p];

for (int p = i; p >= 0; p = (p & (p + 1)) - 1)


dist -= fenw[p];

if (in[pos].second == 1)
--dist;

ans += dist;
for (int p = i; p < SZ(fenw); p = p | (p + 1))
fenw[p] -= 1;
}

return ans;
}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/6-15.in", "r", stdin) ;


std::freopen("shoes.out", "w", stdout) ;

int n;
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 9

//assert(1 == scanf("%d", &n));


n = read_int();

vector<int> S(2 * n);


for (int i = 0; i < 2 * n; i++)
//assert(1 == scanf("%d", &S[i]));
S[i] = read_int();

fclose(stdin);

auto t2 = clock();

long long result = count_swaps(S);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<result<<’\n’<<’\n’;
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.1.8: shoes 143080+grader.cpp


// https://oj.uz/submission/143080

#include <bits/stdc++.h>
#include "shoes.h"

using namespace std;

class Fenwick
{
vector<int> a;

int count(int right)


{
int cnt = 0;
for (; right >= 0; right = (right & (right + 1)) - 1)
{
cnt += a[right];
}
return cnt;
}

public:
explicit Fenwick(int n)
{
a.assign(n, 0);
}

int count(int left, int right)


{
return count(right) - count(left - 1);
}

void put(int index)


{
for (; index < int(a.size()); index = index | (index + 1))
{
a[index]++;
}
}
};
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 10

vector<int> create_index(int n, vector<int> &S)


{
vector<int> index(2 * n + 1);
for (int i = 0; i < 2 * n; i++)
{
index[S[i] + n] = i;
}
return index;
}

long long count_adjacent(int n, vector<int> &S)


{
vector<int> index = create_index(n, S);
Fenwick f = Fenwick(2 * n);
long long ans = 0;
for (int i = 0; i < 2 * n; i++)
{
if (S[i] != 0)
{
int pos = index[-S[i] + n];
ans += pos - i - f.count(i, pos) - (S[i] < 0 ? 1 : 0);
S[pos] = 0;
f.put(pos);
}
}
return ans;
}

vector<int> change(vector<int> v)
{
int n = (int)v.size() / 2;
vector<int> cnt(n, 0);
for(int i = 0; i < 2*n; i++)
if(v[i] > 0)
cnt[v[i] - 1]++;

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


cnt[i] += cnt[i-1];

vector<int> cntl = cnt, cntr = cnt;

for(int i = 0; i < 2*n; i++)


if(v[i] > 0)
v[i] = (--cntr[v[i] - 1]) + 1;
else
v[i] = -((--cntl[-v[i] - 1]) + 1);
return v;
}

long long count_swaps(vector<int> S)


{
S = change(S);
int n = S.size() / 2;
return count_adjacent(n, S);
}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/6-15.in", "r", stdin) ;


std::freopen("shoes.out", "w", stdout) ;

int n;
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 11

//assert(1 == scanf("%d", &n));


n = read_int();

vector<int> S(2 * n);


for (int i = 0; i < 2 * n; i++)
//assert(1 == scanf("%d", &S[i]));
S[i] = read_int();

fclose(stdin);

auto t2 = clock();

long long result = count_swaps(S);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<result<<’\n’<<’\n’;
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.1.9: shoes-ds ok+grader.cpp


// Dmitry _kun_ Sayutin (2019)

#include <bits/stdc++.h>
#include "shoes.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::tuple;
using std::make_tuple;
using std::get;

using std::min;
using std::abs;
using std::max;
using std::swap;

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
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 12

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

vector<int> change(vector<int> v)
{
int n = (int)v.size() / 2;
vector<int> cnt(n, 0);
for(int i = 0; i < 2*n; i++)
if(v[i] > 0)
cnt[v[i] - 1]++;

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


cnt[i] += cnt[i-1];

vector<int> cntl = cnt, cntr = cnt;

for(int i = 0; i < 2*n; i++)


if(v[i] > 0)
v[i] = (--cntr[v[i] - 1]) + 1;
else
v[i] = -((--cntl[-v[i] - 1]) + 1);
return v;
}

long long count_swaps(vector<int> s)


{
s = change(s);
vector<pair<int, bool>> in(SZ(s));

vector<int> where(SZ(s) / 2, -1);

for (int i = 0; i != SZ(s); ++i)


{
in[i].first = abs(s[i]) - 1;
in[i].second = (s[i] < 0 ? 0 : 1);

if (where[in[i].first] == -1)
where[in[i].first] = i;
}

vector<int> fenw(SZ(in));
for (int i = 0; i != SZ(in); ++i)
fenw[i] = 1 + i - (i & (i+1));

long long ans = 0;


vector<char> dead(SZ(in), false);

for (int pos = SZ(in) - 1; pos >= 0; --pos)


if (not dead[pos])
{
dead[pos] = 1;

int i = where[in[pos].first];
dead[i] = 1;

int dist = 0;
for (int p = pos; p >= 0; p = (p & (p + 1)) - 1)
dist += fenw[p];

for (int p = i; p >= 0; p = (p & (p + 1)) - 1)


dist -= fenw[p];

if (in[pos].second == 1)
--dist;

ans += dist;
for (int p = i; p < SZ(fenw); p = p | (p + 1))
fenw[p] -= 1;
}

return ans;
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 13

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/6-15.in", "r", stdin) ;


std::freopen("shoes.out", "w", stdout) ;

int n;
//assert(1 == scanf("%d", &n));
n = read_int();

vector<int> S(2 * n);


for (int i = 0; i < 2 * n; i++)
//assert(1 == scanf("%d", &S[i]));
S[i] = read_int();

fclose(stdin);

auto t2 = clock();

long long result = count_swaps(S);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<result<<’\n’<<’\n’;
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.1.10: shoes-jonathanirvings+grader.cpp


#include "shoes.h"

#include <bits/stdc++.h>

using namespace std;

namespace testcaseCheck
{

const int SUBTASK = 6;

bool permutationsOfShoes(vector<int> S, int N)


{
vector<int> V;
for (int i = 1; i <= N; ++i)
{
V.push_back(-i);
V.push_back(i);
}
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 14

sort(S.begin(), S.end());
sort(V.begin(), V.end());
for (int i = 0; i < 2 * N; ++i)
{
if (S[i] != V[i])
{
return false;
}
}

return true;
}

void run(std::vector<int> S)
{
assert(1 <= SUBTASK && SUBTASK <= 6);

assert(S.size() % 2 == 0);
int N = S.size() / 2;
assert(1 <= N && N <= 100000);

map<int, int> sizeToOccurences;


for (int s : S)
{
sizeToOccurences[abs(s)] += s / abs(s);
}
for (pair<int, int> occurences : sizeToOccurences)
{
assert(occurences.second == 0);
}

if (SUBTASK == 1)
{
assert(N == 1);
}

if (SUBTASK == 2)
{
assert(N <= 8);
}

if (SUBTASK == 3)
{
for (int s : S)
{
assert(abs(s) == abs(S[0]));
}
}

if (SUBTASK == 4)
{
for (int i = 0; i < N; ++i)
{
assert(abs(S[i]) == S[i + N]);
}
}

if (SUBTASK == 5)
{
assert(N <= 1000);
}
}
} // namespace testcaseCheck

struct FenwickTree
{
vector<int> arr;

FenwickTree(int n)
{
arr.resize(n + 1, 0);
}

void update(int x,int y)


{
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 15

for (int i = x + 1; i < arr.size(); i += (i & -i))


{
arr[i] += y;
}
}

int query(int x)
{
int ret = 0;
for (int i = x + 1; i > 0; i -= (i & -i))
{
ret += arr[i];
}
return ret;
}
};

long long count_swaps(std::vector<int> S)


{
testcaseCheck::run(S);

long long answer = 0;


int N = S.size() / 2;

FenwickTree fenwickTree(2 * N);


for (int i = 0; i < 2 * N; ++i)
{
fenwickTree.update(i, 1);
}

map<int, vector<int>> positions;


for (int i = 2 * N - 1; i >= 0; --i)
{
positions[S[i]].push_back(i);
}

for (int i = 0; i < 2 * N; ++i)


{
if (fenwickTree.query(i) == fenwickTree.query(i - 1))
{
continue;
}
if (S[i] > 0)
{
++answer;
}
int pos = positions[-S[i]][positions[-S[i]].size() - 1];
answer += fenwickTree.query(pos - 1) - fenwickTree.query(i);
fenwickTree.update(i, -1);
fenwickTree.update(pos, -1);

positions[S[i]].pop_back();
positions[-S[i]].pop_back();
}

return answer;
}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/6-15.in", "r", stdin) ;


std::freopen("shoes.out", "w", stdout) ;
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 16

int n;
//assert(1 == scanf("%d", &n));
n = read_int();

vector<int> S(2 * n);


for (int i = 0; i < 2 * n; i++)
//assert(1 == scanf("%d", &S[i]));
S[i] = read_int();

fclose(stdin);

auto t2 = clock();

long long result = count_swaps(S);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<result<<’\n’<<’\n’;
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.1.11: shoes-kuzey ok+grader.cpp


// --- Ali Ahmadi ali.ahmadi.star27@gmail.com ---
// correct solution with fenwick

#include <bits/stdc++.h>
#include "shoes.h"

#define lng long long

using namespace std;

const int maxn = 2002002;

int fen[maxn];
int _pos[maxn];
int *pos = _pos + maxn / 2;

void add(int x)
{
for (x ++; x < maxn; x += x & -x)
fen[x] ++;
}

int get(int x)
{
int r = 0;
for (x ++; x; x -= x & -x)
r += fen[x];
return r;
}

vector<int> change(vector<int> v)
{
int n = (int)v.size() / 2;
vector<int> cnt(n, 0);
for(int i = 0; i < 2*n; i++)
if(v[i] > 0)
cnt[v[i] - 1]++;

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


cnt[i] += cnt[i-1];
CAPITOLUL 1. IOI 2019 1.1. ARRANGING SHOES 17

vector<int> cntl = cnt, cntr = cnt;

for(int i = 0; i < 2*n; i++)


if(v[i] > 0)
v[i] = (--cntr[v[i] - 1]) + 1;
else
v[i] = -((--cntl[-v[i] - 1]) + 1);
return v;
}

lng count_swaps(vector<int> S)
{
S = change(S);
memset(fen, 0, sizeof fen);
memset(_pos, 0, sizeof _pos);

lng r = 0;
for (int i = 0; i < S.size(); i ++)
pos[S[i]] = i;

for (int i = 0; i < S.size(); i ++)


if (S[i])
{
r += pos[-S[i]] - get(pos[-S[i]]) - (S[i] < 0);
add(i); add(pos[-S[i]]); S[pos[-S[i]]] = 0; S[i] = 0;
}
return r;
}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/6-15.in", "r", stdin) ;


std::freopen("shoes.out", "w", stdout) ;

int n;
//assert(1 == scanf("%d", &n));
n = read_int();

vector<int> S(2 * n);


for (int i = 0; i < 2 * n; i++)
//assert(1 == scanf("%d", &S[i]));
S[i] = read_int();

fclose(stdin);

auto t2 = clock();

long long result = count_swaps(S);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 18

std::cout <<result<<’\n’<<’\n’;
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

1.1.3 *Rezolvare detaliată

1.2 Split the Attractions


Problema 2 - Split the Attractions 100 de puncte

Authors: Alireza Farhadi, Saeed Seddighin

Există n atracţii ı̂n Baku, numerotate de la 0 la n  1. De asemenea, există m drumuri


bidirecţionale, numerotate de la 0 la m  1. Fiecare drum leagă două atracţii diferite. Se poate
călători ı̂ntre oricare două atracţii folosind drumurile date.
Fatima planifică să viziteze toate atracţiile ı̂n trei zile. Ea va ı̂mpărţi cele n atracţii ı̂n trei
mulţimi A, B şi C de dimensiuni a, b respectiv c. Fiecare atracţie va aparţine exact unei mulţimi,
deci a  b  c n.
Fatima doreşte să identifice mulţimile A, B şi C, astfel ı̂ncât cel puţin două din cele trei
mulţimi să fie conectate.
O mulţime de S atracţii se numeşte conectată dacă ı̂n mulţimea S există posibilitatea de a
călători ı̂ntre oricare două atracţii utilizând drumuri, fără a trece prin atracţii ce nu fac parte din
S.
O ı̂mpărţire a atracţiilor ı̂n mulţimile A, B şi C se numeşte validă dacă ea satisface condiţiile
descrise mai sus.
Ajutaţi-o pe Fatima să găsească o ı̂mpărţire validă a atracţiilor (cunoscându-se a, b şi c), sau
să determine dacă o astfel de ı̂mpărţire nu există.
Dacă există mai multe ı̂mpărţiri valide, puteţi găsi oricare din ele.

Detalii de implementare

Trebuie să implementaţi următoarea funcţie:


int[] find_split(int n, int a, int b, int c, int[] p, int[] q)
a n: numărul de atracţii.
a a, b, şi c: dimensiunile dorite ale mulţimilor A, B şi C.
a p şi q: vectori de lungime m, care conţin extremităţile drumurilor. Pentru fiecare i (0 & i &
m  1), pi şi q i sunt două atracţii conectate prin drumul i.
a Această procedură va returna un vector de lungime n. Notăm acest vector prin s. Dacă nu
există o ı̂mpărţire validă, atunci s va conţine n zerouri. Altfel, pentru fiecare 0 & i & n  1, si
trebuie să fie una din valorile 1, 2, sau 3 ı̂nsemnând că atracţia i este atribuită mulţimii A, B
respectiv C.

Exemple

Exemplul 1
Se consideră următorul apel:
find_split(9, 4, 2, 3, [0, 0, 0, 0, 0, 0, 1, 2, 4, 5],
[1, 3, 4, 6, 7, 8, 2, 3, 5, 6])
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 19

Figura 1.2: Split1

O posibilă soluţie corectă este 1, 1, 1, 1, 2, 2, 3, 3, 3. Această soluţie descrie următoarea
ı̂mpărţire: A r0, 1, 2, 3x, B r4, 5x, şi C r6, 7, 8x. Mulţimile A şi B sunt conectate.
Exemplul 2
Se consideră următorul apel:
find_split(6, 2, 2, 2, [0, 0, 0, 0, 0],
[1, 2, 3, 4, 5])

Figura 1.3: split2

Nu există ı̂mpărţiri valide. Prin urmare, răspunsul corect este 0, 0, 0, 0, 0, 0.

Restricţii

a 3 & n & 100000


a 2 & m & 200000
a 1 & a, b, c & n
a abc n
a Există cel mult un drum ı̂ntre oricare două atracţii.
a Există posibilitatea de a călători ı̂ntre oricare două atracţii utilizând drumurile date.
a 0 & pi, q i & n  1 şi pi j q i pentru 0 & i & m  1

Subtaskuri

1. (7 puncte) Fiecare atracţie reprezintă extremitatea a cel puţin două drumuri.


2. (11 puncte) a 1
3. (22 de puncte) m n  1
4. (24 de puncte) n & 2500, m & 5000
5. (36 de puncte) Fără restricţii suplimentare.

Exemplu de grader

Grader-ul citeşte din input ı̂n următorul format:


linia 1: n m
linia 2: a b c
linia 3  i (oricare 0 & i & m  1): pi q i
Grader-ul afişează pe o singură linie vectorul returnat de find_split.
Timp maxim de executare/test: 2.0 secunde
Memorie: total 1024 MB
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 20

1.2.1 Indicaţii de rezolvare

Subtask 1
In this subtask, the given graph is a path or a cycle. Therefore, the answer is always YES
and a solution can be easily found by partitioning a path: v1 , v2 , ..., vn into A v1 , v2 , ..., va ,
B va1 , va2 , ..., vab , and C vab1 , vab2 , ..., vn . In case of a cycle, we can cut the cycle
in any place and apply the similar solution as we had in path.
Subtask 2
In this subtask, the answer is always YES as follows. Let us assume that a & b & c. Find
a connected subgraph of size b (like using DFS or any graph traversing algorithm) as subset B,
construct subset A consisting of an arbitrary vertex outside of B, and other vertices are set C.
Subtask 3
In this subtask, the given graph is a tree. The solution is similar to that of the the last subtask
but the cases to be considered are easier. Without loss of generality suppose a & b & c. One
solution is to run a DFS on an arbitrary vertex to find some arbitrary spanning tree and find a
vertex v such that the size of the subtree of v [including v] denoted by size v  is at least a, but
the sizes of the subtrees of all children of v are less than a. Remove the edge between v and the
parent of v which partition the tree into two connected components.
If both of the components have a size more than a, then the answer is YES.
Otherwise, the answer is no since removing v from the original graph only produces components
of size less than a.
If the answer is YES, then a connected subgraph of size a (namely A) from the smallest
component, and a connected subgraph of size b (namely B) from the smallest component can be
extracted (since b & c, the size of the larger component is at least b).
Finally, C consists of all of the remaining vertices.
Subtask 4
The input of this subtask is similar to that of the final subtask. However, since the limits for
n and m are more restricted, one can use n instances of DFS instead of only one.
Subtast 5
Similar to the solution of Subtask 3, assume we run a DFS on an arbitrary vertex and found
a vertex v such that size v  ' a but the sizes of the subtrees of the children of v are all smaller
than a.
Note that other edges outside the DFS tree are backward edges. Hence, the children of v may
have backward edges to v or the ancestors of v, but no edge exists between them.
Suppose we want to partition the graph into two connected components by only considering
the edges of the DFS tree and removing the edge between v and the parent of v.
In this way the graph is partitioned into part P1 consisting of v and its subtree and P2 consisting
of all other vertices.
Before proceeding, we check every child of v (in an arbitrary order) like u and if the following
conditions hold, we remove its subtree from P1 and add it to P2 .
The subtree of u has a backward edge to the ancestors of v. The size of P1 after removing the
subtree of u is still at least a. By doing so, one by one, we may encounter a child of v, namely u,
such that
¶P1 ¶ ' a and ¶P1 ¶  size u $ a
Hence,
¶P2 ¶ % n  2a a  b  c  2a b  c  a ' b
In the other case, either ¶P2 ¶ % a, which is also good since ¶P1 ¶ % n  b a  b  c  b a  c % b,
or ¶P2 ¶ $ a and the answer is NO, since v is a cut vertex such that removing it from the original
graph produces several components where all of them are of size less than a.

1.2.2 Coduri sursă


CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 21

Listing 1.2.1: compile cpp.sh


#!/bin/bash

problem=split

g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"

"grader.cpp" "${problem}.cpp"

Listing 1.2.2: split.h


#include <vector>

std::vector<int> find_split(int n, int a, int b, int c, std::vector<int> p,


std::vector<int> q);

Listing 1.2.3: split.cpp


#include "split.h"

using namespace std;

vector<int> find_split(int n, int a, int b, int c, vector<int> p, vector<int> q)


{
vector<int> res;
if (n == 9)
{
res = {1, 1, 3, 1, 2, 2, 3, 1, 3};
}
else
{
res = {0, 0, 0, 0, 0, 0};
}
return res;
}

Listing 1.2.4: grader.cpp


#include "split.h"
#include <cstdio>
#include <cassert>

using namespace std;

int main()
{
int n, m, a, b, c;

assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));

vector<int> p(m), q(m);


for (int i=0; i<m; i++)
assert(2 == scanf("%d%d", &p[i], &q[i]));
fclose(stdin);

vector<int> result = find_split(n, a, b, c, p, q);

for (int i=0; i<(int)result.size(); i++)


printf("%s%d", ((i>0)?" ":""), result[i]);
printf("\n");
fclose(stdout);

return 0;
}

Listing 1.2.5: split-model.cpp


#include "bits/stdc++.h"
#include "split.h"

using namespace std;


CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 22

vector <int> find_split(int n, int a, int b, int c, vector <int> p,


vector <int> q)
{
vector <pair <int, int> > edges;

for(int i=0; i<(int)p.size(); i++) edges.emplace_back(p[i], q[i]);

vector <pair <int, int> > sizes = {{a,0}, {b,1}, {c,2}};

sort(sizes.begin(), sizes.end());
a = sizes[0].first; b = sizes[1].first; c = sizes[2].first;

vector <int> rep(n), siz(n);

for(int i=0; i<n; i++)


{
rep[i] = i;
siz[i] = 1;
}

function <int(int)> find = [&](int x)


{
if(rep[x] == x) return x;
return rep[x] = find(rep[x]);
};

auto unia = [&](int aa, int bb) -> bool


{
int fa = find(aa), fb = find(bb);
if(fa == fb) return false;
if(siz[fa] < siz[fb]) swap(fa, fb);
siz[fa] += siz[fb];
rep[fb] = fa;
return true;
};

vector <vector <int> > G(n);


vector <pair <int, int> > non_tree;

for(auto edge : edges)


{
if(unia(edge.first, edge.second))
{
G[edge.first].push_back(edge.second);
G[edge.second].push_back(edge.first);
}
else
{
non_tree.push_back(edge);
}
}

vector <bool> vis(n);


vector <int> subtree(n);

function<void(int)> dfs1 = [&](int v)


{
vis[v] = true;
subtree[v] = 1;
for(auto w : G[v]) if(!vis[w])
{
dfs1(w);
subtree[v] += subtree[w];
}
};

dfs1(0);

vis = vector<bool>(n, false);


pair <int, int> both_dir = {-1, -1};
vector <pair <int, int> > dir;

function<void(int)> dfs2 = [&](int v)


{
vis[v] = true;
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 23

for(auto w : G[v]) if(!vis[w])


{
int below = subtree[w], above = n - subtree[w];
if(min(below, above) >= a)
{
if(above < below) both_dir = {v, w};
else both_dir = {w, v};
}
else
{
if(below < a) dir.emplace_back(v, w);
else if(above < a) dir.emplace_back(w, v);
}
dfs2(w);
}
};

dfs2(0);

auto find_solution = [&](int pa, int pb) -> vector<int>


{
vector <int> ret(n, -1);
int counter;

function<void(int, int)> dfs3 = [&](int v, int mark)


{
if(counter == 0) return;
--counter;
ret[v] = mark;
for(auto w : G[v]) if(ret[w] == -1) dfs3(w, mark);
};

ret[pa] = sizes[0].second;
ret[pb] = sizes[a].second;
counter = a;
dfs3(pa, sizes[0].second);
vis[pb] = sizes[1].second;
counter = b;
dfs3(pb, sizes[1].second);
vis[pa] = false;
for(int i=0; i<n; i++) if(ret[i] == -1) ret[i] = sizes[2].second;
for(int i=0; i<n; i++) ret[i]++;
return ret;
};

if(both_dir.first != -1)
{
return find_solution(both_dir.first, both_dir.second);
}
else
{
vector <int> in_deg(n);
for(auto edge : dir) in_deg[edge.second]++;
vector <int> CH;
for(int i=0; i<n; i++) if(in_deg[i] == 0) CH.push_back(i);
assert((int)CH.size() == 1);
vis = vector<bool>(n, false);
subtree = vector<int>(n, 0);
int root = CH.back();
for(auto edge : non_tree)
{
G[edge.first].push_back(edge.second);
G[edge.second].push_back(edge.first);
}
dfs1(root);
for(auto son : G[root])
{
if(subtree[son] >= a) return find_solution(son, root);
}
return vector<int>(n, 0);
}
}

Listing 1.2.6: split-kostka-full+grader.cpp


CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 24

#include "bits/stdc++.h"
#include "split.h"

#include <time.h> /* clock */


#include <stdio.h>

using namespace std;

vector <int> find_split(int n, int a, int b, int c,


vector <int> p, vector <int> q)
{
vector <pair <int, int> > edges;

for(int i=0; i<(int)p.size(); i++)


edges.emplace_back(p[i], q[i]);

vector <pair <int, int> > sizes = {{a,0}, {b,1}, {c,2}};

sort(sizes.begin(), sizes.end());

a = sizes[0].first;
b = sizes[1].first;
c = sizes[2].first;

vector <int> rep(n), siz(n);

for(int i=0; i<n; i++)


{
rep[i] = i;
siz[i] = 1;
}

function <int(int)> find = [&](int x)


{
if(rep[x] == x) return x;
return rep[x] = find(rep[x]);
};

auto unia = [&](int aa, int bb) -> bool


{
int fa = find(aa), fb = find(bb);
if(fa == fb) return false;
if(siz[fa] < siz[fb]) swap(fa, fb);
siz[fa] += siz[fb];
rep[fb] = fa;
return true;
};

vector <vector <int> > G(n);


vector <pair <int, int> > non_tree;

for(auto edge : edges)


{
if(unia(edge.first, edge.second))
{
G[edge.first].push_back(edge.second);
G[edge.second].push_back(edge.first);
}
else
{
non_tree.push_back(edge);
}
}

vector <bool> vis(n);


vector <int> subtree(n);

function<void(int)> dfs1 = [&](int v)


{
vis[v] = true;
subtree[v] = 1;
for(auto w : G[v]) if(!vis[w])
{
dfs1(w);
subtree[v] += subtree[w];
}
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 25

};

dfs1(0);

vis = vector<bool>(n, false);


pair <int, int> both_dir = {-1, -1};
vector <pair <int, int> > dir;

function<void(int)> dfs2 = [&](int v)


{
vis[v] = true;
for(auto w : G[v]) if(!vis[w])
{
int below = subtree[w], above = n - subtree[w];
if(min(below, above) >= a)
{
if(above < below) both_dir = {v, w};
else both_dir = {w, v};
}
else
{
if(below < a) dir.emplace_back(v, w);
else if(above < a) dir.emplace_back(w, v);
}
dfs2(w);
}
};

dfs2(0);

auto find_solution = [&](int pa, int pb) -> vector<int>


{
vector <int> ret(n, -1);
int counter;

function<void(int, int)> dfs3 = [&](int v, int mark)


{
if(counter == 0) return;
--counter;
ret[v] = mark;
for(auto w : G[v]) if(ret[w] == -1) dfs3(w, mark);
};

ret[pa] = sizes[0].second;
ret[pb] = sizes[a].second;

counter = a;
dfs3(pa, sizes[0].second);
vis[pb] = sizes[1].second;

counter = b;
dfs3(pb, sizes[1].second);
vis[pa] = false;

for(int i=0; i<n; i++)


if(ret[i] == -1) ret[i] = sizes[2].second;
for(int i=0; i<n; i++)
ret[i]++;

return ret;
};

if(both_dir.first != -1)
{
return find_solution(both_dir.first, both_dir.second);
}
else
{
vector <int> in_deg(n);
for(auto edge : dir) in_deg[edge.second]++;
vector <int> CH;
for(int i=0; i<n; i++) if(in_deg[i] == 0) CH.push_back(i);
assert((int)CH.size() == 1);
vis = vector<bool>(n, false);
subtree = vector<int>(n, 0);
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 26

int root = CH.back();


for(auto edge : non_tree)
{
G[edge.first].push_back(edge.second);
G[edge.second].push_back(edge.first);
}

dfs1(root);

for(auto son : G[root])


{
if(subtree[son] >= a) return find_solution(son, root);
}

return vector<int>(n, 0);


}
}

namespace
{

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

//std::freopen("../tests/1-03.in", "r", stdin) ;


std::freopen("../tests/5-34.in", "r", stdin) ; // execution time : 1.141 s

std::freopen("split.out", "w", stdout) ;

int n, m, a, b, c;
//assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
n = read_int();
m = read_int();
a = read_int();
b = read_int();
c = read_int();

vector<int> p(m), q(m);


for (int i=0; i<m; i++)
//assert(2 == scanf("%d%d", &p[i], &q[i]));
p[i] = read_int(), q[i] = read_int();

fclose(stdin);

auto t2 = clock();

vector<int> result = find_split(n, a, b, c, p, q);

auto t3 = clock();

for (int i=0; i<(int)result.size(); i++)


printf("%s%d", ((i>0)?" ":""), result[i]);
printf("\n");
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 27

return 0;
}

Listing 1.2.7: split 144514+grader.cpp


// https://oj.uz/submission/144514

#include "../split.h"
#include<bits/stdc++.h>
using namespace std;
const int mn=2e5+10;
int p[mn],si[mn];

inline int f(int x)


{
return x==p[x]?x:(p[x]=f(p[x]));
}

inline int siz(int x)


{
return si[f(x)];
}

void mrg(int a,int b)


{
a=f(a),b=f(b);
if(a==b);
else
if(si[a]>si[b])
si[a]+=si[b],p[b]=a;
else
si[b]+=si[a],p[a]=b;
}

void init()
{
iota(p,p+mn,0);
fill(si,si+mn,1);
}

bool u[mn];
vector<int>g[mn],ans;
int s[mn];
int ctr,n;

void dfs(int x,int p)


{
s[x]=1;
for(int y:g[x])
{
if(y==p)continue;
dfs(y,x);
s[x]+=s[y];
}
}

int fc(int x,int p)


{
for(int y:g[x])
{
if(y==p )continue;
if(s[y]*2>=n) return fc(y,x);
}
return x;
}

void dfs2(int x,int p)


{
for(int y:g[x])
{
if(y==p)continue;
dfs2(y,x);
mrg(x,y);
}
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 28

int lef,tar;
bool vis[mn];
int conv[4];

void dfs3(int x)
{
if(!lef)return;
vis[x]=1;
lef--;
ans[x]=conv[tar];
for(int y:g[x])
{
if(!vis[y])
{
dfs3(y);
if(!lef)return;
}
}
}

vector<int> find_split(int N, int a, int b, int c,


vector<int> p, vector<int> q)
{
int sm=min(a,min(b,c)),bi=max(a,max(b,c));

if(a==sm)
conv[1]=1;
else
if(a==bi)
conv[3]=1;
else
conv[2]=1;

if(b==sm&&!conv[1]) conv[1]=2;
else
if(b==bi&&!conv[3])conv[3]=2;
else conv[2]=2;

if(c==sm&&!conv[1])conv[1]=3;
else
if(c==bi&&!conv[3])conv[3]=3;
else conv[2]=3;

b=a+b+c-sm-bi;
a=sm;
c=bi;
n=N;
ans.resize(n,conv[3]);
int m=p.size(),i;

init();
for(i=0;i<m;i++)
{
if(f(p[i])!=f(q[i]))
{
mrg(p[i],q[i]);
g[p[i]].push_back(q[i]);
g[q[i]].push_back(p[i]);
}
}

dfs(0,-1);

ctr=fc(0,-1);
init();
for(int y:g[ctr])
{
dfs2(y,ctr);
if(siz(y)>=a)
{
vis[ctr]=1;
lef=a;
tar=1;
dfs3(y);
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 29

lef=b;
tar=2;
vis[ctr]=0;
dfs3(ctr);
return ans;
}
}

for(i=0;i<m;i++)
{
if(p[i]==ctr||q[i]==ctr)continue;
if(f(p[i])==f(q[i]))continue;
mrg(p[i],q[i]);
g[p[i]].push_back(q[i]);
g[q[i]].push_back(p[i]);
if(siz(p[i])>=a)
{
vis[ctr]=1;
lef=a;
tar=1;
dfs3(p[i]);
lef=b;
tar=2;
vis[ctr]=0;
dfs3(ctr);
return ans;
}
}

fill(ans.begin(),ans.end(),0);
return ans;
}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

//std::freopen("../tests/1-03.in", "r", stdin) ;


std::freopen("../tests/5-34.in", "r", stdin) ;

std::freopen("split.out", "w", stdout) ;

int n, m, a, b, c;
//assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
n = read_int();
m = read_int();
a = read_int();
b = read_int();
c = read_int();

vector<int> p(m), q(m);


for (int i=0; i<m; i++)
//assert(2 == scanf("%d%d", &p[i], &q[i]));
p[i] = read_int(), q[i] = read_int();

fclose(stdin);

auto t2 = clock();

vector<int> result = find_split(n, a, b, c, p, q);

auto t3 = clock();

for (int i=0; i<(int)result.size(); i++)


printf("%s%d", ((i>0)?" ":""), result[i]);
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 30

printf("\n");
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.2.8: split koosaga+grader.cpp


// https://github.com/koosaga/olympiad/blob/master/IOI/ioi19_split.cpp
#include "../split.h"
#include <bits/stdc++.h>

#define sz(v) ((int)(v).size())

using namespace std;

using pi = pair<int, int>;


const int MAXN = 100005;

int n;
vector<int> gph[MAXN];
vector<int> tr[MAXN];

namespace report
{
vector<int> gph[MAXN];
int mark[MAXN], vis[MAXN];
void dfs(int x, vector<int> &dfn)
{
dfn.push_back(x);
vis[x] = 1;
for(auto &i : gph[x])
{
if(mark[x] == mark[i] && !vis[i])
{
dfs(i, dfn);
}
}
}

vector<pi> color;
vector<int> Do(vector<int> S)
{
for(auto &i : S) mark[i] = 1;
vector<int> ans(n, color[2].second);
for(int i=0; i<n; i++)
{
vector<int> dfn;
if(mark[i] == 1 && !vis[i])
{
dfs(i, dfn);
for(int j=0; j<color[0].first; j++)
{
ans[dfn[j]] = color[0].second;
}
}

if(mark[i] == 0 && !vis[i])


{
dfs(i, dfn);
for(int j=0; j<color[1].first; j++)
{
ans[dfn[j]] = color[1].second;
}
}
}
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 31

return ans;
}
}

struct disj
{
int pa[MAXN], sz[MAXN];
void init(int n)
{
iota(pa, pa + n + 1, 0);
fill(sz, sz + n + 1, 1);
}

int find(int x)
{
return pa[x] = (pa[x] == x ? x : find(pa[x]));
}

int getsz(int x)
{
return sz[find(x)];
}

bool uni(int p, int q)


{
p = find(p); q = find(q);
if(p == q) return 0;
sz[p] += sz[q];
pa[q] = p;
return 1;
}
} disj;

int sz[MAXN], msz[MAXN];

void dfsc(int x, int p)


{
sz[x] = 1; msz[x] = 0;
for(auto &i : tr[x])
{
if(i != p)
{
dfsc(i, x);
sz[x] += sz[i];
msz[x] = max(msz[x], sz[i]);
}
}
}

int get_center()
{
dfsc(0, 0);
pi ret(1e9, 1e9);
for(int i=0; i<n; i++)
{
ret = min(ret, pi(max(msz[i], n - sz[i]), i));
}
return ret.second;
}

void dfs(int x, int p, vector<int> &dfn)


{
dfn.push_back(x);
for(auto &i : tr[x])
{
if(i != p)
{
disj.uni(x, i);
dfs(i, x, dfn);
}
}
}

bool vis[MAXN];
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 32

void dfsa(int x, vector<int> &dfn)


{
dfn.push_back(x);
vis[x] = 1;
for(auto &i : gph[x])
{
if(!vis[i]) dfsa(i, dfn);
}
}

vector<int> find_split(int _n, int a, int b, int _c,


vector<int> p, vector<int> q)
{
n = _n;
vector<pi> cs = {pi(a, 1), pi(b, 2), pi(_c, 3)};

sort(cs.begin(), cs.end());

report::color = cs;
a = cs[0].first;
disj.init(n);
for(int i=0; i<sz(p); i++)
{
if(disj.uni(p[i], q[i]))
{
tr[p[i]].push_back(q[i]);
tr[q[i]].push_back(p[i]);
}
report::gph[p[i]].push_back(q[i]);
report::gph[q[i]].push_back(p[i]);
}

disj.init(n);
int c = get_center();
for(auto &i : tr[c])
{
vector<int> dfn;
dfs(i, c, dfn);
if(disj.getsz(i) >= a)
{
return report::Do(dfn);
}
}

for(int i=0; i<sz(p); i++)


{
if(p[i] == c || q[i] == c) continue;
int l = disj.find(p[i]);
int r = disj.find(q[i]);
if(l != r)
{
gph[l].push_back(r);
gph[r].push_back(l);
}
}

for(int i=0; i<n; i++)


{
if(i != c && disj.find(i) == i && !vis[i])
{
vector<int> dfn;
dfsa(i, dfn);
int sum = 0;
set<int> pot;
for(auto &i : dfn)
{
sum += disj.getsz(i);
pot.insert(i);
if(sum >= a) break;
}

if(sum >= a)
{
vector<int> ans;
for(int i=0; i<n; i++)
{
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 33

if(pot.find(disj.find(i)) != pot.end())
{
ans.push_back(i);
}
}
return report::Do(ans);
}
}
}

return vector<int>(n, 0);


}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

//std::freopen("../tests/1-03.in", "r", stdin) ;


std::freopen("../tests/5-34.in", "r", stdin) ;

std::freopen("split.out", "w", stdout) ;

int n, m, a, b, c;
//assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
n = read_int();
m = read_int();
a = read_int();
b = read_int();
c = read_int();

vector<int> p(m), q(m);


for (int i=0; i<m; i++)
//assert(2 == scanf("%d%d", &p[i], &q[i]));
p[i] = read_int(), q[i] = read_int();

fclose(stdin);

auto t2 = clock();

vector<int> result = find_split(n, a, b, c, p, q);

auto t3 = clock();

for (int i=0; i<(int)result.size(); i++)


printf("%s%d", ((i>0)?" ":""), result[i]);
printf("\n");
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.2.9: split-mahdi+grader.cpp


//IOI 2019 execution time : 0.891 s
//Split - Version 1.1 - First Solution
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 34

//Current Version: 9 May 2019


//Older version: 31 March 2019
//Mahdi Safarnejad Boroujeni

#define forn(i, n) for (int i = 0; i < int(n); i++)

#include "../split.h"

#include <vector>
#include <algorithm>
#include<iostream>
#include<ctime>

using namespace std;

const int maxn = 1000000+110;


typedef pair<int,int> intpair;

int n;
vector<int> a[maxn];
vector<int> result(maxn);
intpair b[3];

bool finishedPhase1 = false;

int mark[maxn], sizev[maxn];


int counter=1, startingtime[maxn], mintime[maxn];

int dfs2(int v, int goal, int comp, bool ignore_st=false)


{
int sum=1;
mark[v]=2;
result[v]=comp;
goal -= 1;
forn(i, a[v].size())
if (goal > 0 && mark[a[v][i]] < 2 &&
(ignore_st || (startingtime[a[v][i]] > startingtime[v])))
{
int thisSize = dfs2(a[v][i], goal, comp, ignore_st);
sum += thisSize;
goal -= thisSize;
}
return sum;
}

void dfs(int v, int par)


{
mark[v]=1;
sizev[v]=1;
startingtime[v] = counter++;
mintime[v] = startingtime[v];
int removablesSum=0;

forn(i, a[v].size())
if (!mark[a[v][i]])
{
dfs(a[v][i], v);
if (finishedPhase1)
return;
sizev[v]+=sizev[a[v][i]];
mintime[v] = min(mintime[v], mintime[a[v][i]]);
if (mintime[a[v][i]] < startingtime[v])
removablesSum += sizev[a[v][i]];
}
else
if (a[v][i]!=par)
{
mintime[v] = min(mintime[v], mintime[a[v][i]]);
}

if (sizev[v] >= b[0].first)


{
finishedPhase1 = true;
if (n - sizev[v] + removablesSum < b[0].first)
return; //No Solution
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 35

int element = 0;
if (n - sizev[v] + removablesSum < b[1].first)
element = 1;

result[v] = b[element].second;
mark[v] = 2;
int goal = b[element].first - 1;
forn(i, a[v].size())
{
if (mark[a[v][i]] < 2 &&
goal > 0 &&
mintime[a[v][i]] >= startingtime[v] &&
startingtime[a[v][i]] > startingtime[v])
goal -= dfs2(a[v][i], goal, b[element].second);
}

forn(i, a[v].size())
{
if (mark[a[v][i]] < 2 &&
goal > 0 &&
mintime[a[v][i]] < startingtime[v] &&
startingtime[a[v][i]] > startingtime[v])
goal -= dfs2(a[v][i], goal, b[element].second);
}

dfs2(0, b[1-element].first, b[1-element].second, true);


forn(i, n)
if (result[i]==0)
result[i]=b[2].second;
}
}

vector <int> find_split(int n_, int a_, int b_, int c_,
vector <int> p, vector <int> q)
{
n = n_;
b[0]=intpair(a_, 1);
b[1]=intpair(b_, 2);
b[2]=intpair(c_, 3);

sort(b, b+3);

forn(i, p.size())
{
a[p[i]].push_back(q[i]);
a[q[i]].push_back(p[i]);
}

dfs(0, -1);

result.resize(n);
return result;
}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

int main()
{
auto t1 = clock();

//std::freopen("../tests/1-03.in", "r", stdin) ;


std::freopen("../tests/5-34.in", "r", stdin) ;

std::freopen("split.out", "w", stdout) ;

int n, m, a, b, c;
//assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 36

n = read_int();
m = read_int();
a = read_int();
b = read_int();
c = read_int();

vector<int> p(m), q(m);


for (int i=0; i<m; i++)
//assert(2 == scanf("%d%d", &p[i], &q[i]));
p[i] = read_int(), q[i] = read_int();

fclose(stdin);

auto t2 = clock();

vector<int> result = find_split(n, a, b, c, p, q);

auto t3 = clock();

for (int i=0; i<(int)result.size(); i++)


printf("%s%d", ((i>0)?" ":""), result[i]);
printf("\n");
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

Listing 1.2.10: split-maroon-accepted+grader.cpp


#include "../split.h"
#include <bits/stdc++.h>

using namespace std;

using ll=long long;

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)


#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=a;i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define a first
#define b second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}


template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}

template<class t> using vc=vector<t>;


template<class t> using vvc=vc<vc<t>>;

using pi=pair<int,int>;
using vi=vc<int>;

template<class t,class u>


ostream& operator<<(ostream& os,const pair<t,u>& p)
{
return os<<"{"<<p.a<<","<<p.b<<"}";
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 37

template<class t> ostream& operator<<(ostream& os,const vc<t>& v)


{
os<<"{";
for(auto e:v)os<<e<<",";
return os<<"}";
}

const int nmmax=200010;

vc<pi> g[nmmax];
int ans[nmmax];
bool us[nmmax];

void puti(int v,int i,int&rem)


{
if(ans[v]||rem==0)return;
ans[v]=i;
rem--;
for(auto e:g[v])if(us[e.b])
puti(e.a,i,rem);
}

bool vis[nmmax];
//find any spanning tree
void dfs1(int v,int pe)
{
if(vis[v])return;
vis[v]=1;
if(pe!=-1)
us[pe]=1;
for(auto e:g[v])
dfs1(e.a,e.b);
}

//find a centroid
int dfs2(int v,int p,int n)
{
int s=1,mx=0;
for(auto e:g[v])if(us[e.b]&&p!=e.a)
{
int f=dfs2(e.a,v,n);
if(f<=0)return f;
chmax(mx,f);
s+=f;
}
chmax(mx,n-s);
if(mx*2<=n)return -v;
return s;
}

void dfs3(int v,int p,vi&w)


{
w.pb(v);
for(auto e:g[v])if(us[e.b]&&p!=e.a)
dfs3(e.a,v,w);
}

vector<int> find_split(int n, int a, int b, int c,


vector<int> p, vector<int> q)
{
vc<pi> si{pi(a,1),pi(b,2),pi(c,3)};
sort(all(si));

int m=p.size();
rep(i,m)
{
g[p[i]].eb(q[i],i);
g[q[i]].eb(p[i],i);
}

dfs1(0,-1);

int r=-dfs2(0,-1,n);
CAPITOLUL 1. IOI 2019 1.2. SPLIT THE ATTRACTIONS 38

vvc<int> cmps;
for(auto e:g[r])if(us[e.b])
{
vi w;
dfs3(e.a,r,w);
cmps.pb(w);
}

bool fd=false;
for(auto w:cmps)
{
if(int(w.size())>=si[0].a)
{
ans[r]=-1;
puti(w[0],si[0].b,si[0].a);
ans[r]=0;
puti(r,si[1].b,si[1].a);
fd=true;
break;
}
}

if(!fd)
{
vi idx(n,-1);
rep(i,cmps.size())
for(auto v:cmps[i])
idx[v]=i;

int z=idx[0];
if(z==-1) return vi(n,0);

int cur=cmps[z].size();
vi ad(cmps.size());
ad[z]=1;
rep(i,m)
{
int x=idx[p[i]],y=idx[q[i]];
if(y==z)swap(x,y);

if(x==z&&y!=-1&&!ad[y])
{
ad[y]=1;
cur+=cmps[y].size();
us[i]=1;
if(cur>=si[0].a)
{
ans[r]=-1;
puti(0,si[0].b,si[0].a);
ans[r]=0;
puti(r,si[1].b,si[1].a);
fd=true;
break;
}
}
}
}

if(!fd)return vi(n,0);

assert(si[0].a==0);
assert(si[1].a==0);
rep(i,n)
if(ans[i]==0)
ans[i]=si[2].b;
return vi(ans,ans+n);
}

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 39

return x;
}

int main()
{
auto t1 = clock();

//std::freopen("../tests/1-03.in", "r", stdin) ;


std::freopen("../tests/5-34.in", "r", stdin) ;

std::freopen("split.out", "w", stdout) ;

int n, m, a, b, c;
//assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
n = read_int();
m = read_int();
a = read_int();
b = read_int();
c = read_int();

vector<int> p(m), q(m);


for (int i=0; i<m; i++)
//assert(2 == scanf("%d%d", &p[i], &q[i]));
p[i] = read_int(), q[i] = read_int();

fclose(stdin);

auto t2 = clock();

vector<int> result = find_split(n, a, b, c, p, q);

auto t3 = clock();

for (int i=0; i<(int)result.size(); i++)


printf("%s%d", ((i>0)?" ":""), result[i]);
printf("\n");
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

1.2.3 *Rezolvare detaliată

1.3 Rectangles
Problema 3 - Rectangles 100 de puncte

Author: Peyman Jabbarzade

La ı̂nceputul secolului al 19-lea, conducătorul Hoseyngulu Khan Sardar ordonă să se constru-
iască un palat ı̂n platoul din oraşul Zangi. Platoul oraşului este modelat sub forma unei matrice
cu n  m celule pătratice. Rândurile matricei sunt numerotate de la 0 la n  1, iar coloanele
sunt numerotate de la 0 la m  1. Vom numi celula de pe linia i şi coloana j (0 & i & n  1,
0 & j & m  1), celula i, j . Fiecare celulă i, j  are o anumită ı̂nălţime, notată cu aij .
Hoseyngulu Khan Sardar şi-a rugat arhitecţii să aleagă o zonă dreptunghiulară ı̂n matrice
unde să contruiască palatul. Zona respectivă nu are voie să conţină nici o celulă aflată pe marginea
matricei (linia 0, linia n  1, coloana 0 şi coloana m  1). În concluzie, arhitecţii trebuie să aleagă
patru ı̂ntregi r1 , r2 , c1 şi c2 (1 & r1 , r2 & n  2 şi 1 & c  1, c2 & m  2), care vor defini o zonă ce
va conţine toate celulele i, j  cu proprietatea că r1 & i & r2 şi c1 & j & c2 .
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 40

În plus, o zonă este considerată validă dacă şi numai dacă pentru fiecare celulă i, j  din zona
respectivă, următoarea condiţie va avea loc:
a Se consideră cele două celule adiacente zonei selectate de pe linia i (celulele i, c1  1 şi
i, c2  1) şi cele două celule adiacente zonei de pe coloana j (celulele r1  1, j  şi r2  1, j ).
Înălţimea celulei trebuie să fie strict mai mică decât ı̂nălţimea celor patru celule menţionate mai
sus.
Misiunea voastră este să ı̂i ajutaţi pe arhitecţi să găsească numărul de zone valide ı̂n care poate
să fie stabilit palatul (numărul de moduri ı̂n care se pot alege valorile r1 , r2 , c1 şi c2 care definesc
o zonă validă).

Detalii de implementare

Trebuie să implementaţi următoarea funcţie:


int64 count_rectangles(int[][] a)
a a: o matrice de dimensiune n  m cu valori ı̂ntregi reprezentând ı̂nălţimile celulelor.
a Această funcţie trebuie să returneze numărul de zone valide pentru palat.

Exemple

Exemplul 1
Considerăm următoarea apelare.
count_rectangles([[4, 8, 7, 5, 6],
[7, 4, 10, 3, 5],
[9, 7, 20, 14, 2],
[9, 14, 7, 5, 6],
[5, 7, 5, 2, 7],
[4, 5, 13, 5, 6]])

Figura 1.4: rectangular

Sunt 5 zone valide menţionate mai jos:


a r1 r2 c1 c2 1
a r1 1, r2 2, c1 1, c2 1
a r1 1, r2 1, c1 3, c2 3
a r1 4, r2 4, c1 2, c2 3
a r1 4, r2 4, c1 3, c2 3
De exemplu, r1 1, r2 2, c1 1, c2 1 este o zonă validă deoarece următoarele condiţii se
respectă:
a a11 4 este strict mai mic decât a01 8, a31 14, a10 7 şi a12 10.
a a21 7 este strict mai mic decât a01 8, a31 14, a20 9 şi a22 20.

Restricţii

a 1 & n, m & 2500


a 0 & aij  & 7000000 (oricare 0 & i & n  1, 0 & j & m  1)
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 41

Subtaskuri

1. (8 puncte) n, m & 30
2. (7 puncte) n, m & 80
3. (12 puncte) n, m & 200
4. (22 de puncte) n, m & 700
5. (10 puncte) n & 3
6. (13 puncte) 0 & aij  & 1 (oricare 0 & i & n  1, 0 & j & m  1)
7. (28 de puncte) Fără restricţii suplimentare.

Exemplu de grader

Grader-ul citeşte din input ı̂n următorul format:


a linia 1: n m
a linia 2  i (oricare 0 & i & n  1): ai0 ai1 ... aim  1
Grader-ul afişează o singură linie ce conţine valoarea returnată de funcţia
count_rectangles.

Timp maxim de executare/test: 3.0 secunde


Memorie: total 1024 MB

1.3.1 Indicaţii de rezolvare

Subtasks 1, 2, and 3
6 4
Subtask 1 can be solved via a trivial O n  solution: considering all O n  rectangles and
2
checking whether each of them is valid in O n . Moreover, if we use breaks in loops for checking
whether a rectangle is valid, then this solution is accepted in Subtask 1, 2, and 3.
5 4
Moreover, an O n  solution exist which gets accepted in Subtask 1 and 2, and an O n 
solution exist which gets accepted in Subtask 1, 2, and 3.
Subtask 5
In this subtask, valid rectangles are all consecutive subsets of the middle. All of these rectangles
2
can be checked in time O m .
Subtask 6
This subtask is finding a 0 rectangle with 1 on outer borders (except corners). This is a classic
2 3
dynamic programming problem which can be solved in O n . Moreover, most of the O n  and
2
O n log n solutions run in O n  in this subtask.
Subtasks 4 and 7
Solution 1
We denote a pair of cells in a row or a column which are on the outer border of a valid rectangle
2
as a good pair. The total number of good pairs are bounded by O n  and can be found using a
stack and traversing over cells of each row and each column. Then, we can extend these good pairs
into two potential sides of a valid rectangle. Therefore, we have potential left and right sides, and
potential top and bottom sides of valid rectangles. Finally, we try all cells as the bottom-right
corner of valid rectangles and match sizes.
3
If the matching phase is done naively, we have a O n  solution which get accepted in subtask
4 but not Subtask 7. However, many optimizations results in getting accepted in Subtask 7. The
2
best of which is using a Fenwick tree which decreases the total running time downto O n log n.
Moreover, in order to get accepted in Subtask 7, one might not use time-consuming data
29
structure libraries such as STL maps .
29
time-consuming
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 42

Solution 2
A very useful observation is that in a good pair, if we look at the smaller element, the pair is
unique (one for the left direction, and one for the right direction).
Using this observation, we can change the data structure needed for keeping good pairs into
two simple arrays. For a valid rectangle, let a block corner be a 2  2 square that exactly one of
its elements is inside the rectangle. Each valid rectangle have exactly four block corners. We can
connect block corners of a valid rectangle which are in a row or a column based on whether which
one captures the larger element in the outer border of the row or column.
4
These connections define a structure for a valid rectangle. At most 2 16 structures exist
and for each structure there is a block corner that uniquely define the valid rectangle.
2 2
Therefore, the number of valid rectangles is bounded by O n . After finding these O n 
2
candidate rectangles, we can check each of them in O 1 after a preprocess in O n .

1.3.2 Coduri sursă

Listing 1.3.1: compile cpp.sh


#!/bin/bash

problem=rect

g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"

"grader.cpp" "${problem}.cpp"

Listing 1.3.2: rect.h


#include <vector>

long long count_rectangles(std::vector<std::vector<int> > a);

Listing 1.3.3: rect.cpp


#include "rect.h"

long long count_rectangles(std::vector<std::vector<int> > a)


{
return 1;
}

Listing 1.3.4: grader.cpp


#include "rect.h"
#include <cstdio>
#include <unistd.h>
#include <cassert>
#include <string>

using namespace std;

class InputReader
{
private:
static const int SIZE = 4096;

int inputFileDescriptor;
char buf[SIZE];
int curChar;
int numChars;

public:

inline InputReader(int _inputFileDescriptor):


inputFileDescriptor(_inputFileDescriptor),
curChar(0),
numChars(0) { }
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 43

inline void close()


{
::close(inputFileDescriptor);
}

inline char read()


{
assert(numChars != -1);
if (curChar >= numChars)
{
curChar = 0;
numChars = ::read(inputFileDescriptor, buf, SIZE);
if (numChars == -1)
return -1;
}
return buf[curChar++];
}

inline int readInt()


{
int c = eatWhite();
int sgn = 1;
if (c == ’-’)
{
sgn = -1;
c = read();
}
int res = 0;
do
{
assert(c >= ’0’ && c <= ’9’);
res *= 10;
res += c - ’0’;
c = read();
}
while (!isSpaceChar(c));
return res * sgn;
}

inline string readString()


{
char c = eatWhite();
string res;
do
{
res += c;
c = read();
} while (!isSpaceChar(c));
return res;
}

inline string readLine()


{
string res;
while (true)
{
char c = read();
if (c == ’\n’ || c == ’\r’ || c == -1)
break;
res += c;
}
return res;
}

inline char eatWhite()


{
char c = read();
while (isSpaceChar(c))
c = read();
return c;
}

static inline bool isSpaceChar(char c)


{
return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 44

}
};

int main()
{
InputReader inputReader(STDIN_FILENO);
int n, m;
n = inputReader.readInt();
m = inputReader.readInt();
vector<vector<int>> a(n, vector<int>(m));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
a[i][j] = inputReader.readInt();
}
}
inputReader.close();

long long result = count_rectangles(a);

printf("%lld\n", result);
fclose(stdout);
return 0;
}

Listing 1.3.5: rect-model.cpp


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

#define rep(i, n) for(int i = 0, i##__n = (int)(n); i < i##__n; ++i)


#define fer(i, a, b) for(int i = (int)(a), i##__b = (int)(b); i < i##__b; ++i)
#define rof(i, b, a) for(int i = (int)(b), i##__a = (int)(a); i-- > i##__a; )
#define sz(x) (int((x).size()))
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define X first
#define Y second
//#define endl ’\n’

template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }


template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }

typedef long long ll;


typedef pair<int, int> pii;

const int maxn = 3000 + 10;

int n, m;
vector<vector<int>> H;

short L[maxn][maxn], R[maxn][maxn], U[maxn][maxn], D[maxn][maxn];


short UL[maxn][maxn], DL[maxn][maxn], LU[maxn][maxn], RU[maxn][maxn];

vector<ll> v;

void precalc()
{
short st[maxn];

rep(i, n)
{
int t = 0;
rep(j, m)
{
while(t && H[i][j] > H[i][st[t-1]]) t--;
L[i][j] = (t ? st[t-1] : -1);
st[t++] = j;
}
}
rep(i, n)
{
int t = 0;
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 45

rof(j, m, 0)
{
while(t && H[i][j] > H[i][st[t-1]]) t--;
R[i][j] = (t ? st[t-1] : -1);
st[t++] = j;
}
}
rep(j, m)
{
int t = 0;
rep(i, n)
{
while(t && H[i][j] > H[st[t-1]][j]) t--;
U[i][j] = (t ? st[t-1] : -1);
st[t++] = i;
}
}
rep(j, m)
{
int t = 0;
rof(i, n, 0)
{
while(t && H[i][j] > H[st[t-1]][j]) t--;
D[i][j] = (t ? st[t-1] : -1);
st[t++] = i;
}
}

rep(j, m) rep(i, n)
{
if(U[i][j] != -1)
{
if(j > 0 && U[i][j] == U[i][j-1]) UL[i][j] = UL[i][j-1];
else if(j > 0 && i == D[U[i][j]][j-1]) UL[i][j] = DL[U[i][j]][j-1];
else UL[i][j] = j;
}
if(D[i][j] != -1)
{
if(j > 0 && D[i][j] == D[i][j-1]) DL[i][j] = DL[i][j-1];
else if(j > 0 && i == U[D[i][j]][j-1]) DL[i][j] = UL[D[i][j]][j-1];
else DL[i][j] = j;
}
}

rep(i, n) rep(j, m)
{
if(L[i][j] != -1)
{
if(i > 0 && L[i][j] == L[i-1][j]) LU[i][j] = LU[i-1][j];
else if(i > 0 && j == R[i-1][L[i][j]]) LU[i][j] = RU[i-1][L[i][j]];
else LU[i][j] = i;
}
if(R[i][j] != -1)
{
if(i > 0 && R[i][j] == R[i-1][j]) RU[i][j] = RU[i-1][j];
else if(i > 0 && j == L[i-1][R[i][j]]) RU[i][j] = LU[i-1][R[i][j]];
else RU[i][j] = i;
}
}
}

void check(int i1, int i2, int j1, int j2)


{
if(i2 < i1 || j2 < j1) return;
if(!((R[i2][j1-1]-1 == j2 && RU[i2][j1-1] <= i1) ||
(L[i2][j2+1]+1 == j1 && LU[i2][j2+1] <= i1))) return;
if(!((D[i1-1][j2]-1 == i2 && DL[i1-1][j2] <= j1) ||
(U[i2+1][j2]+1 == i1 && UL[i2+1][j2] <= j1))) return;
v.pb((((ll)i1 * maxn + i2) * maxn + j1) * maxn + j2);
}

int count_rectangles(vector<vector<int>> _H)


{
H = _H;
n = sz(H);
m = sz(H[0]);
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 46

precalc();

fer(i, 1, n-1) fer(j, 1, m-1)


{
if(U[i+1][j] != -1 && L[i][j+1] != -1)
check(U[i+1][j]+1, i, L[i][j+1]+1, j);
if(U[i+1][j] != -1 && R[i][j-1] != -1)
check(U[i+1][j]+1, i, j, R[i][j-1]-1);
if(D[i-1][j] != -1 && L[i][j+1] != -1)
check(i, D[i-1][j]-1, L[i][j+1]+1, j);
if(D[i-1][j] != -1 && R[i][j-1] != -1)
check(i, D[i-1][j]-1, j, R[i][j-1]-1);
if(D[i-1][j] != -1 && R[D[i-1][j]-1][j-1] != -1)
check(i, D[i-1][j]-1, j, R[D[i-1][j]-1][j-1]-1);
if(D[i-1][j] != -1 && L[D[i-1][j]-1][j+1] != -1)
check(i, D[i-1][j]-1, L[D[i-1][j]-1][j+1]+1, j);
}

sort(all(v));
v.resize(unique(all(v)) - v.begin());

return sz(v);
}

Listing 1.3.6: rectangle-mruxim-n2lg+grader.cpp


#include<bits/stdc++.h>
#include<cstdlib>

#include<unistd.h> // close
#include<stdio.h> // STDOUT_FILENO,STDERR_FILENO

using namespace std;

#define rep(i, n) for(int i = 0, i##__n = (int)(n); i < i##__n; ++i)


#define fer(i, a, b) for(int i = (int)(a), i##__b = (int)(b); i < i##__b; ++i)
#define rof(i, b, a) for(int i = (int)(b), i##__a = (int)(a); i-- > i##__a; )
#define sz(x) (int((x).size()))
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define X first
#define Y second

template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }


template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }

typedef long long ll;


typedef pair<int, int> pii;

const int maxn = 3000 + 10;

int n, m;
vector<vector<int>> H;

short L[maxn][maxn], R[maxn][maxn], U[maxn][maxn], D[maxn][maxn];


short UL[maxn][maxn], DL[maxn][maxn], LU[maxn][maxn], RU[maxn][maxn];

vector<ll> v;

void precalc()
{
short st[maxn];

rep(i, n)
{
int t = 0;
rep(j, m)
{
while(t && H[i][j] > H[i][st[t-1]]) t--;
L[i][j] = (t ? st[t-1] : -1);
st[t++] = j;
}
}
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 47

rep(i, n)
{
int t = 0;
rof(j, m, 0)
{
while(t && H[i][j] > H[i][st[t-1]]) t--;
R[i][j] = (t ? st[t-1] : -1);
st[t++] = j;
}
}

rep(j, m)
{
int t = 0;
rep(i, n)
{
while(t && H[i][j] > H[st[t-1]][j]) t--;
U[i][j] = (t ? st[t-1] : -1);
st[t++] = i;
}
}

rep(j, m)
{
int t = 0;
rof(i, n, 0)
{
while(t && H[i][j] > H[st[t-1]][j]) t--;
D[i][j] = (t ? st[t-1] : -1);
st[t++] = i;
}
}

rep(j, m) rep(i, n)
{
if(U[i][j] != -1)
{
if(j > 0 && U[i][j] == U[i][j-1])
UL[i][j] = UL[i][j-1];
else
if(j > 0 && i == D[U[i][j]][j-1])
UL[i][j] = DL[U[i][j]][j-1];
else
UL[i][j] = j;
}

if(D[i][j] != -1)
{
if(j > 0 && D[i][j] == D[i][j-1])
DL[i][j] = DL[i][j-1];
else
if(j > 0 && i == U[D[i][j]][j-1])
DL[i][j] = UL[D[i][j]][j-1];
else
DL[i][j] = j;
}
}

rep(i, n) rep(j, m)
{
if(L[i][j] != -1)
{
if(i > 0 && L[i][j] == L[i-1][j])
LU[i][j] = LU[i-1][j];
else
if(i > 0 && j == R[i-1][L[i][j]])
LU[i][j] = RU[i-1][L[i][j]];
else
LU[i][j] = i;
}

if(R[i][j] != -1)
{
if(i > 0 && R[i][j] == R[i-1][j])
RU[i][j] = RU[i-1][j];
else
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 48

if(i > 0 && j == L[i-1][R[i][j]])


RU[i][j] = LU[i-1][R[i][j]];
else
RU[i][j] = i;
}
}
}

void check(int i1, int i2, int j1, int j2)


{
if(i2 < i1 || j2 < j1) return;

if(!((R[i2][j1-1]-1 == j2 && RU[i2][j1-1] <= i1) ||


(L[i2][j2+1]+1 == j1 && LU[i2][j2+1] <= i1))) return;

if(!((D[i1-1][j2]-1 == i2 && DL[i1-1][j2] <= j1) ||


(U[i2+1][j2]+1 == i1 && UL[i2+1][j2] <= j1))) return;

v.pb((((ll)i1 * maxn + i2) * maxn + j1) * maxn + j2);


}

int count_rectangles(vector<vector<int>> _H)


{
H = _H;
n = sz(H);
m = sz(H[0]);

precalc();

fer(i, 1, n-1) fer(j, 1, m-1)


{
if(U[i+1][j] != -1 && L[i][j+1] != -1)
check(U[i+1][j]+1, i, L[i][j+1]+1, j);

if(U[i+1][j] != -1 && R[i][j-1] != -1)


check(U[i+1][j]+1, i, j, R[i][j-1]-1);

if(D[i-1][j] != -1 && L[i][j+1] != -1)


check(i, D[i-1][j]-1, L[i][j+1]+1, j);

if(D[i-1][j] != -1 && R[i][j-1] != -1)


check(i, D[i-1][j]-1, j, R[i][j-1]-1);

if(D[i-1][j] != -1 && R[D[i-1][j]-1][j-1] != -1)


check(i, D[i-1][j]-1, j, R[D[i-1][j]-1][j-1]-1);

if(D[i-1][j] != -1 && L[D[i-1][j]-1][j+1] != -1)


check(i, D[i-1][j]-1, L[D[i-1][j]-1][j+1]+1, j);
}

sort(all(v));
v.resize(unique(all(v)) - v.begin());

return sz(v);
}

// --------------- start grader ---------------------

class InputReader
{
private:
static const int SIZE = 4096;

int inputFileDescriptor;
char buf[SIZE];
int curChar;
int numChars;

public:

inline InputReader(int _inputFileDescriptor):


inputFileDescriptor(_inputFileDescriptor),
curChar(0),
numChars(0)
{
}
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 49

inline void close()


{
::close(inputFileDescriptor);
}

inline char read()


{
assert(numChars != -1);
if (curChar >= numChars)
{
curChar = 0;
numChars = ::read(inputFileDescriptor, buf, SIZE);

if (numChars == -1)
return -1;
}

return buf[curChar++];
}

inline int readInt()


{
int c = eatWhite();
int sgn = 1;
if (c == ’-’)
{
sgn = -1;
c = read();
}

int res = 0;
do
{
assert(c >= ’0’ && c <= ’9’);
res *= 10;
res += c - ’0’;
c = read();
} while (!isSpaceChar(c));

return res * sgn;


}

inline string readString()


{
char c = eatWhite();
string res;
do
{
res += c;
c = read();
} while (!isSpaceChar(c));

return res;
}

inline string readLine()


{
string res;
while (true)
{
char c = read();
if (c == ’\n’ || c == ’\r’ || c == -1)
break;
res += c;
}

return res;
}

inline char eatWhite()


{
char c = read();
while (isSpaceChar(c))
c = read();
return c;
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 50

static inline bool isSpaceChar(char c)


{
return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
}
};

int main()
{
auto t1 = clock();

//std::freopen("../tests/7-18.in", "r", stdin) ; // execution time : 24.047 s


//std::freopen("../tests/7-15.in", "r", stdin) ; // execution time : 13.788 s
//std::freopen("../tests/7-01.in", "r", stdin) ; // execution time : 4.881 s
std::freopen("../tests/6-07.in", "r", stdin) ; // execution time : 3.562 s s

std::freopen("rectangle.out", "w", stdout) ;

InputReader inputReader(STDIN_FILENO);

int n, m;
n = inputReader.readInt();
m = inputReader.readInt();
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<vector<int>> a(n, vector<int>(m));


for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
a[i][j] = inputReader.readInt();
}
}

inputReader.close();

auto t2 = clock();

long long result = count_rectangles(a);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// --------------- end grader ---------------------

Listing 1.3.7: rectangle 147888+grader.cpp


// https://oj.uz/submission/147888

#include <bits/stdc++.h>
#include "../rect.h"

#include<unistd.h> // close
#include<stdio.h> // STDOUT_FILENO,STDERR_FILENO

#define fi first
#define se second
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 51

using namespace std;

const int MX = 2505;

vector<int> row[MX], col[MX];


vector<pair<int, int>> pair_row, pair_col;
int bit[MX];
int lst_row[MX][MX], cnt_row[MX][MX];
int lst_col[MX][MX], cnt_col[MX][MX];

void update(int u, int v)


{
for (; u < MX; u += u & -u)
bit[u] += v;
}

int query(int u)
{
int ret = 0;
for (; u > 0; u -= u & -u)
ret += bit[u];
return ret;
}

pair<int, int> process(int l, int r, int cur,


int lst[MX][MX], int cnt[MX][MX])
{
if (lst[l][r] != cur - 1)
cnt[l][r] = 0;
cnt[l][r]++; lst[l][r] = cur;
return {r - l - 1, cnt[l][r]};
}

long long find_ans(vector<pair<int, int>> &row,


vector<pair<int, int>> &col)
{
long long ans = 0;
for (pair<int, int> &v : col)
swap(v.fi, v.se);

sort(row.begin(), row.end(), greater<pair<int, int>>());


sort(col.begin(), col.end(), greater<pair<int, int>>());

int pt = 0;
for (pair<int, int> &v : row)
{
for (; pt < col.size() && v.fi <= col[pt].fi; pt++)
update(col[pt].se, 1);
ans += query(v.se);
}

for (int i = 0; i < pt; i++)


update(col[i].se, -1);

return ans;
}

long long count_rectangles(vector<vector<int>> a)


{
bool eq;
int m = a.size(), n = a[0].size();
long long ans = 0;
for (int i = 0; i < m; i++)
row[i] = {0};
for (int i = 0; i < n; i++)
col[i] = {0};
for (int i = 0; i < m - 1; i++)
for (int j = 0; j < n - 1; j++)
{
eq = false; pair_row.clear();
while (!row[i].empty())
{
int u = row[i].back();
if (u < j && !eq)
pair_row.push_back(process(u, j + 1, i,
lst_row, cnt_row));
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 52

eq = (a[i][u] == a[i][j + 1]);


if (a[i][u] <= a[i][j + 1])
row[i].pop_back();
else
break;
}

row[i].push_back(j + 1);
eq = false; pair_col.clear();
while (!col[j].empty())
{
int u = col[j].back();
if (u < i && !eq)
pair_col.push_back(process(u, i + 1, j,
lst_col, cnt_col));
eq = (a[u][j] == a[i + 1][j]);
if (a[u][j] <= a[i + 1][j])
col[j].pop_back();
else
break;
}

col[j].push_back(i + 1);
ans += find_ans(pair_row, pair_col);
}

return ans;
}

// --------------- start grader ---------------------


class InputReader
{
private:
static const int SIZE = 4096;

int inputFileDescriptor;
char buf[SIZE];
int curChar;
int numChars;

public:

inline InputReader(int _inputFileDescriptor):


inputFileDescriptor(_inputFileDescriptor),
curChar(0),
numChars(0)
{
}

inline void close()


{
::close(inputFileDescriptor);
}

inline char read()


{
assert(numChars != -1);
if (curChar >= numChars)
{
curChar = 0;
numChars = ::read(inputFileDescriptor, buf, SIZE);

if (numChars == -1)
return -1;
}

return buf[curChar++];
}

inline int readInt()


{
int c = eatWhite();
int sgn = 1;
if (c == ’-’)
{
sgn = -1;
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 53

c = read();
}

int res = 0;
do
{
assert(c >= ’0’ && c <= ’9’);
res *= 10;
res += c - ’0’;
c = read();
} while (!isSpaceChar(c));

return res * sgn;


}

inline string readString()


{
char c = eatWhite();
string res;
do
{
res += c;
c = read();
} while (!isSpaceChar(c));

return res;
}

inline string readLine()


{
string res;
while (true)
{
char c = read();
if (c == ’\n’ || c == ’\r’ || c == -1)
break;
res += c;
}

return res;
}

inline char eatWhite()


{
char c = read();
while (isSpaceChar(c))
c = read();
return c;
}

static inline bool isSpaceChar(char c)


{
return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
}
};

int main()
{
auto t1 = clock();

std::freopen("../tests/7-18.in", "r", stdin) ; // exec time : 17.859 s


//std::freopen("../tests/7-15.in", "r", stdin) ; // exec time : 10.719 s
//std::freopen("../tests/7-01.in", "r", stdin) ; // exec time : 12.719 s
//std::freopen("../tests/6-07.in", "r", stdin) ; // exec time : 5.438 s

std::freopen("rectangle.out", "w", stdout) ;

InputReader inputReader(STDIN_FILENO);

int n, m;
n = inputReader.readInt();
m = inputReader.readInt();
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<vector<int>> a(n, vector<int>(m));


CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 54

for (int i = 0; i < n; i++)


{
for (int j = 0; j < m; j++)
{
a[i][j] = inputReader.readInt();
}
}

inputReader.close();

auto t2 = clock();

long long result = count_rectangles(a);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// --------------- end grader ---------------------

Listing 1.3.8: rectangle145193+grader.cpp


// https://oj.uz/submission/145193

#include "../rect.h"

#include<bits/stdc++.h>

#include<unistd.h> // close
#include<stdio.h> // STDOUT_FILENO,STDERR_FILENO

using namespace std;

using ll = long long;

ll count_rectangles(vector<vector<int>> G)
{
int N = int(G.size()) - 1;
int M = int(G[0].size()) - 1;

if (N <= 1 || M <= 1) return 0;

vector<stack<int>> stacks(M);
for (int j = 1; j < M; j++)
{
if (G[0][j] > G[1][j])
{
stacks[j].push(0);
}
stacks[j].push(1);
}

vector<vector<pair<int, int>>>
history(M,
vector<pair<int, int>>(M, pair<int, int>(-1, -1)));

ll ans = 0;

for (int i = 1; i < N; i++)


{
stack<pair<int, vector<int>>> s;
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 55

s.emplace(0, vector<int>());
for (int j = 1; j <= M; j++)
{
vector<int> curSet;
bool initialized = false;

auto merge = [&](const vector<int>& v)


{
if (!initialized)
{
curSet = v;
initialized = true;
return;
}

vector<int> nCurSet;
set_intersection(curSet.begin(), curSet.end(),
v.begin(), v.end(), back_inserter(nCurSet));
curSet = std::move(nCurSet);
};

while (!s.empty() && G[i][j] >= G[i][s.top().first])


{
merge(s.top().second);
if (G[i][j] > G[i][s.top().first])
{
s.pop();
if (!s.empty())
{
int l = s.top().first + 1;
int r = j-1;
if (history[l][r].second != i-1)
{
history[l][r].first = i;
}
history[l][r].second = i;

assert(initialized);
int numGood = int(curSet.end() -
lower_bound(curSet.begin(),
curSet.end(),
history[l][r].first));
//cerr << numGood << ’\n’;
ans += numGood;
}
}
else
{
s.pop();
}
}

if (j == M) break;

vector<int> heights;

{ // build heights
while (!stacks[j].empty() &&
G[i+1][j] >= G[stacks[j].top()][j])
{
if (G[i+1][j] > G[stacks[j].top()][j])
{
stacks[j].pop();
if (!stacks[j].empty())
{
heights.push_back(stacks[j].top()+1);
}
}
else
{
stacks[j].pop();
}
}

stacks[j].push(i+1);
}
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 56

reverse(heights.begin(), heights.end());
merge(heights);
s.emplace(j, std::move(curSet));
}
}

return ans;
}

// --------------- start grader ---------------------


class InputReader
{
private:
static const int SIZE = 4096;

int inputFileDescriptor;
char buf[SIZE];
int curChar;
int numChars;

public:

inline InputReader(int _inputFileDescriptor):


inputFileDescriptor(_inputFileDescriptor),
curChar(0),
numChars(0) {
}

inline void close()


{
::close(inputFileDescriptor);
}

inline char read() {


assert(numChars != -1);
if (curChar >= numChars) {
curChar = 0;
numChars = ::read(inputFileDescriptor, buf, SIZE);

if (numChars == -1)
return -1;
}
return buf[curChar++];
}

inline int readInt() {


int c = eatWhite();
int sgn = 1;
if (c == ’-’) {
sgn = -1;
c = read();
}
int res = 0;
do {
assert(c >= ’0’ && c <= ’9’);
res *= 10;
res += c - ’0’;
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}

inline string readString() {


char c = eatWhite();
string res;
do {
res += c;
c = read();
} while (!isSpaceChar(c));
return res;
}

inline string readLine() {


string res;
while (true) {
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 57

char c = read();
if (c == ’\n’ || c == ’\r’ || c == -1)
break;
res += c;
}
return res;
}

inline char eatWhite() {


char c = read();
while (isSpaceChar(c))
c = read();
return c;
}

static inline bool isSpaceChar(char c) {


return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
}
};

int main()
{
auto t1 = clock();

std::freopen("../tests/7-18.in", "r", stdin) ; // execution time : 17.029 s


//std::freopen("../tests/7-15.in", "r", stdin) ; // execution time : 10.061 s
//std::freopen("../tests/7-01.in", "r", stdin) ; // execution time : 14.344 s
//std::freopen("../tests/6-07.in", "r", stdin) ; // execution time : 10.749 s

std::freopen("rectangle.out", "w", stdout) ;

InputReader inputReader(STDIN_FILENO);

int n, m;
n = inputReader.readInt();
m = inputReader.readInt();
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<vector<int>> a(n, vector<int>(m));


for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
a[i][j] = inputReader.readInt();
}
}

inputReader.close();

auto t2 = clock();

long long result = count_rectangles(a);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// --------------- end grader ---------------------

Listing 1.3.9: rectangle-peyman-n2lg-opt+grader.cpp


CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 58

//Peyman Jabbarzade Ganje


//Boarder shouldn’t be equal with inside
#include<bits/stdc++.h>

#include<unistd.h> // close
#include<stdio.h> // STDOUT_FILENO,STDERR_FILENO

#define X first
#define Y second

using namespace std;


typedef pair<int, int> pii;

const int maxn = 3000 + 10, maxx=1e7;


int n,m, fen_t[maxx];
long long ans;

vector<int>vec, nozoli, will_remove;


map<int, int> fp[maxn][maxn];
vector<pii> sp[2][maxn][maxn];

void fen_add(int x,int val)


{
x++;
while(x>0)
{
fen_t[x]+=val;
x -= x&(-x);
}
}

int fen_f(int x)
{
x++;
int ret = 0;
while(x<maxx)
{
ret += fen_t[x];
x += x&(-x);
}

return ret;
}

void add_pair(int type, int lvl, int l, int r)


{
int tmp = 1;
if(fp[lvl][l].find(r) != fp[lvl][l].end())
tmp += fp[lvl][l][r];

if(type == 0)
{
if(r-l-1>0)
sp[type][lvl+1][r].push_back(pii(r-l-1, tmp));
}
else
{
if(r-l-1>0)
sp[type][r][lvl+1].push_back(pii(tmp, r-l-1));
}

fp[lvl+1][l][r] = tmp;
}

void extract_pairs(int type, int lvl)


{
nozoli.clear();
for(int i=0;i<vec.size();i++)
{
int last=-1;
while(nozoli.size() && vec[nozoli.back()] < vec[i])
{
if(vec[nozoli.back()] > last)
add_pair(type, lvl, nozoli.back(), i);
last = vec[nozoli.back()];
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 59

nozoli.pop_back();
}

if(nozoli.size())
add_pair(type, lvl, nozoli.back(), i);
nozoli.push_back(i);
}
}

long long count_rectangles(vector<vector<int> >a)


{
n = a.size();
m = a[0].size();

for(int i=0;i<n;i++)
{
vec.clear();
for(int j=0;j<m;j++)
vec.push_back(a[i][j]);
extract_pairs(0,i);
}

for(int i=0;i<=max(n,m);i++)
for(int j=0;j<=max(n,m);j++)
fp[i][j].clear();

for(int j=0;j<m;j++)
{
vec.clear();
for(int i=0;i<n;i++)
vec.push_back(a[i][j]);
extract_pairs(1,j);
}

for(int i=0;i<=max(n,m);i++)
for(int j=0;j<=max(n,m);j++)
if(sp[0][i][j].size() && sp[1][i][j].size())
{
sort(sp[0][i][j].begin(), sp[0][i][j].end());
sort(sp[1][i][j].begin(), sp[1][i][j].end());
// sp[0].X fix sp[1].X moteghaier
int p = 0;
will_remove.clear();
for(int k=0;k<sp[1][i][j].size();k++)
{
while(p<sp[0][i][j].size() &&
sp[0][i][j][p].X <= sp[1][i][j][k].X)
{
fen_add(sp[0][i][j][p].Y, 1);
will_remove.push_back(sp[0][i][j][p].Y);
p++;
}

ans += fen_f(sp[1][i][j][k].Y);
}

for(int k=0;k<will_remove.size();k++)
fen_add(will_remove[k], -1);
}

return ans;
}

// --------------- start grader ---------------------


class InputReader
{
private:
static const int SIZE = 4096;

int inputFileDescriptor;
char buf[SIZE];
int curChar;
int numChars;

public:
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 60

inline InputReader(int _inputFileDescriptor):


inputFileDescriptor(_inputFileDescriptor),
curChar(0),
numChars(0)
{
}

inline void close()


{
::close(inputFileDescriptor);
}

inline char read()


{
assert(numChars != -1);
if (curChar >= numChars)
{
curChar = 0;
numChars = ::read(inputFileDescriptor, buf, SIZE);

if (numChars == -1)
return -1;
}

return buf[curChar++];
}

inline int readInt()


{
int c = eatWhite();
int sgn = 1;
if (c == ’-’)
{
sgn = -1;
c = read();
}

int res = 0;
do
{
assert(c >= ’0’ && c <= ’9’);
res *= 10;
res += c - ’0’;
c = read();
} while (!isSpaceChar(c));

return res * sgn;


}

inline string readString()


{
char c = eatWhite();
string res;
do
{
res += c;
c = read();
} while (!isSpaceChar(c));

return res;
}

inline string readLine()


{
string res;
while (true)
{
char c = read();
if (c == ’\n’ || c == ’\r’ || c == -1)
break;
res += c;
}

return res;
}
CAPITOLUL 1. IOI 2019 1.3. RECTANGLES 61

inline char eatWhite()


{
char c = read();
while (isSpaceChar(c))
c = read();
return c;
}

static inline bool isSpaceChar(char c)


{
return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
}
};

int main()
{
auto t1 = clock();

std::freopen("../tests/7-18.in", "r", stdin) ; // execution time : 43.187 s


// std::freopen("../tests/7-15.in", "r", stdin) ; // execution time : 26.486 s
//std::freopen("../tests/7-01.in", "r", stdin) ; // execution time : 34.197 s
//std::freopen("../tests/6-07.in", "r", stdin) ; // execution time : 18.419 s

std::freopen("rectangle.out", "w", stdout) ;

InputReader inputReader(STDIN_FILENO);

int n, m;
n = inputReader.readInt();
m = inputReader.readInt();
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<vector<int>> a(n, vector<int>(m));


for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
a[i][j] = inputReader.readInt();
}
}

inputReader.close();

auto t2 = clock();

long long result = count_rectangles(a);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// --------------- end grader ---------------------
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 62

1.3.3 *Rezolvare detaliată

1.4 Broken Line


Problema 4 - Broken Line 100 de puncte

Authors: Tomasz Idziaszek, Jakub Lacki

Azerbaidjan este faimos pentru covoarele sale. Ca un maestru ı̂n designer de covoare, doriţi
să elaboraţi un nou design prin desenarea unei linii frânte. O linie frântă este o secvenţă de t
segmente intr-un plan bidimensional, definite printr-o secvenţă de t  1 puncte p0 , ..., pt după cum
urmează:
Pentru fiecare 0 & j & t  1 există un segment ce conectează punctele pj şi pj 1 .
Pentru a elabora noul design, aţi marcat deja n puncte intr-un plan bidimensional. Coordo-
natele punctului i (1 & i & n) sunt xi, y i. Nu există două puncte care să aibă aceeaşi
coordonată x sau y.
Doriţi să găsiţi o secvenţă de puncte sx0, sy 0, sx1, sy 1, ..., sxk , sy k , ce de-
fineşte o linie frântă care:
a ı̂ncepe la 0, 0 (adică sx0 0 şi sy 0 0),
a conţine toate punctele marcate (nu neapărat ca extremităţi ale segmentelor)
a constă exclusiv din segmente orizontale şi verticale (două puncte consecutive care definesc
linia frântă au aceeaşi coordonată x sau y)
Se permite ca linia frântă să se intersecteze sau să se suprapună ı̂n orice fel. Formal, fiecare
punct din plan poate aparţine oricărui număr de segmente din linia frântă.
Această problemă este de tip output-only, cu scor parţial. Veţi avea 10 fişiere de intrare ı̂n care
vor fi specificate locaţiile punctelor marcate. Pentru fiecare fişier de intrare, trebuie să ı̂ncărcaţi
un fişier de ieşire care descrie o linie frântă cu proprietăţile cerute.
Pentru fiecare fişier de ieşire care descrie o linie frântă validă, scorul vostru va depinde de
numărul de segmente din linia frântă (a se vedea Punctarea de mai jos).
Nu veţi ı̂ncărca vreun cod sursă pentru această problemă.

Format de intrare

Fiecare fişier de intrare va avea următorul format:


a linia 1: n
a linia 1  i (pentru 1 & i & n): xi y i

Format de ieşire

Fiecare fişier de ieşire trebuie să aibe următorul format:


alinia 1: k
alinia 1  j (pentru 1 & j & k): sxj  sy j 
De notat că a doua linie trebuie să conţină sx1 şi sy 1 (ieşirea NU trebuie să conţină
sx0 şi sy 0 ). Fiecare sxj  şi sy j  trebuie să fie un ı̂ntreg.

Exemple

Pentru intrarea:

4
2 1
3 3
4 4
5 2
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 63

o posibilă ieşire este:

6
2 0
2 3
5 3
5 2
4 2
4 4

Figura 1.5: Line

Acest exemplu nu face parte din intrările pentru această problemă.

Restricţii

a1 & n & 100000


1 & xi, y i & 10
9
a
a Toate valorile xi şi y i sunt ı̂ntregi.
a Nu există două puncte cu aceeaşi coordonată x sau y, adica xi1  j xi2  şi y i1  j y i2 
pentru i1 j i2 .
a 2 10 & sxj , sy j  & 2 10
9 9

a Mărimea fiecărui fişier de ieşire (fişier de intrare sau arhivă zip) nu trebuie să depăşească
15M B.

Punctare

Pentru fiecare test, puteţi obţine maxim 10 puncte. Output-ul unui test va fi punctat cu 0
puncte dacă nu conţine o linie frântă cu proprietăţile cerute. Altfel, punctajul va fi determinat
utilizând o secvenţă descrescătoare c1 , ..., c10 care diferă de la test la test.
...
Timp maxim de executare/test: output-only
Memorie: output-only

1.4.1 Indicaţii de rezolvare

In this problem, we are given n dots in a 2D-plane with distinct x coordinates and y coordinates.
Our task is to construct a broken line starting from the origin that will cover all dots.
2n solution
An easy solution is to use 2 segments to cover each dot (for example: first set the x coordinate,
then y coordinate). This solution uses 2n segments and receives 12 points.
Spiral
Let us consider the left-most, top-most, right-most and bottom-most dot. They create a
bounding box which we can cover with 4 segments. We can remove these points and continue
with a smaller problem. To connect the bounding boxes, we can just continue the line from the
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 64

inner bounding box and go toward the proper direction in the outer box. This solution receives
about 50 points, depending on the implementation.
Chain
Let us notice that we can cover a chain of points (for example with increasing x and y values)
without wasting any segment. From the Dilworth’s theorem, we know thatÓ in the sequence of
length k, there exists an increasing sequence or decreasing sequence of length k. Ó We can find this
sequence in O k log k . Moreover, we can decompose the whole sequence Ó
into k log k increasing
and decreasing
Ó sequences. Therefore in our problem, we can create n log n chains and connect
them with k log k additional segments. This solution uses n  2 log n segments and receives about
60 points.
n  6 solution
Let us reconsider the spiral. We waste additional segments only if the bounding box contains
less than 4 points (for example there’s a point that is both the left-most and the top-most one).
We can remove these points and put them in one of two chains (one going from the top-left
corner to the bottom-right corner) and one going from the top-right corner to the bottom-left
one).
The algorithm is as follows:
ˆ if a bounding box has 4 points in it, add them to the spiral, and remove these points,
ˆ otherwise, find a point that lies on two sides of the bounding box, add it to the proper chain
and remove it.

Then we have three objects (spiral and two chains) and we can use up to 2 segments to connect
them to themselves and the origin. This solution gets about 95 points.
n  3 solution
To come up with an even better solution, we need to add some small tricks, including considering
all possible orders in which we can connect these objects and directions in which we can traverse
them. These optimizations lead a solution with n  3 segments, which gets 100 points.

1.4.2 Coduri sursă

Listing 1.4.1: compile cpp.sh


#!/bin/bash

problem=line

g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"

"grader.cpp" "${problem}.cpp"

Listing 1.4.2: line.h


// ***

Listing 1.4.3: line.cpp


// ***

Listing 1.4.4: grader.cpp


// ***

Listing 1.4.5: line-model.cpp


%\begin{lstlisting}[language=C++,numbers=none,commentstyle=\color{purple}, caption={\
hspace{2mm}line-model.cpp}]
#include <cstdio>
#include <cstdlib>
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 65

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <cassert>
#include <ctime>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>

using namespace std;

#define pb push_back
#define mp make_pair
#define fs first
#define sc second

int n;
vector <pair <int, int> > input;
vector <pair <int, int> > answer;
bool last_volatile = false;

void continue_answer(pair <int, int> point)


{
if (answer.empty())
{
answer.pb(point);

return;
}

if (answer.size() >= 2u)


{
bool worked = false;
int last = (int) answer.size() - 1;
int prelast = (int) answer.size() - 2;
if (answer[prelast].fs == answer[last].fs)
{
if ((answer[last].sc - answer[prelast].sc) * 1ll *
(point.sc - answer[last].sc) > 0)
{
answer[last].sc = point.sc;
worked = true;
}
}
else
{
if ((answer[last].fs - answer[prelast].fs) * 1ll *
(point.fs - answer[last].fs) > 0)
{
answer[last].fs = point.fs;
worked = true;
}
}

if (!worked && last_volatile)


{
int preprelast = (int) answer.size() - 3;
answer[prelast].fs = answer[preprelast].fs;
answer[prelast].sc = answer[last].sc;
if (answer[prelast].fs == answer[last].fs)
{
if ((answer[last].sc - answer[prelast].sc) * 1ll *
(point.sc - answer[last].sc) > 0)
{
answer[last].sc = point.sc;
}
}
else
{
if ((answer[last].fs - answer[prelast].fs) * 1ll *
(point.fs - answer[last].fs) > 0)
{
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 66

answer[last].fs = point.fs;
}
}
}
}

last_volatile = (point.fs != answer.back().fs && point.sc !=


answer.back().sc);

if (point.fs != answer.back().fs)
{
answer.pb(mp(point.fs, answer.back().sc));
}
if (point.sc != answer.back().sc)
{
answer.pb(point);
}
}

void add_spiral(vector <pair <pair <int, int>, pair <int, int> > > spiral)
{
for (int i = 0; i < (int) spiral.size(); i++)
{
int lft = spiral[i].fs.fs;
int up = spiral[i].fs.sc;
int rgt = spiral[i].sc.fs;
int down = spiral[i].sc.sc;

pair <int, int> ldc = mp(input[lft].fs, input[down].sc);


pair <int, int> luc = mp(input[lft].fs, input[up].sc);
pair <int, int> rdc = mp(input[rgt].fs, input[down].sc);
pair <int, int> ruc = mp(input[rgt].fs, input[up].sc);

if (i == 0)
{
continue_answer(ldc);
continue_answer(luc);
continue_answer(ruc);
continue_answer(rdc);
continue_answer(ldc);
}
else
{
answer.back().fs = input[lft].fs;
if (input[lft].sc >= answer.back().sc)
{
continue_answer(luc);
continue_answer(ruc);
continue_answer(rdc);
continue_answer(ldc);
}
else
{
continue_answer(ldc);
continue_answer(rdc);
continue_answer(ruc);
continue_answer(luc);
}
}
}
}

void add_chain(vector <pair <int, int> > chain, bool order,


bool flip, bool interleave)
{
if (chain.empty())
return;
int len = (int) chain.size();
if (flip)
{
for (int i = 0; i < len; i++)
{
chain[i].fs *= -1;
}
}
if (order)
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 67

{
for (int i = 0; i < len; i++)
{
chain[i].sc *= -1;
}
}
sort(chain.begin(), chain.end());
if (flip)
{
for (int i = 0; i < len; i++)
{
chain[i].fs *= -1;
}
}
if (order)
{
for (int i = 0; i < len; i++)
{
chain[i].sc *= -1;
}
}

continue_answer(chain[0]);
for (int i = 1; i < len; i++)
{
if (i % 2 xor interleave)
{
continue_answer(mp(chain[i].fs, chain[i - 1].sc));
}
else
{
continue_answer(mp(chain[i - 1].fs, chain[i].sc));
}
}
if (len > 1)
{
continue_answer(chain.back());
}
}

void finalize()
{
cerr << n << ’ ’ << answer.size() - 1 << ’ ’ << answer.size() - 1 - n << ’\n’;
cout << answer.size()-1 << ’\n’;
for (int i = 1; i < (int) answer.size(); i++)
{
cout << answer[i].fs << ’ ’ << answer[i].sc << ’\n’;
}
}

int main ()
{
cin >> n;

for (int i = 0; i < n; i++)


{
int x, y;
cin >> x >> y;
input.pb(mp(x, y));
}

vector <pair <int, int> > chain1, chain2;


vector <pair <pair <int, int>, pair <int, int> > > spiral;
set <pair <int, int> > xs;
set <pair <int, int> > ys;
vector <bool> used(n, false);

for (int i = 0; i < n; i++)


{
xs.insert(mp(input[i].fs, i));
ys.insert(mp(input[i].sc, i));
}

while (true)
{
int lft = -1;
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 68

while (!xs.empty() && used[xs.begin()->sc])


{
xs.erase(xs.begin());
}
if (!xs.empty())
{
lft = xs.begin()->sc;
}

int rgt = -1;


while (!xs.empty() && used[xs.rbegin()->sc])
{
xs.erase( *xs.rbegin());
}
if (!xs.empty())
{
rgt = xs.rbegin()->sc;
}

int down = -1;


while (!ys.empty() && used[ys.begin()->sc])
{
ys.erase(ys.begin());
}
if (!ys.empty())
{
down = ys.begin()->sc;
}

int up = -1;
while (!ys.empty() && used[ys.rbegin()->sc])
{
ys.erase( *ys.rbegin());
}
if (!ys.empty())
{
up = ys.rbegin()->sc;
}

if (lft == -1 || rgt == -1 || down == -1 || up == -1)


{
break;
}

if (lft == down)
{
chain1.pb(input[lft]);
used[lft] = true;
continue;
}
if (lft == up)
{
chain2.pb(input[lft]);
used[lft] = true;
continue;
}
if (rgt == down)
{
chain2.pb(input[rgt]);
used[rgt] = true;
continue;
}
if (rgt == up)
{
chain1.pb(input[rgt]);
used[rgt] = true;
continue;
}
spiral.pb(mp(mp(lft, up), mp(rgt, down)));
used[lft] = true;
used[rgt] = true;
used[down] = true;
used[up] = true;
}

reverse(spiral.begin(), spiral.end());
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 69

vector <pair <int, int> > best_answer;


for (int flip1 = 0; flip1 < 8; flip1++)
{
for (int flip2 = 0; flip2 < 8; flip2++)
{
vector <int> parts = {0, 1, 2};
do
{
answer.clear();
last_volatile = false;
continue_answer(mp(0, 0));
for (int i = 0; i < (int) parts.size(); i++)
{
if (parts[i] == 0)
{
add_spiral(spiral);
}
else
if (parts[i] == 1)
{
add_chain(chain1, flip1 >> 2, (flip1 >> 1) & 1, flip1 & 1);
}
else
{
add_chain(chain2, flip2 >> 2, (flip2 >> 1) & 1, flip2 & 1);
}
}
if (best_answer.empty() || answer.size() < best_answer.size())
{
best_answer = answer;
}
} while (next_permutation(parts.begin(), parts.end()));
}
}

answer = best_answer;

finalize();

return 0;
}

Listing 1.4.6: sol-ge-most-optimal+grader.cpp


#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <cassert>
#include <ctime>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>

using namespace std;

#define pb push_back
#define mp make_pair
#define fs first
#define sc second

int n;
vector <pair <int, int> > input;
vector <pair <int, int> > answer;
bool last_volatile = false;

void continue_answer(pair <int, int> point)


{
if (answer.empty())
{
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 70

answer.pb(point);

return;
}

if (answer.size() >= 2u)


{
bool worked = false;
int last = (int) answer.size() - 1;
int prelast = (int) answer.size() - 2;
if (answer[prelast].fs == answer[last].fs)
{
if ((answer[last].sc - answer[prelast].sc) * 1ll *
(point.sc - answer[last].sc) > 0)
{
answer[last].sc = point.sc;
worked = true;
}
}
else
{
if ((answer[last].fs - answer[prelast].fs) * 1ll *
(point.fs - answer[last].fs) > 0)
{
answer[last].fs = point.fs;
worked = true;
}
}

if (!worked && last_volatile)


{
int preprelast = (int) answer.size() - 3;
answer[prelast].fs = answer[preprelast].fs;
answer[prelast].sc = answer[last].sc;
if (answer[prelast].fs == answer[last].fs)
{
if ((answer[last].sc - answer[prelast].sc) * 1ll *
(point.sc - answer[last].sc) > 0)
{
answer[last].sc = point.sc;
}
}
else
{
if ((answer[last].fs - answer[prelast].fs) * 1ll *
(point.fs - answer[last].fs) > 0)
{
answer[last].fs = point.fs;
}
}
}
}

last_volatile = (point.fs != answer.back().fs && point.sc !=


answer.back().sc);

if (point.fs != answer.back().fs)
{
answer.pb(mp(point.fs, answer.back().sc));
}
if (point.sc != answer.back().sc)
{
answer.pb(point);
}
}

void add_spiral(vector <pair <pair <int, int>, pair <int, int> > > spiral)
{
for (int i = 0; i < (int) spiral.size(); i++)
{
int lft = spiral[i].fs.fs;
int up = spiral[i].fs.sc;
int rgt = spiral[i].sc.fs;
int down = spiral[i].sc.sc;

pair <int, int> ldc = mp(input[lft].fs, input[down].sc);


CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 71

pair <int, int> luc = mp(input[lft].fs, input[up].sc);


pair <int, int> rdc = mp(input[rgt].fs, input[down].sc);
pair <int, int> ruc = mp(input[rgt].fs, input[up].sc);

if (i == 0)
{
continue_answer(ldc);
continue_answer(luc);
continue_answer(ruc);
continue_answer(rdc);
continue_answer(ldc);
}
else
{
answer.back().fs = input[lft].fs;
if (input[lft].sc >= answer.back().sc)
{
continue_answer(luc);
continue_answer(ruc);
continue_answer(rdc);
continue_answer(ldc);
}
else
{
continue_answer(ldc);
continue_answer(rdc);
continue_answer(ruc);
continue_answer(luc);
}
}
}
}

void add_chain(vector <pair <int, int> > chain, bool order,


bool flip, bool interleave)
{
if (chain.empty())
return;
int len = (int) chain.size();
if (flip)
{
for (int i = 0; i < len; i++)
{
chain[i].fs *= -1;
}
}
if (order)
{
for (int i = 0; i < len; i++)
{
chain[i].sc *= -1;
}
}
sort(chain.begin(), chain.end());
if (flip)
{
for (int i = 0; i < len; i++)
{
chain[i].fs *= -1;
}
}
if (order)
{
for (int i = 0; i < len; i++)
{
chain[i].sc *= -1;
}
}

continue_answer(chain[0]);
for (int i = 1; i < len; i++)
{
if (i % 2 xor interleave)
{
continue_answer(mp(chain[i].fs, chain[i - 1].sc));
}
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 72

else
{
continue_answer(mp(chain[i - 1].fs, chain[i].sc));
}
}
if (len > 1)
{
continue_answer(chain.back());
}
}

void finalize()
{
cerr << "cerr : "<<n << ’ ’ << answer.size() - 1
<< ’ ’ << answer.size() - 1 - n << ’\n’;
cout << answer.size()-1 << ’\n’;
for (int i = 1; i < (int) answer.size(); i++)
{
cout << answer[i].fs << ’ ’ << answer[i].sc << ’\n’;
}
}

int main ()
{
auto t1 = clock();

std::freopen("../tests/10.in", "r", stdin) ; // execution time : 15.805 s


//std::freopen("../tests/04.in", "r", stdin) ; // execution time : 7.761 s
//std::freopen("../tests/03.in", "r", stdin) ; // execution time : 0.875 s
//std::freopen("../tests/00.in", "r", stdin) ; // execution time : 0.031 s

std::freopen("line.out", "w", stdout) ;

cin >> n;

cerr<<"cerr : "<<n<<"\n";

for (int i = 0; i < n; i++)


{
int x, y;
cin >> x >> y;
input.pb(mp(x, y));
}

auto t2 = clock();

vector <pair <int, int> > chain1, chain2;


vector <pair <pair <int, int>, pair <int, int> > > spiral;
set <pair <int, int> > xs;
set <pair <int, int> > ys;
vector <bool> used(n, false);

for (int i = 0; i < n; i++)


{
xs.insert(mp(input[i].fs, i));
ys.insert(mp(input[i].sc, i));
}

while (true)
{
int lft = -1;
while (!xs.empty() && used[xs.begin()->sc])
{
xs.erase(xs.begin());
}
if (!xs.empty())
{
lft = xs.begin()->sc;
}

int rgt = -1;


while (!xs.empty() && used[xs.rbegin()->sc])
{
xs.erase( *xs.rbegin());
}
CAPITOLUL 1. IOI 2019 1.4. BROKEN LINE 73

if (!xs.empty())
{
rgt = xs.rbegin()->sc;
}

int down = -1;


while (!ys.empty() && used[ys.begin()->sc])
{
ys.erase(ys.begin());
}
if (!ys.empty())
{
down = ys.begin()->sc;
}

int up = -1;
while (!ys.empty() && used[ys.rbegin()->sc])
{
ys.erase( *ys.rbegin());
}
if (!ys.empty())
{
up = ys.rbegin()->sc;
}

if (lft == -1 || rgt == -1 || down == -1 || up == -1)


{
break;
}

if (lft == down)
{
chain1.pb(input[lft]);
used[lft] = true;
continue;
}
if (lft == up)
{
chain2.pb(input[lft]);
used[lft] = true;
continue;
}
if (rgt == down)
{
chain2.pb(input[rgt]);
used[rgt] = true;
continue;
}
if (rgt == up)
{
chain1.pb(input[rgt]);
used[rgt] = true;
continue;
}
spiral.pb(mp(mp(lft, up), mp(rgt, down)));
used[lft] = true;
used[rgt] = true;
used[down] = true;
used[up] = true;
}

reverse(spiral.begin(), spiral.end());
vector <pair <int, int> > best_answer;
for (int flip1 = 0; flip1 < 8; flip1++)
{
for (int flip2 = 0; flip2 < 8; flip2++)
{
vector <int> parts = {0, 1, 2};
do
{
answer.clear();
last_volatile = false;
continue_answer(mp(0, 0));
for (int i = 0; i < (int) parts.size(); i++)
{
if (parts[i] == 0)
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 74

{
add_spiral(spiral);
}
else
if (parts[i] == 1)
{
add_chain(chain1, flip1 >> 2,
(flip1 >> 1) & 1, flip1 & 1);
}
else
{
add_chain(chain2, flip2 >> 2,
(flip2 >> 1) & 1, flip2 & 1);
}
}
if (best_answer.empty() ||
answer.size() < best_answer.size())
{
best_answer = answer;
}
} while (next_permutation(parts.begin(), parts.end()));
}
}

answer = best_answer;

auto t3 = clock();

finalize();

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

1.4.3 *Rezolvare detaliată

1.5 Vision Program


Problema 5 - Vision Program 100 de puncte

Author: Danylo Mysak

Trebuie să implementaţi un program de interpretare pentru un robot. De fiecare dată când
robotul face o poză, aceasta este stocată ı̂n memoria robotului ca o imagine albnegru. Fiecare
imagine este o matrice cu H  W pixeli, cu rândurile numerotate de la 0 la H  1 şi coloanele
numerotate de la 0 la W  1. Există exact doi pixeli negri ı̂n fiecare imagine şi toţi ceilalţi pixeli
sunt albi.
Robotul poate procesa fiecare imagine folosind un program format din instrucţiuni simple. Se
dau valorile H, W şi un ı̂ntreg pozitiv K. Scopul vostru este să scrieţi o funcţie care să producă
un program pentru robot care, pentru orice imagine primită, să determine dacă distanţa dintre
cei doi pixeli negri este exact K. Distanţa ı̂ntre un pixel de pe linia r1 şi coloana c1 , şi un pixel
de pe linia r2 şi coloana c2 este ¶r1  r2 ¶  ¶c1  c2 ¶. În această formulă, ¶x¶ reprezintă valoarea
absolută a lui x, care este x dacă x ' 0 şi x dacă x $ 0.
Acum vom descrie cum funcţionează robotul.
Memoria robotului este formată dintr-un vector suficient de mare de celule, indexat de la 0.
Fiecare celulă este sau 0, sau 1, iar această valoare odată setată, nu poate fi modificată. Imaginea
este memorată linie cu linie ı̂n celule indexate de la 0 la H W  1. Prima linie este stocată ı̂n
celulele de la 0 la W  1, iar ultima linie este stocată ı̂n celulele de la H  1 W la H W  1.
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 75

Mai exact, dacă pixelul de pe linia i şi coloana j este negru, valoarea celulei i W j este 1, altfel
0.
Programul robotului este format dintr-o serie de instrucţiuni, numerotate prin ı̂ntregi con-
secutivi ı̂ncepând de la 0. Când programul rulează, instrucţiunile sunt executate una câte una.
Fiecare instrucţiune citeşte valorile din una sau mai multe celule (numim aceste valori intrările
instrucţiunii) şi produc o singură valoare egală cu 0 sau 1 (numim această valoare ieşirea
instrucţiunii). Ieşirea instrucţiunii i este stocată ı̂n celula H W  i. Intrările instrucţiunii ipot
fi celule care stochează fie pixeli, fie ieşirile instrucţiunilor precedente, adică celulele de la 0 la
H W  i  1.
Există patru tipuri de instrucţiuni:
a NOT : are o singură intrare. Ieşirea este 1 dacă intrarea e 0, altfel ieşirea este 0.
a AND : are una sau mai multe intrări. Ieşirea este 1 dacă şi numai dacă toate intrările sunt
1.
a OR : are una sau mai multe intrări. Ieşirea este dacă şi numai dacă cel puţin una din
intrări este 1.
a XOR : are una sau mai multe intrări. Ieşirea este 1 dacă şi numai dacă un număr impar
de intrări sunt 1.
Ieşirea ultimei instrucţiuni a programului trebuie să fie 1 dacă distanţa dintre cei doi pixeli
negri este exact K, altfel ieşirea este 0.

Detalii de implementare

Trebuie să implementaţi următoarea funcţie:


void construct_network(int H, int W, int K)
H, W : dimensiunile fiecărei imagini făcute de camera robotului
a
K: un ı̂ntreg pozitiv
a
a Această funcţie trebuie să implementeze programul robotului. Pentru orice imagine făcută
de camera robotului, acest program trebuie să determine dacă distanţa dintre cei doi pixeli negri
din imagine este exact K.
Această funcţie trebuie să apeleze una sau mai multe funcţii din următoarele pentru a adăuga
instrucţiuni programului robotului (care iniţial e gol):
int add_not(int N)
int add_and(int[] Ns)
int add_or(int[] Ns)
int add_xor(int[] Ns)
aAdaugă o instrucţiune NOT , AND , OR , respectiv XOR .
aN (pentru add_not): indicele celulei din care instrucţiunea NOT adăugată ı̂şi citeşte in-
trarea.
a Ns (pentru add_and, add_or, add_xor): un vector ce conţine indicii celulelor din care
instrucţiunile adăugate AND, OR sau XOR ı̂şi citesc intrările.
a Fiecare astfel de funcţie returnează indicele celulei care stochează ieşirea instrucţiunii.
Apelările consecutive ale acestor funcţii returnează ı̂ntregi consecutivi ı̂ncepând de la H W .
Programul robotului poate să conţină cel mult 10000 de instrucţiuni. Instrucţiunile pot citi
cel mult 1000000 de valori ı̂n total. Cu alte cuvinte, lungimea totală a vectorilor Ns din apelurile
funcţiilor add_and, add_or şi add_xor, plus numărul de apeluri ale funcţiei add_not nu poate
depăşi 1000000.
După adăugarea ultimei instrucţiuni, trebuie să ieşiţi din funcţia construct_network.
După aceea, programul robotului va fi evaluat pe o serie de imagini. Soluţia voastră trece un
anumit test dacă pentru fiecare dintre aceste imagini, ieşirea ultimei instrucţiuni este 1 dacă şi
numai dacă distanţa dintre cei doi pixeli negri din imagine este egală cu K.
Evaluarea soluţiei voastre poate returna unul din următoarele mesaje (scrise ı̂n limba engleză):
a Instruction with no inputs: a fost transmis un vector gol ca intrare pentru una din
funcţiile add_and, add_or sau add_xor.
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 76

a Invalid index: indicele unei celule transmis ca intrare pentru una din funcţiile add_and,
add_or, add_xor sau add_not este incorect (posibil negativ).
a Too many instructions: funcţia voastră a ı̂ncercat să adauge mai mult de 10000 de
instrucţiuni.
a Too many inputs: instrucţiunile citesc mai mult de 1000000 de valori ı̂n total.

Exemple

Presupunem că H 2, W 3 şi K 3. Există doar două posibile imagini pentru care distanţa
dintre cei doi pixeli negri este 3.

Figura 1.6: vision

a Cazul 1: pixelii negri sunt 0 şi 5


a Cazul 2: pixelii negri sunt 2 şi 3
O posibilă soluţie este să construim programul robotului prin apelarea următoarelor funcţii:
1. add_and([0, 5]), va adăuga o instrucţiune care afişează 1 dacă şi numai dacă cazul 1
este analizat. Ieşirea este stocată ı̂n celula 6.
2. add_and([2, 3]), va adăuga o instrucţiune care afişează 1 dacă şi numai dacă cazul 2
este analizat. Ieşirea este stocată ı̂n celula 7.
3. add_or([6, 7]), va adăuga o instrucţiune care afişează 1 dacă şi numai dacă unul din
cele două cazuri este analizat.

Restricţii

a 1 & H & 200


a 1 & W & 200
a 2&H W
a 1&K &H W  2

Subtaskuri

1. (10 puncte) max H, W  & 3


2. (11 puncte) max H, W  & 10
3. (11 puncte) max H, W  & 30
4. (15 puncte) max H, W  & 100
5. (12 puncte) min H, W  1
6. (8 puncte) Pixelul de pe linia 0 şi coloana 0 este negru ı̂n fiecare imagine.
7. (14 puncte) K 1
8. (19 puncte) Fără restricţii suplimentare.

Exemplu de grader

Grader-ul citeşte intrarea ı̂n următorul format:


a linia 1: H W K
a linia 2  i (i ' 0): r1 c1 r2 c2
a ultima linie: 1
Fiecare linie, cu excepţia primei şi ultimei linii, reprezintă o imagine cu doi pixeli negri. Notăm
imaginea descrisă pe linia 2  i, imaginea i. Un pixel negru se află pe linia r1 şi coloana c1 , iar
celălalt pe linia r2 , coloana c2 .
Grader-ul mai ı̂ntâi apelează funcţia construct_network(H, W, K). Dacă
construct_network nu respectă anumite condiţii menţionate ı̂n enunţul problemei, grader-ul
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 77

va afişa unul din mesajele de eroare menţionat la sfârşitul secţiunii de Implementare, iar apoi
iese.
Altfel, grader-ul produce două ieşiri.
În prima fază, grader-ul afişează rezultatul programului robotului ı̂n următorul format:
a linia 1  i (0 & i): ieşirea ultimei instrucţiuni din programul robotului pentru imaginea i (1
sau 0).
În a doua fază, grader-ul scrie ı̂n directorul curent un fişier log.txt cu următorul format:
a linia 1  i (0 & i): m0 m1 ... mc  1
Secvenţa de pe linia 1  i descrie valorile stocate ı̂n celulele din memoria robotului după ce
programul robotului a fost rulat, dându-se imaginea i ca intrare. Mai exact, mj  reprezintă
valoarea celulei j. Remarcaţi că valoarea lui c (lungimea secvenţei) este egală cu H W plus
numărul de instrucţiuni din programul robotului.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 1024 MB

1.5.1 Indicaţii de rezolvare

Let’s call a right diagonal a set of pixels that have coordinates r  a, c  a, where r, c is one
of the pixels on the diagonal and a is an integer assuming values in some range. The difference
between coordinates has the same value for all pixels lying on a diagonal: it’s equal to D r  c.
For two right diagonals with such differences equal to D1 and D2 , we say that the diagonals are
at distance d if ¶D1  D2 ¶ d.
In a similar manner, let’s call a left diagonal a set of pixels that have coordinates r  a, c  a.
The sum of coordinates is equal to D r  c for all pixels on a left diagonal. Just as with right
diagonals, two left diagonals are at distance d if ¶D1  D2 ¶ d.
Lemma. Distance between two pixels r1 , c1  and r2 , c2  is less than or equal to d if and
only if both the distance between the left diagonals that contain the pixels is less than or equal to
d and the distance between the right diagonals that contain the pixels is less than or equal to d.
Proof. The condition on diagonals can be written as follows:

¶ r1  c1   r2  c2 ¶ & d,
w
¶ r1  c1   r2  c2 ¶ & d.

This is equivalent to:


d & r1  c1   r2  c2  & d,
w
d & r1  c1   r2  c2  & d.
This in turn is equivalent to the following four double inequalities:

~
„
„
d & r1  r2   c2  c1  & d,
„
„
„d
„ & r2  r1   c1  c2  & d,
‚
„
„
„
„
d & r1  r2   c1  c2  & d,
„
„d
€ & r2  r1   c2  c1  & d.

Finally, this is equivalent to ¶r1  r2 ¶  ¶c1  c2 ¶ & d, where the LHS is the definition of distance
between pixels. The lemma has been proven.
The algorithm then is as follows:
1. Add an instruction for each diagonal (both left and right ones) indicating 1. whether there
is a black pixel within the diagonal.
2. Add an instruction for each diagonal (both left and right ones) indicating whether there are
two black pixels within the diagonal (combining OR and XOR might come in handy).
3. Using instructions from 1. and 2., for each block of K  1 consecutive left diagonals have an
instruction that reports whether there are two black pixels within the block.
4. Do the same for right diagonals.
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 78

5. Add a gate that returns true if and only if there is at least one gate from 3. and at least one
gate from 4. that both return true.
6. Repeat steps 3. - 5. for K instead of K  1.
7. The pixels are at distance K if and only if 5. reports true but 6. reports f alse.

The algorithm requires O H  W  instructions which consume O HW  inputs. Note, however,


that there exist other completely different approaches with the same order of instructions and
inputs used.

1.5.2 Coduri sursă

Listing 1.5.1: compile cpp.sh


#!/bin/bash

problem=vision

g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"

"grader.cpp" "${problem}.cpp"

Listing 1.5.2: vision.h


#include <vector>
#include <string>

void construct_network(int H, int W, int K);

int add_and(std::vector<int> Ns);

int add_or(std::vector<int> Ns);

int add_xor(std::vector<int> Ns);

int add_not(int N);

Listing 1.5.3: vision.cpp


#include "vision.h"

void construct_network(int H, int W, int K)


{
std::vector<int> Ns;
Ns = {0, 1};
int a = add_and(Ns);
Ns = {0, a};
int b = add_or(Ns);
Ns = {0, 1, b};
int c = add_xor(Ns);
add_not(c);
}

Listing 1.5.4: grader.cpp


#include <cstdio>
#include <cassert>
#include <string>
#include "vision.h"

using namespace std;

static const int MAX_INSTRUCTIONS = 10000;


static const int MAX_INPUTS = 1000000;

static const int _AND = 0;


static const int _OR = 1;
static const int _XOR = 2;
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 79

static const int _NOT = 3;

static inline bool increasing(int a, int b, int c)


{
return a <= b && b <= c;
}

[[noreturn]] static inline void error(string message)


{
printf("%s\n", message.c_str());
exit(0);
}

class InstructionNetwork
{

struct Instruction
{
int type;
vector<int> input_indexes;

inline Instruction(int _type, const vector<int>& _input_indexes):


type(_type), input_indexes(_input_indexes) { }

inline int apply(int a, int b) const


{
switch (type)
{
case _AND:
return a & b;
case _OR:
return a | b;
case _XOR:
return a ˆ b;
default:
return 0;
}
}

inline int compute(const vector<int>& memory_cells) const


{
int r = memory_cells[input_indexes[0]];
if (type == _NOT)
return 1 - r;
for (int j = 1; j < (int)input_indexes.size(); j++)
r = apply(r, memory_cells[input_indexes[j]]);
return r;
}
};

int input_size;
int total_inputs;
vector<Instruction> instructions;

public:

inline void init(int _input_size)


{
this->input_size = _input_size;
this->total_inputs = 0;
this->instructions.clear();
}

inline int add_instruction(int type, const vector<int>& input_indexes)


{
if (input_indexes.size() == 0)
error("Instruction with no inputs");

if (instructions.size() + 1 > MAX_INSTRUCTIONS)


error("Too many instructions");

if (total_inputs + input_indexes.size() > MAX_INPUTS)


error("Too many inputs");

instructions.emplace_back(type, input_indexes);
total_inputs += input_indexes.size();
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 80

int new_index = input_size + (int)instructions.size() - 1;

for (int input_index : input_indexes)


if (!increasing(0, input_index, new_index-1))
error("Invalid index");

return new_index;
}

inline int compute(vector<int> &memory_cells) const


{
for (auto &instruction : instructions)
memory_cells.push_back(instruction.compute(memory_cells));
return memory_cells.back();
}
};

static InstructionNetwork instructionNetwork;

int main()
{
int H, W, K;
assert(3 == scanf("%d%d%d", &H, &W, &K));

FILE *log_file = fopen("log.txt","w");

instructionNetwork.init(H * W);
construct_network(H, W, K);

while (true)
{
int rowA, colA, rowB, colB;
assert(1 == scanf("%d", &rowA));
if (rowA == -1)
break;
assert(3 == scanf("%d%d%d", &colA, &rowB, &colB));

if ((!increasing(0, rowA, H-1)) ||


(!increasing(0, colA, W-1)) ||
(!increasing(0, rowB, H-1)) ||
(!increasing(0, colB, W-1)) ||
(rowA == rowB && colA == colB))
{
printf("-1\n");
fprintf(log_file, "-1\n");
fflush(stdout);
fflush(log_file);
continue;
}

vector<int> memory_cells;
for (int row = 0; row < H; row++)
for (int col = 0; col < W; col++)
{
bool active = (row == rowA && col == colA) ||
(row == rowB && col == colB);
memory_cells.push_back(active ? 1 : 0);
}
int computation_result = instructionNetwork.compute(memory_cells);

printf("%d\n", computation_result);
fflush(stdout);

for(int i = 0; i < (int)memory_cells.size(); i++)


fprintf(log_file, (i ? " %d" : "%d"), memory_cells[i]);
fprintf(log_file, "\n");
fflush(log_file);
}
fclose(stdin);
}

int add_and(vector<int> Ns)


{
return instructionNetwork.add_instruction(_AND, Ns);
}
CAPITOLUL 1. IOI 2019 1.5. VISION PROGRAM 81

int add_or(vector<int> Ns)


{
return instructionNetwork.add_instruction(_OR, Ns);
}

int add_xor(vector<int> Ns)


{
return instructionNetwork.add_instruction(_XOR, Ns);
}

int add_not(int N)
{
vector<int> Ns = {N};
return instructionNetwork.add_instruction(_NOT, Ns);
}

Listing 1.5.5: vision-model.cpp


#include "vision.h"

using namespace std;

inline int OR(vector<int> &Ns)


{
return add_or(Ns);
}

inline int XOR(vector<int> &Ns)


{
return add_xor(Ns);
}

inline int XOR(int A, int B)


{
return add_xor({A, B});
}

inline int AND(int A, int B)


{
return add_and({A, B});
}

inline int AND_NOT(int A, int B)


{
return AND(A, add_not(B));
}

vector<int> diagonal(int H, int W, int N, bool D)


{
vector<int> cells;
int first, last, step;
if (D)
{
first = N < W ? N : W * (N - W + 2) - 1;
last = N < H ? N * W : (H - 1) * W + (N - H + 1);
step = W - 1;
}
else
{
first = N < W ? W - 1 - N : W * (N - W + 1);
last = N < H ? (N + 1) * W - 1 : H * W + (H - 2 - N);
step = W + 1;
}
int cur = first;
while (true)
{
cells.emplace_back(cur);
if (cur == last)
{
break;
}
cur += step;
}
return cells;
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 82

void collect_diagonals(int H, int W, bool D, vector<int> &ors, vector<int> &xors)


{
for (int i = 0; i < H + W - 1; i++)
{
vector<int> cells = diagonal(H, W, i, D);
ors.emplace_back(OR(cells));
xors.emplace_back(XOR(cells));
}
}

int find_pair(const vector<int> &ors, const vector<int> &xors, int maxDistance)


{
int n = maxDistance + 1;
vector<int> curOrs(n), curXors(n), results;
for (int i = 0; i < (int)ors.size(); i++)
{
curOrs[i % n] = ors[i];
curXors[i % n] = xors[i];
if (i >= maxDistance)
{
results.emplace_back(AND_NOT(OR(curOrs), XOR(curXors)));
}
}
return OR(results);
}

void construct_network(int H, int W, int K)


{
vector<int> orsA, xorsA, orsB, xorsB;
collect_diagonals(H, W, true, orsA, xorsA);
collect_diagonals(H, W, false, orsB, xorsB);
AND_NOT(AND(find_pair(orsA, xorsA, K),
find_pair(orsB, xorsB, K)),
AND(find_pair(orsA, xorsA, K - 1),
find_pair(orsB, xorsB, K - 1)));
}

1.5.3 *Rezolvare detaliată

1.6 Sky Walking


Problema 6 - Sky Walking 100 de puncte

Author: Riku Kawasaki

Kenan desenează un plan al clădirilor şi pasarelelor de-a lungul bulevardului principal din
Baku. Există clădiri numerotate de la 0 la n  1 şi m pasarele numerotate de la 0 la m  1.
Planul este desenat pe o suprafaţă bidimensională, unde clădirile şi pasarelele sunt segmente
verticale respectiv orizontale.
Partea de jos a clădirii i (0 & j & m  1) se află la punctul xi, 0, iar clădirea are ı̂nălţimea
hi. Prin urmare, este un segment care conectează punctele xi, 0 şi xi, hi.
Pasarela j (0 & j & m  1) are puncte terminale la clădirile numerotate cu lj  şi rj , aflată
la o coordonată pozitivă y cu valoarea y j . Prin urmare, este un segment care uneşte punctele
xlj , y j  şi xrj , y j .
O pasarelă şi o clădire se intersectează dacă au un punct comun. Prin urmare, o pasarelă
intersectează două clădiri ı̂n punctele ei terminale, şi deasemenea poate intersecta alte clădiri ı̂ntre
acestea.
Kenan ar dori să găsească lungimea celui mai scurt drum de la baza clădirii s la baza clădirii
g, presupunând că se poate merge doar prin clădiri şi pasarele, sau să se determine dacă o astfel
de cale nu există.
Reţineţi că nu este permis să mergeţi pe jos, adică de-a lungul liniei orizontale cu coordonata
y egală cu 0.
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 83

În orice intersecţie se poate merge de pe pasarelă ı̂n clădire sau invers. Dacă punctele terminale
pentru două pasarele sunt identice, atunci se poate merge de pe o pasarelă pe alta.
Sarcina voastră este să ı̂l ajutaţi pe Kenan să răspundă la ı̂ntrebarea lui.

Detalii de implementare

Trebuie să implementaţi următoarea funcţie. Aceasta va fi apelată de către grader câte o dată
pentru fiecare test.
int64 min_distance(int[] x, int[] h, int[] l, int[] r, int[] y,
int s, int g)
a x şi h: vectori cu numere ı̂ntregi de lungime n
a l, r şi y: vectori cu numere ı̂ntregi de lungime m
a s şi g: două numere ı̂ntregi
a Această funcţie trebuie să returneze lungimea celui mai scurt drum dintre baza clădirii s şi
baza clădirii g, dacă o astfel de cale există. În caz contrar, trebuie să se returneze 1.

Exemple

Exemplul 1
Consideraţi următorul apel:
min_distance([0, 3, 5, 7, 10, 12, 14],
[8, 7, 9, 7, 6, 6, 9],
[0, 0, 0, 2, 2, 3, 4],
[1, 2, 6, 3, 6, 4, 6],
[1, 6, 8, 1, 7, 2, 5],
1, 5)
Răspunsul corect este 27.
Imaginea de mai jos corespunde Exemplului 1:

Figura 1.7: walk

Exemplul 2
min_distance([0, 4, 5, 6, 9],
[6, 6, 6, 6, 6],
[3, 1, 0],
[4, 3, 2],
[1, 3, 6],
0, 4)
Răspunsul corect este 21.

Restricţii
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 84

a 1 & n, m & 100000


0 & x0 $ x1 $ ... $ xn  1 & 10
9
a
1 & hi & 10 (pentru 0 & i & n  1)
9
a
a 0 & li $ ri & n  1 (pentru 0 & i & m  1)
a 1 & y i & min hli, hri (pentru 0 & i & m  1)
a 0 & s, g & n  1
a sjg
a Două pasarele nu au puncte comune, eventuale excepţii pot fi punctele lor terminale.

Subtaskuri

1. (10 puncte) n, m & 50


2. (14 puncte) Fiecare pasarelă intersectează cel mult 10 clădiri.
3. (15 puncte) s 0, g n  1, şi toate clădirile au aceeaşi ı̂nălţime
4. (18 puncte) s 0, g n  1
5. (43 de puncte) Fără restricţii suplimentare.

Exemplu de grader

Grader-ul citeşte intrarea ı̂n următorul format:


a linia 1: n m
a linia 2  i (0 & i & n  1): xi hi
a linia n  2  j (0 & j & m  1): lj  rj  y j 
a linia n  m  2: s g
Grader-ul va tipări o singură linie conţinând valoarea returnată de min_distance.
Timp maxim de executare/test: 4.0 secunde
Memorie: total 1024 MB

1.6.1 Indicaţii de rezolvare

Subtask 1
For each skywalk and each building, check if they intersect and if they do, add a new vertex
for the intersection point. Additionally, put a node on the bottom of each of the buildings s, and
g.
Add edges between consecutive nodes on each building and also for consecutive nodes on each
skywalk.
Use Dijkstra to find the shortest path from the node on the bottom of building s to the node
at the bottom of buildingg. Number of vertices and edges are O N M , so the total complexity is
O N M log N M .
Subtask 2
The solution for the previous subtask works here as well, however the graph must be built more
efficiently. To do that, iterate in increasing order over heights which either contain a skywalk or
the endpoint of a building. The goal is to keep a list of all buildings that are at least as tall as the
current height. Given such list, for each skywalk, start from its left endpoint and move through
the list. Each element is an intersection between that skywalk and a building. Hence you will
at most visit 10 elements before reaching the right endpoint. Add a vertex for each intersection.
The rest of the solution is the same as the last subtask. For maintaining the list of buildings, it is
enough to start from a list of all buildings. Then at each height, after processing the skywalks for
that height, remove the buildings that have an endpoint at that height. This can be done using
a linked-list or a BST.
Subtask 3
When s 0 and g n  1, it can be proven that skywalks are always traversed from left to
right. Additionally when all of the buildings have the same height, it can be shown that there
exists an optimal path where each skywalk is either not visited or is traversed completely until its
right endpoint (though the entrance point to that skywalk might not be its left endpoint).
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 85

Iterate over skywalks in increasing order of their right endpoint, breaking ties in favor of lower
skywalks. The goal is to maintain the minimum cost to reach each skywalk’s right endpoint.
During the iteration, for each skywalk, update this value for the skywalks just above, and just
below its right endpoint based on its own value. The answer is the minimum cost to reach the
right endpoint of one of skywalks ending at n  1 adding the cost of reaching to the bottom of the
building n  1.
Subtask 4
For this subtask, the solution is to use the same strategy as the first two subtasks and build
a graph. Consider the graph in those two subtasks. Let e be a vertical edge connecting points
x, y1  and x, y2  for some x, y1 , y2 (y1 $ y2 ). Edge e is called irrelevant if the point x, y2  lies
strictly inside of some skywalk. Since s 0 and g n  1, it can be proved that there exists a
shortest path that does not pass through irrelevant edges. Hence, after removing the irrelevant
edges from the graph, the length of the shortest path doesn’t change. In the new graph, for each
vertical edge, the top node is the endpoint of a skywalk. So, there are at most O M  vertical
edges left in the graph. This means at most O M  vertices have at least one edge connected to
them. Discarding the other vertices, the same approach as the first two subtasks can be followed
on the new graph.
Subtask 5
The solution for this subtask is almost the same as the last subtask. However, for the previous
theorem to hold, the skywalks must be adjusted. Specifically, for each skywalk between two
buildings such as l, and r where l $ s $ r, divide it into 3 parts as follows:
ˆ let a be the last building before s (including s) that is as tall as this skywalk.
ˆ let b be the first building after s (including s) that is as tall as this skywalk.
replace the skywalk with the following skywalks:
` skywalk connecting buildings l and a.
` skywalk connecting buildings a and b.
` skywalk connecting buildings b and r.

The same adjustment must be done for all skywalks that intersect or pass over building g.
Then the same solution as subtask 4 works.

1.6.2 Coduri sursă

Listing 1.6.1: compile cpp.sh


#!/bin/bash

problem=walk

g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"

"grader.cpp" "${problem}.cpp"

Listing 1.6.2: walk.h


#include <vector>

long long min_distance(std::vector<int> x, std::vector<int> h,


std::vector<int> l, std::vector<int> r,
std::vector<int> y, int s, int g);

Listing 1.6.3: walk.cpp


#include "walk.h"

long long min_distance(std::vector<int> x, std::vector<int> h,


std::vector<int> l, std::vector<int> r,
std::vector<int> y, int s, int g)
{
return 1;
}
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 86

Listing 1.6.4: grader.cpp


#include "walk.h"
#include <cstdio>
#include <cassert>

using namespace std;

int main()
{
int n, m;
assert(2 == scanf("%d%d", &n, &m));
vector<int> x(n), h(n);
for (int i = 0; i < n; i++)
assert(2 == scanf("%d%d", &x[i], &h[i]));
vector<int> l(m), r(m), y(m);
for (int i = 0; i < m; i++)
assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
int s, g;
assert(2 == scanf("%d%d", &s, &g));
fclose(stdin);

long long result = min_distance(x, h, l, r, y, s, g);

printf("%lld\n", result);
fclose(stdout);
return 0;
}

Listing 1.6.5: walk-model.cpp


#include "walk.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <cassert>

using namespace std;

const int MAXN = 200 * 1000 + 10;


const int MAXM = 200 * 1000 + 10;
const int MAXRM = 5 * MAXM;
const int MAXV = 2 * 2 * MAXRM;
const long long INF = 1000ll * 1000 * 1000 * 1000 * 1000 * 1000;

typedef pair<int, int> pii;


typedef set<pii>::iterator sit;

typedef pair<long long, int> pli;


long long dist[MAXV];

int vcnt = 0;
vector<pii> edges[MAXV];
set<pli> dij;
int last_x[MAXRM];
int last_vertex[MAXRM];
int last_height[MAXN];
int first_height[MAXN];
vector<pii> adds[MAXN];
vector<pii> removes[MAXN];
set<pii> walks;

long long dijkstra(int start, int dest)


{
for (int i = 0; i < vcnt; i++)
dist[i] = INF;
dist[start] = 0;
dij.insert(pli(dist[start], start));
while (!dij.empty())
{
int v = dij.begin()->second;
dij.erase(dij.begin());
if (v == dest)
return dist[v];
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 87

for (int i = 0; i < (int)edges[v].size(); i++)


{
int u = edges[v][i].first;
int w = edges[v][i].second;
if (dist[u] > dist[v] + w)
{
dij.erase(pli(dist[u], u));
dist[u] = dist[v] + w;
dij.insert(pli(dist[u], u));
}
}
}
return -1;
}

void add_edge(int u, int v, int w)


{
edges[u].push_back(pii(v, w));
edges[v].push_back(pii(u, w));
}

int make_vertex(int walk, int x)


{
if (last_x[walk] == x)
return last_vertex[walk];
int cur = vcnt++;
if (last_vertex[walk] != -1)
add_edge(last_vertex[walk], cur, x - last_x[walk]);
last_x[walk] = x;
last_vertex[walk] = cur;
return cur;
}

void break_segments(vector<int> &l, vector<int> &r,


vector<int> &y, int s, vector<int> &h)
{
vector<int> heights = h;
sort(heights.begin(), heights.end());
heights.resize(unique(heights.begin(),
heights.end()) - heights.begin());
for (int i = 0; i < (int)heights.size(); i++)
{
last_height[i] = -1;
first_height[i] = h.size() + 1;
}
for (int i = 0; i < (int)h.size(); i++)
{
int idx = lower_bound(heights.begin(), heights.end(),
h[i]) - heights.begin();
assert(idx < (int)heights.size());
if (i <= s)
last_height[idx] = max(last_height[idx], i);
if (i >= s)
first_height[idx] = min(first_height[idx], i);
}
for (int i = (int)heights.size() - 2; i >= 0; i--)
{
last_height[i] = max(last_height[i], last_height[i + 1]);
first_height[i] = min(first_height[i], first_height[i + 1]);
}
for (int i = 0; i < (int)l.size(); i++)
if (l[i] < s && r[i] > s)
{
int idx = lower_bound(heights.begin(), heights.end(),
y[i]) - heights.begin();
assert(idx < (int)heights.size());
int x = l[i];
int a = last_height[idx];
int b = first_height[idx];
int q = r[i];

assert(a != -1);
assert(b != (int)h.size() + 1);

if (x < a)
{
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 88

r[i] = a;
if (a < b)
{
l.push_back(a);
r.push_back(b);
y.push_back(y[i]);
}
}
else
{
assert(x == a);
r[i] = b;
}
if (q > b)
{
l.push_back(b);
r.push_back(q);
y.push_back(y[i]);
}
else
{
assert(q == b);
}
}
}

long long min_distance(vector<int> x, vector<int> h,


vector<int> l, vector<int> r,
vector<int> y, int s, int g)
{
if (s == g)
return 0;
int n = x.size();

break_segments(l, r, y, s, h);
break_segments(l, r, y, g, h);
int m = l.size();

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


{
last_x[i] = last_vertex[i] = -1;
adds[l[i]].push_back(pii(y[i], i));
removes[r[i]].push_back(pii(y[i], i));
}
int sv = -1, gv = -1;
long long res = 0;
for (int i = 0; i < n; i++)
{
sort(adds[i].begin(), adds[i].end());
for (int j = 0; j < (int)adds[i].size(); j++)
{
int v = make_vertex(adds[i][j].second, x[i]);
sit it = walks.lower_bound(adds[i][j]);
if (it != walks.begin())
{
it--;
int u = make_vertex(it->second, x[i]);
add_edge(u, v, adds[i][j].first - it->first);
}
walks.insert(adds[i][j]);
}
if (i == s)
{
if (walks.empty() || walks.begin()->first > h[i])
return -1;
sv = make_vertex(walks.begin()->second, x[i]);
res += walks.begin()->first;
}
if (i == g)
{
if (walks.empty() || walks.begin()->first > h[i])
return -1;
gv = make_vertex(walks.begin()->second, x[i]);
res += walks.begin()->first;
}
sort(removes[i].begin(), removes[i].end(), greater<pii>());
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 89

for (int j = 0; j < (int)removes[i].size(); j++)


{
int v = make_vertex(removes[i][j].second, x[i]);
sit it = walks.find(removes[i][j]);
if (it != walks.begin())
{
sit it2 = it;
it2--;
int u = make_vertex(it2->second, x[i]);
add_edge(u, v, removes[i][j].first - it2->first);
}
assert(it != walks.end());
walks.erase(it);
}
}
assert(sv != -1);
assert(gv != -1);
long long dij_res = dijkstra(sv, gv);
if(dij_res == -1)
return -1;
return res + dij_res;
}

Listing 1.6.6: akm-full+grader.cpp


#include "walk.h" // execution time : 3.234 s
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <cassert>
#include <cstdio>
#include <ctime>

using namespace std;

const int MAXN = 200 * 1000 + 10;


const int MAXM = 200 * 1000 + 10;
const int MAXRM = 5 * MAXM;
const int MAXV = 2 * 2 * MAXRM;
const long long INF = 1000ll * 1000 * 1000 * 1000 * 1000 * 1000;

typedef pair<int, int> pii;


typedef set<pii>::iterator sit;

typedef pair<long long, int> pli;


long long dist[MAXV];

int vcnt = 0;
vector<pii> edges[MAXV];
set<pli> dij;

int last_x[MAXRM];
int last_vertex[MAXRM];
int last_height[MAXN];
int first_height[MAXN];

vector<pii> adds[MAXN];
vector<pii> removes[MAXN];
set<pii> walks;

long long dijkstra(int start, int dest)


{
for (int i = 0; i < vcnt; i++)
dist[i] = INF;
dist[start] = 0;
dij.insert(pli(dist[start], start));
while (!dij.empty())
{
int v = dij.begin()->second;
dij.erase(dij.begin());
if (v == dest)
return dist[v];
for (int i = 0; i < (int)edges[v].size(); i++)
{
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 90

int u = edges[v][i].first;
int w = edges[v][i].second;
if (dist[u] > dist[v] + w)
{
dij.erase(pli(dist[u], u));
dist[u] = dist[v] + w;
dij.insert(pli(dist[u], u));
}
}
}
return -1;
}

void add_edge(int u, int v, int w)


{
edges[u].push_back(pii(v, w));
edges[v].push_back(pii(u, w));
}

int make_vertex(int walk, int x)


{
if (last_x[walk] == x)
return last_vertex[walk];
int cur = vcnt++;
if (last_vertex[walk] != -1)
add_edge(last_vertex[walk], cur, x - last_x[walk]);
last_x[walk] = x;
last_vertex[walk] = cur;
return cur;
}

void break_segments(vector<int> &l, vector<int> &r,


vector<int> &y, int s, vector<int> &h)
{
vector<int> heights = h;
sort(heights.begin(), heights.end());
heights.resize(unique(heights.begin(),
heights.end()) - heights.begin());
for (int i = 0; i < (int)heights.size(); i++)
{
last_height[i] = -1;
first_height[i] = h.size() + 1;
}

for (int i = 0; i < (int)h.size(); i++)


{
int idx = lower_bound(heights.begin(), heights.end(),
h[i]) - heights.begin();
assert(idx < (int)heights.size());
if (i <= s)
last_height[idx] = max(last_height[idx], i);
if (i >= s)
first_height[idx] = min(first_height[idx], i);
}

for (int i = (int)heights.size() - 2; i >= 0; i--)


{
last_height[i] = max(last_height[i], last_height[i + 1]);
first_height[i] = min(first_height[i], first_height[i + 1]);
}

for (int i = 0; i < (int)l.size(); i++)


if (l[i] < s && r[i] > s)
{
int idx = lower_bound(heights.begin(), heights.end(),
y[i]) - heights.begin();
assert(idx < (int)heights.size());
int x = l[i];
int a = last_height[idx];
int b = first_height[idx];
int q = r[i];

assert(a != -1);
assert(b != (int)h.size() + 1);

if (x < a)
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 91

{
r[i] = a;
if (a < b)
{
l.push_back(a);
r.push_back(b);
y.push_back(y[i]);
}
}
else
{
assert(x == a);
r[i] = b;
}
if (q > b)
{
l.push_back(b);
r.push_back(q);
y.push_back(y[i]);
}
else
{
assert(q == b);
}
}
}

long long min_distance(vector<int> x, vector<int> h,


vector<int> l, vector<int> r,
vector<int> y, int s, int g)
{
if (s == g)
return 0;
int n = x.size();

break_segments(l, r, y, s, h);
break_segments(l, r, y, g, h);
int m = l.size();

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


{
last_x[i] = last_vertex[i] = -1;
adds[l[i]].push_back(pii(y[i], i));
removes[r[i]].push_back(pii(y[i], i));
}

int sv = -1, gv = -1;


long long res = 0;
for (int i = 0; i < n; i++)
{
sort(adds[i].begin(), adds[i].end());
for (int j = 0; j < (int)adds[i].size(); j++)
{
int v = make_vertex(adds[i][j].second, x[i]);
sit it = walks.lower_bound(adds[i][j]);
if (it != walks.begin())
{
it--;
int u = make_vertex(it->second, x[i]);
add_edge(u, v, adds[i][j].first - it->first);
}
walks.insert(adds[i][j]);
}

if (i == s)
{
if (walks.empty() || walks.begin()->first > h[i])
return -1;
sv = make_vertex(walks.begin()->second, x[i]);
res += walks.begin()->first;
}

if (i == g)
{
if (walks.empty() || walks.begin()->first > h[i])
return -1;
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 92

gv = make_vertex(walks.begin()->second, x[i]);
res += walks.begin()->first;
}

sort(removes[i].begin(), removes[i].end(), greater<pii>());

for (int j = 0; j < (int)removes[i].size(); j++)


{
int v = make_vertex(removes[i][j].second, x[i]);
sit it = walks.find(removes[i][j]);
if (it != walks.begin())
{
sit it2 = it;
it2--;
int u = make_vertex(it2->second, x[i]);
add_edge(u, v, removes[i][j].first - it2->first);
}
assert(it != walks.end());
walks.erase(it);
}
}

assert(sv != -1);
assert(gv != -1);

long long dij_res = dijkstra(sv, gv);

if(dij_res == -1)
return -1;
return res + dij_res;
}

//-------------- start grader ------------------

int main()
{
std::freopen("../tests/5-30.in", "r", stdin) ; // execution time : 3.199 s
std::freopen("walking.out", "w", stdout) ;

auto t1 = clock();

int n, m;
assert(2 == scanf("%d%d", &n, &m));
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<int> x(n), h(n);


for (int i = 0; i < n; i++)
assert(2 == scanf("%d%d", &x[i], &h[i]));

vector<int> l(m), r(m), y(m);


for (int i = 0; i < m; i++)
assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
int s, g;
assert(2 == scanf("%d%d", &s, &g));
fclose(stdin);

auto t2 = clock();

long long result = min_distance(x, h, l, r, y, s, g);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 93

return 0;
}
//-------------- end grader ------------------

Listing 1.6.7: maroon-full+grader.cpp


#include <bits/stdc++.h> // execution time : 4.156 s

using namespace std;

using ll = int64_t;

#define FOR(i, a, b) for (int i = int(a); i < int(b); i++)


#define REP(i, b) FOR(i, 0, b)
#define MP make_pair
#define PB push_back
#define EB emplace_back
#define ALL(x) x.begin(), x.end()

auto &errStream = cerr;

#ifdef LOCAL
#define cerr (cerr << "-- line " << __LINE__ << " -- ")
#else
class CerrDummy
{
} cerrDummy;
template <class T>
CerrDummy &operator<<(CerrDummy &cd, const T &)
{
return cd;
}
using charTDummy = char;
using traitsDummy = char_traits<charTDummy>;
CerrDummy &operator<<(CerrDummy &cd, basic_ostream<charTDummy,
traitsDummy> &(basic_ostream<charTDummy,
traitsDummy> &))
{
return cd;
}
#define cerr cerrDummy
#endif

#define REACH cerr << "reached" << endl


#define DMP(x) cerr << #x << ":" << x << endl
#define ZERO(x) memset(x, 0, sizeof(x))
#define ONE(x) memset(x, -1, sizeof(x))

using pi = pair<int, int>;


using vi = vector<int>;
using ld = long double;

template <class T, class U>


ostream &operator<<(ostream &os, const pair<T, U> &p)
{
os << "(" << p.first << "," << p.second << ")";
return os;
}

template <class T>


ostream &operator<<(ostream &os, const vector<T> &v)
{
os << "{";
REP(i, (int)v.size())
{
if (i)
os << ",";
os << v[i];
}
os << "}";
return os;
}

ll read()
{
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 94

ll i;
scanf("%" SCNd64, &i);
return i;
}

void printSpace()
{
printf(" ");
}

void printEoln()
{
printf("\n");
}

void print(ll x, int suc = 1)


{
printf("%" PRId64, x);
if (suc == 1)
printEoln();
if (suc == 2)
printSpace();
}

string readString()
{
static char buf[3341000];
scanf("%s", buf);
return string(buf);
}

char *readCharArray()
{
static char buf[3341000];
static int bufUsed = 0;
char *ret = buf + bufUsed;
scanf("%s", ret);
bufUsed += strlen(ret) + 1;
return ret;
}

template <class T, class U>


void chmax(T &a, U b)
{
if (a < b)
a = b;
}

template <class T, class U>


void chmin(T &a, U b)
{
if (b < a)
a = b;
}

template <class T>


T Sq(const T &t)
{
return t * t;
}

const ll infLL = LLONG_MAX / 3;

#ifdef int
const int inf = infLL;
#else
const int inf = INT_MAX / 2 - 100;
#endif

namespace Subtask4
{
ll Solve(vi x, vi h, vi l, vi r, vi y, int s, int g)
{
int n = x.size();
REP(k, 2)
{
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 95

int w = k == 0 ? s : g;
vector<pi> p{pi(h[w], w)}, q{pi(h[w], w)};

for (int i = w - 1; i >= 0; i--)


if (h[i] > p.back().first)
p.EB(h[i], i);

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


if (h[i] > q.back().first)
q.EB(h[i], i);

vi ll, rr, yy;


REP(i, l.size())
{
if (l[i] < w && w < r[i])
{
int a, b;
{
auto itr = lower_bound(ALL(p), pi(y[i], -1));
assert(itr != p.end());
a = itr->second;
}
{
auto itr = lower_bound(ALL(q), pi(y[i], -1));
assert(itr != q.end());
b = itr->second;
}
if (l[i] < a)
{
ll.PB(l[i]);
rr.PB(a);
yy.PB(y[i]);
}
if (a < b)
{
ll.PB(a);
rr.PB(b);
yy.PB(y[i]);
}
if (b < r[i])
{
ll.PB(b);
rr.PB(r[i]);
yy.PB(y[i]);
}
}
else
{
ll.PB(l[i]);
rr.PB(r[i]);
yy.PB(y[i]);
}
}
l = ll;
r = rr;
y = yy;
}

int m = l.size();

vector<pi> posYX{pi(0, x[s]), pi(0, x[g])};


{
vector<tuple<int, int, int>> xty;
REP(i, m)
{
xty.EB(x[l[i]], 0, y[i]);
xty.EB(x[r[i]], 1, -y[i]);
}

sort(ALL(xty));

multiset<int> ys;
for (auto q : xty)
{
int j=get<0>(q), t=get<1>(q), i = get<2>(q) * (t == 0 ? 1 : -1);
posYX.EB(i, j);
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 96

auto itr = ys.lower_bound(i);


if (itr != ys.begin())
{
--itr;
posYX.EB( *itr, j);
}
if (t == 0)
ys.insert(i);
else
ys.erase(ys.find(i));
}

sort(ALL(posYX));
posYX.erase(unique(ALL(posYX)), posYX.end());
}

const auto Idx = [&](int i, int j)


{
return lower_bound(ALL(posYX), pi(i, j)) - posYX.begin();
};

int vs = posYX.size();
vector<vector<pi>> graph(vs);
const auto AddEdge = [&](int a, int b, int c)
{
graph[a].EB(b, c);
graph[b].EB(a, c);
};

REP(i, m)
{
int k = Idx(y[i], x[l[i]]);
while (k + 1 < int(posYX.size()) && posYX[k + 1] <= pi(y[i], x[r[i]]))
{
AddEdge(k, k + 1, posYX[k + 1].second - posYX[k].second);
k++;
}
}

vector<pi> posXY = posYX;


for (auto &p : posXY)
swap(p.first, p.second);

sort(ALL(posXY));

REP(i, vs - 1)
{
if (posXY[i].first == posXY[i + 1].first)
{
AddEdge(Idx(posXY[i].second, posXY[i].first),
Idx(posXY[i + 1].second, posXY[i + 1].first),
posXY[i + 1].second - posXY[i].second);
}
}

vector<ll> dist(vs, infLL);

using pli = tuple<ll, int>;

priority_queue<pli, vector<pli>, greater<pli>> pq;

const auto Reach = [&](int v, ll d)


{
if (dist[v] > d)
{
dist[v] = d;
pq.push(pli(d, v));
}
};

Reach(Idx(0, x[s]), 0);


while (!pq.empty())
{
int v;
ll d;
tie(d, v) = pq.top();
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 97

pq.pop();
if (dist[v] != d)
continue;
for (auto e : graph[v])
Reach(e.first, dist[v] + e.second);
}

return dist[Idx(0, x[g])] == infLL ? -1 : dist[Idx(0, x[g])];


}
} // namespace Subtask4

long long min_distance(std::vector<int> X, std::vector<int> H,


std::vector<int> L, std::vector<int> R,
std::vector<int> Y, int S, int G)
{
return Subtask4::Solve(X, H, L, R, Y, S, G);
}

//-------------- start grader ------------------

int main()
{
std::freopen("../tests/5-30.in", "r", stdin) ;
std::freopen("walking.out", "w", stdout) ;

auto t1 = clock();

int n, m;
assert(2 == scanf("%d%d", &n, &m));
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<int> x(n), h(n);


for (int i = 0; i < n; i++)
assert(2 == scanf("%d%d", &x[i], &h[i]));

vector<int> l(m), r(m), y(m);


for (int i = 0; i < m; i++)
assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
int s, g;
assert(2 == scanf("%d%d", &s, &g));
fclose(stdin);

auto t2 = clock();

long long result = min_distance(x, h, l, r, y, s, g);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//-------------- end grader ------------------

Listing 1.6.8: mohammad-full-walk+grader.cpp


#include <iostream> // execution time : 6.042 s
#include <vector>
#include <memory.h>
#include <algorithm>
#include <set>
#include <map>
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 98

#include <ctime>
#include <cassert>

#define pb push_back
#define mp make_pair

using namespace std;

const int maxn = 1000000 + 10;


const long long inf = 1e18;

int n, m, X[maxn], Y[maxn], H[maxn], L[maxn], R[maxn],


a[maxn], b[maxn], node, y_map[maxn];

vector<int> neiL[maxn], neiR[maxn];

long long dis[maxn];

vector<pair<int, long long>> adj[maxn];


map<pair<int, int>, int> nodes;
vector<int> all_y;

void devide(int idx)


{
memset(a, -1, sizeof(a));
memset(b, -1, sizeof(b));

vector<pair<pair<int, int>, int>> sky;


vector<pair<int, int>> st;

st.pb(mp(H[idx], idx));
for (int i = idx - 1; i >= 0; i--)
{
if (H[i] > st.back().first)
st.push_back(mp(H[i], i));
for (int j = 0; j < neiL[i].size(); j++)
{
int cur = neiL[i][j];
if (R[cur] > idx)
{
int pos = upper_bound(st.begin(), st.end(),
mp(Y[cur], -1)) - st.begin();
a[cur] = st[pos].second;
}
}
}
st.clear();
st.pb(mp(H[idx], idx));
for (int i = idx + 1; i < n; i++)
{
if (H[i] > st.back().first)
st.push_back(mp(H[i], i));
for (int j = 0; j < neiR[i].size(); j++)
{
int cur = neiR[i][j];
if (L[cur] < idx)
{
int pos = upper_bound(st.begin(), st.end(),
mp(Y[cur], -1)) - st.begin();
b[cur] = st[pos].second;
}
}
}

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


{
if (a[i] != -1)
{
if (a[i] != L[i])
sky.pb(mp(mp(L[i], a[i]), Y[i]));
if (a[i] != b[i])
sky.pb(mp(mp(a[i], b[i]), Y[i]));
if (b[i] != R[i])
sky.pb(mp(mp(b[i], R[i]), Y[i]));
}
else
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 99

sky.pb(mp(mp(L[i], R[i]), Y[i]));


}

for (int i = 0; i < n; i++)


{
neiL[i].clear();
neiR[i].clear();
}

m = sky.size();
for (int i = 0; i < m; i++)
{
L[i] = sky[i].first.first;
R[i] = sky[i].first.second;
Y[i] = sky[i].second;
neiL[L[i]].pb(i);
neiR[R[i]].pb(i);
}
}

long long dijkstra(int source, int sink)


{
set<pair<long long, int>> S;
for (int i = 0; i <= node; i++)
dis[i] = inf;
dis[source] = 0;
S.insert(mp(0, source));
while (S.size())
{
set<pair<long long, int>>::iterator it = S.begin();
long long dist = ( *it).first;
int v = ( *it).second;
S.erase(it);
for (int i = 0; i < adj[v].size(); i++)
{
int u = adj[v][i].first;
long long w = adj[v][i].second;
if (dist + w < dis[u])
{
S.erase(mp(dis[u], u));
dis[u] = dist + w;
S.insert(mp(dis[u], u));
}
}
}

if (dis[sink] == inf)
return -1;
return dis[sink];
}

int add_map(int x, int y)


{
if (!nodes[mp(x, y)])
nodes[mp(x, y)] = ++node;
return nodes[mp(x, y)];
}

void add_edge(int x1, int y1, int x2, int y2)


{
int u = add_map(x1, y1);
int v = add_map(x2, y2);

if (x1 == x2)
{
adj[u].push_back(mp(v, abs(y2 - y1)));
adj[v].push_back(mp(u, abs(y2 - y1)));
}
else
{
adj[u].push_back(mp(v, abs(x2 - x1)));
adj[v].push_back(mp(u, abs(x2 - x1)));
}
}

void build_graph()
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 100

{
set<int> line;
line.insert(0);
set<int>::iterator it;
map<int, int> st;
vector<int> set_res;

memset(y_map, -1, sizeof(y_map));

for (int i = 0; i < n; i++)


{
for (int j = 0; j < neiL[i].size(); j++)
{
int cur = neiL[i][j];
line.insert(-Y[cur]);
}

for (int j = 0; j < neiL[i].size(); j++)


{
int cur = neiL[i][j];
it = line.upper_bound(-Y[cur]);
int y2 = Y[cur];
int y1 = -( *it);
int y2_id = lower_bound(all_y.begin(), all_y.end(), y2) -
all_y.begin() + 1;
int y1_id = lower_bound(all_y.begin(), all_y.end(), y1) -
all_y.begin() + 1;
set_res.pb(y1_id);
set_res.pb(y2_id);
add_edge(X[i], y1, X[i], y2);
if (y_map[y1_id] != -1 && y1 != 0)
{
add_edge(X[i], y1, y_map[y1_id], y1);
}
}

for (int j = 0; j < neiR[i].size(); j++)


{
int cur = neiR[i][j];
it = line.upper_bound(-Y[cur]);
int y2 = Y[cur];
int y1 = -( *it);
int y2_id = lower_bound(all_y.begin(), all_y.end(), y2) -
all_y.begin() + 1;
int y1_id = lower_bound(all_y.begin(), all_y.end(), y1) -
all_y.begin() + 1;
set_res.pb(y1_id);
set_res.pb(y2_id);
add_edge(X[i], y1, X[i], y2);
add_edge(X[i], y2, y_map[y2_id], y2);
if (y_map[y1_id] != -1 && y1 != 0)
{
add_edge(X[i], y1, y_map[y1_id], y1);
}
}

for (int j = 0; j < set_res.size(); j++)


y_map[set_res[j]] = X[i];
set_res.clear();

for (int j = 0; j < neiR[i].size(); j++)


{
int cur = neiR[i][j];
int y2 = lower_bound(all_y.begin(), all_y.end(), Y[cur]) -
all_y.begin() + 1;
y_map[y2] = -1;
line.erase(-Y[cur]);
}

for (int j = 0; j < neiL[i].size(); j++)


{
int cur = neiL[i][j];
int y2 = lower_bound(all_y.begin(), all_y.end(), Y[cur]) -
all_y.begin() + 1;
y_map[y2] = X[i];
line.insert(-Y[cur]);
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 101

}
}
}

bool cmp(pair<int, pair<int, int> > aa, pair<int, pair<int, int> > bb)
{
if(aa.first < bb.first)
return true;
if(aa.first > bb.first)
return false;
return aa.second.first < bb.second.first;

void init()
{
vector<pair<int, pair<int, int> > > tmp, nw;
for(int i = 0; i < m; i++)
tmp.push_back(mp(Y[i], mp(L[i], R[i])));

sort(tmp.begin(), tmp.end(), cmp);

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


{
if(tmp[i].second.first == tmp[i].second.second) continue;
int j = i;
int hei = tmp[i].first;
int lo = tmp[i].second.first;
int hi = tmp[i].second.second;
while(tmp[j].first == hei && tmp[j].second.first <= hi &&
tmp[j].second.first >= lo)
{
hi = max(hi, tmp[j].second.second);
j++;
}
nw.push_back(mp(hei, mp(lo, hi)));
i = j - 1;
}

m = nw.size();
for (int i = 0; i < m; i++)
{
L[i] = nw[i].second.first;
R[i] = nw[i].second.second;
Y[i] = nw[i].first;
neiL[L[i]].pb(i);
neiR[R[i]].pb(i);
}

long long min_distance(vector<int> XX, vector<int> HH, vector<int> LL,


vector<int> RR, vector<int> YY, int S, int G)
{
n = XX.size();
m = LL.size();
for (int i = 0; i < n; i++)
{
X[i] = XX[i];
H[i] = HH[i];
}

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


{
L[i] = LL[i];
R[i] = RR[i];
Y[i] = YY[i];
all_y.push_back(Y[i]);
}

init();
all_y.push_back(0);

sort(all_y.begin(), all_y.end());

all_y.resize(unique(all_y.begin(), all_y.end()) - all_y.begin());


CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 102

devide(S);
devide(G);
build_graph();
return dijkstra(add_map(X[S], 0), add_map(X[G], 0));
}

//-------------- start grader ------------------

int main()
{
std::freopen("../tests/5-30.in", "r", stdin) ;
std::freopen("walking.out", "w", stdout) ;

auto t1 = clock();

int n, m;
assert(2 == scanf("%d%d", &n, &m));
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<int> x(n), h(n);


for (int i = 0; i < n; i++)
assert(2 == scanf("%d%d", &x[i], &h[i]));

vector<int> l(m), r(m), y(m);


for (int i = 0; i < m; i++)
assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
int s, g;
assert(2 == scanf("%d%d", &s, &g));
fclose(stdin);

auto t2 = clock();

long long result = min_distance(x, h, l, r, y, s, g);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//-------------- end grader ------------------

Listing 1.6.9: walk 143257 Benq+grader.cpp


// https://oj.uz/submission/143257

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef long double ld;
typedef complex<ld> cd;

typedef pair<int, int> pi;


typedef pair<ll,ll> pl;
typedef pair<ld,ld> pd;

typedef vector<int> vi;


typedef vector<ld> vd;
typedef vector<ll> vl;
typedef vector<pi> vpi;
typedef vector<pl> vpl;
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 103

typedef vector<cd> vcd;

#define FOR(i, a, b) for (int i = (a); i < (b); i++)


#define F0R(i, a) for (int i = 0; i < (a); i++)
#define FORd(i,a,b) for (int i = (b)-1; i >= (a); i--)
#define F0Rd(i,a) for (int i = (a)-1; i >= 0; i--)
#define trav(a, x) for (auto& a : x)

#define mp make_pair
#define pb push_back
#define f first
#define s second
#define lb lower_bound
#define ub upper_bound

#define sz(x) (int)x.size()


#define all(x) begin(x), end(x)
#define rsz resize

const int MOD = 1000000007; // 998244353


const ll INF = 1e18;
const int MX = 200005;
const ld PI = 4*atan((ld)1);

template<class T> void ckmin(T &a, T b) { a = min(a, b); }


template<class T> void ckmax(T &a, T b) { a = max(a, b); }

namespace input
{
template<class T> void re(complex<T>& x);
template<class T1, class T2> void re(pair<T1,T2>& p);
template<class T> void re(vector<T>& a);
template<class T, size_t SZ> void re(array<T,SZ>& a);

template<class T> void re(T& x) { cin >> x; }


void re(double& x) { string t; re(t); x = stod(t); }
void re(ld& x) { string t; re(t); x = stold(t); }
template<class Arg, class... Args> void re(Arg& first, Args&... rest)
{
re(first); re(rest...);
}

template<class T> void re(complex<T>& x)


{
T a,b; re(a,b);
x = cd(a,b);
}
template<class T1, class T2> void re(pair<T1,T2>& p) { re(p.f,p.s); }
template<class T> void re(vector<T>& a) { F0R(i,sz(a)) re(a[i]); }
template<class T, size_t SZ> void re(array<T,SZ>& a)
{
F0R(i,SZ) re(a[i]);
}
}

using namespace input;

namespace output
{
template<class T1, class T2> void pr(const pair<T1,T2>& x);
template<class T, size_t SZ> void pr(const array<T,SZ>& x);
template<class T> void pr(const vector<T>& x);
template<class T> void pr(const set<T>& x);
template<class T1, class T2> void pr(const map<T1,T2>& x);

template<class T> void pr(const T& x) { cout << x; }


template<class Arg, class... Args> void pr(const Arg& first,
const Args&... rest)
{
pr(first); pr(rest...);
}

template<class T1, class T2> void pr(const pair<T1,T2>& x)


{
pr("{",x.f,", ",x.s,"}");
}
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 104

template<class T> void prContain(const T& x)


{
pr("{");
bool fst = 1;
for (const auto& a: x)
pr(!fst?", ":"",a), fst = 0; // const needed for vector<bool>
pr("}");
}

template<class T, size_t SZ> void pr(const array<T,SZ>& x)


{
prContain(x);
}

template<class T> void pr(const vector<T>& x) { prContain(x); }


template<class T> void pr(const set<T>& x) { prContain(x); }

template<class T1, class T2> void pr(const map<T1,T2>& x)


{
prContain(x);
}

void ps() { pr("\n"); }

template<class Arg> void ps(const Arg& first)


{
pr(first); ps(); // no space at end of line
}

template<class Arg, class... Args> void ps(const Arg& first,


const Args&... rest)
{
pr(first," "); ps(rest...); // print w/ spaces
}
}

using namespace output;

namespace io
{
void setIn(string s) { freopen(s.c_str(),"r",stdin); }
void setOut(string s) { freopen(s.c_str(),"w",stdout); }

void setIO(string s = "")


{
ios_base::sync_with_stdio(0); cin.tie(0); // fast I/O
if (sz(s)) { setIn(s+".in"), setOut(s+".out"); } // for USACO
}
}

using namespace io;

template<class T> T invGeneral(T a, T b)


{
a %= b; if (a == 0) return b == 1 ? 0 : -1;
T x = invGeneral(b,a);
return x == -1 ? -1 : ((1-(ll)b*x)/a+b)%b;
}

template<class T> struct modular


{
T val;
explicit operator T() const { return val; }
modular() { val = 0; }

modular(const ll& v)
{
val = (-MOD <= v && v <= MOD) ? v : v % MOD;
if (val < 0) val += MOD;
}

friend void pr(const modular& a) { pr(a.val); }


friend void re(modular& a) { ll x; re(x); a = modular(x); }

friend bool operator==(const modular& a, const modular& b)


CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 105

{
return a.val == b.val;
}

friend bool operator!=(const modular& a, const modular& b)


{
return !(a == b);
}

friend bool operator<(const modular& a, const modular& b)


{
return a.val < b.val;
}

modular operator-() const { return modular(-val); }

modular& operator+=(const modular& m)


{
if ((val += m.val) >= MOD)
val -= MOD;
return *this;
}

modular& operator-=(const modular& m)


{
if ((val -= m.val) < 0)
val += MOD;
return *this;
}

modular& operator*=(const modular& m)


{
val = (ll)val*m.val%MOD;
return *this;
}

friend modular pow(modular a, ll p)


{
modular ans = 1;
for (; p; p /= 2, a *= a)
if (p&1)
ans *= a;
return ans;
}

friend modular inv(const modular& a)


{
auto i = invGeneral(a.val,MOD); assert(i != -1);
return i;
} // equivalent to return exp(b,MOD-2) if MOD is prime

modular& operator/=(const modular& m) { return ( *this) *= inv(m); }

friend modular operator+(modular a, const modular& b) {return a += b;}


friend modular operator-(modular a, const modular& b) {return a -= b;}
friend modular operator*(modular a, const modular& b) {return a *= b;}

friend modular operator/(modular a, const modular& b) {return a /= b;}


};

typedef modular<int> mi;


typedef pair<mi,mi> pmi;
typedef vector<mi> vmi;
typedef vector<pmi> vpmi;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<class T> using pqg = priority_queue<T,vector<T>,greater<T>>;

template<class T> T poll(pqg<T>& x)


{
T y = x.top(); x.pop();
return y;
}

int nex;
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 106

template<int SZ> struct Dijkstra


{
ll dist[SZ];
vpi adj[SZ];

void addEdge(int A, int B, int C)


{
adj[A].pb({B,C}); adj[B].pb({A,C}); assert(C >= 0);
// if undirected
}

void init(int st)


{
fill_n(dist,SZ,INF);
pqg<pl> q;
q.push({dist[st] = 0,st});
while (sz(q))
{
auto x = poll(q);
if (dist[x.s] < x.f) continue;
trav(y,adj[x.s])
if (x.f+y.s < dist[y.f])
q.push({dist[y.f] = x.f+y.s,y.f});
}
}
};

Dijkstra<10000000> D;

int N, M, S, G;
vi X,H;
vector<pair<int,vi>> bridge;
vi xx;

void split(int h, vi& z, set<int>& cur, int x)


{
vi Z;
F0R(i,sz(z)-1)
{
Z.pb(z[i]);
if (z[i] < x && x < z[i+1])
{
if (H[x] >= h) Z.pb(x);
else
{
auto it = cur.lb(x); assert( *it != x);
if (z[i] < *prev(it)) Z.pb( *prev(it));
if ( *it < z[i+1]) Z.pb( *it);
}
}
}

Z.pb(z.back());
swap(z,Z);
}

vpi cor[500000];
map<int,pi> mm;
vector<pair<int,pi>> BRIDGE;

int makeVert(int seg, int x)


{
cor[seg].pb({x,nex++});
return nex-1;
}

void tri(int seg, int label, int x)


{
auto it = mm.ub(x); if (it == begin(mm) || prev(it)->s.f < x) return;
int SEG = prev(it)->s.s; if (BRIDGE[SEG].f > H[x]) return;
int LABEL = makeVert(SEG,x);
D.addEdge(label,LABEL,abs(BRIDGE[seg].f-BRIDGE[SEG].f));
}

void ins(int seg)


CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 107

{
int l = BRIDGE[seg].s.f, r = BRIDGE[seg].s.s;
while (1)
{
auto it = mm.lb(l);
if (it != begin(mm) && prev(it)->s.f >= l) it --;
if (it == end(mm) || it->f > r) break;
auto IT = *it; mm.erase(it);
if (IT.f < l) mm[IT.f] = {l-1,IT.s.s};
if (IT.s.f > r) mm[r+1] = {IT.s.f,IT.s.s};
}
mm[l] = {r,seg};
}

ll min_distance(vi x, vi h, vi l, vi r, vi y, int s, int g)


{
N = sz(x), M = sz(l); if (s > g) swap(s,g);
X = x, H = h, S = s, G = g;
F0R(i,sz(l)) bridge.pb({y[i],{l[i],r[i]}});

sort(bridge.rbegin(),bridge.rend());

F0R(i,N) xx.pb(i);

sort(all(xx),[](int a, int b) { return H[a] > H[b]; });

int ind = 0; set<int> cur;

trav(z,bridge)
{
while (ind < sz(xx) && H[xx[ind]] >= z.f) cur.insert(xx[ind++]);
// ps("??",z.f,cur);
split(z.f,z.s,cur,S);
split(z.f,z.s,cur,G);
}

trav(z,bridge) F0R(i,sz(z.s)-1) BRIDGE.pb({z.f,{z.s[i],z.s[i+1]}});


BRIDGE.pb({0,{S,S}}); BRIDGE.pb({0,{G,G}});
vi special;
F0R(i,sz(BRIDGE))
{
cor[i] = {{BRIDGE[i].s.f,2*i},{BRIDGE[i].s.s,2*i+1}};
if (BRIDGE[i].f == 0) special.pb(2*i);
}

nex = 2*sz(BRIDGE);
F0R(i,sz(BRIDGE))
{
// ps("HUH",i,BRIDGE[i]);
tri(i,2*i,BRIDGE[i].s.f);
tri(i,2*i+1,BRIDGE[i].s.s);
ins(i);
}

mm.clear();
F0Rd(i,sz(BRIDGE))
{
tri(i,2*i,BRIDGE[i].s.f);
tri(i,2*i+1,BRIDGE[i].s.s);
ins(i);
}

// add bridge edges


F0R(i,sz(BRIDGE))
{
sort(all(cor[i]));
F0R(j,sz(cor[i])-1)
D.addEdge(cor[i][j].s,
cor[i][j+1].s,
X[cor[i][j+1].f]-X[cor[i][j].f]);
}

D.init(special[0]);
auto res = D.dist[special[1]];
if (res == INF) res = -1;
return res;
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 108

// at most M*5 bridges now


// for each endpoint of every bridge:
// add to next thing above it or below it
}

//-------------- start grader ------------------

int main()
{
std::freopen("../tests/5-30.in", "r", stdin) ; // execution time : 6.447 s
std::freopen("walking.out", "w", stdout) ;

auto t1 = clock();

int n, m;
assert(2 == scanf("%d%d", &n, &m));
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<int> x(n), h(n);


for (int i = 0; i < n; i++)
assert(2 == scanf("%d%d", &x[i], &h[i]));

vector<int> l(m), r(m), y(m);


for (int i = 0; i < m; i++)
assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
int s, g;
assert(2 == scanf("%d%d", &s, &g));
fclose(stdin);

auto t2 = clock();

long long result = min_distance(x, h, l, r, y, s, g);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//-------------- end grader ------------------

Listing 1.6.10: walk 147051+grader.cpp


// https://oj.uz/submission/147051

#include "../walk.h"
#include <bits/stdc++.h>

#define sz(v) ((int)(v).size())

using namespace std;

using lint = long long;


using pi = pair<lint, int>;

const int MAXN = 100005;


const int MAXV = 2000005;

struct intv
{
int s, e, x;
bool operator<(const intv &i) const
{
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 109

return x < i.x;


}
};

struct point
{
int x, y, idx;
};

int n, m;
vector<int> witness[MAXN];
vector<pi> event[MAXN];
vector<pi> gph[MAXV];
lint dist[MAXV];

lint dijkstra(int s, int e)


{
priority_queue<pi, vector<pi>, greater<pi> > pq;
memset(dist, 0x3f, sizeof(dist));
dist[s] = 0;
pq.emplace(0, s);
while(!pq.empty())
{
auto x = pq.top();
pq.pop();
if(dist[x.second] != x.first) continue;
for(auto &j : gph[x.second])
{
if(dist[j.second] > x.first + j.first)
{
dist[j.second] = x.first + j.first;
pq.emplace(dist[j.second], j.second);
}
}
}

if(dist[e] > 1e17) return -1;


return dist[e];
}

void add_edge(point x, point y)


{
// printf("(%d, %d) <-> (%d, %d)\n", x.x, x.y, y.x, y.y);
int dist = abs(x.x - y.x) + abs(x.y - y.y);
gph[x.idx].emplace_back(dist, y.idx);
gph[y.idx].emplace_back(dist, x.idx);
}

void make_vertex(vector<intv> v)
{
for(auto &i : v)
{
event[i.s].emplace_back(i.x, +1);
event[i.e+1].emplace_back(i.x, -1);
}
multiset<int> swp;
for(int i=0; i<n; i++)
{
for(auto &j : event[i])
{
if(j.second == +1) swp.insert(j.first);
else swp.erase(swp.find(j.first));
}

vector<int> nxt = witness[i];

for(auto &j : witness[i])


{
if(j == 0) continue;
auto l = swp.upper_bound(j);
if(l != swp.end()) nxt.push_back( *l);
l = swp.lower_bound(j);
if(l != swp.begin()) nxt.push_back( *prev(l));
}

sort(nxt.begin(), nxt.end());
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 110

nxt.resize(unique(nxt.begin(), nxt.end()) - nxt.begin());


witness[i] = nxt;
}
}

long long min_distance(vector<int> x, vector<int> h,


vector<int> l, vector<int> r,
vector<int> y, int s, int e)
{
n = sz(x);
m = sz(l);
for(int i=0; i<m; i++)
{
witness[l[i]].push_back(y[i]);
witness[r[i]].push_back(y[i]);
}

witness[s].push_back(0);
witness[e].push_back(0);

vector<pi> points;
vector<intv> hors;

set<int> alive;
for(int i=0; i<n; i++)
{
points.emplace_back(h[i], i);
alive.insert(i);
}

for(int i=0; i<m; i++) hors.push_back({l[i], r[i], y[i]});

sort(points.begin(), points.end());
sort(hors.begin(), hors.end());

int ptr = 0;
for(auto &i : hors)
{
while(ptr < sz(points) && points[ptr].first < i.x)
{
alive.erase(points[ptr++].second);
}

if(i.s <= s && s <= i.e)


{
auto it = alive.lower_bound(s);
witness[ *it ].push_back(i.x);
if( *it != s) witness[ *prev(it) ].push_back(i.x);
}

if(i.s <= e && e <= i.e)


{
auto it = alive.lower_bound(e);
witness[ *it ].push_back(i.x);
if( *it != e) witness[ *prev(it) ].push_back(i.x);
}
}

make_vertex(hors);
vector<point> ans;
for(int i=0; i<n; i++)
{
for(auto &j : witness[i])
{
int num = ans.size();
ans.push_back({x[i], j, num});
}
}

assert(sz(ans) < MAXV);


auto cmpx = [&](const point &x, const point &y)
{ return pi(x.x, x.y) < pi(y.x, y.y); };
auto cmpy = [&](const point &x, const point &y)
{ return pi(x.y, x.x) < pi(y.y, y.x); };
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 111

sort(ans.begin(), ans.end(), cmpx);

for(int i=0; i<n; i++)


{
int st = lower_bound(ans.begin(), ans.end(),
(point){x[i], 0, -1}, cmpx) - ans.begin();
int ed = upper_bound(ans.begin(), ans.end(),
(point){x[i], h[i], -1}, cmpx) - ans.begin();
for(int j=st+1; j<ed; j++){
add_edge(ans[j-1], ans[j]);
}
}
sort(ans.begin(), ans.end(), cmpy);
for(int i=0; i<m; i++)
{
int st = lower_bound(ans.begin(), ans.end(),
(point){x[l[i]], y[i], -1}, cmpy) - ans.begin();
int ed = upper_bound(ans.begin(), ans.end(),
(point){x[r[i]], y[i], -1}, cmpy) - ans.begin();
for(int j=st+1; j<ed; j++){
add_edge(ans[j-1], ans[j]);
}
}
s = lower_bound(ans.begin(), ans.end(),
(point){x[s], 0, -1}, cmpy)->idx;
e = lower_bound(ans.begin(), ans.end(),
(point){x[e], 0, -1}, cmpy)->idx;
return dijkstra(s, e);
}

//-------------- start grader ------------------

int main()
{
std::freopen("../tests/5-30.in", "r", stdin) ; // execution time : 5.501 s
std::freopen("walking.out", "w", stdout) ;

auto t1 = clock();

int n, m;
assert(2 == scanf("%d%d", &n, &m));
cerr<<"cerr : "<<n<<" "<<m<<"\n";

vector<int> x(n), h(n);


for (int i = 0; i < n; i++)
assert(2 == scanf("%d%d", &x[i], &h[i]));

vector<int> l(m), r(m), y(m);


for (int i = 0; i < m; i++)
assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
int s, g;
assert(2 == scanf("%d%d", &s, &g));
fclose(stdin);

auto t2 = clock();

long long result = min_distance(x, h, l, r, y, s, g);

auto t3 = clock();

printf("%lld\n", result);
fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"result = "<<result<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
CAPITOLUL 1. IOI 2019 1.6. SKY WALKING 112

//-------------- end grader ------------------

1.6.3 *Rezolvare detaliată


Capitolul 2
30
IOI 2018

2.1 Combo
Problema 1 - Combo 100 de puncte

Author: Ammar Fathin Sabili (Indonesia)

Vă jucaţi un joc de acţiune. Controlerul jocului are 4 butoane, A, B, X, şi Y . În acest joc
acumulaţi monede cu diferite combo-uri. Puteţi obţine un combo apăsând o secvenţă de butoane.
Jocul are o secvenţă secretă a butoanelor, care poate fi reprezentată ca o secvenţă de caractere
S formată cu acele 4 caractere. Nu cunoaşteţi secvenţa S, dar ı̂i ştiţi lungimea N .
Ştiţi de asemenea că primul caracter al lui S nu reapare nicăieri ı̂n acesta. De
exemplu, S poate fi ”ABXYY” sau ”XYYAA”, dar nu poate fi ”AAAAA” sau ”BXYBX ”.
Puteţi apăsa o secvenţă de cel mult 4N butoane pentru a forma un combo. Fie p secvenţa de
caractere care reprezintă secvenţa de butoane apăsate. Numărul de monede pe care le veţi primi
pentru acest combo este calculat ca lungimea celui mai lung prefix al lui S care este şi subsecvenţă
a lui p.
O subsecvenţă a unei secvenţe de caractere t este o secvenţă (posibil vidă) de caractere aflate
pe poziţii consecutive ı̂n t.
Un prefix al lui t este o subsecvenţă a lui t care fie este vidă, fie conţine primul caracter din t.
De exemplu, dacă S este ”ABXYY” şi p este ”XXYYABYABXAY”, veţi primi 3 monede
deoarece ”ABX” este cel mai lung prefix al lui S care este şi subsecvenţă a lui p.
Sarcina voastră este să determinaţi secvenţa secretă de caractere S utilizând puţine combo-uri.

Detalii de implementare

Trebuie să implementaţi următoarea funcţie:


string guess_sequence(int N)
a N : lungimea secvenţei.
a Această funcţie este apelată exact o dată pentru fiecare test.
a Această funcţie trebuie să ı̂ntoarcă secvenţa S.
Programul vostru poate apela următoarea funcţie:
int press(string p)
p: o secvenţă de butoane pe care le apăsaţi.
a
p trebuie să fie o secvenţă de caractere cu lungimea cuprinsă intre 0 şi 4N inclusiv. Fiecare
a
caracter din p trebuie să fie ori A, ori B, ori X ori Y .
a Nu puteţi apela această funcţie de mai mult de 8000 de ori pentru fiecare test.

30
argint: Alex Tatomir, Nicolae Bălcescu (Brăila),
. argint: Tiberiu-Ioan Muşat, Tudor Vianu (Bucureşti)
. argint: Ştefan Constantin-Buliga, CNI ”Tudor Vianu” Bucureşti
. bronz: Costin-Andrei Oncescu, Dinicu Golescu (Campulung).

113
CAPITOLUL 2. IOI 2018 2.1. COMBO 114

a Această funcţie va ı̂ntoarce numărul de monede pe care le veţi primi dacă apăsaţi secvenţa
de butoane reprezentată de secvenţa p.
Dacă vreuna din condiţiile de mai sus nu este satisfăcută, programul vostru va fi evaluat ca
Wrong answer. Altfel, programul vostru va fi evaluat ca Accepted, iar punctajul vostru va fi
calculat după numărul de apeluri ale funcţiei press (vezi Subtask-uri).

Exemple

Fie secvenţa S egală cu ”ABXYY”. Grader-ul apelează guess_sequence(5). Un exemplu


de comunicare este prezentat mai jos.

Apel Întoarcere
press(”XXYYABYABXAY”) 3
press(”ABXYY”) 5
press(”ABXYYABXYY”) 5
press(””) 0
press(”X”) 0
press(”BXYY”) 0
press(”YYXBA”) 1
press(”AY”) 1

Tabelul 2.1: Combo

Pentru primul apel al funcţiei press, ”ABX” apare ı̂n ”XXYYABYABXAY” ca subsecvenţă,
dar ”ABXY” nu apare, deci se va ı̂ntoarce 3.
Pentru al treilea apel al funcţie press, ”ABXYY” apare ı̂n ”ABXYYABXYY” ca subsecvenţă,
deci se va ı̂ntoarce 5.
Pentru al şaselea apel al funcţiei press, niciun prefix de-al secvenţei ”ABXYY” ı̂n afară de
subsecvenţa vidă nu apare ı̂n ”BXYY” ca subsecvenţă, deci se va ı̂ntoarce 0.
La sfârşit, guess_sequence(5) ar trebui să ı̂ntoarcă ”ABXYY”.
Fişierul sample-01-in.txt din pachetul arhivat anexat corespunde acestui exemplu.

Restricţii

a 1 & N & 2000


a Fiecare caracter al secvenţei S este ori A, ori B, ori X ori Y .
a Primul caracter al lui S nu reapare ı̂n S.
În această problemă, grader-ul NU este adaptiv. Asta ı̂nseamnă că S este fixat la ı̂nceputul
rulării grader-ului şi nu depinde de ı̂ntrebarile puse de programul vostru.

Subtaskuri

1. (5 puncte) N 3
2. (95 de puncte) Fără constrângeri adiţionale. Pentru acest subtask punctajul vostru este
calculat după cum urmează. Fie q numărul de apeluri ale funcţiei press.
` Dacă q & N  2, punctajul vostru este 95.
` Dacă N  2 $ q & N  10, punctajul vostru este 95  3 q  N  2.
` Dacă N  10 $ q & 2N  1, punctajul vostru este 25.
` Dacă maxrN  10, 2N  1x $ q & 4N , punctajul vostru este 5.
` Altfel, punctajul vostru este 0.

Luaţi la cunoştinţă că punctajul pentru un subtask este minimul dintre punctajele testelor
care alcătuiesc acel subtask.

Exemplu de grader
CAPITOLUL 2. IOI 2018 2.1. COMBO 115

Grader-ul local citeşte datele de intrare ı̂n următoarea formă:


a linia 1: S

Dacă programul vostru este evaluat ca Accepted, atunci grader-ul local va afişa Accepted:
q unde q va reprezenta numărul de apeluri ale funcţiei press.
Daca programul vostru este evaluat ca Wrong answer, atunci grader-ul local va afişa
Wrong answer: M SG. Semnificaţia lui M SG este după cum urmează:
a invalid press: O valoare greşită a lui p a fost folosită ı̂n apelul funcţiei press. Mai
exact, lungimea lui p nu este ı̂ntre 0 şi 4N inclusiv, sau un caracter din p nu este ori A, ori B, ori
X ori Y .
a too many moves: Funcţia press a fost apelată de mai mult de de ori.
a wrong guess: Valoarea ı̂ntoarsă de funcţia guess_sequence nu este S.

2.1.1 Indicaţii de rezolvare

There is a secret string S which consists of four characters ’A’, ’B’, ’X’, or ’Y’. The first character
of S never reappears in S. You know only N , the length of S.
You can ask a query as follows:
ˆ You specify a string of the above four characters whose length is not larger than 4N .
ˆ You get the maximum length of the prefixes of S which are also substrings of the specified
string.

Your task is to determine S.

Subtasks and Solutions


Let Q be the number of asks.
N=3
ˆ Ask by all possible strings until N is returned.

Q=4N
ˆ Determine the characters of S from the beginning one by one.
ˆ For each position, try by all four characters one by one.

Q=2N+1 or 2N
ˆ Determine the characters of S from the beginning one by one.
ˆ For the first position, determine the character by three asks. For each position except the
first one, considering the constraint of S, determine the character by two asks.
ˆ Or for each position, using conditional branches, determine the character by two asks.

Q=N+2
ˆ Determine the characters of S from the beginning one by one.
ˆ Except the first or last position, determine the character by one ask as follows.
` Assume that the first character of S is, for example, ’A’.
` Let s be the prefix of S already known.
` Ask by s+’B’+s+’XB’+s+’XX’+s+’XY’.
` If the next character is ’B’, then ¶s¶  1 is returned. If it is ’X’, then ¶s¶  2 is returned.
If it is ’Y’, then ¶s¶ is returned.
Note that if Q & N  10, you can get nearly full points according as Q.

2.1.2 Coduri sursă


CAPITOLUL 2. IOI 2018 2.1. COMBO 116

Listing 2.1.1: compile cpp.sh


#!/bin/bash

TASK=combo

g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 2.1.2: combo.h


#include <string>

std::string guess_sequence(int N);

int press(std::string p);

Listing 2.1.3: combo.cpp


#include "combo.h"

std::string guess_sequence(int N)
{
std::string p = "";
for (int i = 0; i < 4 * N; ++i)
{
p += ’A’;
}
int coins = press(p);
std::string S = "";
for (int i = 0; i < N; ++i)
{
S += ’A’;
}
return S;
}

Listing 2.1.4: grader.cpp


#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include "combo.h"

namespace
{

constexpr int MAX_N = 2000;


constexpr int MAX_NUM_MOVES = 8000;

int N;
std::string S;

int num_moves;

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

} // namespace

int press(std::string p)
{
if (++num_moves > MAX_NUM_MOVES)
{
wrong_answer("too many moves");
}
int len = p.length();
if (len > 4 * N)
{
wrong_answer("invalid press");
CAPITOLUL 2. IOI 2018 2.1. COMBO 117

}
for (int i = 0; i < len; ++i)
{
if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
{
wrong_answer("invalid press");
}
}
int coins = 0;
for (int i = 0, j = 0; i < len; ++i)
{
if (j < N && S[j] == p[i])
{
++j;
} else if (S[0] == p[i])
{
j = 1;
}
else
{
j = 0;
}
coins = std::max(coins, j);
}
return coins;
}

int main()
{
char buffer[MAX_N + 1];
if (scanf("%s", buffer) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
S = buffer;
N = S.length();

num_moves = 0;
std::string answer = guess_sequence(N);
if (answer != S)
{
wrong_answer("wrong guess");
exit(0);
}
printf("Accepted: %d\n", num_moves);
return 0;
}

Listing 2.1.5: combo-model.cpp


// https://oj.uz/submission/75294 (36 ms C++17)

#include <algorithm>
#include <string>
//#include "combo.h"

using namespace std;

namespace
{
constexpr int MAX_N = 2000;
constexpr int MAX_NUM_MOVES = 8000;

int N;
std::string S;
int num_moves;

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}
} // namespace
CAPITOLUL 2. IOI 2018 2.1. COMBO 118

int press(std::string p)
{
if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }

int len = p.length();


if (len > 4 * N) { wrong_answer("invalid press"); }

for (int i = 0; i < len; ++i)


{
if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
{
wrong_answer("invalid press");
}
}
int coins = 0;
for (int i = 0, j = 0; i < len; ++i)
{
if (j < N && S[j] == p[i]) { ++j; }
else if (S[0] == p[i]) { j = 1; }
else { j = 0; }
coins = std::max(coins, j);
}
return coins;
}

string guess_sequence(int N)
{
string p = "";
if(press("AB") >= 1) p = press("A") ? "A" : "B";
else p = press("X") ? "X" : "Y";

vector<char> chr = {’A’, ’B’, ’X’, ’Y’};


chr.erase(find(chr.begin(), chr.end(), p[0]));

while((int)p.size() <= N - 2)
{
int query = press(p+chr[0]+chr[0]+p+chr[0]+chr[1]+p+chr[1]+chr[0]);
if(query == p.size())
{
p.push_back(chr[2]);
}
else
if(query == p.size() + 1)
{
query = press(p + chr[1] + chr[2]);
if(query == p.size()) p = p + chr[0] + chr[2];
else if(query == p.size() + 1) p = p + chr[1] + chr[1];
else p = p + chr[1] + chr[2];
}
else
{
query = press(p + chr[0] + chr[1]);
if(query == p.size()) p = p + chr[1] + chr[0];
else if(query == p.size() + 1) p = p + chr[0] + chr[0];
else p = p + chr[0] + chr[1];
}
}
while(p.size() != N)
{
if(press(p + chr[0]) == p.size() + 1) p.push_back(chr[0]);
else if(press(p + chr[1]) == p.size() + 1) p.push_back(chr[1]);
else p.push_back(chr[2]);
}
return p;
}

int main()
{
std::freopen("../in/02-077.txt", "r", stdin);
//std::freopen("combo.out", "w", stdout) ;

char buffer[MAX_N + 1];


if (scanf("%s", buffer) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
CAPITOLUL 2. IOI 2018 2.1. COMBO 119

}
S = buffer;
N = S.length();

num_moves = 0;
std::string answer = guess_sequence(N); // functia de implementat ... !!!
if (answer != S)
{
wrong_answer("wrong guess");
exit(0);
}
printf("Accepted: num_moves=%d N=%d\n", num_moves,N);
return 0;
}

Listing 2.1.6: combo 75294.cpp


// https://oj.uz/submission/75294

#include <algorithm>
#include <string>
#include "combo.h"

using namespace std;

namespace
{
constexpr int MAX_N = 2000;
constexpr int MAX_NUM_MOVES = 8000;

int N;
std::string S;
int num_moves;

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}
}

int press(std::string p)
{
if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }

int len = p.length();


if (len > 4 * N) { wrong_answer("invalid press"); }

for (int i = 0; i < len; ++i)


{
if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
{
wrong_answer("invalid press");
}
}

int coins = 0;
for (int i = 0, j = 0; i < len; ++i)
{
if (j < N && S[j] == p[i]) { ++j; }
else if (S[0] == p[i]) { j = 1; }
else { j = 0; }
coins = std::max(coins, j);
}

return coins;
}

string guess_sequence(int N)
{
string p = "";
if(press("AB") >= 1) p = press("A") ? "A" : "B";
else p = press("X") ? "X" : "Y";

vector<char> chr = {’A’, ’B’, ’X’, ’Y’};


CAPITOLUL 2. IOI 2018 2.1. COMBO 120

chr.erase(find(chr.begin(), chr.end(), p[0]));

while((int)p.size() <= N - 2)
{
int query = press(p+chr[0]+chr[0]+p+chr[0]+chr[1]+p+chr[1]+chr[0]);
if(query == p.size())
{
p.push_back(chr[2]);
}
else
if(query == p.size() + 1)
{
query = press(p + chr[1] + chr[2]);
if(query == p.size()) p = p + chr[0] + chr[2];
else if(query == p.size() + 1) p = p + chr[1] + chr[1];
else p = p + chr[1] + chr[2];
}
else
{
query = press(p + chr[0] + chr[1]);
if(query == p.size()) p = p + chr[1] + chr[0];
else if(query == p.size() + 1) p = p + chr[0] + chr[0];
else p = p + chr[0] + chr[1];
}
}

while(p.size() != N)
{
if(press(p + chr[0]) == p.size() + 1) p.push_back(chr[0]);
else if(press(p + chr[1]) == p.size() + 1) p.push_back(chr[1]);
else p.push_back(chr[2]);
}

return p;
}

int main()
{
std::freopen("../in/02-077.txt", "r", stdin);
//std::freopen("combo.out", "w", stdout) ;

char buffer[MAX_N + 1];


if (scanf("%s", buffer) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
S = buffer;
N = S.length();

num_moves = 0;

std::string answer = guess_sequence(N);

if (answer != S)
{
wrong_answer("wrong guess");
exit(0);
}

printf("Accepted: num_moves=%d N=%d\n", num_moves,N);


return 0;
}

Listing 2.1.7: combo 75871.cpp


// https://oj.uz/submission/75871

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include "combo.h"

using namespace std;


CAPITOLUL 2. IOI 2018 2.1. COMBO 121

namespace
{
constexpr int MAX_N = 2000;
constexpr int MAX_NUM_MOVES = 8000;

int N;
std::string S;
int num_moves;

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}
}

int press(std::string p)
{
if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }

int len = p.length();


if (len > 4 * N) { wrong_answer("invalid press"); }

for (int i = 0; i < len; ++i)


{
if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
{
wrong_answer("invalid press");
}
}

int coins = 0;
for (int i = 0, j = 0; i < len; ++i)
{
if (j < N && S[j] == p[i]) { ++j; }
else if (S[0] == p[i]) { j = 1; }
else { j = 0; }
coins = std::max(coins, j);
}

return coins;
}

const static char b[5] = "ABXY";

static string r;
static char a[3];

string guess_sequence(int N)
{
int x = press(string("XY"));
int y = press(string("BY"));

r += b[2 * !!x + !!y];


for(int i = 0, j = 0; i < 4; i++) if(i != 2 * !!x + !!y) a[j++] = b[i];

for(int x; r.length() < N - 1; )


{
x = press(r + a[0] + a[0] + r + a[0] + a[1] + r + a[1] + a[0]);
if(x == r.length()){ r += a[2]; continue; }
if(x == r.length() + 1)
{
x = press(r + a[1] + r + a[1] + a[1]);
if(x == r.length()){ r += a[0]; r += a[2]; }
if(x == r.length() + 1){ r += a[1]; r += a[2]; }
if(x == r.length() + 2){ r += a[1]; r += a[1]; }
}
else
{
x = press(r + a[0] + r + a[0] + a[0]);
if(x == r.length()){ r += a[1]; r += a[0]; }
if(x == r.length() + 1){ r += a[0]; r += a[1]; }
if(x == r.length() + 2){ r += a[0]; r += a[0]; }
}
}
CAPITOLUL 2. IOI 2018 2.1. COMBO 122

if(r.length() < N)
{
int x = press(r + "X" + r + "Y");
int y = press(r + "B" + r + "Y");
r += b[2 * (x - r.length()) + (y - r.length())];
}

return r;
}

int main()
{
std::freopen("../in/02-077.txt", "r", stdin) ;
//std::freopen("combo.out", "w", stdout) ;

char buffer[MAX_N + 1];


if (scanf("%s", buffer) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
S = buffer;
N = S.length();

num_moves = 0;

std::string answer = guess_sequence(N);

if (answer != S)
{
wrong_answer("wrong guess");
exit(0);
}

printf("Accepted: num_moves=%d N=%d\n", num_moves,N);


return 0;
}

Listing 2.1.8: combo 76356.cpp


// https://oj.uz/submission/76356

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include "combo.h"

using namespace std;

namespace
{
constexpr int MAX_N = 2000;
constexpr int MAX_NUM_MOVES = 8000;

int N;
std::string S;
int num_moves;

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}
}

int press(std::string p)
{
if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }

int len = p.length();


if (len > 4 * N) { wrong_answer("invalid press"); }

for (int i = 0; i < len; ++i)


CAPITOLUL 2. IOI 2018 2.1. COMBO 123

{
if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
{
wrong_answer("invalid press");
}
}

int coins = 0;
for (int i = 0, j = 0; i < len; ++i)
{
if (j < N && S[j] == p[i]) { ++j; }
else if (S[0] == p[i]) { j = 1; }
else { j = 0; }
coins = std::max(coins, j);
}

return coins;
}

std::string guess_sequence(int N)
{
char first = press("AB") == 0? "YX"[press("X")]:
"BA"[press("A")];

char a, b, c;
if(first == ’A’) a = ’B’, b = ’X’, c = ’Y’;
if(first == ’B’) a = ’A’, b = ’X’, c = ’Y’;
if(first == ’X’) a = ’A’, b = ’B’, c = ’Y’;
if(first == ’Y’) a = ’A’, b = ’B’, c = ’X’;

std::string S(1, first);


while(S.length() + 2 <= N)
{
int coins = press(S + a + a + S + a + b + S + b + a) - S.length();
if (coins == 0)
{
S += c;
}
else
if (coins == 1)
{
coins = press(S + b + c) - S.length();
if (coins == 0)
{
S += a, S += c;
}
else
if (coins == 1)
{
S += b, S += b;
}
else
if (coins == 2)
{
S += b, S += c;
}
}
else
if (coins == 2)
{
coins = press(S + a + b) - S.length();
if (coins == 0)
{
S += b, S += a;
}
else
if (coins == 1)
{
S += a, S += a;
}
else
if (coins == 2)
{
S += a, S += b;
}
}
CAPITOLUL 2. IOI 2018 2.1. COMBO 124

if (S.length() < N)
{
if (press(S + a) - S.length() == 1)
{
S += a;
}
else
if(press(S + b) - S.length() == 1)
{
S += b;
}
else
{
S += c;
}
}

return S;
}

int main()
{
std::freopen("../in/02-077.txt", "r", stdin) ;
//std::freopen("combo.out", "w", stdout) ;

char buffer[MAX_N + 1];


if (scanf("%s", buffer) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
S = buffer;
N = S.length();

num_moves = 0;

std::string answer = guess_sequence(N);

if (answer != S)
{
wrong_answer("wrong guess");
exit(0);
}

printf("Accepted: num_moves=%d N=%d\n", num_moves,N);


return 0;
}

Listing 2.1.9: combo 77113.cpp


// https://oj.uz/submission/77113

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include "combo.h"

using namespace std;

namespace
{
constexpr int MAX_N = 2000;
constexpr int MAX_NUM_MOVES = 8000;

int N;
std::string S;
int num_moves;

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
CAPITOLUL 2. IOI 2018 2.1. COMBO 125

}
}

int press(std::string p)
{
if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }

int len = p.length();


if (len > 4 * N) { wrong_answer("invalid press"); }

for (int i = 0; i < len; ++i)


{
if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
{
wrong_answer("invalid press");
}
}

int coins = 0;
for (int i = 0, j = 0; i < len; ++i)
{
if (j < N && S[j] == p[i]) { ++j; }
else if (S[0] == p[i]) { j = 1; }
else { j = 0; }
coins = std::max(coins, j);
}

return coins;
}

std::string guess_sequence(int N)
{
int sr = 0;
if (press("AB") > 0) sr += 2;
if (press("AX") > 0) sr++;
string sc[4] = {"Y", "X", "B", "A"};
string s = "";
s += sc[sr];
string c[3];
int cs = 0;
for (int i = 0; i < 4; i++)
if (i != sr)
{
c[cs] = sc[i];
cs++;
}

for (int i = 1; i < N; i++)


{
string ts;
if (i == N-1) ts = s + c[1] + s + c[2];
else ts = s + c[1] + c[0] + s + c[2] + c[0] + s + c[2] + c[2];
int t = press(ts) - s.length();
if (t != 0 && i == N-1)
{
int t1 = press(s + c[1]) - s.length();
if (t1 == 0) s += c[2];
else s += c[1];
continue;
}

if (t == 0) s += c[0];
else if (t == 1)
{
string ps = s + c[1] + c[2];
int t1 = press(ps) - s.length();
if (t1 == 0) s += c[2] + c[1];
else if (t1 == 1) s += c[1] + c[1];
else s += c[1] + c[2];
i++;
}
else
{
string ps = s + c[2] + c[0];
int t1 = press(ps) - s.length();
if (t1 == 0) s += c[1] + c[0];
CAPITOLUL 2. IOI 2018 2.1. COMBO 126

else if (t1 == 1) s += c[2] + c[2];


else s += c[2] + c[0];
i++;
}
}

return s;
}

int main()
{
std::freopen("../in/02-077.txt", "r", stdin) ;
//std::freopen("combo.out", "w", stdout) ;

char buffer[MAX_N + 1];


if (scanf("%s", buffer) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
S = buffer;
N = S.length();

num_moves = 0;

std::string answer = guess_sequence(N);

if (answer != S)
{
wrong_answer("wrong guess");
exit(0);
}

printf("Accepted: num_moves=%d N=%d\n", num_moves,N);


return 0;
}

Listing 2.1.10: combo koosaga.cpp


// https://github.com/koosaga/olympiad/blob/master/IOI/ioi18_combo.cpp

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include "combo.h"

using namespace std;

namespace
{
constexpr int MAX_N = 2000;
constexpr int MAX_NUM_MOVES = 8000;

int N;
std::string S;
int num_moves;

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}
}

int press(std::string p)
{
if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }

int len = p.length();


if (len > 4 * N) { wrong_answer("invalid press"); }

for (int i = 0; i < len; ++i)


{
if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’) {
CAPITOLUL 2. IOI 2018 2.1. COMBO 127

wrong_answer("invalid press");
}
}
int coins = 0;
for (int i = 0, j = 0; i < len; ++i)
{
if (j < N && S[j] == p[i]) { ++j; }
else if (S[0] == p[i]) { j = 1; }
else { j = 0; }
coins = std::max(coins, j);
}
return coins;
}

std::string guess_sequence(int N)
{
std::string p = "";
if(press("AB") >= 1) p = press("A") ? "A" : "B";
else p = press("X") ? "X" : "Y";
std::vector<char> chr = {’A’, ’B’, ’X’, ’Y’};
chr.erase(find(chr.begin(), chr.end(), p[0]));

while((int)p.size() <= N - 2)
{
int query = press(p + chr[0] + chr[0] +
p + chr[0] + chr[1] +
p + chr[1] + chr[0]);
if(query == p.size())
{
p.push_back(chr[2]);
}
else
if(query == p.size() + 1)
{
query = press(p + chr[1] + chr[2]);
if(query == p.size()) p = p + chr[0] + chr[2];
else
if(query == p.size() + 1) p = p + chr[1] + chr[1];
else p = p + chr[1] + chr[2];
}
else
{
query = press(p + chr[0] + chr[1]);
if(query == p.size()) p = p + chr[1] + chr[0];
else
if(query == p.size() + 1) p = p + chr[0] + chr[0];
else p = p + chr[0] + chr[1];
}
}

while(p.size() != N)
{
if(press(p + chr[0]) == p.size() + 1) p.push_back(chr[0]);
else
if(press(p + chr[1]) == p.size() + 1) p.push_back(chr[1]);
else p.push_back(chr[2]);
}

return p;
}

int main()
{
std::freopen("../in/02-077.txt", "r", stdin) ;
//std::freopen("combo.out", "w", stdout) ;

char buffer[MAX_N + 1];


if (scanf("%s", buffer) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
S = buffer;
N = S.length();

num_moves = 0;
CAPITOLUL 2. IOI 2018 2.2. SEATS 128

std::string answer = guess_sequence(N);

if (answer != S)
{
wrong_answer("wrong guess");
exit(0);
}

printf("Accepted: num_moves=%d N=%d\n", num_moves,N);


return 0;
}

2.1.3 *Rezolvare detaliată

2.2 Seats
Problema 2 - Seats 100 de puncte

Author: Mikhail Pyaderkin (Russia)

Organizaţi un concurs internaţional de programare ı̂ntr-un hol dreptunghiular cu H W locuri


aranjate ı̂n H rânduri şi W coloane. Rândurile sunt numerotate de la 0 la H  1 şi coloanele de
la 0 la W  1. Locul din rândul r şi coloana c este notat r, c. Aţi invitat H W concurenţi,
numerotaţi de la 0 la H W  1.
Aţi elaborat de asemenea o schemă de amplasare care atribuie concurentul cu numărul i
(0 & i & H W  1) locul Ri , Ci . Schema atribuie exact câte un concurent fiecărui loc.
O mulţime de locuri din holul S este dreptunghiulară dacă există numerele ı̂ntregi r1 , r2 ,
c1 , şi c2 care satisfac următoarele condiţii:
a 0 & r1 & r2 & H  1
a 0 & c1 & c2 & W  1
a S este exact mulţimea tuturor locurilor r, c astfel ı̂ncât r1 & r & r2 şi c1 & c & c2 .
O mulţime dreptunghiulară formată din k (1 & k & H W ) locuri se consideră frumoasă dacă
concurenţii, cărora le sunt atribuite locurile din mulţime, sunt numerotaţi de la 0 la k  1.
Gradul de frumuseţe al schemei de amplasare este numărul mulţimilor dreptunghiulare
frumoase din schemă.
După elaborarea schemei de amplasare aţi primit câteva cereri de interschimbare a locurilor
unor perechi de concurenţi. Mai exact, sunt Q astfel de cereri, numerotate de la 0 la Q  1 ı̂n
ordine cronologică.
Cererea j (0 & j & Q  1) este de a interschimba locurile asociate concurenţilor Aj şi Bj . Fiecare
cerere se prelucrează imediat şi se actualizează schema de amplasare. După fiecare actualizare
scopul este să calculaţi gradul de frumuseţe al schemei de amplasare curente.

Detalii de implementare

Trebuie să implementaţi următoarea procedură şi funcţie:


give_initial_chart(int H, int W, int[] R, int[] C)
a H, W : numărul de rânduri şi numărul de coloane.
a R, C: tablouri unidimensionale cu H W elemente reprezentând schema de amplasare iniţială.
a Acastă procedură este apelată o singură dată, ı̂nainte de orice apel al funcţiei swap_seats.

int swap_seats(int a, int b)


a Această funcţie descrie o cerere de interschimbare a două locuri.
a a, b: concurenţii a căror locuri urmează a fi interschimbate.
a Această funcţie este apelată de Q ori.
a Această funcţie va ı̂ntoarce gradul de frumuseţe al schemei de amplasare după interschimbare.
CAPITOLUL 2. IOI 2018 2.2. SEATS 129

Exemple

Fie H 2, W 3, R 0, 1, 1, 0, 0, 1, C 0, 0, 1, 1, 2, 2, şi Q 2.


Grader-ul va apela mai ı̂ntâi
give_initial_chart(2, 3, [0, 1, 1, 0, 0, 1], [0,0, 1, 1, 2, 2]).
Ininţial, schema de amplasare este următoarea:

0 3 4
1 2 5

Să presupunem că grader-ul apelează swap_seats(0, 5). După cererea 0, schema de am-
plasare este următoarea:

5 3 4
1 2 0

Mulţimile de locuri care corespund concurenţilor r0x, r0, 1, 2x, şi r0, 1, 2, 3, 4, 5x sunt drep-
tunghiulare şi frumoase. Astfel, gradul de frumuseţe al schemei de amplasare este 3, şi
swap_seats va returna 3.
Să zicem că grader-ul apelează swap_seats(0, 5) din nou. După cererea 1, schema de
amplasare revine la starea iniţială. Mulţimile de locuri care corespund concurenţilor r0x, r0, 1x,
r0, 1, 2, 3x, şi r0, 1, 2, 3, 4, 5x sunt dreptunghiulare şi frumoase. Prin urmare, gradul de frumuseţe
al schemei de amplasare este 4, şi swap_seats va ı̂ntoarce 4.
Fişierele sample-01-in.txt şi sample-01-out.txt ı̂n pachetul arhivat anexat corespund
acestui exemplu. Pachetul conţine şi alte exemple de intrări/ieşiri.

Restricţii

a 1&H
a 1&W
a H W & 1000000
a 0 & Ri & H  1 (0 & i & HW  1)
a 0 & Ci & W  1 (0 & i & HW  1)
a Ri , Ci  j Rj , Cj  (0 & i $ j & H W  1)
a 1 & Q & 50000
a 0 & a & H W  1 pentru orice apel swap_seats
a 0 & b & H W  1 pentru orice apel swap_seats
a a j b pentru orice apel swap_seats

Subtaskuri

1. (5 puncte) H W & 100, Q & 5000


2. (6 puncte) H V & 10000, Q & 5000
3. (20 puncte) H & 1000, W & 1000, Q & 5000
4. (6 puncte) Q & 5000, ¶a  b¶ & 10000 pentru orice apel swap_seats
5. (33 puncte) H 1
6. (30 puncte) Fără constrângeri adiţionale.

Exemplu de grader

Grader-ul local citeşte datele de intrare ı̂n următoarea formă:


a linia 1: H W Q
a linia 2  i (0 & i & H W  1): Ri Ci
a linia 2  H W  j (0 & j & Q  1): Aj Bj
Aici, Aj şi Bj sunt parametrii pentru apelul swap_seats ı̂n cererea j.
Grader-ul local afişează răspunsurile ı̂n următoarea formă:
a linia 1  j (0 & j & Q  1): valoarea ı̂ntoarsă de swap_seats pentru cererea j
CAPITOLUL 2. IOI 2018 2.2. SEATS 130

2.2.1 Indicaţii de rezolvare

The numbers between 0 and HW  1, inclusive, are written on a rectangle divided to H  W


squares such that each square contains one number and each number is written on exactly one
square.
Deal with the following queries one by one:
ˆ Swap the positions of two numbers.
ˆ Count the number of t between 0 and HW  1 such that the squares where the numbers
between 0 and t are written form a rectangle.

Subtasks and Solutions


Subtask 1 (HW is very small)
Determine whether a set of squares forms a rectangule or not in O HW  time and deal with
2 2
each query in O H W  time.
Subtask 2 (HW is small)
Let the square where the number i is written be ri , ci . The sufficient and necessary condition
of r0 , c0 , ..., rt , ct  forming a rectangle is

max ri  min ri  1 max ci  min ci  1 t1

Here i moves between 0 and t.


Check if these conditions hold from t 0 to t HW  1 inductively in O HW  time.
Subtask 3 ( is small)
If max ri  min ri  1 max ci  min ci  1 % t  1, the next t satisfying the condition is not
less than max ri  min ri  1 max ci  min ci  1  1.
So it is enough to determine this condition in O H  W  numbers.
This condition is determinable independently in log HW  time using a segment tree. Then
each query is dealt with in O H  W  log HW  time.
Subtask 4 (Gaps of two swapped numbers are small)
Memorize max and min of ri and ci for each t and recalculate them for t between the two
swapped numbers for each query.
Subtask 5 (One dimensional, namely, )
Take t arbitrarily and color the squares with numbers 0, ..., t black and the squares with
numbers t  1, ..., W  1 white (and add white squares outside of the rectangle.)
The sufficient and necessary condition of black squares forming a rectangle is that the number
of pairs of adjacent two squares with different colors is two.
Manage the numbers of such pairs of squares for all values of t and count the number of t-s
satisying the condition efficiently using a lazy propagation segment tree and deal with each query
in O log W  time.
Subtask 6 (No additional constraints)
Like above, take t arbitrarily and color the squares with numbers 0, ..., t black the squares
with numbers t  1, ..., HW  1 white (and add white squares outside of the rectangle.)
Pay attention to two-times-two squares (sets of adjacent four squares.)
The sufficient and necessary condition of black squares forming a rectangle is as follows:
ˆ There are only four two-times-two squares which contain exactly one black square.
ˆ There are no two-times-two squares which contain exactly three black squares.

Manage the numbers of such two-times-two squares for all values of t and count the number
of t-s satisying the condition efficiently using a lazy propagation segment tree and deal with each
query in O log HW  time.
CAPITOLUL 2. IOI 2018 2.2. SEATS 131

2.2.2 Coduri sursă

Listing 2.2.1: compile cpp.sh


#!/bin/bash

TASK=seats

g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 2.2.2: seat.h


#include <vector>

void give_initial_chart(int H, int W, std::vector<int> R, std::vector<int> C);


int swap_seats(int a, int b);

Listing 2.2.3: seat.cpp


#include "seats.h"

std::vector<int> r;

void give_initial_chart(int H, int W, std::vector<int> R, std::vector<int> C)


{
r = R;
}

int swap_seats(int a, int b)


{
return r[a];
}

Listing 2.2.4: grader.cpp


#include <cstdio>
#include <cstdlib>
#include <vector>
#include "seats.h"

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
int H = read_int();
int W = read_int();
int Q = read_int();
std::vector<int> R(H * W), C(H * W);
for (int i = 0; i < H * W; ++i)
{
R[i] = read_int();
C[i] = read_int();
}
std::vector<int> A(Q), B(Q);
for (int j = 0; j < Q; ++j)
{
A[j] = read_int();
B[j] = read_int();
}
CAPITOLUL 2. IOI 2018 2.2. SEATS 132

give_initial_chart(H, W, R, C);
for (int j = 0; j < Q; ++j)
{
int answer = swap_seats(A[j], B[j]);
printf("%d\n", answer);
}
return 0;
}

Listing 2.2.5: seat-model.cpp


// https://oj.uz/submission/76357
// https://oj.uz/problems/source/364
// https://oj.uz/problems/source/363
// https://oj.uz/problems/source/22

#include <stdio.h> // citire mai rapida !!!


#include <algorithm>
#include <vector>
#include <set>

#include <time.h> /* clock */


#include <iostream>

using namespace std;

const int Z = 1<<20;

struct node
{
node()
{
sum = min = 0; cnt = 1;
}

node(int v)
{
sum = min = v; cnt = 1;
}

int sum, min, cnt;

node operator *(const node &t) const


{
node res;

res.sum = sum + t.sum;

if (min < sum + t.min) res.min = min;


else res.min = sum + t.min;

res.cnt = 0;
if (res.min == min) res.cnt += cnt;
if (res.min == sum + t.min) res.cnt += t.cnt;

return res;
}

void add(int p)
{
sum += p;
min += p;
}
} IT[Z*2];

int N, A[1001001];
bool chk[1001001];
int H, W;
vector<int> R, C;

void upd(int x)
{
x /= 2;
CAPITOLUL 2. IOI 2018 2.2. SEATS 133

while (x)
{
IT[x] = IT[x*2] * IT[x*2+1];
x /= 2;
}
}

set<int> ups;

void add(int i, int p, bool up = true)


{
if (p > 0)
{
if (chk[i]) return;
chk[i] = 1;
}
else
{
if (!chk[i]) return;
chk[i] = 0;
}

int x = R[i], y = C[i];


int dx[5] = {0,1,0,-1,0};
int dy[5] = {1,0,-1,0,1};

for (int k=0;k<4;k++)


{
int u[2];
for (int d=0;d<2;d++)
{
int px = x + dx[k+d];
int py = y + dy[k+d];
if (px < 0 || px >= H || py < 0 || py >= W) u[d] = N;
else u[d] = A[px*W+py];
}

int s = N, e = 0;
if (i < u[0] && i < u[1])
{
s = i + Z;
e = min(u[0],u[1]) + Z;
}

if (u[0] < i && u[1] < i)


{
s = max(u[0],u[1]) + Z;
e = i + Z;
}

if (s < e)
{
IT[s].add(+p);
IT[e].add(-p);
if (up)
{
ups.insert(s);
ups.insert(e);
}
}
}
}

void give_initial_chart(int H_, int W_, vector<int> R_, vector<int> C_)


{
H = H_; W = W_; R = R_; C = C_; N = H * W;
for (int i=0;i<N;i++) A[R[i]*W+C[i]] = i;
IT[N+Z].add(10000);
for (int i=0;i<N;i++) add(i,1,false);
for (int i=Z-1;i>=1;i--) IT[i] = IT[i*2] * IT[i*2+1];
}

int swap_seats(int a, int b)


{
int dx[5] = {0,1,0,-1,0};
int dy[5] = {1,0,-1,0,0};
CAPITOLUL 2. IOI 2018 2.2. SEATS 134

for (int i : {a,b}) for (int k=0;k<5;k++)


{
int x = R[i] + dx[k];
int y = C[i] + dy[k];
if (x < 0 || x >= H || y < 0 || y >= W) continue;
add(A[x*W+y],-1);
}

swap(R[a],R[b]);
swap(C[a],C[b]);
A[R[a]*W+C[a]] = a;
A[R[b]*W+C[b]] = b;

for (int i : {a,b}) for (int k=0;k<5;k++)


{
int x = R[i] + dx[k];
int y = C[i] + dy[k];
if (x < 0 || x >= H || y < 0 || y >= W) continue;
add(A[x*W+y],+1);
}

for (int x : ups) upd(x);


ups.clear();

return IT[1].min == 4 ? IT[1].cnt : 0;


}

//======================================================================

namespace
{

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/05-11.txt", "r", stdin) ;


std::freopen("seats.out", "w", stdout) ;

int H = read_int();
int W = read_int();
int Q = read_int();

std::vector<int> R(H * W), C(H * W);


for (int i = 0; i < H * W; ++i)
{
R[i] = read_int();
C[i] = read_int();
}
std::vector<int> A(Q), B(Q);

for (int j = 0; j < Q; ++j)


{
A[j] = read_int();
B[j] = read_int();
}

auto t2 = clock();

give_initial_chart(H, W, R, C);

auto t3 = clock();

for (int j = 0; j < Q; ++j)


CAPITOLUL 2. IOI 2018 2.2. SEATS 135

{
int answer = swap_seats(A[j], B[j]);
printf("%d\n", answer);
}

auto t4 = clock();
fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//======================================================================
/*
t2-t1 = 0.896
t3-t2 = 0.449
t4-t3 = 1.873

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


Press any key to continue.
*/
//======================================================================

Listing 2.2.6: seats 75159.cpp


// https://oj.uz/submission/75159

#include <ctime>
//#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include <cstdlib>
#include <vector>
#include "seats.h"

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long llong;


typedef pair<int, int> pii;

int h, w, n;

vector<int> R, C;
vector<vector<int>> idx;

struct segtree
{
struct node
{
llong mn, ct, lz;
node operator+(const node &p) const
{
node ret;
ret.mn = min(mn, p.mn);
ret.ct = 0;
if (ret.mn == mn) ret.ct += ct;
if (ret.mn == p.mn) ret.ct += p.ct;
ret.lz = 0;
return ret;
}
} seg[1 << 21];

void init(int i, int s, int e, const vector<llong> &v)


{
CAPITOLUL 2. IOI 2018 2.2. SEATS 136

if (s == e)
{
seg[i].mn = v[s];
seg[i].ct = 1;
seg[i].lz = 0;
return;
}

int m = (s + e) / 2;
init(i << 1, s, m, v);
init(i << 1 | 1, m + 1, e, v);
seg[i] = seg[i << 1] + seg[i << 1 | 1];
}

void spread(int i)
{
seg[i << 1].mn += seg[i].lz;
seg[i << 1].lz += seg[i].lz;
seg[i << 1 | 1].mn += seg[i].lz;
seg[i << 1 | 1].lz += seg[i].lz;
seg[i].lz = 0;
}

void update(int i, int s, int e, int x, int y, llong v)


{
if (e < x || y < s) return;
if (x <= s && e <= y)
{
seg[i].mn += v;
seg[i].lz += v;
return;
}

spread(i);
int m = (s + e) / 2;
update(i << 1, s, m, x, y, v);
update(i << 1 | 1, m + 1, e, x, y, v);
seg[i] = seg[i << 1] + seg[i << 1 | 1];
}

int get() const


{
return seg[1].ct;
}
} seg;

int getIdx(int x, int y)


{
if (x < 0 || h <= x || y < 0 || w <= y) return n;
return idx[x][y];
}

void update(int x, int y, int v)


{
int A[4] = { getIdx(x - 1, y - 1), getIdx(x - 1, y)
, getIdx(x, y - 1), getIdx(x, y) };
sort(A, A + 4);
seg.update(1, 0, n - 1, A[0], A[1] - 1, v);
seg.update(1, 0, n - 1, A[2], A[3] - 1, v * (1ll << 30));
}

void give_initial_chart(int H, int W, vector<int> R, vector<int> C)


{
idx.resize(H, vector<int>(W, 0));
::R = R; ::C = C;
h = H; w = W; n = H * W;
for (int i = 0; i < n; ++i)
{
idx[R[i]][C[i]] = i;
}

vector<llong> sum(n + 1, 0);


for (int i = 0; i <= h; ++i)
{
for (int j = 0; j <= w; ++j)
{
CAPITOLUL 2. IOI 2018 2.2. SEATS 137

int A[4] = { getIdx(i - 1, j - 1), getIdx(i - 1, j)


, getIdx(i, j - 1), getIdx(i, j) };
sort(A, A + 4);
sum[A[0]] += 1;
sum[A[1]] -= 1;
sum[A[2]] += (1ll << 30);
sum[A[3]] -= (1ll << 30);
}
}

for (int i = 1; i < n; ++i) sum[i] += sum[i - 1];


seg.init(1, 0, n - 1, sum);
}

int swap_seats(int a, int b)


{
vector<pii> ps;
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 2; ++j)
{
ps.emplace_back(R[a] + i, C[a] + j);
ps.emplace_back(R[b] + i, C[b] + j);
}
}

sort(ps.begin(), ps.end());
ps.erase(unique(ps.begin(), ps.end()), ps.end());
for (pii i : ps) update(i.first, i.second, -1);
swap(idx[R[a]][C[a]], idx[R[b]][C[b]]);
swap(R[a], R[b]); swap(C[a], C[b]);
for (pii i : ps) update(i.first, i.second, 1);
return seg.get();
}

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

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/06-04.txt", "r", stdin) ;


std::freopen("seats.out", "w", stdout) ;

int H = read_int();
int W = read_int();
int Q = read_int();

std::vector<int> R(H * W), C(H * W);


for (int i = 0; i < H * W; ++i)
{
R[i] = read_int();
C[i] = read_int();
}
std::vector<int> A(Q), B(Q);

for (int j = 0; j < Q; ++j)


{
A[j] = read_int();
B[j] = read_int();
}
CAPITOLUL 2. IOI 2018 2.2. SEATS 138

auto t2 = clock();

give_initial_chart(H, W, R, C);

auto t3 = clock();

for (int j = 0; j < Q; ++j)


{
int answer = swap_seats(A[j], B[j]);
printf("%d\n", answer);
}

auto t4 = clock();
fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 3.124
t3-t2 = 0.67
t4-t3 = 8.047

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


Press any key to continue.
*/

Listing 2.2.7: seats 75485.cpp


// https://oj.uz/submission/75485

#include <ctime>
//#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include <vector>
#include "seats.h"

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn=1000005;

const int dx[4]={0,0,1,1},dy[4]={0,1,0,1};

int H,W,NUM;
int val[maxn],presum[maxn];
vector<int> R,C;
int S[maxn],T[maxn];

struct node
{
int dta;
int s,cnt;
node *lc,*rc;

void tagdta(const int &c)


{
s+=c;
dta+=c;
}

void downdate()
{
CAPITOLUL 2. IOI 2018 2.2. SEATS 139

if(dta)
{
if(lc)lc->tagdta(dta);
if(rc)rc->tagdta(dta);
dta=0;
}
}

void update()
{
if(lc->s<rc->s)
{
s=lc->s;
cnt=lc->cnt;
}
else if(rc->s<lc->s)
{
s=rc->s;
cnt=rc->cnt;
}
else
{
s=lc->s;
cnt=lc->cnt+rc->cnt;
}
}

void Add(int l,int r,const int &a,const int &b,const int &c)
{
if(l>=a&&r<=b)
{
tagdta(c);
return;
}
int mid=l+r>>1;
downdate();
if(a<=mid)lc->Add(l,mid,a,b,c);
if(b>mid)rc->Add(mid+1,r,a,b,c);
update();
}

} ndl[maxn*2],*ns=ndl,*root;

node* build(int l,int r)


{
node *x=ns++;
x->dta=0;
if(l==r)
{
x->s=presum[l];
x->cnt=1;
x->lc=x->rc=NULL;
}
else
{
int mid=l+r>>1;
x->lc=build(l,mid);
x->rc=build(mid+1,r);
x->update();
}
return x;
}

int& seat(int *s,const int &x,const int &y)


{
if(x>=0&&x<H&&y>=0&&y<W)return s[x*W+y];
return NUM;
}

const int ValTable[2][3]={


{1,-1,-1},
{1,1,-1}
};

int CalcVal(int *s,const int &x,const int &y,const int w=15)


{
CAPITOLUL 2. IOI 2018 2.2. SEATS 140

int res=0;
for(int k=0;k<4;k++)
if(w>>k&1)
{
int i=x-1+dx[k];
int j=y-1+dy[k];
int t=seat(s,x,y);
int a=seat(s,i+dx[kˆ1],j+dy[kˆ1]); // XOR
int b=seat(s,i+dx[kˆ2],j+dy[kˆ2]);
int c=seat(s,i+dx[k],j+dy[k]);
res+=ValTable[c<t][(a<t)+(b<t)];
}
//cout<<endl;
return res;
}

void give_initial_chart(int H, int W, std::vector<int> R,


std::vector<int> C)
{
::H=H; // https://www.geeksforgeeks.org/scope-resolution-operator-in-c/
::W=W;
NUM=H*W;
::R.assign(NUM,0);
::C.assign(NUM,0);
for(int i=0;i<NUM;i++)
{
::R[i]=R[i];
::C[i]=C[i];
seat(S,R[i],C[i])=seat(T,R[i],C[i])=i;
}
for(int i=0;i<NUM;i++)
{
val[i]=CalcVal(S,R[i],C[i]);
if(i==0)presum[i]=val[i];
else presum[i]=presum[i-1]+val[i];
}
//for(int i=0;i<NUM;i++)cout<<val[i]<<’ ’;cout<<endl;
root=build(0,NUM-1);
}

void UpdateVal(int x,int v)


{
root->Add(0,NUM-1,x,NUM-1,v-val[x]);
val[x]=v;
}

int swap_seats(int a, int b)


{
swap(seat(T,R[a],C[a]),seat(T,R[b],C[b]));
vector<int> p;
for(int i=-1;i<=1;i++)
for(int j=-1;j<=1;j++)
{
int t;
t=seat(T,R[a]+i,C[a]+j);
if(t!=NUM)p.push_back(t);
t=seat(T,R[b]+i,C[b]+j);
if(t!=NUM)p.push_back(t);
}
swap(R[a],R[b]);
swap(C[a],C[b]);
sort(p.begin(),p.end());
p.erase(unique(p.begin(),p.end()),p.end());
for(auto x:p)
{
UpdateVal(x,CalcVal(T,R[x],C[x]));
}
swap(seat(S,R[a],C[a]),seat(S,R[b],C[b]));
if(root->s!=4)return 0;
return root->cnt;
}

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

namespace
{
CAPITOLUL 2. IOI 2018 2.2. SEATS 141

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/06-04.txt", "r", stdin) ;


std::freopen("seats.out", "w", stdout) ;

int H = read_int();
int W = read_int();
int Q = read_int();

std::vector<int> R(H * W), C(H * W);


for (int i = 0; i < H * W; ++i)
{
R[i] = read_int();
C[i] = read_int();
}
std::vector<int> A(Q), B(Q);

for (int j = 0; j < Q; ++j)


{
A[j] = read_int();
B[j] = read_int();
}

auto t2 = clock();

give_initial_chart(H, W, R, C);

auto t3 = clock();

for (int j = 0; j < Q; ++j)


{
int answer = swap_seats(A[j], B[j]);
printf("%d\n", answer);
}

auto t4 = clock();
fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 3.21
t3-t2 = 0.715
t4-t3 = 2.651

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


Press any key to continue.
*/

Listing 2.2.8: seats 76357.cpp


// https://oj.uz/submission/76357

#include <ctime>
CAPITOLUL 2. IOI 2018 2.2. SEATS 142

//#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include <algorithm>
#include <vector>
#include <set>

#include <iostream>

using namespace std;

const int Z = 1<<20;

struct node
{
node()
{
sum = min = 0; cnt = 1;
}

node(int v)
{
sum = min = v; cnt = 1;
}

int sum, min, cnt;

node operator *(const node &t) const


{
node res;

res.sum = sum + t.sum;

if (min < sum + t.min) res.min = min;


else res.min = sum + t.min;

res.cnt = 0;
if (res.min == min) res.cnt += cnt;
if (res.min == sum + t.min) res.cnt += t.cnt;

return res;
}

void add(int p)
{
sum += p;
min += p;
}
} IT[Z*2];

int N, A[1001001];
bool chk[1001001];
int H, W;
vector<int> R, C;

void upd(int x)
{
x /= 2;
while (x)
{
IT[x] = IT[x*2] * IT[x*2+1];
x /= 2;
}
}

set<int> ups;

void add(int i, int p, bool up = true)


{
if (p > 0)
{
if (chk[i]) return;
chk[i] = 1;
}
CAPITOLUL 2. IOI 2018 2.2. SEATS 143

else
{
if (!chk[i]) return;
chk[i] = 0;
}

int x = R[i], y = C[i];


int dx[5] = {0,1,0,-1,0};
int dy[5] = {1,0,-1,0,1};

for (int k=0;k<4;k++)


{
int u[2];
for (int d=0;d<2;d++)
{
int px = x + dx[k+d];
int py = y + dy[k+d];
if (px < 0 || px >= H || py < 0 || py >= W) u[d] = N;
else u[d] = A[px*W+py];
}

int s = N, e = 0;
if (i < u[0] && i < u[1])
{
s = i + Z;
e = min(u[0],u[1]) + Z;
}

if (u[0] < i && u[1] < i)


{
s = max(u[0],u[1]) + Z;
e = i + Z;
}

if (s < e)
{
IT[s].add(+p);
IT[e].add(-p);
if (up)
{
ups.insert(s);
ups.insert(e);
}
}
}
}

void give_initial_chart(int H_, int W_, vector<int> R_, vector<int> C_)


{
H = H_; W = W_; R = R_; C = C_; N = H * W;
for (int i=0;i<N;i++) A[R[i]*W+C[i]] = i;
IT[N+Z].add(10000);
for (int i=0;i<N;i++) add(i,1,false);
for (int i=Z-1;i>=1;i--) IT[i] = IT[i*2] * IT[i*2+1];
}

int swap_seats(int a, int b)


{
int dx[5] = {0,1,0,-1,0};
int dy[5] = {1,0,-1,0,0};
for (int i : {a,b}) for (int k=0;k<5;k++)
{
int x = R[i] + dx[k];
int y = C[i] + dy[k];
if (x < 0 || x >= H || y < 0 || y >= W) continue;
add(A[x*W+y],-1);
}

swap(R[a],R[b]);
swap(C[a],C[b]);
A[R[a]*W+C[a]] = a;
A[R[b]*W+C[b]] = b;

for (int i : {a,b}) for (int k=0;k<5;k++)


{
int x = R[i] + dx[k];
CAPITOLUL 2. IOI 2018 2.2. SEATS 144

int y = C[i] + dy[k];


if (x < 0 || x >= H || y < 0 || y >= W) continue;
add(A[x*W+y],+1);
}

for (int x : ups) upd(x);


ups.clear();

return IT[1].min == 4 ? IT[1].cnt : 0;


}

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

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/06-04.txt", "r", stdin) ;


std::freopen("seats.out", "w", stdout) ;

int H = read_int();
int W = read_int();
int Q = read_int();

std::vector<int> R(H * W), C(H * W);


for (int i = 0; i < H * W; ++i)
{
R[i] = read_int();
C[i] = read_int();
}
std::vector<int> A(Q), B(Q);

for (int j = 0; j < Q; ++j)


{
A[j] = read_int();
B[j] = read_int();
}

auto t2 = clock();

give_initial_chart(H, W, R, C);

auto t3 = clock();

for (int j = 0; j < Q; ++j)


{
int answer = swap_seats(A[j], B[j]);
printf("%d\n", answer);
}

auto t4 = clock();
fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
CAPITOLUL 2. IOI 2018 2.2. SEATS 145

/*
t2-t1 = 3.182
t3-t2 = 0.458
t4-t3 = 4.476

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


Press any key to continue.
*/

Listing 2.2.9: seats 80434.cpp


// https://oj.uz/submission/80434

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include "seats.h"

#include <bits/stdc++.h>

using namespace std;

typedef long long int lld;


typedef pair<int,int> pii;

int h,w;
int r[1000010];
int c[1000010];
pii arr[1000010];

//segtree
pii sum[8000000];
pii minimo[8000000];
int counter[8000000];
vector<vector<int> >table;

void update_pos(int a, int b)


{
if(a<0 || a>h-1)return;
if(b<0 || b>w-1)return;
int t[3][3];
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)t[i][j]=1;
}
t[1][1]=0;
if(a==0 || b==0 || table[a-1][b-1]>table[a][b])t[0][0]=0;
if(a==0 || b==w-1 || table[a-1][b+1]>table[a][b])t[0][2]=0;
if(a==h-1 || b==0 || table[a+1][b-1]>table[a][b])t[2][0]=0;
if(a==h-1 || b==w-1 || table[a+1][b+1]>table[a][b])t[2][2]=0;
if(a==0 || table[a-1][b]>table[a][b])t[0][1]=0;
if(b==0 || table[a][b-1]>table[a][b])t[1][0]=0;
if(a==h-1 || table[a+1][b]>table[a][b])t[2][1]=0;
if(b==w-1 || table[a][b+1]>table[a][b])t[1][2]=0;

int x,y,z,w;
x=t[0][0]+t[0][1]+t[1][0]+t[1][1];
y=t[1][0]+t[1][1]+t[2][0]+t[2][1];
z=t[0][1]+t[0][2]+t[1][1]+t[1][2];
w=t[1][1]+t[1][2]+t[2][1]+t[2][2];
int cnt[5];
for(int i=0;i<5;i++)cnt[i]=0;
cnt[x]++;
cnt[y]++;
cnt[z]++;
cnt[w]++;

arr[table[a][b]].first=cnt[0]-cnt[1];
arr[table[a][b]].second=cnt[2]-cnt[3];
}

pii Sum(pii a, pii b)


{
pii c=pii(a.first+b.first,a.second+b.second);
return c;
CAPITOLUL 2. IOI 2018 2.2. SEATS 146

pii min(pii a, pii b)


{
if(a.second<b.second)return a;
if(a.second>b.second)return b;
if(a.first>b.first)return b;
return a;
}

void build(int a, int b, int node)


{
if(a==b)
{
sum[node]=arr[a];
minimo[node]=arr[a];
counter[node]=1;
return;
}

int mid=(a+b)/2;
build(a,mid,2*node);
build(mid+1,b,2*node+1);
sum[node]=Sum(sum[2*node],sum[2*node+1]);
minimo[node]=min(minimo[2*node],Sum(minimo[2*node+1],sum[2*node]));
counter[node]=0;
if(minimo[node]==minimo[2*node])
{
counter[node]+=counter[2*node];
}

if(minimo[node]==Sum(minimo[2*node+1],sum[2*node]))
{
counter[node]+=counter[2*node+1];
}
}

void update(int a, int b, int node, int pos)


{
if(pos<a || b<pos)return;
if(a==b)
{
sum[node]=arr[a];
minimo[node]=arr[a];
counter[node]=1;
return;
}

int mid=(a+b)/2;
update(a,mid,2*node,pos);
update(mid+1,b,2*node+1,pos);
sum[node]=Sum(sum[2*node],sum[2*node+1]);
minimo[node]=min(minimo[2*node],Sum(minimo[2*node+1],sum[2*node]));

counter[node]=0;
if(minimo[node]==minimo[2*node])
{
counter[node]+=counter[2*node];
}

if(minimo[node]==Sum(minimo[2*node+1],sum[2*node]))
{
counter[node]+=counter[2*node+1];
}
}

void give_initial_chart(int H, int W, std::vector<int> R,


std::vector<int> C)
{
h=H;
w=W;
for(int i=0;i<h;i++)
{
vector<int> a;
for(int j=0;j<w;j++)a.push_back(-1);
table.push_back(a);
CAPITOLUL 2. IOI 2018 2.2. SEATS 147

for(int i=0;i<w*h;i++)
{
r[i]=R[i];
c[i]=C[i];
table[R[i]][C[i]]=i;
}

for(int i=0;i<h;i++)
{
for(int j=0;j<w;j++)update_pos(i,j);
}

build(0,w*h-1,1);
//for(int i=0;i<h*w;i++)cout<<arr[i].second<<endl;
//cout<<counter[1]<<endl;
}

int swap_seats(int a, int b)


{
swap(table[r[a]][c[a]],table[r[b]][c[b]]);
swap(r[a],r[b]);
swap(c[a],c[b]);
for(int i=-1;i<2;i++)
{
for(int j=-1;j<2;j++)
{
update_pos(r[a]+i,c[a]+j);
if(r[a]+i>=0 && r[a]+i<=h-1 && c[a]+j>=0 && c[a]+j<=w-1)
update(0,h*w-1,1,table[r[a]+i][c[a]+j]);

update_pos(r[b]+i,c[b]+j);
if(r[b]+i>=0 && r[b]+i<=h-1 && c[b]+j>=0 && c[b]+j<=w-1)
update(0,h*w-1,1,table[r[b]+i][c[b]+j]);
}
}

return counter[1];
}

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

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
}

int main()
{
auto t1 = clock();

std::freopen("../in/06-04.txt", "r", stdin) ;


std::freopen("seats.out", "w", stdout) ;

int H = read_int();
int W = read_int();
int Q = read_int();

std::vector<int> R(H * W), C(H * W);


for (int i = 0; i < H * W; ++i)
{
R[i] = read_int();
C[i] = read_int();
}
std::vector<int> A(Q), B(Q);
CAPITOLUL 2. IOI 2018 2.2. SEATS 148

for (int j = 0; j < Q; ++j)


{
A[j] = read_int();
B[j] = read_int();
}

auto t2 = clock();

give_initial_chart(H, W, R, C);

auto t3 = clock();

for (int j = 0; j < Q; ++j)


{
int answer = swap_seats(A[j], B[j]);
printf("%d\n", answer);
}

auto t4 = clock();
fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.87
t3-t2 = 0.859
t4-t3 = 4.672

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


Press any key to continue.
*/

Listing 2.2.10: seats 81209.cpp


// https://oj.uz/submission/81209

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include "seats.h"

#include <algorithm>
#include <bits/stdc++.h>

std::vector<int> r;

using namespace std;

#define rep(i,n) for(int i=0;i<n;i++)

int T[1<<21], A[1<<21], cnt[1<<21];


int R[1000010], C[1000010];
int N, M, P;

void init(int rt, int l, int r)


{
cnt[rt] = r - l + 1;
if(l == r) return;
int m = (l + r) >> 1;
init(rt<<1, l, m);
init(rt<<1|1, m+1, r);
}

inline void pushup(int rt)


{
if(T[rt<<1] == T[rt<<1|1])
cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1], T[rt] = T[rt<<1];
else
CAPITOLUL 2. IOI 2018 2.2. SEATS 149

if(T[rt<<1] < T[rt<<1|1])


cnt[rt] = cnt[rt<<1], T[rt] = T[rt<<1];
else
cnt[rt] = cnt[rt<<1|1], T[rt] = T[rt<<1|1];
}

void add(int rt, int l, int r, int s, int e, int c)


{
if(s <= l && r <= e)
{
T[rt] += c;
A[rt] += c;
return;
}

if(A[rt])
{
T[rt<<1] += A[rt];
A[rt<<1] += A[rt];
T[rt<<1|1] += A[rt];
A[rt<<1|1] += A[rt];
A[rt] = 0;
}

int m = (l + r) >> 1;
if(s <= m) add(rt<<1, l, m, s, e, c);
if(m < e) add(rt<<1|1, m+1, r, s, e, c);
pushup(rt);
}

void add(int l, int r, int v)


{
add(1, 1, P, l, r, v);
}

int rval[1000010];

void get_val(int x, int y, int rr[])


{
int rz = 0;
for(int dx : {0,1}) for(int dy : {0,1})
{
int ni = x + dx, nj = y + dy, val = P + 1;
if(1 <= ni && ni <= N && 1 <= nj && nj <= M)
{
val = rval[(ni-1)*M+(nj-1)];
}
rr[rz++] = val;
}

sort(rr, rr+4);
}

void change(int r, int c, int x)


{
for(int dx:{-1,0}) for(int dy:{-1,0})
{
int tt[4];
get_val(r+dx, c+dy, tt);
if(tt[0]<tt[1]) add(tt[0], tt[1]-1, -1);
if(tt[2]<tt[3]) add(tt[2], tt[3]-1, -100);
}

rval[(r-1)*M+(c-1)] = x;
for(int dx:{-1,0}) for(int dy:{-1,0})
{
int tt[4];
get_val(r+dx, c+dy, tt);
if(tt[0]<tt[1]) add(tt[0], tt[1]-1, 1);
if(tt[2]<tt[3]) add(tt[2], tt[3]-1, 100);
}
}

// ....................................................

void give_initial_chart(int H, int W, std::vector<int> tR,


CAPITOLUL 2. IOI 2018 2.2. SEATS 150

std::vector<int> tC)
{
N = H; M = W;
P = N * M;
init(1, 1, P);
rep(i, P) R[i + 1] = tR[i] + 1, C[i + 1] = tC[i] + 1;

for(int i=1;i<=P;i++) rval[(R[i]-1)*M+(C[i]-1)] = i;

rep(i, N+1) rep(j, M+1)


{
int tt[4];
get_val(i, j, tt);
if(tt[0]<tt[1]) add(tt[0], tt[1]-1, 1);
if(tt[2]<tt[3]) add(tt[2], tt[3]-1, 100);
}
}

int swap_seats(int a, int b)


{
++a; ++b;
change(R[a], C[a], b);
change(R[b], C[b], a);
swap(R[a], R[b]);
swap(C[a], C[b]);
return cnt[1];
}

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

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/06-04.txt", "r", stdin) ;


std::freopen("seats.out", "w", stdout) ;

int H = read_int();
int W = read_int();
int Q = read_int();

std::vector<int> R(H * W), C(H * W);


for (int i = 0; i < H * W; ++i)
{
R[i] = read_int();
C[i] = read_int();
}
std::vector<int> A(Q), B(Q);

for (int j = 0; j < Q; ++j)


{
A[j] = read_int();
B[j] = read_int();
}

auto t2 = clock();

give_initial_chart(H, W, R, C);

auto t3 = clock();

for (int j = 0; j < Q; ++j)


CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 151

{
int answer = swap_seats(A[j], B[j]);
printf("%d\n", answer);
}

auto t4 = clock();
fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.875
t3-t2 = 2.781
t4-t3 = 3.508

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


Press any key to continue.
*/

2.2.3 *Rezolvare detaliată

2.3 Werewolf
Problema 3 - Werewolf 100 de puncte

Authors: Mohammad Roghani (Iran), Helia Ziaei (Iran)

În prefectura Ibaraki din Japonia există N oraşe şi M drumuri. Oraşele sunt numerotate de la
0 la N  1 ı̂n ordine crescătoare a populaţiei. Fiecare drum conectează o pereche de oraşe distincte
şi poate fi traversat ı̂n ambele direcţii. Puteţi călători din orice oraş ı̂n orice alt oraş utilizând
unul sau mai multe din aceste drumuri.
Planificaţi Q călătorii, numerotate de la 0 la Q  1. Călătoria i (0 && Q  1) este o călătorie
din oraşul Si către oraşul Ei .
Sunteţi un vârcolac. Puteţi lua două forme: forma unui om şi forma unui lup. La ı̂nceputul
fiecărei călătorii aveţi forma unui om. La sfârşitul fiecărei călătorii trebuie să aveţi forma unui
lup. Pe parcursul călătoriei trebuie să vă transformaţi (să vă schimbaţi din forma unui om ı̂n
forma unui lup) exact o dată. Vă puteţi transforma doar când sunteţi ı̂ntr-un oraş (posibil Si sau
Ei ).
Viaţa de vârcolac nu este una uşoară. Trebuie să evitaţi oraşele mai puţin populate atunci
când aveţi forma unui om şi oraşele mai populate atunci când aveţi forma unui lup. Pentru orice
călătorie i (0 & i & Q  1), există doi ı̂ntregi Li şi Ri (0 & Li & Ri & N  1), care indică oraşele ce
trebuie evitate. Mai exact, pentru călătoria i, trebuie să evitaţi oraşele 0, 1, ..., Li  1 când aveţi
forma unui om şi să evitaţi oraşele Ri  1, Ri  2, ..., N  1 când aveţi forma unui lup. Aceasta
ı̂nseamnă că, ı̂n călătoria i, vă veţi transforma ı̂n unul din oraşele Li , Li  1, ..., Ri .
Sarcina dumneavoastră este să determinaţi pentru fiecare călătorie dacă este posibil să ajungeţi
din oraşul Si ı̂n oraşul Ei , respectând condiţiile descrise mai sus. Drumul ales poate fi de orice
lungime.

Detalii de implementare

Trebuie să implementaţi următoarea funcţie:


int[] check_validity(int N, int[] X, int[] Y, int[] S, int[] E,
int[]L, int[] R)
a N : numărul de oraşe.
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 152

a X şi Y : tablouri unidimensionale de dimensiune M . Pentru fiecare j (0 & j & M  1), oraşul
X j  este conectat printr-un drum direct cu oraşul Y j .
a S, E, L, şi R: tablouri unidimensionale de dimensiune Q, reprezentând călătoriile.

Luaţi la cunoştinţă că valorile M şi Q reprezintă dimensiunea tablourilor şi pot fi obţinute
după cum este indicat ı̂n Observaţiile de implementare.
Funcţia check_validity este apelată o singură dată pentru fiecare test. Această funcţie
trebuie să ı̂ntoarcă un tablou unidimensional A conţinând Q numere ı̂ntregi. Valoarea lui Ai
(0 & i & Q  1) trebuie să fie 1 dacă călătoria este posibilă satisfăcând constrângerile menţionate
anterior, sau 0 ı̂n caz contrar.

Exemple

Fie N 6, M 6, Q 3, X 5, 1, 1, 3, 3, 5, Y 1, 2, 3, 4, 0, 2, S 4, 4, 5, E 2, 2, 4,


L 1, 2, 3, şi R 2, 2, 4.

Grader-ul apelează
check_validity(6, [5, 1, 1, 3, 3, 5], [1, 2, 3, 4, 0, 2],
[4, 4, 5], [2, 2, 4], [1, 2, 3], [2, 2, 4]).

Figura 2.1: Puncte

Pentru călătoria 0, puteţi traversa din oraşul 4 către oraşul 2 ı̂n felul următor:
a Porniţi din oraşul 4 (aveţi forma unui om)
a Mergeţi către oraşul 3 (aveţi forma unui om)
a Mergeţi către oraşul 1 (aveţi forma unui om)
a Transformaţi-vă ı̂n forma unui lup (aveţi forma unui lup)
a Mergeţi către oraşul 2 (aveţi forma unui lup)
Pentru călătoriile 1 şi 2, nu puteţi traversa ı̂ntre oraşele indicate.
Prin urmare, programul dumneavoastră va ı̂ntoarce 1, 0, 0.
Fişierele sample-01-in.txt şi sample-01-out.txt din pachetul anexat sub forma de
arhivă corespund acestui exemplu. Un alt exemplu intrare/ieşire, este de asemenea disponibil ı̂n
pachet.

Restricţii

a 2 & N & 200000


a N  1 & M & 400000
a 1 & Q & 200000
a Pentru fiecare 0 & j & M  1
` 0 & Xj & N  1
` 0 & Yj & N  1
` Xj j Yj
a Puteţi călători ı̂ntre oricare două oraşe distincte utilizând drumurile.
a Fiecare pereche de oraşe este direct conectată prin cel mult un drum. ı̂n alte cuvinte, oricare
ar fi 0 & j $ k & M  1, Xj , Yj  j Xk , Yk  şi Yj , Xj  j Xk , Yk .
a Pentru fiecare 0 & i & Q  1
` 0 & Li & Si & N  1
` 0 & Ei & Ri & N  1
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 153

` Si j Ei
` Li & Ri

Subtaskuri

1. (7 puncte) N & 100, M & 200, Q & 100


2. (8 puncte) N & 3000, M & 6000, Q & 3000
3. (34 puncte) M N  1 şi orice oraş este incident cu cel mult 2 drumuri (oraşele sunt
conectate ı̂ntr-o linie)
4. (51 puncte) Fără constrângeri adiţionale

Exemplu de grader

Grader-ul local citeşte datele de intrare ı̂n următoarea formă:


a linia 1: N M Q
a linia 2  j (0 & j & M  1): Xj Yj
a linia 2  M  i (0 & i & Q  1): Si Ei Li Ri

Grader-ul local afişează valoarea ı̂ntoarsă de check_validity ı̂n următoarea formă:


alinia 1  i (0 & i & Q  1): Ai

2.3.1 Indicaţii de rezolvare

Given a connected undirected graph with N vertices and M edges. The vertices are numbered
from 0 through N  1.
Q queries are given. The query i (0 & i & Q  1) is represented by four integers Si , Ei , Li ,
Ri satisfying Li & Si and Ei & Ri . You want to travel from the vertex Si to the vertex Ei . Your
route must satisfy the following condition:
ˆ Assume that you visit the vertices V0 , V1 , V2 , ..., Vp in this order (V0 Si , Vp Ei ). Then
there is an index q (0 & q & p) such that Li & V0 , V1 , ..., Vq and Vq , Vq1 , ..., Vp & Ri are
satisfied.

You start the travel in human form, transform yourself from human form to wolf form at
the vertex Vq , and finish the travel in wolf form.
Your task is to determine whether it is possible to travel from the vertex Si to the vertex Ei .
Subtasks and Solutions
Subtask 1 (7 points)
N & 100, M & 200, Q & 100
You choose a V vertex where you transform yourself from human form to wolf form.
For each choice of V , you need to decide whether it is possible to travel from Si to V in human
form (i.e. only using vertices whose indices are ' Li ), and to decide whether it is possible to travel
from V to Ei in wolf form (i.e. only using vertices whose indices are & Ri ).
The time complexity of this solution is O QN N  M .
Subtask 2 (8 points)
N & 3 000, M & 6 000, Q & 3 000
Determine the set of vertices you can visit from Si in human form, and determine the set of
vertices you can visit from Ei in wolf form.
Then check whether these two sets intersect.
The time complexity of this solution is O Q N  M .
Subtask 3 (34 points)
The cities are located on a line. In other words, M N  1 and no city is directly connected
to more than 2 cities.
Let Ui be the set of the vertices which are reachable from Si by passing only vertices with
index at least Li . Similarly, let Vi be the set of the vertices which are reachable from Ei by passing
only vertices with index at most Ri . Note that Ui forms a range on the line on which cities are
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 154

located. This range can be efficiently computed using doubling or segment tree. Vi can be similarly
computed. Then, we can answer the query by checking whether these two ranges interesect.
Subtask 4 (51 points)
No additional constraints
We can construct a rooted tree so that Ui forms a subtree. This can be done using adding
vertices to a disjoint set union structure in the descending order of indices.
Then, using Euler-Tour on this tree, we can obtain a sequence of vertices and every Ui corre-
sponds to a contiguous segment of this sequence. We can compute similar sequence for Vi . Then,
we can answer the query by checking whether two segments for Ui and Vi shares a vertex. This
can be done by the sweep line algorithm with a segment tree. The time complexity of this solution
is O Q  M  log N .

2.3.2 Coduri sursă

Listing 2.3.1: compile cpp.sh


#!/bin/bash

TASK=werewolf

g++ -std=gnu++14 -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 2.3.2: werewolf.h


#include <vector>

std::vector<int> check_validity(int N,
std::vector<int> X, std::vector<int> Y,
std::vector<int> S, std::vector<int> E,
std::vector<int> L, std::vector<int> R);

Listing 2.3.3: werewolf.cpp


#include "werewolf.h"

std::vector<int> check_validity(int N,
std::vector<int> X, std::vector<int> Y,
std::vector<int> S, std::vector<int> E,
std::vector<int> L, std::vector<int> R)
{
int Q = S.size();
std::vector<int> A(Q);
for (int i = 0; i < Q; ++i)
{
A[i] = 0;
}
return A;
}

Listing 2.3.4: grader.cpp


#include <cstdio>
#include <cstdlib>
#include <vector>
#include "werewolf.h"

namespace
{

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 155

}
return x;
}

} // namespace

int main()
{
int N = read_int();
int M = read_int();
int Q = read_int();
std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);

for (int j = 0; j < M; ++j)


{
X[j] = read_int();
Y[j] = read_int();
}
for (int i = 0; i < Q; ++i)
{
S[i] = read_int();
E[i] = read_int();
L[i] = read_int();
R[i] = read_int();
}

std::vector<int> A = check_validity(N, X, Y, S, E, L, R);


for (size_t i = 0; i < A.size(); ++i)
{
printf("%d\n", A[i]);
}
return 0;
}

Listing 2.3.5: werewolf-model.cpp


// https://oj.uz/submission/75793
// https://oj.uz/problems/source/364

#include "werewolf.h"
#include <bits/stdc++.h>

using namespace std;

const int mxN=2e5;


int n, q, p[mxN], st[2][mxN], en[2][mxN], dt, anc[2][mxN],
r[mxN], ta[mxN], ft[mxN+1];
vector<int> adj1[mxN], adj2[mxN];
vector<array<int, 2>> b[mxN+1];
bool d[mxN];

int find(int x)
{
return x==r[x]?x:(r[x]=find(r[x]));
}

void dfs(int u, int st[mxN], int en[mxN])


{
st[u]=dt++;
for(int v : adj2[u])
dfs(v, st, en);
en[u]=dt;
}

void a(int st[mxN], int en[mxN], int anc[mxN])


{
for(int i=0; i<n; ++i)
r[i]=i;
for(int i=0; i<n; ++i)
{
for(int j : adj1[p[i]])
{
if(!d[j]||(j=find(j))==p[i])
continue;
r[j]=p[i];
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 156

adj2[p[i]].push_back(j);
}
for(array<int, 2> c : b[p[i]])
anc[c[1]]=find(c[0]);
b[p[i]].clear();
d[p[i]]=1;
}
dfs(p[n-1], st, en);
}

int qry(int i)
{
int r=0;
for(; i; i-=i&-i)
r+=ft[i];
return r;
}

vector<int> check_validity(int n2, vector<int> x, vector<int> y,


vector<int> s, vector<int> e,
vector<int> l, vector<int> r)
{
n=n2;
for(int i=0; i<x.size(); ++i)
{
adj1[x[i]].push_back(y[i]);
adj1[y[i]].push_back(x[i]);
}
q=s.size();
for(int i=0; i<n; ++i)
p[i]=n-1-i;
for(int i=0; i<q; ++i)
b[l[i]].push_back({s[i], i});
a(st[0], en[0], anc[0]);
for(int i=0; i<n; ++i)
{
adj2[i].clear();
p[i]=i;
}
memset(d, 0, n);
dt=0;
for(int i=0; i<q; ++i)
b[r[i]].push_back({e[i], i});
a(st[1], en[1], anc[1]);
for(int i=0; i<n; ++i)
ta[st[0][i]]=st[1][i];
for(int i=0; i<q; ++i)
{
b[st[0][anc[0][i]]].push_back({i, -1});
b[en[0][anc[0][i]]].push_back({i, 1});
}
vector<int> ans=vector<int>(q);
for(int i=0; i<n; ++i)
{
for(int j=ta[i]+1; j<=n; j+=j&-j)
++ft[j];
for(array<int, 2> c : b[i+1])
ans[c[0]]+=c[1]*
(qry(en[1][anc[1][c[0]]])-qry(st[1][anc[1][c[0]]]));
}
for(int i=0; i<q; ++i)
ans[i]=ans[i]>0;
return ans;
}
//======================================================================
namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 157

}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/04-20.txt", "r", stdin) ;


std::freopen("werewolf.out", "w", stdout) ;

int N = read_int();
int M = read_int();
int Q = read_int();
std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);

for (int j = 0; j < M; ++j)


{
X[j] = read_int();
Y[j] = read_int();
}
for (int i = 0; i < Q; ++i)
{
S[i] = read_int();
E[i] = read_int();
L[i] = read_int();
R[i] = read_int();
}

auto t2 = clock();

std::vector<int> A = check_validity(N, X, Y, S, E, L, R);

auto t3 = clock();

for (size_t i = 0; i < A.size(); ++i)


{
printf("%d\n", A[i]);
}

auto t4 = clock();

fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//======================================================================
/*
t2-t1 = 2.685
t3-t2 = 1.384
t4-t3 = 0.071

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


Press any key to continue.
*/
//======================================================================

Listing 2.3.6: werewolf 75105.cpp


// https://oj.uz/submission/75105

#include <stdio.h> // citire mai rapida !!!


#include <bits/stdc++.h>

using namespace std;

typedef long long llong;

int n, m, q;
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 158

vector<int> edge[200000];

struct UF
{
int par[200000];
UF()
{
for (int i = 0; i < 200000; ++i) par[i] = i;
}
int find(int x)
{
if (par[x] != x) return par[x] = find(par[x]);
return x;
}
} Luf, Ruf;

int Lpar[200000][20];
int Rpar[200000][20];
vector<int> Lchild[200000];
vector<int> Rchild[200000];

int Lst[200000];
int Led[200000];
int Rst[200000];
int Red[200000];
int LRd[200000];

void dfs(vector<int> child[], int st[], int ed[], int x, int &d)
{
st[x] = ++d;
for (int i : child[x])
{
dfs(child, st, ed, i, d);
}
ed[x] = d;
}

struct query
{
int i, t, x;
query(int i, int t, int x) : i(i), t(t), x(x) {}
bool operator<(const query &p) const
{
return t < p.t;
}
};

int seg[200001];
void update(int i)
{
while (i <= n)
{
++seg[i];
i += i & -i;
}
}

int sum(int i)
{
int ret = 0;
while (i)
{
ret += seg[i];
i -= i & -i;
}
return ret;
}

vector<int> check_validity(int N, vector<int> U, vector<int> V,


vector<int> X, vector<int> Y,
vector<int> L, vector<int> R)
{
n = N;
m = U.size();
q = X.size();
for (int i = 0; i < m; ++i)
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 159

{
edge[U[i]].push_back(V[i]);
edge[V[i]].push_back(U[i]);
}
for (int i = 0; i < n; ++i) Lpar[i][0] = 0;
for (int i = n; i--; )
{
for (int j : edge[i])
{
if (j < i) continue;
int x = Luf.find(i);
int y = Luf.find(j);
if (x == y) continue;
Luf.par[y] = x;
Lpar[y][0] = x;
Lchild[x].push_back(y);
}
}

for (int i = 0; i < n; ++i) Rpar[i][0] = n - 1;


for (int i = 0; i < n; ++i)
{
for (int j : edge[i])
{
if (i < j) continue;
int x = Ruf.find(i);
int y = Ruf.find(j);
if (x == y) continue;
Ruf.par[y] = x;
Rpar[y][0] = x;
Rchild[x].push_back(y);
}
}

for (int i = 1; i < 20; ++i)


{
for (int j = 0; j < n; ++j)
{
Lpar[j][i] = Lpar[Lpar[j][i - 1]][i - 1];
Rpar[j][i] = Rpar[Rpar[j][i - 1]][i - 1];
}
}

int ord;
ord = 0;
dfs(Lchild, Lst, Led, Luf.find(0), ord);
ord = 0;
dfs(Rchild, Rst, Red, Ruf.find(0), ord);
for (int i = 0; i < n; ++i) LRd[Lst[i] - 1] = Rst[i];

vector<query> qs;
for (int it = 0; it < q; ++it)
{
int x = X[it];
int y = Y[it];
int l = L[it];
int r = R[it];
for (int i = 20; i--; ) if (l <= Lpar[x][i]) x = Lpar[x][i];
for (int i = 20; i--; ) if (Rpar[y][i] <= r) y = Rpar[y][i];
qs.emplace_back(˜it, Lst[x] - 1, y);
qs.emplace_back(it, Led[x], y);
}

sort(qs.begin(), qs.end());
vector<int> ret(q, 0);
ord = 0;
for (query i : qs)
{
while (ord < i.t) update(LRd[ord++]);
int v = sum(Red[i.x]) - sum(Rst[i.x] - 1);
if (i.i < 0) ret[˜i.i] -= v;
else ret[i.i] += v;
}

for (int &i : ret) i = (i > 0);


return ret;
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 160

//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/04-19.txt", "r", stdin);


std::freopen("werewolf.out", "w", stdout);

int N = read_int();
int M = read_int();
int Q = read_int();

std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);

for (int j = 0; j < M; ++j)


{
X[j] = read_int();
Y[j] = read_int();
}

for (int i = 0; i < Q; ++i)


{
S[i] = read_int();
E[i] = read_int();
L[i] = read_int();
R[i] = read_int();
}

fclose(stdin);
auto t2 = clock();

std::vector<int> A = check_validity(N, X, Y, S, E, L, R);

auto t3 = clock();

for (size_t i = 0; i < A.size(); ++i)


{
printf("%d\n", A[i]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.765
t3-t2 = 2.094
t4-t3 = 0.063

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


Press any key to continue.
*/
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 161

Listing 2.3.7: werewolf 75296.cpp


// https://oj.uz/submission/75296

#include <stdio.h> // citire mai rapida !!!


#include <bits/stdc++.h>

using namespace std;

using pi = pair<int, int>;


const int MAXN = 400005;
const int MAXT = 530000;

struct seg
{
int tree[MAXT], lim;
void init(int n)
{
fill(tree, tree + MAXT, -1e9);
for(lim = 1; lim <= n; lim <<= 1);
}

void add(int x, int v)


{
x += lim;
tree[x] = max(tree[x], v);
while(x > 1)
{
x >>= 1;
tree[x] = max(tree[2*x], tree[2*x+1]);
}
}

int query(int s, int e)


{
s += lim;
e += lim;
int ret = -1e9;
while(s < e)
{
if(s%2 == 1) ret = max(ret, tree[s++]);
if(e%2 == 0) ret = max(ret, tree[e--]);
s >>= 1;
e >>= 1;
}
if(s == e) ret = max(ret, tree[s]);
return ret;
}
} seg;

struct disj
{
int pa[MAXN];
void init(int n)
{
iota(pa, pa + n + 1, 0);
}

int find(int x)
{
return pa[x] = (pa[x] == x ? x : find(pa[x]));
}

bool uni(int p, int q)


{
p = find(p);
q = find(q);
if(p > q) swap(p, q);
if(p == q) return 0;
pa[p] = q; return 1;
}
} disj;

struct graph
{
vector<int> gph[MAXN];
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 162

int din[MAXN], dout[MAXN], piv;

void add_edge(int p, int q)


{
gph[p].push_back(q);
}

void dfs(int x)
{
din[x] = piv;
if(gph[x].empty()) piv++;
for(auto &i : gph[x]) dfs(i);
dout[x] = piv;
}
} g1, g2;

struct queries
{
int s, l, e, r;
int rs, re, idx;
} qr[MAXN];

struct edges
{
int s, e, x;
} ed[MAXN];

std::vector<int> check_validity(int N,
std::vector<int> X, std::vector<int> Y,
std::vector<int> S, std::vector<int> E,
std::vector<int> L, std::vector<int> R)
{
int M = X.size();
int Q = S.size();
for(int i=0; i<Q; i++)
qr[i] = {S[i], L[i], E[i], R[i], -1, -1, i};

// make first tree


int vtx_num = N, ptr = 0;
qr[Q].l = -1e9;
disj.init(N * 2);
for(int i=0; i<M; i++)
ed[i] = {X[i], Y[i], min(X[i], Y[i])};

sort(qr, qr + Q, [&](const queries &a, const queries &b){


return a.l > b.l;
});

sort(ed, ed + M, [&](const edges &a, const edges &b){


return a.x > b.x;
});

for(int i=0; i<=Q; i++)


{
while(ptr < M && ed[ptr].x >= qr[i].l)
{
int l = disj.find(ed[ptr].s);
int r = disj.find(ed[ptr].e);
ptr++;
if(l == r) continue;
disj.uni(l, vtx_num);
disj.uni(r, vtx_num);
g1.add_edge(vtx_num, l);
g1.add_edge(vtx_num, r);
vtx_num++;
}
qr[i].rs = disj.find(qr[i].s);
}

// make second tree


vtx_num = N, ptr = 0;
qr[Q].r = 1e9;
disj.init(N * 2);
for(int i=0; i<M; i++) ed[i] = {X[i], Y[i], max(X[i], Y[i])};

sort(qr, qr + Q, [&](const queries &a, const queries &b){


CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 163

return a.r < b.r;


});

sort(ed, ed + M, [&](const edges &a, const edges &b){


return a.x < b.x;
});

for(int i=0; i<=Q; i++)


{
while(ptr < M && ed[ptr].x <= qr[i].r)
{
int l = disj.find(ed[ptr].s);
int r = disj.find(ed[ptr].e);
ptr++;
if(l == r) continue;
disj.uni(l, vtx_num);
disj.uni(r, vtx_num);
g2.add_edge(vtx_num, l);
g2.add_edge(vtx_num, r);
vtx_num++;
}
qr[i].re = disj.find(qr[i].e);
}

// both done
g1.dfs(2 * N - 2);
g2.dfs(2 * N - 2);

vector<pi> points;
vector<int> ans(Q);

for(int i=0; i<N; i++) points.emplace_back(g1.din[i], g2.din[i]);

sort(points.begin(), points.end());

sort(qr, qr + Q, [&](const queries &a, const queries &b){


return g1.dout[a.rs] < g1.dout[b.rs];
});

seg.init(N);
ptr = 0;
for(int i=0; i<Q; i++)
{
while(ptr < N && points[ptr].first < g1.dout[qr[i].rs])
{
seg.add(points[ptr].second, points[ptr].first);
ptr++;
}

if(seg.query(g2.din[qr[i].re], g2.dout[qr[i].re] - 1) >=


g1.din[qr[i].rs])
{
ans[qr[i].idx] = 1;
}
}

return ans;
}

//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 164

{
auto t1 = clock();

std::freopen("../in/04-19.txt", "r", stdin);


std::freopen("werewolf.out", "w", stdout);

int N = read_int();
int M = read_int();
int Q = read_int();

std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);

for (int j = 0; j < M; ++j)


{
X[j] = read_int();
Y[j] = read_int();
}

for (int i = 0; i < Q; ++i)


{
S[i] = read_int();
E[i] = read_int();
L[i] = read_int();
R[i] = read_int();
}

fclose(stdin);
auto t2 = clock();

std::vector<int> A = check_validity(N, X, Y, S, E, L, R);

auto t3 = clock();

for (size_t i = 0; i < A.size(); ++i)


{
printf("%d\n", A[i]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.797
t3-t2 = 2.411
t4-t3 = 0.063

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


Press any key to continue.
*/

Listing 2.3.8: werewolf 75793.cpp


// https://oj.uz/submission/75793

#include <stdio.h> // citire mai rapida !!!


#include <bits/stdc++.h>

using namespace std;

const int mxN=2e5;


int n, q, p[mxN], st[2][mxN], en[2][mxN], dt, anc[2][mxN],
r[mxN], ta[mxN], ft[mxN+1];
vector<int> adj1[mxN], adj2[mxN];
vector<array<int, 2>> b[mxN+1];
bool d[mxN];

int find(int x)
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 165

{
return x==r[x]?x:(r[x]=find(r[x]));
}

void dfs(int u, int st[mxN], int en[mxN])


{
st[u]=dt++;
for(int v : adj2[u])
dfs(v, st, en);
en[u]=dt;
}

void a(int st[mxN], int en[mxN], int anc[mxN])


{
for(int i=0; i<n; ++i)
r[i]=i;
for(int i=0; i<n; ++i)
{
for(int j : adj1[p[i]])
{
if(!d[j]||(j=find(j))==p[i])
continue;
r[j]=p[i];
adj2[p[i]].push_back(j);
}
for(array<int, 2> c : b[p[i]])
anc[c[1]]=find(c[0]);
b[p[i]].clear();
d[p[i]]=1;
}
dfs(p[n-1], st, en);
}

int qry(int i)
{
int r=0;
for(; i; i-=i&-i)
r+=ft[i];
return r;
}

vector<int> check_validity(int n2, vector<int> x, vector<int> y,


vector<int> s, vector<int> e,
vector<int> l, vector<int> r)
{
n=n2;
for(int i=0; i<x.size(); ++i)
{
adj1[x[i]].push_back(y[i]);
adj1[y[i]].push_back(x[i]);
}

q=s.size();
for(int i=0; i<n; ++i)
p[i]=n-1-i;
for(int i=0; i<q; ++i)
b[l[i]].push_back({s[i], i});

a(st[0], en[0], anc[0]);


for(int i=0; i<n; ++i)
{
adj2[i].clear();
p[i]=i;
}

memset(d, 0, n);

dt=0;
for(int i=0; i<q; ++i)
b[r[i]].push_back({e[i], i});

a(st[1], en[1], anc[1]);


for(int i=0; i<n; ++i)
ta[st[0][i]]=st[1][i];

for(int i=0; i<q; ++i)


CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 166

{
b[st[0][anc[0][i]]].push_back({i, -1});
b[en[0][anc[0][i]]].push_back({i, 1});
}

vector<int> ans=vector<int>(q);
for(int i=0; i<n; ++i)
{
for(int j=ta[i]+1; j<=n; j+=j&-j)
++ft[j];
for(array<int, 2> c : b[i+1])
ans[c[0]]+=c[1]*
(qry(en[1][anc[1][c[0]]])-qry(st[1][anc[1][c[0]]]));
}

for(int i=0; i<q; ++i)


ans[i]=ans[i]>0;

return ans;
}
//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/04-19.txt", "r", stdin);


std::freopen("werewolf.out", "w", stdout);

int N = read_int();
int M = read_int();
int Q = read_int();

std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);

for (int j = 0; j < M; ++j)


{
X[j] = read_int();
Y[j] = read_int();
}

for (int i = 0; i < Q; ++i)


{
S[i] = read_int();
E[i] = read_int();
L[i] = read_int();
R[i] = read_int();
}

fclose(stdin);
auto t2 = clock();

std::vector<int> A = check_validity(N, X, Y, S, E, L, R);

auto t3 = clock();

for (size_t i = 0; i < A.size(); ++i)


{
printf("%d\n", A[i]);
}

auto t4 = clock();
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 167

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.781
t3-t2 = 1.344
t4-t3 = 0.062

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


Press any key to continue.
*/

Listing 2.3.9: werewolf 81140.cpp


// https://oj.uz/submission/81140

#include <stdio.h> // citire mai rapida !!!


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

#define Fi first
#define Se second
#define pb(x) push_back(x)
#define szz(x) (int)x.size()
#define rep(i,n) for(int i=0;i<n;i++)
#define all(x) x.begin(),x.end()

typedef tuple<int, int, int> t3;

struct tree
{
vector <int> F[200020];
int lv[200020], rv[200020], cs = 0;
int up[200020][18];

void addE(int x, int y)


{ // x <- y
F[x].pb(y);
}

void dfs(int x)
{
lv[x] = ++cs;
for(int e : F[x])
{
up[e][0] = x;
for(int i=1;i<18;i++)
{
up[e][i] = up[ up[e][i-1] ][i-1];
}
dfs(e);
}
rv[x] = cs;
}

pii get_seg_g(int x, int l)


{
for(int i=17;i>=0;i--)
{
if(up[x][i] && up[x][i] >= l) x = up[x][i];
}
return pii(lv[x], rv[x]);
}
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 168

pii get_seg_l(int x, int r)


{
for(int i=17;i>=0;i--)
{
if(up[x][i] && up[x][i] <= r) x = up[x][i];
}
return pii(lv[x], rv[x]);
}
} T[2];

int pp[200020];
int Find(int x)
{
return pp[x] == x ? x : pp[x] = Find(pp[x]);
}

int val[200020];
vector <int> EE[200010];
int N;

vector <t3> pquery[200020];


int TT[200020];
int cnt[200020];

void pre_query(int idx, int S, int E, int L, int R)


{
pii p1 = T[0].get_seg_l(E, R);
pii p2 = T[1].get_seg_g(S, L);
pquery[p1.Se].pb(t3(p2.Se, idx, 1));
pquery[p1.Se].pb(t3(p2.Fi-1, idx, -1));
pquery[p1.Fi-1].pb(t3(p2.Se, idx, -1));
pquery[p1.Fi-1].pb(t3(p2.Fi-1, idx, 1));
}

std::vector<int> check_validity(int nn, std::vector<int> X,


std::vector<int> Y,
std::vector<int> S, std::vector<int> E,
std::vector<int> L, std::vector<int> R)
{
N = nn;
int M = szz(X);
for(int i=0;i<M;i++)
{
int x = X[i] + 1, y = Y[i] + 1;
EE[x].pb(y);
EE[y].pb(x);
}

for(int i=1;i<=N;i++) pp[i] = i;


for(int i=1;i<=N;i++) {
for(int e : EE[i]) if(e < i)
{
int x = Find(i), y = Find(e);
if(x == y) continue;
pp[y] = x;
T[0].addE(x, y);
}
}

for(int i=1;i<=N;i++) pp[i] = i;


for(int i=N;i;i--)
{
for(int e : EE[i]) if(e > i)
{
int x = Find(i), y = Find(e);
if(x == y) continue;
pp[y] = x;
T[1].addE(x, y);
}
}

T[0].dfs(N); T[1].dfs(1);
for(int i=1;i<=N;i++)
{
val[T[0].lv[i]] = T[1].lv[i];
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 169

int Q = szz(S);
rep(i, Q)
{
++S[i]; ++E[i]; ++L[i]; ++R[i];
pre_query(i, S[i], E[i], L[i], R[i]);
}

for(int i=1;i<=N;i++)
{
int x = val[i];
for(int j=x;j<200020;j+=(j&-j)) TT[j]++;
for(t3 e : pquery[i])
{
int y, idx, cc;
tie(y, idx, cc) = e;
int s = 0;
for(int j=y;j;j-=(j&-j)) s += TT[j];
cnt[idx] += cc * s;
}
}

vector <int> A(Q);


rep(i, Q)
{
A[i] = cnt[i] > 0 ? 1 : 0;
}

return A;
}

//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/04-19.txt", "r", stdin);


std::freopen("werewolf.out", "w", stdout);

int N = read_int();
int M = read_int();
int Q = read_int();

std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);

for (int j = 0; j < M; ++j)


{
X[j] = read_int();
Y[j] = read_int();
}

for (int i = 0; i < Q; ++i)


{
S[i] = read_int();
E[i] = read_int();
L[i] = read_int();
R[i] = read_int();
}

fclose(stdin);
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 170

auto t2 = clock();

std::vector<int> A = check_validity(N, X, Y, S, E, L, R);

auto t3 = clock();

for (size_t i = 0; i < A.size(); ++i)


{
printf("%d\n", A[i]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.75
t3-t2 = 2.159
t4-t3 = 0.063

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


Press any key to continue.
*/

Listing 2.3.10: werewolf 85439.cpp


// https://oj.uz/submission/85439

#include <stdio.h> // citire mai rapida !!!


#include <bits/stdc++.h>

using namespace std;

#define y0 ___y0
#define y1 ___y1
#define MP make_pair
#define MT make_tuple
#define PB push_back
#define PF push_front
#define fi first
#define se second
#define debug(x) cerr << #x << " = " << x << endl;
#define SZ(x) ((int) (x.size()))

const long double PI = 4.0 * atan(1.0);


const long double EPS = 1e-10;

#define MAXN 400013

typedef long long ll;


typedef long double ld;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pdd;
typedef pair<pii, pii> ppp;

int N, Q, T;
vector<int> edge[MAXN];
ppp quer[MAXN];
ppp range[MAXN];
vi ans;
int parent[2][MAXN];
int ord[2][MAXN];
int ancestor[2][20][MAXN];
int st[2][MAXN], ft[2][MAXN];
int pos[MAXN], arr[MAXN];
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 171

vector<int> edge1[2][MAXN];

struct dsu
{
int dsu[MAXN];
bool flag;

int get(int u)
{
return ((u == dsu[u]) ? u : dsu[u] = get(dsu[u]));
}

void merge(int u, int v)


{
u = get(u);
v = get(v);
if (flag)
{
if (u > v) swap(u, v);
}
else
{
if (u < v) swap(u, v);
}
dsu[u] = v;
// cerr << u << " => " << v << endl;
return;
}
};

void dfs(bool flag, int u)


{
st[flag][u] = T;
ft[flag][u] = T;
ord[flag][T] = u;
T++;
for (int v : edge1[flag][u])
{
if (v == parent[flag][u]) continue;
dfs(flag, v);
ft[flag][u] = ft[flag][v];
}
}

int fen[MAXN];

void update(int idx, int v)


{
for (int e = idx + 1; e <= N; e += e & (-e))
{
fen[e] += v;
}
return;
}

int query(int idx)


{
int res = 0;
for (int e = idx + 1; e > 0; e -= e & (-e))
{
res += fen[e];
}
return res;
}

vector<ppp> queries[MAXN];
dsu d[2];

vi check_validity(int n, vi X, vi Y, vi s, vi e, vi l, vi r)
{
N = n;
d[0].flag = true;
for (int i = 0; i < N; i++)
{
d[0].dsu[i] = i;
d[1].dsu[i] = i;
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 172

parent[0][i] = N;
parent[1][i] = N;
}

for (int i = 0; i < SZ(X); i++)


{
int u = X[i], v = Y[i];
edge[u].PB(v);
edge[v].PB(u);
}

Q = SZ(s);
ans.resize(Q);
for (int i = 0; i < Q; i++)
{
swap(s[i], e[i]);
quer[i] = MP(MP(l[i], r[i]), MP(s[i], e[i]));
}

for (int i = 0; i < N; i++)


{
for (int u : edge[i])
{
if (u > i) continue;
u = d[0].get(u);
if (u == i) continue;
parent[0][u] = i;
d[0].merge(u, i);
}
}

for (int i = N - 1; i >= 0; i--)


{
for (int u : edge[i])
{
if (u < i) continue;
u = d[1].get(u);
if (u == i) continue;
parent[1][u] = i;
d[1].merge(u, i);
}
}

for (int flag = 0; flag < 2; flag++)


{
// cerr << "tree\n";
for (int i = 0; i < N; i++)
{
if (parent[flag][i] != N)
{
edge1[flag][i].PB(parent[flag][i]);
edge1[flag][parent[flag][i]].PB(i);
// cerr << i << ’ ’ << parent[flag][i] << endl;
}
}
for (int i = 0; i < N; i++)
{
if (parent[flag][i] == N)
{
dfs(flag, i);
}
}
T = 0;
for (int i = 0; i <= 19; i++)
{
ancestor[flag][i][N] = N;
}
for (int i = 0; i < N; i++)
{
ancestor[flag][0][i] = parent[flag][i];
}
for (int i = 1; i <= 19; i++)
{
for (int j = 0; j < N; j++)
{
ancestor[flag][i][j] =
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 173

ancestor[flag][i - 1][ancestor[flag][i - 1][j]];


}
}
}

for (int i = 0; i < Q; i++)


{
//l, r, s, e
if (quer[i].se.fi > quer[i].fi.se ||
quer[i].se.se < quer[i].fi.fi)
{
ans[i] = 0;
continue;
}

//how far up can you go without exceeding r...


int u = quer[i].se.fi;
for (int j = 19; j >= 0; j--)
{
if (ancestor[0][j][u] > quer[i].fi.se ||
ancestor[0][j][u] == N) continue;
u = ancestor[0][j][u];
}

// cerr << "starts at " << i << " goes to " << u << endl;
range[i].fi = MP(st[0][u], ft[0][u]);
u = quer[i].se.se;
for (int j = 19; j >= 0; j--)
{
if (ancestor[1][j][u] < quer[i].fi.fi ||
ancestor[1][j][u] == N) continue;
u = ancestor[1][j][u];
}
range[i].se = MP(st[1][u], ft[1][u]);
}

//GIVEN TWO SUBARRAYS OF BIG ARRAY,


// CHECK IF THEY HAVE ANY COMMON ELEMENTS!
for (int i = 0; i < N; i++)
{
pos[ord[1][i]] = i;
}

for (int i = 0; i < N; i++)


{
arr[i] = pos[ord[0][i]];
// debug(arr[i]);
}

for (int i = 0; i < Q; i++)


{
if (range[i].fi.fi != 0)
{
queries[range[i].fi.fi - 1].PB(MP(MP(i, -1), range[i].se));
}
queries[range[i].fi.se].PB(MP(MP(i, 1), range[i].se));
}

for (int i = 0; i < N; i++)


{
update(arr[i], 1);
for (ppp p : queries[i])
{
ans[p.fi.fi] += (p.fi.se) *
(query(p.se.se) - query(p.se.fi - 1));
}
}

for (int i = 0; i < Q; i++)


{
ans[i] = (bool) ans[i];
}
return ans;

//idx, plusminus, l, r
//# of values between
CAPITOLUL 2. IOI 2018 2.3. WEREWOLF 174

//is there any value inside L1...R1 in arr[L0...R0]?


//now it’s just: in this subrange of an array,
// is there a value between L and R?
//(0...R) -> (L...N-1) with (S[i], E[i])
//u know that S[i] can go anywhere in its tree as long
// as path doesn’t contain #s >= R+1
//and then you can unfold the tree!
//oh each of them is like a tree,
// and each time you increase R you merge some trees together
return ans;
}

//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/04-19.txt", "r", stdin);


std::freopen("werewolf.out", "w", stdout);

int N = read_int();
int M = read_int();
int Q = read_int();

std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);

for (int j = 0; j < M; ++j)


{
X[j] = read_int();
Y[j] = read_int();
}

for (int i = 0; i < Q; ++i)


{
S[i] = read_int();
E[i] = read_int();
L[i] = read_int();
R[i] = read_int();
}

fclose(stdin);
auto t2 = clock();

std::vector<int> A = check_validity(N, X, Y, S, E, L, R);

auto t3 = clock();

for (size_t i = 0; i < A.size(); ++i)


{
printf("%d\n", A[i]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 175

return 0;
}
/*
t2-t1 = 0.843
t3-t2 = 2.047
t4-t3 = 0.063

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


Press any key to continue.
*/

2.3.3 *Rezolvare detaliată

2.4 Mechanical Doll


Problema 4 - Mechanical Doll 100 de puncte

Author: Tomasz Idziaszek (Poland)

O jucărie mecanică este o jucărie care repetă automat o secvenţă de mişcări. În Japonia multe
jucării mecanice au fost create ı̂ncă din vremuri antice.
Mişcările unei jucării mecanice sunt controlate de un circuit, constituit din dispozitive. Dis-
pozitivele sunt conectate prin tuburi. Fiecare dispozitiv are una sau două ieşiri şi un număr arbi-
trar (posibil zero) de intrări. Fiecare tub conectează ieşirea unui dispozitiv cu intrarea aceluiaşi
sau altui dispozitiv. Exact un tub este conectat cu fiecare intrare şi exact un tub este conectat cu
fiecare ieşire.
Pentru a descrie mişcările produse de jucărie, consideraţi o bilă plasată ı̂ntr-unul din dispozi-
tive. Bila se mişcă prin circuit. La fiecare moment al traseului acesteia, bila, părăseşte dispozitivul
folosind una din ieşiri, traversează tubul conectat de ieşire şi intră ı̂n dispozitivul de la capătul
celălalt al tubului.
Există trei tipuri de dispozitive: origine, ı̂ntrerupător şi comutator. Există exact o origine,
M ı̂ntrerupătoare şi S comutatoare (S poate fi zero). Voi trebuie să decideţi valoarea lui S. Fiecare
dispozitiv are un cod unic de ı̂nregistrare.
Originea este dispozitivul unde bila se află iniţial. Are exact o ieşire. Codul ei unic de
ı̂nregistrare este 0.

Figura 2.2: doll

Un ı̂ntrerupător face bila să execute o mişcare specială de fiecare dată când bila intră ı̂n acesta.
Orice ı̂ntrerupător are exact o ieşire. Codurile unice de ı̂nregistrare ale ı̂ntrerupătoarelor sunt de
la 1 la M .

Figura 2.3: doll

¬ ¬ ¬ ¬ ¬ ¬
Fiecare comutator are două ieşiri, denumite X şi Y . Starea unui comutator este fie X ,
¬ ¬
fie Y . După ce bila intră ı̂ntr-un comutator ı̂l părăseşte prin ieşirea dată de starea curentă a
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 176

¬ ¬
acestuia. După aceea, starea comutatorului se schimbă. Iniţial starea fiecărui comutator este X .
Codurile unice de ı̂nregistrare ale comutatoarelor sunt de la 1 la S.

Figura 2.4: doll

Vi se dă numărul de ı̂ntrerupătoare M . Vi se dă de asemenea o secvenţă A de lungime N , ale


cărei elemente reprezintă coduri unice de ı̂nregistrare ale ı̂ntrerupătoarelor. Fiecare ı̂ntrerupător
poate apărea de câteva (posibil zero) ori ı̂n secvenţa A. Sarcina voastră este să creaţi un circuit
care satisface următoarele condiţii:
a Bila se ı̂ntoarce ı̂n origine după nişte paşi.
¬ ¬
a Când bila se ı̂ntoarce prima dată ı̂n origine, starea fiecărui comutator este X .
a Bila se ı̂ntoarce prima dată ı̂n origine după ce a intrat ı̂n ı̂ntrerupătoare de exact N ori.
Codurile unice de ı̂nregistrare ale acestor ı̂ntrerupătoare, ı̂n ordinea ı̂n care trebuie să intre bila
ı̂n ele, sunt A0 , A1 , ..., AN 1 .
a Fie P numărul total de schimbări de stare ale comutatoarelor cauzate de bilă ı̂nainte ca
aceasta să se ı̂ntoarcă la origine prima dată. Valoarea lui P nu poate depăşi 20000000.
De asemenea, nu vreţi să folosiţi prea multe comutatoare.

Detalii de implementare

Trebuie să implementaţi următoarea procedură.


create_circuit(int M, int[] A)
a M : numărul de ı̂ntrerupătoare
a A: un tablou unidimensional cu N elemente, reprezentând codurile unice de ı̂nregistrare ale
ı̂ntrerupatoarelor prin care trebuie să intre bila, ı̂n ordinea ı̂n care trebuie să intre bila ı̂n ele.
a Această procedură este apelată exact o dată.
a Luaţi la cunoştinţă că valoarea lui N este dimensiunea tabloului A, şi poate fi obţinut după
cum este indicat ı̂n Observaţiile de implementare.
Programul vostru trebuie să apeleze următoarea procedură pentru a ı̂ntoarce răspunsul.
answer(int[] C, int[] X, int[] Y)
a C: un tablou unidimensional cu M  1 elemente. Ieşirea dispozitivului i (0 & i & M )
este conectată cu dispozitivul C i.
a X, Y : tablouri unidimensionale cu număr egal de elemente. Dimensiunea S a acestor tablouri
¬ ¬
reprezintă numărul de comutatoare. Pentru comutatorul j (1 & j & S) ieşirea X a acestuia este
¬ ¬
conectată cu dispozitivul X j  1 şi ieşirea Y cu dispozitivul Y j  1.
¬ ¬ ¬ ¬ ¬ ¬
a Orice element a lui C , X şi Y trebuie să fie un număr ı̂ntreg ı̂ntre S şi M inclusiv.
a S trebuie să fie cel mult 400000.
a Această procedură trebuie apelată exact o dată.
¬ ¬ ¬ ¬ ¬ ¬
a Circuitul reprezentat de C , X şi Y trebuie să satisfacă condiţiile din enunţul problemei.

Dacă vreuna din condiţiile de mai sus nu este satisfăcută programul vostru va fi evaluat ca
Wrong answer. Altfel, programul vostru va fi evaluat ca Accepted, iar punctajul vostru va fi
calculat pe baza lui S (vedeţi Subtask-uri).

Exemple

Fie M 4, N 4 şi A 1, 2, 1, 3.


CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 177

Grader-ul apelează create_circuit(4, [1, 2, 1,3]).

Figura 2.5: doll

Figura de mai sus arată un circuit care este descris de apelul


answer([1, -1, -2, 0, 2], [2, -2], [3, 1]).
Numerele din figură reprezintă codurile unice de ı̂nregistrare ale dispozitivelor.
Două comutatoare sunt folosite. Deci S 2.
¬ ¬
Iniţial, starea comutatoarelor 1 şi 2 este X .
Bila are următorul traseu:

Figura 2.6: doll

¬ ¬
a Când bila intră prima oară ı̂n comutatorul 1 starea acestuia este X . Deci, bila va traversa
¬ ¬
câtre ı̂ntrerupătorul 2. Apoi, starea comutatorului 1 devine Y .
¬ ¬
a Când bila intră ı̂n comutatorul 1 pentru a doua oară starea acestuia este Y . Deci bila va
¬ ¬
traversa câtre ı̂ntrerupătorul 3. Apoi, starea comutatorului 1 devine X .
Când bila revine prima oară ı̂n origine aceasta a trecut prin ı̂ntrerupătoarele 1, 2, 1, 3. Starea
¬ ¬
comutatoarelor 1 şi 2 este X . Valoarea lui P este 4. Deci circuitul satisface condiţiile.
Fişierul sample-01-in.txt din pachetul arhivat ataşat corespunde acestui exemplu. Alte
exemple se găsesc ı̂n acest pachet.

Restricţii

a 1 & M & 100000


a 1 & N & 200000
a 1 & Ak & M (0 & k & N  1)

Subtaskuri

Punctajele şi restricţiile pentru fiecare test sunt după cum urmează:
1. (2 puncte) Pentru fiecare i (1 & i & M ) numărul i apare cel mult o dată ı̂n secvenţa
A0 , A1 , ..., AN 1 .
2. (4 puncte) Pentru fiecare (1 & i & M ) numărul apare de cel mult de două ori ı̂n secvenţa
A0 , A1 , ..., AN 1 .
3. (10 puncte) Pentru fiecare i (1 & i & M ) numărul apare de cel mult patru ori ı̂n secvenţa
A0 , A1 , ..., AN 1 .
4. (10 puncte) N 16
5. (18 puncte) M 1
6. (56 puncte) Fără constrângeri adiţionale.
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 178

Pentru fiecare test, dacă programul vostru este evaluat ca Accepted, punctajul vostru este
calculat pe baza lui S:
a Dacă S & N  log2 N veţi primi punctajul complet pentru acel test.
a Pentru fiecare test din subtask-urile 5 şi 6, dacă N  log2 N $ S & 2N veţi primi un punctaj

parţial. Punctajul pentru acest test este 0.5  0.4   N2N S 2 multiplicat cu punctajul asociat
log2 N
subtask-ului.
a Altfel punctajul este 0.
Luaţi la cunoştinţă că punctajul pentru fiecare subtask este minimul punctajelor pentru fiecare
test din acel subtask.

Exemplu de grader

Grader-ul local citeşte datele de intrare de la intrarea standard ı̂n următoarea formă.
linia 1: M N
linia 2: A0 , A1 ,..., AN 1
Grader-ul local produce trei ieşiri.
Mai ı̂ntâi grader-ul local va afişa răspunsul vostru ı̂ntr-un fişier numit out.txt ı̂n următoarea
formă.
linia 1: S
linia 2  i (0 & i & M ): C i
linia 2  M  j (1 & j & S): X j  1 Y j  1
Apoi, grader-ul local va simula mişcarea bilei. Va afişa codurile unice de ı̂nregistrare ale
dispozitivelor prin care trece bila ı̂n fişierul log.txt.
În sfârşit, grader-ul local va afişa evaluarea răspunsului vostru la ieşirea standard.
a Dacă programul vostru este evaluat ca Accepted grader-ul local va afişa S şi P ı̂n următoarea
formă Accepted: S P .
a Dacă programul vostru este evaluat ca Wrong Answer, grader-ul local va afişa
Wrong answer: M SG. Semnificaţia lui M SG este după cum urmează:
` answered not exactly once: Procedura answer nu a fost apelată exact o dată.
` wrong array length: Dimensiunea tabloului C nu este , sau dimensiunile lui X şi Y
sunt diferite.
` over 400000 switches: este mai mare decât.
` wrong serial number: Există cel puţin un element ı̂n ’C’, ’X’ sau ’Y’ care este mai
mic decât sau mai mare decât.
` over 20000000 inversions: Bila nu a revenit la origine ı̂n de schimbări de stare
ale comutatoarelor.
` state ’Y’: Există un comutator al cărui stare este ’Y’ când bila revine prima oară ı̂n
origine.
` wrong motion: ı̂ntrerupătoarele care determină mişcarea bilei sunt diferite de cele din
secvenţa.
Luaţi la cunoştinţă că grader-ul local nu creează out.txt şi/sau log.txt atunci când pro-
gramul vostru este evaluat ca Wrong Answer.

2.4.1 Indicaţii de rezolvare

The problem is to create a circuit of devices.


Devices have one or two exits each. In a circuit, devices are connected: each exit is connected
to another (or possibly the same) device. The connections are decided by the contestants.
A ball is placed on one of the devices and travels through the circuit indefinitely. At each step,
the ball leaves the present device through one of its exits and enters the connected device.
There are three types of devices: the origin, triggers, and switches.
The origin is where the ball starts.
Triggers are numbered from 1 to M . Contestants are given an array A of length N .
The ball must visit triggers A0 , A1 , ..., AN 1 , A0 , A1 , ..., AN 1 , A0 , A1 , ... in this order.
Switches have two exits each. The two exits work alternately. Contestants can decide the
number of switches.
The task is to decide the connections so that the circuit satisfies the following conditions:
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 179

ˆ The ball returns to the origin after a finite number of steps.


ˆ By the time the ball first returns to the origin, every switch has been visited even times.
ˆ The ball first returns to the origin after entering triggers exactly times. The consecutive
serial numbers of these triggers are A0 , A1 , ..., AN 1 .

The score is determined according to the number of switches. Contestants get the full score
when N  log2 N switches or less are used.

Subtasks and Solutions


Subtask 1 (2 points)
Each trigger appears in A at most once. N  log2 N switches can be used.
ˆ Connect triggers without switches.

Subtask 2 (4 points)
Each trigger appears in A at most twice. N  log2 N switches can be used.
ˆ Put a switch after each trigger which appears twice.

Subtask 3 (10 points)


Each trigger appears in A at most 4 times. N  log2 N switches can be used.
ˆ For each trigger which appears more than twice, use 3 switches to branch its exit into 4 exits
(Imagine a balanced binary tree). For each trigger which appears 3 times, kill one out of the
first 3 exits by connecting it to the ”root” switch.

Subtask 4 (10 points)


N 16. 20 switches can be used.
4
ˆ Gather exits of all the triggers into one, and branch it into 16 exits with 15 (= 2  1)
switches.

Subtask 5 (18 points)


There is only one trigger.
ˆ (Solution A) (2N switches, half score) Use 2  1 switches for k such that 2N $ 2 ' N ,
k k

and kill excessive exits by connecting them to the ”root” switch.


ˆ (N  log2 N switches, full score) In addition to Solution A, skillfully choose which exits to
kill so that they are closely placed in the circuit. Then many switches are now unnecessary
because their exits are both connected to the ”root” switch.

Subtask 6 (56 points)


No additional constraints.
ˆ (2N switches, half score) Do Solution A for every trigger.
ˆ (Solution B) (2N switches, half score) Gather exits of all the triggers into one, and branch
it using Solution A.
ˆ (N  log2 N switches, full score) In addition to Solution B, save switches by skillfully choosing
which exits to kill.

2.4.2 Coduri sursă

Listing 2.4.1: compile cpp.sh


#!/bin/bash

TASK=doll

g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp


CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 180

Listing 2.4.2: dool.h


#include <vector>

void create_circuit(int M, std::vector<int> A);

void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y);

Listing 2.4.3: dool.cpp


#include "doll.h"

void create_circuit(int M, std::vector<int> A)


{
int N = A.size();
std::vector<int> C(M + 1);
C[0] = -1;
for (int i = 1; i <= M; ++i)
{
C[i] = 1;
}
std::vector<int> X(N), Y(N);
for (int k = 0; k < N; ++k)
{
X[k] = Y[k] = A[k];
}
answer(C, X, Y);
}

Listing 2.4.4: grader.cpp


#include <cstdio>
#include <cstdlib>
#include "doll.h"

namespace
{

constexpr int P_MAX = 20000000;


constexpr int S_MAX = 400000;

int M, N;
std::vector<int> A;

bool answered;
int S;
std::vector<int> IC, IX, IY;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

void simulate()
{
if (S > S_MAX)
{
char str[50];
sprintf(str, "over %d switches", S_MAX);
wrong_answer(str);
}
for (int i = 0; i <= M; ++i)
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 181

{
if (!(-S <= IC[i] && IC[i] <= M))
{
wrong_answer("wrong serial number");
}
}
for (int j = 1; j <= S; ++j)
{
if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
}

int P = 0;
std::vector<bool> state(S + 1, false);
int pos = IC[0];
int k = 0;
FILE *file_log = fopen("log.txt", "w");
fprintf(file_log, "0\n");
for (;;)
{
fprintf(file_log, "%d\n", pos);
if (pos < 0)
{
if (++P > P_MAX)
{
fclose(file_log);
char str[50];
sprintf(str, "over %d inversions", P_MAX);
wrong_answer(str);
}
state[-pos] = !state[-pos];
pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
}
else
{
if (pos == 0)
{
break;
}
if (k >= N)
{
fclose(file_log);
wrong_answer("wrong motion");
}
if (pos != A[k++])
{
fclose(file_log);
wrong_answer("wrong motion");
}
pos = IC[pos];
}
}
fclose(file_log);
if (k != N)
{
wrong_answer("wrong motion");
}
for (int j = 1; j <= S; ++j)
{
if (state[j])
{
wrong_answer("state ’Y’");
}
}
printf("Accepted: %d %d\n", S, P);
}

} // namespace

void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)


CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 182

{
if (answered)
{
wrong_answer("answered not exactly once");
}
answered = true;
// check if input format is correct
if ((int)C.size() != M + 1)
{
wrong_answer("wrong array length");
}
if (X.size() != Y.size())
{
wrong_answer("wrong array length");
}
S = X.size();
IC = C;
IX = X;
IY = Y;
}

int main()
{
M = read_int();
N = read_int();
A.resize(N);
for (int k = 0; k < N; ++k)
{
A[k] = read_int();
}

answered = false;
create_circuit(M, A);
if (!answered)
{
wrong_answer("answered not exactly once");
}
FILE *file_out = fopen("out.txt", "w");
fprintf(file_out, "%d\n", S);
for (int i = 0; i <= M; ++i)
{
fprintf(file_out, "%d\n", IC[i]);
}
for (int j = 1; j <= S; ++j)
{
fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
}
fclose(file_out);
simulate();
return 0;
}

Listing 2.4.5: doll 75123.cpp


// https://oj.uz/submission/75123

#include<fstream>
#include<iostream>

//#include <ctime>
#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include <cstdio>
#include <cstdlib>

#include "doll.h"

using namespace std;

int n;
int seg[1 << 19];
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 183

int bitRev(int x, int n)


{
int r = 0;
while (n >>= 1)
{
r <<= 1;
r ˆ= (x & 1);
x >>= 1;
}
return r;
}

const int inf = 1e9;

void create_circuit(int m, vector<int> A)


{
int sz = 1;
A.push_back(0);
n = A.size();
while (sz < n) sz <<= 1;
for (int i = 0; i < sz; ++i) seg[i + sz] = inf;
for (int i = 0, j = 0; i < sz; ++i)
{
int k = bitRev(i, sz);
if (k < sz - n) continue;
seg[k + sz] = A[j++];
}

int SN = 0;
vector<int> X, Y;
for (int i = sz; --i; )
{
if (seg[i << 1] == seg[i << 1 | 1])
{
seg[i] = seg[i << 1];
continue;
}
int S = SN++;
seg[i] = -(S + 1);
X.push_back(seg[i << 1]);
Y.push_back(seg[i << 1 | 1]);
}

for (int i = 0; i < SN; ++i)


{
if (X[i] == inf) X[i] = seg[1];
if (Y[i] == inf) Y[i] = seg[1];
}

answer(vector<int>(m + 1, seg[1]), X, Y);


}
//-------------- start grader ------------------

namespace
{

constexpr int P_MAX = 20000000;


constexpr int S_MAX = 400000;

int M, N;
std::vector<int> A;

bool answered;
int S;
std::vector<int> IC, IX, IY;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 184

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

void simulate()
{
if (S > S_MAX)
{
char str[50];
sprintf(str, "over %d switches", S_MAX);
wrong_answer(str);
}

for (int i = 0; i <= M; ++i)


{
if (!(-S <= IC[i] && IC[i] <= M))
{
wrong_answer("wrong serial number");
}
}

for (int j = 1; j <= S; ++j)


{
if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
}

int P = 0;
std::vector<bool> state(S + 1, false);
int pos = IC[0];
int k = 0;

FILE *file_log = fopen("log.txt", "w");


fprintf(file_log, "0\n");

for (;;)
{
fprintf(file_log, "%d\n", pos);
if (pos < 0)
{
if (++P > P_MAX)
{
fclose(file_log);
char str[50];
sprintf(str, "over %d inversions", P_MAX);
wrong_answer(str);
}

state[-pos] = !state[-pos];
pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
}
else
{
if (pos == 0)
{
break;
}

if (k >= N)
{
fclose(file_log);
wrong_answer("wrong motion");
}

if (pos != A[k++])
{
fclose(file_log);
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 185

wrong_answer("wrong motion");
}

pos = IC[pos];
}
}

fclose(file_log);

if (k != N)
{
wrong_answer("wrong motion");
}

for (int j = 1; j <= S; ++j)


{
if (state[j])
{
wrong_answer("state ’Y’");
}
}

printf("Accepted: %d %d\n", S, P);


}
} // namespace

void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)


{
if (answered)
{
wrong_answer("answered not exactly once");
}

answered = true;
// check if input format is correct
if ((int)C.size() != M + 1)
{
wrong_answer("wrong array length");
}

if (X.size() != Y.size())
{
wrong_answer("wrong array length");
}

S = X.size();
IC = C;
IX = X;
IY = Y;
}

int main()
{
std::freopen("../in/03-04.txt", "r", stdin);
//std::freopen("doll.out", "w", stdout);

auto t1 = clock();

M = read_int();
N = read_int();
A.resize(N);
for (int k = 0; k < N; ++k)
{
A[k] = read_int();
}
fclose(stdin);

auto t2 = clock();

answered = false;
create_circuit(M, A);
if (!answered)
{
wrong_answer("answered not exactly once");
}
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 186

auto t3 = clock();

FILE *file_out = fopen("out.txt", "w");


fprintf(file_out, "%d\n", S);

for (int i = 0; i <= M; ++i)


{
fprintf(file_out, "%d\n", IC[i]);
}

for (int j = 1; j <= S; ++j)


{
fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
}

fclose(file_out);

auto t4 = clock();

simulate();

auto t5 = clock();

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//-------------- end grader ------------------
/*
Accepted: 200006 3836160
t2-t1 = 0.328
t3-t2 = 0.078
t4-t3 = 0.359
t5-t4 = 3.896

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


Press any key to continue.
*/

Listing 2.4.6: dool 75619.cpp


// https://oj.uz/submission/75619

#include <bits/stdc++.h>

#include "doll.h"

using namespace std;

const int MAXN = 400005;

int vtx_number, total_leaf;


int X[MAXN], Y[MAXN], mp[MAXN];

void dfs(int s, int e, int v, int d)


{
vtx_number++;
if(e - s == 1)
{
if(s < total_leaf) X[vtx_number - 1] = -1;
else X[vtx_number - 1] = v;
Y[vtx_number - 1] = v ˆ (1 << d);
return;
}

int cur_vtx = vtx_number - 1;


int m = (s + e) / 2;
if(m < total_leaf)
{
X[cur_vtx] = -1;
}
else
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 187

{
X[cur_vtx] = -vtx_number - 1;
dfs(s, m, v, d + 1);
}

Y[cur_vtx] = -vtx_number - 1;
dfs(m+1, e, v ˆ (1 << d), d + 1);
}

void create_circuit(int M, std::vector<int> A)


{
if(A.size() == 1)
{
vector<int> C(M + 1, 0), X, Y;
C[0] = A[0];
answer(C, X, Y);
return;
}

vector<int> C(M + 1, -1);


C[0] = A[0];
int K = 0;
while((1 << K) < A.size()) K++;
total_leaf = (1 << K) - A.size();
A.erase(A.begin());
A.push_back(0);

dfs(0, (1 << K) - 1, 0, 0);

for(int i=0; i<vtx_number; i++)


{
if(X[i] >= 0) mp[X[i]] = 1;
if(Y[i] >= 0) mp[Y[i]] = 1;
}

int ptr = 0;
for(int i=0; i<(1<<K); i++)
{
if(mp[i])
{
mp[i] = A[ptr++];
}
}

for(int i=0; i<vtx_number; i++)


{
if(X[i] >= 0) X[i] = mp[X[i]];
if(Y[i] >= 0) Y[i] = mp[Y[i]];
}
vector<int> vx(X, X + vtx_number);
vector<int> vy(Y, Y + vtx_number);
answer(C, vx, vy);
}

//-------------- start grader ------------------

namespace
{
constexpr int P_MAX = 20000000;
constexpr int S_MAX = 400000;

int M, N;
std::vector<int> A;

bool answered;
int S;
std::vector<int> IC, IX, IY;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 188

return x;
}

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

void simulate()
{
if (S > S_MAX)
{
char str[50];
sprintf(str, "over %d switches", S_MAX);
wrong_answer(str);
}

for (int i = 0; i <= M; ++i)


{
if (!(-S <= IC[i] && IC[i] <= M))
{
wrong_answer("wrong serial number");
}
}

for (int j = 1; j <= S; ++j)


{
if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
}

int P = 0;
std::vector<bool> state(S + 1, false);
int pos = IC[0];
int k = 0;

FILE *file_log = fopen("log.txt", "w");


fprintf(file_log, "0\n");

for (;;)
{
fprintf(file_log, "%d\n", pos);
if (pos < 0)
{
if (++P > P_MAX)
{
fclose(file_log);
char str[50];
sprintf(str, "over %d inversions", P_MAX);
wrong_answer(str);
}

state[-pos] = !state[-pos];
pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
}
else
{
if (pos == 0)
{
break;
}

if (k >= N)
{
fclose(file_log);
wrong_answer("wrong motion");
}

if (pos != A[k++])
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 189

{
fclose(file_log);
wrong_answer("wrong motion");
}

pos = IC[pos];
}
}

fclose(file_log);

if (k != N)
{
wrong_answer("wrong motion");
}

for (int j = 1; j <= S; ++j)


{
if (state[j])
{
wrong_answer("state ’Y’");
}
}

printf("Accepted: %d %d\n", S, P);


}
} // namespace

void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)


{
if (answered)
{
wrong_answer("answered not exactly once");
}

answered = true;
// check if input format is correct
if ((int)C.size() != M + 1)
{
wrong_answer("wrong array length");
}

if (X.size() != Y.size())
{
wrong_answer("wrong array length");
}

S = X.size();
IC = C;
IX = X;
IY = Y;
}

int main()
{
std::freopen("../in/03-04.txt", "r", stdin);
//std::freopen("doll.out", "w", stdout);

auto t1 = clock();

M = read_int();
N = read_int();
A.resize(N);
for (int k = 0; k < N; ++k)
{
A[k] = read_int();
}
fclose(stdin);

auto t2 = clock();

answered = false;
create_circuit(M, A);
if (!answered)
{
wrong_answer("answered not exactly once");
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 190

auto t3 = clock();

FILE *file_out = fopen("out.txt", "w");


fprintf(file_out, "%d\n", S);

for (int i = 0; i <= M; ++i)


{
fprintf(file_out, "%d\n", IC[i]);
}

for (int j = 1; j <= S; ++j)


{
fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
}

fclose(file_out);

auto t4 = clock();

simulate();

auto t5 = clock();

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//-------------- end grader ------------------
/*
Accepted: 200006 3836160
t2-t1 = 0.359
t3-t2 = 0.016
t4-t3 = 0.328
t5-t4 = 3.714

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


Press any key to continue.
*/

Listing 2.4.7: dool 76623.cpp


// https://oj.uz/submission/76623
// https://oj.uz/profile/georgerapeanu

#include<fstream>
#include<iostream>

//#include <ctime>
#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include <cstdio>
#include <cstdlib>

#include "doll.h"

using namespace std;

#include <queue>

using namespace std;

int sw[(int)4e5][2];
int trig[(int)2e5];

void create_circuit(int M, vector<int> A)


{
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 191

for(int i = 0;i <= M;i++)


{
trig[i] = -1;
}

int N = A.size();
int lgN = 0;
while((1 << lgN) <= N)
{
lgN++;
}
lgN--;

int last_switch = -(lgN + 1);

queue< pair<int,int> > switches;

for(int i = 0;i < lgN;i++)


{

sw[i + 1][1] = -(i + 2);

if((N >> (lgN - i)) & 1)


{
sw[i + 1][0] = --last_switch;
switches.push({last_switch,lgN - i - 1});
}
else
{
sw[i + 1][0] = -1;
}
}

sw[lgN + 1][1] = 0;
if(N & 1)
{
sw[lgN + 1][0] = 0;
}
else
{
sw[lgN + 1][0] = -1;
}

while(!switches.empty())
{
int s = switches.front().first;
int lvl = switches.front().second;
switches.pop();
if(lvl != 0)
{
sw[-s][0] = --last_switch;
switches.push({last_switch,lvl - 1});
sw[-s][1] = --last_switch;
switches.push({last_switch,lvl - 1});
}
else
{
sw[-s][0] = sw[-s][1] = 0;
}
}

int ind = 0;
int node = -1;

while(node != 0)
{
if(sw[-node][0] == 0)
{
if(ind == A.size())
{
swap(sw[-node][0],sw[-node][1]);
node = 0;
continue;
}
sw[-node][0] = A[ind++];
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 192

swap(sw[-node][0],sw[-node][1]);
node = -1;
}
else
{
swap(sw[-node][0],sw[-node][1]);
node = sw[-node][1];
}
}

vector<int> X(-last_switch),Y(-last_switch),C(M + 1);

for(int i = 0;i <= M;i++)


{
C[i] = trig[i];
}

for(int i = 0;i < -last_switch;i++)


{
X[i] = sw[i + 1][0];
Y[i] = sw[i + 1][1];
}

answer(C,X,Y);
}
//-------------- start grader ------------------

namespace
{

constexpr int P_MAX = 20000000;


constexpr int S_MAX = 400000;

int M, N;
std::vector<int> A;

bool answered;
int S;
std::vector<int> IC, IX, IY;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

void simulate()
{
if (S > S_MAX)
{
char str[50];
sprintf(str, "over %d switches", S_MAX);
wrong_answer(str);
}

for (int i = 0; i <= M; ++i)


{
if (!(-S <= IC[i] && IC[i] <= M))
{
wrong_answer("wrong serial number");
}
}
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 193

for (int j = 1; j <= S; ++j)


{
if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
}

int P = 0;
std::vector<bool> state(S + 1, false);
int pos = IC[0];
int k = 0;

FILE *file_log = fopen("log.txt", "w");


fprintf(file_log, "0\n");

for (;;)
{
fprintf(file_log, "%d\n", pos);
if (pos < 0)
{
if (++P > P_MAX)
{
fclose(file_log);
char str[50];
sprintf(str, "over %d inversions", P_MAX);
wrong_answer(str);
}

state[-pos] = !state[-pos];
pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
}
else
{
if (pos == 0)
{
break;
}

if (k >= N)
{
fclose(file_log);
wrong_answer("wrong motion");
}

if (pos != A[k++])
{
fclose(file_log);
wrong_answer("wrong motion");
}

pos = IC[pos];
}
}

fclose(file_log);

if (k != N)
{
wrong_answer("wrong motion");
}

for (int j = 1; j <= S; ++j)


{
if (state[j])
{
wrong_answer("state ’Y’");
}
}

printf("Accepted: %d %d\n", S, P);


}
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 194

} // namespace

void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)


{
if (answered)
{
wrong_answer("answered not exactly once");
}

answered = true;
// check if input format is correct
if ((int)C.size() != M + 1)
{
wrong_answer("wrong array length");
}

if (X.size() != Y.size())
{
wrong_answer("wrong array length");
}

S = X.size();
IC = C;
IX = X;
IY = Y;
}

int main()
{
std::freopen("../in/03-04.txt", "r", stdin);
//std::freopen("doll.out", "w", stdout);

auto t1 = clock();

M = read_int();
N = read_int();
A.resize(N);
for (int k = 0; k < N; ++k)
{
A[k] = read_int();
}
fclose(stdin);

auto t2 = clock();

answered = false;
create_circuit(M, A);
if (!answered)
{
wrong_answer("answered not exactly once");
}

auto t3 = clock();

FILE *file_out = fopen("out.txt", "w");


fprintf(file_out, "%d\n", S);

for (int i = 0; i <= M; ++i)


{
fprintf(file_out, "%d\n", IC[i]);
}

for (int j = 1; j <= S; ++j)


{
fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
}

fclose(file_out);

auto t4 = clock();

simulate();

auto t5 = clock();

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 195

std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//-------------- end grader ------------------
/*
Accepted: 200006 3836160
t2-t1 = 0.406
t3-t2 = 0.219
t4-t3 = 0.296
t5-t4 = 3.227

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


Press any key to continue.
*/

Listing 2.4.8: dool 78782.cpp


// https://oj.uz/submission/78782
// https://oj.uz/profile/tincamatei

#include <bits/stdc++.h>

#include "doll.h"

using namespace std;

int lastid;

vector<int> X, Y;
vector<pair<int, pair<bool, int> > > poz;

int dfs(int depth, int N, int p, bool branch, int papa, int bit = 0)
{
if(depth >= 0)
{
int id = -((int)X.size()) - 1;
X.push_back(0);
Y.push_back(0);
if((1 << depth) > N)
{
X[-id - 1] = -1;
int r1 = dfs(depth - 1, N, p | (1 << bit), 1, id, bit + 1);
Y[-id - 1] = r1;
}
else
{
int r1 = dfs(depth - 1, (1 << depth), p, 0, id, bit + 1);
int r2 = dfs(depth - 1, N ˆ (1 << depth), p | (1 << bit), 1, id, bit +
1);
X[-id - 1] = r1;
Y[-id - 1] = r2;
}
return id;
}
else
{
if((1 << bit) - 1 == p)
{
return 0;
}
else
if(N == 0)
{
return -1;
}
else
{
poz.push_back(make_pair(p, make_pair(branch, papa)));
return 2;
}
}
}
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 196

void create_circuit(int M, std::vector<int> A)


{
vector<int> C(M + 1, -1);

int maxlg = 0;
int N = A.size();
while((1 << maxlg) <= N)
++maxlg;
lastid = -maxlg;

dfs(maxlg - 1, N, 0, 0, 0);

sort(poz.begin(), poz.end());

for(int i = 0; i < poz.size(); ++i)


{
if(poz[i].second.first)
Y[-poz[i].second.second - 1] = A[i];
else
X[-poz[i].second.second - 1] = A[i];
}

answer(C, X, Y);
}

//-------------- start grader ------------------

namespace
{

constexpr int P_MAX = 20000000;


constexpr int S_MAX = 400000;

int M, N;
std::vector<int> A;

bool answered;
int S;
std::vector<int> IC, IX, IY;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

void simulate()
{
if (S > S_MAX)
{
char str[50];
sprintf(str, "over %d switches", S_MAX);
wrong_answer(str);
}

for (int i = 0; i <= M; ++i)


{
if (!(-S <= IC[i] && IC[i] <= M))
{
wrong_answer("wrong serial number");
}
}

for (int j = 1; j <= S; ++j)


CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 197

{
if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
{
wrong_answer("wrong serial number");
}
}

int P = 0;
std::vector<bool> state(S + 1, false);
int pos = IC[0];
int k = 0;

FILE *file_log = fopen("log.txt", "w");


fprintf(file_log, "0\n");

for (;;)
{
fprintf(file_log, "%d\n", pos);
if (pos < 0)
{
if (++P > P_MAX)
{
fclose(file_log);
char str[50];
sprintf(str, "over %d inversions", P_MAX);
wrong_answer(str);
}

state[-pos] = !state[-pos];
pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
}
else
{
if (pos == 0)
{
break;
}

if (k >= N)
{
fclose(file_log);
wrong_answer("wrong motion");
}

if (pos != A[k++])
{
fclose(file_log);
wrong_answer("wrong motion");
}

pos = IC[pos];
}
}

fclose(file_log);

if (k != N)
{
wrong_answer("wrong motion");
}

for (int j = 1; j <= S; ++j)


{
if (state[j])
{
wrong_answer("state ’Y’");
}
}

printf("Accepted: %d %d\n", S, P);


}
} // namespace
CAPITOLUL 2. IOI 2018 2.4. MECHANICAL DOLL 198

void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)


{
if (answered)
{
wrong_answer("answered not exactly once");
}

answered = true;
// check if input format is correct
if ((int)C.size() != M + 1)
{
wrong_answer("wrong array length");
}

if (X.size() != Y.size())
{
wrong_answer("wrong array length");
}

S = X.size();
IC = C;
IX = X;
IY = Y;
}

int main()
{
std::freopen("../in/03-04.txt", "r", stdin);
//std::freopen("doll.out", "w", stdout);

auto t1 = clock();

M = read_int();
N = read_int();
A.resize(N);
for (int k = 0; k < N; ++k)
{
A[k] = read_int();
}
fclose(stdin);

auto t2 = clock();

answered = false;
create_circuit(M, A);
if (!answered)
{
wrong_answer("answered not exactly once");
}

auto t3 = clock();

FILE *file_out = fopen("out.txt", "w");


fprintf(file_out, "%d\n", S);

for (int i = 0; i <= M; ++i)


{
fprintf(file_out, "%d\n", IC[i]);
}

for (int j = 1; j <= S; ++j)


{
fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
}

fclose(file_out);

auto t4 = clock();

simulate();

auto t5 = clock();

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 199

std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
//-------------- end grader ------------------
/*
Accepted: 200006 3836160
t2-t1 = 0.343
t3-t2 = 0.36
t4-t3 = 0.312
t5-t4 = 3.656

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


Press any key to continue.
*/

2.4.3 *Rezolvare detaliată

2.5 Highway Tolls


Problema 5 - Highway Tolls 100 de puncte

Author: Shogo Murai (Japan)

În Japonia, oraşele sunt conectate de o reţea de autostrăzi. Reţeaua este formată din N oraşe
şi M autostrăzi. Fiecare autostradă conectează o pereche de oraşe distincte. Nu există două
autostrăzi distincte care conectează aceeaşi pereche de oraşe. Oraşele sunt numerotate de la 0 la
N  1 şi autostrăzile sunt numerotate de la 0 la M  1. Puteţi conduce pe oricare autostradă ı̂n
ambele direcţii. Puteţi călători, folosind autostrăzile, ı̂ntre oricare două oraşe.
O taxă este percepută pentru conducerea pe fiecare din autostrăzi. Taxa pe autostradă depinde
de condiţiile de trafic. Traficul poate fi relaxat sau intens. Când traficul este relaxat taxa este
de A yen (valută japoneză). Când traficul este intens taxa este de B yen. Se garantează că A $ B.
Luaţi la cunoştinţă că valorile A şi B sunt cunoscute.
Aveţi un dispozitiv, care, pentru condiţii date ale traficului pe toate autostrăzile calculează
taxa totală minimă pe care cineva trebuie să o achitate pentru a călători ı̂ntre oraşele S şi T
(S j T ), ı̂n condiţii de trafic specificate.
Totuşi, dispozitivul este doar un prototip. Valorile S şi T sunt fixate (adică ı̂n echipament)
şi necunoscute. Trebuie să determinaţi valorile S şi T . Pentru a realiza aceasta, planificaţi să
specificaţi dispozitivului anumite condiţii de trafic şi să folosiţi valorile taxelor calculate de acesta
pentru a deduce S şi T . Deoarece specificarea condiţiilor de trafic costă, nu doriţi să folosiţi
dispozitivul de multe ori.

Detalii de implementare

Trebuie să implementaţi următoarea procedură:


find_pair(int N, int[] U, int[] V, int A, int B)
a N : numărul de oraşe.
a U şi V : tablouri unidimensionale cu M elemente, unde M este numărul de autostrăzi care
conectează oraşele. Pentru fiecare i (0 & i & M  1), autostrada i conectează oraşele U i şi V i.
a A: taxa de autostradă când traficul este relaxat.
a B: taxa de autostradă când traficul este intens.
a Această procedură este apelată exact o dată pentru fiecare test.
a Luaţi la cunoştinţă că valoarea M reprezintă dimensiunea tablourilor şi poate fi obţinută
după cum este indicat ı̂n Observaţiile de implementare.
Procedura find_pair poate apela următoarea funcţie:
int64 ask(int[] w)
a Dimensiunea lui w trebuie să fie M . Tabloul w descrie condiţiile de trafic.
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 200

a Pentru fiecare i (0 & i & M  1), w i descrie condiţiile de trafic pe autostrada i. Valoarea
wi trebuie să fie 0 sau 1.
` w[i] = 0 ı̂nseamnă că traficul pe autostrada i este relaxat.
` w[i] = 1 ı̂nseamnă că traficul pe autostrada i este intens.
a Această funcţie ı̂ntoarce taxa totală minimă pentru călătoria ı̂ntre oraşele S şi T , ı̂n condiţiile
de trafic specificate de w.
a Această funcţie poate fi apelată de cel mult 100 de ori (pentru fiecare test).

find_pair trebuie să apeleze următoarea procedură pentru ı̂ntoarce răspunsul:


answer(int s, int t)
a s şi t trebuie să fie perechea S şi T (ordinea nu contează).
a Această procedură trebuie să fie apelată exact o dată.
Dacă vreuna din condiţiile descrise mai sus nu este respectată, programul este evaluat cu
Wrong Answer. ı̂n caz contrar programul este evaluat cu Accepted şi punctajul este calculat
după numărul de apeluri ale lui ask (vedeţi Subtask-uri).

Exemple

Fie N 4, M 4, U 0, 0, 0, 1, V 1, 2, 3, 2, A 1, B 3, S 1, şi T 3.


Grader-ul apelează
find_pair(4, [0, 0, 0, 1], [1, 2, 3, 2], 1, 3).

Figura 2.7: highway

În figura de mai sus, muchia cu numărul i corespunde autostrăzii i. Unele apeluri posibile
către ask şi valorile corespunzătoare ı̂ntoarse sunt descrise mai jos:

Apel Valoare ı̂ntoarsă


ask([0, 0, 0, 0]) 2
ask([0, 1, 1, 0]) 4
ask([1, 0, 1, 0]) 5
ask([1, 1, 1, 1]) 6

Tabelul 2.2: highway

La apelul funcţiei ask([0, 0, 0, 0]), traficul pe fiecare autostradă este relaxat şi taxa de
autostradă este 1. Ruta de cost minim de la S 1 la T 3 este 1 0 3.
Taxa totală pentru această rută este 2. Astfel, funcţia ı̂ntoarce 2.
Pentru un răspuns corect, procedura find_pair ar trebui să apeleze answer(1, 3) sau
answer(3, 1).
Fişierul sample-01-in.txt din pachetul arhivat anexat corespunde acestui exemplu.
Alte exemple sunt disponibile ı̂n acelaşi pachet.

Restricţii
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 201

a 2 & N & 90000


a 1 & M & 130000
a 1 & A $ B & 1000000000
a Pentru fiecare 0 & i & M  1
` 0 & U i & N  1
` 0 & V i & N  1
` U i j V i
a U i, V i j U j , V j  şi U i, V i j V j , U j  (0 & i $ j & M  1)
a Puteţi călători de la orice oraş la altul folosind autostrăzile.
a 0 & S & N 1
a 0 & T & N 1
a S j T
În această problemă, grader-ul NU este adaptiv. Aceasta ı̂nseamnă că S şi T sunt fixate la
ı̂nceputul rulării grader-ului şi nu vor depinde de ı̂ntrebările puse de soluţia dumneavoastră.

Subtaskuri

1. (5 puncte) unul dintre oraşele S sau T este 0, N & 100, M N  1


2. (7 puncte) unul dintre oraşele S sau T este 0, M N  1
3. (6 puncte) M N  1, U i i, V i i  1 (0 & i & M  1)
4. (33 puncte) M N  1
5. (18 puncte) A 1, B 2
6. (31 puncte) Fără constrângeri adiţionale
Să presupunem că programul dumneavoastră a fost evaluat ca Accepted, şi apelează ask de
X ori. Pentru acest test punctajul P , ı̂n funcţie de numărul subtask-ului, este calculat după cum
urmează:
a Subtask 1. P 5.
a Subtask 2. Dacă X & 60, P 7. Altfel 0.
a Subtask 3. Dacă X & 60, P 6. Altfel 0.
a Subtask 4. Dacă X & 60, P 33. Altfel 0.
a Subtask 5. Dacă X & 52, P 18. Altfel 0.
a Subtask 6.
` Dacă X & 50, P 31.
` Dacă 51 & X & 52, P 21.
` Dacă 53 & X, P 0.
Luaţi la cunoştinţă că punctajul pentru fiecare subtask este minimul punctajelor obţinute pe
testele din acel subtask.

Exemplu de grader

Grader-ul local citeşte detele de intrare ı̂n următorea formă:


linia 1: N M A B S T
linia 2  i (0 & i & M  1): U i V i
Dacă programul dumneavoastră a fost evaluat ca Accepted, grader-ul local va afişa
Accepted: q , unde q este numărul de apeluri ale lui ask.
Dacă programul dumneavoastră a fost evaluat ca Wrong Answer, grader-ul local va afişa
Wrong Answer: M SG , unde M SG este unul dintre mesajele:
a answered not exactly once: Procedura answer nu a fost apelată exact o dată.
a w is invalid: Dimensiunea parametrului w al funcţiei ask nu este M sau w i nu este
nici 0 nici 1 pentru unele valori ale lui i (0 & i & M  1).
a more than 100 calls to ask: Funcţia ask a fost apelată mai mult de 100 ori.
a rs, tx is wrong: Procedura answer este apelată cu o pereche incorectă s şi t.
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 202

2.5.1 Indicaţii de rezolvare

We are given an undirected and unweighted graph G with N vertices and M edges, and constants
1 & A $ B.
Two vertices s and t are fixed but they are unknown to us.
We want to find s and t by calling the following function fewer times:
ˆ For each edge in G, you arbitrarily assign the weight of A or B to turn G into a weighted
graph. Then, the function returns the length of a shortest path between s and t on (weighted)
G.

Subtask and Solutions


Overall constraints: N & 90 000, M & 130 000
Subtask 1 (5 points)
at most 100 function calls, G is a tree, N & 100, s is known
ˆ Test every possible t.

Subtask 2 (7 points)
at most 60 function calls, G is a tree, s is known
ˆ Sort the vertices 0 by the distance from s. Then t can be found using binary search

Subtask 3 (6 points)
at most 60 function calls, G is a path
ˆ Binary search

Subtask 4 (33 points)


at most 60 function calls, G is a tree
ˆ One function call with weight of every edge A to find the distance between s and t in
unweighted G.
ˆ An edge e on the shortest path between s and t can be found using binary search.
ˆ After removing e, the graph will be separated into two subtrees. Then you can perform the
solution of Subtask 2 twice to find s and t separately.
ˆ Centroid decomposition is possible but implementation will be tough.

Subtask 5 (18 points)


at most 52 function calls, A 1, B 2
ˆ Let S be a subset of V , where V is the set of vertices in G.
ˆ We set the weight of edges between S and V § to 1. The weights of other edges are set to 2.
Then, we can tell whether exactly one of s and t belongs to S by looking at the parity of
the answer to the call.
ˆ Thus we can compute s xor t. Using this, we can also find s and t themselves.

Subtask 6 (21 + 10 points)


at most 52 or 50 function calls (21 or 31 points, respectively)
Solution A: 21 points
ˆ A vertex v on a shortest path between s and t can be found using binary search.
ˆ Construct a BFS tree with root v. Then, we can use binary search again to find one of s
and t.
ˆ The other can be found similarly.

Solution B: 31 points
ˆ Find an edge e on a shortest path between s and t as in Subtask 4.
et e uv. Without loss of generality, we can assume s, u, v and t appears in this order on
this shortest path.
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 203

ˆ Then we can prove that s is strictly closer to u than to v. Similarly, t is closer to v than to
u.
ˆ Thus we have disjoint candidate sets S and T such that s and t are contained in S and T ,
respectively. At the same time, we can construct BFS trees of vertex sets S and T with
roots u and v, respectively. We can suppose a shortest path goes only through e and edges
in the BFS trees.
ˆ Now we can find s and t as in the last part of Subtask 4.

2.5.2 Coduri sursă

Listing 2.5.1: compile cpp.sh


#!/bin/bash

TASK=highway

g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 2.5.2: highway.h


#include <vector>

void find_pair(int N, std::vector<int> U, std::vector<int> V, int A, int B);

long long ask(const std::vector<int> &w);


void answer(int s, int t);

Listing 2.5.3: highway.cpp


#include "highway.h"

void find_pair(int N, std::vector<int> U,


std::vector<int> V, int A, int B)
{
int M = U.size();
for (int j = 0; j < 50; ++j)
{
std::vector<int> w(M);
for (int i = 0; i < M; ++i)
{
w[i] = 0;
}
long long toll = ask(w);
}
answer(0, N - 1);
}

Listing 2.5.4: grader.cpp


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <utility>
#include <vector>
#include "highway.h"

namespace
{
constexpr int MAX_NUM_CALLS = 100;
constexpr long long INF = 1LL << 61;

int N, M, A, B, S, T;
std::vector<int> U, V;
std::vector<std::vector<std::pair<int, int>>> graph;

bool answered, wrong_pair;


int num_calls;
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 204

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

} // namespace

long long ask(const std::vector<int> &w)


{
if (++num_calls > MAX_NUM_CALLS)
{
wrong_answer("more than 100 calls to ask");
}
if (w.size() != (size_t)M)
{
wrong_answer("w is invalid");
}
for (size_t i = 0; i < w.size(); ++i)
{
if (!(w[i] == 0 || w[i] == 1))
{
wrong_answer("w is invalid");
}
}

std::vector<bool> visited(N, false);


std::vector<long long> current_dist(N, INF);
std::queue<int> qa, qb;
qa.push(S);
current_dist[S] = 0;
while (!qa.empty() || !qb.empty())
{
int v;
if (qb.empty() ||
(!qa.empty() && current_dist[qa.front()] <=
current_dist[qb.front()]))
{
v = qa.front();
qa.pop();
}
else
{
v = qb.front();
qb.pop();
}
if (visited[v])
{
continue;
}
visited[v] = true;
long long d = current_dist[v];
if (v == T)
{
return d;
}
for (auto e : graph[v])
{
int vv = e.first;
int ei = e.second;
if (!visited[vv])
{
if (w[ei] == 0)
{
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 205

if (current_dist[vv] > d + A)
{
current_dist[vv] = d + A;
qa.push(vv);
}
}
else
{
if (current_dist[vv] > d + B)
{
current_dist[vv] = d + B;
qb.push(vv);
}
}
}
}
}
return -1;
}

void answer(int s, int t)


{
if (answered)
{
wrong_answer("answered not exactly once");
}

if (!((s == S && t == T) || (s == T && t == S)))


{
wrong_pair = true;
}

answered = true;
}

int main()
{
N = read_int();
M = read_int();
A = read_int();
B = read_int();
S = read_int();
T = read_int();

U.resize(M);
V.resize(M);
graph.assign(N, std::vector<std::pair<int, int>>());

for (int i = 0; i < M; ++i)


{
U[i] = read_int();
V[i] = read_int();
graph[U[i]].push_back({V[i], i});
graph[V[i]].push_back({U[i], i});
}

answered = false;
wrong_pair = false;
num_calls = 0;
find_pair(N, U, V, A, B);

if (!answered)
{
wrong_answer("answered not exactly once");
}
if (wrong_pair)
{
wrong_answer("{s, t} is wrong");
}
printf("Accepted: %d\n", num_calls);
return 0;
}

Listing 2.5.5: highway 74962.cpp


CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 206

// https://oj.uz/submission/74962

//#include <ctime>
#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include<bits/stdc++.h>

#include "highway.h"

#define X first
#define Y second

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

const int NN = 90005;


const int MM = 1300005;

const ll inf = 1e18;

int n, m, a, b, p;
ll def, dis[NN];
bool ban[NN], v1[NN], v2[NN];

vector<int> adj[NN], dij[NN], drv[NN], ord;


vector<pii> edg;

queue<int> q;

ll query ()
{
vector<int> V;
for(int i=0;i<m;i++)
{
V.push_back(ban[edg[i].X] || ban[edg[i].Y]);
}
return ask(V);
}

int getpiv ()
{
int S = 0, E = (int)ord.size() - 1;
while(S<E)
{
int Z = (S+E)/2;
for(int i=0;i<=Z;i++)
{
ban[ord[i]] = true;
}
query() != def ? E = Z : S = Z+1;
for(int i=0;i<=Z;i++)
{
ban[ord[i]] = false;
}
}
return ord[S];
}

void dfs1 (int C)


{
if(C == p || v1[C]) return;
v1[C] = true;
for(auto &T : drv[C])
{
dfs1(T);
}
}

void dfs2 (int C)


{
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 207

if(v2[C]) return;
v2[C] = true;
for(auto &T : dij[C])
{
dfs2(T);
}
}

void find_pair(int _N, vector<int> _U, vector<int> _V, int _A, int _B)
{
n = _N;
m = (int)_U.size();
a = _A;
b = _B;
for(int i=0;i<m;i++)
{
edg.push_back({_U[i]+1, _V[i]+1});
adj[_U[i]+1].push_back(_V[i]+1);
adj[_V[i]+1].push_back(_U[i]+1);
}
def = query();
for(int i=1;i<=n;i++)
{
ord.push_back(i);
}
p = getpiv();
ord.clear();
for(int i=1;i<p;i++)
{
ban[i] = true;
}
for(int i=1;i<=n;i++)
{
dis[i] = inf;
}
dis[p] = 0;
q.push(p);
while(!q.empty())
{
int C = q.front();
q.pop();
ord.push_back(C);
for(auto &T : adj[C])
{
if(T < p) continue;
if(dis[T] == inf)
{
dis[T] = dis[C] + 1;
q.push(T);
}
if(dis[T] == dis[C] - 1)
{
dij[T].push_back(C);
drv[C].push_back(T);
}
}
}
reverse(ord.begin(), ord.end());
int PA = getpiv();
ord.clear();
dfs1(PA);
for(int i=p;i<=n;i++)
{
if(v1[i]) dfs2(i);
}
for(int i=p;i<=n;i++)
{
if(!v2[i] && dis[i] + dis[PA] == def / a) ord.push_back(i);
}
int PB = getpiv();
ord.clear();
answer(PA-1, PB-1);
}

//-------------- start grader ------------------


CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 208

namespace
{
constexpr int MAX_NUM_CALLS = 100;
constexpr long long INF = 1LL << 61;

int N, M, A, B, S, T;
std::vector<int> U, V;
std::vector<std::vector<std::pair<int, int>>> graph;

bool answered, wrong_pair;


int num_calls;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

} // namespace

long long ask(const std::vector<int> &w)


{
if (++num_calls > MAX_NUM_CALLS)
{
wrong_answer("more than 100 calls to ask");
}
if (w.size() != (size_t)M)
{
wrong_answer("w is invalid");
}
for (size_t i = 0; i < w.size(); ++i)
{
if (!(w[i] == 0 || w[i] == 1))
{
wrong_answer("w is invalid");
}
}

std::vector<bool> visited(N, false);


std::vector<long long> current_dist(N, INF);
std::queue<int> qa, qb;
qa.push(S);
current_dist[S] = 0;
while (!qa.empty() || !qb.empty())
{
int v;
if (qb.empty() ||
(!qa.empty() && current_dist[qa.front()] <= current_dist[qb.front()]))
{
v = qa.front();
qa.pop();
}
else
{
v = qb.front();
qb.pop();
}
if (visited[v])
{
continue;
}
visited[v] = true;
long long d = current_dist[v];
if (v == T)
{
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 209

return d;
}
for (auto e : graph[v])
{
int vv = e.first;
int ei = e.second;
if (!visited[vv])
{
if (w[ei] == 0)
{
if (current_dist[vv] > d + A)
{
current_dist[vv] = d + A;
qa.push(vv);
}
}
else
{
if (current_dist[vv] > d + B)
{
current_dist[vv] = d + B;
qb.push(vv);
}
}
}
}
}
return -1;
}

void answer(int s, int t)


{
if (answered)
{
wrong_answer("answered not exactly once");
}

if (!((s == S && t == T) || (s == T && t == S)))


{
wrong_pair = true;
}

answered = true;
}

int main()
{

auto t1 = clock();

std::freopen("../in/06-41.txt", "r", stdin) ;


//std::freopen("highway.out", "w", stdout) ;

N = read_int();
M = read_int();
A = read_int();
B = read_int();
S = read_int();
T = read_int();
U.resize(M);
V.resize(M);

graph.assign(N, std::vector<std::pair<int, int>>());

for (int i = 0; i < M; ++i)


{
U[i] = read_int();
V[i] = read_int();
graph[U[i]].push_back({V[i], i});
graph[V[i]].push_back({U[i], i});
}

auto t2 = clock();

answered = false;
wrong_pair = false;
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 210

num_calls = 0;

find_pair(N, U, V, A, B);

auto t3 = clock();

if (!answered)
{
wrong_answer("answered not exactly once");
}
if (wrong_pair)
{
wrong_answer("{s, t} is wrong");
}
printf("Accepted: %d\n", num_calls);

auto t4 = clock();

fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
Accepted: 39
t2-t1 = 0.328
t3-t2 = 3.834
t4-t3 = 0

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


Press any key to continue.
*/

Listing 2.5.6: highway 77105.cpp


// https://oj.uz/submission/77105

//#include <ctime>
#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include <bits/stdc++.h>

#include "highway.h"

using namespace std;

using lint = long long;


using pi = pair<int, int>;
const int MAXN = 90005;

int dist[MAXN];
vector<int> gph[MAXN];

lint cut_ask(int m, vector<int> &c, vector<int> &u, vector<int> &v){


bitset<MAXN> vis;
for(auto &i : c) vis[i] = 1;
vector<int> query(m);
for(int i=0; i<m; i++) if(vis[u[i]] != vis[v[i]]) query[i] = 1;
return ask(query);
}

void find_pair(int N, std::vector<int> U, std::vector<int> V, int A, int B) {


int M = U.size();
for(int i=0; i<M; i++){
gph[U[i]].emplace_back(V[i]);
gph[V[i]].emplace_back(U[i]);
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 211

}
vector<int> v(M);
lint stdist = ask(v) / A;
int s = 0, e = M - 1;
while(s != e){
int m = (s + e) / 2;
fill(v.begin() + m + 1, v.end(), 0);
fill(v.begin(), v.begin() + m + 1, 1);
if(ask(v) != A * stdist) e = m;
else s = m + 1;
}
vector<int> bord[2], dist(N, 1e9);
queue<pi> que;
que.emplace(0, U[s]);
que.emplace(1, V[s]);
dist[U[s]] = dist[V[s]] = 0;
while(!que.empty()){
auto x = que.front(); que.pop();
bord[x.first].push_back(x.second);
for(auto &i : gph[x.second]){
if(dist[i] > dist[x.second] + 1){
dist[i] = dist[x.second] + 1;
que.emplace(x.first, i);
}
}
}
int S = -1, T = -1;
for(int i=0; i<2; i++){
s = 0, e = (int)bord[i].size() - 1;
while(s != e){
int m = (s + e + 1) / 2;
vector<int> C(bord[i].begin() + m, bord[i].end());
if(cut_ask(M, C, U, V) != stdist * A) s = m;
else e = m - 1;
}
if(i) S = bord[i][s];
else T = bord[i][s];
}
answer(S, T);
}

//-------------- start grader ------------------

namespace
{
constexpr int MAX_NUM_CALLS = 100;
constexpr long long INF = 1LL << 61;

int N, M, A, B, S, T;
std::vector<int> U, V;
std::vector<std::vector<std::pair<int, int>>> graph;

bool answered, wrong_pair;


int num_calls;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

void wrong_answer(const char *MSG)


{
printf("Wrong Answer: %s\n", MSG);
exit(0);
}

} // namespace
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 212

long long ask(const std::vector<int> &w)


{
if (++num_calls > MAX_NUM_CALLS)
{
wrong_answer("more than 100 calls to ask");
}
if (w.size() != (size_t)M)
{
wrong_answer("w is invalid");
}
for (size_t i = 0; i < w.size(); ++i)
{
if (!(w[i] == 0 || w[i] == 1))
{
wrong_answer("w is invalid");
}
}

std::vector<bool> visited(N, false);


std::vector<long long> current_dist(N, INF);
std::queue<int> qa, qb;
qa.push(S);
current_dist[S] = 0;
while (!qa.empty() || !qb.empty())
{
int v;
if (qb.empty() ||
(!qa.empty() && current_dist[qa.front()] <= current_dist[qb.front()]))
{
v = qa.front();
qa.pop();
}
else
{
v = qb.front();
qb.pop();
}
if (visited[v]) {
continue;
}
visited[v] = true;
long long d = current_dist[v];
if (v == T)
{
return d;
}
for (auto e : graph[v])
{
int vv = e.first;
int ei = e.second;
if (!visited[vv])
{
if (w[ei] == 0)
{
if (current_dist[vv] > d + A)
{
current_dist[vv] = d + A;
qa.push(vv);
}
}
else
{
if (current_dist[vv] > d + B)
{
current_dist[vv] = d + B;
qb.push(vv);
}
}
}
}
}
return -1;
}

void answer(int s, int t)


{
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 213

if (answered)
{
wrong_answer("answered not exactly once");
}

if (!((s == S && t == T) || (s == T && t == S)))


{
wrong_pair = true;
}

answered = true;
}

int main()
{

auto t1 = clock();

std::freopen("../in/06-41.txt", "r", stdin) ;


//std::freopen("highway.out", "w", stdout) ;

N = read_int();
M = read_int();
A = read_int();
B = read_int();
S = read_int();
T = read_int();
U.resize(M);
V.resize(M);

graph.assign(N, std::vector<std::pair<int, int>>());

for (int i = 0; i < M; ++i)


{
U[i] = read_int();
V[i] = read_int();
graph[U[i]].push_back({V[i], i});
graph[V[i]].push_back({U[i], i});
}

auto t2 = clock();

answered = false;
wrong_pair = false;
num_calls = 0;

find_pair(N, U, V, A, B);

auto t3 = clock();

if (!answered)
{
wrong_answer("answered not exactly once");
}
if (wrong_pair)
{
wrong_answer("{s, t} is wrong");
}
printf("Accepted: %d\n", num_calls);

auto t4 = clock();

fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
Accepted: 45
t2-t1 = 0.343
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 214

t3-t2 = 4.009
t4-t3 = 0

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


Press any key to continue.
*/

Listing 2.5.7: highway 78948.cpp


// https://oj.uz/submission/78948

//#include <ctime>
#include <time.h> /* clock */

//#include <cstdio>
#include <stdio.h> // citire mai rapida !!!

#include <bits/stdc++.h>

#include "highway.h"

using namespace std;

#define pb push_back
#define eb emplace_back
#define mp make_pair
#define f first
#define s second
#define all(a) (a).begin(),(a).end()
#define For(i,a,b) for(auto i=(a);i<(b);i++)
#define FOR(i,b) For(i,0,b)
#define Rev(i,a,b) for(auto i=(a);i>(b);i--)
#define REV(i,a) Rev(i,a,-1)
#define FORE(i,a) for(auto&&i:a)
#define sz(a) (int((a).size()))
#define MIN(a,b) ((a)=min((a),(b)))
#define MAX(a,b) ((a)=max((a),(b)))

using ll=long long;


using ld=long double;
using uint=unsigned int;
using ull=unsigned long long;

using pii=pair<int,int>;
using pll=pair<ll,ll>;
using pill=pair<int,ll>;
using plli=pair<ll,int>;
using pdd=pair<double,double>;
using pld=pair<ld,ld>;

constexpr const char nl=’\n’,sp=’ ’;

constexpr const int INT_INF=0x3f3f3f3f;


constexpr const ll LL_INF=0x3f3f3f3f3f3f3f3f;

constexpr const double D_INF=numeric_limits<double>::infinity();


constexpr const ld LD_INF=numeric_limits<ld>::infinity();

constexpr const double EPS=1e-9;

const int MAXN = 90005, MAXM = 130005;

int to[MAXN], ans[2];


bool tr[MAXM];
vector<int> adj[MAXN], verts[2];

void find_pair(int N, vector<int> U, vector<int> V, int A, int B)


{
int M = sz(U), lo = 0, hi = M - 1, mid;
FOR(i, M)
{
adj[U[i]].pb(i);
adj[V[i]].pb(i);
}
vector<int> Q(M, 0);
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 215

fill(tr, tr + M, 0);
ll DA = ask(Q);
while (lo < hi)
{
mid = lo + (hi - lo) / 2;
FOR(i, M) Q[i] = i <= mid;
if (ask(Q) == DA) lo = mid + 1;
else hi = mid;
}

fill(to, to + N, -1);

queue<pii> q;
q.emplace(U[lo], 0);
q.emplace(V[lo], 1);

tr[lo] = 1;
to[U[lo]] = to[V[lo]] = -INT_INF;

while (!q.empty())
{
pii v = q.front();
q.pop();
verts[v.s].pb(v.f);
FORE(e, adj[v.f])
{
int w = v.f ˆ U[e] ˆ V[e];
if (to[w] != -1) continue;
q.emplace(w, v.s);
tr[to[w] = e] = 1;
}
}

FOR(i, 2)
{
lo = 1, hi = sz(verts[i]) - 1;
while (lo <= hi)
{
mid = lo + (hi - lo) / 2;
FOR(j, M) Q[j] = !tr[j];
For(j, mid, sz(verts[i])) Q[to[verts[i][j]]] = 1;
if (ask(Q) == DA) hi = mid - 1;
else lo = mid + 1;
}
ans[i] = verts[i][hi];
}
answer(ans[0], ans[1]);
}

//-------------- start grader ------------------

namespace
{
constexpr int MAX_NUM_CALLS = 100;
constexpr long long INF = 1LL << 61;

int N, M, A, B, S, T;
std::vector<int> U, V;
std::vector<std::vector<std::pair<int, int>>> graph;

bool answered, wrong_pair;


int num_calls;

int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}

void wrong_answer(const char *MSG)


{
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 216

printf("Wrong Answer: %s\n", MSG);


exit(0);
}

} // namespace

long long ask(const std::vector<int> &w)


{
if (++num_calls > MAX_NUM_CALLS)
{
wrong_answer("more than 100 calls to ask");
}
if (w.size() != (size_t)M)
{
wrong_answer("w is invalid");
}
for (size_t i = 0; i < w.size(); ++i)
{
if (!(w[i] == 0 || w[i] == 1))
{
wrong_answer("w is invalid");
}
}

std::vector<bool> visited(N, false);


std::vector<long long> current_dist(N, INF);
std::queue<int> qa, qb;
qa.push(S);
current_dist[S] = 0;
while (!qa.empty() || !qb.empty())
{
int v;
if (qb.empty() ||
(!qa.empty() && current_dist[qa.front()] <= current_dist[qb.front()]))
{
v = qa.front();
qa.pop();
}
else
{
v = qb.front();
qb.pop();
}
if (visited[v])
{
continue;
}
visited[v] = true;
long long d = current_dist[v];
if (v == T)
{
return d;
}
for (auto e : graph[v])
{
int vv = e.first;
int ei = e.second;
if (!visited[vv])
{
if (w[ei] == 0)
{
if (current_dist[vv] > d + A)
{
current_dist[vv] = d + A;
qa.push(vv);
}
}
else
{
if (current_dist[vv] > d + B)
{
current_dist[vv] = d + B;
qb.push(vv);
}
}
}
CAPITOLUL 2. IOI 2018 2.5. HIGHWAY TOLLS 217

}
}
return -1;
}

void answer(int s, int t)


{
if (answered)
{
wrong_answer("answered not exactly once");
}

if (!((s == S && t == T) || (s == T && t == S)))


{
wrong_pair = true;
}

answered = true;
}

int main()
{

auto t1 = clock();

std::freopen("../in/06-41.txt", "r", stdin) ;


//std::freopen("highway.out", "w", stdout) ;

N = read_int();
M = read_int();
A = read_int();
B = read_int();
S = read_int();
T = read_int();
U.resize(M);
V.resize(M);

graph.assign(N, std::vector<std::pair<int, int>>());

for (int i = 0; i < M; ++i)


{
U[i] = read_int();
V[i] = read_int();
graph[U[i]].push_back({V[i], i});
graph[V[i]].push_back({U[i], i});
}

auto t2 = clock();

answered = false;
wrong_pair = false;
num_calls = 0;

find_pair(N, U, V, A, B);

auto t3 = clock();

if (!answered)
{
wrong_answer("answered not exactly once");
}
if (wrong_pair)
{
wrong_answer("{s, t} is wrong");
}
printf("Accepted: %d\n", num_calls);

auto t4 = clock();

fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 218

std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
Accepted: 45
t2-t1 = 0.343
t3-t2 = 4.04
t4-t3 = 0

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


Press any key to continue.
*/

2.5.3 *Rezolvare detaliată

2.6 Meetings
Problema 6 - Meetings 100 de puncte

Author: Riku Kawasaki (Japan)

Există N munţi aşezaţi ı̂ntr-o linie orizontală, numerotaţi de la 0 la N  1 de la stânga la


dreapta. Înălţimea muntelui i este Hi (0 & i & N  1). Exact o persoană locuieşte pe vârful
fiecărui munte.
Planificaţi să organizaţi Q şedinţe, numerotate de la 0 la Q  1. La şedinţa j (0 & j & Q  1) vor
participa toate persoanele care locuiesc ı̂n munţii de la Lj la Rj inclusiv (0 & Lj & Rj & N  1).
Pentru această şedinţă trebuie să alegeţi muntele x ca loc pentru şedinţă (Lj & x & Rj ).
Costul acestei şedinţe, ı̂n funţie de alegerea voastră, este apoi calculată după cum urmează:
a Costul participantului de la fiecare munte ( ) este ı̂nălţimea maximă a munţilor dintre munţii
şi inclusiv.
a În particular, costul participantului de la muntele este , ı̂nălţimea muntelui.
a Costul şedinţei este suma costurilor tuturor participanţilor.

Pentru fiecare şedinţă doriţi să găsiţi costul minim posibil de organizare a ei.
Luaţi la cunoştinţă că după fiecare şedinţă fiecare participant se ı̂ntoarce la muntele lui; deci
costul unei şedinţe nu este influenţat de şedinţele precedente.

Detalii de implementare

Trebuie să implementaţi următoarea funcţie:


int64[] minimum_costs(int[] H, int[] L, int[] R)
a H: un tablou unidimensional cu N elemente, reprezentând ı̂nălţimile munţilor.
a L şi R: tablouri unidimensionale cu Q elemente, reprezentând intervalul participanţilor la
şedinţe.
a Această funcţie trebuie să ı̂ntoarcă un tablou unidimensional C cu Q elemente. Valoarea Cj
(0 & j & Q  1) trebuie să fie costul minim posibil de organizare a şedinţei j.
a Luaţi la cunoştinţă că valorile N şi Q reprezintă dimensiunile tablourilor şi pot fi obţinute
după cum este indicat ı̂n Observaţiile de implementare.

Exemple

Fie N 4, H 2, 4, 3, 5, Q 2, L 0, 1 şi R 2, 3.


Grader-ul apelează minimum_costs([2, 4, 3, 5], [0, 1], [2, 3]).
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 219

Figura 2.8: Meetings

Şedinţa j 0 are Lj 0 şi Rj 2, deci participanţii vor fi de la munţii 0, 1 şi 2. Dacă muntele
0 este ales ca munte de organizare a şedinţei, atunci costul şedinţei 0 este calculat astfel:
a Costul participantului din muntele 0 este maxrH0 x 2.
a Costul participantului din muntele 1 este maxrH0 , H1 x 4.
a Costul participantului din muntele 2 este maxrH0 , H1 , H2 x 4.
a Deci, costul şedinţei este 2  4  4 10.
Este imposibil de organizat şedinţa cu un cost mai mic, deci costul minim al şedinţei 0 este 10.
Şedinţa j 1 are Lj 1 şi Rj 3, deci participanţii vor fi de la munţii 1, 2 şi 3. Dacă muntele
2 este ales ca munte de organizare a şedinţei, atunci costul şedinţei 1 este calculat astfel:
a Costul participantului din muntele 1 este maxrH1 , H2 x 4.
a Costul participantului din muntele 2 este maxrH2 x 3.
a Costul participantului din muntele 3 este maxrH2 , H3 x 5.
a Deci, costul şedinţei este 4  3  5 12.
Este imposibil de organizat şedinţa 1 cu un cost mai mic, deci costul minim al şedinţei 1 este
12.
Fişierele sample-01-in.txt şi sample-01-out.txt din pachetul anexat sub forma de
arhivă corespund acestui exemplu. Alte exemple sunt de asemenea disponibile ı̂n pachet.

Restricţii

a 1 & N & 750000


a 1 & Q & 750000
a 1 & Hi & 1000000000 (0 & i & N  1)
a 0 & Lj & Rj & N  1 (0 & j & Q  1)
a Lj , Rj  j Lk , Rk  (0 & j $ k & Q  1)

Subtaskuri

1. (4 puncte) N & 3000, Q & 10


2. (15 puncte) N & 5000, Q & 5000
3. (17 puncte) N & 100000, Q & 100000, Hi & 2 (0 & i & N  1)
4. (24 puncte) N & 100000, Q & 100000, Hi & 20 (0 & i & N  1)
5. (40 puncte) Fără constrângeri adiţionale

Exemplu de grader

Grader-ul local citeşte datele de intrare ı̂n următoarea formă:


a linia 1: N Q
a linia 2: H0 H1 ... HN 1
a linia 3  j (0 & j & Q  1): Lj Rj
Grader-ul local afişează valoarea ı̂ntoarsă de minimum_costs ı̂n următoarea formă:
a linia 1  j (0 & j & Q  1): Cj
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 220

2.6.1 Indicaţii de rezolvare

There are N mountains, numbered from 0 through N  1 from left to right. The height of the
mountain i is Hi (0 & i & N  1). Exactly one person lives on each mountain.
You are going to hold Q meetings, numbered from 0 through Q  1. To the meeting j (0 &
j & Q  1), you will invite all people living on the mountains between the mountain Lj and the
mountain Rj , inclusive.
For each meeting, you can choose a mountain as the meeting place. If the mountain x is chosen
as the meeting place, the cost of the meeting is calculated as follows:
ˆ The cost of the meeting is the sum of the costs of all participants.
ˆ The cost of the participant from the mountain y is the maximum height of mountains between
the mountain x and the mountain y, inclusive. Particularly, the cost of the participant from
the mountain x is Hx .

For each meeting, you want to find its minimum cost.


Constraints
ˆ 1 & N & 750 000
ˆ 1 & Q & 750 000
ˆ 1 & Hi & 1 000 000 000 (0 & i & N  1)
ˆ 0 & Lj & Rj & N  1 (0 & j & Q  1)
ˆ Lj , Rj  j Lk , Rk  (0 & j $ k & Q  1)

Subtasks and Solutions


Subtask 1 (4 points)

N & 3 000, Q & 10


If a meeting place is given, you can calculate the cost of a meeting in O N . Thus, by testing
2
every possible meeting place, the cost of a meeting can be calculated in O N  time.
2
The total time complexity is O N Q.
Subtask 2 (15 points)

N & 5 000, Q & 5 000


By iterating through the moutains with maintaining an upper envelope, you can get costs for
all meeting places in O N  time.
The total time complexity is O QN .
Subtask 3 (17 points)

N & 100 000, Q & 100 000, Hi & 2 0 & i & N  1


Find the longest contiguous subsequence consisting only of 1 by Segment Tree. The total time
complexity is O N  Q log N .
Subtask 4 (24 points)

N & 100 000, Q & 100 000, Hi & 20 0 & i & N  1


For each meeting, divide the range at the heighest mountains and recursively solve the problem.
If you pre-calculate the answer to some ranges, such as maximal ranges in which heights of
all mountains are at most some constant, and you prepare a proper data structure for Range
Minimum Queries, you can get the minimum cost of a meeting in O maxrHi x log N  time.
Pre-calculation can be done in O maxrHi xN  time.
The total time complexity is O maxrHi x N  Q log N .
Subtask 5 (40 points)
No additional constraints.
For convenience, let’s assume that all values of H are distinct (this does not matter much).
For each meeting, we can assume that the index of the optimal meeting place is greater than or
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 221

equal to argmaxL&i&R Hi , because by reversing the array H and solving the same problem we
can get a real answer.
We denote the problem of calculating the minimum cost of a meeting with the range L, R
as query L, R.
ˆ Let Cost L, R be the answer to the query L, R.
ˆ Let RangeL v  be the smallest x such that .
ˆ Similarly, let be the largest such that argmaxL&i&R Hi  v.
ˆ Also let S v  be the array of length

RangeR v   RangeL v   1

such that the i-th (0 & i & RangeR v   RangeL v ) value of S v  is

Cost RangeL v , RangeL v   i.

We are going to compute S v  for all v, and then it is easy to get answers to all queries. The
order of indices in which we compute S v  is very important. Here, we use depth-first-search
post-order of the cartesian tree of H.
We define the cartesian tree of H as the rooted tree such that lowest-common-ancestor of nodes
u and v is the node argmaxu&i&v Hi .
The cartesian tree can be obtained in linear time by an iteration with a stack data structure.
It can be easily seen that every node of the cartesian tree has at most two children, one to the
left and another to the right. Let lc v  be the left child of the node v and rc v  be the right child
of the node v (here we assume that the node v has two children).
Now the remaining task is to somehow merge S lc v  and S rc v  into S v . Clearly, first
some elements of S v  is exactly S lc v .
All we need is to compute Cost RangeL v , p, for all p (v & p).
Since Hv is the maximum value in the range RangeL v , RangeR v , you can see
Cost RangeL v , p
minrCost RangeL v , v   p  v   Hv , v  RangeL v   1  Hv  Cost v  1, px
and
Cost RangeL v , v   p  v   Hv   v  RangeL v   1  Hv  Cost v  1, p
& Cost RangeL v, v  p  1  v  Hv   v  RangeL v  1  Hv  Cost v  1, p  1
where p  1 & RangeR v .
The inequality follows from the observation that

Cost v  1, p  1  Cost v  1, p & max Hi  & Hv


v 1&i&p1

It indicates that there exists a certain index z such that

Cost RangeL v mp Cost RangeL v , v   p  v   Hv

for all p & z, and

Cost RangeL v , p v  RangeL v   1  Hv  Cost v  1, p

for all z $ p.
Therefore, you can get S v  in the following way:
ˆ Let T be the array obtained by adding a certain value to all elements of S rc v .
ˆ Update first some elements of T with a certain linear funtion.
ˆ Concatenate S lc v , Cost RangeL v , v , and T .

To carry out these operations fast, we use a compressed representation for S v . S v  is


represented by the list of ranges. Each range has a certain linear funciton such that the values of
S v  in the range can be calculaed by the linear function.
Adding a certain value can be done by lazy propagation.
To update first some elements, we simply iterate through T from the beginning. The ranges
before the break point is replaced by one range, so the total number of iterations is O N .
Concatenating two arrays can be done as follows:
ˆ We maintain end points of ranges in a global set and store the information of ranges in a
global array. Then, we do not have to do anything for ranges.
ˆ Concatenating laze propagation information for adding can be done by Weighted-union
heuristic:
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 222

` Let W s be the value which should be added to elements of the array s.


` When we concatenate two arrays s and t, we pick the smaller one and arrange the
elements of it so that W s W t.
` By Weighted-union heuristic this can be done in O N log N  time in total.

Getting the answer to a query requires one lower bound operation of the set. Thus in
O Q log N  time we can get answers to all queries.
The total time complexity is O N  Q log N .

2.6.2 Coduri sursă

Listing 2.6.1: compile cpp.sh


#!/bin/bash

TASK=meetings

g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 2.6.2: meetings.h


#include <vector>

std::vector<long long> minimum_costs(std::vector<int> H, std::vector<int> L,


std::vector<int> R);

Listing 2.6.3: meetings.cpp


#include "meetings.h"

std::vector<long long> minimum_costs(std::vector<int> H,


std::vector<int> L,
std::vector<int> R)
{
int Q = L.size();
std::vector<long long> C(Q);
for (int j = 0; j < Q; ++j)
{
C[j] = H[L[j]];
}
return C;
}

Listing 2.6.4: grader.cpp


#include <cstdio>
#include <cstdlib>
#include <vector>
#include "meetings.h"

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
int N = read_int();
int Q = read_int();
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 223

std::vector<int> H(N);
for (int i = 0; i < N; ++i)
{
H[i] = read_int();
}
std::vector<int> L(Q), R(Q);
for (int j = 0; j < Q; ++j)
{
L[j] = read_int();
R[j] = read_int();
}

std::vector<long long> C = minimum_costs(H, L, R);

for (size_t j = 0; j < C.size(); ++j)


{
printf("%lld\n", C[j]);
}
return 0;
}

Listing 2.6.5: meetings-model.cpp


// https://oj.uz/submission/120908

#include "meetings.h"

#include <algorithm>
#include <ctime>
#include <iostream>

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

using namespace std;

typedef long long ll;


typedef pair <int, int> pii;

struct minseg
{
pii T[2202020];
int sz = 1 << 20;

void init(vector <int> &H)


{
int i;

for(i=0; i<H.size(); i++)


{
T[i + sz] = pii(H[i], i);
}

for(i=sz-1; i; i--)
{
T[i] = max(T[i << 1], T[i << 1 | 1]);
}
}

int getmax(int l, int r)


{
pii ret(-1, -1);

l += sz; r += sz;
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 224

for(; l<=r; )
{
if(l & 1) ret = max(ret, T[l]);
if(˜r & 1) ret = max(ret, T[r]);
l = l + 1 >> 1;
r = r - 1 >> 1;
}

return ret.second;
}
};

struct line
{
ll x, a, b;
line() {}
line(ll x, ll a, ll b) : x(x), a(a), b(b) {}
};

struct deq
{
line D[808080];
int S[808080], E[808080];
ll V[808080];
int n;
bool t;

void init(bool _t, int _n)


{
int i;
t = _t; n = _n;
for(i=0; i<n; i++)
{
S[i] = i; E[i] = i;
D[i] = line(i, 0, 0);
}
}

void addval(int p, ll v)
{
if(t) p = n - 1 - p;
V[p] += v;
}

void addline(int p, ll x, ll a, ll b)
{
if(t)
{
p = n - 1 - p, x = n - 1 - x;
b = (n - 1) * a + b; a = -a;
}

int &s = S[p], &e = E[p];

b -= V[p];

for(; s<=e; e--)


{
if(D[e].a == a && D[e].b < b) return;
if(D[e].a * D[e].x + D[e].b < a * D[e].x + b)
{
D[++e] = line((D[e].b - b) / (a - D[e].a) + 1, a, b);
return;
}
}

D[++e] = line(x, a, b);


}

ll getval(int p, ll x)
{
if(t) p = n - 1 - p, x = n - 1 - x;

int k = upper_bound(D + S[p], D + E[p] + 1, x, [&](ll x, line l)


{
return x < l.x;
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 225

}) - D - 1;

return D[k].a * x + D[k].b + V[p];


}

void merge(int p, int q)


{
if(t) p = n - 1 - p, q = n - 1 - q;

int i;

if(E[p] - S[p] < E[q] - S[q])


{
swap(S[p], S[q]); swap(E[p], E[q]);
swap(V[p], V[q]);
}

if(S[p] < S[q])


{
for(i=S[q]; i<=E[q]; i++)
{
D[i].b += V[q] - V[p];
D[++E[p]] = D[i];
}
}
else
{
for(i=E[q]; i>=S[q]; i--)
{
D[i].b += V[q] - V[p];
D[--S[p]] = D[i];
}
}
}
};

minseg T;
deq DL, DR;

vector <int> H, L, R;
vector <int> Q[808080];
vector <ll> A;

int n;

int dnc(int s, int e)


{
if(s > e) return -1;

int m, l, r;
ll v, h;

m = T.getmax(s, e); h = H[m];


l = dnc(s, m - 1); r = dnc(m + 1, e);

for(int &q: Q[m])


{
if(L[q] == m && R[q] == m) A[q] = h;
else if(L[q] == m) A[q] = DR.getval(r, R[q]) + h;
else if(R[q] == m) A[q] = DL.getval(l, L[q]) + h;
else A[q] = min(DL.getval(l, L[q]) + h * (R[q] - m + 1),
DR.getval(r, R[q]) + h * (m - L[q] + 1));
}

if(l != -1) DL.merge(m, l);


DL.addval(m, h * (e - m + 1));
DL.addline(m, s, -h, (r != -1? DL.getval(r, m + 1) : 0) + h * (m + 1));
if(r != -1) DL.merge(m, r);

if(r != -1) DR.merge(m, r);


DR.addval(m, h * (m - s + 1));
DR.addline(m, e, h, (l != -1? DR.getval(l, m - 1) : 0) - h * (m - 1));
if(l != -1) DR.merge(m, l);

return m;
}
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 226

vector <ll> minimum_costs(vector <int> _H, vector <int> _L, vector <int> _R)
{
int i;

swap(H, _H); swap(L, _L); swap(R, _R);


n = H.size(); A.resize(L.size());

T.init(H); DL.init(0, n); DR.init(1, n);

for(i=0; i<L.size(); i++)


{
Q[T.getmax(L[i], R[i])].push_back(i);
}

dnc(0, n - 1);

return A;
}
// =======================================================================
int main()
{
auto t1 = clock();

std::freopen("../in/05-03.txt", "r", stdin) ;


std::freopen("meetings.out", "w", stdout) ;

int N = read_int();
int Q = read_int();
std::vector<int> H(N);
for (int i = 0; i < N; ++i) {
H[i] = read_int();
}
std::vector<int> L(Q), R(Q);
for (int j = 0; j < Q; ++j)
{
L[j] = read_int();
R[j] = read_int();
}

auto t2 = clock();

std::vector<long long> C = minimum_costs(H, L, R);

auto t3 = clock();

for (size_t j = 0; j < C.size(); ++j)


{
printf("%lld\n", C[j]);
}

auto t4 = clock();

fclose(stdout);

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// =======================================================================
/*
t2-t1 = 4.192
t3-t2 = 2.416
t4-t3 = 0.97

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


Press any key to continue.
*/

Listing 2.6.6: meetings 76204.cpp


CAPITOLUL 2. IOI 2018 2.6. MEETINGS 227

// https://oj.uz/submission/76204

#include <stdio.h> // citire mai rapida !!!

#include <bits/stdc++.h>

using namespace std;

typedef long long llong;


typedef pair<int, int> pii;

int n;

const llong inf = 1e18;


vector<llong> ans;
pii seg[1 << 21];

struct query
{
int i, l, r;
query(int i, int l, int r) : i(i), l(l), r(r) {}
};

vector<query> qs[750001];
pii arr[750001];

void init(int i, int s, int e)


{
if (s == e)
{
seg[i] = arr[s];
return;
}
int m = (s + e) / 2;
init(i << 1, s, m);
init(i << 1 | 1, m + 1, e);
seg[i] = max(seg[i << 1], seg[i << 1 | 1]);
}

pii getMax(int i, int s, int e, int x, int y)


{
if (e < x || y < s) return pii(0, 0);
if (x <= s && e <= y) return seg[i];
int m = (s + e) / 2;
return max(getMax(i << 1, s,m,x,y), getMax(i << 1 | 1, m+1,e,x,y));
}

struct line
{
int s, e;
llong m, b;
line() {}
line(int s, int e, llong m, llong b) : s(s), e(e), m(m), b(b) {}

llong get(int x) const


{
return m * x + b;
}

bool operator<=(const line &p) const


{
return get(p.s) <= p.get(p.s) && get(p.e) <= p.get(p.e);
}
} ls[750001];

struct que
{
int s, e;
llong add;
que(int s, int e) : s(s), e(e), add(0) {}
llong getRight() const
{
if (s > e) return 0;
return ls[e].get(ls[e].e) + add;
}
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 228

int size() const


{
return e - s + 1;
}
line front()
{
line ret = ls[s];
ret.b += add;
return ret;
}
line back()
{
line ret = ls[e];
ret.b += add;
return ret;
}
void pop_front() { ++s; }
void pop_back() { --e; }
void push_front(line x)
{
x.b -= add;
ls[--s] = x;
}
void push_back(line x)
{
x.b -= add;
ls[++e] = x;
}
llong get(int x) const
{
int st = s, ed = e;
while (st <= ed)
{
int md = (st + ed) / 2;
if (ls[md].s <= x && x <= ls[md].e) return ls[md].get(x)+add;
if (x < ls[md].s) ed = md - 1;
else st = md + 1;
}
return 0;
}
};

que getQuery(int s, int e)


{
if (s > e) return que(s, e);
int m = abs(getMax(1, 1, n, s, e).second);
que L = getQuery(s, m - 1);
que R = getQuery(m + 1, e);
for (query q : qs[m])
{
ans[q.i] = min(ans[q.i], R.get(q.r) + (m-q.l+1ll)*arr[m].first);
}
R.add += (m - s + 1ll) * arr[m].first;
line l(m, m, arr[m].first, L.getRight() + (1ll - m) * arr[m].first);
while (R.size() && l <= R.front())
{
l.e = R.front().e;
R.pop_front();
}
if (R.size())
{
line r = R.front();
R.pop_front();
int st = r.s, ed = r.e;
while (st < ed)
{
int md = (st + ed) / 2;
if (l.get(md) < r.get(md)) st = md + 1;
else ed = md;
}
l.e = st - 1;
r.s = st;
R.push_front(r);
}
R.push_front(l);
if (L.size() < R.size())
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 229

{
while (L.size())
{
R.push_front(L.back());
L.pop_back();
}
return R;
}
else
{
while (R.size())
{
L.push_back(R.front());
R.pop_front();
}
return L;
}
}

vector<llong> minimum_costs(vector<int> H, vector<int> L, vector<int> R)


{
n = H.size();
int q = L.size();
ans.resize(q, inf);

for (int i = 0; i < n; ++i) arr[i + 1] = pii(H[i], i + 1);


init(1, 1, n);
for (int i = 0; i < q; ++i)
{
int mx = getMax(1, 1, n, L[i] + 1, R[i] + 1).second;
qs[mx].emplace_back(i, L[i] + 1, R[i] + 1);
}
getQuery(1, n);

for (int i = 1; i <= n; ++i) qs[i].clear();

for (int i = 0; i < n; ++i) arr[n - i] = pii(H[i], i - n);


init(1, 1, n);
for (int i = 0; i < q; ++i)
{
int mx = -getMax(1, 1, n, n - R[i], n - L[i]).second;
qs[mx].emplace_back(i, n - R[i], n - L[i]);
}
getQuery(1, n);
return ans;
}

//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
std::freopen("../in/05-12.txt", "r", stdin);
std::freopen("meetings.out", "w", stdout);

auto t1 = clock();

int N = read_int();
int Q = read_int();
std::vector<int> H(N);
for (int i = 0; i < N; ++i)
{
H[i] = read_int();
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 230

}
std::vector<int> L(Q), R(Q);
for (int j = 0; j < Q; ++j)
{
L[j] = read_int();
R[j] = read_int();
}

fclose(stdin);
auto t2 = clock();

std::vector<long long> C = minimum_costs(H, L, R);

auto t3 = clock();

for (size_t j = 0; j < C.size(); ++j)


{
printf("%lld\n", C[j]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 1.205
t3-t2 = 11.063
t4-t3 = 0.765

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


Press any key to continue.
*/

Listing 2.6.7: meetings 120908.cpp


// https://oj.uz/submission/120908
#include<fstream>
#include<iostream>

//#include <ctime>
#include <time.h> /* clock */

#include <stdio.h> // citire mai rapida !!!

#include <algorithm>

using namespace std;

typedef long long ll;


typedef pair <int, int> pii;

struct minseg
{
pii T[2202020];
int sz = 1 << 20;

void init(vector <int> &H)


{
int i;

for(i=0; i<H.size(); i++)


{
T[i + sz] = pii(H[i], i);
}

for(i=sz-1; i; i--)
{
T[i] = max(T[i << 1], T[i << 1 | 1]);
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 231

}
}

int getmax(int l, int r)


{
pii ret(-1, -1);

l += sz; r += sz;
for(; l<=r; )
{
if(l & 1) ret = max(ret, T[l]);
if(˜r & 1) ret = max(ret, T[r]);
l = l + 1 >> 1;
r = r - 1 >> 1;
}

return ret.second;
}
};

struct line
{
ll x, a, b;
line() {}
line(ll x, ll a, ll b) : x(x), a(a), b(b) {}
};

struct deq
{
line D[808080];
int S[808080], E[808080];
ll V[808080];
int n;
bool t;

void init(bool _t, int _n)


{
int i;

t = _t; n = _n;

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


{
S[i] = i; E[i] = i;
D[i] = line(i, 0, 0);
}
}

void addval(int p, ll v)
{
if(t) p = n - 1 - p;
V[p] += v;
}

void addline(int p, ll x, ll a, ll b)
{
if(t)
{
p = n - 1 - p, x = n - 1 - x;
b = (n - 1) * a + b; a = -a;
}

int &s = S[p], &e = E[p];

b -= V[p];

for(; s<=e; e--)


{
if(D[e].a == a && D[e].b < b) return;
if(D[e].a * D[e].x + D[e].b < a * D[e].x + b)
{
D[++e] = line((D[e].b - b) / (a - D[e].a) + 1, a, b);
return;
}
}
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 232

D[++e] = line(x, a, b);


}

ll getval(int p, ll x)
{
if(t) p = n - 1 - p, x = n - 1 - x;

int k = upper_bound(D + S[p], D + E[p] + 1, x, [&](ll x, line l)


{
return x < l.x;
}) - D - 1;

return D[k].a * x + D[k].b + V[p];


}

void merge(int p, int q)


{
if(t) p = n - 1 - p, q = n - 1 - q;

int i;

if(E[p] - S[p] < E[q] - S[q])


{
swap(S[p], S[q]); swap(E[p], E[q]);
swap(V[p], V[q]);
}

if(S[p] < S[q])


{
for(i=S[q]; i<=E[q]; i++)
{
D[i].b += V[q] - V[p];
D[++E[p]] = D[i];
}
}
else
{
for(i=E[q]; i>=S[q]; i--)
{
D[i].b += V[q] - V[p];
D[--S[p]] = D[i];
}
}
}
};

minseg T;
deq DL, DR;
vector <int> H, L, R;
vector <int> Q[808080];
vector <ll> A;
int n;

int dnc(int s, int e)


{
if(s > e) return -1;

int m, l, r;
ll v, h;

m = T.getmax(s, e); h = H[m];


l = dnc(s, m - 1); r = dnc(m + 1, e);

for(int &q: Q[m])


{
if(L[q] == m && R[q] == m) A[q] = h;
else if(L[q] == m) A[q] = DR.getval(r, R[q]) + h;
else if(R[q] == m) A[q] = DL.getval(l, L[q]) + h;
else A[q] = min(DL.getval(l, L[q]) + h * (R[q] - m + 1),
DR.getval(r, R[q]) + h * (m - L[q] + 1));
}

if(l != -1) DL.merge(m, l);


DL.addval(m, h * (e - m + 1));
DL.addline(m, s, -h, (r != -1? DL.getval(r, m + 1) : 0) + h * (m+1));
if(r != -1) DL.merge(m, r);
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 233

if(r != -1) DR.merge(m, r);


DR.addval(m, h * (m - s + 1));
DR.addline(m, e, h, (l != -1? DR.getval(l, m - 1) : 0) - h * (m-1));
if(l != -1) DR.merge(m, l);

return m;
}

vector <ll> minimum_costs(vector <int> _H, vector <int> _L,


vector <int> _R)
{
int i;

swap(H, _H);
swap(L, _L);
swap(R, _R);
n = H.size();
A.resize(L.size());

T.init(H);
DL.init(0, n);
DR.init(1, n);

for(i=0; i<L.size(); i++)


{
Q[T.getmax(L[i], R[i])].push_back(i);
}

dnc(0, n - 1);

return A;
}

//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
std::freopen("../in/05-12.txt", "r", stdin);
std::freopen("meetings.out", "w", stdout);

auto t1 = clock();

int N = read_int();
int Q = read_int();
std::vector<int> H(N);
for (int i = 0; i < N; ++i)
{
H[i] = read_int();
}
std::vector<int> L(Q), R(Q);
for (int j = 0; j < Q; ++j)
{
L[j] = read_int();
R[j] = read_int();
}

fclose(stdin);
auto t2 = clock();

std::vector<long long> C = minimum_costs(H, L, R);


CAPITOLUL 2. IOI 2018 2.6. MEETINGS 234

auto t3 = clock();

for (size_t j = 0; j < C.size(); ++j)


{
printf("%lld\n", C[j]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 4.295
t3-t2 = 5.205
t4-t3 = 1.148

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


Press any key to continue.
*/

Listing 2.6.8: meetings 159115.cpp


// https://oj.uz/submission/159115 3257 ms

#include <stdio.h> // citire mai rapida !!!

#include <bits/stdc++.h>

using namespace std;

struct lichao
{
int A, l, r;
long long B, lazy;
lichao() : A(0), l(0), r(0), B(0x3fffffffffffffffLL), lazy(0) {}
};

const int SZ=1<<20;


pair<int,int> Mtree[2*SZ];

lichao ltree[2*SZ], rtree[2*SZ];


vector<long long> res;
vector<tuple<int,int,int>> Q[750001];

pair<int,int> get_max(int s, int e)


{
pair<int,int> ret(-1,-1);
for(s+=SZ,e+=SZ;s<=e;s>>=1,e>>=1)
{
if(s&1) ret=max(ret,Mtree[s++]);
if(˜e&1) ret=max(ret,Mtree[e--]);
}
return ret;
}

void lazy_propagation(lichao *tree, int bit, int s, int e)


{
if(tree[bit].lazy)
{
tree[bit].B+=tree[bit].lazy;
if(s<e)
{
tree[2*bit].lazy+=tree[bit].lazy;
tree[2*bit+1].lazy+=tree[bit].lazy;
}
tree[bit].lazy=0;
}
}
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 235

int get_sign(long long a)


{
return a<0 ? -1:a>0;
}

void add_line(lichao *tree, int n1, int n2,


int A, long long B, int bit=1, int s=0, int e=SZ-1)
{
int m=(s+e)>>1;
lazy_propagation(tree,bit,s,e);
if(n2<n1 || n2<s || e<n1) return;
if(n1<=s && e<=n2)
{
int &pA=tree[bit].A;
long long &pB=tree[bit].B, ys=1LL*A*s+B,
ym=1LL*A*m+B, ye=1LL*A*e+B,
pys=1LL*pA*s+pB, pym=1LL*pA*m+pB,
pye=1LL*pA*e+pB;
if(ym<pym)
{
swap(pA,A); swap(pB,B);
swap(pys,ys); swap(pym,ym); swap(pye,ye);
}

if(pys<=ys && pye<=ye) return;

if(get_sign(ys-pys)*get_sign(ym-pym)<0 || ym==pym && ys<pys)


add_line(tree,n1,n2,A,B,2*bit,s,m);
else
add_line(tree,n1,n2,A,B,2*bit+1,m+1,e);

return;
}

add_line(tree,n1,n2,A,B,2*bit,s,m);
add_line(tree,n1,n2,A,B,2*bit+1,m+1,e);
}

void add_tree(lichao *tree, int n1, int n2, long long v,


int bit=1, int s=0, int e=SZ-1)
{
int m=(s+e)>>1;
lazy_propagation(tree,bit,s,e);
if(n2<n1 || n2<s || e<n1) return;
if(n1<=s && e<=n2)
{
tree[bit].lazy=v;
lazy_propagation(tree,bit,s,e);
return;
}
add_tree(tree,n1,n2,v,2*bit,s,m);
add_tree(tree,n1,n2,v,2*bit+1,m+1,e);
}

long long get_y(lichao *tree, int x, int bit=1, int s=0, int e=SZ-1)
{
int m=(s+e)>>1;
lazy_propagation(tree,bit,s,e);
if(s==e) return 1LL*tree[bit].A*x+tree[bit].B;
return min(x<=m ? get_y(tree,x,2*bit,s,m):
get_y(tree,x,2*bit+1,m+1,e),1LL*tree[bit].A*x+tree[bit].B);
}

void solve(int s, int e)


{
if(s>e) return;
auto[M,m]=get_max(s,e);
solve(s,m-1);
solve(m+1,e);
for(auto[l,r,i]: Q[m])
res[i]=min(get_y(ltree,l)+(r-m+1LL)*M,get_y(rtree,r)+(m-l+1LL)*M);
add_tree(ltree,s,m-1,(e-m+1LL)*M);
add_line(ltree,s,m,-M,(m<e ? get_y(ltree,m+1):0)+M*(m+1LL));
add_tree(rtree,m+1,e,(m-s+1LL)*M);
add_line(rtree,m,e,M,(s<m ? get_y(rtree,m-1):0)+M*(1LL-m));
CAPITOLUL 2. IOI 2018 2.6. MEETINGS 236

vector<long long> minimum_costs(vector<int> H,


vector<int> L, vector<int> R)
{
int N=H.size(), M=L.size();
res.resize(M);
for(int i=0;i<N;i++) Mtree[SZ+i+1]={H[i],i+1};
for(int i=SZ;--i;) Mtree[i]=max(Mtree[2*i],Mtree[2*i+1]);
for(int i=0;i<M;i++)
{
if(++L[i]<++R[i])
Q[get_max(L[i],R[i]).second].emplace_back(L[i],R[i],i);
else
res[i]=H[L[i]-1];
}

solve(1,N);
return res;
}

//-------------- start grader ------------------

namespace
{
int read_int()
{
int x;
if (scanf("%d", &x) != 1)
{
fprintf(stderr, "Error while reading input\n");
exit(1);
}
return x;
}
} // namespace

int main()
{
auto t1 = clock();

std::freopen("../in/05-12.txt", "r", stdin);


std::freopen("meetings.out", "w", stdout);

int N = read_int();
int Q = read_int();
std::vector<int> H(N);
for (int i = 0; i < N; ++i)
{
H[i] = read_int();
}
std::vector<int> L(Q), R(Q);
for (int j = 0; j < Q; ++j)
{
L[j] = read_int();
R[j] = read_int();
}

fclose(stdin);
auto t2 = clock();

std::vector<long long> C = minimum_costs(H, L, R);

auto t3 = clock();

for (size_t j = 0; j < C.size(); ++j)


{
printf("%lld\n", C[j]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


CAPITOLUL 2. IOI 2018 2.6. MEETINGS 237

std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 1.109
t3-t2 = 11.448
t4-t3 = 0.75

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


Press any key to continue.
*/

https://cp-algorithms.com/geometry/convex_hull_trick.html

2.6.3 *Rezolvare detaliată


Capitolul 3
31
IOI 2017

3.1 Nowruz
Problema 1 - Nowruz 100 de puncte

Authors: Monika Steinová (Switzerland) and Michal Foris̆ek (Slovakia)

Au rămas doar câteva zile până la Nowruz (Anul Nou persan) şi bunicul a invitat ı̂ntreaga
familie la o petrecere ı̂n grădina sa. Printre invitaţi sunt şi k copii. Pentru a face petrecerea mai
distractivă pentru copii bunicul vrea să organizeze o joacă de-a v-aţi ascunselea.
Grădina este reprezentată printr-o matrice m  n de celule unitare. Unele celule (posibil nici
una) sunt blocate de pietre, iar celelalte celule se numesc libere. Două celule se numesc vecine
dacă au o latură comună. Astfel, fiecare celulă poate avea până la patru vecini: doi pe orizontală
şi doi pe verticală.
Bunicul doreşte să transforme grădina ı̂ntr-un labirint. ı̂n acest scop el poate bloca unele celule
libere plantând ı̂n ele arbuşti. Celulele ı̂n care au fost plantaţi arbuşti nu mai sunt libere.
Labirintul trebuie să posede următoarea proprietate: pentru fiecare pereche de celule libere a
şi b ı̂n labirint va exista un drum simplu unic, care să le unească.
Un drum simplu ı̂ntre celulele a şi b este o secvenţă de celule libere ı̂n care prima celulă este
a, ultima - b, toate celulele sunt distincte şi fiecare două celule consecutive sunt vecine.
Un copil se poate ascunde ı̂ntr-o celulă dacă şi numai dacă celula este liberă şi are exact un
vecin liber. Nu se pot ascunde doi sau mai mulţi copii ı̂n aceeaşi celulă.
Se dă harta grădinii. Se cere ajutarea bunicului ı̂n realizarea unui labirint ı̂n care să se ascundă
cât mai mulţi copii.
Particularităţi de implementare
Aceasta este o problemă tip output-only cu scoruri parţiale. Primiţi 10 fişiere de intrare, fiecare
dintre ele descriind o grădină a bunicului. Pentru fiecare fişier de intrare trebuie să submitaţi un
fişier de ieşire, care să conţină o hartă a labirintului. Pentru fiecare fişier de ieşire veţi primi
puncte ı̂n funcţie de numărul de copii, care se pot ascunde ı̂n labirintul vostru.
Nu trebuie să submitaţi nici o sursă pentru această problemă.
Format Input
Fiecare fişier de intrare va descrie o matrice reprezentând grădina şi numărul de copii k invitaţi
de bunic. Formatul fişierului este următorul:
a linia 1: m n k
a linia 1  i (pentru 1 & i & m): linia i a matricei, formată dintr-un şir de caractere de lungime
n, format din următoarele caractere (fără spaţii):
` ’.’: o celulă liberă,
` ’#’: piatră.

Format Output
31
aur: Ştefan Constantin-Buliga, CNI ”Tudor Vianu” Bucureşti
. aur: Tamio-Vesa Nakajima, Liceul Vocaţional Pedagogic ”Nicolae Bolcaş”, Beiuş,
. bronz: Costin-Andrei Oncescu, Dinicu Golescu (Campulung),
. bronz: Andrei-Costin Constantinescu, ICHB (Bucureşti).

238
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 239

a linia i (pentru 1 & i & m): linia i a labirintului (grădinii, după plantarea arbuştilor). Este
un şir de caractere de lungime n, format din următoarele caractere (fără spaţii):
` ’.’: o celulă liberă,
` ’#’: piatră,
` ’X’: arbust. (De remarcat că litera X trebuie să fie majusculă.)
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 240

Restricţii
a 1 & m, n & 1024
Punctaje
Fişierul output se consideră a fi valid dacă respectă toate condiţiile următoare:

a Harta de ieşire va coincide cu harta de intrare cu unica excepţie că un număr arbitrar de
caractere ’.’ din harta de intrare pot fi transformate ı̂n caractere ’X’ (celule blocate cu
arbuşti).
a Harta de ieşire va avea proprietatea de labirint, după cum acesta a fost definit ı̂n enunţul
problemei.

Dacă fişierul output pentru un test nu va fi valid, scorul acordat pentru acest test va fi 0. ı̂n
caz contrar, scorul va fi calculat ca min 10, 10 l©k  puncte, trunchiat până la două cifre zecimale
după virgulă. Aici, l este numărul copiilor care se pot ascunde ı̂n labirintul submitat, k fiind
numărul dat ı̂n fişierul de intrare. Veţi primi câte 10 puncte doar pentru acele teste ı̂n care harta
de ieşire le va permite să se ascundă la k sau mai mulţi copii. Pentru fiecare test există o soluţie
care permite acordarea a 10 puncte.
De menţionat, că ı̂n cazul ı̂n care aveţi o soluţie validă dar care acumulează 0 puncte conform
formulei de mai sus, mesajul evaluatorului afişat ı̂n CMS va fi ’Wrong Answer’.
Exemplu
Considerăm următorul input:

4 5 5
....#
.#..#
...#.
....#

Mai jos este prezentat un posibil output valid:

.X.X#
.#..#
...#X
XX..#

Deoarece copii se pot ascunde ı̂n acest labirint, soluţia va primi puncte. Celulele ı̂n care se
ascund copiii sunt marcate mai jos cu O :

OXOX#
.#.O#
...#X
XX.O#

Următoarele trei output-uri nu sunt valide:

.XXX# ...X# XXXX#


.#XX# .#.X# X#XX#
...#. ...#X ..X#X
XX..# XXXX# ..XX#

În output-ul din stânga nu există un drum simplu ı̂ntre celula liberă din colţul stâng-sus şi
celula liberă din cea mai de dreapta coloană. ı̂n celelalte două output-uri, pentru fiecare pereche
de celule libere distincte există exact câte două drumuri simple diferite, care le unesc.
Timp maxim de executare/test: Output-only
Memorie: Output-only
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 241

3.1.1 Indicaţii de rezolvare

ˆ Input: an m  n grid. Some random cells are blocked.


ˆ Output: A tree such that its nodes are a subset of free cells of the grid. The tree should
have as much leaves as possible (a solution is graded based on the number of leaves of the
tree).

Solutions for empty grid


One easy approach is to build some patterns that have many leaves, such as below (all of which
in an empty 35  35 grid):

Unexpectedly, the following code generates a nice Sierpinski fractal with 148 leaves:

mark[1][1] = true
scan the whole grid, starting from (1,1):
mark each cell that has exactly 1 marked neighbor
return mark

But the optimal solution we have so far for this grid can be obtained by a simple change on
the above code. It is a nice fractal with 408 leaves:

mark[1][1] = true
loop
scan the whole grid:
mark each cell that has exactly 1 marked neighbor
until mark values are not changed
return mark
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 242

Solutions for grid with random blocks


The solution above can be adopted to work for grids with random blocks:

do the following search several times:


unmark all cells
select a random free cell and set its mark true
loop:
scan all cells in random order:
mark each free cell that has exactly 1 marked neighbor
until nothing changes
return the best answer

This solution could score for 86 points with the actual test data of the contest.

Another idea is to pick a random free cell to initiate the tree, and keep expanding it as long
as possible. Expanding operation: Pick a free cell that is adjacent to exactly one of the tree cells
so far, add this cell and all of its adjacent cells that can be added and become leaves to the tree.
At each moment, expand the tree where it adds the highest number of leaves. Note that in each
operation, 1 to 5 cells will be added to the tree. This solution gets 99+ percent.

3.1.2 Coduri sursă

Listing 3.1.1: nowruz.cpp


// http://ioi2017.org/tasks/materials/nowruz.zip ---> sandbox/nowruz.cpp
// http://ioi2017.org/tasks/materials/nowruz.zip ---> solution/***.cpp

#include <vector>
#include <set>
#include <string>
#include <queue>
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 243

#include <iostream>

#include <fstream>

using namespace std;

ifstream fin("../tests/02.in");
ofstream fout("02.out.txt");

#define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)


#define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
#define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
#define sz(x) (int((x).size()))
#define pb push_back
#define all(X) (X).begin(),(X).end()
#define X first
#define Y second
//#define endl ’\n’

template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }


template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }

typedef long long ll;


typedef pair<int, int> pii;

////////////////////////////////////////////////////////////////////////////

struct solver
{
const int dx[4] = {0, -1, 0, 1};
const int dy[4] = {-1, 0, 1, 0};

static const int maxn = 2048 + 64;

static const char BLOCK = ’#’;


static const char OPEN = ’.’;
static const char TREE = ’X’;

int n, m;
string grid[maxn];

set<pair<int, pii>> s;

inline bool inside(pii p)


{
return 0 <= p.X && p.X < n && 0 <= p.Y && p.Y < m;
}

inline bool open(pii p)


{
return inside(p) && grid[p.X][p.Y] == OPEN;
}

inline pii adj(pii p, int d)


{
return pii(p.X + dx[d], p.Y + dy[d]);
}

int around(pii p, char ch)


{ // counts number of ch’s around p
int cnt = 0;
rep(d, 4)
if(inside(adj(p, d)) && grid[p.X + dx[d]][p.Y + dy[d]] == ch)
cnt++;
else
if(!inside(adj(p, d)) && ch == BLOCK)
cnt++;
return cnt;
}

int extend(pii p, bool act)


{ // act: determines whether to apply it or not
if(grid[p.X][p.Y] != OPEN || around(p, TREE) != 1)
return -1;

int cnt = 0;
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 244

rep(d, 4) if(open(adj(p, d)))


{
if(around(adj(p, d), TREE) == 0)
{
if(act) add(adj(p, d));
cnt++;
}
}

if(act) grid[p.X][p.Y] = TREE;

return cnt;
}

void add(pii p)
{ // add node p and extension options around p
grid[p.X][p.Y] = TREE;
rep(d, 4)
if(open(pii(p.X + dx[d], p.Y + dy[d])))
{
int cnt = extend(pii(p.X + dx[d], p.Y + dy[d]), 0);
if(cnt != -1)
s.insert({cnt, pii(p.X + dx[d], p.Y + dy[d])});
}
}

vector <int> mark[maxn];


int vmark = 727;
queue <pii> q;

int cnt_size(int x, int y)


{
int size = 1;
vmark++;
mark[x][y] = vmark;
q.push(pii(x, y));

while(!q.empty())
{
pii p = q.front();
q.pop();
rep(dir, 4)
if(open(adj(p, dir)) &&
mark[p.X + dx[dir]][p.Y + dy[dir]] != vmark)
mark[p.X + dx[dir]][p.Y + dy[dir]] = vmark,
q.push(adj(p, dir)),
size++;
}

return size;
}

void init()
{
rep(x, n) mark[x].resize(maxn, 0);
pii start;
int best_cnt = -1;
rep(x, n) rep(y, m) if(!mark[x][y] && grid[x][y] == OPEN)
{
int cnt = cnt_size(x, y);
if(cnt > best_cnt)
best_cnt = cnt, start = pii(x, y);
}

add(start);
}

bool is_path;
int dfs(int x, int y, bool st = true)
{
if(!open(pii(x, y)) ||
(!st && around(pii(x, y), TREE) > 0) ||
mark[x][y] == vmark)
return 0;

mark[x][y] = vmark;
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 245

int res = 1;
int cnt = 0;
rep(dir, 4)
{
int ores = res;
res += dfs(x + dx[dir], y + dy[dir], false);
if(res > ores) cnt++;
}

if(cnt >= 2) is_path = false;


return res;
}

void solve()
{
init();

while(true)
{
while(!s.empty())
{
auto t = *s.rbegin();
s.erase( *s.rbegin());

int cnt = extend(t.second, 0);

if(cnt == t.first)
extend(t.second, 1);
else if(cnt != -1)
s.insert({cnt, t.second});
}

bool changed = false;

rep(x, n)
if(!changed)
rep(y, m)
if(!changed &&
grid[x][y] == OPEN &&
around(pii(x, y), TREE) == 1)
{
add(pii(x, y));
changed = true;
}

rep(x, n)
if(!changed)
rep(y, m)
if(!changed &&
grid[x][y] == OPEN &&
around(pii(x, y), TREE) == 2)
{
int cnt = 0;
rep(dir, 4)
{
pii q = adj(pii(x, y), dir);
if(inside(q) &&
grid[q.X][q.Y] == OPEN &&
around(q, TREE) == 0)
cnt++;
}

if(cnt < 2) continue;

rep(dir, 4)
{
pii q = adj(pii(x, y), dir);
if(inside(q) &&
grid[q.X][q.Y] == TREE &&
around(q, TREE) == 1)
{
grid[q.X][q.Y] = OPEN;
add(pii(x, y));
changed = true;
break;
}
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 246

}
}

rep(x, n)
if(!changed)
rep(y, m)
if(!changed && grid[x][y] == OPEN && around(pii(x, y), TREE) == 2)
{
vmark++; is_path = true;
int size = dfs(x, y);

int cnt = 0;
rep(dir, 4)
{
pii q = adj(pii(x, y), dir);
if(inside(q) &&
grid[q.X][q.Y] == TREE &&
around(q, TREE) == 1)
cnt++;
}

if((cnt == 2 && size < 7) || (size < 4)) continue;

rep(dir, 4)
{
pii q = adj(pii(x, y), dir);
if(inside(q) &&
grid[q.X][q.Y] == TREE &&
around(q, TREE) == 1)
{
grid[q.X][q.Y] = OPEN;
add(pii(x, y));
changed = true;
break;
}
}
}

if(!changed) break;
}
}

void read()
{
fin >> n >> m; // ***
int tmp;
fin >> tmp; // ***
rep(x, n) fin >> grid[x]; // ***
}

void write()
{
rep(x, n)
rep(y, m)
if(grid[x][y] == ’.’ || grid[x][y] == ’X’)
grid[x][y] ˆ= ’X’ ˆ ’.’;

rep(x, n) cout << grid[x] << endl;


rep(x, n) fout << grid[x] << endl;
}
};

int main()
{
ios_base::sync_with_stdio(false);
fin.tie(0);

solver f;

f.read();
f.solve();
f.write();

return 0;
}
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 247

Listing 3.1.2: checker.cpp


#include <algorithm>
#include <queue>
#include <cmath>

#include<iostream>

#include "testlib.h"

#define MAX (1024 + 64)

using namespace std;

int m, n, leaves, answer;


std::vector <std::string> maze;
bool mark[MAX][MAX];
int rplus[4] = {1, -1, 0, 0}, cplus[4] = {0, 0, 1, -1};

struct node
{
node(int _r, int _c, int _pr, int _pc)
{
r = _r; c = _c; pr = _pr; pc = _pc;
}
int r, c, pr, pc;
};

bool inside(int r, int c)


{
return r >= 0 && r < m && c >= 0 && c < n;
}

bool leaf(int r, int c)


{
int adj = 0;
for (int i = 0; i < 4; i++)
if (inside(r + rplus[i], c + cplus[i]) &&
maze[r + rplus[i]][c + cplus[i]] == ’.’)
adj++;
return adj == 1;
}

void BFS(int r, int c)


{
queue <node> Q;
Q.push(node(r, c, -1, -1));
mark[r][c] = true;
while (! Q.empty())
{
node v = Q.front();
Q.pop();
if (leaf(v.r, v.c)) leaves++;
for (int i = 0; i < 4; i++)
{
int a = v.r + rplus[i], b = v.c + cplus[i];
if (inside(a, b) && maze[a][b] == ’.’ && (a != v.pr || b != v.pc))
{
if (mark[a][b]) quitf(_wa, "Output has cycle");
Q.push(node(a, b, v.r, v.c));
mark[a][b] = true;
}
}
}
}

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/02.in", // input
(char*)"../tests/02.out", // rezultat corect
(char*)"02.out.txt", // rezultat de verificat si acordat punctaj
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 248

};

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

registerChecker("nowruz", argc, argv);

m = inf.readInt();
inf.readSpace();

n = inf.readInt();
inf.readSpace();

answer = inf.readInt();
inf.readEoln();

// Read input and output mazes, and comparing if they match


for (int i = 0; i < m; i++)
{
string input = inf.readLine("[.#]{" + std::to_string(n) + "," +
std::to_string(n) + "}");
maze.push_back(ouf.readLine("[.#X]{" + std::to_string(n) + "," +
std::to_string(n) + "}"));
for (int j = 0; j < n; j++)
if (input[j] != maze[i][j] &&
(input[j] != ’.’ || maze[i][j] != ’X’))
quitf(_wa,
"Input and output maps are different at cell [%d, %d]",
i, j);
}

// Check if output maze is a tree, and count its leaves


int component = 0;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (maze[i][j] == ’.’ && ! mark[i][j])
{
if (component++ > 0)
quitf(_wa, "Output maze is not connected");
BFS(i, j);
}

double score = int(leaves / (double)answer * 1000) / 1000.0;

if (abs(score) < 1e-5)


quitf(_wa, "Your rounded-down score is 0");

if (score < 1 - 1e-5)


quitp(score, "number of leaves: %d/%d", leaves, answer);

quitf(_ok, "number of leaves: %d/%d", leaves, answer);


}
/*
argc = 4
checker
../tests/02.in
../tests/02.out
02.out.txt
----------------------
1
Correct
number of leaves: 1348/1338

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


Press any key to continue.
*/

32
OBS: Este folosită biblioteca testlib.h

Listing 3.1.3: addleaves.cpp


#include <iostream>
32
http://code.google.com/p/testlib/
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 249

#include <string>
#include <vector>

#include<ctime>

using namespace std;

vector <string> table;


int m, n, k;
int xplus[4] = {0, 1, 0, -1}, yplus[4] = {1, 0, -1, 0};

int cnt_neighbors(int x, int y)


{
int cnt = 0;
for (int i = 0; i < 4; i++)
if (table[x+xplus[i]][y+yplus[i]] == ’X’) cnt++;
return cnt;
}

bool addleaves(int x, int y)


{
bool found = false;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
{
int a = 1 + (x + i) % m, b = 1 + (y + j) % n;
if (table[a][b] == ’.’ && cnt_neighbors(a, b) == 1)
{
table[a][b] = ’X’;
found = true;
}
}
return found;
}

int main()
{
auto t1 = clock();

// redirect console input to a file


std::freopen("../tests/07.in", "r", stdin) ;

// redirect console output to a file


std::freopen("07.out.txt", "w", stdout) ;

ios_base::sync_with_stdio(false); cin.tie(0);

cin >> m >> n >> k;


string s(n, ’#’);
table.push_back(s);
for (int i = 0; i < m; i++)
{
cin >> s;
table.push_back(’#’ + s + ’#’);
}
table.push_back(string(n, ’#’));

auto t2 = clock();

bool found = false;


for (int i = 2; i < m + 1 && ! found; i++)
for (int j = 2; j < n + 1 && ! found; j++)
if (table[i][j] == ’.’)
{
table[i][j] = ’X’;
addleaves(i, j);
found = true;
}

for(; addleaves(1,1););

for (int i = 1; i < m + 1; i++)


for (int j = 1; j < n + 1; j++)
if (table[i][j] == ’.’ || table[i][j] == ’X’)
table[i][j] = ’.’ + ’X’ - table[i][j];
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 250

auto t3 = clock();

for (int i = 1; i < m + 1; i++)


cout << table[i].substr(1, n) << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0
t3-t2 = 0.796
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/07.in
../tests/07.out
07.out.txt
----------------------
0.828
Partially Correct
number of leaves: 27639/33363

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


Press any key to continue.
*/

Listing 3.1.4: addleaves once.cpp


#include <iostream>
#include <string>
#include <vector>

#include<ctime>

using namespace std;

vector <string> table;


int m, n, k;
int xplus[4] = {0, 1, 0, -1}, yplus[4] = {1, 0, -1, 0};

int cnt_neighbors(int x, int y)


{
int cnt = 0;
for (int i = 0; i < 4; i++)
if (table[x+xplus[i]][y+yplus[i]] == ’X’) cnt++;
return cnt;
}

bool addleaves(int x, int y)


{
bool found = false;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
{
int a = 1 + (x + i) % m, b = 1 + (y + j) % n;
if (table[a][b] == ’.’ && cnt_neighbors(a, b) == 1)
{
table[a][b] = ’X’;
found = true;
}
}
return found;
}
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 251

int main()
{
auto t1 = clock();

// redirect console input to a file


std::freopen("../tests/07.in", "r", stdin) ;

// redirect console output to a file


std::freopen("07.out.txt", "w", stdout) ;

ios_base::sync_with_stdio(false); cin.tie(0);

cin >> m >> n >> k;


string s(n, ’#’);
table.push_back(s);
for (int i = 0; i < m; i++) {
cin >> s;
table.push_back(’#’ + s + ’#’);
}
table.push_back(string(n, ’#’));

auto t2 = clock();

bool found = false;


for (int i = 2; i < m + 1 && ! found; i++)
for (int j = 2; j < n + 1 && ! found; j++)
if (table[i][j] == ’.’)
{
table[i][j] = ’X’;
addleaves(i, j);
found = true;
}

addleaves(0,0);

for (int i = 1; i < m + 1; i++)


for (int j = 1; j < n + 1; j++)
if (table[i][j] == ’.’ || table[i][j] == ’X’)
table[i][j] = ’.’ + ’X’ - table[i][j];

auto t3 = clock();

for (int i = 1; i < m + 1; i++)


cout << table[i].substr(1, n) << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.015
t3-t2 = 0.047
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/07.in
../tests/07.out
07.out.txt
----------------------
0.080
Partially Correct
number of leaves: 2687/33363

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


CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 252

Press any key to continue.


*/

Listing 3.1.5: addleaves rand.cpp


#include <iostream>
#include <string>
#include <vector>

#include<ctime>

#define For(a, b) for (int a = 0; a < b; a++)


#define max_tries (10)

using namespace std;

#define ipair pair <int, int>

vector <string> table, first, best;


int m, n, k, best_num;
int xplus[4] = {0, 1, 0, -1}, yplus[4] = {1, 0, -1, 0};
vector < ipair > allcells;

bool inside(int a, int b)


{
return (a >= 0 && a < m && b >= 0 && b < n);
}

int neighbors(int x, int y)


{
int count = 0;
For(i, 4)
{
int a = x + xplus[i], b = y + yplus[i];
if (inside(a, b) && table[a][b] == ’X’) count ++;
}
return count;
}

int count_leaves()
{
int c = 0;
For(i, m)
For(j, n)
if (table[i][j] == ’X’ && neighbors(i, j) == 1) c++;
return c;
}

bool check_leaf(int x, int y)


{
if (table[x][y] == ’.’ && neighbors(x, y) == 1)
{
table[x][y] = ’X’;
return true;
}
return false;
}

bool break_tie(int x, int y)


{
if (table[x][y] != ’.’) return false;
bool leaf[4] = {0, 0, 0, 0};
int lnum = 0;
For(i, 4)
{
int a = x + xplus[i], b = y + yplus[i];
if (table[a][b] == ’X’)
{
if (neighbors(a, b) != 1) return false;
leaf[i] = true;
lnum++;
}
else
if (neighbors(a, b) != 0)
return false;
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 253

if (lnum != 2) return false;


table[x][y] = ’X’;
For(i, 4)
{
if (leaf[i])
{
table[x + xplus[i]][y+yplus[i]] = ’.’;
//cerr << "Tie found" << endl;
return true;
}
}
return false; // !!!
}

// dir = 0 : forward, 1: backward, 2: random


bool add_leaves(int x, int y, int dir)
{
bool found = false;
if (dir == 2)
{
for (int i = 0; i < m * n; i++)
allcells[i] = allcells[rand() % (i+1)];

for (int i = 0; i < m * n; i++)


found |= check_leaf(allcells[i].first, allcells[i].second);
}
else
For(i, m)
For(j, n)
{
int a = (dir) ? (m + x - i) % m : (i+x) % m;
int b = (dir) ? (n + y - j) % n : (j+y) % n;
found |= check_leaf(a, b);
}

if (! found)
for (int i = 1; i < m - 1; i++)
for (int j = 1; j < n - 1; j++)
found |= break_tie(i, j);

return found;
}

void try_add_leaves(int x, int y, bool random)


{
For(i, m)
For(j, n)
{
int a = (i + x) % m, b = (j + y) % n;
if (table[a][b] == ’.’)
{
table[a][b] = ’X’;
if (random) for (;add_leaves(0, 0, 2););
else for (int l = 0; add_leaves(a, b, l % 2); l++);
int cur_num = count_leaves();
if (cur_num > best_num)
{
best = table;
best_num = cur_num;
//cerr << best_num << endl;
}
table = first;
return;
}
}
}

int main()
{
auto t1 = clock();

// redirect console input to a file


std::freopen("../tests/07.in", "r", stdin) ;
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 254

// redirect console output to a file


std::freopen("07.out.txt", "w", stdout) ;

ios_base::sync_with_stdio(false); cin.tie(0);

cin >> m >> n >> k;


string s;
For(i, m)
{
cin >> s;
table.push_back(s);
For(j, n)
allcells.push_back(ipair(i, j));
}

auto t2 = clock();

first = table;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
try_add_leaves(i, j, false);

for (int tries = 0; tries < max_tries; tries++)


{
try_add_leaves(rand() % m, rand() % n, false);
try_add_leaves(rand() % m, rand() % n, true);
}

For(i, m)
For (j, n)
if (best[i][j] == ’.’ || best[i][j] == ’X’)
best[i][j] = ’.’ + ’X’ - best[i][j];
auto t3 = clock();

For (i, m)
cout << best[i] << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.031
t3-t2 = 8.369
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/07.in
../tests/07.out
07.out.txt
----------------------
0.796
Partially Correct
number of leaves: 26588/33363

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


Press any key to continue.
*/

Listing 3.1.6: dfs.cpp


#include <iostream>
#include <vector>
#include <string>
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 255

#include <bitset>
#include <algorithm>

#include<ctime>

#define For(a, b) for (int a = 0; a < b; a++)


#define MAX (2048 + 64)
const int rplus[4] = {0,0,1,-1}, cplus[4] = {-1, 1, 0, 0};

using namespace std;

int m, n;
double leaf_prob = 0.2;
vector <string> table;
int counter;

void rand_perm(int* p)
{
for (int i = 0; i < 4; i++)
{
p[i] = i;
swap(p[i], p[rand() % (i + 1)]);
}
}

bool check2(int r, int c)


{
return (r < 0 || r >= m || c < 0 || c >= n || table[r][c] != ’X’);
}

bool check(int pr, int pc, int r, int c)


{
if (r < 0 || r >= m || c < 0 || c >= n || table[r][c] != ’.’)
return false;
For(i, 4)
if ((r + rplus[i] != pr || c + cplus[i] != pc) &&
! check2(r + rplus[i], c + cplus[i]))
return false;
return true;
}

void DFS(int r, int c)


{
table[r][c] = ’X’;
if (counter++ > 10 && ((double) rand() / RAND_MAX) < leaf_prob)
return;
int p[4];
rand_perm(p);
For(i, 4)
if (check(r, c, r + rplus[p[i]], c + cplus[p[i]]))
DFS(r + rplus[p[i]], c + cplus[p[i]]);
}

bool inside(int a, int b)


{
return (a >= 0 && a < m && b >= 0 && b < n);
}

int neighbors(int x, int y)


{
int count = 0;
For(i, 4)
{
int a = x + rplus[i], b = y + cplus[i];
if (inside(a, b) && table[a][b] == ’X’) count ++;
}
return count;
}

void add_leaves() {
For(i, m)
For(j, n)
if (table[i][j] == ’.’ && neighbors(i, j) == 1) table[i][j] = ’X’;
}

int main(int argc, char ** argv)


CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 256

{
auto t1 = clock();

// redirect console input to a file


std::freopen("../tests/07.in", "r", stdin) ;

// redirect console output to a file


std::freopen("07.out.txt", "w", stdout) ;

ios_base::sync_with_stdio(false); cin.tie(0);

// leaf_prob = atof(argv[1]);
// cerr << leaf_prob << endl;
cin >> m >> n;
int tmp; cin >> tmp;
string s;
int blocks = 0;
For(i, m)
{
cin >> s;
table.push_back(s);
blocks += count(s.begin(), s.end(), ’#’);
}

auto t2 = clock();

leaf_prob -= (double) blocks / (m * n);


bool found = false;
for (int i = 0; i < m && ! found; i++)
For(j, n)
if (table[i][j] == ’.’)
{
DFS(i, j);
found = true;
break;
}

add_leaves();
For(i, m)
For (j, n)
if (table[i][j] == ’.’ || table[i][j] == ’X’)
table[i][j] = ’.’ + ’X’ - table[i][j];

auto t3 = clock();

For (i, m)
cout << table[i] << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.015
t3-t2 = 0.078
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/07.in
../tests/07.out
07.out.txt
----------------------
0.497
Partially Correct
number of leaves: 16602/33363
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 257

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


Press any key to continue.
*/

Listing 3.1.7: haircomb.cpp


#include <iostream>
#include <vector>
#include <string>

#include<ctime>

#define For(a, b) for (int a = 0; a < b; a++)

using namespace std;

int m, n, k;
vector <string> table;

int main(int argc, char ** argv)


{
auto t1 = clock();

// redirect console input to a file


std::freopen("../tests/07.in", "r", stdin) ;

// redirect console output to a file


std::freopen("07.out.txt", "w", stdout) ;

ios_base::sync_with_stdio(false); cin.tie(0);

cin >> m >> n >> k;

string s;
For(i, m)
{
cin >> s;
table.push_back(s);
}

auto t2 = clock();

int i = 0, j;
for (; i < n && table[0][i] != ’.’; i++);

for (j = i; j < n && table[0][j] == ’.’; table[0][j++] = ’X’)


if (j % 3 == 0)
for (int k = 1; k < m; k++)
{
if (table[k][j] == ’.’)
table[k][j] = ’X’;
else
break;

if (k > 1 && (j + k) % 2 == 0)
{
if (j > 1 && table[k][j-1] == ’.’)
table[k][j-1] = ’X’;
if (j < n - 1 && table[k][j+1] == ’.’)
table[k][j+1] = ’X’;
}
}

For(i, m)
For (j, n)
if (table[i][j] == ’.’ || table[i][j] == ’X’)
table[i][j] = ’.’ + ’X’ - table[i][j];

auto t3 = clock();

For (i, m)
cout << table[i] << endl;

auto t4 = clock();
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 258

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.015
t3-t2 = 0
t4-t3 = 0.016

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


Press any key to continue.

argc = 4
checker
../tests/07.in
../tests/07.out
07.out.txt
----------------------
0.001
Partially Correct
number of leaves: 47/33363

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


Press any key to continue.
*/

Listing 3.1.8: nowruz1.cpp


/*
https://github.com/KieranHorgan/Competitive-Programming/blob/master/
IOI/2017/nowruz/nowruz.cpp
*/

#include <bits/stdc++.h>

using namespace std;

ifstream fin("../tests/02.in");
ofstream fout("02.out.txt");

#define ll long long


#define ld long double
#define endl ’\n’

ll n, m, k;

char grid[1200][1200];
char grid2[1200][1200];
char bestGrid[1200][1200];
ll visited[1200][1200];
ll printed[1200];

ld bestScore, score, lastDone;

int main()
{
//ios_base::sync_with_stdio(0); cin.tie(NULL);
//cerr << setprecision(5) << endl;

fin >> n >> m >> k;


cout<<n<<" "<<m<<" "<<k<<"\n";

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


{
for(int j = 1; j <= m; j++)
{
fin >> grid[i][j];
}
}
CAPITOLUL 3. IOI 2017 3.1. NOWRUZ 259

for(int i = 0; i <= n+1-1000+1000; i++)


{
for(int j = 0; j <= m+1-1000+1000; j++)
{
if(i==0||i==n+1-1000+1000||j==0||j==m+1-1000+1000)
grid[i][j] = ’#’;
if(grid[i][j] != ’#’)
grid[i][j] = ’X’;
}
}

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


for(int j = 0; j <= m+1; j++)
grid2[i][j] = grid[i][j];

// for(int I = 1; I <= n; I++) {


// for(int J = 1; J <= m; J++) {
srand(time(NULL));
ll toDo = 200000000/((n*m));
ll totalToDo = toDo;
while((toDo-->0)&&bestScore<100)
{
ll I = (rand()%n) + 1;
ll J = (rand()%m) + 1;
for(int i = 0; i <= n+1; i++)
for(int j = 0; j <= m+1; j++)
grid[i][j] = grid2[i][j];
memset(visited, 0, sizeof(visited));
queue<pair<ll,ll>> q;
q.push({I, J});
while(!q.empty())
{
ll i = q.front().first;
ll j = q.front().second;
q.pop();

if(grid[i][j] == ’#’ || grid[i][j] == ’.’ ||


((ll)(rand()*0.9))%(((max(n, m)*3)))==1)
continue;

if(visited[i+1][j ]+
visited[i-1][j ]+
visited[i ][j+1]+
visited[i ][j-1] > 1)
{
grid[i][j] = ’X’;
continue;
}
else
{
visited[i][j]=1;
grid[i][j]=’.’;
q.push({i+1, j });
q.push({i , j+1});
q.push({i-1, j });
q.push({i , j-1});
}
}

ll cnt = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(grid[i][j]==’.’ &&
visited[i ][j-1]+
visited[i-1][j ]+
visited[i+1][j ]+
visited[i ][j+1] == 1)
cnt++;
}
}

ld score = min((ld)100, 100*(ld)cnt/k);


CAPITOLUL 3. IOI 2017 3.2. WIRING 260

if(score>bestScore)
{
bestScore = score;
for(int i = 0; i <= n+1; i++)
for(int j = 0; j <= m+1; j++)
bestGrid[i][j] = grid[i][j];
}

if((ll)(((ld)toDo/totalToDo*100))/10 >
(ll)(((ld)(toDo-1)/totalToDo*100))/10)
{
cerr << (ll)((ld)toDo/totalToDo*100) << "%: " << bestScore<<endl;
}

ll done = (((ld)I*m)+J)/(((ld)n*m)+m)*100;
//if((ll)lastDone/10<(ll)done/10)
lastDone = done;
}

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


{
for(int j = 1; j <= m; j++)
{
fout << bestGrid[i][j];
}
fout << endl;
}
}
/*
64 64 1338
90%: 96.861
80%: 98.1315
70%: 98.1315
60%: 98.1315
50%: 98.3558
40%: 98.3558
30%: 98.3558
20%: 98.3558
10%: 98.3558

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


Press any key to continue.

argc = 4
checker
../tests/02.in
../tests/02.out
02.out.txt
----------------------
0.983
Partially Correct
number of leaves: 1316/1338

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


Press any key to continue.
*/

3.1.3 *Rezolvare detaliată

3.2 Wiring
Problema 2 - Wiring 100 de puncte

Author: Aleksandar Ili (Serbia)

Maryam este inginer. Ea vrea să contruiască un turn de comunicaţie. Turnul conţine multe
puncte de conectare plasate la diferite inaltimi. Un cablu poate fi folosit pentru a conecta oricare
CAPITOLUL 3. IOI 2017 3.2. WIRING 261

două puncte de conectare. Toate punctele de conetare pot fi conectate cu un număr arbitrar de
cabluri.
Sunt doua tipuri de cabluri: roşii si albastre.
In corcondanta cu scopul acestei probleme, turnul poate fi văzut ca o linie, iar punctele de
conetare albastre si roşii ca puncte pe aceasta linie având coordonate intregi ne-negative. Lungimea
unui cablu este distanţa dintre cele doua puncte pe care le uneşte.
Scopul vostru este de a o ajuta pe Maryam pentru a găsi o schema de cablare astfel ı̂ncât:
1. Fiecare punct de conetare are cel puţin un cablu către o culoare diferită.
2. Lungimea totala a cablurilor este minima.
Detalii de Implementare
Se cere implementarea următoarei proceduri:

int64 min_total_length(int[] r, int[] b)

a r: şir de lungime conţinând poziţiile punctelor de conectare roşii in ordine crescătoare.


a b: şir de lungime conţinând poziţiile punctelor de conectare albastre in ordine crescătoare.
a Aceasta procedura va returna valoarea minima a lungimii totale a cablurilor, dintre toate
schemele valide de cablare.
a Observaţi ca numărul ce se va returna prin aceasta procedura este de tip int64.

Exemplu

min_total_length([1, 2, 3, 7], [0, 4, 5, 9, 10])

In imaginea care urmează aveţi un exemplu.

a Turnul este reprezentat pe orizontală.


a Pe versiunea printată in alb-negru punctele ı̂nchise la culoare reprezinta punctele de conectare
roşii, iar cele deschise reprezintă punctele de conectare albastre.
a Avem 4 puncte de conectare de culoare roşie localizate pe poziţiile 1, 2, 3 şi 7.
a Avem 5 puncte de conectare de culoare albastra localizate pe poziţiile 0, 4, 5, 9 şi 10.
a O soluţie optima este arătată in imaginea de mai sus.
a In aceasta soluţie lungimea totala a cablurilor este 1+2+2+2+3-10, ceea ce este optim. Deci
procedura va returna 10.
Observaţi, că două cabluri sunt conectate la punctul de conectare ı̂n poziţia 7.
Restricţii şi precizări
a 1 & n, m & 100 000,
0 & ri & 10 (pentru 0 & i & n  1),
9
a

0 & bi & 10 (pentru 0 & i & m  1),


9
a
a fiecare din şirurile r şi b este sortat ı̂n ordine crescătoare.
a Toate cele n  m valori din şirurile r şi b sunt distincte.
Subtask-uri
1. (7 puncte) n, m & 200,
2. (13 puncte) Toate punctele de conectare de culoare roşie au poziţii mai mici decât toate
punctele de conectare de culoare albastră.
3. (10 puncte) Există cel puţin un punct de conectare de culoare roşie şi cel puţin un punct
de conectare de culoare albastră printre oricare 7 puncte de conectare consecutive.
4. (25 puncte) Toate punctele de conectare au poziţii distincte ı̂n intervalul 1, n  m.
5. (45 puncte) Fără restricţii suplimentare.
CAPITOLUL 3. IOI 2017 3.2. WIRING 262

Evaluator local
Evaluatorul local citeşte datele din input ı̂n următorul format:
linia 1: n m
linia 2: r0 r1 ... rn  1
linia 3: b0 b1 ... bm  1
Evaluatorul afişează o singură linie, care conţine valoarea returnată de min_total_length.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 256 MB

3.2.1 Indicaţii de rezolvare

Subtask 1
2 3
There are many different O n  or O n  dp solutions for this subtask. The simplest one is
to define dpi,j as the minimum cost needed for wiring the first i red points and the first j blue
points. Update is like dpi,j min dpi1,j , dpi,j 1 , dpi1,j 1   ¶redi  bluej ¶.
Subtask 2
This subtask is to find the pattern of wiring. The simple solution to this subtask is to calculate
n1 m1
= redn1 redi   = bluei blue0   max n, m  blue0redn1
0 0

Subtask 3
Consider the consecutive clusters of points with the same color. The idea is each wire will
2
have endpoints in two consecutive clusters, so the O n  solutions could be optimized to O n 
M axBlockSize.
Subtask 4
This subtask could be solve greedily, halving each cluster and connecting left half to the left
cluster and right half to the right cluster. The middle point of clusters with odd number of points
should be considered separately.
Full solution
There is O n  m dp solution: Let dpi be the minimum total distance of a valid wiring scheme
for the set of points that are less than or equal to a given point i. This could be updated with
amortized time complexity O 1.

3.2.2 Coduri sursă

Listing 3.2.1: wiring.cpp


// http://ioi2017.org/tasks/materials/wiring.zip ---> sandbox/wiring.cpp
// http://ioi2017.org/tasks/materials/wiring.zip ---> solution/***.cpp
// . .. ... .... ..... be name khoda ..... .... ... .. .

#include<iostream>
#include<algorithm>
#include "wiring.h"

#include <fstream> // ***

using namespace std;

ifstream cinf("01.in"); // ***


CAPITOLUL 3. IOI 2017 3.2. WIRING 263

inline int in() { int x; scanf("%d", &x); return x; }


const int N = 2000002;

const int BLUE = 1;


const int RED = 2;

struct Point
{
int x, color;
bool operator <(const Point &p) const { return x < p.x; }
};

Point p[N], tmp[N];


long long f[N], g[N], sum[N], dp[N];

long long min_total_length(vector<int> red, vector<int> blue)


{
int nb = blue.size();
int nr = red.size();

for(int i = 0; i < nb; i++)


{
tmp[i].x = blue[i];
tmp[i].color = BLUE;
}

for(int i = 0; i < nr; i++)


{
tmp[nb + i].x = red[i];
tmp[nb + i].color = RED;
}

merge(tmp, tmp + nb, tmp + nb, tmp + nb + nr, p + 1);

int n = nb + nr;
int st = 1, lastSz = 0;

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


{
if(p[i].color != p[i - 1].color)
{
for(int j = i; p[j].color == p[i].color; j++)
sum[i] += p[j].x;

for(int j = i - 1; j >= st; j--)


{
f[j] = -sum[j] +
min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i - 1].x;
if(j < i - 1)
f[j] = min(f[j], f[j + 1]);
}

for(int j = st; j < i; j++)


{
g[j] = -sum[j] +
min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i].x;
if(j > st)
g[j] = min(g[j], g[j - 1]);
}

lastSz = i - st;
st = i;
}
else
sum[i] = sum[i - 1] - p[i - 1].x;

if(st == 1)
{
dp[i] = 1e18;
continue;
}

int sz = i - st + 1;
long long curSum = sum[st] - sum[i] + p[i].x;

if(sz >= lastSz)


CAPITOLUL 3. IOI 2017 3.2. WIRING 264

dp[i] = f[st - lastSz] + curSum - 1LL * sz * p[st - 1].x;


else
dp[i] = min(g[st - sz - 1] + curSum - 1LL * sz * p[st].x,
f[st - sz] + curSum - 1LL * sz * p[st - 1].x);
}

return dp[n];
}

int main() // grader.cpp ...


{
int n, m;
cinf >> n >> m; // ***

vector<int> r(n), b(m);

for(int i = 0; i < n; i++)


cinf >>r[i]; // ***

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


cinf >>b[i]; // ***

long long res = min_total_length(r, b);


printf("%lld\n", res);

return 0;
}

Listing 3.2.2: wiring+grader.cpp


// http://ioi2017.org/tasks/materials/wiring.zip ---> sandbox/wiring.cpp
// http://ioi2017.org/tasks/materials/wiring.zip ---> solution/***.cpp

#include <cassert>
#include <cstdio>

#include<iostream>
#include<algorithm>

#include <fstream>
#include<ctime>

using namespace std;

#include "wiring.h"

inline int in() { int x; scanf("%d", &x); return x; }

const int N = 2000002;

const int BLUE = 1;


const int RED = 2;

struct Point
{
int x, color;
bool operator <(const Point &p) const { return x < p.x; }
};

Point p[N], tmp[N];


long long f[N], g[N], sum[N], dp[N];

long long min_total_length(vector<int> red, vector<int> blue)


{
int nb = blue.size();
int nr = red.size();
for(int i = 0; i < nb; i++)
{
tmp[i].x = blue[i];
tmp[i].color = BLUE;
}
for(int i = 0; i < nr; i++)
{
tmp[nb + i].x = red[i];
tmp[nb + i].color = RED;
CAPITOLUL 3. IOI 2017 3.2. WIRING 265

}
merge(tmp, tmp + nb, tmp + nb, tmp + nb + nr, p + 1);

int n = nb + nr;
int st = 1, lastSz = 0;
for(int i = 1; i <= n; i++)
{
if(p[i].color != p[i - 1].color)
{
for(int j = i; p[j].color == p[i].color; j++)
sum[i] += p[j].x;
for(int j = i - 1; j >= st; j--)
{
f[j] = -sum[j] + min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i - 1].x;
if(j < i - 1)
f[j] = min(f[j], f[j + 1]);
}
for(int j = st; j < i; j++)
{
g[j] = -sum[j] + min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i].x;
if(j > st)
g[j] = min(g[j], g[j - 1]);
}
lastSz = i - st;
st = i;
}
else
sum[i] = sum[i - 1] - p[i - 1].x;
if(st == 1)
{
dp[i] = 1e18;
continue;
}
int sz = i - st + 1;
long long curSum = sum[st] - sum[i] + p[i].x;
if(sz >= lastSz)
dp[i] = f[st - lastSz] + curSum - 1LL * sz * p[st - 1].x;
else
dp[i] = min(g[st - sz - 1] + curSum - 1LL * sz * p[st].x,
f[st - sz] + curSum - 1LL * sz * p[st - 1].x);
}
return dp[n];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


CAPITOLUL 3. IOI 2017 3.2. WIRING 266

std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.491
t3-t2 = 0.043
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.3: wiring-haas-ac.cpp


#include "wiring.h"
#include <vector>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cassert>
#include<ctime>

using namespace std;

typedef pair<int,bool> pie;

const bool BLUE = true, RED = false;


const int max_n = 200 * 1000; // TO BE DETERMINED

vector<pie> points;
long long d[max_n + 1], ps[max_n + 1];
int head[max_n], cnt[max_n];

long long val(int l, int r)


{
int m = head[r], x = r+1 - m, y = m - l;
long long res = (ps[r+1] - ps[m]) - (ps[m] - ps[l]);
if (x > y) res -= (long long)(x-y) * points[m-1].first;
if (x < y) res += (long long)(y-x) * points[m].first;
return res;
}

long long min_total_length (vector <int> red, vector <int> blue)


{
int n = blue.size(), m = red.size();
for (int i = 0; i < n; i++) points.push_back(pie(blue[i], BLUE));
for (int i = 0; i < m; i++) points.push_back(pie(red[i], RED));
sort (points.begin(), points.end());

n = points.size();
for (int i = 1; i <= n; i++) ps[i] = ps[i-1] + points[i-1].first;

long long *d = &(::d[1]);


memset(d, 50, sizeof(long long) * n);

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


{
head[i] = head[i-1];
if (points[i].second != points[i-1].second)
{
int prev = head[i];
head[i] = i;
d[i] = d[i-1] + val(i-1, i);
cnt[i] = 1;
for (int j = prev; j < i; j++)
if (d[i] > d[j-1] + val(j, i))
cnt[i] = i - j, d[i] = d[j-1] + val(j, i);
}
else
if (head[i] > 0)
{
CAPITOLUL 3. IOI 2017 3.2. WIRING 267

cnt[i] = cnt[i-1];
int j = head[i] - cnt[i];
if (cnt[i-1] == i - head[i])
if (j > head[j] && d[j-1] + val(j, i) > d[j-2] + val(j-1, i))
cnt[i]++, j--;
d[i] = min(d[j], d[j-1]) + val(j, i);
}
}

return d[n-1];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.493
t3-t2 = 0.307
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.4: wiring-haas-ac-ternary.cpp


#include "wiring.h"
#include <vector>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cassert>
#include<ctime>

using namespace std;

typedef pair<int,bool> pie;

const bool BLUE = true, RED = false;


CAPITOLUL 3. IOI 2017 3.2. WIRING 268

const int max_n = 200 * 1000; // TO BE DETERMINED

const int TOF = 100;

vector<pie> points;
long long d[max_n + 1], ps[max_n + 1];
int head[max_n];

long long val(int l, int r)


{
int m = head[r], x = r+1 - m, y = m - l;
long long res = (ps[r+1] - ps[m]) - (ps[m] - ps[l]);
if (x > y) res -= (long long)(x-y) * points[m-1].first;
if (x < y) res += (long long)(y-x) * points[m].first;
return res;
}

long long min_total_length (vector <int> red, vector <int> blue)


{
int n = blue.size(), m = red.size();
for (int i = 0; i < n; i++) points.push_back(pie(blue[i], BLUE));
for (int i = 0; i < m; i++) points.push_back(pie(red[i], RED));
sort (points.begin(), points.end());

n = points.size();
for (int i = 0; i < n - 1; i++)
assert(points[i].first != points[i + 1].first);
for (int i = 1; i <= n; i++) ps[i] = ps[i-1] + points[i-1].first;

long long *d = &(::d[1]);


memset(d, 50, sizeof(long long) * n);

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


{
head[i] = head[i-1];
if (points[i].second != points[i-1].second)
{
int prev = head[i];
head[i] = i;
d[i] = d[i-1] + val(i-1, i);
for (int j = prev; j < i; j++)
if (d[i] > d[j-1] + val(j, i))
d[i] = d[j-1] + val(j, i);
}
else
if (head[i] > 0)
{
int l = head[head[i] - 1], r = head[i];
if (l == 0)
d[i] = val(0, i);
else
if (r - l < TOF)
for (int j = l; j < r; j++)
d[i] = min(d[i], min(d[j], d[j-1]) + val(j, i));
else
{
while (r - l > 1)
{
int m = (r + l) / 2;
if (d[m-1] + val(m, i) > d[m-2] + val(m-1, i))
r = m;
else
l = m;
}

d[i] = d[l - 1] + val(l, i);


}
}
}

return d[n-1];
}

int main()
{
auto t1 = clock();
CAPITOLUL 3. IOI 2017 3.2. WIRING 269

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.462
t3-t2 = 0.432
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.5: wiring-mahdi-ac.cpp


// . .. ... .... ..... be name khoda ..... .... ... .. .
#include<iostream>
#include<algorithm>
#include<ctime>
#include <cassert>

#include "wiring.h"

using namespace std;

inline int in() { int x; scanf("%d", &x); return x; }


const int N = 2000002;

const int BLUE = 1;


const int RED = 2;

struct Point
{
int x, color;
bool operator <(const Point &p) const { return x < p.x; }
};

Point p[N], tmp[N];


long long f[N], g[N], sum[N], dp[N];

long long min_total_length(vector<int> red, vector<int> blue)


{
int nb = blue.size();
CAPITOLUL 3. IOI 2017 3.2. WIRING 270

int nr = red.size();
for(int i = 0; i < nb; i++)
{
tmp[i].x = blue[i];
tmp[i].color = BLUE;
}

for(int i = 0; i < nr; i++)


{
tmp[nb + i].x = red[i];
tmp[nb + i].color = RED;
}

merge(tmp, tmp + nb, tmp + nb, tmp + nb + nr, p + 1);

int n = nb + nr;
int st = 1, lastSz = 0;
for(int i = 1; i <= n; i++)
{
if(p[i].color != p[i - 1].color)
{
for(int j = i; p[j].color == p[i].color; j++)
sum[i] += p[j].x;

for(int j = i - 1; j >= st; j--)


{
f[j] = -sum[j] + min(dp[j-1], dp[j]) + 1LL * (i-j) * p[i-1].x;
if(j < i - 1)
f[j] = min(f[j], f[j + 1]);
}

for(int j = st; j < i; j++)


{
g[j] = -sum[j] + min(dp[j-1], dp[j]) + 1LL * (i-j) * p[i].x;
if(j > st)
g[j] = min(g[j], g[j - 1]);
}

lastSz = i - st;
st = i;
}
else
sum[i] = sum[i - 1] - p[i - 1].x;

if(st == 1)
{
dp[i] = 1e18;
continue;
}

int sz = i - st + 1;
long long curSum = sum[st] - sum[i] + p[i].x;
if(sz >= lastSz)
dp[i] = f[st - lastSz] + curSum - 1LL * sz * p[st - 1].x;
else
dp[i] = min(g[st - sz - 1] + curSum - 1LL * sz * p[st].x,
f[st - sz] + curSum - 1LL * sz * p[st - 1].x);
}

return dp[n];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
CAPITOLUL 3. IOI 2017 3.2. WIRING 271

assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.464
t3-t2 = 0.037
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.6: wiring-malek-ac.cpp


#include "wiring.h"
#include<bits/stdc++.h>

using namespace std;

#define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)


#define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
#define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
#define sz(x) (int((x).size()))
#define pb push_back
#define all(X) (X).begin(),(X).end()
#define X first
#define Y second
#define endl ’\n’

template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }


template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }

typedef long long ll;


typedef pair<int, int> pii;

////////////////////////////////////////////////////////////////////////////

const ll infll = 1LL << 62;


const int maxn = 500000 + 100;

int n;
pii p[maxn];
int st[maxn], ed[maxn];
ll ps[maxn];
ll dp[maxn][2];

long long min_total_length (vector <int> red, vector <int> blue)


{
int nb = blue.size(), nr = red.size();
rep(i, nb) p[i] = pii(blue[i], 0);
rep(i, nr) p[i+nb] = pii(red[i], 1);

n = nb + nr;
CAPITOLUL 3. IOI 2017 3.2. WIRING 272

sort(p, p+n);

rep(i, n) st[i] = (i == 0 || p[i].Y != p[i-1].Y ? i : st[i-1]);


rof(i, n, 0) ed[i] = (i == n-1 || p[i].Y != p[i+1].Y ? i+1 : ed[i+1]);

ps[0] = 0;
rep(i, n) ps[i+1] = ps[i] + p[i].X;

rep(i, n+1) dp[i][0] = dp[i][1] = infll;


dp[0][0] = 0;
fer(i, 1, n+1)
{
int s = st[i-1];
int e = ed[i-1];

if(s == i-1) smin(dp[i][0], dp[i-1][1]);


if(s != 0) {
smin(dp[i][0], dp[i-1][0] + p[i-1].X - p[s-1].X);
int len = i - s;
if(s - len >= 0)
smin(dp[i][0], dp[s-len][0]+(ps[i]-ps[s])-(ps[s]-ps[s-len]));
}
if(e != n) smin(dp[i][1], dp[i-1][0] + p[e].X - p[i-1].X);
smin(dp[i][0], dp[i][1]);
}

return dp[n][0];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.438
t3-t2 = 0.194
t4-t3 = 0

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


Press any key to continue.
*/
CAPITOLUL 3. IOI 2017 3.2. WIRING 273

Listing 3.2.7: wiring-saeed-ac.cpp


#include "wiring.h"

#include <iostream>
#include <vector>
#include <algorithm>
#include<ctime>
#include <cassert>

using namespace std;

const int MAXN = 1000000;

const long long inf = 1LL<<60;

int nex[MAXN];
long long sum[MAXN], dp[MAXN], f[MAXN];

inline long long get_sum(const int lo, const int hi)


{
return sum[lo] - sum[hi+1];
}

long long min_total_length(vector <int> red, vector <int> blue)


{
int n = blue.size(), m = red.size();
vector< pair<int,int> > q;
int pb = 0, pr = 0;
while (pb < n || pr < m)
{
if ((pr == m) || (pb < n && blue[pb] < red[pr]))
q.push_back(make_pair(blue[pb++], 0));
else
q.push_back(make_pair(red[pr++], 1));
}

int nm = n + m;
sum[nm] = 0, f[nm] = 0;
dp[nm-1] = inf, nex[nm-1] = nm, sum[nm-1] = q[nm-1].first;
for (int i = nm - 2; i >= 0; i--)
{
dp[i] = inf;
sum[i] = sum[i+1] + q[i].first;
nex[i] = q[i].second == q[i+1].second ? nex[i+1] : i+1;
if (nex[i] == i+1)
{
for (int j = nex[i+1]-1; j >= i+1; j--)
f[j] = min(dp[j], q[j].first - q[i].first + f[j+1]);
}

if (nex[i] == nm)
continue;
dp[i] = min(inf, dp[i+1] + q[nex[i]].first - q[i].first);
int sz = nex[i] - i;
if (nex[i]+sz-1 < nex[nex[i]])
dp[i] = min(dp[i],
get_sum(nex[i],
nex[i]+sz-1) - get_sum(i, nex[i]-1) + f[nex[i]+sz]);
}

return dp[0];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
CAPITOLUL 3. IOI 2017 3.2. WIRING 274

assert(1 == scanf("%d", &r[i]));


for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.457
t3-t2 = 0.091
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.8: wiring-197505.cpp


// https://oj.uz/submission/197505 56 ms 9584 KB

#include <bits/stdc++.h>
#include "wiring.h"

using namespace std;

#define pb push_back
#define mp make_pair
long long n,dp[200001],presum[200001];
vector<pair<int,int> > arr;
int prevv[200001];
long long min_total_length(vector<int> r,vector<int> b)
{
n=r.size()+b.size();
int rid=0,bid=0;
while (rid<r.size() || bid<b.size())
{
if (rid==r.size())
{
arr.pb(mp(b[bid],1));
bid++;
}
else
if (bid==b.size())
{
arr.pb(mp(r[rid],0));
rid++;
}
else
{
if (r[rid]<=b[bid])
{
arr.pb(mp(r[rid],0));
rid++;
}
else
CAPITOLUL 3. IOI 2017 3.2. WIRING 275

{
arr.pb(mp(b[bid],1));
bid++;
}
}
}

dp[0]=1e15;
prevv[0]=-1;
presum[0]=arr[0].first;

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


{
presum[i]=presum[i-1]+arr[i].first;
dp[i]=1e15;
if (arr[i].second==arr[i-1].second)
prevv[i]=prevv[i-1];
else
prevv[i]=i-1;

for (int j=prevv[i-1]+1;j<=prevv[i];j++)


{
dp[j]=min(dp[j],dp[j-1]+arr[i].first-arr[j].first);
}

if (prevv[i]==-1) continue;

dp[i]=min(dp[i],dp[i-1]+arr[i].first-arr[prevv[i]].first);
if (prevv[prevv[i]]<=prevv[i]-(i-prevv[i]))
{
long long cur=presum[i]-presum[prevv[i]];
cur-=(presum[prevv[i]]-presum[prevv[i]-(i-prevv[i])]);
if (prevv[i]-(i-prevv[i])!=-1)
cur+=dp[prevv[i]-(i-prevv[i])];
dp[i]=min(dp[i],cur);
}
}

return dp[n-1];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 3. IOI 2017 3.2. WIRING 276

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.406
t3-t2 = 0.062
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.9: wiring-200512.cpp


// https://oj.uz/submission/200512 93 ms 15048 KB

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; /// dont use define int long long

void Minimize(ll &a, const ll &b)


{
a = min(a, b);
}

ll min_total_length(vector<int> r, vector<int> b)
{
vector<vector<ll>> dp(0), prf(0);
vector<pair<ll, ll>> vt;
for(auto i: r) vt.emplace_back(i, 0);
for(auto i: b) vt.emplace_back(i, 1);
sort(vt.begin(), vt.end());
int cur = 0;
while(cur < vt.size())
{
int nxt = cur;
vector<ll> nprf = {0};
for(; nxt < vt.size() && vt[nxt].second == vt[cur].second; nxt++)
{
nprf.push_back(vt[nxt].first + nprf.back());
}
cur = nxt;
vector<ll> blank(nprf.size(), (ll)1e17);
dp.push_back(blank);
prf.push_back(nprf);
}

auto Get = [&](ll x, ll y)


{
ll ans = prf[x][y] - prf[x][y - 1];
return ans;
};

dp[0][0] = 0;
for(int i = 0; i < dp.size(); i++)
{
for(int j = 0; j + 1 < dp[i].size(); j++)
{
if(i > 0)
Minimize(dp[i][j + 1],
dp[i][j] + Get(i, j + 1) - Get(i - 1,
prf[i - 1].size() - 1));

if(i + 1 < dp.size())


{
if(prf[i].size() - j - 1 <= prf[i + 1].size() - 1)
{
Minimize(dp[i + 1][prf[i].size() - j - 1],
dp[i][j] + prf[i + 1][prf[i].size() - j - 1]
- prf[i][prf[i].size() - 1] + prf[i][j]);
}

Minimize(dp[i][j + 1],
dp[i][j] - Get(i, j + 1) + Get(i + 1, 1));
CAPITOLUL 3. IOI 2017 3.2. WIRING 277

}
}

if(i + 1 < dp.size()) Minimize(dp[i + 1][0], dp[i].back());


else return dp[i].back();
}
return 0;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.391
t3-t2 = 0.5
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.10: wiring-206573.cpp


// https://oj.uz/submission/206573 75 ms 21352 KB

#include<bits/stdc++.h>

#define pb push_back
#define mp make_pair
#define sz(x) (int)x.size()
#define f first
#define s second
#define all(x) x.begin(), x.end()
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forev(i, a, b) for(int i = a; i >= b; i--)

using namespace std;

typedef long long ll;


CAPITOLUL 3. IOI 2017 3.2. WIRING 278

typedef vector<int> vi;


typedef pair<int, int> pii;
typedef vector<pii> vpii;

const int maxn = (int)1e6 + 100;


const int mod = (int)1e9 + 7;
const int inf = (int)2e9;
const double pi = acos(-1.0);

int n, m, a, b, go[maxn], was[2], cnt[maxn], used[maxn + maxn], K = 500000;


ll dp[maxn], s[maxn];
vpii v;

ll min_total_length(vi v1, vi v2)


{
n = sz(v1), m = sz(v2);
for(auto a : v1) v.pb(mp(a, 0));
for(auto b : v2) v.pb(mp(b, 1));
sort(all(v));
forn(i, 0, maxn - 1) go[i] = inf;
was[0] = was[1] = -1;
int i = 0;
for(auto x : v)
{
i++;
if(was[!x.s] != -1) go[i] = x.f - was[!x.s];
was[x.s] = x.f;
if(x.s) cnt[i] = 1, s[i] = x.f;
else cnt[i] = -1, s[i] = -x.f;
cnt[i] += cnt[i - 1];
s[i] += s[i - 1];
}

was[0] = was[1] = -1; i = n + m + 1;


reverse(all(v));

for(auto x : v)
{
i--;
if(was[!x.s] != -1) go[i] = min(go[i], was[!x.s] - x.f);
was[x.s] = x.f;
}

memset(used, -1, sizeof(used));


used[K] = 0;
forn(i, 1, n + m)
{
dp[i] = dp[i - 1] + go[i];
if(used[cnt[i] + K] != -1)
{
int j = used[cnt[i] + K] + 1;
dp[i] = min(dp[i], dp[j - 1] + abs(s[i] - s[j - 1]));
}
used[cnt[i] + K] = i;
}
return dp[n + m];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();
CAPITOLUL 3. IOI 2017 3.2. WIRING 279

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.406
t3-t2 = 0.5
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.11: wiring-216947.cpp


// https://oj.uz/submission/216947 69 ms 11144 KB

#include "wiring.h"
#include <algorithm>
#include <vector>

#include<ctime>
#include<iostream>
#include<cassert>

using namespace std;

using llong = long long;


const int inf = 1000’000’000;

llong min_total_length(vector<int> red, vector<int> blue)


{
int n = red.size() + blue.size();
vector<pair<int, char> > temp; temp.reserve(n);

for (int i = 0; i < red.size(); ++i)


temp.push_back({red[i], ’x’});

for (int i = 0; i < blue.size(); ++i)


temp.push_back({blue[i], ’o’});

vector<char> a(n);
vector<int> b(n);

sort(temp.begin(), temp.end());

for (int i = 0; i < n; ++i)


b[i] = temp[i].first, a[i] = temp[i].second;

vector<int> lone(n);
for (int i = 0; i < n; ++i)
{
int j = i;
while (j + 1 < n && a[j + 1] == a[i]) ++j;
for (int k = i; k <= j; ++k)
lone[k] = min(i ? b[k]-b[i-1] : inf,
j+1 < n ? b[j+1]-b[k] : inf);
i = j;
CAPITOLUL 3. IOI 2017 3.2. WIRING 280

vector<int> mirror(n, -1);


vector<llong> mirrorval(n);

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


if (a[i] != a[i - 1])
for (int r = i, l = i - 1;
0 <= l && r < n && a[l] == a[i - 1] && a[r] == a[i];
--l, ++r)
{
mirror[r] = l;
mirrorval[r] = b[r] - b[l];
if (r > i)
mirrorval[r] += mirrorval[r - 1];
}

vector<llong> dp(n);
dp[0] = lone[0];

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


{
dp[i] = dp[i - 1] + lone[i];
if (mirror[i] == -1) continue;
llong curdp = 0;
curdp += (mirror[i] ? dp[mirror[i] - 1] : 0);
curdp += mirrorval[i];
dp[i] = min(dp[i], curdp);
}

return dp[n - 1];


}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.39
t3-t2 = 0.453
t4-t3 = 0
CAPITOLUL 3. IOI 2017 3.2. WIRING 281

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


Press any key to continue.
*/

Listing 3.2.12: wiring-221801.cpp


// https://oj.uz/submission/221801 212 ms 62184 KB
/**
* user: ppavic
* fname: Patrik
* lname: P a v i
* task: wiring
* score: 100.0
* date: 2019-06-13 11:05:56.973728
*/
#include "wiring.h"
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <unordered_map>

#include<ctime>
#include<cassert>
#include<iostream>

#define X first
#define Y second
#define PB push_back

using namespace std;

typedef long long ll;


typedef pair < int, int > pii;
typedef vector < int > vi;
typedef vector < pii > vp;

const int N = 3e5 + 500;


const int LOG = 18;
const ll INF = 1e18;
const int OFF = (1 << LOG);

ll prf[N];
map < int , ll > dp[N];
int n, L[N], R[N], G[N], rv[N];
vp v;

inline ll dis(int x,int y)


{
return v[y].X - v[x].X;
}

inline ll get(int x,int y)


{
if(x > y) return 0;
return prf[y] - (x ? prf[x - 1] : 0);
}

ll f(int i,int lst)


{
if(dp[i][lst] != 0)
return dp[i][lst] - 1;
if(i == n)
{
if(lst > 0 && i > 1)
return -1 + (dp[i][lst] = f(i, lst - 1)
+ dis(L[i - 1] - 1, L[i] - lst)
+ 1);
return -1 + (dp[i][lst] = (lst != 0) * INF + 1);
}

ll ret = INF;
if(lst > 0)
CAPITOLUL 3. IOI 2017 3.2. WIRING 282

ret = min(ret, f(i, lst - 1) + dis(L[i] - lst, L[i]));


if(lst <= G[i])
ret = min(ret, f(i + 1, G[i] - lst)
+ get(L[i], L[i] + lst - 1)
- get(L[i] - lst, L[i] - 1));
if(i > 1 && lst > 0)
ret = min(ret, f(i, lst - 1) + dis(L[i - 1] - 1, L[i] - lst));

return -1 + (dp[i][lst] = ret + 1);


}

ll min_total_length(vi r, vi b)
{
for(int i = 0;i < N;i++) dp[i].clear();
ll S_r = 0, S_b = 0;
for(int x : r) v.PB({x, 1});
for(int x : b) v.PB({x, 0});

sort(v.begin(), v.end());

int l = 0, ll = 0, gr = 0;
for(int i = 0;i < v.size();i++)
{
prf[i] = (long long)(v[i].X) + (i ? prf[i - 1] : 0);
if(v[i].Y != l && i != 0)
{
if(gr)
rv[gr] = G[gr - 1] + rv[gr - 1];
G[gr] = i - ll;
L[gr] = ll;
R[gr++] = i - 1;
ll = i;
}

l = v[i].Y;
}

rv[gr] = G[gr - 1] + rv[gr - 1];


G[gr] = v.size() - ll; L[gr] = ll; R[gr] = v.size() - 1;
n = ++gr; L[n] = v.size();

return f(0, 0);


}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;


CAPITOLUL 3. IOI 2017 3.2. WIRING 283

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.438
t3-t2 = 1.265
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.2.13: wiring-227702.cpp


// https://oj.uz/submission/227702 68 ms 12520 KB

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

#define REP(i, n) for(int i = 0; i < n; i++)


#define FOR(i, a, b) for(int i = a; i <= b; i++)
#define ST first
#define ND second

using LL = long long;


using PII = pair<int, int>;

#include "wiring.h"

LL min_total_length(vector<int> r, vector<int> b)
{
vector<PII> p;
for(int x : r) p.emplace_back(x, 1);
for(int x : b) p.emplace_back(x, 2);
sort(p.begin(), p.end());

int n = size(p);
vector<int> s(n);
FOR(i, 1, n - 1)
s[i] = (p[i].ND != p[i - 1].ND ? i : s[i - 1]);

LL inf = 1e18;
vector<LL> dp(n, inf), x(n), y(n, inf), sum(n);
REP(i, n) sum[i] = (i != 0 ? sum[i - 1] : 0) + p[i].ST;

auto get = [&](int l, int r)


{
return sum[r] - (l != 0 ? sum[l - 1] : 0);
};

REP(i, n)
{
if(s[i] == 0) continue;
int u = s[i] - 1, v = s[i];
auto rel = [&](int l, int r)
{
return abs(get(l, r) - LL(r - l + 1) * p[v].ST);
};

if(v == i)
{
LL cur = inf;
FOR(j, s[u], u)
{
cur = min(cur, (j == 0 ? 0 : dp[j - 1]) + rel(j, u));
x[j] = cur;
}

cur = inf;
CAPITOLUL 3. IOI 2017 3.2. WIRING 284

for(int j = v; j < n && s[j] == v; j++)


{
int t = u + v - j;
cur += p[j].ST - p[u].ST;
if(s[u] <= t)
{
cur = min(cur,
rel(t, u)+rel(v, j)+(t == 0 ? 0 : dp[t-1]));
}
y[j] = cur;
}
}

if(s[i] - s[s[i] - 1] == 1)
dp[i] = min(dp[u],
(u == 0 ? 0 : dp[u - 1]))
+ rel(v, i)
+ LL(i - u) * (p[v].ST - p[u].ST);
else
{
dp[i] = y[i];
if(i - u <= u - s[u] + 1)
dp[i] = min(dp[i], x[u - (i - v)] + rel(v, i));
}
}

return dp[n - 1];


}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.421
t3-t2 = 0.485
t4-t3 = 0

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


Press any key to continue.
*/
CAPITOLUL 3. IOI 2017 3.2. WIRING 285

Listing 3.2.14: wiring-227979.cpp


// https://oj.uz/submission/227979 89 ms 12136 KB
#include "wiring.h"
#include <bits/stdc++.h>

#define ll long long


#define st first
#define nd second

using namespace std;

const ll INF = 1e15+1;

ll min_total_length(vector<int> r, vector<int> b)
{
int n = r.size(), m = b.size();
vector<pair<int, bool>> p;
for(int i: r)
{
p.push_back({i, 0});
}

for(int i: b)
{
p.push_back({i, 1});
}

sort(p.begin(), p.end());

vector<vector<ll>> block;
for(int i=0; i<n+m; ++i)
{
if(i==0 || p[i].nd!=p[i-1].nd)
{
block.push_back({});
}
block.back().push_back(p[i].st);
}

vector<vector<ll>> dp(2, vector<ll> (max(n,m)+1));


for(int i=1; i<=(int)block[0].size(); ++i)
{
dp[0][i]=INF;
}

auto prev=block[0];
block.erase(block.begin());
bool type = 1;
int nr=0;
for(auto i: block)
{
dp[type][0] = dp[!type][(int)prev.size()];
vector<ll> suf(prev.size()+1);
for(int j=1; j<=(int)prev.size(); ++j)
{
suf[j] = suf[j-1]+prev[(int)prev.size()-j];
}

ll pref=0;
int k = 0;
if(nr==0) k=(int)prev.size();
for(int j=1; j<=(int)i.size(); ++j)
{
pref+=i[j-1];
while(k<(int)prev.size())
{
ll v1 = pref - suf[k]
- max(0LL, (ll)j-k)*prev.back()
+ max(0LL, (ll)k-j)*i[0]
+ dp[!type][(int)prev.size()-k];

ll v2 = pref - suf[k+1]
- max(0LL, (ll)j-k-1)*prev.back()
+ max(0LL, (ll)k+1-j)*i[0]
+ dp[!type][(int)prev.size()-k-1];
CAPITOLUL 3. IOI 2017 3.2. WIRING 286

if(v2<=v1)
k++;
else
break;
}

dp[type][j] = pref - suf[k]


- max(0LL, (ll)j-k)*prev.back()
+ max(0LL, (ll)k-j)*i[0]
+ dp[!type][(int)prev.size()-k];
}

prev = i;
type ˆ= 1;
nr++;
}

return dp[!type][(int)prev.size()];
}

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin) ;


std::freopen("wiring.out.txt", "w", stdout) ;

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> r(n), b(m);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &r[i]));
for(int i = 0; i < m; i++)
assert(1 == scanf("%d", &b[i]));

auto t2 = clock();

long long res = min_total_length(r, b);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 15991144105164

t2-t1 = 0.422
t3-t2 = 0.516
t4-t3 = 0

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


Press any key to continue.
*/
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 287

3.2.3 *Rezolvare detaliată

3.3 Toy Train


Problema 3 - Toy Train 100 de puncte

Author: Saeed Seddighin (Iran)

Arezou şi fratele ei Borzou sunt gemeni. Cei doi au primit ca şi cadou de ziua lor un set cu
trenuri de jucărie. Cei doi au construit cu ajutorul cadoului un sistem de cale ferată cu n staţii
şi m linii orientate. Staţiile sunt numerotate de la 0 la n  1. Fiecare linie pleacă dintr-o staţie si
ajunge in aceeasi sau alta staţie. Exista cel putin o linie care pleaca din fiecare staţie.
Unele statii sunt staţii de ı̂ncărcare. Cand un tren ajunge ı̂ntr-o staţie de incărcare, se ı̂ncarcă
complet. Un tren incărcat complet are destulă energie sa parcurgă n linii consecutive. Astfel,
cand trenul ajunge pe a n  1 - a linie de la ultima incărcare, va ramâne fără energie şi se va opri.
Fiecare staţie are un macaz care se poate indrepta către una din liniile care pornesc din staţia
curentă. Cand un tren se află ı̂ntr-o staţie, pleaca din acea staţie prin linia spre care e ı̂ndreptat
macazul.
Gemenii vor să se joace cu trenul lor. Acestia şi-au ı̂mparţit staţiile ı̂ntre ei: fiecare staţie este
deţinuta de Arezou sau de Borzou. Există un singur tren. La ı̂nceputul jocului trenul se află ı̂n
staţia s şi e ı̂ncărcat complet. Pentru a ı̂ncepe jocul, proprietarul staţiei s ı̂ndreapta macazul din
staţie către una din staţiile care pornesc din staţia s. Apoi dau drumul la tren şi trenul işi ı̂ncepe
traseul dealungul liniilor.
De fiecare data cănd trenul intra ı̂ntr-o staţie pentru prima data, proprietarul acelei staţii
fixează macazul din acea staţie.
Odată ce macazul a fost fixat, acesta rămâne ı̂ndreptat spre aceeasi linie pentru restul jocului.
Astfel, daca un tren reintra ı̂ntr-o staţie pe care a vizitat-o ı̂nainte, o va părăsi pe aceeaşi linie ca
data precedentă.
Din moment ce există un număr finit de staţii, trenul eventual va intra intr-un ciclu. Un ciclu
este o secvenţa de staţii distincte c0, c1, ..., ck  1, astfel ı̂ncât trenul pleacă din staţia ci
(pentru 0 & i $ k  1) pe o linie către staţia ci  1, şi pleacă din staţia ck  1 pe o linie către
staţia c0. Un ciclu poate fi format şi dintr-o singură staţie (ex. k 1) dacă trenul pleacă din
staţia c0 pe o linie care se indreaptă ı̂napoi spre c0.
Arezou câştigă jocul dacă trenul merge incontinuu, şi Borzou cı̂stigă dacă trenul rămâne fără
energie. Altfel spus, dacă există cel puţin o staţie de ı̂ncărcare printre c0, c1, ..., ck  1,
trenul se poate reincărca, ceea ce ı̂nseamna că va merge incontinuu şi Arezou va câştiga. ı̂n caz
contrar, trenul va rămâne fără energie (probabil dupa ce va parcurge ciclul de câteva ori), ceea ce
inseamnă că va castiga Borzou.
Se dă sistemul de cale de ferata. Arezou şi Borzou vor juca n jocuri. ı̂n al s -lea joc, pentru
0 & s & n  1, trenul va fi iniţial in statia s. Se cere pentru fiecare joc sa se afle daca exista o
strategie pentru Arezou care sa ı̂i garanteze victoria indiferent cum joaca Borzou.
Detalii de implementare
Se cere să se implementeze următoarea procedură:

int[] who_wins(int[] a, int[] r, int[] u, int[] v)

a a: un sir de lungime n Daca Arezou deţine a i -a staţie, ai 1. Altfel, Borzou deţine a
i -a staţie si ai 0. . a r: un şir de lungime n. Dacă a i -a statie este o staţie de ı̂ncărcare,
atunci ri 1. Altfel, ri 0.
a u şi v: doua şiruri de lungime m. Pentru toate 0 & i & m  1, există o linie orientată care
pleacă din staţia ui şi ajunge ı̂n staţia v i.
a Această procedură ar trebui să returneze un şir w de lungime n. Pentru toate 0 & i & n  1,
valoarea lui wi ar trebui sa fie 1 daca Arezou poate câştiga jocul care incepe ı̂n staţia i, neluand
ı̂n considerare cum joacă Borzou. Altfel, valoare lui wi ar trebui să fie 0.
Exemplu

who_wins([0, 1], [1, 0], [0, 0, 1, 1], [0, 1, 0, 1])


CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 288

a Sunt 2 statii. Borzou este proprietarul staţiei 0, care este o staţie de ı̂ncărcare. Arezou este
proprietarul staţiei 1, care nu este o staţie de ı̂ncărcare.
a Sunt 4 linii (0,0), (0,1), (1,0), si (1,1), unde (i,j) semnifica o linie orientata de la staţia i la
staţia j.
a Considerăm jocul ı̂n care trenul este iniţial ı̂n staţia 0. Daca Borzou fixează macazul staţiei 0
către linia (0,0), trenul va merge incontinuu prin această linie (de luat ı̂n considerare că staţiaeste
o staţie de ı̂ncărcare). ı̂n acest caz, Arezou castigă. ı̂n caz contrar, daca Borzou fixează macazul
staţiei 0 către linia (0,1), Arezou poate ı̂ndrepta macazul staţiei 1 către linia (1,0). ı̂n acest caz
va merge incontinuu prin ambele staţii. Din nou Arezou câştigă, din moment ce staţia 0 este o
statie de ı̂ncărcate şi trenul nu se va opri. Asadar, Arezou câştigă jocul, indiferent ce face Borzou.
a Similar, ı̂n jocul care incepe ı̂n staţia 1 Arezou de asemenea poate câştiga, indiferent cum
joaca Borzou. Asadar, procedura ar trebui să returneze [1,1].
Restricţii si precizări
a 1 & n & 5 000.
a n & m & 20 000.
a Există cel puţin o staţie de ı̂ncărcare.
a Există cel puţin o linie care porneşte din fiecare staţie.
a Pot exista linii care pornesc şi se termină ı̂n aceeaşi staţie (ex. ui v i).
a Fiecare linie este distinctă. In alte cuvinte, nu există doua poziţii i si j (0 & i $ j & m  1)
astfel incăt ui uj  si v i v j .
a 0 & ui, v i & n  1 (pentru toate 0 & i & m  1).

Subtask-uri
1. (5 puncte) Pentru fiecare 0 & i & m  1, avem v i ui ori v i ui  1.
2. (10 puncte) n & 15.
3. (11 puncte) Arezou deţine toate staţiile.
4. (11 puncte) Borzou deţine toate staţiile.
5. (12 puncte) Exista o singură staţie de ı̂ncărcare.
6. (51 puncte) Nu exista restricţii adiţionale.
Evaluator local
Evaluatorul local citeşte inputul ı̂n următorul format:
a linia 1: n m
a linia 2: a0, a1, ..., an  1
a linia 3: r 0, r 1, ..., r n  1,
a linia 4+i (pentru 0 & i & m  1): ui v i
Evaluatorul local printează valoarea returnată de who_wins ı̂n următorul format:
a linia 1: w 0, w 1, ..., w n  1

Timp maxim de executare/test: 2.0 secunde


Memorie: total 256 MB

3.3.1 Indicaţii de rezolvare

For a set of stations S, define fA S  as the set of all stations such that if the train is placed on
them, Arezou can play in a way that the train reaches one of the stations in S at some point
regardless of Borzou’s moves (therefore S N fA S  by definition). We define fB S  similarly.
Initially let T S, and we iteratively add stations to T such that eventually T equals fA S .
While there exists a station v that satisfies one of the following conditions, we add v to set T .
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 289

1. v is owned by Arezou, and there exists an outgoing track from v that leads to a station
already in T .
2. v is owned by Borzou, and all of the outgoing tracks from v leads to the stations already in
T . Similarly we can compute fB S . Notice that both fA S  and fB S  can be computed
iteratively in time O n  m.

Now let R be the set of all the charging stations. By definition, for every station v Š fA R,
Borzou can win the game if the train is initially placed on v.
Therefore, we can solve the problem as follows:
ˆ If fA R is the set of all remaining stations, Arezou can win the game for all initial stations.
ˆ Otherwise:
1. Let X be set of stations not in fA R.
2. Borzou can win the game if the initial stations is in fB X .
3. Remove fB X  from the graph and solve the problem recursively.

This algorithm runs in time O nm.

3.3.2 Coduri sursă

Listing 3.3.1: train.cpp


// http://ioi2017.org/tasks/materials/train.zip ---> sandbox/train.cpp
// http://ioi2017.org/tasks/materials/train.zip ---> solution/***.cpp

#include "train.h"
#include <vector>
#include <algorithm>

#include <cstdio> // grader ...


#include <cassert> // grader ...

#include <fstream> // ***

using namespace std;

ifstream cinf("01.in"); // ***

#define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)


#define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
#define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
#define sz(x) (int((x).size()))
#define pb push_back
#define all(X) (X).begin(),(X).end()
#define X first
#define Y second

template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }


template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }

typedef long long ll;


typedef pair<int, int> pii;

////////////////////////////////////////////////////////////////////////////

const int maxn = 300000 + 100;

vector<int> radj[maxn];
int deg[maxn], need[maxn];

void dfs(int u, vector<int> &w)


{
for(int v: radj[u])
{
need[v]--;
if(!w[v] && need[v] == 0)
dfs(v, w); // only dfs through non-charging vertices
}
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 290

vector<int> who_wins(vector<int> a,vector<int> r,vector<int> u,vector<int> v)


{
int n = sz(a);
rep(i, sz(u)) radj[v[i]].pb(u[i]), deg[u[i]]++;
vector<int> res(n, 1);

for(bool cng = true; !(cng = !cng); )


{
rep(i, n)
need[i] = (a[i] ? 1 : deg[i]); // d only matters here

rep(i, n)
if(r[i] && res[i])
dfs(i, r); // dfs from yet-has-a-chance-to-win wells

rep(i, n)
if(r[i] && res[i] && need[i] > 0)
res[i] = false, cng = true; // and see if they had no chance
}

rep(i, n)
res[i] &= (need[i] <= 0); // look, more fopdoodles

return res;
}

int main() // grader ...


{
int n, m;
cinf >> n >> m; // ***

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


cinf >> a[i]; // ***

for(int i = 0; i < n; i++)


cinf >> r[i]; // ***

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


cinf >> u[i] >> v[i]; // ***

vector<int> res = who_wins(a, r, u, v);

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

return 0;
}

Listing 3.3.2: train+grader.cpp


#include "train.h"

#include <cstdio>
#include <vector>
#include <cassert>

#include <string>
#include <algorithm>
#include<ctime>
#include<iostream>

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

// -----------------------------------------------------------
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 291

#define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)


#define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
#define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
#define sz(x) (int((x).size()))
#define pb push_back
#define all(X) (X).begin(),(X).end()
#define X first
#define Y second

template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }


template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }

typedef long long ll;


typedef pair<int, int> pii;

////////////////////////////////////////////////////////////////////////////

const int maxn = 300000 + 100;

vector<int> radj[maxn];
int deg[maxn], need[maxn];

void dfs(int u, vector<int> &w)


{
for(int v: radj[u])
{
need[v]--;
if(!w[v] && need[v] == 0)
dfs(v, w); // only dfs through non-charging vertices
}
}

vector<int> who_wins(vector<int> a, vector<int> r,


vector<int> u, vector<int> v)
{
int n = sz(a);
rep(i, sz(u)) radj[v[i]].pb(u[i]), deg[u[i]]++;
vector<int> res(n, 1);
for(bool cng = true; !(cng = !cng); )
{
rep(i, n)
need[i] = (a[i] ? 1 : deg[i]); // d only matters here
rep(i, n)
if(r[i] && res[i])
dfs(i, r); // dfs from yet-has-a-chance-to-win wells
rep(i, n)
if(r[i] && res[i] && need[i] > 0)
res[i] = false, cng = true; // and see if they had no chance
}

rep(i, n)
res[i] &= (need[i] <= 0); // look, more fopdoodles

return res;
}

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

int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 292

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.063
t3-t2 = 2.7
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.3.3: checkerModificat.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";

int main()
{
int argc=4;

char* argv[] =
{
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 293

(char*)"checker",
(char*)"../tests/6-25.in", // input
(char*)"../tests/6-25.out", // rezultat corect
(char*)"train.out.txt", // rezultat de verificat si acordat punctaj
};

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

registerChecker("train", argc, argv);

readBothSecrets(output_secret);
readBothGraderResults();

compareRemainingLines(3);
}
/*
argc = 4
checker
../tests/6-25.in
../tests/6-25.out
train.out.txt
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 3.3.4: train-malek-ac.cpp


#include "train.h"

#include <cstdio>
#include <vector>
#include <cassert>

#include <string>
#include <algorithm>
#include<ctime>
#include<iostream>

#include <queue>
#include <cstring>

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

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

#define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)


#define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
#define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
#define sz(x) (int((x).size()))
#define pb push_back
#define all(X) (X).begin(),(X).end()
#define X first
#define Y second

template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }


template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }

typedef long long ll;


typedef pair<int, int> pii;

///////////////////////////////////////////////////////////////////////////

const int maxn = 300000 + 100;


CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 294

vector<int> radj[maxn];
int deg[maxn], need[maxn];

void dfs(int u, vector<int> &w)


{
for(int v: radj[u])
{
need[v]--;
if(!w[v] && need[v] == 0)
dfs(v, w); // only dfs through non-charging vertices
}
}

vector<int> who_wins(vector<int> a, vector<int> r,


vector<int> u, vector<int> v)
{
int n = sz(a);
rep(i, sz(u)) radj[v[i]].pb(u[i]), deg[u[i]]++;
vector<int> res(n, 1);
for(bool cng = true; !(cng = !cng); )
{
rep(i, n)
need[i] = (a[i] ? 1 : deg[i]); // d only matters here
rep(i, n)
if(r[i] && res[i])
dfs(i, r); // dfs from yet-has-a-chance-to-win wells
rep(i, n)
if(r[i] && res[i] && need[i] > 0)
res[i] = false, cng = true; // and see if they had no chance
}

rep(i, n)
res[i] &= (need[i] <= 0); // look, more fopdoodles
return res;
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 295

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.054
t3-t2 = 2.731
t4-t3 = 0.008

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


Press any key to continue.
*/

Listing 3.3.5: train-saeed-ac.cpp


#include "train.h"

#include <cstdio>
#include <vector>
#include <cassert>

//#include <string>
#include <algorithm>
#include<ctime>
#include<iostream>

#include <queue>
#include <cstring>

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

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

const int MAXN = 5000+10;

const int B = 0;
const int A = 1;

bool mark[MAXN], checked[MAXN];


int need[MAXN], f[MAXN];
vector<int> adj[MAXN], bak[MAXN];

vector<int> who_wins(vector<int> a, vector<int> r,


vector<int> u, vector<int> v)
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 296

{
for (int i = 0; i < (int)u.size(); i++)
{
adj[u[i]].push_back(v[i]);
bak[v[i]].push_back(u[i]);
}

int n = (int)a.size();
for (int i = 0; i < n; i++)
f[i] = A;

while (true)
{
for (int i = 0; i < n; i++) if (f[i] != B)
{
mark[i] = false;
checked[i] = false;
need[i] = a[i] == A ? 1 : (int)adj[i].size();
}

queue<int> q;
for (int i = 0; i < n; i++) if (r[i] && f[i] == A)
q.push(i);

while (!q.empty())
{
int front = q.front();
q.pop();
if (checked[front] == true)
continue;
checked[front] = true;
for (int i = 0; i < (int)bak[front].size(); i++)
{
int temp = bak[front][i];
--need[temp];
if (need[temp] == 0 && mark[temp] == false && f[temp] == A)
{
q.push(temp);
mark[temp] = true;
}
}
}

bool flag = false;


for (int i = 0; i < n; i++) if (f[i] == A && mark[i] == false)
{
f[i] = B;
flag = true;
}

if (!flag)
break;
}

vector<int> res;
for (int i = 0; i < n; i++)
res.push_back(f[i]);

return res;
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 297

return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.054
t3-t2 = 3.325
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.3.6: train-134142.cpp


// https://oj.uz/submission/134142 688 ms 1528 KB

#include <bits/stdc++.h>

#include "train.h"

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 298

const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";


// END SECRET

vector<int> who_wins(vector<int> a, vector<int> r,


vector<int> u, vector<int> v)
{
int n = a.size();
int m = u.size();
vector<int> deg(n);
vector<vector<int>> g(n);
for (int i = 0; i < m; ++i)
{
++deg[u[i]];
g[v[i]].push_back(u[i]);
}

while (true)
{
vector<int> ans = r;
{
queue<int> q;
vector<int> cdeg = deg;
for (int i = 0; i < n; ++i)
{
if (ans[i])
{
q.push(i);
}
}

while (!q.empty())
{
int u = q.front(); q.pop();
for (int v : g[u])
{
if (!ans[v])
{
if (a[v] || (--cdeg[v]) == 0)
{
ans[v] = 1;
q.push(v);
}
}
}
}
}

{
queue<int> q;
vector<int> cdeg = deg;
for (int i = 0; i < n; ++i)
{
if (!ans[i])
{
q.push(i);
}
}

while (!q.empty())
{
int u = q.front(); q.pop();
for (int v : g[u])
{
if (ans[v])
{
if (!a[v] || (--cdeg[v]) == 0)
{
ans[v] = 0;
q.push(v);
}
}
}
}
}

bool finish = true;


CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 299

for (int i = 0; i < n; ++i)


{
if (!ans[i] && r[i])
{
r[i] = 0;
finish = false;
}
}

if (finish)
{
return ans;
}
}
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;


CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 300

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.078
t3-t2 = 5.147
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.3.7: train-146520.cpp


// https://oj.uz/submission/146520 588 ms 2552 KB

#include <bits/stdc++.h>
#include "train.h"

#define loop(i, a, b) for(int i=a;i<b;i++)


#define maxn 20100

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

vector<int> ba[maxn], fo[maxn], ans, req, po;

void dfs(int u)
{
loop(i, 0, ba[u].size())
{
int v=ba[u][i];
req[v]--;
if(!req[v] && !po[v])
dfs(v);
}
};

vector<int> who_wins(vector<int> A, vector<int> C,


vector<int> U, vector<int> V)
{
int n=A.size(), m=U.size();
po.resize(n);
loop(i, 0, n)
po[i]=C[i];
loop(i, 0, m)
ba[V[i]].push_back(U[i]), fo[U[i]].push_back(V[i]);

ans.resize(n, 1), req.resize(n);

bool cn=1;
while(cn)
{
cn=0;
loop(i, 0, n) req[i]=(A[i])? 1:fo[i].size();
loop(i, 0, n) if(po[i]&&ans[i]) dfs(i);
loop(i, 0, n) if(ans[i]!=(req[i]<1)) ans[i]=(req[i]<1), cn=1;
}

return ans;
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 301

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.062
t3-t2 = 2.386
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.3.8: train-160375.cpp


CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 302

// https://oj.uz/submission/160375

#include "train.h"
#include <bits/stdc++.h>

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

const int AREZOU = 1;


const int BORZOU = 0;
const int CHARGING_STATION = 1;
const int CHARGING_SET = 1;
const int VISITED = 1;

int N, M;
vector<int> station, owner;
vector<vector<int>> G, G_inverse;

vector<int> out_degree;
vector<int> visited;
vector<int> charging_set;
vector<int> new_charging_set;

void initialize_charging_set()
{
visited.assign(N, !VISITED);
out_degree.assign(N, 0);
charging_set.assign(N, !CHARGING_SET);

queue<int> q;
for (int i = 0; i < N; i++)
if (station[i] == CHARGING_STATION &&
new_charging_set[i] == CHARGING_SET)
q.push(i), visited[i] = VISITED;

while (!q.empty())
{
int u = q.front(); q.pop();
charging_set[u] = CHARGING_SET;
for (auto v : G_inverse[u])
{
if (visited[v] == VISITED) continue;

switch (owner[v])
{
case AREZOU:
if (visited[v] == !VISITED)
{
visited[v] = VISITED;
q.push(v);
}
break;
case BORZOU:
out_degree[v]++;
if (out_degree[v] == G[v].size() &&
visited[v] == !VISITED)
{
visited[v] = VISITED;
q.push(v);
}
break;
}
}
}

vector<int> who_wins(vector<int> owner_, vector<int> charge_,


vector<int> u_, vector<int> v_)
{
owner = owner_, station = charge_;
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 303

N = owner.size(), M = u_.size();
G.resize(N), G_inverse.resize(N);
new_charging_set.resize(N, CHARGING_SET);

for (int i = 0; i < M; i++)


{
G[u_[i]].push_back(v_[i]);
G_inverse[v_[i]].push_back(u_[i]);
}

while (charging_set != new_charging_set)


{
initialize_charging_set();

for (int i = 0; i < N; i++)


{
switch (owner[i])
{
case AREZOU:
new_charging_set[i] = !CHARGING_SET;
for (auto v : G[i])
if (charging_set[v] == CHARGING_SET)
new_charging_set[i] = CHARGING_SET;

break;

case BORZOU:
new_charging_set[i] = CHARGING_SET;
for (auto v : G[i])
if (charging_set[v] == !CHARGING_SET)
new_charging_set[i] = !CHARGING_SET;

break;
}
}

vector<int> res(N);
for (int i = 0; i < N; i++)
res[i] = (charging_set[i] == CHARGING_SET);

return res;
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 304

assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.078
t3-t2 = 6.343
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.3.9: train-163181.cpp


// https://oj.uz/submission/163181 957 ms 1656 KB

#include "train.h"
#include <bits/stdc++.h>

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

#define all(aaa) aaa.begin(), aaa.end();


#define ll long long

const int N = 5005;


vector<int> g[N];
int deg[N], tmp[N];
bool used[N];
int n, m;

vector<int> reachable(vector<int> &a, vector<int> v, int master)


{
for (int i = 0; i < n; i++)
{
used[i] = 0;
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 305

tmp[i] = deg[i];
}

queue<int> q;

auto check = [&](int x)


{
if (!used[x])
{
if (a[x] == master)
{
used[x] = 1;
q.push(x);
}
else
if (--tmp[x] == 0)
{
used[x] = 1;
q.push(x);
}
}
};

for (int x : v)
{
used[x] = 1;
q.push(x);
}

while (!q.empty())
{
int node = q.front();
q.pop();

for (int to : g[node])


{
check(to);
}
}

vector<int> ans;
for (int i = 0; i < n; i++)
{
if (used[i])
ans.push_back(i);
}

return ans;
}

vector<int> who_wins(vector<int> a, vector<int> r,


vector<int> u, vector<int> v)
{
n = a.size(), m = u.size();

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


{
g[v[i]].push_back(u[i]);
deg[u[i]]++;
}

vector<int> res(n, 1);

bool work = true;

while (work)
{
work = false;

vector<int> sts;
for (int i = 0; i < n; i++)
{
if (r[i])
sts.push_back(i);
}
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 306

vector<int> good = reachable(a, sts, 1), killers;

for (int i = 0, j = 0; i < n; i++)


{
while (j < good.size() && good[j] < i)
j++;
if (j == good.size() ||
good[j] != i)
killers.push_back(i);
}

vector<int> bad = reachable(a, killers, 0);

for (int x : bad)


{
if (r[x])
work = true;
res[x] = 0;
r[x] = 0;
}
}

return res;
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 307

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.078
t3-t2 = 7.346
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.3.10: train-201183.cpp


// https://oj.uz/submission/201183 348 ms 1532 KB

#include "train.h"

#include <algorithm>
#include <vector>
#include<string>
#include<ctime>
#include<cassert>
#include<iostream>

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

const int MAXN = 5e3;

int owner[MAXN], charge[MAXN];


std::vector<int> graph[MAXN];
int degree[MAXN];

std::vector<int> undet;
std::vector<int> removing;
bool toRemove[MAXN], removed[MAXN];
int currentDeg[MAXN];

void reset()
{
for (int node : undet)
{
toRemove[node] = false;
removed[node] = false;
currentDeg[node] = degree[node];
}
}

void flipowners()
{
for (int node : undet)
owner[node] = 1 - owner[node];
}
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 308

void removal()
{
for (int node : undet)
if (toRemove[node])
{
removing.emplace_back(node);
removed[node] = true;
}

while (!removing.empty())
{
int rm = removing.back();
removing.pop_back();

for (int next : graph[rm])


if (!removed[next])
{
currentDeg[next]--;
if (owner[next] == 1 || currentDeg[next] == 0)
{
removing.emplace_back(next);
removed[next] = true;
}
}
}
}

void findlosers()
{
reset();
for (int node : undet)
toRemove[node] = charge[node];
removal();

std::vector<int> losers;
for (int node : undet)
if (!removed[node])
losers.emplace_back(node);

reset();
flipowners();

for (int node : losers)


toRemove[node] = true;
removal();
flipowners();
}

std::vector<int> who_wins(std::vector<int> a, std::vector<int> r,


std::vector<int> u, std::vector<int> v)
{
int stations = a.size();
int tracks = u.size();

std::vector<int> winner(stations, 0);


for (int iNode = 0; iNode < stations; iNode++)
{
owner[iNode] = a[iNode];
charge[iNode] = r[iNode];
undet.emplace_back(iNode);
}

for (int iEdge = 0; iEdge < tracks; iEdge++)


{
graph[v[iEdge]].emplace_back(u[iEdge]);
degree[u[iEdge]]++;
}

while (!undet.empty())
{
findlosers();
std::vector<int> losers;

for (int node : undet)


if (removed[node])
losers.emplace_back(node);
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 309

if (losers.empty())
{
for (int node : undet)
winner[node] = 1;
undet.clear();
}
else
{
for (int node : losers)
{
for (int next : graph[node])
degree[next]--;
undet.erase(std::find(undet.begin(), undet.end(), node));
}
}
}

return winner;
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 310

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.062
t3-t2 = 3.797
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.3.11: train-221800.cpp


// https://oj.uz/submission/221800 494 ms 1792 KB
/**
* user: ppavic
* fname: Patrik
* lname: P a v i
* task: train
* score: 100.0
* date: 2019-06-12 09:27:16.425930
*/
#include "train.h"

#include <vector>
#include <cstring>
#include <queue>
#include<string>
#include<ctime>
#include<cassert>
#include<iostream>

#define PB push_back

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

typedef vector < int > vi;

const int N = 5055;

int spas[N]; /// HDZ


int smrt[N]; /// SDP
int n;

vi v[N], r[N];
int cnt[N], izb[N];
queue < int > Q;

vi who_wins(vi a, vi rr, vi uu, vi vv)


{
n = a.size();
for(int i = 0;i < (int)vv.size();i++)
{
v[uu[i]].PB(vv[i]);
r[vv[i]].PB(uu[i]);
}
int kol = 0;
for(;;kol++)
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 311

{
memset(spas, 0, sizeof(spas));
memset(smrt, 0, sizeof(smrt));
memset(cnt, 0, sizeof(cnt));

for(int i = 0;i < n;i++)


{
if(izb[i]) continue;
if(rr[i] == 1) smrt[i] = 1, Q.push(i);
else
if(a[i] == 1) cnt[i] = 1;
else
{
cnt[i] = 0;
for(int x : v[i])
cnt[i] += !izb[x];
}
}

for(;!Q.empty();Q.pop())
{
int cur = Q.front();
for(int x : r[cur])
{
if(izb[x]) continue;
cnt[x]--;
if(cnt[x] == 0 && !smrt[x])
{
smrt[x] = 1;
Q.push(x);
}
}
}

for(int i = 0;i < n;i++)


{
if(izb[i]) continue;
if(smrt[i] == 0) spas[i] = 1, Q.push(i), cnt[i] = 0;
else
if(a[i] == 0) cnt[i] = 1;
else
{
cnt[i] = 0;
for(int x : v[i])
cnt[i] += !izb[x];
}
}

for(;!Q.empty();Q.pop())
{
int cur = Q.front();
for(int x : r[cur])
{
if(izb[x]) continue;
cnt[x]--;
if(cnt[x] == 0 && !spas[x])
{
spas[x] = 1;
Q.push(x);
}
}
}

int cc = 0;
for(int i = 0;i < n;i++)
if(spas[i] && !izb[i]) cc++, izb[i] = 1;
if(cc == 0) break;
}

vi sol;

for(int i = 0;i < n;i++)


sol.PB(!izb[i]);
return sol;
}
// -----------------------------------------------------------
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 312

int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.062
t3-t2 = 3.562
t4-t3 = 0

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


Press any key to continue.
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 313

*/

Listing 3.3.12: train-222137.cpp


// https://oj.uz/submission/222137 1922 ms 1792 KB

#include "train.h"
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include<ctime>
#include<cassert>

using namespace std;

// BEGIN SECRET
const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
// END SECRET

const int maxN = 5000;

vector<int> nei[maxN];
int charge[maxN], a[maxN];
int ans[maxN];
int col[maxN], isCyc[maxN];
int n;

vector<int> rnei[maxN];
bool used1[maxN];
void DFS1(int v, vector<int>& sorted)
{
used1[v] = true;
for (int to: nei[v])
if (!used1[to])
DFS1(to, sorted);
sorted.push_back(v);
}

void DFS2(int v, int curcol)


{
col[v] = curcol;
for (int to: rnei[v])
if (!col[to])
{
DFS2(to, curcol);
isCyc[curcol] = true;
}
else if (col[to] == curcol)
isCyc[curcol] = true;
}

void SCC()
{
vector<int> sorted;
for (int i = 0; i < n; ++i)
if (!used1[i])
DFS1(i, sorted);
reverse(sorted.begin(), sorted.end());
int cols = 0;
for (int v: sorted)
if (!col[v])
DFS2(v, ++cols);
}

bool used3[maxN];

void DFS3(int v)
{
used3[v] = true;
ans[v] = 1;
for (int to: rnei[v])
if (!used3[to])
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 314

DFS3(to);
}

vector<int> Solve3()
{
SCC();
vector<bool> goodcol(n);
for (int v = 0; v < n; ++v)
if (charge[v])
goodcol[col[v]] = true;
for (int v = 0; v < n; ++v)
if (goodcol[col[v]] && isCyc[col[v]] && !used3[v])
DFS3(v);
return vector<int>(ans, ans + n);
}

bool OK(vector<char>& mask)


{
queue<int> cycle;
for (int i = 0; i < n; ++i)
if (mask[i])
cycle.push(i);
int cnt = cycle.size();
while (cycle.size())
{
int u = cycle.front();
cycle.pop();
if (!mask[u]) continue;
bool g = false;

if (a[u])
{
g = true;
for (int to: nei[u])
if (!mask[to] && ans[to] != 0)
g = false;
}
else
{
g = false;
for (int to: nei[u])
if (mask[to] || ans[to] == 0)
g = true;
}

if (!g)
{
--cnt;
mask[u] = 0;
for (int to: rnei[u])
if (mask[to])
cycle.push(to);
}
}
return cnt;
}

void Add(vector<char>& mask)


{
queue<int> cycle;
for (int i = 0; i < n; ++i)
if (mask[i])
for (int to: rnei[i])
cycle.push(to);

while (cycle.size())
{
int u = cycle.front();
cycle.pop();
if (!ans[u]) continue;
bool g = false;
if (a[u])
{
g = true;
for (int to: nei[u])
if (ans[to] != 0)
CAPITOLUL 3. IOI 2017 3.3. TOY TRAIN 315

g = false;
}
else
{
g = false;
for (int to: nei[u])
if (ans[to] == 0)
g = true;
}
if (g)
{
ans[u] = 0;
for (int to: rnei[u])
if (ans[to])
cycle.push(to);
}
}
}

vector<int> Solve()
{
for (int i = 0; i < n; ++i)
ans[i] = 1;

vector<char> stmask(n);

for (int i = 0; i < n; ++i)


if (!charge[i])
stmask[i] = 1;

while (true)
{
auto mask = stmask;
if (!OK(mask))
break;
for (int i = 0; i < n; ++i)
if (mask[i])
{
ans[i] = 0;
stmask[i] = 0;
}
Add(mask);
}

return vector<int>(ans, ans + n);


}

std::vector<int> who_wins(std::vector<int> a_, std::vector<int> charge_,


std::vector<int> u, std::vector<int> v)
{
n = a_.size();
for (int i = 0; i < n; ++i)
a[i] = a_[i], charge[i] = charge_[i];
for (int i = 0; i < u.size(); ++i)
nei[u[i]].push_back(v[i]);
for (int v = 0; v < n; ++v)
for (int to: nei[v])
rnei[to].push_back(v);
if (!count(a, a + n, 0))
return Solve3();
return Solve();
}
// -----------------------------------------------------------
int main()
{
auto t1 = clock();

std::freopen("../tests/6-25.in", "r", stdin) ;


std::freopen("train.out.txt", "w", stdout) ;

// BEGIN SECRET
char secret[1000];
assert(1 == scanf("%s", secret));
if (string(secret) != input_secret)
{
printf("%s\n", output_secret.c_str());
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 316

printf("SV\n");
return 0;
}
// END SECRET

int n, m;
assert(2 == scanf("%d %d", &n, &m));

vector<int> a(n), r(n), u(m), v(m);

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &a[i]));

for(int i = 0; i < n; i++)


assert(1 == scanf("%d", &r[i]));

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

auto t2 = clock();

vector<int> res = who_wins(a, r, u, v);

auto t3 = clock();

// BEGIN SECRET
printf("%s\n", output_secret.c_str());

if((int)res.size() != n)
{
printf("WA\n");
printf("Wrong returned array size\n");
}
else
printf("OK\n");
// END SECRET

for(int i = 0; i < (int)res.size(); i++)


printf(i ? " %d" : "%d", res[i]);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//std::cout <<"res = "<<res<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -----------------------------------------------------------
/*
t2-t1 = 0.062
t3-t2 = 2.719
t4-t3 = 0

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


Press any key to continue.
*/

3.3.3 *Rezolvare detaliată

3.4 Big Prize


Problema 4 - Big Prize 100 de puncte
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 317

Author: Hamed Valizadeh (Iran)

Jocul ”Marele premiu” este un renumit show de televiziune. Sunteţi norocosul participant care
s-a calificat ı̂n runda finală. Staţi ı̂n faţa unui rând de n cutii, numerotate de la stânga la dreapta
de la 0 până la n  1. Fiecare cutie conţine un premiu care nu poate fi văzut până când cutia nu
este deschisă. Există v ' 2 diferite tipuri de premii. Tipurile de premii sunt numerotate de la 1 la
v ı̂n ordinea descrescătoare a valorii.
Premiul de tipul 1 este cel mai scump: un diamant. Există un singur diamant ı̂n cutii. Premiul
de tip v este cel mai ieftin: o acadea. Pentru a face jocul cât mai captivant, numărul premiilor
ieftine este mult mai mare decât numărul premiilor scumpe. Mai exact, pentru toate t cu 2 & t & v
2
se ştie: dacă există k premii de tip t  1 , atunci există strict mai mult decât k premii de tip t.
Aveţi scopul să câştigaţi diamantul. La sfârşitul jocului va trebui să deschideţi cutia şi să luaţi
premiul din cutie. ı̂nainte de a deschide cutia aleasă trebuie să ı̂l ı̂ntrebaţi pe Rambod, gazda
show- ului, câteva ı̂ntrebări. Pentru fiecare ı̂ntrebare, veţi alege o cutie i. Ca răspuns, Rambod
vă va oferi un şir a care conţine doi ı̂ntregi. Semnificaţia acestor ı̂ntregi este:

a Printre cutiile din stânga cutiei i sunt exact a0 cutii care conţin premii mai scumpe decât
premiul din cutia i .
a Printre cutiile din dreapta cutiei i sunt exact a1 cutii care conţin premii mai scumpe decât
premiul din cutia i.

De exemplu, presupunem că n 8 şi alegeti cutia i 2 ca ı̂ntrebare pusă. Ca răspuns Rambod
vă dă şirul a 1, 2. Semnificaţia acestui răspuns este:

a Exact una din cutiile 0 şi 1 conţine un premiu mai scump decât premiul din cutia 2.
a Exact două din cutiile 3, 4, ..., 7 conţin premii mai scumpe decât premiul din cutia 2.

Sarcina voastră este să găsiţi cutia care conţine diamantul punând un număr mic de ı̂ntrebări.
Detalii de implementare
Trebuie să implementaţi următoarea procedură:

int find_best(int n)

a Această procedură este apelată exact o singură data de către evaluator.


a n: numărul de cutii.
a Această procedură returnează numărul cutiei care conţine diamantul, adică un ı̂ntreg unic d
(0 & d & n  1) astfel ı̂ncât cutia d conţine premiul de tip 1.
Procedura de mai sus poate apela următoarea procedură:

int[] ask(int i)

a i: numărul cutiei despre care vreţi să ı̂ntrebaţi. Valoarea lui i trebuie să fie ı̂ntre 0 şi n  1,
inclusiv.
a Această procedură returnează un şir a cu 2 elemente. a0 este numărul premiilor mai
scumpe din cutiile din stânga cutiei i şi a1 este numărul premiilor mai scumpe din cutiile din
dreapta cutiei i.
Exemplu
Evaluatorul execută următorul apel al procedurii:

find_best(8)

Există n 8 cutii. Presupunem că premiile sunt de următoarele tipuri [3,2,3,1,3,3,2,3]. Toate
cazurile posibile de apel a procedurii ask şi respectiv valorile returnate sunt:
ask(0) returnează [0,3]
ask(1) returnează [0,1]
ask(2) returnează [1,2]
ask(3) returnează [0,0]
ask(4) returnează [2,1]
ask(5) returnează [2,1]
ask(6) returnează [1,0]
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 318

ask(7) returnează [3,0]


În acest exemplu, diamantul este ı̂n cutia 3. Deci, procedura find_best trebuie să returneze
3.

Exemplul este ilustrat ı̂n figura de mai sus. Partea de sus arată valorile premiilor ı̂n fiecare
cutie. Partea de jos arată interogarea ask(2). Cutiile marcate conţin premii mai scumpe decât
cutia cu numărul 2.
Restricţii şi precizări
3 & n & 200 000.
a
Tipul premiilor ı̂n fiecare cutie este ı̂ntre 1 şi v, inclusiv.
a
a Există un singur premiu de tipul 1.
a Pentru toate 2 & t & v, dacă există k premii de tipul t  1, atunci există strict mai mult de
2
k premii de tipul t.
Subtask-uri şi punctaj
În unele teste comportamentul evaluatorului este adaptiv. Adică, ı̂n aceste teste, evaluatorul
nu are o secvenţă fixă de premii. ı̂n schimb, răspunsurile oferite de evaluator poate depinde de
ı̂ntrebările transmise de soluţia voastră. Se garantează că evaluatorul răspunde ı̂n aşa fel ı̂ncât
după fiecare răspuns există cel puţin o secvenţă consistentă de premii cu toate răspunsurile date
până acum.

1. (20 puncte) Există exact 1 diamant şi n  1 acadele (prin urmare, v 2). Puteţi apela
procedura ask de cel mult 10 000 de ori.

2. (80 puncte) Fără restricţii adiţionale.

În subtask-ul 2 puteţi obţine un scor parţial. Fie q numărul maxim de apeluri a procedurii
ask pentru toate testele din acest subtask. Punctajul pentru acest subtask este calculat conform
următorului tabel:

ı̂ntrebări Punctaj
10 000 ¡ q 0 (raportat ı̂n CMS ca ’Wrong Answer’)
6 000 $ q & 10 000 70
5 000 $ q & 6 000 80 - (q - 5 000) / 100
q & 5 000 80

Evaluator local
Evaluatorul local nu este adaptiv. ı̂n schimb, el citeşte şi utilizează un şir fix p de tipuri de
premii. Pentru toate 0 & b & n  1, tipul premiului din cutia b este dat ca pb. Formatul intrării
pentru evaluatorul local este:
a linia 1: n
a linia 2: p0 p1 ... pn  1
Evaluatorul local afişează o singură linie care conţine valoarea returnată de find_best şi
numărul de apeluri a procedurii ask.
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 319

Timp maxim de executare/test: 1.0 secunde


Memorie: total 1024 MB

3.4.1 Indicaţii de rezolvare

Subtask 1
In this subtask, We can find the diamond with a simple binary search.
Subtask 2
First we query the first 474 prize in the line. Its sufficient to find at least a lollipops prize
(cheapest). When we have found a lollipop, we can use binary search to find all prize except Ó
lollipops. This approach needs less than 9000 queries. The number of queries required is O n 
log n but there are exists another way which leads to less queries:
Find a lollipop. Then use devide and conquer: ask the middle of segment until finding new
lollipop. then devide the segment into two segment and keep the number of prizes which aren’t
lollipop. choose another random frog. If they are both young we know with the current information
if there are any older frogs between them. If there exists we can recursively solve the interval
between them. This approach needs at most 3000 queries.

3.4.2 Coduri sursă

Listing 3.4.1: prize.cpp


// http://ioi2017.org/tasks/materials/prize.zip ---> sandbox/prize.cpp
// http://ioi2017.org/tasks/materials/prize.zip ---> solution/***.cpp

#include <vector>
#include <cmath>
#include <cstring>
#include "prize.h"
#define X first
#define Y second

#include <iostream> // grader ...


#include <vector> // grader ...
#include <algorithm> // grader ...

#include <fstream> // ***

using namespace std;

ifstream cinf("01.in"); // ***

typedef pair<int,int> pii;

int numb;
pii P[210000];
bool mark[210000];
vector<int>vtmp;

pii query(int x)
{
if(mark[x]) return P[x];
mark[x]=true;
vtmp=ask(x);
pii tmp=pii(vtmp[0],vtmp[1]);
if(tmp.X+tmp.Y==0) throw x;
return P[x]=tmp;
}
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 320

void bs(int l,int r,int nl,int nr)


{
if(l>r) return;
for(int i=0;i<=r-l;i++)
{
int mid,midl=(l+r)/2-i/2,midr=(l+r)/2+(i+1)/2;
if(i%2==0) mid=midl;
else mid=midr;
pii tmp=query(mid);
if(tmp.X+tmp.Y==numb)
{
int tmpl=(i%2==0?0:midr-midl);
int tmpr=(i%2==1?0:midr-midl);
if(tmp.X-tmpl>nl) bs(l,midl-1,nl,tmp.Y+tmpl);
if(tmp.Y-tmpr>nr) bs(midr+1,r,tmp.X+tmpr,nr);
break;
}
}
}

int find_best(int n)
{
if(n==1) return 0;
try{
numb=0;
memset(mark,false,sizeof mark);
int p=0;
for(int i=0;i<sqrt(n)+30 && i<n && numb<=26;i++)
{
pii tmp=query(i);
if(tmp.X+tmp.Y>numb) p=i;
numb=max(numb,tmp.X+tmp.Y);
}
bs(p,n-1,p,0);
}
catch(int ans){
return ans;
}
return -1;
}

// -------------------------------------------------
static const int max_q = 10000;
static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

vector<int> ask(int i) {
query_count++;
if(query_count > max_q) {
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n) {


cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
cinf >> n; // ***

g.resize(n);
for(int i = 0; i < n; i++)
{
cinf >> g[i]; // ***
if(g[i] < 1) {
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 321

cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++) {
for(int i = 1; i <= n; i++) {
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);


cout << res << endl << "Query count: " << query_count << endl;

return 0;
}

Listing 3.4.2: prize+graderpublic.cpp


#include "prize.h"

#include <iostream>
#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------

#include <cmath>
#include <cstring>

#define X first
#define Y second

typedef pair<int,int> pii;

int numb;
pii P[210000];
bool mark[210000];
vector<int>vtmp;

pii query(int x)
{
if(mark[x]) return P[x];
mark[x]=true;
vtmp=ask(x);
pii tmp=pii(vtmp[0],vtmp[1]);
if(tmp.X+tmp.Y==0) throw x;
return P[x]=tmp;
}

void bs(int l,int r,int nl,int nr)


{
if(l>r) return;
for(int i=0;i<=r-l;i++)
{
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 322

int mid,midl=(l+r)/2-i/2,midr=(l+r)/2+(i+1)/2;
if(i%2==0) mid=midl;
else mid=midr;
pii tmp=query(mid);
if(tmp.X+tmp.Y==numb)
{
int tmpl=(i%2==0?0:midr-midl);
int tmpr=(i%2==1?0:midr-midl);
if(tmp.X-tmpl>nl) bs(l,midl-1,nl,tmp.Y+tmpl);
if(tmp.Y-tmpr>nr) bs(midr+1,r,tmp.X+tmpr,nr);
break;
}
}
}

int find_best(int n)
{
if(n==1) return 0;
try
{
numb=0;
memset(mark,false,sizeof mark);
int p=0;
for(int i=0;i<sqrt(n)+30 && i<n && numb<=26;i++)
{
pii tmp=query(i);
if(tmp.X+tmp.Y>numb) p=i;
numb=max(numb,tmp.X+tmp.Y);
}
bs(p,n-1,p,0);
}
catch(int ans)
{
return ans;
}
return -1;
}

// ---------- graderpublic -------------------

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


std::freopen("2-64.out.txt", "w", stdout) ;

string s;
getline (cin,s);
cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 323

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 111111 Query count = 555

t2-t1 = 0.244
t3-t2 = 0.082
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.4.3: prize-169927.cpp


// https://oj.uz/submission/169927 ... 53 ms 2204 KB
#include "prize.h"

#include <iostream>
#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 324

static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------


//#include "prize.h"
#include <bits/stdc++.h>

#define fi first
#define se second
#define ryan bear

//using namespace std;

typedef long long ll;


typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef long double ld;
typedef vector<int> vim;

int N;
set<pair<int, pii> > S[10];
int val[10], tp;

int ans, fl, chk[200010], ar[200010];

int f(int v)
{
for (int i=0; i<tp; i++) if (val[i]==v) return i;
val[tp]=v; return tp++;
}

void myfind(int s, int e)


{
if (s>e) return ;

int md=(s+e)/2;
if (chk[md]) return ;

chk[md]=1;
vim res=ask(md);
ar[md]=res[0]+res[1];
if (res[0]==0&&res[1]==0)
{
ans=md;
fl=1;
return ;
}

int ind=f(ar[md]);
S[ind].insert({md, make_pair(res[0], res[1])});

auto it=S[ind].find({md, make_pair(res[0], res[1])});


pair<int, pii> p=( *it), pr, ne;

if (it==S[ind].begin()) pr={-1, make_pair(0, ar[md])};


else pr=*prev(it);

if (next(it)==S[ind].end()) ne={N, make_pair(ar[md], 0)};


else ne=*next(it);

if (pr.se.fi!=p.se.fi) myfind(max(s, pr.fi+1), p.fi-1);


if (fl) return ;
if (p.se.fi!=ne.se.fi) myfind(p.fi+1, min(e, ne.fi-1));
}

int find_best(int n)
{
N=n;
myfind(0, n-1);
return ans;
}

// ---------- graderpublic -------------------


CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 325

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


std::freopen("2-64.out.txt", "w", stdout) ;

string s;
getline (cin,s);
cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 326

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 111111 Query count = 85

t2-t1 = 0.258
t3-t2 = 0.071
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.4.4: prize-187329.cpp


// https://oj.uz/submission/187329 71 ms 5548 KB
#include "prize.h"

#include <iostream>
#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------


//#include "prize.h"
#include <bits/stdc++.h>

//using namespace std;

static const int MAXN = (int) 2e5;

static bool mark[MAXN];


static vector <int> qry[MAXN];

static inline vector <int> myask(int pos)


{
if(mark[pos] == 0)
{
qry[pos] = ask(pos);
}
mark[pos] = 1;
return qry[pos];
}

static int solve(int l, int r, int numl, int numr, int mx)
{
if(l > r) return -1;

int mid = (l + r) / 2;

int ll = mid, rr = mid;


while(ll >= l && myask(ll)[0] + myask(ll)[1] < mx)
{
auto cur = myask(ll);
if(cur[0] + cur[1] == 0) return ll;
ll--;
}
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 327

while(rr <= r && myask(rr)[0] + myask(rr)[1] < mx)


{
auto cur = myask(rr);
if(cur[0] + cur[1] == 0) return rr;
rr++;
}

auto x = myask(max(ll, l)), y = myask(min(rr, r));


int ans = -1;

if(x[0] - numl > 0)


{
ans = max(ans, solve(l, ll - 1, numl, x[1], mx));
}

if(y[1] - numr > 0)


{
ans = max(ans, solve(rr + 1, r, y[0], numr, mx));
}

return ans;
}

int find_best(int n)
{
srand(time(NULL));

auto myrand = [&]()


{
return (1LL * (1LL << 15) * rand() + rand()) % n;
};

vector <bool> vis(n);


int mx = 0;

for(int i = 0; i < min(n, 200); i++)


{
int p = myrand();
while(vis[p]) { p = myrand(); }
vis[p] = 1;
auto cur = myask(p);
mx = max(mx, cur[0] + cur[1]);
}

return solve(0, n - 1, 0, 0, mx);


}

// ---------- graderpublic -------------------

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


std::freopen("2-64.out.txt", "w", stdout) ;
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 328

string s;
getline (cin,s);
cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 111111 Query count = 324

t2-t1 = 0.253
t3-t2 = 0.074
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.4.5: prize-206577.cpp


// https://oj.uz/submission/206577 60 ms 9980 KB
#include "prize.h"

#include <iostream>
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 329

#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------


//#include "prize.h"
//#include<vector>
#include<map>
#include<utility>
#include<cassert>

#define MAXN 200005

//using namespace std;

map<int, int> Q[MAXN];

int slv(int l, int r)


{
if (l > r) {return(-1);}
int mid = (l + r) >> 1;
vector<int> res = ask(mid);
int tot = res[0] + res[1], resL=-1, resR=-1;

if (tot == 0) {return(mid);}
else if (l == r) {return(-1);}

auto ptr1 = Q[tot].insert({mid, res[0]}).first, ptr2=ptr1;

if ((ptr1 == Q[tot].begin() || ( *(--ptr1)).second != res[0]) && res[0])


{
resL = slv(l, mid-1);
if (resL >= 0) {return(resL);}
}

if (((++ptr2) == Q[tot].end() || ( *ptr2).second != res[0]) && res[1])


{
resR = slv(mid+1, r);
if (resR >= 0) {return(resR);}
}
return(-1);
}

int find_best(int n)
{
return(slv(0, n-1));
}

// ---------- graderpublic -------------------

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 330

res[0] = rank_count[g[i] - 1][i + 1];


res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


std::freopen("2-64.out.txt", "w", stdout) ;

string s;
getline (cin,s);
cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 111111 Query count = 85

t2-t1 = 0.321
t3-t2 = 0.083
t4-t3 = 0
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 331

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


Press any key to continue.
*/

Listing 3.4.6: prize-208873.cpp


// https://oj.uz/submission/208873 58 ms 836 KB
#include "prize.h"

#include <iostream>
#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------


//#include "prize.h"

#include <bits/stdc++.h>

//using namespace std;

map<int, pair<int, int>> memo;

vector<int> query(int i)
{
vector<int> res;
if (memo.count(i))
{
res.emplace_back(memo[i].first);
res.emplace_back(memo[i].second);
}
else
{
res = ask(i);
memo[i] = {res[0], res[1]};
}
return res;
}

int numValuable;
vector<int> candidates; // non-lollipop boxes

void binarySearch(int leftValuable, int rightValuable, int left, int right)


{
if (left > right || leftValuable + rightValuable == numValuable)
{
return;
}

int mid = (left + right) / 2;


vector<int> res = query(mid);

if (res[0] + res[1] == numValuable)


{ // mid is a lollipop box
binarySearch(leftValuable, res[1], left, mid - 1);
binarySearch(res[0], rightValuable, mid + 1, right);
}
else
{ // mid is a non-lollipop box
candidates.emplace_back(mid);
int nearestLeft, nearestRight;

for (int i = mid + 1; i <= right; i++)


{
res = query(i);
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 332

if (res[0] + res[1] == numValuable)


{
nearestRight = i;
binarySearch(query(nearestRight)[0],
rightValuable,
nearestRight + 1,
right);
break;
}
else
{ // i is a non-lollipop box
candidates.emplace_back(i);
}
}

for (int i = mid - 1; i >= left; i--)


{
res = query(i);
if (res[0] + res[1] == numValuable)
{
nearestLeft = i;
binarySearch(leftValuable, query(nearestLeft)[1],
left, nearestLeft - 1);
break;
}
else
{ // i is a non-lollipop box
candidates.emplace_back(i);
}
}
}
}

int find_best(int n)
{
numValuable = 0;
for (int i = 0; i < min(n, 500); i++)
{ // least valuable box must satisfy sumˆ2 <= n,
// and since n <= 200000 then sum <= 500 will suffice
vector<int> res = query(i);
if (res[0] + res[1] > numValuable)
{
numValuable = res[0] + res[1];
}

if (numValuable > 30)


{ // second least valuable box must satisfy (sumˆ2)ˆ2 <= n,
// and since n <= 200000 then sum <= 30 will suffice
break; // we will only check at most one least valuable box,
// the rest will be valuable and checked later anyways
}
}

binarySearch(0, 0, 0, n - 1);

int ans = -1;


for (auto &c : candidates)
{
vector<int> res = query(c);
if (res[0] == 0 && res[1] == 0)
{
ans = c;
}
}

return ans;
}

// ---------- graderpublic -------------------

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 333

exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


std::freopen("2-64.out.txt", "w", stdout) ;

string s;
getline (cin,s);
cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 334

return 0;
}
/*
res = 111111 Query count = 624

t2-t1 = 0.247
t3-t2 = 0.071
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.4.7: prize-221802.cpp


// https://oj.uz/submission/221802 57 ms 2136 KB
#include "prize.h"

#include <iostream>
#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------


/**
* user: ppavic
* fname: Patrik
* lname: P a v i
* task: prize
* score: 100.0
* date: 2019-06-16 09:02:45.353752
*/
//#include "prize.h"
//#include <vector>
//#include <algorithm>
#include <cstring> // memset

#define PB push_back

//using namespace std;

typedef vector < int > vi;

const int N = 2e5 + 500;

int L[N], R[N];

int get_L(int i)
{
if(L[i] != -1) return L[i];
vi res = ask(i);
L[i] = res[0], R[i] = res[1];
return L[i];
}

int get_R(int i)
{
if(L[i] != -1) return R[i];
vi res = ask(i);
L[i] = res[0], R[i] = res[1];
return R[i];
}

int loli;
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 335

vi svi;

void solve(int l,int r)


{
if(l > r) return;
if(get_L(l) + get_R(l) < loli)
{
svi.PB(l); solve(l + 1, r);
return;
}

if(l == r) return;
if(get_L(r) + get_R(r) < loli)
{
svi.PB(r);
solve(l, r - 1);
return;
}

if(get_L(l) == get_L(r) || l + 1 == r) return;


solve(l, (l + r) / 2);
solve((l + r) / 2, r);
}

int find_best(int n)
{
memset(L, -1, sizeof(L));
svi.clear();
loli = 0;
for(int i = 0;i < 100;i++)
{
int x = (rand() + rand()) % n;
x = (x % n + n) % n;
loli = max(loli, get_L(x) + get_R(x));
}

solve(0, n - 1);

for(int x : svi)
{
if(get_L(x) + get_R(x) == 0)
return x;
}

return 0;
}

// ---------- graderpublic -------------------

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 336

std::freopen("2-64.out.txt", "w", stdout) ;

string s;
getline (cin,s);
cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 111111 Query count = 223

t2-t1 = 0.246
t3-t2 = 0.075
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.4.8: prize-229052.cpp


// https://oj.uz/submission/229052 63 ms 10104 KB
#include "prize.h"
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 337

#include <iostream>
#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------

#include<bits/stdc++.h>

//#include "prize.h"
#define god dimasi5eks
#pragma GCC optimize("O3")
#define fi first
#define se second
#define pb push_back
#define pf push_front

// #define fisier 1

using namespace std;

typedef long long ll;

const int mod = 1000000007;


const double dancila = 3.14159265359; // PI
const double eps = 1e-9;

int ans;
map<int, pair<int, int> > m[200002];

pair<int, int> qu(int x)


{
vector<int> v = ask(x);
return {v[0], v[1]};
}

void solve(int st, int dr)


{
if(ans != -1 || st > dr)
return;

int mid = (st + dr) / 2;

pair<int, int> cur = qu(mid);


int sum = cur.fi + cur.se;

if(sum == 0)
{
ans = mid;
return;
}

auto it = m[sum].upper_bound(mid);
bool L = 1, R = 1;
if(it != m[sum].begin())
{
it--;
if(cur.se == it -> se.se)
L = 0;
it++;
}

if(it != m[sum].end())
{
if(cur.se == it -> se.se)
R = 0;
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 338

m[sum][mid] = cur;
if(L)
solve(st, mid - 1);
if(R)
solve(mid + 1, dr);
}

int find_best(int n)
{
ans = -1;
solve(0, n - 1);
return ans;
}

// ---------- graderpublic -------------------

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


std::freopen("2-64.out.txt", "w", stdout) ;

string s;
getline (cin,s);
cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 339

rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 111111 Query count = 98

t2-t1 = 0.241
t3-t2 = 0.051
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.4.9: prize-232826.cpp


// https://oj.uz/submission/232826 343 ms 472108 KB
#include "prize.h"

#include <iostream>
#include <vector>
#include <algorithm>

#include<ctime>
#include <string>

using namespace std;

static const int max_q = 10000;


static int n;
static int query_count = 0;
static vector<int> g;
static vector<vector<int> > rank_count;

// ----------- prize -------------------------

#include<bits/stdc++.h>

//using namespace std;

//#include "prize.h"
#define MID ((l+r)/2)

typedef vector<int> vi;


typedef pair<int, int> ii;

#define F first
#define S second

ii mem[1000000];
bool used[1000000];
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 340

set<int> s[10000000];

ii q(int x)
{
if(used[x]) return mem[x];
used[x] = true;
vi a = ask(x);
mem[x] = ii(a[0],a[1]);
return mem[x];
}

int N;
int mina=0;
int rec(int l=0, int r=N-1)
{
if(l>r) return -1;
ii a = q(MID);
if(a.F+a.S==0) return MID;

int tot = a.F+a.S;


auto it = s[a.F+a.S].insert(MID).F;
if(it==s[tot].begin() || mem[ *prev(it) ] != mem[MID])
{
int b = rec(l,MID-1);
if(b!=-1) return b;
}

if(it==--s[tot].end() || mem[ *next(it) ] != mem[MID])


{
int b = rec(MID+1,r);
if(b!=-1) return b;
}
return -1;
}

int find_best(int n)
{
N = n;
int l=0, r=n-1;
int m;
int pos=0;
return rec();
}

// ---------- graderpublic -------------------

vector<int> ask(int i)
{
query_count++;
if(query_count > max_q)
{
cerr << "Query limit exceeded" << endl;
exit(0);
}

if(i < 0 || i >= n)


{
cerr << "Bad index: " << i << endl;
exit(0);
}

vector<int> res(2);
res[0] = rank_count[g[i] - 1][i + 1];
res[1] = rank_count[g[i] - 1][n] - res[0];
return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/2-64.in", "r", stdin) ;


std::freopen("2-64.out.txt", "w", stdout) ;

string s;
getline (cin,s);
CAPITOLUL 3. IOI 2017 3.4. BIG PRIZE 341

cout<<s<<"\n";

//cin >> n;
scanf("%d",&n);
//cout<<"n = "<<n<<"\n";

g.resize(n);
for(int i = 0; i < n; i++)
{
cin >> g[i];
if(g[i] < 1)
{
cerr << "Invalid rank " << g[i] << " at index " << i << endl;
exit(0);
}
}

auto t2 = clock();

int max_rank = *max_element(g.begin(), g.end());

rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));


for(int r = 0; r <= max_rank; r++)
{
for(int i = 1; i <= n; i++)
{
rank_count[r][i] = rank_count[r][i - 1];
if(g[i - 1] == r)
rank_count[r][i]++;
}
}

for(int i = 0; i <= n; i++)


for(int r = 1; r <= max_rank; r++)
rank_count[r][i] += rank_count[r - 1][i];

int res = find_best(n);

auto t3 = clock();

cout << res << endl << "Query count: " << query_count << endl;

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 111111 Query count = 98

t2-t1 = 0.239
t3-t2 = 0.073
t4-t3 = 0

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


Press any key to continue.
*/
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 342

3.4.3 *Rezolvare detaliată

3.5 Simurgh
Problema 5 - Simurgh 100 de puncte

Author: Saeed Seddighin (Iran)

O legendă veche din Shahnameh spune că Zal, legendarul erou persan s-a ı̂ndrăgostit de
Rubada, prinţesa de Kabul. Când Zal a cerut-o pe Rubada ı̂n căsătorie, tatăl ei i-a cerut să
ı̂ndeplinească mai ı̂ntâi o misiune.
În Persia sunt n oraşe marcate de la 0 la n  1, şi m drumuri bidirecţionale marcate de la 0
la m  1. Fiecare drum conectează o pereche de oraşe distincte. Fiecare pereche de oraşe este
conectată cu cel mult un drum. Unele drumuri sunt drumuri regale folosite ı̂n călătorii de către
familia regală. Zal trebuie să determine drumurile care sunt drumuri regale.
Zal are o hartă cu toate oraşele şi drumurile din Persia. El nu ştie care sunt drumurile regale,
dar poate apela la ajutorul lui Simurgh, o pasăre binevoitoare mitică - protectoarea lui Zal. Totuşi,
Simurgh nu vrea să ı̂i dezvăluie lui Zal setul de drumuri regale ı̂n mod direct. ı̂n schimb, pasărea
i-a şoptit lui Zal că setul de drumuri regale formează un set de aur. Un set de drumuri este un
set de aur, dacă şi numai dacă:

a conţine exact drumuri şi


a pentru oricare pereche de oraşe, este posibilă deplasarea din unul ı̂n celălalt folosind doar
drumurile din acest set.

Mai mult, Zal poate adresa lui Simurgh unele ı̂ntrebări. Pentru fiecare ı̂ntrebare:

1. Zal selectează un set de aur, iar mai apoi


2. Simurgh ı̂i spune lui Zal câte din drumurile setului selectat sunt drumuri regale.

Programul vostru trebuie să-l ajute pe Zal să găsească setul de drumuri regale, adresându-i lui
Simurgh cel mult q ı̂ntrebări. Evaluatorul va juca rolul lui Simurgh.
Detalii de implementare
Trebuie să implementaţi următoarea procedură:

int[] find_roads(int n, int[] u, int[] v)

a n: numărul de oraşe,
a u şi v: şiruri de lungime m. Pentru toate 0 & i & m  1, ui şi v i sunt oraşele conectate
de drumul i.
a Procedura trebuie să returneze un şir de lungime n  1 conţinând indicii drumurilor regale
(ı̂n ordine arbitrară).
Soluţia voastră poate efectua cel mult q apeluri către următoarea procedură a evaluatorului:

int count_common_roads(int[] r)

a r: un şir de lungime n  1 conţinând indicii drumurilor din setul de aur (ı̂n ordine arbitrară).
a Această procedură returnează numărul de drumuri regale ı̂n r.
Exemplu

find_roads(4, [0, 0, 0, 1, 1, 2], [1, 2, 3, 2, 3, 3])


CAPITOLUL 3. IOI 2017 3.5. SIMURGH 343

În acest exemplu sunt 4 oraşe şi 6 drumuri. Notăm prin a, b drumul care conectează oraşele
a şi b. Drumurile sunt numerotate de la 0 la 5 ı̂n ordinea următoare: (0,1), (0,2), (0,3), (1,2),
(1,3) şi (2,3). Fiecare set de aur conţine n  1 3 drumuri.
Fie setul regal format din drumurile cu indicii 0, 1, şi 5, adică drumurile care conectează oraşele
(0,1), (0,2), şi (2,3). Atunci:

a count_common_roads([0, 1, 2]) returnează 2. Această interogare se referă la dru-


murile cu indicii 0, 1, şi 2, adică, la drumurile care conectează oraşele (0,1), (0,2) şi (0,3).
Două dintre acestea sunt drumuri regale.
a count_common_roads([5, 1, 0]) returnează 3. Această interogare se referă la setul
integral de drumuri regale.

Procedura find_roads ar trebui să returneze [5, 1, 0] sau oricare alt şir de lungime 3 care
va conţine aceste trei elemente.
De remarcat, că următoarele apeluri nu sunt permise:

a count_common_roads([0, 1]): aici lungimea lui r nu este 3.


a count_common_roads([0, 1, 3]): aici r nu descrie un set de aur, deoarece este
imposibil de a călători din oraşul 0 ı̂n 3 doar folosind drumurile (0,1), (0,2), (1,2).

Restricţii şi precizări


a 2 & n & 500
a n  1 & m & n n  1©2
a 0 & ui, v i & n  1 (pentru toate 0 & i & m  1)
a Pentru toate 0 & i & m  1, drumul i conectează două oraşe distincte (ui j v i).
a Fiecare pereche de oraşe este conectată cu cel mult un drum.
a Cu ajutorul drumurilor date este posibilă deplasarea ı̂ntre oricare două oraşe.
a Setul compus din toate drumurile regale este un set de aur.
a find_roads trebuie să apeleze count_common_roads de cel mult q ori. ı̂n fiecare apel,
setul de drumuri specificat de r trebuie să fie un set de aur.
Subtask-uri
1. (13 puncte) n & 7, q = 30 000
2. (17 puncte) n & 50, q = 30 000
3. (21 puncte) n & 240, q = 30 000
4. (19 puncte) q = 12 000 şi există un drum ı̂ntre fiecare pereche de oraşe
5. (30 puncte) q = 8 000
Evaluator local
Evaluatorul local citeşte datele din input ı̂n următorul format:
a linia 1: n m
a linia 2  i (pentru toate 0 & i & m  1): ui v i
a linia 2  m: s0 s1 ... sn  2
Aici, 2  m: s0 s1 ... sn  2 sunt indicii drumurilor regale.
Evaluatorul local returnează YES, dacă find_roads apelează count_common_roads cel
mult 30 000 de ori, şi returnează setul corect de drumuri regale. ı̂n caz contrar, este returnat NO.
Atenţie! - procedura count_common_roads ı̂n evaluatorul local nu verifică dacă r posedă
toate proprietăţile unui set de aur. ı̂n schimb, ea numără şi returnează numărul de indici a
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 344

drumurilor regale ı̂n şirul r. Cu toate acestea, ı̂n cazul ı̂n care programul submitat apelează
count_common_roads cu un set de indici care nu descriu un set de aur, verdictul graderului va
fi ’Wrong Answer’.
Observaţie tehnică
Procedura count_common_roads ı̂n C++ şi Pascal foloseşte metoda pass by reference din
considerente de eficienţă. Puteţi apela procedura ı̂n modul obişnuit. Se garantează că evaluatorul
nu va modifica valoarea r.
Timp maxim de executare/test: 3.0 secunde
Memorie: total 1024 MB

3.5.1 Indicaţii de rezolvare

Given a graph G with n vertices and m edges. Zal has selected a spanning tree of the graph
but you don’t know which edges appear in his spanning tree. In every query, you can give him a
spanning tree of the graph and he’ll tell you how many edges your spanning tree has in common
with his. Your wish to find his spanning tree with a small number of queries.
Subtask 1
Iterate over all spanning trees and try all of them.
Subtask 2
start with an arbitrary spanning tree t and keep improving your solution as follows:
- randomly choose an edge e
- add the edge to your solution
- remove a random edge from the cycle of t < e to make it a tree t
- if t has more edges in common with Zal’s tree then set t t 
- stop if t is Zal’s tree

Subtask 3
Exactly one query per edge. Decompose your graph into a number of disjoint (or almost
disjoint) cycles. For each cycle C, find a tree t that connects C to all vertices of the graph (C < t
is a spanning tree with an extra edge). For ech e " C, determine the number of edges that C < te
has in common with Zal’s tree. If all these numbers are equal, then none of the edges of C appear
in Zal’s tree. Otherwise, the edges whose removal decrease the number of common edges are in
Zal’s tree.
Subtask 4
One can determine with 3 queries whether an edge e appears in Zal’s tree; It only suffices
to find 2 other edges that make a triangle together with e and do as mentioned earlier. Fix an
arbitrary tree t and find out which of its edges appear in Zal’s tree. Once we find that, for every
forest F of G we can determine how many edge F shares with Zal’s tree with a single query; add
some of the edges of t to F to make it a spanning tree, query that tree, and determine how many
edges of F are in common with Zal’s tree. Determine the degree of each vertex in Zal’s tree with
n queries. Then every time we find the incident edge of a leaf with log n queries and remove
that edge from the solution. We continue on with the new edges.
Subtask 5
The same as previous subtask. The only difference is that finding a tree and determining which
of its edges appear in Zal’s tree is a bit harder. Roughly speaking, we need to remove the cut
edges (which we know are included in Zal’s tree). Then every component is a 2-edge-connected
graph and we can find an ear-decomposition for them. Note that for every cycle C we can figure
out with ¶C ¶ querier which edges of C are in Zal’s tree. The only extension that we need to that
is that if we already know the status of k edges of C, we can do this with ¶C ¶  k  1 queris.
Therefore, we can solve the problem for each component separately with at most 2n quires.
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 345

3.5.2 Coduri sursă

Listing 3.5.1: simurgh.cpp


// http://ioi2017.org/tasks/materials/simurgh.zip ---> sandbox/simurgh.cpp
// http://ioi2017.org/tasks/materials/simurgh.zip ---> solution/***.cpp

#include "simurgh.h"

#include <cstdio>
#include <cassert>
#include <vector>
#include <cstdlib>
#include <string>

#include<ctime>

using namespace std;

// ------------- simurgh ----------------

#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <cmath>
#include <cstring>

using namespace std;

#define Foreach(i, c) \
for(__typeof((c).begin()) i = (c).begin(); i != (c).end(); ++i)
#define For(i,a,b) for(int (i)=(a);(i) < (b); ++(i))
#define rof(i,a,b) for(int (i)=(a);(i) > (b); --(i))
#define rep(i, c) for(auto &(i) : (c))
#define x first
#define y second
#define pb push_back
#define PB pop_back()
#define iOS ios_base::sync_with_stdio(false)
#define sqr(a) (((a) * (a)))
#define all(a) a.begin() , a.end()
#define error(x) cerr << #x << " = " << (x) <<endl
#define Error(a,b) \
cerr<<"( "<<#a<<" , "<<#b<<" ) = ( "<<(a)<<" , "<<(b)<<" )\n";
#define errop(a) cerr<<#a<<" = ( "<<((a).x)<<" , "<<((a).y)<<" )\n";
#define coud(a,b) cout<<fixed << setprecision((b)) << (a)
#define L(x) ((x)<<1)
#define R(x) (((x)<<1)+1)
#define umap unordered_map
#define double long double

typedef long long ll;


typedef pair<int,int>pii;
typedef vector<int> vi;
template <class T> inline void smax(T &x,T y){ x = max((x), (y));}
template <class T> inline void smin(T &x,T y){ x = min((x), (y));}
template <class T> inline void sminmax(T &mn, T &mx, T x)
{smin(mn, x), smax(mx, x);}

const int maxn = 512, maxm = maxn * maxn / 2;


pii highest[maxn];
bool mark[maxn];

int h[maxn], ind[maxn][maxn], state[maxn],


par[maxn], last_num[maxm], n, m, deg[maxn];

vi __edges_vec;
vi adj[maxn];
pii edges[maxm];
bool bit[maxm];
int _next_ = 1;
int _last_id[maxm];
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 346

vector<int> ANS;

inline void _renew()


{
vi __edges_new;
rep(i, __edges_vec) if(bit[i] && _last_id[i] != _next_)
__edges_new.pb(i), _last_id[i] = _next_;
++ _next_;
__edges_vec = __edges_new;
}

inline int query()


{
_renew();
int res = count_common_roads(__edges_vec);
if(res == n-1)
ANS = __edges_vec;
return res;
}

inline void toggle(int i)


{
if(!bit[i])
{
bit[i] = true;
__edges_vec.pb(i);
}
else
bit[i] = false;
}

inline void reset()


{
while(!__edges_vec.empty())
{
int e = __edges_vec.back();
__edges_vec.PB;
bit[e] = false;
}
}

inline void dfs(int v = 0, int p = -1)


{
par[v] = p;
mark[v] = true;
highest[v] = {h[v], -1};
rep(u, adj[v])
{
int e = ind[v][u];
if(!mark[u])
{
h[u] = h[v] + 1;
dfs(u, v);
if(highest[v].x > highest[u].x)
highest[v] = highest[u];
}
else
if(highest[v].x > h[u] && u != p)
highest[v] = {h[u], e};
}

if(˜p)
toggle(ind[v][p]);
}

inline void DFS(int v = 0)


{
int p = par[v];
rep(u, adj[v]) if(par[u] == v) DFS(u);
if(˜p && state[v] == -1)
{
if(highest[v].x > h[p])
{
state[v] = 1;
return ;
}
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 347

int back_edge = highest[v].y;


int x = edges[back_edge].x, y = edges[back_edge].y;
if(h[x] > h[y]) swap(x, y);
int back_edge_num = query();
int mn = back_edge_num, mx = mn;
int cur = y;
int for_a_one = -1;
toggle(back_edge);

while(cur != x)
{
if(state[cur] == -1 or for_a_one == -1)
{
int cur_edge = ind[cur][par[cur]];
toggle(cur_edge);
last_num[cur_edge] = query();
sminmax(mn, mx, last_num[cur_edge]);
if(˜state[cur])
for_a_one = last_num[cur_edge] - (!state[cur]);
toggle(cur_edge);
}
cur = par[cur];
}

toggle(back_edge);
cur = y;
while(cur != x)
{
if(state[cur] == -1)
{
int cur_edge = ind[cur][par[cur]];
if(˜for_a_one)
state[cur] = last_num[cur_edge] == for_a_one;
else if(mn == mx)
state[cur] = 0;
else
state[cur] = last_num[cur_edge] == mn;
}

cur = par[cur];
}
}
}

vi tree, ans;

inline int root(int v)


{
return par[v] < 0? v: par[v] = root(par[v]);
}

inline bool merge(int ind)


{
int x = edges[ind].x, y = edges[ind].y;
x = root(x), y = root(y);
if(x == y) return false;
toggle(ind);
if(par[y] < par[x]) swap(x, y);
par[x] += par[y];
par[y] = x;
return true;
}

inline int edge_state(int i)


{
int x = edges[i].x, y = edges[i].y;
if(h[x] > h[y]) swap(x, y);
return state[y];
}

inline int query_for_forest(vi subset)


{
reset();
int sum = 0;
memset(par, -1, sizeof par);
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 348

rep(e, subset)
merge(e);
rep(e, tree)
if(merge(e))
sum += edge_state(e);
return query() - sum;
}

inline void calc_deg(int v)


{
vi subset;
rep(u, adj[v])
subset.pb(ind[v][u]);
deg[v] = query_for_forest(subset);
}

inline void remove(int v)


{
if(!deg[v] or mark[v]) return ;
assert(deg[v] == 1);
vi ed;
rep(u, adj[v]) if(!mark[u]) ed.pb(ind[v][u]);
int l = 0, r = ed.size() - 1;
while(r > l)
{
int mid = (l + r)/2;
vi subset(ed.begin()+l, ed.begin()+mid+1);
if(query_for_forest(subset))
r = mid;
else
l = mid + 1;
}

int e = ed[l];
int u = edges[e].x + edges[e].y - v;
ans.pb(e);
-- deg[u];
mark[v] = true;
if(deg[u] == 1)
remove(u);
}

vi find_roads(int n, vi v, vi u)
{
::n = n;
::m = v.size();;
memset(state, -1, sizeof state);
memset(ind, -1, sizeof ind);
For(i,0,m)
{
edges[i] = {v[i], u[i]};
ind[v[i]][u[i]] = ind[u[i]][v[i]] = i;
adj[v[i]].pb(u[i]), adj[u[i]].pb(v[i]);
}

dfs();
DFS();

memset(mark, 0, sizeof mark);


For(i,0,m) if(bit[i])
tree.pb(i);
For(i,0,n) calc_deg(i);
For(i,0,n) if(deg[i] == 1) remove(i);
query_for_forest(ans);
return ANS;
}

// --------- graderpublic -------------------------

static int MAXQ = 30000;


int MAX_Q;

static int q = 0;
//static int n, m, q = 0;

static vector<int> u, v;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 349

static vector<bool> goal;

static void wrong_answer()


{
printf("NO\n");
exit(0);
}

static bool is_valid(const vector<int>& r)


{
if(int(r.size()) != n - 1)
return false;

for(int i = 0; i < n - 1; i++)


if (r[i] < 0 || r[i] >= m)
return false;

return true;
}

static int _count_common_roads_internal(const vector<int>& r)


{
if(!is_valid(r))
wrong_answer();

int common = 0;
for(int i = 0; i < n - 1; i++)
{
bool is_common = goal[r[i]];
if (is_common)
common++;
}

return common;
}

int count_common_roads(const vector<int>& r)


{
q++;
if(q > MAX_Q)
// if(q > MAXQ)

wrong_answer();

return _count_common_roads_internal(r);
}

int main()
{
auto t1 = clock();

std::freopen("../tests/4-07.in", "r", stdin);


//std::freopen("4-07.out.txt", "w", stdout);

string s;
getline (cin,s);
cout<<s<<"\n";

assert(2 == scanf("%d %d", &n, &m));


cout<<"m="<<m<<" n="<<n<<"\n";

assert(1 == scanf("%d", &MAX_Q));


cout<<"MAX_Q = "<<MAX_Q<<"\n\n";

u.resize(m);
v.resize(m);

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

goal.resize(m, false);

for(int i = 0; i < n - 1; i++)


{
int id;
assert(1 == scanf("%d", &id));
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 350

goal[id] = true;
}

auto t2 = clock();

vector<int> res = find_roads(n, u, v);

auto t3 = clock();

if(_count_common_roads_internal(res) != n - 1) wrong_answer();

//printf("YES\n");
printf("YES q = %d\n\n",q);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
m=124750 n=500
MAX_Q = 12000

YES q = 4794

t2-t1 = 0.297
t3-t2 = 1.109
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.5.2: simurgh-33904.cpp


// https://oj.uz/submission/33904 166 ms 7864 KB
#include "simurgh.h"

#include <cstdio>
#include <cassert>
#include <vector>
#include <cstdlib>
#include <string>

#include<ctime>
#include<iostream>

// ------------- simurgh ----------------

#include <algorithm>

using namespace std;

typedef pair<int, int> pi;

struct road
{
int i, x;
};

int n, m;
const int MAXM = 500 * 499 / 2;
vector<road> edge[500];
pi es[MAXM];

bool dfsTree[MAXM];
bool backGraph[MAXM];
vector<int> dfsQ;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 351

int par[500];
int parEdge[500];
int dep[500];
int back[500];
int backEdge[500];
int ord = 0, base;

int royal[MAXM];
int sub[500];
int uf[500];

void dfs(int x, int p)


{
dep[x] = ++ord;
back[x] = dep[x];
backEdge[x] = -1;
for (road i : edge[x])
{
if (dep[i.x] != 0)
{
if (i.x != p && dep[i.x] < back[x])
{
back[x] = dep[i.x];
backEdge[x] = i.i;
}
continue;
}

dfsTree[i.i] = true;
dfsQ.push_back(i.i);
par[i.x] = x;
parEdge[i.x] = i.i;
dfs(i.x, x);

if (back[i.x] == dep[i.x])
royal[i.i] = 1;
else
if (back[i.x] < back[x])
{
back[x] = back[i.x];
backEdge[x] = backEdge[i.x];
}
}

if (back[x] < dep[x])


backGraph[backEdge[x]] = true;
}

int find(int x)
{
if (uf[x] != x) return uf[x] = find(uf[x]);
return x;
}

bool merge(int x, int y)


{
x = find(x);
y = find(y);
if (x == y) return false;
uf[x] = y;
return true;
}

int question(const vector<int> &vt)


{
vector<int> qs;
int other = 0;
for (int i = 0; i < n; ++i) uf[i] = i;
for (int i : vt)
{
qs.push_back(i);
merge(es[i].first, es[i].second);
}
for (int i : dfsQ)
{
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 352

if (merge(es[i].first, es[i].second))
{
qs.push_back(i);
if (royal[i] == 1) ++other;
}
}

return count_common_roads(qs) - other;


}

void bsearch(const vector<int> &vt, int s, int e, int c)


{
if (c == 0)
{
for (int i = s; i <= e; ++i) royal[vt[i]] = -1;
return;
}
if (c == e - s + 1)
{
for (int i = s; i <= e; ++i) royal[vt[i]] = 1;
return;
}

vector<int> qs;
int m = (s + e) / 2;
for (int i = s; i <= m; ++i) qs.push_back(vt[i]);
int d = question(qs);
bsearch(vt, s, m, d);
bsearch(vt, m + 1, e, c - d);
}

std::vector<int> find_roads(int N, std::vector<int> u, std::vector<int> v)


{
n = N;
m = u.size();
for (int i = 0; i < m; ++i)
{
es[i] = pi(u[i], v[i]);
edge[u[i]].push_back({ i, v[i] });
edge[v[i]].push_back({ i, u[i] });
}

dfs(0, -1);
base = count_common_roads(dfsQ);
vector<pi> es;
for (int i = 0; i < m; ++i)
{
if (backGraph[i]) es.push_back(pi(min(dep[u[i]], dep[v[i]]), i));
}

sort(es.begin(), es.end());

for (pi i : es)


{
int s = u[i.second], e = v[i.second];
if (dep[s] < dep[e]) swap(s, e);
int j = s;
bool allZero = true;
while (par[j] != e)
{
if (royal[parEdge[j]] == 0)
{
vector<int> qs;
for (int k : dfsQ)
{
if (k != parEdge[j]) qs.push_back(k);
}
qs.push_back(i.second);
sub[j] = count_common_roads(qs) - base;
if (sub[j] != 0)
{
allZero = false;
royal[parEdge[j]] = -sub[j];
royal[i.second] = sub[j];
}
}
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 353

else
allZero = false;
j = par[j];
}

vector<int> qs;
for (int k : dfsQ)
{
if (k != parEdge[j]) qs.push_back(k);
}

qs.push_back(i.second);
sub[j] = count_common_roads(qs) - base;
if (royal[parEdge[j]] != 0)
{
allZero = false;
royal[i.second] = royal[parEdge[j]] + 2 * sub[j];
}
else
if (sub[j] != 0)
{
allZero = false;
royal[parEdge[j]] = -sub[j];
royal[i.second] = sub[j];
}

if (allZero)
{
j = s;
while (j != e)
{
royal[parEdge[j]] = -1;
j = par[j];
}
royal[i.second] = -1;
continue;
}

j = s;
while (j != e)
{
if (royal[parEdge[j]] == 0)
royal[parEdge[j]] = royal[i.second] - 2 * sub[j];
j = par[j];
}
}

for (int i = 0; i < n; ++i)


{
vector<int> es;
for (road j : edge[i])
{
if (royal[j.i] == 0) es.push_back(j.i);
}
bsearch(es, 0, es.size() - 1, question(es));
}

vector<int> ret;
for (int i = 0; i < m; ++i)
{
if (royal[i] == 1) ret.push_back(i);
}

return ret;
}

// --------- graderpublic -------------------------

static int MAXQ = 30000;


int MAX_Q;

static int q = 0;
//static int n, m, q = 0;

static vector<int> u, v;
static vector<bool> goal;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 354

static void wrong_answer()


{
printf("NO\n");
exit(0);
}

static bool is_valid(const vector<int>& r)


{
if(int(r.size()) != n - 1)
return false;

for(int i = 0; i < n - 1; i++)


if (r[i] < 0 || r[i] >= m)
return false;

return true;
}

static int _count_common_roads_internal(const vector<int>& r)


{
if(!is_valid(r))
wrong_answer();

int common = 0;
for(int i = 0; i < n - 1; i++)
{
bool is_common = goal[r[i]];
if (is_common)
common++;
}

return common;
}

int count_common_roads(const vector<int>& r)


{
q++;
if(q > MAX_Q)
// if(q > MAXQ)

wrong_answer();

return _count_common_roads_internal(r);
}

int main()
{
auto t1 = clock();

std::freopen("../tests/4-07.in", "r", stdin);


//std::freopen("4-07.out.txt", "w", stdout);

string s;
getline (cin,s);
cout<<s<<"\n";

assert(2 == scanf("%d %d", &n, &m));


cout<<"m="<<m<<" n="<<n<<"\n";

assert(1 == scanf("%d", &MAX_Q));


cout<<"MAX_Q = "<<MAX_Q<<"\n\n";

u.resize(m);
v.resize(m);

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

goal.resize(m, false);

for(int i = 0; i < n - 1; i++)


{
int id;
assert(1 == scanf("%d", &id));
goal[id] = true;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 355

auto t2 = clock();

vector<int> res = find_roads(n, u, v);

auto t3 = clock();

if(_count_common_roads_internal(res) != n - 1) wrong_answer();

//printf("YES\n");
printf("YES q = %d\n\n",q);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
m=124750 n=500
MAX_Q = 12000

YES q = 2536

t2-t1 = 0.406
t3-t2 = 0.39
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.5.3: simurgh-70374.cpp


// https://oj.uz/submission/70374 303 ms 16344 KB
#include "simurgh.h"

#include<ctime>

// ------------- simurgh ----------------

#include<bits/stdc++.h>

using namespace std;

#define pii pair<int,int>


#define X first
#define Y second

const int maxn = 500 + 5;


const int maxm = 250000 + 5;

int n,m;
pii e[maxm];

int head[maxn];
vector<pii> way[maxn], edge[maxn];
int ban[maxm], res[maxm];

vector<int> tree;

int h[maxn], par[maxn], rec[maxn];

vector<int> p;
int cut[maxm];

int ask_count;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 356

int ask(int x, int y)


{
for(auto &id : tree) if(id==x) id = y;
ask_count++;

int ret = count_common_roads(tree);


for(auto &id : tree) if(id==y) id = x;
return ret;
}

void dfs(int u, int last, int lid)


{
h[u] = h[last] + 1;
par[u] = last;
rec[u] = lid;
for(auto t : way[u])
{
int v = t.X, id = t.Y;
if(v==last) continue;
dfs(v,u,id);
}
}

void lca(int u, int v)


{
if(u==v) return ;
if(h[u] >= h[v])
{
p.push_back(rec[u]);
lca(par[u],v);
}
else
lca(v,u);
}

int findhead(int x)
{
if(x==head[x]) return x;
return head[x] = findhead(head[x]);
}

int get(int x, int l, int r)


{
int val = 0;
vector<int> vec;
for(int i=0;i<n;i++) head[i] = i;
for(int i=l;i<=r;i++)
{
int v = edge[x][i].X, id = edge[x][i].Y;
head[findhead(x)] = findhead(v);
vec.push_back(id);
val -= res[id];
}

for(auto id : tree)
{
int u = e[id].X, v = e[id].Y;
if(findhead(u)!=findhead(v))
{
head[findhead(u)] = findhead(v);
vec.push_back(id);
val -= res[id];
}
}
ask_count++;
if(ask_count > 8000) assert(0);
val += count_common_roads(vec);
return val;
}

vector<int> find_roads(int N, vector<int> U, vector<int> V)


{
n = N; m = U.size();
for(int i=0;i<n;i++) head[i] = i;
for(int i=0;i<m;i++)
{
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 357

int u = U[i], v = V[i];


e[i] = {u,v};
if(findhead(u)!=findhead(v))
{
head[findhead(u)] = findhead(v);
way[u].push_back({v,i});
way[v].push_back({u,i});
tree.push_back(i);
ban[i] = 1;
res[i] = -1;
}
else
{
edge[u].push_back({v,i});
edge[v].push_back({u,i});
}
}

dfs(0,-1,-1);

int ori = ask(-1,-1);


for(int i=0;i<m;i++)
{
if(ban[i]) continue;
int u = e[i].X, v = e[i].Y, wow = i;

p.clear();
lca(u,v);

int good = 0;
for(auto id : p) if(res[id]==-1) good = 1;
if(!good) continue;

int x = -1;
for(auto id : p) if(res[id]!=-1) x = id;
if(x==-1)
{
for(auto id : p) cut[id] = ask(id,wow);
for(auto id : p) if(cut[id] - ori == 1) res[wow] = 1;
for(auto id : p) res[id] = ori - cut[id] + res[wow];
}
else
{
res[wow] = ask(x,wow) - ori + res[x];
for(auto id : p)
if(res[id]==-1)
res[id] = ori - ask(id,wow) + res[wow];
}
}

for(int i=0;i<m;i++)
{
if(res[i] == -1) res[i] = 1;
}

for(int x=0;x<n;x++)
{
for(int ext=get(x,0,edge[x].size()-1);ext>0;ext--)
{
int l = 0, r = edge[x].size()-1, mid, pos = -1;
while(l<=r)
{
mid = (l+r)/2;
if(get(x,0,mid) >= 1)
{
pos = edge[x][mid].Y;
r = mid-1;
}
else l = mid+1;
}
if(pos==-1) break;
res[pos] = 1;
}
}

vector<int> ans;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 358

for(int i=0;i<m;i++)
if(res[i])
ans.push_back(i);
return ans;
}

// --------- graderpublic -------------------------

static int MAXQ = 30000;


int MAX_Q;

static int q = 0;
//static int n, m, q = 0;

static vector<int> u, v;
static vector<bool> goal;

static void wrong_answer()


{
printf("NO\n");
exit(0);
}

static bool is_valid(const vector<int>& r)


{
if(int(r.size()) != n - 1)
return false;

for(int i = 0; i < n - 1; i++)


if (r[i] < 0 || r[i] >= m)
return false;

return true;
}

static int _count_common_roads_internal(const vector<int>& r)


{
if(!is_valid(r))
wrong_answer();

int common = 0;
for(int i = 0; i < n - 1; i++)
{
bool is_common = goal[r[i]];
if (is_common)
common++;
}

return common;
}

int count_common_roads(const vector<int>& r)


{
q++;
if(q > MAX_Q)
// if(q > MAXQ)

wrong_answer();

return _count_common_roads_internal(r);
}

int main()
{
auto t1 = clock();

std::freopen("../tests/4-07.in", "r", stdin);


//std::freopen("4-07.out.txt", "w", stdout);

string s;
getline (cin,s);
cout<<s<<"\n";

assert(2 == scanf("%d %d", &n, &m));


cout<<"m="<<m<<" n="<<n<<"\n";
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 359

assert(1 == scanf("%d", &MAX_Q));


cout<<"MAX_Q = "<<MAX_Q<<"\n\n";

u.resize(m);
v.resize(m);

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

goal.resize(m, false);

for(int i = 0; i < n - 1; i++)


{
int id;
assert(1 == scanf("%d", &id));
goal[id] = true;
}

auto t2 = clock();

vector<int> res = find_roads(n, u, v);

auto t3 = clock();

if(_count_common_roads_internal(res) != n - 1) wrong_answer();

//printf("YES\n");
printf("YES q = %d\n\n",q);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
m=124750 n=500
MAX_Q = 12000

YES q = 5685

t2-t1 = 0.39
t3-t2 = 1.235
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.5.4: simurgh-70729.cpp


// https://oj.uz/submission/70729 213 ms 30820 KB
#include "simurgh.h"

// ------------- simurgh ----------------

#include <bits/stdc++.h>

using namespace std;

using pii = pair<int, int>;


#define F first
#define S second

const int N = 510;


const int M = N*(N-1)/2;
const int LIM = 30000;

int n, m;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 360

vector<int> U, V; // edge list


vector<pii> G[N]; // adj list
vector<int> T; // tree edges
vector<pii> GT[N]; // tree adj list
bool is_tree[M];

int ans[M]; // answer to each edge (-1 = unknown)


int defcnt; // answer for the default spanning tree

int tick;
pii disc[N], low[N];
void dfs(int u, int p)
{
disc[u] = low[u] = pii(++tick, -1);
for (auto v : G[u])
{
if (!disc[v.F].F)
{
// push edge to tree
is_tree[v.S] = true;
T.push_back(v.S);

GT[u].emplace_back(v.F, v.S);
GT[v.F].emplace_back(u, v.S);

// tarjan’s algorithm
dfs(v.F, u);
low[u] = min(low[u], low[v.F]);
if (low[v.F].F > disc[u].F)
ans[v.S] = 1;
}
else
if (v.F != p)
{
low[u] = min(low[u], pii(disc[v.F].F, v.S));
}
}
}

vector<int> find_path(int u, int e, int t)


{
if (u == t)
{
vector<int> ret;
ret.push_back(e);
return ret;
}
for (auto v : GT[u]) if (v.S != e)
{
vector<int> ret = find_path(v.F, v.S, t);
if (!ret.empty())
{
if (e != -1)
ret.push_back(e);
return ret;
}
}
return vector<int>();
}

int get_changes(int rem, int add)


{
vector<int> q(T);
q.erase(find(q.begin(), q.end(), rem));
q.push_back(add);
return count_common_roads(q) - defcnt;
}

void query_cycle(int ce)


{
if (ans[ce] != -1) return;
int s = U[ce], t = V[ce];
vector<int> path = find_path(s, -1, t);

// find edge in path that we already know answer to


int fnd = -1;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 361

for (auto e : path)


{
if (ans[e] != -1)
{
fnd = e;
break;
}
}

if (fnd != -1)
{ // case where we can quickly determine ce’s value
ans[ce] = ans[fnd] + get_changes(fnd, ce);
for (auto e : path)
{ // update other edge in path we don’t know
if (ans[e] == -1)
ans[e] = ans[ce] - get_changes(e, ce);
}
}
else
{ // don’t know, so query everything in the cycle
for (auto e : path)
{
int d = get_changes(e, ce);
if (d == 1)
ans[ce] = 1, ans[e] = 0;
else if (d == -1)
ans[ce] = 0, ans[e] = 1;
}

if (ans[ce] == -1) ans[ce] = 0;


for (auto e : path)
{
if (ans[e] == -1)
ans[e] = ans[ce];
}
}
}

int par[N];

int root(int u)
{
if (par[u] == u) return u;
return par[u] = root(par[u]);
}

bool merge(int u, int v)


{
u = root(u), v = root(v);
if (u == v) return false;
par[u] = v;
return true;
}

int query_adj(vector<int> &adj)


{
if (adj.empty()) return 0;

iota(par, par+n, 0);

vector<int> q;
for (auto e : adj)
{
if (merge(U[e], V[e]))
q.push_back(e);
}

int cnt = 0;
for (auto e : T)
{
if (merge(U[e], V[e]))
{
cnt += ans[e];
q.push_back(e);
}
}
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 362

int ret = count_common_roads(q);


return ret-cnt;
}

void solve(vector<int> &adj, int cnt = -1)


{
if (cnt == -1)
cnt = query_adj(adj);
if (cnt == 0)
{
for (auto e : adj)
ans[e] = 0;
return;
}

if (cnt == adj.size())
{
for (auto e : adj)
ans[e] = 1;
return;
}

int mid = adj.size()/2;


vector<int> L(adj.begin(), adj.begin()+mid);
vector<int> R(adj.begin()+mid, adj.end());

int lcnt = query_adj(L);


solve(L, lcnt);
solve(R, cnt-lcnt);
}

vector<int> find_roads(int _n, vector<int> _U, vector<int> _V)


{
// create adj list
n = _n;
U = _U, V = _V;
m = U.size();
for (int i = 0; i < m; ++i)
{
int u = U[i], v = V[i];
G[u].emplace_back(v, i);
G[v].emplace_back(u, i);
}

// build dfs tree and find answer for the tree


fill(ans, ans+m, -1);
dfs(0, -1);
defcnt = count_common_roads(T);
for (int i = 0; i < n; ++i)
{
if (low[i].S != -1)
query_cycle(low[i].S);
}

// find answer for each node


for (int u = 0; u < n; ++u)
{
vector<int> adj;
for (auto v : G[u])
{
if (ans[v.S] == -1)
adj.push_back(v.S);
}
solve(adj);
}

// return answer
vector<int> ret;
for (int i = 0; i < m; ++i)
{
if (ans[i])
ret.push_back(i);
}
return ret;
}
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 363

// --------- graderpublic -------------------------

static int MAXQ = 30000;


int MAX_Q;

static int q = 0;
//static int n, m, q = 0;

static vector<int> u, v;
static vector<bool> goal;

static void wrong_answer()


{
printf("NO\n");
exit(0);
}

static bool is_valid(const vector<int>& r)


{
if(int(r.size()) != n - 1)
return false;

for(int i = 0; i < n - 1; i++)


if (r[i] < 0 || r[i] >= m)
return false;

return true;
}

static int _count_common_roads_internal(const vector<int>& r)


{
if(!is_valid(r))
wrong_answer();

int common = 0;
for(int i = 0; i < n - 1; i++)
{
bool is_common = goal[r[i]];
if (is_common)
common++;
}

return common;
}

int count_common_roads(const vector<int>& r)


{
q++;
if(q > MAX_Q)
// if(q > MAXQ)

wrong_answer();

return _count_common_roads_internal(r);
}

int main()
{
auto t1 = clock();

std::freopen("../tests/4-07.in", "r", stdin);


//std::freopen("4-07.out.txt", "w", stdout);

string s;
getline (cin,s);
cout<<s<<"\n";

assert(2 == scanf("%d %d", &n, &m));


cout<<"m="<<m<<" n="<<n<<"\n";

assert(1 == scanf("%d", &MAX_Q));


cout<<"MAX_Q = "<<MAX_Q<<"\n\n";

u.resize(m);
v.resize(m);
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 364

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

goal.resize(m, false);

for(int i = 0; i < n - 1; i++)


{
int id;
assert(1 == scanf("%d", &id));
goal[id] = true;
}

auto t2 = clock();

vector<int> res = find_roads(n, u, v);

auto t3 = clock();

if(_count_common_roads_internal(res) != n - 1) wrong_answer();

//printf("YES\n");
printf("YES q = %d\n\n",q);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
m=124750 n=500
MAX_Q = 12000

YES q = 2541

t2-t1 = 0.343
t3-t2 = 0.422
t4-t3 = 0.016

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


Press any key to continue.
*/

Listing 3.5.5: simurgh-136567.cpp


// https://oj.uz/submission/136567 613 ms 5624 KB
#include "simurgh.h"

// ------------- simurgh ----------------

#include "simurgh.h"
#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define MP make_pair

#define F first
#define S second

typedef pair<int,int> pii;

vector<int> U,V;
vector<pii> G[505];
pii pa[505];

queue<int> q;
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 365

int type[505],dep[505],deg[505],boss[505],n;
set<int> s;
bitset<505> vis,ed;
bitset<250000> used;

int ccr()
{
return count_common_roads(vector<int>(s.begin(),s.end()));
}

void dfs(int u,int f,int num,int d)


{
pa[u]=MP(f,num),dep[u]=d,vis[u]=1;
if(˜num) used[num]=1,s.insert(num);
for(pii i:G[u])
if(!vis[i.F])
dfs(i.F,u,i.S,d+1);
}

int finds(int x)
{
if(x==boss[x]) return x;
return boss[x]=finds(boss[x]);
}

int Union(int a,int b)


{
a=finds(a),b=finds(b);
if(a==b) return 0;
return boss[a]=b,1;
}

int ccr_forest(vector<int> f)
{
int cnt=0;
for(int i=0;i<n;++i)
boss[i]=i;

set<int> tmp;
for(int i:f)
tmp.insert(i),Union(U[i],V[i]);
for(int i=1;i<n;++i)
if(Union(i,pa[i].F))
cnt+=type[i],tmp.insert(pa[i].S);
tmp.swap(s),cnt=ccr()-cnt,tmp.swap(s);

return cnt;
}

int leaf_finding(int u)
{
vector<int> candi;
for(pii i:G[u])
if(!ed[i.F])
candi.pb(i.S);

int l=0,r=candi.size()-1;
while(l<r)
{
vector<int> v;
int m=l+r>>1;
for(int i=l;i<=m;++i)
v.pb(candi[i]);
if(ccr_forest(v)>=1) r=m;
else l=m+1;
}
return candi[l];
}

vector<int> find_roads(int N, vector<int> u, vector<int> v)


{
n=N,U=u,V=v;
vector<int> ans;
for(int i=0;i<u.size();++i)
G[u[i]].pb(MP(v[i],i)),G[v[i]].pb(MP(u[i],i));
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 366

dfs(0,-1,-1,0),fill(type,type+N,-1);

int cur=ccr();
for(int i=0;i<u.size();++i)
{
if(used[i]) continue;
int cnt=0,sz=0,tp=-1,a=u[i],b=v[i];
pii sw;
while(a!=b)
{
if(dep[a]<dep[b]) swap(a,b);
if(˜type[a]) sw=MP(pa[a].S,type[a]);
cnt+=!˜type[a],++sz,a=pa[a].F;
}
if(!cnt) continue;
a=u[i],b=v[i];
if(cnt==sz)
{
vector<int> v;
while(a!=b)
{
if(dep[a]<dep[b]) swap(a,b);
s.erase(pa[a].S),s.insert(i);
int tmp=ccr();
if(tmp==cur) v.pb(a);
else
{
if(tmp>cur) tp=1,type[a]=0;
else tp=0,type[a]=1;
for(int j:v)
type[j]=tp;
s.erase(i),s.insert(pa[a].S),a=pa[a].F;
break;
}
s.erase(i),s.insert(pa[a].S),a=pa[a].F;
}
if(a==b&&!˜tp)
for(int j:v)
type[j]=0;
}
else
s.erase(sw.F),
s.insert(i),
tp=(sw.S==0)ˆ(ccr()==cur),
s.erase(i),
s.insert(sw.F);

while(a!=b)
{
if(dep[a]<dep[b]) swap(a,b);
s.erase(pa[a].S),s.insert(i);
if(!˜type[a])
type[a]=tpˆ(ccr()!=cur);
s.erase(i),s.insert(pa[a].S),a=pa[a].F;
}
}

for(int i=1;i<n;++i)
if(!˜type[i]) type[i]=1;

for(int i=0;i<n;++i)
{
vector<int> v;
for(pii j:G[i])
v.pb(j.S);
deg[i]=ccr_forest(v);
if(deg[i]==1)
q.push(i);
}

while(q.size()>1)
{
int u=q.front();
q.pop(),
ed[u]=1,
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 367

ans.pb(leaf_finding(u)),
uˆ=U[ans.back()]ˆV[ans.back()];
if(--deg[u]==1)
q.push(u);
}
return ans;
}

// --------- graderpublic -------------------------

static int MAXQ = 30000;


int MAX_Q;

static int m,qq = 0;


//static int n, m, q = 0;

static vector<int> u, v;
static vector<bool> goal;

static void wrong_answer()


{
printf("NO\n");
exit(0);
}

static bool is_valid(const vector<int>& r)


{
if(int(r.size()) != n - 1)
return false;

for(int i = 0; i < n - 1; i++)


if (r[i] < 0 || r[i] >= m)
return false;

return true;
}

static int _count_common_roads_internal(const vector<int>& r)


{
if(!is_valid(r))
wrong_answer();

int common = 0;
for(int i = 0; i < n - 1; i++)
{
bool is_common = goal[r[i]];
if (is_common)
common++;
}

return common;
}

int count_common_roads(const vector<int>& r)


{
qq++;
if(qq > MAX_Q)
// if(q > MAXQ)

wrong_answer();

return _count_common_roads_internal(r);
}

int main()
{
auto t1 = clock();

std::freopen("../tests/4-07.in", "r", stdin);


//std::freopen("4-07.out.txt", "w", stdout);

string s;
getline (cin,s);
cout<<s<<"\n";

assert(2 == scanf("%d %d", &n, &m));


CAPITOLUL 3. IOI 2017 3.5. SIMURGH 368

cout<<"m="<<m<<" n="<<n<<"\n";

assert(1 == scanf("%d", &MAX_Q));


cout<<"MAX_Q = "<<MAX_Q<<"\n\n";

u.resize(m);
v.resize(m);

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

goal.resize(m, false);

for(int i = 0; i < n - 1; i++)


{
int id;
assert(1 == scanf("%d", &id));
goal[id] = true;
}

auto t2 = clock();

vector<int> res = find_roads(n, u, v);

auto t3 = clock();

if(_count_common_roads_internal(res) != n - 1) wrong_answer();

//printf("YES\n");
printf("YES qq = %d\n\n",qq);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
m=124750 n=500
MAX_Q = 12000

YES qq = 4800

t2-t1 = 0.312
t3-t2 = 4.481
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.5.6: simurgh-137723.cpp


// https://oj.uz/submission/137723 386 ms 8176 KB
#include "simurgh.h"

#include<bits/stdc++.h>

using namespace std;

#define ll long long


#define pb push_back

const int maxn = 5e2 + 20;


const int maxm = maxn * maxn + 20;

int from[maxm] , to[maxm] , par[maxn] , h[maxn] , is[maxm] , ed[maxm] , num;

vector<int> adj[maxn] , bc[maxn] , ind;


CAPITOLUL 3. IOI 2017 3.5. SIMURGH 369

bool visited[maxn] , tree[maxm];

void plant(int v)
{
visited[v] = 1;
for(auto e : adj[v])
{
int u = from[e] ˆ to[e] ˆ v;
if(!visited[u])
{
ind.pb(e);
h[u] = h[v] + 1;
par[u] = e;
tree[e] = 1;
plant(u);
}
else if(h[u] < h[v] - 1)
bc[v].pb(e);
}
}

vector<int> rem(vector<int> x , int y)


{
vector<int> nw;
for(auto k : x)
if(k != y)
nw.pb(k);

return nw;
}

bool cmp(int x , int y)


{
x = min(h[from[x]] , h[to[x]]);
y = min(h[from[y]] , h[to[y]]);

return x > y;
}

int wtf[maxm];

int get(vector<int> &bc , int sz)


{
if(!sz)
return 0;

vector<int> shit;
for(int i = 0; i < sz; i++)
{
int e = bc[i];
tree[wtf[e]] = 0;
shit.pb(e);
}

int ts = 0;
for(auto x : ind)
if(tree[x])
{
if(is[x] == 1)
ts++;
shit.pb(x);
}

for(int i = 0; i < sz; i++)


{
int e = bc[i];
tree[wtf[e]] = 1;
}

return count_common_roads(shit) - ts;


}

void dfs(int v)
{
for(auto e : adj[v])
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 370

{
int u = from[e] ˆ to[e] ˆ v;
if(h[u] == h[v] + 1)
dfs(u);
}

if(bc[v].empty())
return;

int last = v;

sort(bc[v].begin() , bc[v].end() , cmp);


for(auto e : bc[v])
{
while(!last);
wtf[e] = par[last];
last = from[e] ˆ to[e] ˆ v;
}

int k = bc[v].size() , st = 0 , stl = 0 , shit = get(bc[v] , k);


while(k && shit > st)
{
int l = stl , r = k;
while(r - l > 1)
{
int m = (l + r) / 2;
if(get(bc[v] , m) > st)
r = m;
else
l = m;
}

is[bc[v][r - 1]] = 1;
stl = r;
st++;
}

vector<int> find_roads(int n,vector<int> A,vector<int> B)


{
n = n;
int m = A.size();
for(int i = 0; i < m; i++)
{
int a = A[i] , b = B[i];

adj[a].pb(i);
adj[b].pb(i);

from[i] = a , to[i] = b;
}

plant(0);

memset(ed , -1 , sizeof ed);

num = count_common_roads(ind);
for(int i = 0; i < m; i++)
if(!tree[i])
{
int v = from[i] , u = to[i];
if(h[v] < h[u])
swap(v , u);

vector<int> ed;

bool has0 = 0;
while(v != u)
{
if(is[par[v]] == 0)
has0 = 1;

ed.pb(par[v]);
v = from[par[v]] ˆ to[par[v]] ˆ v;
}
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 371

if(!has0)
continue;

vector<int> eq;
for(auto e : ed)
{
if(is[e] != 0 && is[i] != 0)
continue;

ind = rem(ind , e);


ind.pb(i);
int nw = count_common_roads(ind);
ind.pop_back() , ind.pb(e);

if(nw == num)
{
if(is[e] != 0)
is[i] = is[e];
else
eq.pb(e);
}
else if(nw == num + 1)
{
is[i] = 1;
is[e] = -1;
}
else
{
is[e] = 1;
is[i] = -1;
}
}

if((int)eq.size() == (int)ed.size())
is[i] = -1;

for(auto e : eq)
is[e] = is[i];
}

for(auto x : ind)
if(is[x] == 0)
is[x] = 1;

dfs(0);

vector<int> ans;
for(int i = 0; i < m; i++)
{
if(!is[i])
is[i] = -1;
if(is[i] == 1)
ans.pb(i);
}

while(count_common_roads(ans) != n - 1);

return ans;
}

// --------- graderpublic -------------------------

static int MAXQ = 30000;


int MAX_Q;

static int n, m, q = 0;

static vector<int> u, v;
static vector<bool> goal;

static void wrong_answer()


{
printf("NO\n");
exit(0);
}
CAPITOLUL 3. IOI 2017 3.5. SIMURGH 372

static bool is_valid(const vector<int>& r)


{
if(int(r.size()) != n - 1)
return false;

for(int i = 0; i < n - 1; i++)


if (r[i] < 0 || r[i] >= m)
return false;

return true;
}

static int _count_common_roads_internal(const vector<int>& r)


{
if(!is_valid(r))
wrong_answer();

int common = 0;
for(int i = 0; i < n - 1; i++)
{
bool is_common = goal[r[i]];
if (is_common)
common++;
}

return common;
}

int count_common_roads(const vector<int>& r)


{
q++;
if(q > MAX_Q)
// if(q > MAXQ)

wrong_answer();

return _count_common_roads_internal(r);
}

int main()
{
auto t1 = clock();

std::freopen("../tests/4-07.in", "r", stdin);


//std::freopen("4-07.out.txt", "w", stdout);

string s;
getline (cin,s);
cout<<s<<"\n";

assert(2 == scanf("%d %d", &n, &m));


cout<<"m="<<m<<" n="<<n<<"\n";

assert(1 == scanf("%d", &MAX_Q));


cout<<"MAX_Q = "<<MAX_Q<<"\n\n";

u.resize(m);
v.resize(m);

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


assert(2 == scanf("%d %d", &u[i], &v[i]));

goal.resize(m, false);

for(int i = 0; i < n - 1; i++)


{
int id;
assert(1 == scanf("%d", &id));
goal[id] = true;
}

auto t2 = clock();

vector<int> res = find_roads(n, u, v);


CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 373

auto t3 = clock();

if(_count_common_roads_internal(res) != n - 1) wrong_answer();

//printf("YES\n");
printf("YES q = %d\n\n",q);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
m=124750 n=500
MAX_Q = 12000

YES q = 4637

t2-t1 = 0.437
t3-t2 = 2.274
t4-t3 = 0

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


Press any key to continue.
*/

3.5.3 *Rezolvare detaliată

3.6 Ancient Books


Problema 6 - Ancient Books 100 de puncte

Author: Daniel Graf (Switzerland)

Biblioteca Naţională a Iranului se află ı̂n Teheran. Atracţia principală a bibliotecii este local-
izată de-a lungul unui coridor ı̂n care se află n mese, numerotate de la 0 la n  1 de la stânga la
dreapta. Pe fiecare masă se află câte o carte antică scrisă de mână. Cărţile sunt ordonate după
vârsta lor, acest lucru ı̂ngreunându-i pe vizitatori să caute cărţile după titlu. Aşadar, administra-
torul bibliotecii a decis să sorteze cărţile ı̂n ordine alfabetică după titlu.
Aryan, un bibliotecar, este responsabil pentru această sarcină. El a creat o listă p de lungime
n, care conţine numere ı̂ntregi de la 0 la n  1, distincte două câte două. Această listă reprezintă
modificările necesare pentru rearanjarea cărţilor ı̂n ordine alfabetică: pentru fiecare 0 & i $ n,
cartea care se află ı̂n momentul curent pe masa i trebuie mutată pe masa pi.
Aryan ı̂ncepe sortarea cărţilor pornind de la masa s. După terminarea sortării cărţilor acesta
doreşte să se ı̂ntoarcă la această masă. Având ı̂n vedere valoarea cărţilor, el nu poate avea la el
mai mult de o carte ı̂n orice moment de timp. Cât timp Aryan sortează cărţile, el va efectua o
secvenţă de acţiuni. Fiecare acţiune este una din următoarele:

a Dacă nu are la el o carte şi dacă este o carte pe masa la care se află, el poate lua această
carte.
a Dacă are la el o carte şi o altă carte este pe masa la care se află, el poate interschimba cele
două cărţi.
a Dacă are la el o carte şi masa la care se află este goală, el poate pune pe masă cartea pe
care o are la el.
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 374

a El poate merge la orice masă. El poate căra o singură carte.

Pentru toate 0 & i, j & n  1, distanţa dintre mesele i şi j este de ¶j  i¶ metri. Sarcina voastră
este de a-l ajuta pe Aryan să sorteze cărţile ı̂ntr-un mod ı̂n care distanţa totală parcursă de el să
fie minimă.
Detalii de implementare
Voi trebue să implementaţi următoarea procedură:

int64 minimum_walk(int[] p, int s)

a p este un şir de lungime n. Cartea care se află la ı̂nceput pe masa i trebuie dusă de Aryan
pe masa pi (pentru toate 0 & i $ n).
a s reprezintă poziţia mesei de unde ı̂ncepe Aryan, şi unde trebuie să se afle după sortarea
cărţilor.
a Această procedură trebuie să returneze distanţa totală minimă (ı̂n metri) parcusă de Aryan.

Exemplu

minimum_walk([0, 2, 3, 1], 0)

În acest exemplu, n 4 şi Aryan se află iniţial la masa 0. El sortează cărţile după cum
urmează:

a Merge la masa 1 şi ia cartea de pe masă. Această carte trebuie să ajungă pe masa 2.
a Apoi, merge la masa 2 şi interschimbă cartea din mână cu cea de pe masă. Cartea nouă
trebuie să ajungă pe masa 3.
a Apoi, merge la masa 3 şi interschimbă cartea din mână cu cea de pe masă. Cartea nouă
trebuie să ajungă pe masa 1.
a Apoi, merge la masa 1 şi pune cartea ce o are la el pe această masă.
a În final, el se ı̂ntoarce la masa 0.

Se observă că pe masa 0 cartea se află pe poziţia corectă, deci Aryan nu trebuie să o mute.
Distanţa totală parcursă ı̂n această soluţie este de 6 metri. Aceasta este soluţia optimă; aşadar
procedura trebuie să returneze valoarea 6.
Restricţii şi precizări
a 1 & n & 1 000 000
a 0&s&n1
a şirul p conţine n numere ı̂ntregi distincte ı̂ntre 0 si n  1, inclusiv.
Subtask-uri
1. (12 puncte) n & 4 şi s 0
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 375

2. (10 puncte) n & 1000 şi s 0


3. (28 puncte) s 0
4. (20 puncte) n & 1000
5. (30 puncte) fără restricţii adiţionale
Evaluator local
Evaluatorul local citeşte datele de intrare ı̂n următorul format:
a linia 1: n s
a linia 2: p0 p1 ... pn  1
Evaluatorul local afişează o singură linie reprezentând valoarea returnată de minimum_walk.
Timp maxim de executare/test: 2.0 secunde
Memorie: total 1024 MB

3.6.1 Indicaţii de rezolvare

Daniel Graf - PhD student at Department of Computer Science at ETH Zürich

Example:

In this example, we have N 5 and Mina starts at slot S 0.


The books are called A, B, C, D, E and are initially ordered CABED.
One of the optimal solutions is shown in the picture.
Mina first takes book C and moves it to slot 3 where she swaps it for book E. She can then
bring book E to slot 4, book D to slot 3, book C to slot 2, book B to slot 1 and finally book A to
slot 0.
This trip takes her 8 seconds and there is no way to do it any faster.

Easy subtasks (subtasks 1 and 2)


The first subtask, where there are at most 4 books in the library, can easily be solved by hand.
There are only 1!+2!+3!+4! = 33 possible inputs and so they can all be precomputed by hand
simulating the process with pen and paper.
The second subtask allows a brute force state exploration. We can take the triple (state of the
shelf, Minas position, Minas current book) as the state in a breadth first search. This state space
is roughly of size N  1! N and so for N & 7 we can still find the shortest path to the sorted
state fast enough. See library 20.cpp for an implementation.
Medium subtask (start/end on the very left)
To compute the number of steps (=seconds) needed without actually simulating the entire
sorting process, we need a couple of observations:
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 376

Balancing property. Across every edge of the underlying path graph, Mina will walk equally
often from left to right as she walks right to left. A similar balance also applies to the books. For
every edge of the path, there are equally many books that need to be moved from left to right as
there are from right to left.
Cycles of the permutation The array order specifies a permutation. We will denote that
permutation by π from here on. We will see, that the answer only depends on how π partitions the
set of slots r0, ..., n  1x into disjoint cycles. A book that is placed correctly from the beginning,
we call trivial. Their corresponding trivial cycles (cycles of length one) can almost be ignored, as
we will always just move past them.
Single cycle If π consists of a single cycle then the answer is easy to find: Mina can grab the
book at S, bring it to π S , take the book from there to π π S  and so on until she returns to
S. As she brings one book on slot closer to its target position in every step, her walk surely is
optimal. We can compute this number of steps by
n1
d π = ¶i  π i¶
i 0

which is exactly the sum of the distances between initial and target position of every book.

Lower bound d π  The sum of distances d π  is a lower bound on the answer even if π
consists of multiple cycles. We distinguish two kinds of steps for Mina: A step is called essential
if Mina brings one book one step closer to its target position than this book ever was before.
Otherwise the step is called non-essential. Every way of sorting the books consists of exactly d π 
many essential steps. The number of non-essential steps needed depends on how the cycles of π
overlap.
Two cycles Every cycle of π covers some interval I of the bookshelf which extends from the
leftmost book to the rightmost book that are part of this cycle.
We have I N 0, n  1, where we use i, j  as a shorthand for ri, i  1, ..., j  1, j x.
Let π consist of exactly two non-trivial cycles C1 and C2 with their respective intervals I1 ,
I2 and let S 0 wit S " C1 . Then the answer only depends on whether the cycles overlap
(I1 = I2 j Ø) or not. If they overlap, the answer is d π  otherwise it is d π   2.
Why? If they overlap Mina can sort along C1 until she encounters the first book belonging
to C2 . She then leaves the book she was carrying at that slot to fully sort C2 and return to the
same slot. She can than pick up that book again and finish sorting C1 without ever spending a
non-essential step.

If the two cycles dont overlap (so C1 is entirely to the left of C2 ), Mina can do something
similar.
She starts sorting C1 until she encounters the rightmost slot belonging to C1 . She then takes
the book from there and non-essentially walks with one step to the right to the leftmost slot of
C2 . There, she sorts C2 and picks up the same book again to non-essentially walk back to C1 .
Finally, she finishes sorting C1 and returns to S. This is optimal since the only two non-
essential steps of Minas walk are spent across an edge that no book needs to cross but has to be
crossed by Mina eventually as there are non-trivial books on both sides.
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 377

Multiple cycles The two cases from before generalize to the case where consists of many
cycles. Any two overlapping cycles can be interleaved without non-essential steps and Mina has
to spend two non-essential steps across every edge that no book needs to cross but on both sides
¬
there are either some non-trivial books or S. More formally, let E be the subset of the edges of
the path with the following property:

e i, i  1 " E
¬
no book has to cross e and
some book to the left of e is non-trivial or at S and
some book to the right of e is non-trivial or at S and
 Àj " 0, i  π j  % i  1 0 ¿ © j " i  1, n  1  π j  & i and
¿l " 0, is.t. l j π l 1 l S  and
¿r " i  1, n  1s.t. r j π r  1 r S 
¬
If S 0, these are all the non-essential steps needed, so the answer is d π   2 ¶E ¶.
¬
Implementation With these observations, it is easy to compute both d π  and ¶E ¶. If it
¬
is done in quadratic time (e.g. by just checking the above conditions for E by looping over all
indices for every edge), this will solve subtask 3 (see library_35.cpp for an implementation).
¬
However, it is not hard to compute E in linear time (e.g. in a scanline fashion from left to right),
which will then score for the first four subtasks (see library_50.cpp for an implementation).

Harder subtasks (with S j 0)


If Mina does start somewhere in the middle of the shelf we might need some additional
¬
nonessential steps across edges not in E . It is not obvious whether Mina should first go to
the left or to the right as there might be non-trivial books on both sides.

We could try out both options and define the following subproblem:

How many non-essential steps do we need, if we already know how to connect all the cycles
in the interval l, r with S " l, r?

This gives rise to a dynamic programming formulation with a state of quadratic size (all intervals
containing S).
¬ ¬
For any given interval, we define the function extend l, r with l , r  extend l, r being the
largest part of the shelf that we can sort without spending any additional non-essential steps. So
extend has to repeatedly add all cycles C whose interval I partially or fully overlap with l, r and
then continue with l, r  l, r < I until no more cycles can extend the interval.
Once there is no other overlapping cycle (so l, r extend l, r), we are either done or we
know that we have to spend some non-essential steps.
Let combine l, r be the function that computes the cost of connecting all cycles to the interval
l, r .
We can recursively compute it using

combine l, r 2  min combine l  1, r, combine l, r  1.

We need to take care of the border cases (when l  1 or r  1 are outside the shelf) and initialize
¬ ¬ ¬ ¬
it with combine l , r  0 for l , r  being the smallest interval that contains all non-trivial books
and S.
By implementing extend carefully, we can achieve an amortized constant time complexity
across all calls, so that the dynamic program runs in quadratic time overall. The code in the file
library_70.cpp implements this using memoization. Note that for S 0, the set of states is
only of linear size, so this solution also passes subtask 4.
To solve the problem in linear time, we note that we can decide whether to go left or right
somewhat locally without exploring quadratically many states. If l, r is some extended interval
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 378

33
(l, r extend l, r), we look for two special cycles Cl and Cr . Cl is the first S-containing cycle
that we encounter, when walking from l to the left. Similarly, Cr is the first S-containing cycle
when walking from r to the right.
Let cl be the cost of reaching Cl from l (and define cr ).
Note that cl is not just twice the distance between l and the closest book of Cl as there might be
some small cycles along the way that help us save some non-essential steps. But we can compute
cl quickly by solving the (S 0)-problem between l and the first box of Cl .
Observe that if Cl does not exist, Cr does also not exist (as l, r is maximally extended, the
book of Cl to right of S also has to be to the right of r and vice versa).
Also note that once we reach either Cl or Cr , we also reach Cl <Cr and therefore get extend Cl <
Cr  without any further cost. This means that we can greadily decide for the cheaper of the two
sides (of cost min cl , cr ) and then continue with the interval extend Cl < Cr  regardless of whether
we decided to go left or right.
Finally, one has to take care of the border region, everything outside of the outermost S-
containing cycles (so once Cl and/or Cr no longer exist). But this is easy, as this is just another
(S 0)-case on each side.
We can answer all the extend l, r calls and compute all the cl and cr costs using only one
overall sweep over the shelf (if we precompute the cycle of each shelf and the interval of each cycle,
which we can also do in O n). Therefore, we can find determine the answer d π   combine S, S 
in linear time. The code in the file library_100.cpp implements this optimal solution.

Almost correct solutions


2
One thing a contestant might overlook is that the answer can be of the order O n  and
therefore does not necessarily fit into a 32-bit integer. This causes an overflow in subtasks 4 and
6.
Other submissions might consider only near optimal sorting walks. Some might visit also all
the trivial books at the ends of the paths, even though there is nothing to sort there. Others might
just traverse the path once without carrying any book and then sort every cycle individually along
the way without interleaving anything and hence spending 2n  2 many non-essential steps. Both
of these would result in non-optimal answers.

Overview
To summarize, here are the solution techniques listed per subtask:

subtask points technique


1 10 pen and paper
2 10 state exploration, BFS
3 15 ad hoc
4 15 ad hoc
5 20 dynamic programming
6 30 ad hoc / greedy

3.6.2 Coduri sursă

Listing 3.6.1: books+graderpublic.cpp


// http://ioi2017.org/tasks/materials/books.zip ---> sandbox/books.cpp
// http://ioi2017.org/tasks/materials/books.zip ---> solution/***.cpp

#include <iostream>
#include <vector>
#include "books.h"

#include <cstdio>
#include <cassert>

#include <fstream>
33
A cycle C with interval I is S-containing if and only if S " I.
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 379

#include<ctime>

using namespace std;

const int MAX = 1000005;


int le[MAX], ri[MAX], pos[MAX];
int mark[MAX];

int val(int x)
{
return (x > 0 ? x : -x);
}

void go(int &l, int &r, int nl, int nr)


{
while (nl < l || r < nr)
if (nl != l)
{
l--;
nl = min(nl, le[l]);
nr = max(nr, ri[l]);
}
else
{
r++;
nl = min(nl, le[r]);
nr = max(nr, ri[r]);
}
}

int dist(int s, int t)


{
if (s == t)
return 0;
int ns = s;
if (t < s)
{
while (s >= ns)
{
ns = min(ns, le[s]);
s--;
if (ns <= t)
return 0;
}
}
else
{
while (s <= ns)
{
ns = max(ns, ri[s]);
s++;
if (ns >= t)
return 0;
}
}
return dist(s, t) + 2;
}

long long minimum_walk(vector<int> p, int s)


{
int n = p.size();
long long ans = 0;
for (int i = 0; i < n; i++)
ans += val(i - p[i]);
for (int i = 0; i < n; i++)
le[i] = ri[i] = i;
for (int i = 0; i < n; i++)
pos[p[i]] = i;
for (int i = 0; i < n; i++)
if (!mark[i])
{
int v = i;
while (mark[v] < 2)
{
le[v] = min(le[v], le[p[v]]);
ri[v] = max(ri[v], ri[p[v]]);
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 380

mark[v]++;
v = pos[v];
}
}
int gl = 0;
while (gl < s && p[gl] == gl)
gl++;
int gr = n - 1;
while (gr > s && p[gr] == gr)
gr--;
int l = s + 1, r = s;
go(l, r, s, s);
bool done = false;
while (gl < l || r < gr)
{
int nl = l, nr = r;
if (!done)
{
while (nl >= 0 && ri[nl] <= r)
nl--;
}
if (nl == -1)
done = true;
if (done)
{
nl = l;
if (l <= gl)
{
nr++;
ans += 2;
}
else
{
nl--;
ans += 2;
}
}
else
{
while (l <= le[nr])
nr++;
ans += min(dist(l - 1, nl), dist(r + 1, nr)) + 2;
}
go(l, r, nl, nr);
}
return ans;
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);

auto t3 = clock();

printf("%lld\n", res);
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 381

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.871 ... citire !!!
t3-t2 = 0.219
t4-t3 = 0

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


Press any key to continue.
*/

OBS: Merită de ı̂ncercat o citire mai rapidă din fişier!

Listing 3.6.2: books-42759.cpp


// https://oj.uz/submission/42759 269 ms 169312 KB

#include "books.h"

#include<bits/stdc++.h>

using namespace std;

#define _ int v, int tl, int tr, int l, int r


#define tm (tl+tr >> 1)
#define sol v+v,tl,tm,l,r
#define sag v+v+1,tm+1,tr,l,r

#define mp make_pair
#define pb push_back
#define st first
#define nd second

const int mod = 1e9 + 7;


const int N = 1e6 + 6;

int H[N],T[N],A[N],B[N],n,i,j,l,r,ll,rr,t1,t2,t,a,b,lll,rrr;
long long x;

void relax(int &l, int &r, int &a, int &b)


{
for(;l > a || r < b;)
{
if(l > a) { l--; a = min(a,A[l]); b = max(b,B[l]); }
if(r < b) { r++; a = min(a,A[r]); b = max(b,B[r]); }
}
}

long long minimum_walk(vector < int > p, int s)


{
x = 0;
n = p.size();
for(i=0;i<n;i++){
x += abs(p[i] - i);
if(H[i]) continue;
r = i;
for(j=i; !H[j] ; j = p[j])
{
H[j] = 1;
r = max(r , j);
}
for(j=i; !T[j] ; j = p[j])
{
T[j] = 1; A[j] = i; B[j] = r;
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 382

}
}

for(i=0;i<n && p[i] == i; i++);


for(j=n-1;j>=0 && p[j] == j; j--);

l = r = s;
a = A[s];
b = B[s];

relax(l,r,a,b);

for(; l > i || r < j ;)


{
t1 = 0;
ll = lll = l; rr = rrr = r;
if(l > i)
{
for(a=l, b=r ; rr == r && ll > i ; )
{
ll--;
t1++;
a = min(A[ll],a);
b = max(B[ll],b);
relax(ll,rr,a,b);
}
if(r >= j) { t += t1; break; }
}

t2 = 0;
if(r < j)
{
for(a = l, b = r ; lll == l && rrr < j ; )
{
rrr++;
t2++;
a = min(A[rrr],a);
b = max(B[rrr],b);
relax(lll,rrr,a,b);
}
if(l <= i) { t += t2; break; }
}

if(rr == r) { if(lll != l) assert(0); t += t1+t2; break; }


t += min(t1,t2);
l = ll; r = rr;
if(lll != ll || rrr != rr) assert(0);
}

return x + t+t;
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);


CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 383

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.703
t3-t2 = 0.172
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.6.3: books-51837.cpp


// https://oj.uz/submission/51837 253 ms 167404 KB

#include <bits/stdc++.h>
#include "books.h"

using namespace std;

const int N = 1000005;

long long minimum_walk(vector<int> p, int s)


{
int n = p.size();
long long res = 0;
for (int i = 0; i < n; ++i) res += abs(i - p[i]);
int ml = n - 1, mr = 0;
for (int i = 0; i < n; ++i)
{
if (i != p[i]) ml = min(ml, i), mr = max(mr, i);
}

int cl = s, cr = s;
queue<int> qu;
qu.push(s);

while (1)
{
while (qu.size())
{
int u = qu.front(); qu.pop();
while (cl > p[u])
{
cl--, qu.push(cl);
}

while (cr < p[u])


{
cr++, qu.push(cr);
}
}

int l = cl, r = cr;


int dl = 0, dr = 0;
bool fl = 0, fr = 0;

queue<int> ql, qr;


CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 384

while (1)
{
if (l <= ml) break;
l--, dl++, ql.push(l);
while (ql.size())
{
int u = ql.front();
ql.pop();
while (l > p[u])
{
l--, ql.push(l);
}
if (p[u] > s) fl = 1;
}
if (fl) break;
}

while (1)
{
if (r >= mr) break;
r++, dr++, qr.push(r);
while (qr.size())
{
int u = qr.front();
qr.pop();
while (r < p[u])
{
r++, qr.push(r);
}
if (p[u] < s) fr = 1;
}
if (fr) break;
}

if (fl && fr)


{
res += 2 * min(dl, dr);
while (cl > l) qu.push(--cl);
while (cr < r) qu.push(++cr);
}
else
{
res += 2 * (dl + dr); break;
}
}
return res;
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);

auto t3 = clock();

printf("%lld\n", res);
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 385

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.687
t3-t2 = 0.391
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.6.4: books-94567.cpp


// https://oj.uz/submission/94567 147 ms 19072 KB

#include <bits/stdc++.h>
#include "books.h"

using namespace std;

long long minimum_walk(vector<int> p, int s)


{
int n = p.size();
int l = n, r = -1;
long long ans = 0;
for (int i = 0; i < n; ++i)
{
if (i != p[i])
{
ans += abs(i - p[i]);
l = min(l, i);
r = max(r, i);
}
}

int cur_l = s, cur_r = s;


queue<int> q;
q.push(s);
while (true)
{
while (!q.empty())
{
int x = q.front();
q.pop();
while (cur_l > p[x])
{
q.push(--cur_l);
}
while (cur_r < p[x])
{
q.push(++cur_r);
}
}

int new_l = cur_l, new_r = cur_r;


int cost_l = 0, cost_r = 0;
bool found_l = false, found_r = false;

while (new_l > l && !found_l)


{
q.push(--new_l);
++cost_l;
while (!q.empty())
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 386

{
int x = q.front();
q.pop();
if (p[x] > s)
{
found_l = true;
}

while (new_l > p[x])


{
q.push(--new_l);
}
}
}

while (new_r < r && !found_r)


{
q.push(++new_r);
++cost_r;
while (!q.empty())
{
int x = q.front();
q.pop();
if (p[x] < s)
{
found_r = true;
}

while (new_r < p[x])


{
q.push(++new_r);
}
}
}

if (found_l && found_r)


{
ans += min(cost_l, cost_r) << 1;
while (cur_l > new_l)
{
q.push(--cur_l);
}

while (cur_r < new_r)


{
q.push(++cur_r);
}
}
else
{
ans += cost_l + cost_r << 1;
break;
}
}

return ans;
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 387

assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.703
t3-t2 = 0.328
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.6.5: books-122101.cpp


// https://oj.uz/submission/122101 145 ms 8264 KB

#include <bits/stdc++.h>

#pragma optimization("unroll-loops")
#pragma optimization("Ofast")
#pragma target("avx2")

using namespace std;


long long res;

void push(int& l, int& r, int& mn, int& mx, vector<int>& p)


{
while(l != mn || r != mx)
{
if(l != mn)
{
l--;
mn = min(mn, p[l]);
mx = max(mx, p[l]);
}
else
{
r++;
mn = min(mn, p[r]);
mx = max(mx, p[r]);
}
}
}

long long minimum_walk(vector<int> p, int s)


{
int L = 0, R = p.size() - 1;
while(R >= 0 && p[R] == R) R--;
if(R == -1) return 0;
while(p[L] == L) L++;

if(s > R)
{
res += (s - R) << 1;
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 388

s = R;
}

if(s < L)
{
res += (L - s) << 1;
s = L;
}

int tmn = s, tmx = s;


for(int i = L; i <= R; i++)
{
res += abs(p[i] - i);
if(1LL * (tmx - p[i]) * (tmx - i) <= 0 ||
1LL * (tmn - p[i]) * (tmn - i) <= 0)
{
tmx = max(tmx, max(i, p[i]));
tmn = min(tmn, min(i, p[i]));
}
}

int l, r, mn = min(s, p[s]), mx = max(s, p[s]);


l = r = s;
push(l, r, tmn, tmx, p);

l = r = s;
push(l, r, mn, mx, p);

while(mn != tmn || mx != tmx)


{
res += 2;
if(mn != tmn)mn--;
if(mx != tmx)mx++;
push(l, r, mn, mx, p);
}

for(; l >= L; l--)


{
if(mn > l) res += 2;
mn = min(mn, p[l]);
}

for(; r <= R; r++)


{
if(mx < r) res += 2;
mx = max(mx, p[r]);
}
return res;
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);

auto t3 = clock();
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 389

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.703
t3-t2 = 0.125
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.6.6: books-138862.cpp


// https://oj.uz/submission/138862 234 ms 21568 KB

#include <bits/stdc++.h>
#include "books.h"

//#define long long long

using namespace std;

const int N = 1e6+5;

long long ans = 0;


int n, x, y;
int id[N], L[N], R[N], ptr;
bool check[N];

void extend(int &l, int &r)


{
int ll = min(L[id[l]], L[id[r]]);
int rr = max(R[id[l]], R[id[r]]);
while(ll < l || r < rr)
{
if(ll < l) l--;
else r++;
ll = min(ll, min(L[id[l]], L[id[r]]));
rr = max(rr, max(R[id[l]], R[id[r]]));
}
}

long long minimum_walk(vector<int> p, int s)


{
n = p.size();
x = y = s;
for(int i = 0; i < n; ++i) ans += abs(i - p[i]);
for(int i = 0; i < n; ++i) if(!check[i])
{
id[i] = ++ptr, check[i] = true;
L[ptr] = R[ptr] = i;
int u = i;
while(p[u] != i)
{
id[u = p[u]] = ptr;
check[u] = true;
R[ptr] = max(R[ptr], u);
}
if(i != p[i])
x = min(x, L[ptr]), y = max(y, R[ptr]);
}
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 390

int l = s, r = s;
while(x < l || r < y)
{
extend(l, r);
int al = l, ar = r;
int bl = l, br = r;
long long ca = 0, cb = 0;

while(x < al && ar == r) ca += 2, extend(--al, ar);

while(bl == l && br < y) cb += 2, extend(bl, ++br);

if(ar != r && bl != l) ans += min(ca, cb);


else ans += ca + cb;

l = min(al, bl), r = max(ar, br);


}
return ans;
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.953
t3-t2 = 0.172
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.6.7: books-152524.cpp


// https://oj.uz/submission/152524 171 ms 15048 KB
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 391

#include <bits/stdc++.h>
#include "books.h"

using namespace std;

long long minimum_walk(vector<int> p, int s)


{
int i = s;
int j = s-1;//incl-incl
int mi = s, ma=s;
int n = p.size();
long long cnt = 0, curl = 0, curr = 0;
int b = 0, e = n;
for(int k = 0; k<s&&p[k]==k; k++) b = k+1;
for(int k = n-1; k>s&&p[k]==k; k--) e = k;

while(i>b||j<e-1)
{
bool bol = false;
while(mi < i)
{
bol = true;
i--;
if(p[i]>s)
{
cnt += curl;
curl = 0;
curr = 0;
}
mi = min(mi, p[i]);
ma = max(ma, p[i]);
}

while(j<ma)
{
bol = true;
j++;
if(p[j]<s)
{
cnt+=curr;
curl = 0;
curr = 0;
}
mi = min(mi, p[j]);
ma = max(ma, p[j]);

if(!bol)
{
if(mi>b){mi--;curl+=2;}
if(ma<e-1){ma++;curr+=2;}
}

cnt+=curl+curr;
for(int ii = 0; ii<n; ii++)
{
cnt += abs(ii - p[ii]);
}

return cnt;
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 392

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.843
t3-t2 = 0.047
t4-t3 = 0

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


Press any key to continue.
*/

Listing 3.6.8: books-206638.cpp


// https://oj.uz/submission/206638 262 ms 31740 KB

#include <cassert>
#include<ctime>

#include "books.h"
#include<iostream>

#define MAXN 1000005


#define LL long long

using namespace std;

int N, S, cyc = 1, eL, eR, Lcur, Rcur;


int L[MAXN], R[MAXN], P[MAXN], C[MAXN];
bool vis[MAXN];
LL ans;

LL minimum_walk(vector<int> p, int s)
{
N = p.size(), eL = N+1, S = s+1, Lcur = S, Rcur = S;
for (int i=0; i<N; i++)
{
P[i+1]=p[i]+1; //1 index
ans += (LL) max(p[i] - i, i - p[i]);
}

for (int i=1; i<=N; i++)


{
int cur = P[i];
L[cyc] = 1<<30, R[cyc] = -1<<30;
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 393

while (!vis[cur])
{
L[cyc] = min(L[cyc], cur);
R[cyc] = max(R[cyc], cur);
C[cur] = cyc;
vis[cur] = true;
cur = P[cur];
}
cyc++;
}

for (int i=1; i<=N; i++)


{
if (P[i] != i)
{
eL = i;
break;
}
}

for (int i=N; i>=1; i--)


{
if (P[i] != i)
{
eR = i;
break;
}
}

while (Lcur > eL || Rcur < eR)


{
//expand left and right as much as frontier allows
int pt1 = Lcur, pt2 = Rcur; //[Lcur, Rcur] can be reached
while (pt1 >= Lcur || pt2 <= Rcur)
{
if (pt1 >= Lcur)
{
Lcur = min(Lcur, L[C[pt1]]);
Rcur = max(Rcur, R[C[pt1]]);
pt1--;
}
if (pt2 <= Rcur)
{
Lcur = min(Lcur, L[C[pt2]]);
Rcur = max(Rcur, R[C[pt2]]);
pt2++;
}
}

//if one boundary is reached


if (Lcur <= eL && Rcur >= eR)
{
break;
}
else if (Lcur <= eL)
{
ans+=2;
Rcur++;
}
else if (Rcur >= eR)
{
ans+=2;
Lcur--;
}
else
{
//test expand right Cost to go beyond
int tmpL = Lcur, tmpR = Rcur, ptL = Lcur, ptR = Rcur;
int Lcost = 0, Rcost = 0;
bool root = true;

//check R cost:
while (ptR <= eR)
{
if (ptR > tmpR) {tmpR = ptR, Rcost++;}
tmpR = max(tmpR, R[C[ptR]]);
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 394

if (L[C[ptR]] < Lcur)


{
root = false;
break;
}
ptR++;
}

//check L cost:
while (ptL >= eL)
{
if (ptL < tmpL) {tmpL = ptL, Lcost++;}
tmpL = min(tmpL, L[C[ptL]]);
if (R[C[ptL]] > Rcur)
{
root = false;
break;
}
ptL--;
}

if (root)
{
ans+=2*(Lcost + Rcost);
break;
}
else if (Lcost > Rcost)
{
Rcur = ptR, Lcur = L[C[ptR]];
ans+=2*Rcost;
}
else
{
Lcur = ptL, Rcur = R[C[ptL]];
ans+=2*Lcost;
}
}
}

return(ans);
}

// --------- grader ------------------------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/5-29.in", "r", stdin);


std::freopen("5-29.out.txt", "w", stdout);

string str;
getline (cin,str);
//cout<<str<<"\n";

int n, s;
assert(2 == scanf("%d %d", &n, &s));

vector<int> p((unsigned) n);


for(int i = 0; i < n; i++)
assert(1 == scanf("%d", &p[(unsigned) i]));

auto t2 = clock();

long long res = minimum_walk(p, s);

auto t3 = clock();

printf("%lld\n", res);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"res = "<<res<<"\n";
CAPITOLUL 3. IOI 2017 3.6. ANCIENT BOOKS 395

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
res = 3320522632
t2-t1 = 1.781
t3-t2 = 0.125
t4-t3 = 0

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


Press any key to continue.
*/

3.6.3 *Rezolvare detaliată


Capitolul 4
34
IOI 2016

4.1 Detecting molecules


Problema 1 - Detecting molecules 100 de puncte

Author: Shi-Chun Tsai (Taiwan)

Petru lucrează pentru o companie care a construit un dispozitiv pentru detectarea moleculelor.
Fiecare moleculă are o greutate pozitivă ı̂ntreagă. Dispozitivul are un interval de detectare l, u,
unde l şi u sunt numere ı̂ntregi, pozitive.
Dispozitivul poate detecta un set de molecule dacă şi numai dacă acest set conţine un subset
de molecule cu greutate totală aparţinând intervalului de detectare a dispozitivului.
Formal, considerăm n molecule cu greutăţi ı̂ntregi w0 , ..., wn1 . Detectarea se consideră reuşită
dacă există un set de indici distincţi I ri1 , ..., , im x astfel ı̂ncât

l & wi1  ...  wim & u.


Datorită specificului dispozitivului, decalajul ı̂ntre l şi u este garantat mai mare sau egal cu
decalajul de greutate dintre cea mai grea şi cea mai uşoară moleculă. Formal,

u  l ' wmax  wmin , unde wmax max w0 , ..., wn1 

şi
wmin min w0 , ..., wn1 .
Scrie un program care fie găseşte un subset de molecule cu greutate totală ı̂n intervalul de
detectare, fie determină că nu există un asemenea subset.
Detalii de implementare
Trebuie să implementezi funcţia (metoda):

int[] solve(int l, int u, int[] w)

` l şi u: punctele extreme ale intervalului de detectare,


` w: greutăţile moleculelor.
` dacă subsetul căutat există, funcţia trebuie să returneze un array de indici a moleculelor
care formează acest subset. Dacă există mai multe răspunsuri corecte, se va returna oricare
din ele.
` dacă subsetul căutat nu există, funcţia trebuie să returneze un array vid.

Pentru limbajul C signatura funcţiei este uşor diferită:

int solve(int l, int u, int[] w, int n, int[] result)

` n, numărul de elemente ı̂n w (altfel spus - numărul de molecule),


34
argint: Costin-Andrei Oncescu, Dinicu Golescu (Campulung),
. argint: Andrei-Costin Constantinescu, ICHB (Bucureşti),
. argint: Andrei Popa, Mihail Kogalniceanu (Vaslui),
. bronz: Radu Alexandru Muntean, Tudor Vianu (Bucureşti).

396
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 397

` ceilalţi parametri sunt identici celor descrişi anterior.


` ı̂n locul returnării unui array de m indici (ca ı̂n cazul descris anterior), funcţia trebuie să
ı̂nscrie indicii ı̂n primele m elemente din array-ul result iar apoi să returneze m.
` dacă subsetul căutat nu există, funcţia nu va scrie nimic ı̂n array-ul result şi va returna 0.

Programul poate scrie indicii ı̂n array-ul returnat (sau ı̂n array-ul result pentru limbajul C) ı̂n
oricare ordine.
Te rugăm să foloseşti fişierele şablon furnizate pentru detalii de implementare ı̂n limbajul de
programare pe care ı̂l foloseşti.
Exemple
Exemplul 1

solve(15, 17, [6, 8, 8, 7])

În acest exemplu avem patru molecule cu greutăţile 6, 8, 8 şi 7. Dispozitivul poate detecta
subseturi de molecule cu greutate totală cuprinsă ı̂ntre 15 şi 17, inclusiv. De remarcat, că 17 - 15
' 8 - 6 . Greutatea totală a moleculelor 1 şi 3 este w1 + w3 = 8 + 7 = 15, astfel funcţia poate
returna [1, 3]. Alte răspunsuri corecte posibile sunt [1, 2] ( w1 + w2 = 8 + 8 = 16 ) şi [2, 3] ( w2
+ w3 = 8 + 7 = 15 ).
Exemplul 2

solve(14, 15, [5, 5, 6, 6])

În acest exemplu avem patru molecule cu greutăţile 5, 5, 6 şi 6, şi căutăm un subset cu
greutatea totală ı̂ntre 14 şi 15, inclusiv. La fel, vom remarca 15 - 14 ' 6 - 5. Nu există un subset
de molecule cu greutate totală ı̂ntre 14 şi 15 astfel funcţia va returna un array vid.
Exemplul 3

solve(10, 20, [15, 17, 16, 18])

În acest exemplu avem patru molecule cu greutăţile 15, 17, 16 şi 18, şi căutăm un subset cu
greutatea totală ı̂ntre 10 şi 20, inclusiv. La fel, vom remarca 20 - 10 ' 18 - 15. Oricare subset
format din exact un element satisface cerinţele, astfel că răspunsuri corecte sunt: [0], [1], [2] şi [3].
Subtaskuri
1. (9 puncte): 1 & n & 100, 1 & wi & 100, 1 & u, l & 1000, toate wi sunt egale.
2. (10 puncte): 1 & n & 100, 1 & wi , u, l & 1000, şi
max w0 , ..., wn1   min w0 , ..., wn1  & 1
max w0 , ..., wn1   min w0 , ..., wn1  & 1.
3. (12 puncte): n & 100 şi wi , u, l & 1 000.
4. (15 puncte): n & 10 000 şi wi , u, l & 10 000 .
5. (23 puncte): n & 10 000 şi wi , u, l & 500 000
6. (31 puncte): n & 200 000 şi wi , u, l $ 2 .
31

Sample grader
Sample grader-ul citeşte inputul ı̂n următorul format:
` linia 1: numerele ı̂ntregi n, l, u.
` linia 2: n numere ı̂ntregi: w0 , ..., wn1

Timp maxim de executare/test: 1.0 secunde


Memorie: total 2048 MB

4.1.1 Indicaţii de rezolvare

Author: Shi-Chun Tsai


CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 398

National Chiao-Tnug University, sctsai@cs.nctu.edu.tw, country: Taiwan


Subtask 1 is special case, iterate all possible k & n, try to take exactly k molecules.
Subtask 2 is special case, iterate all possible k1 , k2 : k1  k2 & n, try to take exactly k1
molecules of minimal weight and exactly k2 molecules of maximal weight.
Subtasks 3 and 4 may be solved using dynamic programming. The task is classic knapsack
problem. Use O n u of time, and O u of space.
You may optimize the dynamic programming, use bitset and you’ll pass Subtasks 5.
We suggested, contestant who solves Subtasks 5 and 6 will invent greedy approach.
2
If you implement greedy in O n  you’ll pass only Subtask 5.
Good time to pass Subtask 6 is O n log n.
There are 3 correct greedy solutions. All of them start with sorting the array of weights in
nondecreasing order.
Greedy 1. Let fix k, number of molecules to take. We can choose set of size k with sum in
& u and maxSumk & l. Where minSum[] is partial minimums on prefixes
l..r  if minSumk 
and maxSum[] is partial maximums on sufixes. Both of them may be precalculated in O n.
Proof. Lets take minimal possible k molecules, its summary weight does not exceed l. Lets
change the set smoothly from ”k minimal molecules” to ”k maximal molecules”. One step: drop
any one molecule, take any one another. Each step changes the sum by at most u  l.
The last value of sum is at least l. So one of intermediate steps gives l & sum & u. q.e.d.
Greedy 2. There exists an answer which forms segment.
Use two pointers to find it in O n.
Proof. Lets fix k - number of molecules in the answer. The smallest k molecules form the
leftest segment, the biggest k form the rightest segment. Lets change the set smoothly from ”the
leftest segment” to ”the rightest segment”. One step: drop the leftest molecule, add new one at
the right. q.e.d.
Greedy 3. There exists an answer which forms union of prefix and sufix.
Use two pointers to find it in O n.
Proof. Lets fix k - number of molecules in the answer. The smallest k molecules form prefix,
the biggest k form sufix. Lets change the set smoothly from ”prefix” to ”sufix”. One step: make
prefix shorter by one, make sufix longer by k. q.e.d.

4.1.2 Coduri sursă

Listing 4.1.1: molecules sk.cpp


/** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */

#include <cstdio>
#include <algorithm>
#include <vector>

#include<ctime>
#include<iostream>

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); i++)

typedef pair <int, int> pii;


typedef long long ll;

// return value: result if solution exists and empty vector otherwise


vector<int> find_subset( int l, int u, vector<int> w )
{
int n = w.size();
vector<pii> wp(n);
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 399

forn(i, n)
wp[i] = pii(w[i], i);
sort(wp.begin(), wp.end());
vector<ll> mi(n + 1), ma(n + 1);

forn(i, n)
{
mi[i + 1] = mi[i] + wp[i].first;
ma[i + 1] = ma[i] + wp[n - i - 1].first;
}

forn(i, n + 1)
if (mi[i] <= u && ma[i] >= l)
{
int pos = n - 1;
ll sum = mi[i];
vector<int> result(i);
forn(j, i)
{
while (pos >= i && sum + wp[pos].first - wp[j].first > u)
pos--;
if (pos >= i && sum + wp[pos].first - wp[j].first <= u)
sum += wp[pos].first - wp[j].first, result[j]
= wp[pos--].second;
else
result[j] = wp[j].second;
}
return result;
}

return vector<int>();
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 400

result.size() = 1000

t2-t1 = 0.39
t3-t2 = 0.297
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.1.2: molecules sk greedy 1 n.cpp


/** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */
// idea : answer is a segment of sorted weights
// time = O(n)

#include <cstdio>
#include <algorithm>
#include <vector>

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); i++)

typedef pair <int, int> pii;


typedef long long ll;

// return value: result if solution exists and empty vector otherwise


vector<int> find_subset( int L, int U, vector<int> w )
{
int n = w.size();
vector<pii> wp(n);
forn(i, n)
wp[i] = pii(w[i], i);
sort(wp.begin(), wp.end());
int r = 0;
ll sum = 0;
forn(l, n)
{
while (r < n && sum < L)
sum += wp[r++].first;
if (L <= sum && sum <= U)
{
vector<int> res;
for (; l < r; l++)
res.push_back(wp[l].second);
return res;
}
sum -= wp[l].first;
}
return vector<int>();
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);


CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 401

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.359
t3-t2 = 0.281
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.1.3: molecules sk greedy 2 n.cpp


/** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */
// idea : answer is a prefix + a suffix of sorted weights
// time = O(n)

#include <cstdio>
#include <algorithm>
#include <vector>

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); i++)

typedef pair <int, int> pii;


typedef long long ll;

// return value: result if solution exists and empty vector otherwise


vector<int> find_subset( int L, int U, vector<int> w )
{
int n = w.size();
vector<pii> wp(n);
ll sum = 0;
forn(i, n)
wp[i] = pii(w[i], i), sum += w[i];
sort(wp.begin(), wp.end());

int r = n;
for (int l = n; l >= 0; l--)
{
while (r > l && sum < L)
sum += wp[--r].first;
if (L <= sum && sum <= U)
{
vector<int> res;
while (l--)
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 402

res.push_back(wp[l].second);
while (r < n)
res.push_back(wp[r++].second);
return res;
}
if (l)
sum -= wp[l - 1].first;
}
return vector<int>();
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.375
t3-t2 = 0.281
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.1.4: molecules sk greedy 3 ok.cpp


/** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */
// idea : answer is a segment of sorted weights of length in floor(k0)..ceil(k0)
// time = O(n)

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <numeric> // acumulate
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 403

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;

#define forn(i, n) for (int i = 0; i < (int)(n); i++)


#define forab(i, a, b) for (int i = (a); i <= (b); i++)

typedef pair <int, int> pii;


typedef long long ll;

// return value: result if solution exists and empty vector otherwise


vector<int> find_subset( int L, int U, vector<int> w )
{
int n = w.size();
vector<pii> wp(n);
forn(i, n)
wp[i] = pii(w[i], i);

sort(wp.begin(), wp.end());

double k0 = L / (accumulate(w.begin(), w.end(), 0.0) / n);

vector<ll> pref(n + 1);


forn(i, n)
pref[i + 1] = pref[i] + wp[i].first;
forab(k, (int)floor(k0), (int)ceil(k0))
if (1 <= k && k <= n)
forn(l, n - k + 1)
{
ll sum = pref[l + k] - pref[l];
if (L <= sum && sum <= U)
{
vector<int> res;
forn(i, k)
res.push_back(wp[l + i].second);
return res;
}
}
return vector<int>();
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 404

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.359
t3-t2 = 0.297
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.1.5: molecules-20751.cpp


// https://oj.uz/submission/20751

#include "molecules.h"
#include <stdio.h>
#include <algorithm>

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;

struct point
{
int a, num;
bool operator<(const point &p)const
{
return a < p.a;
}
} P[201000];

std::vector<int> find_subset(int l, int u, std::vector<int> w)


{
int i, n = w.size(), j, k;
long long S1 = 0, S2 = 0;
for(i=0;i<n;i++)P[i].num=i,P[i].a=w[i];

sort(P,P+n);

vector<int>res;
for(i=0;i<n;i++)
{
S1 += P[i].a;
S2 += P[n-i-1].a;
if(S1 <= u && S2 >= l)
{
if(S1 >= l)
{
for(j=0;j<=i;j++)res.push_back(P[j].num);
return res;
}

for(j=i+1;j<n;j++)
{
S1 += P[j].a;
S1 -= P[j-i-1].a;
if(S1 >= l)
{
for(k=j-i;k<=j;k++)res.push_back(P[k].num);
return res;
}
}
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 405

}
}

return res;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.359
t3-t2 = 0.109
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.1.6: molecules-72936.cpp


// https://oj.uz/submission/72936

#include <algorithm>
#include "molecules.h"

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;

typedef long long int ll;

std::vector<int> find_subset(int l, int u, std::vector<int> w)


CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 406

{
vector<int> V;
ll N=w.size();
ll S=0;
vector<pair<ll,int>> W;

for(ll i=0;i<N;i++)
W.push_back({w[i],i});

sort(W.begin(), W.end());

for(ll a=0,b=0;a<N;a++)
{
S-=a?W[a-1].first:0;
while(l>S and b<N)
S+=W[b++].first;
if(l<=S and S<=u)
{
for(ll i=a;i<b;i++)
V.push_back(W[i].second);
return V;
}
}

return V;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.343
t3-t2 = 0.313
t4-t3 = 0
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 407

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


Press any key to continue.
*/

Listing 4.1.7: molecules-93472.cpp


// https://oj.uz/submission/93472 52 ms 7164 KB

#include "molecules.h"
#include <bits/stdc++.h>

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;


using ll = long long;
using pii = pair<ll,int>;

vector<int> res;
vector<pii> v;

vector<int> find_subset(int lw, int u, vector<int> w)


{
v.reserve(w.size());

for(int i=0;i<w.size();i++)
{
v.emplace_back(w[i],i);
}

sort(v.begin(),v.end());

int l = 0;
ll sum = 0;
for(int r = 0;r < v.size();r++)
{
sum += v[r].first;
while(sum > u)
{
sum -= v[l].first;
l++;
}
//cout<<l<<" - "<<r<<": "<<sum<<endl;
if(sum >= lw)
{
for(int i = l;i<=r;i++) res.push_back(v[i].second);
return res;
}
}

return vector<int>();
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 408

puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.359
t3-t2 = 0.312
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.1.8: molecules-114550.cpp


// https://oj.uz/submission/114550

#include "molecules.h"
#include <bits/stdc++.h>

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;


typedef long long ll;

const int MAXN=2e5+5;


pair<ll, int> v[MAXN];
vector<int> resp;
ll L, R;
int n;

vector<int> find_subset(int u, int l, vector<int> w)


{
L=u; R=l;
int n=w.size();
for(int i=1; i<=n; i++) v[i]={w[i-1], i-1};
sort(v+1, v+1+n);

int ind=1;
ll soma=0;
for(int i=1; i<=n; i++)
{
soma+=v[i].first;
while(soma>R&&ind<i) soma-=v[ind++].first;
if(L<=soma&&soma<=R)
{
for(int j=ind; j<=i; j++)
resp.push_back(v[j].second);
break;
}
}

if(resp.size()) sort(resp.begin(), resp.end());


return resp;
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 409

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.359
t3-t2 = 0.203
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.1.9: molecules-159299.cpp


// https://oj.uz/submission/159299

#include<bits/stdc++.h>

#include <fstream>
#include<iostream>
#include<ctime>

using namespace std;

vector<pair<int, int> >v;

vector<int> find_subset(int l, int u, vector<int>w)


{
int n=w.size(); v.resize(n);
for(int i=0; i<n; i++) v[i]={w[i], i};

sort(v.begin(), v.end());
CAPITOLUL 4. IOI 2016 4.1. DETECTING MOLECULES 410

int idx=0;
long long sum=0ll;
for(int i=0; i<n; i++)
{
for(; idx<n && sum<l; idx++)
sum+=v[idx].first;

if(sum>=l && sum<=u)


{
vector<int>res;
for(int j=i; j<idx; j++)
res.push_back(v[j].second);

sort(res.begin(), res.end());

return res;
}

sum-=v[i].first;
}
return vector<int>();
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/108", "r", stdin);


std::freopen("6-108.out.txt", "w", stdout);

int n, l, u;
scanf("%d %d %d", &n, &l, &u);

std::vector<int> w(n);
for (int i = 0; i < n; i++)scanf("%d", &w[i]);

auto t2 = clock();

std::vector<int> result = find_subset(l, u, w);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%d\n", (int)result.size());
for (int x : result) printf("%d ", x);
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"result.size() = "<<(int)result.size()<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
result.size() = 1000

t2-t1 = 0.343
t3-t2 = 0.282
t4-t3 = 0

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


Press any key to continue.
*/
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 411

4.1.3 *Rezolvare detaliată

4.2 Roller Coaster Railroad


Problema 2 - Roller Coaster Railroad 100 de puncte

Author: Kento Nikaido (Japan)

Anna lucrează la un parc de distracţii, iar acum coordonează construcţia unui nou roller-
coaster. Ea a proiectat deja n sectoare speciale (indexate convenabil de la 0 la n  1) care
afectează viteza trenului: sectoare ascendente, sectoare de frânare şi altele. Acum trebuie să
asambleze toate aceste sectoare şi să propună o versiune finală a roller-coaster-ului. ı̂n această
problemă, vom considera că trenul este punctiform (are lungime egală cu 0).
Pentru fiecare i ı̂ntre 0 şi n  1 inclusiv, cel de al i-lea sector special are două proprietăţi:
` atunci când trenul intră ı̂n acest sector, viteza sa trebuie să fie mai mică sau egală cu si
km/h.
` atunci când părăseşte acest sector, viteza trenului va fi exact ti km/h, indiferent de viteza
cu care trenul a intrat ı̂n acest sector.

Roller-coaster-ul va fi asamblat din aceste n sectoare speciale, care vor fi aşezate ı̂ntr-o anumită
ordine. Fiecare din cele n sectoare speciale trebuie să apară exact o dată ı̂n roller-coaster. Mai
mult, trebuie să existe un segment de cale ferată ı̂ntre oricare două sectoare consecutive. Anna
trebuie să stabilească ordinea celor n sectoare, iar apoi să decidă lungimea fiecărui segment de cale
ferată. Lungimea unui segment este măsurată ı̂n metri şi poate fi egală cu orice număr natural
nenegativ (inclusiv 0).
Fiecare metru de cale ferată dintre două sectoare speciale ı̂ncetineşte trenul cu 1 km/h. La
ı̂nceputul călătoriei trenul ı̂ntră ı̂n primul sector special (ı̂n ordinea aleasă de Anna) cu viteza de
1 km/h.
Versiunea finală a roller-coaster-ului trebuie să respecte următoarele cerinţe: trenul nu ı̂ncalcă
nicio limită de viteză atunci când intră ı̂n sectoarele speciale. viteza trenului rămâne permanent
pozitivă.
În toate subtask-urile, cu excepţia subtaskului 3, sarcina voastră este să găsiţi o ordonare
a celor n sectoare speciale şi să decideţi lungimile segmentelor de cale ferate dintre sectoarele
consecutive, asftel ı̂ncât lungimea totală a segmentelor de cale ferată să fie minimă. În subtask-
ul 3 trebuie doar să verificaţi dacă există o ordonare a sectoarelor speciale astfel ı̂ncât această
lungime totală să fie egală cu zero.
Detalii de Implementare
Trebuie să implementaţi următoarea funcţie (metodă):

int64 plan_roller_coaster(int[] s, int[] t).

` s: şir de lungime n, vitezele maxime de intrare.


` t: şir de lungime n, vitezele de ieşire.
` În toate subtask-urile, cu excepţia subtask-ului 3, funcţia trebuie să ı̂ntoarcă lungimea totală
minimă posibilă a segmentelor de cale ferată. În subtask-ul 3, dacă există o ordonare a
sectoarelor astfel ı̂ncât această lungime totală să fie egală cu 0, funcţia trebuie să ı̂ntoarcă
0. Altfel, poate ı̂ntoarce orice număr natural pozitiv.

Pentru limbajulC, prototipul funcţiei este uşor diferit:

int64 plan_roller_coaster(int n, int[] s, int[] t).

` n: numărul de elemente din s şi t (i.e., numărul de sectoare speciale), ceilalţi parametri sunt
identici cu cei precizaţi mai sus.

Exemple
Exemplul 1
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 412

plan_roller_coaster([1, 4, 5, 6], [7, 3, 8, 6])

În acest exemplu există patru sectoare speciale. Cea mai bună soluţie este să le construim ı̂n
ordinea 0, 3, 1, 2 şi să le conectăm cu segmente de lungime 1, 2, 0.
Trenul va călători astfel:
` Iniţial, viteza trenului este de 1 km/h.
` Trenul ı̂ncepe cursa intrând ı̂n sectorul special cu numărul 0.
` Trenul părăseşte sectorul 0 cu o viteză de 7 km/h.
` Urmează apoi un segment de lungime egală cu 1 metru. Când trenul ajunge la finalul acestui
segment, viteza sa este 6 km/h.
` Trenul intră ı̂n sectorul cu numărul 3 cu o viteză de 6 km/h şi ı̂l părăseşte cu aceeaşi viteză.
` După ce părăseşte sectorul 3, trenul călătoreşte de-a lungul unui segment de 2 metri. Viteza
sa scade la 4 km/h.
` Trenul intră apoi ı̂n sectorul 1 cu o viteză de 4 km/h şi ı̂l părăseşte cu o viteză de 3 km/h.
` Imediat după sectorul special 1, trenul intră ı̂n sectorul special cu numărul 2.
` Trenul părăseşte sectorul cu numărul 2. Viteza sa finală este de 8 km/h.

Funcţia trebuie să ı̂ntoarcă lungimea totală a segmentelor de cale ferată:1  2  0 3.


Subtaskuri
În toate subtask-urile 1 & si & 10 şi 1 & ti & 10 .
9 9

1. (11 puncte): 2 & n & 8,


2. (23 de puncte): 2 & n & 16,
3. (30 de puncte): 2 & n & 200 000. ı̂n acest subtask programul vostru trebuie doar să verifice
dacă răspunsul este egal cu 0. Dacă răspunsul nu este 0, orice număr natural pozitiv este
considerat corect.
4. (36 de puncte): 2 & n & 200 000.

Sample grader
Sample grader-ul citeşte datele ı̂n următorul format:
` linia 1: număr ı̂ntreg n.
` linia 2  i, pentru i ı̂ntre 0 şi n  1: numerele ı̂ntregi si şi ti .

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2048 MB

4.2.1 Indicaţii de rezolvare

Author: Kento Nikaido


Keio University, snukent@gmail.com, Japan
Subtask 1. To solve the first subtask, one could iterate over all n! possible permutations of
the special sections. Once the permutation is fixed, the only thing left is to compute the required
th
railway segment length between every two consecutive sections: if the i section is followed by
th
the j one, then the required length is max 0; ti  sj .
Subtask 2. One could use a standard dynamic programming approach to solve the second
n
subtask. Every subset of the given sections set can be encoded as a bit mask (from 0 to 2  1).
Let ansmask i denote the minimum total railway length, considering only the sections from
th
the set encoded by mask with an additional restriction that the i special section should go first.
i
1. If mask 2 (there is only one special section), the answer is clearly zero.
th
2. Otherwise, there must be a section j following the i one, so
i
ansmask i min ansmask  2   max 0; ti  sj 
j ji
j "mask
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 413

n
The answer to the problem is mini ans2  1i. One could also notice that this subtask
is an instance of the well-known TSP (travelling salesman problem), where the special sections
represent the cities, and the distance function is the required railway segment length.
Subtask 3. Let’s add an additional special section with s ™ and t 1: now we can
introduce a restriction that the train must have speed exactly 1 km/h at the end of the journey
(here ™ represents any number greater or equal than any of the numbers given in the input data
9
- 10 would sufice).

Consider an infinite graph, with a vertex set 1; 2; 3; ... and there is an edge from si to ti
for every section i (including the added one). For every positive integer x consider the balance
value: (number of edges going over the segment x, x  1 from left to right) minus (number of
edges going over the segment x, x  1 from right to left). In the picture above one can see three
edges going from left to right (red) and two going in the opposite direction (green), so the balance
is 1.
If one aims to start from 1 km/h and end with the same speed, then the train must cross the
segment x; x  1 equal number of times in both directions. If the balance is positive then it’s
necessary to add an additional green edge to slow down the train, so at least one railway segment
is required and the answer is not zero. If the balance is negative it just means that the train needs
to be accelerated at some point, so one can add as many additional red edges as needed for free.
Once the balance equals zero for every x, it is suficient to check whether the resulting graph
is connected or not. If the graph is not connected, than the answer is clearly not zero: to go from
one component to the other it’s needed to slow down the train at least once, so an additional
railway segment is required. If the graph is connected, then, since all the balances are zero, for
every vertex x its in-degree equals its degree!out-degree, and thus there exists an Euler cycle in
this graph, from which one could construct a valid sections arrangement.
To do this eficiently, one need to consider only the ”interesting” values of x, which are
given in the input data, and instead of considering segments x; x  1 one should consider
interestingi ; interestingi1 .

Subtask 4. The solution for the last subtask naturally emerges from the previous one. If the
balance is positive for some x interestingi , it is required to add additional green edges until
the balance is restored, and every green edge corresponds to a railway segment. Thus, for every
x interestingi one needs to add max 0; balance length to the answer, where length stands for
the distance to the next interesting point (interestingi1  interestingi ).
The last piece is to make the graph connected. In case the balance is zero we can connect
interestingi and interestingi1 with two edges in both directions, paying the length of this seg-
ment. Now we need to solve an instance of the well-know MST (minimum spanning tree) problem.

4.2.2 Coduri sursă

Listing 4.2.1: railroad mp nlogn.cpp


#include <cstdio>
#include <vector>
#include <algorithm>

#include <fstream>
#include<iostream>
#include<ctime>
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 414

using namespace std;

#define mp make_pair
#define pb push_back
#define fs first
#define sc second

typedef long long int64;

const int INF = (int) 1e9;

int dsu_get(vector<int>& p, int u)


{
return (u == p[u]) ? u : (p[u] = dsu_get(p, p[u]));
}

bool dsu_union(vector<int>& p, int u, int v)


{
u = dsu_get(p, u), v = dsu_get(p, v);
p[u] = v;
return (u != v);
}

int64 plan_roller_coaster(vector<int> s, vector<int> t)


{
int n = (int) s.size();
vector< pair< int, pair< int, int > > > e, edges;

for (int i = 0; i < n; ++i)


{
e.pb(mp(s[i], mp(1, i)));
e.pb(mp(t[i], mp(-1, i)));
}

e.pb(mp(INF, mp(1, n)));


e.pb(mp(1, mp(-1, n)));
n++;
vector<int> p(n);

for (int i = 0; i < n; ++i)


{
p[i] = i;
}

sort(e.begin(), e.end());

int64 res = 0;
for (int i = 0, delta = 0; i + 1 < (int) e.size(); ++i)
{
delta += e[i].sc.fs;
res += max(0, delta) * (int64) (e[i + 1].fs - e[i].fs);
edges.pb(mp(e[i + 1].fs - e[i].fs, mp(e[i].sc.sc, e[i + 1].sc.sc)));
if ((e[i + 1].fs == e[i].fs) || (delta != 0))
dsu_union(p, e[i].sc.sc, e[i + 1].sc.sc);
}

sort(edges.begin(), edges.end());

for (int i = 0; i < (int) edges.size(); ++i)


if (dsu_union(p, edges[i].sc.fs, edges[i].sc.sc))
res += edges[i].fs;

return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/092", "r", stdin);


std::freopen("4-092.out.txt", "w", stdout);

int n, need_answer;

scanf("%d", &n);
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 415

std::vector<int> s(n), t(n);

for (int i = 0; i < n; ++i)


scanf("%d%d", &s[i], &t[i]);

auto t2 = clock();

long long ans = plan_roller_coaster(s, t);

auto t3 = clock();

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"ans = "<<ans<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
ans = 309419403

t2-t1 = 0.703
t3-t2 = 1.563
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.2.2: railroad-103076.cpp


// https://oj.uz/submission/103076

#include "railroad.h"
#include <bits/stdc++.h>

#define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);


#define pb push_back
#define MP make_pair
#define F first
#define S second
#define ET cout << "\n"
#define MEM(i,j) memset(i,j,sizeof i)
#define ALL(v) v.begin(),v.end()
#define DB(a,s,e) {for(int i=s;i<e;++i) cerr << a[i] << " ";ET;}

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

struct mode
{
ll p,v,i;
bool operator < (const mode&x) const
{
return p < x.p;
}
};

struct edge
{
ll a,b,w;
bool operator < (const edge&x) const
{
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 416

return w<x.w;
}
};

ll boss[200005];

ll finds(ll a)
{
if(a==boss[a]) return a;
return boss[a]=finds(boss[a]);
}

void Union(ll a,ll b)


{
a=finds(a),b=finds(b);
if(a==b) return;
boss[a]=b;
}

ll plan_roller_coaster(vector<int> s,vector<int> t)
{
ll n=s.size()+1,ans=0,nw=0;
vector<mode> v;
vector<pii> interesting;
vector<edge> e;
s.pb(1000000001),t.pb(1);

for(int i=0;i<n;++i)
boss[i]=i,v.pb(mode{s[i],1,i}),v.pb(mode{t[i],-1,i});

sort(ALL(v));

for(int i=0,t=0;i+1<v.size();)
{
interesting.pb(MP(v[i].p,v[i].i));
while(t<v.size()&&v[i].p==v[t].p)
nw+=v[t].v,++t;
if(nw>0)
ans+=nw*(v[t].p-v[i].p);
if(nw!=0)
Union(v[t].i,v[i].i);
for(++i;i<t;++i)
Union(v[i-1].i,v[i].i);
}

for(int i=0;i+1<interesting.size();++i)
if(finds(interesting[i].S)!=finds(interesting[i+1].S))
e.pb(edge{interesting[i].S,
interesting[i+1].S,
interesting[i+1].F-interesting[i].F});
sort(ALL(e));

for(auto i:e)
if(finds(i.a)!=finds(i.b))
Union(i.a,i.b),ans+=i.w;

return ans;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/092", "r", stdin);


std::freopen("4-092.out.txt", "w", stdout);

int n, need_answer;

scanf("%d", &n);

std::vector<int> s(n), t(n);

for (int i = 0; i < n; ++i)


scanf("%d%d", &s[i], &t[i]);

auto t2 = clock();
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 417

long long ans = plan_roller_coaster(s, t);

auto t3 = clock();

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"ans = "<<ans<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
ans = 309419403

t2-t1 = 0.745
t3-t2 = 0.893
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.2.3: railroad-117891.cpp


// https://oj.uz/submission/117891

#include "railroad.h"
#include <bits/stdc++.h>

using namespace std;

#define N 200005
#define ff first
#define ss second

vector<pair<int, pair<int, int> > > f;


int p[N];

int find(int v)
{
return (v == p[v] ? v : p[v] = find(p[v]));
}

bool unite(int v, int u)


{
v = find(v);
u = find(u);
if(v == u) return 0;
p[v] = u;
return 1;
}

map<int, int> sw;


map<int, vector<int> > in;

long long plan_roller_coaster(std::vector<int> s, std::vector<int> t)


{
int n = s.size();
s.push_back(1000000001);
t.push_back(1);

for(int i = 0; i <= n; i++)


{
sw[s[i]]++;
p[i] = i;
sw[t[i]]--;
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 418

in[s[i]].push_back(i);
in[t[i]].push_back(i);
}

long long res = 0;


int bal = 0, last = 0, lastid = n;

for(auto x : sw)
{
if(bal > 0)
{
res += 1LL * bal * (x.ff - last);
}
for(int y : in[x.ff])
{
f.push_back({ bal == 0 ? x.ff - last : 0, {y, lastid}});
lastid = y;
last = x.ff;
}

bal += x.ss;
}

sort(f.begin(), f.end());

for(auto x : f)
res += x.ff * unite(x.ss.ff, x.ss.ss);

return res;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/092", "r", stdin);


std::freopen("4-092.out.txt", "w", stdout);

int n, need_answer;

scanf("%d", &n);

std::vector<int> s(n), t(n);

for (int i = 0; i < n; ++i)


scanf("%d%d", &s[i], &t[i]);

auto t2 = clock();

long long ans = plan_roller_coaster(s, t);

auto t3 = clock();

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"ans = "<<ans<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
ans = 309419403

t2-t1 = 0.794
t3-t2 = 3.261
t4-t3 = 0

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


CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 419

Press any key to continue.


*/

Listing 4.2.4: railroad-135900.cpp


// https://oj.uz/submission/135900

#include<bits/stdc++.h>

#define x first
#define y second

using namespace std;

typedef long long ll;


const int N = 200005;
int P[N];

int Find(int v)
{
return (P[v] < 0 ? v : (P[v] = Find(P[v])));
}

inline bool Merge(int v, int u)


{
v = Find(v);
u = Find(u);
if (v == u)
return 0;
P[v] = u;
return 1;
}

int64_t plan_roller_coaster(vector < int > S, vector < int > T)


{
int n = (int)S.size();
vector < pair < int , pair < int , int > > > A, E;
for (int i = 0; i < n; i ++)
{
A.push_back({S[i], {1, i}});
A.push_back({T[i], {-1, i}});
}
A.push_back({(int)1e9, {1, n}});
A.push_back({1, {-1, n ++}});

sort(A.begin(), A.end());

int SM = 0;
ll tot = 0;
memset(P, -1, sizeof(P));

for (int i = 0; i + 1 < (int)A.size(); i ++)


{
SM += A[i].y.x;
tot += max(SM, 0) * (ll)(A[i + 1].x - A[i].x);
E.push_back({A[i + 1].x - A[i].x, {A[i].y.y, A[i + 1].y.y}});
if (A[i].x == A[i + 1].x || SM)
Merge(A[i].y.y, A[i + 1].y.y);
}

sort(E.begin(), E.end());

for (auto e : E)
if (Merge(e.y.x, e.y.y))
tot += e.x;

return (tot);
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/092", "r", stdin);


std::freopen("4-092.out.txt", "w", stdout);
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 420

int n, need_answer;

scanf("%d", &n);

std::vector<int> s(n), t(n);

for (int i = 0; i < n; ++i)


scanf("%d%d", &s[i], &t[i]);

auto t2 = clock();

long long ans = plan_roller_coaster(s, t);

auto t3 = clock();

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"ans = "<<ans<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
ans = 309419403

t2-t1 = 0.721
t3-t2 = 1.47
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.2.5: railroad-223610.cpp


// https://oj.uz/submission/223610

#include "railroad.h"
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

///Disjoint Sets
///Sweep Line
///Set the balance always to 0, connecting special sections

int link[200005];
int setSize[200005];
int Find(int u)
{
if(link[u]==u) return u;
else return link[u]=Find(link[u]);
}

bool same(int u,int v)


{
return Find(u)==Find(v);
}

void unite(int u,int v)


{
u=Find(u);
v=Find(v);
if(u==v) return;
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 421

if(setSize[v]>setSize[u]) swap(u,v);
setSize[u]+=setSize[v];
link[v]=u;
}

void initDSU(int n)
{
for(int i=1;i<=n;i++)
{
setSize[i]=1;
link[i]=i;
}
}

struct event
{
int T;
int op;
int id; //Which special section
bool operator<(const event& a)
{
if(T<a.T)
return true;
else
{
if(T==a.T)
{
if(op<a.op) return true;
else return false;
}
else return false;
}
}
};

struct segment
{
ll length;
int a,b; //ID where it starts and ends
bool operator < (const segment& s)
{
return length<s.length;
}
};

vector<event> timeline;
vector<segment> remaining; //Not taken because its balance was 0

long long plan_roller_coaster(vector<int> s, vector<int> t)


{
int n=s.size();
ll cost=0;
initDSU(n);
for(int i=0;i<n;i++)
{
event e={s[i],+1,i+1};
timeline.push_back(e);
e={t[i],-1,i+1};
timeline.push_back(e);
}

sort(timeline.begin(),timeline.end());

//Lines to the right are +1


//Lines to the left are -1
ll balance=-1; //Because there is a line that goes from inf to 1

for(int i=0;i<2*n-1;i++)
{
balance+=timeline[i].op;
if(balance<0)
{
//Add a track from left to right (free) and connect i with i+1
unite(timeline[i].id,timeline[i+1].id);
}
else
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 422

if(balance>0)
{
//We need to slow down the train so we add "balance" tracks
cost+=(ll)(timeline[i+1].T-timeline[i].T)*balance;

//With length: distance between that two events


unite(timeline[i].id,timeline[i+1].id);
}
else
if(balance==0)
{
segment s;
s.length=(timeline[i+1].T-timeline[i].T);
s.a=timeline[i].id;
s.b=timeline[i+1].id;
remaining.push_back(s);
}
}

sort(remaining.begin(),remaining.end());

set<int> R; //Roots
for(int i=1;i<=n;i++)
{
R.insert(Find(i));
}

int roots=R.size();
int i=0;
while(roots>1)
{ //Not fully connected
int a=remaining[i].a;
int b=remaining[i].b;
if(!same(a,b))
{
unite(a,b);
cost+=remaining[i].length;
roots--;
}
i++;
}

return cost;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/092", "r", stdin);


std::freopen("4-092.out.txt", "w", stdout);

int n, need_answer;

scanf("%d", &n);

std::vector<int> s(n), t(n);

for (int i = 0; i < n; ++i)


scanf("%d%d", &s[i], &t[i]);

auto t2 = clock();

long long ans = plan_roller_coaster(s, t);

auto t3 = clock();

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"ans = "<<ans<<"\n\n";
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 423

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
ans = 309419403

t2-t1 = 0.69
t3-t2 = 1.037
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.2.6: railroad-233098.cpp


// https://oj.uz/submission/233098

#include <algorithm>
#include <vector>

#include<ctime> // clock()
#include<cstdint> // int64_t
#include<fstream> // freeopen
#include<iostream> // cout

using namespace std;

#define rep(i, a, b) for (auto i = (a); i < (b); ++i)


#define trav(x, v) for (auto &x : v)
#define all(x) begin(x), end(x)
#define lb(x...) lower_bound(x)
#define sz(x) int((x).size())
#define eb(x...) emplace_back(x)

const int inf = 2e9;


using ll = int64_t;
using vi = vector<int>;

vi nxt;

int head(int u)
{
return nxt[u] != -1 ? nxt[u] = head(nxt[u]) : u;
}

bool unite(int u, int v)


{
u = head(u); v = head(v);
if (u == v) return false;
nxt[v] = u; return true;
}

ll plan_roller_coaster(vi s, vi t)
{
s.eb(inf); t.eb(1); int n = sz(s);
vi p = s; p.insert(end(p), all(t));

sort(all(p)); p.erase(unique(all(p)), end(p));

int m = sz(p); nxt.resize(m, -1);

vi sum(m--), e;
rep(i, 0, n)
{
int l = lb(all(p), s[i]) - begin(p), r = lb(all(p),t[i])-begin(p);
unite(l, r); ++sum[l]; --sum[r];
}
rep(i, 0, m) sum[i + 1] += sum[i];

ll ans = 0;
rep(i, 0, m)
CAPITOLUL 4. IOI 2016 4.2. ROLLER COASTER RAILROAD 424

{
if (sum[i])
{
unite(i, i + 1);
if (sum[i] > 0)
ans += 1ll * sum[i] * (p[i + 1] - p[i]);
}
else e.eb(i);
}

sort(all(e), [&p](int u, int v)


{ return p[u + 1] - p[u] < p[v + 1] - p[v]; });

trav(u, e)
if (unite(u, u + 1))
ans += p[u + 1] - p[u];

return ans;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/092", "r", stdin);


std::freopen("4-092.out.txt", "w", stdout);

int n, need_answer;

scanf("%d", &n);

std::vector<int> s(n), t(n);

for (int i = 0; i < n; ++i)


scanf("%d%d", &s[i], &t[i]);

auto t2 = clock();

long long ans = plan_roller_coaster(s, t);

auto t3 = clock();

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"ans = "<<ans<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
ans = 309419403

t2-t1 = 0.739
t3-t2 = 1.324
t4-t3 = 0

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


Press any key to continue.
*/
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 425

4.2.3 *Rezolvare detaliată

4.3 Shortcut
Problema 3 - Shortcut 100 de puncte

Author: Gleb Evstropov (Russia)

Pavel are un joc cu trenuleţe. Acesta este foarte simplu. Există o singură linie principală
constând ı̂n n staţii consecutive, numerotate ı̂n ordine, de-a lungul liniei, cu numere de la 0 la
n  1. Staţiile şi sunt situate la capetele liniei principale. Distanţa dintre staţiile i şi i  1 este de
li centimetri (0 & i & n  1).
În afară de linia principală pot exista şi linii secundare. Fiecare linie secundară este o linie
de cale fereată ı̂ntre o staţie de pe linia principală şi o nouă staţie nesituată pe linia principală.
(Aceste staţii noi nu sunt numerotate.) Din fiecare staţie de pe linia principală poate pleca cel
mult o linie secundară. Lungimea liniei secundare care pleaca din staţia i este de di centimetri.
Dacă di 0 ı̂nseamnă că nu există linie secundară care pleacă din staţia i.

Pavel ı̂şi propune să construiască o scurtătură: o linie expres ı̂ntre două staţii (posibil vecine)
ale liniei principale. Linia expres va avea lungimea de exact c centimetri, indiferent care vor fi
cele două staţii pe care le va conecta.
Fiecare porţiune de cale ferată, inclusiv linia expres, poate fi parcursă ı̂n ambele sensuri.
Distanţa dintre două staţii este cea mai mică lungime a unui treseu care uneşte cele două staţii
de-a lungul căii ferate. Diametrul ı̂ntregii reţele de cale ferată este maximul distanţelor pentru
oricare pereche de staţii. Cu alte cuvinte, acesta este cel mai mic număr t, astfel ı̂ncât distanţa
ı̂ntre oricare două staţii este cel mult t.
Pavel doreşte să construiască linia expres astfel ı̂ncât diametrul reţelei rezultate să fie mini-
mizat.
Detalii de implementare
Trebuie să implementezi funcţia

int64 find_shortcut(int n, int[] l, int[] d, int c)

` n: numărul de staţii de pe linia principala,


` l: distanţele dintre staţiile de pe linia principală (vector de lungime n  1),
` d: lungimile liniilor secundare (vector de lungime n),
` c: lungimea liniei expres.
` funcţia trebuie să returneze cel mai mic diametru posibil al reţelei de cale ferată după
adăugarea liniei expres.

Folosiţi fişierele template oferite pentru detalii de implementare ı̂n limbajul vostru de progra-
mare.
Exemple
Exemplul 1
Pentru reţeaua de cale ferată de mai jos, graderul va face urmatorul apel:
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 426

find_shortcut(4, [10, 20, 20], [0, 40, 0, 30], 10)

Soluţia optimă este să se construiască linia expres ı̂ntre staţiile 1 şi 3, aşa cum este arătat ı̂n
figură.

Diametrul noii reţele de cale ferată este de 80 de centimetri, aşa că funcţia va trebui sa returneze
80.
Exemplul 2
Graderul face următorul apel:

find_shortcut(9, [10, 10, 10, 10, 10, 10, 10, 10],


[20, 0, 30, 0, 0, 40, 0, 40, 0], 30)

Soluţia optimă este să se conecteze staţiile 2 şi 7, caz ı̂n care diametrul este 110.
Exemplul 3
Graderul face următorul apel:

find_shortcut(4, [2, 2, 2], [1, 10, 10, 1], 1)

Soluţia optimă este să se conecteze staţiile 1 şi 2, reducând diametrul la 21.
Exemplul 4
Graderul face următorul apel:

find_shortcut(3, [1, 1], [1, 1, 1], 3)

Oricum am conecta două staţii cu o linie expres de lungime 3 nu se poate ı̂mbunătăţi diametrul
reţelei iniţiale de cale ferată care este 4.
Subtaskuri
Pentru toate subtaskurile 2 & n & 1 000 000, 1 & li & 109 , 0 & di & 109 , 1 & c & 109 .
1. (9 puncte) 2 & n & 10,
2. (14 puncte) 2 & n & 100,
3. (8 puncte) 2 & n & 250,
4. (7 puncte) 2 & n & 500,
5. (33 puncte) 2 & n & 3 000,
6. (22 puncte) 2 & n & 100 000,
7. (4 puncte) 2 & n & 300 000,
8. (3 puncte) 2 & n & 1 000 000.

Sample grader
Sample grader-ul citeşte date de intrare ı̂n următorul format:
` linia 1: numerele ı̂ntregi n şi c,
` linia 2: numerele ı̂ntregi l0 , l1 , ..., ln2 ,
` linia 3: numerele ı̂ntregi d0 , d1 , ..., dn1 .
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 427

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2048 MB

4.3.1 Indicaţii de rezolvare

Author: Gleb Evstropov


National Research University High School of Economy, glebshp@yandex.ru, country: Russia
Subtasks 1 and 2: try to connect every pair of stations on the main line and then find the
diameter by checking every pair of stations. In Subtask 2 you have to precompute the stations
coordinates along the main line xi = Pi1 j=0 lj . These values will be used in all solutions for the
other subtasks.
Subtasks 3 and 4: we can try to connect every pair of stations on the main line and then
find the resulting diameter. To find the diameter, we can write down the resulting cycle and then
iterate through it, keeping a pointer to the opposite position on the cycle (opposite in terms of
distance). We also can maintain two queues for each of the halves, keeping the furthest stations
in terms of cycle distance plus secondary line length. This will help us to compute the diameter
3
in O n time, making the solution run in O n  time in total. In subtask 3, we can use some data
structures instead of queues, making the solution run in O(n3 log(n)). However, both solutions
are technically complex.
Subtask 5: This subtask requires some clever ideas. First, we can do binary search on the
answer. Then, we have to somehow check if it’s possible to make the diameter less or equal to
some fixed value k. Let’s write some inequations.
Let’s allow the express line start and end not only on the stations, but also on arbitrary points
y and z on the main line. We use the same coordinate system that was used to compute xi.

If we take a look at some pair i and j we can see that if di + dj + jxi xj j 6 k then all pairs
of y and z work. Otherwise, only y and z such that di + dj + jxi yj + jxj zj 6 k are valid. This
o
formula actually describes a square rotated 45 . What we need to do is to try all pairs of i and j,
cross corresponding squares and then check if there is some point where y = xa and z = xb inside
the resulting area.
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 428

So far we can just check for such point by trying all possible pairs of a and b. However, there is
a nice way to perform this check in linear time and not care about its impact to overall complexity
in future. Proceed with a sweep line and keep two pointers: on the lowest point above the square
and highest point below the square. Since functions of both positions from the x-coordinate of
the sweep line are unimodal the overall complexity will be O n.
The total complexity of such solution is O(n2 logM) where M is the maximal possible answer.
Subtask 6: Now, two more observations are needed. Let’s fix j ¿ i and y ¡ z. Now the pair
produces some square iff xj xi + dj + di ¿ k. Let’s fix j and find the intersection of the squares
for all possible i. The bounds of the square produced can be rewritten as follows:

~
„
„
z y & k  xj  dj   xi  di ;
„
„
„z
„ y ' xj  dj   xi  di   k;
‚
„
„
„
„
z y & k  xj  dj   xi  di ;
„
„z
€ y ' x j  dj   x i  di   k 
We can see that the bounds only depend on xi di and xi + di. We need only maximal and minimal
values of xi di and xi +di, so we can use some data structure, for example, segment tree, to find
them in O n log n time. The total complexity of this solution is O(n log(n) log(M)).
Subtasks 7 and 8: Notice that xj xi +dj +di ¿ k is equal to (xj +dj)(xi di) ¿ k. It follows
that if we iterate through j in the order of increasing (xj + dj) then the set of i that is used to
take minimum and maximum values is only expanding, i.e. once some particular i is in the set,
it remains there for all remaining values of j. Thus, we can keep current maximum and minimum
values of xi di and xi + di without any data structure with the use of the two pointers technique
and two sorted arrays. The total complexity of the full-score solution is O(n logM). Note, that
sorting is done at the very beginning and there is no need to resort these two arrays in each
iteration of binary search.
The another approach to achieve this time bound that doesn’t use sort (and thus solves the
decision problem itself in linear time) is to get rid of useless elements. We say that station i
dominates station j if di ¿ dj + jxi xj j. Now, if one station is dominated by the other we can
consider only dominator as the endpoint of the express line (unless the position being dominated
is considered for the other end). The set of positions that are not dominated by any other index
can be found in linear time by computing prefix and sufix maximums. Now when we use the same
solution as for subtask 6, but we query maximum and minimum values for inequality only when
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 429

considering ”good” positions (not dominated by any other positions). This allows to achieve the
situation that the query bound moves only right and we can process each of them in O 1.

4.3.2 Coduri sursă

Listing 4.3.1: shortcut c.cpp


1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include <time.h>
5
6 typedef long long ll;
7
8 typedef struct llpair
9 {
10 ll fi;
11 ll se;
12 } llpair;
13
14 #define maxn 1000005
15 const ll inf = 1e18;
16
17 ll x[maxn], d[maxn];
18 int n, C;
19 llpair q[maxn];
20 int ans1, ans2;
21
22 llpair mp(ll fi, ll se)
23 {
24 llpair res;
25 res.fi = fi;
26 res.se = se;
27 return res;
28 }
29
30 ll min(ll a, ll b) {return a < b ? a : b;}
31
32 ll max(ll a, ll b) { return a > b ? a : b;}
33
34 ll max3(ll a, ll b, ll c) { return max(max(a, b), c);}
35
36 int can(ll diam)
37 {
38 // cout << "can " << diam << endl;
39 int needleft = 0;
40 ll maxsum = inf;
41 ll minsum = -inf;
42 ll maxdif = inf;
43 ll mindif = -inf;
44 llpair mostleft;
45 mostleft.fi = inf;
46 mostleft.se = -inf;
47 llpair mostright;
48 mostright.fi = -inf;
49 mostright.se = -inf;
50 int l = 0;
51 int r = 0;
52 for (int i = 0; i < n; i++)
53 {
54 // no need to check whether it is "small" or not,
55 // because in case of "small" its square is inside "big"’s square
56 while (r > l && x[i] - q[l].fi + d[i] > diam)
57 {
58 int wh = q[l].se;
59 l++;
60 if (x[wh] - d[wh] < mostleft.fi - mostleft.se)
61 mostleft = mp(x[wh], d[wh]);
62 if (x[wh] + d[wh] > mostright.fi + mostright.se)
63 mostright = mp(x[wh], d[wh]);
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 430

64 needleft = 1;
65 }
66
67 if (needleft)
68 {
69 maxsum = min(maxsum,
70 (mostleft.fi + diam - C - d[i] - mostleft.se) + x[i]);
71 minsum = max(minsum,
72 (mostright.fi - (diam - C - d[i] - mostright.se)) + x[i]);
73 maxdif = min(maxdif,
74 x[i] - (mostright.fi - (diam - C - d[i] - mostright.se)));
75 mindif = max(mindif,
76 x[i] - (mostleft.fi + diam - C - d[i] - mostleft.se));
77 }
78 while (r > l && q[r - 1].fi > x[i] - d[i]) r--;
79 q[r++] = mp(x[i] - d[i], i);
80 }
81
82 if (maxsum < minsum || maxdif < mindif) return 0;
83 int curdif = 0;
84 int cursum = n;
85 for (int i = 0; i < n; i++)
86 {
87 while (curdif < n && x[curdif] - x[i] < mindif) curdif++;
88 while (cursum > 0 && x[cursum - 1] + x[i] >= minsum) cursum--;
89 int cur = max3(cursum, curdif, i + 1);
90 if (cur < n && x[cur] + x[i] <= maxsum && x[cur] - x[i] <= maxdif)
91 {
92 ans1 = i;
93 ans2 = cur;
94 return 1;
95 }
96 }
97 return 0;
98 }
99
100 long long find_shortcut(int N, int* L0, int* L, int C_)
101 {
102 n = N;
103 C = C_;
104 ll cursum = 0;
105 for (int i = 0; i < n; i++)
106 {
107 d[i] = L[i];
108 x[i] = cursum;
109 if (i + 1 < n) cursum += L0[i];
110 }
111 ll l = 0;
112 ll r = inf;
113 while (r - l > 1)
114 {
115 ll mid = (l + r) / 2;
116 if (can(mid)) r = mid;
117 else l = mid;
118 }
119 can(r);
120 return r;
121 }
122
123 // BEGIN CUT
124 int main()
125 {
126 clock_t t1, t2, t3, t4;
127
128 t1=clock();
129
130 freopen("../tests/subtask_8/220", "r", stdin);
131 freopen("8-220.out.txt", "w", stdout);
132
133 int n, c;
134 scanf("%d%d", &n, &c);
135
136 int *l = (int *)malloc((n - 1) * sizeof(int)); // N > 1
137 int *d = (int *)malloc(n * sizeof(int));
138 for (int i = 0; i < n - 1; i++) {
139 scanf("%d", &l[i]);
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 431

140 }
141 for (int i = 0; i < n; i++) {
142 scanf("%d", &d[i]);
143 }
144
145 t2=clock();
146
147 long long t = find_shortcut(n, l, d, c);
148
149 t3=clock();
150
151 // BEGIN SECRET
152 puts("14e047d7a2907b9034950b074822b302");
153 // END SECRET
154
155 printf("%lld\n", t);
156
157 t4=clock();
158
159 // reset console output
160 freopen("CON", "w", stdout);
161
162 printf("n = %d c = %d\n", n,c);
163 printf("t = %lld\n\n", t);
164
165 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
166 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
167 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
168
169 return 0;
170 }
171 // END CUT
172 /*
173 n = 1000000 c = 1000
174 t = 1000857674
175
176 t2-t1 = 1.109000
177 t3-t2 = 5.916000
178 t4-t3 = 0.000000
179
180 Process returned 0 (0x0) execution time : 7.065 s
181 Press any key to continue.
182 */

Listing 4.3.2: sol ge nlogd.cpp


#include <cstdio> // n log(d)
#include <cstdlib>
#include <vector>
#include <deque>
#include <iostream>
#include <algorithm>
#include <ctime>

using namespace std;

#define pb push_back
#define mp make_pair
#define fs first
#define sc second

const int maxN = 1000 * 1000;


const long long inf = (long long) 1e18;

long long x[maxN + 1];


long long d[maxN + 1];
bool important[maxN + 1];
long long maxsump[maxN + 1], mindifp[maxN + 1];
long long difl, difr, suml, sumr;
double checkempty = 0.0;

bool isNonempty(int n)
{
if (suml > sumr || difl > difr) return false;
int p = 0;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 432

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


{
while (p < i && x[p] + x[i] <= sumr && x[p] - x[i] <= difr) p++;
while (p > 0 && (x[p] + x[i] > sumr || x[p] - x[i] > difr)) p--;
if (x[i] + x[p] >= suml &&
x[i] + x[p] <= sumr &&
x[p] - x[i] >= difl &&
x[p] - x[i] <= difr)
{
return true;
}
}

return false;
}

bool check(int n, int c, long long k)


{
suml = difl = -inf;
sumr = difr = inf;

int p = -1;
deque <int> q;

for (int i = 0; i < n; i++)


{
if (important[i])
{
if (q.front() == p)
q.pop_front();

while (p + 1 < i &&


d[q.front()] - x[q.front()] > k - x[i] - d[i])
{
p++;
if (q.front() == p)
q.pop_front();
}

if (p != -1)
{
suml = max(suml, x[i] + d[i] - k + c + maxsump[p]);
sumr = min(sumr, x[i] - d[i] + k - c + mindifp[p]);
difl = max(difl, -x[i] + d[i] - k + c + maxsump[p]);
difr = min(difr, -x[i] - d[i] + k - c + mindifp[p]);
}
}

while (!q.empty() && d[q.back()] - x[q.back()] <= d[i] - x[i])


q.pop_back();

q.push_back(i);
}

return isNonempty(n);
}

long long find_shortcut(int n, vector <int> len, vector <int> dep, int c)
{
x[0] = 0ll;

for (int i = 0; i < n; i++)


{
d[i] = dep[i];
if (i + 1 < n)
{
x[i + 1] = x[i] + len[i];
}
}

maxsump[0] = d[0];
mindifp[0] = -d[0];

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


{
maxsump[i] = max(maxsump[i - 1], x[i] + d[i]);
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 433

mindifp[i] = min(mindifp[i - 1], x[i] - d[i]);


}

important[n - 1] = true;
int curb = n - 1;
for (int i = n - 2; i >= 0; i--)
{
if (x[i] - d[i] < x[curb] - d[curb])
{
important[i] = true;
curb = i;
}
}

long long lb = 0ll;


long long rb = x[n - 1] + 2e9;
while (lb < rb)
{
long long mid = (lb + rb) / 2;
if (check(n, c, mid))
rb = mid;
else
lb = mid + 1;
}

return lb;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 434

t2-t1 = 4.79
t3-t2 = 13.439
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.3: sol ge nlogd fastio.cpp


#include <cstdio>
#include <cstdlib>
#include <vector>
#include <deque>
#include <iostream>
#include <algorithm>

#include<ctime> // clock()

using namespace std;

#define pb push_back
#define mp make_pair
#define fs first
#define sc second

const int maxN = 1000 * 1000;


const long long inf = (long long) 1e18;

long long x[maxN + 1];


long long d[maxN + 1];
bool important[maxN + 1];
long long maxsump[maxN + 1], mindifp[maxN + 1];

long long difl, difr, suml, sumr;


double checkempty = 0.0;

/** 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 == ’-’)
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 435

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

struct Flusher
{
˜Flusher() {
if (write_pos)
fwrite(write_buf, 1, write_pos, stdout), write_pos = 0;
}
} flusher;

bool isNonempty(int n)
{
if (suml > sumr || difl > difr)
return false;

int p = 0;
for (int i = 1; i < n; i++)
{
while (p < i && x[p] + x[i] <= sumr && x[p] - x[i] <= difr)
p++;
while (p > 0 && (x[p] + x[i] > sumr || x[p] - x[i] > difr))
p--;

if (x[i] + x[p] >= suml &&


x[i] + x[p] <= sumr &&
x[p] - x[i] >= difl &&
x[p] - x[i] <= difr)
{
return true;
}
}

return false;
}

bool check(int n, int c, long long k)


{
suml = difl = -inf;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 436

sumr = difr = inf;

int p = -1;
deque <int> q;

for (int i = 0; i < n; i++)


{
if (important[i])
{
if (q.front() == p)
q.pop_front();

while (p + 1 < i && d[q.front()] - x[q.front()] > k - x[i] - d[i])


{
p++;
if (q.front() == p)
q.pop_front();
}

if (p != -1)
{
suml = max(suml, x[i] + d[i] - k + c + maxsump[p]);
sumr = min(sumr, x[i] - d[i] + k - c + mindifp[p]);
difl = max(difl, -x[i] + d[i] - k + c + maxsump[p]);
difr = min(difr, -x[i] - d[i] + k - c + mindifp[p]);
}
}

while (!q.empty() && d[q.back()] - x[q.back()] <= d[i] - x[i])


q.pop_back();

q.push_back(i);
}

return isNonempty(n);
}

long long find_shortcut(int n, vector <int> len, vector <int> dep, int c)
{
x[0] = 0ll;

for (int i = 0; i < n; i++)


{
d[i] = dep[i];
if (i + 1 < n)
{
x[i + 1] = x[i] + len[i];
}
}

maxsump[0] = d[0];
mindifp[0] = -d[0];

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


{
maxsump[i] = max(maxsump[i - 1], x[i] + d[i]);
mindifp[i] = min(mindifp[i - 1], x[i] - d[i]);
}

important[n - 1] = true;
int curb = n - 1;
for (int i = n - 2; i >= 0; i--)
{
if (x[i] - d[i] < x[curb] - d[curb])
{
important[i] = true;
curb = i;
}
}

long long lb = 0ll;


long long rb = inf;
while (lb < rb)
{
long long mid = (lb + rb) / 2;
if (check(n, c, mid))
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 437

rb = mid;
else
lb = mid + 1;
}

return lb;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;

n = readInt();
c = readInt();

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

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++) {
l[i] = readInt();
}
for (int i = 0; i < n; i++) {
d[i] = readInt();
}

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 0.328
t3-t2 = 18.613
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.4: sol nk nlogd.cpp


#include <bits/stdc++.h>
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 438

using namespace std;

using ll = long long;


using ld = long double;
using D = double;
using uint = unsigned int;

#ifdef WIN32
#define LLD "%I64d"
#else
#define LLD "%lld"
#endif

#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second

const int maxn = 1000005;


const ll inf = 1e18;

ll x[maxn], d[maxn];
int n, C;
pair<ll, int> q[maxn];
int ans1, ans2;

bool can(ll diam)


{
bool needleft = false;
ll maxsum = inf;
ll minsum = -inf;
ll maxdif = inf;
ll mindif = -inf;
pair<ll, ll> mostleft = {inf, -inf};
pair<ll, ll> mostright = {-inf, -inf};
int l = 0;
int r = 0;
for (int i = 0; i < n; i++)
{
// no need to check whether it is "small" or not,
// because in case of "small" its square is inside "big"’s square
while (r > l && x[i] - q[l].fi + d[i] > diam)
{
int wh = q[l].se;
l++;
if (x[wh] - d[wh] < mostleft.fi - mostleft.se)
mostleft = {x[wh], d[wh]};
if (x[wh] + d[wh] > mostright.fi + mostright.se)
mostright = {x[wh], d[wh]};

needleft = true;
}
if (needleft)
{
maxsum = min(maxsum,
(mostleft.fi + diam - C - d[i] - mostleft.se) + x[i]);
minsum = max(minsum,
(mostright.fi - (diam - C - d[i] - mostright.se)) + x[i]);
maxdif = min(maxdif,
x[i] - (mostright.fi - (diam - C - d[i] - mostright.se)));
mindif = max(mindif,
x[i] - (mostleft.fi + diam - C - d[i] - mostleft.se));
}

while (r > l && q[r - 1].fi > x[i] - d[i]) r--;


q[r++] = {x[i] - d[i], i};
}

if (maxsum < minsum || maxdif < mindif) return false;


int curdif = 0;
int cursum = n;
for (int i = 0; i < n; i++)
{
while (curdif < n && x[curdif] - x[i] < mindif) curdif++;
while (cursum > 0 && x[cursum - 1] + x[i] >= minsum) cursum--;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 439

int cur = max({cursum, curdif, i + 1});


if (cur < n && x[cur] + x[i] <= maxsum && x[cur] - x[i] <= maxdif)
{
ans1 = i;
ans2 = cur;
return true;
}
}
return false;
}

long long find_shortcut(int N, vector <int> L0, vector <int> L, int C_)
{
n = N;
C = C_;
ll cursum = 0;
for (int i = 0; i < n; i++)
{
d[i] = L[i];
x[i] = cursum;
if (i + 1 < n) cursum += L0[i];
}
ll l = 0;
ll r = inf;
while (r - l > 1)
{
ll mid = (l + r) / 2;
if (can(mid)) r = mid;
else l = mid;
}
can(r);
return r;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector <int> l(n - 1);


vector <int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 440

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 4.629
t3-t2 = 8.341
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.5: shortcut-24626.cpp


// https://oj.uz/submission/24626

#include "shortcut.h"
#include <bits/stdc++.h>

using namespace std;

typedef long long lint;


typedef pair<lint, lint> pi;

struct Fucking_locality
{
lint arg1, arg2;
lint first;
int second;
bool operator < (const Fucking_locality &fuck) const
{
return make_pair(first,second) < make_pair(fuck.first,fuck.second);
}
};

int n, c;
lint a[1000005], b[1000005];
Fucking_locality v[1000005], w[1000005];

bool trial(lint x)
{
lint ps = -1e18, pe = 1e18, ms = -1e18, me = 1e18;
lint mx = -1e18, mxp = -1, smx = -1e18;
lint mn = 1e18, mnp = -1, smn = 1e18;
int p = 0;
for(int i=0; i<n; i++)
{
while(p < n && w[p].first + x < v[i].first)
{
lint cmx = w[p].arg1;
lint cmn = w[p].arg2;
if(mx < cmx)
{
smx = mx;
mx = cmx;
mxp = w[p].second;
}
else
if(smx < cmx) smx = cmx;

if(mn > cmn)


{
smn = mn;
mn = cmn;
mnp = w[p].second;
}
else
if(smn > cmn) smn = cmn;
p++;
}

lint q1 = (v[i].second != mxp ? mx : smx);


CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 441

lint q2 = (v[i].second != mnp ? mn : smn);


lint ca = v[i].arg1;
lint cb = v[i].arg2;

ps = max(ps, q1 - x + c + cb + ca);
pe = min(pe, q2 + x - c + cb - ca);
ms = max(ms, q1 - x + c - cb + ca);
me = min(me, q2 + x - c - cb - ca);
}

p = 0;
for(int i=0; i<n; i++)
{
lint s = max(ps - b[i], b[i] - me);
lint e = min(pe - b[i], b[i] - ms);
while(p < n && s > b[p]) p++;
while(p > 0 && s <= b[p-1]) p--;
while(p < n && b[p] <= e) return true;
}

return false;
}

long long find_shortcut(int n, std::vector<int> l,


std::vector<int> d, int c)
{
::n = n;
::c = c;

for(int i=0; i<n; i++)


{
a[i] = d[i];
if(i >= 1) b[i] = b[i-1] + l[i-1];
}

for(int i=0; i<n; i++)


{
v[i] = (Fucking_locality){a[i], b[i], a[i] + b[i], i};
w[i] = (Fucking_locality){a[i] + b[i], -a[i] + b[i], -a[i] + b[i], i};
}

sort(v, v+n);
sort(w, w+n);

lint s = 0, e = 2e15;
while(s != e)
{
lint m = (s+e)/2;
if(trial(m)) e = m;
else s = m+1;
}

return s;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);


CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 442

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.546
t3-t2 = 8.469
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.6: shortcut-33932.cpp


// https://oj.uz/submission/33932

#include "shortcut.h"
#include <algorithm>

#include<ctime>
#include<fstream>
#include<iostream>

using namespace std;

typedef long long llong;

int n, c;
vector<llong> dist;
vector<int> etc;
vector<pair<llong, int>> ad, sb;

bool check(llong m)
{
llong fs = -1ll, se = -1ll;
int fsi = -1, sei = -1, p, q;
llong lad = -1e18, gad = 1e18, lsb = -1e18, gsb = 1e18;
for (int i = 0, j = 0; i < n; ++i)
{
while (j < n && sb[j].first + m < ad[i].first)
{
p = sb[j].second;
llong mx = dist[p] + etc[p];
if (fs < mx)
{
sei = fsi;
fsi = p;
se = fs;
fs = mx;
}
else
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 443

if (se < mx) sei = p, se = mx;


++j;
}

p = ad[i].second;
if (p == sb[0].second && j == 1 || j == 0) continue;

llong adi = (p == fsi ? se : fs);


llong sbi = (p == sb[0].second ? sb[1].first : sb[0].first);
llong adj = dist[p] + etc[p];
llong sbj = dist[p] - etc[p];

lsb = max(lsb, adi - sbj - m + c);


gsb = min(gsb, sbi - adj + m - c);
lad = max(lad, adi + adj - m + c);
gad = min(gad, sbi + sbj + m - c);
}

llong gpos, lpos;


p = 0, q = n - 1;
for (int i = 0; i < n; ++i)
{
gpos = min(dist[i] - lsb, gad - dist[i]);
lpos = max(dist[i] - gsb, lad - dist[i]);

while (p > 0 && dist[p - 1] >= lpos) --p;


while (p < n && dist[p] < lpos) ++p;
while (q >= 0 && dist[q] > gpos) --q;
while (q < n - 1 && dist[q + 1] <= gpos) ++q;

if (p <= q) return true;


}

return false;
}

long long find_shortcut(int N, std::vector<int> l, std::vector<int> d, int C)


{
n = N; c = C;
dist.resize(n);
llong sum = 0ll;
for (int i = 1; i < n; ++i)
{
sum += l[i - 1];
dist[i] = sum;
}

etc = d;
llong ld = 0ll, e = 0ll;
int fs = 0, se = 0;
for (int i = 0; i < n; ++i)
{
ad.push_back({ dist[i] + etc[i], i });
sb.push_back({ dist[i] - etc[i], i });

e = max(e, ld + etc[i]);

if (fs < etc[i])


{
se = fs;
fs = etc[i];
}
else
if (se < etc[i])
{
se = etc[i];
}

if (i < n - 1)
ld = max(ld, (llong)etc[i]) + l[i];
}

sort(ad.begin(), ad.end());
sort(sb.begin(), sb.end());

llong s = fs + se;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 444

while (s < e)
{
llong m = (s + e) / 2;
if (check(m)) e = m;
else s = m + 1ll;
}

return s;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.453
t3-t2 = 6.514
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.7: shortcut-94572.cpp


// https://oj.uz/submission/94572

#include <bits/stdc++.h>
#include "shortcut.h"

using namespace std;


CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 445

const long long inf = 1ll << 60;

long long find_shortcut(int n, vector<int> l, vector<int> d, int c)


{
vector<long long> x(n);
for (int i = 0; i < n - 1; ++i)
{
x[i + 1] = x[i] + l[i];
}

auto check = [&](long long limit)


{
long long lsum = -inf, rsum = inf;
long long ldiff = -inf, rdiff = inf;
long long max_sum = -inf, min_diff = inf;

deque<int> q;
for (int i = 0; i < n; ++i)
{
while (!q.empty() && x[i]-x[q.front()]+d[i]+d[q.front()] > limit)
{
max_sum = max(max_sum, x[q.front()] + d[q.front()]);
min_diff = min(min_diff, x[q.front()] - d[q.front()]);
q.pop_front();
}

if (max_sum >= 0)
{
lsum = max(lsum, x[i] + d[i] + max_sum + c - limit);
rsum = min(rsum, x[i] - d[i] + min_diff + limit - c);
rdiff = min(rdiff, x[i] - d[i] - max_sum + limit - c);
ldiff = max(ldiff, x[i] + d[i] - min_diff + c - limit);
}

while (!q.empty() && x[i] - d[i] < x[q.back()] - d[q.back()])


{
q.pop_back();
}

q.push_back(i);
}

if (lsum > rsum || ldiff > rdiff)


{
return false;
}

for (int i = 0, j = n, k = 0; i < n; ++i)


{
while (j && x[j - 1] + x[i] >= lsum)
{
--j;
}

while (k < n && x[k] - x[i] < ldiff)


{
++k;
}

int p = max(i + 1, max(j, k));


if (p < n && x[p] + x[i] <= rsum && x[p] - x[i] <= rdiff)
{
return true;
}
}

return false;
};

long long low = 0, high = inf;


while (low < high)
{
long long mid = low + high >> 1;
if (check(mid))
{
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 446

high = mid;
}
else
{
low = mid + 1;
}
}

return high;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.526
t3-t2 = 29.047
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.8: shortcut-97033.cpp


// https://oj.uz/submission/97033

#include "shortcut.h"
#include <bits/stdc++.h>
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 447

#define INFLL (0x3f3f3f3f3f3f3f3fll)

using namespace std;

typedef long long ll;


inline void upmin(ll &a, ll b) { if(b < a) a = b; }
inline void upmax(ll &a, ll b) { if(a < b) a = b; }

const int MAXN = 1000005;

ll Mx0[MAXN], Mx1[MAXN];
ll X[MAXN], L[MAXN], TP[MAXN], TM[MAXN];
int O[MAXN], OJ[MAXN];

int N; ll K, Ans;

bool isp(ll Y)
{
ll xymx = -INFLL, xymn = INFLL, yxmx = -INFLL, yxmn = INFLL, rmn, rmx;
for(int oi = 0, oj = 0, j; oj < N; oj++)
{
j = OJ[oj]; ll t = Y-TP[j];
for(; oi < N && t < TM[O[oi]]; oi++);
if(!oi || (1 == oi && j == O[0])) continue;
rmx = TP[j] == Mx0[oi-1] ? Mx1[oi-1] : Mx0[oi-1];
rmn = -(j == O[0] ? TM[O[1]] : TM[O[0]]);
ll a = Y-K - TM[j], b = K-Y + TP[j];
upmin(xymn, a+rmn); upmax(xymx, b+rmx);
upmin(yxmn, a-rmx); upmax(yxmx, b-rmn);
if(xymn < xymx || yxmn < yxmx) return false;
}

for(int s = 0, e = 0; s < N; s++)


{
rmn = max(xymx-X[s], yxmx+X[s]);
rmx = min(xymn-X[s], yxmn+X[s]);
if(rmx < rmn) continue;
for(; e+1 < N && X[e] < rmn; e++);
for(; e && rmx < X[e]; e--);
if(rmn <= X[e]) return true;
}

return false;
}

ll getAns()
{
ll mx = -INFLL;
for(int i = 0; i < N; i++)
{
upmax(Ans, X[i]+L[i] + mx);
upmax(mx, L[i]-X[i]);
TP[i] = X[i]+L[i];
TM[i] = L[i]-X[i];
}

iota(O, O+N, 0);


sort(O, O+N, [&](int a, int b) {return TM[a] > TM[b];});

iota(OJ, OJ+N, 0);


sort(OJ, OJ+N, [&](int a, int b) {return TP[a] < TP[b];});

Mx0[0] = Mx1[0] = -INFLL;


for(int oi = 0, i; oi < N; oi++)
{
if(oi)
{
Mx0[oi] = Mx0[oi-1];
Mx1[oi] = Mx1[oi-1];
}

i = O[oi]; ll c = TP[i];
if(Mx0[oi] < c) swap(Mx0[oi], c);
if(Mx1[oi] < c) swap(Mx1[oi], c);
}
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 448

ll a = 0, b = 0;
for(int i = 0; i < N; i++)
{
ll c = L[i];
if(a < c) swap(a, c);
if(b < c) swap(b, c);
}

ll s = a+b, e = Ans;
for(ll m; s < e;)
{
m = (s+e) >> 1;
if(isp(m)) e = m;
else s = m+1;
}

return s;
}

long long find_shortcut(int n, std::vector<int> l,


std::vector<int> d, int c)
{
::N = n; ::K = c;
for(int i = 1; i < N; i++) ::X[i] = ::X[i-1] + l[i-1];
for(int i = 0; i < N; i++) ::L[i] = d[i];
return getAns();
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 449

t2-t1 = 3.531
t3-t2 = 3.265
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.9: shortcut-99074.cpp


// https://oj.uz/submission/99074

#include <bits/stdc++.h>
#include "shortcut.h"

#define enl printf("\n")


#define case(t) printf("Case #%d: ", (t))
#define ni(n) scanf("%d", &(n))
#define nl(n) scanf("%I64d", &(n))
#define nai(a, n) for (int i = 0; i < (n); i++) ni(a[i])
#define nal(a, n) for (int i = 0; i < (n); i++) nl(a[i])
#define pri(n) printf("%d\n", (n))
#define prl(n) printf("%I64d\n", (n))
#define pii pair<int, int>
#define pll pair<long long, long long>
#define vii vector<pii>
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace std;

typedef long long ll;

const double pi = acos(-1);


const int MOD = 1e9 + 7;
const ll INF = 1e17 + 7;
const int MAXN = 1e6 + 5;
const double eps = 1e-9;
ll x[MAXN], d[MAXN];
pll dif[MAXN];

/*
j>i -> xi-di, xi+di are known
z+y<=len+(xj-dj)+(xi-di) -> mxsm
z+y>=(xj+dj)+(xi+di)-len -> mism
z-y<=len+(xj-dj)-(xi+di) -> mxdi
z-y>=(xj+dj)-(xi-di)-len -> midi

shortest path has to contain shortcut:


(xj+dj) - (xi-di) > k -> calc with 2 pointers
*/

bool solve(ll len, int n, ll c)


{
ll mxsm = INF, mism = -INF, mxdi = INF, midi = -INF;
ll di = INF, sm = -INF;

/*
pll lef = {INF, -INF};
pll rig = {-INF, -INF};
*/

int l = 0, r = 0;
bool skip = false;

// calculate bounds
for (int i = 0; i < n; i++)
{
while (l < r && x[i] + d[i] - dif[l].fi > len)
{
int ind = dif[l++].se; // add element to set of elements
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 450

// producing the intersection

if (x[ind] - d[ind] < di)//lef.fi - lef.se)


di = x[ind] - d[ind];//lef = {x[ind], d[ind]};

if (x[ind] + d[ind] > sm)//rig.fi + rig.se)


sm = x[ind] + d[ind];//rig = {x[ind], d[ind]};
skip = true;
}

if (skip)
{
mxsm = min(mxsm, di + x[i] - d[i] + len - c);
mism = max(mism, sm + x[i] + d[i] - len + c);
mxdi = min(mxdi, x[i] - d[i] - sm + len - c);
midi = max(midi, x[i] + d[i] - di - len + c);
}

// current station dominates last used station -> remove


// 1. cur elem (i) dominates prev (j) -> xi-di < xj-dj
// 2. prev elem (i) dominates cur (j) -> xi+di > xj+dj
// dominating elements have extremal sm and di -> inc di, dec sm
while (l < r && dif[r - 1].fi > x[i] - d[i])
r--;
dif[r++] = {x[i] - d[i], i};
}

if (mxsm < mism || mxdi < midi)


return false;

// check if there is a shortcut satisfying this bounds


int curdi = 0, cursm = n;
for (int i = 0; i < n; i++)
{
while (curdi < n && x[curdi] - x[i] < midi) // violates 4.
curdi++;

// all indices >= curdi satisfy 4.


while (cursm > 0 && x[cursm - 1] + x[i] >= mism) // violates 2.
cursm--;

// all indices >= cursm satisfy 2.


int cur = max(max(cursm, curdi), i + 1); // shortcut with
// next element / all other combinations are checked
if (cur < n && x[cur] + x[i] <= mxsm && x[cur] - x[i] <= mxdi)
return true;
}

return false;
}

ll find_shortcut(int n, vi l, vi d2, int c)


{
for (int i = 0; i < n; i++)
{
d[i] = d2[i];
if (i == 0)
x[i] = 0;
else
x[i] = x[i - 1] + l[i - 1];
}

ll lo = 0, hi = INF, ret = -1;


while (lo <= hi)
{
ll mi = (lo + hi) / 2;
if (solve(mi, n, c))
hi = mi - 1, ret = mi;
else
lo = mi + 1;
}

return ret;
}

// BEGIN CUT
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 451

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.507
t3-t2 = 6.25
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.10: shortcut-99075.cpp


// https://oj.uz/submission/99075

#pragma GCC optimize("O3")

#include <bits/stdc++.h>
#include "shortcut.h"

#define pii pair<int, int>


#define pll pair<long long, long long>
#define vii vector<pii>
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second

using namespace std;


CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 452

typedef long long ll;

const ll INF = 1e17 + 7;


const int MAXN = 1e6 + 5;

ll x[MAXN], d[MAXN];
int dif[MAXN];

/*
j>i -> xi-di, xi+di are known
z+y<=len+(xj-dj)+(xi-di) -> mxsm
z+y>=(xj+dj)+(xi+di)-len -> mism
z-y<=len+(xj-dj)-(xi+di) -> mxdi
z-y>=(xj+dj)-(xi-di)-len -> midi

shortest path has to contain shortcut:


(xj+dj) - (xi-di) > k -> calc with 2 pointers
*/

bool solve(ll len, int n, ll c)


{
ll mxsm = INF, mism = -INF, mxdi = INF, midi = -INF;
ll di = INF, sm = -INF;
int l = 0, r = 0;
bool skip = false;

// calculate bounds
for (int i = 0; i < n; i++)
{
while (l < r && x[i] + d[i] - (x[dif[l]] - d[dif[l]]) > len)
{
int ind = dif[l++]; // add element to set of elements
// producing the intersection
if (x[ind] - d[ind] < di)
di = x[ind] - d[ind];
if (x[ind] + d[ind] > sm)
sm = x[ind] + d[ind];
skip = true;
}

if (skip)
{
mxsm = min(mxsm, di + x[i] - d[i] + len - c);
mism = max(mism, sm + x[i] + d[i] - len + c);
mxdi = min(mxdi, x[i] - d[i] - sm + len - c);
midi = max(midi, x[i] + d[i] - di - len + c);
}

// current station dominates last used station -> remove


// 1. cur elem (i) dominates prev (j) -> xi-di < xj-dj
// 2. prev elem (i) dominates cur (j) -> xi+di > xj+dj
// dominating elements have extremal sm and di -> inc di, dec sm

while (l < r && x[dif[r - 1]] - d[dif[r - 1]] > x[i] - d[i])
r--;
dif[r++] = i;
}

if (mxsm < mism || mxdi < midi)


return false;

// check if there is a shortcut satisfying this bounds


int curdi = 0, cursm = n;
for (int i = 0; i < n; i++)
{
while (curdi < n && x[curdi] - x[i] < midi) // violates 4.
curdi++;
// all indices >= curdi satisfy 4.
while (cursm > 0 && x[cursm - 1] + x[i] >= mism) // violates 2.
cursm--;
// all indices >= cursm satisfy 2.
int cur = max(max(cursm, curdi), i + 1); // shortcut with
// next element / all other combinations are checked
if (cur < n && x[cur] + x[i] <= mxsm && x[cur] - x[i] <= mxdi)
return true;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 453

return false;
}

ll find_shortcut(int n, vi l, vi d2, int c)


{
for (int i = 0; i < n; i++)
{
d[i] = d2[i];
if (i == 0)
x[i] = 0;
else
x[i] = x[i - 1] + l[i - 1];
}

ll lo = 0, hi = INF, ret = -1;


while (lo <= hi)
{
ll mi = (lo + hi) / 2;
if (solve(mi, n, c))
hi = mi - 1, ret = mi;
else
lo = mi + 1;
}

return ret;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 454

t = 1000857674

t2-t1 = 3.886
t3-t2 = 2.516
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.11: shortcut-113364.cpp


// https://oj.uz/submission/113364

#include "shortcut.h"
#include <bits/stdc++.h>

using namespace std;

#define ll long long


const int N=1000050;
const ll inf=5e18;

int n,d[N],c,q[N],ql,qr;
ll x[N];

bool Check(ll k)
{
ll mx=-inf,mn=inf;
ll ls=-inf,rs=inf,ld=-inf,rd=inf;
ql=1;qr=0;
for(int i=1;i<=n;i++)
{
for(;ql<=qr && x[q[ql]]-d[q[ql]]<x[i]+d[i]-k;ql++)
mx=max(mx,x[q[ql]]+d[q[ql]]),mn=min(mn,x[q[ql]]-d[q[ql]]);
ls=max(ls,mx+x[i]+d[i]-k+c);
rs=min(rs,mn+x[i]-d[i]+k-c);
ld=max(ld,mx-x[i]+d[i]-k+c);
rd=min(rd,mn-x[i]-d[i]+k-c);
for(;ql<=qr && x[q[qr]]-d[q[qr]]>=x[i]-d[i];qr--);
q[++qr]=i;
}

int l1=n+1,r1=n+1,l2=0,r2=0;
for(int i=1;i<=n;i++)
{
for(;l1>=2 && x[i]+x[l1-1]>=ls;l1--);
for(;r1>=1 && x[i]+x[r1]>rs;r1--);
for(;l2<=n && x[i]-x[l2]>rd;l2++);
for(;r2<n && x[i]-x[r2+1]>=ld;r2++);
int l=max(l1,l2),r=min(r1,r2);
if(l<=r) return 1;
}

return 0;
}

ll find_shortcut(int n, vector<int> l, vector<int> d, int c)


{
::n=n;
::c=c;
::d[1]=d[0];
int mxd=d[0];
x[n+1]=inf;
for(int i=2;i<=n;i++)
x[i]=x[i-1]+l[i-2],::d[i]=d[i-1],mxd=max(mxd,d[i-1]);

ll lo=0,hi=2*mxd+x[n],mi=lo+hi+1>>1;
for(;lo<hi;mi=lo+hi+1>>1)
{
if(Check(mi)) hi=mi-1;
else lo=mi;
}

return lo+1;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 455

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.507
t3-t2 = 5.25
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.12: shortcut-114144.cpp


// https://oj.uz/submission/114144

#pragma GCC optimize("Ofast")


#include <bits/stdc++.h>

#define pb push_back
#define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define F first
#define S second
#define ET cout << "\n"
#define MP make_pair
#define MEM(i,j) memset(i,j,sizeof i)
#define ALL(v) v.begin(),v.end()
#define DB(a,s,e) {for(int i=s;i<e;++i) cout << a[i] << " ";ET;}
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 456

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

const ll INF=1e18;

/*
j<i
si-sj+di+dj>k
si+di-k>sj-dj

x-y<0
|sj-x|+|si-y|+di+dj+c<=k
x+y>=(sj+dj)+(si+di)-k+c ->b0
x-y>=(sj+dj)-(si-di)-k+c ->b1
x-y<=(sj-dj)-(si+di)+k-c ->b2
x+y<=(sj-dj)+(si-di)+k-c ->b3

y>=b0-x
y>=x-b2
y<=x-b1
y<=b3-x
y<=sn

*/

ll find_shortcut(int n,vector<int> l,vector<int> d, int c)


{
ll mx=-INF,L=1,R=0;
vector<ll> s(n,0),dp(n,0),q(n,0);
for(int i=1;i<n;++i)
s[i]=s[i-1]+l[i-1];
for(int i=0;i<n;++i)
dp[i]=max((ll)d[i],s[i]+mx),mx=max(mx,dp[i]-s[i]);
for(int i=1;i<n;++i)
R=max(R,dp[i-1]+d[i]+s[i]-s[i-1]);
while(L<R)
{
ll mid=L+R>>1,mxt=-INF,mit=INF,b0=-INF,b1=-INF,
b2=-1,b3=INF,flag=0,lf=0,rg=-1;
for(int i=0,j=0;i<n;++i)
{
while(rg>=lf&&s[i]+d[i]-s[q[lf]]+d[q[lf]]>mid)
mxt=max(mxt,s[q[lf]]+d[q[lf]]),
mit=min(mit,s[q[lf]]-d[q[lf]]),lf++;

b0=max(b0,s[i]+d[i]+mxt-mid+c),
b1=max(b1,mxt-s[i]+d[i]-mid+c);

b2=min(b2,mit-s[i]-d[i]+mid-c),
b3=min(b3,mit+s[i]-d[i]+mid-c);

while(rg>=lf&&s[q[rg]]-d[q[rg]]>s[i]-d[i]) --rg;
q[++rg]=i;
}

for(int i=0,j=n,k=0;i+1<n&&!flag;++i)
{
ll up=min({s[n-1],s[i]-b1,b3-s[i]}),dn=max(b0-s[i],s[i]-b2);
while(j>0&&s[j-1]>=b0-s[i]) --j;
while(k<n&&s[k]<s[i]-b2) ++k;
if(max(j,k)!=n&&s[max(j,k)]<=up) flag=1;
}

if(flag) R=mid;
else L=mid+1;
}

return L;
}

// BEGIN CUT
int main()
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 457

{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.64
t3-t2 = 4.953
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.13: shortcut-142952.cpp


// https://oj.uz/submission/142952

#include "shortcut.h"
#include <iostream>

#include<ctime>
#include<fstream>

using namespace std;

const int nmax=1000*1000+5;


const long long inf=1LL*1e18;

int d[nmax];
long long x[nmax],y[nmax];
int i,n;
long long C;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 458

bool check(long long k)


{
long long mxsum=-inf,mndif=inf;
long long suml=-inf,sumr=inf,difl=-inf,difr=inf;
long long sum,dif;
int p=1,u=0;
for(i=1;i<=n;i++)
{
while(p<=u&&x[i]-x[d[p]]+y[i]+y[d[p]]>k)
{
sum=x[d[p]]+y[d[p]];dif=x[d[p]]-y[d[p]];
if(sum>mxsum) mxsum=sum;
if(dif<mndif) mndif=dif;
p++;
}
if(mxsum!=-inf)
{
suml=max(suml,-k+C+mxsum+x[i]+y[i]);
sumr=min(sumr,k-C+mndif+x[i]-y[i]);
difl=max(difl,-k+C-mndif+x[i]+y[i]);
difr=min(difr,k-C-mxsum+x[i]-y[i]);
}
while(p<=u&&x[i]-y[i]<x[d[u]]-y[d[u]])
u--;
d[++u]=i;
}

if(suml>sumr||difl>difr) return 0;
int p1=1,p2=n;
int poz;
for(i=1;i<=n;i++)
{
while(p1<=n&&x[p1]-x[i]<difl)
p1++;
while(p2>=1&&x[p2]+x[i]>=suml)
p2--;
poz=max(i+1,max(p1,p2+1));
if(poz<=n&&
x[poz]+x[i]>=suml&&
x[poz]+x[i]<=sumr&&
x[poz]-x[i]>=difl&&
x[poz]-x[i]<=difr)
return 1;
}
return 0;
}

long long find_shortcut(int N, std::vector<int> l,


std::vector<int> d, int c)
{
C=c;n=N;
for(i=0;i<n-1;i++)
x[i+2]=x[i+1]+l[i];
for(i=0;i<n;i++)
y[i+1]=d[i];

long long ans=0;


for(long long p=50;p>=0;p--)
ans+=(1LL<<p);
for(long long p=50;p>=0;p--)
if(check(ans-(1LL<<p)))
ans-=(1LL<<p);

return ans;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 459

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.562
t3-t2 = 4.498
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.14: shortcut-162764.cpp


// https://oj.uz/submission/162764

#include "shortcut.h"
#include <bits/stdc++.h>

using namespace std;

using lint = long long;


const lint INF = 2e15;

struct SumDif
{
lint sum, dif;
lint X, D;
int id;
};

int N, C;

SumDif SUM[1000000]; // increasing X[i] + D[i]


SumDif DIF[1000000]; // increasing X[i] - D[i]

lint X[1000000]; // location of main stations

bool is_valid(lint T)
{
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 460

lint max_sum = +INF; // maximum of S1 + S2 that is valid


lint min_sum = -INF; // minimum of S1 + S2 that is valid
lint max_dif = +INF; // maximum of S1 - S2 that is valid
lint min_dif = -INF; // minimum of S1 - S2 that is valid

lint sum_big = -INF; // maximum X[i] + D[i], for all valid i


lint sum_sml = -INF; // second maximum X[i] + D[i], for all valid i
int sum_idx = -1; // index of sum_big

lint dif_sml = +INF; // minimum X[i] - D[i], for all valid i


lint dif_big = +INF; // second minimum X[i] - D[i], for all valid i
int dif_idx = -1; // index of dif_sml

for (int j = 0, pos = 0; j < N; j++)


{
while (pos < N && SUM[j].sum - DIF[pos].dif > T)
{
lint sum_val = DIF[pos].sum, dif_val = DIF[pos].dif;

if (sum_big < sum_val)


{
sum_sml = sum_big;
sum_big = sum_val;
sum_idx = DIF[pos].id;
}
else
if (sum_sml < sum_val)
{
sum_sml = sum_val;
}

if (dif_sml > dif_val)


{
dif_big = dif_sml;
dif_sml = dif_val;
dif_idx = DIF[pos].id;
}
else
if (dif_big > dif_val)
{
dif_big = dif_val;
}

pos++;
}

lint valid_sum = ((SUM[j].id != sum_idx)? sum_big : sum_sml);


lint valid_dif = ((SUM[j].id != dif_idx)? dif_sml : dif_big);
lint sum_val = SUM[j].sum, dif_val = SUM[j].dif;

max_sum = min(max_sum, + T + valid_dif + dif_val - C);


min_sum = max(min_sum, - T + valid_sum + sum_val + C);
max_dif = min(max_dif, + T + valid_dif - sum_val - C);
min_dif = max(min_dif, - T + valid_sum - dif_val + C);
}

if (min_sum > max_sum || min_dif > max_dif)


return false;

for (int i = 0, ptl = 0, ptr = N - 1, pos; i < N; i++)


{
while (ptl < N && X[ptl] - X[i] < min_dif) ptl++;
while (ptr >= 0 && X[ptr] + X[i] >= min_sum) ptr--;

pos = max(ptl, ptr + 1);


if (pos < N &&
min_sum <= X[pos] + X[i] &&
X[pos] + X[i] <= max_sum &&
min_dif <= X[pos] - X[i] &&
X[pos] - X[i] <= max_dif)
return true;
}

return false;
}
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 461

lint find_shortcut(int n_, vector<int> l_, vector<int> D, int c_)


{
N = n_, C = c_;
for (int i = 0; i < l_.size(); i++)
X[i + 1] = X[i] + l_[i];
for (int i = 0; i < N; i++)
{
SUM[i] = SumDif{X[i] + D[i], X[i] - D[i], X[i], D[i], i};
DIF[i] = SumDif{X[i] + D[i], X[i] - D[i], X[i], D[i], i};
}

sort(SUM, SUM + N, [&](SumDif &l, SumDif &r) {return l.sum < r.sum;});
sort(DIF, DIF + N, [&](SumDif &l, SumDif &r) {return l.dif < r.dif;});

lint res = 0;

for (lint L = 0, R = INF, M = (L + R) / 2; L <= R; M = (L + R) / 2)


if (is_valid(M))
R = M - 1, res = M;
else
L = M + 1;

return res;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.875
t3-t2 = 6.455
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 462

t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.15: shortcut-207049.cpp


//https://oj.uz/submission/207049

#include "shortcut.h"
#include <bits/stdc++.h>

using namespace std;

using ll = long long;


using pll = pair<ll, ll>;

#define X first
#define Y second

int n, c;
ll d[1000007], x[1000007];
vector<pll> VP, VM;

//x[j] <= min(maxs - x[i], maxd + x[i])


//x[j] >= max(mins - x[i], mind + x[i])

bool find_pair(ll mins, ll maxs, ll mind, ll maxd)


{
for(int i = 1 ; i < n ; i++)
{
ll minv = max(mins - x[i], mind + x[i]);
ll maxv = min(maxs - x[i], maxd + x[i]);
auto it = lower_bound(x + i + 1, x + n + 1, minv);
if(it != x + n + 1 && ( *it) <= maxv)
return true;
}
return false;
}

bool check(ll k)
{
ll mins = 0, maxs = 1e18, mind = 0, maxd = 1e18;
ll minis = 1e18, maxis = 0;
int ii = 0;
for(int jj = 0 ; jj < n ; jj++)
{
int j = VP[jj].Y;
while(ii < n && VM[ii].X + k < VP[jj].X)
{
int i = VM[ii].Y;
minis = min(minis, x[i] + d[i]);
maxis = max(maxis, x[i] + d[i]);
ii++;
}

if(ii && 2 * d[j] <= k)


{
mins = max(mins, VP[jj].X + maxis - k + c);
maxs = min(maxs, k - c + (x[j] - d[j]) + VM[0].X);
mind = max(mind, VP[jj].X - VM[0].X - k + c);
maxd = min(maxd, k - c + (x[j] - d[j]) - maxis);
}
}

for(int j = 1 ; j <= n ; j++)


{
if(2 * d[j] > k)
{
for(int i = 1 ; i < j ; i++)
{
if(x[i] - d[i] + k < x[j] + d[j])
{
mins = max(mins, x[j] + d[j] + x[i] + d[i] - k + c);
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 463

maxs = min(maxs, k - c + (x[j] - d[j]) + x[i] - d[i]);


mind = max(mind, x[j] + d[j] - (x[i] - d[i]) - k + c);
maxd = min(maxd, k - c + (x[j] - d[j]) - (x[i] + d[i]));
}
}
}
}

return find_pair(mins, maxs, mind, maxd);


}

long long find_shortcut(int n, std::vector<int> l, std::vector<int> dd, int c)


{
d[1] = dd[0];
::n = n;
::c = c;
for(int i = 1 ; i < n ; i++)
{
x[i + 1] = x[i] + l[i - 1];
d[i + 1] = dd[i];
}

int max1 = 0, max2 = 0;


for(int i = 1 ; i <= n ; i++)
{
VP.emplace_back(x[i] + d[i], i);
VM.emplace_back(x[i] - d[i], i);
if(d[i] >= max1)
{
max2 = max1;
max1 = d[i];
}
else
if(d[i] > max2)
max2 = d[i];
}

sort(VP.begin(), VP.end());
sort(VM.begin(), VM.end());

ll a = max1 + max2, b = 1e18, mid;


while(a < b)
{
mid = (a + b) / 2;
if(check(mid))
b = mid;
else
a = mid + 1;
}

return a;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 464

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.525
t3-t2 = 10.265
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.3.16: shortcut-225819.cpp


// https://oj.uz/submission/225819

#include <bits/stdc++.h>
#include "shortcut.h"

#define all(x) x.begin(),x.end()


#define PB push_back
#define MP make_pair
#define pli pair<ll,int>
#define pll pair<ll,ll>
#define ft first
#define sd second

using namespace std;

typedef long long ll;

const int N = 1000100;


const ll OO = 1e18;

pli events[N], o_events[N];


pll seg_mx[N], seg_mn[N];
ll pf[N], dst[N], c;
int n;

bool ok(ll mx)


{
bool was = 0;
ll u = -1, d = -1, l = -1, r = -1;

int ptr = 0;

for (int it = 0; it < n; it++)


//for (register int it = 0; it < n; it++)
{
int i = o_events[it].sd;

while (ptr < n && mx + events[ptr].ft < -pf[i] + dst[i])


ptr++;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 465

pll cur = MP(1, 1);

if (!(seg_mx[ptr].ft == -OO ||
(seg_mx[ptr].ft == (pf[i] + dst[i]) &&
seg_mx[ptr].sd == OO)))
{
cur.ft = (seg_mx[ptr].ft == (pf[i] + dst[i]) ?
seg_mx[ptr].sd :
seg_mx[ptr].ft);
cur.sd = (seg_mn[ptr].ft == (pf[i] - dst[i]) ?
seg_mn[ptr].sd :
seg_mn[ptr].ft);

ll cu = cur.sd + pf[i] + (mx - dst[i] - c);


ll cd = cur.ft + pf[i] - (mx - dst[i] - c);
ll cl = -cur.sd + pf[i] - (mx - dst[i] - c);
ll cr = -cur.ft + pf[i] + (mx - dst[i] - c);

if (!was)
{
was = 1;
u = cu; d = cd; l = cl; r = cr;
if (u < d || r < l) return 0;
}
else
{
u = min(u, cu);
d = max(d, cd);
if (u < d) return 0;
l = max(l, cl);
r = min(r, cr);
if (r < l) return 0;
}
}
}

if (!was) return 1;

l >>= 1; r >>= 1;

int l1 = n, r1 = n - 1;
int r2 = -1, l2 = 0;

for (int i = 0; i < n; i++)


//for (register int i = 0; i < n; i++)
{
while (l1 - 1 >= 0 && pf[l1 - 1] + pf[i] >= d)
l1--;
while (r1 >= 0 && pf[r1] + pf[i] > u)
r1--;

while (r2 < n - 1 && ((pf[i] - pf[r2 + 1]) >> 1) >= l)


r2++;
while (l2 < n && ((pf[i] - pf[l2]) >> 1) > r)
l2++;

int j1 = max(l1, l2), j2 = min(r1, r2);

if (j1 > j2) continue;

if (j1 == j2)
{
if (j1 != i)
return 1;
}
else return 1;
}

return 0;
}

long long find_shortcut(int N, std::vector<int> l,


std::vector<int> d, int C)
{
n = N;
CAPITOLUL 4. IOI 2016 4.3. SHORTCUT 466

c = C * 2;

ll mex = 0;

pf[0] = 0;

for (int i = 0; i < n - 1; i++)


//for (register int i = 0; i < n - 1; i++)
{
pf[i + 1] = pf[i] + l[i] * 2;
dst[i] = d[i] * 2;
mex = max(mex, dst[i]);
}

dst[n - 1] = d[n - 1] * 2;
mex = max(mex, dst[n - 1]);

for (int i = 0; i < n; i++)


//for (register int i = 0; i < n; i++)
{
events[i] = MP(-pf[i] - dst[i], i);
o_events[i]= MP(-pf[i] + dst[i], i);
}

sort(events, events + n);


sort(o_events, o_events + n);

seg_mx[0] = MP(-OO, -OO);


seg_mn[0] = MP(OO, OO);

for (int it = 1; it <= n; it++)


//for (register int it = 1; it <= n; it++)
{
int j = events[it - 1].sd;

pll nw = MP(pf[j] + dst[j], pf[j] - dst[j]);

seg_mx[it] = seg_mx[it - 1];


seg_mn[it] = seg_mn[it - 1];

if (seg_mx[it].ft < nw.ft){


swap(seg_mx[it].ft, seg_mx[it].sd);
seg_mx[it].ft = nw.ft;
} else if (seg_mx[it].sd < nw.ft)
seg_mx[it].sd = nw.ft;

if (seg_mn[it].ft > nw.sd){


swap(seg_mn[it].ft, seg_mn[it].sd);
seg_mn[it].ft = nw.sd;
} else if (seg_mn[it].sd > nw.sd)
seg_mn[it].sd = nw.sd;
}

// smth smaller
ll l1 = 0, r1 = mex + pf[n - 1] / 2;

while (l1 < r1){


ll md = (l1 + r1) >> 1;

if (ok(md * 2))
r1 = md;
else l1 = md + 1;
}

ok(16);

return l1;
}

// BEGIN CUT
int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_8/220", "r", stdin);


std::freopen("8-220.out.txt", "w", stdout);
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 467

int n, c;
scanf("%d%d", &n, &c);

vector<int> l(n - 1);


vector<int> d(n);
for (int i = 0; i < n - 1; i++)
scanf("%d", &l[i]);
for (int i = 0; i < n; i++)
scanf("%d", &d[i]);

auto t2 = clock();

long long t = find_shortcut(n, l, d, c);

auto t3 = clock();

// BEGIN SECRET
puts("14e047d7a2907b9034950b074822b302");
// END SECRET

printf("%lld\n", t);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

cout<<"n = "<<n<<" c = "<<c<<"\n";


cout<<"t = "<<t<<"\n\n";

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// END CUT
/*
n = 1000000 c = 1000
t = 1000857674

t2-t1 = 3.673
t3-t2 = 4.234
t4-t3 = 0

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


Press any key to continue.
*/

4.3.3 *Rezolvare detaliată

4.4 Paint By Numbers


Problema 4 - Paint By Numbers 100 de puncte

Author: Michal Foris̆ek (Slovakia)

Paint By Numbers este un binecunoscut joc. Vom considera varianta simplă unidimensională
a acestui joc. ı̂n acest joc, jucatorul are o linie formată din n celule.
Celulele sunt numerotate de la 0 la n  1 de la stânga la dreapta. Jucătorul trebuie să coloreze
fiecare celulă cu negru sau cu alb. Vom folosi ’X’ pentru a simboliza celulele negre şi ’_’ pentru
a simboliza celulele albe.
Jucătorul primeşte un şir c c0 , ..., ck1  de k numere ı̂ntregi pozitive: indiciile.
El trebuie să coloreze celulele ı̂n aşa fel ı̂ncât celulele negre să formeze exact k blocuri de
celule consecutive. Mai mult, numărul celulelor negre din al i-lea bloc (indexat din 0) din stânga
trebuie sa fie egal cu ci . De exemplu , dacă indiciile sunt c 3, 4, jocul rezolvat trebuie să aibă
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 468

exact două blocuri de celule negre consecutive: unul de lungime 3 şi celălalt de lungime 4. Astfel,
daca n 10 şi c 3, 4, o soluţie care satisface indiciile este ’’_XXX__XXXX’’. Observaţi că
’’XXXX_XXX__’’ nu este o soluţie care satisface indiciile: blocurile de celule negre nu sunt ı̂n
ordinea corectă. De asemenea, ’’__XXXXXXX_’’ nu este o soluţie care satisface indiciile: există
un singur bloc de celule negre, nu două separate.
Se dă un joc Paint By Numbers rezolvat parţial. Adică, se ştie n şi c, şi ı̂n plus se cunoaşte
că anumite celule trebuie să fie negre şi anumite celule trebuie să fie albe. Sarcina voastră este să
deduceţi informaţii suplimentare despre celule.
Mai precis, o soluţie validă este una care satisface indiciile şi respectă culorile din celulele
cunoscute. Pogramul vostru trebuie să găsească celulele care sunt colorate ı̂n negru ı̂n orice
soluţie validă şi celulele care sunt colorate ı̂n alb ı̂n orice soluţie validă.
Se garantează că pentru datele de intrare există ı̂ntotdeauna cel puţin o soluţie validă.
Detalii de implementare
Trebuie să implementaţi următoarea funcţie (metodă):

string solve_puzzle(string s, int[] c).

a s: string de lungime n. Pentru fiecare i (0 & i & n  1) caracterul i este:


` ’X’, dacă celula trebuie să fie neagră,
` ’_’, dacă celula trebuie să fie albă,
` ’.’, dacă nu există informaţii despre celula .

a c: şir de lungime k conţinând indiciile, aşa cum au fost definite anterior,


a funcţia trebuie să returneze un string de lungime n. Pentru fiecare i (0 & i & n  1) caracterul
i din stringul returnat trebuie să fie:
` ’X’, dacă celula i este neagră pentru orice soluţie validă,
` ’_’, dacă celula i este albă pentru orice soluţie validă,
` ’?’, altfel (adică, dacă există două soluţii valide astfel ı̂ncât celula i să fie neagră
pentru una şi albă pentru cealaltă).
În limbajul C signatura funcţiei este un pic diferită:

void solve_puzzle(int n, char* s, int k, int* c, char* result)

` n: lungimea stringului s (numărul de celule),


` k: lungimea şirului c (numărul de indicii),
` ceilalţi parametrii sunt ca mai sus,
` ı̂n loc să returneze un string de caracteres, funcţia trebuie să scrie răspunsul ı̂n stringul
result.
Codurile ASCII ale caracterelor utilizate ı̂n această problemă sunt:
’X’: 88,
’_’: 95,
’.’: 46,
’?’: 63.
Vă rugăm utilizaţi fişierele exemplu pentru detalii de implementare ı̂n limbajul vostru de
programare.
Exemple
Exemplul 1

solve_puzzle("..........", [3, 4])

Acestea sunt toate soluţiile valide ale jocului:


’’XXX_XXXX__’’,
’’XXX__XXXX_’’,
’’XXX___XXXX’’,
’’_XXX_XXXX_’’,
’’_XXX__XXXX’’,
’’__XXX_XXXX’’.
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 469

Se observă că celulele (indexate din 0) cu indicii 2, 6, şi 7 sunt negre ı̂n toate soluţiile.
Toate celelate celule pot fi negre, dar nu neapărat. Deci, răspunsul corect este
’’??X???XX??’’.
Exemplul 2

solve_puzzle("........", [3, 4])

În acest exemplu soluţia este unic determinată deci răspunsul corect este ’’XXX_XXXX’’.
Exemplul 3

solve_puzzle("..._._....", [3])

În acest exemplu se poate deduce că şi celula 4 trebuie să fie albă - nu este posibil să avem trei
celule negre consecutive ı̂ntre poziţiile 3 şi 5. Deci răspunsul corect este
Exemplul 4

solve_puzzle(".X........", [3])

Există doar două soluţii valide care se potrivesc descrierii de mai sus:

’’XXX_______’’,
’’_XXX______’’.

De aceea, răspunsul corect este ’’?XX?______’’.


Subtaskuri
Pentru subtaskurile 1 & k & n, şi 1 & ci & n pentru orice 0 & i & k  1.
1. (7 puncte) n & 20, k 1, s conţine numai ’.’ (empty puzzle),
2. (3 puncte) n & 20, s conţine numai ’.’,
3. (22 puncte) n & 100, s conţine numai ’.’,
4. (27 puncte) n & 100, s conţine numai ’.’ şi ’_’ (informaţii numai despre celule albe),
5. (21 puncte) n & 100,
6. (10 puncte) n & 5 000, k & 100,
7. (10 puncte) n & 200 000, k & 100.

Sample grader
Sample grader-ul citeşte datele de intrare ı̂n următorul format:
` linia 1: stringul s,
` linia 2: ı̂ntregul k urmat de k ı̂ntregi c0 , ..., ck1 .

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2048 MB

4.4.1 Indicaţii de rezolvare

Author: Michal Forišek


Comenius University Bratislava, misof@ksp.sk, country: Slovakia
Subtask 1 is as simple as it gets: just generate and intersect all possible locations of the clue.
20
Subtask 2: if you really have to, you can brute force the 2 configurations and check whether
they match the clues. Your loss (of time), directly solving subtask C is much simpler.
Subtask 3 has a special case, helpfully shown in Example 2.
Except for that special case, we can only deduce black cells. This can be done greedily:
for each clue, find the leftmost and the rightmost valid position of the corresponding block. If
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 470

their intersection is non-empty, those cells are guaranteed to be black. (Proof of a more general
statement is given below.)
The same approach works for subtask 4 as well. Additionally, we are able to deduce some
white cells. One case is shown in Example 3. The same logic has to be applied at the beginning
and at the end of a row. (In fact, the easiest solution is to prepend and append a white cell as a
sentinel.)
Here’s a tricky test case for subtask D: n 13, k 4, c 3; 1; 1; 3, s = "...._..._....".
Correct output: ?XX?_X_X_?XX?.
The tricky part here is that we can deduce the white cell at index 6. (Whenever two consecutive
blocks have a fixed position, all cells between those positions have to be white.)
Subtask 4 was as far as we could get with an easy greedy approach. In order to solve the
general version we will use dynamic programming.
One possible solution looks as follows:
ˆ Step 1: Compute prefix sums of given white cells and given black cells (each sepa- rately).
ˆ Step 2: For each i and j, compute the answer P i; j  to the following question: ”Is there a
valid solution if we only consider the first i clues and the first j cells of the given puzzle (as
if cutting away and discarding the rest)?”
ˆ Base case of the recurrence: If i 0, this is possible iff there is no given black cell among
the first j cells.
ˆ Recursive case: If cell j  1 is forced white, P i; j  P i; j  1. If cell j  1 is forced black,
we verify that it is possible to place the last block there (the number of forced white cells it
overlaps must be zero, the next cell to the left must be able to be white) and if that is the
case, we make a recursive call P i  1; j  c  1 where c was the corresponding clue. Finally,
if cell j  1 isn’t forced, the answer is the logical OR of both above cases.
ˆ Step 3: The same in reverse, i.e., with sufixes of the puzzle and the list of clues.
ˆ Step 4: For each cell, we verify whether it can be white. A cell cannot be white if it is forced
to be black. If that is not the case, we try all possibilities for the number of black blocks to
the left of the cell, and verify each possibility using the data we precomputed in steps 2+3.
ˆ Step 5: For each clue, we mark all the cells where it can be located as cells that can be
black. Suppose we are processing clue t and that its value is ct. For each i we verify whether
the clued block can be placed starting from cell i. This requires a few checks that can all be
done in constant time:
– cells i  1 and i  ct must not be forced black
– cells i through i  ct  1 must not be forced white
– there must be a valid solution for the first t clues and the first i  1 cells
– there must be a valid solution for the last k  t  1 clues and the last n  i  ct  1 cells
The above solution can conveniently be implemented in O nk , where k is the number of clues.
2
Less efficient implementations may run in O n , these should not solve subtask 7.
2 2
Other implementations may run in something like O n ¶C ¶ or in O n¶C ¶ . These should
solve subtask 5, but not subtasks 6 and 7.
Proof of the greedy solution (subtasks 1-4)
Theorem 1. Consider the version of our puzzle where initially each cell is either unknown or
forced white.
Suppose that there is a cell x such that there are two valid solutions in which this cell belongs
to different black blocks. Then there is also a valid solution in which this cell is white.
Proof. Suppose that we have i $ j such that in the first solution the block that covers cell x
corresponds to clue i and in the second solution it corresponds to clue j.
We will now construct a new solution as follows: take the first j  1 black blocks from the
second solution and the remaining blocks from the first solution.
This is a valid solution because in the second solution the first j  1 black blocks are all strictly
to the left of cell x and in the first solution all the remaining blocks are strictly to the right of cell
x. It is also obvious that for this reason cell x is white in the new solution. q.e.d.
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 471

4.4.2 Coduri sursă

Listing 4.4.1: paint c.c


1 #include <assert.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include <time.h>
7
8 void reverse(int *begin, int *end)
9 {
10 --end;
11 while (begin < end)
12 {
13 int t = *begin;
14 *begin = *end;
15 *end = t;
16 ++begin;
17 --end;
18 }
19 }
20
21 #define N 222222
22 #define K 111
23
24 int a[N], ans[N], bs[N], ws[N], dp[2][N][K], sumb[N], sumw[N];
25
26 void solve_puzzle(int n, char *s, int k, int *c, char *result)
27 {
28 int cb = 0;
29 int cw = 0;
30 for (int i = 0; i < n; i++)
31 {
32 if (s[i] == ’X’)
33 {
34 bs[cb++] = i + 1;
35 }
36 if (s[i] == ’_’)
37 {
38 ws[cw++] = i + 1;
39 }
40 }
41
42 n += 2;
43 for (int i = 0; i < n; i++) ans[i] = 0;
44 for (int i = 0; i < 2; i++)
45 {
46 for (int j = 0; j < n; j++)
47 {
48 for (int e = 0; e <= k; e++)
49 {
50 dp[i][j][e] = 0;
51 }
52 }
53 }
54
55 for (int it = 0; it < 3; it++)
56 {
57 for (int i = 0; i < n; i++) sumb[i] = sumw[i] = 0;
58 for (int i = 0; i < cb; i++) sumb[bs[i]]++;
59 for (int i = 0; i < cw; i++) sumw[ws[i]]++;
60 for (int i = 1; i < n; i++)
61 {
62 sumb[i] += sumb[i - 1];
63 sumw[i] += sumw[i - 1];
64 }
65
66 if (it == 2) break;
67
68 dp[it][0][0] = 1;
69 for (int i = 1; i < n; i++)
70 {
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 472

71 for (int j = 0; j <= k; j++)


72 {
73 dp[it][i][j] |= dp[it][i - 1][j] && sumb[i] - sumb[i - 1] == 0;
74 dp[it][i][j] |= j > 0 &&
75 i > c[j - 1] &&
76 sumw[i - 1] - sumw[i - c[j - 1] - 1] == 0 &&
77 sumb[i] - sumb[i - 1] == 0 &&
78 dp[it][i - c[j - 1] - 1][j - 1];
79 }
80 }
81
82 reverse(c, c + k);
83 reverse(bs, bs + cb);
84 reverse(ws, ws + cw);
85 for (int i = 0; i < cb; i++) bs[i] = n - bs[i] - 1;
86 for (int i = 0; i < cw; i++) ws[i] = n - ws[i] - 1;
87 }
88
89 for (int i = 0; i < n - i - 1; i++)
90 {
91 for (int j = 0; j <= k; j++)
92 {
93 int t = dp[1][i][j];
94 dp[1][i][j] = dp[1][n - i - 1][j];
95 dp[1][n - i - 1][j] = t;
96 }
97 }
98
99 for (int i = 1; i < n - 1; i++)
100 {
101 for (int j = 0; j <= k; j++)
102 {
103 if (dp[0][i][j] && dp[1][i][k - j])
104 {
105 ans[i - 1] |= 1;
106 }
107 }
108 }
109
110 for (int i = 0; i < n; i++) a[i] = 0;
111
112 for (int i = 1; i < n; i++)
113 {
114 for (int j = 0; j <= k; j++)
115 {
116 if (j > 0 &&
117 i > c[j - 1] &&
118 sumw[i - 1] - sumw[i - c[j - 1] - 1] == 0 &&
119 sumb[i] - sumb[i - 1] == 0 &&
120 dp[0][i - c[j - 1] - 1][j - 1] &&
121 dp[1][i][k - j])
122 {
123 a[i - c[j - 1]]++;
124 a[i]--;
125 }
126 }
127 }
128
129 for (int i = 1; i < n; i++)
130 {
131 a[i] += a[i - 1];
132 if (a[i] > 0)
133 {
134 ans[i - 1] |= 2;
135 }
136 }
137
138 for (int i = 0; i < n - 2; i++)
139 {
140 if (ans[i] == 1) result[i] = ’_’;
141 else
142 if (ans[i] == 2) result[i] = ’X’;
143 else
144 if (ans[i] == 3) result[i] = ’?’;
145 else assert(0);
146 }
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 473

147 }
148
149 // BEGIN CUT
150 #include <string.h>
151 #include <stdio.h>
152 #include <stdlib.h>
153
154 #define S_MAX_LEN (200 * 1000)
155 char s[S_MAX_LEN + 1];
156
157 int main()
158 {
159 clock_t t1, t2, t3, t4;
160
161 t1=clock();
162
163 //freopen("../tests/subtask_1/001", "r", stdin);
164 //freopen("1-001.out.txt", "w", stdout);
165
166 freopen("../tests/subtask_7/125", "r", stdin);
167 freopen("7-125.out.txt", "w", stdout);
168
169 scanf("%s", s);
170 int n = strlen(s);
171 int k;
172
173 scanf("%d", &k);
174
175 int* c = (int*)malloc(k * sizeof(int));
176
177 for (int i = 0; i < k; i++)
178 {
179 scanf("%d", &c[i]);
180 }
181
182 t2=clock();
183
184 char* result = (char*)malloc((n + 1) * sizeof(char));
185
186 solve_puzzle(n, s, k, c, result);
187
188 t3=clock();
189
190 result[n] = 0;
191
192 // BEGIN SECRET
193 //puts("098d134608c94f7413faac591054ee35");
194 // END SECRET
195
196 printf("%s\n", result);
197
198 t4=clock();
199
200 // reset console output
201 freopen("CON", "w", stdout);
202
203 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
204 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
205 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
206
207 /*
208 printf("%s\n", s);
209 printf("%d ", k);
210 for (int i = 0; i < k; i++)
211 {
212 printf("%d ", c[i]);
213 }
214 printf("\n");
215 printf("%s\n", result);
216 */
217
218 return 0;
219 }
220 // END CUT
221 /*
222 t2-t1 = 0.000000
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 474

223 t3-t2 = 3.219000


224 t4-t3 = 0.000000
225
226 Process returned 0 (0x0) execution time : 3.328 s
227 Press any key to continue.
228
229 argc = 4
230 checker
231 ../tests/subtask_7/125
232 ../tests/subtask_7/125.a
233 7-125.out.txt
234 ----------------------
235 1
236 Correct
237
238 Process returned 0 (0x0) execution time : 0.141 s
239 Press any key to continue.
240 */

Listing 4.4.2: paint iz.cpp


#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
#include <cassert>
#include <algorithm>

#include<ctime>

using namespace std;

std::string solve_puzzle(std::string S, std::vector<int> C)


{
int n = S.length();
vector<int> B, W;
for (int i = 0; i < n; i++)
{
if (S[i] == ’X’)
{
B.push_back(i);
}
if (S[i] == ’_’)
{
W.push_back(i);
}
}
n += 2;
for (int i = 0; i < (int)B.size(); i++) B[i]++;
for (int i = 0; i < (int)W.size(); i++) W[i]++;

int k = C.size();

vector<char> ans(n, 0);

vector<vector<vector<int> > >


dp(2, vector<vector<int> >(n, vector<int>(k + 1, 0)));

vector<int> sumB(n), sumW(n);

for (int it = 0; it < 3; it++)


{
sumB.assign(n, 0);
sumW.assign(n, 0);
for (int x : B) sumB[x]++;
for (int x : W) sumW[x]++;
for (int i = 1; i < n; i++)
{
sumB[i] += sumB[i - 1];
sumW[i] += sumW[i - 1];
}

if (it == 2) break;

dp[it][0][0] = 1;
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 475

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


{
for (int j = 0; j <= k; j++)
{
dp[it][i][j] |= dp[it][i-1][j] && (sumB[i]-sumB[i-1] == 0);
dp[it][i][j] |= j > 0 &&
i > C[j - 1] &&
sumW[i - 1] - sumW[i - C[j - 1] - 1] == 0 &&
sumB[i] - sumB[i - 1] == 0 &&
dp[it][i - C[j - 1] - 1][j - 1];
}
}

reverse(C.begin(), C.end());
reverse(B.begin(), B.end());
reverse(W.begin(), W.end());

for (int i = 0; i < (int)B.size(); i++) B[i] = n - B[i] - 1;


for (int i = 0; i < (int)W.size(); i++) W[i] = n - W[i] - 1;
}

reverse(dp[1].begin(), dp[1].end());
for (int i = 1; i < n - 1; i++)
{
for (int j = 0; j <= k; j++)
{
if (dp[0][i][j] && dp[1][i][k - j])
{
ans[i - 1] |= 1;
}
}
}

vector<int> a(n);
for (int i = 1; i < n; i++)
{
for (int j = 0; j <= k; j++)
{
if (j > 0 &&
i > C[j - 1] &&
sumW[i - 1] - sumW[i - C[j - 1] - 1] == 0 &&
sumB[i] - sumB[i - 1] == 0 &&
dp[0][i - C[j - 1] - 1][j - 1] &&
dp[1][i][k - j])
{
a[i - C[j - 1]]++;
a[i]--;
}
}
}

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


{
a[i] += a[i - 1];
if (a[i] > 0)
{
ans[i - 1] |= 2;
}
}

string res = "";


for (int i = 0; i < n - 2; i++)
{
if (ans[i] == 1) res += ’_’;
else if (ans[i] == 2) res += ’X’;
else if (ans[i] == 3) res += ’?’;
else assert(0);
}

return res;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 476

int main()
{
auto t1=clock();

//freopen("../tests/subtask_1/001", "r", stdin);


//freopen("1-001.out.txt", "w", stdout);

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++) {
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

/*
printf("%s\n", s);
printf("%d ", k);
for (int i = 0; i < k; i++)
{
printf("%d ", c[i]);
}
printf("\n");
printf("%s\n", result);
*/

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 6.432000
t4-t3 = 0.016000

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


Press any key to continue.

argc = 4
checker
../tests/subtask_7/125
../tests/subtask_7/125.a
7-125.out.txt
----------------------
1
Correct

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


Press any key to continue.
*/
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 477

Listing 4.4.3: solve-correct-lc.cpp


#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
#include <string>

#include<ctime>

using namespace std;

vector<int> load_vector()
{
int size; cin >> size;
vector<int> tmp(size);
for (int &x : tmp)
cin >> x;
return tmp;
}

vector< vector<bool> > get_possible_prefixes(const vector<int> &C,


const vector<int> &B,
const vector<int> &W)
{
int L = B.size(), N = C.size();

vector<int> Wsums(L+1,0);
for (int l=0; l<L; ++l)
Wsums[l+1]=Wsums[l]+W[l];

vector< vector<bool> > possible( L+1, vector<bool>(N+1, false) );

possible[0][0] = true;
for (int l=1; l<=L; ++l)
possible[l][0] = possible[l-1][0] && !B[l-1];

for (int n=1; n<=N; ++n)


{
for (int l=1; l<=L; ++l)
{
// may cell l-1 be white?
if (!B[l-1] && possible[l-1][n]) possible[l][n] = true;

// may cell l-1 be black -- i.e., the end of block n-1?


if (!W[l-1])
{
int len = C[n-1];
if (l < len)
continue; // doesn’t fit
if (Wsums[l] - Wsums[l-len] > 0)
continue; // overlaps a forced-white cell
if (l > len && B[l-len-1])
continue; // touches a forced-black cell on the left end
if (possible[max(0,l-len-1)][n-1])
possible[l][n] = true;
}
}
}

return possible;
}

std::string solve_puzzle(std::string S, std::vector<int> C)


{
int L = S.size();
int N = C.size();

vector<int> B(L,0);
for (int i = 0; i < L; i++) B[i] = S[i] == ’X’;

vector<int> W(L,0);
for (int i = 0; i < L; i++) W[i] = S[i] == ’_’;

vector< vector<bool> > can_fit_prefix = get_possible_prefixes(C,B,W);


CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 478

reverse( C.begin(), C.end() );


reverse( B.begin(), B.end() );
reverse( W.begin(), W.end() );

vector< vector<bool> > can_fit_suffix = get_possible_prefixes(C,B,W);

reverse( C.begin(), C.end() );


reverse( B.begin(), B.end() );
reverse( W.begin(), W.end() );

vector<bool> can_be_white(L,false);

for (int l=0; l<L; ++l)


for (int n=0; n<=N; ++n)
if (!B[l] &&
can_fit_prefix[l][n] &&
can_fit_suffix[L-l-1][N-n])
can_be_white[l] = true;

vector<int> Wsums(L+1,0);
for (int l=0; l<L; ++l)
Wsums[l+1]=Wsums[l]+W[l];

vector<int> intervals(L+1,0);

for (int n=0; n<N; ++n)


{
for (int start=0; start+C[n]<=L; ++start)
{
if (start > 0 && B[start-1]) continue;
if (start+C[n]<L && B[start+C[n]]) continue;
if (Wsums[start+C[n]] > Wsums[start]) continue;
if (can_fit_prefix[max(0,start-1)][n] &&
can_fit_suffix[max(0,L-start-C[n]-1)][N-n-1])
{
intervals[start] += 1;
intervals[start+C[n]] -= 1;
}
}
}

vector<bool> can_be_black(L+1,false);

int inside = 0;
for (int l=0; l<L; ++l)
{
inside += intervals[l];
if (inside > 0) can_be_black[l] = true;
}

std::string ans;
for (int l=0; l<L; ++l)
{
if (can_be_black[l] && can_be_white[l]) ans.push_back(’?’);
if (can_be_black[l] && !can_be_white[l]) ans.push_back(’X’);
if (!can_be_black[l] && can_be_white[l]) ans.push_back(’_’);
if (!can_be_black[l] && !can_be_white[l]) ans.push_back(’!’);
}

return ans;
}

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

// BEGIN CUT

int main()
{
auto t1=clock();

//freopen("../tests/subtask_1/001", "r", stdin);


//freopen("1-001.out.txt", "w", stdout);

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 479

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++) {
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

/*
printf("%s\n", s);
printf("%d ", k);
for (int i = 0; i < k; i++)
{
printf("%d ", c[i]);
}
printf("\n");
printf("%s\n", result);
*/

return 0;
}
/*
t2-t1 = 0.015000
t3-t2 = 8.406000
t4-t3 = 0.016000

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


Press any key to continue.

argc = 4
checker
../tests/subtask_7/125
../tests/subtask_7/125.a
7-125.out.txt
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 4.4.4: checkerPaintModificat.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

const string output_secret = "098d134608c94f7413faac591054ee35";


CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 480

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask_7/125", // input
(char*)"../tests/subtask_7/125.a", // rezultat corect
(char*)"7-125.out.txt", // rezultat de verificat
};

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

registerChecker("paint", argc, argv);

//readBothSecrets(output_secret);
//readBothGraderResults();
//compareRemainingLines(3); // incepand cu linia nr 3

compareRemainingLines(1); // incepand cu linia nr 1


}

Listing 4.4.5: paint-65961.cpp


// https://oj.uz/submission/65961

#include "paint.h"
#include <cstdio>
#include <cstdlib>

#include<ctime>

using namespace std;

int a[200002];
bool L[200002][111];
bool R[200002][111];
int yes[200002], no[200002];

std::string solve_puzzle(std::string s, std::vector<int> c)


{
int i, j;
int n = s.size(), m = c.size();
for (i = 0; i < n; i++) a[i + 1] = a[i] + (s[i] == ’_’);
L[0][0] = true;
for (i = 1; i <= n; i++) for (j = 0; j <= m; j++)
{
if (s[i - 1] != ’X’ && L[i - 1][j])
{
L[i][j] = true;
continue;
}
if (j && i >= c[j - 1] &&
a[i] == a[i - c[j - 1]] &&
(j == 1 ? L[i - c[0]][0] : i > c[j - 1] &&
L[i - c[j - 1] - 1][j - 1] &&
s[i - c[j - 1] - 1] != ’X’))
L[i][j] = true;
}

R[n + 1][m] = true;


for (i = n; i >= 1; i--)
for (j = m; j >= 0; j--)
{
if (j < m &&
i + c[j] <= n + 1 &&
a[i - 1] == a[i + c[j] - 1] &&
(j + 1 == m ? R[i + c[j]][j + 1] : i + c[j] < n + 1 &&
R[i + c[j] + 1][j + 1] &&
s[i + c[j] - 1] != ’X’))
{
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 481

R[i][j] = true;
if (j ? i > 1 && L[i-2][j] && s[i-2] != ’X’ : L[i-1][0])
{
yes[i - 1]++;
yes[i + c[j] - 1]--;
}
continue;
}
if (s[i - 1] != ’X’ && R[i + 1][j])
R[i][j] = true;
}
std::string res;
for (i = 0; i < n; i++)
{
yes[i + 1] += yes[i];
if (s[i] != ’X’)
{
for (j = 0; j <= m; j++)
if (L[i][j] && R[i + 2][j])
break;
no[i] = j <= m;
}
res.push_back(yes[i] && no[i] ? ’?’ : yes[i] ? ’X’ : ’_’);
}

return res;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.031000
t3-t2 = 1.594000
t4-t3 = 0.015000

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


CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 482

Press any key to continue.


*/

Listing 4.4.6: paint-66328.cpp


// https://oj.uz/submission/66328

#include <bits/stdc++.h>
#include "paint.h"
using namespace std;

const char anyy = ’.’;


const char white = ’_’;
const char black = ’X’;
const char both = ’?’;
const int N = 200010;
const int K = 110;

int wqs[N], bqs[N];


bool dp[N][K], pb[N][K], pw[N][K], reach[N][K];

string solve_puzzle(string s, vector<int> c)


{
int n = s.size();
int k = c.size();
s.insert(s.begin(), ’\0’);
c.insert(c.begin(), 0);
for (int i = 1; i <= n; ++i)
{
wqs[i] = wqs[i-1];
if (s[i] == white)
++wqs[i];
}

dp[n+1][k+1] = true;
for (int i = n; i >= 1; --i)
{
for (int j = k+1; j >= 1; --j)
{
if (j <= k && (s[i] == black || s[i] == anyy))
{
bool blackstrip = i+c[j]-1 <= n &&
wqs[i+c[j]-1]-wqs[i-1] == 0;
bool nextwhite = i+c[j] > n || s[i+c[j]] != black;
bool nextdp = dp[min(n+1, i+c[j]+1)][j+1];
if (blackstrip && nextwhite && nextdp)
{
pb[i][j] = true;
dp[i][j] = true;
}
}

if ((s[i] == white || s[i] == anyy) && dp[i+1][j])


{
pw[i][j] = true;
dp[i][j] = true;
}
}
}
for (int i = 1; i <= n; ++i)
wqs[i] = 0;

reach[1][1] = true;
for (int i = 1; i <= n+1; ++i)
{
for (int j = 1; j <= k+1; ++j) if (reach[i][j] && dp[i][j])
{
if (pb[i][j])
{
bqs[i] += 1;
bqs[i+c[j]] -= 1;
wqs[i+c[j]] = 1;
reach[min(n+1, i+c[j]+1)][j+1] = true;
}
if (pw[i][j])
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 483

{
wqs[i] = 1;
reach[i+1][j] = true;
}
}
}

string ans(n, ’\0’);


for (int i = 1; i <= n; ++i)
{
bqs[i] += bqs[i-1];
if (wqs[i] && bqs[i]) ans[i-1] = both;
else
if (wqs[i]) ans[i-1] = white;
else ans[i-1] = black;
}

return ans;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 1.781000
t4-t3 = 0.016000

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


Press any key to continue.
*/

Listing 4.4.7: paint-97040.cpp


// https://oj.uz/submission/97040
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 484

#include "paint.h"
#include <bits/stdc++.h>
#define pb push_back
using namespace std;

const int MAXN = 200055;


const int MAXK = 105;

bitset<MAXK> DA[2][MAXN], DB[2][MAXN];


int S[MAXN];

char A[MAXN];
int B[MAXK];

int N, K;

void reverse()
{
reverse(A+1, A+N+1);
reverse(B+1, B+K+1);
}

void run(bitset<MAXK> DA[], bitset<MAXK> DB[])


{
DA[0][0] = true;
for(int i = 1; i <= N; i++)
{
S[i] = S[i-1] + (’_’ == A[i]);
for(int j = 0; j <= K; j++)
DA[i][j] = ’X’ != A[i] && (DA[i-1][j] || DB[i-1][j]);
for(int j = 1; j <= K; j++)
DB[i][j] = B[j] <= i && DA[i-B[j]][j-1] && S[i] == S[i-B[j]];
}
}

string getAns()
{
run(DA[0], DB[0]); reverse();
run(DA[1], DB[1]); reverse();
memset(S, 0, (N+1)<<2);
for(int j = 1; j <= K; j++)
{
for(int s = 1, e = B[j]; e <= N; s++, e++)
{
if(!DB[0][e][j] || !DB[1][N-s+1][K-j+1]) continue;
S[s]++; S[e+1]--;
}
}

string ret;
for(int i = 1; i <= N; i++)
{
S[i] += S[i-1];
if(!S[i])
{
ret.pb(’_’);
continue;
}

bool t = false;
for(int j = 0; j <= K; j++)
{
if(!DA[0][i][j] || !DA[1][N-i+1][K-j]) continue;
t = true;
break;
}
ret.pb(t ? ’?’ : ’X’);
}
return ret;
}

std::string solve_puzzle(std::string s, std::vector<int> c)


{
::N = s.length(); ::K = c.size();
for(int i = 0; i < ::N; i++) ::A[i+1] = s[i];
for(int i = 0; i < ::K; i++) ::B[i+1] = c[i];
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 485

return getAns();
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.031000
t3-t2 = 11.633000
t4-t3 = 0.016000

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


Press any key to continue.
*/

Listing 4.4.8: paint-105782.cpp


// https://oj.uz/submission/105782

#include <bits/stdc++.h>
#include "paint.h"
using namespace std;

const int N = 2e5 + 5;


bool f[N][105], g[N][105], can_b[N], can_w[N];
int c[105], black[N], white[N], t[N], n, k;

string solve_puzzle(string s, vector<int> _c)


{

n = s.size(), k = _c.size();
copy(_c.begin(), _c.end(), c + 1);
s = "." + s + ".";

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


{
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 486

black[i] = black[i - 1] + (s[i] == ’X’);


white[i] = white[i - 1] + (s[i] == ’_’);
}
white[n + 1] = white[n];

f[0][0] = 1;
for(int i = 1; i <= n; ++i)
{
f[i][0] = black[i] == 0;
for(int j = 1; j <= k; ++j)
{
bool w = f[i - 1][j];
bool b = (i >= c[j]) &&
(s[i - c[j]] != ’X’) &&
(white[i] == white[i - c[j]])&&
(i > c[j] ? f[i - c[j] - 1][j - 1] : (j == 1));

f[i][j] = s[i] == ’_’ ? w : s[i] == ’X’ ? b : (w | b);


}
}

g[n + 1][k + 1] = g[n + 2][k + 1] = 1;


for(int i = n; i >= 1; --i)
{
g[i][k + 1] = black[n] == black[i - 1];
for(int j = k; j >= 1; --j)
{
bool w = g[i + 1][j];
bool b = (n - i + 1 >= c[j]) &&
(s[i + c[j]] != ’X’) &&
(white[i - 1] == white[i + c[j] - 1]) &&
g[i + c[j] + 1][j + 1];

g[i][j] = s[i] == ’_’ ? w : s[i] == ’X’ ? b : (w | b);


}
}

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


{
if(s[i] == ’_’) can_w[i] = 1;
else
{
for(int c = 0; c <= k && !can_w[i]; ++c)
{
can_w[i] |= f[i - 1][c] && g[i + 1][c + 1];
}
}

for(int j = 1; j <= k; ++j)


{
if(i >= c[j] &&
s[i - c[j]] != ’X’ &&
s[i + 1] != ’X’ &&
white[i] == white[i - c[j]] &&
(i > c[j] ? f[i - c[j] - 1][j - 1] : (j == 1)) &&
g[i + 2][j + 1])
{
t[i - c[j] + 1]++; t[i + 1]--;
}
}
}

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


{
t[i] += t[i - 1];
can_b[i] = t[i] > 0;
}

string ans = "";


for(int i = 1; i <= n; ++i)
{
if(s[i] != ’.’) ans += s[i];
else
{
if(can_w[i] && can_b[i]) ans += ’?’;
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 487

else ans += (can_w[i] ? ’_’ : ’X’);


}
}

return ans;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 2.203000
t4-t3 = 0.016000

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


Press any key to continue.
*/

Listing 4.4.9: paint-107399.cpp


// https://oj.uz/submission/107399

#include "paint.h"
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5+5;

int n, k, w[N], b[N], sum[N];


bool pre[N][105], suf[N][105], black[N], white[N];
vector<int> c(1);

string solve_puzzle(string s, vector<int> _c)


{
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 488

n = s.length(), k = _c.size();
s = ’#’ + s + ’#’;
for(int i : _c) c.emplace_back(i);
for(int i = 1; i <= n; i++)
{
w[i] += w[i-1] + (s[i] == ’_’);
b[i] += b[i-1] + (s[i] == ’X’);
}

pre[0][0] = 1;
for(int i = 1; i <= n; i++)
{
pre[i][0] = (b[i] == 0);
for(int j = 1; j <= k; j++)
{
if(s[i] != ’X’) pre[i][j] |= pre[i-1][j];
if(s[i] != ’_’ && i >= c[j] &&
s[i-c[j]] != ’X’ && w[i] == w[i-c[j]])
{
if(j == 1) pre[i][j] |= pre[i-c[j]][j-1];
else if(i > c[j]) pre[i][j] |= pre[i-c[j]-1][j-1];
}
}
}

suf[n+1][k+1] = suf[n+2][k+1] = 1;
for(int i = n; i; i--)
{
suf[i][k+1] = (b[n] == b[i-1]);
for(int j = k; j; j--)
{
if(s[i] != ’X’) suf[i][j] |= suf[i+1][j];
if(s[i] != ’_’ && i + c[j] <= n + 1 &&
s[i+c[j]] != ’X’ && w[i-1] == w[i+c[j]-1])
suf[i][j] |= suf[i+c[j]+1][j+1];
}
}

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


{
if(s[i] == ’_’) white[i] = 1;
else
for(int j = 0; j <= k; j++)
white[i] |= (pre[i-1][j] && suf[i+1][j+1]);

if(s[i] == ’_’) continue;

for(int j = 1; j <= k; j++)


if(i >= c[j] && s[i-c[j]] != ’X’&&
s[i+1] != ’X’ && w[i] == w[i-c[j]])
{
if(!suf[i+2][j+1]) continue;
if((j == 1 && pre[i-c[j]][j-1]) ||
(i > c[j] && pre[i-c[j]-1][j-1]))
++sum[i-c[j]+1], --sum[i+1];
}
}

string ans = "";


for(int i = 1; i <= n; i++)
{
sum[i] += sum[i-1];
black[i] |= (sum[i] > 0);

if(s[i] != ’.’) ans += s[i];


else if(black[i] && white[i]) ans += ’?’;
else ans += black[i] ? ’X’ : ’_’;
}

return ans;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 489

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 3.141000
t4-t3 = 0.015000

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


Press any key to continue.
*/

Listing 4.4.10: paint-108251.cpp


// https://oj.uz/submission/108251

#include <bits/stdc++.h>
#include "paint.h"

using namespace std;

const int N = 2e5 + 10;

int n, k, w[N], b[N], sum[N];


bool pre[N][110], suf[N][110], black[N], white[N];
vector<int> c(1);

string solve_puzzle( string s, vector<int> f )


{
n = s.length(), k = f.size();
s = ’#’ + s + ’#’;
for( int i : f ) c.emplace_back( i );
for( int i = 1 ; i <= n ; i++ )
{
w[i] += w[i-1] + ( s[i] == ’_’ );
b[i] += b[i-1] + ( s[i] == ’X’ );
}

pre[0][0] = 1;
for( int i = 1 ; i <= n ; i++ )
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 490

{
pre[i][0] = ( b[i] == 0 );
for( int j = 1 ; j <= k ; j++ )
{
if( s[i] != ’X’ ) pre[i][j] |= pre[i-1][j];
if( s[i] != ’_’ && i >= c[j] &&
s[i-c[j]] != ’X’ && w[i] == w[i-c[j]] )
{
if( j == 1 ) pre[i][j] |= pre[i-c[j]][j-1];
else if( i > c[j] ) pre[i][j] |= pre[i-c[j]-1][j-1];
}
}
}

suf[n+1][k+1] = suf[n+2][k+1] = 1;
for( int i = n ; i >= 1 ; i-- )
{
suf[i][k+1] = ( b[n] == b[i-1] );
for( int j = k ; j >= 1 ; j-- )
{
if( s[i] != ’X’ ) suf[i][j] |= suf[i+1][j];
if( s[i] != ’_’ && i + c[j] <= n + 1 &&
s[i+c[j]] != ’X’ && w[i-1] == w[i+c[j]-1] )
suf[i][j] |= suf[i+c[j]+1][j+1];
}
}

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


{
if( s[i] == ’_’ ) white[i] = 1;
else
for( int j = 0 ; j <= k ; j++ )
white[i] |= ( pre[i-1][j] && suf[i+1][j+1] );

if( s[i] == ’_’ ) continue;

for( int j = 1 ; j <= k ; j++ )


if( i >= c[j] && s[i-c[j]] != ’X’ &&
s[i+1] != ’X’ && w[i] == w[i-c[j]] )
{
if( !suf[i+2][j+1] ) continue;
if( ( j == 1 && pre[i-c[j]][j-1] ) ||
( i > c[j] && pre[i-c[j]-1][j-1] ) )
++sum[i-c[j]+1], --sum[i+1];
}
}

string ans = "";


for( int i = 1 ; i <= n ; i++ )
{
sum[i] += sum[i-1];
black[i] |= ( sum[i] > 0 );
if( s[i] != ’.’ ) ans += s[i];
else
if( black[i] && white[i] ) ans += ’?’;
else ans += black[i] ? ’X’ : ’_’;
}

return ans;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 491

scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 2.855000
t4-t3 = 0.016000

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


Press any key to continue.
*/

Listing 4.4.11: paint-112136.cpp


// https://oj.uz/submission/112136

#include "paint.h"
#include <bits/stdc++.h>

#define MAXN 400007


#define MAXK 107

using namespace std;


bool dpl[MAXK][MAXN],dpr[MAXK][MAXN],mb[MAXN],mc[MAXN];
int nz[MAXN],lw[MAXN],nw[MAXN];

std::string solve_puzzle(std::string s, std::vector<int> c)


{
int n=s.size()+2,k=c.size();
for(int i=2;i<n;i++)
{
if(s[i-2]==’X’) nz[i]=0;
if(s[i-2]==’_’) nz[i]=1;
if(s[i-2]==’.’) nz[i]=2;
}

nz[1]=nz[n]=1;
dpl[0][0]=true;
for(int i=1;i<=n;i++) for(int j=0;j<=k;j++)
{
if(nz[i]) dpl[j][i]=dpl[j][i-1];
if(nz[i]==1) lw[i]=i;
else lw[i]=lw[i-1];
if(j>0 && i>=lw[i]+c[j-1] &&
dpl[j-1][i-c[j-1]-1] && nz[i-c[j-1]])
dpl[j][i]=true;
}

dpr[k+1][n+1]=true;
for(int i=n;i>0;i--) for(int j=k+1;j>0;j--)
{
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 492

if(nz[i]) dpr[j][i]=dpr[j][i+1];
if(nz[i]==1) nw[i]=i;
else nw[i]=nw[i+1];
if(j<=k && i+c[j-1]<=nw[i] &&
dpr[j+1][i+c[j-1]+1] && nz[i+c[j-1]])
dpr[j][i]=true;
}

for(int i=2;i<n;i++)
for(int j=0;j<=k;j++)
if(dpl[j][i-1] && dpr[j+1][i+1]) mb[i-2]=true;

for(int j=1;j<=k;j++)
{
int nd=1;
for(int i=2;i<n;i++)
{
if(dpl[j-1][i-2] && dpr[j+1][i+c[j-1]+1] &&
nz[i-1] && nz[i+c[j-1]] && nw[i]>=i+c[j-1])
nd=i+c[j-1]-1;
if(i<=nd) mc[i-2]=true;
}
}

string res="";
for(int i=0;i<n;i++)
{
if(s[i]!=’.’) res+=s[i];
else
{
if(mb[i] && mc[i]) res+=’?’;
if(mb[i] && !mc[i]) res+=’_’;
if(!mb[i] && mc[i]) res+=’X’;
}
}
return res;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 493

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 1.672000
t4-t3 = 0.016000

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


Press any key to continue.
*/

Listing 4.4.12: paint-130510.cpp


// https://oj.uz/submission/130510

#include "paint.h"
#include <bits/stdc++.h>
using namespace std;

const int N = 2e5+5;

int n, k;
int pref_[N], prefX[N];
int canX[N], can_[N];
bool state[105][N];
char arr[N];
int sz[N];

string solve_puzzle(string s, vector<int> C)


{
n = s.size(), k = C.size();
for(int i = 0; i < n; ++i) arr[i+2] = s[i];
for(int i = 0; i < k; ++i) sz[i+1] = C[i];
for(int i = 2; i <= n+1; ++i)
pref_[i] = pref_[i-1] + (arr[i] == ’_’ ? 1 : 0);
for(int i = 2; i <= n+1; ++i)
prefX[i] = prefX[i-1] + (arr[i] == ’X’ ? 1 : 0);

pref_[n+2] = pref_[n+1], prefX[n+2] = prefX[n+1];


state[0][0] = 1;
for(int i = 1; i <= k; ++i)
{
int lst = -1;
for(int j = sz[i]+1; j <= n+1; ++j)
{
if(state[i-1][j-sz[i]-1]) lst = j-sz[i]-1;
if(lst != -1 && (prefX[j-sz[i]] - prefX[lst] == 0) &&
(pref_[j] - pref_[j-sz[i]] == 0) &&
(prefX[(i == k ? n : j) + 1] - prefX[j] == 0))
state[i][j] = 1;
}
}

for(int i = k; i >= 0; --i)


{
if(i == k) for(int j = n+1; j >= 0; --j)
{
if(state[i][j])
{
canX[j-sz[i]+1]++, canX[j+1]--, can_[j+1]++, can_[n+2]--;
}
}
else
{
int l = n+1, r = n+1;
while(true)
{
if(state[i+1][r])
{
if(r-l <= sz[i+1]) state[i][l--] = false;
else
if(state[i][l])
{
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 494

if(prefX[r-sz[i+1]] - prefX[l] == 0)
canX[l-sz[i]+1]++,
canX[l+1]--,
can_[l+1]++,
can_[r-sz[i+1]+1]--,
l--;
else r--;
}
else l--;
}
else r--;

if(l < 0 || r < 0) break;


}

for(int j = l; j >= 0; --j)


state[i][j] = false;
}
}

for(int j = 2; j <= n+1; ++j)


canX[j] += canX[j-1], can_[j] += can_[j-1];

string str;
for(int j = 2; j <= n+1; ++j)
{
if(canX[j] && can_[j]) str.push_back(’?’);
else
if(canX[j]) str.push_back(’X’);
else str.push_back(’_’);
}

return str;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 495

// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 0.516000
t4-t3 = 0.015000

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


Press any key to continue.
*/

Listing 4.4.13: paint-180292.cpp


// https://oj.uz/submission/180292

#include "paint.h"
#include<bits/stdc++.h>

using namespace std;

int vis[200005][105], dp[200005][105];


string ans, A;
vector<int>seg;
int suf[200005], isi[200005], emptyy[200005];
int n, k;

int f(int i, int lf)


{
if(i>n) return 0;
if(i==n) return lf==k;
if(dp[i][lf]!=-1) return dp[i][lf];

int rs=0;
if(A[i]!=’X’) rs|=f(i+1, lf);
if(lf<k && seg[lf]<=suf[i] && A[i+seg[lf]]!=’X’)
{
if(i+seg[lf]+1<=n)
rs|=f(i+seg[lf]+1, lf+1);
else rs|=f(i+seg[lf], lf+1);
}

return dp[i][lf]=rs;
}

void back(int i, int lf)


{
if(i>=n || vis[i][lf]) return;

vis[i][lf]=1;
if(A[i]!=’X’ && f(i+1, lf))
{
emptyy[i]=1;
back(i+1, lf);
}

if(lf<k &&
seg[lf]<=suf[i] &&
A[i+seg[lf]]!=’X’ &&
f(i+seg[lf]+(i+seg[lf]+1<=n), lf+1))
{
isi[i]++;
isi[i+seg[lf]]--;
emptyy[i+seg[lf]]=1;
back(i+seg[lf]+1, lf+1);
}
}

string solve_puzzle(string s, vector<int>c)


{
A=s;
seg=c;
n=s.size();
k=c.size();
for(int i=n-1; i>=0; i--)
suf[i]=A[i]==’_’?0:1+suf[i+1];
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 496

memset(dp, -1, sizeof(dp));

f(0, 0);
back(0, 0);

int st=0;
ans.resize(n);
for(int i=0; i<n; i++)
{
st+=isi[i];
if(emptyy[i] && st>0) ans[i]=’?’;
else
if(emptyy[i]) ans[i]=’_’;
else
if(st>0) ans[i]=’X’;
}
return ans;
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 0.125000
t4-t3 = 0.016000

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


Press any key to continue.
*/

Listing 4.4.14: paint-204502.cpp


// https://oj.uz/submission/204502

#include "paint.h"
CAPITOLUL 4. IOI 2016 4.4. PAINT BY NUMBERS 497

#include <iostream>
#include<string>
#include<vector>

#include<ctime>

#define MAXN 200005


#define MAXK 105

using namespace std;

int N, K, whitesum[MAXN], canblack[MAXN], black;


bool dp[MAXN][MAXK], done[MAXN][MAXK], canwhite[MAXN];

string s;
vector <int> c;

bool gen(int i, int j)


{
if (i >= N && j == K) {return(1);}
if (i >= N && j < K) {return(0);}
if (done[i][j]) {return(dp[i][j]);}
done[i][j]=true;
if (s[i] != ’X’ && gen(i+1,j))
{
dp[i][j]=true;
canwhite[i]=true;
}
if (j < K && i + c[j] <= N)
{
bool can = true;
if (whitesum[i+c[j]] - whitesum[i] > 0 ||
(i+c[j] != N && s[i+c[j]] == ’X’))
{
can=false;
}
if (can && gen(i+c[j]+1, j+1))
{
dp[i][j]=true;
canblack[i]++;
canblack[i+c[j]]--;
canwhite[i+c[j]]=true;
}
}
return(dp[i][j]);
}

string solve_puzzle(string S, vector<int> C)


{
N=S.size(), K=C.size();
s = S, c = C;
for (int i=1; i<=N; i++)
{
whitesum[i]=whitesum[i-1] + (S[i-1] == ’_’);
}

gen(0,0);

string Sol;
for (int i=0; i<N; i++)
{
black+=canblack[i];
if (black && canwhite[i])
{
Sol.push_back(’?’);
}
else
if (black)
{
Sol.push_back(’X’);
}
else
{
Sol.push_back(’_’);
}
}
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 498

return(Sol);
}

// BEGIN CUT

const int S_MAX_LEN = 200 * 1000;


char buf[S_MAX_LEN + 1];

int main()
{
auto t1=clock();

freopen("../tests/subtask_7/125", "r", stdin);


freopen("7-125.out.txt", "w", stdout);

scanf("%s", buf);
std::string s = buf;
int c_len;
scanf("%d", &c_len);
std::vector<int> c(c_len);
for (int i = 0; i < c_len; i++)
{
scanf("%d", &c[i]);
}

auto t2=clock();

std::string ans = solve_puzzle(s, c);

auto t3=clock();

printf("%s\n", ans.data());

auto t4=clock();

// reset console output


freopen("CON", "w", stdout);

printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);


printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);

return 0;
}
// END CUT
/*
t2-t1 = 0.015000
t3-t2 = 0.047000
t4-t3 = 0.016000

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


Press any key to continue.
*/

4.4.3 *Rezolvare detaliată

4.5 Unscrambling a Messy Bug


Problema 5 - Unscrambling a Messy Bug 100 de puncte

Author: Shi-Chun Tsai (Taiwan)

Ilshat este un programator care lucrează la dezvoltarea unor structuri de date eficiente. ı̂ntr-o
zi, a inventat o nouă structură de date. Această structură de date poate stoca o mulţime de ı̂ntregi
nenegativi pe biţi, unde este o putere a lui 2.
Mai exact, pentru un ı̂ntreg nenegativ .
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 499

Structura de date este goală iniţial. Un program care foloseşte acestă structură trebuie să
respecte următoarele reguli:
Programul poate insera ı̂ntregi de biţi ı̂n structura de date, unul câte unul, utilizând funcţia
add_element(x). Dacă programul ı̂ncearcă să insereze un element care este deja prezent ı̂n
structură, nu se ı̂ntâmplă nimic.
După ce a inserat ultimul element, programul trebuie să apeleze funcţia compile_set()
exact o dată.
În final, programul poate apela funcţia check_element(x)\verb pentru a verifica dacă
elementul este prezent ı̂n structura de date. Această funcţie poate fi apelată de mai multe ori.
Când Ilshat a implementat prima oară această structură de date, a creat un bug ı̂n funcţia
compile_set(). Acest bug reordonează cifrele binare ale fiecărui element din mulţime ı̂n aceeaşi
manieră. Ilshat vrea ca voi să găsiţi exact ı̂n ce fel au fost reordonate cifrele ca urmare a acestui
bug.
Formal, consideraţi o secvenţă ı̂n care fiecare număr de la la apare exact o dată. Numim o
asemenea secvenţă o permutare. Consideraţi un element din mulţime, ale cărui cifre ı̂n binar sunt
(cu fiind cel mai semnificativ bit). Când funcţia compile_set() este apelată, acest element
este ı̂nlocuit de elementul .
Aceeaşi permutare este folosită pentru a reordona cifrele tuturor elementelor.
Permutarea este arbitrară, poate fi inclusiv adevărat că pentru toţi .
Spre exemplu, să presupunem că , şi aţi inserat ı̂n set ı̂ntregii care au ca reprezentări binare
stringurile 0000, 1100 şi 0111. Apelând funcţia compile_set aceste elemente se vor schimba ı̂n
0000, 0101 şi 1110, respectiv.
Sarcina voastră este să scrieţi un program care găseşte premutarea interacţionând cu structura
de date. Acest program ar trebui să facă următoarele lucruri (ı̂n această ordine):
1. să aleagă o mulţime de ı̂ntregi de biţi,
2. să insereze acei ı̂ntregi ı̂n structura de date,
3. să apeleze funcţia compile_set pentru a declanşa bug-ul,
4. să verifice prezenţa unor elemente din mulţimea modificată,
5. să folosească informaţia primită pentru a determina şi a ı̂ntoarce permutarea .

Reţineţi că programul vostru poate apela funcţia compile_set exact o dată.
În plus, numărul de apeluri ale funcţiilor din librărie este limitat. Mai exact, programul poate
` apela add_element de cel mult ori ( de la ”writes”),
` apela check_element de cel mult ori ( de la ”reads”).

Detalii de implementare
Trebuie să implementaţi o funcţie (metodă):

int[] restore_permutation(int n, int w, int r)

` n: numărul de biţi din reprezentarea binară a fiecărui element al mulţimii (de-asemenea,


lungimea lui p).
` w: numărul maxim de apeluri ale funcţiei add_element pe care le poate face programul
vostru.
` r: numărul maxim de apeluri ale funcţiei check_element pe care le poate face programul
vostru.
` funcţia va returna permutarea descoperită, p.

În limbajul C, funcţia are o signatură puţin diferită:

void restore_permutation(int n, int w, int r, int* result)

n, w şi r au aceeaşi semnificaţie ca mai sus.


` funcţia va returna permutarea descoperită, p, stocând-o ı̂n array-ul furnizat, result:
pentru fiecare i, va stoca pi ı̂n result[i].

Funcţiile din librărie


Pentru a interacţiona cu structura de date, programul vostru va folosi următoarele trei funcţii
(metode):
a void add_element(string x)
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 500

Această funcţie inserează elementul descris de x ı̂n mulţime.


` x: un string de caractere ’0’ şi ’1’ semnificând reprezentarea binară a ı̂ntregului care va
fi inserat. Lungimea lui x trebuie să fie egală cu n.
a void compile_set()
Această funcţie trebuie apelată exact o dată. Programul vostru nu poate
apela add_element() după acest apel. Programul vostru nu poate apela
check_element() ı̂nainte de acest apel.
a boolean check_element(string x)
Această funcţie verifică dacă elementul x este ı̂n mulţimea alterată.
` x: un string de caractere ’0’ and ’1’ semnificând reprezentarea binară a ı̂ntregului care
va fi interogat. Lungimea lui x trebuie să fie egală cu n.
` ı̂ntoarce true dacă elementul x este ı̂n setul alterat, şi false altfel.

Dacă programul vostru ı̂ncalcă vreuna din restricţiile de mai sus, rezultatul evaluării va fi
”Wrong Answer”.
Pentru toate stringurile, primul caracter denotă cel mai semnificativ bit al ı̂ntregului core-
spunzător.
Grader-ul fixează permutarea ı̂nainte ca funcţia restore_permutation să fie apelată.
Folosiţi fişierele template furnizate pentru detalii de implementare ı̂n limbajul vostru de pro-
gramare.
Exemple
Grader-ul face următorul apel:

restore_permutation(4, 16, 16).

Avem n 4 şi programul poate face cel mult 16 ”writes” şi 16 ”reads”.
Programul face următoarele apeluri de funcţii:
` add_element("0001")
` add_element("0011")
` add_element("0100")
` compile_set()
` check_element("0001") returnează false
` check_element("0010") returnează true
` check_element("0100") returnează true
` check_element("1000") returnează false check_element("0011") returnează
false
` check_element("0101") returnează false
` check_element("1001") returnează false
` check_element("0110") returnează false
` check_element("1010") returnează true
` check_element("1100") returnează false

O singură permutare respectă răspunsurile furnizate de apelurile funcţiei check_element():


permutarea p 2, 1, 3, 0. Astfel, restore_permutation ar trebui să ı̂ntoarcă [2, 1, 3, 0].
Subtaskuri
1. (20 de puncte) n 8, w 256, r 256, iar pi j i pentru cel mult 2 indici i (0 & i & n  1),
2. (18 puncte) n 32, w 320, r 1024,
3. (11 puncte) n 32, w 1024, r 320,
4. (21 de puncte) n 128, w 1792, r 1792,
5. (30 de puncte) n 128, w 896, r 896.

Sample grader
Sample grader-ul citeşte input-ul ı̂n următorul format:
` linia 1: ı̂ntregii n, w, r,
` linia 2: n ı̂ntregi, reprezentând elementele lui p.

Timp maxim de executare/test: 2.0 secunde


CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 501

Memorie: total 2048 MB

4.5.1 Indicaţii de rezolvare

Author: Shi-Chun Tsai


National Chiao-Tnug University, sctsai@cs.nctu.edu.tw, country: Taiwan
n
Subtask 1 allows you to check all 2 possible elements, so it can be solved by various solutions.
n n n1
For example, you can add 2  1 random elements in your set, check all elements, try all 2
transpositions and check that it will give you the same result.
Subtask 2 may be solved by simple solution, using at most n operations add_element and
n n1
at most 2
operations check_element operations.
a Lets add n elements into the set, i-th elements will have first i bits set to one.

add_element("10000000")
add_element("11000000")
add_element("11100000")
add_element("11110000")
add_element("11111000")
add_element("11111100")
add_element("11111110")
add_element("11111111")
aNow we will get positions of bits one by one. First, lets find the position of bit 0.
This can be done using at most n  1 queries:
check_element("10000000") returned false
check_element("01000000") returned false
check_element("00100000") returned false
check_element("00010000") returned false
check_element("00001000") returned false
check_element("00000100") returned true
a Now we know the position of bit 0 and want to find the position of bit 1. This can be done
using at most n  2 queries:
check_element("10000100") returned false
check_element("01000100") returned false
check_element("00100100") returned false
check_element("00010100") returned true
a And so on, we can find position of i-th bit using n  i  1 queries, so the total number of
n n1
queries in the worst case is 2
496.
Subtask 3 can be solved by several optimizations of the previous algorithm. The simplest
n n1
one is to shuffle the order of bytes. This will give us the average number of queries 4
248,
which was enough to pass the tests.
Subtasks 4 and 5 require O n log n solution, in subtasks 4 you can make at most 2n log n
operations of each type, in subtasks 5 you can make only n log n operations.
Subtask 5 may be solved using divide and conquer technique. Lets try to split all bits into
two halves using n requests, and solve problem for each half independently. In this solution we
will make at most n log2 n operations of each type.
a To split group of bits into two halves, we will add into set n©2 elements, i-th element will
have i-th bit set to one, all other set to zero:
add_element("10000000")
add_element("01000000")
add_element("00100000")
add_element("00010000")
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 502

a After this, we will check n elements with single bit set to one. For example:
check_element("10000000") returned false
check_element("01000000") returned true
check_element("00100000") returned false
check_element("00010000") returned false
check_element("00001000") returned true
check_element("00000100") returned true
check_element("00000010") returned false
check_element("00000001") returned true
a Now we know, which n©2 bits correspond to first n©2 bits. In this example we know, that
bits 1, 4, 5, and 7 correspond to bits 0-3. So now we will solve same problem for them only, and
after that solve problem for other n©2 bits.
a In order to solve problem for some subset of bits, we need to make sure that the elements
we use in different subproblems are distinct. We can achieve this by setting all bits that are not
in our subset to ones. For example, when we want to split bits 03 into halves, we will make the
following operations.
add_element("10001111")
add_element("01001111")

check_element("11110010")
check_element("10111010")
check_element("10110110")
check_element("10110011")

4.5.2 Coduri sursă

Listing 4.5.1: checkerMessyModificat.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

//const string output_secret = "098d134608c94f7413faac591054ee35";

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask_4/014", // input
(char*)"../tests/subtask_4/014.a", // rezultat corect
(char*)"4-014.out.txt", // rezultat de verificat
};

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

registerChecker("paint", argc, argv);

//readBothSecrets(output_secret);
//readBothGraderResults();
//compareRemainingLines(3); // incepand cu linia nr 3

compareRemainingLines(1); // incepand cu linia nr 1


}
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 503

Listing 4.5.2: messy cpp ok.cpp


#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>
#include <algorithm>

#include<ctime>

using namespace std;

vector<int> restore_permutation(int, int, int);

namespace variables
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int readInt()
{
int x;
cin >> x;
return x;
}
}

using namespace variables;

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = readInt();
w = readInt();
r = readInt();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = readInt();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

//printf("098d134608c94f7413faac591054ee35\n");
//if (p == answer) { printf("OK\n"); }
//else { printf("WA\n"); }

printf("%d", answer[0]);
for (int i = 1; i < n; i++)
{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//cout<<"res = "<<res<<"\n";
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 504

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

bool check(const string& x)


{
if (x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
printf("WA\n");
exit(0);
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
printf("WA\n");
exit(0);
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
printf("WA\n");
exit(0);
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (const string& s : set_)
{
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[p[i]];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

int nn;
string address;

void helper(int length, int w)


{
fill(address.begin(), address.end(), ’1’);
for (int i = w; i < nn; i += (1 << length))
{
address[i] = ’0’;
}
int j = w;
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 505

for (int i = 0; i < (nn / (1 << length)); i++)


{
address[j] = ’1’;
if (i % 2 == 1)
{
add_element(address);
}
address[j] = ’0’;
j += 1 << length;
}
}

void doWrites(int n_)


{
nn = n_;
address = string(nn, ’0’);
for (int i = 0; i < nn; i++)
{
fill(address.begin(), address.end(), ’0’);
address[i] = ’1’;
if (i % 2 == 1)
{
add_element(address);
}
}

int x = 1;
int log = 0;
while (x < nn)
{
x *= 2;
log++;
}
for (int length = 1; length < log; length++)
{
for (int i = 0; i < (1 << length); i++)
{
helper(length, i);
}
}
}

void read(const vector<int> &t, vector<int>& answer, int step, int w)


{
if (t.size() == 1)
{
answer[t[0]] = w;
return;
}
vector<int> t0;
vector<int> t1;
fill(address.begin(), address.end(), ’1’);
for (int j : t)
{
address[j] = ’0’;
}
for (int j : t)
{
address[j] = ’1’;
if (!check_element(address))
{
t0.push_back(j);
}
else
{
t1.push_back(j);
}
address[j] = ’0’;
}
read(t0, answer, step + 1, w);
read(t1, answer, step + 1, w + (1 << step));
}

void doReads(int nn, vector<int>& answer)


{
vector<int> order = vector<int>(nn);
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 506

for (int i = 0; i < nn; i++)


{
order[i] = i;
}
read(order, answer, 0, 0);
}

vector<int> restore_permutation(int nn, int w, int r)


{
doWrites(nn);
compile_set();
vector<int> answer(nn);
doReads(nn, answer);
return answer;
}
/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.3: messy iz.cpp


#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int readInt()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

vector<int> restore_permutation(int, int, int);

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = readInt();
w = readInt();
r = readInt();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 507

p[i] = readInt();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

//printf("098d134608c94f7413faac591054ee35\n");
//if (p == answer) { printf("OK\n"); }
//else { printf("WA\n"); }

printf("%d", answer[0]);
for (int i = 1; i < n; i++)
{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

bool check(const string& x)


{
if (x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
printf("WA\n");
exit(0);
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
printf("WA\n");
exit(0);
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
printf("WA\n");
exit(0);
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 508

}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (const string& s : set_)
{
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[p[i]];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

#include <vector>
#include <string>

//#include "messy.h"

using namespace std;

std::vector<int> restore_permutation(int n, int w, int r)


{
for (int len = n / 2; len >= 1; len >>= 1)
{
for (int s = 0; s < n; s += 2 * len)
{
string a = "";
for (int i = 0; i < n; i++) a += ’1’;
for (int i = 0; i < len * 2; i++) a[s + i] = ’0’;
for (int i = 0; i < len; i++)
{
string ss = a;
ss[s + i] = ’1’;
add_element(ss);
}
}
}

compile_set();

vector<vector<int> > can(n);


for (int i = 0; i < n; i++) can[0].push_back(i);
for (int len = n / 2; len >= 1; len >>= 1)
{
for (int s = 0; s < n; s += 2 * len)
{
string a = "";
for (int i = 0; i < n; i++) a += ’1’;
for (int i = 0; i < (int)can[s].size(); i++) a[can[s][i]] = ’0’;

vector<int> c1, c2;


for (int i = 0; i < (int)can[s].size(); i++)
{
string ss = a;
ss[can[s][i]] = ’1’;
if (check_element(ss))
{
c1.push_back(can[s][i]);
}
else
{
c2.push_back(can[s][i]);
}
}

can[s] = c1;
can[s + len] = c2;
}
}

vector<int> ans(n);
for (int i = 0; i < n; i++) ans[can[i][0]] = i;
return ans;
}
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 509

/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.4: messy tourist.cpp


#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int readInt()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

vector<int> restore_permutation(int, int, int);

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = readInt();
w = readInt();
r = readInt();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = readInt();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

//printf("098d134608c94f7413faac591054ee35\n");
//if (p == answer) { printf("OK\n"); }
//else { printf("WA\n"); }

printf("%d", answer[0]);
for (int i = 1; i < n; i++)
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 510

{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

bool check(const string& x)


{
if (x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
printf("WA\n");
exit(0);
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
printf("WA\n");
exit(0);
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
printf("WA\n");
exit(0);
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (const string& s : set_)
{
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[p[i]];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 511

#include <bits/stdc++.h>

using namespace std;

int N;

void put(int i, int j)


{
string s(N, ’0’);
s[i] = s[j] = ’1’;
add_element(s);
}

void put(int i, int j, int k)


{
string s(N, ’0’);
s[i] = s[j] = s[k] = ’1’;
add_element(s);
}

bool check(int i, int j)


{
string s(N, ’0’);
s[i] = s[j] = ’1’;
return check_element(s);
}

bool check(int i, int j, int k)


{
string s(N, ’0’);
s[i] = s[j] = s[k] = ’1’;
return check_element(s);
}

bool g[1234][1234];

vector<int> restore_permutation(int nnn, int w, int r)


{
N = nnn;
int k = 0;
while ((1 << k) != N)
{
k++;
}

if (k < 4)
{
string s(N, ’0’);
for (int i = 0; i < N; i++)
{
s[i] = ’1’;
add_element(s);
}

compile_set();

string t(N, ’0’);


vector <int> ans(N);

for (int i = 0; i < N; i++)


{
for (int j = 0; j < N; j++)
{
if (t[j] != ’0’)
{
continue;
}
t[j] = ’1’;
if (check_element(t))
{
ans[j] = i;
break;
}
t[j] = ’0’;
}
}
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 512

return ans;
}

for (int i = 0; i < k; i++)


{
put(i, i);
}

put(0, 1);
put(0, 2);
put(1, 2);
for (int i = 2; i + 1 < k; i++)
{
put(i, i + 1);
}

put(1, 2, 3);
for (int i = k; i < N; i++)
{
for (int j = 0; j < k; j++)
{
if (i & (1 << j))
{
put(i, j);
}
}
}

compile_set();

vector <int> imp;


for (int i = 0; i < N; i++)
{
if (check(i, i))
{
imp.push_back(i);
}
}

assert(imp.size() == k);
for (int i = 0; i < k; i++)
{
for (int j = i + 1; j < k; j++)
{
g[imp[i]][imp[j]] = g[imp[j]][imp[i]] = check(imp[i], imp[j]);
}
}

bool found = false;


do
{
if (!g[imp[0]][imp[1]] || !g[imp[0]][imp[2]] || !g[imp[1]][imp[2]])
{
continue;
}

bool ok = true;
for (int i = 2; i + 1 < k; i++)
{
if (!g[imp[i]][imp[i + 1]])
{
ok = false;
break;
}
}

if (ok)
{
if (!check(imp[1], imp[2], imp[3]))
{
continue;
}
found = true;
break;
}
} while (next_permutation(imp.begin(), imp.end()));
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 513

assert(found);
vector <int> ans(N, -1);
vector <bool> used(N, false);

for (int i = 0; i < k; i++)


{
ans[imp[i]] = i;
used[i] = true;
}

for (int i = 0; i < N; i++)


{
if (ans[i] != -1)
{
continue;
}

int u = 0;
for (int j = k - 1; j >= 0; j--)
{
int options = 0;
int any = -1;
for (int z = u; z < u + (1 << (j + 1)); z++)
{
if (!used[z])
{
options++;
any = z;
}
}

if (options == 1)
{
u = any;
break;
}
if (check(i, imp[j]))
{
u |= (1 << j);
}
}

ans[i] = u;
used[u] = true;
}

return ans;
}
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.5: messy-21910.cpp


// https://oj.uz/submission/21910

//#include "messy.h"

//#include <vector>
//#include <string>
#include <algorithm>
#include <cassert>

using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 514

#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 515

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------


CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 516

int N;

void add_strings(int L, int R, string base)


{
if(L == R) return;
int mid = (L + R)/2;
for(int i = L; i <= mid; i++)
{
base[i] = ’1’;
add_element(base);
base[i] = ’0’;
}

base = string(N, ’0’);


string left = base, right = base;
for(int i = mid + 1; i <= R; i++) left[i] = ’1’;
for(int i = L; i <= mid; i++) right[i] = ’1’;
add_strings(L, mid, left);
add_strings(mid + 1, R, right);
}

vector<int> ans_rev;

void solve(int L, int R, vector<int> A, vector<int> other)


{
if(L == R)
{
assert(A.size() == 1);
ans_rev[L] = A[0];
return;
}

string convert(N, ’0’);


for(int i: other) convert[i] = ’1’;

vector<int> left, right;


for(int i: A)
{
convert[i] = ’1’;
if(check_element(convert)) left.push_back(i);
else right.push_back(i);
convert[i] = ’0’;
}

int mid = (L + R)/2;


solve(L, mid, left, right);
solve(mid + 1, R, right, left);
}

std::vector<int> restore_permutation(int n, int w, int r)


{
N = n;
add_strings(0, N - 1, string(N, ’0’));

compile_set();

ans_rev.resize(N, -1);
vector<int> A;
for(int i = 0; i < N; i++) A.push_back(i);
solve(0, N - 1, A, {});

vector<int> ans(N);
for(int i = 0; i < N; i++) ans[ans_rev[i]] = i;
return ans;
}
/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.6: messy-23726.cpp


CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 517

// https://oj.uz/submission/23726

#include <bits/stdc++.h>
#include "messy.h"

using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 518

return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 519

string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------

const int N = 135;

// [l, r)
void go (int n, int l, int r)
{
if (l + 1 == r) return;
int m = l + r >> 1;
string s = "";
for (int i = 0; i < n; ++i)
{
s += ’1’;
}
for (int i = l; i < r; ++i)
{
s[i] = ’0’;
}
for (int i = l; i < m; ++i)
{
s[i] = ’1’;
add_element(s);
s[i] = ’0’;
}
go(n, l, m);
go(n, m, r);
}

vector <int> v;

// no contains positions that don’t belong to this range


void kill (int n, int l, int r, string no)
{
if (l + 1 == r)
{
for (int i = 0; i < n; ++i)
{
if (no[i] == ’0’)
{
v[i] = l;
break;
}
}
return;
}

string x = no, y = "";


for (int i = 0; i < n; ++i)
{
y += ’1’;
}

for (int i = 0; i < n; ++i)


{
if (no[i] == ’0’)
{
no[i] = ’1’;
if (check_element(no))
{
x[i] = ’1’;
y[i] = ’0’;
}
no[i] = ’0’;
}
}
int m = l + r >> 1;
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 520

kill(n, l, m, y);
kill(n, m, r, x);
}

vector <int> restore_permutation (int n, int w, int r)


{
go(n, 0, n);
compile_set();
v.resize(n);
string s = "";
for (int i = 0; i < n; ++i)
{
s += ’0’;
}
kill(n, 0, n, s);
return v;
}
/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.7: messy-23992.cpp


// https://oj.uz/submission/23992

#include "messy.h"
#include <bits/stdc++.h>

using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 521

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 522

{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------

//int n, occ[128];
int occ[128];

void solve(int s, int e)


{
if(s == e) return;
string str;
for(int j=0; j<n; j++)
{
str.push_back(’0’);
}
for(int j=s; j<=e; j++)
{
str[j] = ’1’;
}
int m = (s+e)/2;
for(int j=m+1; j<=e; j++)
{
str[j] = ’0’;
add_element(str);
str[j] = ’1’;
}
solve(s, m);
solve(m+1, e);
}

void solve2(int s, int e, vector<int> c)


{
if(s == e)
{
occ[c[0]] = s;
return;
}
int m = (s+e)/2;
string str;
for(int i=0; i<n; i++)
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 523

{
str.push_back(’0’);
}
for(auto &i : c)
{
str[i] = ’1’;
}
vector<int> l, h;
for(auto &j : c)
{
str[j] = ’0’;
if(check_element(str)) h.push_back(j);
else l.push_back(j);
str[j] = ’1’;
}
solve2(s, m, l);
solve2(m+1, e, h);
}

vector<int> restore_permutation(int _n, int w, int r)


{
n = _n;
solve(0, n-1);
compile_set();
vector<int> v;
for(int i=0; i<n; i++) v.push_back(i);
solve2(0, n-1, v);
vector<int> dap;
for(int i=0; i<n; i++) dap.push_back(occ[i]);
return dap;
}
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.8: messy-59231.cpp


// https://oj.uz/submission/59231

#include "messy.h"
#include <bits/stdc++.h>

using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 524

int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 525

{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------

string s;
//int n;
vector<int> ans;

void rec1(int l, int r)


{
if(l==r)
return;
int m=(l+r)/2;
for(int i=0; i<l; ++i)
s[i]=’1’;
for(int i=l; i<=r; ++i)
s[i]=’0’;
for(int i=r+1; i<n; ++i)
s[i]=’1’;
for(int i=l; i<=m; ++i)
{
s[i]=’1’;
add_element(s);
s[i]=’0’;
}
rec1(l, m);
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 526

rec1(m+1, r);
}

void rec2(int l, int r, vector<int> &v)


{
if(l==r)
{
ans[v[0]]=l;
return;
}
int m=(l+r)/2;
for(int i=0; i<n; ++i)
s[i]=’1’;
for(int i : v)
s[i]=’0’;
vector<int> vl, vr;
for(int i : v)
{
s[i]=’1’;
if(check_element(s))
vl.push_back(i);
else
vr.push_back(i);
s[i]=’0’;
}
rec2(l, m, vl);
rec2(m+1, r, vr);
}

vector<int> restore_permutation(int n2, int w, int r)


{
n=n2;
s="";
for(int i=0; i<n; ++i)
s+=’0’;
rec1(0, n-1);
compile_set();
vector<int> v;
for(int i=0; i<n; ++i)
v.push_back(i);
ans=vector<int>(n);
rec2(0, n-1, v);
return ans;
}
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.9: messy-66964.cpp


// https://oj.uz/submission/66964

#include <vector>
#include "messy.h"
#include <string>

using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 527

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 528

std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------

// [l..r)
void Add(int l, int r, string base)
{
if (l + 1 == r) return;
int mid = (l + r) / 2;
for (int i = l; i < mid; ++i)
{
base[i] = ’1’;
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 529

add_element(base.c_str());
base[i] = ’0’;
}

// fill one side, recurse in other side


fill(base.begin() + l, base.begin() + mid, ’1’);
Add(mid, r, base);
fill(base.begin() + l, base.begin() + mid, ’0’);
fill(base.begin() + mid, base.begin() + r, ’1’);
Add(l, mid, base);
}

vector<int> ans;

void Solve(int l, int r, vector<int> miss, string base)


{
if (l + 1 == r)
{
ans[miss[0]] = l;
return;
}
vector<int> grL, grR;
for (int x: miss)
{
base[x] = ’1’;
if (check_element(base.c_str())) grL.push_back(x);
else grR.push_back(x);
base[x] = ’0’;
}
int mid = (l + r) / 2;

// fill one side, recurse in other side


for (int x: grR) base[x] = ’1’;
Solve(l, mid, grL, base);
for (int x: grR) base[x] = ’0’;
for (int x: grL) base[x] = ’1’;
Solve(mid, r, grR, base);
}

vector<int> restore_permutation(int n, int w, int r)


{
Add(0, n, string(n, ’0’));

compile_set();

ans.resize(n);
vector<int> all;
for (int i = 0; i < n; ++i) all.push_back(i);
Solve(0, n, all, string(n, ’0’));
return ans;
}
/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.10: messy-67587.cpp


// https://oj.uz/submission/67587

#include <bits/stdc++.h>

#include "messy.h"
//#include "grader.cpp"

using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 530

#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 531

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------


CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 532

//int n;
string s;
vector <int> ans, vv;

void fun(int l, int r)


{
if(l >= r) return ;
s = "";

for(int i = 0; i < n; i ++)


{
if(l <= i && i <= r)
s += ’0’;
else
s += ’1’;
}

int md = (l + r) >> 1;
for(int i = l; i <= md; i ++)
{
s[i] = ’1’;
add_element(s);
s[i] = ’0’;
}
fun(l, md);
fun(md + 1, r);
}

void rec(int l, int r)


{
if(l >= r)
{
return ;
}
s.resize(n);
for(int i = 0; i < n; i ++)
{
if(l <= i && i <= r)
s[ans[i]] = ’0’;
else
s[ans[i]] = ’1’;
}
vector < int > v1, v2;
int md = (l + r) >> 1;
for(int i = l; i <= r; i ++)
{
s[ans[i]] = ’1’;
if(check_element(s))
v1.push_back(ans[i]);
else
v2.push_back(ans[i]);
s[ans[i]] = ’0’;
}

int p1 = 0, p2 = 0;
for(int i = l; i <= r; i ++)
{
if(i <= md)
{
ans[i] = v1[p1 ++];
}
else
{
ans[i] = v2[p2 ++];
}
}

rec(l, md);
rec(md + 1, r);
}

vector<int> restore_permutation(int NN, int w, int r)


{
n = NN;
ans.resize(n);
vv.resize(n);
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 533

for(int i = 0; i < n; i ++)


ans[i] = i;
fun(0, n - 1);
compile_set();
rec(0, n - 1);
for(int i = 0; i < n; i ++)
{
vv[ans[i]] = i;
}
return vv;
}
/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.11: messy-70792.cpp


// https://oj.uz/submission/70792

#include <vector>
#include<string>
#include "messy.h"
using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 534

std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 535

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------

//int n;
string s[1000];
vector<int> g;
void add_build(int l,int r,int v)
{
if(l==r)
return;
string t;
for(int i=0;i<n;i++)
{
if(i>=l && i<=r)
t+=’0’;
else
t+=’1’;
}
int mid=(l+r)/2;
for(int i=l;i<=mid;i++)
{
t[i]=’1’;
add_element(t);
t[i]=’0’;
}
add_build(l,mid,2*v);
add_build(mid+1,r,2*v+1);
}

void check_build(int l,int r,int v)


{
if(l==r)
{
for(int i=0;i<n;i++)
{
if(s[v][i]==’0’)
{
g[i]=l;
break;
}
}
return;
}

s[2*v]=s[v];
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 536

s[2*v+1]=s[v];
for(int i=0;i<n;i++)
{
if(s[v][i]==’1’)
continue;
s[v][i]=’1’;
if(check_element(s[v]))
s[2*v+1][i]=’1’;
else
s[2*v][i]=’1’;
s[v][i]=’0’;
}

int mid=(l+r)/2;
check_build(l,mid,2*v);
check_build(mid+1,r,2*v+1);
}

std::vector<int> restore_permutation(int N, int w, int r)


{
n=N;
for(int i=0;i<n;i++)
g.push_back(0);
add_build(0,n-1,1);
compile_set();
for(int i=0;i<n;i++)
s[1]+=’0’;
check_build(0,n-1,1);
return g;
}
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.12: messy-71456.cpp


// https://oj.uz/submission/71456

#include <bits/stdc++.h>
#include "messy.h"
using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 537

return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 538

}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------

//int n;
//vector<int> p;

void add(int l, int r)


{
if(l == r) return;
string base(n, ’1’);
int m = l + r >> 1;
for(int i = l; i <= r; i++) base[i] = ’0’;
for(int i = l; i <= m; i++)
{
string s = base;
s[i] = ’1’;
add_element(s);
}
add(l, m);
add(m + 1, r);
}

void solve(int l, int r, vector<int> v)


{
if(l == r)
{
p[v[0]] = l;
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 539

return;
}

int m = l + r >> 1;
string base(n, ’1’);
vector<int> L, R;
for(int i : v) base[i] = ’0’;
for(int i : v)
{
string s = base;
s[i] = ’1’;
if(check_element(s))
L.push_back(i);
else R.push_back(i);
}
solve(l, m, L);
solve(m + 1, r, R);
}

vector<int> restore_permutation(int N, int w, int r)


{
n = N;
add(0, n - 1);
compile_set();
vector<int> v;
for(int i = 0; i < n; i++)
v.push_back(i);
p = vector<int>(n, 0);
solve(0, n - 1, v);
return p;
}
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.5.13: messy-102062.cpp


// https://oj.uz/submission/102062

#pragma GCC optimize("Ofast")


#include <bits/stdc++.h>
#define pb push_back
#define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define F first
#define S second
#define ET cout << "\n"
#define MP make_pair
#define MEM(i,j) memset(i,j,sizeof i)
#define ALL(v) v.begin(),v.end()
#define DB(a,s,e) {for(int i=s;i<e;++i) cout << a[i] << " ";ET;}

using namespace std;

// --------------- begin grader -------------------

#include <vector>
#include <cstdio>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>

#include<ctime>

#include "messy.h"

using namespace std;

namespace helper
{
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 540

set<string> set_;
bool compiled = false;
int n;
vector<int> p;
int w;
int r;

int read_int()
{
int x;
cin >> x;
return x;
}
}

using namespace helper;

// A convenience function.
int get_p(int i)
{
int ret = p[i];
return ret;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_4/014", "r", stdin);


std::freopen("4-014.out.txt", "w", stdout);

n = read_int();
w = read_int();
r = read_int();

p = vector<int>(n);

for (int i = 0; i < n; i++)


{
p[i] = read_int();
}

auto t2 = clock();

vector<int> answer = restore_permutation(n, w, r);

auto t3 = clock();

if (answer.size() != n)
{
printf("WA\n");
return 0;
}

printf("%d", answer[0]);

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


{
printf(" %d", answer[i]);
}
printf("\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
CAPITOLUL 4. IOI 2016 4.5. UNSCRAMBLING A MESSY BUG 541

void wa()
{
printf("WA\n");
exit(0);
}

bool check(const string& x)


{
if ((int)x.length() != n)
{
return false;
}
for (int i = 0; i < n; i++)
{
if (x[i] != ’0’ && x[i] != ’1’)
{
return false;
}
}
return true;
}

void add_element(string x)
{
if (--w < 0 || compiled || !check(x))
{
wa();
}
set_.insert(x);
}

bool check_element(string x)
{
if (--r < 0 || !compiled || !check(x))
{
wa();
}
return set_.count(x);
}

void compile_set()
{
if (compiled)
{
wa();
}
compiled = true;
set<string> compiledSet;
string compiledElement = string(n, ’ ’);
for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
{
string s = *it;
for (int i = 0; i < n; i++)
{
compiledElement[i] = s[get_p(i)];
}
compiledSet.insert(compiledElement);
}
set_ = compiledSet;
}

// --------------- end grader -------------------

typedef long long ll;


typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

#include "messy.h"

string t;
vector<int> ans;

void add(int l,int r)


{
if(l==r) return;
int m=l+r>>1;
CAPITOLUL 4. IOI 2016 4.6. ALIENS 542

for(int i=l;i<=m;++i)
t[i]=’1’,add_element(t),t[i]=’0’;
for(int i=m+1;i<=r;++i)
t[i]=’1’;
add(l,m);
for(int i=m+1;i<=r;++i)
t[i]=’0’;
for(int i=l;i<=m;++i)
t[i]=’1’;
add(m+1,r);
for(int i=l;i<=m;++i)
t[i]=’0’;
}

void query(int l,int r,vector<int> &v)


{
if(l==r) return ans[v[0]]=l,void();
vector<int> L,R;
for(int i=0;i<v.size();++i)
{
t[v[i]]=’1’;
if(check_element(t)) L.pb(v[i]);
else R.pb(v[i]);
t[v[i]]=’0’;
}
int m=l+r>>1;
for(int i:R)
t[i]=’1’;
query(l,m,L);
for(int i:R)
t[i]=’0’;
for(int i:L)
t[i]=’1’;
query(m+1,r,R);
for(int i:L)
t[i]=’0’;
}

vector<int> restore_permutation(int n, int w, int r)


{
vector<int> ls;
ans.resize(n),t.resize(n,’0’);
for(int i=0;i<n;++i)
ls.pb(i);
add(0,n-1);
compile_set();
query(0,n-1,ls);
return ans;
}
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

4.5.3 *Rezolvare detaliată

4.6 Aliens
Problema 6 - Aliens 100 de puncte

Author: Chethiya Abeysinghe (Sri Lanka)

Satelitul nostru a descoperit recent o civilizaţie extraterestră pe o planetă ı̂ndepărtată. Am


obţinut deja o fotografie ı̂n rezoluţie joasă a unei zone pătrate a suprafeţei planetei. Pe fotografie
pot fi observate mai multe semne de prezenţă a unei civilizaţii. Experţii au identificat n puncte de
CAPITOLUL 4. IOI 2016 4.6. ALIENS 543

interes pe fotografie. Punctele sunt numerotate de la 0 la n  1. Acum dorim să obţinem fotografii
de rezoluţie ı̂naltă care să conţină toate aceste n puncte.
Satelitul a divizat aria fotografiei de rezoluţie joasă ı̂ntr-un caroiaj de m pe m celule pătrate
cu laturi unitare. Atât liniile, cât şi coloanele caroiajului sunt numerotate consecutiv de la 0 la
m  1 (pornind din stânga, respectiv -sus). Vom folosi s, t pentru a marca celula din linia s şi
coloana t. Punctul cu numărul i se află ı̂n celula ri , ci . Fiecare celulă poate conţine un număr
arbitrar de asemenea puncte.
Satelitul nostru are o orbită stabilă care trece direct pe diagonala principală a caroiajului.
Diagonala principală este segmentul de linie care uneşte colţul de stı̂ngasus cu colţul de dreapta-
jos al caroiajului. Satelitul poate face fotografii de rezoluţiie ı̂naltă pe orice suprafaţă care satisface
următoarelor condiţii:
` conturul suprafeţei este un pătrat,
` două colţuri diagonal opuse ale pătratului aparţin diagonalei principale a caroiajului,
` Oricare celulă a caroiajului este ı̂n ı̂ntregime sau ı̂n interiorul suprafeţei fotografiate, sau ı̂n
afara ei.

Satelitul poate face cel mult k fotografii de rezoluţie ı̂naltă.


După ce satelitul finalizează procesul de fotografiere, urmează transmiterea către bază a imag-
inii de ı̂naltă rezoluţie pentru fiecare din celulele fotografiate (indiferent dacă celula conţine sau
nu puncte de interes). Datele pentru fiecare celulă fotografiată vor fi transmise o singură dată,
chiar dacă celula a fost fotografiată de mai multe ori.
Aşadar, urmează să fie selectate pentru fotografiere cel mult k suprafeţe pătrate, astfel ı̂ncât:
` fiecare celulă care conţine cel puţin un punct de interes va fi fotografiată cel puţin o dată, şi
` numărul celulelor care vor fi fotografiate cel puţin o dată va fi minimal.

Sarcina ta este să găseşti cel mai mic număr total posibil al celulelor fotografiate.
Detalii de implementare
Trebuie să implementezi următoarea funcţie (metodă):

int64 take_photos(int n, int m, int k, int[] r, int[] c)

` n: numărul punctelor de interes,


` m: numărul de linii (şi coloane) ı̂n caroiaj,
` k: numărul maximal de fotografii care pot fi făcute de satelit,
` r şi c: două array-uri de lungime n descriind coordonatele celulelor din caroiaj care conţin
puncte de interes. Pentru 0 & i & n  1, punctul i de interes este localizat ı̂n celula ri, ci,
` funcţia va returna cel mai mic număr total posibil de celule care sunt fotografiate cel puţin
o dată (ı̂n condiţia ı̂n care fotografiile trebuie să conţină toate punctele de interes).

Te rugăm să foloseşti fişierele-template furnizate pentru detalii de implementare ı̂n limbajul
de programare pe care ı̂l utilizezi.
Exemple
Exemplul 1

take_photos(5, 7, 2, [0, 4, 4, 4, 4], [3, 4, 6, 5, 6])

În acest exemplu avem un caroiaj 7  7 cu 5 puncte de interes. Punctele de interes sunt
localizate ı̂n patru celule disticte: (0,3), (4,4), (4,5) şi (4,6). Poţi face cel mult 2 fotografii de
rezoluţie ı̂naltă.
O soluţie pentru a captura toate cele cinci puncte de interes este de a face două fotografii:
prima, un pătrat de dimensiunea 6  6 conţinând celulele (0,0) şi (5,5), şi a doua, un pătrat de
dimensiunea 3  3 conţinând celulele (4,4) şi (6,6). Dacă satelitul va face aceste două fotografii,
vor fi transmise date despre 41 de celule. Această soluţie nu este optimă.
Soluţia optimă foloseşte o fotografie pentru a captura imaginea unui pătrat 4  4 care va conţine
celulele (0,0) şi (3,3) şi o a doua fotografie pentru a captura imaginea unui pătrat 3  3 care va
conţine celulele (4,4) şi (6,6). Acest rezultat conţine doar 25 de celule fotografiate, rezultat optim.
Astfel take_photos va returna 25.
De remarcat că este suficient ca celula (4,6) să fie fotografiată o singură dată, chiar dacă conţine
două puncte de interes.
CAPITOLUL 4. IOI 2016 4.6. ALIENS 544

Acest exemplu este prezentat ı̂n figurile care urmează. Figura din stânga prezintă caroiajul care
corespunde exemplului. Figura din mijloc prezintă soluţia suboptimală, ı̂n care sunt fotografiate
41 celule. Figura din dreapta prezintă soluţia optimă.

Exemplul 2

take_photos(2, 6, 2, [1, 4], [4, 1])

Aici avem 2 puncte de interes localizate simetric ı̂n celulele (1,4) şi (4,1). Oricare fotofrafie
validă care conţine unul dintre puncte, ı̂l va conţine şi pe celălalt. Prin urmare, este suficient să
fie făcută o singură fotografie.
Figura care urmează prezintă acest exemplu şi soluţia lui optimă. În această soluţie satelitul
captează o singură fotografie cu imagini a 16 celule.

Subtask-uri
Pentru toate subtask-urile, 1 & k & n.
1. (4 puncte) 1 & n & 50, 1 & m & 100, k n,
2. (12 puncte) 1 & n & 500, 1 & m & 1000, pentru toţi i astfel ı̂ncât 0 & i & n  1, ri ci ,
3. (9 puncte) 1 & n & 500, 1 & m & 1000,
4. (16 puncte) 1 & n & 50, 1 & m & 100, 1 & n & 50,
5. (19 puncte) 1 & n & 50 000, 1 & k & 100, 1 & m & 1 000 000,
6. (40 puncte) 1 & n & 100 000, 1 & m & 1 000 000.

Sample grader
` linia 1: ı̂ntregii , şi ,
` linia 2  i (0 & i & n  1): ı̂ntregii ri şi ci .

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2048 MB

4.6.1 Indicaţii de rezolvare

Author: Chethiya Abeysinghe


Forestpin (Pvt) Ltd, chethiya@gmail.com, country: Sri Lanka
Subtask 1. k n means that you can use a photo for each point of interest: the minimum
photo containing point ri ; ci  is the square containing points ri ; ri  and ci ; ci .
Constraints in this subtask were small enough to iterate over all cells in that square and mark
them as photographed. After processing all photos, calculate and return the number of cells which
have been marked at least once.
CAPITOLUL 4. IOI 2016 4.6. ALIENS 545

Subtask 2 required the participants to come up with a dynamic programming solution.


Some important observations:

ˆ If a photo covers two points x; x and y; y , then it also covers all points between them.
ˆ Each photo’s boundary must be equal to some ri .

Now we can treat this problem as a dynamic programming problem: cover n points on line
using k segments such that sum of squares of their lengths is as small as possible. Start with
pre-processing the input data: sort all points by ri and remove duplicates. Notice that each photo
should cover some contiguous set of points.

ˆ Let fi,j be the minimum cost to cover first i points with at most j photos.
ˆ f0,j 0 for all 0 & j & k.
2
ˆ fi,j mint$i ft,j 1  ri1  lt  1 .
ˆ fn,k contains the answer.
ˆ O nk  states, calculating transitions from each state takes O n time.
2
ˆ Overall running time: O n k .

Subtask 3 dropped the ri ci restriction. We’ll describe the similar DP solution for this
subtask. It’s possible to prove that photo containing points x; x and y; y  covers point r; c if
and only if segment min r, c; max r, c is fully contained in segment x, y  (x & y).
So if we consider segments min ri , ci ; max ri , ci  instead of points ri , ci , the problem is
reduced to the following: cover all n segments with k larger segments such that their total area
(considering intersections) is minimized.
If segment Si is included in some other segment Sj , then any photo that covers Sj also covers
Si , so we can safely remove Si . Removing all such segments can be done in O n log n time:

ˆ First, sort all the segments in order of increasing left endpoint.


ˆ In case of equality, sort them in order of decreasing right endpoint.
ˆ Iterate over all segments in this order.
ˆ If the current segment is included in the last non-removed segment, remove it. Otherwise
keep it.

Now, since all left endpoints are increasing and no segment is included in the other, then for
all i $ j  ri $ rj and ci $ cj . Observations and definition of fi,j are almost identical to previous
solution:

ˆ f0,j 0 for all 0 & j &k


2 2
ˆ fi,j mint$i ft,j 1  ri1  lt  1  max 0, rt1  lt  1 (1)
ˆ Last term in this formula accounts for the intersection of segments t  1 and t (t % 0).
ˆ fn,k contains the answer.
2
ˆ Overall running time: O n k .

Subtasks 4 and 5. Here you were required to come up with an optimization of the DP
2
solution described above. Subtask 4 allowed O n  solutions. One possible solution uses the
Knuth’s optimization.
2 2
Define Ai,j as the optimal t in (1) and cost t, i ri1  lt  1  max 0, rt1  lt  1 .
Lemma: Ai,j 1 & Ai,j & Ai1,j
2
This allows us to prune the search space on each step, reducing the running time to O n .
If you calculate fi,j in order of increasing j and decreasing i, then at the moment of calculating
fi,j , values of Ai,j 1 and Ai1,j are already known, so you can only check t " Ai,j 1 ; Ai1,j .
CAPITOLUL 4. IOI 2016 4.6. ALIENS 546

It can be rather dificult to prove the correctness formally, but it’s easy to be convinced this is
true. A possible strategy for the competition would be to implement this solution without a formal
proof, then test the hypothesis on smaller inputs using the solution for subtask 3. It is known
2
that this optimization results in O n  running time. Also, don’t forget about 64 bit integers.
Subtask 5 required a different kind of optimization, running in O nk  or O nk log n time.
Implementing any of the two following optimizations was enough to pass all the tests from this
subgroup.

Divide and Conquer optimization (O nk log n)


Using the fact that Ai1;j & Ai,j and that all f ˜, j  can be calculated from all f ˜, j  1,
we can apply divide and conquer optimization.
Consider this recursive function Calculate j, Imin , Imax , Tmin , Tmax  that calculates all f i, j 
for all i " Imin ; Imax  and a given j using known f ˜, j  1.
function CALCULATE j, Imin , Imax , Tmin , Tmax 
if Imin % Imax then
return
Imid  1
I
2 min
 Imax 
calculate fImid ,j naively, let Topt be the optimal t " T min , Tmax 
CALCULATE j, Imin , Imid  1, Tmin , Topt 
CALCULATE j, Imid  1, Imax , Topt , Tmax 
The initial call to this function will be Calculate j, 1, n, 0, n for all j from 1 to k. The time
speedup comes up from the fact that all naive calculations of fImid ,j on each level of recursion
take O n in total, because each recursive call splits the segment Tmin , Tmax  into 2 (almost)
disjoint segments. The depth of recursion is O log n, so the running time of each Calculate call
is O n log n. After calculating all k layers in O kn log n time, we get the answer.

Convex Hull Trick optimization (O nk )


Another possible optimization is called Convex Hull Trick.
Let’s expand (1):

2 2
fi,j min ft,j 1  ri1  lt  1  max 0, rt1  lt  1
t$i
2 2 2
min ft,j 1  ri1  2 lt  1ri1  lt  1  max 0; rt1  lt  1
t$i
Ci  min Mt ri1  Bt,j
t$i

2 2 2
where Ci ri1 , Mt 2 lt  1, Bt,j ft,j 1  lt  1  max 0, rt1  lt  1 .
We see that the formula can be expressed in terms of minimum of linear functions Mt x  Bt,j ,
evaluated at x ri1 . Notice that as i increases, Mi decreases and ri increases. That allows us to
maintain the lower envelope of these linear functions using a stack and query the minimum value
at given x.
Adding a line and querying a point can be implemented in O 1 amortized time, so the total
running time is O nk . This technique is often referred to as the Convex Hull Trick. We will also
use it to get the 100 point solution for this problem.

Subtask 6. Let’s look at fi,k as a function of k and study the differences between two
adjacent values. The following theorem states that these differences are non-increasing. We’ll call
such functions convex.
Theorem: fi,j 1  fi,j ' fi,j  fi,j 1
Let’s assign some constant penalty C for each photo. The new cost function e fi,j fi,j  jC x
x x
is still convex, because fi,j 1  fi,j fi;j 1  fi,j  C.
Let’s introduce another optimization problem without the restriction on number of photos.

gi
n
x
min fi,k
k 1
n
min fi,k  kC 
k 1

This equation for gi can also be expressed only in terms of previous values of gj (j $ i).
2 2
gi min gt  ri1  lt  1  max 0, rt1  lt  1  C
t$i
CAPITOLUL 4. IOI 2016 4.6. ALIENS 547

Using this formula, all gi can be computed in O n time using Convex Hull Trick optimization,
if all li and ri are sorted beforehand. The solution from subtask 5 can also be modified to find
the minimum number of photos required to achieve the optimum, call it p C .
x x x
Since e f is convex, p C  is also equal to the minimum x such that fn,x  fn,x1 & 0 which is
equivalent to fn,x  fn,x1 & C, so p C  is monotone.
2
Also if we set C 0, optimum value of gn is achieved with the n photos, and if we set C M ,
then the optimal solution only contains one photo.
Combining everything above, we can use binary search to find such Copt that p1 p Copt  ' k
and p2 p Copt  1 & k.
This means that all differences fn,p2  fn,p2 1  ; fn,p2 1  fn,p2 2 , ..., fn,p1 1  fn,p1  are
equal, and fn,j is a linear function of j on this interval. Since the desired value of fn,k is somewhere
in this interval, it’s possible to calculate it just by linear interpolation, because all slopes are equal.
This solution requires sorting the segments once and doing O log m iterations of binary search
to find Copt , each iteration running in linear time.
Total running time: O n log n  n log m.

4.6.2 Coduri sursă

Listing 4.6.1: checkerAliens.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

long long readLong(InStream& in)


{
//string secret = in.readLine();
//if (secret != "098d134608c94f7413faac591054ee35")
//{
// in.quitf(_sv, "Wrong secret!");
//}

long long res = in.readLong();


return res;
}

int main()
//int main(int argc, char* argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask_6/175", // input
(char*)"../tests/subtask_6/175.a", // rezultat corect
(char*)"6-175.out.txt", // rezultat de verificat
};

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

registerTestlibCmd(argc, argv);

long long pans = readLong(ouf);


long long jans = readLong(ans);

if (pans != jans)
{
quitf(_wa, "Wrong answer: output = %lld, expected = %lld", pans, jans);
}
CAPITOLUL 4. IOI 2016 4.6. ALIENS 548

else
{
quitf(_ok, "Correct answer: answer = %lld", pans);
}
}

Listing 4.6.2: alien-bsearch.cpp


#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>

#include<ctime>

using namespace std;

#define ll long long


#define MAXN 1000001
#define pb push_back
#define INFI (˜(1LL<<63))
#define sq(x) ((x) * (x))
#define DELTA 0.000001

struct range
{
ll s, e;
friend bool operator < (const range &a, const range &b)
{
if (a.s == b.s)
return (a.e > b.e) ? true : false;
else
return (a.s < b.s) ? true : false;
}
};

struct point
{
ll x, y;
};

struct line
{
ll m, c;
};

struct hull_line
{
ll m, c;
ll k;
};

class Hull
{
private:
int cur, len;
hull_line *L;
double getx(ll m1, ll c1, ll m2, ll c2);

public:
void addline(ll m, ll c, ll k);
pair<ll,ll> getmin(ll x);
void resetline();
Hull();
˜Hull();
};

Hull::Hull()
{
L = (hull_line*)malloc(MAXN * sizeof(hull_line));
}

Hull::˜Hull()
{
free(L);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 549

double Hull::getx(ll m1, ll c1, ll m2, ll c2)


{
return (c2-c1) / (double)(m1-m2);
}

void Hull::addline(ll m, ll c, ll k)
{
while (len >= 2)
{
double a = getx(m, c, L[len-1].m, L[len-1].c);
double b = getx(m, c, L[len-2].m, L[len-2].c);
if (a > b) break;
len--;
}
L[len].m = m;
L[len].c = c;
L[len].k = k;
len++;
}

pair<ll,ll> Hull::getmin(ll x)
{
if (cur >= len) cur = len-1;
while (cur < len-1)
{
double a = getx(L[cur].m, L[cur].c, L[cur+1].m, L[cur+1].c);
if (x > a)
cur++;
else
break;
// in case x==a first such line is considered
// It can be proven using convex property
// that k increase as cur increases
}
return make_pair(L[cur].m * x + L[cur].c, L[cur].k);
}

void Hull::resetline()
{
len = 0;
cur = 0;
}

pair<ll,ll> hull(vector<range> &r, ll C, Hull &h)


{
int n = r.size();
ll m, c;
ll t, lastRes = 0LL, lastK = 0LL;
h.resetline();
pair<ll,ll> res;
for (int i=0; i<n; ++i)
{
m = -2LL * (r[i].s - 1LL);
c = lastRes + sq(r[i].s - 1LL);
if (i > 0)
{
t = max(0LL, r[i-1].e - r[i].s + 1LL);
c -= sq(t);
}
h.addline(m, c, lastK);
res = h.getmin(r[i].e);
lastRes = sq(r[i].e) + C + res.first;
lastK = res.second + 1LL;
}
return make_pair(lastRes, lastK);;
}

ll find(vector<range> &r, int K, int M)


{
Hull h;
int N = r.size();
K = min(K, N);
ll kl=1LL, kr=(ll)N, cl = sq((ll)M), cr=0LL;
ll med;
CAPITOLUL 4. IOI 2016 4.6. ALIENS 550

bool found = false;


pair<ll,ll> res;
if (cl % 2LL == 1LL) cl++; // keep C left to an even integer

// run binary search on C=[cl,cr]


while (kr-kl > 1LL && cl - cr > 1LL)
{
med = cr + (cl-cr)/2LL;
// if (med % 2LL == 1LL) med--; // keep med even
res = hull(r, med + 1LL, h);
if (K == res.second)
{
found = true;
break;
}
if (K < res.second)
{
cr = med;
kr = res.second;
}
else
{
cl = med;
kl = res.second;
}
}

if (found)
{
return res.first - (med+1LL) * K;
}
else
if (kr - kl > 1)
{
// slope is equal from kl to kr.
// Since odd C values are used in hull() it always returns
// leftmost k out of those slopes for given C. i.e. slopes are always even
res = hull(r, cl+1LL, h);
ll fl = res.first - (cl + 1LL) * kl;
res = hull(r, cr+1LL, h);
ll fr = res.first - (cr + 1LL) * kr;
ll h = (fl-fr) / (kr - kl); // interpolate
return fl - (K - kl) * h;
}
else
{
if (kr == K)
{
res = hull(r, cr + 1LL, h);
return res.first - (cr + 1LL) * K;
}
else
{ //kr = K
res = hull(r, cl + 1LL, h);
return res.first - (cl + 1LL) * kl;
}
}
}

vector<range> parseRanges(range* r, int n)


{
sort(&r[0], &r[n]);
vector<range> res;
int cur = 0;
res.pb(r[0]);
for (int i=1; i<n; ++i)
if (r[cur].e < r[i].s || r[cur].e < r[i].e)
{
cur = i;
res.pb(r[i]);
}
return res;
}

long long take_photos(int n, int m, int k, vector<int> row, vector<int> col)


{
CAPITOLUL 4. IOI 2016 4.6. ALIENS 551

point *p = (point*)malloc(MAXN * sizeof(point));


range *r = (range*)malloc(MAXN * sizeof(range));
for (int i=0; i<n; ++i)
{
r[i].s = min(row[i], col[i]);
r[i].e = max(row[i], col[i]);
}
vector<range> R = parseRanges(r, n);
ll res = find(R, k, m);

return res;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.328
t3-t2 = 0.906
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.3: aliens ma nlogm.cpp


#include <cstdio>
#include <algorithm>
#include <vector>
#include <cassert>
#include <iostream>
#include <map>
CAPITOLUL 4. IOI 2016 4.6. ALIENS 552

#include<ctime>

using namespace std;

typedef long long llong;


const llong linf = 1e13;
const int inf = 1e9;

llong sqr(int x)
{
return 1ll * x * x;
}

struct vt
{
llong x, y;
int num = -1;
vt(llong _x, llong _y)
{
x = _x, y = _y;
}
vt() {}
friend vt operator -(vt a, vt b)
{
return vt(a.x - b.x, a.y - b.y);
}
friend llong operator ˆ(vt a, vt b)
{
return a.x * b.y - b.x * a.y;
}
friend llong operator *(vt a, vt b)
{
return a.x * b.x + a.y * b.y;
}
};

long long take_photos(int n, int m, int k, std::vector<int> row,


std::vector<int> column)
{
map<int, int> M;
for (int i = 0; i < n; i++)
{
int x, y;
x = row[i];
y = column[i];
if (x < y)
swap(x, y);
if (!M.count(x))
M[x] = x;
M[x] = min(M[x], y);
}

vector<int> L, R;
for (auto pr : M)
{
while (!L.empty() && L.back() >= pr.second)
L.pop_back(), R.pop_back();
R.push_back(pr.first);
L.push_back(pr.second);
}

n = L.size();
assert(is_sorted(R.begin(), R.end()));
assert(is_sorted(L.begin(), L.end()));

llong ans = linf;


llong a = -1, b = (llong)m * m + 1;
while (b - a > 1)
{
llong x = (a + b) / 2;
vector<vt> st;
int pt = 0;
llong val = 0;
for (int i = 0; i <= (int)L.size(); i++)
{
val = 0;
CAPITOLUL 4. IOI 2016 4.6. ALIENS 553

if (i)
{
int r2 = R[i - 1];
vt dir(1, r2);
while (pt + 1 < (int)st.size())
{
llong scal1 = st[pt] * dir;
llong scal2 = st[pt + 1] * dir;
if (scal2 < scal1)
pt++;
else
break;
}
val = st[pt] * dir + x + (llong)r2 * r2;
}

int r1 = i ? R[i - 1] : -1;


int l = L[i] - 1;
vt cur(val + (llong)l * l - sqr(max(0, r1 - l)), -2 * l);
cur.num = (i == 0) ? 0 : st[pt].num + 1;
while (st.size() >= 2 &&
((st[(int)st.size() - 1] -
st[(int)st.size() - 2]) ˆ
(cur - st[(int)st.size() - 1])) < 0)
st.pop_back();

st.push_back(cur);
pt = min(pt, (int)st.size() - 1);
}

int cnt = st.back().num;


if (cnt > k)
a = x;
else {
ans = val;
b = x;
}
}
ans -= b * k;

return ans;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 554

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.312
t3-t2 = 1.609
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.4: aliens ma nlogm double.cpp


#include <cstdio>
#include <algorithm>
#include <vector>
#include <cassert>
#include <iostream>
#include <map>

#include<ctime>

using namespace std;

typedef long long llong;


const llong linf = 1e13;
const int inf = 1e9;

llong sqr(int x)
{
return 1ll * x * x;
}

struct vt
{
double x, y;
int num = -1;
vt(double _x, double _y)
{
x = _x, y = _y;
}
vt() {}
friend vt operator -(vt a, vt b)
{
return vt(a.x - b.x, a.y - b.y);
}
friend llong operator ˆ(vt a, vt b)
{
return a.x * b.y - b.x * a.y;
}
friend llong operator *(vt a, vt b)
{
return a.x * b.x + a.y * b.y;
}
double slope()
{
return y / x;
}
};

long long take_photos(int n, int m, int k, vector<int> row, vector<int> column)


{
map<int, int> M;
for (int i = 0; i < n; i++)
{
int x, y;
CAPITOLUL 4. IOI 2016 4.6. ALIENS 555

x = row[i];
y = column[i];
if (x < y)
swap(x, y);
if (!M.count(x))
M[x] = x;
M[x] = min(M[x], y);
}

vector<int> L, R;
for (auto pr : M)
{
while (!L.empty() && L.back() >= pr.second)
L.pop_back(), R.pop_back();
R.push_back(pr.first);
L.push_back(pr.second);
}

n = L.size();
assert(is_sorted(R.begin(), R.end()));
assert(is_sorted(L.begin(), L.end()));

llong ans = linf;


llong a = -1, b = (llong)m * m + 1;
while (b - a > 1)
{
llong x = (a + b) / 2;
vector<vt> st;
int pt = 0;
llong val = 0;
for (int i = 0; i <= (int)L.size(); i++)
{
val = 0;
if (i)
{
int r2 = R[i - 1];
vt dir(1, r2);
while (pt + 1 < (int)st.size())
{
llong scal1 = st[pt] * dir;
llong scal2 = st[pt + 1] * dir;
if (scal2 < scal1)
pt++;
else
break;
}
val = st[pt] * dir + x + (llong)r2 * r2;
}

int r1 = i ? R[i - 1] : -1;


int l = L[i] - 1;
vt cur(val + (llong)l * l - sqr(max(0, r1 - l)), -2 * l);
cur.num = (i == 0) ? 0 : st[pt].num + 1;
while (st.size() >= 2 &&
(st[(int)st.size() - 1] -
st[(int)st.size() - 2]).slope() >
(cur - st[(int)st.size() - 1]).slope())
st.pop_back();

st.push_back(cur);
pt = min(pt, (int)st.size() - 1);
}

int cnt = st.back().num;


if (cnt > k)
a = x;
else
{
ans = val;
b = x;
}
}
ans -= b * k;

return ans;
}
CAPITOLUL 4. IOI 2016 4.6. ALIENS 556

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
return 0;
}

// END CUT
/*
t2-t1 = 0.343
t3-t2 = 1.719
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.5: alien-32012.cpp


// https://oj.uz/submission/32012

#include "aliens.h"

#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>

#include<ctime>

using namespace std;

typedef long long llong;


typedef long double ld;

struct point
{
int x, y;
bool operator<(const point &p) const
{
CAPITOLUL 4. IOI 2016 4.6. ALIENS 557

return x < p.x || (x == p.x && y > p.y);


}
};

int n, m, k;
vector<point> _ps;
vector<point> ps;

llong sqr(int x)
{
return (llong)x * x;
}

struct line
{
int cnt;
llong m, b;
} st[100000];

int top, bot;

void push(line x)
{
while (top > bot && (ld)(x.b - st[top].b) / (st[top].m - x.m)
<= (ld)(st[top - 1].b - st[top].b) / (st[top].m - st[top - 1].m))
--top;
st[++top] = x;
}

llong func(line l, int x)


{
return l.m * x + l.b;
}

pair<int, llong> query(int x)


{
while (top > bot && func(st[bot + 1], x) <= func(st[bot], x)) ++bot;
return { st[bot].cnt, func(st[bot], x) };
}

llong dp[100000];
int cnt[100000];

//dp[i] = min(dp[j] + x[j + 1] ˆ 2 - 2 * x[j + 1] * y[i] + y[i] ˆ 2 + cost);

int getPhoto(llong c)
{
top = -1; bot = 0;
push({ 0, -2ll * ps[0].x, sqr(ps[0].x) });
for (int i = 0; i < n; ++i)
{
auto ret = query(ps[i].y + 1);
dp[i] = ret.second + sqr(ps[i].y + 1) + c;
cnt[i] = ret.first + 1;
if (i < n - 1)
push({ cnt[i],
-2ll * ps[i + 1].x,
dp[i] + sqr(ps[i + 1].x) -
sqr(max(0, ps[i].y - ps[i + 1].x + 1)) });
}
return cnt[n - 1];
}

long long take_photos(int _n, int _m, int _k,


vector<int> _r, vector<int> _c)
{
n = _n;
m = _m;
k = _k;

_ps.reserve(n);
ps.reserve(n);

for (int i = 0; i < n; ++i)


{
_ps.push_back({ min(_r[i], _c[i]), max(_r[i], _c[i]) });
CAPITOLUL 4. IOI 2016 4.6. ALIENS 558

sort(_ps.begin(), _ps.end());

for (point i : _ps)


{
if (ps.empty() || ps.back().x < i.x && ps.back().y < i.y)
ps.push_back(i);
}

n = ps.size();
k = min(n, k);
llong s = 0ll, e = (llong)m * m;
int l = -1, r = n + 1;
llong lvalue, rvalue;

while (s <= e)
{
llong m = (s + e) / 2;
int ret = getPhoto(m);
if (ret == k) return dp[n - 1] - ret * m;
if (ret < k)
{
e = m - 1;
if (l < ret)
{
l = ret; lvalue = dp[n - 1] - ret * m;
}
}
else
{
s = m + 1;
if (ret < r)
{
r = ret; rvalue = dp[n - 1] - ret * m;
}
}
}
return rvalue + ((lvalue - rvalue) / (r - l)) * (r - k);
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

//cout<<"ans = "<<ans<<"\n";
CAPITOLUL 4. IOI 2016 4.6. ALIENS 559

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.328
t3-t2 = 0.609
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.6: alien-43618.cpp


// https://oj.uz/submission/43618

#include <bits/stdc++.h>
#include "aliens.h"

using namespace std;

typedef pair<int,int> pp;


typedef long long ll;

pp po[100010];
ll x[100010];
ll y[100010];
int pn;

ll dp[100010];

int top;
ll grad[100010];
ll yint[100010];
int lid[100010];
void add_line(ll g, ll y, int id)
{
while(top>=2)
{
if((yint[top-2]-yint[top-1])*(grad[top-1]-g)>
(yint[top-1]-y)*(grad[top-2]-grad[top-1])) break;
--top;
}
grad[top] = g;
yint[top] = y;
lid[top] = id;
++top;
}

int bx;
ll f(int p, ll x){ return grad[p]*x + yint[p]; }
inline ll sqr(ll x){ return x*x; }
int lst[100010];

int F(ll cost)


{
top = 0; bx = 0;
add_line(-2*x[0], sqr(x[0]), -1);
for(int i=0; i<pn; ++i)
{
while(bx+1 < top && f(bx, y[i]+1) > f(bx+1, y[i]+1)) ++bx;
dp[i]=f(bx, y[i]+1)+sqr(y[i]+1)+cost;
lst[i]=lid[bx];
if(i+1 < pn)
{
add_line(-2*x[i+1],
dp[i]+sqr(x[i+1])-sqr(max(0ll, y[i]-x[i+1]+1)),
i);
bx = min(bx, top-1);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 560

}
}
int cnt = 0;
for(int i=pn-1; i!=-1; i=lst[i]) ++cnt;
return cnt;
}

long long take_photos(int n, int m, int k,


std::vector<int> r, std::vector<int> c)
{
for(int i=0; i<n; ++i)
{
int x=r[i], y=c[i];
if(x>y) swap(x, y);
po[i]={x, y};
}

sort(po, po+n);

for(int i=0; i<n; ++i)


{
int cx=po[i].first, cy=po[i].second;
if(!pn)
{
x[pn]=cx; y[pn]=cy; ++pn;
continue;
}
if(x[pn-1] == cx) y[pn-1] = cy;
else if(y[pn-1] < cy) x[pn]=cx, y[pn]=cy, ++pn;
}

ll cl = -1, cr = m*1LL*m;
while(cl+1 < cr)
{
ll mid = (cl+cr)/2;
(F(mid) <= k ? cr : cl) = mid;
}

F(cr);
return dp[pn-1]-cr*k;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 561

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.281
t3-t2 = 0.406
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.7: alien-45993.cpp


// https://oj.uz/submission/45993

#include <bits/stdc++.h>
#include "aliens.h"
using namespace std;

typedef long long ll;


typedef pair<int, int> ii;
typedef pair<ll, int> li;

#define sq(a) ((ll)(a) * (a))

struct cht
{
vector<ll> m, b, k; int ptr = 0;
bool bad(int l1, int l2, int l3)
{
return 1.0 * (b[l3] - b[l1]) * (m[l1] - m[l2]) <=
1.0 * (b[l2] - b[l1]) * (m[l1] - m[l3]);
}

void add(ll _m, ll _b, int _k)


{
m.push_back(_m); b.push_back(_b); k.push_back(_k);
int s = m.size();
while(s >= 3 && bad(s - 3, s - 2, s - 1))
{
m.erase(m.end() - 2);
b.erase(b.end() - 2);
k.erase(k.end() - 2);
s--;
}
}

ll f(int i, ll x) { return m[i] * x + b[i]; }

li query(ll x)
{
if(ptr >= m.size()) ptr = m.size() - 1;
while(ptr < m.size() - 1 &&
f(ptr, x) > f(ptr + 1, x)) ptr++;
return { f(ptr, x), k[ptr] };
}
void clear() { m.clear(), b.clear(), k.clear(); ptr = 0; }
} ds;

vector<int> l = {-1}, r = {-1};

li get(ll C)
{
int n = l.size() - 1;
li dp[n + 1];
dp[0] = {0, 0};
ds.clear(); ds.add(-2ll * l[1], sq(l[1]), 0);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 562

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


{
ll x = r[i] + 1;
li tmp = ds.query(x);
dp[i].first = tmp.first + sq(x) + C;
dp[i].second = tmp.second + 1;
if(i == n) break;
ds.add(-2ll * l[i + 1],
dp[i].first +
sq(l[i + 1])-
sq(max(0, r[i] - l[i + 1] + 1)),
dp[i].second);
}
return dp[n];
}

ll take_photos(int n, int m, int K, vector<int> R, vector<int> C)


{
vector<ii> tmp;
for(int i = 0; i < n; i++)
{
if(R[i] > C[i]) swap(R[i], C[i]);
tmp.emplace_back(C[i], R[i]);
}

sort(tmp.begin(), tmp.end());

tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());

for(ii seg : tmp)


{
if(seg.first == r.back()) continue;
while(l.size() && seg.second <= l.back() && r.back() <= seg.first)
r.pop_back(), l.pop_back();
l.push_back(seg.second);
r.push_back(seg.first);
}

ll lo = 0, hi = 1e17, idx = -1;


while(lo < hi)
{
ll mid = lo + hi >> 1;
if(get(mid).second <= K) hi = mid;
else lo = mid + 1;
}
return get(lo).first - lo * K;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 563

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 4.025
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.8: alien-70398.cpp


// https://oj.uz/submission/70398

#include "aliens.h"
#include <bits/stdc++.h>
using namespace std;

#define all(x) x.begin(), x.end()


//#define long long long
#define pii pair<int, int>
#define x first
#define y second

vector<pii> V;

void compress()
{
sort(all(V), [](pii a, pii b)
{ if(a.x == b.x) return a.y > b.y; return a.x < b.x; });
int en = -1;
vector<pii> ret;
for(auto x : V)
if(x.y > en)
en = x.y, ret.emplace_back(x);
V = ret;
}

struct cht
{
struct line
{
long long m, c;
int cnt;
line(long long m, long long c, int cnt) : m(m), c(c), cnt(cnt) {}
long long get(long long x) { return m * x + c; }
};

vector<line> f;
bool bad(line l1, line l2, line l3)
{
return (l1.c - l3.c) * (l2.m - l1.m) <=
(l3.m - l1.m) * (l1.c - l2.c);
}

void update(long long m, long long c, int cnt)


{
line l(m, c, cnt);
while(f.size() >= 2 and bad(f[f.size()-2], f[f.size()-1], l))
f.pop_back();
CAPITOLUL 4. IOI 2016 4.6. ALIENS 564

f.emplace_back(l);
}

int idx;
pair<long long, int> query(long long x)
{
while(idx + 1 < f.size() and f[idx+1].get(x) < f[idx].get(x))
++idx;
return make_pair(f[idx].get(x), f[idx].cnt);
}

void clear()
{
f.clear(), idx = 0;
}
} hull;

long long sq(long long x) { return x * x; }

pair<long long, int> f(long long C)


{
vector<pair<long long, int> > dp(V.size()+1);
hull.clear();
hull.update(-2*(V[0].x-1), sq(V[0].x-1), 0);
for(int i = 1; i <= V.size(); ++i)
{
auto ret = hull.query(V[i-1].y);
dp[i] = make_pair(ret.x + sq(V[i-1].y) + C, ret.y + 1);
if(i != V.size())
hull.update(-2*(V[i].x-1),
sq(V[i].x-1) +
dp[i].x -
sq(max(0, V[i-1].y - V[i].x + 1)),
dp[i].y);
}
return dp.back();
}

long long take_photos(int n, int m, int k,


vector<int> _r, vector<int> c)
{
for(int i = 0; i < n; ++i)
V.emplace_back(min(_r[i], c[i]), max(_r[i], c[i]));
compress();
long long l = 0, r = 1e12;
while(l < r)
{
long long m = l + r >> 1;
if(f(m).y <= k) r = m;
else l = m+1;
}
return f(r).x - r*k;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();
CAPITOLUL 4. IOI 2016 4.6. ALIENS 565

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 1.313
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.9: alien-94585.cpp


// https://oj.uz/submission/94585

#include <bits/stdc++.h>
#include "aliens.h"

using namespace std;

long long take_photos(int n, int m, int k, vector<int> r, vector<int> c)


{
vector<pair<int, int>> p(n);
for (int i = 0; i < n; ++i)
{
p[i].first = min(r[i], c[i]);
p[i].second = max(r[i], c[i]);
}

sort(p.begin(), p.end());

{
vector<pair<int, int>> new_p;
for (int i = 0, j = 0; i < n; i = j)
{
while (j < n && p[j].first == p[i].first)
{
++j;
}
if (new_p.empty() || p[j - 1].second > new_p.back().second)
{
new_p.push_back(p[j - 1]);
}
}
swap(p, new_p);
n = p.size();
}

k = min(k, n);
vector<int> x(n), y(n);

for (int i = 0; i < n; ++i)


{
x[i] = p[i].first;
y[i] = p[i].second;
CAPITOLUL 4. IOI 2016 4.6. ALIENS 566

vector<long long> f(n + 1);


vector<int> g(n + 1);
auto sqr = [&](int x)
{
return (long long) x * x;
};

auto solve = [&](long long c)


{
vector<int> q(n + 1);
int l = 0, r = 0;
auto check = [&](int a, int b, int c)
{
return (f[a] + sqr(x[a]) - f[c] - sqr(x[c])) * (x[b] - x[c]) <
(f[b] + sqr(x[b]) - f[c] - sqr(x[c])) * (x[a] - x[c]);
};

for (int i = 0; i < n; ++i)


{
while (r - l > 1 && check(i, q[r - 1], q[r - 2]))
{
--r;
}

q[r++] = i;
while (r - l > 1 && f[q[l]] + sqr(y[i] - x[q[l]] + 1) >
f[q[l + 1]] + sqr(y[i] - x[q[l + 1]] + 1))
{
++l;
}

f[i + 1] = f[q[l]] + sqr(y[i] - x[q[l]] + 1) + c;


if (i + 1 < n && y[i] >= x[i + 1])
{
f[i + 1] -= sqr(y[i] - x[i + 1] + 1);
}
g[i + 1] = g[q[l]] + 1;
}
};

long long low = 0, high = 1ll << 40;

while (low < high)


{
long long mid = low + high >> 1;
solve(mid);
if (g[n] <= k)
{
high = mid;
}
else
{
low = mid + 1;
}
}

solve(high);
return f[n] - k * high;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
CAPITOLUL 4. IOI 2016 4.6. ALIENS 567

scanf("%d %d", &r[i], &c[i]);


}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 1.282
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.10: alien-96357.cpp


// https://oj.uz/submission/96357

#include<bits/stdc++.h>

#include "aliens.h"

#define MAX_N 100005


#define sq(x) ((x)*(x))
#define inf (1LL<<60)

using namespace std;

typedef long long LL;

struct node
{
LL x,y,i;
}V[MAX_N],line[MAX_N];

LL n,m,k,s,e;
LL dp[MAX_N],from[MAX_N];

bool is_delect(node x,node y,node z)


{
return (x.x-z.x)*(y.y-x.y)>=(x.x-y.x)*(z.y-x.y)?true:false;
}

void add(LL x,LL y,LL i)


{
while(e-s>1 && is_delect(line[e-2],line[e-1],{x,y})) e--;
line[e++]={x,y,i};
}

LL get_val(LL i,LL x){return line[i].x*x+line[i].y;}


CAPITOLUL 4. IOI 2016 4.6. ALIENS 568

LL is_ok(LL x)
{
LL i;
s=e=0;
add(-2*V[1].x,sq(V[1].x)-2*V[1].x,0);
for(i=1;i<=n;i++)
{
while(e-s>1 && get_val(s,V[i].y)>=get_val(s+1,V[i].y)) s++;
dp[i]=get_val(s,V[i].y)+sq(V[i].y+1)+x;
from[i]=line[s].i;
add(-2*V[i+1].x,
sq(V[i+1].x)+
dp[i]-2*V[i+1].x-
sq(max(0LL,V[i].y-V[i+1].x+1)),
i);
}
i=n;
LL c;
for(c=0;i;i=from[i],c++);

return c;
}

long long take_photos(int N, int M, int K,


std::vector<int> r, std::vector<int> c)
{
LL i,x,y;
n=N;
m=M;
k=K;
for(i=1;i<=n;i++)
V[i]={min(r[i-1],c[i-1]),max(r[i-1],c[i-1])};

sort(V+1,
V+n+1,
[&](const node x,const node y)
{
return (x.x==y.x)?x.y>y.y:x.x<y.x;
});
m=n;
n=0;
x=-inf;
for(i=1;i<=m;i++)
{
if(x>=V[i].y) continue;
V[++n]=V[i];
x=V[i].y;
}

LL l,rr;
l=0;
rr=sq((LL)M);
k=min(k,n);
LL mid,ans=0;
while(l<=rr)
{
mid=(l+rr)>>1;
x=is_ok(mid);
if(x==k) return dp[n]-k*mid;
if(x<k) rr=mid-1;
else
{
l=mid+1;
ans=max(ans,dp[n]-k*mid);
}
}
return ans;
}

// BEGIN CUT

int main()
{
auto t1 = clock();
CAPITOLUL 4. IOI 2016 4.6. ALIENS 569

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.312
t3-t2 = 0.438
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.11: alien-97219.cpp


// https://oj.uz/submission/97219

#include "aliens.h"
#include <bits/stdc++.h>

using namespace std;

using ll = long long;


using pll = pair<ll, ll>;
using pli = pair<ll, int>;

#define fi first
#define se second

int c[100009];
ll D[100009];

inline ll p(ll x) { return x*x; }

struct line
{
ll a, b;
int idx;
};

line stk[100009]; int t, j;


CAPITOLUL 4. IOI 2016 4.6. ALIENS 570

inline bool del(line P, line Q, line R)


{
return (R.a - P.a) * (Q.b - R.b) < (R.a - Q.a) * (P.b - R.b);
}

void add(line P)
{
while(1<=t && del(stk[t-1], stk[t], P)) --t, j = min(j, t);
stk[++t] = P;
}

pli DP(vector<pll>& A, ll lambda)


{
int N = (int)A.size() - 1;
for(int i=1; i<N; i++)
D[i] = 1LL * 1e18, c[i] = 0;
t = -1;
add((line){-2LL * A[1].fi, p(A[1].fi) - 2*A[1].fi, 0});
j = 0;
for(int i=1; i<N; i++)
{
while(j<t &&
A[i].se * stk[j].a + stk[j].b >
A[i].se * stk[j+1].a + stk[j+1].b)
++j;

ll nw = A[i].se * stk[j].a +
stk[j].b + p(A[i].se) +
2*A[i].se + 1 +
lambda;

if(D[i] > nw)


D[i] = nw, c[i] = c[stk[j].idx] + 1;

add({-2LL * A[i+1].fi,
D[i] +
p(A[i+1].fi) -
2*A[i+1].fi -
p(max(0LL, A[i].se - A[i+1].fi + 1)),
i});
}

return {D[N-1], c[N-1]};


}

long long take_photos(int n, int m, int k, vector<int> r, vector<int> c)


{
vector<pll> A, B;
for(int i=0; i<n; i++)
{
if(r[i] > c[i]) swap(r[i], c[i]);
A.push_back({r[i], c[i]});
}

sort(A.begin(), A.end(), [&](pll P, pll Q)


{
if(P.se == Q.se) return P.fi > Q.fi;
return P.se < Q.se;
});
B.push_back({-1, -1});
for(int i=0; i<n; i++)
{
while(B.size() && B.back().fi >= A[i].fi)
B.pop_back();
B.push_back(A[i]);
}

B.push_back({0, 0});
long long L = 0, R = 1LL*1e12, f = 0;
while(L <= R)
{
long long m = L+R >> 1, v; int cnt;
tie(v, cnt) = DP(B, m);
if(cnt > k) L = m+1;
else R = m-1, f = v - m * k;
CAPITOLUL 4. IOI 2016 4.6. ALIENS 571

}
return f;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.312
t3-t2 = 0.672
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.12: alien-121473.cpp


// https://oj.uz/submission/121473

#include "aliens.h"
#include <bits/stdc++.h>

#define eb emplace_back
#define sz(V) ((int)(V).size())
#define allv(V) ((V).begin()),((V).end())
#define sorv(V) sort(allv(V))
#define univ(V) (V).erase(unique(allv(V)),(V).end())
#define befv(V) ((V)[sz(V)-2])
#define upmin(a,b) (a)=min((a),(b))
#define INFLL (0x3f3f3f3f3f3f3f3fll)

using namespace std;


CAPITOLUL 4. IOI 2016 4.6. ALIENS 572

typedef long long ll;


typedef pair<int, int> pii;
typedef pair<int, ll> pil;
typedef pair<ll, ll> pll;

ll pw(ll n) { return n*n; }

ll operator * (const pll &a, const pll &b)


{
return a.first*b.second - b.first*a.second;
}

ll ccw(const pll &a, const pll &b, const pll &c)


{
return a*b + b*c + c*a;
}

const int MAXN = 100055;

struct CHT
{
pll P[MAXN];
int I[MAXN], n, pv;

void init() { n = pv = 0; }
void push(ll a, ll b, int c)
{
pll p(a, b);
for(; 1 < n && 0 <= ccw(P[n-2], P[n-1], p); n--);
P[n] = p;
I[n] = c;
n++;
}

ll f(int i, ll X) { return P[i].first * X + P[i].second; }

pil get(ll X)
{
if(n <= pv) pv = n-1;
for(ll nw, nxt; pv+1 < n; pv++)
{
nw = f(pv, X); nxt = f(pv+1, X);
if(nw <= nxt) break;
}
return pil(I[pv], f(pv, X));
}
} cht;

ll C[MAXN], D[MAXN];
int E[MAXN];

int X[MAXN], Y[MAXN];

int N, M, K;

void push(int i)
{
cht.push(-2ll*X[i], D[i-1] + pw(X[i]) - C[i] - 2ll*X[i], i);
}

int f(ll L)
{
cht.init();
for(int i = 1; i <= N; i++)
{
push(i);
pil ret = cht.get(Y[i]);
E[i] = E[ret.first-1] + 1;
D[i] = ret.second + pw(Y[i]+1) + L;
}
return E[N];
}

ll getAns()
{
{
CAPITOLUL 4. IOI 2016 4.6. ALIENS 573

vector<pii> V, T;
for(int i = 1; i <= N; i++)
{
if(X[i] > Y[i]) swap(X[i], Y[i]);
V.eb(X[i], Y[i]);
}

sorv(V);

for(auto &v : V)
{
int x, y; tie(x, y) = v;
if(!T.empty() && T.back().first == x) T.back().second = y;
if(T.empty() || T.back().second < y) T.eb(x, y);
}

N = sz(T);
for(int i = 1; i <= N; i++) tie(X[i], Y[i]) = T[i-1];
}

if(N < K) K = N;

for(int i = 2; i <= N; i++)


C[i] = pw(max(0, Y[i-1] - X[i] + 1));

ll s = 0, e = pw(M)+5;
ll l = -1, ly, r = e+1, ry;

for(ll m; s <= e;)


{
m = (s+e) >> 1;
int t = f(m);
ll y = D[N] - m * t;
if(t == K) return y;
if(K < t)
{
s = m+1;
if(t < r) { r = t; ry = y; }
}
else
{
e = m-1;
if(l < t) { l = t; ly = y; }
}
}

return ry + (ly-ry) / (r-l) * (r-K);


}

long long take_photos(int n, int m, int k,


std::vector<int> r, std::vector<int> c)
{
::N = n;
::M = m;
::K = k;

for(int i = 1; i <= ::N; i++)


{
::X[i] = r[i-1];
::Y[i] = c[i-1];
}
return getAns();
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 574

for (int i = 0; i < n; i++)


{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.3
t3-t2 = 0.781
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.13: alien-166476.cpp


// https://oj.uz/submission/166476

#include "aliens.h"
#include <bits/stdc++.h>

using namespace std;

typedef pair<long long,long long> pp;

#define f first
#define s second

pp a[100005];
long long dp[100005];
pair<pp,int> ss[100005];
int nn;
pp aa[100005];
int p[100005];

inline long double cr(pp x,pp y)


{
if(x.f==y.f)return 0;
return (long double)(y.s-x.s)/(long double)(x.f-y.f);
}

pair<long long,long long> CHT(long long c)


{
int i,j=1;
int t=0;
for(i=1 ; i<=nn ; i++)
{
pair<pp,int> k={{-2*aa[i].f,
CAPITOLUL 4. IOI 2016 4.6. ALIENS 575

aa[i].f*aa[i].f+
dp[i-1]+c-
(long long)(i>=2)*
max((long long)0,aa[i-1].s-aa[i].f)*
max((long long)0,aa[i-1].s-aa[i].f)},
p[i-1]};

while(t>=2 && cr(ss[t].f,ss[t-1].f)>=cr(ss[t].f,k.f))


t--;

ss[++t]=k;
for( ; j<t &&
cr(ss[j].f,ss[j+1].f)<=(long double)aa[i].second ;
j++);

dp[i]=ss[j].f.f*aa[i].s+ss[j].f.s+aa[i].s*aa[i].s;
p[i]=ss[j].s+1;
}

return {p[nn],dp[nn]};
}

long long take_photos(int n, int m, int k,


std::vector<int> r, std::vector<int> c)
{
int i,j=0;
for(i=1 ; i<=n ; i++)
{
a[i]={r[i-1],c[i-1]};
if(a[i].s<a[i].f) swap(a[i].f,a[i].s);
}

sort(a+1,a+n+1);

a[0].f=-1;
a[0].s=-1;
a[n+1].f=-1;
for(i=1 ; i<=n ; i++)
{
if(a[i].s<=a[j].s || a[i].f==a[i+1].f);
else
{
aa[++nn]=a[i];
j=i;
}
}

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


aa[i].f--;

long long ans=0;


long long lef=0;
long long rig=(long long)m*m;

while(rig>=lef)
{
long long mid=(lef+rig)/2;
pp v=CHT(mid);
ans=max(ans,-mid*k+v.s);
if(v.f<=k)rig=mid-1;
else lef=mid+1;
}

return ans;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
CAPITOLUL 4. IOI 2016 4.6. ALIENS 576

scanf("%d %d %d", &n, &m, &k);


std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.359
t3-t2 = 0.812
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.14: alien-171563.cpp


// https://oj.uz/submission/171563

#include "aliens.h"
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const int MAXN = 1e5;

struct Point { ll y, x; };

struct CHT
{
struct Line { ll a, b, k; };

double cross(const Line &p, const Line &q)


{
return (double)(q.b-p.b)/(p.a-q.a);
}

vector<Line> S;

void update(Line p)
{
while(S.size()>1 &&
cross(S[S.size()-1], p) <=
CAPITOLUL 4. IOI 2016 4.6. ALIENS 577

cross(S[S.size()-1], S[S.size()-2]))
S.pop_back();

S.push_back(p);
}

int pos=0;
pll query(ll x)
{
if(S.size()<=pos)
pos=S.size()-1;
else
while(pos+1<S.size() && cross(S[pos], S[pos+1])<=x)
pos++;

return {S[pos].a*x+S[pos].b, S[pos].k};


}

void init()
{
S.clear();
pos=0;
}
} cht;

int N, M, K;
Point B[MAXN+10], A[MAXN+10];
pll dp[MAXN+10];

ll solve(ll lambda)
{
int i, j;
cht.init();
for(i=1; i<=N; i++)
{
dp[i]={2*(A[i].y-A[1].x+1)*(A[i].y-A[1].x+1)+lambda, 1};
if(i!=1)
{
pll val=cht.query(A[i].y);
dp[i]=min(dp[i],
{val.first+2*A[i].y*A[i].y+lambda, val.second+1});
}

cht.update({-2*2*(A[i+1].x-1),
2*(A[i+1].x-1)*(A[i+1].x-1)-
2*max(0ll, A[i].y-A[i+1].x+1)*
max(0ll, A[i].y-A[i+1].x+1)+
dp[i].first,
dp[i].second});
}

return dp[N].second;
}

ll take_photos(int _N, int _M, int _K, vector<int> R, vector<int> C)


{
int i, j, k;
N=_N; M=_M; K=_K;

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


B[i]={min(R[i-1], C[i-1]), max(R[i-1], C[i-1])};
sort(B+1, B+N+1, [&](const Point &p, const Point &q)
{
if(p.y==q.y) return p.x>q.x;
return p.y<q.y;
});

int cnt=1;
ll val=-1;
for(i=1; i<=N; i++)
if(val<B[i].x)
A[cnt++]=B[i], val=B[i].x;

N=cnt-1;
for(i=1; i<=N; i++)
if(A[i].x>A[i].y)
CAPITOLUL 4. IOI 2016 4.6. ALIENS 578

swap(A[i].x, A[i].y);

ll lo=-1, hi=1e15;
while(lo+1<hi)
{
ll mid=lo+hi>>1;
if(solve(mid*2+1)>K) lo=mid;
else hi=mid;
}

solve(hi*2);
ll ans=dp[N].first/2-K*hi;

return ans;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 1.469
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.15: alien-172712.cpp


// https://oj.uz/submission/172712

#include<bits/stdc++.h>
#include<ext/rope>
CAPITOLUL 4. IOI 2016 4.6. ALIENS 579

using namespace std;


using namespace __gnu_cxx;

#define fi first
#define se second
#define fastio ios_base::sync_with_stdio(false);cin.tie(0)
#define fopen freopen("input.txt", "r", stdin)
#define pb push_back
#define prec(a) cout<<fixed;cout.precision(a);
#define all(a) (a).begin(), (a).end()

typedef long long ll;


typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef tuple<int,int,int> tiii;

const ll INF = 1e18;


const int inf = 2e9;

template<class T>
void pr(T t) {cout << t << " ";}

template<class T, class ...Args>


void pr(T a, Args ...args) {cout << a << " ";pr(args...);}

template<class ...Args>
void prl(Args ...args) {pr(args...);cout << endl;}

ll dp[101010];
vector<pll> tmp, p;
int cur=0;
struct Line
{
ll a, b;
int cnt;
Line(){}
Line(ll aa, ll bb, int cc){a=aa;b=bb;cnt=cc;}
};

vector<Line> line;
double cross(Line A, Line B)
{
return (double)(B.b-A.b)/(double)(A.a-B.a);
}

pll get(ll x)
{
while(cur+1<line.size()&&cross(line[cur],line[cur+1])<=(double)x)
cur++;

if(cur>=line.size()) return pll(0,0);

return pll(line[cur].a*x+line[cur].b,line[cur].cnt);
}

void add(Line nl)


{
while(line.size()>=2&&
cross(line[line.size()-2],line[line.size()-1])>=
cross(line[line.size()-2],nl))
line.pop_back();
line.push_back(nl);
}

int get_take(ll c)
{
cur=0;
line.clear();
add(Line(-2*(p[1].fi-1),c+(p[1].fi-1)*(p[1].fi-1),1));

pll t;
for(int i=1;i<(int)p.size();i++)
{
if(i>1)
CAPITOLUL 4. IOI 2016 4.6. ALIENS 580

{
if(p[i].fi<=p[i-1].se)
add(Line(-2*(p[i].fi-1),
c+t.fi+
(p[i].fi-1)*(p[i].fi-1) -
(p[i-1].se-p[i].fi+1)*(p[i-1].se-p[i].fi+1),
t.se+1));
else
add(Line(-2*(p[i].fi-1),
c+t.fi+(p[i].fi-1)*(p[i].fi-1),
t.se+1));
}

t = get(p[i].se);
t.fi+=p[i].se*p[i].se;
dp[i]=t.fi;
}

return (int)t.se;
}

ll take_photos(int n, int m, int k, vector<int>x, vector<int>y)


{
k=min(n,k);
for(int i=0;i<n;i++)
{
if(x[i]>y[i]) swap(x[i], y[i]);
tmp.pb({x[i],y[i]});
}

sort(tmp.begin(), tmp.end(), [](pii a, pii b)


{
if(a.se==b.se)
return a.fi>b.fi;
return a.se<b.se;
});
p.pb({-1,-1});
for(auto i:tmp)
{
while(!p.empty()&&p[p.size()-1].fi>=i.fi)
p.pop_back();
p.pb(i);
}

k=min(k,(int)p.size()-1);
ll s = 0,e=(ll)m*m;
ll l=-1,r=e+1,ly,ry;

while(s<=e)
{
ll mid = (s+e)/2;
int num = get_take(mid);
if(num==k) return dp[p.size()-1]-mid*k;
if(num>k)
{
s = mid+1;
if(r>num) r=num,ry=dp[p.size()-1]-mid*num;
}

if(num<k)
{
e = mid-1;
if(l<num) l=num,ly=dp[p.size()-1]-mid*num;
}
}

if(r==(ll)m*m+1) return ly;


if(l==-1) return ry;
return ry+(ly-ry)/(r-l)*(r-k);
}

// BEGIN CUT

int main()
{
auto t1 = clock();
CAPITOLUL 4. IOI 2016 4.6. ALIENS 581

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 1.188
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.16: alien-173410.cpp


// https://oj.uz/submission/173410

#include "aliens.h"
#include <bits/stdc++.h>

#define all(v) (v).begin(), (v).end()


#define sortv(v) sort(all(v))
#define uniqv(v) (v).erase(unique(all(v)), (v).end())
#define pb push_back
#define FI first
#define SE second
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define test 0
#define TEST if(test)

using namespace std;

typedef long long ll;


typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

const int MOD = 1000000007; // 998244353


CAPITOLUL 4. IOI 2016 4.6. ALIENS 582

const int INF = 2e9;


const ll INFLL = 1e12;
const int MAX_N = 100000;

int N, M, K;
vector<pii> vt;
vector<ll> C;
ll dp[MAX_N+1];

bool sf(pii a, pii b)


{
if(a.first==b.first)
{
return a.second>b.second;
}
return a.first<b.first;
}

struct S
{
ll a, b;
int idx;
};

vector<S> st;
int idx = 0;

void add(ll a, ll b, int i)


{
while(st.size()>=2)
{
S prv = st.back(); st.pop_back();
if((b-prv.b) * (prv.a - st.back().a) >=
(a-prv.a) * (prv.b - st.back().b))
{
if(idx==st.size()) idx--;
continue;
}
else
{
st.pb(prv);
break;
}
}
st.pb({a, b, i});
}

int cnt[MAX_N+1];

ll calc(ll x, int i)
{
while(idx+1<st.size())
{
if(st[idx+1].a * x + st[idx+1].b < st[idx].a * x + st[idx].b)
idx++;
else break;
}

cnt[i] = cnt[st[idx].idx]+1;
return st[idx].a * x + st[idx].b;
}

int solve(ll x)
{
st.clear();
idx = 0;
for(int i=0; i<vt.size(); i++)
{
add(-4LL * (ll)vt[i].first,
dp[i] - 2LL * C[i] + 2LL * (ll)vt[i].first * (ll)vt[i].first,
i);

dp[i+1] = 2LL * (ll)vt[i].second * (ll)vt[i].second +


calc((ll)vt[i].second, i+1) +
x;
}
CAPITOLUL 4. IOI 2016 4.6. ALIENS 583

return cnt[vt.size()];
}

long long take_photos(int n, int m, int k,


std::vector<int> r, std::vector<int> c)
{
N = n; M = m; K = k;
for(int i=0; i<N; i++)
{
vt.pb({min(r[i], c[i]), max(r[i], c[i]) + 1});
}

sort(vt.begin(), vt.end(), sf);

int mx = -1;
for(int i=0 ;i<N; i++)
{
if(mx>=vt[i].second)
{
vt[i].second = INF;
vt[i].first = INF;
}
else
mx = max(mx, vt[i].second);
}

sort(vt.begin(), vt.end());

while(!vt.empty() && vt.back().first==INF && vt.back().second==INF)


vt.pop_back();

C.pb(0LL);
for(int i=1; i<vt.size(); i++)
{
C.pb(max(0LL, (ll)(vt[i-1].second - vt[i].first)) *
max(0LL, (ll)(vt[i-1].second - vt[i].first)));
}

ll s = 0, e = (ll)M*(ll)M, mid;
while(s<e)
{
mid = (s+e)/2LL;
int t = solve(2LL*mid+1LL);
if(t<=K)
{
e = mid;
}
else
{
s = mid+1LL;
}
}

solve(2LL*s);

return (dp[vt.size()] - 2LL * s * (ll)K)/2LL;


}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}
CAPITOLUL 4. IOI 2016 4.6. ALIENS 584

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 1.422
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.17: alien-224940.cpp


// https://oj.uz/submission/224940

#include "aliens.h"
#include <bits/stdc++.h>

#define x first
#define y second
#define all(v) v.begin(), v.end()

using namespace std;

typedef long long ll;


typedef pair<ll, ll> p;

int n, m, k;
ll dp[101010];
int cut[101010];
p a[101010];
vector<p> v;

struct CHT
{
struct Line
{
ll a, b, c;
Line(ll a = 0, ll b = 0, ll c = 0) : a(a), b(b), c(c) {}
ll f(ll x){ return a * x + b; }
};

Line v[101010];
int pv, top;

ll f(int x, ll y){ return v[x].f(y); }

void init(){ pv = top = 0; }

int chk(const Line &a, const Line &b, const Line &c)
CAPITOLUL 4. IOI 2016 4.6. ALIENS 585

{
return (double)(a.b - b.b) / (b.a - a.a) >=
(double)(c.b - b.b) / (b.a - c.a);
}

void update(Line l)
{
while(top >= pv+2 && chk(v[top-2], v[top-1], l)) top--;
v[top++] = l;
}

p query(ll x)
{
while(pv+1 < top && v[pv].f(x) >= v[pv+1].f(x)) pv++;
return {v[pv].f(x), v[pv].c};
}
} cht;

void init(int N, int M, int K, const vector<int> &R, const vector<int> &C)
{
m = M, k = K; v.clear();
for(int i=0; i<N; i++)
v.emplace_back(max(R[i], C[i]) + 1, min(R[i], C[i]));

sort(all(v));

for(int i=0; i<N; i++)


{
while(n && a[n].y >= v[i].y) n--;
a[++n] = v[i];
}
}

int chk(ll c)
{
cht.init();
cht.update(CHT::Line(-2 * a[1].y * 2, a[1].y * a[1].y * 2, 0));

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


{
auto t = cht.query(a[i].x);
dp[i] = t.x + a[i].x * a[i].x * 2 + c;
cut[i] = cut[t.y] + 1;
ll ta = a[i+1].y, tb = max(0LL, a[i].x - a[i+1].y);
ll aa = -2 * a[i+1].y * 2;
ll bb = dp[i] + ta*ta*2 - tb*tb*2;
cht.update(CHT::Line(aa, bb, i));
}

return cut[n];
}

ll take_photos(int N, int M, int K,


vector<int> R, vector<int> C)
{
init(N, M, K, R, C);
int q = min(n, k);

ll l = 0, r = 1e15;
while(l < r)
{
ll m = l + r >> 1;
if(chk(m << 1 | 1) <= q) r = m;
else l = m + 1;
}

chk(r << 1);


return dp[n] / 2 - r * q;
}

// BEGIN CUT

int main()
{
auto t1 = clock();
CAPITOLUL 4. IOI 2016 4.6. ALIENS 586

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 0.672
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.18: alien-225911.cpp


// https://oj.uz/submission/225911

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>

#include<ctime>

using namespace std;

#define endl ’\n’


#define ll long long
#define pi pair<ll, ll>
#define pii pair<int, pi>
#define f first
#define s second

const int maxn = 100001;


int n;
pi a[maxn], dp[maxn];
pii l[maxn];
int s, e;

ll sq(ll x)
{
CAPITOLUL 4. IOI 2016 4.6. ALIENS 587

return (ll)x * x;
}

bool cp(pii x, pii y, pii z)


{
return (x.s.f - z.s.f) * (y.s.s - x.s.s) >=
(x.s.f - y.s.f) * (z.s.s - x.s.s);
}

ll f(int x, ll y)
{
return l[x].s.f * y + l[x].s.s;
}

void g(ll x, ll y, int z)


{
while(e - s > 1 && cp(l[e - 2], l[e - 1], {z, {x, y}})) e--;
l[e++] = {z, {x, y}};
}

pi solve(ll x)
{
s = e = 0;
g(-2 * (a[0].f - 1), sq(a[0].f - 1), 0);
for(int i = 1; i <= n; i++)
{
while(e - s > 1 && f(s, a[i - 1].s) >= f(s + 1, a[i - 1].s))
s++;
dp[i] = {f(s, a[i - 1].s) + sq(a[i - 1].s) + x, dp[l[s].f].s + 1};
g(-2 * (a[i].f - 1),
dp[i].f + sq(a[i].f - 1) - sq(max((ll)0, a[i - 1].s - a[i].f + 1)),
i);
}

return dp[n];
}

ll take_photos(int N, int m, int k, vector<int> x, vector<int> y)


{
for(int i = 0; i < N; i++)
a[i] = {min(x[i], y[i]), max(x[i], y[i])};

sort(a, a + N, [&](pi x, pi y)
{
return x.f == y.f ? x.s > y.s : x.f < y.f;
});

n = 1;
for(int i = 1; i < N; i++)
if(a[i].s > a[n - 1].s)
a[n++] = a[i];

ll l = 0, r = sq(m);
while(r - l > 1)
{
ll mid = (l + r) / 2;
if(k <= solve(mid).s) l = mid;
else r = mid;
}

return solve(l).f - k * l;
}

// BEGIN CUT

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
CAPITOLUL 4. IOI 2016 4.6. ALIENS 588

{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.312
t3-t2 = 0.797
t4-t3 = 0

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


Press any key to continue.
*/

Listing 4.6.19: alien-228077.cpp


// https://oj.uz/submission/228077

#include "aliens.h"
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

struct TConvexHullTrick
{
struct TLine
{
ll a;
ll b;
int id;

TLine(ll a = 0, ll b = 0, int id = 0)
: a(a), b(b), id(id) {
}
};

std::deque<TLine> Lines;

TConvexHullTrick()
{
Lines.clear();
}

bool bad(const TLine& l1, const TLine& l2, const TLine& l3)
{
return (l1.b - l3.b) * (l2.a - l1.a) <
(l1.b - l2.b) * (l3.a - l1.a);
CAPITOLUL 4. IOI 2016 4.6. ALIENS 589

void add(ll a, ll b, int id)


{
TLine line(a, b, id);
while (Lines.size() >= 2 &&
bad(Lines[Lines.size()-2], Lines[Lines.size()-1], line))
{
Lines.pop_back();
}
Lines.push_back(line);
}

ll func(const TLine &d, ll x)


{
return d.a * x + d.b;
}

std::pair<ll, int> get(ll x)


{
while (Lines.size() >= 2 && func(Lines[0], x) > func(Lines[1], x))
{
Lines.pop_front();
}
return std::make_pair(func(Lines[0], x), Lines[0].id);
}
};

namespace Solver
{
const int N = 1e5 + 5;
std::pair<int, int> input[N];
int l[N], r[N];
int n, m, k;

ll dp[N];
int trace[N];

void process()
{
std::sort(input, input + n, [](const auto& x, const auto& y)
{
return x.first < y.first ||
(x.first == y.first && x.second > y.second);
});

int rmax = -1;


int cnt = 0;
for (int i = 0; i != n; ++i)
{
if (rmax < input[i].second)
{
rmax = input[i].second;
++cnt;
l[cnt] = input[i].first;
r[cnt] = input[i].second;
}
}

n = cnt;
}

int check(ll lambda)


{
TConvexHullTrick cht;

memset(dp, 0, sizeof dp);


memset(trace, 0, sizeof trace);

auto sqr = [](int i, int j, bool checkZero)


{
if (!checkZero || r[j] >= l[i])
{
return (r[j] - l[i]) * 1ll *(r[j] - l[i]);
}
CAPITOLUL 4. IOI 2016 4.6. ALIENS 590

else
{
return 0ll;
}
};

cht.add(-2 * l[1], 1LL * l[1] * l[1], 0);


for (int i = 1; i <= n; ++i)
{
pair<ll, int> g = cht.get(r[i]);

dp[i] = g.first + 1LL * r[i] * r[i] + lambda;


trace[i] = g.second;

ll bCoef = dp[i]-sqr(i+1, i, true) + 1ll * l[i+1] * l[i+1];


cht.add(-2 * l[i + 1], bCoef, i);
}

int pos = n, cnt = 0;


while(pos)
{
++cnt;
pos = trace[pos];
}
return cnt;
}

ll solve()
{
process();

ll l = 0, r = 1e13;
while(l < r)
{
ll mid = ((l + r) >> 1);
if (check(mid) <= k)
{
r = mid;
}
else
{
l = mid + 1;
}
}

check(l); // l = lambda_opt
return dp[n] - l * k;
}
};

ll take_photos(int n, int m, int k,


vector<int> r, vector<int> c)
{
Solver::n = n;
Solver::m = m;
Solver::k = k;
for (int i = 0; i < n; ++i)
{
if (r[i] > c[i])
{
std::swap(r[i], c[i]);
}

Solver::input[i].first = r[i];
Solver::input[i].second = ++c[i];
}

return Solver::solve();
}

// BEGIN CUT

int main()
{
auto t1 = clock();
CAPITOLUL 4. IOI 2016 4.6. ALIENS 591

std::freopen("../tests/subtask_6/175", "r", stdin);


std::freopen("6-175.out.txt", "w", stdout);

int n, m, k;
scanf("%d %d %d", &n, &m, &k);
std::vector<int> r(n), c(n);
for (int i = 0; i < n; i++)
{
scanf("%d %d", &r[i], &c[i]);
}

auto t2 = clock();

long long ans = take_photos(n, m, k, r, c);

auto t3 = clock();

// BEGIN SECRET
//puts("098d134608c94f7413faac591054ee35");
// END SECRET

printf("%lld\n", ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

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

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// END CUT
/*
t2-t1 = 0.296
t3-t2 = 3.047
t4-t3 = 0

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


Press any key to continue.
*/

4.6.3 *Rezolvare detaliată


Capitolul 5
35
IOI 2015

5.1 Boxes with souvenirs


Problema 1 - Boxes with souvenirs 100 de puncte

Author: Monika Steinová (SUI)

Ultima din activităţile din cadrul ceremoniei de deschidere a IOI 2015 este ı̂n plină desfăşuare.
Se presupune că ı̂n timpul ceremoniei de deschidere fiecare echipă va primi câte o cutie, conţinând
un suvenir din partea gazdelor. Totuşi, toţi voluntarii sunt atât de fascinaţi de ceremonie, ı̂ncât
au uitat totalmente de suvenire. Unica persoană care mai ţine minte despre suvenire este Aman.
El este un voluntar entuziast şi doreşte ca organizarea IOI să fie perfectă, prin urmare, el doreşte
să livreze toate suvenirele ı̂ntr-un timp minim.
Scena pe care se desfăşoară ceremonia de deschidere reprezintă un cerc divizat ı̂n L sectoare
identice. Sectoarele de pe cerc sunt numerotate consecutiv de la 0 la L  1. Aşa dar, pentru
0 & i & L  2 , sectorul i şi sectorul i  1 sunt adiacente, sectorul L  1 si sectorul 0 sunt si ele
adiacente. Sunt N echipe pe scenă. Fiecare echipă este amplasată ı̂n unul din sectoare. ı̂n fiecare
sector se poate afla un număr oarecare de echipe. Unele sectoare pot fi libere.
Sunt N suvenire identice. Iniţial, atât Aman, cât şi toate suvenirele se găsesc ı̂n sectorul 0.
Aman trebuie să repartizeze câte un suvenir pentru fiecare echipă, iar după livrarea ultimului
suvenir urmează să revină ı̂n sectorul 0. De remarcat că unele echipe se pot afla ı̂n sectorul 0.
Într-un moment arbitrar de timp, Aman poate duce cel mult K suvenire. Aman ia suvenirele
din sectorul 0, fără a consuma timp. Fiecare suvenir trebuie dus până ı̂n momentul ı̂n care acesta
este livrat uneia dintre echipe. La fiecare moment Aman duce unul sau mai multe suvenire până
ajunge la un sector ı̂n care se află o echipă ce nu a primit ı̂ncă suvenir. El poate oferi acestei
echipe unul din suvenirele pe care le are. Oferirea suvenirului nu consumă timp. Singurul lucru
care consumă timp este deplasarea. Aman se poate deplasa pe cerc ı̂n ambele direcţii. Deplasarea
ı̂n sectorul adiacent (ı̂n direcţia mişcării acelor de ceasornic sau ı̂n direcţia opusă mişcării acelor
de ceasornic) ı̂i ia exact o secundă, indiferent de aceea, câte suvenire el duce.
Sarcina ta este să determini timpul minim, ı̂n secunde, de care are nevoie Aman pentru a livra
toate suvenirele şi a reveni ı̂n poziţia iniţială.
Exemplu
În acest exemplu avem N 3 echipe, numărul maximal de suvenire pe care le poate duce Aman
concomitent este K 2, numărul de sectoare este L 8. Echipele sunt amplasate ı̂n sectoarele 1,
2 şi 5.
35
aur: Rareş Darius Buhai, Liviu Rebreanu (Bistriţa)
. argint: Alexandru Velea, Tiberiu Popoviciu (Cluj),
. argint: Valentin-Marius Hărşan, ICHB (Bucureşti)
. bronz: Andrei Popa, Mihail Kogalniceanu (Vaslui).

592
CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 593

Una dintre soluţiile optime este prezentată ı̂n desenul de mai sus. ı̂n prima tură Aman ia două
suvenire, livrează unul dintre ele echipei din sectorul 2, apoi altul echipei din sectorul 5 şi, ı̂n
final, revine ı̂n sectorul 0. Această tură durează 8 secunde. ı̂n tura secundă Aman oferă suvenirul
rămas echipei din sectorul 1 şi revine ı̂n sectorul 0. El are nevoie de alte 2 secunde pentru aceasta.
Astfel, timpul total este de 10 secunde.
Cerinţă
Sunt date valorile N , K, L, precum şi poziţiile tuturor echipelor. Calculează timpul minim ı̂n
secunde necesar lui Aman pentru a livra toate suvenirele şi a reveni ı̂n sectorul 0. Urmează să
implementezi funcţia delivery:
delivery(N, K, L, positions) - Această funcţie va fi apelată de grader o singură dată.
a N : numărul de echipe.
a K: numărul maximal de suvenire pe care Aman le poate duce concomitent.
a L: numărul de sectoare ı̂n care este divizată scena ceremoniei de deschidere.
a positions: un tablou de lungime N . positions[0], ..., positions[N-1] descriu
numerele sectoarelor ı̂n care se află toate echipele. Valorile elementelor tabloului positions
sunt ı̂n ordine nedescrescătoare.
a Funcţia urmează să returneze timpul minim ı̂n secunde necesar lui Aman pentru a finaliza
activitatea sa.

Subprobleme

subproblemă puncte N K L
1&N & 1, 000 1 & L & 10
9
1 10 K 1
1&N & 1, 000 1 & L & 10
9
2 10 K N
1&N & 10 1&K&N 1 & L & 10
9
3 15
1&N & 1, 000 1&K&N 1 & L & 10
9
4 15
1&N & 106 1 & K & 3, 000 1 & L & 10
9
5 20
1&N & 107 1&K&N 1 & L & 10
9
6 30

Grader-ul de pe calculatorul tău


Grader-ul de pe calculatorul tau citeşte datele de intrare ı̂n următorul format:
ˆ linia 1: N K L
ˆ linia 2: positions[0] ... positions[N-1]

Grader-ul de pe coalculatorul tau afişează valoarea returnată de delivery.


Timp maxim de executare/test: 2.0 secunde
Memorie: total 1500 MB

5.1.1 Indicaţii de rezolvare

The problem has a solution in linear time. The solution is basically greedy, and it is based on
several observations. Discovering only a subset of those observations usually leads to a correct
but slower solution.
CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 594

We will use the word trip to denote the part of a solution between two visits to a warehouse.
The numbering of locations increases in the CW direction. (Imagine the circle as a clock face with
the warehouse at 12 = 0, and locations at 1 through 11.)
Clearly, in an optimal solution we will never go twice along the same segment in the same
direction during the same trip - any such trip can be shortened while still visiting the same set of
locations. The moment when we deliver boxes also do not matter. WLOG we may assume that
we deliver a box we want the first time we visit its location. Hence, we can divide the trips into
three groups:
CW trips: The boxes are delivered while going CW, we return by going CCW.
CCW trips: The same with directions reversed.
Full-circle trips: The entire circle is traversed once in either direction.

Also, we may easily note that each CW/CCW trip must end at one of the boxes we leave in
that trip - going any farther can be skipped.
Lemma 3 In an optimal solution, the distance traveled CW in a CW trip is at most l/2 (half
of the circle). The same holds for CCW trips.
The proof is trivial: if you travel more than l/2 while delivering the boxes and now you want
back, it is cheaper to continue in the same direction than to return - i.e., to make a full-circle trip
instead.
Lemma 4 There exists an optimal solution satisfying the property from Lemma 3 where in
each trip we deliver a contiguous subset of boxes (i.e., boxes with no other box located between
them).
Proof: Consider any optimal solution.
First of all, note that in each half of the circle, the boxes that are delivered during full-circle
trips are all at least as far from the warehouse as the boxes delivered in CW/CCW trips.
This is because we could take the farthest box delivered in a CW/CCW trip and swap it for
one that is closer but delivered in a full-circle trip. The full-circle trip will remain the same length,
the CW/CCW trip will now be shorter, which contradicts the optimality of the original solution.
Now we know that the set of boxes delivered by all full-circle trips is contiguous. As we can
pick any of them during each full-circle trip, we can easily plan the full-circle trips so that each of
them delivers a contiguous subset of these boxes.
Now let us look at the boxes delivered during the CW trips. Obviously, the k farthest from
the warehouse can be delivered in the same trip - this can be proved using a switching argument
similar to the one above. QED.
Lemma 5 There is always an optimal solution with at most one full-circle trip. Additionally,
we deliver exactly k boxes during that trip (or all of them if n $ k).
Proof: It always makes sense to drop as many boxes as we can during a full-circle trip, because
we can move boxes from CW/CCW trips to full-circle trips at no cost.
As we already know, there is an optimal solution in which each trip drops a contiguous segment
of boxes. For any partition of boxes into contiguous segments, at most one such segment contains
boxes on both sides of the circle. Only that one segment needs to be delivered in a full-circle trip,
for each other segment of boxes a CW/CCW trip is at most as expensive as a full-circle one.
Theorem 6 The problem can be solved in O n time.
Proof: Let li be the optimal distance to deliver the first i boxes in the CW direction using
CW trips. These values can be precomputed in O n time. Let ri be the same values for the
CCW direction.
There may be no full-circle trip. In O n, try all possibilities for how many boxes are delivered
in CW trips. (This is overkill but the benefit is that we do not have to handle boxes opposite the
warehouse as a special case.) Look for the minimum of li  rni .
There may be one full-circle trip. Again in O n, try all possibilities for the k boxes delivered
during such a trip. Look for the minimum of li  l  rnki . Return the minimum of all those
values.
Another proof: If there are fewer than k boxes in the location opposite the warehouse, there
are clearly O k  different possibilities for the segment delivered during a full-circle trip, and O k 
possibilities to consider if there is no full-circle trip.
If there are k or more boxes in the location opposite the warehouse, there is an optimal solution
with no full-circle trip. Additionally, we can obviously assume that less than k of those boxes are
CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 595

delivered in CW trips. This gives us, again, O k  possibilities for the split between boxes delivered
in either direction.
In either case, we have O k  possibilities to try. For each of those O k  possibilities, we can
compute the optimal total cost without any precomputation: we simply go through the locations
of the remaining boxes with step k and add those distances up. Thus, each single possibility can
be evaluated in O n©k , which gives a total time complexity of O n again.

5.1.2 Coduri sursă

Listing 5.1.1: boxes-16533.cpp


// https://oj.uz/submission/16533 492 ms 196104 KB

#include <algorithm>
#include <stdio.h>

long long L[11111111];


long long R[11111111];

long long delivery(int n,int k,int l,int a[])


{
long long r;
int i;
for(i=1;i<=n;i++)
{
L[i]=std::min(a[i-1]<<1,l);
if(i>k)L[i]+=L[i-k];
}
r=L[n];
for(i=n-1;i>=0;i--)
{
R[i]=std::min((l-a[i])<<1,l);
if(i+k<n)R[i]+=R[i+k];
r=std::min(r,L[i]+R[i]);
}
return r;
}

// ----------------- start grader ---------------------

static char _buffer[1024];

static int _currentChar = 0;


static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}
if (!_charsNumber || _currentChar == _charsNumber)
{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}
if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 596

{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtask6/09", "rb");
_outputFile = fopen("boxes.out", "w");

int N, K, L, i;
N = _readInt();
K = _readInt();
L = _readInt();

int *p = (int*)malloc(sizeof(int) * (unsigned int)N);

for (i = 0; i < N; i++)


{
p[i] = _readInt();
}

fprintf(_outputFile, "%lld\n", delivery(N, K, L, p));


return 0;
}
// ----------------- start grader ---------------------
/*
execution time : 2.288 s
*/

Listing 5.1.2: boxes-64042.cpp


// https://oj.uz/submission/64042 381 ms 39552 KB

#include "boxes.h"
#include <map>
#include <iostream>
#include <algorithm>
#include <assert.h>

#include <stdio.h>
#include <stdlib.h>

long long delivery(int n, int k, int l, int p[])


{

if (n == 1)
{
return std::min(p[0] * 2LL, (l - p[0]) * 2LL);
}

int here = n;
for (int i = 0; i < n; ++i)
{
if (p[i] > l - p[i] && here == n)
{
here = i;
}
if (i >= here)
CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 597

{
p[i] = l - p[i];
}
}

if (here == 0)
{
long long int sumleft = p[0], leftleft = -1;
int cur = k - 1;

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


{
if (cur == 0)
{
sumleft += p[i - 1] + p[i] * 1LL;
cur = k - 1;
}
else
{
sumleft += p[i - 1] - p[i] * 1LL;
--cur;
}
}

sumleft += p[n - 1] * 1LL;

return sumleft;
}
else
if (here == n)
{
long long int sumright = p[here - 1], leftright = -1;
int cur = k - 1;

for (int i = here - 2; i >= 0; --i)


{
if (cur == 0)
{
sumright += p[i + 1] + p[i] * 1LL;
cur = k - 1;
}
else
{
sumright += p[i + 1] - p[i] * 1LL;
--cur;
}
}

sumright += p[0] * 1LL;

return sumright;
}

long long int sumleft = p[here - 1];


int cur = k - 1;

for (int i = here - 2; i >= 0; --i)


{
if (cur == 0)
{
sumleft += p[i + 1] + p[i] * 1LL;
cur = k - 1;
}
else
{
sumleft += p[i + 1] - p[i] * 1LL;
--cur;
}
}

sumleft += p[0] * 1LL;

long long int sumright = p[here];


cur = k - 1;

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


CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 598

{
if (cur == 0)
{
sumright += p[i - 1] + p[i] * 1LL;
cur = k - 1;
}
else
{
sumright += p[i - 1] - p[i] * 1LL;
--cur;
}
}

sumright += p[n - 1] * 1LL;

long long int curmin = sumleft + sumright;

if (k == 1)
return curmin;

long long int curval, min = 1e17;


int start;
for (int i = std::max(here - k + 1, 0);; ++i)
{
if (i >= here)
break;
if (i + k - 1 >= n)
break;

curval = l;

if (i != 0)
{
start = i - 1;

while (1)
{
if (start == i - 1)
curval += p[start] * 1LL;
else
curval += p[start] * 2LL;

if (start - k >= 0)
{
curval += p[start] - p[start - k] * 1LL;
start -= k;
}
else
{
curval += p[start] * 1LL;
break;
}
}
}
if (i + k < n)
{
start = i + k;

while (1)
{
if (start == i + k)
curval += p[start] * 1LL;
else
curval += p[start] * 2LL;

if (start + k < n)
{
curval += p[start] - p[start + k] * 1LL;
start += k;
}
else
{
curval += p[start] * 1LL;
break;
}
}
CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 599

if (curval < min)


min = curval;
}

return std::min(curmin, min);


}

// ----------------- start grader ---------------------

static char _buffer[1024];

static int _currentChar = 0;


static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}
if (!_charsNumber || _currentChar == _charsNumber)
{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}
if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtask6/09", "rb");
_outputFile = fopen("boxes.out", "w");

int N, K, L, i;
N = _readInt();
K = _readInt();
L = _readInt();

int *p = (int*)malloc(sizeof(int) * (unsigned int)N);

for (i = 0; i < N; i++)


{
p[i] = _readInt();
CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 600

fprintf(_outputFile, "%lld\n", delivery(N, K, L, p));


return 0;
}
// ----------------- start grader ---------------------
/*
execution time : 2.609 s
*/

Listing 5.1.3: boxes-70567.cpp


// https://oj.uz/submission/70567 425 ms 117864 KB

#include "boxes.h"
#include <bits/stdc++.h>

using namespace std;

#define ll long long


#define NN ((int)1e7+10)

ll dp[NN];

ll delivery(int n,int k, int l, int p[])


{
int id=0;
for(int i=0;i<n && p[i]<=l/2;i++,id++)
dp[i]=2*p[i]+((i-k<0)?0:dp[i-k]);

for(int i=n-1;i>=id;i--)
dp[i]=2*(l-p[i])+((i+k>=n)?0:dp[i+k]);

ll ans=min((ll)(n+k-1)/k*l,((id>0)?dp[id-1]:0)+dp[id]);

int lft=max(0,id-k),rght=lft+k;
while(lft<=id && rght<=n)
ans=min(ans,dp[rght]+((lft>0)?dp[lft-1]:0)+l),
lft++,rght++;
return ans;
}

// ----------------- start grader ---------------------

static char _buffer[1024];

static int _currentChar = 0;


static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}
if (!_charsNumber || _currentChar == _charsNumber)
{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}
if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
CAPITOLUL 5. IOI 2015 5.1. BOXES WITH SOUVENIRS 601

c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtask6/09", "rb");
_outputFile = fopen("boxes.out", "w");

int N, K, L, i;
N = _readInt();
K = _readInt();
L = _readInt();

int *p = (int*)malloc(sizeof(int) * (unsigned int)N);

for (i = 0; i < N; i++)


{
p[i] = _readInt();
}

fprintf(_outputFile, "%lld\n", delivery(N, K, L, p));


return 0;
}
// ----------------- start grader ---------------------
/*
execution time : 2.578 s
*/

Listing 5.1.4: checkerBoxes.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

long long readLong(InStream& in)


{
long long res = in.readLong();
return res;
}

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask6/09", // argv[1]=<input-file>
(char*)"boxes.out", // argv[2]=<output-file>
(char*)"../tests/subtask6/09.a", // argv[3]=<answer-file>
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";
CAPITOLUL 5. IOI 2015 5.2. SCALES 602

registerTestlibCmd(argc, argv);

long long pans = readLong(ouf);


long long jans = readLong(ans);

if (pans != jans)
{
quitf(_wa, "Wrong answer: output = %lld, expected = %lld", pans, jans);
}
else
{
quitf(_ok, "Correct answer: answer = %lld", pans);
}
}

5.1.3 *Rezolvare detaliată

5.2 Scales
Problema 2 - Scales 100 de puncte

Author: Eryk Kopczynski (POL)

Amina are şase monezi numerotate de la 1 la 6. Ea ştie că monezile au greutăţi diferite. Ea ar
dori să le ordoneze ı̂n raport cu greutatea. Pentru acest scop ea a construit un nou tip de balanţă.
O balanţă traditională are două talere. Pentru a utiliza o astfel de balanţă, se aşează câte o
monedă pe fiecare taler şi balanţa determină care dintre monezi este mai grea.
Noua balanţă a Aminei este mult mai complexă. Aceasta are patru talere, etichetate A, B, C
şi D.
Balanţa are patru setari diferite, fiecare răspunzând unei anumite ı̂ntrebari ı̂n legatură cu
monezile.
Pentru a utiliza balanţa, Amina trebuie să plaseze exact câte o monedă pe fiecare dintre talerele
A, B şi C. ı̂n plus, pentru a patra setare ea trebuie sa aşeze deasemenea exact o monedă pe talerul
D.
Cele patru setari vor răspunde la următoarele patru ı̂ntrebări:
1. Care dintre monezile de pe talerele A, B şi C este cea mai grea?
2. Care dintre monezile de pe talerele A, B şi C este cea mai uşoară?
3. Care dintre monezile de pe talerele A, B şi C este cea mediană? ( Adică nu este nici cea
mai grea nici cea mai uşoară dintre cele trei.)
4. Dintre monezile de pe talerele A, B şi C se consideră doar cele care sunt mai grele decât cea
situată pe talerul D. Dacă există astfel de monezi, care dintre aceste monezi este cea mai
usoară? Altfel, dacă nu există o astfel de monedă , care dintre monezile situate pe talerele
A, B şi C este cea mai usoară?

Cerinţă
Scrie un program care ordonează cele şase monezi ale Aminei ı̂n funcţie de greutate. Programul
poate să ceară balanţei Aminei să compare greutăţi ale monezilor. Programului tău i se vor da
câteva teste spre rezolvare, fiecare corespunzând unui nou set de şase monede.
Programul tău trebuie sa implementeze funcţiile init şi orderCoins. Pe parcursul fiecărei
rulări, grader-ul va apela iniţial functia init exact o dată. Aceasta va furniza numărul de teste
şi vă va permite sa iniţializaţi orice variabilă. Apoi grader-ul va apela functia orderCoins()
câte o dată pentru fiecare test.
ˆ init(T)
– T : Numărul de teste pe care programul tău va trebui să ı̂l rezolve la rularea curentă.
T este un ı̂ntreg din intervalul 1, ..., 18.
– Aceasată funcţie nu va returna nicio valoare.
ˆ orderCoins()
CAPITOLUL 5. IOI 2015 5.2. SCALES 603

– Această funcţie va fi apelată exact o dată pentru fiecare test.


– Funcţia trebuie să determine corect ordinea monezilor Aminei apelând funcţiile
grader-ului getHeaviest(), getLightest(), getMedian(), şi/sau
getNextLightest().
– ı̂n momentul ı̂n care funcţia cunoaşte ordinea corectă, va trebui sa raporteze acest lucru
prin apelul functiei grader-ului answer().
– După apelul answer(), funcţia orderCoins() trebuie sa returneze. Ea nu va re-
turna nicio valoare.

Poţi utiliza urmatoarele funcţii ale grader-ului in programul tău:


ˆ answer(W) - programul trebuie să utilizeze această funcţie pentru a raporta răspunsul pe
care l-a găsit.
– W : Un şir de lungime 6 conţinând ordinea corectă a monezilor. W 0 până la W 5
trebuie să fie numerele monezilor (adică numere de la 1 la 6) ı̂n ordine de la cea mai
usoară la cea mai grea monedă.
– Programul tău trebuie să apeleze această funcţie din orderCoins(), o dată pentru
fiecare test.
– Această funcţie nu returnează nicio valoare.
ˆ getHeaviest(A, B, C), getLightest(A, B, C), getMedian(A, B, C) -
– Aceastea corespund setărilor 1, 2, respectiv 3 pentru balanţa Aminei.
– A, B, C: Monezile care sunt puse pe talerele A, B, respectiv C. A, B, şi C trebuie să
fie trei ı̂ntregi distincţi, fiecare ı̂ntre 1 şi 6 inclusiv.
– Fiecare funcţie returnează unul dintre numerele A, B şi C: numărul asociat monezii
corecte. De exemplu, getHeaviest(A, B, C) returnează numărul celei mai grele
dintre cele trei monezi date.
ˆ getNextLightest(A, B, C, D) - aceasta corespunde setării 4 pentru balanţa Aminei
– A, B, C, D: Monezile care sunt puse pe talerele A, B, C, respectiv D. A, B, C şi D
trebuie să fie patru ı̂ntregi distincţi, fiecare ı̂ntre 1 şi 6 inclusiv.
– Funcţia returnează unul dintre numerele A, B şi C: numărul monezii selectate de balanţă
aşa cum e descrisă anterior setarea 4. Adică, moneda returnată este cea mai uşoară
dintre monezile de pe talerele A, B, şi C care este mai grea decât cea de pe talerul D;
sau dacă niciuna nu este mai grea decât cea de pe talerul D, moneda returnată este
pur şi simplu cea mai usoară dintre monezile situate pe talerele A, B şi C.

Punctaj
Această problemă nu are subprobleme. ı̂n schimb scorul tău va fi calculat ı̂n funcţie de numărul
de cântăriri (adică de numărul total de apeluri ale funcţiilor grader getLightest()\verb,
getHeaviest(), getMedian() şi/sau getNextLightest()) pe care le face programul tău.
Programul tău va fi rulat de mai multe ori şi pe mai multe teste la fiecare rulare. Fie r numărul
de rulări ale programului tău. Acest număr este fixat ı̂n datele de test. Dacă programul tău nu
ordonează monezile corect la oricare test din oricare rulare, vei obţine 0 puncte. Altfel, fiecare
rulare va fi punctată individual după cum urmează.
Fie Q cel mai mic număr posibil de cântı̂riri astfel ı̂ncât să se poată sorta orice şir de şase
monezi folosind Q cântăriri cu balanţa Aminei. Pentru a face problema mai interesantă nu vom
dezvălui aici valoarea lui Q.
Să presupunem că cel mai mare număr de cântăriri pe toate testele din toate rulările este
Q  y unde y este un ı̂ntreg. Să considerăm apoi o singură rulare a programului tău. Fie Q  x
cel mai mare număr de cântı̂riri dintre toate cele T teste - unde x este un ı̂ntreg nenegativ. (
Dacă utilizaţi mai putin de Q cântăriri pe fiecare test atunci x 0.) Scorul rulării curente va fi
100
r xy ©51
, rotunjit ı̂n jos la două zecimale.
ı̂n particular, dacă programul face cel mult Q cântăriri pe fiecare test vei obţine 100 de puncte.
Exemplu
Presupunem că monezile sunt ordonate 3 4 6 2 1 5 de la cea mai uşoară la cea mai grea.
CAPITOLUL 5. IOI 2015 5.2. SCALES 604

Apel de funcţie Returnări Explicaţie


getMedian(4, 5, 6) 6 Moneda este cea mediană ı̂ntre monezile , şi .
getHeaviest(3, 1,2) 1 Moneda este cea mai grea dintre monezile , şi .
getNextLightest(2,3, 4, 5) 3 Monezile , şi sunt toate mai uşoare decât moneda ,
deci cea mai uşoară dintre ele ( ) este returnată.
getNextLightest(1,6, 3, 4) 6 Monezile şi sunt ambele mai grele decât moneda .
Dintre monezile şi , moneda este cea mai uşoară.
getHeaviest(3, 5,6) 5 Moneda este cea mediană ı̂ntre monezile , şi .
getMedian(1, 5, 6) 1 Moneda este cea mediană ı̂ntre monezile , şi .
getMedian(2, 4, 6) 6 Moneda este cea mediană ı̂ntre monezile , şi .
answer([3, 4, 6, 2,1, 5]) Programul a găsit răspunsul corect pentru acest test.

Grader-ul de pe calculatorul tău


Grader-ul de pe calculatorul tău citeşte datele de intrare ı̂n următorul format:
ˆ linia 1: T numărul de teste
ˆ fiecare din liniile de la 2 la T  1: un şir de 6 numere distincte de la 1 la 6: ordinea monezilor
de la cea mai uşoară la cea mai grea.
De exemplu, dacă datele de intrare constau ı̂n două teste unde monezile sunt ordonate 1 2 3 4
5 6 şi 3 4 6 2 1 5 formatul datelor de intrare este următorul:
21
2 3 4 5 6
3 4 6 2 1 5
Grader-ul de pe calculatorul vostru afişează şirul trimis ca parametru de funcţia answer().
Timp maxim de executare/test: 1.0 secunde
Memorie: total 1500 MB

5.2.1 Indicaţii de rezolvare

This is based on the well-known problem of ordering five coins with 7 (binary) weighing. There
are 120 permutations and 128 possible sequences of seven answers, and it is indeed possible to
find a strategy which always works.
In our problem, there are 720 possible answers, and with at most 6 ternary weighing, theoret-
6
ically we could obtain 3 729 possible answers. The gap is very small, but it turns out that it
is again possible to find a strategy which uses just 6 weighing.
The easiest solution here is to generate a memorization function which, for each possible subset
of possible permutations, tries all the possible questions, and returns the best one. It is possible
to bound the branching, as follows:

ˆ Always take care that there are at most 243 consistent results after 1 weighing, at most 81
consistent results after 2 weighing, etc.
ˆ If, for the given subset of power P , we have already found a solution which uses N weighing,
and P % 3 N  1, then do not look further (we have already found the best possible answer).

With these optimizations, we could write a program which generates the strategy tree, in the
form of a program which could be submitted for judging (see builder.cpp, which does not build
it in the right format yet, but is close enough). With the optimizations above the builder runs
fast enough, so it is also possible to erate it on the fly.

5.2.2 Coduri sursă


CAPITOLUL 5. IOI 2015 5.2. SCALES 605

Listing 5.2.1: graderlib.c


1 #include <assert.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include "scales.h"
5
6 #include<iostream>
7
8 using namespace std;
9
10 #define _MAXN 6
11 #define _MAX_ANSWER_CALLS 1
12
13 static int _realC[_MAXN];
14 static int _ind[_MAXN];
15 static int _numQueries;
16 static int _numAnswerCalls;
17 static FILE * _f;
18 static FILE * _of;
19
20 static int _getNumberOfTests()
21 {
22 int T, ret;
23
24 _f = fopen("../tests/40.in", "r");
25 _of = fopen("scales.out", "w");
26
27 ret = fscanf(_f, "%d", &T);
28 //cout<<"ret = "<<ret<<" T = "<<T<<"\n";
29
30 assert(ret == 1);
31 return T;
32 }
33
34 static void _initNewTest()
35 {
36 int i, ret;
37
38 for (i = 0; i < _MAXN; i++)
39 {
40 ret = fscanf(_f, "%d", &_realC[i]);
41 assert(ret == 1);
42 _realC[i]--;
43 _ind[_realC[i]] = i;
44 }
45
46 _numQueries = 0;
47 _numAnswerCalls = 0;
48 }
49
50 void answer(int W[])
51 {
52 int i;
53
54 _numAnswerCalls++;
55 if (_numAnswerCalls > _MAX_ANSWER_CALLS)
56 return;
57
58 for (i = 0; i < 6; i++)
59 fprintf(_of, "%d ", W[i]);
60
61 //fprintf(_of, "\n%d\n", _numQueries);
62 fprintf(_of, "%d\n", _numQueries);
63 }
64
65 static void _checkQuery(int A, int B, int C, int D)
66 {
67 if (D == -1)
68 {
69 if (A < 1 || A > 6 || B < 1 || B > 6 || C < 1 || C > 6)
70 assert(0);
71 if (A == B || B == C || A == C)
72 assert(0);
73 }
74 else
CAPITOLUL 5. IOI 2015 5.2. SCALES 606

75 {
76 if (A < 1 || A > 6 || B < 1 || B > 6 || C < 1 || C > 6 || D<1 || D>6)
77 assert(0);
78 if (A == B || A == C || A == D || B == C || B == D || C == D)
79 assert(0);
80 }
81 }
82
83 int getMedian(int A, int B, int C)
84 {
85 _numQueries++;
86 _checkQuery(A, B, C, -1);
87
88 A--; B--; C--;
89
90 if (_ind[B] < _ind[A] && _ind[A] < _ind[C])
91 return A + 1;
92
93 if (_ind[C] < _ind[A] && _ind[A] < _ind[B])
94 return A + 1;
95
96 if (_ind[A] < _ind[B] && _ind[B] < _ind[C])
97 return B + 1;
98
99 if (_ind[C] < _ind[B] && _ind[B] < _ind[A])
100 return B + 1;
101
102 return C + 1;
103 }
104
105 int getHeaviest(int A, int B, int C)
106 {
107 _numQueries++;
108 _checkQuery(A, B, C, -1);
109
110 A--; B--; C--;
111
112 if (_ind[A] > _ind[B] && _ind[A] > _ind[C])
113 return A + 1;
114
115 if (_ind[B] > _ind[A] && _ind[B] > _ind[C])
116 return B + 1;
117
118 return C + 1;
119 }
120
121 int getLightest(int A, int B, int C)
122 {
123 _numQueries++;
124 _checkQuery(A, B, C, -1);
125
126 A--; B--; C--;
127
128 if (_ind[A] < _ind[B] && _ind[A] < _ind[C])
129 return A + 1;
130
131 if (_ind[B] < _ind[A] && _ind[B] < _ind[C])
132 return B + 1;
133
134 return C + 1;
135 }
136
137 int getNextLightest(int A, int B, int C, int D)
138 {
139 int allLess = 1;
140
141 _numQueries++;
142 _checkQuery(A, B, C, D);
143
144 A--; B--; C--; D--;
145
146 if (_ind[A] > _ind[D] || _ind[B] > _ind[D] || _ind[C] > _ind[D])
147 allLess = 0;
148
149 if (allLess == 1)
150 {
CAPITOLUL 5. IOI 2015 5.2. SCALES 607

151 if (_ind[A] < _ind[B] && _ind[A] < _ind[C])


152 return A + 1;
153
154 if (_ind[B] < _ind[A] && _ind[B] < _ind[C])
155 return B + 1;
156
157 return C + 1;
158 }
159
160 if (_ind[A] > _ind[D])
161 {
162 if ((_ind[A] < _ind[B] || _ind[B] < _ind[D]) &&
163 (_ind[A] < _ind[C] || _ind[C] < _ind[D]))
164 return A + 1;
165 }
166
167 if (_ind[B] > _ind[D])
168 {
169 if ((_ind[B] < _ind[A] || _ind[A] < _ind[D]) &&
170 (_ind[B] < _ind[C] || _ind[C] < _ind[D]))
171 return B + 1;
172 }
173
174 return C + 1;
175 }

Listing 5.2.2: scales-45773.cpp


// https://oj.uz/submission/45773 109 ms 1512 KB

#include "graderlib.c"

#include "scales.h"

#include <bits/stdc++.h>

using namespace std;

int p[720][7] = {{0, 1, 2, 3, 4, 5, 6}};

struct query
{
query() {}
query(int t, int a, int b, int c, int d) : t(t),a(a),b(b),c(c),d(d) {}
int t, a, b, c, d;
int moi(int k)
{
if (t==1)
{ // Heaviest
if (p[k][a] > p[k][b] and p[k][a] > p[k][c]) return 0;
else return (p[k][b] > p[k][c]) ? 1 : 2;
}
if (t==2)
{ // Lightest
if (p[k][a] < p[k][b] and p[k][a] < p[k][c]) return 0;
else return (p[k][b] < p[k][c]) ? 1 : 2;
}
if (t==3)
{ // Median
if (p[k][a] < p[k][b])
{
if (p[k][b] < p[k][c]) return 1;
else return p[k][a] < p[k][c] ? 2 : 0;
}
else
{
if (p[k][a] < p[k][c]) return 0;
else return p[k][b] < p[k][c] ? 2 : 1;
}
}

if (t==4)
{ // NextHeaviest
int x = p[k][a], y = p[k][b], z = p[k][c];
if (not (x < p[k][d] and y < p[k][d] and z < p[k][d]))
CAPITOLUL 5. IOI 2015 5.2. SCALES 608

{
if (x < p[k][d]) x = 7;
if (y < p[k][d]) y = 7;
if (z < p[k][d]) z = 7;
}
if (x<y and x<z) return 0;
else return y<z ? 1 : 2;
} return -1;
}

int real()
{
int res;
if (t==1) res = getHeaviest(a, b, c);
if (t==2) res = getLightest(a, b, c);
if (t==3) res = getMedian(a, b, c);
if (t==4) res = getNextLightest(a, b, c, d);
if (res == a) return 0;
if (res == b) return 1;
if (res == c) return 2;
return -123; // ... !!!
}
};

vector<query> q;

struct node
{
query q;
node *ch[3];
set<int> s;
} *root;

bool make_tree(node* k, set<int> s)


{
k->s = s;
for (int i=0; i<3; i++)
if (!k->ch[i]) k->ch[i] = new node();
if (s.size() <= 1) return true;
for (int i=0; i<q.size(); i++)
{
set<int> r[3];
for (int e : s) r[q[i].moi(e)].insert(e);

if (max({r[0].size(), r[1].size(), r[2].size()}) -


min({r[0].size(), r[1].size(), r[2].size()}) < 2)
if (make_tree(k->ch[0], r[0]) and
make_tree(k->ch[1], r[1]) and
make_tree(k->ch[2], r[2]))
{
k->q = q[i];
return true;
}
} return false;
}

void init(int T)
{
for (int x=1; x<=6; x++)
for (int y=x+1; y<=6; y++)
for (int z=y+1; z<=6; z++)
{
q.emplace_back(1, x, y, z, -1);
q.emplace_back(2, x, y, z, -1);
q.emplace_back(3, x, y, z, -1);

for (int w=1; w<=6; w++)


if (x!=w and y!=w and z!=w)
q.emplace_back(4, x, y, z, w);
}

for (int i=1; i<720; i++)


{
copy(p[i-1]+1, p[i-1]+7, p[i]+1);
next_permutation(p[i]+1, p[i]+7);
}
CAPITOLUL 5. IOI 2015 5.2. SCALES 609

set<int> st;
for (int i=0; i<720; i++) st.insert(i);
root = new node();
make_tree(root, st);
}

void orderCoins()
{
node *now = root;
while (now->s.size() > 1)
{
now = now->ch[now->q.real()];
}
int ans[6], *rans = p[ *now->s.begin() ];
for (int i=1; i<=6; i++)
ans[rans[i]-1] = i;
answer(ans);
}

// ------------- start grader --------------------

int main()
{
auto t1 = clock();

int T, i;

auto t2 = clock();

T = _getNumberOfTests();
init(T);

auto t3 = clock();

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


{
_initNewTest();
orderCoins();
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader --------------------


/*
t2-t1 = 0
t3-t2 = 0.578
t4-t3 = 0

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


Press any key to continue.
*/

Listing 5.2.3: scales-115418.cpp


// https://oj.uz/submission/115418 14 ms 768 KB

#include "graderlib.c"

#include "scales.h"

#include <bits/stdc++.h>

using namespace std;


CAPITOLUL 5. IOI 2015 5.2. SCALES 610

int depmax[] = {729, 243, 81, 27, 9, 3, 1};

struct Ord
{
int a[7];
Ord(vector<int>& v)
{
for(int i = 0; i < 6; i++)
a[i + 1] = v[i];
}
};

struct Query
{
int a, b, c, d;
Query(int _a, int _b, int _c, int _d)
{
a = _a;
b = _b;
c = _c;
d = _d;
}

int ask()
{
int res;
if(d == a) res = getLightest(a, b, c);
else
if(d == b) res = getMedian(a, b, c);
else
if(d == c) res = getHeaviest(a, b, c);
else
res = getNextLightest(a, b, c, d);

if(res == a) return 1;
if(res == b) return 2;
if(res == c) return 3;
return -123; // ... !!!
}

int simask(Ord& ord)


{
int i = a, j = b, l = c, res;
if(ord.a[i] > ord.a[j]) swap(i, j);
if(ord.a[j] > ord.a[l]) swap(j, l);
if(ord.a[i] > ord.a[j]) swap(i, j);
if(a == d) res = i;
else
if(b == d) res = j;
else
if(c == d) res = l;
else
if(ord.a[d] < ord.a[i]) res = i;
else
if(ord.a[d] < ord.a[j]) res = j;
else
if(ord.a[d] < ord.a[l]) res = l;
else res = i;

if(res == a) return 1;
if(res == b) return 2;
if(res == c) return 3;
return -123; // ... !!!
}
};

vector<Query> lists;

struct Node
{
int dep;
Query* q;
vector<Ord*> pos;
Node* go[4];

bool init()
CAPITOLUL 5. IOI 2015 5.2. SCALES 611

{
if(pos.size() <= 1) return 1;
int cnt[4] = {0};
bool ok = 0;
for(auto& qq : lists)
{
cnt[1] = 0;
cnt[2] = 0;
cnt[3] = 0;

for(auto& ord : pos)


{
int res = qq.simask( *ord);
cnt[res]++;
if(cnt[res] > depmax[dep]) break;
}

if(cnt[1] > depmax[dep]) continue;


if(cnt[2] > depmax[dep]) continue;
if(cnt[3] > depmax[dep]) continue;
q = &qq;
for(int i = 1; i <= 3; i++)
{
go[i] = new Node();
go[i] -> dep = dep + 1;
}

for(auto ord : pos)


{
go[qq.simask( *ord)] -> pos.push_back(ord);
}

if(!go[1] -> init())


{
delete go[1];
continue;
}
if(!go[2] -> init())
{
delete go[2];
continue;
}
if(!go[3] -> init())
{
delete go[3];
continue;
}

ok = 1;
break;
}
return ok;
}

} *root;

void create()
{
for(int i = 1; i < 5; i++)
{
for(int j = i + 1; j < 6; j++)
{
for(int l = j + 1; l <= 6; l++)
{
for(int k = 1; k <= 6; k++)
{
lists.push_back(Query(i, j, l, k));
}
}
}
}
}

void init(int T)
{
create();
CAPITOLUL 5. IOI 2015 5.2. SCALES 612

root = new Node();


vector<int> v = {1, 2, 3, 4, 5, 6};

do
{
root -> pos.push_back(new Ord(v));
} while(next_permutation(v.begin(), v.end()));
root -> dep = 1;
root -> init();
}

void orderCoins()
{
int W[] = {1, 2, 3, 4, 5, 6};
Node* ans = root;
while(ans -> pos.size() != 1)
ans = ans -> go[ans -> q -> ask()];
for(int i = 1; i <= 6; i++)
{
W[ans -> pos[0] -> a[i] - 1] = i;
}
answer(W);
}

// ------------- start grader --------------------

int main()
{
auto t1 = clock();

int T, i;

auto t2 = clock();

T = _getNumberOfTests();
init(T);

auto t3 = clock();

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


{
_initNewTest();
orderCoins();
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader --------------------


/*
t2-t1 = 0
t3-t2 = 0.062
t4-t3 = 0

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


Press any key to continue.
*/

Listing 5.2.4: scales-122639.cpp


// https://oj.uz/submission/122639 5 ms 504 KB

#include "graderlib.c"

#include "scales.h"
CAPITOLUL 5. IOI 2015 5.2. SCALES 613

#include <bits/stdc++.h>

#define pii pair<int, int>


#define x first
#define y second

using namespace std;

vector<pair<int, vector<int> > > que;


vector<vector<int> > perm;

int ids[] = {0, 107, 107, 107, 49, 48, 46, 45, 44, 42, 38, 37, 35, 88,
95, 28, 67, 74, 15, 8, 40, 40, 85, 95, 28, 64, 74, 15, 11, 40, 40, 85,
88, 28, 64, 67, 15, 11, 33, 33, 31, 11, 9, 31, 11, 9, 10, 52, 52, 31,
24, 22, 31, 24, 22, 23, 52, 52, 72, 99, 79, 29, 18, 70, 29, 83, 5, 34,
8, 6, 34, 8, 6, 52, 7, 52, 34, 21, 19, 34, 21, 19, 52, 20, 52, 72, 103,
83, 32, 18, 69, 32, 79, 5, 41, 5, 3, 41, 5, 3, 55, 55, 4, 41, 18, 16,
41, 18, 16, 55, 55, 17, 103, 79, 83, 39, 21, 76, 39, 72, 8, 75, 55, 75,
65, 65, 55, 75, 18, 31, 68, 52, 68, 65, 65, 52, 18, 68, 31, 65, 65, 1,
8, 21, 58, 5, 75, 58, 96, 96, 55, 86, 86, 55, 96, 5, 31, 89, 89, 52,
86, 86, 52, 5, 89, 31, 86, 86, 1, 8, 8, 58, 5, 73, 58, 96, 16, 18, 58,
52, 68, 18, 52, 22, 68, 52, 65, 55, 96, 96, 55, 58, 8, 66, 86, 52, 21,
21, 86, 55, 75, 75, 75, 58, 75, 68, 68, 58, 75, 18, 34, 65, 52, 65, 68,
68, 52, 18, 65, 34, 19, 6, 55, 68, 68, 1, 75, 5, 55, 96, 96, 58, 89, 89,
58, 96, 5, 34, 86, 86, 52, 89, 89, 52, 5, 86, 34, 6, 6, 55, 89, 89, 1,
73, 5, 55, 16, 96, 18, 55, 52, 65, 18, 52, 19, 65, 52, 68, 96, 58, 96, 55,
55, 11, 63, 89, 52, 24, 24, 89, 75, 58, 75, 68, 58, 68, 75, 75, 58, 68,
21, 41, 65, 55, 65, 75, 75, 55, 21, 65, 41, 16, 3, 52, 68, 3, 52, 75,
75, 1, 89, 89, 58, 96, 96, 58, 89, 8, 41, 86, 86, 55, 96, 96, 55, 8, 86,
41, 3, 3, 52, 66, 3, 52, 96, 96, 1, 55, 52, 65, 16, 89, 21, 52, 21, 16,
65, 55, 75, 89, 58, 89, 52, 52, 11, 63, 96, 55, 24, 24, 96, 68, 58, 68, 0};
int mx = 0;
int zzz;

int dfs(int u, vector<int> vec, int lv)


{
if(vec.size() == 1) return vec[0];
mx = max(mx, u);
for(int ii = ids[u];;)
{
auto& z = que[ii];
vector<int> Mp[6];
int abc;
if(z.x == 0)
abc = getHeaviest(z.y[0]+1, z.y[1]+1, z.y[2]+1);
if(z.x == 1)
abc = getLightest(z.y[0]+1, z.y[1]+1, z.y[2]+1);
if(z.x == 2)
abc = getMedian(z.y[0]+1, z.y[1]+1, z.y[2]+1);
if(z.x == 3)
abc = getNextLightest(z.y[0]+1, z.y[1]+1, z.y[2]+1, z.y[3]+1);

for(int v : vec)
{
vector<pii> val;
for(int i = 0; i < 3; ++i)
val.emplace_back(perm[v][z.y[i]], z.y[i]);

sort(val.begin(), val.end());

if(z.x == 0)
Mp[val[2].y].emplace_back(v);
else
if(z.x == 1)
Mp[val[0].y].emplace_back(v);
else
if(z.x == 2)
Mp[val[1].y].emplace_back(v);
else
{
bool st = false;
for(int i = 0; i < 3; ++i)
{
if(val[i].x > perm[v][z.y[3]])
{
Mp[val[i].y].emplace_back(v);
CAPITOLUL 5. IOI 2015 5.2. SCALES 614

st = true;
break;
}
}

if(!st) Mp[val[0].y].emplace_back(v);
}

int step = 0;
for(int i = 0; i < 6; ++i) if(Mp[i].size())
{
step++;
if(i == abc-1)
return dfs(u*3 + step, Mp[i], lv / 3);
}
}

return false;
}

int nth = 6;

void init(int T)
{
for(int i = 0; i < (1 << nth); ++i)
{
vector<int> vec;
for(int j = 0; j < nth; ++j) if(i >> j & 1)
vec.emplace_back(j);
if(vec.size() == 3)
for(int j = 0; j < 3; ++j) que.emplace_back(j, vec);
if(vec.size() == 4)
{
for(int i = 0; i < 4; ++i)
{
vector<int> ret;
for(int j = 0; j < 4; ++j)
if(i != j)
ret.emplace_back(vec[j]);
ret.emplace_back(vec[i]);
que.emplace_back(3, ret);
}
}
}

vector<int> now;
for(int i = 0; i < nth; ++i)
now.emplace_back(i+1);
do
{
perm.emplace_back(now);
} while(next_permutation(now.begin(), now.end()));
}

void orderCoins()
{
int* ans = new int[6];
vector<int> zz;
for(int i = 0; i < perm.size(); ++i)
zz.emplace_back(i);
int k = dfs(0, zz, 2187);
for(int i = 0; i < 6; ++i)
ans[perm[k][i]-1] = i+1;
answer(ans);
}

// ------------- start grader --------------------

int main()
{
auto t1 = clock();

int T, i;
CAPITOLUL 5. IOI 2015 5.3. TEAMS 615

auto t2 = clock();

T = _getNumberOfTests();
init(T);

auto t3 = clock();

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


{
_initNewTest();
orderCoins();
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader --------------------


/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 0.078

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


Press any key to continue.
*/

Listing 5.2.5: checkerScales.cpp


#include "testlib.h"
#include<iostream>

using namespace std;

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/40", // argv[1]=<input-file>
(char*)"scales.out", // argv[2]=<output-file>
(char*)"../tests/40.a", // argv[3]=<answer-file>
};
cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("scales", argc, argv);


compareRemainingLines(1); // incepand cu linia nr 1

return 0;
}

5.2.3 *Rezolvare detaliată

5.3 Teams
Problema 3 - Teams 100 de puncte
CAPITOLUL 5. IOI 2015 5.3. TEAMS 616

Author: Adam Karczmarz (POL)

Avem o clasă cu studenţi numerotaţi de la 0 la N  1. ı̂n fiecare zi profesorul clasei are unele
proiecte pentru studenţi. Fiecare proiect trebuie realizat de o echipă de studenţi ı̂n decursul
aceleiaşi zi. Proiectele pot avea diverse solicitări. Pentru fiecare proiect profesorul cunoaşte
numărul exact de studenţi care formează echipa ce va lucra la el.
Fiecare student preferă să lucreze ı̂n echipe cu număr de membri dintr-un anumit interval. Mai
exact, studentul i poate fi inclus numai ı̂ntr-o echipă care conţine ı̂ntre Ai şi B i membri. ı̂n
fiecare zi un student poate fi inclus ı̂n cel mult o echipă. Unii studenţi pot să nu fie incluşi ı̂n nicio
echipă. Fiecare echipă va lucra la un singur proiect.
Profesorul a ales deja proiectele pentru fiecare din următoarele Q zile. Pentru fiecare dintre
aceste zile, determinaţi dacă este posibil să asociaţi studenţii cu echipele astfel ı̂ncât să existe câte
o echipă care să lucreze la fiecare proiect.
Exemplu
Presupunem N 4 studenţi şi Q 2 zile. Constrângerile de dimensiune a echipelor pentru
studenţi sunt date ı̂n următorul tabel:

student 0 1 2 3
A 1 2 2 2
B 2 3 3 4

În prima zi sunt M 2 proiecte. Dimensiunile cerute pentru echipe sunt K 0 1 si K 1 3.
Aceste două echipe pot fi alcătuite incluzând studentul 0 ı̂n echipa de dimensiune 1 şi ceilalti 3
studenţi ı̂n echipa de dimensiune 3.
În a doua zi sunt din nou M 2 proiecte dar timpii ceruţi pentru echipe sunt K 0 1 şi
K 1 1.
În acest caz nu este posibil să formam echipele fiind un singur student ce poate inclus ı̂ntr-o
echipă de dimensiune 1.
Cerinţă
Se dă descrierea pentru toţi studenţii: N , A, şi B, precum şi o secvenţă de Q ı̂ntrebări - câte
una pentru fiecare zi. Fiecare ı̂ntrebare constă din numarul M al proiectelor din acea zi şi o
secvenţă K de lungime M conţinând dimensiunile solicitate ale echipelor. Pentru fiecare ı̂ntrebare
programul tău trebuie să returneze dacă este posibil sa formezi toate echipele.
Trebuie să implementezi funcţiile init şi can:
ˆ init(N, A, B) - Grader-ul va apela această funcţie la ı̂nceput exact o dată.
– N : numărul de studenţi.
– A: un şir de lungime N : Ai dimensiunea minimă a unei echipe ı̂n care poate să
lucreze studentul i.
– B: un şir de lungime N : B i dimensiunea maximă a unei echipe ı̂n care poate să
lucreze studentul i.
– Aceasta funcţie nu returnează nicio valoare.
– Se ştie că 1 & Ai & B i & N pentru fiecare i 0, ..., N  1.
ˆ can(M, K) - După apelul lui init, grader-ul va apela aceasta funcţie de Q ori la rând,
câte o dată pentru fiecare zi.
– M : numărul de proiecte pentru această zi.
– K: un şir de lungime M conţinând dimensiunile cerute pentru echipă fiecarui proiect.
– Funcţia trebuie să returneze 1 dacă este posibil să se formeze toate echipele cerute şi 0
ı̂n caz contrar.
– Se ştie ca 1 & M & N , şi pentru fiecare i 0, ..., M  1 avem 1 & K i & N . De
remarcat că suma tuturor K i poate depăşi N .

Subprobleme
Să presupunem că S este suma tuturor valorilor lui M ı̂n toate apelurile can(M, K).
CAPITOLUL 5. IOI 2015 5.3. TEAMS 617

subproblema puncte N Q Constrangeri aditionale


1 21 1 & N & 100 1 & Q & 100 nu
2 13 1 & N & 100, 000 Q 1 nu
3 43 1 & N & 100, 000 1 & Q & 100, 000 S & 100, 000
4 23 1 & N & 500, 000 1 & Q & 200, 000 S & 200, 000

Grader-ul de pe calculatorul tău


Grader-ul de pe calculatorul tău citeşte intrarea ı̂n următorul format:
ˆ linia 1: N
ˆ liniile 2, ..., N  1: Ai B i
ˆ linia N + 2: Q
ˆ liniile N + 3, ..., N  Q  2: M K 0 K 1 ... K M  1

Pentru fiecare ı̂ntrebare, grader-ul de pe calculatorul afişează valoarea returnată de can.


Timp maxim de executare/test: 4.0 secunde
Memorie: total 1500 MB

5.3.1 Indicaţii de rezolvare

No preprocessing, O n  mlogn per query


The following greedy assignment works: take the smallest team size k and repeat k times:
assign the child with ai & k and smallest bi ' k. It can be implemented in time O n  m log n
by processing the teams and children in increasing order and maintaining a priority queue of
children available for assignment.
Constructive approach
Now we enter the world of geometry. If we map the children to points ai , bi , forming a set
P , a team of size k can be mapped to a rectangle R k  0, k   k, ™. We can reformulate
our query as follows: - is it possible to simultaneously assign ki points to a rectangle R ki ? (no
point can be assigned to 2 or more rectangles).
From now on we assume that we preprocess the set P , so that we can query the number of
points in some rectangle x1, x2  y1, y2 in time O log n. This can be done for example using
a persistent segment tree or some other method.
We again try to form teams in the order of increasing ks. The idea is to maintain the set of
”forbidden” points, that is, the region of plane containing the points which are either assigned to
some previously processed rectangle, or not available because yi $ k.
We need to assign k lowest points (smallest y) from outside the forbidden region and update
the forbidden region. If we represent the region by corners c1 , c2 , ..., cz , the update will remove
some (maybe 0) of consecutive corners starting before c2 and insert a single new corner.
Therefore, we only need to find the right place for the new corner.
2 2
O n log n preprocessing, O m log n  m log n query
We can find the appropriate corner cl such that there are at least k allowed points in a rectangle
0, k   0, cl  in time O l log n (we test sequentially c1 , c2 , ..., each time asking about points in
some rectangle and summing them up).
Once we found cl , we know that the y coordinate of our new corner will be somewhere between
2
y cl1  and y cl . We can binary search for the exact y in time O log n.
Ó
O n log n preprocessing, O m n log n amortized query
We Ócan combine the two solutions above to obtain
Ó
a better bound: - we run the first solution
if m % n, - we run the second solution if m & n.
2
O n log n preprocessing, O m log n query
If we store the corners in some kind of binary search tree and decompose the forbidden area
into rectangles (picture) so that each corner knows how (is responsible for) many points are there
CAPITOLUL 5. IOI 2015 5.3. TEAMS 618

in its rectangle, then we can binary search for the y of new corner directly: each guess will involve
a range sum in the corners structure and a single query to the points structure.
2
Thus, the running time is O m log n.
Non-constructive approach
The above solutions, although implicitly, construct the assignments. However, our question is
binary and thus another approach is possible.
The Hall theorem says the following:
It’s not possible to assign children to teams if there exists such subset A of team sizes ki , that
the set of points that can be assigned to any team from A (let’s call those points neighbors) is
smaller than the sum of numbers in A.
Therefore, we want to construct such set A, that the number c A = —neighbors of A— -
(sum of numbers in A) is smallest possible. If the smallest c A turns out to be negative, then
the answer is NO, otherwise it’s YES.
Assume that ki ’s are distinct and sorted (just for clarity).
We can propose a simple dynamic programming solution.
Let Di be the minimal c A such that ki is the greatest element of A. Then: Di minrDj 
Z j, i  ki  j $ ix, where Z j, i = —children a, b s.t. a " kj  1, ki , b % ki —
Optimal c A is thus equal to minrDi x.
2
Another O m log n solution
As computing Z j, i is asking about number of points in some rectangle, we can implement
2
this DP in O m log n.
Ó
Another O m n log n amortized solution
This can be again combined with the brute force solution to speed it up.
O m log n solution
Let us assume, that we have three indices i $ j $ k, such that it’s more beneficial to take index
i than j, while computing the minimum in the formula for Dk . Then, for any l % k, it’s also more
beneficial to take index i instead of j. Why? We have from out assumption:
Di  Z i, k  & Dj  Z j, k , which is equivalent to:
Dj  Di ' — children a, b s.t. a " ki  1, kj , b ' kk —.
If we replace kk with kl ' kk , the right side won’t increase, so indeed Di  Z i, l & Dj  Z j, l.
Therefore, for any indices i $ j we can compute the exact moment W i, j  of DP computation,
when the index i will be better than j.
It can be done in time O log n.
The improved DP goes like this: when computing Dk , we maintain a set of those indices that
might be useful according to our current knowledge. It also means that if indices i and j, i $ j
are in the set right now, then j is more beneficial. Hence, the last index from the set constitutes
an optimal transition for Dk .
Maintaining the set of indices involves a queue of events. For each two indices i $ j that
happen to be neighbors in the set at some point of time, we push the event ”remove j from the
set once you reach computation of Dl ”, for some l.
There are O m set updates/accesses and each time we use O log n time to compute a moment
when j % i is useless.
It remains to show, how to preprocess the input points to be able to find W i, j  in time
O log n.
Let B Dj  Di .
We need to find smallest y, such that there are at least B input points in ki  1, kj   y, ™.
This can be solved with a variant of a segment tree.
However, in a node A, B  of the segment tree we store all the points (children) with y in
A, B . The points inside a single node are sorted by increasing x and each point stores the
pointers to:
¬ ¬
ˆ the first point with x ' x and last with x & xinA, mid
¬ ¬
ˆ the first point with x ' x and last with x & xinmid  1, B .
This allows us to binary search for ki and kj only in the root of the segment tree, and then just
follow the pointers on a path to the leaf.
CAPITOLUL 5. IOI 2015 5.3. TEAMS 619

5.3.2 Coduri sursă

Listing 5.3.1: teams-17286.cpp


// https://oj.uz/submission/17286 1065 ms 330208 KB

#include "teams.h"

#include <stdio.h>
#include <stdlib.h>

#include <bits/stdc++.h>

using namespace std;

#define type(x) __typeof((x).begin())


#define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); it++)

typedef long long ll;


typedef pair < int, int > ii;

const int inf = 1e9 + 333;


const ll linf = 1e18 + inf;

const int N = 5e5 + 5;

ii a[N];

class node
{ public:
int x, l, r, lazy;
node()
{
x = l = r = lazy = 0;
}
};

node t[N * 40];


int del, ps[N];

int cnt = 1;

int insert(int x, int l, int r, int x1)


{
int nw = cnt++;
if(l == r)
{
t[nw].x = 1;
return nw;
}
int m = l + r >> 1;
if(x1 <= m)
{
t[nw].l = insert(t[x].l, l, m, x1);
t[nw].r = t[x].r;
}
else
{
t[nw].l = t[x].l;
t[nw].r = insert(t[x].r, m + 1, r, x1);
}
t[nw].x = t[t[nw].l].x + t[t[nw].r].x;
return nw;
}

int n;

void init(int x, int l, int r)


{
if(l == r)
return;
int m = l + r >> 1;
t[x].l = cnt++;
t[x].r = cnt++;
CAPITOLUL 5. IOI 2015 5.3. TEAMS 620

init(t[x].l, l, m);
init(t[x].r, m + 1, r);
}

void init(int N, int A[], int B[])


{
n = N;
for(int i = 1; i <= n; i++)
{
a[i] = ii(B[i - 1], A[i - 1]);
}

sort(a + 1, a + n + 1);

vector < ii > vs;


for(int i = 1; i <= n; i++)
vs.push_back(ii(a[i].second, i));

sort(vs.begin(), vs.end());

int ptr = 0;
ps[0] = cnt++;
init(ps[0], 1, n);
for(int i = 1; i <= n; i++)
{
ps[i] = ps[i - 1];
while(ptr < vs.size() and vs[ptr].first <= i)
{
ps[i] = insert(ps[i], 1, n, vs[ptr].second);
ptr++;
}
}
del = cnt++;
init(del, 1, n);
}

void push(int x)
{
if(t[x].lazy)
{
t[t[x].l].x = t[t[t[x].lazy].l].x;
t[t[x].r].x = t[t[t[x].lazy].r].x;
t[t[x].l].lazy = t[t[x].lazy].l;
t[t[x].r].lazy = t[t[x].lazy].r;
t[x].lazy = 0;
}
}

int get(int x, int del, int l, int r, int x1, int x2, int k)
{
if(x1 <= l and r <= x2 and t[x].x - t[del].x <= k)
{
t[del].lazy = x;
int ret = t[x].x - t[del].x;
t[del].x += ret;
return ret;
}

if(x2 < l or r < x1 or l == r)


return 0;
int m = l + r >> 1;
push(del);
int lf = get(t[x].l, t[del].l, l, m, x1, x2, k);
if(lf == k)
{
t[del].x = t[t[del].l].x + t[t[del].r].x;
return k;
}
k -= lf;
int rf = get(t[x].r, t[del].r, m + 1, r, x1, x2, k);
t[del].x = t[t[del].l].x + t[t[del].r].x;
return lf + rf;
}

void clear(int x)
{
CAPITOLUL 5. IOI 2015 5.3. TEAMS 621

if(t[x].x and t[x].l)


clear(t[x].l);
if(t[x].x and t[x].r)
clear(t[x].r);
t[x].x = t[x].lazy = 0;
}

int can(int M, int K[])


{
sort(K, K + M);
int sum = 0;
for(int i = 0; i < M; i++)
{
if(sum + K[i] > n)
return 0;
sum += K[i];
}
clear(del);
for(int i = 0; i < M; i++)
{
int id = lower_bound(a + 1, a + n + 1, ii(K[i], 0)) - a;
int res = get(ps[K[i]], del, 1, n, id, n, K[i]);
if(res != K[i])
return 0;
}
return 1;
}

// --------------- begin grader ----------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;
static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}

return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
CAPITOLUL 5. IOI 2015 5.3. TEAMS 622

}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/72", "rb");
_outputFile = fopen("teams.out", "w");

int N;
N = _readInt();

int *A = (int*)malloc(sizeof(int)*(unsigned int)N);


int *B = (int*)malloc(sizeof(int)*(unsigned int)N);

for (int i = 0; i < N; ++i)


{
A[i] = _readInt();
B[i] = _readInt();
}

init(N, A, B);

int Q;
Q = _readInt();
for (int i = 0; i < Q; ++i)
{
int M;
M = _readInt();
int *K = (int*)malloc(sizeof(int)*(unsigned int)M);
for (int j = 0; j < M; ++j)
{
K[j] = _readInt();
}
fprintf(_outputFile,"%d\n", can(M, K));
}

return 0;
}
// --------------- end grader ----------------------
/*
execution time : 3.156 s
*/

Listing 5.3.2: teams-69885.cpp


// https://oj.uz/submission/69885 1675 ms 205196 KB

#include "teams.h"

#include <stdio.h>
#include <stdlib.h>

#include<bits/stdc++.h>

using namespace std;

#define mx 500005
#define tm (tl+tr >> 1)

int s[mx*20],L[mx*20],R[mx*20],root[mx],nw;

int up(int v, int tl, int tr, int i)


{
if(tl > i || tr < i) return v;
int w = ++nw;
if(tl < tr)
{
L[w] = up(L[v],tl,tm,i);
R[w] = up(R[v],tm+1,tr,i);
}
s[w] = s[v]+1;
return w;
}
CAPITOLUL 5. IOI 2015 5.3. TEAMS 623

int qry(int a, int b, int tl, int tr, int l, int r)


{
if(tl > r || tr < l) return 0;
if(tl >= l && tr <= r) return s[a]-s[b];
return qry(L[a],L[b],tl,tm,l,r) + qry(R[a],R[b],tm+1,tr,l,r);
}

int bs(int a, int b, int tl, int tr, int k)


{
if(tl == tr) return tl;
if(s[ L[a] ] - s[ L[b] ] < k)
return bs(R[a],R[b],tm+1,tr,k-(s[ L[a] ] - s[ L[b] ]));
return bs(L[a],L[b],tl,tm,k);
}

#define mp make_pair
#define st first
#define nd second
int n;

int can(int m, int *K)


{
int t,i,l,md,r,x,y,z,las,req,ex;
stack < pair < int , pair<int,int> > > S;
S.push(mp(0,mp(mx,0)));
sort(K , K+m);
for(i=0;i<m;i++)
{
las = req = x = K[i];
ex = 0;
for(;;)
{
y = S.top().st;
z = S.top().nd.st;
if(z < las)
{
S.pop();
continue;
}
if(z == las)
{
ex += S.top().nd.nd;
S.pop();
continue;
}

l = bs(root[x],root[y],1,n,
req+ex+qry(root[x],
root[y],1,n,1,las-1) );

if(l >= (t = S.top().nd.st))


{
req -= qry(root[x],root[y],1,n,las,t-1) - ex;
las = t;
ex = S.top().nd.nd;
S.pop();
continue;
}
else
{
req -= qry(root[x],root[y],1,n,las,l)-ex;
if(req > 0) return 0;
ex = req + qry(root[x],root[y],1,n,l,l);
S.push(mp(x,mp(l,ex)));
break;
}
}
}

return 1;
}

vector < int > V[mx];


void init(int ss, int *a, int *b)
{
CAPITOLUL 5. IOI 2015 5.3. TEAMS 624

n = ss;
int i,j,p=0;
for(i=0;i<n;i++) V[ a[i] ].push_back(b[i]);
for(i=1;i<=n;i++)
{
for(j=0;j<V[i].size();j++)
p = up(p,1,n,V[i][j]);
root[i] = p;
}
}

// --------------- begin grader ----------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;
static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}

return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/72", "rb");
_outputFile = fopen("teams.out", "w");

int N;
N = _readInt();

int *A = (int*)malloc(sizeof(int)*(unsigned int)N);


int *B = (int*)malloc(sizeof(int)*(unsigned int)N);

for (int i = 0; i < N; ++i)


CAPITOLUL 5. IOI 2015 5.3. TEAMS 625

{
A[i] = _readInt();
B[i] = _readInt();
}

init(N, A, B);

int Q;
Q = _readInt();
for (int i = 0; i < Q; ++i)
{
int M;
M = _readInt();
int *K = (int*)malloc(sizeof(int)*(unsigned int)M);
for (int j = 0; j < M; ++j)
{
K[j] = _readInt();
}
fprintf(_outputFile,"%d\n", can(M, K));
}

return 0;
}
// --------------- end grader ----------------------
/*
execution time : 2.250 s
*/

Listing 5.3.3: teams-172439.cpp


// https://oj.uz/submission/172439 1552 ms 219300 KB

#include "teams.h"

#include <stdio.h>
#include <stdlib.h>

#include <bits/stdc++.h>

#define fi first
#define se second
#define eb emplace_back
#define em emplace
#define all(v) v.begin(), v.end()

using namespace std;

typedef long long ll;


typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

const int MAX = 505050;


const int INF = INT_MAX >> 1;
const ll LINF = LLONG_MAX >> 1;
const ll mod = 1e9+9;

struct Node
{
int l, r, val;
Node(int l, int r, int val) : l(l), r(r), val(val) {}
};

int n, root[MAX], h[MAX];


vector <Node> tree;
vector <int> num, numx;
pii arr[MAX];

void expand_tree(int s, int e, int k, int pnd, int cnd)


{
tree[cnd].val = tree[pnd].val + 1;
if(s == e) return;
int m = s + e >> 1;
if(k <= m)
{
tree[cnd].r = tree[pnd].r;
CAPITOLUL 5. IOI 2015 5.3. TEAMS 626

tree[cnd].l = tree.size();
tree.eb(0, 0, 0);
expand_tree(s, m, k, tree[pnd].l, tree[cnd].l);
}
else
{
tree[cnd].l = tree[pnd].l;
tree[cnd].r = tree.size();
tree.eb(0, 0, 0);
expand_tree(m+1, e, k, tree[pnd].r, tree[cnd].r);
}
}

void init(int N, int A[], int B[])


{
n = N;
for(int i = 0; i < n; i++)
{
arr[i] = make_pair(A[i], B[i]);
num.eb(B[i]);
numx.eb(A[i]);
}

sort(all(num));
sort(all(numx));
sort(arr, arr+n, [](pii a, pii b)
{
if(a.se == b.se) return a.fi < b.fi;
return a.se < b.se;
});
for(int i = 0; i < n; i++)
{
arr[i].se = i + 1;
}
sort(arr, arr+n);
tree.eb(0, 0, 0);
for(int i = 0; i < n; i++)
{
root[i+1] = tree.size();
tree.eb(0, 0, 0);
expand_tree(1, n, arr[i].se, root[i], root[i+1]);
}
return;
}

int cal(int s, int e, int l, int r, int pnd, int cnd)


{
if(s > r || e < l || l > r) return 0;
if(s >= l && e <= r) return tree[cnd].val - tree[pnd].val;
int m = s + e >> 1;
return cal(s, m, l, r, tree[pnd].l, tree[cnd].l)
+ cal(m+1, e, l, r, tree[pnd].r, tree[cnd].r);
}

int get_r(int s, int e, int cnt, int pnd, int cnd)


{
if(s == e) return s;
int m = s + e >> 1,
lcnt = tree[tree[cnd].l].val - tree[tree[pnd].l].val;
if(lcnt >= cnt)
return get_r(s, m, cnt, tree[pnd].l, tree[cnd].l);
else
return get_r(m+1, e, cnt - lcnt, tree[pnd].r, tree[cnd].r);
}

int can(int M, int K[])


{
ll temp = 0;
for(int i = 0; i < M; i++) temp += K[i];
if(temp > n) return 0;

sort(K, K+M);
stack <pii> stk;

stk.em(0, n+1);
for(int i = 0; i < M; i++)
CAPITOLUL 5. IOI 2015 5.3. TEAMS 627

{
int last = lower_bound(all(num), K[i]) - num.begin() + 1,
cnt = K[i];
int rx = upper_bound(all(numx), K[i]) - numx.begin();
while(!stk.empty() && stk.top().se < last) stk.pop();
while(!stk.empty())
{
int x = stk.top().fi, y = stk.top().se;
int temp = cal(1, n, last, y, root[x], root[rx]);
if(temp < cnt)
{
cnt -= temp;
last = y + 1;
stk.pop();
}
else
{
int l = cal(1, n, 1, last - 1, root[x], root[rx]);
int ny = get_r(1, n, l + cnt, root[x], root[rx]);
stk.em(rx, ny);
break;
}
}

if(stk.empty()) return 0;
}
return 1;
}
// --------------- begin grader ----------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;
static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}

return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
CAPITOLUL 5. IOI 2015 5.3. TEAMS 628

}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/72", "rb");
_outputFile = fopen("teams.out", "w");

int N;
N = _readInt();

int *A = (int*)malloc(sizeof(int)*(unsigned int)N);


int *B = (int*)malloc(sizeof(int)*(unsigned int)N);

for (int i = 0; i < N; ++i)


{
A[i] = _readInt();
B[i] = _readInt();
}

init(N, A, B);

int Q;
Q = _readInt();
for (int i = 0; i < Q; ++i)
{
int M;
M = _readInt();
int *K = (int*)malloc(sizeof(int)*(unsigned int)M);
for (int j = 0; j < M; ++j)
{
K[j] = _readInt();
}
fprintf(_outputFile,"%d\n", can(M, K));
}

return 0;
}
// --------------- end grader ----------------------
/*
execution time : 6.203 s
*/

Listing 5.3.4: checkerTeams.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

const string output_secret = "098d134608c94f7413faac591054ee35";

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/72", // argv[1]=<input-file>
(char*)"teams.out", // argv[2]=<output-file>
(char*)"../tests/72.a", // argv[3]=<answer-file>
};
cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("teams", argc, argv);


compareRemainingLines(1); // incepand cu linia nr 1
return 0;
}
CAPITOLUL 5. IOI 2015 5.4. HORSES 629

5.3.3 *Rezolvare detaliată

5.4 Horses
Problema 4 - Horses 100 de puncte

Author: Mansur Kutybayev (KAZ)

Lui Mansur ı̂i place să crească cai urmând tradiţia strămoşilor săi. El are acum ce mai mare
herghelie din Kazakhstan. Nu aşa stăteau lucrurile cu N ani ı̂n urmă. Când Mansur era doar un
dzhigit ( cuvântul Kazakh pentru tânăr ) el avea doar un singur cal. El visa să facă o grămadă
de bani şi ı̂n cele din urmă să ajungă un bai ( cuvântul kazakh pentru om foarte bogat).
Să numerotăm anii de la 0 la N  1 ı̂n ordine cronologică (adică anul N  1 este cel mai recent
an). Clima din fiecare an influenţa creşterea hergheliei. Pentru fiecare an i Mansur memorează
un coeficient de creştere ı̂ntreg şi pozitiv X i. Dacă la ı̂nceputul anului i aveai h cai atunci la
sfârşitul acestuia aveai h X i cai ı̂n herghelie.
Caii puteau fi vânduţi numai la sfârşitul unui an. Pentru fiecare an i, Mansur memorează un
ı̂ntreg pozitiv Y i: preţul unui cal la sfârşitul anului i. La sfârşitul fiecărui an era posibil să vinzi
oricâţi cai, fiecare la acelasi preţ Y i.
Mansur se ı̂ntreabă care este cea mai mare sumă de bani pe care ar putea să o obţină dacă
alege cele mai bune momente ı̂n care să vândă cai pe parcursul celor N ani. Tu ai onoarea să fii
invitaltul lui Mansur ı̂n toi ( cuvântul kazakh pentru vacanţă ) şi să răspunzi la ı̂ntrebarea lui.
Memoria lui Mansur se ı̂mbunătăţeşte seara, aşa ca va face un şir de M modificări. Fiecare
modificare va schimba fie una dintre valorile X i, fie una dintre valorile Y i. După fiecare
modificare el te ı̂ntreabă dinnou care e suma cea mai mare pe care o poate obţine din vânzarea
cailor. Modificările lui Manur sunt cumulative: fiecare răspuns trebuie să ţină cont de toate
modificările precedente. Reţineţi că oricare dintre valorile X i sau Y i ar putea fi modificată
de mai multe ori.
Răspunsul lui Mansur poate fi un număr foarte mare. Pentru a evita lucrul cu numere mari
9
se cere doar restul modulo 10  7 al răspunsului.
Exemplu
Să presupunem că N 3 ani, cu următoarele informaţii:

0 1 2
X 2 1 3
Y 3 4 1

Pentru valorile iniţiale Mansur poate obţine cel mai mult dacă vinde ambii săi cai la sfârşitul
anului 1.
Procesul decurge după cum urmează:
ˆ Iniţial, Mansur are un cal.
ˆ După anul 0 el are 1 X 0 2 cai.
ˆ După anul 1 el are 2 X 1 2 cai .
ˆ El poate acum să vândă cei doi cai. Profitul total va fi 2 Y 1 8.

Să presupunem acum că există M 1 modificări: Schimbă valoarea lui Y 1 ı̂n 2.
După modificare avem:

0 1 2
X 2 1 3
Y 3 2 1

În acest caz, una dintre soluţiile optime este să vinzi un cal după anul 0 şi apoi trei cai după
anul 2.
Procesul decurge după cum urmează:
ˆ Iniţial, Mansur are un cal.
ˆ După anul 0 el are 1 X 0 2 cai.
ˆ El poate să vândă unul dintre cai pentru Y 0 3, şi ı̂i mai rămâne un cal.
ˆ După anul 1 el are 1 X 1 1 cal.
CAPITOLUL 5. IOI 2015 5.4. HORSES 630

ˆ După anul 2 el are 1 X 2 3 cai.


ˆ El poate acum să vândă cei trei cai pentru 3 Y 2 3. Profitul total va fi 3  3 6.

Cerinţă
Se dau N , X, Y , şi lista de modificări. ı̂nainte de prima modificare şi după fiecare modificare,
9
calculează suma maximă pe care o poate obţine Mansur pe caii săi, modulo 10  7.
Trebuie să implementezi funcţiile init, updateX şi updateY.
ˆ init(N, X, Y) - Grader-ul va apela prima această funcţie, exact o dată.
– N : Numărul de ani.
– X: un şir de lungime N . Pentru 0 & i & N  1, X i dă coeficientul de creştere pentru
anul i.
– Y : un şir de lungime N . Pentru 0 & i & N  1, Y i dă preţul unui cal după anul i.
– Remarcaţi că atât X cât şi Y specifică valorile iniţiale date de Mansur (ı̂nainte de orice
modificare).
– După ce apelul init se ı̂ncheie, şirurile X şi Y rămân valabile, şi poţi modifica
conţinutul lor după cum doreşti.
– Această funcţie trebuie să returneze suma maximă pe care o poate obţine Mansur pe
9
caii săi pentru aceste valori iniţiale ale lui X şi Y , modulo 10  7.
ˆ updateX(pos, val)
– pos: un ı̂ntreg din intervalul 0, ..., N  1.
– val: noua valoare a lui X pos.
– Această funcţie trebuie să returneze suma maximă pe care o poate obţine Mansur după
9
această modificare, modulo 10  7.
ˆ updateY(pos, val)
– pos: un ı̂ntreg din intervalul 0, ..., N  1.
– val: noua valoare a lui Y pos.
– Această funcţie trebuie să returneze suma maximă pe care o poate obţine Mansur după
9
această modificare, modulo 10  7.
9
Se asigură că atât valorile iniţiale cât şi cele modificate pentru X i şi Y i sunt ı̂ntre 1 şi 10
inclusiv.
După init, grader-ul va apela updateX şi updateY de câteva ori. Numărul total de apeluri
ale funcţiilor updateX şi updateY va fi M .
Subprobleme

Subproblema puncte N M Precizări suplimentare


1 17 1 & N & 10 M 0 X i, Y i & 10
X 0 X 1 ... X N 1 & 1, 000
2 17 1 & N & 1, 000 0 & M & 1, 000 none
3 20 1 & N & 500, 000 0 & M & 100, 000 X i ' 2 şi val ' 2 pentru init
şi apelurile updateX
4 23 1&N & 500, 000 0 & M & 10, 000 none
5 23 1&N & 500, 000 0 & M & 100, 000 none

Grader-ul de pe calculatorul tău


Grader-ul de pe calculatorul tău citeşte date de intrare din fişierul horses.in ı̂n următorul
format:
ˆ linia 1: N
ˆ linia 2: X 0...X N  1
ˆ linia 3: Y 0...Y N  1
ˆ linia 4: M
ˆ liniile 5, ..., M  4: trei numere type pos val (type=1 pentru updateX şi type=2 pentru
updateY).
CAPITOLUL 5. IOI 2015 5.4. HORSES 631

Grader-ul de pe calculatorul tău afişează valoarea returnată de apelul init urmată de valorile
returnate de toate apelurile updateX şi updateY.
Timp maxim de executare/test: 1.5 secunde
Memorie: total 1500 MB

5.4.1 Indicaţii de rezolvare

To solve 1-st subtask we need just calculate our answer using dynamic programming dpij  -
what is maximal profit if we pass i  1 days and we have j horses then we got j ˜ xi horses and
check all number of horses that we sell today and go to the next day.
#1 Observation
First of all let’s consider to points i and j, what if we sell k horses in i-th and j-th day.
And check in what day it’s more preferable.
Profit of i-th day: x1 ˜ x2 ˜ ... ˜ xi ˜ yi
Profit of j-th day: x1 ˜ x2 ˜ ... ˜ xi ˜ xi1 ˜ ... ˜ xj ˜ yj
It depends on this
yi ? xi1 ˜ ... ˜ xj ˜ yj
¿
¡
=
so
if it’s ¿ then it’s profitably sell horses in i-th day
if it’s ¡ then it’s profitably sell horses in j-th day
if it’s = then it’s no matter when we sell them in i-th day or in j-th day.
From this point it’s clear that it is better to sell all our horses in one day that gives us maximal
profit.
Using this observation we can solve 2 subtasks.
After each query we can solve problem in O n
#2 Observation
If all xi ' 2, then after 30 days xi ˜ xi1 ˜ xi2 ˜ ... ˜ xi29 ' 2 % 10 , so position less
30 9
9
then and equal n  30 never can be optimal solution, because even if yn  30 10 and yn 1,
2 ˜ yn % yn  30
30

It’s enough to check only last 30 positions


#3 Observation
The main problem in last subtask is xi 1, but in that cases multiplication first xi numbers
and xi  1 is not change, this gives us opportunity to merge consecutive positions with xi 1,
when we merge this positions we should take the maximal yi . So if we do that, it’s enough to
check last 60 positions, because it can be no more than 30 merged 1’s between 30 last positions
where xi ' 2.
So we can store our state in some structure like set, that provide us information about current
9
merged positions, and some structure that provide us RMQ. So solution per query will be log 10 ˜
log n. Then we have to calculate answer we can use something like segment tree.
9
So overall complexity will be O m ˜ log 10  ˜ log n.

5.4.2 Coduri sursă


CAPITOLUL 5. IOI 2015 5.4. HORSES 632

Listing 5.4.1: horses-91995.cpp


// https://oj.uz/submission/91995 214 ms 58508 KB

#include "horses.h"
#include <stdio.h>
#include <stdlib.h>

#include <bits/stdc++.h>

using namespace std;

const int maxn = 5e5+10;


const int mod = 1e9+7;

typedef long long ll;

ll x[maxn], y[maxn];
int n;

struct node
{
double maior, soma;
ll maiorp, prod;
} tree[4*maxn];

void join(int node)


{
tree[node].maior = max(tree[2*node].maior,
tree[2*node].soma+tree[2*node+1].maior);
tree[node].soma = tree[2*node].soma+tree[2*node+1].soma;

if (tree[2*node].maior > tree[2*node].soma+tree[2*node+1].maior)


tree[node].maiorp = tree[2*node].maiorp;
else
tree[node].maiorp = (tree[2*node].prod*tree[2*node+1].maiorp)%mod;

tree[node].prod = (tree[2*node].prod*tree[2*node+1].prod)%mod;
}

void build(int node, int l, int r)


{
if (l == r)
{
tree[node].maior = log2(x[l])+log2(y[l]);
tree[node].soma = log2(x[l]);

tree[node].maiorp = (x[l]*y[l])%mod;
tree[node].prod = x[l]%mod;

return;
}

int mid = (l+r)>>1;

build(2*node, l, mid); build(2*node+1, mid+1, r);


join(node);
}

void upd(int node, int l, int r, int pos)


{
if (l == r)
{
tree[node].maior = log2(x[l])+log2(y[l]);
tree[node].soma = log2(x[l]);

tree[node].maiorp = (x[l]*y[l])%mod;
tree[node].prod = x[l]%mod;

return;
}

int mid = (l+r)>>1;

if (pos <= mid) upd(2*node, l, mid, pos);


else upd(2*node+1, mid+1, r, pos);
CAPITOLUL 5. IOI 2015 5.4. HORSES 633

join(node);
}

int init(int N, int X[], int Y[])


{
n = N;
for (int i = 0; i < n; i++)
x[i] = (ll)X[i], y[i] = (ll)Y[i];
build(1, 0, n-1);

return (int)tree[1].maiorp;
}

int updateX(int pos, int val)


{
x[pos] = (ll)val;
upd(1, 0, n-1, pos);

return (int)tree[1].maiorp;
}

int updateY(int pos, int val)


{
y[pos] = (ll)val;
upd(1, 0, n-1, pos);

return (int)tree[1].maiorp;
}

// ------------------ begin grader ----------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;
static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}
if (!_charsNumber || _currentChar == _charsNumber)
{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}
if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
CAPITOLUL 5. IOI 2015 5.4. HORSES 634

if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/59", "rb");
_outputFile = fopen("horses.out", "w");

int N; N = _readInt();

int *X = (int*)malloc(sizeof(int)*(unsigned int)N);


int *Y = (int*)malloc(sizeof(int)*(unsigned int)N);

for (int i = 0; i < N; i++)


{
X[i] = _readInt();
}

for (int i = 0; i < N; i++)


{
Y[i] = _readInt();
}

fprintf(_outputFile,"%d\n",init(N,X,Y));

int M; M = _readInt();

for (int i = 0; i < M; i++)


{
int type; type = _readInt();
int pos; pos = _readInt();
int val; val = _readInt();

if (type == 1)
{
fprintf(_outputFile,"%d\n",updateX(pos,val));
}
else
if (type == 2)
{
fprintf(_outputFile,"%d\n",updateY(pos,val));
}
}

return 0;
}

// ------------------ end grader ----------------


/*
execution time : 0.344 s
*/

Listing 5.4.2: horses-102703.cpp


// https://oj.uz/submission/102703 216 ms 45508 KB

#include "horses.h"
#include <stdio.h>
#include <stdlib.h>

#include <bits/stdc++.h>

using namespace std;

const int md = (int) 1e9 + 7;


const long long inf = 1ll << 30;

inline int mul(int x, int y)


{
return (int) ((long long) x * y % md);
}

class node
{
CAPITOLUL 5. IOI 2015 5.4. HORSES 635

public:
int all;
int left;
int right;
int sell;
int prod;
int ans;
};

vector<node> tree;
vector<int> a, b;
int n;

void pull(int x, int z)


{
tree[x].all = min((long long) tree[x + 1].all * tree[z].all, inf);
tree[x].prod = mul(tree[x + 1].prod, tree[z].prod);
if (tree[x + 1].sell > (long long) tree[x + 1].right * tree[z].left)
{
tree[x].ans = tree[x + 1].ans;
tree[x].sell = tree[x + 1].sell;
tree[x].left = tree[x + 1].left;
tree[x].right = min((long long) tree[x + 1].right * tree[z].all, inf);
}
else
{
tree[x].ans = mul(tree[z].ans, tree[x + 1].prod);
tree[x].sell = tree[z].sell;
tree[x].left = min((long long) tree[x + 1].all * tree[z].left, inf);
tree[x].right = tree[z].right;
}
}

void build(int x, int l, int r)


{
if (l == r)
{
tree[x].all = a[l];
tree[x].prod = a[l];
tree[x].ans = mul(a[l], b[l]);
tree[x].sell = b[l];
tree[x].left = min((long long) a[l] * b[l], inf);
tree[x].right = 1;
}
else
{
int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
build(x + 1, l, y);
build(z, y + 1, r);
pull(x, z);
}
}

void modify(int x, int l, int r, int p)


{
if (l == r)
{
tree[x].all = a[l];
tree[x].prod = a[l];
tree[x].ans = mul(a[l], b[l]);
tree[x].sell = b[l];
tree[x].left = min((long long) a[l] * b[l], inf);
tree[x].right = 1;
}
else
{
int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
if (p <= y)
{
modify(x + 1, l, y, p);
}
else
{
modify(z, y + 1, r, p);
}
pull(x, z);
CAPITOLUL 5. IOI 2015 5.4. HORSES 636

}
}

int init(int N, int X[], int Y[])


{
n = N;
for (int i = 0; i < n; ++i)
{
a.push_back(X[i]);
b.push_back(Y[i]);
}
tree.resize(n * 2 - 1);
build(0, 0, n - 1);
return tree[0].ans;
}

int updateX(int pos, int val)


{
a[pos] = val;
modify(0, 0, n - 1, pos);
return tree[0].ans;
}

int updateY(int pos, int val)


{
b[pos] = val;
modify(0, 0, n - 1, pos);
return tree[0].ans;
}

// ------------------ begin grader ----------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;
static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}
if (!_charsNumber || _currentChar == _charsNumber)
{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}
if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
CAPITOLUL 5. IOI 2015 5.4. HORSES 637

if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/59", "rb");
_outputFile = fopen("horses.out", "w");

int N; N = _readInt();

int *X = (int*)malloc(sizeof(int)*(unsigned int)N);


int *Y = (int*)malloc(sizeof(int)*(unsigned int)N);

for (int i = 0; i < N; i++)


{
X[i] = _readInt();
}

for (int i = 0; i < N; i++)


{
Y[i] = _readInt();
}

fprintf(_outputFile,"%d\n",init(N,X,Y));

int M; M = _readInt();

for (int i = 0; i < M; i++)


{
int type; type = _readInt();
int pos; pos = _readInt();
int val; val = _readInt();

if (type == 1)
{
fprintf(_outputFile,"%d\n",updateX(pos,val));
}
else
if (type == 2)
{
fprintf(_outputFile,"%d\n",updateY(pos,val));
}
}

return 0;
}

// ------------------ end grader ----------------


/*
execution time : 1.225 s
*/

Listing 5.4.3: horses-202434.cpp


// https://oj.uz/submission/202434 198 ms 66680 KB

#include "horses.h"
#include <stdio.h>
#include <stdlib.h>

#include<iostream>

#define MOD (1000000007LL)


#define LL long long
#define INF (1LL<<30)
#define MAXN 500005

using namespace std;

LL n, x[MAXN], y[MAXN];

class Segtree
{
LL ST[MAXN*4][5]; // mod product, actual (or inf), opt product,
CAPITOLUL 5. IOI 2015 5.4. HORSES 638

// opt, prod of stuff to right of opt


public:
void pushup(int cur)
{
ST[cur][0]=(ST[2*cur][0]*ST[2*cur+1][0])%MOD;
ST[cur][1]=min(ST[2*cur][1]*ST[2*cur+1][1], INF);
if (ST[2*cur][4] == INF ||
y[ST[2*cur][3]] <= ST[2*cur+1][2]*ST[2*cur][4])
{
ST[cur][2] = min(ST[2*cur+1][2]*ST[2*cur][1], INF);
ST[cur][3] = ST[2*cur+1][3];
ST[cur][4] = ST[2*cur+1][4];
}
else
{
ST[cur][2] = ST[2*cur][2];
ST[cur][3] = ST[2*cur][3];
ST[cur][4] = min(ST[2*cur][4]*ST[2*cur+1][1], INF);
}
}

void build(int l, int r, int cur)


{
if (l == r)
{
ST[cur][0] = x[l];
ST[cur][1] = x[l];
ST[cur][2] = min(x[l]*y[l], INF);
ST[cur][3] = l;
ST[cur][4] = 1;
}
else
{
int mid=(l+r)>>1;
build(l, mid, 2*cur);
build(mid+1, r, 2*cur+1);
pushup(cur);
}
}

void upd(int ind, int l, int r, int cur)


{
if (l > ind || r < ind) {return;}
if (l == r)
{
ST[cur][0] = x[l];
ST[cur][1] = x[l];
ST[cur][2] = min(x[l]*y[l], INF);
ST[cur][3] = l;
}
else
{
int mid=(l+r)>>1;
upd(ind, l, mid, 2*cur);
upd(ind, mid+1, r, 2*cur+1);
pushup(cur);
}
}

LL ask(int ind, int l, int r, int cur)


{ //ask for prefix mod product
if (ind < l) {return(1);}
if (r <= ind) {return(ST[cur][0]);}
else
{
int mid=(l+r)>>1;
return((ask(ind, l, mid, 2*cur)*
ask(ind, mid+1, r, 2*cur+1))%MOD);
}
}

LL opt() {return(ST[1][3]);}
} ST;

int init(int N, int X[], int Y[])


{
CAPITOLUL 5. IOI 2015 5.4. HORSES 639

n=N;
for (int i=0; i<N; i++)
{
x[i] = X[i];
y[i] = Y[i];
}
ST.build(0, N-1, 1);
LL p = ST.opt(), prod = ST.ask(p, 0, N-1, 1);
LL ans = (prod * y[p])%MOD;
return( (int) ans);
}

int updateX(int pos, int val)


{
x[pos]=val;
ST.upd(pos, 0, n-1, 1);
LL p = ST.opt(), prod = ST.ask(p, 0, n-1, 1);
LL ans = (prod * y[p])%MOD;
return( (int) ans);
}

int updateY(int pos, int val)


{
y[pos]=val;
ST.upd(pos, 0, n-1, 1);
LL p = ST.opt(), prod = ST.ask(p, 0, n-1, 1);
LL ans = (prod * y[p])%MOD;
return( (int) ans);
}

// ------------------ begin grader ----------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;
static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}
if (!_charsNumber || _currentChar == _charsNumber)
{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}
if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
CAPITOLUL 5. IOI 2015 5.4. HORSES 640

if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/59", "rb");
_outputFile = fopen("horses.out", "w");

int N; N = _readInt();

int *X = (int*)malloc(sizeof(int)*(unsigned int)N);


int *Y = (int*)malloc(sizeof(int)*(unsigned int)N);

for (int i = 0; i < N; i++)


{
X[i] = _readInt();
}

for (int i = 0; i < N; i++)


{
Y[i] = _readInt();
}

fprintf(_outputFile,"%d\n",init(N,X,Y));

int M; M = _readInt();

for (int i = 0; i < M; i++)


{
int type; type = _readInt();
int pos; pos = _readInt();
int val; val = _readInt();

if (type == 1)
{
fprintf(_outputFile,"%d\n",updateX(pos,val));
}
else
if (type == 2)
{
fprintf(_outputFile,"%d\n",updateY(pos,val));
}
}

return 0;
}

// ------------------ end grader ----------------


/*
execution time : 1.109 s
*/

Listing 5.4.4: checkerHorses.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

const string output_secret = "098d134608c94f7413faac591054ee35";

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/59", // argv[1]=<input-file>
(char*)"horses.out", // argv[2]=<output-file>
(char*)"../tests/59.a", // argv[3]=<answer-file>
};
cout<<"argc = "<<argc<<"\n";
CAPITOLUL 5. IOI 2015 5.5. SORTING 641

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

registerChecker("horses", argc, argv);


compareRemainingLines(1); // incepand cu linia nr 1
return 0;
}

5.4.3 *Rezolvare detaliată

5.5 Sorting
Problema 5 - Sorting 100 de puncte

Author: Weidong Hu (CHN)

Aizhan are un şir de N numere ı̂ntregi S 0, S 1, ..., S N  1. Acesta constă ı̂n numere
distincte de la 0 la N  1. Ea incearcă să sorteze aceast şir ı̂n ordine crescătoare interschimbând
anumite perechi de elemente. Prietenul său Ermek interschimbă, de asemenea anumite perechi de
elemente - dar nu neapărat ı̂ntr-un mod care să o ajute.
Ermek şi Aizhan modifica şirul ı̂ntr-o serie de runde. ı̂n fiecare rundă, ı̂ntâi Ermek face o
interschimbare şi atunci Aizhan face şi ea o alta. Mai exact, persoana care face interschimbarea
alege doi indici valizi şi interschimbă elementele cu aceşti indici. De remarcat că cei doi indici nu
trebuie neapărat ca să fie distincţi. Dacă ei sunt egali, persoana schimbă un element cu el ı̂nsuşi,
deci nu afectează şirul.
Aizhan ştie că lui Ermek nu ı̂i pasă să sorteze şirul S. Ea ştie, de asemenea, indicii pe care
Ermek ı̂i va alege. Ermek plănuieşte să ia parte la M runde de interschimbări. Numerotăm aceste
runde de la 0 la M  1. Pentru fiecare i cuprins ı̂ntre 0 şi M  1 inclusiv, Ermek va alege indicii
X i şi Y i pentru runda i.
Aizhan vrea să sorteze şirul S. ı̂naintea fiecărei runde, dacă Aizhan vede că şirul este deja
sortat crescător, ea va ı̂ncheia procesul. Fiind dat şirul S şi indicii pe care ı̂i va alege Ermek,
sarcina ta este să găseşti secvenţa de interschimbări prin care Aizhan să ordoneze şirul S. ı̂n plus,
ı̂n unele subprobleme ţi se cere să găseşti o secvenţă de interschimbări cât mai scurtă posibil. Poţi
presupune că şirul S se poate ordona ı̂n M sau mai puţine runde.
De remarcat că, dacă Aizhan observă şirul S sortat după o mutare a lui Ermek, ea poate
alege doi indici egali (de exemplu 0 şi 0). şirul rezultat este de asemenea sortat, deci Aizhan ı̂şi
ı̂ndeplineşte scopul. De remarcat, de asemenea, că dacă şirul iniţial S este deja sortat, numărul
minim de runde necesar ordonării este 0.
Examplul 1
Presupunem următoarele:
ˆ şirul iniţial este S 4, 3, 2, 1, 0.
ˆ Ermek ı̂şi propune să facă M 6 mutări.
ˆ şirurile X şi Y care descriu indicii pe care Ermek ı̂i foloseşte sunt: X 0, 1, 2, 3, 0, 1 şi
Y 1, 2, 3, 4, 1, 2. Cu alte cuvinte, perechile de indici pe care Ermek plănuieşte să ı̂i aleagă
sunt: (0,1), (1,2), (2,3), (3,4), (0,1) şi (1,2).

În această situaţie Aizhan poate ordona şirul S ı̂n ordinea 0, 1, 2, 3, 4 ı̂n trei runde. Ea poate
face asta alegând indicii (0,4), (1,3) şi (3,4).
Tabelul următor arată cum Ermek şi Aizhan modifică şirul.
CAPITOLUL 5. IOI 2015 5.5. SORTING 642

Runda Jucătorul Perechea de indici de la in- şirul


terschimbare
la ı̂nceput 4,3,2,1,0
0 Ermek (0,1) 3,4,2,1,0
0 Aizhan (0,4) 0,4,2,1,3
1 Ermek (1,2) 0,2,4,1,3
1 Aizhan (1,3) 0,1,4,2,3
2 Ermek (2,3) 0,1,2,4,3
2 Aizhan (3,4) 0,1,2,3,4

Examplul 2
Presupunem următoarele:
ˆ Şirul iniţial este S 3, 0, 4, 2, 1.
ˆ Ermek ı̂şi propune să facă M 5 mutări.
ˆ Perechile de indici pe care Ermek plănuieşte să ı̂i aleagă sunt: (1,1), (4,0), (2,3), (1,4) şi
(0,4).

În această situaţie Aizhan poate sorta secvenţa S ı̂n trei runde, de exemplu alegând perechile
de indici (1,4), (4,2) şi (2,2). Următorul tabel arată cum Ermek şi Aizhan modifică secvenţa.

Runda Jucătorul Perechea de indici de la in- şirul


terschimbare
la ı̂nceput 3,0,4,2,1
0 Ermek (1,1) 3,0,4,2,1
0 Aizhan (1,4) 3,1,4,2,0
1 Ermek (4,0) 0,1,4,2,3
1 Aizhan (4,2) 0,1,3,2,4
2 Ermek (2,3) 0,1,2,3,4
2 Aizhan (2,2) 0,1,2,3,4

Cerinţa
Ţi se dă şirul S, numărul M şi şirurile de indici X şi Y . Determină o secvenţă de inter-
schimbări pe care Aizhan o poate folosi pentru a sorta şirul S. ı̂n subproblemele 5 şi 6 secvenţa
de interschimbări pe care să o găseşti trebuie să fie cea mai scurtă posibilă.
Trebuie să implementezi funcţia findSwapPairs:
ˆ findSwapPairs(N, S, M, X, Y, P, Q) - Această funcţie va fi apelată de grader ex-
act o dată.
– N : lungimea şirului S.
– S: un şir de ı̂ntregi reprezentând şirul S iniţial.
– M : numărul de paşi pe care Ermek plănuieşte să ı̂i facă.
– X, Y : tablouri de ı̂ntregi de lungime M . Pentru 0 & i & M  1, ı̂n runda i, Ermek
plănuieşte să aleagă perechea de indici X i şi Y i.
– P , Q: tablouri de ı̂ntregi. Utilizează aceste tablouri pentru a returna o posibilă
secvenţă de interschimbări prin care Aizhan poate ordona şirul S. Notăm cu R lungimea
secvenţei de interschimbări pe care programul tău a găsit-o. Pentru fiecare i cuprins
ı̂ntre 0 şi R  1 inclusiv, indicii pe care Aizhan trebuie să ı̂i aleagă ı̂n runda sunt
memoraţi ı̂n P i şi Qi. Poţi presupune că tablourile P şi Q au deja alocate câte M
elemente fiecare.
– Această funcţie trebuie să returneze valoarea lui R (definită mai sus).

Subprobleme
CAPITOLUL 5. IOI 2015 5.5. SORTING 643

subproblema puncte N M restricţii pentru X, Y cerinţa


pentru R
1&N &5 R&M
2
1 8 M N X i Y i 0 for all i
2 12 1 & N & 100 M 30N X i Y i 0 for all i R&M
3 16 1 & N & 100 M 30N X i 0, Y i 1 for all i R&M
4 18 1 & N & 500 M 30N none R&M
5 20 1 & N & 2, 000 M 3N none minimum
posibil
6 26 1&N & 200, 000 M 3N none minimum
posibil

Poţi presupune că există o soluţie care să necesite maxim M runde.
Grader-ul de pe calculatorul tău
Grader-ul de pe calculatorul tău citeşte intrarea din fişierul sorting.in ı̂n următorul format:
ˆ linia 1: N
ˆ linia 2: S 0 ... S N  1
ˆ linia 3: M
ˆ liniile 4, ..., M  3: X i Y i

Grader-ul de pe calculatorul tău afişează următoarele:


ˆ inia 1: valoarea R returnată de findSwapPairs
ˆ liniile 2  i, pentru 0 & i $ R: P i Qi

Timp maxim de executare/test: 1.0 secunde


Memorie: total 1500 MB

5.5.1 Indicaţii de rezolvare

Subtask 1
In subtask 1, Bob do not change sequence s, thus any method that can sort the sequence in
increasing order will be a solution.
2
A bubble sort or insertion sort may cost at most O n  swaps. A merge sort or quick sort may
cost at most O n log n swaps.
The optimized solutions is selection sort. Since the sequence contains 0, 1, ..., n  1 exactly
once, we can swap value i into position i one by one (i=0, 1, ..., n  1). This costs at most n  1
swaps. It can be proved that the number of swaps is minimized.
Subtask 2
In subtask 2, Bob will swap positions 0 and 1, which makes the problem more complicated.
However, the solution does not change much. We can swap value i into position i one by one from
the rightmost position to the leftmost one (i n  1, n  2, ..., 1, 0). The number of swaps is also
minimized.
Subtask 3
In subtask 3, we distinguish Alice and Bobs swaps. Let a0 0, a1 1, ..., an  1 n  1
be n plates in a queue, and b0 s0, b1 s1, ..., bn  1 sn  1 be n apples on plates
0, 1, ..., n  1. Then si bai.

If Bob swap two positions x and y, it can be viewed as two plates are swapped. For example,
in the above case, if Bob swaps numbers at positions 0 and 1, the result sequence will be:
CAPITOLUL 5. IOI 2015 5.5. SORTING 644

If Alice swap two positions p and q, it can be viewed as two apples sp and sq  are swapped.
For example, if Alice swaps numbers are positions 0 and 4, it can be viewed as apples s0 3
and s4 0 are swapped:

Now Bob swaps two plates in each round and Alice swaps two apples. Since they operates on
difference objects, the sequence of operations can be separated. Let t be the number of rounds
Alice takes. It can be viewed as Bob first swap t pairs of plates, then Alice swap t pairs of apples.
Since Alice can sort all apples in increasing order in less than n swaps, the minimum number of
rounds is less than n.
Solution: One solution for this problem would be enumerate t from 0 to n  1, and check
whether Alice can sort all apples within t swaps after Bob swap first t pairs of plates. This costs
2
O n  time and will get timeout for some test cases.
Binary Search: For this problem, if p0 , q0 , p1 , q1 , ..., pt1 , qt1  be a solution for Alice
in t swaps, then p0 , q0 , p1 , q1 , ..., pt1 , qt1 , xt , yt , ..., xe1 , ye1  must be a solution for
Alice in e swaps (e % t). Which means that if there is a feasible solution in t swaps, then there
must be feasible solutions in more than t swaps. Which makes the problem solvable using binary
search. The time complexity reduces to O n log n and full mark is expected.
Implementation: The implementation of this problem may be a little bit error prone. It is
recommended that programs first find out pairs of numbers (i.e. pairs of apples) to swap, then
transform numbers into their positions. An inversed array that maps each number to its position
is helpful.
Comments
2
The O n  time solution is essentially updating a set of cycles under splicing (edge insertion
/ deletion) operations. Some care is needed in test data making to distinguish it from O n log n
time solutions, perhaps the bounds can be raised to ease this process.
1.5
This solution can also be made to run in O n  or O n log n time using BBST-like data
structures (e.g. http://contest.felk.cvut.cz/11cerc/solved/r/). This is way more
work than the intended solution though.

5.5.2 Coduri sursă

Listing 5.5.1: sorting-70140.cpp


// https://oj.uz/submission/70140 187 ms 16432 KB

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "sorting.h"

#include<algorithm>
#include<vector>
#include<iostream>

using namespace std;

vector<pair<int,int> > v;
int n,ps[300000],s[300000],x[300000],y[300000],rev_ps[300000],r[300000];
CAPITOLUL 5. IOI 2015 5.5. SORTING 645

int qayl_qan()
{
int q=0;
for(int i=0;i<n;i++)
rev_ps[ps[i]]=i;
for(int i=0;i<n;i++)
{
if(ps[rev_ps[i]]==ps[i])
continue;
int e=ps[i];
swap(ps[rev_ps[i]],ps[i]);
q++;
swap(rev_ps[i],rev_ps[e]);
}
return q;
}

int stug(int e)
{
for(int i=0;i<n;i++)
ps[i]=s[i];
for(int i=0;i<=e;i++)
swap(ps[x[i]],ps[y[i]]);
if(qayl_qan()<=e+1)
return 1;
else
return 0;
}

int bin(int l,int r)


{
if(l==r)
return r;

int mid=(l+r)/2;

if(stug(mid))
bin(l,mid);
else
bin(mid+1,r);
}

int findSwapPairs(int N, int S[], int M, int X[], int Y[], int P[], int Q[])
{
if(M>=N)
M=N-1;
for(int i=0;i<N;i++)
s[i]=S[i];
for(int i=0;i<M;i++)
{
x[i]=X[i];
y[i]=Y[i];
}
n=N;
for(int i=0;i<n;i++)
ps[i]=s[i];
if(qayl_qan()==0)
return 0;
int t=bin(0,M-1);
t++;
for(int i=0;i<n;i++)
ps[i]=s[i];
for(int i=0;i<t;i++)
swap(ps[x[i]],ps[y[i]]);
for(int i=0;i<n;i++)
rev_ps[ps[i]]=i;
for(int i=0;i<n;i++)
{
if(ps[rev_ps[i]]==ps[i])
continue;
int e=ps[i];
swap(ps[rev_ps[i]],ps[i]);
v.push_back({ps[i],e});
swap(rev_ps[i],rev_ps[e]);
}
for(int i=0;i<n;i++)
CAPITOLUL 5. IOI 2015 5.5. SORTING 646

r[s[i]]=i;
for(int i=0;i<t;i++)
{
swap(s[x[i]],s[y[i]]);
swap(r[s[x[i]]],r[s[y[i]]]);
if(v.size()>i)
{
P[i]=r[v[i].first];
Q[i]=r[v[i].second];
}
else
{
P[i]=0;
Q[i]=0;
}
swap(s[P[i]],s[Q[i]]);
swap(r[s[P[i]]],r[s[Q[i]]]);
}
return t;
}

// -------------- begin grader ------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
CAPITOLUL 5. IOI 2015 5.5. SORTING 647

_inputFile = fopen("../tests/subtaskf/25", "rb");


_outputFile = fopen("sorting.out", "w");

int N, M;
N = _readInt();
int *S = (int*)malloc(sizeof(int) * (unsigned int)N);

for (int i = 0; i < N; ++i)


S[i] = _readInt();

M = _readInt();

int *X = (int*)malloc(sizeof(int) * (unsigned int)M),


*Y = (int*)malloc(sizeof(int) * (unsigned int)M);
for (int i = 0; i < M; ++i)
{
X[i] = _readInt();
Y[i] = _readInt();
}

int *P = (int*)malloc(sizeof(int) * (unsigned int)M),


*Q = (int*)malloc(sizeof(int) * (unsigned int)M);

int ans = findSwapPairs(N, S, M, X, Y, P, Q);

fprintf(_outputFile, "%d\n", ans);

for (int i = 0; i < ans; ++i)


fprintf(_outputFile, "%d %d\n", P[i], Q[i]);

return 0;
}

// -------------- end grader ------------------


/*
execution time : 1.750 s
*/

Listing 5.5.2: sorting-114135.cpp


// https://oj.uz/submission/114135 200 ms 23028 KB

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "sorting.h"

#include <bits/stdc++.h>

#define pii pair<int, int>


#define ff first
#define ss second

using namespace std;

#define NN 200005

int val[NN];
int loc[NN];
int S[NN];
int n;
vector<pii> sw;

bool in[NN];

void dfs(int s, int now)


{
in[now] = 1;
if(now == s) return;
dfs(s, S[now]);
}

int findSwapPairs(int N, int qq[], int M,


int X[], int Y[], int P[], int Q[])
{
CAPITOLUL 5. IOI 2015 5.5. SORTING 648

n = N;
for(int i = 0; i < N; i++)
{
S[i] = qq[i];
val[i] = S[i];
loc[val[i]] = i;
}
int l = 0, r = N, m;
while(l != r)
{
int cyc = 0;
m = (l + r) >> 1;
for(int i = 0; i < m; i++)
swap(S[X[i]], S[Y[i]]);

for(int i = 0; i < n; i++)


{
if(in[i]) continue;
cyc++;
dfs(i, S[i]);
}
if(n - cyc <= m) r = m;
else l = m + 1;
memset(in, 0, sizeof in);
for(int i = 0; i < n; i++)
S[i] = val[i];

for(int i = 0; i < r; i++)


swap(S[X[i]], S[Y[i]]);
for(int i = 0; i < n; i++)
{
while(S[i] != i)
{
sw.push_back({S[i], i});
swap(S[i], S[S[i]]);
}
}

for(int i = 0; i < r; i++)


{
swap(val[X[i]], val[Y[i]]);
swap(loc[val[X[i]]], loc[val[Y[i]]]);
if(sw.empty())
{
P[i] = 0;
Q[i] = 0;
}
else
{
P[i] = loc[sw.back().ff];
Q[i] = loc[sw.back().ss];
swap(val[P[i]], val[Q[i]]);
swap(loc[val[P[i]]], loc[val[Q[i]]]);
sw.pop_back();
}
}

return l;
}

// -------------- begin grader ------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}
CAPITOLUL 5. IOI 2015 5.5. SORTING 649

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtaskf/25", "rb");
_outputFile = fopen("sorting.out", "w");

int N, M;
N = _readInt();
int *S = (int*)malloc(sizeof(int) * (unsigned int)N);

for (int i = 0; i < N; ++i)


S[i] = _readInt();

M = _readInt();

int *X = (int*)malloc(sizeof(int) * (unsigned int)M),


*Y = (int*)malloc(sizeof(int) * (unsigned int)M);
for (int i = 0; i < M; ++i)
{
X[i] = _readInt();
Y[i] = _readInt();
}

int *P = (int*)malloc(sizeof(int) * (unsigned int)M),


*Q = (int*)malloc(sizeof(int) * (unsigned int)M);

int ans = findSwapPairs(N, S, M, X, Y, P, Q);

fprintf(_outputFile, "%d\n", ans);

for (int i = 0; i < ans; ++i)


fprintf(_outputFile, "%d %d\n", P[i], Q[i]);

return 0;
}

// -------------- end grader ------------------


/*
CAPITOLUL 5. IOI 2015 5.5. SORTING 650

execution time : 1.688 s


*/

Listing 5.5.3: sorting-129522.cpp


// https://oj.uz/submission/129522 186 ms 21988 KB
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "sorting.h"

#include <vector>
#include <iostream>

using namespace std;

int findSwapPairs(int n, int S[], int M, int X[], int Y[], int P[], int Q[])
{
vector<int> perm(n);
bool bc = true;
for (int i = 0; i < n; i++)
{
perm[i] = S[i];
if (perm[i] != i) bc = false;
}
if (bc) return 0;
vector<int> b = perm;
vector<int> beg = perm;
for (int i = 0; i < n; i++)
{
swap(perm[X[i]], perm[Y[i]]);
}
vector<int> end = perm;
int l = 0;
int r = n;
while (r - l > 1)
{
int m = (l + r)/2;
vector<int> mv = beg;
for (int i = l; i < m; i++)
{
swap(mv[X[i]], mv[Y[i]]);
}

vector<bool> v(n, false);


int ctr = 0;
for (int i = 0; i < n; i++)
{
if (v[i]) continue;
v[i] = true;
int cc = 0;
int cr = i;
while (mv[cr] != i)
{
cc++;
cr = mv[cr];
v[cr] = true;
}
ctr += cc;
}

if (ctr <= m)
{
r = m;
end = mv;
}
else
{
l = m;
beg = mv;
}
}

vector<pair<int,int>> swp;
perm = b;
CAPITOLUL 5. IOI 2015 5.5. SORTING 651

for (int i = 0; i < n; i++)


{
while (end[i] != i)
{
swp.push_back({end[i], end[end[i]]});
swap(end[i], end[end[i]]);
}
}

vector<int> revind(n);
for (int i = 0; i < n; i++)
{
revind[perm[i]] = i;
}

for (int i = 0; i < r; i++)


{
swap(revind[perm[X[i]]], revind[perm[Y[i]]]);
swap(perm[X[i]], perm[Y[i]]);
if (i < swp.size())
{
Q[i] = revind[swp[i].first];
P[i] = revind[swp[i].second];
swap(revind[swp[i].first], revind[swp[i].second]);
swap(perm[Q[i]], perm[P[i]]);
}
else
{
Q[i] = 0;
P[i] = 0;
}
}

return r;
}

// -------------- begin grader ------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
CAPITOLUL 5. IOI 2015 5.5. SORTING 652

{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtaskf/25", "rb");
_outputFile = fopen("sorting.out", "w");

int N, M;
N = _readInt();
int *S = (int*)malloc(sizeof(int) * (unsigned int)N);

for (int i = 0; i < N; ++i)


S[i] = _readInt();

M = _readInt();

int *X = (int*)malloc(sizeof(int) * (unsigned int)M),


*Y = (int*)malloc(sizeof(int) * (unsigned int)M);
for (int i = 0; i < M; ++i)
{
X[i] = _readInt();
Y[i] = _readInt();
}

int *P = (int*)malloc(sizeof(int) * (unsigned int)M),


*Q = (int*)malloc(sizeof(int) * (unsigned int)M);

int ans = findSwapPairs(N, S, M, X, Y, P, Q);

fprintf(_outputFile, "%d\n", ans);

for (int i = 0; i < ans; ++i)


fprintf(_outputFile, "%d %d\n", P[i], Q[i]);

return 0;
}

// -------------- end grader ------------------


/*
execution time : 1.828 s
*/

Listing 5.5.4: sorting-155927.cpp


// https://oj.uz/submission/155927 508 ms 22904 KB
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "sorting.h"

#include <bits/stdc++.h>

using namespace std;

int n,m,*x,*y,*s,*p,*q;
int a[500010],b[500010],c[500010],d[500010];
bool f(int z)
{
int i,j;
for(i=0;i<n;i++)
{
a[i]=b[i]=s[i];
}
CAPITOLUL 5. IOI 2015 5.5. SORTING 653

for(i=0;i<z;i++)
{
swap(a[x[i]],a[y[i]]);
}
for(i=0;i<n;i++)
{
c[a[i]]=i;
d[b[i]]=i;
}
j=0;
for(i=0;i<z;i++)
{
swap(d[b[x[i]]],d[b[y[i]]]);
swap(b[x[i]],b[y[i]]);
while(j<n&&a[j]==j)j++;
if(j==n)p[i]=q[i]=0;
else
{
p[i]=d[j],q[i]=d[a[j]];
swap(b[d[j]],b[d[a[j]]]);
swap(d[j],d[a[j]]);
c[a[j]]=c[j];
swap(a[j],a[c[j]]);
c[j]=j;
}
}

for(i=0;i+1<n;i++) if(a[i]>a[i+1]) return false;


return true;
}

int findSwapPairs(int N, int S[], int M, int X[], int Y[], int P[], int Q[])
{
n=N;s=S;m=M;x=X;y=Y;p=P;q=Q;
int lo=0,hi=M;
while(lo<hi)
{
int mid=(lo+hi)/2;
if(f(mid))hi=mid;
else lo=mid+1;
}
f(lo);
return lo;
}

// -------------- begin grader ------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}
CAPITOLUL 5. IOI 2015 5.5. SORTING 654

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtaskf/25", "rb");
_outputFile = fopen("sorting.out", "w");

int N, M;
N = _readInt();
int *S = (int*)malloc(sizeof(int) * (unsigned int)N);

for (int i = 0; i < N; ++i)


S[i] = _readInt();

M = _readInt();

int *X = (int*)malloc(sizeof(int) * (unsigned int)M),


*Y = (int*)malloc(sizeof(int) * (unsigned int)M);
for (int i = 0; i < M; ++i)
{
X[i] = _readInt();
Y[i] = _readInt();
}

int *P = (int*)malloc(sizeof(int) * (unsigned int)M),


*Q = (int*)malloc(sizeof(int) * (unsigned int)M);

int ans = findSwapPairs(N, S, M, X, Y, P, Q);

fprintf(_outputFile, "%d\n", ans);

for (int i = 0; i < ans; ++i)


fprintf(_outputFile, "%d %d\n", P[i], Q[i]);

return 0;
}

// -------------- end grader ------------------


/*
execution time : 2.391 s
*/

Listing 5.5.5: sorting-225877.cpp


// https://oj.uz/submission/225877 442 ms 11492 KB
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "sorting.h"

#include<bits/stdc++.h>

using namespace std;

#define REP(i, n) for(int i = 0; i < n; i++)


CAPITOLUL 5. IOI 2015 5.5. SORTING 655

#include "sorting.h"

int findSwapPairs(int N, int S[], int M, int X[], int Y[], int P[], int Q[])
{
auto check = [&](int R)
{
vector<int> s(N), ws(N), wf(N);
REP(i, N) s[i] = S[i];
auto f = s;
REP(i, R) swap(f[X[i]], f[Y[i]]);
REP(i, N)
{
ws[S[i]] = i;
wf[f[i]] = i;
}

int j = 0;
REP(i, R)
{
int &p = s[X[i]], &q = s[Y[i]];
swap(p, q);
swap(ws[p], ws[q]);

while(j < N && j == f[j]) j++;


if(j < N)
{
int &a = ws[j], &b = ws[f[j]], &x = wf[j], &y = wf[f[j]];
P[i] = a, Q[i] = b;
swap(a, b), swap(s[a], s[b]);
swap(x, y), swap(f[x], f[y]);
}
else P[i] = Q[i] = 0;
}

REP(i, N) if(i != s[i]) return false;


return true;
};

int l = 0, r = M;
while(l < r)
{
int m = (l + r) / 2;
if(check(m))
r = m;
else
l = m + 1;
}

check(l);
return l;
}

// -------------- begin grader ------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}
CAPITOLUL 5. IOI 2015 5.5. SORTING 656

if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtaskf/25", "rb");
_outputFile = fopen("sorting.out", "w");

int N, M;
N = _readInt();
int *S = (int*)malloc(sizeof(int) * (unsigned int)N);

for (int i = 0; i < N; ++i)


S[i] = _readInt();

M = _readInt();

int *X = (int*)malloc(sizeof(int) * (unsigned int)M),


*Y = (int*)malloc(sizeof(int) * (unsigned int)M);
for (int i = 0; i < M; ++i)
{
X[i] = _readInt();
Y[i] = _readInt();
}

int *P = (int*)malloc(sizeof(int) * (unsigned int)M),


*Q = (int*)malloc(sizeof(int) * (unsigned int)M);

int ans = findSwapPairs(N, S, M, X, Y, P, Q);

fprintf(_outputFile, "%d\n", ans);

for (int i = 0; i < ans; ++i)


fprintf(_outputFile, "%d %d\n", P[i], Q[i]);

return 0;
}

// -------------- end grader ------------------


/*
execution time : 3.016 s
*/

Listing 5.5.6: sorting-233228.cpp


// https://oj.uz/submission/233228 242 ms 34048 KB
#include <stdlib.h>
#include <stdio.h>
CAPITOLUL 5. IOI 2015 5.5. SORTING 657

#include <string.h>

#include "sorting.h"

#include <bits/stdc++.h>

using namespace std;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef long long ll;


typedef long double ld;

#define INF 2001001001


#define MOD 1000000007

int N,S[200001];
int M,X[600001],Y[600001];
int arr[200001];
bool vis[200001];
int to[200001];

deque<pair<int,int>>q;

int pos[200001];

void dfs(int node)


{
vis[node]=true;
if (!vis[arr[node]]){
q.push_back({node,arr[node]});
dfs(arr[node]);
}
}

bool check(int mid)


{
q.clear();
for (int i=0;i<N;i++)
{
arr[i]=S[i];
vis[i]=false;
}
for (int i=1;i<=mid;i++)
swap(arr[X[i]],arr[Y[i]]);
int comps=0;
for (int i=0;i<N;i++)
{
pos[arr[i]]=i;
if (!vis[arr[i]])
{
comps++;
q.push_back({i,arr[i]});
dfs(arr[i]);
q.pop_back();
}
}
return N-comps<=mid;
}

int findSwapPairs(int n,int s[],int m, int x[], int y[], int P[], int Q[])
{
N=n;
for (int i=0;i<N;i++)
S[i]=s[i];
M=m;
for (int i=1;i<=M;i++)
{
X[i]=x[i-1];
Y[i]=y[i-1];
}
int low=-1,high=M+1;
while (low+1<high)
{
int mid=(low+high)/2;
if (check(mid))
CAPITOLUL 5. IOI 2015 5.5. SORTING 658

high=mid;
else
low=mid;
}

check(high);
for (int i=high;i>=1;i--)
{
if (!q.empty()){
P[i-1]=pos[q[0].first];
Q[i-1]=pos[q[0].second];
swap(pos[arr[X[i]]],pos[arr[Y[i]]]);
swap(arr[X[i]],arr[Y[i]]);
q.pop_front();
}
}

for (int i=1;i<=high;i++)


{
swap(S[X[i]],S[Y[i]]);
swap(S[P[i-1]],S[Q[i-1]]);
}
for (int i=0;i<N;i++)
if (S[i]!=i)
assert(0);
return high;
}

// -------------- begin grader ------------------

static char _buffer[1024];


static int _currentChar = 0;
static int _charsNumber = 0;

static FILE *_inputFile, *_outputFile;

static inline int _read()


{
if (_charsNumber < 0)
{
exit(1);
}

if (!_charsNumber || _currentChar == _charsNumber)


{
_charsNumber = (int)fread(_buffer,
sizeof(_buffer[0]),
sizeof(_buffer),
_inputFile);
_currentChar = 0;
}

if (_charsNumber <= 0)
{
return -1;
}
return _buffer[_currentChar++];
}

static inline int _readInt()


{
int c, x, s;
c = _read();
while (c <= 32) c = _read();
x = 0;
s = 1;
if (c == ’-’)
{
s = -1;
c = _read();
}
while (c > 32)
{
x *= 10;
x += c - ’0’;
c = _read();
CAPITOLUL 5. IOI 2015 5.6. TOWNS 659

}
if (s < 0) x = -x;
return x;
}

int main()
{
_inputFile = fopen("../tests/subtaskf/25", "rb");
_outputFile = fopen("sorting.out", "w");

int N, M;
N = _readInt();
int *S = (int*)malloc(sizeof(int) * (unsigned int)N);

for (int i = 0; i < N; ++i)


S[i] = _readInt();

M = _readInt();

int *X = (int*)malloc(sizeof(int) * (unsigned int)M),


*Y = (int*)malloc(sizeof(int) * (unsigned int)M);
for (int i = 0; i < M; ++i)
{
X[i] = _readInt();
Y[i] = _readInt();
}

int *P = (int*)malloc(sizeof(int) * (unsigned int)M),


*Q = (int*)malloc(sizeof(int) * (unsigned int)M);

int ans = findSwapPairs(N, S, M, X, Y, P, Q);

fprintf(_outputFile, "%d\n", ans);

for (int i = 0; i < ans; ++i)


fprintf(_outputFile, "%d %d\n", P[i], Q[i]);

return 0;
}

// -------------- end grader ------------------


/*
execution time : 2.341 s
*/

5.5.3 *Rezolvare detaliată

5.6 Towns
Problema 6 - Towns 100 de puncte

Author: Bang Ye Wu (TWN)

În Kazakhstan sunt N orăşele mici, numerotate de la 0 la N  1. De asemenea, există un


număr necunoscut de municipii (oraşe mari). Orăşelele şi municipiile din Kazakhstan sunt numite
ı̂n general localităţi.
Toate localităţile din Kazakhstan sunt conectate ı̂ntr-o singură reţea bidirecţională de au-
tostrăzi. Fiecare autostradă conectează două localităţi distincte şi fiecare pereche de localităţi
este conectată direct prin cel mult o autostradă. Pentru fiecare pereche de localităţi a şi b există
o singură cale prin care se poate ajunge da la a la folosind reţeaua de autostrăzi, astfel ı̂ncât nici
una din autostrăzi nu se va folosi mai mult decât o singură dată.
Se ştie că fiecare orăşel se conectează direct la o singură altă localitate şi fiecare municipiu este
conectat direct la trei sau mai multe localităţi.
Următoarea figură ilustrează o reţea, care conectează 11 orăşele şi 7 municipii. Orăşelele mici
sunt reprezentate prin cercuri şi marcate cu numere ı̂ntregi, municipiile sunt reprezentate prin
pătrate şi marcate cu litere.
CAPITOLUL 5. IOI 2015 5.6. TOWNS 660

Fiecare autostradă are o lungime pozitivă ı̂ntreagă. Distanţa dintre două localităţi este suma
minimă a lungimilor autostrăzilor necesare pentru a ajunge dintr-o localitate ı̂n cealaltă.
Pentru fiecare municipiu C putem măsura distanţa r C  până la cel mai depărtat de el orăşel.
Municipiul C este un hub dacă distanţa r C  este cea mai mică dintre toate municipiile.
Distanţa dintre hub şi cel mai depărtat de el orăşel este notată prin R. Astfel, R este cea mai
mică dintre toate valorile r C .
În exemplul de mai sus cel mai depărtat orăşel de municipiul a este cel cu numărul 8 şi distanţa
dintre ele este r a 1  4  12 17. Pentru municipiul g de asemena avem r g  17. (Unul
dintre orăşelele cele mai depărtate de g este cel cu numărul 6). Unicul hub din exemplu este
municipiul f cu r f  16. Astfel, ı̂n exemplul de mai sus R este 16.
Eliminarea unui hub ı̂mparte reţeaua ı̂n multiple componente conexe. Un hub este echilibrat
dacă fiecare dintre aceste componente conţine cel mult N ©2$ orăşele. (Subliniem că municipiile
nu se numără). Prin x$ se notează cel mai mare ı̂ntreg care nu depăşeşte x.
În exemplul nostru municipiul f este hub. Dacă f este eliminat, reţeaua se va ı̂mpărţi ı̂n patru
componente conexe. Aceste patru componente sunt formate din următoarele seturi de orăşele:
{0,1,10}, {2,3}, {4,5,6,7}, şi {8,9}. Niciuna dintre componente nu are mai mult de 11©2$ 5
orăşele, astfel municipiul f este un hub echilibrat.
Cerinţă
Iniţial, unica informaţie despre reţeaua localităţilor şi autostrăzilor de care dispunem este
numărul de orăşele. Numărul de municipii nu este cunoscut. De asemenea nu se ştie nimic despre
schema autostrăzilor din ţară. Putem obţine informaţii noi doar prin interogări privind distanţa
dintre perchi de orăşele.
Sarcina ta este să determini:
ˆ ı̂n toate subproblemele: distanţa .
ˆ ı̂n subproblemele de la 3 la 6: dacă există un hub echilibrat ı̂n reţea.

Urmează să implementezi funcţia hubDistance. Grader-ul va evalua teste multiple ı̂ntr-o
singură execuţie. Numărul de teste pentru o execuţie va fi cel mult 40. Pentru fiecare test grader-
ul va apela funcţia ta hubDistance exact o singură dată. Asigură-te că funcţia ta iniţializează
toate variabilele necesare de fiecare dată când este apelată.
ˆ hubDistance(N, sub)
– N : numărul de orăşele.
– sub: numărul subproblemei (explicat ı̂n secţiunea Subprobleme).
– Dacă sub este 1 sau 2, funcţia poate returna R sau R

– Dacă sub este mai mare decât 2 şi există un hub echilibrat, atunci funcţia va returna
R, ı̂n caz contrar va returna R.

Funcţia ta hubDistance poate obţine informaţia despre reţeaua de autostrăzi apelând funcţia
graderului getDistance(i, j). Această funcţie returnează distanţa dintre orăşelele i şi j. De
remarcat că, pentru i şi j egale, funcţia returnează 0. Ea de asemena va returna 0 ı̂n cazul ı̂n care
argumentele sunt nevalide.
Subprobleme
În fiecare test:
CAPITOLUL 5. IOI 2015 5.6. TOWNS 661

ˆ N este ı̂ntre 0 şi 110 inclusiv.


ˆ Distanţa ı̂ntre oricare două orăşele distincte este ı̂ntre 1 şi 1,000,000 inclusiv.

Numărul de interogări pe care le poate face programul tău este limitat. Limita variază ı̂n
funcţie de subproblemă, după cum este indicat ı̂n tabelul care urmează. Dacă programul tău
ı̂ncearcă să depăşească limita de interogări, el va fi terminat şi se va considera că ai obţinut un
răspuns incorect.

subproblemă puncte număr de găseşte hub precizări


interogări echilibrat
N N 1
1 13 2
NO none
7N
2 12 , 2 2 NO none
N N 1
3 13 2
YES none
7N
4 10 , 2 2 YES fiecare municipiu este conectat la
exact trei localităţi
5N
5 13 2
YES none
7N
6 39 , 2 2 YES none

Amintim că, prin *x0 se notează cel mai mic ı̂ntreg care este mai mare sau egal cu x.
Grader-ul de pe calculatorul tău
Atenţionăm că, numărul subproblemei este o parte din input. Grader-ul de pe calculatorul tău
ı̂şi schimbă comportamentul ı̂n concordanţă cu numărul subproblemei.
Grader-ul de pe calculatorul tău citeşte input-ul din fişierul towns.in ı̂n următorul format:
ˆ linia 1: Numărul subproblemei şi numărul de teste.
ˆ linia 2: N1 , numărul de orăşele ı̂n primul test.
ˆ următoarele N1 linii: Al j-lea număr (1 & j & N1 ) ı̂n a i-a din aceste linii 1 & i & N1 este
distanţa dintre orăşelele i  1 şi j  1.
ˆ Urmează celelalte teste. Ele sunt descrise ı̂n acelaşi format ca şi primul test.

Pentru fiecare test grader-ul afişează valoarea returnată de hubDistance şi numărul de
apeluri efectuate, ı̂n linii separate.
Fişierul input care corespunde exemplului descris anterior este:

1 1
11
0 17 18 20 17 12 20 16 23 20 11
17 0 23 25 22 17 25 21 28 25 16
18 23 0 12 21 16 24 20 27 24 17
20 25 12 0 23 18 26 22 29 26 19
17 22 21 23 0 9 21 17 26 23 16
12 17 16 18 9 0 16 12 21 18 11
20 25 24 26 21 16 0 10 29 26 19
16 21 20 22 17 12 10 0 25 22 15
23 28 27 29 26 21 29 25 0 21 22
20 25 24 26 23 18 26 22 21 0 19
11 16 17 19 16 11 19 15 22 19 0

Acest format este diferit de specificarea listei de autostrăzi. De notat că este permis să modifici
grader-ele de pe calculatorul tău, astfel ı̂ncât ele să folosească un format diferit pentru input.
Timp maxim de executare/test: 1.0 secunde
Memorie: total 1500 MB
CAPITOLUL 5. IOI 2015 5.6. TOWNS 662

5.6.1 Indicaţii de rezolvare

The graph given in this task is an edge-weighted tree T ), in which the degree of each internal node
is at least three.
Let n denote the number of leaves of T ).
The goal is to find the centers of the tree and determine if any center is also a leaf median (an
internal node such that after removing the node, every component contains at most n/2 leaves.
In this task, the tree is not given, and the contestants must solve the task by using limited
number of queries for distances between leaves.
An algorithm using 7n©2 queries is sketched as follows. The details are in the next sections.
* First, find the centers by at most 2n  3 queries; and then
* for each center (at most two centers), determine if it is also a leaf median by using no more
than 3n©2 queries.
Radius and centers
The diameter of a tree can be found with 2n  3 queries as follows.
* Farthest to-Farthest: Pick an arbitrary vertex v and find a vertex s farthest to r. Find a
vertex t farthest to s. It can be shown that d s, t is a diameter of the tree.
* Any center must be one the vs-path.
* Then the vertices on the vs-path with its distance to s closest to d s, t©2 are centers.
Determining if a center is also a median
Let v, s be the two leaves in the above process and m be a center on the vs-path with
d s, m r. We need to determine if each component of T  m has at most n©2.
Let S be the set of all leaves.
First we compute the multiset

B r d u, s  d s, v   d u, v ©2¶¾u " S x.
Each of the different values in B identifies a unique internal vertex on the sv-path.
0 0
Let m be the internal vertex on the sv-path with d s, m  α, where α is the median of the
multiset B.
0
If we root T at the sv-path, the leaf median must be in the subtree rooted at m . Therefore if
0
r α (i.e., m m ), then m is not a median. Note that there are two medians in B if ¶B ¶ is even.
Otherwise it remains to solve the ”giant component” problem: checking if there is a component
in T  m with more than n©2 leaves.
Let
X ru " S ¶d u, s  d s, v   d u, v  2rx
which is the set of the leaves branching from sv-path at m.
For x1 , x2 " X, we have that x1 , x2 are in the same component of T  m iff

d s, x1  d s, x2  d x1, x2 % 2r.

Then, we can solve the giant component problem by algorithms for the following problem:
There are n color balls and we want to determine if there are more than n©2 balls of the same
color.
The cost is counted by the number of performed queries R u, v  which returns Equal/NotEqual
according to if u and v are of the same color.
It was shown that *3n©20  2 queries are necessary and sufficient in Fischer and Salzberg
(1982), Solution to problem 81-5, J. Algorithms, pp. 376-379.
(https://www.researchgate.net/publication/
242568474_Finding_a_majority_among_n_votes_Solution_to_problem_81-5)
The following is another similar approach.
Initially each element is itself a set. At each iteration, we arbitrarily pair up the survival
sets and compare their representatives. If they are equal, then join the two sets into their union.
Otherwise, mark both dead.
In the case that the number of alive sets is odd, mark anyone dead. Repeating this process,
eventually either there is exactly one survival set or all sets are dead.
It can be shown that if there is a majority originally, then it must be the survival one.
CAPITOLUL 5. IOI 2015 5.6. TOWNS 663

So the remaining work is to compare the survival representative with the representatives of all
DEAD sets.
The number of comparisons: Let Ai denote the number of alive sets in the i-th iteration
(A0 n). The number of comparisons to obtain the only survival set is

1©2 A0  A1  A2 ....

In the second stage the number of comparisons is the number of dead sets.
Let Di denote the number of sets die at iteration i. Then, we have D1 A0  2A1 .
Similarly Di Ai1  2Ai .
Therefore, the total number of dead sets is

A0  2A1   A1  2A2   ... A0  A1  A2  ....

In summary, the number of comparisons is

3©2A0  1©2 A1  A2  ... $ 3n©2.

5.6.2 Coduri sursă

Listing 5.6.1: graderlib.c


1 #include <stdio.h>
2 #include <stdlib.h>
3
4 static int _N;
5 static int _dist[110][110];
6 static int _quota, _user_query;
7
8 void _ini_query(int N, int k)
9 {
10 int ret;
11 _N = N;
12 for (int i = 0; i < _N; i++)
13 for (int j = 0; j < _N; j++)
14 {
15 ret = scanf("%d", &_dist[i][j]);
16 assert(ret == 1);
17 }
18
19 if (k == 1 || k == 3) _quota = _N * (_N - 1) / 2;
20 else
21 if (k == 2 || k == 4 || k == 6) _quota = (7 * _N + 1) / 2;
22 else _quota = 5 * _N;
23 _user_query = 0;
24 }
25
26 int getDistance(int i, int j)
27 {
28 _user_query++;
29 if (_user_query > _quota)
30 {
31 printf("0\n");
32 exit(0);
33 }
34 if (i < 0 || i >= _N) return 0;
35 if (j < 0 || j >= _N) return 0;
36 return _dist[i][j];
37 }

Listing 5.6.2: towns-127541.cpp


// https://oj.uz/submission/127541 21 ms 1156 KB

#include "towns.h"
#include <assert.h>
CAPITOLUL 5. IOI 2015 5.6. TOWNS 664

#include <stdio.h>
#include <stdlib.h>
#include "graderlib.c"

#include <bits/stdc++.h>

using namespace std;

const int maxn = 150;


const int inf = 0x3f3f3f3f;

int qs[maxn][maxn];
int deb = 0;

int n;
int query(int a, int b)
{
assert(a < n && b < n && a >= 0 && b >= 0);
if (a > b) swap(a, b);
if (qs[a][b] == -1) qs[a][b] = getDistance(a, b);
return qs[a][b];
}

int a, b;
int B;
bool subtree(int x, int y)
{
int xd = (query(a, x) + query(b, x) - query(a, b)) / 2;
int yd = (query(a, y) + query(b, y) - query(a, b)) / 2;
return (xd + yd) > query(x, y);
}

int hubDistance(int n, int sub)


{
deb++;
::n = n;
memset(qs, -1, sizeof qs);
a = b = B = 0;

for (int i = 0; i < n; i++)


qs[i][i] = 0;

a = 0, b = 0;
for (int i = 0; i < n; i++)
if (query(0, a) < query(0, i)) a = i;

for (int i = 0; i < n; i++)


if (query(a, b) < query(a, i)) b = i;

B = b; b = 0;
int hu = -1, out = inf;
for (int i = 0; i < n; i++)
{
int cd = (query(a, i) + query(b, i) - query(a, b)) / 2;
int ad = query(a, i) - cd;
int bd = query(a, B) - ad;

if (out > max(ad, bd)) out = max(ad, bd), hu = ad;


}
assert(hu > -1);

int lef = 0, rig = 0;


vector<int> v;
for (int i = 0; i < n; i++)
{
int cd = (query(a, i) + query(b, i) - query(a, b)) / 2;
int ad = query(a, i) - cd;

if (ad < hu) lef++;


else
if (ad > hu) rig++;
else v.push_back(i);
}

if (max(lef, rig) > n / 2)


{
CAPITOLUL 5. IOI 2015 5.6. TOWNS 665

hu = query(a, B) - hu;

int lef = 0, rig = 0;


vector<int> v;
for (int i = 0; i < n; i++)
{
int cd = (query(a, i) + query(b, i) - query(a, b)) / 2;
int ad = query(a, i) - cd;

if (ad < hu) lef++;


else
if (ad > hu) rig++;
else v.push_back(i);
}

if (max(lef, rig) > n / 2) return -out;


}

if (v.size() == 0)
{
if (max(lef, rig) > n / 2) return -out;
else return out;
}

vector<int> li, buk;


li.push_back(v[0]);
for (int i = 1; i < v.size(); i++)
{
if (subtree(v[i], li.back())) buk.push_back(v[i]);
else
{
li.push_back(v[i]);
if (!buk.empty())
{
li.push_back(buk.back());
buk.pop_back();
}
}
}

int cnt = 0;
int kan = li.back();

while (li.size() > 0)


{
if (subtree(li.back(), kan))
{
if (li.size() == 1)
{
buk.push_back(li.back());
li.pop_back();
}
else
{
li.pop_back();
li.pop_back();
cnt++;
}
}
else
{
if (buk.empty()) return out;
else
{
li.pop_back();
buk.pop_back();
cnt++;
}
}
}

cnt += buk.size();

if (cnt > n / 2) return -out;


else return out;
}
CAPITOLUL 5. IOI 2015 5.6. TOWNS 666

// ------------- begin grader ----------------------

int main()
{
FILE *f;
f = freopen("../tests/subtask_6/03","r",stdin);
assert(f != NULL);

f = freopen("towns.out","w",stdout);
assert(f != NULL);

int ncase, R, N;
int subtask;
int ret;

ret = scanf("%d%d",&subtask,&ncase);
assert(ret == 2);

for (int i = 0; i < ncase; i++)


{
ret = scanf("%d",&N);
assert(ret == 1);

_ini_query(N,subtask);
R=hubDistance(N,subtask);

printf("%d\n",R);
}

return 0;
}

// ------------- end grader ------------------------


/*
execution time : 0.328 s
*/

Listing 5.6.3: towns-134867.cpp


// https://oj.uz/submission/134867 22 ms 504 KB
#include "towns.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "graderlib.c"

#include <bits/stdc++.h>

#define ff first
#define ss second

using namespace std;

int d[2][115], v;
map<int, int> cntr;

int hubDistance(int N, int sub)


{
memset(d, 0, sizeof d);
cntr.clear();
for(int i = 0; i < N; i++)
if(d[0][v] < (d[0][i] = getDistance(0, i)))
v = i;

int T = d[0][v], D = 0;
for(int i = 0; i < N; i++)
D = max(D, d[1][i] = getDistance(v, i));
int r = 1000000000;

for(int i = 0; i < N; i++)


{
int s = (T + d[1][i] - d[0][i]) >> 1;
cntr[s]++;
r = min(r, max(s, D - s));
CAPITOLUL 5. IOI 2015 5.6. TOWNS 667

int chk = -1, l = 0;

for(auto& x : cntr)
{
if(l <= N / 2 && N - (l += x.ss) <= N / 2)
{
if(max(x.ff, D - x.ff) == r)
{
if(x.ss <= N / 2) return r;
chk = x.ff << 1;
}
}
}

if(chk == -1) return -r;


vector<vector<int> > s, t, de;
vector<int> la;
for(int i = 0; i < N; i++)
if(chk == T + d[1][i] - d[0][i])
s.push_back({i});

while(s.size() > 1)
{
for(int i = 1; i < s.size(); i += 2)
{
if(getDistance(s[i - 1][0],
s[i][0]) == d[0][s[i - 1][0]] + d[1][s[i][0]] - T)
{
de.push_back(s[i - 1]);
de.push_back(s[i]);
}
else
{
for(int& x : s[i - 1])
s[i].push_back(x);
t.push_back(s[i]);
}
}

if(s.size() & 1)
{
de.push_back(s.back());
la = s.back();
}
swap(s, t);
t.clear();
}

int sz = 0;
if(s.empty()) s.push_back(la);
else sz = s[0].size();

if(s[0].empty()) return r;
for(auto& x : de)
{
if(getDistance(x[0], s[0][0]) != d[0][s[0][0]] + d[1][x[0]] - T)
sz += x.size();
}

if(sz > N / 2) return -r;


return r;
}

// ------------- begin grader ----------------------

int main()
{
FILE *f;
f = freopen("../tests/subtask_6/03","r",stdin);
assert(f != NULL);

f = freopen("towns.out","w",stdout);
assert(f != NULL);
CAPITOLUL 5. IOI 2015 5.6. TOWNS 668

int ncase, R, N;
int subtask;
int ret;

ret = scanf("%d%d",&subtask,&ncase);
assert(ret == 2);

for (int i = 0; i < ncase; i++)


{
ret = scanf("%d",&N);
assert(ret == 1);

_ini_query(N,subtask);
R=hubDistance(N,subtask);

printf("%d\n",R);
}

return 0;
}

// ------------- end grader ------------------------


/*
execution time : 0.375 s
*/

Listing 5.6.4: towns-151462.cpp


// https://oj.uz/submission/151462 23 ms 1016 KB
#include "towns.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "graderlib.c"

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int nmax=205;

int n;
int d[2][nmax];
int to[nmax],wh[nmax],sz[nmax];
vector<int> domin[nmax];

int eq(int x,int y)


{
return (to[x]+to[y]!=getDistance(x,y));
}

bool checkHub(int x)
{
vector<int> cand;
for(int i=0;i<n;i++)
{
domin[i].clear();
sz[i]=0;
}
for(int i=0;i<n;i++)
if(wh[i]==x)
cand.push_back(i);
int act=0,bal=0;
for(auto it: cand)
{
if(bal==0)
{
act=it;
bal++;
}
else
{
if(eq(act,it)) bal++;
CAPITOLUL 5. IOI 2015 5.6. TOWNS 669

else
{
domin[act].push_back(it);
bal--;
}
}
sz[act]++;
}
int ap=0;
for(auto it:cand)
{
if(sz[it])
{
if(eq(act,it)) ap+=sz[it]-(int)domin[it].size();
else
{
for(auto oth: domin[it])
{
ap+=eq(act,oth);
}
}
}
}
return (ap<=n/2);
}

int hubDistance(int N, int sub)


{
int d1=0,R=0,mx=0,d2=0;
n=N;
for(int i=0;i<2;i++)
for(int j=0;j<n;j++)
d[i][j]=0;
for(int i=1; i<n; i++)
{
d[0][i]=getDistance(0,i);
if(d[0][i]>d[0][d1])
d1=i;
}

for(int i=0; i<n; i++)


if(i!=d1)
{
d[1][i]=getDistance(d1,i);
if(d[1][i]>mx)
mx=d[1][i],d2=i;
}

int minDist=(1<<30);
int lim=d[1][d2]-(d[0][d2]+d[1][d2]-d[0][d1])/2;
for(int i=0; i<n; i++)
{
to[i]=(d[0][i]+d[1][i]-d[0][d1])/2;
wh[i]=(d[1][i]-to[i]);
if(max(wh[i],mx-wh[i])<minDist&&wh[i]<=lim)
minDist=max(wh[i],mx-wh[i]);
}

vector<int> hubs;
for(int i=0;i<n;i++)
if(max(wh[i],mx-wh[i])==minDist&&wh[i]<=lim)
hubs.push_back(wh[i]);

sort(hubs.begin(),hubs.end());

hubs.resize(unique(hubs.begin(),hubs.end())-hubs.begin());

bool ok=0;
for(auto cen :hubs)
{
int mici=0,mari=0;
for(int i=0;i<n;i++)
{
if(wh[i]<cen) mici++;
if(wh[i]>cen) mari++;
}
CAPITOLUL 5. IOI 2015 5.6. TOWNS 670

if(mici<=n/2&&mari<=n/2)
ok=checkHub(cen);
}

R=minDist;
if(!ok) R=-R;
return R;
}

// ------------- begin grader ----------------------

int main()
{
FILE *f;
f = freopen("../tests/subtask_6/03","r",stdin);
assert(f != NULL);

f = freopen("towns.out","w",stdout);
assert(f != NULL);

int ncase, R, N;
int subtask;
int ret;

ret = scanf("%d%d",&subtask,&ncase);
assert(ret == 2);

for (int i = 0; i < ncase; i++)


{
ret = scanf("%d",&N);
assert(ret == 1);

_ini_query(N,subtask);
R=hubDistance(N,subtask);

printf("%d\n",R);
}

return 0;
}

// ------------- end grader ------------------------


/*
execution time : 0.344 s
*/

Listing 5.6.5: checkerTowns.cpp


#include "testlib.h"

#include<iostream>

using namespace std;

const string output_secret = "098d134608c94f7413faac591054ee35";

int main()
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask_6/03", // argv[1]=<input-file>
(char*)"towns.out", // argv[2]=<output-file>
(char*)"../tests/subtask_6/03.a", // argv[3]=<answer-file>
};
cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";

registerChecker("towns", argc, argv);

compareRemainingLines(1); // incepand cu linia nr 1


CAPITOLUL 5. IOI 2015 5.6. TOWNS 671

5.6.3 *Rezolvare detaliată


Capitolul 6
36
IOI 2014

6.1 Game
Problema 1 - Game 100 de puncte

Authors: Jonathan Mosheiff and Nir Lavee, Israel

Jian-Jia este un tânăr căruia ı̂i plac jocurile. Când i se pune o ı̂ntrebare, el preferă să joace
jocuri ı̂n loc să răspundă direct. Jian-Jia a ı̂ntâlnit-o pe prietena sa Mei-Yu şi i-a povestit despre
reţeaua aeriană din Taiwan. Există n oraşe ı̂n Taiwan (numerotate 0, ..., n  1), dintre care unele
sunt conectate prin zboruri directe. Fiecare zbor conectează două oraşe şi poate fi folosit ı̂n ambele
direcţii.
Mei-Yu l-a ı̂ntrebat pe Jian-Jia dacă este posibil să călătorească cu avionul ı̂ntre oricare două
oraşe (fie direct, fie indirect). Jian-Jia nu a vrut să răspundă, dar ı̂n schimb, a sugerat să joace
un joc. Mei- Yu poate să ı̂i pună ı̂ntrebări de forma: ”Există un zbor direct ı̂ntre oraşele x şi y?”,
şi Jian-Jia va răspunde acestor ı̂ntrebări imediat. Mei-Yu va ı̂ntreba despre fiecare pereche de
oraşe o singură dată, punând r n n  1©2 ı̂ntrebări ı̂n total. Mei-Yu câştigă jocul dacă, după
ce obţine răspunsurile la primele i ı̂ntrebări cu i $ r, poate afirma cu certitudine dacă reţeaua
de zbor este conexă, adică dacă este posibil zborul (direct sau indirect) ı̂ntre oricare două oraşe.
Altfel, adică dacă are nevoie de toate cele r ı̂ntrebări, ı̂nvingător este considerat Jian-Jia.
Pentru a face jocul mai amuzant pentru Jian-Jia, cei doi au convenit că el poate răspunde după
cum doreşte, fără a ţine cont de reţeaua aeriană reală din Taiwan, inventând reţeaua pe măsură
ce jocul progresează, alegându-şi răspunsurile ı̂n funcţie de ı̂ntrebările puse anterior de Mei-Yu.
Sarcina ta este să-l ajuţi pe Jian-Jia să câştige jocul, decizând, care ar trebui să fie răspunsurile
lui.
Exemple
Vom explica regulile jocului prin următoarele trei exemple. Fiecare exemplu are n 4 oraşe
şi r 6 runde de ı̂ntrebări şi răspunsuri.
În primul exemplu (din următorul tabel), Jian-Jia pierde deoarece după runda 4, Mei-Yu ştie
cu certitudine că se poate zbura ı̂ntre oricare două oraşe, indiferent de răspunsurile lui Jian-Jia la
ı̂ntrebările 5 sau 6.
rundă ı̂ntrebare răspuns
1 0, 1 yes
2 3, 0 yes
3 1, 2 no
4 0, 2 yes
... ... ...
5 3, 1 no
6 2, 3 no
36
aur: Rareş Darius Buhai, Liviu Rebreanu (Bistriţa)
. argint: Alexandru Velea, Tiberiu Popoviciu (Cluj)
. argint: Andrei Heidelbacher, C.D. Loga (Timişoara)
. bronz: Mihai-Dan Gheorghe, ICHB (Bucureşti).

672
CAPITOLUL 6. IOI 2014 6.1. GAME 673

În următorul exemplu Mei-Yu poate demonstra după runda a treia, că indiferent cum va
răspunde Jian-Jia la ı̂ntrebările 4, 5, sau 6, nu se poate călători ı̂ntre oraşele 0 şi 1 prin zboruri,
deci Jian-Jia pierde din nou.

rundă ı̂ntrebare răspuns


1 0, 3 no
2 2, 0 no
3 0, 1 no
... ... ...
4 1, 2 yes
5 1, 3 yes
6 2, 3 yes

În exemplul final Mei-Yu nu poate determina dacă se poate călători ı̂ntre oricare două oraşe
prin zboruri, până când Jian-Jia nu va răspunde la toate cele şase ı̂ntrebări, astfel Jian-Jia câştigă
jocul.
Mai exact, deoarece Jian-Jia a răspuns yes la ultima ı̂ntrebare (din următorul tabel), este
posibilă călătoria ı̂ntre oricare două oraşe. Totuşi, dacă Jian-Jia ar fi răspuns no la ultima ı̂ntrebare
acest lucru nu ar fi fost posibil.

rundă ı̂ntrebare răspuns


1 0, 3 no
2 1, 0 yes
3 0, 2 no
4 3, 1 yes
5 1, 2 no
6 2, 3 yes

Cerinţă
Scrieţi un program care ı̂l ajută pe Jian-Jia să câştige jocul. Se ştie că nici Mei-Yu nici Jian-Jia
nu cunosc unul strategia celuilalt. Mei-Yu poate ı̂ntreba despre perechile de oraşe ı̂n orice ordine,
iar Jian- Jia trebuie să răspundă la ele imediat, fără a cunoaşte următoarele ı̂ntrebări. Voi trebuie
să implementaţi următoarele două funcţii.
ˆ initialize(n) – Noi vă vom apela funcţia initialize o singură dată, la ı̂nceput.
Parametrul n reprezintă numărul de oraşe.
ˆ hasEdge(u, v) – Apoi vă vom apela funcţia hasEdge de ori. Aceste apeluri reprezintă
ı̂ntrebările domnişoarei Mei-Yu ı̂n ordinea ı̂n care acestea sunt puse. Voi trebuie să răspundeţi
dacă există sau nu un zbor direct ı̂ntre oraşele şi . Mai exact, valoarea returnată trebuie să
fie 1 dacă există un zbor direct şi 0 ı̂n caz contrar.

Subprobleme
Fiecare subproblemă (subtask) constă din mai multe jocuri. Veţi primi puncte pentru o sub-
problemă dacă programul vostru câştigă toate jocurile pentru Jian-Jia.

subproblemă puncte n
1 15 n 4
2 27 4 & n & 80
3 58 4 & n & 1500

Detalii de implementare
Trebuie să ı̂ncărcaţi exact un fişier, numit game.c, game.cpp sau game.pas. ı̂n acest fişier vor
fi implementate cele două funcţii descrise mai sus, utilizând următoarele antete.
pentru programele C/C++

void initialize(int n);


int hasEdge(int u, int v);

pentru programele Pascal


CAPITOLUL 6. IOI 2014 6.1. GAME 674

procedure initialize(n: longint);


function hasEdge(u, v: longint): longint;

Grader-ul de pe computerul vostru


Grader-ul de pe computerul vostru citeşte datele de intrare ı̂n următorul format:
ˆ linia 1: n
ˆ următoarele linii: fiecare linie conţine două numere ı̂ntregi u şi v care descriu ı̂ntrebarea
referitoare la oraşele u şi v.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 256 MB

6.1.1 Indicaţii de rezolvare

Let Eyes be the set of edges about which the contestant has answered ”yes” (connected), Eno the
set of edges about which contestant has answered ”no”, and Emaybe the rest of the edges, whose
statuses are not yet determined.
Also, let G V, Eyes  and H V, Eyes < Emaybe . G is the graph you get by assuming that
every edge in Emaybe are not connected, while H is the graph you get by assuming that all edges
in Emaybe are connected.
Initially, G is empty and thus not connected, while H is connected. In order not to reveal any
clue to the judge, the contestant should maintain the invariant: G should always be disconnected,
while H should always be connected.
There are several possible ways to maintain the invariant.
4
An O n  solution
When asked by the judge whether an edge e u, v  is connected, answer ”no” if and only if
e is part of a cycle in H. One can see that this does not change the connectivity of G and H.
To decide whether e forms a circle, one can perform a depth-first search to find out whether
2
there is a path from u to v in V, Eyes Emaybe  u, v . This is an O n  operation. As there are
2 4
O n  edges, the total running time is O n .
In other words, we answer ”yes” if and only if e is a bridge in H.
2
An O n  solution
Given a vertex v, let D v  be the connected component v belongs to in G. We maintain two
data structures:

1. R is a table mapping each v to a representative of D v).


2. S is a symmetric matrix indexed by V . For u and v in V , if R u jR v , S R u, R v 
is the number of edges, in Emaybe , that connects D u and D v .

The contestant answers ”yes” to query u, v  if and only if S R u, R v  1.


R can be implemented as a disjoint-set linked list. Each disjoint set is represented by a linked
list of its elements, and the representative is the one at the head.
Each element has a pointer to its representative. To unite two sets we connect the lists, and
update the pointers.
An union takes O n time and a find takes O 1 time.
As for S, initially S u, v  1 unless u v. Whenever the judge asks about u, v , S is updated
as follows.

1. If the contestant answers ”no”, we decrement S R u, R v  by 1.


2. If the contestant answers ”yes”, let w be the representative after uniting D u and D v .
For each x that is a representative of some connected component, both S w, x and S x, w
are updated to S R u, x  S R v , x.
CAPITOLUL 6. IOI 2014 6.1. GAME 675

2
There can be at most n  1 unions, thus the total time spent on union is O n .
An update of S requires O 1 time for a ”no” response, and O n time for a ”yes” response.
Since the graph G is a tree, we respond ”yes” exactly n  1 times. Thus the time spent on updating
2 2
S is also O n . We thus have an O n  algorithm.
An One-Liner O(n2) Algorithm
2
There is a surprising one-line O n  algorithm:

#include "game.h"

void initialize(int n)
{
// DO NOTHING!
}

int c[1500];

int hasEdge(int u, int v)


{
return ++c[u > v ? u : v] == (u > v ? u : v);
}

To understand the algorithm, imagine that we partition the set of all the possible edges into
E1 , E2 , ..., En1 , with Ei i, j ¶i % j. Each Ei has exactly i possible edges. The algorithm above
answers ”yes” to u, v  (where u % v) if it is the last edge in Eu that is queried.
To see how it works, consider the last query. Denote the queried edge by e, and the graph
G V, Eyes  e. The contestant wins if G is disconnected, while G  e is connected.

ˆ G is disconnected, since it contains only n  2 edges.

ˆ G  e is connected, since it contains n  1 edges, and there is no cycle in G  e. One can see
that there is no cycle since, in each Ei , we answer yes to only one edge. Formally, if there
is a cycle C in G  e, considering the node u in C with largest id, Eu must has exactly one
edge in G  e. But u has two neighbors in C with smaller ids, a contradiction.

6.1.2 Coduri sursă

Listing 6.1.1: game-92195.cpp


// https://oj.uz/submission/92195 322 ms 16532 KB ...

#include <bits/stdc++.h>

#include "game.h"

int ctr[2000];
int n;

void initialize(int n)
{
::n=n;
}

int hasEdge(int u, int v)


{
if(v<u)
{
u+=v;
v=u-v;
u-=v;
}
ctr[v]++;
CAPITOLUL 6. IOI 2014 6.1. GAME 676

return (ctr[v]==v);
}

// -------------- begin grader ---------------

int read_int()
{
int x;
assert(scanf("%d", &x) == 1);
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/03-114.in", "r", stdin) ;


std::freopen("game.out", "w", stdout) ;

int n, u, v;
n = read_int();

auto t2 = clock();

initialize(n);
for (int i = 0; i < n * (n - 1) / 2; i++)
{
u = read_int();
v = read_int();
printf("%d\n", hasEdge(u, v));
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- end grader ---------------


/*
t2-t1 = 0
t3-t2 = 4.312
t4-t3 = 0.016

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


Press any key to continue.
argc = 4
checker
../tests/03-114.in
game.out
../tests/03-114.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 6.1.2: game-117330.cpp


// https://oj.uz/submission/117330 329 ms 16376 KB

#include "game.h"
#include <bits/stdc++.h>
CAPITOLUL 6. IOI 2014 6.1. GAME 677

using namespace std;

int cnt[1500];
int first_time;

void initialize(int n) {}
int c[1500];
int hasEdge(int u, int v) {
return ++c[u > v ? u : v] == (u > v ? u : v);
}

// -------------- begin grader ---------------

int read_int()
{
int x;
assert(scanf("%d", &x) == 1);
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/03-114.in", "r", stdin) ;


std::freopen("game.out", "w", stdout) ;

int n, u, v;
n = read_int();

auto t2 = clock();

initialize(n);
for (int i = 0; i < n * (n - 1) / 2; i++)
{
u = read_int();
v = read_int();
printf("%d\n", hasEdge(u, v));
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- end grader ---------------


/*
t2-t1 = 0
t3-t2 = 3.765
t4-t3 = 0.016

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


Press any key to continue.
argc = 4
checker
../tests/03-114.in
game.out
../tests/03-114.out
----------------------
1
Correct

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


Press any key to continue.
CAPITOLUL 6. IOI 2014 6.1. GAME 678

*/

Listing 6.1.3: game-231330.cpp


// https://oj.uz/submission/231330 372 ms 7160 KB

#include <cstdio>
#include <cassert>
#include "game.h"

#include<ctime>
#include<iostream>

int d[1500];

void initialize(int){}

int hasEdge(int u,int v)


{
return ++d[u=u>v?u:v]==u;
}

// -------------- begin grader ---------------

int read_int()
{
int x;
assert(scanf("%d", &x) == 1);
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/03-114.in", "r", stdin) ;


std::freopen("game.out", "w", stdout) ;

int n, u, v;
n = read_int();

auto t2 = clock();

initialize(n);
for (int i = 0; i < n * (n - 1) / 2; i++)
{
u = read_int();
v = read_int();
printf("%d\n", hasEdge(u, v));
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- end grader ---------------


/*
t2-t1 = 0.015
t3-t2 = 3.799
t4-t3 = 0.016

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


Press any key to continue.
CAPITOLUL 6. IOI 2014 6.1. GAME 679

argc = 4
checker
../tests/03-114.in
game.out
../tests/03-114.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 6.1.4: game nˆ2.cpp


// https://oj.uz/submission/231330 372 ms 7160 KB

#include <cstdio>
#include <cassert>
#include "game.h"

#include<ctime>
#include<iostream>

int c[1500];

void initialize(int){}

int hasEdge(int u,int v)


{
return ++c[u > v ? u : v] == (u > v ? u : v);
}

// -------------- begin grader ---------------

int read_int()
{
int x;
assert(scanf("%d", &x) == 1);
return x;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/03-114.in", "r", stdin) ;


std::freopen("game.out", "w", stdout) ;

int n, u, v;
n = read_int();

auto t2 = clock();

initialize(n);
for (int i = 0; i < n * (n - 1) / 2; i++)
{
u = read_int();
v = read_int();
printf("%d\n", hasEdge(u, v));
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 6. IOI 2014 6.2. RAIL 680

return 0;
}

// -------------- end grader ---------------


/*
t2-t1 = 0
t3-t2 = 3.791
t4-t3 = 0.015

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


Press any key to continue.
argc = 4
checker
../tests/03-114.in
game.out
../tests/03-114.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 6.1.5: checkerGame.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/03-114.in",
(char*)"game.out",
(char*)"../tests/03-114.out",
};

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

registerChecker("game", argc, argv);


compareRemainingLines();
}

6.1.3 *Rezolvare detaliată

6.2 Rail
Problema 2 - Rail 100 de puncte

Author: Vytautas Gruslys, Lithuania

În Taiwan există o cale ferată lungă care leagă ţărmul vestic al insulei de cel estic. Calea ferată
este compusă din m blocuri. Blocurile sunt numerotate consecutiv cu numerele 0, ..., m  1 de la
vest la est. Fiecare bloc este compus dintr-o linie cu sens unic spre vest situată ı̂n nordul blocului,
o linie cu sens unic spre est situată ı̂n sudul blocului, şi opţional o gară ı̂ntre cele două linii.
Există trei tipuri de blocuri. Un bloc de tip C are o gară ı̂n care trenurile intră de pe linia
nordică şi din care ies pe linia sudică, un bloc de tip D are o gară ı̂n care trenurile intră de pe linia
sudică şi din care ies pe linia nordică, iar un bloc de tip gol nu are gară. De exemplu, ı̂n figura
CAPITOLUL 6. IOI 2014 6.2. RAIL 681

de mai jos blocurile 0, 4 şi 6 sunt de tip gol, blocurile 1, 2 şi 3 sunt de tip C, iar blocul 5 este
de tip D. Blocurile sunt conectate ı̂ntre ele pe orizontală. Liniile de cale ferată ale două blocuri
consecutive sunt legate prin ite conectori, ilustraţi prin dreptunghiuri gri ı̂n figura următoare.

Pe calea ferată există gări numerotate de la 0 la n  1. Se cunoaşte că, folosind calea ferată,
putem ajunge din orice gară ı̂n oricare altă gară. De exemplu putem ajunge din gara 0 ı̂n gara
2 plecând din blocul 2, apoi trecând prin blocurile 3 şi 4 pe linia sudică, apoi trecând prin gara
1 din blocul 5, apoi trecând prin blocul 4 pe linia nordică şi ı̂n final ajungând ı̂n gara 2 aflată ı̂n
blocul 3.
Deoarece pot exista mai multe rute posibile, distanţa de la o gară la alta se defineşte ca fiind
numărul minim de conectori prin care trece un traseu valid. De exemplu, distanţa minimă de la
gara 0 la gara 2 este prin blocurile 2-3-4-5-4-3 şi trece prin 5 conectori, deci distanţa este 5.
Un sistem computerizat monitorizează calea ferată. Din nefericire, după o pană de curent
sistemul nu mai cunoaşte unde se află gările şi ı̂n ce tip de bloc se află acestea. Singurul indiciu
rămas ı̂n sistem este numărul blocului ı̂n care se află gara 0, care se află mereu ı̂ntr-un bloc de tip
C. Din fericire, sistemul poate ı̂ntreba care este distanţa de la orice gară la oricare altă gară. De
exemplu, sistemul poate pune următoarea ı̂ntrebare: ’Care este distanţa de la gara 0 la gara 2?’,
primind ca răspuns 5.
Cerinţă
Trebuie să implementaţi funcţia findLocation care determină pentru fiecare gară numărul
şi tipul blocului ı̂n care se află.
ˆ findLocation(n, first, location, stype)
ˆ n: numărul de staţii.
ˆ first: numărul blocului care conţine gara 0.
ˆ location: un tablou unidimensional de dimensiune n; la finalul execuţiei acestei funcţii
numărul blocului ı̂n care se află gara i trebuie să se afle ı̂n celula location[i].
ˆ stype: un tablou unidimensional de dimensiune n; la finalul execuţiei acestei funcţii tipul
blocului ı̂n care se află gara i trebuie să se afle ı̂n celula stype[i]: 1 pentru tipul C sau 2
pentru tipul D.
Puteţi apela funcţia getDistance pentru a vă ajuta să determinaţi poziţiile şi tipurile
blocurilor ı̂n care se află gările.
ˆ getDistance(i, j) returnează distanţa de la gara i la gara j. getDistance(i, i)
va returna 0. getDistance(i, j) va returna -1 dacă i sau j se află ı̂n afara intervalului
0 & i, j & n  1.

Subprobleme
Pentru toate subproblemele (subtask-urile) numărul de blocuri m nu depăşeşte 1,000,000. ı̂n
unele dintre subprobleme numărul de apeluri ale funcţiei getDistance este limitat. Această
limită variază de la subproblemă la subproblemă. Programul vostru va primi ’wrong answer’ dacă
depăşeşte această limită.
subproblemă puncte n apeluri către note
getDistance
1 8 1 & n & 100 nelimitat Toate gările cu excepţia lui 0
sunt ı̂n blocuri de tip D.
2 22 1 & n & 100 nelimitat Toate gările situate ı̂n estul gării
0 se află ı̂n blocuri de tip D, şi
toate gările situate la vestul gării
0 se află ı̂n blocuri de tip C.
3 26 1 & n & 5, 000 n n  1©2 fără restricţii adiţionale
4 44 1 & n & 5, 000 3 n  1 fără restricţii adiţionale
CAPITOLUL 6. IOI 2014 6.2. RAIL 682

Detalii de implementare
Voi trebuie să ı̂ncărcaţi exact un fişier, denumit rail.c, rail.cpp sau rail.pas. Acest fişier imple-
mentează funcţia findLocation aşa cum este descrisă mai sus, utilizând unul din următoarele
antete. Pentru programele C/C++ trebuie să includeţi şi header-ul rail.h.
pentru programele C/C++

void findLocation(int n, int first, int location[], int stype[]);

pentru programele Pascal

procedure findLocation(n, first : longint; var location,


stype : array of longint);

Antetul funcţiei getDistance este următorul:


pentru programele C/C++

int getDistance(int i, int j);

pentru programele Pascal

function getDistance(i, j: longint): longint;

Grader-ul de pe computerul vostru


Grader-ul de pe computerul vostru citeşte datele de intrare ı̂n următoarul format:
ˆ linia 1: numărul subproblemei
ˆ linia 2: numărul n
ˆ linia 3+i, (0 & i & n  1): numerele stype[i] (1 pentru tip C şi 2 pentru tip D), şi
location[i].

Grader-ul de pe computerul vostru va afişă Correct dacă location[0] ... location[n-1]


şi stype[0] ... stype[n-1] calculate de programul vostru sunt egale cu valorile corespunzătoare
din datele de intrare după ce findLocation termină execuţia, sau Incorrect dacă nu sunt egale.
Timp maxim de executare/test: 3.0 secunde
Memorie: total 256 MB

6.2.1 Indicaţii de rezolvare

Way : Ad Hoc
Query complexity: 3 N  1
Time complexity : O N log N 
** This problem can be solved by the following steps :
First, we know that station 0 is C type, and its location. We can query all the other stations’
distances from station 0, we call this: dis0i
Second, we sort all the dis0i, and obviously, the station x with the shortest distance
dis0x must be the first D type location after station 0.
Third, we process each station one by one according to their shortest distance with station 0
(that is, the order obtained in second step). For each station processed, we determine its type
and location immediately as follows:
3.1 First, we maintain the information (location,id) of the leftmost C type and the rightmost
D type as the algorithm proceeds.
3.2 To process the current station k, we use two queries, query(k,leftmost C type) as disk L,
query(k, rightmost D type) as disk R. And we also have dis0k . By some observations, we
CAPITOLUL 6. IOI 2014 6.2. RAIL 683

know that either disk L or disk R is achieved with a ’direct’ route (without moving forth
and back).
For example, we have only 4 cases to consider :

a. dis[k][L] is a direct route


( ) )
L k R

b. dis[k][L] is a direct route


( ) )
L R k

c. dis[k][R] is a direct route


( ( )
k L R

d. dis[k][R] is a direct route


( ( )
L k R

By careful analysis (some if/else conditions), we can get the answer. Sometimes, we may need
extra information to resolve the four cases, where we can check with dis0k .
Take case (a) for example :

a. dis[k][L] is a direct route


( ) )
L k R

the location of k might be L  disk L, Then use this location, we need to check if disk R
is reasonable or not.

a.1
( ( ) )
L p k R

There might be some p(type C) between L and k, So the disk R is dispR  dispk ,
we need to check whether p exists or not. If p doesn’t exist, then case (a) might be wrong, then
try cases (b),(c), and (d) until we find the answer.

6.2.2 Coduri sursă

Listing 6.2.1: rail-117000.cpp


//https://oj.uz/submission/117000 77 ms 760 KB

#include "rail.h"

#include <bits/stdc++.h>

#define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);


#define pb push_back
#define ET cout << "\n"
#define MEM(i,j) memset(i,j,sizeof i)
#define F first
#define S second
#define MP make_pair
#define ALL(v) v.begin(),v.end()
#define DB(a,s,e) {for(int i=s;i<e;++i) cerr << a[i] << " ";ET;}

using namespace std;

typedef long long ll;


typedef pair<int,int> pii;
CAPITOLUL 6. IOI 2014 6.2. RAIL 684

typedef pair<ll,ll> pll;

const int INF=1e9;

int gd(int i,int j)


{
return getDistance(i,j);
}

void findLocation(int N, int first, int location[], int stype[])


{
location[0]=first,stype[0]=1;
set<int> loc[3];
if(N==1) return;
vector<int> d0(N,0),d1(N,0),vR,vL;
deque<pii> vis;
for(int i=1;i<N;++i)
d0[i]=gd(0,i),vis.pb(MP(d0[i],i));
sort(ALL(vis));
int x=vis[0].S,tpR,tpL;
vis.pop_front(),location[x]=d0[x]+first,stype[x]=2;
for(int i=1;i<N;++i)
if(i!=x)
d1[i]=gd(i,x);
while(!vis.empty())
{
auto tmp=vis[0].S;
vis.pop_front();
if(d0[tmp]-d1[tmp]==d0[x])
if(d1[tmp]<=d0[x])
location[tmp]=location[x]-d1[tmp],stype[tmp]=1;
else
if(vL.empty())
location[tmp]=first+2*d0[x]-d0[tmp],
stype[tmp]=1,
vL.pb(location[tmp]),
tpL=tmp;
else
{
int dis=gd(tpL,tmp),
delta=location[tpL]+(d0[tpL]+dis-d0[tmp])/2;
int p=*lower_bound(vL.rbegin(),vL.rend(),delta);
if(p!=delta)
location[tmp]=first+2*d0[x]-d0[tmp],
stype[tmp]=1,
vL.pb(location[tmp]),
tpL=tmp;
else
location[tmp]=location[tpL]+dis,
stype[tmp]=2;
}
else
if(vR.empty())
location[tmp]=d0[tmp]+first,
stype[tmp]=2,
vR.pb(location[tmp]),
tpR=tmp;
else
{
int dis=gd(tpR,tmp),
delta=location[tpR]-(d0[tpR]+dis-d0[tmp])/2;
int p=*lower_bound(ALL(vR),delta);
if(p!=delta)
location[tmp]=d0[tmp]+first,
stype[tmp]=2,
vR.pb(location[tmp]),
tpR=tmp;
else
location[tmp]=location[tpR]-dis,
stype[tmp]=1;
}
}
}

// ------------- begin grader ---------------------


CAPITOLUL 6. IOI 2014 6.2. RAIL 685

typedef struct Station


{
int index;
int type;
int location;
int L,R;
} STATION;

long long cnt;


static int S,SUBTASK;
static STATION stations[10004];

int cmp_fun_1(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.location < d.location ? -1 : 1;
}

int cmp_fun_2(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.index < d.index ? -1 : 1;
}

void now_I_want_to_getLR()
{
int now = stations[S-1].index,i;
for(i=S-2;i>=0;i--)
{
stations[i].R = now;
if(stations[i].type==2) now = stations[i].index;
}
now = stations[0].index;
for(i=1;i<S;i++)
{
stations[i].L = now;
if(stations[i].type==1) now = stations[i].index;
}
}

int getDistance(int x,int y)


{
cnt++;
if(x==y) return 0;
if(x<0 || x>=S || y<0 || y>=S) return -1;
if(stations[x].location > stations[y].location)
{
int tmp = x;
x = y;
y = tmp;
}

int ret = 0;
if(stations[x].type==1 && stations[y].type==1)
{
ret = stations[stations[y].R].location-
stations[x].location+
stations[stations[y].R].location-
stations[y].location;
}
else
if(stations[x].type==1 && stations[y].type==2)
{
ret = stations[y].location-stations[x].location;
}
else
if(stations[x].type==2 && stations[y].type==2)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[y].location-
stations[stations[x].L].location;
CAPITOLUL 6. IOI 2014 6.2. RAIL 686

}
else
if(stations[x].type==2 && stations[y].type==1)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[y].location;
}

return ret;
}

void getInput()
{
int g;
g = scanf("%d",&SUBTASK);
g = scanf("%d",&S);

int s;
for (s = 0; s < S; s++)
{
int type, location;
g = scanf(" %d %d",&type,&location);
stations[s].index = s;
stations[s].location = location;
stations[s].type = type;
stations[s].L = -1;
stations[s].R = -1;
}

qsort(stations, S, sizeof(STATION), cmp_fun_1);


now_I_want_to_getLR();
qsort(stations, S, sizeof(STATION), cmp_fun_2);
}

int serverGetStationNumber()
{
return S;
}

int serverGetSubtaskNumber()
{
return SUBTASK;
}

int serverGetFirstStationLocation()
{
return stations[0].location;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/04-020.in", "r", stdin) ;


//std::freopen("rail.out", "w", stdout) ;

int i;
getInput();
cnt = 0;

int location[10005];
int type[10005];
int ok = 1;

auto t2 = clock();

findLocation(S, serverGetFirstStationLocation(),location, type);

auto t3 = clock();

if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;


if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;
CAPITOLUL 6. IOI 2014 6.2. RAIL 687

for (i = 0; i < S; i++)


if(type[i]!=stations[i].type || location[i]!=stations[i].location)
ok = 0;

if(ok==0) printf("Incorrect\n\n");
else printf("Correct\n\n");

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader ----------------------
/*
Correct

t2-t1 = 0.031
t3-t2 = 0.015
t4-t3 = 0.016

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


Press any key to continue.
*/

Listing 6.2.2: rail-121219.cpp


// https://oj.uz/submission/121219 80 ms 760 KB

#include <bits/stdc++.h>

using namespace std;

#define x first
#define y second
#define ll long long
#define pi pair<int,int>
#define pl pair<ll,ll>
#define pd pair<double,double>
#define ld long double
#define pld pair<ld,ld>
#define lg length()
#define sz size()
#define vi vector<int>
#define vl vector<ll>
#define vp vector<pi>
#define vpl vector<pl>
#define pb push_back
#define INF 1000000005
#define LINF 1000000000000000005

#ifdef LOCAL_DEFINE
mt19937 rng(69);
#else
seed_seq seq
{
(uint64_t) chrono::
duration_cast<chrono::
nanoseconds>(chrono::
high_resolution_clock::
now().time_since_epoch()).count(),
//(uint64_t) __builtin_ia32_rdtsc(),
//(uint64_t) (uintptr_t) make_unique<char>().get()
};
mt19937 rng(seq);
#endif
CAPITOLUL 6. IOI 2014 6.2. RAIL 688

#include "rail.h"

void findLocation(int n, int x, int p[], int t[])


{
if(n==1)
{
p[0]=x; t[0]=1;
return;
}
int a[n+5],b[n+5];
for(int i=1;i<n;i++) a[i]=getDistance(0,i);
int mn=INF,y=-1;
for(int i=1;i<n;i++)
{
if(a[i]<mn) mn=a[i],y=i;
}
for(int i=0;i<n;i++)
{
if(i==y) continue;
b[i]=getDistance(y,i);
}
mn=INF;
int z=-1;
for(int i=0;i<n;i++)
{
if(i==y) continue;
if(b[i]<mn) mn=b[i],z=i;
}
int diff=b[z]-a[y];
p[z]=x-diff;
p[y]=p[z]+b[z];
t[z]=1;
t[y]=2;
for(int i=0;i<n;i++)
{
a[i]+=diff;
}
vector <pi> l,r;
vector <int> u,v;
for(int i=0;i<n;i++)
{
if(i==y || i==z) continue;
if(i==0) a[i]=2*(p[y]-p[z])-diff;
if(a[i]>b[i]) l.pb({b[i],i});
else r.pb({a[i],i});
}

sort(l.begin(),l.end());
u.pb(p[z]);
int f=z;
for(pi i : l)
{
int d=getDistance(i.y,f);
p[i.y]=p[f]+d;
if(p[i.y]>=p[z])
{
p[i.y]=p[y]-b[i.y];
t[i.y]=1;
u.pb(p[i.y]);
f=i.y;
}
int c=*(lower_bound(u.begin(),u.end(),p[i.y],greater<int>()));
if(b[i.y]==p[i.y]+p[y]-2*c && p[i.y]!=c)
{
t[i.y]=2;
}
else
{
p[i.y]=p[y]-b[i.y];
t[i.y]=1;
u.pb(p[i.y]);
f=i.y;
}
}

sort(r.begin(),r.end());
CAPITOLUL 6. IOI 2014 6.2. RAIL 689

v.pb(p[y]);
f=y;
for(pi i : r)
{
int d=getDistance(i.y,f);
p[i.y]=p[f]-d;
if(p[i.y]<=p[y])
{
p[i.y]=p[z]+a[i.y];
t[i.y]=2;
v.pb(p[i.y]);
f=i.y;
}
int c=*(lower_bound(v.begin(),v.end(),p[i.y]));
if(a[i.y]==2*c-p[i.y]-p[z] && p[i.y]!=c)
{
t[i.y]=1;
}
else
{
p[i.y]=p[z]+a[i.y];
t[i.y]=2;
v.pb(p[i.y]);
f=i.y;
}
}
}

// ------------- begin grader ---------------------

typedef struct Station


{
int index;
int type;
int location;
int L,R;
} STATION;

long long cnt;


static int S,SUBTASK;
static STATION stations[10004];

int cmp_fun_1(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.location < d.location ? -1 : 1;
}

int cmp_fun_2(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.index < d.index ? -1 : 1;
}

void now_I_want_to_getLR()
{
int now = stations[S-1].index,i;
for(i=S-2;i>=0;i--)
{
stations[i].R = now;
if(stations[i].type==2) now = stations[i].index;
}
now = stations[0].index;
for(i=1;i<S;i++)
{
stations[i].L = now;
if(stations[i].type==1) now = stations[i].index;
}
}

int getDistance(int x,int y)


{
CAPITOLUL 6. IOI 2014 6.2. RAIL 690

cnt++;
if(x==y) return 0;
if(x<0 || x>=S || y<0 || y>=S) return -1;
if(stations[x].location > stations[y].location)
{
int tmp = x;
x = y;
y = tmp;
}

int ret = 0;
if(stations[x].type==1 && stations[y].type==1)
{
ret = stations[stations[y].R].location-
stations[x].location+
stations[stations[y].R].location-
stations[y].location;
}
else
if(stations[x].type==1 && stations[y].type==2)
{
ret = stations[y].location-stations[x].location;
}
else
if(stations[x].type==2 && stations[y].type==2)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[y].location-
stations[stations[x].L].location;
}
else
if(stations[x].type==2 && stations[y].type==1)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[y].location;
}

return ret;
}

void getInput()
{
int g;
g = scanf("%d",&SUBTASK);
g = scanf("%d",&S);

int s;
for (s = 0; s < S; s++)
{
int type, location;
g = scanf(" %d %d",&type,&location);
stations[s].index = s;
stations[s].location = location;
stations[s].type = type;
stations[s].L = -1;
stations[s].R = -1;
}

qsort(stations, S, sizeof(STATION), cmp_fun_1);


now_I_want_to_getLR();
qsort(stations, S, sizeof(STATION), cmp_fun_2);
}

int serverGetStationNumber()
{
return S;
}

int serverGetSubtaskNumber()
{
return SUBTASK;
CAPITOLUL 6. IOI 2014 6.2. RAIL 691

int serverGetFirstStationLocation()
{
return stations[0].location;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/04-020.in", "r", stdin) ;


//std::freopen("rail.out", "w", stdout) ;

int i;
getInput();
cnt = 0;

int location[10005];
int type[10005];
int ok = 1;

auto t2 = clock();

findLocation(S, serverGetFirstStationLocation(),location, type);

auto t3 = clock();

if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;


if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;

for (i = 0; i < S; i++)


if(type[i]!=stations[i].type || location[i]!=stations[i].location)
ok = 0;

if(ok==0) printf("Incorrect\n\n");
else printf("Correct\n\n");

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader ----------------------
/*
Correct

t2-t1 = 0.031
t3-t2 = 0.015
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.2.3: rail-125767.cpp


// https://oj.uz/submission/125767 87 ms 924 KB

#include "rail.h"
#include <bits/stdc++.h>
using namespace std;

const int mxN=5e3;


array<int, 2> a[mxN];
map<int, int> mp;
CAPITOLUL 6. IOI 2014 6.2. RAIL 692

void findLocation(int n, int f, int *p, int *s)


{
p[0]=f;
s[0]=1;
mp[f]=0;
for(int i=1; i<n; ++i)
a[i]={getDistance(0, i), i};

sort(a, a+n);

int l=0, r=a[1][1];


p[r]=f+a[1][0];
s[r]=2;
mp[p[r]]=r;
for(int i=2; i<n; ++i)
{
int dl=getDistance(l, a[i][1]),
dr=getDistance(r, a[i][1]),
m=(p[l]+dl+p[r]-dr)/2,
t=mp.find(m)==mp.end()?(m>f?1:2):s[mp[m]];

if(tˆ2)
{
p[a[i][1]]=p[l]+dl;
s[a[i][1]]=2;
if(p[a[i][1]]>p[r])
r=a[i][1];
}
else
{
p[a[i][1]]=p[r]-dr;
s[a[i][1]]=1;
if(p[a[i][1]]<p[l])
l=a[i][1];
}

mp[p[a[i][1]]]=a[i][1];
}
}

// ------------- begin grader ---------------------

typedef struct Station


{
int index;
int type;
int location;
int L,R;
} STATION;

long long cnt;


static int S,SUBTASK;
static STATION stations[10004];

int cmp_fun_1(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.location < d.location ? -1 : 1;
}

int cmp_fun_2(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.index < d.index ? -1 : 1;
}

void now_I_want_to_getLR()
{
int now = stations[S-1].index,i;
for(i=S-2;i>=0;i--)
{
stations[i].R = now;
CAPITOLUL 6. IOI 2014 6.2. RAIL 693

if(stations[i].type==2) now = stations[i].index;


}
now = stations[0].index;
for(i=1;i<S;i++)
{
stations[i].L = now;
if(stations[i].type==1) now = stations[i].index;
}
}

int getDistance(int x,int y)


{
cnt++;
if(x==y) return 0;
if(x<0 || x>=S || y<0 || y>=S) return -1;
if(stations[x].location > stations[y].location)
{
int tmp = x;
x = y;
y = tmp;
}

int ret = 0;
if(stations[x].type==1 && stations[y].type==1)
{
ret = stations[stations[y].R].location-
stations[x].location+
stations[stations[y].R].location-
stations[y].location;
}
else
if(stations[x].type==1 && stations[y].type==2)
{
ret = stations[y].location-stations[x].location;
}
else
if(stations[x].type==2 && stations[y].type==2)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[y].location-
stations[stations[x].L].location;
}
else
if(stations[x].type==2 && stations[y].type==1)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[y].location;
}

return ret;
}

void getInput()
{
int g;
g = scanf("%d",&SUBTASK);
g = scanf("%d",&S);

int s;
for (s = 0; s < S; s++)
{
int type, location;
g = scanf(" %d %d",&type,&location);
stations[s].index = s;
stations[s].location = location;
stations[s].type = type;
stations[s].L = -1;
stations[s].R = -1;
}

qsort(stations, S, sizeof(STATION), cmp_fun_1);


CAPITOLUL 6. IOI 2014 6.2. RAIL 694

now_I_want_to_getLR();
qsort(stations, S, sizeof(STATION), cmp_fun_2);
}

int serverGetStationNumber()
{
return S;
}

int serverGetSubtaskNumber()
{
return SUBTASK;
}

int serverGetFirstStationLocation()
{
return stations[0].location;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/04-020.in", "r", stdin) ;


//std::freopen("rail.out", "w", stdout) ;

int i;
getInput();
cnt = 0;

int location[10005];
int type[10005];
int ok = 1;

auto t2 = clock();

findLocation(S, serverGetFirstStationLocation(),location, type);

auto t3 = clock();

if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;


if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;

for (i = 0; i < S; i++)


if(type[i]!=stations[i].type || location[i]!=stations[i].location)
ok = 0;

if(ok==0) printf("Incorrect\n\n");
else printf("Correct\n\n");

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader ----------------------
/*
Correct

t2-t1 = 0.031
t3-t2 = 0.031
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.2.4: rail-139096.cpp


CAPITOLUL 6. IOI 2014 6.2. RAIL 695

// https://oj.uz/submission/139096 87 ms 888 KB

#include "rail.h"
#include <bits/stdc++.h>

using namespace std;

void findLocation(int n, int first, int l[], int t[])


{
l[0] = first;
t[0] = 1;
vector <pair <int, int>> v;
for (int i = 1; i < n; i++)
{
v.push_back({getDistance(0, i), i});
}

sort(v.begin(), v.end());
l[v[0].second] = first + v[0].first;
t[v[0].second] = 2;

set <int> C, D;
C.insert(l[0]); D.insert(l[v[0].second]);

int L = 0, R = v[0].second;
for (int i = 1; i < v.size(); i++)
{
int id = v[i].second;
int x = getDistance(id, L), y = getDistance(id, R);
int lcan = l[L] + x, rcan = l[R] - y, res;
auto it = --C.upper_bound(lcan);
if (y == lcan - *it + l[R] - *it)
{
res = 1;
}
else
{
auto it = D.upper_bound(rcan);
if (it != D.end() && x == *it - rcan + *it - l[L])
{
res = 0;
}
else
{
if (v[i].first == 2 * l[v[0].second] - first - rcan) res = 0;
else res = 1;
}
}

if (res == 1)
{
l[id] = lcan;
t[id] = 2;
D.insert(l[id]);
if (l[R] < l[id]) R = id;
}
else
{
l[id] = rcan;
t[id] = 1;
C.insert(l[id]);
if (l[L] > l[id]) L = id;
}
}
}

// ------------- begin grader ---------------------

typedef struct Station


{
int index;
int type;
int location;
int L,R;
} STATION;
CAPITOLUL 6. IOI 2014 6.2. RAIL 696

long long cnt;


static int S,SUBTASK;
static STATION stations[10004];

int cmp_fun_1(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.location < d.location ? -1 : 1;
}

int cmp_fun_2(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.index < d.index ? -1 : 1;
}

void now_I_want_to_getLR()
{
int now = stations[S-1].index,i;
for(i=S-2;i>=0;i--)
{
stations[i].R = now;
if(stations[i].type==2) now = stations[i].index;
}
now = stations[0].index;
for(i=1;i<S;i++)
{
stations[i].L = now;
if(stations[i].type==1) now = stations[i].index;
}
}

int getDistance(int x,int y)


{
cnt++;
if(x==y) return 0;
if(x<0 || x>=S || y<0 || y>=S) return -1;
if(stations[x].location > stations[y].location)
{
int tmp = x;
x = y;
y = tmp;
}

int ret = 0;
if(stations[x].type==1 && stations[y].type==1)
{
ret = stations[stations[y].R].location-
stations[x].location+
stations[stations[y].R].location-
stations[y].location;
}
else
if(stations[x].type==1 && stations[y].type==2)
{
ret = stations[y].location-stations[x].location;
}
else
if(stations[x].type==2 && stations[y].type==2)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[y].location-
stations[stations[x].L].location;
}
else
if(stations[x].type==2 && stations[y].type==1)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
CAPITOLUL 6. IOI 2014 6.2. RAIL 697

stations[stations[x].L].location+
stations[stations[y].R].location-
stations[y].location;
}

return ret;
}

void getInput()
{
int g;
g = scanf("%d",&SUBTASK);
g = scanf("%d",&S);

int s;
for (s = 0; s < S; s++)
{
int type, location;
g = scanf(" %d %d",&type,&location);
stations[s].index = s;
stations[s].location = location;
stations[s].type = type;
stations[s].L = -1;
stations[s].R = -1;
}

qsort(stations, S, sizeof(STATION), cmp_fun_1);


now_I_want_to_getLR();
qsort(stations, S, sizeof(STATION), cmp_fun_2);
}

int serverGetStationNumber()
{
return S;
}

int serverGetSubtaskNumber()
{
return SUBTASK;
}

int serverGetFirstStationLocation()
{
return stations[0].location;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/04-020.in", "r", stdin) ;


//std::freopen("rail.out", "w", stdout) ;

int i;
getInput();
cnt = 0;

int location[10005];
int type[10005];
int ok = 1;

auto t2 = clock();

findLocation(S, serverGetFirstStationLocation(),location, type);

auto t3 = clock();

if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;


if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;

for (i = 0; i < S; i++)


if(type[i]!=stations[i].type || location[i]!=stations[i].location)
ok = 0;

if(ok==0) printf("Incorrect\n\n");
else printf("Correct\n\n");
CAPITOLUL 6. IOI 2014 6.2. RAIL 698

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader ----------------------
/*
Correct

t2-t1 = 0.031
t3-t2 = 0.015
t4-t3 = 0.016

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


Press any key to continue.
*/

Listing 6.2.5: rail-173712.cpp


// https://oj.uz/submission/173712 87 ms 964 KB

#include "rail.h"
#include<bits/stdc++.h>

using namespace std;

vector<pair<int, int> >v;


set<int>C, D;

void findLocation(int n, int first, int l[], int t[])


{
l[0]=first; t[0]=1; //block 0, C station
for(int i=1; i<n; i++)
v.push_back({getDistance(0, i), i});
sort(v.begin(), v.end());
l[v[0].second]=first+v[0].first;
t[v[0].second]=2; //the first D station
C.insert(l[0]);
D.insert(l[v[0].second]);
int L=0, R=v[0].second;
for(int i=1; i<v.size(); i++)
{
int idx=v[i].second;
int lf=getDistance(idx, L);
int rg=getDistance(idx, R);
int lfcan=l[L]+lf, rgcan=l[R]-rg;
bool isC;
auto it1=--C.upper_bound(lfcan);
if(rg==lfcan-*it1+l[R]-*it1)
{
isC=false;
}
else
{
auto it2=D.upper_bound(rgcan);
if(it2!=D.end() && lf==*it2-rgcan+*it2-l[L])
{
isC=true;
}
else
{
if(v[i].first==2*l[v[0].second]-l[0]-rgcan)
isC=true;
else isC=false;
}
}
CAPITOLUL 6. IOI 2014 6.2. RAIL 699

if(isC)
{
l[idx]=rgcan;
t[idx]=1;
C.insert(l[idx]);
if(l[L]>l[idx]) L=idx;
}
else
{
l[idx]=lfcan;
t[idx]=2;
D.insert(l[idx]);
if(l[R]<l[idx]) R=idx;
}
}
}

// ------------- begin grader ---------------------

typedef struct Station


{
int index;
int type;
int location;
int L,R;
} STATION;

long long cnt;


static int S,SUBTASK;
static STATION stations[10004];

int cmp_fun_1(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.location < d.location ? -1 : 1;
}

int cmp_fun_2(const void *a,const void *b)


{
STATION c,d;
c = *(STATION*)(a);
d = *(STATION*)(b);
return c.index < d.index ? -1 : 1;
}

void now_I_want_to_getLR()
{
int now = stations[S-1].index,i;
for(i=S-2;i>=0;i--)
{
stations[i].R = now;
if(stations[i].type==2) now = stations[i].index;
}
now = stations[0].index;
for(i=1;i<S;i++)
{
stations[i].L = now;
if(stations[i].type==1) now = stations[i].index;
}
}

int getDistance(int x,int y)


{
cnt++;
if(x==y) return 0;
if(x<0 || x>=S || y<0 || y>=S) return -1;
if(stations[x].location > stations[y].location)
{
int tmp = x;
x = y;
y = tmp;
}
CAPITOLUL 6. IOI 2014 6.2. RAIL 700

int ret = 0;
if(stations[x].type==1 && stations[y].type==1)
{
ret = stations[stations[y].R].location-
stations[x].location+
stations[stations[y].R].location-
stations[y].location;
}
else
if(stations[x].type==1 && stations[y].type==2)
{
ret = stations[y].location-stations[x].location;
}
else
if(stations[x].type==2 && stations[y].type==2)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[y].location-
stations[stations[x].L].location;
}
else
if(stations[x].type==2 && stations[y].type==1)
{
ret = stations[x].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[stations[x].L].location+
stations[stations[y].R].location-
stations[y].location;
}

return ret;
}

void getInput()
{
int g;
g = scanf("%d",&SUBTASK);
g = scanf("%d",&S);

int s;
for (s = 0; s < S; s++)
{
int type, location;
g = scanf(" %d %d",&type,&location);
stations[s].index = s;
stations[s].location = location;
stations[s].type = type;
stations[s].L = -1;
stations[s].R = -1;
}

qsort(stations, S, sizeof(STATION), cmp_fun_1);


now_I_want_to_getLR();
qsort(stations, S, sizeof(STATION), cmp_fun_2);
}

int serverGetStationNumber()
{
return S;
}

int serverGetSubtaskNumber()
{
return SUBTASK;
}

int serverGetFirstStationLocation()
{
return stations[0].location;
}

int main()
{
auto t1 = clock();
CAPITOLUL 6. IOI 2014 6.3. WALL 701

std::freopen("../tests/04-020.in", "r", stdin) ;


//std::freopen("rail.out", "w", stdout) ;

int i;
getInput();
cnt = 0;

int location[10005];
int type[10005];
int ok = 1;

auto t2 = clock();

findLocation(S, serverGetFirstStationLocation(),location, type);

auto t3 = clock();

if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;


if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;

for (i = 0; i < S; i++)


if(type[i]!=stations[i].type || location[i]!=stations[i].location)
ok = 0;

if(ok==0) printf("Incorrect\n\n");
else printf("Correct\n\n");

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader ----------------------
/*
Correct

t2-t1 = 0.015
t3-t2 = 0.016
t4-t3 = 0

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


Press any key to continue.
*/

6.2.3 *Rezolvare detaliată

6.3 Wall
Problema 3 - Wall 100 de puncte

Author: Bartosz Tarnawski, Poland

Jian-Jia construieşte un perete din cărămizi de dimensiuni egale. Peretele este format din n
coloane de cărămizi, numerotate de la 0 la n  1 de la stânga la dreapta. Coloanele pot avea
ı̂nălţimi diferite. ı̂nălţimea unei coloane este numărul de cărămizi care o constituie.
Jian-Jia construieşte peretele după cum urmează: iniţial toate coloanele nu conţin nici o
cărămidă.
CAPITOLUL 6. IOI 2014 6.3. WALL 702

Apoi, Jian-Jia parcurge k faze adăugând sau scăzând cărămizi. Procesul de construire a
peretelui se ı̂ncheie atunci când toate cele k faze sunt parcurse. La fiecare fază lui Jian-Jia ı̂i sunt
date un interval de coloane consecutive şi o ı̂nălţime h. El aplică apoi următoarea procedură:
ˆ ı̂ntr-o fază de adăugare, Jian-Jia adaugă cărămizi la acele coloane din intervalul dat care au
mai puţin de h cărămizi, astfel ı̂ncât să ajungă la ı̂nălţimea de exact h cărămizi. Coloanele
care au h sau mai multe cărămizi rămân neschimbate.
ˆ ı̂ntr-o fază de scădere, Jian-Jia scoate cărămizi din acele coloane din intervalul dat care au
mai mult de h cărămizi, astfel ı̂ncât ele să ajungă la ı̂nălţimea de exact h cărămizi. Coloanele
care au h sau mai puţine cărămizi rămân neschimbate.
Sarcina voastră este să determinaţi forma finală a peretelui.
Exemplu
Să presupunem că avem 10 coloane din cărămizi şi 6 faze de construire a peretelui. ı̂n tabelul
de mai jos sunt incluse toate intervalele. Formele peretelui după fiecare fază sunt arătate mai jos.
faza tipul intervalul ı̂nălţimea
0 adăugare coloanele de la 1 la 8 4
1 scădere coloanele de la 4 la 9 1
2 scădere coloanele de la 3 la 6 5
3 adăugare coloanele de la 0 la 5 3
4 adăugare coloana 2 5
5 scădere coloanele de la 6 la 7 0
După faza 0 fiecare coloană de la 1 la 8 va avea câte 4 cărămizi,
deoarece toate coloanele iniţial sunt goale.
Coloanele 0 şi 9 rămân ı̂n continuare goale.
În faza 1, cărămizile sunt scoase de la coloanele de la 4 la 8
până când fiecare din coloane va avea câte o cărămidă, iar coloana
9 rămâne ı̂n continuare goală.
Coloanele de la 0 la 3, care sunt ı̂n afara intervalului, rămân
neschimbate.
Faza 2 nu produce nici o schimbare, deoarece coloanele de la 3
la 6 nu au mai mult de 5 cărămizi.
După faza 3, numărul de cărămizi ı̂n coloanele 0, 4, şi 5 creşte
la 3.
După faza 4, vom avea 5 cărămizi ı̂n coloana 2.
Faza 5 elimină toate cărămizile din coloanele 6 şi 7.
Cerinţă
Având descrierile celor k faze, vă rugăm să calculaţi numărul
de cărămizi din fiecare coloană după parcurgerea tuturor fazelor.
Trebuie să implementaţi funcţia buildWall.
ˆ buildWall(n, k, op, left, right, height, finalHeight)
– n: numărul de coloane care formează peretele.
– k: numărul de faze.
– op: tablou unidimensional de lungime k; opi este tipul fazei i: 1 pentru faza de
adăugare şi 2 pentru faza de scădere, pentru 0 & i & k  1.
– lef t şi right: tablouri unidimensionale de lungime k; intervalul de coloane ı̂n faza
i ı̂ncepe cu coloana lef ti şi se termină cu coloana righti (inclusiv ambele capete
lef ti şi righti), pentru 0 & i & k  1. Se presupune că ı̂ntodeauna lef ti & righti.
– height: tablou unidimensional de lungime k; heighti este parametrul de ı̂nălţime
pentru faza i, pentru 0 & i & k  1.
– f inalHeight: tablou unidimensional de lungime n; veţi returna ı̂n acesta rezultatele
obţinute plasând numărul final de cărămizi din coloana i ı̂n f inalHeighti, pentru
0 & i & n  1.

Subprobleme
Pentru toate subproblemele (subtask-urile) parametrii de ı̂nălţime din toate fazele sunt numere
ı̂ntregi nenegative mai mici sau egale cu 100,000.
CAPITOLUL 6. IOI 2014 6.3. WALL 703

subproblemă puncte n k notă


1 8 1 & n & 10, 000 1 & k & 5, 000 nu există alte limitări
2 24 1 & n & 100, 000 1 & k & 500, 000 fazele de adăugare sunt
parcurse ı̂naintea fazelor
de scădere
3 29 1 & n & 100, 000 1&k & 500, 000 nu există alte limitări
4 39 1 & n & 2, 000, 000 1&k & 500, 000 nu există alte limitări

Detalii de implementare
Trebuie să ı̂ncărcaţi exact un fişier, numit wall.c, wall.cpp sau wall.pas. ı̂n acest fişier se va
implementa funcţia descrisă mai sus, utilizând antetul de mai jos. De asemenea veţi include un
fişier header wall.h pentru programele C/C++.
pentru programele C/C++

void buildWall(int n, int k, int op[], int left[], int right[],


int height[], int finalHeight[]);

pentru programele Pascal

procedure buildWall(n, k : longint; op, left, right, height :


array of longint; var finalHeight : array of longint);

Grader-ul de pe computerul vostru


Grader-rul de pe computerul vostru citeşte datele de intrare ı̂n următorul format:
ˆ linia 1: n, k.
ˆ linia 2+i (0 & i & k  1): opi, lef ti, righti, heighti.

Timp maxim de executare/test: 3.0 secunde


Memorie: total 256 MB

6.3.1 Indicaţii de rezolvare

Overview
We’ll simplify the notion of the problem as follow:
Initially we have an array of length n where the value at each index is 0, and we are to process
k queries in order. We will denote the value at index x as Ax.
There are two kinds of operations:

ˆ M inimize l, r, t: For all indices x between l, r, the value become min Ax, t
ˆ M aximize l, r, t: For all indices x between l, r, the value become max Ax, t

The minimize operation corresponds to the original remove operation;


the maximize operation corresponds to the original add operation.
Trivial solution - O nk 
A trivial solution to this problem is to use an array of length n to maintain the current height
at each index, and for each query perform a linear update in O n.
This solution runs in O nk .
Segment Tree - O n  k  log n
One crucial observation is that for a specific position, any cascade of operations applied on it
could be simplified to one minimize operation and one maximize operation.
For example, applying (here we omit the paramter l, r as we consider operations solely on a
specific index):
CAPITOLUL 6. IOI 2014 6.3. WALL 704

ˆ minimize(9), minimize(8), minimize(7) ) minimize(7), maximize(inf)

ˆ minimize(3), maximize(7), minimize(4) ) minimize(4), maximize(4)

In general, this observation could be verified by simple induction.


With this observation we could now improve the update time. We instead use a segment tree
to maintain the final operations applied on an interval.
A segment tree is basically a binary tree, where each node contains the following information:

class Node
{
Node *left, *right; // children nodes
int from, to; // corresponding interval [from, to]
int opmin, opmax; // min/max operation applied on interval
};

An minimize operation would look something like:

void Node::minimize(int l, int r, int t)


{
if( from==l && to==r )
{ // applied on full segment
opmin = min(opmin, t);
opmax = min(opmin, opmax);
}
else
{
// pass down previously applied operation
left->minimize( left->from, left->to, opmin );
left->maximize( left->from, left->to, opmax );
right->minimize( right->from, right->to, opmin );
right->maximize( right->from, right->to, opmax );

// recursively apply current minimize operation


if( r<=left->to )
left->minimize( l, r, t );
else
if( l>=right->from )
right->minimize( l, r, t );
else
{
left->minimize( l, left->to, t );
right->minimize( right->from, r, t );
}
}
}

A single round of minimize operation runs in O log n, likewise for maximize operations. Note
that in the snippet above, we use the well-known lazy-update technique to maintain the operations
applied at one position. i.e. the message is passed down to children nodes only when necessary,
that is, when new operations are imposed on only part of the segment, so previous operations
should be passed down first.
After all queries are processed, we simply scan each position and check (with log n complexity)
the operations applied on it, obtaining the final value at the position.
The overall running time is therefore O n  k  log n.
It is possible to optimize further so that the overall running time become O n  k log k , it is
however entirely not necessary for this problem.
CAPITOLUL 6. IOI 2014 6.3. WALL 705

6.3.2 Coduri sursă

Listing 6.3.1: wall-39307.cpp


// https://oj.uz/submission/39307 803 ms 80156 KB

#include "wall.h"
#include <bits/stdc++.h>

using namespace std;

#define oo 2000000000
const int N = 2000010;
pair<int,int> seg[4 * N];

void update(int s,int e,int idx,int l,int r,int val,bool b)


{
val = max(val,seg[idx].first);
val = min(val,seg[idx].second);
if(s > r || e < l) return;
if(s >= l && e <= r)
{
if(!b) seg[idx].first = val; else seg[idx].second = val;
return;
}
update(s,(s+e)/2,idx*2,l,r,val,b);
update((s+e)/2+1,e,idx*2+1,l,r,val,b);
}

void make(int s,int e,int idx,int cur,int *arr)


{
cur = max(cur,seg[idx].first);
cur = min(cur,seg[idx].second);
if(s == e)
{
arr[s] = cur;
return;
}
make(s,(s+e)/2,idx*2,cur,arr);
make((s+e)/2+1,e,idx*2+1,cur,arr);
}

void buildWall(int n, int k, int op[], int left[], int right[],


int height[], int finalHeight[])
{
for(int i=1;i<=4 * n;i++) seg[i] = make_pair(0,oo);
for(int i=k-1;i>=0;i--)
update(0,n-1,1,left[i],right[i],height[i],op[i]-1);
make(0,n-1,1,0,finalHeight);
return;
}

// ------------ begin grader ------------------


int main()
{
auto t1 = clock();

std::freopen("../tests/04-030.in", "r", stdin) ;


std::freopen("wall.out", "w", stdout) ;

int n;
int k;

int i, j;
int status = 0;

status = scanf("%d%d", &n, &k);


assert(status == 2);

int* op = (int*)calloc(sizeof(int), k);


int* left = (int*)calloc(sizeof(int), k);
int* right = (int*)calloc(sizeof(int), k);
int* height = (int*)calloc(sizeof(int), k);
int* finalHeight = (int*)calloc(sizeof(int), n);
CAPITOLUL 6. IOI 2014 6.3. WALL 706

for (i = 0; i < k; i++)


{
status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
assert(status == 4);
}

auto t2 = clock();

buildWall(n, k, op, left, right, height, finalHeight);

auto t3 = clock();

for (j = 0; j < n; j++)


printf("%d\n", finalHeight[j]);

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader --------------------


/*
t2-t1 = 2.422
t3-t2 = 2.25
t4-t3 = 1.016

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


Press any key to continue.

argc = 4
checker
../tests/04-030.in
wall.out
../tests/04-030.out
----------------------
1
Correct

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


Press any key to continue.
*/

93939

Listing 6.3.2: wall-93939.cpp


// https://oj.uz/submission/93939 637 ms 69496 KB

#include "wall.h"
#include <bits/stdc++.h>
#define left_son (node<<1)
#define right_son ((node<<1)|1)
#define mid ((st+dr)>>1)

using namespace std;

const int lim = 1e5, Nmax = 2e6 + 5;

class SegTree
{
int a[Nmax<<2], b[Nmax<<2];

void combine(int node, int x, int y)


{
if(b[node] <= x) a[node] = b[node] = x;
else if(a[node] >= y) a[node] = b[node] = y;
CAPITOLUL 6. IOI 2014 6.3. WALL 707

else a[node] = max(a[node], x), b[node] = min(b[node], y);


}

void propag(int node)


{
combine(left_son, a[node], b[node]);
combine(right_son, a[node], b[node]);
a[node] = 0; b[node] = lim;
}

public:
void update(int node, int st, int dr, int L, int R, int A, int B)
{
if(L <= st && dr <= R)
{
combine(node, A, B);
return;
}

propag(node);

if(L <= mid) update(left_son, st, mid, L, R, A, B);


if(mid < R) update(right_son, mid+1, dr, L, R, A, B);
}

void propag(int node, int st, int dr, int *Ans)


{
if(st == dr)
{
Ans[st] = a[node];
return;
}
propag(node);
propag(left_son, st, mid, Ans);
propag(right_son, mid+1, dr, Ans);
}

void init(int node, int st, int dr)


{
a[node] = 0, b[node] = lim;
if(st == dr) return;
init(left_son, st, mid); init(right_son, mid+1, dr);
}
} aint;

void buildWall(int n, int k, int op[], int left[], int right[],


int height[], int finalHeight[])
{
aint.init(1, 0, n-1);
int i;
for(i=0; i<k; ++i)
aint.update(1, 0, n-1, left[i], right[i],
(op[i] == 1 ? height[i] : 0),
(op[i] == 1 ? lim : height[i]));

aint.propag(1, 0, n-1, finalHeight);


}

// ------------ begin grader ------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/04-030.in", "r", stdin) ;


std::freopen("wall.out", "w", stdout) ;

int n;
int k;

int i, j;
int status = 0;

status = scanf("%d%d", &n, &k);


assert(status == 2);
CAPITOLUL 6. IOI 2014 6.3. WALL 708

int* op = (int*)calloc(sizeof(int), k);


int* left = (int*)calloc(sizeof(int), k);
int* right = (int*)calloc(sizeof(int), k);
int* height = (int*)calloc(sizeof(int), k);
int* finalHeight = (int*)calloc(sizeof(int), n);

for (i = 0; i < k; i++)


{
status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
assert(status == 4);
}

auto t2 = clock();

buildWall(n, k, op, left, right, height, finalHeight);

auto t3 = clock();

for (j = 0; j < n; j++)


printf("%d\n", finalHeight[j]);

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
return 0;
}

// ------------ end grader --------------------


/*
t2-t1 = 2.615
t3-t2 = 3.217
t4-t3 = 1.016

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


Press any key to continue.

argc = 4
checker
../tests/04-030.in
wall.out
../tests/04-030.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 6.3.3: wall-173006.cpp


// https://oj.uz/submission/173006 649 ms 72884 KB

#include "wall.h"
#include <bits/stdc++.h>

#define adds 1
#define remov 2
#define left(n) (n<<1)
#define right(n) (n<<1|1)

using namespace std;

const int last = 1 << 21;


const int szt = 5e6;
const int maxn = 1<<21;
const int inf = 0x3f3f3f3f;
CAPITOLUL 6. IOI 2014 6.3. WALL 709

int ql, qr, state, val;

struct segtree
{
int mx[szt], mn[szt];

segtree()
{
memset(mx, inf, sizeof(mx));
}

void go(int node)


{
if(state == 2)
{
mx[node] = min(mx[node], val);
mn[node] = min(mn[node], val);
}
else
{
mx[node] = max(mx[node], val);
mn[node] = max(mn[node], val);
}
}

void merge(int par, int kid)


{
mx[kid] = min(mx[kid], mx[par]);
mx[kid] = max(mx[kid], mn[par]);
mn[kid] = max(mn[kid], mn[par]);
mn[kid] = min(mn[kid], mx[par]);
}

void unlazy(int node)


{
merge(node, left(node));
merge(node, right(node));
mx[node] = inf; mn[node] = 0;
}

void update(int node, int l, int r)


{
if(ql <= l && r <= qr)
{
go(node);
return;
}
unlazy(node);
int mid = (l + r) >> 1;
if(ql <= mid) update(left(node), l, mid);
if(qr > mid) update(right(node), mid + 1, r);
}

void finish()
{
for(int i = 1; i < last; i++)unlazy(i);
}
};

segtree tree;

void buildWall(int n, int k, int op[], int left[],


int right[], int height[], int ans[])
{
for(int i = 0; i < k; i++)
{
state = op[i], ql = left[i] + 1, qr = right[i] + 1, val = height[i];
tree.update(1, 1, last);
}

tree.finish();
int cnt = 0;
for(int i = last; i < last + n; i++)
{
ans[cnt++] = min(tree.mx[i], tree.mn[i]);
}
CAPITOLUL 6. IOI 2014 6.3. WALL 710

return;
}

// ------------ begin grader ------------------


int main()
{
auto t1 = clock();

std::freopen("../tests/04-030.in", "r", stdin) ;


std::freopen("wall.out", "w", stdout) ;

int n;
int k;

int i, j;
int status = 0;

status = scanf("%d%d", &n, &k);


assert(status == 2);

int* op = (int*)calloc(sizeof(int), k);


int* left = (int*)calloc(sizeof(int), k);
int* right = (int*)calloc(sizeof(int), k);
int* height = (int*)calloc(sizeof(int), k);
int* finalHeight = (int*)calloc(sizeof(int), n);

for (i = 0; i < k; i++)


{
status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
assert(status == 4);
}

auto t2 = clock();

buildWall(n, k, op, left, right, height, finalHeight);

auto t3 = clock();

for (j = 0; j < n; j++)


printf("%d\n", finalHeight[j]);

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
return 0;
}

// ------------ end grader --------------------


/*
t2-t1 = 2.688
t3-t2 = 4.484
t4-t3 = 1

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


Press any key to continue.

argc = 4
checker
../tests/04-030.in
wall.out
../tests/04-030.out
----------------------
1
Correct

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


Press any key to continue.
*/
CAPITOLUL 6. IOI 2014 6.3. WALL 711

Listing 6.3.4: wall-232336.cpp


// https://oj.uz/submission/232336 692 ms 60536 KB

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "wall.h"

#include<ctime>
#include<iostream>

using namespace std;

const int inf = 1e9, N = 1 << 22;


int mx[N], mn[N];

void change(int v, int d, int u)


{
mn[v] = max(u, min(d, mn[v]));
mx[v] = min(d, max(u, mx[v]));
}

void push(int v)
{
change(v << 1|0, mn[v], mx[v]);
change(v << 1|1, mn[v], mx[v]);
mx[v] = 0; mn[v] = inf;
}

int d, u, lo, hi;


void update(int v, int l, int r)
{
if (hi <= l || r <= lo) return;
if (lo <= l && r <= hi) return change(v, d, u);
push(v); int m = (l + r) / 2;
update(v << 1|0, l, m);
update(v << 1|1, m, r);
}

void final(int ans[], int v, int l, int r)


{
if (r - l == 1)
return void(ans[l] = min(mx[v], mn[v]));
push(v); int m = (l + r) / 2;
final(ans, v << 1|0, l, m);
final(ans, v << 1|1, m, r);
}

void buildWall(int n, int q, int op[], int l[], int r[], int h[], int ans[])
{
for (int i = 0; i < N; ++i) mn[i] = inf, mx[i] = 0;
for (int i = 0; i < q; ++i)
{
u = 0; d = inf;
lo = l[i]; hi = r[i] + 1;
op[i] != 1 ? d = h[i]: u = h[i];
update(1, 0, n);
}
final(ans, 1, 0, n);
}

// ------------ begin grader ------------------


int main()
{
auto t1 = clock();

std::freopen("../tests/04-030.in", "r", stdin) ;


std::freopen("wall.out", "w", stdout) ;

int n;
int k;

int i, j;
int status = 0;
CAPITOLUL 6. IOI 2014 6.3. WALL 712

status = scanf("%d%d", &n, &k);


assert(status == 2);

int* op = (int*)calloc(sizeof(int), k);


int* left = (int*)calloc(sizeof(int), k);
int* right = (int*)calloc(sizeof(int), k);
int* height = (int*)calloc(sizeof(int), k);
int* finalHeight = (int*)calloc(sizeof(int), n);

for (i = 0; i < k; i++)


{
status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
assert(status == 4);
}

auto t2 = clock();

buildWall(n, k, op, left, right, height, finalHeight);

auto t3 = clock();

for (j = 0; j < n; j++)


printf("%d\n", finalHeight[j]);

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader --------------------


/*
t2-t1 = 0.593
t3-t2 = 3.922
t4-t3 = 0.813

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


Press any key to continue.

argc = 4
checker
../tests/04-030.in
wall.out
../tests/04-030.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 6.3.5: checkerWall.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
CAPITOLUL 6. IOI 2014 6.4. FRIEND 713

(char*)"../tests/04-030.in",
(char*)"wall.out",
(char*)"../tests/04-030.out",
};

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

registerChecker("wall", argc, argv);


compareRemainingLines();
}

6.3.3 *Rezolvare detaliată

6.4 Friend
Problema 4 - Friend 100 de puncte

Author: Sun-Yuan Hsieh, Taiwan

Construim o reţea de socializare din n persoane, numerotate 0, ... , n  1. Unele perechi de


persoane din reţea vor deveni prieteni. Dacă persoana x devine prieten al persoanei y, atunci
persoana y devine de asemenea prieten al lui x.
Membrii sunt adăugaţi ı̂n reţea ı̂n n etape, numerotate de la 0 la n  1. Persoana i este
adăugată la etapa i. La etapa 0 persoana 0 este adăugată ca unica persoană din reţea. La fiecare
din următoarele n  1 etape o persoană este adăugată ı̂n reţea de o gazdă, care trebuie să fie deja
un membru al reţelei. La etapa i (0 $ i $ n), gazda acestei etape poate adăuga ı̂n reţea persoana
cu numărul i folosind unul din următoarele trei protocoale:
ˆ IAmY ourF riend: persoana i devine prieten doar cu gazda.
ˆ M yF riendsAreY ourF riends: persoana i devine prieten cu fiecare persoană care la acest
moment este prieten al gazdei. Atenţie, acest protocol nu face prieteni persoana i cu gazda.
ˆ W eAreY ourF riends: persoana i devine prieten atât cu gazda, cât şi cu fiecare persoană
care la acest moment este prieten al gazdei.

După ce construim reţeaua, dorim să selectăm un eşantion pentru un sondaj, ceea ce pre-
supune alegerea unui grup de persoane din reţea. Deoarece prietenii de obicei au interese similare,
eşantionul trebuie să nu includă nicio pereche de prieteni. Fiecare persoană are asociată o ı̂ncredere
de sondaj, exprimată printr-un număr ı̂ntreg pozitiv. Vom ı̂ncerca să determinăm un eşantion de
ı̂ncredere totală maximă (prin ı̂ncredere totală a unui eşantion se ı̂nţelege suma ı̂ncrederilor aso-
ciate presoanelor care ı̂l formează).
Exemplu

etapă gazdă protocol relaţii de prietenie adăugate


1 0 IAmYourFriend (1, 0)
2 0 MyFriendsAreYourFriends (2, 1)
3 1 WeAreYourFriends (3, 1), (3, 0), (3, 2)
4 2 MyFriendsAreYourFriends (4, 1), (4, 3)
5 0 IAmYourFriend (5, 0)

Iniţial reţeaua este formată doar din persoana 0. Gazda etapei 1 (persoana 0) invită persoana
1 prin protocolul IAmY ourF riend, astfel ei devin prieteni.
Gazda etapei 2 (din nou persoana 0) invită persoana 2 prin protocolul
M yF riendsAreY ourF riends, ceea ce face ca persoana 1 (unicul prieten al gazdei) să devină
prieten al persoanei 2 (fără ca persoana 0 să devină prieten cu 2).
Gazda etapei 3 (persoana 1) adaugă persoana 3 prin protocolul WeAreYourFriends care face
persoana 3 prieten cu persoana 1 (gazda) dar şi cu persoanele 0 şi 2 (prietenii gazdei).
CAPITOLUL 6. IOI 2014 6.4. FRIEND 714

Etapele 4 şi 5 sunt de asemenea prezentate ı̂n tabelul de mai sus. Reţeaua finală este prezen-
tată ı̂n figura următoare, ı̂n care numerele ı̂nscrise ı̂n cercuri reprezintă indicii membrilor reţelei,
iar numerele alăturate cercurilor reprezintă ı̂ncrederea de sondaj asociată persoanelor respective.
Eşantionul care constă din persoanele 3 şi 5 are ı̂ncrederea totală egală cu 20 + 15 = 35, care este
ı̂ncrederea totală maximă posibilă.

Cerinţă
Fiind dată descrierea fiecărei etape şi valoarea ı̂ncrederii asociate fiecărei persoane, găsiţi un
eşantion cu ı̂ncredere totală maximă. Trebuie să implementaţi funcţia findSample.
ˆ findSample(n, confidence, host, protocol)
– n: numărul de persoane.
– conf idence: tablou unidimensional de lungime n; conf idencei reprezintă valoarea
ı̂ncrederii asociate persoanei i.
– host: tablou unidimensional de lungime n; hosti reprezintă gazda etapei i.
– protocol: tablou unidimensional de lungime n; protocoli reprezintă codul pro-
tocolului folosit la etapa i (0 $ i $ n): 0 pentru IAmY ourF riend, 1 pentru
M yF riendsAreY ourF riends, şi 2 pentru W eAreY ourF riends.
– Deoarece nu există gazdă la etapa 0, host[0] şi protocol[0] sunt nedefinite şi nu trebuie
să fie accesate de programul vostru. Funcţia trebuie să returneze ı̂ncrederea totală
maximă a unui eşantion.

Subprobleme
Unele subprobleme (subtask-uri) vor folosi doar o parte din protocoale, aşa cum se vede ı̂n
tabelul ce urmează.

subtask puncte n ı̂ncredere protocoale folosite


1 11 2 & n & 10 1 & conf idence & 1, 000, 000 Toate cele trei protocoale
2 8 2 & n & 1, 000 1 & conf idence & 1, 000, 000 Doar
M yF riendsAreY ourF riends
3 8 2 & n & 1, 000 1 & conf idence & 1, 000, 000 Doar W eAreY ourF riends
4 19 2 & n & 1, 000 1 & conf idence & 1, 000, 000 Doar IAmY ourF riend
5 23 2 & n & 1, 000 Toate valorile de ı̂ncredere Doar
sunt 1 M yF riendsAreY ourF riends
şi IAmY ourF riend
6 31 2 & n & 100, 000 1 & conf idence & 10, 000 Toate cele trei protocoale

Detalii de implementare
Trebuie să ı̂ncărcaţi exact un fişier, numit friend.c, friend.cpp sau friend.pas. Fişierul trebuie să
conţină implementarea subprogramului descris mai sus, utilizând următorul antet. De asemenea
trebuie să includeţi fişierul header friend.h pentru implementările C/C++.
pentru programele C/C++

int findSample(int n, int confidence[], int host[], int protocol[]);

pentru programele Pascal


CAPITOLUL 6. IOI 2014 6.4. FRIEND 715

function findSample(n: longint, confidence: array of longint,


host: array of longint; protocol: array of longint): longint;

Grader-ul de pe computerul vostru


Grader-ul de pe computerul vostru citeşte datele de intrare ı̂n formatul următor:
ˆ line 1: n
ˆ line 2: conf idence0, ..., conf idencen  1
ˆ line 3: host1, protocol1, host2, protocol2, ..., hostn  1, protocoln  1

Grader-ul de pe computerul vostru va afişa valoarea returnată de f indSample.


Timp maxim de executare/test: 1.0 secunde
Memorie: total 16 MB

6.4.1 Indicaţii de rezolvare

6 solutions to 6 subtasks
Solution1 Solution2 Solution3 Solution4 Solution5 Solution6
Subtask1 AC WA WA WA WA AC
Subtask2 TLE AC WA WA WA AC
Subtask3 TLE WA AC WA WA AC
Subtask4 TLE WA WA AC WA AC
Subtask5 TLE WA WA WA AC AC
Subtask6 TLE WA WA WA WA AC
Table 1-1: The result of each solution applying to each subtask.
Note: AC=Accepted, WA=Wrong Answer, TLE=Time Limit Exceeded.
Solution1
In subtask1, N is at most 10. So just apply backtracking for every person - to chose or not to
N
chose. The complexity is O N ˜ 2 , Accepted.
The sizes of N in other subtasks are too large to apply this solution, resulting in Time Limit
Exceeded with this complexity.
Solution2
In subtask2, there’re all ’MyFriendsAreYourFriends’ relations, forming a graph with no edge.
That is equivalent to chose all persons, with complexity of O N .
For other subtasks, there’re not only this kind of relations, so this solution does not work and
will result in Wrong Answer.
Solution3
In subtask3, there’re all ’WeAreYourFriends’ relations, forming a complete graph. Since every
pair of two persons is connected by an edge, the answer to this problem is equivalent to choose
the maximum confidence among all people, with complexity of O N .
For other subtasks, there’re not only this kind of relations, so this solution does not work and
will result in Wrong Answer.
Solution4
In subtask4, all relations are ’IamYourFriend’, forming a tree. So we apply the DP-in-tree
method.
Define dpij  as the maximum sum for the i-th node with status j, where j 0 stands for
not choosing this node and j 1 stands for choosing this node. Then:

(1) If the ith node is leaf, then


– dpij  0, for j 0.
– dpij  conf idencei, for j 1.
(2) Otherwise,
CAPITOLUL 6. IOI 2014 6.4. FRIEND 716

– dpij  max dpk 0, dpk 1, for j 0 and for all k, where k is i’s child.
– dpij  dpk 0, for j 1 and for all k, where k is i’s child.

The final answer is max dproot0; dproot1, where root stands for the root of this tree.
For other subtasks, there’re not only ’IamYourFriend’ relations, so this solution will not work
and will result in Wrong Answer.
Solution5
Since there’re only ’MyFriendsAreYourFriends’ and ’IamYourFriend’ relations, the resulting
graph contains no odd cycle. That is, we obtain a bipartite graph.
With all confidence equals to 1, the problem becomes finding maximum independent set in a
bipartite graph. As we know, a set is independent if and only if its complement is a vertex cover.
If the complement of independent set is not a vertex cover, then there exists at least one edge
with end points u and v, which is included in the independent set, conflicting with the definition
of independent set. Trivially, a maximum independent set is the complement of minimum vertex
cover.
According to Konig’s theorem[1], in any bipartite graph, the number of edges in a maximum
matching is equal to the number of vertices in a minimum vertex cover. Thus, we can apply
the augmenting path algorithm toÓ find out maximum cardinality matching in a bipartite graph,
with complexity of O N E  or O N E , depending on different implementations, where E is the
number of edges.
Let k be the result of maximum cardinality bipartite matching, the answer to this problem
equals to N  k, since maximum independent set is the complement of minimum vertex cover.
As for partitioning the graph into bipartite, we apply dfs to mark out the odd points and the
even points, and then put all odd points one side, even points the other side.
In subtask 2 and 4, the relations are ’MyFriendsAreYourFriends’ and ’IamYourFriend’. How-
ever, the confidence value in those two subtasks are not all equals to 1. As a result, this solution
can only solve subtask5 correctly, and Wrong Answer for other subtasks.
Solution6
By using Greedy method to eliminate each person in the reverse order of building process, we
will finally get the p; q  pair for the last person. The answer will be max p; q  of the last person.
Here we briefly introduce this method. Initially, we maintain two values p x and q x for
each person x, where p x conf idencex and q x 0. Physically, p stands for ’choose’ and q
stands for ’not choose’.
¬ ¬
To simplify the notation, we call p for p x, q for q x, p for p y , q for q y .
(1) WeAreYourFriends

To eliminate y, we can either choose x or chose y, or neither of both.


¬
(a) Choose x  p p  q .
¬
(b) Choose y  p p  q.
¬ ¬ ¬
(c) Neither: q q  q . So p max p  q ; p0  q , q q  q .
(2) MyFriendsAreYourFriends

To eliminate y, we can choose x, choose y or choose both, or neither of both.


CAPITOLUL 6. IOI 2014 6.4. FRIEND 717

¬
(a) Choose x  p p  q .
¬
(b) Choose y  p p  q.
¬
(c) Choose both: p p  p .
¬ ¬ ¬ ¬ ¬
(d) Neither: q q  q . So p max p  q ; p  q; p  p , q qq.
(3) IamYourFriend

To eliminate y, we can choose x, or either choose y or choose neither.


¬
(a) Choose x  p p  q .
¬
(b) Choose y  q p  q.
¬ ¬ ¬ ¬
(c) Neither: q q  q . So p p  q , q max p  q; q  q .
The complexity of this scheme is O N . This method is capable of solving all subtasks.
Reference
[1] Konig’s theorem
https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_(graph_theory)

6.4.2 Coduri sursă

Listing 6.4.1: friend-16524.cpp


// https://oj.uz/submission/16524 26 ms 3036 KB
#include <cstdio>
#include <cassert>

#include<ctime>
#include<iostream>

#define __MAXSIZE__ 100002

#include "friend.h"
#include <algorithm>

using namespace std;

int a[100010],b[100010];
int findSample(int n,int *c,int *h,int *p)
{
int i;
for(i=0;i<n;++i)a[i]=c[i];
for(;--i;)
{
if(!p[i])
{
a[h[i]]+=b[i];
b[h[i]]+=max(a[i],b[i]);
}
else
if(p[i]==1)
{
a[h[i]]=max(a[h[i]]+max(a[i],b[i]),b[h[i]]+a[i]);
b[h[i]]+=b[i];
}
else
{
a[h[i]]=max(a[h[i]]+b[i],b[h[i]]+a[i]);
b[h[i]]+=b[i];
}
}
CAPITOLUL 6. IOI 2014 6.4. FRIEND 718

return max(a[0],b[0]);
}

// ----------- begin grader --------------------

// Confidence
int confidence[__MAXSIZE__];

// Host
int host[__MAXSIZE__];

// Protocol
int protocol[__MAXSIZE__];

// Main
int main(void)
{
auto t1 = clock();

std::freopen("../tests/06-015.in", "r", stdin) ;


std::freopen("friend.out", "w", stdout) ;

int n,i;

// Number of people
assert(scanf("%d",&n)==1);

// Confidence
for(i=0;i<n;i++)
assert(scanf("%d",&confidence[i])==1);

// Host and Protocol


for(i=1;i<n;i++)
assert(scanf("%d %d",&host[i],&protocol[i])==2);

auto t2 = clock();

// Answer
printf("%d\n",findSample(n,confidence,host,protocol));
auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------- end grader ----------------------


/*
t2-t1 = 0.359
t3-t2 = 0.016
t4-t3 = 0

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


Press any key to continue.

rgc = 4
checker
../tests/06-015.in
friend.out
../tests/06-015.out
----------------------
1
Correct

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


Press any key to continue.
*/
CAPITOLUL 6. IOI 2014 6.4. FRIEND 719

Listing 6.4.2: friend-115760.cpp


// https://oj.uz/submission/115760 31 ms 2688 KB

#include <cstdio>
#include <cassert>

#include<ctime>
#include<iostream>

#define __MAXSIZE__ 100002

using namespace std;

#include "friend.h"
#define max(a, b) ((a) > (b) ? (a) : (b))

int findSample(int n, int val[], int host[], int typ[])


{
int ans = 0;
for(int i = n - 1; i >= 1; --i)
{
int &A = val[host[i]], &B = val[i];
if(typ[i] == 0) ans += B, A = max(0, A - B);
if(typ[i] == 1) A += B;
if(typ[i] == 2) A = max(A, B);
}
return ans + val[0];
}

// ----------- begin grader --------------------

// Confidence
int confidence[__MAXSIZE__];

// Host
int host[__MAXSIZE__];

// Protocol
int protocol[__MAXSIZE__];

// Main
int main(void)
{
auto t1 = clock();

std::freopen("../tests/06-015.in", "r", stdin) ;


std::freopen("friend.out", "w", stdout) ;

int n,i;

// Number of people
assert(scanf("%d",&n)==1);

// Confidence
for(i=0;i<n;i++)
assert(scanf("%d",&confidence[i])==1);

// Host and Protocol


for(i=1;i<n;i++)
assert(scanf("%d %d",&host[i],&protocol[i])==2);

auto t2 = clock();

// Answer
printf("%d\n",findSample(n,confidence,host,protocol));
auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


CAPITOLUL 6. IOI 2014 6.4. FRIEND 720

std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------- end grader ----------------------


/*
t2-t1 = 0.374
t3-t2 = 0
t4-t3 = 0.016

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


Press any key to continue.

argc = 4
checker
../tests/06-015.in
friend.out
../tests/06-015.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 6.4.3: friend-119597.cpp


// https://oj.uz/submission/119597 31 ms 2688 KB

//#include <cstdio>
//#include <cassert>

#define __MAXSIZE__ 100002


#include "friend.h"

#include <bits/stdc++.h>
using namespace std;
int findSample(int n,int c[],int h[],int p[])
{
int ans=0;
for(int i=n-1;i>0;i--)
{
if(p[i]==0)ans+=c[i],c[h[i]]=max(0,c[h[i]]-c[i]);
if(p[i]==1)c[h[i]]+=c[i];
if(p[i]==2)c[h[i]]=max(c[h[i]],c[i]);
}
return ans+c[0];
}

// ----------- begin grader --------------------

// Confidence
int confidence[__MAXSIZE__];

// Host
int host[__MAXSIZE__];

// Protocol
int protocol[__MAXSIZE__];

// Main
int main(void)
{
auto t1 = clock();

std::freopen("../tests/06-015.in", "r", stdin) ;


std::freopen("friend.out", "w", stdout) ;

int n,i;

// Number of people
assert(scanf("%d",&n)==1);
CAPITOLUL 6. IOI 2014 6.4. FRIEND 721

// Confidence
for(i=0;i<n;i++)
assert(scanf("%d",&confidence[i])==1);

// Host and Protocol


for(i=1;i<n;i++)
assert(scanf("%d %d",&host[i],&protocol[i])==2);

auto t2 = clock();

// Answer
printf("%d\n",findSample(n,confidence,host,protocol));
auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------- end grader ----------------------


/*
t2-t1 = 0.469
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/06-015.in
friend.out
../tests/06-015.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 6.4.4: checkerFriend.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/06-015.in",
(char*)"friend.out",
(char*)"../tests/06-015.out",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 722

registerChecker("wall", argc, argv);


compareRemainingLines();
}

6.4.3 *Rezolvare detaliată

6.5 Gondola
Problema 5 - Gondola 100 de puncte

Author: Michal Foris̆ek. Slovakia

Telegondola Mao-Kong este o atracţie turistică faimoasă ı̂n Taiwan. Sistemul telegondolei
constă dintr-o şină circulară, o singură staţie, şi n gondole numerotate consecutiv de la 1 la n
care merg circular pe linie ı̂ntr-o direcţie fixată. După ce gondola cu numărul i trece prin staţie,
următoarea gondolă care trece prin staţie va fi gondola cu numărul i  1 dacă i $ n, sau gondola
cu numărul 1 dacă i n.
Gondolele pot să se defecteze. Din fericire avem la dispoziţie oricâte gondole de rezervă, care
sunt numerotate n  1, n  2, şi aşa mai departe. Când o gondolă se defectează, o ı̂nlocuim (ı̂n
aceeaşi poziţie pe şină) cu prima gondolă de rezervă disponibilă, adică cu cea care are cel mai mic
număr. De exemplu, dacă pe şină există 5 gondole şi gondola cu numărul 1 se defectează prima,
o ı̂nlocuim cu gondola cu numărul 6.
Ţie ı̂ţi place să stai ı̂n staţie şi să te uiţi cum trec gondolele. O secvenţă de gondole este o
secvenţă de n numere ale gondolelor ı̂n ordinea ı̂n care trec ele prin staţie. Este posibil ca una sau
mai multe din gondole să se fi defectat (şi să fi fost ı̂nlocuite) ı̂nainte să fi ajuns tu ı̂n staţie, dar
niciuna din gondole nu se defectează ı̂n timpul ı̂n care tu te uiţi la gondole.
Observaţi că aceeaşi configuraţie a gondolelor pe şină poate produce mai multe secvenţe de
gondole, ı̂n funcţie de care gondolă trece prima oară prin staţie când ajungi tu acolo. De exemplu,
dacă nicuna din gondole nu s-a defectat, atunci (2, 3, 4, 5, 1) şi (4, 5, 1, 2, 3) sunt secvenţe posibile
de gondole, ı̂n timp ce (4, 3, 2, 5, 1) nu este (deoarece gondolele apar ı̂n ordinea greşită).
Dacă se defectează gondola cu numărul 1, atunci am putea observa secvenţa de gondole (4, 5,
6, 2, 3).
Dacă următoarea care se defectează este gondola cu numărul 4, o ı̂nlocuim cu gondola numărul
7 şi am putea observa secvenţa de gondole (6, 2, 3, 7, 5). Dacă apoi se defectează gondola cu
numărul 7, o ı̂nlocuim cu gondola numărul 8 şi am putea observa secvenţa de gondole (3, 8, 5, 6,
2).

gondola stricată gondola nouă secvenţă posibilă de gondole


1 6 (4, 5, 6, 2, 3)
4 7 (6, 2, 3, 7, 5)
7 8 (3, 8, 5, 6, 2)

O secvenţă de ı̂nlocuiri este o secvenţă care conţine numerele gondolelor care s-au stricat, ı̂n
ordinea ı̂n care s-au stricat. Pentru exemplul precedent, secvenţa de ı̂nlocuiri este (1, 4, 7). O
secvenţă de ı̂nlocuiri r produce o secvenţă de gondole g dacă, după ce gondolele se strică conform
cu secvenţa de ı̂nlocuiri r, secvenţa de gondole g ar putea fi observată.
Verificarea unei secvenţe de gondole
Pentru primele trei subprobleme (subtask-uri) trebuie să verificaţi dacă o secvenţă dată este
o secvenţă de gondole. ı̂n tabelul de mai jos puteţi vedea exemple de secvenţe care sunt sau nu
secvenţe de gondole. Trebuie să impementaţi funcţia valid.
ˆ valid n, inputSeq 
– n: lungimea secvenţei date.
– inputSeq: tablou unidimensional de lungime n; inputSeq i este elementul i al secvenţei
date, pentru 0 & i & n  1.
– Funcţia trebuie să retuneze 1 dacă secvenţa dată este o secvenţă de gondole, sau 0 ı̂n
caz contrar.
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 723

Subproblemele 1, 2, 3

subproblemă puncte n inputSeq


1 5 n & 100 fiecare număr de la 1 la n apare exact o dată
2 5 n & 100, 000 1 & inputSeq i & n
3 10 n & 100, 000 1 & inputSeq i & 250, 000

Exemple

subproblemă inputSeq valoarea returnată note


1 (1, 2, 3, 4, 5, 6, 7) 1
1 (3, 4, 5, 6, 1, 2) 1
1 (1, 5, 3, 4, 2, 7, 6) 0 1 nu poate apărea imediat ı̂naintea lui 5
1 (4, 3, 2, 1) 0 4 nu poate apărea imediat ı̂naintea lui 3
2 (1, 2, 3, 4, 5, 6, 5) 0 există două gondole cu numărul 5
3 (2, 3, 4, 9, 6, 7, 1) 1 secvenţa de ı̂nlocuiri este (5, 8)
3 (10, 4, 3, 11, 12) 0 4 nu poate apărea imediat ı̂naintea lui 3

Secvenţa de ı̂nlocuiri
Pentru următoarele trei subprobleme trebuie să contruiţi o secvenţă de ı̂nlocuiri care produce
o secvenţă de gondole dată. Orice secvenţă corectă de ı̂nlocuiri va fi acceptată. Trebuie să
implementaţi funcţia replacement.
ˆ replacement(n, gondolaSeq, replacementSeq)
– n este lungimea secvenţei de gondole.
– gondolaSeq: tablou unidimensional de lungime n; se garantează că gondolaSeq este o
secvenţă de gondole, şi gondolaSeq i este elementul cu numărul i din secvenţă, pentru
0 & i & n  1.
– Funcţia trebuie să returneze numărul l, lungimea secvenţei de ı̂nlocuiri.
– replacementSeq: un tablou unidimensional care este suficient de mare pentru a stoca
elementele secvenţei de ı̂nlocuiri; trebuie să transmiteţi secvenţa prin plasarea elemen-
tului i al secvenţei de ı̂nloucuiri găsite de voi ı̂n replacementSeq i, pentru 0 & i & l  1.

Subproblemele 4, 5, 6

subproblemă puncte n gondolaSeq


4 5 n & 100 1 & gondolaSeq i & n  1
5 10 n & 1, 000 1 & gondolaSeq i & 5, 000
6 20 n & 100, 000 1 & gondolaSeq i & 250, 000

Exemple

subproblemă gondolaSeq valoare returnată replacementSeq


4 (3, 1, 4) 1 (2)
4 (5, 1, 2, 3, 4) 0 ()
5 (2, 3, 4, 9, 6, 7, 1) 2 (5, 8)

Numărarea secvenţelor de ı̂nlocuiri


Pentru următoarele patru subprobleme trebuie să determinaţi numărul de secvenţe posibile
de ı̂nlocuiri care produc o secvenţă (care poate sau nu să fie o secvenţă de gondole), modulo
1,000,000,009.
Trebuie să implementaţi funcţia countReplacement.
ˆ countReplacement n, inputSeq 
– n: lungimea secvenţei date.
– inputSeq: tablou unidimensional de lungime n; inputSeq i este elementul cu numărul
i din secvenţa dată, pentru 0 & i & n  1.
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 724

– Dacă secvenţa dată este o secvenţă de gondole, atunci trebuie să determinaţi numărul
(care poate fi foarte mare) de secvenţe de ı̂nlocuiri posibile care produc această secvenţă,
şi să returnaţi acest număr modulo 1,000,000,009. Dacă secvenţa dată nu este o secvenţă
de gondole, funcţia trebuie să returneze 0. Dacă secvenţa dată este o secvenţă de
gondole dar nicio gondolă nu s-a stricat, funcţia trebuie să retuneze 1.

Subproblemele 7, 8, 9, 10

subproblemă puncte n inputSeq


7 5 4 & n & 50 1 & inputSeq i & n  3
8 15 4 & n & 50 1 & inputSeq i & 100, şi cel puţin n  3 din
gondolele iniţiale 1, ..., n nu s-au stricat.
9 15 n & 100, 000 1 & inputSeq i & 250, 000
10 10 n & 100, 000 1 & inputSeq i & 1, 000, 000

Exemple

subproblemă inputSeq valoare secvenţă de ı̂nlocuiri


returnată
7 (1, 2, 7, 6) 2 (3, 4, 5) sau (4, 5, 3)
8 (2, 3, 4, 12, 6, 7, 1) 1 (5, 8, 9, 10, 11)
9 (4, 7, 4, 7) 0 inputSeq nu este o secvenţă de gondole
10 (3, 4) 2 (1, 2) or (2, 1)

Detalii de implementare
Trebuie să ı̂ncărcaţi exact un fişier, numit gondola.c, gondola.cpp sau gondola.pas. Acest
fişier trebuie să implementeze toate cele trei subprograme descrise mai sus (chiar dacă plănuiţi
să rezolvaţi doar unele din subprobleme), folosing următoarele antete. De asemenea trebuie să
includeţi fişierul header gondola.h pentru implementări C/C++.
pentru programe C/C++

int valid(int n, int inputSeq[]);


int replacement(int n, int gondolaSeq[], int replacementSeq[]);
int countReplacement(int n, int inputSeq[]);

pentru programe Pascal

function valid(n: longint; inputSeq: array of longint): integer;


function replacement(n: longint; gondolaSeq: array of longint;
var replacementSeq: array of longint): longint;
function countReplacement(n: longint; inputSeq: array of longint):
longint;

Grader-ul de pe computerul vostru


Graderul de pe computerul vostru citeşte datele de intrare ı̂n următorul format:
ˆ linia 1: T , subproblema pe care programul vostru trebuie să o rezolve (1 & T & 10).
ˆ linia 2: n, lungimea secvenţei date.
ˆ linia 3: Dacă T este 4, 5, sau 6, această linie conţine gondolaSeq 0, ..., gondolaSeq n  1.
În caz contrar această linie conţine inputSeq 0, ..., inputSeq n  1.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 256 MB
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 725

6.5.1 Indicaţii de rezolvare

Overview
This is an easy problem. Even though it might seem that there are three separate tasks (as
indicated by the grouping of subtasks), the task is actually incremental. The three parts (check
whether there is a solution - find one solution - count all solutions) are closely related.
Subtasks 1-3
In subtask 1, once we see the first gondola (i.e., inputSeq 0), the rest is uniquely determined.
We just need to iterate through the sequence and check whether everything matches.

ˆ N = len(sequence)
ˆ for n:=1..N: if sequence[n] % N != (sequence[0]+n) % N: return False
ˆ return True

The same code will actually solve subtask 2.


The pseudocode for the general subtask 3 is:

ˆ If there exist one of the original gondolas: check whether the other original gondolas are in
the expected places, if not, return false.
ˆ Return true if all the values are distinct, false otherwise.

Subtasks 4-6
A simple solution for subtask 4: if the largest number in the sequence is n, terminate, otherwise
output the only missing number and terminate.
Solutions for subtasks 5 and 6 share the same idea, the difference is that subtask 4 allows its
inefficient implementations. There are many possible solutions. Here is one of them.

ˆ Collect all non-original gondolas. For each of them determine the original gondola it replaced.
ˆ Sort these records according to the new gondola number.
ˆ In sorted order, replace the original gondolas by new ones until the expected numbers are
reached.

Note that the case where no original gondolas are present may require special attention - not
just for the contestants, but also in the grader for these subtasks.
Subtasks 7-10
Counting the repair sequences directly is hard. One way of doing it is by asking the question:
How many repair sequences start with gondola x being replaced? for each x. Each choice of x
leads us to a new state with one fewer gondolas to replace.
Using this idea we can now solve subtask 8 by dynamic programming: for each admissible state
of the lift we compute the number of ways in which it can be solved.
Subtasks 9 and 10 require one additional insight. (This is, probably, the only tricky part of
this problem, and the insight needed is not too hard.)
Instead of looking at the old gondola that is being removed, we will simply look at the new
gondola that is being added. How many different options do we have for its place? If the new
gondola is present in the final sequence, its place is uniquely determined. Otherwise, the number
of places where this new gondola can be added is simply the number of places that end up having
a gondola with a larger number.
Additionally, we need to multiply the result by n if none of the original gondolas is present.
(All n cyclic rotations of the original sequence are now possible, and the repair sequences for
different rotations are necessarily distinct.) We outline the algorithm as follows:

ˆ Run the algorithm for subtasks 1-3 to check whether the input sequence is valid or not.
ˆ If valid, for each replaced gondola we find the original gondola it ultimately replaced, and
we sort these records according to the new gondola number.
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 726

ˆ The total number of possibilities can now be computed by multiplying the number of possible
locations for each gondola between n+1 and the largest replaced gondola number present.
Note the multiplicative operation is modular here.
ˆ Finally, multiply by n if all original gondolas are replaced.

6.5.2 Coduri sursă

Listing 6.5.1: gondola-7306.cpp


// https://oj.uz/submission/7306 24 ms 4412 KB
#include <stdio.h>
#include <assert.h>

#include<ctime>
#include<iostream>

#include "gondola.h"

int gondolaSequence[100001];
int replacementSequence[250001];

#include <algorithm>

#define MOD 1000000009

int arr[250001];
int now[250001];

int valid(int n, int inputSeq[])


{
int i,j;
for(i=0;i<n;i++)if(inputSeq[i]<=n)break;
if(i<n)
for(j=0;j<n;j++)
if(inputSeq[j]<=n&&(inputSeq[j]+i)%n!=(inputSeq[i]+j)%n)
return 0;

std::sort(inputSeq,inputSeq+n);

for(i=1;i<n;i++)
if(inputSeq[i-1]==inputSeq[i])
return 0;
return 1;
}

int replacement(int n, int gondolaSeq[], int replacementSeq[])


{
int i,j;
for(i=0;i<n;i++)arr[gondolaSeq[i]]=i+1;
for(i=0;i<n;i++)if(gondolaSeq[i]<=n)break;
for(j=0;j<n;j++)now[j]=i<n?(gondolaSeq[i]+j-i+n-1)%n+1:j+1;

j=0;
for(i=0;i<n;i++)if(gondolaSeq[i]>j)j=gondolaSeq[i];
for(i=n+1;i<=j;i++)
{
replacementSeq[i-n-1]=arr[i]>0?now[arr[i]-1]:now[arr[j]-1];
now[arr[i]>0?arr[i]-1:arr[j]-1]=i;
}

return j-n;
}

int f(int x,int y)


{
if(y==0||x==1)return 1;
if(y&1)return 1LL*x*f(x,y-1)%MOD;
x=f(x,y>>1);
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 727

return 1LL*x*x%MOD;
}

int countReplacement(int n, int inputSeq[])


{
if(!valid(n,inputSeq))return 0;
int i,r=1;

std::sort(inputSeq,inputSeq+n);

for(i=0;i<n;i++)
{
if(inputSeq[i]<=n)continue;
r=1LL*r*f(n-i,inputSeq[i]-(i>0?std::max(inputSeq[i-1],n):n)-1)%MOD;
}

return 1LL*r*(inputSeq[0]>n?n:1)%MOD;
}

// -------------- begin grader ------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/10-018.in", "r", stdin) ;


std::freopen("gondola.out", "w", stdout) ;

int i, n, tag;
int nr;

assert(scanf("%d", &tag)==1);
assert(scanf("%d", &n)==1);

for(i=0;i< n;i++)
assert( scanf("%d", &gondolaSequence[i]) ==1);

auto t2 = clock();

switch (tag)
{
case 1: case 2: case 3:
printf("%d\n", valid(n, gondolaSequence));
break;

case 4: case 5: case 6:


nr = replacement(n, gondolaSequence, replacementSequence);
printf("%d ", nr);
if (nr > 0)
{
for (i=0; i<nr-1; i++)
printf("%d ", replacementSequence[i]);
printf("%d\n", replacementSequence[nr-1]);
}
else printf("\n");
break;

case 7: case 8: case 9: case 10:


printf("%d\n", countReplacement(n, gondolaSequence));
break;
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 728

}
// -------------- end grader --------------------
/*
t2-t1 = 0.016
t3-t2 = 0.031
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.5.2: gondola-93790map.cpp


// https://oj.uz/submission/93790 101 ms 10892 KB

#include "gondola.h"
#include <iostream>
#include <map>
#include <algorithm>

#include <stdio.h>
#include <assert.h>

#include<ctime>

int gondolaSequence[100001];
int replacementSequence[250001];

using namespace std;

const int nmax=100005;


const long long mod=1000*1000*1000+9;

map<int,int> ap;

int v[nmax];
int i,act;

void permuta(int a[],int b[],int n)


{
int offset=0;
for(int i=0;i<n;i++)
if(a[i]<=n)
offset=i+1-a[i];
for(int i=0;i<n;i++)
b[i]=a[(i+offset+n)%n];
}

int valid(int n, int inputSeq[])


{
permuta(inputSeq,v,n);
for(int i=0;i<n;i++)
{
if(v[i]<=n&&v[i]!=i+1)
return 0;
if(ap[v[i]]) return 0;
ap[v[i]]=1;
}

return 1;
}

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

int replacement(int n, int gondolaSeq[], int replacementSeq[])


{
int l=0;
permuta(gondolaSeq,v,n);
int mx=0;
for(i=0;i<n;i++)
{
ap[v[i]]=i+1;
mx=max(mx,v[i]);
}
act=n;
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 729

for(i=n+1;i<=mx;i++)
if(ap[i])
{
replacementSeq[l++]=ap[i];
act++;
while(act<i)
{
replacementSeq[l++]=act;
act++;
}
}
return l;
}

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

long long expo(long long A,int B)


{
long long ret=1,p2=A;
for(int p=0;p<=30;p++)
{
if(((1<<p)&B))
ret=(1LL*ret*p2)%mod;
p2=(1LL*p2*p2)%mod;
}
return ret;
}

int countReplacement(int n, int inputSeq[])


{
long long f=0,ans=1;
if(!valid(n,inputSeq)) return 0;
bool ordered=1;
for(int i=1;i<=n;i++)
if(ap[i])
ordered=0;

sort(inputSeq,inputSeq+n);

for(i=n-2;i>=0;i--)
if(inputSeq[i]>n||inputSeq[i+1]>n)
{
f=n-1-i;if(inputSeq[i]<n) inputSeq[i]=n;
ans=(1LL*ans*expo(f,inputSeq[i+1]-inputSeq[i]-1))%mod;
}

if(ordered)
{
f=n;
ans=(1LL*ans*expo(f,inputSeq[0]-n-1))%mod;
f=n;
ans=(1LL*ans*f)%mod;
}
return ans;
}

// -------------- begin grader ------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/10-018.in", "r", stdin) ;


std::freopen("gondola.out", "w", stdout) ;

int i, n, tag;
int nr;

assert(scanf("%d", &tag)==1);
assert(scanf("%d", &n)==1);

for(i=0;i< n;i++)
assert( scanf("%d", &gondolaSequence[i]) ==1);

auto t2 = clock();
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 730

switch (tag)
{
case 1: case 2: case 3:
printf("%d\n", valid(n, gondolaSequence));
break;

case 4: case 5: case 6:


nr = replacement(n, gondolaSequence, replacementSequence);
printf("%d ", nr);
if (nr > 0)
{
for (i=0; i<nr-1; i++)
printf("%d ", replacementSequence[i]);
printf("%d\n", replacementSequence[nr-1]);
}
else printf("\n");
break;

case 7: case 8: case 9: case 10:


printf("%d\n", countReplacement(n, gondolaSequence));
break;
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader --------------------
/*
t2-t1 = 0.124
t3-t2 = 0.188
t4-t3 = 0.016

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


Press any key to continue.
*/

Listing 6.5.3: gondola-102776.cpp


// https://oj.uz/submission/102776 31 ms 2204 KB

int gondolaSequence[100001];
int replacementSequence[250001];

#include <bits/stdc++.h>
#include "gondola.h"

using namespace std;

const int md = (int) 1e9 + 9;

inline int mul(int x, int y)


{
return (int) ((long long) x * y % md);
}

inline int power(int x, int y)


{
int res = 1;
while (y)
{
if (y & 1)
{
res = mul(res, x);
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 731

}
x = mul(x, x);
y >>= 1;
}
return res;
}

int valid(int n, int inputSeq[])


{
vector<int> a(inputSeq, inputSeq + n);
sort(a.begin(), a.end());
if (unique(a.begin(), a.end()) != a.end())
{
return 0;
}

int p = min_element(inputSeq, inputSeq + n) - inputSeq;


for (int i = 0; i < n; ++i)
{
if (inputSeq[(p + i) % n] <= n &&
inputSeq[(p + i) % n] != inputSeq[p] + i)
{
return 0;
}
}

return 1;
}

int replacement(int n, int gondolaSeq[], int replacementSeq[])


{
int q = min_element(gondolaSeq, gondolaSeq + n) - gondolaSeq;
if (gondolaSeq[q] <= n)
{
q = (q - (gondolaSeq[q] - 1) + n) % n;
}

int p = max_element(gondolaSeq, gondolaSeq + n) - gondolaSeq;


int m = gondolaSeq[p];

vector<int> pos(m + 1, -1);


for (int i = 0; i < n; ++i)
{
pos[gondolaSeq[i]] = (i - q + n) % n + 1;
}

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


{
if (pos[i] != -1)
{
replacementSeq[i - n - 1] = pos[i];
}
else
{
replacementSeq[i - n - 1] = pos[m];
pos[m] = i;
}
}

return m - n;
}

int countReplacement(int n, int inputSeq[])


{
if (!valid(n, inputSeq))
{
return 0;
}

sort(inputSeq, inputSeq + n);

int ans = 1;
if (inputSeq[0] > n)
{
ans = n;
}
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 732

for (int i = 0; i < n; ++i)


{
if (inputSeq[i] > n)
{
ans = mul(ans,
power(n - i,
inputSeq[i]-max(n, i ? inputSeq[i-1] : n) - 1));
}
}

return ans;
}

// -------------- begin grader ------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/10-018.in", "r", stdin) ;


std::freopen("gondola.out", "w", stdout) ;

int i, n, tag;
int nr;

assert(scanf("%d", &tag)==1);
assert(scanf("%d", &n)==1);

for(i=0;i< n;i++)
assert( scanf("%d", &gondolaSequence[i]) ==1);

auto t2 = clock();

switch (tag)
{
case 1: case 2: case 3:
printf("%d\n", valid(n, gondolaSequence));
break;

case 4: case 5: case 6:


nr = replacement(n, gondolaSequence, replacementSequence);
printf("%d ", nr);
if (nr > 0)
{
for (i=0; i<nr-1; i++)
printf("%d ", replacementSequence[i]);
printf("%d\n", replacementSequence[nr-1]);
}
else printf("\n");
break;

case 7: case 8: case 9: case 10:


printf("%d\n", countReplacement(n, gondolaSequence));
break;
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader --------------------
/*
t2-t1 = 0.063
t3-t2 = 0.046
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 733

t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.5.4: gondola-153281.cpp


// https://oj.uz/submission/153281 37 ms 2552 KB

int gondolaSequence[100001];
int replacementSequence[250001];

#include "gondola.h"
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

const int MAXN = 1e5;


const ll MOD = 1e9+9;

int valid(int N, int *A)


{
int i, j;

vector<int> S(A, A+N);

sort(S.begin(), S.end());

S.erase(unique(S.begin(), S.end()), S.end());

if(S.size()!=N) return 0;

int flag=-1;
for(i=0; i<N; i++)
{
if(A[i]>N) continue;
int t=(A[i]-1-i+N)%N;

if(flag==-1) flag=t;
else
if(flag!=t) return 0;
}

return 1;
}

int replacement(int N, int *A, int *B)


{
int i, j, ret=0;

int flag=0;
vector<pii> V;
for(i=0; i<N; i++)
{
if(A[i]<=N)
{
flag=(A[i]-1-i+N)%N;
continue;
}
V.push_back({A[i], i});
}

for(i=0; i<N; i++) A[i]=(i+flag)%N+1;

sort(V.begin(), V.end());

int now=N+1;
for(i=0; i<V.size(); i++)
{
int p=V[i].first, q=V[i].second;
CAPITOLUL 6. IOI 2014 6.5. GONDOLA 734

while(A[q]!=p)
{
B[ret++]=A[q];
A[q]=now++;
}
}
return ret;
}

ll mypow(ll x, ll y)
{
if(y==0) return 1;

if(y%2) return mypow(x, y-1)*x%MOD;

ll ret=mypow(x, y/2);
return ret*ret%MOD;
}

int countReplacement(int N, int *A)


{
int i, j; ll ans=1;
if(!valid(N, A)) return 0;

int flag=-1;
vector<int> V; V.push_back(N);
for(i=0; i<N; i++)
{
if(A[i]<=N) flag=0;
else V.push_back(A[i]);
}

sort(V.begin(), V.end());
for(i=1; i<V.size(); i++)
ans=ans*mypow(V.size()-i, V[i]-V[i-1]-1)%MOD;

if(flag==-1) ans=ans*N%MOD;
return ans;
}

// -------------- begin grader ------------------

int main()
{
auto t1 = clock();

std::freopen("../tests/10-018.in", "r", stdin) ;


std::freopen("gondola.out", "w", stdout) ;

int i, n, tag;
int nr;

assert(scanf("%d", &tag)==1);
assert(scanf("%d", &n)==1);

for(i=0;i< n;i++)
assert( scanf("%d", &gondolaSequence[i]) ==1);

auto t2 = clock();

switch (tag)
{
case 1: case 2: case 3:
printf("%d\n", valid(n, gondolaSequence));
break;

case 4: case 5: case 6:


nr = replacement(n, gondolaSequence, replacementSequence);
printf("%d ", nr);
if (nr > 0)
{
for (i=0; i<nr-1; i++)
printf("%d ", replacementSequence[i]);
printf("%d\n", replacementSequence[nr-1]);
}
else printf("\n");
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 735

break;

case 7: case 8: case 9: case 10:


printf("%d\n", countReplacement(n, gondolaSequence));
break;
}

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// -------------- end grader --------------------
/*
t2-t1 = 0.078
t3-t2 = 0.062
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.5.5: checkerGondola.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/10-018.in",
(char*)"gondola.out",
(char*)"../tests/10-018.out",
};

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

registerChecker("gondola", argc, argv);


compareRemainingLines();
}

6.5.3 *Rezolvare detaliată

6.6 Holiday
Problema 6 - Holiday 100 de puncte

Author: Jakub Lacki, Poland


CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 736

Jian-Jia planifică să-şi petreacă următoarea vacanţă ı̂n Taiwan. Pe durata vacanţei, Jian-Jia
va călători dintr-un oraş ı̂n altul şi va vizita atracţiile oraşelor.
Există n oraşe ı̂n Taiwan, toate situate de-a lungul unei singure autostrăzi. Oraşele sunt
numerotate consecutiv de la 0 la n. Pentru oricare oraş i, unde 0 $ i $ n  1, oraşele adiacente
sunt i  1 şi i  1. Oraşul 0 este adiacent doar cu oraşul 1, iar oraşul n  1 este adiacent doar cu
oraşul n  2.
Fiecare oraş conţine un anumit număr de atracţii. Jian-Jia are d zile de vacanţă şi intenţionează
să viziteze cat mai multe atracţii posibile. Jian-Jia a ales deja oraşul din care ı̂şi va ı̂ncepe
vacanţa. ı̂n fiecare zi de vacanţă, Jian-Jia poate călători ı̂n unul din oraşele adiacente ori vizita
toate atracţiile din oraşul ı̂n care se află, dar nu poate face ambele. Jian-Jia nu va vizita niciodată
de mai multe ori atracţiile dintr-un oraş chiar dacă vizitează acelaşi oraş de mai multe ori. Vă
rugăm să-l ajutaţi pe Jian-Jia să-şi planifice vacanţa astfel ı̂ncât să viziteze cât mai multe atracţii
posibile.
Exemplu
Să presupunem că avem 5 oraşe (listate ı̂n tabelul de mai jos), iar Jian-Jia are o vacanţă de
7 zile pe care o ı̂ncepe din oraşul 2. ı̂n prima zi, Jian-Jia va vizita cele 20 de atracţii din oraşul
2. ı̂n a doua zi Jian-Jia călătoreşte din oraşul 2 ı̂n oraşul 3, iar ı̂n a treia zi vizitează cele 30 de
atracţii din oraşul 3. Apoi, Jian-Jia călătoreşte următoarele trei zile de la oraşul 3 la oraşul 0, şi
vizitează cele 10 atracţii din oraşul 0 ı̂n cea de-a şaptea zi. Numărul total de atracţii pe care le
va vizita Jian-Jia este 20 + 30 + 10 = 60, care reprezintă numărul maxim de atracţiii pe care le
poate vizita Jian-Jia ı̂n cele 7 zile de vacanţă, ı̂n cazul ı̂n care primul oraş vizitat este 2.

oraş număr de atracţii


0 10
1 2
2 20
3 30
4 1

zi acţiune
1 vizitează atracţiile din oraşul 2
2 călătoreşte de la oraşul 2 la oraşul 3
3 vizitează atracţiile din oraşul 3
4 călătoreşte de la oraşul 3 la oraşul 2
5 călătoreşte de la oraşul 2 la oraşul 1
6 călătoreşte de la oraşul 1 la oraşul 0
7 vizitează atracţiile din oraşul 0

Cerinţă
Trebuie să implementaţi funcţia f indM axAttraction care calculează numărul maxim de
atracţii pe care Jian-Jia le poate vizita.
ˆ f indM axAttraction n, start, d, attraction
– n: numărul de oraşe.
– start: indicele primului oraş vizitat.
– d: numărul de zile.
– attraction: un tablou unidimensional de lungime n; attractioni este numărul de
atracţii din oraşul i, pentru 0 & i & n  1.
– Funcţia trebuie să returneze numărul maxim de atracţii pe care Jian-Jia le poate vizita.

Subprobleme
În toate subproblemele 0 & d & 2n  n©2$ şi numărul de atracţii din fiecare oraş este nenegativ.
Constrângeri adiţionale:
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 737

subproblemă puncte n numărul maxim de primul oraş vizitat


atracţii dintr-un oraş
1 7 2 & n & 20 1,000,000,000 fără constrângeri
2 23 2 & n & 100, 000 100 oraşul 0
3 17 2 & n & 3, 000 1,000,000,000 fără constrângeri
4 53 2 & n & 100, 000 1,000,000,000 fără constrângeri

Detalii de implementare
Trebuie să ı̂ncărcaţi un singur fişier, cu numele holiday.c, holiday.cpp sau holiday.pas.
Acest fişier va conţine un subprogram care implementează subproblemele descrise mai sus
folosind următorul antet. De asemenea trebuie să includeţi headerul holiday.h pentru imple-
mentările C/C++.
Atenţie: rezultatul poate fi un număr mare şi tipul valorii returnate de funcţia
f indM axAttraction este un ı̂ntreg pe 64-biţi.
pentru programele C/C++

long long int findMaxAttraction(int n, int start, int d,


int attraction[]);

pentru programele Pascal

function findMaxAttraction(n, start, d : longint;


attraction : array of longint): int64;

Grader-ul de pe computerul vostru


Grader-ul de pe computerul vostru citeşte datele de intrare ı̂n următorul format:
ˆ linia 1: n, start, d.
ˆ linia 2: attraction0, ..., attractionn  1.
Grader-ul de pe computerul vostru va afişa valoarea returnată de funcţia f indM axAttraction.
Timp maxim de executare/test: 5.0 secunde
Memorie: total 64 MB

6.6.1 Indicaţii de rezolvare

For ease of description, we will first describe a simple O n log n-time solution for the special case
of the starting city start being the one with the index 0 for any fixed d. That is, start 0 and d
2
is a given number. Then we extend this solution to build a table of solutions in O n log n time
for all possible values of d. Finally we describe how to extend this solution to solve the general
case of an arbitrary start with the same asymptotic time complexity.
Simple solution for start 0 and a given fixed d
Without lost of generality, assume we start at the leftmost city and move right. It is easy to
see that we only need to move right and there is no need to move left at any time.
Assume in an optimal solution, city right is the rightmost city we will travel to. Then we can
visit up to d  right cities among the cities with labels 0, 1, ..., right. In order for the solution to
be optimal, we want to visit the d  right cities with the largest number of attractions. That is,
if we sort the cities with labels 0, 1, ..., right using the number of attractions as their keys, then
we want to know the sum of the d  right largest number of attractions.
Segment tree We use a data structure called segment tree for this part though it may appear
this data structure is not needed to solve this very special case. However, it will be clear why this
data structure is used in the solution to the intermediate case. The segment tree data structure
has been used in previous IOI contests including 2001 Baltic OI.
The segment tree has many variations. We will use the following one. A segment tree is a
rooted complete binary tree with leaves carrying a flag indicating whether this leaf is active or not,
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 738

and a value. For each internal node v, it keeps the sum of the values of all of the active leaves in
the subtree rooted at v. Each internal node also maintains the number of currently active leaves
in the subtree rooted at v.
Assume the segment tree has n leaves. Note that we need to add dummy leaves of n is not
a power of 2. Further assume the values of the leaves, active or in-active, are in non-increasing
order from left to right. To maintain this data structure, it takes O log n time to turn on or off
any leaf. It also takes O log n time to find out the sum of the values in the largest x active leaves
for any given x. A side note is when x is more than the number of active leaves, then we simply
output the sum of the values of all active leaves.
Algorithm Initially, we sort the cities using their number of attractions as keys in non-
increasing order. Then in this order, we place them as leaves in the segment tree from left to
right with all leaves in-active. The number of attractions are now the values of the leaves. The
initialization phase takes O n log n time. We turn on a leaf when it is being move to during the
search of our solution. We iterate on all possible values of the rightmost city we can move to.
Hence it takes a total of O n log n time to find a solution for this easy special case.
Intermediate solution for start 0 and all possible values of d
Now we describe how to solve this intermediate case. In our previous solution, we can find the
maximum number of attractions we can visit given any d.
Let f d be the label of the city we move to in d days so that the maximum number of
attractions can be found.
Note that f d may not be unique. In the case of multiple ones, we pick the one with the
smallest label. We now want to build a table for all possible values of d.
The idea is to use recursive divide-and-conquer approach.
Let M be the maximum number of d. For ease of description, let M be a power of 2.
To compute the solutions for f 1, f 2, ..., f M  we first find f M ©2 using our pre-
vious algorithm by iterating through all cities from 0 to n and then recursively compute
f 1, f 2, ..., f M ©2  1 in one branch by considering only cities from 0 to f M ©2, and
f M ©2  1, f M ©2  2, ..., f M  in the other branch by considering only cities from f M ©2 to
n.
In the branch of computing f 1, f 2, ..., f M ©2  1, we first compute f M ©4 among cities
0 to f M ©2. In general, the total amount of time spent in each level of recursive calls takes a
total of O n log n. There are a total of O log n levels. Hence the overall time complexity is
2
O n log n.
In solving this intermediate case, it is now clear how the segment tree is useful. First we only
need to do the initialization once. Secondly, we can easily turn on or off a leaf to accommodate
the fact that the cities that we pay attention to in each level of recursion. For example, only half
of the leaves are active during the second level of recursion.
General solution for an arbitrary value of start
Now we are ready to show the general solution. Observe the fact that when the value of start
is arbitrary, then the solution can be found in either one of the following 2 cases.
We first move right to a city, then move left from this point, and then finally stop at a city
left of start. Or we first move left to a city, then move right from this point, and then finally stop
at a city right of start. That is, we only change the direction once.
Without lost of generality, we assume the first scenario. We first use the immediately solution
to find f t for all possible of values t. Then we also use the immediately solution to find g t for
all possible values of t where g t is the city we will stop in an optimal solution if we only move
left and the starting city is start  1.
We first iterate on d0 the days we want to spend on moving and visiting cities to the right of,
and including, start.
Using the solution to the intermediate case, we know f d0 .
Then we know we can spend d  d0  f d0   start  1 days on the cities to the left of start.
2
Hence the overall time complexity is O n log n.

6.6.2 Coduri sursă


CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 739

Listing 6.6.1: holiday-120785.cpp


// https://oj.uz/submission/120785 306 ms 45448 KB

#include "holiday.h"
#include <bits/stdc++.h>

#define upmax(a,b) (a)=max((a),(b))


#define INFLL (0x3f3f3f3f3f3f3f3fll)

using namespace std;

typedef long long ll;

const int MAXN = 100055;

struct NOD
{
NOD() : sum(0), cnt(0), l(0), r(0) {}

ll sum;
int cnt, l, r;
} arr[MAXN*18]; int arrn;

int newNOD()
{
return ++arrn;
}

int pst[MAXN];

int A[MAXN], AO[MAXN], ARO[MAXN];

ll Ans;
int N, X, L;

void makeTree(int bf, int nw, int s, int e, int x, int r)


{
arr[nw].cnt = arr[bf].cnt + 1;
arr[nw].sum = arr[bf].sum + r;

if(s == e) return;

int m = (s+e) >> 1;


if(x <= m)
{
arr[nw].r = arr[bf].r;
arr[nw].l = newNOD();
makeTree(arr[bf].l, arr[nw].l, s, m, x, r);
}
else
{
arr[nw].l = arr[bf].l;
arr[nw].r = newNOD();
makeTree(arr[bf].r, arr[nw].r, m+1, e, x, r);
}
}

ll _get(int bf, int nw, int x)


{
if(!x) return 0;
if(arr[nw].cnt - arr[bf].cnt == x)
return arr[nw].sum - arr[bf].sum;
int lc = arr[arr[nw].l].cnt - arr[arr[bf].l].cnt;
if(x <= lc)
return _get(arr[bf].l, arr[nw].l, x);

return (arr[arr[nw].l].sum - arr[arr[bf].l].sum) +


_get(arr[bf].r, arr[nw].r, x-lc);
}

ll get(int s, int e, int x)


{
return _get(pst[s-1], pst[e], x);
}
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 740

void f(int s, int e, int p, int q)


{
if(s > e || p > q) return;
int m = (s+e) >> 1;
ll hc = -INFLL; int hi = -1;

for(int x = p, xe = min({N, (L+X+m)/2, q}); x <= xe; x++)


{
ll t = get(m, x, min(x-m+1, L-(x+x-m-X)));
if(t <= hc) continue;
hc = t;
hi = x;
}

upmax(Ans, hc);
f(s, m-1, p, hi);
f(m+1, e, hi, q);
}

ll getAns()
{
iota(AO, AO+N+1, 0);
sort(AO+1, AO+N+1, [&](int a, int b)
{
return A[a] > A[b];
});

for(int i = 1; i <= N; i++) ARO[AO[i]] = i;

for(int i = 1; i <= N; i++)


{
pst[i] = newNOD();
makeTree(pst[i-1], pst[i], 1, N, ARO[i], A[i]);
}

f(max(1, X-L), X, X, N);

arrn = 0;

X = N+1-X;
reverse(A+1, A+N+1);
iota(AO, AO+N+1, 0);
sort(AO+1, AO+N+1, [&](int a, int b) {return A[a] > A[b];});

for(int i = 1; i <= N; i++) ARO[AO[i]] = i;


for(int i = 1; i <= N; i++)
{
pst[i] = newNOD();
makeTree(pst[i-1], pst[i], 1, N, ARO[i], A[i]);
}

f(max(1, X-L), X, X, N);

return Ans;
}

long long int findMaxAttraction(int n, int start, int d, int attraction[])


{
::N = n;
::X = start + 1;
::L = d;

for(int i = 0; i < n; i++)


::A[i+1] = attraction[i];

return getAns();
}

// ----------- begin grader -------------

int main()
{
auto t1 = clock();

std::freopen("../tests/04-009.in", "r", stdin) ;


CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 741

std::freopen("holiday.out", "w", stdout) ;

int n, start, d;
int attraction[100000];
int i, n_s;

n_s = scanf("%d %d %d", &n, &start, &d);


for (i = 0 ; i < n; ++i)
{
n_s = scanf("%d", &attraction[i]);
}

auto t2 = clock();

printf("%lld\n", findMaxAttraction(n, start, d, attraction));

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------- end grader ----------------


/*
t2-t1 = 0.266
t3-t2 = 1.031
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.6.2: holiday-134641.cpp


// https://oj.uz/submission/134641 259 ms 49520 KB

#include "holiday.h"
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 2;

struct st
{
int l, r, cnt;
long long sum;
st()
{
l = r = cnt = sum = 0;
}
} t[N * 20];

int d, start, sz;


long long ans;
vector <int> vec;
int root[N], cn;

void upd(int ov, int v, int pos, int tl = 0, int tr = sz)


{
if(tl == tr)
{
t[v].sum = t[ov].sum + vec[tl];
t[v].cnt = t[ov].cnt + 1;
}
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 742

else
{
int tm = (tl + tr) >> 1;
if(pos <= tm)
{
if(!t[v].l) t[v].l = ++ cn;
t[v].r = t[ov].r;
upd(t[ov].l, t[v].l, pos, tl, tm);
}
else
{
if(!t[v].r) t[v].r = ++ cn;
t[v].l = t[ov].l;
upd(t[ov].r, t[v].r, pos, tm + 1, tr);
}

t[v].cnt = t[ t[v].l ].cnt + t[ t[v].r ].cnt;


t[v].sum = t[ t[v].l ].sum + t[ t[v].r ].sum;
}
}

long long get(int ov, int v, int cnt, int tl = 0, int tr = sz)
{
if(cnt <= 0) return 0ll;
if(tl == tr)
{
long long k = min(t[v].cnt - t[ov].cnt, cnt);
return k * 1ll * vec[tl];
}

int tm = (tl + tr) >> 1;


int k = t[ t[v].r ].cnt - t[ t[ov].r ].cnt;
if(k <= cnt)
return t[ t[v].r ].sum -
t[ t[ov].r ].sum +
get(t[ov].l, t[v].l, cnt - k, tl, tm);

return get(t[ov].r, t[v].r, cnt, tm + 1, tr);


}

void calc(int l, int r, int opl, int opr)


{
if(l > r) return ;
int md = (l + r) >> 1, opt = -1;
long long ret = -1;
for(int i = opl; i <= opr; i ++)
{
int lenA = (start - md), lenB = (i - start);
int cnt = d - lenA - lenB - min(lenA, lenB);
long long now = get(root[md], root[i + 1], cnt);
if(ret < now)
{
ret = now;
opt = i;
}
}

ans = max(ans, ret);


calc(l, md - 1, opl, opt);
calc(md + 1, r, opt, opr);
}

long long int findMaxAttraction(int n, int startS, int D, int ar[])


{
d = D;
start = startS;
vec.push_back(-2e9);
for(int i = 0; i < n; i ++)
{
vec.push_back(ar[i]);
}

sort(vec.begin(),vec.end());

vec.resize(unique(vec.begin(), vec.end()) - vec.begin());


CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 743

sz = (int)vec.size() - 1;
for(int i = 0; i < n; i ++)
{
ar[i] = lower_bound(vec.begin(),vec.end(), ar[i])-vec.begin();
}

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


{
root[i] = ++ cn;
upd(root[i - 1], root[i], ar[i - 1]);
}

calc(0, start, start, n);

return ans;
}

// ----------- begin grader -------------

int main()
{
auto t1 = clock();

std::freopen("../tests/04-009.in", "r", stdin) ;


std::freopen("holiday.out", "w", stdout) ;

int n, start, d;
int attraction[100000];
int i, n_s;

n_s = scanf("%d %d %d", &n, &start, &d);


for (i = 0 ; i < n; ++i)
{
n_s = scanf("%d", &attraction[i]);
}

auto t2 = clock();

printf("%lld\n", findMaxAttraction(n, start, d, attraction));

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------- end grader ----------------


/*
t2-t1 = 0.187
t3-t2 = 0.938
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.6.3: holiday-211797.cpp


// https://oj.uz/submission/211797 250 ms 43776 KB

#include <bits/stdc++.h>
#include "holiday.h"

using namespace std;


CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 744

using ll=long long;

struct pst
{
struct T
{
ll x=0;
int cnt=0, l=0, r=0;
};

int N, id=0, td=0;


vector<T> A;
vector<int> D, X;

pst(vector<int>&& _): N(_.size()), A(1800000), D(N+1), X(move(_)) {}

void add(int x, int s, int e, int&i)


{
int p=i;
i=++id;
A[i]={A[p].x+X[x], A[p].cnt+1, A[p].l, A[p].r};
if(s==e) return;

int m=s+e>>1;
if(x<=m) add(x, s, m, A[i].l);
else add(x, m+1, e, A[i].r);
}

void add(int x)
{
td++;
add(x, 0, N-1, D[td]=D[td-1]);
}

ll sum(int k, int s, int e, int i, int j)


{
if(A[j].cnt-A[i].cnt<=k) return A[j].x-A[i].x;
if(s==e) return X[s]*ll(k);

int m=s+e>>1, n=A[A[j].r].cnt-A[A[i].r].cnt;


if(k<=n)
return sum(k, m+1, e, A[i].r, A[j].r);
else
return sum(k-n, s, m, A[i].l, A[j].l)+A[A[j].r].x-A[A[i].r].x;
}

ll sum(int l, int r, int k)


{
return sum(k, 0, N-1, D[l], D[r+1]);
}
};

ll findMaxAttraction(int N, int st, int M, int A[])


{
vector<int> X(A, A+N);

sort(begin(X), end(X));

for(int i=0; i<N; i++)


A[i]=lower_bound(begin(X), end(X), A[i])-begin(X);

pst t(move(X));
for(int i=0; i<N; i++) t.add(A[i]);

function <ll(int, int, int, int)>


f=[&](int s, int e, int l, int r)
{
if(s>e || l>r) return ll(0);

int m=s+e>>1;
pair<ll, int> n={-1, -1};

for(int i=l; i<=r; i++)


n=max(n, {t.sum(m, i, M+m-i-min(i-st, st-m)), i});

return max({n.first,
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 745

f(s, m-1, l, n.second),


f(m+1, e, n.second, r)});
};

return f(max(st-M, 0), st, st, min(st+M, N-1));


}

// ----------- begin grader -------------

int main()
{
auto t1 = clock();

std::freopen("../tests/04-009.in", "r", stdin) ;


std::freopen("holiday.out", "w", stdout) ;

int n, start, d;
int attraction[100000];
int i, n_s;

n_s = scanf("%d %d %d", &n, &start, &d);


for (i = 0 ; i < n; ++i)
{
n_s = scanf("%d", &attraction[i]);
}

auto t2 = clock();

printf("%lld\n", findMaxAttraction(n, start, d, attraction));

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------- end grader ----------------


/*
t2-t1 = 0.203
t3-t2 = 1.875
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.6.4: holiday-229391.cpp


// https://oj.uz/submission/229391 283 ms 49776 KB

#include<bits/stdc++.h>
#include"holiday.h"

using namespace std;

#define ii pair <int, int>


#define app push_back
#define all(a) a.begin(), a.end()
#define bp __builtin_popcountll
#define ll long long
#define mp make_pair
#define f first
#define s second
#define Time (double)clock()/CLOCKS_PER_SEC
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 746

const int N = 1e5 + 7;


int start, hol, a[N];
vector <int> c;

struct Node
{
int cnt;
ll sum;
int l, r;
};

int ptr = 0;
const int V = N * 20;
Node d[V];
int t[N];

int newNode()
{
++ptr;
return ptr - 1;
}

int build(int l, int r)


{
int t = newNode();
if (l == r)
return t;
int m = (l + r) >> 1;
d[t].l = build(l, m);
d[t].r = build(m + 1, r);
return t;
}

int add(int t, int l, int r, int i)


{
int ans = newNode();
if (l == r)
{
d[ans].cnt = d[t].cnt + 1;
d[ans].sum = d[t].sum + c[i];
return ans;
}

int m = (l + r) >> 1;
if (i <= m)
{
d[ans].l = add(d[t].l, l, m, i);
d[ans].r = d[t].r;
}
else
{
d[ans].l = d[t].l;
d[ans].r = add(d[t].r, m + 1, r, i);
}

d[ans].cnt = d[d[ans].l].cnt + d[d[ans].r].cnt;


d[ans].sum = d[d[ans].l].sum + d[d[ans].r].sum;

return ans;
}

ll sum(int tl, int tr, int l, int r, int k)


{
if (l == r)
return (ll)k * c[l];

int m = (l + r) >> 1;
int r_cnt = d[d[tr].r].cnt - d[d[tl].r].cnt;
if (k <= r_cnt)
return sum(d[tl].r, d[tr].r, m + 1, r, k);
else
return (d[d[tr].r].sum - d[d[tl].r].sum) +
sum(d[tl].l, d[tr].l, l, m, k - r_cnt);
}

ll ans = 0;
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 747

ll get(int l, int r)
{
int go = (start - l) + (r - start) + min(start - l, r - start);
if (hol <= go)
return 0;
int k = hol - go;

return sum(t[l], t[r + 1], 0, N, min(r - l + 1, k));


}

int get_opt(int r, int opt_l, int opt_r)


{
ll nn = 0, opt = opt_l;
for (int l = opt_l; l <= opt_r; ++l)
{
ll t = get(l, r);
if (t > nn)
{
nn = t;
opt = l;
}
}

ans = max(ans, nn);


return opt;
}

void solve(int l, int r, int opt_l, int opt_r)


{
if (r < l)
return;
int m = (l + r) >> 1;
int opt_m = get_opt(m, opt_l, opt_r);

solve(l, m - 1, opt_l, opt_m);


solve(m + 1, r, opt_m, opt_r);
}

long long int findMaxAttraction(int n, int start_, int hol_, int a_[])
{
for (int i = 0; i < V; ++i)
{
d[i].cnt = d[i].sum = 0;
d[i].l = d[i].r = -1;
}

start = start_;
hol = hol_;
for (int i = 0; i < n; ++i)
a[i] = a_[i];

for (int i = 0; i < n; ++i)


c.app(a[i]);

sort(all(c));
c.resize(unique(all(c)) - c.begin());

for (int i = 0; i < n; ++i)


a[i] = lower_bound(all(c), a[i]) - c.begin();

t[0] = build(0, N);


for (int i = 0; i < n; ++i)
t[i + 1] = add(t[i], 0, N, a[i]);

solve(start, n - 1, 0, start);
return ans;
}

// ----------- begin grader -------------

int main()
{
auto t1 = clock();

std::freopen("../tests/04-009.in", "r", stdin) ;


std::freopen("holiday.out", "w", stdout) ;
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 748

int n, start, d;
int attraction[100000];
int i, n_s;

n_s = scanf("%d %d %d", &n, &start, &d);


for (i = 0 ; i < n; ++i)
{
n_s = scanf("%d", &attraction[i]);
}

auto t2 = clock();

printf("%lld\n", findMaxAttraction(n, start, d, attraction));

auto t3 = clock();

fclose(stdout);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------- end grader ----------------


/*
t2-t1 = 0.187
t3-t2 = 1.156
t4-t3 = 0

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


Press any key to continue.
*/

Listing 6.6.5: checkerHoliday.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/04-009.in",
(char*)"holiday.out",
(char*)"../tests/04-009.out",
};

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

registerChecker("holiday", argc, argv);


compareRemainingLines();
}
CAPITOLUL 6. IOI 2014 6.6. HOLIDAY 749

6.6.3 *Rezolvare detaliată


Capitolul 7
37
IOI 2013

7.1 Art Class


Problema 1 - Art Class 100 de puncte

Author: John Dethridge

Urmează să dai un examen la Istoria Artei, dar ai dat mai multă importanţă informaticii decât
cursului de artă. Va trebui să scrieţi un program care va da examenul ı̂n locul vostru.
Examenul va consta din mai multe picturi. Fiecare pictură va fi un exemplu din una dintre 4
stiluri distincte, numerotate 1, 2, 3 şi 4.
Primul stil conţine artă modernă neoplazică. De exemplu:

Al doilea stil contine peisaje impresioniste. De exemplu:

Al treilea stil conţine picturi de acţiune expresioniste. De exemplu:

Ultimul stil conţine picturi pe domeniu de culori. De exemplu:


37
aur: Rareş Darius Buhai, Liviu Rebreanu (Bistriţa)
. aur: Vlad Alexandru Gavrilă, ICHB (Bucureşti)
. argint: Andrei Heidelbacher, C.D. Loga (Timişoara)
. bronz: Mihai Popa, Fraţii Buzeşti (Craiova).

750
CAPITOLUL 7. IOI 2013 7.1. ART CLASS 751

Dată fiind o imagine digitală a unei picturi, se pune problema determinării stilului de care
aparţine.
Comisia IOI a colecţionat mai multe imagini din fiecare stil. Nouă imagini din fiecare stil au
fost selectate aleator şi incluse in materialul problemei pe computer-ul vostru, astfel să le puteţi
examina manual şi folosi pentru testare. Imaginile rămase vor fi date programului tău ı̂n timpul
gradării.
Imaginea va fi oferită ca un grid de H  W pixeli. Rândurile imaginii sunt numerotate 0, ...,
H  1 de sus ı̂n jos, iar coloanele sunt numerotate 0, ..., W  1 de la stânga la dreapta.
Pixelii sunt descrişi folosind matricele R, G şi B, care vă spun cantitatea de roşu, verde
şi albastru al fiecărui pixel din imagine. Aceste cantităţi variază de la 0 (fără roşu, verde sau
albastru) la 255 (cantitatea maxima de roşu, verde sau albastru).
Implementare
Va trebui să submitaţi un fişier ce implementează functia travelTime(), după cum
urmează:
Funcţia voastră: travelTime()
C/C++

int style(int H, int W, int R[500][500], int G[500][500],


int B[500][500]);

Pascal

type artArrayType = array[0..499, 0..499] of longint;


function style(H, W : LongInt;var R, G, B : artArrayType) : LongInt;

Descriere
Această funcţie ar trebui să determine stilul de care aparţine imaginea.
Parametrii
ˆ H : Numărul de rânduri de pixeli din imagine.
ˆ W : Numărului de coloane de pixeli din imagine.
ˆ R : O matrice de mărime H  W ce conţine cantitatea de roşu din fiecare pixel al imaginii.
ˆ G : O matrice de mărime H  W ce conţine cantitatea de verde din fiecare pixel al imaginii.
ˆ B : O matrice de mărime H  W ce conţine cantitatea de albastru din fiecare pixel al
imaginii.
ˆ Returnează: Stilului imaginii, care poate fi 1 , 2 , 3 sau 4 , cum e descris mai sus.

Fiecare element al matricei Rij , Gij  şi B ij  reprezintă pixelul de pe linia i şi coloana
j, şi va fi un număr ı̂ntreg ı̂ntre 0 şi 255 inclusiv.
Constrângeri
ˆ Limită de timp: 5 secunde
ˆ Limită de memorie: 64 MiB
ˆ 100 & H & 500
ˆ 100 & W & 500

Punctaje
Nu există subtaskuri. ı̂n schimb, scorul vostru pentru acest task va fi calculat pe baza
numărului de imagini pe care programul vostru le clasifică corect.
Presupunând că veţi clasifica corect un procent de P imagini (deci 0 & P & 100):
CAPITOLUL 7. IOI 2013 7.2. CAVE 752

ˆ Dacă P $ 25 atunci veţi obţine 0 puncte.


ˆ Dacă 25 & P $ 50 atunci veţi obţine ı̂ntre 0 şi 10 puncte, pe o scară liniară. Mai precis, veţi
primi 10 P  25©25 puncte, rotunjite la cel mai apropiat ı̂ntreg.
ˆ Dacă 50 & P $ 90 atunci veţi obţine ı̂ntre 10 şi 100 de puncte, pe o scară liniară. Mai precis,
veţi primi 10  90 P  50©40, rotunjite la cel mai apropiat ı̂ntreg.
ˆ Daca 90 & P atunci veţi obţine 100 de puncte.

Testare
Grader-ul de pe computerul vostru va citi input-ul din fişierul artclass.jpg. Acest fişier
trebuie sa conţină o imagine ı̂n format JPEG.
Vă este permis să folosiţi orice aplicaţie de procesare grafică pentru a studia imaginile, dar
acest lucru nu este necesar pentru a rezolva problema. (Vedeţi meniul ”Applications ¿ Graphics”.)
Note de limbaj
C/C++: Trebuie să faceţi #include "artclass.h".
Pascal: Trebuie să definiţi unit ArtClass. Toţi vectorii sunt indexaţi de la 0 (nu de la 1).
Vedeţi template-urile de soluţii de pe calculatorul vostru pentru exemple.
Timp maxim de executare/test: 5.0 secunde
Memorie: total 64 MB

7.1.1 Indicaţii de rezolvare

http://blog.brucemerry.org.za/2013/07/ioi-2013-day-1-analysis.html
This is a heuristic problem that can probably be solved in many ways.
Since it was a hammer I have, I decided to hit it with a very simple wavelet-based spectral
analysis.
To find the highest-frequency components, downsample the image, upsample it again, and
subtract it from the original.
Then take the sum of squared values in the residual.
Now start with the downsampled image and repeat recursively to get progressively lower fre-
quencies.
For the downsample and upsample I took a simple box filter. I kept 6 frequencies, since 26 is
less than the minimum image size.
For each style, I computed the mean and standard deviation of each of the 6 power values from
the examples.
To classify a picture, I sum up the squared differences from the means, with the differences
scaled using the standard deviations. It’s not 100% accurate, but good enough to get a perfect
score.

7.1.2 *Coduri sursă

7.1.3 *Rezolvare detaliată

7.2 Cave
Problema 2 - Cave 100 de puncte

Authors: Amaury Pouly and Arthur Charguéraud (France)


CAPITOLUL 7. IOI 2013 7.2. CAVE 753

Pierdându-vă pe drumul lung de la colegiu la UQ Centre, aţi dat peste intrarea ı̂ntr-un sistem
secret de peşteri ce se ı̂ntinde adânc sub universitate. Intrarea este blocată de un sistem de
securitate consistând din N uşi consecutive, aflate una ı̂n spatele alteia şi N ı̂ntrerupătoare,
fiecare ı̂ntrerupător conectat la o uşă diferită.

Uşile sunt numerotate 0, 1, ..., N  1 ı̂n ordine, uşa 0 fiind cea mai apropiată de voi.
Întrerupătoarele sunt de asemenea numerotate 0, 1, ..., N  1, dar nu cunoaşteţi care
ı̂ntrerupător este conectat la care uşă.
Întrerupătoarele se află la intrarea ı̂n peşteră. Fiecare ı̂ntrerupător poate fi ı̂n poziţia up sau
ı̂n poziţia down. Numai una din aceste poziţii este corectă pentru fiecare ı̂ntrerupător. Dacă
un ı̂ntrerupător este ı̂n poziţia corectă atunci uşa la care este conectat se va deschide, iar dacă
ı̂ntrerupătorul se află ı̂n poziţia greşita atunci uşa la care este conectat nu se va deschide. Poziţia
corectă poate fi diferită pentru diferite ı̂ntrerupătoare, şi voi nu ştiţi care poziţii sunt cele corecte.
Aţi vrea să ı̂nţelegeţi sistemul de securitate. Pentru a face asta, puteţi seta ı̂ntrerupătoarele
ı̂n orice combinaţie, şi apoi puteţi merge ı̂n peşteră pentru a vedea care este prima uşă ı̂nchisă.
Uşile nu sunt transparente: o dată ce aţi ı̂ntâlnit prima uşă ı̂nchisă, nu puteţi vedea nicio uşă ce
se află după aceasta.
Aveţi timp să ı̂ncercaţi 70,000 de combinaţii de ı̂ntrerupătoare, dar nu mai mult de atât. Taskul
vostru este să determinaţi poziţia corectă pentru fiecare ı̂ntrerupător şi cu care uşă este conectat
fiecare ı̂ntrerupător.
Implementare
Va trebui să submitaţi un fişier ce implementează procedura exploreCave(). Acesta poate
apela funcţia tryCombination() a grader-ului de maxim 70,000 de ori, şi trebuie să termine
apelând procedura answer() a grader-ului. Aceste funcţii şi proceduri sunt descrise mai jos.
Funcţia grader-ului: tryCombination()
C/C++

int tryCombination(int S[]);

Pascal

function tryCombination(var S: array of LongInt) : LongInt;

Descriere
Grader-ul va avea acestă funcţie. Aceasta vă permite să ı̂ncercaţi o combinaţie de
ı̂ntrerupătoare, şi apoi să intraţi ı̂n peşteră pentru a determina care este prima uşă ı̂nchisă. Dacă
toate uşile sunt deschise, atunci va returna -1. Acestă funcţie se execută ı̂ntr-o complexitate
temporală de O N ; adică ı̂n cel mai rău caz se execută ı̂ntr-un timp propoţional cu N .
Acestă funcţie poate fi apelată de maxim 70,000 de ori.
Parametrii

ˆ S: Un array de dimensiune N , indicând poziţia fiecarui ı̂ntrerupător. Elementul S i core-


spunde cu ı̂ntrerupătorul i. O valoare de 0 indică faptul că ı̂ntrerupătorul este ı̂n poziţia de
up, iar o valoare de 1 indică faptul că este ı̂n poziţia de down.
ˆ Returnează: Numărul primei uşi care este ı̂nchisă, sau -1 dacă toate uşile sunt deschise.
CAPITOLUL 7. IOI 2013 7.2. CAVE 754

Procedura grader-ului: answer()


C/C++

void answer(int S[], int D[]);

Pascal

procedure answer(var S, D: array of LongInt);

Descriere
Apelaţi acestă procedură atunci când aţi identificat o combinaţie de ı̂ntrerupătoare care să
deschidă toate uşile, şi uşa la care este conectat fiecare ı̂ntrerupător.
Parametrii
ˆ S: Un array de lungime N , indicând poziţia corectă a fiecărui ı̂ntrerupător. Formatul
acestuia este la fel cu formatul din funcţia tryCombination() de mai sus.
ˆ D: Un array de lungime N , indicând uşa la care este conectat fiecare ı̂ntrerupător. Mai
precis, elementul Di ar trebui să conţină numărul uşii la care ı̂ntrerupatorul i este conectat.
ˆ Returns: Această procedură nu returneaza, ci va cauza programul sa facă exit.

Procedura voastră: exploreCave()


C/C++

void exploreCave(int N);

Pascal

procedure exploreCave(N: longint);

Descriere
Submisia voatră trebuie să implementeze această procedură.
Această procedură ar trebui să folosească funcţia tryCombination() a grader-ului pentru a
determina poziţia corectă a fiecărui ı̂ntrerupător, şi uşa la care este conectat fiecare ı̂ntrerupător,
iar apoi, când are aceste informaţii, să apeleze answer().
Parametrii

ˆ N : Numărul de ı̂ntrerupătoare şi uşi din peşteră.

Exemplu
Presupuneţi că uşile si ı̂ntrerupătoarele sunt aranjate ca ı̂n imaginea de mai sus:
Apel de funcţie Returnează Explicaţie
tryCombination([1,0,1,1]) 1 Aceasta corespunde imaginii.
ı̂ntrerupătoarele 0, 2 şi 3 sunt down,
iar ı̂ntrerupătorul 1 este up. Funcţia
returnează 1, indicând că uşa 1 este
prima uşă de la stânga care este ı̂nchisă.
tryCombination([0,1,1,0]) 3 Uşile 0, 1 şi 2 sunt deschise, iar uşa 3 este
ı̂nchisă.
tryCombination([1,1,1,0]) -1 Mutând ı̂nterupătorul 0 ı̂n poziţia down
cauzează toate uşile să se deschidă, lucru
indicat de valoarea de retur -1
answer([1,1,1,0],[3,1 0,2]) (Programul Ne putem da seama că răspunsul este [1,
ı̂şi ı̂ncheie 1, 1,0], iar ı̂ntrerupătoarele 0, 1, 2 şi 3
execuţia) sunt conectate la uşile 3, 1, 0 şi 2.
Constrângeri
ˆ Limită de timp: 2 secunde
ˆ Limită de memorie: 32 MiB
ˆ 1 & N & 5, 000
CAPITOLUL 7. IOI 2013 7.2. CAVE 755

Subtask-uri
Subtask Punctaj Constrângeri adiţionale
1 12 Pentru fiecare i, ı̂nterupătorul i este conectat la uşa i. Task-ul vostru
este să determinaţi poziţia corectă a ı̂ntrerupătoarelor.
2 13 Poziţia corectă va fi tot timpul [0, 0, 0, ..., 0].
Task-ul vostru este să determinaţi cu ce uşă este conectat fiecare
ı̂ntrerupător.
3 21 N & 100
4 30 N & 2, 000
5 24 (Nimic)
Testare
Grader-ul de pe computerul vostru va citi datele de intrare din fişierul cave.in, care trebuie
să fie ı̂n următorul format:
ˆ linia 1: N
ˆ linia 2: S 0 S 1 ... S N 1
ˆ linia 3: D0 D1 ... DN 1

Aici N este numărul de uşi şi ı̂ntrerupătoare, S i este poziţia corectă pentru ı̂ntrerupătorul
i, şi Di este uşa la care este conectat ı̂ntrerupătorul i.
Astfel, exemplul de mai sus ar fi dat ı̂n următorul format:

41
1 1 0
3 1 0 2

Note de limbaj
C/C++: Trebuie să faceţi #include "cave.h".
Pascal: Trebuie să definiţi unit Cave, şi trebuie să importaţi rutinele grader-ului facând
uses GraderHelpLib. Toate array-urile sunt indexate de la 0 (nu de la 1).
Vedeţi template-urile de soluţii de pe computer-ul vostru pentru exemple.
Timp maxim de executare/test: 2.0 secunde
Memorie: total 32 MB

7.2.1 Indicaţii de rezolvare

http://blog.brucemerry.org.za/2013/07/ioi-2013-day-2-analysis.html
This is a nice problem following a recent IOI trend of having problems where the limiting
factor is not execution time, but some abstract measure of efficiency. This is slightly easier than
some of the previous problems in each vein.
To achieve 100%, we can use up to 14 queries per door. Since 214 is slightly more than the
number of doors, this strongly suggests some form of binary search on each door. It will be easiest
if we attack the doors in order. Once we know which switch and position opens a door, we can lock
that switch in place so that we will always be able to see the next door, and thereafter pretend
the switch does not even exist.
To solve for a door, we can start with all switches down, which will immediately tell us which
switch position opens the door. At this point every switch is a candidate. We can then test with
half the candidates down and half up, which will eliminate half the candidates. This process is
then repeated until only one candidate remains.

7.2.2 Coduri sursă


CAPITOLUL 7. IOI 2013 7.2. CAVE 756

Listing 7.2.1: cave-1749.cpp


// https://oj.uz/submission/1749 284 ms 896 KB

#include <stdio.h>
#include <stdlib.h>

#include<iostream>
#include<ctime>

using namespace std;

int tryCombination(int S[]);


void answer(int S[], int D[]);

// -------- begin exploreCave(int N) -----------------

//#include "cave.h"

int w[14][5000],F[5000];
void exploreCave(int N)
{
int i,j,t,c,ck;
for(i=0;i<14;i++)
{
for(j=0;j<N;j++)
{
if((1<<i)&j)w[i][j]=1;
}
}

for(i=0;i<N;i++)
{
c=0;
t=tryCombination(w[13]);
ck=(t==i);
for(j=0;j<13;j++)
{
t=tryCombination(w[j]);
if(t==i && !ck)c+=1<<j;
if(t!=i && ck)c+=1<<j;
}
for(j=0;j<14;j++)
w[j][c]=ck;
F[c]=i;
}

answer(w[0],F);
}

// -------- end exploreCave(int N) -----------------

#define MAX_N 5000


#define MAX_CALLS 70000

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

/* Symbol obfuscation */
#define N koala
#define realS kangaroo
#define realD possum
#define inv platypus
#define num_calls echidna

static int N;
static int realS[MAX_N];
static int realD[MAX_N];
static int inv[MAX_N];
static int num_calls;

void answer(int S[], int D[])


{
int i;
CAPITOLUL 7. IOI 2013 7.2. CAVE 757

int correct = 1;
for (i = 0; i < N; ++i)
if (S[i] != realS[i] || D[i] != realD[i])
{
correct = 0;
break;
}

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", S[i]);
}
printf("\n");

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", D[i]);
}
printf("\n");

//exit(0);
}

int tryCombination(int S[])


{
int i;

if (num_calls >= MAX_CALLS)


{
printf("INCORRECT\nToo many calls to tryCombination().\n");
exit(0);
}
++num_calls;

for (i = 0; i < N; ++i)


if (S[inv[i]] != realS[inv[i]])
return i;
return -1;
}

int init()
{
int i, res;

res = scanf("%d", &N);

for (i = 0; i < N; ++i)


{
res = scanf("%d", &realS[i]);
}
for (i = 0; i < N; ++i)
{
res = scanf("%d", &realD[i]);
inv[realD[i]] = i;
}

num_calls = 0;
return N;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);


std::freopen("cave.out", "w", stdout);

int N;

N = init();

auto t2 = clock();
CAPITOLUL 7. IOI 2013 7.2. CAVE 758

exploreCave(N);

auto t3 = clock();

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0.053
t3-t2 = 1.487
t4-t3 = 0

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


Press any key to continue.
*/

Listing 7.2.2: cave-16655.cpp


// https://oj.uz/submission/16655 331 ms 612 KB

#include <stdio.h>
#include <stdlib.h>

#include<iostream>
#include<ctime>

using namespace std;

int tryCombination(int S[]);


void answer(int S[], int D[]);

// -------- begin exploreCave(int N) -----------------

int a[5010],b[5010],chk[5010];
void exploreCave(int N)
{
int i,j,s,e,m,t,p;
for(i=0;i<N;++i)
{
t=tryCombination(a);
s=0,e=N-1;
while(s<e)
{
m=(s+e)/2;
for(j=s;j<=m;++j)
if(!chk[j])
a[j]=!a[j];

p=t,t=tryCombination(a);

(p==i)ˆ(t==i) ? e=m : s=m+1;


}

if(t==i)
a[s]=!a[s];
b[s]=i,chk[s]=1;
}

answer(a,b);
}

// -------- end exploreCave(int N) -----------------

#define MAX_N 5000


#define MAX_CALLS 70000
CAPITOLUL 7. IOI 2013 7.2. CAVE 759

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

/* Symbol obfuscation */
#define N koala
#define realS kangaroo
#define realD possum
#define inv platypus
#define num_calls echidna

static int N;
static int realS[MAX_N];
static int realD[MAX_N];
static int inv[MAX_N];
static int num_calls;

void answer(int S[], int D[])


{
int i;
int correct = 1;
for (i = 0; i < N; ++i)
if (S[i] != realS[i] || D[i] != realD[i])
{
correct = 0;
break;
}

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", S[i]);
}
printf("\n");

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", D[i]);
}
printf("\n");

//exit(0);
}

int tryCombination(int S[])


{
int i;

if (num_calls >= MAX_CALLS)


{
printf("INCORRECT\nToo many calls to tryCombination().\n");
exit(0);
}
++num_calls;

for (i = 0; i < N; ++i)


if (S[inv[i]] != realS[inv[i]])
return i;
return -1;
}

int init()
{
int i, res;

res = scanf("%d", &N);

for (i = 0; i < N; ++i)


{
res = scanf("%d", &realS[i]);
}
for (i = 0; i < N; ++i)
CAPITOLUL 7. IOI 2013 7.2. CAVE 760

{
res = scanf("%d", &realD[i]);
inv[realD[i]] = i;
}

num_calls = 0;
return N;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);


std::freopen("cave.out", "w", stdout);

int N;

N = init();

auto t2 = clock();

exploreCave(N);

auto t3 = clock();

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0
t3-t2 = 1.791
t4-t3 = 0

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


Press any key to continue.
*/

Listing 7.2.3: cave-170339.cpp


// https://oj.uz/submission/170339 275 ms 632 KB

#include <stdio.h>
#include <stdlib.h>

#include<iostream>
#include<ctime>

#include <bits/stdc++.h>

using namespace std;

int tryCombination(int S[]);


void answer(int S[], int D[]);

// -------- begin exploreCave(int N) -----------------

int s[5005], d[5005], res;


bool c[5005], pr, dw;

void exploreCave(int N)
{
memset(d, -1, sizeof(d));
res = tryCombination(s);
for(int i = 0; i < N; i++)
{
if(res == -1 or res > i) pr = true;
CAPITOLUL 7. IOI 2013 7.2. CAVE 761

else pr = false;
int maxi = N, mini = -1;
while(maxi - mini > 1)
{
int medi = (maxi + mini) / 2;
for (int j = mini + 1; j <= medi; j++)
{
if (c[j]) continue;
s[j] = !s[j];
}
res = tryCombination(s);
if (res == -1 or res > i) dw = true;
else dw = false;
if (dw == pr) mini = medi;
else maxi = medi;
pr = dw;
}

d[maxi] = i;
c[maxi] = true;
if(!dw)
{
s[maxi] = !s[maxi];
res = tryCombination(s);
}
}
answer(s, d);
}

// -------- end exploreCave(int N) -----------------

#define MAX_N 5000


#define MAX_CALLS 70000

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

/* Symbol obfuscation */
#define N koala
#define realS kangaroo
#define realD possum
#define inv platypus
#define num_calls echidna

static int N;
static int realS[MAX_N];
static int realD[MAX_N];
static int inv[MAX_N];
static int num_calls;

void answer(int S[], int D[])


{
int i;
int correct = 1;
for (i = 0; i < N; ++i)
if (S[i] != realS[i] || D[i] != realD[i])
{
correct = 0;
break;
}

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", S[i]);
}
printf("\n");

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", D[i]);
CAPITOLUL 7. IOI 2013 7.2. CAVE 762

}
printf("\n");

//exit(0);
}

int tryCombination(int S[])


{
int i;

if (num_calls >= MAX_CALLS)


{
printf("INCORRECT\nToo many calls to tryCombination().\n");
exit(0);
}
++num_calls;

for (i = 0; i < N; ++i)


if (S[inv[i]] != realS[inv[i]])
return i;
return -1;
}

int init()
{
int i, res;

res = scanf("%d", &N);

for (i = 0; i < N; ++i)


{
res = scanf("%d", &realS[i]);
}
for (i = 0; i < N; ++i)
{
res = scanf("%d", &realD[i]);
inv[realD[i]] = i;
}

num_calls = 0;
return N;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);


std::freopen("cave.out", "w", stdout);

int N;

N = init();

auto t2 = clock();

exploreCave(N);

auto t3 = clock();

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0
t3-t2 = 1.671
t4-t3 = 0
CAPITOLUL 7. IOI 2013 7.2. CAVE 763

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


Press any key to continue.
*/

Listing 7.2.4: cave-231414.cpp


// https://oj.uz/submission/231414 244 ms 640 KB

#include <stdio.h>
#include <stdlib.h>

#include<iostream>
#include<ctime>

#include <bits/stdc++.h>

using namespace std;

int tryCombination(int S[]);


void answer(int S[], int D[]);

// -------- begin exploreCave(int N) -----------------

void exploreCave(int N)
{
int n=N;

int R[n+10];
int P[n+10];
int aux2[n+10];

int x,y;
int z=1;
for(int i=0;i<n;i++)
{
R[i]=1;
P[i]=-1;
}
for(int i=0;i<n;i++)
{
for(int l=0;l<n;l++){aux2[l]=R[l];}

int p1=0,p2=n-1;
x=tryCombination(aux2);
z=0;
while(p1!=p2)
{
int p3=(p1+p2)/2;
if(z==1)
{
for(int l=p3+1;l<=p2;l++)
{
if(P[l]==-1)
{
aux2[l]=1;
}
}
}
else
{
for(int l=p1;l<=p3;l++)
{
if(P[l]==-1)
{
aux2[l]=0;
}
}
}

y=tryCombination(aux2);

if(z==1)
{
if((x>=i+1 || x==-1) && (y>=i+1 || y==-1)){p2=p3;z=1;}
else
CAPITOLUL 7. IOI 2013 7.2. CAVE 764

if((x<i+1 && x!=-1) && (y<i+1 && y!=-1)){p2=p3;z=1;}


else{p1=p3+1;z=0;}
}
else
{
if((x>=i+1 || x==-1) && (y>=i+1 || y==-1)){p1=p3+1;z=0;}
else
if((x<i+1 && x!=-1) && (y<i+1 && y!=-1)){p1=p3+1;z=0;}
else{p2=p3;z=1;}
}

x=y;
}

if(x>=i+1 || x==-1) {R[p1]=aux2[p1];P[p1]=i;}


else
{
if(aux2[p1]==1) {aux2[p1]=0;}
else {aux2[p1]=1;}

R[p1]=aux2[p1];P[p1]=i;
}
}
answer(R,P);
}

// -------- end exploreCave(int N) -----------------

#define MAX_N 5000


#define MAX_CALLS 70000

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

/* Symbol obfuscation */
#define N koala
#define realS kangaroo
#define realD possum
#define inv platypus
#define num_calls echidna

static int N;
static int realS[MAX_N];
static int realD[MAX_N];
static int inv[MAX_N];
static int num_calls;

void answer(int S[], int D[])


{
int i;
int correct = 1;
for (i = 0; i < N; ++i)
if (S[i] != realS[i] || D[i] != realD[i])
{
correct = 0;
break;
}

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", S[i]);
}
printf("\n");

for (i = 0; i < N; ++i)


{
if (i > 0)
printf(" ");
printf("%d", D[i]);
}
printf("\n");
CAPITOLUL 7. IOI 2013 7.2. CAVE 765

//exit(0);
}

int tryCombination(int S[])


{
int i;

if (num_calls >= MAX_CALLS)


{
printf("INCORRECT\nToo many calls to tryCombination().\n");
exit(0);
}
++num_calls;

for (i = 0; i < N; ++i)


if (S[inv[i]] != realS[inv[i]])
return i;
return -1;
}

int init()
{
int i, res;

res = scanf("%d", &N);

for (i = 0; i < N; ++i)


{
res = scanf("%d", &realS[i]);
}
for (i = 0; i < N; ++i)
{
res = scanf("%d", &realD[i]);
inv[realD[i]] = i;
}

num_calls = 0;
return N;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);


std::freopen("cave.out", "w", stdout);

int N;

N = init();

auto t2 = clock();

exploreCave(N);

auto t3 = clock();

//printf("INCORRECT\nYour solution did not call answer().\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
/*
t2-t1 = 0
t3-t2 = 1.968
t4-t3 = 0

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


CAPITOLUL 7. IOI 2013 7.3. DREAMING 766

Press any key to continue.


*/

Listing 7.2.5: checkerCave.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/random-perm-5000-many-closed.in",
(char*)"cave.out",
(char*)"../tests/random-perm-5000-many-closed.out",
};

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

registerChecker("cave", argc, argv);


compareRemainingLines();
}

7.2.3 *Rezolvare detaliată

7.3 Dreaming
Problema 3 - Dreaming 100 de puncte

Author: Kazuhiro Hosaka (Japan)

Această poveste se petrece cu mult timp ı̂n urmă, atunci când lumea era nouă şi IOI nu fusese
ı̂ncă visat.
Serpent trăieşte ı̂ntr-o lume ı̂n care există N băltoace, numerotate 0, ..., N  1. Există M poteci
bidirecţionale, care unesc perechi de băltoace, pe care Serpent poate călători. Fiecare pereche de
băltoace este conectată (direct sau indirect) de cel mult o secvenţă de poteci, deşi unele perechi
de băltoace pot să nu fie conectate deloc (deci, M & N  1). Fiecare potecă necesită un număr de
zile pentru ca Serpent să o parcurgă: acest număr poate fi diferit pentru fiecare potecă ı̂n parte.
Prietenul lui Serpent, Kangaroo, doreşte să construiască N  M  1 noi poteci, astfel ı̂ncât să
fie posibil ca Serpent să călătorească ı̂ntre oricare pereche de băltoace. Kangaroo poate crea poteci
ı̂ntre oricare pereche de băltoace, iar fiecare potecă creată de Kangaroo va dura L zile pentru ca
Serpent să o călătorească.
În plus, Kangaroo doreşte să facă călătoriile lui Serpent pe cât de scurte posibil. Kangaroo va
construi noi poteci astfel ı̂ncât timpul de parcurgere al celui mai lung drum ı̂ntre oricare pereche
de băltoace să fie cât mai mic posibil. Ajutaţi-l pe Kangaroo şi Serpent să determine cel mai lung
timp de călătorie ı̂ntre oricare pereche de băltoace, dupa ce Kangaroo a construit potecile noi ı̂n
stiul descris.
Exemple
CAPITOLUL 7. IOI 2013 7.3. DREAMING 767

În imaginea de mai sus, sunt N 12 băltoace şi M 8 poteci. Presupunând că L 2 atunci
fiecare potecă nouă va fi călătorită de Serpent ı̂n 2 zile. Atunci Kangaroo poate construi trei noi
poteci:
ˆ ı̂ntre băltoacele 1 şi 2;
ˆ ı̂ntre băltoacele 1 şi 6;
ˆ ı̂ntre băltoacele 4 şi 10.

Imaginea de mai sus arată setul final de poteci. Cel mai lung timp de călătorie este de 18 zile,
ı̂ntre băltoacele 0 şi 11. Acesta este cel mai mic rezultat posibil - oricum Kangaroo ar construi
potecile, va exista o pereche de băltoace care să necesite ca Serpent să călătorească 18 zile sau
mai mult.
Implementare
Va trebui să submitaţi un fişier ce implementează functia travelTime(), după cum urmează:
Funcţia voastră: travelTime()
C/C++

int travelTime(int N, int M, int L,


int A[], int B[], int T[]);

Pascal

function travelTime(N, M, L : LongInt;


var A, B, T : array of LongInt) : LongInt;

Descriere
CAPITOLUL 7. IOI 2013 7.3. DREAMING 768

Această funcţie trebuie să calculeze cel mai mare timp de călătorie (măsurat ı̂n zile) ı̂ntre orice
pereche de băltoace, presupunând că Kangaroo a adăugat N  M  1 poteci astfel ı̂ncât toate
băltoacele sunt conectate şi timpul maxim de călătorie este cel mai mic posibil.
Parametrii
ˆ N : Numărul de băltoace.
ˆ M : Numărul de poteci ce există deja.
ˆ L: Timpul ı̂n zile care care ı̂i trebuie lui Serpent să călătoarească pe o potecă nouă.
ˆ A, B and T : Vectori de lungime M ce specifică capetele şi timpul de parcurgere pentru
fiecare potecă existentă, astfel ı̂ncât a i-a potecă uneşte băltoacele Ai  1 şi B i  1, şi
necesită T i  1 zile pentru a fi călătorită ı̂n orice direcţie.
ˆ Returnează: Cel mai mare timp de călătorie ı̂ntre orice pereche de băltoace, aşa cum este
descris mai sus.

Exemplu de scenariu
Următorul scenariu descrie exemplul de mai sus:
Parametru Valoare
N 12
M 8
L 2
A [0, 8, 2, 5, 5, 1, 1, 10]
B [8, 2, 7, 11, 1, 3, 9, 6]
T [4, 2, 4, 3, 7, 1, 5, 3]
Returns 18
Constrângeri
ˆ Limită de timp: 1 secundă
ˆ Limită de memorie: 64 MiB
ˆ 1 & N & 100, 000
ˆ 0&M &N 1
ˆ 0 & Ai, B i & N  1
ˆ 1 & T i & 10, 000
ˆ 1 & L & 10, 000

Subtask-uri
Subtask Puncte Constrângeri suplimentare
1 14 M = N - 2 , şi există exact una sau două poteci pre-existente ce pleacă din
fiecare băltoacă. Cu alte cuvinte, există două seturi de băltoace conectate,
şi in fiecare set potecile formeaza un drum ce nu se desprinde.
2 10 M N  2 şi N 100
3 23 M N 2
4 18 Există cel mult o singură potecă pre-existentă ce pleacă din fiecare băltoacă.
5 12 N & 3, 000
6 23 (Nimic)
Testare
Grader-ul de pe computerul vostru va citi input-ul din fişierul dreaming.in, care trebuie să
fie ı̂n următorul format:

ˆ linia 1: N M 4L4

ˆ liniile 2, ..., M  1: Ai B i T i

În acest fel, exemplul de mai sus trebuie să fie dat ı̂n următorul format:

12 8 2
0 8 4
8 2 2
2 7 4
5 11 3
5 1 7
CAPITOLUL 7. IOI 2013 7.3. DREAMING 769

1 3 1
1 9 5
10 6 3

Note de limbaj
C/C++: Trebuie să faceţi #include "dreaming.h".
Pascal: Trebuie să definiţi unit Dreaming. Toţi vectorii sunt indexaţi de la 0 (nu de la 1
).
Timp maxim de executare/test: 1.0 secunde
Memorie: total 64 MB

7.3.1 Indicaţii de rezolvare

http://blog.brucemerry.org.za/2013/07/ioi-2013-day-1-analysis.html
This is a combination of a few common tree processing tasks. Firstly, the longest path might
just be within one of the original trees, i.e., a tree diameter. This can be computed recursively on
a tree by determining, for each node, the two longest paths downwards via different children (one
or both can be zero if there are fewer than 2 children). The diameter path will have a highest
node, and so the diameter will be the sum of these two lengths.
When add an edge to a tree, we must decide where to make the connection. The longest path
from the connection point to anywhere in the tree ought to be as short as possible, and so for each
point in the tree we need to know the distance to the furthest point. This is slightly more complex
than before, since we also have to consider paths that start upwards. However, a second recursive
walk (this time computing top-down instead of bottom-up) allows the shortest such paths to be
found. For a given tree, let the radius be the distance from the optimal connection point to the
furthest point in the tree.
Finally, we must decide how to connect the trees together. Sort the trees by decreasing radius
r1 ¿ r2 ¿ .... Clearly, there will be a path of at least length r1 + r2 + L. If there at least three
trees, they can’t all be connected to each other, so there must also be a path of at least length r2
+ r3 + 2L. Conversely, by connecting the first tree to every other tree (always using the optimal
connection points), it is not hard to see that there are no other paths that can be longer than the
worst of these.

7.3.2 Coduri sursă

Listing 7.3.1: dreaming-3168.cpp


// https://oj.uz/submission/3168 86 ms 9336 KB
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#include "dreaming.h"
#include<algorithm>
#include<vector>

using namespace std;

int N,M,L;
int Y[100010],tl,inl;
int tmp[100010],Q[100010][3];
int check[100010];

struct line
CAPITOLUL 7. IOI 2013 7.3. DREAMING 770

{
line(){}
line(int en,int len):en(en),len(len){}
int en,len;
};

vector <line> edge[100010];

int far(int x)
{
int fr=1,re=0,ret=x,mx=0;
Q[0][0]=x;
Q[0][1]=0;
check[x]=1;
while(fr!=re)
{
int i;
for(i=0;i<edge[Q[re][0]].size();i++)
{
int tx=edge[Q[re][0]][i].en;
if(check[tx])continue;
check[tx]=1;
Q[fr][0]=tx;
Q[fr][1]=Q[re][1]+edge[Q[re][0]][i].len;
if(mx<Q[fr][1])
{
mx=Q[fr][1];
ret=Q[fr][0];
}
fr++;
}
re++;
}
return ret;
}

void solve(int x)
{
int t=far(x),fr=1,re=0,tp=0;
Q[0][0]=t;
Q[0][1]=0;
Q[0][2]=-1;
check[t]=2;
while(fr!=re)
{
int i;
for(i=0;i<edge[Q[re][0]].size();i++)
{
int tx=edge[Q[re][0]][i].en;
if(check[tx]==2) continue;
check[tx]=2;
Q[fr][0]=tx;
Q[fr][1]=Q[re][1]+edge[Q[re][0]][i].len;
Q[fr][2]=re;
if(Q[fr][1]>Q[tp][1])tp=fr;
fr++;
}
re++;
}

int len=Q[tp][1],tx=0;
while(Q[tp][2]!=-1)
{
if(Q[tp][1]*2>len) tx=Q[tp][1];
else
{
tx=min(tx,len-Q[tp][1]);
break;
}
tp=Q[tp][2];
}

Y[tl]=tx;tl++;
inl=max(inl,len);
}
CAPITOLUL 7. IOI 2013 7.3. DREAMING 771

int travelTime(int Nn, int Mm, int Ll, int A[], int B[], int T[])
{
N=Nn,M=Mm,L=Ll;
int i;
for(i=0;i<M;i++)
{
edge[A[i]+1].push_back(line(B[i]+1,T[i]));
edge[B[i]+1].push_back(line(A[i]+1,T[i]));
}

for(i=1;i<=N;i++)
{
if(check[i])continue;
solve(i);
}
sort(Y,Y+tl);

if(tl==1)return inl;
if(tl==2)
return max(inl,L+Y[tl-1]+Y[tl-2]);
else
return max(inl,max(L+Y[tl-1]+Y[tl-2],2*L+Y[tl-2]+Y[tl-3]));
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_N 100000

static int A[MAX_N];


static int B[MAX_N];
static int T[MAX_N];

int main()
{
auto t1 = clock();

std::freopen("../tests/two-sticks-9.in", "r", stdin) ;


std::freopen("dreaming.out", "w", stdout) ;

int N, M, L, i;
int res;

res = scanf("%d%d%d", &N, &M, &L);

for (i = 0; i < M; i++)


res = scanf("%d%d%d", &A[i], &B[i], &T[i]);

auto t2 = clock();

int answer = travelTime(N, M, L, A, B, T);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.093
t3-t2 = 0.188
CAPITOLUL 7. IOI 2013 7.3. DREAMING 772

t4-t3 = 0

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


Press any key to continue.
*/

Listing 7.3.2: dreaming-16636.cpp


// https://oj.uz/submission/16636 71 ms 12280 KB

#include<ctime>
#include<iostream>

#include "dreaming.h"
#include <stdio.h>
#include <algorithm>
#include <vector>

#define MM 100000

using namespace std;

int sta[MM+1],chi[MM*2+1],wei[MM*2+1],nxt[MM*2+1];
int n,m,l,d[2][MM+1],v[2][MM+1],itr;
vector<int> vec;
bool ch[MM+1];

void addEdge(int x,int y,int w)


{
nxt[++m]=sta[x];
chi[m]=y;
wei[m]=w;
sta[x]=m;
}

int DFS1(int x,int p)


{
int i;

for(i=sta[x];i;i=nxt[i])
{
if(chi[i]!=p)
{
int k=DFS1(chi[i],x)+wei[i];
if(d[0][x]<k)
{
d[1][x]=d[0][x];
v[1][x]=v[0][x];
d[0][x]=k;
v[0][x]=i;
}
else
if(d[1][x]<k)
{
d[1][x]=k;
v[1][x]=i;
}
}
}

return d[0][x];
}

int DFS2(int x,int s,int p)


{
int i,ret;

if(d[0][x]<s)
{
d[1][x]=d[0][x];
v[1][x]=v[0][x];
d[0][x]=s;
v[0][x]=-1;
}
else
CAPITOLUL 7. IOI 2013 7.3. DREAMING 773

if(d[1][x]<s)
{
d[1][x]=s;
v[1][x]=-1;
}

ret=d[0][x];
ch[x]=true;

for(i=sta[x];i;i=nxt[i])
{
if(chi[i]!=p)
{
int k;
if(v[0][x]!=i)
{
k=DFS2(chi[i],d[0][x]+wei[i],x);
}
else
{
k=DFS2(chi[i],d[1][x]+wei[i],x);
}

ret=min(ret,k);
}
}

itr=max(itr,d[0][x]+d[1][x]);
return ret;
}

int travelTime(int _n, int _m, int _l, int A[], int B[], int T[])
{
int i;
n=_n;
l=_l;
m=0;
for(i=0;i<_m;i++)
{
addEdge(A[i],B[i],T[i]);
addEdge(B[i],A[i],T[i]);
}

for(i=0;i<n;i++)
{
if(!ch[i])
{
DFS1(i,-1);
vec.push_back(DFS2(i,0,-1));
}
}

int mx1=-1,mx2=-1,mx3=-1;
for(i=0;i<vec.size();i++)
{
if(mx3<vec[i]){mx3=vec[i];}
if(mx2<vec[i]){mx3=mx2;mx2=vec[i];}
if(mx1<vec[i]){mx2=mx1;mx1=vec[i];}
}
if(mx2==-1) return itr;
if(mx3==-1) return max(itr,mx1+mx2+l);

return max(itr,max(mx1+mx2+l,mx2+mx3+l*2));
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_N 100000

static int A[MAX_N];


static int B[MAX_N];
CAPITOLUL 7. IOI 2013 7.3. DREAMING 774

static int T[MAX_N];

int main()
{
auto t1 = clock();

std::freopen("../tests/two-sticks-9.in", "r", stdin) ;


std::freopen("dreaming.out", "w", stdout) ;

int N, M, L, i;
int res;

res = scanf("%d%d%d", &N, &M, &L);

for (i = 0; i < M; i++)


res = scanf("%d%d%d", &A[i], &B[i], &T[i]);

auto t2 = clock();

int answer = travelTime(N, M, L, A, B, T);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.437
t3-t2 = 0.062
t4-t3 = 0

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


Press any key to continue.
*/

Listing 7.3.3: dreaming-81476.cpp


// https://oj.uz/submission/81476 96 ms 16376 KB

#include "dreaming.h"
#include <bits/stdc++.h>

#define fi first
#define se second
#define lo long long
#define inf 1000000009
#define md 1000000007
#define li 100005
#define mp make_pair
#define pb push_back

using namespace std;

int vis[li],mn,M[li],M2[li],node[li],ss;
vector< pair<int,int> > v[li];

void f(int x)
{
vis[x]=1;
int t;
for(int i=0;i<(int)v[x].size();i++)
{
int go=v[x][i].fi;
CAPITOLUL 7. IOI 2013 7.3. DREAMING 775

int knr=v[x][i].se;
if(vis[go]==1) continue;
f(go);
t=M[go]+knr;
if(M[x]<t)
{
M2[x]=M[x];
M[x]=t;
node[x]=go;
}
else
if(M2[x]<t)
{
M2[x]=t;
}
}

ss=max(ss,M[x]+M2[x]);
}

void g(int x,int p,int t)


{
mn=min(mn,max(M[x],t));
for(int i=0;i<(int)v[x].size();i++)
{
int go=v[x][i].fi;
int knr=v[x][i].se;
if(go==p) continue;
int a=max(t,node[x]==go?M2[x]:M[x]);
g(go,x,a+knr);
}
}

int travelTime(int n,int m,int l,int *A,int *B,int *T)


{
int t,x,xx,xxx;
for(int i=0;i<m;i++)
{
v[A[i]].pb(mp(B[i],T[i]));
v[B[i]].pb(mp(A[i],T[i]));
}

x=-inf,xx=-inf,xxx=-inf;
for(int i=0;i<n;i++)
{
if(vis[i]==1) continue;
mn=1e9;
f(i);
g(i,-1,0);
if(x<mn)
{
xxx=xx;
xx=x;
x=mn;
}
else
if(xx<mn)
{
xxx=xx;
xx=mn;
}
else
if(xxx<mn)
{
xxx=mn;
}
}

return max(ss,max(x+xx+l,xx+xxx+l+l));
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
CAPITOLUL 7. IOI 2013 7.3. DREAMING 776

} while(0)

#define MAX_N 100000

static int A[MAX_N];


static int B[MAX_N];
static int T[MAX_N];

int main()
{
auto t1 = clock();

std::freopen("../tests/two-sticks-9.in", "r", stdin) ;


std::freopen("dreaming.out", "w", stdout) ;

int N, M, L, i;
int res;

res = scanf("%d%d%d", &N, &M, &L);

for (i = 0; i < M; i++)


res = scanf("%d%d%d", &A[i], &B[i], &T[i]);

auto t2 = clock();

int answer = travelTime(N, M, L, A, B, T);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.422
t3-t2 = 0.219
t4-t3 = 0

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


Press any key to continue.
*/

Listing 7.3.4: dreaming-172308.cpp


// https://oj.uz/submission/172308 117 ms 17272 KB

#include "dreaming.h"
#include <bits/stdc++.h>

using namespace std;

using pii = pair<int, int>;

#define ff first
#define ss second

vector<pii> edge[100005];
int par[100005], X[100005];
bool vis[100005];

pii dfs(int x, int p)


{
pii ret = {0, x};
for (auto i : edge[x])
CAPITOLUL 7. IOI 2013 7.3. DREAMING 777

{
if (i.ss == p) continue;
auto t = dfs(i.ss, x);
ret = max(ret, pii(t.ff + i.ff, t.ss));
}
X[x] = ret.ff;
par[x] = p;
vis[x] = 1;
return ret;
}

int travelTime(int N, int M, int L, int A[], int B[], int T[])
{
for (int i = 0; i < M; ++i)
{
edge[A[i]].emplace_back(T[i], B[i]);
edge[B[i]].emplace_back(T[i], A[i]);
}

vector<int> vec;
int ans, dia = 0;
for (int i = 0; i < N; ++i)
{
if (vis[i]) continue;
auto t = dfs(dfs(i, i).ss, -1);
int x = t.ss, mn = t.ff;
for (; x >= 0; x = par[x]) mn = min(mn, max(X[x], t.ff - X[x]));
vec.push_back(mn);
dia = max(dia, t.ff);
}

sort(vec.rbegin(), vec.rend());

if (vec.size() > 2)
{
ans = L + vec[0] + vec[1];
for (int i = 2; i < vec.size(); ++i)
ans = max(ans, L + L + vec[1] + vec[i]);
}
else
if (vec.size() == 2) ans = L + vec[0] + vec[1];
else ans = 0;

ans = max(ans, dia);


return ans;
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_N 100000

static int A[MAX_N];


static int B[MAX_N];
static int T[MAX_N];

int main()
{
auto t1 = clock();

std::freopen("../tests/two-sticks-9.in", "r", stdin) ;


std::freopen("dreaming.out", "w", stdout) ;

int N, M, L, i;
int res;

res = scanf("%d%d%d", &N, &M, &L);

for (i = 0; i < M; i++)


res = scanf("%d%d%d", &A[i], &B[i], &T[i]);

auto t2 = clock();
CAPITOLUL 7. IOI 2013 7.3. DREAMING 778

int answer = travelTime(N, M, L, A, B, T);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.406
t3-t2 = 0.234
t4-t3 = 0

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


Press any key to continue.
*/

Listing 7.3.5: dreaming-198762.cpp


// https://oj.uz/submission/198762 143 ms 17272 KB

#include"dreaming.h"
#include<bits/stdc++.h>

using namespace std;

vector<pair<int, int> >adj[100005];


vector<int>v;
bool vis[100005];
int par[100005], X[100005];

pair<int, int> dfs(int nw, int bf)


{
pair<int, int> rs={0, nw};
for(pair<int, int> i:adj[nw])
{
if(i.second==bf) continue;
pair<int, int> rst=dfs(i.second, nw);
rs=max(rs, {rst.first+i.first, rst.second});
}
X[nw]=rs.first;
par[nw]=bf; vis[nw]=true;
return rs;
}

int travelTime(int n, int m, int L, int A[], int B[], int T[])
{
for(int i=0; i<m; i++)
{
adj[A[i]].push_back({T[i], B[i]});
adj[B[i]].push_back({T[i], A[i]});
}

int ans, diameter=0;


for(int i=0; i<n; i++)
{
if(vis[i]) continue;
pair<int, int> rs=dfs(dfs(i, i).second, -1);
int nd=rs.second, mn=rs.first;
for(; nd>=0; nd=par[nd])
mn=min(mn, max(X[nd], rs.first-X[nd]));

v.push_back(mn);
CAPITOLUL 7. IOI 2013 7.3. DREAMING 779

diameter=max(diameter, rs.first);
}

sort(v.begin(), v.end());
reverse(v.begin(), v.end());

if(v.size()>2)
{
ans=L+v[0]+v[1];
for(int i=2; i<v.size(); i++)
ans=max(ans, 2*L+v[1]+v[i]);
}
else
if(v.size()==2) ans=L+v[0]+v[1];
else ans=0;

ans=max(ans, diameter);
return ans;
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_N 100000

static int A[MAX_N];


static int B[MAX_N];
static int T[MAX_N];

int main()
{
auto t1 = clock();

std::freopen("../tests/two-sticks-9.in", "r", stdin) ;


std::freopen("dreaming.out", "w", stdout) ;

int N, M, L, i;
int res;

res = scanf("%d%d%d", &N, &M, &L);

for (i = 0; i < M; i++)


res = scanf("%d%d%d", &A[i], &B[i], &T[i]);

auto t2 = clock();

int answer = travelTime(N, M, L, A, B, T);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.438
t3-t2 = 0.234
t4-t3 = 0

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


Press any key to continue.
CAPITOLUL 7. IOI 2013 7.4. GAME 780

*/

Listing 7.3.6: checkerDreaming.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/two-sticks-9.in",
(char*)"dreaming.out",
(char*)"../tests/two-sticks-9.out",
};

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

registerChecker("dreaming", argc, argv);


compareRemainingLines();
}

7.3.3 *Rezolvare detaliată

7.4 Game
Problema 4 - Game 100 de puncte

Author: Monika Steinová (Slovakia)

Bazza şi Shazza vor să joace un joc. Tabla este un grid de celule, cu R rânduri numerotate 0,
..., R  1 şi C coloane numerotate 0, ..., C  1. Fie p, q  celula din rândul p şi coloana q. Fiecare
celulă conţine un ı̂ntreg pozitiv, la ı̂nceputul jocului toate acestea conţinând zero.
Jocul se desfăşoară ı̂n felul următor. ı̂n orice moment, Bazza poate:
ˆ să facă un update la celula p, q , schimbând ı̂ntregul pe care ı̂l conţine;
ˆ să ı̂i ceară lui Shazza să calculeze cel mai mare divizor comun (CMMDC) al tuturor ı̂ntregilor
dintr-o regiune dreptunghiulară de celule, având colţurile opuse p, q  şi u, v  (inclusiv aceste
celule).
Bazza va face cel mult NU  NQ acţiuni (updatare celule de NU ori şi punere de ı̂ntrebări de
NQ ori) până se plictiseşte şi iese afară ca să se joace cricket.
Voi trebuie să calculaţi răspunsurile corecte.
Exemple
Presupunând că R 2 and C 3, şi Bazza ı̂ncepe cu următoarele update-uri:
ˆ Update la celula (0, 0) la valoarea 20;
ˆ Update la celula (0, 2) la valoarea 15;
ˆ Update la celula (1, 1) la valoarea 12.
CAPITOLUL 7. IOI 2013 7.4. GAME 781

Grid-ul rezultat este afişat ı̂n imaginea de mai sus. Bazza poate să ı̂ntrebe care este CMMDC-ul
următoarelor zone dreptunghiulare:
ˆ Colţuri opuse (0, 0) and (0, 2) : cei trei ı̂ntregi din acest dreptunghi sunt 20, 0 şi 15, având
CMMDC-ul 5.
ˆ Colţuri opuse (0, 0) and (1, 1) : cei patru ı̂ntregi din acest dreptunghi sunt 20, 0, 0 şi 12,
având CMMDC-ul 4.

Presupunând că acum Bazza face următoarele update-uri:


ˆ Update la celula (0, 1) la valoarea 6;
ˆ Update la celula (1, 1) la valoarea 14.

Noul grid este afişat ı̂n imaginea de mai sus. Bazza poate ı̂ntreba pentru CMMDC-urile
următoarelor zone dreptunghiulare (din nou):
ˆ Colţuri opuse (0, 0) şi (0, 2) : acum cei trei ı̂ntregi ı̂n acest dreptunghi sunt 20, 6 şi 15,
având CMMDC-ul 1.
ˆ Colţuri opuse (0, 0) şi (1, 1) : acum cei patru ı̂ntregi din acest dreptunghi sunt 20, 6, 0 şi
14, având CMMDC-ul 2.

Aici Bazza a performat NU 5 update-uri şi NQ 4 query-uri.


Implementare
Trebuie să submitaţi un fişier care implementează funcţiile init(), update() şi
calculate(), conform descrierii următoare.
Pentru a vă ajuta, soluţiile - template de pe calculatorul vostru (game.c, game.cpp şi game.pas)
conţine fiecare o funcţie gcd2 X, Y  pentru calculul celui mai mare divizor comun pentru două
numere ı̂ntregi nenegative X şi Y . Dacă X Y 0 atunci gcd2 X, Y  va returna deasemenea 0.
Această funcţie este suficient de rapidă pentru a obţine scorul maxim; mai precis, timpul de
execuţie este ı̂n cel mai rău caz proporţional cu log X  Y .
Procedura voastră: init()
C/C++ void init(int R, int C);
Pascal procedure init(R, C : LongInt);
Descriere
Submisia voastră trebuie să implementeze această funcţie.
Această funcţie vă furnizeaza dimensiunile iniţiale ale gridului şi vă permite să iniţializaţi orice
variabile sau structuri de date globale. Aceasta va fi apelată o singură dată ı̂nainte de orice apel
pentru update() sau calculate().
Parametetri
ˆ R: Numărul de linii.
ˆ C: Numărul de coloane.

Procedura voastră: update()


C/C++ void update(int P, int Q, long long K);
Pascal procedure update(P, Q : LongInt; K : Int64);
Descriere
Submisia voastră trebuie sa implementeze această funcţie.
Această funcţie va fi apelată atunci cı̂nd Bazza schimbă valoarea ı̂ntr-o anumită celulă din
grid.
Parametri
ˆ P : Linia celulei de grid ( 0 & P & R  1 ).
ˆ Q: Coloana celulei de grid ( 0 & Q & C  1 ).
CAPITOLUL 7. IOI 2013 7.4. GAME 782

ˆ K: Valoarea nouă care va fi conţinută de celula din grid ( 0 & K & 1018 ). Poate fi la fel ca
valoarea curentă.

Funcţia vostră: calculate()


C/C++ long long calculate(int P, int Q, int U, int V);
Pascal function calculate(P, Q, U, V : LongInt) : Int64;
Descriere
Submisia voastră trebuie sa implementeze această funcţie.
Această funcţie trebuie să calculeze cel mai mare divizor comun a tuturor intregilor din drep-
tunghiul cu colţurile ı̂n celulele de grid P, Q şi U, V . Colţurile fac parte din dreptunghi.
Dacă toate valorile din dreptunghi sunt nule funcţia trebuie să returneze zero.
Parametri
ˆ P : Linia celulei stânga-sus a dreptunghiului ( 0 & P & R  1 ).
ˆ Q: Coloana celulei stânga-sus a dreptunghiului( 0 & Q & C  1 ).
ˆ U : Linia celulei dreapta-jos a dreptunghiului ( P & U & R  1 ).
ˆ V : Coloana celulei dreapta-jos a dreptunghiului ( Q & V & C  1 ).
ˆ Return: Cel mai mare divizor comun al tuturor valorilor din dreprunghi sau 0 toate aceste
valori sunt zero.

Exemplu
Aici aveţi descris exemplul de mai sus:
Apelul Funcţiei Returnează
init(2, 3)
update(0, 0, 20)
update(0, 2, 15)
update(1, 1, 12)
calculate(0, 0, 0, 2) 5
calculate(0, 0, 1, 1) 4
update(0, 1, 6)
update(1, 1, 14)
calculate(0, 0, 0, 2) 1
calculate(0, 0, 1, 1) 2
Constrângeri
ˆ Limită de timp: vezi subtask-uri
ˆ Limită de memorie: vezi subtask-uri
ˆ 1 & R, C & 10
9

ˆ 0 & K & 10 , unde K este un ı̂ntreg pe care Bazza ı̂l plasează ı̂n grid.
18

Subtask-uri
Vezi versiunea in engleză pentru parametrii subtask-urilor.

Testare
Graderul de pe computerul vostru va citi input din fişierul game.in. Acest fişier trebuie să
aibă următorul format:
CAPITOLUL 7. IOI 2013 7.4. GAME 783

ˆ linia 1: R C N
ˆ următoarele N linii: o acţiune pe linie, ı̂n ordinea ı̂n care apar

Linia pentru fiecare acţiune trebuie să fie una dintre următoarele formate:
ˆ indică update(P, Q, K): 1 P Q K
ˆ indică calculate(P, Q, U, V): 2 P Q U V

Astfel, exemplul de mai sus ar fi dat ı̂n următorul format:


2 3 9
1 0 0 20
1 0 2 15
1 1 1 12
2 0 0 0 2
2 0 0 1 1
1 0 1 6
1 1 1 14
2 0 0 0 2
2 0 0 1 1

Note de limbaj
C/C++ Trebuie să faceţi #include "game.h".
Pascal Trebuie sa definiţi unit Game. Toţi vectorii sunt indexaţi incepând de la 0 (nu de la
1).
Deoarece ı̂ntregii din grid pot fi foarte mari, utilizatorii C/C++ sunt sfătuiţi să folosească tipul
de date long long, şi utilizatorii Pascal sunt sfătuiţi să folosească tipul de date Int64.
Timp maxim de executare/test: 13.0 secunde
Memorie: total 230 MB

7.4.1 Indicaţii de rezolvare

http://blog.brucemerry.org.za/2013/07/ioi-2013-day-2-analysis.html
I disliked this problem because it has a nice solution that takes just a bit too much memory. I
only managed to get 80% for it in the time I spent on it, and I didn’t feel inspired to modify my
solution to pass fully.
In 1D, this can be solved by a fairly straightforward use of a segment tree: each node stores
the GCD of its two children. Since R can be quite big, this needs to be a sparse segment tree;
another alternative would be a balanced binary tree.
In 2D, it is tempting to use a quadtree, but in fact that doesn’t guarantee poly-logarithmic
time. A 1  C query will force refinement down to the individual non-zero cell entries. Instead,
we can use a range tree, which is a tree of trees: an outer tree is built over the columns, and for
each column span corresponding to a node in this tree, we have an inner tree over the rows. Each
node in this inner tree corresponds to a rectangle of the grid, and stores the GCD of elements
from this rectangle. A query now uses the columns to select O log C  nodes from the outer tree
i.e., O log C  inner trees, and applies a query to each of them. Queries thus take O log R log C 
time when the implementation uses segment trees for the inner and outer trees. With balanced
2
binary trees, it would only be O log N u .
Unfortunately, using segment trees also requires O log R log C  memory per non-zero element,
which just exceeds the available memory. Using balanced binary trees instead should fit within
memory, but is a lot more painful to implement. I think it might also be possible to make it work
by sticking with segment trees, but compressing the representation by compacting chains of nodes
with one child.
CAPITOLUL 7. IOI 2013 7.4. GAME 784

7.4.2 Coduri sursă

Listing 7.4.1: game-220385.cpp


// https://oj.uz/submission/220385 3289 ms 73500 KB

#include <stdio.h>
#include <stdlib.h>
#include "game.h"

#include<bits/stdc++.h>
#include "game.h"

using namespace std;

mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());

const int N = 3e5 + 9;

struct node
{
node *l, *r;
int pos, key, mn, mx;
long long val, g;
node(int position, long long value)
{
l = r = nullptr;
mn = mx = pos = position;
key = rnd();
val = g = value;
}

void pull()
{
g = val;
if(l) g = __gcd(g, l->g);
if(r) g = __gcd(g, r->g);
mn = (l ? l->mn : pos);
mx = (r ? r->mx : pos);
}
};

//memory O(n)
struct treap
{
node *root;
treap()
{
root = nullptr;
}

void split(node *t, int pos, node *&l, node *&r)


{
if (t == nullptr)
{
l = r = nullptr;
return;
}
if (t->pos < pos)
{
split(t->r, pos, l, r);
t->r = l;
l = t;
}
else
{
split(t->l, pos, l, r);
t->l = r;
r = t;
}
t->pull();
}

node* merge(node *l, node *r)


CAPITOLUL 7. IOI 2013 7.4. GAME 785

{
if (!l || !r) return l ? l : r;
if (l->key < r->key)
{
l->r = merge(l->r, r);
l->pull();
return l;
}
r->l = merge(l, r->l);
r->pull();
return r;
}

bool find(int pos)


{
node *t = root;
while (t)
{
if (t->pos == pos) return true;
if (t->pos > pos) t = t->l;
else t = t->r;
}
return false;
}

void upd(node *t, int pos, long long val)


{
if (t->pos == pos)
{
t->val = val;
t->pull();
return;
}
if (t->pos > pos) upd(t->l, pos, val);
else upd(t->r, pos, val);
t->pull();
}

void insert(int pos, long long val) //set a_pos = val


{
if (find(pos)) upd(root, pos, val);
else
{
node *l, *r;
split(root, pos, l, r);
root = merge(merge(l, new node(pos, val)), r);
}
}

long long query(node *t, int st, int en)


{
if (t->mx < st || en < t->mn) return 0;
if (st <= t->mn && t->mx <= en) return t->g;
long long ans = (st <= t->pos && t->pos <= en ? t->val : 0);
if (t->l) ans = __gcd(ans, query(t->l, st, en));
if (t->r) ans = __gcd(ans, query(t->r, st, en));
return ans;
}

long long query(int l, int r) //gcd of a_i such that l <= i <= r
{
if (!root) return 0;
return query(root, l, r);
}

void print(node *t)


{
if (!t) return;
print(t->l);
cout << t->val << " ";
print(t->r);
}
};

//total memory along with treap = n log n


struct ST
CAPITOLUL 7. IOI 2013 7.4. GAME 786

{
ST *l, *r;
treap t;
int b, e;

ST()
{
l = r = nullptr;
}

ST(int st, int en)


{
l = r = nullptr;
b = st, e = en;
}

void fix(int pos)


{
long long val = 0;
if (l) val = __gcd(val, l->t.query(pos, pos));
if (r) val = __gcd(val, r->t.query(pos, pos));
t.insert(pos, val);
}

void upd(int x, int y, long long val) //set a[x][y] = val


{
if (e < x || x < b) return;
if (b == e)
{
t.insert(y, val);
return;
}

if (b != e)
{
if (x <= (b + e) / 2)
{
if (!l) l = new ST(b, (b + e) / 2);
l->upd(x, y, val);
}
else
{
if (!r) r = new ST((b + e) / 2 + 1, e);
r->upd(x, y, val);
}
}
fix(y);
}

long long query(int i, int j, int st, int en) //gcd of a[x][y]
// such that i <= x <= j && st <= y <= en
{
if (e < i || j < b) return 0;
if (i <= b && e <= j) return t.query(st, en);
long long ans = 0;
if (l) ans = __gcd(ans, l->query(i, j, st, en));
if (r) ans = __gcd(ans, r->query(i, j, st, en));
return ans;
}
};

int r, c, q;
ST t;

void init(int R, int C)


{
r = R; c = C;
t = ST(0, r - 1);
}

void update(int P, int Q, long long K)


{
t.upd(P, Q, K);
}

long long calculate(int P, int Q, int U, int V)


CAPITOLUL 7. IOI 2013 7.4. GAME 787

{
return t.query(P, U, Q, V);
}

// ------------- begin grader ----------------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_SIZE 1000000000


//#define MAX_N 100000
#define MAX_N 272000

int main()
{
auto t1 = clock();

std::freopen("../tests/05.st-4-random-1.in", "r", stdin);


std::freopen("game.out", "w", stdout);

int R, C, N;
int P, Q, U, V;
long long K;
int i, type;
int res;

res = scanf("%d", &R);

if (res != 1)
fail("Failed to read R from input file.");
if (R < 1 || R > MAX_SIZE)
fail("R is out of bounds.");

res = scanf("%d", &C);

if (res != 1)
fail("Failed to read C from input file.");
if (C < 1 || C > MAX_SIZE)
fail("C is out of bounds.");

res = scanf("%d", &N);

if (res != 1)
fail("Failed to read N from input file.");
if (N < 0 || N > MAX_N)
fail("N is out of bounds.");

auto t2 = clock();

init(R, C);

auto t3 = clock();

for (i = 0; i < N; i++)


{
res = scanf("%d", &type);

if (type == 1)
{
res = scanf("%d%d%lld", &P, &Q, &K);

update(P, Q, K);
}
else
if (type == 2)
{
res = scanf("%d%d%d%d", &P, &Q, &U, &V);

printf("%lld\n", calculate(P, Q, U, V));


}
else
fail("Invalid action type in input file.");
}
CAPITOLUL 7. IOI 2013 7.4. GAME 788

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------------


/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 9.14

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


Press any key to continue.
*/

Listing 7.4.2: game-224978.cpp


// https://oj.uz/submission/224978 3504 ms 82424 KB

#include <stdio.h>
#include <stdlib.h>
#include "game.h"

#include<ctime>
#include<iostream>

using namespace std;

#define MAXR 1000000000


#define MAXC 1000000000

#include <assert.h>
#include <stddef.h>

long long gcd2(long long X, long long Y)


{
if(X == 0 || Y == 0)
{
return X + Y;
}

long long tmp;


while (X != Y && Y != 0)
{
tmp = X;
X = Y;
Y = tmp % Y;
}
return X;
}

struct layer2_node
{
layer2_node(int low, int high)
: low(low), high(high), lft(NULL), rht(NULL), value(0LL) { }

int low;
int high;
layer2_node* lft;
layer2_node* rht;
long long value;
};

struct layer1_node
{
layer1_node() : lft(NULL), rht(NULL), l2(0, MAXC) { }

layer1_node* lft;
CAPITOLUL 7. IOI 2013 7.4. GAME 789

layer1_node* rht;
layer2_node l2;
};

static layer1_node root;

static void update2(layer2_node* node, int Q, long long K)


{
int low = node->low;
int high = node->high;
int mid = (low + high) / 2;
if(low + 1 == high)
{
/* For leaf nodes we just update the value directly. */
node->value = K;
return;
}

layer2_node*& tgt = Q < mid ? node->lft : node->rht;


if(tgt == NULL)
{
/* If there is no node on this side of the tree create a new leaf node
* containing exactly our update point. */
tgt = new layer2_node(Q, Q + 1);
tgt->value = K;
}
else
if(tgt->low <= Q && Q < tgt->high)
{
/* If the existing node contains our query point continue
* inserting there.
*/
update2(tgt, Q, K);
}
else
{
/* Otherwise traverse down the virtual tree until the side of
* our query node
* and the side of the existing node differ. Create this node
* and continue
* updating along it. */
do
{
(Q < mid ? high : low) = mid;
mid = (low + high) / 2;
} while((Q < mid) == (tgt->low < mid));

layer2_node* nnode = new layer2_node(low, high);


(tgt->low < mid ? nnode->lft : nnode->rht) = tgt;
tgt = nnode;

update2(nnode, Q, K);
}

/* Internal nodes get updated as the gcd of its leaves. */


node->value = gcd2(node->lft ? node->lft->value : 0,
node->rht ? node->rht->value : 0);
}

long long query2(layer2_node* nd, int A, int B)


{
/* The same as the level 1 queries except the interval each node
* represents
* may skip some levels of the tree so we store them in the node
* itself. */
if(nd == NULL || B <= nd->low || nd->high <= A)
{
return 0;
}
else
if(A <= nd->low && nd->high <= B)
{
return nd->value;
}
return gcd2(query2(nd->lft, A, B), query2(nd->rht, A, B));
}
CAPITOLUL 7. IOI 2013 7.4. GAME 790

static void update1(layer1_node* node, int low, int high,


int P, int Q, long long K)
{
int mid = (low + high) / 2;

if(low + 1 == high)
{
update2(&node->l2, Q, K);
}
else
{
layer1_node*& nnode = P < mid ? node->lft : node->rht;
(P < mid ? high : low) = mid;
if(nnode == NULL)
{
nnode = new layer1_node();
}
update1(nnode, low, high, P, Q, K);

/* Compute what value to update with. */


K = gcd2(node->lft ? query2(&node->lft->l2, Q, Q + 1) : 0,
node->rht ? query2(&node->rht->l2, Q, Q + 1) : 0);
update2(&node->l2, Q, K);
}
}

long long query1(layer1_node* nd, int low, int high,


int A1, int B1, int A2, int B2)
{
/* Scan down the tree short-circuiting when we reach a node that
* contains
* our entire query interval and combining the result of the level2
* queries.
*/
if(nd == NULL || B1 <= low || high <= A1)
{
return 0;
}
else
if(A1 <= low && high <= B1)
{
return query2(&nd->l2, A2, B2);
}
int mid = (low + high) / 2;
return gcd2(query1(nd->lft, low, mid, A1, B1, A2, B2),
query1(nd->rht, mid, high, A1, B1, A2, B2));
}

void init(int R, int C) { }

void update(int P, int Q, long long K)


{
update1(&root, 0, MAXR, P, Q, K);
}

long long calculate(int P, int Q, int U, int V)


{
return query1(&root, 0, MAXR, P, U + 1, Q, V + 1);
}

// ------------- begin grader ----------------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_SIZE 1000000000


//#define MAX_N 100000
#define MAX_N 272000

int main()
{
auto t1 = clock();
CAPITOLUL 7. IOI 2013 7.4. GAME 791

std::freopen("../tests/05.st-4-random-1.in", "r", stdin);


std::freopen("game.out", "w", stdout);

int R, C, N;
int P, Q, U, V;
long long K;
int i, type;
int res;

res = scanf("%d", &R);

if (res != 1)
fail("Failed to read R from input file.");
if (R < 1 || R > MAX_SIZE)
fail("R is out of bounds.");

res = scanf("%d", &C);

if (res != 1)
fail("Failed to read C from input file.");
if (C < 1 || C > MAX_SIZE)
fail("C is out of bounds.");

res = scanf("%d", &N);

if (res != 1)
fail("Failed to read N from input file.");
if (N < 0 || N > MAX_N)
fail("N is out of bounds.");

auto t2 = clock();

init(R, C);

auto t3 = clock();

for (i = 0; i < N; i++)


{
res = scanf("%d", &type);

if (type == 1)
{
res = scanf("%d%d%lld", &P, &Q, &K);

update(P, Q, K);
}
else
if (type == 2)
{
res = scanf("%d%d%d%d", &P, &Q, &U, &V);

printf("%lld\n", calculate(P, Q, U, V));


}
else
fail("Invalid action type in input file.");
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------------


/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 6.031

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


CAPITOLUL 7. IOI 2013 7.4. GAME 792

Press any key to continue.


*/

Listing 7.4.3: game-225963.cpp


// https://oj.uz/submission/225963 3732 ms 94012 KB

#include <stdio.h>
#include <stdlib.h>
#include "game.h"

#include <bits/stdc++.h>

using namespace std;

int _R, _C;

struct node2
{
int l, r;
long long v;
node2 *L, *R;
node2(int a, int b)
{
l = a, r = b;
v = 0;
L = R = NULL;
}
};

struct node1
{
int l, r;
node2 *now;
node1 *L, *R;
node1(int a, int b)
{
l = a, r = b;
now = new node2(1, _C);
L = R = NULL;
}
};

node1 *root;

long long gcd2(long long X, long long Y)


{
long long tmp;
while (X != Y && Y != 0)
{
tmp = X;
X = Y;
Y = tmp % Y;
}
return X;
}

long long query2(node2 *seg, int a, int b)


{
if(!seg || seg->r < a || b < seg->l) return 0;
if(a <= seg->l && seg->r <= b) return seg->v;
return gcd2(query2(seg->L, a, b), query2(seg->R, a, b));
}

long long query1(node1 *seg, int a, int b, int c, int d)


{
if(!seg || seg->r < a || b < seg->l) return 0;
if(a <= seg->l && seg->r <= b) return query2(seg->now, c, d);
return gcd2(query1(seg->L, a, b, c, d), query1(seg->R, a, b, c, d));
}

void update2(node2 *&seg, int p, long long k)


{
if(seg->l == seg->r) return void(seg->v = k);
int l = seg->l, r = seg->r;
CAPITOLUL 7. IOI 2013 7.4. GAME 793

int mid = (l+r)>>1;


node2 *&now = (p <= mid) ? seg->L : seg->R;
if(!now)
{
now = new node2(p, p);
now->v = k;
}
else
if(now->l <= p && p <= now->r) update2(now, p, k);
else
{
while((p <= mid) == (now->l <= mid))
{
(p <= mid) ? r = mid : l = mid+1;
mid = (l+r)>>1;
}
node2 *next = new node2(l, r);
(now->l <= mid) ? next->L = now : next->R = now;
now = next;
update2(next, p, k);
}
seg->v = gcd2((seg->L) ? seg->L->v : 0, (seg->R) ? seg->R->v : 0);
}

void update1(node1 *&seg, int p, int q, long long k)


{
if(seg->l == seg->r) return void(update2(seg->now, q, k));
int mid = (seg->l + seg->r)>>1;
if(p <= mid)
{
if(!seg->L) seg->L = new node1(seg->l, mid);
update1(seg->L, p, q, k);
}
else
{
if(!seg->R) seg->R = new node1(mid+1, seg->r);
update1(seg->R, p, q, k);
}

long long v = gcd2((seg->L) ? query2(seg->L->now, q, q) : 0,


(seg->R) ? query2(seg->R->now, q, q) : 0);
update2(seg->now, q, v);
}

void init(int R, int C)


{
_R = R, _C = C;
root = new node1(1, _R);
}

void update(int P, int Q, long long K)


{
P++, Q++;
update1(root, P, Q, K);
}

long long calculate(int P, int Q, int U, int V)


{
P++, Q++, U++, V++;
return query1(root, P, U, Q, V);
}

// ------------- begin grader ----------------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_SIZE 1000000000


//#define MAX_N 100000
#define MAX_N 272000

int main()
{
auto t1 = clock();
CAPITOLUL 7. IOI 2013 7.4. GAME 794

std::freopen("../tests/05.st-4-random-1.in", "r", stdin);


std::freopen("game.out", "w", stdout);

int R, C, N;
int P, Q, U, V;
long long K;
int i, type;
int res;

res = scanf("%d", &R);

if (res != 1)
fail("Failed to read R from input file.");
if (R < 1 || R > MAX_SIZE)
fail("R is out of bounds.");

res = scanf("%d", &C);

if (res != 1)
fail("Failed to read C from input file.");
if (C < 1 || C > MAX_SIZE)
fail("C is out of bounds.");

res = scanf("%d", &N);

if (res != 1)
fail("Failed to read N from input file.");
if (N < 0 || N > MAX_N)
fail("N is out of bounds.");

auto t2 = clock();

init(R, C);

auto t3 = clock();

for (i = 0; i < N; i++)


{
res = scanf("%d", &type);

if (type == 1)
{
res = scanf("%d%d%lld", &P, &Q, &K);

update(P, Q, K);
}
else
if (type == 2)
{
res = scanf("%d%d%d%d", &P, &Q, &U, &V);

printf("%lld\n", calculate(P, Q, U, V));


}
else
fail("Invalid action type in input file.");
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------------


/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 6.859
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 795

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


Press any key to continue.
*/

Listing 7.4.4: checkerGame.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/05.st-4-random-1.in",
(char*)"game.out",
(char*)"../tests/05.st-4-random-1.out",
};

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

registerChecker("game", argc, argv);


compareRemainingLines();
}

7.4.3 *Rezolvare detaliată

7.5 Robots
Problema 5 - Robots 100 de puncte

Author: Vytautas Gruslys (Lithuania)

Frăţiorul Maritei şi-a lăsat jucăriile peste tot pe podeaua din sufragerie. Din fericire, Marita
a construit roboţi speciali pentru a strânge jucăriile. Ea are nevoie de ajutorul vostru pentru a
determina care roboţi ar trebui să culeagă jucăriile.
Există T jucării, fiecare cu o greutate - număr ı̂ntreg W i şi o dimensiune - număr ı̂ntreg
S i. Roboţii sunt de două tipuri: weak şi small.
ˆ Există A roboţi weak. Fiecare robot weak are o greutate limită X i, şi poate să care orice
jucărie cu greutatea strict mai mică decât X i. Dimensiunea jucăriei nu contează.
ˆ Există B roboţi small . Fiecare robot small are o dimensiune limită Y[i], şi poate să care
jucării cu dimensiunea strict mai mică decât Y i. Greutatea jucăriei nu contează.
Oricare robot al Maritei poate să culeagă exact o jucarie pe minut. Roboţi diferiţi pot căra
jucării diferite ı̂n acelaşi timp.
Sarcina voastră este să determinaţi dacă roboţii Maritei pot culege toate jucăriile şi ı̂n caz
afirmativ care este timpul minim in care acestea pot fi culese.
Exemple
Ca un prim exemplu, presupunem că există A 3 roboţi weak cu greutatea limită X 6, 2, 9,
B 2 roboţi small cu dimensiunea limită Y 4, 7, şi T 10 jucării după cum urmează:

Număr de jucării 0 1 2 3 4 5 6 7 8 9
Greutăţi 4 8 2 7 1 5 3 8 7 10
Dimensiuni 6 5 3 9 8 1 3 7 6 5
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 796

Cel mai scurt timp pentru a culege toate jucăriile este de trei minute:
Robotul Robotul Robotul Robotul Robotul
Weak 0 Weak 1 Weak 2 Small 0 Small 1
Primul minut Jucăria 0 Jucăria 4 Jucăria 1 Jucăria 6 Jucăria 2
Al doilea minut Jucăria 5 Jucăria 3 Jucăria 8
Al treilea minut Jucăria 7 Jucăria 9
Ca un al doilea exemplu, presupunem că există A 2 roboţi weak cu greutatea limită X
2, 5, B 1 robot small cu dimensiunea limită Y 2, şi T 3 jucării după cum urmează:
Număr jucărie 0 1 2
Greutăţi 3 5 2
Dimensiuni 1 3 2
Niciun robot nu poate culege jucăria de greutate 5 si dimensiune 3 deci este imposibil ca roboţii
să culeaga toate jucăriile.
Implementare
Trebuie să submitaţi un fişier care implementează funcţia putaway() după cum urmează:
Funcţia voastră: putaway()
C/C++

int putaway(int A, int B, int T,


int X[], int Y[], int W[], int S[]);

Pascal

function putaway(A, B, T : LongInt;


var X, Y, W, S : array of LongInt) : LongInt;

Descriere
Această funcţie trebuie să returneze cel mai mic număr de minute necesar pentru ca roboţii să
culeagă toate jucăriile , sau să returneze -1 dacă acest lucru nu este posibil.
Parametri
ˆ A: Numărul de roboţi weak.
ˆ B: Numărul de roboţi small.
ˆ T: Numărul de jucării.
ˆ X: Un vector de ı̂ntregi de lungime A conţinând greutăţile limită pentru fiecare robot weak.
ˆ Y: Un vector de ı̂ntregi de lungime B conţinând dimensiunile limită pentru fiecare robot
small.
ˆ W: Un vector de ı̂ntregi de lungime T conţinând greutatea fiecărei jucării.
ˆ S: Un vector de ı̂ntregi de lungime T conţinând dimensiunea fiecărei jucării.
ˆ Returnează: Cel mai mic număr de minute ı̂n care se pot culege toate jucăriile sau -1 dacă
jucăriile nu pot fi culese.

Exemple
Aici aveţi descris primul exemplu:
Parametri Valori
A 3
B 2
T 10
X [6, 2, 9]
Y [4, 7]
W [4, 8, 2, 7, 1, 5, 3, 8, 7, 10]
S [6, 5, 3, 9, 8, 1, 3, 7, 6, 5]
Valoare returnată 3
Aici aveţi descris al doilea exemplu
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 797

Parametri Valori
A 2
B 1
T 3
X [2, 5]
Y [2]
W [3, 5, 2]
S [1, 3, 2]
Valoare returnată 1
Restricţii
ˆ Limită de timp: 3 secunde
ˆ Limită de memorie limit: 64 MiB
ˆ 1 & T & 1, 000, 000
ˆ 0 & A, B & 50, 000 şi 1 & A  B
ˆ 1 & X i, Y i, W i, S i & 2, 000, 000, 000

Subtask-uri
Subtask Puncte Restricţii suplimentare
1 14 T 2 şi A  B 2 (Exact două jucării şi doi roboţi)
2 14 B 0 (toţi roboţii sunt weak)
3 25 T & 50, şi A  B & 50
4 37 T & 10, 000 şi A  B & 1, 000
5 10 (Niciuna)
Testare
Graderul de pe calculatorul vostru va citi datele de intrare din fişierul robots.in, care trebuie
să aibă următorul format:
ˆ linia 1: A B T
ˆ linia 2: X 0 ... X A  1
ˆ linia 3: Y 0 ... Y B  1
ˆ următoarele T linii: W i S i

Astfel, primul exemplu prezentat ar trebui sa primească datele de intrare in următorul format
3 2 10
6 2 9
4 7
4 6
8 5
2 3
7 9
1 8
5 1
3 3
8 7
7 6
10 5

Dacă A 0 sau B 0 linia corespunzătoare(linia 2 sau linia 3) trebuie sa fie goale.


Note de limbaj
C/C++ Trebuie să faceţi #include "robots.h".
Pascal Trebuie sa definiţi unit Robots. Toţi vectorii sunt indexaţi incepând de la 0 (nu de
la 1).
Vedeţi template-urile de soluţii de pe calculatorul vostru pentru exemple.
Timp maxim de executare/test: 3.0 secunde
Memorie: total 64 MB
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 798

7.5.1 Indicaţii de rezolvare

http://blog.brucemerry.org.za/2013/07/ioi-2013-day-2-analysis.html
As is commonly the case for problems that ask how long some agents need to achieve a goal,
the answer can be found by a binary search on the answer. So we need to decide whether the
robots can clear the floor in S seconds.
We can simplify the problem slightly by noting that for each toy, we only need to know how
many weak and how many small robots are able to move it, which can be found by binary searching
the two lists (after sorting them). Of course, if a toy cannot be moved by any robot, return -1.
Let’s first decide the actions of the weak robots, starting from the weakest. There will be some
set of toys it can handle. Since they effectively differ only in size, the weakest robot should work
from the largest downwards, so as to make the job of the small robots easier. Also, there is never
any reason for it to move fewer than S toys, unless it runs out. Now consider the 2nd-weakest
robot. There will be extra toys it can handle, plus any light toys that the weakest robot didn’t
have time for. Since the weakest robot is finished, the difference in weights are irrelevant, and the
2nd-weakest robot should again work in decreasing order of size amongst the toys it can handle.
The same process can be continued for the remaining weak robots.
Now consider the small robots, from largest to smallest. These can again take up to S toys,
starting from the largest remaining one. If a robot is unable to handle the largest remaining toy,
then S was too small.
Implementation can be done using a priority queue, implemented as a binary heap, representing
toys that are light enough to be handled by the current robot and ordered with the largest at the
head. The toys are initially sorted by weight. Each time a new weak robot is considered, new
elements are added from the list of toys to the priority queue, and the robot then removes items
starting from the head of the queue.
2
Assuming that T is larger than A, B, the running time will be O T log T  : one log T for
the binary search, the other for the priority queue operations. I think that it may be possible to
reduce this to something like O T log T log max A, B  using the right sort of data structure
for the priority queue (to allow S items to be removed in log time): something like an interval
tree for the number of toys of each size.

7.5.2 Coduri sursă

Listing 7.5.1: robot-7026.cpp


// https://oj.uz/submission/7026 1884 ms 22552 KB

#include <stdio.h>
#include <stdlib.h>
#include "robots.h"

#include<ctime>
#include<iostream>

#include<algorithm>
#include<set>
#include<vector>

int V[1000000];
std::pair<int,int> D[1000000];
int R[50000];
std::vector<int> E[50000];

int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
{
int i,j,l,r,mid;
for(i=0;i<T;i++)
{
D[i].first=W[i]+1;
D[i].second=S[i]+1;
}
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 799

std::sort(X,X+A);
std::sort(Y,Y+B);
std::sort(D,D+T);
for(i=0;i<T;i++)
if((!A||D[i].first>X[A-1])&&(!B||D[i].second>Y[B-1]))
return -1;
l=1;
r=T;
while(l<r)
{
mid=(l+r)/2;
for(i=0;i<T;i++)V[i]=0;
for(i=0;i<B;i++)E[i].clear();
for(i=0;i<T;i++)
{
j=std::lower_bound(Y,Y+B,D[i].second)-Y;
if(j<B)E[j].push_back(i);
}

j=0;
for(i=B-1;i>=0;i--)
{
j+=mid;
if(j>2e9)j=2e9;
while(j>0&&E[i].size())
{
V[E[i][E[i].size()-1]]=1;
E[i].pop_back();
j--;
}
}

for(i=0;i<A;i++)R[i]=0;
j=0;
for(i=0;i<T;i++)if(!V[i])
{
while(j<A&&(X[j]<D[i].first||R[j]==mid))j++;
if(j<A)
{
V[i]=1;
R[j]++;
}
else break;
}

if(i==T)r=mid;
else l=mid+1;
}
return r;
}

// ---------------- begin grader ----------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_A 50000


#define MAX_B 50000
#define MAX_T 1000000

static int X[MAX_A];


static int Y[MAX_B];
static int W[MAX_T];
static int S[MAX_T];

int main()
{
auto t1 = clock();

std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);


std::freopen("robot.out", "w", stdout);

int A, B, T, i;
int res;
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 800

res = scanf("%d", &A);

if (res != 1) fail("Failed to read A from input file.");


if (A < 0 || A > MAX_A) fail("A is out of bounds.");

res = scanf("%d", &B);

if (res != 1) fail("Failed to read B from input file.");


if (B < 0 || B > MAX_B) fail("B is out of bounds.");

res = scanf("%d", &T);

if (res != 1) fail("Failed to read T from input file.");


if (T < 1 || T > MAX_T) fail("T is out of bounds.");

for (i = 0; i < A; i++)


{
res = scanf("%d", &X[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < B; i++)


{
res = scanf("%d", &Y[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < T; i++)


{
res = scanf("%d%d", &W[i], &S[i]);

if (res != 2) fail("Failed to read data from input file.");


}

auto t2 = clock();

int answer = putaway(A, B, T, X, Y, W, S);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------------- end grader ----------------

/*
t2-t1 = 0.515
t3-t2 = 8.761
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/st5-random-highS-highW-clusters.in
robot.out
../tests/st5-random-highS-highW-clusters.out
----------------------
1
Correct
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 801

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


Press any key to continue.
*/

Listing 7.5.2: robot-7037.cpp


// https://oj.uz/submission/7037 508 ms 22552 KB

#include <stdio.h>
#include <stdlib.h>
#include "robots.h"

#include<ctime>
#include<iostream>

#include<algorithm>
#include<set>
#include<vector>

int V[1000000];
std::pair<int,int> D[1000000];
int R[50000];
std::vector<int> E[50000];

int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
{
int i,j,k,l,r,mid;
for(i=0;i<T;i++)
{
D[i].first=W[i]+1;
D[i].second=S[i]+1;
}

std::sort(X,X+A);
std::sort(Y,Y+B);
std::sort(D,D+T);

for(i=0;i<T;i++)
if((!A||D[i].first>X[A-1])&&(!B||D[i].second>Y[B-1]))
return -1;

for(i=0;i<B;i++)E[i].clear();
for(i=0;i<T;i++)
{
j=std::lower_bound(Y,Y+B,D[i].second)-Y;
if(j<B)E[j].push_back(i);
}

l=1;
r=T;
while(l<r)
{
mid=(l+r)/2;
for(i=0;i<T;i++)V[i]=0;
j=0;
for(i=B-1;i>=0;i--)
{
j+=mid;
if(j>2e9)j=2e9;
for(k=E[i].size()-1;k>=0&&j>0;k--)
{
V[E[i][k]]=1;
j--;
}
}

for(i=0;i<A;i++)R[i]=0;
j=0;
for(i=0;i<T;i++)if(!V[i])
{
while(j<A&&(X[j]<D[i].first||R[j]==mid))j++;
if(j<A)
{
V[i]=1;
R[j]++;
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 802

}
else break;
}

if(i==T)r=mid;
else l=mid+1;
}
return r;
}

// ---------------- begin grader ----------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_A 50000


#define MAX_B 50000
#define MAX_T 1000000

static int X[MAX_A];


static int Y[MAX_B];
static int W[MAX_T];
static int S[MAX_T];

int main()
{
auto t1 = clock();

std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);


std::freopen("robot.out", "w", stdout);

int A, B, T, i;
int res;

res = scanf("%d", &A);

if (res != 1) fail("Failed to read A from input file.");


if (A < 0 || A > MAX_A) fail("A is out of bounds.");

res = scanf("%d", &B);

if (res != 1) fail("Failed to read B from input file.");


if (B < 0 || B > MAX_B) fail("B is out of bounds.");

res = scanf("%d", &T);

if (res != 1) fail("Failed to read T from input file.");


if (T < 1 || T > MAX_T) fail("T is out of bounds.");

for (i = 0; i < A; i++)


{
res = scanf("%d", &X[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < B; i++)


{
res = scanf("%d", &Y[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < T; i++)


{
res = scanf("%d%d", &W[i], &S[i]);

if (res != 2) fail("Failed to read data from input file.");


}

auto t2 = clock();

int answer = putaway(A, B, T, X, Y, W, S);


CAPITOLUL 7. IOI 2013 7.5. ROBOTS 803

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------------- end grader ----------------

/*
t2-t1 = 0.531
t3-t2 = 1.453
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/st5-random-highS-highW-clusters.in
robot.out
../tests/st5-random-highS-highW-clusters.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 7.5.3: robot-17227.cpp


// https://oj.uz/submission/17227 449 ms 36440 KB

#include <bits/stdc++.h>

using namespace std;

#define type(x) __typeof((x).begin())


#define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); it++)

typedef long long ll;


typedef pair < int, int > ii;

const int inf = 1e9 + 333;


const ll linf = 1e18 + inf;

#include "robots.h"

const int N = 1e6 + 5;

int n;
int A, B;
int X[N], Y[N];
ii a[N];
int id[N], root[N], sizev[N];

vector < int > v;

int dsu(int x)
{
if(x == root[x])
return x;
return root[x] = dsu(root[x]);
}
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 804

bool f(int x)
{
v.clear();
for(int i = 0; i <= A; i++)
{
root[i] = i;
sizev[i] = x;
}

for(int i = n - 1; i >= 0; i--)


{
int go = dsu(id[i]);
if(go < A)
{
sizev[go]--;
if(!sizev[go])
{
root[go] = go + 1;
}
}
else
{
v.push_back(a[i].second);
}
}

int ptr = 0;
for(int i = B - 1; i >= 0; i--)
{
int rem = x;
while(ptr < v.size() and rem and Y[i] > v[ptr])
{
rem--;
ptr++;
}
}
return ptr == v.size();
}

int putaway(int A, int B, int n, int X[], int Y[], int W[], int S[])
{
:: n = n;
:: A = A;
:: B = B;

sort(X, X + A);
sort(Y, Y + B);

for(int i = 0; i < A; i++)


:: X[i] = X[i];

for(int i = 0; i < B; i++)


:: Y[i] = Y[i];

for(int i = 0; i < n; i++)


{
a[i] = ii(S[i], W[i]);
}

sort(a, a + n);

for(int i = 0; i < n; i++)


swap(a[i].first, a[i].second);

for(int i = 0; i < n; i++)


{
id[i] = upper_bound(X, X + A, a[i].first) - X;
}

if(!f(n))
return -1;

int l = 1, r = n;
while(l < r)
{
int m = l + r >> 1;
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 805

if(f(m))
r = m;
else
l = m + 1;
}
return l;
}

// ---------------- begin grader ----------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_A 50000


#define MAX_B 50000
#define MAX_T 1000000

static int XX[MAX_A];


static int YY[MAX_B];
static int WW[MAX_T];
static int SS[MAX_T];

int main()
{
auto t1 = clock();

std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);


std::freopen("robot.out", "w", stdout);

int A, B, T, i;
int res;

res = scanf("%d", &A);

if (res != 1) fail("Failed to read A from input file.");


if (A < 0 || A > MAX_A) fail("A is out of bounds.");

res = scanf("%d", &B);

if (res != 1) fail("Failed to read B from input file.");


if (B < 0 || B > MAX_B) fail("B is out of bounds.");

res = scanf("%d", &T);

if (res != 1) fail("Failed to read T from input file.");


if (T < 1 || T > MAX_T) fail("T is out of bounds.");

for (i = 0; i < A; i++)


{
res = scanf("%d", &XX[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < B; i++)


{
res = scanf("%d", &YY[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < T; i++)


{
res = scanf("%d%d", &WW[i], &SS[i]);

if (res != 2) fail("Failed to read data from input file.");


}

auto t2 = clock();

int answer = putaway(A, B, T, XX, YY, WW, SS);

auto t3 = clock();
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 806

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------------- end grader ----------------

/*
t2-t1 = 2.005
t3-t2 = 1.218
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/st5-random-highS-highW-clusters.in
robot.out
../tests/st5-random-highS-highW-clusters.out
----------------------
1
Correct

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


Press any key to continue.
*/

Listing 7.5.4: robot-96364.cpp


// https://oj.uz/submission/96364 468 ms 27360 KB

#include "robots.h"
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


const int maxt = 1e6+50;

vector<pair<int,int>> tw,ts,w;
int t;
bool used[maxt];

bool can(int md)


{
for(int x=0;x<t;x++)
used[x] = 0;
int i = 0,j = 0;
for(int x=0;x<w.size();x++)
{
if(w[x].second == 1)
{
int cur = w[x].first;
int use = 0;
while(use < md && j < t && cur > ts[j].first)
{
if(used[ts[j].second])
{
j++;
continue;
}
used[ts[j].second] = 1;
j++;
use++;
}
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 807

}
else
{
int cur = w[x].first;
int use = 0;
while(use < md && i < t && cur > tw[i].first)
{
if(used[tw[i].second])
{
i++;
continue;
}
used[tw[i].second] = 1;
i++;
use++;
}
}
}

for(int x=0;x<t;x++)
if(!used[x])
return 0;
return 1;
}

int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
{
t = T;
for(int i=0;i<A;i++)w.push_back({X[i],0});
for(int i=0;i<B;i++)w.push_back({Y[i],1});
for(int i=0;i<T;i++)tw.push_back({W[i],i});
for(int i=0;i<T;i++)ts.push_back({S[i],i});

sort(w.begin(),w.end());
sort(tw.begin(),tw.end());
sort(ts.begin(),ts.end());

int md,lo=0,hi=T,ans=-1;
while(lo <= hi)
{
md = (lo+hi)/2;
if(can(md))
{
ans = md;
hi = md-1;
}
else
lo = md+1;
}
return ans;
}

// ---------------- begin grader ----------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_A 50000


#define MAX_B 50000
#define MAX_T 1000000

static int X[MAX_A];


static int Y[MAX_B];
static int W[MAX_T];
static int S[MAX_T];

int main()
{
auto t1 = clock();

std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);


std::freopen("robot.out", "w", stdout);

int A, B, T, i;
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 808

int res;

res = scanf("%d", &A);

if (res != 1) fail("Failed to read A from input file.");


if (A < 0 || A > MAX_A) fail("A is out of bounds.");

res = scanf("%d", &B);

if (res != 1) fail("Failed to read B from input file.");


if (B < 0 || B > MAX_B) fail("B is out of bounds.");

res = scanf("%d", &T);

if (res != 1) fail("Failed to read T from input file.");


if (T < 1 || T > MAX_T) fail("T is out of bounds.");

for (i = 0; i < A; i++)


{
res = scanf("%d", &X[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < B; i++)


{
res = scanf("%d", &Y[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < T; i++)


{
res = scanf("%d%d", &W[i], &S[i]);

if (res != 2) fail("Failed to read data from input file.");


}

auto t2 = clock();

int answer = putaway(A, B, T, X, Y, W, S);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------------- end grader ----------------

/*
t2-t1 = 2
t3-t2 = 2.453
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/st5-random-highS-highW-clusters.in
robot.out
../tests/st5-random-highS-highW-clusters.out
----------------------
1
Correct
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 809

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


Press any key to continue.
*/

Listing 7.5.5: checkerRobot.cpp


// https://oj.uz/submission/96364 468 ms 27360 KB

#include "robots.h"
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


const int maxt = 1e6+50;

vector<pair<int,int>> tw,ts,w;
int t;
bool used[maxt];

bool can(int md)


{
for(int x=0;x<t;x++)
used[x] = 0;
int i = 0,j = 0;
for(int x=0;x<w.size();x++)
{
if(w[x].second == 1)
{
int cur = w[x].first;
int use = 0;
while(use < md && j < t && cur > ts[j].first)
{
if(used[ts[j].second])
{
j++;
continue;
}
used[ts[j].second] = 1;
j++;
use++;
}
}
else
{
int cur = w[x].first;
int use = 0;
while(use < md && i < t && cur > tw[i].first)
{
if(used[tw[i].second])
{
i++;
continue;
}
used[tw[i].second] = 1;
i++;
use++;
}
}
}

for(int x=0;x<t;x++)
if(!used[x])
return 0;
return 1;
}

int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
{
t = T;
for(int i=0;i<A;i++)w.push_back({X[i],0});
for(int i=0;i<B;i++)w.push_back({Y[i],1});
for(int i=0;i<T;i++)tw.push_back({W[i],i});
for(int i=0;i<T;i++)ts.push_back({S[i],i});
CAPITOLUL 7. IOI 2013 7.5. ROBOTS 810

sort(w.begin(),w.end());
sort(tw.begin(),tw.end());
sort(ts.begin(),ts.end());

int md,lo=0,hi=T,ans=-1;
while(lo <= hi)
{
md = (lo+hi)/2;
if(can(md))
{
ans = md;
hi = md-1;
}
else
lo = md+1;
}
return ans;
}

// ---------------- begin grader ----------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

#define MAX_A 50000


#define MAX_B 50000
#define MAX_T 1000000

static int X[MAX_A];


static int Y[MAX_B];
static int W[MAX_T];
static int S[MAX_T];

int main()
{
auto t1 = clock();

std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);


std::freopen("robot.out", "w", stdout);

int A, B, T, i;
int res;

res = scanf("%d", &A);

if (res != 1) fail("Failed to read A from input file.");


if (A < 0 || A > MAX_A) fail("A is out of bounds.");

res = scanf("%d", &B);

if (res != 1) fail("Failed to read B from input file.");


if (B < 0 || B > MAX_B) fail("B is out of bounds.");

res = scanf("%d", &T);

if (res != 1) fail("Failed to read T from input file.");


if (T < 1 || T > MAX_T) fail("T is out of bounds.");

for (i = 0; i < A; i++)


{
res = scanf("%d", &X[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < B; i++)


{
res = scanf("%d", &Y[i]);

if (res != 1) fail("Failed to read data from input file.");


}

for (i = 0; i < T; i++)


CAPITOLUL 7. IOI 2013 7.6. WOMBATS 811

{
res = scanf("%d%d", &W[i], &S[i]);

if (res != 2) fail("Failed to read data from input file.");


}

auto t2 = clock();

int answer = putaway(A, B, T, X, Y, W, S);

auto t3 = clock();

printf("%d\n", answer);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ---------------- end grader ----------------

/*
t2-t1 = 2
t3-t2 = 2.453
t4-t3 = 0

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


Press any key to continue.

argc = 4
checker
../tests/st5-random-highS-highW-clusters.in
robot.out
../tests/st5-random-highS-highW-clusters.out
----------------------
1
Correct

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


Press any key to continue.
*/

7.5.3 *Rezolvare detaliată

7.6 Wombats
Problema 6 - Wombats 100 de puncte

Author: Richard Peng (USA)

Oraşul Brisbane a fost preluat de wombaţi mutanţi uriaşi şi voi trebuie să conduceţi oamenii
zona sigură.
Străzile din Brisbane formează un grid. Există R străzi orizontale care merg de la est la vest,
numerotate cu 0, ..., R  1 ı̂n ordine de la nord la sud şi C străzi verticale care merg de la nord
la sud, numerotate cu 0, ..., C  1 de la vest la est, ca in figura urmatoare.
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 812

Wombaţii au invadat din nord şi oameni scapă prin sud. Oamenii pot fugi pe străzile orizontale
ı̂n ambele direcţii, dar pe străzile verticale ei vor putea fugi doar spre sud, spre zona sigură.
Intersecţia străzii orizontale P cu strada verticală Q este notată P, Q. Fiecare segment de
stradă dintre două intersecţii conţine un număr de wombaţi şi aceste numere se pot schimba ı̂n
timp.
Vi se cere să conduceţi anumite persoane din anumite intersecţii date din nord (de pe strada
orizontală 0) la anumite intersecţii din sud (pe strada orizontală R  1), folosind o rută care conţine
cât mai puţini wombaţi posibil.
Iniţial veţi primi dimensiunea gridului şi numărul de wombaţi de pe fiecare segment de stradă.
ı̂n continuare veţi primi o serie de E evenimente din unul din următoarele două tipuri:
ˆ change, care modifică numărul de wombaţi pe un anumit segment de strada
ˆ escape, prin care o anumită persoană soseşte la o amunită intersecţie dată de pe strada
orizontală 0 şi trebuie să găsiţi un traseu până la o anumită intersecţie dată de pe linia R  1
şi trece peste cât mai puţini wombaţi posibil.

Trebuie să trataţi aceste evenimente implementând rutinele init(), changeH(),


changeV() şi escape(), ı̂n modul descris mai jos.
Exemple

Imaginea de mai sus arată iniţial o hartă cu R 3 drumuri orizontale si C 4 drumuri


verticale, cu numarul de wombaţi marcaţi pe fiecare segment. Consideraţi următoarea serie de
evenimente:
ˆ O persoană soseşte la intersecţia A 0, 2 şi doreşte să scape la intersecţia B 2, 1.
Numărul minim de wombaţi cu care se ı̂ntâlneşte este 2, cum este indicat de linia punctată.
ˆ Altă persoană soseşte ı̂n intersecţia X 0, 3 şi doreşte să scape la intersecţia Y 2, 3.
Numărul minim de wombaţi cu care se ı̂ntâlneşte este 7, iarăşi indicat de linia punctată.
ˆ Două evenimente de modificare apar: numărul de wombaţi pe segmentul de top din drumul
vertical 0 se modifică ı̂n 5, iar numărul de wombaţi din segmentul de mijloc al drumului
orizontal 1 se modifică ı̂n 6. Vedeţi numerele ı̂ncercuite din imaginea de mai jos.

ˆ O a treia persoană soseşte ı̂n A 0, 2 şi doreşte să scape la intersecţia B 2, 1. Acum
numărul minim de wombaţi cu care se ı̂ntâlneşte este 5, cum este indicat de linia punctată.
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 813

Implementare
Va trebui sa submitaţi un fişier implementând procedurile init(), changeH() şi
changeV() şi funcţia escape(), după cum urmează:
Procedura vostră: init()
C/C++

void init(int R, int C, int H[5000][200], int V[5000][200]);

Pascal

type wombatsArrayType = array[0..4999, 0..199] of LongInt;


procedure init(R, C : LongInt; var H, V : wombatsArrayType);

Descriere
Această procedură vă dă configuraţia iniţiala a hărţii, şi vă permite să iniţializaţi eventualele
variabile globale şi structuri de date. Va fi apelată o singură dată, ı̂nainte de orice apel către
changeH(), changeV() sau escape().
Parametrii
ˆ R : Numărul de drumuri orizontale.
ˆ C : Numărul de drumuri verticale.
ˆ H : Array bidimensional de dimensiune R  C  1, unde H P Q vă dă numarul de
wombaţi de pe segmentul de drum orizontal din dintre intersecţiile P, Q şi P, Q  1.
ˆ V : Array bidimensional de dimensiune R  1  C, unde V P Q vă dă numărul de
wombaţi de pe segmentul de drum vertical dintre intersecţiile P, Q şi P  1, Q.

Procedura voastră: changeH():


C/C++

void changeH(int P, int Q, int W);

Pascal
procedure changeH(P, Q, W: LongInt);

procedure changeH(P, Q, W: LongInt);

Descriere
Acestă procedură va fi apelată atunci când numărul de wombaţi se modifică pe segmentul
orizontal dintre intersecţiile (P, Q) şi (P, Q + 1).
Parametrii
ˆ P : Indică care drum orizontal este afectat ( 0 P R - 1 ).
ˆ Q : Indică ı̂ntre care două drumuri verticale se află segmentul ( 0 Q C - 2 ).

Procedura voastră: changeV()


C/C++

void changeV(int P, int Q, int W);

Pascal

procedure changeV(P, Q, W: LongInt);

Descriere
Acestă procedură va fi apelată când numărul de wombaţi se modifică pe pe segmentul de drum
vertical dintre intersecţiile (P, Q) şi (P + 1, Q).
Parametrii
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 814

ˆ P : Indică ı̂ntre care două drumuri orizontale se află segmentul ( 0 P R - 2 ).


ˆ Q : Indică care drum este afectat ( 0 Q C - 1 ).
ˆ W : Numărul nou de wombaţi pe acest segment de drum ( 0 W 1,000 ).

Funcţia voastră: escape()


C/C++

int escape(int V1, int V2);

Pascal

function escape(V1, V2 : LongInt) : LongInt;

Descriere
Acestă funcţie trebuie să calculeze numărul minim de wombaţi pe care o persoană ı̂i va ı̂ntâlni
atunci când călătoreşte de la intersecţia (0, V1) la (R-1, V2).
Parametrii
ˆ V1 : Indică unde o persoană ı̂ncepe pe rândul orizontal 0 ( 0 V1 C-1 ).
ˆ V2 : Indică unde o persoana sfârşeşte pe rândul orizontal R-1 ( 0 V2 C-1 ).
ˆ Returnează: Numărul minim de wombaţi pe care persoana va fi necesar să ı̂i ı̂ntâlnească.

Exemplu de Sesiune
Următorul scenariu descrie exemplul de mai sus:
Function Call Returns
init(3, 4, [[0,2,5], [7,1,1], [0,4,0]], [[0,0,0,2], [0,3,4,7]])
escape(2,1) 2
escape(3,3) 7
changeV(0,0,5)
changeH(1,1,6)
escape(2,1) 5
Constrângeri
ˆ Limită de timp: 20 secunde
ˆ Limită de memorie: 256 MiB
ˆ 2 & R & 5 000
ˆ 1 & C & 200
ˆ Cel mult 500 de schimbări (apeluri la changeH() sau changeV())
ˆ Cel mult 200 000 apeluri către escape()
ˆ Cel mult 1 000 wombaţi pe fiecare segment ı̂n orice moment.

Subtaskuri
Subtask Punctaje Constrângeri Adiţionale a Inputului
1 9 C 1
2 12 R, C & 20, şi nu vor fi apeluri către changeH() sau changeV()
3 16 R, C & 100, şi vor fi cel mult 100 de apeluri către escape()
4 18 C 2
5 21 C & 100
6 24 (None)
Testare
Graderul de pe computerul vostru va citi input din fişierul wombats.in, care va fi ı̂n următorul
format:
ˆ linia 1: R C
ˆ linia 2: H 00 ... H 0C  2
ˆ ...
ˆ linia R  1: H R  10...H R  1C  2
ˆ linia R  2: V 00...V 0C  1
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 815

ˆ ...
ˆ linia 2R: V R  20...V R  2C  1
ˆ următoarea linie: E
ˆ următoarele E linii: un eveniment pe linie, ı̂n ordinea ı̂n care apar

Dacă C 1, liniile goale conţinând numărul de wombaţi pe drumurile orizontale (liniile 2 ...
R  1) nu sunt necesare.
Linia fiecărui eveniment trebuie să fie ı̂n una din formatele:
ˆ pentru a indica changeH(P, Q, W): 1 P Q W
ˆ pentru a indica changeV(P, Q, W): 2 P Q W
ˆ pentru a indica escape(V1, V2): 3 V 1 V 2

În acest fel, exemplul de mai sus trebuie să fie dat ı̂n următorul format:

3 4
0 2 5
7 1 1
0 4 0
0 0 0 2
0 3 4 7
5
3 2 1
3 3 3
2 0 0 5
1 1 1 6
3 2 1

Note de limbaj
C/C++ Trebuie să faceţi #include "wombats.h".
Pascal Trebuie să definiţi unit Wombats. Toţi vectorii sunt indexaţi de la 0 (nu de la 1).
Vedeţi template-urile de soluţii de pe calculatoarele voastre pentru exemple.
Timp maxim de executare/test: 20.0 secunde
Memorie: total 256 MB

7.6.1 Indicaţii de rezolvare

http://blog.brucemerry.org.za/2013/07/ioi-2013-day-1-analysis.html
I found this to be the most difficult of the tasks. Apart from being conceptually difficult, it
also required a reasonably amount of tuning, and my solution still takes over 10s in many cases.
The basis of the solution is to note that C is relatively small, and so it is feasible to precompute
the costs to get from any point on row X to any point on row Y , for some X and Y . Let’s write
such a table as rX, Y x. What’s less obvious is that it’s possible to combine rX, Y x and rY, Z x to
2
produce rX, Z x in O C  time. The trick is to use the fact that optimal paths won’t cross over
each other. Thus, if i $ j, X, i to Z, j  1 goes via Y, p, and X, i  1 to Z, j  goes via
Y, q , then the optimal path from X, i to Z, j  will go via Y, r where p & r & q. By iterating
in order of increasing j  i, it is possible to compute rX, Z x in quadratic time.
We can combine this observation with a segment tree: for each appropriate i and a, we maintain
ra 2i, a  1 2ix, computing it either directly (i 0) or by combining two smaller intervals as
above (where the upper bound exceeds R  1, it is clamped). Each change invalidates O log R of
2
these, so the time to update after a change is O C logR. Queries can be answered in O 1 time
using the root of the segment tree.
The catch with this approach is that it requires too much memory: we can’t even afford R
different rX, Y x tables. Instead of keeping rX, X  1x at the finest level of the segment tree, we
can instead store, say, r10X, 10X  10x for each X, and use 1/10th the memory. The cost is that
updating the base level of the segment tree will now take 10 times as long.
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 816

7.6.2 Coduri sursă

Listing 7.6.1: wombats-108194.cpp


// https://oj.uz/submission/108194 4263 ms 174140 KB

#include "wombats.h"
#include <bits/stdc++.h>

using namespace std;

const int r=5e3, c=200, bs=10, bc=r/bs, sts=1024;


int h[r][c-1], v[r][c], st[sts][c][c], o[c+1];

void upd(int l1, int r1, int i=1, int l2=0, int r2=bc-1)
{
if(l2==r2)
{
memset(st[i], 0x3f, sizeof(st[i]));
for(int j=0; j<c; ++j)
{
st[i][j][j]=0;
for(int k=l2*bs; k<(l2+1)*bs; ++k)
{
for(int l=1; l<c; ++l)
st[i][j][l]=min(st[i][j][l-1]+h[k][l-1],st[i][j][l]);
for(int l=c-1; l; --l)
st[i][j][l-1]=min(st[i][j][l]+h[k][l-1],st[i][j][l-1]);
for(int l=0; l<c; ++l)
st[i][j][l]+=v[k][l];
}
}
return;
}

int m2=(l2+r2)/2;
if(l1<=m2)
upd(l1, r1, 2*i, l2, m2);
if(m2<r1)
upd(l1, r1, 2*i+1, m2+1, r2);

memset(o, 0, 4*c);
for(int j1=0; j1<c; ++j1)
{
for(int j2=c-1; ˜j2; --j2)
{
array<int, 2> d{INT_MAX, 0};
for(int k=o[j2]; k<=o[j2+1]; ++k)
d=min(array<int,2>{st[2*i][j1][k]+st[2*i+1][k][j2], -k},d);
st[i][j1][j2]=d[0];
o[j2]=-d[1];
}
}
}

void init(int r, int c, int h[5000][200], int v[5000][200])


{
memset(::h, 0x3f, sizeof(::h));
for(int i=0; i<r; ++i)
for(int j=0; j<c-1; ++j)
::h[i][j]=h[i][j];
for(int i=0; i<r-1; ++i)
for(int j=0; j<c; ++j)
::v[i][j]=v[i][j];
o[::c]=::c-1;
upd(0, bc-1);
}

void changeH(int p, int q, int w)


{
h[p][q]=w;
upd(p/bs, p/bs);
}
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 817

void changeV(int p, int q, int w)


{
v[p][q]=w;
upd(p/bs, p/bs);
}

int escape(int a, int b)


{
return st[1][a][b];
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

static int H[5000][200];


static int V[5000][200];

int main()
{
auto t1 = clock();

std::freopen("../tests/100x100-bottom-changes-many-queries.in","r",stdin);
std::freopen("wombats.out", "w", stdout);

int R, C, E, P, Q, W, V1, V2, event, i, j;


int res;

res = scanf("%d%d", &R, &C);

for (i = 0; i < R; ++i)


for (j = 0; j < C-1; ++j)
res = scanf("%d", &H[i][j]);

for (i = 0; i < R-1; ++i)


for (j = 0; j < C; ++j)
res = scanf("%d", &V[i][j]);

auto t2 = clock();

init(R, C, H, V);

res = scanf("%d", &E);

auto t3 = clock();

for (i = 0; i < E; i++)


{
res = scanf("%d", &event);

if (event == 1)
{
res = scanf("%d%d%d", &P, &Q, &W);

changeH(P, Q, W);
}
else
if (event == 2)
{
res = scanf("%d%d%d", &P, &Q, &W);

changeV(P, Q, W);
}
else
if (event == 3)
{
res = scanf("%d%d", &V1, &V2);

printf("%d\n", escape(V1, V2));


}
else
fail("Invalid event type.");
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 818

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.031
t3-t2 = 26.213
t4-t3 = 18.542

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


Press any key to continue.
*/

Listing 7.6.2: wombats-108196.cpp


// https://oj.uz/submission/108196 3395 ms 174124 KB

#pragma GCC optimize("O3")


#include "wombats.h"
#include <bits/stdc++.h>

using namespace std;

const int r=5e3, c=200, bs=10, bc=r/bs, sts=1024;


int h[r][c-1], v[r][c], st[sts][c][c], o[c+1];

void upd(int l1, int r1, int i=1, int l2=0, int r2=bc-1)
{
if(l2==r2)
{
memset(st[i], 0x3f, sizeof(st[i]));
for(int j=0; j<c; ++j)
{
st[i][j][j]=0;
for(int k=l2*bs; k<(l2+1)*bs; ++k)
{
for(int l=1; l<c; ++l)
st[i][j][l]=min(st[i][j][l-1]+h[k][l-1],st[i][j][l]);
for(int l=c-1; l; --l)
st[i][j][l-1]=min(st[i][j][l]+h[k][l-1],st[i][j][l-1]);
for(int l=0; l<c; ++l)
st[i][j][l]+=v[k][l];
}
}
return;
}

int m2=(l2+r2)/2;
if(l1<=m2)
upd(l1, r1, 2*i, l2, m2);
if(m2<r1)
upd(l1, r1, 2*i+1, m2+1, r2);

memset(o, 0, 4*c);

for(int j1=0; j1<c; ++j1)


{
for(int j2=c-1; ˜j2; --j2)
{
array<int, 2> d{INT_MAX, 0};
for(int k=o[j2]; k<=o[j2+1]; ++k)
d=min(array<int,2>{st[2*i][j1][k]+st[2*i+1][k][j2], -k},d);
st[i][j1][j2]=d[0];
o[j2]=-d[1];
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 819

}
}
}

void init(int r, int c, int h[5000][200], int v[5000][200])


{
memset(::h, 0x3f, sizeof(::h));
for(int i=0; i<r; ++i)
for(int j=0; j<c-1; ++j)
::h[i][j]=h[i][j];
for(int i=0; i<r-1; ++i)
for(int j=0; j<c; ++j)
::v[i][j]=v[i][j];
o[::c]=::c-1;
upd(0, bc-1);
}

void changeH(int p, int q, int w)


{
h[p][q]=w;
upd(p/bs, p/bs);
}

void changeV(int p, int q, int w) {


v[p][q]=w;
upd(p/bs, p/bs);
}

int escape(int a, int b)


{
return st[1][a][b];
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

static int H[5000][200];


static int V[5000][200];

int main()
{
auto t1 = clock();

std::freopen("../tests/100x100-bottom-changes-many-queries.in", "r",stdin);
std::freopen("wombats.out", "w", stdout);

int R, C, E, P, Q, W, V1, V2, event, i, j;


int res;

res = scanf("%d%d", &R, &C);

for (i = 0; i < R; ++i)


for (j = 0; j < C-1; ++j)
res = scanf("%d", &H[i][j]);

for (i = 0; i < R-1; ++i)


for (j = 0; j < C; ++j)
res = scanf("%d", &V[i][j]);

auto t2 = clock();

init(R, C, H, V);

res = scanf("%d", &E);

auto t3 = clock();

for (i = 0; i < E; i++)


{
res = scanf("%d", &event);
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 820

if (event == 1)
{
res = scanf("%d%d%d", &P, &Q, &W);

changeH(P, Q, W);
}
else
if (event == 2)
{
res = scanf("%d%d%d", &P, &Q, &W);

changeV(P, Q, W);
}
else
if (event == 3)
{
res = scanf("%d%d", &V1, &V2);

printf("%d\n", escape(V1, V2));


}
else
fail("Invalid event type.");
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.041
t3-t2 = 9.078
t4-t3 = 8.622

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


Press any key to continue.
*/

Listing 7.6.3: wombats-118871.cpp


// https://oj.uz/submission/118871 3904 ms 183532 KB

#include "wombats.h"
#include <bits/stdc++.h>

using namespace std;

const int r=5e3, c=200, bs=10, bc=r/bs, sts=1024;


int h[r][c-1], v[r][c], st[sts][c][c], o[c+1];

void upd(int l1, int r1, int i=1, int l2=0, int r2=bc-1)
{
if(l2==r2)
{
memset(st[i], 0x3f, sizeof(st[i]));
for(int j=0; j<c; ++j)
{
st[i][j][j]=0;
for(int k=l2*bs; k<(l2+1)*bs; ++k)
{
for(int l=1; l<c; ++l)
st[i][j][l]=min(st[i][j][l-1]+h[k][l-1], st[i][j][l]);
for(int l=c-1; l; --l)
st[i][j][l-1]=min(st[i][j][l]+h[k][l-1],st[i][j][l-1]);
for(int l=0; l<c; ++l)
st[i][j][l]+=v[k][l];
}
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 821

}
return;
}

int m2=(l2+r2)/2;
if(l1<=m2)
upd(l1, r1, 2*i, l2, m2);
if(m2<r1)
upd(l1, r1, 2*i+1, m2+1, r2);
memset(o, 0, 4*c);
for(int j1=0; j1<c; ++j1)
{
for(int j2=c-1; ˜j2; --j2)
{
array<int, 2> d{INT_MAX, 0};
for(int k=o[j2]; k<=o[j2+1]; ++k)
d=min(array<int, 2>{st[2*i][j1][k]+st[2*i+1][k][j2],-k},d);
st[i][j1][j2]=d[0];
o[j2]=-d[1];
}
}
}

void init(int r, int c, int h[5000][200], int v[5000][200])


{
memset(::h, 0x3f, sizeof(::h));
for(int i=0; i<r; ++i)
for(int j=0; j<c-1; ++j)
::h[i][j]=h[i][j];
for(int i=0; i<r-1; ++i)
for(int j=0; j<c; ++j)
::v[i][j]=v[i][j];
o[::c]=::c-1;
upd(0, bc-1);
}

void changeH(int p, int q, int w)


{
h[p][q]=w;
upd(p/bs, p/bs);
}

void changeV(int p, int q, int w)


{
v[p][q]=w;
upd(p/bs, p/bs);
}

int escape(int a, int b)


{
return st[1][a][b];
}

// ------------- begin grader --------------

#define fail(s, x...) do { \


fprintf(stderr, s "\n", ## x); \
exit(1); \
} while(0)

static int H[5000][200];


static int V[5000][200];

int main()
{
auto t1 = clock();

std::freopen("../tests/100x100-bottom-changes-many-queries.in","r",stdin);
std::freopen("wombats.out", "w", stdout) ;

int R, C, E, P, Q, W, V1, V2, event, i, j;


int res;

res = scanf("%d%d", &R, &C);

for (i = 0; i < R; ++i)


CAPITOLUL 7. IOI 2013 7.6. WOMBATS 822

for (j = 0; j < C-1; ++j)


res = scanf("%d", &H[i][j]);

for (i = 0; i < R-1; ++i)


for (j = 0; j < C; ++j)
res = scanf("%d", &V[i][j]);

auto t2 = clock();

init(R, C, H, V);

res = scanf("%d", &E);

auto t3 = clock();

for (i = 0; i < E; i++)


{
res = scanf("%d", &event);

if (event == 1)
{
res = scanf("%d%d%d", &P, &Q, &W);

changeH(P, Q, W);
}
else
if (event == 2)
{
res = scanf("%d%d%d", &P, &Q, &W);

changeV(P, Q, W);
}
else
if (event == 3)
{
res = scanf("%d%d", &V1, &V2);

printf("%d\n", escape(V1, V2));


}
else
fail("Invalid event type.");
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------


/*
t2-t1 = 0.03
t3-t2 = 26.169
t4-t3 = 18.445

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


Press any key to continue.
*/

Listing 7.6.4: checkerWombats.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
CAPITOLUL 7. IOI 2013 7.6. WOMBATS 823

int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/100x100-bottom-changes-many-queries.in",
(char*)"wombats.out",
(char*)"../tests/100x100-bottom-changes-many-queries.out",
};

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

registerChecker("wombats", argc, argv);


compareRemainingLines();
}

7.6.3 *Rezolvare detaliată


Capitolul 8
38
IOI 2012

8.1 Pebbling odometer


Problema 1 - Pebbling odometer 100 de puncte

Author: Michal Foris̆ek

Leonardo a inventat odometrul: o căruţă care putea măsura distanţele aruncând pietricele
după cum se roteau roţile acesteia. Numărând pietricelele aflam numărul de rotiri al roţii, ceea
ce permitea utilizatorului să calculeze distanţă parcursa de odometru. Ca programatori, noi
am adăugat un soft de control odometrului, extinzăndu-i funcţionalităţile. Sarcina ta este să
programezi odometrul respectând regulile specificate mai jos.
Zona de funcţionare
Odometrul se mişcă pe o matrice pătratică imaginară având 256  256 celule. Fiecare celulă
poate conţine cel mult 15 pietricele şi se identifică printr-o pereche de coordonate (rând, coloană),
unde fiecare coordonată se află ı̂n intervalul 0, ..., 255. Pentru o celuă i, j , celulele adiacente
ei sunt (dacă există) i  1, j , i  1, j , i, j  1 şi i, j  1. Oricare celulă aflată pe primul
sau ultimul rând, sau pe prima sau ultima coloană, este numită frontieră. Odometrul ı̂ncepe
ı̂ntotdeauna din celula (0,0) (colţul nord-vest), orientat spre nord.
Comenzile de bază
Odometrul poate fi programat folosind următoarele comenzi.
ˆ left - se roteşte cu 90 de grade spre stânga (invers acelor de ceasornic) şi rămâne ı̂n celula
curentă (de exemplu: dacă ı̂nainte a fost orientat spre sud, după executarea comenzii va fi
orientat spre est).
ˆ right - se roteşte cu 90 de grade spre dreapta (ı̂n sensul acelor de ceasornic) şi rămâne ı̂n
celula curentă (de exemplu: dacă ı̂naintă a fost orientat spre vest, după executarea comenzii
va fi orientat spre nord).
ˆ move - se mută exact o unitate ı̂nainte (ı̂n direcţia ı̂n care este orientat odometrul) ı̂n celula
adiacentă. Dacă o astfel de celulă nu există (adică frontiera ı̂n această direcţie a fost deja
atinsă) atunci comanda nu are niciun efect.
ˆ get - elimină o pietricică din celula curentă. Dacă celula curentă nu are nicio pietricică,
atunci comanda nu are niciun efect.
ˆ put - adaugă o pietricică ı̂n celula curentă. Dacă celula curentă are deja 15 pietricele, atunci
comanda nu are niciun efect. Odometrul nu rămâne niciodată fără pietricele.
ˆ halt - ı̂ncheie execuţia programului.

Odometrul execută comenzile ı̂n ordinea ı̂n care sunt date ı̂n program. Programul trebuie să
conţină cel mult o comandă pe linie. Liniile goale vor fi ignorate. Simbolul # indică un comentariu;
orice text care urmează până la sfârşitul liniei, este ignorat. Dacă odometrul ajunge la sfârşitul
programului, execuţia se ı̂ncheie.
Examplul 1
38
aur: Adrian Budău, ICHB (Bucureşti)
. aur: Vlad Alexandru Gavrilă, ICHB (Bucureşti)
. aur: Rareş Darius Buhai, Liviu Rebreanu (Bistriţa)
. bronz: Radu Ştefan Voroneanu, IL Caragiale (Ploieşti).

824
CAPITOLUL 8. IOI 2012 8.1. PEBBLING ODOMETER 825

Fie următorul program pentru odometru. El duce odometrul ı̂n celula (0, 2), orientat spre est.
(remarcaţi că primul move este ignorat, deoarece odometrul este ı̂n colţul nord-vest orientat spre
nord).

move # fara efect


right
# acum odometrul este orientat spre est
move
move

Etichete, frontiere şi pietricele


Pentru a modifica funcţionarea programului ı̂n funcţie de starea curentă, poţi utiliza etichete,
care sunt stringuri case-sensitive având cel mult 128 de simboluri din a, ..., z, A, ..., Z, 0, ..., 9.
Noile comenzi referitoare etichetelor sunt listate mai jos. ı̂n descrierea de mai jos, L reprezintă
orice etichetă validă.
ˆ L: (adică L urmat de două puncte ’:’) - declară locaţia ı̂n interiorul programului a etichetei
L. Toate declaraţiile etichetelor trebuie să fie unice. Declararea unei etichete nu are niciun
efect asupra odometrului.
ˆ jump L - continuă execuţia prin salt necondiţionat la eticheta L.
ˆ border L - continuă execuţia sărind la linia cu eticheta L, doar dacă odometrul se află pe o
celulă frontieră orientat spre exteriorul matricei (adică o instrucţiune move nu ar avea efect);
altfel, execuţia continuă normal iar această comandă nu are niciun efect.
ˆ pebble L - continuă execuţia sărind la linia cu eticheta L, doar dacă odometrul se află pe o
celulă care conţine măcar o pietricică; altfel, execuţia continuă normal iar această comandă
nu are niciun efect.

Exemplul 2
Următorul program localizează prima (cea mai din vest) pietricică din rândul 0 şi se opreşte
acolo; dacă nu există nicio pietricică ı̂n rândul 0, programul se opreşte pe frontieră la sfârşitul
rândului. El foloseşte două etichete leonardo şi davinci.

right
leonardo:
pebble davinci \# pietricica gasita
border davinci \# sfarsitul randului
move
jump leonardo
davinci:
halt

Odometrul porneşte rotindu-se spre dreapta. Bucla ı̂ncepe cu declararea etichetei leonardo
şi se termină cu comanda jump leonardo. ı̂n buclă, odometrul verifică dacă se află pietricele
sau dacă se află pe frontieră la sfârşitul rândului; dacă nu, odometrul execută comanda move din
celula curentă 0, j  ı̂n celula adiacenă 0, j  1 cât timp cea din urmă există. (Comanda halt
nu este strict necesara aici deoarece programul se ı̂ncheie oricum).
Enunţ
Tu trebuie să trimiţi un program ı̂n limbajul odometrului, cum a fost descris mai sus, care
să facă odometrul să se comporte conform aşteptărilor. Fiecare subtask (vezi mai jos) specifică
o comportare pe care odometrul trebuie să o ı̂ndeplinească şi restricţiile pe care soluţia trimisă
trebuie să le ı̂ndeplinească. Restricţiile se refer urătoarele două aspecte.
ˆ Dimensiunea programului - programul trebuie să fie suficient de scurt. Dimensiunea progra-
mului este numărul de comenzi pe care ı̂l conţine. Declaraţiile de etichete, comentariile şi
liniile libere nu se contorizează” ı̂n dimensiune.
ˆ Lungimea execuţiei - programul trebuie să se termine suficient de repede. Lungimea execuţiei
este numărul de paşi: fiecare execuţie a unei comenzi este numărată ca un pas, chiar dacă
comanda are efect sau nu; declararea etichetelor, comentariile şi liniile libere nu se numără
ca paşi.
CAPITOLUL 8. IOI 2012 8.1. PEBBLING ODOMETER 826

În primul exemplu, programul are dimensiunea 4 şi lungimea execuţiei 4. În al doilea exem-
plu, programul are dimensiunea 6 şi, când este executat pe o matrice având o singură pietricică
ı̂n celula (0,10), lungimea execuţiei este de 43 de paşi: right, 10 iteraţii prin buclă, fiecare
conţinând câte 4 paşi (pebble davinci; border davinci; move; jump leonardo), şi la
final, pebble davinci şi halt.
Subtask 1 [9 puncte]
La ı̂nceput se află x pietricele ı̂n celula (0,0) şi y pietricele ı̂n celula (0,1), ı̂n timp ce toate
celalte celule sunt libere. Reţineţi că pot fi cel mult 15 pietricele ı̂ntr-o celulă. Scrie un program
care se ı̂ncheie cu odometrul ı̂n celula (0,0) dacă x & y, sau ı̂n celula (0,1) ı̂n caz contrar (Nu
contează cum este orientat odometrul la sfârşit sau câte pietricele există, ı̂n final, ı̂n matrice şi
nici unde sunt aşezate).
Limite: Dimensiunea programului & 100, lungimea execuţiei & 1 000.
Subtask 2 [12 puncte]
La fel ca subtask-ul anterior, dar când programul se ı̂ncheie, celula (0,0) trebuie să conţină
exact x pietricele iar celula (0,1) trebuie să conţină exact y pietricele.
Limite: Dimensiunea programului & 200, lungimea execuţiei & 2 000.
Subtask 3 [19 puncte]
Există exact două pietricele pe rândul 0: una ı̂n celula 0, x, iar cealalta ı̂n celula 0, y ; x şi
y sunt distincte, iar x  y este par. Scrie un program care lasă odometrul ı̂n celula (0, (x+y)/2),
adică exact la mijlocul dintre cele două celule care conţin pietricele. Starea finală a matricii este
irelevantă.
Limite: Dimensiunea programului & 100, lungimea execuţiei & 200 000.
Subtask 4 [pana la 32 de puncte]
Se află cel mult 15 pietricele ı̂n matrice, oricare două ı̂n celule diferite. Scrie un program care
le adună pe toate ı̂n colţul nord-vest; mai exact, dacă la ı̂nceput existau x pietricele ı̂n matrice,
atunci la sfârşit trebuie să existe exact x pietricele ı̂n celula (0,0) şi nici o altă pietricică ı̂n celelalte
celule.
Scorul acestui subtask depinde de lungimea execuţiei a programului trimis. Mai exact, dacă L
este lungimea de execuţie maximă a unui test, scorul tău va fi:
ˆ 32 de puncte dacă L & 200 000;
ˆ 32 - 32 log10 L©200 000 de puncte dacă 200 000 ¡ L $ 2 000 000;
ˆ 0 puncte dacă L ' 2 000 000.

Limite: dimensiunea programului & 200.


Subtask 5 [până la 28 de puncte]
Pot exista oricâte pietricele ı̂n fiecare celulă a matrice (desigur, ı̂ntre 0 şi 15). Scrie un program
care găseşte minimul, adică, care se termină cu odometrul ı̂n celula i, j  astfel ı̂ncât oricare altă
celulă conţine cel puţin la fel de multe pietricele ca şi celula i, j . După rularea programului,
numărul pietricelelor din fiecare celulă trebuie să fie acelaşi ca ı̂nainte de rularea programului.
Scorul acestui subtask depinde de dimensiunea programului P a programului trimis. Mai exact,
scorul tău va fi:
ˆ 28 de puncte dacă P & 444;
ˆ 28 - 28 log10 P ©444 de puncte dacă 444 ¡ P ¡ 4 440;
ˆ 0 puncte dacă P ' 4 440.

Limite: lungimea execuţiei & 44 400 000.


Detalii de implementare
Trebuie să trimiţi exact un fişier pentru fiecare subtask, respectând regulile de sintaxă spec-
ificate mai sus. Fiecare fişier trimis poate avea cel mult 5 MB. Pentru fiecare subtask, codul
odometrului va fi testat pe câteva teste şi vei primi feedback ı̂n legătură cu resursele utilizate de
codul tău. ı̂n cazul ı̂n care codul nu respectă sintaxa şi astfel este imposibil de testat, tu vei primi
informaţii despre sintaxa de eroare.
Nu este necesar ca submisiile să conţină programe pentru odometru la toate subtask-urile.
Dacă submisia curentă nu conţine programul odometrului pentru subtask-ul x, cea mai recentă
CAPITOLUL 8. IOI 2012 8.1. PEBBLING ODOMETER 827

submisie pentru task-ul x va fi automat inclusă; dacă nu există deja trimis un astfel de program,
acest subtask va primi 0 puncte pentru acea submisie.
Ca de obicei, punctajul unui submit se calculează ı̂nsumand scorurile subtask-urilor, iar scorul
final al sarcinii este punctajul maxim din rândul submit-urilor precedente şi al ultimului.
Simulator
În scopuri de testare, vi se oferă un simulator de odometru, căruia ii poţi furniza programele tale
şi matricele de intrare. Programele odometrului vor fi scrise ı̂n formatul folosit pentru submit-uri
(de exemplu, unul din cele descrise mai sus).
Descrierea matricei va fi dată cu ajutorul următorului format: fiecare linie a fişierului trebuie
să conţină trei numere R, C şi P , semnificand faptul că celula din randul R şi coloana C conţine
P pietricele. Se presupune că celulele ce nu sunt specificate ı̂n descrierea matriceise nu conţin
pietricele. Pentru exemplificare se consideră următorul fişier:

0 10 3
4 5 12

Matricea descrisă de acest fişier trebuie să conţină 15 pietricele: 3 ı̂n celulă (0,10) şi 12 ı̂n
celulă (4,5).
Poţi invoca simulatorul de testare prin apelarea programului simulator.py ı̂n directorul
sarcinei tale, transmiţandui ca argument numele fişierului de program ca argument. Programul
de simulare va accepta ı̂n linia de comandă următoarele opţiuni:
ˆ -h va oferi o scurtă trecere ı̂n revistă a opţiunilor disponibile;
ˆ -g GRID_FILE ı̂ncarcă descrierea matricei din fişierul GRID_FILE (implicit: matricrea
vidă);
ˆ -s GRID_SIDE setează dimensiunea matricei ca fiind egală cu GRID_SIDE  GRID_SIDE
(implicit 256, aşa cum este specificat ı̂n problemă); utilizarea unei matrice mai mici ar putea
fi utilă ı̂n cazul depănarii programului;
ˆ -m STEPS limitează numărul de paşi executaţi ı̂n procesul de simulare la cel mult STEPS;
ˆ -c intră ı̂n modul de compilare; ı̂n modul de compilare, simulatorul returnează exact aceiaşi
ieşire, dar ı̂n loc de a face o simulare cu Python, el generează şi compilează un mic program
ı̂n limbajul C. Acest lucru duce la cheltuieli mai mari la start, dar dă rezultate mult mai
rapid; se recomandă să-l foşoseşti atunci când se preconizează ca programul tau va rula mai
mult de circa 10 000 000 de paşi.

Numărul de submit-uri
Numărul maxim de submit-uri permise pentru această sarcină este de 128.
Timp maxim de executare/test: 2.0 secunde
Memorie: total 1024 MB

8.1.1 Indicaţii de rezolvare

As an illustrative example, we give directly the solution of Subtask 5, where code sharing is
employed. Note that finding the minimum is not complicated, removing one pebble per cell.
However, all the removed pebbles should be put back to their cells, and this complicates the
solution.
Subtask 5: solution generator in Python

Listing 8.1.1: odometer.py


1 # For each possible minimum value (except 15),
2 # look for a cell that holds that many pebbles.
3 # Various optimizations reduce the number of instructions.
4 # The first step is looking for zeroes.
5 print "jump 0_scan_all"
6 for i in xrange(0,15):
7 # This section tries to move on the next row after a single scan. If it hits
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 828

8 # the border, we’re ready to search for the next candidate minimum.
9 print "%d_test_next_row:" % i
10 print "right"
11 print "border %d_scan_all" % (i+1)
12 print "move"
13 print "right"
14 print "%d_test_next_row_l1:" % i
15 print "border %d_test_next_row_l1end" % i
16 print "move"
17 print "jump %d_test_next_row_l1" % i
18 print "%d_test_next_row_l1end:" % i
19 print "right"
20 # Start the evaluation of the next row of the grid.
21 print "%d_scan_all:" % i
22 print "right"
23 print "%d_test_scan_row:" % i
24 for j in xrange(i):
25 print "get"
26 print "pebble %d_test_scan_row_continue" % i
27 print "jump end_%d" % i
28 print "%d_test_scan_row_continue:" % i
29 for j in xrange(i):
30 print "put"
31 # When it hits the border, try to go to the next row and go back to the
32 # first column.
33 print "border %d_test_next_row" % i
34 print "move"
35 print "jump %d_test_scan_row" %i
36 # When you find the minimum, you can share the code that puts back the pebbles
37 # in the cell.
38 for i in xrange(14,0,-1):
39 print "end_%d:" % i
40 print "put"
41 print "end_0:"
42 # If all the cells have 15 pebbles, any position is ok.
43 print "15_scan_all:"

8.1.2 *Coduri sursă

8.1.3 *Rezolvare detaliată

8.2 Parachute rings


Problema 2 - Parachute rings 100 de puncte

Author: Michal Foris̆ek

O versiune timpurie şi destul de sofisticată de ceea ce noi acum numim paraşută este descrisă
ı̂n lucrarea lui Leonardo Codex atlanticus (cca. 1485). Paraşuta lui Leonardo constă dintr-o pânză
cerată, ţinutâ deschisă cu ajutorul unei structuri din lemn ı̂n formă de piramidă.
Inelele legate
Cu 500 de ani mai târziu, paraşutistul Adrian Nicolae a testat proiectul lui Leonardo. ı̂n acest
scop, o structură uşoară modernă leagă paraşuta lui Leonardo de corpul uman. Această structură
este formată din carabine ı̂n formă de inele, confecţionate dintr-un material rezistent. Inelele pot
fi uşor legate ı̂ntre ele, iar fiecare inel poate fi ı̂nchis sau deschis din nou. Numim lanţ o secvenţă
de unul sau mai multe inele legate, ı̂n care fiecare inel este legat cu alte două inele, cu excepţia
primului şi ultimului, care sunt legate cu cate un singur inel, după cum este ilustrat mai jos. Prin
definiţie, un singur inel este, de asemenea, un lanţ.
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 829

Evident, ı̂ntrucât un inel poate fi legat nu numai cu două, dar cu trei şi chiar mai multe inele,
sunt posibile şi alte configuraţii. Spunem că un inel este critic, dacă după deschiderea şi eliminarea
acestuia, inele rămase formează un set de lanţuri disjuncte sau alte inele nu mai există. Prin alte
cuvinte, lipsa de inele este şi ea un lanţ.
Exemplu
Se consideră cele 7 inele din figura ce urmează, numerotate de la 0 la 6. Există două inele
critice. Unul din inelele critice este cel cu numărul 2. După eliminarea acestui inel, inelele rămase
formează lanturile [1], [0, 5, 3, 4] şi [6]. Un alt inel critic este cel cu numărul 3. După eliminarea
acestuia, inelele rămase formează lanţurile [1, 2, 0, 5], [4] şi [6]. Dacă am elimina un oricare alt
inel, nu vom obţine un set de lanţuri disjuncte. De exemplu, deşi după eliminarea inelului 5 vom
obţine lanţul [6], inelele legate 0, 1, 2, 3 şi 4 nu formează un lanţ.

Sarcină
Trebuie să elaborezi un program care calculează numărul de inele critice din configuraţia dată.
La ı̂nceput, există un anumit număr de inele disjuncte. După aceea, inele sunt legate ı̂mpreună.
La un moment dat, se cere să returnezi numărul de inele critice din configuraţia curentă. Mai
concret, trebui să implementezii trei rutine.
ˆ Init(N) - la ı̂nceput aceasta se apelează exact o singură dată pentru a afla numărul N de
inele disjuncte din configuraţia iniţială, numerotate de la 0 la N  1 (inclusiv).
ˆ Link(A, B) - cele două inele A şi B vor fi legate ı̂mpreună. Este garantat că A şi B
sunt diferite si nu sunt ı̂ncă legate ı̂n mod direct. Nu există alte restricţii suplimentare faţă
de A şi B, ı̂n particular, restricţii ce ar rezulta din constrangeri fizice. Evident, apelurile
Link(A,B) şi Link(B,A) sunt echivalente.
ˆ CountCritical() - returnează numărul de inele critice din configuraţia curentă.

Exemplu
Se consideră N 7 inele, care, iniţial, sunt deconectate. Vom arata o secvenţă posibilă de
apeluril, după ultimul din care se va obţine configuraţia prezentată ı̂n figura de mai sus.
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 830

Apelul Valorile returnate


Init(7)
CountCritical() 7
Link(1, 2)
CountCritical() 7
Link(0, 5)
CountCritical() 7
Link(2, 0)
CountCritical() 7
Link(3, 2)
CountCritical() 4
Link(3, 5)
CountCritical() 3
Link(4, 3)
CountCritical() 2

Subtask 1 [20 de puncte]


ˆ N & 5 000.
ˆ Funcţia CountCritical este apelată doar o singură dată, după toate celelate apeluri;
funcţia Link este apelată de cel mult 5 000 de ori.

Subtask 2 [17 puncte]


ˆ N & 1 000 000.
ˆ Funcţia CountCritical este apelată doar o singură dată, după toate celelate apeluri;
funcţia Link este apelată de cel mult 1 000 000 ori.

Subtask 3 [18 puncte]


ˆ N & 20 000.
ˆ Funcţia CountCritical este apelată de cel mult de 100 de ori; funcţia Link este apelată
de cel mult 10 000 ori.

Subtask 4 [14 puncte]


ˆ N & 100 000.
ˆ Funcţiile CountCritical şi Link sunt apelate ı̂n total de cel mult 100 000 de ori.

Subtask 5 [31 de puncte]


ˆ N & 1 000 000.
ˆ Funcţiile CountCritical şi Link sunt apelate ı̂n total de cel mult 1 000 000 de ori.

Detalii de implementare
Tu trebuie să transmiţi exact un singur fişier, denumit rings.c, rings.cpp sau rings.pas.
Acest fişier implementează subprogramele descrise mai sus utilizand signaturile ce urmează.
Programele ı̂n limbajul C/C++

void Init(int N);


void Link(int A, int B);
int CountCritical();

Programele ı̂n limbajul Pascal

procedure Init(N : LongInt);


procedure Link(A, B : LongInt);
function CountCritical() : LongInt;

Aceste subprograme trebuie să se comporte aşa cum este descris mai sus. Desigur, eşti liber
să implementezi pentru uzul intern al acestora şi alte subprograme. Submit-urile tale nu trebuie
să interacţioneze ı̂n nici ı̂ntr-un fel cu intrarea / ieşirea standard, şi nici cu oricare alt fişier.
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 831

Model de evaluator
Modelul de evaluator citeşte intrarea ı̂n formatul ce urmează:
ˆ linia 1: N , L;
ˆ liniile 2, ..., L  1:
` -1 apelează CountCritical;
` A, B parametrii pentru Link.

Modelul de evaluator va tipări toate rezultatele returnate de CountCritical.


Timp maxim de executare/test: *.* secunde
Memorie: total *** MB

8.2.1 Indicaţii de rezolvare

A suboptimal solution covering all but the last subtask considers the following conditions:

ˆ if there is a vertex V of degree ' 4, no other vertex can be critical (because removing V still
leaves one or more vertices of degree ' 3); so if there is more than one vertex of degree ' 4,
there are no critical vertices;
ˆ if there is a vertex V of degree 3, each critical vertex is either V or one of its neighbors;

ˆ if there is a cycle, all critical vertices lie on the cycle;

ˆ if the graph is linear (a set of disjoint paths), all of its vertices are critical.

These checks can be easily extended to the dynamic case of the last subtask: the only nontrivial
check is keeping track of cycle formation, which can be dealt with using suitable data structures
(union-find d.s., etc.).

8.2.2 Coduri sursă

Listing 8.2.1: rings.cpp


/*
O(N+M+C) solution for Rings

N = number of vertices;
M = number of calls to Link;
C = number of calls to CountCritical.

Author: Giovanni Paolini


*/

#include<ctime>
#include<iostream>

#include <cstdio>
#include <vector>
#include <cassert>

using namespace std;

int const MAXN = 1000000;

int n;
bool quadruplication = 0;

int numcycles = 0;
int cycle_length;// If numcycles==1, here we store the length of the only cycle
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 832

int other_endpoint[4][MAXN]; // -1 if the node is not an endpoint,


// otherwise the other endpoint
vector<int> neighbours[MAXN];

int destroyed[4]; // The destroyed node of each graph


// (only if quadruplication==TRUE)

int degree[4][MAXN];
bool islinear[4]; // Whether each graph is linear or not

void Init(int k)
{
n = k;
for (int i=0; i<n; ++i)
{
other_endpoint[0][i] = i;
}
}

void add_new_edge(int x, int y)


{
// Adds an edge in case of quadruplication

for (int i=0; i<4; ++i)


{
// Operating on graph i
if ( !islinear[i] ) continue;
if ( x == destroyed[i] || y == destroyed[i] ) continue;

degree[i][x]++;
degree[i][y]++;

assert( degree[i][x] <= 3 && degree[i][y] <= 3 );

if ( degree[i][x] == 3 || degree[i][y] == 3 )
{
islinear[i] = 0;
continue;
}

if ( other_endpoint[i][x] == y )
{
// Cycle!

islinear[i] = 0;
continue;
}

int a = other_endpoint[i][x];
int b = other_endpoint[i][y];

other_endpoint[i][x] = -1;
other_endpoint[i][y] = -1;
other_endpoint[i][a] = b;
other_endpoint[i][b] = a;
}
}

void quadruplicate (int x)


{
quadruplication = 1;

destroyed[0] = x;
destroyed[1] = neighbours[x][0];
destroyed[2] = neighbours[x][1];
destroyed[3] = neighbours[x][2];

for (int i=0; i<4; ++i)


{
for (int j=0; j<n; ++j)
{
other_endpoint[i][j] = j;
degree[i][j] = 0;
}
}
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 833

for (int i=0; i<4; ++i)


{
islinear[i] = 1;
}

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


{
for (vector<int>::iterator j = neighbours[k].begin();
j != neighbours[k].end(); ++j)
{

if ( k < ( *j) ) add_new_edge( k, ( *j) );

}
}

void Link(int xx, int yy)


{

int x = xx;
int y = yy;

if ( quadruplication == 0 )
{

neighbours[x].push_back(y);
neighbours[y].push_back(x);

degree[0][x]++;
degree[0][y]++;

// If a node has degree 3, only it or its neighbours can be critical.


// So we can keep track of each of the 4 graphs obtained by removing
// one of these 4 nodes.
if ( degree[0][x] == 3 )
{
quadruplicate(x);
return;
}

if ( degree[0][y] == 3 )
{
quadruplicate(y);
return;
}

// If their degree is < 3, then they were necessarily endpoints!

if ( other_endpoint[0][x] != y )
{ // A longer path is formed
int a = other_endpoint[0][x];
int b = other_endpoint[0][y];

other_endpoint[0][x] = -1;
other_endpoint[0][y] = -1;
other_endpoint[0][a] = b;
other_endpoint[0][b] = a;
}
else
{ // A cycle is formed
numcycles++;
if ( numcycles == 1 )
{

int length = 1;
int previous_node = x;
int current_node = neighbours[x][0];

while ( current_node != x )
{
int possibility = neighbours[ current_node ][0];
if ( possibility == previous_node )
possibility = neighbours[ current_node ][1];
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 834

previous_node = current_node;
current_node = possibility;

length++;
}

cycle_length = length;
}
}
}

else
{
add_new_edge(x,y);
}
}

int CountCritical()
{
if ( quadruplication == 0 )
{
switch (numcycles)
{
case 0:
return n;
case 1:
return cycle_length;
default:
return 0;
}
}
else
{
int answer = 0;
for (int i=0; i<4; ++i)
{
if ( islinear[i] ) answer++;
}
return answer;
}
}

// ----------------- begin grader ------------------

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init(int N);


int CountCritical();
void Link(int a, int b);

int main()
{
auto t1 = clock();

std::freopen("../input/input54.txt", "r", stdin);


std::freopen("rings.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

auto t2 = clock();

int N, L;
tmp = scanf("%d %d", &N, &L);
assert(tmp == 2);
Init(N);

auto t3 = clock();
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 835

int i;
for (i = 0; i < L; i++)
{
int A, B;
tmp = scanf("%d", &A);

if (A == -1)
{
int critical;
critical = CountCritical();
printf("%d\n", critical);
}
else
{
tmp = scanf("%d", &B);
assert(tmp == 1);
Link(A, B);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// ----------------- end grader ------------------


/*
t2-t1 = 0
t3-t2 = 0.016
t4-t3 = 3.612

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


Press any key to continue.
*/

Listing 8.2.2: rings-13540.cpp


// https://oj.uz/submission/13540 1111 ms 56956 KB

#include<ctime>
#include<iostream>

#include<cassert>

using namespace std;

int n, cyc, Res;


int E[1010000][3], deg[5][1010000], chk[5], Num[5];
int par[5][1010000], SZ[1010000];
void Init(int N_)
{
n = N_;
Res = n;
int i;
for (i = 1; i <= n; i++)SZ[i] = 1, par[0][i] = i;
}

int Find(int ck, int a)


{
if (par[ck][a] == a)return a;
return par[ck][a] = Find(ck, par[ck][a]);
}

void Add(int ck, int a, int b)


{
if (chk[ck])return;
if (ck && (Num[ck] == a || Num[ck] == b))return;
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 836

if (!ck)
{
E[a][deg[0][a]] = b, E[b][deg[0][b]] = a;
}
deg[ck][a]++, deg[ck][b]++;
if (ck && (deg[ck][a] >= 3 || deg[ck][b] >= 3))
{
chk[ck] = 1;
return;
}
a = Find(ck, a), b = Find(ck, b);
if (a == b)
{
if (!ck)
{
cyc++;
if (cyc == 1)Res = SZ[a];
}
else chk[ck] = 1;
}
else
{
par[ck][a] = b;
if (!ck)
{
SZ[b] += SZ[a];
SZ[a] = 0;
}
}
}

void Make(int a)
{
int i, j, k, x;
Num[1] = a;
for (i = 0; i < 3; i++)
{
Num[2 + i] = E[a][i];
}
chk[0] = 1;
for (k = 1; k <= 4; k++)
{
for (i = 1; i <= n; i++)par[k][i] = i;
for (i = 1; i <= n; i++)
{
for (j = 0; j < deg[0][i]; j++){
x = E[i][j];
if (i == Num[k] || x == Num[k] || x > i)continue;
Add(k, i, x);
}
}
}
}

void Link(int A, int B)


{
if (!Res)return;
A++, B++;
if (!chk[0])
{
if (cyc == 2)Res = 0;
Add(0, A, B);
if (deg[0][A] == 3)
{
Make(A);
}
else
if (deg[0][B] == 3)
{
Make(B);
}
}
else
{
int i;
for (i = 1; i <= 4; i++)Add(i, A, B);
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 837

}
}

int CountCritical()
{
if (!chk[0])
{
if (cyc == 2)Res = 0;
return Res;
}
int i, r = 0;
for (i = 1; i <= 4; i++)if (!chk[i])r++;
return r;
}

// ----------------- begin grader ------------------

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init(int N);


int CountCritical();
void Link(int a, int b);

int main()
{
auto t1 = clock();

std::freopen("../input/input54.txt", "r", stdin);


std::freopen("rings.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

auto t2 = clock();

int N, L;
tmp = scanf("%d %d", &N, &L);
assert(tmp == 2);
Init(N);

auto t3 = clock();

int i;
for (i = 0; i < L; i++)
{
int A, B;
tmp = scanf("%d", &A);

if (A == -1)
{
int critical;
critical = CountCritical();
printf("%d\n", critical);
}
else
{
tmp = scanf("%d", &B);
assert(tmp == 1);
Link(A, B);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 838

std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// ----------------- end grader ------------------


/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 3.563

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


Press any key to continue.
*/

Listing 8.2.3: rings-49004.cpp


// https://oj.uz/submission/49004 832 ms 50460 KB

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> pp;

vector<pp> links;

int n;

struct simulator
{
int deg [1000010];
int oppo[1000010];
bool crit;
int R;

void init(int rem)


{
R=rem;
for(int i=0; i<n; ++i) deg[i]=0, oppo[i]=i;
crit=true;
for(auto& q:links)
{
update(q.first, q.second);
}
}

void update(int a,int b)


{
if(!crit) return;
if(a!=R && b!=R)
{
++deg[a], ++deg[b];
int p=oppo[a], q=oppo[b];
if(b==p)
{
crit=false;
return;
}
oppo[p]=q; oppo[q]=p;
if(deg[a]>=3 || deg[b]>=3)
{
crit=false;
}
}
}
};

int par[1000010];
int sz [1000010];
int cyc_cnt;
int cyc_size;

int R(int x)
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 839

{
return (par[x]==x)?x:(par[x]=R(par[x]));
}

void join(int a,int b)


{
a=R(a); b=R(b);
if(a == b)
{
++cyc_cnt;
cyc_size += sz[a];
}
else
{
sz[b] += sz[a];
par[a] = b;
}
}

void Init(int n_)


{
n=n_;
for(int i=0; i<n; ++i) par[i]=i, sz[i]=1;
}

int first_deg[1000010];
bool isSim;

simulator ss[4];

void Link(int A, int B)


{
if(!isSim)
{
++first_deg[A]; ++first_deg[B];
links.push_back({A, B});
join(A,B);

int thr = -1;


if(first_deg[A] == 3) thr = A;
if(first_deg[B] == 3) thr = B;

if(thr != -1)
{
vector<int> inj;
for(pp& l:links)
{
int a,b; tie(a,b)=l;
if(a==thr) inj.push_back(b);
if(b==thr) inj.push_back(a);
}
ss[3].init(thr);
for(int i=0; i<3; ++i) ss[i].init(inj[i]);
isSim = true;
}
}
else
{
for(int i=0; i<4; ++i)
ss[i].update(A, B);
}
}

int CountCritical()
{
if(!isSim)
{
if(cyc_cnt >= 2)
return 0;
else return cyc_cnt ? cyc_size : n;
}
int ans=0;
for(int i=0; i<4; ++i) if(ss[i].crit) ++ans;
return ans;
}
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 840

// ----------------- begin grader ------------------

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init(int N);


int CountCritical();
void Link(int a, int b);

int main()
{
auto t1 = clock();

std::freopen("../input/input54.txt", "r", stdin);


std::freopen("rings.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

auto t2 = clock();

int N, L;
tmp = scanf("%d %d", &N, &L);
assert(tmp == 2);
Init(N);

auto t3 = clock();

int i;
for (i = 0; i < L; i++)
{
int A, B;
tmp = scanf("%d", &A);

if (A == -1)
{
int critical;
critical = CountCritical();
printf("%d\n", critical);
}
else
{
tmp = scanf("%d", &B);
assert(tmp == 1);
Link(A, B);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------------- end grader ------------------


/*
t2-t1 = 0
t3-t2 = 0.031
t4-t3 = 3.5

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


Press any key to continue.
*/
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 841

Listing 8.2.4: rings-62499.cpp


// https://oj.uz/submission/62499 1040 ms 57172 KB

#include<ctime>
#include<iostream>

#include<cassert>

using namespace std;

int n, cyc, Res;


int E[1010000][3], deg[5][1010000], chk[5], Num[5];
int par[5][1010000], SZ[1010000];
void Init(int N_)
{
n = N_;
Res = n;
int i;
for (i = 1; i <= n; i++)SZ[i] = 1, par[0][i] = i;
}

int Find(int ck, int a)


{
if (par[ck][a] == a)return a;
return par[ck][a] = Find(ck, par[ck][a]);
}

void Add(int ck, int a, int b)


{
if (chk[ck])return;
if (ck && (Num[ck] == a || Num[ck] == b))return;
if (!ck)
{
E[a][deg[0][a]] = b, E[b][deg[0][b]] = a;
}
deg[ck][a]++, deg[ck][b]++;
if (ck && (deg[ck][a] >= 3 || deg[ck][b] >= 3))
{
chk[ck] = 1;
return;
}
a = Find(ck, a), b = Find(ck, b);
if (a == b)
{
if (!ck)
{
cyc++;
if (cyc == 1)Res = SZ[a];
}
else chk[ck] = 1;
}
else
{
par[ck][a] = b;
if (!ck)
{
SZ[b] += SZ[a];
SZ[a] = 0;
}
}
}

void Make(int a)
{
int i, j, k, x;
Num[1] = a;
for (i = 0; i < 3; i++)
{
Num[2 + i] = E[a][i];
}
chk[0] = 1;
for (k = 1; k <= 4; k++)
{
for (i = 1; i <= n; i++)par[k][i] = i;
for (i = 1; i <= n; i++)
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 842

{
for (j = 0; j < deg[0][i]; j++)
{
x = E[i][j];
if (i == Num[k] || x == Num[k] || x > i)continue;
Add(k, i, x);
}
}
}
}

void Link(int A, int B)


{
if (!Res)return;
A++, B++;
if (!chk[0])
{
if (cyc == 2)Res = 0;
Add(0, A, B);
if (deg[0][A] == 3)
{
Make(A);
}
else
if (deg[0][B] == 3)
{
Make(B);
}
}
else
{
int i;
for (i = 1; i <= 4; i++)Add(i, A, B);
}
}

int CountCritical()
{
if (!chk[0])
{
if (cyc == 2)Res = 0;
return Res;
}
int i, r = 0;
for (i = 1; i <= 4; i++)if (!chk[i])r++;
return r;
}

// ----------------- begin grader ------------------

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init(int N);


int CountCritical();
void Link(int a, int b);

int main()
{
auto t1 = clock();

std::freopen("../input/input54.txt", "r", stdin);


std::freopen("rings.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

auto t2 = clock();

int N, L;
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 843

tmp = scanf("%d %d", &N, &L);


assert(tmp == 2);
Init(N);

auto t3 = clock();

int i;
for (i = 0; i < L; i++)
{
int A, B;
tmp = scanf("%d", &A);

if (A == -1)
{
int critical;
critical = CountCritical();
printf("%d\n", critical);
}
else
{
tmp = scanf("%d", &B);
assert(tmp == 1);
Link(A, B);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// ----------------- end grader ------------------


/*
t2-t1 = 0
t3-t2 = 0.031
t4-t3 = 3.468

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


Press any key to continue.
*/

Listing 8.2.5: rings-223749.cpp


// https://oj.uz/submission/223749 861 ms 48336 KB

#include<bits/stdc++.h>

using namespace std;

#define REP(i, n) for(int i = 0; i < n; i++)

template<class T> int size(T && a) { return (int) a.size(); }

struct Graph
{
vector<int> rep, deg;
int excluded = -1;

int find(int x)
{
return rep[x] < 0 ? x : rep[x] = find(rep[x]);
}

int bicomp = -1;


int get_cycle()
{
return -rep[find(bicomp)];
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 844

void join(int x, int y)


{
x = find(x), y = find(y);
if(x == y)
{
if(bicomp == -1) bicomp = x;
else bicomp = -2;
return;
}
if(rep[x] > rep[y]) swap(x, y);
rep[x] += rep[y];
rep[y] = x;
}

int max_deg = 0;
void add_edge(int a, int b)
{
if(a == excluded || b == excluded)
return;
max_deg = max(max_deg, ++deg[a]);
max_deg = max(max_deg, ++deg[b]);
join(a, b);
}

Graph(int n = 0, int e = -1) : rep(n, -1), deg(n), excluded(e) {}


};

int n;
vector<pair<int, int>> edges;
Graph graph;
vector<Graph> without;

void Init(int N)
{
n = N;
graph = Graph(n);
}

void Link(int A, int B)


{
edges.emplace_back(A, B);
if(graph.max_deg < 3)
{
graph.add_edge(A, B);
if(graph.max_deg == 3)
{
if(graph.deg[A] != 3)
swap(A, B);

vector<int> crit = {A};


for(auto &[u, v] : edges)
{
if(u == A) crit.emplace_back(v);
if(v == A) crit.emplace_back(u);
}

for(int x : crit)
{
without.emplace_back(n, x);
for(auto &[u, v] : edges)
without.back().add_edge(v, u);
}
}
}
else
{
for(auto &g : without)
g.add_edge(A, B);
}
}

int CountCritical()
{
if(graph.max_deg < 3)
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 845

{
if(graph.bicomp == -1) return n;
if(graph.bicomp == -2) return 0;
return graph.get_cycle();
}
else
{
int ret = 0;
for(auto &g : without)
{
if(g.bicomp == -1 && g.max_deg < 3)
ret++;
}
return ret;
}
}

// ----------------- begin grader ------------------

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init(int N);


int CountCritical();
void Link(int a, int b);

int main()
{
auto t1 = clock();

std::freopen("../input/input54.txt", "r", stdin);


std::freopen("rings.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

auto t2 = clock();

int N, L;
tmp = scanf("%d %d", &N, &L);
assert(tmp == 2);
Init(N);

auto t3 = clock();

int i;
for (i = 0; i < L; i++)
{
int A, B;
tmp = scanf("%d", &A);

if (A == -1)
{
int critical;
critical = CountCritical();
printf("%d\n", critical);
}
else
{
tmp = scanf("%d", &B);
assert(tmp == 1);
Link(A, B);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 846

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// ----------------- end grader ------------------


/*
t2-t1 = 0
t3-t2 = 0.031
t4-t3 = 4.92

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


Press any key to continue.
*/

Listing 8.2.6: rings-233348.cpp


// https://oj.uz/submission/233348 710 ms 41592 KB

#include<bits/stdc++.h>

using namespace std;

#define taskname "A"


#define pb push_back
#define mp make_pair
typedef pair<int,int> ii;

typedef long double ld;


typedef long long ll;

const int maxn = 1e6 + 5;

int adj[maxn][2];

int n;

struct G
{
bitset<maxn> is_link;
int lab[maxn];
int banned = -1;
bool ok = 1;
void init(int u)
{
banned = u;
for(int i = 0 ; i < n ; ++i)lab[i] = -1;
for(int i = 0 ; i < n ; ++i)
{
for(int j = 0 ; j < 2 ; ++j)
{
if(adj[i][j] != -1 && i > adj[i][j])
{
Connect(i,adj[i][j]);
}
}
}
}

int FindLab(int u){return lab[u] < 0 ? u : lab[u] = FindLab(lab[u]);}

void Connect(int u , int v)


{
if(ok == 0 && banned != -1)return;
if(u == banned)is_link[v] = 1;
if(v == banned)is_link[u] = 1;
if(u == banned || v == banned)return;
u = FindLab(u);
v = FindLab(v);
if(u == v)
{
ok = 0;
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 847

return;
}
if(lab[u] > lab[v])swap(u , v);
lab[u] += lab[v];
lab[v] = u;
}
} a[4];

void Init(int _n)


{
memset(adj,-1,sizeof adj);
n = _n;
for(int i = 0 ; i < n ; ++i)a[0].lab[i] = -1;
}

int cycle = 0;
int CurState = 0;

int dfs(int u , int b)


{
int res = 1;
int par = b;
while(u != b)
{
res++;
for(int i = 0 ; i < 2 ; ++i)
{
if(adj[u][i] != -1 && adj[u][i] != par)
{
par = u;u = adj[u][i];
break;
}
}
}
return res;
}

int deg[maxn];

void Link(int a , int b)


{
if(CurState == 4)return;
if(deg[a] < deg[b])swap(a,b);
deg[a]++;deg[b]++;
if(CurState == 3)
{
for(int i = 0 ; i < 4 ; ++i)
{
::a[i].Connect(a,b);
if((a != ::a[i].banned && deg[a] - ::a[i].is_link[a] > 2) ||
(b != ::a[i].banned && deg[b] - ::a[i].is_link[b] > 2))
::a[i].ok = 0;
}
return;
}

if(deg[a] == 3)
{
CurState = 3;
for(int i = 0 ; i < 2 ; ++i)
{
::a[i].init(adj[a][i]);
}
::a[2].init(b);
::a[3].init(a);
for(int i = 0 ; i < 4 ; ++i)
{
::a[i].Connect(a,b);
if((a != ::a[i].banned && deg[a] - ::a[i].is_link[a] > 2) ||
(b != ::a[i].banned && deg[b] - ::a[i].is_link[b] > 2))
{
::a[i].ok = 0;
}
}
return;
}
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 848

for(int i = 0 ; i < 2 ; ++i)


{
if(adj[a][i] == -1)
{
adj[a][i] = b;
break;
}
}

for(int i = 0 ; i < 2 ; ++i)


{
if(adj[b][i] == -1)
{
adj[b][i] = a;
break;
}
}

if(::a[0].FindLab(a) == ::a[0].FindLab(b))
{
if(CurState == 2)CurState = 4;
else cycle = dfs(a , b) , CurState = 2;
}
else
{
::a[0].Connect(a , b);
}
}

int CountCritical()
{
if(CurState == 4)return 0;
if(CurState == 3)
{
int res = 0;
for(int i = 0 ; i < 4 ; ++i)
{
res += a[i].ok;
}
if(res == 0)CurState = 4;
return res;
}

if(CurState == 2)return cycle;


return n;
}

// ----------------- begin grader ------------------

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init(int N);


int CountCritical();
void Link(int a, int b);

int main()
{
auto t1 = clock();

std::freopen("../input/input54.txt", "r", stdin);


std::freopen("rings.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

auto t2 = clock();

int N, L;
CAPITOLUL 8. IOI 2012 8.2. PARACHUTE RINGS 849

tmp = scanf("%d %d", &N, &L);


assert(tmp == 2);
Init(N);

auto t3 = clock();

int i;
for (i = 0; i < L; i++)
{
int A, B;
tmp = scanf("%d", &A);

if (A == -1)
{
int critical;
critical = CountCritical();
printf("%d\n", critical);
}
else
{
tmp = scanf("%d", &B);
assert(tmp == 1);
Link(A, B);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// ----------------- end grader ------------------


/*
t2-t1 = 0
t3-t2 = 0.015
t4-t3 = 4.063

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


Press any key to continue.
*/

Listing 8.2.7: checkerRings.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../input/input54.txt",
(char*)"rings.out",
(char*)"../output/output54.txt",
};

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

registerChecker("rings", argc, argv);


compareRemainingLines();
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 850

8.2.3 *Rezolvare detaliată

8.3 Crayfish scrivener


Problema 3 - Crayfish scrivener 100 de puncte
Author: Bruce Merry
Se spune că Leonardo a fost un mare admirator al lui Johannes Gutenberg, fierarul german
care a inventat tiparul şi ı̂n onoarea sa a proiectat un dispozitiv numit editorul rac - crayfish
scrivener - un dispozitiv simplu pentru scris. El este similar cu maşina de scris modernă şi acceptă
doar două comenzi: una pentru a scrie următorul caracter şi una de anulare (undo) a ultimelor
comenzi. Cea mai importantă proprietate a editorului rac este că undo este o comandă foarte
puternică: ea este considerată o comandă ı̂n sine şi poate fi la rândul ei anulată.
Enunţ
Sarcina ta este să realizezi o versiune software a editorului rac: se ı̂ncepe cu un text vid şi
acceptă o succesiune de comenzi introduse de utilizator, şi ı̂ntrebări pentru anumite poziţii din
versiunea curentă a textului, după cum urmează.
ˆ Init() - se apelează o singură dată la ı̂nceputul execuţiei, fără argumente. Se utilizează
pentru iniţializarea structurilor de date. Aceasta nu va trebui anulată niciodată.
ˆ TypeLetter(L) - adaugă la sfârşitul textului o singură literă mică L din intervalul a, ...,
z.
ˆ UndoCommands(U) - anulează ultimele U comenzi, unde U este un număr intreg pozitiv.
ˆ GetLetter(P) - returnează litera de la poziţia P din textul curent, unde P este un indice
nenegativ. Prima literă din text are indicele 0. (Această ı̂ntrebare nu este o comandă şi este
ignorată de comanda undo).
După apelul iniţial al Init, celelalte rutine pot fi apelate de zero sau mai multe ori ı̂n orice
ordine. Se garantează că U nu va depăşi numărul de comenzi primite anterior, şi că P va fi mai
mic decât lungimea textului curent (numărul de litere al textului curent).
ı̂n ceea ce priveşte UndoCommands(U), ea anulează ultimele U comenzi ı̂n ordine inversă:
dacă comanda ce trebuie anulată este TypeLetter(L), atunci ea elimină litera L de la sfârşitul
textului curent; dacă comanda ce trebuie anulată este UndoCommands(X) pentru o valoare X,
aceasta reface ultimele X comenzi ı̂n ordinea lor ”originală”.
Exemplu
Vă vom arăta o secvenţă posibilă de apeluri, ı̂mpreună cu configuraţia textului după fiecare
apel.
Apel — Returnează Textul curent
Init()
TypeLetter(a) a
TypeLetter(b) ab
GetLetter(1) b ab
TypeLetter(d) abd
UndoCommands(2) a
UndoCommands(1) abd
GetLetter(2) d abd
TypeLetter(e) abde
UndoCommands(1) abd
UndoCommands(5) ab
TypeLetter(c) abc
GetLetter(2) c abc
UndoCommands(2) abd
GetLetter(2) d abd
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 851

Subtask 1 [5 puncte]
ˆ Numărul de comenzi şi ı̂ntrebări este ı̂ntre 1 şi 100 (inclusiv) şi nu va exista niciun apel
UndoCommands.

Subtask 2 [7 puncte]
ˆ Numărul de comenzi şi ı̂ntrebări este ı̂ntre 1 şi 100 (inclusiv) şi niciun apel UndoCommands
nu va fi anulat.

Subtask 3 [22 puncte]


ˆ Numărul de comenzi şi ı̂ntrebări este ı̂ntre 1 şi 5 000 (inclusiv).

Subtask 4 [26 de puncte]


ˆ Numărul de comenzi şi ı̂ntrebări este ı̂ntre 1 şi 1 000 000 (inclusiv). Toate apelurile
GetLetter vor apărea după toate apelurile TypeLetter şi UndoCommands.

Subtask 5 [40 de puncte]


ˆ Numărul de comenzi şi ı̂ntrebări este ı̂ntre 1 şi 1 000 000 (inclusiv).

Detalii de implementare
Trebuie să trimiţi exact un fişier, numit scrivener.c, scrivener.cpp sau
scrivener.pas. Acest fişier trebuie să implementeze subprogramele descrise mai sus folosind
următoarele semnături.
programele ı̂n limbajul C/C++

void Init();
void TypeLetter(char L);
void UndoCommands(int U);
char GetLetter(int P);

programele ı̂n limbajul Pascal

procedure Init;
procedure TypeLetter(L : Char);
procedure UndoCommands(U : LongInt);
function GetLetter(P : LongInt) : Char;

Aceste subprograme trebuie să se comporte aşa cum este descris mai sus. Desigur, eşti liber
să implementezi pentru uzul intern al acestora şi alte subprograme. Submit-urile tale nu trebuie
să interacţioneze ı̂n nici ı̂ntr-un fel cu intrarea / ieşirea standard, şi nici cu oricare alt fişier.
Modelul de evaluator
Modelul de evaluator citeşte intrarea ı̂n formatul ce urmează:
ˆ linia 1: numărul total de comenzi şi ı̂ntrebări din input;
ˆ pe fiecare dintre următoarele linii:
– T urmat de un spaţiu şi o literă mică a pentru comanda TypeLetter;
– U urmat de un spaţiu şi un număr ı̂ntreg pentru comanda UndoCommands;
– P urmat de un spaţiu şi un număr ı̂ntreg pentru comanda GetLetter.

Modelul de evaluator va scrie caracterele returnate de GetLetter, fiecare pe câte o linie.


Timp maxim de executare/test: *.* secunde
Memorie: total *** MB
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 852

8.3.1 Indicaţii de rezolvare

A clever way to get an efficient solution consists in representing the evolution of the system through
a trie, containing all the contents of the text so far; a point in time is represented by a single
pointer to a node in the trie.
Command processing requires O 1 time:

ˆ typing a letter just requires moving down in the trie (creating a new node if necessary)

ˆ undoing K commands requires moving K states back.

For all the subtasks except the final one, after processing all the commands, the final contents
can be extracted from the trie into an array and used to answer queries in O 1 time, giving O N 
time and space overall.
Subtask 5 requires a definitely more sophisticated approach to find a point in the text. For
this it is sufficient to be able to determine the k-ancestor of the current node: There are a number
of standard data structures for this problem that give O N log N  time overall. For example,
k
every node at depth D can contain a pointer to its 2 -th ancestor, where k is the position of the
rightmost 1 in the binary expansion of D.

8.3.2 Coduri sursă

Listing 8.3.1: scrivener.cpp


/*
Solution for Scrivener,
with TypeLetter and GetLetter in O(log N)
and UndoCommands in O(1).

Author: Matteo Boscariol


*/

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include<ctime>
#include<iostream>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init();
void TypeLetter(char L);
void UndoCommands(int U);
char GetLetter(int P);

#include<cstdlib>
#define MAXC 1000000
#define LOGMAXC 20

class node
{
public:
char l;
node** parents;
int depth;

node(char letter, node* parent)


{
l = letter;
if(parent == NULL)
{
depth=-1;
parents = new node*[1];
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 853

parents[0] = NULL;
}
else
{
depth=parent->depth+1;
parents = new node*[LOGMAXC];
parents[0] = parent;
for(int i=1, j=2; j<=depth+1; i++, j*=2)
{
parents[i] = parents[i-1]->parents[i-1];
}
}
};
};

node** command_base;
int n_commands;
node* current_position;
node* tree_root;

void Init()
{
command_base = new node*[MAXC];
n_commands = 0;
current_position = tree_root = new node(’\0’,NULL);
}

void TypeLetter(char L)
{
command_base[n_commands] = current_position;
n_commands++;
node* n = new node(L, current_position);
current_position = n;
}

void UndoCommands(int U)
{
node *n = command_base[n_commands - U];
command_base[n_commands] = current_position;
n_commands++;
current_position = n;
}

char GetLetter(int P)
{
node* n = current_position;
int distance = current_position->depth - P;

for(int shift = LOGMAXC; shift >= 0; shift--)


{
if((distance >> shift)%2 == 1)
n = n->parents[shift];
}

return n->l;
}

// ------------------ begin grader ----------------

int main()
{
auto t1 = clock();

std::freopen("../input/input49.txt", "r", stdin);


std::freopen("scrivener.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

Init();
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 854

auto t2 = clock();

int cmd_num;
tmp = scanf("%d", &cmd_num);
assert(tmp == 1);

auto t3 = clock();

int i;
for (i = 0; i < cmd_num; i++)
{
char cmd;
tmp = scanf(" %c", &cmd);
assert(tmp == 1);
if (cmd == ’T’)
{
char letter;
tmp = scanf(" %c", &letter);
assert(tmp == 1);
TypeLetter(letter);
}
else
if (cmd == ’U’)
{
int number;
tmp = scanf("%d", &number);
assert(tmp == 1);
UndoCommands(number);
}
else
if (cmd == ’P’)
{
int index;
char letter;
tmp = scanf("%d", &index);
assert(tmp == 1);
letter = GetLetter(index);
printf("%c\n", letter);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

}
// ------------------ end grader ----------------
/*
t2-t1 = 0.015
t3-t2 = 0
t4-t3 = 2.313

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


Press any key to continue.

argc = 4
checker
../input/input49.txt
scrivener.out
../output/output49.txt
----------------------
1
Correct

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


Press any key to continue.
*/
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 855

Listing 8.3.2: scrivener-7279.cpp


// https://oj.uz/submission/7279 604 ms 176648 KB

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include<ctime>
#include<iostream>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init();
void TypeLetter(char L);
void UndoCommands(int U);
char GetLetter(int P);

#include <vector>
#include <map>

using namespace std;

map<char, int> trie[1000001]; int prv[1000001][20];


int node,now,ttime,hist[1000001],len[1000001];
char last[10000001];

void Init() {}

void TypeLetter(char L)
{
int &n = trie[now][L];
if (n == 0)
{
n = ++node;
prv[n][0] = now;
for (int i=1;i<20;i++)
prv[n][i] = prv[prv[n][i-1]][i-1];
len[n] = len[now] + 1;
last[n] = L;
}
hist[++ttime] = now = n;
}

void UndoCommands(int U)
{
now = hist[ttime-U];
hist[++ttime] = now;
}

char GetLetter(int P)
{
int x = now;
P = len[x] - P - 1;
for (int i=0;i<20;i++)
if (P & (1 << i))
x = prv[x][i];

return last[x];
}

// ------------------ begin grader ----------------

int main()
{
auto t1 = clock();

std::freopen("../input/input49.txt", "r", stdin);


std::freopen("scrivener.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 856

outbuf = (char*) malloc(outbuf_len * sizeof(char));


tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

Init();

auto t2 = clock();

int cmd_num;
tmp = scanf("%d", &cmd_num);
assert(tmp == 1);

auto t3 = clock();

int i;
for (i = 0; i < cmd_num; i++)
{
char cmd;
tmp = scanf(" %c", &cmd);
assert(tmp == 1);
if (cmd == ’T’)
{
char letter;
tmp = scanf(" %c", &letter);
assert(tmp == 1);
TypeLetter(letter);
}
else
if (cmd == ’U’)
{
int number;
tmp = scanf("%d", &number);
assert(tmp == 1);
UndoCommands(number);
}
else
if (cmd == ’P’)
{
int index;
char letter;
tmp = scanf("%d", &index);
assert(tmp == 1);
letter = GetLetter(index);
printf("%c\n", letter);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

}
// ------------------ end grader ----------------
/*
t2-t1 = 0.015
t3-t2 = 0
t4-t3 = 2.859

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


Press any key to continue.
*/

Listing 8.3.3: scrivener-18725.cpp


// https://oj.uz/submission/18725 408 ms 87988 KB

#include <stdlib.h>
#include <stdio.h>
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 857

#include <assert.h>

#include<ctime>
#include<iostream>

using namespace std;

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init();
void TypeLetter(char L);
void UndoCommands(int U);
char GetLetter(int P);

int go[1000005],len[1000005],t=1,par[1000005][20];
char al[1000005];

void Init(){}

void TypeLetter(char L)
{
al[t]=L;
len[t]=len[go[t-1]]+1;
par[t][0]=go[t-1];
int u=1;
while(1)
{
if(len[t]>=(1<<u))
{
par[t][u]=par[par[t][u-1]][u-1];
u++;
}
else break;
}

go[t]=t;
t++;
}

void UndoCommands(int U)
{
go[t]=go[t-U-1];
t++;
}

char GetLetter(int P)
{
int o=len[go[t-1]];
o=o-P-1;
if(o==0)return al[go[t-1]];
int u=0,h=go[t-1];
while(o)
{
if(o%2)
{
h=par[h][u];
}
o/=2;
u++;
}

return al[h];
}

// ------------------ begin grader ----------------

int main()
{
auto t1 = clock();

std::freopen("../input/input49.txt", "r", stdin);


std::freopen("scrivener.out", "w", stdout);

int tmp;
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 858

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

Init();

auto t2 = clock();

int cmd_num;
tmp = scanf("%d", &cmd_num);
assert(tmp == 1);

auto t3 = clock();

int i;
for (i = 0; i < cmd_num; i++)
{
char cmd;
tmp = scanf(" %c", &cmd);
assert(tmp == 1);
if (cmd == ’T’)
{
char letter;
tmp = scanf(" %c", &letter);
assert(tmp == 1);
TypeLetter(letter);
}
else
if (cmd == ’U’)
{
int number;
tmp = scanf("%d", &number);
assert(tmp == 1);
UndoCommands(number);
}
else
if (cmd == ’P’)
{
int index;
char letter;
tmp = scanf("%d", &index);
assert(tmp == 1);
letter = GetLetter(index);
printf("%c\n", letter);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

}
// ------------------ end grader ----------------
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 2.203

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


Press any key to continue.
*/

Listing 8.3.4: scrivener-230126.cpp


// https://oj.uz/submission/230126 438 ms 67448 KB
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 859

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include<ctime>
#include<iostream>

using namespace std;

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

void Init();
void TypeLetter(char L);
void UndoCommands(int U);
char GetLetter(int P);

int cnt, cnt2, par[20][1000001], A[1000001], dep[1000001];


char S[1000001];

void Init() { }

void TypeLetter(char L)
{
++cnt2;
S[cnt2] = L;
dep[cnt2] = dep[A[cnt]] + 1;
par[0][cnt2] = A[cnt];
for (int i = 1; i < 20; ++i)
par[i][cnt2] = par[i - 1][par[i - 1][cnt2]];
++cnt;
A[cnt] = cnt2;
}

void UndoCommands(int U)
{
int x = cnt - U;
++cnt;
A[cnt] = A[x];
}

char GetLetter(int P)
{
int x = A[cnt];
for (int i = 0; i < 20; ++i)
if (((dep[x] - P - 1) >> i) & 1)
{
x = par[i][x];
}
return S[x];
}

// ------------------ begin grader ----------------

int main()
{
auto t1 = clock();

std::freopen("../input/input49.txt", "r", stdin);


std::freopen("scrivener.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

Init();

auto t2 = clock();
CAPITOLUL 8. IOI 2012 8.3. CRAYFISH SCRIVENER 860

int cmd_num;
tmp = scanf("%d", &cmd_num);
assert(tmp == 1);

auto t3 = clock();

int i;
for (i = 0; i < cmd_num; i++)
{
char cmd;
tmp = scanf(" %c", &cmd);
assert(tmp == 1);
if (cmd == ’T’)
{
char letter;
tmp = scanf(" %c", &letter);
assert(tmp == 1);
TypeLetter(letter);
}
else
if (cmd == ’U’)
{
int number;
tmp = scanf("%d", &number);
assert(tmp == 1);
UndoCommands(number);
}
else
if (cmd == ’P’)
{
int index;
char letter;
tmp = scanf("%d", &index);
assert(tmp == 1);
letter = GetLetter(index);
printf("%c\n", letter);
}
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

}
// ------------------ end grader ----------------
/*
t2-t1 = 0
t3-t2 = 0
t4-t3 = 2.359

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


Press any key to continue.
*/

Listing 8.3.5: checkerScrivener.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 861

(char*)"../input/input49.txt",
(char*)"scrivener.out",
(char*)"../output/output49.txt",
};

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

registerChecker("scrivener", argc, argv);


compareRemainingLines();
}

8.3.3 *Rezolvare detaliată

8.4 Ideal city


Problema 4 - Ideal city 100 de puncte

Author: Aleksandar Ilić and Andreja Ilić

Leonardo, ca mulţi alţi oameni de ştiinţă şi artişti italieni de vârsta lui, era foarte interesat
de planificarea oraşelor si urbanism. El dorea să modeleze un oraş ideal: confortabil, spaţios, şi
raţional ı̂n privinţa utilizării resurselor, mult diferite de oraşele ı̂ngustate şi claustrofobice din evul
mediu.
Oraşul ideal
Oraşul era format din N blocuri plasate pe un grid infinit de formă pătrată format din celule.
Fiecare celulă se identifică prin perechea de coordonate (rând, coloană). Celula (0,0) este ı̂n colţul
din stânga sus a gridului. Pentru celula i, j , celulele adiacente (dacă există) sunt: i  1, j ,
i  1, j , i, j  1, şi i, j  1. Fiecare block, când este plasat pe grid, acoperă exact o celulă. Un
bloc poate fi plasat ı̂n celula i, j  dacă şi numai dacă 1 & i, j & 2  2. Vom folosi, de asemenea,
31

coordonatele celulelor pentru a ne referi la blocurile plasate pe ele. Două blocuri sunt adiacente
dacă sunt plasate pe celule adiacente. ı̂ntr-un oraş ideal, toate blocurile sale sunt conectate astfel
ı̂ncât nu există ”găuri” ı̂n interiorul frontierei, adică celulele trebuie să ı̂ndeplinească următoarele
două condiţii.
ˆ Pentru oricare două celule neacoperite, există cel puţin o secvenţă de celule neacoperite
adiacente care le conectează.
ˆ Pentru oricare două celule acoperite, există cel puţin o secvenţă de celule acoperite adiacente
care le conectează.

Exemplul 1
Nici una din configuraţiile de blocuri de mai jos nu reprezintă un oraş ideal: primele două de
la stânga nu respectă prima condiţie, a treia nu respectă a doua condiţie, iar a patra nu respectă
nici una dintre condiţii.

Distanţa
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 862

Când traversăm oraşul, o săritură ı̂nseamnă trecerea de la un bloc la un alt bloc adiacent.
Celulele neacoperite nu pot fi traversate. Fie v  0, v1 , ..., vN 1 coordonatele a N blocuri plasate
pe grid. Pentru oricare două blocuri distincte aflate la coordonatele vi şi vj , distanţa dintre
d vi , vj  este cel mai mic număr de salturi necesare pentru a ajunge de la un bloc la celălalt.
Exemplul 2
Configuraţia de mai jos reprezintă un oraş ideal format din N 11 blocuri la coordonatele
v0 2, 5, v1 2, 6, v2 3, 3, v3 3, 6, v4 4, 3, v5 4, 4, v6 4, 5, v7 4, 6,
v8 5, 3, v9 5, 4, şi v10 5, 6. De exemplu, d v1 , v3  1, d v1 , v8  6, d v6 , v10  2, şi
d v9 , v10  4.

Enunţ
Sarcina ta este să scrii un program care, primind un oraş ideal, să calculeze suma distanţelor
dintre oricare perechi de blocuri vi şi vj cu i $ j. Mai exact, programul tău trebuie să calculeze
următoarea sumă:
=
d vi , vj , unde 0 & i $ j & N  1
Mai exact, trebuie să implementezi o rutină DistanceSum(N,X,Y) care, primind N şi doi
vectori X şi Y care descriu oraşul, calculează formula de mai sus. Atât X cât şi Y conţin exact N
elemente; blocul i se află la coordonatele X i, Y i cu 0 & i & N  1, şi 1 & X i, Y i & 2  2.
31

Deoarece rezultatul poate fi prea mare pentru a fi reprezentat pe 32 de biţi, el trebuie calculat
modulo 1 000 000 000 (un miliard).
ı̂n exemplul 2, sunt 11  10 / 2 = 55 perechi de blocuri. Suma distanţelor dintre toate perechile
este 174.
Subtask 1 [11 puncte]
Se consideră că N & 200.
Subtask 2 [21 de puncte]
Se consideră că N & 2 000.
Subtask 3 [23 de puncte]
Se consideră că N & 100 000.
În plus, următoarele două condiţii se ı̂ndeplinesc: pentru două celule acoperite i şi j astfel
ı̂ncât X i X j , oricare celulă dintre ele este acoperită; pentru două celul acoperite i şi j astfel
ı̂ncât Y i Y j , oricare celulă dintre ele este acoperită;
Subtask 4 [45 de puncte]
Se consideră că N & 100 000.
Detalii de implementare
Trebuie să trimiţi exact un fişier, numit city.c, city.cpp sau city.pas. Acest fişier
trebuie să implementeze subprogramul descris mai sus având următoarele semnături.
Programe C/C++

int DistanceSum(int N, int *X, int *Y);


CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 863

Programe Pascal

function DistanceSum(N:LongInt; var X,Y:array of LongInt):LongInt;

Acest subprogram trebuie să se comporte cum a fost descris mai sus. Desigur poţi implementa
alte subprograme pentru uz intern. Submisia ta nu trebuie să interacţioneze ı̂n vre-un fel cu
intrarea/ieşirea standard, şi nici cu alte fişiere.
Exemplu de evaluator
Exemplul de evaluator furnizat aşteaptă inputul ı̂n următorul format:
ˆ linia 1: N ;
ˆ liniile 2, ..., N  1: X i, Y i.

Limite de timp şi memorie


ˆ Limită de timp: o secundă.
ˆ Limită de memorie: 256 MB.

8.4.1 Indicaţii de rezolvare

Simple solutions use Floyd-Warshall algorithm or iterated BFS on the unary-cost edges, and both
3 2
require O N  space: time is O N  for Floyd-Warshall, and O N  for the iterated BFS, which
requires N times the number O N  of edges.
A more efficient solution is the following one.

ˆ For every row r, consider the connected groups of cells on row r; each such group becomes
a node of a tree, with a weight corresponding to the cardinality of the group. Two nodes of
this tree are adjacent iff there are at least two cells in the corresponding groups sharing a
common edge. Repeat the same argument for every column c.
ˆ The above description yields two node-weighted trees, one (let us call it TH) corresponding
to horizontal node-groups and another (TV) for vertical node-groups.
ˆ Now, a shortest path between any two cells can be decomposed into two shortest paths
along TV and TH: the two corresponding integers are called the vertical and horizontal
contribution, respectively.
ˆ Let us limit ourselves to the horizontal contributions. The sum of all horizontal contributions
can be computed as the sum of w x ˜ w y  ˜ d x, y  over all possible distinct pairs of distinct
nodes x and y in TV: here, w x and w y  are their weight (number of cells) and d x, y  is
their distance in TV.
ˆ The latter summation can be computed in linear time in the number of edges of TV, by
¬
observing that it is equivalent to the sum of S e ˜ S e over all edges e of TV, where
¬
S e and S e are the sum of the weights of the two components of the tree obtained after
removing the edge e.

8.4.2 Coduri sursă

Listing 8.4.1: city.cpp


/*
O(N*logN) solution for City.

Author: Giovanni Paolini


CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 864

*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include<ctime>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

int DistanceSum(int N, int *X, int *Y);

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

struct Point
{
int x;
int y;

Point() {}

Point(int _x, int _y)


{
x = _x;
y = _y;
}
};

bool operator< (const Point &a, const Point &b)


{
if ( a.x != b.x ) return ( a.x < b.x );
return ( a.y < b.y );
}

int const MAXN = 1000005;

int n;
Point squares[MAXN];

void read(int N, int *X, int *Y)


{
n = N;

for (int i=0; i<n; ++i)


{
squares[i] = Point(X[i], Y[i]);
}

void exchange()
{ // Exchanges the x and y coordinates of all the points.
for (int i=0; i<n; ++i)
{
squares[i] = Point( squares[i].y, squares[i].x );
}
}

struct Node
{
int x;
int ymin;
int ymax;
vector<int> neighbours;

Node () {}

Node (int _x, int _ymin, int _ymax)


{
x = _x;
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 865

ymin = _ymin;
ymax = _ymax;
}
};

vector<Node> nodes;

void make_tree()
{ // Builds the tree of vertically/horizontally-collapsed nodes

sort( squares, squares + n );


nodes.clear();

// Create nodes

int cont = 0;
while ( cont < n )
{
int x = squares[cont].x;
int ymin = squares[cont].y;

int y = ymin;

int i;
for (i=cont+1; i<n; ++i)
{
if ( squares[i].y == y+1 ) y++;
else break;
}

int ymax = y;
cont = i;

nodes.push_back( Node( x, ymin, ymax ) );


}

// Create edges

int cont1,cont2;

cont1 = 0;
for ( cont2 = 1; cont2 < nodes.size(); cont2++ )
{
while ( ( nodes[cont1].x + 1 < nodes[cont2].x ) ||
( nodes[cont1].x + 1 == nodes[cont2].x &&
nodes[cont1].ymax < nodes[cont2].ymin ) )
cont1++;

int numedges = 0;
while ( nodes[cont1].x + 1 == nodes[cont2].x &&
nodes[cont1].ymin <= nodes[cont2].ymax )
{
numedges++;
nodes[cont1].neighbours.push_back(cont2);
nodes[cont2].neighbours.push_back(cont1);
cont1++;
}

if ( numedges > 0 ) cont1--;


}

int weight (int i)


{
return nodes[i].ymax - nodes[i].ymin + 1;
}

bool visited[MAXN];
long long int s; // sum of all weights
long long int tot; // required total
long long int const MOD = 1000000000;

long long int dfs (int k)


{
if ( visited[k] ) return 0;
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 866

visited[k] = 1;

long long int w = weight(k);


for (int i=0; i<nodes[k].neighbours.size(); ++i)
{
w += dfs( nodes[k].neighbours[i] );
w %= MOD;
}

tot += w * (s - w);
tot %= MOD;

return w;
}

long long int sum ()


{
make_tree();

s = 0;
tot = 0;
for (int i=0; i<nodes.size(); ++i)
{
s += weight(i);
s %= MOD;
visited[i] = 0;
}

dfs(0);

return tot;
}

int DistanceSum(int N, int *X, int *Y)


{
read(N, X, Y);

// Find the horizontal sum


long long int first = sum();

exchange();

// Find the vertical sum


long long int second = sum();

long long int sol = first + second;


sol %= MOD;

return sol;
}

// --------------- begin grader --------------

int main()
{
auto t1 = clock();

std::freopen("../input/input40.txt", "r", stdin);


std::freopen("city.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;

inbuf = (char*) malloc(inbuf_len * sizeof(char));


outbuf = (char*) malloc(outbuf_len * sizeof(char));

tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);


assert(tmp == 0);

tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);


assert(tmp == 0);

int N, i;
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 867

tmp = scanf("%d", &N);


assert(tmp == 1);

int *sq_x, *sq_y;


sq_x = (int*) malloc(N * sizeof(int));
sq_y = (int*) malloc(N * sizeof(int));

for (i = 0; i < N; i++)


{
tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
assert(tmp == 2);
}

auto t2 = clock();

int ds = DistanceSum(N, sq_x, sq_y);

auto t3 = clock();

printf("%d\n", ds);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// --------------- begin grader --------------


/*
t2-t1 = 0.063
t3-t2 = 0.187
t4-t3 = 0

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


Press any key to continue.
*/

Listing 8.4.2: city-16324.cpp


// https://oj.uz/submission/16324 115 ms 8592 KB

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

int DistanceSum(int N, int *X, int *Y);

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef pair<int,int> pp;
typedef vector<int> vec;
typedef vector<vec> mat;

#define pb push_back
#define MOD 1000000000

struct pt
{
int x,y,v;
pt(){}
pt(int x_,int y_):x(x_),y(y_){}
bool operator < (const pt& r) const
{
return x!=r.x?x<r.x:y<r.y;
}
} s[100000];
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 868

mat path;
int n;
int cnt[100000];

ll dfs(int v,int p,ll &ans)


{
ll ret=0;
for(int i=0;i<path[v].size();i++)
{
int u=path[v][i];
if(u==p)continue;
ret+=dfs(u,v,ans);
}
ret+=cnt[v];
ans+=(n-ret)*ret;
ans%=MOD;
return ret;
}

int f(int N, int *X, int *Y)


{
n=N;
for(int i=0;i<N;i++) s[i]=pt(X[i],Y[i]);
sort(s,s+N);
int sz=0;
for(int i=0;i<N;i++)
{
int p=i;
while(s[i].x==s[i+1].x)i++;
int v=p;
while(v<=i)
{
while(v<i&&s[v].y+1==s[v+1].y)
{
s[v].v=sz;
v++;
}
s[v].v=sz;
cnt[sz++]=s[v].y-s[p].y+1;
p=++v;
}
}

path.clear();
path.assign(sz,vec());

for(int i=0;i<N;i++)
{
pt* it=lower_bound(s,s+N,pt(s[i].x-1,s[i].y));
if(it->x==s[i].x-1&&it->y==s[i].y) path[s[i].v].pb(it->v);
it=lower_bound(s,s+N,pt(s[i].x+1,s[i].y));
if(it->x==s[i].x+1&&it->y==s[i].y) path[s[i].v].pb(it->v);
}

for(int i=0;i<sz;i++)
{
sort(path[i].begin(),path[i].end());
path[i].erase(unique(path[i].begin(),path[i].end()),path[i].end());
}

ll ret=0;
dfs(0,0,ret);
return (int)ret;
}

int DistanceSum(int N, int *X, int *Y)


{
return (f(N,X,Y)+f(N,Y,X))%MOD;
}

// --------------- begin grader --------------

int main()
{
auto t1 = clock();
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 869

std::freopen("../input/input40.txt", "r", stdin);


std::freopen("city.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;

inbuf = (char*) malloc(inbuf_len * sizeof(char));


outbuf = (char*) malloc(outbuf_len * sizeof(char));

tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);


assert(tmp == 0);

tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);


assert(tmp == 0);

int N, i;
tmp = scanf("%d", &N);
assert(tmp == 1);

int *sq_x, *sq_y;


sq_x = (int*) malloc(N * sizeof(int));
sq_y = (int*) malloc(N * sizeof(int));

for (i = 0; i < N; i++)


{
tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
assert(tmp == 2);
}

auto t2 = clock();

int ds = DistanceSum(N, sq_x, sq_y);

auto t3 = clock();

printf("%d\n", ds);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// --------------- begin grader --------------


/*
t2-t1 = 0.266
t3-t2 = 0.703
t4-t3 = 0

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


Press any key to continue.
*/

Listing 8.4.3: city-18847.cpp


// https://oj.uz/submission/18847 51 ms 15584 KB

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include<ctime>
#include<iostream>

using namespace std;


CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 870

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

int DistanceSum(int N, int *X, int *Y);

#include<cstdio>
#include<algorithm>
#include<vector>

int a,xx,yy,i,j,p,q,back,t,x[100002],y[100002];
long long sum,mod=1e9,sz[100002];
std::vector<int>G[100002],E[100002];

struct A
{
int num,s,e;
};

std::vector<A>T[100002];

void dfs(int r,int par)


{
for(int k=0;k<E[r].size();k++)
{
if(par==E[r][k])continue;
dfs(E[r][k],r);
sz[r]+=sz[E[r][k]];
}
sum+=sz[r]*(long long)(a-sz[r]);
}

void f()
{
t=0;
for(i=0;i<=a;i++)
G[i].clear(),T[i].clear(),E[i].clear();

for(i=1;i<=a;i++)
{
G[x[i]].push_back(y[i]);
}

for(i=0;G[i].size();i++)
std::sort(G[i].begin(),G[i].end());

for(i=0;G[i].size();i++)
{
back=G[i][0];
for(j=0;j<G[i].size();j++)
{
if(j==G[i].size()-1||G[i][j]+1!=G[i][j+1])
{
T[i].push_back({t++,back,G[i][j]});
sz[t-1]=G[i][j]-back+1;
if(j!=G[i].size()-1)back=G[i][j+1];
}
}
}

for(i=1;T[i].size();i++)
{
p=0;q=0;
while(p<T[i-1].size()&&q<T[i].size())
{
if(T[i-1][p].e<T[i][q].s||T[i][q].e<T[i-1][p].s)
{

}
else
{
E[T[i-1][p].num].push_back(T[i][q].num);
E[T[i][q].num].push_back(T[i-1][p].num);
}

if(T[i-1][p].e<T[i][q].e) p++;
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 871

else q++;
}
}

dfs(0,-1);
}

int DistanceSum (int N, int *X, int *Y)


{
a=N;
xx=2147483647;yy=2147483647;
for(i=1;i<=a;i++)
x[i]=X[i-1],y[i]=Y[i-1];
for(i=1;i<=a;i++)
xx=std::min(xx,x[i]),yy=std::min(yy,y[i]);
for(i=1;i<=a;i++)
x[i]-=xx,y[i]-=yy;
f();
for(i=1;i<=a;i++)
t=x[i],x[i]=y[i],y[i]=t;
f();
return sum%mod;
}

// --------------- begin grader --------------

int main()
{
auto t1 = clock();

std::freopen("../input/input40.txt", "r", stdin);


std::freopen("city.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;

inbuf = (char*) malloc(inbuf_len * sizeof(char));


outbuf = (char*) malloc(outbuf_len * sizeof(char));

tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);


assert(tmp == 0);

tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);


assert(tmp == 0);

int N, i;
tmp = scanf("%d", &N);
assert(tmp == 1);

int *sq_x, *sq_y;


sq_x = (int*) malloc(N * sizeof(int));
sq_y = (int*) malloc(N * sizeof(int));

for (i = 0; i < N; i++)


{
tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
assert(tmp == 2);
}

auto t2 = clock();

int ds = DistanceSum(N, sq_x, sq_y);

auto t3 = clock();

printf("%d\n", ds);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 872

std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// --------------- begin grader --------------


/*
t2-t1 = 0.11
t3-t2 = 0.156
t4-t3 = 0

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


Press any key to continue.
*/

Listing 8.4.4: city-32163.cpp


// https://oj.uz/submission/32163 73 ms 8268 KB

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include<ctime>
#include<iostream>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

int DistanceSum(int N, int *X, int *Y);

#include <algorithm>
#include <vector>

using namespace std;

typedef long long llong;


typedef pair<int, int> pi;

int n;
const int MOD = 1e9;
const int PMAX = 2147483646;

pi point[100000];
int idx[100000];
int cnt[100000];
vector<int> edge[100000];

bool haveNext(int i)
{
if (i == n - 1) return false;
if (point[i].first != point[i + 1].first) return false;
if (point[i].second + 1 != point[i + 1].second) return false;
return true;
}

int bsearch(pi x)
{
int s = 0, e = n - 1;
int m;

while (s < e)
{
m = (s + e) / 2;
if (point[m] == x) return m;
if (point[m] < x) s = m + 1;
else e = m - 1;
}

if (point[s] == x) return s;
return -1;
}

int ans;
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 873

int dfs(int x, int p)


{
int ret = cnt[x];
for (int i : edge[x])
{
if (i == p) continue;
ret += dfs(i, x);
}

if (x)
{
llong sum = ret;
sum *= n - ret;
ans = (ans + sum) % MOD;
}

return ret;
}

int getAns()
{
ans = 0;
sort(point, point + n);
idx[0] = 0;
edge[0].clear();
cnt[0] = 1;
for (int i = 1; i < n; ++i)
{
if (haveNext(i - 1))
++cnt[idx[i] = idx[i - 1]];
else
cnt[idx[i] = idx[i - 1] + 1] = 1, edge[idx[i]].clear();

int j = bsearch({ point[i].first - 1, point[i].second });

if (j == -1) continue;

if (!edge[idx[i]].empty() && idx[j] == edge[idx[i]].back())


continue;

edge[idx[i]].push_back(idx[j]);
edge[idx[j]].push_back(idx[i]);
}

dfs(0, -1);
return ans;
}

int DistanceSum(int N, int *X, int *Y)


{
n = N;
for (int i = 0; i < n; ++i)
{
point[i] = { X[i], Y[i] };
}

int t = getAns();
for (int i = 0; i < n; ++i)
{
point[i] = { Y[i], X[i] };
}

return (getAns() + t) % MOD;


}

// --------------- begin grader --------------

int main()
{
auto t1 = clock();

std::freopen("../input/input40.txt", "r", stdin);


std::freopen("city.out", "w", stdout);

int tmp;
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 874

/* Set input and output buffering */


char *inbuf, *outbuf;

inbuf = (char*) malloc(inbuf_len * sizeof(char));


outbuf = (char*) malloc(outbuf_len * sizeof(char));

tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);


assert(tmp == 0);

tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);


assert(tmp == 0);

int N, i;
tmp = scanf("%d", &N);
assert(tmp == 1);

int *sq_x, *sq_y;


sq_x = (int*) malloc(N * sizeof(int));
sq_y = (int*) malloc(N * sizeof(int));

for (i = 0; i < N; i++)


{
tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
assert(tmp == 2);
}

auto t2 = clock();

int ds = DistanceSum(N, sq_x, sq_y);

auto t3 = clock();

printf("%d\n", ds);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// --------------- begin grader --------------


/*
t2-t1 = 0.063
t3-t2 = 0.312
t4-t3 = 0

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


Press any key to continue.
*/

Listing 8.4.5: city-197022.cpp


// https://oj.uz/submission/197022 88 ms 10044 KB

#include <stdlib.h>
#include <assert.h>

#include<ctime>
#include<iostream>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

int DistanceSum(int N, int *X, int *Y);

#include<stdio.h>
#include<algorithm>
#include<vector>
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 875

using namespace std;

vector<int>E[101000];
int n, SZ[101000], P[101000];
long long Res, D[101000], C[101000];

struct point
{
int x, y, num;
bool operator < (const point &p) const
{
return x != p.x ? x < p.x : y < p.y;
}
} w[101000], w2[101000], L[101000];

void DFS(int a, int pp)


{
int i;
C[a] = SZ[a];
for (i = 0; i < E[a].size(); i++)
{
if (E[a][i] != pp)
{
DFS(E[a][i], a);
C[a] += C[E[a][i]];
}
}

Res += C[a] * (n-C[a]);


}

void Do()
{
int i, cnt = 0, a, b, j, cc = 0;
sort(w, w + n);
for (i = 0; i < n; i++)
{
if (!i || w[i].x != w[i - 1].x || w[i - 1].y + 1 != w[i].y)
SZ[++cnt] = 0;
SZ[cnt]++;
w2[i].num = cnt, w2[i].x = w[i].y, w2[i].y = w[i].x;
}

for (i = 1; i <= cnt; i++)P[i] = 0;


sort(w2, w2 + n);
for (i = 0; i < n - 1; i++)
{
if (w2[i].x == w2[i + 1].x && w2[i].y + 1 == w2[i + 1].y)
{
a = w2[i].num, b = w2[i + 1].num;
if (P[a] != b)
{
P[a] = b;
L[cc].x = a, L[cc].y = b; cc++;
}
}
}

for (i = 0; i < cc; i++)


{
E[L[i].x].push_back(L[i].y);
E[L[i].y].push_back(L[i].x);
}

DFS(1, 0);
for (i = 1; i <= cnt; i++)E[i].clear();
}

int DistanceSum(int N, int *X, int *Y)


{
int i;
n = N;
for (i = 0; i < N; i++)
{
w[i].x = X[i], w[i].y = Y[i];
CAPITOLUL 8. IOI 2012 8.4. IDEAL CITY 876

Do();
for (i = 0; i < N; i++)
{
w[i].x = Y[i], w[i].y = X[i];
}

Do();
return Res%1000000000;
}

// --------------- begin grader --------------

int main()
{
auto t1 = clock();

std::freopen("../input/input40.txt", "r", stdin);


std::freopen("city.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;

inbuf = (char*) malloc(inbuf_len * sizeof(char));


outbuf = (char*) malloc(outbuf_len * sizeof(char));

tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);


assert(tmp == 0);

tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);


assert(tmp == 0);

int N, i;
tmp = scanf("%d", &N);
assert(tmp == 1);

int *sq_x, *sq_y;


sq_x = (int*) malloc(N * sizeof(int));
sq_y = (int*) malloc(N * sizeof(int));

for (i = 0; i < N; i++)


{
tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
assert(tmp == 2);
}

auto t2 = clock();

int ds = DistanceSum(N, sq_x, sq_y);

auto t3 = clock();

printf("%d\n", ds);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;

// --------------- begin grader --------------


/*
t2-t1 = 0.063
t3-t2 = 0.312
t4-t3 = 0
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 877

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


Press any key to continue.
*/

Listing 8.4.6: checkerCity.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../input/input40.txt",
(char*)"city.out",
(char*)"../output/output40.txt",
};

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

registerChecker("city", argc, argv);


compareRemainingLines();
}

8.4.3 *Rezolvare detaliată

8.5 Last Supper


Problema 5 - Last Supper 100 de puncte

Author: Richard Královic̆

Leonardo era foarte activ când lucra la ”Cina cea de taină”, cea mai faimoasă pictură murală:
una din primele sarcini ale zilei era să decidă ce vopsele să folosească ı̂n cursul zilei. El avea nevoie
de multe culori, dar putea păstra pe schelă doar un număr limitat de culori. Pe lânga alte lucruri,
asistentul său trebuia să urce pe schelă pentru a-i furniza culorile şi pentru a le coborâ pe un
anumit raft pe podea.
În această problemă, trebuie să scrieţi două programe separate pentru a ajuta asistentul.
Primul program va primi instrucţiunile lui Leonardo (secvenţa de culori de care Leonardo are
nevoie ı̂n timpul zilei), şi crează un scurt string de biţi, numit secvenţă ajutătoare. ı̂n timpul
procesării cerinţelor lui Leonardo din cursul zilei, asistentul nu va avea acces la cerinţele ce vor
urma, el va avea acces doar la secvenţa ajutătoare produsă de primul program. Al doilea program
va primi secvenţa ajutătoare, şi apoi va primi şi procesa cerinţele lui Leonarda ı̂ntr-o manieră ”on-
line” (adică una câte una). Acest program trebuie să ı̂nţeleagă ce reprezintă secvenţa ajutătoare
şi să facă alegerile optime. Totul este explicat mai jos ı̂n detaliu.
Mutarea culorilor ı̂ntre raft şi schelă
Vom considera un scenariu simplificat. Presupunem că există N culori numerotate de la 0
la N  1, şi ı̂n fiecare zi, Leonardo ı̂i cere asistentului o nouă culoare de exact N ori. Fie C o
secnvenţă de N culori cerute de Leonardo. Putem privi C ca o secvenţă de N numere, fiecare
ı̂ntre 0 şi N  1, inclusiv. Unele culori ar putea să nu apară deloc ı̂n C, iar altele ar putea să apară
de mai multe ori.
Schela este ı̂ntotdeauna plină şi conţine K din cele N culori, cu K $ N. Iniţial, schela conţine
culorile de la 0 la K  1, inclusiv.
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 878

Asistentul procesează cererile lui Leonardo una câte una. Oricând culoarea cerută este deja pe
schelă, asistentul se poate odihni. Altfel, el trebuie să ia culoarea cerută de pe raft şi să o ducă
pe schelă. Desigur, nu va mai fi loc pentru o nouă culoare, deci asistentul trebuie să aleagă una
din culorile existente pe schelă şi să o ducă pe raft, astfel ı̂ncât să facă loc pentru noua culoare.
Strategia optimă a lui Leonardo
Asistentul vrea să se odihnească de cât mai multe ori posibil. Numărul de cereri ı̂n care el se
poate odihni depinde de alegerile din timpul procesului. Mai precis, de fiecare dată când asistentul
trebuie să elimine o culoare de pe schelă, diferite alegeri pot condice la diferite situaţii viitoare.
Leonardo ı̂i explică asistentului cum poate să-şi atingă scopul cunoscând secvenţa C. Cea mai
bună alegere de a elimina o culoare de pe schelă se obţine examinând culorile existente pe schelă,
şi ce cerinţe au mai rămas ı̂n C. O culoare trebuie alese dintre cele de pe schelă respectând
următoarele reguli:
ˆ Dacă există o culoare pe schelă care nu va mai fi niciodată necesară ı̂n viitor, asistentul
trebuie să elimine o astfel de culoare de pe schelă.
ˆ Altfel, culoarea eliminată de pe schelă trebuie să fie aceea care va fi necesară cel mai târziu.
(Adică, dintre toate culorile de pe schelă, găsim viitoarea sa apariţie ı̂n cererile care urmează.
Culoarea ı̂ntoarsă pe raft va fi cea care este necesară ultima)
Se poate demonstra că utilizând strategia lui Leonardo, asistentul se va odhni de cele mai
multe ori.
Exemplul 1
Fie N 4, deci avem 4 culori (numerotate de la 0 la 3) şi 4 cereri. Fie secvenţa de cereri
C 2, 0, 3, 0. Presupunem K 2, adică Leonardo are o schelă capabilă să ţină 2 culori ı̂n
acelaşi timp. Cum s-a mai sus, iniţial, schela conţine culorile 0 şi 1, deci conţinutul schelei este:
[0, 1]. Un posibil mod ı̂n care asistentul poate rezolva cererile este următorul.
ˆ Prima culoare cerută (culoarea 2) nu este pe schelă. Asistentul o pune pe schelă şi decide
să elimine culoarea 1. Acum schela conţine culorile [0, 2].
ˆ Următoarea culoare cerută (culoarea 0) se află deja pe schelă, deci asistentul se poate odihni.
ˆ Pentru a treia cerere (culoarea 3), asistentul scoate culoarea 0, schimbând schela ı̂n
configuraţia [3, 2].
ˆ În final, a patra cerere (culoarea 0) trebuie să fie mutată de pe raft pe schelă. Asistentul
decide să elimine culoarea 2, deci schela devine acum [3, 0].
Se observă că ı̂n exemplul de mai sus, asistentul nu a urmat strategia optimă a lui Leonardo.
Strategia optimă ar fi eliminat culoarea 2 la pasul 3, deci asistentul s-ar fi putut odihni din
nou ı̂n pasul final.
Strategia asistentului când memoria sa este limitată
Dimineaţa, asistentul ı̂i cere lui Leonardă să scrie secvenţa C pe o foaie de hârtie, astfel ı̂ncât
să găsească şi să urmeze strategia optimă. Cu toate acestea, Leonardo este obsedat să-şi păstreze
tehnicile de lucru secrete, deci refuză să-i permită asistentului să folosească hârtia. El ı̂l lasă pe
asistent doar să citească secvenţa C şi să ı̂ncerce să o memoreze.
Din păcate, asistentul nu are o memorie bună. El este capabil să memoreze cel mult M biţi.
Din acest motiv, este posibil să nu poate reconstrui ı̂ntreaga secvenţă C. Totuşi, asistentul trebuie
să găsească o idee inteligentă pentru reconcstruiea secvenţei folosind biţii pe care ı̂i va memora.
Vom numi această secvenţă secvenţă ajutătoare şi o vom nota cu A.
Exemplul 2
De dimineaţă, asistentul poate lua hârtia lui Leonardo cu secvenţa C, să o citească, şi să facă
toate alegerile necesare. Un lucru pe care ar putea să-l facă ar fi să examineze starea schelei
după fiecare cerere. De exemplu, când foloseşte strategia (neoptimă) dată ı̂n exemplul 1, secvenţa
stărilor schelei ar fi [0, 2], [0, 2], [3, 2], [3, 0]. (Reamintim că el ştie că starea iniţială a schelei este
[0, 1]).
Acum presupunem că M 16, deci asistentul este capabil să reţină până la 16 biţi de informaţii.
Cum N 4, se pot memora culorile utilizând 2 biţi. De aceea, 16 biţi sunt suficienţi pentru a
stoca secvenţa de mai sus cu stările schelei. Deci, asistentul poate construi următoarea secvenţă
ajutătoare: A = (0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0).
În cursul zilei, asistentul poate decodifica această secvenţă ajutătoare şi o poate utiliza ı̂n
alegerile sale.
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 879

(Desigur, cu M 16, asistentul poate să memoreze ı̂ntreagă secvenţă C, utilizând doar 8 din
cei 16 biţi disponibili. In acest exemplu am vrut doar să arătăm că există mai multe opţiuni, fără
a da orice solutie bună)
Enunţ
Trebuie să scrii două programe separate ı̂n acelaşi limbaj de programare. Aceste programe vor
fi executate pe rând, fără ca acestea să poată comunica ı̂ntre ele ı̂n timpul execuţiei.
Primul program va fi cel utilizat de asistent dimineaţa. Acest program va primi secvenţa C,
iar el trebuie să calculeze secvenţa de ajutor A.
Al doilea program va fi cel utilizat de asistent ı̂n cursul zilei. Acest program va primi secvenţa
ajutătoare A, iar el trebuie să proceseze şirul C cu cererile lui Leonardo. De remarcat că acest
program va primi secvenţa C element cu element, iar fiecare element curent trebuie să fie procesat
ı̂nainte de a fi primit următorul.
Mai exact, ı̂n primul program trebuie implementată o singură rutină
ComputeAdvice(C, N, K,M) având ca input vectorul C cu N elmente ı̂ntregi (fiecare
ı̂n 0, ..., N  1), numărul K de culori care pot ı̂ncăpea pe schelă, şi numărm M de biţi disponibili
pentru secvenţa ajutătoare. Acest program trebuie să calculeze secvenţa ajutătoare A, care poate
conţine cel mult M biţi. După aceasta, programul trebuie să transmită secvenţa A sistemului,
apelând pentru fiecare bit din A (ı̂n ordine), următoarea rutină:
ˆ WriteAdvice(B) - adaugă bitul B secvenţei ajutătoare A (poţi apela această rutină de
cel mult M ori).

În al doilea program trebuie să implementezi o singură rutină Assist(A, N, K, R). Inputul
acestei rutine este secvenţa ajutătoare A, intregii N şi K definiţi mai sus, şi numărul curent de biţi
R al secvenţei A (R & M ). Această rutină trebuie să execute strategia propusă de voi asistentului,
folosind următoarele rutine care ı̂ţi sunt oferite:
ˆ GetRequest() - returnează următoarea culoare cerută de către Leonardo. (Nu este oferită
nicio altă informaţie referitoare la cererile care urmează).
ˆ PutBack(T) - mută culoarea T din schelă ı̂napoi pe raft. Poţi apela această rutină doar
dacă culoarea T este una din culorile de pe schelă.

În timpul execuţiei rutina Assist trebuie să apeleze GetRequest de exact N ori, de fiecare
dată primind una din cele N cereri ale lui Leonardo, ı̂n ordine. După fiecare apel GetRequest,
dacă culoarea returnată nu se află pe schelă, trebuie apelat PutBack(T) cu T ales de tine.
Dacă culoarea se află pe schelă nu trebuie apelat PutBack deloc. Dacă nu se procesează aşa, se
consideră eroare şi aceasta va cauza ı̂ntreruperea execuţiei programului. Vă amintim că la ı̂nceput
schela conţine toate culorile de la 0 la K  1, inclusiv.
Un test va fi considerat rezolvat doar dacă cele două rutine respectă toate constrângerile, iar
numărul total de apeluri PutBack este acelaşi cu cel din strategia optimă a lui Leonardo. De
remarcat că dacă există mai multe strategii de a obţine acelaşi număr de apeluri PutBack, se
poate utiliza oricare dintre ele. (Adică nu este obligatoriu să fie urmată strategia lui Leonardo,
dacă există o strategie la fel de bună).
Exemplul 3
În continuarea exemplului 2, presupunem că ı̂n ComputeAdvice ai calculat A = (0,
0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0). Pentru a comunica aceasta sistemului, tre-
buie să realizezi următoarea secvenţă de apeluri: WriteAdvice(0), WriteAdvice(0),
WriteAdvice(1), WriteAdvice(0), WriteAdvice(0), WriteAdvice(0), WriteAd-
vice(1)—, WriteAdvice(0), WriteAdvice(1), WriteAdvice(1), WriteAd-
vice(1)—, WriteAdvice(0), WriteAdvice(1), WriteAdvice(1), WriteAdvice(0)—,
WriteAdvice(0).
A doua rutină Assist va fi apoi executată, primind secvenţa A de mai sus, şi valorile N 4,
K 2 şi R 16. Apoi, rutina Assist trebuie să facă exact N 4 apeluri GetRequest.
De asemenea, după unele dintre aceste cereri, Assist trebuie apelat PutBack(T) cu alegeri
potrivite pentru T .
Tabelul de mai jos arată secvenţa de apeluri corespunzătoare alegerilor (neoptime) pentru
exemplul 1. Liniuţa ”-” din tabel arată că nu s-a apelat PutBack.
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 880

GetRequest() Acţiune
2 PutBack(1)
0 -
3 PutBack(0)
0 PutBack(2)

Subtask 1 [8 puncte]
ˆ N & 5 000.
ˆ Poţi folosi cel mult M = 65 000 biţi.

Subtask 2 [9 puncte]
ˆ N & 100 000.
ˆ Poţi folosi cel mult M = 2 000 000 biţi.

Subtask 3 [9 puncte]
ˆ N & 100 000.
ˆ K & 25 000.
ˆ Poti folosi cel mult M = 1 500 000 biţi.

Subtask 4 [35 de puncte]


ˆ N & 5 000.
ˆ Poţi folosi cel mult M = 10 000 biţi.

Subtask 5 [până la 39 de puncte]


ˆ N & 100 000.
ˆ K & 25 000.
ˆ Poţi folosi cel mult M = 1 800 000 biţi.

Scorul pentru acest subtask depinde de lungimea R a secvenţei ajutătoare comunicată de


programul tău. Mai precis, dacă R este cea mai mare (dintre toate testele) lungime a secvenţei
ajutătoare produsă de rutina ComputeAdvice, scorul tău va fi:
ˆ 39 de puncte dacă R & 200 000;
ˆ 39 (1 800 000 - Rmax ) / 1 600 000 puncte dacă 200 000 ¡ R ¡ 1 800 000;
ˆ 0 puncte dacă R ' 1 800 000.

Detalii de implementare
Trebuie să trimiţi exact două fişiere ı̂n acelaşi limbaj de programare.
Primul fişier trebuie numit advisor.c, advisor.cpp sau advisor.pas. Acest fişier tre-
buie să implementeze rutina ComputeAdvice după cum a fost descrisă mai sus şi trebuie să
apeleze WriteAdvice. Al doilea fişier trebuie numit assistant.c, assistant.cpp sau
assistant.pas. Acest fişier trebuie să implementeze rutina Assist după cum a fost descrisă
mai sus şi poate apela GetRequest şi PutBack.
Semnăturile celor două rutine sunt următoarele.
Programe C/C++

void ComputeAdvice(int *C, int N, int K, int M);


void WriteAdvice(unsigned char a);
void Assist(unsigned char *A, int N, int K, int R);
void PutBack(int T);
int GetRequest();

Programe Pascal

procedure ComputeAdvice(var C : array of LongInt; N, K, M : LongInt);


procedure WriteAdvice(a : Byte);
procedure Assist(var A : array of Byte; N, K, R : LongInt);
procedure PutBack(T : LongInt);
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 881

function GetRequest : LongInt;

Aceste rutine trebuie să se comporte aşa cum s-a descris mai sus. Desigur, sunteţi liberi
să implementaţi alte rutine pentru uzul intern. Pentru programele C/C++, rutinele interne
trebuie declarate static, ca evaluatorul să le poată lega. Alternativ, evidaţi să aveţi două
rutine (una ı̂n fiecare program) cu acelaşi nume. Submisiile voastre nu au voie să interacţioneze
cu intrarea/ieşirea standard, sau cu orice alt fişier.
Când scrieţi soluţiile, trebuie să aveţi grije la următoarele instructiuni (modelele pe care le
puteţi găsi pe calculator, respectă cerinţele listate mai jos).
Programe C/C++
La ı̂nceputul sursei, trebuie să includeţi fişierul advisor.h ı̂n programul advisor şi
assistant.h ı̂n programul assistant. Acest lucru poate fi realizat inserând ı̂n sursă linia:

#include "advisor.h"

sau

#include "assistant.h"

Cele două fişiere advisor.h şi assistant.h vor fi furnizate atât pe calculator (ı̂ntr-un
director) cât şi pe interfaţa web a concursului. De altfe, vei avea acces (prin aceleaşi canale) la
cod şi scripturi pentru a compila şi testa soluţia pe calculator. Mai precis, după copierea soluţiei ı̂n
directorul cu aceste scripturi, trebuie să rulaţi compile_c.sh or compile_cpp.sh (ı̂n funcţie
de limbajul de programare al codului).
Programe Pascal
Trebuie să utilizaţi uniturile advisorlib ı̂n programul advisor şi assistantlib ı̂n pro-
gramul assistant. Acest lucru poate fi realizat inserând ı̂n sursă linia:

uses advisorlib;

sau

uses assistantlib;

Cele două fişiere advisorlib.pas şi assistantlib.pas vor fi furnizate atât pe calculator
(ı̂ntr-un director) cât şi pe interfaţa web a concursului. De altfe, vei avea acces (prin aceleaşi
canale) la cod şi scripturi pentru a compila şi testa soluţia pe calculator. Mai precis, după
copierea soluţiei ı̂n directorul cu aceste scripturi, trebuie să rulaţi compile_pas.sh
Exemplu de evaluator
Exemplul de evaluator va accepta inputul ı̂n următorul format:
ˆ linia 1: N , K, M ;
ˆ liniile 2, ..., N  1: C i.

Prima dată, evaluatorul va executa rutina ComputeAdvice. Acesta va genera un fişier


advice.txt conţinând biţii secvenţei ajutătoare. Biţii vor fi separaţi prin spaţii, iar fişierul
se va termina cu cifra 2.
Apoi se va trece la execuţia rutinei Assist, şi se va genera output-ul ı̂n care, fiecare linie
este fie de forma "R [number]", fie de forma "P [number]. Liniile de primul tip indica
apeluri GetRequest() ı̂mpreună cu răspunsul primit. Liniile de al doulea tip reprezintă apeluri
PutBack() ı̂mpreună cu culoarea aleasă pentru a fi pusă ı̂napoi pe raft. Output-ul este terminat
cu o linie de forma "E".
Precizăm că timpul de rulare al evaluatorului oficial poate să difere puţin faţă de cel de pe
calculatorul local. Această diferenţă nu ar trebui să fie semnificativă. Totuşi, vă sfătuim să
utilizaţi interfaţa de testare pentru a verifica dacă soluţia voastră se ı̂ncadrează ı̂n timp.
Limtele de timp şi memorie
ˆ Limita de timp: 7 secunde.
ˆ Limita de memorie: 256 MB.
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 882

8.5.1 Indicaţii de rezolvare

Let us first describe how to compute the optimal strategy of Leonardo in O N log N  time.

ˆ Use an array of size N and scan the requests in C backwards: for each request, it is possible
to compute how far in the future the same color will be requested.
ˆ Process the requests in C forward: for each request, we can determine if the color is in the
scaffold and which of the colors to remove; the latter can be established in time O log N  by
keeping a priority queue of colors where the priority is determined by the time of the next
request.
For encoding the advice, the trivial solution would be to use N log K bits, i.e. log K for
every color fault (i.e., every time the requested color is missing from the scaffold): this way
we might specify exactly which color (within the K colors available on the scaffold) should
be removed.
Here is an alternative encoding that uses only N  K bits and it is optimal in the worst case.
ˆ Divide the colors currently in the scaffold between ”active” and ”passive”: an ”active” color
is one that will be requested before it is removed from the scaffold according to the optimal
strategy of Leonardo; a ”passive” one will not.
ˆ Using N  K bits it is possible to keep track of which colors are active:
` The initial active colors can be specified using K bits overall.
` Moreover, with each request we can provide a bit saying if the currently requested color
is active.
ˆ Note that removing any passive color is always ok. Of course, if you remove it arbitrarily
you will not produce the optimal solution you started from, but an equivalent one with the
same number of colors removed.

8.5.2 Coduri sursă

Listing 8.5.1: supper.cpp


// https://oj.uz/submission/231107
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "advisor.h"
#include "assistant.h"

#include <cstdio>
#include <queue>
#include <cassert>

#include <stack>

#include<ctime>
#include<iostream>

using namespace std;

// ----------------- advisor ----------------

int const MAXN = 100000;

struct Color
{
int id;
int np; // next position
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 883

Color () {}

Color (int _id, int _np)


{
id = _id;
np = _np;
}
};

bool operator< (Color const &a, Color const &b)


{
return ( a.np < b.np );
}

int vnext[MAXN]; // vnext[i] = index of next occurrence of d[i] in array d


// (n if it is the last occurrence)
int position[MAXN]; // position[j] = index of the next occurrence of color j
// in array d (n if it is the last occurrence)

bool in_scaffolda[MAXN]; // whether color j is in the scaffold or not

priority_queue<Color> coda;

int solution[MAXN]; // -1 if no change is necessary,


// otherwise the color to be removed

queue<int> events[MAXN]; // events[j] is the queue of the events of color j:


// 1 = used; 0 = removed.

int accumulated = 0;
int counter = 0;

void ComputeAdvice (int *d, int n, int k, int m)


{
// Finding the next occurrence of each color
for (int i=0; i<n; ++i)
{
position[i] = n;
}

for (int i=n-1; i>=0; --i)


{
vnext[i] = position[d[i]];
position[d[i]] = i;
}

// Initial settings: inserting colors 0,...,k in the player


for (int j=0; j<k; ++j)
{
in_scaffolda[j] = 1;
coda.push( Color( j, position[j] ) );
}

// Finding the solution


for (int i=0; i<n; ++i)
{

int color = d[i];


if ( in_scaffolda[color] )
{
coda.push( Color( color, vnext[i] ) );
solution[i] = -1;
events[ color ].push( 1 );
continue;
}

// Removing useless color in scaffold


Color useless = coda.top();
coda.pop();
in_scaffolda[ useless.id ] = 0;

// Inserting new color in scaffold


coda.push( Color( color, vnext[i] ) );
in_scaffolda[ color ] = 1;
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 884

// Writing the solution


solution[i] = useless.id;
events[ useless.id ].push( 0 );
events[ color ].push( 1 );
}

// Writing the advice


for (int j=0; j<k; ++j)
{

if ( events[j].empty() )
{
WriteAdvice(0);
continue;
}

if ( events[j].front() == 0 )
{
events[j].pop();
WriteAdvice(0);
}
else
{
WriteAdvice(1);
}

for (int i=0; i<n; ++i)


{

int color = d[i];


assert( events[ color ].front() == 1 );
events[ color ].pop();

if ( events[ color ].empty() )


{
// The color will not be used or removed any more
WriteAdvice(0);
continue;
}

if ( events[ color ].front() == 0 )


{
events[ color ].pop();
// The color is now inactive
WriteAdvice(0);
}
else
{
// The color is now active
WriteAdvice(1);
}

}
}

// ----------------- assistant ----------------

//int const MAXN = 100000;

bool in_the_scaffold[MAXN];
queue<int> passive; // queue of passive colors

void Assist (unsigned char *real_advice, int n, int k, int l)


{
for (int i=0; i<k; ++i)
{
in_the_scaffold[i] = 1;
if ( real_advice[i] == 0 ) passive.push(i);
}

for (int i=0; i<n; ++i)


{
int color = GetRequest();
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 885

if ( !in_the_scaffold[color] )
{

in_the_scaffold[ passive.front() ] = 0;
in_the_scaffold[ color ] = 1;

PutBack( passive.front() );
passive.pop();

if ( real_advice[k+i] == 0 ) passive.push(color);
}
}

// ------------------- begin grader ----------------------

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

static int N, K, M;
static int *C;

static int R;
static unsigned char *A;

static FILE *fadvice;

static int current_request = -1;


static int *in_scaffold;
static int expect_put_back;

int GetRequest(void)
{
int req;

if (expect_put_back)
{
fprintf(stderr, "Not putting back color when it is not on the scaffold\n");
exit(1);
}

req = C[++current_request];

if (!in_scaffold[req])
expect_put_back = TRUE;
else
expect_put_back = FALSE;

printf("R %d\n", req);


return req;
}

void PutBack(int T)
{
int req;

if (!expect_put_back)
{
fprintf(stderr,"Putting back a color when it is already on the scaffold\n");
exit(1);
}

if (!in_scaffold[T])
{
fprintf(stderr, "Putting back a color that is not on the scaffold\n");
exit(1);
}
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 886

req = C[current_request];
in_scaffold[req] = TRUE;
in_scaffold[T] = FALSE;
expect_put_back = FALSE;

printf("P %d\n", T);


}

void WriteAdvice(unsigned char a)


{
/* Normalize a to be 0 or 1 */
a = !!a;

if (R < M)
{
fprintf(fadvice, "%hhu ", a);
A[R] = a;
R++;
}
else
{
fprintf(stderr, "Advisor is providing too many bits of advice\n");
exit(1);
};
}

static inline void input_assert(int ok)


{
if (!ok)
{
fprintf(stderr, "Invalid input file.\n");
exit(1);
}
}

int main()
{
auto t1 = clock();

std::freopen("../input/input50.txt", "r", stdin);


std::freopen("supper.out", "w", stdout);

int tmp;
int i;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

fadvice = fopen("advice.txt", "w");

tmp = scanf("%d %d %d", &N, &K, &M);


input_assert(tmp == 3);

C = (int*) malloc(N * sizeof(int));


for (i = 0; i < N; i++)
{
tmp = scanf("%d", &C[i]);
input_assert(tmp == 1);
}

auto t2 = clock();

A = (unsigned char*) malloc(M * sizeof(int));

ComputeAdvice(C, N, K, M);

fprintf(fadvice, "\n2\n");

auto t3 = clock();

/* Init the scaffold */


in_scaffold = (int*) malloc(N * sizeof(int));
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 887

for (i = 0; i < K; i++)


in_scaffold[i] = TRUE;
for (i = K; i < N; i++)
in_scaffold[i] = FALSE;

Assist(A, N, K, R);

printf("E\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// ------------------- end grader ----------------------

Listing 8.5.2: supper-231107.cpp


// https://oj.uz/submission/231107
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "advisor.h"
#include "assistant.h"

#include <bits/stdc++.h>

// ----------------- advisor ----------------

#define lp(i,a,b) for(int i = a ; i < b ; i++)


#define pii pair<int,int>
#define pb push_back
#define mk make_pair
#define ff first
#define ss second

const int MAXN = 1e5+10 ;


const int inf = 1e9+10 ;

using namespace std ;

bool is_active[MAXN*2] , scaffold[MAXN] ;


vector<int> cores[MAXN] ;
int ptr[MAXN] , active[MAXN] ;
set<pii> s ;

int get_next(int cor)


{
if( ptr[cor] >= (int)cores[cor].size() ) return inf ;
return cores[cor][ ptr[cor]++ ] ;
}

void ComputeAdvice(int *C, int N, int K, int M)


{
lp(i,0,N) cores[ C[i] ].pb( i ) ;
lp(i,0,K)
s.insert( mk( get_next(i) , i ) ),
scaffold[i] = true,
active[i] = N+i ;

for(int i = 0 ; i < N ; i++ )


{
active[ C[i] ] = i ;

if( scaffold[ C[i] ] )


{
s.erase( s.find( mk( i , C[i] ) ) ) ;
s.insert( mk( get_next(C[i]) , C[i] ) ) ;
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 888

continue ;
}

auto par = *prev(s.end()) ;


s.erase( prev( s.end() ) ) ;

scaffold[ par.ss ] = false ;


scaffold[ C[i] ] = true ;

is_active[ active[par.ss] ] = true ;

int x = get_next( C[i] ) ;


if( x <= i ) x = get_next(C[i]) ;

s.insert( mk( x , C[i] ) ) ;


}

lp(i,0,K) WriteAdvice( is_active[N+i] ) ;


lp(i,0,N) WriteAdvice( is_active[i] ) ;
}

// ----------------- assistant ----------------

#define lp(i,a,b) for(int i = a ; i < b ; i++)


#define pii pair<int,int>
#define pb push_back
#define mk make_pair
#define ff first
#define ss second

//const int MAXN = 1e5+10 ;

using namespace std ;

set<int> actives ;
bool scaff[MAXN] ;

void Assist(unsigned char *A, int N, int K, int R)


{
int idx = -1 ;
lp(i,0,K)
{
scaff[i] = true ;
if( A[++idx] ) actives.insert(i) ;
}

lp(i,0,N)
{
int x = GetRequest() ;
if( scaff[x] )
{
if( A[++idx] ) actives.insert( x ) ;
continue ;
}

scaff[x] = true ;

int toErase = *actives.begin() ;


actives.erase( actives.begin() ) ;

scaff[ toErase ] = false ;

PutBack(toErase) ;

if( A[++idx] ) actives.insert(x) ;

}
}

// ------------------- begin grader ----------------------

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 889

#define FALSE 0
#endif

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

static int N, K, M;
static int *C;

static int R;
static unsigned char *A;

static FILE *fadvice;

static int current_request = -1;


static int *in_scaffold;
static int expect_put_back;

int GetRequest(void)
{
int req;

if (expect_put_back) {
fprintf(stderr, "Not putting back color when it is not on the scaffold\n");
exit(1);
}

req = C[++current_request];

if (!in_scaffold[req])
expect_put_back = TRUE;
else
expect_put_back = FALSE;

printf("R %d\n", req);


return req;
}

void PutBack(int T)
{
int req;

if (!expect_put_back)
{
fprintf(stderr,"Putting back a color when it is already on the scaffold\n");
exit(1);
}

if (!in_scaffold[T])
{
fprintf(stderr, "Putting back a color that is not on the scaffold\n");
exit(1);
}

req = C[current_request];
in_scaffold[req] = TRUE;
in_scaffold[T] = FALSE;
expect_put_back = FALSE;

printf("P %d\n", T);


}

void WriteAdvice(unsigned char a)


{
/* Normalize a to be 0 or 1 */
a = !!a;

if (R < M)
{
fprintf(fadvice, "%hhu ", a);
A[R] = a;
R++;
}
else
{
fprintf(stderr, "Advisor is providing too many bits of advice\n");
CAPITOLUL 8. IOI 2012 8.5. LAST SUPPER 890

exit(1);
};
}

static inline void input_assert(int ok)


{
if (!ok)
{
fprintf(stderr, "Invalid input file.\n");
exit(1);
}
}

int main()
{
auto t1 = clock();

std::freopen("../input/input50.txt", "r", stdin);


std::freopen("supper.out", "w", stdout);

int tmp;
int i;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);

fadvice = fopen("advice.txt", "w");

tmp = scanf("%d %d %d", &N, &K, &M);


input_assert(tmp == 3);

C = (int*) malloc(N * sizeof(int));


for (i = 0; i < N; i++)
{
tmp = scanf("%d", &C[i]);
input_assert(tmp == 1);
}

auto t2 = clock();

A = (unsigned char*) malloc(M * sizeof(int));

ComputeAdvice(C, N, K, M);

fprintf(fadvice, "\n2\n");

auto t3 = clock();

/* Init the scaffold */


in_scaffold = (int*) malloc(N * sizeof(int));
for (i = 0; i < K; i++)
in_scaffold[i] = TRUE;
for (i = K; i < N; i++)
in_scaffold[i] = FALSE;

Assist(A, N, K, R);

printf("E\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
// ------------------- end grader ----------------------
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 891

8.5.3 *Rezolvare detaliată

8.6 Tournament
Problema 6 - Turnirul 100 de puncte

Author: Luke Harrison

În anul 1491 ducele de Milano Lodovico l-a rugat pe Leonardo să-i orchestreze nunta sa cu
Beatrice d’Este, care includea şi un mare turneu, ce trebuia să dureze trei zile. Dar cel mai vestit
cavaler ı̂ntârzia ...
Turnirul
Într-un turneu, N cavaleri sunt mai ı̂ntâi aranjaţi ı̂ntr-o linie, iar poziţiile lor de-a lungul liniei
sunt numerotate de la 0 la N  1. Arbitrul turneului stabileşte o rundă cerând să iasă din linie
cavalerii din poziţiile dintre S şi E inclusiv (unde 0 & S $ E & N  1). Toţi cavalerii de pe poziţiile
ı̂ntre S şi E (inclusiv) se luptă ı̂ntre ei. Câştigătorul acestei lupte se ı̂ntoarce la locul său din linie
şi rămâne ı̂n turneu, ı̂n timp ce perdanţii iese de pe câmp şi sunt eliminaţi din turneu.
În continuare, cavalerii rămaşi se aranjează de-a lungul liniei, de la ı̂nceputul ei, păstrând
ordinea lor relativă din linia precedentă, ocupând astfel poziţiile de la 0 la N  E  S   1. ı̂n
continuare, arbitrul stabileşte runda următoare, repetând acest proces până când rămâne doar
un singur cavaler. Leonardo cunoaşte faptul că toţi cavalerii au puteri diferite, reprezentate prin
ranguri distincte de la 0 (cel mai slab) la N  1 (cel mai puternic). De asemenea, el cunoaşte exact
ce echipe vor fi desemnate de arbitru pentru cele C runde. La urma urmei el e Leonardo, ... şi el
este sigur că ı̂n fiecare din aceste runde va câştiga cavalerul cu rangul cel mai ı̂nalt.
Cavalerul ce a ı̂ntârziat
Cei N  1 din totalul de N cavaleri sunt deja aranjaţi ı̂n linie. Lipseşte doar cavalerul cel mai
vestit. Acest cavaler are rangul R şi vine la turnir un pic mai târziu. Pentru a crea o atmosferă
de divertisment, Leonardo vrea să exploateze popularitatea cavalerului ı̂ntârziat şi alege pentru el
o astfel de poziţie ı̂n linie, care va maximiza numărul de runde pe care cavalerul ı̂ntârziat le va
câştiga.
Reţineţi că nu suntem interesaţi ı̂n rundele ı̂n care nu participă cavelerul ı̂ntârziat, ci doar ı̂n
rundele la care el ia parte şi le câştigă.
Exemplu
Pentru N 5, cei N  1 cavaleri aranjaţi ı̂n linie au rangurile [1, 0, 2, 4], respectiv. Evident,
rangul cavalerului ı̂ntârziat este R 3. Pentru fiecare dim cele C 3 runde, arbitrul intenţionează
să scoată din linie cavalerii de pe poziţiile S, E , ı̂n următoarea ordine: (1, 3), (0, 1), (0, 1).
Dacă Leonardo plasează cavalerul ı̂ntârziat ı̂n prima poziţie, rangurile cavalerilor de pe linie
vor fi [3, 1, 0, 2, 4]. ı̂n prima rundă se vor lupta cavalerii de pe a poziţiile 1, 2, 3, cu rangurile 1,
0, 2, iar ı̂nvingător va fi cavalerul cu rangul 2. Cavalerii din linia nouă vor avea rangurile [3, 2, 4].
ı̂n următoarea rundă se vor lupta cavalerii cu rangurile 3 şi 2 (din poziţiile 0, 1), câştigător va fi
cavalerul cu rangul R 3. După acestă rundă rangurile cavalerilor din linie vor fi [3, 4]. Runda
finala (cavalerii de pe poziţiile 0, 1) este câştigată de cavalerul cu rangul 4. Prin urmare, cavalerul
ı̂ntârziat câştigă doar o runda (a doua).
În cazul ı̂n care Leonardo ar plasa cavalerul ı̂ntârziat ı̂ntre cavalerii cu rangurile 1 si 0, linia
ar arăta ı̂n felul următor: [1, 3, 0, 2, 4]. De data aceasta, ı̂n prima rundă participă cavalerii cu
rangurile 3, 0, 2, câştigător fiind cavalerul cu rangul R 3. Linia urmatoare este formată din
cavalerii cu rangurile [1, 3, 4]. ı̂n runda următoare participă cavalerii cu rangurile (1 şi 3), din
nou cı̂ştigător fiind cel cu rangul R 3. Linia finală este formată din cavalerii cu rangurile [3,
4], câştigător fiind cel cu rangul 4. Astfel, cavalerul ı̂ntârziat câştigă două runde; de fapt, acesta
este cel mai bun plasament posibil, deoarece nu există nici o altă modalitate de plasare ı̂n care
cavalerul ı̂ntârziat ar câştiga mai mult decât de două ori.
Sarcină
Trebuie să scrii un program care alege cea mai bună poziţie pentru cavalerul ı̂ntârziat, astfel
ı̂ncât numărul de runde castigate de el sa fie maxim, aşa cum şi-o doreşte Leonardo. Mai exact,
trebuie să elaborezi o rutina denumită GetBestPosition (N, C, R, K, S, E), unde:
ˆ N este numărul de cavaleri;
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 892

ˆ C este numărul de runde organizate de arbitru (1 & C & N  1);


ˆ R este rangul cavalerului ı̂ntârziat; rangurile tuturor cavalerilor (atât a celora care sunt deja
ı̂n linie, cât şi al celui ı̂ntârziat) sunt numere distincte din intervalul 0, ..., N  1; cu toate
că rangul R al cavalerului ı̂ntârziat poate fi dedus, el este indicat explicit;
ˆ K este un tablou din N  1 numere ı̂ntregi, reprezentând rangurile celor N  1 cavaleri
aranjaţi deja ı̂n linia iniţială;
ˆ S şi E sunt două tablouri de dimensiunea C. Pentru orice i de la 0 până la C  1, inclusiv,
runda i  1 formată de arbitru va include toţi cavalerii de pe poziţiile S i până la E i,
inclusiv. Pentru fiecare i, S i $ E i.

Toate apelurile acestei rutine vor fi corecte: E i va fi mai mic decât numărul curent de cavaleri
rămaşi pentru runda i  1, iar după C runde va rămânea exact un singur cavaler.
GetBestPosition(N, C, R, K, S, E) trebuie să returneze cea mai bună poziţie P ı̂n
care Leonardo trebuie să-l plaseze pe cavalerul ı̂ntârziat (0 & P & N  1). Dacă există mai multe
poziţii echivalente, retunaţi poziţia cea mai mică. (Poziţia P este o poziţia 0-bazată a cavalerului
ı̂ntârziat ı̂n linia rezultat. Prin alte cuvinte, P este numărul celorlaţi cavaleri care ı̂n soluţia
optimală stau ı̂naintea cavalerului ı̂ntârziat. ı̂n particular, P 0 semnifică faptul că cavalerul
ı̂ntârziat este la ı̂nceputul liniei, iar P N  1 semnifică faptul că el este la sfârşitul liniei).
Subtask 1 [17 puncte]
ˆ Se considră că N & 500.
Subtask 2 [32 de puncte]
ˆ Se considră că N & 5 000.

Subtask 3 [51 de puncte]


ˆ Se considră că N & 100 000.

Detalii de implementare
Trebuie să transmiţi exact un singur fişier, denumit tournament.c\verb,
tournament.cpp sau tournament.pas. Acest fiţier trebuie să implementeze subpro-
gramul descris mai sus, având următoarele signaturi:
Programele C/C++

int GetBestPosition(int N, int C, int R, int *K, int *S, int *E);

Programele Pascal

function GetBestPosition(N, C, R : LongInt;


var K, S, E : array of LongInt) : LongInt;

Aceste subprograme trebuie să se comporte aşa cum este descris mai sus. Desigur, pentru
uzul intern eşti liber să pui ı̂n aplicare şi alte subprograme. Submit-urile tale nu trebuie să
interacţioneze ı̂n nici un fel cu intrarea/ieşirea standard şi cu alte fişiere.
Model de evaluator
Modelul de evaluator furnizat de mediul de evaluare acceptă următorul format al datelor de
intrare:
ˆ linia 1: N , C, R;
ˆ liniile 2, ..., N : K i;
ˆ liniile N  1, ..., N  C: S i, E i.

Restricţii pentru timpul de execuţie şi memoria folosită


ˆ Limita de timp: 1 secundă.
ˆ Limita de memorie: 256 MiB.
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 893

8.6.1 Indicaţii de rezolvare

Solutions of various ranges of complexity are possible: all of them are essentially based on the
trivial idea of trying all possible positions and simulating the tournament.
3 2
A trivial way to do that takes time O N . We hereby describe a O N -time solution.

ˆ The whole tournament can be thought of as a tree whose leaves represent the knights and
where all other nodes represent the winner of a round. The structure of the tree is the same
regardless of where we put ourselves in the initial rank, only labels of the nodes (i.e., round
winners) change.
2 2
ˆ The latter tree leads to an O N  solution: the tree construction takes time O N ; then
for each of the possible N positions, we have to determine how far up our knight can go, so
2
we are left with another O N  checks. To go down to O N log N  time we need to optimize
the tree construction and the tournaments simulations.
ˆ To speed up the tree construction to O N log N , we can use a binary range tree to get
quickly the knight that is currently in any given challenged position.
ˆ To speed up the second phase, we make two observations.
1. Let us call ”white”/”black” the knights that are weaker/stronger than the late knight.
Then, when we place the late knight in a certain position, we have to determine how far
up we can go without finding a node that has some black leaf below it. For example, if
we decide to place the late knight in the leftmost position, we want to find the leftmost
node that has the longest path to a leaf and contains only white leaves under it.
2. Every time we place the knight in a given position, we are simply shifting (to the left)
every knight to his left.
ˆ Combining these two observations, we dont need to actually try a position to see how far up
we can go: it is sufficient to proceed as described in the first observation but allowing the
leftmost descendant to be black, and requiring only the remaining ones to be white.
ˆ Doing it in this way, the second phase is O N  because of the second observation.

8.6.2 Coduri sursă

Listing 8.6.1: tournament.cpp


/*
O(N*logN) solution for Tournament.

Author: Giovanni Paolini


*/

#include<ctime>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

#include <cstdio>
#include <iostream>
#include <cassert>
#include <vector>
#include <queue>

using namespace std;

int const MAXN = 2000000;


int const MAXLOGN = 22;

struct Node
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 894

{
int start;
int end;

bool all_white; // True if the interval contains good knights only


bool almost_all_white; // True if the interval contains good knights only,
// except at most the first knight

int best_result; // The maximum length of a victory-path finishing in


// that node
int where_best; // Where is the best_result achieved

Node() {}

Node(int _start, int _end)


{
start = _start;
end = _end;
best_result = 0;
}
};

int n,c,o;

bool rankb[MAXN];

// Range tree
int range_tree[MAXLOGN][MAXN];
int size_of_the_range_tree; // the size of the range tree

void make_range_tree()
{

int m = n;
int step = 0;

while ( m > 0 )
{
for (int i=0; i<m; ++i)
{
if ( step == 0 ) range_tree[step][i] = 1;
else
{
range_tree[step][i] = range_tree[step-1][2*i] +
range_tree[step-1][2*i+1];
}
}

if ( m > 1 ) m = (m+1)/2;
else m = 0;
step++;
}

size_of_the_range_tree = step;

void change (int k, int delta)


{ // Change the value of position k by delta
int m = k;
for (int step = 0; step < size_of_the_range_tree; ++step)
{
range_tree[step][m] += delta;
m /= 2;
}
}

int find_knight (int k, int step, int m)


{ // Find the (initial) position of the k-th living knight,
// starting from a given node (step,m) of the range tree
if ( k > range_tree[step][m] ) return n;

if ( step == 0 )
{
assert(k == 1);
return m;
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 895

if ( range_tree[step-1][2*m] >= k )
return find_knight( k, step-1, 2*m );
else
return find_knight( k - range_tree[step-1][2*m], step-1, 2*m+1 );
}

// Calls tree

vector<Node> calls_tree;

int father[MAXN]; // The index of current interval of the knight

int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
{
n = N;
c = C;
int o = R;

make_range_tree();

rankb[0] = 1;
Node nodo = Node( 0, 0 );
nodo.all_white = 1;
nodo.almost_all_white = 1;
nodo.where_best = 0;
calls_tree.push_back( nodo );
father[0] = 0;

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


{
int r = K[i-1];
rankb[i] = (r > o);
father[i] = i;

Node nodo = Node( i, i );


nodo.all_white = !rankb[i];
nodo.almost_all_white = 1;
nodo.where_best = i;
calls_tree.push_back( nodo );
}

for (int i=0; i<c; ++i)


{
int s = S[i]+1;
int e = E[i]+1;

int first = find_knight( s, size_of_the_range_tree-1, 0 );


int last = find_knight( e+1, size_of_the_range_tree-1, 0 ) - 1;

Node interval = Node( first, last ); // The new interval

bool all_white = 1;
bool almost_all_white = 1;

int best_child = -1;


int the_best = -1;

queue<int> to_change;

for (int j=s; j<=e; ++j)


{

int knight = find_knight( j, size_of_the_range_tree-1, 0 );


if ( j > s ) to_change.push( knight );
int old_int = father[knight];

father[knight] = calls_tree.size();

if ( calls_tree[old_int].all_white == 0 )
{
all_white = 0;

if ( j > s ) almost_all_white = 0;
else
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 896

if ( calls_tree[old_int].almost_all_white == 0 )
almost_all_white = 0;
}

if ( calls_tree[old_int].best_result > the_best )


{
the_best = calls_tree[old_int].best_result;
best_child = old_int;
}
}

while ( !(to_change.empty()) )
{
int knight = to_change.front();
to_change.pop();
change( knight, -1 );
}

interval.all_white = all_white;
interval.almost_all_white = almost_all_white;

if ( almost_all_white )
{
interval.best_result = the_best + 1;
interval.where_best = calls_tree[ best_child ].where_best;
// Found an interval for which it is possible
// to win interval.best_result times by starting in
// position interval.where_best
}
else
{
interval.best_result = the_best;
interval.where_best = calls_tree[ best_child ].where_best;
}

calls_tree.push_back(interval);
}

int t = calls_tree.size();

return calls_tree[t-1].where_best;
}

// -------------- begin grader -------------------------

int main()
{
auto t1 = clock();

std::freopen("../input/input30.txt", "r", stdin);


std::freopen("tournament.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
assert(tmp == 0);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
assert(tmp == 0);

auto t2 = clock();

int N, C, R;
int *K, *S, *E;
tmp = scanf("%d %d %d", &N, &C, &R);
assert(tmp == 3);
K = (int*) malloc((N-1) * sizeof(int));
S = (int*) malloc(C * sizeof(int));
E = (int*) malloc(C * sizeof(int));
int i;
for (i = 0; i < N-1; i++)
{
tmp = scanf("%d", &K[i]);
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 897

assert(tmp == 1);
}
for (i = 0; i < C; i++)
{
tmp = scanf("%d %d", &S[i], &E[i]);
assert(tmp == 2);
}
auto t3 = clock();

printf("%d\n", GetBestPosition(N, C, R, K, S, E));

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- end grader -------------------------


/*
t2-t1 = 0
t3-t2 = 0.187
t4-t3 = 0.109

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


Press any key to continue.
*/

Listing 8.6.2: tournament-2193.cpp


// https://oj.uz/submission/2193 46 ms 4616 KB

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include<ctime>
#include<iostream>

using namespace std;

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

const int Z = 1 << 17;


int nextv[Z],cnt[Z*2];

int getnth(int n)
{
int x = 1;
while (x < Z)
{
x <<= 1;
if (cnt[x] < n) n -= cnt[x++];
}
return x - Z;
}

void up(int x, int p)


{
x += Z;
while (x)
{
cnt[x] += p;
x >>= 1;
}
}

int add[Z],res[Z];
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 898

int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
{
int i,peo,x,y;

for (i=0;i<N;i++) nextv[i] = i+1, cnt[i+Z] = 1;


for (i=Z-1;i>=1;i--) cnt[i] = cnt[i*2] + cnt[i*2+1];

for (i=0;i<C;i++)
{
peo = E[i] - S[i];
S[i] = getnth(S[i]+1);
x = nextv[S[i]];
while (peo--)
{
up(x,-1);
x = nextv[x];
}
E[i] = x - 1;
nextv[S[i]] = x;
}

for (i=1;i<N;i++) add[i] = add[i-1] + (K[i-1] > R);

for (i=0;i<C;i++)
{
if (add[E[i]] == add[S[i]])
{
res[S[i]]++;
res[E[i]]--;
}
}

int s = 0, p = -1, ind;


for (i=0;i<N;i++)
{
s += res[i];
if (p < s)
{
p = s;
ind = i;
}
}

return ind;
}

// -------------- begin grader -------------------------

int main()
{
auto t1 = clock();

std::freopen("../input/input30.txt", "r", stdin);


std::freopen("tournament.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
assert(tmp == 0);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
assert(tmp == 0);

auto t2 = clock();

int N, C, R;
int *K, *S, *E;
tmp = scanf("%d %d %d", &N, &C, &R);
assert(tmp == 3);
K = (int*) malloc((N-1) * sizeof(int));
S = (int*) malloc(C * sizeof(int));
E = (int*) malloc(C * sizeof(int));
int i;
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 899

for (i = 0; i < N-1; i++)


{
tmp = scanf("%d", &K[i]);
assert(tmp == 1);
}
for (i = 0; i < C; i++) {
tmp = scanf("%d %d", &S[i], &E[i]);
assert(tmp == 2);
}
auto t3 = clock();

printf("%d\n", GetBestPosition(N, C, R, K, S, E));

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- end grader -------------------------


/*
t2-t1 = 0
t3-t2 = 0.046
t4-t3 = 0.032

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


Press any key to continue.
*/

Listing 8.6.3: tournament-4657.cpp


// https://oj.uz/submission/4657 44 ms 4036 KB

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include<ctime>
#include<iostream>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

#include<algorithm>

using namespace std;

const int idx = 1<<17;


int T[1<<18];

void update(int a){a+=idx;while(a)T[a]--,a>>=1;}

int read1(int x)
{
if(x==-1)return -1;
int a=1;
while(a<idx)
{
if(T[2*a]<x)x-=T[2*a],a=2*a+1;
else a=2*a;
}
return a-idx;
}

int read2(int a,int b)


{
a+=idx,b+=idx;
int ret = 0;
while(a<=b)
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 900

{
ret=max(ret,T[a]);
ret=max(ret,T[b]);
a = (a+1)>>1, b = (b-1)>>1;
}
return ret;
}

int sum[100010],ans;
int d[100010];

int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
{
int i;
for(i=0;i<N;i++)d[i]=i+1,T[i+idx]=1;
for(i=idx-1;i;i--)T[i]=T[i<<1] + T[i<<1|1];
for(i=0;i<C;i++)
{
int a = read1(S[i]+1);
int tmp = a;
for(int j=0;j<E[i]-S[i];j++)
{
tmp = d[tmp];
update(tmp);
}
d[a] = d[tmp];
E[i] = d[tmp]-1;
S[i] = a;
}
for(i=0;i<N-1;i++)T[i+idx]=K[i];
for(i=idx-1;i;i--)T[i]=max(T[i<<1],T[i<<1|1]);
for(i=0;i<C;i++)
{
int s = read2(S[i],E[i]-1);
if(s<R)sum[S[i]]++,sum[E[i]+1]--;
}
for(i=0;i<N;i++)sum[i]+=sum[i-1];
for(i=0;i<N;i++)if(sum[ans]<sum[i])ans=i;
return ans;
}

// -------------- begin grader -------------------------

int main()
{
auto t1 = clock();

std::freopen("../input/input30.txt", "r", stdin);


std::freopen("tournament.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
assert(tmp == 0);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
assert(tmp == 0);

auto t2 = clock();

int N, C, R;
int *K, *S, *E;
tmp = scanf("%d %d %d", &N, &C, &R);
assert(tmp == 3);
K = (int*) malloc((N-1) * sizeof(int));
S = (int*) malloc(C * sizeof(int));
E = (int*) malloc(C * sizeof(int));
int i;
for (i = 0; i < N-1; i++)
{
tmp = scanf("%d", &K[i]);
assert(tmp == 1);
}
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 901

for (i = 0; i < C; i++)


{
tmp = scanf("%d %d", &S[i], &E[i]);
assert(tmp == 2);
}
auto t3 = clock();

printf("%d\n", GetBestPosition(N, C, R, K, S, E));

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- end grader -------------------------


/*
t2-t1 = 0
t3-t2 = 0.062
t4-t3 = 0.031

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


Press any key to continue.
*/

Listing 8.6.4: tournament-14774.cpp


// https://oj.uz/submission/14774 77 ms 4164 KB

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include<ctime>
#include<iostream>

#define inbuf_len 1 << 16


#define outbuf_len 1 << 16

#include <cstdio>
#include <algorithm>

using namespace std;

struct bit
{
int tree[131073], lim;
void add(int x, int v)
{
while(x <= lim)
{
tree[x] += v;
x += x & -x;
}
}

int lbnd(int x)
{
int p = 0;
for(int i=lim/2; i; i >>= 1)
{
if(tree[p+i] < x)
{
x -= tree[p+i];
p += i;
}
}
return ++p;
}
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 902

void init(int n)
{
for(lim = 1; lim <= n; lim <<= 1);
for(int i=1; i<=n; i++) add(i,1);
}
} bit;

struct rmq
{
int tree[265000], lim;
void init(int n, int* a)
{
for(lim = 1; lim <= n; lim <<= 1);
for(int i=0; i<n; i++)
{
tree[lim + i + 1] = a[i];
}

for(int i=lim-1; i; i--)


{
tree[i] = max(tree[2*i],tree[2*i+1]);
}
}

int q(int s, int e)


{
int ret = 0;
s += lim;
e += lim;
while(s < e)
{
if(s%2 == 1) ret = max(ret,tree[s++]);
if(e%2 == 0) ret = max(ret,tree[e--]);
s >>= 1;
e >>= 1;
}
if(s == e) ret = max(ret,tree[s]);
return ret;
}
} rmq;

int dx[100005];

int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
{
for (int i=0; i<C; i++)
{
S[i]++;
E[i]++;
}
bit.init(N);
rmq.init(N-1,K);
for (int i=0; i<C; i++)
{
int st = bit.lbnd(S[i]);
int ed = bit.lbnd(E[i] + 1) - 1;
for (int j=E[i]; j>S[i]; j--)
{
bit.add(bit.lbnd(j),-1);
}
S[i] = st;
E[i] = min(ed,N)-1;
if(rmq.q(S[i],E[i]) < R)
{
dx[S[i]]++;
dx[E[i]+1]--;
}
}
for (int i=1; i<=N-1; i++)
{
dx[i] += dx[i-1];
}
return (int)(max_element(dx+1,dx+N) - dx - 1);
}
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 903

// -------------- begin grader -------------------------

int main()
{
auto t1 = clock();

std::freopen("../input/input30.txt", "r", stdin);


std::freopen("tournament.out", "w", stdout);

int tmp;

/* Set input and output buffering */


char *inbuf, *outbuf;
inbuf = (char*) malloc(inbuf_len * sizeof(char));
outbuf = (char*) malloc(outbuf_len * sizeof(char));
tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
assert(tmp == 0);
tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
assert(tmp == 0);

auto t2 = clock();

int N, C, R;
int *K, *S, *E;
tmp = scanf("%d %d %d", &N, &C, &R);
assert(tmp == 3);
K = (int*) malloc((N-1) * sizeof(int));
S = (int*) malloc(C * sizeof(int));
E = (int*) malloc(C * sizeof(int));
int i;
for (i = 0; i < N-1; i++)
{
tmp = scanf("%d", &K[i]);
assert(tmp == 1);
}
for (i = 0; i < C; i++)
{
tmp = scanf("%d %d", &S[i], &E[i]);
assert(tmp == 2);
}
auto t3 = clock();

printf("%d\n", GetBestPosition(N, C, R, K, S, E));

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- end grader -------------------------


/*
t2-t1 = 0
t3-t2 = 0.062
t4-t3 = 0.063

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


Press any key to continue.
*/

Listing 8.6.5: checkerTournament.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
CAPITOLUL 8. IOI 2012 8.6. TOURNAMENT 904

{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../input/input30.txt",
(char*)"tournament.out",
(char*)"../output/output30.txt",
};

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

registerChecker("tournament", argc, argv);


compareRemainingLines();
}

8.6.3 *Rezolvare detaliată


Capitolul 9
39
IOI 2011

9.1 Tropical Garden


Problema 1 - Tropical Garden 100 de puncte

Author: Normunds Vilcins

Botanist Somhed regularly takes groups of students to one of Thailand’s largest tropical gar-
dens.
The landscape of this garden is composed of N fountains (numbered 0, 1, , N  1) and M
trails.
Each trail connects a different pair of distinct fountains, and can be traveled in either direction.
There is at least one trail leaving each fountain. These trails feature beautiful botanical collections
that Somhed would like to see. Each group can start their trip at any fountain.
Somhed loves beautiful tropical plants. Therefore, from any fountain he and his students will
take the most beautiful trail leaving that fountain, unless it is the most recent trail taken and
there is an alternative. In that case, they will take the second most beautiful trail instead. Of
course, if there is no alternative, they will walk back, using the same trail for the second time.
Note that since Somhed is a professional botanist, no two trails are considered equally beautiful
for him.
His students are not very interested in the plants. However, they would love to have lunch
at a premium restaurant located beside fountain number P . Somhed knows that his students
will become hungry after taking exactly K trails, where K could be different for each group of
students.
Somhed wonders how many different routes he could choose for each group, given that:
ˆ each group can start at any fountain;
ˆ the successive trails must be chosen in the way described above; and
ˆ each group must finish at fountain number P after traversing exactly K trails.

Note that they may pass fountain number P earlier on their route, although they still need to
finish their route at fountain number P .
Your task
Given the information on the fountains and the trails, you have to find the answers for Q
groups of students; that is, Q values of K.
Write a procedure count_routes(N,M,P,R,Q,G) that takes the following parameters:
ˆ N - the number of fountains. The fountains are numbered 0 through N  1.
ˆ M - the number of trails. The trails are numbered 0 through M  1. The trails will be given
in decreasing order of beauty: for 0 & i $ M  1, trail i is more beautiful than trail i  1.
ˆ P - the fountain at which the premium restaurant is located.
ˆ R - a two-dimensional array representing the trails. For 0 & i $ M , trail i connects the
fountains Ri0 and Ri1. Recall that each trail joins a pair of distinct fountains, and
no two trails join the same pair of fountains.
39
argint: Vlad Alexandru Gavrilă, ICHB (Bucureşti)
. argint: Adrian Budău, ICHB (Bucureşti)
. argint: Andrei Purice, IL Caragiale (Ploieşti)
. 253p: Mihai-Dan Gheorghe, ICHB (Bucureşti).

905
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 906

ˆ Q - the number of groups of students.


ˆ G - a one-dimensional array of integers containing the values of K. For 0 & i $ Q, Gi is
the number of trails K that the i-th group will take.

For 0 & i $ Q, your procedure must find the number of possible routes with exactly Gi trails
that group i could possibly take to reach fountain P . For each group i, your procedure should
call the procedure answer(X) to report that the number of routes is X. The answers must be
given in the same order as the groups. If there are no valid routes, your procedure must call
answer(0).
Examples
Example 1
Consider the case shown in Figure 1,
where N=6, M=6, P=0, Q=1, G[0]=3, and
1 2
0 1
0 3
R=
3 4
4 5
1 5
Note that trails are listed in decreasing order of beauty.
That is, trail 0 is the most beautiful one, trail 1 is the second
most beautiful one, and so on.
There are only two possible valid routes that follow 3 trails:
ˆ 1 2 1 0, and
ˆ 5 4 3 0.

The first route starts at fountain 1. The most beautiful trail from here leads to fountain 2. At
fountain 2, the group has no choice, they must return using the same trail. Back at fountain 1,
the group will now avoid trail 0 and choose trail 1 instead. This trail does indeed bring them to
the fountain P 0.
Thus, the procedure should call answer(2).
Example 2
Consider the case shown in Figure 2,
where N=5, M=5, P=2, Q=2, G[0]=3, G[1]=1, and
1 0
1 2
R= 3 2
1 3
4 2
For the first group, there is only one valid route that
reaches fountain 2 after following 3 trails: 1 0 1
2.
For the second group, there are two valid routes that reach
fountain 2 after following 1 trail: 3 2, and 4 2.
Therefore, the correct implementation of count_routes should first call answer(1) to
report the answer for the first group, and then call answer(2) to report the answer for the
second group.
Subtasks
Subtask 1 (49 points)
ˆ 2 & N & 1 000
ˆ 1 & M & 10 000
ˆ Q 1
ˆ each element of G is an integer between 1 and 100, inclusive.

Subtask 2 (20 points)


ˆ 2 & N & 150 000
ˆ 1 & M & 150 000
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 907

ˆ Q 1
ˆ each element of G is an integer between 1 and 1 000 000 000, inclusive.

Subtask 3 (31 points)


ˆ 2 & N & 150 000
ˆ 1 & M & 150 000
ˆ 1 & Q & 2 000
ˆ each element of G is an integer between 1 and 1 000 000 000, inclusive.

Implementation details
Limits
ˆ CPU time limit: 5 seconds
ˆ Memory limit: 256 MB
Note: There is no explicit limit for the size of stack memory. Stack memory counts towards
the total memory usage.

Interface (API)
ˆ Implementation folder: garden/
ˆ To be implemented by contestant: garden.c or garden.cpp or garden.pas
ˆ Contestant interface: garden.h or garden.pas
ˆ Grader interface: gardenlib.h or gardenlib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas
ˆ Sample grader input: grader.in., grader.in.2, ...
Note: The sample grader reads the input in the following format:
– Line 1: N , M , and P .
– Lines 2 to M  1: description of the trails; i.e., line i  2 contains Ri0 and Ri1,
separated by a space, for 0 & i $ M .
– Line M  2: Q.
– Line M  3: array G as a sequence of space-separated integers.
– Line M  4: array of expected solutions as a sequence of space-separated integers.
ˆ Expected output for sample grader input: grader.expect.1, grader.expect.2, ...
For this task, each one of these files should contain precisely the text ”Correct.”

9.1.1 Indicaţii de rezolvare

Proposed by: Normunds Vilcins

In this task, we would like to compute the number of possible paths that could have led each
group to the specified intersection P , using the given number of steps K.
Notice that each path is completely determined by its initial intersection. Thus, to compute
the number of possible paths, we only need to check whether each intersection, if used as an initial
intersection, would bring the group to intersection P after exactly K steps. As we need to check
all N initial intersections for each of the Q groups, an efficient algorithm for checking whether
the group will be at intersection P after exactly K steps is needed, which will be discussed in
following sections.
1 Graph construction
This problem can be treated as a graph problem. A natural approach is to construct a graph
G containing the following information: for each intersection, where the group would move to.
Since they may take only one of the two most beautiful trails, we will use two vertices to represent
an intersection. Namely, for the i-th intersection, let vi represent this intersection where the next
¬
chosen trail must be the most beautiful trail incident to it, and vi represent this same intersection
but where the next chosen trail must be the second most beautiful trail incident to it (or the most
beautiful trail if no alternative is available). In other words, vi represents the i-th intersection when
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 908

¬
the last taken trail is not the most beautiful trail incident to this intersection, and vi represents
this intersection when the last taken trial is the most beautiful one incident to this intersection.
Now for each vertex, we add an outgoing edge representing the most beautiful or second most
beautiful trail, according to the conditions mentioned above. With this, G will contain 2N vertices,
and exactly one outgoing edge from each vertex.
The construction of the graph G takes O M  N  running time by first creating 2N vertices,
then scanning through the array R representing trails, and conditionally adding these edges to G
under the described conditions.
2 An O M  N KQ solution
A simple way to check where the couple would arrive after K steps is to simulate their path,
for each intersection as an initial vertex. Since they always choose the most beautiful trail in the
first steps, the corresponding starting vertices in G are v0 , ..., vN 1 .
To simulate their walk, we simply start at some vertex vs , then follow the unique outgoing
edge for that vertex, and repeat this process for K steps. Since the vertices corresponding to
¬
intersection P are vP and vP , then this path ends at this intersection if and only if after K steps,
we stop at one of these vertices. That is, to find the number of possible paths, we simulate their
walk for all possible initial vertices vi , and count the number of starting vertices that end at vP
¬
or vP after K steps.
Clearly, this process takes O K  total running time for each starting vertex. Since there are
N possible starting vertices and Q questions, this algorithm takes O M  N KQ running time,
including graph construction. This running time is sufficient to fully solve subtask 1.
3 An O M  N Q log K  solution
As K becomes large in subtask 2, we need a better way to simulate the algorithm mentioned
in the previous section. Notice that the edges in G represents 1-step traveling. To simulate faster,
we will use the permutation-composition approach.
k
We first precompute the result of 2 -step traveling from each vertex in G, where k 0, 1, 2, ...,
using a technique similar to successive squaring. Let Tv,2k represents the vertex we arrive at after
k
traveling from v for 2 steps. Then for k 0, 1, 2, ..., we can compute Tv,2k easily:
If k 0, then the destination is specified in G; otherwise, we compose the two paths of length
k1
2 using the formula
Tv,2k TTv,2k1 ,2k1 .
k k1
In other words, traveling 2 steps from v is the same as traveling 2 steps from v, then from
k1
that vertex, continue for 2 more steps.
Then, notice that for each value of K, we can decompose this number into sum of distinct,
non-negative powers of two. Suppose that K 2 1  2 2  ...  2 l where k1 $ k2 $ ... $ kl for some
k k k

positive integer l. Then the result of traveling k steps from v can be found by simply composing
k k k
travelings of 2 1 , 2 2 , ..., 2 l that we have precomputed. Using this technique, therefore, we can
compute the destination for each starting intersection in O log K  running time.
Note that since K $ 2 , we only need to compute Tv,2k for k 0, 1, 2, ..., 29.
30

This algorithm takes O N log K  extra running time to compute the values of Tv,2k , as each
of them can be computed in constant time. Then, we can find the destination of each path in
O log K . Thus, the total running time is O M  N Q log K , which is sufficient to fully solve
subtask 2.
4 An O M  N Q solution
Let us consider a more general question of determining whether a path starting at vertex s
with length K ends at vertex t. Recall that each vertex in G has exactly one outgoing edge. So,
from any initial vertex, by simply following these edges, we will eventually enter a cycle. Thus, if
we start at s, exactly one of the following conditions are met:

ˆ We never reach t.

ˆ We reach t just once exactly after some number of steps F . In this case, t is reachable, but
is not on any cycle.
ˆ We reach t for the first time after some number of steps F , and enter it every C steps. In
this case, t is reachable, and is on a cycle of size C.
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 909

For our purpose of solving the problem, s can vary depending on our initial vertex; namely, it
¬
can be any of the vertices vi for i 0, 1, ..., N  1. However, t can only be vertices vP and vP .
Since t does not vary very much, it is easier to check whether t is on a cycle, and whether it is
possible to reach t from s.
T
To solve this problem, we create the graph G , which is the same as graph G with its edges
reversed. Then, we perform a depth-first search on this graph starting at t. During this search,
we keep track of the distance of each reachable vertex from t. This number is the distance from s
to t in G; that is, the number of steps F that brings us from s to t for the first time. At the same
time, if we reach some vertex with a departing edge to t, then we obtain the size of the cycle C,
which is the distance from t to that vertex plus 1.
Thus, whether the path in G starting at s with length K ends at t can be determined as
following:
T
ˆ If we cannot reach s in G , then this path in G cannot end at t.
T
ˆ If we reach s in G after F steps, but t is not on a cycle, then this path in G ends at t if
and only if K F .
T
ˆ If we reach s in G after F steps, and t is in a cycle of size C, then this path in G ends at t
if and only if K F  nC for some non-negative integer n.

For our task, the path starting at the i-th intersection with length K will end at intersection
¬
P if and only if the path in G starting at vi with length K ends at vP or vP . Note that during
T
the implementation, we do not need to create graph G, but we can create G directly. It is also
convenient to first create an array for storing Fs for each vertex s. We then initialize Fs and C
to 1, and update them during the depth-first search. We perform the search twice, starting at vP
¬
and vP , respectively.
Since the depth-first search takes O M  N  running time and each query can be checked in
constant running time, this algorithm takes O M  N Q running time in total, which is sufficient
to obtain full marks for this task.

9.1.2 Coduri sursă

Listing 9.1.1: garden-49449.cpp


//https://oj.uz/submission/49449 187 ms 31352 KB

#include "garden.h"
#include "gardenlib.h"

#include<ctime>
#include<iostream>

#include <cstdio>
#include <vector>
#include <initializer_list>

using namespace std;

#define MAX_M 1000000


#define MAX_Q 2000

static int N, M, P, Q;
static int R[MAX_M][2];
static int G[MAX_Q];
static int solutions[MAX_Q];
static int answers[MAX_Q];
static int answer_count;

inline void my_assert(int e) {if (!e) abort();}

const int INF = 0x3f3f3f3f;


const int MAXN = 151515;
int minv1[MAXN], minv2[MAXN];
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 910

int outlist[2*MAXN];
vector<int> inlist[2*MAXN];
bool visit[2*MAXN];
int ans1[2*MAXN], ans2[2*MAXN];

void run(int P, int dist, int *ans, int &res, int oP)
{
if(visit[P])
{
if(P == oP) res = dist;
return;
}
visit[P] = true;
if(P<N) ans[dist]++;
for(auto x: inlist[P])
run(x, dist+1, ans, res, oP);
}

void count_routes(int _N, int M, int P, int R[][2], int Q, int G[])
{
N = _N;
for(int i=0; i<N; ++i) minv1[i] = minv2[i] = -1;
for(int i=0; i<M; ++i)
{
int u = R[i][0], v = R[i][1];
for(int x: {u, v})
{
int y = u+v-x;
if(minv1[x] == -1) minv1[x] = y;
else if(minv2[x] == -1) minv2[x] = y;
}
}

for(int i=0; i<N; ++i)


if(minv2[i] == -1)
minv2[i] = minv1[i];

for(int i=0; i<2*N; ++i)


{
int to = (i<N) ? minv1[i%N] : minv2[i%N];
if(i%N == minv1[to]) to += N;
outlist[i] = to;
inlist[to].push_back(i);
}

int res1 = 0x3f3f3f3f, res2 = 0x3f3f3f3f;


run(P, 0, ans1, res1, P);
for(int i=0; i<2*N; ++i) visit[i] = false;
run(P+N, 0, ans2, res2, P+N);

for(int i=0; i<Q; ++i)


{
int ans = 0;
for(int j=G[i]%res1; j<=G[i] && j <= N; j += res1)
ans += ans1[j];
for(int j=G[i]%res2; j<=G[i] && j <= N; j += res2)
ans += ans2[j];
answer(ans);
}
return ;
}

// ------------- begin grader -----------------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&M,&P));
for(i=0; i<M; i++)
my_assert(2==scanf("%d %d",&R[i][0],&R[i][1]));
my_assert(1==scanf("%d",&Q));
for(i=0; i<Q; i++)
my_assert(1==scanf("%d",&G[i]));
for(i=0; i<Q; i++)
my_assert(1==scanf("%d",&solutions[i]));
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 911

void answer(int x)
{
if(answer_count>=Q)
{
printf("Incorrect. Too many answers.\n");
exit(0);
}
answers[answer_count] = x;
answer_count++;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask3/grader.in.14", "r", stdin);


std::freopen("garden.out", "w", stdout);

int correct, i;

read_input();

auto t2 = clock();

answer_count = 0;
count_routes(N,M,P,R,Q,G);

auto t3 = clock();

if(answer_count!=Q)
{
printf("Incorrect. Too few answers.\n");
exit(0);
}

correct = 1;
for(i=0; i<Q; i++)
if(answers[i]!=solutions[i])
correct = 0;

if(correct)
printf("Correct.\n");
else
{
printf("Incorrect.\n");
printf("Expected: ");
for(i=0; i<Q; i++)
printf("%d ",solutions[i]);
printf("\nReturned: ");
for(i=0; i<Q; i++)
printf("%d ",answers[i]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
return 0;
}

// ------------- end grader -----------------------


/*
t2-t1 = 0.484
t3-t2 = 0.235
t4-t3 = 0

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


Press any key to continue.
*/
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 912

Listing 9.1.2: garden-49789.cpp


// https://oj.uz/submission/49789 69 ms 13788 KB

#include "garden.h"
#include "gardenlib.h"

#include<ctime>
#include<iostream>

#define MAX_M 1000000


#define MAX_Q 2000

static int N, M, P, Q;
static int R[MAX_M][2];
static int G[MAX_Q];
static int solutions[MAX_Q];
static int answers[MAX_Q];
static int answer_count;

inline void my_assert(int e) {if (!e) abort();}

#include <cstring>

using namespace std;

void answer(int x);

const int MAXN = 150000;


const int MAXN2 = MAXN * 2;

int arr[MAXN2 + 10];


int dis[2][MAXN2 + 10];
bool chk[2][MAXN2 + 10];

void f(int x, int p)


{
chk[p % 2][x] = 1;
if(arr[x] == p)
{
dis[p % 2][x] = 1;
return;
}

if(!chk[p % 2][arr[x]])
f(arr[x], p);

if(dis[p % 2][arr[x]])
dis[p % 2][x] = dis[p % 2][arr[x]] + 1;
}

int mem1[MAXN + 10];


int mem2[MAXN + 10];
int sum[2][MAXN2 + 10];

void count_routes(int N, int M, int P, int R[][2], int Q, int G[])


{
memset(mem1, -1, sizeof mem1);
memset(mem2, -1, sizeof mem2);
memset(arr, -1, sizeof arr);

for(int i = 0; i < M; i++)


{
int x = R[i][0];
int y = R[i][1];

if(mem1[x] == -1)
mem1[x] = y;
else if(mem2[x] == -1)
mem2[x] = y;

if(mem1[y] == -1)
mem1[y] = x;
else if(mem2[y] == -1)
mem2[y] = x;
}
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 913

for(int i = 0; i < N; i++)


{
if(mem1[mem1[i]] == i && mem2[mem1[i]] != -1)
arr[2 * i] = 2 * mem1[i] + 1;
else
arr[2 * i] = 2 * mem1[i];

if(mem2[i] != -1)
{
if(mem1[mem2[i]] == i && mem2[mem2[i]] != -1)
arr[2 * i + 1] = 2 * mem2[i] + 1;
else
arr[2 * i + 1] = 2 * mem2[i];
}
}

for(int r = 0; r < 2; r++)


for(int i = 0; i < 2 * N; i++)
if(!chk[r][i] && arr[i] != -1)
f(i, 2 * P + r);

for(int r = 0; r < 2; r++)


for(int i = 0; i < N; i++)
if(dis[r][2 * i])
sum[r][dis[r][2 * i]]++;

for(int r = 0; r < 2; r++)


{
int c = dis[r][2 * P + r];
if(!c)
continue;

for(int i = 1; i <= 2 * N; i++)


if(i - c >= 1)
sum[r][i] += sum[r][i - c];
}

int c[2] = { dis[0][2 * P], dis[1][2 * P + 1] };

if(!c[0])
c[0] = c[1];
if(!c[1])
c[1] = c[0];
for(int i = 0; i < Q; i++)
{
int ans = 0;
for(int r=0;r<2;r++)
{
int t = G[i];
if(t > 2 * N && c[r])
t -= ((t - 2 * N + c[r] - 1) / c[r]) * c[r];

if(t <= 2 * N)
ans += sum[r][t];
}
answer(ans);
}
}

// ------------- begin grader -----------------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&M,&P));
for(i=0; i<M; i++)
my_assert(2==scanf("%d %d",&R[i][0],&R[i][1]));
my_assert(1==scanf("%d",&Q));
for(i=0; i<Q; i++)
my_assert(1==scanf("%d",&G[i]));
for(i=0; i<Q; i++)
my_assert(1==scanf("%d",&solutions[i]));
}

void answer(int x)
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 914

{
if(answer_count>=Q)
{
printf("Incorrect. Too many answers.\n");
exit(0);
}
answers[answer_count] = x;
answer_count++;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask3/grader.in.14", "r", stdin);


std::freopen("garden.out", "w", stdout);

int correct, i;

read_input();

auto t2 = clock();

answer_count = 0;
count_routes(N,M,P,R,Q,G);

auto t3 = clock();

if(answer_count!=Q)
{
printf("Incorrect. Too few answers.\n");
exit(0);
}

correct = 1;
for(i=0; i<Q; i++)
if(answers[i]!=solutions[i])
correct = 0;
if(correct)
printf("Correct.\n");
else
{
printf("Incorrect.\n");
printf("Expected: ");
for(i=0; i<Q; i++)
printf("%d ",solutions[i]);
printf("\nReturned: ");
for(i=0; i<Q; i++)
printf("%d ",answers[i]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
return 0;
}

// ------------- end grader -----------------------


/*
t2-t1 = 0.421
t3-t2 = 0.063
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.1.3: garden-116557.cpp


// https://oj.uz/submission/116557 3046 ms 29732 KB
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 915

#include "garden.h"
#include "gardenlib.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#include <cstdio>
#include <vector>
#include <initializer_list>

#define MAX_M 1000000


#define MAX_Q 2000

static int N, M, P, Q;
static int R[MAX_M][2];
static int G[MAX_Q];
static int solutions[MAX_Q];
static int answers[MAX_Q];
static int answer_count;

inline void my_assert(int e) {if (!e) abort();}

#include <queue>

using namespace std;

const int INF=1e9+7;

static std::vector<int> adj[150005];


static int to[300005];
static std::vector<int> from[300005];

static int dist[2][300005];

static void bfs(int start,int* dist)


{
std::queue<int> q;
q.push(start);
while(!q.empty())
{
int node=q.front();
q.pop();
for(int child:from[node])
{
if(!dist[child])
{
dist[child]=dist[node]+1;
q.push(child);
}
}
}
}

void count_routes(int N, int M, int P, int R[][2], int Q, int G[])


{
for(int i=0;i<M;i++)
{
adj[R[i][0]].push_back(R[i][1]);
adj[R[i][1]].push_back(R[i][0]);
}

for(int i=0;i<N;i++)
{
to[i<<1]=(adj[i][0]<<1)|(i==adj[adj[i][0]][0]);
if(adj[i].size()>1)
{
to[i<<1|1]=(adj[i][1]<<1)|(i==adj[adj[i][1]][0]);
}
else
{
to[i<<1|1]=to[i<<1];
}
}
CAPITOLUL 9. IOI 2011 9.1. TROPICAL GARDEN 916

for(int i=0;i<N*2;i++)
{
from[to[i]].push_back(i);
}
bfs(P<<1,dist[0]);
bfs(P<<1|1,dist[1]);
int cycle[2];
cycle[0]=dist[0][P<<1]?dist[0][P<<1]:INF;
cycle[1]=dist[1][P<<1|1]?dist[1][P<<1|1]:INF;
for(int q=0; q<Q; q++)
{
int K=G[q];
int ans=0;
for(int i=0;i<N*2;i+=2)
{
if(dist[0][i]) ans+=(K>=dist[0][i]&&(K-dist[0][i])%cycle[0]==0);
if(dist[1][i]) ans+=(K>=dist[1][i]&&(K-dist[1][i])%cycle[1]==0);
}
answer(ans);
}
}

// ------------- begin grader -----------------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&M,&P));
for(i=0; i<M; i++)
my_assert(2==scanf("%d %d",&R[i][0],&R[i][1]));
my_assert(1==scanf("%d",&Q));
for(i=0; i<Q; i++)
my_assert(1==scanf("%d",&G[i]));
for(i=0; i<Q; i++)
my_assert(1==scanf("%d",&solutions[i]));
}

void answer(int x)
{
if(answer_count>=Q) {
printf("Incorrect. Too many answers.\n");
exit(0);
}
answers[answer_count] = x;
answer_count++;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask3/grader.in.14", "r", stdin);


std::freopen("garden.out", "w", stdout);

int correct, i;

read_input();

auto t2 = clock();

answer_count = 0;
count_routes(N,M,P,R,Q,G);

auto t3 = clock();

if(answer_count!=Q)
{
printf("Incorrect. Too few answers.\n");
exit(0);
}

correct = 1;
for(i=0; i<Q; i++)
if(answers[i]!=solutions[i])
correct = 0;
CAPITOLUL 9. IOI 2011 9.2. RACE 917

if(correct)
printf("Correct.\n");
else {
printf("Incorrect.\n");
printf("Expected: ");
for(i=0; i<Q; i++)
printf("%d ",solutions[i]);
printf("\nReturned: ");
for(i=0; i<Q; i++)
printf("%d ",answers[i]);
}

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader -----------------------


/*
t2-t1 = 0.109
t3-t2 = 2.985
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.1.4: checkerGarden.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask3/grader.in.14",
(char*)"garden.out",
(char*)"../tests/subtask3/grader.expect.14",
};

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

registerChecker("tournament", argc, argv);


compareRemainingLines();
}

9.1.3 *Rezolvare detaliată

9.2 Race
Problema 2 - Race 100 de puncte
Author: Martin Fixman
CAPITOLUL 9. IOI 2011 9.2. RACE 918

In conjunction with the IOI, Pattaya City will host a race: the International Olympiad in
Racing (IOR) 2011. As the host, we have to find the best possible course for the race.
In the Pattaya-Chonburi metropolitan area, there are N cities connected by a network of N  1
highways. Each highway is bidirectional, connects two different cities, and has an integer length in
kilometers. Furthermore, there is exactly one possible path connecting any pair of cities. That is,
there is exactly one way to travel from one city to another city by a sequence of highways without
visiting any city twice.
The IOR has specific regulations that require the course to be a path whose total length is
exactly K kilometers, starting and ending in different cities. Obviously, no highway (and therefore
also no city) may be used twice on the course to prevent collisions. To minimize traffic disruption,
the course must contain as few highways as possible.
Your task
Write a procedure best_path(N,K,H,L) that takes the following parameters:
ˆ N - the number of cities. The cities are numbered 0 through N  1.
ˆ K - the required distance for the race course.
ˆ H - a two-dimensional array representing highways. For 0 & i $ N  1, highway i connects
the cities H i0 and H i1.
ˆ L - a one-dimensional array representing the lengths of the highways. For 0 & i $ N  1, the
length of highway i is Li.

You may assume that all values in the array H are between 0 and N  1, inclusive, and that
the highways described by this array connect all cities as described above. You may also assume
that all values in the array L are integers between 0 and 1 000 000, inclusive.
Your procedure must return the minimum number of highways on a valid race course of length
exactly K. If there is no such course, your procedure must return -1.
Examples
Example 1
Consider the case shown in Figure 1, where N=4, K=3,
0 1 1
H= 1 2 L= 2
1 3 4
The course can start in city 0, go to city 1, and terminate in city 2. Its
length will be exactly 1 km + 2 km = 3 km, and it consists of two highways.
This is the best possible course; therefore best_path(N,K,H,L) must return 2.
Example 2
Consider the case shown in Figure 2, where N=3, K=3,
0 1 1
H= L=
1 2 1
There is no valid course. In this case, best_path(N,K,H,L) must return -1.
Example 3
Consider the case shown in Figure 3, where N=11, K=12,
0 1 3
0 2 4
2 3 5
3 4 4
4 5 6
H= L=
0 6 3
6 7 2
6 8 5
8 9 6
8 10 7
One possible course consists of 3 highways: from city 6
via city 0 and city 2 to city 3. Another course starts in city 10 and goes via city 8 to city 6. Both
of these courses have length exactly 12 km, as required. The second one is optimal, as there is no
valid course with a single highway. Hence, best_path(N,K,H,L) must return 2.
CAPITOLUL 9. IOI 2011 9.2. RACE 919

Subtasks
Subtask 1 (9 points)
ˆ 1 & N & 100
ˆ 1 & K & 100
ˆ The network of highways forms the simplest possible line: For 0 &i$ N  1, highway i
connects cities i and i  1.

Subtask 2 (12 points)


ˆ 1 & N & 1 000
ˆ 1 & K & 1 000 000

Subtask 3 (22 points)


ˆ 1 & N & 200 000
ˆ 1 & K & 100

Subtask 4 (57 points)


ˆ 1 & N & 200 000
ˆ 1 & K & 1 000 000

Implementation details
Limits
ˆ CPU time limit: 3 seconds
ˆ Memory limit: 256 MB Note: There is no explicit limit for the size of stack memory. Stack
memory counts towards the total memory usage.

Interface (API)
ˆ Implementation folder: race/
ˆ To be implemented by contestant: race.c or race.cpp or race.pas
ˆ Contestant interface: race.h or race.pas
ˆ Grader interface: race.h or racelib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas
ˆ Sample grader input: grader.in.1, grader.in.2, ... Note: The sample grader reads
the input in the following format:
– Line 1: N and K.
– Lines 2 to N : information on the highways; i.e., line i  2 contains H i0, H i1,
and Li, separated by a space, for 0i $ N  1.
– Line N  1: the expected solution.
ˆ Expected output for sample grader input: grader.expect.1, grader.expect.2, ...
For this task, each one of these files should contain precisely the text ”Correct.”

9.2.1 Indicaţii de rezolvare

Proposed by: Martin Fixman


˜
Given a tree T with N nodes, this task asks for a path P of length K with the minimum
number of edges. It looks like a usual dynamic programming task. However, when K is large,
another approach is required.
The model solution for this task follows a divide-and-conquer approach.
Consider a node u in the graph. There are two possible cases: when node u belongs to the
˜
solution path P ; or when node u does not.
In the second case, we can delete node u from the tree and break it into smaller trees. We can
then recurse on each of the resulted trees to find the solution.
With this general approach in mind, we have to answer the following questions.
CAPITOLUL 9. IOI 2011 9.2. RACE 920

ˆ How to find the best path that contains node u?

ˆ How to choose u to achieve a better running time?

Note that the second question is very important because if we can guarantee that the sizes of
all resulting trees are small, we can bound the number of recursion levels.
1 Finding the solution containing u
˜
Consider the case that P contains node u. Let’s consider a simpler case where we only want
to find if there exists a path of length exactly K that contains u.
˜
If u is one of the endpoints in P , we can find the path using one application of depth first
search (DFS).
˜ ˜
However, if u is ”inside” P , then two of u’s adjacent nodes x and y must also be in P . Thus,
we shall find x and y.
Consider some node w adjacent to u. With one application of DFS, we can find the set Lw of
all path lengths for all paths starting at u and containing edge u, w.
Hence, to find x and y, we need to find two nodes x and y such that there exists a pair lx " Lx
and ly " Ly for which lx  ly K. This can be done by DFS from u through every edge u, w
for all adjacent nodes w with careful book keeping using an array A0, ..., K  of size K  1.
The running time for this step is O N .
2 Finding the right node
Our goal is to find node u such that after deleting u, each resulting trees are all sufficiently
”small”. In this case, we shall find node u such that each remaining tree has at most N 2 nodes.
We shall refer to node u as the central node.
It is not clear if such a node exists. So let’s argue about that first.
¬
Pick an arbitrary node v as a candidate. Denote by T T rv x the forest obtained by deleting
¬
v from T . For each node w adjacent to v, denote by Tw the tree containing w in T . If every tree
¬
Tw " T has at most N 2 nodes, we are done and v is the required central node.
Otherwise there exists one tree Tw that contains more than N 2 nodes. (Note that there can
be only one tree violating our criteria.) In this case, we pick w as our new candidate and repeat
the process.
This process will eventually stop at some candidate node and that’s the required central node.
To see this, note that after leaving v, we shall never go back to pick v again; since there are N
nodes, the process can repeat at most N times.
After knowing that the central node exists, there are many ways to find it. We can follow the
process directly as in the argument. But this is too slow to be useful.
The following are two procedures that fid the central node in O N log N  time and O N  time.
2.1 Bottom-up approach
We can find node u in a bottom-up fashion. We shall keep a priority queue Q of all ”processed”
subtrees using their sizes as weights.
We maintain, for each node, its state which can either be new or processed; initially all nodes
are new. Every node also has a weight. Initially every node has weight of 1.
We start with all leaf nodes in Q. Note that each node in Q is every node which has all but
one of its adjacent nodes processed. For each node v " Q, we denote by p v  the unique neighbor
of v which is new.
While there are nodes in Q, we extract node v with the smallest weight. We update v state
to processed and increase the weight of p v  by v’s weight. If all but one neighbor of p v  are
processed, we insert v into Q.
Using this procedure, the last node inserted to Q is the desired central node.
2.2 DFS with bookkeeping
With DFS and a good bookkeeping, we can find the central node in O N .
We pick an arbitrary node r to start a DFS. With this procedure, we can consider T as rooted
at r and the parent-child relationship between adjacent pairs of nodes are clearly defined.
While performing DFS, we compute, for each node v, the number of its descendants D v .
With this information, we can figure out if a candidate u is the central node. For each node w
adjacent to u, if w is one of u’s children, the size of the resulting tree containing w after deleting
CAPITOLUL 9. IOI 2011 9.2. RACE 921

u is D w  1. If w is u’s parent, the size of the resulting tree containing w after deleting u is

n1 = D v   1,
v "Ch u

where Ch u are a set of children of u. If the size of each resulting tree is at most N 2, u is the
desired central node. The time needed to check u is proportional to u’s degree. Therefore, we can
check all nodes in time O N .
3 Running time
Let T N  be the worst-case running time when the tree has N nodes. We can write the
recurrence as
T N  A N   cN  =
T Ni ,
i

where A N  is the time for finding u, Ni is the size of the i-th new trees, and c is some
constant.
Since we know that Ni & N ©2, there are at most O log N  levels of the recursion.
If we use O N -time to find u, each level would run in time O N  and the total running time
is O N log N . If we use a slower O N log N -time procedure, the total running time will be
2
O N log N .
4 Notes
There are other heuristics for finding u that do not always work. Here are some examples.

ˆ In divide-and-conquer solution, the highest degree node is picked.


ˆ In divide-and-conquer solution, the node that minimizes the maximum distance to any node
is picked.

9.2.2 Coduri sursă

Listing 9.2.1: race-28930.cpp


// https://oj.uz/submission/28930 518 ms 66484 KB

#include "race.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 500000

static int N, K;
static int H[MAX_N][2];
static int L[MAX_N];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <vector>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int MAXN = 200005;


struct EDGE { int x, w; };
int ans = -1;

vector <EDGE> v[MAXN];

void f(int x, int par, unordered_map <int, int> &d, int &L, int &W)
CAPITOLUL 9. IOI 2011 9.2. RACE 922

{
d[0] = 0;
for (auto &y : v[x])
{
if (y.x == par) continue;
unordered_map <int, int> d2;
int L2 = 0, W2 = 0;
f(y.x, x, d2, L2, W2);
L2++;
W2 += y.w;
if (d.size() < d2.size())
{
d.swap(d2);
swap(L, L2);
swap(W, W2);
}

for (auto &t : d2)


{
if (d.count(K - (t.first + W2 + W)))
{
if (ans == -1 ||
ans > d[K - (t.first + W2 + W)] + t.second + L + L2)
ans = d[K - (t.first + W2 + W)] + t.second + L + L2;
}
}

for (auto &t : d2)


{
if (t.first + W2 > K) continue;
if (t.first + W2 == K)
{
if (ans == -1 || ans > t.second + L2)
ans = t.second + L2;
}
if (d.count(t.first + W2 - W))
d[t.first + W2 - W] = min(d[t.first + W2 - W],
t.second + L2 - L);
else
d[t.first + W2 - W] = t.second + L2 - L;
}
}
}

int best_path(int _N, int _K, int H[][2], int L[])


{
N = _N, K = _K;
for (int i = 0; i < N - 1; i++)
{
v[H[i][0]].push_back({ H[i][1], L[i] });
v[H[i][1]].push_back({ H[i][0], L[i] });
}

unordered_map <int, int> d;


int A = 0, B = 0;
f(0, -1, d, A, B);

return ans;
}

// ----------------------- begin grader -----------------

void read_input()
{
int i;
my_assert(2==scanf("%d %d",&N,&K));
for(i=0; i<N-1; i++)
my_assert(3==scanf("%d %d %d",&H[i][0],&H[i][1],&L[i]));
my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask4/grader.in.16", "r", stdin);


CAPITOLUL 9. IOI 2011 9.2. RACE 923

std::freopen("rase.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = best_path(N,K,H,L);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------------------- end grader -----------------


/*
t2-t1 = 0.203
t3-t2 = 1.187
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.2.2: race-112526.cpp


// https://oj.uz/submission/112526 588 ms 85672 KB

#include<ctime>
#include<iostream>

#define MAX_N 500000

static int N, K;
static int H[MAX_N][2];
static int L[MAX_N];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> P;

#define F first
#define S second
#define PB push_back
#define INF 1000000000

int n,k,vs[200005],w[200005],la[200005],ans=INF;
vector<P>g[200005];
map<int,int>m[200005];

void merge(int x,int y)


{
if(m[vs[x]].size()<m[vs[y]].size()) swap(x,y);
int vx=vs[x],vy=vs[y];
for(map<int,int>::iterator i=m[vy].begin(); i!=m[vy].end(); i++)
{
CAPITOLUL 9. IOI 2011 9.2. RACE 924

if(m[vx].find(k-w[vx]-w[vy]-i->F)!=m[vx].end())
ans=min(ans,m[vx][k-w[vx]-w[vy]-i->F] + i->S + la[vx]+la[vy]);
}

for(map<int,int>::iterator i=m[vy].begin();i!=m[vy].end();i++)
{
int c=i->F-w[vx]+w[vy];
if(m[vx].find(c)!=m[vx].end())
m[vx][c]=min(m[vx][c],i->S-la[vx]+la[vy]);
else
m[vx][c]=i->S-la[vx]+la[vy];
}

vs[y]=vx;
}

void dfs(int v,int p)


{
for(int i=0;i<g[v].size();i++)
{
int u=g[v][i].F,l=g[v][i].S;
if(u==p)continue;
dfs(u,v);
w[vs[u]]+=l;
la[vs[u]]++;
merge(v,u);
}
}

int best_path(int N,int K,int H[][2],int L[])


{
n=N;
k=K;
for(int i=0;i<n-1;i++)
{
int a=H[i][0],b=H[i][1],c=L[i];
g[a].PB(P(b,c));
g[b].PB(P(a,c));
}

for(int i=0;i<n;i++)
vs[i]=i,m[i][0]=0;

dfs(0,-1);

if(ans==INF)return -1;
else return ans;
}

// ----------------------- begin grader -----------------

void read_input()
{
int i;
my_assert(2==scanf("%d %d",&N,&K));
for(i=0; i<N-1; i++)
my_assert(3==scanf("%d %d %d",&H[i][0],&H[i][1],&L[i]));
my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask4/grader.in.16", "r", stdin);


std::freopen("rase.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = best_path(N,K,H,L);

auto t3 = clock();
CAPITOLUL 9. IOI 2011 9.2. RACE 925

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------------------- end grader -----------------


/*
t2-t1 = 0.734
t3-t2 = 1.532
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.2.3: race-216556.cpp


// https://oj.uz/submission/216556 488 ms 33752 KB

#include "race.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 500000

static int N, K;
static int H[MAX_N][2];
static int L[MAX_N];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <bits/stdc++.h>

using namespace std;

#define in ({int x=0; \


int c=getchar(),n=0; \
for(;!isdigit(c);c=getchar()) n=(c==’-’); \
for(;isdigit(c);c=getchar()) x=x*10+c-’0’; \
n?-x:x;})

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l,int r){return l+rng()%(r-l+1);}

#define fasty ios_base::sync_with_stdio(0),cin.tie(0);


#define forinc(a,b,c) for(int a=b,_c=c;a<=_c;++a)
#define fordec(a,b,c) for(int a=b,_c=c;a>=_c;--a)
#define forv(a,b) for(auto&a:b)
#define fi first
#define se second
#define pb push_back
#define ii pair<int,int>
#define mt make_tuple
#define all(a) a.begin(),a.end()
#define reset(f, x) memset(f, x, sizeof(f))
#define bit(x,i) ((x>>(i-1))&1)
#define on(x,i) (x|(1ll<<(i-1)))
#define off(x,i) (x&˜(1<<(i-1)))
#define gg exit(0);
CAPITOLUL 9. IOI 2011 9.2. RACE 926

//#define unx 1
#ifndef unx
#include "race.h"
#endif

const int NN=200010;

int n,cnt,k,ret;
int sz[NN],dd[NN],f[NN*5];
vector<ii> val;
vector<ii> ad[NN];

void dfs1(int u,int p=-1)


{
sz[u]=1; cnt++;
forv(v,ad[u])
if(!dd[v.fi] && v.fi!=p)
dfs1(v.fi,u), sz[u]+=sz[v.fi];
}

int ftr(int u,int p=-1)


{
forv(v,ad[u])
if(!dd[v.fi] && v.fi!=p && sz[v.fi]>cnt/2)
return ftr(v.fi,u);
return u;
}

void dfs2(int u,int p,int c,int d)


{
if(c>k) return;
val.push_back({c,d});
forv(v,ad[u])
if(!dd[v.fi] && v.fi!=p && v.se+c<=k)
dfs2(v.fi,u,c+v.se,d+1);
}

void ctr(int u)
{
cnt=0;
dfs1(u);
dd[u=ftr(u)]=1;

f[0]=0;
vector<int> reval;
forv(v,ad[u])
if(!dd[v.fi])
{
dfs2(v.fi,u,v.se,1);
forv(i,val)
if(f[k-i.fi]>-1)
ret=min(ret,i.se+f[k-i.fi]);
forv(i,val)
f[i.fi]=f[i.fi]<0 ? i.se :
min(f[i.fi],i.se), reval.push_back(i.fi);
val.clear();
}

forv(i,reval) f[i]=-1;

forv(v,ad[u])
if(!dd[v.fi]) ctr(v.fi);
}

int best_path(int n,int _k,int h[][2],int l[])


{
k=_k;
ret=n;
forinc(i,0,n-2)
{
int u=h[i][0], v=h[i][1], w=l[i];
ad[u].push_back({v,w});
ad[v].push_back({u,w});
}
reset(f,-1);
CAPITOLUL 9. IOI 2011 9.2. RACE 927

ctr(0);
return ret<n ? ret : -1;
}

// ----------------------- begin grader -----------------

void read_input()
{
int i;
my_assert(2==scanf("%d %d",&N,&K));
for(i=0; i<N-1; i++)
my_assert(3==scanf("%d %d %d",&H[i][0],&H[i][1],&L[i]));
my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask4/grader.in.16", "r", stdin);


std::freopen("rase.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = best_path(N,K,H,L);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------------------- end grader -----------------


/*
t2-t1 = 0.188
t3-t2 = 1
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.2.4: checkerRace.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask4/grader.in.16",
(char*)"rase.out",
(char*)"../tests/subtask4/grader.expect.16",
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 928

};

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

registerChecker("race", argc, argv);


compareRemainingLines();
}

9.2.3 *Rezolvare detaliată

9.3 Rice Hub


Problema 3 - Rice Hub 100 de puncte

Author: Christian Kauth

In the countryside, you can find a long straight road known as the Rice Way. Along this road
there are R rice fields. Each field is located at an integer coordinate between 1 and L, inclusive.
The rice fields will be presented in non-decreasing order of their coordinates. Formally, for
0 & i $ R, rice field i is at coordinate X i. You may assume that 1 & X 0 & ... & X R  1 & L.
Please note that multiple rice fields may share the same coordinate.
We plan to construct a single rice hub as a common place to store as much of the harvest as
possible. As with the rice fields, the hub has to be at an integer coordinate between 1 and L,
inclusive.
The rice hub can be at any location, including one that already contains one or more rice fields.
Each rice field produces exactly 1 truckload of rice every harvest season. To transport the rice
to the hub, the city has to hire a truck driver. The driver charges 1 Baht to transport a truckload
of rice per unit of distance towards the hub. In other words, the cost of transporting rice from a
given field to the rice hub is numerically equal to the difference between their coordinates.
Unfortunately, our budget for this season is tight: we may only spend at most B Baht on
transportation. Your task is to help us strategically place the hub to gather as much rice as
possible.
Your task
Write a procedure besthub(R,L,X,B) that takes the following parameters:
ˆ R - the number of rice fields. The fields are numbered 0 through R  1.
ˆ L - the maximum coordinate.
ˆ X - a one-dimensional array of integers sorted from smallest to largest. For each 0 & i $ R,
field i is located at X i.
ˆ B - the budget.

Your procedure must find an optimal location of the hub and return the maximum number of
truckloads of rice that can be transported to the hub within the budget.
Note that the total cost of transporting the rice can be very large. The budget is given as a
64-bit integer, and we recommend that you use 64-bit integers in your computation. In C/C++,
use the type long long; in Pascal, use the type Int64.
Example
1
2
Consider the case where R 5, L 20, B 6, and X = 10
12
14
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 929

For this case, there are multiple optimal locations for the hub: you can place it anywhere
between locations 10 and 14, inclusive. The figure above shows one of these optimal locations.
You will then be able to transport rice from fields at coordinates 10, 12, and 14. For any optimal
hub location, the total cost of this transportation will be at most 6 Baht. Clearly, no hub location
will allow us to gather rice from more than three fields, hence this solution is optimal and besthub
should return 3.
Subtasks
Subtask 1 (17 points)
ˆ 1 & R & 100
ˆ 1 & L & 100
ˆ 0 & B & 10 000
ˆ No two rice fields share the same coordinate (only for this subtask).

Subtask 2 (25 points)


ˆ 1 & R & 500
ˆ 1 & L & 10 000
ˆ 0 & B & 1 000 000

Subtask 3 (26 points)


ˆ 1 & R & 5 000
ˆ 1 & L & 1 000 000
ˆ 0 & B & 2 000 000 000

Subtask 4 (32 points)


ˆ 1 & R & 100 000
ˆ 1 & L & 1 000 000 000
ˆ 0 & B & 2 000 000 000 000 000

Implementation details
Limits
ˆ CPU time limit: 1 second
ˆ Memory limit: 256 MB Note: There is no explicit limit for the size of stack memory. Stack
memory counts towards the total memory usage.

Interface (API)
ˆ Implementation folder: ricehub/
ˆ To be implemented by contestant: ricehub.c or ricehub.cpp or ricehub.pas
ˆ Contestant interface: ricehub.h or ricehub.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas
ˆ Sample grader input: grader.in.1, grader.in.2, ...
Note: The sample grader reads the input in the following format:
– Line 1: R, L, and B.
– Lines 2 to R  1: locations of rice fields; i.e., line i  2 contains X i, for 0 & i $ R.
– Line R  2: the expected solution.
ˆ Expected output for sample grader input: grader.expect.1, grader.expect.2, ...
For this task, each one of these files should contain precisely the text ”Correct.”
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 930

9.3.1 Indicaţii de rezolvare

Proposed by: Christian Kauth

The key insight to solving this problem is the observation that for any K rice fields located
at r0 & r1 & ... & rK 1 , the transportation cost from all these K fields is minimized by placing
the rice hub at a median. For example, when K 1, the hub should be at r0 , and when K 2,
placing it between r0 and r1 is optimal.
In this problem, we will place the rice hub at rK ©2$ for simplicity.
Following this observation, we denote a solution by a sequence S N …r0 , ..., rR1 ‹ and let ¶S ¶
denote the length of S, which is the solution’s value (the number of rice fields whose rice will be
transported to the hub). The cost of S is

cost S  = ¶r j  h S ¶,
rj "S

where h S  is the ¶S ¶©2$-th element of S.


3
1 An O R  solution
Armed with this, we can solve the task by a guess-and-verify algorithm. We try all possible
˜
lengths of S (ranging between 1 and R). Next observe that in any optimal solution S , the rice
˜
fields involved must be contiguous; that is, S is necessarily …rs , rs1 , ..., rt ‹ for some 0 & s & t &
R  1. Therefore, there are R  K  1 solutions of length K.
For each choice of S, we compute h S  and the transportation cost in O ¶S ¶ time and check
if it is within the budget B.
3
This leads to an O R  algorithm, which suffices to solve subtask 2.
2
2 An O R  solution
2
To improve it to O R , we will speed up the computation of cost S . Notice that we are only
dealing with consecutive rice fields. Thus, for each S, the cost cost S  can be computed in O 1
after precomputing certain prefix sums.
Specifically, let T i be the sum of all coordinates to the left of rice field i, i.e., T 0 0 and
i1
T i = X j .
j 0

Then, if S …rs , ..., rt ‹, cost S  is given by

p  srp  T p  T s  T t  1  T p  1  t  prp ,

where p  s  t©2$.
2
This O R  algorithm suffices to solve subtask 3.
3 An O R log R solution
Applying a binary search to find the right length in place of a linear search improves the
running time to O R log R and suffices to solve all subtasks.
4 An O R solution
We replace binary search with a variant of linear search carefully designed to take advantage
of the feedback obtained each time we examine a combination of rice fields. In particular, imagine
˜
adding in the rice fields one by one. In iteration i, we add ri and find (1) Si i, the best solution
˜
that uses only (a subsequence of) the first i rice fields (i.e., Si N …r0 , ..., ri  1‹, and (2) Si , the
best solution that uses only (a subsequence of) the first i rice fields and contains ri  1.
˜
This can be computed inductively as follows. As a base case, when i 0, both Si and Si
are just …r0 ‹ and cost 0, which is within the budget B ' 0. For the inductive case, assume that
˜
Si and Si are known. Now consider that Si1 is Si appended with ri , denoted by Si ri , if the
cost cost Si ri  is at most B, or otherwise it is the longest prefix of Si ri that costs at most
˜ ˜
B. Futhermore, Si1 is the better of Si and Si1 . To implement this, we represent each Si by its
starting point s and ending point t; thus, each iteration involves incrementing t and possibly s,
but s is always at most t.
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 931

Since cost …rs , ..., rt ‹ takes O 1 to compute, the running time of this algorithm is O R and
suffices to solve all subtasks.

9.3.2 Coduri sursă

Listing 9.3.1: racehub-7321.cpp


// https://oj.uz/submission/7321 24 ms 4992 KB

#include "ricehub.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_R 1000000

static int R, L;
static long long B;
static int X[MAX_R];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

int besthub(int N, int L, int *X, long long B)


{
int A = 0;
long long S = 0;
for (int i=0,j=0;j<N;)
{
for (S+=X[j++]-X[(i+j)/2];S>B;S-=X[(i+j)/2]-X[i++]);
if (A < j - i) A = j - i;
}
return A;
}

// --------------- begin grader ---------------

static void read_input()


{
int i;
my_assert(3==scanf("%d %d %lld",&R,&L,&B));

for(i=0; i<R; i++)


my_assert(1==scanf("%d",&X[i]));

my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask4/grader.in.19", "r", stdin);


std::freopen("rasehub.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = besthub(R,L,X,B);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 932

printf("Incorrect. Returned %d instead of %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// --------------- end grader ---------------


/*
t2-t1 = 0.046
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.3.2: racehub-95096.cpp


// https://oj.uz/submission/95096 17 ms 3420 KB

#include<ctime>
#include<iostream>

#define MAX_R 1000000

static int R, L;
static long long B;
static int X[MAX_R];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


const int Nmax = 1e5 + 5;

ll x[Nmax], s[Nmax], lim;


int n;

bool check(int nr)


{
int i, m;
ll curr;
for(i=0; i<=n-nr; ++i)
{
m = (i + i + nr - 1) / 2;

curr = (m - i + 1) * x[m] - (s[m] - s[i-1]);


curr += (s[i+nr-1] - s[m]) - (i+nr-1 - m) * x[m];
if(curr <= lim) return 1;
}
return 0;
}

int besthub(int R, int L, int X[], ll B)


{
int st = 0, dr = R, mid, i;

n = R; lim = B;
for(i=0; i<n; ++i)
x[i] = X[i], s[i] = s[i-1] + x[i];

while(st <= dr)


{
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 933

mid = (st + dr) / 2;


if(check(mid)) st = mid+1;
else dr = mid-1;
}
return dr;
}

// --------------- begin grader ---------------

static void read_input()


{
int i;
my_assert(3==scanf("%d %d %lld",&R,&L,&B));

for(i=0; i<R; i++)


my_assert(1==scanf("%d",&X[i]));

my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask4/grader.in.19", "r", stdin);


std::freopen("rasehub.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = besthub(R,L,X,B);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d instead of %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// --------------- end grader ---------------


/*
t2-t1 = 0.187
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.3.3: racehub-113697.cpp


// https://oj.uz/submission/113697 16 ms 1792 KB

#include<ctime>
#include<iostream>

#define MAX_R 1000000

static int R, L;
static long long B;
static int X[MAX_R];
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 934

static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include<bits/stdc++.h>

using namespace std;

int besthub(int r,int l,int x[],long long int b)


{
int M=0;
int ini=0,fim=0;
long long int soma=0;
while(fim<r)
{
if(soma<=b)
{
M=max(M,fim-ini+1);
int med=(fim+ini+1)/2;
fim++;
soma+=(x[fim]-x[med]);
}
else
{
int med=(fim+ini+1)/2;
soma-=(x[med]-x[ini]);
ini++;
}
}
return M;
}

// --------------- begin grader ---------------

static void read_input()


{
int i;
my_assert(3==scanf("%d %d %lld",&R,&L,&B));

for(i=0; i<R; i++)


my_assert(1==scanf("%d",&X[i]));

my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask4/grader.in.19", "r", stdin);


std::freopen("rasehub.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = besthub(R,L,X,B);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d instead of %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 935

// --------------- end grader ---------------


/*
t2-t1 = 0.187
t3-t2 = 0
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.3.4: racehub-116283.cpp


// https://oj.uz/submission/116283 18 ms 2936 KB

#include "ricehub.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_R 1000000

static int R, L;
static long long B;
static int X[MAX_R];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <vector>

int besthub(int R, int L, int X[], long long B)


{
std::vector<long long> prefix({0});
for(int i=0;i<R;i++)
{
prefix.push_back(prefix.back()+X[i]);
}
int best=0;
int j=1;
for(int i=1;i<=R;i++)
{
while(true)
{
int kl=(i+j)/2;
int kr=(i+j+1)/2;
long long cost=(prefix[i]-prefix[kr-1])-(prefix[kl]-prefix[j-1]);
if(cost<=B) break;
j++;
}
best=std::max(best,i-j+1);
}
return best;
}

// --------------- begin grader ---------------

static void read_input()


{
int i;
my_assert(3==scanf("%d %d %lld",&R,&L,&B));

for(i=0; i<R; i++)


my_assert(1==scanf("%d",&X[i]));

my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();
CAPITOLUL 9. IOI 2011 9.3. RICE HUB 936

std::freopen("../tests/subtask4/grader.in.19", "r", stdin);


std::freopen("rasehub.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = besthub(R,L,X,B);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d instead of %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// --------------- end grader ---------------


/*
t2-t1 = 0.062
t3-t2 = 0.016
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.3.5: checkerRaceHub.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask4/grader.in.19",
(char*)"rasehub.out",
(char*)"../tests/subtask4/grader.expect.19",
};

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

registerChecker("racehub", argc, argv);


compareRemainingLines();
}
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 937

9.3.3 *Rezolvare detaliată

9.4 Crocodile’s Underground City


Problema 4 - Crocodile’s Underground City 100 de puncte
Author: Mihai Pătraşcu (România)
Archaeologist Benjamas is running for her life after investigating the mysterious Crocodile’s
Underground City. The city has N chambers. There are M bidirectional corridors, each connecting
a different pair of distinct chambers. Running through different corridors may require different
amounts of time. Only K of the N chambers are exit chambers that allow her to escape. Benjamas
starts in chamber 0. She wants to reach an exit chamber as quickly as possible.
The Crocodile gatekeeper wants to prevent Benjamas from escaping. From his den, he controls
secret doors that can block any single corridor. That is, whenever he blocks a new corridor, the
previously blocked one has to be reopened.
Benjamas’s situation can be described as follows: Each time she tries to leave a chamber,
the Crocodile gatekeeper may choose to block one of the corridors adjacent to it. Benjamas
then chooses and follows one of the unblocked corridors to the next chamber. Once Benjamas
enters a corridor, the Crocodile gatekeeper may not block it until Benjamas reaches the other end.
Once she enters the next chamber, the gatekeeper may again choose to block one of the outgoing
corridors (possi - bly the corridor that Benjamas just followed), and so on.
She would like to have a simple escape plan in advance. More precisely, she would like to have
a set of instructions that tell her what to do when she gets to a chamber. Let A be one of the
chambers. If it is an exit chamber, no instructions are needed-obviously, she can escape the city.
Otherwise, the instruction for chamber A should have one of the following forms:
ˆ ”If you ever reach chamber A, take the corridor leading to chamber B. However, if that
corridor is blocked, then take the corridor leading to chamber C.”
ˆ ”Don’t bother about chamber A; according to this escape plan you cannot possibly reach
it.”
Note that in some cases (for example, if your plan directs Benjamas to run in a cycle) the
gatekeeper may be able to prevent Benjamas from reaching an exit. An escape plan is good if
Benjamas is guaranteed to reach an exit chamber after a finite amount of time, regardless of what
the gatekeeper does. For a good escape plan, let T be the smallest time such that after time T ,
Benjamas is guaranteed to reach an exit. In that case, we say that the good escape plan takes time
T.
Your task
Write a procedure travel_plan(N,M,R,L,K,P) that takes the following parameters:
ˆ N - the number of chambers. The chambers are numbered 0 through N  1.
ˆ M - the number of corridors. The corridors are numbered 0 through M  1.
ˆ R - a two-dimensional array of integers representing the corridors. For 0 & i $ M , corridor
i connects two distinct chambers Ri0 and Ri1. No two corridors join the same pair
of chambers.
ˆ L - a one-dimensional array of integers containing the times needed to traverse the corridors.
For 0 & i $ M , the value 1 & Li & 1 000 000 000 is the time Benjamas needs to runthrough
the i-th corridor.
ˆ K - the number of exit chambers. You may assume that 1 & K $ N .
ˆ P - a one-dimensional array of integers with K distinct entries describing the exit chambers.
For 0 & i $ K, the value P i is the number of the ith exit chamber. Chamber 0 will never
be one of the exit chambers.
Your procedure must return the smallest time T for which there exists a good escape plan that
takes time T .
You may assume that each non-exit chamber will have at least two corridors leaving it. You
may also assume that in each test case there is a good escape plan for which T & 1 000 000 000.
Examples
Example 1
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 938

Consider the case shown in Figure 1,


where N=5, M=4, K=3, and
0 1 2
1
0 2 3
R= L= P= 3
3 2 1
4
2 4 4
Chambers are shown as circles, and corridors connecting them are shown
as lines. Exit chambers are shown as thick-bordered circles. Benjamas
starts at chamber 0 (marked by a triangle). An optimal escape plan is the
following one:
ˆ If you ever reach chamber 0, take the corridor leading to chamber 1. However, if that corridor
is blocked, then take the corridor leading to chamber 2.
ˆ If you ever reach chamber 2, take the corridor leading to chamber 3. However, if that corridor
is blocked, then take the corridor leading to chamber 4.

In the worst case, Benjamas will reach an exit chamber in 7 units of time. Hence,
travel_plan should return 7.
Example 2
Consider the case shown in Figure 2,
where N=5, M=7, K=2, and
0 2 4
0 3 3
3 2 2
1
R= 2 1 L = 10 P=
3
0 1 100
0 4 7
3 4 9
Here is an optimal escape plan:
ˆ If you ever reach chamber 0, take the corridor leading to chamber 3. However, if that corridor
is blocked, then take the corridor leading to chamber 2.
ˆ If you ever reach chamber 2, take the corridor leading to chamber 3. However, if that corridor
is blocked, then take the corridor leading to chamber 1.
ˆ Don’t bother about chamber 4; according to this escape plan you cannot possibly reach it.

Benjamas will reach one of the exit chambers no later than after 14 units of time. Therefore,
travel_plan should return 14.
Subtasks
Subtask 1 (46 points)
ˆ 3 & N & 1 000.
ˆ The underground city is a tree. That is, M N  1 and for each pair of chambers i and j
there is a sequence of corridors connecting i and j.
ˆ Every exit chamber is connected to exactly one other chamber.
ˆ Any other chamber is connected directly to three or more other chambers.

Subtask 2 (43 points)


ˆ 3 & N & 1 000.
ˆ 2 & M & 100 000.

Subtask 3 (11 points)


ˆ 3 & N & 100 000.
ˆ 2 & M & 1 000 000.

Implementation details
Limits
ˆ CPU time limit: 2 seconds
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 939

ˆ Memory limit: 256 MB


Note: There is no explicit limit for the size of stack memory. Stack memory counts towards
the total memory usage.

Interface (API)
ˆ Implementation folder: crocodile/
ˆ To be implemented by contestant: crocodile.c or crocodile.cpp or crocodile.pas
ˆ Contestant interface: crocodile.h or crocodile.pas
ˆ Grader interface: crocodile.h or crocodilelib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas and crocodilelib.pas
ˆ Sample grader input: grader.in.1, grader.in.2, ...
Note: The sample grader reads the input in the following format:
– Line 1: N , M , and K.
– Lines 2 to M  1: For 0 & i $ M , line i  2 contains Ri0, Ri1, and Li,
separated by a space.
– Line M  2: a list of K integers P 0, P 1, ..., P K  1, separated by a space.
– Line M  3: the expected solution.
ˆ Expected output for sample grader input: grader.expect.1, grader.expect.2, ...
For this task, each one of these files should contain precisely the text ”Correct.”

9.4.1 Indicaţii de rezolvare

Proposed by: Mihai Pătraşcu


1 Subtask 1: Trees
In this subtask, we can infer that the underlying graph is a tree and the exit chambers are
precisely the leaves. This is simpler than the general case but will be instructive for the next
subtasks. To begin, let L u, v  denote the time to travel along the corridor connecting node u to
node v. For simplicity, we will root the tree at the starting node (chamber 0).
The crucial observation then is that in any successful escape plan, the instruction at each
node, if reachable from the root, will always tell Somying to move further away from the root
(otherwise, she can be forced to cycle around the underground city forever). This leads to a
simple dynamic programming (DP) solution: Let T u denote the best time to reach an exit
chamber. First, T u 0 if u is a leaf. Otherwise, if u has children v1 , ..., vk , ordered such that
T v1  L u, v1  & T v2   L u, v2  & ... & T vk   L u, vk , then T u T v2   L u, v2 . The
problem statement guarantees that k ' 2.
It is easy to show inductively that T u is indeed the best time starting at u. Specifically,
we prove that first, Benjamas can reach an exit chamber from u in T u time regardless of what
the gatekeeper does, and second, the gatekeeper can force Benjamas to spend T u time in the
underground city. Clearly, if u is a leaf node and hence an exit chamber, T u is 0. Assuming
inductively that T vi  is as claimed for each child vi of u, we have that in time T vi  L u, vi ,
Benjamas can reach an exit from u, through u, vi , if the corridor is not blocked. Since the evil
crocodile can only block one corridor at a time, he can force Benjamas to spend T v2   L u, v2 ,
by blocking u, v1  - blocking any other corridor only helps Benjamas reach an exit faster, in
T v1   L u, v1  time.
To compute this DP, we traverse the tree in postorder (i.e., the leaves are visited first and each
parent is visited after all its children); the final answer is stored in T 0. The computation at
each node involves finding the second smallest value, which can be done in O du  time. Here, du
denotes the (out-)degree of u.
Therefore, the total running time is C < u du O N , for some positive constant C, because
the degrees of the tree nodes sum to 2 N  1.
2 Subtasks 2 and 3: General Graphs
The challenge in generalizing our current algorithm to the general setting is the lack of a clear
sense of direction; in our current algorithm, we know Benjamas must always move further from
the root as necessitated by the tree structure.
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 940

A moment’s thought reveals striking similarity between Dijkstra’s single-source shortest path
algorithm and our algorithm for trees. Indeed, the algorithm iteratively grows the frontier set,
where at any point in time, a node u is in the set if T u has been determined. From this view, our
algorithm for trees can be seen as running Dijkstra’s algorithm starting from the exit chambers.
The algorithm is standard except for how the cost at a node is defined.
Consider the following algorithm: For all nodes u, set T u to 1 except when u is an exit
chamber, set T u 0. Initially, the frontier set S contains exactly the exit chambers. During
the execution of the algorithm, we maintain that for w Š S, the cost of w can be conceptually
computed by producing the list rv " N w  T v   L w, v x, sorting this list, and returning
40

the second value (i.e., the second smallest value in this list). When a node u enters the frontier
(it has the lowest cost among non-frontier nodes), T u is set to the cost of u at that moment.
Claim 1. For each node u, Benjamas can reach an exit from u in T u time regardless of
what the gatekeeper does. Furthermore, the crocodile gatekeeper can force Benjamas to spend T u
time in the underground city.
This claim can be shown by a similar inductive argument as in the tree case and by observing
that as in Dijkstra’s algorithm, once a node enters the frontier its cost cannot decrease because
the edges have positive cost.
Implementation Details. For each node, we can maintain its cost by keeping two numbers
- the smallest value and the second smallest value - which can be updated in O 1 when the
neighboring values change. Using this, Dijkstra’s algorithm takes O M  N  log N  using a heap
2
or O N  without using one.

9.4.2 Coduri sursă

Listing 9.4.1: crocodile-10431.cpp


//https://oj.uz/submission/10431 576 ms 161392 KB

#include "crocodile.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 50000


#define MAX_M 10000000

static int N, M;
static int R[MAX_M][2];
static int L[MAX_M];
static int K;
static int P[MAX_N];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <vector>
#include <queue>
#include <utility>

using namespace std;

typedef pair<int,int> pi;

int v0[100005], v1[100005];


priority_queue<pi,vector<pi>,greater<pi> > pq;
vector<pi> graph[100005];

int travel_plan(int N, int M, int R[][2], int L[], int K, int P[])
{
40
N w denotes the set of neighbors of w
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 941

for (int i=0; i<M; i++)


{
graph[R[i][0]].push_back(pi(L[i],R[i][1]));
graph[R[i][1]].push_back(pi(L[i],R[i][0]));
}

for (int i=0; i<K; i++)


{
v0[P[i]] = 1;
pq.push(pi(0,P[i]));
}

while (!pq.empty())
{
pi x = pq.top();
pq.pop();
if(v0[x.second] == 0)
{
v0[x.second] = 1;
continue;
}

if(v1[x.second]) continue;
v1[x.second] = 1;
if(x.second == 0) return x.first;
for (int i=0; i<graph[x.second].size(); i++)
{
pi t = graph[x.second][i];
if(v1[t.second]) continue;
pq.push(pi(t.first + x.first,t.second));
}
}

return -123; // ... !!!


}

// -------------- begin grader -----------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&M,&K));
for(i=0; i<M; i++)
my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
for(i=0; i<K; i++)
my_assert(1==scanf("%d",&P[i]));
my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask3/grader.in.7", "r", stdin);


std::freopen("crocodile.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = travel_plan(N,M,R,L,K,P);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 942

std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- begin grader -----------------


/*
t2-t1 = 0.922
t3-t2 = 2.704
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.4.2: crocodile-16656.cpp


// https://oj.uz/submission/16656 444 ms 146380 KB

#include "crocodile.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 50000


#define MAX_M 10000000

static int N, M;
static int R[MAX_M][2];
static int L[MAX_M];
static int K;
static int P[MAX_N];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <queue>

#define NN 100000
#define MM 1000000
#define INF 1000000000

using namespace std;

struct Inque
{
int x;
int w;
Inque(int _x=0,int _w=0):x(_x),w(_w){}
bool operator<(const Inque &r)const
{
return w>r.w;
}
};

int sta[NN+1],chi[MM*2+1],wei[MM*2+1],nxt[MM*2+1],m,n;
int d[2][NN+1];
bool v[NN+1];

priority_queue<Inque> Q;

void addEdge(int x,int y,int w,int p)


{
nxt[p]=sta[x];
chi[p]=y;
wei[p]=w;
sta[x]=p;
}

int travel_plan(int _n, int _m, int R[][2], int L[], int K, int P[])
{
int i,j;
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 943

n=_n; m=_m;

for(i=0;i<m;i++)
{
addEdge(R[i][0],R[i][1],L[i],i*2+1);
addEdge(R[i][1],R[i][0],L[i],i*2+2);
}

for(i=0;i<n;i++)
{
for(j=0;j<2;j++) d[j][i]=INF+1;
}

for(i=0;i<K;i++)
{
for(j=0;j<2;j++) d[j][P[i]]=0;
Q.push(Inque(P[i],0));
}

for(i=0;i<n;i++)
{
while(!Q.empty() && v[Q.top().x]) Q.pop();
if(Q.empty()) break;
Inque t=Q.top(); Q.pop();
if(t.x==0) break;
v[t.x]=true;
for(j=sta[t.x];j;j=nxt[j])
{
bool l=true;
if(d[0][chi[j]]>t.w+wei[j])
{
d[1][chi[j]]=d[0][chi[j]];
d[0][chi[j]]=t.w+wei[j];
}
else
if(d[1][chi[j]]>t.w+wei[j])
{
d[1][chi[j]]=t.w+wei[j];
}
else
{
l=false;
}

if(l)
{
if(d[1][chi[j]]!=INF+1)
{
Q.push(Inque(chi[j],d[1][chi[j]]));
}
}
}
}
return d[1][0];
}

// -------------- begin grader -----------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&M,&K));
for(i=0; i<M; i++)
my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
for(i=0; i<K; i++)
my_assert(1==scanf("%d",&P[i]));
my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask3/grader.in.7", "r", stdin);


std::freopen("crocodile.out", "w", stdout);
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 944

int ans;
read_input();

auto t2 = clock();

ans = travel_plan(N,M,R,L,K,P);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- begin grader -----------------


/*
t2-t1 = 0.92
t3-t2 = 0.407
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.4.3: crocodile-133704.cpp


// https://oj.uz/submission/133704 729 ms 61348 KB

#include "crocodile.h"

#include<ctime>
#include<iostream>

#define MAX_N 50000


#define MAX_M 10000000

static int N, M;
static int R[MAX_M][2];
static int L[MAX_M];
static int K;
static int P[MAX_N];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> Pa;


const int maxN=100100;
struct edge{int v,d,nxt;}a[20*maxN];
int vis[maxN],head[maxN];
int cnt=0;

priority_queue < Pa, vector<Pa>, greater<Pa> > Q;

void add(int u,int v,int d)


{
a[cnt].v=v;
a[cnt].d=d;
a[cnt].nxt=head[u];
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 945

head[u]=cnt++;
}

int travel_plan(int N, int M, int R[][2], int L[], int K, int P[])
{
for (int i=0;i<N;i++){vis[i]=0;head[i]=-1;}
for (int i=0;i<M;i++)
{
add(R[i][0],R[i][1],L[i]);
add(R[i][1],R[i][0],L[i]);
}

for (int i=0;i<K;i++)


{
Q.push(make_pair(0,P[i]));
vis[P[i]]=1;
}

while(!Q.empty())
{
int dis=Q.top().first,u=Q.top().second;Q.pop();
if (++vis[u]!=2)continue;
if (u==0)return dis;
for (int i=head[u];i!=-1;i=a[i].nxt)
Q.push(make_pair(dis+a[i].d,a[i].v));
}

return -123; // !!!


}

// -------------- begin grader -----------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&M,&K));
for(i=0; i<M; i++)
my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
for(i=0; i<K; i++)
my_assert(1==scanf("%d",&P[i]));
my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask3/grader.in.7", "r", stdin);


std::freopen("crocodile.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = travel_plan(N,M,R,L,K,P);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 946

// -------------- begin grader -----------------


/*
t2-t1 = 3.449
t3-t2 = 4.718
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.4.4: crocodile-228833.cpp


// https://oj.uz/submission/228833 640 ms 54884 KB

#include "crocodile.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 50000


#define MAX_M 10000000

static int N, M;
static int R[MAX_M][2];
static int L[MAX_M];
static int K;
static int P[MAX_N];
static int solution;

inline void my_assert(int e) {if (!e) abort();}

#include<bits/stdc++.h>

using namespace std;

int travel_plan(int N, int M, int R[][2], int L[], int K, int P[])
{
vector<vector<pair<int, int>>> adj(N);
for(int i = 0; i < M; i++)
{
int u = R[i][0], v = R[i][1];
adj[u].emplace_back(v, L[i]);
adj[v].emplace_back(u, L[i]);
}

vector<int> vis(N);
priority_queue<pair<int, int>> Q;
for(int i = 0; i < K; i++)
{
vis[P[i]]++;
Q.emplace(0, P[i]);
}

while(!Q.empty())
{
auto [d, v] = Q.top();
Q.pop();
if(vis[v]++ == 1)
{
if(v == 0) return -d;
for(auto &[u, w] : adj[v])
Q.emplace(d - w, u);
}
}

return -123; // !!!


}

// -------------- begin grader -----------------

void read_input()
{
int i;
CAPITOLUL 9. IOI 2011 9.4. CROCODILE’S UNDERGROUND CITY 947

my_assert(3==scanf("%d %d %d",&N,&M,&K));
for(i=0; i<M; i++)
my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
for(i=0; i<K; i++)
my_assert(1==scanf("%d",&P[i]));
my_assert(1==scanf("%d",&solution));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask3/grader.in.7", "r", stdin);


std::freopen("crocodile.out", "w", stdout);

int ans;
read_input();

auto t2 = clock();

ans = travel_plan(N,M,R,L,K,P);

auto t3 = clock();

if(ans==solution)
printf("Correct.\n");
else
printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// -------------- begin grader -----------------


/*
t2-t1 = 0.906
t3-t2 = 5.007
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.4.5: checkerCrocodile.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask3/grader.in.7",
(char*)"crocodile.out",
(char*)"../tests/subtask3/grader.expect.7",
};

cout<<"argc = "<<argc<<"\n";
for(int kk=0;kk<argc;kk++)
cout<<argv[kk]<<"\n";
cout<<"----------------------\n";
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 948

registerChecker("crocodile", argc, argv);


compareRemainingLines();
}

9.4.3 *Rezolvare detaliată

9.5 Dancing Elephants


Problema 5 - Dancing Elephants 100 de puncte

Author: Mihai Pătraşcu (România)

Dancing Elephants is a spectacular show in Pattaya that features N elephants dancing on a


line, known as the stage.
After years of training, elephants in this show are capable of many amazing dances. The show
consists of a series of acts. In each act, exactly one elephant performs a cute dance while possibly
moving to a different position.
The show producers want to produce a photo book that contains pictures of the entire show.
After each act, they want to take pictures of all elephants as seen by the spectators.
At any time during the show, multiple elephants may share the same position. In that case,
they simply stand behind one another at the same position.
A single camera can take a picture of a group of elephants if and only if all their positions
lie on some segment of length L (including both its endpoints). As the elephants can spread out
across the stage, multiple cameras may be needed in order to take simultaneous snapshots of all
the elephants.
As an example, suppose that L=10 and that the elephants are at positions 10, 15, 17, and 20
on the stage. At this moment, a single camera can take their picture, as shown below. (Elephants
are shown as triangles; cameras are shown as trapezoids.)

In the following act, the elephant at position 15 dances to position 32. After this act, we need
at least two cameras to take the snapshot.

In the next act, the elephant at position 10 moves to position 7. For the new arrangement of
elephants, we need three cameras to photograph all of them.

In this interactive task, you have to determine the minimum number of cameras needed to
take the pictures after each of the acts. Note that the number of cameras needed may increase,
decrease, or stay the same between acts.
Your task
Write the following procedures:
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 949

ˆ Procedure init(N,L,X) that takes the following parameters:


– N - the number of elephants. The elephants are numbered 0 through N  1.
– L - the length of the segment captured by a single camera. You may assume that L is
an integer such that 0 & L & 1 000 000 000.
– X - a one-dimensional array of integers representing the initial positions of the ele-
phants. For 0 & i $ N , elephant i starts at the position X i. The initial positions are
in sorted order. More precisely, you may assume that 0 & X 0 & ... & X N  1 & 1
000 000 000. Note that during the dance the elephants may reorder themselves.
This procedure will be called only once, prior to all calls to update. It does not return any
value.
ˆ Procedure update(i,y) that takes the following parameters:
– i - the number of the elephant that moves in the current act.
– y - the position where the elephant i will stand after the current act. You may assume
that y is an integer such that 0 & y & 1 000 000 000.
This procedure will be called multiple times. Each call corresponds to a single act (which
follows on from all of the previous acts). Each call must return the minimum number of
cameras needed to photograph all elephants after the corresponding act.

Example
10
15
Consider the case where N 4, L 10, and the initial positions of the elephants are X =
17
20
First, your procedure init will be called with these parameters. Afterwards, your procedure
update will be called once for each act. Here is an example sequence of calls and their correct
return values:
act call parameters return value
1 update(2,16) 1
2 update(1,25) 2
3 update(3,35) 2
4 update(0,38) 2
5 update(2,0) 3
Subtasks
Subtask 1 (10 points)
ˆ There are exactly N 2 elephants.
ˆ Initially, and after each act, the positions of all elephants will be distinct.
ˆ Your procedure update will be called at most 100 times.

Subtask 2 (16 points)


ˆ 1 & N & 100.
ˆ Initially, and after each act, the positions of all elephants will be distinct.
ˆ Your procedure update will be called at most 100 times.

Subtask 3 (24 points)


ˆ 1 & N & 50 000.
ˆ Initially, and after each act, the positions of all elephants will be distinct.
ˆ Your procedure update will be called at most 50 000 times.

Subtask 4 (47 points)


ˆ 1 & N & 70 000.
ˆ Elephants may share the same position.
ˆ Your procedure update will be called at most 70 000 times.

Subtask 5 (3 points)
ˆ 1&N & 150 000.
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 950

ˆ Elephants may share the same position.


ˆ Your procedure update will be called at most 150 000 times.
ˆ Please see the note about the CPU time limit under the Implementation Details Section.

Implementation details
Limits
ˆ CPU time limit: 9 seconds
Note: The collection templates in the C++ Standard Library (STL) can be slow; in par-
ticular, it might not be possible to solve subtask 5 if you use them.
ˆ Memory limit: 256 MB
Note: There is no explicit limit for the size of stack memory. Stack memory counts towards
the total memory usage.

Interface (API)
ˆ Implementation folder: elephants/
ˆ To be implemented by contestant: elephants.c or elephants.cpp or elephants.pas
ˆ Contestant interface: elephants.h or elephants.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas
ˆ Sample grader input: grader.in.1, grader.in.2, ...
Note: The sample grader reads the input in the following format:
– Line 1: N , L, and M , where M is the number of acts in the show.
– Lines 2 to N  1: the initial positions; i.e., line k  2 contains X k  for 0 & k $ N .
– Lines N  2 to N  M  1: information on M acts; i.e. line N  1  j contains ij ,
y j , and sj , separated by a space, denoting that in the j-th act elephant ij  moves
to position y j , and after that act, sj  is the mininal number of cameras needed, for
1 & j & M.
ˆ Expected output for sample grader input: grader.expect.1, grader.expect.2, ...
For this task, each one of these files should contain precisely the text ”Correct.”

9.5.1 Indicaţii de rezolvare

Proposed by: Mihai Păatraşcu


1 Subtask 1
In subtask 1, there are only two elephants. Thus we can easily determine the number of
required cameras in constant time. Namely, we only need one camera if the elephants are at most
distance L apart; otherwise, we need two cameras.
2 Subtasks 2 and 3
If elephants are at positions x0 , x1 , ..., xN 1 , such that xi & xi1 for all 0 & i $ N  1, we can
compute the minimum number of cameras required using a greedy algorithm. We start with an
empty set of cameras. While the current set of cameras do not cover all elephants, we choose an
elephant which is not already covered with the minimum position xj , and place a camera to cover
elephants at positions in xj , xj  L. We can implement this procedure to run in time O N  by
iterating through the list of sorted positions once.
Each time an elephant moves in the show, we can update the sorted list of positions in O N 
time. Hence, we have an O N M  solution to this problem, which is sufficient to fully solve subtask
2, and an adequate optimization is required to fully solve subtask 3. However, a faster algorithm
is required for subtasks 4 and 5.
3 Subtasks 4 and 5
3.1 Bucketing elephants
To find the number of cameras, instead of iterating through all elephants, we shall build a data
structure that allows us to ”jump” over lots of elephants.
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 951

We maintain k buckets B0 , B1 , ..., Bk1 of elephants such that buckets with lower indices store
elephants with lower positions, i.e., for any 0 & b $ k  1, for all xi " Bb and xj " Bb1 , xi & xj .
Also, elephants in each bucket are sorted according to their positions.
The goal is to make sure that to find the number of required cameras, one needs to visit each
bucket once. For simplicity, we will always place cameras so that the left-most position covered
by a camera is the position of some elephant.
Consider bucket b with p elephants. Denote the list of indices of elephants in Bb as e0 , e1 , ..., ep1
(that is, xei & xei1 ). Given an elephant ei , we would like to answer the following two questions
quickly:

ˆ Q1: If we would like to cover all elephants starting from ei (i.e., elephants in the set
rei , ei1 , ..., ep1 x), how many cameras are needed?

ˆ Q2: What is the highest position that these set of cameras cover?

For elephant e, denote the answer for Q1 for as J e and the answer to Q2 as T e.
If we have these answers for every elephant in every bucket, we can find the number of cameras
in time O k log N  as follows.
We start by placing the camera at the first elephant in bucket B0 , so that the position of this
elephant is the left-most position covered by this camera.
Now consider placing a camera at elephant e in bucket Bi in the same fashion. We know that
to cover all elephants in Bi , we have to use J e cameras and these cameras cover positions up to
T e. We find the first elephant e0 not covered by these cameras in the next bucket Bi1 by binary
searching for the elephant in Bi1 whose position is minimum but greater than T e. Then, we
start placing the camera at elephant e0 in bucket Bi1 .
We repeat this step until we reach the last bucket. Since each step runs in O log N  time
(from binary search), we spend O k log N  time as required.
We can precompute the answers for Q1 and Q2 for elephants in Bi in O ¶Bi ¶ time by iterating
over each elephant ej from ep1 to e0 and keeping a pointer to the first elephant outside the range
xej  L. For implementation details, please see the appendix.
It is crucial to note that we can process each bucket independent of all other buckets.
3.2 Updating the data structure
When an elephant e moves, we will have to update two buckets: the current bucket Bi and the
new bucket Bj . This can be done in time proportional to the current size of the bucket. To find
the current bucket of e we can store a pointer from e, but it takes O k  to find the new bucket
anyway. Therefore, the running time for the update is O k  ¶Bi ¶  ¶Bj ¶.
Note that the time depends heavily on the size of each bucket. Initially, we would have about
N k elephants in each bucket, but the number may grow as elephants can move. To keep the
size of each bucket bounded above by O N ©k , we will rebuild the whole data structure for every
*N k 0 updates. The rebuilding takes time O N .
3.3 Choosing the right parameter
We need to handle M updates and answer one question after each of these updates. The total
running time is

O M k log N   O M k  N ©k   O M N © N ©k ;

where the first term denotes the total query time, the second term denotes the total updating
time, and the lastÓterm denotes the total rebuilding time.
Ó
Choosing k N gives the running time of O M N log N , which is sufficient to obtain full
marks for this problem. However, an inefficient implementation may not be able to solve subtask
5. For example, using the set data structure in the C++ Standard Template Library can introduce
an extra factor of log n to the running time of rebuilding the data structure. This can be avoided
by using simple arrays.
A Processing each bucket
To simplify the presentation, we add a dummy elephant ep at position xep1  L  1. Also, we
let yj xej be the position of the j-th left-most elephant in bucket Bi .
We consider each elephant j from the right-most elephant ep1 to the left-most one. We also
maintain an index t that points to the left-most elephant et whose position yt % yj  L. Initially,
j p  1 and t p.
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 952

For each elephant ej , we will compute J ej  and last ej , the left-most elephant in the right-
most camera in the set of cameras covering rej , ej 1 , ..., ep x.
For the dummy node, we let J ep  0 and last ep  ep . For elephant ej , we check if we need
to move t, i.e., if the position of et1 is greater than yj  L; if that’s the case we find the smallest
t such that yt % yj  L. We let J ej  J et   1 and last ej  last et .
Finally, for each elephant ej such that last ej  points to the dummy elephant ep , we change
last ej  to ej .
We can complete the process using only one pass over all elephants in the bucket Bi and note
that the pointer t moves over each elephant only once. Thus, the running time is O ¶Bi ¶ as
claimed.
To answer question Q2 for each elephant ej , we report ylast ej   L.

9.5.2 Coduri sursă

Listing 9.5.1: elephants-115857.cpp


// https://oj.uz/submission/115857 3493 ms 13384 KB

#include "elephants.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 1000000


#define MAX_M 1000000

static int N,L,M;


static int X[MAX_N];
static int ii[MAX_M];
static int yy[MAX_M];
static int sol[MAX_M];

inline void my_assert(int e) {if (!e) abort();}

#include <bits/stdc++.h>

using namespace std;

int K, Q;

int Sq=400;
int B[400][1000], sz[400];
int D[400][1000], C[400][1000];
int A[150505], T[150505];

void calc(int t)
{
if (!sz[t]) return;
int k=sz[t]-1;
for (int i=sz[t]-1; i>=0; i--)
{
while (B[t][i] + K < B[t][k]) k--;
if (k == sz[t]-1) D[t][i] = 1, C[t][i] = B[t][i] + K;
else D[t][i] = D[t][k+1] + 1, C[t][i] = C[t][k+1];
}
}

void init(int n, int l, int X[])


{
N=n, K=l;
M = (N+Sq-1)/Sq;
for (int i=0; i<N; i++)
{
B[i/Sq][sz[i/Sq]++] = X[i];
if (!Q) A[i] = X[i];
}
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 953

for (int i=0; i<M; i++) calc(i);


}

void del(int x)
{
int t, k;
for (t=0; t<M; t++)
{
if (!sz[t]) continue;
k = lower_bound(B[t], B[t]+sz[t], x) - B[t];
if (k < sz[t] && B[t][k] == x) break;
}
sz[t]--;
for (; k<sz[t]; k++)
B[t][k] = B[t][k+1];
calc(t);
}

void add(int x)
{
int t;
for (t=0; t<M-1; t++)
if (sz[t] && x <= B[t][sz[t]-1]) break;
B[t][sz[t]++] = x;
for (int i=sz[t]-1; i>0 && B[t][i-1] > B[t][i]; i--)
swap(B[t][i-1], B[t][i]);
calc(t);
}

int update(int x, int y)


{
del(A[x]);
A[x] = y;
add(y);

Q++;
if (Q % Sq == 0)
{
int cnt=0;
for (int i=0; i<M; i++)
{
for (int j=0; j<sz[i]; j++) T[cnt++] = B[i][j];
sz[i] = 0;
}
init(N, K, T);
}

int p=-1, ans=0;


for (int i=0; i<M; i++)
{
if (!sz[i]) continue;
int k = upper_bound(B[i], B[i]+sz[i], p) - B[i];
if (k == sz[i]) continue;
ans += D[i][k], p = C[i][k];
}
return ans;
}

// ------------- begin grader ----------------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&L,&M));

for(i=0; i<N; i++)


my_assert(1==scanf("%d",&X[i]));

for(i=0; i<M; i++)


my_assert(3==scanf("%d %d %d",&ii[i],&yy[i],&sol[i]));
}

int main()
{
auto t1 = clock();
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 954

std::freopen("../tests/subtask5/grader.in.20", "r", stdin);


std::freopen("elephants.out", "w", stdout);

int i, ans;

read_input();

auto t2= clock();

init(N,L,X);

for(i=0; i<M; i++)


{
ans = update(ii[i],yy[i]);
if(ans==sol[i]) continue;

printf("Incorrect. In %d-th move, answered %d (%d expected).\n",


i+1, ans, sol[i]);
return 0;
}

auto t3 = clock();

printf("Correct.\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------------


/*
t2-t1 = 0.25
t3-t2 = 0.11
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.5.2: elephants-192442.cpp


// https://oj.uz/submission/192442 2854 ms 12468 KB

#include "elephants.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 1000000


#define MAX_M 1000000

static int N,L,M;


static int X[MAX_N];
static int ii[MAX_M];
static int yy[MAX_M];
static int sol[MAX_M];

inline void my_assert(int e) {if (!e) abort();}

#include <bits/stdc++.h>

using namespace std;

#define b_size (400)


CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 955

int n, l, m;
int x[150010], v[150010];
int bnum;
int ucnt;

struct Bucket
{
int sz;
int x[2 * b_size + 1];
int num[2 * b_size + 1];
int bound[2 * b_size + 1];
void calc()
{
int t = sz;
for(int i = sz - 1; i >= 0; i--)
{
while(t > 0 && x[t - 1] > x[i] + l) t--;
if(t == sz)
num[i] = 1, bound[i] = x[i] + l;
else
num[i] = num[t] + 1, bound[i] = bound[t];
}
}

void ins(int y)
{
int p = (int)(upper_bound(x, x + sz, y) - x);
sz++;
for(int i = sz - 1; i > p; i--)
x[i] = x[i - 1];
x[p] = y;
calc();
}

void del(int y)
{
int p = (int)(lower_bound(x, x + sz, y) - x);
sz--;
for(int i = p; i < sz; i++)
x[i] = x[i + 1];
calc();
}

} b[b_size + 1];

void init(int n, int l, int x[])


{
::n = n;
::l = l;

for(int i = 0; i < n; i++)


::x[i] = x[i];

for(int i = 0; i < n; i += b_size)


{
for(int j = i; j < n && j < i + b_size; j++)
{
b[bnum].x[b[bnum].sz++] = x[j];
}

b[bnum++].calc();
}
}

void rebucket()
{
int cnt = 0;
for(int i = 0; i < bnum; i++)
{
for(int j = 0; j < b[i].sz; j++)
{
v[cnt++] = b[i].x[j];
}
}

bnum = 0;
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 956

for(int i = 0; i < n; i += b_size)


{
b[bnum].sz = 0;
for(int j = i; j < n && j < i + b_size; j++)
{
b[bnum].x[b[bnum].sz++] = v[j];
}
b[bnum++].calc();
}
}

int update(int i, int y)


{
int pv = x[i];
x[i] = y;
for(int i = 0; i < bnum; i++)
{
if(b[i].sz == 0) continue;
if(b[i].x[0] <= pv && b[i].x[b[i].sz - 1] >= pv){b[i].del(pv); break;}
}

for(int i = 0; i < bnum; i++)


{
if(b[i].sz == 0) continue;
if((i == 0 || b[i].x[0] <= y) && (i == bnum - 1 || b[i + 1].x[0] > y))
{
b[i].ins(y);
break;
}
}

int ret = 0;
int lst = -1;
for(int i = 0; i < bnum; i++)
{
if(lst >= b[i].x[b[i].sz - 1]) continue;
if(lst < b[i].x[0])
{
ret += b[i].num[0];
lst = b[i].bound[0];
}
else
{
int p = (int)(upper_bound(b[i].x, b[i].x + b[i].sz, lst) - b[i].x);
ret += b[i].num[p]; lst = b[i].bound[p];
}
}

ucnt++;
if(ucnt % (b_size - 5) == 0) rebucket();
return ret;
}

// ------------- begin grader ----------------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&L,&M));

for(i=0; i<N; i++)


my_assert(1==scanf("%d",&X[i]));

for(i=0; i<M; i++)


my_assert(3==scanf("%d %d %d",&ii[i],&yy[i],&sol[i]));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask5/grader.in.20", "r", stdin);


std::freopen("elephants.out", "w", stdout);

int i, ans;
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 957

read_input();

auto t2= clock();

init(N,L,X);

for(i=0; i<M; i++)


{
ans = update(ii[i],yy[i]);
if(ans==sol[i]) continue;

printf("Incorrect. In %d-th move, answered %d (%d expected).\n",


i+1, ans, sol[i]);
return 0;
}

auto t3 = clock();

printf("Correct.\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------------


/*
t2-t1 = 0.25
t3-t2 = 22.304
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.5.3: elephants-207733.cpp


// https://oj.uz/submission/207733 3742 ms 7704 KB

#include "elephants.h"
#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>

#define MAX_N 1000000


#define MAX_M 1000000

static int N,L,M;


static int X[MAX_N];
static int ii[MAX_M];
static int yy[MAX_M];
static int sol[MAX_M];

inline void my_assert(int e) {if (!e) abort();}

#include <bits/stdc++.h>

#define lb(a, x) lower_bound(begin(a), end(a), x)

using namespace std;

constexpr int MX=150000, B=448, C=(MX+B-1)/B;

struct iii
{
int x, n, y;
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 958

};

bool operator < (const iii& a, int b) { return a.x<b; }


bool operator < (int a, const iii& b) { return a<b.x; }

int A[MX], Q;

vector<iii> D[C];

void calc_bucket(vector<iii>& v)
{
for(int n=v.size(), i=n, p=n; i--;)
{
while(p && v[i].x<v[p-1].x-M) p--;

if(p==n)
v[i].n=1, v[i].y=v[i].x+M;
else
v[i].n=v[p].n+1, v[i].y=v[p].y;
}
}

void init_buckets()
{
int k=0;
for(auto&v:D)
{
for(auto[x,n,y]:v) X[k++]=x;
v.clear();
}

for(int i=0, j=0; i<N;)


{
D[j].push_back({X[i], 0, 0});
if(++i%B==0 || i==N) calc_bucket(D[j++]);
}
}

void del(int x)
{
for(auto&v:D) if(v.size() && v.front().x<=x && x<=v.back().x)
{
v.erase(lb(v, x));
calc_bucket(v);
break;
}
}

void add(int x)
{
int p=0, n=N-1;
for(auto&v:D)
if(!(n-=v.size()) || v.size() && p<=x && x<=(p=v.back().x))
{
v.insert(lb(v, x), {x, 0, 0});
calc_bucket(v);
break;
}
}

int answer()
{
int p=0, n=0;
for(auto&v:D) if(v.size() && p<=v.back().x)
{
auto[x,m,y]=*lb(v, p);
n+=m, p=y+1;
}

return n;
}

void init(int n, int m, int E[])


{
N=n, M=m;
for(int i=0; i<C; i++)
CAPITOLUL 9. IOI 2011 9.5. DANCING ELEPHANTS 959

D[i].reserve(B);
for(int i=0; i<N; i++)
D[i/B].push_back({A[i]=E[i], 0, 0});
}

int update(int n, int x)


{
if(Q++%B==0) init_buckets();
del(A[n]);
add(A[n]=x);
return answer();
}

// ------------- begin grader ----------------------

void read_input()
{
int i;
my_assert(3==scanf("%d %d %d",&N,&L,&M));

for(i=0; i<N; i++)


my_assert(1==scanf("%d",&X[i]));

for(i=0; i<M; i++)


my_assert(3==scanf("%d %d %d",&ii[i],&yy[i],&sol[i]));
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask5/grader.in.20", "r", stdin);


std::freopen("elephants.out", "w", stdout);

int i, ans;

read_input();

auto t2= clock();

init(N,L,X);

for(i=0; i<M; i++)


{
ans = update(ii[i],yy[i]);
if(ans==sol[i]) continue;

printf("Incorrect. In %d-th move, answered %d (%d expected).\n",


i+1, ans, sol[i]);
return 0;
}

auto t3 = clock();

printf("Correct.\n");

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader ----------------------


/*
t2-t1 = 0.249
t3-t2 = 1.454
t4-t3 = 0

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


Press any key to continue.
CAPITOLUL 9. IOI 2011 9.6. PARROTS 960

*/

Listing 9.5.4: checkerElephants.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/subtask5/grader.in.20",
(char*)"elephants.out",
(char*)"../tests/subtask5/grader.expect.20",
};

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

registerChecker("elephants", argc, argv);


compareRemainingLines();
}

9.5.3 *Rezolvare detaliată

9.6 Parrots
Problema 6 - Parrots 100 de puncte

Author: Jittat Fakcharoenphol

Yanee is a bird enthusiast. Since reading about IP over Avian Carriers (IPoAC), she has spent
much of her time training a flock of intelligent parrots to carry messages over long distances.
Yanee’s dream is to use her birds to send a message M to a land far far away. Her message M
is a sequence of N (not necessarily distinct) integers, each between 0 and 255, inclusive. Yanee
keeps K specially-trained parrots. All the parrots look the same; Yanee cannot tell them apart.
Each bird can remember a single integer between 0 and R, inclusive.
Early on, she tried a simple scheme: to send a message, Yanee carefully let the birds out of the
cage one by one. Before each bird soared into the air, she taught it a number from the message
sequence in order. Unfortunately, this scheme did not work. Eventually, all the birds did arrive
at the destination, but they did not necessarily arrive in the order in which they left. With this
scheme, Yanee could recover all the numbers she sent, but she was unable to put them into the
right order.
To realize her dream, Yanee will need a better scheme, and for that she needs your help.
Given a message M , she plans to let the birds out one by one like before. She needs you to write
a program that will perform two separate operations:
ˆ First, your program should be able to read a message M and transform it into a sequence
of at most K integers between 0 and R that she will teach the birds.
ˆ Second, your program should be able to read the list of integers between 0 and R received
as the birds reach their destination, and then transform it back to the original message M .

You may assume that all parrots always arrive at the destination, and that each of them
remembers the number it was assigned. Yanee reminds you once again that the parrots may
arrive in any order. Note that Yanee only has K parrots, so the sequence of integers between 0
and R thatb you produce must contain at most K integers.
CAPITOLUL 9. IOI 2011 9.6. PARROTS 961

Your task
Write two separate procedures. One of them will be used by the sender (encoder) and the
other by the receiver (decoder).
The overall process is shown in the following figure.

The two procedures you are to write are:


ˆ Procedure encode(N,M) that takes the following parameters:
– N - the length of the message.
– M - a one-dimensional array of N integers representing the message. You may assume
that 0 & M i & 255 for 0 & i $ N .
This procedure must encode the message M into a sequence of integers between 0 and R,
inclusive, that shall be sent using the parrots. To report this sequence, your procedure
encode must call the procedure send(a) for each integer a that you wish to give to one
of the birds.
ˆ Procedure decode(N,L,X) that takes the following parameters:
– N - the length of the original message.
– L - the length of the message received (the number of birds that were sent).
– X - a one-dimensional array of L integers representing the received numbers. The
numbers X i for 0 & i $ L are precisely the numbers that your procedure encode
produced, but possibly rearranged into a different order.
This procedure must recover the original message. To report it, your procedure decode
must call the procedure output(b) for each integer b in the decoded message, in the correct
order.

Note that R and K are not given as input parameters - please see the subtask descriptions
below.
In order to correctly solve a given subtask, your procedures must satisfy the following condi-
tions:
ˆ All integers sent by your procedure encode must be in the range specified in the subtask.
ˆ The number of times your procedure encode calls the procedure send must not exceed the
limit K specified in the subtask. Please note that K depends on the length of the message.
ˆ Procedure decode\verb must correctly recover the original message M and call the proce-
dure output(b) exactly N times, with b equal to M 0, M 1, ..., M N  1, respectively.

In the last subtask, your score varies according to the ratio between the lengths of the encoded
message and the original message.
Example
10
Consider the case where N 3, and M = 30
20
Procedure encode(N,M), using some strange method, may encode the message as the se-
quence of numbers (7, 3, 2, 70, 15, 20, 3). To report this sequence, it should call the procedure
send as follows:
send(7)
send(3)
send(2)
send(70)
send(15)
send(20)
send(3)
CAPITOLUL 9. IOI 2011 9.6. PARROTS 962

Once all parrots reach their destination, assume we obtain the following list of transcribed
numbers: (3, 20, 70, 15, 2, 3, 7). The procedure decode will then be called with N 3, L 7,
and
3
20
70
X = 15
2
3
7
The procedure decode must produce the original message (10, 30, 20). It reports the result
by calling the procedure output as follows.
output(10)
output(30)
output(20)
Subtasks
Subtask 1 (17 points)
ˆ N 8, and each integer in the array M is either 0 or 1.
ˆ Each encoded integer must be in the range from 0 to R 65535, inclusive.
ˆ The number of times you can call the procedure send is at most K 10  N .

Subtask 2 (17 points)


ˆ 1 & N & 16.
ˆ Each encoded integer must be in the range from 0 to R 65535, inclusive.
ˆ The number of times you can call the procedure send is at most K 10  N .

Subtask 3 (18 points)


ˆ 1 & N & 16.
ˆ Each encoded integer must be in the range from 0 to R 255, inclusive.
ˆ The number of times you can call the procedure send is at most K 10  N .

Subtask 4 (29 points)


ˆ 1 & N & 32.
ˆ Each encoded integer must be in the range from 0 to R 255, inclusive.
ˆ The number of times you can call the procedure send is at most K 10  N .

Subtask 5 (up to 19 points)


ˆ 16 & N & 64.
ˆ Each encoded integer must be in the range from 0 to R 255, inclusive.
ˆ The number of times you can call the procedure send is at most K 15  N .
ˆ Important: the score for this subtask depends on the ratio between the length of the
encoded message and that of the original message.
For a given test case t in this subtask, let Pt Lt ©Nt be the ratio between the length Lt of
the encoded message and the length Nt of the original message. Let P be the maximum of
all Pt . Your score for this subtask will be determined using the following rules:
– If P & 5, you get the full score of 19 points.
– If 5 $ P & 6, you get 18 points.
– If 6 $ P & 7, you get 17 points.
– If 7 $ P & 15, your score is 1  2  15  P , rounded down to the nearest integer.
– If P ¿ 15 or any of your outputs is incorrect, your score is 0.
ˆ Important: Any valid solution for subtasks 1 to 4 will also solve all preceding subtasks.
However, due to the larger bound on K, a valid solution to subtask 5 might not be able to
solve subtasks 1 to 4. It is possible to solve all subtasks using the same solution.

Implementation details
Limits
CAPITOLUL 9. IOI 2011 9.6. PARROTS 963

ˆ Grading Environment: In the real grading environment, your submissions will be compiled
into two programs e and d to be executed separately. Both your encoder and decoder
modules will be linked to each executable, but e only calls encode and d only calls decode.
ˆ CPU time limit: Program e will make 50 calls to procedure encode and it should run in 2
seconds. Program d will make 50 calls to procedure decode and it should run in 2 seconds.
ˆ Memory limit: 256 MB
Note: There is no explicit limit for the size of stack memory. Stack memory counts towards
the total memory usage.

Interface (API)
ˆ Implementation folder: parrots/
ˆ To be implemented by contestant:
– encoder.c or encoder.cpp or encoder.pas
– decoder.c or decoder.cpp or decoder.pas
Note for C/C++ programmers: both in the sample grader and in the real grader, en-
coder.c[pp] and decoder.c[pp] are linked together with the grader. Therefore, you should
declare all global variables inside each file as static to prevent them from interfering with
variables from other files.
ˆ Contestant interface:
– encoder.h or encoder.pas
– decoder.h or decoder.pas
ˆ Grader interface:
– encoderlib.h or encoderlib.pas
– decoderlib.h or decoderlib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas
The sample grader executes two separate rounds. In each round, it first calls encode with the
given data, and then it calls decode with the output your procedure encode produced. In the
first round the grader does not change the order of the integers in the encoded message. In
the second round the sample grader swaps the integers on odd and even positions. The real
grader will apply various kinds of permutations to the encoded messages. You can change
how the sample grader shuffles the data by modifying its procedure shuffle (in C/C++) or
Shuffle (in Pascal).
The sample grader also checks for both range and length of the encoded data. By default,
it checks that the encoded data is in the range between 0 and 65535, inclusive, and that the
length is at most 10  N . You can change this by adjusting the constants CHANNEL_RANGE
(from 65535 to 255, for example) and max_expansion (from 10 to 15 or 7, for example).
ˆ Sample grader input: grader.in.1, grader.in.2, ...
Note: The sample grader reads the input in the following format:
– Line 1: N
– Line 2: a list of N numbers: M 0, M 1, ..., M N  1
ˆ Expected output for sample grader input: grader.expect.1, grader.expect.2, ...
For this task, each one of these files should contain precisely the text ”Correct.”

9.6.1 Indicaţii de rezolvare

Proposed by: Jittat Fakcharoenphol

For this task, contestants are given a message M of length N , which consists of a sequence
of integers between 0 and 255, inclusive, and they are asked to find an encoding and decoding
scheme such that the encoded message X are invariant up to permutations. The encoded message
should be as concise as possible. The scores will be rewarded based on the ratio of the length of
the encoded message X to the length of the original message M .
CAPITOLUL 9. IOI 2011 9.6. PARROTS 964

There are many ways to solve this task. It will be instructive to look at the first few subtasks.
1 Subtask 1
For this subtask, recall that the original message M is just a sequence of two possible numbers,
0 or 1. Assuming that the original message is indexed from 0 to N  1, one can instead choose
the send the positions in the original message where all number 1’s are located. This technique
uses at most N integers to send the message.
2 Subtask 2
For subtask 2, since the numbers in the original message M could be from 0 to 255, they can
be encoded using only 8 bits. However, this subtask allows one to use up to 16 bit per number in
the encoded message E. Hence, you can encode the position of each number using the higher 8
bits (similar to the solution to subtask 1), and use the lower 8 bits to encode the actual number
in the original message M , i.e.
Ei 256 i  Mi
where Ei denotes the i-th number in the original message.
Note that the encoded numbers are also ordered, i.e. Ei $ Ei1 , for 0 & i $ N  1. Therefore,
when we would like to decode the message from the shuffled array X, we can sort it to get E.
Then, to obtain the original message from the sorted E should be pretty obvious.
This method also uses at most N parrots, which is satisfactory for this subtask.
3 Subtask 3
Thinking about the first two subtasks should get us familiar with the approach. There are
many roads to take from here. However, to formalize the settings, we shall think of a general
representation first.
Because we shall keep integers in the encoded array, a natural representation would be to use
a sorted sequence. Note that this representation is very robust against permutation; provided that
the encoded array E is sorted, by sorting a shuffled array X, we can easily recover the encoded
array E, as in subtask 2.
We shall try to extend the approach in subtask 2. However, for subtasks 3-5, we do not have
”extra” bits for keeping the position data. In these cases, we look at the original message as a
sequence of binary digits (or bits), 0 and 1, by representing each number using only 8 bits.
Thus, the original message of length N will have 8N bits.
This leads to a way to solve subtask 3. Note that the origianl message M contains at most
16  8 128 bits. For now, let Bi be the i-th bit of the original message, indexing from 0 to
8N  1. In this case, it is sufficient to take 7 bits to represent all positions of every single bit
Bi , and use one last bit to represent the actual data. Hence, the encoding scheme looks like the
following
Ei 2 i  Bi
therefore, it takes 7 bits to represent posible positions and another bit to keep the data. That is,
for each bit bi , we shall encode it as 2 i  bi . The length of the encoded message for each test
case of this subtask is 8N bytes.
4 Subtask 4
For this subtask, there are 32  8 256 bit positions; and it requires 8 bits just to represent the
positions. Hence, there is no way to include the information of each bit of the original message in
the encoding. If we recall the technique we use to solve subtask 1, we can improve the technique
we used in subtask 3 by choosing to encode the positions of all 1 bits. This approach again sends
at most 8N bytes.
5 Subtask 5
For this subtask, contestants are allowed to obtain some partial scores, depending on the ratio
of the encoding message to the original message. In our point of view, to obtain nearly perfect
CAPITOLUL 9. IOI 2011 9.6. PARROTS 965

scores like 98 is relatively much easier than to obtain the full points. We consider the last 2 points
to be the reward to those who choose to implement more sophisticated method of encoding that
achieve better results than our expectations.
We discuss many potential solutions that might achieve some partial scores.
5.1 A ratio=12 approach
We are going to generalize the solution to the previous subtask case since the number of bits
is more than 256. In this case, there are at most 512 bit positions.
To deal with this issue, we partition the message into 256 pairs of bits. We define the i-th bit
pair Pi to be M2i , M2i1 . Again, the bit pair P should be indexed from 0 to 4N  1.
Note that there are 4 possible values for each bit pair, which can be represented with integers
from 0 to 3. One possible method is to send the positions of the bit pair (which uses 8 bit to
encode) in the encoding data, and then the actual bit pair data will be encoded as the number of
occurrences of such positions.
Using this approach, for each pair of bits, we may have to send at most 3 bytes. Therefore,
we send at most 32 8N 12N bytes. Contestants will achieve 7 points on this subtask for
implementing this method.
5.2 A ratio=6.25 approach
With a simple observation, we can reduce the size of the encoded messages by roughly a factor
of two. Note that the best case (in terms of encoding length) for the previous encoding method is
when the original message only contains zero bits, and the worst case is when the message contains
only one bits. We shall try to get the average of these two cases.
Consider two encoding scheme, called ONE and ZERO. For a pair of bits b2i , b2i1 , the one
scheme sends 2 b2i  b2i1 copies of i, but the zero scheme sends 3 2 b2i  b2i1  copies. Note that
both schemes work, provided that the decoder knows which of the schemes the encoder actually
uses.
For each pair of bits, the sum of the number of encoded bytes used by both scheme is always
2 b2i  b2i1  3  2 b2i  b2i1  3 bytes. Considering all pairs, the sum is 3 8N 2
12N .
Now, if we choose the scheme with the lower number of encoding data, we will need at most
half of this number, i.e., at most 6N bytes.
There are many ways to signal the decoder which encoding scheme the encoder uses without
too much penalty on the ratio. For example, we can add 4 copies of 255 iff the ZERO scheme is
used. This will make the ratio goes up by N4 . However, since N ' 16, the worst ratio is at most
6.25. Contestants will achieve 17 points on this subtask for implementing this method.
5.3 Better approaches
There are many other approaches that give better compression ratio.
5.3.1 One byte to 4 numbers
Consider the number of encoded messages of length no greater than 7, only consisting of
numbers 0, 1, 2, and 3 (0, 0, 1, 1, 1, 1, 3 is one such message). To analyze this, it might be
easier to consider an equivalent scheme: encoded messages of length exactly 7, only consisting of
numbers 0, 1, 2, 3, and BLANK. There are 44 711 4
 330 of them, which is enough to encode
one byte of the original message.
Since the original message is at most 64 bytes long, it is possible to allocate 4 numbers per byte
(0, 1, 2, and 3 for the first byte, for example), and therefore numbers from 0 to 255 are suffcient.
This scheme yields a compression ratio of at most 7.
5.3.2 Optimal ratio
Theoretically, one can encode 64 bytes of data to the minimum of 261 bytes of encoded message
261 517  1.47  10154 different encoded messages
without loss of information. There are 256256 256
using no more than 261 bytes. This is just more than 256  1.34  10 , the number of different
64 154

64-byte original messages. The compression ratio achieved is about 4.08. For smaller data, the
ratio is even smaller.
This method yields a perfect score for this task. However, implementation of this scheme is
comparatively difficult.
CAPITOLUL 9. IOI 2011 9.6. PARROTS 966

9.6.2 Coduri sursă

Listing 9.6.1: parrot-224033.cpp


//https://oj.uz/submission/224033 173 ms 27832 KB

#include "encoder.h"
#include "decoder.h"
#include "encoderlib.h"
#include "decoderlib.h"

#include <bits/stdc++.h>

using namespace std;

#define MAX_N 1000


#define MAX_M 10000

static int message[MAX_N];


static int N, NN;
static int encoded_message[MAX_M];
static int M;
static int output_message[MAX_N];
static int O;
static int max_expansion, channel_range;

// --------------- encode --------------------

const int MAXP = 330;


const int MAXL = 260;
const int MAXB = 520;
const int MAXV = 600;

class BigNum
{
public:

BigNum() : base( 1000000000 ) {}

int size() { return num.size(); }


int digit(int i) { return num[i]; }
void insert(int k) { num.push_back( k ); }
void change(int i, int k) { num[i] -= k; }

BigNum operator + (BigNum& aux)


{
BigNum ans;

BigNum A = aux;
BigNum B = *this;

if( A.size() < B.size() ) swap( A , B );

while( B.size() != A.size() )


B.insert( 0 );

int acc = 0;

for(int i = 0 ; i < B.size() ; i++)


{
ans.insert( A.digit(i) + B.digit(i) + acc );
acc = 0;

int curDigit = ans.digit(i);

if( curDigit >= base )


{
acc = curDigit/base;
ans.change( i , acc*base );
}
}

if( acc > 0 ) ans.insert( acc );


CAPITOLUL 9. IOI 2011 9.6. PARROTS 967

return ans;
}

bool operator <= (BigNum aux)


{
if( this->size() != aux.size() ) return this->size() < aux.size();

for(int i = aux.size() - 1 ; i >= 0 ; i--)


if( this->digit(i) != aux.digit(i) ) return this->digit(i) < aux.digit(i
);

return true;
}

bool operator < (BigNum aux)


{
if( this->size() != aux.size() ) return this->size() < aux.size();

for(int i = aux.size() - 1 ; i >= 0 ; i--)


if( this->digit(i) != aux.digit(i) ) return this->digit(i) < aux.digit(i
);

return false;
}

private:

int base;

vector< int > num;


};

bool hasInit = false;

BigNum pot[MAXB];
BigNum dp[MAXV][MAXL];

void buildPowers()
{
pot[0].insert( 1 );

for(int i = 1 ; i < MAXB ; i++)


pot[i] = pot[i - 1] + pot[i - 1];
}

void buildBinomials()
{
for(int i = 0 ; i < MAXV ; i++)
dp[i][0].insert( 1 );

for(int i = 1 ; i < MAXV ; i++)


for(int j = 1 ; j <= i && j < MAXL ; j++)
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
}

void encode(int N, int M[])


{
if( !hasInit )
{
buildPowers();
buildBinomials();

hasInit = true;
}

BigNum binary;

for(int i = 0 ; i < N ; i++)


for(int j = 0 ; j < 8 ; j++)
if( M[i] & (1 << j) ) binary = binary + pot[ 8*i + j ];

int remainParrots = 5*N;

BigNum sum;

for(int i = -1 ; i < 255 ; i++)


CAPITOLUL 9. IOI 2011 9.6. PARROTS 968

{
int qtdParrots;
int qtdNumbers = 256 - i - 1;

for(qtdParrots = 0 ; qtdParrots <= remainParrots ; qtdParrots++)


{
BigNum aux = sum;
aux = aux+dp[remainParrots-qtdParrots+qtdNumbers-1][qtdNumbers-1];

if( binary < aux ) break;

sum = aux;
}

remainParrots -= qtdParrots;

if( i == -1 ) continue;

for(int j = 1 ; j <= qtdParrots ; j++)


send( i );
}

while( remainParrots > 0 )


{
send( 255 );
remainParrots--;
}
}

// --------------- decode --------------------

int freq[MAXL];

void decode(int N, int L, int X[])


{
if( !hasInit )
{
buildPowers();
buildBinomials();

hasInit = true;
}

BigNum sum;

int qtd = 5*N - L;

for(int i = 0 ; i < qtd ; i++)


sum = sum + dp[ 5*N - i + 256 ][ 256 ];

for(int i = 0 ; i < L ; i++)


freq[ X[i] ]++;

int remainParrots = L;

for(int i = 0 ; i < 255 ; i++)


{
int qtdNumbers = 256 - i - 1;

while( freq[i] > 0 )


{
sum = sum + dp[ remainParrots + qtdNumbers - 1 ][ qtdNumbers-1];

freq[i]--;
remainParrots--;
}
}

BigNum cur;

vector< int > binary;

for(int i = 8*N - 1 ; i >= 0 ; i--)


{
BigNum aux = cur + pot[i];
CAPITOLUL 9. IOI 2011 9.6. PARROTS 969

if( aux <= sum )


{
cur = aux;
binary.push_back( 1 );
}
else binary.push_back( 0 );
}

reverse( binary.begin() , binary.end() );

for(int i = 0 ; i < N ; i++)


{
int v = 0;

for(int j = 0 ; j < 8 ; j++)


if( binary[8*i + j] == 1 ) v += (1 << j);

output( v );
}
}

// ----------------- begin grader --------------------

void send(int x)
{
if(M == MAX_M) {
printf("Encoded message too long\n");
exit(0);
}
encoded_message[M] = x;
M++;
}

int read_data()
{
if(NN == M) {
printf("Read too many encoded message\n");
exit(0);
}
NN++;
return encoded_message[NN-1];
}

void output(int y)
{
if(O == N)
O++;
if(O > N)
return;
output_message[O] = y;
O++;
}

static void sort_message(int d)


{
int i, j, b, bi, t;
for(i=0; i<M-1; i++)
{
bi = i;
b = encoded_message[i];
for(j=i+1; j<M; j++)
if(((d==0) && (encoded_message[j] < b)) ||
((d==1) && (encoded_message[j] > b)))
{
b = encoded_message[j];
bi = j;
}
t = encoded_message[i];
encoded_message[i] = encoded_message[bi];
encoded_message[bi] = t;
}
}

static void random_shuffle()


{
int i, t, p;
CAPITOLUL 9. IOI 2011 9.6. PARROTS 970

for(i=0; i<M-1; i++)


{
p = rand()%(M-i);
t = encoded_message[i];
encoded_message[i] = encoded_message[i+p];
encoded_message[i+p] = t;
}
}

static void shuffle(int method)


{
if(method==0)
sort_message(0);
else if(method==1)
sort_message(1);
else
random_shuffle();
}

static void check_encoded_message()


{
int i;
if(M > max_expansion * N)
{
printf("Encoded message too long.");
exit(0);
}
for(i=0; i < M; i++)
if((encoded_message[i] < 0) ||
(encoded_message[i] > channel_range))
{
printf("Bad encoded integer\n");
exit(0);
}
}

static int check_output()


{
int i;

if(O!=N)
return 0;
for(i = 0; i < N; i++)
if(message[i] != output_message[i])
return 0;
return 1;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask5/grader.in.7", "r", stdin);


//std::freopen("parrots.out", "w", stdout);

int i,tt,t,p,r;
int correct;

scanf("%d",&tt);
scanf("%d %d",&max_expansion,&channel_range);

scanf("%d",&r);
srand(r);

auto t2 = clock();

for(t=0; t<tt; t++)


{
scanf("%d",&N);
for(i = 0; i < N; i++)
scanf("%d",&message[i]);

M = 0;
encode(N,message);

check_encoded_message();
CAPITOLUL 9. IOI 2011 9.6. PARROTS 971

scanf("%d",&p);
shuffle(p);

NN = 0;
O = 0;
decode(N,M,encoded_message);

if(!check_output())
{
printf("Incorrect\n");
exit(0);
}
}

auto t3 = clock();

printf("Correct.\n");

fprintf(stderr,"Ratio: %f\n",(float)M/N);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------------- end grader --------------------


/*
Correct.
Ratio: 5.000000
t2-t1 = 0
t3-t2 = 1.324
t4-t3 = 0

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


Press any key to continue.
*/

Listing 9.6.2: parrots-235627.cpp


// https://oj.uz/submission/235627 ... 507 ms 66800 KB

#include "encoder.h"
#include "decoder.h"
#include "encoderlib.h"
#include "decoderlib.h"

#include <stdio.h>
#include <stdlib.h>

#include<ctime>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

#define base 5000000

#define MAX_N 1000


#define MAX_M 10000

static int message[MAX_N];


static int N, NN;
static int encoded_message[MAX_M];
static int M;
static int output_message[MAX_N];
static int O;
CAPITOLUL 9. IOI 2011 9.6. PARROTS 972

static int max_expansion, channel_range;

// --------------- encode --------------------

static int d[350][260][100], pt[70][100], num[100];

static void adun(int a[], int b[], int c[])


{
int i, t = 0;
c[0] = max(a[0], b[0]);
for(i = 1; i <= c[0]; i++)
{
c[i] = t + a[i] + b[i];
t = c[i] / base;
c[i] %= base;
}
if(t != 0)
{
c[ ++c[0] ] = t;
}
}

static void mult(int a[], int x, int b[])


{
int i, t = 0;
b[0] = a[0];
for(i = 1; i <= a[0]; i++)
{
b[i] = a[i] * x + t;
t = b[i] / base;
b[i] %= base;
}
while(t != 0)
{
b[ ++b[0] ] = t % base;
t /= base;
}
}

static void scad(int a[], int b[])


{
int i, t = 0;
for(i = 1; i <= a[0]; i++)
{
if(a[i] < b[i] + t)
{
a[i] = a[i] + base - b[i] - t;
t = 1;
}
else
{
a[i] -= b[i] + t;
t = 0;
}
}

while(a[0] > 1 && a[ a[0] ] == 0)


{
a[0]--;
}
}

static int comp(int a[], int b[])


{
if(a[0] != b[0])
{
if(a[0] < b[0])
{
return 1;
}
return 0;
}

for(int i = a[0]; i >= 1; i--)


{
if(a[i] != b[i])
CAPITOLUL 9. IOI 2011 9.6. PARROTS 973

{
if(a[i] < b[i])
{
return 1;
}
return 0;
}
}
return 0;
}

static void calcpt(int n)


{
pt[0][0] = 1; pt[0][1] = 1;
for(int i = 1; i <= n; i++)
{
mult(pt[i - 1], 256, pt[i]);
}
}

static void calcdp(int n)


{
int i, j;
memset(num, 0, sizeof(num) );
for(j = 0; j < 256; j++)
{
d[n - 1][j][0] = d[n - 1][j][1] = 1;
}

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


{
for(j = 255; j >= 0; j--)
{
adun(d[i + 1][j], d[i][j + 1], d[i][j]);
}
}
}

void encode(int n, int v[])


{
int i, j, x, k = 5 * n - 1;
calcdp(k);
calcpt(n);
for(i = 0; i < n; i++)
{
for(j = 0; j < v[i]; j++)
{
adun(num, pt[n - i - 1], num);
}
}
adun(num, pt[0], num);
x = 0;
for(i = 0; i < k; i++)
{
while( comp(d[i][x], num) )
{
scad(num, d[i][x]);
x++;
}
send(x);
}
}

// --------------- decode --------------------

void decode(int n, int k, int v[])


{
int i, x, j;
memset(num, 0, sizeof(num) );
calcpt(n);
calcdp(k);
sort(v, v + k);
for(j = 0; j < v[i]; j++)
{
adun(num, d[0][j], num);
}
CAPITOLUL 9. IOI 2011 9.6. PARROTS 974

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


{
for(j = v[i - 1]; j < v[i]; j++)
{
adun(num, d[i][j], num);
}
}
adun(num, pt[0], num);
for(i = 0; i < n; i++)
{
for(j = 0; j < 256; j++)
{
if(comp(pt[n - i - 1], num) == 0)
{
output(j);
break;
}
else
{
scad(num, pt[n - i - 1]);
}
}
}
}

// ----------------- begin grader --------------------

void send(int x)
{
if(M == MAX_M)
{
printf("Encoded message too long\n");
exit(0);
}
encoded_message[M] = x;
M++;
}

int read_data()
{
if(NN == M)
{
printf("Read too many encoded message\n");
exit(0);
}
NN++;
return encoded_message[NN-1];
}

void output(int y)
{
if(O == N)
O++;
if(O > N)
return;
output_message[O] = y;
O++;
}

static void sort_message(int d)


{
int i, j, b, bi, t;
for(i=0; i<M-1; i++)
{
bi = i;
b = encoded_message[i];
for(j=i+1; j<M; j++)
if(((d==0) && (encoded_message[j] < b)) ||
((d==1) && (encoded_message[j] > b)))
{
b = encoded_message[j];
bi = j;
}
t = encoded_message[i];
encoded_message[i] = encoded_message[bi];
encoded_message[bi] = t;
CAPITOLUL 9. IOI 2011 9.6. PARROTS 975

}
}

static void random_shuffle()


{
int i, t, p;
for(i=0; i<M-1; i++)
{
p = rand()%(M-i);
t = encoded_message[i];
encoded_message[i] = encoded_message[i+p];
encoded_message[i+p] = t;
}
}

static void shuffle(int method)


{
if(method==0)
sort_message(0);
else if(method==1)
sort_message(1);
else
random_shuffle();
}

static void check_encoded_message()


{
int i;
if(M > max_expansion * N)
{
printf("Encoded message too long.");
exit(0);
}

for(i=0; i < M; i++)


if((encoded_message[i] < 0) ||
(encoded_message[i] > channel_range))
{
printf("Bad encoded integer\n");
exit(0);
}
}

static int check_output()


{
int i;

if(O!=N)
return 0;
for(i = 0; i < N; i++)
if(message[i] != output_message[i])
return 0;
return 1;
}

int main()
{
auto t1 = clock();

std::freopen("../tests/subtask5/grader.in.7", "r", stdin);


//std::freopen("parrots.out", "w", stdout);

int i,tt,t,p,r;
int correct;

scanf("%d",&tt);
scanf("%d %d",&max_expansion,&channel_range);

scanf("%d",&r);
srand(r);

auto t2 = clock();

for(t=0; t<tt; t++)


{
scanf("%d",&N);
CAPITOLUL 9. IOI 2011 9.6. PARROTS 976

for(i = 0; i < N; i++)


scanf("%d",&message[i]);

M = 0;
encode(N,message);

check_encoded_message();

scanf("%d",&p);
shuffle(p);

NN = 0;
O = 0;
decode(N,M,encoded_message);

if(!check_output())
{
printf("Incorrect\n");
exit(0);
}
}

auto t3 = clock();

printf("Correct.\n");

fprintf(stderr,"Ratio: %f\n",(float)M/N);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ----------------- end grader --------------------


/*
Correct.
Ratio: 4.984375
t2-t1 = 0
t3-t2 = 2.963
t4-t3 = 0

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


Press any key to continue.
*/

9.6.3 *Rezolvare detaliată


Capitolul 10
41
IOI 2010

10.1 Cluedo
Problema 1 - Cluedo 100 de puncte

Author: Gordon Cormack (CAN)

Dr. Black has been murdered. Detective Jill must determine the murderer, the location, and
the weapon. There are six possible murderers, numbered 1 to 6. There are ten possible locations,
numbered 1 to 10. There are six possible weapons, numbered 1 to 6.
For illustration only, we show the names of the possible murderers, locations and weapons. The
names are not required to solve the task.
Murderer Location Weapon
1. Ballroom
2. Kitchen
1. Professor Plum 3. Conservatory 1. Lead pipe
2. Miss Scarlet 4. Dining Room 2. Dagger
3. Colonel Mustard 5. Billiard Room 3. Candlestick
4. Mrs. White 6. Library 4. Revolver
5. Reverend Green 7. Lounge 5. Rope
6. Mrs. Peacock 8. Hall 6. Spanner
9. Study
10. Cellar
Jill repeatedly tries to guess the correct combination of murderer, location and weapon. Each
guess is called a theory. She asks her assistant Jack to confirm or to refute each theory in turn.
When Jack confirms a theory, Jill is done. When Jack refutes a theory, he reports to Jill that one
of the murderer, location or weapon is wrong.
You are to implement the procedure Solve that plays Jill’s role. The grader will call
Solve many times, each time with a new case to be solved. Solve must repeatedly call
Theory(M,L,W), which is implemented by the grader. M , L and W are numbers denoting
a particular combination of murderer, location and weapon. Theory(M,L,W) returns 0 if the
theory is correct. If the theory is wrong, a value of 1, 2 or 3 is returned. 1 indicates that the
murderer is wrong; 2 indicates that the location is wrong; 3 indicates that the weapon is wrong.
If more than one is wrong, Jack picks one arbitrarily between the wrong ones (not necessarily in
a deterministic way). When Theory(M,L,W) returns 0, Solve should return.
Example
As example, assume that Miss Scarlet committed the murder (Murderer 2) in the conservatory
(Location 3) using a revolver (Weapon 4). When procedure Solve makes the following calls to
function Theory, the results in the second column could be returned.
41
argint: Bogdan-Cristian Tătăroiu, ICHB (Bucureşti)
. bronz: Vlad Alexandru Gavrilă, ICHB (Bucureşti)
. bronz: Andrei-Bogdan Pârvu, Tudor Vianu (Bucureşti),
. bronz: Victor Ionescu, ICHB (Bucureşti).

977
CAPITOLUL 10. IOI 2010 10.1. CLUEDO 978

Call Returned value Explanation


Theory(1, 1, 1) 1, or 2, or 3 All three are wrong
Theory(3, 3, 3) 1, or 3 Only the location is correct
Theory(5, 3, 4) 1 Only the murderer is wrong
Theory(2, 3, 4) 0 All are correct
Subtask 1 [50 points]
Each test run may call Solve up to 100 times. Each call might correspond to a different
combination of murderer, location and weapon as the answer. Each time Solve is called, it must
find the correct theory with no more than 360 calls to Theory(M,L,W). Be sure to initialize any
variables used by Solve every time it is called.
Subtask 2 [50 points]
Each test run may call Solve up to 100 times. Each time Solve is called, it must find the correct
theory with no more than 20 calls to Theory(M,L,W). Be sure to initialize any variables used
by Solve every time it is called.
Implementation Details
ˆ Implementation folder: /home/ioi2010-contestant/cluedo/
ˆ To be implemented by contestant: cluedo.c or cluedo.cpp or cluedo.pas
ˆ Contestant interface: cluedo.h or cluedo.pas
ˆ Grader interface: grader.h or graderlib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas and graderlib.pas
ˆ Sample grader input: grader.in.1.
Note: Each line of input contains three numbers denoting the murderer, the location and the
weapon.
ˆ Expected output for sample grader input: if Solve correctly solves all cases, the output file
will contain OK t where t is the maximum number of calls to Theory used for any case.
ˆ Compile and run (command line): runc grader.c or runc grader.cpp or
runc grader.pas
ˆ Compile and run (gedit plugin): Control-R, while editing any implementation file.
ˆ Submit (command line): submit grader.c or submit grader.cpp or
submit grader.pas
ˆ Submit (gedit plugin): Control-J, while editing any implementation or grader file.

10.1.1 Indicaţii de rezolvare

Task Author: Gordon Cormack (CAN)

This was intended to be a very easy task. The number of features to be determined (murderer,
location, weapon), and the number of options for each feature were intentionally fixed, and not
parameterized.
Given that there are 6 candidate murderers, 10 candidate locations, and 6 candidate weapons,
there is a total of 6*10*6=360 theories.
Subtask 1 could be solved by trying each possible theory (three nested for loops).
Because the response to a refuted theory will identify one feature for which a wrong option was
guessed, the search can be expedited. All theories having that wrong option for that particular
feature are now ruled out.
Subtask 2 can be solved by a single loop, incrementing whichever feature was wrong (a mono-
tonic search). The total number of options equals 6+10+6=22, and the last option not ruled out
must be correct (it was given that there is exactly one correct theory). Therefore, at most 22-3=19
refuted calls to Theory are needed. One confirming call to Theory was required, so a total of 20
calls suffices.
Here is a Pascal solution that can readily be generalized (the constant, type, and auxiliary
function definitions could be eliminated, but they document the relevant concepts nicely):

Listing 10.1.1: cluedosol.pas


CAPITOLUL 10. IOI 2010 10.1. CLUEDO 979

1 const
2 NFeatures = 3; { number of features }
3 Confirmed = 0; { result when theory is confirmed }
4
5 type
6 TFeature = 1 .. NFeatures;
7 TOption = 1 .. MaxInt; { value for a feature }
8 TTheory = array [ TFeature ] of TOption;
9 TResult = Confirmed .. NFeatures;
10
11 function TestTheory(T: TTheory): TResult;
12 begin
13 TestTheory := Theory(T[1], T[2], T[3])
14 end;
15
16 procedure Solve;
17 var
18 T: TTheory = (6, 10, 6); { candidate theory }
19 i: TResult; { result of TestTheory(T) }
20
21 begin
22 repeat
23 i := TestTheory(T);
24 if i <> Confirmed then { T refuted }
25 T[i] := T[i] - 1
26 until i = Confirmed
27 { T confirmed }
28 end;

In C, without constant and type definitions, it could look like this:

Listing 10.1.2: cluedosol.cpp


void Solve()
{
int T[] = {0, 6, 10, 6}; // candidate theory, ignore T[0]
int i; // result of Theory
do
{
i = Theory(T[1], T[2], T[3]);
if (i != 0) --T[i];
} while (i != 0);
}

10.1.2 Coduri sursă

Listing 10.1.3: cluedo-218031.cpp


// https://oj.uz/submission/218031 15 ms 512 KB

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include<ctime>
#include<iostream>

#include "grader.h"
#include "cluedo.h"

static int M,L,W,gotit,cnt,maxcnt;

#include<bits/stdc++.h>

using namespace std;

void Solve()
{
CAPITOLUL 10. IOI 2010 10.1. CLUEDO 980

int m=1,l=1,w=1;
while(1)
{
int r = Theory(m,l,w);
if(!r) return ;
if(r==1) ++m;
if(r==2) ++l;
if(r==3) ++w;
}
}

// -------------- begin grader ----------------------

int Theory(int m, int l, int w)


{
++cnt;
if (m < 1 || m > 6 || l < 1 || l > 10 || w < 1 || w > 6) exit(92);

if (rand()%2 && m != M) return 1;


else
if (rand()%2 && l != L) return 2;
else
if (rand()%2 && w != W) return 3;
else
if (m != M) return 1;
else
if (l != L) return 2;
else
if (w != W) return 3;

gotit = 1;
return 0;
}

int main()
{
std::freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
//std::freopen("cluedo.out", "w", stdout);

while (3 == scanf("%d%d%d",&M,&L,&W))
{
cnt = gotit = 0;

Solve();

if (cnt > maxcnt) maxcnt = cnt;


if (!gotit)
{
printf("NO\n");
return 91;
}
}

printf("OK %d\n",maxcnt);

return 0;
}

// -------------- end grader ----------------------

Listing 10.1.4: cluedo-220599.cpp


// https://oj.uz/submission/220599 16 ms 384 KB

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include<ctime>
#include<iostream>

#include "grader.h"
#include "cluedo.h"

static int M,L,W,gotit,cnt,maxcnt;


CAPITOLUL 10. IOI 2010 10.1. CLUEDO 981

void Solve()
{
int i=1, j=1, k=1, r;
while(1)
{
r = Theory(i, j, k);
if(r==0) return;
else if(r==1) i++;
else if(r==2) j++;
else if(r==3) k++;
}
}

// -------------- begin grader ----------------------

int Theory(int m, int l, int w)


{
++cnt;
if (m < 1 || m > 6 || l < 1 || l > 10 || w < 1 || w > 6) exit(92);

if (rand()%2 && m != M) return 1;


else
if (rand()%2 && l != L) return 2;
else
if (rand()%2 && w != W) return 3;
else
if (m != M) return 1;
else
if (l != L) return 2;
else
if (w != W) return 3;

gotit = 1;
return 0;
}

int main()
{
std::freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
//std::freopen("cluedo.out", "w", stdout);

while (3 == scanf("%d%d%d",&M,&L,&W))
{
cnt = gotit = 0;

Solve();

if (cnt > maxcnt) maxcnt = cnt;


if (!gotit)
{
printf("NO\n");
return 91;
}
}

printf("OK %d\n",maxcnt);

return 0;
}
// -------------- end grader ----------------------

Listing 10.1.5: cluedo-229322.cpp


// https://oj.uz/submission/229322 17 ms 384 KB

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include<ctime>
#include<iostream>

#include "grader.h"
#include "cluedo.h"
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 982

static int M,L,W,gotit,cnt,maxcnt;

#include <bits/stdc++.h>

void Solve(void)
{
int i[] = {1, 1, 1};
while(true)
{
int r = Theory(i[0], i[1], i[2]);
if(r == 0) return;
i[r - 1]++;
}
}

// -------------- begin grader ----------------------

int Theory(int m, int l, int w)


{
++cnt;
if (m < 1 || m > 6 || l < 1 || l > 10 || w < 1 || w > 6) exit(92);

if (rand()%2 && m != M) return 1;


else
if (rand()%2 && l != L) return 2;
else
if (rand()%2 && w != W) return 3;
else
if (m != M) return 1;
else
if (l != L) return 2;
else
if (w != W) return 3;

gotit = 1;
return 0;
}

int main()
{
std::freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
//std::freopen("cluedo.out", "w", stdout);

while (3 == scanf("%d%d%d",&M,&L,&W))
{
cnt = gotit = 0;

Solve();

if (cnt > maxcnt) maxcnt = cnt;


if (!gotit)
{
printf("NO\n");
return 91;
}
}

printf("OK %d\n",maxcnt);

return 0;
}

// -------------- end grader ----------------------

10.1.3 *Rezolvare detaliată

10.2 Hotter Colder


Problema 2 - Hotter Colder 100 de puncte
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 983

Author: Gordon Cormack (CAN)

Jack and Jill play a game called Hotter, Colder. Jill has a number between 1 and N , and Jack
makes repeated attempts to guess it.
Each of Jack’s guesses is a number between 1 and N . In response to each guess, Jill answers
hotter, colder or same. For Jack’s first guess, Jill answers same. For the remaining guesses Jill
answers:
ˆ hotter if this guess is closer to Jill’s number than his previous guess
ˆ colder if this guess is farther from Jill’s number than his previous guess
ˆ same if this guess is neither closer to nor further from Jill’s number than his previous guess.

You are to implement a procedure HC(N) that plays Jack’s role. This implementation may
repeatedly call Guess(G), with G a number between 1 and N . Guess(G) will return 1 to
indicate hotter, -1 to indicate colder or 0 to indicate same. HC(N) must return Jill’s number.
Example
As example, assume N 5, and Jill has chosen the number 2. When procedure HC makes the
following sequence of calls to Guess, the results in the second column will be returned.
Call Returned value Explanation
Guess(5) 0 Same (first call)
Guess(3) 1 Hotter
Guess(4) -1 Colder
Guess(1) 1 Hotter
Guess(3) 0 Same
At this point Jack knows the answer, and HC should return 2. It has taken Jack 5 guesses to
determine Jill’s number. You can do better.
Subtask 1 [25 points]
HC(N) must call Guess(G) at most 500 times. There will be at most 125 250 calls to HC(N),
with N between 1 and 500.
Subtask 2 [25 points]
HC(N) must call Guess(G) at most 18 times. There will be at most 125 250 calls to HC(N)
with N between 1 and 500.
Subtask 3 [25 points]
HC(N) must call Guess(G) at most 16 times. There will be at most 125 250 calls to HC(N)
with N between 1 and 500.
Subtask 4 [up to 25 points]
Let W be the largest integer, such that 2W 3N . For this subtask your solution will score:
ˆ 0 points, if HC(N) calls Guess(G) 2W times or more,
ˆ 25α points, where α is the largest real number, such that 0 $ α $ 1 and HC(N) calls
Guess(G) at most 2W  αW times,
ˆ 25 points, if HC(N) calls Guess(G) at most W times.

There will be at most 1 000 000 calls to HC(N) with N between 1 and 500 000 000.
Be sure to initialize any variables used by HC every time it is called.
Implementation Details
ˆ Implementation folder: /home/ioi2010-contestant/hottercolder/
ˆ To be implemented by contestant: hottercolder.c or hottercolder.cpp or
hottercolder.pas
ˆ Contestant interface: hottercolder.h or hottercolder.pas
ˆ Grader interface: grader.h or graderlib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas and graderlib.pas
ˆ Sample grader input: grader.in.1 grader.in.2.
Note: The input file contains several lines, each containing N and Jill’s number.
ˆ Expected output for sample grader input: the grader will create files grader.out.1
grader.out.2 etc.
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 984

` If the implementation correctly implements Subtask 1, one line of output will contain
OK 1
` If the implementation correctly implements Subtask 2, one line of output will contain
OK 2
` If the implementation correctly implements Subtask 3, one line of output will contain
OK 3
` If the implementation correctly implements Subtask 4, one line of output will contain
OK 4 alpha α
ˆ Compile and run (command line): runc grader.c or runc grader.cpp or
runc grader.pas
ˆ Compile and run (gedit plugin): Control-R, while editing any implementation file.
ˆ Submit (command line): submit grader.c or submit grader.cpp or
submit grader.pas
ˆ Submit (gedit plugin): Control-J, while editing any implementation or grader file.

10.2.1 Indicaţii de rezolvare

This problem is an interesting variant of the well-known guessing game Higher-Lower, also featured
in the demonstration task Guess.
Higher-Lower is efficiently solved by the, also well-known, Binary Search algorithm. Binary
Search maintains an interval of still possible numbers (candidates). Initially, this interval includes
all numbers in the range. By comparing to the middle candidate, the interval can be halved by
a single guess. Thus, the secret number can be determined in a logarithmic (to base 2) number
of guesses. Or, to put it differently, if the range of allowed numbers is doubled, than the secret
number can be determined with one additional guess.
Subtask 1
Doing a Linear Search, that is, successively calling Guess(i) for i from 1 to N , yields a
solution requiring N calls to Guess, in the worst case. This solves Subtask 1. See below for a
Pascal program.
Analysis
To get a better understanding of the Hotter-Colder problem, it helps to formalize the rules of
this game.
Let J be Jill’s number, and let P be the most recent guess, that is, Guess P  was called last.
In that situation, Guess G will return

HOTTER if abs(G - J) < abs(P - J)


COLDER if abs(G - J) > abs(P - J)
SAME if abs(G - J) = abs(P - J)

Or in a single formula: sign(abs(P - J) - abs(G - J)).


Letting M P  G©2, this can be rephrased as

if P <= G then
HOTTER if J > M
COLDER if J < M
SAME if J = M
else
HOTTER if J < M
COLDER if J > M
SAME if J = M

Or in a single formula: sign(G - P) * sign(J - M).


Thus, we see that each Guess G effectively provides a high-low comparison to the midpoint
M . In fact, sign G  P  ˜ Guess G sign J  M  offers a genuine high-low comparison.
Unfortunately, due to the range restriction on G, we cannot make the midpoint M go wherever
we want. So, a straightforward Binary Search is not going to work.
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 985

Subtask 2
Ignoring the results of all odd calls to Guess, we can extract one bit of information out of every
successive pair of odd-numbered and next even-numbered call to Guess. This yields a solution
that calls Guess at most W times, where W is the largest integer such that 2
W ©2
& N . That is,
2 9
it makes at most log2 N (rounded up) calls to Guess. For N 500 (almost 2 ), this boils down
to making at most 18 calls.
Subtask 3
By exploiting the fact that we actually do a high/low/equal comparison instead of a pure
high/low (binary) comparison, we can gain almost one extra bit of information (taken over all
guesses).
k k
Explanation: a complete binary tree with 2 leaves has 2  1 internal nodes. So, the same
number of high/low/equal guesses can reach twice the number of nodes minus one (compared to
using just binary high/low guesses).
A Pascal program is given below.
Subtask 4
The preceding approaches obviously throw away (ignore) valuable information. However, using
this information requires careful tuning of the guesses. It helps to do some small cases by hand.

ˆ N 3 can obviously be done in 2 guesses, by straddling the middle, for example, Guess 1
followed by Guess 3 does a high/low/equal comparison to 2.
ˆ N 5 can be done in 3, but this already needs some care, because it does not work to set
this up so that the first two guesses compare to the middle number 3. When, after Guess 1
Guess 5, or Guess 2 Guess 4, the result of the second guess is colder, you won’t be able
to solve the remaining problem in a single guess.
You need to start with Guess 1 Guess 3 (or symmetrically Guess 5 Guess 3). If the
result of the second guess is same, Jill’s number is 2; if the result is colder, only candidate 1
remains and this must be Jill’s number. If the result is hotter, candidates 3, 4, and 5 remain.
Since 3 was the most recent guess, doing Guess 5 will compare to 4, and we are done.

In general, it turns out to be possible to determine Jill’s number in no more than log2 3 ˜ N
log2 3  log2 N calls of Guess.
We explain one such algorithm. Because of the nature of the guess (being a comparison), at
any moment you have an interval of remaining candidate numbers. You can distinghuish two cases
for the location of this interval with respect to the initial interval:

1. either this interval of candidates contains 1 or N (is ”against a wall”);


2. or it contains neither 1 nor N (is ”in the middle”).

Furthermore, you know what the previous guess was, say P .


If the interval of candidates is ”in the middle”, then you are home free (provided you are a
bit careful), because now each guess can be made to reduce the interval sufficiently. In K more
K 1
guesses, you can find Jill’s number among 2  1 candidates. [Details suppressed (for the time
being)]
If the interval of candidates is ”against a wall”, then you can always arrange it so that the
interval is 1 through P (or symmetrically on the other side). With two extra steps you can grow
K 2
a solution that solves for P in K more guesses to one that solves for P  2 in K  2 more
guesses.
The base cases are P 3, K 1 and P 7, K 2.
The construction works like this. Consider the interval
aaaabbbbbbdddddddddd
where
ˆ aaaa is the interval 1 through P (and we assume that if the most recent guess was at P ,
then an additional K guesses can determine Jill’s number in this interval);
K 1
ˆ bbbbbb is of length 2 2; 

K 1
ˆ dddddddddd is of length 2  2;
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 986

K 2
ˆ the most recent guess was R P  2 .
Your next guess is G P  2:
aaaabbbbbbdddddddddd
1G P M R
K 2 K 1
This guess compares to M G  R©2 P 2P 2 ©2 P  2  2  1, that is,
the first element of the d-labeled subinterval. Do a case distinction on the result of this guess:
ˆ Same: Jill’s number is M ; done.
ˆ Colder: the interval is reduced to M  1 through R; continue with a ”middle game” on
K 1
ddddddddd of length 2  1;

ˆ Hotter: the interval is reduced to 1 through M  1:


ˆ aaaabbbbbb
ˆ 1G P M
Next, guess P , which boils down to comparing to G  P ©2 P  1. Do a case distinction
on the result:

` Colder: ”wall game” on interval 1 through P (aaaa), which we assumed can be solved in
K more guesses;
K 1
` Hotter: ”middle game” on abbbbbb of length 2  1.

A C program solving Subtask 4 can be found.


Pascal program for Linear Search solving Subtask 1

Listing 10.2.1: coldersol1.pas


1 const
2 Colder = -1;
3 Same = 0;
4 Hotter = +1;
5
6 type
7 TResult = Colder .. Hotter;
8
9 function HC(N: Longint): Longint;
10 { returns secret number of Jill }
11
12 var
13 r: TResult; { result of Guess } G: Longint; { argument for Guess }
14
15 begin
16 if N = 1 then begin HC := N
17 ; Exit
18 end { if }
19 { N >= 2 }
20 ; G := 1
21 ; r := Guess(G) { ignored }
22
23 ; repeat
24 { numbers >= G are remaining candidates; G < N } G := G + 1
25 ; r := Guess(G) { compares to G - 0.5; r <> Same } until (r = Colder) or (G = N)
26
27 ; case r of
28 Colder: HC := G - 1; Hotter: HC := G;
29 end { case r } end;

Pascal program for wasteful Binary Search solving Subtask 3

Listing 10.2.2: coldersol2.pas


1 const
2 Colder = -1;
3 Same = 0;
4 Hotter = +1;
5
6 type
7 TResult = Colder .. Hotter;
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 987

8
9 function HC(N: Longint): Longint;
10 { returns secret number of Jill }
11
12 var
13 r: TResult; { result of Guess }
14 a, b: Longint; { [a .. b] is interval of remaining candidates }
15
16 begin
17 if N = 1 then begin HC := N
18 ; Exit
19 end { if }
20 { N >= 2 }
21
22 ; a := 1
23 ; b := N
24
25 { invariant: 1 <= a <= b <= N }
26 ; while a <> b do begin
27 r := Guess(a) { ignored }
28 ; r := Guess(b) { compares to (a+b)/2 }
29 ; case r of
30 Colder: b := (a + b - 1) div 2; { largest integer < (a+b)/2 } Same: begin a := (a + b)
div 2 ; b :=
31 a end;
32 Hotter: a := (a + b + 1) div 2; { smallest integer > (a+b)/2 } end { case r }
33 end { while }
34 { a = b }
35 ; HC := a
36 end;

10.2.2 Coduri sursă

Listing 10.2.3: hottercolder.c


1 // Author: Gordon V. Cormack; solves Subtask 4
2
3 #include "grader.h"
4 #include "hottercolder.h"
5
6 #include <stdio.h>
7 #include <math.h>
8 #include <stdlib.h>
9 #include <assert.h>
10
11 int t[50];
12
13 #define max(a, b) ((a)>(b)?(a):(b))
14 #define min(a, b) ((a)<(b)?(a):(b))
15
16 int fix(int end, int x)
17 {
18 // returns value at x steps from end
19 if (end == 1) return x;
20 return end-x+1;
21 }
22
23 int midgame(int p, int a, int b)
24 {
25 // returns Jill’s number, assuming
26 // p = previous guess, [a .. b] = interval of remaining candidates
27 if (a == b) return a;
28 if (a > b)
29 {
30 int t = a;
31 a = b;
32 b = t;
33 }
34
35 // a < b
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 988

36 int sz, mid=-999;


37 for (sz=3; b-a+1 > sz; sz = 2*sz+1);
38
39 if (p < b-sz+1) mid = b-sz/2;
40 else
41 if (p > a+sz-1) mid = a+sz/2;
42 else
43 if (p <= a) mid = p+sz/2;
44 else
45 if (p >= b) mid = p-sz/2;
46
47 int q = mid + (mid-p);
48 int g = Guess(q); // compares to mid
49 if (g == 0) return mid;
50 if (q > mid && g > 0 || q < mid && g < 0)
51 return midgame(q, min(mid+1, b), b);
52
53 return midgame(q, a, max(mid-1, a));
54 }
55
56 int endgame(int end, int n)
57 {
58 // returns Jill’s number, assuming
59 // end = value on edge (1 or N);
60 // n = number of remaining candidate values
61 int z, g;
62 for (z=0; t[z]<n; z++);
63
64 if (n == 2)
65 {
66 g = Guess(fix(end, 1));
67 if (g > 0) return fix(end, 1);
68 return fix(end, 2);
69 }
70
71 if (n == 3)
72 {
73 g = Guess(fix(end, 1));
74 if (g > 0) return fix(end, 1);
75 if (g == 0) return fix(end, 2);
76 if (g < 0) return fix(end, 3);
77 }
78
79 if (n == 4 || n == 5)
80 {
81 g = Guess(fix(end, 3));
82 if (g < 0) return fix(end, n);
83 if (g == 0) return fix(end, 4);
84 g = Guess(fix(end, 1));
85 if (g > 0) return fix(end, 1);
86 if (g == 0) return fix(end, 2);
87 if (g < 0) return fix(end, 3);
88 }
89
90 if (n == 6)
91 {
92 g = Guess(fix(end, 1));
93 if (g > 0)
94 {
95 g = Guess(fix(end, 3));
96 if (g > 0) return fix(end, 3);
97 if (g == 0) return fix(end, 2);
98 if (g < 0) return fix(end, 1);
99 }
100
101 g = Guess(fix(end, 9));
102 if (g > 0) return fix(end, 6);
103 if (g == 0) return fix(end, 5);
104 if (g < 0) return fix(end, 4);
105 }
106
107 if (n == 7)
108 {
109 g = Guess(fix(end, 1));
110 if (g == 0) return fix(end, 4);
111 if (g > 0)
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 989

112 {
113 int g = Guess(fix(end, 3));
114 if (g > 0) return fix(end, 3);
115 if (g == 0) return fix(end, 2);
116 return fix(end, 1);
117 }
118
119 g = Guess(fix(end, 11));
120 if (g > 0) return fix(end, 7);
121 if (g == 0) return fix(end, 6);
122 if (g < 0) return fix(end, 5);
123 }
124
125 g = Guess(fix(end, t[z-2]-2));
126 if (g == 0) return fix(end, (t[z-2]-2+n)/2);
127 if (g < 0)
128 return midgame(fix(end, t[z-2]-2),
129 fix(end, (t[z-2]-2+n)/2+1),
130 fix(end, n));
131
132 // g > 0
133 g = Guess(fix(end, t[z-2]));
134 if (g < 0) return endgame(end, t[z-2]);
135 if (g == 0) return fix(end, t[z-2]-1);
136 return midgame(fix(end, t[z-2]),
137 fix(end, t[z-2]),
138 fix(end, (t[z-2]-2+n-1)/2));
139 return 0;
140 }
141
142 int HC(int N)
143 {
144 // returns Jill’s number
145 int i, mid;
146 if (!t[0])
147 {
148 t[0] = 1;
149 t[1] = 3;
150 t[2] = 7;
151 for (i=3; i<30; i++) t[i] = t[i-2] + (1l<<i);
152 }
153
154 if (N == 1) return 1;
155 if (N == 2)
156 {
157 Guess(1);
158 i = Guess(2);
159 if (i > 0) return 2;
160 else return 1;
161 }
162
163 if (N == 3)
164 {
165 Guess(1);
166 i = Guess(3);
167 if (i > 0) return 3;
168 if (i < 0) return 1;
169 return 2;
170 }
171
172 mid = (N+2)/2;
173 Guess(mid-2);
174 i = Guess(mid);
175 if (i == 0) return mid-1;
176 if (i < 0) return endgame(1, mid);
177 return endgame(N, N-mid+1);
178 }
179
180 // -------------- begin grader ---------------------
181
182 static int moves, TT, NN, prev = -1;
183 int Guess(int x)
184 {
185 int r;
186
187 if (prev == -1 || abs(x-TT) == abs(prev-TT)) r = 0;
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 990

188 else
189 if (abs(x-TT) > abs(prev-TT)) r = -1;
190 else r = 1;
191
192 prev = x;
193 if (x < 1 || x > NN) exit(92);
194 moves++;
195 return r;
196 }
197
198 int main()
199 {
200 freopen("../tests/Subtask4-data/grader.in.2", "r", stdin);
201 //freopen("hottercolder.out", "w", stdout);
202
203 int n=0,i,t,OK=0,sub1=0,sub2=0,sub3=0;
204 double worst = 999999;
205 while (2 == scanf("%d%d",&NN,&TT))
206 {
207 if (NN > n) n = NN;
208 prev = -1;
209 moves = 0;
210 int h = HC(NN);
211 if (h != TT)
212 {
213 exit(91);
214 }
215
216 int W = floor(0.00001+log(3*NN)/log(2));
217 double alpha = 2 - (double)moves/W;
218 if (alpha < worst) worst = alpha;
219
220 // 1 means failure
221 if ( NN <= 500 && moves > 500 ) exit(93);
222 if ( NN <= 500 && moves > 18 ) sub2=1;
223 if ( NN <= 500 && moves > 16 ) sub3=1;
224 OK++;
225 }
226
227 if (!sub1) printf("OK 1\n");
228 if (!sub2) printf("OK 2\n");
229 if (!sub3) printf("OK 3\n");
230
231 if (worst > 0) printf("OK 4 alpha %0.2lf\n",worst);
232
233 return 0;
234 }
235
236 // -------------- end grader ---------------------
237 /*
238 OK 1
239 OK 2
240 OK 3
241 OK 4 alpha 1.00
242
243 Process returned 0 (0x0) execution time : 4.323 s
244 Press any key to continue.
245 */

Listing 10.2.4: hottercolder-173047.cpp


// https://oj.uz/submission/173047 895 ms 8172 KB

#include "grader.h"
#include "hottercolder.h"

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int cal(int n)
{
return n < 5 ? 1 : (n + 1 >> 1) - cal(n >> 1);
}
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 991

int HC(int N)
{
int L = 1, R = N, prv, nxt, t, W = log(3 * N) / log(2);
while (L < R)
{
if (R == 2)
{
Guess(1);
return Guess(2) < 0 ? 1 : 2;
}
int cut = W & 1 ? (2 << W - 2) / 3 + 1 : (2 << W - 2) / 3 + 2;
nxt = R == N ? cut + cal(R - cut) : cut + cut - 1;
prv = cut + cut - nxt;
Guess(prv);
t = Guess(nxt);
if (t == -1) R = prv + nxt - 1 >> 1;
if (t == 0) return prv + nxt >> 1;
if (t == 1)
{
L = prv + nxt + 2 >> 1;
prv = nxt;
while (L < R)
{
nxt = (L + R >> 1 << 1) - prv;
if (nxt == prv) nxt++;
if (nxt < 1) nxt = 1;
if (nxt > N) nxt = N;
t = Guess(nxt);
if (t == -1)
{
if (prv < nxt) R = nxt + prv - 1 >> 1;
else L = nxt + prv + 2 >> 1;
}
if (t == 0) return nxt + prv >> 1;
if (t == 1)
{
if (prv < nxt) L = nxt + prv + 2 >> 1;
else R = nxt + prv - 1 >> 1;
}
prv = nxt;
}
}
W -= 2;
}
return L;
}

// ----------------------- begin grader --------------------

static int moves, TT, NN, prev = -1;


int Guess(int x){
int r;
if (prev == -1 || abs(x-TT) == abs(prev-TT)) r = 0;
else if (abs(x-TT) > abs(prev-TT)) r = -1;
else r = 1;
prev = x;
if (x < 1 || x > NN) exit(92);
moves++;
return r;
}

int main()
{
//freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
freopen("../tests/Subtask4-data/grader.in.2", "r", stdin);

//freopen("hottercolder.out", "w", stdout);

int n=0,i,t,OK=0,sub1=0,sub2=0,sub3=0;
double worst = 999999;
while (2 == scanf("%d%d",&NN,&TT))
{
if (NN > n) n = NN;
prev = -1;
moves = 0;
int h = HC(NN);
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 992

if (h != TT)
{
exit(91);
}
int W = floor(0.00001+log(3*NN)/log(2));
double alpha = 2 - (double)moves/W;
if (alpha < worst) worst = alpha;
// 1 means failure
if ( NN <= 500 && moves > 500 ) exit(93);
if ( NN <= 500 && moves > 18 ) sub2=1;
if ( NN <= 500 && moves > 16 ) sub3=1;
OK++;
}
if (!sub1) printf("OK 1\n");
if (!sub2) printf("OK 2\n");
if (!sub3) printf("OK 3\n");
if (worst > 0) printf("OK 4 alpha %0.2lf\n",worst);
return 0;
}
// ----------------------- end grader --------------------
/*
OK 1
OK 2
OK 3
OK 4 alpha 1.00

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


Press any key to continue.
*/

Listing 10.2.5: hottercolder-201977.cpp


// https://oj.uz/submission/201977 1672 ms 8192 KB

#include "grader.h"
#include "hottercolder.h"

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


typedef long double ld;
typedef pair<int,int> pi;
typedef vector<int> vi;
typedef vector<pi> vpi;

#define f first
#define s second
#define sz(x) (int)x.size()
#define all(x) begin(x), end(x)
#define rsz resize
#define bk back()
#define pb push_back

#define FOR(i,a,b) for (int i = (a); i < (b); ++i)


#define F0R(i,a) FOR(i,0,a)
#define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
#define R0F(i,a) ROF(i,0,a)
#define trav(a,x) for (auto& a: x)

const int MOD = 1e9+7;


const ld PI = acos((ld)-1);

template<class T> bool ckmin(T& a, const T& b)


{
return b < a ? a = b, 1 : 0;
}
template<class T> bool ckmax(T& a, const T& b)
{
return a < b ? a = b, 1 : 0;
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 993

vi soFar, guess;
pi posi;
int N;

bool tri(int x)
{
guess.pb(Guess(x));
if (guess.bk == 0)
{
if (sz(soFar) && soFar.bk != x)
posi.f = posi.s = (soFar.bk+x)/2;
}
else
{
if (guess.bk == -1)
{
if (x < soFar.bk)
{
ckmax(posi.f,(soFar.bk+x)/2+1);
}
else
{
ckmin(posi.s,(soFar.bk+x-1)/2);
}
}
else
{
if (x < soFar.bk)
{
ckmin(posi.s,(soFar.bk+x-1)/2);
}
else
{
ckmax(posi.f,(soFar.bk+x)/2+1);
}
}
}
soFar.pb(x); assert(posi.f <= posi.s);
return posi.f == posi.s;
}

int binSearch()
{
while (1)
{
int des = posi.f+posi.s-soFar.bk;
ckmax(des,1); ckmin(des,N);
if (tri(des)) return posi.f;
}
}

int HC(int _N)


{
N = _N;
guess.clear(), soFar.clear(); posi = {1,N};
if (N == 1) return 1;
int m = N/2+1;
int a = m-1, b = m;
tri(a); if (tri(b)) return posi.f;
if (posi.f == 1)
{
while (1)
{
if (soFar.bk <= 3)
{
assert(tri(1));
return posi.f;
}
if (soFar.bk <= 7)
{
tri(1);
return binSearch();
}
vi one = {3}, two = {7};
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 994

for (int i = 3;;++i)


{
if (i&1)
{
one.pb(one.bk+(1<<i));
if (one.bk >= soFar.bk)
{
int p = one[sz(one)-2];
if (tri(p+2)) return posi.f;
if (posi.f != 1) return binSearch();
if (tri(p)) return posi.f;
if (posi.f != 1) return binSearch();
break;
}
}
else
{
two.pb(two.bk+(1<<i));
if (two.bk >= soFar.bk)
{
int p = two[sz(two)-2];
if (tri(p+2)) return posi.f;
if (posi.f != 1) return binSearch();
if (tri(p)) return posi.f;
if (posi.f != 1) return binSearch();
break;
}
}
}
}
}
else
{
while (1)
{
if (soFar.bk >= N+1-3)
{
assert(tri(N));
return posi.f;
}
if (soFar.bk >= N+1-7)
{
tri(N);
return binSearch();
}
vi one = {3}, two = {7};
for (int i = 3;;++i)
{
if (i&1)
{
one.pb(one.bk+(1<<i));
if (N+1-one.bk <= soFar.bk)
{
int p = one[sz(one)-2];
if (tri((N+1)-(p+2))) return posi.f;
if (posi.s != N) return binSearch();
if (tri(N+1-p)) return posi.f;
if (posi.s != N) return binSearch();
break;
}
}
else
{
two.pb(two.bk+(1<<i));
if (N+1-two.bk <= soFar.bk)
{
int p = two[sz(two)-2];
if (tri((N+1)-(p+2))) return posi.f;
if (posi.s != N) return binSearch();
if (tri((N+1)-p)) return posi.f;
if (posi.s != N) return binSearch();
break;
}
}
}
}
CAPITOLUL 10. IOI 2010 10.2. HOTTER COLDER 995

}
}

// ----------------------- begin grader --------------------

static int moves, TT, NN, prevv = -1;

int Guess(int x)
{
int r;
if (prevv == -1 || abs(x-TT) == abs(prevv-TT)) r = 0;
else
if (abs(x-TT) > abs(prevv-TT)) r = -1;
else r = 1;

prevv = x;
if (x < 1 || x > NN) exit(92);
moves++;
return r;
}

int main()
{
//freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
freopen("../tests/Subtask4-data/grader.in.2", "r", stdin);

//freopen("hottercolder.out", "w", stdout);

int n=0,i,t,OK=0,sub1=0,sub2=0,sub3=0;
double worst = 999999;
while (2 == scanf("%d%d",&NN,&TT))
{
if (NN > n) n = NN;
prevv = -1;
moves = 0;
int h = HC(NN);
if (h != TT)
{
exit(91);
}
int W = floor(0.00001+log(3*NN)/log(2));
double alpha = 2 - (double)moves/W;
if (alpha < worst) worst = alpha;
// 1 means failure
if ( NN <= 500 && moves > 500 ) exit(93);
if ( NN <= 500 && moves > 18 ) sub2=1;
if ( NN <= 500 && moves > 16 ) sub3=1;
OK++;
}
if (!sub1) printf("OK 1\n");
if (!sub2) printf("OK 2\n");
if (!sub3) printf("OK 3\n");
if (worst > 0) printf("OK 4 alpha %0.2lf\n",worst);
return 0;
}
// ----------------------- end grader --------------------
/*
OK 1
OK 2
OK 3
OK 4 alpha 1.00

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


Press any key to continue.
*/
CAPITOLUL 10. IOI 2010 10.3. QUALITY OF LIVING 996

10.2.3 *Rezolvare detaliată

10.3 Quality of Living


Problema 3 - Quality of Living 100 de puncte

Author: Christopher Chen (AUS)

Cities in Alberta tend to be laid out as rectangular grids of blocks. Blocks are labeled with
coordinates 0 to R  1 from north to south and 0 to C  1 from west to east.
The quality of living in each particular block has been ranked by a distinct number, called
quality rank, between 1 and R ˜ C, where 1 is the best and R ˜ C is the worst.
The city planning department wishes to identify a rectangular set of blocks with dimensions H
from north to south and W from west to east, such that the median quality rank among all blocks
in the rectangle is the best. H and W are odd numbers not exceeding R and C respectively. The
median quality rank among an odd number of quality ranks is defined to be the quality rank m in
the set such that the number of quality ranks better than m equals the number of quality ranks
worse than m.
You are to implement a procedure rectangle(R,C,H,W,Q) where R and C represent the
total size of the city, H and W represent the dimensions of the set of blocks, and Q is an array
such that Qab is the quality rank for the block labeled a from north to south and b from west
to east.
Your implementation of rectangle must return a number: the best (numerically smallest)
possible median quality rank of an H by W rectangle of blocks.
Each test run will only call rectangle once.
Example 1
5 11 12 16 25
17 18 2 7 10
R 5, C 5, H 3, W 3, Q = 4 23 20 3 1
24 21 19 14 9
6 22 8 13 15
For this example, the best (numerically smallest) median quality rank of 9 is achieved by the
middle-right rectangle of Q shown in bold. That is, rectangle(R,C,H,W,Q)=9
Example 2
6 1 2 11 7 5
R 2, C 6, H 1, W 5, Q =
9 3 4 10 12 8
For this example the correct answer is 5.
Subtask 1 [20 points]
Assume R and C do not exceed 30.
Subtask 2 [20 points]
Assume R and C do not exceed 100.
Subtask 3 [20 points]
Assume R and C do not exceed 300.
Subtask 4 [20 points]
Assume R and C do not exceed 1 000.
Subtask 5 [20 points]
Assume R and C do not exceed 3 000.
Implementation Details
ˆ Implementation folder: /home/ioi2010-contestant/quality/
ˆ To be implemented by contestant: quality.c or quality.cpp or quality.pas
ˆ Contestant interface: quality.h or quality.pas
ˆ Grader interface: none
ˆ Sample grader: grader.c or grader.cpp or grader.pas
CAPITOLUL 10. IOI 2010 10.3. QUALITY OF LIVING 997

ˆ Sample grader input: grader.in.1 grader.in.2 etc.


Note: The first line of input contains: R, C, H, W The following lines contain the elements
of Q, in row-major order.
ˆ Expected output for sample grader input: grader.expect.1 grader.expect.2 etc.
ˆ Compile and run (command line): runc grader.c or runc grader.cpp or
runc grader.pas
ˆ Compile and run (gedit plugin): Control-R, while editing any implementation file.
ˆ Submit (command line): submit grader.c or submit grader.cpp or
submit grader.pas
ˆ Submit (gedit plugin): Control-J, while editing any implementation or grader file.

10.3.1 Indicaţii de rezolvare

Task Author: Christopher Chen (AUS)

This problem looks like many other grid tasks. Such problems have also appeared on some
previous IOIs. Heavy range-search algorithms might seem to be useful, but actually a much
simpler 100% solution exists.
Let N R ˜ C measure the size of a problem instance.
Subtask 1
Subtask 1 can be solved by the most obvious brute force algorithm that considers each rectangle
2
(there are R  H  1 ˜ C  W  1 of these), quadratically sorts its contents ( H ˜ W  steps),
and directly picks out the median rank, and optimizes this. The worst case situation is obtained
3
by H R©2 and W C ©2. Therefore, this algorithm’s time complexity is O N .
Subtask 2
Using any O N log N  sort algorithm (these are well-known), the brute force algorithm can be
2
improved to O N log N . This solves subtask 2.
2
Also, an O N  sort (bucket sort) is possible, to obtain a simple O N  algorithm, but this
does not suffice to solve Subtask 3. See below for a Pascal implementation.
There are some obvious opportunities for improvement, such as exploiting the large overlap
between certain rectangles when filling/emptying the array to be sorted. But these improvements
do not affect the time complexity.
Subtask 3
1.5
O N log N  algorithms are also possible and they solve Subtask 3. Here is one: say the
0.5
vertical offset of the final rectangle is known [O N  possibilities]. Then scan across the row,
using some efficient data structure to keep track of the median (think of incremental/sliding
window, such as a range tree plus binary search, or a pair of heaps for values less/greater than the
median). [Each value is added/subtracted from the data structure exactly once, for an O N log N 
scan length.]
This is rather involved to code; see Subtask 5 for a simpler and better solution.
Subtask 4
This subtask accommodates possibleO N log N log N  algorithms, although we have not en-
countered them.
Subtask 5
Here is a O N log N  solution. Observe that the program’s output can be verified by some
algorithm which answers the question ”Does any rectangle have median & X?” This query can
be answered in O n  time. A rectangle has median & X if and only if it contains more values
2

& X than otherwise. Assign all cells in the grid a ’value’ according to a ’threshold’ function: -1 if
greater than X, 0 if equal to X, 1 if less than X. Using the well-known cumulative data structure
for queries on rectangular sums, try all possible rectangle locations and return ”yes” if the ’values’
inside any sum to ' 0. We simply binary search values of X to find the minimum value for which
the answer is ”yes”.
CAPITOLUL 10. IOI 2010 10.3. QUALITY OF LIVING 998

N.B. An O N log N  algorithm suffices for 100% score.


Linear Solution
42
Mihai Pătraşcu (ROM) found an O N  solution with the following reasoning.
Claim 1:
Given a value X, one can identify in O HW time which rectangles have a median better than X.
Proof:
In linear time, build a partial-sums array: Aij  = #{elements in Q0..i0..j  better than
X }. The number of elements better than X in any rectangle can now be found by combining 4
values of A. The median of an H by W rectangle is better than X if and only if the number of
elements better than X is less than HW ©2. QED
Claim 2:
2
One can find the best median of K designated rectangles, in running time O K log HW   HW .
Proof:
Compress everything to a K  K grid and binary search for the best median. In each step
of the binary search, I need to go over the entire K  K grid, and a number of elements that is
originally HW , but decreasing geometrically each time. QED
Claim 3:
One can find the best median of all rectangles in O HW  time.
Proof:
By the above, one can set K sqrt HW © log HW  and still get linear time. Sample K
rectangles; apply Claim 2. Apply Claim 1 to filter everybody with worse medians. One is left
with HW ©K rectangles. Do the same again, one is left with HW ©K2 & log HW  rectangles.
Now just apply Claim 2. QED
2
O N  Solution for Subtask 2 in Pascal using bucket sort

Listing 10.3.1: rectanglesol.pas


1 const
2 MaxDimension = 3000;
3 MaxRank = sqr(MaxDimension);
4 type
5 TCoordinate = 0 .. MaxDimension - 1;
6 TRank = 1 .. MaxRank;
7 Qtype = array [ TCoordinate, TCoordinate ] of TRank;
8 TRankSet = array [ TRank ] of Boolean;
9 var
10 sorttemp: TRankSet; { for bucket sorting and median finding,
11 kept global for speed }
12 function rectangle(R, C, H, W: Longint; var Q: Qtype): TRank;
13 { returns best median of all HxW rectangles in RxC city Q }
14 function median(a, b: TCoordinate): TRank;
15 { returns median rank in rectangle with top-left corner at [a, b] }
16 var
17 i, j: TCoordinate; { traverses Q }
18 r: Longint; { traverses [ 0 ] + sorttemp }
19 c: Longint; { counts elements in sorttemp <= r }
20 begin
21 { insert elements from rectangle into the buckets }
22 for i := a to a + H - 1 do begin
23 for j := b to b + W - 1 do begin
24 sorttemp[ Q[i, j] ] := True
25 end { for j }
26 end { for i }
27 { determine the median by counting off half the elements }
28 ; r := 0 { r in [ 0 ] + sorttemp }
29 ; for c := 1 to (H * W) div 2 + 1 do begin
30 repeat r := r + 1 until sorttemp[r]
31 end { for c }
32 ; median := r
42
(17 July 1982 - 5 June 2012) https://en.wikipedia.org/wiki/Mihai_Patrascu
CAPITOLUL 10. IOI 2010 10.3. QUALITY OF LIVING 999

33 { restore invariant for sorttemp, by removing the elements }


34 ; for i := a to a + H - 1 do begin
35 for j := b to b + W - 1 do begin
36 sorttemp[ Q[i, j] ] := False
37 end { for j }
38 end { for i }
39 end; { median }
40 var
41 i: TRank; { traverses sortemp for initialization }
42 result: TRank; { best rank seen so far }
43 a, b: TCoordinate; { traverses all rectangles }
44 m: TRank; { median of rectangle with top-left corner at [a, b] }
45 begin
46 for i := 1 to R * C do sorttemp[i] := False
47 ; result := MaxRank;
48 ; for a := 0 to R - H do begin
49 for b := 0 to C - W do begin
50 m := median(a, b)
51 ; if m < result then result := m
52 end { for b }
53 end { for a }
54 ; rectangle := result;
55 end; { rectangle }

10.3.2 Coduri sursă

Listing 10.3.2: quality-208958.cpp


// https://oj.uz/submission/208958 2952 ms 176984 KB

#include <stdio.h>
#include <stdlib.h>
#include "quality.h"

#include<ctime>

#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <vector>
#include <bitset>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#include <tuple>
#include <list>
#include <set>
#include <map>
#define add push_back
#define m_p make_pair
#define fr first
#define sc second
#define endl ’\n’

using namespace std;

typedef long long ll;


typedef unsigned long long llu;
typedef long double ld;
typedef pair<ll, ll> pii;
CAPITOLUL 10. IOI 2010 10.3. QUALITY OF LIVING 1000

const ll N = 1000005, mod = 998244353, M = 3001;


ll n, m, k, i, j, ans, kaskad[N], matrix[M][M], pref[M][M], h, w;

bool check(ll value)


{
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
ll x = -1;
if (matrix[i][j] > value)
x = 1;
else if (matrix[i][j] == value)
x = 0;
pref[i][j] = pref[i - 1][j] + pref[i][j-1]-pref[i-1][j-1]+x;
}
}
for (int i = h; i <= n; ++i)
{
for (int j = w; j <= m; ++j)
{
if (pref[i][j]-pref[i-h][j]-pref[i][j-w]+pref[i-h][j-w] <= 0)
return true;
}
}
return false;
}

void BinarySearch(ll left, ll right)


{
if (right - left <= 1)
return;
ll mid = (left + right) / 2;
if (!check(mid))
{
BinarySearch(mid, right);
}
else
{
ans = mid;
BinarySearch(left, mid);
}
}

int rectangle(int nn, int mm, int hh, int ww, int a[M][M])
{
n = nn;
m = mm;
h = hh;
w = ww;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
matrix[i + 1][j + 1] = a[i][j];
}
}
BinarySearch(0, n * m);
return ans;
}

// ------------ begin grader ---------------

//static int R,C,H,W,Q[3001][3001],i,j,ans;


static int R,C,H,W,Q[3001][3001];

int main()
{
auto t1 = clock();

freopen("../tests/Subtask5-data/grader.in.3-5", "r", stdin);


freopen("quality.out", "w", stdout);

scanf("%d%d%d%d",&R,&C,&H,&W);
for (i=0;i<R;i++)
for (j=0;j<C;j++) scanf("%d",&Q[i][j]);
CAPITOLUL 10. IOI 2010 10.3. QUALITY OF LIVING 1001

auto t2 = clock();

ans = rectangle(R,C,H,W,Q);

auto t3 = clock();

printf("%d\n",ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader ---------------


/*
t2-t1 = 3.828
t3-t2 = 7.109
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.3.3: quality-234933.cpp


// https://oj.uz/submission/234933 2332 ms 216116 KB
#include <stdio.h>
#include <stdlib.h>

#include<bits/stdc++.h>
#include "quality.h"

#define ll long long int


#define pb push_back
#define mp make_pair
#define FOR(i,n) for(i=0;i<(n);++i)
#define FORe(i,n) for(i=1;i<=(n);++i)
#define FORr(i,a,b) for(i=(a);i<(b);++i)
#define FORrev(i,n) for(i=(n);i>=0;--i)
#define F0R(i,n) for(int i=0;i<(n);++i)
#define F0Re(i,n) for(int i=1;i<=(n);++i)
#define F0Rr(i,a,b) for(ll i=(a);i<(b);++i)
#define F0Rrev(i,n) for(int i=(n);i>=0;--i)
#define ii pair<ll,ll>
#define vi vector<ll>
#define vii vector<ii>
#define ff first
#define ss second
#define cd complex<double>
#define vcd vector<cd>
#define ldd long double
#define dbgLine cout<<"Line : "<<__LINE__<<’\n’
#define all(x) (x).begin(),(x).end()

using namespace std;

const short int __PRECISION = 10;

int rect[3000][3000], pre[3000][3000];


pair<int, int> Lookup[9000000];

#define in(t) (Lookup[t].ff > i-H && \


Lookup[t].ff <= i && Lookup[t].ss > j-W && \
Lookup[t].ss <= j)

inline bool bs(int R, int C, int H, int W, int target)


CAPITOLUL 10. IOI 2010 10.3. QUALITY OF LIVING 1002

{
if(target < (H*W)/2)
return false;
F0R(i, R)
F0R(j ,C)
{
pre[i][j]=(rect[i][j]==target ? 0:(rect[i][j]<target ? -1:1))+
(i==0 ? 0:pre[i-1][j]) + (j==0 ? 0:pre[i][j-1]) -
((i==0||j==0) ? 0 : pre[i-1][j-1]);

if(i >= H-1 && j >= W-1)


{
int tmp = pre[i][j] - (i==H-1 ? 0 : pre[i-H][j]) -
(j==W-1 ? 0 : pre[i][j-W]) +
((i==H-1||j==W-1) ? 0 : pre[i-H][j-W]);

if((tmp < 0) || (tmp == 0 && in(target)))


{
return true;
}
}
}
return false;
}

int rectangle(int R, int C, int H, int W, int( * Q)[3001])


{
F0R(i, R)
F0R(j, C)
rect[i][j] = Q[i][j]-1;
F0R(i, R)
F0R(j, C)
Lookup[rect[i][j]] = mp(i, j);
int lo = 0, hi = R*C - 1, mid;
while(hi > lo + 1)
{
mid = (hi + lo)/2;
if(bs(R, C, H, W, mid))
hi = mid;
else
lo = mid;
}
return hi+1;
}

// ------------ begin grader ---------------

static int R,C,H,W,Q[3001][3001],i,j,ans;

int main()
{
auto t1 = clock();

freopen("../tests/Subtask5-data/grader.in.3-5", "r", stdin);


freopen("quality.out", "w", stdout);

scanf("%d%d%d%d",&R,&C,&H,&W);
for (i=0;i<R;i++)
for (j=0;j<C;j++) scanf("%d",&Q[i][j]);

auto t2 = clock();

ans = rectangle(R,C,H,W,Q);

auto t3 = clock();

printf("%d\n",ans);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1003

std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader ---------------


/*
t2-t1 = 3.921
t3-t2 = 8.5
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.3.4: checkerQuality.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/Subtask5-data/grader.in.3-5",
(char*)"../tests/Subtask5-data/grader.expect.3-5",
(char*)"quality.out",
};

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

registerChecker("quality", argc, argv);


compareRemainingLines();
}

10.3.3 *Rezolvare detaliată

10.4 Languages
Problema 4 - Languages 100 de puncte

Author: Gordon Cormack (CAN)

You are to write an interactive program that, given a sequence of Wikipedia excerpts (see
example below), guesses the language of each, in turn. After each guess, your program is given
the correct answer, so that it may learn to make better guesses the longer it plays.
Each language is represented by a number L between 0 and 55. Each excerpt has exactly 100
symbols, represented as an array E of 100 integers between 1 and 65 535. These integers between
1 and 65 535 have been assigned arbitrarily, and do not correspond to any standard encoding.
You are to implement the procedure excerpt(E) where E is an array of 100 numbers representing
a Wikipedia excerpt as described above. Your implementation must call language(L) once, where
L is its guess of the language of the Wikipedia edition from which E was extracted. The grading
server implements language(L), which scores your guess and returns the correct language. That
is, the guess was correct if language(L) = L.
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1004

The grading server calls excerpt(E) 10 000 times, once for each excerpt in its input file. Your
implementation’s accuracy is the fraction of excerpts for which excerpt(E) guessed the correct
language.
You may use any method you wish to solve this problem. Rocchio’s method is an approach
that will yield accuracy of approximately 0.4. Rocchio’s method computes the similarity of E to
each language L seen so far, and chooses the language that is most similar. Similarity is defined
as the total number of distinct symbols in E that appear anywhere amongst the previous excerpts
from language L.
Note that the input data have been downloaded from real Wikipedia articles, and that there
may be a few malformed characters or fragments of text. This is to be expected, and forms part
of the task.
Example
For illustration only, we show the textual representation of excerpts from 56 language-specific
editions of Wikipedia.
1. Yshokkie word meestal in Kanada , die noorde van die VSA en in Europa gespeel. Dit is
bekend as ’n b
...
54. Paris By Night 84: In Atlanta - Passport to Music & Fashion (m nhc v Thi trang) l chng
tr
55. ISO 3166-2:GU ni akoole ninu ISO 3166-2 , apa opagun ISO 3166 ti International Organi-
zation for Stan
56. /Tbn Kama 2002 22
The sample input file grader.in.1 contains 10 000 such examples. The 56 languages are those
listed as ”mother tongue” in the IOI 2010 registration data. The language for each excerpt is
chosen at random from these 56 languages, and each excerpt is taken from the first paragraph
of an article chosen at random from the corresponding Wikipedia edition. Each line of the file
contains:
ˆ The two-letter ISO code for the Wikipedia language edition;
ˆ 100 numbers between 1 and 65 535, representing the first 100 symbols, in sequence, of the
first paragraph of the article;
ˆ a viewable representation (in UTF-8) of the 100 symbols that you can read in your text
editor or Firefox web browser. This viewable representation is for your convenience only,
and is not intended to be used as input for your program.

The official grader uses 10 000 different excerpts, selected in the same way from the same 56
Wikipedia editions. However, the grader assigns a different number between 0 and 55 to each
language, and a different number between 1 and 65 535 to each symbol.
Subtask 1 [30 points]
Your submission must achieve accuracy of 0.3 or better on the grading server.
Subtask 2 [up to 80 points]
Your score will be 114 α  0.3, rounded to the nearest integer, where α is the accuracy of
your submission.
Implementation Details
ˆ Implementation folder: /home/ioi2010-contestant/language/
ˆ To be implemented by contestant: lang.c or lang.cpp or lang.pas
ˆ Contestant interface: lang.h or lang.pas
ˆ Grader interface: grader.h or graderlib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas and graderlib.pas
ˆ Sample grader input: grader.in.1.
Note: Each line of input contains: a two-character language code; an excerpt represented as
100 numbers separated by spaces; the text representation of the excerpt.
ˆ Expected output for sample grader input: If the implementation calls language as specified
for each of the 10 000 examples, the sample grader will output OKalpha where alpha is the
accuracy.
ˆ Compile and run (command line): runc grader.c or runc grader.cpp or
runc grader.pas
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1005

ˆ Compile and run (gedit plugin): Control-R, while editing any implementation file.
ˆ Submit (command line): submit grader.c or submit grader.cpp or
submit grader.pas
ˆ Submit (gedit plugin): Control-J, while editing any implementation or grader file.

10.4.1 Indicaţii de rezolvare

Task Author: Gordon Cormack (CAN)

The nature of this problem is innovative within the IOI. Its purpose is to bring the field
of information retrieval under the attention. This problem is discussed in detail in the book
Information Retrieval: Implementing and Evaluating Search Engines by S. Bttcher, C.L.A. Clarke,
and G.V. Cormack (MIT Press, to appear soon). Especially see Chapter 10 on Categorization
and Filtering.
One important observation is that excerpts from the same language version of Wikipedia will
share some characteristics in a statistical sense. Because many random excerpts are offered, the
variability between excerpts from the same language play a negligible role. It has been confirmed
that the statistical resemblance between the provided test input and the official grader input is
highly predictable.
Note that because of the random re-coding of the language codes and symbol codes, there is
no opportunity to hard code any specific (personal) language knowledge into a solution.
There are many approaches possible. Rocchio’s method, which was informally described in the
task description, suffices to solve Subtask 1.
For Subtask 2, one needs to do more than simply look at symbol frequencies. Collecting
statistics on bigrams (pairs of neighboring symbols), trigrams (three consecutive symbols) will
yield higher accuracies.

10.4.2 Coduri sursă

Listing 10.4.1: lang-8124.cpp


// https://oj.uz/submission/8124 4576 ms 112584 KB

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#include<iostream>

#include <cmath>
#include <string.h>

#include "grader.h"
#include "lang.h"

using namespace std;

unsigned short pre3[56][1048576];


unsigned short num3[1048576];
int pre1[65536][56];
int num1[65536];
double now[56];
const unsigned int mod=1048575;

void excerpt(int *E)


{
int c,r,mxnum=0,i,j;
unsigned int p3[98];
double z,mx;
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1006

memset(now,0,sizeof(now));
p3[0]=(E[0]<<10ˆE[1]<<5ˆE[2])&mod;
c=num3[p3[0]];
if(c!=0)
{
for(j=0;j<56;j++)
{
z=cbrt(pre3[j][p3[0]]);
now[j]+=2*z*z/(c+1);
}
}
for(i=1;i<98;i++)
{
p3[i]=(p3[i-1]<<5ˆE[i+2])&mod;
c=num3[p3[i]];
if(c!=0)
{
for(j=0;j<56;++j)
{
z=cbrt(pre3[j][p3[i]]);
now[j]+=z*z/(c+2);
}
}
}
for(i=0;i<100;i++)
{
if(num1[E[i]]!=0)
{
for(j=0;j<56;++j)
{
z=cbrt(pre1[E[i]][j]);
now[j]+=z*z/num1[E[i]];
}
}
}
mx=now[0];
for(i=1;i<56;i++)
{
if(now[i]>mx)
{
mx=now[i];
mxnum=i;
}
}
r=language(mxnum);
for(i=0;i<98;i++)
{
num3[p3[i]]++;
pre3[r][p3[i]]++;

}
for(i=0;i<100;i++)
{
num1[E[i]]++;
pre1[E[i]][r]++;
}
}

// ------------ begin grader --------------------

#define N 100

static char lang[20], lan[100][20];


static int lnum, i,j,k,n,nl, uni[N], rightt, tot;

int language(int L) {
if (L < 0 || L >= 56) exit(92);
rightt += (L == lnum);
tot++;
return lnum;
}

int main()
{
auto t1 = clock();
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1007

freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);


freopen("language.out", "w", stdout);

auto t2 = clock();

for (n=0; 1 == scanf("%s",lang); n++)


{
for (i=0;i<nl && strcmp(lang,lan[i]);i++);
strcpy(lan[i],lang);
if (i == nl) {
nl++;
}
lnum = i;
for (i=0;i<N;i++) scanf("%d,",&uni[i]);
scanf("%*[ˆ\n]");
excerpt(uni);
}

auto t3 = clock();

printf("OK %0.2lf%%\n",100.0*rightt/tot);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

printf("OK %0.2lf%%\n",100.0*rightt/tot);
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader --------------------


/*
OK 91.72%
t2-t1 = 0.015
t3-t2 = 13.578
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.4.2: lang-12891.cpp


// https://oj.uz/submission/12891 1735 ms 32268 KB

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#include<iostream>
#include<cstring>

#include <map>
#include <algorithm>
#include <vector>
#include "lang.h"
#include "grader.h"

using namespace std;

struct WD
{
long long t1, t2;
void init()
{
t1 = t2 = 0;
}
void addwd(int p, int pos)
{
if(pos == 0) t1 = p;
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1008

else t2 |= (1ll << (16 * (4 - pos))) * p;


}
};

bool operator<(WD a, WD b)
{
return a.t1 != b.t1 ? (a.t1 > b.t1) : (a.t2 > b.t2);
}

map<WD,int> mp;
const int l = 5;

void excerpt(int *E)


{
int cnt[105] = {};
vector<WD> v;
for (int i=0; i<96; i++)
{
WD px;
px.init();
for (int j=0; j<l; j++)
{
px.addwd(E[i+j],j);
}

map<WD,int> ::iterator it = mp.lower_bound(px);

if(it != mp.end()) cnt[( *it).second] += 100;


it = mp.upper_bound(px);
if(it != mp.end()) cnt[( *it).second] += 85;
if(it != mp.begin()) cnt[( *--it).second] += 100;
v.push_back(px);
}

int mv = (int)(max_element(cnt,cnt+56) - cnt);


int t = language(mv);
for (int i=0; i<v.size(); i++)
{
mp[v[i]] = t;
}
}

// ------------ begin grader --------------------

#define N 100

static char lang[20], lan[100][20];


static int lnum, i,j,k,n,nl, uni[N], rightt, tot;

int language(int L)
{
if (L < 0 || L >= 56) exit(92);
rightt += (L == lnum);
tot++;
return lnum;
}

int main()
{
auto t1 = clock();

freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);


freopen("language.out", "w", stdout);

auto t2 = clock();

for (n=0; 1 == scanf("%s",lang); n++)


{
for (i=0;i<nl && strcmp(lang,lan[i]);i++);
strcpy(lan[i],lang);
if (i == nl) {
nl++;
}
lnum = i;
for (i=0;i<N;i++) scanf("%d,",&uni[i]);
scanf("%*[ˆ\n]");
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1009

excerpt(uni);
}

auto t3 = clock();

printf("OK %0.2lf%%\n",100.0*rightt/tot);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

printf("OK %0.2lf%%\n",100.0*rightt/tot);
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader --------------------


/*
OK 90.78%
t2-t1 = 0
t3-t2 = 5.234
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.4.3: lang-155338.cpp


// https://oj.uz/submission/155338 3788 ms 55452 KB

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#include<iostream>

#include "grader.h"
#include "lang.h"

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

const int n = 100, m = 56;

const int mod = 80021;

int cnt[57][80808];
int cnt2[57][80808];
int cnt3[57][80808];
int cnt4[57][80808];
int lann[57];

int f(int a, int b)


{
long long ret = (long long)a << 16;
ret |= b;
ret %= mod;
return ret;
}

int f(int a, int b, int c)


{
long long ret = (long long)f(a, b) << 16;
ret |= c;
ret %= mod;
return ret;
}

int f(int a, int b, int c, int d)


{
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1010

long long ret = (long long)f(a, b, c) << 16;


ret |= d;
ret %= mod;
return ret;
}

double f(int x)
{
return (double)x / (x + 1);
}

void excerpt(int *arr)


{
double mx = 0;
int idx = 0;
for(int i=0; i<m; i++)
{
double now = 0;
for(int j=0; j<n; j++)
{
now += f(cnt[i][arr[i]]);
if(j >= n-1) continue;
now += f(cnt2[i][f(arr[j], arr[j+1])]) * 100;
if(j >= n-2) continue;
now += f(cnt3[i][f(arr[j], arr[j+1], arr[j+2])]) * 150;
if(j >= n-3) continue;
now += f(cnt4[i][f(arr[j],arr[j+1],arr[j+2],arr[j+3])])*100;
}
now /= log(lann[i] + 1);
if(now > mx)
{
mx = now; idx = i;
}
}

int ans = language(idx);


lann[ans]++;
for(int i=0; i<n; i++)
{
cnt[ans][arr[i]]++;
if(i >= n-1) continue;
cnt2[ans][f(arr[i], arr[i+1])]++;
if(i >= n-2) continue;
cnt3[ans][f(arr[i], arr[i+1], arr[i+2])]++;
if(i >= n-3) continue;
cnt4[ans][f(arr[i], arr[i+1], arr[i+2], arr[i+3])]++;
}
}

// ------------ begin grader --------------------

#define N 100

static char lang[20], lan[100][20];


static int lnum, i,j,k,nn,nl, uni[N], rightt, tot;

int language(int L)
{
if (L < 0 || L >= 56) exit(92);
rightt += (L == lnum);
tot++;
return lnum;
}

int main()
{
auto t1 = clock();

freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);


freopen("language.out", "w", stdout);

auto t2 = clock();

for (nn=0; 1 == scanf("%s",lang); nn++)


{
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1011

for (i=0;i<nl && strcmp(lang,lan[i]);i++);


strcpy(lan[i],lang);
if (i == nl)
{
nl++;
}
lnum = i;
for (i=0;i<N;i++) scanf("%d,",&uni[i]);
scanf("%*[ˆ\n]");
excerpt(uni);
}

auto t3 = clock();

printf("OK %0.2lf%%\n",100.0*rightt/tot);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

printf("OK %0.2lf%%\n",100.0*rightt/tot);
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader --------------------


/*
OK 92.40%
t2-t1 = 0
t3-t2 = 19.515
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.4.4: lang-235517.cpp


// https://oj.uz/submission/235517 2196 ms 118528 KB

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#include<iostream>

#include <bits/stdc++.h>
#define FOR(i, x, y) for (int i = x; i < y; i++)
typedef long long ll;

#include "grader.h"

const int SZ = 100, LANGS = 56, MOD = 539653;

ll si[SZ], bi[SZ], tr[SZ], qu[SZ];


double cnt[LANGS];
int freq[LANGS][MOD];

inline double hyperb(int x) { return x / (x + 1.0); }

void excerpt(int *E)


{
FOR(i, 0, SZ - 3)
{
si[i] = E[i];
bi[i] = ((si[i] << 16) + E[i + 1]) % MOD;
tr[i] = ((bi[i] << 16) + E[i + 2]) % MOD;
qu[i] = ((tr[i] << 16) + E[i + 3]) % MOD;
}

int best = 0;
CAPITOLUL 10. IOI 2010 10.4. LANGUAGES 1012

double best_sim = 0;
FOR(i, 0, LANGS)
{
double sim = 0;
FOR(j, 0, SZ - 3)
{
sim += hyperb(freq[i][qu[j]]) * 138;
sim += hyperb(freq[i][tr[j]]) * 60;
sim += hyperb(freq[i][bi[j]]) * 97;
sim += hyperb(freq[i][si[j]]) * 1;
}
sim /= log(cnt[i] + 1);

if (sim > best_sim) best = i, best_sim = sim;


}

int ans = language(best);


cnt[ans]++;
FOR(i, 0, SZ - 3)
{
freq[ans][qu[i]]++;
freq[ans][tr[i]]++;
freq[ans][bi[i]]++;
freq[ans][si[i]]++;
}
}

// ------------ begin grader --------------------

#define N 100

static char lang[20], lan[100][20];


static int lnum, i,j,k,n,nl, uni[N], rightt, tot;

int language(int L)
{
if (L < 0 || L >= 56) exit(92);
rightt += (L == lnum);
tot++;
return lnum;
}

int main()
{
auto t1 = clock();

freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);


freopen("language.out", "w", stdout);

auto t2 = clock();

for (n=0; 1 == scanf("%s",lang); n++)


{
for (i=0;i<nl && strcmp(lang,lan[i]);i++);
strcpy(lan[i],lang);
if (i == nl)
{
nl++;
}
lnum = i;
for (i=0;i<N;i++) scanf("%d,",&uni[i]);
scanf("%*[ˆ\n]");
excerpt(uni);
}

auto t3 = clock();

printf("OK %0.2lf%%\n",100.0*rightt/tot);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

printf("OK %0.2lf%%\n",100.0*rightt/tot);
std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 10. IOI 2010 10.5. MEMORY 1013

std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------ end grader --------------------


/*
OK 92.51%
t2-t1 = 0
t3-t2 = 8.062
t4-t3 = 0

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


Press any key to continue.
*/

10.4.3 *Rezolvare detaliată

10.5 Memory
Problema 5 - Memory 100 de puncte

Author: Gordon Cormack (CAN)

A game called Memory is played using 50 cards. Each card has


one of the letters from A to Y (ASCII 65 to 89) printed on the
face, so that each letter appears on exactly two cards. The cards
are shuffled into some random order and dealt face down on the
table.
Jack plays the game by turning two cards face up so the letters are visible. For each of the 25
letters Jack gets a candy from his mother the first time he sees both copies of the letter on the
two face up cards. For example, the first time Jack turns over both cards that contain the letter
M , he gets a candy. Regardless of whether the letters were equal or not, Jack then turns both
cards face down again. The process is repeated until Jack receives 25 candies one for each letter.
You are to implement a procedure play that plays the game. Your implementation should
call the procedure faceup(C) which is implemented by the grader. C is a number between 1
and 50 denoting a particular card you wish to be turned face up. The card must not currently be
face up. faceup(C) returns the character that is printed on the card C.
After every second call to faceup, the grader automatically turns both cards face down again.
Your procedure play may only terminate once Jack has received all 25 candies. It is allowed
to make calls to faceup(C) even after the moment when Jack gets the last candy.
Example
The following is one possible sequence of calls your procedure play could make, with explanations.
CAPITOLUL 10. IOI 2010 10.5. MEMORY 1014

Call Returned value Explanation


faceup(1) ’B’ Card 1 contains B.
faceup(7) ’X’ Card 7 contains X. The letters are not equal.
The grader automatically turns cards 1 and 7 face down.
faceup(7) ’X’ Card 7 contains X.
faceup(15) ’O’ Card 15 contains O. The letters are not equal.
The grader automatically turns cards 7 and 15 face down.
faceup(50) ’X’ Card 50 contains X.
faceup(7) ’X’ Card 7 contains X. Jack gets his first candy.
The grader automatically turns cards 50 and 7 face down.
faceup(7) ’X’ Card 7 contains X.
faceup(50) ’X’ Card 50 contains X. Equal letters, but Jack gets no candy.
The grader automatically turns cards 7 and 50 face down.
faceup(2) ’B’ Card 2 contains B.
...
(some function calls were omitted)
...
faceup(1) ’B’ Card 1 contains B.
faceup(2) ’B’ Card 2 contains B. Jack gets his 25th candy.
Subtask 1 [50 points]
Implement any strategy that obeys the rules of the game and finishes it within the time limit.
For example, there is a simple strategy that always makes exactly 2*(49+48+...+2+1) = 2450
calls to faceup(C).
Subtask 2 [50 points]
Implement a strategy that finishes any possible game with at most 100 calls to faceup(C).
Implementation Details
ˆ Implementation folder: /home/ioi2010-contestant/memory/
ˆ To be implemented by contestant: memory.c or memory.cpp or memory.pas
ˆ Contestant interface: memory.h or memory.pas
ˆ Grader interface: grader.h or graderlib.pas
ˆ Sample grader: grader.c or grader.cpp or grader.pas and graderlib.pas
ˆ Sample grader input: grader.in.1.
Note: the input file contains one line with 50 characters denoting the letters on the cards,
in order, from 1 to 50.
ˆ Expected output for sample grader input: if your implementation is correct, the output file
will contain OK n where n is the number of calls to faceup(C).
ˆ Compile and run (command line): runc grader.c or runc grader.cpp or
runc grader.pas
ˆ Compile and run (gedit plugin): Control-R, while editing any implementation file.
ˆ Submit (command line): submit grader.c or submit grader.cpp or
submit grader.pas
ˆ Submit (gedit plugin): Control-J, while editing any implementation or grader file.

10.5.1 Indicaţii de rezolvare

Task Author: Gordon Cormack (CAN)

This was intended to be another very easy task, though slightly more difficult than the one on
Day 1 when aiming for a full score.
Turning each possible pair of cards face up in some sequence is guaranteed to obtain all 25
candies. There are 50-choose-2 = 50 * 49 / 2 = 1225 such pairs. Hence, doing 2450 card turns
(that is, calls to faceup) suffices. This can be programmed with two nested for-loops, and it
solves Subtask 1.
CAPITOLUL 10. IOI 2010 10.5. MEMORY 1015

But it does not solve Subtask 2, where no more than 100 card turns are allowed. Note that
the try-all-pairs solution does not look at what is on the cards that are turned face up. That is,
it does not make use of the values returned by faceup. By using these returned values, you can
gather information that can be used later to reduce the number of cards turned up.
In particular, taking this to an extreme, you can first turn all cards, in pairs, to discover and
record where all the letters are, without caring about turning up equal pairs. In this first round,
you might already obtain some candies by accident, but that is irrelevant. In the next round, you
know where equal pairs are and you can flip them, in sequence, to obtain all remaining candies.
The first round requires 50 turns (calls to faceup), and the second round another 50 turns.
Thus, altogether 100 times a card is turned, and thereby Subtask 2 is solved.
In the second round, you could skip equal pairs that were already identified in the first round.
However, that will not improve the worst-case performance and it will complicate the coding.
Here is a Pascal solution that can readily be generalized (the constant and type definitions
could be eliminated, but they document the relevant concepts nicely):

Listing 10.5.1: memory.pas


1 type
2 TCard = ’A’ .. ’Y’; { which letters appear on the cards }
3
4 const
5 NLetters = Ord(High(TCard)) - Ord(Low(TCard)) + 1; { number of letters }
6 NCardsPerLetter = 2; { number of cards per letter }
7 NCards = NCardsPerLetter * NLetters; { number of cards }
8 Unknown = 0; { when card index is unknown }
9
10 type
11 TIndex = 1 .. NCards; { index of card }
12
13 procedure play;
14
15 var
16 index: array [ TCard, 1 .. NCardsPerLetter ] of Unknown .. NCards;
17 { index[lt, k] = index of k-th card with letter lt }
18 lt: TCard; { traverses index }
19 k: 1 .. NCardsPerLetter; { traverses index }
20 i: TIndex; { traverses cards }
21 r: TCard; { result of faceup }
22
23 begin
24 { initialize index to Unknown }
25 for lt := Low(TCard) to High(TCard) do begin
26 for k := 1 to NCardsPerLetter do begin
27 index[lt, k] := Unknown
28 end { for k }
29 end { for lt }
30 ;
31
32 { first round: don’t care about candy; discover where all letter are }
33 for i := 1 to NCards do begin
34 r := faceup(i)
35 ; k := 1
36 ; while index[r, k] <> Unknown do k := k + 1
37 ; index[r, k] := i
38 end { for i }
39 ;
40
41 { second round: now collect all (remaining) candies }
42 for lt := Low(TCard) to High(TCard) do begin
43 for k := 1 to NCardsPerLetter do begin
44 r := faceup( index[lt, k] ) { ignore result }
45 end { for k }
46 end { for lt }
47 end;

In C, without constant and type defintions, it could be coded as follows:

Listing 10.5.2: memory.cpp

void play()
{
CAPITOLUL 10. IOI 2010 10.5. MEMORY 1016

// letters ’A’ to ’Y’ are converted to integers 0 to 24

int index[50][2]; // locations of cards; 0 = unknown


int lt, k; // traverses index
int i; // traverses cards
char r; // result of faceup

// initialize index
for (lt = 0; lt < 25; ++lt)
{
for (k = 0; k < 2; ++k)
{
index[lt][k] = 0;
}
}

// first round
for (i = 1; i <= 50; ++i)
{
r = faceup(i);
lt = (int)(r) - (int)(’A’); // int corresponding to char r
k = (index[lt][0]) ? 1 : 0;
index[lt][k] = i;
}

// second round
for (lt = 0; lt < 25; ++lt)
{
faceup( index[lt][0] ); // result ignored
faceup( index[lt][1] ); // result ignored
}

10.5.2 Coduri sursă

Listing 10.5.3: memory-206951.cpp


// https://oj.uz/submission/206951 8 ms 504 KB
#include "memory.h"
#include "grader.h"
#include <stdio.h>
#include <stdlib.h>

#include<bits/stdc++.h>

using namespace std;

void play()
{
map<char, vector<int>> mp;
for (int i = 1; i <= 50; i++)
mp[faceup(i)].push_back(i);

for (auto p : mp)


faceup(p.second[0]), faceup(p.second[1]);
}

// ------------ begin grader --------------------

static char card[51];


static int up[2], is_up[51], candy[25], candies, moves;

char faceup(int C)
{
int c0, c1;
if (C < 1 || C > 50 || is_up[C])
{
exit(92);
}
CAPITOLUL 10. IOI 2010 10.5. MEMORY 1017

is_up[C] = 1;
up[moves%2] = C;
moves++;
if (moves%2 == 0)
{
c0 = card[ up[0] ] - ’A’;
c1 = card[ up[1] ] - ’A’;
if (c0==c1 && !candy[c0])
{
candy[c0] = 1;
++candies;
}
is_up[ up[0] ] = is_up[ up[1] ] = 0;
}
return card[C];
}

void playgame()
{
int i;
for (i=1;i<=50;i++)
{
card[i] = getchar();
}
moves = candies = 0;
play();
if (candies != 25)
{
exit(91);
}
}

int main()
{
std::freopen("../tests/Subtask1-data/grader.in.99", "r", stdin);
//std::freopen("cluedo.out", "w", stdout);

playgame();
printf("OK %d\n",moves);
return 0;
}
// ------------ end grader --------------------

Listing 10.5.4: memory-217254.cpp


// https://oj.uz/submission/217254 6 ms 512 KB
#include "memory.h"
#include "grader.h"
#include <stdio.h>
#include <stdlib.h>

#include <bits/stdc++.h>

using namespace std;

vector<int> a[26];

void play()
{
for(int i=1;i <= 50;i++){ char x=faceup(i); a[x-’A’].push_back(i); }
for(int i = 0;i < 25;i++) faceup(a[i][0]),faceup(a[i][1]);
}

// ------------ begin grader --------------------

static char card[51];


static int up[2], is_up[51], candy[25], candies, moves;

char faceup(int C)
{
int c0, c1;
if (C < 1 || C > 50 || is_up[C])
{
exit(92);
}
CAPITOLUL 10. IOI 2010 10.5. MEMORY 1018

is_up[C] = 1;
up[moves%2] = C;
moves++;
if (moves%2 == 0)
{
c0 = card[ up[0] ] - ’A’;
c1 = card[ up[1] ] - ’A’;
if (c0==c1 && !candy[c0])
{
candy[c0] = 1;
++candies;
}
is_up[ up[0] ] = is_up[ up[1] ] = 0;
}
return card[C];
}

void playgame()
{
int i;
for (i=1;i<=50;i++)
{
card[i] = getchar();
}
moves = candies = 0;
play();
if (candies != 25)
{
exit(91);
}
}

int main()
{
std::freopen("../tests/Subtask1-data/grader.in.98", "r", stdin);
//std::freopen("cluedo.out", "w", stdout);

playgame();
printf("OK %d\n",moves);
return 0;
}

// ------------ end grader --------------------

Listing 10.5.5: memory-232610.cpp


// https://oj.uz/submission/232610 6 ms 436 KB
#include "memory.h"
#include "grader.h"

#include <stdio.h>
#include <stdlib.h>

#include<bits/stdc++.h>

using namespace std;

int A[51][2],B[51];
void play()
{
for (int i = 1; i <= 50; i++)
{
int x = (faceup(i) - ’A’ + 1);
A[x][B[x]++] = i;
}
for (int i = 1; i <= 25; i++)
{
faceup(A[i][0]);
faceup(A[i][1]);
}
return ;
}

// ------------ begin grader --------------------


CAPITOLUL 10. IOI 2010 10.5. MEMORY 1019

static char card[51];


static int up[2], is_up[51], candy[25], candies, moves;

char faceup(int C)
{
int c0, c1;
if (C < 1 || C > 50 || is_up[C])
{
exit(92);
}
is_up[C] = 1;
up[moves%2] = C;
moves++;
if (moves%2 == 0)
{
c0 = card[ up[0] ] - ’A’;
c1 = card[ up[1] ] - ’A’;
if (c0==c1 && !candy[c0])
{
candy[c0] = 1;
++candies;
}
is_up[ up[0] ] = is_up[ up[1] ] = 0;
}
return card[C];
}

void playgame()
{
int i;
for (i=1;i<=50;i++)
{
card[i] = getchar();
}
moves = candies = 0;
play();
if (candies != 25)
{
exit(91);
}
}

int main()
{
std::freopen("../tests/Subtask1-data/grader.in.97", "r", stdin);
//std::freopen("cluedo.out", "w", stdout);

playgame();
printf("OK %d\n",moves);
return 0;
}

// ------------ end grader --------------------

10.5.3 *Rezolvare detaliată


CAPITOLUL 10. IOI 2010 10.6. TRAFFIC CONGESTION 1020

10.6 Traffic Congestion


Problema 6 - Traffic Congestion 100 de puncte

Author: Jorge Bernadas (VEN)

Although Canada is a large country, many areas are un-


inhabited, and most of the population lives near the southern
border. The Trans-Canada Highway, completed in 1962, con-
nects the people living in this strip of land, from St. John’s in
the East to Victoria in the West, a distance of 7 821 km.
Canadians like hockey. After a hockey game, thousands of fans get in their cars and drive
home from the game, causing heavy congestion on the roads. A wealthy entrepreneur wants to
buy a hockey team and build a new hockey arena. Your task is to help him select a location for
the arena to minimize the traffic congestion after a hockey game.
The country is organized into cities connected
by a network of roads. All roads are bidirectional,
and there is exactly one route connecting any pair of
cities. A route connecting the cities c0 and ck is a se-
quence of distinct cities c0 , ..., ck such that there is a
road from ci1 to ci for each i. The new arena must
be built in one of the cities, which we will call the
arena city. After a hockey game, all of the hockey
fans travel from the arena city to their home city,
except those who already live in the arena city. The
amount of congestion on each road is proportional
to the number of hockey fans that travel along the
road. You must locate the arena city such that the
amount of congestion on the most congested road
is as small as possible. If there are several equally
good locations, you may choose any one.
You are to implement a procedure
LocateCentre(N,P,S,D).
N is a positive integer, the number of cities. The
cities are numbered from 0 to N  1. P is an array of
N positive integers; for each i, P i is the number
of hockey fans living in the city numbered i. The total number of hockey fans in all the cities will
be at most 2 000 000 000. S and D are arrays of N  1 integers each, specifying the locations
of roads. For each i, there is a road connecting the two cities whose numbers are S i and Di.
The procedure must return an integer, the number of the city that should be the arena city.
Example
As an example, consider the network of five cities in the top diagram on the right, where cities
0, 1 and 2 contain 10 hockey fans each, and cities 3 and 4 contain 20 hockey fans each. The middle
diagram shows the congestions when the new arena is in city 2, the worst congestion being 40 on
the thicker arrow. The bottom diagram shows the congestions when the new arena is in city 3,
the worst congestion being 30 on the thicker arrow. Therefore, city 3 would be a better location
for the arena than city 2. The data for this example are in grader.in.3a.
Note
We remind contestants that with the given constraints, it is possible to submit a solution that
passes Subtask 3 and fails Subtask 2. However, remember that your final score for the entire task
is determined by only one of your submissions.
Subtask 1 [25 points]
Assume that all the cities lie in a straight line from East to West, and that the roads all follow
this straight line with no branches. More specifically, assume that for all i with 0 & i & N  2,
S i i and Di i  1.
There are at most 1000 cities.
Subtask 2 [25 points]
CAPITOLUL 10. IOI 2010 10.6. TRAFFIC CONGESTION 1021

Make the same assumptions as in Subtask 1, but there are at most 1 000 000 cities.
Subtask 3 [25 points]
The assumptions from Subtask 1 may no longer be true.
There are at most 1000 cities.
Subtask 4 [25 points]
The assumptions from Subtask 1 may no longer be true.
There are at most 1 000 000 cities.
Implementation Details
ˆ Implementation folder: /home/ioi2010-contestant/traffic/
ˆ To be implemented by contestant: traffic.c or traffic.cpp or traffic.pas
ˆ Contestant interface: traffic.h or traffic.pas
ˆ Grader interface: none
ˆ Sample grader: grader.c or grader.cpp or grader.pas
ˆ Sample grader input: grader.in.1 grader.in.2
Note: The first line of the input file contains N . The following N lines contain P i for i
between 0 and N  1. The following N  1 lines contain pairs S i Di for i between 0 and
N  2.
ˆ Expected output for sample grader input: grader.expect.1 grader.expect.2 etc.
ˆ Compile and run (command line): runc grader.c or runc grader.cpp or
runc grader.pas
ˆ Compile and run (gedit plugin): Control-R, while editing any implementation file.
ˆ Submit (command line): submit grader.c or submit grader.cpp or
submit grader.pas
ˆ Submit (gedit plugin): Control-J, while editing any implementation or grader file.

10.6.1 Indicaţii de rezolvare

Task Author: Jorge Bernadas (VEN)

This was (by intention) a fairly standard task. Though, it should be mentioned that graph
problems always are a bit trickier than one might at first think because of the need to handle
specific graph encodings.
The information provided below will be expanded in the future, but for now should help in
understanding what each subtask was expecting in the form of algorithms.

ˆ Subtask 1: Quadratic works. Because of the highly regular (linear) structure of the network
graph, it is easy to try each city as location for the arena, calculate the worst congestions
and pick out the location where this worst congestion is minimal.
ˆ Subtask 2: Requires linear algorithm, but because there are only two leaves and the graph
representation is highly regular, it is easy to see that one sweep over the cities along the
roads suffices to determine the optimum location.
ˆ Subtask 3: Quadratic works, but now the general graph must be handled. Again, as in
Subtask 1, every city can be tried as arena location, the worst congestion can then be
calculated, and best location can be found.
ˆ Subtask 4: This is the full problem. A linear traversal of the graph, accumulating congestion
information appropriately, enables one to determine the optimal location of the arena in
linear time.
CAPITOLUL 10. IOI 2010 10.6. TRAFFIC CONGESTION 1022

10.6.2 Coduri sursă

Listing 10.6.1: quality-229321.cpp


// https://oj.uz/submission/229321 1353 ms 141292 KB
#include "traffic.h"
#include <stdio.h>

#include<iostream>
#include<ctime>

#pragma GCC optimize("Ofast")

#include<vector>
#include<algorithm>

using namespace std;

vector<int> g[1000005];
long long num[1000005],minv=1000000000000000000;
int n,ans;

long long DFS(int v,int p)


{
for(auto x:g[v])
if(x!=p)
num[v]+=DFS(x,v);
return num[v];
}

void DP(int v,int p)


{
for(auto x:g[v])
{
if(x==p)
continue;
long long pre=num[v];
num[v]-=num[x];
num[x]=pre;
DP(x,v);
num[x]-=num[v];
num[v]=pre;
}

long long maxv=0;


for(auto x:g[v])
maxv=max(maxv,num[x]);
if(maxv<minv)
{
minv=maxv;
ans=v;
}
}

int LocateCentre(int N,int *P,int *S,int *D)


{
int i;
n=N;
for(i=0;i<n;i++)
num[i]=P[i];
for(i=0;i<n-1;i++)
{
g[S[i]].emplace_back(D[i]);
g[D[i]].emplace_back(S[i]);
}
DFS(0,-1);
DP(0,-1);
return ans;
}

// ------------- begin grader -----------------------

static int N,P[1000000],S[1000000],D[1000000];


CAPITOLUL 10. IOI 2010 10.6. TRAFFIC CONGESTION 1023

int main()
{
auto t1 = clock();

std::freopen("../tests/Subtask4-data/grader.in.52-4", "r", stdin);


std::freopen("traffic.out", "w", stdout);

int i;
scanf("%d",&N);
for (i=0;i<N;i++) scanf("%d",&P[i]);
for (i=0;i<N-1;i++) scanf("%d%d",&S[i],&D[i]);

auto t2 = clock();

int r = LocateCentre(N,P,S,D);

auto t3 = clock();

printf("%d\n",r);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader -----------------------


/*
t2-t1 = 1.031
t3-t2 = 1.438
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.6.2: quality-232654.cpp


// https://oj.uz/submission/232654 1206 ms 156712 KB
#include "traffic.h"
#include <stdio.h>

#include<iostream>
#include<ctime>

#include<bits/stdc++.h>

using namespace std;

int dp[1000006],sum[1000001], C[1000006] , tot;


vector <int> adj[1000005];

void dfs(int v,int p)


{
dp[v] = sum[v] = 0;
int s = 0;

for (int i:adj[v])


{
if (i != p)
{
dfs (i,v);
dp[v] = max(dp[v], sum[i] + C[i]);
sum[v] += sum[i] + C[i];
}
}
dp[v] = max(dp[v],tot - C[v] - sum[v]);
}
CAPITOLUL 10. IOI 2010 10.6. TRAFFIC CONGESTION 1024

int LocateCentre(int n,int P[],int S[],int D[])


{
for (int i = 0; i < n-1; i++)
adj[S[i]].push_back(D[i]),
adj[D[i]].push_back(S[i]);
for (int i = 0; i < n; i++)
C[i] = P[i], tot += C[i];
pair<int,int> ans = {INT_MAX,-1};
dfs(0,-1);
for (int i = 0; i < n; i++)
{
ans = min(ans,{dp[i],i});
}

return ans.second;
}

// ------------- begin grader -----------------------

static int N,P[1000000],S[1000000],D[1000000];

int main()
{
auto t1 = clock();

std::freopen("../tests/Subtask4-data/grader.in.52-4", "r", stdin);


std::freopen("traffic.out", "w", stdout);

int i;
scanf("%d",&N);
for (i=0;i<N;i++) scanf("%d",&P[i]);
for (i=0;i<N-1;i++) scanf("%d%d",&S[i],&D[i]);

auto t2 = clock();

int r = LocateCentre(N,P,S,D);

auto t3 = clock();

printf("%d\n",r);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader -----------------------


/*
t2-t1 = 1.037
t3-t2 = 1.516
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.6.3: quality-236364.cpp


// https://oj.uz/submission/236364 1212 ms 156792 KB
#include "traffic.h"
#include <stdio.h>

#include<iostream>
#include<ctime>

#include <bits/stdc++.h>

#define DIM 1000010


CAPITOLUL 10. IOI 2010 10.6. TRAFFIC CONGESTION 1025

#define INF 2000000000000000000LL

using namespace std;

vector <int> L[DIM];


long long sum[DIM];
int fth[DIM];

void dfs (int nod, int tata)


{
fth[nod] = tata;
for (auto vecin : L[nod])
if (vecin != tata)
{
dfs (vecin,nod);
sum[nod] += sum[vecin];
}
}

int LocateCentre (int n, int p[], int s[], int d[])


{

for (int i=0;i<n-1;i++)


{
s[i]++, d[i]++;
L[s[i]].push_back(d[i]);
L[d[i]].push_back(s[i]);
}

long long sum_total = 0;


for (int i=1;i<=n;i++)
{
sum[i] = p[i-1];
sum_total += sum[i];
}

dfs (1,0);

long long mini = INF;


int sol = 0;
for (int i=1;i<=n;i++)
{
long long maxi = 0;
for (auto vecin : L[i])
{
if (vecin == fth[i])
continue;
maxi = max (maxi,sum[vecin]);
}

maxi = max (maxi,sum_total - sum[i]);

if (maxi < mini)


{
mini = maxi;
sol = i;
}
}

return sol - 1;

// ------------- begin grader -----------------------

static int N,P[1000000],S[1000000],D[1000000];

int main()
{
auto t1 = clock();

std::freopen("../tests/Subtask4-data/grader.in.52-4", "r", stdin);


std::freopen("traffic.out", "w", stdout);

int i;
scanf("%d",&N);
CAPITOLUL 10. IOI 2010 10.7. MAZE 1026

for (i=0;i<N;i++) scanf("%d",&P[i]);


for (i=0;i<N-1;i++) scanf("%d%d",&S[i],&D[i]);

auto t2 = clock();

int r = LocateCentre(N,P,S,D);

auto t3 = clock();

printf("%d\n",r);

auto t4 = clock();

// reset console output


freopen("CON", "w", stdout);

std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;


std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;

return 0;
}

// ------------- end grader -----------------------


/*
t2-t1 = 1.047
t3-t2 = 1.547
t4-t3 = 0

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


Press any key to continue.
*/

Listing 10.6.4: checkerQuality.cpp


#include "testlib.h"

using namespace std;

int main()
//int main(int argc, char * argv[])
{
int argc=4;

char* argv[] =
{
(char*)"checker",
(char*)"../tests/Subtask4-data/grader.in.52-4",
(char*)"../tests/Subtask4-data/grader.expect.52-4",
(char*)"traffic.out",
};

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

registerChecker("traffic", argc, argv);


compareRemainingLines();
}

10.6.3 *Rezolvare detaliată

10.7 Maze
Problema 7 - Maze 100 de puncte
Authors: Monika Steinová (CHE/SWK), Michal Foris̆ek (SWK)
CAPITOLUL 10. IOI 2010 10.7. MAZE 1027

In southern Ontario, many corn


farmers create cornstalk mazes like
the one shown. The mazes are cre-
ated in the fall, after the grain has
been harvested. There is still time
for you to help design the best maze
ever for 2010.
A field is covered with corn stalks
except for a few obstacles (trees,
buildings and the like) where corn cannot grow. The stalks, which are extremely tall, form the
walls of the maze. Pathways are created on a square grid by crushing 1m square areas of stalks.
One grid square on the edge is the entrance, and one grid square is the core of the maze.
Jack visits a corn maze every year, and has become adept at finding his way quickly from the
entrance to the core. You are designing a new maze, and your job is to determine which stalks to
crush, so as to maximize the number of squares Jack must visit.
The grader will determine which square is the entrance (the only one on the perimeter) and
which square is the core (the one that Jack must walk farthest to reach).
A map of the rectangular field is represented as text; for example, a 6m by 10m field with
eight trees might be represented as:

##X#######
###X######
####X##X##
##########
##XXXX####
##########

The symbol # represents a square with standing cornstalks, and X represents a square with
an obstacle (such as a tree) that cannot be crushed to form a pathway.
The field is transformed into a maze by crushing squares occupied by corn. One crushed square
(the entrance) must be on the edge of the field. The other crushed squares must be in the interior.
The objective is to maximize the shortest path from the entrance to the core, measured by the
number of crushed squares that Jack must pass through, including the entrance and the core. It
is possible to pass from one square to another only if both are crushed and they share an edge.
In your submission, the crushed squares should be identified by periods (.). Exactly one of the
crushed squares should be on the perimeter. For example:

#.X#######
#.#X#...##
#...X#.X.#
#.#......#
#.XXXX##.#
##########

Below, for illustration purposes only, we mark the entrance E, the core C and remainder of
the path using . The path length is 12.

#EX#######
#+#X#C+.##
#+++X#+X.#
#.#++++..#
#.XXXX##.#
##########

The folder /home/ioi2010-contestant/maze contains several text files named


field1.txt field2.txt etc. containing maps of cornfields. You are to copy them to files
CAPITOLUL 10. IOI 2010 10.7. MAZE 1028

named maze1.txt maze2.txt etc., and transform them into valid mazes by replacing some of
the # symbols by periods.
Note: the Grading Server Public Test will award 1 point per subtask for any valid solution
(regardless of the path length). The Grading Server Release Test will award the remaining points.
The total score for the task will be rounded to the nearest integer between 0 and 110.
Subtask 1 [up to 11 points]
The field described above (of size 6  10) may be found in the file field1.txt. Create a
maze for this field named maze1.txt that has a shortest path from the entrance to the core
P ©20
with length P . Your score for this subtask will be the minimum of 11 and 10 . Note that the
sample solution scores 3.98 points.
Subtask 2 [up to 11 points]
The file field2.txt represents a field of size 100  100. Create a maze for this field named
maze2.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©4000
this subtask will be the minimum of 11 and 10 .
Subtask 3 [up to 11 points]
The file field3.txt represents a field of size 100  100. Create a maze for this field named
maze3.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©4000
this subtask will be the minimum of 11 and 10 .
Subtask 4 [up to 11 points]
The file field4.txt represents a field of size 100  100. Create a maze for this field named
maze4.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©4000
this subtask will be the minimum of 11 and 10 .
Subtask 5 [up to 11 points]
The file field5.txt represents a field of size 100  100. Create a maze for this field named
maze5.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©5000
this subtask will be the minimum of 11 and 10 .
Subtask 6 [up to 11 points]
The file field6.txt represents a field of size 11  11. Create a maze for this field named
maze6.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©54
this subtask will be the minimum of 11 and 10 .
Subtask 7 [up to 11 points]
The file field7.txt represents a field of size 20  20. Create a maze for this field named
maze7.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©33
this subtask will be the minimum of 11 and 10 .
Subtask 8 [up to 11 points]
The file field8.txt represents a field of size 20  20. Create a maze for this field named
maze8.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©95
this subtask will be the minimum of 11 and 10 .
Subtask 9 [up to 11 points]
The file field9.txt represents a field of size 11  21. Create a maze for this field named
maze9.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©104
this subtask will be the minimum of 11 and 10 .
Subtask 10 [up to 11 points]
The file fieldA.txt represents a field of size 200  200. Create a maze for this field named
mazeA.txt that has a shortest path from the entrance to the core of length P . Your score for
P ©7800
this subtask will be the minimum of 11 and 10 .
Implementation Details
ˆ This is an output-only task.
ˆ Implementation folder: /home/ioi2010-contestant/maze/
ˆ To be submitted by contestant: maze1.txt maze2.txt maze3.txt maze4.txt
maze5.txt maze6.txt maze7.txt maze8.txt maze9.txt mazeA.txt.
ˆ Contestant interface: none
ˆ Grader interface: none
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1029

ˆ Sample grader: grader.c or grader.cpp or grader.pas


ˆ Sample grader input: grader.in.1 grader.in.2 etc.
Note: the implementation folder contains very simple solutions maze1.txt, maze2.txt etc..
Copy these to grader.in.1 grader.in.2 etc. for testing.
ˆ Expected output for sample grader input: if the input is a valid maze for subtask N , the
sample grader will output OK N P where P is the path length.
ˆ Compile and run (command line): runc grader.c or runc grader.cpp or
runc grader.pas
ˆ Compile and run (gedit plugin): Control-R, while editing the grader.
ˆ Submit (command line): submit maze1.txt or submit maze2.txt etc. All .txt files
in the implementation folder will be submitted, regardless of which is specified in the submit
command.
ˆ Submit (gedit plugin): Control-J, while editing any .txt file.

10.7.1 Indicaţii de rezolvare

Task Authors: Monika Steinová (CHE/SWK), Michal Foris̆ek (SWK)


This is known to be a hard problem. No general polynomial algorithm is known for this problem,
that is, no algorithm has been discovered that is guaranteed to solve each problem instance in a
time polynomial in the size of the problem (area of the corn field).
This is also the reason why this task was offered as an output-only task, where the contestants
were given 10 specific instances (corn fields) to tackle. They could do this by hand, or write
programs to analyze these mazes, and write yet other programs (potentially, a different program
for each field) to produce a long(est) path. Note that the path need not be optimal; points could
also be scored for (good) approximations.
Here are some characteristics and results by Tor Myklebust (HSC member) for the 10 instances
that the contestants had to tackle:
Characteristics Tor’s result
Instance Dimensions Obstructions Path length Score
1 10x10 8 20 10.00
2 100x100 1766 4026 10.15
3 100x100 3216 3740 8.61
4 100x100 2283 3733 8.58
5 100x100 1357 4738 8.86
6 11x11 0 54 10.00
7 20x20 210 33 10.00
8 20x20 122 95 10.00
9 11x21 10 106 10.45
10 200x200 15224 7506 9.17
Total 95.82

10.7.2 *Coduri sursă

10.7.3 *Rezolvare detaliată

10.8 Saveit
Problema 8 - Saveit 100 de puncte
Author: Mihai Pătraşcu (ROM)
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1030

The Xedef Courier Company provides air package delivery


among several cities. Some of these cities are Xedef hubs where
special processing facilities are established. Each of Xedef’s air-
craft shuttles back and forth between one pair of cities, carrying
packages in either direction as required.
To be shipped from one city to another, a package must be
transported by a sequence of hops, where each hop carries the
package between a pair of cities served by one of the aircraft.
Furthermore, the sequence must include at least one of Xedef’s
hubs.
To facilitate routing, Xedef wishes to encode the length of
the shortest sequence of hops from every city to every hub on
the shipping label of every package. (The length of the shortest
sequence leading from a hub to itself is zero.) Obviously, a
compact representation of this information is required.
You are to implement two procedures, encode(N,H,P,A,B) and decode(N,H). N is the
number of cities and H is the number of hubs. Assume that the cities are numbered from 0 to
N  1, and that the hubs are the cities with numbers between 0 and H  1. Further assume that
N & 1000 and H & 36. P is the number of pairs of cities connected by aircraft. All (unordered)
pairs of cities will be distinct. A and B are arrays of size P , such that the first pair of connected
cities is A0, B 0, the second pair is A1, B 1, and so on.
encode must compute a sequence of bits from which decode can determine the number of
hops from every city to every hub. encode will transmit the sequence of bits to the grading
server by a sequence of calls to encode_bit(b) where b is either 0 or 1. decode will receive
the sequence of bits from the grading server by making calls to decode_bit. The i-th call to
decode_bit will return the value of b from the i-th call to encode_bit(b). Note that you
must ensure that the number of times decode calls decode_bit will always be at most equal to
the number of times encode previously called encode_bit(b).
After decoding the numbers of hops, decode must call hops(h,c,d) for every hub h and
every city c (including every hub, that is, also for c h), giving the minimum number d of hops
necessary to ship a package between h and c. That is, there must be N ˜ H calls to hops(h,c,d).
The order does not matter. You are guaranteed that it is always possible to ship a package between
every hub and every city.
Note: encode and decode must communicate only through the specified interface. Shared vari-
ables, file access and network access are prohibited. In C or C++, you may declare persistent
variables to be static to retain information for encode or decode, while preventing them
from being shared. In Pascal, you may declare persistent variables in the implementation part
of your solution files.
Example
As an example, consider the diagram on the right.
It shows five cities (N=5) connected by seven aircraft
(P=7). Cities 0, 1 and 2 are hubs (H=3). One hop
is needed to ship a package between hub 0 and city 3,
whereas 2 hops are needed to ship a package between
hub 2 and city 3. The data for this example are in grader.in.1.
The entries in the following table are all d-values that decode must deliver by calling
hops(h,c,d):

Subtask 1 [25 points]


encode must make no more than 16 000 000 calls to encode_bit(b).
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1031

Subtask 2 [25 points]


encode must make no more than 360 000 calls to encode_bit(b).
Subtask 3 [25 points]
encode must make no more than 80 000 calls to encode_bit(b).
Subtask 4 [25 points]
encode must make no more than 70 000 calls to encode_bit(b).
Implementation Details
ˆ Implementation folder: /home/ioi2010-contestant/saveit/
ˆ To be implemented by contestant:
` encoder.c or encoder.cpp or encoder.pas
` decoder.c or decoder.cpp or decoder.pas

ˆ Contestant interface:
` encoder.h or encoder.pas
` decoder.h or decoder.pas

ˆ Grader interface: grader.h or graderlib.pas


ˆ Sample grader: grader.c or grader.cpp or grader.pas and graderlib.pas
ˆ Sample grader input: grader.in.1 grader.in.2 etc.
Note: The first line of each file contains N P H. The next P lines contain the pairs of cities
A0 B 0, A1 B 1, etc. The next H ˜ N lines contain the numbers of hops from each
hub to each city (including itself and all other hubs); that is, the number of hops from a hub
i to a city j is in the i ˜ N  j  1-st of these lines.
ˆ Expected output for sample grader input:
` If the implementation is correct for subtask 1, the output will contain OK 1
` If the implementation is correct for subtask 2, the output will contain OK 2
` If the implementation is correct for subtask 3, the output will contain OK 3
` If the implementation is correct for subtask 4, the output will contain OK 4

ˆ Compile and run (command line): runc grader.c or runc grader.cpp or


runc grader.pas
ˆ Compile and run (gedit plugin): Control-R, while editing any implementation file.
ˆ Submit (command line): submit grader.c or submit grader.cpp or
submit grader.pas
ˆ Submit (gedit plugin): Control-J, while editing any implementation or grader file.

10.8.1 Indicaţii de rezolvare

Task Author: Mihai Pătraşcu (ROM)

This task also is innovative for the IOI. In general, for most IOI tasks efficiency matters.
However, in this case it is not execution time or memory usage but rather communication efficiency:
how to represent some complex data in as few bits as possible, without losing information.
This difference in focus makes the tasks possibly somewhat harder to understand. Further-
more, it is technically more complicated, because the contestant has to program two independent
procedures that are inverses to each other. The communication format is not prescribed; all that
matters is that the decoder programmed by the contestant can decode the data from the encoder
that is also programmed by the contestant. The grading server then connects these two procedures
to verify that the decoder can indeed ”understand” what the encouder produced.
Two things are important. First, find a way to encode adjacency information about Xedef’s
package transportation network. Second, to transmit that information with communication effi-
ciency.
Briefly stated, the subtasks could be tackled as follows:

ˆ Subtask 1: You can send the entire adjacency matrix ”as is”; this information is naturally
expressed in terms of bits, other encodings are imaginable as well. All that the encoder and
decoder need to agree upon is the order of the bits. Since there are 1000 cities, this requires
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1032

no more than 1000*1000=1 000 000 bits. Other approaches using more bits also work in
this subtask.
ˆ Subtask 2: You can send the entire table with all hop counts directly. Since there are no
more than 1000 cities, the maximum hop count is less than 1000, and thus can be encoded
in 10 bits. The size of the table is at most 1000*36=36 . Hence, not more than 360 000 bits
are needed.
ˆ Subtask 3: One needs a new idea to improve the communication efficiency further. The crux
is to come up with the idea of considering a spanning tree; any spanning tree will do. The
distance from v1 to v2 is one of

1. distance from parent(v1) to v2


2. 1 + distance of parent(v1) to v2
3. -1 + distance of parent(v1) to v2

Then all one has to do is encode these possibilities with 2 bits each.

ˆ Subtask 4: Using two bits to record a one-of-three choice is excessive. It is possible to map 3
ternary decisions (27 choices) to 5 bits (32 possibilities). This improves the communication
further.

10.8.2 Coduri sursă

Listing 10.8.1: saveit-231998.cpp


// https://oj.uz/submission/231998

#include "grader.h"
#include "encoder.h"
#include "decoder.h"

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include<iostream>

#include <vector>
#include <cmath>
#include <cassert>
#include <queue>

using namespace std;

static int nv, ne, c;


static int curbit = 0;

static int v1[1234567], v2[1234567];


static int h[1000][1000], hcnt;
static int bits[16000000], nb;

#define FOR(i,n) for (int i = 0; i < (n); i++)

void encode_bit(int bit)


{
bits[nb++] = bit;
}

int decode_bit()
{
if (curbit >= nb)
{
exit(92);
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1033

}
return bits[curbit++];
}

void hops(int a, int b, int d)


{
if (h[a][b] != d)
{
exit(92);
}
h[a][b] = -1;
hcnt++;
}

// -------------- encoder --------------------

int const nmax = 1000;


int const kmax = 36; // = 40 ... !!!

using ll = long long;

std::vector<int> g[1 + nmax];

std::vector<int> bfs(int n, int node)


{
std::vector<int> dist(n);
std::queue<int> q;
q.push(node);
dist[node] = 1;
while(0 < q.size())
{
int node = q.front();
q.pop();
for(int h = 0; h < g[node].size(); h++)
{
int to = g[node][h];
if(dist[to] == 0)
{
dist[to] = dist[node] + 1;
q.push(to);
}
}
}
for(int i = 0; i < n; i++)
dist[i]--;
return dist;
}

std::vector<int> dist[1 + kmax];


int far[1 + nmax];

int const necessary = 35 / 5 * 8;

void push(ll number, int bits)


{
for(int i = 0; i < bits; i++)
encode_bit(0 < (number & (1LL << i)));
}

void encode(int n, int k, int m, int *v1, int *v2)


{
for(int i = 0; i < m; i++)
{
int x = v1[i];
int y = v2[i];
g[x].push_back(y);
g[y].push_back(x);
}

for(int i = 0; i < k; i++)


dist[i] = bfs(n, i);

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


{
for(int h = 0; h < g[i].size(); h++)
{
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1034

int to = g[i][h];
if(dist[0][to] + 1 == dist[0][i])
{
far[i] = to;
push(to, 10);
break;
}
}
}

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


{
ll number = 0;
int par = far[i];
for(int h = 1; h < k; h++)
number = number * 3 + (dist[h][i] - dist[h][par]) + 1;
push(number, necessary);
}

return;
}

// -------------- decoder --------------------

using ll = long long;


//int const necessary = 35 / 5 * 8;
//int const kmax = 40;
//int const nmax = 1000;

int _far[1 + nmax];

ll extract(int bits)
{
ll result = 0;
for(int i = 0; i < bits; i++)
result += decode_bit() * (1LL << i);
return result;
}

std::vector<int> _g[1 + nmax];


int cost[1 + kmax][1 + nmax];
int dp[1 + kmax][1 + nmax];

void mark(int node, int era)


{
for(int h = 0; h < _g[node].size(); h++)
{
int to = _g[node][h];
dp[era][to] = dp[era][node] + cost[era][to];
mark(to, era);
}
}

void decode(int n, int k)


{
for(int i = 1;i < n; i++)
{
_far[i] = extract(10);
_g[_far[i]].push_back(i);
}

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


{
ll number = extract(necessary);
cost[0][i] = 1;
for(int h = k - 1; 0 < h; h--)
{
cost[h][i] = number % 3 - 1;
number /= 3;
}
}

for(int h = 0; h < k; h++)


{
mark(0, h);
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1035

for(int i = 0; i < n; i++)


hops(h, i, dp[h][i] - dp[h][h]);
}
}

// ----------------- begin grader ------------------------

/* and here is the driver */

int main(int argc, char **argv)


{
std::freopen("../tests/Subtask1-data/grader.in.25a", "r", stdin);

//std::freopen("saveit.out", "w", stdout);

assert(3 == scanf("%i%i%i", &nv, &ne, &c));

FOR(i,ne)
assert(2 == scanf("%i%i", v1+i, v2+i));

for (int i=0;i<c;i++)


for (int j=0;j<nv;j++)
scanf("%d",&h[i][j]);

encode(nv, c, ne, v1, v2);

decode(nv, c);

if (hcnt != c*nv)
{
exit(92);
}

printf("%s 1\n",nb<=16000000?"OK":"NO");
printf("%s 2\n",nb<=360000?"OK":"NO");
printf("%s 3\n",nb<=80000?"OK":"NO");
printf("%s 4\n",nb<=70000?"OK":"NO");
}

// ----------------- end grader ------------------------


/*
OK 1
OK 2
OK 3
OK 4

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


Press any key to continue.
*/

Listing 10.8.2: saveit-224546.cpp


// https://oj.uz/submission/224546

#include "grader.h"
#include "encoder.h"
#include "decoder.h"

#include<iostream>

#include <bits/stdc++.h>

using namespace std;

static int nv, ne, c;


static int curbit = 0;

static int v1[1234567], v2[1234567];


static int h[1000][1000], hcnt;
static int bits[16000000], nb;

#define FOR(i,n) for (int i = 0; i < (n); i++)

void encode_bit(int bit)


{
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1036

bits[nb++] = bit;
}

int decode_bit()
{
if (curbit >= nb)
{
exit(92);
}
return bits[curbit++];
}

void hops(int a, int b, int d)


{
if (h[a][b] != d)
{
exit(92);
}
h[a][b] = -1;
hcnt++;
}

// -------------- encoder --------------------

int dst[44][1010];
vector<int> g[1010];
int par[1010];
int vis[1010];

void dfs(int v = 1)
{
vis[v] = 1;
for(auto i : g[v]) if(!vis[i])
{
par[i] = v; dfs(i);
}
}

void bfs(int st)


{
dst[st][st] = 0;
queue<int> q; q.push(st);
while(q.size())
{
int now = q.front(); q.pop();
for(auto nxt : g[now]) if(dst[st][nxt] == -1)
{
dst[st][nxt] = dst[st][now] + 1;
q.push(nxt);
}
}
}

void encode(int N, int H, int M, int *v1, int *v2)


{
int n = N, m = M, h = H;
for(int i=0; i<m; i++)
{
int s = v1[i] + 1, e = v2[i] + 1;
g[s].push_back(e); g[e].push_back(s);
}

dfs();

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


{
for(int k=0; k<10; k++) encode_bit(!!(par[i] & (1 << k)));
}

memset(dst, -1, sizeof dst);


for(int i=1; i<=h; i++) bfs(i);

for(int i=1; i<=h; i++) for(int j=2; j<=n; j+=5)


{
int t = 0;
for(int k=0; k<5; k++)
CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1037

{
int now = dst[i][j+k] - dst[i][par[j+k]] + 1;
if(j + k > n) now = 0;
t = t * 3 + now;
}

for(int k=0; k<8; k++)


encode_bit(!!(t & (1 << k)));
}
}

// -------------- decoder --------------------

/*
dst[i][j] - dst[i][par[j]]
1 : 0
-1 : 11
0 : 10
*/

int p[1010];
int mm[44][1010];
int chk[1010];
int ans[44][1010];

void go(int hh, int v)


{
if(v == 1) return;
if(!chk[p[v]]) go(hh, p[v]);
ans[hh][v] = ans[hh][p[v]] + mm[hh][v];
chk[v] = 1;
}

void decode(int N, int H)


{
int n = N, h = H;
for(int i=2; i<=n; i++)
{
for(int j=0; j<10; j++) p[i] += (decode_bit() << j);
}

for(int i=1; i<=h; i++) for(int j=2; j<=n; j+=5)


{
int ret = 0;
for(int k=0; k<8; k++)
ret += (decode_bit() << k);
for(int k=4; k>=0; k--)
{
mm[i][j+k] = ret % 3 - 1;
ret /= 3;
}
}

for(int i=1; i<=h; i++)


{
memset(chk, 0, sizeof chk);
chk[1] = 1;
for(int j=2; j<=n; j++) go(i, j);
}

for(int i=1; i<=h; i++)


for(int j=1; j<=n; j++)
hops(i-1, j-1, ans[i][j] - ans[i][i]);
}

// ----------------- begin grader ------------------------

/* and here is the driver */

int main(int argc, char **argv)


{
std::freopen("../tests/Subtask1-data/grader.in.25a", "r", stdin);

//std::freopen("saveit.out", "w", stdout);

assert(3 == scanf("%i%i%i", &nv, &ne, &c));


CAPITOLUL 10. IOI 2010 10.8. SAVEIT 1038

FOR(i,ne)
assert(2 == scanf("%i%i", v1+i, v2+i));

for (int i=0;i<c;i++)


for (int j=0;j<nv;j++)
scanf("%d",&h[i][j]);

encode(nv, c, ne, v1, v2);

decode(nv, c);

if (hcnt != c*nv)
{
exit(92);
}

printf("%s 1\n",nb<=16000000?"OK":"NO");
printf("%s 2\n",nb<=360000?"OK":"NO");
printf("%s 3\n",nb<=80000?"OK":"NO");
printf("%s 4\n",nb<=70000?"OK":"NO");
}

// ----------------- end grader ------------------------


/*
OK 1
OK 2
OK 3
OK 4

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


Press any key to continue.
*/

10.8.3 *Rezolvare detaliată


1039
Appendix A

”Instalare” C++

Ca să putem ”lucra” cu C++ avem nevoie de

ˆ un compilator pentru C++, şi

ˆ un IDE (Integrated Development Enviroment) pentru C++.

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

1040
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1041

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


43
t IDE-ul Code::Blocks ,
t compilatorul pentru C: gcc.exe,
44
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 A.2: CodeBlocks & C++ Reference

Figura A.3: Ce conţine C:¯ OJI ¯

A.1.1 Code::Blocks
Pentru versiuni mai noi, de Code::Blocks şi compilatoare, se poate accesa site-ul
http://www.codeblocks.org/downloads/binaries
de unde se poate descărca, de exemplu, codeblocks-20.03mingw-setup.exe.
Versiuni mai vechi, dar foarte bune, se pot descărca de la adresa
43
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
44
g++.exe este compilatorul de C++, programul care va verifica dacă instrucţiunile noastre sunt ok sau nu ...

şi care ne va supăra mereu cu erorile pe care ni le arată ... ... Este “o mică artă” să ı̂nţelegem mesajele de
eroare pe care le vedem pe ecran şi să fim ı̂n stare să “depanăm” programele noastre!
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1042

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

A.1.2 Folder de lucru

Figura A.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 A. ”INSTALARE” C++ A.1. KIT OJI 2017 1043

Figura A.5: New -¿ Text document

Figura A.6: Schimbare nume fişier şi nume extensie fişier


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1044

Figura A.7: Confirmare schimbare extensie ı̂n .cpp

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

A.1.3 Utilizare Code::Blocks

Figura A.9: Pregătit pentru a scrie cod de program C++ ı̂n Code::Blocks
APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1045

Figura A.10: Primul cod de program C++ ı̂n Code::Blocks

Figura A.11: Build - compilare


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1046

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

Figura A.13: Run - execuţie


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1047

Figura A.14: Executat corect: a făcut “nimic”

A.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 A.15: Settings  % Compiler


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1048

Figura A.16: Toolchain executables

Figura A.17: Unde sunt acele programe


APPENDIX A. ”INSTALARE” C++ A.1. KIT OJI 2017 1049

A.1.5 Multe surse ı̂n Code Blocks


Settings Environment: pe calculatorul meu setările sunt:

Figura A.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 A.19: Multe surse in Code Blocks - exemple


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1050

A.2 winlibs
A.2.1 GCC şi MinGW-w64 pentru Windows
Se descarcă de la
http://winlibs.com/#download-release
unul dintre fişierele:

ˆ 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 A.20: mingw64 pe D:

A.2.2 PATH
Trebuie pusă ı̂n PATH calea pentru D:\mingw64\bin\. În “Type here to search” scrieţi: path
şi apoi click pe “Edit the system environment variables”, ca ı̂n figura următoare:
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1051

Figura A.21: search path

Apare fereastra “System properties”:

Figura A.22: System properties –¿ Advanced

Fereastra este poziţionată pe tab-ul “Advanced”. Se selectează “Environment Variables”.


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1052

Figura A.23: Environment Variables

Se selecteaza “Path” şi click pe “Edit”. Apare fereastra “Edit Environment Variables”

Figura A.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 A. ”INSTALARE” C++ A.2. WINLIBS 1053

Figura A.25: Calea şi versiunea pentru gcc

Dacă totul este OK atunci se trece la instalarea IDE-ului preferat (Integrated Development
45
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."

A.2.3 CodeBlocks
CodeBlocks se poate descărca de la http://www.codeblocks.org/downloads/26. Sunt
mai multe variante dar eu am descărcat numai codeblocks-20.03-setup.exe care are 35.7 MB.
La instalare am lăsat totul implicit, deci ... Next, Next, ..., Next până la sfârşit!
La prima lansare ı̂n execuţie este necesară setarea locaţiei compilatorului de C++ (gcc-ul pe
care tocmai l-am instalat!).
45
https://en.wikipedia.org/wiki/Integrated_development_environment
https://ro.wikipedia.org/wiki/Mediu_de_dezvoltare
APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1054

Figura A.26: Settings –¿ Compiler

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


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1055

Figura A.28: New –¿ Text Document

Figura A.29: New text Document.txt

Figura A.30: Schimbare nume şi extensie


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1056

Figura A.31: Moore apps

Figura A.32: Look for another app


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1057

Figura A.33: Cale pentru codeblocks.exe

Figura A.34: Selectare codeblocks.exe


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1058

Figura A.35: Editare test01.cpp

Figura A.36: Compilare test01


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1059

Figura A.37: Mesaje după compilare

Figura A.38: Execuţie test01


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1060

Figura A.39: Rezultat execuţie test01

Figura A.40: Fişiere apărute după compilare!

Figura A.41: Creare test02.cpp gol! + ¡dublu click¿ sau ¡Enter¿


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1061

Figura A.42: Lista programelor de utilizat

Figura A.43: Selectare Code::Blocks IDE pentru fişierele .cpp

Figura A.44: Editare+Compilare+Execuţie pentru test02


APPENDIX A. ”INSTALARE” C++ A.2. WINLIBS 1062

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


Appendix B

Exponenţiere rapidă

B.1 Analogie baza 2 cu baza 10

Figura B.1: Analogie B2 cu B10

În figura B.1 este considerat numărul nr 234 care ı̂n baza 2 se scrie sub forma 11101010 (celula
C10).
Pe R3 (rândul 3) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 10: se ”şterge” ulima cifră (care este egală cu nr%10 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 10).
Pe R10 (rândul 10) este arătat ce se ı̂ntâmplă cu nr atunci când se fac ı̂mpărţiri succesive prin
baza 2: se ”şterge” ulima cifră (care este egală cu nr%2 adică ultima cifră este de fapt restul
ı̂mpărţirii lui nr la baza 2, la fel cum se ı̂ntâmplă ı̂n baza 10).
Observaţia 1. Cifrele se obţin ı̂n ordine inversă. Prima cifră obţinută ca rest este de fapt ultima
cifră din număr (vezi R4 şi R11).
Pe R6 şi R16 sunt precizate puterile bazei.
Valoarea numărului nr este suma produselor dintre cifre şi puterile corespunzătoare:
ˆ ı̂n baza 10: 4*1 + 3*10 + 2*100 = 234 (R3 şi R6)
ˆ ı̂n baza 2: 0*1 + 1*2 + 0*4 + 1*8 + 0*16 + 1*32 + 1*64 + 1*128 = 234 (R13 şi R16)

1063
APPENDIX B. EXPONENŢIERE RAPIDĂ B.2. NOTAŢII, RELAŢII ŞI FORMULE 1064

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

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

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

Listing B.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 2. În acest cod actualizarea lui p se face după actualizările pentru a şi n.
Observaţia 3. În codul următor actualizarea lui p se face ı̂naintea actualizărilor pentru a şi n,
corespunzător relaţiilor (B.4.2).
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 1066

Listing B.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 (B.4.2)
„
„ pk1 ˜ ak1 , k ' 0, dacă ek 1; ak1
1
„
„ ak1 modifică produsul pk1 
„
„ k w
p
„ k ' 0, dacă ek 0; ak1
0
„
„ pk1 , 1 nu modifică produsul pk1 
„
„
„actualizări k ' 0:
„
„
„
„
„a ak1 , k ' 0;
2
„
„
„
„
k
„nk nk1 ©2 k ' 0; şi nk1 j 0 desigur ! 
„
€
APPENDIX B. EXPONENŢIERE RAPIDĂ B.4. CODUL 1067

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

Listing B.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 5. Produsul poate deveni foarte mare şi din cauza asta se cere rezultatul modulo un
număr prim. Codul următor arată acest lucru:

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

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

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

Iar aceasta este metoda ”rapidă”:

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

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

Asta este tot!


Deci, secvenţa de cod este:

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

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

46
Este o glumă!
Index

¯, 772 bucket sort, 997, 998


::, 141, 350, 818
::N = n;, 449, 574 calcpt, 976
::n = n;, 442, 455, 666 calloc, 706
[&](){}, 328 cartesian tree, 221
[&](...){...}, 446 ceil, 404
#include ¡bits/stdc++.h¿, 628 cerr, 321, 350
ˆ, 368 char* argv[], 248
typeof, 350 checker, 248
readInt(), 600, 625 chrono, 788
\, 350 count(), 788
0x3f3f3f3f, 666 now(), 788
1LL ¡¡ 15, 328 steady clock, 788
1e18, 442 time since epoch(), 788
cin.tie, 246
binary search, 547 CINOR, vi
class, 622
accumulate, 404
clear(), 336, 668
adun, 976
comp, 976
and, 609
complete graph, 715
assign, 141, 218, 476
connected subgraph, 20
auto, 328, 350, 670
const, 246, 264
auto&, 612, 668
constexpr, 218
backtracking, 715 convex function, 546
backward edges, 20 Convex Hull Trick optimization, 546
balanced binary tree, 179 copy, 609
balanced binary trees, 783 cut vertex, 20
begin(), 735 cycle, 20
BFS, 863
BFS tree, 202 decode, 976
BigNum, 971 define, 218
binary range tree, 893 degree
binary search, 202, 618, 644, 798, 930, 951, out-degree, 413
997 depth-first search, 674
binary tree, 704 depth-first-search, 221
bipartite graph, 716 deque, 446
maximum matching, 716 devide and conquer, 319
minimum vertex cover, 716 DFS, 20
bit mask, 412 dfs, 164, 246, 290, 716
bitset, 368, 398 Dijkstra, 84
bool, 321, 328 Dilworth’s theorem, 64
bool operator, 877 divide and conquer, 501
bool operator ¡, 405, 417, 423 Divide and Conquer optimization, 546
breadth first search, 375 divide-and-conquer, 919
bridge, 674 divide-and-conquer approach, 738
brute force, 375, 618, 997 doubling, 154
bsearch, 874 dynamic programming, 41, 377, 398, 412,
BST, 84 470, 545

1071
INDEX INDEX 1072

empty(), 246, 628, 668 next permutation, 609


encode, 976
end(), 735 operator, 271
erase, 246, 424, 735 pair, 246, 290, 339, 368, 625, 706
Euler cycle, 413 path, 20
Euler-Tour, 154 permutations, 412
post-order, 221
Fenwick tree, 3, 41
postorder, 939
fill, 164, 218
power(...), 733
find, 326
pre-processing, 545
fixed, 350
precompute, 951
floor, 404
preprocess, 42
Floyd-Warshall, 863
priority queue, 617, 798, 882, 920
front(), 246
priority queue, 946
function, 745
lambda, 745 queue, 218, 246, 368, 385

greedy, 3, 398, 470, 617, 950 rand(), 328, 336


Greedy method, 716 Range Minimum Queries, 220
guess-and-verify algorithm, 930 range tree, 997
rbegin(), 246
Hall theorem, 618 resize, 218, 246, 321, 670, 743
heap, 940, 997 unique, 743
reverse, 476
I.d.k.: reverse(...), 474
I don’t know who the author is., i, v RMQ, 631
in-degree, 413 rmq, 903
independent set, 716
complement, 716 scad, 976
inline, 246, 264, 271, 328, 449 segment tree, 3, 130, 154, 220, 617, 618,
insert, 424 631, 704, 737, 783, 815
int64 t, 420, 424 SegTree, 708
iota, 164, 449 selection sort, 643
set, 246, 326, 341, 368, 423, 609, 631, 951
Knuth’s optimization, 545 extra factor, 951
Konig’s theorem, 716 setprecision, 350
setvbuf, 872, 890
L, 202 IOFBF, 872, 890
lazy propagation, 221 size(), 668, 735
segment tree, 130 sizeof, 267, 336, 628
lazy-update technique, 704 sort, 164, 398, 449, 545
lazy propagation, 237 [&](...){...}, 449, 462
lca, 359 lambda, 164
Li Chao tree, 237 spanning tree, 20
Liceul Militar Dimitrie Cantemir, vi spanning trees, 344
linear interpolation, 547 srand(time(NULL));, 328
linked list, 674 stack, 41, 221, 546, 625
linked-list, 84 static, 328
lower envelope, 546 static const, 246
lower bound, 622, 628, 743 STL maps, 41
lowest-common-ancestor, 221 struct, 164, 246, 264, 271, 405, 423, 609, 628
edges, 164
malloc, 625, 872, 890 graph, 164
map, 331, 339, 419, 668, 730 swap, 449, 668
max element, 321, 733 sweep line algorithm, 154
memoization, 377 sync with stdio, 246
memset, 267, 336, 373, 420, 666, 818
merge, 221, 264, 271 Tarjan’s algorithm, 364
min element, 733 template, 246, 290
minimum spanning tree, 413 testlib.h, 248, 480, 503
mt19937, 788 trage cu tunul, iv
mult, 976 travelling salesman problem, 413
INDEX INDEX 1073

trie, 852 unsigned, 381


typedef, 246, 267, 290 upper bound, 339, 628
using, 218
unique, 424, 670, 733, 735
Universitatea Ovidius, vi
unordered map, 350 vertex cover, 716
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.

1074
BIBLIOGRAFIE BIBLIOGRAFIE 1075

[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 1076

[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

Adam Karczmarz, 616 Jonathan Mosheiff, 672


Aleksandar Ilić, 260, 861 Jorge Bernadas, 1020, 1021
Alireza Farhadi, 18
Amaury Pouly, 752 Kazuhiro Hosaka, 766
Ammar Fathin Sabili, 113 Kento Nikaido, 411
Andreja Ilić, 861
Arthur Charguéraud, 752 Luke Harrison, 891

Bang Ye Wu, 659 Mansur Kutybayev, 629


Bartosz Tarnawski, 701 Martin Fixman, 917
Bruce Merry, 850 Michal Foris̆ek, 238, 467, 722, 824, 828,
1026, 1029
Chethiya Abeysinghe, 542 Mihai Pătraşcu, 937, 948, 1029, 1031
Christian Kauth, 928 Mikhail Pyaderkin, 128
Christopher Chen, 996, 997 Mohammad Roghani, 151
Constantin Tudor, vi Monika Steinová, 238, 592, 780, 1026, 1029

Daniel Graf, 373 Nir Lavee, 672


Danylo Mysak, 1, 74 Normunds Vilcins, 905

Eryk Kopczynski, 602 Peyman Jabbarzade, 39

Gheorghe Dodescu, vi Richard Královic̆, 877


Gleb Evstropov, 425 Richard Peng, 811
Gordon Cormack, 977, 978, 983, 1003, 1005, Riku Kawasaki, 82, 218
1013, 1014
Saeed Seddighin, 18, 287, 342
Hamed Valizadeh, 317 Shi-Chun Tsai, 396, 397, 498
Helia Ziaei, 151 Shogo Murai, 199
Sun-Yuan Hsieh, 713
Ion Văduva, vi
Tomasz Idziaszek, 62, 175
Jakub Lacki, 62
Jakub Lacki, Poland, 735 Vytautas Gruslys, 680, 795
Jittat Fakcharoenphol, 960
John Dethridge, 750 Weidong Hu, 641

1077
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