Sunteți pe pagina 1din 1212

PROBLEME DE INFORMATIC€

date la olimpiade

în

2020
2019 2018 2017 2016 2015
2014 2013 2012 2011 2010

la I.O.I.

... draft (ciorn ) ...


*** Nobody is perfect ***

Ph.D. Adrian R bâea

9 decembrie 2020
Dedicaµie

I would like to dedicate this book ...

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

That is because ...

3
When I Die Nobody Will Remember Me

1
I Dedicate This Book to Myself  By Carol Lynne
2
To be, or not to be ..., Hamlet, Act III, Scene I, William Shakespeare, 1564-1616
3
https://www.youtube.com/watch?v=eMtcDkSh7fU

ii
Prefaµ 
Stilul acestor c rµi este ... ca ³i cum a³ vorbi cu nepoµii mei (³i chiar cu mine însumi!) ... încercând
împreun  s  g sim o rezolvare cât mai bun  pentru o problem  dat  la olimpiad .
Ideea, de a scrie aceste culegeri de probleme date la olimpiadele de informatic , a ap rut acum
câµiva ani când am întrebat un student (care nu reu³ea s  rezolve ni³te probleme foarte simple):
Ce te faci dac  un elev, care ³tie c  e³ti student ³i c  studiezi ³i informatic , te roag , din când în
când, s -l ajuµi s  rezolve câte o problem  de informatic  dat  la gimnaziu la olimpiad , sau pur
³i simplu ca tem  de cas , ³i tu, aproape de ecare dat , nu îl poµi ajuta? Eu cred c  nu este prea
bine ³i poate c  ... te faci ... de râs! Pe vremea mea (!), când eram elev de gimnaziu, un student
era ca un fel de ... zeu! Cu trecerea anilor am înµeles c  nu este chiar a³a! “i înc  ceva: nu am
reu³it s  înµeleg de ce, atunci când cineva nu poate s  rezolve corect o problem  de informatic 
de clasa a 6-a, dat  la olimpiada de informatic  sau ca tem  de cas , folose³te aceast  scuz : eu
nu am f cut informatic  în liceu! ³i acest cineva este zeul sau zeiµa despre care vorbeam!.
4
Sunt convins c  este important s  studiem cu atenµie cât mai multe probleme rezolvate! Cred
cred c  sunt utile ³i primele versiuni în care sunt prezentate chiar ³i numai enunµurile ³i indicaµiile
"ociale" de rezolvare. Acestea se g sesc în multe locuri; aici încerc s  le pun pe toate la un loc !
Limbajul de programare se alege în funcµie de problema pe care o avem de rezolvat. Cu ni³te
ani în urm  alegerea era mai simpl : dac  era o problem  de calcul se alegea Fortran iar dac  era
o problem  de prelucrarea masiv  a datelor atunci se alegea Cobol. Acum alegerea este ceva mai
5 6
dicil ! :-) Vezi, de exemplu, IOI2020 ³i IOI2019 , IOI2015 .
Cred c , de cele mai multe ori, este foarte greu s  gândim "simplu" ³i s  nu "ne complic m"
atunci când caut m o rezolvare pentru o problem  dat  la olimpiad . Acesta este motivul pentru
care vom analiza cu foarte mare atenµie atât exemplele date în enunµurile problemelor cât ³i
"restricµiile" care apar acolo (ele sigur "ascund" ceva interesant din punct de vedere al algoritmului
7
de rezolvare!) .
Am început câteva c rµi (pentru clasele de liceu) cu mai mulµi ani în urm , pentru perioada
2000-2007 ([29] - [33]), cu anii în ordine cresc toare!). A urmat o pauz  de câµiva ani (destul de
mulµi!). Am observat c  acele cursuri s-au împr ³tiat un pic pe net ([48] - [56])! Încerc acum
s  ajung acolo unde am r mas ... plecând mereu din prezent ... pân  când nu va mai  posibil ...
a³a c , de aceast  dat , anii sunt în ordine ... descresc toare! :-)
Codurile surs  sunt cele ociale (publicate pe site-urile olimpiadelor) sau publicate pe alte
site-uri (dac  mi s-a p rut c  sunt utile ³i se poate înv µa câte ceva din ele).
Pentru liceu perioada acoperit  este de azi (pân  când va exista acest azi pentru mine!)
pân  în anul 2000 (aveam deja perioada 2000-2007!).
Pentru gimnaziu perioada acoperit  este de azi pân  în anul 2010 (nu am prea mult timp
disponibil ³i, oricum, calculatoarele folosite la olimpiade înainte de 2010 erau ceva mai 'slabe' ³i
8
... restricµiile de memorie, din enunµurile problemelor, par 'ciudate' acum!). “i indc  a venit
vorba despre calculatoare mai  slabe sau mai  puternice: laptopul meu¯t u este puµin mai  slab
decât cel mai puternic calculator din lume în 1985 dar ³i ... un pic mai  puternic decât cel mai
9
puternic calculator din lume în 1983. (armaµia este valabil  acum, în 2020).
4
Se poate observa din Coduri surs  c  orice problem  are numeroase soluµii, atât ca algoritmi de rezolvare
cât ³i ca stil de programare! Studiind aceste coduri ... avem ce înv µa ... de³i uneori pare c  'se trage cu tunul' ...
5
IOI2019 ³i IOI2020 au a permis utilizarea limbajelor de programare C++ ³i Java
6
IOI2015 a permis utilizarea limbajelor de programare C++, Java, Pascal, Python ³i Rubi (...)
7
8
Vezi cele 5 secunde pentru Timp maxim de executare/test din problema avârcolaci - ONI2014 clasa a 11-a
Când eram eu elev/student un calculator obi³nuit executa în jur de 1.000.000 de operaµii pe secund , acum
execut  1.000.000.000 de operaµii pe secund , iar mai târziu ... cine stie ce va mai ?!
9
https://en.wikipedia.org/wiki/List_of_fastest_computers

iii
În perioada 2017-2020 cele mai puternice calculatoare din lume au fost: în noiembrie 2017 în
China, în noiembrie 2019 în SUA ³i ... în iunie 2020 în Japonia (Fugaku: 415 petaops, adic 
15 10
415 ˜ 10 operaµii pe secund , adic  ... 415 milioane de ... miliarde de ... operaµii pe secund ).
11
O mic  observaµie: în 2017 a fost prima ediµie a olimpiadei EJOI în Bulgaria ³i ... tot în
12
Bulgaria a fost ³i prima ediµie a olimpiadei IOI în 1989.
Dar ... prima ediµie a olimpiadei IMO (International Mathematical Olympiad) a fost în
13
România în 1959. Tot în România s-au µinut ediµiile din anii 1960, 1969, 1978, 1999 ³i 2018.
Revenind la ...  culegerile noastre ... mai departe, probabil, va urma completarea unor
informaµii în Rezolv ri detaliate ... pentru unele probleme numai (tot din cauza lipsei timpului
necesar pentru toate!). Prioritate vor avea problemele de gimnaziu (nu pentru c  sunt mai 'u³oare'
ci pentru c  ... elevii de liceu se descurc  ³i singuri!). Totu³i, vor  prezentate ³i Rezolv ri
detaliate ale problemelor de liceu (pe care le-am considerat în mod subiectiv!) utile.

Îmi aduc aminte c  exista o interesant  vorb  de duh printre programatorii din generaµia mea:
nu se trage cu tunul într-o musc  . Sensul este: nu se scrie un cod complicat dac  se poate
scrie un cod simplu ³i clar! Asta încerc eu în Rezolv ri detaliate .
Vom încerca, împreun , ³i câteva probleme de ... IOI ... dar asta este o treab  ... nu prea
u³oar ! Cred totu³i c  este mai bine s  prezint numai enunµuri ale problemelor date la IOI în
ultimii câµiva ani! (asta a³a, ca s  vedem cum sunt problemele la acest nivel!). Cei care ajung
acolo sau vor s  ajung  acolo (la IOI) nu au nevoie de ajutorul meu! Se descurc  singuri! La
Indicaµii de rezolvare voi prezenta numai ... numele algoritmilor clasici folosiµi în rezolvare.

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


e de folos, a³a cum cred c  sunt [1] - [28], [34] - [47], [57] - [82], ... ³i multe alte c rµi ³i site-uri!.
O alt  mic  observaµie: ce am strâns ³i am scris în aceste c rµi se adreseaz  celor interesaµi de
aceste teme! Nu cârcota³ilor! Sunt evidente sursele de pe net (³i locurile în care au fost folosite).
Nu sunt necesare preciz ri suplimentare!
“i un ultim gând: criticile ³i sfaturile sunt utile dac  au valoare! Dac  sunt numai a³a ...
cum critic  lumea la un meci de fotbal ... sau cum, pe banc  în parc, î³i d  cu p rerea despre
rezolvarea problemelor economice ale µ rii ... atunci ... !!!

"I'm only responsible for what I say,


14
not for what you understand."

Adrese interesante (rezultatele elevilor români):


https://stats.ioinformatics.org/halloffame/
https://stats.ioinformatics.org/tasks/
http://stats.ioinformatics.org/results/ROU

Adresele acestor cursuri:


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

Adrese utile (programe ³colare):


http://www.ise.ro/wp-content/uploads/2017/01/Informatica-si-TIC.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20IX-a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20X_a.pdf
http://programe.ise.ro/Portals/1/Curriculum/Progr_Lic/TH/Informatica_teoretic_vocatio
nal_intensiv_clasa%20a%20XI-a.pdf

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


9 decembrie 2020

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

Bistriµa, 9 decembrie 2020

Adrian R.

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

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

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


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

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


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

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


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

Locuri de munc : (1979-2018):


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

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

vi
Cuprins

Prefaµ  iii
Cuprins vii
Lista gurilor xiii
Lista tabelelor xiv
Lista programelor xv
1 IOI 2020 1
1.1 plants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.2 supertrees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
1.3 tickets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
1.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
1.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
1.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.4 biscuits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
1.5 mushrooms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
1.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
1.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
1.6 stations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
1.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
1.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
1.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

2 IOI 2019 142


2.1 Arranging Shoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
2.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
2.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
2.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
2.2 Split the Attractions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
2.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
2.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
2.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
2.3 Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
2.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
2.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
2.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
2.4 Broken Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

vii
2.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
2.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
2.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
2.5 Vision Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
2.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
2.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
2.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
2.6 Sky Walking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
2.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
2.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
2.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253

3 IOI 2018 254


3.1 Combo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
3.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
3.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
3.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
3.2 Seats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
3.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
3.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
3.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
3.3 Werewolf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
3.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
3.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
3.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
3.4 Mechanical Doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
3.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
3.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
3.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
3.5 Highway Tolls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
3.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
3.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
3.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
3.6 Meetings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
3.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
3.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
3.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378

4 IOI 2017 379


4.1 Nowruz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
4.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
4.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
4.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
4.2 Wiring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
4.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
4.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
4.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
4.3 Toy Train . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
4.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
4.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
4.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
4.4 Big Prize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
4.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
4.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
4.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
4.5 Simurgh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
4.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
4.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
4.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514
4.6 Ancient Books . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514
4.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516
4.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
4.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536

5 IOI 2016 537


5.1 Detecting molecules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537
5.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
5.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
5.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
5.2 Roller Coaster Railroad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
5.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
5.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
5.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
5.3 Shortcut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
5.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
5.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
5.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
5.4 Paint By Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
5.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
5.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
5.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639
5.5 Unscrambling a Messy Bug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639
5.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
5.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
5.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
5.6 Aliens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
5.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
5.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
5.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732

6 IOI 2015 733


6.1 Boxes with souvenirs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
6.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734
6.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 736
6.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
6.2 Scales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
6.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
6.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
6.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
6.3 Teams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
6.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
6.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 760
6.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
6.4 Horses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
6.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 772
6.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 772
6.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
6.5 Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
6.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
6.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
6.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
6.6 Towns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
6.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803
6.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 804
6.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812
7 IOI 2014 813
7.1 Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813
7.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
7.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
7.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
7.2 Rail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
7.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823
7.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
7.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842
7.3 Wall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842
7.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844
7.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846
7.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854
7.4 Friend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854
7.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856
7.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858
7.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
7.5 Gondola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
7.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 866
7.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
7.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
7.6 Holiday . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
7.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878
7.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879
7.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890

8 IOI 2013 891


8.1 Art Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891
8.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
8.1.2 *Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
8.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
8.2 Cave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
8.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 896
8.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 896
8.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907
8.3 Dreaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907
8.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 910
8.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 910
8.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921
8.4 Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921
8.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924
8.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924
8.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936
8.5 Robots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 936
8.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939
8.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939
8.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952
8.6 Wombats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952
8.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 956
8.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
8.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964

9 IOI 2012 965


9.1 Pebbling odometer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965
9.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 968
9.1.2 *Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
9.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
9.2 Parachute rings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
9.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 972
9.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 972
9.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 991
9.3 Craysh scrivener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 991
9.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993
9.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993
9.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
9.4 Ideal city . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
9.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1004
9.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1004
9.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
9.5 Last Supper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
9.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023
9.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023
9.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032
9.6 Tournament . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032
9.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
9.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
9.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1045

10 IOI 2011 1046


10.1 Tropical Garden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1046
10.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048
10.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050
10.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1058
10.2 Race . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1058
10.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1060
10.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1062
10.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069
10.3 Rice Hub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069
10.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071
10.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1072
10.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1078
10.4 Crocodile's Underground City . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1078
10.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080
10.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
10.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1089
10.5 Dancing Elephants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1089
10.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1091
10.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1093
10.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101
10.6 Parrots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101
10.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1104
10.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107
10.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1117

11 IOI 2010 1118


11.1 Cluedo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1118
11.1.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119
11.1.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
11.1.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1123
11.2 Hotter Colder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1123
11.2.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1125
11.2.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1128
11.2.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1137
11.3 Quality of Living . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1137
11.3.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1138
11.3.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1140
11.3.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144
11.4 Languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144
11.4.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146
11.4.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146
11.4.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1154
11.5 Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1154
11.5.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155
11.5.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1157
11.5.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1160
11.6 Trac Congestion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1161
11.6.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1162
11.6.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1163
11.6.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1167
11.7 Maze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1167
11.7.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1170
11.7.2 *Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1170
11.7.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1170
11.8 Saveit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1170
11.8.1 Indicaµii de rezolvare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1172
11.8.2 Coduri surs  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1173
11.8.3 *Rezolvare detaliat  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1179

Anexa A Secvenµe de cod - utile 1181


A.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1181
A.1.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1181

Anexa B Formule - utile 1182


B.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1182
B.1.1 ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1182

Glosar 1183
Bibliograe 1186
Lista autorilor 1189
Lista gurilor
2.1 Shoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
2.2 Split1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
2.3 split2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
2.4 rectangular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
2.5 Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
2.6 vision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
2.7 walk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224

3.1 Puncte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293


3.2 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
3.3 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
3.4 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
3.5 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
3.6 doll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
3.7 highway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
3.8 Meetings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360

xiii
Lista tabelelor
3.1 Combo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
3.2 highway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341

xiv
Lista programelor
1.1.1 graderPlants-sandbox.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.2 checkerPlants.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.1.3 plants_300416.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.1.4 plants_301340.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.1.5 plants_304504.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.1.6 plants_308350.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.1 graderSupertrees-sandbox.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.2.2 checkerSupertrees.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.2.3 supertrees_300160.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.2.4 supertrees_300292.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.2.5 supertrees_300293.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.2.6 supertrees_300296.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.2.7 supertrees_300447.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
1.2.8 supertrees_300489.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
1.2.9 supertrees_300657.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
1.2.10 supertrees_300806.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
1.3.1 graderTickets-sandbox.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
1.3.2 checkerTickets.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.3.3 tickets_300231.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
1.3.4 tickets_300591.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.3.5 tickets_300700.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
1.3.6 tickets_300781.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.3.7 tickets_300782.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.4.1 graderBiscuits-sandbox.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.4.2 checkerBiscuits.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.4.3 biscuits_305541.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.4.4 biscuits_305864.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.4.5 biscuits_306418.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.4.6 biscuits_306482.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.5.1 mushrooms_Model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.5.2 mushrooms_303767.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
1.5.3 mushrooms_306077.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
1.5.4 mushrooms_938329.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
1.6.1 checkerStations.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
1.6.2 stations_304908.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
1.6.3 stations_304916.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
1.6.4 stations_304990.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
1.6.5 stations_306236.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
1.6.6 stations_306242.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
1.6.7 stations_306508.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
1.6.8 stations_306854.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
1.6.9 stations_308162.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
2.1.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
2.1.2 shoes.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
2.1.3 shoes.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
2.1.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
2.1.5 shoes-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
2.1.6 shoes-model+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
2.1.7 shoes 143076+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148

xv
2.1.8 shoes 143080+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
2.1.9 shoes-ds_ok+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
2.1.10 shoes-jonathanirvings+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 154
2.1.11 shoes-kuzey_ok+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
2.2.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
2.2.2 split.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
2.2.3 split.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
2.2.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
2.2.5 split-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
2.2.6 split-kostka-full+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
2.2.7 split 144514+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
2.2.8 split_koosaga+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
2.2.9 split-mahdi+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
2.2.10 split-maroon-accepted+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 177
2.3.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
2.3.2 rect.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
2.3.3 rect.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
2.3.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
2.3.5 rect-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
2.3.6 rectangle-mruxim-n2lg+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 187
2.3.7 rectangle 147888+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
2.3.8 rectangle145193+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
2.3.9 rectangle-peyman-n2lg-opt+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . 198
2.4.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
2.4.2 line.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
2.4.3 line.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
2.4.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
2.4.5 line-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
2.4.6 sol-ge-most-optimal+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
2.5.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
2.5.2 vision.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
2.5.3 vision.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
2.5.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
2.5.5 vision-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
2.6.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
2.6.2 walk.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
2.6.3 walk.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
2.6.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
2.6.5 walk-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
2.6.6 akm-full+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
2.6.7 maroon-full+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
2.6.8 mohammad-full-walk+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
2.6.9 walk 143257 Benq+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
2.6.10 walk 147051+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
3.1.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
3.1.2 combo.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
3.1.3 combo.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
3.1.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
3.1.5 combo-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
3.1.6 combo_75294.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
3.1.7 combo_75871.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
3.1.8 combo_76356.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
3.1.9 combo_77113.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
3.1.10 combo_koosaga.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
3.2.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
3.2.2 seat.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
3.2.3 seat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
3.2.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
3.2.5 seat-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
3.2.6 seats_75159.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
3.2.7 seats_75485.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
3.2.8 seats_76357.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
3.2.9 seats_80434.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
3.2.10 seats_81209.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
3.3.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
3.3.2 werewolf.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
3.3.3 werewolf.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
3.3.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
3.3.5 werewolf-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
3.3.6 werewolf_75105.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
3.3.7 werewolf_75296.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
3.3.8 werewolf_75793.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
3.3.9 werewolf_81140.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
3.3.10 werewolf_85439.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
3.4.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
3.4.2 dool.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
3.4.3 dool.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
3.4.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
3.4.5 doll_75123.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
3.4.6 dool_75619.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
3.4.7 dool_76623.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
3.4.8 dool_78782.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
3.5.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
3.5.2 highway.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
3.5.3 highway.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
3.5.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
3.5.5 highway_74962.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
3.5.6 highway_77105.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
3.5.7 highway_78948.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
3.6.1 compile_cpp.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
3.6.2 meetings.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
3.6.3 meetings.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
3.6.4 grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
3.6.5 meetings-model.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
3.6.6 meetings_76204.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
3.6.7 meetings_120908.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
3.6.8 meetings_159115.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
4.1.1 nowruz.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
4.1.2 checker.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
4.1.3 addleaves.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
4.1.4 addleaves_once.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
4.1.5 addleaves_rand.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
4.1.6 dfs.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
4.1.7 haircomb.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
4.1.8 nowruz1.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
4.2.1 wiring.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
4.2.2 wiring+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
4.2.3 wiring-haas-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
4.2.4 wiring-haas-ac-ternary.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
4.2.5 wiring-mahdi-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
4.2.6 wiring-malek-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
4.2.7 wiring-saeed-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
4.2.8 wiring-197505.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
4.2.9 wiring-200512.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
4.2.10 wiring-206573.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
4.2.11 wiring-216947.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
4.2.12 wiring-221801.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
4.2.13 wiring-227702.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
4.2.14 wiring-227979.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
4.3.1 train.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
4.3.2 train+grader.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
4.3.3 checkerModicat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
4.3.4 train-malek-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
4.3.5 train-saeed-ac.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
4.3.6 train-134142.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
4.3.7 train-146520.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
4.3.8 train-160375.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
4.3.9 train-163181.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
4.3.10 train-201183.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
4.3.11 train-221800.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
4.3.12 train-222137.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
4.4.1 prize.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
4.4.2 prize+graderpublic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
4.4.3 prize-169927.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
4.4.4 prize-187329.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
4.4.5 prize-206577.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
4.4.6 prize-208873.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
4.4.7 prize-221802.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
4.4.8 prize-229052.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
4.4.9 prize-232826.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
4.5.1 simurgh.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
4.5.2 simurgh-33904.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
4.5.3 simurgh-70374.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
4.5.4 simurgh-70729.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
4.5.5 simurgh-136567.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
4.5.6 simurgh-137723.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
4.6.1 books+graderpublic.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
4.6.2 books-42759.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
4.6.3 books-51837.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
4.6.4 books-94567.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
4.6.5 books-122101.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
4.6.6 books-138862.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
4.6.7 books-152524.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
4.6.8 books-206638.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
5.1.1 molecules_sk.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
5.1.2 molecules_sk_greedy_1_n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
5.1.3 molecules_sk_greedy_2_n.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
5.1.4 molecules_sk_greedy_3_ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 543
5.1.5 molecules-20751.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
5.1.6 molecules-72936.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
5.1.7 molecules-93472.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
5.1.8 molecules-114550.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
5.1.9 molecules-159299.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
5.2.1 railroad_mp_nlogn.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
5.2.2 railroad-103076.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
5.2.3 railroad-117891.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
5.2.4 railroad-135900.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
5.2.5 railroad-223610.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
5.2.6 railroad-233098.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
5.3.1 shortcut_c.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
5.3.2 sol_ge_nlogd.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572
5.3.3 sol_ge_nlogd_fastio.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575
5.3.4 sol_nk_nlogd.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
5.3.5 shortcut-24626.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
5.3.6 shortcut-33932.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
5.3.7 shortcut-94572.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
5.3.8 shortcut-97033.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587
5.3.9 shortcut-99074.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
5.3.10 shortcut-99075.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
5.3.11 shortcut-113364.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
5.3.12 shortcut-114144.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
5.3.13 shortcut-142952.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
5.3.14 shortcut-162764.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
5.3.15 shortcut-207049.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
5.3.16 shortcut-225819.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
5.4.1 paint_c.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
5.4.2 paint_iz.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
5.4.3 solve-correct-lc.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
5.4.4 checkerPaintModicat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
5.4.5 paint-65961.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621
5.4.6 paint-66328.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
5.4.7 paint-97040.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
5.4.8 paint-105782.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
5.4.9 paint-107399.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
5.4.10 paint-108251.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
5.4.11 paint-112136.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
5.4.12 paint-130510.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634
5.4.13 paint-180292.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
5.4.14 paint-204502.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
5.5.1 checkerMessyModicat.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
5.5.2 messy_cpp_ok.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
5.5.3 messy_iz.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
5.5.4 messy_tourist.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650
5.5.5 messy-21910.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
5.5.6 messy-23726.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 658
5.5.7 messy-23992.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
5.5.8 messy-59231.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 664
5.5.9 messy-66964.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667
5.5.10 messy-67587.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
5.5.11 messy-70792.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
5.5.12 messy-71456.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
5.5.13 messy-102062.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
5.6.1 checkerAliens.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
5.6.2 alien-bsearch.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
5.6.3 aliens_ma_nlogm.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
5.6.4 aliens_ma_nlogm_double.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
5.6.5 alien-32012.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
5.6.6 alien-43618.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700
5.6.7 alien-45993.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702
5.6.8 alien-70398.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
5.6.9 alien-94585.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
5.6.10 alien-96357.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
5.6.11 alien-97219.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
5.6.12 alien-121473.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
5.6.13 alien-166476.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
5.6.14 alien-171563.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
5.6.15 alien-172712.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
5.6.16 alien-173410.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
5.6.17 alien-224940.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
5.6.18 alien-225911.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727
5.6.19 alien-228077.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
6.1.1 boxes-16533.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 736
6.1.2 boxes-64042.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 737
6.1.3 boxes-70567.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741
6.1.4 checkerBoxes.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742
6.2.1 graderlib.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
6.2.2 scales-45773.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
6.2.3 scales-115418.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 750
6.2.4 scales-122639.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
6.2.5 checkerScales.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
6.3.1 teams-17286.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 760
6.3.2 teams-69885.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763
6.3.3 teams-172439.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
6.3.4 checkerTeams.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
6.4.1 horses-91995.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773
6.4.2 horses-102703.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
6.4.3 horses-202434.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778
6.4.4 checkerHorses.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781
6.5.1 sorting-70140.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
6.5.2 sorting-114135.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
6.5.3 sorting-129522.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
6.5.4 sorting-155927.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793
6.5.5 sorting-225877.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
6.5.6 sorting-233228.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
6.6.1 graderlib.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 804
6.6.2 towns-127541.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 804
6.6.3 towns-134867.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
6.6.4 towns-151462.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
6.6.5 checkerTowns.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
7.1.1 game-92195.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
7.1.2 game-117330.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
7.1.3 game-231330.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819
7.1.4 game_n2.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
7.1.5 checkerGame.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
7.2.1 rail-117000.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
7.2.2 rail-121219.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828
7.2.3 rail-125767.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832
7.2.4 rail-139096.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835
7.2.5 rail-173712.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
7.3.1 wall-39307.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846
7.3.2 wall-93939.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
7.3.3 wall-173006.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
7.3.4 wall-232336.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851
7.3.5 checkerWall.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
7.4.1 friend-16524.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858
7.4.2 friend-115760.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
7.4.3 friend-119597.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861
7.4.4 checkerFriend.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
7.5.1 gondola-7306.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
7.5.2 gondola-93790map.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
7.5.3 gondola-102776.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871
7.5.4 gondola-153281.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 874
7.5.5 checkerGondola.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
7.6.1 holiday-120785.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 880
7.6.2 holiday-134641.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 882
7.6.3 holiday-211797.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884
7.6.4 holiday-229391.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
7.6.5 checkerHoliday.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
8.2.1 cave-1749.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 897
8.2.2 cave-16655.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899
8.2.3 cave-170339.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901
8.2.4 cave-231414.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904
8.2.5 checkerCave.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907
8.3.1 dreaming-3168.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 910
8.3.2 dreaming-16636.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 913
8.3.3 dreaming-81476.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915
8.3.4 dreaming-172308.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917
8.3.5 dreaming-198762.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 919
8.3.6 checkerDreaming.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920
8.4.1 game-220385.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925
8.4.2 game-224978.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929
8.4.3 game-225963.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 932
8.4.4 checkerGame.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935
8.5.1 robot-7026.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939
8.5.2 robot-7037.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942
8.5.3 robot-17227.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 944
8.5.4 robot-96364.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947
8.5.5 checkerRobot.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950
8.6.1 wombats-108194.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
8.6.2 wombats-108196.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959
8.6.3 wombats-118871.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961
8.6.4 checkerWombats.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963
9.1.1 odometer.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 968
9.2.1 rings.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 972
9.2.2 rings-13540.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 976
9.2.3 rings-49004.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979
9.2.4 rings-62499.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 981
9.2.5 rings-223749.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 984
9.2.6 rings-233348.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 987
9.2.7 checkerRings.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 990
9.3.1 scrivener.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993
9.3.2 scrivener-7279.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 995
9.3.3 scrivener-18725.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997
9.3.4 scrivener-230126.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999
9.3.5 checkerScrivener.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001
9.4.1 city.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1004
9.4.2 city-16324.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1008
9.4.3 city-18847.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1010
9.4.4 city-32163.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1013
9.4.5 city-197022.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
9.4.6 checkerCity.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
9.5.1 supper.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023
9.5.2 supper-231107.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1028
9.6.1 tournament.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034
9.6.2 tournament-2193.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1038
9.6.3 tournament-4657.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1040
9.6.4 tournament-14774.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1042
9.6.5 checkerTournament.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1044
10.1.1 garden-49449.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050
10.1.2 garden-49789.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1052
10.1.3 garden-116557.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055
10.1.4 checkerGarden.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1058
10.2.1 race-28930.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1062
10.2.2 race-112526.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064
10.2.3 race-216556.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1066
10.2.4 checkerRace.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
10.3.1 racehub-7321.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1072
10.3.2 racehub-95096.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1073
10.3.3 racehub-113697.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1074
10.3.4 racehub-116283.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1076
10.3.5 checkerRaceHub.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1077
10.4.1 crocodile-10431.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081
10.4.2 crocodile-16656.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1083
10.4.3 crocodile-133704.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1085
10.4.4 crocodile-228833.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1087
10.4.5 checkerCrocodile.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1088
10.5.1 elephants-115857.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1093
10.5.2 elephants-192442.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1095
10.5.3 elephants-207733.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1098
10.5.4 checkerElephants.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101
10.6.1 parrot-224033.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107
10.6.2 parrots-235627.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1112
11.1.1 cluedosol.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119
11.1.2 cluedosol.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
11.1.3 cluedo-218031.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
11.1.4 cluedo-220599.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121
11.1.5 cluedo-229322.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1122
11.2.1 coldersol1.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1127
11.2.2 coldersol2.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1127
11.2.3 hottercolder.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1128
11.2.4 hottercolder-173047.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1131
11.2.5 hottercolder-201977.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133
11.3.1 rectanglesol.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1139
11.3.2 quality-208958.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1140
11.3.3 quality-234933.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1142
11.3.4 checkerQuality.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144
11.4.1 lang-8124.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146
11.4.2 lang-12891.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1148
11.4.3 lang-155338.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1150
11.4.4 lang-235517.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1152
11.5.1 memory.pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1156
11.5.2 memory.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1156
11.5.3 memory-206951.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1157
11.5.4 memory-217254.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1158
11.5.5 memory-232610.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1159
11.6.1 quality-229321.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1163
11.6.2 quality-232654.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1164
11.6.3 quality-236364.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1165
11.6.4 checkerQuality.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1167
11.8.1 saveit-231998.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1173
11.8.2 saveit-224546.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1176
Capitolul 1

IOI 202021

1.1 plants
Problema 1 - Comparing Plants 100 de puncte
Author: Jo Sunghyeon

Botanistul Hazel a vizitat o expoziµie special  în Gr dina Botanic  din Singapore. În aceast 
expoziµie, n plante cu în lµimi distincte sunt plasate într-un cerc. Aceste plante sunt etichetate
de la 0 la n  1 în ordinea acelor de ceasornic, cu planta n  1 lâng  planta 0.
Pentru ecare plant  i (0 & i & n  1), Hazel a comparat planta i cu ecare dintre urm toarele
k  1 plante în ordine în sensul acelor de ceasornic ³i a notat num rul ri indicând câte dintre
aceste k  1 plante sunt mai înalte decât planta i. Astfel, ecare valoare ri depinde de în lµimile
relative a k plante consecutive.
De exemplu, s  presupunem c  n 5, k 3 ³i i 3. Urm toarele k  1 2 plante în ordinea
acelor de ceasornic dup  planta i 3 vor  plantele 4 ³i 0. Dac  planta 4 este mai înalt  decât
planta 3 ³i planta 0 este mai scund  decât planta 3, atunci Hazel notateaz  r3 1.
Puteµi presupune c  Hazel a înregistrat corect valorile ri. Astfel, exist  cel puµin o congu-
raµie de în lµimi distincte ale plantelor în concordanµ  cu aceste valori.
Vi se cere s  comparaµi în lµimile a q perechi de plante. Din p cate, nu aveµi acces la expoziµie.
Singura dvs. surs  de informaµii este caietul lui Hazel ce conµine valoarea lui k ³i secvenµa valorilor
r0, ..., rn  1.
Pentru ecare pereche de plante diferite x ³i y care trebuie comparate, determinaµi în care
dintre urm toarele trei situaµii ne încadr m:

ˆ Planta x este cu siguranµ  mai înalt  decât planta y : în orice conguraµie de în lµimi distincte
h0, ..., hn  1 în concordanµ  cu tabloul r avem hx % hy .
ˆ Planta x este cu siguranµ  mai scund  decât planta y : în orice conguraµie de în lµimi
distincte h0, ..., hn  1 în concordanµ  cu tabloul r avem hx % hy .

ˆ Comparaµia este neconcludent : niciunul dintre cele dou  cazuri anterioare nu se aplic .

Detalii de implementare
Trebuie s  implementaµi urm toarele funcµii:

void init(int k, int[] r)

ˆ k : num rul de plante consecutive ale c ror în lµimi determin  valorile individuale ale lui
ri.
21
aur: Alexandru Luchianov, Liceul Internaµional de Informatic  din Bucure³ti,
. argint: Maria Alexa Tudose, C. N. I. L. Caragiale din Ploie³ti,
. argint: Alexandra Maria Udri³toiu, C. N. Fraµii Buze³ti din Craiova,
. bronz: George Alexandru Râpeanu, C. N. Emil Racoviµ  din Cluj-Napoca

1
CAPITOLUL 1. IOI 2020 2

ˆ r: un tablou de dimensiune n, cu elementele sale ri reprezentând num rul de plante mai


înalte decât planta i aate în urm toarele k  1 plante luate în ordinea acelor de ceasornic.

ˆ Funcµia se apeleaz  o singur  dat , înaintea oric rui apel al funcµiei compare_plants.

int compare_plants(int x, int y)

ˆ x, y : etichetele plantelor pe care le compar m.

ˆ Aceast  procedur  va trebui s  returneze:

` 1 dac  planta x este cu siguranµ  mai înalt  decât planta y ,


` -1 dac  planta x este cu siguranµ  mai scund  decât planta y ,
` 0 dac  comparaµia este neconcludent .

ˆ Aceast  funcµie va  apelat  de exact q ori.

Exemple
Exemplul 1 Consider m apelul:
init(3, [0, 1, 1, 2])

S  presupunem c  graderul apeleaza compare_plants (0, 2). Deoarece r0 0 putem


deduce imediat c  planta 2 nu este mai înalt  decât planta 0. Prin urmare, apelul ar trebui s 
returneze 1.
S  presupunem c  graderul apeleaza în continuare compare_plants(1, 2). Pentru toate
conguraµiile de în lµimi posibile ce respect  constrângerile, planta 1 este mai scund  decât planta
2. De aceea, se va returna -1.

Examplul 2 Consider m apelul:


init(2, [0, 1, 0, 1])

S  presupunem c  graderul apeleaza compare_plants(0, 3). Din r3 1, ³tim c  planta


0 este mai înalt  decât planta 3. Prin urmare, se va returna 1.
S  presupunem c  graderul apeleaza în continuare compare_plants(1, 3). Exist  dou 
conguraµii de în lµimi 3, 1, 4, 2 ³i 3, 2, 4, 1, ambele respect  constrângerile impuse de m su-
r torile lui Hazel. Deoarece într-o conguraµie planta 1 este mai scund  decât planta 3, iar în
cealalt  conguraµie este mai înalt  decât planta 3, acest apel ar trebui s  returneze 0.

Restricµii
a 2 & k & n & 200 000
a 1 & q & 2000 000
a 0 & r i & k  1 (oricare ar  0 & i & n  1)
a 0 & x $ y & n1
a Exist  una sau mai multe conguraµii de în lµimi distincte ale plantelor în concordanµ 
cu tabloul r.

Subtaskuri
1. (5 puncte) k 2
2. (14 puncte) n & 5000, 2 k % n
3. (13 puncte) 2 k % n
4. (17 puncte) R spunsul corect pentru ecare apel compare_plants este 1 sau -1.
(11 puncte) n & 300, q &
n n1
5. 2
6. (15 puncte) x 0 pentru toate apelurile compare_plants.
7. (25 de puncte) Fara restricµii suplimentare.
CAPITOLUL 1. IOI 2020 3

Sample grader
Graderul cite³te în formatul urm tor:
ˆ linia 1: n k q
ˆ linia 2: r0 r1 ... rn  1
ˆ linia 3  i (0 & i & q  1): x y pentru al i-lea apel al funcµiei compare_plants
Graderul tip re³te r spunsul t u în formatul urm tor:
ˆ linia 1  i (0 & i & q  1): x valoarea returnat  de al i-lea apel al funcµiei compare_plants.

Timp maxim de executare/test: 4.0 secunde


Memorie: total 2000 MB

1.1.1 Indicaµii de rezolvare

For simplicity of describing the solutions, all indices of the array will be taken modulo n.

Subtask 1
We are given the height comparisons of each pair of adjacent plants. We can conclude that plant
x is taller than plant y if either of the following holds:
a r x rx  1 ... ry  1 0
a r y  1 ... rn  2 rn  1 1 and r0 r1 ... rx  1 1
The condition for returning -1 is similar.
If none of the conditions hold, return 0.
Implementing this naively requires O nq  time. We can speed this up by using a static prex
sum array.

Subtask 2
The overall idea is to generate a possible conguration of heights.
First observe that the tallest plant x must satisfy rx 0. This suggests the following
algorithm:
Repeat the following for n times:
1. Look for some index x such that rx 0
2. Decrement rx  k  1, rx  k  2, ..., rx  1, rx by 1
Consider the input n 3, k 2, r 0, 1, 0. The tallest plant must be 2. However, we also
have rx 0. We therefore need to replace rule (1) with the following:
a Look for some index x such that rx 0 and rx  k  1, rx  k  2, ..., rx  1 j 0. (*)
The index x satisfying the above condition is always unique (and therefore the nal congu-
ration of heights is also unique). The order in which these x are chosen will give rise to a possible
conguration of heights, in which we can answer all the queries by simply comparing the heights.
2
Complexity O n .

Subtask 3
Same as above; except that we need to use a segment tree with lazy propagation to speed up the
algorithm. However, it can be tricky to implement (*).
To do this, we choose a value t such that rt 0. If there are multiple of them, choose the
smallest. We can do a range minimum query to check if any of rt  k  1, ..., rt  1 is zero. If
none of them are zero, we choose x t.
If not, we let x be the smallest index among t  k  1, ..., t  1 such that rx 0.
Other than the potentially more tedious implementation of (*), the rest of the algorithm
remains the same. Complexity O n log n.
CAPITOLUL 1. IOI 2020 4

Subtask 4

The implementation of (*) becomes more problematic here. To illustrate, suppose that k 2 and
r0 r1 ... rn  2 0, rn  1 1.
If we choose the value t n  2, we realise that rn  3 0. However, we run into further
problems because rn  4 is also zero.
As such, we need to recursively search for the correct index. Complexity O n log n.

extract(x):
while there is a y in {x-k+1, x-k+2, ..., x-1} such that r[y]==0:
extract(y)
end
decrement r[x-k+1], r[x-k+2], ..., r[x] by 1

The ordering of the heights is then given by the order in which the procedure returns (rather
than the order the of being called). The queries can still be answered by a direct comparison of
the heights.

Subtask 5
We rst generate a possible conguration of heights h0, 1, ..., n  1 using the techniques from the
previous subtasks.
Dene d x, y  min ¶x  y ¶, ¶x  n  y ¶, ¶y  n  x¶ to be the distance between plants x and
y . Generate a directed graph with x y if and only if hx % hy  and d x, y  $ k .
Lemma:
a The above graph generated does not depend on the choice of h . In other words, any two
height arrays h1   and h2   consistent with r  will generate the same directed graph.
Proof:
a Let G be any directed graph consistent with the input r   (that is, for each i, there are
exactly ri values of j among i  1, i  2, ..., i  k  1 such that j i).
a We rst observe that any valid conguration h  corresponds to a run of the algorithm (*).
a It is therefore sucient to show that any run of the algorithm (*) generates a topological sort
of this graph G.
22
a The proof is similar to Kahn's algorithm . When plant is removed by the algorithm (*), the
conditions for removing guarantee that must have zero in-degree.
a Since we only remove nodes with zero in-degree, the order generated must correspond to a
valid topological sort of G.
To check if plant x is taller than plant y , we check if there exists a (directed) path from x to y .
To do so, we can compute the transitive closure using Flyod-Warshall] and pre-compute the
3
answers to the entire space of queries. Complexity O n .

Alternative solution to subtask 5 We follow the idea of (*). We look for some z such that
rz  0 and rz  k  1, rz  k  2, ..., rz  1 j 0, but now including an additional constraint
that z j x. We stop when x is the only plant we can remove.
There exists a conguration of heights such that plant y is taller than plant x if and only if
the above algorithm removes plant x.
2 4
Take note that doing so naively would require computation O nq  O n  time, if we do not
use a segment tree.
However, it is easy to speed this up to O n . For each plant 0 & x & n  1, we pre-compute
3

in advance all y such that y can possibly be taller than x, storing them in a n  n table.

Subtask 6
Same idea as subtask 5. However, we are unable to generate the entire graph as there can be up
to O nk  edges.
22
https://www.geeksforgeeks.org/topological-sorting-indegree-based-solution/
CAPITOLUL 1. IOI 2020 5

For each plant x, dene the 'left edge' and 'right edge' as follows:
a lef t x the tallest plant shorter than x among x  k  1, ..., x  1 or -1 if it does not exist
a lef t x the tallest plant shorter than x among x  1, ..., x  k  1 or -1 if it does not exist
We now generate a graph with edges x lef t x and x right x for each x. To check if
plant is taller than plant x, we check if there exists a (directed) path from x to y .
Since we only need to compare against plant 0, we can use single-source BFS or DFS to nd
all vertices reachable from 0.
Complexity O n log n (the queries are answered in O 1 time, however we need O n log n to
generate the array h , each value of lef t x and right x requires O log n to compute.
We may also use the idea to the alternative solution of subtask 5. After nding all the plants
that can be taller than plant 0, we can invert the ranks (i.e. ri replace with k  1  ri for each
i) to check if a plant can shorter than plant 0.

Subtask 7
For each query x, y , let z be the rst value among lef t x, lef t lef t x, lef t lef t lef t x
... such that y is between z and lef t z .
If hz  % hy , we can then conclude that x is taller than y .
We do so similarly for the right edges.
To speed up this computation, we use power-of-2 tables:

lef t1 x  lef t x, lef tk1 x lef tk lef tk x

Using this approach, we may compute z using O log n time.


Total complexity: O n log n  q log n.
To the readers who are keen for an extra challenge: gure out how to solve this problem using
n log n preprocessing time and O 1 time per query!

1.1.2 Coduri surs 

Listing 1.1.1: graderPlants-sandbox.cpp


1 #include "plants.h"
2
3 #include <cstdio>
4 #include <cassert>
5 #include <vector>
6
7 static int n, k, q;
8
9 static std:: vector<int> r;
10 static std:: vector<int> x;
11 static std:: vector<int> y;
12 static std:: vector<int> answer;
13
14 #include <iostream> /* cout */
15 //#include <ctime> /* clock_t, clock, CLOCKS_PER_SEC */
16
17 #include <iomanip> /* setprecision */
18 #include <chrono> /* chrono */
19
20 // ---------------------------------------------------
21
22 using namespace std;
23
24 const int n_bits=18;
25 const int inf = 1e9;
26 const int n_max = 2e5+5;
27
28 int arr[1<<(n_bits+1)];
29 int lazyadd[1<<(n_bits+1)];
CAPITOLUL 1. IOI 2020 6

30 int p_lt[n_max];
31 int p_rg[n_max];
32 int stash[n_max]; // a manual queue
33 int tallest[n_max];
34 int shortest[n_max];
35
36 struct segtree
37 {
38 int node = 1;
39 int lazy = 0;
40 segtree(){}
41 void build(vector<int> &v)
42 {
43 assert(v.size()<(1<<n_bits));
44 for(int i=0; i<(1<<n_bits); i++)
45 {
46 arr[i+(1<<n_bits)] = (i<(int)v.size() ? v[i] : inf);
47 }
48 for(int i=(1<<n_bits)-1; i>=1; i--)
49 {
50 arr[i] = min(arr[2*i], arr[2*i+1]);
51 }
52 for(int i=0; i<(1<<(n_bits+1)); i++)
53 {
54 lazyadd[i] = 0;
55 }
56 }
57
58 void prefix_update(int x, int change)
59 {
60 if(x==-1) return;
61 x += (1<<n_bits);
62 lazyadd[x] += change;
63 for(int i=0; i<n_bits; i++)
64 {
65 if(x&1)
66 {
67 lazyadd[x-1] += change;
68 }
69 arr[x/2] = min(arr[x]+lazyadd[x], arr[x^1]+lazyadd[x^1]);
70 x=x/2;
71 }
72 }
73
74 void decr(int left, int right)
75 {
76 prefix_update(left-1, 1);
77 prefix_update(right,-1);
78 }
79
80 int find_and_remove_zero()
81 {
82 node = 1;
83 lazy = 0;
84 for(int i=0; i<n_bits; i++)
85 {
86 lazy += lazyadd[node];
87 node *= 2;
88 if(lazy + lazyadd[node] + arr[node]) node++;
89 }
90 int ans = node - (1<<n_bits);
91 lazyadd[node] = 1e9;
92 for(int i=0; i<n_bits; i++)
93 {
94 arr[node/2] = min(arr[node]+lazyadd[node],
95 arr[node^1]+lazyadd[node^1]);
96 node = node/2;
97 }
98 return ans;
99 }
100 };
101
102 void lexi_smallest(int k, vector<int> &r, int* ptr, int* ord)
103 {
104 segtree s;
105 s.build(r);
CAPITOLUL 1. IOI 2020 7

106 int n = r.size();


107 stash[0] = n;
108 ord[n] = -1;
109 int* fr = stash+1;
110 int* bk = stash+1;
111 for(int i=0; i<(int)r.size(); i++)
112 {
113 while(lazyadd[1] + arr[1])
114 {
115 s.decr((*fr++)-k+1+n, n-1);
116 }
117 int p = s.find_and_remove_zero();
118 ord[p] = i;
119 if(p<k-1)
120 {
121 s.prefix_update(p, -1);
122 *bk++ = p;
123 }
124 else
125 {
126 s.decr(p-k+1, p);
127 }
128 ptr[p] = ord[ *(fr-1) ];
129 }
130 }
131
132 void init(int k, std::vector<int> r)
133 {
134 lexi_smallest(k,r,p_lt, tallest);
135 for(int &i: r) i = k-1-i;
136 lexi_smallest(k,r,p_rg, shortest);
137 }
138
139 int compare_plants(int x, int y)
140 {
141 if(x>y) return -compare_plants(y,x);
142 if(tallest[x]>tallest[y] || p_rg[y]>=shortest[x]) return -1;
143 if(shortest[x]>shortest[y] || p_lt[y]>=tallest[x]) return 1;
144 return 0;
145 }
146
147 // ---------------------------------------------------
148
149 int main()
150 {
151 std::clock_t c_start = std::clock();
152 auto t_start = std::chrono::high_resolution_clock::now();
153
154 auto t1 = clock();
155
156 std::freopen("../tests/7-15.in", "r", stdin) ;
157 std::freopen("plants.out", "w", stdout) ;
158
159 assert(scanf("%d%d%d", &n, &k, &q) == 3);
160
161 r.resize(n);
162 answer.resize(q);
163 for (int i = 0; i < n; i++)
164 {
165 int value;
166 assert(scanf("%d", &value) == 1);
167 r[i] = value;
168 }
169 x.resize(q);
170 y.resize(q);
171 for (int i = 0; i < q; i++)
172 {
173 assert(scanf("%d%d", &x[i], &y[i]) == 2);
174 }
175 fclose(stdin);
176
177 auto t2 = clock();
178
179 init(k, r);
180 for (int i = 0; i < q; i++)
181 {
CAPITOLUL 1. IOI 2020 8

182 answer[i] = compare_plants(x[i], y[i]);


183 }
184
185 auto t3 = clock();
186
187 for (int i = 0; i < q; i++)
188 {
189 printf("%d\n", answer[i]);
190 }
191
192 fclose(stdout);
193
194 auto t4 = clock();
195
196 std::clock_t c_end = std::clock();
197 auto t_end = std::chrono::high_resolution_clock::now();
198
199 // reset console output
200 freopen("CON", "w", stdout);
201
202 //std::cout <<result<<’\n’<<’\n’;
203 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
204 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
205 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
206
207 std::cout << std::fixed << std::setprecision(2)
208 << "\nCPU time used: "
209 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
210 << "Wall clock time passed: "
211 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
212 << " ms\n";
213
214 return 0;
215 }
216 /*
217 t2-t1 = 0.859
218 t3-t2 = 1.14
219 t4-t3 = 0.094
220
221 CPU time used: 2093.00 ms
222 Wall clock time passed: 2093.74 ms
223
224 Process returned 0 (0x0) execution time : 2.141 s
225 Press any key to continue.
226 */

Listing 1.1.2: checkerPlants.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 //static string output_secret = "23b69acd873f5d7e892bae7de83615";
6
7 int main()
8 //int main(int argc, char * argv[])
9 {
10 int argc=4;
11
12 char* argv[] =
13 {
14 (char*)"checker",
15 (char*)"../tests/7-15.in", // input
16 (char*)"../tests/7-15.out", // rezultat corect
17 (char*)"plants.out", // rezultat de verificat si acordat punctaj
18 };
19
20 cout<<"argc = "<<argc<<"\n";
21 for(int kk=0;kk<argc;kk++)
22 cout<<argv[kk]<<"\n";
23 cout<<"----------------------\n";
24
25 registerChecker("plants", argc, argv);
26
27 //readBothSecrets(output_secret);
CAPITOLUL 1. IOI 2020 9

28 //readBothGraderResults();
29
30 compareRemainingLines();
31 }
32 /*
33 argc = 4
34 checker
35 ../tests/7-15.in
36 ../tests/7-15.out
37 plants.out
38 ----------------------
39 1
40 Correct
41
42 Process returned 0 (0x0) execution time : 0.641 s
43 Press any key to continue.
44 */

Listing 1.1.3: plants_300416.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300416 417 ms 19036 KB
3
4 #include "plants.h"
5
6 #include <bits/stdc++.h>
7
8 static int n, k, q;
9
10 static std:: vector<int> r;
11 static std:: vector<int> x;
12 static std:: vector<int> y;
13 static std:: vector<int> answer;
14
15 // ---------------------------------------------------
16
17 using namespace std;
18
19 namespace
20 {
21 class plants_solver
22 {
23 struct index_data
24 {
25 int l_to_r;
26 int l_to_r_2;
27 int r_to_l;
28 int r_to_l_2;
29 };
30 std::vector<index_data> v;
31
32 public:
33 plants_solver() { }
34 plants_solver(int N, int K, std::vector<int> R) : v(N)
35 {
36 using std::min;
37 using std::max;
38
39 assert(N == int(R.size()));
40 assert(1 <= K && K <= N);
41 for (int& r : R) assert(0 <= r && r < K);
42
43 struct seg_node
44 {
45 int val;
46 int ind;
47 int lazy;
48 int last_taken;
49 };
50 std::vector<seg_node> seg(2*N);
51
52 auto update_node = [&](int a)
53 {
54 assert(a < N);
55 if (seg[2*a].val < seg[2*a+1].val)
CAPITOLUL 1. IOI 2020 10

56 {
57 seg[a].val = seg[2*a].val;
58 seg[a].ind = seg[2*a].ind;
59 }
60 else
61 {
62 seg[a].val = seg[2*a+1].val;
63 seg[a].ind = seg[2*a+1].ind;
64 }
65 seg[a].val += seg[a].lazy;
66 seg[a].last_taken = max(seg[2*a].last_taken,
67 seg[2*a+1].last_taken);
68 };
69
70 for (int i = 0; i < N; i++)
71 {
72 seg[N+i] = seg_node{R[i], i, 0, -1};
73 }
74 for (int a = N-1; a; a--) update_node(a);
75
76 auto downdate_all = [&](int n)
77 {
78 n >>= __builtin_ctz(n); n >>= 1;
79 if (!n) return;
80 for (int l = 31 - __builtin_clz(n); l >= 0; l--)
81 {
82 int a = n >> l;
83 assert(a < N);
84 seg[2*a].val += seg[a].lazy;
85 seg[2*a].lazy += seg[a].lazy;
86 seg[2*a+1].val += seg[a].lazy;
87 seg[2*a+1].lazy += seg[a].lazy;
88 seg[a].lazy = 0;
89 }
90 };
91
92 std::vector<int> toposort; toposort.reserve(N);
93 std::vector<int> stk; stk.reserve(N);
94 std::vector<int> prev_bigger(N);
95 std::vector<int> next_bigger(N);
96 while (int(toposort.size()) < N)
97 {
98 if (stk.empty())
99 {
100 assert(seg[1].val == 0);
101 stk.push_back(seg[1].ind);
102 }
103
104 int cur = stk.back();
105 //std::cerr << "trying " << cur << ’\n’;
106
107 int prev_taken = -1;
108 int lo = cur-(K-1), hi = cur;
109 if (lo < 0)
110 {
111 lo += 2 * N, hi = 2 * (N + hi);
112 }
113 else
114 {
115 lo += N, hi += N;
116 }
117
118 downdate_all(lo);
119 downdate_all(hi);
120
121 int zero_ind = -1;
122 for (int a = lo, b = hi; a < b; a >>= 1, b >>= 1)
123 {
124 if (a & 1)
125 {
126 if (seg[a].val == 0)
127 {
128 zero_ind = seg[a].ind;
129 goto found_zero;
130 }
131 prev_taken = max(prev_taken, seg[a].last_taken);
CAPITOLUL 1. IOI 2020 11

132 a++;
133 }
134
135 if (b & 1)
136 {
137 --b;
138 if (seg[b].val == 0)
139 {
140 zero_ind = seg[b].ind;
141 goto found_zero;
142 }
143 prev_taken = max(prev_taken, seg[b].last_taken);
144 }
145 }
146
147 goto not_found_zero;
148
149 found_zero:
150 {
151 //std::cerr << "bad: " << zero_ind << ’\n’;
152 stk.push_back(zero_ind);
153 continue;
154 }
155
156 not_found_zero:;
157 int next_taken = -1;
158 {
159 int lo2 = cur+1, hi2 = cur+K;
160 assert(lo2 <= N);
161 lo2 += N;
162 if (hi2 > N)
163 {
164 hi2 = 2 * hi2;
165 }
166 else
167 {
168 hi2 += N;
169 }
170
171 for (int a = lo2, b = hi2; a < b; a >>= 1, b >>= 1)
172 {
173 if (a & 1)
174 {
175 next_taken = max(next_taken, seg[a].last_taken);
176 a++;
177 }
178
179 if (b & 1)
180 {
181 --b;
182 next_taken = max(next_taken, seg[b].last_taken);
183 }
184 }
185 }
186
187 assert(stk.back() == cur);
188 stk.pop_back();
189 int t = int(toposort.size());
190 toposort.push_back(cur);
191 prev_bigger[cur]=(prev_taken==-1) ? -1 : toposort[prev_taken];
192 next_bigger[cur]=(next_taken==-1) ? -1 : toposort[next_taken];
193
194 //std::cerr << "take " << cur << ’ ’ << \
195 prev_bigger[cur] << ’ ’ << next_bigger[cur] << ’\n’;
196
197 seg[N+cur].last_taken = t;
198 seg[N+cur].val += K; // should never be bad
199 for (int a = lo, b = hi; a < b; a >>= 1, b >>= 1)
200 {
201 if (a & 1)
202 {
203 seg[a].lazy--;
204 seg[a].val--;
205 a++;
206 }
207
CAPITOLUL 1. IOI 2020 12

208 if (b & 1)
209 {
210 --b;
211 seg[b].lazy--;
212 seg[b].val--;
213 }
214 }
215
216 for (int a = (N+cur) >> 1; a; a >>= 1) update_node(a);
217 for (int a = lo >> __builtin_ctz(lo) >> 1;
218 a;
219 a >>= 1) update_node(a);
220 }
221
222 {
223 std::vector<int> pred(2*N+1, -1);
224 {
225 int cur = 2*N;
226 for (int a : toposort)
227 {
228 if (a > N-K)
229 {
230 pred[cur] = N+a;
231 cur = N+a;
232 }
233 }
234 }
235
236 for (int i = 2*N-K; i >= 0; i--)
237 {
238 assert(i + K <= 2 * N);
239 int j = next_bigger[i >= N ? i-N : i];
240 if (j == -1)
241 {
242 j = 2 * N;
243 }
244 else
245 {
246 if (j < i) j += N;
247 assert(i < j && j < i + K);
248 }
249 pred[i] = pred[j];
250 pred[j] = i;
251 }
252
253 for (int cur = pred[2*N], idx = 2*N-1;
254 idx >= 0;
255 cur = pred[cur], idx--)
256 {
257 if (cur >= N)
258 {
259 v[cur - N].l_to_r_2 = idx;
260 }
261 else
262 {
263 v[cur].l_to_r = idx;
264 }
265 }
266 }
267
268 for (int i = 0; i < N; i++)
269 {
270 //std::cerr << v[i].l_to_r << ’ ’ << v[i].l_to_r_2 << ’\n’;
271 assert(v[i].l_to_r > v[i].l_to_r_2);
272 }
273
274 {
275 std::vector<int> pred(2*N+1, -1);
276 {
277 int cur = 2 * N;
278 for (int a : toposort)
279 {
280 if (a < K)
281 {
282 pred[cur] = a;
283 cur = a;
CAPITOLUL 1. IOI 2020 13

284 }
285 }
286 }
287
288 for (int i = K; i < 2*N; i++)
289 {
290 int j = prev_bigger[i >= N ? i-N : i];
291 if (j == -1)
292 {
293 j = 2 * N;
294 }
295 else
296 {
297 if (j + N < i)
298 {
299 j += N;
300 }
301 assert(i - K < j && j < i);
302 }
303 pred[i] = pred[j];
304 pred[j] = i;
305 }
306
307 for (int cur = pred[2*N], idx = 2*N-1;
308 idx >= 0;
309 cur = pred[cur], idx--)
310 {
311 if (cur >= N)
312 {
313 v[cur - N].r_to_l_2 = idx;
314 }
315 else
316 {
317 v[cur].r_to_l = idx;
318 }
319 }
320 }
321
322 for (int i = 0; i < N; i++)
323 {
324 //std::cerr << v[i].r_to_l << ’ ’ << v[i].r_to_l_2 << ’\n’;
325 assert(v[i].r_to_l_2 > v[i].r_to_l);
326 }
327 }
328
329 public:
330 int compare_plants(int x, int y) const
331 {
332 if (x == y) return 0;
333 if (x > y)
334 {
335 return -compare_plants(y, x);
336 }
337 assert(x < y);
338 const auto& a = v[x];
339 const auto& b = v[y];
340 if (a.l_to_r < b.l_to_r)
341 {
342 return -1;
343 }
344 if (b.l_to_r < a.l_to_r_2)
345 {
346 return 1;
347 }
348 if (a.r_to_l_2 < b.r_to_l)
349 {
350 return -1;
351 }
352 if (b.r_to_l < a.r_to_l)
353 {
354 return 1;
355 }
356
357 return 0;
358 }
359 } SOLVER;
CAPITOLUL 1. IOI 2020 14

360
361 }
362
363 void init(int K, std::vector<int> R)
364 {
365 int N = int(R.size());
366 SOLVER = plants_solver(N, K, R);
367 }
368
369 int compare_plants(int x, int y)
370 {
371 return SOLVER.compare_plants(x, y);
372 }
373
374 // ---------------------------------------------------
375
376 int main()
377 {
378 std::clock_t c_start = std::clock();
379 auto t_start = std::chrono::high_resolution_clock::now();
380
381 auto t1 = clock();
382
383 std::freopen("../tests/7-15.in", "r", stdin) ;
384 std::freopen("plants.out", "w", stdout) ;
385
386 assert(scanf("%d%d%d", &n, &k, &q) == 3);
387
388 r.resize(n);
389 answer.resize(q);
390 for (int i = 0; i < n; i++)
391 {
392 int value;
393 assert(scanf("%d", &value) == 1);
394 r[i] = value;
395 }
396 x.resize(q);
397 y.resize(q);
398 for (int i = 0; i < q; i++)
399 {
400 assert(scanf("%d%d", &x[i], &y[i]) == 2);
401 }
402 fclose(stdin);
403
404 auto t2 = clock();
405
406 init(k, r);
407 for (int i = 0; i < q; i++)
408 {
409 answer[i] = compare_plants(x[i], y[i]);
410 }
411
412 auto t3 = clock();
413
414 for (int i = 0; i < q; i++)
415 {
416 printf("%d\n", answer[i]);
417 }
418
419 fclose(stdout);
420
421 auto t4 = clock();
422
423 std::clock_t c_end = std::clock();
424 auto t_end = std::chrono::high_resolution_clock::now();
425
426 // reset console output
427 freopen("CON", "w", stdout);
428
429 //std::cout <<result<<’\n’<<’\n’;
430 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
431 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
432 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
433
434 std::cout << std::fixed << std::setprecision(2)
435 << "\nCPU time used: "
CAPITOLUL 1. IOI 2020 15

436 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"


437 << "Wall clock time passed: "
438 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
439 << " ms\n";
440
441 return 0;
442 }
443 /*
444 t2-t1 = 1.035
445 t3-t2 = 2.637
446 t4-t3 = 0.102
447
448 CPU time used: 3774.00 ms
449 Wall clock time passed: 3774.41 ms
450
451 Process returned 0 (0x0) execution time : 3.815 s
452 Press any key to continue.
453 */

Listing 1.1.4: plants_301340.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/301340 1662 ms 26668 KB
3
4 #include "plants.h"
5 #include <bits/stdc++.h>
6
7 static int n, k, q;
8
9 static std:: vector<int> r;
10 static std:: vector<int> x;
11 static std:: vector<int> y;
12 static std:: vector<int> answer;
13
14 // ---------------------------------------------------
15
16 #define low(i) (i<<(__builtin_clz(i)-31+n_bits))-(1<<n_bits)
17 #define high(i) ((i+1)<<(__builtin_clz(i)-31+n_bits))-(1<<n_bits)-1
18
19 using namespace std;
20
21 vector<int> tallest;
22 vector<int> shortest;
23
24 int n_global;
25
26 const int n_bits=20;
27 const int inf = 1e9;
28
29 int arr[1<<(n_bits+1)];
30 int lazyadd[1<<(n_bits+1)];
31
32 struct segtree
33 {
34 segtree(){}
35 void build(vector<int> v)
36 {
37 assert(v.size()<(1<<n_bits));
38 for(int i=0; i<(1<<n_bits); i++)
39 {
40 arr[i+(1<<n_bits)] = (i<(int)v.size() ? v[i] : inf);
41 }
42 for(int i=(1<<n_bits)-1; i>=1; i--)
43 {
44 arr[i] = min(arr[2*i], arr[2*i+1]);
45 }
46 for(int i=0; i<(1<<(n_bits+1)); i++)
47 {
48 lazyadd[i] = 0;
49 }
50 }
51
52 int value(int node)
53 {
54 arr[node] += lazyadd[node];
CAPITOLUL 1. IOI 2020 16

55 if(node<(1<<n_bits))
56 {
57 lazyadd[2*node] += lazyadd[node];
58 lazyadd[2*node+1] += lazyadd[node];
59 }
60 lazyadd[node] = 0;
61 return arr[node];
62 }
63
64 void update(int node, int left, int right, int change)
65 {
66 if(right>=high(node) && left<=low(node))
67 {
68 lazyadd[node] += change;
69 }
70 else
71 if(right<low(node) || left>high(node))
72 {
73 return;
74 }
75 else
76 {
77 update(2*node, left, right, change);
78 update(2*node+1, left, right, change);
79 arr[node] = min(value(node*2), value(node*2+1));
80 }
81 }
82
83 void decr(int left, int right)
84 {
85 update(1, left, right, -1);
86 }
87
88 int find_zero(int node)
89 {
90 if(value(node)!=0)
91 {
92 return -1;
93 }
94 if(node >= (1<<n_bits))
95 {
96 return arr[node]==0 ? node-(1<<n_bits) : -1;
97 }
98 int x = find_zero(node*2);
99 if(x!=-1) return x;
100 return find_zero(node*2+1);
101 }
102 };
103
104 // gives you the lexi smallest ordering given the doubled array r
105 vector<int> lexi_smallest(int k, vector<int> r)
106 {
107 segtree s;
108 s.build(r);
109 vector<int> ret;
110 ret.resize(r.size());
111 queue<int> stash;
112
113 for(int i=0; i<(int)r.size(); i++)
114 {
115 int p = s.find_zero(1);
116 while(p==-1)
117 {
118 s.decr(stash.front(), 2*n_global-1);
119 p = s.find_zero(1);
120 stash.pop();
121 }
122 ret[p] = i;
123 s.update(1, p, p, 1e9);
124
125 s.decr(max(0, p-k+1), p);
126 if(p<k-1)
127 {
128 stash.push(p-k+1+2*n_global);
129 }
130 }
CAPITOLUL 1. IOI 2020 17

131
132 return ret;
133 }
134
135 void init(int k, std::vector<int> r)
136 {
137 n_global = r.size();
138 for(int i=0; i<n_global; i++)
139 {
140 r.push_back(r[i]);
141 }
142 tallest = lexi_smallest(k,r);
143 for(int &i: r) i = k-1-i;
144 shortest = lexi_smallest(k,r);
145 }
146
147 int compare_plants(int x, int y)
148 {
149 if(x>y) return -compare_plants(y,x);
150 if(tallest[x]>tallest[y] || shortest[y]>shortest[x+n_global]) return -1;
151 if(shortest[x]>shortest[y] || tallest[y]>tallest[x+n_global]) return 1;
152 return 0;
153 }
154
155 // ---------------------------------------------------
156
157 int main()
158 {
159 std::clock_t c_start = std::clock();
160 auto t_start = std::chrono::high_resolution_clock::now();
161
162 auto t1 = clock();
163
164 std::freopen("../tests/7-15.in", "r", stdin) ;
165 std::freopen("plants.out", "w", stdout) ;
166
167 assert(scanf("%d%d%d", &n, &k, &q) == 3);
168
169 r.resize(n);
170 answer.resize(q);
171 for (int i = 0; i < n; i++)
172 {
173 int value;
174 assert(scanf("%d", &value) == 1);
175 r[i] = value;
176 }
177 x.resize(q);
178 y.resize(q);
179 for (int i = 0; i < q; i++)
180 {
181 assert(scanf("%d%d", &x[i], &y[i]) == 2);
182 }
183 fclose(stdin);
184
185 auto t2 = clock();
186
187 init(k, r);
188 for (int i = 0; i < q; i++)
189 {
190 answer[i] = compare_plants(x[i], y[i]);
191 }
192
193 auto t3 = clock();
194
195 for (int i = 0; i < q; i++)
196 {
197 printf("%d\n", answer[i]);
198 }
199
200 fclose(stdout);
201
202 auto t4 = clock();
203
204 std::clock_t c_end = std::clock();
205 auto t_end = std::chrono::high_resolution_clock::now();
206
CAPITOLUL 1. IOI 2020 18

207 // reset console output


208 freopen("CON", "w", stdout);
209
210 //std::cout <<result<<’\n’<<’\n’;
211 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
212 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
213 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
214
215 std::cout << std::fixed << std::setprecision(2)
216 << "\nCPU time used: "
217 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
218 << "Wall clock time passed: "
219 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
220 << " ms\n";
221
222 return 0;
223 }
224 /*
225 t2-t1 = 0.952
226 t3-t2 = 6.884
227 t4-t3 = 0.091
228
229 CPU time used: 7927.00 ms
230 Wall clock time passed: 7927.95 ms
231
232 Process returned 0 (0x0) execution time : 7.964 s
233 Press any key to continue.
234 */

Listing 1.1.5: plants_304504.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/304504 366 ms 12316 KB
3
4 #include "plants.h"
5 #include <bits/stdc++.h>
6
7 static int n, k, q;
8
9 static std:: vector<int> r;
10 static std:: vector<int> x;
11 static std:: vector<int> y;
12 static std:: vector<int> answer;
13
14 // ---------------------------------------------------
15
16 using namespace std;
17
18 const int n_bits=18;
19 const int inf = 1e9;
20 const int n_max = 2e5+5;
21
22 int arr[1<<(n_bits+1)];
23 int lazyadd[1<<(n_bits+1)];
24 int p_lt[n_max];
25 int p_rg[n_max];
26 int stash[n_max]; // a manual queue
27 int tallest[n_max];
28 int shortest[n_max];
29
30 struct segtree
31 {
32 int node = 1;
33 int lazy = 0;
34 segtree(){}
35
36 void build(vector<int> &v)
37 {
38 assert(v.size()<(1<<n_bits));
39 for(int i=0; i<(1<<n_bits); i++)
40 {
41 arr[i+(1<<n_bits)] = (i<(int)v.size() ? v[i] : inf);
42 }
43 for(int i=(1<<n_bits)-1; i>=1; i--)
44 {
CAPITOLUL 1. IOI 2020 19

45 arr[i] = min(arr[2*i], arr[2*i+1]);


46 }
47 for(int i=0; i<(1<<(n_bits+1)); i++)
48 {
49 lazyadd[i] = 0;
50 }
51 }
52
53 void prefix_update(int x, int change)
54 {
55 if(x==-1) return;
56 x += (1<<n_bits);
57 lazyadd[x] += change;
58 for(int i=0; i<n_bits; i++)
59 {
60 if(x&1)
61 {
62 lazyadd[x-1] += change;
63 }
64 arr[x/2] = min(arr[x]+lazyadd[x], arr[x^1]+lazyadd[x^1]);
65 x=x/2;
66 }
67 }
68
69 void decr(int left, int right)
70 {
71 prefix_update(left-1, 1);
72 prefix_update(right,-1);
73 }
74
75 int find_and_remove_zero()
76 {
77 node = 1;
78 lazy = 0;
79 for(int i=0; i<n_bits; i++)
80 {
81 lazy += lazyadd[node];
82 node *= 2;
83 if(lazy + lazyadd[node] + arr[node]) node++;
84 }
85 int ans = node - (1<<n_bits);
86 lazyadd[node] = 1e9;
87 for(int i=0; i<n_bits; i++)
88 {
89 arr[node/2] = min(arr[node]+lazyadd[node], arr[node^1]+lazyadd[node^1]);
90 node = node/2;
91 }
92 return ans;
93 }
94 };
95
96 void lexi_smallest(int k, vector<int> &r, int* ptr, int* ord)
97 {
98 segtree s;
99 s.build(r);
100 int n = r.size();
101 stash[0] = n;
102 ord[n] = -1;
103 int* fr = stash+1;
104 int* bk = stash+1;
105 for(int i=0; i<(int)r.size(); i++)
106 {
107 while(lazyadd[1] + arr[1])
108 {
109 s.decr((*fr++)-k+1+n, n-1);
110 }
111 int p = s.find_and_remove_zero();
112 ord[p] = i;
113 if(p<k-1)
114 {
115 s.prefix_update(p, -1);
116 *bk++ = p;
117 }
118 else
119 {
120 s.decr(p-k+1, p);
CAPITOLUL 1. IOI 2020 20

121 }
122 ptr[p] = ord[ *(fr-1) ];
123 }
124 }
125
126 void init(int k, std::vector<int> r)
127 {
128 lexi_smallest(k,r,p_lt, tallest);
129 for(int &i: r) i = k-1-i;
130 lexi_smallest(k,r,p_rg, shortest);
131 }
132
133 int compare_plants(int x, int y)
134 {
135 if(x>y) return -compare_plants(y,x);
136 if(tallest[x]>tallest[y] || p_rg[y]>=shortest[x]) return -1;
137 if(shortest[x]>shortest[y] || p_lt[y]>=tallest[x]) return 1;
138 return 0;
139 }
140
141 // ---------------------------------------------------
142
143 int main()
144 {
145 std::clock_t c_start = std::clock();
146 auto t_start = std::chrono::high_resolution_clock::now();
147
148 auto t1 = clock();
149
150 std::freopen("../tests/7-15.in", "r", stdin) ;
151 std::freopen("plants.out", "w", stdout) ;
152
153 assert(scanf("%d%d%d", &n, &k, &q) == 3);
154
155 r.resize(n);
156 answer.resize(q);
157 for (int i = 0; i < n; i++)
158 {
159 int value;
160 assert(scanf("%d", &value) == 1);
161 r[i] = value;
162 }
163 x.resize(q);
164 y.resize(q);
165 for (int i = 0; i < q; i++)
166 {
167 assert(scanf("%d%d", &x[i], &y[i]) == 2);
168 }
169 fclose(stdin);
170
171 auto t2 = clock();
172
173 init(k, r);
174 for (int i = 0; i < q; i++)
175 {
176 answer[i] = compare_plants(x[i], y[i]);
177 }
178
179 auto t3 = clock();
180
181 for (int i = 0; i < q; i++)
182 {
183 printf("%d\n", answer[i]);
184 }
185
186 fclose(stdout);
187
188 auto t4 = clock();
189
190 std::clock_t c_end = std::clock();
191 auto t_end = std::chrono::high_resolution_clock::now();
192
193 // reset console output
194 freopen("CON", "w", stdout);
195
196 //std::cout <<result<<’\n’<<’\n’;
CAPITOLUL 1. IOI 2020 21

197 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
198 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
199 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
200
201 std::cout << std::fixed << std::setprecision(2)
202 << "\nCPU time used: "
203 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
204 << "Wall clock time passed: "
205 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
206 << " ms\n";
207
208 return 0;
209 }
210 /*
211 t2-t1 = 1.035
212 t3-t2 = 1.15
213 t4-t3 = 0.089
214
215 CPU time used: 2274.00 ms
216 Wall clock time passed: 2273.99 ms
217
218 Process returned 0 (0x0) execution time : 2.326 s
219 Press any key to continue.
220 */

Listing 1.1.6: plants_308350.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/308350 1424 ms 18408 KB
3
4 #include <bits/stdc++.h>
5
6 static int n, k, q;
7
8 static std:: vector<int> r;
9 static std:: vector<int> x;
10 static std:: vector<int> y;
11 static std:: vector<int> answer;
12
13 // ---------------------------------------------------
14
15 using namespace std;
16
17 #define f first
18 #define s second
19 #define lc (rt<<1)
20 #define rc (rt<<1|1)
21 #define pb push_back
22 #define cl(a, b) memset(a, b, sizeof(a))
23
24 typedef long long ll;
25 typedef pair<int, int> pi;
26 typedef pair<int, pi> pii;
27 typedef vector<int> vi;
28 typedef vector<ll> vl;
29 typedef vector<pi> vii;
30 typedef vector<pii> viii;
31
32 const int inf = 0x3F3F3F3F;
33 const ll infl = 0x3F3F3F3F3F3F3F3FLL;
34 const int mod = 1e9 + 7, MM = 2e5+5;
35
36 int N, K, lz[6*MM], mi[6*MM], stk[MM], tp=-1; vi ord1, ord2;
37
38 void pushdn(int rt)
39 {
40 mi[lc]+=lz[rt]; mi[rc]+=lz[rt];
41 lz[lc]+=lz[rt]; lz[rc]+=lz[rt]; lz[rt]=0;
42 }
43
44 void upd(int L, int R, int l, int r, int val, int rt)
45 {
46 if(r < L || l > R) return;
47 if(l <= L && R <= r){ mi[rt]+=val; lz[rt]+=val; return; }
48 if(lz[rt]) pushdn(rt);
CAPITOLUL 1. IOI 2020 22

49 int mid = (L+R)/2;


50 if(l <= mid) upd(L, mid, l, r, val, lc);
51 if(r > mid) upd(mid+1, R, l, r, val, rc);
52 mi[rt] = min(mi[lc], mi[rc]);
53 }
54
55 int qry(int L, int R, int rt)
56 {
57 if(mi[rt] > 0) return -1;
58 if(L==R) return L;
59 if(lz[rt]) pushdn(rt);
60 int mid = (L+R)/2;
61 if(mi[lc] > 0) return qry(mid+1, R, rc);
62 return qry(L, mid, lc);
63 }
64
65 vi solve(vi& r)
66 {
67 vi ret(N); cl(lz, 0); cl(mi, 0);
68 for(int i=0; i<N; i++)
69 upd(0, N-1, i, i, r[i], 1);
70 for(int i=0; i<N; i++)
71 {
72 int pos = qry(0, N-1, 1);
73 while(pos < 0 && tp >= 0)
74 {
75 upd(0, N-1, stk[tp--], N-1, -1, 1);
76 pos = qry(0, N-1, 1);
77 }
78 ret[pos] = i;
79 upd(0, N-1, pos, pos, inf, 1);
80 upd(0, N-1, pos-K+1, pos-1, -1, 1);
81 if(pos-K+1<0) stk[++tp] = pos+N-K;
82 }
83 // for(int x: ret) cout << x << " ";
84 // cout << endl;
85 return ret;
86 }
87
88 void init(int k, vi r)
89 {
90 r.insert(r.end(), r.begin(), r.end());
91 N = r.size(); K = k;
92 ord1 = solve(r);
93 for(int &x: r) x = k-1-x;
94 ord2 = solve(r);
95 }
96
97 int compare_plants(int x, int y)
98 {
99 if(x > y) return -compare_plants(y, x);
100 if(ord1[x] > ord1[y] || ord2[x+N/2] < ord2[y]) return -1;
101 if(ord2[x] > ord2[y] || ord1[x+N/2] < ord1[y]) return 1;
102 return 0;
103 }
104
105 // ---------------------------------------------------
106
107 int main()
108 {
109 std::clock_t c_start = std::clock();
110 auto t_start = std::chrono::high_resolution_clock::now();
111
112 auto t1 = clock();
113
114 std::freopen("../tests/7-15.in", "r", stdin) ;
115 std::freopen("plants.out", "w", stdout) ;
116
117 assert(scanf("%d%d%d", &n, &k, &q) == 3);
118
119 r.resize(n);
120 answer.resize(q);
121 for (int i = 0; i < n; i++)
122 {
123 int value;
124 assert(scanf("%d", &value) == 1);
CAPITOLUL 1. IOI 2020 23

125 r[i] = value;


126 }
127 x.resize(q);
128 y.resize(q);
129 for (int i = 0; i < q; i++)
130 {
131 assert(scanf("%d%d", &x[i], &y[i]) == 2);
132 }
133 fclose(stdin);
134
135 auto t2 = clock();
136
137 init(k, r);
138 for (int i = 0; i < q; i++)
139 {
140 answer[i] = compare_plants(x[i], y[i]);
141 }
142
143 auto t3 = clock();
144
145 for (int i = 0; i < q; i++)
146 {
147 printf("%d\n", answer[i]);
148 }
149
150 fclose(stdout);
151
152 auto t4 = clock();
153
154 std::clock_t c_end = std::clock();
155 auto t_end = std::chrono::high_resolution_clock::now();
156
157 // reset console output
158 freopen("CON", "w", stdout);
159
160 //std::cout <<result<<’\n’<<’\n’;
161 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
162 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
164
165 std::cout << std::fixed << std::setprecision(2)
166 << "\nCPU time used: "
167 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
168 << "Wall clock time passed: "
169 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
170 << " ms\n";
171
172 return 0;
173 }
174 /*
175 t2-t1 = 1.086
176 t3-t2 = 3.4
177 t4-t3 = 0.089
178
179 CPU time used: 4575.00 ms
180 Wall clock time passed: 4575.25 ms
181
182 Process returned 0 (0x0) execution time : 4.627 s
183 Press any key to continue.
184 */

1.1.3 *Rezolvare detaliat 

1.2 supertrees
Problema 2 - Connecting Supertrees 100 de puncte
Authors: Ranald Lam Yun Shao and Ling Yan Hao

Gardens by the Bay este un mare parc natural din Singapore. În parc sunt turnuri, cunoscute
ca supercopaci. Aceste turnuri sunt numerotate de la 0 la n  1. Vrem s  construim o mulµime
CAPITOLUL 1. IOI 2020 24

constând în zero sau mai multe poduri. Fiecare pod conecteaz  o pereche de turnuri distincte ³i
poate  traversat în ambele direcµii. Nu este permis ca dou  poduri s  conecteze aceea³i pereche
de turnuri.
Un drum de la un turn x la un turn y este o secvenµ  format  din unul sau mai multe turnuri
astfel încât:
a primul element din secvenµ  este x,
a ultimul element din secvenµ  este y ,
a toate elementele din secvenµ  sunt distincte, ³i
a ecare dou  elemente consecutive (turnuri) din secvenµ  sunt conectate printr-un pod.

Observaµi c , prin deniµie, exist  exact un drum de la un turn la el însu³i ³i c  num rul de
drumuri de la turnul i la turnul j este exact acela³i ca num rul de drumuri de la turnul j la turnul
i.
Arhitectul sef responsabil cu designul î³i dore³te ca podurile s  e construite astfel încât oricare
ar  0 & i, j & n  1 s  existe exact pij  drumuri distincte de la turnul i la turnul j , unde
0 & pij  & 3.
Construiµi o mulµime de poduri care satisfac condiµiile arhitectului ³ef, sau determinaµi c  acest
lucru este imposibil.

Detalii de implementare
Trebuie s  implementaµi urm toarea funcµie:

int construct(int[][] p)

a p: o matrice de dimensiuni n  n respectând condiµiile arhitectului ³ef.


a Dac  o construcµie este posibil , funcµia va apela exact o dat  funcµia build (vezi mai jos)
pentru a raporta construcµia, dup  care va returna 1.
a Altfel, procedura va returna 0, f r  s  apeleze niciodat  funcµia build.
a Aceast  funcµie va  apelat  exact o dat .
Funcµia build este denit  dupa cum urmeaz :

void build(int[][] b)

a b: o matrice de dimensiuni n  n, unde bij  semnic  existenµa unui pod ce conecteaz 


turnurile i ³i j , altfel avem bij  0.
a Observaµi c  aceast  matrice trebuie s  satisfac  bij  bj i oricare ar  0 & i, j & n  1
si bii 0 oricare ar  0 & i & n  1.

Exemple
Exemplu 1 S  consider m urm torul apel:
construct([[1, 1, 2, 2], [1, 1, 2, 2], [2, 2, 1, 2], [2, 2, 2, 1]])

Acesta înseamn  c  trebuie s  existe exact un drum de la trunul 0 la turnul 1, iar pentru toate
celelalte perechi de turnuri x, y  astfel încât 0 & x $ y & 3, trebuie s  existe exact dou  drumuri
de la turnul x la turnul y .
Condiµiile arhitectului ³ef pot  întrunite construind 4 poduri, conectand perechile de turnuri
(0,1), (1,2), (1,3) ³i (2,3).
Pentru a raporta aceasta solutie, functia construct trebuie sa faca urmatorul apel:
a build([[0,1,0,0], [1,0,1,1], [0,1,0,1], [0,1,1,0]])
CAPITOLUL 1. IOI 2020 25

“i apoi s  returneze 1.
În acest caz exist  mai multe construcµii care respect  condiµiile, toate ind considerate corecte.

Exemplu 2 S  consider m urm torul apel:


construct([[1, 0], [0, 1]])

Acesta înseamn  c  nu trebuie s  e nicio modalitate de a c l tori de la un turn la cel lalt.


Singura metod  de a satisface condiµiile în acest caz este s  nu construim niciun pod.
Prin urmare, funcµia construct trebuie s  fac  urm torul apel:
a build([[0, 0], [0, 0]])
Dup  care funcµia construct va returna 1.

Exemplu 3 S  consider m urm torul apel:


construct([[1, 3], [3, 1]])

Acesta înseamn  c  trebuie s  existe exact 3 drumuri între turnul 0 ³i turnul 1. în acest caz,
condiµiile nu pot  satisf cute. Prin urmare, funcµia construct trebuie s  returneze 0 f r  s 
apeleze niciodat  funcµia build.

Restricµii
a 1 & n & 1000
a pii 1 (oricare ar  0 & i & n  1)
a pij  pj i (oricare ar  0 & i, j & n  1)
a 0 & pij  & 3 (oricare ar  0 & i, j & n  1)

Subtaskuri
1. (11 puncte) pij  1 (oricare ar  0 & i, j & n  1)
2. (10 puncte) pij  0 sau 1 (oricare ar  0 & i, j & n  1)
3. (19 puncte) pij  0 sau 2 (oricare ar  i j j , 0 & i, j & n  1)
4. (35 de puncte) 0 & pij  & 2 (oricare ar  0 & i, j & n  1) ³i exist  cel puµin o construcµie
care satisface condiµiile.
5. (21 de puncte) 0 & pij  & 2 (oricare ar  0 & i, j & n  1)
6. (4 puncte) F r  restricµii suplimentare.

Sample grader
Graderul cite³te inputul în urm torul format:

a linia 1: n
a linia 2  i (0 & i & n  1): pi0 pi1 ... pin  1
Outputul graderului are urm torul format:
a linia 1: valoarea returnat  de construct.
Dac  valoarea returnat  de construct este 1, atunci graderul mai a³eaz  urm toarele:
a linia 2  i (0 & i & n  1): bi0 bi1 ... bin  1
CAPITOLUL 1. IOI 2020 26

Timp maxim de executare/test: 1.0 secunde


Memorie: total 2000 MB

1.2.1 Indicaµii de rezolvare

Subtask 1
Construct any tree on nodes.

Subtask 2
We know that pij  0 if and only if i and j belong to dierent connected components. If
pij  % 0 and pj k  % 0, then we must have pik  % 0; we may return impossible if this
condition is not satised.
Once this condition is satised, we proceed to identify the connected components and construct
any tree for each connected component.

Subtask 3
We proceed to identify the connected components as per the previous section. Instead of con-
structing a tree through each connected component, we construct a cycle instead.
Take note that connected components cannot have size 2; there are no cycles of length 2.

Subtask 4
As with the previous subtasks, we deal with each connected component separately.
For each i, consider the set of all j such that pij  1. For each of these sets, we construct
any tree through these nodes. For each of these trees, assign a special node which we will call the
`root'.

For each connected component, we now construct a cycle through these roots.

Subtask 5
We need to check for an additional condition: if pij  1 and pj k  1, then we must have
pik  1.
The previous checks from subtask 3 (consistency of connected components, no cycles of length
2) still apply as usual.

Subtask 6
¬ ¬
If pij  3 for some pair i, j , then there must be another pair i , j  such that pi j 
¬ ¬
' 4.
Therefore, this cannot occur.
We simply return 0 whenever this occurs.

1.2.2 Coduri surs 

Listing 1.2.1: graderSupertrees-sandbox.cpp


1 #include "supertrees.h"
2
3 static int n;
4 static std::vector<std::vector<int>> p;
5 static std::vector<std::vector<int>> b;
6 static bool called = false;
7
8 // ---------------------------------------------------
9
10 #include <bits/stdc++.h>
CAPITOLUL 1. IOI 2020 27

11
12 using namespace std;
13
14 int construct(vector<vector<int>> p)
15 {
16 int n = p.size();
17 for (int i = 0; i < n; i++)
18 {
19 for (int j = 0; j < n; j++)
20 {
21 if (p[i][j] == 3) return 0;
22 }
23 }
24
25 vector<vector<int>> answer;
26 for (int i = 0; i < n; i++)
27 {
28 vector<int> row;
29 row.resize(n);
30 answer.push_back(row);
31 }
32
33 vector<bool> visited(n);
34 vector<int> componentIndex(n);
35 vector<int> lineIndex(n);
36 for (int aa = 0; aa < n; aa++)
37 {
38 if (visited[aa]) continue;
39 queue<int> qcomponent;
40 qcomponent.push(aa);
41 vector<int> cycle;
42 while (!qcomponent.empty())
43 {
44 int x = qcomponent.front();
45 qcomponent.pop();
46 if (visited[x]) continue;
47 vector<int> curline;
48 queue<int> qline;
49 qline.push(x);
50 cycle.push_back(x);
51 while (!qline.empty())
52 {
53 int y = qline.front();
54 qline.pop();
55 if (visited[y]) continue;
56 curline.push_back(y);
57 visited[y] = true;
58 componentIndex[y] = aa;
59 lineIndex[y] = x;
60 for (int i = 0; i < n; i++)
61 {
62 if (p[y][i] == 2)
63 qcomponent.push(i);
64 else if (p[y][i] == 1)
65 qline.push(i);
66 }
67 }
68
69 // for (int i : curline) printf("%d ", i); printf("\n");
70 if (curline.size() > 1)
71 {
72 for (int i = 0; i < (int)curline.size() - 1; i++)
73 {
74 int a = curline[i];
75 int b = curline[i + 1];
76 answer[a][b] = answer[b][a] = 1;
77 }
78 }
79 }
80
81 if (cycle.size() == 2) return 0;
82 if (cycle.size() == 1) continue;
83 for (int i = 0; i < (int)cycle.size(); i++)
84 {
85 int a = cycle[i];
86 int b = cycle[(i + 1) % cycle.size()];
CAPITOLUL 1. IOI 2020 28

87 answer[a][b] = answer[b][a] = 1;
88 }
89 }
90
91 for (int i = 0; i < n; i++)
92 {
93 for (int j = i + 1; j < n; j++)
94 {
95 if (lineIndex[i] == lineIndex[j])
96 {
97 if (p[i][j] != 1) return 0;
98 }
99 else
100 if (componentIndex[i] == componentIndex[j])
101 {
102 if (p[i][j] != 2) return 0;
103 }
104 else
105 {
106 if (p[i][j] != 0) return 0;
107 }
108 }
109 }
110
111 build(answer);
112
113 return 1;
114 }
115
116 // ---------------------------------------------------
117
118 static void check(bool cond, std::string message)
119 {
120 if (!cond)
121 {
122 printf("%s\n", message.c_str());
123 fclose(stdout);
124 exit(0);
125 }
126 }
127
128 void build(std::vector<std::vector<int>> _b)
129 {
130 check(!called, "build is called more than once");
131 called = true;
132 check((int)_b.size() == n, "Invalid number of rows in b");
133 for (int i = 0; i < n; i++)
134 {
135 check((int)_b[i].size() == n, "Invalid number of columns in b");
136 }
137 b = _b;
138 }
139
140 int main()
141 {
142 std::clock_t c_start = std::clock();
143 auto t_start = std::chrono::high_resolution_clock::now();
144
145 auto t1 = clock();
146
147 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
148 std::freopen("supertrees.out", "w", stdout) ;
149
150 assert(scanf("%d", &n) == 1);
151
152 p.resize(n);
153 for (int i = 0; i < n; i++)
154 {
155 p[i].resize(n);
156 }
157
158 for (int i = 0; i < n; i++)
159 {
160 for (int j = 0; j < n; j++)
161 {
162 assert(scanf("%d", &p[i][j]) == 1);
CAPITOLUL 1. IOI 2020 29

163 }
164 }
165 fclose(stdin);
166
167 auto t2 = clock();
168
169 int possible = construct(p);
170
171 auto t3 = clock();
172
173 check(possible == 0 || possible == 1, "Invalid return value of construct");
174 if (possible == 1)
175 {
176 check(called, "construct returned 1 without calling build");
177 }
178 else
179 {
180 check(!called, "construct called build but returned 0");
181 }
182
183 printf("%d\n", possible);
184 if (possible == 1)
185 {
186 for (int i = 0; i < n; i++)
187 {
188 for (int j = 0; j < n; j++)
189 {
190 if (j)
191 {
192 printf(" ");
193 }
194 printf("%d", b[i][j]);
195 }
196 printf("\n");
197 }
198 }
199 fclose(stdout);
200
201
202 auto t4 = clock();
203
204 std::clock_t c_end = std::clock();
205 auto t_end = std::chrono::high_resolution_clock::now();
206
207 // reset console output
208 freopen("CON", "w", stdout);
209
210 //std::cout <<result<<’\n’<<’\n’;
211 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
212 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
213 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
214
215 std::cout << std::fixed << std::setprecision(2)
216 << "\nCPU time used: "
217 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
218 << "Wall clock time passed: "
219 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
220 << " ms\n";
221
222 return 0;
223 }
224 /*
225 t2-t1 = 1.468
226 t3-t2 = 0.016
227 t4-t3 = 0
228
229 CPU time used: 1484.00 ms
230 Wall clock time passed: 1484.36 ms
231
232 Process returned 0 (0x0) execution time : 1.531 s
233 Press any key to continue.
234 */

Listing 1.2.2: checkerSupertrees.cpp


CAPITOLUL 1. IOI 2020 30

1 #include "testlib.h"
2 #include <bits/stdc++.h>
3
4 using namespace std;
5
6 //const std::string output_secret = "1f0934cc-cf56-429c-b010-8533ab3ded9e";
7
8 const int MAXP = 3;
9 const int MAXB = 1;
10
11 int n;
12 std::vector<std::vector<int>> p, b, b_adj;
13 vector<vector<int>> actual_ways;
14 vector<int> visited;
15
16 void too_many(int src, int dst)
17 {
18 quitf(
19 _wa,
20 "Too many ways to get from %d to %d, should be %d found no less than %d",
21 src,
22 dst,
23 p[src][dst],
24 actual_ways[src][dst]
25 );
26 }
27
28 void dfs(int src, int x)
29 {
30 visited[x] = 1;
31 // printf("in %d / ", x);
32 actual_ways[src][x] += 1;
33 if (actual_ways[src][x] > p[src][x])
34 {
35 too_many(src, x);
36 }
37 for (int i = 0; i < (int) b_adj[x].size(); i++)
38 {
39 if (!visited[b_adj[x][i]])
40 {
41 dfs(src, b_adj[x][i]);
42 }
43 }
44 // printf("out %d / ", x);
45 visited[x] = 0;
46 }
47
48 int main()
49 //int main(int argc, char * argv[])
50 {
51 int argc=4;
52
53 char* argv[] =
54 {
55 (char*)"checker",
56 (char*)"../tests/4-17.in", // input
57 (char*)"../tests/4-17.out", // rezultat corect
58 (char*)"supertrees.out", // rezultat de verificat si acordat punctaj
59 };
60
61 cout<<"argc = "<<argc<<"\n";
62 for(int kk=0;kk<argc;kk++)
63 cout<<argv[kk]<<"\n";
64 cout<<"----------------------\n";
65
66 registerChecker("supertrees", argc, argv);
67
68 //readBothSecrets(output_secret);
69 //readBothGraderResults();
70
71 //inf.readLine(); // Input Secret
72 n = inf.readInt();
73
74 int actual_possible = ans.readInt();
75 int possible = ouf.readInt();
76
CAPITOLUL 1. IOI 2020 31

77 if (actual_possible != possible)
78 {
79 quitf(_wa, "Answer gives possible %d while actual possible %d",
80 possible, actual_possible);
81 }
82
83 if (!possible)
84 {
85 quit(_ok);
86 }
87
88 p.resize(n);
89 b.resize(n);
90 b_adj.resize(n);
91 for (int i = 0; i < n; i++)
92 {
93 p[i] = inf.readInts(n, 0, MAXP, "p_i_j");
94 b[i] = ouf.readInts(n, 0, MAXB, "b_i_j");
95 for (int j = 0; j < n; j++)
96 {
97 if (b[i][j])
98 {
99 b_adj[i].push_back(j);
100 }
101 }
102 }
103
104 // Check that b is symmetric
105 for (int i = 0; i < n; i++)
106 {
107 for (int j = 0; j < n; j++)
108 {
109 if (b[i][j] != b[j][i])
110 {
111 quitf(_wa,
112 "b is not symmetric: b[%d][%d] (%d) != b[%d][%d] (%d)",
113 i, j, b[i][j], j, i, b[j][i]);
114 }
115 }
116 }
117
118 // Check that b diagonal is 0
119 for (int i = 0; i < n; i++)
120 {
121 if (b[i][i] != 0)
122 {
123 quitf(_wa, "b[%d][%d] is not 0", i, i);
124 }
125 }
126
127 // Populate actual_ways
128 actual_ways.resize(n);
129 for (int i = 0; i < n; i++)
130 {
131 actual_ways[i].resize(n, 0);
132 }
133 visited.resize(n, 0);
134
135 for (int i = 0; i < n; i++)
136 {
137 dfs(i, i);
138 }
139
140 for (int i = 0; i < n; i++)
141 {
142 for (int j = 0; j < n; j++)
143 {
144 if (p[i][j] != actual_ways[i][j])
145 {
146 quitf(
147 _wa,
148 "Too few ways to get from %d to %d, should be %d found %d",
149 i,
150 j,
151 p[i][j],
152 actual_ways[i][j]
CAPITOLUL 1. IOI 2020 32

153 );
154 }
155 }
156 }
157
158 quit(_ok);
159 }
160 /*
161 argc = 4
162 checker
163 ../tests/4-17.in
164 ../tests/4-17.out
165 supertrees.out
166 ----------------------
167 1
168 Correct
169
170 Process returned 0 (0x0) execution time : 1.159 s
171 Press any key to continue.
172 */

Listing 1.2.3: supertrees_300160.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300160 314 ms 22264 KB
3
4 #include "supertrees.h"
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 static int n;
11 static std::vector<std::vector<int>> p;
12 static std::vector<std::vector<int>> b;
13 static bool called = false;
14
15 // ---------------------------------------------------
16
17 // limli-construct
18 int construct(vector<vector<int>> p)
19 {
20 int n = p.size();
21 for (int i = 0; i < n; i++)
22 {
23 for (int j = 0; j < n; j++)
24 {
25 if (p[i][j] == 3) return 0;
26 }
27 }
28
29 vector<vector<int>> answer;
30 for (int i = 0; i < n; i++)
31 {
32 vector<int> row;
33 row.resize(n);
34 answer.push_back(row);
35 }
36
37 vector<bool> visited(n);
38 vector<int> componentIndex(n);
39 vector<int> lineIndex(n);
40 for (int aa = 0; aa < n; aa++)
41 {
42 if (visited[aa]) continue;
43
44 queue<int> qcomponent;
45
46 qcomponent.push(aa);
47 vector<int> cycle;
48 while (!qcomponent.empty())
49 {
50 int x = qcomponent.front();
51 qcomponent.pop();
52 if (visited[x]) continue;
CAPITOLUL 1. IOI 2020 33

53
54 vector<int> curline;
55 queue<int> qline;
56
57 qline.push(x);
58 cycle.push_back(x);
59 while (!qline.empty())
60 {
61 int y = qline.front();
62 qline.pop();
63 if (visited[y]) continue;
64 curline.push_back(y);
65 visited[y] = true;
66 componentIndex[y] = aa;
67 lineIndex[y] = x;
68 for (int i = 0; i < n; i++)
69 {
70 if (p[y][i] == 2)
71 qcomponent.push(i);
72 else
73 if (p[y][i] == 1)
74 qline.push(i);
75 }
76 }
77
78 // for (int i : curline) printf("%d ", i); printf("\n");
79 if (curline.size() > 1)
80 {
81 for (int i = 0; i < (int)curline.size() - 1; i++)
82 {
83 int a = curline[i];
84 int b = curline[i + 1];
85 answer[a][b] = answer[b][a] = 1;
86 }
87 }
88 }
89
90 if (cycle.size() == 2) return 0;
91 if (cycle.size() == 1) continue;
92 for (int i = 0; i < (int)cycle.size(); i++)
93 {
94 int a = cycle[i];
95 int b = cycle[(i + 1) % cycle.size()];
96 answer[a][b] = answer[b][a] = 1;
97 }
98 }
99
100 for (int i = 0; i < n; i++)
101 {
102 for (int j = i + 1; j < n; j++)
103 {
104 if (lineIndex[i] == lineIndex[j])
105 {
106 if (p[i][j] != 1) return 0;
107 }
108 else
109 if (componentIndex[i] == componentIndex[j])
110 {
111 if (p[i][j] != 2) return 0;
112 }
113 else
114 {
115 if (p[i][j] != 0) return 0;
116 }
117 }
118 }
119
120 build(answer);
121 return 1;
122 }
123
124 // ---------------------------------------------------
125
126 static void check(bool cond, std::string message)
127 {
128 if (!cond)
CAPITOLUL 1. IOI 2020 34

129 {
130 printf("%s\n", message.c_str());
131 fclose(stdout);
132 exit(0);
133 }
134 }
135
136 void build(std::vector<std::vector<int>> _b)
137 {
138 check(!called, "build is called more than once");
139 called = true;
140 check((int)_b.size() == n, "Invalid number of rows in b");
141 for (int i = 0; i < n; i++)
142 {
143 check((int)_b[i].size() == n, "Invalid number of columns in b");
144 }
145 b = _b;
146 }
147
148 int main()
149 {
150 std::clock_t c_start = std::clock();
151 auto t_start = std::chrono::high_resolution_clock::now();
152
153 auto t1 = clock();
154
155 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
156 std::freopen("supertrees.out", "w", stdout) ;
157
158 assert(scanf("%d", &n) == 1);
159
160 p.resize(n);
161 for (int i = 0; i < n; i++)
162 {
163 p[i].resize(n);
164 }
165
166 for (int i = 0; i < n; i++)
167 {
168 for (int j = 0; j < n; j++)
169 {
170 assert(scanf("%d", &p[i][j]) == 1);
171 }
172 }
173 fclose(stdin);
174
175 auto t2 = clock();
176
177 int possible = construct(p);
178
179 auto t3 = clock();
180
181 check(possible == 0 || possible == 1, "Invalid return value of construct");
182 if (possible == 1)
183 {
184 check(called, "construct returned 1 without calling build");
185 }
186 else
187 {
188 check(!called, "construct called build but returned 0");
189 }
190
191 printf("%d\n", possible);
192 if (possible == 1)
193 {
194 for (int i = 0; i < n; i++)
195 {
196 for (int j = 0; j < n; j++)
197 {
198 if (j)
199 {
200 printf(" ");
201 }
202 printf("%d", b[i][j]);
203 }
204 printf("\n");
CAPITOLUL 1. IOI 2020 35

205 }
206 }
207 fclose(stdout);
208
209
210 auto t4 = clock();
211
212 std::clock_t c_end = std::clock();
213 auto t_end = std::chrono::high_resolution_clock::now();
214
215 // reset console output
216 freopen("CON", "w", stdout);
217
218 //std::cout <<result<<’\n’<<’\n’;
219 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
220 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
221 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
222
223 std::cout << std::fixed << std::setprecision(2)
224 << "\nCPU time used: "
225 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
226 << "Wall clock time passed: "
227 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
228 << " ms\n";
229
230 return 0;
231 }
232 /*
233 t2-t1 = 1.359
234 t3-t2 = 0.125
235 t4-t3 = 0.437
236
237 CPU time used: 1921.00 ms
238 Wall clock time passed: 1921.87 ms
239
240 Process returned 0 (0x0) execution time : 1.969 s
241 Press any key to continue.
242 */

Listing 1.2.4: supertrees_300292.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300292 272 ms 22284 KB
3
4 #include "supertrees.h"
5
6 #include <bits/stdc++.h>
7
8 static int n;
9 static std::vector<std::vector<int>> p;
10 static std::vector<std::vector<int>> b;
11 static bool called = false;
12
13 // ---------------------------------------------------
14
15 int construct(std::vector<std::vector<int>> P)
16 {
17 int N = int(P.size());
18 std::vector<std::vector<int>> answer(N, std::vector<int>(N, 0));
19
20 for (int i = 0; i < N; i++)
21 {
22 for (int j = 0; j < N; j++)
23 {
24 assert(P[i][j] == P[j][i]);
25 assert(0 <= P[i][j] && P[i][j] <= 3);
26 if (P[i][j] == 3) return false;
27 }
28 }
29
30 std::vector<bool> is_root(N, false);
31 // check rectangles of 1’s
32 for (int i = 0; i < N; i++)
33 {
34 assert(P[i][i] == 1);
CAPITOLUL 1. IOI 2020 36

35
36 int cc = 0;
37 while (P[i][cc] != 1) cc++;
38
39 if (cc != i)
40 {
41 if (P[i] != P[cc])
42 {
43 return false;
44 }
45
46 answer[i][cc] = answer[cc][i] = true;
47 }
48 else
49 {
50 is_root[i] = true;
51 }
52 }
53
54 // check rectangles of nonzero’s
55 for (int i = 0; i < N; i++)
56 {
57 if (!is_root[i]) continue;
58
59 int cc = 0;
60 while (P[i][cc] == 0) cc++;
61 for (int j = 0; j < N; j++)
62 {
63 if ((P[i][j] == 0) != (P[cc][j] == 0))
64 {
65 return false;
66 }
67 }
68
69 if (cc == i)
70 {
71 int prv = i;
72 for (int j = i+1; j < N; j++)
73 {
74 if (is_root[j] && P[i][j])
75 {
76 answer[prv][j] = answer[j][prv] = true;
77 prv = j;
78 }
79 }
80
81 if (prv == i)
82 {
83 // do nothing
84 }
85 else
86 if (answer[i][prv])
87 {
88 return false;
89 }
90 else
91 {
92 answer[i][prv] = answer[prv][i] = true;
93 }
94 }
95 }
96
97 build(answer); return true;
98 }
99
100 // ---------------------------------------------------
101
102 static void check(bool cond, std::string message)
103 {
104 if (!cond)
105 {
106 printf("%s\n", message.c_str());
107 fclose(stdout);
108 exit(0);
109 }
110 }
CAPITOLUL 1. IOI 2020 37

111
112 void build(std::vector<std::vector<int>> _b)
113 {
114 check(!called, "build is called more than once");
115 called = true;
116 check((int)_b.size() == n, "Invalid number of rows in b");
117 for (int i = 0; i < n; i++)
118 {
119 check((int)_b[i].size() == n, "Invalid number of columns in b");
120 }
121 b = _b;
122 }
123
124 int main()
125 {
126 std::clock_t c_start = std::clock();
127 auto t_start = std::chrono::high_resolution_clock::now();
128
129 auto t1 = clock();
130
131 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
132 std::freopen("supertrees.out", "w", stdout) ;
133
134 assert(scanf("%d", &n) == 1);
135
136 p.resize(n);
137 for (int i = 0; i < n; i++)
138 {
139 p[i].resize(n);
140 }
141
142 for (int i = 0; i < n; i++)
143 {
144 for (int j = 0; j < n; j++)
145 {
146 assert(scanf("%d", &p[i][j]) == 1);
147 }
148 }
149 fclose(stdin);
150
151 auto t2 = clock();
152
153 int possible = construct(p);
154
155 auto t3 = clock();
156
157 check(possible == 0 || possible == 1, "Invalid return value of construct");
158 if (possible == 1)
159 {
160 check(called, "construct returned 1 without calling build");
161 }
162 else
163 {
164 check(!called, "construct called build but returned 0");
165 }
166
167 printf("%d\n", possible);
168 if (possible == 1)
169 {
170 for (int i = 0; i < n; i++)
171 {
172 for (int j = 0; j < n; j++)
173 {
174 if (j)
175 {
176 printf(" ");
177 }
178 printf("%d", b[i][j]);
179 }
180 printf("\n");
181 }
182 }
183 fclose(stdout);
184
185
186 auto t4 = clock();
CAPITOLUL 1. IOI 2020 38

187
188 std::clock_t c_end = std::clock();
189 auto t_end = std::chrono::high_resolution_clock::now();
190
191 // reset console output
192 freopen("CON", "w", stdout);
193
194 //std::cout <<result<<’\n’<<’\n’;
195 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
196 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
197 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
198
199 std::cout << std::fixed << std::setprecision(2)
200 << "\nCPU time used: "
201 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
202 << "Wall clock time passed: "
203 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
204 << " ms\n";
205
206 return 0;
207 }
208 /*
209 t2-t1 = 1.5
210 t3-t2 = 0.141
211 t4-t3 = 0.593
212
213 CPU time used: 2234.00 ms
214 Wall clock time passed: 2234.37 ms
215
216 Process returned 0 (0x0) execution time : 2.281 s
217 Press any key to continue.
218 */

Listing 1.2.5: supertrees_300293.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300293 254 ms 26360 KB
3
4 #include "supertrees.h"
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 static int n;
11 static std::vector<std::vector<int>> p;
12 static std::vector<std::vector<int>> b;
13 static bool called = false;
14
15 // ---------------------------------------------------
16
17 bool possible = true;
18
19 bitset<1005> vis;
20 bitset<1005> onevis;
21 bitset<1005> isOne;
22
23 vector<vector<int>> components;
24 int compNum[1005];
25 vector<vector<int>> onecomponents;
26 int onecompNum[1005];
27
28 void dfs(int u)
29 {
30 vis[u] = 1;
31 compNum[u] = components.size();
32 components.back().push_back(u);
33 for(int v = 0; v < n; v++)
34 if(!vis[v] && p[u][v])
35 dfs(v);
36 }
37
38 void onedfs(int u)
39 {
40 onevis[u] = 1;
CAPITOLUL 1. IOI 2020 39

41 onecompNum[u] = onecomponents.size();
42 onecomponents.back().push_back(u);
43 for(int v = 0; v < n; v++)
44 if(!onevis[v] && p[u][v] == 1)
45 onedfs(v);
46 }
47
48 int construct(vector<vector<int>> p_)
49 {
50 p = p_;
51 n = p.size();
52
53 for (int i = 0; i < n; i++)
54 {
55 vector<int> row;
56 row.resize(n);
57 b.push_back(row);
58 }
59
60 vis.reset();
61
62 for(int i = 0; i < n; i++)
63 {
64 if(!vis[i])
65 {
66 components.push_back(vector<int>(0));
67 dfs(i);
68 }
69 }
70
71 for(int i = 0; i < n; i++)
72 {
73 if(!onevis[i])
74 {
75 onecomponents.push_back(vector<int>(0));
76 onedfs(i);
77 if(onecomponents.back().size() == 1)
78 {
79 onecompNum[i] = 0;
80 onecomponents.pop_back();
81 }
82 }
83 }
84
85 for(int i = 0; i < n; i++)
86 for(int j = 0; j < n; j++)
87 {
88 if(p[i][j] == 3) return 0;
89
90 if(compNum[i] == compNum[j] && p[i][j] == 0)
91 return 0;
92 else
93 if(compNum[i] != compNum[j] && p[i][j] > 0)
94 return 0;
95
96 if(onecompNum[i] == onecompNum[j] && onecompNum[i] != 0 && p[i][i] != 1)
97 return 0;
98 }
99
100 for(auto &v: components)
101 {
102 isOne.reset();
103 if(v.size() == 1)
104 continue;
105
106 vector<int> is[3];
107 for(auto &x: v)
108 if(onecompNum[x] == 0) is[2].push_back(x);
109
110 for(auto &x: v)
111 {
112 if(onecompNum[x] > 0)
113 {
114 auto &w = onecomponents[onecompNum[x]-1];
115 for(int i = 0; i+1 < (int)w.size(); i++)
116 {
CAPITOLUL 1. IOI 2020 40

117 b[w[i]][w[i+1]] = 1;
118 b[w[i+1]][w[i]] = 1;
119 }
120
121 is[2].push_back(w.back());
122 for(auto y: w)
123 onecompNum[y] = -1;
124 }
125 }
126
127 if(is[2].size() == 2) return 0;
128
129 if(is[2].size() > 2)
130 {
131 for(int i = 1; i < (int)is[2].size(); i++)
132 {
133 b[is[2][i-1]][is[2][i]] = 1;
134 b[is[2][i]][is[2][i-1]] = 1;
135 }
136
137 b[is[2][is[2].size()-1]][is[2][0]] = 1;
138 b[is[2][0]][is[2][is[2].size()-1]] = 1;
139 }
140 }
141
142 if(!possible)
143 return 0;
144
145 build(b);
146
147 return 1;
148 }
149
150 // ---------------------------------------------------
151
152 static void check(bool cond, std::string message)
153 {
154 if (!cond)
155 {
156 printf("%s\n", message.c_str());
157 fclose(stdout);
158 exit(0);
159 }
160 }
161
162 void build(std::vector<std::vector<int>> _b)
163 {
164 check(!called, "build is called more than once");
165 called = true;
166 check((int)_b.size() == n, "Invalid number of rows in b");
167 for (int i = 0; i < n; i++)
168 {
169 check((int)_b[i].size() == n, "Invalid number of columns in b");
170 }
171 b = _b;
172 }
173
174 int main()
175 {
176 std::clock_t c_start = std::clock();
177 auto t_start = std::chrono::high_resolution_clock::now();
178
179 auto t1 = clock();
180
181 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
182 std::freopen("supertrees.out", "w", stdout) ;
183
184 assert(scanf("%d", &n) == 1);
185
186 p.resize(n);
187 for (int i = 0; i < n; i++)
188 {
189 p[i].resize(n);
190 }
191
192 for (int i = 0; i < n; i++)
CAPITOLUL 1. IOI 2020 41

193 {
194 for (int j = 0; j < n; j++)
195 {
196 assert(scanf("%d", &p[i][j]) == 1);
197 }
198 }
199 fclose(stdin);
200
201 auto t2 = clock();
202
203 int possible = construct(p);
204
205 auto t3 = clock();
206
207 check(possible == 0 || possible == 1, "Invalid return value of construct");
208 if (possible == 1)
209 {
210 check(called, "construct returned 1 without calling build");
211 }
212 else
213 {
214 check(!called, "construct called build but returned 0");
215 }
216
217 printf("%d\n", possible);
218 if (possible == 1)
219 {
220 for (int i = 0; i < n; i++)
221 {
222 for (int j = 0; j < n; j++)
223 {
224 if (j)
225 {
226 printf(" ");
227 }
228 printf("%d", b[i][j]);
229 }
230 printf("\n");
231 }
232 }
233 fclose(stdout);
234
235
236 auto t4 = clock();
237
238 std::clock_t c_end = std::clock();
239 auto t_end = std::chrono::high_resolution_clock::now();
240
241 // reset console output
242 freopen("CON", "w", stdout);
243
244 //std::cout <<result<<’\n’<<’\n’;
245 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
246 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
247 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
248
249 std::cout << std::fixed << std::setprecision(2)
250 << "\nCPU time used: "
251 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
252 << "Wall clock time passed: "
253 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
254 << " ms\n";
255
256 return 0;
257 }
258 /*
259 t2-t1 = 1.422
260 t3-t2 = 0.188
261 t4-t3 = 0.484
262
263 CPU time used: 2094.00 ms
264 Wall clock time passed: 2093.73 ms
265
266 Process returned 0 (0x0) execution time : 2.156 s
267 Press any key to continue.
268 */
CAPITOLUL 1. IOI 2020 42

Listing 1.2.6: supertrees_300296.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300296 303 ms 24952 KB
3
4 #include "supertrees.h"
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 static int n;
11 static std::vector<std::vector<int>> p;
12 static std::vector<std::vector<int>> b;
13 static bool called = false;
14
15 // ---------------------------------------------------
16
17 #define all(c) ((c).begin()), ((c).end())
18
19 struct dsu
20 {
21 vector<vector<int>> components;
22 int n;
23 vector<int> par;
24 dsu(int n) : n(n), par(n)
25 {
26 iota(all(par), 0);
27 components.resize(n);
28 for(int i = 0; i < n; i++) components[i] = {i};
29 }
30
31 int root(int x)
32 {
33 return (x == par[x]) ? x : (par[x] = root(par[x]));
34 }
35
36 void merge(int x, int y)
37 {
38 x = root(x); y = root(y);
39 if(x == y) return;
40 copy(all(components[x]), back_inserter(components[y]));
41 components[x].clear();
42 par[x] = y;
43 }
44 };
45
46 int construct(vector<vector<int>> p)
47 {
48 int n = p.size();
49 vector<vector<int>> adj(n, vector<int>(n, 0));
50
51 function<void(int, int)> add_edge =
52 [&](int x, int y)
53 {
54 adj[x][y] = adj[y][x] = 1;
55 };
56
57 dsu D(n);
58 for(int i = 0; i < n; i++)
59 {
60 for(int j = i + 1; j < n; j++)
61 {
62 if(p[i][j] > 0) D.merge(i, j);
63 }
64 }
65
66 for(int i = 0; i < n; i++)
67 if(D.par[i] == i)
68 {
69 vector<int> v = D.components[i];
70 for(int x : v)
71 for(int y : v)
72 if(p[x][y] == 0 || p[x][y] == 3)
73 return 0;
74
CAPITOLUL 1. IOI 2020 43

75 dsu D2(n);
76 for(int x : v)
77 for(int y : v)
78 if(x != y && p[x][y] == 1)
79 D2.merge(x, y);
80
81 vector<int> cycle;
82 for(int x : v)
83 if(D2.par[x] == x)
84 {
85 cycle.push_back(x);
86 vector<int> U = D2.components[x];
87 int lst = -1;
88 for(int a : U)
89 {
90 for(int b : U)
91 if(p[a][b] != 1)
92 return 0;
93
94 if(lst != -1) add_edge(a, lst);
95 lst = a;
96 }
97 }
98
99 int m = cycle.size();
100 if(m == 2) return 0;
101 if(m > 1)
102 for(int k = 0; k < m; k++)
103 add_edge(cycle[k], cycle[(k + 1) % m]);
104 }
105
106 build(adj);
107
108 return 1;
109 }
110
111 // ---------------------------------------------------
112
113 static void check(bool cond, std::string message)
114 {
115 if (!cond)
116 {
117 printf("%s\n", message.c_str());
118 fclose(stdout);
119 exit(0);
120 }
121 }
122
123 void build(std::vector<std::vector<int>> _b)
124 {
125 check(!called, "build is called more than once");
126 called = true;
127 check((int)_b.size() == n, "Invalid number of rows in b");
128 for (int i = 0; i < n; i++)
129 {
130 check((int)_b[i].size() == n, "Invalid number of columns in b");
131 }
132 b = _b;
133 }
134
135 int main()
136 {
137 std::clock_t c_start = std::clock();
138 auto t_start = std::chrono::high_resolution_clock::now();
139
140 auto t1 = clock();
141
142 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
143 std::freopen("supertrees.out", "w", stdout) ;
144
145 assert(scanf("%d", &n) == 1);
146
147 p.resize(n);
148 for (int i = 0; i < n; i++)
149 {
150 p[i].resize(n);
CAPITOLUL 1. IOI 2020 44

151 }
152
153 for (int i = 0; i < n; i++)
154 {
155 for (int j = 0; j < n; j++)
156 {
157 assert(scanf("%d", &p[i][j]) == 1);
158 }
159 }
160 fclose(stdin);
161
162 auto t2 = clock();
163
164 int possible = construct(p);
165
166 auto t3 = clock();
167
168 check(possible == 0 || possible == 1, "Invalid return value of construct");
169 if (possible == 1)
170 {
171 check(called, "construct returned 1 without calling build");
172 }
173 else
174 {
175 check(!called, "construct called build but returned 0");
176 }
177
178 printf("%d\n", possible);
179 if (possible == 1)
180 {
181 for (int i = 0; i < n; i++)
182 {
183 for (int j = 0; j < n; j++)
184 {
185 if (j)
186 {
187 printf(" ");
188 }
189 printf("%d", b[i][j]);
190 }
191 printf("\n");
192 }
193 }
194 fclose(stdout);
195
196
197 auto t4 = clock();
198
199 std::clock_t c_end = std::clock();
200 auto t_end = std::chrono::high_resolution_clock::now();
201
202 // reset console output
203 freopen("CON", "w", stdout);
204
205 //std::cout <<result<<’\n’<<’\n’;
206 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
207 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
208 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
209
210 std::cout << std::fixed << std::setprecision(2)
211 << "\nCPU time used: "
212 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
213 << "Wall clock time passed: "
214 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
215 << " ms\n";
216
217 return 0;
218 }
219 /*
220 t2-t1 = 1.625
221 t3-t2 = 0.063
222 t4-t3 = 0.453
223
224 CPU time used: 2141.00 ms
225 Wall clock time passed: 2140.62 ms
226
CAPITOLUL 1. IOI 2020 45

227 Process returned 0 (0x0) execution time : 2.187 s


228 Press any key to continue.
229 */

Listing 1.2.7: supertrees_300447.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300447 261 ms 26276 KB
3
4 #include "supertrees.h"
5
6 #include<bits/stdc++.h>
7
8 using namespace std;
9
10 static int n;
11 static std::vector<std::vector<int>> pp;
12 static std::vector<std::vector<int>> b;
13 static bool called = false;
14
15 // ---------------------------------------------------
16
17 typedef long long ll;
18
19 #define pb push_back
20 #define mp make_pair
21 #define fi first
22 #define se second
23
24 const ll NN = 1e3 + 5;
25
26 ll bisa = 1, p[NN], x[NN], kel[NN], ket[NN];
27
28 std::vector<std::vector<int>> answer;
29 vector<ll> v[NN];
30 std::vector<std::vector<int>> px;
31
32 ll car(ll pos)
33 {
34 if(p[pos] == pos)
35 return pos;
36 else
37 return p[pos] = car(p[pos]);
38 }
39
40 int construct(std::vector<std::vector<int>> pt)
41 {
42 px = pt;
43 n = px.size();
44 for (int i = 0; i < n; i++)
45 {
46 std::vector<int> row;
47 row.resize(n, 0);
48 answer.push_back(row);
49 }
50
51 for (int i = 0; i < n; i++)
52 p[i] = i;
53
54 for (int i = 0; i < n; i++)
55 for (int j = i + 1; j < n; j++)
56 if(px[i][j])
57 p[car(i)] = car(j);
58
59 for (int i = 0; i < n; i++)
60 v[car(i)].pb(i);
61
62 for (int i = 0; i < n; i++)
63 {
64 if(!bisa)
65 break;
66
67 ll sz = v[i].size();
68 x[0] = 0;
69 x[1] = 0;
CAPITOLUL 1. IOI 2020 46

70 x[2] = 0;
71 x[3] = 0;
72 for(ll j = 0; j < sz; j++)
73 for(ll k = j + 1; k < sz; k++)
74 x[px[v[i][j]][v[i][k]]] = 1;
75
76 if(x[0] + x[1] + x[2] + x[3] == 0)
77 continue;
78 else
79 if(x[0])
80 bisa = 0;
81 else
82 if(x[2] == 1 && x[3] == 1)
83 bisa = 0;
84 else
85 if(x[1] == 1 && x[2] == 0 && x[3] == 0)
86 {
87 for(ll j = 1; j < sz; j++)
88 {
89 answer[v[i][j - 1]][v[i][j]] = 1;
90 answer[v[i][j]][v[i][j - 1]] = 1;
91 }
92 }
93 else
94 if(x[2] == 1)
95 {
96 ll te = 0;
97 for(ll j = 0; j < sz; j++)
98 {
99 if(kel[v[i][j]])
100 continue;
101
102 te++;
103 kel[v[i][j]] = te;
104 ket[te] = v[i][j];
105
106 for(ll k = 0; k < sz; k++)
107 {
108 if(j == k)
109 continue;
110
111 if(px[v[i][j]][v[i][k]] == 1)
112 {
113 kel[v[i][k]] = te;
114 answer[v[i][j]][v[i][k]] = 1;
115 answer[v[i][k]][v[i][j]] = 1;
116 }
117 }
118 }
119
120 for(ll j = 0; j < sz; j++)
121 for(ll k = j + 1; k < sz; k++)
122 {
123 if(px[v[i][j]][v[i][k]] == 1 && kel[v[i][j]] != kel[v[i][k]])
124 bisa = 0;
125
126 if(px[v[i][j]][v[i][k]] != 1 && kel[v[i][j]] == kel[v[i][k]])
127 bisa = 0;
128 }
129
130 if(te <= 2)
131 bisa = 0;
132
133 if(!bisa)
134 break;
135
136 for(ll j = 1; j < te; j++)
137 {
138 answer[ket[j]][ket[j + 1]] = 1;
139 answer[ket[j + 1]][ket[j]] = 1;
140 }
141
142 answer[ket[te]][ket[1]] = 1;
143 answer[ket[1]][ket[te]] = 1;
144 }
145 else
CAPITOLUL 1. IOI 2020 47

146 if(x[3] == 1)
147 {
148 bisa = 0;
149 }
150 }
151
152 if(!bisa)return 0;
153 build(answer);
154
155 return 1;
156 }
157
158 // ---------------------------------------------------
159
160 static void check(bool cond, std::string message)
161 {
162 if (!cond)
163 {
164 printf("%s\n", message.c_str());
165 fclose(stdout);
166 exit(0);
167 }
168 }
169
170 void build(std::vector<std::vector<int>> _b)
171 {
172 check(!called, "build is called more than once");
173 called = true;
174 check((int)_b.size() == n, "Invalid number of rows in b");
175 for (int i = 0; i < n; i++)
176 {
177 check((int)_b[i].size() == n, "Invalid number of columns in b");
178 }
179 b = _b;
180 }
181
182 int main()
183 {
184 std::clock_t c_start = std::clock();
185 auto t_start = std::chrono::high_resolution_clock::now();
186
187 auto t1 = clock();
188
189 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
190 std::freopen("supertrees.out", "w", stdout) ;
191
192 assert(scanf("%d", &n) == 1);
193
194 pp.resize(n);
195 for (int i = 0; i < n; i++)
196 {
197 pp[i].resize(n);
198 }
199
200 for (int i = 0; i < n; i++)
201 {
202 for (int j = 0; j < n; j++)
203 {
204 assert(scanf("%d", &pp[i][j]) == 1);
205 }
206 }
207 fclose(stdin);
208
209 auto t2 = clock();
210
211 int possible = construct(pp);
212
213 auto t3 = clock();
214
215 check(possible == 0 || possible == 1, "Invalid return value of construct");
216 if (possible == 1)
217 {
218 check(called, "construct returned 1 without calling build");
219 }
220 else
221 {
CAPITOLUL 1. IOI 2020 48

222 check(!called, "construct called build but returned 0");


223 }
224
225 printf("%d\n", possible);
226 if (possible == 1)
227 {
228 for (int i = 0; i < n; i++)
229 {
230 for (int j = 0; j < n; j++)
231 {
232 if (j)
233 {
234 printf(" ");
235 }
236 printf("%d", b[i][j]);
237 }
238 printf("\n");
239 }
240 }
241 fclose(stdout);
242
243
244 auto t4 = clock();
245
246 std::clock_t c_end = std::clock();
247 auto t_end = std::chrono::high_resolution_clock::now();
248
249 // reset console output
250 freopen("CON", "w", stdout);
251
252 //std::cout <<result<<’\n’<<’\n’;
253 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
254 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
255 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
256
257 std::cout << std::fixed << std::setprecision(2)
258 << "\nCPU time used: "
259 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
260 << "Wall clock time passed: "
261 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
262 << " ms\n";
263
264 return 0;
265 }
266 /*
267 t2-t1 = 1.422
268 t3-t2 = 0.047
269 t4-t3 = 0.453
270
271 CPU time used: 1922.00 ms
272 Wall clock time passed: 1921.88 ms
273
274 Process returned 0 (0x0) execution time : 2.000 s
275 Press any key to continue.
276 */

Listing 1.2.8: supertrees_300489.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300489 282 ms 23928 KB
3
4 #include "supertrees.h"
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 static int n;
11 static std::vector<std::vector<int>> p;
12 static std::vector<std::vector<int>> b;
13 static bool called = false;
14
15 // ---------------------------------------------------
16
17 const int N=1e3+10;
CAPITOLUL 1. IOI 2020 49

18 int f[N][4];
19 int Find(int x,int op)
20 {
21 if(f[x][op]==x) return x;
22 else return f[x][op]=Find(f[x][op],op);
23 }
24
25 void Union(int x,int y,int op)
26 {
27 x=Find(x,op),y=Find(y,op);
28 if(x==y) return;
29 f[y][op]=x;
30 }
31
32 int rol[N][N],len[N],vis[N][N];
33
34 int construct(std::vector<std::vector<int>> p)
35 {
36 int n = p.size();
37 for(int i=0;i<n;i++) f[i][1]=f[i][2]=i;
38 for(int i=0;i<n;i++)
39 {
40 for(int j=i+1;j<n;j++)
41 {
42 if(p[i][j]==3) return 0;
43 if(p[i][j]==1)
44 {
45 Union(i,j,1);
46 }
47 if(p[i][j]==2)
48 {
49 Union(i,j,2);
50 }
51 }
52 }
53
54 vector< vector<int> > ans;
55 for(int i=0;i<n;i++)
56 {
57 vector<int> row;
58 for(int j=0;j<n;j++) row.push_back(0);
59 ans.push_back(row);
60 }
61
62 for(int i=0;i<n;i++)
63 {
64 int t1=Find(i,1),t2=Find(t1,2);
65 if(t1!=t2 && Find(t1,1)==Find(t2,1))
66 {
67 f[t1][1]=t2,f[t2][1]=t2;
68 t1=t2;
69 }
70
71 if(t1!=i)
72 {
73 ans[t1][i]=ans[i][t1]=1;
74 }
75
76 if(t2!=t1)
77 {
78 if(!vis[t2][t1]) rol[t2][++len[t2]]=t1;
79 vis[t2][t1]=1;
80 }
81 }
82
83 //for(int i=0;i<n;i++) cout<<Find(i,1)<<" "<<Find(i,2)<<endl;
84 for(int i=0;i<n;i++)
85 {
86 if(len[i])
87 {
88 if(len[i]==1) return 0;
89 rol[i][0]=i;
90 for(int j=0;j<len[i];j++)
91 {
92 ans[rol[i][j]][rol[i][j+1]]=ans[rol[i][j+1]][rol[i][j]]=1;
93 }
CAPITOLUL 1. IOI 2020 50

94 ans[ rol[i][len[i]] ][i]=ans[i][ rol[i][len[i]] ]=1;


95 }
96 }
97
98 for(int i=0;i<n;i++)
99 {
100 for(int j=i+1;j<n;j++)
101 {
102 if(p[i][j]==0)
103 {
104 if(Find(i,1)==Find(j,1) || Find(i,2)==Find(j,2)) return 0;
105 }
106 }
107 }
108
109 build(ans);
110 return 1;
111 }
112
113 // ---------------------------------------------------
114
115 static void check(bool cond, std::string message)
116 {
117 if (!cond)
118 {
119 printf("%s\n", message.c_str());
120 fclose(stdout);
121 exit(0);
122 }
123 }
124
125 void build(std::vector<std::vector<int>> _b)
126 {
127 check(!called, "build is called more than once");
128 called = true;
129 check((int)_b.size() == n, "Invalid number of rows in b");
130 for (int i = 0; i < n; i++)
131 {
132 check((int)_b[i].size() == n, "Invalid number of columns in b");
133 }
134 b = _b;
135 }
136
137 int main()
138 {
139 std::clock_t c_start = std::clock();
140 auto t_start = std::chrono::high_resolution_clock::now();
141
142 auto t1 = clock();
143
144 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
145 std::freopen("supertrees.out", "w", stdout) ;
146
147 assert(scanf("%d", &n) == 1);
148
149 p.resize(n);
150 for (int i = 0; i < n; i++)
151 {
152 p[i].resize(n);
153 }
154
155 for (int i = 0; i < n; i++)
156 {
157 for (int j = 0; j < n; j++)
158 {
159 assert(scanf("%d", &p[i][j]) == 1);
160 }
161 }
162 fclose(stdin);
163
164 auto t2 = clock();
165
166 int possible = construct(p);
167
168 auto t3 = clock();
169
CAPITOLUL 1. IOI 2020 51

170 check(possible == 0 || possible == 1, "Invalid return value of construct");


171 if (possible == 1)
172 {
173 check(called, "construct returned 1 without calling build");
174 }
175 else
176 {
177 check(!called, "construct called build but returned 0");
178 }
179
180 printf("%d\n", possible);
181 if (possible == 1)
182 {
183 for (int i = 0; i < n; i++)
184 {
185 for (int j = 0; j < n; j++)
186 {
187 if (j)
188 {
189 printf(" ");
190 }
191 printf("%d", b[i][j]);
192 }
193 printf("\n");
194 }
195 }
196 fclose(stdout);
197
198
199 auto t4 = clock();
200
201 std::clock_t c_end = std::clock();
202 auto t_end = std::chrono::high_resolution_clock::now();
203
204 // reset console output
205 freopen("CON", "w", stdout);
206
207 //std::cout <<result<<’\n’<<’\n’;
208 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
209 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
210 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
211
212 std::cout << std::fixed << std::setprecision(2)
213 << "\nCPU time used: "
214 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
215 << "Wall clock time passed: "
216 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
217 << " ms\n";
218
219 return 0;
220 }
221 /*
222 t2-t1 = 1.27
223 t3-t2 = 0.172
224 t4-t3 = 0.453
225
226 CPU time used: 1895.00 ms
227 Wall clock time passed: 1895.16 ms
228
229 Process returned 0 (0x0) execution time : 1.942 s
230 Press any key to continue.
231 */

Listing 1.2.9: supertrees_300657.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300657 290 ms 22392 KB
3
4 #include "supertrees.h"
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 static int n;
CAPITOLUL 1. IOI 2020 52

11 static std::vector<std::vector<int>> p;
12 static std::vector<std::vector<int>> b;
13 static bool called = false;
14
15 // ---------------------------------------------------
16
17 int par[1010];
18 vector<int> ls[1010];
19
20 int fnd(int x) { return x == par[x] ? x : par[x] = fnd(par[x]); }
21 void uni(int x, int y) { par[fnd(x)] = fnd(y); }
22
23 int construct(vector<vector<int>> p)
24 {
25 int n = p.size();
26 vector<vector<int>> ret(n, vector<int>(n));
27
28 for(int i = 0; i < n; ++i) par[i] = i;
29 for(int i = 0; i < n; ++i)
30 {
31 for(int j = 0; j < n; ++j)
32 {
33 if(p[i][j] == 3) return 0;
34 if(p[i][j]) uni(i, j);
35 }
36 }
37
38 for(int i = 0; i < n; ++i) ls[fnd(i)].push_back(i);
39
40 for(int i = 0; i < n; ++i) if(ls[i].size())
41 {
42 vector<int> tmp = ls[i];
43 for(int j : tmp) for(int k : tmp) if(p[j][k] == 0) return 0;
44 for(int j : tmp) par[j] = j, ls[j].clear();
45 for(int j : tmp) for(int k : tmp) if(p[j][k] == 1) uni(j, k);
46 for(int j : tmp) ls[fnd(j)].push_back(j);
47
48 vector<int> head;
49 for(int j : tmp) if(ls[j].size())
50 {
51 head.push_back(j);
52 for(int k : ls[j]) for(int l : ls[j]) if(p[k][l] == 2) return 0;
53 for(int k = 0; k < (int)ls[j].size() - 1; ++k)
54 ret[ls[j][k]][ls[j][k + 1]] = ret[ls[j][k + 1]][ls[j][k]] = 1;
55 }
56
57 if(head.size() == 2) return 0;
58 if(head.size() >= 3)
59 {
60 for(int j = 0; j < (int)head.size() - 1; ++j)
61 ret[head[j]][head[j + 1]] = ret[head[j + 1]][head[j]] = 1;
62
63 ret[head.back()][head[0]] = ret[head[0]][head.back()] = 1;
64 }
65 }
66
67 build(ret);
68 return 1;
69 }
70
71 // ---------------------------------------------------
72
73 static void check(bool cond, std::string message)
74 {
75 if (!cond)
76 {
77 printf("%s\n", message.c_str());
78 fclose(stdout);
79 exit(0);
80 }
81 }
82
83 void build(std::vector<std::vector<int>> _b)
84 {
85 check(!called, "build is called more than once");
86 called = true;
CAPITOLUL 1. IOI 2020 53

87 check((int)_b.size() == n, "Invalid number of rows in b");


88 for (int i = 0; i < n; i++)
89 {
90 check((int)_b[i].size() == n, "Invalid number of columns in b");
91 }
92 b = _b;
93 }
94
95 int main()
96 {
97 std::clock_t c_start = std::clock();
98 auto t_start = std::chrono::high_resolution_clock::now();
99
100 auto t1 = clock();
101
102 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
103 std::freopen("supertrees.out", "w", stdout) ;
104
105 assert(scanf("%d", &n) == 1);
106
107 p.resize(n);
108 for (int i = 0; i < n; i++)
109 {
110 p[i].resize(n);
111 }
112
113 for (int i = 0; i < n; i++)
114 {
115 for (int j = 0; j < n; j++)
116 {
117 assert(scanf("%d", &p[i][j]) == 1);
118 }
119 }
120 fclose(stdin);
121
122 auto t2 = clock();
123
124 int possible = construct(p);
125
126 auto t3 = clock();
127
128 check(possible == 0 || possible == 1, "Invalid return value of construct");
129 if (possible == 1)
130 {
131 check(called, "construct returned 1 without calling build");
132 }
133 else
134 {
135 check(!called, "construct called build but returned 0");
136 }
137
138 printf("%d\n", possible);
139 if (possible == 1)
140 {
141 for (int i = 0; i < n; i++)
142 {
143 for (int j = 0; j < n; j++)
144 {
145 if (j)
146 {
147 printf(" ");
148 }
149 printf("%d", b[i][j]);
150 }
151 printf("\n");
152 }
153 }
154 fclose(stdout);
155
156
157 auto t4 = clock();
158
159 std::clock_t c_end = std::clock();
160 auto t_end = std::chrono::high_resolution_clock::now();
161
162 // reset console output
CAPITOLUL 1. IOI 2020 54

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


164
165 //std::cout <<result<<’\n’<<’\n’;
166 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
167 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
168 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
169
170 std::cout << std::fixed << std::setprecision(2)
171 << "\nCPU time used: "
172 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
173 << "Wall clock time passed: "
174 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
175 << " ms\n";
176
177 return 0;
178 }
179 /*
180 t2-t1 = 1.469
181 t3-t2 = 0.062
182 t4-t3 = 0.46
183
184 CPU time used: 1991.00 ms
185 Wall clock time passed: 1990.42 ms
186
187 Process returned 0 (0x0) execution time : 2.037 s
188 Press any key to continue.
189 */

Listing 1.2.10: supertrees_300806.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300806 258 ms 22396 KB
3
4 #include "supertrees.h"
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 static int n;
11 static std::vector<std::vector<int>> p;
12 static std::vector<std::vector<int>> b;
13 static bool called = false;
14
15 // ---------------------------------------------------
16
17 int Num[1010], Num2[1010], PPP[1010], vis[1010];
18 vector<vector<int>> E;
19
20 void Add(int a, int b)
21 {
22 if(a==-1||b==-1||a==b)return;
23 E[a][b]=E[b][a]=1;
24 }
25
26 int construct(std::vector<std::vector<int>> p)
27 {
28 int n = p.size();
29 int i, j, cnt = 0;
30 E.resize(n);
31 for(i=0;i<n;i++)
32 {
33 E[i].resize(n);
34 for(j=0;j<n;j++)
35 {
36 E[i][j]=0;
37 }
38 }
39
40 for(i=0;i<n;i++)
41 {
42 if(!Num[i])cnt++;
43 else continue;
44
45 for(j=0;j<n;j++)
CAPITOLUL 1. IOI 2020 55

46 {
47 if(p[i][j])
48 {
49 Num[j]=cnt;
50 }
51 }
52 }
53
54 for(i=0;i<n;i++)
55 {
56 for(j=0;j<n;j++)
57 {
58 if((Num[i]==Num[j])!=(p[i][j]!=0))return 0;
59 if(p[i][j]==3)return 0;
60 }
61 }
62
63 int cc=0;
64 for(i=0;i<n;i++)PPP[i]=-1;
65
66 vector<int>T;
67 for(i=0;i<n;i++)
68 {
69 if(!Num2[i])cc++;
70 else continue;
71
72 T.push_back(i);
73 for(j=0;j<n;j++)
74 {
75 if(p[i][j]==1)
76 {
77 Num2[j]=cc;
78 PPP[j]=i;
79 }
80 }
81 }
82
83 for(i=0;i<n;i++)
84 {
85 for(j=0;j<n;j++)
86 {
87 if(i==j)continue;
88 if((Num2[i]==Num2[j])!=(p[i][j]==1))return 0;
89 }
90 }
91
92 for(i=0;i<n;i++)
93 Add(i,PPP[i]);
94
95 for(auto &t : T)
96 {
97 vector<int>Z;
98 if(vis[t])continue;
99 for(auto &x : T)
100 if(!vis[x] && Num[t]==Num[x])
101 Z.push_back(x);
102
103 for(auto &x : Z)vis[x]=1;
104 int sz = Z.size();
105 if(sz==2)
106 return 0;
107
108 if(sz>1)
109 for(i=0;i<sz;i++)
110 Add(Z[i],Z[(i+1)%sz]);
111 }
112
113 build(E);
114
115 return 1;
116 }
117
118 // ---------------------------------------------------
119
120 static void check(bool cond, std::string message)
121 {
CAPITOLUL 1. IOI 2020 56

122 if (!cond)
123 {
124 printf("%s\n", message.c_str());
125 fclose(stdout);
126 exit(0);
127 }
128 }
129
130 void build(std::vector<std::vector<int>> _b)
131 {
132 check(!called, "build is called more than once");
133 called = true;
134 check((int)_b.size() == n, "Invalid number of rows in b");
135 for (int i = 0; i < n; i++)
136 {
137 check((int)_b[i].size() == n, "Invalid number of columns in b");
138 }
139 b = _b;
140 }
141
142 int main()
143 {
144 std::clock_t c_start = std::clock();
145 auto t_start = std::chrono::high_resolution_clock::now();
146
147 auto t1 = clock();
148
149 std::freopen("../tests/4-17.in", "r", stdin) ; //6-16, 4-17,
150 std::freopen("supertrees.out", "w", stdout) ;
151
152 assert(scanf("%d", &n) == 1);
153
154 p.resize(n);
155 for (int i = 0; i < n; i++)
156 {
157 p[i].resize(n);
158 }
159
160 for (int i = 0; i < n; i++)
161 {
162 for (int j = 0; j < n; j++)
163 {
164 assert(scanf("%d", &p[i][j]) == 1);
165 }
166 }
167 fclose(stdin);
168
169 auto t2 = clock();
170
171 int possible = construct(p);
172
173 auto t3 = clock();
174
175 check(possible == 0 || possible == 1, "Invalid return value of construct");
176 if (possible == 1)
177 {
178 check(called, "construct returned 1 without calling build");
179 }
180 else
181 {
182 check(!called, "construct called build but returned 0");
183 }
184
185 printf("%d\n", possible);
186 if (possible == 1)
187 {
188 for (int i = 0; i < n; i++)
189 {
190 for (int j = 0; j < n; j++)
191 {
192 if (j)
193 {
194 printf(" ");
195 }
196 printf("%d", b[i][j]);
197 }
CAPITOLUL 1. IOI 2020 57

198 printf("\n");
199 }
200 }
201 fclose(stdout);
202
203
204 auto t4 = clock();
205
206 std::clock_t c_end = std::clock();
207 auto t_end = std::chrono::high_resolution_clock::now();
208
209 // reset console output
210 freopen("CON", "w", stdout);
211
212 //std::cout <<result<<’\n’<<’\n’;
213 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
214 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
215 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
216
217 std::cout << std::fixed << std::setprecision(2)
218 << "\nCPU time used: "
219 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
220 << "Wall clock time passed: "
221 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
222 << " ms\n";
223
224 return 0;
225 }
226 /*
227 t2-t1 = 1.265
228 t3-t2 = 0.125
229 t4-t3 = 0.438
230
231 CPU time used: 1828.00 ms
232 Wall clock time passed: 1828.12 ms
233
234 Process returned 0 (0x0) execution time : 1.891 s
235 Press any key to continue.
236 */

1.2.3 *Rezolvare detaliat 

1.3 tickets
Problema 3 - Carnival Tickets 100 de puncte
Author: Xiao Mao
Ringo este la un carnaval în Singapore. El are câteva bilete în bagaj, pe care vrea s  le
foloseasc  la un stand cu jocuri. Fiecare bilet are o culoare din cele n culori posibile ³i are un
întreg nenegativ imprimat pe el. întregul imprimat poate  acela³i pentru mai multe dintre bilete.
Datorit  unei ciud µenii în regulile carnavalului, n este garantat par.
Ringo are câte m bilete de ecare culoare în bagaj, pentru un total de n m bilete. Biletul j
de culoare i are întregul xij  imprimat pe el (0 & i & n  1 ³i 0 & j & m  1).
Jocul se joac  în k runde, numerotate de la 0 la k  1. Fiecare rund  este jucat  în ordinea
urm toare:
ˆ Din bagaj, Ringo selecteaz  o mulµime de n bilete, un bilet din ecare culoare. El d  apoi
biletele responsabilului de la stand.
ˆ Responsabilul standului noteaz  întregii a0, a1, ..., an  1 imprimaµi pe biletele din
mulµime. Ordinea acestor n întregi nu este important .
ˆ Responsabilul standului extrage o carte special  dintr-o cutie ³i noteaz  întregul b imprimat
pe aceast  carte.
ˆ Responsabilul standului calculeaz  modulul diferenµei dintre ai ³i b pentru ecare i de la
0 la n  1. Fie S suma acestor module.
ˆ Pentru aceast  rund , responsabilul standului îi d  lui Ringo un premiu în valoare egal  cu
S.
CAPITOLUL 1. IOI 2020 58

ˆ Biletele din mulµime sunt aruncate ³i nu mai pot  folosite în rundele urm toare.

Biletele r mase în bagajul lui Ringo dup  k runde ale jocului sunt aruncate.
Privind cu atenµie, Ringo realizeaz  c  jocul este trucat! Exist  de fapt o imprimant  în
interiorul cutiei din care se extrage cartea special . În ecare rund , responsabilul standului
g se³te un întreg b care minimizeaz  valoarea premiului pentru acea rund . Valoarea aleas  de
responsabilul standului este imprimat  pe cartea extras  în acea rund .
Având toate aceste informaµii, Ringo ar dori s  aloce biletele pentru rundele jocului. Mai exact,
acesta dore³te s  selecteze mulµimea de bilete folosit  în ecare rund  astfel încât s  maximizeze
valoarea total  a premiilor.

Detalii de implementare
Trebuie s  implementaµi urm toarea funcµie:

int64 find_maximum(int k, int[][] x)

ˆ k : num rul rundelor.
ˆ x: o matrice de dimensiuni n  m conµinând întregii de pe bilete. Biletele de ecare culoare
sunt sortate în ordinea cresc toare a întregilor imprimaµi pe ele.
ˆ Aceast  funcµie este apelat  o singur  dat .
ˆ Aceast  funcµie trebuie s  fac  un singur apel c tre allocate_tickets (vezi mai jos),
pentru k mulµimi de bilete, unul pentru ecare rund .
ˆ Aceast  funcµie trebuie s  returneze valoarea maxim  a premiilor.

Funcµia allocate_tickets este denit  astfel:

void allocate_tickets(int[][] s)

ˆ s: o matrice de dimensiuni n  m. Valoarea lui sij  este r dac  biletul j de culoare i este
utilizat în mulµimea rundei r a jocului, sau -1 dac  nu este folosit deloc.
ˆ Pentru ecare 0 & i & n  1, printre si0, si1, ..., sim  1 ecare valoare 0, 1, 2,
..., k  1 trebuie s  apar  o singur  dat , iar celelalte valori s  e -1.
ˆ Dac  exist  mai multe aloc ri pentru valoarea maxim  a premiului, se poate raporta oricare.

Exemple
Exemplul 1 Consider m urm torul apel:
find_maximum(2, [[0, 2, 5],[1, 1, 3]])

Asta înseamn  c :


ˆ sunt k 2 runde;
ˆ întregii imprimaµi pe biletele de culoare 0 sunt 0, 2 ³i 5;
ˆ îngtregii imprimaµi pe biletele de culoare 1 sunt 1, 1, ³i 3.

O posibil  alocare este:


ˆ În runda 0, Ringo alege biletul 0 de culoare 0 (cu întregul 0) ³i biletul 2 de culoare 1 (cu
întregul 3). Cea mai mic  valoare a premiului în aceast  rund  este 3. E.g., responsabilul
jocului ar putea alege b 1: ¶1  0¶  ¶1  3¶ 1  2 3.
ˆ În runda 1, Ringo alege biletul 2 de culoare 0 (cu întregul 5) ³i biletul 1 de culoare 1 (cu
întregul 1). Cea mai mic  valoare a premiului în aceast  rund  este 4. E.g., responsabilul
jocului ar putea alege b 3: ¶3  1¶  ¶3  5¶ 2  2 4.
ˆ Astfel, valoarea total  a premiilor ar  3+4=7.

Pentru a raporta aceast  alocare, funcµia find_maximum ar trebui s  fac  urm torul apel
c tre allocate_tickets:
a allocate_tickets([[0, -1, 1], [-1, 1, 0]])
În nal, funcµia find_maximum trebuie s  returneze .

Exemplul 2 Cosider m urm torul apel:


CAPITOLUL 1. IOI 2020 59

find_maximum(1, [[5, 9], [1, 4], [3, 6], [2, 7]])

Asta înseamn  c :


ˆ exist  o singur  rund ,
ˆ întregii de pe culoarea 0 sunt 5 ³i 9;
ˆ întregii de pe culoarea 1 sunt 1 ³i 4;
ˆ întregii de pe culoarea 2 bsunt 3 ³i 6;
ˆ întregii de pe culoarea 3 sunt 2 ³i 7.

O posibil  alocare este:


ˆ În runda 0, Ringo alege biletul 1 de culoare 0 (cu întregul 9), biletul 0 de culoare 1 (cu
întregul 1), biletul 0 de culoare 2 (cu întregul 3), ³i biletul 1 de culoare 3 (cu întregul 7).
Cea mai mic  valoare a premiului în aceast  rund  este 12, când responsabilul jocului alege
b 3: |3-9|+|3-1|+|3-3|+|3-7| = 6+2+0+4 = 12.

Pentru a raporta aceast  alocare, funcµia find_maximum ar trebui s  fac  urm torul apel
c tre allocate_tickets:
a allocate_tickets([[-1, 0], [0, -1], [0, -1], [-1, 0]])

În nal, funcµia find_maximum trebuie s  returneze 12.

Restricµii
ˆ 2 & n & 1500 ³i n este par.
ˆ 1 & k & m & 1500
0 & xij  & 10 (oricare ar  0 & i & n  1 si 0 & j & m  1)
9
ˆ
ˆ xij  1 & xij  (oricare ar  0 & i & n  1 si 0 & j & m  1)

Subtaskuri
1. (11 puncte) m 1
2. (16 puncte) k 1
3. (14 puncte) 0 & xij  & 1 (oricare ar  0 & i & n  1 si 0 & j &m1 )
4. (14 puncte) k m
5. (12 puncte) n, m & 80
6. (23 puncte) n, m & 300
7. (10 puncte) F r  restricµii suplimentare.

Sample grader
Sample graderul cite³te intr rile în urm torul format:
ˆ linia 1: n m k
ˆ linia 2  i (0 & i & n  1): xi0 xi1 ... xim  1)

Sample graderul a³eaz  r spunsul t u în urm torul format:


ˆ linia 1: valoarea returnata de find_maximum
ˆ linia 2  i (0 & i & n  1): si0 si1 ... sim  1)

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2000 MB

1.3.1 Indicaµii de rezolvare

Observe that the special card pulled by the game master is always equal to the it median of the
input.
Since n is even, we may assume that the value of each prize is given by the sum of the top half
values, minus the sum of the bottom values.
We can now change the rules as follows(*):
ˆ On each round, 1 ticket of each color is chosen.
ˆ These n tickets are split into two piles labeled '+' and '-'.
CAPITOLUL 1. IOI 2020 60

ˆ Each pile consists of exactly n©2 tickets


ˆ The value of the prize is given by the sum of all tickets labeled '+', minus the sum of all
tickets labeled '-'.

It is easy to see that a player who wishes to maximize the value of his prize will choose to put
the tickets of larger value into the `+' pile. By doing so, the value of the prize remains the same
as compared to the original version.
It is in fact convenient to relax the rules even further and combine the k rounds into a single
round(**):
ˆ These kn tickets are split into two piles labeled '+' and '-', each pile consisting of kn©2
tickets
ˆ The value of the prize is given by the sum of all tickets labeled '+', minus the sum of all
tickets labeled '-'.

It is clear that the maximal attainable value using the rules in (**) is at least as large the
maximal value attainable in (*): this is because we can simply merge the '+' and '-' piles.
We claim that they are in fact equal (this would solve subtask 4).
To show that they are equal, we need a method for `splitting' up these tickets back into k piles.
Starting with the kn©2 tickets, we proceed as follows until all the tickets are split into k rounds:
ˆ Sort the colors by the total number of '+' in each pile.
ˆ For each of the colors with the most total number of '+', take 1 ticket with a '+' label from
each of them. For the remaining numbers, take 1 ticket with a '-' label.
ˆ Collect the tickets from the previous step, and form 1 round with it.

It remains to solve the problem using the rules in (**).


We proceed using a greedy approach. Initially, the k smallest tickets of each color is assigned
'-'. A step consisting of removing one of these '-' and replacing it with a '+' from the same color.
We proceed to greedily maximize the sum of all '+' minus the sum of all '-' at each step.
To execute this greedy algorithm, we record down the largest ticket in the '-' pile of that color
and the largest unused ticket of that color. The increase in score is given by the sum of these two
numbers, therefore we can choose the color which corresponds to the largest value.
2
If we do so naively, the running time is O n m. We can speed this up by using a priority queue.

1.3.2 Coduri surs 

Listing 1.3.1: graderTickets-sandbox.cpp


1 #include "tickets.h"
2 #include <cassert>
3 #include <cstdio>
4 #include <vector>
5 #include <string>
6
7 #include <iostream> /* cout */
8 #include <iomanip> /* setprecision */
9 #include <chrono> /* chrono */
10
11 #include <algorithm>
12 #include <queue>
13
14 using namespace std;
15
16 static int n;
17 static int m;
18 static int k;
19 static std::vector<std::vector<int>> d;
20 static std::vector<std::vector<int>> x;
21 static int called = 0;
22
23 // ---------------------------------------------------
24
CAPITOLUL 1. IOI 2020 61

25 typedef long long int lli;


26 typedef pair<lli, lli> pii;
27
28 long long find_maximum(int k, vector<vector<int>> d)
29 {
30 // Number of companies
31 int c = d.size();
32
33 // tickets per company
34 int s = d[0].size();
35
36 // Sort each company’s tickets by their distance
37 // o[company][place] => index of ship which is <place>th from left
38 vector<vector<int>> o;
39 o.resize(c);
40 for (int i = 0; i < c; i++)
41 {
42 o[i].resize(s);
43 for (int j = 0; j < s; j++)
44 {
45 o[i][j] = j;
46 }
47 sort(o[i].begin(), o[i].end(), [&](const int &a, const int &b)
48 {
49 return d[i][a] < d[i][b];
50 });
51 }
52
53 // d_o(company, place) => distance of ship which is <place>th form left
54 auto d_o = [&](const int &a, const int &b)
55 {
56 return d[a][o[a][b]];
57 };
58
59 // gain: (change to cost by turning a - into a +, company index)
60 priority_queue<pii> gain;
61
62 // Total cost of the arrangement
63 lli cost = 0;
64
65 // plus_count[company] => How many +s the company will send
66 vector<int> plus_count;
67 plus_count.resize(c, 0);
68
69 for (int i = 0; i < c; i++)
70 {
71 for (int j = 0; j < k; j++)
72 {
73 // First incur cost of all -s
74 cost -= d_o(i, j);
75 }
76
77 // Queue a possible - to +
78 gain.push
79 (
80 pii
81 (
82 d_o(i, s - 1 - plus_count[i])
83 + d_o(i, k - 1 - plus_count[i]),
84 i
85 )
86 );
87 }
88
89 // Take ck/2 +s
90 for (int i = 0; i < c * k / 2; i++)
91 {
92 pii top = gain.top(); gain.pop();
93 cost += top.first;
94
95 // If the company hasnt reached k +s, queue another possible +
96 int this_company = top.second;
97 plus_count[this_company]++;
98 if (plus_count[this_company] < k)
99 {
100 gain.push
CAPITOLUL 1. IOI 2020 62

101 (
102 pii
103 (
104 d_o(this_company, s - 1 - plus_count[this_company])
105 + d_o(this_company, k - 1 - plus_count[this_company]),
106 this_company
107 )
108 );
109 }
110 }
111
112 // Empty out the priority queue, will use again later
113 while (!gain.empty()) gain.pop();
114
115 // gain: (+s remaining, company index)
116 for (int i = 0; i < c; i++)
117 {
118 gain.push(pii(plus_count[i], i));
119 }
120
121 // minus_count[company] => How many -s the company has sent so far
122 vector<int> minus_count;
123 minus_count.resize(c, 0);
124
125 // Prepare answer vector dimensions and fill with -1s
126 vector<vector<int>> answer;
127 answer.resize(c);
128 for (int i = 0; i < c; i++)
129 {
130 answer[i].resize(s, -1);
131 }
132
133 // take[company] => Does this company send a + in this days
134 vector<int> take;
135 take.resize(c, 0);
136
137 // Simulate k days of tickets
138 for (int i = 0; i < k; i++)
139 {
140 // Pick c/2 companies with the most +s left
141 for (int j = 0; j < c / 2; j ++)
142 {
143 take[gain.top().second] = 1;
144 gain.pop();
145 }
146
147 // Check if each company sent a - or +, and change answer accordingly
148 for (int j = 0; j < c; j++)
149 {
150 if (take[j])
151 {
152 // Company j sent a + this round, send the smallest +
153 // Queue the company back
154 answer[j][o[j][s - plus_count[j]]] = i;
155 plus_count[j]--;
156 gain.push(pii(plus_count[j], j));
157 }
158 else
159 {
160 // Company j sent a - this round, send the smallest -
161 answer[j][o[j][minus_count[j]]] = i;
162 minus_count[j]++;
163 }
164 take[j] = 0;
165 }
166 }
167
168 // Return to grader
169 allocate_tickets(answer);
170
171 return cost;
172 }
173
174 // ---------------------------------------------------
175
176 static void check(bool cond, std::string message)
CAPITOLUL 1. IOI 2020 63

177 {
178 if (!cond)
179 {
180 printf("%s\n", message.c_str());
181 exit(0);
182 }
183 }
184
185 void allocate_tickets( std::vector<std::vector<int>> _d)
186 {
187 check(!called, "allocate_tickets called more than once");
188 d = _d;
189 check((int)d.size() == n,
190 "allocate_tickets called with parameter of wrong size");
191 for (int i = 0; i < n; i++)
192 {
193 check((int)d[i].size() == m,
194 "allocate_tickets called with parameter of wrong size");
195 }
196 called = 1;
197 }
198
199 int main()
200 {
201 std::clock_t c_start = std::clock();
202 auto t_start = std::chrono::high_resolution_clock::now();
203
204 auto t1 = clock();
205
206 std::freopen("../tests/7-14.in", "r", stdin) ;
207 std::freopen("tickets.out", "w", stdout) ;
208
209 assert(scanf("%d %d %d", &n, &m, &k) == 3);
210 x.resize(n);
211 for (int i = 0; i < n; i++)
212 {
213 x[i].resize(m);
214 for (int j=0; j < m; j++)
215 {
216 assert(scanf("%d", &x[i][j]) == 1);
217 }
218 }
219 fclose(stdin);
220
221 auto t2 = clock();
222
223 long long answer = find_maximum(k, x);
224
225 auto t3 = clock();
226
227 check(called, "failure to call allocate_tickets");
228
229 printf("%lld\n", answer);
230 for (int i = 0; i < n; i++)
231 {
232 for (int j = 0; j < m; j++)
233 {
234 if (j) printf(" ");
235 printf("%d", d[i][j]);
236 }
237 printf("\n");
238 }
239 fclose(stdout);
240
241 auto t4 = clock();
242
243 std::clock_t c_end = std::clock();
244 auto t_end = std::chrono::high_resolution_clock::now();
245
246 // reset console output
247 freopen("CON", "w", stdout);
248
249 //std::cout <<result<<’\n’<<’\n’;
250 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
251 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
252 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 1. IOI 2020 64

253
254 std::cout << std::fixed << std::setprecision(2)
255 << "\nCPU time used: "
256 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
257 << "Wall clock time passed: "
258 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
259 << " ms\n";
260
261 return 0;
262 }
263 /*
264 t2-t1 = 4.423
265 t3-t2 = 4.341
266 t4-t3 = 1.237
267
268 CPU time used: 10001.00 ms
269 Wall clock time passed: 10001.12 ms
270
271 Process returned 0 (0x0) execution time : 10.065 s
272 Press any key to continue.
273 */

Listing 1.3.2: checkerTickets.cpp


1 #include "testlib.h"
2 #include <vector>
3
4 using namespace std;
5
6 //const std::string output_secret = "131b432f-1f84-4f8f-9d55-41e868a6bc5d";
7
8 int main()
9 //int main(int argc, char * argv[])
10 {
11 int argc=4;
12
13 char* argv[] =
14 {
15 (char*)"checker",
16 (char*)"../tests/7-14.in", // input
17 (char*)"../tests/7-14.out", // rezultat corect
18 (char*)"tickets.out", // rezultat de verificat si acordat punctaj
19 };
20
21 cout<<"argc = "<<argc<<"\n";
22 for(int kk=0;kk<argc;kk++)
23 cout<<argv[kk]<<"\n";
24 cout<<"----------------------\n";
25
26 registerChecker("tickets", argc, argv);
27
28 //readBothSecrets(output_secret);
29 //readBothGraderResults();
30
31 long long answer = ouf.readLong(); // contestant output
32 long long actual = ans.readLong(); // correct answer
33
34
35 // Read from input file
36 //inf.readLine(); // Input secret
37 int c = inf.readInt();
38 int s = inf.readInt();
39 int k = inf.readInt();
40 std::vector<std::vector<int>> d;
41 d.resize(c);
42 for (int i = 0; i < c; i++)
43 {
44 d[i].resize(s);
45 for (int j = 0; j < s; j++)
46 {
47 d[i][j] = inf.readInt();
48 }
49 }
50
51 // Validate output file rows
CAPITOLUL 1. IOI 2020 65

52 std::vector<std::vector<int>> x; //x[day][company] = location


53 x.resize(k);
54 for (int i = 0; i < c; i++)
55 {
56 for (int j = 0; j < s; j++)
57 {
58 int day = ouf.readInt();
59 if (0 <= day && day < k)
60 {
61 // Ship i,j was sent on day loc
62 x[day].push_back(d[i][j]);
63 }
64 else
65 if (day != -1)
66 {
67 quitf(_wa,
68 "Ticket %d of color %d is played on invalid day %d",
69 i, j, day);
70 }
71 }
72
73 // All days should now have i+1 boats
74 for (int j = 0; j < k; j++)
75 {
76 if ((int)x[j].size() < i + 1)
77 {
78 quitf(_wa, "There is no ticket of color %d on day %d", i, j);
79 }
80 else
81 if ((int)x[j].size() > i + 1)
82 {
83 quitf(_wa,
84 "There is multiple tickets of color %d on day %d",
85 i, j);
86 }
87 }
88 }
89
90 // Calculate actual cost
91 long long calculated = 0;
92 for (int i = 0; i < k; i++)
93 {
94 sort(x[i].begin(), x[i].end());
95 for (int j = 0; j < c; j++)
96 {
97 calculated += abs(x[i][j] - x[i][c/2]);
98 }
99 }
100
101 if (calculated != answer)
102 {
103 quitf(_wa,
104 "Contestant returned %lld but the tickets gives a total value of %lld",
105 calculated, answer);
106 }
107
108 if (answer > actual)
109 {
110 quitf(_fail, "Contestant found a better solution than model.");
111 }
112 else
113 if (answer < actual)
114 {
115 quitf(_wa,
116 "Contestant returned %lld while correct return value is %lld.",
117 answer, actual);
118 }
119
120 quit(_ok);
121 }
122 /*
123 argc = 4
124 checker
125 ../tests/7-14.in
126 ../tests/7-14.out
127 tickets.out
CAPITOLUL 1. IOI 2020 66

128 ----------------------
129 1
130 Correct
131
132 Process returned 0 (0x0) execution time : 4.211 s
133 Press any key to continue.
134 */

Listing 1.3.3: tickets_300231.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300231 924 ms 90620 KB
3
4 #include "tickets.h"
5 #include <cassert>
6 #include <cstdio>
7 #include <vector>
8 #include <string>
9
10 #include <iostream> /* cout */
11 #include <iomanip> /* setprecision */
12 #include <chrono> /* chrono */
13
14 #include <algorithm>
15 #include <queue>
16 #include <tuple>
17
18 using namespace std;
19
20 static int n;
21 static int m;
22 static int k;
23 static std::vector<std::vector<int>> d;
24 static std::vector<std::vector<int>> x;
25 static int called = 0;
26
27 // ---------------------------------------------------
28
29 long long find_maximum(int k, vector<vector<int>> x)
30 {
31 int n = x.size();
32 int m = x[0].size();
33
34 vector<vector<int>> sgn(n, vector<int>(m));
35 vector<pair<int, pair<int, int>>> profit;
36
37 for (int i = 0; i < n; ++i)
38 {
39 for (int j = 0; j < k; ++j)
40 {
41 sgn[i][j] = -1;
42 profit.emplace_back(x[i][j] + x[i][j + m - k], make_pair(i, j));
43 }
44 }
45
46 nth_element(profit.begin(), profit.begin() + n * k / 2, profit.end());
47
48 for (int i = n * k / 2; i < n * k; ++i)
49 {
50 int u, v;
51 tie(u, v) = profit[i].second;
52 ++sgn[u][v];
53 ++sgn[u][v + m - k];
54 }
55
56 long long ret = 0;
57 vector<queue<int>> pos(n), neg(n);
58 for (int i = 0; i < n; ++i)
59 {
60 for (int j = 0; j < m; ++j)
61 {
62 if (sgn[i][j] > 0)
63 {
64 pos[i].push(j);
65 ret += x[i][j];
CAPITOLUL 1. IOI 2020 67

66 }
67 else if (sgn[i][j] < 0)
68 {
69 neg[i].push(j);
70 ret -= x[i][j];
71 }
72 }
73 }
74
75 vector<int> level(n, -1);
76 vector<vector<int>> answer(n, vector<int>(m, -1));
77
78 for (int rep = 0; rep < k; ++rep)
79 {
80 int val = 0;
81 for (int i = 0; i < n; ++i)
82 {
83 if (pos[i].empty())
84 {
85 --val;
86 answer[i][neg[i].front()] = rep;
87 neg[i].pop();
88 level[i] = rep;
89 }
90 else if (neg[i].empty())
91 {
92 ++val;
93 answer[i][pos[i].front()] = rep;
94 pos[i].pop();
95 level[i] = rep;
96 }
97 }
98
99 for (int i = 0; i < n; ++i)
100 {
101 if (level[i] == rep) continue;
102 if (val < 0)
103 {
104 ++val;
105 answer[i][pos[i].front()] = rep;
106 pos[i].pop();
107 }
108 else
109 {
110 --val;
111 answer[i][neg[i].front()] = rep;
112 neg[i].pop();
113 }
114 }
115 }
116
117 allocate_tickets(answer);
118
119 return ret;
120 }
121
122 // ---------------------------------------------------
123
124 static void check(bool cond, std::string message)
125 {
126 if (!cond)
127 {
128 printf("%s\n", message.c_str());
129 exit(0);
130 }
131 }
132
133 void allocate_tickets( std::vector<std::vector<int>> _d)
134 {
135 check(!called, "allocate_tickets called more than once");
136 d = _d;
137 check((int)d.size() == n,
138 "allocate_tickets called with parameter of wrong size");
139 for (int i = 0; i < n; i++)
140 {
141 check((int)d[i].size() == m,
CAPITOLUL 1. IOI 2020 68

142 "allocate_tickets called with parameter of wrong size");


143 }
144 called = 1;
145 }
146
147 int main()
148 {
149 std::clock_t c_start = std::clock();
150 auto t_start = std::chrono::high_resolution_clock::now();
151
152 auto t1 = clock();
153
154 std::freopen("../tests/7-14.in", "r", stdin) ;
155 std::freopen("tickets.out", "w", stdout) ;
156
157 assert(scanf("%d %d %d", &n, &m, &k) == 3);
158 x.resize(n);
159 for (int i = 0; i < n; i++)
160 {
161 x[i].resize(m);
162 for (int j=0; j < m; j++)
163 {
164 assert(scanf("%d", &x[i][j]) == 1);
165 }
166 }
167 fclose(stdin);
168
169 auto t2 = clock();
170
171 long long answer = find_maximum(k, x);
172
173 auto t3 = clock();
174
175 check(called, "failure to call allocate_tickets");
176
177 printf("%lld\n", answer);
178 for (int i = 0; i < n; i++)
179 {
180 for (int j = 0; j < m; j++)
181 {
182 if (j) printf(" ");
183 printf("%d", d[i][j]);
184 }
185 printf("\n");
186 }
187 fclose(stdout);
188
189 auto t4 = clock();
190
191 std::clock_t c_end = std::clock();
192 auto t_end = std::chrono::high_resolution_clock::now();
193
194 // reset console output
195 freopen("CON", "w", stdout);
196
197 //std::cout <<result<<’\n’<<’\n’;
198 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
199 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
200 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
201
202 std::cout << std::fixed << std::setprecision(2)
203 << "\nCPU time used: "
204 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
205 << "Wall clock time passed: "
206 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
207 << " ms\n";
208
209 return 0;
210 }
211 /*
212 t2-t1 = 4.252
213 t3-t2 = 1.109
214 t4-t3 = 1.203
215
216 CPU time used: 6564.00 ms
217 Wall clock time passed: 6564.53 ms
CAPITOLUL 1. IOI 2020 69

218
219 Process returned 0 (0x0) execution time : 6.658 s
220 Press any key to continue.
221 */

Listing 1.3.4: tickets_300591.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300591 853 ms 62328 KB
3
4 #include "tickets.h"
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 static int n;
10 static int m;
11 static int k;
12 static std::vector<std::vector<int>> d;
13 static std::vector<std::vector<int>> x;
14 static int called = 0;
15
16 // ---------------------------------------------------
17
18 long long find_maximum(int K, std::vector<std::vector<int>> X)
19 {
20 int N = int(X.size());
21 int M = int(X[0].size());
22
23 std::vector<std::vector<int>> answer(N, std::vector<int>(M, -1));
24
25 struct cnd_t
26 {
27 int val;
28 int i;
29 };
30
31 std::vector<cnd_t> cnds; cnds.reserve(N*K);
32
33 int64_t tot_val = 0;
34 {
35 std::vector<std::pair<int, int>> vals(M);
36 for (int i = 0; i < N; i++)
37 {
38 for (int j = 0; j < K; j++)
39 {
40 tot_val -= X[i][j];
41 cnds.push_back({X[i][j] + X[i][j+(M-K)], i});
42 }
43 }
44 }
45
46 auto md = cnds.begin() + N/2*K;
47
48 std::nth_element(cnds.begin(), md, cnds.end(),
49 [&](const auto& a, const auto& b) { return a.val > b.val; });
50
51 cnds.erase(md, cnds.end());
52
53 std::vector<int> num_hi(N, 0);
54 for (auto [v, i] : cnds)
55 {
56 tot_val += v;
57 num_hi[i]++;
58 }
59
60 for (int k = K-1; k >= 0; k--)
61 {
62 int c0 = 0;
63 int c1 = 0;
64 for (int i = 0; i < N; i++)
65 {
66 if (num_hi[i] == k+1)
67 {
68 c1++;
CAPITOLUL 1. IOI 2020 70

69 }
70 else if (num_hi[i] == 0)
71 {
72 c0++;
73 }
74 else
75 {
76 // do nothing
77 }
78 }
79
80 assert(c0 <= N/2 && c1 <= N/2);
81 for (int i = 0; i < N; i++)
82 {
83 int d;
84 if (num_hi[i] == k+1)
85 {
86 d = 1;
87 }
88 else if (num_hi[i] == 0)
89 {
90 d = 0;
91 }
92 else if (c0 < N/2)
93 {
94 c0++;
95 d = 0;
96 }
97 else if (c1 < N/2)
98 {
99 c1++;
100 d = 1;
101 }
102 else assert(false);
103
104 if (d)
105 {
106 answer[i][M - (num_hi[i]--)] = k;
107 }
108 else
109 {
110 answer[i][k - num_hi[i]] = k;
111 }
112 }
113 }
114
115 allocate_tickets(answer);
116
117 return tot_val;
118 }
119
120 // ---------------------------------------------------
121
122 static void check(bool cond, std::string message)
123 {
124 if (!cond)
125 {
126 printf("%s\n", message.c_str());
127 exit(0);
128 }
129 }
130
131 void allocate_tickets( std::vector<std::vector<int>> _d)
132 {
133 check(!called, "allocate_tickets called more than once");
134 d = _d;
135 check((int)d.size() == n,
136 "allocate_tickets called with parameter of wrong size");
137 for (int i = 0; i < n; i++)
138 {
139 check((int)d[i].size() == m,
140 "allocate_tickets called with parameter of wrong size");
141 }
142 called = 1;
143 }
144
CAPITOLUL 1. IOI 2020 71

145 int main()


146 {
147 std::clock_t c_start = std::clock();
148 auto t_start = std::chrono::high_resolution_clock::now();
149
150 auto t1 = clock();
151
152 std::freopen("../tests/7-14.in", "r", stdin) ;
153 std::freopen("tickets.out", "w", stdout) ;
154
155 assert(scanf("%d %d %d", &n, &m, &k) == 3);
156 x.resize(n);
157 for (int i = 0; i < n; i++)
158 {
159 x[i].resize(m);
160 for (int j=0; j < m; j++)
161 {
162 assert(scanf("%d", &x[i][j]) == 1);
163 }
164 }
165 fclose(stdin);
166
167 auto t2 = clock();
168
169 long long answer = find_maximum(k, x);
170
171 auto t3 = clock();
172
173 check(called, "failure to call allocate_tickets");
174
175 printf("%lld\n", answer);
176 for (int i = 0; i < n; i++)
177 {
178 for (int j = 0; j < m; j++)
179 {
180 if (j) printf(" ");
181 printf("%d", d[i][j]);
182 }
183 printf("\n");
184 }
185 fclose(stdout);
186
187 auto t4 = clock();
188
189 std::clock_t c_end = std::clock();
190 auto t_end = std::chrono::high_resolution_clock::now();
191
192 // reset console output
193 freopen("CON", "w", stdout);
194
195 //std::cout <<result<<’\n’<<’\n’;
196 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
197 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
198 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
199
200 std::cout << std::fixed << std::setprecision(2)
201 << "\nCPU time used: "
202 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
203 << "Wall clock time passed: "
204 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
205 << " ms\n";
206
207 return 0;
208 }
209 /*
210 t2-t1 = 4.843
211 t3-t2 = 0.422
212 t4-t3 = 1.703
213
214 CPU time used: 6968.00 ms
215 Wall clock time passed: 6968.66 ms
216
217 Process returned 0 (0x0) execution time : 7.031 s
218 Press any key to continue.
219 */
CAPITOLUL 1. IOI 2020 72

Listing 1.3.5: tickets_300700.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300700 1183 ms 62428 KB
3
4 #include <bits/stdc++.h>
5 #include "tickets.h"
6
7 using namespace std;
8
9 static int n;
10 static int m;
11 static int k;
12 static std::vector<std::vector<int>> d;
13 static std::vector<std::vector<int>> x;
14 static int called = 0;
15
16 // ---------------------------------------------------
17
18 typedef long long ll;
19 typedef pair <int, int> pii;
20
21 ll find_maximum(int K, vector <vector <int>> x)
22 {
23 int N = x.size();
24 int M = x[0].size();
25 ll sol = 0;
26 vector <pii> diffs;
27
28 for (int i = 0; i < N; i++)
29 for (int j = 1; j <= K; j++)
30 {
31 diffs.push_back({x[i][K - j] + x[i][M - j], i});
32 sol -= x[i][K - j];
33 }
34
35 sort(diffs.begin(), diffs.end(), greater <pii> ());
36
37 vector <int> cnt(N);
38 for (int i = 0; i < K * N / 2; i++)
39 {
40 cnt[diffs[i].second]++;
41 sol += diffs[i].first;
42 }
43
44 vector <int> lo(N, 0), hi(N, M - 1);
45 vector <vector <int>> round(N, vector <int> (M, -1));
46
47 for (int k = 0; k < K; k++)
48 {
49 vector <pii> sorted;
50 for (int i = 0; i < N; i++)
51 sorted.push_back({cnt[i], i});
52
53 sort(sorted.begin(), sorted.end(), greater <pii> ());
54
55 for (int i = 0; i < N; i++)
56 {
57 int colour = sorted[i].second;
58 if (i < N / 2)
59 {
60 round[colour][hi[colour]--] = k;
61 cnt[colour]--;
62 }
63 else
64 round[colour][lo[colour]++] = k;
65 }
66 }
67
68 allocate_tickets(round);
69
70 return sol;
71 }
72
73 // ---------------------------------------------------
74
CAPITOLUL 1. IOI 2020 73

75 static void check(bool cond, std::string message)


76 {
77 if (!cond)
78 {
79 printf("%s\n", message.c_str());
80 exit(0);
81 }
82 }
83
84 void allocate_tickets( std::vector<std::vector<int>> _d)
85 {
86 check(!called, "allocate_tickets called more than once");
87 d = _d;
88 check((int)d.size() == n,
89 "allocate_tickets called with parameter of wrong size");
90 for (int i = 0; i < n; i++)
91 {
92 check((int)d[i].size() == m,
93 "allocate_tickets called with parameter of wrong size");
94 }
95 called = 1;
96 }
97
98 int main()
99 {
100 std::clock_t c_start = std::clock();
101 auto t_start = std::chrono::high_resolution_clock::now();
102
103 auto t1 = clock();
104
105 std::freopen("../tests/7-14.in", "r", stdin) ;
106 std::freopen("tickets.out", "w", stdout) ;
107
108 assert(scanf("%d %d %d", &n, &m, &k) == 3);
109 x.resize(n);
110 for (int i = 0; i < n; i++)
111 {
112 x[i].resize(m);
113 for (int j=0; j < m; j++)
114 {
115 assert(scanf("%d", &x[i][j]) == 1);
116 }
117 }
118 fclose(stdin);
119
120 auto t2 = clock();
121
122 long long answer = find_maximum(k, x);
123
124 auto t3 = clock();
125
126 check(called, "failure to call allocate_tickets");
127
128 printf("%lld\n", answer);
129 for (int i = 0; i < n; i++)
130 {
131 for (int j = 0; j < m; j++)
132 {
133 if (j) printf(" ");
134 printf("%d", d[i][j]);
135 }
136 printf("\n");
137 }
138 fclose(stdout);
139
140 auto t4 = clock();
141
142 std::clock_t c_end = std::clock();
143 auto t_end = std::chrono::high_resolution_clock::now();
144
145 // reset console output
146 freopen("CON", "w", stdout);
147
148 //std::cout <<result<<’\n’<<’\n’;
149 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
150 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 1. IOI 2020 74

151 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
152
153 std::cout << std::fixed << std::setprecision(2)
154 << "\nCPU time used: "
155 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
156 << "Wall clock time passed: "
157 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
158 << " ms\n";
159
160 return 0;
161 }
162 /*
163 t2-t1 = 4.702
164 t3-t2 = 3.39
165 t4-t3 = 1.203
166
167 CPU time used: 9295.00 ms
168 Wall clock time passed: 9295.29 ms
169
170 Process returned 0 (0x0) execution time : 9.358 s
171 Press any key to continue.
172 */

Listing 1.3.6: tickets_300781.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300781 1153 ms 54392 KB
3
4 #include <bits/stdc++.h>
5 #include "tickets.h"
6
7 using namespace std;
8
9 static int n;
10 static int m;
11 static int k;
12 static std::vector<std::vector<int>> d;
13 static std::vector<std::vector<int>> x;
14 static int called = 0;
15
16 // ---------------------------------------------------
17
18 typedef long long ll;
19 typedef pair <int,int> pii;
20
21 ll find_maximum(int k, vector <vector <int>> a)
22 {
23 int n = a.size();
24 int m = a[0].size();
25
26 vector <int> PrefixTaken(n, k);
27 vector <int> SuffixTaken(n, 0);
28
29 ll Ans = 0;
30 for(int i = 0; i < n; i++)
31 for(int j = 0; j < k; j++)
32 Ans -= a[i][j];
33
34 priority_queue <pair <int,pii>> q;
35
36 for(int i = 0; i < n; i++)
37 q.emplace(a[i][m - 1] + a[i][k - 1], pii(i, m - 1));
38
39 for(int repeat = 0; repeat < k * n / 2; repeat++)
40 {
41 pair <int,pii> p = q.top();
42 q.pop();
43
44 int x = p.first;
45 int i = p.second.first;
46 int j = p.second.second;
47 int r = m - j + 1;
48
49 PrefixTaken[i]--;
50 SuffixTaken[i]++;
CAPITOLUL 1. IOI 2020 75

51
52 Ans += x;
53
54 if(k - r >= 0)
55 q.emplace(a[i][m - r] + a[i][k - r], pii(i, j - 1));
56 }
57
58 vector <vector <int>> s(n, vector <int> (m, -1));
59
60 vector <pii> Sum(k);
61 for(int j = 0; j < k; j++) Sum[j] = pii(0, j);
62
63 for(int i = 0; i < n; i++)
64 {
65 for(int j = 0; j < PrefixTaken[i]; j++)
66 {
67 Sum[k - j - 1].first--;
68 s[i][j] = Sum[k - j - 1].second;
69 }
70
71 for(int j = 0; j < SuffixTaken[i]; j++)
72 {
73 Sum[j].first++;
74 s[i][m - j - 1] = Sum[j].second;
75 }
76
77 sort(Sum.begin(), Sum.end());
78 }
79
80 for(int j = 0; j < k; j++)
81 assert(Sum[j].first == 0);
82
83 return allocate_tickets(s), Ans;
84 }
85
86 // ---------------------------------------------------
87
88 static void check(bool cond, std::string message)
89 {
90 if (!cond)
91 {
92 printf("%s\n", message.c_str());
93 exit(0);
94 }
95 }
96
97 void allocate_tickets( std::vector<std::vector<int>> _d)
98 {
99 check(!called, "allocate_tickets called more than once");
100 d = _d;
101 check((int)d.size() == n,
102 "allocate_tickets called with parameter of wrong size");
103 for (int i = 0; i < n; i++)
104 {
105 check((int)d[i].size() == m,
106 "allocate_tickets called with parameter of wrong size");
107 }
108 called = 1;
109 }
110
111 int main()
112 {
113 std::clock_t c_start = std::clock();
114 auto t_start = std::chrono::high_resolution_clock::now();
115
116 auto t1 = clock();
117
118 std::freopen("../tests/7-14.in", "r", stdin) ;
119 std::freopen("tickets.out", "w", stdout) ;
120
121 assert(scanf("%d %d %d", &n, &m, &k) == 3);
122 x.resize(n);
123 for (int i = 0; i < n; i++)
124 {
125 x[i].resize(m);
126 for (int j=0; j < m; j++)
CAPITOLUL 1. IOI 2020 76

127 {
128 assert(scanf("%d", &x[i][j]) == 1);
129 }
130 }
131 fclose(stdin);
132
133 auto t2 = clock();
134
135 long long answer = find_maximum(k, x);
136
137 auto t3 = clock();
138
139 check(called, "failure to call allocate_tickets");
140
141 printf("%lld\n", answer);
142 for (int i = 0; i < n; i++)
143 {
144 for (int j = 0; j < m; j++)
145 {
146 if (j) printf(" ");
147 printf("%d", d[i][j]);
148 }
149 printf("\n");
150 }
151 fclose(stdout);
152
153 auto t4 = clock();
154
155 std::clock_t c_end = std::clock();
156 auto t_end = std::chrono::high_resolution_clock::now();
157
158 // reset console output
159 freopen("CON", "w", stdout);
160
161 //std::cout <<result<<’\n’<<’\n’;
162 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
164 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
165
166 std::cout << std::fixed << std::setprecision(2)
167 << "\nCPU time used: "
168 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
169 << "Wall clock time passed: "
170 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
171 << " ms\n";
172
173 return 0;
174 }
175 /*
176 t2-t1 = 4.326
177 t3-t2 = 2.063
178 t4-t3 = 1.203
179
180 CPU time used: 7592.00 ms
181 Wall clock time passed: 7592.16 ms
182
183 Process returned 0 (0x0) execution time : 7.655 s
184 Press any key to continue.
185 */

Listing 1.3.7: tickets_300782.cpp


1 // https://oj.uz/problems/source/533
2 // https://oj.uz/submission/300782 1063 ms 63224 KB
3
4 #include "tickets.h"
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 static int n;
10 static int m;
11 static int k;
12 static std::vector<std::vector<int>> dd;
13 static std::vector<std::vector<int>> x;
CAPITOLUL 1. IOI 2020 77

14 static int called = 0;


15
16 // ---------------------------------------------------
17
18 typedef long long ll;
19
20 priority_queue<pair<ll,int> > q;
21 ll sum;
22 int pos[1510],tot,st[1510][1510],R[1510],L[1510];
23 pair<int,int> d[1510];
24 int vis[1510];
25
26 ll find_maximum(int k, vector<vector<int> > x)
27 {
28 int n = x.size();
29 int m = x[0].size();
30 vector<vector<int> > ans;
31
32 for (int i = 0; i < n; i++)
33 {
34 vector<int> row(m);
35 for (int j = 0; j < m; j++) row[j]=-1;
36 ans.push_back(row);
37 }
38
39 for (int i=0;i<n;i++)
40 {
41 for (int j=0;j<k;j++) st[i][j]=-1;
42 q.push(make_pair(x[i][k-1]+x[i][m-1],i));
43 pos[i]=1;
44 }
45
46 for (int I=1;I<=n*k/2;I++)
47 {
48 int i=q.top().second; q.pop();
49 st[i][k-pos[i]]++,st[i][m-pos[i]]++;
50 pos[i]++;
51 if (k-pos[i]>=0)
52 {
53 q.push(make_pair(x[i][k-pos[i]]+x[i][m-pos[i]],i));
54 }
55 }
56
57 for (int i=0;i<n;i++) R[i]=pos[i],vis[i]=-1;
58
59 tot=-1;
60 while (k--)
61 {
62 for (int i=0;i<n;i++) d[i]=make_pair(R[i],i);
63
64 sort(d,d+n);
65 reverse(d,d+n);
66
67 tot++;
68 for (int i=0;i<n/2;i++) vis[d[i].second]=tot;
69
70 for (int i=0;i<n;i++)
71 if (vis[i]==tot)
72 {
73 ans[i][m-R[i]+1]=tot;
74 R[i]--;
75 }
76 else
77 {
78 ans[i][L[i]]=tot;
79 L[i]++;
80 }
81 }
82
83 allocate_tickets(ans);
84 for (int i=0;i<n;i++) for (int j=0;j<m;j++) sum+=st[i][j]*x[i][j];
85
86 return sum;
87 }
88
89 // ---------------------------------------------------
CAPITOLUL 1. IOI 2020 78

90
91 static void check(bool cond, std::string message)
92 {
93 if (!cond)
94 {
95 printf("%s\n", message.c_str());
96 exit(0);
97 }
98 }
99
100 void allocate_tickets( std::vector<std::vector<int>> _d)
101 {
102 check(!called, "allocate_tickets called more than once");
103 dd = _d;
104 check((int)dd.size() == n,
105 "allocate_tickets called with parameter of wrong size");
106 for (int i = 0; i < n; i++)
107 {
108 check((int)dd[i].size() == m,
109 "allocate_tickets called with parameter of wrong size");
110 }
111 called = 1;
112 }
113
114 int main()
115 {
116 std::clock_t c_start = std::clock();
117 auto t_start = std::chrono::high_resolution_clock::now();
118
119 auto t1 = clock();
120
121 std::freopen("../tests/7-14.in", "r", stdin) ;
122 std::freopen("tickets.out", "w", stdout) ;
123
124 assert(scanf("%d %d %d", &n, &m, &k) == 3);
125 x.resize(n);
126 for (int i = 0; i < n; i++)
127 {
128 x[i].resize(m);
129 for (int j=0; j < m; j++)
130 {
131 assert(scanf("%d", &x[i][j]) == 1);
132 }
133 }
134 fclose(stdin);
135
136 auto t2 = clock();
137
138 long long answer = find_maximum(k, x);
139
140 auto t3 = clock();
141
142 check(called, "failure to call allocate_tickets");
143
144 printf("%lld\n", answer);
145 for (int i = 0; i < n; i++)
146 {
147 for (int j = 0; j < m; j++)
148 {
149 if (j) printf(" ");
150 printf("%d", dd[i][j]);
151 }
152 printf("\n");
153 }
154 fclose(stdout);
155
156 auto t4 = clock();
157
158 std::clock_t c_end = std::clock();
159 auto t_end = std::chrono::high_resolution_clock::now();
160
161 // reset console output
162 freopen("CON", "w", stdout);
163
164 //std::cout <<result<<’\n’<<’\n’;
165 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 1. IOI 2020 79

166 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
167 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
168
169 std::cout << std::fixed << std::setprecision(2)
170 << "\nCPU time used: "
171 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
172 << "Wall clock time passed: "
173 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
174 << " ms\n";
175
176 return 0;
177 }
178 /*
179 t2-t1 = 4.734
180 t3-t2 = 1.993
181 t4-t3 = 1.422
182
183 CPU time used: 8149.00 ms
184 Wall clock time passed: 8149.66 ms
185
186 Process returned 0 (0x0) execution time : 8.197 s
187 Press any key to continue.
188 */

1.3.3 *Rezolvare detaliat 

1.4 biscuits
Problema 4 - Packing Biscuits 100 de puncte
Author: Mikhail Tikhomirov

M tu³a Khong organizeaz  o competiµie cu x participanµi ³i dore³te s  ofere ec rui participant


câte o pung  de biscuiµi. Exist  k tipuri diferite de biscuiµi, numerotate de la 0 la k  1. Fiecare
biscuit de tip i (0 & i & k  1) are o valoare a aromei de 2 . M tu³a Khong are în c mar  ai
i

(posibil zero) biscuiµi de tip i.


Fiecare pung  a M tu³ii Khong va conµine zero sau mai mulµi biscuiµi din ecare tip. Num rul
total de biscuiµi de tip i din toate pungile nu trebuie s  dep ³easc  ai. Suma valorilor aromelor
tuturor biscuiµilor dintr-o pung  se nume³te totalul aromelor pungii.
Ajutaµi-o pe M tu³a Khong s  ae câte valori diferite y exist , astfel încât s  existe o posibilitate
de a împacheta cele x pungi de biscuiµi, ecare pung  având totalul aromelor egal cu y .

Detalii de implementare
Trebuie s  implementaµi urm toarea funcµie:

int64 count_tastiness(int64 x, int64[] a)

ˆ x: num rul de pungi de biscuµi de împachetat.


ˆ a: un ³ir de lungime k . Pentru 0 & i & k  1, ai indic  num rul de biscuiµi de tip i din
c mar .
ˆ Funcµia trebuie s  returneze num rul de valori distincte y , pentru care M tu³a poate s 
împacheteze biscuµii în x pungi, astfel încât ecare pung  s  aib  totalul aromelor egal cu
y.
ˆ Funcµia va  apelat  în total de q ori (consultaµi secµiunile Restricµii ³i Subtaskuri pentru
diferitele valori permise ale lui q ). Fiecare dintre aceste apeluri trebuie tratat ca un scenariu
separat.

Exemple
Exemplul 1 S  consider m apelul urm tor:
count_tastiness(3, [5, 2, 1])
CAPITOLUL 1. IOI 2020 80

Acesta înseamn  c  M tu³a dore³te s  împacheteze 3 pungi, ³i exist  3 tipuri de biscuiµi în magazin:
ˆ 5 biscuiµi de tip 0, ecare având valoarea aromei 1,
ˆ 2 biscuiµi de tip 1, ecare având valoarea aromei 2,
ˆ 1 biscuit de tip 2, ecare având valoarea aromei 4.

Valorile y posibile sunt 0, 1, 2, 3, 4. De exemplu, pentru a împacheta 3 pungi cu totalul
aromelor având valoarea 3, M tu³a poate împacheta:
ˆ o pung  ce conµine trei biscuiµi de tip 0 ³i
ˆ dou  pungi, ecare conµinând câte un biscuit de tip 0 ³i un biscuit de tip 1.

Deoarece exist  5 valori posibile pentru y , funcµia trebuie s  returneze 5.

Exemplul 2 S  consider m apelul urm tor:


count_tastiness(2, [2, 1, 2])

Acesta înseamn  c  M tu³a dore³te s  împacheteze 2 pungi, ³i exist  3 tipuri de biscuiµi în


magazin:
ˆ 2 biscuiµi de tip 0, ecare având valoarea aromei 1,
ˆ 1 biscuit de tip 1, ecare având valoarea aromei 2,
ˆ 2 biscuiµi de tip 2, ecare având valoarea aromei 4.

Valorile posibile sunt 0, 1, 2, 4, 5, 6. Deoarece exist  6 valori posibile pentru y , funcµia va
trebui s  returneze valoarea 6.

Restricµii
ˆ 1 & k & 60
ˆ 1 & q & 1000
1 & x & 10
18
ˆ
0 & ai & 10 (for all 0 & i & k  1)
18
ˆ
ˆ Pentru ecare apel al count_tastiness, suma valorilor aromelor tuturor biscuiµilor din
18
c mar  nu va dep ³i 10 .

Subtaskuri
1. (9 puncte) q & 10, ³i pentru ecare apel al count_tastiness, valorilor aromelor tuturor
biscuiµilor din c mar  nu dep ³e³te 100 000.
2. (12 puncte) x 1, 1 & 10
3. (21 de puncte) x & 10 000, q & 10
4. (35 de puncte) Pentru ecare apel al count_tastiness rezultatul corect nu dep ³e³te
200 000.
5. (23 de puncte) F r  restricµii suplimentare.

Sample grader
Sample graderul cite³te intrarea în formatul urm tor. Prima linie conµine un întreg q . Dup  aceea,
urmeaz  q perechi de linii, ecare pereche reprezentând câte un scenariu în urm torul format:
ˆ linia 1: k x
ˆ linia 2: a0 a1 ... ak  1
CAPITOLUL 1. IOI 2020 81

Ie³irea graderului este în urm torul format:


ˆ linia i (1 & i & q ): valoarea returnat  de count_tastiness pentru cel de al i-lea scenariu
din input.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 2000 MB

1.4.1 Indicaµii de rezolvare


We say that a value of is good if it is possible to pack bags, each of total tastiness.

Subtask 1
We proceed by checking every value of y , noting that y & 10 . For each value of y , we use the
5

`greedy coin change' algorithm to decide if it is good or not.

Implementing this naively gives a runtime of  10


5
x operations. There are two ways to speed
it up.
One possible way is to remove x coins at a time instead of removing them one by one.
5
The other way is to check for values of y at most 10 ©x, so that we have less values of y to
check in the even that x is large. However, contestants will have to return 1 immediately if x is
5
larger than 10 to avoid actually packing x empty bags!

Subtask 2
We begin with the following observation:
ˆ If ai ' 3, the solution remains unchanged if we decrease ai by 2 and increase ai  1 by
1.
Using this observation, we may combine smaller biscuits into bigger ones such that ai "
r0, 1, 2x.
A second observation is the following:
ˆ Suppose ai 0 for some i. Let y be any integer, we perform division with remainder and
i i
write y 2 q  r. Then y is good if and only if 2 q is good and r is good.
ˆ Suppose ai j 0 for all i. Then for any integer y , y is good if and only if the total tastiness
is at least y .
Using this observation, we may split the input into consecutive segments of non-zero values.
The nal answer is the product of the answers individual segments.

Subtask 3
Similar to the previous subtask, if ai ' x  2, we may decrement ai by 2 and increase ai  1
by 1.
We now proceed by dynamic programming. Let f n, i be the answer if we replace a0, a1,
..., ai  1 by 0 and increment ai by n.
We obtain the following recurrence relation:

n  ai $ x
nai
f &,i  1
f 0, 60 1, f n, i w 2
1 n  ai ' x
nai naix
f 2
&,i  1  f  2
&,i 

To explain the relation, let S be the set of all valid y when a0, a1, ..., ai  1 are replaced
by 0 and ai incremented by n.
i1 i
If y is a multiple of 2 , then the biscuits of tastiness 2 must be used in pairs. We therefore
i1
merge pairs of biscuits to make biscuits of tastiness 2 .
i1 i
If y is not a multiple of 2 but not a multiple of 2 , then we need to use x biscuits of tastiness
i
2 . The remaining ai  n  x biscuits must be used up in pairs.
i
If y is not a multiple of 2 , then y cannot be good.
CAPITOLUL 1. IOI 2020 82

Subtask 4
Dene
= ai
i
i
si 2
j 0

to be the total tastiness the rst i  1 types of biscuits.


We need another observation:
ˆ Let 2 & y $ 2 . Then y is good if and only if si ' x y and y  2 is good.
i i1 i

The forward direction is clear. We will justify the backward direction. For simplicity, assume
i
ai  1 ai  2 ... ak  1 0. Since y  2 is good, consider some way to pack them,
remove these biscuits from our collection.
i
We need to pack the remaining biscuits into x packs of 2 each. We do so by merging smaller
biscuits into bigger ones- whenever there are two biscuits of tastiness 2 for some j $ i, we replace
j
j 1
them with a single biscuit of tastiness 2 . It can be shown that we will end up with at least x
i
biscuits of tastiness 2 each after the merging.
We now have an ecient way to enumerate all the solutions. Let Ai be the set of good values
i
which are at most 2 . We then have

Ai1 Ai < ty ¶y  2
i
" Ai and si©x ' yz
We can now explicitly list out all elements of Ai . Thus the time taken for a single query is
linear in the size of the answer returned.

Subtask 5
Let g n be the number of possible y which are less than n. The following recurrence relation
solves the problem:
ˆ Let 2 $ n & 2 . Then
i i1

i i
g n g 2   g min n, 1  si©x  2 
with the initial values g n 0 for all n & 0 and g 1 1.
2
By using a hash table to store all previously computed values, this algorithm runs in O k  time.
0 1 2 i1
The time complexity is justied by the fact that once g 2 , g 2 , g 2 , ..., g 2  are
i
computed, we only need O k  time to compute g 2 .

1.4.2 Coduri surs 

Listing 1.4.1: graderBiscuits-sandbox.cpp


1 #include "biscuits.h"
2 #include <cassert>
3 #include <cstdio>
4
5 #include <iostream> /* cout */
6 #include <iomanip> /* setprecision */
7 #include <chrono> /* chrono */
8
9 // ---------------------------------------------------
10
11 #include <vector>
12 #include <map>
13
14 using namespace std;
15
16 map<long long, long long> m;
17
18 long long f(vector<long long> &s, long long x, long long n)
19 {
CAPITOLUL 1. IOI 2020 83

20 if(n<=0) return 0;
21 if(n==1) return 1;
22 if(m.find(n)!=m.end())
23 {
24 return m[n];
25 }
26 long long a = __lg(n-1);
27 return m[n] = f(s,x,1LL<<a) + f(s,x,min(n,1+s[a]/x)-(1LL<<a));
28 }
29
30 long long count_tastiness(long long x, std::vector<long long> a)
31 {
32 m.clear();
33 for(int i=1; i<(int)a.size(); i++)
34 {
35 a[i] = a[i-1] + (a[i]<<i);
36 }
37
38 while(a.size()<=60) a.push_back(a.back());
39 return f(a, x, 1+a.back());
40 }
41
42 // ---------------------------------------------------
43
44 int main()
45 {
46 std::clock_t c_start = std::clock();
47 auto t_start = std::chrono::high_resolution_clock::now();
48
49 auto t1 = clock();
50
51 std::freopen("../tests/5-11.in", "r", stdin) ;
52 std::freopen("biscuits.out", "w", stdout) ;
53
54 int q;
55 assert(scanf("%d", &q) == 1);
56 vector<int> k(q);
57 vector<long long> x(q);
58 vector<vector<long long>> a(q);
59 vector<long long> results(q);
60 for (int t = 0; t < q; t++)
61 {
62 assert(scanf("%d%lld", &k[t], &x[t]) == 2);
63 a[t] = vector<long long>(k[t]);
64 for (int i = 0; i < k[t]; i++)
65 {
66 assert(scanf("%lld", &a[t][i]) == 1);
67 }
68 }
69 fclose(stdin);
70
71 auto t2 = clock();
72
73 for (int t = 0; t < q; t++)
74 {
75 results[t] = count_tastiness(x[t], a[t]);
76 }
77
78 auto t3 = clock();
79
80 for (int t = 0; t < q; t++)
81 {
82 printf("%lld\n", results[t]);
83 }
84 fclose(stdout);
85
86 auto t4 = clock();
87
88 std::clock_t c_end = std::clock();
89 auto t_end = std::chrono::high_resolution_clock::now();
90
91 // reset console output
92 freopen("CON", "w", stdout);
93
94 //std::cout <<result<<’\n’<<’\n’;
95 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 1. IOI 2020 84

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


97 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
98
99 std::cout << std::fixed << std::setprecision(2)
100 << "\nCPU time used: "
101 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
102 << "Wall clock time passed: "
103 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
104 << " ms\n";
105
106 return 0;
107 }
108 /*
109 t2-t1 = 0.11
110 t3-t2 = 0.484
111 t4-t3 = 0.015
112
113 CPU time used: 609.00 ms
114 Wall clock time passed: 609.36 ms
115
116 Process returned 0 (0x0) execution time : 0.672 s
117 Press any key to continue.
118 */

Listing 1.4.2: checkerBiscuits.cpp


1 #include "testlib.h"
2 using namespace std;
3
4 //static string output_secret = "23b69acd873f5d7e892bae7de83615";
5
6 int main()
7 //int main(int argc, char * argv[])
8 {
9 int argc=4;
10
11 char* argv[] =
12 {
13 (char*)"checker",
14 (char*)"../tests/5-11.in", // input
15 (char*)"../tests/5-11.out", // rezultat corect
16 (char*)"biscuits.out", // rezultat de verificat si acordat punctaj
17 };
18
19 cout<<"argc = "<<argc<<"\n";
20 for(int kk=0;kk<argc;kk++)
21 cout<<argv[kk]<<"\n";
22 cout<<"----------------------\n";
23
24 registerChecker("biscuits", argc, argv);
25
26 //readBothSecrets(output_secret);
27 //readBothGraderResults();
28
29 compareRemainingLines();
30 }
31 /*
32 argc = 4
33 checker
34 ../tests/5-11.in
35 ../tests/5-11.out
36 biscuits.out
37 ----------------------
38 1
39 Correct
40
41 Process returned 0 (0x0) execution time : 0.031 s
42 Press any key to continue.
43 */

Listing 1.4.3: biscuits_305541.cpp


1 // https://oj.uz/problems/source/534
2 // https://oj.uz/submission/305541 20 ms 896 KB
CAPITOLUL 1. IOI 2020 85

3
4 #include <bits/stdc++.h>
5 #include "biscuits.h"
6
7 using namespace std;
8
9 #define debug(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
10
11 typedef long long lld;
12
13 lld count_tastiness(lld x, vector<lld> a)
14 {
15 vector <lld> status(63, 0);
16 lld sum = 0;
17 for (int i=0;i<63;i++)
18 {
19 if (i < a.size()) sum += a[i]<<i;
20 status[i] = sum;
21 }
22
23 vector <lld> dp(63, 0);
24 dp[0] = 1;
25 for (int i=1;i<63;i++)
26 {
27 lld cur = sum; dp[i] = 1;
28 for (int j=i;j--;)
29 {
30 cur = min(cur, status[j]);
31 if (x <= cur>>j)
32 {
33 cur -= x<<j;
34 dp[i] += dp[j];
35 }
36 }
37 }
38
39 return dp.back();
40 }
41
42 // ---------------------------------------------------
43
44 int main()
45 {
46 std::clock_t c_start = std::clock();
47 auto t_start = std::chrono::high_resolution_clock::now();
48
49 auto t1 = clock();
50
51 std::freopen("../tests/5-11.in", "r", stdin) ;
52 std::freopen("biscuits.out", "w", stdout) ;
53
54 int q;
55 assert(scanf("%d", &q) == 1);
56 vector<int> k(q);
57 vector<long long> x(q);
58 vector<vector<long long>> a(q);
59 vector<long long> results(q);
60 for (int t = 0; t < q; t++)
61 {
62 assert(scanf("%d%lld", &k[t], &x[t]) == 2);
63 a[t] = vector<long long>(k[t]);
64 for (int i = 0; i < k[t]; i++)
65 {
66 assert(scanf("%lld", &a[t][i]) == 1);
67 }
68 }
69 fclose(stdin);
70
71 auto t2 = clock();
72
73 for (int t = 0; t < q; t++)
74 {
75 results[t] = count_tastiness(x[t], a[t]);
76 }
77
78 auto t3 = clock();
CAPITOLUL 1. IOI 2020 86

79
80 for (int t = 0; t < q; t++)
81 {
82 printf("%lld\n", results[t]);
83 }
84 fclose(stdout);
85
86 auto t4 = clock();
87
88 std::clock_t c_end = std::clock();
89 auto t_end = std::chrono::high_resolution_clock::now();
90
91 // reset console output
92 freopen("CON", "w", stdout);
93
94 //std::cout <<result<<’\n’<<’\n’;
95 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
97 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
98
99 std::cout << std::fixed << std::setprecision(2)
100 << "\nCPU time used: "
101 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
102 << "Wall clock time passed: "
103 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
104 << " ms\n";
105
106 return 0;
107 }
108 /*
109 t2-t1 = 0.1
110 t3-t2 = 0.07
111 t4-t3 = 0.015
112
113 CPU time used: 185.00 ms
114 Wall clock time passed: 185.02 ms
115
116 Process returned 0 (0x0) execution time : 0.248 s
117 Press any key to continue.
118 */

Listing 1.4.4: biscuits_305864.cpp


1 // https://oj.uz/problems/source/534
2 // https://oj.uz/submission/305864 13 ms 928 KB
3
4 #include "biscuits.h"
5
6 #include <bits/stdc++.h>
7
8 #define fi first
9 #define se second
10 #define eb emplace_back
11 #define all(v) (v).begin(), (v).end()
12 #define rmin(r, x) r = min(r, x)
13 #define rmax(r, x) r = max(r, x)
14 #define ends ’ ’
15 #define endl ’\n’
16 #define fastio ios_base::sync_with_stdio(0), cin.tie(0)
17
18 using namespace std;
19
20 typedef long long ll;
21 typedef pair<int, int> pii;
22 typedef pair<ll, ll> pll;
23
24 const int maxk = 65;
25 const int maxa = 2e5 + 10;
26
27 bool p;
28 ll r[maxk], c[maxk];
29
30 ll get_r(ll T)
31 {
32 if(!T) return 1;
CAPITOLUL 1. IOI 2020 87

33 if(T == 1) return 1 + p;
34 int i = 63 - __builtin_clzll(T);
35 return c[i - 1] + min(r[i] + 1, get_r(T - (1LL << i)));
36 }
37
38 long long count_tastiness(long long x, vector<ll> s)
39 {
40 //int k = s.size();
41 s.resize(maxk);
42 for(int i = 1; i <= 60; ++i) s[i] = s[i - 1] + (1LL << i) * s[i];
43
44 p = (s[0] >= x);
45 r[0] = p - 1;
46 c[0] = p + 1;
47
48 for(int i = 1; i <= 60; ++i)
49 {
50 ll T = s[i] / x - (1LL << i);
51 if(T < 0) r[i] = -1;
52 else
53 {
54 if((1LL << i) - 1 <= T) r[i] = c[i - 1] - 1;
55 else r[i] = get_r(T) - 1;
56 }
57
58 c[i] = c[i - 1] + r[i] + 1;
59 }
60
61 return c[60];
62 }
63
64 // ---------------------------------------------------
65
66 int main()
67 {
68 std::clock_t c_start = std::clock();
69 auto t_start = std::chrono::high_resolution_clock::now();
70
71 auto t1 = clock();
72
73 std::freopen("../tests/5-11.in", "r", stdin) ;
74 std::freopen("biscuits.out", "w", stdout) ;
75
76 int q;
77 assert(scanf("%d", &q) == 1);
78 vector<int> k(q);
79 vector<long long> x(q);
80 vector<vector<long long>> a(q);
81 vector<long long> results(q);
82 for (int t = 0; t < q; t++)
83 {
84 assert(scanf("%d%lld", &k[t], &x[t]) == 2);
85 a[t] = vector<long long>(k[t]);
86 for (int i = 0; i < k[t]; i++)
87 {
88 assert(scanf("%lld", &a[t][i]) == 1);
89 }
90 }
91 fclose(stdin);
92
93 auto t2 = clock();
94
95 for (int t = 0; t < q; t++)
96 {
97 results[t] = count_tastiness(x[t], a[t]);
98 }
99
100 auto t3 = clock();
101
102 for (int t = 0; t < q; t++)
103 {
104 printf("%lld\n", results[t]);
105 }
106 fclose(stdout);
107
108 auto t4 = clock();
CAPITOLUL 1. IOI 2020 88

109
110 std::clock_t c_end = std::clock();
111 auto t_end = std::chrono::high_resolution_clock::now();
112
113 // reset console output
114 freopen("CON", "w", stdout);
115
116 //std::cout <<result<<’\n’<<’\n’;
117 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
118 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
119 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
120
121 std::cout << std::fixed << std::setprecision(2)
122 << "\nCPU time used: "
123 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
124 << "Wall clock time passed: "
125 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
126 << " ms\n";
127
128 return 0;
129 }
130 /*
131 t2-t1 = 0.116
132 t3-t2 = 0.016
133 t4-t3 = 0.015
134
135 CPU time used: 147.00 ms
136 Wall clock time passed: 147.20 ms
137
138 Process returned 0 (0x0) execution time : 0.201 s
139 Press any key to continue.
140 */

Listing 1.4.5: biscuits_306418.cpp


1 // https://oj.uz/problems/source/534
2 // https://oj.uz/submission/306418 18 ms 1024 KB
3
4 #include "biscuits.h"
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 typedef long long ll;
10
11 #define sz(x) (int)(x).size()
12
13 ll count_tastiness(ll x, vector<ll> a)
14 {
15 while (sz(a) < 60)
16 a.push_back(0);
17
18 vector<ll> dp(61,1), pref(61,0);
19
20 dp[0] = 1;
21 for (int i = 0; i < 60; i++)
22 {
23 pref[i] = (!i ? 0 : pref[i-1] / 2) + a[i];
24 ll sm = 0;
25 for (int j = i; j >= 0; j--)
26 {
27 if (pref[j] - sm >= x)
28 {
29 dp[i+1] += dp[j];
30 sm += x;
31 }
32
33 sm = 2 * (sm - a[j]);
34 sm = max(sm, 0ll);
35 }
36 }
37
38 return dp[60];
39 }
40
CAPITOLUL 1. IOI 2020 89

41 // ---------------------------------------------------
42
43 int main()
44 {
45 std::clock_t c_start = std::clock();
46 auto t_start = std::chrono::high_resolution_clock::now();
47
48 auto t1 = clock();
49
50 std::freopen("../tests/5-11.in", "r", stdin) ;
51 std::freopen("biscuits.out", "w", stdout) ;
52
53 int q;
54 assert(scanf("%d", &q) == 1);
55 vector<int> k(q);
56 vector<long long> x(q);
57 vector<vector<long long>> a(q);
58 vector<long long> results(q);
59 for (int t = 0; t < q; t++)
60 {
61 assert(scanf("%d%lld", &k[t], &x[t]) == 2);
62 a[t] = vector<long long>(k[t]);
63 for (int i = 0; i < k[t]; i++)
64 {
65 assert(scanf("%lld", &a[t][i]) == 1);
66 }
67 }
68 fclose(stdin);
69
70 auto t2 = clock();
71
72 for (int t = 0; t < q; t++)
73 {
74 results[t] = count_tastiness(x[t], a[t]);
75 }
76
77 auto t3 = clock();
78
79 for (int t = 0; t < q; t++)
80 {
81 printf("%lld\n", results[t]);
82 }
83 fclose(stdout);
84
85 auto t4 = clock();
86
87 std::clock_t c_end = std::clock();
88 auto t_end = std::chrono::high_resolution_clock::now();
89
90 // reset console output
91 freopen("CON", "w", stdout);
92
93 //std::cout <<result<<’\n’<<’\n’;
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 std::cout << std::fixed << std::setprecision(2)
99 << "\nCPU time used: "
100 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
101 << "Wall clock time passed: "
102 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
103 << " ms\n";
104
105 return 0;
106 }
107 /*
108 t2-t1 = 0.123
109 t3-t2 = 0.078
110 t4-t3 = 0.022
111
112 CPU time used: 223.00 ms
113 Wall clock time passed: 222.80 ms
114
115 Process returned 0 (0x0) execution time : 0.270 s
116 Press any key to continue.
CAPITOLUL 1. IOI 2020 90

117 */

Listing 1.4.6: biscuits_306482.cpp


1 // https://oj.uz/problems/source/534
2 // https://oj.uz/submission/306482 17 ms 896 KB
3
4 #include<bits/stdc++.h>
5
6 using namespace std;
7
8 const int c=60;
9 long long dp[c+1], b[c+1], h, o=0, sum;
10
11 long long count_tastiness(long long x, vector<long long> a)
12 {
13 while(a.size()<c) a.push_back(0);
14
15 b[0]=a[0], dp[0]=1;
16 for (int i=1; i<c; i++) b[i]=b[i-1]/2+a[i];
17
18 for (int i=1; i<=c; i++)
19 {
20 dp[i]=dp[i-1];
21 if (b[i-1]>=x)
22 {
23 h=max(o, x-a[i-1])*2, sum=0;
24 for (int j=i-2; j>=0; j--)
25 {
26 if (b[j]>=x+h) sum+=dp[j], h=max(o, h+x-a[j])*2;
27 else h=max(o, h-a[j])*2;
28 }
29
30 if (!h) sum++;
31 dp[i]+=sum;
32 }
33 }
34
35 return dp[c];
36 }
37
38 // ---------------------------------------------------
39
40 int main()
41 {
42 std::clock_t c_start = std::clock();
43 auto t_start = std::chrono::high_resolution_clock::now();
44
45 auto t1 = clock();
46
47 std::freopen("../tests/5-11.in", "r", stdin) ;
48 std::freopen("biscuits.out", "w", stdout) ;
49
50 int q;
51 assert(scanf("%d", &q) == 1);
52 vector<int> k(q);
53 vector<long long> x(q);
54 vector<vector<long long>> a(q);
55 vector<long long> results(q);
56 for (int t = 0; t < q; t++)
57 {
58 assert(scanf("%d%lld", &k[t], &x[t]) == 2);
59 a[t] = vector<long long>(k[t]);
60 for (int i = 0; i < k[t]; i++)
61 {
62 assert(scanf("%lld", &a[t][i]) == 1);
63 }
64 }
65 fclose(stdin);
66
67 auto t2 = clock();
68
69 for (int t = 0; t < q; t++)
70 {
71 results[t] = count_tastiness(x[t], a[t]);
CAPITOLUL 1. IOI 2020 91

72 }
73
74 auto t3 = clock();
75
76 for (int t = 0; t < q; t++)
77 {
78 printf("%lld\n", results[t]);
79 }
80 fclose(stdout);
81
82 auto t4 = clock();
83
84 std::clock_t c_end = std::clock();
85 auto t_end = std::chrono::high_resolution_clock::now();
86
87 // reset console output
88 freopen("CON", "w", stdout);
89
90 //std::cout <<result<<’\n’<<’\n’;
91 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
92 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
93 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
94
95 std::cout << std::fixed << std::setprecision(2)
96 << "\nCPU time used: "
97 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
98 << "Wall clock time passed: "
99 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
100 << " ms\n";
101
102 return 0;
103 }
104 /*
105 t2-t1 = 0.116
106 t3-t2 = 0.019
107 t4-t3 = 0.02
108
109 CPU time used: 155.00 ms
110 Wall clock time passed: 155.12 ms
111
112 Process returned 0 (0x0) execution time : 0.205 s
113 Press any key to continue.
114 */

1.4.3 *Rezolvare detaliat 

1.5 mushrooms
Problema 5 - Counting Mushrooms 100 de puncte
Author: Angus Ritossa
Expertul în ciuperci Andrew investigheaz  ciupercile native din Singapore.
Ca parte a cercet rii sale, Andrew culege n ciuperci marcate de la 0 la n  1. Fiecare ciuperc 
aparµine uneia din cele dou  specii numite A ³i B.
Andrew ³tie c  ciuperca 0 aparµine speciei A, dar, pentru c  cele dou  specii arat  la fel,
el nu cunoa³te speciile ciupercilor de la 1 la n  1.
Din fericire, Andrew are o ma³in  în laborator care îl poate ajuta cu asta. Pentru a folosi
aceast  ma³in , trebuie plasate dou  sau mai multe ciuperci în linie în interiorul ma³inii ³i apoi
pornit  ma³ina. Apoi, ma³ina calculeaz  num rul perechilor adiacente de ciuperci ce sunt din
specii diferite. De exemplu, dac  punem ciuperci din speciile [A,B,B,A] (în aceast  ordine) în
ma³in , rezultatul va  2.
Totu³i, pentru c  operarea ma³inii este foarte scump , ma³ina poate  folosit pentru un num r
limitat de teste. în plus, num rul total de ciuperci amplasate în ma³in  cumulat pentru toate
testele nu poate dep ³i 100 000. Folosiµi aceast  ma³in  pentru a-l ajuta pe Andrew s  numere
ciupercile adunate din specia A.
CAPITOLUL 1. IOI 2020 92

Detalii de implementare
Trebuie s  implementaµi urm toarea funcµie:

int count_mushrooms(int n)

ˆ n: num rul de ciuperci adunate de Andrew.


ˆ Aceast  funcµie este apelat  o singur  dat , ³i trebuie s  returneze num rul de ciuperci din
specia A.

Funcµia poate face apeluri c tre funcµia:

int use_machine(int[] x)

ˆ x: un vector cu lungimea între 2 ³i n inclusiv, reprezentând etichetele ciupercilor plasate în


ma³in , în ordine.
ˆ Elementele lui x trebuie s  e întregi distincµi de la 0 la n  1 inclusiv.
ˆ Aceast  funcµie poate  apelat  de cel mult 20 000 ori.
ˆ Lungimea total  a lui x pasat  c tre use_machine în toate apelurile nu poate dep ³i 100
000.

Exemple
Exemplul 1 Consider m un scenariu în care avem 3 ciuperci din speciile [A,B,B], în aceast 
ordine. Funcµia count_mushrooms este apelat  astfel:

count_mushrooms(3)

Aceast  funcµie poate apela use_machine([0, 1, 2]), care (în acest scenariu) returneaz  1.
Mai apoi, ar putea apela use_machine([2, 1]), care returneaz  0.
În acest moment, sunt suciente informaµii pentru a trage concluzia c  exist  o singur  ciuperc 
din specia A. Astfel, funcµia count_mushrooms ar trebui s  returneze 1.

Exemplul 2 Consider m cazul în care avem 4 ciuperci din speciile [A,B,A,A], în aceast  ordine.
Funcµia count_mushrooms este apelat  astfel:

count_mushrooms(4)

Funcµia poate apela use_machine([0, 2, 1, 3]), care returneaz  2. Poate apoi apela
use_machine([1, 2]), care returneaz  1.
În acest moment, sunt suciente informaµii pentru a trage concluzia c  exist  3 ciuperci din
specia A.
Astfel, funcµia count_mushrooms ar trebui s  returneze 3.

Restricµii
ˆ 2 & n & 20 000

Punctare
În toate testele, dac  apelurile funcµiei use_machine nu sunt conform regulilor de mai sus,
sau valoarea returnat  de count_mushrooms este incorect , scorul soluµiei va  0. Altfel, e Q
num rul maxim de apeluri al funcµiei use_machine. Atunci, scorul va  calculat dup  urm torul
tabel:
Condiµie Scor
20 000 $ Q 0
10 010 $ Q & 20 000 10
904 $ Q & 10 010 25
226 $ Q & 904 226
Q
100
Q & 226 100
CAPITOLUL 1. IOI 2020 93

Pentru unele cazuri comportamentul graderului este adaptiv. Asta înseamn  c  pentru aceste
teste graderul nu are o secvenµ  x  a speciilor de ciuperci. în schimb, r spunsul dat de grader
poate depinde de apelurile anterioare ale funcµiei use_machine.
Totu³i, se garanteaz  c  r spunsul graderului este astfel încât dup  ecare interacµiune exist 
cel puµin o secvenµ  de ciuperci consistent  cu toate r spunsurile date pân  acum.

Sample grader
Sample graderul cite³te un vector de întregi s reprezentând speciile de ciuperci. Pentru toate
valorile 0 & i & n  1, si 0 înseamn  c  specia ciupercii i este A, iar si 1 înseamn  c 
specia ciupercii i este B. Graderul cite³te în urm torul format:
ˆ line 1: n
ˆ line 2: s0 s1 ... sn  1

Ie³irea sample graderului este în urm torul format:


ˆ line 1: valoarea returnat  de count_mushrooms.
ˆ line 2: num rul de apeluri ale use_machine.

Sample graderul nu este adaptiv.

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2000 MB

1.5.1 Indicaµii de rezolvare

10 points
For each 1 & i & n  1, query use_machine([0,i]). We can thus determine the species of
every mushroom.

25 points
Same idea as above, except that we query use_machine([i, 0, i+1]) for all odd values of
i. As we only query odd values, the number of queries required is halved.

Beyond 25 points
The general strategy is as follows:
ˆ Collect a set of k mushrooms which are known to be of the same species, let these mushrooms
be x1 , x2 , ..., xk .
ˆ We can now make a query of the form y1 , x1 , y2 , x2 , ..., yk , xk . The return value to this
query reveals the number of type A mushrooms among y1 , y2 , ..., yk .

A possible strategy is to query 0, i for each 1 & i & 2 k  2, thus determining the species of
the rst 2k  1 mushrooms. We can then nd k mushrooms of the same type. The total number
of queries is given by 2k  2  * n  2k  1©k 0. Optimizing over possible values of k gives 397
queries, achieved when 94 & k & 107.
It is possible to further reduce the number of queries to k  * n  2k  1©k 0. We rst query
0, 1 and 0, 2. This gives us two types of mushrooms of the same species, call it x, y .

We can proceed by querying 3, x, 4, y . The return value of this function will identify the
species of both mushroom 3 and 4.
Using this approach, we can now nd the species of the rst 2k  2 mushrooms with only k
queries. By setting to any value between 137 and 146, we use at most 281 queries.
Finally, observe that when we query y1 , x1 , y2 , x2 , ...yk , xk , we gain information about the
species of y1 by looking at the parity of the return value.
Thus, we can repeatedly expand our set of known mushrooms and increase the value of k
appropriately, using at most 245 queries when 73 & k & 94.
CAPITOLUL 1. IOI 2020 94

100 points
Full solution identies about 5 mushrooms in 2 queries during the rst phase. There are con-
structive appoaches, but we can also try to brute-force all possible strategies that can achieve
this.
Suppose that we need to identify mushrooms A, B, C, D, E, and we have already identied x
and y mushrooms of types 0 and 1 respectively. We can try all possible strings of characters A,
B, C, D, E, 0 (x times), 1 (y times) to ask as a rst query q1 .
Depending on the answer to q1 , the second query q2 should be able to unambiguously determine
each type of A, B, C, D, E, so we can brute-force it as well (note that q2 may depend on the result
of q1 ).
The number of possible queries is small enough so that all decision trees can be processed
within reasonable time.
To avoid overhead for identifying the rst few mushrooms, we can actually have two decision
trees:
ˆ The rst tree uses the only additional mushroom of type 0, and will either gure out that
A, B, C, D, E are all type 0 in 1 query, or identify them in 3 queries.
ˆ The second tree uses, when available, 3 mushrooms of type 0 and 1 mushroom of type 1 (or
vice versa). It always identies A, B, C, D, E in 5 queries.

We will employ the second tree whenever there are enough extra mushrooms, and the rst
one otherwise (that only happens when all identied mushrooms are type 0). This approach can
unconditionally identify 5k mushrooms in 2k  1 queries. Combining this with ideas above and
choosing k optimally, we can solve the problem in 226 queries.
Note that there is a much simpler randomized solution that also identies  2.5 mushrooms
per query, but it can't be used because of adaptive grading (unless you can somehow confuse the
grader ...).

Beyond 100 points


Since the end of the contest a much better solution has been discovered by the community:
https://codeforces.com/blog/entry/82924

Solving IOI2020 D2P2 (Mushroom) within 203 Queries (Step by Step)


Zhenting Zhu, Wuhu, China
From Tsinghua University

Short statement: there are N & 20 000 objects with value 0 or 1. Each time you can select
some objects and permute them to form a 0-1 sequence, then the interactor will tell you the total
number of 0-1 and 1-0 shifts in the sequence (i.e. 011000 has 2 shifts and 000101 has 3 shifts).
It is guaranteed that the value of the 1st object is 0. You are required to nd out the number of
objects with value 0 within 226 queries (100%) / 904 queries (>25%), and the total length of the
5
sequence in the queries is bounded by 10 .
When encountering the problem, a linear algorithm comes directly. We can check whether
there is a shift in value sequence 0, x . If so x 1, otherwise x 0. Hence we can solve the
problem within N queries, getting 10% as a result.
We then can come up to do some tiny optimizations, without changing the linearity of the
algorithm. After two queries, we will have at least two 0 s or 1 s. Then we can set the value
sequence to be x, 0, y, 0 or x, 1, y, 1, then the value of x and y can be determined simultaneously.
This lead to an approach with N2  1 queries which gets 25%.
For higher points, we need a sublinear algorithm. As we only need the sum of the values of
objects, we don't have to determine the value of each object. One observation is that we can
take advantage of the objects we have determined, and infer the sum of values by asking for
a, X, b, X, c, X, d, X, ..., X where X 0 or 1 depending on which kind of object we have more (so
that the sequence can contain more unseen objects for counting).
CAPITOLUL 1. IOI 2020 95

When X 0 as an example, the returned number by the interactor is just a  2b  2c  2d  ....


Notably, the value of a can be determined by the last digit of the result, since each object will
be counted twice except a. Hence we can get more objects with determining value (which can
be used as X to extend the length of our query) when we count the objects. Directly apply this
<
approach enables us to solve for N & i 1 *i©20 within m queries, which gets 80%.
m1

However, one should notice that putting more than one value between X or replacing X with
both 1 and 0 seems useless. The former is just why we need pattern like a, X, b, X, ..., and the
latter is because of, if we get variable p  q , we are required to perform another query to solve p
and q , then why not ask p and q individually. This may make us confused about how to optimize
the algorithm.
In fact, there is still an easy optimization of the current algorithm. We can divide the program
into two phases: in the rst phase, we use patterns like x, 0, y, 0, and determine two variables at
a time; in the second phase, we use the determined variables to do queries. BalancingÓ
the two
phases (seems 3:4 in number of queries) gives 244 queries as a result (about 1.75 n according to
calculation), which gets 92.62%.
We will reformulate the statement (which is stronger) according to our discussion above. We
can hold a value k on our hand, initially k 1. Then in each step, you can ask for the sum of no
more than *k ©20 objects. Once you know the value of some specic object, we can increase k by
one; and after each step, k will automatically increase one. Our target is to determine the sum
of all objects. Then our two-phase algorithm is just to ask one object in the rst phase, and as
many objects as possible in the second phase.
Intuitively, the bottleneck of our algorithm seems to be that only ask for one object at a time,
which only utilizes 1 bits of information. This still seems hard to optimize, but if you once met
another problem before, it will be easier for you to solve this problem. The target of that problem
is to recover a 0-1 sequence of length n withinO n© log n steps by asking the sum of some elements
(for example, n 1000 and solve it in 300 steps).
The solution of that problem is a divide-and-conquer like approach. Let fm be the longest
m
sequence we can recover within 2 steps, and qm be the query sequence of it, with the last step
asking for the sum of the whole sequence, then we can have fm1 ' 2fm  2  1. This is done
m
m
by dividing the sequence into two blocks of size fm and one block of size 2  1. Then we rst
ask for the total number of ones c in the second block of size fm . For each non-last query qm,i
in qm , if we apply it on the two fm blocks, we will get a and b respectively. We construct two
queries, asking for a  b and a  c  b  i, where i is a bit in the last block, then clearly we can
recover a, b, i through the query. The last query is used to obtain the total number of ones in the
sequence. We recursively apply this approach, and nally, we are done with O n© log n steps.
m
Thus in the rst phase of our algorithm, we can try to use a bunch of 2 queries to obtain fm
determined objects if we have enough determined objects for calculating the sum. We can sort
the required bits in qm and get the required initial bits, then do a DP on the number of queries
(we take the addition on k in each step into consideration). Finally, we enumerate a position to
begin the second phase. This yields 209 queries in total, which is much smaller than the bound
given in the problem. A tiny optimization reducing the number of ones required in the last query
of qm further enables us to solve the problem in 203 queries.
The implementation of this approach can be found here:
https://loj.ac/submission/938329

1.5.2 Coduri surs 

Listing 1.5.1: mushrooms_Model.cpp


1 /* Q <= 226 ==> 100 pct
2 * Model solution for task mushrooms
3 *
4 * Author: Mikhail Tikhomirov
5 */
6
7 #include "mushrooms.h"
CAPITOLUL 1. IOI 2020 96

8 #include <vector>
9 #include <string>
10 #include <map>
11 #include <cassert>
12
13 // -------------------------------------------------------------
14
15 #include <cstdio>
16 #include <cstdlib>
17 #include <cstdarg>
18
19 #include <iostream> /* cout */
20 #include <iomanip> /* setprecision */
21 #include <chrono> /* chrono */
22
23 // -------------------------------------------------------------
24
25 #define mp make_pair
26 #define mt make_tuple
27 #define fi first
28 #define se second
29 #define pb push_back
30 #define all(x) (x).begin(), (x).end()
31 #define rall(x) (x).rbegin(), (x).rend()
32 #define forn(i, n) for (int i = 0; i < (int)(n); ++i)
33 #define for1(i, n) for (int i = 1; i <= (int)(n); ++i)
34 #define ford(i, n) for (int i = (int)(n) - 1; i >= 0; --i)
35 #define fore(i, a, b) for (int i = (int)(a); i <= (int)(b); ++i)
36
37 using namespace std;
38
39 typedef pair<int, int> pii;
40 typedef vector<int> vi;
41 typedef vector<pii> vpi;
42 typedef vector<vi> vvi;
43 typedef long long i64;
44 typedef vector<i64> vi64;
45 typedef vector<vi64> vvi64;
46 typedef pair<i64, i64> pi64;
47 typedef double ld;
48
49 template<class T> bool uin(T &a, T b) {return a > b ? (a = b, true) : false;}
50 template<class T> bool uax(T &a, T b) {return a < b ? (a = b, true) : false;}
51
52 vi labels;
53 vvi lst;
54 int K = 110;
55
56 int ans;
57
58 void label(int p, int z)
59 {
60 // cerr << p << ’ ’ << z << ’\n’;
61 labels[p] = z;
62 lst[z].pb(p);
63 if (!z) ++ans;
64 }
65
66 int p;
67 int z;
68 vi toQ;
69
70 string subs(string s, int mask)
71 {
72 for (char &c: s)
73 if (c >= ’A’ && c <= ’Z’)
74 c = (char)(’0’ + ((mask >> (c - ’A’)) & 1));
75 return s;
76 }
77
78 int eval(const string &s)
79 {
80 int ret = 0;
81 forn(i, (int)s.size() - 1) ret += int(s[i] != s[i + 1]);
82 return ret;
83 }
CAPITOLUL 1. IOI 2020 97

84
85 int query(string s)
86 {
87 vi v;
88 vi ptr(2);
89 for (char c: s)
90 {
91 if (c == ’0’ || c == ’1’)
92 {
93 int d = (c - ’0’) ^ z;
94 assert(ptr[d] < (int)lst[d].size());
95 v.pb(lst[d][ptr[d]++]);
96 }
97 else
98 {
99 int l = c - ’A’;
100 v.pb(toQ[l]);
101 }
102 }
103
104 return use_machine(v);
105 }
106
107 map<vi, string> prec1 =
108 {
109 {{}, "A0B0C0DE"},
110 {{1}, "CED"},
111 {{2}, "DBE0A"},
112 {{3}, "CB0E0DA1"},
113 {{4}, "D0A0E0CB1"},
114 {{5}, "DBCE1"},
115 {{6}, "CED"}
116 };
117
118 map<vi, string> prec2 =
119 {
120 {{}, "ABCDE0"},
121 {{1}, "B0"},
122 {{1, 1}, "EADC"},
123 {{2}, "CB0D"},
124 {{2, 1}, "EADB"},
125 {{2, 2}, "DAE0CB"},
126 {{3}, "CB0D"},
127 {{3, 1}, "EDB0"},
128 {{3, 2}, "AED"},
129 {{3, 3}, "ED"},
130 {{4}, "B0"},
131 {{4, 1}, "0EACD"}
132 };
133
134
135 void magic(map<vi, string> &m)
136 {
137 toQ.clear();
138 forn(i, 5) toQ.pb(p + i);
139 vi seq;
140 map<string, int> res;
141 while (m.count(seq))
142 {
143 string s = m[seq];
144 int x = query(s);
145 res[s] = x;
146 seq.pb(x);
147 }
148
149 vi goodM;
150 forn(mask, 32)
151 {
152 bool ok = true;
153 for (auto w: res) ok &= eval(subs(w.fi, mask)) == w.se;
154 if (ok) goodM.pb(mask);
155 }
156
157 assert(goodM.size() == 1);
158 int mask = goodM[0];
159 forn(i, 5) label(p++, ((mask >> i) & 1) ^ z);
CAPITOLUL 1. IOI 2020 98

160 }
161
162 int count_mushrooms(int n)
163 {
164 labels = vi(n, -1);
165 lst = vvi(2);
166 p = 1;
167 ans = 0;
168 label(0, 0);
169 while (p < n)
170 {
171 z = lst[0].size() > lst[1].size() ? 0 : 1;
172 if (n - p < 5 || (int)lst[z].size() >= K)
173 {
174 vi m;
175 int i = 0;
176 while (p < n && i < (int)lst[z].size())
177 {
178 m.pb(p++);
179 m.pb(lst[z][i++]);
180 }
181 int x = use_machine(m);
182 lst[z ^ (x & 1)].pb(m[0]);
183 x = (x + 1) / 2;
184 ans += (z ? x : i - x);
185 }
186 else
187 magic(!lst[z ^ 1].empty() && lst[z].size() >= 3 ? prec1 : prec2);
188 }
189
190 return ans;
191 }
192
193 // ----------------------------------------------------------
194
195 static char fmt_buffer[100000];
196 #define FMT_TO_STR(fmt, result) va_list vargs; va_start(vargs, fmt); \
197 vsnprintf(fmt_buffer, sizeof(fmt_buffer), fmt, vargs); \
198 va_end(vargs); fmt_buffer[sizeof(fmt_buffer)-1] = 0; \
199 std::string result(fmt_buffer);
200
201 static const int min_n = 2;
202 static const int max_n = 20000;
203 static const int max_qc = 20000;
204 static const int max_qs = 100000;
205 static const int species_A = 0;
206 static const int species_B = 1;
207
208 static int n;
209 static std::vector<int> species;
210 static int qc, qs;
211
212 static inline void error_if(bool cond, std::string message)
213 {
214 if (cond)
215 {
216 printf("%s\n", message.c_str());
217 exit(0);
218 }
219 }
220
221 static inline void wrong_if(bool cond, std::string message)
222 {
223 error_if(cond, "Wrong Answer: "+message);
224 }
225
226 int use_machine(std::vector<int> x)
227 {
228 const int xs = x.size();
229 wrong_if(xs < 2, "Too small array for query.");
230 wrong_if(xs > n, "Too large array for query.");
231 qc++;
232 wrong_if(qc > max_qc, "Too many queries.");
233 qs += xs;
234 wrong_if(qs > max_qs, "Too many total array sizes as queries.");
235 for (int i = 0; i < xs; i++)
CAPITOLUL 1. IOI 2020 99

236 wrong_if(x[i] < 0 || n-1 < x[i], "Invalid value in the query array.");
237
238 std::vector<bool> used(n, false);
239 for (int i = 0; i < xs; i++)
240 {
241 wrong_if(used[x[i]], "Duplicate value in the query array.");
242 used[x[i]] = true;
243 }
244
245 int diffs = 0;
246 for (int i = 1; i < xs; i++)
247 diffs += int(species[x[i]] != species[x[i-1]]);
248 return diffs;
249 }
250
251 #ifdef __GNUC__
252 __attribute__ ((format(printf, 2, 3)))
253 #endif
254
255 static inline void check_input(bool cond, const char* message_fmt, ...)
256 {
257 FMT_TO_STR(message_fmt, message);
258 error_if(!cond, "Invalid input: "+message);
259 }
260
261 int main()
262 {
263 std::clock_t c_start = std::clock();
264 auto t_start = std::chrono::high_resolution_clock::now();
265
266 auto t1 = clock();
267
268 //std::freopen("../tests/n5-16.in", "r", stdin);
269 std::freopen("../tests/1-09.in", "r", stdin);// 1-05 ... 1-11
270 //std::freopen("mushrooms.out", "w", stdout);
271
272 string strategy;
273 std::getline (std::cin,strategy);
274 //cout<<"strategy = "<<strategy<<"\n";
275
276 check_input(1 == scanf("%d", &n),
277 "Could not read n.");
278 check_input(min_n <= n,
279 "n must not be less than %d, but it is %d.", min_n, n);
280 check_input(n <= max_n,
281 "n must not be greater than %d, but it is %d.", max_n, n);
282
283 species.resize(n);
284 for (int i = 0; i < n; i++)
285 {
286 check_input(1 == scanf("%d",
287 &species[i]),
288 "Could not read species element [%d].", i);
289 check_input(species[i]==species_A || species[i] == species_B,
290 "Species elements must be %d or %d, \
291 but species element [%d] is %d.",
292 species_A, species_B, i, species[i]);
293 }
294
295 check_input(species[0] == species_A,
296 "Species element [%d] must be %d.", 0, species_A);
297 // Postponed closing standard input in order to allow \
298 interactive usage of the grader.
299
300 auto t2 = clock();
301
302 qc = 0;
303 qs = 0;
304 int answer = count_mushrooms(n);
305
306 auto t3 = clock();
307
308 printf("%d\n%d\n", answer, qc);
309
310 fclose(stdout);
311 fclose(stdin);
CAPITOLUL 1. IOI 2020 100

312
313 auto t4 = clock();
314
315 std::clock_t c_end = std::clock();
316 auto t_end = std::chrono::high_resolution_clock::now();
317
318 // reset console output
319 freopen("CON", "w", stdout);
320
321 std::cout<<"answer = "<<answer<<"\n";
322 std::cout<<"qc = "<<qc<<"\n\n";
323
324 //std::cout <<result<<’\n’<<’\n’;
325 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
326 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
327 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
328
329 std::cout << std::fixed << std::setprecision(2)
330 << "\nCPU time used: "
331 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
332 << "Wall clock time passed: "
333 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
334 << " ms\n";
335
336 return 0;
337 }
338 /*
339 answer = 10124
340 qc = 216
341
342 t2-t1 = 0.085
343 t3-t2 = 0.074
344 t4-t3 = 0.002
345
346 CPU time used: 161.00 ms
347 Wall clock time passed: 160.62 ms
348
349 Process returned 0 (0x0) execution time : 0.200 s
350 Press any key to continue..
351 */

Listing 1.5.2: mushrooms_303767.cpp


1 // https://oj.uz/submission/303767 Q <= 226 ==> 100 pct
2
3 #include "mushrooms.h"
4
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 // -------------------------------------------------------------
10
11 static int ask(string s, vector<int> A,
12 const vector<int> &z, const vector<int> &o)
13 {
14 vector<int> q;
15 int iz = 0, io = 0;
16 for (char c : s)
17 {
18 if (’0’ <= c && c <= ’9’)
19 {
20 q.push_back(A[c - ’0’]);
21 }
22 else if (c == ’A’)
23 {
24 q.push_back(z[iz++]);
25 }
26 else
27 {
28 q.push_back(o[io++]);
29 }
30 }
31
32 return use_machine(q);
CAPITOLUL 1. IOI 2020 101

33 }
34
35 int calc(string s, int x)
36 {
37 for (int i = 0; i < int(s.length()); ++i)
38 {
39 if (’0’ <= s[i] && s[i] <= ’9’)
40 {
41 s[i] = ’A’ + ((x >> s[i] - ’0’) & 1);
42 }
43 }
44
45 int ret = 0;
46 for (int i = 1; i < int(s.length()); ++i)
47 {
48 ret += s[i - 1] != s[i];
49 }
50
51 return ret;
52 }
53
54 int count_mushrooms(int n)
55 {
56 const string start = "0A1A2A3A4";
57 string qu[9];
58 qu[0] = "01234A"; // (1, 0)
59 qu[1] = "01234A"; // (1, 0)
60 qu[2] = "012B3B4A"; // (1, 2)
61 qu[3] = "0124AAB3B"; // (2, 2)
62 qu[4] = "014A23BA"; // (2, 1)
63 qu[5] = "0124A3A"; // (2, 0)
64 qu[6] = "012A3A4A"; // (3, 0)
65 qu[7] = "01234A"; // (1, 0)
66 qu[8] = "01234A"; // (1, 0)
67
68 const int B = 100;
69 vector<int> A[2];
70 A[0].push_back(0);
71 int i = 1;
72 for (; i < n && int(max(A[0].size(), A[1].size())) < 2; ++i)
73 {
74 A[use_machine({ 0, i })].push_back(i);
75 }
76
77 for (; i + 1 < n && int(max(A[0].size(), A[1].size())) < B; )
78 {
79 if (i + 4 < n &&
80 int(max(A[0].size(), A[1].size())) >= 4 &&
81 int(min(A[0].size(), A[1].size())) >= 2)
82 {
83 vector<int> X;
84 while (int(X.size()) < 5) X.push_back(i++);
85
86 int b = A[0].size() > A[1].size() ? 0 : 1;
87 int ret1 = ask(start, X, A[b], A[1 - b]);
88 int ret2 = ask(qu[ret1], X, A[b], A[1 - b]);
89 int bit;
90
91 for (bit = 0;
92 calc(start, bit) != ret1 || calc(qu[ret1], bit) != ret2;
93 ++bit);
94
95 if (b) bit = 31 - bit;
96 for (int i = 0; i < 5; ++i)
97 {
98 A[(bit >> i) & 1].push_back(X[i]);
99 }
100 }
101 else if (int(A[0].size()) >= 2)
102 {
103 int ret = use_machine({ A[0][0], i, A[0][1], i + 1 });
104 A[ret % 2].push_back(i + 1);
105 A[ret / 2].push_back(i);
106 i += 2;
107 }
108 else
CAPITOLUL 1. IOI 2020 102

109 {
110 int ret = use_machine({ A[1][0], i, A[1][1], i + 1 });
111 A[1 - ret % 2].push_back(i + 1);
112 A[1 - ret / 2].push_back(i);
113 i += 2;
114 }
115 }
116
117 int ret = A[0].size();
118 for (; i < n; )
119 {
120 if (A[0].size() > A[1].size())
121 {
122 vector<int> q1;
123 for (; i < n && q1.size() < A[0].size(); ++i)
124 q1.push_back(i);
125
126 vector<int> q;
127 for (int i = 0; i < int(q1.size()); ++i)
128 {
129 q.push_back(A[0][i]);
130 q.push_back(q1[i]);
131 }
132
133 int u = use_machine(q);
134 A[u % 2].push_back(q1.back());
135 ret += int(q1.size()) - (u + 1) / 2;
136 }
137 else
138 {
139 vector<int> q1;
140 for (; i < n && q1.size() < A[1].size(); ++i)
141 q1.push_back(i);
142
143 vector<int> q;
144 for (int i = 0; i < int(q1.size()); ++i)
145 {
146 q.push_back(A[1][i]);
147 q.push_back(q1[i]);
148 }
149
150 int u = use_machine(q);
151 A[1 - u % 2].push_back(q1.back());
152 ret += (u + 1) / 2;
153 }
154 }
155
156 return ret;
157 }
158
159 // ----------------------------------------------------------
160
161 static char fmt_buffer[100000];
162 #define FMT_TO_STR(fmt, result) va_list vargs; va_start(vargs, fmt); \
163 vsnprintf(fmt_buffer, sizeof(fmt_buffer), fmt, vargs); \
164 va_end(vargs); fmt_buffer[sizeof(fmt_buffer)-1] = 0; \
165 std::string result(fmt_buffer);
166
167 static const int min_n = 2;
168 static const int max_n = 20000;
169 static const int max_qc = 20000;
170 static const int max_qs = 100000;
171 static const int species_A = 0;
172 static const int species_B = 1;
173
174 static int n;
175 static std::vector<int> species;
176 static int qc, qs;
177
178 static inline void error_if(bool cond, std::string message)
179 {
180 if (cond)
181 {
182 printf("%s\n", message.c_str());
183 exit(0);
184 }
CAPITOLUL 1. IOI 2020 103

185 }
186
187 static inline void wrong_if(bool cond, std::string message)
188 {
189 error_if(cond, "Wrong Answer: "+message);
190 }
191
192 int use_machine(std::vector<int> x)
193 {
194 const int xs = x.size();
195 wrong_if(xs < 2, "Too small array for query.");
196 wrong_if(xs > n, "Too large array for query.");
197 qc++;
198 wrong_if(qc > max_qc, "Too many queries.");
199 qs += xs;
200 wrong_if(qs > max_qs, "Too many total array sizes as queries.");
201 for (int i = 0; i < xs; i++)
202 wrong_if(x[i] < 0 || n-1 < x[i], "Invalid value in the query array.");
203
204 std::vector<bool> used(n, false);
205 for (int i = 0; i < xs; i++)
206 {
207 wrong_if(used[x[i]], "Duplicate value in the query array.");
208 used[x[i]] = true;
209 }
210
211 int diffs = 0;
212 for (int i = 1; i < xs; i++)
213 diffs += int(species[x[i]] != species[x[i-1]]);
214 return diffs;
215 }
216
217 #ifdef __GNUC__
218 __attribute__ ((format(printf, 2, 3)))
219 #endif
220
221 static inline void check_input(bool cond, const char* message_fmt, ...)
222 {
223 FMT_TO_STR(message_fmt, message);
224 error_if(!cond, "Invalid input: "+message);
225 }
226
227 int main()
228 {
229 std::clock_t c_start = std::clock();
230 auto t_start = std::chrono::high_resolution_clock::now();
231
232 auto t1 = clock();
233
234 //std::freopen("../tests/n5-16.in", "r", stdin);
235 std::freopen("../tests/1-09.in", "r", stdin);// 1-05 ... 1-11
236 //std::freopen("mushrooms.out", "w", stdout);
237
238 string strategy;
239 std::getline (std::cin,strategy);
240 //cout<<"strategy = "<<strategy<<"\n";
241
242 check_input(1 == scanf("%d", &n),
243 "Could not read n.");
244 check_input(min_n <= n,
245 "n must not be less than %d, but it is %d.", min_n, n);
246 check_input(n <= max_n,
247 "n must not be greater than %d, but it is %d.", max_n, n);
248
249 species.resize(n);
250 for (int i = 0; i < n; i++)
251 {
252 check_input(1 == scanf("%d",
253 &species[i]),
254 "Could not read species element [%d].", i);
255 check_input(species[i]==species_A || species[i] == species_B,
256 "Species elements must be %d or %d, \
257 but species element [%d] is %d.",
258 species_A, species_B, i, species[i]);
259 }
260
CAPITOLUL 1. IOI 2020 104

261 check_input(species[0] == species_A,


262 "Species element [%d] must be %d.", 0, species_A);
263 // Postponed closing standard input in order to allow \
264 interactive usage of the grader.
265
266 auto t2 = clock();
267
268 qc = 0;
269 qs = 0;
270 int answer = count_mushrooms(n);
271
272 auto t3 = clock();
273
274 printf("%d\n%d\n", answer, qc);
275
276 fclose(stdout);
277 fclose(stdin);
278
279 auto t4 = clock();
280
281
282 std::clock_t c_end = std::clock();
283 auto t_end = std::chrono::high_resolution_clock::now();
284
285 // reset console output
286 freopen("CON", "w", stdout);
287
288 std::cout<<"answer = "<<answer<<"\n";
289 std::cout<<"qc = "<<qc<<"\n\n";
290
291 //std::cout <<result<<’\n’<<’\n’;
292 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
293 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
294 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
295
296 std::cout << std::fixed << std::setprecision(2)
297 << "\nCPU time used: "
298 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
299 << "Wall clock time passed: "
300 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
301 << " ms\n";
302
303 return 0;
304 }
305 /*
306 answer = 10124
307 qc = 221
308
309 t2-t1 = 0.1
310 t3-t2 = 0.066
311 t4-t3 = 0
312
313 CPU time used: 166.00 ms
314 Wall clock time passed: 166.12 ms
315
316 Process returned 0 (0x0) execution time : 0.190 s
317 Press any key to continue.
318 */

Listing 1.5.3: mushrooms_306077.cpp


1 //https://oj.uz/submission/306077 Q <= 226 ==> 100 pct
2
3 #include <bits/stdc++.h>
4
5 #include "mushrooms.h"
6
7 using namespace std;
8
9 // -------------------------------------------------------------
10
11 #define debug(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
12
13 map <vector<int>, string> rule3_1_2 =
14 {
CAPITOLUL 1. IOI 2020 105

15 {{}, "A0B0C0D1E"},
16 {{1}, "0D"},
17 {{2}, "0A0D0E"},
18 {{3}, "0A1BC0E0D"},
19 {{4}, "0A0E0BC1D"},
20 {{5}, "0A0BC1E0D"},
21 {{6}, "0A0D0E"},
22 {{7}, "0D"}
23 };
24
25 map <vector<int>, string> rule1_0_3 =
26 {
27 {{}, "0ABCDE"},
28 {{1}, "0ABCD"},
29 {{1, 1}, "AB0C"},
30 {{2}, "A0BCD"},
31 {{2, 1}, "AB0CE"},
32 {{2, 2}, "ABDC"},
33 {{2, 3}, "0C"},
34 {{3}, "A0BCE"},
35 {{3, 1}, "0B"},
36 {{3, 2}, "0BDC"},
37 {{3, 3}, "0D"},
38 {{3, 4}, "0D"},
39 {{4}, "A0BCD"},
40 {{4, 2}, "0C"},
41 {{4, 3}, "0ABCD"}
42 };
43
44 int eval(const string &choice, int msk, bool base)
45 {
46 int n = choice.length();
47 vector <bool> arr(n);
48 for (int i=0;i<n;i++)
49 {
50 if (choice[i] == ’0’) arr[i] = base;
51 else if (choice[i] == ’1’) arr[i] = base^1;
52 else arr[i] = msk>>(choice[i]-’A’)&1;
53 }
54
55 int ret = 0;
56 for (int i=1;i<n;i++) if (arr[i-1] != arr[i]) ret++;
57 return ret;
58 }
59
60 int count_mushrooms(int N)
61 {
62 int K = 97;
63 vector<int> arr[2] = {{0}, {}};
64 int pt = 1;
65
66 // Step 1: Check type of mushroom based on rule
67 while (N-pt >= 5 && max(arr[0].size(), arr[1].size()) < K)
68 {
69 int t = arr[1].size() > arr[0].size();
70 auto &rule = (
71 arr[t].size() >= 3 && arr[t^1].size() >= 1 ?
72 rule3_1_2 :
73 rule1_0_3
74 );
75
76 vector<int> path;
77 vector<bool> valid(32, 1);
78 while (rule.count(path))
79 {
80 string command = rule[path];
81 vector<int> test, p(2);
82 for (char c: command)
83 {
84 if (c == ’0’ || c == ’1’) test.push_back(arr[(c-’0’)^t][p[(c-’0’)^t]++])
;
85 else test.push_back(pt+(c-’A’));
86 }
87
88 int res = use_machine(test);
89 for (int i=0;i<32;i++) if (valid[i])
CAPITOLUL 1. IOI 2020 106

90 {
91 if (eval(command, i, t) != res) valid[i] = 0;
92 }
93
94 path.push_back(res);
95 }
96
97 int cnt = 0, msk;
98 for (int i=0;i<32;i++)
99 if (valid[i])
100 cnt++, msk = i;
101
102 assert(cnt == 1);
103
104 for (int i=0;i<5;i++)
105 arr[msk>>i&1].push_back(pt+i);
106
107 pt += 5;
108 }
109
110 // Step 2: Count number of type 0 mushrooms many by one
111 int zero_count = arr[0].size();
112 while (pt < N)
113 {
114 int t = arr[1].size() > arr[0].size();
115
116 vector<int> test;
117 for (int i=0;i<arr[t].size()&&pt+i<N;i++)
118 {
119 test.push_back(arr[t][i]);
120 test.push_back(pt+i);
121 }
122
123 int res = use_machine(test);
124 int cnt = res+1>>1;
125 if (!t) cnt = test.size()/2-cnt;
126 zero_count += cnt;
127 arr[res&1^t].push_back(test.back());
128 pt += test.size()/2;
129 }
130
131 return zero_count;
132 }
133
134 // ----------------------------------------------------------
135
136 static char fmt_buffer[100000];
137 #define FMT_TO_STR(fmt, result) va_list vargs; va_start(vargs, fmt); \
138 vsnprintf(fmt_buffer, sizeof(fmt_buffer), fmt, vargs); \
139 va_end(vargs); fmt_buffer[sizeof(fmt_buffer)-1] = 0; \
140 std::string result(fmt_buffer);
141
142 static const int min_n = 2;
143 static const int max_n = 20000;
144 static const int max_qc = 20000;
145 static const int max_qs = 100000;
146 static const int species_A = 0;
147 static const int species_B = 1;
148
149 static int n;
150 static std::vector<int> species;
151 static int qc, qs;
152
153 static inline void error_if(bool cond, std::string message)
154 {
155 if (cond)
156 {
157 printf("%s\n", message.c_str());
158 exit(0);
159 }
160 }
161
162 static inline void wrong_if(bool cond, std::string message)
163 {
164 error_if(cond, "Wrong Answer: "+message);
165 }
CAPITOLUL 1. IOI 2020 107

166
167 int use_machine(std::vector<int> x)
168 {
169 const int xs = x.size();
170 wrong_if(xs < 2, "Too small array for query.");
171 wrong_if(xs > n, "Too large array for query.");
172 qc++;
173 wrong_if(qc > max_qc, "Too many queries.");
174 qs += xs;
175 wrong_if(qs > max_qs, "Too many total array sizes as queries.");
176 for (int i = 0; i < xs; i++)
177 wrong_if(x[i] < 0 || n-1 < x[i], "Invalid value in the query array.");
178
179 std::vector<bool> used(n, false);
180 for (int i = 0; i < xs; i++)
181 {
182 wrong_if(used[x[i]], "Duplicate value in the query array.");
183 used[x[i]] = true;
184 }
185
186 int diffs = 0;
187 for (int i = 1; i < xs; i++)
188 diffs += int(species[x[i]] != species[x[i-1]]);
189 return diffs;
190 }
191
192 #ifdef __GNUC__
193 __attribute__ ((format(printf, 2, 3)))
194 #endif
195
196 static inline void check_input(bool cond, const char* message_fmt, ...)
197 {
198 FMT_TO_STR(message_fmt, message);
199 error_if(!cond, "Invalid input: "+message);
200 }
201
202 int main()
203 {
204 std::clock_t c_start = std::clock();
205 auto t_start = std::chrono::high_resolution_clock::now();
206
207 auto t1 = clock();
208
209 //std::freopen("../tests/n5-16.in", "r", stdin);
210 std::freopen("../tests/1-09.in", "r", stdin);// 1-05 ... 1-11
211 //std::freopen("mushrooms.out", "w", stdout);
212
213 string strategy;
214 std::getline (std::cin,strategy);
215 //cout<<"strategy = "<<strategy<<"\n";
216
217 check_input(1 == scanf("%d", &n),
218 "Could not read n.");
219 check_input(min_n <= n,
220 "n must not be less than %d, but it is %d.", min_n, n);
221 check_input(n <= max_n,
222 "n must not be greater than %d, but it is %d.", max_n, n);
223
224 species.resize(n);
225 for (int i = 0; i < n; i++)
226 {
227 check_input(1 == scanf("%d",
228 &species[i]),
229 "Could not read species element [%d].", i);
230 check_input(species[i]==species_A || species[i] == species_B,
231 "Species elements must be %d or %d, \
232 but species element [%d] is %d.",
233 species_A, species_B, i, species[i]);
234 }
235
236 check_input(species[0] == species_A,
237 "Species element [%d] must be %d.", 0, species_A);
238 // Postponed closing standard input in order to allow \
239 interactive usage of the grader.
240
241 auto t2 = clock();
CAPITOLUL 1. IOI 2020 108

242
243 qc = 0;
244 qs = 0;
245 int answer = count_mushrooms(n);
246
247 auto t3 = clock();
248
249 printf("%d\n%d\n", answer, qc);
250
251 fclose(stdout);
252 fclose(stdin);
253
254 auto t4 = clock();
255
256 std::clock_t c_end = std::clock();
257 auto t_end = std::chrono::high_resolution_clock::now();
258
259 // reset console output
260 freopen("CON", "w", stdout);
261
262 std::cout<<"answer = "<<answer<<"\n";
263 std::cout<<"qc = "<<qc<<"\n\n";
264
265 //std::cout <<result<<’\n’<<’\n’;
266 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
267 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
268 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
269
270 std::cout << std::fixed << std::setprecision(2)
271 << "\nCPU time used: "
272 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
273 << "Wall clock time passed: "
274 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
275 << " ms\n";
276
277 return 0;
278 }
279
280 /*
281 answer = 10124
282 qc = 222
283
284 t2-t1 = 0.1
285 t3-t2 = 0.07
286 t4-t3 = 0
287
288 CPU time used: 170.00 ms
289 Wall clock time passed: 170.71 ms
290
291 Process returned 0 (0x0) execution time : 0.210 s
292 Press any key to continue.
293 */

Listing 1.5.4: mushrooms_938329.cpp


1 // https://codeforces.com/blog/entry/82924
2 // https://loj.ac/submission/938329
3
4 #include <bits/stdc++.h>
5 #include "mushrooms.h"
6
7 #define pb emplace_back
8 #define fi first
9 #define se second
10
11 using namespace std;
12
13 const int maxL = 7, maxN = 1000;
14
15 struct data_structure
16 {
17 int len, pos;
18 vector<bitset<maxN>> seq;
19 } st[maxL + 1];
20
CAPITOLUL 1. IOI 2020 109

21 vector<int> solve(int k, vector<int> res)


22 {
23 if (k == 0)
24 return res;
25
26 vector<int> resL, resR;
27 vector<int> retL, retR, retM;
28 int m = res.size(), sum = res[m - 2], sumM = 0;
29
30 for (int i = 0; i + 2 < (int)res.size(); i += 2)
31 {
32 int u = res[i] - sum, v = res[i + 1];
33 resL.pb((u + v) / 2), resR.pb((v - u) / 2);
34 retM.pb((u + v) % 2), sumM += retM.back();
35 }
36
37 resL.pb(res[m - 1] - res[m - 2] - sumM);
38 resR.pb(res[m - 2]);
39 retL = solve(k - 1, resL);
40 retR = solve(k - 1, resR);
41
42 vector<int> ret;
43 for (int v : retL) ret.pb(v);
44 for (int v : retR) ret.pb(v);
45 for (int v : retM) ret.pb(v);
46 return ret;
47 }
48
49 const int N = 203;
50
51 int dp[N + 5], from[N + 5];
52
53 void init()
54 {
55 st[0].len = 1;
56 st[0].pos = 3;
57 st[0].seq.pb(1);
58 for (int i = 1; i <= maxL; ++i)
59 {
60 auto &p = st[i - 1].seq;
61 auto &u = st[i].seq;
62 int d = st[i - 1].len;
63 st[i].len = d * 2 + (1 << (i - 1)) - 1;
64 for (int i = 0; i + 1 < (int)p.size(); ++i)
65 {
66 u.pb(p[i] | (p.back() ^ p[i]) << d);
67 u.pb(p[i] | p[i] << d | bitset<maxN>(1) << (2 * d + i));
68 }
69
70 u.pb(p.back() << d);
71 bitset<maxN> b = 0;
72 for (int j = 0; j < st[i].len; ++j) b[j] = 1;
73 u.pb(b);
74
75 vector<int> ord;
76 for (int i = 0; i < (int)u.size(); ++i)
77 {
78 ord.pb(u[i].count());
79 }
80
81 ord[(1 << i) - 1] -= ord[(1 << i) - 2];
82
83 sort(ord.begin(), ord.end());
84
85 int cnt = 0;
86 for (int i = 0; i < (int)ord.size(); ++i)
87 {
88 cnt = max(cnt, ord[i] * 2 + 1 - i);
89 }
90
91 st[i].pos = cnt;
92 }
93
94 dp[0] = 1;
95 dp[1] = 2;
96 dp[2] = 3;
CAPITOLUL 1. IOI 2020 110

97 for (int i = 2; i < N; ++i)


98 {
99 for (int j = 0; j <= maxL; ++j)
100 {
101 if (dp[i] < st[j].pos)
102 {
103 break;
104 }
105
106 if (i + (1 << j) <= N && dp[i] + st[j].len > dp[i + (1 << j)])
107 {
108 dp[i + (1 << j)] = dp[i] + st[j].len + (1 << j);
109 from[i + (1 << j)] = j;
110 }
111 }
112 }
113 }
114
115 int qcnt;
116 int query(vector<int> x)
117 {
118 assert(++qcnt <= 203);
119 return use_machine(x);
120 }
121
122 int count_mushrooms(int n)
123 {
124 if (n == 2)
125 {
126 return 2 - query({ 0, 1 });
127 }
128 init();
129 int ans = 1, pos = 0;
130 for (int i = 2; i <= N && dp[i] <= n; ++i)
131 {
132 int cur = dp[i];
133 for (int j = 0; j < N - i; ++j)
134 {
135 cur += 1 + (dp[i] + j - 1) / 2;
136 }
137 if (ans < cur)
138 {
139 ans = cur;
140 pos = i;
141 }
142 }
143
144 assert(ans >= n);
145 vector<int> seq;
146 for (int i = pos; i != 2; i -= 1 << from[i])
147 {
148 seq.pb(from[i]);
149 }
150 reverse(seq.begin(), seq.end());
151 vector<int> zero, one;
152 int l = 1;
153 zero.pb(0);
154 (query({ 0, l }) ? one : zero).pb(l);
155 ++l;
156 (query({ 0, l }) ? one : zero).pb(l);
157 ++l;
158 for (int j : seq)
159 {
160 vector<int> oc;
161 auto &s = st[j].seq;
162 int &n = st[j].len;
163 vector<int> id(1 << j), res(1 << j);
164 for (int k = 0; k < (1 << j); ++k)
165 {
166 id[k] = k;
167 }
168
169 sort(id.begin(),
170 id.end(),
171 [&](int x, int y) { return s[x].count() < s[y].count(); });
172
CAPITOLUL 1. IOI 2020 111

173 for (int k = 0; k < (1 << j); ++k)


174 {
175 int c = 0;
176 auto b = s[id[k]];
177 if (j > 0 && id[k] + 1 == (1 << j))
178 {
179 b ^= s[id[k] - 1];
180 }
181 oc.clear();
182 oc.pb(l + n + k);
183 oc.pb((one.size() > zero.size() ? one : zero)[c++]);
184 for (int i = 0; i < n; ++i)
185 {
186 if (b[i])
187 {
188 oc.pb(l + i);
189 oc.pb((one.size() > zero.size() ? one : zero)[c++]);
190 }
191 }
192
193 int w = query(oc);
194 int op = zero.size() >= one.size();
195 res[id[k]] = op ? (w >> 1) : (c - 1 - (w >> 1));
196 ((op ^ (w & 1)) ? zero : one).pb(l + n + k);
197 }
198
199 if (j > 0)
200 res[(1 << j) - 1] += res[(1 << j) - 2];
201 vector<int> val = solve(j, res);
202 for (int i = 0; i < n; ++i)
203 {
204 (val[i] ? one : zero).pb(l + i);
205 }
206 l += n + (1 << j);
207 }
208
209 int ret = zero.size();
210 while (l < n)
211 {
212 auto &p = one.size() > zero.size() ? one : zero;
213 int r = min(l + (int)p.size(), n);
214 vector<int> oc;
215 for (int i = l; i < r; ++i)
216 {
217 oc.pb(i);
218 oc.pb(p[i - l]);
219 }
220 int w = query(oc);
221 if (one.size() > zero.size())
222 {
223 w = (r - l) * 2 - w - 1;
224 }
225 ret += (r - l) - ((w + 1) >> 1);
226 ((w & 1) ? one : zero).pb(l);
227 l = r;
228 }
229
230 return ret;
231 }
232
233 // ----------------------------------------------------------
234
235 static char fmt_buffer[100000];
236 #define FMT_TO_STR(fmt, result) va_list vargs; va_start(vargs, fmt); \
237 vsnprintf(fmt_buffer, sizeof(fmt_buffer), fmt, vargs); \
238 va_end(vargs); fmt_buffer[sizeof(fmt_buffer)-1] = 0; \
239 std::string result(fmt_buffer);
240
241 static const int min_n = 2;
242 static const int max_n = 20000;
243 static const int max_qc = 20000;
244 static const int max_qs = 100000;
245 static const int species_A = 0;
246 static const int species_B = 1;
247
248 static int n;
CAPITOLUL 1. IOI 2020 112

249 static std::vector<int> species;


250 static int qc, qs;
251
252 static inline void error_if(bool cond, std::string message)
253 {
254 if (cond)
255 {
256 printf("%s\n", message.c_str());
257 exit(0);
258 }
259 }
260
261 static inline void wrong_if(bool cond, std::string message)
262 {
263 error_if(cond, "Wrong Answer: "+message);
264 }
265
266 int use_machine(std::vector<int> x)
267 {
268 const int xs = x.size();
269 wrong_if(xs < 2, "Too small array for query.");
270 wrong_if(xs > n, "Too large array for query.");
271 qc++;
272 wrong_if(qc > max_qc, "Too many queries.");
273 qs += xs;
274 wrong_if(qs > max_qs, "Too many total array sizes as queries.");
275 for (int i = 0; i < xs; i++)
276 wrong_if(x[i] < 0 || n-1 < x[i], "Invalid value in the query array.");
277
278 std::vector<bool> used(n, false);
279 for (int i = 0; i < xs; i++)
280 {
281 wrong_if(used[x[i]], "Duplicate value in the query array.");
282 used[x[i]] = true;
283 }
284
285 int diffs = 0;
286 for (int i = 1; i < xs; i++)
287 diffs += int(species[x[i]] != species[x[i-1]]);
288 return diffs;
289 }
290
291 #ifdef __GNUC__
292 __attribute__ ((format(printf, 2, 3)))
293 #endif
294
295 static inline void check_input(bool cond, const char* message_fmt, ...)
296 {
297 FMT_TO_STR(message_fmt, message);
298 error_if(!cond, "Invalid input: "+message);
299 }
300
301 int main()
302 {
303 std::clock_t c_start = std::clock();
304 auto t_start = std::chrono::high_resolution_clock::now();
305
306 auto t1 = clock();
307
308 //std::freopen("../tests/n5-16.in", "r", stdin);
309 std::freopen("../tests/1-09.in", "r", stdin);// 1-05 ... 1-11
310 //std::freopen("mushrooms.out", "w", stdout);
311
312 string strategy;
313 std::getline (std::cin,strategy);
314 //cout<<"strategy = "<<strategy<<"\n";
315
316 check_input(1 == scanf("%d", &n),
317 "Could not read n.");
318 check_input(min_n <= n,
319 "n must not be less than %d, but it is %d.", min_n, n);
320 check_input(n <= max_n,
321 "n must not be greater than %d, but it is %d.", max_n, n);
322
323 species.resize(n);
324 for (int i = 0; i < n; i++)
CAPITOLUL 1. IOI 2020 113

325 {
326 check_input(1 == scanf("%d",
327 &species[i]),
328 "Could not read species element [%d].", i);
329 check_input(species[i]==species_A || species[i] == species_B,
330 "Species elements must be %d or %d, \
331 but species element [%d] is %d.",
332 species_A, species_B, i, species[i]);
333 }
334
335 check_input(species[0] == species_A,
336 "Species element [%d] must be %d.", 0, species_A);
337 // Postponed closing standard input in order to allow \
338 interactive usage of the grader.
339
340 auto t2 = clock();
341
342 qc = 0;
343 qs = 0;
344 int answer = count_mushrooms(n);
345
346 auto t3 = clock();
347
348 printf("%d\n%d\n", answer, qc);
349
350 fclose(stdout);
351 fclose(stdin);
352
353 auto t4 = clock();
354
355 std::clock_t c_end = std::clock();
356 auto t_end = std::chrono::high_resolution_clock::now();
357
358 // reset console output
359 freopen("CON", "w", stdout);
360
361 std::cout<<"answer = "<<answer<<"\n";
362 std::cout<<"qc = "<<qc<<"\n\n";
363
364 //std::cout <<result<<’\n’<<’\n’;
365 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
366 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
367 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
368
369 std::cout << std::fixed << std::setprecision(2)
370 << "\nCPU time used: "
371 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
372 << "Wall clock time passed: "
373 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
374 << " ms\n";
375
376 return 0;
377 }
378 /*
379 answer = 10124
380 qc = 198
381
382 t2-t1 = 0.084
383 t3-t2 = 0.078
384 t4-t3 = 0
385
386 CPU time used: 162.00 ms
387 Wall clock time passed: 162.82 ms
388
389 Process returned 0 (0x0) execution time : 0.270 s
390 Press any key to continue.
391 */
CAPITOLUL 1. IOI 2020 114

1.5.3 *Rezolvare detaliat 

1.6 stations
Problema 6 - Stations 100 de puncte
Author: Mikhail Tikhomirov

Singapore's Internet Backbone (SIB) const  în n staµii, c rora li s-au atribuit indici între 0
³i n  1. De asemenea, exist  n  1 leg turi bidirecµionale, numerotate de la 0 la n  2. Fiecare
leg tur  conecteaz  dou  staµii distincte. Dou  staµii conectate printr-o leg tur  se numesc vecine.
Un drum de la staµia x la staµia y este o secvenµ  de staµii distincte a0 , a1 , ..., ap , astfel încât
a  0 x, ap y ³i oricare dou  staµii consecutive pe drum sunt vecine. Exist  exact un drum
între oricare doua staµii distincte x ³i y .
Orice staµie x poate crea un pachet (de date) ³i s  îl trimit  c tre oricare alt  staµie y , numit 
destinaµia pachetului. Acest pachet trebuie rutat pe unicul drum de la x la y , dup  cum urmeaz .
Fie z staµia ce deµine la momentul actual pachetul, a c rui staµie destinaµie este y (z neqy ).
În acest caz staµia z :
1. Execut  o procedur  de rutare ce determin  vecinul lui z care se a  pe unicul drum de
la z la y , ³i
2. trimite mai departe pachetul c tre acest vecin.

Cu toate acestea, staµiile au la dispoziµie o cantitate limitat  de memorie ³i nu pot reµine


întreaga list  de leg turi din SIB pentru a o folosi la procedura de rutare.
Sarcina voastr  este s  implementaµi o schem  de rutare pentru SIB, care const  în dou 
proceduri:
ˆ Prima procedur  prime³te n, lista de legaturi din SIB ³i un întreg k ' n  1. Aceasta atribuie
ec rei staµii un întreg unic, numit eticheta (eng. label), între 0 ³i k , inclusiv.
ˆ A doua procedur  este procedura de rutare, care este lansat  c tre toate staµiile dup  ce
etichetele au fost atribuite. Aceasta prime³te doar urm toarele intr ri:
` s, eticheta staµiei ce deµine pachetul la momentul actual,
` t, eticheta staµiei destinaµie a pachetului (t j s),
` c, lista etichetelor tuturor vecinilor lui s.

Aceasta va returna eticheta acelui vecin al lui s c ruia îi trebuie trimis mai departe pachetul.

Într-un subtask, scorul submisiei dvs. depinde de eticheta maxim  atribuit  unei staµii (în
general, cu cât mai mic, cu atât mai bine).

Detalii de implementare
Trebuie s  implementaµi urm toarele proceduri:

int[] label(int n, int k, int[] u, int[] v)

ˆ n: num rul de staµii din SIB.


ˆ k : valoarea maxim  a unei etichete ce poate  folosit .
ˆ u ³i v : tablouri de m rime n  1 ce descriu leg turile din SIB. Oricare ar  i (0 & i & n  2),
leg tura i conecteaz  staµiile cu indicii ui ³i v i.
ˆ Procedura va returna un singur tablou L de lungime n. Oricare ar  i (0 & i & n  1) Li va
reprezenta eticheta atribuit  statiei de indice i. Elementele tabloului L trebuie sa e unice
si cuprinse intre 0 si k , inclusiv.

int find_next_station(int s, int t, int[] c)

ˆ s: eticheta staµiei ce deµine pachetul.


ˆ t: eticheta staµiei destinaµie a pachetului.
ˆ c: un tablou reprezentând etichetele vecinilor lui s. Tabloul c va  sortat cresc tor.
ˆ Procedura va returna eticheta vecinului lui s c ruia îi trebuie trimis mai departe pachetul.
CAPITOLUL 1. IOI 2020 115

Fiecare test const  în unul sau mai multe scenarii independente (adic  diferite descrieri ale
SIB). Pentru ecare test constând în r scenarii, un program ce apeleaz  procedurile de mai sus
este rulat de exact dou  ori, dup  cum urmeaz .
La prima rulare a programului:
ˆ procedura label este apelat  de r ori,
ˆ etichetele returnate sunt reµinute de sistemul de evaluare, ³i
ˆ find_next_station nu este apelat .
La a doua rulare a programului:
ˆ find_next_station poate  apelata de mai multe ori. La ecare apel este ales un
scenariu arbitrar, iar apoi etichetele returnate de procedura label pentru acel scenariu
sunt folosite ca intr ri pentru find_next_station.
ˆ label nu este apelata.
Orice informaµie reµinut  în variabile statice sau globale la prima rulare nu va  disponibila în
procedura find_next_station.

Exemplu
Consider m urm torul apel:

label(5, 10, [0, 1, 1, 2], [1, 2, 3, 4])

În total sunt 5 staµii ³i 4 leg turi conectând perechile de staµii cu indicii (0,1), (1,2), (1,3) ³i
(2,4).
Etichetele pot  orice num r întreg între 0 ³i k 10.
Pentru a raporta urm toarea etichetare:
Indice Eticheta
0 6
1 2
2 9
3 3
4 7
procedura label trebuie s  returneze 6, 2, 9, 3, 7. Numerele din urm toarea gur  arat  indicii
(în stânga) ³i etichetele atribuite (în dreapta).

S  presupunem c  etichetele au fost atribuite ca mai sus ³i s  consider m urm torul apel:

find_next_station(9, 6, [2, 7])

Acesta înseamn  ca staµia ce deµine pachetul are eticheta 9, iar staµia destinaµie a pachetului
are eticheta 6. Etichetele staµiilor de pe drumul pân  la staµia destinaµie sunt 9, 2, 6. Prin
urmare, apelul trebuie s  returneze 2, reprezentând eticheta staµiei c tre care pachetul trebuie
trimis mai departe (aceasta având indice 1).
S  consider m înc  un posibil apel:

find_next_station(2, 3, [3, 6, 9])

Procedura trebuie s  returneze 3, deoarece staµia destinaµie, având eticheta 3, este vecin al
staµiei cu eticheta 2, ³i prin urmare poate primi pachetul direct.
CAPITOLUL 1. IOI 2020 116

Restricµii
ˆ 1&r & 10
Pentru ecare apel al procedurii label:
ˆ 2 & n & 1000
ˆ k 'n1
ˆ 0 & ui, v i & n  1 (oricare ar  0 & i & n  2)

Pentru ecare apel al procedurii find_next_station, intrarea provine de la un apel anterior


arbitrar ales al procedurii label. S  consider m etichetele produse.
Atunci:
ˆ s ³i t sunt etichetele a dou  staµii diferite.
ˆ c reprezint  secvenµa de etichete a vecinilor staµiei cu eticheta s, în ordine cresc toare.

Pentru ecare test, lungimea totala a tuturor tablourilor c primite de procedura


find_next_station nu va dep ³i 100 000 pentru toate scenariile la un loc.

Subtaskuri
1. (5 puncte) k 1000, nicio staµie nu va avea mai mult de 2 vecini.
¬
2. (8 puncte) k 1000, legatura i conecteaz  staµiile i  1 ³i f raci2&.
3. (16 puncte) k 1 000 000, cel mult o staµie va avea mai mult de 2 vecini.
(10 puncte) n & 8, k 10
9
4.
9
5. (61 de puncte) k 10

În subtaskul 5 puteµi obµine punctaj parµial. Fie m eticheta maxim  returnat  de procedura
label, luând în calcul toate scenariile. Scorul dvs. pentru acest subtask este calculat conform
cu urm torul tabel:
Etichet  Maxim  Scor
m ' 10
9
0
2000 & m $ 10
9
9
50 log5 105  10
m

1000 $ m % 2000 50
m & 1000 61

Sample grader
Sample graderul cite³te intrarea în urm torul format:
ˆ linia 1: r

r blocuri urmeaz , ecare descriind câte un singur scenariu. Formatul ec rui bloc este dup 
cum urmeaz :
ˆ linia 1: n k
ˆ linia 2  i (0 & i & n  2): ui v i
ˆ linia 1  n: q : num rul de apeluri ale procedurii find_next_station.
ˆ linia 2  n  j (0 & j & q  1): z j  y j  wj : indicii staµiilor implicate în al j -lea apel al
procedurii find_next_station. Staµia z j  deµine pachetul, staµia y j  este destinaµia
pachetului, iar staµia wj  este accea c tre care pachetul trebuie trimis mai departe.

Sample graderul a³eaz  rezultatul în urm torul format:


ˆ linia 1: m

r blocuri ce corespund la scenarii consecutive din input. Formatul ec rui bloc este dup  cum
urmeaz :
ˆ linia 1  j (0 & j & q  1): indicele staµiei a c rei etichet  a fost returnat  de c tre al j -lea
apel al find_next_station în acest scenariu.

B gaµi de seam  cum ecare rulare a sample graderului apeleaz  atat label cât ³i
find_next_station.

Timp maxim de executare/test: 1.0 secunde


Memorie: total 2000 MB
CAPITOLUL 1. IOI 2020 117

1.6.1 Indicaµii de rezolvare


Let us refer to the station with label L given by Ali as node L. The SIB network forms a tree.

Suppose we root the tree at the station with the smallest label. We observe that if Brenda
can, given 2 nodes A and B , determine whether
ˆ node A is an ancestor of node B
ˆ node B is an ancestor of node A
ˆ nodes A and B are not ancestors of each other
then Brenda can solve each query as follows:
1. For the source node S , check each neighbouring node to see if it is an ancestor of node S .
` If it is, it must be the parent of node S , which we shall call node P . There can be at
most 1 such node.
` Otherwise, it is a child of node S . Check if it is an ancestor of the destination node T .
If yes, return this child.
2. If no answer is returned by the steps above, either node T is an ancestor of node S or they
are not ancestors of each other. In either case, the answer is node P .
Hence, if Ali can label the stations such that Brenda can check the above just from the labels
of the station, then Brenda can solve all the queries.

Subtask 1
The network is a line. Ali can nd a leaf station and label the stations 0 to N  1 in DFS order in
O N  time. Then, node A is an ancestor of node B if and only if A & B , which can be checked
in O 1 time.

Subtask 2
The network is a binary tree. For each station X in the input, Ali can give the label X  1. That
way, the parent of node L is node L©2$. Hence, node A is an ancestor of node B if and only
if A & B and repeatedly applying B L©2$ reaches A before reaching 0. This takes O log N 
time per check.

Subtask 3
Ali can give label 0 to the station with degree more than 2, or to any station if no such station
exists, and label its neighbours 1 to M where M is the degree of node 0. If we remove node 0,
the remaining stations will form disjoint lines where one station in each line is adjacent to node
0. For each line, let the station adjacent to node 0 be node L. Ali can label each station X in the
line with d L, X   1000  L where d L, X  is the number of edges in the unique path between
station X and node L. After this, node A is an ancestor of node B if and only if A & B and either
A 0 or A%1000 B%1000 which takes O 1 time to check.

Subtask 4
The network is small with at most 8 stations. Select a random station and label it 1, this will be
the root of the tree. For each node L with M children, label it's children L  8 to L  8  M  1.
That way, the parent of node L is node L©8$. Hence, node A is an ancestor of node B if and
only if A & B and repeatedly applying B B ©8$ reaches A before reaching 0. The maximum
possible label is 8 $ 10 .
7 9

Subtask 5
In the general case, select a random station to root the tree and perform a DFS from the root:

counter = 0
dfs(station v):
in[v] = counter++
call dfs(c) for each child c of v
out[v] = counter++
CAPITOLUL 1. IOI 2020 118

call dfs(root)

Suppose Brenda knows inv  and outv  for the source, destination and the neighbours of the
source.
For any 2 stations v1 and v2 , she can deduce that v1 is an ancestor of v2 if and only if
inv1  $ inv2  $ outv1 , and thus be able to solve the query using the method described in the
previous section.
One simple way to encode this is that Ali can label each station with L inv   1000  outv ,
so Brenda can get inv  L©1000 and outv  L%1000.
The maximum label will be 999  1000  999 999999 which will get around 65 points if the
rst 4 subtasks are handled separately.
To increase the points further, we can make a few observations (*)
ˆ Brenda can nd the parent of the source station s by looking for the neighbouring station v
with the smallest inv  or outv , unless ins 0 in which case station s is the root.
ˆ If we sort the m children c  of station s in ascending order of inv  to get c1 , c2 , ..., cm ,
then outc1  inc2   1, outc2  inc3   1 and so on. Also, ins inc1   1 and
outcm  outs  1.

Hence, some minor optimisations can be made to get 70-80 points by not encoding some values
of outv  and deriving them from the inv  of the other neighbours instead.
In order to get 89 points, we need Ali to use a maximum label of 2 000 or 2n. We label each
station v as follows:
ˆ If the number of edges in the unique path from station v to the root is even, label the station
with inv .
ˆ Otherwise, label the station with outv 

For Brenda, there are 2 possible cases to consider:


ˆ If the label of the source station s is smaller than the label of its neighbours, then the label
of station s is ins and we can use the observations in (*) to nd the parent of station s (if
station s is not the root) and inv  and outv  for all its children v and be able to solve the
problem as described above.
ˆ Otherwise, the label of station s is outs and we can use the same idea to also nd the
parent of station s (if station s is not the root) and inv  and outv  for all its children and
do the same.

To get the full 100 points, we make a nal observation that the exact values of the labels are
not essential since the 89 point solution only needs to know how each label compares with each
other. Since the labels are distinct, we can simply label the stations 0 to N  1 based on the rank
of the labels in the 89 point solution.

1.6.2 Coduri surs 

Listing 1.6.1: checkerStations.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/1-08.in", // input
14 (char*)"../tests/1-08.out", // rezultat corect
15 (char*)"stations.out", // rezultat de verificat si acordat punctaj
CAPITOLUL 1. IOI 2020 119

16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("stations", argc, argv);
24
25 compareRemainingLines();
26 }
27 /*
28 argc = 4
29 checker
30 ../tests/1-08.in
31 ../tests/1-08.out
32 stations.out
33 ----------------------
34 1
35 Correct
36
37 Process returned 0 (0x0) execution time : 0.031 s
38 Press any key to continue.
39 */

Listing 1.6.2: stations_304908.cpp


1 // https://oj.uz/submission/304908 1236 ms 1520 KB
2
3 #include "stations.h"
4
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 static int max_label = 0;
10 static int r, n, k, q;
11
12 static std::vector<int> u, v, answers;
13 static std::vector<int> labels;
14
15 static std::map<int, int> reverse_labels;
16 static std::vector<std::vector<int>> adjlist;
17 static int s, t, w;
18 static std::vector<int> c;
19
20 // -----------------------------------------------
21
22 vector<int> sz;
23 vector<vector<int>> E;
24 int dfsSiz(int x, int p = -1)
25 {
26 sz[x] = 1;
27 for (int y : E[x])
28 {
29 if (y == p) continue;
30 sz[x] += dfsSiz(y, x);
31 }
32
33 return sz[x];
34 }
35
36 void dfs(int x, int s, int e, int p = -1, int h = 0)
37 {
38 if (h & 1)
39 {
40 labels[x] = e--;
41 }
42 else
43 {
44 labels[x] = s++;
45 }
46
47 for (int y : E[x])
48 {
CAPITOLUL 1. IOI 2020 120

49 if (y == p) continue;
50 dfs(y, s, s + sz[y] - 1, x, h + 1);
51 s += sz[y];
52 }
53
54 assert(s == e + 1);
55 }
56
57 vector<int> label(int n, int k, vector<int> u, vector<int> v)
58 {
59 assert((int)u.size() == n - 1 && (int)v.size() == n - 1);
60 sz = vector<int>(n);
61 labels = vector<int>(n);
62 E = vector<vector<int>>(n);
63 for (int i = 0; i < n - 1; ++i)
64 {
65 E[u[i]].push_back(v[i]);
66 E[v[i]].push_back(u[i]);
67 }
68
69 dfsSiz(0);
70 dfs(0, 0, n - 1);
71
72 return labels;
73 }
74
75 int find_next_station(int s, int t, vector<int> c)
76 {
77 vector<int> sub{s, s};
78 int m = c.size();
79 if (c[0] < s)
80 {
81 if (m > 1) sub[0] = c[1];
82 if (t < sub[0] || t > sub[1]) return c[0];
83 for (int i = 1; i + 1 < m; ++i)
84 if (c[i + 1] > t) return c[i];
85 return c[m - 1];
86 }
87 else
88 {
89 if (s != 0 && m > 1)
90 sub[1] = c[m - 2];
91 else
92 if (s == 0)
93 sub[1] = c[m - 1];
94
95 if (t < sub[0] || t > sub[1])
96 return c[m - 1];
97
98 for (int i = 0;; ++i)
99 if (c[i] >= t) return c[i];
100 }
101 }
102
103 // -----------------------------------------------
104
105 int main()
106 {
107 std::clock_t c_start = std::clock();
108 auto t_start = std::chrono::high_resolution_clock::now();
109
110 auto t1 = clock();
111
112 //std::freopen("../tests/5-49.in", "r", stdin);
113 std::freopen("../tests/1-08.in", "r", stdin);
114 //std::freopen("../tests/0-00.in", "r", stdin);
115
116 std::freopen("stations.out", "w", stdout);
117
118 assert(scanf("%d", &r) == 1);
119
120 auto t2 = clock();
121
122 for (int tc = 0; tc < r; tc++)
123 {
124 assert(scanf("%d%d", &n, &k) == 2);
CAPITOLUL 1. IOI 2020 121

125 u.resize(n - 1);


126 v.resize(n - 1);
127 adjlist.clear();
128 adjlist.resize(n);
129 for (int i = 0; i < n - 1; i++)
130 {
131 assert(scanf("%d%d", &u[i], &v[i]) == 2);
132 adjlist[u[i]].push_back(v[i]);
133 adjlist[v[i]].push_back(u[i]);
134 }
135
136 labels = label(n, k, u, v); // label(...) ...
137
138 if ((int)labels.size() != n) {
139 printf("Number of labels not equal to %d\n", n);
140 exit(0);
141 }
142
143 reverse_labels.clear();
144
145 for (int i = 0; i < n; i++)
146 {
147 if (labels[i] < 0 || labels[i] > k)
148 {
149 printf("Label not in range 0 to %d\n", k);
150 exit(0);
151 }
152
153 if (reverse_labels.find(labels[i]) != reverse_labels.end())
154 {
155 printf("Labels not unique\n");
156 exit(0);
157 }
158
159 reverse_labels[labels[i]] = i;
160 if (labels[i] > max_label)
161 {
162 max_label = labels[i];
163 }
164 }
165
166 assert(scanf("%d", &q) == 1);
167
168 for (int i = 0; i < q; i++)
169 {
170 assert(scanf("%d%d%d", &s, &t, &w) == 3);
171 c.clear();
172 for (int v : adjlist[s])
173 {
174 c.push_back(labels[v]);
175 }
176
177 std::sort(c.begin(), c.end());
178
179 int answer = find_next_station(labels[s], labels[t], c);
180
181 if (!std::binary_search(c.begin(), c.end(), answer))
182 {
183 printf("Label %d returned by find_next_station \
184 not found in c\n", answer);
185 exit(0);
186 }
187
188 answers.push_back(reverse_labels[answer]);
189 }
190 }
191
192 auto t3 = clock();
193
194 printf("%d\n", max_label);
195 for (int index : answers)
196 {
197 printf("%d\n", index);
198 }
199
200 auto t4 = clock();
CAPITOLUL 1. IOI 2020 122

201
202 std::clock_t c_end = std::clock();
203 auto t_end = std::chrono::high_resolution_clock::now();
204
205 // reset console output
206 freopen("CON", "w", stdout);
207
208 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
209 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
210 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
211
212 std::cout << std::fixed << std::setprecision(2)
213 << "\nCPU time used: "
214 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
215 << "Wall clock time passed: "
216 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
217 << " ms\n";
218
219 //return 0;
220
221 exit(0);
222 }

Listing 1.6.3: stations_304916.cpp


1 // https://oj.uz/submission/304916 1394 ms 1088 KB
2
3 #include "stations.h"
4
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 static int max_label = 0;
10 static int r, n, k, q;
11
12 static std::vector<int> u, v, answers;
13 static std::vector<int> labels;
14
15 static std::map<int, int> reverse_labels;
16 static std::vector<std::vector<int>> adjlist;
17 static int s, t, w;
18 static std::vector<int> c;
19
20 // -----------------------------------------------
21
22 struct Labeller
23 {
24 int n, ord = 0;
25 vector<vector<int>> adj;
26 vector<int> labels;
27 Labeller(int n, const vector<int>& u,
28 const vector<int>& v): n(n), adj(n), labels(n)
29 {
30 for (int i = 0; i < n - 1; i++)
31 {
32 adj[u[i]].push_back(v[i]);
33 adj[v[i]].push_back(u[i]);
34 }
35
36 preposterous(0, -1, false);
37 assert(ord == n);
38 }
39
40 void preposterous(int s, int p, bool x)
41 {
42 if (x) labels[s] = ord++;
43 for (int t : adj[s])
44 if (t != p)
45 preposterous(t, s, !x);
46
47 if (!x) labels[s] = ord++;
48 }
49 };
50
CAPITOLUL 1. IOI 2020 123

51 vector<int> label(int n, int k, vector<int> u, vector<int> v)


52 {
53 return Labeller(n, u, v).labels;
54 }
55
56 int find_next_station(int s, int t, vector<int> c)
57 {
58 if (s > c.back())
59 reverse(c.begin(), c.end());
60
61 for (int u : c)
62 if (min(u, s) <= t && t <= max(u, s))
63 return u;
64
65 return c.back();
66 }
67
68 // -----------------------------------------------
69
70 int main()
71 {
72 std::clock_t c_start = std::clock();
73 auto t_start = std::chrono::high_resolution_clock::now();
74
75 auto t1 = clock();
76
77 //std::freopen("../tests/5-49.in", "r", stdin);
78 std::freopen("../tests/1-08.in", "r", stdin);
79 //std::freopen("../tests/0-00.in", "r", stdin);
80
81 std::freopen("stations.out", "w", stdout);
82
83 assert(scanf("%d", &r) == 1);
84
85 auto t2 = clock();
86
87 for (int tc = 0; tc < r; tc++)
88 {
89 assert(scanf("%d%d", &n, &k) == 2);
90 u.resize(n - 1);
91 v.resize(n - 1);
92 adjlist.clear();
93 adjlist.resize(n);
94 for (int i = 0; i < n - 1; i++)
95 {
96 assert(scanf("%d%d", &u[i], &v[i]) == 2);
97 adjlist[u[i]].push_back(v[i]);
98 adjlist[v[i]].push_back(u[i]);
99 }
100
101 labels = label(n, k, u, v);
102 if ((int)labels.size() != n) {
103 printf("Number of labels not equal to %d\n", n);
104 exit(0);
105 }
106
107 reverse_labels.clear();
108
109 for (int i = 0; i < n; i++)
110 {
111 if (labels[i] < 0 || labels[i] > k)
112 {
113 printf("Label not in range 0 to %d\n", k);
114 exit(0);
115 }
116
117 if (reverse_labels.find(labels[i]) != reverse_labels.end())
118 {
119 printf("Labels not unique\n");
120 exit(0);
121 }
122
123 reverse_labels[labels[i]] = i;
124 if (labels[i] > max_label)
125 {
126 max_label = labels[i];
CAPITOLUL 1. IOI 2020 124

127 }
128 }
129
130 assert(scanf("%d", &q) == 1);
131
132 for (int i = 0; i < q; i++)
133 {
134 assert(scanf("%d%d%d", &s, &t, &w) == 3);
135 c.clear();
136 for (int v : adjlist[s])
137 {
138 c.push_back(labels[v]);
139 }
140
141 std::sort(c.begin(), c.end());
142
143 int answer = find_next_station(labels[s], labels[t], c);
144 if (!std::binary_search(c.begin(), c.end(), answer))
145 {
146 printf("Label %d returned by find_next_station \
147 not found in c\n", answer);
148 exit(0);
149 }
150
151 answers.push_back(reverse_labels[answer]);
152 }
153 }
154
155 auto t3 = clock();
156
157 printf("%d\n", max_label);
158 for (int index : answers)
159 {
160 printf("%d\n", index);
161 }
162
163 auto t4 = clock();
164
165 std::clock_t c_end = std::clock();
166 auto t_end = std::chrono::high_resolution_clock::now();
167
168 // reset console output
169 freopen("CON", "w", stdout);
170
171 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
172 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
173 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
174
175 std::cout << std::fixed << std::setprecision(2)
176 << "\nCPU time used: "
177 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
178 << "Wall clock time passed: "
179 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
180 << " ms\n";
181
182 //return 0;
183
184 exit(0);
185 }

Listing 1.6.4: stations_304990.cpp


1 // https://oj.uz/submission/304990 1162 ms 952 KB
2
3 #include "stations.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 static int max_label = 0;
9 static int r, n, k, q;
10
11 static std::vector<int> u, v, answers;
12 static std::vector<int> labels;
13
CAPITOLUL 1. IOI 2020 125

14 static std::map<int, int> reverse_labels;


15 static std::vector<std::vector<int>> adjlist;
16 static int s, t, w;
17 static std::vector<int> c;
18
19 // -----------------------------------------------
20
21 vector<int>ans;
22 int head[1010],o,cnt;
23
24 struct edge
25 {
26 int to,link;
27 } e[2010];
28
29 void add_edge(int u,int v)
30 {
31 e[++o].to=v,e[o].link=head[u],head[u]=o;
32 e[++o].to=u,e[o].link=head[v],head[v]=o;
33 }
34
35 void dfs(int u,int pre,int dep)
36 {
37 if(dep&1) ans[u]=cnt++;
38
39 for(int i=head[u];i;i=e[i].link)
40 {
41 if(e[i].to==pre) continue;
42 dfs(e[i].to,u,dep+1);
43 }
44
45 if(!(dep&1)) ans[u]=cnt++;
46 }
47
48 vector<int> label(int n,int k,vector<int>u,vector<int>v)
49 {
50 for(int i=0;i<n;i++) head[i]=0;
51 cnt=o=0, ans.clear(), ans.resize(n);
52 for(int i=0;i<n-1;i++) add_edge(u[i],v[i]);
53 dfs(0,-1,1);
54 return ans;
55 }
56
57 int find_next_station(int s,int t,vector<int>c)
58 {
59 if(s<c[0])
60 {
61 if(t<s) return c[c.size()-1];
62 if(t>=c[c.size()-1]) return c[c.size()-1];
63 for(int i=0;i<(int)c.size();i++) if(t<=c[i]) return c[i];
64 return c[c.size()-1];
65 }
66 else
67 {
68 if(t>s) return c[0];
69 if(t<c[1]) return c[0];
70 for(int i=c.size()-1;i>=0;i--) if(t>=c[i]) return c[i];
71 return c[0];
72 }
73 }
74
75 // -----------------------------------------------
76
77 int main()
78 {
79 std::clock_t c_start = std::clock();
80 auto t_start = std::chrono::high_resolution_clock::now();
81
82 auto t1 = clock();
83
84 //std::freopen("../tests/5-49.in", "r", stdin);
85 std::freopen("../tests/1-08.in", "r", stdin);
86 //std::freopen("../tests/0-00.in", "r", stdin);
87
88 std::freopen("stations.out", "w", stdout);
89
CAPITOLUL 1. IOI 2020 126

90 assert(scanf("%d", &r) == 1);


91
92 auto t2 = clock();
93
94 for (int tc = 0; tc < r; tc++)
95 {
96 assert(scanf("%d%d", &n, &k) == 2);
97 u.resize(n - 1);
98 v.resize(n - 1);
99 adjlist.clear();
100 adjlist.resize(n);
101 for (int i = 0; i < n - 1; i++)
102 {
103 assert(scanf("%d%d", &u[i], &v[i]) == 2);
104 adjlist[u[i]].push_back(v[i]);
105 adjlist[v[i]].push_back(u[i]);
106 }
107
108 labels = label(n, k, u, v);
109 if ((int)labels.size() != n) {
110 printf("Number of labels not equal to %d\n", n);
111 exit(0);
112 }
113
114 reverse_labels.clear();
115
116 for (int i = 0; i < n; i++)
117 {
118 if (labels[i] < 0 || labels[i] > k)
119 {
120 printf("Label not in range 0 to %d\n", k);
121 exit(0);
122 }
123
124 if (reverse_labels.find(labels[i]) != reverse_labels.end())
125 {
126 printf("Labels not unique\n");
127 exit(0);
128 }
129
130 reverse_labels[labels[i]] = i;
131 if (labels[i] > max_label)
132 {
133 max_label = labels[i];
134 }
135 }
136
137 assert(scanf("%d", &q) == 1);
138
139 for (int i = 0; i < q; i++)
140 {
141 assert(scanf("%d%d%d", &s, &t, &w) == 3);
142 c.clear();
143 for (int v : adjlist[s])
144 {
145 c.push_back(labels[v]);
146 }
147
148 std::sort(c.begin(), c.end());
149
150 int answer = find_next_station(labels[s], labels[t], c);
151 if (!std::binary_search(c.begin(), c.end(), answer))
152 {
153 printf("Label %d returned by find_next_station \
154 not found in c\n", answer);
155 exit(0);
156 }
157
158 answers.push_back(reverse_labels[answer]);
159 }
160 }
161
162 auto t3 = clock();
163
164 printf("%d\n", max_label);
165 for (int index : answers)
CAPITOLUL 1. IOI 2020 127

166 {
167 printf("%d\n", index);
168 }
169
170 auto t4 = clock();
171
172 std::clock_t c_end = std::clock();
173 auto t_end = std::chrono::high_resolution_clock::now();
174
175 // reset console output
176 freopen("CON", "w", stdout);
177
178 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
179 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
180 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
181
182 std::cout << std::fixed << std::setprecision(2)
183 << "\nCPU time used: "
184 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
185 << "Wall clock time passed: "
186 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
187 << " ms\n";
188
189 //return 0;
190
191 exit(0);
192 }

Listing 1.6.5: stations_306236.cpp


1 // https://oj.uz/submission/306236 1160 ms 1264 KB
2
3 #include "stations.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 static int max_label = 0;
9 static int r, n, k, q;
10
11 static std::vector<int> u, v, answers;
12 static std::vector<int> labels;
13
14 static std::map<int, int> reverse_labels;
15 static std::vector<std::vector<int>> adjlist;
16 static int s, t, w;
17 static std::vector<int> c;
18
19 // -----------------------------------------------
20
21 vector<vector<int>> gri;
22 vector<int> ret;
23
24 int cn;
25
26 void dfs(int x, int p, int d)
27 {
28 if(!(d&1))ret[x] = cn++;
29 for(auto i : gri[x])
30 {
31 if(i == p)continue;
32 dfs(i,x,d+1);
33 }
34
35 if(d&1)ret[x] = cn++;
36 }
37
38 vector<int> label(int n, int k, vector<int> u, vector<int> v)
39 {
40 cn = 0;
41 gri.assign(n, vector<int>(0));
42 ret.assign(n,-1);
43 for(int i = 0; i < n-1; ++i)
44 {
45 gri[u[i]].push_back(v[i]);
CAPITOLUL 1. IOI 2020 128

46 gri[v[i]].push_back(u[i]);
47 }
48
49 dfs(0,0,0);
50
51 return ret;
52 }
53
54 int find_next_station(int s, int t, vector<int> c)
55 {
56 int m = c.size();
57 if(m == 1) return c[0];
58 for(auto i : c)
59 if(i == t) return i;
60 if(s < c[0])
61 {
62 sort(c.begin(),c.end());
63 if(t < s)return c.back();
64 for(int i = 0; i < m-1; ++i)
65 {
66 if(c[i] > t)return c[i];
67 }
68
69 return c.back();
70 }
71
72 sort(c.rbegin(),c.rend());
73 if(s < t) return c.back();
74 for(int i = 0; i < m-1; ++i)
75 {
76 if(c[i] < t)return c[i];
77 }
78
79 return c.back();
80 }
81
82 // -----------------------------------------------
83
84 int main()
85 {
86 std::clock_t c_start = std::clock();
87 auto t_start = std::chrono::high_resolution_clock::now();
88
89 auto t1 = clock();
90
91 //std::freopen("../tests/5-49.in", "r", stdin);
92 std::freopen("../tests/1-08.in", "r", stdin);
93 //std::freopen("../tests/0-00.in", "r", stdin);
94
95 std::freopen("stations.out", "w", stdout);
96
97 assert(scanf("%d", &r) == 1);
98
99 auto t2 = clock();
100
101 for (int tc = 0; tc < r; tc++)
102 {
103 assert(scanf("%d%d", &n, &k) == 2);
104 u.resize(n - 1);
105 v.resize(n - 1);
106 adjlist.clear();
107 adjlist.resize(n);
108 for (int i = 0; i < n - 1; i++)
109 {
110 assert(scanf("%d%d", &u[i], &v[i]) == 2);
111 adjlist[u[i]].push_back(v[i]);
112 adjlist[v[i]].push_back(u[i]);
113 }
114
115 labels = label(n, k, u, v);
116 if ((int)labels.size() != n) {
117 printf("Number of labels not equal to %d\n", n);
118 exit(0);
119 }
120
121 reverse_labels.clear();
CAPITOLUL 1. IOI 2020 129

122
123 for (int i = 0; i < n; i++)
124 {
125 if (labels[i] < 0 || labels[i] > k)
126 {
127 printf("Label not in range 0 to %d\n", k);
128 exit(0);
129 }
130
131 if (reverse_labels.find(labels[i]) != reverse_labels.end())
132 {
133 printf("Labels not unique\n");
134 exit(0);
135 }
136
137 reverse_labels[labels[i]] = i;
138 if (labels[i] > max_label)
139 {
140 max_label = labels[i];
141 }
142 }
143
144 assert(scanf("%d", &q) == 1);
145
146 for (int i = 0; i < q; i++)
147 {
148 assert(scanf("%d%d%d", &s, &t, &w) == 3);
149 c.clear();
150 for (int v : adjlist[s])
151 {
152 c.push_back(labels[v]);
153 }
154
155 std::sort(c.begin(), c.end());
156
157 int answer = find_next_station(labels[s], labels[t], c);
158 if (!std::binary_search(c.begin(), c.end(), answer))
159 {
160 printf("Label %d returned by find_next_station \
161 not found in c\n", answer);
162 exit(0);
163 }
164
165 answers.push_back(reverse_labels[answer]);
166 }
167 }
168
169 auto t3 = clock();
170
171 printf("%d\n", max_label);
172 for (int index : answers)
173 {
174 printf("%d\n", index);
175 }
176
177 auto t4 = clock();
178
179 std::clock_t c_end = std::clock();
180 auto t_end = std::chrono::high_resolution_clock::now();
181
182 // reset console output
183 freopen("CON", "w", stdout);
184
185 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
186 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
187 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
188
189 std::cout << std::fixed << std::setprecision(2)
190 << "\nCPU time used: "
191 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
192 << "Wall clock time passed: "
193 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
194 << " ms\n";
195
196 //return 0;
197
CAPITOLUL 1. IOI 2020 130

198 exit(0);
199 }

Listing 1.6.6: stations_306242.cpp


1 // https://oj.uz/submission/306242 999 ms 1392 KB
2
3 #include "stations.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 static int max_label = 0;
9 static int r, n, k, q;
10
11 static std::vector<int> u, v, answers;
12 static std::vector<int> labels;
13
14 static std::map<int, int> reverse_labels;
15 static std::vector<std::vector<int>> adjlist;
16 static int s, t, w;
17 static std::vector<int> c;
18
19 // -----------------------------------------------
20
21 const int MN = 1001;
22
23 set<int> adj[MN];
24 vector<int> lbl;
25
26 int cur;
27 void dfs(int node, int par, bool pre)
28 {
29 if (pre) lbl[node] = cur++;
30 for (auto it=adj[node].begin(); it!=adj[node].end(); ++it)
31 {
32 if (*it == par) continue;
33 dfs(*it, node, !pre);
34 }
35
36 if (!pre) lbl[node] = cur++;
37 }
38
39 std::vector<int> label(int n, int k, std::vector<int> u, std::vector<int> v)
40 {
41 for (int i=0; i<n; ++i)
42 {
43 adj[i].clear();
44 }
45
46 assert(u.size() == v.size());
47 for (int i=0; i<(int)u.size(); ++i)
48 {
49 adj[u[i]].insert(v[i]);
50 adj[v[i]].insert(u[i]);
51 }
52
53 lbl = vector<int>(n);
54 cur = 0;
55 dfs(0, -1, true);
56
57 return lbl;
58 }
59
60 int find_next_station(int s, int t, std::vector<int> c)
61 {
62 assert(!c.empty());
63 if (c.size() == 1)
64 {
65 return c[0];
66 }
67
68 if (s < c[0])
69 { // preorder
70 if (s > t || t > c[c.size() - 2])
CAPITOLUL 1. IOI 2020 131

71 {
72 return c.back();
73 }
74 else
75 {
76 return *lower_bound(c.begin(), c.end(), t);
77 }
78 }
79 else if (s > c.back())
80 { // postorder
81 if (s < t || t < c[1])
82 {
83 return c[0];
84 }
85 else
86 {
87 int idx = upper_bound(c.begin(), c.end(), t) - c.begin();
88 assert(idx > 0);
89 return c[idx - 1];
90 }
91 }
92 else assert(false);
93
94 return -123;
95 }
96
97 // -----------------------------------------------
98
99 int main()
100 {
101 std::clock_t c_start = std::clock();
102 auto t_start = std::chrono::high_resolution_clock::now();
103
104 auto t1 = clock();
105
106 //std::freopen("../tests/5-49.in", "r", stdin);
107 std::freopen("../tests/1-08.in", "r", stdin);
108 //std::freopen("../tests/0-00.in", "r", stdin);
109
110 std::freopen("stations.out", "w", stdout);
111
112 assert(scanf("%d", &r) == 1);
113
114 auto t2 = clock();
115
116 for (int tc = 0; tc < r; tc++)
117 {
118 assert(scanf("%d%d", &n, &k) == 2);
119 u.resize(n - 1);
120 v.resize(n - 1);
121 adjlist.clear();
122 adjlist.resize(n);
123 for (int i = 0; i < n - 1; i++)
124 {
125 assert(scanf("%d%d", &u[i], &v[i]) == 2);
126 adjlist[u[i]].push_back(v[i]);
127 adjlist[v[i]].push_back(u[i]);
128 }
129
130 labels = label(n, k, u, v);
131 if ((int)labels.size() != n) {
132 printf("Number of labels not equal to %d\n", n);
133 exit(0);
134 }
135
136 reverse_labels.clear();
137
138 for (int i = 0; i < n; i++)
139 {
140 if (labels[i] < 0 || labels[i] > k)
141 {
142 printf("Label not in range 0 to %d\n", k);
143 exit(0);
144 }
145
146 if (reverse_labels.find(labels[i]) != reverse_labels.end())
CAPITOLUL 1. IOI 2020 132

147 {
148 printf("Labels not unique\n");
149 exit(0);
150 }
151
152 reverse_labels[labels[i]] = i;
153 if (labels[i] > max_label)
154 {
155 max_label = labels[i];
156 }
157 }
158
159 assert(scanf("%d", &q) == 1);
160
161 for (int i = 0; i < q; i++)
162 {
163 assert(scanf("%d%d%d", &s, &t, &w) == 3);
164 c.clear();
165 for (int v : adjlist[s])
166 {
167 c.push_back(labels[v]);
168 }
169
170 std::sort(c.begin(), c.end());
171
172 int answer = find_next_station(labels[s], labels[t], c);
173 if (!std::binary_search(c.begin(), c.end(), answer))
174 {
175 printf("Label %d returned by find_next_station \
176 not found in c\n", answer);
177 exit(0);
178 }
179
180 answers.push_back(reverse_labels[answer]);
181 }
182 }
183
184 auto t3 = clock();
185
186 printf("%d\n", max_label);
187 for (int index : answers)
188 {
189 printf("%d\n", index);
190 }
191
192 auto t4 = clock();
193
194 std::clock_t c_end = std::clock();
195 auto t_end = std::chrono::high_resolution_clock::now();
196
197 // reset console output
198 freopen("CON", "w", stdout);
199
200 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
201 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
202 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
203
204 std::cout << std::fixed << std::setprecision(2)
205 << "\nCPU time used: "
206 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
207 << "Wall clock time passed: "
208 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
209 << " ms\n";
210
211 //return 0;
212
213 exit(0);
214 }

Listing 1.6.7: stations_306508.cpp


1 // https://oj.uz/submission/306508 1022 ms 1132 KB
2
3 #include "stations.h"
4
CAPITOLUL 1. IOI 2020 133

5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 static int max_label = 0;
10 static int r, n, k, q;
11
12 static std::vector<int> u, v, answers;
13 static std::vector<int> labels;
14
15 static std::map<int, int> reverse_labels;
16 static std::vector<std::vector<int>> adjlist;
17 static int s, t, w;
18 static std::vector<int> c;
19
20 // -----------------------------------------------
21
22 int ti;
23 vector<int> adj[1005];
24
25 void dfs(int u, int p, int d, vector<int> &labels)
26 {
27 if (d % 2 == 0)
28 labels[u] = ti++;
29 for (int v : adj[u])
30 if (v != p)
31 dfs(v, u, d + 1, labels);
32 if (d % 2 == 1)
33 labels[u] = ti++;
34 }
35
36 std::vector<int> label(int n, int k, std::vector<int> u, std::vector<int> v)
37 {
38 std::vector<int> labels(n);
39
40 ti = 0;
41 for (int i=0; i<n; i++)
42 adj[i].clear();
43
44 for (int i=0; i<n-1; i++)
45 {
46 adj[u[i]].push_back(v[i]);
47 adj[v[i]].push_back(u[i]);
48 }
49
50 dfs(0, -1, 0, labels);
51
52 return labels;
53 }
54
55 int find_next_station(int s, int t, std::vector<int> c)
56 {
57 if (c.back() < s)
58 {
59 // odd depth
60 if (t > s || t < c[0])
61 return c[0];
62
63 for (int i=0; i<(int)c.size()-1; i++)
64 if (t >= c[i] && t < c[i+1])
65 return c[i];
66
67 return c.back();
68 }
69 else
70 {
71 // even depth
72 if (t < s || t > c.back())
73 return c.back();
74
75 for (int i=(int)c.size()-1; i>0; i--)
76 if (t <= c[i] && t > c[i-1])
77 return c[i];
78
79 return c[0];
80 }
CAPITOLUL 1. IOI 2020 134

81 }
82
83 // -----------------------------------------------
84
85 int main()
86 {
87 std::clock_t c_start = std::clock();
88 auto t_start = std::chrono::high_resolution_clock::now();
89
90 auto t1 = clock();
91
92 //std::freopen("../tests/5-49.in", "r", stdin);
93 std::freopen("../tests/1-08.in", "r", stdin);
94 //std::freopen("../tests/0-00.in", "r", stdin);
95
96 std::freopen("stations.out", "w", stdout);
97
98 assert(scanf("%d", &r) == 1);
99
100 auto t2 = clock();
101
102 for (int tc = 0; tc < r; tc++)
103 {
104 assert(scanf("%d%d", &n, &k) == 2);
105 u.resize(n - 1);
106 v.resize(n - 1);
107 adjlist.clear();
108 adjlist.resize(n);
109 for (int i = 0; i < n - 1; i++)
110 {
111 assert(scanf("%d%d", &u[i], &v[i]) == 2);
112 adjlist[u[i]].push_back(v[i]);
113 adjlist[v[i]].push_back(u[i]);
114 }
115
116 labels = label(n, k, u, v);
117 if ((int)labels.size() != n) {
118 printf("Number of labels not equal to %d\n", n);
119 exit(0);
120 }
121
122 reverse_labels.clear();
123
124 for (int i = 0; i < n; i++)
125 {
126 if (labels[i] < 0 || labels[i] > k)
127 {
128 printf("Label not in range 0 to %d\n", k);
129 exit(0);
130 }
131
132 if (reverse_labels.find(labels[i]) != reverse_labels.end())
133 {
134 printf("Labels not unique\n");
135 exit(0);
136 }
137
138 reverse_labels[labels[i]] = i;
139 if (labels[i] > max_label)
140 {
141 max_label = labels[i];
142 }
143 }
144
145 assert(scanf("%d", &q) == 1);
146
147 for (int i = 0; i < q; i++)
148 {
149 assert(scanf("%d%d%d", &s, &t, &w) == 3);
150 c.clear();
151 for (int v : adjlist[s])
152 {
153 c.push_back(labels[v]);
154 }
155
156 std::sort(c.begin(), c.end());
CAPITOLUL 1. IOI 2020 135

157
158 int answer = find_next_station(labels[s], labels[t], c);
159 if (!std::binary_search(c.begin(), c.end(), answer))
160 {
161 printf("Label %d returned by find_next_station \
162 not found in c\n", answer);
163 exit(0);
164 }
165
166 answers.push_back(reverse_labels[answer]);
167 }
168 }
169
170 auto t3 = clock();
171
172 printf("%d\n", max_label);
173 for (int index : answers)
174 {
175 printf("%d\n", index);
176 }
177
178 auto t4 = clock();
179
180 std::clock_t c_end = std::clock();
181 auto t_end = std::chrono::high_resolution_clock::now();
182
183 // reset console output
184 freopen("CON", "w", stdout);
185
186 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
187 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
188 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
189
190 std::cout << std::fixed << std::setprecision(2)
191 << "\nCPU time used: "
192 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
193 << "Wall clock time passed: "
194 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
195 << " ms\n";
196
197 //return 0;
198
199 exit(0);
200 }

Listing 1.6.8: stations_306854.cpp


1 // https://oj.uz/submission/306854 915 ms 1280 KB
2
3 #include <bits/stdc++.h>
4 #include "stations.h"
5
6 #define DIM 1010
7
8 using namespace std;
9
10 static int max_label = 0;
11 static int r, n, k, q;
12
13 static std::vector<int> u, v, answers;
14 static std::vector<int> labels;
15
16 static std::map<int, int> reverse_labels;
17 static std::vector<std::vector<int>> adjlist;
18 static int s, t, w;
19 static std::vector<int> c;
20
21 // -----------------------------------------------
22
23 vector <int> L[DIM],aux;
24 int viz[DIM],level[DIM],fth[DIM];
25 pair <int,int> poz[DIM];
26 int idx;
27
28 void dfs (int nod)
CAPITOLUL 1. IOI 2020 136

29 {
30
31 viz[nod] = 1;
32 poz[nod].first = ++idx;
33 for (auto vecin : L[nod])
34 if (!viz[vecin])
35 {
36 fth[vecin] = nod;
37 level[vecin] = 1 + level[nod];
38 dfs (vecin);
39 }
40
41 poz[nod].second = ++idx;
42 }
43
44 int cautare_binara (int n, int val)
45 {
46 int st = 0, dr = n-1;
47 while (st <= dr)
48 {
49 int mid = (st + dr)>>1;
50 if (aux[mid] == val)
51 return mid;
52
53 if (aux[mid] < val)
54 st = mid+1;
55 else
56 dr = mid-1;
57 }
58
59 return -123;
60 }
61
62 vector <int> label (int n, int k, vector <int> u, vector <int> v)
63 {
64 int i;
65
66 for (i=0;i<n;i++)
67 L[i].clear();
68
69 for (i=0;i<n-1;i++)
70 {
71 int x = u[i], y = v[i];
72 L[x].push_back(y);
73 L[y].push_back(x);
74 }
75
76 memset (viz,0,sizeof viz);
77 memset (level,0,sizeof level);
78 memset (poz,0,sizeof poz); // ... ???
79 memset (fth,0,sizeof fth);
80
81 idx = -1;
82
83 dfs (0);
84
85 vector <int> labels;
86 for (i=0;i<n;i++)
87 {
88 if (level[i] % 2 == 0)
89 labels.push_back(poz[i].first);
90 else
91 labels.push_back(poz[i].second);
92 }
93
94 /// pot sa le normalizez??
95 aux = labels;
96 sort (aux.begin(),aux.end());
97 for (i=0;i<n;i++)
98 labels[i] = cautare_binara (n,labels[i]);
99
100 return labels;
101 }
102
103 int find_next_station (int x, int y, vector <int> v)
104 {
CAPITOLUL 1. IOI 2020 137

105 /// mai intai vad daca se afla pe un nivel par sau impar
106
107 int ok = 0;
108 for (auto it : v)
109 if (it < x)
110 {
111 ok = 1;
112 break;
113 }
114
115 if (!ok)
116 { /// labelul x inseamna de fapt un inceput de interval
117
118 if (y < x)
119 return v.back();
120
121 for (auto it : v)
122 if (y <= it)
123 return it;
124
125 return v.back();
126 }
127 else
128 {
129 if (y > x)
130 return v.front();
131
132 for (int i=v.size()-1;i>=0;i--)
133 if (y >= v[i])
134 return v[i];
135
136 return v.front();
137 }
138 }
139
140 // -----------------------------------------------
141
142 int main()
143 {
144 std::clock_t c_start = std::clock();
145 auto t_start = std::chrono::high_resolution_clock::now();
146
147 auto t1 = clock();
148
149 //std::freopen("../tests/5-49.in", "r", stdin);
150 std::freopen("../tests/1-08.in", "r", stdin);
151 //std::freopen("../tests/0-00.in", "r", stdin);
152
153 std::freopen("stations.out", "w", stdout);
154
155 assert(scanf("%d", &r) == 1);
156
157 auto t2 = clock();
158
159 for (int tc = 0; tc < r; tc++)
160 {
161 assert(scanf("%d%d", &n, &k) == 2);
162 u.resize(n - 1);
163 v.resize(n - 1);
164 adjlist.clear();
165 adjlist.resize(n);
166 for (int i = 0; i < n - 1; i++)
167 {
168 assert(scanf("%d%d", &u[i], &v[i]) == 2);
169 adjlist[u[i]].push_back(v[i]);
170 adjlist[v[i]].push_back(u[i]);
171 }
172
173 labels = label(n, k, u, v);
174 if ((int)labels.size() != n) {
175 printf("Number of labels not equal to %d\n", n);
176 exit(0);
177 }
178
179 reverse_labels.clear();
180
CAPITOLUL 1. IOI 2020 138

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


182 {
183 if (labels[i] < 0 || labels[i] > k)
184 {
185 printf("Label not in range 0 to %d\n", k);
186 exit(0);
187 }
188
189 if (reverse_labels.find(labels[i]) != reverse_labels.end())
190 {
191 printf("Labels not unique\n");
192 exit(0);
193 }
194
195 reverse_labels[labels[i]] = i;
196 if (labels[i] > max_label)
197 {
198 max_label = labels[i];
199 }
200 }
201
202 assert(scanf("%d", &q) == 1);
203
204 for (int i = 0; i < q; i++)
205 {
206 assert(scanf("%d%d%d", &s, &t, &w) == 3);
207 c.clear();
208 for (int v : adjlist[s])
209 {
210 c.push_back(labels[v]);
211 }
212
213 std::sort(c.begin(), c.end());
214
215 int answer = find_next_station(labels[s], labels[t], c);
216 if (!std::binary_search(c.begin(), c.end(), answer))
217 {
218 printf("Label %d returned by find_next_station \
219 not found in c\n", answer);
220 exit(0);
221 }
222
223 answers.push_back(reverse_labels[answer]);
224 }
225 }
226
227 auto t3 = clock();
228
229 printf("%d\n", max_label);
230 for (int index : answers)
231 {
232 printf("%d\n", index);
233 }
234
235 auto t4 = clock();
236
237 std::clock_t c_end = std::clock();
238 auto t_end = std::chrono::high_resolution_clock::now();
239
240 // reset console output
241 freopen("CON", "w", stdout);
242
243 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
244 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
245 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
246
247 std::cout << std::fixed << std::setprecision(2)
248 << "\nCPU time used: "
249 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
250 << "Wall clock time passed: "
251 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
252 << " ms\n";
253
254 //return 0;
255
256 exit(0);
CAPITOLUL 1. IOI 2020 139

257 }

Listing 1.6.9: stations_308162.cpp


1 // https://oj.uz/submission/308162 1013 ms 1160 KB
2
3 #include "stations.h"
4
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 static int max_label = 0;
10 static int r, n, k, q;
11
12 static std::vector<int> u, v, answers;
13 static std::vector<int> labels;
14
15 static std::map<int, int> reverse_labels;
16 static std::vector<std::vector<int>> adjlist;
17 static int s, t, w;
18 static std::vector<int> c;
19
20 // -----------------------------------------------
21
22 vector<int>E[1010];
23
24 int Num[1010], cnt;
25
26 void DFS(int a, int pp, int dep)
27 {
28 //int i;
29 if(!dep)Num[a]=++cnt;
30 for(auto &x : E[a])
31 {
32 if(x==pp)continue;
33 DFS(x,a,!dep);
34 }
35
36 if(dep)Num[a]=++cnt;
37 }
38
39 std::vector<int> label(int n, int k, std::vector<int> u, std::vector<int> v)
40 {
41 std::vector<int> labels(n);
42 cnt = 0;
43 for(int i=0;i<n;i++)E[i].clear();
44 for (int i = 0; i < n; i++)
45 {
46 labels[i] = i;
47 }
48
49 for(int i=0;i<n-1;i++)
50 {
51 E[u[i]].push_back(v[i]);
52 E[v[i]].push_back(u[i]);
53 }
54
55 DFS(0,-1,0);
56 for(int i=0;i<n;i++)
57 {
58 labels[i] = Num[i]-1;
59 }
60
61 return labels;
62 }
63
64 int find_next_station(int s, int t, std::vector<int> c)
65 {
66 int b = s, e = s;
67 int sz =c.size();
68 if(s < c[0])
69 {
70 if(sz>=2)e = c[sz-2];
71 if(b<=t && t<=e)
CAPITOLUL 1. IOI 2020 140

72 {
73 for(auto &x : c)
74 {
75 if(x>=t)return x;
76 }
77 }
78
79 return c[sz-1];
80 }
81 else
82 {
83 if(sz>=2)b = c[1];
84 if(b<=t && t<=e)
85 {
86 reverse(c.begin(),c.end());
87 for(auto &x : c)
88 {
89 if(x<=t)return x;
90 }
91 }
92
93 return c[0];
94 }
95 }
96
97 // -----------------------------------------------
98
99 int main()
100 {
101 std::clock_t c_start = std::clock();
102 auto t_start = std::chrono::high_resolution_clock::now();
103
104 auto t1 = clock();
105
106 //std::freopen("../tests/5-49.in", "r", stdin);
107 std::freopen("../tests/1-08.in", "r", stdin);
108 //std::freopen("../tests/0-00.in", "r", stdin);
109
110 std::freopen("stations.out", "w", stdout);
111
112 assert(scanf("%d", &r) == 1);
113
114 auto t2 = clock();
115
116 for (int tc = 0; tc < r; tc++)
117 {
118 assert(scanf("%d%d", &n, &k) == 2);
119 u.resize(n - 1);
120 v.resize(n - 1);
121 adjlist.clear();
122 adjlist.resize(n);
123 for (int i = 0; i < n - 1; i++)
124 {
125 assert(scanf("%d%d", &u[i], &v[i]) == 2);
126 adjlist[u[i]].push_back(v[i]);
127 adjlist[v[i]].push_back(u[i]);
128 }
129
130 labels = label(n, k, u, v);
131 if ((int)labels.size() != n) {
132 printf("Number of labels not equal to %d\n", n);
133 exit(0);
134 }
135
136 reverse_labels.clear();
137
138 for (int i = 0; i < n; i++)
139 {
140 if (labels[i] < 0 || labels[i] > k)
141 {
142 printf("Label not in range 0 to %d\n", k);
143 exit(0);
144 }
145
146 if (reverse_labels.find(labels[i]) != reverse_labels.end())
147 {
CAPITOLUL 1. IOI 2020 141

148 printf("Labels not unique\n");


149 exit(0);
150 }
151
152 reverse_labels[labels[i]] = i;
153 if (labels[i] > max_label)
154 {
155 max_label = labels[i];
156 }
157 }
158
159 assert(scanf("%d", &q) == 1);
160
161 for (int i = 0; i < q; i++)
162 {
163 assert(scanf("%d%d%d", &s, &t, &w) == 3);
164 c.clear();
165 for (int v : adjlist[s])
166 {
167 c.push_back(labels[v]);
168 }
169
170 std::sort(c.begin(), c.end());
171
172 int answer = find_next_station(labels[s], labels[t], c);
173 if (!std::binary_search(c.begin(), c.end(), answer))
174 {
175 printf("Label %d returned by find_next_station \
176 not found in c\n", answer);
177 exit(0);
178 }
179
180 answers.push_back(reverse_labels[answer]);
181 }
182 }
183
184 auto t3 = clock();
185
186 printf("%d\n", max_label);
187 for (int index : answers)
188 {
189 printf("%d\n", index);
190 }
191
192 auto t4 = clock();
193
194 std::clock_t c_end = std::clock();
195 auto t_end = std::chrono::high_resolution_clock::now();
196
197 // reset console output
198 freopen("CON", "w", stdout);
199
200 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
201 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
202 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
203
204 std::cout << std::fixed << std::setprecision(2)
205 << "\nCPU time used: "
206 << 1000.0 * (c_end-c_start) / CLOCKS_PER_SEC << " ms\n"
207 << "Wall clock time passed: "
208 << std::chrono::duration<double, std::milli>(t_end-t_start).count()
209 << " ms\n";
210
211 //return 0;
212
213 exit(0);
214 }

1.6.3 *Rezolvare detaliat 


Capitolul 2

IOI 201923

2.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 panto tocmai a sosit în magazin. Fiecare pereche este format  din doi panto de
aceea³i m rime: stânga respectiv dreapta. Adnan a pus toµi cei 2n panto î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 pantoi î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 Pantoi 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 panto adiacenµi în acel moment ³i îi interschimb  (îi ridic  ³i pune
ecare pantof pe locul celuilalt). Doi panto 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 panto.

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 ecare i (0 & i & 2n  1), S i este o valoare
nenul  ce descrie pantoful iniµial aat 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 panto 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])
23
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).

142
CAPITOLUL 2. IOI 2019 143

Figura 2.1: Shoes

Adnan poate obµine un aranjament valid în 4 interschimb ri.


De exemplu, el poate face prima interschimbare între pantoi 1 ³i 1, apoi 1 ³i 2, apoi 1 ³i
2, ³i în nal 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 pantoi au aceea³i m rime:
count_swaps([-2, 2, 2, -2, -2, 2])
Adnan poate schimba pantoi 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 ecare 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 pantolor prin efectuarea unor secvenµe de interschim-
b ri

Subtaskuri

1. (10 puncte) n 1
2. (20 de puncte) n & 8
3. (20 de puncte) Toµi pantoi sunt de aceea³i m rime.
4. (15 puncte) Toµi pantoi de pe poziµiile 0, ..., n  1 sunt de stânga, iar toµi pantoi de pe
poziµiile n, ..., 2n  1 sunt de dreapta. De asemenea, pentru ecare i (0 & i & n  1), pantoi 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 2. IOI 2019 144

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

2.1.1 Indicaµii de rezolvare


24
Let's consider the rst (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 rst kind do not aect the
relative ordering of all the other shoes. At the same time, swaps of the second kind do not aect
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 rst 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, rst 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 eciently in
O n log n using a Fenwick tree or a segment tree.

2.1.2 Coduri surs 

Listing 2.1.1: compile_cpp.sh


1 #!/bin/bash
2
3 problem=shoes
4
5 g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"
6
7 "grader.cpp" "${problem}.cpp"

Listing 2.1.2: shoes.h


1 #include <vector>
2
3 long long count_swaps(std::vector<int> S);

Listing 2.1.3: shoes.cpp


1 #include "shoes.h"
2
3 long long count_swaps(std::vector<int> s)
4 {
5 return 1;
6 }

24
Dac  cineva are nevoie de traducere, cred c  a nimerit un pic aiurea pe aici!!!
CAPITOLUL 2. IOI 2019 145

Listing 2.1.4: grader.cpp


1 #include "shoes.h"
2 #include <cstdio>
3 #include <cassert>
4
5 using namespace std;
6
7 int main()
8 {
9 int n;
10 assert(1 == scanf("%d", &n));
11 vector<int> S(2 * n);
12 for (int i = 0; i < 2 * n; i++)
13 assert(1 == scanf("%d", &S[i]));
14 fclose(stdin);
15
16 long long result = count_swaps(S);
17
18 printf("%lld\n", result);
19 fclose(stdout);
20 return 0;
21 }

Listing 2.1.5: shoes-model.cpp


1 #include "shoes.h"
2
3 using namespace std;
4
5 class Fenwick
6 {
7 vector<int> a;
8
9 int count(int right)
10 {
11 int cnt = 0;
12 for (; right >= 0; right = (right & (right + 1)) - 1)
13 {
14 cnt += a[right];
15 }
16 return cnt;
17 }
18
19 public:
20 explicit Fenwick(int n)
21 {
22 a.assign(n, 0);
23 }
24
25 int count(int left, int right)
26 {
27 return count(right) - count(left - 1);
28 }
29
30 void put(int index)
31 {
32 for (; index < int(a.size()); index = index | (index + 1))
33 {
34 a[index]++;
35 }
36 }
37 };
38
39 vector<int> create_index(int n, vector<int> &S)
40 {
41 vector<int> index(2 * n + 1);
42 for (int i = 0; i < 2 * n; i++)
43 {
44 index[S[i] + n] = i;
45 }
46 return index;
47 }
48
49 long long count_adjacent(int n, vector<int> &S)
CAPITOLUL 2. IOI 2019 146

50 {
51 vector<int> index = create_index(n, S);
52 Fenwick f = Fenwick(2 * n);
53 long long ans = 0;
54 for (int i = 0; i < 2 * n; i++)
55 {
56 if (S[i] != 0)
57 {
58 int pos = index[-S[i] + n];
59 ans += pos - i - f.count(i, pos) - (S[i] < 0 ? 1 : 0);
60 S[pos] = 0;
61 f.put(pos);
62 }
63 }
64 return ans;
65 }
66
67 vector<int> change(vector<int> v)
68 {
69 int n = (int)v.size() / 2;
70 vector<int> cnt(n, 0);
71 for(int i = 0; i < 2*n; i++)
72 if(v[i] > 0)
73 cnt[v[i] - 1]++;
74
75 for(int i = 1; i < n; i++)
76 cnt[i] += cnt[i-1];
77
78 vector<int> cntl = cnt, cntr = cnt;
79
80 for(int i = 0; i < 2*n; i++)
81 if(v[i] > 0)
82 v[i] = (--cntr[v[i] - 1]) + 1;
83 else
84 v[i] = -((--cntl[-v[i] - 1]) + 1);
85 return v;
86 }
87
88 long long count_swaps(vector<int> S)
89 {
90 S = change(S);
91 int n = S.size() / 2;
92 return count_adjacent(n, S);
93 }

Listing 2.1.6: shoes-model+grader.cpp


1 #include "shoes.h"
2
3 #include <cstdio>
4 #include <cassert>
5
6 #include <time.h> /* clock */
7 #include <iostream>
8
9 using namespace std;
10
11 class Fenwick
12 {
13 vector<int> a;
14
15 int count(int right)
16 {
17 int cnt = 0;
18 for (; right >= 0; right = (right & (right + 1)) - 1)
19 {
20 cnt += a[right];
21 }
22 return cnt;
23 }
24
25 public:
26 explicit Fenwick(int n)
27 {
28 a.assign(n, 0);
CAPITOLUL 2. IOI 2019 147

29 }
30
31 int count(int left, int right)
32 {
33 return count(right) - count(left - 1);
34 }
35
36 void put(int index)
37 {
38 for (; index < int(a.size()); index = index | (index + 1))
39 {
40 a[index]++;
41 }
42 }
43 };
44
45 vector<int> create_index(int n, vector<int> &S)
46 {
47 vector<int> index(2 * n + 1);
48 for (int i = 0; i < 2 * n; i++)
49 {
50 index[S[i] + n] = i;
51 }
52 return index;
53 }
54
55 long long count_adjacent(int n, vector<int> &S)
56 {
57 vector<int> index = create_index(n, S);
58 Fenwick f = Fenwick(2 * n);
59 long long ans = 0;
60 for (int i = 0; i < 2 * n; i++)
61 {
62 if (S[i] != 0)
63 {
64 int pos = index[-S[i] + n];
65 ans += pos - i - f.count(i, pos) - (S[i] < 0 ? 1 : 0);
66 S[pos] = 0;
67 f.put(pos);
68 }
69 }
70 return ans;
71 }
72
73 vector<int> change(vector<int> v)
74 {
75 int n = (int)v.size() / 2;
76 vector<int> cnt(n, 0);
77 for(int i = 0; i < 2*n; i++)
78 if(v[i] > 0)
79 cnt[v[i] - 1]++;
80
81 for(int i = 1; i < n; i++)
82 cnt[i] += cnt[i-1];
83
84 vector<int> cntl = cnt, cntr = cnt;
85
86 for(int i = 0; i < 2*n; i++)
87 if(v[i] > 0)
88 v[i] = (--cntr[v[i] - 1]) + 1;
89 else
90 v[i] = -((--cntl[-v[i] - 1]) + 1);
91 return v;
92 }
93
94
95 long long count_swaps(vector<int> S)
96 {
97 S = change(S);
98 int n = S.size() / 2;
99 return count_adjacent(n, S);
100 }
101
102 int read_int()
103 {
104 int x;
CAPITOLUL 2. IOI 2019 148

105 if (scanf("%d", &x) != 1)


106 {
107 fprintf(stderr, "Error while reading input\n");
108 exit(1);
109 }
110 return x;
111 }
112
113 int main()
114 {
115 auto t1 = clock();
116
117 std::freopen("../tests/6-15.in", "r", stdin) ;
118 std::freopen("shoes.out", "w", stdout) ;
119
120 int n;
121 //assert(1 == scanf("%d", &n));
122 n = read_int();
123
124 vector<int> S(2 * n);
125 for (int i = 0; i < 2 * n; i++)
126 //assert(1 == scanf("%d", &S[i]));
127 S[i] = read_int();
128
129 fclose(stdin);
130
131 auto t2 = clock();
132
133 long long result = count_swaps(S);
134
135 auto t3 = clock();
136
137
138 printf("%lld\n", result);
139 fclose(stdout);
140
141 auto t4 = clock();
142
143 // reset console output
144 freopen("CON", "w", stdout);
145
146 std::cout <<result<<’\n’<<’\n’;
147 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
148 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
149 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
150
151 return 0;
152 }

Listing 2.1.7: shoes 143076+grader.cpp


1 // https://oj.uz/submission/143076
2
3 #include <bits/stdc++.h>
4 #include "shoes.h"
5
6 using std::vector;
7 using std::pair;
8
9 #define SZ(vec) int((vec).size())
10
11 vector<int> change(vector<int> v)
12 {
13 int n = (int)v.size() / 2;
14 vector<int> cnt(n, 0);
15 for(int i = 0; i < 2*n; i++)
16 if(v[i] > 0)
17 cnt[v[i] - 1]++;
18
19 for(int i = 1; i < n; i++)
20 cnt[i] += cnt[i-1];
21
22 vector<int> cntl = cnt, cntr = cnt;
23
24 for(int i = 0; i < 2*n; i++)
CAPITOLUL 2. IOI 2019 149

25 if(v[i] > 0)
26 v[i] = (--cntr[v[i] - 1]) + 1;
27 else
28 v[i] = -((--cntl[-v[i] - 1]) + 1);
29 return v;
30 }
31
32 long long count_swaps(vector<int> s)
33 {
34 s = change(s);
35 vector<pair<int, bool>> in(SZ(s));
36
37 vector<int> where(SZ(s) / 2, -1);
38
39 for (int i = 0; i != SZ(s); ++i)
40 {
41 in[i].first = abs(s[i]) - 1;
42 in[i].second = (s[i] < 0 ? 0 : 1);
43
44 if (where[in[i].first] == -1)
45 where[in[i].first] = i;
46 }
47
48 vector<int> fenw(SZ(in));
49 for (int i = 0; i != SZ(in); ++i)
50 fenw[i] = 1 + i - (i & (i+1));
51
52 long long ans = 0;
53 vector<char> dead(SZ(in), false);
54
55 for (int pos = SZ(in) - 1; pos >= 0; --pos)
56 if (not dead[pos])
57 {
58 dead[pos] = 1;
59
60 int i = where[in[pos].first];
61 dead[i] = 1;
62
63 int dist = 0;
64 for (int p = pos; p >= 0; p = (p & (p + 1)) - 1)
65 dist += fenw[p];
66
67 for (int p = i; p >= 0; p = (p & (p + 1)) - 1)
68 dist -= fenw[p];
69
70 if (in[pos].second == 1)
71 --dist;
72
73 ans += dist;
74 for (int p = i; p < SZ(fenw); p = p | (p + 1))
75 fenw[p] -= 1;
76 }
77
78 return ans;
79 }
80
81
82 int read_int()
83 {
84 int x;
85 if (scanf("%d", &x) != 1)
86 {
87 fprintf(stderr, "Error while reading input\n");
88 exit(1);
89 }
90 return x;
91 }
92
93 int main()
94 {
95 auto t1 = clock();
96
97 std::freopen("../tests/6-15.in", "r", stdin) ;
98 std::freopen("shoes.out", "w", stdout) ;
99
100 int n;
CAPITOLUL 2. IOI 2019 150

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


102 n = read_int();
103
104 vector<int> S(2 * n);
105 for (int i = 0; i < 2 * n; i++)
106 //assert(1 == scanf("%d", &S[i]));
107 S[i] = read_int();
108
109 fclose(stdin);
110
111 auto t2 = clock();
112
113 long long result = count_swaps(S);
114
115 auto t3 = clock();
116
117
118 printf("%lld\n", result);
119 fclose(stdout);
120
121 auto t4 = clock();
122
123 // reset console output
124 freopen("CON", "w", stdout);
125
126 std::cout <<result<<’\n’<<’\n’;
127 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
128 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
129 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
130
131 return 0;
132 }

Listing 2.1.8: shoes 143080+grader.cpp


1 // https://oj.uz/submission/143080
2
3 #include <bits/stdc++.h>
4 #include "shoes.h"
5
6 using namespace std;
7
8 class Fenwick
9 {
10 vector<int> a;
11
12 int count(int right)
13 {
14 int cnt = 0;
15 for (; right >= 0; right = (right & (right + 1)) - 1)
16 {
17 cnt += a[right];
18 }
19 return cnt;
20 }
21
22 public:
23 explicit Fenwick(int n)
24 {
25 a.assign(n, 0);
26 }
27
28 int count(int left, int right)
29 {
30 return count(right) - count(left - 1);
31 }
32
33 void put(int index)
34 {
35 for (; index < int(a.size()); index = index | (index + 1))
36 {
37 a[index]++;
38 }
39 }
40 };
CAPITOLUL 2. IOI 2019 151

41
42 vector<int> create_index(int n, vector<int> &S)
43 {
44 vector<int> index(2 * n + 1);
45 for (int i = 0; i < 2 * n; i++)
46 {
47 index[S[i] + n] = i;
48 }
49 return index;
50 }
51
52 long long count_adjacent(int n, vector<int> &S)
53 {
54 vector<int> index = create_index(n, S);
55 Fenwick f = Fenwick(2 * n);
56 long long ans = 0;
57 for (int i = 0; i < 2 * n; i++)
58 {
59 if (S[i] != 0)
60 {
61 int pos = index[-S[i] + n];
62 ans += pos - i - f.count(i, pos) - (S[i] < 0 ? 1 : 0);
63 S[pos] = 0;
64 f.put(pos);
65 }
66 }
67 return ans;
68 }
69
70 vector<int> change(vector<int> v)
71 {
72 int n = (int)v.size() / 2;
73 vector<int> cnt(n, 0);
74 for(int i = 0; i < 2*n; i++)
75 if(v[i] > 0)
76 cnt[v[i] - 1]++;
77
78 for(int i = 1; i < n; i++)
79 cnt[i] += cnt[i-1];
80
81 vector<int> cntl = cnt, cntr = cnt;
82
83 for(int i = 0; i < 2*n; i++)
84 if(v[i] > 0)
85 v[i] = (--cntr[v[i] - 1]) + 1;
86 else
87 v[i] = -((--cntl[-v[i] - 1]) + 1);
88 return v;
89 }
90
91 long long count_swaps(vector<int> S)
92 {
93 S = change(S);
94 int n = S.size() / 2;
95 return count_adjacent(n, S);
96 }
97
98 int read_int()
99 {
100 int x;
101 if (scanf("%d", &x) != 1)
102 {
103 fprintf(stderr, "Error while reading input\n");
104 exit(1);
105 }
106 return x;
107 }
108
109 int main()
110 {
111 auto t1 = clock();
112
113 std::freopen("../tests/6-15.in", "r", stdin) ;
114 std::freopen("shoes.out", "w", stdout) ;
115
116 int n;
CAPITOLUL 2. IOI 2019 152

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


118 n = read_int();
119
120 vector<int> S(2 * n);
121 for (int i = 0; i < 2 * n; i++)
122 //assert(1 == scanf("%d", &S[i]));
123 S[i] = read_int();
124
125 fclose(stdin);
126
127 auto t2 = clock();
128
129 long long result = count_swaps(S);
130
131 auto t3 = clock();
132
133
134 printf("%lld\n", result);
135 fclose(stdout);
136
137 auto t4 = clock();
138
139 // reset console output
140 freopen("CON", "w", stdout);
141
142 std::cout <<result<<’\n’<<’\n’;
143 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
144 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
145 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
146
147 return 0;
148 }

Listing 2.1.9: shoes-ds_ok+grader.cpp


1 // Dmitry _kun_ Sayutin (2019)
2
3 #include <bits/stdc++.h>
4 #include "shoes.h"
5
6 using std::cin;
7 using std::cout;
8 using std::cerr;
9
10 using std::vector;
11 using std::map;
12 using std::array;
13 using std::set;
14 using std::string;
15
16 using std::pair;
17 using std::make_pair;
18
19 using std::tuple;
20 using std::make_tuple;
21 using std::get;
22
23 using std::min;
24 using std::abs;
25 using std::max;
26 using std::swap;
27
28 using std::unique;
29 using std::sort;
30 using std::generate;
31 using std::reverse;
32 using std::min_element;
33 using std::max_element;
34
35 #ifdef LOCAL
36 #define LASSERT(X) assert(X)
37 #else
38 #define LASSERT(X) {}
39 #endif
40
CAPITOLUL 2. IOI 2019 153

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


42 #define ALL(data) data.begin(),data.end()
43 #define RALL(data) data.rbegin(),data.rend()
44 #define TYPEMAX(type) std::numeric_limits<type>::max()
45 #define TYPEMIN(type) std::numeric_limits<type>::min()
46
47 vector<int> change(vector<int> v)
48 {
49 int n = (int)v.size() / 2;
50 vector<int> cnt(n, 0);
51 for(int i = 0; i < 2*n; i++)
52 if(v[i] > 0)
53 cnt[v[i] - 1]++;
54
55 for(int i = 1; i < n; i++)
56 cnt[i] += cnt[i-1];
57
58 vector<int> cntl = cnt, cntr = cnt;
59
60 for(int i = 0; i < 2*n; i++)
61 if(v[i] > 0)
62 v[i] = (--cntr[v[i] - 1]) + 1;
63 else
64 v[i] = -((--cntl[-v[i] - 1]) + 1);
65 return v;
66 }
67
68
69 long long count_swaps(vector<int> s)
70 {
71 s = change(s);
72 vector<pair<int, bool>> in(SZ(s));
73
74 vector<int> where(SZ(s) / 2, -1);
75
76 for (int i = 0; i != SZ(s); ++i)
77 {
78 in[i].first = abs(s[i]) - 1;
79 in[i].second = (s[i] < 0 ? 0 : 1);
80
81 if (where[in[i].first] == -1)
82 where[in[i].first] = i;
83 }
84
85 vector<int> fenw(SZ(in));
86 for (int i = 0; i != SZ(in); ++i)
87 fenw[i] = 1 + i - (i & (i+1));
88
89 long long ans = 0;
90 vector<char> dead(SZ(in), false);
91
92 for (int pos = SZ(in) - 1; pos >= 0; --pos)
93 if (not dead[pos])
94 {
95 dead[pos] = 1;
96
97 int i = where[in[pos].first];
98 dead[i] = 1;
99
100 int dist = 0;
101 for (int p = pos; p >= 0; p = (p & (p + 1)) - 1)
102 dist += fenw[p];
103
104 for (int p = i; p >= 0; p = (p & (p + 1)) - 1)
105 dist -= fenw[p];
106
107 if (in[pos].second == 1)
108 --dist;
109
110 ans += dist;
111 for (int p = i; p < SZ(fenw); p = p | (p + 1))
112 fenw[p] -= 1;
113 }
114
115
116 return ans;
CAPITOLUL 2. IOI 2019 154

117 }
118
119 int read_int()
120 {
121 int x;
122 if (scanf("%d", &x) != 1)
123 {
124 fprintf(stderr, "Error while reading input\n");
125 exit(1);
126 }
127 return x;
128 }
129
130 int main()
131 {
132 auto t1 = clock();
133
134 std::freopen("../tests/6-15.in", "r", stdin) ;
135 std::freopen("shoes.out", "w", stdout) ;
136
137 int n;
138 //assert(1 == scanf("%d", &n));
139 n = read_int();
140
141 vector<int> S(2 * n);
142 for (int i = 0; i < 2 * n; i++)
143 //assert(1 == scanf("%d", &S[i]));
144 S[i] = read_int();
145
146 fclose(stdin);
147
148 auto t2 = clock();
149
150 long long result = count_swaps(S);
151
152 auto t3 = clock();
153
154
155 printf("%lld\n", result);
156 fclose(stdout);
157
158 auto t4 = clock();
159
160 // reset console output
161 freopen("CON", "w", stdout);
162
163 std::cout <<result<<’\n’<<’\n’;
164 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
165 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
166 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
167
168 return 0;
169 }

Listing 2.1.10: shoes-jonathanirvings+grader.cpp


1 #include "shoes.h"
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 namespace testcaseCheck
8 {
9
10 const int SUBTASK = 6;
11
12 bool permutationsOfShoes(vector<int> S, int N)
13 {
14 vector<int> V;
15 for (int i = 1; i <= N; ++i)
16 {
17 V.push_back(-i);
18 V.push_back(i);
19 }
CAPITOLUL 2. IOI 2019 155

20
21 sort(S.begin(), S.end());
22 sort(V.begin(), V.end());
23 for (int i = 0; i < 2 * N; ++i)
24 {
25 if (S[i] != V[i])
26 {
27 return false;
28 }
29 }
30
31 return true;
32 }
33
34 void run(std::vector<int> S)
35 {
36 assert(1 <= SUBTASK && SUBTASK <= 6);
37
38 assert(S.size() % 2 == 0);
39 int N = S.size() / 2;
40 assert(1 <= N && N <= 100000);
41
42 map<int, int> sizeToOccurences;
43 for (int s : S)
44 {
45 sizeToOccurences[abs(s)] += s / abs(s);
46 }
47 for (pair<int, int> occurences : sizeToOccurences)
48 {
49 assert(occurences.second == 0);
50 }
51
52 if (SUBTASK == 1)
53 {
54 assert(N == 1);
55 }
56
57 if (SUBTASK == 2)
58 {
59 assert(N <= 8);
60 }
61
62 if (SUBTASK == 3)
63 {
64 for (int s : S)
65 {
66 assert(abs(s) == abs(S[0]));
67 }
68 }
69
70 if (SUBTASK == 4)
71 {
72 for (int i = 0; i < N; ++i)
73 {
74 assert(abs(S[i]) == S[i + N]);
75 }
76 }
77
78 if (SUBTASK == 5)
79 {
80 assert(N <= 1000);
81 }
82 }
83 } // namespace testcaseCheck
84
85 struct FenwickTree
86 {
87 vector<int> arr;
88
89 FenwickTree(int n)
90 {
91 arr.resize(n + 1, 0);
92 }
93
94 void update(int x,int y)
95 {
CAPITOLUL 2. IOI 2019 156

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


97 {
98 arr[i] += y;
99 }
100 }
101
102 int query(int x)
103 {
104 int ret = 0;
105 for (int i = x + 1; i > 0; i -= (i & -i))
106 {
107 ret += arr[i];
108 }
109 return ret;
110 }
111 };
112
113 long long count_swaps(std::vector<int> S)
114 {
115 testcaseCheck::run(S);
116
117 long long answer = 0;
118 int N = S.size() / 2;
119
120 FenwickTree fenwickTree(2 * N);
121 for (int i = 0; i < 2 * N; ++i)
122 {
123 fenwickTree.update(i, 1);
124 }
125
126 map<int, vector<int>> positions;
127 for (int i = 2 * N - 1; i >= 0; --i)
128 {
129 positions[S[i]].push_back(i);
130 }
131
132 for (int i = 0; i < 2 * N; ++i)
133 {
134 if (fenwickTree.query(i) == fenwickTree.query(i - 1))
135 {
136 continue;
137 }
138 if (S[i] > 0)
139 {
140 ++answer;
141 }
142 int pos = positions[-S[i]][positions[-S[i]].size() - 1];
143 answer += fenwickTree.query(pos - 1) - fenwickTree.query(i);
144 fenwickTree.update(i, -1);
145 fenwickTree.update(pos, -1);
146
147 positions[S[i]].pop_back();
148 positions[-S[i]].pop_back();
149 }
150
151 return answer;
152 }
153
154 int read_int()
155 {
156 int x;
157 if (scanf("%d", &x) != 1)
158 {
159 fprintf(stderr, "Error while reading input\n");
160 exit(1);
161 }
162 return x;
163 }
164
165 int main()
166 {
167 auto t1 = clock();
168
169 std::freopen("../tests/6-15.in", "r", stdin) ;
170 std::freopen("shoes.out", "w", stdout) ;
171
CAPITOLUL 2. IOI 2019 157

172 int n;
173 //assert(1 == scanf("%d", &n));
174 n = read_int();
175
176 vector<int> S(2 * n);
177 for (int i = 0; i < 2 * n; i++)
178 //assert(1 == scanf("%d", &S[i]));
179 S[i] = read_int();
180
181 fclose(stdin);
182
183 auto t2 = clock();
184
185 long long result = count_swaps(S);
186
187 auto t3 = clock();
188
189 printf("%lld\n", result);
190 fclose(stdout);
191
192 auto t4 = clock();
193
194 // reset console output
195 freopen("CON", "w", stdout);
196
197 std::cout <<result<<’\n’<<’\n’;
198 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
199 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
200 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
201
202 return 0;
203 }

Listing 2.1.11: shoes-kuzey_ok+grader.cpp


1 // --- Ali Ahmadi ali.ahmadi.star27@gmail.com ---
2 // correct solution with fenwick
3
4 #include <bits/stdc++.h>
5 #include "shoes.h"
6
7 #define lng long long
8
9 using namespace std;
10
11 const int maxn = 2002002;
12
13 int fen[maxn];
14 int _pos[maxn];
15 int *pos = _pos + maxn / 2;
16
17 void add(int x)
18 {
19 for (x ++; x < maxn; x += x & -x)
20 fen[x] ++;
21 }
22
23 int get(int x)
24 {
25 int r = 0;
26 for (x ++; x; x -= x & -x)
27 r += fen[x];
28 return r;
29 }
30
31 vector<int> change(vector<int> v)
32 {
33 int n = (int)v.size() / 2;
34 vector<int> cnt(n, 0);
35 for(int i = 0; i < 2*n; i++)
36 if(v[i] > 0)
37 cnt[v[i] - 1]++;
38
39 for(int i = 1; i < n; i++)
40 cnt[i] += cnt[i-1];
CAPITOLUL 2. IOI 2019 158

41
42 vector<int> cntl = cnt, cntr = cnt;
43
44 for(int i = 0; i < 2*n; i++)
45 if(v[i] > 0)
46 v[i] = (--cntr[v[i] - 1]) + 1;
47 else
48 v[i] = -((--cntl[-v[i] - 1]) + 1);
49 return v;
50 }
51
52
53 lng count_swaps(vector<int> S)
54 {
55 S = change(S);
56 memset(fen, 0, sizeof fen);
57 memset(_pos, 0, sizeof _pos);
58
59 lng r = 0;
60 for (int i = 0; i < S.size(); i ++)
61 pos[S[i]] = i;
62
63 for (int i = 0; i < S.size(); i ++)
64 if (S[i])
65 {
66 r += pos[-S[i]] - get(pos[-S[i]]) - (S[i] < 0);
67 add(i); add(pos[-S[i]]); S[pos[-S[i]]] = 0; S[i] = 0;
68 }
69 return r;
70 }
71
72
73 int read_int()
74 {
75 int x;
76 if (scanf("%d", &x) != 1)
77 {
78 fprintf(stderr, "Error while reading input\n");
79 exit(1);
80 }
81 return x;
82 }
83
84 int main()
85 {
86 auto t1 = clock();
87
88 std::freopen("../tests/6-15.in", "r", stdin) ;
89 std::freopen("shoes.out", "w", stdout) ;
90
91 int n;
92 //assert(1 == scanf("%d", &n));
93 n = read_int();
94
95 vector<int> S(2 * n);
96 for (int i = 0; i < 2 * n; i++)
97 //assert(1 == scanf("%d", &S[i]));
98 S[i] = read_int();
99
100 fclose(stdin);
101
102 auto t2 = clock();
103
104 long long result = count_swaps(S);
105
106 auto t3 = clock();
107
108
109 printf("%lld\n", result);
110 fclose(stdout);
111
112 auto t4 = clock();
113
114 // reset console output
115 freopen("CON", "w", stdout);
116
CAPITOLUL 2. IOI 2019 159

117 std::cout <<result<<’\n’<<’\n’;


118 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
119 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
120 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
121
122 return 0;
123 }

2.1.3 *Rezolvare detaliat 

2.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 planic  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  identice mulµimile A, B ³i C , astfel încât cel puµin dou  din cele trei
mulµimi s  e 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 ecare 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 ecare 0 & i & n  1, si
trebuie s  e 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 2. IOI 2019 160

Figura 2.2: Split1

O posibil  soluµie corect  este 1, 1, 1, 1, 2, 2, 3, 3, 3. Aceast  soluµie descrie urm toarea îm-
p 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 2.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 a³eaz  pe o singur  linie vectorul returnat de find_split.
Timp maxim de executare/test: 2.0 secunde
Memorie: total 1024 MB
CAPITOLUL 2. IOI 2019 161

2.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 nd some arbitrary spanning tree and nd 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 nal 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.

2.2.2 Coduri surs 


CAPITOLUL 2. IOI 2019 162

Listing 2.2.1: compile_cpp.sh


1 #!/bin/bash
2
3 problem=split
4
5 g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"
6
7 "grader.cpp" "${problem}.cpp"

Listing 2.2.2: split.h


1 #include <vector>
2
3 std::vector<int> find_split(int n, int a, int b, int c, std::vector<int> p,
4 std::vector<int> q);

Listing 2.2.3: split.cpp


1 #include "split.h"
2
3 using namespace std;
4
5 vector<int> find_split(int n, int a, int b, int c, vector<int> p, vector<int> q)
6 {
7 vector<int> res;
8 if (n == 9)
9 {
10 res = {1, 1, 3, 1, 2, 2, 3, 1, 3};
11 }
12 else
13 {
14 res = {0, 0, 0, 0, 0, 0};
15 }
16 return res;
17 }

Listing 2.2.4: grader.cpp


1 #include "split.h"
2 #include <cstdio>
3 #include <cassert>
4
5 using namespace std;
6
7 int main()
8 {
9 int n, m, a, b, c;
10
11 assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
12
13 vector<int> p(m), q(m);
14 for (int i=0; i<m; i++)
15 assert(2 == scanf("%d%d", &p[i], &q[i]));
16 fclose(stdin);
17
18 vector<int> result = find_split(n, a, b, c, p, q);
19
20 for (int i=0; i<(int)result.size(); i++)
21 printf("%s%d", ((i>0)?" ":""), result[i]);
22 printf("\n");
23 fclose(stdout);
24
25 return 0;
26 }

Listing 2.2.5: split-model.cpp


1 #include "bits/stdc++.h"
2 #include "split.h"
3
4 using namespace std;
CAPITOLUL 2. IOI 2019 163

5
6 vector <int> find_split(int n, int a, int b, int c, vector <int> p,
7 vector <int> q)
8 {
9 vector <pair <int, int> > edges;
10
11 for(int i=0; i<(int)p.size(); i++) edges.emplace_back(p[i], q[i]);
12
13 vector <pair <int, int> > sizes = {{a,0}, {b,1}, {c,2}};
14
15 sort(sizes.begin(), sizes.end());
16 a = sizes[0].first; b = sizes[1].first; c = sizes[2].first;
17
18 vector <int> rep(n), siz(n);
19
20 for(int i=0; i<n; i++)
21 {
22 rep[i] = i;
23 siz[i] = 1;
24 }
25
26 function <int(int)> find = [&](int x)
27 {
28 if(rep[x] == x) return x;
29 return rep[x] = find(rep[x]);
30 };
31
32 auto unia = [&](int aa, int bb) -> bool
33 {
34 int fa = find(aa), fb = find(bb);
35 if(fa == fb) return false;
36 if(siz[fa] < siz[fb]) swap(fa, fb);
37 siz[fa] += siz[fb];
38 rep[fb] = fa;
39 return true;
40 };
41
42 vector <vector <int> > G(n);
43 vector <pair <int, int> > non_tree;
44
45 for(auto edge : edges)
46 {
47 if(unia(edge.first, edge.second))
48 {
49 G[edge.first].push_back(edge.second);
50 G[edge.second].push_back(edge.first);
51 }
52 else
53 {
54 non_tree.push_back(edge);
55 }
56 }
57
58 vector <bool> vis(n);
59 vector <int> subtree(n);
60
61 function<void(int)> dfs1 = [&](int v)
62 {
63 vis[v] = true;
64 subtree[v] = 1;
65 for(auto w : G[v]) if(!vis[w])
66 {
67 dfs1(w);
68 subtree[v] += subtree[w];
69 }
70 };
71
72 dfs1(0);
73
74 vis = vector<bool>(n, false);
75 pair <int, int> both_dir = {-1, -1};
76 vector <pair <int, int> > dir;
77
78 function<void(int)> dfs2 = [&](int v)
79 {
80 vis[v] = true;
CAPITOLUL 2. IOI 2019 164

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


82 {
83 int below = subtree[w], above = n - subtree[w];
84 if(min(below, above) >= a)
85 {
86 if(above < below) both_dir = {v, w};
87 else both_dir = {w, v};
88 }
89 else
90 {
91 if(below < a) dir.emplace_back(v, w);
92 else if(above < a) dir.emplace_back(w, v);
93 }
94 dfs2(w);
95 }
96 };
97
98 dfs2(0);
99
100 auto find_solution = [&](int pa, int pb) -> vector<int>
101 {
102 vector <int> ret(n, -1);
103 int counter;
104
105 function<void(int, int)> dfs3 = [&](int v, int mark)
106 {
107 if(counter == 0) return;
108 --counter;
109 ret[v] = mark;
110 for(auto w : G[v]) if(ret[w] == -1) dfs3(w, mark);
111 };
112
113 ret[pa] = sizes[0].second;
114 ret[pb] = sizes[a].second;
115 counter = a;
116 dfs3(pa, sizes[0].second);
117 vis[pb] = sizes[1].second;
118 counter = b;
119 dfs3(pb, sizes[1].second);
120 vis[pa] = false;
121 for(int i=0; i<n; i++) if(ret[i] == -1) ret[i] = sizes[2].second;
122 for(int i=0; i<n; i++) ret[i]++;
123 return ret;
124 };
125
126 if(both_dir.first != -1)
127 {
128 return find_solution(both_dir.first, both_dir.second);
129 }
130 else
131 {
132 vector <int> in_deg(n);
133 for(auto edge : dir) in_deg[edge.second]++;
134 vector <int> CH;
135 for(int i=0; i<n; i++) if(in_deg[i] == 0) CH.push_back(i);
136 assert((int)CH.size() == 1);
137 vis = vector<bool>(n, false);
138 subtree = vector<int>(n, 0);
139 int root = CH.back();
140 for(auto edge : non_tree)
141 {
142 G[edge.first].push_back(edge.second);
143 G[edge.second].push_back(edge.first);
144 }
145 dfs1(root);
146 for(auto son : G[root])
147 {
148 if(subtree[son] >= a) return find_solution(son, root);
149 }
150 return vector<int>(n, 0);
151 }
152 }

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


CAPITOLUL 2. IOI 2019 165

1 #include "bits/stdc++.h"
2 #include "split.h"
3
4 #include <time.h> /* clock */
5 #include <stdio.h>
6
7 using namespace std;
8
9 vector <int> find_split(int n, int a, int b, int c,
10 vector <int> p, vector <int> q)
11 {
12 vector <pair <int, int> > edges;
13
14 for(int i=0; i<(int)p.size(); i++)
15 edges.emplace_back(p[i], q[i]);
16
17 vector <pair <int, int> > sizes = {{a,0}, {b,1}, {c,2}};
18
19 sort(sizes.begin(), sizes.end());
20
21 a = sizes[0].first;
22 b = sizes[1].first;
23 c = sizes[2].first;
24
25 vector <int> rep(n), siz(n);
26
27 for(int i=0; i<n; i++)
28 {
29 rep[i] = i;
30 siz[i] = 1;
31 }
32
33 function <int(int)> find = [&](int x)
34 {
35 if(rep[x] == x) return x;
36 return rep[x] = find(rep[x]);
37 };
38
39 auto unia = [&](int aa, int bb) -> bool
40 {
41 int fa = find(aa), fb = find(bb);
42 if(fa == fb) return false;
43 if(siz[fa] < siz[fb]) swap(fa, fb);
44 siz[fa] += siz[fb];
45 rep[fb] = fa;
46 return true;
47 };
48
49 vector <vector <int> > G(n);
50 vector <pair <int, int> > non_tree;
51
52 for(auto edge : edges)
53 {
54 if(unia(edge.first, edge.second))
55 {
56 G[edge.first].push_back(edge.second);
57 G[edge.second].push_back(edge.first);
58 }
59 else
60 {
61 non_tree.push_back(edge);
62 }
63 }
64
65 vector <bool> vis(n);
66 vector <int> subtree(n);
67
68 function<void(int)> dfs1 = [&](int v)
69 {
70 vis[v] = true;
71 subtree[v] = 1;
72 for(auto w : G[v]) if(!vis[w])
73 {
74 dfs1(w);
75 subtree[v] += subtree[w];
76 }
CAPITOLUL 2. IOI 2019 166

77
78 };
79
80 dfs1(0);
81
82 vis = vector<bool>(n, false);
83 pair <int, int> both_dir = {-1, -1};
84 vector <pair <int, int> > dir;
85
86 function<void(int)> dfs2 = [&](int v)
87 {
88 vis[v] = true;
89 for(auto w : G[v]) if(!vis[w])
90 {
91 int below = subtree[w], above = n - subtree[w];
92 if(min(below, above) >= a)
93 {
94 if(above < below) both_dir = {v, w};
95 else both_dir = {w, v};
96 }
97 else
98 {
99 if(below < a) dir.emplace_back(v, w);
100 else if(above < a) dir.emplace_back(w, v);
101 }
102 dfs2(w);
103 }
104 };
105
106 dfs2(0);
107
108 auto find_solution = [&](int pa, int pb) -> vector<int>
109 {
110 vector <int> ret(n, -1);
111 int counter;
112
113 function<void(int, int)> dfs3 = [&](int v, int mark)
114 {
115 if(counter == 0) return;
116 --counter;
117 ret[v] = mark;
118 for(auto w : G[v]) if(ret[w] == -1) dfs3(w, mark);
119 };
120
121 ret[pa] = sizes[0].second;
122 ret[pb] = sizes[a].second;
123
124 counter = a;
125 dfs3(pa, sizes[0].second);
126 vis[pb] = sizes[1].second;
127
128 counter = b;
129 dfs3(pb, sizes[1].second);
130 vis[pa] = false;
131
132 for(int i=0; i<n; i++)
133 if(ret[i] == -1) ret[i] = sizes[2].second;
134 for(int i=0; i<n; i++)
135 ret[i]++;
136
137 return ret;
138 };
139
140 if(both_dir.first != -1)
141 {
142 return find_solution(both_dir.first, both_dir.second);
143 }
144 else
145 {
146 vector <int> in_deg(n);
147 for(auto edge : dir) in_deg[edge.second]++;
148 vector <int> CH;
149 for(int i=0; i<n; i++) if(in_deg[i] == 0) CH.push_back(i);
150 assert((int)CH.size() == 1);
151 vis = vector<bool>(n, false);
152 subtree = vector<int>(n, 0);
CAPITOLUL 2. IOI 2019 167

153 int root = CH.back();


154 for(auto edge : non_tree)
155 {
156 G[edge.first].push_back(edge.second);
157 G[edge.second].push_back(edge.first);
158 }
159
160 dfs1(root);
161
162 for(auto son : G[root])
163 {
164 if(subtree[son] >= a) return find_solution(son, root);
165 }
166
167 return vector<int>(n, 0);
168 }
169 }
170
171 namespace
172 {
173
174 int read_int()
175 {
176 int x;
177 if (scanf("%d", &x) != 1)
178 {
179 fprintf(stderr, "Error while reading input\n");
180 exit(1);
181 }
182 return x;
183 }
184 } // namespace
185
186 int main()
187 {
188 auto t1 = clock();
189
190 //std::freopen("../tests/1-03.in", "r", stdin) ;
191 std::freopen("../tests/5-34.in", "r", stdin) ; // execution time : 1.141 s
192
193 std::freopen("split.out", "w", stdout) ;
194
195 int n, m, a, b, c;
196 //assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
197 n = read_int();
198 m = read_int();
199 a = read_int();
200 b = read_int();
201 c = read_int();
202
203 vector<int> p(m), q(m);
204 for (int i=0; i<m; i++)
205 //assert(2 == scanf("%d%d", &p[i], &q[i]));
206 p[i] = read_int(), q[i] = read_int();
207
208 fclose(stdin);
209
210 auto t2 = clock();
211
212 vector<int> result = find_split(n, a, b, c, p, q);
213
214 auto t3 = clock();
215
216 for (int i=0; i<(int)result.size(); i++)
217 printf("%s%d", ((i>0)?" ":""), result[i]);
218 printf("\n");
219 fclose(stdout);
220
221 auto t4 = clock();
222
223 // reset console output
224 freopen("CON", "w", stdout);
225
226 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
227 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
228 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 2. IOI 2019 168

229
230 return 0;
231 }

Listing 2.2.7: split 144514+grader.cpp


1 // https://oj.uz/submission/144514
2
3 #include "../split.h"
4 #include<bits/stdc++.h>
5 using namespace std;
6 const int mn=2e5+10;
7 int p[mn],si[mn];
8
9 inline int f(int x)
10 {
11 return x==p[x]?x:(p[x]=f(p[x]));
12 }
13
14 inline int siz(int x)
15 {
16 return si[f(x)];
17 }
18
19 void mrg(int a,int b)
20 {
21 a=f(a),b=f(b);
22 if(a==b);
23 else
24 if(si[a]>si[b])
25 si[a]+=si[b],p[b]=a;
26 else
27 si[b]+=si[a],p[a]=b;
28 }
29
30 void init()
31 {
32 iota(p,p+mn,0);
33 fill(si,si+mn,1);
34 }
35
36 bool u[mn];
37 vector<int>g[mn],ans;
38 int s[mn];
39 int ctr,n;
40
41 void dfs(int x,int p)
42 {
43 s[x]=1;
44 for(int y:g[x])
45 {
46 if(y==p)continue;
47 dfs(y,x);
48 s[x]+=s[y];
49 }
50 }
51
52 int fc(int x,int p)
53 {
54 for(int y:g[x])
55 {
56 if(y==p )continue;
57 if(s[y]*2>=n) return fc(y,x);
58 }
59 return x;
60 }
61
62 void dfs2(int x,int p)
63 {
64 for(int y:g[x])
65 {
66 if(y==p)continue;
67 dfs2(y,x);
68 mrg(x,y);
69 }
CAPITOLUL 2. IOI 2019 169

70 }
71
72 int lef,tar;
73 bool vis[mn];
74 int conv[4];
75
76 void dfs3(int x)
77 {
78 if(!lef)return;
79 vis[x]=1;
80 lef--;
81 ans[x]=conv[tar];
82 for(int y:g[x])
83 {
84 if(!vis[y])
85 {
86 dfs3(y);
87 if(!lef)return;
88 }
89 }
90 }
91
92 vector<int> find_split(int N, int a, int b, int c,
93 vector<int> p, vector<int> q)
94 {
95 int sm=min(a,min(b,c)),bi=max(a,max(b,c));
96
97 if(a==sm)
98 conv[1]=1;
99 else
100 if(a==bi)
101 conv[3]=1;
102 else
103 conv[2]=1;
104
105 if(b==sm&&!conv[1]) conv[1]=2;
106 else
107 if(b==bi&&!conv[3])conv[3]=2;
108 else conv[2]=2;
109
110 if(c==sm&&!conv[1])conv[1]=3;
111 else
112 if(c==bi&&!conv[3])conv[3]=3;
113 else conv[2]=3;
114
115 b=a+b+c-sm-bi;
116 a=sm;
117 c=bi;
118 n=N;
119 ans.resize(n,conv[3]);
120 int m=p.size(),i;
121
122 init();
123 for(i=0;i<m;i++)
124 {
125 if(f(p[i])!=f(q[i]))
126 {
127 mrg(p[i],q[i]);
128 g[p[i]].push_back(q[i]);
129 g[q[i]].push_back(p[i]);
130 }
131 }
132
133 dfs(0,-1);
134
135 ctr=fc(0,-1);
136 init();
137 for(int y:g[ctr])
138 {
139 dfs2(y,ctr);
140 if(siz(y)>=a)
141 {
142 vis[ctr]=1;
143 lef=a;
144 tar=1;
145 dfs3(y);
CAPITOLUL 2. IOI 2019 170

146 lef=b;
147 tar=2;
148 vis[ctr]=0;
149 dfs3(ctr);
150 return ans;
151 }
152 }
153
154 for(i=0;i<m;i++)
155 {
156 if(p[i]==ctr||q[i]==ctr)continue;
157 if(f(p[i])==f(q[i]))continue;
158 mrg(p[i],q[i]);
159 g[p[i]].push_back(q[i]);
160 g[q[i]].push_back(p[i]);
161 if(siz(p[i])>=a)
162 {
163 vis[ctr]=1;
164 lef=a;
165 tar=1;
166 dfs3(p[i]);
167 lef=b;
168 tar=2;
169 vis[ctr]=0;
170 dfs3(ctr);
171 return ans;
172 }
173 }
174
175 fill(ans.begin(),ans.end(),0);
176 return ans;
177 }
178
179 int read_int()
180 {
181 int x;
182 if (scanf("%d", &x) != 1)
183 {
184 fprintf(stderr, "Error while reading input\n");
185 exit(1);
186 }
187 return x;
188 }
189
190 int main()
191 {
192 auto t1 = clock();
193
194 //std::freopen("../tests/1-03.in", "r", stdin) ;
195 std::freopen("../tests/5-34.in", "r", stdin) ;
196
197 std::freopen("split.out", "w", stdout) ;
198
199 int n, m, a, b, c;
200 //assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
201 n = read_int();
202 m = read_int();
203 a = read_int();
204 b = read_int();
205 c = read_int();
206
207 vector<int> p(m), q(m);
208 for (int i=0; i<m; i++)
209 //assert(2 == scanf("%d%d", &p[i], &q[i]));
210 p[i] = read_int(), q[i] = read_int();
211
212 fclose(stdin);
213
214 auto t2 = clock();
215
216 vector<int> result = find_split(n, a, b, c, p, q);
217
218 auto t3 = clock();
219
220 for (int i=0; i<(int)result.size(); i++)
221 printf("%s%d", ((i>0)?" ":""), result[i]);
CAPITOLUL 2. IOI 2019 171

222 printf("\n");
223 fclose(stdout);
224
225 auto t4 = clock();
226
227 // reset console output
228 freopen("CON", "w", stdout);
229
230 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
231 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
232 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
233
234 return 0;
235 }

Listing 2.2.8: split_koosaga+grader.cpp


1 // https://github.com/koosaga/olympiad/blob/master/IOI/ioi19_split.cpp
2 #include "../split.h"
3 #include <bits/stdc++.h>
4
5 #define sz(v) ((int)(v).size())
6
7 using namespace std;
8
9 using pi = pair<int, int>;
10 const int MAXN = 100005;
11
12 int n;
13 vector<int> gph[MAXN];
14 vector<int> tr[MAXN];
15
16 namespace report
17 {
18 vector<int> gph[MAXN];
19 int mark[MAXN], vis[MAXN];
20 void dfs(int x, vector<int> &dfn)
21 {
22 dfn.push_back(x);
23 vis[x] = 1;
24 for(auto &i : gph[x])
25 {
26 if(mark[x] == mark[i] && !vis[i])
27 {
28 dfs(i, dfn);
29 }
30 }
31 }
32
33 vector<pi> color;
34 vector<int> Do(vector<int> S)
35 {
36 for(auto &i : S) mark[i] = 1;
37 vector<int> ans(n, color[2].second);
38 for(int i=0; i<n; i++)
39 {
40 vector<int> dfn;
41 if(mark[i] == 1 && !vis[i])
42 {
43 dfs(i, dfn);
44 for(int j=0; j<color[0].first; j++)
45 {
46 ans[dfn[j]] = color[0].second;
47 }
48 }
49
50 if(mark[i] == 0 && !vis[i])
51 {
52 dfs(i, dfn);
53 for(int j=0; j<color[1].first; j++)
54 {
55 ans[dfn[j]] = color[1].second;
56 }
57 }
58 }
CAPITOLUL 2. IOI 2019 172

59
60 return ans;
61 }
62 }
63
64 struct disj
65 {
66 int pa[MAXN], sz[MAXN];
67 void init(int n)
68 {
69 iota(pa, pa + n + 1, 0);
70 fill(sz, sz + n + 1, 1);
71 }
72
73 int find(int x)
74 {
75 return pa[x] = (pa[x] == x ? x : find(pa[x]));
76 }
77
78 int getsz(int x)
79 {
80 return sz[find(x)];
81 }
82
83 bool uni(int p, int q)
84 {
85 p = find(p); q = find(q);
86 if(p == q) return 0;
87 sz[p] += sz[q];
88 pa[q] = p;
89 return 1;
90 }
91 } disj;
92
93 int sz[MAXN], msz[MAXN];
94
95 void dfsc(int x, int p)
96 {
97 sz[x] = 1; msz[x] = 0;
98 for(auto &i : tr[x])
99 {
100 if(i != p)
101 {
102 dfsc(i, x);
103 sz[x] += sz[i];
104 msz[x] = max(msz[x], sz[i]);
105 }
106 }
107 }
108
109 int get_center()
110 {
111 dfsc(0, 0);
112 pi ret(1e9, 1e9);
113 for(int i=0; i<n; i++)
114 {
115 ret = min(ret, pi(max(msz[i], n - sz[i]), i));
116 }
117 return ret.second;
118 }
119
120 void dfs(int x, int p, vector<int> &dfn)
121 {
122 dfn.push_back(x);
123 for(auto &i : tr[x])
124 {
125 if(i != p)
126 {
127 disj.uni(x, i);
128 dfs(i, x, dfn);
129 }
130 }
131 }
132
133 bool vis[MAXN];
134
CAPITOLUL 2. IOI 2019 173

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


136 {
137 dfn.push_back(x);
138 vis[x] = 1;
139 for(auto &i : gph[x])
140 {
141 if(!vis[i]) dfsa(i, dfn);
142 }
143 }
144
145 vector<int> find_split(int _n, int a, int b, int _c,
146 vector<int> p, vector<int> q)
147 {
148 n = _n;
149 vector<pi> cs = {pi(a, 1), pi(b, 2), pi(_c, 3)};
150
151 sort(cs.begin(), cs.end());
152
153 report::color = cs;
154 a = cs[0].first;
155 disj.init(n);
156 for(int i=0; i<sz(p); i++)
157 {
158 if(disj.uni(p[i], q[i]))
159 {
160 tr[p[i]].push_back(q[i]);
161 tr[q[i]].push_back(p[i]);
162 }
163 report::gph[p[i]].push_back(q[i]);
164 report::gph[q[i]].push_back(p[i]);
165 }
166
167 disj.init(n);
168 int c = get_center();
169 for(auto &i : tr[c])
170 {
171 vector<int> dfn;
172 dfs(i, c, dfn);
173 if(disj.getsz(i) >= a)
174 {
175 return report::Do(dfn);
176 }
177 }
178
179 for(int i=0; i<sz(p); i++)
180 {
181 if(p[i] == c || q[i] == c) continue;
182 int l = disj.find(p[i]);
183 int r = disj.find(q[i]);
184 if(l != r)
185 {
186 gph[l].push_back(r);
187 gph[r].push_back(l);
188 }
189 }
190
191 for(int i=0; i<n; i++)
192 {
193 if(i != c && disj.find(i) == i && !vis[i])
194 {
195 vector<int> dfn;
196 dfsa(i, dfn);
197 int sum = 0;
198 set<int> pot;
199 for(auto &i : dfn)
200 {
201 sum += disj.getsz(i);
202 pot.insert(i);
203 if(sum >= a) break;
204 }
205
206 if(sum >= a)
207 {
208 vector<int> ans;
209 for(int i=0; i<n; i++)
210 {
CAPITOLUL 2. IOI 2019 174

211 if(pot.find(disj.find(i)) != pot.end())


212 {
213 ans.push_back(i);
214 }
215 }
216 return report::Do(ans);
217 }
218 }
219 }
220
221 return vector<int>(n, 0);
222 }
223
224 int read_int()
225 {
226 int x;
227 if (scanf("%d", &x) != 1)
228 {
229 fprintf(stderr, "Error while reading input\n");
230 exit(1);
231 }
232 return x;
233 }
234
235 int main()
236 {
237 auto t1 = clock();
238
239 //std::freopen("../tests/1-03.in", "r", stdin) ;
240 std::freopen("../tests/5-34.in", "r", stdin) ;
241
242 std::freopen("split.out", "w", stdout) ;
243
244 int n, m, a, b, c;
245 //assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
246 n = read_int();
247 m = read_int();
248 a = read_int();
249 b = read_int();
250 c = read_int();
251
252 vector<int> p(m), q(m);
253 for (int i=0; i<m; i++)
254 //assert(2 == scanf("%d%d", &p[i], &q[i]));
255 p[i] = read_int(), q[i] = read_int();
256
257 fclose(stdin);
258
259 auto t2 = clock();
260
261 vector<int> result = find_split(n, a, b, c, p, q);
262
263 auto t3 = clock();
264
265 for (int i=0; i<(int)result.size(); i++)
266 printf("%s%d", ((i>0)?" ":""), result[i]);
267 printf("\n");
268 fclose(stdout);
269
270 auto t4 = clock();
271
272 // reset console output
273 freopen("CON", "w", stdout);
274
275 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
276 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
277 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
278
279 return 0;
280 }

Listing 2.2.9: split-mahdi+grader.cpp


1 //IOI 2019 execution time : 0.891 s
2 //Split - Version 1.1 - First Solution
CAPITOLUL 2. IOI 2019 175

3 //Current Version: 9 May 2019


4 //Older version: 31 March 2019
5 //Mahdi Safarnejad Boroujeni
6
7 #define forn(i, n) for (int i = 0; i < int(n); i++)
8
9 #include "../split.h"
10
11 #include <vector>
12 #include <algorithm>
13 #include<iostream>
14 #include<ctime>
15
16 using namespace std;
17
18 const int maxn = 1000000+110;
19 typedef pair<int,int> intpair;
20
21 int n;
22 vector<int> a[maxn];
23 vector<int> result(maxn);
24 intpair b[3];
25
26 bool finishedPhase1 = false;
27
28 int mark[maxn], sizev[maxn];
29 int counter=1, startingtime[maxn], mintime[maxn];
30
31 int dfs2(int v, int goal, int comp, bool ignore_st=false)
32 {
33 int sum=1;
34 mark[v]=2;
35 result[v]=comp;
36 goal -= 1;
37 forn(i, a[v].size())
38 if (goal > 0 && mark[a[v][i]] < 2 &&
39 (ignore_st || (startingtime[a[v][i]] > startingtime[v])))
40 {
41 int thisSize = dfs2(a[v][i], goal, comp, ignore_st);
42 sum += thisSize;
43 goal -= thisSize;
44 }
45 return sum;
46 }
47
48 void dfs(int v, int par)
49 {
50 mark[v]=1;
51 sizev[v]=1;
52 startingtime[v] = counter++;
53 mintime[v] = startingtime[v];
54 int removablesSum=0;
55
56 forn(i, a[v].size())
57 if (!mark[a[v][i]])
58 {
59 dfs(a[v][i], v);
60 if (finishedPhase1)
61 return;
62 sizev[v]+=sizev[a[v][i]];
63 mintime[v] = min(mintime[v], mintime[a[v][i]]);
64 if (mintime[a[v][i]] < startingtime[v])
65 removablesSum += sizev[a[v][i]];
66 }
67 else
68 if (a[v][i]!=par)
69 {
70 mintime[v] = min(mintime[v], mintime[a[v][i]]);
71 }
72
73 if (sizev[v] >= b[0].first)
74 {
75 finishedPhase1 = true;
76 if (n - sizev[v] + removablesSum < b[0].first)
77 return; //No Solution
78
CAPITOLUL 2. IOI 2019 176

79 int element = 0;
80 if (n - sizev[v] + removablesSum < b[1].first)
81 element = 1;
82
83 result[v] = b[element].second;
84 mark[v] = 2;
85 int goal = b[element].first - 1;
86 forn(i, a[v].size())
87 {
88 if (mark[a[v][i]] < 2 &&
89 goal > 0 &&
90 mintime[a[v][i]] >= startingtime[v] &&
91 startingtime[a[v][i]] > startingtime[v])
92 goal -= dfs2(a[v][i], goal, b[element].second);
93 }
94
95 forn(i, a[v].size())
96 {
97 if (mark[a[v][i]] < 2 &&
98 goal > 0 &&
99 mintime[a[v][i]] < startingtime[v] &&
100 startingtime[a[v][i]] > startingtime[v])
101 goal -= dfs2(a[v][i], goal, b[element].second);
102 }
103
104 dfs2(0, b[1-element].first, b[1-element].second, true);
105 forn(i, n)
106 if (result[i]==0)
107 result[i]=b[2].second;
108 }
109 }
110
111 vector <int> find_split(int n_, int a_, int b_, int c_,
112 vector <int> p, vector <int> q)
113 {
114 n = n_;
115 b[0]=intpair(a_, 1);
116 b[1]=intpair(b_, 2);
117 b[2]=intpair(c_, 3);
118
119 sort(b, b+3);
120
121 forn(i, p.size())
122 {
123 a[p[i]].push_back(q[i]);
124 a[q[i]].push_back(p[i]);
125 }
126
127 dfs(0, -1);
128
129 result.resize(n);
130 return result;
131 }
132
133 int read_int()
134 {
135 int x;
136 if (scanf("%d", &x) != 1)
137 {
138 fprintf(stderr, "Error while reading input\n");
139 exit(1);
140 }
141 return x;
142 }
143
144 int main()
145 {
146 auto t1 = clock();
147
148 //std::freopen("../tests/1-03.in", "r", stdin) ;
149 std::freopen("../tests/5-34.in", "r", stdin) ;
150
151 std::freopen("split.out", "w", stdout) ;
152
153 int n, m, a, b, c;
154 //assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
CAPITOLUL 2. IOI 2019 177

155 n = read_int();
156 m = read_int();
157 a = read_int();
158 b = read_int();
159 c = read_int();
160
161 vector<int> p(m), q(m);
162 for (int i=0; i<m; i++)
163 //assert(2 == scanf("%d%d", &p[i], &q[i]));
164 p[i] = read_int(), q[i] = read_int();
165
166 fclose(stdin);
167
168 auto t2 = clock();
169
170 vector<int> result = find_split(n, a, b, c, p, q);
171
172 auto t3 = clock();
173
174 for (int i=0; i<(int)result.size(); i++)
175 printf("%s%d", ((i>0)?" ":""), result[i]);
176 printf("\n");
177 fclose(stdout);
178
179 auto t4 = clock();
180
181 // reset console output
182 freopen("CON", "w", stdout);
183
184 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
185 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
186 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
187
188 return 0;
189 }

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


1 #include "../split.h"
2 #include <bits/stdc++.h>
3
4 using namespace std;
5
6 using ll=long long;
7
8 #define rng(i,a,b) for(int i=int(a);i<int(b);i++)
9 #define rep(i,b) rng(i,0,b)
10 #define gnr(i,a,b) for(int i=int(b)-1;i>=a;i--)
11 #define per(i,b) gnr(i,0,b)
12 #define pb push_back
13 #define eb emplace_back
14 #define a first
15 #define b second
16 #define bg begin()
17 #define ed end()
18 #define all(x) x.bg,x.ed
19 #ifdef LOCAL
20 #define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
21 #else
22 #define dmp(x) void(0)
23 #endif
24
25 template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
26 template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}
27
28 template<class t> using vc=vector<t>;
29 template<class t> using vvc=vc<vc<t>>;
30
31 using pi=pair<int,int>;
32 using vi=vc<int>;
33
34 template<class t,class u>
35 ostream& operator<<(ostream& os,const pair<t,u>& p)
36 {
37 return os<<"{"<<p.a<<","<<p.b<<"}";
CAPITOLUL 2. IOI 2019 178

38 }
39
40 template<class t> ostream& operator<<(ostream& os,const vc<t>& v)
41 {
42 os<<"{";
43 for(auto e:v)os<<e<<",";
44 return os<<"}";
45 }
46
47 const int nmmax=200010;
48
49 vc<pi> g[nmmax];
50 int ans[nmmax];
51 bool us[nmmax];
52
53 void puti(int v,int i,int&rem)
54 {
55 if(ans[v]||rem==0)return;
56 ans[v]=i;
57 rem--;
58 for(auto e:g[v])if(us[e.b])
59 puti(e.a,i,rem);
60 }
61
62 bool vis[nmmax];
63 //find any spanning tree
64 void dfs1(int v,int pe)
65 {
66 if(vis[v])return;
67 vis[v]=1;
68 if(pe!=-1)
69 us[pe]=1;
70 for(auto e:g[v])
71 dfs1(e.a,e.b);
72 }
73
74 //find a centroid
75 int dfs2(int v,int p,int n)
76 {
77 int s=1,mx=0;
78 for(auto e:g[v])if(us[e.b]&&p!=e.a)
79 {
80 int f=dfs2(e.a,v,n);
81 if(f<=0)return f;
82 chmax(mx,f);
83 s+=f;
84 }
85 chmax(mx,n-s);
86 if(mx*2<=n)return -v;
87 return s;
88 }
89
90 void dfs3(int v,int p,vi&w)
91 {
92 w.pb(v);
93 for(auto e:g[v])if(us[e.b]&&p!=e.a)
94 dfs3(e.a,v,w);
95 }
96
97 vector<int> find_split(int n, int a, int b, int c,
98 vector<int> p, vector<int> q)
99 {
100 vc<pi> si{pi(a,1),pi(b,2),pi(c,3)};
101 sort(all(si));
102
103 int m=p.size();
104 rep(i,m)
105 {
106 g[p[i]].eb(q[i],i);
107 g[q[i]].eb(p[i],i);
108 }
109
110 dfs1(0,-1);
111
112 int r=-dfs2(0,-1,n);
113
CAPITOLUL 2. IOI 2019 179

114 vvc<int> cmps;


115 for(auto e:g[r])if(us[e.b])
116 {
117 vi w;
118 dfs3(e.a,r,w);
119 cmps.pb(w);
120 }
121
122 bool fd=false;
123 for(auto w:cmps)
124 {
125 if(int(w.size())>=si[0].a)
126 {
127 ans[r]=-1;
128 puti(w[0],si[0].b,si[0].a);
129 ans[r]=0;
130 puti(r,si[1].b,si[1].a);
131 fd=true;
132 break;
133 }
134 }
135
136 if(!fd)
137 {
138 vi idx(n,-1);
139 rep(i,cmps.size())
140 for(auto v:cmps[i])
141 idx[v]=i;
142
143 int z=idx[0];
144 if(z==-1) return vi(n,0);
145
146 int cur=cmps[z].size();
147 vi ad(cmps.size());
148 ad[z]=1;
149 rep(i,m)
150 {
151 int x=idx[p[i]],y=idx[q[i]];
152 if(y==z)swap(x,y);
153
154 if(x==z&&y!=-1&&!ad[y])
155 {
156 ad[y]=1;
157 cur+=cmps[y].size();
158 us[i]=1;
159 if(cur>=si[0].a)
160 {
161 ans[r]=-1;
162 puti(0,si[0].b,si[0].a);
163 ans[r]=0;
164 puti(r,si[1].b,si[1].a);
165 fd=true;
166 break;
167 }
168 }
169 }
170 }
171
172 if(!fd)return vi(n,0);
173
174 assert(si[0].a==0);
175 assert(si[1].a==0);
176 rep(i,n)
177 if(ans[i]==0)
178 ans[i]=si[2].b;
179 return vi(ans,ans+n);
180 }
181
182 int read_int()
183 {
184 int x;
185 if (scanf("%d", &x) != 1)
186 {
187 fprintf(stderr, "Error while reading input\n");
188 exit(1);
189 }
CAPITOLUL 2. IOI 2019 180

190 return x;
191 }
192
193 int main()
194 {
195 auto t1 = clock();
196
197 //std::freopen("../tests/1-03.in", "r", stdin) ;
198 std::freopen("../tests/5-34.in", "r", stdin) ;
199
200 std::freopen("split.out", "w", stdout) ;
201
202 int n, m, a, b, c;
203 //assert(5 == scanf("%d%d%d%d%d", &n, &m, &a, &b, &c));
204 n = read_int();
205 m = read_int();
206 a = read_int();
207 b = read_int();
208 c = read_int();
209
210 vector<int> p(m), q(m);
211 for (int i=0; i<m; i++)
212 //assert(2 == scanf("%d%d", &p[i], &q[i]));
213 p[i] = read_int(), q[i] = read_int();
214
215 fclose(stdin);
216
217 auto t2 = clock();
218
219 vector<int> result = find_split(n, a, b, c, p, q);
220
221 auto t3 = clock();
222
223 for (int i=0; i<(int)result.size(); i++)
224 printf("%s%d", ((i>0)?" ":""), result[i]);
225 printf("\n");
226 fclose(stdout);
227
228 auto t4 = clock();
229
230 // reset console output
231 freopen("CON", "w", stdout);
232
233 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
234 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
235 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
236
237 return 0;
238 }

2.2.3 *Rezolvare detaliat 

2.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  aat  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 deni o zon  ce
va conµine toate celulele i, j  cu proprietatea c  r1 & i & r2 ³i c1 & j & c2 .
CAPITOLUL 2. IOI 2019 181

În plus, o zon  este considerat  valid  dac  ³i numai dac  pentru ecare 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  e 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  e stabilit palatul (num rul de moduri în care se pot alege valorile r1 , r2 , c1 ³i c2 care denesc
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 2.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 2. IOI 2019 182

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 a³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

2.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 nding 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
25
structure libraries such as STL maps .
25
time-consuming
CAPITOLUL 2. IOI 2019 183

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 dene a structure for a valid rectangle. At most 2 16 structures exist
and for each structure there is a block corner that uniquely dene the valid rectangle.
2 2
Therefore, the number of valid rectangles is bounded by O n . After nding these O n 
2
candidate rectangles, we can check each of them in O 1 after a preprocess in O n .

2.3.2 Coduri surs 

Listing 2.3.1: compile_cpp.sh


1 #!/bin/bash
2
3 problem=rect
4
5 g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"
6
7 "grader.cpp" "${problem}.cpp"

Listing 2.3.2: rect.h


1 #include <vector>
2
3 long long count_rectangles(std::vector<std::vector<int> > a);

Listing 2.3.3: rect.cpp


1 #include "rect.h"
2
3 long long count_rectangles(std::vector<std::vector<int> > a)
4 {
5 return 1;
6 }

Listing 2.3.4: grader.cpp


1 #include "rect.h"
2 #include <cstdio>
3 #include <unistd.h>
4 #include <cassert>
5 #include <string>
6
7 using namespace std;
8
9 class InputReader
10 {
11 private:
12 static const int SIZE = 4096;
13
14 int inputFileDescriptor;
15 char buf[SIZE];
16 int curChar;
17 int numChars;
18
19 public:
20
21 inline InputReader(int _inputFileDescriptor):
22 inputFileDescriptor(_inputFileDescriptor),
23 curChar(0),
24 numChars(0) { }
CAPITOLUL 2. IOI 2019 184

25
26 inline void close()
27 {
28 ::close(inputFileDescriptor);
29 }
30
31 inline char read()
32 {
33 assert(numChars != -1);
34 if (curChar >= numChars)
35 {
36 curChar = 0;
37 numChars = ::read(inputFileDescriptor, buf, SIZE);
38 if (numChars == -1)
39 return -1;
40 }
41 return buf[curChar++];
42 }
43
44 inline int readInt()
45 {
46 int c = eatWhite();
47 int sgn = 1;
48 if (c == ’-’)
49 {
50 sgn = -1;
51 c = read();
52 }
53 int res = 0;
54 do
55 {
56 assert(c >= ’0’ && c <= ’9’);
57 res *= 10;
58 res += c - ’0’;
59 c = read();
60 }
61 while (!isSpaceChar(c));
62 return res * sgn;
63 }
64
65 inline string readString()
66 {
67 char c = eatWhite();
68 string res;
69 do
70 {
71 res += c;
72 c = read();
73 } while (!isSpaceChar(c));
74 return res;
75 }
76
77 inline string readLine()
78 {
79 string res;
80 while (true)
81 {
82 char c = read();
83 if (c == ’\n’ || c == ’\r’ || c == -1)
84 break;
85 res += c;
86 }
87 return res;
88 }
89
90 inline char eatWhite()
91 {
92 char c = read();
93 while (isSpaceChar(c))
94 c = read();
95 return c;
96 }
97
98 static inline bool isSpaceChar(char c)
99 {
100 return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
CAPITOLUL 2. IOI 2019 185

101 }
102 };
103
104
105 int main()
106 {
107 InputReader inputReader(STDIN_FILENO);
108 int n, m;
109 n = inputReader.readInt();
110 m = inputReader.readInt();
111 vector<vector<int>> a(n, vector<int>(m));
112 for (int i = 0; i < n; i++)
113 {
114 for (int j = 0; j < m; j++)
115 {
116 a[i][j] = inputReader.readInt();
117 }
118 }
119 inputReader.close();
120
121 long long result = count_rectangles(a);
122
123 printf("%lld\n", result);
124 fclose(stdout);
125 return 0;
126 }

Listing 2.3.5: rect-model.cpp


1 #include<bits/stdc++.h>
2 using namespace std;
3
4 #define rep(i, n) for(int i = 0, i##__n = (int)(n); i < i##__n; ++i)
5 #define fer(i, a, b) for(int i = (int)(a), i##__b = (int)(b); i < i##__b; ++i)
6 #define rof(i, b, a) for(int i = (int)(b), i##__a = (int)(a); i-- > i##__a; )
7 #define sz(x) (int((x).size()))
8 #define pb push_back
9 #define all(x) (x).begin(), (x).end()
10 #define X first
11 #define Y second
12 //#define endl ’\n’
13
14 template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }
15 template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }
16
17 typedef long long ll;
18 typedef pair<int, int> pii;
19
20 const int maxn = 3000 + 10;
21
22 int n, m;
23 vector<vector<int>> H;
24
25 short L[maxn][maxn], R[maxn][maxn], U[maxn][maxn], D[maxn][maxn];
26 short UL[maxn][maxn], DL[maxn][maxn], LU[maxn][maxn], RU[maxn][maxn];
27
28 vector<ll> v;
29
30 void precalc()
31 {
32 short st[maxn];
33
34 rep(i, n)
35 {
36 int t = 0;
37 rep(j, m)
38 {
39 while(t && H[i][j] > H[i][st[t-1]]) t--;
40 L[i][j] = (t ? st[t-1] : -1);
41 st[t++] = j;
42 }
43 }
44 rep(i, n)
45 {
46 int t = 0;
CAPITOLUL 2. IOI 2019 186

47 rof(j, m, 0)
48 {
49 while(t && H[i][j] > H[i][st[t-1]]) t--;
50 R[i][j] = (t ? st[t-1] : -1);
51 st[t++] = j;
52 }
53 }
54 rep(j, m)
55 {
56 int t = 0;
57 rep(i, n)
58 {
59 while(t && H[i][j] > H[st[t-1]][j]) t--;
60 U[i][j] = (t ? st[t-1] : -1);
61 st[t++] = i;
62 }
63 }
64 rep(j, m)
65 {
66 int t = 0;
67 rof(i, n, 0)
68 {
69 while(t && H[i][j] > H[st[t-1]][j]) t--;
70 D[i][j] = (t ? st[t-1] : -1);
71 st[t++] = i;
72 }
73 }
74
75 rep(j, m) rep(i, n)
76 {
77 if(U[i][j] != -1)
78 {
79 if(j > 0 && U[i][j] == U[i][j-1]) UL[i][j] = UL[i][j-1];
80 else if(j > 0 && i == D[U[i][j]][j-1]) UL[i][j] = DL[U[i][j]][j-1];
81 else UL[i][j] = j;
82 }
83 if(D[i][j] != -1)
84 {
85 if(j > 0 && D[i][j] == D[i][j-1]) DL[i][j] = DL[i][j-1];
86 else if(j > 0 && i == U[D[i][j]][j-1]) DL[i][j] = UL[D[i][j]][j-1];
87 else DL[i][j] = j;
88 }
89 }
90
91 rep(i, n) rep(j, m)
92 {
93 if(L[i][j] != -1)
94 {
95 if(i > 0 && L[i][j] == L[i-1][j]) LU[i][j] = LU[i-1][j];
96 else if(i > 0 && j == R[i-1][L[i][j]]) LU[i][j] = RU[i-1][L[i][j]];
97 else LU[i][j] = i;
98 }
99 if(R[i][j] != -1)
100 {
101 if(i > 0 && R[i][j] == R[i-1][j]) RU[i][j] = RU[i-1][j];
102 else if(i > 0 && j == L[i-1][R[i][j]]) RU[i][j] = LU[i-1][R[i][j]];
103 else RU[i][j] = i;
104 }
105 }
106 }
107
108 void check(int i1, int i2, int j1, int j2)
109 {
110 if(i2 < i1 || j2 < j1) return;
111 if(!((R[i2][j1-1]-1 == j2 && RU[i2][j1-1] <= i1) ||
112 (L[i2][j2+1]+1 == j1 && LU[i2][j2+1] <= i1))) return;
113 if(!((D[i1-1][j2]-1 == i2 && DL[i1-1][j2] <= j1) ||
114 (U[i2+1][j2]+1 == i1 && UL[i2+1][j2] <= j1))) return;
115 v.pb((((ll)i1 * maxn + i2) * maxn + j1) * maxn + j2);
116 }
117
118 int count_rectangles(vector<vector<int>> _H)
119 {
120 H = _H;
121 n = sz(H);
122 m = sz(H[0]);
CAPITOLUL 2. IOI 2019 187

123
124 precalc();
125
126 fer(i, 1, n-1) fer(j, 1, m-1)
127 {
128 if(U[i+1][j] != -1 && L[i][j+1] != -1)
129 check(U[i+1][j]+1, i, L[i][j+1]+1, j);
130 if(U[i+1][j] != -1 && R[i][j-1] != -1)
131 check(U[i+1][j]+1, i, j, R[i][j-1]-1);
132 if(D[i-1][j] != -1 && L[i][j+1] != -1)
133 check(i, D[i-1][j]-1, L[i][j+1]+1, j);
134 if(D[i-1][j] != -1 && R[i][j-1] != -1)
135 check(i, D[i-1][j]-1, j, R[i][j-1]-1);
136 if(D[i-1][j] != -1 && R[D[i-1][j]-1][j-1] != -1)
137 check(i, D[i-1][j]-1, j, R[D[i-1][j]-1][j-1]-1);
138 if(D[i-1][j] != -1 && L[D[i-1][j]-1][j+1] != -1)
139 check(i, D[i-1][j]-1, L[D[i-1][j]-1][j+1]+1, j);
140 }
141
142 sort(all(v));
143 v.resize(unique(all(v)) - v.begin());
144
145 return sz(v);
146 }

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


1 #include<bits/stdc++.h>
2 #include<cstdlib>
3
4 #include<unistd.h> // close
5 #include<stdio.h> // STDOUT_FILENO,STDERR_FILENO
6
7 using namespace std;
8
9 #define rep(i, n) for(int i = 0, i##__n = (int)(n); i < i##__n; ++i)
10 #define fer(i, a, b) for(int i = (int)(a), i##__b = (int)(b); i < i##__b; ++i)
11 #define rof(i, b, a) for(int i = (int)(b), i##__a = (int)(a); i-- > i##__a; )
12 #define sz(x) (int((x).size()))
13 #define pb push_back
14 #define all(x) (x).begin(), (x).end()
15 #define X first
16 #define Y second
17
18 template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }
19 template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }
20
21 typedef long long ll;
22 typedef pair<int, int> pii;
23
24 const int maxn = 3000 + 10;
25
26 int n, m;
27 vector<vector<int>> H;
28
29 short L[maxn][maxn], R[maxn][maxn], U[maxn][maxn], D[maxn][maxn];
30 short UL[maxn][maxn], DL[maxn][maxn], LU[maxn][maxn], RU[maxn][maxn];
31
32 vector<ll> v;
33
34 void precalc()
35 {
36 short st[maxn];
37
38 rep(i, n)
39 {
40 int t = 0;
41 rep(j, m)
42 {
43 while(t && H[i][j] > H[i][st[t-1]]) t--;
44 L[i][j] = (t ? st[t-1] : -1);
45 st[t++] = j;
46 }
47 }
48
CAPITOLUL 2. IOI 2019 188

49 rep(i, n)
50 {
51 int t = 0;
52 rof(j, m, 0)
53 {
54 while(t && H[i][j] > H[i][st[t-1]]) t--;
55 R[i][j] = (t ? st[t-1] : -1);
56 st[t++] = j;
57 }
58 }
59
60 rep(j, m)
61 {
62 int t = 0;
63 rep(i, n)
64 {
65 while(t && H[i][j] > H[st[t-1]][j]) t--;
66 U[i][j] = (t ? st[t-1] : -1);
67 st[t++] = i;
68 }
69 }
70
71 rep(j, m)
72 {
73 int t = 0;
74 rof(i, n, 0)
75 {
76 while(t && H[i][j] > H[st[t-1]][j]) t--;
77 D[i][j] = (t ? st[t-1] : -1);
78 st[t++] = i;
79 }
80 }
81
82 rep(j, m) rep(i, n)
83 {
84 if(U[i][j] != -1)
85 {
86 if(j > 0 && U[i][j] == U[i][j-1])
87 UL[i][j] = UL[i][j-1];
88 else
89 if(j > 0 && i == D[U[i][j]][j-1])
90 UL[i][j] = DL[U[i][j]][j-1];
91 else
92 UL[i][j] = j;
93 }
94
95 if(D[i][j] != -1)
96 {
97 if(j > 0 && D[i][j] == D[i][j-1])
98 DL[i][j] = DL[i][j-1];
99 else
100 if(j > 0 && i == U[D[i][j]][j-1])
101 DL[i][j] = UL[D[i][j]][j-1];
102 else
103 DL[i][j] = j;
104 }
105 }
106
107 rep(i, n) rep(j, m)
108 {
109 if(L[i][j] != -1)
110 {
111 if(i > 0 && L[i][j] == L[i-1][j])
112 LU[i][j] = LU[i-1][j];
113 else
114 if(i > 0 && j == R[i-1][L[i][j]])
115 LU[i][j] = RU[i-1][L[i][j]];
116 else
117 LU[i][j] = i;
118 }
119
120 if(R[i][j] != -1)
121 {
122 if(i > 0 && R[i][j] == R[i-1][j])
123 RU[i][j] = RU[i-1][j];
124 else
CAPITOLUL 2. IOI 2019 189

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


126 RU[i][j] = LU[i-1][R[i][j]];
127 else
128 RU[i][j] = i;
129 }
130 }
131 }
132
133 void check(int i1, int i2, int j1, int j2)
134 {
135 if(i2 < i1 || j2 < j1) return;
136
137 if(!((R[i2][j1-1]-1 == j2 && RU[i2][j1-1] <= i1) ||
138 (L[i2][j2+1]+1 == j1 && LU[i2][j2+1] <= i1))) return;
139
140 if(!((D[i1-1][j2]-1 == i2 && DL[i1-1][j2] <= j1) ||
141 (U[i2+1][j2]+1 == i1 && UL[i2+1][j2] <= j1))) return;
142
143 v.pb((((ll)i1 * maxn + i2) * maxn + j1) * maxn + j2);
144 }
145
146 int count_rectangles(vector<vector<int>> _H)
147 {
148 H = _H;
149 n = sz(H);
150 m = sz(H[0]);
151
152 precalc();
153
154 fer(i, 1, n-1) fer(j, 1, m-1)
155 {
156 if(U[i+1][j] != -1 && L[i][j+1] != -1)
157 check(U[i+1][j]+1, i, L[i][j+1]+1, j);
158
159 if(U[i+1][j] != -1 && R[i][j-1] != -1)
160 check(U[i+1][j]+1, i, j, R[i][j-1]-1);
161
162 if(D[i-1][j] != -1 && L[i][j+1] != -1)
163 check(i, D[i-1][j]-1, L[i][j+1]+1, j);
164
165 if(D[i-1][j] != -1 && R[i][j-1] != -1)
166 check(i, D[i-1][j]-1, j, R[i][j-1]-1);
167
168 if(D[i-1][j] != -1 && R[D[i-1][j]-1][j-1] != -1)
169 check(i, D[i-1][j]-1, j, R[D[i-1][j]-1][j-1]-1);
170
171 if(D[i-1][j] != -1 && L[D[i-1][j]-1][j+1] != -1)
172 check(i, D[i-1][j]-1, L[D[i-1][j]-1][j+1]+1, j);
173 }
174
175 sort(all(v));
176 v.resize(unique(all(v)) - v.begin());
177
178 return sz(v);
179 }
180
181 // --------------- start grader ---------------------
182
183 class InputReader
184 {
185 private:
186 static const int SIZE = 4096;
187
188 int inputFileDescriptor;
189 char buf[SIZE];
190 int curChar;
191 int numChars;
192
193 public:
194
195 inline InputReader(int _inputFileDescriptor):
196 inputFileDescriptor(_inputFileDescriptor),
197 curChar(0),
198 numChars(0)
199 {
200 }
CAPITOLUL 2. IOI 2019 190

201
202 inline void close()
203 {
204 ::close(inputFileDescriptor);
205 }
206
207 inline char read()
208 {
209 assert(numChars != -1);
210 if (curChar >= numChars)
211 {
212 curChar = 0;
213 numChars = ::read(inputFileDescriptor, buf, SIZE);
214
215 if (numChars == -1)
216 return -1;
217 }
218
219 return buf[curChar++];
220 }
221
222 inline int readInt()
223 {
224 int c = eatWhite();
225 int sgn = 1;
226 if (c == ’-’)
227 {
228 sgn = -1;
229 c = read();
230 }
231
232 int res = 0;
233 do
234 {
235 assert(c >= ’0’ && c <= ’9’);
236 res *= 10;
237 res += c - ’0’;
238 c = read();
239 } while (!isSpaceChar(c));
240
241 return res * sgn;
242 }
243
244 inline string readString()
245 {
246 char c = eatWhite();
247 string res;
248 do
249 {
250 res += c;
251 c = read();
252 } while (!isSpaceChar(c));
253
254 return res;
255 }
256
257 inline string readLine()
258 {
259 string res;
260 while (true)
261 {
262 char c = read();
263 if (c == ’\n’ || c == ’\r’ || c == -1)
264 break;
265 res += c;
266 }
267
268 return res;
269 }
270
271 inline char eatWhite()
272 {
273 char c = read();
274 while (isSpaceChar(c))
275 c = read();
276 return c;
CAPITOLUL 2. IOI 2019 191

277 }
278
279 static inline bool isSpaceChar(char c)
280 {
281 return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
282 }
283 };
284
285
286 int main()
287 {
288 auto t1 = clock();
289
290 //std::freopen("../tests/7-18.in", "r", stdin) ; // execution time : 24.047 s
291 //std::freopen("../tests/7-15.in", "r", stdin) ; // execution time : 13.788 s
292 //std::freopen("../tests/7-01.in", "r", stdin) ; // execution time : 4.881 s
293 std::freopen("../tests/6-07.in", "r", stdin) ; // execution time : 3.562 s s
294
295 std::freopen("rectangle.out", "w", stdout) ;
296
297 InputReader inputReader(STDIN_FILENO);
298
299 int n, m;
300 n = inputReader.readInt();
301 m = inputReader.readInt();
302 cerr<<"cerr : "<<n<<" "<<m<<"\n";
303
304 vector<vector<int>> a(n, vector<int>(m));
305 for (int i = 0; i < n; i++)
306 {
307 for (int j = 0; j < m; j++)
308 {
309 a[i][j] = inputReader.readInt();
310 }
311 }
312
313 inputReader.close();
314
315 auto t2 = clock();
316
317 long long result = count_rectangles(a);
318
319 auto t3 = clock();
320
321 printf("%lld\n", result);
322 fclose(stdout);
323
324 auto t4 = clock();
325
326 // reset console output
327 freopen("CON", "w", stdout);
328
329 std::cout <<"result = "<<result<<’\n’<<’\n’;
330
331 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
332 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
333 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
334
335 return 0;
336 }
337 // --------------- end grader ---------------------

Listing 2.3.7: rectangle 147888+grader.cpp


1 // https://oj.uz/submission/147888
2
3 #include <bits/stdc++.h>
4 #include "../rect.h"
5
6 #include<unistd.h> // close
7 #include<stdio.h> // STDOUT_FILENO,STDERR_FILENO
8
9 #define fi first
10 #define se second
11
CAPITOLUL 2. IOI 2019 192

12 using namespace std;


13
14 const int MX = 2505;
15
16 vector<int> row[MX], col[MX];
17 vector<pair<int, int>> pair_row, pair_col;
18 int bit[MX];
19 int lst_row[MX][MX], cnt_row[MX][MX];
20 int lst_col[MX][MX], cnt_col[MX][MX];
21
22 void update(int u, int v)
23 {
24 for (; u < MX; u += u & -u)
25 bit[u] += v;
26 }
27
28 int query(int u)
29 {
30 int ret = 0;
31 for (; u > 0; u -= u & -u)
32 ret += bit[u];
33 return ret;
34 }
35
36 pair<int, int> process(int l, int r, int cur,
37 int lst[MX][MX], int cnt[MX][MX])
38 {
39 if (lst[l][r] != cur - 1)
40 cnt[l][r] = 0;
41 cnt[l][r]++; lst[l][r] = cur;
42 return {r - l - 1, cnt[l][r]};
43 }
44
45 long long find_ans(vector<pair<int, int>> &row,
46 vector<pair<int, int>> &col)
47 {
48 long long ans = 0;
49 for (pair<int, int> &v : col)
50 swap(v.fi, v.se);
51
52 sort(row.begin(), row.end(), greater<pair<int, int>>());
53 sort(col.begin(), col.end(), greater<pair<int, int>>());
54
55 int pt = 0;
56 for (pair<int, int> &v : row)
57 {
58 for (; pt < col.size() && v.fi <= col[pt].fi; pt++)
59 update(col[pt].se, 1);
60 ans += query(v.se);
61 }
62
63 for (int i = 0; i < pt; i++)
64 update(col[i].se, -1);
65
66 return ans;
67 }
68
69 long long count_rectangles(vector<vector<int>> a)
70 {
71 bool eq;
72 int m = a.size(), n = a[0].size();
73 long long ans = 0;
74 for (int i = 0; i < m; i++)
75 row[i] = {0};
76 for (int i = 0; i < n; i++)
77 col[i] = {0};
78 for (int i = 0; i < m - 1; i++)
79 for (int j = 0; j < n - 1; j++)
80 {
81 eq = false; pair_row.clear();
82 while (!row[i].empty())
83 {
84 int u = row[i].back();
85 if (u < j && !eq)
86 pair_row.push_back(process(u, j + 1, i,
87 lst_row, cnt_row));
CAPITOLUL 2. IOI 2019 193

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


89 if (a[i][u] <= a[i][j + 1])
90 row[i].pop_back();
91 else
92 break;
93 }
94
95 row[i].push_back(j + 1);
96 eq = false; pair_col.clear();
97 while (!col[j].empty())
98 {
99 int u = col[j].back();
100 if (u < i && !eq)
101 pair_col.push_back(process(u, i + 1, j,
102 lst_col, cnt_col));
103 eq = (a[u][j] == a[i + 1][j]);
104 if (a[u][j] <= a[i + 1][j])
105 col[j].pop_back();
106 else
107 break;
108 }
109
110 col[j].push_back(i + 1);
111 ans += find_ans(pair_row, pair_col);
112 }
113
114 return ans;
115 }
116
117 // --------------- start grader ---------------------
118 class InputReader
119 {
120 private:
121 static const int SIZE = 4096;
122
123 int inputFileDescriptor;
124 char buf[SIZE];
125 int curChar;
126 int numChars;
127
128 public:
129
130 inline InputReader(int _inputFileDescriptor):
131 inputFileDescriptor(_inputFileDescriptor),
132 curChar(0),
133 numChars(0)
134 {
135 }
136
137 inline void close()
138 {
139 ::close(inputFileDescriptor);
140 }
141
142 inline char read()
143 {
144 assert(numChars != -1);
145 if (curChar >= numChars)
146 {
147 curChar = 0;
148 numChars = ::read(inputFileDescriptor, buf, SIZE);
149
150 if (numChars == -1)
151 return -1;
152 }
153
154 return buf[curChar++];
155 }
156
157 inline int readInt()
158 {
159 int c = eatWhite();
160 int sgn = 1;
161 if (c == ’-’)
162 {
163 sgn = -1;
CAPITOLUL 2. IOI 2019 194

164 c = read();
165 }
166
167 int res = 0;
168 do
169 {
170 assert(c >= ’0’ && c <= ’9’);
171 res *= 10;
172 res += c - ’0’;
173 c = read();
174 } while (!isSpaceChar(c));
175
176 return res * sgn;
177 }
178
179 inline string readString()
180 {
181 char c = eatWhite();
182 string res;
183 do
184 {
185 res += c;
186 c = read();
187 } while (!isSpaceChar(c));
188
189 return res;
190 }
191
192 inline string readLine()
193 {
194 string res;
195 while (true)
196 {
197 char c = read();
198 if (c == ’\n’ || c == ’\r’ || c == -1)
199 break;
200 res += c;
201 }
202
203 return res;
204 }
205
206 inline char eatWhite()
207 {
208 char c = read();
209 while (isSpaceChar(c))
210 c = read();
211 return c;
212 }
213
214 static inline bool isSpaceChar(char c)
215 {
216 return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
217 }
218 };
219
220
221 int main()
222 {
223 auto t1 = clock();
224
225 std::freopen("../tests/7-18.in", "r", stdin) ; // exec time : 17.859 s
226 //std::freopen("../tests/7-15.in", "r", stdin) ; // exec time : 10.719 s
227 //std::freopen("../tests/7-01.in", "r", stdin) ; // exec time : 12.719 s
228 //std::freopen("../tests/6-07.in", "r", stdin) ; // exec time : 5.438 s
229
230 std::freopen("rectangle.out", "w", stdout) ;
231
232 InputReader inputReader(STDIN_FILENO);
233
234 int n, m;
235 n = inputReader.readInt();
236 m = inputReader.readInt();
237 cerr<<"cerr : "<<n<<" "<<m<<"\n";
238
239 vector<vector<int>> a(n, vector<int>(m));
CAPITOLUL 2. IOI 2019 195

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


241 {
242 for (int j = 0; j < m; j++)
243 {
244 a[i][j] = inputReader.readInt();
245 }
246 }
247
248 inputReader.close();
249
250 auto t2 = clock();
251
252 long long result = count_rectangles(a);
253
254 auto t3 = clock();
255
256 printf("%lld\n", result);
257 fclose(stdout);
258
259 auto t4 = clock();
260
261 // reset console output
262 freopen("CON", "w", stdout);
263
264 std::cout <<"result = "<<result<<’\n’<<’\n’;
265
266 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
267 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
268 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
269
270 return 0;
271 }
272 // --------------- end grader ---------------------

Listing 2.3.8: rectangle145193+grader.cpp


1 // https://oj.uz/submission/145193
2
3 #include "../rect.h"
4
5 #include<bits/stdc++.h>
6
7 #include<unistd.h> // close
8 #include<stdio.h> // STDOUT_FILENO,STDERR_FILENO
9
10 using namespace std;
11
12 using ll = long long;
13
14 ll count_rectangles(vector<vector<int>> G)
15 {
16 int N = int(G.size()) - 1;
17 int M = int(G[0].size()) - 1;
18
19 if (N <= 1 || M <= 1) return 0;
20
21 vector<stack<int>> stacks(M);
22 for (int j = 1; j < M; j++)
23 {
24 if (G[0][j] > G[1][j])
25 {
26 stacks[j].push(0);
27 }
28 stacks[j].push(1);
29 }
30
31 vector<vector<pair<int, int>>>
32 history(M,
33 vector<pair<int, int>>(M, pair<int, int>(-1, -1)));
34
35 ll ans = 0;
36
37 for (int i = 1; i < N; i++)
38 {
39 stack<pair<int, vector<int>>> s;
CAPITOLUL 2. IOI 2019 196

40 s.emplace(0, vector<int>());
41 for (int j = 1; j <= M; j++)
42 {
43 vector<int> curSet;
44 bool initialized = false;
45
46 auto merge = [&](const vector<int>& v)
47 {
48 if (!initialized)
49 {
50 curSet = v;
51 initialized = true;
52 return;
53 }
54
55 vector<int> nCurSet;
56 set_intersection(curSet.begin(), curSet.end(),
57 v.begin(), v.end(), back_inserter(nCurSet));
58 curSet = std::move(nCurSet);
59 };
60
61 while (!s.empty() && G[i][j] >= G[i][s.top().first])
62 {
63 merge(s.top().second);
64 if (G[i][j] > G[i][s.top().first])
65 {
66 s.pop();
67 if (!s.empty())
68 {
69 int l = s.top().first + 1;
70 int r = j-1;
71 if (history[l][r].second != i-1)
72 {
73 history[l][r].first = i;
74 }
75 history[l][r].second = i;
76
77 assert(initialized);
78 int numGood = int(curSet.end() -
79 lower_bound(curSet.begin(),
80 curSet.end(),
81 history[l][r].first));
82 //cerr << numGood << ’\n’;
83 ans += numGood;
84 }
85 }
86 else
87 {
88 s.pop();
89 }
90 }
91
92 if (j == M) break;
93
94 vector<int> heights;
95
96 { // build heights
97 while (!stacks[j].empty() &&
98 G[i+1][j] >= G[stacks[j].top()][j])
99 {
100 if (G[i+1][j] > G[stacks[j].top()][j])
101 {
102 stacks[j].pop();
103 if (!stacks[j].empty())
104 {
105 heights.push_back(stacks[j].top()+1);
106 }
107 }
108 else
109 {
110 stacks[j].pop();
111 }
112 }
113
114 stacks[j].push(i+1);
115 }
CAPITOLUL 2. IOI 2019 197

116 reverse(heights.begin(), heights.end());


117 merge(heights);
118 s.emplace(j, std::move(curSet));
119 }
120 }
121
122 return ans;
123 }
124
125
126 // --------------- start grader ---------------------
127 class InputReader
128 {
129 private:
130 static const int SIZE = 4096;
131
132 int inputFileDescriptor;
133 char buf[SIZE];
134 int curChar;
135 int numChars;
136
137 public:
138
139 inline InputReader(int _inputFileDescriptor):
140 inputFileDescriptor(_inputFileDescriptor),
141 curChar(0),
142 numChars(0) {
143 }
144
145 inline void close()
146 {
147 ::close(inputFileDescriptor);
148 }
149
150 inline char read() {
151 assert(numChars != -1);
152 if (curChar >= numChars) {
153 curChar = 0;
154 numChars = ::read(inputFileDescriptor, buf, SIZE);
155
156 if (numChars == -1)
157 return -1;
158 }
159 return buf[curChar++];
160 }
161
162 inline int readInt() {
163 int c = eatWhite();
164 int sgn = 1;
165 if (c == ’-’) {
166 sgn = -1;
167 c = read();
168 }
169 int res = 0;
170 do {
171 assert(c >= ’0’ && c <= ’9’);
172 res *= 10;
173 res += c - ’0’;
174 c = read();
175 } while (!isSpaceChar(c));
176 return res * sgn;
177 }
178
179 inline string readString() {
180 char c = eatWhite();
181 string res;
182 do {
183 res += c;
184 c = read();
185 } while (!isSpaceChar(c));
186 return res;
187 }
188
189 inline string readLine() {
190 string res;
191 while (true) {
CAPITOLUL 2. IOI 2019 198

192 char c = read();


193 if (c == ’\n’ || c == ’\r’ || c == -1)
194 break;
195 res += c;
196 }
197 return res;
198 }
199
200 inline char eatWhite() {
201 char c = read();
202 while (isSpaceChar(c))
203 c = read();
204 return c;
205 }
206
207 static inline bool isSpaceChar(char c) {
208 return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
209 }
210 };
211
212
213 int main()
214 {
215 auto t1 = clock();
216
217 std::freopen("../tests/7-18.in", "r", stdin) ; // execution time : 17.029 s
218 //std::freopen("../tests/7-15.in", "r", stdin) ; // execution time : 10.061 s
219 //std::freopen("../tests/7-01.in", "r", stdin) ; // execution time : 14.344 s
220 //std::freopen("../tests/6-07.in", "r", stdin) ; // execution time : 10.749 s
221
222 std::freopen("rectangle.out", "w", stdout) ;
223
224 InputReader inputReader(STDIN_FILENO);
225
226 int n, m;
227 n = inputReader.readInt();
228 m = inputReader.readInt();
229 cerr<<"cerr : "<<n<<" "<<m<<"\n";
230
231 vector<vector<int>> a(n, vector<int>(m));
232 for (int i = 0; i < n; i++)
233 {
234 for (int j = 0; j < m; j++)
235 {
236 a[i][j] = inputReader.readInt();
237 }
238 }
239
240 inputReader.close();
241
242 auto t2 = clock();
243
244 long long result = count_rectangles(a);
245
246 auto t3 = clock();
247
248 printf("%lld\n", result);
249 fclose(stdout);
250
251 auto t4 = clock();
252
253 // reset console output
254 freopen("CON", "w", stdout);
255
256 std::cout <<"result = "<<result<<’\n’<<’\n’;
257
258 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
259 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
260 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
261
262 return 0;
263 }
264 // --------------- end grader ---------------------

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


CAPITOLUL 2. IOI 2019 199

1 //Peyman Jabbarzade Ganje


2 //Boarder shouldn’t be equal with inside
3 #include<bits/stdc++.h>
4
5 #include<unistd.h> // close
6 #include<stdio.h> // STDOUT_FILENO,STDERR_FILENO
7
8 #define X first
9 #define Y second
10
11 using namespace std;
12 typedef pair<int, int> pii;
13
14 const int maxn = 3000 + 10, maxx=1e7;
15 int n,m, fen_t[maxx];
16 long long ans;
17
18 vector<int>vec, nozoli, will_remove;
19 map<int, int> fp[maxn][maxn];
20 vector<pii> sp[2][maxn][maxn];
21
22 void fen_add(int x,int val)
23 {
24 x++;
25 while(x>0)
26 {
27 fen_t[x]+=val;
28 x -= x&(-x);
29 }
30 }
31
32 int fen_f(int x)
33 {
34 x++;
35 int ret = 0;
36 while(x<maxx)
37 {
38 ret += fen_t[x];
39 x += x&(-x);
40 }
41
42 return ret;
43 }
44
45 void add_pair(int type, int lvl, int l, int r)
46 {
47 int tmp = 1;
48 if(fp[lvl][l].find(r) != fp[lvl][l].end())
49 tmp += fp[lvl][l][r];
50
51 if(type == 0)
52 {
53 if(r-l-1>0)
54 sp[type][lvl+1][r].push_back(pii(r-l-1, tmp));
55 }
56 else
57 {
58 if(r-l-1>0)
59 sp[type][r][lvl+1].push_back(pii(tmp, r-l-1));
60 }
61
62 fp[lvl+1][l][r] = tmp;
63 }
64
65 void extract_pairs(int type, int lvl)
66 {
67 nozoli.clear();
68 for(int i=0;i<vec.size();i++)
69 {
70 int last=-1;
71 while(nozoli.size() && vec[nozoli.back()] < vec[i])
72 {
73 if(vec[nozoli.back()] > last)
74 add_pair(type, lvl, nozoli.back(), i);
75 last = vec[nozoli.back()];
CAPITOLUL 2. IOI 2019 200

76 nozoli.pop_back();
77 }
78
79 if(nozoli.size())
80 add_pair(type, lvl, nozoli.back(), i);
81 nozoli.push_back(i);
82 }
83 }
84
85 long long count_rectangles(vector<vector<int> >a)
86 {
87 n = a.size();
88 m = a[0].size();
89
90 for(int i=0;i<n;i++)
91 {
92 vec.clear();
93 for(int j=0;j<m;j++)
94 vec.push_back(a[i][j]);
95 extract_pairs(0,i);
96 }
97
98 for(int i=0;i<=max(n,m);i++)
99 for(int j=0;j<=max(n,m);j++)
100 fp[i][j].clear();
101
102 for(int j=0;j<m;j++)
103 {
104 vec.clear();
105 for(int i=0;i<n;i++)
106 vec.push_back(a[i][j]);
107 extract_pairs(1,j);
108 }
109
110 for(int i=0;i<=max(n,m);i++)
111 for(int j=0;j<=max(n,m);j++)
112 if(sp[0][i][j].size() && sp[1][i][j].size())
113 {
114 sort(sp[0][i][j].begin(), sp[0][i][j].end());
115 sort(sp[1][i][j].begin(), sp[1][i][j].end());
116 // sp[0].X fix sp[1].X moteghaier
117 int p = 0;
118 will_remove.clear();
119 for(int k=0;k<sp[1][i][j].size();k++)
120 {
121 while(p<sp[0][i][j].size() &&
122 sp[0][i][j][p].X <= sp[1][i][j][k].X)
123 {
124 fen_add(sp[0][i][j][p].Y, 1);
125 will_remove.push_back(sp[0][i][j][p].Y);
126 p++;
127 }
128
129 ans += fen_f(sp[1][i][j][k].Y);
130 }
131
132 for(int k=0;k<will_remove.size();k++)
133 fen_add(will_remove[k], -1);
134 }
135
136 return ans;
137 }
138
139 // --------------- start grader ---------------------
140 class InputReader
141 {
142 private:
143 static const int SIZE = 4096;
144
145 int inputFileDescriptor;
146 char buf[SIZE];
147 int curChar;
148 int numChars;
149
150 public:
151
CAPITOLUL 2. IOI 2019 201

152 inline InputReader(int _inputFileDescriptor):


153 inputFileDescriptor(_inputFileDescriptor),
154 curChar(0),
155 numChars(0)
156 {
157 }
158
159 inline void close()
160 {
161 ::close(inputFileDescriptor);
162 }
163
164 inline char read()
165 {
166 assert(numChars != -1);
167 if (curChar >= numChars)
168 {
169 curChar = 0;
170 numChars = ::read(inputFileDescriptor, buf, SIZE);
171
172 if (numChars == -1)
173 return -1;
174 }
175
176 return buf[curChar++];
177 }
178
179 inline int readInt()
180 {
181 int c = eatWhite();
182 int sgn = 1;
183 if (c == ’-’)
184 {
185 sgn = -1;
186 c = read();
187 }
188
189 int res = 0;
190 do
191 {
192 assert(c >= ’0’ && c <= ’9’);
193 res *= 10;
194 res += c - ’0’;
195 c = read();
196 } while (!isSpaceChar(c));
197
198 return res * sgn;
199 }
200
201 inline string readString()
202 {
203 char c = eatWhite();
204 string res;
205 do
206 {
207 res += c;
208 c = read();
209 } while (!isSpaceChar(c));
210
211 return res;
212 }
213
214 inline string readLine()
215 {
216 string res;
217 while (true)
218 {
219 char c = read();
220 if (c == ’\n’ || c == ’\r’ || c == -1)
221 break;
222 res += c;
223 }
224
225 return res;
226 }
227
CAPITOLUL 2. IOI 2019 202

228 inline char eatWhite()


229 {
230 char c = read();
231 while (isSpaceChar(c))
232 c = read();
233 return c;
234 }
235
236 static inline bool isSpaceChar(char c)
237 {
238 return c == ’ ’ || c == ’\n’ || c == ’\r’ || c == ’\t’ || c == -1;
239 }
240 };
241
242
243 int main()
244 {
245 auto t1 = clock();
246
247 std::freopen("../tests/7-18.in", "r", stdin) ; // execution time : 43.187 s
248 // std::freopen("../tests/7-15.in", "r", stdin) ; // execution time : 26.486 s
249 //std::freopen("../tests/7-01.in", "r", stdin) ; // execution time : 34.197 s
250 //std::freopen("../tests/6-07.in", "r", stdin) ; // execution time : 18.419 s
251
252 std::freopen("rectangle.out", "w", stdout) ;
253
254 InputReader inputReader(STDIN_FILENO);
255
256 int n, m;
257 n = inputReader.readInt();
258 m = inputReader.readInt();
259 cerr<<"cerr : "<<n<<" "<<m<<"\n";
260
261 vector<vector<int>> a(n, vector<int>(m));
262 for (int i = 0; i < n; i++)
263 {
264 for (int j = 0; j < m; j++)
265 {
266 a[i][j] = inputReader.readInt();
267 }
268 }
269
270 inputReader.close();
271
272 auto t2 = clock();
273
274 long long result = count_rectangles(a);
275
276 auto t3 = clock();
277
278 printf("%lld\n", result);
279 fclose(stdout);
280
281 auto t4 = clock();
282
283 // reset console output
284 freopen("CON", "w", stdout);
285
286 std::cout <<"result = "<<result<<’\n’<<’\n’;
287
288 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
289 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
290 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
291
292 return 0;
293 }
294 // --------------- end grader ---------------------
CAPITOLUL 2. IOI 2019 203

2.3.3 *Rezolvare detaliat 

2.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, denite printr-o secvenµ  de t  1 puncte p0 , ..., pt dup  cum
urmeaz :
Pentru ecare 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-
ne³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 denesc
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, ecare
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 ³iere de intrare în care
vor  specicate locaµiile punctelor marcate. Pentru ecare ³ier de intrare, trebuie s  înc rcaµi
un ³ier de ie³ire care descrie o linie frânt  cu propriet µile cerute.
Pentru ecare ³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 ³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 ³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  e un întreg.

Exemple

Pentru intrarea:

4
2 1
3 3
4 4
5 2
CAPITOLUL 2. IOI 2019 204

o posibil  ie³ire este:

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

Figura 2.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 ec rui ³ier de ie³ire (³ier de intrare sau arhiv  zip) nu trebuie s  dep ³easc 
15M B .

Punctare

Pentru ecare test, puteµi obµine maxim 10 puncte. Output-ul unui test va  punctat cu 0
puncte dac  nu conµine o linie frânt  cu propriet µile cerute. Altfel, punctajul va  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

2.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: rst 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 2. IOI 2019 205

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

2.4.2 Coduri surs 

Listing 2.4.1: compile_cpp.sh


1 #!/bin/bash
2
3 problem=line
4
5 g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"
6
7 "grader.cpp" "${problem}.cpp"

Listing 2.4.2: line.h


1 // ***

Listing 2.4.3: line.cpp


1 // ***

Listing 2.4.4: grader.cpp


1 // ***

Listing 2.4.5: line-model.cpp


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

4 #include <iostream>
5 #include <algorithm>
6 #include <cstring>
7 #include <string>
8 #include <cmath>
9 #include <cassert>
10 #include <ctime>
11 #include <vector>
12 #include <set>
13 #include <map>
14 #include <unordered_set>
15 #include <unordered_map>
16
17 using namespace std;
18
19 #define pb push_back
20 #define mp make_pair
21 #define fs first
22 #define sc second
23
24 int n;
25 vector <pair <int, int> > input;
26 vector <pair <int, int> > answer;
27 bool last_volatile = false;
28
29 void continue_answer(pair <int, int> point)
30 {
31 if (answer.empty())
32 {
33 answer.pb(point);
34
35 return;
36 }
37
38 if (answer.size() >= 2u)
39 {
40 bool worked = false;
41 int last = (int) answer.size() - 1;
42 int prelast = (int) answer.size() - 2;
43 if (answer[prelast].fs == answer[last].fs)
44 {
45 if ((answer[last].sc - answer[prelast].sc) * 1ll *
46 (point.sc - answer[last].sc) > 0)
47 {
48 answer[last].sc = point.sc;
49 worked = true;
50 }
51 }
52 else
53 {
54 if ((answer[last].fs - answer[prelast].fs) * 1ll *
55 (point.fs - answer[last].fs) > 0)
56 {
57 answer[last].fs = point.fs;
58 worked = true;
59 }
60 }
61
62 if (!worked && last_volatile)
63 {
64 int preprelast = (int) answer.size() - 3;
65 answer[prelast].fs = answer[preprelast].fs;
66 answer[prelast].sc = answer[last].sc;
67 if (answer[prelast].fs == answer[last].fs)
68 {
69 if ((answer[last].sc - answer[prelast].sc) * 1ll *
70 (point.sc - answer[last].sc) > 0)
71 {
72 answer[last].sc = point.sc;
73 }
74 }
75 else
76 {
77 if ((answer[last].fs - answer[prelast].fs) * 1ll *
78 (point.fs - answer[last].fs) > 0)
79 {
CAPITOLUL 2. IOI 2019 207

80 answer[last].fs = point.fs;
81 }
82 }
83 }
84 }
85
86 last_volatile = (point.fs != answer.back().fs && point.sc !=
87 answer.back().sc);
88
89 if (point.fs != answer.back().fs)
90 {
91 answer.pb(mp(point.fs, answer.back().sc));
92 }
93 if (point.sc != answer.back().sc)
94 {
95 answer.pb(point);
96 }
97 }
98
99 void add_spiral(vector <pair <pair <int, int>, pair <int, int> > > spiral)
100 {
101 for (int i = 0; i < (int) spiral.size(); i++)
102 {
103 int lft = spiral[i].fs.fs;
104 int up = spiral[i].fs.sc;
105 int rgt = spiral[i].sc.fs;
106 int down = spiral[i].sc.sc;
107
108 pair <int, int> ldc = mp(input[lft].fs, input[down].sc);
109 pair <int, int> luc = mp(input[lft].fs, input[up].sc);
110 pair <int, int> rdc = mp(input[rgt].fs, input[down].sc);
111 pair <int, int> ruc = mp(input[rgt].fs, input[up].sc);
112
113 if (i == 0)
114 {
115 continue_answer(ldc);
116 continue_answer(luc);
117 continue_answer(ruc);
118 continue_answer(rdc);
119 continue_answer(ldc);
120 }
121 else
122 {
123 answer.back().fs = input[lft].fs;
124 if (input[lft].sc >= answer.back().sc)
125 {
126 continue_answer(luc);
127 continue_answer(ruc);
128 continue_answer(rdc);
129 continue_answer(ldc);
130 }
131 else
132 {
133 continue_answer(ldc);
134 continue_answer(rdc);
135 continue_answer(ruc);
136 continue_answer(luc);
137 }
138 }
139 }
140 }
141
142 void add_chain(vector <pair <int, int> > chain, bool order,
143 bool flip, bool interleave)
144 {
145 if (chain.empty())
146 return;
147 int len = (int) chain.size();
148 if (flip)
149 {
150 for (int i = 0; i < len; i++)
151 {
152 chain[i].fs *= -1;
153 }
154 }
155 if (order)
CAPITOLUL 2. IOI 2019 208

156 {
157 for (int i = 0; i < len; i++)
158 {
159 chain[i].sc *= -1;
160 }
161 }
162 sort(chain.begin(), chain.end());
163 if (flip)
164 {
165 for (int i = 0; i < len; i++)
166 {
167 chain[i].fs *= -1;
168 }
169 }
170 if (order)
171 {
172 for (int i = 0; i < len; i++)
173 {
174 chain[i].sc *= -1;
175 }
176 }
177
178 continue_answer(chain[0]);
179 for (int i = 1; i < len; i++)
180 {
181 if (i % 2 xor interleave)
182 {
183 continue_answer(mp(chain[i].fs, chain[i - 1].sc));
184 }
185 else
186 {
187 continue_answer(mp(chain[i - 1].fs, chain[i].sc));
188 }
189 }
190 if (len > 1)
191 {
192 continue_answer(chain.back());
193 }
194 }
195
196 void finalize()
197 {
198 cerr << n << ’ ’ << answer.size() - 1 << ’ ’ << answer.size() - 1 - n << ’\n’;
199 cout << answer.size()-1 << ’\n’;
200 for (int i = 1; i < (int) answer.size(); i++)
201 {
202 cout << answer[i].fs << ’ ’ << answer[i].sc << ’\n’;
203 }
204 }
205
206 int main ()
207 {
208 cin >> n;
209
210 for (int i = 0; i < n; i++)
211 {
212 int x, y;
213 cin >> x >> y;
214 input.pb(mp(x, y));
215 }
216
217 vector <pair <int, int> > chain1, chain2;
218 vector <pair <pair <int, int>, pair <int, int> > > spiral;
219 set <pair <int, int> > xs;
220 set <pair <int, int> > ys;
221 vector <bool> used(n, false);
222
223 for (int i = 0; i < n; i++)
224 {
225 xs.insert(mp(input[i].fs, i));
226 ys.insert(mp(input[i].sc, i));
227 }
228
229 while (true)
230 {
231 int lft = -1;
CAPITOLUL 2. IOI 2019 209

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


233 {
234 xs.erase(xs.begin());
235 }
236 if (!xs.empty())
237 {
238 lft = xs.begin()->sc;
239 }
240
241 int rgt = -1;
242 while (!xs.empty() && used[xs.rbegin()->sc])
243 {
244 xs.erase( *xs.rbegin());
245 }
246 if (!xs.empty())
247 {
248 rgt = xs.rbegin()->sc;
249 }
250
251 int down = -1;
252 while (!ys.empty() && used[ys.begin()->sc])
253 {
254 ys.erase(ys.begin());
255 }
256 if (!ys.empty())
257 {
258 down = ys.begin()->sc;
259 }
260
261 int up = -1;
262 while (!ys.empty() && used[ys.rbegin()->sc])
263 {
264 ys.erase( *ys.rbegin());
265 }
266 if (!ys.empty())
267 {
268 up = ys.rbegin()->sc;
269 }
270
271 if (lft == -1 || rgt == -1 || down == -1 || up == -1)
272 {
273 break;
274 }
275
276 if (lft == down)
277 {
278 chain1.pb(input[lft]);
279 used[lft] = true;
280 continue;
281 }
282 if (lft == up)
283 {
284 chain2.pb(input[lft]);
285 used[lft] = true;
286 continue;
287 }
288 if (rgt == down)
289 {
290 chain2.pb(input[rgt]);
291 used[rgt] = true;
292 continue;
293 }
294 if (rgt == up)
295 {
296 chain1.pb(input[rgt]);
297 used[rgt] = true;
298 continue;
299 }
300 spiral.pb(mp(mp(lft, up), mp(rgt, down)));
301 used[lft] = true;
302 used[rgt] = true;
303 used[down] = true;
304 used[up] = true;
305 }
306
307 reverse(spiral.begin(), spiral.end());
CAPITOLUL 2. IOI 2019 210

308 vector <pair <int, int> > best_answer;


309 for (int flip1 = 0; flip1 < 8; flip1++)
310 {
311 for (int flip2 = 0; flip2 < 8; flip2++)
312 {
313 vector <int> parts = {0, 1, 2};
314 do
315 {
316 answer.clear();
317 last_volatile = false;
318 continue_answer(mp(0, 0));
319 for (int i = 0; i < (int) parts.size(); i++)
320 {
321 if (parts[i] == 0)
322 {
323 add_spiral(spiral);
324 }
325 else
326 if (parts[i] == 1)
327 {
328 add_chain(chain1, flip1 >> 2, (flip1 >> 1) & 1, flip1 & 1);
329 }
330 else
331 {
332 add_chain(chain2, flip2 >> 2, (flip2 >> 1) & 1, flip2 & 1);
333 }
334 }
335 if (best_answer.empty() || answer.size() < best_answer.size())
336 {
337 best_answer = answer;
338 }
339 } while (next_permutation(parts.begin(), parts.end()));
340 }
341 }
342
343 answer = best_answer;
344
345 finalize();
346
347 return 0;
348 }

Listing 2.4.6: sol-ge-most-optimal+grader.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include <iostream>
4 #include <algorithm>
5 #include <cstring>
6 #include <string>
7 #include <cmath>
8 #include <cassert>
9 #include <ctime>
10 #include <vector>
11 #include <set>
12 #include <map>
13 #include <unordered_set>
14 #include <unordered_map>
15
16 using namespace std;
17
18 #define pb push_back
19 #define mp make_pair
20 #define fs first
21 #define sc second
22
23 int n;
24 vector <pair <int, int> > input;
25 vector <pair <int, int> > answer;
26 bool last_volatile = false;
27
28 void continue_answer(pair <int, int> point)
29 {
30 if (answer.empty())
31 {
CAPITOLUL 2. IOI 2019 211

32 answer.pb(point);
33
34 return;
35 }
36
37 if (answer.size() >= 2u)
38 {
39 bool worked = false;
40 int last = (int) answer.size() - 1;
41 int prelast = (int) answer.size() - 2;
42 if (answer[prelast].fs == answer[last].fs)
43 {
44 if ((answer[last].sc - answer[prelast].sc) * 1ll *
45 (point.sc - answer[last].sc) > 0)
46 {
47 answer[last].sc = point.sc;
48 worked = true;
49 }
50 }
51 else
52 {
53 if ((answer[last].fs - answer[prelast].fs) * 1ll *
54 (point.fs - answer[last].fs) > 0)
55 {
56 answer[last].fs = point.fs;
57 worked = true;
58 }
59 }
60
61 if (!worked && last_volatile)
62 {
63 int preprelast = (int) answer.size() - 3;
64 answer[prelast].fs = answer[preprelast].fs;
65 answer[prelast].sc = answer[last].sc;
66 if (answer[prelast].fs == answer[last].fs)
67 {
68 if ((answer[last].sc - answer[prelast].sc) * 1ll *
69 (point.sc - answer[last].sc) > 0)
70 {
71 answer[last].sc = point.sc;
72 }
73 }
74 else
75 {
76 if ((answer[last].fs - answer[prelast].fs) * 1ll *
77 (point.fs - answer[last].fs) > 0)
78 {
79 answer[last].fs = point.fs;
80 }
81 }
82 }
83 }
84
85 last_volatile = (point.fs != answer.back().fs && point.sc !=
86 answer.back().sc);
87
88 if (point.fs != answer.back().fs)
89 {
90 answer.pb(mp(point.fs, answer.back().sc));
91 }
92 if (point.sc != answer.back().sc)
93 {
94 answer.pb(point);
95 }
96 }
97
98 void add_spiral(vector <pair <pair <int, int>, pair <int, int> > > spiral)
99 {
100 for (int i = 0; i < (int) spiral.size(); i++)
101 {
102 int lft = spiral[i].fs.fs;
103 int up = spiral[i].fs.sc;
104 int rgt = spiral[i].sc.fs;
105 int down = spiral[i].sc.sc;
106
107 pair <int, int> ldc = mp(input[lft].fs, input[down].sc);
CAPITOLUL 2. IOI 2019 212

108 pair <int, int> luc = mp(input[lft].fs, input[up].sc);


109 pair <int, int> rdc = mp(input[rgt].fs, input[down].sc);
110 pair <int, int> ruc = mp(input[rgt].fs, input[up].sc);
111
112 if (i == 0)
113 {
114 continue_answer(ldc);
115 continue_answer(luc);
116 continue_answer(ruc);
117 continue_answer(rdc);
118 continue_answer(ldc);
119 }
120 else
121 {
122 answer.back().fs = input[lft].fs;
123 if (input[lft].sc >= answer.back().sc)
124 {
125 continue_answer(luc);
126 continue_answer(ruc);
127 continue_answer(rdc);
128 continue_answer(ldc);
129 }
130 else
131 {
132 continue_answer(ldc);
133 continue_answer(rdc);
134 continue_answer(ruc);
135 continue_answer(luc);
136 }
137 }
138 }
139 }
140
141 void add_chain(vector <pair <int, int> > chain, bool order,
142 bool flip, bool interleave)
143 {
144 if (chain.empty())
145 return;
146 int len = (int) chain.size();
147 if (flip)
148 {
149 for (int i = 0; i < len; i++)
150 {
151 chain[i].fs *= -1;
152 }
153 }
154 if (order)
155 {
156 for (int i = 0; i < len; i++)
157 {
158 chain[i].sc *= -1;
159 }
160 }
161 sort(chain.begin(), chain.end());
162 if (flip)
163 {
164 for (int i = 0; i < len; i++)
165 {
166 chain[i].fs *= -1;
167 }
168 }
169 if (order)
170 {
171 for (int i = 0; i < len; i++)
172 {
173 chain[i].sc *= -1;
174 }
175 }
176
177 continue_answer(chain[0]);
178 for (int i = 1; i < len; i++)
179 {
180 if (i % 2 xor interleave)
181 {
182 continue_answer(mp(chain[i].fs, chain[i - 1].sc));
183 }
CAPITOLUL 2. IOI 2019 213

184 else
185 {
186 continue_answer(mp(chain[i - 1].fs, chain[i].sc));
187 }
188 }
189 if (len > 1)
190 {
191 continue_answer(chain.back());
192 }
193 }
194
195 void finalize()
196 {
197 cerr << "cerr : "<<n << ’ ’ << answer.size() - 1
198 << ’ ’ << answer.size() - 1 - n << ’\n’;
199 cout << answer.size()-1 << ’\n’;
200 for (int i = 1; i < (int) answer.size(); i++)
201 {
202 cout << answer[i].fs << ’ ’ << answer[i].sc << ’\n’;
203 }
204 }
205
206 int main ()
207 {
208 auto t1 = clock();
209
210 std::freopen("../tests/10.in", "r", stdin) ; // execution time : 15.805 s
211 //std::freopen("../tests/04.in", "r", stdin) ; // execution time : 7.761 s
212 //std::freopen("../tests/03.in", "r", stdin) ; // execution time : 0.875 s
213 //std::freopen("../tests/00.in", "r", stdin) ; // execution time : 0.031 s
214
215
216 std::freopen("line.out", "w", stdout) ;
217
218 cin >> n;
219
220 cerr<<"cerr : "<<n<<"\n";
221
222 for (int i = 0; i < n; i++)
223 {
224 int x, y;
225 cin >> x >> y;
226 input.pb(mp(x, y));
227 }
228
229 auto t2 = clock();
230
231 vector <pair <int, int> > chain1, chain2;
232 vector <pair <pair <int, int>, pair <int, int> > > spiral;
233 set <pair <int, int> > xs;
234 set <pair <int, int> > ys;
235 vector <bool> used(n, false);
236
237 for (int i = 0; i < n; i++)
238 {
239 xs.insert(mp(input[i].fs, i));
240 ys.insert(mp(input[i].sc, i));
241 }
242
243 while (true)
244 {
245 int lft = -1;
246 while (!xs.empty() && used[xs.begin()->sc])
247 {
248 xs.erase(xs.begin());
249 }
250 if (!xs.empty())
251 {
252 lft = xs.begin()->sc;
253 }
254
255 int rgt = -1;
256 while (!xs.empty() && used[xs.rbegin()->sc])
257 {
258 xs.erase( *xs.rbegin());
259 }
CAPITOLUL 2. IOI 2019 214

260 if (!xs.empty())
261 {
262 rgt = xs.rbegin()->sc;
263 }
264
265 int down = -1;
266 while (!ys.empty() && used[ys.begin()->sc])
267 {
268 ys.erase(ys.begin());
269 }
270 if (!ys.empty())
271 {
272 down = ys.begin()->sc;
273 }
274
275 int up = -1;
276 while (!ys.empty() && used[ys.rbegin()->sc])
277 {
278 ys.erase( *ys.rbegin());
279 }
280 if (!ys.empty())
281 {
282 up = ys.rbegin()->sc;
283 }
284
285 if (lft == -1 || rgt == -1 || down == -1 || up == -1)
286 {
287 break;
288 }
289
290 if (lft == down)
291 {
292 chain1.pb(input[lft]);
293 used[lft] = true;
294 continue;
295 }
296 if (lft == up)
297 {
298 chain2.pb(input[lft]);
299 used[lft] = true;
300 continue;
301 }
302 if (rgt == down)
303 {
304 chain2.pb(input[rgt]);
305 used[rgt] = true;
306 continue;
307 }
308 if (rgt == up)
309 {
310 chain1.pb(input[rgt]);
311 used[rgt] = true;
312 continue;
313 }
314 spiral.pb(mp(mp(lft, up), mp(rgt, down)));
315 used[lft] = true;
316 used[rgt] = true;
317 used[down] = true;
318 used[up] = true;
319 }
320
321 reverse(spiral.begin(), spiral.end());
322 vector <pair <int, int> > best_answer;
323 for (int flip1 = 0; flip1 < 8; flip1++)
324 {
325 for (int flip2 = 0; flip2 < 8; flip2++)
326 {
327 vector <int> parts = {0, 1, 2};
328 do
329 {
330 answer.clear();
331 last_volatile = false;
332 continue_answer(mp(0, 0));
333 for (int i = 0; i < (int) parts.size(); i++)
334 {
335 if (parts[i] == 0)
CAPITOLUL 2. IOI 2019 215

336 {
337 add_spiral(spiral);
338 }
339 else
340 if (parts[i] == 1)
341 {
342 add_chain(chain1, flip1 >> 2,
343 (flip1 >> 1) & 1, flip1 & 1);
344 }
345 else
346 {
347 add_chain(chain2, flip2 >> 2,
348 (flip2 >> 1) & 1, flip2 & 1);
349 }
350 }
351 if (best_answer.empty() ||
352 answer.size() < best_answer.size())
353 {
354 best_answer = answer;
355 }
356 } while (next_permutation(parts.begin(), parts.end()));
357 }
358 }
359
360 answer = best_answer;
361
362 auto t3 = clock();
363
364 finalize();
365
366 auto t4 = clock();
367
368 // reset console output
369 freopen("CON", "w", stdout);
370
371 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
372 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
373 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
374
375 return 0;
376 }

2.4.3 *Rezolvare detaliat 

2.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 ecare 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 ecare imagine ³i toµi ceilalµi pixeli
sunt albi.
Robotul poate procesa ecare 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 sucient de mare de celule, indexat de la 0.
Fiecare celul  este sau 0, sau 1, iar aceast  valoare odat  setat , nu poate  modicat . 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 2. IOI 2019 216

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 consecu-
tivi î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  celule care
stocheaz  e pixeli, e 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  e 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 ec 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)
a Adaug  o instrucµiune NOT , AND , OR , respectiv XOR .
a N (pentru add_not): indicele celulei din care instrucµiunea NOT ad ugat  î³i cite³te intra-
rea.
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. Ape-
l 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  evaluat pe o serie de imagini. Soluµia voastr  trece un
anumit test dac  pentru ecare 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.
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).
CAPITOLUL 2. IOI 2019 217

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 2.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 a³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 a³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 a³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 ecare 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 a  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
va a³a unul din mesajele de eroare menµionat la sfâr³itul secµiunii de Implementare, iar apoi
iese.
CAPITOLUL 2. IOI 2019 218

Altfel, grader-ul produce dou  ie³iri.


În prima faz , grader-ul a³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 ³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

2.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 dierence
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 dierences 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 denition 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.
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.
CAPITOLUL 2. IOI 2019 219

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 dierent approaches with the same order of instructions and
inputs used.

2.5.2 Coduri surs 

Listing 2.5.1: compile_cpp.sh


1 #!/bin/bash
2
3 problem=vision
4
5 g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"
6
7 "grader.cpp" "${problem}.cpp"

Listing 2.5.2: vision.h


1 #include <vector>
2 #include <string>
3
4 void construct_network(int H, int W, int K);
5
6 int add_and(std::vector<int> Ns);
7
8 int add_or(std::vector<int> Ns);
9
10 int add_xor(std::vector<int> Ns);
11
12 int add_not(int N);

Listing 2.5.3: vision.cpp


1 #include "vision.h"
2
3 void construct_network(int H, int W, int K)
4 {
5 std::vector<int> Ns;
6 Ns = {0, 1};
7 int a = add_and(Ns);
8 Ns = {0, a};
9 int b = add_or(Ns);
10 Ns = {0, 1, b};
11 int c = add_xor(Ns);
12 add_not(c);
13 }

Listing 2.5.4: grader.cpp


1 #include <cstdio>
2 #include <cassert>
3 #include <string>
4 #include "vision.h"
5
6 using namespace std;
7
8 static const int MAX_INSTRUCTIONS = 10000;
9 static const int MAX_INPUTS = 1000000;
10
11 static const int _AND = 0;
12 static const int _OR = 1;
13 static const int _XOR = 2;
14 static const int _NOT = 3;
15
16 static inline bool increasing(int a, int b, int c)
CAPITOLUL 2. IOI 2019 220

17 {
18 return a <= b && b <= c;
19 }
20
21 [[noreturn]] static inline void error(string message)
22 {
23 printf("%s\n", message.c_str());
24 exit(0);
25 }
26
27 class InstructionNetwork
28 {
29
30 struct Instruction
31 {
32 int type;
33 vector<int> input_indexes;
34
35 inline Instruction(int _type, const vector<int>& _input_indexes):
36 type(_type), input_indexes(_input_indexes) { }
37
38 inline int apply(int a, int b) const
39 {
40 switch (type)
41 {
42 case _AND:
43 return a & b;
44 case _OR:
45 return a | b;
46 case _XOR:
47 return a ^ b;
48 default:
49 return 0;
50 }
51 }
52
53 inline int compute(const vector<int>& memory_cells) const
54 {
55 int r = memory_cells[input_indexes[0]];
56 if (type == _NOT)
57 return 1 - r;
58 for (int j = 1; j < (int)input_indexes.size(); j++)
59 r = apply(r, memory_cells[input_indexes[j]]);
60 return r;
61 }
62 };
63
64 int input_size;
65 int total_inputs;
66 vector<Instruction> instructions;
67
68 public:
69
70 inline void init(int _input_size)
71 {
72 this->input_size = _input_size;
73 this->total_inputs = 0;
74 this->instructions.clear();
75 }
76
77 inline int add_instruction(int type, const vector<int>& input_indexes)
78 {
79 if (input_indexes.size() == 0)
80 error("Instruction with no inputs");
81
82 if (instructions.size() + 1 > MAX_INSTRUCTIONS)
83 error("Too many instructions");
84
85 if (total_inputs + input_indexes.size() > MAX_INPUTS)
86 error("Too many inputs");
87
88 instructions.emplace_back(type, input_indexes);
89 total_inputs += input_indexes.size();
90 int new_index = input_size + (int)instructions.size() - 1;
91
92 for (int input_index : input_indexes)
CAPITOLUL 2. IOI 2019 221

93 if (!increasing(0, input_index, new_index-1))


94 error("Invalid index");
95
96 return new_index;
97 }
98
99 inline int compute(vector<int> &memory_cells) const
100 {
101 for (auto &instruction : instructions)
102 memory_cells.push_back(instruction.compute(memory_cells));
103 return memory_cells.back();
104 }
105 };
106
107
108 static InstructionNetwork instructionNetwork;
109
110 int main()
111 {
112 int H, W, K;
113 assert(3 == scanf("%d%d%d", &H, &W, &K));
114
115 FILE *log_file = fopen("log.txt","w");
116
117 instructionNetwork.init(H * W);
118 construct_network(H, W, K);
119
120 while (true)
121 {
122 int rowA, colA, rowB, colB;
123 assert(1 == scanf("%d", &rowA));
124 if (rowA == -1)
125 break;
126 assert(3 == scanf("%d%d%d", &colA, &rowB, &colB));
127
128 if ((!increasing(0, rowA, H-1)) ||
129 (!increasing(0, colA, W-1)) ||
130 (!increasing(0, rowB, H-1)) ||
131 (!increasing(0, colB, W-1)) ||
132 (rowA == rowB && colA == colB))
133 {
134 printf("-1\n");
135 fprintf(log_file, "-1\n");
136 fflush(stdout);
137 fflush(log_file);
138 continue;
139 }
140
141 vector<int> memory_cells;
142 for (int row = 0; row < H; row++)
143 for (int col = 0; col < W; col++)
144 {
145 bool active = (row == rowA && col == colA) ||
146 (row == rowB && col == colB);
147 memory_cells.push_back(active ? 1 : 0);
148 }
149 int computation_result = instructionNetwork.compute(memory_cells);
150
151 printf("%d\n", computation_result);
152 fflush(stdout);
153
154 for(int i = 0; i < (int)memory_cells.size(); i++)
155 fprintf(log_file, (i ? " %d" : "%d"), memory_cells[i]);
156 fprintf(log_file, "\n");
157 fflush(log_file);
158 }
159 fclose(stdin);
160 }
161
162 int add_and(vector<int> Ns)
163 {
164 return instructionNetwork.add_instruction(_AND, Ns);
165 }
166
167 int add_or(vector<int> Ns)
168 {
CAPITOLUL 2. IOI 2019 222

169 return instructionNetwork.add_instruction(_OR, Ns);


170 }
171
172 int add_xor(vector<int> Ns)
173 {
174 return instructionNetwork.add_instruction(_XOR, Ns);
175 }
176
177 int add_not(int N)
178 {
179 vector<int> Ns = {N};
180 return instructionNetwork.add_instruction(_NOT, Ns);
181 }

Listing 2.5.5: vision-model.cpp


1 #include "vision.h"
2
3 using namespace std;
4
5 inline int OR(vector<int> &Ns)
6 {
7 return add_or(Ns);
8 }
9
10 inline int XOR(vector<int> &Ns)
11 {
12 return add_xor(Ns);
13 }
14
15 inline int XOR(int A, int B)
16 {
17 return add_xor({A, B});
18 }
19
20 inline int AND(int A, int B)
21 {
22 return add_and({A, B});
23 }
24
25 inline int AND_NOT(int A, int B)
26 {
27 return AND(A, add_not(B));
28 }
29
30 vector<int> diagonal(int H, int W, int N, bool D)
31 {
32 vector<int> cells;
33 int first, last, step;
34 if (D)
35 {
36 first = N < W ? N : W * (N - W + 2) - 1;
37 last = N < H ? N * W : (H - 1) * W + (N - H + 1);
38 step = W - 1;
39 }
40 else
41 {
42 first = N < W ? W - 1 - N : W * (N - W + 1);
43 last = N < H ? (N + 1) * W - 1 : H * W + (H - 2 - N);
44 step = W + 1;
45 }
46 int cur = first;
47 while (true)
48 {
49 cells.emplace_back(cur);
50 if (cur == last)
51 {
52 break;
53 }
54 cur += step;
55 }
56 return cells;
57 }
58
59 void collect_diagonals(int H, int W, bool D, vector<int> &ors, vector<int> &xors)
CAPITOLUL 2. IOI 2019 223

60 {
61 for (int i = 0; i < H + W - 1; i++)
62 {
63 vector<int> cells = diagonal(H, W, i, D);
64 ors.emplace_back(OR(cells));
65 xors.emplace_back(XOR(cells));
66 }
67 }
68
69 int find_pair(const vector<int> &ors, const vector<int> &xors, int maxDistance)
70 {
71 int n = maxDistance + 1;
72 vector<int> curOrs(n), curXors(n), results;
73 for (int i = 0; i < (int)ors.size(); i++)
74 {
75 curOrs[i % n] = ors[i];
76 curXors[i % n] = xors[i];
77 if (i >= maxDistance)
78 {
79 results.emplace_back(AND_NOT(OR(curOrs), XOR(curXors)));
80 }
81 }
82 return OR(results);
83 }
84
85 void construct_network(int H, int W, int K)
86 {
87 vector<int> orsA, xorsA, orsB, xorsB;
88 collect_diagonals(H, W, true, orsA, xorsA);
89 collect_diagonals(H, W, false, orsB, xorsB);
90 AND_NOT(AND(find_pair(orsA, xorsA, K),
91 find_pair(orsB, xorsB, K)),
92 AND(find_pair(orsA, xorsA, K - 1),
93 find_pair(orsB, xorsB, K - 1)));
94 }

2.5.3 *Rezolvare detaliat 

2.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 a  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 , aat 
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.
Î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.
CAPITOLUL 2. IOI 2019 224

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  apelat  de c tre grader câte o dat 
pentru ecare 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 2.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

a 1 & n, m & 100000


0 & x0 $ x1 $ ... $ xn  1 & 10
9
a
1 & hi & 10 (pentru 0 & i & n  1)
9
a
CAPITOLUL 2. IOI 2019 225

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

2.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 nd 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
eciently. 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).
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.
CAPITOLUL 2. IOI 2019 226

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 rst 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 rst 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. Specically, 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 rst 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.

2.6.2 Coduri surs 

Listing 2.6.1: compile_cpp.sh


1 #!/bin/bash
2
3 problem=walk
4
5 g++ -std=gnu++14 -O2 -Wall -pipe -static -o "${problem}"
6
7 "grader.cpp" "${problem}.cpp"

Listing 2.6.2: walk.h


1 #include <vector>
2
3 long long min_distance(std::vector<int> x, std::vector<int> h,
4 std::vector<int> l, std::vector<int> r,
5 std::vector<int> y, int s, int g);

Listing 2.6.3: walk.cpp


1 #include "walk.h"
2
3 long long min_distance(std::vector<int> x, std::vector<int> h,
4 std::vector<int> l, std::vector<int> r,
5 std::vector<int> y, int s, int g)
6 {
7 return 1;
8 }
CAPITOLUL 2. IOI 2019 227

Listing 2.6.4: grader.cpp


1 #include "walk.h"
2 #include <cstdio>
3 #include <cassert>
4
5 using namespace std;
6
7 int main()
8 {
9 int n, m;
10 assert(2 == scanf("%d%d", &n, &m));
11 vector<int> x(n), h(n);
12 for (int i = 0; i < n; i++)
13 assert(2 == scanf("%d%d", &x[i], &h[i]));
14 vector<int> l(m), r(m), y(m);
15 for (int i = 0; i < m; i++)
16 assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
17 int s, g;
18 assert(2 == scanf("%d%d", &s, &g));
19 fclose(stdin);
20
21 long long result = min_distance(x, h, l, r, y, s, g);
22
23 printf("%lld\n", result);
24 fclose(stdout);
25 return 0;
26 }

Listing 2.6.5: walk-model.cpp


1 #include "walk.h"
2 #include <iostream>
3 #include <vector>
4 #include <algorithm>
5 #include <set>
6 #include <cassert>
7
8 using namespace std;
9
10 const int MAXN = 200 * 1000 + 10;
11 const int MAXM = 200 * 1000 + 10;
12 const int MAXRM = 5 * MAXM;
13 const int MAXV = 2 * 2 * MAXRM;
14 const long long INF = 1000ll * 1000 * 1000 * 1000 * 1000 * 1000;
15
16 typedef pair<int, int> pii;
17 typedef set<pii>::iterator sit;
18
19 typedef pair<long long, int> pli;
20 long long dist[MAXV];
21
22 int vcnt = 0;
23 vector<pii> edges[MAXV];
24 set<pli> dij;
25 int last_x[MAXRM];
26 int last_vertex[MAXRM];
27 int last_height[MAXN];
28 int first_height[MAXN];
29 vector<pii> adds[MAXN];
30 vector<pii> removes[MAXN];
31 set<pii> walks;
32
33 long long dijkstra(int start, int dest)
34 {
35 for (int i = 0; i < vcnt; i++)
36 dist[i] = INF;
37 dist[start] = 0;
38 dij.insert(pli(dist[start], start));
39 while (!dij.empty())
40 {
41 int v = dij.begin()->second;
42 dij.erase(dij.begin());
43 if (v == dest)
44 return dist[v];
CAPITOLUL 2. IOI 2019 228

45 for (int i = 0; i < (int)edges[v].size(); i++)


46 {
47 int u = edges[v][i].first;
48 int w = edges[v][i].second;
49 if (dist[u] > dist[v] + w)
50 {
51 dij.erase(pli(dist[u], u));
52 dist[u] = dist[v] + w;
53 dij.insert(pli(dist[u], u));
54 }
55 }
56 }
57 return -1;
58 }
59
60 void add_edge(int u, int v, int w)
61 {
62 edges[u].push_back(pii(v, w));
63 edges[v].push_back(pii(u, w));
64 }
65
66 int make_vertex(int walk, int x)
67 {
68 if (last_x[walk] == x)
69 return last_vertex[walk];
70 int cur = vcnt++;
71 if (last_vertex[walk] != -1)
72 add_edge(last_vertex[walk], cur, x - last_x[walk]);
73 last_x[walk] = x;
74 last_vertex[walk] = cur;
75 return cur;
76 }
77
78 void break_segments(vector<int> &l, vector<int> &r,
79 vector<int> &y, int s, vector<int> &h)
80 {
81 vector<int> heights = h;
82 sort(heights.begin(), heights.end());
83 heights.resize(unique(heights.begin(),
84 heights.end()) - heights.begin());
85 for (int i = 0; i < (int)heights.size(); i++)
86 {
87 last_height[i] = -1;
88 first_height[i] = h.size() + 1;
89 }
90 for (int i = 0; i < (int)h.size(); i++)
91 {
92 int idx = lower_bound(heights.begin(), heights.end(),
93 h[i]) - heights.begin();
94 assert(idx < (int)heights.size());
95 if (i <= s)
96 last_height[idx] = max(last_height[idx], i);
97 if (i >= s)
98 first_height[idx] = min(first_height[idx], i);
99 }
100 for (int i = (int)heights.size() - 2; i >= 0; i--)
101 {
102 last_height[i] = max(last_height[i], last_height[i + 1]);
103 first_height[i] = min(first_height[i], first_height[i + 1]);
104 }
105 for (int i = 0; i < (int)l.size(); i++)
106 if (l[i] < s && r[i] > s)
107 {
108 int idx = lower_bound(heights.begin(), heights.end(),
109 y[i]) - heights.begin();
110 assert(idx < (int)heights.size());
111 int x = l[i];
112 int a = last_height[idx];
113 int b = first_height[idx];
114 int q = r[i];
115
116 assert(a != -1);
117 assert(b != (int)h.size() + 1);
118
119 if (x < a)
120 {
CAPITOLUL 2. IOI 2019 229

121 r[i] = a;
122 if (a < b)
123 {
124 l.push_back(a);
125 r.push_back(b);
126 y.push_back(y[i]);
127 }
128 }
129 else
130 {
131 assert(x == a);
132 r[i] = b;
133 }
134 if (q > b)
135 {
136 l.push_back(b);
137 r.push_back(q);
138 y.push_back(y[i]);
139 }
140 else
141 {
142 assert(q == b);
143 }
144 }
145 }
146
147 long long min_distance(vector<int> x, vector<int> h,
148 vector<int> l, vector<int> r,
149 vector<int> y, int s, int g)
150 {
151 if (s == g)
152 return 0;
153 int n = x.size();
154
155 break_segments(l, r, y, s, h);
156 break_segments(l, r, y, g, h);
157 int m = l.size();
158
159 for (int i = 0; i < m; i++)
160 {
161 last_x[i] = last_vertex[i] = -1;
162 adds[l[i]].push_back(pii(y[i], i));
163 removes[r[i]].push_back(pii(y[i], i));
164 }
165 int sv = -1, gv = -1;
166 long long res = 0;
167 for (int i = 0; i < n; i++)
168 {
169 sort(adds[i].begin(), adds[i].end());
170 for (int j = 0; j < (int)adds[i].size(); j++)
171 {
172 int v = make_vertex(adds[i][j].second, x[i]);
173 sit it = walks.lower_bound(adds[i][j]);
174 if (it != walks.begin())
175 {
176 it--;
177 int u = make_vertex(it->second, x[i]);
178 add_edge(u, v, adds[i][j].first - it->first);
179 }
180 walks.insert(adds[i][j]);
181 }
182 if (i == s)
183 {
184 if (walks.empty() || walks.begin()->first > h[i])
185 return -1;
186 sv = make_vertex(walks.begin()->second, x[i]);
187 res += walks.begin()->first;
188 }
189 if (i == g)
190 {
191 if (walks.empty() || walks.begin()->first > h[i])
192 return -1;
193 gv = make_vertex(walks.begin()->second, x[i]);
194 res += walks.begin()->first;
195 }
196 sort(removes[i].begin(), removes[i].end(), greater<pii>());
CAPITOLUL 2. IOI 2019 230

197 for (int j = 0; j < (int)removes[i].size(); j++)


198 {
199 int v = make_vertex(removes[i][j].second, x[i]);
200 sit it = walks.find(removes[i][j]);
201 if (it != walks.begin())
202 {
203 sit it2 = it;
204 it2--;
205 int u = make_vertex(it2->second, x[i]);
206 add_edge(u, v, removes[i][j].first - it2->first);
207 }
208 assert(it != walks.end());
209 walks.erase(it);
210 }
211 }
212 assert(sv != -1);
213 assert(gv != -1);
214 long long dij_res = dijkstra(sv, gv);
215 if(dij_res == -1)
216 return -1;
217 return res + dij_res;
218 }

Listing 2.6.6: akm-full+grader.cpp


1 #include "walk.h" // execution time : 3.234 s
2 #include <iostream>
3 #include <vector>
4 #include <algorithm>
5 #include <set>
6 #include <cassert>
7 #include <cstdio>
8 #include <ctime>
9
10 using namespace std;
11
12 const int MAXN = 200 * 1000 + 10;
13 const int MAXM = 200 * 1000 + 10;
14 const int MAXRM = 5 * MAXM;
15 const int MAXV = 2 * 2 * MAXRM;
16 const long long INF = 1000ll * 1000 * 1000 * 1000 * 1000 * 1000;
17
18 typedef pair<int, int> pii;
19 typedef set<pii>::iterator sit;
20
21 typedef pair<long long, int> pli;
22 long long dist[MAXV];
23
24 int vcnt = 0;
25 vector<pii> edges[MAXV];
26 set<pli> dij;
27
28 int last_x[MAXRM];
29 int last_vertex[MAXRM];
30 int last_height[MAXN];
31 int first_height[MAXN];
32
33 vector<pii> adds[MAXN];
34 vector<pii> removes[MAXN];
35 set<pii> walks;
36
37 long long dijkstra(int start, int dest)
38 {
39 for (int i = 0; i < vcnt; i++)
40 dist[i] = INF;
41 dist[start] = 0;
42 dij.insert(pli(dist[start], start));
43 while (!dij.empty())
44 {
45 int v = dij.begin()->second;
46 dij.erase(dij.begin());
47 if (v == dest)
48 return dist[v];
49 for (int i = 0; i < (int)edges[v].size(); i++)
50 {
CAPITOLUL 2. IOI 2019 231

51 int u = edges[v][i].first;
52 int w = edges[v][i].second;
53 if (dist[u] > dist[v] + w)
54 {
55 dij.erase(pli(dist[u], u));
56 dist[u] = dist[v] + w;
57 dij.insert(pli(dist[u], u));
58 }
59 }
60 }
61 return -1;
62 }
63
64 void add_edge(int u, int v, int w)
65 {
66 edges[u].push_back(pii(v, w));
67 edges[v].push_back(pii(u, w));
68 }
69
70 int make_vertex(int walk, int x)
71 {
72 if (last_x[walk] == x)
73 return last_vertex[walk];
74 int cur = vcnt++;
75 if (last_vertex[walk] != -1)
76 add_edge(last_vertex[walk], cur, x - last_x[walk]);
77 last_x[walk] = x;
78 last_vertex[walk] = cur;
79 return cur;
80 }
81
82 void break_segments(vector<int> &l, vector<int> &r,
83 vector<int> &y, int s, vector<int> &h)
84 {
85 vector<int> heights = h;
86 sort(heights.begin(), heights.end());
87 heights.resize(unique(heights.begin(),
88 heights.end()) - heights.begin());
89 for (int i = 0; i < (int)heights.size(); i++)
90 {
91 last_height[i] = -1;
92 first_height[i] = h.size() + 1;
93 }
94
95 for (int i = 0; i < (int)h.size(); i++)
96 {
97 int idx = lower_bound(heights.begin(), heights.end(),
98 h[i]) - heights.begin();
99 assert(idx < (int)heights.size());
100 if (i <= s)
101 last_height[idx] = max(last_height[idx], i);
102 if (i >= s)
103 first_height[idx] = min(first_height[idx], i);
104 }
105
106 for (int i = (int)heights.size() - 2; i >= 0; i--)
107 {
108 last_height[i] = max(last_height[i], last_height[i + 1]);
109 first_height[i] = min(first_height[i], first_height[i + 1]);
110 }
111
112 for (int i = 0; i < (int)l.size(); i++)
113 if (l[i] < s && r[i] > s)
114 {
115 int idx = lower_bound(heights.begin(), heights.end(),
116 y[i]) - heights.begin();
117 assert(idx < (int)heights.size());
118 int x = l[i];
119 int a = last_height[idx];
120 int b = first_height[idx];
121 int q = r[i];
122
123 assert(a != -1);
124 assert(b != (int)h.size() + 1);
125
126 if (x < a)
CAPITOLUL 2. IOI 2019 232

127 {
128 r[i] = a;
129 if (a < b)
130 {
131 l.push_back(a);
132 r.push_back(b);
133 y.push_back(y[i]);
134 }
135 }
136 else
137 {
138 assert(x == a);
139 r[i] = b;
140 }
141 if (q > b)
142 {
143 l.push_back(b);
144 r.push_back(q);
145 y.push_back(y[i]);
146 }
147 else
148 {
149 assert(q == b);
150 }
151 }
152 }
153
154 long long min_distance(vector<int> x, vector<int> h,
155 vector<int> l, vector<int> r,
156 vector<int> y, int s, int g)
157 {
158 if (s == g)
159 return 0;
160 int n = x.size();
161
162 break_segments(l, r, y, s, h);
163 break_segments(l, r, y, g, h);
164 int m = l.size();
165
166 for (int i = 0; i < m; i++)
167 {
168 last_x[i] = last_vertex[i] = -1;
169 adds[l[i]].push_back(pii(y[i], i));
170 removes[r[i]].push_back(pii(y[i], i));
171 }
172
173 int sv = -1, gv = -1;
174 long long res = 0;
175 for (int i = 0; i < n; i++)
176 {
177 sort(adds[i].begin(), adds[i].end());
178 for (int j = 0; j < (int)adds[i].size(); j++)
179 {
180 int v = make_vertex(adds[i][j].second, x[i]);
181 sit it = walks.lower_bound(adds[i][j]);
182 if (it != walks.begin())
183 {
184 it--;
185 int u = make_vertex(it->second, x[i]);
186 add_edge(u, v, adds[i][j].first - it->first);
187 }
188 walks.insert(adds[i][j]);
189 }
190
191 if (i == s)
192 {
193 if (walks.empty() || walks.begin()->first > h[i])
194 return -1;
195 sv = make_vertex(walks.begin()->second, x[i]);
196 res += walks.begin()->first;
197 }
198
199 if (i == g)
200 {
201 if (walks.empty() || walks.begin()->first > h[i])
202 return -1;
CAPITOLUL 2. IOI 2019 233

203 gv = make_vertex(walks.begin()->second, x[i]);


204 res += walks.begin()->first;
205 }
206
207 sort(removes[i].begin(), removes[i].end(), greater<pii>());
208
209 for (int j = 0; j < (int)removes[i].size(); j++)
210 {
211 int v = make_vertex(removes[i][j].second, x[i]);
212 sit it = walks.find(removes[i][j]);
213 if (it != walks.begin())
214 {
215 sit it2 = it;
216 it2--;
217 int u = make_vertex(it2->second, x[i]);
218 add_edge(u, v, removes[i][j].first - it2->first);
219 }
220 assert(it != walks.end());
221 walks.erase(it);
222 }
223 }
224
225 assert(sv != -1);
226 assert(gv != -1);
227
228 long long dij_res = dijkstra(sv, gv);
229
230 if(dij_res == -1)
231 return -1;
232 return res + dij_res;
233 }
234
235 //-------------- start grader ------------------
236
237 int main()
238 {
239 std::freopen("../tests/5-30.in", "r", stdin) ; // execution time : 3.199 s
240 std::freopen("walking.out", "w", stdout) ;
241
242 auto t1 = clock();
243
244 int n, m;
245 assert(2 == scanf("%d%d", &n, &m));
246 cerr<<"cerr : "<<n<<" "<<m<<"\n";
247
248 vector<int> x(n), h(n);
249 for (int i = 0; i < n; i++)
250 assert(2 == scanf("%d%d", &x[i], &h[i]));
251
252 vector<int> l(m), r(m), y(m);
253 for (int i = 0; i < m; i++)
254 assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
255 int s, g;
256 assert(2 == scanf("%d%d", &s, &g));
257 fclose(stdin);
258
259 auto t2 = clock();
260
261 long long result = min_distance(x, h, l, r, y, s, g);
262
263 auto t3 = clock();
264
265 printf("%lld\n", result);
266 fclose(stdout);
267
268 auto t4 = clock();
269
270 // reset console output
271 freopen("CON", "w", stdout);
272
273 std::cout <<"result = "<<result<<’\n’<<’\n’;
274
275 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
276 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
277 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
278
CAPITOLUL 2. IOI 2019 234

279 return 0;
280 }
281 //-------------- end grader ------------------

Listing 2.6.7: maroon-full+grader.cpp


1 #include <bits/stdc++.h> // execution time : 4.156 s
2
3 using namespace std;
4
5 using ll = int64_t;
6
7 #define FOR(i, a, b) for (int i = int(a); i < int(b); i++)
8 #define REP(i, b) FOR(i, 0, b)
9 #define MP make_pair
10 #define PB push_back
11 #define EB emplace_back
12 #define ALL(x) x.begin(), x.end()
13
14 auto &errStream = cerr;
15
16 #ifdef LOCAL
17 #define cerr (cerr << "-- line " << __LINE__ << " -- ")
18 #else
19 class CerrDummy
20 {
21 } cerrDummy;
22 template <class T>
23 CerrDummy &operator<<(CerrDummy &cd, const T &)
24 {
25 return cd;
26 }
27 using charTDummy = char;
28 using traitsDummy = char_traits<charTDummy>;
29 CerrDummy &operator<<(CerrDummy &cd, basic_ostream<charTDummy,
30 traitsDummy> &(basic_ostream<charTDummy,
31 traitsDummy> &))
32 {
33 return cd;
34 }
35 #define cerr cerrDummy
36 #endif
37
38 #define REACH cerr << "reached" << endl
39 #define DMP(x) cerr << #x << ":" << x << endl
40 #define ZERO(x) memset(x, 0, sizeof(x))
41 #define ONE(x) memset(x, -1, sizeof(x))
42
43 using pi = pair<int, int>;
44 using vi = vector<int>;
45 using ld = long double;
46
47 template <class T, class U>
48 ostream &operator<<(ostream &os, const pair<T, U> &p)
49 {
50 os << "(" << p.first << "," << p.second << ")";
51 return os;
52 }
53
54 template <class T>
55 ostream &operator<<(ostream &os, const vector<T> &v)
56 {
57 os << "{";
58 REP(i, (int)v.size())
59 {
60 if (i)
61 os << ",";
62 os << v[i];
63 }
64 os << "}";
65 return os;
66 }
67
68 ll read()
69 {
CAPITOLUL 2. IOI 2019 235

70 ll i;
71 scanf("%" SCNd64, &i);
72 return i;
73 }
74
75 void printSpace()
76 {
77 printf(" ");
78 }
79
80 void printEoln()
81 {
82 printf("\n");
83 }
84
85 void print(ll x, int suc = 1)
86 {
87 printf("%" PRId64, x);
88 if (suc == 1)
89 printEoln();
90 if (suc == 2)
91 printSpace();
92 }
93
94 string readString()
95 {
96 static char buf[3341000];
97 scanf("%s", buf);
98 return string(buf);
99 }
100
101 char *readCharArray()
102 {
103 static char buf[3341000];
104 static int bufUsed = 0;
105 char *ret = buf + bufUsed;
106 scanf("%s", ret);
107 bufUsed += strlen(ret) + 1;
108 return ret;
109 }
110
111 template <class T, class U>
112 void chmax(T &a, U b)
113 {
114 if (a < b)
115 a = b;
116 }
117
118 template <class T, class U>
119 void chmin(T &a, U b)
120 {
121 if (b < a)
122 a = b;
123 }
124
125 template <class T>
126 T Sq(const T &t)
127 {
128 return t * t;
129 }
130
131 const ll infLL = LLONG_MAX / 3;
132
133 #ifdef int
134 const int inf = infLL;
135 #else
136 const int inf = INT_MAX / 2 - 100;
137 #endif
138
139 namespace Subtask4
140 {
141 ll Solve(vi x, vi h, vi l, vi r, vi y, int s, int g)
142 {
143 int n = x.size();
144 REP(k, 2)
145 {
CAPITOLUL 2. IOI 2019 236

146 int w = k == 0 ? s : g;
147 vector<pi> p{pi(h[w], w)}, q{pi(h[w], w)};
148
149 for (int i = w - 1; i >= 0; i--)
150 if (h[i] > p.back().first)
151 p.EB(h[i], i);
152
153 for (int i = w + 1; i < n; i++)
154 if (h[i] > q.back().first)
155 q.EB(h[i], i);
156
157 vi ll, rr, yy;
158 REP(i, l.size())
159 {
160 if (l[i] < w && w < r[i])
161 {
162 int a, b;
163 {
164 auto itr = lower_bound(ALL(p), pi(y[i], -1));
165 assert(itr != p.end());
166 a = itr->second;
167 }
168 {
169 auto itr = lower_bound(ALL(q), pi(y[i], -1));
170 assert(itr != q.end());
171 b = itr->second;
172 }
173 if (l[i] < a)
174 {
175 ll.PB(l[i]);
176 rr.PB(a);
177 yy.PB(y[i]);
178 }
179 if (a < b)
180 {
181 ll.PB(a);
182 rr.PB(b);
183 yy.PB(y[i]);
184 }
185 if (b < r[i])
186 {
187 ll.PB(b);
188 rr.PB(r[i]);
189 yy.PB(y[i]);
190 }
191 }
192 else
193 {
194 ll.PB(l[i]);
195 rr.PB(r[i]);
196 yy.PB(y[i]);
197 }
198 }
199 l = ll;
200 r = rr;
201 y = yy;
202 }
203
204 int m = l.size();
205
206 vector<pi> posYX{pi(0, x[s]), pi(0, x[g])};
207 {
208 vector<tuple<int, int, int>> xty;
209 REP(i, m)
210 {
211 xty.EB(x[l[i]], 0, y[i]);
212 xty.EB(x[r[i]], 1, -y[i]);
213 }
214
215 sort(ALL(xty));
216
217 multiset<int> ys;
218 for (auto q : xty)
219 {
220 int j=get<0>(q), t=get<1>(q), i = get<2>(q) * (t == 0 ? 1 : -1);
221 posYX.EB(i, j);
CAPITOLUL 2. IOI 2019 237

222 auto itr = ys.lower_bound(i);


223 if (itr != ys.begin())
224 {
225 --itr;
226 posYX.EB( *itr, j);
227 }
228 if (t == 0)
229 ys.insert(i);
230 else
231 ys.erase(ys.find(i));
232 }
233
234 sort(ALL(posYX));
235 posYX.erase(unique(ALL(posYX)), posYX.end());
236 }
237
238 const auto Idx = [&](int i, int j)
239 {
240 return lower_bound(ALL(posYX), pi(i, j)) - posYX.begin();
241 };
242
243 int vs = posYX.size();
244 vector<vector<pi>> graph(vs);
245 const auto AddEdge = [&](int a, int b, int c)
246 {
247 graph[a].EB(b, c);
248 graph[b].EB(a, c);
249 };
250
251 REP(i, m)
252 {
253 int k = Idx(y[i], x[l[i]]);
254 while (k + 1 < int(posYX.size()) && posYX[k + 1] <= pi(y[i], x[r[i]]))
255 {
256 AddEdge(k, k + 1, posYX[k + 1].second - posYX[k].second);
257 k++;
258 }
259 }
260
261 vector<pi> posXY = posYX;
262 for (auto &p : posXY)
263 swap(p.first, p.second);
264
265 sort(ALL(posXY));
266
267 REP(i, vs - 1)
268 {
269 if (posXY[i].first == posXY[i + 1].first)
270 {
271 AddEdge(Idx(posXY[i].second, posXY[i].first),
272 Idx(posXY[i + 1].second, posXY[i + 1].first),
273 posXY[i + 1].second - posXY[i].second);
274 }
275 }
276
277 vector<ll> dist(vs, infLL);
278
279 using pli = tuple<ll, int>;
280
281 priority_queue<pli, vector<pli>, greater<pli>> pq;
282
283 const auto Reach = [&](int v, ll d)
284 {
285 if (dist[v] > d)
286 {
287 dist[v] = d;
288 pq.push(pli(d, v));
289 }
290 };
291
292 Reach(Idx(0, x[s]), 0);
293 while (!pq.empty())
294 {
295 int v;
296 ll d;
297 tie(d, v) = pq.top();
CAPITOLUL 2. IOI 2019 238

298 pq.pop();
299 if (dist[v] != d)
300 continue;
301 for (auto e : graph[v])
302 Reach(e.first, dist[v] + e.second);
303 }
304
305 return dist[Idx(0, x[g])] == infLL ? -1 : dist[Idx(0, x[g])];
306 }
307 } // namespace Subtask4
308
309 long long min_distance(std::vector<int> X, std::vector<int> H,
310 std::vector<int> L, std::vector<int> R,
311 std::vector<int> Y, int S, int G)
312 {
313 return Subtask4::Solve(X, H, L, R, Y, S, G);
314 }
315
316 //-------------- start grader ------------------
317
318 int main()
319 {
320 std::freopen("../tests/5-30.in", "r", stdin) ;
321 std::freopen("walking.out", "w", stdout) ;
322
323 auto t1 = clock();
324
325 int n, m;
326 assert(2 == scanf("%d%d", &n, &m));
327 cerr<<"cerr : "<<n<<" "<<m<<"\n";
328
329 vector<int> x(n), h(n);
330 for (int i = 0; i < n; i++)
331 assert(2 == scanf("%d%d", &x[i], &h[i]));
332
333 vector<int> l(m), r(m), y(m);
334 for (int i = 0; i < m; i++)
335 assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
336 int s, g;
337 assert(2 == scanf("%d%d", &s, &g));
338 fclose(stdin);
339
340 auto t2 = clock();
341
342 long long result = min_distance(x, h, l, r, y, s, g);
343
344 auto t3 = clock();
345
346 printf("%lld\n", result);
347 fclose(stdout);
348
349 auto t4 = clock();
350
351 // reset console output
352 freopen("CON", "w", stdout);
353
354 std::cout <<"result = "<<result<<’\n’<<’\n’;
355
356 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
357 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
358 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
359
360 return 0;
361 }
362 //-------------- end grader ------------------

Listing 2.6.8: mohammad-full-walk+grader.cpp


1 #include <iostream> // execution time : 6.042 s
2 #include <vector>
3 #include <memory.h>
4 #include <algorithm>
5 #include <set>
6 #include <map>
7
CAPITOLUL 2. IOI 2019 239

8 #include <ctime>
9 #include <cassert>
10
11 #define pb push_back
12 #define mp make_pair
13
14 using namespace std;
15
16 const int maxn = 1000000 + 10;
17 const long long inf = 1e18;
18
19 int n, m, X[maxn], Y[maxn], H[maxn], L[maxn], R[maxn],
20 a[maxn], b[maxn], node, y_map[maxn];
21
22 vector<int> neiL[maxn], neiR[maxn];
23
24 long long dis[maxn];
25
26 vector<pair<int, long long>> adj[maxn];
27 map<pair<int, int>, int> nodes;
28 vector<int> all_y;
29
30 void devide(int idx)
31 {
32 memset(a, -1, sizeof(a));
33 memset(b, -1, sizeof(b));
34
35 vector<pair<pair<int, int>, int>> sky;
36 vector<pair<int, int>> st;
37
38 st.pb(mp(H[idx], idx));
39 for (int i = idx - 1; i >= 0; i--)
40 {
41 if (H[i] > st.back().first)
42 st.push_back(mp(H[i], i));
43 for (int j = 0; j < neiL[i].size(); j++)
44 {
45 int cur = neiL[i][j];
46 if (R[cur] > idx)
47 {
48 int pos = upper_bound(st.begin(), st.end(),
49 mp(Y[cur], -1)) - st.begin();
50 a[cur] = st[pos].second;
51 }
52 }
53 }
54 st.clear();
55 st.pb(mp(H[idx], idx));
56 for (int i = idx + 1; i < n; i++)
57 {
58 if (H[i] > st.back().first)
59 st.push_back(mp(H[i], i));
60 for (int j = 0; j < neiR[i].size(); j++)
61 {
62 int cur = neiR[i][j];
63 if (L[cur] < idx)
64 {
65 int pos = upper_bound(st.begin(), st.end(),
66 mp(Y[cur], -1)) - st.begin();
67 b[cur] = st[pos].second;
68 }
69 }
70 }
71
72 for (int i = 0; i < m; i++)
73 {
74 if (a[i] != -1)
75 {
76 if (a[i] != L[i])
77 sky.pb(mp(mp(L[i], a[i]), Y[i]));
78 if (a[i] != b[i])
79 sky.pb(mp(mp(a[i], b[i]), Y[i]));
80 if (b[i] != R[i])
81 sky.pb(mp(mp(b[i], R[i]), Y[i]));
82 }
83 else
CAPITOLUL 2. IOI 2019 240

84 sky.pb(mp(mp(L[i], R[i]), Y[i]));


85 }
86
87 for (int i = 0; i < n; i++)
88 {
89 neiL[i].clear();
90 neiR[i].clear();
91 }
92
93 m = sky.size();
94 for (int i = 0; i < m; i++)
95 {
96 L[i] = sky[i].first.first;
97 R[i] = sky[i].first.second;
98 Y[i] = sky[i].second;
99 neiL[L[i]].pb(i);
100 neiR[R[i]].pb(i);
101 }
102 }
103
104 long long dijkstra(int source, int sink)
105 {
106 set<pair<long long, int>> S;
107 for (int i = 0; i <= node; i++)
108 dis[i] = inf;
109 dis[source] = 0;
110 S.insert(mp(0, source));
111 while (S.size())
112 {
113 set<pair<long long, int>>::iterator it = S.begin();
114 long long dist = ( *it).first;
115 int v = ( *it).second;
116 S.erase(it);
117 for (int i = 0; i < adj[v].size(); i++)
118 {
119 int u = adj[v][i].first;
120 long long w = adj[v][i].second;
121 if (dist + w < dis[u])
122 {
123 S.erase(mp(dis[u], u));
124 dis[u] = dist + w;
125 S.insert(mp(dis[u], u));
126 }
127 }
128 }
129
130 if (dis[sink] == inf)
131 return -1;
132 return dis[sink];
133 }
134
135 int add_map(int x, int y)
136 {
137 if (!nodes[mp(x, y)])
138 nodes[mp(x, y)] = ++node;
139 return nodes[mp(x, y)];
140 }
141
142 void add_edge(int x1, int y1, int x2, int y2)
143 {
144 int u = add_map(x1, y1);
145 int v = add_map(x2, y2);
146
147 if (x1 == x2)
148 {
149 adj[u].push_back(mp(v, abs(y2 - y1)));
150 adj[v].push_back(mp(u, abs(y2 - y1)));
151 }
152 else
153 {
154 adj[u].push_back(mp(v, abs(x2 - x1)));
155 adj[v].push_back(mp(u, abs(x2 - x1)));
156 }
157 }
158
159 void build_graph()
CAPITOLUL 2. IOI 2019 241

160 {
161 set<int> line;
162 line.insert(0);
163 set<int>::iterator it;
164 map<int, int> st;
165 vector<int> set_res;
166
167 memset(y_map, -1, sizeof(y_map));
168
169 for (int i = 0; i < n; i++)
170 {
171 for (int j = 0; j < neiL[i].size(); j++)
172 {
173 int cur = neiL[i][j];
174 line.insert(-Y[cur]);
175 }
176
177 for (int j = 0; j < neiL[i].size(); j++)
178 {
179 int cur = neiL[i][j];
180 it = line.upper_bound(-Y[cur]);
181 int y2 = Y[cur];
182 int y1 = -( *it);
183 int y2_id = lower_bound(all_y.begin(), all_y.end(), y2) -
184 all_y.begin() + 1;
185 int y1_id = lower_bound(all_y.begin(), all_y.end(), y1) -
186 all_y.begin() + 1;
187 set_res.pb(y1_id);
188 set_res.pb(y2_id);
189 add_edge(X[i], y1, X[i], y2);
190 if (y_map[y1_id] != -1 && y1 != 0)
191 {
192 add_edge(X[i], y1, y_map[y1_id], y1);
193 }
194 }
195
196 for (int j = 0; j < neiR[i].size(); j++)
197 {
198 int cur = neiR[i][j];
199 it = line.upper_bound(-Y[cur]);
200 int y2 = Y[cur];
201 int y1 = -( *it);
202 int y2_id = lower_bound(all_y.begin(), all_y.end(), y2) -
203 all_y.begin() + 1;
204 int y1_id = lower_bound(all_y.begin(), all_y.end(), y1) -
205 all_y.begin() + 1;
206 set_res.pb(y1_id);
207 set_res.pb(y2_id);
208 add_edge(X[i], y1, X[i], y2);
209 add_edge(X[i], y2, y_map[y2_id], y2);
210 if (y_map[y1_id] != -1 && y1 != 0)
211 {
212 add_edge(X[i], y1, y_map[y1_id], y1);
213 }
214 }
215
216 for (int j = 0; j < set_res.size(); j++)
217 y_map[set_res[j]] = X[i];
218 set_res.clear();
219
220 for (int j = 0; j < neiR[i].size(); j++)
221 {
222 int cur = neiR[i][j];
223 int y2 = lower_bound(all_y.begin(), all_y.end(), Y[cur]) -
224 all_y.begin() + 1;
225 y_map[y2] = -1;
226 line.erase(-Y[cur]);
227 }
228
229 for (int j = 0; j < neiL[i].size(); j++)
230 {
231 int cur = neiL[i][j];
232 int y2 = lower_bound(all_y.begin(), all_y.end(), Y[cur]) -
233 all_y.begin() + 1;
234 y_map[y2] = X[i];
235 line.insert(-Y[cur]);
CAPITOLUL 2. IOI 2019 242

236 }
237 }
238 }
239
240 bool cmp(pair<int, pair<int, int> > aa, pair<int, pair<int, int> > bb)
241 {
242 if(aa.first < bb.first)
243 return true;
244 if(aa.first > bb.first)
245 return false;
246 return aa.second.first < bb.second.first;
247
248 }
249
250 void init()
251 {
252 vector<pair<int, pair<int, int> > > tmp, nw;
253 for(int i = 0; i < m; i++)
254 tmp.push_back(mp(Y[i], mp(L[i], R[i])));
255
256 sort(tmp.begin(), tmp.end(), cmp);
257
258 for(int i = 0; i < m; i++)
259 {
260 if(tmp[i].second.first == tmp[i].second.second) continue;
261 int j = i;
262 int hei = tmp[i].first;
263 int lo = tmp[i].second.first;
264 int hi = tmp[i].second.second;
265 while(tmp[j].first == hei && tmp[j].second.first <= hi &&
266 tmp[j].second.first >= lo)
267 {
268 hi = max(hi, tmp[j].second.second);
269 j++;
270 }
271 nw.push_back(mp(hei, mp(lo, hi)));
272 i = j - 1;
273 }
274
275 m = nw.size();
276 for (int i = 0; i < m; i++)
277 {
278 L[i] = nw[i].second.first;
279 R[i] = nw[i].second.second;
280 Y[i] = nw[i].first;
281 neiL[L[i]].pb(i);
282 neiR[R[i]].pb(i);
283 }
284
285 }
286
287 long long min_distance(vector<int> XX, vector<int> HH, vector<int> LL,
288 vector<int> RR, vector<int> YY, int S, int G)
289 {
290 n = XX.size();
291 m = LL.size();
292 for (int i = 0; i < n; i++)
293 {
294 X[i] = XX[i];
295 H[i] = HH[i];
296 }
297
298 for (int i = 0; i < m; i++)
299 {
300 L[i] = LL[i];
301 R[i] = RR[i];
302 Y[i] = YY[i];
303 all_y.push_back(Y[i]);
304 }
305
306 init();
307 all_y.push_back(0);
308
309 sort(all_y.begin(), all_y.end());
310
311 all_y.resize(unique(all_y.begin(), all_y.end()) - all_y.begin());
CAPITOLUL 2. IOI 2019 243

312 devide(S);
313 devide(G);
314 build_graph();
315 return dijkstra(add_map(X[S], 0), add_map(X[G], 0));
316 }
317
318 //-------------- start grader ------------------
319
320 int main()
321 {
322 std::freopen("../tests/5-30.in", "r", stdin) ;
323 std::freopen("walking.out", "w", stdout) ;
324
325 auto t1 = clock();
326
327 int n, m;
328 assert(2 == scanf("%d%d", &n, &m));
329 cerr<<"cerr : "<<n<<" "<<m<<"\n";
330
331 vector<int> x(n), h(n);
332 for (int i = 0; i < n; i++)
333 assert(2 == scanf("%d%d", &x[i], &h[i]));
334
335 vector<int> l(m), r(m), y(m);
336 for (int i = 0; i < m; i++)
337 assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
338 int s, g;
339 assert(2 == scanf("%d%d", &s, &g));
340 fclose(stdin);
341
342 auto t2 = clock();
343
344 long long result = min_distance(x, h, l, r, y, s, g);
345
346 auto t3 = clock();
347
348 printf("%lld\n", result);
349 fclose(stdout);
350
351 auto t4 = clock();
352
353 // reset console output
354 freopen("CON", "w", stdout);
355
356 std::cout <<"result = "<<result<<’\n’<<’\n’;
357
358 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
359 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
360 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
361
362 return 0;
363 }
364 //-------------- end grader ------------------

Listing 2.6.9: walk 143257 Benq+grader.cpp


1 // https://oj.uz/submission/143257
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 typedef long long ll;
8 typedef long double ld;
9 typedef complex<ld> cd;
10
11 typedef pair<int, int> pi;
12 typedef pair<ll,ll> pl;
13 typedef pair<ld,ld> pd;
14
15 typedef vector<int> vi;
16 typedef vector<ld> vd;
17 typedef vector<ll> vl;
18 typedef vector<pi> vpi;
19 typedef vector<pl> vpl;
CAPITOLUL 2. IOI 2019 244

20 typedef vector<cd> vcd;


21
22 #define FOR(i, a, b) for (int i = (a); i < (b); i++)
23 #define F0R(i, a) for (int i = 0; i < (a); i++)
24 #define FORd(i,a,b) for (int i = (b)-1; i >= (a); i--)
25 #define F0Rd(i,a) for (int i = (a)-1; i >= 0; i--)
26 #define trav(a, x) for (auto& a : x)
27
28 #define mp make_pair
29 #define pb push_back
30 #define f first
31 #define s second
32 #define lb lower_bound
33 #define ub upper_bound
34
35 #define sz(x) (int)x.size()
36 #define all(x) begin(x), end(x)
37 #define rsz resize
38
39 const int MOD = 1000000007; // 998244353
40 const ll INF = 1e18;
41 const int MX = 200005;
42 const ld PI = 4*atan((ld)1);
43
44 template<class T> void ckmin(T &a, T b) { a = min(a, b); }
45 template<class T> void ckmax(T &a, T b) { a = max(a, b); }
46
47 namespace input
48 {
49 template<class T> void re(complex<T>& x);
50 template<class T1, class T2> void re(pair<T1,T2>& p);
51 template<class T> void re(vector<T>& a);
52 template<class T, size_t SZ> void re(array<T,SZ>& a);
53
54 template<class T> void re(T& x) { cin >> x; }
55 void re(double& x) { string t; re(t); x = stod(t); }
56 void re(ld& x) { string t; re(t); x = stold(t); }
57 template<class Arg, class... Args> void re(Arg& first, Args&... rest)
58 {
59 re(first); re(rest...);
60 }
61
62 template<class T> void re(complex<T>& x)
63 {
64 T a,b; re(a,b);
65 x = cd(a,b);
66 }
67 template<class T1, class T2> void re(pair<T1,T2>& p) { re(p.f,p.s); }
68 template<class T> void re(vector<T>& a) { F0R(i,sz(a)) re(a[i]); }
69 template<class T, size_t SZ> void re(array<T,SZ>& a)
70 {
71 F0R(i,SZ) re(a[i]);
72 }
73 }
74
75 using namespace input;
76
77 namespace output
78 {
79 template<class T1, class T2> void pr(const pair<T1,T2>& x);
80 template<class T, size_t SZ> void pr(const array<T,SZ>& x);
81 template<class T> void pr(const vector<T>& x);
82 template<class T> void pr(const set<T>& x);
83 template<class T1, class T2> void pr(const map<T1,T2>& x);
84
85 template<class T> void pr(const T& x) { cout << x; }
86 template<class Arg, class... Args> void pr(const Arg& first,
87 const Args&... rest)
88 {
89 pr(first); pr(rest...);
90 }
91
92 template<class T1, class T2> void pr(const pair<T1,T2>& x)
93 {
94 pr("{",x.f,", ",x.s,"}");
95 }
CAPITOLUL 2. IOI 2019 245

96
97 template<class T> void prContain(const T& x)
98 {
99 pr("{");
100 bool fst = 1;
101 for (const auto& a: x)
102 pr(!fst?", ":"",a), fst = 0; // const needed for vector<bool>
103 pr("}");
104 }
105
106 template<class T, size_t SZ> void pr(const array<T,SZ>& x)
107 {
108 prContain(x);
109 }
110
111 template<class T> void pr(const vector<T>& x) { prContain(x); }
112 template<class T> void pr(const set<T>& x) { prContain(x); }
113
114 template<class T1, class T2> void pr(const map<T1,T2>& x)
115 {
116 prContain(x);
117 }
118
119 void ps() { pr("\n"); }
120
121 template<class Arg> void ps(const Arg& first)
122 {
123 pr(first); ps(); // no space at end of line
124 }
125
126 template<class Arg, class... Args> void ps(const Arg& first,
127 const Args&... rest)
128 {
129 pr(first," "); ps(rest...); // print w/ spaces
130 }
131 }
132
133 using namespace output;
134
135 namespace io
136 {
137 void setIn(string s) { freopen(s.c_str(),"r",stdin); }
138 void setOut(string s) { freopen(s.c_str(),"w",stdout); }
139
140 void setIO(string s = "")
141 {
142 ios_base::sync_with_stdio(0); cin.tie(0); // fast I/O
143 if (sz(s)) { setIn(s+".in"), setOut(s+".out"); } // for USACO
144 }
145 }
146
147 using namespace io;
148
149 template<class T> T invGeneral(T a, T b)
150 {
151 a %= b; if (a == 0) return b == 1 ? 0 : -1;
152 T x = invGeneral(b,a);
153 return x == -1 ? -1 : ((1-(ll)b*x)/a+b)%b;
154 }
155
156 template<class T> struct modular
157 {
158 T val;
159 explicit operator T() const { return val; }
160 modular() { val = 0; }
161
162 modular(const ll& v)
163 {
164 val = (-MOD <= v && v <= MOD) ? v : v % MOD;
165 if (val < 0) val += MOD;
166 }
167
168 friend void pr(const modular& a) { pr(a.val); }
169 friend void re(modular& a) { ll x; re(x); a = modular(x); }
170
171 friend bool operator==(const modular& a, const modular& b)
CAPITOLUL 2. IOI 2019 246

172 {
173 return a.val == b.val;
174 }
175
176 friend bool operator!=(const modular& a, const modular& b)
177 {
178 return !(a == b);
179 }
180
181 friend bool operator<(const modular& a, const modular& b)
182 {
183 return a.val < b.val;
184 }
185
186 modular operator-() const { return modular(-val); }
187
188 modular& operator+=(const modular& m)
189 {
190 if ((val += m.val) >= MOD)
191 val -= MOD;
192 return *this;
193 }
194
195 modular& operator-=(const modular& m)
196 {
197 if ((val -= m.val) < 0)
198 val += MOD;
199 return *this;
200 }
201
202 modular& operator*=(const modular& m)
203 {
204 val = (ll)val*m.val%MOD;
205 return *this;
206 }
207
208 friend modular pow(modular a, ll p)
209 {
210 modular ans = 1;
211 for (; p; p /= 2, a *= a)
212 if (p&1)
213 ans *= a;
214 return ans;
215 }
216
217 friend modular inv(const modular& a)
218 {
219 auto i = invGeneral(a.val,MOD); assert(i != -1);
220 return i;
221 } // equivalent to return exp(b,MOD-2) if MOD is prime
222
223 modular& operator/=(const modular& m) { return ( *this) *= inv(m); }
224
225 friend modular operator+(modular a, const modular& b) {return a += b;}
226 friend modular operator-(modular a, const modular& b) {return a -= b;}
227 friend modular operator*(modular a, const modular& b) {return a *= b;}
228
229 friend modular operator/(modular a, const modular& b) {return a /= b;}
230 };
231
232 typedef modular<int> mi;
233 typedef pair<mi,mi> pmi;
234 typedef vector<mi> vmi;
235 typedef vector<pmi> vpmi;
236
237 mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
238
239 template<class T> using pqg = priority_queue<T,vector<T>,greater<T>>;
240
241 template<class T> T poll(pqg<T>& x)
242 {
243 T y = x.top(); x.pop();
244 return y;
245 }
246
247 int nex;
CAPITOLUL 2. IOI 2019 247

248
249 template<int SZ> struct Dijkstra
250 {
251 ll dist[SZ];
252 vpi adj[SZ];
253
254 void addEdge(int A, int B, int C)
255 {
256 adj[A].pb({B,C}); adj[B].pb({A,C}); assert(C >= 0);
257 // if undirected
258 }
259
260 void init(int st)
261 {
262 fill_n(dist,SZ,INF);
263 pqg<pl> q;
264 q.push({dist[st] = 0,st});
265 while (sz(q))
266 {
267 auto x = poll(q);
268 if (dist[x.s] < x.f) continue;
269 trav(y,adj[x.s])
270 if (x.f+y.s < dist[y.f])
271 q.push({dist[y.f] = x.f+y.s,y.f});
272 }
273 }
274 };
275
276 Dijkstra<10000000> D;
277
278 int N, M, S, G;
279 vi X,H;
280 vector<pair<int,vi>> bridge;
281 vi xx;
282
283 void split(int h, vi& z, set<int>& cur, int x)
284 {
285 vi Z;
286 F0R(i,sz(z)-1)
287 {
288 Z.pb(z[i]);
289 if (z[i] < x && x < z[i+1])
290 {
291 if (H[x] >= h) Z.pb(x);
292 else
293 {
294 auto it = cur.lb(x); assert( *it != x);
295 if (z[i] < *prev(it)) Z.pb( *prev(it));
296 if ( *it < z[i+1]) Z.pb( *it);
297 }
298 }
299 }
300
301 Z.pb(z.back());
302 swap(z,Z);
303 }
304
305 vpi cor[500000];
306 map<int,pi> mm;
307 vector<pair<int,pi>> BRIDGE;
308
309 int makeVert(int seg, int x)
310 {
311 cor[seg].pb({x,nex++});
312 return nex-1;
313 }
314
315 void tri(int seg, int label, int x)
316 {
317 auto it = mm.ub(x); if (it == begin(mm) || prev(it)->s.f < x) return;
318 int SEG = prev(it)->s.s; if (BRIDGE[SEG].f > H[x]) return;
319 int LABEL = makeVert(SEG,x);
320 D.addEdge(label,LABEL,abs(BRIDGE[seg].f-BRIDGE[SEG].f));
321 }
322
323 void ins(int seg)
CAPITOLUL 2. IOI 2019 248

324 {
325 int l = BRIDGE[seg].s.f, r = BRIDGE[seg].s.s;
326 while (1)
327 {
328 auto it = mm.lb(l);
329 if (it != begin(mm) && prev(it)->s.f >= l) it --;
330 if (it == end(mm) || it->f > r) break;
331 auto IT = *it; mm.erase(it);
332 if (IT.f < l) mm[IT.f] = {l-1,IT.s.s};
333 if (IT.s.f > r) mm[r+1] = {IT.s.f,IT.s.s};
334 }
335 mm[l] = {r,seg};
336 }
337
338 ll min_distance(vi x, vi h, vi l, vi r, vi y, int s, int g)
339 {
340 N = sz(x), M = sz(l); if (s > g) swap(s,g);
341 X = x, H = h, S = s, G = g;
342 F0R(i,sz(l)) bridge.pb({y[i],{l[i],r[i]}});
343
344 sort(bridge.rbegin(),bridge.rend());
345
346 F0R(i,N) xx.pb(i);
347
348 sort(all(xx),[](int a, int b) { return H[a] > H[b]; });
349
350 int ind = 0; set<int> cur;
351
352 trav(z,bridge)
353 {
354 while (ind < sz(xx) && H[xx[ind]] >= z.f) cur.insert(xx[ind++]);
355 // ps("??",z.f,cur);
356 split(z.f,z.s,cur,S);
357 split(z.f,z.s,cur,G);
358 }
359
360 trav(z,bridge) F0R(i,sz(z.s)-1) BRIDGE.pb({z.f,{z.s[i],z.s[i+1]}});
361 BRIDGE.pb({0,{S,S}}); BRIDGE.pb({0,{G,G}});
362 vi special;
363 F0R(i,sz(BRIDGE))
364 {
365 cor[i] = {{BRIDGE[i].s.f,2*i},{BRIDGE[i].s.s,2*i+1}};
366 if (BRIDGE[i].f == 0) special.pb(2*i);
367 }
368
369 nex = 2*sz(BRIDGE);
370 F0R(i,sz(BRIDGE))
371 {
372 // ps("HUH",i,BRIDGE[i]);
373 tri(i,2*i,BRIDGE[i].s.f);
374 tri(i,2*i+1,BRIDGE[i].s.s);
375 ins(i);
376 }
377
378 mm.clear();
379 F0Rd(i,sz(BRIDGE))
380 {
381 tri(i,2*i,BRIDGE[i].s.f);
382 tri(i,2*i+1,BRIDGE[i].s.s);
383 ins(i);
384 }
385
386 // add bridge edges
387 F0R(i,sz(BRIDGE))
388 {
389 sort(all(cor[i]));
390 F0R(j,sz(cor[i])-1)
391 D.addEdge(cor[i][j].s,
392 cor[i][j+1].s,
393 X[cor[i][j+1].f]-X[cor[i][j].f]);
394 }
395
396 D.init(special[0]);
397 auto res = D.dist[special[1]];
398 if (res == INF) res = -1;
399 return res;
CAPITOLUL 2. IOI 2019 249

400 // at most M*5 bridges now


401 // for each endpoint of every bridge:
402 // add to next thing above it or below it
403 }
404
405 //-------------- start grader ------------------
406
407 int main()
408 {
409 std::freopen("../tests/5-30.in", "r", stdin) ; // execution time : 6.447 s
410 std::freopen("walking.out", "w", stdout) ;
411
412 auto t1 = clock();
413
414 int n, m;
415 assert(2 == scanf("%d%d", &n, &m));
416 cerr<<"cerr : "<<n<<" "<<m<<"\n";
417
418 vector<int> x(n), h(n);
419 for (int i = 0; i < n; i++)
420 assert(2 == scanf("%d%d", &x[i], &h[i]));
421
422 vector<int> l(m), r(m), y(m);
423 for (int i = 0; i < m; i++)
424 assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
425 int s, g;
426 assert(2 == scanf("%d%d", &s, &g));
427 fclose(stdin);
428
429 auto t2 = clock();
430
431 long long result = min_distance(x, h, l, r, y, s, g);
432
433 auto t3 = clock();
434
435 printf("%lld\n", result);
436 fclose(stdout);
437
438 auto t4 = clock();
439
440 // reset console output
441 freopen("CON", "w", stdout);
442
443 std::cout <<"result = "<<result<<’\n’<<’\n’;
444
445 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
446 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
447 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
448
449 return 0;
450 }
451 //-------------- end grader ------------------

Listing 2.6.10: walk 147051+grader.cpp


1 // https://oj.uz/submission/147051
2
3 #include "../walk.h"
4 #include <bits/stdc++.h>
5
6 #define sz(v) ((int)(v).size())
7
8 using namespace std;
9
10 using lint = long long;
11 using pi = pair<lint, int>;
12
13 const int MAXN = 100005;
14 const int MAXV = 2000005;
15
16 struct intv
17 {
18 int s, e, x;
19 bool operator<(const intv &i) const
20 {
CAPITOLUL 2. IOI 2019 250

21 return x < i.x;


22 }
23 };
24
25 struct point
26 {
27 int x, y, idx;
28 };
29
30 int n, m;
31 vector<int> witness[MAXN];
32 vector<pi> event[MAXN];
33 vector<pi> gph[MAXV];
34 lint dist[MAXV];
35
36 lint dijkstra(int s, int e)
37 {
38 priority_queue<pi, vector<pi>, greater<pi> > pq;
39 memset(dist, 0x3f, sizeof(dist));
40 dist[s] = 0;
41 pq.emplace(0, s);
42 while(!pq.empty())
43 {
44 auto x = pq.top();
45 pq.pop();
46 if(dist[x.second] != x.first) continue;
47 for(auto &j : gph[x.second])
48 {
49 if(dist[j.second] > x.first + j.first)
50 {
51 dist[j.second] = x.first + j.first;
52 pq.emplace(dist[j.second], j.second);
53 }
54 }
55 }
56
57 if(dist[e] > 1e17) return -1;
58 return dist[e];
59 }
60
61 void add_edge(point x, point y)
62 {
63 // printf("(%d, %d) <-> (%d, %d)\n", x.x, x.y, y.x, y.y);
64 int dist = abs(x.x - y.x) + abs(x.y - y.y);
65 gph[x.idx].emplace_back(dist, y.idx);
66 gph[y.idx].emplace_back(dist, x.idx);
67 }
68
69 void make_vertex(vector<intv> v)
70 {
71 for(auto &i : v)
72 {
73 event[i.s].emplace_back(i.x, +1);
74 event[i.e+1].emplace_back(i.x, -1);
75 }
76 multiset<int> swp;
77 for(int i=0; i<n; i++)
78 {
79 for(auto &j : event[i])
80 {
81 if(j.second == +1) swp.insert(j.first);
82 else swp.erase(swp.find(j.first));
83 }
84
85 vector<int> nxt = witness[i];
86
87 for(auto &j : witness[i])
88 {
89 if(j == 0) continue;
90 auto l = swp.upper_bound(j);
91 if(l != swp.end()) nxt.push_back( *l);
92 l = swp.lower_bound(j);
93 if(l != swp.begin()) nxt.push_back( *prev(l));
94 }
95
96 sort(nxt.begin(), nxt.end());
CAPITOLUL 2. IOI 2019 251

97
98 nxt.resize(unique(nxt.begin(), nxt.end()) - nxt.begin());
99 witness[i] = nxt;
100 }
101 }
102
103 long long min_distance(vector<int> x, vector<int> h,
104 vector<int> l, vector<int> r,
105 vector<int> y, int s, int e)
106 {
107 n = sz(x);
108 m = sz(l);
109 for(int i=0; i<m; i++)
110 {
111 witness[l[i]].push_back(y[i]);
112 witness[r[i]].push_back(y[i]);
113 }
114
115 witness[s].push_back(0);
116 witness[e].push_back(0);
117
118 vector<pi> points;
119 vector<intv> hors;
120
121 set<int> alive;
122 for(int i=0; i<n; i++)
123 {
124 points.emplace_back(h[i], i);
125 alive.insert(i);
126 }
127
128 for(int i=0; i<m; i++) hors.push_back({l[i], r[i], y[i]});
129
130 sort(points.begin(), points.end());
131 sort(hors.begin(), hors.end());
132
133 int ptr = 0;
134 for(auto &i : hors)
135 {
136 while(ptr < sz(points) && points[ptr].first < i.x)
137 {
138 alive.erase(points[ptr++].second);
139 }
140
141 if(i.s <= s && s <= i.e)
142 {
143 auto it = alive.lower_bound(s);
144 witness[ *it ].push_back(i.x);
145 if( *it != s) witness[ *prev(it) ].push_back(i.x);
146 }
147
148 if(i.s <= e && e <= i.e)
149 {
150 auto it = alive.lower_bound(e);
151 witness[ *it ].push_back(i.x);
152 if( *it != e) witness[ *prev(it) ].push_back(i.x);
153 }
154 }
155
156 make_vertex(hors);
157 vector<point> ans;
158 for(int i=0; i<n; i++)
159 {
160 for(auto &j : witness[i])
161 {
162 int num = ans.size();
163 ans.push_back({x[i], j, num});
164 }
165 }
166
167 assert(sz(ans) < MAXV);
168 auto cmpx = [&](const point &x, const point &y)
169 { return pi(x.x, x.y) < pi(y.x, y.y); };
170 auto cmpy = [&](const point &x, const point &y)
171 { return pi(x.y, x.x) < pi(y.y, y.x); };
172
CAPITOLUL 2. IOI 2019 252

173 sort(ans.begin(), ans.end(), cmpx);


174
175 for(int i=0; i<n; i++)
176 {
177 int st = lower_bound(ans.begin(), ans.end(),
178 (point){x[i], 0, -1}, cmpx) - ans.begin();
179 int ed = upper_bound(ans.begin(), ans.end(),
180 (point){x[i], h[i], -1}, cmpx) - ans.begin();
181 for(int j=st+1; j<ed; j++){
182 add_edge(ans[j-1], ans[j]);
183 }
184 }
185 sort(ans.begin(), ans.end(), cmpy);
186 for(int i=0; i<m; i++)
187 {
188 int st = lower_bound(ans.begin(), ans.end(),
189 (point){x[l[i]], y[i], -1}, cmpy) - ans.begin();
190 int ed = upper_bound(ans.begin(), ans.end(),
191 (point){x[r[i]], y[i], -1}, cmpy) - ans.begin();
192 for(int j=st+1; j<ed; j++){
193 add_edge(ans[j-1], ans[j]);
194 }
195 }
196 s = lower_bound(ans.begin(), ans.end(),
197 (point){x[s], 0, -1}, cmpy)->idx;
198 e = lower_bound(ans.begin(), ans.end(),
199 (point){x[e], 0, -1}, cmpy)->idx;
200 return dijkstra(s, e);
201 }
202
203 //-------------- start grader ------------------
204
205 int main()
206 {
207 std::freopen("../tests/5-30.in", "r", stdin) ; // execution time : 5.501 s
208 std::freopen("walking.out", "w", stdout) ;
209
210 auto t1 = clock();
211
212 int n, m;
213 assert(2 == scanf("%d%d", &n, &m));
214 cerr<<"cerr : "<<n<<" "<<m<<"\n";
215
216 vector<int> x(n), h(n);
217 for (int i = 0; i < n; i++)
218 assert(2 == scanf("%d%d", &x[i], &h[i]));
219
220 vector<int> l(m), r(m), y(m);
221 for (int i = 0; i < m; i++)
222 assert(3 == scanf("%d%d%d", &l[i], &r[i], &y[i]));
223 int s, g;
224 assert(2 == scanf("%d%d", &s, &g));
225 fclose(stdin);
226
227 auto t2 = clock();
228
229 long long result = min_distance(x, h, l, r, y, s, g);
230
231 auto t3 = clock();
232
233 printf("%lld\n", result);
234 fclose(stdout);
235
236 auto t4 = clock();
237
238 // reset console output
239 freopen("CON", "w", stdout);
240
241 std::cout <<"result = "<<result<<’\n’<<’\n’;
242
243 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
244 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
245 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
246
247 return 0;
248 }
CAPITOLUL 2. IOI 2019 253

249 //-------------- end grader ------------------

2.6.3 *Rezolvare detaliat 


Capitolul 3

IOI 201826

3.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  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  "ABXYY" sau "XYYAA", dar nu poate  "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 prex 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 aate
pe poziµii consecutive în t.
Un prex al lui t este o subsecvenµ  a lui t care e este vid , e 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 prex 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 ecare 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  e o secvenµ  de caractere cu lungimea cuprins  intre 0 ³i 4N inclusiv. Fiecare
a
caracter din p trebuie s  e ori A, ori B , ori X ori Y .
a Nu puteµi apela aceast  funcµie de mai mult de 8000 de ori pentru ecare test.

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

254
CAPITOLUL 3. IOI 2018 255

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  evaluat ca
Wrong answer. Altfel, programul vostru va  evaluat ca Accepted, iar punctajul vostru va 
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

Tabela 3.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 prex 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 xat 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 3. IOI 2018 256

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 a³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 a³a
Wrong answer: M SG. Semnicaµ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 .

3.1.1 Indicaµii de rezolvare


There is a secret string S which consists of four characters 'A', 'B', 'X', or 'Y'. The rst 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 prexes of S which are also substrings of the specied
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 rst position, determine the character by three asks. For each position except the
rst 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 rst or last position, determine the character by one ask as follows.
` Assume that the rst character of S is, for example, 'A'.
` Let s be the prex 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.

3.1.2 Coduri surs 


CAPITOLUL 3. IOI 2018 257

Listing 3.1.1: compile_cpp.sh


1 #!/bin/bash
2
3 TASK=combo
4
5 g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 3.1.2: combo.h


1 #include <string>
2
3 std::string guess_sequence(int N);
4
5 int press(std::string p);

Listing 3.1.3: combo.cpp


1 #include "combo.h"
2
3 std::string guess_sequence(int N)
4 {
5 std::string p = "";
6 for (int i = 0; i < 4 * N; ++i)
7 {
8 p += ’A’;
9 }
10 int coins = press(p);
11 std::string S = "";
12 for (int i = 0; i < N; ++i)
13 {
14 S += ’A’;
15 }
16 return S;
17 }

Listing 3.1.4: grader.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include <algorithm>
4 #include <string>
5 #include "combo.h"
6
7 namespace
8 {
9
10 constexpr int MAX_N = 2000;
11 constexpr int MAX_NUM_MOVES = 8000;
12
13 int N;
14 std::string S;
15
16 int num_moves;
17
18 void wrong_answer(const char *MSG)
19 {
20 printf("Wrong Answer: %s\n", MSG);
21 exit(0);
22 }
23
24 } // namespace
25
26 int press(std::string p)
27 {
28 if (++num_moves > MAX_NUM_MOVES)
29 {
30 wrong_answer("too many moves");
31 }
32 int len = p.length();
33 if (len > 4 * N)
34 {
35 wrong_answer("invalid press");
CAPITOLUL 3. IOI 2018 258

36 }
37 for (int i = 0; i < len; ++i)
38 {
39 if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
40 {
41 wrong_answer("invalid press");
42 }
43 }
44 int coins = 0;
45 for (int i = 0, j = 0; i < len; ++i)
46 {
47 if (j < N && S[j] == p[i])
48 {
49 ++j;
50 } else if (S[0] == p[i])
51 {
52 j = 1;
53 }
54 else
55 {
56 j = 0;
57 }
58 coins = std::max(coins, j);
59 }
60 return coins;
61 }
62
63 int main()
64 {
65 char buffer[MAX_N + 1];
66 if (scanf("%s", buffer) != 1)
67 {
68 fprintf(stderr, "Error while reading input\n");
69 exit(1);
70 }
71 S = buffer;
72 N = S.length();
73
74 num_moves = 0;
75 std::string answer = guess_sequence(N);
76 if (answer != S)
77 {
78 wrong_answer("wrong guess");
79 exit(0);
80 }
81 printf("Accepted: %d\n", num_moves);
82 return 0;
83 }

Listing 3.1.5: combo-model.cpp


1 // https://oj.uz/submission/75294 (36 ms C++17)
2
3 #include <algorithm>
4 #include <string>
5 //#include "combo.h"
6
7 using namespace std;
8
9 namespace
10 {
11 constexpr int MAX_N = 2000;
12 constexpr int MAX_NUM_MOVES = 8000;
13
14 int N;
15 std::string S;
16 int num_moves;
17
18 void wrong_answer(const char *MSG)
19 {
20 printf("Wrong Answer: %s\n", MSG);
21 exit(0);
22 }
23 } // namespace
24
CAPITOLUL 3. IOI 2018 259

25 int press(std::string p)
26 {
27 if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }
28
29 int len = p.length();
30 if (len > 4 * N) { wrong_answer("invalid press"); }
31
32 for (int i = 0; i < len; ++i)
33 {
34 if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
35 {
36 wrong_answer("invalid press");
37 }
38 }
39 int coins = 0;
40 for (int i = 0, j = 0; i < len; ++i)
41 {
42 if (j < N && S[j] == p[i]) { ++j; }
43 else if (S[0] == p[i]) { j = 1; }
44 else { j = 0; }
45 coins = std::max(coins, j);
46 }
47 return coins;
48 }
49
50 string guess_sequence(int N)
51 {
52 string p = "";
53 if(press("AB") >= 1) p = press("A") ? "A" : "B";
54 else p = press("X") ? "X" : "Y";
55
56 vector<char> chr = {’A’, ’B’, ’X’, ’Y’};
57 chr.erase(find(chr.begin(), chr.end(), p[0]));
58
59 while((int)p.size() <= N - 2)
60 {
61 int query = press(p+chr[0]+chr[0]+p+chr[0]+chr[1]+p+chr[1]+chr[0]);
62 if(query == p.size())
63 {
64 p.push_back(chr[2]);
65 }
66 else
67 if(query == p.size() + 1)
68 {
69 query = press(p + chr[1] + chr[2]);
70 if(query == p.size()) p = p + chr[0] + chr[2];
71 else if(query == p.size() + 1) p = p + chr[1] + chr[1];
72 else p = p + chr[1] + chr[2];
73 }
74 else
75 {
76 query = press(p + chr[0] + chr[1]);
77 if(query == p.size()) p = p + chr[1] + chr[0];
78 else if(query == p.size() + 1) p = p + chr[0] + chr[0];
79 else p = p + chr[0] + chr[1];
80 }
81 }
82 while(p.size() != N)
83 {
84 if(press(p + chr[0]) == p.size() + 1) p.push_back(chr[0]);
85 else if(press(p + chr[1]) == p.size() + 1) p.push_back(chr[1]);
86 else p.push_back(chr[2]);
87 }
88 return p;
89 }
90
91 int main()
92 {
93 std::freopen("../in/02-077.txt", "r", stdin);
94 //std::freopen("combo.out", "w", stdout) ;
95
96 char buffer[MAX_N + 1];
97 if (scanf("%s", buffer) != 1)
98 {
99 fprintf(stderr, "Error while reading input\n");
100 exit(1);
CAPITOLUL 3. IOI 2018 260

101 }
102 S = buffer;
103 N = S.length();
104
105 num_moves = 0;
106 std::string answer = guess_sequence(N); // functia de implementat ... !!!
107 if (answer != S)
108 {
109 wrong_answer("wrong guess");
110 exit(0);
111 }
112 printf("Accepted: num_moves=%d N=%d\n", num_moves,N);
113 return 0;
114 }

Listing 3.1.6: combo_75294.cpp


1 // https://oj.uz/submission/75294
2
3 #include <algorithm>
4 #include <string>
5 #include "combo.h"
6
7 using namespace std;
8
9 namespace
10 {
11 constexpr int MAX_N = 2000;
12 constexpr int MAX_NUM_MOVES = 8000;
13
14 int N;
15 std::string S;
16 int num_moves;
17
18 void wrong_answer(const char *MSG)
19 {
20 printf("Wrong Answer: %s\n", MSG);
21 exit(0);
22 }
23 }
24
25 int press(std::string p)
26 {
27 if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }
28
29 int len = p.length();
30 if (len > 4 * N) { wrong_answer("invalid press"); }
31
32 for (int i = 0; i < len; ++i)
33 {
34 if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
35 {
36 wrong_answer("invalid press");
37 }
38 }
39
40 int coins = 0;
41 for (int i = 0, j = 0; i < len; ++i)
42 {
43 if (j < N && S[j] == p[i]) { ++j; }
44 else if (S[0] == p[i]) { j = 1; }
45 else { j = 0; }
46 coins = std::max(coins, j);
47 }
48
49 return coins;
50 }
51
52 string guess_sequence(int N)
53 {
54 string p = "";
55 if(press("AB") >= 1) p = press("A") ? "A" : "B";
56 else p = press("X") ? "X" : "Y";
57
58 vector<char> chr = {’A’, ’B’, ’X’, ’Y’};
CAPITOLUL 3. IOI 2018 261

59 chr.erase(find(chr.begin(), chr.end(), p[0]));


60
61 while((int)p.size() <= N - 2)
62 {
63 int query = press(p+chr[0]+chr[0]+p+chr[0]+chr[1]+p+chr[1]+chr[0]);
64 if(query == p.size())
65 {
66 p.push_back(chr[2]);
67 }
68 else
69 if(query == p.size() + 1)
70 {
71 query = press(p + chr[1] + chr[2]);
72 if(query == p.size()) p = p + chr[0] + chr[2];
73 else if(query == p.size() + 1) p = p + chr[1] + chr[1];
74 else p = p + chr[1] + chr[2];
75 }
76 else
77 {
78 query = press(p + chr[0] + chr[1]);
79 if(query == p.size()) p = p + chr[1] + chr[0];
80 else if(query == p.size() + 1) p = p + chr[0] + chr[0];
81 else p = p + chr[0] + chr[1];
82 }
83 }
84
85 while(p.size() != N)
86 {
87 if(press(p + chr[0]) == p.size() + 1) p.push_back(chr[0]);
88 else if(press(p + chr[1]) == p.size() + 1) p.push_back(chr[1]);
89 else p.push_back(chr[2]);
90 }
91
92 return p;
93 }
94
95 int main()
96 {
97 std::freopen("../in/02-077.txt", "r", stdin);
98 //std::freopen("combo.out", "w", stdout) ;
99
100 char buffer[MAX_N + 1];
101 if (scanf("%s", buffer) != 1)
102 {
103 fprintf(stderr, "Error while reading input\n");
104 exit(1);
105 }
106 S = buffer;
107 N = S.length();
108
109 num_moves = 0;
110
111 std::string answer = guess_sequence(N);
112
113 if (answer != S)
114 {
115 wrong_answer("wrong guess");
116 exit(0);
117 }
118
119 printf("Accepted: num_moves=%d N=%d\n", num_moves,N);
120 return 0;
121 }

Listing 3.1.7: combo_75871.cpp


1 // https://oj.uz/submission/75871
2
3 #include <cstdio>
4 #include <cstdlib>
5 #include <algorithm>
6 #include <string>
7 #include "combo.h"
8
9 using namespace std;
CAPITOLUL 3. IOI 2018 262

10
11 namespace
12 {
13 constexpr int MAX_N = 2000;
14 constexpr int MAX_NUM_MOVES = 8000;
15
16 int N;
17 std::string S;
18 int num_moves;
19
20 void wrong_answer(const char *MSG)
21 {
22 printf("Wrong Answer: %s\n", MSG);
23 exit(0);
24 }
25 }
26
27 int press(std::string p)
28 {
29 if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }
30
31 int len = p.length();
32 if (len > 4 * N) { wrong_answer("invalid press"); }
33
34 for (int i = 0; i < len; ++i)
35 {
36 if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
37 {
38 wrong_answer("invalid press");
39 }
40 }
41
42 int coins = 0;
43 for (int i = 0, j = 0; i < len; ++i)
44 {
45 if (j < N && S[j] == p[i]) { ++j; }
46 else if (S[0] == p[i]) { j = 1; }
47 else { j = 0; }
48 coins = std::max(coins, j);
49 }
50
51 return coins;
52 }
53
54 const static char b[5] = "ABXY";
55
56 static string r;
57 static char a[3];
58
59 string guess_sequence(int N)
60 {
61 int x = press(string("XY"));
62 int y = press(string("BY"));
63
64 r += b[2 * !!x + !!y];
65 for(int i = 0, j = 0; i < 4; i++) if(i != 2 * !!x + !!y) a[j++] = b[i];
66
67 for(int x; r.length() < N - 1; )
68 {
69 x = press(r + a[0] + a[0] + r + a[0] + a[1] + r + a[1] + a[0]);
70 if(x == r.length()){ r += a[2]; continue; }
71 if(x == r.length() + 1)
72 {
73 x = press(r + a[1] + r + a[1] + a[1]);
74 if(x == r.length()){ r += a[0]; r += a[2]; }
75 if(x == r.length() + 1){ r += a[1]; r += a[2]; }
76 if(x == r.length() + 2){ r += a[1]; r += a[1]; }
77 }
78 else
79 {
80 x = press(r + a[0] + r + a[0] + a[0]);
81 if(x == r.length()){ r += a[1]; r += a[0]; }
82 if(x == r.length() + 1){ r += a[0]; r += a[1]; }
83 if(x == r.length() + 2){ r += a[0]; r += a[0]; }
84 }
85 }
CAPITOLUL 3. IOI 2018 263

86
87 if(r.length() < N)
88 {
89 int x = press(r + "X" + r + "Y");
90 int y = press(r + "B" + r + "Y");
91 r += b[2 * (x - r.length()) + (y - r.length())];
92 }
93
94 return r;
95 }
96
97 int main()
98 {
99 std::freopen("../in/02-077.txt", "r", stdin) ;
100 //std::freopen("combo.out", "w", stdout) ;
101
102 char buffer[MAX_N + 1];
103 if (scanf("%s", buffer) != 1)
104 {
105 fprintf(stderr, "Error while reading input\n");
106 exit(1);
107 }
108 S = buffer;
109 N = S.length();
110
111 num_moves = 0;
112
113 std::string answer = guess_sequence(N);
114
115 if (answer != S)
116 {
117 wrong_answer("wrong guess");
118 exit(0);
119 }
120
121 printf("Accepted: num_moves=%d N=%d\n", num_moves,N);
122 return 0;
123 }

Listing 3.1.8: combo_76356.cpp


1 // https://oj.uz/submission/76356
2
3 #include <cstdio>
4 #include <cstdlib>
5 #include <algorithm>
6 #include <string>
7 #include "combo.h"
8
9 using namespace std;
10
11 namespace
12 {
13 constexpr int MAX_N = 2000;
14 constexpr int MAX_NUM_MOVES = 8000;
15
16 int N;
17 std::string S;
18 int num_moves;
19
20 void wrong_answer(const char *MSG)
21 {
22 printf("Wrong Answer: %s\n", MSG);
23 exit(0);
24 }
25 }
26
27 int press(std::string p)
28 {
29 if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }
30
31 int len = p.length();
32 if (len > 4 * N) { wrong_answer("invalid press"); }
33
34 for (int i = 0; i < len; ++i)
CAPITOLUL 3. IOI 2018 264

35 {
36 if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
37 {
38 wrong_answer("invalid press");
39 }
40 }
41
42 int coins = 0;
43 for (int i = 0, j = 0; i < len; ++i)
44 {
45 if (j < N && S[j] == p[i]) { ++j; }
46 else if (S[0] == p[i]) { j = 1; }
47 else { j = 0; }
48 coins = std::max(coins, j);
49 }
50
51 return coins;
52 }
53
54 std::string guess_sequence(int N)
55 {
56 char first = press("AB") == 0? "YX"[press("X")]:
57 "BA"[press("A")];
58
59 char a, b, c;
60 if(first == ’A’) a = ’B’, b = ’X’, c = ’Y’;
61 if(first == ’B’) a = ’A’, b = ’X’, c = ’Y’;
62 if(first == ’X’) a = ’A’, b = ’B’, c = ’Y’;
63 if(first == ’Y’) a = ’A’, b = ’B’, c = ’X’;
64
65 std::string S(1, first);
66 while(S.length() + 2 <= N)
67 {
68 int coins = press(S + a + a + S + a + b + S + b + a) - S.length();
69 if (coins == 0)
70 {
71 S += c;
72 }
73 else
74 if (coins == 1)
75 {
76 coins = press(S + b + c) - S.length();
77 if (coins == 0)
78 {
79 S += a, S += c;
80 }
81 else
82 if (coins == 1)
83 {
84 S += b, S += b;
85 }
86 else
87 if (coins == 2)
88 {
89 S += b, S += c;
90 }
91 }
92 else
93 if (coins == 2)
94 {
95 coins = press(S + a + b) - S.length();
96 if (coins == 0)
97 {
98 S += b, S += a;
99 }
100 else
101 if (coins == 1)
102 {
103 S += a, S += a;
104 }
105 else
106 if (coins == 2)
107 {
108 S += a, S += b;
109 }
110 }
CAPITOLUL 3. IOI 2018 265

111 }
112
113 if (S.length() < N)
114 {
115 if (press(S + a) - S.length() == 1)
116 {
117 S += a;
118 }
119 else
120 if(press(S + b) - S.length() == 1)
121 {
122 S += b;
123 }
124 else
125 {
126 S += c;
127 }
128 }
129
130 return S;
131 }
132
133 int main()
134 {
135 std::freopen("../in/02-077.txt", "r", stdin) ;
136 //std::freopen("combo.out", "w", stdout) ;
137
138 char buffer[MAX_N + 1];
139 if (scanf("%s", buffer) != 1)
140 {
141 fprintf(stderr, "Error while reading input\n");
142 exit(1);
143 }
144 S = buffer;
145 N = S.length();
146
147 num_moves = 0;
148
149 std::string answer = guess_sequence(N);
150
151 if (answer != S)
152 {
153 wrong_answer("wrong guess");
154 exit(0);
155 }
156
157 printf("Accepted: num_moves=%d N=%d\n", num_moves,N);
158 return 0;
159 }

Listing 3.1.9: combo_77113.cpp


1 // https://oj.uz/submission/77113
2
3 #include <cstdio>
4 #include <cstdlib>
5 #include <algorithm>
6 #include <string>
7 #include "combo.h"
8
9 using namespace std;
10
11 namespace
12 {
13 constexpr int MAX_N = 2000;
14 constexpr int MAX_NUM_MOVES = 8000;
15
16 int N;
17 std::string S;
18 int num_moves;
19
20 void wrong_answer(const char *MSG)
21 {
22 printf("Wrong Answer: %s\n", MSG);
23 exit(0);
CAPITOLUL 3. IOI 2018 266

24 }
25 }
26
27 int press(std::string p)
28 {
29 if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }
30
31 int len = p.length();
32 if (len > 4 * N) { wrong_answer("invalid press"); }
33
34 for (int i = 0; i < len; ++i)
35 {
36 if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’)
37 {
38 wrong_answer("invalid press");
39 }
40 }
41
42 int coins = 0;
43 for (int i = 0, j = 0; i < len; ++i)
44 {
45 if (j < N && S[j] == p[i]) { ++j; }
46 else if (S[0] == p[i]) { j = 1; }
47 else { j = 0; }
48 coins = std::max(coins, j);
49 }
50
51 return coins;
52 }
53
54 std::string guess_sequence(int N)
55 {
56 int sr = 0;
57 if (press("AB") > 0) sr += 2;
58 if (press("AX") > 0) sr++;
59 string sc[4] = {"Y", "X", "B", "A"};
60 string s = "";
61 s += sc[sr];
62 string c[3];
63 int cs = 0;
64 for (int i = 0; i < 4; i++)
65 if (i != sr)
66 {
67 c[cs] = sc[i];
68 cs++;
69 }
70
71 for (int i = 1; i < N; i++)
72 {
73 string ts;
74 if (i == N-1) ts = s + c[1] + s + c[2];
75 else ts = s + c[1] + c[0] + s + c[2] + c[0] + s + c[2] + c[2];
76 int t = press(ts) - s.length();
77 if (t != 0 && i == N-1)
78 {
79 int t1 = press(s + c[1]) - s.length();
80 if (t1 == 0) s += c[2];
81 else s += c[1];
82 continue;
83 }
84
85 if (t == 0) s += c[0];
86 else if (t == 1)
87 {
88 string ps = s + c[1] + c[2];
89 int t1 = press(ps) - s.length();
90 if (t1 == 0) s += c[2] + c[1];
91 else if (t1 == 1) s += c[1] + c[1];
92 else s += c[1] + c[2];
93 i++;
94 }
95 else
96 {
97 string ps = s + c[2] + c[0];
98 int t1 = press(ps) - s.length();
99 if (t1 == 0) s += c[1] + c[0];
CAPITOLUL 3. IOI 2018 267

100 else if (t1 == 1) s += c[2] + c[2];


101 else s += c[2] + c[0];
102 i++;
103 }
104 }
105
106 return s;
107 }
108
109 int main()
110 {
111 std::freopen("../in/02-077.txt", "r", stdin) ;
112 //std::freopen("combo.out", "w", stdout) ;
113
114 char buffer[MAX_N + 1];
115 if (scanf("%s", buffer) != 1)
116 {
117 fprintf(stderr, "Error while reading input\n");
118 exit(1);
119 }
120 S = buffer;
121 N = S.length();
122
123 num_moves = 0;
124
125 std::string answer = guess_sequence(N);
126
127 if (answer != S)
128 {
129 wrong_answer("wrong guess");
130 exit(0);
131 }
132
133 printf("Accepted: num_moves=%d N=%d\n", num_moves,N);
134 return 0;
135 }

Listing 3.1.10: combo_koosaga.cpp


1 // https://github.com/koosaga/olympiad/blob/master/IOI/ioi18_combo.cpp
2
3 #include <cstdio>
4 #include <cstdlib>
5 #include <algorithm>
6 #include <string>
7 #include "combo.h"
8
9 using namespace std;
10
11 namespace
12 {
13 constexpr int MAX_N = 2000;
14 constexpr int MAX_NUM_MOVES = 8000;
15
16 int N;
17 std::string S;
18 int num_moves;
19
20 void wrong_answer(const char *MSG)
21 {
22 printf("Wrong Answer: %s\n", MSG);
23 exit(0);
24 }
25 }
26
27 int press(std::string p)
28 {
29 if (++num_moves > MAX_NUM_MOVES) { wrong_answer("too many moves"); }
30
31 int len = p.length();
32 if (len > 4 * N) { wrong_answer("invalid press"); }
33
34 for (int i = 0; i < len; ++i)
35 {
36 if (p[i] != ’A’ && p[i] != ’B’ && p[i] != ’X’ && p[i] != ’Y’) {
CAPITOLUL 3. IOI 2018 268

37 wrong_answer("invalid press");
38 }
39 }
40 int coins = 0;
41 for (int i = 0, j = 0; i < len; ++i)
42 {
43 if (j < N && S[j] == p[i]) { ++j; }
44 else if (S[0] == p[i]) { j = 1; }
45 else { j = 0; }
46 coins = std::max(coins, j);
47 }
48 return coins;
49 }
50
51 std::string guess_sequence(int N)
52 {
53 std::string p = "";
54 if(press("AB") >= 1) p = press("A") ? "A" : "B";
55 else p = press("X") ? "X" : "Y";
56 std::vector<char> chr = {’A’, ’B’, ’X’, ’Y’};
57 chr.erase(find(chr.begin(), chr.end(), p[0]));
58
59 while((int)p.size() <= N - 2)
60 {
61 int query = press(p + chr[0] + chr[0] +
62 p + chr[0] + chr[1] +
63 p + chr[1] + chr[0]);
64 if(query == p.size())
65 {
66 p.push_back(chr[2]);
67 }
68 else
69 if(query == p.size() + 1)
70 {
71 query = press(p + chr[1] + chr[2]);
72 if(query == p.size()) p = p + chr[0] + chr[2];
73 else
74 if(query == p.size() + 1) p = p + chr[1] + chr[1];
75 else p = p + chr[1] + chr[2];
76 }
77 else
78 {
79 query = press(p + chr[0] + chr[1]);
80 if(query == p.size()) p = p + chr[1] + chr[0];
81 else
82 if(query == p.size() + 1) p = p + chr[0] + chr[0];
83 else p = p + chr[0] + chr[1];
84 }
85 }
86
87 while(p.size() != N)
88 {
89 if(press(p + chr[0]) == p.size() + 1) p.push_back(chr[0]);
90 else
91 if(press(p + chr[1]) == p.size() + 1) p.push_back(chr[1]);
92 else p.push_back(chr[2]);
93 }
94
95 return p;
96 }
97
98 int main()
99 {
100 std::freopen("../in/02-077.txt", "r", stdin) ;
101 //std::freopen("combo.out", "w", stdout) ;
102
103 char buffer[MAX_N + 1];
104 if (scanf("%s", buffer) != 1)
105 {
106 fprintf(stderr, "Error while reading input\n");
107 exit(1);
108 }
109 S = buffer;
110 N = S.length();
111
112 num_moves = 0;
CAPITOLUL 3. IOI 2018 269

113
114 std::string answer = guess_sequence(N);
115
116 if (answer != S)
117 {
118 wrong_answer("wrong guess");
119 exit(0);
120 }
121
122 printf("Accepted: num_moves=%d N=%d\n", num_moves,N);
123 return 0;
124 }

3.1.3 *Rezolvare detaliat 

3.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 ec 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  ecare 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  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 3. IOI 2018 270

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 dreptun-
ghiulare ³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 a³eaz  r spunsurile în urm toarea form :
a linia 1  j (0 & j & Q  1): valoarea întoars  de swap_seats pentru cererea j
CAPITOLUL 3. IOI 2018 271

3.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 sucient 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 sucient and necessary condition of black squares forming a rectangle is that the number
of pairs of adjacent two squares with dierent 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 eciently 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 sucient 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 eciently using a lazy propagation segment tree and deal with each
query in O log HW  time.
CAPITOLUL 3. IOI 2018 272

3.2.2 Coduri surs 

Listing 3.2.1: compile_cpp.sh


1 #!/bin/bash
2
3 TASK=seats
4
5 g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 3.2.2: seat.h


1 #include <vector>
2
3 void give_initial_chart(int H, int W, std::vector<int> R, std::vector<int> C);
4 int swap_seats(int a, int b);

Listing 3.2.3: seat.cpp


1 #include "seats.h"
2
3 std::vector<int> r;
4
5 void give_initial_chart(int H, int W, std::vector<int> R, std::vector<int> C)
6 {
7 r = R;
8 }
9
10 int swap_seats(int a, int b)
11 {
12 return r[a];
13 }

Listing 3.2.4: grader.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include <vector>
4 #include "seats.h"
5
6 namespace
7 {
8 int read_int()
9 {
10 int x;
11 if (scanf("%d", &x) != 1)
12 {
13 fprintf(stderr, "Error while reading input\n");
14 exit(1);
15 }
16 return x;
17 }
18 } // namespace
19
20 int main()
21 {
22 int H = read_int();
23 int W = read_int();
24 int Q = read_int();
25 std::vector<int> R(H * W), C(H * W);
26 for (int i = 0; i < H * W; ++i)
27 {
28 R[i] = read_int();
29 C[i] = read_int();
30 }
31 std::vector<int> A(Q), B(Q);
32 for (int j = 0; j < Q; ++j)
33 {
34 A[j] = read_int();
35 B[j] = read_int();
36 }
37
CAPITOLUL 3. IOI 2018 273

38 give_initial_chart(H, W, R, C);
39 for (int j = 0; j < Q; ++j)
40 {
41 int answer = swap_seats(A[j], B[j]);
42 printf("%d\n", answer);
43 }
44 return 0;
45 }

Listing 3.2.5: seat-model.cpp


1 // https://oj.uz/submission/76357
2 // https://oj.uz/problems/source/364
3 // https://oj.uz/problems/source/363
4 // https://oj.uz/problems/source/22
5
6 #include <stdio.h> // citire mai rapida !!!
7 #include <algorithm>
8 #include <vector>
9 #include <set>
10
11 #include <time.h> /* clock */
12 #include <iostream>
13
14 using namespace std;
15
16 const int Z = 1<<20;
17
18 struct node
19 {
20 node()
21 {
22 sum = min = 0; cnt = 1;
23 }
24
25 node(int v)
26 {
27 sum = min = v; cnt = 1;
28 }
29
30 int sum, min, cnt;
31
32 node operator *(const node &t) const
33 {
34 node res;
35
36 res.sum = sum + t.sum;
37
38 if (min < sum + t.min) res.min = min;
39 else res.min = sum + t.min;
40
41 res.cnt = 0;
42 if (res.min == min) res.cnt += cnt;
43 if (res.min == sum + t.min) res.cnt += t.cnt;
44
45 return res;
46 }
47
48 void add(int p)
49 {
50 sum += p;
51 min += p;
52 }
53 } IT[Z*2];
54
55
56 int N, A[1001001];
57 bool chk[1001001];
58 int H, W;
59 vector<int> R, C;
60
61
62 void upd(int x)
63 {
64 x /= 2;
CAPITOLUL 3. IOI 2018 274

65 while (x)
66 {
67 IT[x] = IT[x*2] * IT[x*2+1];
68 x /= 2;
69 }
70 }
71
72 set<int> ups;
73
74 void add(int i, int p, bool up = true)
75 {
76 if (p > 0)
77 {
78 if (chk[i]) return;
79 chk[i] = 1;
80 }
81 else
82 {
83 if (!chk[i]) return;
84 chk[i] = 0;
85 }
86
87 int x = R[i], y = C[i];
88 int dx[5] = {0,1,0,-1,0};
89 int dy[5] = {1,0,-1,0,1};
90
91 for (int k=0;k<4;k++)
92 {
93 int u[2];
94 for (int d=0;d<2;d++)
95 {
96 int px = x + dx[k+d];
97 int py = y + dy[k+d];
98 if (px < 0 || px >= H || py < 0 || py >= W) u[d] = N;
99 else u[d] = A[px*W+py];
100 }
101
102 int s = N, e = 0;
103 if (i < u[0] && i < u[1])
104 {
105 s = i + Z;
106 e = min(u[0],u[1]) + Z;
107 }
108
109 if (u[0] < i && u[1] < i)
110 {
111 s = max(u[0],u[1]) + Z;
112 e = i + Z;
113 }
114
115 if (s < e)
116 {
117 IT[s].add(+p);
118 IT[e].add(-p);
119 if (up)
120 {
121 ups.insert(s);
122 ups.insert(e);
123 }
124 }
125 }
126 }
127
128 void give_initial_chart(int H_, int W_, vector<int> R_, vector<int> C_)
129 {
130 H = H_; W = W_; R = R_; C = C_; N = H * W;
131 for (int i=0;i<N;i++) A[R[i]*W+C[i]] = i;
132 IT[N+Z].add(10000);
133 for (int i=0;i<N;i++) add(i,1,false);
134 for (int i=Z-1;i>=1;i--) IT[i] = IT[i*2] * IT[i*2+1];
135 }
136
137 int swap_seats(int a, int b)
138 {
139 int dx[5] = {0,1,0,-1,0};
140 int dy[5] = {1,0,-1,0,0};
CAPITOLUL 3. IOI 2018 275

141 for (int i : {a,b}) for (int k=0;k<5;k++)


142 {
143 int x = R[i] + dx[k];
144 int y = C[i] + dy[k];
145 if (x < 0 || x >= H || y < 0 || y >= W) continue;
146 add(A[x*W+y],-1);
147 }
148
149 swap(R[a],R[b]);
150 swap(C[a],C[b]);
151 A[R[a]*W+C[a]] = a;
152 A[R[b]*W+C[b]] = b;
153
154 for (int i : {a,b}) for (int k=0;k<5;k++)
155 {
156 int x = R[i] + dx[k];
157 int y = C[i] + dy[k];
158 if (x < 0 || x >= H || y < 0 || y >= W) continue;
159 add(A[x*W+y],+1);
160 }
161
162 for (int x : ups) upd(x);
163 ups.clear();
164
165 return IT[1].min == 4 ? IT[1].cnt : 0;
166 }
167
168 //======================================================================
169
170 namespace
171 {
172
173 int read_int()
174 {
175 int x;
176 if (scanf("%d", &x) != 1)
177 {
178 fprintf(stderr, "Error while reading input\n");
179 exit(1);
180 }
181 return x;
182 }
183 } // namespace
184
185 int main()
186 {
187 auto t1 = clock();
188
189 std::freopen("../in/05-11.txt", "r", stdin) ;
190 std::freopen("seats.out", "w", stdout) ;
191
192 int H = read_int();
193 int W = read_int();
194 int Q = read_int();
195
196 std::vector<int> R(H * W), C(H * W);
197 for (int i = 0; i < H * W; ++i)
198 {
199 R[i] = read_int();
200 C[i] = read_int();
201 }
202 std::vector<int> A(Q), B(Q);
203
204 for (int j = 0; j < Q; ++j)
205 {
206 A[j] = read_int();
207 B[j] = read_int();
208 }
209
210 auto t2 = clock();
211
212 give_initial_chart(H, W, R, C);
213
214 auto t3 = clock();
215
216 for (int j = 0; j < Q; ++j)
CAPITOLUL 3. IOI 2018 276

217 {
218 int answer = swap_seats(A[j], B[j]);
219 printf("%d\n", answer);
220 }
221
222 auto t4 = clock();
223 fclose(stdout);
224
225 // reset console output
226 freopen("CON", "w", stdout);
227
228 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
229 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
230 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
231
232 return 0;
233 }
234 //======================================================================
235 /*
236 t2-t1 = 0.896
237 t3-t2 = 0.449
238 t4-t3 = 1.873
239
240 Process returned 0 (0x0) execution time : 3.909 s
241 Press any key to continue.
242 */
243 //======================================================================

Listing 3.2.6: seats_75159.cpp


1 // https://oj.uz/submission/75159
2
3 #include <ctime>
4 //#include <time.h> /* clock */
5
6 //#include <cstdio>
7 #include <stdio.h> // citire mai rapida !!!
8
9 #include <cstdlib>
10 #include <vector>
11 #include "seats.h"
12
13 #include <cstring>
14 #include <iostream>
15 #include <algorithm>
16
17 using namespace std;
18
19 typedef long long llong;
20 typedef pair<int, int> pii;
21
22 int h, w, n;
23
24 vector<int> R, C;
25 vector<vector<int>> idx;
26
27 struct segtree
28 {
29 struct node
30 {
31 llong mn, ct, lz;
32 node operator+(const node &p) const
33 {
34 node ret;
35 ret.mn = min(mn, p.mn);
36 ret.ct = 0;
37 if (ret.mn == mn) ret.ct += ct;
38 if (ret.mn == p.mn) ret.ct += p.ct;
39 ret.lz = 0;
40 return ret;
41 }
42 } seg[1 << 21];
43
44 void init(int i, int s, int e, const vector<llong> &v)
45 {
CAPITOLUL 3. IOI 2018 277

46 if (s == e)
47 {
48 seg[i].mn = v[s];
49 seg[i].ct = 1;
50 seg[i].lz = 0;
51 return;
52 }
53
54 int m = (s + e) / 2;
55 init(i << 1, s, m, v);
56 init(i << 1 | 1, m + 1, e, v);
57 seg[i] = seg[i << 1] + seg[i << 1 | 1];
58 }
59
60 void spread(int i)
61 {
62 seg[i << 1].mn += seg[i].lz;
63 seg[i << 1].lz += seg[i].lz;
64 seg[i << 1 | 1].mn += seg[i].lz;
65 seg[i << 1 | 1].lz += seg[i].lz;
66 seg[i].lz = 0;
67 }
68
69 void update(int i, int s, int e, int x, int y, llong v)
70 {
71 if (e < x || y < s) return;
72 if (x <= s && e <= y)
73 {
74 seg[i].mn += v;
75 seg[i].lz += v;
76 return;
77 }
78
79 spread(i);
80 int m = (s + e) / 2;
81 update(i << 1, s, m, x, y, v);
82 update(i << 1 | 1, m + 1, e, x, y, v);
83 seg[i] = seg[i << 1] + seg[i << 1 | 1];
84 }
85
86 int get() const
87 {
88 return seg[1].ct;
89 }
90 } seg;
91
92 int getIdx(int x, int y)
93 {
94 if (x < 0 || h <= x || y < 0 || w <= y) return n;
95 return idx[x][y];
96 }
97
98 void update(int x, int y, int v)
99 {
100 int A[4] = { getIdx(x - 1, y - 1), getIdx(x - 1, y)
101 , getIdx(x, y - 1), getIdx(x, y) };
102 sort(A, A + 4);
103 seg.update(1, 0, n - 1, A[0], A[1] - 1, v);
104 seg.update(1, 0, n - 1, A[2], A[3] - 1, v * (1ll << 30));
105 }
106
107 void give_initial_chart(int H, int W, vector<int> R, vector<int> C)
108 {
109 idx.resize(H, vector<int>(W, 0));
110 ::R = R; ::C = C;
111 h = H; w = W; n = H * W;
112 for (int i = 0; i < n; ++i)
113 {
114 idx[R[i]][C[i]] = i;
115 }
116
117 vector<llong> sum(n + 1, 0);
118 for (int i = 0; i <= h; ++i)
119 {
120 for (int j = 0; j <= w; ++j)
121 {
CAPITOLUL 3. IOI 2018 278

122 int A[4] = { getIdx(i - 1, j - 1), getIdx(i - 1, j)


123 , getIdx(i, j - 1), getIdx(i, j) };
124 sort(A, A + 4);
125 sum[A[0]] += 1;
126 sum[A[1]] -= 1;
127 sum[A[2]] += (1ll << 30);
128 sum[A[3]] -= (1ll << 30);
129 }
130 }
131
132 for (int i = 1; i < n; ++i) sum[i] += sum[i - 1];
133 seg.init(1, 0, n - 1, sum);
134 }
135
136 int swap_seats(int a, int b)
137 {
138 vector<pii> ps;
139 for (int i = 0; i < 2; ++i)
140 {
141 for (int j = 0; j < 2; ++j)
142 {
143 ps.emplace_back(R[a] + i, C[a] + j);
144 ps.emplace_back(R[b] + i, C[b] + j);
145 }
146 }
147
148 sort(ps.begin(), ps.end());
149 ps.erase(unique(ps.begin(), ps.end()), ps.end());
150 for (pii i : ps) update(i.first, i.second, -1);
151 swap(idx[R[a]][C[a]], idx[R[b]][C[b]]);
152 swap(R[a], R[b]); swap(C[a], C[b]);
153 for (pii i : ps) update(i.first, i.second, 1);
154 return seg.get();
155 }
156
157 // ----------------------------------------------
158
159 namespace
160 {
161 int read_int()
162 {
163 int x;
164 if (scanf("%d", &x) != 1)
165 {
166 fprintf(stderr, "Error while reading input\n");
167 exit(1);
168 }
169 return x;
170 }
171 } // namespace
172
173 int main()
174 {
175 auto t1 = clock();
176
177 std::freopen("../in/06-04.txt", "r", stdin) ;
178 std::freopen("seats.out", "w", stdout) ;
179
180 int H = read_int();
181 int W = read_int();
182 int Q = read_int();
183
184 std::vector<int> R(H * W), C(H * W);
185 for (int i = 0; i < H * W; ++i)
186 {
187 R[i] = read_int();
188 C[i] = read_int();
189 }
190 std::vector<int> A(Q), B(Q);
191
192 for (int j = 0; j < Q; ++j)
193 {
194 A[j] = read_int();
195 B[j] = read_int();
196 }
197
CAPITOLUL 3. IOI 2018 279

198 auto t2 = clock();


199
200 give_initial_chart(H, W, R, C);
201
202 auto t3 = clock();
203
204 for (int j = 0; j < Q; ++j)
205 {
206 int answer = swap_seats(A[j], B[j]);
207 printf("%d\n", answer);
208 }
209
210 auto t4 = clock();
211 fclose(stdout);
212
213 // reset console output
214 freopen("CON", "w", stdout);
215
216 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
217 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
218 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
219
220 return 0;
221 }
222 /*
223 t2-t1 = 3.124
224 t3-t2 = 0.67
225 t4-t3 = 8.047
226
227 Process returned 0 (0x0) execution time : 11.920 s
228 Press any key to continue.
229 */

Listing 3.2.7: seats_75485.cpp


1 // https://oj.uz/submission/75485
2
3 #include <ctime>
4 //#include <time.h> /* clock */
5
6 //#include <cstdio>
7 #include <stdio.h> // citire mai rapida !!!
8
9 #include <vector>
10 #include "seats.h"
11
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15
16 using namespace std;
17
18 const int maxn=1000005;
19
20 const int dx[4]={0,0,1,1},dy[4]={0,1,0,1};
21
22 int H,W,NUM;
23 int val[maxn],presum[maxn];
24 vector<int> R,C;
25 int S[maxn],T[maxn];
26
27 struct node
28 {
29 int dta;
30 int s,cnt;
31 node *lc,*rc;
32
33 void tagdta(const int &c)
34 {
35 s+=c;
36 dta+=c;
37 }
38
39 void downdate()
40 {
CAPITOLUL 3. IOI 2018 280

41 if(dta)
42 {
43 if(lc)lc->tagdta(dta);
44 if(rc)rc->tagdta(dta);
45 dta=0;
46 }
47 }
48
49 void update()
50 {
51 if(lc->s<rc->s)
52 {
53 s=lc->s;
54 cnt=lc->cnt;
55 }
56 else if(rc->s<lc->s)
57 {
58 s=rc->s;
59 cnt=rc->cnt;
60 }
61 else
62 {
63 s=lc->s;
64 cnt=lc->cnt+rc->cnt;
65 }
66 }
67
68 void Add(int l,int r,const int &a,const int &b,const int &c)
69 {
70 if(l>=a&&r<=b)
71 {
72 tagdta(c);
73 return;
74 }
75 int mid=l+r>>1;
76 downdate();
77 if(a<=mid)lc->Add(l,mid,a,b,c);
78 if(b>mid)rc->Add(mid+1,r,a,b,c);
79 update();
80 }
81
82 } ndl[maxn*2],*ns=ndl,*root;
83
84 node* build(int l,int r)
85 {
86 node *x=ns++;
87 x->dta=0;
88 if(l==r)
89 {
90 x->s=presum[l];
91 x->cnt=1;
92 x->lc=x->rc=NULL;
93 }
94 else
95 {
96 int mid=l+r>>1;
97 x->lc=build(l,mid);
98 x->rc=build(mid+1,r);
99 x->update();
100 }
101 return x;
102 }
103
104 int& seat(int *s,const int &x,const int &y)
105 {
106 if(x>=0&&x<H&&y>=0&&y<W)return s[x*W+y];
107 return NUM;
108 }
109
110 const int ValTable[2][3]={
111 {1,-1,-1},
112 {1,1,-1}
113 };
114
115 int CalcVal(int *s,const int &x,const int &y,const int w=15)
116 {
CAPITOLUL 3. IOI 2018 281

117 int res=0;


118 for(int k=0;k<4;k++)
119 if(w>>k&1)
120 {
121 int i=x-1+dx[k];
122 int j=y-1+dy[k];
123 int t=seat(s,x,y);
124 int a=seat(s,i+dx[k^1],j+dy[k^1]); // XOR
125 int b=seat(s,i+dx[k^2],j+dy[k^2]);
126 int c=seat(s,i+dx[k],j+dy[k]);
127 res+=ValTable[c<t][(a<t)+(b<t)];
128 }
129 //cout<<endl;
130 return res;
131 }
132
133 void give_initial_chart(int H, int W, std::vector<int> R,
134 std::vector<int> C)
135 {
136 ::H=H; // https://www.geeksforgeeks.org/scope-resolution-operator-in-c/
137 ::W=W;
138 NUM=H*W;
139 ::R.assign(NUM,0);
140 ::C.assign(NUM,0);
141 for(int i=0;i<NUM;i++)
142 {
143 ::R[i]=R[i];
144 ::C[i]=C[i];
145 seat(S,R[i],C[i])=seat(T,R[i],C[i])=i;
146 }
147 for(int i=0;i<NUM;i++)
148 {
149 val[i]=CalcVal(S,R[i],C[i]);
150 if(i==0)presum[i]=val[i];
151 else presum[i]=presum[i-1]+val[i];
152 }
153 //for(int i=0;i<NUM;i++)cout<<val[i]<<’ ’;cout<<endl;
154 root=build(0,NUM-1);
155 }
156
157 void UpdateVal(int x,int v)
158 {
159 root->Add(0,NUM-1,x,NUM-1,v-val[x]);
160 val[x]=v;
161 }
162
163 int swap_seats(int a, int b)
164 {
165 swap(seat(T,R[a],C[a]),seat(T,R[b],C[b]));
166 vector<int> p;
167 for(int i=-1;i<=1;i++)
168 for(int j=-1;j<=1;j++)
169 {
170 int t;
171 t=seat(T,R[a]+i,C[a]+j);
172 if(t!=NUM)p.push_back(t);
173 t=seat(T,R[b]+i,C[b]+j);
174 if(t!=NUM)p.push_back(t);
175 }
176 swap(R[a],R[b]);
177 swap(C[a],C[b]);
178 sort(p.begin(),p.end());
179 p.erase(unique(p.begin(),p.end()),p.end());
180 for(auto x:p)
181 {
182 UpdateVal(x,CalcVal(T,R[x],C[x]));
183 }
184 swap(seat(S,R[a],C[a]),seat(S,R[b],C[b]));
185 if(root->s!=4)return 0;
186 return root->cnt;
187 }
188
189 // ----------------------------------------------
190
191 namespace
192 {
CAPITOLUL 3. IOI 2018 282

193 int read_int()


194 {
195 int x;
196 if (scanf("%d", &x) != 1)
197 {
198 fprintf(stderr, "Error while reading input\n");
199 exit(1);
200 }
201 return x;
202 }
203 } // namespace
204
205 int main()
206 {
207 auto t1 = clock();
208
209 std::freopen("../in/06-04.txt", "r", stdin) ;
210 std::freopen("seats.out", "w", stdout) ;
211
212 int H = read_int();
213 int W = read_int();
214 int Q = read_int();
215
216 std::vector<int> R(H * W), C(H * W);
217 for (int i = 0; i < H * W; ++i)
218 {
219 R[i] = read_int();
220 C[i] = read_int();
221 }
222 std::vector<int> A(Q), B(Q);
223
224 for (int j = 0; j < Q; ++j)
225 {
226 A[j] = read_int();
227 B[j] = read_int();
228 }
229
230 auto t2 = clock();
231
232 give_initial_chart(H, W, R, C);
233
234 auto t3 = clock();
235
236 for (int j = 0; j < Q; ++j)
237 {
238 int answer = swap_seats(A[j], B[j]);
239 printf("%d\n", answer);
240 }
241
242 auto t4 = clock();
243 fclose(stdout);
244
245 // reset console output
246 freopen("CON", "w", stdout);
247
248 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
249 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
250 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
251
252 return 0;
253 }
254 /*
255 t2-t1 = 3.21
256 t3-t2 = 0.715
257 t4-t3 = 2.651
258
259 Process returned 0 (0x0) execution time : 6.649 s
260 Press any key to continue.
261 */

Listing 3.2.8: seats_76357.cpp


1 // https://oj.uz/submission/76357
2
3 #include <ctime>
CAPITOLUL 3. IOI 2018 283

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


5
6 //#include <cstdio>
7 #include <stdio.h> // citire mai rapida !!!
8
9 #include <algorithm>
10 #include <vector>
11 #include <set>
12
13 #include <iostream>
14
15 using namespace std;
16
17 const int Z = 1<<20;
18
19 struct node
20 {
21 node()
22 {
23 sum = min = 0; cnt = 1;
24 }
25
26 node(int v)
27 {
28 sum = min = v; cnt = 1;
29 }
30
31 int sum, min, cnt;
32
33 node operator *(const node &t) const
34 {
35 node res;
36
37 res.sum = sum + t.sum;
38
39 if (min < sum + t.min) res.min = min;
40 else res.min = sum + t.min;
41
42 res.cnt = 0;
43 if (res.min == min) res.cnt += cnt;
44 if (res.min == sum + t.min) res.cnt += t.cnt;
45
46 return res;
47 }
48
49 void add(int p)
50 {
51 sum += p;
52 min += p;
53 }
54 } IT[Z*2];
55
56 int N, A[1001001];
57 bool chk[1001001];
58 int H, W;
59 vector<int> R, C;
60
61 void upd(int x)
62 {
63 x /= 2;
64 while (x)
65 {
66 IT[x] = IT[x*2] * IT[x*2+1];
67 x /= 2;
68 }
69 }
70
71 set<int> ups;
72
73 void add(int i, int p, bool up = true)
74 {
75 if (p > 0)
76 {
77 if (chk[i]) return;
78 chk[i] = 1;
79 }
CAPITOLUL 3. IOI 2018 284

80 else
81 {
82 if (!chk[i]) return;
83 chk[i] = 0;
84 }
85
86 int x = R[i], y = C[i];
87 int dx[5] = {0,1,0,-1,0};
88 int dy[5] = {1,0,-1,0,1};
89
90 for (int k=0;k<4;k++)
91 {
92 int u[2];
93 for (int d=0;d<2;d++)
94 {
95 int px = x + dx[k+d];
96 int py = y + dy[k+d];
97 if (px < 0 || px >= H || py < 0 || py >= W) u[d] = N;
98 else u[d] = A[px*W+py];
99 }
100
101 int s = N, e = 0;
102 if (i < u[0] && i < u[1])
103 {
104 s = i + Z;
105 e = min(u[0],u[1]) + Z;
106 }
107
108 if (u[0] < i && u[1] < i)
109 {
110 s = max(u[0],u[1]) + Z;
111 e = i + Z;
112 }
113
114 if (s < e)
115 {
116 IT[s].add(+p);
117 IT[e].add(-p);
118 if (up)
119 {
120 ups.insert(s);
121 ups.insert(e);
122 }
123 }
124 }
125 }
126
127 void give_initial_chart(int H_, int W_, vector<int> R_, vector<int> C_)
128 {
129 H = H_; W = W_; R = R_; C = C_; N = H * W;
130 for (int i=0;i<N;i++) A[R[i]*W+C[i]] = i;
131 IT[N+Z].add(10000);
132 for (int i=0;i<N;i++) add(i,1,false);
133 for (int i=Z-1;i>=1;i--) IT[i] = IT[i*2] * IT[i*2+1];
134 }
135
136 int swap_seats(int a, int b)
137 {
138 int dx[5] = {0,1,0,-1,0};
139 int dy[5] = {1,0,-1,0,0};
140 for (int i : {a,b}) for (int k=0;k<5;k++)
141 {
142 int x = R[i] + dx[k];
143 int y = C[i] + dy[k];
144 if (x < 0 || x >= H || y < 0 || y >= W) continue;
145 add(A[x*W+y],-1);
146 }
147
148 swap(R[a],R[b]);
149 swap(C[a],C[b]);
150 A[R[a]*W+C[a]] = a;
151 A[R[b]*W+C[b]] = b;
152
153 for (int i : {a,b}) for (int k=0;k<5;k++)
154 {
155 int x = R[i] + dx[k];
CAPITOLUL 3. IOI 2018 285

156 int y = C[i] + dy[k];


157 if (x < 0 || x >= H || y < 0 || y >= W) continue;
158 add(A[x*W+y],+1);
159 }
160
161 for (int x : ups) upd(x);
162 ups.clear();
163
164 return IT[1].min == 4 ? IT[1].cnt : 0;
165 }
166
167 // ----------------------------------------------
168
169 namespace
170 {
171 int read_int()
172 {
173 int x;
174 if (scanf("%d", &x) != 1)
175 {
176 fprintf(stderr, "Error while reading input\n");
177 exit(1);
178 }
179 return x;
180 }
181 } // namespace
182
183 int main()
184 {
185 auto t1 = clock();
186
187 std::freopen("../in/06-04.txt", "r", stdin) ;
188 std::freopen("seats.out", "w", stdout) ;
189
190 int H = read_int();
191 int W = read_int();
192 int Q = read_int();
193
194 std::vector<int> R(H * W), C(H * W);
195 for (int i = 0; i < H * W; ++i)
196 {
197 R[i] = read_int();
198 C[i] = read_int();
199 }
200 std::vector<int> A(Q), B(Q);
201
202 for (int j = 0; j < Q; ++j)
203 {
204 A[j] = read_int();
205 B[j] = read_int();
206 }
207
208 auto t2 = clock();
209
210 give_initial_chart(H, W, R, C);
211
212 auto t3 = clock();
213
214 for (int j = 0; j < Q; ++j)
215 {
216 int answer = swap_seats(A[j], B[j]);
217 printf("%d\n", answer);
218 }
219
220 auto t4 = clock();
221 fclose(stdout);
222
223 // reset console output
224 freopen("CON", "w", stdout);
225
226 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
227 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
228 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
229
230 return 0;
231 }
CAPITOLUL 3. IOI 2018 286

232 /*
233 t2-t1 = 3.182
234 t3-t2 = 0.458
235 t4-t3 = 4.476
236
237 Process returned 0 (0x0) execution time : 8.239 s
238 Press any key to continue.
239 */

Listing 3.2.9: seats_80434.cpp


1 // https://oj.uz/submission/80434
2
3 //#include <cstdio>
4 #include <stdio.h> // citire mai rapida !!!
5
6 #include "seats.h"
7
8 #include <bits/stdc++.h>
9
10 using namespace std;
11
12 typedef long long int lld;
13 typedef pair<int,int> pii;
14
15 int h,w;
16 int r[1000010];
17 int c[1000010];
18 pii arr[1000010];
19
20 //segtree
21 pii sum[8000000];
22 pii minimo[8000000];
23 int counter[8000000];
24 vector<vector<int> >table;
25
26 void update_pos(int a, int b)
27 {
28 if(a<0 || a>h-1)return;
29 if(b<0 || b>w-1)return;
30 int t[3][3];
31 for(int i=0;i<3;i++)
32 {
33 for(int j=0;j<3;j++)t[i][j]=1;
34 }
35 t[1][1]=0;
36 if(a==0 || b==0 || table[a-1][b-1]>table[a][b])t[0][0]=0;
37 if(a==0 || b==w-1 || table[a-1][b+1]>table[a][b])t[0][2]=0;
38 if(a==h-1 || b==0 || table[a+1][b-1]>table[a][b])t[2][0]=0;
39 if(a==h-1 || b==w-1 || table[a+1][b+1]>table[a][b])t[2][2]=0;
40 if(a==0 || table[a-1][b]>table[a][b])t[0][1]=0;
41 if(b==0 || table[a][b-1]>table[a][b])t[1][0]=0;
42 if(a==h-1 || table[a+1][b]>table[a][b])t[2][1]=0;
43 if(b==w-1 || table[a][b+1]>table[a][b])t[1][2]=0;
44
45 int x,y,z,w;
46 x=t[0][0]+t[0][1]+t[1][0]+t[1][1];
47 y=t[1][0]+t[1][1]+t[2][0]+t[2][1];
48 z=t[0][1]+t[0][2]+t[1][1]+t[1][2];
49 w=t[1][1]+t[1][2]+t[2][1]+t[2][2];
50 int cnt[5];
51 for(int i=0;i<5;i++)cnt[i]=0;
52 cnt[x]++;
53 cnt[y]++;
54 cnt[z]++;
55 cnt[w]++;
56
57 arr[table[a][b]].first=cnt[0]-cnt[1];
58 arr[table[a][b]].second=cnt[2]-cnt[3];
59 }
60
61 pii Sum(pii a, pii b)
62 {
63 pii c=pii(a.first+b.first,a.second+b.second);
64 return c;
CAPITOLUL 3. IOI 2018 287

65 }
66
67 pii min(pii a, pii b)
68 {
69 if(a.second<b.second)return a;
70 if(a.second>b.second)return b;
71 if(a.first>b.first)return b;
72 return a;
73 }
74
75 void build(int a, int b, int node)
76 {
77 if(a==b)
78 {
79 sum[node]=arr[a];
80 minimo[node]=arr[a];
81 counter[node]=1;
82 return;
83 }
84
85 int mid=(a+b)/2;
86 build(a,mid,2*node);
87 build(mid+1,b,2*node+1);
88 sum[node]=Sum(sum[2*node],sum[2*node+1]);
89 minimo[node]=min(minimo[2*node],Sum(minimo[2*node+1],sum[2*node]));
90 counter[node]=0;
91 if(minimo[node]==minimo[2*node])
92 {
93 counter[node]+=counter[2*node];
94 }
95
96 if(minimo[node]==Sum(minimo[2*node+1],sum[2*node]))
97 {
98 counter[node]+=counter[2*node+1];
99 }
100 }
101
102 void update(int a, int b, int node, int pos)
103 {
104 if(pos<a || b<pos)return;
105 if(a==b)
106 {
107 sum[node]=arr[a];
108 minimo[node]=arr[a];
109 counter[node]=1;
110 return;
111 }
112
113 int mid=(a+b)/2;
114 update(a,mid,2*node,pos);
115 update(mid+1,b,2*node+1,pos);
116 sum[node]=Sum(sum[2*node],sum[2*node+1]);
117 minimo[node]=min(minimo[2*node],Sum(minimo[2*node+1],sum[2*node]));
118
119 counter[node]=0;
120 if(minimo[node]==minimo[2*node])
121 {
122 counter[node]+=counter[2*node];
123 }
124
125 if(minimo[node]==Sum(minimo[2*node+1],sum[2*node]))
126 {
127 counter[node]+=counter[2*node+1];
128 }
129 }
130
131 void give_initial_chart(int H, int W, std::vector<int> R,
132 std::vector<int> C)
133 {
134 h=H;
135 w=W;
136 for(int i=0;i<h;i++)
137 {
138 vector<int> a;
139 for(int j=0;j<w;j++)a.push_back(-1);
140 table.push_back(a);
CAPITOLUL 3. IOI 2018 288

141 }
142
143 for(int i=0;i<w*h;i++)
144 {
145 r[i]=R[i];
146 c[i]=C[i];
147 table[R[i]][C[i]]=i;
148 }
149
150 for(int i=0;i<h;i++)
151 {
152 for(int j=0;j<w;j++)update_pos(i,j);
153 }
154
155 build(0,w*h-1,1);
156 //for(int i=0;i<h*w;i++)cout<<arr[i].second<<endl;
157 //cout<<counter[1]<<endl;
158 }
159
160 int swap_seats(int a, int b)
161 {
162 swap(table[r[a]][c[a]],table[r[b]][c[b]]);
163 swap(r[a],r[b]);
164 swap(c[a],c[b]);
165 for(int i=-1;i<2;i++)
166 {
167 for(int j=-1;j<2;j++)
168 {
169 update_pos(r[a]+i,c[a]+j);
170 if(r[a]+i>=0 && r[a]+i<=h-1 && c[a]+j>=0 && c[a]+j<=w-1)
171 update(0,h*w-1,1,table[r[a]+i][c[a]+j]);
172
173 update_pos(r[b]+i,c[b]+j);
174 if(r[b]+i>=0 && r[b]+i<=h-1 && c[b]+j>=0 && c[b]+j<=w-1)
175 update(0,h*w-1,1,table[r[b]+i][c[b]+j]);
176 }
177 }
178
179 return counter[1];
180 }
181
182 // ----------------------------------------------
183
184 namespace
185 {
186 int read_int()
187 {
188 int x;
189 if (scanf("%d", &x) != 1)
190 {
191 fprintf(stderr, "Error while reading input\n");
192 exit(1);
193 }
194 return x;
195 }
196 }
197
198 int main()
199 {
200 auto t1 = clock();
201
202 std::freopen("../in/06-04.txt", "r", stdin) ;
203 std::freopen("seats.out", "w", stdout) ;
204
205 int H = read_int();
206 int W = read_int();
207 int Q = read_int();
208
209 std::vector<int> R(H * W), C(H * W);
210 for (int i = 0; i < H * W; ++i)
211 {
212 R[i] = read_int();
213 C[i] = read_int();
214 }
215 std::vector<int> A(Q), B(Q);
216
CAPITOLUL 3. IOI 2018 289

217 for (int j = 0; j < Q; ++j)


218 {
219 A[j] = read_int();
220 B[j] = read_int();
221 }
222
223 auto t2 = clock();
224
225 give_initial_chart(H, W, R, C);
226
227 auto t3 = clock();
228
229 for (int j = 0; j < Q; ++j)
230 {
231 int answer = swap_seats(A[j], B[j]);
232 printf("%d\n", answer);
233 }
234
235 auto t4 = clock();
236 fclose(stdout);
237
238 // reset console output
239 freopen("CON", "w", stdout);
240
241 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
242 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
243 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
244
245 return 0;
246 }
247 /*
248 t2-t1 = 0.87
249 t3-t2 = 0.859
250 t4-t3 = 4.672
251
252 Process returned 0 (0x0) execution time : 6.464 s
253 Press any key to continue.
254 */

Listing 3.2.10: seats_81209.cpp


1 // https://oj.uz/submission/81209
2
3 //#include <cstdio>
4 #include <stdio.h> // citire mai rapida !!!
5
6 #include "seats.h"
7
8 #include <algorithm>
9 #include <bits/stdc++.h>
10
11 std::vector<int> r;
12
13 using namespace std;
14
15 #define rep(i,n) for(int i=0;i<n;i++)
16
17 int T[1<<21], A[1<<21], cnt[1<<21];
18 int R[1000010], C[1000010];
19 int N, M, P;
20
21 void init(int rt, int l, int r)
22 {
23 cnt[rt] = r - l + 1;
24 if(l == r) return;
25 int m = (l + r) >> 1;
26 init(rt<<1, l, m);
27 init(rt<<1|1, m+1, r);
28 }
29
30 inline void pushup(int rt)
31 {
32 if(T[rt<<1] == T[rt<<1|1])
33 cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1], T[rt] = T[rt<<1];
34 else
CAPITOLUL 3. IOI 2018 290

35 if(T[rt<<1] < T[rt<<1|1])


36 cnt[rt] = cnt[rt<<1], T[rt] = T[rt<<1];
37 else
38 cnt[rt] = cnt[rt<<1|1], T[rt] = T[rt<<1|1];
39 }
40
41 void add(int rt, int l, int r, int s, int e, int c)
42 {
43 if(s <= l && r <= e)
44 {
45 T[rt] += c;
46 A[rt] += c;
47 return;
48 }
49
50 if(A[rt])
51 {
52 T[rt<<1] += A[rt];
53 A[rt<<1] += A[rt];
54 T[rt<<1|1] += A[rt];
55 A[rt<<1|1] += A[rt];
56 A[rt] = 0;
57 }
58
59 int m = (l + r) >> 1;
60 if(s <= m) add(rt<<1, l, m, s, e, c);
61 if(m < e) add(rt<<1|1, m+1, r, s, e, c);
62 pushup(rt);
63 }
64
65 void add(int l, int r, int v)
66 {
67 add(1, 1, P, l, r, v);
68 }
69
70 int rval[1000010];
71
72 void get_val(int x, int y, int rr[])
73 {
74 int rz = 0;
75 for(int dx : {0,1}) for(int dy : {0,1})
76 {
77 int ni = x + dx, nj = y + dy, val = P + 1;
78 if(1 <= ni && ni <= N && 1 <= nj && nj <= M)
79 {
80 val = rval[(ni-1)*M+(nj-1)];
81 }
82 rr[rz++] = val;
83 }
84
85 sort(rr, rr+4);
86 }
87
88 void change(int r, int c, int x)
89 {
90 for(int dx:{-1,0}) for(int dy:{-1,0})
91 {
92 int tt[4];
93 get_val(r+dx, c+dy, tt);
94 if(tt[0]<tt[1]) add(tt[0], tt[1]-1, -1);
95 if(tt[2]<tt[3]) add(tt[2], tt[3]-1, -100);
96 }
97
98 rval[(r-1)*M+(c-1)] = x;
99 for(int dx:{-1,0}) for(int dy:{-1,0})
100 {
101 int tt[4];
102 get_val(r+dx, c+dy, tt);
103 if(tt[0]<tt[1]) add(tt[0], tt[1]-1, 1);
104 if(tt[2]<tt[3]) add(tt[2], tt[3]-1, 100);
105 }
106 }
107
108 // ....................................................
109
110 void give_initial_chart(int H, int W, std::vector<int> tR,
CAPITOLUL 3. IOI 2018 291

111 std::vector<int> tC)


112 {
113 N = H; M = W;
114 P = N * M;
115 init(1, 1, P);
116 rep(i, P) R[i + 1] = tR[i] + 1, C[i + 1] = tC[i] + 1;
117
118 for(int i=1;i<=P;i++) rval[(R[i]-1)*M+(C[i]-1)] = i;
119
120 rep(i, N+1) rep(j, M+1)
121 {
122 int tt[4];
123 get_val(i, j, tt);
124 if(tt[0]<tt[1]) add(tt[0], tt[1]-1, 1);
125 if(tt[2]<tt[3]) add(tt[2], tt[3]-1, 100);
126 }
127 }
128
129 int swap_seats(int a, int b)
130 {
131 ++a; ++b;
132 change(R[a], C[a], b);
133 change(R[b], C[b], a);
134 swap(R[a], R[b]);
135 swap(C[a], C[b]);
136 return cnt[1];
137 }
138
139 // ----------------------------------------------
140
141 namespace
142 {
143 int read_int()
144 {
145 int x;
146 if (scanf("%d", &x) != 1)
147 {
148 fprintf(stderr, "Error while reading input\n");
149 exit(1);
150 }
151 return x;
152 }
153 } // namespace
154
155 int main()
156 {
157 auto t1 = clock();
158
159 std::freopen("../in/06-04.txt", "r", stdin) ;
160 std::freopen("seats.out", "w", stdout) ;
161
162 int H = read_int();
163 int W = read_int();
164 int Q = read_int();
165
166 std::vector<int> R(H * W), C(H * W);
167 for (int i = 0; i < H * W; ++i)
168 {
169 R[i] = read_int();
170 C[i] = read_int();
171 }
172 std::vector<int> A(Q), B(Q);
173
174 for (int j = 0; j < Q; ++j)
175 {
176 A[j] = read_int();
177 B[j] = read_int();
178 }
179
180 auto t2 = clock();
181
182 give_initial_chart(H, W, R, C);
183
184 auto t3 = clock();
185
186 for (int j = 0; j < Q; ++j)
CAPITOLUL 3. IOI 2018 292

187 {
188 int answer = swap_seats(A[j], B[j]);
189 printf("%d\n", answer);
190 }
191
192 auto t4 = clock();
193 fclose(stdout);
194
195 // reset console output
196 freopen("CON", "w", stdout);
197
198 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
199 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
200 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
201
202 return 0;
203 }
204 /*
205 t2-t1 = 0.875
206 t3-t2 = 2.781
207 t4-t3 = 3.508
208
209 Process returned 0 (0x0) execution time : 7.227 s
210 Press any key to continue.
211 */

3.2.3 *Rezolvare detaliat 

3.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  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.
Planicaµ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
ec rei c l torii aveµi forma unui om. La sfâr³itul ec 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 ecare 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  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 3. IOI 2018 293

a X ³i Y : tablouri unidimensionale de dimensiune M . Pentru ecare 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  obµinute


dup  cum este indicat în Observaµiile de implementare.
Funcµia check_validity este apelat  o singur  dat  pentru ecare 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  e 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 3.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 ecare 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  0 & j $ k & M  1, Xj , Yj  j Xk , Yk  ³i Yj , Xj  j Xk , Yk .
a Pentru ecare 0 & i & Q  1
` 0 & Li & Si & N  1
` 0 & Ei & Ri & N  1
CAPITOLUL 3. IOI 2018 294

` 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 a³eaz  valoarea întoars  de check_validity în urm toarea form :


alinia 1  i (0 & i & Q  1): Ai

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

You start the travel in human form, transform yourself from human form to wolf form at
the vertex Vq , and nish 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 3. IOI 2018 295

located. This range can be eciently 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 corres-
ponds 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 .

3.3.2 Coduri surs 

Listing 3.3.1: compile_cpp.sh


1 #!/bin/bash
2
3 TASK=werewolf
4
5 g++ -std=gnu++14 -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 3.3.2: werewolf.h


1 #include <vector>
2
3 std::vector<int> check_validity(int N,
4 std::vector<int> X, std::vector<int> Y,
5 std::vector<int> S, std::vector<int> E,
6 std::vector<int> L, std::vector<int> R);

Listing 3.3.3: werewolf.cpp


1 #include "werewolf.h"
2
3 std::vector<int> check_validity(int N,
4 std::vector<int> X, std::vector<int> Y,
5 std::vector<int> S, std::vector<int> E,
6 std::vector<int> L, std::vector<int> R)
7 {
8 int Q = S.size();
9 std::vector<int> A(Q);
10 for (int i = 0; i < Q; ++i)
11 {
12 A[i] = 0;
13 }
14 return A;
15 }

Listing 3.3.4: grader.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include <vector>
4 #include "werewolf.h"
5
6 namespace
7 {
8
9 int read_int()
10 {
11 int x;
12 if (scanf("%d", &x) != 1)
13 {
14 fprintf(stderr, "Error while reading input\n");
15 exit(1);
CAPITOLUL 3. IOI 2018 296

16 }
17 return x;
18 }
19
20 } // namespace
21
22 int main()
23 {
24 int N = read_int();
25 int M = read_int();
26 int Q = read_int();
27 std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);
28
29 for (int j = 0; j < M; ++j)
30 {
31 X[j] = read_int();
32 Y[j] = read_int();
33 }
34 for (int i = 0; i < Q; ++i)
35 {
36 S[i] = read_int();
37 E[i] = read_int();
38 L[i] = read_int();
39 R[i] = read_int();
40 }
41
42 std::vector<int> A = check_validity(N, X, Y, S, E, L, R);
43 for (size_t i = 0; i < A.size(); ++i)
44 {
45 printf("%d\n", A[i]);
46 }
47 return 0;
48 }

Listing 3.3.5: werewolf-model.cpp


1 // https://oj.uz/submission/75793
2 // https://oj.uz/problems/source/364
3
4 #include "werewolf.h"
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 const int mxN=2e5;
10 int n, q, p[mxN], st[2][mxN], en[2][mxN], dt, anc[2][mxN],
11 r[mxN], ta[mxN], ft[mxN+1];
12 vector<int> adj1[mxN], adj2[mxN];
13 vector<array<int, 2>> b[mxN+1];
14 bool d[mxN];
15
16 int find(int x)
17 {
18 return x==r[x]?x:(r[x]=find(r[x]));
19 }
20
21 void dfs(int u, int st[mxN], int en[mxN])
22 {
23 st[u]=dt++;
24 for(int v : adj2[u])
25 dfs(v, st, en);
26 en[u]=dt;
27 }
28
29 void a(int st[mxN], int en[mxN], int anc[mxN])
30 {
31 for(int i=0; i<n; ++i)
32 r[i]=i;
33 for(int i=0; i<n; ++i)
34 {
35 for(int j : adj1[p[i]])
36 {
37 if(!d[j]||(j=find(j))==p[i])
38 continue;
39 r[j]=p[i];
CAPITOLUL 3. IOI 2018 297

40 adj2[p[i]].push_back(j);
41 }
42 for(array<int, 2> c : b[p[i]])
43 anc[c[1]]=find(c[0]);
44 b[p[i]].clear();
45 d[p[i]]=1;
46 }
47 dfs(p[n-1], st, en);
48 }
49
50 int qry(int i)
51 {
52 int r=0;
53 for(; i; i-=i&-i)
54 r+=ft[i];
55 return r;
56 }
57
58 vector<int> check_validity(int n2, vector<int> x, vector<int> y,
59 vector<int> s, vector<int> e,
60 vector<int> l, vector<int> r)
61 {
62 n=n2;
63 for(int i=0; i<x.size(); ++i)
64 {
65 adj1[x[i]].push_back(y[i]);
66 adj1[y[i]].push_back(x[i]);
67 }
68 q=s.size();
69 for(int i=0; i<n; ++i)
70 p[i]=n-1-i;
71 for(int i=0; i<q; ++i)
72 b[l[i]].push_back({s[i], i});
73 a(st[0], en[0], anc[0]);
74 for(int i=0; i<n; ++i)
75 {
76 adj2[i].clear();
77 p[i]=i;
78 }
79 memset(d, 0, n);
80 dt=0;
81 for(int i=0; i<q; ++i)
82 b[r[i]].push_back({e[i], i});
83 a(st[1], en[1], anc[1]);
84 for(int i=0; i<n; ++i)
85 ta[st[0][i]]=st[1][i];
86 for(int i=0; i<q; ++i)
87 {
88 b[st[0][anc[0][i]]].push_back({i, -1});
89 b[en[0][anc[0][i]]].push_back({i, 1});
90 }
91 vector<int> ans=vector<int>(q);
92 for(int i=0; i<n; ++i)
93 {
94 for(int j=ta[i]+1; j<=n; j+=j&-j)
95 ++ft[j];
96 for(array<int, 2> c : b[i+1])
97 ans[c[0]]+=c[1]*
98 (qry(en[1][anc[1][c[0]]])-qry(st[1][anc[1][c[0]]]));
99 }
100 for(int i=0; i<q; ++i)
101 ans[i]=ans[i]>0;
102 return ans;
103 }
104 //======================================================================
105 namespace
106 {
107 int read_int()
108 {
109 int x;
110 if (scanf("%d", &x) != 1)
111 {
112 fprintf(stderr, "Error while reading input\n");
113 exit(1);
114 }
115 return x;
CAPITOLUL 3. IOI 2018 298

116 }
117 } // namespace
118
119 int main()
120 {
121 auto t1 = clock();
122
123 std::freopen("../in/04-20.txt", "r", stdin) ;
124 std::freopen("werewolf.out", "w", stdout) ;
125
126 int N = read_int();
127 int M = read_int();
128 int Q = read_int();
129 std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);
130
131 for (int j = 0; j < M; ++j)
132 {
133 X[j] = read_int();
134 Y[j] = read_int();
135 }
136 for (int i = 0; i < Q; ++i)
137 {
138 S[i] = read_int();
139 E[i] = read_int();
140 L[i] = read_int();
141 R[i] = read_int();
142 }
143
144 auto t2 = clock();
145
146 std::vector<int> A = check_validity(N, X, Y, S, E, L, R);
147
148 auto t3 = clock();
149
150 for (size_t i = 0; i < A.size(); ++i)
151 {
152 printf("%d\n", A[i]);
153 }
154
155 auto t4 = clock();
156
157 fclose(stdout);
158
159 // reset console output
160 freopen("CON", "w", stdout);
161
162 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
164 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
165
166 return 0;
167 }
168 //======================================================================
169 /*
170 t2-t1 = 2.685
171 t3-t2 = 1.384
172 t4-t3 = 0.071
173
174 Process returned 0 (0x0) execution time : 4.888 s
175 Press any key to continue.
176 */
177 //======================================================================

Listing 3.3.6: werewolf_75105.cpp


1 // https://oj.uz/submission/75105
2
3 #include <stdio.h> // citire mai rapida !!!
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 typedef long long llong;
9
10 int n, m, q;
CAPITOLUL 3. IOI 2018 299

11 vector<int> edge[200000];
12
13 struct UF
14 {
15 int par[200000];
16 UF()
17 {
18 for (int i = 0; i < 200000; ++i) par[i] = i;
19 }
20 int find(int x)
21 {
22 if (par[x] != x) return par[x] = find(par[x]);
23 return x;
24 }
25 } Luf, Ruf;
26
27 int Lpar[200000][20];
28 int Rpar[200000][20];
29 vector<int> Lchild[200000];
30 vector<int> Rchild[200000];
31
32 int Lst[200000];
33 int Led[200000];
34 int Rst[200000];
35 int Red[200000];
36 int LRd[200000];
37
38 void dfs(vector<int> child[], int st[], int ed[], int x, int &d)
39 {
40 st[x] = ++d;
41 for (int i : child[x])
42 {
43 dfs(child, st, ed, i, d);
44 }
45 ed[x] = d;
46 }
47
48 struct query
49 {
50 int i, t, x;
51 query(int i, int t, int x) : i(i), t(t), x(x) {}
52 bool operator<(const query &p) const
53 {
54 return t < p.t;
55 }
56 };
57
58 int seg[200001];
59 void update(int i)
60 {
61 while (i <= n)
62 {
63 ++seg[i];
64 i += i & -i;
65 }
66 }
67
68 int sum(int i)
69 {
70 int ret = 0;
71 while (i)
72 {
73 ret += seg[i];
74 i -= i & -i;
75 }
76 return ret;
77 }
78
79 vector<int> check_validity(int N, vector<int> U, vector<int> V,
80 vector<int> X, vector<int> Y,
81 vector<int> L, vector<int> R)
82 {
83 n = N;
84 m = U.size();
85 q = X.size();
86 for (int i = 0; i < m; ++i)
CAPITOLUL 3. IOI 2018 300

87 {
88 edge[U[i]].push_back(V[i]);
89 edge[V[i]].push_back(U[i]);
90 }
91 for (int i = 0; i < n; ++i) Lpar[i][0] = 0;
92 for (int i = n; i--; )
93 {
94 for (int j : edge[i])
95 {
96 if (j < i) continue;
97 int x = Luf.find(i);
98 int y = Luf.find(j);
99 if (x == y) continue;
100 Luf.par[y] = x;
101 Lpar[y][0] = x;
102 Lchild[x].push_back(y);
103 }
104 }
105
106 for (int i = 0; i < n; ++i) Rpar[i][0] = n - 1;
107 for (int i = 0; i < n; ++i)
108 {
109 for (int j : edge[i])
110 {
111 if (i < j) continue;
112 int x = Ruf.find(i);
113 int y = Ruf.find(j);
114 if (x == y) continue;
115 Ruf.par[y] = x;
116 Rpar[y][0] = x;
117 Rchild[x].push_back(y);
118 }
119 }
120
121 for (int i = 1; i < 20; ++i)
122 {
123 for (int j = 0; j < n; ++j)
124 {
125 Lpar[j][i] = Lpar[Lpar[j][i - 1]][i - 1];
126 Rpar[j][i] = Rpar[Rpar[j][i - 1]][i - 1];
127 }
128 }
129
130 int ord;
131 ord = 0;
132 dfs(Lchild, Lst, Led, Luf.find(0), ord);
133 ord = 0;
134 dfs(Rchild, Rst, Red, Ruf.find(0), ord);
135 for (int i = 0; i < n; ++i) LRd[Lst[i] - 1] = Rst[i];
136
137 vector<query> qs;
138 for (int it = 0; it < q; ++it)
139 {
140 int x = X[it];
141 int y = Y[it];
142 int l = L[it];
143 int r = R[it];
144 for (int i = 20; i--; ) if (l <= Lpar[x][i]) x = Lpar[x][i];
145 for (int i = 20; i--; ) if (Rpar[y][i] <= r) y = Rpar[y][i];
146 qs.emplace_back(~it, Lst[x] - 1, y);
147 qs.emplace_back(it, Led[x], y);
148 }
149
150 sort(qs.begin(), qs.end());
151 vector<int> ret(q, 0);
152 ord = 0;
153 for (query i : qs)
154 {
155 while (ord < i.t) update(LRd[ord++]);
156 int v = sum(Red[i.x]) - sum(Rst[i.x] - 1);
157 if (i.i < 0) ret[~i.i] -= v;
158 else ret[i.i] += v;
159 }
160
161 for (int &i : ret) i = (i > 0);
162 return ret;
CAPITOLUL 3. IOI 2018 301

163 }
164
165 //-------------- start grader ------------------
166
167 namespace
168 {
169 int read_int()
170 {
171 int x;
172 if (scanf("%d", &x) != 1)
173 {
174 fprintf(stderr, "Error while reading input\n");
175 exit(1);
176 }
177 return x;
178 }
179 } // namespace
180
181 int main()
182 {
183 auto t1 = clock();
184
185 std::freopen("../in/04-19.txt", "r", stdin);
186 std::freopen("werewolf.out", "w", stdout);
187
188 int N = read_int();
189 int M = read_int();
190 int Q = read_int();
191
192 std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);
193
194 for (int j = 0; j < M; ++j)
195 {
196 X[j] = read_int();
197 Y[j] = read_int();
198 }
199
200 for (int i = 0; i < Q; ++i)
201 {
202 S[i] = read_int();
203 E[i] = read_int();
204 L[i] = read_int();
205 R[i] = read_int();
206 }
207
208 fclose(stdin);
209 auto t2 = clock();
210
211 std::vector<int> A = check_validity(N, X, Y, S, E, L, R);
212
213 auto t3 = clock();
214
215 for (size_t i = 0; i < A.size(); ++i)
216 {
217 printf("%d\n", A[i]);
218 }
219
220 auto t4 = clock();
221
222 // reset console output
223 freopen("CON", "w", stdout);
224
225 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
226 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
227 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
228
229 return 0;
230 }
231 /*
232 t2-t1 = 0.765
233 t3-t2 = 2.094
234 t4-t3 = 0.063
235
236 Process returned 0 (0x0) execution time : 3.109 s
237 Press any key to continue.
238 */
CAPITOLUL 3. IOI 2018 302

Listing 3.3.7: werewolf_75296.cpp


1 // https://oj.uz/submission/75296
2
3 #include <stdio.h> // citire mai rapida !!!
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 using pi = pair<int, int>;
9 const int MAXN = 400005;
10 const int MAXT = 530000;
11
12 struct seg
13 {
14 int tree[MAXT], lim;
15 void init(int n)
16 {
17 fill(tree, tree + MAXT, -1e9);
18 for(lim = 1; lim <= n; lim <<= 1);
19 }
20
21 void add(int x, int v)
22 {
23 x += lim;
24 tree[x] = max(tree[x], v);
25 while(x > 1)
26 {
27 x >>= 1;
28 tree[x] = max(tree[2*x], tree[2*x+1]);
29 }
30 }
31
32 int query(int s, int e)
33 {
34 s += lim;
35 e += lim;
36 int ret = -1e9;
37 while(s < e)
38 {
39 if(s%2 == 1) ret = max(ret, tree[s++]);
40 if(e%2 == 0) ret = max(ret, tree[e--]);
41 s >>= 1;
42 e >>= 1;
43 }
44 if(s == e) ret = max(ret, tree[s]);
45 return ret;
46 }
47 } seg;
48
49 struct disj
50 {
51 int pa[MAXN];
52 void init(int n)
53 {
54 iota(pa, pa + n + 1, 0);
55 }
56
57 int find(int x)
58 {
59 return pa[x] = (pa[x] == x ? x : find(pa[x]));
60 }
61
62 bool uni(int p, int q)
63 {
64 p = find(p);
65 q = find(q);
66 if(p > q) swap(p, q);
67 if(p == q) return 0;
68 pa[p] = q; return 1;
69 }
70 } disj;
71
72 struct graph
73 {
74 vector<int> gph[MAXN];
CAPITOLUL 3. IOI 2018 303

75 int din[MAXN], dout[MAXN], piv;


76
77 void add_edge(int p, int q)
78 {
79 gph[p].push_back(q);
80 }
81
82 void dfs(int x)
83 {
84 din[x] = piv;
85 if(gph[x].empty()) piv++;
86 for(auto &i : gph[x]) dfs(i);
87 dout[x] = piv;
88 }
89 } g1, g2;
90
91 struct queries
92 {
93 int s, l, e, r;
94 int rs, re, idx;
95 } qr[MAXN];
96
97 struct edges
98 {
99 int s, e, x;
100 } ed[MAXN];
101
102 std::vector<int> check_validity(int N,
103 std::vector<int> X, std::vector<int> Y,
104 std::vector<int> S, std::vector<int> E,
105 std::vector<int> L, std::vector<int> R)
106 {
107 int M = X.size();
108 int Q = S.size();
109 for(int i=0; i<Q; i++)
110 qr[i] = {S[i], L[i], E[i], R[i], -1, -1, i};
111
112 // make first tree
113 int vtx_num = N, ptr = 0;
114 qr[Q].l = -1e9;
115 disj.init(N * 2);
116 for(int i=0; i<M; i++)
117 ed[i] = {X[i], Y[i], min(X[i], Y[i])};
118
119 sort(qr, qr + Q, [&](const queries &a, const queries &b){
120 return a.l > b.l;
121 });
122
123 sort(ed, ed + M, [&](const edges &a, const edges &b){
124 return a.x > b.x;
125 });
126
127 for(int i=0; i<=Q; i++)
128 {
129 while(ptr < M && ed[ptr].x >= qr[i].l)
130 {
131 int l = disj.find(ed[ptr].s);
132 int r = disj.find(ed[ptr].e);
133 ptr++;
134 if(l == r) continue;
135 disj.uni(l, vtx_num);
136 disj.uni(r, vtx_num);
137 g1.add_edge(vtx_num, l);
138 g1.add_edge(vtx_num, r);
139 vtx_num++;
140 }
141 qr[i].rs = disj.find(qr[i].s);
142 }
143
144 // make second tree
145 vtx_num = N, ptr = 0;
146 qr[Q].r = 1e9;
147 disj.init(N * 2);
148 for(int i=0; i<M; i++) ed[i] = {X[i], Y[i], max(X[i], Y[i])};
149
150 sort(qr, qr + Q, [&](const queries &a, const queries &b){
CAPITOLUL 3. IOI 2018 304

151 return a.r < b.r;


152 });
153
154 sort(ed, ed + M, [&](const edges &a, const edges &b){
155 return a.x < b.x;
156 });
157
158 for(int i=0; i<=Q; i++)
159 {
160 while(ptr < M && ed[ptr].x <= qr[i].r)
161 {
162 int l = disj.find(ed[ptr].s);
163 int r = disj.find(ed[ptr].e);
164 ptr++;
165 if(l == r) continue;
166 disj.uni(l, vtx_num);
167 disj.uni(r, vtx_num);
168 g2.add_edge(vtx_num, l);
169 g2.add_edge(vtx_num, r);
170 vtx_num++;
171 }
172 qr[i].re = disj.find(qr[i].e);
173 }
174
175 // both done
176 g1.dfs(2 * N - 2);
177 g2.dfs(2 * N - 2);
178
179 vector<pi> points;
180 vector<int> ans(Q);
181
182 for(int i=0; i<N; i++) points.emplace_back(g1.din[i], g2.din[i]);
183
184 sort(points.begin(), points.end());
185
186 sort(qr, qr + Q, [&](const queries &a, const queries &b){
187 return g1.dout[a.rs] < g1.dout[b.rs];
188 });
189
190 seg.init(N);
191 ptr = 0;
192 for(int i=0; i<Q; i++)
193 {
194 while(ptr < N && points[ptr].first < g1.dout[qr[i].rs])
195 {
196 seg.add(points[ptr].second, points[ptr].first);
197 ptr++;
198 }
199
200 if(seg.query(g2.din[qr[i].re], g2.dout[qr[i].re] - 1) >=
201 g1.din[qr[i].rs])
202 {
203 ans[qr[i].idx] = 1;
204 }
205 }
206
207 return ans;
208 }
209
210 //-------------- start grader ------------------
211
212 namespace
213 {
214 int read_int()
215 {
216 int x;
217 if (scanf("%d", &x) != 1)
218 {
219 fprintf(stderr, "Error while reading input\n");
220 exit(1);
221 }
222 return x;
223 }
224 } // namespace
225
226 int main()
CAPITOLUL 3. IOI 2018 305

227 {
228 auto t1 = clock();
229
230 std::freopen("../in/04-19.txt", "r", stdin);
231 std::freopen("werewolf.out", "w", stdout);
232
233 int N = read_int();
234 int M = read_int();
235 int Q = read_int();
236
237 std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);
238
239 for (int j = 0; j < M; ++j)
240 {
241 X[j] = read_int();
242 Y[j] = read_int();
243 }
244
245 for (int i = 0; i < Q; ++i)
246 {
247 S[i] = read_int();
248 E[i] = read_int();
249 L[i] = read_int();
250 R[i] = read_int();
251 }
252
253 fclose(stdin);
254 auto t2 = clock();
255
256 std::vector<int> A = check_validity(N, X, Y, S, E, L, R);
257
258 auto t3 = clock();
259
260 for (size_t i = 0; i < A.size(); ++i)
261 {
262 printf("%d\n", A[i]);
263 }
264
265 auto t4 = clock();
266
267 // reset console output
268 freopen("CON", "w", stdout);
269
270 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
271 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
272 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
273
274 return 0;
275 }
276 /*
277 t2-t1 = 0.797
278 t3-t2 = 2.411
279 t4-t3 = 0.063
280
281 Process returned 0 (0x0) execution time : 3.427 s
282 Press any key to continue.
283 */

Listing 3.3.8: werewolf_75793.cpp


1 // https://oj.uz/submission/75793
2
3 #include <stdio.h> // citire mai rapida !!!
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 const int mxN=2e5;
9 int n, q, p[mxN], st[2][mxN], en[2][mxN], dt, anc[2][mxN],
10 r[mxN], ta[mxN], ft[mxN+1];
11 vector<int> adj1[mxN], adj2[mxN];
12 vector<array<int, 2>> b[mxN+1];
13 bool d[mxN];
14
15 int find(int x)
CAPITOLUL 3. IOI 2018 306

16 {
17 return x==r[x]?x:(r[x]=find(r[x]));
18 }
19
20 void dfs(int u, int st[mxN], int en[mxN])
21 {
22 st[u]=dt++;
23 for(int v : adj2[u])
24 dfs(v, st, en);
25 en[u]=dt;
26 }
27
28 void a(int st[mxN], int en[mxN], int anc[mxN])
29 {
30 for(int i=0; i<n; ++i)
31 r[i]=i;
32 for(int i=0; i<n; ++i)
33 {
34 for(int j : adj1[p[i]])
35 {
36 if(!d[j]||(j=find(j))==p[i])
37 continue;
38 r[j]=p[i];
39 adj2[p[i]].push_back(j);
40 }
41 for(array<int, 2> c : b[p[i]])
42 anc[c[1]]=find(c[0]);
43 b[p[i]].clear();
44 d[p[i]]=1;
45 }
46 dfs(p[n-1], st, en);
47 }
48
49 int qry(int i)
50 {
51 int r=0;
52 for(; i; i-=i&-i)
53 r+=ft[i];
54 return r;
55 }
56
57 vector<int> check_validity(int n2, vector<int> x, vector<int> y,
58 vector<int> s, vector<int> e,
59 vector<int> l, vector<int> r)
60 {
61 n=n2;
62 for(int i=0; i<x.size(); ++i)
63 {
64 adj1[x[i]].push_back(y[i]);
65 adj1[y[i]].push_back(x[i]);
66 }
67
68 q=s.size();
69 for(int i=0; i<n; ++i)
70 p[i]=n-1-i;
71 for(int i=0; i<q; ++i)
72 b[l[i]].push_back({s[i], i});
73
74 a(st[0], en[0], anc[0]);
75 for(int i=0; i<n; ++i)
76 {
77 adj2[i].clear();
78 p[i]=i;
79 }
80
81 memset(d, 0, n);
82
83 dt=0;
84 for(int i=0; i<q; ++i)
85 b[r[i]].push_back({e[i], i});
86
87 a(st[1], en[1], anc[1]);
88 for(int i=0; i<n; ++i)
89 ta[st[0][i]]=st[1][i];
90
91 for(int i=0; i<q; ++i)
CAPITOLUL 3. IOI 2018 307

92 {
93 b[st[0][anc[0][i]]].push_back({i, -1});
94 b[en[0][anc[0][i]]].push_back({i, 1});
95 }
96
97 vector<int> ans=vector<int>(q);
98 for(int i=0; i<n; ++i)
99 {
100 for(int j=ta[i]+1; j<=n; j+=j&-j)
101 ++ft[j];
102 for(array<int, 2> c : b[i+1])
103 ans[c[0]]+=c[1]*
104 (qry(en[1][anc[1][c[0]]])-qry(st[1][anc[1][c[0]]]));
105 }
106
107 for(int i=0; i<q; ++i)
108 ans[i]=ans[i]>0;
109
110 return ans;
111 }
112 //-------------- start grader ------------------
113
114 namespace
115 {
116 int read_int()
117 {
118 int x;
119 if (scanf("%d", &x) != 1)
120 {
121 fprintf(stderr, "Error while reading input\n");
122 exit(1);
123 }
124 return x;
125 }
126 } // namespace
127
128 int main()
129 {
130 auto t1 = clock();
131
132 std::freopen("../in/04-19.txt", "r", stdin);
133 std::freopen("werewolf.out", "w", stdout);
134
135 int N = read_int();
136 int M = read_int();
137 int Q = read_int();
138
139 std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);
140
141 for (int j = 0; j < M; ++j)
142 {
143 X[j] = read_int();
144 Y[j] = read_int();
145 }
146
147 for (int i = 0; i < Q; ++i)
148 {
149 S[i] = read_int();
150 E[i] = read_int();
151 L[i] = read_int();
152 R[i] = read_int();
153 }
154
155 fclose(stdin);
156 auto t2 = clock();
157
158 std::vector<int> A = check_validity(N, X, Y, S, E, L, R);
159
160 auto t3 = clock();
161
162 for (size_t i = 0; i < A.size(); ++i)
163 {
164 printf("%d\n", A[i]);
165 }
166
167 auto t4 = clock();
CAPITOLUL 3. IOI 2018 308

168
169 // reset console output
170 freopen("CON", "w", stdout);
171
172 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
173 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
174 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
175
176 return 0;
177 }
178 /*
179 t2-t1 = 0.781
180 t3-t2 = 1.344
181 t4-t3 = 0.062
182
183 Process returned 0 (0x0) execution time : 2.359 s
184 Press any key to continue.
185 */

Listing 3.3.9: werewolf_81140.cpp


1 // https://oj.uz/submission/81140
2
3 #include <stdio.h> // citire mai rapida !!!
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 typedef long long ll;
9 typedef pair<int, int> pii;
10 typedef pair<ll, ll> pll;
11
12 #define Fi first
13 #define Se second
14 #define pb(x) push_back(x)
15 #define szz(x) (int)x.size()
16 #define rep(i,n) for(int i=0;i<n;i++)
17 #define all(x) x.begin(),x.end()
18
19 typedef tuple<int, int, int> t3;
20
21 struct tree
22 {
23 vector <int> F[200020];
24 int lv[200020], rv[200020], cs = 0;
25 int up[200020][18];
26
27 void addE(int x, int y)
28 { // x <- y
29 F[x].pb(y);
30 }
31
32 void dfs(int x)
33 {
34 lv[x] = ++cs;
35 for(int e : F[x])
36 {
37 up[e][0] = x;
38 for(int i=1;i<18;i++)
39 {
40 up[e][i] = up[ up[e][i-1] ][i-1];
41 }
42 dfs(e);
43 }
44 rv[x] = cs;
45 }
46
47 pii get_seg_g(int x, int l)
48 {
49 for(int i=17;i>=0;i--)
50 {
51 if(up[x][i] && up[x][i] >= l) x = up[x][i];
52 }
53 return pii(lv[x], rv[x]);
54 }
CAPITOLUL 3. IOI 2018 309

55
56 pii get_seg_l(int x, int r)
57 {
58 for(int i=17;i>=0;i--)
59 {
60 if(up[x][i] && up[x][i] <= r) x = up[x][i];
61 }
62 return pii(lv[x], rv[x]);
63 }
64 } T[2];
65
66 int pp[200020];
67 int Find(int x)
68 {
69 return pp[x] == x ? x : pp[x] = Find(pp[x]);
70 }
71
72 int val[200020];
73 vector <int> EE[200010];
74 int N;
75
76 vector <t3> pquery[200020];
77 int TT[200020];
78 int cnt[200020];
79
80 void pre_query(int idx, int S, int E, int L, int R)
81 {
82 pii p1 = T[0].get_seg_l(E, R);
83 pii p2 = T[1].get_seg_g(S, L);
84 pquery[p1.Se].pb(t3(p2.Se, idx, 1));
85 pquery[p1.Se].pb(t3(p2.Fi-1, idx, -1));
86 pquery[p1.Fi-1].pb(t3(p2.Se, idx, -1));
87 pquery[p1.Fi-1].pb(t3(p2.Fi-1, idx, 1));
88 }
89
90 std::vector<int> check_validity(int nn, std::vector<int> X,
91 std::vector<int> Y,
92 std::vector<int> S, std::vector<int> E,
93 std::vector<int> L, std::vector<int> R)
94 {
95 N = nn;
96 int M = szz(X);
97 for(int i=0;i<M;i++)
98 {
99 int x = X[i] + 1, y = Y[i] + 1;
100 EE[x].pb(y);
101 EE[y].pb(x);
102 }
103
104 for(int i=1;i<=N;i++) pp[i] = i;
105 for(int i=1;i<=N;i++) {
106 for(int e : EE[i]) if(e < i)
107 {
108 int x = Find(i), y = Find(e);
109 if(x == y) continue;
110 pp[y] = x;
111 T[0].addE(x, y);
112 }
113 }
114
115 for(int i=1;i<=N;i++) pp[i] = i;
116 for(int i=N;i;i--)
117 {
118 for(int e : EE[i]) if(e > i)
119 {
120 int x = Find(i), y = Find(e);
121 if(x == y) continue;
122 pp[y] = x;
123 T[1].addE(x, y);
124 }
125 }
126
127 T[0].dfs(N); T[1].dfs(1);
128 for(int i=1;i<=N;i++)
129 {
130 val[T[0].lv[i]] = T[1].lv[i];
CAPITOLUL 3. IOI 2018 310

131 }
132
133 int Q = szz(S);
134 rep(i, Q)
135 {
136 ++S[i]; ++E[i]; ++L[i]; ++R[i];
137 pre_query(i, S[i], E[i], L[i], R[i]);
138 }
139
140 for(int i=1;i<=N;i++)
141 {
142 int x = val[i];
143 for(int j=x;j<200020;j+=(j&-j)) TT[j]++;
144 for(t3 e : pquery[i])
145 {
146 int y, idx, cc;
147 tie(y, idx, cc) = e;
148 int s = 0;
149 for(int j=y;j;j-=(j&-j)) s += TT[j];
150 cnt[idx] += cc * s;
151 }
152 }
153
154 vector <int> A(Q);
155 rep(i, Q)
156 {
157 A[i] = cnt[i] > 0 ? 1 : 0;
158 }
159
160 return A;
161 }
162
163 //-------------- start grader ------------------
164
165 namespace
166 {
167 int read_int()
168 {
169 int x;
170 if (scanf("%d", &x) != 1)
171 {
172 fprintf(stderr, "Error while reading input\n");
173 exit(1);
174 }
175 return x;
176 }
177 } // namespace
178
179 int main()
180 {
181 auto t1 = clock();
182
183 std::freopen("../in/04-19.txt", "r", stdin);
184 std::freopen("werewolf.out", "w", stdout);
185
186 int N = read_int();
187 int M = read_int();
188 int Q = read_int();
189
190 std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);
191
192 for (int j = 0; j < M; ++j)
193 {
194 X[j] = read_int();
195 Y[j] = read_int();
196 }
197
198 for (int i = 0; i < Q; ++i)
199 {
200 S[i] = read_int();
201 E[i] = read_int();
202 L[i] = read_int();
203 R[i] = read_int();
204 }
205
206 fclose(stdin);
CAPITOLUL 3. IOI 2018 311

207 auto t2 = clock();


208
209 std::vector<int> A = check_validity(N, X, Y, S, E, L, R);
210
211 auto t3 = clock();
212
213 for (size_t i = 0; i < A.size(); ++i)
214 {
215 printf("%d\n", A[i]);
216 }
217
218 auto t4 = clock();
219
220 // reset console output
221 freopen("CON", "w", stdout);
222
223 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
224 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
225 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
226
227 return 0;
228 }
229 /*
230 t2-t1 = 0.75
231 t3-t2 = 2.159
232 t4-t3 = 0.063
233
234 Process returned 0 (0x0) execution time : 3.175 s
235 Press any key to continue.
236 */

Listing 3.3.10: werewolf_85439.cpp


1 // https://oj.uz/submission/85439
2
3 #include <stdio.h> // citire mai rapida !!!
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 #define y0 ___y0
9 #define y1 ___y1
10 #define MP make_pair
11 #define MT make_tuple
12 #define PB push_back
13 #define PF push_front
14 #define fi first
15 #define se second
16 #define debug(x) cerr << #x << " = " << x << endl;
17 #define SZ(x) ((int) (x.size()))
18
19 const long double PI = 4.0 * atan(1.0);
20 const long double EPS = 1e-10;
21
22 #define MAXN 400013
23
24 typedef long long ll;
25 typedef long double ld;
26 typedef vector<int> vi;
27 typedef vector<ll> vl;
28 typedef pair<int, int> pii;
29 typedef pair<ll, ll> pll;
30 typedef pair<ld, ld> pdd;
31 typedef pair<pii, pii> ppp;
32
33 int N, Q, T;
34 vector<int> edge[MAXN];
35 ppp quer[MAXN];
36 ppp range[MAXN];
37 vi ans;
38 int parent[2][MAXN];
39 int ord[2][MAXN];
40 int ancestor[2][20][MAXN];
41 int st[2][MAXN], ft[2][MAXN];
42 int pos[MAXN], arr[MAXN];
CAPITOLUL 3. IOI 2018 312

43 vector<int> edge1[2][MAXN];
44
45 struct dsu
46 {
47 int dsu[MAXN];
48 bool flag;
49
50 int get(int u)
51 {
52 return ((u == dsu[u]) ? u : dsu[u] = get(dsu[u]));
53 }
54
55 void merge(int u, int v)
56 {
57 u = get(u);
58 v = get(v);
59 if (flag)
60 {
61 if (u > v) swap(u, v);
62 }
63 else
64 {
65 if (u < v) swap(u, v);
66 }
67 dsu[u] = v;
68 // cerr << u << " => " << v << endl;
69 return;
70 }
71 };
72
73 void dfs(bool flag, int u)
74 {
75 st[flag][u] = T;
76 ft[flag][u] = T;
77 ord[flag][T] = u;
78 T++;
79 for (int v : edge1[flag][u])
80 {
81 if (v == parent[flag][u]) continue;
82 dfs(flag, v);
83 ft[flag][u] = ft[flag][v];
84 }
85 }
86
87 int fen[MAXN];
88
89 void update(int idx, int v)
90 {
91 for (int e = idx + 1; e <= N; e += e & (-e))
92 {
93 fen[e] += v;
94 }
95 return;
96 }
97
98 int query(int idx)
99 {
100 int res = 0;
101 for (int e = idx + 1; e > 0; e -= e & (-e))
102 {
103 res += fen[e];
104 }
105 return res;
106 }
107
108 vector<ppp> queries[MAXN];
109 dsu d[2];
110
111 vi check_validity(int n, vi X, vi Y, vi s, vi e, vi l, vi r)
112 {
113 N = n;
114 d[0].flag = true;
115 for (int i = 0; i < N; i++)
116 {
117 d[0].dsu[i] = i;
118 d[1].dsu[i] = i;
CAPITOLUL 3. IOI 2018 313

119 parent[0][i] = N;
120 parent[1][i] = N;
121 }
122
123 for (int i = 0; i < SZ(X); i++)
124 {
125 int u = X[i], v = Y[i];
126 edge[u].PB(v);
127 edge[v].PB(u);
128 }
129
130 Q = SZ(s);
131 ans.resize(Q);
132 for (int i = 0; i < Q; i++)
133 {
134 swap(s[i], e[i]);
135 quer[i] = MP(MP(l[i], r[i]), MP(s[i], e[i]));
136 }
137
138 for (int i = 0; i < N; i++)
139 {
140 for (int u : edge[i])
141 {
142 if (u > i) continue;
143 u = d[0].get(u);
144 if (u == i) continue;
145 parent[0][u] = i;
146 d[0].merge(u, i);
147 }
148 }
149
150 for (int i = N - 1; i >= 0; i--)
151 {
152 for (int u : edge[i])
153 {
154 if (u < i) continue;
155 u = d[1].get(u);
156 if (u == i) continue;
157 parent[1][u] = i;
158 d[1].merge(u, i);
159 }
160 }
161
162 for (int flag = 0; flag < 2; flag++)
163 {
164 // cerr << "tree\n";
165 for (int i = 0; i < N; i++)
166 {
167 if (parent[flag][i] != N)
168 {
169 edge1[flag][i].PB(parent[flag][i]);
170 edge1[flag][parent[flag][i]].PB(i);
171 // cerr << i << ’ ’ << parent[flag][i] << endl;
172 }
173 }
174 for (int i = 0; i < N; i++)
175 {
176 if (parent[flag][i] == N)
177 {
178 dfs(flag, i);
179 }
180 }
181 T = 0;
182 for (int i = 0; i <= 19; i++)
183 {
184 ancestor[flag][i][N] = N;
185 }
186 for (int i = 0; i < N; i++)
187 {
188 ancestor[flag][0][i] = parent[flag][i];
189 }
190 for (int i = 1; i <= 19; i++)
191 {
192 for (int j = 0; j < N; j++)
193 {
194 ancestor[flag][i][j] =
CAPITOLUL 3. IOI 2018 314

195 ancestor[flag][i - 1][ancestor[flag][i - 1][j]];


196 }
197 }
198 }
199
200 for (int i = 0; i < Q; i++)
201 {
202 //l, r, s, e
203 if (quer[i].se.fi > quer[i].fi.se ||
204 quer[i].se.se < quer[i].fi.fi)
205 {
206 ans[i] = 0;
207 continue;
208 }
209
210 //how far up can you go without exceeding r...
211 int u = quer[i].se.fi;
212 for (int j = 19; j >= 0; j--)
213 {
214 if (ancestor[0][j][u] > quer[i].fi.se ||
215 ancestor[0][j][u] == N) continue;
216 u = ancestor[0][j][u];
217 }
218
219 // cerr << "starts at " << i << " goes to " << u << endl;
220 range[i].fi = MP(st[0][u], ft[0][u]);
221 u = quer[i].se.se;
222 for (int j = 19; j >= 0; j--)
223 {
224 if (ancestor[1][j][u] < quer[i].fi.fi ||
225 ancestor[1][j][u] == N) continue;
226 u = ancestor[1][j][u];
227 }
228 range[i].se = MP(st[1][u], ft[1][u]);
229 }
230
231 //GIVEN TWO SUBARRAYS OF BIG ARRAY,
232 // CHECK IF THEY HAVE ANY COMMON ELEMENTS!
233 for (int i = 0; i < N; i++)
234 {
235 pos[ord[1][i]] = i;
236 }
237
238 for (int i = 0; i < N; i++)
239 {
240 arr[i] = pos[ord[0][i]];
241 // debug(arr[i]);
242 }
243
244 for (int i = 0; i < Q; i++)
245 {
246 if (range[i].fi.fi != 0)
247 {
248 queries[range[i].fi.fi - 1].PB(MP(MP(i, -1), range[i].se));
249 }
250 queries[range[i].fi.se].PB(MP(MP(i, 1), range[i].se));
251 }
252
253 for (int i = 0; i < N; i++)
254 {
255 update(arr[i], 1);
256 for (ppp p : queries[i])
257 {
258 ans[p.fi.fi] += (p.fi.se) *
259 (query(p.se.se) - query(p.se.fi - 1));
260 }
261 }
262
263 for (int i = 0; i < Q; i++)
264 {
265 ans[i] = (bool) ans[i];
266 }
267 return ans;
268
269 //idx, plusminus, l, r
270 //# of values between
CAPITOLUL 3. IOI 2018 315

271 //is there any value inside L1...R1 in arr[L0...R0]?


272 //now it’s just: in this subrange of an array,
273 // is there a value between L and R?
274 //(0...R) -> (L...N-1) with (S[i], E[i])
275 //u know that S[i] can go anywhere in its tree as long
276 // as path doesn’t contain #s >= R+1
277 //and then you can unfold the tree!
278 //oh each of them is like a tree,
279 // and each time you increase R you merge some trees together
280 return ans;
281 }
282
283 //-------------- start grader ------------------
284
285 namespace
286 {
287 int read_int()
288 {
289 int x;
290 if (scanf("%d", &x) != 1)
291 {
292 fprintf(stderr, "Error while reading input\n");
293 exit(1);
294 }
295 return x;
296 }
297 } // namespace
298
299 int main()
300 {
301 auto t1 = clock();
302
303 std::freopen("../in/04-19.txt", "r", stdin);
304 std::freopen("werewolf.out", "w", stdout);
305
306 int N = read_int();
307 int M = read_int();
308 int Q = read_int();
309
310 std::vector<int> X(M), Y(M), S(Q), E(Q), L(Q), R(Q);
311
312 for (int j = 0; j < M; ++j)
313 {
314 X[j] = read_int();
315 Y[j] = read_int();
316 }
317
318 for (int i = 0; i < Q; ++i)
319 {
320 S[i] = read_int();
321 E[i] = read_int();
322 L[i] = read_int();
323 R[i] = read_int();
324 }
325
326 fclose(stdin);
327 auto t2 = clock();
328
329 std::vector<int> A = check_validity(N, X, Y, S, E, L, R);
330
331 auto t3 = clock();
332
333 for (size_t i = 0; i < A.size(); ++i)
334 {
335 printf("%d\n", A[i]);
336 }
337
338 auto t4 = clock();
339
340 // reset console output
341 freopen("CON", "w", stdout);
342
343 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
344 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
345 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
346
CAPITOLUL 3. IOI 2018 316

347 return 0;
348 }
349 /*
350 t2-t1 = 0.843
351 t3-t2 = 2.047
352 t4-t3 = 0.063
353
354 Process returned 0 (0x0) execution time : 3.266 s
355 Press any key to continue.
356 */

3.3.3 *Rezolvare detaliat 

3.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
arbitrar (posibil zero) de intr ri. Fiecare tub conecteaz  ie³irea unui dispozitiv cu intrarea acelu-
ia³i sau altui dispozitiv. Exact un tub este conectat cu ecare intrare ³i exact un tub este conectat
cu ecare 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 ecare 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  zero). Voi trebuie s  decideµi valoarea lui S . Fiecare
dispozitiv are un cod unic de înregistrare.
Originea este dispozitivul unde bila se a  iniµial. Are exact o ie³ire. Codul ei unic de înregis-
trare este 0.

Figura 3.2: doll

Un întrerup tor face bila s  execute o mi³care special  de ecare 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 3.3: doll

Fiecare comutator are dou  ie³iri, denumite X ³i Y . Starea unui comutator este e X ,
¬ ¬ ¬ ¬ ¬ ¬

¬ ¬
e Y . Dup  ce bila intr  într-un comutator îl p r se³te prin ie³irea dat  de starea curent  a
CAPITOLUL 3. IOI 2018 317

¬ ¬
acestuia. Dup  aceea, starea comutatorului se schimb . Iniµial starea ec rui comutator este X .
Codurile unice de înregistrare ale comutatoarelor sunt de la 1 la S .

Figura 3.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 ec 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  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  e un num r întreg între S ³i M inclusiv.
a S trebuie s  e 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  evaluat ca
Wrong answer. Altfel, programul vostru va  evaluat ca Accepted, iar punctajul vostru va 
calculat pe baza lui S (vedeµi Subtask-uri).

Exemple

Fie M 4, N 4 ³i A 1, 2, 1, 3.


CAPITOLUL 3. IOI 2018 318

Grader-ul apeleaz  create_circuit(4, [1, 2, 1,3]).

Figura 3.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 gur  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 3.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 ecare test sunt dup  cum urmeaz :


1. (2 puncte) Pentru ecare i (1 & i & M ) num rul i apare cel mult o dat  în secvenµa
A0 , A1 , ..., AN 1 .
2. (4 puncte) Pentru ecare (1 & i & M ) num rul apare de cel mult de dou  ori în secvenµa
A0 , A1 , ..., AN 1 .
3. (10 puncte) Pentru ecare 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 3. IOI 2018 319

Pentru ecare 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 ecare test din subtask-urile 5 ³i 6, dac  N  log2 N $ S & 2N veµi primi un punctaj
2
parµial. Punctajul pentru acest test este 0.5  0.4   N2N S
log2 N
multiplicat cu punctajul asociat
subtask-ului.
a Altfel punctajul este 0.
Luaµi la cuno³tinµ  c  punctajul pentru ecare subtask este minimul punctajelor pentru ecare
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 a³a r spunsul vostru într-un ³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 a³a codurile unice de înregistrare ale
dispozitivelor prin care trece bila în ³ierul log.txt.
În sfâr³it, grader-ul local va a³a evaluarea r spunsului vostru la ie³irea standard.
a Dac  programul vostru este evaluat ca Accepted grader-ul local va a³a S ³i P în urm toarea
form  Accepted: S P .
a Dac  programul vostru este evaluat ca Wrong Answer, grader-ul local va a³a
Wrong answer: M SG. Semnicaµ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.

3.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 indenitely. 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 satises the following conditions:
CAPITOLUL 3. IOI 2018 320

ˆ The ball returns to the origin after a nite number of steps.


ˆ By the time the ball rst returns to the origin, every switch has been visited even times.
ˆ The ball rst 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
rst 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.

3.4.2 Coduri surs 

Listing 3.4.1: compile_cpp.sh


1 #!/bin/bash
2
3 TASK=doll
4
5 g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp
CAPITOLUL 3. IOI 2018 321

Listing 3.4.2: dool.h


1 #include <vector>
2
3 void create_circuit(int M, std::vector<int> A);
4
5 void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y);

Listing 3.4.3: dool.cpp


1 #include "doll.h"
2
3 void create_circuit(int M, std::vector<int> A)
4 {
5 int N = A.size();
6 std::vector<int> C(M + 1);
7 C[0] = -1;
8 for (int i = 1; i <= M; ++i)
9 {
10 C[i] = 1;
11 }
12 std::vector<int> X(N), Y(N);
13 for (int k = 0; k < N; ++k)
14 {
15 X[k] = Y[k] = A[k];
16 }
17 answer(C, X, Y);
18 }

Listing 3.4.4: grader.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include "doll.h"
4
5 namespace
6 {
7
8 constexpr int P_MAX = 20000000;
9 constexpr int S_MAX = 400000;
10
11 int M, N;
12 std::vector<int> A;
13
14 bool answered;
15 int S;
16 std::vector<int> IC, IX, IY;
17
18 int read_int()
19 {
20 int x;
21 if (scanf("%d", &x) != 1)
22 {
23 fprintf(stderr, "Error while reading input\n");
24 exit(1);
25 }
26 return x;
27 }
28
29 void wrong_answer(const char *MSG)
30 {
31 printf("Wrong Answer: %s\n", MSG);
32 exit(0);
33 }
34
35 void simulate()
36 {
37 if (S > S_MAX)
38 {
39 char str[50];
40 sprintf(str, "over %d switches", S_MAX);
41 wrong_answer(str);
42 }
43 for (int i = 0; i <= M; ++i)
CAPITOLUL 3. IOI 2018 322

44 {
45 if (!(-S <= IC[i] && IC[i] <= M))
46 {
47 wrong_answer("wrong serial number");
48 }
49 }
50 for (int j = 1; j <= S; ++j)
51 {
52 if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
53 {
54 wrong_answer("wrong serial number");
55 }
56 if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
57 {
58 wrong_answer("wrong serial number");
59 }
60 }
61
62 int P = 0;
63 std::vector<bool> state(S + 1, false);
64 int pos = IC[0];
65 int k = 0;
66 FILE *file_log = fopen("log.txt", "w");
67 fprintf(file_log, "0\n");
68 for (;;)
69 {
70 fprintf(file_log, "%d\n", pos);
71 if (pos < 0)
72 {
73 if (++P > P_MAX)
74 {
75 fclose(file_log);
76 char str[50];
77 sprintf(str, "over %d inversions", P_MAX);
78 wrong_answer(str);
79 }
80 state[-pos] = !state[-pos];
81 pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
82 }
83 else
84 {
85 if (pos == 0)
86 {
87 break;
88 }
89 if (k >= N)
90 {
91 fclose(file_log);
92 wrong_answer("wrong motion");
93 }
94 if (pos != A[k++])
95 {
96 fclose(file_log);
97 wrong_answer("wrong motion");
98 }
99 pos = IC[pos];
100 }
101 }
102 fclose(file_log);
103 if (k != N)
104 {
105 wrong_answer("wrong motion");
106 }
107 for (int j = 1; j <= S; ++j)
108 {
109 if (state[j])
110 {
111 wrong_answer("state ’Y’");
112 }
113 }
114 printf("Accepted: %d %d\n", S, P);
115 }
116
117 } // namespace
118
119 void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)
CAPITOLUL 3. IOI 2018 323

120 {
121 if (answered)
122 {
123 wrong_answer("answered not exactly once");
124 }
125 answered = true;
126 // check if input format is correct
127 if ((int)C.size() != M + 1)
128 {
129 wrong_answer("wrong array length");
130 }
131 if (X.size() != Y.size())
132 {
133 wrong_answer("wrong array length");
134 }
135 S = X.size();
136 IC = C;
137 IX = X;
138 IY = Y;
139 }
140
141 int main()
142 {
143 M = read_int();
144 N = read_int();
145 A.resize(N);
146 for (int k = 0; k < N; ++k)
147 {
148 A[k] = read_int();
149 }
150
151 answered = false;
152 create_circuit(M, A);
153 if (!answered)
154 {
155 wrong_answer("answered not exactly once");
156 }
157 FILE *file_out = fopen("out.txt", "w");
158 fprintf(file_out, "%d\n", S);
159 for (int i = 0; i <= M; ++i)
160 {
161 fprintf(file_out, "%d\n", IC[i]);
162 }
163 for (int j = 1; j <= S; ++j)
164 {
165 fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
166 }
167 fclose(file_out);
168 simulate();
169 return 0;
170 }

Listing 3.4.5: doll_75123.cpp


1 // https://oj.uz/submission/75123
2
3 #include<fstream>
4 #include<iostream>
5
6 //#include <ctime>
7 #include <time.h> /* clock */
8
9 //#include <cstdio>
10 #include <stdio.h> // citire mai rapida !!!
11
12 #include <cstdio>
13 #include <cstdlib>
14
15 #include "doll.h"
16
17 using namespace std;
18
19 int n;
20 int seg[1 << 19];
21
CAPITOLUL 3. IOI 2018 324

22 int bitRev(int x, int n)


23 {
24 int r = 0;
25 while (n >>= 1)
26 {
27 r <<= 1;
28 r ^= (x & 1);
29 x >>= 1;
30 }
31 return r;
32 }
33
34 const int inf = 1e9;
35
36 void create_circuit(int m, vector<int> A)
37 {
38 int sz = 1;
39 A.push_back(0);
40 n = A.size();
41 while (sz < n) sz <<= 1;
42 for (int i = 0; i < sz; ++i) seg[i + sz] = inf;
43 for (int i = 0, j = 0; i < sz; ++i)
44 {
45 int k = bitRev(i, sz);
46 if (k < sz - n) continue;
47 seg[k + sz] = A[j++];
48 }
49
50 int SN = 0;
51 vector<int> X, Y;
52 for (int i = sz; --i; )
53 {
54 if (seg[i << 1] == seg[i << 1 | 1])
55 {
56 seg[i] = seg[i << 1];
57 continue;
58 }
59 int S = SN++;
60 seg[i] = -(S + 1);
61 X.push_back(seg[i << 1]);
62 Y.push_back(seg[i << 1 | 1]);
63 }
64
65 for (int i = 0; i < SN; ++i)
66 {
67 if (X[i] == inf) X[i] = seg[1];
68 if (Y[i] == inf) Y[i] = seg[1];
69 }
70
71 answer(vector<int>(m + 1, seg[1]), X, Y);
72 }
73 //-------------- start grader ------------------
74
75 namespace
76 {
77
78 constexpr int P_MAX = 20000000;
79 constexpr int S_MAX = 400000;
80
81 int M, N;
82 std::vector<int> A;
83
84 bool answered;
85 int S;
86 std::vector<int> IC, IX, IY;
87
88 int read_int()
89 {
90 int x;
91 if (scanf("%d", &x) != 1)
92 {
93 fprintf(stderr, "Error while reading input\n");
94 exit(1);
95 }
96 return x;
97 }
CAPITOLUL 3. IOI 2018 325

98
99 void wrong_answer(const char *MSG)
100 {
101 printf("Wrong Answer: %s\n", MSG);
102 exit(0);
103 }
104
105 void simulate()
106 {
107 if (S > S_MAX)
108 {
109 char str[50];
110 sprintf(str, "over %d switches", S_MAX);
111 wrong_answer(str);
112 }
113
114 for (int i = 0; i <= M; ++i)
115 {
116 if (!(-S <= IC[i] && IC[i] <= M))
117 {
118 wrong_answer("wrong serial number");
119 }
120 }
121
122 for (int j = 1; j <= S; ++j)
123 {
124 if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
125 {
126 wrong_answer("wrong serial number");
127 }
128 if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
129 {
130 wrong_answer("wrong serial number");
131 }
132 }
133
134 int P = 0;
135 std::vector<bool> state(S + 1, false);
136 int pos = IC[0];
137 int k = 0;
138
139 FILE *file_log = fopen("log.txt", "w");
140 fprintf(file_log, "0\n");
141
142 for (;;)
143 {
144 fprintf(file_log, "%d\n", pos);
145 if (pos < 0)
146 {
147 if (++P > P_MAX)
148 {
149 fclose(file_log);
150 char str[50];
151 sprintf(str, "over %d inversions", P_MAX);
152 wrong_answer(str);
153 }
154
155 state[-pos] = !state[-pos];
156 pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
157 }
158 else
159 {
160 if (pos == 0)
161 {
162 break;
163 }
164
165 if (k >= N)
166 {
167 fclose(file_log);
168 wrong_answer("wrong motion");
169 }
170
171 if (pos != A[k++])
172 {
173 fclose(file_log);
CAPITOLUL 3. IOI 2018 326

174 wrong_answer("wrong motion");


175 }
176
177 pos = IC[pos];
178 }
179 }
180
181 fclose(file_log);
182
183 if (k != N)
184 {
185 wrong_answer("wrong motion");
186 }
187
188 for (int j = 1; j <= S; ++j)
189 {
190 if (state[j])
191 {
192 wrong_answer("state ’Y’");
193 }
194 }
195
196 printf("Accepted: %d %d\n", S, P);
197 }
198 } // namespace
199
200 void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)
201 {
202 if (answered)
203 {
204 wrong_answer("answered not exactly once");
205 }
206
207 answered = true;
208 // check if input format is correct
209 if ((int)C.size() != M + 1)
210 {
211 wrong_answer("wrong array length");
212 }
213
214 if (X.size() != Y.size())
215 {
216 wrong_answer("wrong array length");
217 }
218
219 S = X.size();
220 IC = C;
221 IX = X;
222 IY = Y;
223 }
224
225 int main()
226 {
227 std::freopen("../in/03-04.txt", "r", stdin);
228 //std::freopen("doll.out", "w", stdout);
229
230 auto t1 = clock();
231
232 M = read_int();
233 N = read_int();
234 A.resize(N);
235 for (int k = 0; k < N; ++k)
236 {
237 A[k] = read_int();
238 }
239 fclose(stdin);
240
241 auto t2 = clock();
242
243 answered = false;
244 create_circuit(M, A);
245 if (!answered)
246 {
247 wrong_answer("answered not exactly once");
248 }
249
CAPITOLUL 3. IOI 2018 327

250 auto t3 = clock();


251
252 FILE *file_out = fopen("out.txt", "w");
253 fprintf(file_out, "%d\n", S);
254
255 for (int i = 0; i <= M; ++i)
256 {
257 fprintf(file_out, "%d\n", IC[i]);
258 }
259
260 for (int j = 1; j <= S; ++j)
261 {
262 fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
263 }
264
265 fclose(file_out);
266
267 auto t4 = clock();
268
269 simulate();
270
271 auto t5 = clock();
272
273 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
274 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
275 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
276 std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;
277
278 return 0;
279 }
280 //-------------- end grader ------------------
281 /*
282 Accepted: 200006 3836160
283 t2-t1 = 0.328
284 t3-t2 = 0.078
285 t4-t3 = 0.359
286 t5-t4 = 3.896
287
288 Process returned 0 (0x0) execution time : 4.709 s
289 Press any key to continue.
290 */

Listing 3.4.6: dool_75619.cpp


1 // https://oj.uz/submission/75619
2
3 #include <bits/stdc++.h>
4
5 #include "doll.h"
6
7 using namespace std;
8
9 const int MAXN = 400005;
10
11 int vtx_number, total_leaf;
12 int X[MAXN], Y[MAXN], mp[MAXN];
13
14 void dfs(int s, int e, int v, int d)
15 {
16 vtx_number++;
17 if(e - s == 1)
18 {
19 if(s < total_leaf) X[vtx_number - 1] = -1;
20 else X[vtx_number - 1] = v;
21 Y[vtx_number - 1] = v ^ (1 << d);
22 return;
23 }
24
25 int cur_vtx = vtx_number - 1;
26 int m = (s + e) / 2;
27 if(m < total_leaf)
28 {
29 X[cur_vtx] = -1;
30 }
31 else
CAPITOLUL 3. IOI 2018 328

32 {
33 X[cur_vtx] = -vtx_number - 1;
34 dfs(s, m, v, d + 1);
35 }
36
37 Y[cur_vtx] = -vtx_number - 1;
38 dfs(m+1, e, v ^ (1 << d), d + 1);
39 }
40
41 void create_circuit(int M, std::vector<int> A)
42 {
43 if(A.size() == 1)
44 {
45 vector<int> C(M + 1, 0), X, Y;
46 C[0] = A[0];
47 answer(C, X, Y);
48 return;
49 }
50
51 vector<int> C(M + 1, -1);
52 C[0] = A[0];
53 int K = 0;
54 while((1 << K) < A.size()) K++;
55 total_leaf = (1 << K) - A.size();
56 A.erase(A.begin());
57 A.push_back(0);
58
59 dfs(0, (1 << K) - 1, 0, 0);
60
61 for(int i=0; i<vtx_number; i++)
62 {
63 if(X[i] >= 0) mp[X[i]] = 1;
64 if(Y[i] >= 0) mp[Y[i]] = 1;
65 }
66
67 int ptr = 0;
68 for(int i=0; i<(1<<K); i++)
69 {
70 if(mp[i])
71 {
72 mp[i] = A[ptr++];
73 }
74 }
75
76 for(int i=0; i<vtx_number; i++)
77 {
78 if(X[i] >= 0) X[i] = mp[X[i]];
79 if(Y[i] >= 0) Y[i] = mp[Y[i]];
80 }
81 vector<int> vx(X, X + vtx_number);
82 vector<int> vy(Y, Y + vtx_number);
83 answer(C, vx, vy);
84 }
85
86 //-------------- start grader ------------------
87
88 namespace
89 {
90 constexpr int P_MAX = 20000000;
91 constexpr int S_MAX = 400000;
92
93 int M, N;
94 std::vector<int> A;
95
96 bool answered;
97 int S;
98 std::vector<int> IC, IX, IY;
99
100 int read_int()
101 {
102 int x;
103 if (scanf("%d", &x) != 1)
104 {
105 fprintf(stderr, "Error while reading input\n");
106 exit(1);
107 }
CAPITOLUL 3. IOI 2018 329

108 return x;
109 }
110
111 void wrong_answer(const char *MSG)
112 {
113 printf("Wrong Answer: %s\n", MSG);
114 exit(0);
115 }
116
117 void simulate()
118 {
119 if (S > S_MAX)
120 {
121 char str[50];
122 sprintf(str, "over %d switches", S_MAX);
123 wrong_answer(str);
124 }
125
126 for (int i = 0; i <= M; ++i)
127 {
128 if (!(-S <= IC[i] && IC[i] <= M))
129 {
130 wrong_answer("wrong serial number");
131 }
132 }
133
134 for (int j = 1; j <= S; ++j)
135 {
136 if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
137 {
138 wrong_answer("wrong serial number");
139 }
140 if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
141 {
142 wrong_answer("wrong serial number");
143 }
144 }
145
146 int P = 0;
147 std::vector<bool> state(S + 1, false);
148 int pos = IC[0];
149 int k = 0;
150
151 FILE *file_log = fopen("log.txt", "w");
152 fprintf(file_log, "0\n");
153
154 for (;;)
155 {
156 fprintf(file_log, "%d\n", pos);
157 if (pos < 0)
158 {
159 if (++P > P_MAX)
160 {
161 fclose(file_log);
162 char str[50];
163 sprintf(str, "over %d inversions", P_MAX);
164 wrong_answer(str);
165 }
166
167 state[-pos] = !state[-pos];
168 pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
169 }
170 else
171 {
172 if (pos == 0)
173 {
174 break;
175 }
176
177 if (k >= N)
178 {
179 fclose(file_log);
180 wrong_answer("wrong motion");
181 }
182
183 if (pos != A[k++])
CAPITOLUL 3. IOI 2018 330

184 {
185 fclose(file_log);
186 wrong_answer("wrong motion");
187 }
188
189 pos = IC[pos];
190 }
191 }
192
193 fclose(file_log);
194
195 if (k != N)
196 {
197 wrong_answer("wrong motion");
198 }
199
200 for (int j = 1; j <= S; ++j)
201 {
202 if (state[j])
203 {
204 wrong_answer("state ’Y’");
205 }
206 }
207
208 printf("Accepted: %d %d\n", S, P);
209 }
210 } // namespace
211
212 void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)
213 {
214 if (answered)
215 {
216 wrong_answer("answered not exactly once");
217 }
218
219 answered = true;
220 // check if input format is correct
221 if ((int)C.size() != M + 1)
222 {
223 wrong_answer("wrong array length");
224 }
225
226 if (X.size() != Y.size())
227 {
228 wrong_answer("wrong array length");
229 }
230
231 S = X.size();
232 IC = C;
233 IX = X;
234 IY = Y;
235 }
236
237 int main()
238 {
239 std::freopen("../in/03-04.txt", "r", stdin);
240 //std::freopen("doll.out", "w", stdout);
241
242 auto t1 = clock();
243
244 M = read_int();
245 N = read_int();
246 A.resize(N);
247 for (int k = 0; k < N; ++k)
248 {
249 A[k] = read_int();
250 }
251 fclose(stdin);
252
253 auto t2 = clock();
254
255 answered = false;
256 create_circuit(M, A);
257 if (!answered)
258 {
259 wrong_answer("answered not exactly once");
CAPITOLUL 3. IOI 2018 331

260 }
261
262 auto t3 = clock();
263
264 FILE *file_out = fopen("out.txt", "w");
265 fprintf(file_out, "%d\n", S);
266
267 for (int i = 0; i <= M; ++i)
268 {
269 fprintf(file_out, "%d\n", IC[i]);
270 }
271
272 for (int j = 1; j <= S; ++j)
273 {
274 fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
275 }
276
277 fclose(file_out);
278
279 auto t4 = clock();
280
281 simulate();
282
283 auto t5 = clock();
284
285 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
286 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
287 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
288 std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;
289
290 return 0;
291 }
292 //-------------- end grader ------------------
293 /*
294 Accepted: 200006 3836160
295 t2-t1 = 0.359
296 t3-t2 = 0.016
297 t4-t3 = 0.328
298 t5-t4 = 3.714
299
300 Process returned 0 (0x0) execution time : 4.449 s
301 Press any key to continue.
302 */

Listing 3.4.7: dool_76623.cpp


1 // https://oj.uz/submission/76623
2 // https://oj.uz/profile/georgerapeanu
3
4 #include<fstream>
5 #include<iostream>
6
7 //#include <ctime>
8 #include <time.h> /* clock */
9
10 //#include <cstdio>
11 #include <stdio.h> // citire mai rapida !!!
12
13 #include <cstdio>
14 #include <cstdlib>
15
16 #include "doll.h"
17
18 using namespace std;
19
20 #include <queue>
21
22 using namespace std;
23
24 int sw[(int)4e5][2];
25 int trig[(int)2e5];
26
27 void create_circuit(int M, vector<int> A)
28 {
29
CAPITOLUL 3. IOI 2018 332

30 for(int i = 0;i <= M;i++)


31 {
32 trig[i] = -1;
33 }
34
35 int N = A.size();
36 int lgN = 0;
37 while((1 << lgN) <= N)
38 {
39 lgN++;
40 }
41 lgN--;
42
43 int last_switch = -(lgN + 1);
44
45 queue< pair<int,int> > switches;
46
47 for(int i = 0;i < lgN;i++)
48 {
49
50 sw[i + 1][1] = -(i + 2);
51
52 if((N >> (lgN - i)) & 1)
53 {
54 sw[i + 1][0] = --last_switch;
55 switches.push({last_switch,lgN - i - 1});
56 }
57 else
58 {
59 sw[i + 1][0] = -1;
60 }
61 }
62
63 sw[lgN + 1][1] = 0;
64 if(N & 1)
65 {
66 sw[lgN + 1][0] = 0;
67 }
68 else
69 {
70 sw[lgN + 1][0] = -1;
71 }
72
73 while(!switches.empty())
74 {
75 int s = switches.front().first;
76 int lvl = switches.front().second;
77 switches.pop();
78 if(lvl != 0)
79 {
80 sw[-s][0] = --last_switch;
81 switches.push({last_switch,lvl - 1});
82 sw[-s][1] = --last_switch;
83 switches.push({last_switch,lvl - 1});
84 }
85 else
86 {
87 sw[-s][0] = sw[-s][1] = 0;
88 }
89 }
90
91
92 int ind = 0;
93 int node = -1;
94
95 while(node != 0)
96 {
97 if(sw[-node][0] == 0)
98 {
99 if(ind == A.size())
100 {
101 swap(sw[-node][0],sw[-node][1]);
102 node = 0;
103 continue;
104 }
105 sw[-node][0] = A[ind++];
CAPITOLUL 3. IOI 2018 333

106 swap(sw[-node][0],sw[-node][1]);
107 node = -1;
108 }
109 else
110 {
111 swap(sw[-node][0],sw[-node][1]);
112 node = sw[-node][1];
113 }
114 }
115
116
117 vector<int> X(-last_switch),Y(-last_switch),C(M + 1);
118
119 for(int i = 0;i <= M;i++)
120 {
121 C[i] = trig[i];
122 }
123
124
125 for(int i = 0;i < -last_switch;i++)
126 {
127 X[i] = sw[i + 1][0];
128 Y[i] = sw[i + 1][1];
129 }
130
131 answer(C,X,Y);
132 }
133 //-------------- start grader ------------------
134
135 namespace
136 {
137
138 constexpr int P_MAX = 20000000;
139 constexpr int S_MAX = 400000;
140
141 int M, N;
142 std::vector<int> A;
143
144 bool answered;
145 int S;
146 std::vector<int> IC, IX, IY;
147
148 int read_int()
149 {
150 int x;
151 if (scanf("%d", &x) != 1)
152 {
153 fprintf(stderr, "Error while reading input\n");
154 exit(1);
155 }
156 return x;
157 }
158
159 void wrong_answer(const char *MSG)
160 {
161 printf("Wrong Answer: %s\n", MSG);
162 exit(0);
163 }
164
165 void simulate()
166 {
167 if (S > S_MAX)
168 {
169 char str[50];
170 sprintf(str, "over %d switches", S_MAX);
171 wrong_answer(str);
172 }
173
174 for (int i = 0; i <= M; ++i)
175 {
176 if (!(-S <= IC[i] && IC[i] <= M))
177 {
178 wrong_answer("wrong serial number");
179 }
180 }
181
CAPITOLUL 3. IOI 2018 334

182 for (int j = 1; j <= S; ++j)


183 {
184 if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
185 {
186 wrong_answer("wrong serial number");
187 }
188 if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
189 {
190 wrong_answer("wrong serial number");
191 }
192 }
193
194 int P = 0;
195 std::vector<bool> state(S + 1, false);
196 int pos = IC[0];
197 int k = 0;
198
199 FILE *file_log = fopen("log.txt", "w");
200 fprintf(file_log, "0\n");
201
202 for (;;)
203 {
204 fprintf(file_log, "%d\n", pos);
205 if (pos < 0)
206 {
207 if (++P > P_MAX)
208 {
209 fclose(file_log);
210 char str[50];
211 sprintf(str, "over %d inversions", P_MAX);
212 wrong_answer(str);
213 }
214
215 state[-pos] = !state[-pos];
216 pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
217 }
218 else
219 {
220 if (pos == 0)
221 {
222 break;
223 }
224
225 if (k >= N)
226 {
227 fclose(file_log);
228 wrong_answer("wrong motion");
229 }
230
231 if (pos != A[k++])
232 {
233 fclose(file_log);
234 wrong_answer("wrong motion");
235 }
236
237 pos = IC[pos];
238 }
239 }
240
241 fclose(file_log);
242
243 if (k != N)
244 {
245 wrong_answer("wrong motion");
246 }
247
248 for (int j = 1; j <= S; ++j)
249 {
250 if (state[j])
251 {
252 wrong_answer("state ’Y’");
253 }
254 }
255
256 printf("Accepted: %d %d\n", S, P);
257 }
CAPITOLUL 3. IOI 2018 335

258 } // namespace
259
260 void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)
261 {
262 if (answered)
263 {
264 wrong_answer("answered not exactly once");
265 }
266
267 answered = true;
268 // check if input format is correct
269 if ((int)C.size() != M + 1)
270 {
271 wrong_answer("wrong array length");
272 }
273
274 if (X.size() != Y.size())
275 {
276 wrong_answer("wrong array length");
277 }
278
279 S = X.size();
280 IC = C;
281 IX = X;
282 IY = Y;
283 }
284
285 int main()
286 {
287 std::freopen("../in/03-04.txt", "r", stdin);
288 //std::freopen("doll.out", "w", stdout);
289
290 auto t1 = clock();
291
292 M = read_int();
293 N = read_int();
294 A.resize(N);
295 for (int k = 0; k < N; ++k)
296 {
297 A[k] = read_int();
298 }
299 fclose(stdin);
300
301 auto t2 = clock();
302
303 answered = false;
304 create_circuit(M, A);
305 if (!answered)
306 {
307 wrong_answer("answered not exactly once");
308 }
309
310 auto t3 = clock();
311
312 FILE *file_out = fopen("out.txt", "w");
313 fprintf(file_out, "%d\n", S);
314
315 for (int i = 0; i <= M; ++i)
316 {
317 fprintf(file_out, "%d\n", IC[i]);
318 }
319
320 for (int j = 1; j <= S; ++j)
321 {
322 fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
323 }
324
325 fclose(file_out);
326
327 auto t4 = clock();
328
329 simulate();
330
331 auto t5 = clock();
332
333 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 3. IOI 2018 336

334 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
335 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
336 std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;
337
338 return 0;
339 }
340 //-------------- end grader ------------------
341 /*
342 Accepted: 200006 3836160
343 t2-t1 = 0.406
344 t3-t2 = 0.219
345 t4-t3 = 0.296
346 t5-t4 = 3.227
347
348 Process returned 0 (0x0) execution time : 4.196 s
349 Press any key to continue.
350 */

Listing 3.4.8: dool_78782.cpp


1 // https://oj.uz/submission/78782
2 // https://oj.uz/profile/tincamatei
3
4 #include <bits/stdc++.h>
5
6 #include "doll.h"
7
8 using namespace std;
9
10 int lastid;
11
12 vector<int> X, Y;
13 vector<pair<int, pair<bool, int> > > poz;
14
15 int dfs(int depth, int N, int p, bool branch, int papa, int bit = 0)
16 {
17 if(depth >= 0)
18 {
19 int id = -((int)X.size()) - 1;
20 X.push_back(0);
21 Y.push_back(0);
22 if((1 << depth) > N)
23 {
24 X[-id - 1] = -1;
25 int r1 = dfs(depth - 1, N, p | (1 << bit), 1, id, bit + 1);
26 Y[-id - 1] = r1;
27 }
28 else
29 {
30 int r1 = dfs(depth - 1, (1 << depth), p, 0, id, bit + 1);
31 int r2 = dfs(depth - 1, N ^ (1 << depth), p | (1 << bit), 1, id, bit +
1);
32 X[-id - 1] = r1;
33 Y[-id - 1] = r2;
34 }
35 return id;
36 }
37 else
38 {
39 if((1 << bit) - 1 == p)
40 {
41 return 0;
42 }
43 else
44 if(N == 0)
45 {
46 return -1;
47 }
48 else
49 {
50 poz.push_back(make_pair(p, make_pair(branch, papa)));
51 return 2;
52 }
53 }
54 }
CAPITOLUL 3. IOI 2018 337

55
56 void create_circuit(int M, std::vector<int> A)
57 {
58 vector<int> C(M + 1, -1);
59
60 int maxlg = 0;
61 int N = A.size();
62 while((1 << maxlg) <= N)
63 ++maxlg;
64 lastid = -maxlg;
65
66 dfs(maxlg - 1, N, 0, 0, 0);
67
68 sort(poz.begin(), poz.end());
69
70 for(int i = 0; i < poz.size(); ++i)
71 {
72 if(poz[i].second.first)
73 Y[-poz[i].second.second - 1] = A[i];
74 else
75 X[-poz[i].second.second - 1] = A[i];
76 }
77
78 answer(C, X, Y);
79 }
80
81 //-------------- start grader ------------------
82
83 namespace
84 {
85
86 constexpr int P_MAX = 20000000;
87 constexpr int S_MAX = 400000;
88
89 int M, N;
90 std::vector<int> A;
91
92 bool answered;
93 int S;
94 std::vector<int> IC, IX, IY;
95
96 int read_int()
97 {
98 int x;
99 if (scanf("%d", &x) != 1)
100 {
101 fprintf(stderr, "Error while reading input\n");
102 exit(1);
103 }
104 return x;
105 }
106
107 void wrong_answer(const char *MSG)
108 {
109 printf("Wrong Answer: %s\n", MSG);
110 exit(0);
111 }
112
113 void simulate()
114 {
115 if (S > S_MAX)
116 {
117 char str[50];
118 sprintf(str, "over %d switches", S_MAX);
119 wrong_answer(str);
120 }
121
122 for (int i = 0; i <= M; ++i)
123 {
124 if (!(-S <= IC[i] && IC[i] <= M))
125 {
126 wrong_answer("wrong serial number");
127 }
128 }
129
130 for (int j = 1; j <= S; ++j)
CAPITOLUL 3. IOI 2018 338

131 {
132 if (!(-S <= IX[j - 1] && IX[j - 1] <= M))
133 {
134 wrong_answer("wrong serial number");
135 }
136 if (!(-S <= IY[j - 1] && IY[j - 1] <= M))
137 {
138 wrong_answer("wrong serial number");
139 }
140 }
141
142 int P = 0;
143 std::vector<bool> state(S + 1, false);
144 int pos = IC[0];
145 int k = 0;
146
147 FILE *file_log = fopen("log.txt", "w");
148 fprintf(file_log, "0\n");
149
150 for (;;)
151 {
152 fprintf(file_log, "%d\n", pos);
153 if (pos < 0)
154 {
155 if (++P > P_MAX)
156 {
157 fclose(file_log);
158 char str[50];
159 sprintf(str, "over %d inversions", P_MAX);
160 wrong_answer(str);
161 }
162
163 state[-pos] = !state[-pos];
164 pos = state[-pos] ? IX[-(1 + pos)] : IY[-(1 + pos)];
165 }
166 else
167 {
168 if (pos == 0)
169 {
170 break;
171 }
172
173 if (k >= N)
174 {
175 fclose(file_log);
176 wrong_answer("wrong motion");
177 }
178
179 if (pos != A[k++])
180 {
181 fclose(file_log);
182 wrong_answer("wrong motion");
183 }
184
185 pos = IC[pos];
186 }
187 }
188
189 fclose(file_log);
190
191 if (k != N)
192 {
193 wrong_answer("wrong motion");
194 }
195
196 for (int j = 1; j <= S; ++j)
197 {
198 if (state[j])
199 {
200 wrong_answer("state ’Y’");
201 }
202 }
203
204 printf("Accepted: %d %d\n", S, P);
205 }
206 } // namespace
CAPITOLUL 3. IOI 2018 339

207
208 void answer(std::vector<int> C, std::vector<int> X, std::vector<int> Y)
209 {
210 if (answered)
211 {
212 wrong_answer("answered not exactly once");
213 }
214
215 answered = true;
216 // check if input format is correct
217 if ((int)C.size() != M + 1)
218 {
219 wrong_answer("wrong array length");
220 }
221
222 if (X.size() != Y.size())
223 {
224 wrong_answer("wrong array length");
225 }
226
227 S = X.size();
228 IC = C;
229 IX = X;
230 IY = Y;
231 }
232
233 int main()
234 {
235 std::freopen("../in/03-04.txt", "r", stdin);
236 //std::freopen("doll.out", "w", stdout);
237
238 auto t1 = clock();
239
240 M = read_int();
241 N = read_int();
242 A.resize(N);
243 for (int k = 0; k < N; ++k)
244 {
245 A[k] = read_int();
246 }
247 fclose(stdin);
248
249 auto t2 = clock();
250
251 answered = false;
252 create_circuit(M, A);
253 if (!answered)
254 {
255 wrong_answer("answered not exactly once");
256 }
257
258 auto t3 = clock();
259
260 FILE *file_out = fopen("out.txt", "w");
261 fprintf(file_out, "%d\n", S);
262
263 for (int i = 0; i <= M; ++i)
264 {
265 fprintf(file_out, "%d\n", IC[i]);
266 }
267
268 for (int j = 1; j <= S; ++j)
269 {
270 fprintf(file_out, "%d %d\n", IX[j - 1], IY[j - 1]);
271 }
272
273 fclose(file_out);
274
275 auto t4 = clock();
276
277 simulate();
278
279 auto t5 = clock();
280
281 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
282 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 3. IOI 2018 340

283 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
284 std::cout <<"t5-t4 = " << (double)(t5 - t4) / CLOCKS_PER_SEC << ’\n’;
285
286 return 0;
287 }
288 //-------------- end grader ------------------
289 /*
290 Accepted: 200006 3836160
291 t2-t1 = 0.343
292 t3-t2 = 0.36
293 t4-t3 = 0.312
294 t5-t4 = 3.656
295
296 Process returned 0 (0x0) execution time : 4.703 s
297 Press any key to continue.
298 */

3.4.3 *Rezolvare detaliat 

3.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 ecare din autostr zi. Taxa pe autostrad  depinde
de condiµiile de trac. Tracul poate  relaxat sau intens. Când tracul este relaxat taxa este
de A yen (valut  japonez ). Când tracul 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 tracului 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 trac specicate.
Totu³i, dispozitivul este doar un prototip. Valorile S ³i T sunt xate (adic  în echipament)
³i necunoscute. Trebuie s  determinaµi valorile S ³i T . Pentru a realiza aceasta, planicaµi s 
specicaµi dispozitivului anumite condiµii de trac ³i s  folosiµi valorile taxelor calculate de acesta
pentru a deduce S ³i T . Deoarece specicarea condiµiilor de trac 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 ecare i (0 & i & M  1), autostrada i conecteaz  ora³ele U i ³i V i.
a A: taxa de autostrad  când tracul este relaxat.
a B : taxa de autostrad  când tracul este intens.
a Aceast  procedur  este apelat  exact o dat  pentru ecare test.
a Luaµi la cuno³tinµ  c  valoarea M reprezint  dimensiunea tablourilor ³i poate  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  e M . Tabloul w descrie condiµiile de trac.
CAPITOLUL 3. IOI 2018 341

a Pentru ecare i (0 & i & M  1), w i descrie condiµiile de trac pe autostrada i. Valoarea
wi trebuie s  e 0 sau 1.
` w[i] = 0 înseamn  c  tracul pe autostrada i este relaxat.
` w[i] = 1 înseamn  c  tracul 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 trac specicate de w.
a Aceast  funcµie poate  apelat  de cel mult 100 de ori (pentru ecare test).

find_pair trebuie s  apeleze urm toarea procedur  pentru întoarce r spunsul:


answer(int s, int t)
a s ³i t trebuie s  e perechea S ³i T (ordinea nu conteaz ).
a Aceast  procedur  trebuie s  e 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 3.7: highway

În gura 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

Tabela 3.2: highway

La apelul funcµiei ask([0, 0, 0, 0]), tracul pe ecare 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 3. IOI 2018 342

a 2 & N & 90000


a 1 & M & 130000
a 1 & A $ B & 1000000000
a Pentru ecare 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 xate 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 ecare 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 a³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 a³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 3. IOI 2018 343

3.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 xed but they are unknown to us.
We want to nd 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 nd 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 nd 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 nd 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 nd 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 3. IOI 2018 344

ˆ 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 nd s and t as in the last part of Subtask 4.

3.5.2 Coduri surs 

Listing 3.5.1: compile_cpp.sh


1 #!/bin/bash
2
3 TASK=highway
4
5 g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 3.5.2: highway.h


1 #include <vector>
2
3 void find_pair(int N, std::vector<int> U, std::vector<int> V, int A, int B);
4
5 long long ask(const std::vector<int> &w);
6 void answer(int s, int t);

Listing 3.5.3: highway.cpp


1 #include "highway.h"
2
3 void find_pair(int N, std::vector<int> U,
4 std::vector<int> V, int A, int B)
5 {
6 int M = U.size();
7 for (int j = 0; j < 50; ++j)
8 {
9 std::vector<int> w(M);
10 for (int i = 0; i < M; ++i)
11 {
12 w[i] = 0;
13 }
14 long long toll = ask(w);
15 }
16 answer(0, N - 1);
17 }

Listing 3.5.4: grader.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #include <queue>
5 #include <utility>
6 #include <vector>
7 #include "highway.h"
8
9 namespace
10 {
11 constexpr int MAX_NUM_CALLS = 100;
12 constexpr long long INF = 1LL << 61;
13
14 int N, M, A, B, S, T;
15 std::vector<int> U, V;
16 std::vector<std::vector<std::pair<int, int>>> graph;
17
18 bool answered, wrong_pair;
19 int num_calls;
CAPITOLUL 3. IOI 2018 345

20
21 int read_int()
22 {
23 int x;
24 if (scanf("%d", &x) != 1)
25 {
26 fprintf(stderr, "Error while reading input\n");
27 exit(1);
28 }
29 return x;
30 }
31
32 void wrong_answer(const char *MSG)
33 {
34 printf("Wrong Answer: %s\n", MSG);
35 exit(0);
36 }
37
38 } // namespace
39
40 long long ask(const std::vector<int> &w)
41 {
42 if (++num_calls > MAX_NUM_CALLS)
43 {
44 wrong_answer("more than 100 calls to ask");
45 }
46 if (w.size() != (size_t)M)
47 {
48 wrong_answer("w is invalid");
49 }
50 for (size_t i = 0; i < w.size(); ++i)
51 {
52 if (!(w[i] == 0 || w[i] == 1))
53 {
54 wrong_answer("w is invalid");
55 }
56 }
57
58 std::vector<bool> visited(N, false);
59 std::vector<long long> current_dist(N, INF);
60 std::queue<int> qa, qb;
61 qa.push(S);
62 current_dist[S] = 0;
63 while (!qa.empty() || !qb.empty())
64 {
65 int v;
66 if (qb.empty() ||
67 (!qa.empty() && current_dist[qa.front()] <=
68 current_dist[qb.front()]))
69 {
70 v = qa.front();
71 qa.pop();
72 }
73 else
74 {
75 v = qb.front();
76 qb.pop();
77 }
78 if (visited[v])
79 {
80 continue;
81 }
82 visited[v] = true;
83 long long d = current_dist[v];
84 if (v == T)
85 {
86 return d;
87 }
88 for (auto e : graph[v])
89 {
90 int vv = e.first;
91 int ei = e.second;
92 if (!visited[vv])
93 {
94 if (w[ei] == 0)
95 {
CAPITOLUL 3. IOI 2018 346

96 if (current_dist[vv] > d + A)
97 {
98 current_dist[vv] = d + A;
99 qa.push(vv);
100 }
101 }
102 else
103 {
104 if (current_dist[vv] > d + B)
105 {
106 current_dist[vv] = d + B;
107 qb.push(vv);
108 }
109 }
110 }
111 }
112 }
113 return -1;
114 }
115
116 void answer(int s, int t)
117 {
118 if (answered)
119 {
120 wrong_answer("answered not exactly once");
121 }
122
123 if (!((s == S && t == T) || (s == T && t == S)))
124 {
125 wrong_pair = true;
126 }
127
128 answered = true;
129 }
130
131 int main()
132 {
133 N = read_int();
134 M = read_int();
135 A = read_int();
136 B = read_int();
137 S = read_int();
138 T = read_int();
139
140 U.resize(M);
141 V.resize(M);
142 graph.assign(N, std::vector<std::pair<int, int>>());
143
144 for (int i = 0; i < M; ++i)
145 {
146 U[i] = read_int();
147 V[i] = read_int();
148 graph[U[i]].push_back({V[i], i});
149 graph[V[i]].push_back({U[i], i});
150 }
151
152 answered = false;
153 wrong_pair = false;
154 num_calls = 0;
155 find_pair(N, U, V, A, B);
156
157 if (!answered)
158 {
159 wrong_answer("answered not exactly once");
160 }
161 if (wrong_pair)
162 {
163 wrong_answer("{s, t} is wrong");
164 }
165 printf("Accepted: %d\n", num_calls);
166 return 0;
167 }

Listing 3.5.5: highway_74962.cpp


CAPITOLUL 3. IOI 2018 347

1 // https://oj.uz/submission/74962
2
3 //#include <ctime>
4 #include <time.h> /* clock */
5
6 //#include <cstdio>
7 #include <stdio.h> // citire mai rapida !!!
8
9 #include<bits/stdc++.h>
10
11 #include "highway.h"
12
13 #define X first
14 #define Y second
15
16 using namespace std;
17
18 typedef long long ll;
19 typedef pair<int,int> pii;
20 typedef pair<ll,ll> pll;
21
22 const int NN = 90005;
23 const int MM = 1300005;
24
25 const ll inf = 1e18;
26
27 int n, m, a, b, p;
28 ll def, dis[NN];
29 bool ban[NN], v1[NN], v2[NN];
30
31 vector<int> adj[NN], dij[NN], drv[NN], ord;
32 vector<pii> edg;
33
34 queue<int> q;
35
36 ll query ()
37 {
38 vector<int> V;
39 for(int i=0;i<m;i++)
40 {
41 V.push_back(ban[edg[i].X] || ban[edg[i].Y]);
42 }
43 return ask(V);
44 }
45
46 int getpiv ()
47 {
48 int S = 0, E = (int)ord.size() - 1;
49 while(S<E)
50 {
51 int Z = (S+E)/2;
52 for(int i=0;i<=Z;i++)
53 {
54 ban[ord[i]] = true;
55 }
56 query() != def ? E = Z : S = Z+1;
57 for(int i=0;i<=Z;i++)
58 {
59 ban[ord[i]] = false;
60 }
61 }
62 return ord[S];
63 }
64
65 void dfs1 (int C)
66 {
67 if(C == p || v1[C]) return;
68 v1[C] = true;
69 for(auto &T : drv[C])
70 {
71 dfs1(T);
72 }
73 }
74
75 void dfs2 (int C)
76 {
CAPITOLUL 3. IOI 2018 348

77 if(v2[C]) return;
78 v2[C] = true;
79 for(auto &T : dij[C])
80 {
81 dfs2(T);
82 }
83 }
84
85 void find_pair(int _N, vector<int> _U, vector<int> _V, int _A, int _B)
86 {
87 n = _N;
88 m = (int)_U.size();
89 a = _A;
90 b = _B;
91 for(int i=0;i<m;i++)
92 {
93 edg.push_back({_U[i]+1, _V[i]+1});
94 adj[_U[i]+1].push_back(_V[i]+1);
95 adj[_V[i]+1].push_back(_U[i]+1);
96 }
97 def = query();
98 for(int i=1;i<=n;i++)
99 {
100 ord.push_back(i);
101 }
102 p = getpiv();
103 ord.clear();
104 for(int i=1;i<p;i++)
105 {
106 ban[i] = true;
107 }
108 for(int i=1;i<=n;i++)
109 {
110 dis[i] = inf;
111 }
112 dis[p] = 0;
113 q.push(p);
114 while(!q.empty())
115 {
116 int C = q.front();
117 q.pop();
118 ord.push_back(C);
119 for(auto &T : adj[C])
120 {
121 if(T < p) continue;
122 if(dis[T] == inf)
123 {
124 dis[T] = dis[C] + 1;
125 q.push(T);
126 }
127 if(dis[T] == dis[C] - 1)
128 {
129 dij[T].push_back(C);
130 drv[C].push_back(T);
131 }
132 }
133 }
134 reverse(ord.begin(), ord.end());
135 int PA = getpiv();
136 ord.clear();
137 dfs1(PA);
138 for(int i=p;i<=n;i++)
139 {
140 if(v1[i]) dfs2(i);
141 }
142 for(int i=p;i<=n;i++)
143 {
144 if(!v2[i] && dis[i] + dis[PA] == def / a) ord.push_back(i);
145 }
146 int PB = getpiv();
147 ord.clear();
148 answer(PA-1, PB-1);
149 }
150
151 //-------------- start grader ------------------
152
CAPITOLUL 3. IOI 2018 349

153 namespace
154 {
155 constexpr int MAX_NUM_CALLS = 100;
156 constexpr long long INF = 1LL << 61;
157
158 int N, M, A, B, S, T;
159 std::vector<int> U, V;
160 std::vector<std::vector<std::pair<int, int>>> graph;
161
162 bool answered, wrong_pair;
163 int num_calls;
164
165 int read_int()
166 {
167 int x;
168 if (scanf("%d", &x) != 1)
169 {
170 fprintf(stderr, "Error while reading input\n");
171 exit(1);
172 }
173 return x;
174 }
175
176 void wrong_answer(const char *MSG)
177 {
178 printf("Wrong Answer: %s\n", MSG);
179 exit(0);
180 }
181
182 } // namespace
183
184 long long ask(const std::vector<int> &w)
185 {
186 if (++num_calls > MAX_NUM_CALLS)
187 {
188 wrong_answer("more than 100 calls to ask");
189 }
190 if (w.size() != (size_t)M)
191 {
192 wrong_answer("w is invalid");
193 }
194 for (size_t i = 0; i < w.size(); ++i)
195 {
196 if (!(w[i] == 0 || w[i] == 1))
197 {
198 wrong_answer("w is invalid");
199 }
200 }
201
202 std::vector<bool> visited(N, false);
203 std::vector<long long> current_dist(N, INF);
204 std::queue<int> qa, qb;
205 qa.push(S);
206 current_dist[S] = 0;
207 while (!qa.empty() || !qb.empty())
208 {
209 int v;
210 if (qb.empty() ||
211 (!qa.empty() && current_dist[qa.front()] <= current_dist[qb.front()]))
212 {
213 v = qa.front();
214 qa.pop();
215 }
216 else
217 {
218 v = qb.front();
219 qb.pop();
220 }
221 if (visited[v])
222 {
223 continue;
224 }
225 visited[v] = true;
226 long long d = current_dist[v];
227 if (v == T)
228 {
CAPITOLUL 3. IOI 2018 350

229 return d;
230 }
231 for (auto e : graph[v])
232 {
233 int vv = e.first;
234 int ei = e.second;
235 if (!visited[vv])
236 {
237 if (w[ei] == 0)
238 {
239 if (current_dist[vv] > d + A)
240 {
241 current_dist[vv] = d + A;
242 qa.push(vv);
243 }
244 }
245 else
246 {
247 if (current_dist[vv] > d + B)
248 {
249 current_dist[vv] = d + B;
250 qb.push(vv);
251 }
252 }
253 }
254 }
255 }
256 return -1;
257 }
258
259 void answer(int s, int t)
260 {
261 if (answered)
262 {
263 wrong_answer("answered not exactly once");
264 }
265
266 if (!((s == S && t == T) || (s == T && t == S)))
267 {
268 wrong_pair = true;
269 }
270
271 answered = true;
272 }
273
274 int main()
275 {
276
277 auto t1 = clock();
278
279 std::freopen("../in/06-41.txt", "r", stdin) ;
280 //std::freopen("highway.out", "w", stdout) ;
281
282 N = read_int();
283 M = read_int();
284 A = read_int();
285 B = read_int();
286 S = read_int();
287 T = read_int();
288 U.resize(M);
289 V.resize(M);
290
291 graph.assign(N, std::vector<std::pair<int, int>>());
292
293 for (int i = 0; i < M; ++i)
294 {
295 U[i] = read_int();
296 V[i] = read_int();
297 graph[U[i]].push_back({V[i], i});
298 graph[V[i]].push_back({U[i], i});
299 }
300
301 auto t2 = clock();
302
303 answered = false;
304 wrong_pair = false;
CAPITOLUL 3. IOI 2018 351

305 num_calls = 0;
306
307 find_pair(N, U, V, A, B);
308
309 auto t3 = clock();
310
311 if (!answered)
312 {
313 wrong_answer("answered not exactly once");
314 }
315 if (wrong_pair)
316 {
317 wrong_answer("{s, t} is wrong");
318 }
319 printf("Accepted: %d\n", num_calls);
320
321 auto t4 = clock();
322
323 fclose(stdout);
324
325 // reset console output
326 freopen("CON", "w", stdout);
327
328 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
329 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
330 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
331
332 return 0;
333 }
334 /*
335 Accepted: 39
336 t2-t1 = 0.328
337 t3-t2 = 3.834
338 t4-t3 = 0
339
340 Process returned 0 (0x0) execution time : 4.271 s
341 Press any key to continue.
342 */

Listing 3.5.6: highway_77105.cpp


1 // https://oj.uz/submission/77105
2
3 //#include <ctime>
4 #include <time.h> /* clock */
5
6 //#include <cstdio>
7 #include <stdio.h> // citire mai rapida !!!
8
9 #include <bits/stdc++.h>
10
11 #include "highway.h"
12
13 using namespace std;
14
15 using lint = long long;
16 using pi = pair<int, int>;
17 const int MAXN = 90005;
18
19 int dist[MAXN];
20 vector<int> gph[MAXN];
21
22 lint cut_ask(int m, vector<int> &c, vector<int> &u, vector<int> &v){
23 bitset<MAXN> vis;
24 for(auto &i : c) vis[i] = 1;
25 vector<int> query(m);
26 for(int i=0; i<m; i++) if(vis[u[i]] != vis[v[i]]) query[i] = 1;
27 return ask(query);
28 }
29
30 void find_pair(int N, std::vector<int> U, std::vector<int> V, int A, int B) {
31 int M = U.size();
32 for(int i=0; i<M; i++){
33 gph[U[i]].emplace_back(V[i]);
34 gph[V[i]].emplace_back(U[i]);
CAPITOLUL 3. IOI 2018 352

35 }
36 vector<int> v(M);
37 lint stdist = ask(v) / A;
38 int s = 0, e = M - 1;
39 while(s != e){
40 int m = (s + e) / 2;
41 fill(v.begin() + m + 1, v.end(), 0);
42 fill(v.begin(), v.begin() + m + 1, 1);
43 if(ask(v) != A * stdist) e = m;
44 else s = m + 1;
45 }
46 vector<int> bord[2], dist(N, 1e9);
47 queue<pi> que;
48 que.emplace(0, U[s]);
49 que.emplace(1, V[s]);
50 dist[U[s]] = dist[V[s]] = 0;
51 while(!que.empty()){
52 auto x = que.front(); que.pop();
53 bord[x.first].push_back(x.second);
54 for(auto &i : gph[x.second]){
55 if(dist[i] > dist[x.second] + 1){
56 dist[i] = dist[x.second] + 1;
57 que.emplace(x.first, i);
58 }
59 }
60 }
61 int S = -1, T = -1;
62 for(int i=0; i<2; i++){
63 s = 0, e = (int)bord[i].size() - 1;
64 while(s != e){
65 int m = (s + e + 1) / 2;
66 vector<int> C(bord[i].begin() + m, bord[i].end());
67 if(cut_ask(M, C, U, V) != stdist * A) s = m;
68 else e = m - 1;
69 }
70 if(i) S = bord[i][s];
71 else T = bord[i][s];
72 }
73 answer(S, T);
74 }
75
76
77
78 //-------------- start grader ------------------
79
80 namespace
81 {
82 constexpr int MAX_NUM_CALLS = 100;
83 constexpr long long INF = 1LL << 61;
84
85 int N, M, A, B, S, T;
86 std::vector<int> U, V;
87 std::vector<std::vector<std::pair<int, int>>> graph;
88
89 bool answered, wrong_pair;
90 int num_calls;
91
92 int read_int()
93 {
94 int x;
95 if (scanf("%d", &x) != 1)
96 {
97 fprintf(stderr, "Error while reading input\n");
98 exit(1);
99 }
100 return x;
101 }
102
103 void wrong_answer(const char *MSG)
104 {
105 printf("Wrong Answer: %s\n", MSG);
106 exit(0);
107 }
108
109 } // namespace
110
CAPITOLUL 3. IOI 2018 353

111 long long ask(const std::vector<int> &w)


112 {
113 if (++num_calls > MAX_NUM_CALLS)
114 {
115 wrong_answer("more than 100 calls to ask");
116 }
117 if (w.size() != (size_t)M)
118 {
119 wrong_answer("w is invalid");
120 }
121 for (size_t i = 0; i < w.size(); ++i)
122 {
123 if (!(w[i] == 0 || w[i] == 1))
124 {
125 wrong_answer("w is invalid");
126 }
127 }
128
129 std::vector<bool> visited(N, false);
130 std::vector<long long> current_dist(N, INF);
131 std::queue<int> qa, qb;
132 qa.push(S);
133 current_dist[S] = 0;
134 while (!qa.empty() || !qb.empty())
135 {
136 int v;
137 if (qb.empty() ||
138 (!qa.empty() && current_dist[qa.front()] <= current_dist[qb.front()]))
139 {
140 v = qa.front();
141 qa.pop();
142 }
143 else
144 {
145 v = qb.front();
146 qb.pop();
147 }
148 if (visited[v]) {
149 continue;
150 }
151 visited[v] = true;
152 long long d = current_dist[v];
153 if (v == T)
154 {
155 return d;
156 }
157 for (auto e : graph[v])
158 {
159 int vv = e.first;
160 int ei = e.second;
161 if (!visited[vv])
162 {
163 if (w[ei] == 0)
164 {
165 if (current_dist[vv] > d + A)
166 {
167 current_dist[vv] = d + A;
168 qa.push(vv);
169 }
170 }
171 else
172 {
173 if (current_dist[vv] > d + B)
174 {
175 current_dist[vv] = d + B;
176 qb.push(vv);
177 }
178 }
179 }
180 }
181 }
182 return -1;
183 }
184
185 void answer(int s, int t)
186 {
CAPITOLUL 3. IOI 2018 354

187 if (answered)
188 {
189 wrong_answer("answered not exactly once");
190 }
191
192 if (!((s == S && t == T) || (s == T && t == S)))
193 {
194 wrong_pair = true;
195 }
196
197 answered = true;
198 }
199
200 int main()
201 {
202
203 auto t1 = clock();
204
205 std::freopen("../in/06-41.txt", "r", stdin) ;
206 //std::freopen("highway.out", "w", stdout) ;
207
208 N = read_int();
209 M = read_int();
210 A = read_int();
211 B = read_int();
212 S = read_int();
213 T = read_int();
214 U.resize(M);
215 V.resize(M);
216
217 graph.assign(N, std::vector<std::pair<int, int>>());
218
219 for (int i = 0; i < M; ++i)
220 {
221 U[i] = read_int();
222 V[i] = read_int();
223 graph[U[i]].push_back({V[i], i});
224 graph[V[i]].push_back({U[i], i});
225 }
226
227 auto t2 = clock();
228
229 answered = false;
230 wrong_pair = false;
231 num_calls = 0;
232
233 find_pair(N, U, V, A, B);
234
235 auto t3 = clock();
236
237 if (!answered)
238 {
239 wrong_answer("answered not exactly once");
240 }
241 if (wrong_pair)
242 {
243 wrong_answer("{s, t} is wrong");
244 }
245 printf("Accepted: %d\n", num_calls);
246
247 auto t4 = clock();
248
249 fclose(stdout);
250
251 // reset console output
252 freopen("CON", "w", stdout);
253
254 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
255 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
256 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
257
258 return 0;
259 }
260 /*
261 Accepted: 45
262 t2-t1 = 0.343
CAPITOLUL 3. IOI 2018 355

263 t3-t2 = 4.009


264 t4-t3 = 0
265
266 Process returned 0 (0x0) execution time : 4.431 s
267 Press any key to continue.
268 */

Listing 3.5.7: highway_78948.cpp


1 // https://oj.uz/submission/78948
2
3 //#include <ctime>
4 #include <time.h> /* clock */
5
6 //#include <cstdio>
7 #include <stdio.h> // citire mai rapida !!!
8
9 #include <bits/stdc++.h>
10
11 #include "highway.h"
12
13 using namespace std;
14
15 #define pb push_back
16 #define eb emplace_back
17 #define mp make_pair
18 #define f first
19 #define s second
20 #define all(a) (a).begin(),(a).end()
21 #define For(i,a,b) for(auto i=(a);i<(b);i++)
22 #define FOR(i,b) For(i,0,b)
23 #define Rev(i,a,b) for(auto i=(a);i>(b);i--)
24 #define REV(i,a) Rev(i,a,-1)
25 #define FORE(i,a) for(auto&&i:a)
26 #define sz(a) (int((a).size()))
27 #define MIN(a,b) ((a)=min((a),(b)))
28 #define MAX(a,b) ((a)=max((a),(b)))
29
30 using ll=long long;
31 using ld=long double;
32 using uint=unsigned int;
33 using ull=unsigned long long;
34
35 using pii=pair<int,int>;
36 using pll=pair<ll,ll>;
37 using pill=pair<int,ll>;
38 using plli=pair<ll,int>;
39 using pdd=pair<double,double>;
40 using pld=pair<ld,ld>;
41
42 constexpr const char nl=’\n’,sp=’ ’;
43
44 constexpr const int INT_INF=0x3f3f3f3f;
45 constexpr const ll LL_INF=0x3f3f3f3f3f3f3f3f;
46
47 constexpr const double D_INF=numeric_limits<double>::infinity();
48 constexpr const ld LD_INF=numeric_limits<ld>::infinity();
49
50 constexpr const double EPS=1e-9;
51
52 const int MAXN = 90005, MAXM = 130005;
53
54 int to[MAXN], ans[2];
55 bool tr[MAXM];
56 vector<int> adj[MAXN], verts[2];
57
58 void find_pair(int N, vector<int> U, vector<int> V, int A, int B)
59 {
60 int M = sz(U), lo = 0, hi = M - 1, mid;
61 FOR(i, M)
62 {
63 adj[U[i]].pb(i);
64 adj[V[i]].pb(i);
65 }
66 vector<int> Q(M, 0);
CAPITOLUL 3. IOI 2018 356

67 fill(tr, tr + M, 0);
68 ll DA = ask(Q);
69 while (lo < hi)
70 {
71 mid = lo + (hi - lo) / 2;
72 FOR(i, M) Q[i] = i <= mid;
73 if (ask(Q) == DA) lo = mid + 1;
74 else hi = mid;
75 }
76
77 fill(to, to + N, -1);
78
79 queue<pii> q;
80 q.emplace(U[lo], 0);
81 q.emplace(V[lo], 1);
82
83 tr[lo] = 1;
84 to[U[lo]] = to[V[lo]] = -INT_INF;
85
86 while (!q.empty())
87 {
88 pii v = q.front();
89 q.pop();
90 verts[v.s].pb(v.f);
91 FORE(e, adj[v.f])
92 {
93 int w = v.f ^ U[e] ^ V[e];
94 if (to[w] != -1) continue;
95 q.emplace(w, v.s);
96 tr[to[w] = e] = 1;
97 }
98 }
99
100 FOR(i, 2)
101 {
102 lo = 1, hi = sz(verts[i]) - 1;
103 while (lo <= hi)
104 {
105 mid = lo + (hi - lo) / 2;
106 FOR(j, M) Q[j] = !tr[j];
107 For(j, mid, sz(verts[i])) Q[to[verts[i][j]]] = 1;
108 if (ask(Q) == DA) hi = mid - 1;
109 else lo = mid + 1;
110 }
111 ans[i] = verts[i][hi];
112 }
113 answer(ans[0], ans[1]);
114 }
115
116 //-------------- start grader ------------------
117
118 namespace
119 {
120 constexpr int MAX_NUM_CALLS = 100;
121 constexpr long long INF = 1LL << 61;
122
123 int N, M, A, B, S, T;
124 std::vector<int> U, V;
125 std::vector<std::vector<std::pair<int, int>>> graph;
126
127 bool answered, wrong_pair;
128 int num_calls;
129
130 int read_int()
131 {
132 int x;
133 if (scanf("%d", &x) != 1)
134 {
135 fprintf(stderr, "Error while reading input\n");
136 exit(1);
137 }
138 return x;
139 }
140
141 void wrong_answer(const char *MSG)
142 {
CAPITOLUL 3. IOI 2018 357

143 printf("Wrong Answer: %s\n", MSG);


144 exit(0);
145 }
146
147 } // namespace
148
149 long long ask(const std::vector<int> &w)
150 {
151 if (++num_calls > MAX_NUM_CALLS)
152 {
153 wrong_answer("more than 100 calls to ask");
154 }
155 if (w.size() != (size_t)M)
156 {
157 wrong_answer("w is invalid");
158 }
159 for (size_t i = 0; i < w.size(); ++i)
160 {
161 if (!(w[i] == 0 || w[i] == 1))
162 {
163 wrong_answer("w is invalid");
164 }
165 }
166
167 std::vector<bool> visited(N, false);
168 std::vector<long long> current_dist(N, INF);
169 std::queue<int> qa, qb;
170 qa.push(S);
171 current_dist[S] = 0;
172 while (!qa.empty() || !qb.empty())
173 {
174 int v;
175 if (qb.empty() ||
176 (!qa.empty() && current_dist[qa.front()] <= current_dist[qb.front()]))
177 {
178 v = qa.front();
179 qa.pop();
180 }
181 else
182 {
183 v = qb.front();
184 qb.pop();
185 }
186 if (visited[v])
187 {
188 continue;
189 }
190 visited[v] = true;
191 long long d = current_dist[v];
192 if (v == T)
193 {
194 return d;
195 }
196 for (auto e : graph[v])
197 {
198 int vv = e.first;
199 int ei = e.second;
200 if (!visited[vv])
201 {
202 if (w[ei] == 0)
203 {
204 if (current_dist[vv] > d + A)
205 {
206 current_dist[vv] = d + A;
207 qa.push(vv);
208 }
209 }
210 else
211 {
212 if (current_dist[vv] > d + B)
213 {
214 current_dist[vv] = d + B;
215 qb.push(vv);
216 }
217 }
218 }
CAPITOLUL 3. IOI 2018 358

219 }
220 }
221 return -1;
222 }
223
224 void answer(int s, int t)
225 {
226 if (answered)
227 {
228 wrong_answer("answered not exactly once");
229 }
230
231 if (!((s == S && t == T) || (s == T && t == S)))
232 {
233 wrong_pair = true;
234 }
235
236 answered = true;
237 }
238
239 int main()
240 {
241
242 auto t1 = clock();
243
244 std::freopen("../in/06-41.txt", "r", stdin) ;
245 //std::freopen("highway.out", "w", stdout) ;
246
247 N = read_int();
248 M = read_int();
249 A = read_int();
250 B = read_int();
251 S = read_int();
252 T = read_int();
253 U.resize(M);
254 V.resize(M);
255
256 graph.assign(N, std::vector<std::pair<int, int>>());
257
258 for (int i = 0; i < M; ++i)
259 {
260 U[i] = read_int();
261 V[i] = read_int();
262 graph[U[i]].push_back({V[i], i});
263 graph[V[i]].push_back({U[i], i});
264 }
265
266 auto t2 = clock();
267
268 answered = false;
269 wrong_pair = false;
270 num_calls = 0;
271
272 find_pair(N, U, V, A, B);
273
274 auto t3 = clock();
275
276 if (!answered)
277 {
278 wrong_answer("answered not exactly once");
279 }
280 if (wrong_pair)
281 {
282 wrong_answer("{s, t} is wrong");
283 }
284 printf("Accepted: %d\n", num_calls);
285
286 auto t4 = clock();
287
288 fclose(stdout);
289
290 // reset console output
291 freopen("CON", "w", stdout);
292
293 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
294 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 3. IOI 2018 359

295 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
296
297 return 0;
298 }
299 /*
300 Accepted: 45
301 t2-t1 = 0.343
302 t3-t2 = 4.04
303 t4-t3 = 0
304
305 Process returned 0 (0x0) execution time : 4.461 s
306 Press any key to continue.
307 */

3.5.3 *Rezolvare detaliat 

3.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
ec rui munte.
Planicaµ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 ecare 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 ecare ³edinµ  doriµi s  g siµi costul minim posibil de organizare a ei.
Luaµi la cuno³tinµ  c  dup  ecare ³edinµ  ecare participant se întoarce la muntele lui; deci
costul unei ³edinµe nu este inuenµ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  e 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  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 3. IOI 2018 360

Figura 3.8: Meetings

“edinµa j 0 are Lj 0 ³i Rj 2, deci participanµii vor  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  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 a³eaz  valoarea întoars  de minimum_costs în urm toarea form :
a linia 1  j (0 & j & Q  1): Cj
CAPITOLUL 3. IOI 2018 361

3.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 nd 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 3. IOI 2018 362

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-rst-search
post-order of the cartesian tree of H .
We dene 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, rst
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 rst 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 rst 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 3. IOI 2018 363

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

3.6.2 Coduri surs 

Listing 3.6.1: compile_cpp.sh


1 #!/bin/bash
2
3 TASK=meetings
4
5 g++ -std=gnu++14 -Wall -O2 -static -o ${TASK} grader.cpp ${TASK}.cpp

Listing 3.6.2: meetings.h


1 #include <vector>
2
3 std::vector<long long> minimum_costs(std::vector<int> H, std::vector<int> L,
4 std::vector<int> R);

Listing 3.6.3: meetings.cpp


1 #include "meetings.h"
2
3 std::vector<long long> minimum_costs(std::vector<int> H,
4 std::vector<int> L,
5 std::vector<int> R)
6 {
7 int Q = L.size();
8 std::vector<long long> C(Q);
9 for (int j = 0; j < Q; ++j)
10 {
11 C[j] = H[L[j]];
12 }
13 return C;
14 }

Listing 3.6.4: grader.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include <vector>
4 #include "meetings.h"
5
6 namespace
7 {
8 int read_int()
9 {
10 int x;
11 if (scanf("%d", &x) != 1)
12 {
13 fprintf(stderr, "Error while reading input\n");
14 exit(1);
15 }
16 return x;
17 }
18 } // namespace
19
20 int main()
21 {
22 int N = read_int();
23 int Q = read_int();
CAPITOLUL 3. IOI 2018 364

24 std::vector<int> H(N);
25 for (int i = 0; i < N; ++i)
26 {
27 H[i] = read_int();
28 }
29 std::vector<int> L(Q), R(Q);
30 for (int j = 0; j < Q; ++j)
31 {
32 L[j] = read_int();
33 R[j] = read_int();
34 }
35
36 std::vector<long long> C = minimum_costs(H, L, R);
37
38 for (size_t j = 0; j < C.size(); ++j)
39 {
40 printf("%lld\n", C[j]);
41 }
42 return 0;
43 }

Listing 3.6.5: meetings-model.cpp


1 // https://oj.uz/submission/120908
2
3 #include "meetings.h"
4
5 #include <algorithm>
6 #include <ctime>
7 #include <iostream>
8
9 namespace
10 {
11 int read_int()
12 {
13 int x;
14 if (scanf("%d", &x) != 1)
15 {
16 fprintf(stderr, "Error while reading input\n");
17 exit(1);
18 }
19 return x;
20 }
21 } // namespace
22
23 using namespace std;
24
25 typedef long long ll;
26 typedef pair <int, int> pii;
27
28 struct minseg
29 {
30 pii T[2202020];
31 int sz = 1 << 20;
32
33 void init(vector <int> &H)
34 {
35 int i;
36
37 for(i=0; i<H.size(); i++)
38 {
39 T[i + sz] = pii(H[i], i);
40 }
41
42 for(i=sz-1; i; i--)
43 {
44 T[i] = max(T[i << 1], T[i << 1 | 1]);
45 }
46 }
47
48 int getmax(int l, int r)
49 {
50 pii ret(-1, -1);
51
52 l += sz; r += sz;
CAPITOLUL 3. IOI 2018 365

53 for(; l<=r; )
54 {
55 if(l & 1) ret = max(ret, T[l]);
56 if(~r & 1) ret = max(ret, T[r]);
57 l = l + 1 >> 1;
58 r = r - 1 >> 1;
59 }
60
61 return ret.second;
62 }
63 };
64
65 struct line
66 {
67 ll x, a, b;
68 line() {}
69 line(ll x, ll a, ll b) : x(x), a(a), b(b) {}
70 };
71
72 struct deq
73 {
74 line D[808080];
75 int S[808080], E[808080];
76 ll V[808080];
77 int n;
78 bool t;
79
80 void init(bool _t, int _n)
81 {
82 int i;
83 t = _t; n = _n;
84 for(i=0; i<n; i++)
85 {
86 S[i] = i; E[i] = i;
87 D[i] = line(i, 0, 0);
88 }
89 }
90
91 void addval(int p, ll v)
92 {
93 if(t) p = n - 1 - p;
94 V[p] += v;
95 }
96
97 void addline(int p, ll x, ll a, ll b)
98 {
99 if(t)
100 {
101 p = n - 1 - p, x = n - 1 - x;
102 b = (n - 1) * a + b; a = -a;
103 }
104
105 int &s = S[p], &e = E[p];
106
107 b -= V[p];
108
109 for(; s<=e; e--)
110 {
111 if(D[e].a == a && D[e].b < b) return;
112 if(D[e].a * D[e].x + D[e].b < a * D[e].x + b)
113 {
114 D[++e] = line((D[e].b - b) / (a - D[e].a) + 1, a, b);
115 return;
116 }
117 }
118
119 D[++e] = line(x, a, b);
120 }
121
122 ll getval(int p, ll x)
123 {
124 if(t) p = n - 1 - p, x = n - 1 - x;
125
126 int k = upper_bound(D + S[p], D + E[p] + 1, x, [&](ll x, line l)
127 {
128 return x < l.x;
CAPITOLUL 3. IOI 2018 366

129 }) - D - 1;
130
131 return D[k].a * x + D[k].b + V[p];
132 }
133
134 void merge(int p, int q)
135 {
136 if(t) p = n - 1 - p, q = n - 1 - q;
137
138 int i;
139
140 if(E[p] - S[p] < E[q] - S[q])
141 {
142 swap(S[p], S[q]); swap(E[p], E[q]);
143 swap(V[p], V[q]);
144 }
145
146 if(S[p] < S[q])
147 {
148 for(i=S[q]; i<=E[q]; i++)
149 {
150 D[i].b += V[q] - V[p];
151 D[++E[p]] = D[i];
152 }
153 }
154 else
155 {
156 for(i=E[q]; i>=S[q]; i--)
157 {
158 D[i].b += V[q] - V[p];
159 D[--S[p]] = D[i];
160 }
161 }
162 }
163 };
164
165 minseg T;
166 deq DL, DR;
167
168 vector <int> H, L, R;
169 vector <int> Q[808080];
170 vector <ll> A;
171
172 int n;
173
174 int dnc(int s, int e)
175 {
176 if(s > e) return -1;
177
178 int m, l, r;
179 ll v, h;
180
181 m = T.getmax(s, e); h = H[m];
182 l = dnc(s, m - 1); r = dnc(m + 1, e);
183
184 for(int &q: Q[m])
185 {
186 if(L[q] == m && R[q] == m) A[q] = h;
187 else if(L[q] == m) A[q] = DR.getval(r, R[q]) + h;
188 else if(R[q] == m) A[q] = DL.getval(l, L[q]) + h;
189 else A[q] = min(DL.getval(l, L[q]) + h * (R[q] - m + 1),
190 DR.getval(r, R[q]) + h * (m - L[q] + 1));
191 }
192
193 if(l != -1) DL.merge(m, l);
194 DL.addval(m, h * (e - m + 1));
195 DL.addline(m, s, -h, (r != -1? DL.getval(r, m + 1) : 0) + h * (m + 1));
196 if(r != -1) DL.merge(m, r);
197
198 if(r != -1) DR.merge(m, r);
199 DR.addval(m, h * (m - s + 1));
200 DR.addline(m, e, h, (l != -1? DR.getval(l, m - 1) : 0) - h * (m - 1));
201 if(l != -1) DR.merge(m, l);
202
203 return m;
204 }
CAPITOLUL 3. IOI 2018 367

205
206 vector <ll> minimum_costs(vector <int> _H, vector <int> _L, vector <int> _R)
207 {
208 int i;
209
210 swap(H, _H); swap(L, _L); swap(R, _R);
211 n = H.size(); A.resize(L.size());
212
213 T.init(H); DL.init(0, n); DR.init(1, n);
214
215 for(i=0; i<L.size(); i++)
216 {
217 Q[T.getmax(L[i], R[i])].push_back(i);
218 }
219
220 dnc(0, n - 1);
221
222 return A;
223 }
224 // =======================================================================
225 int main()
226 {
227 auto t1 = clock();
228
229 std::freopen("../in/05-03.txt", "r", stdin) ;
230 std::freopen("meetings.out", "w", stdout) ;
231
232 int N = read_int();
233 int Q = read_int();
234 std::vector<int> H(N);
235 for (int i = 0; i < N; ++i) {
236 H[i] = read_int();
237 }
238 std::vector<int> L(Q), R(Q);
239 for (int j = 0; j < Q; ++j)
240 {
241 L[j] = read_int();
242 R[j] = read_int();
243 }
244
245 auto t2 = clock();
246
247 std::vector<long long> C = minimum_costs(H, L, R);
248
249 auto t3 = clock();
250
251 for (size_t j = 0; j < C.size(); ++j)
252 {
253 printf("%lld\n", C[j]);
254 }
255
256 auto t4 = clock();
257
258 fclose(stdout);
259
260 // reset console output
261 freopen("CON", "w", stdout);
262
263 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
264 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
265 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
266
267 return 0;
268 }
269 // =======================================================================
270 /*
271 t2-t1 = 4.192
272 t3-t2 = 2.416
273 t4-t3 = 0.97
274
275 Process returned 0 (0x0) execution time : 8.449 s
276 Press any key to continue.
277 */

Listing 3.6.6: meetings_76204.cpp


CAPITOLUL 3. IOI 2018 368

1 // https://oj.uz/submission/76204
2
3 #include <stdio.h> // citire mai rapida !!!
4
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 typedef long long llong;
10 typedef pair<int, int> pii;
11
12 int n;
13
14 const llong inf = 1e18;
15 vector<llong> ans;
16 pii seg[1 << 21];
17
18 struct query
19 {
20 int i, l, r;
21 query(int i, int l, int r) : i(i), l(l), r(r) {}
22 };
23
24 vector<query> qs[750001];
25 pii arr[750001];
26
27 void init(int i, int s, int e)
28 {
29 if (s == e)
30 {
31 seg[i] = arr[s];
32 return;
33 }
34 int m = (s + e) / 2;
35 init(i << 1, s, m);
36 init(i << 1 | 1, m + 1, e);
37 seg[i] = max(seg[i << 1], seg[i << 1 | 1]);
38 }
39
40 pii getMax(int i, int s, int e, int x, int y)
41 {
42 if (e < x || y < s) return pii(0, 0);
43 if (x <= s && e <= y) return seg[i];
44 int m = (s + e) / 2;
45 return max(getMax(i << 1, s,m,x,y), getMax(i << 1 | 1, m+1,e,x,y));
46 }
47
48 struct line
49 {
50 int s, e;
51 llong m, b;
52 line() {}
53 line(int s, int e, llong m, llong b) : s(s), e(e), m(m), b(b) {}
54
55 llong get(int x) const
56 {
57 return m * x + b;
58 }
59
60 bool operator<=(const line &p) const
61 {
62 return get(p.s) <= p.get(p.s) && get(p.e) <= p.get(p.e);
63 }
64 } ls[750001];
65
66 struct que
67 {
68 int s, e;
69 llong add;
70 que(int s, int e) : s(s), e(e), add(0) {}
71 llong getRight() const
72 {
73 if (s > e) return 0;
74 return ls[e].get(ls[e].e) + add;
75 }
CAPITOLUL 3. IOI 2018 369

76 int size() const


77 {
78 return e - s + 1;
79 }
80 line front()
81 {
82 line ret = ls[s];
83 ret.b += add;
84 return ret;
85 }
86 line back()
87 {
88 line ret = ls[e];
89 ret.b += add;
90 return ret;
91 }
92 void pop_front() { ++s; }
93 void pop_back() { --e; }
94 void push_front(line x)
95 {
96 x.b -= add;
97 ls[--s] = x;
98 }
99 void push_back(line x)
100 {
101 x.b -= add;
102 ls[++e] = x;
103 }
104 llong get(int x) const
105 {
106 int st = s, ed = e;
107 while (st <= ed)
108 {
109 int md = (st + ed) / 2;
110 if (ls[md].s <= x && x <= ls[md].e) return ls[md].get(x)+add;
111 if (x < ls[md].s) ed = md - 1;
112 else st = md + 1;
113 }
114 return 0;
115 }
116 };
117
118 que getQuery(int s, int e)
119 {
120 if (s > e) return que(s, e);
121 int m = abs(getMax(1, 1, n, s, e).second);
122 que L = getQuery(s, m - 1);
123 que R = getQuery(m + 1, e);
124 for (query q : qs[m])
125 {
126 ans[q.i] = min(ans[q.i], R.get(q.r) + (m-q.l+1ll)*arr[m].first);
127 }
128 R.add += (m - s + 1ll) * arr[m].first;
129 line l(m, m, arr[m].first, L.getRight() + (1ll - m) * arr[m].first);
130 while (R.size() && l <= R.front())
131 {
132 l.e = R.front().e;
133 R.pop_front();
134 }
135 if (R.size())
136 {
137 line r = R.front();
138 R.pop_front();
139 int st = r.s, ed = r.e;
140 while (st < ed)
141 {
142 int md = (st + ed) / 2;
143 if (l.get(md) < r.get(md)) st = md + 1;
144 else ed = md;
145 }
146 l.e = st - 1;
147 r.s = st;
148 R.push_front(r);
149 }
150 R.push_front(l);
151 if (L.size() < R.size())
CAPITOLUL 3. IOI 2018 370

152 {
153 while (L.size())
154 {
155 R.push_front(L.back());
156 L.pop_back();
157 }
158 return R;
159 }
160 else
161 {
162 while (R.size())
163 {
164 L.push_back(R.front());
165 R.pop_front();
166 }
167 return L;
168 }
169 }
170
171 vector<llong> minimum_costs(vector<int> H, vector<int> L, vector<int> R)
172 {
173 n = H.size();
174 int q = L.size();
175 ans.resize(q, inf);
176
177 for (int i = 0; i < n; ++i) arr[i + 1] = pii(H[i], i + 1);
178 init(1, 1, n);
179 for (int i = 0; i < q; ++i)
180 {
181 int mx = getMax(1, 1, n, L[i] + 1, R[i] + 1).second;
182 qs[mx].emplace_back(i, L[i] + 1, R[i] + 1);
183 }
184 getQuery(1, n);
185
186 for (int i = 1; i <= n; ++i) qs[i].clear();
187
188 for (int i = 0; i < n; ++i) arr[n - i] = pii(H[i], i - n);
189 init(1, 1, n);
190 for (int i = 0; i < q; ++i)
191 {
192 int mx = -getMax(1, 1, n, n - R[i], n - L[i]).second;
193 qs[mx].emplace_back(i, n - R[i], n - L[i]);
194 }
195 getQuery(1, n);
196 return ans;
197 }
198
199 //-------------- start grader ------------------
200
201 namespace
202 {
203 int read_int()
204 {
205 int x;
206 if (scanf("%d", &x) != 1)
207 {
208 fprintf(stderr, "Error while reading input\n");
209 exit(1);
210 }
211 return x;
212 }
213 } // namespace
214
215 int main()
216 {
217 std::freopen("../in/05-12.txt", "r", stdin);
218 std::freopen("meetings.out", "w", stdout);
219
220 auto t1 = clock();
221
222 int N = read_int();
223 int Q = read_int();
224 std::vector<int> H(N);
225 for (int i = 0; i < N; ++i)
226 {
227 H[i] = read_int();
CAPITOLUL 3. IOI 2018 371

228 }
229 std::vector<int> L(Q), R(Q);
230 for (int j = 0; j < Q; ++j)
231 {
232 L[j] = read_int();
233 R[j] = read_int();
234 }
235
236 fclose(stdin);
237 auto t2 = clock();
238
239 std::vector<long long> C = minimum_costs(H, L, R);
240
241 auto t3 = clock();
242
243 for (size_t j = 0; j < C.size(); ++j)
244 {
245 printf("%lld\n", C[j]);
246 }
247
248 auto t4 = clock();
249
250 // reset console output
251 freopen("CON", "w", stdout);
252
253 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
254 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
255 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
256
257 return 0;
258 }
259 /*
260 t2-t1 = 1.205
261 t3-t2 = 11.063
262 t4-t3 = 0.765
263
264 Process returned 0 (0x0) execution time : 13.340 s
265 Press any key to continue.
266 */

Listing 3.6.7: meetings_120908.cpp


1 // https://oj.uz/submission/120908
2 #include<fstream>
3 #include<iostream>
4
5 //#include <ctime>
6 #include <time.h> /* clock */
7
8 #include <stdio.h> // citire mai rapida !!!
9
10 #include <algorithm>
11
12 using namespace std;
13
14 typedef long long ll;
15 typedef pair <int, int> pii;
16
17 struct minseg
18 {
19 pii T[2202020];
20 int sz = 1 << 20;
21
22 void init(vector <int> &H)
23 {
24 int i;
25
26 for(i=0; i<H.size(); i++)
27 {
28 T[i + sz] = pii(H[i], i);
29 }
30
31 for(i=sz-1; i; i--)
32 {
33 T[i] = max(T[i << 1], T[i << 1 | 1]);
CAPITOLUL 3. IOI 2018 372

34 }
35 }
36
37 int getmax(int l, int r)
38 {
39 pii ret(-1, -1);
40
41 l += sz; r += sz;
42 for(; l<=r; )
43 {
44 if(l & 1) ret = max(ret, T[l]);
45 if(~r & 1) ret = max(ret, T[r]);
46 l = l + 1 >> 1;
47 r = r - 1 >> 1;
48 }
49
50 return ret.second;
51 }
52 };
53
54 struct line
55 {
56 ll x, a, b;
57 line() {}
58 line(ll x, ll a, ll b) : x(x), a(a), b(b) {}
59 };
60
61 struct deq
62 {
63 line D[808080];
64 int S[808080], E[808080];
65 ll V[808080];
66 int n;
67 bool t;
68
69 void init(bool _t, int _n)
70 {
71 int i;
72
73 t = _t; n = _n;
74
75 for(i=0; i<n; i++)
76 {
77 S[i] = i; E[i] = i;
78 D[i] = line(i, 0, 0);
79 }
80 }
81
82 void addval(int p, ll v)
83 {
84 if(t) p = n - 1 - p;
85 V[p] += v;
86 }
87
88 void addline(int p, ll x, ll a, ll b)
89 {
90 if(t)
91 {
92 p = n - 1 - p, x = n - 1 - x;
93 b = (n - 1) * a + b; a = -a;
94 }
95
96 int &s = S[p], &e = E[p];
97
98 b -= V[p];
99
100 for(; s<=e; e--)
101 {
102 if(D[e].a == a && D[e].b < b) return;
103 if(D[e].a * D[e].x + D[e].b < a * D[e].x + b)
104 {
105 D[++e] = line((D[e].b - b) / (a - D[e].a) + 1, a, b);
106 return;
107 }
108 }
109
CAPITOLUL 3. IOI 2018 373

110 D[++e] = line(x, a, b);


111 }
112
113 ll getval(int p, ll x)
114 {
115 if(t) p = n - 1 - p, x = n - 1 - x;
116
117 int k = upper_bound(D + S[p], D + E[p] + 1, x, [&](ll x, line l)
118 {
119 return x < l.x;
120 }) - D - 1;
121
122 return D[k].a * x + D[k].b + V[p];
123 }
124
125 void merge(int p, int q)
126 {
127 if(t) p = n - 1 - p, q = n - 1 - q;
128
129 int i;
130
131 if(E[p] - S[p] < E[q] - S[q])
132 {
133 swap(S[p], S[q]); swap(E[p], E[q]);
134 swap(V[p], V[q]);
135 }
136
137 if(S[p] < S[q])
138 {
139 for(i=S[q]; i<=E[q]; i++)
140 {
141 D[i].b += V[q] - V[p];
142 D[++E[p]] = D[i];
143 }
144 }
145 else
146 {
147 for(i=E[q]; i>=S[q]; i--)
148 {
149 D[i].b += V[q] - V[p];
150 D[--S[p]] = D[i];
151 }
152 }
153 }
154 };
155
156 minseg T;
157 deq DL, DR;
158 vector <int> H, L, R;
159 vector <int> Q[808080];
160 vector <ll> A;
161 int n;
162
163 int dnc(int s, int e)
164 {
165 if(s > e) return -1;
166
167 int m, l, r;
168 ll v, h;
169
170 m = T.getmax(s, e); h = H[m];
171 l = dnc(s, m - 1); r = dnc(m + 1, e);
172
173 for(int &q: Q[m])
174 {
175 if(L[q] == m && R[q] == m) A[q] = h;
176 else if(L[q] == m) A[q] = DR.getval(r, R[q]) + h;
177 else if(R[q] == m) A[q] = DL.getval(l, L[q]) + h;
178 else A[q] = min(DL.getval(l, L[q]) + h * (R[q] - m + 1),
179 DR.getval(r, R[q]) + h * (m - L[q] + 1));
180 }
181
182 if(l != -1) DL.merge(m, l);
183 DL.addval(m, h * (e - m + 1));
184 DL.addline(m, s, -h, (r != -1? DL.getval(r, m + 1) : 0) + h * (m+1));
185 if(r != -1) DL.merge(m, r);
CAPITOLUL 3. IOI 2018 374

186
187 if(r != -1) DR.merge(m, r);
188 DR.addval(m, h * (m - s + 1));
189 DR.addline(m, e, h, (l != -1? DR.getval(l, m - 1) : 0) - h * (m-1));
190 if(l != -1) DR.merge(m, l);
191
192 return m;
193 }
194
195 vector <ll> minimum_costs(vector <int> _H, vector <int> _L,
196 vector <int> _R)
197 {
198 int i;
199
200 swap(H, _H);
201 swap(L, _L);
202 swap(R, _R);
203 n = H.size();
204 A.resize(L.size());
205
206 T.init(H);
207 DL.init(0, n);
208 DR.init(1, n);
209
210 for(i=0; i<L.size(); i++)
211 {
212 Q[T.getmax(L[i], R[i])].push_back(i);
213 }
214
215 dnc(0, n - 1);
216
217 return A;
218 }
219
220 //-------------- start grader ------------------
221
222 namespace
223 {
224 int read_int()
225 {
226 int x;
227 if (scanf("%d", &x) != 1)
228 {
229 fprintf(stderr, "Error while reading input\n");
230 exit(1);
231 }
232 return x;
233 }
234 } // namespace
235
236 int main()
237 {
238 std::freopen("../in/05-12.txt", "r", stdin);
239 std::freopen("meetings.out", "w", stdout);
240
241 auto t1 = clock();
242
243 int N = read_int();
244 int Q = read_int();
245 std::vector<int> H(N);
246 for (int i = 0; i < N; ++i)
247 {
248 H[i] = read_int();
249 }
250 std::vector<int> L(Q), R(Q);
251 for (int j = 0; j < Q; ++j)
252 {
253 L[j] = read_int();
254 R[j] = read_int();
255 }
256
257 fclose(stdin);
258 auto t2 = clock();
259
260 std::vector<long long> C = minimum_costs(H, L, R);
261
CAPITOLUL 3. IOI 2018 375

262 auto t3 = clock();


263
264 for (size_t j = 0; j < C.size(); ++j)
265 {
266 printf("%lld\n", C[j]);
267 }
268
269 auto t4 = clock();
270
271 // reset console output
272 freopen("CON", "w", stdout);
273
274 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
275 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
276 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
277
278 return 0;
279 }
280 /*
281 t2-t1 = 4.295
282 t3-t2 = 5.205
283 t4-t3 = 1.148
284
285 Process returned 0 (0x0) execution time : 10.932 s
286 Press any key to continue.
287 */

Listing 3.6.8: meetings_159115.cpp


1 // https://oj.uz/submission/159115 3257 ms
2
3 #include <stdio.h> // citire mai rapida !!!
4
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 struct lichao
10 {
11 int A, l, r;
12 long long B, lazy;
13 lichao() : A(0), l(0), r(0), B(0x3fffffffffffffffLL), lazy(0) {}
14 };
15
16 const int SZ=1<<20;
17 pair<int,int> Mtree[2*SZ];
18
19 lichao ltree[2*SZ], rtree[2*SZ];
20 vector<long long> res;
21 vector<tuple<int,int,int>> Q[750001];
22
23 pair<int,int> get_max(int s, int e)
24 {
25 pair<int,int> ret(-1,-1);
26 for(s+=SZ,e+=SZ;s<=e;s>>=1,e>>=1)
27 {
28 if(s&1) ret=max(ret,Mtree[s++]);
29 if(~e&1) ret=max(ret,Mtree[e--]);
30 }
31 return ret;
32 }
33
34 void lazy_propagation(lichao *tree, int bit, int s, int e)
35 {
36 if(tree[bit].lazy)
37 {
38 tree[bit].B+=tree[bit].lazy;
39 if(s<e)
40 {
41 tree[2*bit].lazy+=tree[bit].lazy;
42 tree[2*bit+1].lazy+=tree[bit].lazy;
43 }
44 tree[bit].lazy=0;
45 }
46 }
CAPITOLUL 3. IOI 2018 376

47
48 int get_sign(long long a)
49 {
50 return a<0 ? -1:a>0;
51 }
52
53 void add_line(lichao *tree, int n1, int n2,
54 int A, long long B, int bit=1, int s=0, int e=SZ-1)
55 {
56 int m=(s+e)>>1;
57 lazy_propagation(tree,bit,s,e);
58 if(n2<n1 || n2<s || e<n1) return;
59 if(n1<=s && e<=n2)
60 {
61 int &pA=tree[bit].A;
62 long long &pB=tree[bit].B, ys=1LL*A*s+B,
63 ym=1LL*A*m+B, ye=1LL*A*e+B,
64 pys=1LL*pA*s+pB, pym=1LL*pA*m+pB,
65 pye=1LL*pA*e+pB;
66 if(ym<pym)
67 {
68 swap(pA,A); swap(pB,B);
69 swap(pys,ys); swap(pym,ym); swap(pye,ye);
70 }
71
72 if(pys<=ys && pye<=ye) return;
73
74 if(get_sign(ys-pys)*get_sign(ym-pym)<0 || ym==pym && ys<pys)
75 add_line(tree,n1,n2,A,B,2*bit,s,m);
76 else
77 add_line(tree,n1,n2,A,B,2*bit+1,m+1,e);
78
79 return;
80 }
81
82 add_line(tree,n1,n2,A,B,2*bit,s,m);
83 add_line(tree,n1,n2,A,B,2*bit+1,m+1,e);
84 }
85
86 void add_tree(lichao *tree, int n1, int n2, long long v,
87 int bit=1, int s=0, int e=SZ-1)
88 {
89 int m=(s+e)>>1;
90 lazy_propagation(tree,bit,s,e);
91 if(n2<n1 || n2<s || e<n1) return;
92 if(n1<=s && e<=n2)
93 {
94 tree[bit].lazy=v;
95 lazy_propagation(tree,bit,s,e);
96 return;
97 }
98 add_tree(tree,n1,n2,v,2*bit,s,m);
99 add_tree(tree,n1,n2,v,2*bit+1,m+1,e);
100 }
101
102 long long get_y(lichao *tree, int x, int bit=1, int s=0, int e=SZ-1)
103 {
104 int m=(s+e)>>1;
105 lazy_propagation(tree,bit,s,e);
106 if(s==e) return 1LL*tree[bit].A*x+tree[bit].B;
107 return min(x<=m ? get_y(tree,x,2*bit,s,m):
108 get_y(tree,x,2*bit+1,m+1,e),1LL*tree[bit].A*x+tree[bit].B);
109 }
110
111 void solve(int s, int e)
112 {
113 if(s>e) return;
114 auto[M,m]=get_max(s,e);
115 solve(s,m-1);
116 solve(m+1,e);
117 for(auto[l,r,i]: Q[m])
118 res[i]=min(get_y(ltree,l)+(r-m+1LL)*M,get_y(rtree,r)+(m-l+1LL)*M);
119 add_tree(ltree,s,m-1,(e-m+1LL)*M);
120 add_line(ltree,s,m,-M,(m<e ? get_y(ltree,m+1):0)+M*(m+1LL));
121 add_tree(rtree,m+1,e,(m-s+1LL)*M);
122 add_line(rtree,m,e,M,(s<m ? get_y(rtree,m-1):0)+M*(1LL-m));
CAPITOLUL 3. IOI 2018 377

123 }
124
125 vector<long long> minimum_costs(vector<int> H,
126 vector<int> L, vector<int> R)
127 {
128 int N=H.size(), M=L.size();
129 res.resize(M);
130 for(int i=0;i<N;i++) Mtree[SZ+i+1]={H[i],i+1};
131 for(int i=SZ;--i;) Mtree[i]=max(Mtree[2*i],Mtree[2*i+1]);
132 for(int i=0;i<M;i++)
133 {
134 if(++L[i]<++R[i])
135 Q[get_max(L[i],R[i]).second].emplace_back(L[i],R[i],i);
136 else
137 res[i]=H[L[i]-1];
138 }
139
140 solve(1,N);
141 return res;
142 }
143
144 //-------------- start grader ------------------
145
146 namespace
147 {
148 int read_int()
149 {
150 int x;
151 if (scanf("%d", &x) != 1)
152 {
153 fprintf(stderr, "Error while reading input\n");
154 exit(1);
155 }
156 return x;
157 }
158 } // namespace
159
160 int main()
161 {
162 auto t1 = clock();
163
164 std::freopen("../in/05-12.txt", "r", stdin);
165 std::freopen("meetings.out", "w", stdout);
166
167 int N = read_int();
168 int Q = read_int();
169 std::vector<int> H(N);
170 for (int i = 0; i < N; ++i)
171 {
172 H[i] = read_int();
173 }
174 std::vector<int> L(Q), R(Q);
175 for (int j = 0; j < Q; ++j)
176 {
177 L[j] = read_int();
178 R[j] = read_int();
179 }
180
181 fclose(stdin);
182 auto t2 = clock();
183
184 std::vector<long long> C = minimum_costs(H, L, R);
185
186 auto t3 = clock();
187
188 for (size_t j = 0; j < C.size(); ++j)
189 {
190 printf("%lld\n", C[j]);
191 }
192
193 auto t4 = clock();
194
195 // reset console output
196 freopen("CON", "w", stdout);
197
198 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 3. IOI 2018 378

199 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
200 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
201
202 return 0;
203 }
204 /*
205 t2-t1 = 1.109
206 t3-t2 = 11.448
207 t4-t3 = 0.75
208
209 Process returned 0 (0x0) execution time : 13.730 s
210 Press any key to continue.
211 */

https://cp-algorithms.com/geometry/convex_hull_trick.html

3.6.3 *Rezolvare detaliat 


Capitolul 4

IOI 201727

4.1 Nowruz
Problema 1 - Nowruz 100 de puncte
Authors: Monika Steinová (Switzerland) and Michal Forisek (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, ecare 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 ecare 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 ecare 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 ³iere de intrare, ecare
dintre ele descriind o gr din  a bunicului. Pentru ecare ³ier de intrare trebuie s  submitaµi un
³ier de ie³ire, care s  conµin  o hart  a labirintului. Pentru ecare ³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 ³ier de intrare va descrie o matrice reprezentând gr dina ³i num rul de copii k invitaµi
de bunic. Formatul ³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
27
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).

379
CAPITOLUL 4. IOI 2017 380

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  e majuscul .)
CAPITOLUL 4. IOI 2017 381

Restricµii
a 1 & m, n & 1024
Punctaje
Fi³ierul output se consider  a  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  transformate în caractere 'X' (celule blocate cu
arbu³ti).

a Harta de ie³ire va avea proprietatea de labirint, dup  cum acesta a fost denit în enunµul
problemei.

Dac  ³ierul output pentru un test nu va  valid, scorul acordat pentru acest test va  0. în
caz contrar, scorul va  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 ind
num rul dat în ³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 ecare 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 a³at în CMS va  '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 ecare 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 4. IOI 2017 382

4.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 4. IOI 2017 383

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.

4.1.2 Coduri surs 

Listing 4.1.1: nowruz.cpp


1 // http://ioi2017.org/tasks/materials/nowruz.zip ---> sandbox/nowruz.cpp
2 // http://ioi2017.org/tasks/materials/nowruz.zip ---> solution/***.cpp
3
4 #include <vector>
5 #include <set>
6 #include <string>
7 #include <queue>
CAPITOLUL 4. IOI 2017 384

8 #include <iostream>
9
10 #include <fstream>
11
12 using namespace std;
13
14 ifstream fin("../tests/02.in");
15 ofstream fout("02.out.txt");
16
17 #define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)
18 #define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
19 #define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
20 #define sz(x) (int((x).size()))
21 #define pb push_back
22 #define all(X) (X).begin(),(X).end()
23 #define X first
24 #define Y second
25 //#define endl ’\n’
26
27 template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }
28 template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }
29
30 typedef long long ll;
31 typedef pair<int, int> pii;
32
33 ////////////////////////////////////////////////////////////////////////////
34
35 struct solver
36 {
37 const int dx[4] = {0, -1, 0, 1};
38 const int dy[4] = {-1, 0, 1, 0};
39
40 static const int maxn = 2048 + 64;
41
42 static const char BLOCK = ’#’;
43 static const char OPEN = ’.’;
44 static const char TREE = ’X’;
45
46 int n, m;
47 string grid[maxn];
48
49 set<pair<int, pii>> s;
50
51 inline bool inside(pii p)
52 {
53 return 0 <= p.X && p.X < n && 0 <= p.Y && p.Y < m;
54 }
55
56 inline bool open(pii p)
57 {
58 return inside(p) && grid[p.X][p.Y] == OPEN;
59 }
60
61 inline pii adj(pii p, int d)
62 {
63 return pii(p.X + dx[d], p.Y + dy[d]);
64 }
65
66 int around(pii p, char ch)
67 { // counts number of ch’s around p
68 int cnt = 0;
69 rep(d, 4)
70 if(inside(adj(p, d)) && grid[p.X + dx[d]][p.Y + dy[d]] == ch)
71 cnt++;
72 else
73 if(!inside(adj(p, d)) && ch == BLOCK)
74 cnt++;
75 return cnt;
76 }
77
78 int extend(pii p, bool act)
79 { // act: determines whether to apply it or not
80 if(grid[p.X][p.Y] != OPEN || around(p, TREE) != 1)
81 return -1;
82
83 int cnt = 0;
CAPITOLUL 4. IOI 2017 385

84 rep(d, 4) if(open(adj(p, d)))


85 {
86 if(around(adj(p, d), TREE) == 0)
87 {
88 if(act) add(adj(p, d));
89 cnt++;
90 }
91 }
92
93 if(act) grid[p.X][p.Y] = TREE;
94
95 return cnt;
96 }
97
98 void add(pii p)
99 { // add node p and extension options around p
100 grid[p.X][p.Y] = TREE;
101 rep(d, 4)
102 if(open(pii(p.X + dx[d], p.Y + dy[d])))
103 {
104 int cnt = extend(pii(p.X + dx[d], p.Y + dy[d]), 0);
105 if(cnt != -1)
106 s.insert({cnt, pii(p.X + dx[d], p.Y + dy[d])});
107 }
108 }
109
110 vector <int> mark[maxn];
111 int vmark = 727;
112 queue <pii> q;
113
114 int cnt_size(int x, int y)
115 {
116 int size = 1;
117 vmark++;
118 mark[x][y] = vmark;
119 q.push(pii(x, y));
120
121 while(!q.empty())
122 {
123 pii p = q.front();
124 q.pop();
125 rep(dir, 4)
126 if(open(adj(p, dir)) &&
127 mark[p.X + dx[dir]][p.Y + dy[dir]] != vmark)
128 mark[p.X + dx[dir]][p.Y + dy[dir]] = vmark,
129 q.push(adj(p, dir)),
130 size++;
131 }
132
133 return size;
134 }
135
136 void init()
137 {
138 rep(x, n) mark[x].resize(maxn, 0);
139 pii start;
140 int best_cnt = -1;
141 rep(x, n) rep(y, m) if(!mark[x][y] && grid[x][y] == OPEN)
142 {
143 int cnt = cnt_size(x, y);
144 if(cnt > best_cnt)
145 best_cnt = cnt, start = pii(x, y);
146 }
147
148 add(start);
149 }
150
151 bool is_path;
152 int dfs(int x, int y, bool st = true)
153 {
154 if(!open(pii(x, y)) ||
155 (!st && around(pii(x, y), TREE) > 0) ||
156 mark[x][y] == vmark)
157 return 0;
158
159 mark[x][y] = vmark;
CAPITOLUL 4. IOI 2017 386

160 int res = 1;


161 int cnt = 0;
162 rep(dir, 4)
163 {
164 int ores = res;
165 res += dfs(x + dx[dir], y + dy[dir], false);
166 if(res > ores) cnt++;
167 }
168
169 if(cnt >= 2) is_path = false;
170 return res;
171 }
172
173 void solve()
174 {
175 init();
176
177 while(true)
178 {
179 while(!s.empty())
180 {
181 auto t = *s.rbegin();
182 s.erase( *s.rbegin());
183
184 int cnt = extend(t.second, 0);
185
186 if(cnt == t.first)
187 extend(t.second, 1);
188 else if(cnt != -1)
189 s.insert({cnt, t.second});
190 }
191
192 bool changed = false;
193
194 rep(x, n)
195 if(!changed)
196 rep(y, m)
197 if(!changed &&
198 grid[x][y] == OPEN &&
199 around(pii(x, y), TREE) == 1)
200 {
201 add(pii(x, y));
202 changed = true;
203 }
204
205 rep(x, n)
206 if(!changed)
207 rep(y, m)
208 if(!changed &&
209 grid[x][y] == OPEN &&
210 around(pii(x, y), TREE) == 2)
211 {
212 int cnt = 0;
213 rep(dir, 4)
214 {
215 pii q = adj(pii(x, y), dir);
216 if(inside(q) &&
217 grid[q.X][q.Y] == OPEN &&
218 around(q, TREE) == 0)
219 cnt++;
220 }
221
222 if(cnt < 2) continue;
223
224 rep(dir, 4)
225 {
226 pii q = adj(pii(x, y), dir);
227 if(inside(q) &&
228 grid[q.X][q.Y] == TREE &&
229 around(q, TREE) == 1)
230 {
231 grid[q.X][q.Y] = OPEN;
232 add(pii(x, y));
233 changed = true;
234 break;
235 }
CAPITOLUL 4. IOI 2017 387

236 }
237 }
238
239 rep(x, n)
240 if(!changed)
241 rep(y, m)
242 if(!changed && grid[x][y] == OPEN && around(pii(x, y), TREE) == 2)
243 {
244 vmark++; is_path = true;
245 int size = dfs(x, y);
246
247 int cnt = 0;
248 rep(dir, 4)
249 {
250 pii q = adj(pii(x, y), dir);
251 if(inside(q) &&
252 grid[q.X][q.Y] == TREE &&
253 around(q, TREE) == 1)
254 cnt++;
255 }
256
257 if((cnt == 2 && size < 7) || (size < 4)) continue;
258
259 rep(dir, 4)
260 {
261 pii q = adj(pii(x, y), dir);
262 if(inside(q) &&
263 grid[q.X][q.Y] == TREE &&
264 around(q, TREE) == 1)
265 {
266 grid[q.X][q.Y] = OPEN;
267 add(pii(x, y));
268 changed = true;
269 break;
270 }
271 }
272 }
273
274 if(!changed) break;
275 }
276 }
277
278 void read()
279 {
280 fin >> n >> m; // ***
281 int tmp;
282 fin >> tmp; // ***
283 rep(x, n) fin >> grid[x]; // ***
284 }
285
286 void write()
287 {
288 rep(x, n)
289 rep(y, m)
290 if(grid[x][y] == ’.’ || grid[x][y] == ’X’)
291 grid[x][y] ^= ’X’ ^ ’.’;
292
293 rep(x, n) cout << grid[x] << endl;
294 rep(x, n) fout << grid[x] << endl;
295 }
296 };
297
298 int main()
299 {
300 ios_base::sync_with_stdio(false);
301 fin.tie(0);
302
303 solver f;
304
305 f.read();
306 f.solve();
307 f.write();
308
309 return 0;
310 }
CAPITOLUL 4. IOI 2017 388

Listing 4.1.2: checker.cpp


1 #include <algorithm>
2 #include <queue>
3 #include <cmath>
4
5 #include<iostream>
6
7 #include "testlib.h"
8
9 #define MAX (1024 + 64)
10
11 using namespace std;
12
13 int m, n, leaves, answer;
14 std::vector <std::string> maze;
15 bool mark[MAX][MAX];
16 int rplus[4] = {1, -1, 0, 0}, cplus[4] = {0, 0, 1, -1};
17
18 struct node
19 {
20 node(int _r, int _c, int _pr, int _pc)
21 {
22 r = _r; c = _c; pr = _pr; pc = _pc;
23 }
24 int r, c, pr, pc;
25 };
26
27 bool inside(int r, int c)
28 {
29 return r >= 0 && r < m && c >= 0 && c < n;
30 }
31
32 bool leaf(int r, int c)
33 {
34 int adj = 0;
35 for (int i = 0; i < 4; i++)
36 if (inside(r + rplus[i], c + cplus[i]) &&
37 maze[r + rplus[i]][c + cplus[i]] == ’.’)
38 adj++;
39 return adj == 1;
40 }
41
42 void BFS(int r, int c)
43 {
44 queue <node> Q;
45 Q.push(node(r, c, -1, -1));
46 mark[r][c] = true;
47 while (! Q.empty())
48 {
49 node v = Q.front();
50 Q.pop();
51 if (leaf(v.r, v.c)) leaves++;
52 for (int i = 0; i < 4; i++)
53 {
54 int a = v.r + rplus[i], b = v.c + cplus[i];
55 if (inside(a, b) && maze[a][b] == ’.’ && (a != v.pr || b != v.pc))
56 {
57 if (mark[a][b]) quitf(_wa, "Output has cycle");
58 Q.push(node(a, b, v.r, v.c));
59 mark[a][b] = true;
60 }
61 }
62 }
63 }
64
65 int main()
66 {
67 int argc=4;
68
69 char* argv[] =
70 {
71 (char*)"checker",
72 (char*)"../tests/02.in", // input
73 (char*)"../tests/02.out", // rezultat corect
74 (char*)"02.out.txt", // rezultat de verificat si acordat punctaj
CAPITOLUL 4. IOI 2017 389

75 };
76
77 cout<<"argc = "<<argc<<"\n";
78 for(int kk=0;kk<argc;kk++)
79 cout<<argv[kk]<<"\n";
80 cout<<"----------------------\n";
81
82 registerChecker("nowruz", argc, argv);
83
84 m = inf.readInt();
85 inf.readSpace();
86
87 n = inf.readInt();
88 inf.readSpace();
89
90 answer = inf.readInt();
91 inf.readEoln();
92
93 // Read input and output mazes, and comparing if they match
94 for (int i = 0; i < m; i++)
95 {
96 string input = inf.readLine("[.#]{" + std::to_string(n) + "," +
97 std::to_string(n) + "}");
98 maze.push_back(ouf.readLine("[.#X]{" + std::to_string(n) + "," +
99 std::to_string(n) + "}"));
100 for (int j = 0; j < n; j++)
101 if (input[j] != maze[i][j] &&
102 (input[j] != ’.’ || maze[i][j] != ’X’))
103 quitf(_wa,
104 "Input and output maps are different at cell [%d, %d]",
105 i, j);
106 }
107
108 // Check if output maze is a tree, and count its leaves
109 int component = 0;
110 for (int i = 0; i < m; i++)
111 for (int j = 0; j < n; j++)
112 if (maze[i][j] == ’.’ && ! mark[i][j])
113 {
114 if (component++ > 0)
115 quitf(_wa, "Output maze is not connected");
116 BFS(i, j);
117 }
118
119 double score = int(leaves / (double)answer * 1000) / 1000.0;
120
121 if (abs(score) < 1e-5)
122 quitf(_wa, "Your rounded-down score is 0");
123
124 if (score < 1 - 1e-5)
125 quitp(score, "number of leaves: %d/%d", leaves, answer);
126
127 quitf(_ok, "number of leaves: %d/%d", leaves, answer);
128 }
129 /*
130 argc = 4
131 checker
132 ../tests/02.in
133 ../tests/02.out
134 02.out.txt
135 ----------------------
136 1
137 Correct
138 number of leaves: 1348/1338
139
140 Process returned 0 (0x0) execution time : 0.047 s
141 Press any key to continue.
142 */

28
OBS: Este folosit  biblioteca testlib.h

Listing 4.1.3: addleaves.cpp


1 #include <iostream>
28
http://code.google.com/p/testlib/
CAPITOLUL 4. IOI 2017 390

2 #include <string>
3 #include <vector>
4
5 #include<ctime>
6
7 using namespace std;
8
9 vector <string> table;
10 int m, n, k;
11 int xplus[4] = {0, 1, 0, -1}, yplus[4] = {1, 0, -1, 0};
12
13 int cnt_neighbors(int x, int y)
14 {
15 int cnt = 0;
16 for (int i = 0; i < 4; i++)
17 if (table[x+xplus[i]][y+yplus[i]] == ’X’) cnt++;
18 return cnt;
19 }
20
21 bool addleaves(int x, int y)
22 {
23 bool found = false;
24 for (int i = 0; i < m; i++)
25 for (int j = 0; j < n; j++)
26 {
27 int a = 1 + (x + i) % m, b = 1 + (y + j) % n;
28 if (table[a][b] == ’.’ && cnt_neighbors(a, b) == 1)
29 {
30 table[a][b] = ’X’;
31 found = true;
32 }
33 }
34 return found;
35 }
36
37 int main()
38 {
39 auto t1 = clock();
40
41 // redirect console input to a file
42 std::freopen("../tests/07.in", "r", stdin) ;
43
44 // redirect console output to a file
45 std::freopen("07.out.txt", "w", stdout) ;
46
47 ios_base::sync_with_stdio(false); cin.tie(0);
48
49 cin >> m >> n >> k;
50 string s(n, ’#’);
51 table.push_back(s);
52 for (int i = 0; i < m; i++)
53 {
54 cin >> s;
55 table.push_back(’#’ + s + ’#’);
56 }
57 table.push_back(string(n, ’#’));
58
59 auto t2 = clock();
60
61 bool found = false;
62 for (int i = 2; i < m + 1 && ! found; i++)
63 for (int j = 2; j < n + 1 && ! found; j++)
64 if (table[i][j] == ’.’)
65 {
66 table[i][j] = ’X’;
67 addleaves(i, j);
68 found = true;
69 }
70
71 for(; addleaves(1,1););
72
73 for (int i = 1; i < m + 1; i++)
74 for (int j = 1; j < n + 1; j++)
75 if (table[i][j] == ’.’ || table[i][j] == ’X’)
76 table[i][j] = ’.’ + ’X’ - table[i][j];
77
CAPITOLUL 4. IOI 2017 391

78 auto t3 = clock();
79
80 for (int i = 1; i < m + 1; i++)
81 cout << table[i].substr(1, n) << endl;
82
83 auto t4 = clock();
84
85 // reset console output
86 freopen("CON", "w", stdout);
87
88 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
89 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
90 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
91
92 return 0;
93 }
94 /*
95 t2-t1 = 0
96 t3-t2 = 0.796
97 t4-t3 = 0
98
99 Process returned 0 (0x0) execution time : 0.844 s
100 Press any key to continue.
101
102 argc = 4
103 checker
104 ../tests/07.in
105 ../tests/07.out
106 07.out.txt
107 ----------------------
108 0.828
109 Partially Correct
110 number of leaves: 27639/33363
111
112 Process returned 0 (0x0) execution time : 0.203 s
113 Press any key to continue.
114 */

Listing 4.1.4: addleaves_once.cpp


1 #include <iostream>
2 #include <string>
3 #include <vector>
4
5 #include<ctime>
6
7 using namespace std;
8
9 vector <string> table;
10 int m, n, k;
11 int xplus[4] = {0, 1, 0, -1}, yplus[4] = {1, 0, -1, 0};
12
13 int cnt_neighbors(int x, int y)
14 {
15 int cnt = 0;
16 for (int i = 0; i < 4; i++)
17 if (table[x+xplus[i]][y+yplus[i]] == ’X’) cnt++;
18 return cnt;
19 }
20
21 bool addleaves(int x, int y)
22 {
23 bool found = false;
24 for (int i = 0; i < m; i++)
25 for (int j = 0; j < n; j++)
26 {
27 int a = 1 + (x + i) % m, b = 1 + (y + j) % n;
28 if (table[a][b] == ’.’ && cnt_neighbors(a, b) == 1)
29 {
30 table[a][b] = ’X’;
31 found = true;
32 }
33 }
34 return found;
35 }
CAPITOLUL 4. IOI 2017 392

36
37 int main()
38 {
39 auto t1 = clock();
40
41 // redirect console input to a file
42 std::freopen("../tests/07.in", "r", stdin) ;
43
44 // redirect console output to a file
45 std::freopen("07.out.txt", "w", stdout) ;
46
47 ios_base::sync_with_stdio(false); cin.tie(0);
48
49 cin >> m >> n >> k;
50 string s(n, ’#’);
51 table.push_back(s);
52 for (int i = 0; i < m; i++) {
53 cin >> s;
54 table.push_back(’#’ + s + ’#’);
55 }
56 table.push_back(string(n, ’#’));
57
58 auto t2 = clock();
59
60 bool found = false;
61 for (int i = 2; i < m + 1 && ! found; i++)
62 for (int j = 2; j < n + 1 && ! found; j++)
63 if (table[i][j] == ’.’)
64 {
65 table[i][j] = ’X’;
66 addleaves(i, j);
67 found = true;
68 }
69
70 addleaves(0,0);
71
72 for (int i = 1; i < m + 1; i++)
73 for (int j = 1; j < n + 1; j++)
74 if (table[i][j] == ’.’ || table[i][j] == ’X’)
75 table[i][j] = ’.’ + ’X’ - table[i][j];
76
77 auto t3 = clock();
78
79 for (int i = 1; i < m + 1; i++)
80 cout << table[i].substr(1, n) << endl;
81
82 auto t4 = clock();
83
84 // reset console output
85 freopen("CON", "w", stdout);
86
87 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
88 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
89 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
90
91 return 0;
92 }
93 /*
94 t2-t1 = 0.015
95 t3-t2 = 0.047
96 t4-t3 = 0
97
98 Process returned 0 (0x0) execution time : 0.109 s
99 Press any key to continue.
100
101 argc = 4
102 checker
103 ../tests/07.in
104 ../tests/07.out
105 07.out.txt
106 ----------------------
107 0.080
108 Partially Correct
109 number of leaves: 2687/33363
110
111 Process returned 0 (0x0) execution time : 0.141 s
CAPITOLUL 4. IOI 2017 393

112 Press any key to continue.


113 */

Listing 4.1.5: addleaves_rand.cpp


1 #include <iostream>
2 #include <string>
3 #include <vector>
4
5 #include<ctime>
6
7 #define For(a, b) for (int a = 0; a < b; a++)
8 #define max_tries (10)
9
10 using namespace std;
11
12 #define ipair pair <int, int>
13
14 vector <string> table, first, best;
15 int m, n, k, best_num;
16 int xplus[4] = {0, 1, 0, -1}, yplus[4] = {1, 0, -1, 0};
17 vector < ipair > allcells;
18
19 bool inside(int a, int b)
20 {
21 return (a >= 0 && a < m && b >= 0 && b < n);
22 }
23
24 int neighbors(int x, int y)
25 {
26 int count = 0;
27 For(i, 4)
28 {
29 int a = x + xplus[i], b = y + yplus[i];
30 if (inside(a, b) && table[a][b] == ’X’) count ++;
31 }
32 return count;
33 }
34
35 int count_leaves()
36 {
37 int c = 0;
38 For(i, m)
39 For(j, n)
40 if (table[i][j] == ’X’ && neighbors(i, j) == 1) c++;
41 return c;
42 }
43
44 bool check_leaf(int x, int y)
45 {
46 if (table[x][y] == ’.’ && neighbors(x, y) == 1)
47 {
48 table[x][y] = ’X’;
49 return true;
50 }
51 return false;
52 }
53
54 bool break_tie(int x, int y)
55 {
56 if (table[x][y] != ’.’) return false;
57 bool leaf[4] = {0, 0, 0, 0};
58 int lnum = 0;
59 For(i, 4)
60 {
61 int a = x + xplus[i], b = y + yplus[i];
62 if (table[a][b] == ’X’)
63 {
64 if (neighbors(a, b) != 1) return false;
65 leaf[i] = true;
66 lnum++;
67 }
68 else
69 if (neighbors(a, b) != 0)
70 return false;
CAPITOLUL 4. IOI 2017 394

71 }
72
73 if (lnum != 2) return false;
74 table[x][y] = ’X’;
75 For(i, 4)
76 {
77 if (leaf[i])
78 {
79 table[x + xplus[i]][y+yplus[i]] = ’.’;
80 //cerr << "Tie found" << endl;
81 return true;
82 }
83 }
84 return false; // !!!
85 }
86
87 // dir = 0 : forward, 1: backward, 2: random
88 bool add_leaves(int x, int y, int dir)
89 {
90 bool found = false;
91 if (dir == 2)
92 {
93 for (int i = 0; i < m * n; i++)
94 allcells[i] = allcells[rand() % (i+1)];
95
96 for (int i = 0; i < m * n; i++)
97 found |= check_leaf(allcells[i].first, allcells[i].second);
98 }
99 else
100 For(i, m)
101 For(j, n)
102 {
103 int a = (dir) ? (m + x - i) % m : (i+x) % m;
104 int b = (dir) ? (n + y - j) % n : (j+y) % n;
105 found |= check_leaf(a, b);
106 }
107
108 if (! found)
109 for (int i = 1; i < m - 1; i++)
110 for (int j = 1; j < n - 1; j++)
111 found |= break_tie(i, j);
112
113 return found;
114 }
115
116 void try_add_leaves(int x, int y, bool random)
117 {
118 For(i, m)
119 For(j, n)
120 {
121 int a = (i + x) % m, b = (j + y) % n;
122 if (table[a][b] == ’.’)
123 {
124 table[a][b] = ’X’;
125 if (random) for (;add_leaves(0, 0, 2););
126 else for (int l = 0; add_leaves(a, b, l % 2); l++);
127 int cur_num = count_leaves();
128 if (cur_num > best_num)
129 {
130 best = table;
131 best_num = cur_num;
132 //cerr << best_num << endl;
133 }
134 table = first;
135 return;
136 }
137 }
138 }
139
140 int main()
141 {
142 auto t1 = clock();
143
144 // redirect console input to a file
145 std::freopen("../tests/07.in", "r", stdin) ;
146
CAPITOLUL 4. IOI 2017 395

147 // redirect console output to a file


148 std::freopen("07.out.txt", "w", stdout) ;
149
150 ios_base::sync_with_stdio(false); cin.tie(0);
151
152 cin >> m >> n >> k;
153 string s;
154 For(i, m)
155 {
156 cin >> s;
157 table.push_back(s);
158 For(j, n)
159 allcells.push_back(ipair(i, j));
160 }
161
162 auto t2 = clock();
163
164 first = table;
165 for (int i = 0; i < 3; i++)
166 for (int j = 0; j < 3; j++)
167 try_add_leaves(i, j, false);
168
169 for (int tries = 0; tries < max_tries; tries++)
170 {
171 try_add_leaves(rand() % m, rand() % n, false);
172 try_add_leaves(rand() % m, rand() % n, true);
173 }
174
175 For(i, m)
176 For (j, n)
177 if (best[i][j] == ’.’ || best[i][j] == ’X’)
178 best[i][j] = ’.’ + ’X’ - best[i][j];
179 auto t3 = clock();
180
181 For (i, m)
182 cout << best[i] << endl;
183
184 auto t4 = clock();
185
186 // reset console output
187 freopen("CON", "w", stdout);
188
189 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
190 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
191 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
192
193 return 0;
194 }
195 /*
196 t2-t1 = 0.031
197 t3-t2 = 8.369
198 t4-t3 = 0
199
200 Process returned 0 (0x0) execution time : 8.447 s
201 Press any key to continue.
202
203 argc = 4
204 checker
205 ../tests/07.in
206 ../tests/07.out
207 07.out.txt
208 ----------------------
209 0.796
210 Partially Correct
211 number of leaves: 26588/33363
212
213 Process returned 0 (0x0) execution time : 0.203 s
214 Press any key to continue.
215 */

Listing 4.1.6: dfs.cpp


1 #include <iostream>
2 #include <vector>
3 #include <string>
CAPITOLUL 4. IOI 2017 396

4 #include <bitset>
5 #include <algorithm>
6
7 #include<ctime>
8
9 #define For(a, b) for (int a = 0; a < b; a++)
10 #define MAX (2048 + 64)
11 const int rplus[4] = {0,0,1,-1}, cplus[4] = {-1, 1, 0, 0};
12
13 using namespace std;
14
15 int m, n;
16 double leaf_prob = 0.2;
17 vector <string> table;
18 int counter;
19
20 void rand_perm(int* p)
21 {
22 for (int i = 0; i < 4; i++)
23 {
24 p[i] = i;
25 swap(p[i], p[rand() % (i + 1)]);
26 }
27 }
28
29 bool check2(int r, int c)
30 {
31 return (r < 0 || r >= m || c < 0 || c >= n || table[r][c] != ’X’);
32 }
33
34 bool check(int pr, int pc, int r, int c)
35 {
36 if (r < 0 || r >= m || c < 0 || c >= n || table[r][c] != ’.’)
37 return false;
38 For(i, 4)
39 if ((r + rplus[i] != pr || c + cplus[i] != pc) &&
40 ! check2(r + rplus[i], c + cplus[i]))
41 return false;
42 return true;
43 }
44
45 void DFS(int r, int c)
46 {
47 table[r][c] = ’X’;
48 if (counter++ > 10 && ((double) rand() / RAND_MAX) < leaf_prob)
49 return;
50 int p[4];
51 rand_perm(p);
52 For(i, 4)
53 if (check(r, c, r + rplus[p[i]], c + cplus[p[i]]))
54 DFS(r + rplus[p[i]], c + cplus[p[i]]);
55 }
56
57 bool inside(int a, int b)
58 {
59 return (a >= 0 && a < m && b >= 0 && b < n);
60 }
61
62 int neighbors(int x, int y)
63 {
64 int count = 0;
65 For(i, 4)
66 {
67 int a = x + rplus[i], b = y + cplus[i];
68 if (inside(a, b) && table[a][b] == ’X’) count ++;
69 }
70 return count;
71 }
72
73 void add_leaves() {
74 For(i, m)
75 For(j, n)
76 if (table[i][j] == ’.’ && neighbors(i, j) == 1) table[i][j] = ’X’;
77 }
78
79 int main(int argc, char ** argv)
CAPITOLUL 4. IOI 2017 397

80 {
81 auto t1 = clock();
82
83 // redirect console input to a file
84 std::freopen("../tests/07.in", "r", stdin) ;
85
86 // redirect console output to a file
87 std::freopen("07.out.txt", "w", stdout) ;
88
89 ios_base::sync_with_stdio(false); cin.tie(0);
90
91 // leaf_prob = atof(argv[1]);
92 // cerr << leaf_prob << endl;
93 cin >> m >> n;
94 int tmp; cin >> tmp;
95 string s;
96 int blocks = 0;
97 For(i, m)
98 {
99 cin >> s;
100 table.push_back(s);
101 blocks += count(s.begin(), s.end(), ’#’);
102 }
103
104 auto t2 = clock();
105
106 leaf_prob -= (double) blocks / (m * n);
107 bool found = false;
108 for (int i = 0; i < m && ! found; i++)
109 For(j, n)
110 if (table[i][j] == ’.’)
111 {
112 DFS(i, j);
113 found = true;
114 break;
115 }
116
117 add_leaves();
118 For(i, m)
119 For (j, n)
120 if (table[i][j] == ’.’ || table[i][j] == ’X’)
121 table[i][j] = ’.’ + ’X’ - table[i][j];
122
123 auto t3 = clock();
124
125 For (i, m)
126 cout << table[i] << endl;
127
128 auto t4 = clock();
129
130 // reset console output
131 freopen("CON", "w", stdout);
132
133 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
134 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
135 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
136
137 return 0;
138 }
139 /*
140 t2-t1 = 0.015
141 t3-t2 = 0.078
142 t4-t3 = 0
143
144 Process returned 0 (0x0) execution time : 0.141 s
145 Press any key to continue.
146
147 argc = 4
148 checker
149 ../tests/07.in
150 ../tests/07.out
151 07.out.txt
152 ----------------------
153 0.497
154 Partially Correct
155 number of leaves: 16602/33363
CAPITOLUL 4. IOI 2017 398

156
157 Process returned 0 (0x0) execution time : 0.188 s
158 Press any key to continue.
159 */

Listing 4.1.7: haircomb.cpp


1 #include <iostream>
2 #include <vector>
3 #include <string>
4
5 #include<ctime>
6
7 #define For(a, b) for (int a = 0; a < b; a++)
8
9 using namespace std;
10
11 int m, n, k;
12 vector <string> table;
13
14 int main(int argc, char ** argv)
15 {
16 auto t1 = clock();
17
18 // redirect console input to a file
19 std::freopen("../tests/07.in", "r", stdin) ;
20
21 // redirect console output to a file
22 std::freopen("07.out.txt", "w", stdout) ;
23
24 ios_base::sync_with_stdio(false); cin.tie(0);
25
26 cin >> m >> n >> k;
27
28 string s;
29 For(i, m)
30 {
31 cin >> s;
32 table.push_back(s);
33 }
34
35 auto t2 = clock();
36
37 int i = 0, j;
38 for (; i < n && table[0][i] != ’.’; i++);
39
40 for (j = i; j < n && table[0][j] == ’.’; table[0][j++] = ’X’)
41 if (j % 3 == 0)
42 for (int k = 1; k < m; k++)
43 {
44 if (table[k][j] == ’.’)
45 table[k][j] = ’X’;
46 else
47 break;
48
49 if (k > 1 && (j + k) % 2 == 0)
50 {
51 if (j > 1 && table[k][j-1] == ’.’)
52 table[k][j-1] = ’X’;
53 if (j < n - 1 && table[k][j+1] == ’.’)
54 table[k][j+1] = ’X’;
55 }
56 }
57
58 For(i, m)
59 For (j, n)
60 if (table[i][j] == ’.’ || table[i][j] == ’X’)
61 table[i][j] = ’.’ + ’X’ - table[i][j];
62
63 auto t3 = clock();
64
65 For (i, m)
66 cout << table[i] << endl;
67
68 auto t4 = clock();
CAPITOLUL 4. IOI 2017 399

69
70 // reset console output
71 freopen("CON", "w", stdout);
72
73 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
74 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
75 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
76
77 return 0;
78 }
79 /*
80 t2-t1 = 0.015
81 t3-t2 = 0
82 t4-t3 = 0.016
83
84 Process returned 0 (0x0) execution time : 0.063 s
85 Press any key to continue.
86
87 argc = 4
88 checker
89 ../tests/07.in
90 ../tests/07.out
91 07.out.txt
92 ----------------------
93 0.001
94 Partially Correct
95 number of leaves: 47/33363
96
97 Process returned 0 (0x0) execution time : 0.141 s
98 Press any key to continue.
99 */

Listing 4.1.8: nowruz1.cpp


1 /*
2 https://github.com/KieranHorgan/Competitive-Programming/blob/master/
3 IOI/2017/nowruz/nowruz.cpp
4 */
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 ifstream fin("../tests/02.in");
11 ofstream fout("02.out.txt");
12
13 #define ll long long
14 #define ld long double
15 #define endl ’\n’
16
17 ll n, m, k;
18
19 char grid[1200][1200];
20 char grid2[1200][1200];
21 char bestGrid[1200][1200];
22 ll visited[1200][1200];
23 ll printed[1200];
24
25 ld bestScore, score, lastDone;
26
27 int main()
28 {
29 //ios_base::sync_with_stdio(0); cin.tie(NULL);
30 //cerr << setprecision(5) << endl;
31
32 fin >> n >> m >> k;
33 cout<<n<<" "<<m<<" "<<k<<"\n";
34
35 for(int i = 1; i <= n; i++)
36 {
37 for(int j = 1; j <= m; j++)
38 {
39 fin >> grid[i][j];
40 }
41 }
CAPITOLUL 4. IOI 2017 400

42
43 for(int i = 0; i <= n+1-1000+1000; i++)
44 {
45 for(int j = 0; j <= m+1-1000+1000; j++)
46 {
47 if(i==0||i==n+1-1000+1000||j==0||j==m+1-1000+1000)
48 grid[i][j] = ’#’;
49 if(grid[i][j] != ’#’)
50 grid[i][j] = ’X’;
51 }
52 }
53
54 for(int i = 0; i <= n+1; i++)
55 for(int j = 0; j <= m+1; j++)
56 grid2[i][j] = grid[i][j];
57
58 // for(int I = 1; I <= n; I++) {
59 // for(int J = 1; J <= m; J++) {
60 srand(time(NULL));
61 ll toDo = 200000000/((n*m));
62 ll totalToDo = toDo;
63 while((toDo-->0)&&bestScore<100)
64 {
65 ll I = (rand()%n) + 1;
66 ll J = (rand()%m) + 1;
67 for(int i = 0; i <= n+1; i++)
68 for(int j = 0; j <= m+1; j++)
69 grid[i][j] = grid2[i][j];
70 memset(visited, 0, sizeof(visited));
71 queue<pair<ll,ll>> q;
72 q.push({I, J});
73 while(!q.empty())
74 {
75 ll i = q.front().first;
76 ll j = q.front().second;
77 q.pop();
78
79 if(grid[i][j] == ’#’ || grid[i][j] == ’.’ ||
80 ((ll)(rand()*0.9))%(((max(n, m)*3)))==1)
81 continue;
82
83 if(visited[i+1][j ]+
84 visited[i-1][j ]+
85 visited[i ][j+1]+
86 visited[i ][j-1] > 1)
87 {
88 grid[i][j] = ’X’;
89 continue;
90 }
91 else
92 {
93 visited[i][j]=1;
94 grid[i][j]=’.’;
95 q.push({i+1, j });
96 q.push({i , j+1});
97 q.push({i-1, j });
98 q.push({i , j-1});
99 }
100 }
101
102 ll cnt = 0;
103 for(int i = 1; i <= n; i++)
104 {
105 for(int j = 1; j <= m; j++)
106 {
107 if(grid[i][j]==’.’ &&
108 visited[i ][j-1]+
109 visited[i-1][j ]+
110 visited[i+1][j ]+
111 visited[i ][j+1] == 1)
112 cnt++;
113 }
114 }
115
116 ld score = min((ld)100, 100*(ld)cnt/k);
117
CAPITOLUL 4. IOI 2017 401

118 if(score>bestScore)
119 {
120 bestScore = score;
121 for(int i = 0; i <= n+1; i++)
122 for(int j = 0; j <= m+1; j++)
123 bestGrid[i][j] = grid[i][j];
124 }
125
126 if((ll)(((ld)toDo/totalToDo*100))/10 >
127 (ll)(((ld)(toDo-1)/totalToDo*100))/10)
128 {
129 cerr << (ll)((ld)toDo/totalToDo*100) << "%: " << bestScore<<endl;
130 }
131
132 ll done = (((ld)I*m)+J)/(((ld)n*m)+m)*100;
133 //if((ll)lastDone/10<(ll)done/10)
134 lastDone = done;
135 }
136
137 for(int i = 1; i <= n; i++)
138 {
139 for(int j = 1; j <= m; j++)
140 {
141 fout << bestGrid[i][j];
142 }
143 fout << endl;
144 }
145 }
146 /*
147 64 64 1338
148 90%: 96.861
149 80%: 98.1315
150 70%: 98.1315
151 60%: 98.1315
152 50%: 98.3558
153 40%: 98.3558
154 30%: 98.3558
155 20%: 98.3558
156 10%: 98.3558
157
158 Process returned 0 (0x0) execution time : 257.489 s
159 Press any key to continue.
160
161 argc = 4
162 checker
163 ../tests/02.in
164 ../tests/02.out
165 02.out.txt
166 ----------------------
167 0.983
168 Partially Correct
169 number of leaves: 1316/1338
170
171 Process returned 0 (0x0) execution time : 1.931 s
172 Press any key to continue.
173 */

4.1.3 *Rezolvare detaliat 

4.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  folosit pentru a conecta oricare
CAPITOLUL 4. IOI 2017 402

dou  puncte de conectare. Toate punctele de conetare pot  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  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 ecare 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 4. IOI 2017 403

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 a³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

4.2.1 Indicaµii de rezolvare

Subtask 1
2 3
There are many dierent O n  or O n  dp solutions for this subtask. The simplest one is
to dene dpi,j as the minimum cost needed for wiring the rst i red points and the rst 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 nd 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.

4.2.2 Coduri surs 

Listing 4.2.1: wiring.cpp


1 // http://ioi2017.org/tasks/materials/wiring.zip ---> sandbox/wiring.cpp
2 // http://ioi2017.org/tasks/materials/wiring.zip ---> solution/***.cpp
3 // . .. ... .... ..... be name khoda ..... .... ... .. .
4
5 #include<iostream>
6 #include<algorithm>
7 #include "wiring.h"
8
9 #include <fstream> // ***
10
11 using namespace std;
12
13 ifstream cinf("01.in"); // ***
14
CAPITOLUL 4. IOI 2017 404

15 inline int in() { int x; scanf("%d", &x); return x; }


16 const int N = 2000002;
17
18 const int BLUE = 1;
19 const int RED = 2;
20
21 struct Point
22 {
23 int x, color;
24 bool operator <(const Point &p) const { return x < p.x; }
25 };
26
27 Point p[N], tmp[N];
28 long long f[N], g[N], sum[N], dp[N];
29
30 long long min_total_length(vector<int> red, vector<int> blue)
31 {
32 int nb = blue.size();
33 int nr = red.size();
34
35 for(int i = 0; i < nb; i++)
36 {
37 tmp[i].x = blue[i];
38 tmp[i].color = BLUE;
39 }
40
41 for(int i = 0; i < nr; i++)
42 {
43 tmp[nb + i].x = red[i];
44 tmp[nb + i].color = RED;
45 }
46
47 merge(tmp, tmp + nb, tmp + nb, tmp + nb + nr, p + 1);
48
49 int n = nb + nr;
50 int st = 1, lastSz = 0;
51
52 for(int i = 1; i <= n; i++)
53 {
54 if(p[i].color != p[i - 1].color)
55 {
56 for(int j = i; p[j].color == p[i].color; j++)
57 sum[i] += p[j].x;
58
59 for(int j = i - 1; j >= st; j--)
60 {
61 f[j] = -sum[j] +
62 min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i - 1].x;
63 if(j < i - 1)
64 f[j] = min(f[j], f[j + 1]);
65 }
66
67 for(int j = st; j < i; j++)
68 {
69 g[j] = -sum[j] +
70 min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i].x;
71 if(j > st)
72 g[j] = min(g[j], g[j - 1]);
73 }
74
75 lastSz = i - st;
76 st = i;
77 }
78 else
79 sum[i] = sum[i - 1] - p[i - 1].x;
80
81 if(st == 1)
82 {
83 dp[i] = 1e18;
84 continue;
85 }
86
87 int sz = i - st + 1;
88 long long curSum = sum[st] - sum[i] + p[i].x;
89
90 if(sz >= lastSz)
CAPITOLUL 4. IOI 2017 405

91 dp[i] = f[st - lastSz] + curSum - 1LL * sz * p[st - 1].x;


92 else
93 dp[i] = min(g[st - sz - 1] + curSum - 1LL * sz * p[st].x,
94 f[st - sz] + curSum - 1LL * sz * p[st - 1].x);
95 }
96
97 return dp[n];
98 }
99
100 int main() // grader.cpp ...
101 {
102 int n, m;
103 cinf >> n >> m; // ***
104
105 vector<int> r(n), b(m);
106
107 for(int i = 0; i < n; i++)
108 cinf >>r[i]; // ***
109
110 for(int i = 0; i < m; i++)
111 cinf >>b[i]; // ***
112
113 long long res = min_total_length(r, b);
114 printf("%lld\n", res);
115
116 return 0;
117 }

Listing 4.2.2: wiring+grader.cpp


1 // http://ioi2017.org/tasks/materials/wiring.zip ---> sandbox/wiring.cpp
2 // http://ioi2017.org/tasks/materials/wiring.zip ---> solution/***.cpp
3
4 #include <cassert>
5 #include <cstdio>
6
7 #include<iostream>
8 #include<algorithm>
9
10 #include <fstream>
11 #include<ctime>
12
13 using namespace std;
14
15 #include "wiring.h"
16
17 inline int in() { int x; scanf("%d", &x); return x; }
18
19 const int N = 2000002;
20
21 const int BLUE = 1;
22 const int RED = 2;
23
24 struct Point
25 {
26 int x, color;
27 bool operator <(const Point &p) const { return x < p.x; }
28 };
29
30 Point p[N], tmp[N];
31 long long f[N], g[N], sum[N], dp[N];
32
33 long long min_total_length(vector<int> red, vector<int> blue)
34 {
35 int nb = blue.size();
36 int nr = red.size();
37 for(int i = 0; i < nb; i++)
38 {
39 tmp[i].x = blue[i];
40 tmp[i].color = BLUE;
41 }
42 for(int i = 0; i < nr; i++)
43 {
44 tmp[nb + i].x = red[i];
45 tmp[nb + i].color = RED;
CAPITOLUL 4. IOI 2017 406

46 }
47 merge(tmp, tmp + nb, tmp + nb, tmp + nb + nr, p + 1);
48
49 int n = nb + nr;
50 int st = 1, lastSz = 0;
51 for(int i = 1; i <= n; i++)
52 {
53 if(p[i].color != p[i - 1].color)
54 {
55 for(int j = i; p[j].color == p[i].color; j++)
56 sum[i] += p[j].x;
57 for(int j = i - 1; j >= st; j--)
58 {
59 f[j] = -sum[j] + min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i - 1].x;
60 if(j < i - 1)
61 f[j] = min(f[j], f[j + 1]);
62 }
63 for(int j = st; j < i; j++)
64 {
65 g[j] = -sum[j] + min(dp[j - 1], dp[j]) + 1LL * (i - j) * p[i].x;
66 if(j > st)
67 g[j] = min(g[j], g[j - 1]);
68 }
69 lastSz = i - st;
70 st = i;
71 }
72 else
73 sum[i] = sum[i - 1] - p[i - 1].x;
74 if(st == 1)
75 {
76 dp[i] = 1e18;
77 continue;
78 }
79 int sz = i - st + 1;
80 long long curSum = sum[st] - sum[i] + p[i].x;
81 if(sz >= lastSz)
82 dp[i] = f[st - lastSz] + curSum - 1LL * sz * p[st - 1].x;
83 else
84 dp[i] = min(g[st - sz - 1] + curSum - 1LL * sz * p[st].x,
85 f[st - sz] + curSum - 1LL * sz * p[st - 1].x);
86 }
87 return dp[n];
88 }
89
90 int main()
91 {
92 auto t1 = clock();
93
94 std::freopen("../tests/5-29.in", "r", stdin) ;
95 std::freopen("wiring.out.txt", "w", stdout) ;
96
97 int n, m;
98 assert(2 == scanf("%d %d", &n, &m));
99
100 vector<int> r(n), b(m);
101 for(int i = 0; i < n; i++)
102 assert(1 == scanf("%d", &r[i]));
103 for(int i = 0; i < m; i++)
104 assert(1 == scanf("%d", &b[i]));
105
106 auto t2 = clock();
107
108 long long res = min_total_length(r, b);
109
110 auto t3 = clock();
111
112 printf("%lld\n", res);
113
114 auto t4 = clock();
115
116 // reset console output
117 freopen("CON", "w", stdout);
118
119 std::cout <<"res = "<<res<<’\n’<<’\n’;
120
121 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 4. IOI 2017 407

122 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
123 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
124
125 return 0;
126 }
127 /*
128 res = 15991144105164
129
130 t2-t1 = 0.491
131 t3-t2 = 0.043
132 t4-t3 = 0
133
134 Process returned 0 (0x0) execution time : 0.582 s
135 Press any key to continue.
136 */

Listing 4.2.3: wiring-haas-ac.cpp


1 #include "wiring.h"
2 #include <vector>
3 #include <algorithm>
4 #include <iostream>
5 #include <cstring>
6 #include <cassert>
7 #include<ctime>
8
9 using namespace std;
10
11 typedef pair<int,bool> pie;
12
13 const bool BLUE = true, RED = false;
14 const int max_n = 200 * 1000; // TO BE DETERMINED
15
16 vector<pie> points;
17 long long d[max_n + 1], ps[max_n + 1];
18 int head[max_n], cnt[max_n];
19
20 long long val(int l, int r)
21 {
22 int m = head[r], x = r+1 - m, y = m - l;
23 long long res = (ps[r+1] - ps[m]) - (ps[m] - ps[l]);
24 if (x > y) res -= (long long)(x-y) * points[m-1].first;
25 if (x < y) res += (long long)(y-x) * points[m].first;
26 return res;
27 }
28
29 long long min_total_length (vector <int> red, vector <int> blue)
30 {
31 int n = blue.size(), m = red.size();
32 for (int i = 0; i < n; i++) points.push_back(pie(blue[i], BLUE));
33 for (int i = 0; i < m; i++) points.push_back(pie(red[i], RED));
34 sort (points.begin(), points.end());
35
36 n = points.size();
37 for (int i = 1; i <= n; i++) ps[i] = ps[i-1] + points[i-1].first;
38
39 long long *d = &(::d[1]);
40 memset(d, 50, sizeof(long long) * n);
41
42 for (int i = 1; i < n; i++)
43 {
44 head[i] = head[i-1];
45 if (points[i].second != points[i-1].second)
46 {
47 int prev = head[i];
48 head[i] = i;
49 d[i] = d[i-1] + val(i-1, i);
50 cnt[i] = 1;
51 for (int j = prev; j < i; j++)
52 if (d[i] > d[j-1] + val(j, i))
53 cnt[i] = i - j, d[i] = d[j-1] + val(j, i);
54 }
55 else
56 if (head[i] > 0)
57 {
CAPITOLUL 4. IOI 2017 408

58 cnt[i] = cnt[i-1];
59 int j = head[i] - cnt[i];
60 if (cnt[i-1] == i - head[i])
61 if (j > head[j] && d[j-1] + val(j, i) > d[j-2] + val(j-1, i))
62 cnt[i]++, j--;
63 d[i] = min(d[j], d[j-1]) + val(j, i);
64 }
65 }
66
67 return d[n-1];
68 }
69
70 int main()
71 {
72 auto t1 = clock();
73
74 std::freopen("../tests/5-29.in", "r", stdin) ;
75 std::freopen("wiring.out.txt", "w", stdout) ;
76
77 int n, m;
78 assert(2 == scanf("%d %d", &n, &m));
79
80 vector<int> r(n), b(m);
81 for(int i = 0; i < n; i++)
82 assert(1 == scanf("%d", &r[i]));
83 for(int i = 0; i < m; i++)
84 assert(1 == scanf("%d", &b[i]));
85
86 auto t2 = clock();
87
88 long long res = min_total_length(r, b);
89
90 auto t3 = clock();
91
92 printf("%lld\n", res);
93
94 auto t4 = clock();
95
96 // reset console output
97 freopen("CON", "w", stdout);
98
99 std::cout <<"res = "<<res<<’\n’<<’\n’;
100
101 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
102 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
103 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
104
105 return 0;
106 }
107 /*
108 res = 15991144105164
109
110 t2-t1 = 0.493
111 t3-t2 = 0.307
112 t4-t3 = 0
113
114 Process returned 0 (0x0) execution time : 0.847 s
115 Press any key to continue.
116 */

Listing 4.2.4: wiring-haas-ac-ternary.cpp


1 #include "wiring.h"
2 #include <vector>
3 #include <algorithm>
4 #include <iostream>
5 #include <cstring>
6 #include <cassert>
7 #include<ctime>
8
9 using namespace std;
10
11 typedef pair<int,bool> pie;
12
13 const bool BLUE = true, RED = false;
CAPITOLUL 4. IOI 2017 409

14 const int max_n = 200 * 1000; // TO BE DETERMINED


15
16 const int TOF = 100;
17
18 vector<pie> points;
19 long long d[max_n + 1], ps[max_n + 1];
20 int head[max_n];
21
22 long long val(int l, int r)
23 {
24 int m = head[r], x = r+1 - m, y = m - l;
25 long long res = (ps[r+1] - ps[m]) - (ps[m] - ps[l]);
26 if (x > y) res -= (long long)(x-y) * points[m-1].first;
27 if (x < y) res += (long long)(y-x) * points[m].first;
28 return res;
29 }
30
31 long long min_total_length (vector <int> red, vector <int> blue)
32 {
33 int n = blue.size(), m = red.size();
34 for (int i = 0; i < n; i++) points.push_back(pie(blue[i], BLUE));
35 for (int i = 0; i < m; i++) points.push_back(pie(red[i], RED));
36 sort (points.begin(), points.end());
37
38 n = points.size();
39 for (int i = 0; i < n - 1; i++)
40 assert(points[i].first != points[i + 1].first);
41 for (int i = 1; i <= n; i++) ps[i] = ps[i-1] + points[i-1].first;
42
43 long long *d = &(::d[1]);
44 memset(d, 50, sizeof(long long) * n);
45
46 for (int i = 1; i < n; i++)
47 {
48 head[i] = head[i-1];
49 if (points[i].second != points[i-1].second)
50 {
51 int prev = head[i];
52 head[i] = i;
53 d[i] = d[i-1] + val(i-1, i);
54 for (int j = prev; j < i; j++)
55 if (d[i] > d[j-1] + val(j, i))
56 d[i] = d[j-1] + val(j, i);
57 }
58 else
59 if (head[i] > 0)
60 {
61 int l = head[head[i] - 1], r = head[i];
62 if (l == 0)
63 d[i] = val(0, i);
64 else
65 if (r - l < TOF)
66 for (int j = l; j < r; j++)
67 d[i] = min(d[i], min(d[j], d[j-1]) + val(j, i));
68 else
69 {
70 while (r - l > 1)
71 {
72 int m = (r + l) / 2;
73 if (d[m-1] + val(m, i) > d[m-2] + val(m-1, i))
74 r = m;
75 else
76 l = m;
77 }
78
79 d[i] = d[l - 1] + val(l, i);
80 }
81 }
82 }
83
84 return d[n-1];
85 }
86
87 int main()
88 {
89 auto t1 = clock();
CAPITOLUL 4. IOI 2017 410

90
91 std::freopen("../tests/5-29.in", "r", stdin) ;
92 std::freopen("wiring.out.txt", "w", stdout) ;
93
94 int n, m;
95 assert(2 == scanf("%d %d", &n, &m));
96
97 vector<int> r(n), b(m);
98 for(int i = 0; i < n; i++)
99 assert(1 == scanf("%d", &r[i]));
100 for(int i = 0; i < m; i++)
101 assert(1 == scanf("%d", &b[i]));
102
103 auto t2 = clock();
104
105 long long res = min_total_length(r, b);
106
107 auto t3 = clock();
108
109 printf("%lld\n", res);
110
111 auto t4 = clock();
112
113 // reset console output
114 freopen("CON", "w", stdout);
115
116 std::cout <<"res = "<<res<<’\n’<<’\n’;
117
118 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
119 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
120 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
121
122 return 0;
123 }
124 /*
125 res = 15991144105164
126
127 t2-t1 = 0.462
128 t3-t2 = 0.432
129 t4-t3 = 0
130
131 Process returned 0 (0x0) execution time : 0.950 s
132 Press any key to continue.
133 */

Listing 4.2.5: wiring-mahdi-ac.cpp


1 // . .. ... .... ..... be name khoda ..... .... ... .. .
2 #include<iostream>
3 #include<algorithm>
4 #include<ctime>
5 #include <cassert>
6
7 #include "wiring.h"
8
9 using namespace std;
10
11 inline int in() { int x; scanf("%d", &x); return x; }
12 const int N = 2000002;
13
14 const int BLUE = 1;
15 const int RED = 2;
16
17 struct Point
18 {
19 int x, color;
20 bool operator <(const Point &p) const { return x < p.x; }
21 };
22
23 Point p[N], tmp[N];
24 long long f[N], g[N], sum[N], dp[N];
25
26 long long min_total_length(vector<int> red, vector<int> blue)
27 {
28 int nb = blue.size();
CAPITOLUL 4. IOI 2017 411

29 int nr = red.size();
30 for(int i = 0; i < nb; i++)
31 {
32 tmp[i].x = blue[i];
33 tmp[i].color = BLUE;
34 }
35
36 for(int i = 0; i < nr; i++)
37 {
38 tmp[nb + i].x = red[i];
39 tmp[nb + i].color = RED;
40 }
41
42 merge(tmp, tmp + nb, tmp + nb, tmp + nb + nr, p + 1);
43
44 int n = nb + nr;
45 int st = 1, lastSz = 0;
46 for(int i = 1; i <= n; i++)
47 {
48 if(p[i].color != p[i - 1].color)
49 {
50 for(int j = i; p[j].color == p[i].color; j++)
51 sum[i] += p[j].x;
52
53 for(int j = i - 1; j >= st; j--)
54 {
55 f[j] = -sum[j] + min(dp[j-1], dp[j]) + 1LL * (i-j) * p[i-1].x;
56 if(j < i - 1)
57 f[j] = min(f[j], f[j + 1]);
58 }
59
60 for(int j = st; j < i; j++)
61 {
62 g[j] = -sum[j] + min(dp[j-1], dp[j]) + 1LL * (i-j) * p[i].x;
63 if(j > st)
64 g[j] = min(g[j], g[j - 1]);
65 }
66
67 lastSz = i - st;
68 st = i;
69 }
70 else
71 sum[i] = sum[i - 1] - p[i - 1].x;
72
73 if(st == 1)
74 {
75 dp[i] = 1e18;
76 continue;
77 }
78
79 int sz = i - st + 1;
80 long long curSum = sum[st] - sum[i] + p[i].x;
81 if(sz >= lastSz)
82 dp[i] = f[st - lastSz] + curSum - 1LL * sz * p[st - 1].x;
83 else
84 dp[i] = min(g[st - sz - 1] + curSum - 1LL * sz * p[st].x,
85 f[st - sz] + curSum - 1LL * sz * p[st - 1].x);
86 }
87
88 return dp[n];
89 }
90
91 int main()
92 {
93 auto t1 = clock();
94
95 std::freopen("../tests/5-29.in", "r", stdin) ;
96 std::freopen("wiring.out.txt", "w", stdout) ;
97
98 int n, m;
99 assert(2 == scanf("%d %d", &n, &m));
100
101 vector<int> r(n), b(m);
102 for(int i = 0; i < n; i++)
103 assert(1 == scanf("%d", &r[i]));
104 for(int i = 0; i < m; i++)
CAPITOLUL 4. IOI 2017 412

105 assert(1 == scanf("%d", &b[i]));


106
107 auto t2 = clock();
108
109 long long res = min_total_length(r, b);
110
111 auto t3 = clock();
112
113 printf("%lld\n", res);
114
115 auto t4 = clock();
116
117 // reset console output
118 freopen("CON", "w", stdout);
119
120 std::cout <<"res = "<<res<<’\n’<<’\n’;
121
122 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
123 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
124 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
125
126 return 0;
127 }
128 /*
129 res = 15991144105164
130
131 t2-t1 = 0.464
132 t3-t2 = 0.037
133 t4-t3 = 0
134
135 Process returned 0 (0x0) execution time : 0.555 s
136 Press any key to continue.
137 */

Listing 4.2.6: wiring-malek-ac.cpp


1 #include "wiring.h"
2 #include<bits/stdc++.h>
3
4 using namespace std;
5
6 #define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)
7 #define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
8 #define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
9 #define sz(x) (int((x).size()))
10 #define pb push_back
11 #define all(X) (X).begin(),(X).end()
12 #define X first
13 #define Y second
14 #define endl ’\n’
15
16 template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }
17 template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }
18
19 typedef long long ll;
20 typedef pair<int, int> pii;
21
22 ////////////////////////////////////////////////////////////////////////////
23
24 const ll infll = 1LL << 62;
25 const int maxn = 500000 + 100;
26
27 int n;
28 pii p[maxn];
29 int st[maxn], ed[maxn];
30 ll ps[maxn];
31 ll dp[maxn][2];
32
33 long long min_total_length (vector <int> red, vector <int> blue)
34 {
35 int nb = blue.size(), nr = red.size();
36 rep(i, nb) p[i] = pii(blue[i], 0);
37 rep(i, nr) p[i+nb] = pii(red[i], 1);
38
39 n = nb + nr;
CAPITOLUL 4. IOI 2017 413

40 sort(p, p+n);
41
42 rep(i, n) st[i] = (i == 0 || p[i].Y != p[i-1].Y ? i : st[i-1]);
43 rof(i, n, 0) ed[i] = (i == n-1 || p[i].Y != p[i+1].Y ? i+1 : ed[i+1]);
44
45 ps[0] = 0;
46 rep(i, n) ps[i+1] = ps[i] + p[i].X;
47
48 rep(i, n+1) dp[i][0] = dp[i][1] = infll;
49 dp[0][0] = 0;
50 fer(i, 1, n+1)
51 {
52 int s = st[i-1];
53 int e = ed[i-1];
54
55 if(s == i-1) smin(dp[i][0], dp[i-1][1]);
56 if(s != 0) {
57 smin(dp[i][0], dp[i-1][0] + p[i-1].X - p[s-1].X);
58 int len = i - s;
59 if(s - len >= 0)
60 smin(dp[i][0], dp[s-len][0]+(ps[i]-ps[s])-(ps[s]-ps[s-len]));
61 }
62 if(e != n) smin(dp[i][1], dp[i-1][0] + p[e].X - p[i-1].X);
63 smin(dp[i][0], dp[i][1]);
64 }
65
66 return dp[n][0];
67 }
68
69 int main()
70 {
71 auto t1 = clock();
72
73 std::freopen("../tests/5-29.in", "r", stdin) ;
74 std::freopen("wiring.out.txt", "w", stdout) ;
75
76 int n, m;
77 assert(2 == scanf("%d %d", &n, &m));
78
79 vector<int> r(n), b(m);
80 for(int i = 0; i < n; i++)
81 assert(1 == scanf("%d", &r[i]));
82 for(int i = 0; i < m; i++)
83 assert(1 == scanf("%d", &b[i]));
84
85 auto t2 = clock();
86
87 long long res = min_total_length(r, b);
88
89 auto t3 = clock();
90
91 printf("%lld\n", res);
92
93 auto t4 = clock();
94
95 // reset console output
96 freopen("CON", "w", stdout);
97
98 std::cout <<"res = "<<res<<’\n’<<’\n’;
99
100 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
101 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
102 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
103
104 return 0;
105 }
106 /*
107 res = 15991144105164
108
109 t2-t1 = 0.438
110 t3-t2 = 0.194
111 t4-t3 = 0
112
113 Process returned 0 (0x0) execution time : 0.682 s
114 Press any key to continue.
115 */
CAPITOLUL 4. IOI 2017 414

Listing 4.2.7: wiring-saeed-ac.cpp


1 #include "wiring.h"
2
3 #include <iostream>
4 #include <vector>
5 #include <algorithm>
6 #include<ctime>
7 #include <cassert>
8
9 using namespace std;
10
11 const int MAXN = 1000000;
12
13 const long long inf = 1LL<<60;
14
15 int nex[MAXN];
16 long long sum[MAXN], dp[MAXN], f[MAXN];
17
18 inline long long get_sum(const int lo, const int hi)
19 {
20 return sum[lo] - sum[hi+1];
21 }
22
23 long long min_total_length(vector <int> red, vector <int> blue)
24 {
25 int n = blue.size(), m = red.size();
26 vector< pair<int,int> > q;
27 int pb = 0, pr = 0;
28 while (pb < n || pr < m)
29 {
30 if ((pr == m) || (pb < n && blue[pb] < red[pr]))
31 q.push_back(make_pair(blue[pb++], 0));
32 else
33 q.push_back(make_pair(red[pr++], 1));
34 }
35
36 int nm = n + m;
37 sum[nm] = 0, f[nm] = 0;
38 dp[nm-1] = inf, nex[nm-1] = nm, sum[nm-1] = q[nm-1].first;
39 for (int i = nm - 2; i >= 0; i--)
40 {
41 dp[i] = inf;
42 sum[i] = sum[i+1] + q[i].first;
43 nex[i] = q[i].second == q[i+1].second ? nex[i+1] : i+1;
44 if (nex[i] == i+1)
45 {
46 for (int j = nex[i+1]-1; j >= i+1; j--)
47 f[j] = min(dp[j], q[j].first - q[i].first + f[j+1]);
48 }
49
50 if (nex[i] == nm)
51 continue;
52 dp[i] = min(inf, dp[i+1] + q[nex[i]].first - q[i].first);
53 int sz = nex[i] - i;
54 if (nex[i]+sz-1 < nex[nex[i]])
55 dp[i] = min(dp[i],
56 get_sum(nex[i],
57 nex[i]+sz-1) - get_sum(i, nex[i]-1) + f[nex[i]+sz]);
58 }
59
60 return dp[0];
61 }
62
63 int main()
64 {
65 auto t1 = clock();
66
67 std::freopen("../tests/5-29.in", "r", stdin) ;
68 std::freopen("wiring.out.txt", "w", stdout) ;
69
70 int n, m;
71 assert(2 == scanf("%d %d", &n, &m));
72
73 vector<int> r(n), b(m);
74 for(int i = 0; i < n; i++)
CAPITOLUL 4. IOI 2017 415

75 assert(1 == scanf("%d", &r[i]));


76 for(int i = 0; i < m; i++)
77 assert(1 == scanf("%d", &b[i]));
78
79 auto t2 = clock();
80
81 long long res = min_total_length(r, b);
82
83 auto t3 = clock();
84
85 printf("%lld\n", res);
86
87 auto t4 = clock();
88
89 // reset console output
90 freopen("CON", "w", stdout);
91
92 std::cout <<"res = "<<res<<’\n’<<’\n’;
93
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 return 0;
99 }
100 /*
101 res = 15991144105164
102
103 t2-t1 = 0.457
104 t3-t2 = 0.091
105 t4-t3 = 0
106
107 Process returned 0 (0x0) execution time : 0.600 s
108 Press any key to continue.
109 */

Listing 4.2.8: wiring-197505.cpp


1 // https://oj.uz/submission/197505 56 ms 9584 KB
2
3 #include <bits/stdc++.h>
4 #include "wiring.h"
5
6 using namespace std;
7
8 #define pb push_back
9 #define mp make_pair
10 long long n,dp[200001],presum[200001];
11 vector<pair<int,int> > arr;
12 int prevv[200001];
13 long long min_total_length(vector<int> r,vector<int> b)
14 {
15 n=r.size()+b.size();
16 int rid=0,bid=0;
17 while (rid<r.size() || bid<b.size())
18 {
19 if (rid==r.size())
20 {
21 arr.pb(mp(b[bid],1));
22 bid++;
23 }
24 else
25 if (bid==b.size())
26 {
27 arr.pb(mp(r[rid],0));
28 rid++;
29 }
30 else
31 {
32 if (r[rid]<=b[bid])
33 {
34 arr.pb(mp(r[rid],0));
35 rid++;
36 }
37 else
CAPITOLUL 4. IOI 2017 416

38 {
39 arr.pb(mp(b[bid],1));
40 bid++;
41 }
42 }
43 }
44
45 dp[0]=1e15;
46 prevv[0]=-1;
47 presum[0]=arr[0].first;
48
49 for (int i=1;i<n;i++)
50 {
51 presum[i]=presum[i-1]+arr[i].first;
52 dp[i]=1e15;
53 if (arr[i].second==arr[i-1].second)
54 prevv[i]=prevv[i-1];
55 else
56 prevv[i]=i-1;
57
58 for (int j=prevv[i-1]+1;j<=prevv[i];j++)
59 {
60 dp[j]=min(dp[j],dp[j-1]+arr[i].first-arr[j].first);
61 }
62
63 if (prevv[i]==-1) continue;
64
65 dp[i]=min(dp[i],dp[i-1]+arr[i].first-arr[prevv[i]].first);
66 if (prevv[prevv[i]]<=prevv[i]-(i-prevv[i]))
67 {
68 long long cur=presum[i]-presum[prevv[i]];
69 cur-=(presum[prevv[i]]-presum[prevv[i]-(i-prevv[i])]);
70 if (prevv[i]-(i-prevv[i])!=-1)
71 cur+=dp[prevv[i]-(i-prevv[i])];
72 dp[i]=min(dp[i],cur);
73 }
74 }
75
76 return dp[n-1];
77 }
78
79 int main()
80 {
81 auto t1 = clock();
82
83 std::freopen("../tests/5-29.in", "r", stdin) ;
84 std::freopen("wiring.out.txt", "w", stdout) ;
85
86 int n, m;
87 assert(2 == scanf("%d %d", &n, &m));
88
89 vector<int> r(n), b(m);
90 for(int i = 0; i < n; i++)
91 assert(1 == scanf("%d", &r[i]));
92 for(int i = 0; i < m; i++)
93 assert(1 == scanf("%d", &b[i]));
94
95 auto t2 = clock();
96
97 long long res = min_total_length(r, b);
98
99 auto t3 = clock();
100
101 printf("%lld\n", res);
102
103 auto t4 = clock();
104
105 // reset console output
106 freopen("CON", "w", stdout);
107
108 std::cout <<"res = "<<res<<’\n’<<’\n’;
109
110 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
111 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
112 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
113
CAPITOLUL 4. IOI 2017 417

114 return 0;
115 }
116 /*
117 res = 15991144105164
118
119 t2-t1 = 0.406
120 t3-t2 = 0.062
121 t4-t3 = 0
122
123 Process returned 0 (0x0) execution time : 0.500 s
124 Press any key to continue.
125 */

Listing 4.2.9: wiring-200512.cpp


1 // https://oj.uz/submission/200512 93 ms 15048 KB
2
3 #include <bits/stdc++.h>
4 using namespace std;
5 typedef long long ll; /// dont use define int long long
6
7 void Minimize(ll &a, const ll &b)
8 {
9 a = min(a, b);
10 }
11
12 ll min_total_length(vector<int> r, vector<int> b)
13 {
14 vector<vector<ll>> dp(0), prf(0);
15 vector<pair<ll, ll>> vt;
16 for(auto i: r) vt.emplace_back(i, 0);
17 for(auto i: b) vt.emplace_back(i, 1);
18 sort(vt.begin(), vt.end());
19 int cur = 0;
20 while(cur < vt.size())
21 {
22 int nxt = cur;
23 vector<ll> nprf = {0};
24 for(; nxt < vt.size() && vt[nxt].second == vt[cur].second; nxt++)
25 {
26 nprf.push_back(vt[nxt].first + nprf.back());
27 }
28 cur = nxt;
29 vector<ll> blank(nprf.size(), (ll)1e17);
30 dp.push_back(blank);
31 prf.push_back(nprf);
32 }
33
34 auto Get = [&](ll x, ll y)
35 {
36 ll ans = prf[x][y] - prf[x][y - 1];
37 return ans;
38 };
39
40 dp[0][0] = 0;
41 for(int i = 0; i < dp.size(); i++)
42 {
43 for(int j = 0; j + 1 < dp[i].size(); j++)
44 {
45 if(i > 0)
46 Minimize(dp[i][j + 1],
47 dp[i][j] + Get(i, j + 1) - Get(i - 1,
48 prf[i - 1].size() - 1));
49
50 if(i + 1 < dp.size())
51 {
52 if(prf[i].size() - j - 1 <= prf[i + 1].size() - 1)
53 {
54 Minimize(dp[i + 1][prf[i].size() - j - 1],
55 dp[i][j] + prf[i + 1][prf[i].size() - j - 1]
56 - prf[i][prf[i].size() - 1] + prf[i][j]);
57 }
58
59 Minimize(dp[i][j + 1],
60 dp[i][j] - Get(i, j + 1) + Get(i + 1, 1));
CAPITOLUL 4. IOI 2017 418

61 }
62 }
63
64 if(i + 1 < dp.size()) Minimize(dp[i + 1][0], dp[i].back());
65 else return dp[i].back();
66 }
67 return 0;
68 }
69
70 int main()
71 {
72 auto t1 = clock();
73
74 std::freopen("../tests/5-29.in", "r", stdin) ;
75 std::freopen("wiring.out.txt", "w", stdout) ;
76
77 int n, m;
78 assert(2 == scanf("%d %d", &n, &m));
79
80 vector<int> r(n), b(m);
81 for(int i = 0; i < n; i++)
82 assert(1 == scanf("%d", &r[i]));
83 for(int i = 0; i < m; i++)
84 assert(1 == scanf("%d", &b[i]));
85
86 auto t2 = clock();
87
88 long long res = min_total_length(r, b);
89
90 auto t3 = clock();
91
92 printf("%lld\n", res);
93
94 auto t4 = clock();
95
96 // reset console output
97 freopen("CON", "w", stdout);
98
99 std::cout <<"res = "<<res<<’\n’<<’\n’;
100
101 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
102 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
103 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
104
105 return 0;
106 }
107 /*
108 res = 15991144105164
109
110 t2-t1 = 0.391
111 t3-t2 = 0.5
112 t4-t3 = 0
113
114 Process returned 0 (0x0) execution time : 0.937 s
115 Press any key to continue.
116 */

Listing 4.2.10: wiring-206573.cpp


1 // https://oj.uz/submission/206573 75 ms 21352 KB
2
3 #include<bits/stdc++.h>
4
5 #define pb push_back
6 #define mp make_pair
7 #define sz(x) (int)x.size()
8 #define f first
9 #define s second
10 #define all(x) x.begin(), x.end()
11 #define forn(i, a, b) for(int i = a; i <= b; i++)
12 #define forev(i, a, b) for(int i = a; i >= b; i--)
13
14 using namespace std;
15
16 typedef long long ll;
CAPITOLUL 4. IOI 2017 419

17 typedef vector<int> vi;


18 typedef pair<int, int> pii;
19 typedef vector<pii> vpii;
20
21 const int maxn = (int)1e6 + 100;
22 const int mod = (int)1e9 + 7;
23 const int inf = (int)2e9;
24 const double pi = acos(-1.0);
25
26 int n, m, a, b, go[maxn], was[2], cnt[maxn], used[maxn + maxn], K = 500000;
27 ll dp[maxn], s[maxn];
28 vpii v;
29
30 ll min_total_length(vi v1, vi v2)
31 {
32 n = sz(v1), m = sz(v2);
33 for(auto a : v1) v.pb(mp(a, 0));
34 for(auto b : v2) v.pb(mp(b, 1));
35 sort(all(v));
36 forn(i, 0, maxn - 1) go[i] = inf;
37 was[0] = was[1] = -1;
38 int i = 0;
39 for(auto x : v)
40 {
41 i++;
42 if(was[!x.s] != -1) go[i] = x.f - was[!x.s];
43 was[x.s] = x.f;
44 if(x.s) cnt[i] = 1, s[i] = x.f;
45 else cnt[i] = -1, s[i] = -x.f;
46 cnt[i] += cnt[i - 1];
47 s[i] += s[i - 1];
48 }
49
50 was[0] = was[1] = -1; i = n + m + 1;
51 reverse(all(v));
52
53 for(auto x : v)
54 {
55 i--;
56 if(was[!x.s] != -1) go[i] = min(go[i], was[!x.s] - x.f);
57 was[x.s] = x.f;
58 }
59
60 memset(used, -1, sizeof(used));
61 used[K] = 0;
62 forn(i, 1, n + m)
63 {
64 dp[i] = dp[i - 1] + go[i];
65 if(used[cnt[i] + K] != -1)
66 {
67 int j = used[cnt[i] + K] + 1;
68 dp[i] = min(dp[i], dp[j - 1] + abs(s[i] - s[j - 1]));
69 }
70 used[cnt[i] + K] = i;
71 }
72 return dp[n + m];
73 }
74
75 int main()
76 {
77 auto t1 = clock();
78
79 std::freopen("../tests/5-29.in", "r", stdin) ;
80 std::freopen("wiring.out.txt", "w", stdout) ;
81
82 int n, m;
83 assert(2 == scanf("%d %d", &n, &m));
84
85 vector<int> r(n), b(m);
86 for(int i = 0; i < n; i++)
87 assert(1 == scanf("%d", &r[i]));
88 for(int i = 0; i < m; i++)
89 assert(1 == scanf("%d", &b[i]));
90
91 auto t2 = clock();
92
CAPITOLUL 4. IOI 2017 420

93 long long res = min_total_length(r, b);


94
95 auto t3 = clock();
96
97 printf("%lld\n", res);
98
99 auto t4 = clock();
100
101 // reset console output
102 freopen("CON", "w", stdout);
103
104 std::cout <<"res = "<<res<<’\n’<<’\n’;
105
106 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
107 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
108 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
109
110 return 0;
111 }
112 /*
113 res = 15991144105164
114
115 t2-t1 = 0.406
116 t3-t2 = 0.5
117 t4-t3 = 0
118
119 Process returned 0 (0x0) execution time : 0.937 s
120 Press any key to continue.
121 */

Listing 4.2.11: wiring-216947.cpp


1 // https://oj.uz/submission/216947 69 ms 11144 KB
2
3 #include "wiring.h"
4 #include <algorithm>
5 #include <vector>
6
7 #include<ctime>
8 #include<iostream>
9 #include<cassert>
10
11 using namespace std;
12
13 using llong = long long;
14 const int inf = 1000’000’000;
15
16 llong min_total_length(vector<int> red, vector<int> blue)
17 {
18 int n = red.size() + blue.size();
19 vector<pair<int, char> > temp; temp.reserve(n);
20
21 for (int i = 0; i < red.size(); ++i)
22 temp.push_back({red[i], ’x’});
23
24 for (int i = 0; i < blue.size(); ++i)
25 temp.push_back({blue[i], ’o’});
26
27 vector<char> a(n);
28 vector<int> b(n);
29
30 sort(temp.begin(), temp.end());
31
32 for (int i = 0; i < n; ++i)
33 b[i] = temp[i].first, a[i] = temp[i].second;
34
35 vector<int> lone(n);
36 for (int i = 0; i < n; ++i)
37 {
38 int j = i;
39 while (j + 1 < n && a[j + 1] == a[i]) ++j;
40 for (int k = i; k <= j; ++k)
41 lone[k] = min(i ? b[k]-b[i-1] : inf,
42 j+1 < n ? b[j+1]-b[k] : inf);
43 i = j;
CAPITOLUL 4. IOI 2017 421

44 }
45
46 vector<int> mirror(n, -1);
47 vector<llong> mirrorval(n);
48
49 for (int i = 1; i < n; ++i)
50 if (a[i] != a[i - 1])
51 for (int r = i, l = i - 1;
52 0 <= l && r < n && a[l] == a[i - 1] && a[r] == a[i];
53 --l, ++r)
54 {
55 mirror[r] = l;
56 mirrorval[r] = b[r] - b[l];
57 if (r > i)
58 mirrorval[r] += mirrorval[r - 1];
59 }
60
61 vector<llong> dp(n);
62 dp[0] = lone[0];
63
64 for (int i = 1; i < n; ++i)
65 {
66 dp[i] = dp[i - 1] + lone[i];
67 if (mirror[i] == -1) continue;
68 llong curdp = 0;
69 curdp += (mirror[i] ? dp[mirror[i] - 1] : 0);
70 curdp += mirrorval[i];
71 dp[i] = min(dp[i], curdp);
72 }
73
74 return dp[n - 1];
75 }
76
77 int main()
78 {
79 auto t1 = clock();
80
81 std::freopen("../tests/5-29.in", "r", stdin) ;
82 std::freopen("wiring.out.txt", "w", stdout) ;
83
84 int n, m;
85 assert(2 == scanf("%d %d", &n, &m));
86
87 vector<int> r(n), b(m);
88 for(int i = 0; i < n; i++)
89 assert(1 == scanf("%d", &r[i]));
90 for(int i = 0; i < m; i++)
91 assert(1 == scanf("%d", &b[i]));
92
93 auto t2 = clock();
94
95 long long res = min_total_length(r, b);
96
97 auto t3 = clock();
98
99 printf("%lld\n", res);
100
101 auto t4 = clock();
102
103 // reset console output
104 freopen("CON", "w", stdout);
105
106 std::cout <<"res = "<<res<<’\n’<<’\n’;
107
108 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
109 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
110 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
111
112 return 0;
113 }
114 /*
115 res = 15991144105164
116
117 t2-t1 = 0.39
118 t3-t2 = 0.453
119 t4-t3 = 0
CAPITOLUL 4. IOI 2017 422

120
121 Process returned 0 (0x0) execution time : 0.875 s
122 Press any key to continue.
123 */

Listing 4.2.12: wiring-221801.cpp


1 // https://oj.uz/submission/221801 212 ms 62184 KB
2 /**
3 * user: ppavic
4 * fname: Patrik
5 * lname: PaviÄĞ
6 * task: wiring
7 * score: 100.0
8 * date: 2019-06-13 11:05:56.973728
9 */
10 #include "wiring.h"
11 #include <cstdio>
12 #include <cstring>
13 #include <algorithm>
14 #include <vector>
15 #include <map>
16 #include <unordered_map>
17
18 #include<ctime>
19 #include<cassert>
20 #include<iostream>
21
22 #define X first
23 #define Y second
24 #define PB push_back
25
26 using namespace std;
27
28 typedef long long ll;
29 typedef pair < int, int > pii;
30 typedef vector < int > vi;
31 typedef vector < pii > vp;
32
33 const int N = 3e5 + 500;
34 const int LOG = 18;
35 const ll INF = 1e18;
36 const int OFF = (1 << LOG);
37
38 ll prf[N];
39 map < int , ll > dp[N];
40 int n, L[N], R[N], G[N], rv[N];
41 vp v;
42
43 inline ll dis(int x,int y)
44 {
45 return v[y].X - v[x].X;
46 }
47
48 inline ll get(int x,int y)
49 {
50 if(x > y) return 0;
51 return prf[y] - (x ? prf[x - 1] : 0);
52 }
53
54 ll f(int i,int lst)
55 {
56 if(dp[i][lst] != 0)
57 return dp[i][lst] - 1;
58 if(i == n)
59 {
60 if(lst > 0 && i > 1)
61 return -1 + (dp[i][lst] = f(i, lst - 1)
62 + dis(L[i - 1] - 1, L[i] - lst)
63 + 1);
64 return -1 + (dp[i][lst] = (lst != 0) * INF + 1);
65 }
66
67 ll ret = INF;
68 if(lst > 0)
CAPITOLUL 4. IOI 2017 423

69 ret = min(ret, f(i, lst - 1) + dis(L[i] - lst, L[i]));


70 if(lst <= G[i])
71 ret = min(ret, f(i + 1, G[i] - lst)
72 + get(L[i], L[i] + lst - 1)
73 - get(L[i] - lst, L[i] - 1));
74 if(i > 1 && lst > 0)
75 ret = min(ret, f(i, lst - 1) + dis(L[i - 1] - 1, L[i] - lst));
76
77 return -1 + (dp[i][lst] = ret + 1);
78 }
79
80
81 ll min_total_length(vi r, vi b)
82 {
83 for(int i = 0;i < N;i++) dp[i].clear();
84 ll S_r = 0, S_b = 0;
85 for(int x : r) v.PB({x, 1});
86 for(int x : b) v.PB({x, 0});
87
88 sort(v.begin(), v.end());
89
90 int l = 0, ll = 0, gr = 0;
91 for(int i = 0;i < v.size();i++)
92 {
93 prf[i] = (long long)(v[i].X) + (i ? prf[i - 1] : 0);
94 if(v[i].Y != l && i != 0)
95 {
96 if(gr)
97 rv[gr] = G[gr - 1] + rv[gr - 1];
98 G[gr] = i - ll;
99 L[gr] = ll;
100 R[gr++] = i - 1;
101 ll = i;
102 }
103
104 l = v[i].Y;
105 }
106
107 rv[gr] = G[gr - 1] + rv[gr - 1];
108 G[gr] = v.size() - ll; L[gr] = ll; R[gr] = v.size() - 1;
109 n = ++gr; L[n] = v.size();
110
111 return f(0, 0);
112 }
113
114
115 int main()
116 {
117 auto t1 = clock();
118
119 std::freopen("../tests/5-29.in", "r", stdin) ;
120 std::freopen("wiring.out.txt", "w", stdout) ;
121
122 int n, m;
123 assert(2 == scanf("%d %d", &n, &m));
124
125 vector<int> r(n), b(m);
126 for(int i = 0; i < n; i++)
127 assert(1 == scanf("%d", &r[i]));
128 for(int i = 0; i < m; i++)
129 assert(1 == scanf("%d", &b[i]));
130
131 auto t2 = clock();
132
133 long long res = min_total_length(r, b);
134
135 auto t3 = clock();
136
137 printf("%lld\n", res);
138
139 auto t4 = clock();
140
141 // reset console output
142 freopen("CON", "w", stdout);
143
144 std::cout <<"res = "<<res<<’\n’<<’\n’;
CAPITOLUL 4. IOI 2017 424

145
146 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
147 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
148 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
149
150 return 0;
151 }
152 /*
153 res = 15991144105164
154
155 t2-t1 = 0.438
156 t3-t2 = 1.265
157 t4-t3 = 0
158
159 Process returned 0 (0x0) execution time : 1.828 s
160 Press any key to continue.
161 */

Listing 4.2.13: wiring-227702.cpp


1 // https://oj.uz/submission/227702 68 ms 12520 KB
2
3 #include<bits/stdc++.h>
4 using namespace std;
5
6 #define REP(i, n) for(int i = 0; i < n; i++)
7 #define FOR(i, a, b) for(int i = a; i <= b; i++)
8 #define ST first
9 #define ND second
10
11 using LL = long long;
12 using PII = pair<int, int>;
13
14 #include "wiring.h"
15
16 LL min_total_length(vector<int> r, vector<int> b)
17 {
18 vector<PII> p;
19 for(int x : r) p.emplace_back(x, 1);
20 for(int x : b) p.emplace_back(x, 2);
21 sort(p.begin(), p.end());
22
23 int n = size(p);
24 vector<int> s(n);
25 FOR(i, 1, n - 1)
26 s[i] = (p[i].ND != p[i - 1].ND ? i : s[i - 1]);
27
28 LL inf = 1e18;
29 vector<LL> dp(n, inf), x(n), y(n, inf), sum(n);
30 REP(i, n) sum[i] = (i != 0 ? sum[i - 1] : 0) + p[i].ST;
31
32 auto get = [&](int l, int r)
33 {
34 return sum[r] - (l != 0 ? sum[l - 1] : 0);
35 };
36
37 REP(i, n)
38 {
39 if(s[i] == 0) continue;
40 int u = s[i] - 1, v = s[i];
41 auto rel = [&](int l, int r)
42 {
43 return abs(get(l, r) - LL(r - l + 1) * p[v].ST);
44 };
45
46 if(v == i)
47 {
48 LL cur = inf;
49 FOR(j, s[u], u)
50 {
51 cur = min(cur, (j == 0 ? 0 : dp[j - 1]) + rel(j, u));
52 x[j] = cur;
53 }
54
55 cur = inf;
CAPITOLUL 4. IOI 2017 425

56 for(int j = v; j < n && s[j] == v; j++)


57 {
58 int t = u + v - j;
59 cur += p[j].ST - p[u].ST;
60 if(s[u] <= t)
61 {
62 cur = min(cur,
63 rel(t, u)+rel(v, j)+(t == 0 ? 0 : dp[t-1]));
64 }
65 y[j] = cur;
66 }
67 }
68
69 if(s[i] - s[s[i] - 1] == 1)
70 dp[i] = min(dp[u],
71 (u == 0 ? 0 : dp[u - 1]))
72 + rel(v, i)
73 + LL(i - u) * (p[v].ST - p[u].ST);
74 else
75 {
76 dp[i] = y[i];
77 if(i - u <= u - s[u] + 1)
78 dp[i] = min(dp[i], x[u - (i - v)] + rel(v, i));
79 }
80 }
81
82 return dp[n - 1];
83 }
84
85 int main()
86 {
87 auto t1 = clock();
88
89 std::freopen("../tests/5-29.in", "r", stdin) ;
90 std::freopen("wiring.out.txt", "w", stdout) ;
91
92 int n, m;
93 assert(2 == scanf("%d %d", &n, &m));
94
95 vector<int> r(n), b(m);
96 for(int i = 0; i < n; i++)
97 assert(1 == scanf("%d", &r[i]));
98 for(int i = 0; i < m; i++)
99 assert(1 == scanf("%d", &b[i]));
100
101 auto t2 = clock();
102
103 long long res = min_total_length(r, b);
104
105 auto t3 = clock();
106
107 printf("%lld\n", res);
108
109 auto t4 = clock();
110
111 // reset console output
112 freopen("CON", "w", stdout);
113
114 std::cout <<"res = "<<res<<’\n’<<’\n’;
115
116 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
117 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
118 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
119
120 return 0;
121 }
122 /*
123 res = 15991144105164
124
125 t2-t1 = 0.421
126 t3-t2 = 0.485
127 t4-t3 = 0
128
129 Process returned 0 (0x0) execution time : 0.953 s
130 Press any key to continue.
131 */
CAPITOLUL 4. IOI 2017 426

Listing 4.2.14: wiring-227979.cpp


1 // https://oj.uz/submission/227979 89 ms 12136 KB
2 #include "wiring.h"
3 #include <bits/stdc++.h>
4
5 #define ll long long
6 #define st first
7 #define nd second
8
9 using namespace std;
10
11 const ll INF = 1e15+1;
12
13 ll min_total_length(vector<int> r, vector<int> b)
14 {
15 int n = r.size(), m = b.size();
16 vector<pair<int, bool>> p;
17 for(int i: r)
18 {
19 p.push_back({i, 0});
20 }
21
22 for(int i: b)
23 {
24 p.push_back({i, 1});
25 }
26
27 sort(p.begin(), p.end());
28
29 vector<vector<ll>> block;
30 for(int i=0; i<n+m; ++i)
31 {
32 if(i==0 || p[i].nd!=p[i-1].nd)
33 {
34 block.push_back({});
35 }
36 block.back().push_back(p[i].st);
37 }
38
39 vector<vector<ll>> dp(2, vector<ll> (max(n,m)+1));
40 for(int i=1; i<=(int)block[0].size(); ++i)
41 {
42 dp[0][i]=INF;
43 }
44
45 auto prev=block[0];
46 block.erase(block.begin());
47 bool type = 1;
48 int nr=0;
49 for(auto i: block)
50 {
51 dp[type][0] = dp[!type][(int)prev.size()];
52 vector<ll> suf(prev.size()+1);
53 for(int j=1; j<=(int)prev.size(); ++j)
54 {
55 suf[j] = suf[j-1]+prev[(int)prev.size()-j];
56 }
57
58 ll pref=0;
59 int k = 0;
60 if(nr==0) k=(int)prev.size();
61 for(int j=1; j<=(int)i.size(); ++j)
62 {
63 pref+=i[j-1];
64 while(k<(int)prev.size())
65 {
66 ll v1 = pref - suf[k]
67 - max(0LL, (ll)j-k)*prev.back()
68 + max(0LL, (ll)k-j)*i[0]
69 + dp[!type][(int)prev.size()-k];
70
71 ll v2 = pref - suf[k+1]
72 - max(0LL, (ll)j-k-1)*prev.back()
73 + max(0LL, (ll)k+1-j)*i[0]
74 + dp[!type][(int)prev.size()-k-1];
CAPITOLUL 4. IOI 2017 427

75
76 if(v2<=v1)
77 k++;
78 else
79 break;
80 }
81
82 dp[type][j] = pref - suf[k]
83 - max(0LL, (ll)j-k)*prev.back()
84 + max(0LL, (ll)k-j)*i[0]
85 + dp[!type][(int)prev.size()-k];
86 }
87
88 prev = i;
89 type ^= 1;
90 nr++;
91 }
92
93 return dp[!type][(int)prev.size()];
94 }
95
96 int main()
97 {
98 auto t1 = clock();
99
100 std::freopen("../tests/5-29.in", "r", stdin) ;
101 std::freopen("wiring.out.txt", "w", stdout) ;
102
103 int n, m;
104 assert(2 == scanf("%d %d", &n, &m));
105
106 vector<int> r(n), b(m);
107 for(int i = 0; i < n; i++)
108 assert(1 == scanf("%d", &r[i]));
109 for(int i = 0; i < m; i++)
110 assert(1 == scanf("%d", &b[i]));
111
112 auto t2 = clock();
113
114 long long res = min_total_length(r, b);
115
116 auto t3 = clock();
117
118 printf("%lld\n", res);
119
120 auto t4 = clock();
121
122 // reset console output
123 freopen("CON", "w", stdout);
124
125 std::cout <<"res = "<<res<<’\n’<<’\n’;
126
127 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
128 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
129 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
130
131 return 0;
132 }
133 /*
134 res = 15991144105164
135
136 t2-t1 = 0.422
137 t3-t2 = 0.516
138 t4-t3 = 0
139
140 Process returned 0 (0x0) execution time : 0.984 s
141 Press any key to continue.
142 */
CAPITOLUL 4. IOI 2017 428

4.2.3 *Rezolvare detaliat 

4.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 ecare 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 a  î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: ecare staµie este
deµinuta de Arezou sau de Borzou. Exist  un singur tren. La începutul jocului trenul se a  î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 ecare data c nd trenul intra într-o staµie pentru prima data, proprietarul acelei staµii
xeaz  macazul din acea staµie.
Odat  ce macazul a fost xat, 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 nit 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  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  iniµial in statia s. Se cere pentru ecare joc sa se ae 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 e 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  e 0.
Exemplu

who_wins([0, 1], [1, 0], [0, 0, 1, 1], [0, 1, 0, 1])


CAPITOLUL 4. IOI 2017 429

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 ) semnica 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 xeaz  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 xeaz  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 ecare 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 ecare 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

4.3.1 Indicaµii de rezolvare

For a set of stations S , dene 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 denition). We dene 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 satises one of the following conditions, we add v to set T .
CAPITOLUL 4. IOI 2017 430

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

4.3.2 Coduri surs 

Listing 4.3.1: train.cpp


1 // http://ioi2017.org/tasks/materials/train.zip ---> sandbox/train.cpp
2 // http://ioi2017.org/tasks/materials/train.zip ---> solution/***.cpp
3
4 #include "train.h"
5 #include <vector>
6 #include <algorithm>
7
8 #include <cstdio> // grader ...
9 #include <cassert> // grader ...
10
11 #include <fstream> // ***
12
13 using namespace std;
14
15 ifstream cinf("01.in"); // ***
16
17 #define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)
18 #define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
19 #define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
20 #define sz(x) (int((x).size()))
21 #define pb push_back
22 #define all(X) (X).begin(),(X).end()
23 #define X first
24 #define Y second
25
26 template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }
27 template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }
28
29 typedef long long ll;
30 typedef pair<int, int> pii;
31
32 ////////////////////////////////////////////////////////////////////////////
33
34 const int maxn = 300000 + 100;
35
36 vector<int> radj[maxn];
37 int deg[maxn], need[maxn];
38
39 void dfs(int u, vector<int> &w)
40 {
41 for(int v: radj[u])
42 {
43 need[v]--;
44 if(!w[v] && need[v] == 0)
45 dfs(v, w); // only dfs through non-charging vertices
46 }
CAPITOLUL 4. IOI 2017 431

47 }
48
49 vector<int> who_wins(vector<int> a,vector<int> r,vector<int> u,vector<int> v)
50 {
51 int n = sz(a);
52 rep(i, sz(u)) radj[v[i]].pb(u[i]), deg[u[i]]++;
53 vector<int> res(n, 1);
54
55 for(bool cng = true; !(cng = !cng); )
56 {
57 rep(i, n)
58 need[i] = (a[i] ? 1 : deg[i]); // d only matters here
59
60 rep(i, n)
61 if(r[i] && res[i])
62 dfs(i, r); // dfs from yet-has-a-chance-to-win wells
63
64 rep(i, n)
65 if(r[i] && res[i] && need[i] > 0)
66 res[i] = false, cng = true; // and see if they had no chance
67 }
68
69 rep(i, n)
70 res[i] &= (need[i] <= 0); // look, more fopdoodles
71
72 return res;
73 }
74
75 int main() // grader ...
76 {
77 int n, m;
78 cinf >> n >> m; // ***
79
80 vector<int> a(n), r(n), u(m), v(m);
81
82 for(int i = 0; i < n; i++)
83 cinf >> a[i]; // ***
84
85 for(int i = 0; i < n; i++)
86 cinf >> r[i]; // ***
87
88 for(int i = 0; i < m; i++)
89 cinf >> u[i] >> v[i]; // ***
90
91 vector<int> res = who_wins(a, r, u, v);
92
93 for(int i = 0; i < (int)res.size(); i++)
94 printf(i ? " %d" : "%d", res[i]);
95 printf("\n");
96
97 return 0;
98 }

Listing 4.3.2: train+grader.cpp


1 #include "train.h"
2
3 #include <cstdio>
4 #include <vector>
5 #include <cassert>
6
7 #include <string>
8 #include <algorithm>
9 #include<ctime>
10 #include<iostream>
11
12 using namespace std;
13
14 // BEGIN SECRET
15 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
16 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
17 // END SECRET
18
19 // -----------------------------------------------------------
20
CAPITOLUL 4. IOI 2017 432

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


22 #define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
23 #define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
24 #define sz(x) (int((x).size()))
25 #define pb push_back
26 #define all(X) (X).begin(),(X).end()
27 #define X first
28 #define Y second
29
30 template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }
31 template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }
32
33 typedef long long ll;
34 typedef pair<int, int> pii;
35
36 ////////////////////////////////////////////////////////////////////////////
37
38 const int maxn = 300000 + 100;
39
40 vector<int> radj[maxn];
41 int deg[maxn], need[maxn];
42
43 void dfs(int u, vector<int> &w)
44 {
45 for(int v: radj[u])
46 {
47 need[v]--;
48 if(!w[v] && need[v] == 0)
49 dfs(v, w); // only dfs through non-charging vertices
50 }
51 }
52
53 vector<int> who_wins(vector<int> a, vector<int> r,
54 vector<int> u, vector<int> v)
55 {
56 int n = sz(a);
57 rep(i, sz(u)) radj[v[i]].pb(u[i]), deg[u[i]]++;
58 vector<int> res(n, 1);
59 for(bool cng = true; !(cng = !cng); )
60 {
61 rep(i, n)
62 need[i] = (a[i] ? 1 : deg[i]); // d only matters here
63 rep(i, n)
64 if(r[i] && res[i])
65 dfs(i, r); // dfs from yet-has-a-chance-to-win wells
66 rep(i, n)
67 if(r[i] && res[i] && need[i] > 0)
68 res[i] = false, cng = true; // and see if they had no chance
69 }
70
71 rep(i, n)
72 res[i] &= (need[i] <= 0); // look, more fopdoodles
73
74 return res;
75 }
76
77 // -----------------------------------------------------------
78
79 int main()
80 {
81 auto t1 = clock();
82
83 std::freopen("../tests/6-25.in", "r", stdin) ;
84 std::freopen("train.out.txt", "w", stdout) ;
85
86
87 // BEGIN SECRET
88 char secret[1000];
89 assert(1 == scanf("%s", secret));
90 if (string(secret) != input_secret)
91 {
92 printf("%s\n", output_secret.c_str());
93 printf("SV\n");
94 return 0;
95 }
96 // END SECRET
CAPITOLUL 4. IOI 2017 433

97
98 int n, m;
99 assert(2 == scanf("%d %d", &n, &m));
100
101 vector<int> a(n), r(n), u(m), v(m);
102
103 for(int i = 0; i < n; i++)
104 assert(1 == scanf("%d", &a[i]));
105
106 for(int i = 0; i < n; i++)
107 assert(1 == scanf("%d", &r[i]));
108
109 for(int i = 0; i < m; i++)
110 assert(2 == scanf("%d %d", &u[i], &v[i]));
111
112 auto t2 = clock();
113
114 vector<int> res = who_wins(a, r, u, v);
115
116 auto t3 = clock();
117
118 // BEGIN SECRET
119 printf("%s\n", output_secret.c_str());
120
121 if((int)res.size() != n)
122 {
123 printf("WA\n");
124 printf("Wrong returned array size\n");
125 }
126 else
127 printf("OK\n");
128 // END SECRET
129
130 for(int i = 0; i < (int)res.size(); i++)
131 printf(i ? " %d" : "%d", res[i]);
132 printf("\n");
133
134 auto t4 = clock();
135
136 // reset console output
137 freopen("CON", "w", stdout);
138
139 //std::cout <<"res = "<<res<<’\n’<<’\n’;
140
141 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
142 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
143 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
144
145 return 0;
146 }
147 /*
148 t2-t1 = 0.063
149 t3-t2 = 2.7
150 t4-t3 = 0
151
152 Process returned 0 (0x0) execution time : 2.857 s
153 Press any key to continue.
154 */

Listing 4.3.3: checkerModicat.cpp


1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
8
9 int main()
10 {
11 int argc=4;
12
13 char* argv[] =
14 {
CAPITOLUL 4. IOI 2017 434

15 (char*)"checker",
16 (char*)"../tests/6-25.in", // input
17 (char*)"../tests/6-25.out", // rezultat corect
18 (char*)"train.out.txt", // rezultat de verificat si acordat punctaj
19 };
20
21 cout<<"argc = "<<argc<<"\n";
22 for(int kk=0;kk<argc;kk++)
23 cout<<argv[kk]<<"\n";
24 cout<<"----------------------\n";
25
26 registerChecker("train", argc, argv);
27
28 readBothSecrets(output_secret);
29 readBothGraderResults();
30
31 compareRemainingLines(3);
32 }
33 /*
34 argc = 4
35 checker
36 ../tests/6-25.in
37 ../tests/6-25.out
38 train.out.txt
39 ----------------------
40 1
41 Correct
42
43 Process returned 0 (0x0) execution time : 0.109 s
44 Press any key to continue.
45 */

Listing 4.3.4: train-malek-ac.cpp


1 #include "train.h"
2
3 #include <cstdio>
4 #include <vector>
5 #include <cassert>
6
7 #include <string>
8 #include <algorithm>
9 #include<ctime>
10 #include<iostream>
11
12 #include <queue>
13 #include <cstring>
14
15 using namespace std;
16
17 // BEGIN SECRET
18 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
19 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
20 // END SECRET
21
22 // -----------------------------------------------------------
23
24 #define rep(i, n) for (int i = 0, _n = (int)(n); i < _n; i++)
25 #define fer(i, x, n) for (int i = (int)(x), _n = (int)(n); i < _n; i++)
26 #define rof(i, n, x) for (int i = (int)(n), _x = (int)(x); i-- > _x; )
27 #define sz(x) (int((x).size()))
28 #define pb push_back
29 #define all(X) (X).begin(),(X).end()
30 #define X first
31 #define Y second
32
33 template<class P, class Q> inline void smin(P &a, Q b) { if (b < a) a = b; }
34 template<class P, class Q> inline void smax(P &a, Q b) { if (a < b) a = b; }
35
36 typedef long long ll;
37 typedef pair<int, int> pii;
38
39 ///////////////////////////////////////////////////////////////////////////
40
41 const int maxn = 300000 + 100;
CAPITOLUL 4. IOI 2017 435

42
43 vector<int> radj[maxn];
44 int deg[maxn], need[maxn];
45
46 void dfs(int u, vector<int> &w)
47 {
48 for(int v: radj[u])
49 {
50 need[v]--;
51 if(!w[v] && need[v] == 0)
52 dfs(v, w); // only dfs through non-charging vertices
53 }
54 }
55
56 vector<int> who_wins(vector<int> a, vector<int> r,
57 vector<int> u, vector<int> v)
58 {
59 int n = sz(a);
60 rep(i, sz(u)) radj[v[i]].pb(u[i]), deg[u[i]]++;
61 vector<int> res(n, 1);
62 for(bool cng = true; !(cng = !cng); )
63 {
64 rep(i, n)
65 need[i] = (a[i] ? 1 : deg[i]); // d only matters here
66 rep(i, n)
67 if(r[i] && res[i])
68 dfs(i, r); // dfs from yet-has-a-chance-to-win wells
69 rep(i, n)
70 if(r[i] && res[i] && need[i] > 0)
71 res[i] = false, cng = true; // and see if they had no chance
72 }
73
74 rep(i, n)
75 res[i] &= (need[i] <= 0); // look, more fopdoodles
76 return res;
77 }
78 // -----------------------------------------------------------
79 int main()
80 {
81 auto t1 = clock();
82
83 std::freopen("../tests/6-25.in", "r", stdin) ;
84 std::freopen("train.out.txt", "w", stdout) ;
85
86
87 // BEGIN SECRET
88 char secret[1000];
89 assert(1 == scanf("%s", secret));
90 if (string(secret) != input_secret)
91 {
92 printf("%s\n", output_secret.c_str());
93 printf("SV\n");
94 return 0;
95 }
96 // END SECRET
97
98 int n, m;
99 assert(2 == scanf("%d %d", &n, &m));
100
101 vector<int> a(n), r(n), u(m), v(m);
102
103 for(int i = 0; i < n; i++)
104 assert(1 == scanf("%d", &a[i]));
105
106 for(int i = 0; i < n; i++)
107 assert(1 == scanf("%d", &r[i]));
108
109 for(int i = 0; i < m; i++)
110 assert(2 == scanf("%d %d", &u[i], &v[i]));
111
112 auto t2 = clock();
113
114 vector<int> res = who_wins(a, r, u, v);
115
116 auto t3 = clock();
117
CAPITOLUL 4. IOI 2017 436

118 // BEGIN SECRET


119 printf("%s\n", output_secret.c_str());
120
121 if((int)res.size() != n)
122 {
123 printf("WA\n");
124 printf("Wrong returned array size\n");
125 }
126 else
127 printf("OK\n");
128 // END SECRET
129
130 for(int i = 0; i < (int)res.size(); i++)
131 printf(i ? " %d" : "%d", res[i]);
132 printf("\n");
133
134 auto t4 = clock();
135
136 // reset console output
137 freopen("CON", "w", stdout);
138
139 //std::cout <<"res = "<<res<<’\n’<<’\n’;
140
141 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
142 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
143 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
144
145 return 0;
146 }
147 // -----------------------------------------------------------
148 /*
149 t2-t1 = 0.054
150 t3-t2 = 2.731
151 t4-t3 = 0.008
152
153 Process returned 0 (0x0) execution time : 2.876 s
154 Press any key to continue.
155 */

Listing 4.3.5: train-saeed-ac.cpp


1 #include "train.h"
2
3 #include <cstdio>
4 #include <vector>
5 #include <cassert>
6
7 //#include <string>
8 #include <algorithm>
9 #include<ctime>
10 #include<iostream>
11
12 #include <queue>
13 #include <cstring>
14
15 using namespace std;
16
17 // BEGIN SECRET
18 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
19 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
20 // END SECRET
21
22 // -----------------------------------------------------------
23
24 const int MAXN = 5000+10;
25
26 const int B = 0;
27 const int A = 1;
28
29 bool mark[MAXN], checked[MAXN];
30 int need[MAXN], f[MAXN];
31 vector<int> adj[MAXN], bak[MAXN];
32
33 vector<int> who_wins(vector<int> a, vector<int> r,
34 vector<int> u, vector<int> v)
CAPITOLUL 4. IOI 2017 437

35 {
36 for (int i = 0; i < (int)u.size(); i++)
37 {
38 adj[u[i]].push_back(v[i]);
39 bak[v[i]].push_back(u[i]);
40 }
41
42 int n = (int)a.size();
43 for (int i = 0; i < n; i++)
44 f[i] = A;
45
46 while (true)
47 {
48 for (int i = 0; i < n; i++) if (f[i] != B)
49 {
50 mark[i] = false;
51 checked[i] = false;
52 need[i] = a[i] == A ? 1 : (int)adj[i].size();
53 }
54
55 queue<int> q;
56 for (int i = 0; i < n; i++) if (r[i] && f[i] == A)
57 q.push(i);
58
59 while (!q.empty())
60 {
61 int front = q.front();
62 q.pop();
63 if (checked[front] == true)
64 continue;
65 checked[front] = true;
66 for (int i = 0; i < (int)bak[front].size(); i++)
67 {
68 int temp = bak[front][i];
69 --need[temp];
70 if (need[temp] == 0 && mark[temp] == false && f[temp] == A)
71 {
72 q.push(temp);
73 mark[temp] = true;
74 }
75 }
76 }
77
78 bool flag = false;
79 for (int i = 0; i < n; i++) if (f[i] == A && mark[i] == false)
80 {
81 f[i] = B;
82 flag = true;
83 }
84
85 if (!flag)
86 break;
87 }
88
89 vector<int> res;
90 for (int i = 0; i < n; i++)
91 res.push_back(f[i]);
92
93 return res;
94 }
95 // -----------------------------------------------------------
96 int main()
97 {
98 auto t1 = clock();
99
100 std::freopen("../tests/6-25.in", "r", stdin) ;
101 std::freopen("train.out.txt", "w", stdout) ;
102
103
104 // BEGIN SECRET
105 char secret[1000];
106 assert(1 == scanf("%s", secret));
107 if (string(secret) != input_secret)
108 {
109 printf("%s\n", output_secret.c_str());
110 printf("SV\n");
CAPITOLUL 4. IOI 2017 438

111 return 0;
112 }
113 // END SECRET
114
115 int n, m;
116 assert(2 == scanf("%d %d", &n, &m));
117
118 vector<int> a(n), r(n), u(m), v(m);
119
120 for(int i = 0; i < n; i++)
121 assert(1 == scanf("%d", &a[i]));
122
123 for(int i = 0; i < n; i++)
124 assert(1 == scanf("%d", &r[i]));
125
126 for(int i = 0; i < m; i++)
127 assert(2 == scanf("%d %d", &u[i], &v[i]));
128
129 auto t2 = clock();
130
131 vector<int> res = who_wins(a, r, u, v);
132
133 auto t3 = clock();
134
135 // BEGIN SECRET
136 printf("%s\n", output_secret.c_str());
137
138 if((int)res.size() != n)
139 {
140 printf("WA\n");
141 printf("Wrong returned array size\n");
142 }
143 else
144 printf("OK\n");
145 // END SECRET
146
147 for(int i = 0; i < (int)res.size(); i++)
148 printf(i ? " %d" : "%d", res[i]);
149 printf("\n");
150
151 auto t4 = clock();
152
153 // reset console output
154 freopen("CON", "w", stdout);
155
156 //std::cout <<"res = "<<res<<’\n’<<’\n’;
157
158 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
159 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
160 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
161
162 return 0;
163 }
164 // -----------------------------------------------------------
165 /*
166 t2-t1 = 0.054
167 t3-t2 = 3.325
168 t4-t3 = 0
169
170 Process returned 0 (0x0) execution time : 3.456 s
171 Press any key to continue.
172 */

Listing 4.3.6: train-134142.cpp


1 // https://oj.uz/submission/134142 688 ms 1528 KB
2
3 #include <bits/stdc++.h>
4
5 #include "train.h"
6
7 using namespace std;
8
9 // BEGIN SECRET
10 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
CAPITOLUL 4. IOI 2017 439

11 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";


12 // END SECRET
13
14 vector<int> who_wins(vector<int> a, vector<int> r,
15 vector<int> u, vector<int> v)
16 {
17 int n = a.size();
18 int m = u.size();
19 vector<int> deg(n);
20 vector<vector<int>> g(n);
21 for (int i = 0; i < m; ++i)
22 {
23 ++deg[u[i]];
24 g[v[i]].push_back(u[i]);
25 }
26
27 while (true)
28 {
29 vector<int> ans = r;
30 {
31 queue<int> q;
32 vector<int> cdeg = deg;
33 for (int i = 0; i < n; ++i)
34 {
35 if (ans[i])
36 {
37 q.push(i);
38 }
39 }
40
41 while (!q.empty())
42 {
43 int u = q.front(); q.pop();
44 for (int v : g[u])
45 {
46 if (!ans[v])
47 {
48 if (a[v] || (--cdeg[v]) == 0)
49 {
50 ans[v] = 1;
51 q.push(v);
52 }
53 }
54 }
55 }
56 }
57
58 {
59 queue<int> q;
60 vector<int> cdeg = deg;
61 for (int i = 0; i < n; ++i)
62 {
63 if (!ans[i])
64 {
65 q.push(i);
66 }
67 }
68
69 while (!q.empty())
70 {
71 int u = q.front(); q.pop();
72 for (int v : g[u])
73 {
74 if (ans[v])
75 {
76 if (!a[v] || (--cdeg[v]) == 0)
77 {
78 ans[v] = 0;
79 q.push(v);
80 }
81 }
82 }
83 }
84 }
85
86 bool finish = true;
CAPITOLUL 4. IOI 2017 440

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


88 {
89 if (!ans[i] && r[i])
90 {
91 r[i] = 0;
92 finish = false;
93 }
94 }
95
96 if (finish)
97 {
98 return ans;
99 }
100 }
101 }
102 // -----------------------------------------------------------
103 int main()
104 {
105 auto t1 = clock();
106
107 std::freopen("../tests/6-25.in", "r", stdin) ;
108 std::freopen("train.out.txt", "w", stdout) ;
109
110 // BEGIN SECRET
111 char secret[1000];
112 assert(1 == scanf("%s", secret));
113 if (string(secret) != input_secret)
114 {
115 printf("%s\n", output_secret.c_str());
116 printf("SV\n");
117 return 0;
118 }
119 // END SECRET
120
121 int n, m;
122 assert(2 == scanf("%d %d", &n, &m));
123
124 vector<int> a(n), r(n), u(m), v(m);
125
126 for(int i = 0; i < n; i++)
127 assert(1 == scanf("%d", &a[i]));
128
129 for(int i = 0; i < n; i++)
130 assert(1 == scanf("%d", &r[i]));
131
132 for(int i = 0; i < m; i++)
133 assert(2 == scanf("%d %d", &u[i], &v[i]));
134
135 auto t2 = clock();
136
137 vector<int> res = who_wins(a, r, u, v);
138
139 auto t3 = clock();
140
141 // BEGIN SECRET
142 printf("%s\n", output_secret.c_str());
143
144 if((int)res.size() != n)
145 {
146 printf("WA\n");
147 printf("Wrong returned array size\n");
148 }
149 else
150 printf("OK\n");
151 // END SECRET
152
153 for(int i = 0; i < (int)res.size(); i++)
154 printf(i ? " %d" : "%d", res[i]);
155 printf("\n");
156
157 auto t4 = clock();
158
159 // reset console output
160 freopen("CON", "w", stdout);
161
162 //std::cout <<"res = "<<res<<’\n’<<’\n’;
CAPITOLUL 4. IOI 2017 441

163
164 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
165 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
166 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
167
168 return 0;
169 }
170 // -----------------------------------------------------------
171 /*
172 t2-t1 = 0.078
173 t3-t2 = 5.147
174 t4-t3 = 0
175
176 Process returned 0 (0x0) execution time : 5.273 s
177 Press any key to continue.
178 */

Listing 4.3.7: train-146520.cpp


1 // https://oj.uz/submission/146520 588 ms 2552 KB
2
3 #include <bits/stdc++.h>
4 #include "train.h"
5
6 #define loop(i, a, b) for(int i=a;i<b;i++)
7 #define maxn 20100
8
9 using namespace std;
10
11 // BEGIN SECRET
12 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
13 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
14 // END SECRET
15
16 vector<int> ba[maxn], fo[maxn], ans, req, po;
17
18 void dfs(int u)
19 {
20 loop(i, 0, ba[u].size())
21 {
22 int v=ba[u][i];
23 req[v]--;
24 if(!req[v] && !po[v])
25 dfs(v);
26 }
27 };
28
29 vector<int> who_wins(vector<int> A, vector<int> C,
30 vector<int> U, vector<int> V)
31 {
32 int n=A.size(), m=U.size();
33 po.resize(n);
34 loop(i, 0, n)
35 po[i]=C[i];
36 loop(i, 0, m)
37 ba[V[i]].push_back(U[i]), fo[U[i]].push_back(V[i]);
38
39 ans.resize(n, 1), req.resize(n);
40
41 bool cn=1;
42 while(cn)
43 {
44 cn=0;
45 loop(i, 0, n) req[i]=(A[i])? 1:fo[i].size();
46 loop(i, 0, n) if(po[i]&&ans[i]) dfs(i);
47 loop(i, 0, n) if(ans[i]!=(req[i]<1)) ans[i]=(req[i]<1), cn=1;
48 }
49
50 return ans;
51 }
52 // -----------------------------------------------------------
53 int main()
54 {
55 auto t1 = clock();
56
CAPITOLUL 4. IOI 2017 442

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


58 std::freopen("train.out.txt", "w", stdout) ;
59
60
61 // BEGIN SECRET
62 char secret[1000];
63 assert(1 == scanf("%s", secret));
64 if (string(secret) != input_secret)
65 {
66 printf("%s\n", output_secret.c_str());
67 printf("SV\n");
68 return 0;
69 }
70 // END SECRET
71
72 int n, m;
73 assert(2 == scanf("%d %d", &n, &m));
74
75 vector<int> a(n), r(n), u(m), v(m);
76
77 for(int i = 0; i < n; i++)
78 assert(1 == scanf("%d", &a[i]));
79
80 for(int i = 0; i < n; i++)
81 assert(1 == scanf("%d", &r[i]));
82
83 for(int i = 0; i < m; i++)
84 assert(2 == scanf("%d %d", &u[i], &v[i]));
85
86 auto t2 = clock();
87
88 vector<int> res = who_wins(a, r, u, v);
89
90 auto t3 = clock();
91
92 // BEGIN SECRET
93 printf("%s\n", output_secret.c_str());
94
95 if((int)res.size() != n)
96 {
97 printf("WA\n");
98 printf("Wrong returned array size\n");
99 }
100 else
101 printf("OK\n");
102 // END SECRET
103
104 for(int i = 0; i < (int)res.size(); i++)
105 printf(i ? " %d" : "%d", res[i]);
106 printf("\n");
107
108 auto t4 = clock();
109
110 // reset console output
111 freopen("CON", "w", stdout);
112
113 //std::cout <<"res = "<<res<<’\n’<<’\n’;
114
115 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
116 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
117 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
118
119 return 0;
120 }
121 // -----------------------------------------------------------
122 /*
123 t2-t1 = 0.062
124 t3-t2 = 2.386
125 t4-t3 = 0
126
127 Process returned 0 (0x0) execution time : 2.495 s
128 Press any key to continue.
129 */

Listing 4.3.8: train-160375.cpp


CAPITOLUL 4. IOI 2017 443

1 // https://oj.uz/submission/160375
2
3 #include "train.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 // BEGIN SECRET
9 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
10 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
11 // END SECRET
12
13 const int AREZOU = 1;
14 const int BORZOU = 0;
15 const int CHARGING_STATION = 1;
16 const int CHARGING_SET = 1;
17 const int VISITED = 1;
18
19 int N, M;
20 vector<int> station, owner;
21 vector<vector<int>> G, G_inverse;
22
23 vector<int> out_degree;
24 vector<int> visited;
25 vector<int> charging_set;
26 vector<int> new_charging_set;
27
28 void initialize_charging_set()
29 {
30 visited.assign(N, !VISITED);
31 out_degree.assign(N, 0);
32 charging_set.assign(N, !CHARGING_SET);
33
34 queue<int> q;
35 for (int i = 0; i < N; i++)
36 if (station[i] == CHARGING_STATION &&
37 new_charging_set[i] == CHARGING_SET)
38 q.push(i), visited[i] = VISITED;
39
40 while (!q.empty())
41 {
42 int u = q.front(); q.pop();
43 charging_set[u] = CHARGING_SET;
44 for (auto v : G_inverse[u])
45 {
46 if (visited[v] == VISITED) continue;
47
48 switch (owner[v])
49 {
50 case AREZOU:
51 if (visited[v] == !VISITED)
52 {
53 visited[v] = VISITED;
54 q.push(v);
55 }
56 break;
57 case BORZOU:
58 out_degree[v]++;
59 if (out_degree[v] == G[v].size() &&
60 visited[v] == !VISITED)
61 {
62 visited[v] = VISITED;
63 q.push(v);
64 }
65 break;
66 }
67 }
68 }
69
70 }
71
72 vector<int> who_wins(vector<int> owner_, vector<int> charge_,
73 vector<int> u_, vector<int> v_)
74 {
75 owner = owner_, station = charge_;
CAPITOLUL 4. IOI 2017 444

76 N = owner.size(), M = u_.size();
77 G.resize(N), G_inverse.resize(N);
78 new_charging_set.resize(N, CHARGING_SET);
79
80 for (int i = 0; i < M; i++)
81 {
82 G[u_[i]].push_back(v_[i]);
83 G_inverse[v_[i]].push_back(u_[i]);
84 }
85
86 while (charging_set != new_charging_set)
87 {
88 initialize_charging_set();
89
90 for (int i = 0; i < N; i++)
91 {
92 switch (owner[i])
93 {
94 case AREZOU:
95 new_charging_set[i] = !CHARGING_SET;
96 for (auto v : G[i])
97 if (charging_set[v] == CHARGING_SET)
98 new_charging_set[i] = CHARGING_SET;
99
100 break;
101
102 case BORZOU:
103 new_charging_set[i] = CHARGING_SET;
104 for (auto v : G[i])
105 if (charging_set[v] == !CHARGING_SET)
106 new_charging_set[i] = !CHARGING_SET;
107
108 break;
109 }
110 }
111
112 }
113
114 vector<int> res(N);
115 for (int i = 0; i < N; i++)
116 res[i] = (charging_set[i] == CHARGING_SET);
117
118 return res;
119 }
120 // -----------------------------------------------------------
121 int main()
122 {
123 auto t1 = clock();
124
125 std::freopen("../tests/6-25.in", "r", stdin) ;
126 std::freopen("train.out.txt", "w", stdout) ;
127
128
129 // BEGIN SECRET
130 char secret[1000];
131 assert(1 == scanf("%s", secret));
132 if (string(secret) != input_secret)
133 {
134 printf("%s\n", output_secret.c_str());
135 printf("SV\n");
136 return 0;
137 }
138 // END SECRET
139
140 int n, m;
141 assert(2 == scanf("%d %d", &n, &m));
142
143 vector<int> a(n), r(n), u(m), v(m);
144
145 for(int i = 0; i < n; i++)
146 assert(1 == scanf("%d", &a[i]));
147
148 for(int i = 0; i < n; i++)
149 assert(1 == scanf("%d", &r[i]));
150
151 for(int i = 0; i < m; i++)
CAPITOLUL 4. IOI 2017 445

152 assert(2 == scanf("%d %d", &u[i], &v[i]));


153
154 auto t2 = clock();
155
156 vector<int> res = who_wins(a, r, u, v);
157
158 auto t3 = clock();
159
160 // BEGIN SECRET
161 printf("%s\n", output_secret.c_str());
162
163 if((int)res.size() != n)
164 {
165 printf("WA\n");
166 printf("Wrong returned array size\n");
167 }
168 else
169 printf("OK\n");
170 // END SECRET
171
172 for(int i = 0; i < (int)res.size(); i++)
173 printf(i ? " %d" : "%d", res[i]);
174 printf("\n");
175
176 auto t4 = clock();
177
178 // reset console output
179 freopen("CON", "w", stdout);
180
181 //std::cout <<"res = "<<res<<’\n’<<’\n’;
182
183 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
184 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
185 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
186
187 return 0;
188 }
189 // -----------------------------------------------------------
190 /*
191 t2-t1 = 0.078
192 t3-t2 = 6.343
193 t4-t3 = 0
194
195 Process returned 0 (0x0) execution time : 6.469 s
196 Press any key to continue.
197 */

Listing 4.3.9: train-163181.cpp


1 // https://oj.uz/submission/163181 957 ms 1656 KB
2
3 #include "train.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 // BEGIN SECRET
9 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
10 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
11 // END SECRET
12
13 #define all(aaa) aaa.begin(), aaa.end();
14 #define ll long long
15
16 const int N = 5005;
17 vector<int> g[N];
18 int deg[N], tmp[N];
19 bool used[N];
20 int n, m;
21
22 vector<int> reachable(vector<int> &a, vector<int> v, int master)
23 {
24 for (int i = 0; i < n; i++)
25 {
26 used[i] = 0;
CAPITOLUL 4. IOI 2017 446

27 tmp[i] = deg[i];
28 }
29
30 queue<int> q;
31
32 auto check = [&](int x)
33 {
34 if (!used[x])
35 {
36 if (a[x] == master)
37 {
38 used[x] = 1;
39 q.push(x);
40 }
41 else
42 if (--tmp[x] == 0)
43 {
44 used[x] = 1;
45 q.push(x);
46 }
47 }
48 };
49
50 for (int x : v)
51 {
52 used[x] = 1;
53 q.push(x);
54 }
55
56 while (!q.empty())
57 {
58 int node = q.front();
59 q.pop();
60
61 for (int to : g[node])
62 {
63 check(to);
64 }
65 }
66
67 vector<int> ans;
68 for (int i = 0; i < n; i++)
69 {
70 if (used[i])
71 ans.push_back(i);
72 }
73
74 return ans;
75 }
76
77 vector<int> who_wins(vector<int> a, vector<int> r,
78 vector<int> u, vector<int> v)
79 {
80 n = a.size(), m = u.size();
81
82 for (int i = 0; i < m; i++)
83 {
84 g[v[i]].push_back(u[i]);
85 deg[u[i]]++;
86 }
87
88 vector<int> res(n, 1);
89
90 bool work = true;
91
92 while (work)
93 {
94 work = false;
95
96 vector<int> sts;
97 for (int i = 0; i < n; i++)
98 {
99 if (r[i])
100 sts.push_back(i);
101 }
102
CAPITOLUL 4. IOI 2017 447

103 vector<int> good = reachable(a, sts, 1), killers;


104
105 for (int i = 0, j = 0; i < n; i++)
106 {
107 while (j < good.size() && good[j] < i)
108 j++;
109 if (j == good.size() ||
110 good[j] != i)
111 killers.push_back(i);
112 }
113
114 vector<int> bad = reachable(a, killers, 0);
115
116 for (int x : bad)
117 {
118 if (r[x])
119 work = true;
120 res[x] = 0;
121 r[x] = 0;
122 }
123 }
124
125 return res;
126 }
127 // -----------------------------------------------------------
128 int main()
129 {
130 auto t1 = clock();
131
132 std::freopen("../tests/6-25.in", "r", stdin) ;
133 std::freopen("train.out.txt", "w", stdout) ;
134
135
136 // BEGIN SECRET
137 char secret[1000];
138 assert(1 == scanf("%s", secret));
139 if (string(secret) != input_secret)
140 {
141 printf("%s\n", output_secret.c_str());
142 printf("SV\n");
143 return 0;
144 }
145 // END SECRET
146
147 int n, m;
148 assert(2 == scanf("%d %d", &n, &m));
149
150 vector<int> a(n), r(n), u(m), v(m);
151
152 for(int i = 0; i < n; i++)
153 assert(1 == scanf("%d", &a[i]));
154
155 for(int i = 0; i < n; i++)
156 assert(1 == scanf("%d", &r[i]));
157
158 for(int i = 0; i < m; i++)
159 assert(2 == scanf("%d %d", &u[i], &v[i]));
160
161 auto t2 = clock();
162
163 vector<int> res = who_wins(a, r, u, v);
164
165 auto t3 = clock();
166
167 // BEGIN SECRET
168 printf("%s\n", output_secret.c_str());
169
170 if((int)res.size() != n)
171 {
172 printf("WA\n");
173 printf("Wrong returned array size\n");
174 }
175 else
176 printf("OK\n");
177 // END SECRET
178
CAPITOLUL 4. IOI 2017 448

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


180 printf(i ? " %d" : "%d", res[i]);
181 printf("\n");
182
183 auto t4 = clock();
184
185 // reset console output
186 freopen("CON", "w", stdout);
187
188 //std::cout <<"res = "<<res<<’\n’<<’\n’;
189
190 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
191 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
192 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
193
194 return 0;
195 }
196 // -----------------------------------------------------------
197 /*
198 t2-t1 = 0.078
199 t3-t2 = 7.346
200 t4-t3 = 0
201
202 Process returned 0 (0x0) execution time : 7.472 s
203 Press any key to continue.
204 */

Listing 4.3.10: train-201183.cpp


1 // https://oj.uz/submission/201183 348 ms 1532 KB
2
3 #include "train.h"
4
5 #include <algorithm>
6 #include <vector>
7 #include<string>
8 #include<ctime>
9 #include<cassert>
10 #include<iostream>
11
12 using namespace std;
13
14 // BEGIN SECRET
15 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
16 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
17 // END SECRET
18
19 const int MAXN = 5e3;
20
21 int owner[MAXN], charge[MAXN];
22 std::vector<int> graph[MAXN];
23 int degree[MAXN];
24
25 std::vector<int> undet;
26 std::vector<int> removing;
27 bool toRemove[MAXN], removed[MAXN];
28 int currentDeg[MAXN];
29
30
31 void reset()
32 {
33 for (int node : undet)
34 {
35 toRemove[node] = false;
36 removed[node] = false;
37 currentDeg[node] = degree[node];
38 }
39 }
40
41 void flipowners()
42 {
43 for (int node : undet)
44 owner[node] = 1 - owner[node];
45 }
46
CAPITOLUL 4. IOI 2017 449

47 void removal()
48 {
49 for (int node : undet)
50 if (toRemove[node])
51 {
52 removing.emplace_back(node);
53 removed[node] = true;
54 }
55
56 while (!removing.empty())
57 {
58 int rm = removing.back();
59 removing.pop_back();
60
61 for (int next : graph[rm])
62 if (!removed[next])
63 {
64 currentDeg[next]--;
65 if (owner[next] == 1 || currentDeg[next] == 0)
66 {
67 removing.emplace_back(next);
68 removed[next] = true;
69 }
70 }
71 }
72 }
73
74 void findlosers()
75 {
76 reset();
77 for (int node : undet)
78 toRemove[node] = charge[node];
79 removal();
80
81 std::vector<int> losers;
82 for (int node : undet)
83 if (!removed[node])
84 losers.emplace_back(node);
85
86 reset();
87 flipowners();
88
89 for (int node : losers)
90 toRemove[node] = true;
91 removal();
92 flipowners();
93 }
94
95 std::vector<int> who_wins(std::vector<int> a, std::vector<int> r,
96 std::vector<int> u, std::vector<int> v)
97 {
98 int stations = a.size();
99 int tracks = u.size();
100
101 std::vector<int> winner(stations, 0);
102 for (int iNode = 0; iNode < stations; iNode++)
103 {
104 owner[iNode] = a[iNode];
105 charge[iNode] = r[iNode];
106 undet.emplace_back(iNode);
107 }
108
109 for (int iEdge = 0; iEdge < tracks; iEdge++)
110 {
111 graph[v[iEdge]].emplace_back(u[iEdge]);
112 degree[u[iEdge]]++;
113 }
114
115 while (!undet.empty())
116 {
117 findlosers();
118 std::vector<int> losers;
119
120 for (int node : undet)
121 if (removed[node])
122 losers.emplace_back(node);
CAPITOLUL 4. IOI 2017 450

123
124 if (losers.empty())
125 {
126 for (int node : undet)
127 winner[node] = 1;
128 undet.clear();
129 }
130 else
131 {
132 for (int node : losers)
133 {
134 for (int next : graph[node])
135 degree[next]--;
136 undet.erase(std::find(undet.begin(), undet.end(), node));
137 }
138 }
139 }
140
141 return winner;
142 }
143 // -----------------------------------------------------------
144 int main()
145 {
146 auto t1 = clock();
147
148 std::freopen("../tests/6-25.in", "r", stdin) ;
149 std::freopen("train.out.txt", "w", stdout) ;
150
151
152 // BEGIN SECRET
153 char secret[1000];
154 assert(1 == scanf("%s", secret));
155 if (string(secret) != input_secret)
156 {
157 printf("%s\n", output_secret.c_str());
158 printf("SV\n");
159 return 0;
160 }
161 // END SECRET
162
163 int n, m;
164 assert(2 == scanf("%d %d", &n, &m));
165
166 vector<int> a(n), r(n), u(m), v(m);
167
168 for(int i = 0; i < n; i++)
169 assert(1 == scanf("%d", &a[i]));
170
171 for(int i = 0; i < n; i++)
172 assert(1 == scanf("%d", &r[i]));
173
174 for(int i = 0; i < m; i++)
175 assert(2 == scanf("%d %d", &u[i], &v[i]));
176
177 auto t2 = clock();
178
179 vector<int> res = who_wins(a, r, u, v);
180
181 auto t3 = clock();
182
183 // BEGIN SECRET
184 printf("%s\n", output_secret.c_str());
185
186 if((int)res.size() != n)
187 {
188 printf("WA\n");
189 printf("Wrong returned array size\n");
190 }
191 else
192 printf("OK\n");
193 // END SECRET
194
195 for(int i = 0; i < (int)res.size(); i++)
196 printf(i ? " %d" : "%d", res[i]);
197 printf("\n");
198
CAPITOLUL 4. IOI 2017 451

199 auto t4 = clock();


200
201 // reset console output
202 freopen("CON", "w", stdout);
203
204 //std::cout <<"res = "<<res<<’\n’<<’\n’;
205
206 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
207 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
208 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
209
210 return 0;
211 }
212 // -----------------------------------------------------------
213 /*
214 t2-t1 = 0.062
215 t3-t2 = 3.797
216 t4-t3 = 0
217
218 Process returned 0 (0x0) execution time : 3.922 s
219 Press any key to continue.
220 */

Listing 4.3.11: train-221800.cpp


1 // https://oj.uz/submission/221800 494 ms 1792 KB
2 /**
3 * user: ppavic
4 * fname: Patrik
5 * lname: PaviÄĞ
6 * task: train
7 * score: 100.0
8 * date: 2019-06-12 09:27:16.425930
9 */
10 #include "train.h"
11
12 #include <vector>
13 #include <cstring>
14 #include <queue>
15 #include<string>
16 #include<ctime>
17 #include<cassert>
18 #include<iostream>
19
20 #define PB push_back
21
22 using namespace std;
23
24 // BEGIN SECRET
25 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
26 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
27 // END SECRET
28
29 typedef vector < int > vi;
30
31 const int N = 5055;
32
33 int spas[N]; /// HDZ
34 int smrt[N]; /// SDP
35 int n;
36
37 vi v[N], r[N];
38 int cnt[N], izb[N];
39 queue < int > Q;
40
41 vi who_wins(vi a, vi rr, vi uu, vi vv)
42 {
43 n = a.size();
44 for(int i = 0;i < (int)vv.size();i++)
45 {
46 v[uu[i]].PB(vv[i]);
47 r[vv[i]].PB(uu[i]);
48 }
49 int kol = 0;
50 for(;;kol++)
CAPITOLUL 4. IOI 2017 452

51 {
52 memset(spas, 0, sizeof(spas));
53 memset(smrt, 0, sizeof(smrt));
54 memset(cnt, 0, sizeof(cnt));
55
56 for(int i = 0;i < n;i++)
57 {
58 if(izb[i]) continue;
59 if(rr[i] == 1) smrt[i] = 1, Q.push(i);
60 else
61 if(a[i] == 1) cnt[i] = 1;
62 else
63 {
64 cnt[i] = 0;
65 for(int x : v[i])
66 cnt[i] += !izb[x];
67 }
68 }
69
70 for(;!Q.empty();Q.pop())
71 {
72 int cur = Q.front();
73 for(int x : r[cur])
74 {
75 if(izb[x]) continue;
76 cnt[x]--;
77 if(cnt[x] == 0 && !smrt[x])
78 {
79 smrt[x] = 1;
80 Q.push(x);
81 }
82 }
83 }
84
85 for(int i = 0;i < n;i++)
86 {
87 if(izb[i]) continue;
88 if(smrt[i] == 0) spas[i] = 1, Q.push(i), cnt[i] = 0;
89 else
90 if(a[i] == 0) cnt[i] = 1;
91 else
92 {
93 cnt[i] = 0;
94 for(int x : v[i])
95 cnt[i] += !izb[x];
96 }
97 }
98
99 for(;!Q.empty();Q.pop())
100 {
101 int cur = Q.front();
102 for(int x : r[cur])
103 {
104 if(izb[x]) continue;
105 cnt[x]--;
106 if(cnt[x] == 0 && !spas[x])
107 {
108 spas[x] = 1;
109 Q.push(x);
110 }
111 }
112 }
113
114 int cc = 0;
115 for(int i = 0;i < n;i++)
116 if(spas[i] && !izb[i]) cc++, izb[i] = 1;
117 if(cc == 0) break;
118 }
119
120 vi sol;
121
122 for(int i = 0;i < n;i++)
123 sol.PB(!izb[i]);
124 return sol;
125 }
126 // -----------------------------------------------------------
CAPITOLUL 4. IOI 2017 453

127 int main()


128 {
129 auto t1 = clock();
130
131 std::freopen("../tests/6-25.in", "r", stdin) ;
132 std::freopen("train.out.txt", "w", stdout) ;
133
134
135 // BEGIN SECRET
136 char secret[1000];
137 assert(1 == scanf("%s", secret));
138 if (string(secret) != input_secret)
139 {
140 printf("%s\n", output_secret.c_str());
141 printf("SV\n");
142 return 0;
143 }
144 // END SECRET
145
146 int n, m;
147 assert(2 == scanf("%d %d", &n, &m));
148
149 vector<int> a(n), r(n), u(m), v(m);
150
151 for(int i = 0; i < n; i++)
152 assert(1 == scanf("%d", &a[i]));
153
154 for(int i = 0; i < n; i++)
155 assert(1 == scanf("%d", &r[i]));
156
157 for(int i = 0; i < m; i++)
158 assert(2 == scanf("%d %d", &u[i], &v[i]));
159
160 auto t2 = clock();
161
162 vector<int> res = who_wins(a, r, u, v);
163
164 auto t3 = clock();
165
166 // BEGIN SECRET
167 printf("%s\n", output_secret.c_str());
168
169 if((int)res.size() != n)
170 {
171 printf("WA\n");
172 printf("Wrong returned array size\n");
173 }
174 else
175 printf("OK\n");
176 // END SECRET
177
178 for(int i = 0; i < (int)res.size(); i++)
179 printf(i ? " %d" : "%d", res[i]);
180 printf("\n");
181
182 auto t4 = clock();
183
184 // reset console output
185 freopen("CON", "w", stdout);
186
187 //std::cout <<"res = "<<res<<’\n’<<’\n’;
188
189 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
190 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
191 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
192
193 return 0;
194 }
195 // -----------------------------------------------------------
196 /*
197 t2-t1 = 0.062
198 t3-t2 = 3.562
199 t4-t3 = 0
200
201 Process returned 0 (0x0) execution time : 3.688 s
202 Press any key to continue.
CAPITOLUL 4. IOI 2017 454

203 */

Listing 4.3.12: train-222137.cpp


1 // https://oj.uz/submission/222137 1922 ms 1792 KB
2
3 #include "train.h"
4 #include <algorithm>
5 #include <iostream>
6 #include <vector>
7 #include <queue>
8 #include <set>
9 #include<ctime>
10 #include<cassert>
11
12 using namespace std;
13
14 // BEGIN SECRET
15 const string input_secret = "3d2051c242fe2ae63792f9868123a5eb";
16 const string output_secret = "d16905a4427c193ad87ae7fa91a2bb55";
17 // END SECRET
18
19 const int maxN = 5000;
20
21 vector<int> nei[maxN];
22 int charge[maxN], a[maxN];
23 int ans[maxN];
24 int col[maxN], isCyc[maxN];
25 int n;
26
27 vector<int> rnei[maxN];
28 bool used1[maxN];
29 void DFS1(int v, vector<int>& sorted)
30 {
31 used1[v] = true;
32 for (int to: nei[v])
33 if (!used1[to])
34 DFS1(to, sorted);
35 sorted.push_back(v);
36 }
37
38 void DFS2(int v, int curcol)
39 {
40 col[v] = curcol;
41 for (int to: rnei[v])
42 if (!col[to])
43 {
44 DFS2(to, curcol);
45 isCyc[curcol] = true;
46 }
47 else if (col[to] == curcol)
48 isCyc[curcol] = true;
49 }
50
51 void SCC()
52 {
53 vector<int> sorted;
54 for (int i = 0; i < n; ++i)
55 if (!used1[i])
56 DFS1(i, sorted);
57 reverse(sorted.begin(), sorted.end());
58 int cols = 0;
59 for (int v: sorted)
60 if (!col[v])
61 DFS2(v, ++cols);
62 }
63
64 bool used3[maxN];
65
66 void DFS3(int v)
67 {
68 used3[v] = true;
69 ans[v] = 1;
70 for (int to: rnei[v])
71 if (!used3[to])
CAPITOLUL 4. IOI 2017 455

72 DFS3(to);
73 }
74
75 vector<int> Solve3()
76 {
77 SCC();
78 vector<bool> goodcol(n);
79 for (int v = 0; v < n; ++v)
80 if (charge[v])
81 goodcol[col[v]] = true;
82 for (int v = 0; v < n; ++v)
83 if (goodcol[col[v]] && isCyc[col[v]] && !used3[v])
84 DFS3(v);
85 return vector<int>(ans, ans + n);
86 }
87
88 bool OK(vector<char>& mask)
89 {
90 queue<int> cycle;
91 for (int i = 0; i < n; ++i)
92 if (mask[i])
93 cycle.push(i);
94 int cnt = cycle.size();
95 while (cycle.size())
96 {
97 int u = cycle.front();
98 cycle.pop();
99 if (!mask[u]) continue;
100 bool g = false;
101
102 if (a[u])
103 {
104 g = true;
105 for (int to: nei[u])
106 if (!mask[to] && ans[to] != 0)
107 g = false;
108 }
109 else
110 {
111 g = false;
112 for (int to: nei[u])
113 if (mask[to] || ans[to] == 0)
114 g = true;
115 }
116
117 if (!g)
118 {
119 --cnt;
120 mask[u] = 0;
121 for (int to: rnei[u])
122 if (mask[to])
123 cycle.push(to);
124 }
125 }
126 return cnt;
127 }
128
129 void Add(vector<char>& mask)
130 {
131 queue<int> cycle;
132 for (int i = 0; i < n; ++i)
133 if (mask[i])
134 for (int to: rnei[i])
135 cycle.push(to);
136
137 while (cycle.size())
138 {
139 int u = cycle.front();
140 cycle.pop();
141 if (!ans[u]) continue;
142 bool g = false;
143 if (a[u])
144 {
145 g = true;
146 for (int to: nei[u])
147 if (ans[to] != 0)
CAPITOLUL 4. IOI 2017 456

148 g = false;
149 }
150 else
151 {
152 g = false;
153 for (int to: nei[u])
154 if (ans[to] == 0)
155 g = true;
156 }
157 if (g)
158 {
159 ans[u] = 0;
160 for (int to: rnei[u])
161 if (ans[to])
162 cycle.push(to);
163 }
164 }
165 }
166
167 vector<int> Solve()
168 {
169 for (int i = 0; i < n; ++i)
170 ans[i] = 1;
171
172 vector<char> stmask(n);
173
174 for (int i = 0; i < n; ++i)
175 if (!charge[i])
176 stmask[i] = 1;
177
178 while (true)
179 {
180 auto mask = stmask;
181 if (!OK(mask))
182 break;
183 for (int i = 0; i < n; ++i)
184 if (mask[i])
185 {
186 ans[i] = 0;
187 stmask[i] = 0;
188 }
189 Add(mask);
190 }
191
192 return vector<int>(ans, ans + n);
193 }
194
195 std::vector<int> who_wins(std::vector<int> a_, std::vector<int> charge_,
196 std::vector<int> u, std::vector<int> v)
197 {
198 n = a_.size();
199 for (int i = 0; i < n; ++i)
200 a[i] = a_[i], charge[i] = charge_[i];
201 for (int i = 0; i < u.size(); ++i)
202 nei[u[i]].push_back(v[i]);
203 for (int v = 0; v < n; ++v)
204 for (int to: nei[v])
205 rnei[to].push_back(v);
206 if (!count(a, a + n, 0))
207 return Solve3();
208 return Solve();
209 }
210 // -----------------------------------------------------------
211 int main()
212 {
213 auto t1 = clock();
214
215 std::freopen("../tests/6-25.in", "r", stdin) ;
216 std::freopen("train.out.txt", "w", stdout) ;
217
218 // BEGIN SECRET
219 char secret[1000];
220 assert(1 == scanf("%s", secret));
221 if (string(secret) != input_secret)
222 {
223 printf("%s\n", output_secret.c_str());
CAPITOLUL 4. IOI 2017 457

224 printf("SV\n");
225 return 0;
226 }
227 // END SECRET
228
229 int n, m;
230 assert(2 == scanf("%d %d", &n, &m));
231
232 vector<int> a(n), r(n), u(m), v(m);
233
234 for(int i = 0; i < n; i++)
235 assert(1 == scanf("%d", &a[i]));
236
237 for(int i = 0; i < n; i++)
238 assert(1 == scanf("%d", &r[i]));
239
240 for(int i = 0; i < m; i++)
241 assert(2 == scanf("%d %d", &u[i], &v[i]));
242
243 auto t2 = clock();
244
245 vector<int> res = who_wins(a, r, u, v);
246
247 auto t3 = clock();
248
249 // BEGIN SECRET
250 printf("%s\n", output_secret.c_str());
251
252 if((int)res.size() != n)
253 {
254 printf("WA\n");
255 printf("Wrong returned array size\n");
256 }
257 else
258 printf("OK\n");
259 // END SECRET
260
261 for(int i = 0; i < (int)res.size(); i++)
262 printf(i ? " %d" : "%d", res[i]);
263 printf("\n");
264
265 auto t4 = clock();
266
267 // reset console output
268 freopen("CON", "w", stdout);
269
270 //std::cout <<"res = "<<res<<’\n’<<’\n’;
271
272 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
273 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
274 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
275
276 return 0;
277 }
278 // -----------------------------------------------------------
279 /*
280 t2-t1 = 0.062
281 t3-t2 = 2.719
282 t4-t3 = 0
283
284 Process returned 0 (0x0) execution time : 2.828 s
285 Press any key to continue.
286 */

4.3.3 *Rezolvare detaliat 

4.4 Big Prize


Problema 4 - Big Prize 100 de puncte
CAPITOLUL 4. IOI 2017 458

Author: Hamed Valizadeh (Iran)

Jocul "Marele premiu" este un renumit show de televiziune. Sunteµi norocosul participant care
s-a calicat în runda nal . 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  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 ecare întrebare, veµi alege o cutie i. Ca r spuns, Rambod
v  va oferi un ³ir a care conµine doi întregi. Semnicaµ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. Semnicaµ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  e î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 4. IOI 2017 459

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 gura de mai sus. Partea de sus arat  valorile premiilor în ecare
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 ecare 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µ  x  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  ecare 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 x 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 a³eaz  o singur  linie care conµine valoarea returnat  de find_best ³i
num rul de apeluri a procedurii ask.
CAPITOLUL 4. IOI 2017 460

Timp maxim de executare/test: 1.0 secunde


Memorie: total 1024 MB

4.4.1 Indicaµii de rezolvare

Subtask 1
In this subtask, We can nd the diamond with a simple binary search.

Subtask 2
First we query the rst 474 prize in the line. It’s sucient to nd at least a lollipops
prize (cheapest). When we have found a lollipop, we can use binary search to nd 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 nding 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.

4.4.2 Coduri surs 

Listing 4.4.1: prize.cpp


1 // http://ioi2017.org/tasks/materials/prize.zip ---> sandbox/prize.cpp
2 // http://ioi2017.org/tasks/materials/prize.zip ---> solution/***.cpp
3
4 #include <vector>
5 #include <cmath>
6 #include <cstring>
7 #include "prize.h"
8 #define X first
9 #define Y second
10
11 #include <iostream> // grader ...
12 #include <vector> // grader ...
13 #include <algorithm> // grader ...
14
15
16 #include <fstream> // ***
17
18 using namespace std;
19
20 ifstream cinf("01.in"); // ***
21
22
23 typedef pair<int,int> pii;
24
25 int numb;
26 pii P[210000];
27 bool mark[210000];
28 vector<int>vtmp;
29
30 pii query(int x)
31 {
32 if(mark[x]) return P[x];
33 mark[x]=true;
34 vtmp=ask(x);
35 pii tmp=pii(vtmp[0],vtmp[1]);
36 if(tmp.X+tmp.Y==0) throw x;
37 return P[x]=tmp;
38 }
CAPITOLUL 4. IOI 2017 461

39
40 void bs(int l,int r,int nl,int nr)
41 {
42 if(l>r) return;
43 for(int i=0;i<=r-l;i++)
44 {
45 int mid,midl=(l+r)/2-i/2,midr=(l+r)/2+(i+1)/2;
46 if(i%2==0) mid=midl;
47 else mid=midr;
48 pii tmp=query(mid);
49 if(tmp.X+tmp.Y==numb)
50 {
51 int tmpl=(i%2==0?0:midr-midl);
52 int tmpr=(i%2==1?0:midr-midl);
53 if(tmp.X-tmpl>nl) bs(l,midl-1,nl,tmp.Y+tmpl);
54 if(tmp.Y-tmpr>nr) bs(midr+1,r,tmp.X+tmpr,nr);
55 break;
56 }
57 }
58 }
59
60 int find_best(int n)
61 {
62 if(n==1) return 0;
63 try{
64 numb=0;
65 memset(mark,false,sizeof mark);
66 int p=0;
67 for(int i=0;i<sqrt(n)+30 && i<n && numb<=26;i++)
68 {
69 pii tmp=query(i);
70 if(tmp.X+tmp.Y>numb) p=i;
71 numb=max(numb,tmp.X+tmp.Y);
72 }
73 bs(p,n-1,p,0);
74 }
75 catch(int ans){
76 return ans;
77 }
78 return -1;
79 }
80
81 // -------------------------------------------------
82 static const int max_q = 10000;
83 static int n;
84 static int query_count = 0;
85 static vector<int> g;
86 static vector<vector<int> > rank_count;
87
88 vector<int> ask(int i) {
89 query_count++;
90 if(query_count > max_q) {
91 cerr << "Query limit exceeded" << endl;
92 exit(0);
93 }
94
95 if(i < 0 || i >= n) {
96 cerr << "Bad index: " << i << endl;
97 exit(0);
98 }
99
100 vector<int> res(2);
101 res[0] = rank_count[g[i] - 1][i + 1];
102 res[1] = rank_count[g[i] - 1][n] - res[0];
103 return res;
104 }
105
106 int main()
107 {
108 cinf >> n; // ***
109
110 g.resize(n);
111 for(int i = 0; i < n; i++)
112 {
113 cinf >> g[i]; // ***
114 if(g[i] < 1) {
CAPITOLUL 4. IOI 2017 462

115 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
116 exit(0);
117 }
118 }
119
120 int max_rank = *max_element(g.begin(), g.end());
121
122 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
123 for(int r = 0; r <= max_rank; r++) {
124 for(int i = 1; i <= n; i++) {
125 rank_count[r][i] = rank_count[r][i - 1];
126 if(g[i - 1] == r)
127 rank_count[r][i]++;
128 }
129 }
130
131 for(int i = 0; i <= n; i++)
132 for(int r = 1; r <= max_rank; r++)
133 rank_count[r][i] += rank_count[r - 1][i];
134
135 int res = find_best(n);
136 cout << res << endl << "Query count: " << query_count << endl;
137
138 return 0;
139 }

Listing 4.4.2: prize+graderpublic.cpp


1 #include "prize.h"
2
3 #include <iostream>
4 #include <vector>
5 #include <algorithm>
6
7 #include<ctime>
8 #include <string>
9
10 using namespace std;
11
12 static const int max_q = 10000;
13 static int n;
14 static int query_count = 0;
15 static vector<int> g;
16 static vector<vector<int> > rank_count;
17
18 // ----------- prize -------------------------
19
20 #include <cmath>
21 #include <cstring>
22
23 #define X first
24 #define Y second
25
26 typedef pair<int,int> pii;
27
28 int numb;
29 pii P[210000];
30 bool mark[210000];
31 vector<int>vtmp;
32
33 pii query(int x)
34 {
35 if(mark[x]) return P[x];
36 mark[x]=true;
37 vtmp=ask(x);
38 pii tmp=pii(vtmp[0],vtmp[1]);
39 if(tmp.X+tmp.Y==0) throw x;
40 return P[x]=tmp;
41 }
42
43 void bs(int l,int r,int nl,int nr)
44 {
45 if(l>r) return;
46 for(int i=0;i<=r-l;i++)
47 {
CAPITOLUL 4. IOI 2017 463

48 int mid,midl=(l+r)/2-i/2,midr=(l+r)/2+(i+1)/2;
49 if(i%2==0) mid=midl;
50 else mid=midr;
51 pii tmp=query(mid);
52 if(tmp.X+tmp.Y==numb)
53 {
54 int tmpl=(i%2==0?0:midr-midl);
55 int tmpr=(i%2==1?0:midr-midl);
56 if(tmp.X-tmpl>nl) bs(l,midl-1,nl,tmp.Y+tmpl);
57 if(tmp.Y-tmpr>nr) bs(midr+1,r,tmp.X+tmpr,nr);
58 break;
59 }
60 }
61 }
62
63 int find_best(int n)
64 {
65 if(n==1) return 0;
66 try
67 {
68 numb=0;
69 memset(mark,false,sizeof mark);
70 int p=0;
71 for(int i=0;i<sqrt(n)+30 && i<n && numb<=26;i++)
72 {
73 pii tmp=query(i);
74 if(tmp.X+tmp.Y>numb) p=i;
75 numb=max(numb,tmp.X+tmp.Y);
76 }
77 bs(p,n-1,p,0);
78 }
79 catch(int ans)
80 {
81 return ans;
82 }
83 return -1;
84 }
85
86 // ---------- graderpublic -------------------
87
88 vector<int> ask(int i)
89 {
90 query_count++;
91 if(query_count > max_q)
92 {
93 cerr << "Query limit exceeded" << endl;
94 exit(0);
95 }
96
97 if(i < 0 || i >= n)
98 {
99 cerr << "Bad index: " << i << endl;
100 exit(0);
101 }
102
103 vector<int> res(2);
104 res[0] = rank_count[g[i] - 1][i + 1];
105 res[1] = rank_count[g[i] - 1][n] - res[0];
106 return res;
107 }
108
109 int main()
110 {
111 auto t1 = clock();
112
113 std::freopen("../tests/2-64.in", "r", stdin) ;
114 std::freopen("2-64.out.txt", "w", stdout) ;
115
116 string s;
117 getline (cin,s);
118 cout<<s<<"\n";
119
120 //cin >> n;
121 scanf("%d",&n);
122 //cout<<"n = "<<n<<"\n";
123
CAPITOLUL 4. IOI 2017 464

124 g.resize(n);
125 for(int i = 0; i < n; i++)
126 {
127 cin >> g[i];
128 if(g[i] < 1)
129 {
130 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
131 exit(0);
132 }
133 }
134
135 auto t2 = clock();
136
137 int max_rank = *max_element(g.begin(), g.end());
138
139 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
140 for(int r = 0; r <= max_rank; r++)
141 {
142 for(int i = 1; i <= n; i++)
143 {
144 rank_count[r][i] = rank_count[r][i - 1];
145 if(g[i - 1] == r)
146 rank_count[r][i]++;
147 }
148 }
149
150 for(int i = 0; i <= n; i++)
151 for(int r = 1; r <= max_rank; r++)
152 rank_count[r][i] += rank_count[r - 1][i];
153
154 int res = find_best(n);
155
156 auto t3 = clock();
157
158 cout << res << endl << "Query count: " << query_count << endl;
159
160 auto t4 = clock();
161
162 // reset console output
163 freopen("CON", "w", stdout);
164
165 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
166
167 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
168 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
169 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
170
171 return 0;
172 }
173 /*
174 res = 111111 Query count = 555
175
176 t2-t1 = 0.244
177 t3-t2 = 0.082
178 t4-t3 = 0
179
180 Process returned 0 (0x0) execution time : 0.352 s
181 Press any key to continue.
182 */

Listing 4.4.3: prize-169927.cpp


1 // https://oj.uz/submission/169927 ... 53 ms 2204 KB
2 #include "prize.h"
3
4 #include <iostream>
5 #include <vector>
6 #include <algorithm>
7
8 #include<ctime>
9 #include <string>
10
11 using namespace std;
12
13 static const int max_q = 10000;
CAPITOLUL 4. IOI 2017 465

14 static int n;
15 static int query_count = 0;
16 static vector<int> g;
17 static vector<vector<int> > rank_count;
18
19 // ----------- prize -------------------------
20 //#include "prize.h"
21 #include <bits/stdc++.h>
22
23 #define fi first
24 #define se second
25 #define ryan bear
26
27 //using namespace std;
28
29 typedef long long ll;
30 typedef pair<int,int> pii;
31 typedef pair<ll,ll> pll;
32 typedef long double ld;
33 typedef vector<int> vim;
34
35 int N;
36 set<pair<int, pii> > S[10];
37 int val[10], tp;
38
39 int ans, fl, chk[200010], ar[200010];
40
41 int f(int v)
42 {
43 for (int i=0; i<tp; i++) if (val[i]==v) return i;
44 val[tp]=v; return tp++;
45 }
46
47 void myfind(int s, int e)
48 {
49 if (s>e) return ;
50
51 int md=(s+e)/2;
52 if (chk[md]) return ;
53
54 chk[md]=1;
55 vim res=ask(md);
56 ar[md]=res[0]+res[1];
57 if (res[0]==0&&res[1]==0)
58 {
59 ans=md;
60 fl=1;
61 return ;
62 }
63
64 int ind=f(ar[md]);
65 S[ind].insert({md, make_pair(res[0], res[1])});
66
67 auto it=S[ind].find({md, make_pair(res[0], res[1])});
68 pair<int, pii> p=( *it), pr, ne;
69
70 if (it==S[ind].begin()) pr={-1, make_pair(0, ar[md])};
71 else pr=*prev(it);
72
73 if (next(it)==S[ind].end()) ne={N, make_pair(ar[md], 0)};
74 else ne=*next(it);
75
76 if (pr.se.fi!=p.se.fi) myfind(max(s, pr.fi+1), p.fi-1);
77 if (fl) return ;
78 if (p.se.fi!=ne.se.fi) myfind(p.fi+1, min(e, ne.fi-1));
79 }
80
81 int find_best(int n)
82 {
83 N=n;
84 myfind(0, n-1);
85 return ans;
86 }
87
88 // ---------- graderpublic -------------------
89
CAPITOLUL 4. IOI 2017 466

90 vector<int> ask(int i)
91 {
92 query_count++;
93 if(query_count > max_q)
94 {
95 cerr << "Query limit exceeded" << endl;
96 exit(0);
97 }
98
99 if(i < 0 || i >= n)
100 {
101 cerr << "Bad index: " << i << endl;
102 exit(0);
103 }
104
105 vector<int> res(2);
106 res[0] = rank_count[g[i] - 1][i + 1];
107 res[1] = rank_count[g[i] - 1][n] - res[0];
108 return res;
109 }
110
111 int main()
112 {
113 auto t1 = clock();
114
115 std::freopen("../tests/2-64.in", "r", stdin) ;
116 std::freopen("2-64.out.txt", "w", stdout) ;
117
118 string s;
119 getline (cin,s);
120 cout<<s<<"\n";
121
122 //cin >> n;
123 scanf("%d",&n);
124 //cout<<"n = "<<n<<"\n";
125
126 g.resize(n);
127 for(int i = 0; i < n; i++)
128 {
129 cin >> g[i];
130 if(g[i] < 1)
131 {
132 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
133 exit(0);
134 }
135 }
136
137 auto t2 = clock();
138
139 int max_rank = *max_element(g.begin(), g.end());
140
141 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
142 for(int r = 0; r <= max_rank; r++)
143 {
144 for(int i = 1; i <= n; i++)
145 {
146 rank_count[r][i] = rank_count[r][i - 1];
147 if(g[i - 1] == r)
148 rank_count[r][i]++;
149 }
150 }
151
152 for(int i = 0; i <= n; i++)
153 for(int r = 1; r <= max_rank; r++)
154 rank_count[r][i] += rank_count[r - 1][i];
155
156 int res = find_best(n);
157
158 auto t3 = clock();
159
160 cout << res << endl << "Query count: " << query_count << endl;
161
162 auto t4 = clock();
163
164 // reset console output
165 freopen("CON", "w", stdout);
CAPITOLUL 4. IOI 2017 467

166
167 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
168
169 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
170 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
171 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
172
173 return 0;
174 }
175 /*
176 res = 111111 Query count = 85
177
178 t2-t1 = 0.258
179 t3-t2 = 0.071
180 t4-t3 = 0
181
182 Process returned 0 (0x0) execution time : 0.364 s
183 Press any key to continue.
184 */

Listing 4.4.4: prize-187329.cpp


1 // https://oj.uz/submission/187329 71 ms 5548 KB
2 #include "prize.h"
3
4 #include <iostream>
5 #include <vector>
6 #include <algorithm>
7
8 #include<ctime>
9 #include <string>
10
11 using namespace std;
12
13 static const int max_q = 10000;
14 static int n;
15 static int query_count = 0;
16 static vector<int> g;
17 static vector<vector<int> > rank_count;
18
19 // ----------- prize -------------------------
20 //#include "prize.h"
21 #include <bits/stdc++.h>
22
23 //using namespace std;
24
25 static const int MAXN = (int) 2e5;
26
27 static bool mark[MAXN];
28 static vector <int> qry[MAXN];
29
30 static inline vector <int> myask(int pos)
31 {
32 if(mark[pos] == 0)
33 {
34 qry[pos] = ask(pos);
35 }
36 mark[pos] = 1;
37 return qry[pos];
38 }
39
40 static int solve(int l, int r, int numl, int numr, int mx)
41 {
42 if(l > r) return -1;
43
44 int mid = (l + r) / 2;
45
46 int ll = mid, rr = mid;
47 while(ll >= l && myask(ll)[0] + myask(ll)[1] < mx)
48 {
49 auto cur = myask(ll);
50 if(cur[0] + cur[1] == 0) return ll;
51 ll--;
52 }
53
CAPITOLUL 4. IOI 2017 468

54 while(rr <= r && myask(rr)[0] + myask(rr)[1] < mx)


55 {
56 auto cur = myask(rr);
57 if(cur[0] + cur[1] == 0) return rr;
58 rr++;
59 }
60
61 auto x = myask(max(ll, l)), y = myask(min(rr, r));
62 int ans = -1;
63
64 if(x[0] - numl > 0)
65 {
66 ans = max(ans, solve(l, ll - 1, numl, x[1], mx));
67 }
68
69 if(y[1] - numr > 0)
70 {
71 ans = max(ans, solve(rr + 1, r, y[0], numr, mx));
72 }
73
74 return ans;
75 }
76
77 int find_best(int n)
78 {
79 srand(time(NULL));
80
81 auto myrand = [&]()
82 {
83 return (1LL * (1LL << 15) * rand() + rand()) % n;
84 };
85
86 vector <bool> vis(n);
87 int mx = 0;
88
89 for(int i = 0; i < min(n, 200); i++)
90 {
91 int p = myrand();
92 while(vis[p]) { p = myrand(); }
93 vis[p] = 1;
94 auto cur = myask(p);
95 mx = max(mx, cur[0] + cur[1]);
96 }
97
98 return solve(0, n - 1, 0, 0, mx);
99 }
100
101 // ---------- graderpublic -------------------
102
103 vector<int> ask(int i)
104 {
105 query_count++;
106 if(query_count > max_q)
107 {
108 cerr << "Query limit exceeded" << endl;
109 exit(0);
110 }
111
112 if(i < 0 || i >= n)
113 {
114 cerr << "Bad index: " << i << endl;
115 exit(0);
116 }
117
118 vector<int> res(2);
119 res[0] = rank_count[g[i] - 1][i + 1];
120 res[1] = rank_count[g[i] - 1][n] - res[0];
121 return res;
122 }
123
124 int main()
125 {
126 auto t1 = clock();
127
128 std::freopen("../tests/2-64.in", "r", stdin) ;
129 std::freopen("2-64.out.txt", "w", stdout) ;
CAPITOLUL 4. IOI 2017 469

130
131 string s;
132 getline (cin,s);
133 cout<<s<<"\n";
134
135 //cin >> n;
136 scanf("%d",&n);
137 //cout<<"n = "<<n<<"\n";
138
139 g.resize(n);
140 for(int i = 0; i < n; i++)
141 {
142 cin >> g[i];
143 if(g[i] < 1)
144 {
145 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
146 exit(0);
147 }
148 }
149
150 auto t2 = clock();
151
152 int max_rank = *max_element(g.begin(), g.end());
153
154 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
155 for(int r = 0; r <= max_rank; r++)
156 {
157 for(int i = 1; i <= n; i++)
158 {
159 rank_count[r][i] = rank_count[r][i - 1];
160 if(g[i - 1] == r)
161 rank_count[r][i]++;
162 }
163 }
164
165 for(int i = 0; i <= n; i++)
166 for(int r = 1; r <= max_rank; r++)
167 rank_count[r][i] += rank_count[r - 1][i];
168
169 int res = find_best(n);
170
171 auto t3 = clock();
172
173 cout << res << endl << "Query count: " << query_count << endl;
174
175 auto t4 = clock();
176
177 // reset console output
178 freopen("CON", "w", stdout);
179
180 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
181
182 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
183 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
184 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
185
186 return 0;
187 }
188 /*
189 res = 111111 Query count = 324
190
191 t2-t1 = 0.253
192 t3-t2 = 0.074
193 t4-t3 = 0
194
195 Process returned 0 (0x0) execution time : 0.363 s
196 Press any key to continue.
197 */

Listing 4.4.5: prize-206577.cpp


1 // https://oj.uz/submission/206577 60 ms 9980 KB
2 #include "prize.h"
3
4 #include <iostream>
CAPITOLUL 4. IOI 2017 470

5 #include <vector>
6 #include <algorithm>
7
8 #include<ctime>
9 #include <string>
10
11 using namespace std;
12
13 static const int max_q = 10000;
14 static int n;
15 static int query_count = 0;
16 static vector<int> g;
17 static vector<vector<int> > rank_count;
18
19 // ----------- prize -------------------------
20 //#include "prize.h"
21 //#include<vector>
22 #include<map>
23 #include<utility>
24 #include<cassert>
25
26 #define MAXN 200005
27
28 //using namespace std;
29
30 map<int, int> Q[MAXN];
31
32 int slv(int l, int r)
33 {
34 if (l > r) {return(-1);}
35 int mid = (l + r) >> 1;
36 vector<int> res = ask(mid);
37 int tot = res[0] + res[1], resL=-1, resR=-1;
38
39 if (tot == 0) {return(mid);}
40 else if (l == r) {return(-1);}
41
42 auto ptr1 = Q[tot].insert({mid, res[0]}).first, ptr2=ptr1;
43
44 if ((ptr1 == Q[tot].begin() || ( *(--ptr1)).second != res[0]) && res[0])
45 {
46 resL = slv(l, mid-1);
47 if (resL >= 0) {return(resL);}
48 }
49
50 if (((++ptr2) == Q[tot].end() || ( *ptr2).second != res[0]) && res[1])
51 {
52 resR = slv(mid+1, r);
53 if (resR >= 0) {return(resR);}
54 }
55 return(-1);
56 }
57
58 int find_best(int n)
59 {
60 return(slv(0, n-1));
61 }
62
63 // ---------- graderpublic -------------------
64
65 vector<int> ask(int i)
66 {
67 query_count++;
68 if(query_count > max_q)
69 {
70 cerr << "Query limit exceeded" << endl;
71 exit(0);
72 }
73
74 if(i < 0 || i >= n)
75 {
76 cerr << "Bad index: " << i << endl;
77 exit(0);
78 }
79
80 vector<int> res(2);
CAPITOLUL 4. IOI 2017 471

81 res[0] = rank_count[g[i] - 1][i + 1];


82 res[1] = rank_count[g[i] - 1][n] - res[0];
83 return res;
84 }
85
86 int main()
87 {
88 auto t1 = clock();
89
90 std::freopen("../tests/2-64.in", "r", stdin) ;
91 std::freopen("2-64.out.txt", "w", stdout) ;
92
93 string s;
94 getline (cin,s);
95 cout<<s<<"\n";
96
97 //cin >> n;
98 scanf("%d",&n);
99 //cout<<"n = "<<n<<"\n";
100
101 g.resize(n);
102 for(int i = 0; i < n; i++)
103 {
104 cin >> g[i];
105 if(g[i] < 1)
106 {
107 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
108 exit(0);
109 }
110 }
111
112 auto t2 = clock();
113
114 int max_rank = *max_element(g.begin(), g.end());
115
116 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
117 for(int r = 0; r <= max_rank; r++)
118 {
119 for(int i = 1; i <= n; i++)
120 {
121 rank_count[r][i] = rank_count[r][i - 1];
122 if(g[i - 1] == r)
123 rank_count[r][i]++;
124 }
125 }
126
127 for(int i = 0; i <= n; i++)
128 for(int r = 1; r <= max_rank; r++)
129 rank_count[r][i] += rank_count[r - 1][i];
130
131 int res = find_best(n);
132
133 auto t3 = clock();
134
135 cout << res << endl << "Query count: " << query_count << endl;
136
137 auto t4 = clock();
138
139 // reset console output
140 freopen("CON", "w", stdout);
141
142 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
143
144 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
145 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
146 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
147
148 return 0;
149 }
150 /*
151 res = 111111 Query count = 85
152
153 t2-t1 = 0.321
154 t3-t2 = 0.083
155 t4-t3 = 0
156
CAPITOLUL 4. IOI 2017 472

157 Process returned 0 (0x0) execution time : 0.456 s


158 Press any key to continue.
159 */

Listing 4.4.6: prize-208873.cpp


1 // https://oj.uz/submission/208873 58 ms 836 KB
2 #include "prize.h"
3
4 #include <iostream>
5 #include <vector>
6 #include <algorithm>
7
8 #include<ctime>
9 #include <string>
10
11 using namespace std;
12
13 static const int max_q = 10000;
14 static int n;
15 static int query_count = 0;
16 static vector<int> g;
17 static vector<vector<int> > rank_count;
18
19 // ----------- prize -------------------------
20 //#include "prize.h"
21
22 #include <bits/stdc++.h>
23
24 //using namespace std;
25
26 map<int, pair<int, int>> memo;
27
28 vector<int> query(int i)
29 {
30 vector<int> res;
31 if (memo.count(i))
32 {
33 res.emplace_back(memo[i].first);
34 res.emplace_back(memo[i].second);
35 }
36 else
37 {
38 res = ask(i);
39 memo[i] = {res[0], res[1]};
40 }
41 return res;
42 }
43
44 int numValuable;
45 vector<int> candidates; // non-lollipop boxes
46
47 void binarySearch(int leftValuable, int rightValuable, int left, int right)
48 {
49 if (left > right || leftValuable + rightValuable == numValuable)
50 {
51 return;
52 }
53
54 int mid = (left + right) / 2;
55 vector<int> res = query(mid);
56
57 if (res[0] + res[1] == numValuable)
58 { // mid is a lollipop box
59 binarySearch(leftValuable, res[1], left, mid - 1);
60 binarySearch(res[0], rightValuable, mid + 1, right);
61 }
62 else
63 { // mid is a non-lollipop box
64 candidates.emplace_back(mid);
65 int nearestLeft, nearestRight;
66
67 for (int i = mid + 1; i <= right; i++)
68 {
69 res = query(i);
CAPITOLUL 4. IOI 2017 473

70 if (res[0] + res[1] == numValuable)


71 {
72 nearestRight = i;
73 binarySearch(query(nearestRight)[0],
74 rightValuable,
75 nearestRight + 1,
76 right);
77 break;
78 }
79 else
80 { // i is a non-lollipop box
81 candidates.emplace_back(i);
82 }
83 }
84
85 for (int i = mid - 1; i >= left; i--)
86 {
87 res = query(i);
88 if (res[0] + res[1] == numValuable)
89 {
90 nearestLeft = i;
91 binarySearch(leftValuable, query(nearestLeft)[1],
92 left, nearestLeft - 1);
93 break;
94 }
95 else
96 { // i is a non-lollipop box
97 candidates.emplace_back(i);
98 }
99 }
100 }
101 }
102
103 int find_best(int n)
104 {
105 numValuable = 0;
106 for (int i = 0; i < min(n, 500); i++)
107 { // least valuable box must satisfy sum^2 <= n,
108 // and since n <= 200000 then sum <= 500 will suffice
109 vector<int> res = query(i);
110 if (res[0] + res[1] > numValuable)
111 {
112 numValuable = res[0] + res[1];
113 }
114
115 if (numValuable > 30)
116 { // second least valuable box must satisfy (sum^2)^2 <= n,
117 // and since n <= 200000 then sum <= 30 will suffice
118 break; // we will only check at most one least valuable box,
119 // the rest will be valuable and checked later anyways
120 }
121 }
122
123 binarySearch(0, 0, 0, n - 1);
124
125 int ans = -1;
126 for (auto &c : candidates)
127 {
128 vector<int> res = query(c);
129 if (res[0] == 0 && res[1] == 0)
130 {
131 ans = c;
132 }
133 }
134
135 return ans;
136 }
137
138 // ---------- graderpublic -------------------
139
140 vector<int> ask(int i)
141 {
142 query_count++;
143 if(query_count > max_q)
144 {
145 cerr << "Query limit exceeded" << endl;
CAPITOLUL 4. IOI 2017 474

146 exit(0);
147 }
148
149 if(i < 0 || i >= n)
150 {
151 cerr << "Bad index: " << i << endl;
152 exit(0);
153 }
154
155 vector<int> res(2);
156 res[0] = rank_count[g[i] - 1][i + 1];
157 res[1] = rank_count[g[i] - 1][n] - res[0];
158 return res;
159 }
160
161 int main()
162 {
163 auto t1 = clock();
164
165 std::freopen("../tests/2-64.in", "r", stdin) ;
166 std::freopen("2-64.out.txt", "w", stdout) ;
167
168 string s;
169 getline (cin,s);
170 cout<<s<<"\n";
171
172 //cin >> n;
173 scanf("%d",&n);
174 //cout<<"n = "<<n<<"\n";
175
176 g.resize(n);
177 for(int i = 0; i < n; i++)
178 {
179 cin >> g[i];
180 if(g[i] < 1)
181 {
182 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
183 exit(0);
184 }
185 }
186
187 auto t2 = clock();
188
189 int max_rank = *max_element(g.begin(), g.end());
190
191 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
192 for(int r = 0; r <= max_rank; r++)
193 {
194 for(int i = 1; i <= n; i++)
195 {
196 rank_count[r][i] = rank_count[r][i - 1];
197 if(g[i - 1] == r)
198 rank_count[r][i]++;
199 }
200 }
201
202 for(int i = 0; i <= n; i++)
203 for(int r = 1; r <= max_rank; r++)
204 rank_count[r][i] += rank_count[r - 1][i];
205
206 int res = find_best(n);
207
208 auto t3 = clock();
209
210 cout << res << endl << "Query count: " << query_count << endl;
211
212 auto t4 = clock();
213
214 // reset console output
215 freopen("CON", "w", stdout);
216
217 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
218
219 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
220 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
221 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 4. IOI 2017 475

222
223 return 0;
224 }
225 /*
226 res = 111111 Query count = 624
227
228 t2-t1 = 0.247
229 t3-t2 = 0.071
230 t4-t3 = 0
231
232 Process returned 0 (0x0) execution time : 0.352 s
233 Press any key to continue.
234 */

Listing 4.4.7: prize-221802.cpp


1 // https://oj.uz/submission/221802 57 ms 2136 KB
2 #include "prize.h"
3
4 #include <iostream>
5 #include <vector>
6 #include <algorithm>
7
8 #include<ctime>
9 #include <string>
10
11 using namespace std;
12
13 static const int max_q = 10000;
14 static int n;
15 static int query_count = 0;
16 static vector<int> g;
17 static vector<vector<int> > rank_count;
18
19 // ----------- prize -------------------------
20 /**
21 * user: ppavic
22 * fname: Patrik
23 * lname: PaviÄĞ
24 * task: prize
25 * score: 100.0
26 * date: 2019-06-16 09:02:45.353752
27 */
28 //#include "prize.h"
29 //#include <vector>
30 //#include <algorithm>
31 #include <cstring> // memset
32
33 #define PB push_back
34
35 //using namespace std;
36
37 typedef vector < int > vi;
38
39 const int N = 2e5 + 500;
40
41 int L[N], R[N];
42
43 int get_L(int i)
44 {
45 if(L[i] != -1) return L[i];
46 vi res = ask(i);
47 L[i] = res[0], R[i] = res[1];
48 return L[i];
49 }
50
51 int get_R(int i)
52 {
53 if(L[i] != -1) return R[i];
54 vi res = ask(i);
55 L[i] = res[0], R[i] = res[1];
56 return R[i];
57 }
58
59 int loli;
CAPITOLUL 4. IOI 2017 476

60 vi svi;
61
62 void solve(int l,int r)
63 {
64 if(l > r) return;
65 if(get_L(l) + get_R(l) < loli)
66 {
67 svi.PB(l); solve(l + 1, r);
68 return;
69 }
70
71 if(l == r) return;
72 if(get_L(r) + get_R(r) < loli)
73 {
74 svi.PB(r);
75 solve(l, r - 1);
76 return;
77 }
78
79 if(get_L(l) == get_L(r) || l + 1 == r) return;
80 solve(l, (l + r) / 2);
81 solve((l + r) / 2, r);
82 }
83
84
85 int find_best(int n)
86 {
87 memset(L, -1, sizeof(L));
88 svi.clear();
89 loli = 0;
90 for(int i = 0;i < 100;i++)
91 {
92 int x = (rand() + rand()) % n;
93 x = (x % n + n) % n;
94 loli = max(loli, get_L(x) + get_R(x));
95 }
96
97 solve(0, n - 1);
98
99 for(int x : svi)
100 {
101 if(get_L(x) + get_R(x) == 0)
102 return x;
103 }
104
105 return 0;
106 }
107
108 // ---------- graderpublic -------------------
109
110 vector<int> ask(int i)
111 {
112 query_count++;
113 if(query_count > max_q)
114 {
115 cerr << "Query limit exceeded" << endl;
116 exit(0);
117 }
118
119 if(i < 0 || i >= n)
120 {
121 cerr << "Bad index: " << i << endl;
122 exit(0);
123 }
124
125 vector<int> res(2);
126 res[0] = rank_count[g[i] - 1][i + 1];
127 res[1] = rank_count[g[i] - 1][n] - res[0];
128 return res;
129 }
130
131 int main()
132 {
133 auto t1 = clock();
134
135 std::freopen("../tests/2-64.in", "r", stdin) ;
CAPITOLUL 4. IOI 2017 477

136 std::freopen("2-64.out.txt", "w", stdout) ;


137
138 string s;
139 getline (cin,s);
140 cout<<s<<"\n";
141
142 //cin >> n;
143 scanf("%d",&n);
144 //cout<<"n = "<<n<<"\n";
145
146 g.resize(n);
147 for(int i = 0; i < n; i++)
148 {
149 cin >> g[i];
150 if(g[i] < 1)
151 {
152 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
153 exit(0);
154 }
155 }
156
157 auto t2 = clock();
158
159 int max_rank = *max_element(g.begin(), g.end());
160
161 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
162 for(int r = 0; r <= max_rank; r++)
163 {
164 for(int i = 1; i <= n; i++)
165 {
166 rank_count[r][i] = rank_count[r][i - 1];
167 if(g[i - 1] == r)
168 rank_count[r][i]++;
169 }
170 }
171
172 for(int i = 0; i <= n; i++)
173 for(int r = 1; r <= max_rank; r++)
174 rank_count[r][i] += rank_count[r - 1][i];
175
176 int res = find_best(n);
177
178 auto t3 = clock();
179
180 cout << res << endl << "Query count: " << query_count << endl;
181
182 auto t4 = clock();
183
184 // reset console output
185 freopen("CON", "w", stdout);
186
187 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
188
189 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
190 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
191 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
192
193 return 0;
194 }
195 /*
196 res = 111111 Query count = 223
197
198 t2-t1 = 0.246
199 t3-t2 = 0.075
200 t4-t3 = 0
201
202 Process returned 0 (0x0) execution time : 0.345 s
203 Press any key to continue.
204 */

Listing 4.4.8: prize-229052.cpp


1 // https://oj.uz/submission/229052 63 ms 10104 KB
2 #include "prize.h"
3
CAPITOLUL 4. IOI 2017 478

4 #include <iostream>
5 #include <vector>
6 #include <algorithm>
7
8 #include<ctime>
9 #include <string>
10
11 using namespace std;
12
13 static const int max_q = 10000;
14 static int n;
15 static int query_count = 0;
16 static vector<int> g;
17 static vector<vector<int> > rank_count;
18
19 // ----------- prize -------------------------
20
21 #include<bits/stdc++.h>
22
23 //#include "prize.h"
24 #define god dimasi5eks
25 #pragma GCC optimize("O3")
26 #define fi first
27 #define se second
28 #define pb push_back
29 #define pf push_front
30
31 // #define fisier 1
32
33 using namespace std;
34
35 typedef long long ll;
36
37 const int mod = 1000000007;
38 const double dancila = 3.14159265359; // PI
39 const double eps = 1e-9;
40
41 int ans;
42 map<int, pair<int, int> > m[200002];
43
44 pair<int, int> qu(int x)
45 {
46 vector<int> v = ask(x);
47 return {v[0], v[1]};
48 }
49
50 void solve(int st, int dr)
51 {
52 if(ans != -1 || st > dr)
53 return;
54
55 int mid = (st + dr) / 2;
56
57 pair<int, int> cur = qu(mid);
58 int sum = cur.fi + cur.se;
59
60 if(sum == 0)
61 {
62 ans = mid;
63 return;
64 }
65
66 auto it = m[sum].upper_bound(mid);
67 bool L = 1, R = 1;
68 if(it != m[sum].begin())
69 {
70 it--;
71 if(cur.se == it -> se.se)
72 L = 0;
73 it++;
74 }
75
76 if(it != m[sum].end())
77 {
78 if(cur.se == it -> se.se)
79 R = 0;
CAPITOLUL 4. IOI 2017 479

80 }
81
82 m[sum][mid] = cur;
83 if(L)
84 solve(st, mid - 1);
85 if(R)
86 solve(mid + 1, dr);
87 }
88
89 int find_best(int n)
90 {
91 ans = -1;
92 solve(0, n - 1);
93 return ans;
94 }
95
96 // ---------- graderpublic -------------------
97
98 vector<int> ask(int i)
99 {
100 query_count++;
101 if(query_count > max_q)
102 {
103 cerr << "Query limit exceeded" << endl;
104 exit(0);
105 }
106
107 if(i < 0 || i >= n)
108 {
109 cerr << "Bad index: " << i << endl;
110 exit(0);
111 }
112
113 vector<int> res(2);
114 res[0] = rank_count[g[i] - 1][i + 1];
115 res[1] = rank_count[g[i] - 1][n] - res[0];
116 return res;
117 }
118
119 int main()
120 {
121 auto t1 = clock();
122
123 std::freopen("../tests/2-64.in", "r", stdin) ;
124 std::freopen("2-64.out.txt", "w", stdout) ;
125
126 string s;
127 getline (cin,s);
128 cout<<s<<"\n";
129
130 //cin >> n;
131 scanf("%d",&n);
132 //cout<<"n = "<<n<<"\n";
133
134 g.resize(n);
135 for(int i = 0; i < n; i++)
136 {
137 cin >> g[i];
138 if(g[i] < 1)
139 {
140 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
141 exit(0);
142 }
143 }
144
145 auto t2 = clock();
146
147 int max_rank = *max_element(g.begin(), g.end());
148
149 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
150 for(int r = 0; r <= max_rank; r++)
151 {
152 for(int i = 1; i <= n; i++)
153 {
154 rank_count[r][i] = rank_count[r][i - 1];
155 if(g[i - 1] == r)
CAPITOLUL 4. IOI 2017 480

156 rank_count[r][i]++;
157 }
158 }
159
160 for(int i = 0; i <= n; i++)
161 for(int r = 1; r <= max_rank; r++)
162 rank_count[r][i] += rank_count[r - 1][i];
163
164 int res = find_best(n);
165
166 auto t3 = clock();
167
168 cout << res << endl << "Query count: " << query_count << endl;
169
170 auto t4 = clock();
171
172 // reset console output
173 freopen("CON", "w", stdout);
174
175 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
176
177 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
178 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
179 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
180
181 return 0;
182 }
183 /*
184 res = 111111 Query count = 98
185
186 t2-t1 = 0.241
187 t3-t2 = 0.051
188 t4-t3 = 0
189
190 Process returned 0 (0x0) execution time : 0.348 s
191 Press any key to continue.
192 */

Listing 4.4.9: prize-232826.cpp


1 // https://oj.uz/submission/232826 343 ms 472108 KB
2 #include "prize.h"
3
4 #include <iostream>
5 #include <vector>
6 #include <algorithm>
7
8 #include<ctime>
9 #include <string>
10
11 using namespace std;
12
13 static const int max_q = 10000;
14 static int n;
15 static int query_count = 0;
16 static vector<int> g;
17 static vector<vector<int> > rank_count;
18
19 // ----------- prize -------------------------
20
21 #include<bits/stdc++.h>
22
23 //using namespace std;
24
25 //#include "prize.h"
26 #define MID ((l+r)/2)
27
28 typedef vector<int> vi;
29 typedef pair<int, int> ii;
30
31 #define F first
32 #define S second
33
34 ii mem[1000000];
35 bool used[1000000];
CAPITOLUL 4. IOI 2017 481

36 set<int> s[10000000];
37
38 ii q(int x)
39 {
40 if(used[x]) return mem[x];
41 used[x] = true;
42 vi a = ask(x);
43 mem[x] = ii(a[0],a[1]);
44 return mem[x];
45 }
46
47 int N;
48 int mina=0;
49 int rec(int l=0, int r=N-1)
50 {
51 if(l>r) return -1;
52 ii a = q(MID);
53 if(a.F+a.S==0) return MID;
54
55 int tot = a.F+a.S;
56 auto it = s[a.F+a.S].insert(MID).F;
57 if(it==s[tot].begin() || mem[ *prev(it) ] != mem[MID])
58 {
59 int b = rec(l,MID-1);
60 if(b!=-1) return b;
61 }
62
63 if(it==--s[tot].end() || mem[ *next(it) ] != mem[MID])
64 {
65 int b = rec(MID+1,r);
66 if(b!=-1) return b;
67 }
68 return -1;
69 }
70
71 int find_best(int n)
72 {
73 N = n;
74 int l=0, r=n-1;
75 int m;
76 int pos=0;
77 return rec();
78 }
79
80 // ---------- graderpublic -------------------
81
82 vector<int> ask(int i)
83 {
84 query_count++;
85 if(query_count > max_q)
86 {
87 cerr << "Query limit exceeded" << endl;
88 exit(0);
89 }
90
91 if(i < 0 || i >= n)
92 {
93 cerr << "Bad index: " << i << endl;
94 exit(0);
95 }
96
97 vector<int> res(2);
98 res[0] = rank_count[g[i] - 1][i + 1];
99 res[1] = rank_count[g[i] - 1][n] - res[0];
100 return res;
101 }
102
103 int main()
104 {
105 auto t1 = clock();
106
107 std::freopen("../tests/2-64.in", "r", stdin) ;
108 std::freopen("2-64.out.txt", "w", stdout) ;
109
110 string s;
111 getline (cin,s);
CAPITOLUL 4. IOI 2017 482

112 cout<<s<<"\n";
113
114 //cin >> n;
115 scanf("%d",&n);
116 //cout<<"n = "<<n<<"\n";
117
118 g.resize(n);
119 for(int i = 0; i < n; i++)
120 {
121 cin >> g[i];
122 if(g[i] < 1)
123 {
124 cerr << "Invalid rank " << g[i] << " at index " << i << endl;
125 exit(0);
126 }
127 }
128
129 auto t2 = clock();
130
131 int max_rank = *max_element(g.begin(), g.end());
132
133 rank_count.resize(max_rank + 1, vector<int>(n + 1, 0));
134 for(int r = 0; r <= max_rank; r++)
135 {
136 for(int i = 1; i <= n; i++)
137 {
138 rank_count[r][i] = rank_count[r][i - 1];
139 if(g[i - 1] == r)
140 rank_count[r][i]++;
141 }
142 }
143
144 for(int i = 0; i <= n; i++)
145 for(int r = 1; r <= max_rank; r++)
146 rank_count[r][i] += rank_count[r - 1][i];
147
148 int res = find_best(n);
149
150 auto t3 = clock();
151
152 cout << res << endl << "Query count: " << query_count << endl;
153
154 auto t4 = clock();
155
156 // reset console output
157 freopen("CON", "w", stdout);
158
159 std::cout <<"res = "<<res<< " Query count = " << query_count<<’\n’<<’\n’;
160
161 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
162 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
164
165 return 0;
166 }
167 /*
168 res = 111111 Query count = 98
169
170 t2-t1 = 0.239
171 t3-t2 = 0.073
172 t4-t3 = 0
173
174 Process returned 0 (0x0) execution time : 1.510 s
175 Press any key to continue.
176 */
CAPITOLUL 4. IOI 2017 483

4.4.3 *Rezolvare detaliat 

4.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 Ru-
bada, prinµesa de Kabul. Când Zal a cerut-o pe Rubada în c s torie, tat l ei i-a cerut s  îndepli-
neasc  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 ecare î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 4. IOI 2017 484

Î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 ecare apel,
setul de drumuri specicat de r trebuie s  e 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 ecare 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 veric  dac  r posed 
toate propriet µile unui set de aur. în schimb, ea num r  ³i returneaz  num rul de indici a
CAPITOLUL 4. IOI 2017 485

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
 'Wrong Answer'.
Observaµie tehnic 
Procedura count_common_roads în C++ ³i Pascal folose³te metoda pass by reference din
considerente de ecienµ . Puteµi apela procedura în modul obi³nuit. Se garanteaz  c  evaluatorul
nu va modica valoarea r.
Timp maxim de executare/test: 3.0 secunde
Memorie: total 1024 MB

4.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 nd 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 , nd 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 suces
to nd 2 other edges that make a triangle together with e and do as mentioned earlier. Fix an
arbitrary tree t and nd out which of its edges appear in Zal's tree. Once we nd 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 nd 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 dierence is that nding 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 nd an ear-decomposition for them. Note that for every cycle C we can gure
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 4. IOI 2017 486

4.5.2 Coduri surs 

Listing 4.5.1: simurgh.cpp


1 // http://ioi2017.org/tasks/materials/simurgh.zip ---> sandbox/simurgh.cpp
2 // http://ioi2017.org/tasks/materials/simurgh.zip ---> solution/***.cpp
3
4 #include "simurgh.h"
5
6 #include <cstdio>
7 #include <cassert>
8 #include <vector>
9 #include <cstdlib>
10 #include <string>
11
12 #include<ctime>
13
14 using namespace std;
15
16 // ------------- simurgh ----------------
17
18 #include <iostream>
19 #include <algorithm>
20 #include <set>
21 #include <map>
22 #include <cmath>
23 #include <cstring>
24
25 using namespace std;
26
27 #define Foreach(i, c) \
28 for(__typeof((c).begin()) i = (c).begin(); i != (c).end(); ++i)
29 #define For(i,a,b) for(int (i)=(a);(i) < (b); ++(i))
30 #define rof(i,a,b) for(int (i)=(a);(i) > (b); --(i))
31 #define rep(i, c) for(auto &(i) : (c))
32 #define x first
33 #define y second
34 #define pb push_back
35 #define PB pop_back()
36 #define iOS ios_base::sync_with_stdio(false)
37 #define sqr(a) (((a) * (a)))
38 #define all(a) a.begin() , a.end()
39 #define error(x) cerr << #x << " = " << (x) <<endl
40 #define Error(a,b) \
41 cerr<<"( "<<#a<<" , "<<#b<<" ) = ( "<<(a)<<" , "<<(b)<<" )\n";
42 #define errop(a) cerr<<#a<<" = ( "<<((a).x)<<" , "<<((a).y)<<" )\n";
43 #define coud(a,b) cout<<fixed << setprecision((b)) << (a)
44 #define L(x) ((x)<<1)
45 #define R(x) (((x)<<1)+1)
46 #define umap unordered_map
47 #define double long double
48
49 typedef long long ll;
50 typedef pair<int,int>pii;
51 typedef vector<int> vi;
52 template <class T> inline void smax(T &x,T y){ x = max((x), (y));}
53 template <class T> inline void smin(T &x,T y){ x = min((x), (y));}
54 template <class T> inline void sminmax(T &mn, T &mx, T x)
55 {smin(mn, x), smax(mx, x);}
56
57 const int maxn = 512, maxm = maxn * maxn / 2;
58 pii highest[maxn];
59 bool mark[maxn];
60
61 int h[maxn], ind[maxn][maxn], state[maxn],
62 par[maxn], last_num[maxm], n, m, deg[maxn];
63
64 vi __edges_vec;
65 vi adj[maxn];
66 pii edges[maxm];
67 bool bit[maxm];
68 int _next_ = 1;
69 int _last_id[maxm];
70
CAPITOLUL 4. IOI 2017 487

71 vector<int> ANS;
72
73 inline void _renew()
74 {
75 vi __edges_new;
76 rep(i, __edges_vec) if(bit[i] && _last_id[i] != _next_)
77 __edges_new.pb(i), _last_id[i] = _next_;
78 ++ _next_;
79 __edges_vec = __edges_new;
80 }
81
82 inline int query()
83 {
84 _renew();
85 int res = count_common_roads(__edges_vec);
86 if(res == n-1)
87 ANS = __edges_vec;
88 return res;
89 }
90
91 inline void toggle(int i)
92 {
93 if(!bit[i])
94 {
95 bit[i] = true;
96 __edges_vec.pb(i);
97 }
98 else
99 bit[i] = false;
100 }
101
102 inline void reset()
103 {
104 while(!__edges_vec.empty())
105 {
106 int e = __edges_vec.back();
107 __edges_vec.PB;
108 bit[e] = false;
109 }
110 }
111
112 inline void dfs(int v = 0, int p = -1)
113 {
114 par[v] = p;
115 mark[v] = true;
116 highest[v] = {h[v], -1};
117 rep(u, adj[v])
118 {
119 int e = ind[v][u];
120 if(!mark[u])
121 {
122 h[u] = h[v] + 1;
123 dfs(u, v);
124 if(highest[v].x > highest[u].x)
125 highest[v] = highest[u];
126 }
127 else
128 if(highest[v].x > h[u] && u != p)
129 highest[v] = {h[u], e};
130 }
131
132 if(~p)
133 toggle(ind[v][p]);
134 }
135
136 inline void DFS(int v = 0)
137 {
138 int p = par[v];
139 rep(u, adj[v]) if(par[u] == v) DFS(u);
140 if(~p && state[v] == -1)
141 {
142 if(highest[v].x > h[p])
143 {
144 state[v] = 1;
145 return ;
146 }
CAPITOLUL 4. IOI 2017 488

147
148 int back_edge = highest[v].y;
149 int x = edges[back_edge].x, y = edges[back_edge].y;
150 if(h[x] > h[y]) swap(x, y);
151 int back_edge_num = query();
152 int mn = back_edge_num, mx = mn;
153 int cur = y;
154 int for_a_one = -1;
155 toggle(back_edge);
156
157 while(cur != x)
158 {
159 if(state[cur] == -1 or for_a_one == -1)
160 {
161 int cur_edge = ind[cur][par[cur]];
162 toggle(cur_edge);
163 last_num[cur_edge] = query();
164 sminmax(mn, mx, last_num[cur_edge]);
165 if(~state[cur])
166 for_a_one = last_num[cur_edge] - (!state[cur]);
167 toggle(cur_edge);
168 }
169 cur = par[cur];
170 }
171
172 toggle(back_edge);
173 cur = y;
174 while(cur != x)
175 {
176 if(state[cur] == -1)
177 {
178 int cur_edge = ind[cur][par[cur]];
179 if(~for_a_one)
180 state[cur] = last_num[cur_edge] == for_a_one;
181 else if(mn == mx)
182 state[cur] = 0;
183 else
184 state[cur] = last_num[cur_edge] == mn;
185 }
186
187 cur = par[cur];
188 }
189 }
190 }
191
192 vi tree, ans;
193
194 inline int root(int v)
195 {
196 return par[v] < 0? v: par[v] = root(par[v]);
197 }
198
199 inline bool merge(int ind)
200 {
201 int x = edges[ind].x, y = edges[ind].y;
202 x = root(x), y = root(y);
203 if(x == y) return false;
204 toggle(ind);
205 if(par[y] < par[x]) swap(x, y);
206 par[x] += par[y];
207 par[y] = x;
208 return true;
209 }
210
211 inline int edge_state(int i)
212 {
213 int x = edges[i].x, y = edges[i].y;
214 if(h[x] > h[y]) swap(x, y);
215 return state[y];
216 }
217
218 inline int query_for_forest(vi subset)
219 {
220 reset();
221 int sum = 0;
222 memset(par, -1, sizeof par);
CAPITOLUL 4. IOI 2017 489

223 rep(e, subset)


224 merge(e);
225 rep(e, tree)
226 if(merge(e))
227 sum += edge_state(e);
228 return query() - sum;
229 }
230
231 inline void calc_deg(int v)
232 {
233 vi subset;
234 rep(u, adj[v])
235 subset.pb(ind[v][u]);
236 deg[v] = query_for_forest(subset);
237 }
238
239 inline void remove(int v)
240 {
241 if(!deg[v] or mark[v]) return ;
242 assert(deg[v] == 1);
243 vi ed;
244 rep(u, adj[v]) if(!mark[u]) ed.pb(ind[v][u]);
245 int l = 0, r = ed.size() - 1;
246 while(r > l)
247 {
248 int mid = (l + r)/2;
249 vi subset(ed.begin()+l, ed.begin()+mid+1);
250 if(query_for_forest(subset))
251 r = mid;
252 else
253 l = mid + 1;
254 }
255
256 int e = ed[l];
257 int u = edges[e].x + edges[e].y - v;
258 ans.pb(e);
259 -- deg[u];
260 mark[v] = true;
261 if(deg[u] == 1)
262 remove(u);
263 }
264
265 vi find_roads(int n, vi v, vi u)
266 {
267 ::n = n;
268 ::m = v.size();;
269 memset(state, -1, sizeof state);
270 memset(ind, -1, sizeof ind);
271 For(i,0,m)
272 {
273 edges[i] = {v[i], u[i]};
274 ind[v[i]][u[i]] = ind[u[i]][v[i]] = i;
275 adj[v[i]].pb(u[i]), adj[u[i]].pb(v[i]);
276 }
277
278 dfs();
279 DFS();
280
281 memset(mark, 0, sizeof mark);
282 For(i,0,m) if(bit[i])
283 tree.pb(i);
284 For(i,0,n) calc_deg(i);
285 For(i,0,n) if(deg[i] == 1) remove(i);
286 query_for_forest(ans);
287 return ANS;
288 }
289
290 // --------- graderpublic -------------------------
291
292 static int MAXQ = 30000;
293 int MAX_Q;
294
295 static int q = 0;
296 //static int n, m, q = 0;
297
298 static vector<int> u, v;
CAPITOLUL 4. IOI 2017 490

299 static vector<bool> goal;


300
301 static void wrong_answer()
302 {
303 printf("NO\n");
304 exit(0);
305 }
306
307 static bool is_valid(const vector<int>& r)
308 {
309 if(int(r.size()) != n - 1)
310 return false;
311
312 for(int i = 0; i < n - 1; i++)
313 if (r[i] < 0 || r[i] >= m)
314 return false;
315
316 return true;
317 }
318
319 static int _count_common_roads_internal(const vector<int>& r)
320 {
321 if(!is_valid(r))
322 wrong_answer();
323
324 int common = 0;
325 for(int i = 0; i < n - 1; i++)
326 {
327 bool is_common = goal[r[i]];
328 if (is_common)
329 common++;
330 }
331
332 return common;
333 }
334
335 int count_common_roads(const vector<int>& r)
336 {
337 q++;
338 if(q > MAX_Q)
339 // if(q > MAXQ)
340
341 wrong_answer();
342
343 return _count_common_roads_internal(r);
344 }
345
346 int main()
347 {
348 auto t1 = clock();
349
350 std::freopen("../tests/4-07.in", "r", stdin);
351 //std::freopen("4-07.out.txt", "w", stdout);
352
353 string s;
354 getline (cin,s);
355 cout<<s<<"\n";
356
357 assert(2 == scanf("%d %d", &n, &m));
358 cout<<"m="<<m<<" n="<<n<<"\n";
359
360 assert(1 == scanf("%d", &MAX_Q));
361 cout<<"MAX_Q = "<<MAX_Q<<"\n\n";
362
363 u.resize(m);
364 v.resize(m);
365
366 for(int i = 0; i < m; i++)
367 assert(2 == scanf("%d %d", &u[i], &v[i]));
368
369 goal.resize(m, false);
370
371 for(int i = 0; i < n - 1; i++)
372 {
373 int id;
374 assert(1 == scanf("%d", &id));
CAPITOLUL 4. IOI 2017 491

375 goal[id] = true;


376 }
377
378 auto t2 = clock();
379
380 vector<int> res = find_roads(n, u, v);
381
382 auto t3 = clock();
383
384 if(_count_common_roads_internal(res) != n - 1) wrong_answer();
385
386 //printf("YES\n");
387 printf("YES q = %d\n\n",q);
388
389 auto t4 = clock();
390
391 // reset console output
392 freopen("CON", "w", stdout);
393
394 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
395 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
396 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
397
398 return 0;
399 }
400 /*
401 wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
402 m=124750 n=500
403 MAX_Q = 12000
404
405 YES q = 4794
406
407 t2-t1 = 0.297
408 t3-t2 = 1.109
409 t4-t3 = 0
410
411 Process returned 0 (0x0) execution time : 1.438 s
412 Press any key to continue.
413 */

Listing 4.5.2: simurgh-33904.cpp


1 // https://oj.uz/submission/33904 166 ms 7864 KB
2 #include "simurgh.h"
3
4 #include <cstdio>
5 #include <cassert>
6 #include <vector>
7 #include <cstdlib>
8 #include <string>
9
10 #include<ctime>
11 #include<iostream>
12
13 // ------------- simurgh ----------------
14
15 #include <algorithm>
16
17 using namespace std;
18
19 typedef pair<int, int> pi;
20
21 struct road
22 {
23 int i, x;
24 };
25
26 int n, m;
27 const int MAXM = 500 * 499 / 2;
28 vector<road> edge[500];
29 pi es[MAXM];
30
31 bool dfsTree[MAXM];
32 bool backGraph[MAXM];
33 vector<int> dfsQ;
CAPITOLUL 4. IOI 2017 492

34
35 int par[500];
36 int parEdge[500];
37 int dep[500];
38 int back[500];
39 int backEdge[500];
40 int ord = 0, base;
41
42 int royal[MAXM];
43 int sub[500];
44 int uf[500];
45
46 void dfs(int x, int p)
47 {
48 dep[x] = ++ord;
49 back[x] = dep[x];
50 backEdge[x] = -1;
51 for (road i : edge[x])
52 {
53 if (dep[i.x] != 0)
54 {
55 if (i.x != p && dep[i.x] < back[x])
56 {
57 back[x] = dep[i.x];
58 backEdge[x] = i.i;
59 }
60 continue;
61 }
62
63 dfsTree[i.i] = true;
64 dfsQ.push_back(i.i);
65 par[i.x] = x;
66 parEdge[i.x] = i.i;
67 dfs(i.x, x);
68
69 if (back[i.x] == dep[i.x])
70 royal[i.i] = 1;
71 else
72 if (back[i.x] < back[x])
73 {
74 back[x] = back[i.x];
75 backEdge[x] = backEdge[i.x];
76 }
77 }
78
79 if (back[x] < dep[x])
80 backGraph[backEdge[x]] = true;
81 }
82
83 int find(int x)
84 {
85 if (uf[x] != x) return uf[x] = find(uf[x]);
86 return x;
87 }
88
89 bool merge(int x, int y)
90 {
91 x = find(x);
92 y = find(y);
93 if (x == y) return false;
94 uf[x] = y;
95 return true;
96 }
97
98 int question(const vector<int> &vt)
99 {
100 vector<int> qs;
101 int other = 0;
102 for (int i = 0; i < n; ++i) uf[i] = i;
103 for (int i : vt)
104 {
105 qs.push_back(i);
106 merge(es[i].first, es[i].second);
107 }
108 for (int i : dfsQ)
109 {
CAPITOLUL 4. IOI 2017 493

110 if (merge(es[i].first, es[i].second))


111 {
112 qs.push_back(i);
113 if (royal[i] == 1) ++other;
114 }
115 }
116
117 return count_common_roads(qs) - other;
118 }
119
120 void bsearch(const vector<int> &vt, int s, int e, int c)
121 {
122 if (c == 0)
123 {
124 for (int i = s; i <= e; ++i) royal[vt[i]] = -1;
125 return;
126 }
127 if (c == e - s + 1)
128 {
129 for (int i = s; i <= e; ++i) royal[vt[i]] = 1;
130 return;
131 }
132
133 vector<int> qs;
134 int m = (s + e) / 2;
135 for (int i = s; i <= m; ++i) qs.push_back(vt[i]);
136 int d = question(qs);
137 bsearch(vt, s, m, d);
138 bsearch(vt, m + 1, e, c - d);
139 }
140
141 std::vector<int> find_roads(int N, std::vector<int> u, std::vector<int> v)
142 {
143 n = N;
144 m = u.size();
145 for (int i = 0; i < m; ++i)
146 {
147 es[i] = pi(u[i], v[i]);
148 edge[u[i]].push_back({ i, v[i] });
149 edge[v[i]].push_back({ i, u[i] });
150 }
151
152 dfs(0, -1);
153 base = count_common_roads(dfsQ);
154 vector<pi> es;
155 for (int i = 0; i < m; ++i)
156 {
157 if (backGraph[i]) es.push_back(pi(min(dep[u[i]], dep[v[i]]), i));
158 }
159
160 sort(es.begin(), es.end());
161
162 for (pi i : es)
163 {
164 int s = u[i.second], e = v[i.second];
165 if (dep[s] < dep[e]) swap(s, e);
166 int j = s;
167 bool allZero = true;
168 while (par[j] != e)
169 {
170 if (royal[parEdge[j]] == 0)
171 {
172 vector<int> qs;
173 for (int k : dfsQ)
174 {
175 if (k != parEdge[j]) qs.push_back(k);
176 }
177 qs.push_back(i.second);
178 sub[j] = count_common_roads(qs) - base;
179 if (sub[j] != 0)
180 {
181 allZero = false;
182 royal[parEdge[j]] = -sub[j];
183 royal[i.second] = sub[j];
184 }
185 }
CAPITOLUL 4. IOI 2017 494

186 else
187 allZero = false;
188 j = par[j];
189 }
190
191 vector<int> qs;
192 for (int k : dfsQ)
193 {
194 if (k != parEdge[j]) qs.push_back(k);
195 }
196
197 qs.push_back(i.second);
198 sub[j] = count_common_roads(qs) - base;
199 if (royal[parEdge[j]] != 0)
200 {
201 allZero = false;
202 royal[i.second] = royal[parEdge[j]] + 2 * sub[j];
203 }
204 else
205 if (sub[j] != 0)
206 {
207 allZero = false;
208 royal[parEdge[j]] = -sub[j];
209 royal[i.second] = sub[j];
210 }
211
212 if (allZero)
213 {
214 j = s;
215 while (j != e)
216 {
217 royal[parEdge[j]] = -1;
218 j = par[j];
219 }
220 royal[i.second] = -1;
221 continue;
222 }
223
224 j = s;
225 while (j != e)
226 {
227 if (royal[parEdge[j]] == 0)
228 royal[parEdge[j]] = royal[i.second] - 2 * sub[j];
229 j = par[j];
230 }
231 }
232
233 for (int i = 0; i < n; ++i)
234 {
235 vector<int> es;
236 for (road j : edge[i])
237 {
238 if (royal[j.i] == 0) es.push_back(j.i);
239 }
240 bsearch(es, 0, es.size() - 1, question(es));
241 }
242
243 vector<int> ret;
244 for (int i = 0; i < m; ++i)
245 {
246 if (royal[i] == 1) ret.push_back(i);
247 }
248
249 return ret;
250 }
251
252 // --------- graderpublic -------------------------
253
254 static int MAXQ = 30000;
255 int MAX_Q;
256
257 static int q = 0;
258 //static int n, m, q = 0;
259
260 static vector<int> u, v;
261 static vector<bool> goal;
CAPITOLUL 4. IOI 2017 495

262
263 static void wrong_answer()
264 {
265 printf("NO\n");
266 exit(0);
267 }
268
269 static bool is_valid(const vector<int>& r)
270 {
271 if(int(r.size()) != n - 1)
272 return false;
273
274 for(int i = 0; i < n - 1; i++)
275 if (r[i] < 0 || r[i] >= m)
276 return false;
277
278 return true;
279 }
280
281 static int _count_common_roads_internal(const vector<int>& r)
282 {
283 if(!is_valid(r))
284 wrong_answer();
285
286 int common = 0;
287 for(int i = 0; i < n - 1; i++)
288 {
289 bool is_common = goal[r[i]];
290 if (is_common)
291 common++;
292 }
293
294 return common;
295 }
296
297 int count_common_roads(const vector<int>& r)
298 {
299 q++;
300 if(q > MAX_Q)
301 // if(q > MAXQ)
302
303 wrong_answer();
304
305 return _count_common_roads_internal(r);
306 }
307
308 int main()
309 {
310 auto t1 = clock();
311
312 std::freopen("../tests/4-07.in", "r", stdin);
313 //std::freopen("4-07.out.txt", "w", stdout);
314
315 string s;
316 getline (cin,s);
317 cout<<s<<"\n";
318
319 assert(2 == scanf("%d %d", &n, &m));
320 cout<<"m="<<m<<" n="<<n<<"\n";
321
322 assert(1 == scanf("%d", &MAX_Q));
323 cout<<"MAX_Q = "<<MAX_Q<<"\n\n";
324
325 u.resize(m);
326 v.resize(m);
327
328 for(int i = 0; i < m; i++)
329 assert(2 == scanf("%d %d", &u[i], &v[i]));
330
331 goal.resize(m, false);
332
333 for(int i = 0; i < n - 1; i++)
334 {
335 int id;
336 assert(1 == scanf("%d", &id));
337 goal[id] = true;
CAPITOLUL 4. IOI 2017 496

338 }
339
340 auto t2 = clock();
341
342 vector<int> res = find_roads(n, u, v);
343
344 auto t3 = clock();
345
346 if(_count_common_roads_internal(res) != n - 1) wrong_answer();
347
348 //printf("YES\n");
349 printf("YES q = %d\n\n",q);
350
351 auto t4 = clock();
352
353 // reset console output
354 freopen("CON", "w", stdout);
355
356 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
357 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
358 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
359
360 return 0;
361 }
362 /*
363 wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
364 m=124750 n=500
365 MAX_Q = 12000
366
367 YES q = 2536
368
369 t2-t1 = 0.406
370 t3-t2 = 0.39
371 t4-t3 = 0
372
373 Process returned 0 (0x0) execution time : 0.813 s
374 Press any key to continue.
375 */

Listing 4.5.3: simurgh-70374.cpp


1 // https://oj.uz/submission/70374 303 ms 16344 KB
2 #include "simurgh.h"
3
4 #include<ctime>
5
6 // ------------- simurgh ----------------
7
8 #include<bits/stdc++.h>
9
10 using namespace std;
11
12 #define pii pair<int,int>
13 #define X first
14 #define Y second
15
16 const int maxn = 500 + 5;
17 const int maxm = 250000 + 5;
18
19 int n,m;
20 pii e[maxm];
21
22 int head[maxn];
23 vector<pii> way[maxn], edge[maxn];
24 int ban[maxm], res[maxm];
25
26 vector<int> tree;
27
28 int h[maxn], par[maxn], rec[maxn];
29
30 vector<int> p;
31 int cut[maxm];
32
33 int ask_count;
34
CAPITOLUL 4. IOI 2017 497

35 int ask(int x, int y)


36 {
37 for(auto &id : tree) if(id==x) id = y;
38 ask_count++;
39
40 int ret = count_common_roads(tree);
41 for(auto &id : tree) if(id==y) id = x;
42 return ret;
43 }
44
45 void dfs(int u, int last, int lid)
46 {
47 h[u] = h[last] + 1;
48 par[u] = last;
49 rec[u] = lid;
50 for(auto t : way[u])
51 {
52 int v = t.X, id = t.Y;
53 if(v==last) continue;
54 dfs(v,u,id);
55 }
56 }
57
58 void lca(int u, int v)
59 {
60 if(u==v) return ;
61 if(h[u] >= h[v])
62 {
63 p.push_back(rec[u]);
64 lca(par[u],v);
65 }
66 else
67 lca(v,u);
68 }
69
70 int findhead(int x)
71 {
72 if(x==head[x]) return x;
73 return head[x] = findhead(head[x]);
74 }
75
76 int get(int x, int l, int r)
77 {
78 int val = 0;
79 vector<int> vec;
80 for(int i=0;i<n;i++) head[i] = i;
81 for(int i=l;i<=r;i++)
82 {
83 int v = edge[x][i].X, id = edge[x][i].Y;
84 head[findhead(x)] = findhead(v);
85 vec.push_back(id);
86 val -= res[id];
87 }
88
89 for(auto id : tree)
90 {
91 int u = e[id].X, v = e[id].Y;
92 if(findhead(u)!=findhead(v))
93 {
94 head[findhead(u)] = findhead(v);
95 vec.push_back(id);
96 val -= res[id];
97 }
98 }
99 ask_count++;
100 if(ask_count > 8000) assert(0);
101 val += count_common_roads(vec);
102 return val;
103 }
104
105 vector<int> find_roads(int N, vector<int> U, vector<int> V)
106 {
107 n = N; m = U.size();
108 for(int i=0;i<n;i++) head[i] = i;
109 for(int i=0;i<m;i++)
110 {
CAPITOLUL 4. IOI 2017 498

111 int u = U[i], v = V[i];


112 e[i] = {u,v};
113 if(findhead(u)!=findhead(v))
114 {
115 head[findhead(u)] = findhead(v);
116 way[u].push_back({v,i});
117 way[v].push_back({u,i});
118 tree.push_back(i);
119 ban[i] = 1;
120 res[i] = -1;
121 }
122 else
123 {
124 edge[u].push_back({v,i});
125 edge[v].push_back({u,i});
126 }
127 }
128
129 dfs(0,-1,-1);
130
131 int ori = ask(-1,-1);
132 for(int i=0;i<m;i++)
133 {
134 if(ban[i]) continue;
135 int u = e[i].X, v = e[i].Y, wow = i;
136
137 p.clear();
138 lca(u,v);
139
140 int good = 0;
141 for(auto id : p) if(res[id]==-1) good = 1;
142 if(!good) continue;
143
144 int x = -1;
145 for(auto id : p) if(res[id]!=-1) x = id;
146 if(x==-1)
147 {
148 for(auto id : p) cut[id] = ask(id,wow);
149 for(auto id : p) if(cut[id] - ori == 1) res[wow] = 1;
150 for(auto id : p) res[id] = ori - cut[id] + res[wow];
151 }
152 else
153 {
154 res[wow] = ask(x,wow) - ori + res[x];
155 for(auto id : p)
156 if(res[id]==-1)
157 res[id] = ori - ask(id,wow) + res[wow];
158 }
159 }
160
161 for(int i=0;i<m;i++)
162 {
163 if(res[i] == -1) res[i] = 1;
164 }
165
166 for(int x=0;x<n;x++)
167 {
168 for(int ext=get(x,0,edge[x].size()-1);ext>0;ext--)
169 {
170 int l = 0, r = edge[x].size()-1, mid, pos = -1;
171 while(l<=r)
172 {
173 mid = (l+r)/2;
174 if(get(x,0,mid) >= 1)
175 {
176 pos = edge[x][mid].Y;
177 r = mid-1;
178 }
179 else l = mid+1;
180 }
181 if(pos==-1) break;
182 res[pos] = 1;
183 }
184 }
185
186 vector<int> ans;
CAPITOLUL 4. IOI 2017 499

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


188 if(res[i])
189 ans.push_back(i);
190 return ans;
191 }
192
193 // --------- graderpublic -------------------------
194
195 static int MAXQ = 30000;
196 int MAX_Q;
197
198 static int q = 0;
199 //static int n, m, q = 0;
200
201 static vector<int> u, v;
202 static vector<bool> goal;
203
204 static void wrong_answer()
205 {
206 printf("NO\n");
207 exit(0);
208 }
209
210 static bool is_valid(const vector<int>& r)
211 {
212 if(int(r.size()) != n - 1)
213 return false;
214
215 for(int i = 0; i < n - 1; i++)
216 if (r[i] < 0 || r[i] >= m)
217 return false;
218
219 return true;
220 }
221
222 static int _count_common_roads_internal(const vector<int>& r)
223 {
224 if(!is_valid(r))
225 wrong_answer();
226
227 int common = 0;
228 for(int i = 0; i < n - 1; i++)
229 {
230 bool is_common = goal[r[i]];
231 if (is_common)
232 common++;
233 }
234
235 return common;
236 }
237
238 int count_common_roads(const vector<int>& r)
239 {
240 q++;
241 if(q > MAX_Q)
242 // if(q > MAXQ)
243
244 wrong_answer();
245
246 return _count_common_roads_internal(r);
247 }
248
249 int main()
250 {
251 auto t1 = clock();
252
253 std::freopen("../tests/4-07.in", "r", stdin);
254 //std::freopen("4-07.out.txt", "w", stdout);
255
256 string s;
257 getline (cin,s);
258 cout<<s<<"\n";
259
260 assert(2 == scanf("%d %d", &n, &m));
261 cout<<"m="<<m<<" n="<<n<<"\n";
262
CAPITOLUL 4. IOI 2017 500

263 assert(1 == scanf("%d", &MAX_Q));


264 cout<<"MAX_Q = "<<MAX_Q<<"\n\n";
265
266 u.resize(m);
267 v.resize(m);
268
269 for(int i = 0; i < m; i++)
270 assert(2 == scanf("%d %d", &u[i], &v[i]));
271
272 goal.resize(m, false);
273
274 for(int i = 0; i < n - 1; i++)
275 {
276 int id;
277 assert(1 == scanf("%d", &id));
278 goal[id] = true;
279 }
280
281 auto t2 = clock();
282
283 vector<int> res = find_roads(n, u, v);
284
285 auto t3 = clock();
286
287 if(_count_common_roads_internal(res) != n - 1) wrong_answer();
288
289 //printf("YES\n");
290 printf("YES q = %d\n\n",q);
291
292 auto t4 = clock();
293
294 // reset console output
295 freopen("CON", "w", stdout);
296
297 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
298 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
299 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
300
301 return 0;
302 }
303 /*
304 wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
305 m=124750 n=500
306 MAX_Q = 12000
307
308 YES q = 5685
309
310 t2-t1 = 0.39
311 t3-t2 = 1.235
312 t4-t3 = 0
313
314 Process returned 0 (0x0) execution time : 1.641 s
315 Press any key to continue.
316 */

Listing 4.5.4: simurgh-70729.cpp


1 // https://oj.uz/submission/70729 213 ms 30820 KB
2 #include "simurgh.h"
3
4 // ------------- simurgh ----------------
5
6 #include <bits/stdc++.h>
7
8 using namespace std;
9
10 using pii = pair<int, int>;
11 #define F first
12 #define S second
13
14 const int N = 510;
15 const int M = N*(N-1)/2;
16 const int LIM = 30000;
17
18 int n, m;
CAPITOLUL 4. IOI 2017 501

19 vector<int> U, V; // edge list


20 vector<pii> G[N]; // adj list
21 vector<int> T; // tree edges
22 vector<pii> GT[N]; // tree adj list
23 bool is_tree[M];
24
25 int ans[M]; // answer to each edge (-1 = unknown)
26 int defcnt; // answer for the default spanning tree
27
28 int tick;
29 pii disc[N], low[N];
30 void dfs(int u, int p)
31 {
32 disc[u] = low[u] = pii(++tick, -1);
33 for (auto v : G[u])
34 {
35 if (!disc[v.F].F)
36 {
37 // push edge to tree
38 is_tree[v.S] = true;
39 T.push_back(v.S);
40
41 GT[u].emplace_back(v.F, v.S);
42 GT[v.F].emplace_back(u, v.S);
43
44 // tarjan’s algorithm
45 dfs(v.F, u);
46 low[u] = min(low[u], low[v.F]);
47 if (low[v.F].F > disc[u].F)
48 ans[v.S] = 1;
49 }
50 else
51 if (v.F != p)
52 {
53 low[u] = min(low[u], pii(disc[v.F].F, v.S));
54 }
55 }
56 }
57
58 vector<int> find_path(int u, int e, int t)
59 {
60 if (u == t)
61 {
62 vector<int> ret;
63 ret.push_back(e);
64 return ret;
65 }
66 for (auto v : GT[u]) if (v.S != e)
67 {
68 vector<int> ret = find_path(v.F, v.S, t);
69 if (!ret.empty())
70 {
71 if (e != -1)
72 ret.push_back(e);
73 return ret;
74 }
75 }
76 return vector<int>();
77 }
78
79 int get_changes(int rem, int add)
80 {
81 vector<int> q(T);
82 q.erase(find(q.begin(), q.end(), rem));
83 q.push_back(add);
84 return count_common_roads(q) - defcnt;
85 }
86
87 void query_cycle(int ce)
88 {
89 if (ans[ce] != -1) return;
90 int s = U[ce], t = V[ce];
91 vector<int> path = find_path(s, -1, t);
92
93 // find edge in path that we already know answer to
94 int fnd = -1;
CAPITOLUL 4. IOI 2017 502

95 for (auto e : path)


96 {
97 if (ans[e] != -1)
98 {
99 fnd = e;
100 break;
101 }
102 }
103
104 if (fnd != -1)
105 { // case where we can quickly determine ce’s value
106 ans[ce] = ans[fnd] + get_changes(fnd, ce);
107 for (auto e : path)
108 { // update other edge in path we don’t know
109 if (ans[e] == -1)
110 ans[e] = ans[ce] - get_changes(e, ce);
111 }
112 }
113 else
114 { // don’t know, so query everything in the cycle
115 for (auto e : path)
116 {
117 int d = get_changes(e, ce);
118 if (d == 1)
119 ans[ce] = 1, ans[e] = 0;
120 else if (d == -1)
121 ans[ce] = 0, ans[e] = 1;
122 }
123
124 if (ans[ce] == -1) ans[ce] = 0;
125 for (auto e : path)
126 {
127 if (ans[e] == -1)
128 ans[e] = ans[ce];
129 }
130 }
131 }
132
133 int par[N];
134
135 int root(int u)
136 {
137 if (par[u] == u) return u;
138 return par[u] = root(par[u]);
139 }
140
141 bool merge(int u, int v)
142 {
143 u = root(u), v = root(v);
144 if (u == v) return false;
145 par[u] = v;
146 return true;
147 }
148
149 int query_adj(vector<int> &adj)
150 {
151 if (adj.empty()) return 0;
152
153 iota(par, par+n, 0);
154
155 vector<int> q;
156 for (auto e : adj)
157 {
158 if (merge(U[e], V[e]))
159 q.push_back(e);
160 }
161
162 int cnt = 0;
163 for (auto e : T)
164 {
165 if (merge(U[e], V[e]))
166 {
167 cnt += ans[e];
168 q.push_back(e);
169 }
170 }
CAPITOLUL 4. IOI 2017 503

171
172 int ret = count_common_roads(q);
173 return ret-cnt;
174 }
175
176 void solve(vector<int> &adj, int cnt = -1)
177 {
178 if (cnt == -1)
179 cnt = query_adj(adj);
180 if (cnt == 0)
181 {
182 for (auto e : adj)
183 ans[e] = 0;
184 return;
185 }
186
187 if (cnt == adj.size())
188 {
189 for (auto e : adj)
190 ans[e] = 1;
191 return;
192 }
193
194 int mid = adj.size()/2;
195 vector<int> L(adj.begin(), adj.begin()+mid);
196 vector<int> R(adj.begin()+mid, adj.end());
197
198 int lcnt = query_adj(L);
199 solve(L, lcnt);
200 solve(R, cnt-lcnt);
201 }
202
203 vector<int> find_roads(int _n, vector<int> _U, vector<int> _V)
204 {
205 // create adj list
206 n = _n;
207 U = _U, V = _V;
208 m = U.size();
209 for (int i = 0; i < m; ++i)
210 {
211 int u = U[i], v = V[i];
212 G[u].emplace_back(v, i);
213 G[v].emplace_back(u, i);
214 }
215
216 // build dfs tree and find answer for the tree
217 fill(ans, ans+m, -1);
218 dfs(0, -1);
219 defcnt = count_common_roads(T);
220 for (int i = 0; i < n; ++i)
221 {
222 if (low[i].S != -1)
223 query_cycle(low[i].S);
224 }
225
226 // find answer for each node
227 for (int u = 0; u < n; ++u)
228 {
229 vector<int> adj;
230 for (auto v : G[u])
231 {
232 if (ans[v.S] == -1)
233 adj.push_back(v.S);
234 }
235 solve(adj);
236 }
237
238 // return answer
239 vector<int> ret;
240 for (int i = 0; i < m; ++i)
241 {
242 if (ans[i])
243 ret.push_back(i);
244 }
245 return ret;
246 }
CAPITOLUL 4. IOI 2017 504

247
248 // --------- graderpublic -------------------------
249
250 static int MAXQ = 30000;
251 int MAX_Q;
252
253 static int q = 0;
254 //static int n, m, q = 0;
255
256 static vector<int> u, v;
257 static vector<bool> goal;
258
259 static void wrong_answer()
260 {
261 printf("NO\n");
262 exit(0);
263 }
264
265 static bool is_valid(const vector<int>& r)
266 {
267 if(int(r.size()) != n - 1)
268 return false;
269
270 for(int i = 0; i < n - 1; i++)
271 if (r[i] < 0 || r[i] >= m)
272 return false;
273
274 return true;
275 }
276
277 static int _count_common_roads_internal(const vector<int>& r)
278 {
279 if(!is_valid(r))
280 wrong_answer();
281
282 int common = 0;
283 for(int i = 0; i < n - 1; i++)
284 {
285 bool is_common = goal[r[i]];
286 if (is_common)
287 common++;
288 }
289
290 return common;
291 }
292
293 int count_common_roads(const vector<int>& r)
294 {
295 q++;
296 if(q > MAX_Q)
297 // if(q > MAXQ)
298
299 wrong_answer();
300
301 return _count_common_roads_internal(r);
302 }
303
304 int main()
305 {
306 auto t1 = clock();
307
308 std::freopen("../tests/4-07.in", "r", stdin);
309 //std::freopen("4-07.out.txt", "w", stdout);
310
311 string s;
312 getline (cin,s);
313 cout<<s<<"\n";
314
315 assert(2 == scanf("%d %d", &n, &m));
316 cout<<"m="<<m<<" n="<<n<<"\n";
317
318 assert(1 == scanf("%d", &MAX_Q));
319 cout<<"MAX_Q = "<<MAX_Q<<"\n\n";
320
321 u.resize(m);
322 v.resize(m);
CAPITOLUL 4. IOI 2017 505

323
324 for(int i = 0; i < m; i++)
325 assert(2 == scanf("%d %d", &u[i], &v[i]));
326
327 goal.resize(m, false);
328
329 for(int i = 0; i < n - 1; i++)
330 {
331 int id;
332 assert(1 == scanf("%d", &id));
333 goal[id] = true;
334 }
335
336 auto t2 = clock();
337
338 vector<int> res = find_roads(n, u, v);
339
340 auto t3 = clock();
341
342 if(_count_common_roads_internal(res) != n - 1) wrong_answer();
343
344 //printf("YES\n");
345 printf("YES q = %d\n\n",q);
346
347 auto t4 = clock();
348
349 // reset console output
350 freopen("CON", "w", stdout);
351
352 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
353 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
354 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
355
356 return 0;
357 }
358 /*
359 wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
360 m=124750 n=500
361 MAX_Q = 12000
362
363 YES q = 2541
364
365 t2-t1 = 0.343
366 t3-t2 = 0.422
367 t4-t3 = 0.016
368
369 Process returned 0 (0x0) execution time : 0.797 s
370 Press any key to continue.
371 */

Listing 4.5.5: simurgh-136567.cpp


1 // https://oj.uz/submission/136567 613 ms 5624 KB
2 #include "simurgh.h"
3
4 // ------------- simurgh ----------------
5
6 #include "simurgh.h"
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 #define pb push_back
12 #define MP make_pair
13
14 #define F first
15 #define S second
16
17 typedef pair<int,int> pii;
18
19 vector<int> U,V;
20 vector<pii> G[505];
21 pii pa[505];
22
23 queue<int> q;
CAPITOLUL 4. IOI 2017 506

24
25 int type[505],dep[505],deg[505],boss[505],n;
26 set<int> s;
27 bitset<505> vis,ed;
28 bitset<250000> used;
29
30 int ccr()
31 {
32 return count_common_roads(vector<int>(s.begin(),s.end()));
33 }
34
35 void dfs(int u,int f,int num,int d)
36 {
37 pa[u]=MP(f,num),dep[u]=d,vis[u]=1;
38 if(~num) used[num]=1,s.insert(num);
39 for(pii i:G[u])
40 if(!vis[i.F])
41 dfs(i.F,u,i.S,d+1);
42 }
43
44 int finds(int x)
45 {
46 if(x==boss[x]) return x;
47 return boss[x]=finds(boss[x]);
48 }
49
50 int Union(int a,int b)
51 {
52 a=finds(a),b=finds(b);
53 if(a==b) return 0;
54 return boss[a]=b,1;
55 }
56
57 int ccr_forest(vector<int> f)
58 {
59 int cnt=0;
60 for(int i=0;i<n;++i)
61 boss[i]=i;
62
63 set<int> tmp;
64 for(int i:f)
65 tmp.insert(i),Union(U[i],V[i]);
66 for(int i=1;i<n;++i)
67 if(Union(i,pa[i].F))
68 cnt+=type[i],tmp.insert(pa[i].S);
69 tmp.swap(s),cnt=ccr()-cnt,tmp.swap(s);
70
71 return cnt;
72 }
73
74 int leaf_finding(int u)
75 {
76 vector<int> candi;
77 for(pii i:G[u])
78 if(!ed[i.F])
79 candi.pb(i.S);
80
81 int l=0,r=candi.size()-1;
82 while(l<r)
83 {
84 vector<int> v;
85 int m=l+r>>1;
86 for(int i=l;i<=m;++i)
87 v.pb(candi[i]);
88 if(ccr_forest(v)>=1) r=m;
89 else l=m+1;
90 }
91 return candi[l];
92 }
93
94 vector<int> find_roads(int N, vector<int> u, vector<int> v)
95 {
96 n=N,U=u,V=v;
97 vector<int> ans;
98 for(int i=0;i<u.size();++i)
99 G[u[i]].pb(MP(v[i],i)),G[v[i]].pb(MP(u[i],i));
CAPITOLUL 4. IOI 2017 507

100
101 dfs(0,-1,-1,0),fill(type,type+N,-1);
102
103 int cur=ccr();
104 for(int i=0;i<u.size();++i)
105 {
106 if(used[i]) continue;
107 int cnt=0,sz=0,tp=-1,a=u[i],b=v[i];
108 pii sw;
109 while(a!=b)
110 {
111 if(dep[a]<dep[b]) swap(a,b);
112 if(~type[a]) sw=MP(pa[a].S,type[a]);
113 cnt+=!~type[a],++sz,a=pa[a].F;
114 }
115 if(!cnt) continue;
116 a=u[i],b=v[i];
117 if(cnt==sz)
118 {
119 vector<int> v;
120 while(a!=b)
121 {
122 if(dep[a]<dep[b]) swap(a,b);
123 s.erase(pa[a].S),s.insert(i);
124 int tmp=ccr();
125 if(tmp==cur) v.pb(a);
126 else
127 {
128 if(tmp>cur) tp=1,type[a]=0;
129 else tp=0,type[a]=1;
130 for(int j:v)
131 type[j]=tp;
132 s.erase(i),s.insert(pa[a].S),a=pa[a].F;
133 break;
134 }
135 s.erase(i),s.insert(pa[a].S),a=pa[a].F;
136 }
137 if(a==b&&!~tp)
138 for(int j:v)
139 type[j]=0;
140 }
141 else
142 s.erase(sw.F),
143 s.insert(i),
144 tp=(sw.S==0)^(ccr()==cur),
145 s.erase(i),
146 s.insert(sw.F);
147
148 while(a!=b)
149 {
150 if(dep[a]<dep[b]) swap(a,b);
151 s.erase(pa[a].S),s.insert(i);
152 if(!~type[a])
153 type[a]=tp^(ccr()!=cur);
154 s.erase(i),s.insert(pa[a].S),a=pa[a].F;
155 }
156 }
157
158 for(int i=1;i<n;++i)
159 if(!~type[i]) type[i]=1;
160
161 for(int i=0;i<n;++i)
162 {
163 vector<int> v;
164 for(pii j:G[i])
165 v.pb(j.S);
166 deg[i]=ccr_forest(v);
167 if(deg[i]==1)
168 q.push(i);
169 }
170
171 while(q.size()>1)
172 {
173 int u=q.front();
174 q.pop(),
175 ed[u]=1,
CAPITOLUL 4. IOI 2017 508

176 ans.pb(leaf_finding(u)),
177 u^=U[ans.back()]^V[ans.back()];
178 if(--deg[u]==1)
179 q.push(u);
180 }
181 return ans;
182 }
183
184 // --------- graderpublic -------------------------
185
186 static int MAXQ = 30000;
187 int MAX_Q;
188
189 static int m,qq = 0;
190 //static int n, m, q = 0;
191
192 static vector<int> u, v;
193 static vector<bool> goal;
194
195 static void wrong_answer()
196 {
197 printf("NO\n");
198 exit(0);
199 }
200
201 static bool is_valid(const vector<int>& r)
202 {
203 if(int(r.size()) != n - 1)
204 return false;
205
206 for(int i = 0; i < n - 1; i++)
207 if (r[i] < 0 || r[i] >= m)
208 return false;
209
210 return true;
211 }
212
213 static int _count_common_roads_internal(const vector<int>& r)
214 {
215 if(!is_valid(r))
216 wrong_answer();
217
218 int common = 0;
219 for(int i = 0; i < n - 1; i++)
220 {
221 bool is_common = goal[r[i]];
222 if (is_common)
223 common++;
224 }
225
226 return common;
227 }
228
229 int count_common_roads(const vector<int>& r)
230 {
231 qq++;
232 if(qq > MAX_Q)
233 // if(q > MAXQ)
234
235 wrong_answer();
236
237 return _count_common_roads_internal(r);
238 }
239
240 int main()
241 {
242 auto t1 = clock();
243
244 std::freopen("../tests/4-07.in", "r", stdin);
245 //std::freopen("4-07.out.txt", "w", stdout);
246
247 string s;
248 getline (cin,s);
249 cout<<s<<"\n";
250
251 assert(2 == scanf("%d %d", &n, &m));
CAPITOLUL 4. IOI 2017 509

252 cout<<"m="<<m<<" n="<<n<<"\n";


253
254 assert(1 == scanf("%d", &MAX_Q));
255 cout<<"MAX_Q = "<<MAX_Q<<"\n\n";
256
257 u.resize(m);
258 v.resize(m);
259
260 for(int i = 0; i < m; i++)
261 assert(2 == scanf("%d %d", &u[i], &v[i]));
262
263 goal.resize(m, false);
264
265 for(int i = 0; i < n - 1; i++)
266 {
267 int id;
268 assert(1 == scanf("%d", &id));
269 goal[id] = true;
270 }
271
272 auto t2 = clock();
273
274 vector<int> res = find_roads(n, u, v);
275
276 auto t3 = clock();
277
278 if(_count_common_roads_internal(res) != n - 1) wrong_answer();
279
280 //printf("YES\n");
281 printf("YES qq = %d\n\n",qq);
282
283 auto t4 = clock();
284
285 // reset console output
286 freopen("CON", "w", stdout);
287
288 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
289 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
290 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
291
292 return 0;
293 }
294 /*
295 wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
296 m=124750 n=500
297 MAX_Q = 12000
298
299 YES qq = 4800
300
301 t2-t1 = 0.312
302 t3-t2 = 4.481
303 t4-t3 = 0
304
305 Process returned 0 (0x0) execution time : 4.841 s
306 Press any key to continue.
307 */

Listing 4.5.6: simurgh-137723.cpp


1 // https://oj.uz/submission/137723 386 ms 8176 KB
2 #include "simurgh.h"
3
4 #include<bits/stdc++.h>
5
6 using namespace std;
7
8 #define ll long long
9 #define pb push_back
10
11 const int maxn = 5e2 + 20;
12 const int maxm = maxn * maxn + 20;
13
14 int from[maxm] , to[maxm] , par[maxn] , h[maxn] , is[maxm] , ed[maxm] , num;
15
16 vector<int> adj[maxn] , bc[maxn] , ind;
CAPITOLUL 4. IOI 2017 510

17
18 bool visited[maxn] , tree[maxm];
19
20 void plant(int v)
21 {
22 visited[v] = 1;
23 for(auto e : adj[v])
24 {
25 int u = from[e] ^ to[e] ^ v;
26 if(!visited[u])
27 {
28 ind.pb(e);
29 h[u] = h[v] + 1;
30 par[u] = e;
31 tree[e] = 1;
32 plant(u);
33 }
34 else if(h[u] < h[v] - 1)
35 bc[v].pb(e);
36 }
37 }
38
39 vector<int> rem(vector<int> x , int y)
40 {
41 vector<int> nw;
42 for(auto k : x)
43 if(k != y)
44 nw.pb(k);
45
46 return nw;
47 }
48
49 bool cmp(int x , int y)
50 {
51 x = min(h[from[x]] , h[to[x]]);
52 y = min(h[from[y]] , h[to[y]]);
53
54 return x > y;
55 }
56
57 int wtf[maxm];
58
59 int get(vector<int> &bc , int sz)
60 {
61 if(!sz)
62 return 0;
63
64 vector<int> shit;
65 for(int i = 0; i < sz; i++)
66 {
67 int e = bc[i];
68 tree[wtf[e]] = 0;
69 shit.pb(e);
70 }
71
72 int ts = 0;
73 for(auto x : ind)
74 if(tree[x])
75 {
76 if(is[x] == 1)
77 ts++;
78 shit.pb(x);
79 }
80
81 for(int i = 0; i < sz; i++)
82 {
83 int e = bc[i];
84 tree[wtf[e]] = 1;
85 }
86
87 return count_common_roads(shit) - ts;
88 }
89
90 void dfs(int v)
91 {
92 for(auto e : adj[v])
CAPITOLUL 4. IOI 2017 511

93 {
94 int u = from[e] ^ to[e] ^ v;
95 if(h[u] == h[v] + 1)
96 dfs(u);
97 }
98
99 if(bc[v].empty())
100 return;
101
102 int last = v;
103
104 sort(bc[v].begin() , bc[v].end() , cmp);
105 for(auto e : bc[v])
106 {
107 while(!last);
108 wtf[e] = par[last];
109 last = from[e] ^ to[e] ^ v;
110 }
111
112 int k = bc[v].size() , st = 0 , stl = 0 , shit = get(bc[v] , k);
113 while(k && shit > st)
114 {
115 int l = stl , r = k;
116 while(r - l > 1)
117 {
118 int m = (l + r) / 2;
119 if(get(bc[v] , m) > st)
120 r = m;
121 else
122 l = m;
123 }
124
125 is[bc[v][r - 1]] = 1;
126 stl = r;
127 st++;
128 }
129
130 }
131
132 vector<int> find_roads(int n,vector<int> A,vector<int> B)
133 {
134 n = n;
135 int m = A.size();
136 for(int i = 0; i < m; i++)
137 {
138 int a = A[i] , b = B[i];
139
140 adj[a].pb(i);
141 adj[b].pb(i);
142
143 from[i] = a , to[i] = b;
144 }
145
146 plant(0);
147
148 memset(ed , -1 , sizeof ed);
149
150 num = count_common_roads(ind);
151 for(int i = 0; i < m; i++)
152 if(!tree[i])
153 {
154 int v = from[i] , u = to[i];
155 if(h[v] < h[u])
156 swap(v , u);
157
158 vector<int> ed;
159
160 bool has0 = 0;
161 while(v != u)
162 {
163 if(is[par[v]] == 0)
164 has0 = 1;
165
166 ed.pb(par[v]);
167 v = from[par[v]] ^ to[par[v]] ^ v;
168 }
CAPITOLUL 4. IOI 2017 512

169
170 if(!has0)
171 continue;
172
173 vector<int> eq;
174 for(auto e : ed)
175 {
176 if(is[e] != 0 && is[i] != 0)
177 continue;
178
179 ind = rem(ind , e);
180 ind.pb(i);
181 int nw = count_common_roads(ind);
182 ind.pop_back() , ind.pb(e);
183
184 if(nw == num)
185 {
186 if(is[e] != 0)
187 is[i] = is[e];
188 else
189 eq.pb(e);
190 }
191 else if(nw == num + 1)
192 {
193 is[i] = 1;
194 is[e] = -1;
195 }
196 else
197 {
198 is[e] = 1;
199 is[i] = -1;
200 }
201 }
202
203 if((int)eq.size() == (int)ed.size())
204 is[i] = -1;
205
206 for(auto e : eq)
207 is[e] = is[i];
208 }
209
210 for(auto x : ind)
211 if(is[x] == 0)
212 is[x] = 1;
213
214 dfs(0);
215
216 vector<int> ans;
217 for(int i = 0; i < m; i++)
218 {
219 if(!is[i])
220 is[i] = -1;
221 if(is[i] == 1)
222 ans.pb(i);
223 }
224
225 while(count_common_roads(ans) != n - 1);
226
227 return ans;
228 }
229
230 // --------- graderpublic -------------------------
231
232 static int MAXQ = 30000;
233 int MAX_Q;
234
235 static int n, m, q = 0;
236
237 static vector<int> u, v;
238 static vector<bool> goal;
239
240 static void wrong_answer()
241 {
242 printf("NO\n");
243 exit(0);
244 }
CAPITOLUL 4. IOI 2017 513

245
246 static bool is_valid(const vector<int>& r)
247 {
248 if(int(r.size()) != n - 1)
249 return false;
250
251 for(int i = 0; i < n - 1; i++)
252 if (r[i] < 0 || r[i] >= m)
253 return false;
254
255 return true;
256 }
257
258 static int _count_common_roads_internal(const vector<int>& r)
259 {
260 if(!is_valid(r))
261 wrong_answer();
262
263 int common = 0;
264 for(int i = 0; i < n - 1; i++)
265 {
266 bool is_common = goal[r[i]];
267 if (is_common)
268 common++;
269 }
270
271 return common;
272 }
273
274 int count_common_roads(const vector<int>& r)
275 {
276 q++;
277 if(q > MAX_Q)
278 // if(q > MAXQ)
279
280 wrong_answer();
281
282 return _count_common_roads_internal(r);
283 }
284
285 int main()
286 {
287 auto t1 = clock();
288
289 std::freopen("../tests/4-07.in", "r", stdin);
290 //std::freopen("4-07.out.txt", "w", stdout);
291
292 string s;
293 getline (cin,s);
294 cout<<s<<"\n";
295
296 assert(2 == scanf("%d %d", &n, &m));
297 cout<<"m="<<m<<" n="<<n<<"\n";
298
299 assert(1 == scanf("%d", &MAX_Q));
300 cout<<"MAX_Q = "<<MAX_Q<<"\n\n";
301
302 u.resize(m);
303 v.resize(m);
304
305 for(int i = 0; i < m; i++)
306 assert(2 == scanf("%d %d", &u[i], &v[i]));
307
308 goal.resize(m, false);
309
310 for(int i = 0; i < n - 1; i++)
311 {
312 int id;
313 assert(1 == scanf("%d", &id));
314 goal[id] = true;
315 }
316
317 auto t2 = clock();
318
319 vector<int> res = find_roads(n, u, v);
320
CAPITOLUL 4. IOI 2017 514

321 auto t3 = clock();


322
323 if(_count_common_roads_internal(res) != n - 1) wrong_answer();
324
325 //printf("YES\n");
326 printf("YES q = %d\n\n",q);
327
328 auto t4 = clock();
329
330 // reset console output
331 freopen("CON", "w", stdout);
332
333 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
334 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
335 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
336
337 return 0;
338 }
339 /*
340 wrslcnopzlckvxbnair_input_simurgh_lmncvpisadngpiqdfngslcnvd
341 m=124750 n=500
342 MAX_Q = 12000
343
344 YES q = 4637
345
346 t2-t1 = 0.437
347 t3-t2 = 2.274
348 t4-t3 = 0
349
350 Process returned 0 (0x0) execution time : 2.759 s
351 Press any key to continue.
352 */

4.5.3 *Rezolvare detaliat 

4.6 Ancient Books


Problema 6 - Ancient Books 100 de puncte
Author: Daniel Graf (Switzerland)

Biblioteca Naµional  a Iranului se a  în Teheran. Atracµia principal  a bibliotecii este loca-
lizat  de-a lungul unui coridor în care se a  n mese, numerotate de la 0 la n  1 de la stânga la
dreapta. Pe ecare mas  se a  câte o carte antic  scris  de mân . C rµile sunt ordonate dup  vâr-
sta lor, acest lucru îngreunându-i pe vizitatori s  caute c rµile dup  titlu. A³adar, administratorul
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 
modic rile necesare pentru rearanjarea c rµilor în ordine alfabetic : pentru ecare 0 & i $ n,
cartea care se a  î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 a , el poate lua aceast 
carte.

a Dac  are la el o carte ³i o alt  carte este pe masa la care se a , el poate interschimba cele
dou  c rµi.

a Dac  are la el o carte ³i masa la care se a  este goal , el poate pune pe mas  cartea pe care
o are la el.
CAPITOLUL 4. IOI 2017 515

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 
e 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 a  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 ae 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 a  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 nal, el se întoarce la masa 0.

Se observ  c  pe masa 0 cartea se a  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 4. IOI 2017 516

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 a³eaz  o singur  linie reprezentând valoarea returnat  de minimum_walk.
Timp maxim de executare/test: 2.0 secunde
Memorie: total 1024 MB

4.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 rst 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 nally 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 rst 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 rst search. This state space
is roughly of size N  1! N and so for N & 7 we can still nd 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 4. IOI 2017 517

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 species 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 nd: 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 rst 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 nish sorting C1 without ever spending a
non-essential step.

If the two cycles don’t 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 nishes sorting C1 and returns to S . This is optimal since the only two non-
essential steps of Mina’s 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 4. IOI 2017 518

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 rst 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 none-
¬
ssential steps across edges not in E . It is not obvious whether Mina should rst 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 dene 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 dene 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 le
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 4. IOI 2017 519

29
(l, r extend l, r), we look for two special cycles Cl and Cr . Cl is the rst S -containing cycle
that we encounter, when walking from l to the left. Similarly, Cr is the rst S -containing cycle
when walking from r to the right.
Let cl be the cost of reaching Cl from l (and dene 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 rst 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 nd determine the answer d π   combine S, S 
in linear time. The code in the le 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 t into a 32-bit integer. This causes an overow 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

4.6.2 Coduri surs 

Listing 4.6.1: books+graderpublic.cpp


1 // http://ioi2017.org/tasks/materials/books.zip ---> sandbox/books.cpp
2 // http://ioi2017.org/tasks/materials/books.zip ---> solution/***.cpp
3
4 #include <iostream>
5 #include <vector>
6 #include "books.h"
7
8 #include <cstdio>
9 #include <cassert>
10
11 #include <fstream>
29
A cycle C with interval I is S -containing if and only if S " I.
CAPITOLUL 4. IOI 2017 520

12 #include<ctime>
13
14 using namespace std;
15
16 const int MAX = 1000005;
17 int le[MAX], ri[MAX], pos[MAX];
18 int mark[MAX];
19
20 int val(int x)
21 {
22 return (x > 0 ? x : -x);
23 }
24
25 void go(int &l, int &r, int nl, int nr)
26 {
27 while (nl < l || r < nr)
28 if (nl != l)
29 {
30 l--;
31 nl = min(nl, le[l]);
32 nr = max(nr, ri[l]);
33 }
34 else
35 {
36 r++;
37 nl = min(nl, le[r]);
38 nr = max(nr, ri[r]);
39 }
40 }
41
42 int dist(int s, int t)
43 {
44 if (s == t)
45 return 0;
46 int ns = s;
47 if (t < s)
48 {
49 while (s >= ns)
50 {
51 ns = min(ns, le[s]);
52 s--;
53 if (ns <= t)
54 return 0;
55 }
56 }
57 else
58 {
59 while (s <= ns)
60 {
61 ns = max(ns, ri[s]);
62 s++;
63 if (ns >= t)
64 return 0;
65 }
66 }
67 return dist(s, t) + 2;
68 }
69
70 long long minimum_walk(vector<int> p, int s)
71 {
72 int n = p.size();
73 long long ans = 0;
74 for (int i = 0; i < n; i++)
75 ans += val(i - p[i]);
76 for (int i = 0; i < n; i++)
77 le[i] = ri[i] = i;
78 for (int i = 0; i < n; i++)
79 pos[p[i]] = i;
80 for (int i = 0; i < n; i++)
81 if (!mark[i])
82 {
83 int v = i;
84 while (mark[v] < 2)
85 {
86 le[v] = min(le[v], le[p[v]]);
87 ri[v] = max(ri[v], ri[p[v]]);
CAPITOLUL 4. IOI 2017 521

88 mark[v]++;
89 v = pos[v];
90 }
91 }
92 int gl = 0;
93 while (gl < s && p[gl] == gl)
94 gl++;
95 int gr = n - 1;
96 while (gr > s && p[gr] == gr)
97 gr--;
98 int l = s + 1, r = s;
99 go(l, r, s, s);
100 bool done = false;
101 while (gl < l || r < gr)
102 {
103 int nl = l, nr = r;
104 if (!done)
105 {
106 while (nl >= 0 && ri[nl] <= r)
107 nl--;
108 }
109 if (nl == -1)
110 done = true;
111 if (done)
112 {
113 nl = l;
114 if (l <= gl)
115 {
116 nr++;
117 ans += 2;
118 }
119 else
120 {
121 nl--;
122 ans += 2;
123 }
124 }
125 else
126 {
127 while (l <= le[nr])
128 nr++;
129 ans += min(dist(l - 1, nl), dist(r + 1, nr)) + 2;
130 }
131 go(l, r, nl, nr);
132 }
133 return ans;
134 }
135
136 // --------- grader ------------------------------------
137
138 int main()
139 {
140 auto t1 = clock();
141
142 std::freopen("../tests/5-29.in", "r", stdin);
143 std::freopen("5-29.out.txt", "w", stdout);
144
145 string str;
146 getline (cin,str);
147 //cout<<str<<"\n";
148
149 int n, s;
150 assert(2 == scanf("%d %d", &n, &s));
151
152 vector<int> p((unsigned) n);
153 for(int i = 0; i < n; i++)
154 assert(1 == scanf("%d", &p[(unsigned) i]));
155
156 auto t2 = clock();
157
158 long long res = minimum_walk(p, s);
159
160 auto t3 = clock();
161
162 printf("%lld\n", res);
163
CAPITOLUL 4. IOI 2017 522

164 auto t4 = clock();


165
166 // reset console output
167 freopen("CON", "w", stdout);
168
169 cout<<"res = "<<res<<"\n";
170
171 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
172 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
173 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
174
175 return 0;
176 }
177 /*
178 res = 3320522632
179 t2-t1 = 1.871 ... citire !!!
180 t3-t2 = 0.219
181 t4-t3 = 0
182
183 Process returned 0 (0x0) execution time : 2.137 s
184 Press any key to continue.
185 */

OBS: Merit  de încercat o citire mai rapid  din ³ier!

Listing 4.6.2: books-42759.cpp


1 // https://oj.uz/submission/42759 269 ms 169312 KB
2
3 #include "books.h"
4
5 #include<bits/stdc++.h>
6
7 using namespace std;
8
9 #define _ int v, int tl, int tr, int l, int r
10 #define tm (tl+tr >> 1)
11 #define sol v+v,tl,tm,l,r
12 #define sag v+v+1,tm+1,tr,l,r
13
14 #define mp make_pair
15 #define pb push_back
16 #define st first
17 #define nd second
18
19 const int mod = 1e9 + 7;
20 const int N = 1e6 + 6;
21
22 int H[N],T[N],A[N],B[N],n,i,j,l,r,ll,rr,t1,t2,t,a,b,lll,rrr;
23 long long x;
24
25 void relax(int &l, int &r, int &a, int &b)
26 {
27 for(;l > a || r < b;)
28 {
29 if(l > a) { l--; a = min(a,A[l]); b = max(b,B[l]); }
30 if(r < b) { r++; a = min(a,A[r]); b = max(b,B[r]); }
31 }
32 }
33
34 long long minimum_walk(vector < int > p, int s)
35 {
36 x = 0;
37 n = p.size();
38 for(i=0;i<n;i++){
39 x += abs(p[i] - i);
40 if(H[i]) continue;
41 r = i;
42 for(j=i; !H[j] ; j = p[j])
43 {
44 H[j] = 1;
45 r = max(r , j);
46 }
47 for(j=i; !T[j] ; j = p[j])
48 {
49 T[j] = 1; A[j] = i; B[j] = r;
CAPITOLUL 4. IOI 2017 523

50 }
51 }
52
53 for(i=0;i<n && p[i] == i; i++);
54 for(j=n-1;j>=0 && p[j] == j; j--);
55
56 l = r = s;
57 a = A[s];
58 b = B[s];
59
60 relax(l,r,a,b);
61
62 for(; l > i || r < j ;)
63 {
64 t1 = 0;
65 ll = lll = l; rr = rrr = r;
66 if(l > i)
67 {
68 for(a=l, b=r ; rr == r && ll > i ; )
69 {
70 ll--;
71 t1++;
72 a = min(A[ll],a);
73 b = max(B[ll],b);
74 relax(ll,rr,a,b);
75 }
76 if(r >= j) { t += t1; break; }
77 }
78
79 t2 = 0;
80 if(r < j)
81 {
82 for(a = l, b = r ; lll == l && rrr < j ; )
83 {
84 rrr++;
85 t2++;
86 a = min(A[rrr],a);
87 b = max(B[rrr],b);
88 relax(lll,rrr,a,b);
89 }
90 if(l <= i) { t += t2; break; }
91 }
92
93 if(rr == r) { if(lll != l) assert(0); t += t1+t2; break; }
94 t += min(t1,t2);
95 l = ll; r = rr;
96 if(lll != ll || rrr != rr) assert(0);
97 }
98
99 return x + t+t;
100 }
101
102
103 // --------- grader ------------------------------------
104
105 int main()
106 {
107 auto t1 = clock();
108
109 std::freopen("../tests/5-29.in", "r", stdin);
110 std::freopen("5-29.out.txt", "w", stdout);
111
112 string str;
113 getline (cin,str);
114 //cout<<str<<"\n";
115
116 int n, s;
117 assert(2 == scanf("%d %d", &n, &s));
118
119 vector<int> p((unsigned) n);
120 for(int i = 0; i < n; i++)
121 assert(1 == scanf("%d", &p[(unsigned) i]));
122
123 auto t2 = clock();
124
125 long long res = minimum_walk(p, s);
CAPITOLUL 4. IOI 2017 524

126
127 auto t3 = clock();
128
129 printf("%lld\n", res);
130
131 auto t4 = clock();
132
133 // reset console output
134 freopen("CON", "w", stdout);
135
136 cout<<"res = "<<res<<"\n";
137
138 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
139 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
140 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
141
142 return 0;
143 }
144 /*
145 res = 3320522632
146 t2-t1 = 1.703
147 t3-t2 = 0.172
148 t4-t3 = 0
149
150 Process returned 0 (0x0) execution time : 1.906 s
151 Press any key to continue.
152 */

Listing 4.6.3: books-51837.cpp


1 // https://oj.uz/submission/51837 253 ms 167404 KB
2
3 #include <bits/stdc++.h>
4 #include "books.h"
5
6 using namespace std;
7
8 const int N = 1000005;
9
10 long long minimum_walk(vector<int> p, int s)
11 {
12 int n = p.size();
13 long long res = 0;
14 for (int i = 0; i < n; ++i) res += abs(i - p[i]);
15 int ml = n - 1, mr = 0;
16 for (int i = 0; i < n; ++i)
17 {
18 if (i != p[i]) ml = min(ml, i), mr = max(mr, i);
19 }
20
21 int cl = s, cr = s;
22 queue<int> qu;
23 qu.push(s);
24
25 while (1)
26 {
27 while (qu.size())
28 {
29 int u = qu.front(); qu.pop();
30 while (cl > p[u])
31 {
32 cl--, qu.push(cl);
33 }
34
35 while (cr < p[u])
36 {
37 cr++, qu.push(cr);
38 }
39 }
40
41 int l = cl, r = cr;
42 int dl = 0, dr = 0;
43 bool fl = 0, fr = 0;
44
45 queue<int> ql, qr;
CAPITOLUL 4. IOI 2017 525

46 while (1)
47 {
48 if (l <= ml) break;
49 l--, dl++, ql.push(l);
50 while (ql.size())
51 {
52 int u = ql.front();
53 ql.pop();
54 while (l > p[u])
55 {
56 l--, ql.push(l);
57 }
58 if (p[u] > s) fl = 1;
59 }
60 if (fl) break;
61 }
62
63 while (1)
64 {
65 if (r >= mr) break;
66 r++, dr++, qr.push(r);
67 while (qr.size())
68 {
69 int u = qr.front();
70 qr.pop();
71 while (r < p[u])
72 {
73 r++, qr.push(r);
74 }
75 if (p[u] < s) fr = 1;
76 }
77 if (fr) break;
78 }
79
80 if (fl && fr)
81 {
82 res += 2 * min(dl, dr);
83 while (cl > l) qu.push(--cl);
84 while (cr < r) qu.push(++cr);
85 }
86 else
87 {
88 res += 2 * (dl + dr); break;
89 }
90 }
91 return res;
92 }
93
94 // --------- grader ------------------------------------
95
96 int main()
97 {
98 auto t1 = clock();
99
100 std::freopen("../tests/5-29.in", "r", stdin);
101 std::freopen("5-29.out.txt", "w", stdout);
102
103 string str;
104 getline (cin,str);
105 //cout<<str<<"\n";
106
107 int n, s;
108 assert(2 == scanf("%d %d", &n, &s));
109
110 vector<int> p((unsigned) n);
111 for(int i = 0; i < n; i++)
112 assert(1 == scanf("%d", &p[(unsigned) i]));
113
114 auto t2 = clock();
115
116 long long res = minimum_walk(p, s);
117
118 auto t3 = clock();
119
120 printf("%lld\n", res);
121
CAPITOLUL 4. IOI 2017 526

122 auto t4 = clock();


123
124 // reset console output
125 freopen("CON", "w", stdout);
126
127 cout<<"res = "<<res<<"\n";
128
129 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
130 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
131 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
132
133 return 0;
134 }
135 /*
136 res = 3320522632
137 t2-t1 = 1.687
138 t3-t2 = 0.391
139 t4-t3 = 0
140
141 Process returned 0 (0x0) execution time : 2.109 s
142 Press any key to continue.
143 */

Listing 4.6.4: books-94567.cpp


1 // https://oj.uz/submission/94567 147 ms 19072 KB
2
3 #include <bits/stdc++.h>
4 #include "books.h"
5
6 using namespace std;
7
8 long long minimum_walk(vector<int> p, int s)
9 {
10 int n = p.size();
11 int l = n, r = -1;
12 long long ans = 0;
13 for (int i = 0; i < n; ++i)
14 {
15 if (i != p[i])
16 {
17 ans += abs(i - p[i]);
18 l = min(l, i);
19 r = max(r, i);
20 }
21 }
22
23 int cur_l = s, cur_r = s;
24 queue<int> q;
25 q.push(s);
26 while (true)
27 {
28 while (!q.empty())
29 {
30 int x = q.front();
31 q.pop();
32 while (cur_l > p[x])
33 {
34 q.push(--cur_l);
35 }
36 while (cur_r < p[x])
37 {
38 q.push(++cur_r);
39 }
40 }
41
42 int new_l = cur_l, new_r = cur_r;
43 int cost_l = 0, cost_r = 0;
44 bool found_l = false, found_r = false;
45
46 while (new_l > l && !found_l)
47 {
48 q.push(--new_l);
49 ++cost_l;
50 while (!q.empty())
CAPITOLUL 4. IOI 2017 527

51 {
52 int x = q.front();
53 q.pop();
54 if (p[x] > s)
55 {
56 found_l = true;
57 }
58
59 while (new_l > p[x])
60 {
61 q.push(--new_l);
62 }
63 }
64 }
65
66 while (new_r < r && !found_r)
67 {
68 q.push(++new_r);
69 ++cost_r;
70 while (!q.empty())
71 {
72 int x = q.front();
73 q.pop();
74 if (p[x] < s)
75 {
76 found_r = true;
77 }
78
79 while (new_r < p[x])
80 {
81 q.push(++new_r);
82 }
83 }
84 }
85
86 if (found_l && found_r)
87 {
88 ans += min(cost_l, cost_r) << 1;
89 while (cur_l > new_l)
90 {
91 q.push(--cur_l);
92 }
93
94 while (cur_r < new_r)
95 {
96 q.push(++cur_r);
97 }
98 }
99 else
100 {
101 ans += cost_l + cost_r << 1;
102 break;
103 }
104 }
105
106 return ans;
107 }
108
109 // --------- grader ------------------------------------
110
111 int main()
112 {
113 auto t1 = clock();
114
115 std::freopen("../tests/5-29.in", "r", stdin);
116 std::freopen("5-29.out.txt", "w", stdout);
117
118 string str;
119 getline (cin,str);
120 //cout<<str<<"\n";
121
122 int n, s;
123 assert(2 == scanf("%d %d", &n, &s));
124
125 vector<int> p((unsigned) n);
126 for(int i = 0; i < n; i++)
CAPITOLUL 4. IOI 2017 528

127 assert(1 == scanf("%d", &p[(unsigned) i]));


128
129 auto t2 = clock();
130
131 long long res = minimum_walk(p, s);
132
133 auto t3 = clock();
134
135 printf("%lld\n", res);
136
137 auto t4 = clock();
138
139 // reset console output
140 freopen("CON", "w", stdout);
141
142 cout<<"res = "<<res<<"\n";
143
144 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
145 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
146 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
147
148 return 0;
149 }
150 /*
151 res = 3320522632
152 t2-t1 = 1.703
153 t3-t2 = 0.328
154 t4-t3 = 0
155
156 Process returned 0 (0x0) execution time : 2.062 s
157 Press any key to continue.
158 */

Listing 4.6.5: books-122101.cpp


1 // https://oj.uz/submission/122101 145 ms 8264 KB
2
3 #include <bits/stdc++.h>
4
5 #pragma optimization("unroll-loops")
6 #pragma optimization("Ofast")
7 #pragma target("avx2")
8
9 using namespace std;
10 long long res;
11
12 void push(int& l, int& r, int& mn, int& mx, vector<int>& p)
13 {
14 while(l != mn || r != mx)
15 {
16 if(l != mn)
17 {
18 l--;
19 mn = min(mn, p[l]);
20 mx = max(mx, p[l]);
21 }
22 else
23 {
24 r++;
25 mn = min(mn, p[r]);
26 mx = max(mx, p[r]);
27 }
28 }
29 }
30
31 long long minimum_walk(vector<int> p, int s)
32 {
33 int L = 0, R = p.size() - 1;
34 while(R >= 0 && p[R] == R) R--;
35 if(R == -1) return 0;
36 while(p[L] == L) L++;
37
38 if(s > R)
39 {
40 res += (s - R) << 1;
CAPITOLUL 4. IOI 2017 529

41 s = R;
42 }
43
44 if(s < L)
45 {
46 res += (L - s) << 1;
47 s = L;
48 }
49
50 int tmn = s, tmx = s;
51 for(int i = L; i <= R; i++)
52 {
53 res += abs(p[i] - i);
54 if(1LL * (tmx - p[i]) * (tmx - i) <= 0 ||
55 1LL * (tmn - p[i]) * (tmn - i) <= 0)
56 {
57 tmx = max(tmx, max(i, p[i]));
58 tmn = min(tmn, min(i, p[i]));
59 }
60 }
61
62 int l, r, mn = min(s, p[s]), mx = max(s, p[s]);
63 l = r = s;
64 push(l, r, tmn, tmx, p);
65
66 l = r = s;
67 push(l, r, mn, mx, p);
68
69 while(mn != tmn || mx != tmx)
70 {
71 res += 2;
72 if(mn != tmn)mn--;
73 if(mx != tmx)mx++;
74 push(l, r, mn, mx, p);
75 }
76
77 for(; l >= L; l--)
78 {
79 if(mn > l) res += 2;
80 mn = min(mn, p[l]);
81 }
82
83 for(; r <= R; r++)
84 {
85 if(mx < r) res += 2;
86 mx = max(mx, p[r]);
87 }
88 return res;
89 }
90
91 // --------- grader ------------------------------------
92
93 int main()
94 {
95 auto t1 = clock();
96
97 std::freopen("../tests/5-29.in", "r", stdin);
98 std::freopen("5-29.out.txt", "w", stdout);
99
100 string str;
101 getline (cin,str);
102 //cout<<str<<"\n";
103
104 int n, s;
105 assert(2 == scanf("%d %d", &n, &s));
106
107 vector<int> p((unsigned) n);
108 for(int i = 0; i < n; i++)
109 assert(1 == scanf("%d", &p[(unsigned) i]));
110
111 auto t2 = clock();
112
113 long long res = minimum_walk(p, s);
114
115 auto t3 = clock();
116
CAPITOLUL 4. IOI 2017 530

117 printf("%lld\n", res);


118
119 auto t4 = clock();
120
121 // reset console output
122 freopen("CON", "w", stdout);
123
124 cout<<"res = "<<res<<"\n";
125
126 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
127 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
128 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
129
130 return 0;
131 }
132 /*
133 res = 3320522632
134 t2-t1 = 1.703
135 t3-t2 = 0.125
136 t4-t3 = 0
137
138 Process returned 0 (0x0) execution time : 1.859 s
139 Press any key to continue.
140 */

Listing 4.6.6: books-138862.cpp


1 // https://oj.uz/submission/138862 234 ms 21568 KB
2
3 #include <bits/stdc++.h>
4 #include "books.h"
5
6 //#define long long long
7
8 using namespace std;
9
10 const int N = 1e6+5;
11
12 long long ans = 0;
13 int n, x, y;
14 int id[N], L[N], R[N], ptr;
15 bool check[N];
16
17 void extend(int &l, int &r)
18 {
19 int ll = min(L[id[l]], L[id[r]]);
20 int rr = max(R[id[l]], R[id[r]]);
21 while(ll < l || r < rr)
22 {
23 if(ll < l) l--;
24 else r++;
25 ll = min(ll, min(L[id[l]], L[id[r]]));
26 rr = max(rr, max(R[id[l]], R[id[r]]));
27 }
28 }
29
30 long long minimum_walk(vector<int> p, int s)
31 {
32 n = p.size();
33 x = y = s;
34 for(int i = 0; i < n; ++i) ans += abs(i - p[i]);
35 for(int i = 0; i < n; ++i) if(!check[i])
36 {
37 id[i] = ++ptr, check[i] = true;
38 L[ptr] = R[ptr] = i;
39 int u = i;
40 while(p[u] != i)
41 {
42 id[u = p[u]] = ptr;
43 check[u] = true;
44 R[ptr] = max(R[ptr], u);
45 }
46 if(i != p[i])
47 x = min(x, L[ptr]), y = max(y, R[ptr]);
48 }
CAPITOLUL 4. IOI 2017 531

49
50 int l = s, r = s;
51 while(x < l || r < y)
52 {
53 extend(l, r);
54 int al = l, ar = r;
55 int bl = l, br = r;
56 long long ca = 0, cb = 0;
57
58 while(x < al && ar == r) ca += 2, extend(--al, ar);
59
60 while(bl == l && br < y) cb += 2, extend(bl, ++br);
61
62 if(ar != r && bl != l) ans += min(ca, cb);
63 else ans += ca + cb;
64
65 l = min(al, bl), r = max(ar, br);
66 }
67 return ans;
68 }
69
70 // --------- grader ------------------------------------
71
72 int main()
73 {
74 auto t1 = clock();
75
76 std::freopen("../tests/5-29.in", "r", stdin);
77 std::freopen("5-29.out.txt", "w", stdout);
78
79 string str;
80 getline (cin,str);
81 //cout<<str<<"\n";
82
83 int n, s;
84 assert(2 == scanf("%d %d", &n, &s));
85
86 vector<int> p((unsigned) n);
87 for(int i = 0; i < n; i++)
88 assert(1 == scanf("%d", &p[(unsigned) i]));
89
90 auto t2 = clock();
91
92 long long res = minimum_walk(p, s);
93
94 auto t3 = clock();
95
96 printf("%lld\n", res);
97
98 auto t4 = clock();
99
100 // reset console output
101 freopen("CON", "w", stdout);
102
103 cout<<"res = "<<res<<"\n";
104
105 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
106 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
107 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
108
109 return 0;
110 }
111 /*
112 res = 3320522632
113 t2-t1 = 1.953
114 t3-t2 = 0.172
115 t4-t3 = 0
116
117 Process returned 0 (0x0) execution time : 2.156 s
118 Press any key to continue.
119 */

Listing 4.6.7: books-152524.cpp


1 // https://oj.uz/submission/152524 171 ms 15048 KB
CAPITOLUL 4. IOI 2017 532

2
3 #include <bits/stdc++.h>
4 #include "books.h"
5
6 using namespace std;
7
8 long long minimum_walk(vector<int> p, int s)
9 {
10 int i = s;
11 int j = s-1;//incl-incl
12 int mi = s, ma=s;
13 int n = p.size();
14 long long cnt = 0, curl = 0, curr = 0;
15 int b = 0, e = n;
16 for(int k = 0; k<s&&p[k]==k; k++) b = k+1;
17 for(int k = n-1; k>s&&p[k]==k; k--) e = k;
18
19
20 while(i>b||j<e-1)
21 {
22 bool bol = false;
23 while(mi < i)
24 {
25 bol = true;
26 i--;
27 if(p[i]>s)
28 {
29 cnt += curl;
30 curl = 0;
31 curr = 0;
32 }
33 mi = min(mi, p[i]);
34 ma = max(ma, p[i]);
35 }
36
37 while(j<ma)
38 {
39 bol = true;
40 j++;
41 if(p[j]<s)
42 {
43 cnt+=curr;
44 curl = 0;
45 curr = 0;
46 }
47 mi = min(mi, p[j]);
48 ma = max(ma, p[j]);
49
50 }
51
52 if(!bol)
53 {
54 if(mi>b){mi--;curl+=2;}
55 if(ma<e-1){ma++;curr+=2;}
56 }
57
58 }
59
60 cnt+=curl+curr;
61 for(int ii = 0; ii<n; ii++)
62 {
63 cnt += abs(ii - p[ii]);
64 }
65
66 return cnt;
67 }
68
69 // --------- grader ------------------------------------
70
71 int main()
72 {
73 auto t1 = clock();
74
75 std::freopen("../tests/5-29.in", "r", stdin);
76 std::freopen("5-29.out.txt", "w", stdout);
77
CAPITOLUL 4. IOI 2017 533

78 string str;
79 getline (cin,str);
80 //cout<<str<<"\n";
81
82 int n, s;
83 assert(2 == scanf("%d %d", &n, &s));
84
85 vector<int> p((unsigned) n);
86 for(int i = 0; i < n; i++)
87 assert(1 == scanf("%d", &p[(unsigned) i]));
88
89 auto t2 = clock();
90
91 long long res = minimum_walk(p, s);
92
93 auto t3 = clock();
94
95 printf("%lld\n", res);
96
97 auto t4 = clock();
98
99 // reset console output
100 freopen("CON", "w", stdout);
101
102 cout<<"res = "<<res<<"\n";
103
104 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
105 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
106 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
107
108 return 0;
109 }
110 /*
111 res = 3320522632
112 t2-t1 = 1.843
113 t3-t2 = 0.047
114 t4-t3 = 0
115
116 Process returned 0 (0x0) execution time : 1.938 s
117 Press any key to continue.
118 */

Listing 4.6.8: books-206638.cpp


1 // https://oj.uz/submission/206638 262 ms 31740 KB
2
3 #include <cassert>
4 #include<ctime>
5
6 #include "books.h"
7 #include<iostream>
8
9 #define MAXN 1000005
10 #define LL long long
11
12 using namespace std;
13
14 int N, S, cyc = 1, eL, eR, Lcur, Rcur;
15 int L[MAXN], R[MAXN], P[MAXN], C[MAXN];
16 bool vis[MAXN];
17 LL ans;
18
19 LL minimum_walk(vector<int> p, int s)
20 {
21 N = p.size(), eL = N+1, S = s+1, Lcur = S, Rcur = S;
22 for (int i=0; i<N; i++)
23 {
24 P[i+1]=p[i]+1; //1 index
25 ans += (LL) max(p[i] - i, i - p[i]);
26 }
27
28 for (int i=1; i<=N; i++)
29 {
30 int cur = P[i];
31 L[cyc] = 1<<30, R[cyc] = -1<<30;
CAPITOLUL 4. IOI 2017 534

32 while (!vis[cur])
33 {
34 L[cyc] = min(L[cyc], cur);
35 R[cyc] = max(R[cyc], cur);
36 C[cur] = cyc;
37 vis[cur] = true;
38 cur = P[cur];
39 }
40 cyc++;
41 }
42
43 for (int i=1; i<=N; i++)
44 {
45 if (P[i] != i)
46 {
47 eL = i;
48 break;
49 }
50 }
51
52 for (int i=N; i>=1; i--)
53 {
54 if (P[i] != i)
55 {
56 eR = i;
57 break;
58 }
59 }
60
61 while (Lcur > eL || Rcur < eR)
62 {
63 //expand left and right as much as frontier allows
64 int pt1 = Lcur, pt2 = Rcur; //[Lcur, Rcur] can be reached
65 while (pt1 >= Lcur || pt2 <= Rcur)
66 {
67 if (pt1 >= Lcur)
68 {
69 Lcur = min(Lcur, L[C[pt1]]);
70 Rcur = max(Rcur, R[C[pt1]]);
71 pt1--;
72 }
73 if (pt2 <= Rcur)
74 {
75 Lcur = min(Lcur, L[C[pt2]]);
76 Rcur = max(Rcur, R[C[pt2]]);
77 pt2++;
78 }
79 }
80
81 //if one boundary is reached
82 if (Lcur <= eL && Rcur >= eR)
83 {
84 break;
85 }
86 else if (Lcur <= eL)
87 {
88 ans+=2;
89 Rcur++;
90 }
91 else if (Rcur >= eR)
92 {
93 ans+=2;
94 Lcur--;
95 }
96 else
97 {
98 //test expand right Cost to go beyond
99 int tmpL = Lcur, tmpR = Rcur, ptL = Lcur, ptR = Rcur;
100 int Lcost = 0, Rcost = 0;
101 bool root = true;
102
103 //check R cost:
104 while (ptR <= eR)
105 {
106 if (ptR > tmpR) {tmpR = ptR, Rcost++;}
107 tmpR = max(tmpR, R[C[ptR]]);
CAPITOLUL 4. IOI 2017 535

108 if (L[C[ptR]] < Lcur)


109 {
110 root = false;
111 break;
112 }
113 ptR++;
114 }
115
116 //check L cost:
117 while (ptL >= eL)
118 {
119 if (ptL < tmpL) {tmpL = ptL, Lcost++;}
120 tmpL = min(tmpL, L[C[ptL]]);
121 if (R[C[ptL]] > Rcur)
122 {
123 root = false;
124 break;
125 }
126 ptL--;
127 }
128
129 if (root)
130 {
131 ans+=2*(Lcost + Rcost);
132 break;
133 }
134 else if (Lcost > Rcost)
135 {
136 Rcur = ptR, Lcur = L[C[ptR]];
137 ans+=2*Rcost;
138 }
139 else
140 {
141 Lcur = ptL, Rcur = R[C[ptL]];
142 ans+=2*Lcost;
143 }
144 }
145 }
146
147 return(ans);
148 }
149
150 // --------- grader ------------------------------------
151
152 int main()
153 {
154 auto t1 = clock();
155
156 std::freopen("../tests/5-29.in", "r", stdin);
157 std::freopen("5-29.out.txt", "w", stdout);
158
159 string str;
160 getline (cin,str);
161 //cout<<str<<"\n";
162
163 int n, s;
164 assert(2 == scanf("%d %d", &n, &s));
165
166 vector<int> p((unsigned) n);
167 for(int i = 0; i < n; i++)
168 assert(1 == scanf("%d", &p[(unsigned) i]));
169
170 auto t2 = clock();
171
172 long long res = minimum_walk(p, s);
173
174 auto t3 = clock();
175
176 printf("%lld\n", res);
177
178 auto t4 = clock();
179
180 // reset console output
181 freopen("CON", "w", stdout);
182
183 cout<<"res = "<<res<<"\n";
CAPITOLUL 4. IOI 2017 536

184
185 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
186 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
187 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
188
189 return 0;
190 }
191 /*
192 res = 3320522632
193 t2-t1 = 1.781
194 t3-t2 = 0.125
195 t4-t3 = 0
196
197 Process returned 0 (0x0) execution time : 1.938 s
198 Press any key to continue.
199 */

4.6.3 *Rezolvare detaliat 


Capitolul 5

IOI 201630

5.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  specicului 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 e g se³te un subset de molecule cu greutate total  în intervalul de
detectare, e 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),


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

537
CAPITOLUL 5. IOI 2016 538

` 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 ³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

5.1.1 Indicaµii de rezolvare


Author: Shi-Chun Tsai
CAPITOLUL 5. IOI 2016 539

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.
If you implement greedy in O n  you'll pass only Subtask 5.
2

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 x 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 prexes
l..r  if minSumk 
and maxSum[] is partial maximums on suxes. 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 nd it in O n.
Proof. Lets x 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 prex and sux.
Use two pointers to nd it in O n.
Proof. Lets x k - number of molecules in the answer. The smallest k molecules form prex,
the biggest k form sux. Lets change the set smoothly from "prex" to "sux". One step: make
prex shorter by one, make sux longer by k . q.e.d.

5.1.2 Coduri surs 

Listing 5.1.1: molecules_sk.cpp


1 /** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */
2
3 #include <cstdio>
4 #include <algorithm>
5 #include <vector>
6
7 #include<ctime>
8 #include<iostream>
9
10 using namespace std;
11
12 #define forn(i, n) for (int i = 0; i < (int)(n); i++)
13
14 typedef pair <int, int> pii;
15 typedef long long ll;
16
17 // return value: result if solution exists and empty vector otherwise
18 vector<int> find_subset( int l, int u, vector<int> w )
19 {
20 int n = w.size();
21 vector<pii> wp(n);
CAPITOLUL 5. IOI 2016 540

22 forn(i, n)
23 wp[i] = pii(w[i], i);
24 sort(wp.begin(), wp.end());
25 vector<ll> mi(n + 1), ma(n + 1);
26
27 forn(i, n)
28 {
29 mi[i + 1] = mi[i] + wp[i].first;
30 ma[i + 1] = ma[i] + wp[n - i - 1].first;
31 }
32
33 forn(i, n + 1)
34 if (mi[i] <= u && ma[i] >= l)
35 {
36 int pos = n - 1;
37 ll sum = mi[i];
38 vector<int> result(i);
39 forn(j, i)
40 {
41 while (pos >= i && sum + wp[pos].first - wp[j].first > u)
42 pos--;
43 if (pos >= i && sum + wp[pos].first - wp[j].first <= u)
44 sum += wp[pos].first - wp[j].first, result[j]
45 = wp[pos--].second;
46 else
47 result[j] = wp[j].second;
48 }
49 return result;
50 }
51
52 return vector<int>();
53 }
54
55 // BEGIN CUT
56 int main()
57 {
58 auto t1 = clock();
59
60 std::freopen("../tests/subtask_6/108", "r", stdin);
61 std::freopen("6-108.out.txt", "w", stdout);
62
63 int n, l, u;
64 scanf("%d %d %d", &n, &l, &u);
65
66 std::vector<int> w(n);
67 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
68
69 auto t2 = clock();
70
71 std::vector<int> result = find_subset(l, u, w);
72
73 auto t3 = clock();
74
75 // BEGIN SECRET
76 puts("14e047d7a2907b9034950b074822b302");
77 // END SECRET
78
79 printf("%d\n", (int)result.size());
80 for (int x : result) printf("%d ", x);
81 printf("\n");
82
83 auto t4 = clock();
84
85 // reset console output
86 freopen("CON", "w", stdout);
87
88 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
89
90 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
91 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
92 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
93
94 return 0;
95 }
96 // END CUT
97 /*
CAPITOLUL 5. IOI 2016 541

98 result.size() = 1000
99
100 t2-t1 = 0.39
101 t3-t2 = 0.297
102 t4-t3 = 0
103
104 Process returned 0 (0x0) execution time : 0.734 s
105 Press any key to continue.
106 */

Listing 5.1.2: molecules_sk_greedy_1_n.cpp


1 /** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */
2 // idea : answer is a segment of sorted weights
3 // time = O(n)
4
5 #include <cstdio>
6 #include <algorithm>
7 #include <vector>
8
9 #include <fstream>
10 #include<iostream>
11 #include<ctime>
12
13 using namespace std;
14
15 #define forn(i, n) for (int i = 0; i < (int)(n); i++)
16
17 typedef pair <int, int> pii;
18 typedef long long ll;
19
20 // return value: result if solution exists and empty vector otherwise
21 vector<int> find_subset( int L, int U, vector<int> w )
22 {
23 int n = w.size();
24 vector<pii> wp(n);
25 forn(i, n)
26 wp[i] = pii(w[i], i);
27 sort(wp.begin(), wp.end());
28 int r = 0;
29 ll sum = 0;
30 forn(l, n)
31 {
32 while (r < n && sum < L)
33 sum += wp[r++].first;
34 if (L <= sum && sum <= U)
35 {
36 vector<int> res;
37 for (; l < r; l++)
38 res.push_back(wp[l].second);
39 return res;
40 }
41 sum -= wp[l].first;
42 }
43 return vector<int>();
44 }
45
46 // BEGIN CUT
47 int main()
48 {
49 auto t1 = clock();
50
51 std::freopen("../tests/subtask_6/108", "r", stdin);
52 std::freopen("6-108.out.txt", "w", stdout);
53
54 int n, l, u;
55 scanf("%d %d %d", &n, &l, &u);
56
57 std::vector<int> w(n);
58 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
59
60 auto t2 = clock();
61
62 std::vector<int> result = find_subset(l, u, w);
63
CAPITOLUL 5. IOI 2016 542

64 auto t3 = clock();
65
66 // BEGIN SECRET
67 puts("14e047d7a2907b9034950b074822b302");
68 // END SECRET
69
70 printf("%d\n", (int)result.size());
71 for (int x : result) printf("%d ", x);
72 printf("\n");
73
74 auto t4 = clock();
75
76 // reset console output
77 freopen("CON", "w", stdout);
78
79 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
80
81 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
82 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
83 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
84
85 return 0;
86 }
87 // END CUT
88 /*
89 result.size() = 1000
90
91 t2-t1 = 0.359
92 t3-t2 = 0.281
93 t4-t3 = 0
94
95 Process returned 0 (0x0) execution time : 0.672 s
96 Press any key to continue.
97 */

Listing 5.1.3: molecules_sk_greedy_2_n.cpp


1 /** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */
2 // idea : answer is a prefix + a suffix of sorted weights
3 // time = O(n)
4
5 #include <cstdio>
6 #include <algorithm>
7 #include <vector>
8
9 #include <fstream>
10 #include<iostream>
11 #include<ctime>
12
13 using namespace std;
14
15 #define forn(i, n) for (int i = 0; i < (int)(n); i++)
16
17 typedef pair <int, int> pii;
18 typedef long long ll;
19
20 // return value: result if solution exists and empty vector otherwise
21 vector<int> find_subset( int L, int U, vector<int> w )
22 {
23 int n = w.size();
24 vector<pii> wp(n);
25 ll sum = 0;
26 forn(i, n)
27 wp[i] = pii(w[i], i), sum += w[i];
28 sort(wp.begin(), wp.end());
29
30 int r = n;
31 for (int l = n; l >= 0; l--)
32 {
33 while (r > l && sum < L)
34 sum += wp[--r].first;
35 if (L <= sum && sum <= U)
36 {
37 vector<int> res;
38 while (l--)
CAPITOLUL 5. IOI 2016 543

39 res.push_back(wp[l].second);
40 while (r < n)
41 res.push_back(wp[r++].second);
42 return res;
43 }
44 if (l)
45 sum -= wp[l - 1].first;
46 }
47 return vector<int>();
48 }
49
50 // BEGIN CUT
51 int main()
52 {
53 auto t1 = clock();
54
55 std::freopen("../tests/subtask_6/108", "r", stdin);
56 std::freopen("6-108.out.txt", "w", stdout);
57
58 int n, l, u;
59 scanf("%d %d %d", &n, &l, &u);
60
61 std::vector<int> w(n);
62 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
63
64 auto t2 = clock();
65
66 std::vector<int> result = find_subset(l, u, w);
67
68 auto t3 = clock();
69
70 // BEGIN SECRET
71 puts("14e047d7a2907b9034950b074822b302");
72 // END SECRET
73
74 printf("%d\n", (int)result.size());
75 for (int x : result) printf("%d ", x);
76 printf("\n");
77
78 auto t4 = clock();
79
80 // reset console output
81 freopen("CON", "w", stdout);
82
83 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
84
85 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
86 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
87 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
88
89 return 0;
90 }
91 // END CUT
92 /*
93 result.size() = 1000
94
95 t2-t1 = 0.375
96 t3-t2 = 0.281
97 t4-t3 = 0
98
99 Process returned 0 (0x0) execution time : 0.688 s
100 Press any key to continue.
101 */

Listing 5.1.4: molecules_sk_greedy_3_ok.cpp


1 /** Author: Sergey Kopeliovich (Burunduk30@gmail.com) */
2 // idea : answer is a segment of sorted weights of length in floor(k0)..ceil(k0)
3 // time = O(n)
4
5 #include <cstdio>
6 #include <cmath>
7 #include <algorithm>
8 #include <vector>
9 #include <numeric> // acumulate
CAPITOLUL 5. IOI 2016 544

10
11 #include <fstream>
12 #include<iostream>
13 #include<ctime>
14
15 using namespace std;
16
17 #define forn(i, n) for (int i = 0; i < (int)(n); i++)
18 #define forab(i, a, b) for (int i = (a); i <= (b); i++)
19
20 typedef pair <int, int> pii;
21 typedef long long ll;
22
23 // return value: result if solution exists and empty vector otherwise
24 vector<int> find_subset( int L, int U, vector<int> w )
25 {
26 int n = w.size();
27 vector<pii> wp(n);
28 forn(i, n)
29 wp[i] = pii(w[i], i);
30
31 sort(wp.begin(), wp.end());
32
33 double k0 = L / (accumulate(w.begin(), w.end(), 0.0) / n);
34
35 vector<ll> pref(n + 1);
36 forn(i, n)
37 pref[i + 1] = pref[i] + wp[i].first;
38 forab(k, (int)floor(k0), (int)ceil(k0))
39 if (1 <= k && k <= n)
40 forn(l, n - k + 1)
41 {
42 ll sum = pref[l + k] - pref[l];
43 if (L <= sum && sum <= U)
44 {
45 vector<int> res;
46 forn(i, k)
47 res.push_back(wp[l + i].second);
48 return res;
49 }
50 }
51 return vector<int>();
52 }
53
54 // BEGIN CUT
55 int main()
56 {
57 auto t1 = clock();
58
59 std::freopen("../tests/subtask_6/108", "r", stdin);
60 std::freopen("6-108.out.txt", "w", stdout);
61
62 int n, l, u;
63 scanf("%d %d %d", &n, &l, &u);
64
65 std::vector<int> w(n);
66 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
67
68 auto t2 = clock();
69
70 std::vector<int> result = find_subset(l, u, w);
71
72 auto t3 = clock();
73
74 // BEGIN SECRET
75 puts("14e047d7a2907b9034950b074822b302");
76 // END SECRET
77
78 printf("%d\n", (int)result.size());
79 for (int x : result) printf("%d ", x);
80 printf("\n");
81
82 auto t4 = clock();
83
84 // reset console output
85 freopen("CON", "w", stdout);
CAPITOLUL 5. IOI 2016 545

86
87 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
88
89 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
90 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
91 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
92
93 return 0;
94 }
95 // END CUT
96 /*
97 result.size() = 1000
98
99 t2-t1 = 0.359
100 t3-t2 = 0.297
101 t4-t3 = 0
102
103 Process returned 0 (0x0) execution time : 0.688 s
104 Press any key to continue.
105 */

Listing 5.1.5: molecules-20751.cpp


1 // https://oj.uz/submission/20751
2
3 #include "molecules.h"
4 #include <stdio.h>
5 #include <algorithm>
6
7 #include <fstream>
8 #include<iostream>
9 #include<ctime>
10
11 using namespace std;
12
13 struct point
14 {
15 int a, num;
16 bool operator<(const point &p)const
17 {
18 return a < p.a;
19 }
20 } P[201000];
21
22 std::vector<int> find_subset(int l, int u, std::vector<int> w)
23 {
24 int i, n = w.size(), j, k;
25 long long S1 = 0, S2 = 0;
26 for(i=0;i<n;i++)P[i].num=i,P[i].a=w[i];
27
28 sort(P,P+n);
29
30 vector<int>res;
31 for(i=0;i<n;i++)
32 {
33 S1 += P[i].a;
34 S2 += P[n-i-1].a;
35 if(S1 <= u && S2 >= l)
36 {
37 if(S1 >= l)
38 {
39 for(j=0;j<=i;j++)res.push_back(P[j].num);
40 return res;
41 }
42
43 for(j=i+1;j<n;j++)
44 {
45 S1 += P[j].a;
46 S1 -= P[j-i-1].a;
47 if(S1 >= l)
48 {
49 for(k=j-i;k<=j;k++)res.push_back(P[k].num);
50 return res;
51 }
52 }
CAPITOLUL 5. IOI 2016 546

53 }
54 }
55
56 return res;
57 }
58
59 // BEGIN CUT
60 int main()
61 {
62 auto t1 = clock();
63
64 std::freopen("../tests/subtask_6/108", "r", stdin);
65 std::freopen("6-108.out.txt", "w", stdout);
66
67 int n, l, u;
68 scanf("%d %d %d", &n, &l, &u);
69
70 std::vector<int> w(n);
71 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
72
73 auto t2 = clock();
74
75 std::vector<int> result = find_subset(l, u, w);
76
77 auto t3 = clock();
78
79 // BEGIN SECRET
80 puts("14e047d7a2907b9034950b074822b302");
81 // END SECRET
82
83 printf("%d\n", (int)result.size());
84 for (int x : result) printf("%d ", x);
85 printf("\n");
86
87 auto t4 = clock();
88
89 // reset console output
90 freopen("CON", "w", stdout);
91
92 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
93
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 return 0;
99 }
100 // END CUT
101 /*
102 result.size() = 1000
103
104 t2-t1 = 0.359
105 t3-t2 = 0.109
106 t4-t3 = 0
107
108 Process returned 0 (0x0) execution time : 0.516 s
109 Press any key to continue.
110 */

Listing 5.1.6: molecules-72936.cpp


1 // https://oj.uz/submission/72936
2
3 #include <algorithm>
4 #include "molecules.h"
5
6 #include <fstream>
7 #include<iostream>
8 #include<ctime>
9
10 using namespace std;
11
12 typedef long long int ll;
13
14 std::vector<int> find_subset(int l, int u, std::vector<int> w)
CAPITOLUL 5. IOI 2016 547

15 {
16 vector<int> V;
17 ll N=w.size();
18 ll S=0;
19 vector<pair<ll,int>> W;
20
21 for(ll i=0;i<N;i++)
22 W.push_back({w[i],i});
23
24 sort(W.begin(), W.end());
25
26 for(ll a=0,b=0;a<N;a++)
27 {
28 S-=a?W[a-1].first:0;
29 while(l>S and b<N)
30 S+=W[b++].first;
31 if(l<=S and S<=u)
32 {
33 for(ll i=a;i<b;i++)
34 V.push_back(W[i].second);
35 return V;
36 }
37 }
38
39 return V;
40 }
41
42 // BEGIN CUT
43 int main()
44 {
45 auto t1 = clock();
46
47 std::freopen("../tests/subtask_6/108", "r", stdin);
48 std::freopen("6-108.out.txt", "w", stdout);
49
50 int n, l, u;
51 scanf("%d %d %d", &n, &l, &u);
52
53 std::vector<int> w(n);
54 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
55
56 auto t2 = clock();
57
58 std::vector<int> result = find_subset(l, u, w);
59
60 auto t3 = clock();
61
62 // BEGIN SECRET
63 puts("14e047d7a2907b9034950b074822b302");
64 // END SECRET
65
66 printf("%d\n", (int)result.size());
67 for (int x : result) printf("%d ", x);
68 printf("\n");
69
70 auto t4 = clock();
71
72 // reset console output
73 freopen("CON", "w", stdout);
74
75 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
76
77 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
78 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
79 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
80
81 return 0;
82 }
83 // END CUT
84 /*
85 result.size() = 1000
86
87 t2-t1 = 0.343
88 t3-t2 = 0.313
89 t4-t3 = 0
90
CAPITOLUL 5. IOI 2016 548

91 Process returned 0 (0x0) execution time : 0.719 s


92 Press any key to continue.
93 */

Listing 5.1.7: molecules-93472.cpp


1 // https://oj.uz/submission/93472 52 ms 7164 KB
2
3 #include "molecules.h"
4 #include <bits/stdc++.h>
5
6 #include <fstream>
7 #include<iostream>
8 #include<ctime>
9
10 using namespace std;
11 using ll = long long;
12 using pii = pair<ll,int>;
13
14 vector<int> res;
15 vector<pii> v;
16
17 vector<int> find_subset(int lw, int u, vector<int> w)
18 {
19 v.reserve(w.size());
20
21 for(int i=0;i<w.size();i++)
22 {
23 v.emplace_back(w[i],i);
24 }
25
26 sort(v.begin(),v.end());
27
28 int l = 0;
29 ll sum = 0;
30 for(int r = 0;r < v.size();r++)
31 {
32 sum += v[r].first;
33 while(sum > u)
34 {
35 sum -= v[l].first;
36 l++;
37 }
38 //cout<<l<<" - "<<r<<": "<<sum<<endl;
39 if(sum >= lw)
40 {
41 for(int i = l;i<=r;i++) res.push_back(v[i].second);
42 return res;
43 }
44 }
45
46 return vector<int>();
47 }
48
49 // BEGIN CUT
50 int main()
51 {
52 auto t1 = clock();
53
54 std::freopen("../tests/subtask_6/108", "r", stdin);
55 std::freopen("6-108.out.txt", "w", stdout);
56
57 int n, l, u;
58 scanf("%d %d %d", &n, &l, &u);
59
60 std::vector<int> w(n);
61 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
62
63 auto t2 = clock();
64
65 std::vector<int> result = find_subset(l, u, w);
66
67 auto t3 = clock();
68
69 // BEGIN SECRET
CAPITOLUL 5. IOI 2016 549

70 puts("14e047d7a2907b9034950b074822b302");
71 // END SECRET
72
73 printf("%d\n", (int)result.size());
74 for (int x : result) printf("%d ", x);
75 printf("\n");
76
77 auto t4 = clock();
78
79 // reset console output
80 freopen("CON", "w", stdout);
81
82 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
83
84 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
85 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
86 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
87
88 return 0;
89 }
90 // END CUT
91 /*
92 result.size() = 1000
93
94 t2-t1 = 0.359
95 t3-t2 = 0.312
96 t4-t3 = 0
97
98 Process returned 0 (0x0) execution time : 0.703 s
99 Press any key to continue.
100 */

Listing 5.1.8: molecules-114550.cpp


1 // https://oj.uz/submission/114550
2
3 #include "molecules.h"
4 #include <bits/stdc++.h>
5
6 #include <fstream>
7 #include<iostream>
8 #include<ctime>
9
10 using namespace std;
11 typedef long long ll;
12
13 const int MAXN=2e5+5;
14 pair<ll, int> v[MAXN];
15 vector<int> resp;
16 ll L, R;
17 int n;
18
19 vector<int> find_subset(int u, int l, vector<int> w)
20 {
21 L=u; R=l;
22 int n=w.size();
23 for(int i=1; i<=n; i++) v[i]={w[i-1], i-1};
24 sort(v+1, v+1+n);
25
26 int ind=1;
27 ll soma=0;
28 for(int i=1; i<=n; i++)
29 {
30 soma+=v[i].first;
31 while(soma>R&&ind<i) soma-=v[ind++].first;
32 if(L<=soma&&soma<=R)
33 {
34 for(int j=ind; j<=i; j++)
35 resp.push_back(v[j].second);
36 break;
37 }
38 }
39
40 if(resp.size()) sort(resp.begin(), resp.end());
41 return resp;
CAPITOLUL 5. IOI 2016 550

42 }
43
44 // BEGIN CUT
45 int main()
46 {
47 auto t1 = clock();
48
49 std::freopen("../tests/subtask_6/108", "r", stdin);
50 std::freopen("6-108.out.txt", "w", stdout);
51
52 int n, l, u;
53 scanf("%d %d %d", &n, &l, &u);
54
55 std::vector<int> w(n);
56 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
57
58 auto t2 = clock();
59
60 std::vector<int> result = find_subset(l, u, w);
61
62 auto t3 = clock();
63
64 // BEGIN SECRET
65 puts("14e047d7a2907b9034950b074822b302");
66 // END SECRET
67
68 printf("%d\n", (int)result.size());
69 for (int x : result) printf("%d ", x);
70 printf("\n");
71
72 auto t4 = clock();
73
74 // reset console output
75 freopen("CON", "w", stdout);
76
77 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
78
79 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
80 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
81 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
82
83 return 0;
84 }
85 // END CUT
86 /*
87 result.size() = 1000
88
89 t2-t1 = 0.359
90 t3-t2 = 0.203
91 t4-t3 = 0
92
93 Process returned 0 (0x0) execution time : 0.594 s
94 Press any key to continue.
95 */

Listing 5.1.9: molecules-159299.cpp


1 // https://oj.uz/submission/159299
2
3 #include<bits/stdc++.h>
4
5 #include <fstream>
6 #include<iostream>
7 #include<ctime>
8
9 using namespace std;
10
11 vector<pair<int, int> >v;
12
13 vector<int> find_subset(int l, int u, vector<int>w)
14 {
15 int n=w.size(); v.resize(n);
16 for(int i=0; i<n; i++) v[i]={w[i], i};
17
18 sort(v.begin(), v.end());
CAPITOLUL 5. IOI 2016 551

19
20 int idx=0;
21 long long sum=0ll;
22 for(int i=0; i<n; i++)
23 {
24 for(; idx<n && sum<l; idx++)
25 sum+=v[idx].first;
26
27 if(sum>=l && sum<=u)
28 {
29 vector<int>res;
30 for(int j=i; j<idx; j++)
31 res.push_back(v[j].second);
32
33 sort(res.begin(), res.end());
34
35 return res;
36 }
37
38 sum-=v[i].first;
39 }
40 return vector<int>();
41 }
42
43 // BEGIN CUT
44 int main()
45 {
46 auto t1 = clock();
47
48 std::freopen("../tests/subtask_6/108", "r", stdin);
49 std::freopen("6-108.out.txt", "w", stdout);
50
51 int n, l, u;
52 scanf("%d %d %d", &n, &l, &u);
53
54 std::vector<int> w(n);
55 for (int i = 0; i < n; i++)scanf("%d", &w[i]);
56
57 auto t2 = clock();
58
59 std::vector<int> result = find_subset(l, u, w);
60
61 auto t3 = clock();
62
63 // BEGIN SECRET
64 puts("14e047d7a2907b9034950b074822b302");
65 // END SECRET
66
67 printf("%d\n", (int)result.size());
68 for (int x : result) printf("%d ", x);
69 printf("\n");
70
71 auto t4 = clock();
72
73 // reset console output
74 freopen("CON", "w", stdout);
75
76 cout<<"result.size() = "<<(int)result.size()<<"\n\n";
77
78 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
79 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
80 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
81
82 return 0;
83 }
84 // END CUT
85 /*
86 result.size() = 1000
87
88 t2-t1 = 0.343
89 t3-t2 = 0.282
90 t4-t3 = 0
91
92 Process returned 0 (0x0) execution time : 0.672 s
93 Press any key to continue.
94 */
CAPITOLUL 5. IOI 2016 552

5.1.3 *Rezolvare detaliat 

5.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 nal  a roller-coaster-ului. în aceast 
problem , vom considera c  trenul este punctiform (are lungime egal  cu 0).
Pentru ecare 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  e mai mic  sau egal  cu si
km/h.
` atunci când p r se³te acest sector, viteza trenului va  exact ti km/h, indiferent de viteza
cu care trenul a intrat în acest sector.

Roller-coaster-ul va  asamblat din aceste n sectoare speciale, care vor  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 ec rui segment de cale
ferat . Lungimea unui segment este m surat  în metri ³i poate  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 nal  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  e minim . În subtask-
ul 3 trebuie doar s  vericaµi dac  exist  o ordonare a sectoarelor speciale astfel încât aceast 
lungime total  s  e 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  e 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 5. IOI 2016 553

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 nalul 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 nal  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  verice
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

5.2.1 Indicaµii de rezolvare

Author: Kento Nikaido


Keio University, snukent@gmail.com, Japan
Subtask 1. To solve the rst subtask, one could iterate over all n! possible permutations of
the special sections. Once the permutation is xed, 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 rst.
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 5. IOI 2016 554

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

Consider an innite 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 sucient 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 eciently, 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.

5.2.2 Coduri surs 

Listing 5.2.1: railroad_mp_nlogn.cpp


1 #include <cstdio>
2 #include <vector>
3 #include <algorithm>
4
5 #include <fstream>
6 #include<iostream>
7 #include<ctime>
CAPITOLUL 5. IOI 2016 555

8
9 using namespace std;
10
11 #define mp make_pair
12 #define pb push_back
13 #define fs first
14 #define sc second
15
16 typedef long long int64;
17
18 const int INF = (int) 1e9;
19
20 int dsu_get(vector<int>& p, int u)
21 {
22 return (u == p[u]) ? u : (p[u] = dsu_get(p, p[u]));
23 }
24
25 bool dsu_union(vector<int>& p, int u, int v)
26 {
27 u = dsu_get(p, u), v = dsu_get(p, v);
28 p[u] = v;
29 return (u != v);
30 }
31
32 int64 plan_roller_coaster(vector<int> s, vector<int> t)
33 {
34 int n = (int) s.size();
35 vector< pair< int, pair< int, int > > > e, edges;
36
37 for (int i = 0; i < n; ++i)
38 {
39 e.pb(mp(s[i], mp(1, i)));
40 e.pb(mp(t[i], mp(-1, i)));
41 }
42
43 e.pb(mp(INF, mp(1, n)));
44 e.pb(mp(1, mp(-1, n)));
45 n++;
46 vector<int> p(n);
47
48 for (int i = 0; i < n; ++i)
49 {
50 p[i] = i;
51 }
52
53 sort(e.begin(), e.end());
54
55 int64 res = 0;
56 for (int i = 0, delta = 0; i + 1 < (int) e.size(); ++i)
57 {
58 delta += e[i].sc.fs;
59 res += max(0, delta) * (int64) (e[i + 1].fs - e[i].fs);
60 edges.pb(mp(e[i + 1].fs - e[i].fs, mp(e[i].sc.sc, e[i + 1].sc.sc)));
61 if ((e[i + 1].fs == e[i].fs) || (delta != 0))
62 dsu_union(p, e[i].sc.sc, e[i + 1].sc.sc);
63 }
64
65 sort(edges.begin(), edges.end());
66
67 for (int i = 0; i < (int) edges.size(); ++i)
68 if (dsu_union(p, edges[i].sc.fs, edges[i].sc.sc))
69 res += edges[i].fs;
70
71 return res;
72 }
73
74 int main()
75 {
76 auto t1 = clock();
77
78 std::freopen("../tests/subtask_4/092", "r", stdin);
79 std::freopen("4-092.out.txt", "w", stdout);
80
81 int n, need_answer;
82
83 scanf("%d", &n);
CAPITOLUL 5. IOI 2016 556

84
85 std::vector<int> s(n), t(n);
86
87 for (int i = 0; i < n; ++i)
88 scanf("%d%d", &s[i], &t[i]);
89
90 auto t2 = clock();
91
92 long long ans = plan_roller_coaster(s, t);
93
94 auto t3 = clock();
95
96 printf("%lld\n", ans);
97
98 auto t4 = clock();
99
100 // reset console output
101 freopen("CON", "w", stdout);
102
103 cout<<"ans = "<<ans<<"\n\n";
104
105 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
106 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
107 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
108
109 return 0;
110 }
111 /*
112 ans = 309419403
113
114 t2-t1 = 0.703
115 t3-t2 = 1.563
116 t4-t3 = 0
117
118 Process returned 0 (0x0) execution time : 2.313 s
119 Press any key to continue.
120 */

Listing 5.2.2: railroad-103076.cpp


1 // https://oj.uz/submission/103076
2
3 #include "railroad.h"
4 #include <bits/stdc++.h>
5
6 #define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
7 #define pb push_back
8 #define MP make_pair
9 #define F first
10 #define S second
11 #define ET cout << "\n"
12 #define MEM(i,j) memset(i,j,sizeof i)
13 #define ALL(v) v.begin(),v.end()
14 #define DB(a,s,e) {for(int i=s;i<e;++i) cerr << a[i] << " ";ET;}
15
16 using namespace std;
17
18 typedef long long ll;
19 typedef pair<int,int> pii;
20 typedef pair<ll,ll> pll;
21
22 struct mode
23 {
24 ll p,v,i;
25 bool operator < (const mode&x) const
26 {
27 return p < x.p;
28 }
29 };
30
31 struct edge
32 {
33 ll a,b,w;
34 bool operator < (const edge&x) const
35 {
CAPITOLUL 5. IOI 2016 557

36 return w<x.w;
37 }
38 };
39
40 ll boss[200005];
41
42 ll finds(ll a)
43 {
44 if(a==boss[a]) return a;
45 return boss[a]=finds(boss[a]);
46 }
47
48 void Union(ll a,ll b)
49 {
50 a=finds(a),b=finds(b);
51 if(a==b) return;
52 boss[a]=b;
53 }
54
55 ll plan_roller_coaster(vector<int> s,vector<int> t)
56 {
57 ll n=s.size()+1,ans=0,nw=0;
58 vector<mode> v;
59 vector<pii> interesting;
60 vector<edge> e;
61 s.pb(1000000001),t.pb(1);
62
63 for(int i=0;i<n;++i)
64 boss[i]=i,v.pb(mode{s[i],1,i}),v.pb(mode{t[i],-1,i});
65
66 sort(ALL(v));
67
68 for(int i=0,t=0;i+1<v.size();)
69 {
70 interesting.pb(MP(v[i].p,v[i].i));
71 while(t<v.size()&&v[i].p==v[t].p)
72 nw+=v[t].v,++t;
73 if(nw>0)
74 ans+=nw*(v[t].p-v[i].p);
75 if(nw!=0)
76 Union(v[t].i,v[i].i);
77 for(++i;i<t;++i)
78 Union(v[i-1].i,v[i].i);
79 }
80
81 for(int i=0;i+1<interesting.size();++i)
82 if(finds(interesting[i].S)!=finds(interesting[i+1].S))
83 e.pb(edge{interesting[i].S,
84 interesting[i+1].S,
85 interesting[i+1].F-interesting[i].F});
86 sort(ALL(e));
87
88 for(auto i:e)
89 if(finds(i.a)!=finds(i.b))
90 Union(i.a,i.b),ans+=i.w;
91
92 return ans;
93 }
94
95 int main()
96 {
97 auto t1 = clock();
98
99 std::freopen("../tests/subtask_4/092", "r", stdin);
100 std::freopen("4-092.out.txt", "w", stdout);
101
102 int n, need_answer;
103
104 scanf("%d", &n);
105
106 std::vector<int> s(n), t(n);
107
108 for (int i = 0; i < n; ++i)
109 scanf("%d%d", &s[i], &t[i]);
110
111 auto t2 = clock();
CAPITOLUL 5. IOI 2016 558

112
113 long long ans = plan_roller_coaster(s, t);
114
115 auto t3 = clock();
116
117 printf("%lld\n", ans);
118
119 auto t4 = clock();
120
121 // reset console output
122 freopen("CON", "w", stdout);
123
124 cout<<"ans = "<<ans<<"\n\n";
125
126 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
127 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
128 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
129
130 return 0;
131 }
132 /*
133 ans = 309419403
134
135 t2-t1 = 0.745
136 t3-t2 = 0.893
137 t4-t3 = 0
138
139 Process returned 0 (0x0) execution time : 1.669 s
140 Press any key to continue.
141 */

Listing 5.2.3: railroad-117891.cpp


1 // https://oj.uz/submission/117891
2
3 #include "railroad.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 #define N 200005
9 #define ff first
10 #define ss second
11
12 vector<pair<int, pair<int, int> > > f;
13 int p[N];
14
15 int find(int v)
16 {
17 return (v == p[v] ? v : p[v] = find(p[v]));
18 }
19
20 bool unite(int v, int u)
21 {
22 v = find(v);
23 u = find(u);
24 if(v == u) return 0;
25 p[v] = u;
26 return 1;
27 }
28
29 map<int, int> sw;
30 map<int, vector<int> > in;
31
32 long long plan_roller_coaster(std::vector<int> s, std::vector<int> t)
33 {
34 int n = s.size();
35 s.push_back(1000000001);
36 t.push_back(1);
37
38 for(int i = 0; i <= n; i++)
39 {
40 sw[s[i]]++;
41 p[i] = i;
42 sw[t[i]]--;
CAPITOLUL 5. IOI 2016 559

43 in[s[i]].push_back(i);
44 in[t[i]].push_back(i);
45 }
46
47 long long res = 0;
48 int bal = 0, last = 0, lastid = n;
49
50 for(auto x : sw)
51 {
52 if(bal > 0)
53 {
54 res += 1LL * bal * (x.ff - last);
55 }
56 for(int y : in[x.ff])
57 {
58 f.push_back({ bal == 0 ? x.ff - last : 0, {y, lastid}});
59 lastid = y;
60 last = x.ff;
61 }
62
63 bal += x.ss;
64 }
65
66 sort(f.begin(), f.end());
67
68 for(auto x : f)
69 res += x.ff * unite(x.ss.ff, x.ss.ss);
70
71 return res;
72 }
73
74 int main()
75 {
76 auto t1 = clock();
77
78 std::freopen("../tests/subtask_4/092", "r", stdin);
79 std::freopen("4-092.out.txt", "w", stdout);
80
81 int n, need_answer;
82
83 scanf("%d", &n);
84
85 std::vector<int> s(n), t(n);
86
87 for (int i = 0; i < n; ++i)
88 scanf("%d%d", &s[i], &t[i]);
89
90 auto t2 = clock();
91
92 long long ans = plan_roller_coaster(s, t);
93
94 auto t3 = clock();
95
96 printf("%lld\n", ans);
97
98 auto t4 = clock();
99
100 // reset console output
101 freopen("CON", "w", stdout);
102
103 cout<<"ans = "<<ans<<"\n\n";
104
105 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
106 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
107 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
108
109 return 0;
110 }
111 /*
112 ans = 309419403
113
114 t2-t1 = 0.794
115 t3-t2 = 3.261
116 t4-t3 = 0
117
118 Process returned 0 (0x0) execution time : 4.500 s
CAPITOLUL 5. IOI 2016 560

119 Press any key to continue.


120 */

Listing 5.2.4: railroad-135900.cpp


1 // https://oj.uz/submission/135900
2
3 #include<bits/stdc++.h>
4
5 #define x first
6 #define y second
7
8 using namespace std;
9
10 typedef long long ll;
11 const int N = 200005;
12 int P[N];
13
14 int Find(int v)
15 {
16 return (P[v] < 0 ? v : (P[v] = Find(P[v])));
17 }
18
19 inline bool Merge(int v, int u)
20 {
21 v = Find(v);
22 u = Find(u);
23 if (v == u)
24 return 0;
25 P[v] = u;
26 return 1;
27 }
28
29 int64_t plan_roller_coaster(vector < int > S, vector < int > T)
30 {
31 int n = (int)S.size();
32 vector < pair < int , pair < int , int > > > A, E;
33 for (int i = 0; i < n; i ++)
34 {
35 A.push_back({S[i], {1, i}});
36 A.push_back({T[i], {-1, i}});
37 }
38 A.push_back({(int)1e9, {1, n}});
39 A.push_back({1, {-1, n ++}});
40
41 sort(A.begin(), A.end());
42
43 int SM = 0;
44 ll tot = 0;
45 memset(P, -1, sizeof(P));
46
47 for (int i = 0; i + 1 < (int)A.size(); i ++)
48 {
49 SM += A[i].y.x;
50 tot += max(SM, 0) * (ll)(A[i + 1].x - A[i].x);
51 E.push_back({A[i + 1].x - A[i].x, {A[i].y.y, A[i + 1].y.y}});
52 if (A[i].x == A[i + 1].x || SM)
53 Merge(A[i].y.y, A[i + 1].y.y);
54 }
55
56 sort(E.begin(), E.end());
57
58 for (auto e : E)
59 if (Merge(e.y.x, e.y.y))
60 tot += e.x;
61
62 return (tot);
63 }
64
65 int main()
66 {
67 auto t1 = clock();
68
69 std::freopen("../tests/subtask_4/092", "r", stdin);
70 std::freopen("4-092.out.txt", "w", stdout);
CAPITOLUL 5. IOI 2016 561

71
72 int n, need_answer;
73
74 scanf("%d", &n);
75
76 std::vector<int> s(n), t(n);
77
78 for (int i = 0; i < n; ++i)
79 scanf("%d%d", &s[i], &t[i]);
80
81 auto t2 = clock();
82
83 long long ans = plan_roller_coaster(s, t);
84
85 auto t3 = clock();
86
87 printf("%lld\n", ans);
88
89 auto t4 = clock();
90
91 // reset console output
92 freopen("CON", "w", stdout);
93
94 cout<<"ans = "<<ans<<"\n\n";
95
96 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
97 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
98 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
99
100 return 0;
101 }
102 /*
103 ans = 309419403
104
105 t2-t1 = 0.721
106 t3-t2 = 1.47
107 t4-t3 = 0
108
109 Process returned 0 (0x0) execution time : 2.232 s
110 Press any key to continue.
111 */

Listing 5.2.5: railroad-223610.cpp


1 // https://oj.uz/submission/223610
2
3 #include "railroad.h"
4 #include<bits/stdc++.h>
5
6 using namespace std;
7
8 typedef long long ll;
9
10 ///Disjoint Sets
11 ///Sweep Line
12 ///Set the balance always to 0, connecting special sections
13
14 int link[200005];
15 int setSize[200005];
16 int Find(int u)
17 {
18 if(link[u]==u) return u;
19 else return link[u]=Find(link[u]);
20 }
21
22 bool same(int u,int v)
23 {
24 return Find(u)==Find(v);
25 }
26
27 void unite(int u,int v)
28 {
29 u=Find(u);
30 v=Find(v);
31 if(u==v) return;
CAPITOLUL 5. IOI 2016 562

32 if(setSize[v]>setSize[u]) swap(u,v);
33 setSize[u]+=setSize[v];
34 link[v]=u;
35 }
36
37 void initDSU(int n)
38 {
39 for(int i=1;i<=n;i++)
40 {
41 setSize[i]=1;
42 link[i]=i;
43 }
44 }
45
46 struct event
47 {
48 int T;
49 int op;
50 int id; //Which special section
51 bool operator<(const event& a)
52 {
53 if(T<a.T)
54 return true;
55 else
56 {
57 if(T==a.T)
58 {
59 if(op<a.op) return true;
60 else return false;
61 }
62 else return false;
63 }
64 }
65 };
66
67 struct segment
68 {
69 ll length;
70 int a,b; //ID where it starts and ends
71 bool operator < (const segment& s)
72 {
73 return length<s.length;
74 }
75 };
76
77 vector<event> timeline;
78 vector<segment> remaining; //Not taken because its balance was 0
79
80 long long plan_roller_coaster(vector<int> s, vector<int> t)
81 {
82 int n=s.size();
83 ll cost=0;
84 initDSU(n);
85 for(int i=0;i<n;i++)
86 {
87 event e={s[i],+1,i+1};
88 timeline.push_back(e);
89 e={t[i],-1,i+1};
90 timeline.push_back(e);
91 }
92
93 sort(timeline.begin(),timeline.end());
94
95 //Lines to the right are +1
96 //Lines to the left are -1
97 ll balance=-1; //Because there is a line that goes from inf to 1
98
99 for(int i=0;i<2*n-1;i++)
100 {
101 balance+=timeline[i].op;
102 if(balance<0)
103 {
104 //Add a track from left to right (free) and connect i with i+1
105 unite(timeline[i].id,timeline[i+1].id);
106 }
107 else
CAPITOLUL 5. IOI 2016 563

108 if(balance>0)
109 {
110 //We need to slow down the train so we add "balance" tracks
111 cost+=(ll)(timeline[i+1].T-timeline[i].T)*balance;
112
113 //With length: distance between that two events
114 unite(timeline[i].id,timeline[i+1].id);
115 }
116 else
117 if(balance==0)
118 {
119 segment s;
120 s.length=(timeline[i+1].T-timeline[i].T);
121 s.a=timeline[i].id;
122 s.b=timeline[i+1].id;
123 remaining.push_back(s);
124 }
125 }
126
127 sort(remaining.begin(),remaining.end());
128
129 set<int> R; //Roots
130 for(int i=1;i<=n;i++)
131 {
132 R.insert(Find(i));
133 }
134
135 int roots=R.size();
136 int i=0;
137 while(roots>1)
138 { //Not fully connected
139 int a=remaining[i].a;
140 int b=remaining[i].b;
141 if(!same(a,b))
142 {
143 unite(a,b);
144 cost+=remaining[i].length;
145 roots--;
146 }
147 i++;
148 }
149
150 return cost;
151 }
152
153 int main()
154 {
155 auto t1 = clock();
156
157 std::freopen("../tests/subtask_4/092", "r", stdin);
158 std::freopen("4-092.out.txt", "w", stdout);
159
160 int n, need_answer;
161
162 scanf("%d", &n);
163
164 std::vector<int> s(n), t(n);
165
166 for (int i = 0; i < n; ++i)
167 scanf("%d%d", &s[i], &t[i]);
168
169 auto t2 = clock();
170
171 long long ans = plan_roller_coaster(s, t);
172
173 auto t3 = clock();
174
175 printf("%lld\n", ans);
176
177 auto t4 = clock();
178
179 // reset console output
180 freopen("CON", "w", stdout);
181
182 cout<<"ans = "<<ans<<"\n\n";
183
CAPITOLUL 5. IOI 2016 564

184 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
185 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
186 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
187
188 return 0;
189 }
190 /*
191 ans = 309419403
192
193 t2-t1 = 0.69
194 t3-t2 = 1.037
195 t4-t3 = 0
196
197 Process returned 0 (0x0) execution time : 1.779 s
198 Press any key to continue.
199 */

Listing 5.2.6: railroad-233098.cpp


1 // https://oj.uz/submission/233098
2
3 #include <algorithm>
4 #include <vector>
5
6 #include<ctime> // clock()
7 #include<cstdint> // int64_t
8 #include<fstream> // freeopen
9 #include<iostream> // cout
10
11 using namespace std;
12
13 #define rep(i, a, b) for (auto i = (a); i < (b); ++i)
14 #define trav(x, v) for (auto &x : v)
15 #define all(x) begin(x), end(x)
16 #define lb(x...) lower_bound(x)
17 #define sz(x) int((x).size())
18 #define eb(x...) emplace_back(x)
19
20 const int inf = 2e9;
21 using ll = int64_t;
22 using vi = vector<int>;
23
24 vi nxt;
25
26 int head(int u)
27 {
28 return nxt[u] != -1 ? nxt[u] = head(nxt[u]) : u;
29 }
30
31 bool unite(int u, int v)
32 {
33 u = head(u); v = head(v);
34 if (u == v) return false;
35 nxt[v] = u; return true;
36 }
37
38 ll plan_roller_coaster(vi s, vi t)
39 {
40 s.eb(inf); t.eb(1); int n = sz(s);
41 vi p = s; p.insert(end(p), all(t));
42
43 sort(all(p)); p.erase(unique(all(p)), end(p));
44
45 int m = sz(p); nxt.resize(m, -1);
46
47 vi sum(m--), e;
48 rep(i, 0, n)
49 {
50 int l = lb(all(p), s[i]) - begin(p), r = lb(all(p),t[i])-begin(p);
51 unite(l, r); ++sum[l]; --sum[r];
52 }
53 rep(i, 0, m) sum[i + 1] += sum[i];
54
55 ll ans = 0;
56 rep(i, 0, m)
CAPITOLUL 5. IOI 2016 565

57 {
58 if (sum[i])
59 {
60 unite(i, i + 1);
61 if (sum[i] > 0)
62 ans += 1ll * sum[i] * (p[i + 1] - p[i]);
63 }
64 else e.eb(i);
65 }
66
67 sort(all(e), [&p](int u, int v)
68 { return p[u + 1] - p[u] < p[v + 1] - p[v]; });
69
70 trav(u, e)
71 if (unite(u, u + 1))
72 ans += p[u + 1] - p[u];
73
74 return ans;
75 }
76
77 int main()
78 {
79 auto t1 = clock();
80
81 std::freopen("../tests/subtask_4/092", "r", stdin);
82 std::freopen("4-092.out.txt", "w", stdout);
83
84 int n, need_answer;
85
86 scanf("%d", &n);
87
88 std::vector<int> s(n), t(n);
89
90 for (int i = 0; i < n; ++i)
91 scanf("%d%d", &s[i], &t[i]);
92
93 auto t2 = clock();
94
95 long long ans = plan_roller_coaster(s, t);
96
97 auto t3 = clock();
98
99 printf("%lld\n", ans);
100
101 auto t4 = clock();
102
103 // reset console output
104 freopen("CON", "w", stdout);
105
106 cout<<"ans = "<<ans<<"\n\n";
107
108 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
109 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
110 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
111
112 return 0;
113 }
114 /*
115 ans = 309419403
116
117 t2-t1 = 0.739
118 t3-t2 = 1.324
119 t4-t3 = 0
120
121 Process returned 0 (0x0) execution time : 2.094 s
122 Press any key to continue.
123 */
CAPITOLUL 5. IOI 2016 566

5.2.3 *Rezolvare detaliat 

5.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 ecare 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 
cele dou  staµii pe care le va conecta.
Fiecare porµiune de cale ferat , inclusiv linia expres, poate  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  e minimi-
zat.
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 ³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 5. IOI 2016 567

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

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 5. IOI 2016 568

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2048 MB

5.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 nd the
diameter by checking every pair of stations. In Subtask 2 you have to precompute the stations
coordinates along the main line xi = Pi􀀀1 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
nd the resulting diameter. To nd 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 xed 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
o
6 k are valid. This 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 5. IOI 2016 569

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 x j > i and y < z. Now the pair
produces some square i xj 􀀀 xi + dj + di > k. Let's x j and nd 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 ' xj  dj   xi  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 nd 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 prex and sux maximums. Now when we
CAPITOLUL 5. IOI 2016 570

use the same solution as for subtask 6, but we query maximum and minimum values for inequality
only when 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.

5.3.2 Coduri surs 

Listing 5.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]);
CAPITOLUL 5. IOI 2016 571

62 if (x[wh] + d[wh] > mostright.fi + mostright.se)


63 mostright = mp(x[wh], d[wh]);
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));
CAPITOLUL 5. IOI 2016 572

138 for (int i = 0; i < n - 1; i++) {


139 scanf("%d", &l[i]);
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 5.3.2: sol_ge_nlogd.cpp


1 #include <cstdio> // n log(d)
2 #include <cstdlib>
3 #include <vector>
4 #include <deque>
5 #include <iostream>
6 #include <algorithm>
7 #include <ctime>
8
9 using namespace std;
10
11 #define pb push_back
12 #define mp make_pair
13 #define fs first
14 #define sc second
15
16 const int maxN = 1000 * 1000;
17 const long long inf = (long long) 1e18;
18
19 long long x[maxN + 1];
20 long long d[maxN + 1];
21 bool important[maxN + 1];
22 long long maxsump[maxN + 1], mindifp[maxN + 1];
23 long long difl, difr, suml, sumr;
24 double checkempty = 0.0;
25
26 bool isNonempty(int n)
27 {
CAPITOLUL 5. IOI 2016 573

28 if (suml > sumr || difl > difr) return false;


29 int p = 0;
30 for (int i = 1; i < n; i++)
31 {
32 while (p < i && x[p] + x[i] <= sumr && x[p] - x[i] <= difr) p++;
33 while (p > 0 && (x[p] + x[i] > sumr || x[p] - x[i] > difr)) p--;
34 if (x[i] + x[p] >= suml &&
35 x[i] + x[p] <= sumr &&
36 x[p] - x[i] >= difl &&
37 x[p] - x[i] <= difr)
38 {
39 return true;
40 }
41 }
42
43 return false;
44 }
45
46 bool check(int n, int c, long long k)
47 {
48 suml = difl = -inf;
49 sumr = difr = inf;
50
51 int p = -1;
52 deque <int> q;
53
54 for (int i = 0; i < n; i++)
55 {
56 if (important[i])
57 {
58 if (q.front() == p)
59 q.pop_front();
60
61 while (p + 1 < i &&
62 d[q.front()] - x[q.front()] > k - x[i] - d[i])
63 {
64 p++;
65 if (q.front() == p)
66 q.pop_front();
67 }
68
69 if (p != -1)
70 {
71 suml = max(suml, x[i] + d[i] - k + c + maxsump[p]);
72 sumr = min(sumr, x[i] - d[i] + k - c + mindifp[p]);
73 difl = max(difl, -x[i] + d[i] - k + c + maxsump[p]);
74 difr = min(difr, -x[i] - d[i] + k - c + mindifp[p]);
75 }
76 }
77
78 while (!q.empty() && d[q.back()] - x[q.back()] <= d[i] - x[i])
79 q.pop_back();
80
81 q.push_back(i);
82 }
83
84 return isNonempty(n);
85 }
86
87 long long find_shortcut(int n, vector <int> len, vector <int> dep, int c)
88 {
89 x[0] = 0ll;
90
91 for (int i = 0; i < n; i++)
92 {
93 d[i] = dep[i];
94 if (i + 1 < n)
95 {
96 x[i + 1] = x[i] + len[i];
97 }
98 }
99
100 maxsump[0] = d[0];
101 mindifp[0] = -d[0];
102
103 for (int i = 1; i < n; i++)
CAPITOLUL 5. IOI 2016 574

104 {
105 maxsump[i] = max(maxsump[i - 1], x[i] + d[i]);
106 mindifp[i] = min(mindifp[i - 1], x[i] - d[i]);
107 }
108
109 important[n - 1] = true;
110 int curb = n - 1;
111 for (int i = n - 2; i >= 0; i--)
112 {
113 if (x[i] - d[i] < x[curb] - d[curb])
114 {
115 important[i] = true;
116 curb = i;
117 }
118 }
119
120 long long lb = 0ll;
121 long long rb = x[n - 1] + 2e9;
122 while (lb < rb)
123 {
124 long long mid = (lb + rb) / 2;
125 if (check(n, c, mid))
126 rb = mid;
127 else
128 lb = mid + 1;
129 }
130
131 return lb;
132 }
133
134 // BEGIN CUT
135 int main()
136 {
137 auto t1 = clock();
138
139 std::freopen("../tests/subtask_8/220", "r", stdin);
140 std::freopen("8-220.out.txt", "w", stdout);
141
142 int n, c;
143 scanf("%d%d", &n, &c);
144
145 vector<int> l(n - 1);
146 vector<int> d(n);
147 for (int i = 0; i < n - 1; i++)
148 scanf("%d", &l[i]);
149 for (int i = 0; i < n; i++)
150 scanf("%d", &d[i]);
151
152 auto t2 = clock();
153
154 long long t = find_shortcut(n, l, d, c);
155
156 auto t3 = clock();
157
158 // BEGIN SECRET
159 puts("14e047d7a2907b9034950b074822b302");
160 // END SECRET
161
162 printf("%lld\n", t);
163
164 auto t4 = clock();
165
166 // reset console output
167 freopen("CON", "w", stdout);
168
169 cout<<"n = "<<n<<" c = "<<c<<"\n";
170 cout<<"t = "<<t<<"\n\n";
171
172 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
173 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
174 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
175
176 return 0;
177 }
178 // END CUT
179 /*
CAPITOLUL 5. IOI 2016 575

180 n = 1000000 c = 1000


181 t = 1000857674
182
183 t2-t1 = 4.79
184 t3-t2 = 13.439
185 t4-t3 = 0
186
187 Process returned 0 (0x0) execution time : 18.311 s
188 Press any key to continue.
189 */

Listing 5.3.3: sol_ge_nlogd_fastio.cpp


1 #include <cstdio>
2 #include <cstdlib>
3 #include <vector>
4 #include <deque>
5 #include <iostream>
6 #include <algorithm>
7
8 #include<ctime> // clock()
9
10 using namespace std;
11
12 #define pb push_back
13 #define mp make_pair
14 #define fs first
15 #define sc second
16
17 const int maxN = 1000 * 1000;
18 const long long inf = (long long) 1e18;
19
20 long long x[maxN + 1];
21 long long d[maxN + 1];
22 bool important[maxN + 1];
23 long long maxsump[maxN + 1], mindifp[maxN + 1];
24
25 long long difl, difr, suml, sumr;
26 double checkempty = 0.0;
27
28 /** Interface */
29
30 inline int readChar();
31 template <class T = int> inline T readInt();
32 template <class T> inline void writeInt( T x, char end = 0 );
33 inline void writeChar( int x );
34 inline void writeWord( const char *s );
35
36 /** Read */
37
38 static const int buf_size = 4096;
39
40 inline int getChar()
41 {
42 static char buf[buf_size];
43 static int len = 0, pos = 0;
44 if (pos == len)
45 pos = 0, len = fread(buf, 1, buf_size, stdin);
46 if (pos == len)
47 return -1;
48 return buf[pos++];
49 }
50
51 inline int readChar()
52 {
53 int c = getChar();
54 while (c <= 32)
55 c = getChar();
56 return c;
57 }
58
59 template <class T>
60 inline T readInt()
61 {
62 int s = 1, c = readChar();
CAPITOLUL 5. IOI 2016 576

63 T x = 0;
64 if (c == ’-’)
65 s = -1, c = getChar();
66 while (’0’ <= c && c <= ’9’)
67 x = x * 10 + c - ’0’, c = getChar();
68 return s == 1 ? x : -x;
69 }
70
71 /** Write */
72
73 static int write_pos = 0;
74 static char write_buf[buf_size];
75
76 inline void writeChar( int x )
77 {
78 if (write_pos == buf_size)
79 fwrite(write_buf, 1, buf_size, stdout), write_pos = 0;
80 write_buf[write_pos++] = x;
81 }
82
83 template <class T>
84 inline void writeInt( T x, char end )
85 {
86 if (x < 0)
87 writeChar(’-’), x = -x;
88
89 char s[24];
90 int n = 0;
91 while (x || !n)
92 s[n++] = ’0’ + x % 10, x /= 10;
93 while (n--)
94 writeChar(s[n]);
95 if (end)
96 writeChar(end);
97 }
98
99 inline void writeWord( const char *s )
100 {
101 while ( *s)
102 writeChar( *s++);
103 }
104
105 struct Flusher
106 {
107 ~Flusher() {
108 if (write_pos)
109 fwrite(write_buf, 1, write_pos, stdout), write_pos = 0;
110 }
111 } flusher;
112
113 bool isNonempty(int n)
114 {
115 if (suml > sumr || difl > difr)
116 return false;
117
118 int p = 0;
119 for (int i = 1; i < n; i++)
120 {
121 while (p < i && x[p] + x[i] <= sumr && x[p] - x[i] <= difr)
122 p++;
123 while (p > 0 && (x[p] + x[i] > sumr || x[p] - x[i] > difr))
124 p--;
125
126 if (x[i] + x[p] >= suml &&
127 x[i] + x[p] <= sumr &&
128 x[p] - x[i] >= difl &&
129 x[p] - x[i] <= difr)
130 {
131 return true;
132 }
133 }
134
135 return false;
136 }
137
138 bool check(int n, int c, long long k)
CAPITOLUL 5. IOI 2016 577

139 {
140 suml = difl = -inf;
141 sumr = difr = inf;
142
143 int p = -1;
144 deque <int> q;
145
146 for (int i = 0; i < n; i++)
147 {
148 if (important[i])
149 {
150 if (q.front() == p)
151 q.pop_front();
152
153 while (p + 1 < i && d[q.front()] - x[q.front()] > k - x[i] - d[i])
154 {
155 p++;
156 if (q.front() == p)
157 q.pop_front();
158 }
159
160 if (p != -1)
161 {
162 suml = max(suml, x[i] + d[i] - k + c + maxsump[p]);
163 sumr = min(sumr, x[i] - d[i] + k - c + mindifp[p]);
164 difl = max(difl, -x[i] + d[i] - k + c + maxsump[p]);
165 difr = min(difr, -x[i] - d[i] + k - c + mindifp[p]);
166 }
167 }
168
169 while (!q.empty() && d[q.back()] - x[q.back()] <= d[i] - x[i])
170 q.pop_back();
171
172 q.push_back(i);
173 }
174
175 return isNonempty(n);
176 }
177
178 long long find_shortcut(int n, vector <int> len, vector <int> dep, int c)
179 {
180 x[0] = 0ll;
181
182 for (int i = 0; i < n; i++)
183 {
184 d[i] = dep[i];
185 if (i + 1 < n)
186 {
187 x[i + 1] = x[i] + len[i];
188 }
189 }
190
191 maxsump[0] = d[0];
192 mindifp[0] = -d[0];
193
194 for (int i = 1; i < n; i++)
195 {
196 maxsump[i] = max(maxsump[i - 1], x[i] + d[i]);
197 mindifp[i] = min(mindifp[i - 1], x[i] - d[i]);
198 }
199
200 important[n - 1] = true;
201 int curb = n - 1;
202 for (int i = n - 2; i >= 0; i--)
203 {
204 if (x[i] - d[i] < x[curb] - d[curb])
205 {
206 important[i] = true;
207 curb = i;
208 }
209 }
210
211 long long lb = 0ll;
212 long long rb = inf;
213 while (lb < rb)
214 {
CAPITOLUL 5. IOI 2016 578

215 long long mid = (lb + rb) / 2;


216 if (check(n, c, mid))
217 rb = mid;
218 else
219 lb = mid + 1;
220 }
221
222 return lb;
223 }
224
225 // BEGIN CUT
226 int main()
227 {
228 auto t1 = clock();
229
230 std::freopen("../tests/subtask_8/220", "r", stdin);
231 std::freopen("8-220.out.txt", "w", stdout);
232
233 int n, c;
234
235 n = readInt();
236 c = readInt();
237
238 //cout<<"n = "<<n<<" c = "<<c<<"\n";
239
240 vector<int> l(n - 1);
241 vector<int> d(n);
242 for (int i = 0; i < n - 1; i++) {
243 l[i] = readInt();
244 }
245 for (int i = 0; i < n; i++) {
246 d[i] = readInt();
247 }
248
249 auto t2 = clock();
250
251 long long t = find_shortcut(n, l, d, c);
252
253 auto t3 = clock();
254
255 // BEGIN SECRET
256 puts("14e047d7a2907b9034950b074822b302");
257 // END SECRET
258
259 printf("%lld\n", t);
260
261 auto t4 = clock();
262
263 // reset console output
264 freopen("CON", "w", stdout);
265
266 cout<<"n = "<<n<<" c = "<<c<<"\n";
267 cout<<"t = "<<t<<"\n\n";
268
269 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
270 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
271 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
272
273 return 0;
274 }
275 // END CUT
276 /*
277 n = 1000000 c = 1000
278 t = 1000857674
279
280 t2-t1 = 0.328
281 t3-t2 = 18.613
282 t4-t3 = 0
283
284 Process returned 0 (0x0) execution time : 18.988 s
285 Press any key to continue.
286 */

Listing 5.3.4: sol_nk_nlogd.cpp


CAPITOLUL 5. IOI 2016 579

1 #include <bits/stdc++.h>
2
3 using namespace std;
4
5 using ll = long long;
6 using ld = long double;
7 using D = double;
8 using uint = unsigned int;
9
10 #ifdef WIN32
11 #define LLD "%I64d"
12 #else
13 #define LLD "%lld"
14 #endif
15
16 #define pb push_back
17 #define mp make_pair
18 #define all(x) (x).begin(),(x).end()
19 #define fi first
20 #define se second
21
22 const int maxn = 1000005;
23 const ll inf = 1e18;
24
25 ll x[maxn], d[maxn];
26 int n, C;
27 pair<ll, int> q[maxn];
28 int ans1, ans2;
29
30 bool can(ll diam)
31 {
32 bool needleft = false;
33 ll maxsum = inf;
34 ll minsum = -inf;
35 ll maxdif = inf;
36 ll mindif = -inf;
37 pair<ll, ll> mostleft = {inf, -inf};
38 pair<ll, ll> mostright = {-inf, -inf};
39 int l = 0;
40 int r = 0;
41 for (int i = 0; i < n; i++)
42 {
43 // no need to check whether it is "small" or not,
44 // because in case of "small" its square is inside "big"’s square
45 while (r > l && x[i] - q[l].fi + d[i] > diam)
46 {
47 int wh = q[l].se;
48 l++;
49 if (x[wh] - d[wh] < mostleft.fi - mostleft.se)
50 mostleft = {x[wh], d[wh]};
51 if (x[wh] + d[wh] > mostright.fi + mostright.se)
52 mostright = {x[wh], d[wh]};
53
54 needleft = true;
55 }
56 if (needleft)
57 {
58 maxsum = min(maxsum,
59 (mostleft.fi + diam - C - d[i] - mostleft.se) + x[i]);
60 minsum = max(minsum,
61 (mostright.fi - (diam - C - d[i] - mostright.se)) + x[i]);
62 maxdif = min(maxdif,
63 x[i] - (mostright.fi - (diam - C - d[i] - mostright.se)));
64 mindif = max(mindif,
65 x[i] - (mostleft.fi + diam - C - d[i] - mostleft.se));
66 }
67
68 while (r > l && q[r - 1].fi > x[i] - d[i]) r--;
69 q[r++] = {x[i] - d[i], i};
70 }
71
72 if (maxsum < minsum || maxdif < mindif) return false;
73 int curdif = 0;
74 int cursum = n;
75 for (int i = 0; i < n; i++)
76 {
CAPITOLUL 5. IOI 2016 580

77 while (curdif < n && x[curdif] - x[i] < mindif) curdif++;


78 while (cursum > 0 && x[cursum - 1] + x[i] >= minsum) cursum--;
79 int cur = max({cursum, curdif, i + 1});
80 if (cur < n && x[cur] + x[i] <= maxsum && x[cur] - x[i] <= maxdif)
81 {
82 ans1 = i;
83 ans2 = cur;
84 return true;
85 }
86 }
87 return false;
88 }
89
90 long long find_shortcut(int N, vector <int> L0, vector <int> L, int C_)
91 {
92 n = N;
93 C = C_;
94 ll cursum = 0;
95 for (int i = 0; i < n; i++)
96 {
97 d[i] = L[i];
98 x[i] = cursum;
99 if (i + 1 < n) cursum += L0[i];
100 }
101 ll l = 0;
102 ll r = inf;
103 while (r - l > 1)
104 {
105 ll mid = (l + r) / 2;
106 if (can(mid)) r = mid;
107 else l = mid;
108 }
109 can(r);
110 return r;
111 }
112
113 // BEGIN CUT
114 int main()
115 {
116 auto t1 = clock();
117
118 std::freopen("../tests/subtask_8/220", "r", stdin);
119 std::freopen("8-220.out.txt", "w", stdout);
120
121 int n, c;
122 scanf("%d%d", &n, &c);
123
124 vector <int> l(n - 1);
125 vector <int> d(n);
126 for (int i = 0; i < n - 1; i++)
127 scanf("%d", &l[i]);
128 for (int i = 0; i < n; i++)
129 scanf("%d", &d[i]);
130
131 auto t2 = clock();
132
133 long long t = find_shortcut(n, l, d, c);
134
135 auto t3 = clock();
136
137 // BEGIN SECRET
138 puts("14e047d7a2907b9034950b074822b302");
139 // END SECRET
140
141 printf("%lld\n", t);
142
143 auto t4 = clock();
144
145 // reset console output
146 freopen("CON", "w", stdout);
147
148 cout<<"n = "<<n<<" c = "<<c<<"\n";
149 cout<<"t = "<<t<<"\n\n";
150
151 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
152 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 5. IOI 2016 581

153 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
154
155 return 0;
156 }
157 // END CUT
158 /*
159 n = 1000000 c = 1000
160 t = 1000857674
161
162 t2-t1 = 4.629
163 t3-t2 = 8.341
164 t4-t3 = 0
165
166 Process returned 0 (0x0) execution time : 13.030 s
167 Press any key to continue.
168 */

Listing 5.3.5: shortcut-24626.cpp


1 // https://oj.uz/submission/24626
2
3 #include "shortcut.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 typedef long long lint;
9 typedef pair<lint, lint> pi;
10
11 struct Fucking_locality
12 {
13 lint arg1, arg2;
14 lint first;
15 int second;
16 bool operator < (const Fucking_locality &fuck) const
17 {
18 return make_pair(first,second) < make_pair(fuck.first,fuck.second);
19 }
20 };
21
22 int n, c;
23 lint a[1000005], b[1000005];
24 Fucking_locality v[1000005], w[1000005];
25
26 bool trial(lint x)
27 {
28 lint ps = -1e18, pe = 1e18, ms = -1e18, me = 1e18;
29 lint mx = -1e18, mxp = -1, smx = -1e18;
30 lint mn = 1e18, mnp = -1, smn = 1e18;
31 int p = 0;
32 for(int i=0; i<n; i++)
33 {
34 while(p < n && w[p].first + x < v[i].first)
35 {
36 lint cmx = w[p].arg1;
37 lint cmn = w[p].arg2;
38 if(mx < cmx)
39 {
40 smx = mx;
41 mx = cmx;
42 mxp = w[p].second;
43 }
44 else
45 if(smx < cmx) smx = cmx;
46
47 if(mn > cmn)
48 {
49 smn = mn;
50 mn = cmn;
51 mnp = w[p].second;
52 }
53 else
54 if(smn > cmn) smn = cmn;
55 p++;
56 }
CAPITOLUL 5. IOI 2016 582

57
58 lint q1 = (v[i].second != mxp ? mx : smx);
59 lint q2 = (v[i].second != mnp ? mn : smn);
60 lint ca = v[i].arg1;
61 lint cb = v[i].arg2;
62
63 ps = max(ps, q1 - x + c + cb + ca);
64 pe = min(pe, q2 + x - c + cb - ca);
65 ms = max(ms, q1 - x + c - cb + ca);
66 me = min(me, q2 + x - c - cb - ca);
67 }
68
69 p = 0;
70 for(int i=0; i<n; i++)
71 {
72 lint s = max(ps - b[i], b[i] - me);
73 lint e = min(pe - b[i], b[i] - ms);
74 while(p < n && s > b[p]) p++;
75 while(p > 0 && s <= b[p-1]) p--;
76 while(p < n && b[p] <= e) return true;
77 }
78
79 return false;
80 }
81
82 long long find_shortcut(int n, std::vector<int> l,
83 std::vector<int> d, int c)
84 {
85 ::n = n;
86 ::c = c;
87
88 for(int i=0; i<n; i++)
89 {
90 a[i] = d[i];
91 if(i >= 1) b[i] = b[i-1] + l[i-1];
92 }
93
94 for(int i=0; i<n; i++)
95 {
96 v[i] = (Fucking_locality){a[i], b[i], a[i] + b[i], i};
97 w[i] = (Fucking_locality){a[i] + b[i], -a[i] + b[i], -a[i] + b[i], i};
98 }
99
100 sort(v, v+n);
101 sort(w, w+n);
102
103 lint s = 0, e = 2e15;
104 while(s != e)
105 {
106 lint m = (s+e)/2;
107 if(trial(m)) e = m;
108 else s = m+1;
109 }
110
111 return s;
112 }
113
114 // BEGIN CUT
115 int main()
116 {
117 auto t1 = clock();
118
119 std::freopen("../tests/subtask_8/220", "r", stdin);
120 std::freopen("8-220.out.txt", "w", stdout);
121
122 int n, c;
123 scanf("%d%d", &n, &c);
124
125 vector<int> l(n - 1);
126 vector<int> d(n);
127 for (int i = 0; i < n - 1; i++)
128 scanf("%d", &l[i]);
129 for (int i = 0; i < n; i++)
130 scanf("%d", &d[i]);
131
132 auto t2 = clock();
CAPITOLUL 5. IOI 2016 583

133
134 long long t = find_shortcut(n, l, d, c);
135
136 auto t3 = clock();
137
138 // BEGIN SECRET
139 puts("14e047d7a2907b9034950b074822b302");
140 // END SECRET
141
142 printf("%lld\n", t);
143
144 auto t4 = clock();
145
146 // reset console output
147 freopen("CON", "w", stdout);
148
149 cout<<"n = "<<n<<" c = "<<c<<"\n";
150 cout<<"t = "<<t<<"\n\n";
151
152 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
153 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
154 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
155
156 return 0;
157 }
158 // END CUT
159 /*
160 n = 1000000 c = 1000
161 t = 1000857674
162
163 t2-t1 = 3.546
164 t3-t2 = 8.469
165 t4-t3 = 0
166
167 Process returned 0 (0x0) execution time : 12.344 s
168 Press any key to continue.
169 */

Listing 5.3.6: shortcut-33932.cpp


1 // https://oj.uz/submission/33932
2
3 #include "shortcut.h"
4 #include <algorithm>
5
6 #include<ctime>
7 #include<fstream>
8 #include<iostream>
9
10 using namespace std;
11
12 typedef long long llong;
13
14 int n, c;
15 vector<llong> dist;
16 vector<int> etc;
17 vector<pair<llong, int>> ad, sb;
18
19 bool check(llong m)
20 {
21 llong fs = -1ll, se = -1ll;
22 int fsi = -1, sei = -1, p, q;
23 llong lad = -1e18, gad = 1e18, lsb = -1e18, gsb = 1e18;
24 for (int i = 0, j = 0; i < n; ++i)
25 {
26 while (j < n && sb[j].first + m < ad[i].first)
27 {
28 p = sb[j].second;
29 llong mx = dist[p] + etc[p];
30 if (fs < mx)
31 {
32 sei = fsi;
33 fsi = p;
34 se = fs;
35 fs = mx;
CAPITOLUL 5. IOI 2016 584

36 }
37 else
38 if (se < mx) sei = p, se = mx;
39 ++j;
40 }
41
42 p = ad[i].second;
43 if (p == sb[0].second && j == 1 || j == 0) continue;
44
45 llong adi = (p == fsi ? se : fs);
46 llong sbi = (p == sb[0].second ? sb[1].first : sb[0].first);
47 llong adj = dist[p] + etc[p];
48 llong sbj = dist[p] - etc[p];
49
50 lsb = max(lsb, adi - sbj - m + c);
51 gsb = min(gsb, sbi - adj + m - c);
52 lad = max(lad, adi + adj - m + c);
53 gad = min(gad, sbi + sbj + m - c);
54 }
55
56 llong gpos, lpos;
57 p = 0, q = n - 1;
58 for (int i = 0; i < n; ++i)
59 {
60 gpos = min(dist[i] - lsb, gad - dist[i]);
61 lpos = max(dist[i] - gsb, lad - dist[i]);
62
63 while (p > 0 && dist[p - 1] >= lpos) --p;
64 while (p < n && dist[p] < lpos) ++p;
65 while (q >= 0 && dist[q] > gpos) --q;
66 while (q < n - 1 && dist[q + 1] <= gpos) ++q;
67
68 if (p <= q) return true;
69 }
70
71 return false;
72 }
73
74 long long find_shortcut(int N, std::vector<int> l, std::vector<int> d, int C)
75 {
76 n = N; c = C;
77 dist.resize(n);
78 llong sum = 0ll;
79 for (int i = 1; i < n; ++i)
80 {
81 sum += l[i - 1];
82 dist[i] = sum;
83 }
84
85 etc = d;
86 llong ld = 0ll, e = 0ll;
87 int fs = 0, se = 0;
88 for (int i = 0; i < n; ++i)
89 {
90 ad.push_back({ dist[i] + etc[i], i });
91 sb.push_back({ dist[i] - etc[i], i });
92
93 e = max(e, ld + etc[i]);
94
95 if (fs < etc[i])
96 {
97 se = fs;
98 fs = etc[i];
99 }
100 else
101 if (se < etc[i])
102 {
103 se = etc[i];
104 }
105
106 if (i < n - 1)
107 ld = max(ld, (llong)etc[i]) + l[i];
108 }
109
110 sort(ad.begin(), ad.end());
111 sort(sb.begin(), sb.end());
CAPITOLUL 5. IOI 2016 585

112
113 llong s = fs + se;
114 while (s < e)
115 {
116 llong m = (s + e) / 2;
117 if (check(m)) e = m;
118 else s = m + 1ll;
119 }
120
121 return s;
122 }
123
124 // BEGIN CUT
125 int main()
126 {
127 auto t1 = clock();
128
129 std::freopen("../tests/subtask_8/220", "r", stdin);
130 std::freopen("8-220.out.txt", "w", stdout);
131
132 int n, c;
133 scanf("%d%d", &n, &c);
134
135 vector<int> l(n - 1);
136 vector<int> d(n);
137 for (int i = 0; i < n - 1; i++)
138 scanf("%d", &l[i]);
139 for (int i = 0; i < n; i++)
140 scanf("%d", &d[i]);
141
142 auto t2 = clock();
143
144 long long t = find_shortcut(n, l, d, c);
145
146 auto t3 = clock();
147
148 // BEGIN SECRET
149 puts("14e047d7a2907b9034950b074822b302");
150 // END SECRET
151
152 printf("%lld\n", t);
153
154 auto t4 = clock();
155
156 // reset console output
157 freopen("CON", "w", stdout);
158
159 cout<<"n = "<<n<<" c = "<<c<<"\n";
160 cout<<"t = "<<t<<"\n\n";
161
162 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
164 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
165
166 return 0;
167 }
168 // END CUT
169 /*
170 n = 1000000 c = 1000
171 t = 1000857674
172
173 t2-t1 = 3.453
174 t3-t2 = 6.514
175 t4-t3 = 0
176
177 Process returned 0 (0x0) execution time : 10.030 s
178 Press any key to continue.
179 */

Listing 5.3.7: shortcut-94572.cpp


1 // https://oj.uz/submission/94572
2
3 #include <bits/stdc++.h>
4 #include "shortcut.h"
CAPITOLUL 5. IOI 2016 586

5
6 using namespace std;
7
8 const long long inf = 1ll << 60;
9
10 long long find_shortcut(int n, vector<int> l, vector<int> d, int c)
11 {
12 vector<long long> x(n);
13 for (int i = 0; i < n - 1; ++i)
14 {
15 x[i + 1] = x[i] + l[i];
16 }
17
18 auto check = [&](long long limit)
19 {
20 long long lsum = -inf, rsum = inf;
21 long long ldiff = -inf, rdiff = inf;
22 long long max_sum = -inf, min_diff = inf;
23
24 deque<int> q;
25 for (int i = 0; i < n; ++i)
26 {
27 while (!q.empty() && x[i]-x[q.front()]+d[i]+d[q.front()] > limit)
28 {
29 max_sum = max(max_sum, x[q.front()] + d[q.front()]);
30 min_diff = min(min_diff, x[q.front()] - d[q.front()]);
31 q.pop_front();
32 }
33
34 if (max_sum >= 0)
35 {
36 lsum = max(lsum, x[i] + d[i] + max_sum + c - limit);
37 rsum = min(rsum, x[i] - d[i] + min_diff + limit - c);
38 rdiff = min(rdiff, x[i] - d[i] - max_sum + limit - c);
39 ldiff = max(ldiff, x[i] + d[i] - min_diff + c - limit);
40 }
41
42 while (!q.empty() && x[i] - d[i] < x[q.back()] - d[q.back()])
43 {
44 q.pop_back();
45 }
46
47 q.push_back(i);
48 }
49
50 if (lsum > rsum || ldiff > rdiff)
51 {
52 return false;
53 }
54
55 for (int i = 0, j = n, k = 0; i < n; ++i)
56 {
57 while (j && x[j - 1] + x[i] >= lsum)
58 {
59 --j;
60 }
61
62 while (k < n && x[k] - x[i] < ldiff)
63 {
64 ++k;
65 }
66
67 int p = max(i + 1, max(j, k));
68 if (p < n && x[p] + x[i] <= rsum && x[p] - x[i] <= rdiff)
69 {
70 return true;
71 }
72 }
73
74 return false;
75 };
76
77 long long low = 0, high = inf;
78 while (low < high)
79 {
80 long long mid = low + high >> 1;
CAPITOLUL 5. IOI 2016 587

81 if (check(mid))
82 {
83 high = mid;
84 }
85 else
86 {
87 low = mid + 1;
88 }
89 }
90
91 return high;
92 }
93
94 // BEGIN CUT
95 int main()
96 {
97 auto t1 = clock();
98
99 std::freopen("../tests/subtask_8/220", "r", stdin);
100 std::freopen("8-220.out.txt", "w", stdout);
101
102 int n, c;
103 scanf("%d%d", &n, &c);
104
105 vector<int> l(n - 1);
106 vector<int> d(n);
107 for (int i = 0; i < n - 1; i++)
108 scanf("%d", &l[i]);
109 for (int i = 0; i < n; i++)
110 scanf("%d", &d[i]);
111
112 auto t2 = clock();
113
114 long long t = find_shortcut(n, l, d, c);
115
116 auto t3 = clock();
117
118 // BEGIN SECRET
119 puts("14e047d7a2907b9034950b074822b302");
120 // END SECRET
121
122 printf("%lld\n", t);
123
124 auto t4 = clock();
125
126 // reset console output
127 freopen("CON", "w", stdout);
128
129 cout<<"n = "<<n<<" c = "<<c<<"\n";
130 cout<<"t = "<<t<<"\n\n";
131
132 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
133 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
134 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
135
136 return 0;
137 }
138 // END CUT
139 /*
140 n = 1000000 c = 1000
141 t = 1000857674
142
143 t2-t1 = 3.526
144 t3-t2 = 29.047
145 t4-t3 = 0
146
147 Process returned 0 (0x0) execution time : 32.620 s
148 Press any key to continue.
149 */

Listing 5.3.8: shortcut-97033.cpp


1 // https://oj.uz/submission/97033
2
3 #include "shortcut.h"
CAPITOLUL 5. IOI 2016 588

4 #include <bits/stdc++.h>
5
6 #define INFLL (0x3f3f3f3f3f3f3f3fll)
7
8 using namespace std;
9
10 typedef long long ll;
11 inline void upmin(ll &a, ll b) { if(b < a) a = b; }
12 inline void upmax(ll &a, ll b) { if(a < b) a = b; }
13
14 const int MAXN = 1000005;
15
16 ll Mx0[MAXN], Mx1[MAXN];
17 ll X[MAXN], L[MAXN], TP[MAXN], TM[MAXN];
18 int O[MAXN], OJ[MAXN];
19
20 int N; ll K, Ans;
21
22 bool isp(ll Y)
23 {
24 ll xymx = -INFLL, xymn = INFLL, yxmx = -INFLL, yxmn = INFLL, rmn, rmx;
25 for(int oi = 0, oj = 0, j; oj < N; oj++)
26 {
27 j = OJ[oj]; ll t = Y-TP[j];
28 for(; oi < N && t < TM[O[oi]]; oi++);
29 if(!oi || (1 == oi && j == O[0])) continue;
30 rmx = TP[j] == Mx0[oi-1] ? Mx1[oi-1] : Mx0[oi-1];
31 rmn = -(j == O[0] ? TM[O[1]] : TM[O[0]]);
32 ll a = Y-K - TM[j], b = K-Y + TP[j];
33 upmin(xymn, a+rmn); upmax(xymx, b+rmx);
34 upmin(yxmn, a-rmx); upmax(yxmx, b-rmn);
35 if(xymn < xymx || yxmn < yxmx) return false;
36 }
37
38 for(int s = 0, e = 0; s < N; s++)
39 {
40 rmn = max(xymx-X[s], yxmx+X[s]);
41 rmx = min(xymn-X[s], yxmn+X[s]);
42 if(rmx < rmn) continue;
43 for(; e+1 < N && X[e] < rmn; e++);
44 for(; e && rmx < X[e]; e--);
45 if(rmn <= X[e]) return true;
46 }
47
48 return false;
49 }
50
51 ll getAns()
52 {
53 ll mx = -INFLL;
54 for(int i = 0; i < N; i++)
55 {
56 upmax(Ans, X[i]+L[i] + mx);
57 upmax(mx, L[i]-X[i]);
58 TP[i] = X[i]+L[i];
59 TM[i] = L[i]-X[i];
60 }
61
62 iota(O, O+N, 0);
63 sort(O, O+N, [&](int a, int b) {return TM[a] > TM[b];});
64
65 iota(OJ, OJ+N, 0);
66 sort(OJ, OJ+N, [&](int a, int b) {return TP[a] < TP[b];});
67
68 Mx0[0] = Mx1[0] = -INFLL;
69 for(int oi = 0, i; oi < N; oi++)
70 {
71 if(oi)
72 {
73 Mx0[oi] = Mx0[oi-1];
74 Mx1[oi] = Mx1[oi-1];
75 }
76
77 i = O[oi]; ll c = TP[i];
78 if(Mx0[oi] < c) swap(Mx0[oi], c);
79 if(Mx1[oi] < c) swap(Mx1[oi], c);
CAPITOLUL 5. IOI 2016 589

80 }
81
82 ll a = 0, b = 0;
83 for(int i = 0; i < N; i++)
84 {
85 ll c = L[i];
86 if(a < c) swap(a, c);
87 if(b < c) swap(b, c);
88 }
89
90 ll s = a+b, e = Ans;
91 for(ll m; s < e;)
92 {
93 m = (s+e) >> 1;
94 if(isp(m)) e = m;
95 else s = m+1;
96 }
97
98 return s;
99 }
100
101 long long find_shortcut(int n, std::vector<int> l,
102 std::vector<int> d, int c)
103 {
104 ::N = n; ::K = c;
105 for(int i = 1; i < N; i++) ::X[i] = ::X[i-1] + l[i-1];
106 for(int i = 0; i < N; i++) ::L[i] = d[i];
107 return getAns();
108 }
109
110 // BEGIN CUT
111 int main()
112 {
113 auto t1 = clock();
114
115 std::freopen("../tests/subtask_8/220", "r", stdin);
116 std::freopen("8-220.out.txt", "w", stdout);
117
118 int n, c;
119 scanf("%d%d", &n, &c);
120
121 vector<int> l(n - 1);
122 vector<int> d(n);
123 for (int i = 0; i < n - 1; i++)
124 scanf("%d", &l[i]);
125 for (int i = 0; i < n; i++)
126 scanf("%d", &d[i]);
127
128 auto t2 = clock();
129
130 long long t = find_shortcut(n, l, d, c);
131
132 auto t3 = clock();
133
134 // BEGIN SECRET
135 puts("14e047d7a2907b9034950b074822b302");
136 // END SECRET
137
138 printf("%lld\n", t);
139
140 auto t4 = clock();
141
142 // reset console output
143 freopen("CON", "w", stdout);
144
145 cout<<"n = "<<n<<" c = "<<c<<"\n";
146 cout<<"t = "<<t<<"\n\n";
147
148 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
149 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
150 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
151
152 return 0;
153 }
154 // END CUT
155 /*
CAPITOLUL 5. IOI 2016 590

156 n = 1000000 c = 1000


157 t = 1000857674
158
159 t2-t1 = 3.531
160 t3-t2 = 3.265
161 t4-t3 = 0
162
163 Process returned 0 (0x0) execution time : 6.828 s
164 Press any key to continue.
165 */

Listing 5.3.9: shortcut-99074.cpp


1 // https://oj.uz/submission/99074
2
3 #include <bits/stdc++.h>
4 #include "shortcut.h"
5
6 #define enl printf("\n")
7 #define case(t) printf("Case #%d: ", (t))
8 #define ni(n) scanf("%d", &(n))
9 #define nl(n) scanf("%I64d", &(n))
10 #define nai(a, n) for (int i = 0; i < (n); i++) ni(a[i])
11 #define nal(a, n) for (int i = 0; i < (n); i++) nl(a[i])
12 #define pri(n) printf("%d\n", (n))
13 #define prl(n) printf("%I64d\n", (n))
14 #define pii pair<int, int>
15 #define pll pair<long long, long long>
16 #define vii vector<pii>
17 #define vi vector<int>
18 #define pb push_back
19 #define mp make_pair
20 #define fi first
21 #define se second
22
23 using namespace std;
24
25 typedef long long ll;
26
27 const double pi = acos(-1);
28 const int MOD = 1e9 + 7;
29 const ll INF = 1e17 + 7;
30 const int MAXN = 1e6 + 5;
31 const double eps = 1e-9;
32 ll x[MAXN], d[MAXN];
33 pll dif[MAXN];
34
35 /*
36 j>i -> xi-di, xi+di are known
37 z+y<=len+(xj-dj)+(xi-di) -> mxsm
38 z+y>=(xj+dj)+(xi+di)-len -> mism
39 z-y<=len+(xj-dj)-(xi+di) -> mxdi
40 z-y>=(xj+dj)-(xi-di)-len -> midi
41
42 shortest path has to contain shortcut:
43 (xj+dj) - (xi-di) > k -> calc with 2 pointers
44 */
45
46 bool solve(ll len, int n, ll c)
47 {
48 ll mxsm = INF, mism = -INF, mxdi = INF, midi = -INF;
49 ll di = INF, sm = -INF;
50
51 /*
52 pll lef = {INF, -INF};
53 pll rig = {-INF, -INF};
54 */
55
56 int l = 0, r = 0;
57 bool skip = false;
58
59 // calculate bounds
60 for (int i = 0; i < n; i++)
61 {
62 while (l < r && x[i] + d[i] - dif[l].fi > len)
CAPITOLUL 5. IOI 2016 591

63 {
64 int ind = dif[l++].se; // add element to set of elements
65 // producing the intersection
66
67 if (x[ind] - d[ind] < di)//lef.fi - lef.se)
68 di = x[ind] - d[ind];//lef = {x[ind], d[ind]};
69
70 if (x[ind] + d[ind] > sm)//rig.fi + rig.se)
71 sm = x[ind] + d[ind];//rig = {x[ind], d[ind]};
72 skip = true;
73 }
74
75 if (skip)
76 {
77 mxsm = min(mxsm, di + x[i] - d[i] + len - c);
78 mism = max(mism, sm + x[i] + d[i] - len + c);
79 mxdi = min(mxdi, x[i] - d[i] - sm + len - c);
80 midi = max(midi, x[i] + d[i] - di - len + c);
81 }
82
83 // current station dominates last used station -> remove
84 // 1. cur elem (i) dominates prev (j) -> xi-di < xj-dj
85 // 2. prev elem (i) dominates cur (j) -> xi+di > xj+dj
86 // dominating elements have extremal sm and di -> inc di, dec sm
87 while (l < r && dif[r - 1].fi > x[i] - d[i])
88 r--;
89 dif[r++] = {x[i] - d[i], i};
90 }
91
92 if (mxsm < mism || mxdi < midi)
93 return false;
94
95 // check if there is a shortcut satisfying this bounds
96 int curdi = 0, cursm = n;
97 for (int i = 0; i < n; i++)
98 {
99 while (curdi < n && x[curdi] - x[i] < midi) // violates 4.
100 curdi++;
101
102 // all indices >= curdi satisfy 4.
103 while (cursm > 0 && x[cursm - 1] + x[i] >= mism) // violates 2.
104 cursm--;
105
106 // all indices >= cursm satisfy 2.
107 int cur = max(max(cursm, curdi), i + 1); // shortcut with
108 // next element / all other combinations are checked
109 if (cur < n && x[cur] + x[i] <= mxsm && x[cur] - x[i] <= mxdi)
110 return true;
111 }
112
113 return false;
114 }
115
116 ll find_shortcut(int n, vi l, vi d2, int c)
117 {
118 for (int i = 0; i < n; i++)
119 {
120 d[i] = d2[i];
121 if (i == 0)
122 x[i] = 0;
123 else
124 x[i] = x[i - 1] + l[i - 1];
125 }
126
127 ll lo = 0, hi = INF, ret = -1;
128 while (lo <= hi)
129 {
130 ll mi = (lo + hi) / 2;
131 if (solve(mi, n, c))
132 hi = mi - 1, ret = mi;
133 else
134 lo = mi + 1;
135 }
136
137 return ret;
138 }
CAPITOLUL 5. IOI 2016 592

139
140 // BEGIN CUT
141 int main()
142 {
143 auto t1 = clock();
144
145 std::freopen("../tests/subtask_8/220", "r", stdin);
146 std::freopen("8-220.out.txt", "w", stdout);
147
148 int n, c;
149 scanf("%d%d", &n, &c);
150
151 vector<int> l(n - 1);
152 vector<int> d(n);
153 for (int i = 0; i < n - 1; i++)
154 scanf("%d", &l[i]);
155 for (int i = 0; i < n; i++)
156 scanf("%d", &d[i]);
157
158 auto t2 = clock();
159
160 long long t = find_shortcut(n, l, d, c);
161
162 auto t3 = clock();
163
164 // BEGIN SECRET
165 puts("14e047d7a2907b9034950b074822b302");
166 // END SECRET
167
168 printf("%lld\n", t);
169
170 auto t4 = clock();
171
172 // reset console output
173 freopen("CON", "w", stdout);
174
175 cout<<"n = "<<n<<" c = "<<c<<"\n";
176 cout<<"t = "<<t<<"\n\n";
177
178 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
179 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
180 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
181
182 return 0;
183 }
184 // END CUT
185 /*
186 n = 1000000 c = 1000
187 t = 1000857674
188
189 t2-t1 = 3.507
190 t3-t2 = 6.25
191 t4-t3 = 0
192
193 Process returned 0 (0x0) execution time : 9.805 s
194 Press any key to continue.
195 */

Listing 5.3.10: shortcut-99075.cpp


1 // https://oj.uz/submission/99075
2
3 #pragma GCC optimize("O3")
4
5 #include <bits/stdc++.h>
6 #include "shortcut.h"
7
8 #define pii pair<int, int>
9 #define pll pair<long long, long long>
10 #define vii vector<pii>
11 #define vi vector<int>
12 #define pb push_back
13 #define mp make_pair
14 #define fi first
15 #define se second
CAPITOLUL 5. IOI 2016 593

16
17 using namespace std;
18
19 typedef long long ll;
20
21 const ll INF = 1e17 + 7;
22 const int MAXN = 1e6 + 5;
23
24 ll x[MAXN], d[MAXN];
25 int dif[MAXN];
26
27 /*
28 j>i -> xi-di, xi+di are known
29 z+y<=len+(xj-dj)+(xi-di) -> mxsm
30 z+y>=(xj+dj)+(xi+di)-len -> mism
31 z-y<=len+(xj-dj)-(xi+di) -> mxdi
32 z-y>=(xj+dj)-(xi-di)-len -> midi
33
34 shortest path has to contain shortcut:
35 (xj+dj) - (xi-di) > k -> calc with 2 pointers
36 */
37
38 bool solve(ll len, int n, ll c)
39 {
40 ll mxsm = INF, mism = -INF, mxdi = INF, midi = -INF;
41 ll di = INF, sm = -INF;
42 int l = 0, r = 0;
43 bool skip = false;
44
45 // calculate bounds
46 for (int i = 0; i < n; i++)
47 {
48 while (l < r && x[i] + d[i] - (x[dif[l]] - d[dif[l]]) > len)
49 {
50 int ind = dif[l++]; // add element to set of elements
51 // producing the intersection
52 if (x[ind] - d[ind] < di)
53 di = x[ind] - d[ind];
54 if (x[ind] + d[ind] > sm)
55 sm = x[ind] + d[ind];
56 skip = true;
57 }
58
59 if (skip)
60 {
61 mxsm = min(mxsm, di + x[i] - d[i] + len - c);
62 mism = max(mism, sm + x[i] + d[i] - len + c);
63 mxdi = min(mxdi, x[i] - d[i] - sm + len - c);
64 midi = max(midi, x[i] + d[i] - di - len + c);
65 }
66
67 // current station dominates last used station -> remove
68 // 1. cur elem (i) dominates prev (j) -> xi-di < xj-dj
69 // 2. prev elem (i) dominates cur (j) -> xi+di > xj+dj
70 // dominating elements have extremal sm and di -> inc di, dec sm
71
72 while (l < r && x[dif[r - 1]] - d[dif[r - 1]] > x[i] - d[i])
73 r--;
74 dif[r++] = i;
75 }
76
77 if (mxsm < mism || mxdi < midi)
78 return false;
79
80 // check if there is a shortcut satisfying this bounds
81 int curdi = 0, cursm = n;
82 for (int i = 0; i < n; i++)
83 {
84 while (curdi < n && x[curdi] - x[i] < midi) // violates 4.
85 curdi++;
86 // all indices >= curdi satisfy 4.
87 while (cursm > 0 && x[cursm - 1] + x[i] >= mism) // violates 2.
88 cursm--;
89 // all indices >= cursm satisfy 2.
90 int cur = max(max(cursm, curdi), i + 1); // shortcut with
91 // next element / all other combinations are checked
CAPITOLUL 5. IOI 2016 594

92 if (cur < n && x[cur] + x[i] <= mxsm && x[cur] - x[i] <= mxdi)
93 return true;
94 }
95
96 return false;
97 }
98
99 ll find_shortcut(int n, vi l, vi d2, int c)
100 {
101 for (int i = 0; i < n; i++)
102 {
103 d[i] = d2[i];
104 if (i == 0)
105 x[i] = 0;
106 else
107 x[i] = x[i - 1] + l[i - 1];
108 }
109
110 ll lo = 0, hi = INF, ret = -1;
111 while (lo <= hi)
112 {
113 ll mi = (lo + hi) / 2;
114 if (solve(mi, n, c))
115 hi = mi - 1, ret = mi;
116 else
117 lo = mi + 1;
118 }
119
120 return ret;
121 }
122
123 // BEGIN CUT
124 int main()
125 {
126 auto t1 = clock();
127
128 std::freopen("../tests/subtask_8/220", "r", stdin);
129 std::freopen("8-220.out.txt", "w", stdout);
130
131 int n, c;
132 scanf("%d%d", &n, &c);
133
134 vector<int> l(n - 1);
135 vector<int> d(n);
136 for (int i = 0; i < n - 1; i++)
137 scanf("%d", &l[i]);
138 for (int i = 0; i < n; i++)
139 scanf("%d", &d[i]);
140
141 auto t2 = clock();
142
143 long long t = find_shortcut(n, l, d, c);
144
145 auto t3 = clock();
146
147 // BEGIN SECRET
148 puts("14e047d7a2907b9034950b074822b302");
149 // END SECRET
150
151 printf("%lld\n", t);
152
153 auto t4 = clock();
154
155 // reset console output
156 freopen("CON", "w", stdout);
157
158 cout<<"n = "<<n<<" c = "<<c<<"\n";
159 cout<<"t = "<<t<<"\n\n";
160
161 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
162 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
164
165 return 0;
166 }
167 // END CUT
CAPITOLUL 5. IOI 2016 595

168 /*
169 n = 1000000 c = 1000
170 t = 1000857674
171
172 t2-t1 = 3.886
173 t3-t2 = 2.516
174 t4-t3 = 0
175
176 Process returned 0 (0x0) execution time : 6.449 s
177 Press any key to continue.
178 */

Listing 5.3.11: shortcut-113364.cpp


1 // https://oj.uz/submission/113364
2
3 #include "shortcut.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 #define ll long long
9 const int N=1000050;
10 const ll inf=5e18;
11
12 int n,d[N],c,q[N],ql,qr;
13 ll x[N];
14
15 bool Check(ll k)
16 {
17 ll mx=-inf,mn=inf;
18 ll ls=-inf,rs=inf,ld=-inf,rd=inf;
19 ql=1;qr=0;
20 for(int i=1;i<=n;i++)
21 {
22 for(;ql<=qr && x[q[ql]]-d[q[ql]]<x[i]+d[i]-k;ql++)
23 mx=max(mx,x[q[ql]]+d[q[ql]]),mn=min(mn,x[q[ql]]-d[q[ql]]);
24 ls=max(ls,mx+x[i]+d[i]-k+c);
25 rs=min(rs,mn+x[i]-d[i]+k-c);
26 ld=max(ld,mx-x[i]+d[i]-k+c);
27 rd=min(rd,mn-x[i]-d[i]+k-c);
28 for(;ql<=qr && x[q[qr]]-d[q[qr]]>=x[i]-d[i];qr--);
29 q[++qr]=i;
30 }
31
32 int l1=n+1,r1=n+1,l2=0,r2=0;
33 for(int i=1;i<=n;i++)
34 {
35 for(;l1>=2 && x[i]+x[l1-1]>=ls;l1--);
36 for(;r1>=1 && x[i]+x[r1]>rs;r1--);
37 for(;l2<=n && x[i]-x[l2]>rd;l2++);
38 for(;r2<n && x[i]-x[r2+1]>=ld;r2++);
39 int l=max(l1,l2),r=min(r1,r2);
40 if(l<=r) return 1;
41 }
42
43 return 0;
44 }
45
46 ll find_shortcut(int n, vector<int> l, vector<int> d, int c)
47 {
48 ::n=n;
49 ::c=c;
50 ::d[1]=d[0];
51 int mxd=d[0];
52 x[n+1]=inf;
53 for(int i=2;i<=n;i++)
54 x[i]=x[i-1]+l[i-2],::d[i]=d[i-1],mxd=max(mxd,d[i-1]);
55
56 ll lo=0,hi=2*mxd+x[n],mi=lo+hi+1>>1;
57 for(;lo<hi;mi=lo+hi+1>>1)
58 {
59 if(Check(mi)) hi=mi-1;
60 else lo=mi;
61 }
CAPITOLUL 5. IOI 2016 596

62
63 return lo+1;
64 }
65
66 // BEGIN CUT
67 int main()
68 {
69 auto t1 = clock();
70
71 std::freopen("../tests/subtask_8/220", "r", stdin);
72 std::freopen("8-220.out.txt", "w", stdout);
73
74 int n, c;
75 scanf("%d%d", &n, &c);
76
77 vector<int> l(n - 1);
78 vector<int> d(n);
79 for (int i = 0; i < n - 1; i++)
80 scanf("%d", &l[i]);
81 for (int i = 0; i < n; i++)
82 scanf("%d", &d[i]);
83
84 auto t2 = clock();
85
86 long long t = find_shortcut(n, l, d, c);
87
88 auto t3 = clock();
89
90 // BEGIN SECRET
91 puts("14e047d7a2907b9034950b074822b302");
92 // END SECRET
93
94 printf("%lld\n", t);
95
96 auto t4 = clock();
97
98 // reset console output
99 freopen("CON", "w", stdout);
100
101 cout<<"n = "<<n<<" c = "<<c<<"\n";
102 cout<<"t = "<<t<<"\n\n";
103
104 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
105 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
106 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
107
108 return 0;
109 }
110 // END CUT
111 /*
112 n = 1000000 c = 1000
113 t = 1000857674
114
115 t2-t1 = 3.507
116 t3-t2 = 5.25
117 t4-t3 = 0
118
119 Process returned 0 (0x0) execution time : 8.804 s
120 Press any key to continue.
121 */

Listing 5.3.12: shortcut-114144.cpp


1 // https://oj.uz/submission/114144
2
3 #pragma GCC optimize("Ofast")
4 #include <bits/stdc++.h>
5
6 #define pb push_back
7 #define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
8 #define F first
9 #define S second
10 #define ET cout << "\n"
11 #define MP make_pair
12 #define MEM(i,j) memset(i,j,sizeof i)
CAPITOLUL 5. IOI 2016 597

13 #define ALL(v) v.begin(),v.end()


14 #define DB(a,s,e) {for(int i=s;i<e;++i) cout << a[i] << " ";ET;}
15
16 using namespace std;
17
18 typedef long long ll;
19 typedef pair<int,int> pii;
20 typedef pair<ll,ll> pll;
21
22 const ll INF=1e18;
23
24 /*
25 j<i
26 si-sj+di+dj>k
27 si+di-k>sj-dj
28
29 x-y<0
30 |sj-x|+|si-y|+di+dj+c<=k
31 x+y>=(sj+dj)+(si+di)-k+c ->b0
32 x-y>=(sj+dj)-(si-di)-k+c ->b1
33 x-y<=(sj-dj)-(si+di)+k-c ->b2
34 x+y<=(sj-dj)+(si-di)+k-c ->b3
35
36 y>=b0-x
37 y>=x-b2
38 y<=x-b1
39 y<=b3-x
40 y<=sn
41
42 */
43
44 ll find_shortcut(int n,vector<int> l,vector<int> d, int c)
45 {
46 ll mx=-INF,L=1,R=0;
47 vector<ll> s(n,0),dp(n,0),q(n,0);
48 for(int i=1;i<n;++i)
49 s[i]=s[i-1]+l[i-1];
50 for(int i=0;i<n;++i)
51 dp[i]=max((ll)d[i],s[i]+mx),mx=max(mx,dp[i]-s[i]);
52 for(int i=1;i<n;++i)
53 R=max(R,dp[i-1]+d[i]+s[i]-s[i-1]);
54 while(L<R)
55 {
56 ll mid=L+R>>1,mxt=-INF,mit=INF,b0=-INF,b1=-INF,
57 b2=-1,b3=INF,flag=0,lf=0,rg=-1;
58 for(int i=0,j=0;i<n;++i)
59 {
60 while(rg>=lf&&s[i]+d[i]-s[q[lf]]+d[q[lf]]>mid)
61 mxt=max(mxt,s[q[lf]]+d[q[lf]]),
62 mit=min(mit,s[q[lf]]-d[q[lf]]),lf++;
63
64 b0=max(b0,s[i]+d[i]+mxt-mid+c),
65 b1=max(b1,mxt-s[i]+d[i]-mid+c);
66
67 b2=min(b2,mit-s[i]-d[i]+mid-c),
68 b3=min(b3,mit+s[i]-d[i]+mid-c);
69
70 while(rg>=lf&&s[q[rg]]-d[q[rg]]>s[i]-d[i]) --rg;
71 q[++rg]=i;
72 }
73
74 for(int i=0,j=n,k=0;i+1<n&&!flag;++i)
75 {
76 ll up=min({s[n-1],s[i]-b1,b3-s[i]}),dn=max(b0-s[i],s[i]-b2);
77 while(j>0&&s[j-1]>=b0-s[i]) --j;
78 while(k<n&&s[k]<s[i]-b2) ++k;
79 if(max(j,k)!=n&&s[max(j,k)]<=up) flag=1;
80 }
81
82 if(flag) R=mid;
83 else L=mid+1;
84 }
85
86 return L;
87 }
88
CAPITOLUL 5. IOI 2016 598

89 // BEGIN CUT
90 int main()
91 {
92 auto t1 = clock();
93
94 std::freopen("../tests/subtask_8/220", "r", stdin);
95 std::freopen("8-220.out.txt", "w", stdout);
96
97 int n, c;
98 scanf("%d%d", &n, &c);
99
100 vector<int> l(n - 1);
101 vector<int> d(n);
102 for (int i = 0; i < n - 1; i++)
103 scanf("%d", &l[i]);
104 for (int i = 0; i < n; i++)
105 scanf("%d", &d[i]);
106
107 auto t2 = clock();
108
109 long long t = find_shortcut(n, l, d, c);
110
111 auto t3 = clock();
112
113 // BEGIN SECRET
114 puts("14e047d7a2907b9034950b074822b302");
115 // END SECRET
116
117 printf("%lld\n", t);
118
119 auto t4 = clock();
120
121 // reset console output
122 freopen("CON", "w", stdout);
123
124 cout<<"n = "<<n<<" c = "<<c<<"\n";
125 cout<<"t = "<<t<<"\n\n";
126
127 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
128 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
129 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
130
131 return 0;
132 }
133 // END CUT
134 /*
135 n = 1000000 c = 1000
136 t = 1000857674
137
138 t2-t1 = 3.64
139 t3-t2 = 4.953
140 t4-t3 = 0
141
142 Process returned 0 (0x0) execution time : 8.625 s
143 Press any key to continue.
144 */

Listing 5.3.13: shortcut-142952.cpp


1 // https://oj.uz/submission/142952
2
3 #include "shortcut.h"
4 #include <iostream>
5
6 #include<ctime>
7 #include<fstream>
8
9 using namespace std;
10
11 const int nmax=1000*1000+5;
12 const long long inf=1LL*1e18;
13
14 int d[nmax];
15 long long x[nmax],y[nmax];
16 int i,n;
CAPITOLUL 5. IOI 2016 599

17 long long C;
18
19 bool check(long long k)
20 {
21 long long mxsum=-inf,mndif=inf;
22 long long suml=-inf,sumr=inf,difl=-inf,difr=inf;
23 long long sum,dif;
24 int p=1,u=0;
25 for(i=1;i<=n;i++)
26 {
27 while(p<=u&&x[i]-x[d[p]]+y[i]+y[d[p]]>k)
28 {
29 sum=x[d[p]]+y[d[p]];dif=x[d[p]]-y[d[p]];
30 if(sum>mxsum) mxsum=sum;
31 if(dif<mndif) mndif=dif;
32 p++;
33 }
34 if(mxsum!=-inf)
35 {
36 suml=max(suml,-k+C+mxsum+x[i]+y[i]);
37 sumr=min(sumr,k-C+mndif+x[i]-y[i]);
38 difl=max(difl,-k+C-mndif+x[i]+y[i]);
39 difr=min(difr,k-C-mxsum+x[i]-y[i]);
40 }
41 while(p<=u&&x[i]-y[i]<x[d[u]]-y[d[u]])
42 u--;
43 d[++u]=i;
44 }
45
46 if(suml>sumr||difl>difr) return 0;
47 int p1=1,p2=n;
48 int poz;
49 for(i=1;i<=n;i++)
50 {
51 while(p1<=n&&x[p1]-x[i]<difl)
52 p1++;
53 while(p2>=1&&x[p2]+x[i]>=suml)
54 p2--;
55 poz=max(i+1,max(p1,p2+1));
56 if(poz<=n&&
57 x[poz]+x[i]>=suml&&
58 x[poz]+x[i]<=sumr&&
59 x[poz]-x[i]>=difl&&
60 x[poz]-x[i]<=difr)
61 return 1;
62 }
63 return 0;
64 }
65
66 long long find_shortcut(int N, std::vector<int> l,
67 std::vector<int> d, int c)
68 {
69 C=c;n=N;
70 for(i=0;i<n-1;i++)
71 x[i+2]=x[i+1]+l[i];
72 for(i=0;i<n;i++)
73 y[i+1]=d[i];
74
75 long long ans=0;
76 for(long long p=50;p>=0;p--)
77 ans+=(1LL<<p);
78 for(long long p=50;p>=0;p--)
79 if(check(ans-(1LL<<p)))
80 ans-=(1LL<<p);
81
82 return ans;
83 }
84
85 // BEGIN CUT
86 int main()
87 {
88 auto t1 = clock();
89
90 std::freopen("../tests/subtask_8/220", "r", stdin);
91 std::freopen("8-220.out.txt", "w", stdout);
92
CAPITOLUL 5. IOI 2016 600

93 int n, c;
94 scanf("%d%d", &n, &c);
95
96 vector<int> l(n - 1);
97 vector<int> d(n);
98 for (int i = 0; i < n - 1; i++)
99 scanf("%d", &l[i]);
100 for (int i = 0; i < n; i++)
101 scanf("%d", &d[i]);
102
103 auto t2 = clock();
104
105 long long t = find_shortcut(n, l, d, c);
106
107 auto t3 = clock();
108
109 // BEGIN SECRET
110 puts("14e047d7a2907b9034950b074822b302");
111 // END SECRET
112
113 printf("%lld\n", t);
114
115 auto t4 = clock();
116
117 // reset console output
118 freopen("CON", "w", stdout);
119
120 cout<<"n = "<<n<<" c = "<<c<<"\n";
121 cout<<"t = "<<t<<"\n\n";
122
123 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
124 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
125 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
126
127 return 0;
128 }
129 // END CUT
130 /*
131 n = 1000000 c = 1000
132 t = 1000857674
133
134 t2-t1 = 3.562
135 t3-t2 = 4.498
136 t4-t3 = 0
137
138 Process returned 0 (0x0) execution time : 8.123 s
139 Press any key to continue.
140 */

Listing 5.3.14: shortcut-162764.cpp


1 // https://oj.uz/submission/162764
2
3 #include "shortcut.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 using lint = long long;
9 const lint INF = 2e15;
10
11 struct SumDif
12 {
13 lint sum, dif;
14 lint X, D;
15 int id;
16 };
17
18 int N, C;
19
20 SumDif SUM[1000000]; // increasing X[i] + D[i]
21 SumDif DIF[1000000]; // increasing X[i] - D[i]
22
23 lint X[1000000]; // location of main stations
24
CAPITOLUL 5. IOI 2016 601

25 bool is_valid(lint T)
26 {
27 lint max_sum = +INF; // maximum of S1 + S2 that is valid
28 lint min_sum = -INF; // minimum of S1 + S2 that is valid
29 lint max_dif = +INF; // maximum of S1 - S2 that is valid
30 lint min_dif = -INF; // minimum of S1 - S2 that is valid
31
32 lint sum_big = -INF; // maximum X[i] + D[i], for all valid i
33 lint sum_sml = -INF; // second maximum X[i] + D[i], for all valid i
34 int sum_idx = -1; // index of sum_big
35
36 lint dif_sml = +INF; // minimum X[i] - D[i], for all valid i
37 lint dif_big = +INF; // second minimum X[i] - D[i], for all valid i
38 int dif_idx = -1; // index of dif_sml
39
40 for (int j = 0, pos = 0; j < N; j++)
41 {
42 while (pos < N && SUM[j].sum - DIF[pos].dif > T)
43 {
44 lint sum_val = DIF[pos].sum, dif_val = DIF[pos].dif;
45
46 if (sum_big < sum_val)
47 {
48 sum_sml = sum_big;
49 sum_big = sum_val;
50 sum_idx = DIF[pos].id;
51 }
52 else
53 if (sum_sml < sum_val)
54 {
55 sum_sml = sum_val;
56 }
57
58 if (dif_sml > dif_val)
59 {
60 dif_big = dif_sml;
61 dif_sml = dif_val;
62 dif_idx = DIF[pos].id;
63 }
64 else
65 if (dif_big > dif_val)
66 {
67 dif_big = dif_val;
68 }
69
70 pos++;
71 }
72
73 lint valid_sum = ((SUM[j].id != sum_idx)? sum_big : sum_sml);
74 lint valid_dif = ((SUM[j].id != dif_idx)? dif_sml : dif_big);
75 lint sum_val = SUM[j].sum, dif_val = SUM[j].dif;
76
77 max_sum = min(max_sum, + T + valid_dif + dif_val - C);
78 min_sum = max(min_sum, - T + valid_sum + sum_val + C);
79 max_dif = min(max_dif, + T + valid_dif - sum_val - C);
80 min_dif = max(min_dif, - T + valid_sum - dif_val + C);
81 }
82
83 if (min_sum > max_sum || min_dif > max_dif)
84 return false;
85
86 for (int i = 0, ptl = 0, ptr = N - 1, pos; i < N; i++)
87 {
88 while (ptl < N && X[ptl] - X[i] < min_dif) ptl++;
89 while (ptr >= 0 && X[ptr] + X[i] >= min_sum) ptr--;
90
91 pos = max(ptl, ptr + 1);
92 if (pos < N &&
93 min_sum <= X[pos] + X[i] &&
94 X[pos] + X[i] <= max_sum &&
95 min_dif <= X[pos] - X[i] &&
96 X[pos] - X[i] <= max_dif)
97 return true;
98 }
99
100 return false;
CAPITOLUL 5. IOI 2016 602

101 }
102
103 lint find_shortcut(int n_, vector<int> l_, vector<int> D, int c_)
104 {
105 N = n_, C = c_;
106 for (int i = 0; i < l_.size(); i++)
107 X[i + 1] = X[i] + l_[i];
108 for (int i = 0; i < N; i++)
109 {
110 SUM[i] = SumDif{X[i] + D[i], X[i] - D[i], X[i], D[i], i};
111 DIF[i] = SumDif{X[i] + D[i], X[i] - D[i], X[i], D[i], i};
112 }
113
114 sort(SUM, SUM + N, [&](SumDif &l, SumDif &r) {return l.sum < r.sum;});
115 sort(DIF, DIF + N, [&](SumDif &l, SumDif &r) {return l.dif < r.dif;});
116
117 lint res = 0;
118
119 for (lint L = 0, R = INF, M = (L + R) / 2; L <= R; M = (L + R) / 2)
120 if (is_valid(M))
121 R = M - 1, res = M;
122 else
123 L = M + 1;
124
125 return res;
126 }
127
128 // BEGIN CUT
129 int main()
130 {
131 auto t1 = clock();
132
133 std::freopen("../tests/subtask_8/220", "r", stdin);
134 std::freopen("8-220.out.txt", "w", stdout);
135
136 int n, c;
137 scanf("%d%d", &n, &c);
138
139 vector<int> l(n - 1);
140 vector<int> d(n);
141 for (int i = 0; i < n - 1; i++)
142 scanf("%d", &l[i]);
143 for (int i = 0; i < n; i++)
144 scanf("%d", &d[i]);
145
146 auto t2 = clock();
147
148 long long t = find_shortcut(n, l, d, c);
149
150 auto t3 = clock();
151
152 // BEGIN SECRET
153 puts("14e047d7a2907b9034950b074822b302");
154 // END SECRET
155
156 printf("%lld\n", t);
157
158 auto t4 = clock();
159
160 // reset console output
161 freopen("CON", "w", stdout);
162
163 cout<<"n = "<<n<<" c = "<<c<<"\n";
164 cout<<"t = "<<t<<"\n\n";
165
166 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
167 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
168 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
169
170 return 0;
171 }
172 // END CUT
173 /*
174 n = 1000000 c = 1000
175 t = 1000857674
176
CAPITOLUL 5. IOI 2016 603

177 t2-t1 = 3.875


178 t3-t2 = 6.455
179 t4-t3 = 0
180
181 Process returned 0 (0x0) execution time : 10.377 s
182 Press any key to continue.
183 */

Listing 5.3.15: shortcut-207049.cpp


1 //https://oj.uz/submission/207049
2
3 #include "shortcut.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 using ll = long long;
9 using pll = pair<ll, ll>;
10
11 #define X first
12 #define Y second
13
14 int n, c;
15 ll d[1000007], x[1000007];
16 vector<pll> VP, VM;
17
18 //x[j] <= min(maxs - x[i], maxd + x[i])
19 //x[j] >= max(mins - x[i], mind + x[i])
20
21 bool find_pair(ll mins, ll maxs, ll mind, ll maxd)
22 {
23 for(int i = 1 ; i < n ; i++)
24 {
25 ll minv = max(mins - x[i], mind + x[i]);
26 ll maxv = min(maxs - x[i], maxd + x[i]);
27 auto it = lower_bound(x + i + 1, x + n + 1, minv);
28 if(it != x + n + 1 && ( *it) <= maxv)
29 return true;
30 }
31 return false;
32 }
33
34 bool check(ll k)
35 {
36 ll mins = 0, maxs = 1e18, mind = 0, maxd = 1e18;
37 ll minis = 1e18, maxis = 0;
38 int ii = 0;
39 for(int jj = 0 ; jj < n ; jj++)
40 {
41 int j = VP[jj].Y;
42 while(ii < n && VM[ii].X + k < VP[jj].X)
43 {
44 int i = VM[ii].Y;
45 minis = min(minis, x[i] + d[i]);
46 maxis = max(maxis, x[i] + d[i]);
47 ii++;
48 }
49
50 if(ii && 2 * d[j] <= k)
51 {
52 mins = max(mins, VP[jj].X + maxis - k + c);
53 maxs = min(maxs, k - c + (x[j] - d[j]) + VM[0].X);
54 mind = max(mind, VP[jj].X - VM[0].X - k + c);
55 maxd = min(maxd, k - c + (x[j] - d[j]) - maxis);
56 }
57 }
58
59 for(int j = 1 ; j <= n ; j++)
60 {
61 if(2 * d[j] > k)
62 {
63 for(int i = 1 ; i < j ; i++)
64 {
65 if(x[i] - d[i] + k < x[j] + d[j])
CAPITOLUL 5. IOI 2016 604

66 {
67 mins = max(mins, x[j] + d[j] + x[i] + d[i] - k + c);
68 maxs = min(maxs, k - c + (x[j] - d[j]) + x[i] - d[i]);
69 mind = max(mind, x[j] + d[j] - (x[i] - d[i]) - k + c);
70 maxd = min(maxd, k - c + (x[j] - d[j]) - (x[i] + d[i]));
71 }
72 }
73 }
74 }
75
76 return find_pair(mins, maxs, mind, maxd);
77 }
78
79 long long find_shortcut(int n, std::vector<int> l, std::vector<int> dd, int c)
80 {
81 d[1] = dd[0];
82 ::n = n;
83 ::c = c;
84 for(int i = 1 ; i < n ; i++)
85 {
86 x[i + 1] = x[i] + l[i - 1];
87 d[i + 1] = dd[i];
88 }
89
90 int max1 = 0, max2 = 0;
91 for(int i = 1 ; i <= n ; i++)
92 {
93 VP.emplace_back(x[i] + d[i], i);
94 VM.emplace_back(x[i] - d[i], i);
95 if(d[i] >= max1)
96 {
97 max2 = max1;
98 max1 = d[i];
99 }
100 else
101 if(d[i] > max2)
102 max2 = d[i];
103 }
104
105 sort(VP.begin(), VP.end());
106 sort(VM.begin(), VM.end());
107
108 ll a = max1 + max2, b = 1e18, mid;
109 while(a < b)
110 {
111 mid = (a + b) / 2;
112 if(check(mid))
113 b = mid;
114 else
115 a = mid + 1;
116 }
117
118 return a;
119 }
120
121 // BEGIN CUT
122 int main()
123 {
124 auto t1 = clock();
125
126 std::freopen("../tests/subtask_8/220", "r", stdin);
127 std::freopen("8-220.out.txt", "w", stdout);
128
129 int n, c;
130 scanf("%d%d", &n, &c);
131
132 vector<int> l(n - 1);
133 vector<int> d(n);
134 for (int i = 0; i < n - 1; i++)
135 scanf("%d", &l[i]);
136 for (int i = 0; i < n; i++)
137 scanf("%d", &d[i]);
138
139 auto t2 = clock();
140
141 long long t = find_shortcut(n, l, d, c);
CAPITOLUL 5. IOI 2016 605

142
143 auto t3 = clock();
144
145 // BEGIN SECRET
146 puts("14e047d7a2907b9034950b074822b302");
147 // END SECRET
148
149 printf("%lld\n", t);
150
151 auto t4 = clock();
152
153 // reset console output
154 freopen("CON", "w", stdout);
155
156 cout<<"n = "<<n<<" c = "<<c<<"\n";
157 cout<<"t = "<<t<<"\n\n";
158
159 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
160 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
161 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
162
163 return 0;
164 }
165 // END CUT
166 /*
167 n = 1000000 c = 1000
168 t = 1000857674
169
170 t2-t1 = 3.525
171 t3-t2 = 10.265
172 t4-t3 = 0
173
174 Process returned 0 (0x0) execution time : 13.853 s
175 Press any key to continue.
176 */

Listing 5.3.16: shortcut-225819.cpp


1 // https://oj.uz/submission/225819
2
3 #include <bits/stdc++.h>
4 #include "shortcut.h"
5
6 #define all(x) x.begin(),x.end()
7 #define PB push_back
8 #define MP make_pair
9 #define pli pair<ll,int>
10 #define pll pair<ll,ll>
11 #define ft first
12 #define sd second
13
14 using namespace std;
15
16 typedef long long ll;
17
18 const int N = 1000100;
19 const ll OO = 1e18;
20
21 pli events[N], o_events[N];
22 pll seg_mx[N], seg_mn[N];
23 ll pf[N], dst[N], c;
24 int n;
25
26 bool ok(ll mx)
27 {
28 bool was = 0;
29 ll u = -1, d = -1, l = -1, r = -1;
30
31 int ptr = 0;
32
33 for (int it = 0; it < n; it++)
34 //for (register int it = 0; it < n; it++)
35 {
36 int i = o_events[it].sd;
37
CAPITOLUL 5. IOI 2016 606

38 while (ptr < n && mx + events[ptr].ft < -pf[i] + dst[i])


39 ptr++;
40
41 pll cur = MP(1, 1);
42
43 if (!(seg_mx[ptr].ft == -OO ||
44 (seg_mx[ptr].ft == (pf[i] + dst[i]) &&
45 seg_mx[ptr].sd == OO)))
46 {
47 cur.ft = (seg_mx[ptr].ft == (pf[i] + dst[i]) ?
48 seg_mx[ptr].sd :
49 seg_mx[ptr].ft);
50 cur.sd = (seg_mn[ptr].ft == (pf[i] - dst[i]) ?
51 seg_mn[ptr].sd :
52 seg_mn[ptr].ft);
53
54 ll cu = cur.sd + pf[i] + (mx - dst[i] - c);
55 ll cd = cur.ft + pf[i] - (mx - dst[i] - c);
56 ll cl = -cur.sd + pf[i] - (mx - dst[i] - c);
57 ll cr = -cur.ft + pf[i] + (mx - dst[i] - c);
58
59 if (!was)
60 {
61 was = 1;
62 u = cu; d = cd; l = cl; r = cr;
63 if (u < d || r < l) return 0;
64 }
65 else
66 {
67 u = min(u, cu);
68 d = max(d, cd);
69 if (u < d) return 0;
70 l = max(l, cl);
71 r = min(r, cr);
72 if (r < l) return 0;
73 }
74 }
75 }
76
77 if (!was) return 1;
78
79 l >>= 1; r >>= 1;
80
81 int l1 = n, r1 = n - 1;
82 int r2 = -1, l2 = 0;
83
84 for (int i = 0; i < n; i++)
85 //for (register int i = 0; i < n; i++)
86 {
87 while (l1 - 1 >= 0 && pf[l1 - 1] + pf[i] >= d)
88 l1--;
89 while (r1 >= 0 && pf[r1] + pf[i] > u)
90 r1--;
91
92 while (r2 < n - 1 && ((pf[i] - pf[r2 + 1]) >> 1) >= l)
93 r2++;
94 while (l2 < n && ((pf[i] - pf[l2]) >> 1) > r)
95 l2++;
96
97 int j1 = max(l1, l2), j2 = min(r1, r2);
98
99 if (j1 > j2) continue;
100
101 if (j1 == j2)
102 {
103 if (j1 != i)
104 return 1;
105 }
106 else return 1;
107 }
108
109 return 0;
110 }
111
112 long long find_shortcut(int N, std::vector<int> l,
113 std::vector<int> d, int C)
CAPITOLUL 5. IOI 2016 607

114 {
115 n = N;
116 c = C * 2;
117
118 ll mex = 0;
119
120 pf[0] = 0;
121
122 for (int i = 0; i < n - 1; i++)
123 //for (register int i = 0; i < n - 1; i++)
124 {
125 pf[i + 1] = pf[i] + l[i] * 2;
126 dst[i] = d[i] * 2;
127 mex = max(mex, dst[i]);
128 }
129
130 dst[n - 1] = d[n - 1] * 2;
131 mex = max(mex, dst[n - 1]);
132
133 for (int i = 0; i < n; i++)
134 //for (register int i = 0; i < n; i++)
135 {
136 events[i] = MP(-pf[i] - dst[i], i);
137 o_events[i]= MP(-pf[i] + dst[i], i);
138 }
139
140 sort(events, events + n);
141 sort(o_events, o_events + n);
142
143 seg_mx[0] = MP(-OO, -OO);
144 seg_mn[0] = MP(OO, OO);
145
146 for (int it = 1; it <= n; it++)
147 //for (register int it = 1; it <= n; it++)
148 {
149 int j = events[it - 1].sd;
150
151 pll nw = MP(pf[j] + dst[j], pf[j] - dst[j]);
152
153 seg_mx[it] = seg_mx[it - 1];
154 seg_mn[it] = seg_mn[it - 1];
155
156 if (seg_mx[it].ft < nw.ft){
157 swap(seg_mx[it].ft, seg_mx[it].sd);
158 seg_mx[it].ft = nw.ft;
159 } else if (seg_mx[it].sd < nw.ft)
160 seg_mx[it].sd = nw.ft;
161
162 if (seg_mn[it].ft > nw.sd){
163 swap(seg_mn[it].ft, seg_mn[it].sd);
164 seg_mn[it].ft = nw.sd;
165 } else if (seg_mn[it].sd > nw.sd)
166 seg_mn[it].sd = nw.sd;
167 }
168
169 // smth smaller
170 ll l1 = 0, r1 = mex + pf[n - 1] / 2;
171
172 while (l1 < r1){
173 ll md = (l1 + r1) >> 1;
174
175 if (ok(md * 2))
176 r1 = md;
177 else l1 = md + 1;
178 }
179
180 ok(16);
181
182 return l1;
183 }
184
185 // BEGIN CUT
186 int main()
187 {
188 auto t1 = clock();
189
CAPITOLUL 5. IOI 2016 608

190 std::freopen("../tests/subtask_8/220", "r", stdin);


191 std::freopen("8-220.out.txt", "w", stdout);
192
193 int n, c;
194 scanf("%d%d", &n, &c);
195
196 vector<int> l(n - 1);
197 vector<int> d(n);
198 for (int i = 0; i < n - 1; i++)
199 scanf("%d", &l[i]);
200 for (int i = 0; i < n; i++)
201 scanf("%d", &d[i]);
202
203 auto t2 = clock();
204
205 long long t = find_shortcut(n, l, d, c);
206
207 auto t3 = clock();
208
209 // BEGIN SECRET
210 puts("14e047d7a2907b9034950b074822b302");
211 // END SECRET
212
213 printf("%lld\n", t);
214
215 auto t4 = clock();
216
217 // reset console output
218 freopen("CON", "w", stdout);
219
220 cout<<"n = "<<n<<" c = "<<c<<"\n";
221 cout<<"t = "<<t<<"\n\n";
222
223 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
224 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
225 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
226
227 return 0;
228 }
229 // END CUT
230 /*
231 n = 1000000 c = 1000
232 t = 1000857674
233
234 t2-t1 = 3.673
235 t3-t2 = 4.234
236 t4-t3 = 0
237
238 Process returned 0 (0x0) execution time : 7.970 s
239 Press any key to continue.
240 */

5.3.3 *Rezolvare detaliat 

5.4 Paint By Numbers


Problema 4 - Paint By Numbers 100 de puncte
Author: Michal Forisek (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
ecare 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
CAPITOLUL 5. IOI 2016 609

trebuie sa e egal cu ci . De exemplu , dac  indiciile sunt c 3, 4, jocul rezolvat trebuie s  aib 
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  e negre ³i anumite celule trebuie s  e 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 ecare i (0 & i & n  1) caracterul i este:


` ’X’, dac  celula trebuie s  e neagr ,
` ’_’, dac  celula trebuie s  e alb ,
` ’.’, dac  nu exist  informaµii despre celula .

a c: ³ir de lungime k conµinând indiciile, a³a cum au fost denite anterior,


a funcµia trebuie s  returneze un string de lungime n. Pentru ecare i (0 & i & n  1) caracterul
i din stringul returnat trebuie s  e:
` ’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  e 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 ³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_’’,
CAPITOLUL 5. IOI 2016 610

’’_XXX__XXXX’’,
’’__XXX_XXXX’’.

Se observ  c  celulele (indexate din 0) cu indicii 2, 6, ³i 7 sunt negre în toate soluµiile.


Toate celelate celule pot  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  e 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

5.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.
Subtask 2: if you really have to, you can brute force the 220 congurations and check whether
they match the clues. Your loss (of time), directly solving subtask C is much simpler.
CAPITOLUL 5. IOI 2016 611

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, nd the leftmost and the rightmost valid position of the corresponding block. If
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 xed 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 prex 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 rst i clues and the rst 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 i there is no given black cell among
the rst 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 suxes 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 rst t clues and the rst 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 ecient 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 dierent 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 rst 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 rst j  1 black blocks from the
second solution and the remaining blocks from the rst solution.
CAPITOLUL 5. IOI 2016 612

This is a valid solution because in the second solution the rst j  1 black blocks are all strictly
to the left of cell x and in the rst 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.

5.4.2 Coduri surs 

Listing 5.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];
CAPITOLUL 5. IOI 2016 613

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 {
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++)
CAPITOLUL 5. IOI 2016 614

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 }
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");
CAPITOLUL 5. IOI 2016 615

215 printf("%s\n", result);


216 */
217
218 return 0;
219 }
220 // END CUT
221 /*
222 t2-t1 = 0.000000
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 5.4.2: paint_iz.cpp


1 #include <iostream>
2 #include <cstdlib>
3 #include <vector>
4 #include <string>
5 #include <cassert>
6 #include <algorithm>
7
8 #include<ctime>
9
10 using namespace std;
11
12 std::string solve_puzzle(std::string S, std::vector<int> C)
13 {
14 int n = S.length();
15 vector<int> B, W;
16 for (int i = 0; i < n; i++)
17 {
18 if (S[i] == ’X’)
19 {
20 B.push_back(i);
21 }
22 if (S[i] == ’_’)
23 {
24 W.push_back(i);
25 }
26 }
27 n += 2;
28 for (int i = 0; i < (int)B.size(); i++) B[i]++;
29 for (int i = 0; i < (int)W.size(); i++) W[i]++;
30
31 int k = C.size();
32
33 vector<char> ans(n, 0);
34
35 vector<vector<vector<int> > >
36 dp(2, vector<vector<int> >(n, vector<int>(k + 1, 0)));
37
38 vector<int> sumB(n), sumW(n);
39
40 for (int it = 0; it < 3; it++)
41 {
42 sumB.assign(n, 0);
43 sumW.assign(n, 0);
44 for (int x : B) sumB[x]++;
45 for (int x : W) sumW[x]++;
46 for (int i = 1; i < n; i++)
CAPITOLUL 5. IOI 2016 616

47 {
48 sumB[i] += sumB[i - 1];
49 sumW[i] += sumW[i - 1];
50 }
51
52 if (it == 2) break;
53
54 dp[it][0][0] = 1;
55 for (int i = 1; i < n; i++)
56 {
57 for (int j = 0; j <= k; j++)
58 {
59 dp[it][i][j] |= dp[it][i-1][j] && (sumB[i]-sumB[i-1] == 0);
60 dp[it][i][j] |= j > 0 &&
61 i > C[j - 1] &&
62 sumW[i - 1] - sumW[i - C[j - 1] - 1] == 0 &&
63 sumB[i] - sumB[i - 1] == 0 &&
64 dp[it][i - C[j - 1] - 1][j - 1];
65 }
66 }
67
68 reverse(C.begin(), C.end());
69 reverse(B.begin(), B.end());
70 reverse(W.begin(), W.end());
71
72 for (int i = 0; i < (int)B.size(); i++) B[i] = n - B[i] - 1;
73 for (int i = 0; i < (int)W.size(); i++) W[i] = n - W[i] - 1;
74 }
75
76 reverse(dp[1].begin(), dp[1].end());
77 for (int i = 1; i < n - 1; i++)
78 {
79 for (int j = 0; j <= k; j++)
80 {
81 if (dp[0][i][j] && dp[1][i][k - j])
82 {
83 ans[i - 1] |= 1;
84 }
85 }
86 }
87
88 vector<int> a(n);
89 for (int i = 1; i < n; i++)
90 {
91 for (int j = 0; j <= k; j++)
92 {
93 if (j > 0 &&
94 i > C[j - 1] &&
95 sumW[i - 1] - sumW[i - C[j - 1] - 1] == 0 &&
96 sumB[i] - sumB[i - 1] == 0 &&
97 dp[0][i - C[j - 1] - 1][j - 1] &&
98 dp[1][i][k - j])
99 {
100 a[i - C[j - 1]]++;
101 a[i]--;
102 }
103 }
104 }
105
106 for (int i = 1; i < n; i++)
107 {
108 a[i] += a[i - 1];
109 if (a[i] > 0)
110 {
111 ans[i - 1] |= 2;
112 }
113 }
114
115 string res = "";
116 for (int i = 0; i < n - 2; i++)
117 {
118 if (ans[i] == 1) res += ’_’;
119 else if (ans[i] == 2) res += ’X’;
120 else if (ans[i] == 3) res += ’?’;
121 else assert(0);
122 }
CAPITOLUL 5. IOI 2016 617

123
124 return res;
125 }
126
127 // BEGIN CUT
128
129 const int S_MAX_LEN = 200 * 1000;
130 char buf[S_MAX_LEN + 1];
131
132 int main()
133 {
134 auto t1=clock();
135
136 //freopen("../tests/subtask_1/001", "r", stdin);
137 //freopen("1-001.out.txt", "w", stdout);
138
139 freopen("../tests/subtask_7/125", "r", stdin);
140 freopen("7-125.out.txt", "w", stdout);
141
142 scanf("%s", buf);
143 std::string s = buf;
144 int c_len;
145 scanf("%d", &c_len);
146 std::vector<int> c(c_len);
147 for (int i = 0; i < c_len; i++) {
148 scanf("%d", &c[i]);
149 }
150
151 auto t2=clock();
152
153 std::string ans = solve_puzzle(s, c);
154
155 auto t3=clock();
156
157 // BEGIN SECRET
158 //puts("098d134608c94f7413faac591054ee35");
159 // END SECRET
160
161 printf("%s\n", ans.data());
162
163 auto t4=clock();
164
165 // reset console output
166 freopen("CON", "w", stdout);
167
168 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
169 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
170 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
171
172 /*
173 printf("%s\n", s);
174 printf("%d ", k);
175 for (int i = 0; i < k; i++)
176 {
177 printf("%d ", c[i]);
178 }
179 printf("\n");
180 printf("%s\n", result);
181 */
182
183 return 0;
184 }
185 // END CUT
186 /*
187 t2-t1 = 0.015000
188 t3-t2 = 6.432000
189 t4-t3 = 0.016000
190
191 Process returned 0 (0x0) execution time : 6.541 s
192 Press any key to continue.
193
194 argc = 4
195 checker
196 ../tests/subtask_7/125
197 ../tests/subtask_7/125.a
198 7-125.out.txt
CAPITOLUL 5. IOI 2016 618

199 ----------------------
200 1
201 Correct
202
203 Process returned 0 (0x0) execution time : 0.078 s
204 Press any key to continue.
205 */

Listing 5.4.3: solve-correct-lc.cpp


1 #include <algorithm>
2 #include <cassert>
3 #include <iostream>
4 #include <vector>
5 #include <string>
6
7 #include<ctime>
8
9 using namespace std;
10
11 vector<int> load_vector()
12 {
13 int size; cin >> size;
14 vector<int> tmp(size);
15 for (int &x : tmp)
16 cin >> x;
17 return tmp;
18 }
19
20 vector< vector<bool> > get_possible_prefixes(const vector<int> &C,
21 const vector<int> &B,
22 const vector<int> &W)
23 {
24 int L = B.size(), N = C.size();
25
26 vector<int> Wsums(L+1,0);
27 for (int l=0; l<L; ++l)
28 Wsums[l+1]=Wsums[l]+W[l];
29
30 vector< vector<bool> > possible( L+1, vector<bool>(N+1, false) );
31
32 possible[0][0] = true;
33 for (int l=1; l<=L; ++l)
34 possible[l][0] = possible[l-1][0] && !B[l-1];
35
36 for (int n=1; n<=N; ++n)
37 {
38 for (int l=1; l<=L; ++l)
39 {
40 // may cell l-1 be white?
41 if (!B[l-1] && possible[l-1][n]) possible[l][n] = true;
42
43 // may cell l-1 be black -- i.e., the end of block n-1?
44 if (!W[l-1])
45 {
46 int len = C[n-1];
47 if (l < len)
48 continue; // doesn’t fit
49 if (Wsums[l] - Wsums[l-len] > 0)
50 continue; // overlaps a forced-white cell
51 if (l > len && B[l-len-1])
52 continue; // touches a forced-black cell on the left end
53 if (possible[max(0,l-len-1)][n-1])
54 possible[l][n] = true;
55 }
56 }
57 }
58
59 return possible;
60 }
61
62 std::string solve_puzzle(std::string S, std::vector<int> C)
63 {
64 int L = S.size();
65 int N = C.size();
CAPITOLUL 5. IOI 2016 619

66
67 vector<int> B(L,0);
68 for (int i = 0; i < L; i++) B[i] = S[i] == ’X’;
69
70 vector<int> W(L,0);
71 for (int i = 0; i < L; i++) W[i] = S[i] == ’_’;
72
73 vector< vector<bool> > can_fit_prefix = get_possible_prefixes(C,B,W);
74
75 reverse( C.begin(), C.end() );
76 reverse( B.begin(), B.end() );
77 reverse( W.begin(), W.end() );
78
79 vector< vector<bool> > can_fit_suffix = get_possible_prefixes(C,B,W);
80
81 reverse( C.begin(), C.end() );
82 reverse( B.begin(), B.end() );
83 reverse( W.begin(), W.end() );
84
85 vector<bool> can_be_white(L,false);
86
87 for (int l=0; l<L; ++l)
88 for (int n=0; n<=N; ++n)
89 if (!B[l] &&
90 can_fit_prefix[l][n] &&
91 can_fit_suffix[L-l-1][N-n])
92 can_be_white[l] = true;
93
94 vector<int> Wsums(L+1,0);
95 for (int l=0; l<L; ++l)
96 Wsums[l+1]=Wsums[l]+W[l];
97
98 vector<int> intervals(L+1,0);
99
100 for (int n=0; n<N; ++n)
101 {
102 for (int start=0; start+C[n]<=L; ++start)
103 {
104 if (start > 0 && B[start-1]) continue;
105 if (start+C[n]<L && B[start+C[n]]) continue;
106 if (Wsums[start+C[n]] > Wsums[start]) continue;
107 if (can_fit_prefix[max(0,start-1)][n] &&
108 can_fit_suffix[max(0,L-start-C[n]-1)][N-n-1])
109 {
110 intervals[start] += 1;
111 intervals[start+C[n]] -= 1;
112 }
113 }
114 }
115
116 vector<bool> can_be_black(L+1,false);
117
118 int inside = 0;
119 for (int l=0; l<L; ++l)
120 {
121 inside += intervals[l];
122 if (inside > 0) can_be_black[l] = true;
123 }
124
125 std::string ans;
126 for (int l=0; l<L; ++l)
127 {
128 if (can_be_black[l] && can_be_white[l]) ans.push_back(’?’);
129 if (can_be_black[l] && !can_be_white[l]) ans.push_back(’X’);
130 if (!can_be_black[l] && can_be_white[l]) ans.push_back(’_’);
131 if (!can_be_black[l] && !can_be_white[l]) ans.push_back(’!’);
132 }
133
134 return ans;
135 }
136
137 const int S_MAX_LEN = 200 * 1000;
138 char buf[S_MAX_LEN + 1];
139
140 // BEGIN CUT
141
CAPITOLUL 5. IOI 2016 620

142 int main()


143 {
144 auto t1=clock();
145
146 //freopen("../tests/subtask_1/001", "r", stdin);
147 //freopen("1-001.out.txt", "w", stdout);
148
149 freopen("../tests/subtask_7/125", "r", stdin);
150 freopen("7-125.out.txt", "w", stdout);
151
152 scanf("%s", buf);
153 std::string s = buf;
154 int c_len;
155 scanf("%d", &c_len);
156 std::vector<int> c(c_len);
157 for (int i = 0; i < c_len; i++) {
158 scanf("%d", &c[i]);
159 }
160
161 auto t2=clock();
162
163 std::string ans = solve_puzzle(s, c);
164
165 auto t3=clock();
166
167 // BEGIN SECRET
168 //puts("098d134608c94f7413faac591054ee35");
169 // END SECRET
170
171 printf("%s\n", ans.data());
172
173 auto t4=clock();
174
175 // reset console output
176 freopen("CON", "w", stdout);
177
178 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
179 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
180 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
181
182 /*
183 printf("%s\n", s);
184 printf("%d ", k);
185 for (int i = 0; i < k; i++)
186 {
187 printf("%d ", c[i]);
188 }
189 printf("\n");
190 printf("%s\n", result);
191 */
192
193 return 0;
194 }
195 /*
196 t2-t1 = 0.015000
197 t3-t2 = 8.406000
198 t4-t3 = 0.016000
199
200 Process returned 0 (0x0) execution time : 8.484 s
201 Press any key to continue.
202
203 argc = 4
204 checker
205 ../tests/subtask_7/125
206 ../tests/subtask_7/125.a
207 7-125.out.txt
208 ----------------------
209 1
210 Correct
211
212 Process returned 0 (0x0) execution time : 0.078 s
213 Press any key to continue.
214 */

Listing 5.4.4: checkerPaintModicat.cpp


CAPITOLUL 5. IOI 2016 621

1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 const string output_secret = "098d134608c94f7413faac591054ee35";
8
9 int main()
10 {
11 int argc=4;
12
13 char* argv[] =
14 {
15 (char*)"checker",
16 (char*)"../tests/subtask_7/125", // input
17 (char*)"../tests/subtask_7/125.a", // rezultat corect
18 (char*)"7-125.out.txt", // rezultat de verificat
19 };
20
21 cout<<"argc = "<<argc<<"\n";
22 for(int kk=0;kk<argc;kk++)
23 cout<<argv[kk]<<"\n";
24 cout<<"----------------------\n";
25
26 registerChecker("paint", argc, argv);
27
28 //readBothSecrets(output_secret);
29 //readBothGraderResults();
30 //compareRemainingLines(3); // incepand cu linia nr 3
31
32 compareRemainingLines(1); // incepand cu linia nr 1
33 }

Listing 5.4.5: paint-65961.cpp


1 // https://oj.uz/submission/65961
2
3 #include "paint.h"
4 #include <cstdio>
5 #include <cstdlib>
6
7 #include<ctime>
8
9 using namespace std;
10
11 int a[200002];
12 bool L[200002][111];
13 bool R[200002][111];
14 int yes[200002], no[200002];
15
16 std::string solve_puzzle(std::string s, std::vector<int> c)
17 {
18 int i, j;
19 int n = s.size(), m = c.size();
20 for (i = 0; i < n; i++) a[i + 1] = a[i] + (s[i] == ’_’);
21 L[0][0] = true;
22 for (i = 1; i <= n; i++) for (j = 0; j <= m; j++)
23 {
24 if (s[i - 1] != ’X’ && L[i - 1][j])
25 {
26 L[i][j] = true;
27 continue;
28 }
29 if (j && i >= c[j - 1] &&
30 a[i] == a[i - c[j - 1]] &&
31 (j == 1 ? L[i - c[0]][0] : i > c[j - 1] &&
32 L[i - c[j - 1] - 1][j - 1] &&
33 s[i - c[j - 1] - 1] != ’X’))
34 L[i][j] = true;
35 }
36
37 R[n + 1][m] = true;
38 for (i = n; i >= 1; i--)
39 for (j = m; j >= 0; j--)
CAPITOLUL 5. IOI 2016 622

40 {
41 if (j < m &&
42 i + c[j] <= n + 1 &&
43 a[i - 1] == a[i + c[j] - 1] &&
44 (j + 1 == m ? R[i + c[j]][j + 1] : i + c[j] < n + 1 &&
45 R[i + c[j] + 1][j + 1] &&
46 s[i + c[j] - 1] != ’X’))
47 {
48 R[i][j] = true;
49 if (j ? i > 1 && L[i-2][j] && s[i-2] != ’X’ : L[i-1][0])
50 {
51 yes[i - 1]++;
52 yes[i + c[j] - 1]--;
53 }
54 continue;
55 }
56 if (s[i - 1] != ’X’ && R[i + 1][j])
57 R[i][j] = true;
58 }
59 std::string res;
60 for (i = 0; i < n; i++)
61 {
62 yes[i + 1] += yes[i];
63 if (s[i] != ’X’)
64 {
65 for (j = 0; j <= m; j++)
66 if (L[i][j] && R[i + 2][j])
67 break;
68 no[i] = j <= m;
69 }
70 res.push_back(yes[i] && no[i] ? ’?’ : yes[i] ? ’X’ : ’_’);
71 }
72
73 return res;
74 }
75
76 // BEGIN CUT
77
78 const int S_MAX_LEN = 200 * 1000;
79 char buf[S_MAX_LEN + 1];
80
81 int main()
82 {
83 auto t1=clock();
84
85 freopen("../tests/subtask_7/125", "r", stdin);
86 freopen("7-125.out.txt", "w", stdout);
87
88 scanf("%s", buf);
89 std::string s = buf;
90 int c_len;
91 scanf("%d", &c_len);
92 std::vector<int> c(c_len);
93 for (int i = 0; i < c_len; i++)
94 {
95 scanf("%d", &c[i]);
96 }
97
98 auto t2=clock();
99
100 std::string ans = solve_puzzle(s, c);
101
102 auto t3=clock();
103
104 printf("%s\n", ans.data());
105
106 auto t4=clock();
107
108 // reset console output
109 freopen("CON", "w", stdout);
110
111 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
112 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
113 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
114
115 return 0;
CAPITOLUL 5. IOI 2016 623

116 }
117 // END CUT
118 /*
119 t2-t1 = 0.031000
120 t3-t2 = 1.594000
121 t4-t3 = 0.015000
122
123 Process returned 0 (0x0) execution time : 1.750 s
124 Press any key to continue.
125 */

Listing 5.4.6: paint-66328.cpp


1 // https://oj.uz/submission/66328
2
3 #include <bits/stdc++.h>
4 #include "paint.h"
5 using namespace std;
6
7 const char anyy = ’.’;
8 const char white = ’_’;
9 const char black = ’X’;
10 const char both = ’?’;
11 const int N = 200010;
12 const int K = 110;
13
14 int wqs[N], bqs[N];
15 bool dp[N][K], pb[N][K], pw[N][K], reach[N][K];
16
17 string solve_puzzle(string s, vector<int> c)
18 {
19 int n = s.size();
20 int k = c.size();
21 s.insert(s.begin(), ’\0’);
22 c.insert(c.begin(), 0);
23 for (int i = 1; i <= n; ++i)
24 {
25 wqs[i] = wqs[i-1];
26 if (s[i] == white)
27 ++wqs[i];
28 }
29
30 dp[n+1][k+1] = true;
31 for (int i = n; i >= 1; --i)
32 {
33 for (int j = k+1; j >= 1; --j)
34 {
35 if (j <= k && (s[i] == black || s[i] == anyy))
36 {
37 bool blackstrip = i+c[j]-1 <= n &&
38 wqs[i+c[j]-1]-wqs[i-1] == 0;
39 bool nextwhite = i+c[j] > n || s[i+c[j]] != black;
40 bool nextdp = dp[min(n+1, i+c[j]+1)][j+1];
41 if (blackstrip && nextwhite && nextdp)
42 {
43 pb[i][j] = true;
44 dp[i][j] = true;
45 }
46 }
47
48 if ((s[i] == white || s[i] == anyy) && dp[i+1][j])
49 {
50 pw[i][j] = true;
51 dp[i][j] = true;
52 }
53 }
54 }
55 for (int i = 1; i <= n; ++i)
56 wqs[i] = 0;
57
58 reach[1][1] = true;
59 for (int i = 1; i <= n+1; ++i)
60 {
61 for (int j = 1; j <= k+1; ++j) if (reach[i][j] && dp[i][j])
62 {
CAPITOLUL 5. IOI 2016 624

63 if (pb[i][j])
64 {
65 bqs[i] += 1;
66 bqs[i+c[j]] -= 1;
67 wqs[i+c[j]] = 1;
68 reach[min(n+1, i+c[j]+1)][j+1] = true;
69 }
70 if (pw[i][j])
71 {
72 wqs[i] = 1;
73 reach[i+1][j] = true;
74 }
75 }
76 }
77
78 string ans(n, ’\0’);
79 for (int i = 1; i <= n; ++i)
80 {
81 bqs[i] += bqs[i-1];
82 if (wqs[i] && bqs[i]) ans[i-1] = both;
83 else
84 if (wqs[i]) ans[i-1] = white;
85 else ans[i-1] = black;
86 }
87
88 return ans;
89 }
90
91 // BEGIN CUT
92
93 const int S_MAX_LEN = 200 * 1000;
94 char buf[S_MAX_LEN + 1];
95
96 int main()
97 {
98 auto t1=clock();
99
100 freopen("../tests/subtask_7/125", "r", stdin);
101 freopen("7-125.out.txt", "w", stdout);
102
103 scanf("%s", buf);
104 std::string s = buf;
105 int c_len;
106 scanf("%d", &c_len);
107 std::vector<int> c(c_len);
108 for (int i = 0; i < c_len; i++)
109 {
110 scanf("%d", &c[i]);
111 }
112
113 auto t2=clock();
114
115 std::string ans = solve_puzzle(s, c);
116
117 auto t3=clock();
118
119 printf("%s\n", ans.data());
120
121 auto t4=clock();
122
123 // reset console output
124 freopen("CON", "w", stdout);
125
126 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
127 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
128 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
129
130 return 0;
131 }
132 // END CUT
133 /*
134 t2-t1 = 0.015000
135 t3-t2 = 1.781000
136 t4-t3 = 0.016000
137
138 Process returned 0 (0x0) execution time : 2.016 s
CAPITOLUL 5. IOI 2016 625

139 Press any key to continue.


140 */

Listing 5.4.7: paint-97040.cpp


1 // https://oj.uz/submission/97040
2
3 #include "paint.h"
4 #include <bits/stdc++.h>
5 #define pb push_back
6 using namespace std;
7
8 const int MAXN = 200055;
9 const int MAXK = 105;
10
11 bitset<MAXK> DA[2][MAXN], DB[2][MAXN];
12 int S[MAXN];
13
14 char A[MAXN];
15 int B[MAXK];
16
17 int N, K;
18
19 void reverse()
20 {
21 reverse(A+1, A+N+1);
22 reverse(B+1, B+K+1);
23 }
24
25 void run(bitset<MAXK> DA[], bitset<MAXK> DB[])
26 {
27 DA[0][0] = true;
28 for(int i = 1; i <= N; i++)
29 {
30 S[i] = S[i-1] + (’_’ == A[i]);
31 for(int j = 0; j <= K; j++)
32 DA[i][j] = ’X’ != A[i] && (DA[i-1][j] || DB[i-1][j]);
33 for(int j = 1; j <= K; j++)
34 DB[i][j] = B[j] <= i && DA[i-B[j]][j-1] && S[i] == S[i-B[j]];
35 }
36 }
37
38 string getAns()
39 {
40 run(DA[0], DB[0]); reverse();
41 run(DA[1], DB[1]); reverse();
42 memset(S, 0, (N+1)<<2);
43 for(int j = 1; j <= K; j++)
44 {
45 for(int s = 1, e = B[j]; e <= N; s++, e++)
46 {
47 if(!DB[0][e][j] || !DB[1][N-s+1][K-j+1]) continue;
48 S[s]++; S[e+1]--;
49 }
50 }
51
52 string ret;
53 for(int i = 1; i <= N; i++)
54 {
55 S[i] += S[i-1];
56 if(!S[i])
57 {
58 ret.pb(’_’);
59 continue;
60 }
61
62 bool t = false;
63 for(int j = 0; j <= K; j++)
64 {
65 if(!DA[0][i][j] || !DA[1][N-i+1][K-j]) continue;
66 t = true;
67 break;
68 }
69 ret.pb(t ? ’?’ : ’X’);
70 }
CAPITOLUL 5. IOI 2016 626

71 return ret;
72 }
73
74 std::string solve_puzzle(std::string s, std::vector<int> c)
75 {
76 ::N = s.length(); ::K = c.size();
77 for(int i = 0; i < ::N; i++) ::A[i+1] = s[i];
78 for(int i = 0; i < ::K; i++) ::B[i+1] = c[i];
79 return getAns();
80 }
81
82 // BEGIN CUT
83
84 const int S_MAX_LEN = 200 * 1000;
85 char buf[S_MAX_LEN + 1];
86
87 int main()
88 {
89 auto t1=clock();
90
91 freopen("../tests/subtask_7/125", "r", stdin);
92 freopen("7-125.out.txt", "w", stdout);
93
94 scanf("%s", buf);
95 std::string s = buf;
96 int c_len;
97 scanf("%d", &c_len);
98 std::vector<int> c(c_len);
99 for (int i = 0; i < c_len; i++)
100 {
101 scanf("%d", &c[i]);
102 }
103
104 auto t2=clock();
105
106 std::string ans = solve_puzzle(s, c);
107
108 auto t3=clock();
109
110 printf("%s\n", ans.data());
111
112 auto t4=clock();
113
114 // reset console output
115 freopen("CON", "w", stdout);
116
117 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
118 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
119 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
120
121 return 0;
122 }
123 // END CUT
124 /*
125 t2-t1 = 0.031000
126 t3-t2 = 11.633000
127 t4-t3 = 0.016000
128
129 Process returned 0 (0x0) execution time : 11.727 s
130 Press any key to continue.
131 */

Listing 5.4.8: paint-105782.cpp


1 // https://oj.uz/submission/105782
2
3 #include <bits/stdc++.h>
4 #include "paint.h"
5 using namespace std;
6
7 const int N = 2e5 + 5;
8 bool f[N][105], g[N][105], can_b[N], can_w[N];
9 int c[105], black[N], white[N], t[N], n, k;
10
11 string solve_puzzle(string s, vector<int> _c)
CAPITOLUL 5. IOI 2016 627

12 {
13
14 n = s.size(), k = _c.size();
15 copy(_c.begin(), _c.end(), c + 1);
16 s = "." + s + ".";
17
18 for(int i = 1; i <= n; ++i)
19 {
20 black[i] = black[i - 1] + (s[i] == ’X’);
21 white[i] = white[i - 1] + (s[i] == ’_’);
22 }
23 white[n + 1] = white[n];
24
25
26 f[0][0] = 1;
27 for(int i = 1; i <= n; ++i)
28 {
29 f[i][0] = black[i] == 0;
30 for(int j = 1; j <= k; ++j)
31 {
32 bool w = f[i - 1][j];
33 bool b = (i >= c[j]) &&
34 (s[i - c[j]] != ’X’) &&
35 (white[i] == white[i - c[j]])&&
36 (i > c[j] ? f[i - c[j] - 1][j - 1] : (j == 1));
37
38 f[i][j] = s[i] == ’_’ ? w : s[i] == ’X’ ? b : (w | b);
39 }
40 }
41
42 g[n + 1][k + 1] = g[n + 2][k + 1] = 1;
43 for(int i = n; i >= 1; --i)
44 {
45 g[i][k + 1] = black[n] == black[i - 1];
46 for(int j = k; j >= 1; --j)
47 {
48 bool w = g[i + 1][j];
49 bool b = (n - i + 1 >= c[j]) &&
50 (s[i + c[j]] != ’X’) &&
51 (white[i - 1] == white[i + c[j] - 1]) &&
52 g[i + c[j] + 1][j + 1];
53
54 g[i][j] = s[i] == ’_’ ? w : s[i] == ’X’ ? b : (w | b);
55 }
56 }
57
58 for(int i = 1; i <= n; ++i)
59 {
60 if(s[i] == ’_’) can_w[i] = 1;
61 else
62 {
63 for(int c = 0; c <= k && !can_w[i]; ++c)
64 {
65 can_w[i] |= f[i - 1][c] && g[i + 1][c + 1];
66 }
67 }
68
69 for(int j = 1; j <= k; ++j)
70 {
71 if(i >= c[j] &&
72 s[i - c[j]] != ’X’ &&
73 s[i + 1] != ’X’ &&
74 white[i] == white[i - c[j]] &&
75 (i > c[j] ? f[i - c[j] - 1][j - 1] : (j == 1)) &&
76 g[i + 2][j + 1])
77 {
78 t[i - c[j] + 1]++; t[i + 1]--;
79 }
80 }
81 }
82
83 for(int i = 1; i <= n; ++i)
84 {
85 t[i] += t[i - 1];
86 can_b[i] = t[i] > 0;
87 }
CAPITOLUL 5. IOI 2016 628

88
89 string ans = "";
90 for(int i = 1; i <= n; ++i)
91 {
92 if(s[i] != ’.’) ans += s[i];
93 else
94 {
95 if(can_w[i] && can_b[i]) ans += ’?’;
96 else ans += (can_w[i] ? ’_’ : ’X’);
97 }
98 }
99
100 return ans;
101 }
102
103 // BEGIN CUT
104
105 const int S_MAX_LEN = 200 * 1000;
106 char buf[S_MAX_LEN + 1];
107
108 int main()
109 {
110 auto t1=clock();
111
112 freopen("../tests/subtask_7/125", "r", stdin);
113 freopen("7-125.out.txt", "w", stdout);
114
115 scanf("%s", buf);
116 std::string s = buf;
117 int c_len;
118 scanf("%d", &c_len);
119 std::vector<int> c(c_len);
120 for (int i = 0; i < c_len; i++)
121 {
122 scanf("%d", &c[i]);
123 }
124
125 auto t2=clock();
126
127 std::string ans = solve_puzzle(s, c);
128
129 auto t3=clock();
130
131 printf("%s\n", ans.data());
132
133 auto t4=clock();
134
135 // reset console output
136 freopen("CON", "w", stdout);
137
138 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
139 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
140 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
141
142 return 0;
143 }
144 // END CUT
145 /*
146 t2-t1 = 0.015000
147 t3-t2 = 2.203000
148 t4-t3 = 0.016000
149
150 Process returned 0 (0x0) execution time : 2.422 s
151 Press any key to continue.
152 */

Listing 5.4.9: paint-107399.cpp


1 // https://oj.uz/submission/107399
2
3 #include "paint.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
CAPITOLUL 5. IOI 2016 629

8 const int N = 2e5+5;


9
10 int n, k, w[N], b[N], sum[N];
11 bool pre[N][105], suf[N][105], black[N], white[N];
12 vector<int> c(1);
13
14 string solve_puzzle(string s, vector<int> _c)
15 {
16 n = s.length(), k = _c.size();
17 s = ’#’ + s + ’#’;
18 for(int i : _c) c.emplace_back(i);
19 for(int i = 1; i <= n; i++)
20 {
21 w[i] += w[i-1] + (s[i] == ’_’);
22 b[i] += b[i-1] + (s[i] == ’X’);
23 }
24
25 pre[0][0] = 1;
26 for(int i = 1; i <= n; i++)
27 {
28 pre[i][0] = (b[i] == 0);
29 for(int j = 1; j <= k; j++)
30 {
31 if(s[i] != ’X’) pre[i][j] |= pre[i-1][j];
32 if(s[i] != ’_’ && i >= c[j] &&
33 s[i-c[j]] != ’X’ && w[i] == w[i-c[j]])
34 {
35 if(j == 1) pre[i][j] |= pre[i-c[j]][j-1];
36 else if(i > c[j]) pre[i][j] |= pre[i-c[j]-1][j-1];
37 }
38 }
39 }
40
41 suf[n+1][k+1] = suf[n+2][k+1] = 1;
42 for(int i = n; i; i--)
43 {
44 suf[i][k+1] = (b[n] == b[i-1]);
45 for(int j = k; j; j--)
46 {
47 if(s[i] != ’X’) suf[i][j] |= suf[i+1][j];
48 if(s[i] != ’_’ && i + c[j] <= n + 1 &&
49 s[i+c[j]] != ’X’ && w[i-1] == w[i+c[j]-1])
50 suf[i][j] |= suf[i+c[j]+1][j+1];
51 }
52 }
53
54 for(int i = 1; i <= n; i++)
55 {
56 if(s[i] == ’_’) white[i] = 1;
57 else
58 for(int j = 0; j <= k; j++)
59 white[i] |= (pre[i-1][j] && suf[i+1][j+1]);
60
61 if(s[i] == ’_’) continue;
62
63 for(int j = 1; j <= k; j++)
64 if(i >= c[j] && s[i-c[j]] != ’X’&&
65 s[i+1] != ’X’ && w[i] == w[i-c[j]])
66 {
67 if(!suf[i+2][j+1]) continue;
68 if((j == 1 && pre[i-c[j]][j-1]) ||
69 (i > c[j] && pre[i-c[j]-1][j-1]))
70 ++sum[i-c[j]+1], --sum[i+1];
71 }
72 }
73
74 string ans = "";
75 for(int i = 1; i <= n; i++)
76 {
77 sum[i] += sum[i-1];
78 black[i] |= (sum[i] > 0);
79
80 if(s[i] != ’.’) ans += s[i];
81 else if(black[i] && white[i]) ans += ’?’;
82 else ans += black[i] ? ’X’ : ’_’;
83 }
CAPITOLUL 5. IOI 2016 630

84
85 return ans;
86 }
87
88 // BEGIN CUT
89
90 const int S_MAX_LEN = 200 * 1000;
91 char buf[S_MAX_LEN + 1];
92
93 int main()
94 {
95 auto t1=clock();
96
97 freopen("../tests/subtask_7/125", "r", stdin);
98 freopen("7-125.out.txt", "w", stdout);
99
100 scanf("%s", buf);
101 std::string s = buf;
102 int c_len;
103 scanf("%d", &c_len);
104 std::vector<int> c(c_len);
105 for (int i = 0; i < c_len; i++)
106 {
107 scanf("%d", &c[i]);
108 }
109
110 auto t2=clock();
111
112 std::string ans = solve_puzzle(s, c);
113
114 auto t3=clock();
115
116 printf("%s\n", ans.data());
117
118 auto t4=clock();
119
120 // reset console output
121 freopen("CON", "w", stdout);
122
123 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
124 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
125 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
126
127 return 0;
128 }
129 // END CUT
130 /*
131 t2-t1 = 0.015000
132 t3-t2 = 3.141000
133 t4-t3 = 0.015000
134
135 Process returned 0 (0x0) execution time : 3.219 s
136 Press any key to continue.
137 */

Listing 5.4.10: paint-108251.cpp


1 // https://oj.uz/submission/108251
2
3 #include <bits/stdc++.h>
4 #include "paint.h"
5
6 using namespace std;
7
8 const int N = 2e5 + 10;
9
10 int n, k, w[N], b[N], sum[N];
11 bool pre[N][110], suf[N][110], black[N], white[N];
12 vector<int> c(1);
13
14 string solve_puzzle( string s, vector<int> f )
15 {
16 n = s.length(), k = f.size();
17 s = ’#’ + s + ’#’;
18 for( int i : f ) c.emplace_back( i );
CAPITOLUL 5. IOI 2016 631

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


20 {
21 w[i] += w[i-1] + ( s[i] == ’_’ );
22 b[i] += b[i-1] + ( s[i] == ’X’ );
23 }
24
25 pre[0][0] = 1;
26 for( int i = 1 ; i <= n ; i++ )
27 {
28 pre[i][0] = ( b[i] == 0 );
29 for( int j = 1 ; j <= k ; j++ )
30 {
31 if( s[i] != ’X’ ) pre[i][j] |= pre[i-1][j];
32 if( s[i] != ’_’ && i >= c[j] &&
33 s[i-c[j]] != ’X’ && w[i] == w[i-c[j]] )
34 {
35 if( j == 1 ) pre[i][j] |= pre[i-c[j]][j-1];
36 else if( i > c[j] ) pre[i][j] |= pre[i-c[j]-1][j-1];
37 }
38 }
39 }
40
41 suf[n+1][k+1] = suf[n+2][k+1] = 1;
42 for( int i = n ; i >= 1 ; i-- )
43 {
44 suf[i][k+1] = ( b[n] == b[i-1] );
45 for( int j = k ; j >= 1 ; j-- )
46 {
47 if( s[i] != ’X’ ) suf[i][j] |= suf[i+1][j];
48 if( s[i] != ’_’ && i + c[j] <= n + 1 &&
49 s[i+c[j]] != ’X’ && w[i-1] == w[i+c[j]-1] )
50 suf[i][j] |= suf[i+c[j]+1][j+1];
51 }
52 }
53
54 for( int i = 1 ; i <= n ; i++ )
55 {
56 if( s[i] == ’_’ ) white[i] = 1;
57 else
58 for( int j = 0 ; j <= k ; j++ )
59 white[i] |= ( pre[i-1][j] && suf[i+1][j+1] );
60
61 if( s[i] == ’_’ ) continue;
62
63 for( int j = 1 ; j <= k ; j++ )
64 if( i >= c[j] && s[i-c[j]] != ’X’ &&
65 s[i+1] != ’X’ && w[i] == w[i-c[j]] )
66 {
67 if( !suf[i+2][j+1] ) continue;
68 if( ( j == 1 && pre[i-c[j]][j-1] ) ||
69 ( i > c[j] && pre[i-c[j]-1][j-1] ) )
70 ++sum[i-c[j]+1], --sum[i+1];
71 }
72 }
73
74 string ans = "";
75 for( int i = 1 ; i <= n ; i++ )
76 {
77 sum[i] += sum[i-1];
78 black[i] |= ( sum[i] > 0 );
79 if( s[i] != ’.’ ) ans += s[i];
80 else
81 if( black[i] && white[i] ) ans += ’?’;
82 else ans += black[i] ? ’X’ : ’_’;
83 }
84
85 return ans;
86 }
87
88 // BEGIN CUT
89
90 const int S_MAX_LEN = 200 * 1000;
91 char buf[S_MAX_LEN + 1];
92
93 int main()
94 {
CAPITOLUL 5. IOI 2016 632

95 auto t1=clock();
96
97 freopen("../tests/subtask_7/125", "r", stdin);
98 freopen("7-125.out.txt", "w", stdout);
99
100 scanf("%s", buf);
101 std::string s = buf;
102 int c_len;
103 scanf("%d", &c_len);
104 std::vector<int> c(c_len);
105 for (int i = 0; i < c_len; i++)
106 {
107 scanf("%d", &c[i]);
108 }
109
110 auto t2=clock();
111
112 std::string ans = solve_puzzle(s, c);
113
114 auto t3=clock();
115
116 printf("%s\n", ans.data());
117
118 auto t4=clock();
119
120 // reset console output
121 freopen("CON", "w", stdout);
122
123 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
124 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
125 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
126
127 return 0;
128 }
129 // END CUT
130 /*
131 t2-t1 = 0.015000
132 t3-t2 = 2.855000
133 t4-t3 = 0.016000
134
135 Process returned 0 (0x0) execution time : 2.949 s
136 Press any key to continue.
137 */

Listing 5.4.11: paint-112136.cpp


1 // https://oj.uz/submission/112136
2
3 #include "paint.h"
4 #include <bits/stdc++.h>
5
6 #define MAXN 400007
7 #define MAXK 107
8
9 using namespace std;
10 bool dpl[MAXK][MAXN],dpr[MAXK][MAXN],mb[MAXN],mc[MAXN];
11 int nz[MAXN],lw[MAXN],nw[MAXN];
12
13 std::string solve_puzzle(std::string s, std::vector<int> c)
14 {
15 int n=s.size()+2,k=c.size();
16 for(int i=2;i<n;i++)
17 {
18 if(s[i-2]==’X’) nz[i]=0;
19 if(s[i-2]==’_’) nz[i]=1;
20 if(s[i-2]==’.’) nz[i]=2;
21 }
22
23 nz[1]=nz[n]=1;
24 dpl[0][0]=true;
25 for(int i=1;i<=n;i++) for(int j=0;j<=k;j++)
26 {
27 if(nz[i]) dpl[j][i]=dpl[j][i-1];
28 if(nz[i]==1) lw[i]=i;
29 else lw[i]=lw[i-1];
CAPITOLUL 5. IOI 2016 633

30 if(j>0 && i>=lw[i]+c[j-1] &&


31 dpl[j-1][i-c[j-1]-1] && nz[i-c[j-1]])
32 dpl[j][i]=true;
33 }
34
35 dpr[k+1][n+1]=true;
36 for(int i=n;i>0;i--) for(int j=k+1;j>0;j--)
37 {
38 if(nz[i]) dpr[j][i]=dpr[j][i+1];
39 if(nz[i]==1) nw[i]=i;
40 else nw[i]=nw[i+1];
41 if(j<=k && i+c[j-1]<=nw[i] &&
42 dpr[j+1][i+c[j-1]+1] && nz[i+c[j-1]])
43 dpr[j][i]=true;
44 }
45
46 for(int i=2;i<n;i++)
47 for(int j=0;j<=k;j++)
48 if(dpl[j][i-1] && dpr[j+1][i+1]) mb[i-2]=true;
49
50 for(int j=1;j<=k;j++)
51 {
52 int nd=1;
53 for(int i=2;i<n;i++)
54 {
55 if(dpl[j-1][i-2] && dpr[j+1][i+c[j-1]+1] &&
56 nz[i-1] && nz[i+c[j-1]] && nw[i]>=i+c[j-1])
57 nd=i+c[j-1]-1;
58 if(i<=nd) mc[i-2]=true;
59 }
60 }
61
62 string res="";
63 for(int i=0;i<n;i++)
64 {
65 if(s[i]!=’.’) res+=s[i];
66 else
67 {
68 if(mb[i] && mc[i]) res+=’?’;
69 if(mb[i] && !mc[i]) res+=’_’;
70 if(!mb[i] && mc[i]) res+=’X’;
71 }
72 }
73 return res;
74 }
75
76 // BEGIN CUT
77
78 const int S_MAX_LEN = 200 * 1000;
79 char buf[S_MAX_LEN + 1];
80
81 int main()
82 {
83 auto t1=clock();
84
85 freopen("../tests/subtask_7/125", "r", stdin);
86 freopen("7-125.out.txt", "w", stdout);
87
88 scanf("%s", buf);
89 std::string s = buf;
90 int c_len;
91 scanf("%d", &c_len);
92 std::vector<int> c(c_len);
93 for (int i = 0; i < c_len; i++)
94 {
95 scanf("%d", &c[i]);
96 }
97
98 auto t2=clock();
99
100 std::string ans = solve_puzzle(s, c);
101
102 auto t3=clock();
103
104 printf("%s\n", ans.data());
105
CAPITOLUL 5. IOI 2016 634

106 auto t4=clock();


107
108 // reset console output
109 freopen("CON", "w", stdout);
110
111 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
112 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
113 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
114
115 return 0;
116 }
117 // END CUT
118 /*
119 t2-t1 = 0.015000
120 t3-t2 = 1.672000
121 t4-t3 = 0.016000
122
123 Process returned 0 (0x0) execution time : 1.891 s
124 Press any key to continue.
125 */

Listing 5.4.12: paint-130510.cpp


1 // https://oj.uz/submission/130510
2
3 #include "paint.h"
4 #include <bits/stdc++.h>
5 using namespace std;
6
7 const int N = 2e5+5;
8
9 int n, k;
10 int pref_[N], prefX[N];
11 int canX[N], can_[N];
12 bool state[105][N];
13 char arr[N];
14 int sz[N];
15
16 string solve_puzzle(string s, vector<int> C)
17 {
18 n = s.size(), k = C.size();
19 for(int i = 0; i < n; ++i) arr[i+2] = s[i];
20 for(int i = 0; i < k; ++i) sz[i+1] = C[i];
21 for(int i = 2; i <= n+1; ++i)
22 pref_[i] = pref_[i-1] + (arr[i] == ’_’ ? 1 : 0);
23 for(int i = 2; i <= n+1; ++i)
24 prefX[i] = prefX[i-1] + (arr[i] == ’X’ ? 1 : 0);
25
26 pref_[n+2] = pref_[n+1], prefX[n+2] = prefX[n+1];
27 state[0][0] = 1;
28 for(int i = 1; i <= k; ++i)
29 {
30 int lst = -1;
31 for(int j = sz[i]+1; j <= n+1; ++j)
32 {
33 if(state[i-1][j-sz[i]-1]) lst = j-sz[i]-1;
34 if(lst != -1 && (prefX[j-sz[i]] - prefX[lst] == 0) &&
35 (pref_[j] - pref_[j-sz[i]] == 0) &&
36 (prefX[(i == k ? n : j) + 1] - prefX[j] == 0))
37 state[i][j] = 1;
38 }
39 }
40
41 for(int i = k; i >= 0; --i)
42 {
43 if(i == k) for(int j = n+1; j >= 0; --j)
44 {
45 if(state[i][j])
46 {
47 canX[j-sz[i]+1]++, canX[j+1]--, can_[j+1]++, can_[n+2]--;
48 }
49 }
50 else
51 {
52 int l = n+1, r = n+1;
CAPITOLUL 5. IOI 2016 635

53 while(true)
54 {
55 if(state[i+1][r])
56 {
57 if(r-l <= sz[i+1]) state[i][l--] = false;
58 else
59 if(state[i][l])
60 {
61 if(prefX[r-sz[i+1]] - prefX[l] == 0)
62 canX[l-sz[i]+1]++,
63 canX[l+1]--,
64 can_[l+1]++,
65 can_[r-sz[i+1]+1]--,
66 l--;
67 else r--;
68 }
69 else l--;
70 }
71 else r--;
72
73 if(l < 0 || r < 0) break;
74 }
75
76 for(int j = l; j >= 0; --j)
77 state[i][j] = false;
78 }
79 }
80
81 for(int j = 2; j <= n+1; ++j)
82 canX[j] += canX[j-1], can_[j] += can_[j-1];
83
84 string str;
85 for(int j = 2; j <= n+1; ++j)
86 {
87 if(canX[j] && can_[j]) str.push_back(’?’);
88 else
89 if(canX[j]) str.push_back(’X’);
90 else str.push_back(’_’);
91 }
92
93 return str;
94 }
95
96 // BEGIN CUT
97
98 const int S_MAX_LEN = 200 * 1000;
99 char buf[S_MAX_LEN + 1];
100
101 int main()
102 {
103 auto t1=clock();
104
105 freopen("../tests/subtask_7/125", "r", stdin);
106 freopen("7-125.out.txt", "w", stdout);
107
108 scanf("%s", buf);
109 std::string s = buf;
110 int c_len;
111 scanf("%d", &c_len);
112 std::vector<int> c(c_len);
113 for (int i = 0; i < c_len; i++)
114 {
115 scanf("%d", &c[i]);
116 }
117
118 auto t2=clock();
119
120 std::string ans = solve_puzzle(s, c);
121
122 auto t3=clock();
123
124 printf("%s\n", ans.data());
125
126 auto t4=clock();
127
128 // reset console output
CAPITOLUL 5. IOI 2016 636

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


130
131 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
132 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
133 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
134
135 return 0;
136 }
137 // END CUT
138 /*
139 t2-t1 = 0.015000
140 t3-t2 = 0.516000
141 t4-t3 = 0.015000
142
143 Process returned 0 (0x0) execution time : 0.734 s
144 Press any key to continue.
145 */

Listing 5.4.13: paint-180292.cpp


1 // https://oj.uz/submission/180292
2
3 #include "paint.h"
4 #include<bits/stdc++.h>
5
6 using namespace std;
7
8 int vis[200005][105], dp[200005][105];
9 string ans, A;
10 vector<int>seg;
11 int suf[200005], isi[200005], emptyy[200005];
12 int n, k;
13
14 int f(int i, int lf)
15 {
16 if(i>n) return 0;
17 if(i==n) return lf==k;
18 if(dp[i][lf]!=-1) return dp[i][lf];
19
20 int rs=0;
21 if(A[i]!=’X’) rs|=f(i+1, lf);
22 if(lf<k && seg[lf]<=suf[i] && A[i+seg[lf]]!=’X’)
23 {
24 if(i+seg[lf]+1<=n)
25 rs|=f(i+seg[lf]+1, lf+1);
26 else rs|=f(i+seg[lf], lf+1);
27 }
28
29 return dp[i][lf]=rs;
30 }
31
32 void back(int i, int lf)
33 {
34 if(i>=n || vis[i][lf]) return;
35
36 vis[i][lf]=1;
37 if(A[i]!=’X’ && f(i+1, lf))
38 {
39 emptyy[i]=1;
40 back(i+1, lf);
41 }
42
43 if(lf<k &&
44 seg[lf]<=suf[i] &&
45 A[i+seg[lf]]!=’X’ &&
46 f(i+seg[lf]+(i+seg[lf]+1<=n), lf+1))
47 {
48 isi[i]++;
49 isi[i+seg[lf]]--;
50 emptyy[i+seg[lf]]=1;
51 back(i+seg[lf]+1, lf+1);
52 }
53 }
54
55 string solve_puzzle(string s, vector<int>c)
CAPITOLUL 5. IOI 2016 637

56 {
57 A=s;
58 seg=c;
59 n=s.size();
60 k=c.size();
61 for(int i=n-1; i>=0; i--)
62 suf[i]=A[i]==’_’?0:1+suf[i+1];
63
64 memset(dp, -1, sizeof(dp));
65
66 f(0, 0);
67 back(0, 0);
68
69 int st=0;
70 ans.resize(n);
71 for(int i=0; i<n; i++)
72 {
73 st+=isi[i];
74 if(emptyy[i] && st>0) ans[i]=’?’;
75 else
76 if(emptyy[i]) ans[i]=’_’;
77 else
78 if(st>0) ans[i]=’X’;
79 }
80 return ans;
81 }
82
83 // BEGIN CUT
84
85 const int S_MAX_LEN = 200 * 1000;
86 char buf[S_MAX_LEN + 1];
87
88 int main()
89 {
90 auto t1=clock();
91
92 freopen("../tests/subtask_7/125", "r", stdin);
93 freopen("7-125.out.txt", "w", stdout);
94
95 scanf("%s", buf);
96 std::string s = buf;
97 int c_len;
98 scanf("%d", &c_len);
99 std::vector<int> c(c_len);
100 for (int i = 0; i < c_len; i++)
101 {
102 scanf("%d", &c[i]);
103 }
104
105 auto t2=clock();
106
107 std::string ans = solve_puzzle(s, c);
108
109 auto t3=clock();
110
111 printf("%s\n", ans.data());
112
113 auto t4=clock();
114
115 // reset console output
116 freopen("CON", "w", stdout);
117
118 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
119 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
120 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
121
122 return 0;
123 }
124 // END CUT
125 /*
126 t2-t1 = 0.015000
127 t3-t2 = 0.125000
128 t4-t3 = 0.016000
129
130 Process returned 0 (0x0) execution time : 1.188 s
131 Press any key to continue.
CAPITOLUL 5. IOI 2016 638

132 */

Listing 5.4.14: paint-204502.cpp


1 // https://oj.uz/submission/204502
2
3 #include "paint.h"
4 #include <iostream>
5 #include<string>
6 #include<vector>
7
8 #include<ctime>
9
10 #define MAXN 200005
11 #define MAXK 105
12
13 using namespace std;
14
15 int N, K, whitesum[MAXN], canblack[MAXN], black;
16 bool dp[MAXN][MAXK], done[MAXN][MAXK], canwhite[MAXN];
17
18 string s;
19 vector <int> c;
20
21 bool gen(int i, int j)
22 {
23 if (i >= N && j == K) {return(1);}
24 if (i >= N && j < K) {return(0);}
25 if (done[i][j]) {return(dp[i][j]);}
26 done[i][j]=true;
27 if (s[i] != ’X’ && gen(i+1,j))
28 {
29 dp[i][j]=true;
30 canwhite[i]=true;
31 }
32 if (j < K && i + c[j] <= N)
33 {
34 bool can = true;
35 if (whitesum[i+c[j]] - whitesum[i] > 0 ||
36 (i+c[j] != N && s[i+c[j]] == ’X’))
37 {
38 can=false;
39 }
40 if (can && gen(i+c[j]+1, j+1))
41 {
42 dp[i][j]=true;
43 canblack[i]++;
44 canblack[i+c[j]]--;
45 canwhite[i+c[j]]=true;
46 }
47 }
48 return(dp[i][j]);
49 }
50
51 string solve_puzzle(string S, vector<int> C)
52 {
53 N=S.size(), K=C.size();
54 s = S, c = C;
55 for (int i=1; i<=N; i++)
56 {
57 whitesum[i]=whitesum[i-1] + (S[i-1] == ’_’);
58 }
59
60 gen(0,0);
61
62 string Sol;
63 for (int i=0; i<N; i++)
64 {
65 black+=canblack[i];
66 if (black && canwhite[i])
67 {
68 Sol.push_back(’?’);
69 }
70 else
71 if (black)
CAPITOLUL 5. IOI 2016 639

72 {
73 Sol.push_back(’X’);
74 }
75 else
76 {
77 Sol.push_back(’_’);
78 }
79 }
80
81 return(Sol);
82 }
83
84 // BEGIN CUT
85
86 const int S_MAX_LEN = 200 * 1000;
87 char buf[S_MAX_LEN + 1];
88
89 int main()
90 {
91 auto t1=clock();
92
93 freopen("../tests/subtask_7/125", "r", stdin);
94 freopen("7-125.out.txt", "w", stdout);
95
96 scanf("%s", buf);
97 std::string s = buf;
98 int c_len;
99 scanf("%d", &c_len);
100 std::vector<int> c(c_len);
101 for (int i = 0; i < c_len; i++)
102 {
103 scanf("%d", &c[i]);
104 }
105
106 auto t2=clock();
107
108 std::string ans = solve_puzzle(s, c);
109
110 auto t3=clock();
111
112 printf("%s\n", ans.data());
113
114 auto t4=clock();
115
116 // reset console output
117 freopen("CON", "w", stdout);
118
119 printf("t2-t1 = %f\n", (double)(t2 - t1) / CLOCKS_PER_SEC);
120 printf("t3-t2 = %f\n", (double)(t3 - t2) / CLOCKS_PER_SEC);
121 printf("t4-t3 = %f\n", (double)(t4 - t3) / CLOCKS_PER_SEC);
122
123 return 0;
124 }
125 // END CUT
126 /*
127 t2-t1 = 0.015000
128 t3-t2 = 0.047000
129 t4-t3 = 0.016000
130
131 Process returned 0 (0x0) execution time : 0.125 s
132 Press any key to continue.
133 */

5.4.3 *Rezolvare detaliat 

5.5 Unscrambling a Messy Bug


Problema 5 - Unscrambling a Messy Bug 100 de puncte
Author: Shi-Chun Tsai (Taiwan)
CAPITOLUL 5. IOI 2016 640

Ilshat este un programator care lucreaz  la dezvoltarea unor structuri de date eciente. î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 .
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 nal, programul poate apela funcµia check_element(x)\verb pentru a verica dac 
elementul este prezent în structura de date. Aceast  funcµie poate  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 ec 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 ecare 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 ind cel mai semnicativ 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  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  verice prezenµa unor elemente din mulµimea modicat ,
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 ec 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 semnicaµie ca mai sus.


` funcµia va returna permutarea descoperit , p, stocând-o în array-ul furnizat, result:
pentru ecare i, va stoca pi în result[i].
CAPITOLUL 5. IOI 2016 641

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)
Aceast  funcµie insereaz  elementul descris de x în mulµime.
` x: un string de caractere '0' ³i '1' semnicând reprezentarea binar  a întregului care va
 inserat. Lungimea lui x trebuie s  e 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 veric  dac  elementul x este în mulµimea alterat .
` x: un string de caractere '0' and '1' semnicând reprezentarea binar  a întregului care
va  interogat. Lungimea lui x trebuie s  e 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 
"Wrong Answer".
Pentru toate stringurile, primul caracter denot  cel mai semnicativ bit al întregului cores-
punz tor.
Grader-ul xeaz  permutarea înainte ca funcµia restore_permutation s  e apelat .
Folosiµi ³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:
CAPITOLUL 5. IOI 2016 642

` linia 1: întregii n, w, r,
` linia 2: n întregi, reprezentând elementele lui p.

Timp maxim de executare/test: 2.0 secunde


Memorie: total 2048 MB

5.5.1 Indicaµii de rezolvare

Author: Shi-Chun Tsai


National Chiao-Tnug University, sctsai@cs.nctu.edu.tw, country: Taiwan
Subtask 1 allows you to check all 2n 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 rst 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 nd 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 nd 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 nd 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 shue 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:
CAPITOLUL 5. IOI 2016 643

add_element("10000000")
add_element("01000000")
add_element("00100000")
add_element("00010000")

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 rst 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 dierent 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")

5.5.2 Coduri surs 

Listing 5.5.1: checkerMessyModicat.cpp


1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 //const string output_secret = "098d134608c94f7413faac591054ee35";
8
9 int main()
10 {
11 int argc=4;
12
13 char* argv[] =
14 {
15 (char*)"checker",
16 (char*)"../tests/subtask_4/014", // input
17 (char*)"../tests/subtask_4/014.a", // rezultat corect
18 (char*)"4-014.out.txt", // rezultat de verificat
19 };
20
21 cout<<"argc = "<<argc<<"\n";
22 for(int kk=0;kk<argc;kk++)
23 cout<<argv[kk]<<"\n";
24 cout<<"----------------------\n";
25
26 registerChecker("paint", argc, argv);
CAPITOLUL 5. IOI 2016 644

27
28 //readBothSecrets(output_secret);
29 //readBothGraderResults();
30 //compareRemainingLines(3); // incepand cu linia nr 3
31
32 compareRemainingLines(1); // incepand cu linia nr 1
33 }

Listing 5.5.2: messy_cpp_ok.cpp


1 #include <vector>
2 #include <cstdio>
3 #include <string>
4 #include <set>
5 #include <cstdlib>
6 #include <iostream>
7 #include <algorithm>
8
9 #include<ctime>
10
11 using namespace std;
12
13 vector<int> restore_permutation(int, int, int);
14
15 namespace variables
16 {
17 set<string> set_;
18 bool compiled = false;
19 int n;
20 vector<int> p;
21 int w;
22 int r;
23
24 int readInt()
25 {
26 int x;
27 cin >> x;
28 return x;
29 }
30 }
31
32 using namespace variables;
33
34 int main()
35 {
36 auto t1 = clock();
37
38 std::freopen("../tests/subtask_4/014", "r", stdin);
39 std::freopen("4-014.out.txt", "w", stdout);
40
41 n = readInt();
42 w = readInt();
43 r = readInt();
44
45 p = vector<int>(n);
46
47 for (int i = 0; i < n; i++)
48 {
49 p[i] = readInt();
50 }
51
52 auto t2 = clock();
53
54 vector<int> answer = restore_permutation(n, w, r);
55
56 auto t3 = clock();
57
58 //printf("098d134608c94f7413faac591054ee35\n");
59 //if (p == answer) { printf("OK\n"); }
60 //else { printf("WA\n"); }
61
62 printf("%d", answer[0]);
63 for (int i = 1; i < n; i++)
64 {
65 printf(" %d", answer[i]);
CAPITOLUL 5. IOI 2016 645

66 }
67 printf("\n");
68
69 auto t4 = clock();
70
71 // reset console output
72 freopen("CON", "w", stdout);
73
74 //cout<<"res = "<<res<<"\n";
75
76 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
77 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
78 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
79
80 return 0;
81 }
82
83 bool check(const string& x)
84 {
85 if (x.length() != n)
86 {
87 return false;
88 }
89 for (int i = 0; i < n; i++)
90 {
91 if (x[i] != ’0’ && x[i] != ’1’)
92 {
93 return false;
94 }
95 }
96 return true;
97 }
98
99 void add_element(string x)
100 {
101 if (--w < 0 || compiled || !check(x))
102 {
103 printf("WA\n");
104 exit(0);
105 }
106 set_.insert(x);
107 }
108
109 bool check_element(string x)
110 {
111 if (--r < 0 || !compiled || !check(x))
112 {
113 printf("WA\n");
114 exit(0);
115 }
116 return set_.count(x);
117 }
118
119 void compile_set()
120 {
121 if (compiled)
122 {
123 printf("WA\n");
124 exit(0);
125 }
126 compiled = true;
127 set<string> compiledSet;
128 string compiledElement = string(n, ’ ’);
129 for (const string& s : set_)
130 {
131 for (int i = 0; i < n; i++)
132 {
133 compiledElement[i] = s[p[i]];
134 }
135 compiledSet.insert(compiledElement);
136 }
137 set_ = compiledSet;
138 }
139
140 int nn;
141 string address;
CAPITOLUL 5. IOI 2016 646

142
143 void helper(int length, int w)
144 {
145 fill(address.begin(), address.end(), ’1’);
146 for (int i = w; i < nn; i += (1 << length))
147 {
148 address[i] = ’0’;
149 }
150 int j = w;
151 for (int i = 0; i < (nn / (1 << length)); i++)
152 {
153 address[j] = ’1’;
154 if (i % 2 == 1)
155 {
156 add_element(address);
157 }
158 address[j] = ’0’;
159 j += 1 << length;
160 }
161 }
162
163 void doWrites(int n_)
164 {
165 nn = n_;
166 address = string(nn, ’0’);
167 for (int i = 0; i < nn; i++)
168 {
169 fill(address.begin(), address.end(), ’0’);
170 address[i] = ’1’;
171 if (i % 2 == 1)
172 {
173 add_element(address);
174 }
175 }
176
177 int x = 1;
178 int log = 0;
179 while (x < nn)
180 {
181 x *= 2;
182 log++;
183 }
184 for (int length = 1; length < log; length++)
185 {
186 for (int i = 0; i < (1 << length); i++)
187 {
188 helper(length, i);
189 }
190 }
191 }
192
193 void read(const vector<int> &t, vector<int>& answer, int step, int w)
194 {
195 if (t.size() == 1)
196 {
197 answer[t[0]] = w;
198 return;
199 }
200 vector<int> t0;
201 vector<int> t1;
202 fill(address.begin(), address.end(), ’1’);
203 for (int j : t)
204 {
205 address[j] = ’0’;
206 }
207 for (int j : t)
208 {
209 address[j] = ’1’;
210 if (!check_element(address))
211 {
212 t0.push_back(j);
213 }
214 else
215 {
216 t1.push_back(j);
217 }
CAPITOLUL 5. IOI 2016 647

218 address[j] = ’0’;


219 }
220 read(t0, answer, step + 1, w);
221 read(t1, answer, step + 1, w + (1 << step));
222 }
223
224 void doReads(int nn, vector<int>& answer)
225 {
226 vector<int> order = vector<int>(nn);
227 for (int i = 0; i < nn; i++)
228 {
229 order[i] = i;
230 }
231 read(order, answer, 0, 0);
232 }
233
234 vector<int> restore_permutation(int nn, int w, int r)
235 {
236 doWrites(nn);
237 compile_set();
238 vector<int> answer(nn);
239 doReads(nn, answer);
240 return answer;
241 }
242 /*
243 t2-t1 = 0
244 t3-t2 = 0.015
245 t4-t3 = 0
246
247 Process returned 0 (0x0) execution time : 0.047 s
248 Press any key to continue.
249 */

Listing 5.5.3: messy_iz.cpp


1 #include <vector>
2 #include <cstdio>
3 #include <string>
4 #include <set>
5 #include <cstdlib>
6 #include <iostream>
7
8 #include<ctime>
9
10 #include "messy.h"
11
12 using namespace std;
13
14 namespace helper
15 {
16 set<string> set_;
17 bool compiled = false;
18 int n;
19 vector<int> p;
20 int w;
21 int r;
22
23 int readInt()
24 {
25 int x;
26 cin >> x;
27 return x;
28 }
29 }
30
31 using namespace helper;
32
33 vector<int> restore_permutation(int, int, int);
34
35 int main()
36 {
37 auto t1 = clock();
38
39 std::freopen("../tests/subtask_4/014", "r", stdin);
40 std::freopen("4-014.out.txt", "w", stdout);
CAPITOLUL 5. IOI 2016 648

41
42 n = readInt();
43 w = readInt();
44 r = readInt();
45
46 p = vector<int>(n);
47
48 for (int i = 0; i < n; i++)
49 {
50 p[i] = readInt();
51 }
52
53 auto t2 = clock();
54
55 vector<int> answer = restore_permutation(n, w, r);
56
57 auto t3 = clock();
58
59 //printf("098d134608c94f7413faac591054ee35\n");
60 //if (p == answer) { printf("OK\n"); }
61 //else { printf("WA\n"); }
62
63 printf("%d", answer[0]);
64 for (int i = 1; i < n; i++)
65 {
66 printf(" %d", answer[i]);
67 }
68 printf("\n");
69
70 auto t4 = clock();
71
72 // reset console output
73 freopen("CON", "w", stdout);
74
75 //cout<<"res = "<<res<<"\n";
76
77 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
78 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
79 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
80
81 return 0;
82 }
83
84 bool check(const string& x)
85 {
86 if (x.length() != n)
87 {
88 return false;
89 }
90 for (int i = 0; i < n; i++)
91 {
92 if (x[i] != ’0’ && x[i] != ’1’)
93 {
94 return false;
95 }
96 }
97 return true;
98 }
99
100 void add_element(string x)
101 {
102 if (--w < 0 || compiled || !check(x))
103 {
104 printf("WA\n");
105 exit(0);
106 }
107 set_.insert(x);
108 }
109
110 bool check_element(string x)
111 {
112 if (--r < 0 || !compiled || !check(x))
113 {
114 printf("WA\n");
115 exit(0);
116 }
CAPITOLUL 5. IOI 2016 649

117 return set_.count(x);


118 }
119
120 void compile_set()
121 {
122 if (compiled)
123 {
124 printf("WA\n");
125 exit(0);
126 }
127 compiled = true;
128 set<string> compiledSet;
129 string compiledElement = string(n, ’ ’);
130 for (const string& s : set_)
131 {
132 for (int i = 0; i < n; i++)
133 {
134 compiledElement[i] = s[p[i]];
135 }
136 compiledSet.insert(compiledElement);
137 }
138 set_ = compiledSet;
139 }
140
141 #include <vector>
142 #include <string>
143
144 //#include "messy.h"
145
146 using namespace std;
147
148 std::vector<int> restore_permutation(int n, int w, int r)
149 {
150 for (int len = n / 2; len >= 1; len >>= 1)
151 {
152 for (int s = 0; s < n; s += 2 * len)
153 {
154 string a = "";
155 for (int i = 0; i < n; i++) a += ’1’;
156 for (int i = 0; i < len * 2; i++) a[s + i] = ’0’;
157 for (int i = 0; i < len; i++)
158 {
159 string ss = a;
160 ss[s + i] = ’1’;
161 add_element(ss);
162 }
163 }
164 }
165
166 compile_set();
167
168 vector<vector<int> > can(n);
169 for (int i = 0; i < n; i++) can[0].push_back(i);
170 for (int len = n / 2; len >= 1; len >>= 1)
171 {
172 for (int s = 0; s < n; s += 2 * len)
173 {
174 string a = "";
175 for (int i = 0; i < n; i++) a += ’1’;
176 for (int i = 0; i < (int)can[s].size(); i++) a[can[s][i]] = ’0’;
177
178 vector<int> c1, c2;
179 for (int i = 0; i < (int)can[s].size(); i++)
180 {
181 string ss = a;
182 ss[can[s][i]] = ’1’;
183 if (check_element(ss))
184 {
185 c1.push_back(can[s][i]);
186 }
187 else
188 {
189 c2.push_back(can[s][i]);
190 }
191 }
192
CAPITOLUL 5. IOI 2016 650

193 can[s] = c1;


194 can[s + len] = c2;
195 }
196 }
197
198 vector<int> ans(n);
199 for (int i = 0; i < n; i++) ans[can[i][0]] = i;
200 return ans;
201 }
202 /*
203 t2-t1 = 0
204 t3-t2 = 0.015
205 t4-t3 = 0
206
207 Process returned 0 (0x0) execution time : 0.094 s
208 Press any key to continue.
209 */

Listing 5.5.4: messy_tourist.cpp


1 #include <vector>
2 #include <cstdio>
3 #include <string>
4 #include <set>
5 #include <cstdlib>
6 #include <iostream>
7
8 #include<ctime>
9
10 #include "messy.h"
11
12 using namespace std;
13
14 namespace helper
15 {
16 set<string> set_;
17 bool compiled = false;
18 int n;
19 vector<int> p;
20 int w;
21 int r;
22
23 int readInt()
24 {
25 int x;
26 cin >> x;
27 return x;
28 }
29 }
30
31 using namespace helper;
32
33 vector<int> restore_permutation(int, int, int);
34
35 int main()
36 {
37 auto t1 = clock();
38
39 std::freopen("../tests/subtask_4/014", "r", stdin);
40 std::freopen("4-014.out.txt", "w", stdout);
41
42 n = readInt();
43 w = readInt();
44 r = readInt();
45
46 p = vector<int>(n);
47
48 for (int i = 0; i < n; i++)
49 {
50 p[i] = readInt();
51 }
52
53 auto t2 = clock();
54
55 vector<int> answer = restore_permutation(n, w, r);
CAPITOLUL 5. IOI 2016 651

56
57 auto t3 = clock();
58
59 //printf("098d134608c94f7413faac591054ee35\n");
60 //if (p == answer) { printf("OK\n"); }
61 //else { printf("WA\n"); }
62
63 printf("%d", answer[0]);
64 for (int i = 1; i < n; i++)
65 {
66 printf(" %d", answer[i]);
67 }
68 printf("\n");
69
70 auto t4 = clock();
71
72 // reset console output
73 freopen("CON", "w", stdout);
74
75 //cout<<"res = "<<res<<"\n";
76
77 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
78 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
79 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
80
81 return 0;
82 }
83
84 bool check(const string& x)
85 {
86 if (x.length() != n)
87 {
88 return false;
89 }
90 for (int i = 0; i < n; i++)
91 {
92 if (x[i] != ’0’ && x[i] != ’1’)
93 {
94 return false;
95 }
96 }
97 return true;
98 }
99
100 void add_element(string x)
101 {
102 if (--w < 0 || compiled || !check(x))
103 {
104 printf("WA\n");
105 exit(0);
106 }
107 set_.insert(x);
108 }
109
110 bool check_element(string x)
111 {
112 if (--r < 0 || !compiled || !check(x))
113 {
114 printf("WA\n");
115 exit(0);
116 }
117 return set_.count(x);
118 }
119
120 void compile_set()
121 {
122 if (compiled)
123 {
124 printf("WA\n");
125 exit(0);
126 }
127 compiled = true;
128 set<string> compiledSet;
129 string compiledElement = string(n, ’ ’);
130 for (const string& s : set_)
131 {
CAPITOLUL 5. IOI 2016 652

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


133 {
134 compiledElement[i] = s[p[i]];
135 }
136 compiledSet.insert(compiledElement);
137 }
138 set_ = compiledSet;
139 }
140
141 #include <bits/stdc++.h>
142
143 using namespace std;
144
145 int N;
146
147 void put(int i, int j)
148 {
149 string s(N, ’0’);
150 s[i] = s[j] = ’1’;
151 add_element(s);
152 }
153
154 void put(int i, int j, int k)
155 {
156 string s(N, ’0’);
157 s[i] = s[j] = s[k] = ’1’;
158 add_element(s);
159 }
160
161 bool check(int i, int j)
162 {
163 string s(N, ’0’);
164 s[i] = s[j] = ’1’;
165 return check_element(s);
166 }
167
168 bool check(int i, int j, int k)
169 {
170 string s(N, ’0’);
171 s[i] = s[j] = s[k] = ’1’;
172 return check_element(s);
173 }
174
175 bool g[1234][1234];
176
177 vector<int> restore_permutation(int nnn, int w, int r)
178 {
179 N = nnn;
180 int k = 0;
181 while ((1 << k) != N)
182 {
183 k++;
184 }
185
186 if (k < 4)
187 {
188 string s(N, ’0’);
189 for (int i = 0; i < N; i++)
190 {
191 s[i] = ’1’;
192 add_element(s);
193 }
194
195 compile_set();
196
197 string t(N, ’0’);
198 vector <int> ans(N);
199
200 for (int i = 0; i < N; i++)
201 {
202 for (int j = 0; j < N; j++)
203 {
204 if (t[j] != ’0’)
205 {
206 continue;
207 }
CAPITOLUL 5. IOI 2016 653

208 t[j] = ’1’;


209 if (check_element(t))
210 {
211 ans[j] = i;
212 break;
213 }
214 t[j] = ’0’;
215 }
216 }
217 return ans;
218 }
219
220 for (int i = 0; i < k; i++)
221 {
222 put(i, i);
223 }
224
225 put(0, 1);
226 put(0, 2);
227 put(1, 2);
228 for (int i = 2; i + 1 < k; i++)
229 {
230 put(i, i + 1);
231 }
232
233 put(1, 2, 3);
234 for (int i = k; i < N; i++)
235 {
236 for (int j = 0; j < k; j++)
237 {
238 if (i & (1 << j))
239 {
240 put(i, j);
241 }
242 }
243 }
244
245 compile_set();
246
247 vector <int> imp;
248 for (int i = 0; i < N; i++)
249 {
250 if (check(i, i))
251 {
252 imp.push_back(i);
253 }
254 }
255
256 assert(imp.size() == k);
257 for (int i = 0; i < k; i++)
258 {
259 for (int j = i + 1; j < k; j++)
260 {
261 g[imp[i]][imp[j]] = g[imp[j]][imp[i]] = check(imp[i], imp[j]);
262 }
263 }
264
265 bool found = false;
266 do
267 {
268 if (!g[imp[0]][imp[1]] || !g[imp[0]][imp[2]] || !g[imp[1]][imp[2]])
269 {
270 continue;
271 }
272
273 bool ok = true;
274 for (int i = 2; i + 1 < k; i++)
275 {
276 if (!g[imp[i]][imp[i + 1]])
277 {
278 ok = false;
279 break;
280 }
281 }
282
283 if (ok)
CAPITOLUL 5. IOI 2016 654

284 {
285 if (!check(imp[1], imp[2], imp[3]))
286 {
287 continue;
288 }
289 found = true;
290 break;
291 }
292 } while (next_permutation(imp.begin(), imp.end()));
293
294 assert(found);
295 vector <int> ans(N, -1);
296 vector <bool> used(N, false);
297
298 for (int i = 0; i < k; i++)
299 {
300 ans[imp[i]] = i;
301 used[i] = true;
302 }
303
304 for (int i = 0; i < N; i++)
305 {
306 if (ans[i] != -1)
307 {
308 continue;
309 }
310
311 int u = 0;
312 for (int j = k - 1; j >= 0; j--)
313 {
314 int options = 0;
315 int any = -1;
316 for (int z = u; z < u + (1 << (j + 1)); z++)
317 {
318 if (!used[z])
319 {
320 options++;
321 any = z;
322 }
323 }
324
325 if (options == 1)
326 {
327 u = any;
328 break;
329 }
330 if (check(i, imp[j]))
331 {
332 u |= (1 << j);
333 }
334 }
335
336 ans[i] = u;
337 used[u] = true;
338 }
339
340 return ans;
341 }
342 /*
343 t2-t1 = 0
344 t3-t2 = 0
345 t4-t3 = 0
346
347 Process returned 0 (0x0) execution time : 0.500 s
348 Press any key to continue.
349 */

Listing 5.5.5: messy-21910.cpp


1 // https://oj.uz/submission/21910
2
3 //#include "messy.h"
4
5 //#include <vector>
6 //#include <string>
CAPITOLUL 5. IOI 2016 655

7 #include <algorithm>
8 #include <cassert>
9
10 using namespace std;
11
12 // --------------- begin grader -------------------
13
14 #include <vector>
15 #include <cstdio>
16 #include <string>
17 #include <set>
18 #include <cstdlib>
19 #include <iostream>
20
21 #include<ctime>
22
23 #include "messy.h"
24
25 using namespace std;
26
27 namespace helper
28 {
29 set<string> set_;
30 bool compiled = false;
31 int n;
32 vector<int> p;
33 int w;
34 int r;
35
36 int read_int()
37 {
38 int x;
39 cin >> x;
40 return x;
41 }
42 }
43
44 using namespace helper;
45
46 // A convenience function.
47 int get_p(int i)
48 {
49 int ret = p[i];
50 return ret;
51 }
52
53 int main()
54 {
55 auto t1 = clock();
56
57 std::freopen("../tests/subtask_4/014", "r", stdin);
58 std::freopen("4-014.out.txt", "w", stdout);
59
60 n = read_int();
61 w = read_int();
62 r = read_int();
63
64 p = vector<int>(n);
65
66 for (int i = 0; i < n; i++)
67 {
68 p[i] = read_int();
69 }
70
71 auto t2 = clock();
72
73 vector<int> answer = restore_permutation(n, w, r);
74
75 auto t3 = clock();
76
77 if (answer.size() != n)
78 {
79 printf("WA\n");
80 return 0;
81 }
82
CAPITOLUL 5. IOI 2016 656

83 printf("%d", answer[0]);
84
85 for (int i = 1; i < n; i++)
86 {
87 printf(" %d", answer[i]);
88 }
89 printf("\n");
90
91 auto t4 = clock();
92
93 // reset console output
94 freopen("CON", "w", stdout);
95
96 //cout<<"res = "<<res<<"\n";
97
98 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
99 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
100 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
101
102 return 0;
103 }
104
105 void wa()
106 {
107 printf("WA\n");
108 exit(0);
109 }
110
111 bool check(const string& x)
112 {
113 if ((int)x.length() != n)
114 {
115 return false;
116 }
117 for (int i = 0; i < n; i++)
118 {
119 if (x[i] != ’0’ && x[i] != ’1’)
120 {
121 return false;
122 }
123 }
124 return true;
125 }
126
127 void add_element(string x)
128 {
129 if (--w < 0 || compiled || !check(x))
130 {
131 wa();
132 }
133 set_.insert(x);
134 }
135
136 bool check_element(string x)
137 {
138 if (--r < 0 || !compiled || !check(x))
139 {
140 wa();
141 }
142 return set_.count(x);
143 }
144
145 void compile_set()
146 {
147 if (compiled)
148 {
149 wa();
150 }
151 compiled = true;
152 set<string> compiledSet;
153 string compiledElement = string(n, ’ ’);
154 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
155 {
156 string s = *it;
157 for (int i = 0; i < n; i++)
158 {
CAPITOLUL 5. IOI 2016 657

159 compiledElement[i] = s[get_p(i)];


160 }
161 compiledSet.insert(compiledElement);
162 }
163 set_ = compiledSet;
164 }
165
166 // --------------- end grader -------------------
167
168 int N;
169
170 void add_strings(int L, int R, string base)
171 {
172 if(L == R) return;
173 int mid = (L + R)/2;
174 for(int i = L; i <= mid; i++)
175 {
176 base[i] = ’1’;
177 add_element(base);
178 base[i] = ’0’;
179 }
180
181 base = string(N, ’0’);
182 string left = base, right = base;
183 for(int i = mid + 1; i <= R; i++) left[i] = ’1’;
184 for(int i = L; i <= mid; i++) right[i] = ’1’;
185 add_strings(L, mid, left);
186 add_strings(mid + 1, R, right);
187 }
188
189 vector<int> ans_rev;
190
191 void solve(int L, int R, vector<int> A, vector<int> other)
192 {
193 if(L == R)
194 {
195 assert(A.size() == 1);
196 ans_rev[L] = A[0];
197 return;
198 }
199
200 string convert(N, ’0’);
201 for(int i: other) convert[i] = ’1’;
202
203 vector<int> left, right;
204 for(int i: A)
205 {
206 convert[i] = ’1’;
207 if(check_element(convert)) left.push_back(i);
208 else right.push_back(i);
209 convert[i] = ’0’;
210 }
211
212 int mid = (L + R)/2;
213 solve(L, mid, left, right);
214 solve(mid + 1, R, right, left);
215 }
216
217 std::vector<int> restore_permutation(int n, int w, int r)
218 {
219 N = n;
220 add_strings(0, N - 1, string(N, ’0’));
221
222 compile_set();
223
224 ans_rev.resize(N, -1);
225 vector<int> A;
226 for(int i = 0; i < N; i++) A.push_back(i);
227 solve(0, N - 1, A, {});
228
229 vector<int> ans(N);
230 for(int i = 0; i < N; i++) ans[ans_rev[i]] = i;
231 return ans;
232 }
233 /*
234 t2-t1 = 0
CAPITOLUL 5. IOI 2016 658

235 t3-t2 = 0.015


236 t4-t3 = 0
237
238 Process returned 0 (0x0) execution time : 0.094 s
239 Press any key to continue.
240 */

Listing 5.5.6: messy-23726.cpp


1 // https://oj.uz/submission/23726
2
3 #include <bits/stdc++.h>
4 #include "messy.h"
5
6 using namespace std;
7
8 // --------------- begin grader -------------------
9
10 #include <vector>
11 #include <cstdio>
12 #include <string>
13 #include <set>
14 #include <cstdlib>
15 #include <iostream>
16
17 #include<ctime>
18
19 #include "messy.h"
20
21 using namespace std;
22
23 namespace helper
24 {
25 set<string> set_;
26 bool compiled = false;
27 int n;
28 vector<int> p;
29 int w;
30 int r;
31
32 int read_int()
33 {
34 int x;
35 cin >> x;
36 return x;
37 }
38 }
39
40 using namespace helper;
41
42 // A convenience function.
43 int get_p(int i)
44 {
45 int ret = p[i];
46 return ret;
47 }
48
49 int main()
50 {
51 auto t1 = clock();
52
53 std::freopen("../tests/subtask_4/014", "r", stdin);
54 std::freopen("4-014.out.txt", "w", stdout);
55
56 n = read_int();
57 w = read_int();
58 r = read_int();
59
60 p = vector<int>(n);
61
62 for (int i = 0; i < n; i++)
63 {
64 p[i] = read_int();
65 }
66
CAPITOLUL 5. IOI 2016 659

67 auto t2 = clock();
68
69 vector<int> answer = restore_permutation(n, w, r);
70
71 auto t3 = clock();
72
73 if (answer.size() != n)
74 {
75 printf("WA\n");
76 return 0;
77 }
78
79 printf("%d", answer[0]);
80
81 for (int i = 1; i < n; i++)
82 {
83 printf(" %d", answer[i]);
84 }
85 printf("\n");
86
87 auto t4 = clock();
88
89 // reset console output
90 freopen("CON", "w", stdout);
91
92 //cout<<"res = "<<res<<"\n";
93
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 return 0;
99 }
100
101 void wa()
102 {
103 printf("WA\n");
104 exit(0);
105 }
106
107 bool check(const string& x)
108 {
109 if ((int)x.length() != n)
110 {
111 return false;
112 }
113 for (int i = 0; i < n; i++)
114 {
115 if (x[i] != ’0’ && x[i] != ’1’)
116 {
117 return false;
118 }
119 }
120 return true;
121 }
122
123 void add_element(string x)
124 {
125 if (--w < 0 || compiled || !check(x))
126 {
127 wa();
128 }
129 set_.insert(x);
130 }
131
132 bool check_element(string x)
133 {
134 if (--r < 0 || !compiled || !check(x))
135 {
136 wa();
137 }
138 return set_.count(x);
139 }
140
141 void compile_set()
142 {
CAPITOLUL 5. IOI 2016 660

143 if (compiled)
144 {
145 wa();
146 }
147 compiled = true;
148 set<string> compiledSet;
149 string compiledElement = string(n, ’ ’);
150 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
151 {
152 string s = *it;
153 for (int i = 0; i < n; i++)
154 {
155 compiledElement[i] = s[get_p(i)];
156 }
157 compiledSet.insert(compiledElement);
158 }
159 set_ = compiledSet;
160 }
161
162 // --------------- end grader -------------------
163
164 const int N = 135;
165
166 // [l, r)
167 void go (int n, int l, int r)
168 {
169 if (l + 1 == r) return;
170 int m = l + r >> 1;
171 string s = "";
172 for (int i = 0; i < n; ++i)
173 {
174 s += ’1’;
175 }
176 for (int i = l; i < r; ++i)
177 {
178 s[i] = ’0’;
179 }
180 for (int i = l; i < m; ++i)
181 {
182 s[i] = ’1’;
183 add_element(s);
184 s[i] = ’0’;
185 }
186 go(n, l, m);
187 go(n, m, r);
188 }
189
190 vector <int> v;
191
192 // no contains positions that don’t belong to this range
193 void kill (int n, int l, int r, string no)
194 {
195 if (l + 1 == r)
196 {
197 for (int i = 0; i < n; ++i)
198 {
199 if (no[i] == ’0’)
200 {
201 v[i] = l;
202 break;
203 }
204 }
205 return;
206 }
207
208 string x = no, y = "";
209 for (int i = 0; i < n; ++i)
210 {
211 y += ’1’;
212 }
213
214 for (int i = 0; i < n; ++i)
215 {
216 if (no[i] == ’0’)
217 {
218 no[i] = ’1’;
CAPITOLUL 5. IOI 2016 661

219 if (check_element(no))
220 {
221 x[i] = ’1’;
222 y[i] = ’0’;
223 }
224 no[i] = ’0’;
225 }
226 }
227 int m = l + r >> 1;
228 kill(n, l, m, y);
229 kill(n, m, r, x);
230 }
231
232 vector <int> restore_permutation (int n, int w, int r)
233 {
234 go(n, 0, n);
235 compile_set();
236 v.resize(n);
237 string s = "";
238 for (int i = 0; i < n; ++i)
239 {
240 s += ’0’;
241 }
242 kill(n, 0, n, s);
243 return v;
244 }
245 /*
246 t2-t1 = 0
247 t3-t2 = 0.015
248 t4-t3 = 0
249
250 Process returned 0 (0x0) execution time : 0.828 s
251 Press any key to continue.
252 */

Listing 5.5.7: messy-23992.cpp


1 // https://oj.uz/submission/23992
2
3 #include "messy.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 // --------------- begin grader -------------------
9
10 #include <vector>
11 #include <cstdio>
12 #include <string>
13 #include <set>
14 #include <cstdlib>
15 #include <iostream>
16
17 #include<ctime>
18
19 #include "messy.h"
20
21 using namespace std;
22
23 namespace helper
24 {
25 set<string> set_;
26 bool compiled = false;
27 int n;
28 vector<int> p;
29 int w;
30 int r;
31
32 int read_int()
33 {
34 int x;
35 cin >> x;
36 return x;
37 }
38 }
CAPITOLUL 5. IOI 2016 662

39
40 using namespace helper;
41
42 // A convenience function.
43 int get_p(int i)
44 {
45 int ret = p[i];
46 return ret;
47 }
48
49 int main()
50 {
51 auto t1 = clock();
52
53 std::freopen("../tests/subtask_4/014", "r", stdin);
54 std::freopen("4-014.out.txt", "w", stdout);
55
56 n = read_int();
57 w = read_int();
58 r = read_int();
59
60 p = vector<int>(n);
61
62 for (int i = 0; i < n; i++)
63 {
64 p[i] = read_int();
65 }
66
67 auto t2 = clock();
68
69 vector<int> answer = restore_permutation(n, w, r);
70
71 auto t3 = clock();
72
73 if (answer.size() != n)
74 {
75 printf("WA\n");
76 return 0;
77 }
78
79 printf("%d", answer[0]);
80
81 for (int i = 1; i < n; i++)
82 {
83 printf(" %d", answer[i]);
84 }
85 printf("\n");
86
87 auto t4 = clock();
88
89 // reset console output
90 freopen("CON", "w", stdout);
91
92 //cout<<"res = "<<res<<"\n";
93
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 return 0;
99 }
100
101 void wa()
102 {
103 printf("WA\n");
104 exit(0);
105 }
106
107 bool check(const string& x)
108 {
109 if ((int)x.length() != n)
110 {
111 return false;
112 }
113 for (int i = 0; i < n; i++)
114 {
CAPITOLUL 5. IOI 2016 663

115 if (x[i] != ’0’ && x[i] != ’1’)


116 {
117 return false;
118 }
119 }
120 return true;
121 }
122
123 void add_element(string x)
124 {
125 if (--w < 0 || compiled || !check(x))
126 {
127 wa();
128 }
129 set_.insert(x);
130 }
131
132 bool check_element(string x)
133 {
134 if (--r < 0 || !compiled || !check(x))
135 {
136 wa();
137 }
138 return set_.count(x);
139 }
140
141 void compile_set()
142 {
143 if (compiled)
144 {
145 wa();
146 }
147 compiled = true;
148 set<string> compiledSet;
149 string compiledElement = string(n, ’ ’);
150 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
151 {
152 string s = *it;
153 for (int i = 0; i < n; i++)
154 {
155 compiledElement[i] = s[get_p(i)];
156 }
157 compiledSet.insert(compiledElement);
158 }
159 set_ = compiledSet;
160 }
161
162 // --------------- end grader -------------------
163
164 //int n, occ[128];
165 int occ[128];
166
167 void solve(int s, int e)
168 {
169 if(s == e) return;
170 string str;
171 for(int j=0; j<n; j++)
172 {
173 str.push_back(’0’);
174 }
175 for(int j=s; j<=e; j++)
176 {
177 str[j] = ’1’;
178 }
179 int m = (s+e)/2;
180 for(int j=m+1; j<=e; j++)
181 {
182 str[j] = ’0’;
183 add_element(str);
184 str[j] = ’1’;
185 }
186 solve(s, m);
187 solve(m+1, e);
188 }
189
190 void solve2(int s, int e, vector<int> c)
CAPITOLUL 5. IOI 2016 664

191 {
192 if(s == e)
193 {
194 occ[c[0]] = s;
195 return;
196 }
197 int m = (s+e)/2;
198 string str;
199 for(int i=0; i<n; i++)
200 {
201 str.push_back(’0’);
202 }
203 for(auto &i : c)
204 {
205 str[i] = ’1’;
206 }
207 vector<int> l, h;
208 for(auto &j : c)
209 {
210 str[j] = ’0’;
211 if(check_element(str)) h.push_back(j);
212 else l.push_back(j);
213 str[j] = ’1’;
214 }
215 solve2(s, m, l);
216 solve2(m+1, e, h);
217 }
218
219 vector<int> restore_permutation(int _n, int w, int r)
220 {
221 n = _n;
222 solve(0, n-1);
223 compile_set();
224 vector<int> v;
225 for(int i=0; i<n; i++) v.push_back(i);
226 solve2(0, n-1, v);
227 vector<int> dap;
228 for(int i=0; i<n; i++) dap.push_back(occ[i]);
229 return dap;
230 }
231 /*
232 t2-t1 = 0
233 t3-t2 = 0
234 t4-t3 = 0
235
236 Process returned 0 (0x0) execution time : 0.984 s
237 Press any key to continue.
238 */

Listing 5.5.8: messy-59231.cpp


1 // https://oj.uz/submission/59231
2
3 #include "messy.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 // --------------- begin grader -------------------
9
10 #include <vector>
11 #include <cstdio>
12 #include <string>
13 #include <set>
14 #include <cstdlib>
15 #include <iostream>
16
17 #include<ctime>
18
19 #include "messy.h"
20
21 using namespace std;
22
23 namespace helper
24 {
CAPITOLUL 5. IOI 2016 665

25 set<string> set_;
26 bool compiled = false;
27 int n;
28 vector<int> p;
29 int w;
30 int r;
31
32 int read_int()
33 {
34 int x;
35 cin >> x;
36 return x;
37 }
38 }
39
40 using namespace helper;
41
42 // A convenience function.
43 int get_p(int i)
44 {
45 int ret = p[i];
46 return ret;
47 }
48
49 int main()
50 {
51 auto t1 = clock();
52
53 std::freopen("../tests/subtask_4/014", "r", stdin);
54 std::freopen("4-014.out.txt", "w", stdout);
55
56 n = read_int();
57 w = read_int();
58 r = read_int();
59
60 p = vector<int>(n);
61
62 for (int i = 0; i < n; i++)
63 {
64 p[i] = read_int();
65 }
66
67 auto t2 = clock();
68
69 vector<int> answer = restore_permutation(n, w, r);
70
71 auto t3 = clock();
72
73 if (answer.size() != n)
74 {
75 printf("WA\n");
76 return 0;
77 }
78
79 printf("%d", answer[0]);
80
81 for (int i = 1; i < n; i++)
82 {
83 printf(" %d", answer[i]);
84 }
85 printf("\n");
86
87 auto t4 = clock();
88
89 // reset console output
90 freopen("CON", "w", stdout);
91
92 //cout<<"res = "<<res<<"\n";
93
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 return 0;
99 }
100
CAPITOLUL 5. IOI 2016 666

101 void wa()


102 {
103 printf("WA\n");
104 exit(0);
105 }
106
107 bool check(const string& x)
108 {
109 if ((int)x.length() != n)
110 {
111 return false;
112 }
113 for (int i = 0; i < n; i++)
114 {
115 if (x[i] != ’0’ && x[i] != ’1’)
116 {
117 return false;
118 }
119 }
120 return true;
121 }
122
123 void add_element(string x)
124 {
125 if (--w < 0 || compiled || !check(x))
126 {
127 wa();
128 }
129 set_.insert(x);
130 }
131
132 bool check_element(string x)
133 {
134 if (--r < 0 || !compiled || !check(x))
135 {
136 wa();
137 }
138 return set_.count(x);
139 }
140
141 void compile_set()
142 {
143 if (compiled)
144 {
145 wa();
146 }
147 compiled = true;
148 set<string> compiledSet;
149 string compiledElement = string(n, ’ ’);
150 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
151 {
152 string s = *it;
153 for (int i = 0; i < n; i++)
154 {
155 compiledElement[i] = s[get_p(i)];
156 }
157 compiledSet.insert(compiledElement);
158 }
159 set_ = compiledSet;
160 }
161
162 // --------------- end grader -------------------
163
164 string s;
165 //int n;
166 vector<int> ans;
167
168 void rec1(int l, int r)
169 {
170 if(l==r)
171 return;
172 int m=(l+r)/2;
173 for(int i=0; i<l; ++i)
174 s[i]=’1’;
175 for(int i=l; i<=r; ++i)
176 s[i]=’0’;
CAPITOLUL 5. IOI 2016 667

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


178 s[i]=’1’;
179 for(int i=l; i<=m; ++i)
180 {
181 s[i]=’1’;
182 add_element(s);
183 s[i]=’0’;
184 }
185 rec1(l, m);
186 rec1(m+1, r);
187 }
188
189 void rec2(int l, int r, vector<int> &v)
190 {
191 if(l==r)
192 {
193 ans[v[0]]=l;
194 return;
195 }
196 int m=(l+r)/2;
197 for(int i=0; i<n; ++i)
198 s[i]=’1’;
199 for(int i : v)
200 s[i]=’0’;
201 vector<int> vl, vr;
202 for(int i : v)
203 {
204 s[i]=’1’;
205 if(check_element(s))
206 vl.push_back(i);
207 else
208 vr.push_back(i);
209 s[i]=’0’;
210 }
211 rec2(l, m, vl);
212 rec2(m+1, r, vr);
213 }
214
215 vector<int> restore_permutation(int n2, int w, int r)
216 {
217 n=n2;
218 s="";
219 for(int i=0; i<n; ++i)
220 s+=’0’;
221 rec1(0, n-1);
222 compile_set();
223 vector<int> v;
224 for(int i=0; i<n; ++i)
225 v.push_back(i);
226 ans=vector<int>(n);
227 rec2(0, n-1, v);
228 return ans;
229 }
230 /*
231 t2-t1 = 0
232 t3-t2 = 0
233 t4-t3 = 0
234
235 Process returned 0 (0x0) execution time : 0.125 s
236 Press any key to continue.
237 */

Listing 5.5.9: messy-66964.cpp


1 // https://oj.uz/submission/66964
2
3 #include <vector>
4 #include "messy.h"
5 #include <string>
6
7 using namespace std;
8
9 // --------------- begin grader -------------------
10
11 #include <vector>
CAPITOLUL 5. IOI 2016 668

12 #include <cstdio>
13 #include <string>
14 #include <set>
15 #include <cstdlib>
16 #include <iostream>
17
18 #include<ctime>
19
20 #include "messy.h"
21
22 using namespace std;
23
24 namespace helper
25 {
26 set<string> set_;
27 bool compiled = false;
28 int n;
29 vector<int> p;
30 int w;
31 int r;
32
33 int read_int()
34 {
35 int x;
36 cin >> x;
37 return x;
38 }
39 }
40
41 using namespace helper;
42
43 // A convenience function.
44 int get_p(int i)
45 {
46 int ret = p[i];
47 return ret;
48 }
49
50 int main()
51 {
52 auto t1 = clock();
53
54 std::freopen("../tests/subtask_4/014", "r", stdin);
55 std::freopen("4-014.out.txt", "w", stdout);
56
57 n = read_int();
58 w = read_int();
59 r = read_int();
60
61 p = vector<int>(n);
62
63 for (int i = 0; i < n; i++)
64 {
65 p[i] = read_int();
66 }
67
68 auto t2 = clock();
69
70 vector<int> answer = restore_permutation(n, w, r);
71
72 auto t3 = clock();
73
74 if (answer.size() != n)
75 {
76 printf("WA\n");
77 return 0;
78 }
79
80 printf("%d", answer[0]);
81
82 for (int i = 1; i < n; i++)
83 {
84 printf(" %d", answer[i]);
85 }
86 printf("\n");
87
CAPITOLUL 5. IOI 2016 669

88 auto t4 = clock();
89
90 // reset console output
91 freopen("CON", "w", stdout);
92
93 //cout<<"res = "<<res<<"\n";
94
95 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
97 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
98
99 return 0;
100 }
101
102 void wa()
103 {
104 printf("WA\n");
105 exit(0);
106 }
107
108 bool check(const string& x)
109 {
110 if ((int)x.length() != n)
111 {
112 return false;
113 }
114 for (int i = 0; i < n; i++)
115 {
116 if (x[i] != ’0’ && x[i] != ’1’)
117 {
118 return false;
119 }
120 }
121 return true;
122 }
123
124 void add_element(string x)
125 {
126 if (--w < 0 || compiled || !check(x))
127 {
128 wa();
129 }
130 set_.insert(x);
131 }
132
133 bool check_element(string x)
134 {
135 if (--r < 0 || !compiled || !check(x))
136 {
137 wa();
138 }
139 return set_.count(x);
140 }
141
142 void compile_set()
143 {
144 if (compiled)
145 {
146 wa();
147 }
148 compiled = true;
149 set<string> compiledSet;
150 string compiledElement = string(n, ’ ’);
151 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
152 {
153 string s = *it;
154 for (int i = 0; i < n; i++)
155 {
156 compiledElement[i] = s[get_p(i)];
157 }
158 compiledSet.insert(compiledElement);
159 }
160 set_ = compiledSet;
161 }
162
163 // --------------- end grader -------------------
CAPITOLUL 5. IOI 2016 670

164
165 // [l..r)
166 void Add(int l, int r, string base)
167 {
168 if (l + 1 == r) return;
169 int mid = (l + r) / 2;
170 for (int i = l; i < mid; ++i)
171 {
172 base[i] = ’1’;
173 add_element(base.c_str());
174 base[i] = ’0’;
175 }
176
177 // fill one side, recurse in other side
178 fill(base.begin() + l, base.begin() + mid, ’1’);
179 Add(mid, r, base);
180 fill(base.begin() + l, base.begin() + mid, ’0’);
181 fill(base.begin() + mid, base.begin() + r, ’1’);
182 Add(l, mid, base);
183 }
184
185 vector<int> ans;
186
187 void Solve(int l, int r, vector<int> miss, string base)
188 {
189 if (l + 1 == r)
190 {
191 ans[miss[0]] = l;
192 return;
193 }
194 vector<int> grL, grR;
195 for (int x: miss)
196 {
197 base[x] = ’1’;
198 if (check_element(base.c_str())) grL.push_back(x);
199 else grR.push_back(x);
200 base[x] = ’0’;
201 }
202 int mid = (l + r) / 2;
203
204 // fill one side, recurse in other side
205 for (int x: grR) base[x] = ’1’;
206 Solve(l, mid, grL, base);
207 for (int x: grR) base[x] = ’0’;
208 for (int x: grL) base[x] = ’1’;
209 Solve(mid, r, grR, base);
210 }
211
212 vector<int> restore_permutation(int n, int w, int r)
213 {
214 Add(0, n, string(n, ’0’));
215
216 compile_set();
217
218 ans.resize(n);
219 vector<int> all;
220 for (int i = 0; i < n; ++i) all.push_back(i);
221 Solve(0, n, all, string(n, ’0’));
222 return ans;
223 }
224 /*
225 t2-t1 = 0
226 t3-t2 = 0.015
227 t4-t3 = 0
228
229 Process returned 0 (0x0) execution time : 1.152 s
230 Press any key to continue.
231 */

Listing 5.5.10: messy-67587.cpp


1 // https://oj.uz/submission/67587
2
3 #include <bits/stdc++.h>
4
CAPITOLUL 5. IOI 2016 671

5 #include "messy.h"
6 //#include "grader.cpp"
7
8 using namespace std;
9
10 // --------------- begin grader -------------------
11
12 #include <vector>
13 #include <cstdio>
14 #include <string>
15 #include <set>
16 #include <cstdlib>
17 #include <iostream>
18
19 #include<ctime>
20
21 #include "messy.h"
22
23 using namespace std;
24
25 namespace helper
26 {
27 set<string> set_;
28 bool compiled = false;
29 int n;
30 vector<int> p;
31 int w;
32 int r;
33
34 int read_int()
35 {
36 int x;
37 cin >> x;
38 return x;
39 }
40 }
41
42 using namespace helper;
43
44 // A convenience function.
45 int get_p(int i)
46 {
47 int ret = p[i];
48 return ret;
49 }
50
51 int main()
52 {
53 auto t1 = clock();
54
55 std::freopen("../tests/subtask_4/014", "r", stdin);
56 std::freopen("4-014.out.txt", "w", stdout);
57
58 n = read_int();
59 w = read_int();
60 r = read_int();
61
62 p = vector<int>(n);
63
64 for (int i = 0; i < n; i++)
65 {
66 p[i] = read_int();
67 }
68
69 auto t2 = clock();
70
71 vector<int> answer = restore_permutation(n, w, r);
72
73 auto t3 = clock();
74
75 if (answer.size() != n)
76 {
77 printf("WA\n");
78 return 0;
79 }
80
CAPITOLUL 5. IOI 2016 672

81 printf("%d", answer[0]);
82
83 for (int i = 1; i < n; i++)
84 {
85 printf(" %d", answer[i]);
86 }
87 printf("\n");
88
89 auto t4 = clock();
90
91 // reset console output
92 freopen("CON", "w", stdout);
93
94 //cout<<"res = "<<res<<"\n";
95
96 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
97 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
98 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
99
100 return 0;
101 }
102
103 void wa()
104 {
105 printf("WA\n");
106 exit(0);
107 }
108
109 bool check(const string& x)
110 {
111 if ((int)x.length() != n)
112 {
113 return false;
114 }
115 for (int i = 0; i < n; i++)
116 {
117 if (x[i] != ’0’ && x[i] != ’1’)
118 {
119 return false;
120 }
121 }
122 return true;
123 }
124
125 void add_element(string x)
126 {
127 if (--w < 0 || compiled || !check(x))
128 {
129 wa();
130 }
131 set_.insert(x);
132 }
133
134 bool check_element(string x)
135 {
136 if (--r < 0 || !compiled || !check(x))
137 {
138 wa();
139 }
140 return set_.count(x);
141 }
142
143 void compile_set()
144 {
145 if (compiled)
146 {
147 wa();
148 }
149 compiled = true;
150 set<string> compiledSet;
151 string compiledElement = string(n, ’ ’);
152 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
153 {
154 string s = *it;
155 for (int i = 0; i < n; i++)
156 {
CAPITOLUL 5. IOI 2016 673

157 compiledElement[i] = s[get_p(i)];


158 }
159 compiledSet.insert(compiledElement);
160 }
161 set_ = compiledSet;
162 }
163
164 // --------------- end grader -------------------
165
166 //int n;
167 string s;
168 vector <int> ans, vv;
169
170 void fun(int l, int r)
171 {
172 if(l >= r) return ;
173 s = "";
174
175 for(int i = 0; i < n; i ++)
176 {
177 if(l <= i && i <= r)
178 s += ’0’;
179 else
180 s += ’1’;
181 }
182
183 int md = (l + r) >> 1;
184 for(int i = l; i <= md; i ++)
185 {
186 s[i] = ’1’;
187 add_element(s);
188 s[i] = ’0’;
189 }
190 fun(l, md);
191 fun(md + 1, r);
192 }
193
194 void rec(int l, int r)
195 {
196 if(l >= r)
197 {
198 return ;
199 }
200 s.resize(n);
201 for(int i = 0; i < n; i ++)
202 {
203 if(l <= i && i <= r)
204 s[ans[i]] = ’0’;
205 else
206 s[ans[i]] = ’1’;
207 }
208 vector < int > v1, v2;
209 int md = (l + r) >> 1;
210 for(int i = l; i <= r; i ++)
211 {
212 s[ans[i]] = ’1’;
213 if(check_element(s))
214 v1.push_back(ans[i]);
215 else
216 v2.push_back(ans[i]);
217 s[ans[i]] = ’0’;
218 }
219
220 int p1 = 0, p2 = 0;
221 for(int i = l; i <= r; i ++)
222 {
223 if(i <= md)
224 {
225 ans[i] = v1[p1 ++];
226 }
227 else
228 {
229 ans[i] = v2[p2 ++];
230 }
231 }
232
CAPITOLUL 5. IOI 2016 674

233 rec(l, md);


234 rec(md + 1, r);
235 }
236
237 vector<int> restore_permutation(int NN, int w, int r)
238 {
239 n = NN;
240 ans.resize(n);
241 vv.resize(n);
242 for(int i = 0; i < n; i ++)
243 ans[i] = i;
244 fun(0, n - 1);
245 compile_set();
246 rec(0, n - 1);
247 for(int i = 0; i < n; i ++)
248 {
249 vv[ans[i]] = i;
250 }
251 return vv;
252 }
253 /*
254 t2-t1 = 0
255 t3-t2 = 0.015
256 t4-t3 = 0
257
258 Process returned 0 (0x0) execution time : 0.109 s
259 Press any key to continue.
260 */

Listing 5.5.11: messy-70792.cpp


1 // https://oj.uz/submission/70792
2
3 #include <vector>
4 #include<string>
5 #include "messy.h"
6 using namespace std;
7
8 // --------------- begin grader -------------------
9
10 #include <vector>
11 #include <cstdio>
12 #include <string>
13 #include <set>
14 #include <cstdlib>
15 #include <iostream>
16
17 #include<ctime>
18
19 #include "messy.h"
20
21 using namespace std;
22
23 namespace helper
24 {
25 set<string> set_;
26 bool compiled = false;
27 int n;
28 vector<int> p;
29 int w;
30 int r;
31
32 int read_int()
33 {
34 int x;
35 cin >> x;
36 return x;
37 }
38 }
39
40 using namespace helper;
41
42 // A convenience function.
43 int get_p(int i)
44 {
CAPITOLUL 5. IOI 2016 675

45 int ret = p[i];


46 return ret;
47 }
48
49 int main()
50 {
51 auto t1 = clock();
52
53 std::freopen("../tests/subtask_4/014", "r", stdin);
54 std::freopen("4-014.out.txt", "w", stdout);
55
56 n = read_int();
57 w = read_int();
58 r = read_int();
59
60 p = vector<int>(n);
61
62 for (int i = 0; i < n; i++)
63 {
64 p[i] = read_int();
65 }
66
67 auto t2 = clock();
68
69 vector<int> answer = restore_permutation(n, w, r);
70
71 auto t3 = clock();
72
73 if (answer.size() != n)
74 {
75 printf("WA\n");
76 return 0;
77 }
78
79 printf("%d", answer[0]);
80
81 for (int i = 1; i < n; i++)
82 {
83 printf(" %d", answer[i]);
84 }
85 printf("\n");
86
87 auto t4 = clock();
88
89 // reset console output
90 freopen("CON", "w", stdout);
91
92 //cout<<"res = "<<res<<"\n";
93
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 return 0;
99 }
100
101 void wa()
102 {
103 printf("WA\n");
104 exit(0);
105 }
106
107 bool check(const string& x)
108 {
109 if ((int)x.length() != n)
110 {
111 return false;
112 }
113 for (int i = 0; i < n; i++)
114 {
115 if (x[i] != ’0’ && x[i] != ’1’)
116 {
117 return false;
118 }
119 }
120 return true;
CAPITOLUL 5. IOI 2016 676

121 }
122
123 void add_element(string x)
124 {
125 if (--w < 0 || compiled || !check(x))
126 {
127 wa();
128 }
129 set_.insert(x);
130 }
131
132 bool check_element(string x)
133 {
134 if (--r < 0 || !compiled || !check(x))
135 {
136 wa();
137 }
138 return set_.count(x);
139 }
140
141 void compile_set()
142 {
143 if (compiled)
144 {
145 wa();
146 }
147 compiled = true;
148 set<string> compiledSet;
149 string compiledElement = string(n, ’ ’);
150 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
151 {
152 string s = *it;
153 for (int i = 0; i < n; i++)
154 {
155 compiledElement[i] = s[get_p(i)];
156 }
157 compiledSet.insert(compiledElement);
158 }
159 set_ = compiledSet;
160 }
161
162 // --------------- end grader -------------------
163
164 //int n;
165 string s[1000];
166 vector<int> g;
167 void add_build(int l,int r,int v)
168 {
169 if(l==r)
170 return;
171 string t;
172 for(int i=0;i<n;i++)
173 {
174 if(i>=l && i<=r)
175 t+=’0’;
176 else
177 t+=’1’;
178 }
179 int mid=(l+r)/2;
180 for(int i=l;i<=mid;i++)
181 {
182 t[i]=’1’;
183 add_element(t);
184 t[i]=’0’;
185 }
186 add_build(l,mid,2*v);
187 add_build(mid+1,r,2*v+1);
188 }
189
190 void check_build(int l,int r,int v)
191 {
192 if(l==r)
193 {
194 for(int i=0;i<n;i++)
195 {
196 if(s[v][i]==’0’)
CAPITOLUL 5. IOI 2016 677

197 {
198 g[i]=l;
199 break;
200 }
201 }
202 return;
203 }
204
205 s[2*v]=s[v];
206 s[2*v+1]=s[v];
207 for(int i=0;i<n;i++)
208 {
209 if(s[v][i]==’1’)
210 continue;
211 s[v][i]=’1’;
212 if(check_element(s[v]))
213 s[2*v+1][i]=’1’;
214 else
215 s[2*v][i]=’1’;
216 s[v][i]=’0’;
217 }
218
219 int mid=(l+r)/2;
220 check_build(l,mid,2*v);
221 check_build(mid+1,r,2*v+1);
222 }
223
224 std::vector<int> restore_permutation(int N, int w, int r)
225 {
226 n=N;
227 for(int i=0;i<n;i++)
228 g.push_back(0);
229 add_build(0,n-1,1);
230 compile_set();
231 for(int i=0;i<n;i++)
232 s[1]+=’0’;
233 check_build(0,n-1,1);
234 return g;
235 }
236 /*
237 t2-t1 = 0
238 t3-t2 = 0
239 t4-t3 = 0
240
241 Process returned 0 (0x0) execution time : 0.125 s
242 Press any key to continue.
243 */

Listing 5.5.12: messy-71456.cpp


1 // https://oj.uz/submission/71456
2
3 #include <bits/stdc++.h>
4 #include "messy.h"
5 using namespace std;
6
7 // --------------- begin grader -------------------
8
9 #include <vector>
10 #include <cstdio>
11 #include <string>
12 #include <set>
13 #include <cstdlib>
14 #include <iostream>
15
16 #include<ctime>
17
18 #include "messy.h"
19
20 using namespace std;
21
22 namespace helper
23 {
24 set<string> set_;
25 bool compiled = false;
CAPITOLUL 5. IOI 2016 678

26 int n;
27 vector<int> p;
28 int w;
29 int r;
30
31 int read_int()
32 {
33 int x;
34 cin >> x;
35 return x;
36 }
37 }
38
39 using namespace helper;
40
41 // A convenience function.
42 int get_p(int i)
43 {
44 int ret = p[i];
45 return ret;
46 }
47
48 int main()
49 {
50 auto t1 = clock();
51
52 std::freopen("../tests/subtask_4/014", "r", stdin);
53 std::freopen("4-014.out.txt", "w", stdout);
54
55 n = read_int();
56 w = read_int();
57 r = read_int();
58
59 p = vector<int>(n);
60
61 for (int i = 0; i < n; i++)
62 {
63 p[i] = read_int();
64 }
65
66 auto t2 = clock();
67
68 vector<int> answer = restore_permutation(n, w, r);
69
70 auto t3 = clock();
71
72 if (answer.size() != n)
73 {
74 printf("WA\n");
75 return 0;
76 }
77
78 printf("%d", answer[0]);
79
80 for (int i = 1; i < n; i++)
81 {
82 printf(" %d", answer[i]);
83 }
84 printf("\n");
85
86 auto t4 = clock();
87
88 // reset console output
89 freopen("CON", "w", stdout);
90
91 //cout<<"res = "<<res<<"\n";
92
93 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
94 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
96
97 return 0;
98 }
99
100 void wa()
101 {
CAPITOLUL 5. IOI 2016 679

102 printf("WA\n");
103 exit(0);
104 }
105
106 bool check(const string& x)
107 {
108 if ((int)x.length() != n)
109 {
110 return false;
111 }
112 for (int i = 0; i < n; i++)
113 {
114 if (x[i] != ’0’ && x[i] != ’1’)
115 {
116 return false;
117 }
118 }
119 return true;
120 }
121
122 void add_element(string x)
123 {
124 if (--w < 0 || compiled || !check(x))
125 {
126 wa();
127 }
128 set_.insert(x);
129 }
130
131 bool check_element(string x)
132 {
133 if (--r < 0 || !compiled || !check(x))
134 {
135 wa();
136 }
137 return set_.count(x);
138 }
139
140 void compile_set()
141 {
142 if (compiled)
143 {
144 wa();
145 }
146 compiled = true;
147 set<string> compiledSet;
148 string compiledElement = string(n, ’ ’);
149 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
150 {
151 string s = *it;
152 for (int i = 0; i < n; i++)
153 {
154 compiledElement[i] = s[get_p(i)];
155 }
156 compiledSet.insert(compiledElement);
157 }
158 set_ = compiledSet;
159 }
160
161 // --------------- end grader -------------------
162
163 //int n;
164 //vector<int> p;
165
166 void add(int l, int r)
167 {
168 if(l == r) return;
169 string base(n, ’1’);
170 int m = l + r >> 1;
171 for(int i = l; i <= r; i++) base[i] = ’0’;
172 for(int i = l; i <= m; i++)
173 {
174 string s = base;
175 s[i] = ’1’;
176 add_element(s);
177 }
CAPITOLUL 5. IOI 2016 680

178 add(l, m);


179 add(m + 1, r);
180 }
181
182 void solve(int l, int r, vector<int> v)
183 {
184 if(l == r)
185 {
186 p[v[0]] = l;
187 return;
188 }
189
190 int m = l + r >> 1;
191 string base(n, ’1’);
192 vector<int> L, R;
193 for(int i : v) base[i] = ’0’;
194 for(int i : v)
195 {
196 string s = base;
197 s[i] = ’1’;
198 if(check_element(s))
199 L.push_back(i);
200 else R.push_back(i);
201 }
202 solve(l, m, L);
203 solve(m + 1, r, R);
204 }
205
206 vector<int> restore_permutation(int N, int w, int r)
207 {
208 n = N;
209 add(0, n - 1);
210 compile_set();
211 vector<int> v;
212 for(int i = 0; i < n; i++)
213 v.push_back(i);
214 p = vector<int>(n, 0);
215 solve(0, n - 1, v);
216 return p;
217 }
218 /*
219 t2-t1 = 0
220 t3-t2 = 0
221 t4-t3 = 0
222
223 Process returned 0 (0x0) execution time : 0.031 s
224 Press any key to continue.
225 */

Listing 5.5.13: messy-102062.cpp


1 // https://oj.uz/submission/102062
2
3 #pragma GCC optimize("Ofast")
4 #include <bits/stdc++.h>
5 #define pb push_back
6 #define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
7 #define F first
8 #define S second
9 #define ET cout << "\n"
10 #define MP make_pair
11 #define MEM(i,j) memset(i,j,sizeof i)
12 #define ALL(v) v.begin(),v.end()
13 #define DB(a,s,e) {for(int i=s;i<e;++i) cout << a[i] << " ";ET;}
14
15 using namespace std;
16
17 // --------------- begin grader -------------------
18
19 #include <vector>
20 #include <cstdio>
21 #include <string>
22 #include <set>
23 #include <cstdlib>
24 #include <iostream>
CAPITOLUL 5. IOI 2016 681

25
26 #include<ctime>
27
28 #include "messy.h"
29
30 using namespace std;
31
32 namespace helper
33 {
34 set<string> set_;
35 bool compiled = false;
36 int n;
37 vector<int> p;
38 int w;
39 int r;
40
41 int read_int()
42 {
43 int x;
44 cin >> x;
45 return x;
46 }
47 }
48
49 using namespace helper;
50
51 // A convenience function.
52 int get_p(int i)
53 {
54 int ret = p[i];
55 return ret;
56 }
57
58 int main()
59 {
60 auto t1 = clock();
61
62 std::freopen("../tests/subtask_4/014", "r", stdin);
63 std::freopen("4-014.out.txt", "w", stdout);
64
65 n = read_int();
66 w = read_int();
67 r = read_int();
68
69 p = vector<int>(n);
70
71 for (int i = 0; i < n; i++)
72 {
73 p[i] = read_int();
74 }
75
76 auto t2 = clock();
77
78 vector<int> answer = restore_permutation(n, w, r);
79
80 auto t3 = clock();
81
82 if (answer.size() != n)
83 {
84 printf("WA\n");
85 return 0;
86 }
87
88 printf("%d", answer[0]);
89
90 for (int i = 1; i < n; i++)
91 {
92 printf(" %d", answer[i]);
93 }
94 printf("\n");
95
96 auto t4 = clock();
97
98 // reset console output
99 freopen("CON", "w", stdout);
100
CAPITOLUL 5. IOI 2016 682

101 //cout<<"res = "<<res<<"\n";


102
103 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
104 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
105 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
106
107 return 0;
108 }
109
110 void wa()
111 {
112 printf("WA\n");
113 exit(0);
114 }
115
116 bool check(const string& x)
117 {
118 if ((int)x.length() != n)
119 {
120 return false;
121 }
122 for (int i = 0; i < n; i++)
123 {
124 if (x[i] != ’0’ && x[i] != ’1’)
125 {
126 return false;
127 }
128 }
129 return true;
130 }
131
132 void add_element(string x)
133 {
134 if (--w < 0 || compiled || !check(x))
135 {
136 wa();
137 }
138 set_.insert(x);
139 }
140
141 bool check_element(string x)
142 {
143 if (--r < 0 || !compiled || !check(x))
144 {
145 wa();
146 }
147 return set_.count(x);
148 }
149
150 void compile_set()
151 {
152 if (compiled)
153 {
154 wa();
155 }
156 compiled = true;
157 set<string> compiledSet;
158 string compiledElement = string(n, ’ ’);
159 for (set<string>::iterator it = set_.begin(); it != set_.end(); it++)
160 {
161 string s = *it;
162 for (int i = 0; i < n; i++)
163 {
164 compiledElement[i] = s[get_p(i)];
165 }
166 compiledSet.insert(compiledElement);
167 }
168 set_ = compiledSet;
169 }
170
171 // --------------- end grader -------------------
172
173 typedef long long ll;
174 typedef pair<int,int> pii;
175 typedef pair<ll,ll> pll;
176
CAPITOLUL 5. IOI 2016 683

177 #include "messy.h"


178
179 string t;
180 vector<int> ans;
181
182 void add(int l,int r)
183 {
184 if(l==r) return;
185 int m=l+r>>1;
186 for(int i=l;i<=m;++i)
187 t[i]=’1’,add_element(t),t[i]=’0’;
188 for(int i=m+1;i<=r;++i)
189 t[i]=’1’;
190 add(l,m);
191 for(int i=m+1;i<=r;++i)
192 t[i]=’0’;
193 for(int i=l;i<=m;++i)
194 t[i]=’1’;
195 add(m+1,r);
196 for(int i=l;i<=m;++i)
197 t[i]=’0’;
198 }
199
200 void query(int l,int r,vector<int> &v)
201 {
202 if(l==r) return ans[v[0]]=l,void();
203 vector<int> L,R;
204 for(int i=0;i<v.size();++i)
205 {
206 t[v[i]]=’1’;
207 if(check_element(t)) L.pb(v[i]);
208 else R.pb(v[i]);
209 t[v[i]]=’0’;
210 }
211 int m=l+r>>1;
212 for(int i:R)
213 t[i]=’1’;
214 query(l,m,L);
215 for(int i:R)
216 t[i]=’0’;
217 for(int i:L)
218 t[i]=’1’;
219 query(m+1,r,R);
220 for(int i:L)
221 t[i]=’0’;
222 }
223
224 vector<int> restore_permutation(int n, int w, int r)
225 {
226 vector<int> ls;
227 ans.resize(n),t.resize(n,’0’);
228 for(int i=0;i<n;++i)
229 ls.pb(i);
230 add(0,n-1);
231 compile_set();
232 query(0,n-1,ls);
233 return ans;
234 }
235 /*
236 t2-t1 = 0
237 t3-t2 = 0
238 t4-t3 = 0
239
240 Process returned 0 (0x0) execution time : 0.047 s
241 Press any key to continue.
242 */
CAPITOLUL 5. IOI 2016 684

5.5.3 *Rezolvare detaliat 

5.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 fotograe în rezoluµie joas  a unei zone p trate a suprafeµei planetei. Pe fotograe
pot  observate mai multe semne de prezenµ  a unei civilizaµii. Experµii au identicat n puncte de
interes pe fotograe. Punctele sunt numerotate de la 0 la n  1. Acum dorim s  obµinem fotograi
de rezoluµie înalt  care s  conµin  toate aceste n puncte.
Satelitul a divizat aria fotograei 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 a  î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 fotograi 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 fotograate, sau în
afara ei.

Satelitul poate face cel mult k fotograi de rezoluµie înalt .


Dup  ce satelitul nalizeaz  procesul de fotograere, urmeaz  transmiterea c tre baz  a ima-
ginii de înalt  rezoluµie pentru ecare din celulele fotograate (indiferent dac  celula conµine sau
nu puncte de interes). Datele pentru ecare celul  fotograat  vor  transmise o singur  dat ,
chiar dac  celula a fost fotograat  de mai multe ori.
A³adar, urmeaz  s  e selectate pentru fotograere cel mult k suprafeµe p trate, astfel încât:
` ecare celul  care conµine cel puµin un punct de interes va  fotograat  cel puµin o dat , ³i
` num rul celulelor care vor  fotograate cel puµin o dat  va  minimal.

Sarcina ta este s  g se³ti cel mai mic num r total posibil al celulelor fotograate.
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 fotograi care pot  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 fotograate cel puµin
o dat  (în condiµia în care fotograile trebuie s  conµin  toate punctele de interes).

Te rug m s  folose³ti ³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])


CAPITOLUL 5. IOI 2016 685

Î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 fotograi de
rezoluµie înalt .
O soluµie pentru a captura toate cele cinci puncte de interes este de a face dou  fotograi:
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  fotograi,
vor  transmise date despre 41 de celule. Aceast  soluµie nu este optim .
Soluµia optim  folose³te o fotograe pentru a captura imaginea unui p trat 4  4 care va conµine
celulele (0,0) ³i (3,3) ³i o a doua fotograe 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 fotograate, rezultat optim.
Astfel take_photos va returna 25.
De remarcat c  este sucient ca celula (4,6) s  e fotograat  o singur  dat , chiar dac  conµine
dou  puncte de interes.
Acest exemplu este prezentat în gurile care urmeaz . Figura din stânga prezint  caroiajul care
corespunde exemplului. Figura din mijloc prezint  soluµia suboptimal , în care sunt fotograate
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 fotofrae
valid  care conµine unul dintre puncte, îl va conµine ³i pe cel lalt. Prin urmare, este sucient s 
e f cut  o singur  fotograe.
Figura care urmeaz  prezint  acest exemplu ³i soluµia lui optim . În aceast  soluµie satelitul
capteaz  o singur  fotograe 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
CAPITOLUL 5. IOI 2016 686

5.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.
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 rst 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 denition 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 .
CAPITOLUL 5. IOI 2016 687

Subtasks 4 and 5. Here you were required to come up with an optimization of the DP
solution described above. Subtask 4 allowed O n  solutions. One possible solution uses the
2

Knuth's optimization.
2 2
Dene 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 .
It can be rather dicult 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 dierent 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 dierences between two
adjacent values. The following theorem states that these dierences are non-increasing. We'll call
such functions convex.
Theorem: fi,j 1  fi,j ' fi,j  fi,j 1
CAPITOLUL 5. IOI 2016 688

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

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 modied to nd
the minimum number of photos required to achieve the optimum, call it p C .
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 x
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 nd such Copt that p1 p Copt  ' k
and p2 p Copt  1 & k .
This means that all dierences 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 nd Copt , each iteration running in linear time.
Total running time: O n log n  n log m.

5.6.2 Coduri surs 

Listing 5.6.1: checkerAliens.cpp


1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 long long readLong(InStream& in)
8 {
9 //string secret = in.readLine();
10 //if (secret != "098d134608c94f7413faac591054ee35")
11 //{
12 // in.quitf(_sv, "Wrong secret!");
13 //}
14
15 long long res = in.readLong();
16 return res;
17 }
18
19 int main()
20 //int main(int argc, char* argv[])
21 {
22 int argc=4;
23
24 char* argv[] =
25 {
26 (char*)"checker",
27 (char*)"../tests/subtask_6/175", // input
28 (char*)"../tests/subtask_6/175.a", // rezultat corect
29 (char*)"6-175.out.txt", // rezultat de verificat
30 };
31
32 cout<<"argc = "<<argc<<"\n";
CAPITOLUL 5. IOI 2016 689

33 for(int kk=0;kk<argc;kk++)
34 cout<<argv[kk]<<"\n";
35 cout<<"----------------------\n";
36
37 registerTestlibCmd(argc, argv);
38
39 long long pans = readLong(ouf);
40 long long jans = readLong(ans);
41
42 if (pans != jans)
43 {
44 quitf(_wa, "Wrong answer: output = %lld, expected = %lld", pans, jans);
45 }
46 else
47 {
48 quitf(_ok, "Correct answer: answer = %lld", pans);
49 }
50 }

Listing 5.6.2: alien-bsearch.cpp


1 #include<iostream>
2 #include<vector>
3 #include<algorithm>
4 #include<cmath>
5
6 #include<ctime>
7
8 using namespace std;
9
10 #define ll long long
11 #define MAXN 1000001
12 #define pb push_back
13 #define INFI (~(1LL<<63))
14 #define sq(x) ((x) * (x))
15 #define DELTA 0.000001
16
17 struct range
18 {
19 ll s, e;
20 friend bool operator < (const range &a, const range &b)
21 {
22 if (a.s == b.s)
23 return (a.e > b.e) ? true : false;
24 else
25 return (a.s < b.s) ? true : false;
26 }
27 };
28
29 struct point
30 {
31 ll x, y;
32 };
33
34 struct line
35 {
36 ll m, c;
37 };
38
39 struct hull_line
40 {
41 ll m, c;
42 ll k;
43 };
44
45 class Hull
46 {
47 private:
48 int cur, len;
49 hull_line *L;
50 double getx(ll m1, ll c1, ll m2, ll c2);
51
52 public:
53 void addline(ll m, ll c, ll k);
54 pair<ll,ll> getmin(ll x);
CAPITOLUL 5. IOI 2016 690

55 void resetline();
56 Hull();
57 ~Hull();
58 };
59
60 Hull::Hull()
61 {
62 L = (hull_line*)malloc(MAXN * sizeof(hull_line));
63 }
64
65 Hull::~Hull()
66 {
67 free(L);
68 }
69
70 double Hull::getx(ll m1, ll c1, ll m2, ll c2)
71 {
72 return (c2-c1) / (double)(m1-m2);
73 }
74
75 void Hull::addline(ll m, ll c, ll k)
76 {
77 while (len >= 2)
78 {
79 double a = getx(m, c, L[len-1].m, L[len-1].c);
80 double b = getx(m, c, L[len-2].m, L[len-2].c);
81 if (a > b) break;
82 len--;
83 }
84 L[len].m = m;
85 L[len].c = c;
86 L[len].k = k;
87 len++;
88 }
89
90 pair<ll,ll> Hull::getmin(ll x)
91 {
92 if (cur >= len) cur = len-1;
93 while (cur < len-1)
94 {
95 double a = getx(L[cur].m, L[cur].c, L[cur+1].m, L[cur+1].c);
96 if (x > a)
97 cur++;
98 else
99 break;
100 // in case x==a first such line is considered
101 // It can be proven using convex property
102 // that k increase as cur increases
103 }
104 return make_pair(L[cur].m * x + L[cur].c, L[cur].k);
105 }
106
107 void Hull::resetline()
108 {
109 len = 0;
110 cur = 0;
111 }
112
113 pair<ll,ll> hull(vector<range> &r, ll C, Hull &h)
114 {
115 int n = r.size();
116 ll m, c;
117 ll t, lastRes = 0LL, lastK = 0LL;
118 h.resetline();
119 pair<ll,ll> res;
120 for (int i=0; i<n; ++i)
121 {
122 m = -2LL * (r[i].s - 1LL);
123 c = lastRes + sq(r[i].s - 1LL);
124 if (i > 0)
125 {
126 t = max(0LL, r[i-1].e - r[i].s + 1LL);
127 c -= sq(t);
128 }
129 h.addline(m, c, lastK);
130 res = h.getmin(r[i].e);
CAPITOLUL 5. IOI 2016 691

131 lastRes = sq(r[i].e) + C + res.first;


132 lastK = res.second + 1LL;
133 }
134 return make_pair(lastRes, lastK);;
135 }
136
137 ll find(vector<range> &r, int K, int M)
138 {
139 Hull h;
140 int N = r.size();
141 K = min(K, N);
142 ll kl=1LL, kr=(ll)N, cl = sq((ll)M), cr=0LL;
143 ll med;
144 bool found = false;
145 pair<ll,ll> res;
146 if (cl % 2LL == 1LL) cl++; // keep C left to an even integer
147
148 // run binary search on C=[cl,cr]
149 while (kr-kl > 1LL && cl - cr > 1LL)
150 {
151 med = cr + (cl-cr)/2LL;
152 // if (med % 2LL == 1LL) med--; // keep med even
153 res = hull(r, med + 1LL, h);
154 if (K == res.second)
155 {
156 found = true;
157 break;
158 }
159 if (K < res.second)
160 {
161 cr = med;
162 kr = res.second;
163 }
164 else
165 {
166 cl = med;
167 kl = res.second;
168 }
169 }
170
171 if (found)
172 {
173 return res.first - (med+1LL) * K;
174 }
175 else
176 if (kr - kl > 1)
177 {
178 // slope is equal from kl to kr.
179 // Since odd C values are used in hull() it always returns
180 // leftmost k out of those slopes for given C. i.e. slopes are always even
181 res = hull(r, cl+1LL, h);
182 ll fl = res.first - (cl + 1LL) * kl;
183 res = hull(r, cr+1LL, h);
184 ll fr = res.first - (cr + 1LL) * kr;
185 ll h = (fl-fr) / (kr - kl); // interpolate
186 return fl - (K - kl) * h;
187 }
188 else
189 {
190 if (kr == K)
191 {
192 res = hull(r, cr + 1LL, h);
193 return res.first - (cr + 1LL) * K;
194 }
195 else
196 { //kr = K
197 res = hull(r, cl + 1LL, h);
198 return res.first - (cl + 1LL) * kl;
199 }
200 }
201 }
202
203 vector<range> parseRanges(range* r, int n)
204 {
205 sort(&r[0], &r[n]);
206 vector<range> res;
CAPITOLUL 5. IOI 2016 692

207 int cur = 0;


208 res.pb(r[0]);
209 for (int i=1; i<n; ++i)
210 if (r[cur].e < r[i].s || r[cur].e < r[i].e)
211 {
212 cur = i;
213 res.pb(r[i]);
214 }
215 return res;
216 }
217
218 long long take_photos(int n, int m, int k, vector<int> row, vector<int> col)
219 {
220 point *p = (point*)malloc(MAXN * sizeof(point));
221 range *r = (range*)malloc(MAXN * sizeof(range));
222 for (int i=0; i<n; ++i)
223 {
224 r[i].s = min(row[i], col[i]);
225 r[i].e = max(row[i], col[i]);
226 }
227 vector<range> R = parseRanges(r, n);
228 ll res = find(R, k, m);
229
230 return res;
231 }
232
233 // BEGIN CUT
234
235 int main()
236 {
237 auto t1 = clock();
238
239 std::freopen("../tests/subtask_6/175", "r", stdin);
240 std::freopen("6-175.out.txt", "w", stdout);
241
242 int n, m, k;
243 scanf("%d %d %d", &n, &m, &k);
244 std::vector<int> r(n), c(n);
245 for (int i = 0; i < n; i++)
246 {
247 scanf("%d %d", &r[i], &c[i]);
248 }
249
250 auto t2 = clock();
251
252 long long ans = take_photos(n, m, k, r, c);
253
254 auto t3 = clock();
255
256 // BEGIN SECRET
257 //puts("098d134608c94f7413faac591054ee35");
258 // END SECRET
259
260 printf("%lld\n", ans);
261
262 auto t4 = clock();
263
264 // reset console output
265 freopen("CON", "w", stdout);
266
267 //cout<<"ans = "<<ans<<"\n";
268
269 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
270 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
271 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
272
273 return 0;
274 }
275
276 // END CUT
277 /*
278 t2-t1 = 0.328
279 t3-t2 = 0.906
280 t4-t3 = 0
281
282 Process returned 0 (0x0) execution time : 1.266 s
CAPITOLUL 5. IOI 2016 693

283 Press any key to continue.


284 */

Listing 5.6.3: aliens_ma_nlogm.cpp


1 #include <cstdio>
2 #include <algorithm>
3 #include <vector>
4 #include <cassert>
5 #include <iostream>
6 #include <map>
7
8 #include<ctime>
9
10 using namespace std;
11
12 typedef long long llong;
13 const llong linf = 1e13;
14 const int inf = 1e9;
15
16 llong sqr(int x)
17 {
18 return 1ll * x * x;
19 }
20
21 struct vt
22 {
23 llong x, y;
24 int num = -1;
25 vt(llong _x, llong _y)
26 {
27 x = _x, y = _y;
28 }
29 vt() {}
30 friend vt operator -(vt a, vt b)
31 {
32 return vt(a.x - b.x, a.y - b.y);
33 }
34 friend llong operator ^(vt a, vt b)
35 {
36 return a.x * b.y - b.x * a.y;
37 }
38 friend llong operator *(vt a, vt b)
39 {
40 return a.x * b.x + a.y * b.y;
41 }
42 };
43
44 long long take_photos(int n, int m, int k, std::vector<int> row,
45 std::vector<int> column)
46 {
47 map<int, int> M;
48 for (int i = 0; i < n; i++)
49 {
50 int x, y;
51 x = row[i];
52 y = column[i];
53 if (x < y)
54 swap(x, y);
55 if (!M.count(x))
56 M[x] = x;
57 M[x] = min(M[x], y);
58 }
59
60 vector<int> L, R;
61 for (auto pr : M)
62 {
63 while (!L.empty() && L.back() >= pr.second)
64 L.pop_back(), R.pop_back();
65 R.push_back(pr.first);
66 L.push_back(pr.second);
67 }
68
69 n = L.size();
70 assert(is_sorted(R.begin(), R.end()));
CAPITOLUL 5. IOI 2016 694

71 assert(is_sorted(L.begin(), L.end()));
72
73 llong ans = linf;
74 llong a = -1, b = (llong)m * m + 1;
75 while (b - a > 1)
76 {
77 llong x = (a + b) / 2;
78 vector<vt> st;
79 int pt = 0;
80 llong val = 0;
81 for (int i = 0; i <= (int)L.size(); i++)
82 {
83 val = 0;
84 if (i)
85 {
86 int r2 = R[i - 1];
87 vt dir(1, r2);
88 while (pt + 1 < (int)st.size())
89 {
90 llong scal1 = st[pt] * dir;
91 llong scal2 = st[pt + 1] * dir;
92 if (scal2 < scal1)
93 pt++;
94 else
95 break;
96 }
97 val = st[pt] * dir + x + (llong)r2 * r2;
98 }
99
100 int r1 = i ? R[i - 1] : -1;
101 int l = L[i] - 1;
102 vt cur(val + (llong)l * l - sqr(max(0, r1 - l)), -2 * l);
103 cur.num = (i == 0) ? 0 : st[pt].num + 1;
104 while (st.size() >= 2 &&
105 ((st[(int)st.size() - 1] -
106 st[(int)st.size() - 2]) ^
107 (cur - st[(int)st.size() - 1])) < 0)
108 st.pop_back();
109
110 st.push_back(cur);
111 pt = min(pt, (int)st.size() - 1);
112 }
113
114 int cnt = st.back().num;
115 if (cnt > k)
116 a = x;
117 else {
118 ans = val;
119 b = x;
120 }
121 }
122 ans -= b * k;
123
124 return ans;
125 }
126
127 // BEGIN CUT
128
129 int main()
130 {
131 auto t1 = clock();
132
133 std::freopen("../tests/subtask_6/175", "r", stdin);
134 std::freopen("6-175.out.txt", "w", stdout);
135
136 int n, m, k;
137 scanf("%d %d %d", &n, &m, &k);
138 std::vector<int> r(n), c(n);
139 for (int i = 0; i < n; i++)
140 {
141 scanf("%d %d", &r[i], &c[i]);
142 }
143
144 auto t2 = clock();
145
146 long long ans = take_photos(n, m, k, r, c);
CAPITOLUL 5. IOI 2016 695

147
148 auto t3 = clock();
149
150 // BEGIN SECRET
151 //puts("098d134608c94f7413faac591054ee35");
152 // END SECRET
153
154 printf("%lld\n", ans);
155
156 auto t4 = clock();
157
158 // reset console output
159 freopen("CON", "w", stdout);
160
161 //cout<<"ans = "<<ans<<"\n";
162
163 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
164 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
165 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
166
167 return 0;
168 }
169
170 // END CUT
171 /*
172 t2-t1 = 0.312
173 t3-t2 = 1.609
174 t4-t3 = 0
175
176 Process returned 0 (0x0) execution time : 1.953 s
177 Press any key to continue.
178 */

Listing 5.6.4: aliens_ma_nlogm_double.cpp


1 #include <cstdio>
2 #include <algorithm>
3 #include <vector>
4 #include <cassert>
5 #include <iostream>
6 #include <map>
7
8 #include<ctime>
9
10 using namespace std;
11
12 typedef long long llong;
13 const llong linf = 1e13;
14 const int inf = 1e9;
15
16 llong sqr(int x)
17 {
18 return 1ll * x * x;
19 }
20
21 struct vt
22 {
23 double x, y;
24 int num = -1;
25 vt(double _x, double _y)
26 {
27 x = _x, y = _y;
28 }
29 vt() {}
30 friend vt operator -(vt a, vt b)
31 {
32 return vt(a.x - b.x, a.y - b.y);
33 }
34 friend llong operator ^(vt a, vt b)
35 {
36 return a.x * b.y - b.x * a.y;
37 }
38 friend llong operator *(vt a, vt b)
39 {
40 return a.x * b.x + a.y * b.y;
CAPITOLUL 5. IOI 2016 696

41 }
42 double slope()
43 {
44 return y / x;
45 }
46 };
47
48 long long take_photos(int n, int m, int k, vector<int> row, vector<int> column)
49 {
50 map<int, int> M;
51 for (int i = 0; i < n; i++)
52 {
53 int x, y;
54 x = row[i];
55 y = column[i];
56 if (x < y)
57 swap(x, y);
58 if (!M.count(x))
59 M[x] = x;
60 M[x] = min(M[x], y);
61 }
62
63 vector<int> L, R;
64 for (auto pr : M)
65 {
66 while (!L.empty() && L.back() >= pr.second)
67 L.pop_back(), R.pop_back();
68 R.push_back(pr.first);
69 L.push_back(pr.second);
70 }
71
72 n = L.size();
73 assert(is_sorted(R.begin(), R.end()));
74 assert(is_sorted(L.begin(), L.end()));
75
76 llong ans = linf;
77 llong a = -1, b = (llong)m * m + 1;
78 while (b - a > 1)
79 {
80 llong x = (a + b) / 2;
81 vector<vt> st;
82 int pt = 0;
83 llong val = 0;
84 for (int i = 0; i <= (int)L.size(); i++)
85 {
86 val = 0;
87 if (i)
88 {
89 int r2 = R[i - 1];
90 vt dir(1, r2);
91 while (pt + 1 < (int)st.size())
92 {
93 llong scal1 = st[pt] * dir;
94 llong scal2 = st[pt + 1] * dir;
95 if (scal2 < scal1)
96 pt++;
97 else
98 break;
99 }
100 val = st[pt] * dir + x + (llong)r2 * r2;
101 }
102
103 int r1 = i ? R[i - 1] : -1;
104 int l = L[i] - 1;
105 vt cur(val + (llong)l * l - sqr(max(0, r1 - l)), -2 * l);
106 cur.num = (i == 0) ? 0 : st[pt].num + 1;
107 while (st.size() >= 2 &&
108 (st[(int)st.size() - 1] -
109 st[(int)st.size() - 2]).slope() >
110 (cur - st[(int)st.size() - 1]).slope())
111 st.pop_back();
112
113 st.push_back(cur);
114 pt = min(pt, (int)st.size() - 1);
115 }
116
CAPITOLUL 5. IOI 2016 697

117 int cnt = st.back().num;


118 if (cnt > k)
119 a = x;
120 else
121 {
122 ans = val;
123 b = x;
124 }
125 }
126 ans -= b * k;
127
128 return ans;
129 }
130
131 // BEGIN CUT
132
133 int main()
134 {
135 auto t1 = clock();
136
137 std::freopen("../tests/subtask_6/175", "r", stdin);
138 std::freopen("6-175.out.txt", "w", stdout);
139 int n, m, k;
140 scanf("%d %d %d", &n, &m, &k);
141 std::vector<int> r(n), c(n);
142 for (int i = 0; i < n; i++)
143 {
144 scanf("%d %d", &r[i], &c[i]);
145 }
146
147 auto t2 = clock();
148
149 long long ans = take_photos(n, m, k, r, c);
150
151 auto t3 = clock();
152
153 // BEGIN SECRET
154 //puts("098d134608c94f7413faac591054ee35");
155 // END SECRET
156
157 printf("%lld\n", ans);
158
159 auto t4 = clock();
160
161 // reset console output
162 freopen("CON", "w", stdout);
163
164 //cout<<"ans = "<<ans<<"\n";
165
166 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
167 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
168 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
169 return 0;
170 }
171
172 // END CUT
173 /*
174 t2-t1 = 0.343
175 t3-t2 = 1.719
176 t4-t3 = 0
177
178 Process returned 0 (0x0) execution time : 2.094 s
179 Press any key to continue.
180 */

Listing 5.6.5: alien-32012.cpp


1 // https://oj.uz/submission/32012
2
3 #include "aliens.h"
4
5 #include<iostream>
6 #include<vector>
7 #include<algorithm>
8 #include<cmath>
CAPITOLUL 5. IOI 2016 698

9
10 #include<ctime>
11
12 using namespace std;
13
14 typedef long long llong;
15 typedef long double ld;
16
17 struct point
18 {
19 int x, y;
20 bool operator<(const point &p) const
21 {
22 return x < p.x || (x == p.x && y > p.y);
23 }
24 };
25
26 int n, m, k;
27 vector<point> _ps;
28 vector<point> ps;
29
30 llong sqr(int x)
31 {
32 return (llong)x * x;
33 }
34
35 struct line
36 {
37 int cnt;
38 llong m, b;
39 } st[100000];
40
41 int top, bot;
42
43 void push(line x)
44 {
45 while (top > bot && (ld)(x.b - st[top].b) / (st[top].m - x.m)
46 <= (ld)(st[top - 1].b - st[top].b) / (st[top].m - st[top - 1].m))
47 --top;
48 st[++top] = x;
49 }
50
51 llong func(line l, int x)
52 {
53 return l.m * x + l.b;
54 }
55
56 pair<int, llong> query(int x)
57 {
58 while (top > bot && func(st[bot + 1], x) <= func(st[bot], x)) ++bot;
59 return { st[bot].cnt, func(st[bot], x) };
60 }
61
62 llong dp[100000];
63 int cnt[100000];
64
65 //dp[i] = min(dp[j] + x[j + 1] ^ 2 - 2 * x[j + 1] * y[i] + y[i] ^ 2 + cost);
66
67 int getPhoto(llong c)
68 {
69 top = -1; bot = 0;
70 push({ 0, -2ll * ps[0].x, sqr(ps[0].x) });
71 for (int i = 0; i < n; ++i)
72 {
73 auto ret = query(ps[i].y + 1);
74 dp[i] = ret.second + sqr(ps[i].y + 1) + c;
75 cnt[i] = ret.first + 1;
76 if (i < n - 1)
77 push({ cnt[i],
78 -2ll * ps[i + 1].x,
79 dp[i] + sqr(ps[i + 1].x) -
80 sqr(max(0, ps[i].y - ps[i + 1].x + 1)) });
81 }
82 return cnt[n - 1];
83 }
84
CAPITOLUL 5. IOI 2016 699

85 long long take_photos(int _n, int _m, int _k,


86 vector<int> _r, vector<int> _c)
87 {
88 n = _n;
89 m = _m;
90 k = _k;
91
92 _ps.reserve(n);
93 ps.reserve(n);
94
95 for (int i = 0; i < n; ++i)
96 {
97 _ps.push_back({ min(_r[i], _c[i]), max(_r[i], _c[i]) });
98 }
99
100 sort(_ps.begin(), _ps.end());
101
102 for (point i : _ps)
103 {
104 if (ps.empty() || ps.back().x < i.x && ps.back().y < i.y)
105 ps.push_back(i);
106 }
107
108 n = ps.size();
109 k = min(n, k);
110 llong s = 0ll, e = (llong)m * m;
111 int l = -1, r = n + 1;
112 llong lvalue, rvalue;
113
114 while (s <= e)
115 {
116 llong m = (s + e) / 2;
117 int ret = getPhoto(m);
118 if (ret == k) return dp[n - 1] - ret * m;
119 if (ret < k)
120 {
121 e = m - 1;
122 if (l < ret)
123 {
124 l = ret; lvalue = dp[n - 1] - ret * m;
125 }
126 }
127 else
128 {
129 s = m + 1;
130 if (ret < r)
131 {
132 r = ret; rvalue = dp[n - 1] - ret * m;
133 }
134 }
135 }
136 return rvalue + ((lvalue - rvalue) / (r - l)) * (r - k);
137 }
138
139 // BEGIN CUT
140
141 int main()
142 {
143 auto t1 = clock();
144
145 std::freopen("../tests/subtask_6/175", "r", stdin);
146 std::freopen("6-175.out.txt", "w", stdout);
147
148 int n, m, k;
149 scanf("%d %d %d", &n, &m, &k);
150 std::vector<int> r(n), c(n);
151 for (int i = 0; i < n; i++)
152 {
153 scanf("%d %d", &r[i], &c[i]);
154 }
155
156 auto t2 = clock();
157
158 long long ans = take_photos(n, m, k, r, c);
159
160 auto t3 = clock();
CAPITOLUL 5. IOI 2016 700

161
162 // BEGIN SECRET
163 //puts("098d134608c94f7413faac591054ee35");
164 // END SECRET
165
166 printf("%lld\n", ans);
167
168 auto t4 = clock();
169
170 // reset console output
171 freopen("CON", "w", stdout);
172
173 //cout<<"ans = "<<ans<<"\n";
174
175 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
176 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
177 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
178
179 return 0;
180 }
181
182 // END CUT
183 /*
184 t2-t1 = 0.328
185 t3-t2 = 0.609
186 t4-t3 = 0
187
188 Process returned 0 (0x0) execution time : 1.219 s
189 Press any key to continue.
190 */

Listing 5.6.6: alien-43618.cpp


1 // https://oj.uz/submission/43618
2
3 #include <bits/stdc++.h>
4 #include "aliens.h"
5
6 using namespace std;
7
8 typedef pair<int,int> pp;
9 typedef long long ll;
10
11 pp po[100010];
12 ll x[100010];
13 ll y[100010];
14 int pn;
15
16 ll dp[100010];
17
18 int top;
19 ll grad[100010];
20 ll yint[100010];
21 int lid[100010];
22 void add_line(ll g, ll y, int id)
23 {
24 while(top>=2)
25 {
26 if((yint[top-2]-yint[top-1])*(grad[top-1]-g)>
27 (yint[top-1]-y)*(grad[top-2]-grad[top-1])) break;
28 --top;
29 }
30 grad[top] = g;
31 yint[top] = y;
32 lid[top] = id;
33 ++top;
34 }
35
36 int bx;
37 ll f(int p, ll x){ return grad[p]*x + yint[p]; }
38 inline ll sqr(ll x){ return x*x; }
39 int lst[100010];
40
41 int F(ll cost)
42 {
CAPITOLUL 5. IOI 2016 701

43 top = 0; bx = 0;
44 add_line(-2*x[0], sqr(x[0]), -1);
45 for(int i=0; i<pn; ++i)
46 {
47 while(bx+1 < top && f(bx, y[i]+1) > f(bx+1, y[i]+1)) ++bx;
48 dp[i]=f(bx, y[i]+1)+sqr(y[i]+1)+cost;
49 lst[i]=lid[bx];
50 if(i+1 < pn)
51 {
52 add_line(-2*x[i+1],
53 dp[i]+sqr(x[i+1])-sqr(max(0ll, y[i]-x[i+1]+1)),
54 i);
55 bx = min(bx, top-1);
56 }
57 }
58 int cnt = 0;
59 for(int i=pn-1; i!=-1; i=lst[i]) ++cnt;
60 return cnt;
61 }
62
63 long long take_photos(int n, int m, int k,
64 std::vector<int> r, std::vector<int> c)
65 {
66 for(int i=0; i<n; ++i)
67 {
68 int x=r[i], y=c[i];
69 if(x>y) swap(x, y);
70 po[i]={x, y};
71 }
72
73 sort(po, po+n);
74
75 for(int i=0; i<n; ++i)
76 {
77 int cx=po[i].first, cy=po[i].second;
78 if(!pn)
79 {
80 x[pn]=cx; y[pn]=cy; ++pn;
81 continue;
82 }
83 if(x[pn-1] == cx) y[pn-1] = cy;
84 else if(y[pn-1] < cy) x[pn]=cx, y[pn]=cy, ++pn;
85 }
86
87 ll cl = -1, cr = m*1LL*m;
88 while(cl+1 < cr)
89 {
90 ll mid = (cl+cr)/2;
91 (F(mid) <= k ? cr : cl) = mid;
92 }
93
94 F(cr);
95 return dp[pn-1]-cr*k;
96 }
97
98 // BEGIN CUT
99
100 int main()
101 {
102 auto t1 = clock();
103
104 std::freopen("../tests/subtask_6/175", "r", stdin);
105 std::freopen("6-175.out.txt", "w", stdout);
106
107 int n, m, k;
108 scanf("%d %d %d", &n, &m, &k);
109 std::vector<int> r(n), c(n);
110 for (int i = 0; i < n; i++)
111 {
112 scanf("%d %d", &r[i], &c[i]);
113 }
114
115 auto t2 = clock();
116
117 long long ans = take_photos(n, m, k, r, c);
118
CAPITOLUL 5. IOI 2016 702

119 auto t3 = clock();


120
121 // BEGIN SECRET
122 //puts("098d134608c94f7413faac591054ee35");
123 // END SECRET
124
125 printf("%lld\n", ans);
126
127 auto t4 = clock();
128
129 // reset console output
130 freopen("CON", "w", stdout);
131
132 //cout<<"ans = "<<ans<<"\n";
133
134 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
135 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
136 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
137
138 return 0;
139 }
140
141 // END CUT
142 /*
143 t2-t1 = 0.281
144 t3-t2 = 0.406
145 t4-t3 = 0
146
147 Process returned 0 (0x0) execution time : 0.984 s
148 Press any key to continue.
149 */

Listing 5.6.7: alien-45993.cpp


1 // https://oj.uz/submission/45993
2
3 #include <bits/stdc++.h>
4 #include "aliens.h"
5 using namespace std;
6
7 typedef long long ll;
8 typedef pair<int, int> ii;
9 typedef pair<ll, int> li;
10
11 #define sq(a) ((ll)(a) * (a))
12
13 struct cht
14 {
15 vector<ll> m, b, k; int ptr = 0;
16 bool bad(int l1, int l2, int l3)
17 {
18 return 1.0 * (b[l3] - b[l1]) * (m[l1] - m[l2]) <=
19 1.0 * (b[l2] - b[l1]) * (m[l1] - m[l3]);
20 }
21
22 void add(ll _m, ll _b, int _k)
23 {
24 m.push_back(_m); b.push_back(_b); k.push_back(_k);
25 int s = m.size();
26 while(s >= 3 && bad(s - 3, s - 2, s - 1))
27 {
28 m.erase(m.end() - 2);
29 b.erase(b.end() - 2);
30 k.erase(k.end() - 2);
31 s--;
32 }
33 }
34
35 ll f(int i, ll x) { return m[i] * x + b[i]; }
36
37 li query(ll x)
38 {
39 if(ptr >= m.size()) ptr = m.size() - 1;
40 while(ptr < m.size() - 1 &&
41 f(ptr, x) > f(ptr + 1, x)) ptr++;
CAPITOLUL 5. IOI 2016 703

42 return { f(ptr, x), k[ptr] };


43 }
44 void clear() { m.clear(), b.clear(), k.clear(); ptr = 0; }
45 } ds;
46
47 vector<int> l = {-1}, r = {-1};
48
49 li get(ll C)
50 {
51 int n = l.size() - 1;
52 li dp[n + 1];
53 dp[0] = {0, 0};
54 ds.clear(); ds.add(-2ll * l[1], sq(l[1]), 0);
55 for(int i = 1; i <= n; i++)
56 {
57 ll x = r[i] + 1;
58 li tmp = ds.query(x);
59 dp[i].first = tmp.first + sq(x) + C;
60 dp[i].second = tmp.second + 1;
61 if(i == n) break;
62 ds.add(-2ll * l[i + 1],
63 dp[i].first +
64 sq(l[i + 1])-
65 sq(max(0, r[i] - l[i + 1] + 1)),
66 dp[i].second);
67 }
68 return dp[n];
69 }
70
71 ll take_photos(int n, int m, int K, vector<int> R, vector<int> C)
72 {
73 vector<ii> tmp;
74 for(int i = 0; i < n; i++)
75 {
76 if(R[i] > C[i]) swap(R[i], C[i]);
77 tmp.emplace_back(C[i], R[i]);
78 }
79
80 sort(tmp.begin(), tmp.end());
81
82 tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());
83
84 for(ii seg : tmp)
85 {
86 if(seg.first == r.back()) continue;
87 while(l.size() && seg.second <= l.back() && r.back() <= seg.first)
88 r.pop_back(), l.pop_back();
89 l.push_back(seg.second);
90 r.push_back(seg.first);
91 }
92
93 ll lo = 0, hi = 1e17, idx = -1;
94 while(lo < hi)
95 {
96 ll mid = lo + hi >> 1;
97 if(get(mid).second <= K) hi = mid;
98 else lo = mid + 1;
99 }
100 return get(lo).first - lo * K;
101 }
102
103 // BEGIN CUT
104
105 int main()
106 {
107 auto t1 = clock();
108
109 std::freopen("../tests/subtask_6/175", "r", stdin);
110 std::freopen("6-175.out.txt", "w", stdout);
111
112 int n, m, k;
113 scanf("%d %d %d", &n, &m, &k);
114 std::vector<int> r(n), c(n);
115 for (int i = 0; i < n; i++)
116 {
117 scanf("%d %d", &r[i], &c[i]);
CAPITOLUL 5. IOI 2016 704

118 }
119
120 auto t2 = clock();
121
122 long long ans = take_photos(n, m, k, r, c);
123
124 auto t3 = clock();
125
126 // BEGIN SECRET
127 //puts("098d134608c94f7413faac591054ee35");
128 // END SECRET
129
130 printf("%lld\n", ans);
131
132 auto t4 = clock();
133
134 // reset console output
135 freopen("CON", "w", stdout);
136
137 //cout<<"ans = "<<ans<<"\n";
138
139 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
140 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
141 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
142
143 return 0;
144 }
145
146 // END CUT
147 /*
148 t2-t1 = 0.296
149 t3-t2 = 4.025
150 t4-t3 = 0
151
152 Process returned 0 (0x0) execution time : 4.602 s
153 Press any key to continue.
154 */

Listing 5.6.8: alien-70398.cpp


1 // https://oj.uz/submission/70398
2
3 #include "aliens.h"
4 #include <bits/stdc++.h>
5 using namespace std;
6
7 #define all(x) x.begin(), x.end()
8 //#define long long long
9 #define pii pair<int, int>
10 #define x first
11 #define y second
12
13 vector<pii> V;
14
15 void compress()
16 {
17 sort(all(V), [](pii a, pii b)
18 { if(a.x == b.x) return a.y > b.y; return a.x < b.x; });
19 int en = -1;
20 vector<pii> ret;
21 for(auto x : V)
22 if(x.y > en)
23 en = x.y, ret.emplace_back(x);
24 V = ret;
25 }
26
27 struct cht
28 {
29 struct line
30 {
31 long long m, c;
32 int cnt;
33 line(long long m, long long c, int cnt) : m(m), c(c), cnt(cnt) {}
34 long long get(long long x) { return m * x + c; }
35 };
CAPITOLUL 5. IOI 2016 705

36
37 vector<line> f;
38 bool bad(line l1, line l2, line l3)
39 {
40 return (l1.c - l3.c) * (l2.m - l1.m) <=
41 (l3.m - l1.m) * (l1.c - l2.c);
42 }
43
44 void update(long long m, long long c, int cnt)
45 {
46 line l(m, c, cnt);
47 while(f.size() >= 2 and bad(f[f.size()-2], f[f.size()-1], l))
48 f.pop_back();
49 f.emplace_back(l);
50 }
51
52 int idx;
53 pair<long long, int> query(long long x)
54 {
55 while(idx + 1 < f.size() and f[idx+1].get(x) < f[idx].get(x))
56 ++idx;
57 return make_pair(f[idx].get(x), f[idx].cnt);
58 }
59
60 void clear()
61 {
62 f.clear(), idx = 0;
63 }
64 } hull;
65
66 long long sq(long long x) { return x * x; }
67
68 pair<long long, int> f(long long C)
69 {
70 vector<pair<long long, int> > dp(V.size()+1);
71 hull.clear();
72 hull.update(-2*(V[0].x-1), sq(V[0].x-1), 0);
73 for(int i = 1; i <= V.size(); ++i)
74 {
75 auto ret = hull.query(V[i-1].y);
76 dp[i] = make_pair(ret.x + sq(V[i-1].y) + C, ret.y + 1);
77 if(i != V.size())
78 hull.update(-2*(V[i].x-1),
79 sq(V[i].x-1) +
80 dp[i].x -
81 sq(max(0, V[i-1].y - V[i].x + 1)),
82 dp[i].y);
83 }
84 return dp.back();
85 }
86
87 long long take_photos(int n, int m, int k,
88 vector<int> _r, vector<int> c)
89 {
90 for(int i = 0; i < n; ++i)
91 V.emplace_back(min(_r[i], c[i]), max(_r[i], c[i]));
92 compress();
93 long long l = 0, r = 1e12;
94 while(l < r)
95 {
96 long long m = l + r >> 1;
97 if(f(m).y <= k) r = m;
98 else l = m+1;
99 }
100 return f(r).x - r*k;
101 }
102
103 // BEGIN CUT
104
105 int main()
106 {
107 auto t1 = clock();
108
109 std::freopen("../tests/subtask_6/175", "r", stdin);
110 std::freopen("6-175.out.txt", "w", stdout);
111
CAPITOLUL 5. IOI 2016 706

112 int n, m, k;
113 scanf("%d %d %d", &n, &m, &k);
114 std::vector<int> r(n), c(n);
115 for (int i = 0; i < n; i++)
116 {
117 scanf("%d %d", &r[i], &c[i]);
118 }
119
120 auto t2 = clock();
121
122 long long ans = take_photos(n, m, k, r, c);
123
124 auto t3 = clock();
125
126 // BEGIN SECRET
127 //puts("098d134608c94f7413faac591054ee35");
128 // END SECRET
129
130 printf("%lld\n", ans);
131
132 auto t4 = clock();
133
134 // reset console output
135 freopen("CON", "w", stdout);
136
137 //cout<<"ans = "<<ans<<"\n";
138
139 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
140 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
141 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
142
143 return 0;
144 }
145
146 // END CUT
147 /*
148 t2-t1 = 0.296
149 t3-t2 = 1.313
150 t4-t3 = 0
151
152 Process returned 0 (0x0) execution time : 1.875 s
153 Press any key to continue.
154 */

Listing 5.6.9: alien-94585.cpp


1 // https://oj.uz/submission/94585
2
3 #include <bits/stdc++.h>
4 #include "aliens.h"
5
6 using namespace std;
7
8 long long take_photos(int n, int m, int k, vector<int> r, vector<int> c)
9 {
10 vector<pair<int, int>> p(n);
11 for (int i = 0; i < n; ++i)
12 {
13 p[i].first = min(r[i], c[i]);
14 p[i].second = max(r[i], c[i]);
15 }
16
17 sort(p.begin(), p.end());
18
19 {
20 vector<pair<int, int>> new_p;
21 for (int i = 0, j = 0; i < n; i = j)
22 {
23 while (j < n && p[j].first == p[i].first)
24 {
25 ++j;
26 }
27 if (new_p.empty() || p[j - 1].second > new_p.back().second)
28 {
29 new_p.push_back(p[j - 1]);
CAPITOLUL 5. IOI 2016 707

30 }
31 }
32 swap(p, new_p);
33 n = p.size();
34 }
35
36 k = min(k, n);
37 vector<int> x(n), y(n);
38
39 for (int i = 0; i < n; ++i)
40 {
41 x[i] = p[i].first;
42 y[i] = p[i].second;
43 }
44
45 vector<long long> f(n + 1);
46 vector<int> g(n + 1);
47 auto sqr = [&](int x)
48 {
49 return (long long) x * x;
50 };
51
52 auto solve = [&](long long c)
53 {
54 vector<int> q(n + 1);
55 int l = 0, r = 0;
56 auto check = [&](int a, int b, int c)
57 {
58 return (f[a] + sqr(x[a]) - f[c] - sqr(x[c])) * (x[b] - x[c]) <
59 (f[b] + sqr(x[b]) - f[c] - sqr(x[c])) * (x[a] - x[c]);
60 };
61
62 for (int i = 0; i < n; ++i)
63 {
64 while (r - l > 1 && check(i, q[r - 1], q[r - 2]))
65 {
66 --r;
67 }
68
69 q[r++] = i;
70 while (r - l > 1 && f[q[l]] + sqr(y[i] - x[q[l]] + 1) >
71 f[q[l + 1]] + sqr(y[i] - x[q[l + 1]] + 1))
72 {
73 ++l;
74 }
75
76 f[i + 1] = f[q[l]] + sqr(y[i] - x[q[l]] + 1) + c;
77 if (i + 1 < n && y[i] >= x[i + 1])
78 {
79 f[i + 1] -= sqr(y[i] - x[i + 1] + 1);
80 }
81 g[i + 1] = g[q[l]] + 1;
82 }
83 };
84
85 long long low = 0, high = 1ll << 40;
86
87 while (low < high)
88 {
89 long long mid = low + high >> 1;
90 solve(mid);
91 if (g[n] <= k)
92 {
93 high = mid;
94 }
95 else
96 {
97 low = mid + 1;
98 }
99 }
100
101 solve(high);
102 return f[n] - k * high;
103 }
104
105 // BEGIN CUT
CAPITOLUL 5. IOI 2016 708

106
107 int main()
108 {
109 auto t1 = clock();
110
111 std::freopen("../tests/subtask_6/175", "r", stdin);
112 std::freopen("6-175.out.txt", "w", stdout);
113
114 int n, m, k;
115 scanf("%d %d %d", &n, &m, &k);
116 std::vector<int> r(n), c(n);
117 for (int i = 0; i < n; i++)
118 {
119 scanf("%d %d", &r[i], &c[i]);
120 }
121
122 auto t2 = clock();
123
124 long long ans = take_photos(n, m, k, r, c);
125
126 auto t3 = clock();
127
128 // BEGIN SECRET
129 //puts("098d134608c94f7413faac591054ee35");
130 // END SECRET
131
132 printf("%lld\n", ans);
133
134 auto t4 = clock();
135
136 // reset console output
137 freopen("CON", "w", stdout);
138
139 //cout<<"ans = "<<ans<<"\n";
140
141 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
142 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
143 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
144
145 return 0;
146 }
147
148 // END CUT
149 /*
150 t2-t1 = 0.296
151 t3-t2 = 1.282
152 t4-t3 = 0
153
154 Process returned 0 (0x0) execution time : 1.844 s
155 Press any key to continue.
156 */

Listing 5.6.10: alien-96357.cpp


1 // https://oj.uz/submission/96357
2
3 #include<bits/stdc++.h>
4
5 #include "aliens.h"
6
7 #define MAX_N 100005
8 #define sq(x) ((x)*(x))
9 #define inf (1LL<<60)
10
11 using namespace std;
12
13 typedef long long LL;
14
15 struct node
16 {
17 LL x,y,i;
18 }V[MAX_N],line[MAX_N];
19
20 LL n,m,k,s,e;
21 LL dp[MAX_N],from[MAX_N];
CAPITOLUL 5. IOI 2016 709

22
23 bool is_delect(node x,node y,node z)
24 {
25 return (x.x-z.x)*(y.y-x.y)>=(x.x-y.x)*(z.y-x.y)?true:false;
26 }
27
28 void add(LL x,LL y,LL i)
29 {
30 while(e-s>1 && is_delect(line[e-2],line[e-1],{x,y})) e--;
31 line[e++]={x,y,i};
32 }
33
34 LL get_val(LL i,LL x){return line[i].x*x+line[i].y;}
35
36 LL is_ok(LL x)
37 {
38 LL i;
39 s=e=0;
40 add(-2*V[1].x,sq(V[1].x)-2*V[1].x,0);
41 for(i=1;i<=n;i++)
42 {
43 while(e-s>1 && get_val(s,V[i].y)>=get_val(s+1,V[i].y)) s++;
44 dp[i]=get_val(s,V[i].y)+sq(V[i].y+1)+x;
45 from[i]=line[s].i;
46 add(-2*V[i+1].x,
47 sq(V[i+1].x)+
48 dp[i]-2*V[i+1].x-
49 sq(max(0LL,V[i].y-V[i+1].x+1)),
50 i);
51 }
52 i=n;
53 LL c;
54 for(c=0;i;i=from[i],c++);
55
56 return c;
57 }
58
59 long long take_photos(int N, int M, int K,
60 std::vector<int> r, std::vector<int> c)
61 {
62 LL i,x,y;
63 n=N;
64 m=M;
65 k=K;
66 for(i=1;i<=n;i++)
67 V[i]={min(r[i-1],c[i-1]),max(r[i-1],c[i-1])};
68
69 sort(V+1,
70 V+n+1,
71 [&](const node x,const node y)
72 {
73 return (x.x==y.x)?x.y>y.y:x.x<y.x;
74 });
75 m=n;
76 n=0;
77 x=-inf;
78 for(i=1;i<=m;i++)
79 {
80 if(x>=V[i].y) continue;
81 V[++n]=V[i];
82 x=V[i].y;
83 }
84
85 LL l,rr;
86 l=0;
87 rr=sq((LL)M);
88 k=min(k,n);
89 LL mid,ans=0;
90 while(l<=rr)
91 {
92 mid=(l+rr)>>1;
93 x=is_ok(mid);
94 if(x==k) return dp[n]-k*mid;
95 if(x<k) rr=mid-1;
96 else
97 {
CAPITOLUL 5. IOI 2016 710

98 l=mid+1;
99 ans=max(ans,dp[n]-k*mid);
100 }
101 }
102 return ans;
103 }
104
105 // BEGIN CUT
106
107 int main()
108 {
109 auto t1 = clock();
110
111 std::freopen("../tests/subtask_6/175", "r", stdin);
112 std::freopen("6-175.out.txt", "w", stdout);
113
114 int n, m, k;
115 scanf("%d %d %d", &n, &m, &k);
116 std::vector<int> r(n), c(n);
117 for (int i = 0; i < n; i++)
118 {
119 scanf("%d %d", &r[i], &c[i]);
120 }
121
122 auto t2 = clock();
123
124 long long ans = take_photos(n, m, k, r, c);
125
126 auto t3 = clock();
127
128 // BEGIN SECRET
129 //puts("098d134608c94f7413faac591054ee35");
130 // END SECRET
131
132 printf("%lld\n", ans);
133
134 auto t4 = clock();
135
136 // reset console output
137 freopen("CON", "w", stdout);
138
139 //cout<<"ans = "<<ans<<"\n";
140
141 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
142 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
143 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
144
145 return 0;
146 }
147
148 // END CUT
149 /*
150 t2-t1 = 0.312
151 t3-t2 = 0.438
152 t4-t3 = 0
153
154 Process returned 0 (0x0) execution time : 1.016 s
155 Press any key to continue.
156 */

Listing 5.6.11: alien-97219.cpp


1 // https://oj.uz/submission/97219
2
3 #include "aliens.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 using ll = long long;
9 using pll = pair<ll, ll>;
10 using pli = pair<ll, int>;
11
12 #define fi first
13 #define se second
CAPITOLUL 5. IOI 2016 711

14
15 int c[100009];
16 ll D[100009];
17
18 inline ll p(ll x) { return x*x; }
19
20 struct line
21 {
22 ll a, b;
23 int idx;
24 };
25
26 line stk[100009]; int t, j;
27
28 inline bool del(line P, line Q, line R)
29 {
30 return (R.a - P.a) * (Q.b - R.b) < (R.a - Q.a) * (P.b - R.b);
31 }
32
33 void add(line P)
34 {
35 while(1<=t && del(stk[t-1], stk[t], P)) --t, j = min(j, t);
36 stk[++t] = P;
37 }
38
39 pli DP(vector<pll>& A, ll lambda)
40 {
41 int N = (int)A.size() - 1;
42 for(int i=1; i<N; i++)
43 D[i] = 1LL * 1e18, c[i] = 0;
44 t = -1;
45 add((line){-2LL * A[1].fi, p(A[1].fi) - 2*A[1].fi, 0});
46 j = 0;
47 for(int i=1; i<N; i++)
48 {
49 while(j<t &&
50 A[i].se * stk[j].a + stk[j].b >
51 A[i].se * stk[j+1].a + stk[j+1].b)
52 ++j;
53
54 ll nw = A[i].se * stk[j].a +
55 stk[j].b + p(A[i].se) +
56 2*A[i].se + 1 +
57 lambda;
58
59 if(D[i] > nw)
60 D[i] = nw, c[i] = c[stk[j].idx] + 1;
61
62 add({-2LL * A[i+1].fi,
63 D[i] +
64 p(A[i+1].fi) -
65 2*A[i+1].fi -
66 p(max(0LL, A[i].se - A[i+1].fi + 1)),
67 i});
68 }
69
70 return {D[N-1], c[N-1]};
71 }
72
73 long long take_photos(int n, int m, int k, vector<int> r, vector<int> c)
74 {
75 vector<pll> A, B;
76 for(int i=0; i<n; i++)
77 {
78 if(r[i] > c[i]) swap(r[i], c[i]);
79 A.push_back({r[i], c[i]});
80 }
81
82 sort(A.begin(), A.end(), [&](pll P, pll Q)
83 {
84 if(P.se == Q.se) return P.fi > Q.fi;
85 return P.se < Q.se;
86 });
87 B.push_back({-1, -1});
88 for(int i=0; i<n; i++)
89 {
CAPITOLUL 5. IOI 2016 712

90 while(B.size() && B.back().fi >= A[i].fi)


91 B.pop_back();
92 B.push_back(A[i]);
93 }
94
95 B.push_back({0, 0});
96 long long L = 0, R = 1LL*1e12, f = 0;
97 while(L <= R)
98 {
99 long long m = L+R >> 1, v; int cnt;
100 tie(v, cnt) = DP(B, m);
101 if(cnt > k) L = m+1;
102 else R = m-1, f = v - m * k;
103 }
104 return f;
105 }
106
107 // BEGIN CUT
108
109 int main()
110 {
111 auto t1 = clock();
112
113 std::freopen("../tests/subtask_6/175", "r", stdin);
114 std::freopen("6-175.out.txt", "w", stdout);
115
116 int n, m, k;
117 scanf("%d %d %d", &n, &m, &k);
118 std::vector<int> r(n), c(n);
119 for (int i = 0; i < n; i++)
120 {
121 scanf("%d %d", &r[i], &c[i]);
122 }
123
124 auto t2 = clock();
125
126 long long ans = take_photos(n, m, k, r, c);
127
128 auto t3 = clock();
129
130 // BEGIN SECRET
131 //puts("098d134608c94f7413faac591054ee35");
132 // END SECRET
133
134 printf("%lld\n", ans);
135
136 auto t4 = clock();
137
138 // reset console output
139 freopen("CON", "w", stdout);
140
141 //cout<<"ans = "<<ans<<"\n";
142
143 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
144 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
145 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
146
147 return 0;
148 }
149
150 // END CUT
151 /*
152 t2-t1 = 0.312
153 t3-t2 = 0.672
154 t4-t3 = 0
155
156 Process returned 0 (0x0) execution time : 1.234 s
157 Press any key to continue.
158 */

Listing 5.6.12: alien-121473.cpp


1 // https://oj.uz/submission/121473
2
3 #include "aliens.h"
CAPITOLUL 5. IOI 2016 713

4 #include <bits/stdc++.h>
5
6 #define eb emplace_back
7 #define sz(V) ((int)(V).size())
8 #define allv(V) ((V).begin()),((V).end())
9 #define sorv(V) sort(allv(V))
10 #define univ(V) (V).erase(unique(allv(V)),(V).end())
11 #define befv(V) ((V)[sz(V)-2])
12 #define upmin(a,b) (a)=min((a),(b))
13 #define INFLL (0x3f3f3f3f3f3f3f3fll)
14
15 using namespace std;
16
17 typedef long long ll;
18 typedef pair<int, int> pii;
19 typedef pair<int, ll> pil;
20 typedef pair<ll, ll> pll;
21
22 ll pw(ll n) { return n*n; }
23
24 ll operator * (const pll &a, const pll &b)
25 {
26 return a.first*b.second - b.first*a.second;
27 }
28
29 ll ccw(const pll &a, const pll &b, const pll &c)
30 {
31 return a*b + b*c + c*a;
32 }
33
34 const int MAXN = 100055;
35
36 struct CHT
37 {
38 pll P[MAXN];
39 int I[MAXN], n, pv;
40
41 void init() { n = pv = 0; }
42 void push(ll a, ll b, int c)
43 {
44 pll p(a, b);
45 for(; 1 < n && 0 <= ccw(P[n-2], P[n-1], p); n--);
46 P[n] = p;
47 I[n] = c;
48 n++;
49 }
50
51 ll f(int i, ll X) { return P[i].first * X + P[i].second; }
52
53 pil get(ll X)
54 {
55 if(n <= pv) pv = n-1;
56 for(ll nw, nxt; pv+1 < n; pv++)
57 {
58 nw = f(pv, X); nxt = f(pv+1, X);
59 if(nw <= nxt) break;
60 }
61 return pil(I[pv], f(pv, X));
62 }
63 } cht;
64
65 ll C[MAXN], D[MAXN];
66 int E[MAXN];
67
68 int X[MAXN], Y[MAXN];
69
70 int N, M, K;
71
72 void push(int i)
73 {
74 cht.push(-2ll*X[i], D[i-1] + pw(X[i]) - C[i] - 2ll*X[i], i);
75 }
76
77 int f(ll L)
78 {
79 cht.init();
CAPITOLUL 5. IOI 2016 714

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


81 {
82 push(i);
83 pil ret = cht.get(Y[i]);
84 E[i] = E[ret.first-1] + 1;
85 D[i] = ret.second + pw(Y[i]+1) + L;
86 }
87 return E[N];
88 }
89
90 ll getAns()
91 {
92 {
93 vector<pii> V, T;
94 for(int i = 1; i <= N; i++)
95 {
96 if(X[i] > Y[i]) swap(X[i], Y[i]);
97 V.eb(X[i], Y[i]);
98 }
99
100 sorv(V);
101
102 for(auto &v : V)
103 {
104 int x, y; tie(x, y) = v;
105 if(!T.empty() && T.back().first == x) T.back().second = y;
106 if(T.empty() || T.back().second < y) T.eb(x, y);
107 }
108
109 N = sz(T);
110 for(int i = 1; i <= N; i++) tie(X[i], Y[i]) = T[i-1];
111 }
112
113 if(N < K) K = N;
114
115 for(int i = 2; i <= N; i++)
116 C[i] = pw(max(0, Y[i-1] - X[i] + 1));
117
118 ll s = 0, e = pw(M)+5;
119 ll l = -1, ly, r = e+1, ry;
120
121 for(ll m; s <= e;)
122 {
123 m = (s+e) >> 1;
124 int t = f(m);
125 ll y = D[N] - m * t;
126 if(t == K) return y;
127 if(K < t)
128 {
129 s = m+1;
130 if(t < r) { r = t; ry = y; }
131 }
132 else
133 {
134 e = m-1;
135 if(l < t) { l = t; ly = y; }
136 }
137 }
138
139 return ry + (ly-ry) / (r-l) * (r-K);
140 }
141
142 long long take_photos(int n, int m, int k,
143 std::vector<int> r, std::vector<int> c)
144 {
145 ::N = n;
146 ::M = m;
147 ::K = k;
148
149 for(int i = 1; i <= ::N; i++)
150 {
151 ::X[i] = r[i-1];
152 ::Y[i] = c[i-1];
153 }
154 return getAns();
155 }
CAPITOLUL 5. IOI 2016 715

156
157 // BEGIN CUT
158
159 int main()
160 {
161 auto t1 = clock();
162
163 std::freopen("../tests/subtask_6/175", "r", stdin);
164 std::freopen("6-175.out.txt", "w", stdout);
165
166 int n, m, k;
167 scanf("%d %d %d", &n, &m, &k);
168 std::vector<int> r(n), c(n);
169 for (int i = 0; i < n; i++)
170 {
171 scanf("%d %d", &r[i], &c[i]);
172 }
173
174 auto t2 = clock();
175
176 long long ans = take_photos(n, m, k, r, c);
177
178 auto t3 = clock();
179
180 // BEGIN SECRET
181 //puts("098d134608c94f7413faac591054ee35");
182 // END SECRET
183
184 printf("%lld\n", ans);
185
186 auto t4 = clock();
187
188 // reset console output
189 freopen("CON", "w", stdout);
190
191 //cout<<"ans = "<<ans<<"\n";
192
193 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
194 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
195 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
196
197 return 0;
198 }
199
200 // END CUT
201 /*
202 t2-t1 = 0.3
203 t3-t2 = 0.781
204 t4-t3 = 0
205
206 Process returned 0 (0x0) execution time : 1.441 s
207 Press any key to continue.
208 */

Listing 5.6.13: alien-166476.cpp


1 // https://oj.uz/submission/166476
2
3 #include "aliens.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 typedef pair<long long,long long> pp;
9
10 #define f first
11 #define s second
12
13 pp a[100005];
14 long long dp[100005];
15 pair<pp,int> ss[100005];
16 int nn;
17 pp aa[100005];
18 int p[100005];
19
CAPITOLUL 5. IOI 2016 716

20 inline long double cr(pp x,pp y)


21 {
22 if(x.f==y.f)return 0;
23 return (long double)(y.s-x.s)/(long double)(x.f-y.f);
24 }
25
26 pair<long long,long long> CHT(long long c)
27 {
28 int i,j=1;
29 int t=0;
30 for(i=1 ; i<=nn ; i++)
31 {
32 pair<pp,int> k={{-2*aa[i].f,
33 aa[i].f*aa[i].f+
34 dp[i-1]+c-
35 (long long)(i>=2)*
36 max((long long)0,aa[i-1].s-aa[i].f)*
37 max((long long)0,aa[i-1].s-aa[i].f)},
38 p[i-1]};
39
40 while(t>=2 && cr(ss[t].f,ss[t-1].f)>=cr(ss[t].f,k.f))
41 t--;
42
43 ss[++t]=k;
44 for( ; j<t &&
45 cr(ss[j].f,ss[j+1].f)<=(long double)aa[i].second ;
46 j++);
47
48 dp[i]=ss[j].f.f*aa[i].s+ss[j].f.s+aa[i].s*aa[i].s;
49 p[i]=ss[j].s+1;
50 }
51
52 return {p[nn],dp[nn]};
53 }
54
55 long long take_photos(int n, int m, int k,
56 std::vector<int> r, std::vector<int> c)
57 {
58 int i,j=0;
59 for(i=1 ; i<=n ; i++)
60 {
61 a[i]={r[i-1],c[i-1]};
62 if(a[i].s<a[i].f) swap(a[i].f,a[i].s);
63 }
64
65 sort(a+1,a+n+1);
66
67 a[0].f=-1;
68 a[0].s=-1;
69 a[n+1].f=-1;
70 for(i=1 ; i<=n ; i++)
71 {
72 if(a[i].s<=a[j].s || a[i].f==a[i+1].f);
73 else
74 {
75 aa[++nn]=a[i];
76 j=i;
77 }
78 }
79
80 for(i=1 ; i<=nn ; i++)
81 aa[i].f--;
82
83 long long ans=0;
84 long long lef=0;
85 long long rig=(long long)m*m;
86
87 while(rig>=lef)
88 {
89 long long mid=(lef+rig)/2;
90 pp v=CHT(mid);
91 ans=max(ans,-mid*k+v.s);
92 if(v.f<=k)rig=mid-1;
93 else lef=mid+1;
94 }
95
CAPITOLUL 5. IOI 2016 717

96 return ans;
97 }
98
99 // BEGIN CUT
100
101 int main()
102 {
103 auto t1 = clock();
104
105 std::freopen("../tests/subtask_6/175", "r", stdin);
106 std::freopen("6-175.out.txt", "w", stdout);
107
108 int n, m, k;
109 scanf("%d %d %d", &n, &m, &k);
110 std::vector<int> r(n), c(n);
111 for (int i = 0; i < n; i++)
112 {
113 scanf("%d %d", &r[i], &c[i]);
114 }
115
116 auto t2 = clock();
117
118 long long ans = take_photos(n, m, k, r, c);
119
120 auto t3 = clock();
121
122 // BEGIN SECRET
123 //puts("098d134608c94f7413faac591054ee35");
124 // END SECRET
125
126 printf("%lld\n", ans);
127
128 auto t4 = clock();
129
130 // reset console output
131 freopen("CON", "w", stdout);
132
133 //cout<<"ans = "<<ans<<"\n";
134
135 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
136 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
137 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
138
139 return 0;
140 }
141
142 // END CUT
143 /*
144 t2-t1 = 0.359
145 t3-t2 = 0.812
146 t4-t3 = 0
147
148 Process returned 0 (0x0) execution time : 1.469 s
149 Press any key to continue.
150 */

Listing 5.6.14: alien-171563.cpp


1 // https://oj.uz/submission/171563
2
3 #include "aliens.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 typedef long long ll;
9 typedef pair<int, int> pii;
10 typedef pair<ll, ll> pll;
11
12 const int MAXN = 1e5;
13
14 struct Point { ll y, x; };
15
16 struct CHT
17 {
CAPITOLUL 5. IOI 2016 718

18 struct Line { ll a, b, k; };
19
20 double cross(const Line &p, const Line &q)
21 {
22 return (double)(q.b-p.b)/(p.a-q.a);
23 }
24
25 vector<Line> S;
26
27 void update(Line p)
28 {
29 while(S.size()>1 &&
30 cross(S[S.size()-1], p) <=
31 cross(S[S.size()-1], S[S.size()-2]))
32 S.pop_back();
33
34 S.push_back(p);
35 }
36
37 int pos=0;
38 pll query(ll x)
39 {
40 if(S.size()<=pos)
41 pos=S.size()-1;
42 else
43 while(pos+1<S.size() && cross(S[pos], S[pos+1])<=x)
44 pos++;
45
46 return {S[pos].a*x+S[pos].b, S[pos].k};
47 }
48
49 void init()
50 {
51 S.clear();
52 pos=0;
53 }
54 } cht;
55
56 int N, M, K;
57 Point B[MAXN+10], A[MAXN+10];
58 pll dp[MAXN+10];
59
60 ll solve(ll lambda)
61 {
62 int i, j;
63 cht.init();
64 for(i=1; i<=N; i++)
65 {
66 dp[i]={2*(A[i].y-A[1].x+1)*(A[i].y-A[1].x+1)+lambda, 1};
67 if(i!=1)
68 {
69 pll val=cht.query(A[i].y);
70 dp[i]=min(dp[i],
71 {val.first+2*A[i].y*A[i].y+lambda, val.second+1});
72 }
73
74 cht.update({-2*2*(A[i+1].x-1),
75 2*(A[i+1].x-1)*(A[i+1].x-1)-
76 2*max(0ll, A[i].y-A[i+1].x+1)*
77 max(0ll, A[i].y-A[i+1].x+1)+
78 dp[i].first,
79 dp[i].second});
80 }
81
82 return dp[N].second;
83 }
84
85 ll take_photos(int _N, int _M, int _K, vector<int> R, vector<int> C)
86 {
87 int i, j, k;
88 N=_N; M=_M; K=_K;
89
90 for(i=1; i<=N; i++)
91 B[i]={min(R[i-1], C[i-1]), max(R[i-1], C[i-1])};
92 sort(B+1, B+N+1, [&](const Point &p, const Point &q)
93 {
CAPITOLUL 5. IOI 2016 719

94 if(p.y==q.y) return p.x>q.x;


95 return p.y<q.y;
96 });
97
98 int cnt=1;
99 ll val=-1;
100 for(i=1; i<=N; i++)
101 if(val<B[i].x)
102 A[cnt++]=B[i], val=B[i].x;
103
104 N=cnt-1;
105 for(i=1; i<=N; i++)
106 if(A[i].x>A[i].y)
107 swap(A[i].x, A[i].y);
108
109 ll lo=-1, hi=1e15;
110 while(lo+1<hi)
111 {
112 ll mid=lo+hi>>1;
113 if(solve(mid*2+1)>K) lo=mid;
114 else hi=mid;
115 }
116
117 solve(hi*2);
118 ll ans=dp[N].first/2-K*hi;
119
120 return ans;
121 }
122
123 // BEGIN CUT
124
125 int main()
126 {
127 auto t1 = clock();
128
129 std::freopen("../tests/subtask_6/175", "r", stdin);
130 std::freopen("6-175.out.txt", "w", stdout);
131
132 int n, m, k;
133 scanf("%d %d %d", &n, &m, &k);
134 std::vector<int> r(n), c(n);
135 for (int i = 0; i < n; i++)
136 {
137 scanf("%d %d", &r[i], &c[i]);
138 }
139
140 auto t2 = clock();
141
142 long long ans = take_photos(n, m, k, r, c);
143
144 auto t3 = clock();
145
146 // BEGIN SECRET
147 //puts("098d134608c94f7413faac591054ee35");
148 // END SECRET
149
150 printf("%lld\n", ans);
151
152 auto t4 = clock();
153
154 // reset console output
155 freopen("CON", "w", stdout);
156
157 //cout<<"ans = "<<ans<<"\n";
158
159 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
160 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
161 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
162
163 return 0;
164 }
165
166 // END CUT
167 /*
168 t2-t1 = 0.296
169 t3-t2 = 1.469
CAPITOLUL 5. IOI 2016 720

170 t4-t3 = 0
171
172 Process returned 0 (0x0) execution time : 2.063 s
173 Press any key to continue.
174 */

Listing 5.6.15: alien-172712.cpp


1 // https://oj.uz/submission/172712
2
3 #include<bits/stdc++.h>
4 #include<ext/rope>
5
6 using namespace std;
7 using namespace __gnu_cxx;
8
9 #define fi first
10 #define se second
11 #define fastio ios_base::sync_with_stdio(false);cin.tie(0)
12 #define fopen freopen("input.txt", "r", stdin)
13 #define pb push_back
14 #define prec(a) cout<<fixed;cout.precision(a);
15 #define all(a) (a).begin(), (a).end()
16
17 typedef long long ll;
18 typedef long double ld;
19 typedef pair<int,int> pii;
20 typedef pair<ll,ll> pll;
21 typedef tuple<int,int,int> tiii;
22
23 const ll INF = 1e18;
24 const int inf = 2e9;
25
26 template<class T>
27 void pr(T t) {cout << t << " ";}
28
29 template<class T, class ...Args>
30 void pr(T a, Args ...args) {cout << a << " ";pr(args...);}
31
32 template<class ...Args>
33 void prl(Args ...args) {pr(args...);cout << endl;}
34
35 ll dp[101010];
36 vector<pll> tmp, p;
37 int cur=0;
38 struct Line
39 {
40 ll a, b;
41 int cnt;
42 Line(){}
43 Line(ll aa, ll bb, int cc){a=aa;b=bb;cnt=cc;}
44 };
45
46 vector<Line> line;
47 double cross(Line A, Line B)
48 {
49 return (double)(B.b-A.b)/(double)(A.a-B.a);
50 }
51
52 pll get(ll x)
53 {
54 while(cur+1<line.size()&&cross(line[cur],line[cur+1])<=(double)x)
55 cur++;
56
57 if(cur>=line.size()) return pll(0,0);
58
59 return pll(line[cur].a*x+line[cur].b,line[cur].cnt);
60 }
61
62 void add(Line nl)
63 {
64 while(line.size()>=2&&
65 cross(line[line.size()-2],line[line.size()-1])>=
66 cross(line[line.size()-2],nl))
67 line.pop_back();
CAPITOLUL 5. IOI 2016 721

68 line.push_back(nl);
69 }
70
71 int get_take(ll c)
72 {
73 cur=0;
74 line.clear();
75 add(Line(-2*(p[1].fi-1),c+(p[1].fi-1)*(p[1].fi-1),1));
76
77 pll t;
78 for(int i=1;i<(int)p.size();i++)
79 {
80 if(i>1)
81 {
82 if(p[i].fi<=p[i-1].se)
83 add(Line(-2*(p[i].fi-1),
84 c+t.fi+
85 (p[i].fi-1)*(p[i].fi-1) -
86 (p[i-1].se-p[i].fi+1)*(p[i-1].se-p[i].fi+1),
87 t.se+1));
88 else
89 add(Line(-2*(p[i].fi-1),
90 c+t.fi+(p[i].fi-1)*(p[i].fi-1),
91 t.se+1));
92 }
93
94 t = get(p[i].se);
95 t.fi+=p[i].se*p[i].se;
96 dp[i]=t.fi;
97 }
98
99 return (int)t.se;
100 }
101
102 ll take_photos(int n, int m, int k, vector<int>x, vector<int>y)
103 {
104 k=min(n,k);
105 for(int i=0;i<n;i++)
106 {
107 if(x[i]>y[i]) swap(x[i], y[i]);
108 tmp.pb({x[i],y[i]});
109 }
110
111 sort(tmp.begin(), tmp.end(), [](pii a, pii b)
112 {
113 if(a.se==b.se)
114 return a.fi>b.fi;
115 return a.se<b.se;
116 });
117 p.pb({-1,-1});
118 for(auto i:tmp)
119 {
120 while(!p.empty()&&p[p.size()-1].fi>=i.fi)
121 p.pop_back();
122 p.pb(i);
123 }
124
125 k=min(k,(int)p.size()-1);
126 ll s = 0,e=(ll)m*m;
127 ll l=-1,r=e+1,ly,ry;
128
129 while(s<=e)
130 {
131 ll mid = (s+e)/2;
132 int num = get_take(mid);
133 if(num==k) return dp[p.size()-1]-mid*k;
134 if(num>k)
135 {
136 s = mid+1;
137 if(r>num) r=num,ry=dp[p.size()-1]-mid*num;
138 }
139
140 if(num<k)
141 {
142 e = mid-1;
143 if(l<num) l=num,ly=dp[p.size()-1]-mid*num;
CAPITOLUL 5. IOI 2016 722

144 }
145 }
146
147 if(r==(ll)m*m+1) return ly;
148 if(l==-1) return ry;
149 return ry+(ly-ry)/(r-l)*(r-k);
150 }
151
152 // BEGIN CUT
153
154 int main()
155 {
156 auto t1 = clock();
157
158 std::freopen("../tests/subtask_6/175", "r", stdin);
159 std::freopen("6-175.out.txt", "w", stdout);
160
161 int n, m, k;
162 scanf("%d %d %d", &n, &m, &k);
163 std::vector<int> r(n), c(n);
164 for (int i = 0; i < n; i++)
165 {
166 scanf("%d %d", &r[i], &c[i]);
167 }
168
169 auto t2 = clock();
170
171 long long ans = take_photos(n, m, k, r, c);
172
173 auto t3 = clock();
174
175 // BEGIN SECRET
176 //puts("098d134608c94f7413faac591054ee35");
177 // END SECRET
178
179 printf("%lld\n", ans);
180
181 auto t4 = clock();
182
183 // reset console output
184 freopen("CON", "w", stdout);
185
186 //cout<<"ans = "<<ans<<"\n";
187
188 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
189 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
190 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
191
192 return 0;
193 }
194
195 // END CUT
196 /*
197 t2-t1 = 0.296
198 t3-t2 = 1.188
199 t4-t3 = 0
200
201 Process returned 0 (0x0) execution time : 1.750 s
202 Press any key to continue.
203 */

Listing 5.6.16: alien-173410.cpp


1 // https://oj.uz/submission/173410
2
3 #include "aliens.h"
4 #include <bits/stdc++.h>
5
6 #define all(v) (v).begin(), (v).end()
7 #define sortv(v) sort(all(v))
8 #define uniqv(v) (v).erase(unique(all(v)), (v).end())
9 #define pb push_back
10 #define FI first
11 #define SE second
12 #define lb lower_bound
CAPITOLUL 5. IOI 2016 723

13 #define ub upper_bound
14 #define mp make_pair
15 #define test 0
16 #define TEST if(test)
17
18 using namespace std;
19
20 typedef long long ll;
21 typedef pair<int, int> pii;
22 typedef pair<ll, ll> pll;
23 typedef vector<int> vi;
24
25 const int MOD = 1000000007; // 998244353
26 const int INF = 2e9;
27 const ll INFLL = 1e12;
28 const int MAX_N = 100000;
29
30 int N, M, K;
31 vector<pii> vt;
32 vector<ll> C;
33 ll dp[MAX_N+1];
34
35 bool sf(pii a, pii b)
36 {
37 if(a.first==b.first)
38 {
39 return a.second>b.second;
40 }
41 return a.first<b.first;
42 }
43
44 struct S
45 {
46 ll a, b;
47 int idx;
48 };
49
50 vector<S> st;
51 int idx = 0;
52
53 void add(ll a, ll b, int i)
54 {
55 while(st.size()>=2)
56 {
57 S prv = st.back(); st.pop_back();
58 if((b-prv.b) * (prv.a - st.back().a) >=
59 (a-prv.a) * (prv.b - st.back().b))
60 {
61 if(idx==st.size()) idx--;
62 continue;
63 }
64 else
65 {
66 st.pb(prv);
67 break;
68 }
69 }
70 st.pb({a, b, i});
71 }
72
73 int cnt[MAX_N+1];
74
75 ll calc(ll x, int i)
76 {
77 while(idx+1<st.size())
78 {
79 if(st[idx+1].a * x + st[idx+1].b < st[idx].a * x + st[idx].b)
80 idx++;
81 else break;
82 }
83
84 cnt[i] = cnt[st[idx].idx]+1;
85 return st[idx].a * x + st[idx].b;
86 }
87
88 int solve(ll x)
CAPITOLUL 5. IOI 2016 724

89 {
90 st.clear();
91 idx = 0;
92 for(int i=0; i<vt.size(); i++)
93 {
94 add(-4LL * (ll)vt[i].first,
95 dp[i] - 2LL * C[i] + 2LL * (ll)vt[i].first * (ll)vt[i].first,
96 i);
97
98 dp[i+1] = 2LL * (ll)vt[i].second * (ll)vt[i].second +
99 calc((ll)vt[i].second, i+1) +
100 x;
101 }
102
103 return cnt[vt.size()];
104 }
105
106 long long take_photos(int n, int m, int k,
107 std::vector<int> r, std::vector<int> c)
108 {
109 N = n; M = m; K = k;
110 for(int i=0; i<N; i++)
111 {
112 vt.pb({min(r[i], c[i]), max(r[i], c[i]) + 1});
113 }
114
115 sort(vt.begin(), vt.end(), sf);
116
117 int mx = -1;
118 for(int i=0 ;i<N; i++)
119 {
120 if(mx>=vt[i].second)
121 {
122 vt[i].second = INF;
123 vt[i].first = INF;
124 }
125 else
126 mx = max(mx, vt[i].second);
127 }
128
129 sort(vt.begin(), vt.end());
130
131 while(!vt.empty() && vt.back().first==INF && vt.back().second==INF)
132 vt.pop_back();
133
134 C.pb(0LL);
135 for(int i=1; i<vt.size(); i++)
136 {
137 C.pb(max(0LL, (ll)(vt[i-1].second - vt[i].first)) *
138 max(0LL, (ll)(vt[i-1].second - vt[i].first)));
139 }
140
141 ll s = 0, e = (ll)M*(ll)M, mid;
142 while(s<e)
143 {
144 mid = (s+e)/2LL;
145 int t = solve(2LL*mid+1LL);
146 if(t<=K)
147 {
148 e = mid;
149 }
150 else
151 {
152 s = mid+1LL;
153 }
154 }
155
156 solve(2LL*s);
157
158 return (dp[vt.size()] - 2LL * s * (ll)K)/2LL;
159 }
160
161 // BEGIN CUT
162
163 int main()
164 {
CAPITOLUL 5. IOI 2016 725

165 auto t1 = clock();


166
167 std::freopen("../tests/subtask_6/175", "r", stdin);
168 std::freopen("6-175.out.txt", "w", stdout);
169
170 int n, m, k;
171 scanf("%d %d %d", &n, &m, &k);
172 std::vector<int> r(n), c(n);
173 for (int i = 0; i < n; i++)
174 {
175 scanf("%d %d", &r[i], &c[i]);
176 }
177
178 auto t2 = clock();
179
180 long long ans = take_photos(n, m, k, r, c);
181
182 auto t3 = clock();
183
184 // BEGIN SECRET
185 //puts("098d134608c94f7413faac591054ee35");
186 // END SECRET
187
188 printf("%lld\n", ans);
189
190 auto t4 = clock();
191
192 // reset console output
193 freopen("CON", "w", stdout);
194
195 //cout<<"ans = "<<ans<<"\n";
196
197 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
198 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
199 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
200
201 return 0;
202 }
203
204 // END CUT
205 /*
206 t2-t1 = 0.296
207 t3-t2 = 1.422
208 t4-t3 = 0
209
210 Process returned 0 (0x0) execution time : 1.984 s
211 Press any key to continue.
212 */

Listing 5.6.17: alien-224940.cpp


1 // https://oj.uz/submission/224940
2
3 #include "aliens.h"
4 #include <bits/stdc++.h>
5
6 #define x first
7 #define y second
8 #define all(v) v.begin(), v.end()
9
10 using namespace std;
11
12 typedef long long ll;
13 typedef pair<ll, ll> p;
14
15 int n, m, k;
16 ll dp[101010];
17 int cut[101010];
18 p a[101010];
19 vector<p> v;
20
21 struct CHT
22 {
23 struct Line
24 {
CAPITOLUL 5. IOI 2016 726

25 ll a, b, c;
26 Line(ll a = 0, ll b = 0, ll c = 0) : a(a), b(b), c(c) {}
27 ll f(ll x){ return a * x + b; }
28 };
29
30 Line v[101010];
31 int pv, top;
32
33 ll f(int x, ll y){ return v[x].f(y); }
34
35 void init(){ pv = top = 0; }
36
37 int chk(const Line &a, const Line &b, const Line &c)
38 {
39 return (double)(a.b - b.b) / (b.a - a.a) >=
40 (double)(c.b - b.b) / (b.a - c.a);
41 }
42
43 void update(Line l)
44 {
45 while(top >= pv+2 && chk(v[top-2], v[top-1], l)) top--;
46 v[top++] = l;
47 }
48
49 p query(ll x)
50 {
51 while(pv+1 < top && v[pv].f(x) >= v[pv+1].f(x)) pv++;
52 return {v[pv].f(x), v[pv].c};
53 }
54 } cht;
55
56 void init(int N, int M, int K, const vector<int> &R, const vector<int> &C)
57 {
58 m = M, k = K; v.clear();
59 for(int i=0; i<N; i++)
60 v.emplace_back(max(R[i], C[i]) + 1, min(R[i], C[i]));
61
62 sort(all(v));
63
64 for(int i=0; i<N; i++)
65 {
66 while(n && a[n].y >= v[i].y) n--;
67 a[++n] = v[i];
68 }
69 }
70
71 int chk(ll c)
72 {
73 cht.init();
74 cht.update(CHT::Line(-2 * a[1].y * 2, a[1].y * a[1].y * 2, 0));
75
76 for(int i=1; i<=n; i++)
77 {
78 auto t = cht.query(a[i].x);
79 dp[i] = t.x + a[i].x * a[i].x * 2 + c;
80 cut[i] = cut[t.y] + 1;
81 ll ta = a[i+1].y, tb = max(0LL, a[i].x - a[i+1].y);
82 ll aa = -2 * a[i+1].y * 2;
83 ll bb = dp[i] + ta*ta*2 - tb*tb*2;
84 cht.update(CHT::Line(aa, bb, i));
85 }
86
87 return cut[n];
88 }
89
90 ll take_photos(int N, int M, int K,
91 vector<int> R, vector<int> C)
92 {
93 init(N, M, K, R, C);
94 int q = min(n, k);
95
96 ll l = 0, r = 1e15;
97 while(l < r)
98 {
99 ll m = l + r >> 1;
100 if(chk(m << 1 | 1) <= q) r = m;
CAPITOLUL 5. IOI 2016 727

101 else l = m + 1;
102 }
103
104 chk(r << 1);
105 return dp[n] / 2 - r * q;
106 }
107
108 // BEGIN CUT
109
110 int main()
111 {
112 auto t1 = clock();
113
114 std::freopen("../tests/subtask_6/175", "r", stdin);
115 std::freopen("6-175.out.txt", "w", stdout);
116
117 int n, m, k;
118 scanf("%d %d %d", &n, &m, &k);
119 std::vector<int> r(n), c(n);
120 for (int i = 0; i < n; i++)
121 {
122 scanf("%d %d", &r[i], &c[i]);
123 }
124
125 auto t2 = clock();
126
127 long long ans = take_photos(n, m, k, r, c);
128
129 auto t3 = clock();
130
131 // BEGIN SECRET
132 //puts("098d134608c94f7413faac591054ee35");
133 // END SECRET
134
135 printf("%lld\n", ans);
136
137 auto t4 = clock();
138
139 // reset console output
140 freopen("CON", "w", stdout);
141
142 //cout<<"ans = "<<ans<<"\n";
143
144 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
145 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
146 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
147
148 return 0;
149 }
150
151 // END CUT
152 /*
153 t2-t1 = 0.296
154 t3-t2 = 0.672
155 t4-t3 = 0
156
157 Process returned 0 (0x0) execution time : 2.630 s
158 Press any key to continue.
159 */

Listing 5.6.18: alien-225911.cpp


1 // https://oj.uz/submission/225911
2
3 #include <iostream>
4 #include <cstdio>
5 #include <algorithm>
6 #include <vector>
7
8 #include<ctime>
9
10 using namespace std;
11
12 #define endl ’\n’
13 #define ll long long
CAPITOLUL 5. IOI 2016 728

14 #define pi pair<ll, ll>


15 #define pii pair<int, pi>
16 #define f first
17 #define s second
18
19 const int maxn = 100001;
20 int n;
21 pi a[maxn], dp[maxn];
22 pii l[maxn];
23 int s, e;
24
25 ll sq(ll x)
26 {
27 return (ll)x * x;
28 }
29
30 bool cp(pii x, pii y, pii z)
31 {
32 return (x.s.f - z.s.f) * (y.s.s - x.s.s) >=
33 (x.s.f - y.s.f) * (z.s.s - x.s.s);
34 }
35
36 ll f(int x, ll y)
37 {
38 return l[x].s.f * y + l[x].s.s;
39 }
40
41 void g(ll x, ll y, int z)
42 {
43 while(e - s > 1 && cp(l[e - 2], l[e - 1], {z, {x, y}})) e--;
44 l[e++] = {z, {x, y}};
45 }
46
47 pi solve(ll x)
48 {
49 s = e = 0;
50 g(-2 * (a[0].f - 1), sq(a[0].f - 1), 0);
51 for(int i = 1; i <= n; i++)
52 {
53 while(e - s > 1 && f(s, a[i - 1].s) >= f(s + 1, a[i - 1].s))
54 s++;
55 dp[i] = {f(s, a[i - 1].s) + sq(a[i - 1].s) + x, dp[l[s].f].s + 1};
56 g(-2 * (a[i].f - 1),
57 dp[i].f + sq(a[i].f - 1) - sq(max((ll)0, a[i - 1].s - a[i].f + 1)),
58 i);
59 }
60
61 return dp[n];
62 }
63
64 ll take_photos(int N, int m, int k, vector<int> x, vector<int> y)
65 {
66 for(int i = 0; i < N; i++)
67 a[i] = {min(x[i], y[i]), max(x[i], y[i])};
68
69 sort(a, a + N, [&](pi x, pi y)
70 {
71 return x.f == y.f ? x.s > y.s : x.f < y.f;
72 });
73
74 n = 1;
75 for(int i = 1; i < N; i++)
76 if(a[i].s > a[n - 1].s)
77 a[n++] = a[i];
78
79 ll l = 0, r = sq(m);
80 while(r - l > 1)
81 {
82 ll mid = (l + r) / 2;
83 if(k <= solve(mid).s) l = mid;
84 else r = mid;
85 }
86
87 return solve(l).f - k * l;
88 }
89
CAPITOLUL 5. IOI 2016 729

90 // BEGIN CUT
91
92 int main()
93 {
94 auto t1 = clock();
95
96 std::freopen("../tests/subtask_6/175", "r", stdin);
97 std::freopen("6-175.out.txt", "w", stdout);
98
99 int n, m, k;
100 scanf("%d %d %d", &n, &m, &k);
101 std::vector<int> r(n), c(n);
102 for (int i = 0; i < n; i++)
103 {
104 scanf("%d %d", &r[i], &c[i]);
105 }
106
107 auto t2 = clock();
108
109 long long ans = take_photos(n, m, k, r, c);
110
111 auto t3 = clock();
112
113 // BEGIN SECRET
114 //puts("098d134608c94f7413faac591054ee35");
115 // END SECRET
116
117 printf("%lld\n", ans);
118
119 auto t4 = clock();
120
121 // reset console output
122 freopen("CON", "w", stdout);
123
124 //cout<<"ans = "<<ans<<"\n";
125
126 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
127 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
128 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
129
130 return 0;
131 }
132
133 // END CUT
134 /*
135 t2-t1 = 0.312
136 t3-t2 = 0.797
137 t4-t3 = 0
138
139 Process returned 0 (0x0) execution time : 1.375 s
140 Press any key to continue.
141 */

Listing 5.6.19: alien-228077.cpp


1 // https://oj.uz/submission/228077
2
3 #include "aliens.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 using ll = long long;
9
10 struct TConvexHullTrick
11 {
12 struct TLine
13 {
14 ll a;
15 ll b;
16 int id;
17
18 TLine(ll a = 0, ll b = 0, int id = 0)
19 : a(a), b(b), id(id) {
20 }
CAPITOLUL 5. IOI 2016 730

21 };
22
23 std::deque<TLine> Lines;
24
25 TConvexHullTrick()
26 {
27 Lines.clear();
28 }
29
30 bool bad(const TLine& l1, const TLine& l2, const TLine& l3)
31 {
32 return (l1.b - l3.b) * (l2.a - l1.a) <
33 (l1.b - l2.b) * (l3.a - l1.a);
34 }
35
36 void add(ll a, ll b, int id)
37 {
38 TLine line(a, b, id);
39 while (Lines.size() >= 2 &&
40 bad(Lines[Lines.size()-2], Lines[Lines.size()-1], line))
41 {
42 Lines.pop_back();
43 }
44 Lines.push_back(line);
45 }
46
47 ll func(const TLine &d, ll x)
48 {
49 return d.a * x + d.b;
50 }
51
52 std::pair<ll, int> get(ll x)
53 {
54 while (Lines.size() >= 2 && func(Lines[0], x) > func(Lines[1], x))
55 {
56 Lines.pop_front();
57 }
58 return std::make_pair(func(Lines[0], x), Lines[0].id);
59 }
60 };
61
62
63 namespace Solver
64 {
65 const int N = 1e5 + 5;
66 std::pair<int, int> input[N];
67 int l[N], r[N];
68 int n, m, k;
69
70 ll dp[N];
71 int trace[N];
72
73 void process()
74 {
75 std::sort(input, input + n, [](const auto& x, const auto& y)
76 {
77 return x.first < y.first ||
78 (x.first == y.first && x.second > y.second);
79 });
80
81 int rmax = -1;
82 int cnt = 0;
83 for (int i = 0; i != n; ++i)
84 {
85 if (rmax < input[i].second)
86 {
87 rmax = input[i].second;
88 ++cnt;
89 l[cnt] = input[i].first;
90 r[cnt] = input[i].second;
91 }
92 }
93
94 n = cnt;
95 }
96
CAPITOLUL 5. IOI 2016 731

97 int check(ll lambda)


98 {
99 TConvexHullTrick cht;
100
101 memset(dp, 0, sizeof dp);
102 memset(trace, 0, sizeof trace);
103
104 auto sqr = [](int i, int j, bool checkZero)
105 {
106 if (!checkZero || r[j] >= l[i])
107 {
108 return (r[j] - l[i]) * 1ll *(r[j] - l[i]);
109 }
110 else
111 {
112 return 0ll;
113 }
114 };
115
116 cht.add(-2 * l[1], 1LL * l[1] * l[1], 0);
117 for (int i = 1; i <= n; ++i)
118 {
119 pair<ll, int> g = cht.get(r[i]);
120
121 dp[i] = g.first + 1LL * r[i] * r[i] + lambda;
122 trace[i] = g.second;
123
124 ll bCoef = dp[i]-sqr(i+1, i, true) + 1ll * l[i+1] * l[i+1];
125 cht.add(-2 * l[i + 1], bCoef, i);
126 }
127
128 int pos = n, cnt = 0;
129 while(pos)
130 {
131 ++cnt;
132 pos = trace[pos];
133 }
134 return cnt;
135 }
136
137 ll solve()
138 {
139 process();
140
141 ll l = 0, r = 1e13;
142 while(l < r)
143 {
144 ll mid = ((l + r) >> 1);
145 if (check(mid) <= k)
146 {
147 r = mid;
148 }
149 else
150 {
151 l = mid + 1;
152 }
153 }
154
155 check(l); // l = lambda_opt
156 return dp[n] - l * k;
157 }
158 };
159
160 ll take_photos(int n, int m, int k,
161 vector<int> r, vector<int> c)
162 {
163 Solver::n = n;
164 Solver::m = m;
165 Solver::k = k;
166 for (int i = 0; i < n; ++i)
167 {
168 if (r[i] > c[i])
169 {
170 std::swap(r[i], c[i]);
171 }
172
CAPITOLUL 5. IOI 2016 732

173 Solver::input[i].first = r[i];


174 Solver::input[i].second = ++c[i];
175 }
176
177 return Solver::solve();
178 }
179
180 // BEGIN CUT
181
182 int main()
183 {
184 auto t1 = clock();
185
186 std::freopen("../tests/subtask_6/175", "r", stdin);
187 std::freopen("6-175.out.txt", "w", stdout);
188
189 int n, m, k;
190 scanf("%d %d %d", &n, &m, &k);
191 std::vector<int> r(n), c(n);
192 for (int i = 0; i < n; i++)
193 {
194 scanf("%d %d", &r[i], &c[i]);
195 }
196
197 auto t2 = clock();
198
199 long long ans = take_photos(n, m, k, r, c);
200
201 auto t3 = clock();
202
203 // BEGIN SECRET
204 //puts("098d134608c94f7413faac591054ee35");
205 // END SECRET
206
207 printf("%lld\n", ans);
208
209 auto t4 = clock();
210
211 // reset console output
212 freopen("CON", "w", stdout);
213
214 //cout<<"ans = "<<ans<<"\n";
215
216 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
217 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
218 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
219
220 return 0;
221 }
222
223 // END CUT
224 /*
225 t2-t1 = 0.296
226 t3-t2 = 3.047
227 t4-t3 = 0
228
229 Process returned 0 (0x0) execution time : 3.594 s
230 Press any key to continue.
231 */

5.6.3 *Rezolvare detaliat 


Capitolul 6

IOI 201531

6.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 ecare 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  e 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 ecare
sector se poate aa un num r oarecare de echipe. Unele sectoare pot  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 ecare echip , iar dup  livrarea ultimului
suvenir urmeaz  s  revin  în sectorul 0. De remarcat c  unele echipe se pot aa î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 ecare moment Aman duce unul sau mai multe suvenire pân 
ajunge la un sector în care se a  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.
31
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).

733
CAPITOLUL 6. IOI 2015 734

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
nal, 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  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 a  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 naliza
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 a³eaz  valoarea returnat  de delivery.


Timp maxim de executare/test: 2.0 secunde
Memorie: total 1500 MB

6.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 6. IOI 2015 735

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 rst 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 rst 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 benet 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  dierent 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 6. IOI 2015 736

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.

6.1.2 Coduri surs 

Listing 6.1.1: boxes-16533.cpp


1 // https://oj.uz/submission/16533 492 ms 196104 KB
2
3 #include <algorithm>
4 #include <stdio.h>
5
6 long long L[11111111];
7 long long R[11111111];
8
9 long long delivery(int n,int k,int l,int a[])
10 {
11 long long r;
12 int i;
13 for(i=1;i<=n;i++)
14 {
15 L[i]=std::min(a[i-1]<<1,l);
16 if(i>k)L[i]+=L[i-k];
17 }
18 r=L[n];
19 for(i=n-1;i>=0;i--)
20 {
21 R[i]=std::min((l-a[i])<<1,l);
22 if(i+k<n)R[i]+=R[i+k];
23 r=std::min(r,L[i]+R[i]);
24 }
25 return r;
26 }
27
28 // ----------------- start grader ---------------------
29
30 static char _buffer[1024];
31
32 static int _currentChar = 0;
33 static int _charsNumber = 0;
34
35 static FILE *_inputFile, *_outputFile;
36
37 static inline int _read()
38 {
39 if (_charsNumber < 0)
40 {
41 exit(1);
42 }
43 if (!_charsNumber || _currentChar == _charsNumber)
44 {
45 _charsNumber = (int)fread(_buffer,
46 sizeof(_buffer[0]),
47 sizeof(_buffer),
48 _inputFile);
49 _currentChar = 0;
50 }
51 if (_charsNumber <= 0)
52 {
53 return -1;
54 }
55 return _buffer[_currentChar++];
56 }
57
58 static inline int _readInt()
CAPITOLUL 6. IOI 2015 737

59 {
60 int c, x, s;
61 c = _read();
62 while (c <= 32) c = _read();
63 x = 0;
64 s = 1;
65 if (c == ’-’)
66 {
67 s = -1;
68 c = _read();
69 }
70 while (c > 32)
71 {
72 x *= 10;
73 x += c - ’0’;
74 c = _read();
75 }
76 if (s < 0) x = -x;
77 return x;
78 }
79
80 int main()
81 {
82 _inputFile = fopen("../tests/subtask6/09", "rb");
83 _outputFile = fopen("boxes.out", "w");
84
85 int N, K, L, i;
86 N = _readInt();
87 K = _readInt();
88 L = _readInt();
89
90 int *p = (int*)malloc(sizeof(int) * (unsigned int)N);
91
92 for (i = 0; i < N; i++)
93 {
94 p[i] = _readInt();
95 }
96
97 fprintf(_outputFile, "%lld\n", delivery(N, K, L, p));
98 return 0;
99 }
100 // ----------------- start grader ---------------------
101 /*
102 execution time : 2.288 s
103 */

Listing 6.1.2: boxes-64042.cpp


1 // https://oj.uz/submission/64042 381 ms 39552 KB
2
3 #include "boxes.h"
4 #include <map>
5 #include <iostream>
6 #include <algorithm>
7 #include <assert.h>
8
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 long long delivery(int n, int k, int l, int p[])
13 {
14
15 if (n == 1)
16 {
17 return std::min(p[0] * 2LL, (l - p[0]) * 2LL);
18 }
19
20 int here = n;
21 for (int i = 0; i < n; ++i)
22 {
23 if (p[i] > l - p[i] && here == n)
24 {
25 here = i;
26 }
27 if (i >= here)
CAPITOLUL 6. IOI 2015 738

28 {
29 p[i] = l - p[i];
30 }
31 }
32
33 if (here == 0)
34 {
35 long long int sumleft = p[0], leftleft = -1;
36 int cur = k - 1;
37
38 for (int i = 1; i < n; ++i)
39 {
40 if (cur == 0)
41 {
42 sumleft += p[i - 1] + p[i] * 1LL;
43 cur = k - 1;
44 }
45 else
46 {
47 sumleft += p[i - 1] - p[i] * 1LL;
48 --cur;
49 }
50 }
51
52 sumleft += p[n - 1] * 1LL;
53
54 return sumleft;
55 }
56 else
57 if (here == n)
58 {
59 long long int sumright = p[here - 1], leftright = -1;
60 int cur = k - 1;
61
62 for (int i = here - 2; i >= 0; --i)
63 {
64 if (cur == 0)
65 {
66 sumright += p[i + 1] + p[i] * 1LL;
67 cur = k - 1;
68 }
69 else
70 {
71 sumright += p[i + 1] - p[i] * 1LL;
72 --cur;
73 }
74 }
75
76 sumright += p[0] * 1LL;
77
78 return sumright;
79 }
80
81 long long int sumleft = p[here - 1];
82 int cur = k - 1;
83
84 for (int i = here - 2; i >= 0; --i)
85 {
86 if (cur == 0)
87 {
88 sumleft += p[i + 1] + p[i] * 1LL;
89 cur = k - 1;
90 }
91 else
92 {
93 sumleft += p[i + 1] - p[i] * 1LL;
94 --cur;
95 }
96 }
97
98 sumleft += p[0] * 1LL;
99
100 long long int sumright = p[here];
101 cur = k - 1;
102
103 for (int i = here + 1; i < n; ++i)
CAPITOLUL 6. IOI 2015 739

104 {
105 if (cur == 0)
106 {
107 sumright += p[i - 1] + p[i] * 1LL;
108 cur = k - 1;
109 }
110 else
111 {
112 sumright += p[i - 1] - p[i] * 1LL;
113 --cur;
114 }
115 }
116
117 sumright += p[n - 1] * 1LL;
118
119 long long int curmin = sumleft + sumright;
120
121 if (k == 1)
122 return curmin;
123
124 long long int curval, min = 1e17;
125 int start;
126 for (int i = std::max(here - k + 1, 0);; ++i)
127 {
128 if (i >= here)
129 break;
130 if (i + k - 1 >= n)
131 break;
132
133 curval = l;
134
135 if (i != 0)
136 {
137 start = i - 1;
138
139 while (1)
140 {
141 if (start == i - 1)
142 curval += p[start] * 1LL;
143 else
144 curval += p[start] * 2LL;
145
146 if (start - k >= 0)
147 {
148 curval += p[start] - p[start - k] * 1LL;
149 start -= k;
150 }
151 else
152 {
153 curval += p[start] * 1LL;
154 break;
155 }
156 }
157 }
158 if (i + k < n)
159 {
160 start = i + k;
161
162 while (1)
163 {
164 if (start == i + k)
165 curval += p[start] * 1LL;
166 else
167 curval += p[start] * 2LL;
168
169 if (start + k < n)
170 {
171 curval += p[start] - p[start + k] * 1LL;
172 start += k;
173 }
174 else
175 {
176 curval += p[start] * 1LL;
177 break;
178 }
179 }
CAPITOLUL 6. IOI 2015 740

180 }
181
182 if (curval < min)
183 min = curval;
184 }
185
186 return std::min(curmin, min);
187 }
188
189 // ----------------- start grader ---------------------
190
191 static char _buffer[1024];
192
193 static int _currentChar = 0;
194 static int _charsNumber = 0;
195
196 static FILE *_inputFile, *_outputFile;
197
198 static inline int _read()
199 {
200 if (_charsNumber < 0)
201 {
202 exit(1);
203 }
204 if (!_charsNumber || _currentChar == _charsNumber)
205 {
206 _charsNumber = (int)fread(_buffer,
207 sizeof(_buffer[0]),
208 sizeof(_buffer),
209 _inputFile);
210 _currentChar = 0;
211 }
212 if (_charsNumber <= 0)
213 {
214 return -1;
215 }
216 return _buffer[_currentChar++];
217 }
218
219 static inline int _readInt()
220 {
221 int c, x, s;
222 c = _read();
223 while (c <= 32) c = _read();
224 x = 0;
225 s = 1;
226 if (c == ’-’)
227 {
228 s = -1;
229 c = _read();
230 }
231 while (c > 32)
232 {
233 x *= 10;
234 x += c - ’0’;
235 c = _read();
236 }
237 if (s < 0) x = -x;
238 return x;
239 }
240
241 int main()
242 {
243 _inputFile = fopen("../tests/subtask6/09", "rb");
244 _outputFile = fopen("boxes.out", "w");
245
246 int N, K, L, i;
247 N = _readInt();
248 K = _readInt();
249 L = _readInt();
250
251 int *p = (int*)malloc(sizeof(int) * (unsigned int)N);
252
253 for (i = 0; i < N; i++)
254 {
255 p[i] = _readInt();
CAPITOLUL 6. IOI 2015 741

256 }
257
258 fprintf(_outputFile, "%lld\n", delivery(N, K, L, p));
259 return 0;
260 }
261 // ----------------- start grader ---------------------
262 /*
263 execution time : 2.609 s
264 */

Listing 6.1.3: boxes-70567.cpp


1 // https://oj.uz/submission/70567 425 ms 117864 KB
2
3 #include "boxes.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 #define ll long long
9 #define NN ((int)1e7+10)
10
11 ll dp[NN];
12
13 ll delivery(int n,int k, int l, int p[])
14 {
15 int id=0;
16 for(int i=0;i<n && p[i]<=l/2;i++,id++)
17 dp[i]=2*p[i]+((i-k<0)?0:dp[i-k]);
18
19 for(int i=n-1;i>=id;i--)
20 dp[i]=2*(l-p[i])+((i+k>=n)?0:dp[i+k]);
21
22 ll ans=min((ll)(n+k-1)/k*l,((id>0)?dp[id-1]:0)+dp[id]);
23
24 int lft=max(0,id-k),rght=lft+k;
25 while(lft<=id && rght<=n)
26 ans=min(ans,dp[rght]+((lft>0)?dp[lft-1]:0)+l),
27 lft++,rght++;
28 return ans;
29 }
30
31 // ----------------- start grader ---------------------
32
33 static char _buffer[1024];
34
35 static int _currentChar = 0;
36 static int _charsNumber = 0;
37
38 static FILE *_inputFile, *_outputFile;
39
40 static inline int _read()
41 {
42 if (_charsNumber < 0)
43 {
44 exit(1);
45 }
46 if (!_charsNumber || _currentChar == _charsNumber)
47 {
48 _charsNumber = (int)fread(_buffer,
49 sizeof(_buffer[0]),
50 sizeof(_buffer),
51 _inputFile);
52 _currentChar = 0;
53 }
54 if (_charsNumber <= 0)
55 {
56 return -1;
57 }
58 return _buffer[_currentChar++];
59 }
60
61 static inline int _readInt()
62 {
63 int c, x, s;
CAPITOLUL 6. IOI 2015 742

64 c = _read();
65 while (c <= 32) c = _read();
66 x = 0;
67 s = 1;
68 if (c == ’-’)
69 {
70 s = -1;
71 c = _read();
72 }
73 while (c > 32)
74 {
75 x *= 10;
76 x += c - ’0’;
77 c = _read();
78 }
79 if (s < 0) x = -x;
80 return x;
81 }
82
83 int main()
84 {
85 _inputFile = fopen("../tests/subtask6/09", "rb");
86 _outputFile = fopen("boxes.out", "w");
87
88 int N, K, L, i;
89 N = _readInt();
90 K = _readInt();
91 L = _readInt();
92
93 int *p = (int*)malloc(sizeof(int) * (unsigned int)N);
94
95 for (i = 0; i < N; i++)
96 {
97 p[i] = _readInt();
98 }
99
100 fprintf(_outputFile, "%lld\n", delivery(N, K, L, p));
101 return 0;
102 }
103 // ----------------- start grader ---------------------
104 /*
105 execution time : 2.578 s
106 */

Listing 6.1.4: checkerBoxes.cpp


1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 long long readLong(InStream& in)
8 {
9 long long res = in.readLong();
10 return res;
11 }
12
13 int main()
14 {
15 int argc=4;
16
17 char* argv[] =
18 {
19 (char*)"checker",
20 (char*)"../tests/subtask6/09", // argv[1]=<input-file>
21 (char*)"boxes.out", // argv[2]=<output-file>
22 (char*)"../tests/subtask6/09.a", // argv[3]=<answer-file>
23 };
24
25 cout<<"argc = "<<argc<<"\n";
26 for(int kk=0;kk<argc;kk++)
27 cout<<argv[kk]<<"\n";
28 cout<<"----------------------\n";
29
CAPITOLUL 6. IOI 2015 743

30 registerTestlibCmd(argc, argv);
31
32 long long pans = readLong(ouf);
33 long long jans = readLong(ans);
34
35 if (pans != jans)
36 {
37 quitf(_wa, "Wrong answer: output = %lld, expected = %lld", pans, jans);
38 }
39 else
40 {
41 quitf(_ok, "Correct answer: answer = %lld", pans);
42 }
43 }

6.1.3 *Rezolvare detaliat 

6.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 ecare 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, ecare 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 ecare 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, ecare corespunzând unui nou set de ³ase monede.
Programul t u trebuie sa implementeze funcµiile init ³i orderCoins. Pe parcursul ec 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 ecare 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 6. IOI 2015 744

 Aceast  funcµie va  apelat  exact o dat  pentru ecare 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  e 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
ecare 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 
e trei întregi distincµi, ecare î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  e patru întregi distincµi, ecare î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  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  rulat de mai multe ori ³i pe mai multe teste la ecare rulare. Fie r num rul
de rul ri ale programului t u. Acest num r este xat în datele de test. Dac  programul t u nu
ordoneaz  monezile corect la oricare test din oricare rulare, vei obµine 0 puncte. Altfel, ecare
rulare va  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 ecare test atunci x 0.) Scorul rul rii curente va 
100
r xy ©51
, rotunjit în jos la dou  zecimale.
în particular, dac  programul face cel mult Q cânt riri pe ecare 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 6. IOI 2015 745

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
ˆ ecare 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 a³eaz  ³irul trimis ca parametru de funcµia answer().
Timp maxim de executare/test: 1.0 secunde
Memorie: total 1500 MB

6.2.1 Indicaµii de rezolvare

This is based on the well-known problem of ordering ve coins with 7 (binary) weighing. There
are 120 permutations and 128 possible sequences of seven answers, and it is indeed possible to
nd a strategy which always works.
In our problem, there are 720 possible answers, and with at most 6 ternary weighing, theore-
6
tically we could obtain 3 729 possible answers. The gap is very small, but it turns out that it
is again possible to nd 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 y.

6.2.2 Coduri surs 


CAPITOLUL 6. IOI 2015 746

Listing 6.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 6. IOI 2015 747

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 6. IOI 2015 748

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 6.2.2: scales-45773.cpp


1 // https://oj.uz/submission/45773 109 ms 1512 KB
2
3 #include "graderlib.c"
4
5 #include "scales.h"
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 int p[720][7] = {{0, 1, 2, 3, 4, 5, 6}};
12
13 struct query
14 {
15 query() {}
16 query(int t, int a, int b, int c, int d) : t(t),a(a),b(b),c(c),d(d) {}
17 int t, a, b, c, d;
18 int moi(int k)
19 {
20 if (t==1)
21 { // Heaviest
22 if (p[k][a] > p[k][b] and p[k][a] > p[k][c]) return 0;
23 else return (p[k][b] > p[k][c]) ? 1 : 2;
24 }
25 if (t==2)
26 { // Lightest
27 if (p[k][a] < p[k][b] and p[k][a] < p[k][c]) return 0;
28 else return (p[k][b] < p[k][c]) ? 1 : 2;
29 }
30 if (t==3)
31 { // Median
32 if (p[k][a] < p[k][b])
33 {
34 if (p[k][b] < p[k][c]) return 1;
35 else return p[k][a] < p[k][c] ? 2 : 0;
36 }
37 else
38 {
39 if (p[k][a] < p[k][c]) return 0;
40 else return p[k][b] < p[k][c] ? 2 : 1;
41 }
42 }
43
44 if (t==4)
45 { // NextHeaviest
46 int x = p[k][a], y = p[k][b], z = p[k][c];
47 if (not (x < p[k][d] and y < p[k][d] and z < p[k][d]))
CAPITOLUL 6. IOI 2015 749

48 {
49 if (x < p[k][d]) x = 7;
50 if (y < p[k][d]) y = 7;
51 if (z < p[k][d]) z = 7;
52 }
53 if (x<y and x<z) return 0;
54 else return y<z ? 1 : 2;
55 } return -1;
56 }
57
58 int real()
59 {
60 int res;
61 if (t==1) res = getHeaviest(a, b, c);
62 if (t==2) res = getLightest(a, b, c);
63 if (t==3) res = getMedian(a, b, c);
64 if (t==4) res = getNextLightest(a, b, c, d);
65 if (res == a) return 0;
66 if (res == b) return 1;
67 if (res == c) return 2;
68 return -123; // ... !!!
69 }
70 };
71
72 vector<query> q;
73
74 struct node
75 {
76 query q;
77 node *ch[3];
78 set<int> s;
79 } *root;
80
81 bool make_tree(node* k, set<int> s)
82 {
83 k->s = s;
84 for (int i=0; i<3; i++)
85 if (!k->ch[i]) k->ch[i] = new node();
86 if (s.size() <= 1) return true;
87 for (int i=0; i<q.size(); i++)
88 {
89 set<int> r[3];
90 for (int e : s) r[q[i].moi(e)].insert(e);
91
92 if (max({r[0].size(), r[1].size(), r[2].size()}) -
93 min({r[0].size(), r[1].size(), r[2].size()}) < 2)
94 if (make_tree(k->ch[0], r[0]) and
95 make_tree(k->ch[1], r[1]) and
96 make_tree(k->ch[2], r[2]))
97 {
98 k->q = q[i];
99 return true;
100 }
101 } return false;
102 }
103
104 void init(int T)
105 {
106 for (int x=1; x<=6; x++)
107 for (int y=x+1; y<=6; y++)
108 for (int z=y+1; z<=6; z++)
109 {
110 q.emplace_back(1, x, y, z, -1);
111 q.emplace_back(2, x, y, z, -1);
112 q.emplace_back(3, x, y, z, -1);
113
114 for (int w=1; w<=6; w++)
115 if (x!=w and y!=w and z!=w)
116 q.emplace_back(4, x, y, z, w);
117 }
118
119 for (int i=1; i<720; i++)
120 {
121 copy(p[i-1]+1, p[i-1]+7, p[i]+1);
122 next_permutation(p[i]+1, p[i]+7);
123 }
CAPITOLUL 6. IOI 2015 750

124
125 set<int> st;
126 for (int i=0; i<720; i++) st.insert(i);
127 root = new node();
128 make_tree(root, st);
129 }
130
131 void orderCoins()
132 {
133 node *now = root;
134 while (now->s.size() > 1)
135 {
136 now = now->ch[now->q.real()];
137 }
138 int ans[6], *rans = p[ *now->s.begin() ];
139 for (int i=1; i<=6; i++)
140 ans[rans[i]-1] = i;
141 answer(ans);
142 }
143
144 // ------------- start grader --------------------
145
146 int main()
147 {
148 auto t1 = clock();
149
150 int T, i;
151
152 auto t2 = clock();
153
154 T = _getNumberOfTests();
155 init(T);
156
157 auto t3 = clock();
158
159 for (i = 1; i <= T; i++)
160 {
161 _initNewTest();
162 orderCoins();
163 }
164
165 auto t4 = clock();
166
167 // reset console output
168 freopen("CON", "w", stdout);
169
170 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
171 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
172 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
173
174 return 0;
175 }
176
177 // ------------- end grader --------------------
178 /*
179 t2-t1 = 0
180 t3-t2 = 0.578
181 t4-t3 = 0
182
183 Process returned 0 (0x0) execution time : 0.625 s
184 Press any key to continue.
185 */

Listing 6.2.3: scales-115418.cpp


1 // https://oj.uz/submission/115418 14 ms 768 KB
2
3 #include "graderlib.c"
4
5 #include "scales.h"
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
CAPITOLUL 6. IOI 2015 751

11 int depmax[] = {729, 243, 81, 27, 9, 3, 1};


12
13 struct Ord
14 {
15 int a[7];
16 Ord(vector<int>& v)
17 {
18 for(int i = 0; i < 6; i++)
19 a[i + 1] = v[i];
20 }
21 };
22
23 struct Query
24 {
25 int a, b, c, d;
26 Query(int _a, int _b, int _c, int _d)
27 {
28 a = _a;
29 b = _b;
30 c = _c;
31 d = _d;
32 }
33
34 int ask()
35 {
36 int res;
37 if(d == a) res = getLightest(a, b, c);
38 else
39 if(d == b) res = getMedian(a, b, c);
40 else
41 if(d == c) res = getHeaviest(a, b, c);
42 else
43 res = getNextLightest(a, b, c, d);
44
45 if(res == a) return 1;
46 if(res == b) return 2;
47 if(res == c) return 3;
48 return -123; // ... !!!
49 }
50
51 int simask(Ord& ord)
52 {
53 int i = a, j = b, l = c, res;
54 if(ord.a[i] > ord.a[j]) swap(i, j);
55 if(ord.a[j] > ord.a[l]) swap(j, l);
56 if(ord.a[i] > ord.a[j]) swap(i, j);
57 if(a == d) res = i;
58 else
59 if(b == d) res = j;
60 else
61 if(c == d) res = l;
62 else
63 if(ord.a[d] < ord.a[i]) res = i;
64 else
65 if(ord.a[d] < ord.a[j]) res = j;
66 else
67 if(ord.a[d] < ord.a[l]) res = l;
68 else res = i;
69
70 if(res == a) return 1;
71 if(res == b) return 2;
72 if(res == c) return 3;
73 return -123; // ... !!!
74 }
75 };
76
77 vector<Query> lists;
78
79 struct Node
80 {
81 int dep;
82 Query* q;
83 vector<Ord*> pos;
84 Node* go[4];
85
86 bool init()
CAPITOLUL 6. IOI 2015 752

87 {
88 if(pos.size() <= 1) return 1;
89 int cnt[4] = {0};
90 bool ok = 0;
91 for(auto& qq : lists)
92 {
93 cnt[1] = 0;
94 cnt[2] = 0;
95 cnt[3] = 0;
96
97 for(auto& ord : pos)
98 {
99 int res = qq.simask( *ord);
100 cnt[res]++;
101 if(cnt[res] > depmax[dep]) break;
102 }
103
104 if(cnt[1] > depmax[dep]) continue;
105 if(cnt[2] > depmax[dep]) continue;
106 if(cnt[3] > depmax[dep]) continue;
107 q = &qq;
108 for(int i = 1; i <= 3; i++)
109 {
110 go[i] = new Node();
111 go[i] -> dep = dep + 1;
112 }
113
114 for(auto ord : pos)
115 {
116 go[qq.simask( *ord)] -> pos.push_back(ord);
117 }
118
119 if(!go[1] -> init())
120 {
121 delete go[1];
122 continue;
123 }
124 if(!go[2] -> init())
125 {
126 delete go[2];
127 continue;
128 }
129 if(!go[3] -> init())
130 {
131 delete go[3];
132 continue;
133 }
134
135 ok = 1;
136 break;
137 }
138 return ok;
139 }
140
141 } *root;
142
143 void create()
144 {
145 for(int i = 1; i < 5; i++)
146 {
147 for(int j = i + 1; j < 6; j++)
148 {
149 for(int l = j + 1; l <= 6; l++)
150 {
151 for(int k = 1; k <= 6; k++)
152 {
153 lists.push_back(Query(i, j, l, k));
154 }
155 }
156 }
157 }
158 }
159
160 void init(int T)
161 {
162 create();
CAPITOLUL 6. IOI 2015 753

163 root = new Node();


164 vector<int> v = {1, 2, 3, 4, 5, 6};
165
166 do
167 {
168 root -> pos.push_back(new Ord(v));
169 } while(next_permutation(v.begin(), v.end()));
170 root -> dep = 1;
171 root -> init();
172 }
173
174 void orderCoins()
175 {
176 int W[] = {1, 2, 3, 4, 5, 6};
177 Node* ans = root;
178 while(ans -> pos.size() != 1)
179 ans = ans -> go[ans -> q -> ask()];
180 for(int i = 1; i <= 6; i++)
181 {
182 W[ans -> pos[0] -> a[i] - 1] = i;
183 }
184 answer(W);
185 }
186
187 // ------------- start grader --------------------
188
189 int main()
190 {
191 auto t1 = clock();
192
193 int T, i;
194
195 auto t2 = clock();
196
197 T = _getNumberOfTests();
198 init(T);
199
200 auto t3 = clock();
201
202 for (i = 1; i <= T; i++)
203 {
204 _initNewTest();
205 orderCoins();
206 }
207
208 auto t4 = clock();
209
210 // reset console output
211 freopen("CON", "w", stdout);
212
213 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
214 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
215 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
216
217 return 0;
218 }
219
220 // ------------- end grader --------------------
221 /*
222 t2-t1 = 0
223 t3-t2 = 0.062
224 t4-t3 = 0
225
226 Process returned 0 (0x0) execution time : 0.094 s
227 Press any key to continue.
228 */

Listing 6.2.4: scales-122639.cpp


1 // https://oj.uz/submission/122639 5 ms 504 KB
2
3 #include "graderlib.c"
4
5 #include "scales.h"
6
CAPITOLUL 6. IOI 2015 754

7 #include <bits/stdc++.h>
8
9 #define pii pair<int, int>
10 #define x first
11 #define y second
12
13 using namespace std;
14
15 vector<pair<int, vector<int> > > que;
16 vector<vector<int> > perm;
17
18 int ids[] = {0, 107, 107, 107, 49, 48, 46, 45, 44, 42, 38, 37, 35, 88,
19 95, 28, 67, 74, 15, 8, 40, 40, 85, 95, 28, 64, 74, 15, 11, 40, 40, 85,
20 88, 28, 64, 67, 15, 11, 33, 33, 31, 11, 9, 31, 11, 9, 10, 52, 52, 31,
21 24, 22, 31, 24, 22, 23, 52, 52, 72, 99, 79, 29, 18, 70, 29, 83, 5, 34,
22 8, 6, 34, 8, 6, 52, 7, 52, 34, 21, 19, 34, 21, 19, 52, 20, 52, 72, 103,
23 83, 32, 18, 69, 32, 79, 5, 41, 5, 3, 41, 5, 3, 55, 55, 4, 41, 18, 16,
24 41, 18, 16, 55, 55, 17, 103, 79, 83, 39, 21, 76, 39, 72, 8, 75, 55, 75,
25 65, 65, 55, 75, 18, 31, 68, 52, 68, 65, 65, 52, 18, 68, 31, 65, 65, 1,
26 8, 21, 58, 5, 75, 58, 96, 96, 55, 86, 86, 55, 96, 5, 31, 89, 89, 52,
27 86, 86, 52, 5, 89, 31, 86, 86, 1, 8, 8, 58, 5, 73, 58, 96, 16, 18, 58,
28 52, 68, 18, 52, 22, 68, 52, 65, 55, 96, 96, 55, 58, 8, 66, 86, 52, 21,
29 21, 86, 55, 75, 75, 75, 58, 75, 68, 68, 58, 75, 18, 34, 65, 52, 65, 68,
30 68, 52, 18, 65, 34, 19, 6, 55, 68, 68, 1, 75, 5, 55, 96, 96, 58, 89, 89,
31 58, 96, 5, 34, 86, 86, 52, 89, 89, 52, 5, 86, 34, 6, 6, 55, 89, 89, 1,
32 73, 5, 55, 16, 96, 18, 55, 52, 65, 18, 52, 19, 65, 52, 68, 96, 58, 96, 55,
33 55, 11, 63, 89, 52, 24, 24, 89, 75, 58, 75, 68, 58, 68, 75, 75, 58, 68,
34 21, 41, 65, 55, 65, 75, 75, 55, 21, 65, 41, 16, 3, 52, 68, 3, 52, 75,
35 75, 1, 89, 89, 58, 96, 96, 58, 89, 8, 41, 86, 86, 55, 96, 96, 55, 8, 86,
36 41, 3, 3, 52, 66, 3, 52, 96, 96, 1, 55, 52, 65, 16, 89, 21, 52, 21, 16,
37 65, 55, 75, 89, 58, 89, 52, 52, 11, 63, 96, 55, 24, 24, 96, 68, 58, 68, 0};
38 int mx = 0;
39 int zzz;
40
41 int dfs(int u, vector<int> vec, int lv)
42 {
43 if(vec.size() == 1) return vec[0];
44 mx = max(mx, u);
45 for(int ii = ids[u];;)
46 {
47 auto& z = que[ii];
48 vector<int> Mp[6];
49 int abc;
50 if(z.x == 0)
51 abc = getHeaviest(z.y[0]+1, z.y[1]+1, z.y[2]+1);
52 if(z.x == 1)
53 abc = getLightest(z.y[0]+1, z.y[1]+1, z.y[2]+1);
54 if(z.x == 2)
55 abc = getMedian(z.y[0]+1, z.y[1]+1, z.y[2]+1);
56 if(z.x == 3)
57 abc = getNextLightest(z.y[0]+1, z.y[1]+1, z.y[2]+1, z.y[3]+1);
58
59 for(int v : vec)
60 {
61 vector<pii> val;
62 for(int i = 0; i < 3; ++i)
63 val.emplace_back(perm[v][z.y[i]], z.y[i]);
64
65 sort(val.begin(), val.end());
66
67 if(z.x == 0)
68 Mp[val[2].y].emplace_back(v);
69 else
70 if(z.x == 1)
71 Mp[val[0].y].emplace_back(v);
72 else
73 if(z.x == 2)
74 Mp[val[1].y].emplace_back(v);
75 else
76 {
77 bool st = false;
78 for(int i = 0; i < 3; ++i)
79 {
80 if(val[i].x > perm[v][z.y[3]])
81 {
82 Mp[val[i].y].emplace_back(v);
CAPITOLUL 6. IOI 2015 755

83 st = true;
84 break;
85 }
86 }
87
88 if(!st) Mp[val[0].y].emplace_back(v);
89 }
90
91 }
92
93 int step = 0;
94 for(int i = 0; i < 6; ++i) if(Mp[i].size())
95 {
96 step++;
97 if(i == abc-1)
98 return dfs(u*3 + step, Mp[i], lv / 3);
99 }
100 }
101
102 return false;
103 }
104
105 int nth = 6;
106
107 void init(int T)
108 {
109 for(int i = 0; i < (1 << nth); ++i)
110 {
111 vector<int> vec;
112 for(int j = 0; j < nth; ++j) if(i >> j & 1)
113 vec.emplace_back(j);
114 if(vec.size() == 3)
115 for(int j = 0; j < 3; ++j) que.emplace_back(j, vec);
116 if(vec.size() == 4)
117 {
118 for(int i = 0; i < 4; ++i)
119 {
120 vector<int> ret;
121 for(int j = 0; j < 4; ++j)
122 if(i != j)
123 ret.emplace_back(vec[j]);
124 ret.emplace_back(vec[i]);
125 que.emplace_back(3, ret);
126 }
127 }
128 }
129
130 vector<int> now;
131 for(int i = 0; i < nth; ++i)
132 now.emplace_back(i+1);
133 do
134 {
135 perm.emplace_back(now);
136 } while(next_permutation(now.begin(), now.end()));
137 }
138
139 void orderCoins()
140 {
141 int* ans = new int[6];
142 vector<int> zz;
143 for(int i = 0; i < perm.size(); ++i)
144 zz.emplace_back(i);
145 int k = dfs(0, zz, 2187);
146 for(int i = 0; i < 6; ++i)
147 ans[perm[k][i]-1] = i+1;
148 answer(ans);
149 }
150
151 // ------------- start grader --------------------
152
153 int main()
154 {
155 auto t1 = clock();
156
157 int T, i;
158
CAPITOLUL 6. IOI 2015 756

159 auto t2 = clock();


160
161 T = _getNumberOfTests();
162 init(T);
163
164 auto t3 = clock();
165
166 for (i = 1; i <= T; i++)
167 {
168 _initNewTest();
169 orderCoins();
170 }
171
172 auto t4 = clock();
173
174 // reset console output
175 freopen("CON", "w", stdout);
176
177 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
178 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
179 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
180
181 return 0;
182 }
183
184 // ------------- end grader --------------------
185 /*
186 t2-t1 = 0
187 t3-t2 = 0
188 t4-t3 = 0.078
189
190 Process returned 0 (0x0) execution time : 0.203 s
191 Press any key to continue.
192 */

Listing 6.2.5: checkerScales.cpp


1 #include "testlib.h"
2 #include<iostream>
3
4 using namespace std;
5
6 int main()
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/40", // argv[1]=<input-file>
14 (char*)"scales.out", // argv[2]=<output-file>
15 (char*)"../tests/40.a", // argv[3]=<answer-file>
16 };
17 cout<<"argc = "<<argc<<"\n";
18 for(int kk=0;kk<argc;kk++)
19 cout<<argv[kk]<<"\n";
20 cout<<"----------------------\n";
21
22 registerChecker("scales", argc, argv);
23 compareRemainingLines(1); // incepand cu linia nr 1
24
25 return 0;
26 }

6.2.3 *Rezolvare detaliat 

6.3 Teams
Problema 3 - Teams 100 de puncte
CAPITOLUL 6. IOI 2015 757

Author: Adam Karczmarz (POL)

Avem o clas  cu studenµi numerotaµi de la 0 la N  1. în ecare 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 ecare 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  inclus numai într-o echip  care conµine între Ai ³i B i membri. în
ecare zi un student poate  inclus în cel mult o echip . Unii studenµi pot s  nu e inclu³i în nicio
echip . Fiecare echip  va lucra la un singur proiect.
Profesorul a ales deja proiectele pentru ecare din urm toarele Q zile. Pentru ecare 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 ecare 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  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 ind 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 ecare 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 ecare î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 ecare 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 ecare zi.
 M : num rul de proiecte pentru aceast  zi.
 K : un ³ir de lungime M conµinând dimensiunile cerute pentru echip  ecarui 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 ecare 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 6. IOI 2015 758

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 ecare întrebare, grader-ul de pe calculatorul a³eaz  valoarea returnat  de can.


Timp maxim de executare/test: 4.0 secunde
Memorie: total 1500 MB

6.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 nd the right place for the new corner.
O n log n preprocessing, O m log n  m log n query
2 2

We can nd 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 rst solution
if m % n, - we run the second solution if m & n.
O n log n preprocessing, O m log n query
2

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 6. IOI 2015 759

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.
Another O m2 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 benecial to take index
i than j , while computing the minimum in the formula for Dk . Then, for any l % k , it's also more
benecial 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 benecial. 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 nd W i, j  in time
O log n.
Let B Dj  Di .
We need to nd 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 rst point with x
¬
' x and last with x & xinA, mid
¬

ˆ the rst 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 6. IOI 2015 760

6.3.2 Coduri surs 

Listing 6.3.1: teams-17286.cpp


1 // https://oj.uz/submission/17286 1065 ms 330208 KB
2
3 #include "teams.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include <bits/stdc++.h>
9
10 using namespace std;
11
12 #define type(x) __typeof((x).begin())
13 #define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); it++)
14
15 typedef long long ll;
16 typedef pair < int, int > ii;
17
18 const int inf = 1e9 + 333;
19 const ll linf = 1e18 + inf;
20
21 const int N = 5e5 + 5;
22
23 ii a[N];
24
25 class node
26 { public:
27 int x, l, r, lazy;
28 node()
29 {
30 x = l = r = lazy = 0;
31 }
32 };
33
34 node t[N * 40];
35 int del, ps[N];
36
37 int cnt = 1;
38
39 int insert(int x, int l, int r, int x1)
40 {
41 int nw = cnt++;
42 if(l == r)
43 {
44 t[nw].x = 1;
45 return nw;
46 }
47 int m = l + r >> 1;
48 if(x1 <= m)
49 {
50 t[nw].l = insert(t[x].l, l, m, x1);
51 t[nw].r = t[x].r;
52 }
53 else
54 {
55 t[nw].l = t[x].l;
56 t[nw].r = insert(t[x].r, m + 1, r, x1);
57 }
58 t[nw].x = t[t[nw].l].x + t[t[nw].r].x;
59 return nw;
60 }
61
62 int n;
63
64 void init(int x, int l, int r)
65 {
66 if(l == r)
67 return;
68 int m = l + r >> 1;
69 t[x].l = cnt++;
70 t[x].r = cnt++;
CAPITOLUL 6. IOI 2015 761

71 init(t[x].l, l, m);
72 init(t[x].r, m + 1, r);
73 }
74
75 void init(int N, int A[], int B[])
76 {
77 n = N;
78 for(int i = 1; i <= n; i++)
79 {
80 a[i] = ii(B[i - 1], A[i - 1]);
81 }
82
83 sort(a + 1, a + n + 1);
84
85 vector < ii > vs;
86 for(int i = 1; i <= n; i++)
87 vs.push_back(ii(a[i].second, i));
88
89 sort(vs.begin(), vs.end());
90
91 int ptr = 0;
92 ps[0] = cnt++;
93 init(ps[0], 1, n);
94 for(int i = 1; i <= n; i++)
95 {
96 ps[i] = ps[i - 1];
97 while(ptr < vs.size() and vs[ptr].first <= i)
98 {
99 ps[i] = insert(ps[i], 1, n, vs[ptr].second);
100 ptr++;
101 }
102 }
103 del = cnt++;
104 init(del, 1, n);
105 }
106
107 void push(int x)
108 {
109 if(t[x].lazy)
110 {
111 t[t[x].l].x = t[t[t[x].lazy].l].x;
112 t[t[x].r].x = t[t[t[x].lazy].r].x;
113 t[t[x].l].lazy = t[t[x].lazy].l;
114 t[t[x].r].lazy = t[t[x].lazy].r;
115 t[x].lazy = 0;
116 }
117 }
118
119 int get(int x, int del, int l, int r, int x1, int x2, int k)
120 {
121 if(x1 <= l and r <= x2 and t[x].x - t[del].x <= k)
122 {
123 t[del].lazy = x;
124 int ret = t[x].x - t[del].x;
125 t[del].x += ret;
126 return ret;
127 }
128
129 if(x2 < l or r < x1 or l == r)
130 return 0;
131 int m = l + r >> 1;
132 push(del);
133 int lf = get(t[x].l, t[del].l, l, m, x1, x2, k);
134 if(lf == k)
135 {
136 t[del].x = t[t[del].l].x + t[t[del].r].x;
137 return k;
138 }
139 k -= lf;
140 int rf = get(t[x].r, t[del].r, m + 1, r, x1, x2, k);
141 t[del].x = t[t[del].l].x + t[t[del].r].x;
142 return lf + rf;
143 }
144
145 void clear(int x)
146 {
CAPITOLUL 6. IOI 2015 762

147 if(t[x].x and t[x].l)


148 clear(t[x].l);
149 if(t[x].x and t[x].r)
150 clear(t[x].r);
151 t[x].x = t[x].lazy = 0;
152 }
153
154 int can(int M, int K[])
155 {
156 sort(K, K + M);
157 int sum = 0;
158 for(int i = 0; i < M; i++)
159 {
160 if(sum + K[i] > n)
161 return 0;
162 sum += K[i];
163 }
164 clear(del);
165 for(int i = 0; i < M; i++)
166 {
167 int id = lower_bound(a + 1, a + n + 1, ii(K[i], 0)) - a;
168 int res = get(ps[K[i]], del, 1, n, id, n, K[i]);
169 if(res != K[i])
170 return 0;
171 }
172 return 1;
173 }
174
175 // --------------- begin grader ----------------------
176
177 static char _buffer[1024];
178 static int _currentChar = 0;
179 static int _charsNumber = 0;
180 static FILE *_inputFile, *_outputFile;
181
182 static inline int _read()
183 {
184 if (_charsNumber < 0)
185 {
186 exit(1);
187 }
188
189 if (!_charsNumber || _currentChar == _charsNumber)
190 {
191 _charsNumber = (int)fread(_buffer,
192 sizeof(_buffer[0]),
193 sizeof(_buffer),
194 _inputFile);
195 _currentChar = 0;
196 }
197
198 if (_charsNumber <= 0)
199 {
200 return -1;
201 }
202
203 return _buffer[_currentChar++];
204 }
205
206 static inline int _readInt()
207 {
208 int c, x, s;
209 c = _read();
210 while (c <= 32) c = _read();
211 x = 0;
212 s = 1;
213 if (c == ’-’)
214 {
215 s = -1;
216 c = _read();
217 }
218 while (c > 32)
219 {
220 x *= 10;
221 x += c - ’0’;
222 c = _read();
CAPITOLUL 6. IOI 2015 763

223 }
224 if (s < 0) x = -x;
225 return x;
226 }
227
228 int main()
229 {
230 _inputFile = fopen("../tests/72", "rb");
231 _outputFile = fopen("teams.out", "w");
232
233 int N;
234 N = _readInt();
235
236 int *A = (int*)malloc(sizeof(int)*(unsigned int)N);
237 int *B = (int*)malloc(sizeof(int)*(unsigned int)N);
238
239 for (int i = 0; i < N; ++i)
240 {
241 A[i] = _readInt();
242 B[i] = _readInt();
243 }
244
245 init(N, A, B);
246
247 int Q;
248 Q = _readInt();
249 for (int i = 0; i < Q; ++i)
250 {
251 int M;
252 M = _readInt();
253 int *K = (int*)malloc(sizeof(int)*(unsigned int)M);
254 for (int j = 0; j < M; ++j)
255 {
256 K[j] = _readInt();
257 }
258 fprintf(_outputFile,"%d\n", can(M, K));
259 }
260
261 return 0;
262 }
263 // --------------- end grader ----------------------
264 /*
265 execution time : 3.156 s
266 */

Listing 6.3.2: teams-69885.cpp


1 // https://oj.uz/submission/69885 1675 ms 205196 KB
2
3 #include "teams.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include<bits/stdc++.h>
9
10 using namespace std;
11
12 #define mx 500005
13 #define tm (tl+tr >> 1)
14
15 int s[mx*20],L[mx*20],R[mx*20],root[mx],nw;
16
17 int up(int v, int tl, int tr, int i)
18 {
19 if(tl > i || tr < i) return v;
20 int w = ++nw;
21 if(tl < tr)
22 {
23 L[w] = up(L[v],tl,tm,i);
24 R[w] = up(R[v],tm+1,tr,i);
25 }
26 s[w] = s[v]+1;
27 return w;
28 }
CAPITOLUL 6. IOI 2015 764

29
30 int qry(int a, int b, int tl, int tr, int l, int r)
31 {
32 if(tl > r || tr < l) return 0;
33 if(tl >= l && tr <= r) return s[a]-s[b];
34 return qry(L[a],L[b],tl,tm,l,r) + qry(R[a],R[b],tm+1,tr,l,r);
35 }
36
37 int bs(int a, int b, int tl, int tr, int k)
38 {
39 if(tl == tr) return tl;
40 if(s[ L[a] ] - s[ L[b] ] < k)
41 return bs(R[a],R[b],tm+1,tr,k-(s[ L[a] ] - s[ L[b] ]));
42 return bs(L[a],L[b],tl,tm,k);
43 }
44
45 #define mp make_pair
46 #define st first
47 #define nd second
48 int n;
49
50 int can(int m, int *K)
51 {
52 int t,i,l,md,r,x,y,z,las,req,ex;
53 stack < pair < int , pair<int,int> > > S;
54 S.push(mp(0,mp(mx,0)));
55 sort(K , K+m);
56 for(i=0;i<m;i++)
57 {
58 las = req = x = K[i];
59 ex = 0;
60 for(;;)
61 {
62 y = S.top().st;
63 z = S.top().nd.st;
64 if(z < las)
65 {
66 S.pop();
67 continue;
68 }
69 if(z == las)
70 {
71 ex += S.top().nd.nd;
72 S.pop();
73 continue;
74 }
75
76 l = bs(root[x],root[y],1,n,
77 req+ex+qry(root[x],
78 root[y],1,n,1,las-1) );
79
80 if(l >= (t = S.top().nd.st))
81 {
82 req -= qry(root[x],root[y],1,n,las,t-1) - ex;
83 las = t;
84 ex = S.top().nd.nd;
85 S.pop();
86 continue;
87 }
88 else
89 {
90 req -= qry(root[x],root[y],1,n,las,l)-ex;
91 if(req > 0) return 0;
92 ex = req + qry(root[x],root[y],1,n,l,l);
93 S.push(mp(x,mp(l,ex)));
94 break;
95 }
96 }
97 }
98
99 return 1;
100 }
101
102 vector < int > V[mx];
103 void init(int ss, int *a, int *b)
104 {
CAPITOLUL 6. IOI 2015 765

105 n = ss;
106 int i,j,p=0;
107 for(i=0;i<n;i++) V[ a[i] ].push_back(b[i]);
108 for(i=1;i<=n;i++)
109 {
110 for(j=0;j<V[i].size();j++)
111 p = up(p,1,n,V[i][j]);
112 root[i] = p;
113 }
114 }
115
116 // --------------- begin grader ----------------------
117
118 static char _buffer[1024];
119 static int _currentChar = 0;
120 static int _charsNumber = 0;
121 static FILE *_inputFile, *_outputFile;
122
123 static inline int _read()
124 {
125 if (_charsNumber < 0)
126 {
127 exit(1);
128 }
129
130 if (!_charsNumber || _currentChar == _charsNumber)
131 {
132 _charsNumber = (int)fread(_buffer,
133 sizeof(_buffer[0]),
134 sizeof(_buffer),
135 _inputFile);
136 _currentChar = 0;
137 }
138
139 if (_charsNumber <= 0)
140 {
141 return -1;
142 }
143
144 return _buffer[_currentChar++];
145 }
146
147 static inline int _readInt()
148 {
149 int c, x, s;
150 c = _read();
151 while (c <= 32) c = _read();
152 x = 0;
153 s = 1;
154 if (c == ’-’)
155 {
156 s = -1;
157 c = _read();
158 }
159 while (c > 32)
160 {
161 x *= 10;
162 x += c - ’0’;
163 c = _read();
164 }
165 if (s < 0) x = -x;
166 return x;
167 }
168
169 int main()
170 {
171 _inputFile = fopen("../tests/72", "rb");
172 _outputFile = fopen("teams.out", "w");
173
174 int N;
175 N = _readInt();
176
177 int *A = (int*)malloc(sizeof(int)*(unsigned int)N);
178 int *B = (int*)malloc(sizeof(int)*(unsigned int)N);
179
180 for (int i = 0; i < N; ++i)
CAPITOLUL 6. IOI 2015 766

181 {
182 A[i] = _readInt();
183 B[i] = _readInt();
184 }
185
186 init(N, A, B);
187
188 int Q;
189 Q = _readInt();
190 for (int i = 0; i < Q; ++i)
191 {
192 int M;
193 M = _readInt();
194 int *K = (int*)malloc(sizeof(int)*(unsigned int)M);
195 for (int j = 0; j < M; ++j)
196 {
197 K[j] = _readInt();
198 }
199 fprintf(_outputFile,"%d\n", can(M, K));
200 }
201
202 return 0;
203 }
204 // --------------- end grader ----------------------
205 /*
206 execution time : 2.250 s
207 */

Listing 6.3.3: teams-172439.cpp


1 // https://oj.uz/submission/172439 1552 ms 219300 KB
2
3 #include "teams.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include <bits/stdc++.h>
9
10 #define fi first
11 #define se second
12 #define eb emplace_back
13 #define em emplace
14 #define all(v) v.begin(), v.end()
15
16 using namespace std;
17
18 typedef long long ll;
19 typedef pair <int, int> pii;
20 typedef pair <ll, ll> pll;
21
22 const int MAX = 505050;
23 const int INF = INT_MAX >> 1;
24 const ll LINF = LLONG_MAX >> 1;
25 const ll mod = 1e9+9;
26
27 struct Node
28 {
29 int l, r, val;
30 Node(int l, int r, int val) : l(l), r(r), val(val) {}
31 };
32
33 int n, root[MAX], h[MAX];
34 vector <Node> tree;
35 vector <int> num, numx;
36 pii arr[MAX];
37
38 void expand_tree(int s, int e, int k, int pnd, int cnd)
39 {
40 tree[cnd].val = tree[pnd].val + 1;
41 if(s == e) return;
42 int m = s + e >> 1;
43 if(k <= m)
44 {
45 tree[cnd].r = tree[pnd].r;
CAPITOLUL 6. IOI 2015 767

46 tree[cnd].l = tree.size();
47 tree.eb(0, 0, 0);
48 expand_tree(s, m, k, tree[pnd].l, tree[cnd].l);
49 }
50 else
51 {
52 tree[cnd].l = tree[pnd].l;
53 tree[cnd].r = tree.size();
54 tree.eb(0, 0, 0);
55 expand_tree(m+1, e, k, tree[pnd].r, tree[cnd].r);
56 }
57 }
58
59 void init(int N, int A[], int B[])
60 {
61 n = N;
62 for(int i = 0; i < n; i++)
63 {
64 arr[i] = make_pair(A[i], B[i]);
65 num.eb(B[i]);
66 numx.eb(A[i]);
67 }
68
69 sort(all(num));
70 sort(all(numx));
71 sort(arr, arr+n, [](pii a, pii b)
72 {
73 if(a.se == b.se) return a.fi < b.fi;
74 return a.se < b.se;
75 });
76 for(int i = 0; i < n; i++)
77 {
78 arr[i].se = i + 1;
79 }
80 sort(arr, arr+n);
81 tree.eb(0, 0, 0);
82 for(int i = 0; i < n; i++)
83 {
84 root[i+1] = tree.size();
85 tree.eb(0, 0, 0);
86 expand_tree(1, n, arr[i].se, root[i], root[i+1]);
87 }
88 return;
89 }
90
91 int cal(int s, int e, int l, int r, int pnd, int cnd)
92 {
93 if(s > r || e < l || l > r) return 0;
94 if(s >= l && e <= r) return tree[cnd].val - tree[pnd].val;
95 int m = s + e >> 1;
96 return cal(s, m, l, r, tree[pnd].l, tree[cnd].l)
97 + cal(m+1, e, l, r, tree[pnd].r, tree[cnd].r);
98 }
99
100 int get_r(int s, int e, int cnt, int pnd, int cnd)
101 {
102 if(s == e) return s;
103 int m = s + e >> 1,
104 lcnt = tree[tree[cnd].l].val - tree[tree[pnd].l].val;
105 if(lcnt >= cnt)
106 return get_r(s, m, cnt, tree[pnd].l, tree[cnd].l);
107 else
108 return get_r(m+1, e, cnt - lcnt, tree[pnd].r, tree[cnd].r);
109 }
110
111 int can(int M, int K[])
112 {
113 ll temp = 0;
114 for(int i = 0; i < M; i++) temp += K[i];
115 if(temp > n) return 0;
116
117 sort(K, K+M);
118 stack <pii> stk;
119
120 stk.em(0, n+1);
121 for(int i = 0; i < M; i++)
CAPITOLUL 6. IOI 2015 768

122 {
123 int last = lower_bound(all(num), K[i]) - num.begin() + 1,
124 cnt = K[i];
125 int rx = upper_bound(all(numx), K[i]) - numx.begin();
126 while(!stk.empty() && stk.top().se < last) stk.pop();
127 while(!stk.empty())
128 {
129 int x = stk.top().fi, y = stk.top().se;
130 int temp = cal(1, n, last, y, root[x], root[rx]);
131 if(temp < cnt)
132 {
133 cnt -= temp;
134 last = y + 1;
135 stk.pop();
136 }
137 else
138 {
139 int l = cal(1, n, 1, last - 1, root[x], root[rx]);
140 int ny = get_r(1, n, l + cnt, root[x], root[rx]);
141 stk.em(rx, ny);
142 break;
143 }
144 }
145
146 if(stk.empty()) return 0;
147 }
148 return 1;
149 }
150 // --------------- begin grader ----------------------
151
152 static char _buffer[1024];
153 static int _currentChar = 0;
154 static int _charsNumber = 0;
155 static FILE *_inputFile, *_outputFile;
156
157 static inline int _read()
158 {
159 if (_charsNumber < 0)
160 {
161 exit(1);
162 }
163
164 if (!_charsNumber || _currentChar == _charsNumber)
165 {
166 _charsNumber = (int)fread(_buffer,
167 sizeof(_buffer[0]),
168 sizeof(_buffer),
169 _inputFile);
170 _currentChar = 0;
171 }
172
173 if (_charsNumber <= 0)
174 {
175 return -1;
176 }
177
178 return _buffer[_currentChar++];
179 }
180
181 static inline int _readInt()
182 {
183 int c, x, s;
184 c = _read();
185 while (c <= 32) c = _read();
186 x = 0;
187 s = 1;
188 if (c == ’-’)
189 {
190 s = -1;
191 c = _read();
192 }
193 while (c > 32)
194 {
195 x *= 10;
196 x += c - ’0’;
197 c = _read();
CAPITOLUL 6. IOI 2015 769

198 }
199 if (s < 0) x = -x;
200 return x;
201 }
202
203 int main()
204 {
205 _inputFile = fopen("../tests/72", "rb");
206 _outputFile = fopen("teams.out", "w");
207
208 int N;
209 N = _readInt();
210
211 int *A = (int*)malloc(sizeof(int)*(unsigned int)N);
212 int *B = (int*)malloc(sizeof(int)*(unsigned int)N);
213
214 for (int i = 0; i < N; ++i)
215 {
216 A[i] = _readInt();
217 B[i] = _readInt();
218 }
219
220 init(N, A, B);
221
222 int Q;
223 Q = _readInt();
224 for (int i = 0; i < Q; ++i)
225 {
226 int M;
227 M = _readInt();
228 int *K = (int*)malloc(sizeof(int)*(unsigned int)M);
229 for (int j = 0; j < M; ++j)
230 {
231 K[j] = _readInt();
232 }
233 fprintf(_outputFile,"%d\n", can(M, K));
234 }
235
236 return 0;
237 }
238 // --------------- end grader ----------------------
239 /*
240 execution time : 6.203 s
241 */

Listing 6.3.4: checkerTeams.cpp


1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 const string output_secret = "098d134608c94f7413faac591054ee35";
8
9 int main()
10 {
11 int argc=4;
12
13 char* argv[] =
14 {
15 (char*)"checker",
16 (char*)"../tests/72", // argv[1]=<input-file>
17 (char*)"teams.out", // argv[2]=<output-file>
18 (char*)"../tests/72.a", // argv[3]=<answer-file>
19 };
20 cout<<"argc = "<<argc<<"\n";
21 for(int kk=0;kk<argc;kk++)
22 cout<<argv[kk]<<"\n";
23 cout<<"----------------------\n";
24
25 registerChecker("teams", argc, argv);
26 compareRemainingLines(1); // incepand cu linia nr 1
27 return 0;
28 }
CAPITOLUL 6. IOI 2015 770

6.3.3 *Rezolvare detaliat 

6.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 ecare an inuenµa cre³terea hergheliei. Pentru ecare an i Mansur memoreaz 
un coecient 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  vânduµi numai la sfâr³itul unui an. Pentru ecare an i, Mansur memoreaz  un
întreg pozitiv Y i: preµul unui cal la sfâr³itul anului i. La sfâr³itul ec rui an era posibil s  vinzi
oricâµi cai, ecare 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  i
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 modic ri. Fiecare
modicare va schimba e una dintre valorile X i, e una dintre valorile Y i. Dup  ecare
modicare el te întreab  dinnou care e suma cea mai mare pe care o poate obµine din vânzarea
cailor. Modic rile lui Manur sunt cumulative: ecare r spuns trebuie s  µin  cont de toate
modic rile precedente. Reµineµi c  oricare dintre valorile X i sau Y i ar putea  modicat 
de mai multe ori.
R spunsul lui Mansur poate  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. Protul total va  2 Y 1 8.

S  presupunem acum c  exist  M 1 modic ri: Schimb  valoarea lui Y 1 în 2.


Dup  modicare 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 6. IOI 2015 771

ˆ Dup  anul 2 el are 1 X 2 3 cai.


ˆ El poate acum s  vând  cei trei cai pentru 3 Y 2 3. Protul total va  3  3 6.

Cerinµ 
Se dau N , X , Y , ³i lista de modic ri. înainte de prima modicare ³i dup  ecare modicare,
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  coecientul 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 specic  valorile iniµiale date de Mansur (înainte de orice
modicare).
 Dup  ce apelul init se încheie, ³irurile X ³i Y r mân valabile, ³i poµi modica conµi-
nutul 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  modicare, 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  modicare, modulo 10  7.
9
Se asigur  c  atât valorile iniµiale cât ³i cele modicate 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  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 ³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 6. IOI 2015 772

Grader-ul de pe calculatorul t u a³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

6.4.1 Indicaµii de rezolvare

To solve 1-st subtask we need just calculate our answer using dynamic programming dpij  -
what is maximal prot 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.
Prot of i-th day: x1 ˜ x2 ˜ ... ˜ xi ˜ yi
Prot 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 protably sell horses in i-th day

if it's < then it's protably 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
prot.
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 rst 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.

6.4.2 Coduri surs 


CAPITOLUL 6. IOI 2015 773

Listing 6.4.1: horses-91995.cpp


1 // https://oj.uz/submission/91995 214 ms 58508 KB
2
3 #include "horses.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 const int maxn = 5e5+10;
12 const int mod = 1e9+7;
13
14 typedef long long ll;
15
16 ll x[maxn], y[maxn];
17 int n;
18
19 struct node
20 {
21 double maior, soma;
22 ll maiorp, prod;
23 } tree[4*maxn];
24
25 void join(int node)
26 {
27 tree[node].maior = max(tree[2*node].maior,
28 tree[2*node].soma+tree[2*node+1].maior);
29 tree[node].soma = tree[2*node].soma+tree[2*node+1].soma;
30
31 if (tree[2*node].maior > tree[2*node].soma+tree[2*node+1].maior)
32 tree[node].maiorp = tree[2*node].maiorp;
33 else
34 tree[node].maiorp = (tree[2*node].prod*tree[2*node+1].maiorp)%mod;
35
36 tree[node].prod = (tree[2*node].prod*tree[2*node+1].prod)%mod;
37 }
38
39 void build(int node, int l, int r)
40 {
41 if (l == r)
42 {
43 tree[node].maior = log2(x[l])+log2(y[l]);
44 tree[node].soma = log2(x[l]);
45
46 tree[node].maiorp = (x[l]*y[l])%mod;
47 tree[node].prod = x[l]%mod;
48
49 return;
50 }
51
52 int mid = (l+r)>>1;
53
54 build(2*node, l, mid); build(2*node+1, mid+1, r);
55 join(node);
56 }
57
58 void upd(int node, int l, int r, int pos)
59 {
60 if (l == r)
61 {
62 tree[node].maior = log2(x[l])+log2(y[l]);
63 tree[node].soma = log2(x[l]);
64
65 tree[node].maiorp = (x[l]*y[l])%mod;
66 tree[node].prod = x[l]%mod;
67
68 return;
69 }
70
71 int mid = (l+r)>>1;
72
73 if (pos <= mid) upd(2*node, l, mid, pos);
74 else upd(2*node+1, mid+1, r, pos);
CAPITOLUL 6. IOI 2015 774

75
76 join(node);
77 }
78
79 int init(int N, int X[], int Y[])
80 {
81 n = N;
82 for (int i = 0; i < n; i++)
83 x[i] = (ll)X[i], y[i] = (ll)Y[i];
84 build(1, 0, n-1);
85
86 return (int)tree[1].maiorp;
87 }
88
89 int updateX(int pos, int val)
90 {
91 x[pos] = (ll)val;
92 upd(1, 0, n-1, pos);
93
94 return (int)tree[1].maiorp;
95 }
96
97 int updateY(int pos, int val)
98 {
99 y[pos] = (ll)val;
100 upd(1, 0, n-1, pos);
101
102 return (int)tree[1].maiorp;
103 }
104
105 // ------------------ begin grader ----------------
106
107 static char _buffer[1024];
108 static int _currentChar = 0;
109 static int _charsNumber = 0;
110 static FILE *_inputFile, *_outputFile;
111
112 static inline int _read()
113 {
114 if (_charsNumber < 0)
115 {
116 exit(1);
117 }
118 if (!_charsNumber || _currentChar == _charsNumber)
119 {
120 _charsNumber = (int)fread(_buffer,
121 sizeof(_buffer[0]),
122 sizeof(_buffer),
123 _inputFile);
124 _currentChar = 0;
125 }
126 if (_charsNumber <= 0)
127 {
128 return -1;
129 }
130 return _buffer[_currentChar++];
131 }
132
133 static inline int _readInt()
134 {
135 int c, x, s;
136 c = _read();
137 while (c <= 32) c = _read();
138 x = 0;
139 s = 1;
140 if (c == ’-’)
141 {
142 s = -1;
143 c = _read();
144 }
145 while (c > 32)
146 {
147 x *= 10;
148 x += c - ’0’;
149 c = _read();
150 }
CAPITOLUL 6. IOI 2015 775

151 if (s < 0) x = -x;


152 return x;
153 }
154
155 int main()
156 {
157 _inputFile = fopen("../tests/59", "rb");
158 _outputFile = fopen("horses.out", "w");
159
160 int N; N = _readInt();
161
162 int *X = (int*)malloc(sizeof(int)*(unsigned int)N);
163 int *Y = (int*)malloc(sizeof(int)*(unsigned int)N);
164
165 for (int i = 0; i < N; i++)
166 {
167 X[i] = _readInt();
168 }
169
170 for (int i = 0; i < N; i++)
171 {
172 Y[i] = _readInt();
173 }
174
175 fprintf(_outputFile,"%d\n",init(N,X,Y));
176
177 int M; M = _readInt();
178
179 for (int i = 0; i < M; i++)
180 {
181 int type; type = _readInt();
182 int pos; pos = _readInt();
183 int val; val = _readInt();
184
185 if (type == 1)
186 {
187 fprintf(_outputFile,"%d\n",updateX(pos,val));
188 }
189 else
190 if (type == 2)
191 {
192 fprintf(_outputFile,"%d\n",updateY(pos,val));
193 }
194 }
195
196 return 0;
197 }
198
199 // ------------------ end grader ----------------
200 /*
201 execution time : 0.344 s
202 */

Listing 6.4.2: horses-102703.cpp


1 // https://oj.uz/submission/102703 216 ms 45508 KB
2
3 #include "horses.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 const int md = (int) 1e9 + 7;
12 const long long inf = 1ll << 30;
13
14 inline int mul(int x, int y)
15 {
16 return (int) ((long long) x * y % md);
17 }
18
19 class node
20 {
CAPITOLUL 6. IOI 2015 776

21 public:
22 int all;
23 int left;
24 int right;
25 int sell;
26 int prod;
27 int ans;
28 };
29
30 vector<node> tree;
31 vector<int> a, b;
32 int n;
33
34 void pull(int x, int z)
35 {
36 tree[x].all = min((long long) tree[x + 1].all * tree[z].all, inf);
37 tree[x].prod = mul(tree[x + 1].prod, tree[z].prod);
38 if (tree[x + 1].sell > (long long) tree[x + 1].right * tree[z].left)
39 {
40 tree[x].ans = tree[x + 1].ans;
41 tree[x].sell = tree[x + 1].sell;
42 tree[x].left = tree[x + 1].left;
43 tree[x].right = min((long long) tree[x + 1].right * tree[z].all, inf);
44 }
45 else
46 {
47 tree[x].ans = mul(tree[z].ans, tree[x + 1].prod);
48 tree[x].sell = tree[z].sell;
49 tree[x].left = min((long long) tree[x + 1].all * tree[z].left, inf);
50 tree[x].right = tree[z].right;
51 }
52 }
53
54 void build(int x, int l, int r)
55 {
56 if (l == r)
57 {
58 tree[x].all = a[l];
59 tree[x].prod = a[l];
60 tree[x].ans = mul(a[l], b[l]);
61 tree[x].sell = b[l];
62 tree[x].left = min((long long) a[l] * b[l], inf);
63 tree[x].right = 1;
64 }
65 else
66 {
67 int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
68 build(x + 1, l, y);
69 build(z, y + 1, r);
70 pull(x, z);
71 }
72 }
73
74 void modify(int x, int l, int r, int p)
75 {
76 if (l == r)
77 {
78 tree[x].all = a[l];
79 tree[x].prod = a[l];
80 tree[x].ans = mul(a[l], b[l]);
81 tree[x].sell = b[l];
82 tree[x].left = min((long long) a[l] * b[l], inf);
83 tree[x].right = 1;
84 }
85 else
86 {
87 int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
88 if (p <= y)
89 {
90 modify(x + 1, l, y, p);
91 }
92 else
93 {
94 modify(z, y + 1, r, p);
95 }
96 pull(x, z);
CAPITOLUL 6. IOI 2015 777

97 }
98 }
99
100 int init(int N, int X[], int Y[])
101 {
102 n = N;
103 for (int i = 0; i < n; ++i)
104 {
105 a.push_back(X[i]);
106 b.push_back(Y[i]);
107 }
108 tree.resize(n * 2 - 1);
109 build(0, 0, n - 1);
110 return tree[0].ans;
111 }
112
113 int updateX(int pos, int val)
114 {
115 a[pos] = val;
116 modify(0, 0, n - 1, pos);
117 return tree[0].ans;
118 }
119
120 int updateY(int pos, int val)
121 {
122 b[pos] = val;
123 modify(0, 0, n - 1, pos);
124 return tree[0].ans;
125 }
126
127 // ------------------ begin grader ----------------
128
129 static char _buffer[1024];
130 static int _currentChar = 0;
131 static int _charsNumber = 0;
132 static FILE *_inputFile, *_outputFile;
133
134 static inline int _read()
135 {
136 if (_charsNumber < 0)
137 {
138 exit(1);
139 }
140 if (!_charsNumber || _currentChar == _charsNumber)
141 {
142 _charsNumber = (int)fread(_buffer,
143 sizeof(_buffer[0]),
144 sizeof(_buffer),
145 _inputFile);
146 _currentChar = 0;
147 }
148 if (_charsNumber <= 0)
149 {
150 return -1;
151 }
152 return _buffer[_currentChar++];
153 }
154
155 static inline int _readInt()
156 {
157 int c, x, s;
158 c = _read();
159 while (c <= 32) c = _read();
160 x = 0;
161 s = 1;
162 if (c == ’-’)
163 {
164 s = -1;
165 c = _read();
166 }
167 while (c > 32)
168 {
169 x *= 10;
170 x += c - ’0’;
171 c = _read();
172 }
CAPITOLUL 6. IOI 2015 778

173 if (s < 0) x = -x;


174 return x;
175 }
176
177 int main()
178 {
179 _inputFile = fopen("../tests/59", "rb");
180 _outputFile = fopen("horses.out", "w");
181
182 int N; N = _readInt();
183
184 int *X = (int*)malloc(sizeof(int)*(unsigned int)N);
185 int *Y = (int*)malloc(sizeof(int)*(unsigned int)N);
186
187 for (int i = 0; i < N; i++)
188 {
189 X[i] = _readInt();
190 }
191
192 for (int i = 0; i < N; i++)
193 {
194 Y[i] = _readInt();
195 }
196
197 fprintf(_outputFile,"%d\n",init(N,X,Y));
198
199 int M; M = _readInt();
200
201 for (int i = 0; i < M; i++)
202 {
203 int type; type = _readInt();
204 int pos; pos = _readInt();
205 int val; val = _readInt();
206
207 if (type == 1)
208 {
209 fprintf(_outputFile,"%d\n",updateX(pos,val));
210 }
211 else
212 if (type == 2)
213 {
214 fprintf(_outputFile,"%d\n",updateY(pos,val));
215 }
216 }
217
218 return 0;
219 }
220
221 // ------------------ end grader ----------------
222 /*
223 execution time : 1.225 s
224 */

Listing 6.4.3: horses-202434.cpp


1 // https://oj.uz/submission/202434 198 ms 66680 KB
2
3 #include "horses.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<iostream>
8
9 #define MOD (1000000007LL)
10 #define LL long long
11 #define INF (1LL<<30)
12 #define MAXN 500005
13
14 using namespace std;
15
16 LL n, x[MAXN], y[MAXN];
17
18 class Segtree
19 {
20 LL ST[MAXN*4][5]; // mod product, actual (or inf), opt product,
CAPITOLUL 6. IOI 2015 779

21 // opt, prod of stuff to right of opt


22 public:
23 void pushup(int cur)
24 {
25 ST[cur][0]=(ST[2*cur][0]*ST[2*cur+1][0])%MOD;
26 ST[cur][1]=min(ST[2*cur][1]*ST[2*cur+1][1], INF);
27 if (ST[2*cur][4] == INF ||
28 y[ST[2*cur][3]] <= ST[2*cur+1][2]*ST[2*cur][4])
29 {
30 ST[cur][2] = min(ST[2*cur+1][2]*ST[2*cur][1], INF);
31 ST[cur][3] = ST[2*cur+1][3];
32 ST[cur][4] = ST[2*cur+1][4];
33 }
34 else
35 {
36 ST[cur][2] = ST[2*cur][2];
37 ST[cur][3] = ST[2*cur][3];
38 ST[cur][4] = min(ST[2*cur][4]*ST[2*cur+1][1], INF);
39 }
40 }
41
42 void build(int l, int r, int cur)
43 {
44 if (l == r)
45 {
46 ST[cur][0] = x[l];
47 ST[cur][1] = x[l];
48 ST[cur][2] = min(x[l]*y[l], INF);
49 ST[cur][3] = l;
50 ST[cur][4] = 1;
51 }
52 else
53 {
54 int mid=(l+r)>>1;
55 build(l, mid, 2*cur);
56 build(mid+1, r, 2*cur+1);
57 pushup(cur);
58 }
59 }
60
61 void upd(int ind, int l, int r, int cur)
62 {
63 if (l > ind || r < ind) {return;}
64 if (l == r)
65 {
66 ST[cur][0] = x[l];
67 ST[cur][1] = x[l];
68 ST[cur][2] = min(x[l]*y[l], INF);
69 ST[cur][3] = l;
70 }
71 else
72 {
73 int mid=(l+r)>>1;
74 upd(ind, l, mid, 2*cur);
75 upd(ind, mid+1, r, 2*cur+1);
76 pushup(cur);
77 }
78 }
79
80 LL ask(int ind, int l, int r, int cur)
81 { //ask for prefix mod product
82 if (ind < l) {return(1);}
83 if (r <= ind) {return(ST[cur][0]);}
84 else
85 {
86 int mid=(l+r)>>1;
87 return((ask(ind, l, mid, 2*cur)*
88 ask(ind, mid+1, r, 2*cur+1))%MOD);
89 }
90 }
91
92 LL opt() {return(ST[1][3]);}
93 } ST;
94
95 int init(int N, int X[], int Y[])
96 {
CAPITOLUL 6. IOI 2015 780

97 n=N;
98 for (int i=0; i<N; i++)
99 {
100 x[i] = X[i];
101 y[i] = Y[i];
102 }
103 ST.build(0, N-1, 1);
104 LL p = ST.opt(), prod = ST.ask(p, 0, N-1, 1);
105 LL ans = (prod * y[p])%MOD;
106 return( (int) ans);
107 }
108
109 int updateX(int pos, int val)
110 {
111 x[pos]=val;
112 ST.upd(pos, 0, n-1, 1);
113 LL p = ST.opt(), prod = ST.ask(p, 0, n-1, 1);
114 LL ans = (prod * y[p])%MOD;
115 return( (int) ans);
116 }
117
118 int updateY(int pos, int val)
119 {
120 y[pos]=val;
121 ST.upd(pos, 0, n-1, 1);
122 LL p = ST.opt(), prod = ST.ask(p, 0, n-1, 1);
123 LL ans = (prod * y[p])%MOD;
124 return( (int) ans);
125 }
126
127 // ------------------ begin grader ----------------
128
129 static char _buffer[1024];
130 static int _currentChar = 0;
131 static int _charsNumber = 0;
132 static FILE *_inputFile, *_outputFile;
133
134 static inline int _read()
135 {
136 if (_charsNumber < 0)
137 {
138 exit(1);
139 }
140 if (!_charsNumber || _currentChar == _charsNumber)
141 {
142 _charsNumber = (int)fread(_buffer,
143 sizeof(_buffer[0]),
144 sizeof(_buffer),
145 _inputFile);
146 _currentChar = 0;
147 }
148 if (_charsNumber <= 0)
149 {
150 return -1;
151 }
152 return _buffer[_currentChar++];
153 }
154
155 static inline int _readInt()
156 {
157 int c, x, s;
158 c = _read();
159 while (c <= 32) c = _read();
160 x = 0;
161 s = 1;
162 if (c == ’-’)
163 {
164 s = -1;
165 c = _read();
166 }
167 while (c > 32)
168 {
169 x *= 10;
170 x += c - ’0’;
171 c = _read();
172 }
CAPITOLUL 6. IOI 2015 781

173 if (s < 0) x = -x;


174 return x;
175 }
176
177 int main()
178 {
179 _inputFile = fopen("../tests/59", "rb");
180 _outputFile = fopen("horses.out", "w");
181
182 int N; N = _readInt();
183
184 int *X = (int*)malloc(sizeof(int)*(unsigned int)N);
185 int *Y = (int*)malloc(sizeof(int)*(unsigned int)N);
186
187 for (int i = 0; i < N; i++)
188 {
189 X[i] = _readInt();
190 }
191
192 for (int i = 0; i < N; i++)
193 {
194 Y[i] = _readInt();
195 }
196
197 fprintf(_outputFile,"%d\n",init(N,X,Y));
198
199 int M; M = _readInt();
200
201 for (int i = 0; i < M; i++)
202 {
203 int type; type = _readInt();
204 int pos; pos = _readInt();
205 int val; val = _readInt();
206
207 if (type == 1)
208 {
209 fprintf(_outputFile,"%d\n",updateX(pos,val));
210 }
211 else
212 if (type == 2)
213 {
214 fprintf(_outputFile,"%d\n",updateY(pos,val));
215 }
216 }
217
218 return 0;
219 }
220
221 // ------------------ end grader ----------------
222 /*
223 execution time : 1.109 s
224 */

Listing 6.4.4: checkerHorses.cpp


1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 const string output_secret = "098d134608c94f7413faac591054ee35";
8
9 int main()
10 {
11 int argc=4;
12
13 char* argv[] =
14 {
15 (char*)"checker",
16 (char*)"../tests/59", // argv[1]=<input-file>
17 (char*)"horses.out", // argv[2]=<output-file>
18 (char*)"../tests/59.a", // argv[3]=<answer-file>
19 };
20 cout<<"argc = "<<argc<<"\n";
CAPITOLUL 6. IOI 2015 782

21 for(int kk=0;kk<argc;kk++)
22 cout<<argv[kk]<<"\n";
23 cout<<"----------------------\n";
24
25 registerChecker("horses", argc, argv);
26 compareRemainingLines(1); // incepand cu linia nr 1
27 return 0;
28 }

6.4.3 *Rezolvare detaliat 

6.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 modica ³irul într-o serie de runde. în ecare 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  e 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 ecare 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 ec 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 modic  ³irul.
CAPITOLUL 6. IOI 2015 783

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 modic  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 interschim-
b 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  e cea mai scurt  posibil .
Trebuie s  implementezi funcµia findSwapPairs:
ˆ findSwapPairs(N, S, M, X, Y, P, Q) - Aceast  funcµie va  apelat  de grader
exact 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  sec-
venµ  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 ecare i cuprins
între 0 ³i R  1 inclusiv, indicii pe care Aizhan trebuie s  îi aleag  în runda sunt me-
moraµi în P i ³i Qi. Poµi presupune c  tablourile P ³i Q au deja alocate câte M
elemente ecare.
 Aceast  funcµie trebuie s  returneze valoarea lui R (denit  mai sus).

Subprobleme
CAPITOLUL 6. IOI 2015 784

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 ³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 a³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

6.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 Bob’s 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 6. IOI 2015 785

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
dierence objects, the sequence of operations can be separated. Let t be the number of rounds
Alice takes. It can be viewed as Bob rst 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 rst 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 rst nd 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.

6.5.2 Coduri surs 

Listing 6.5.1: sorting-70140.cpp


1 // https://oj.uz/submission/70140 187 ms 16432 KB
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "sorting.h"
7
8 #include<algorithm>
9 #include<vector>
10 #include<iostream>
11
12 using namespace std;
13
14 vector<pair<int,int> > v;
15 int n,ps[300000],s[300000],x[300000],y[300000],rev_ps[300000],r[300000];
CAPITOLUL 6. IOI 2015 786

16 int qayl_qan()
17 {
18 int q=0;
19 for(int i=0;i<n;i++)
20 rev_ps[ps[i]]=i;
21 for(int i=0;i<n;i++)
22 {
23 if(ps[rev_ps[i]]==ps[i])
24 continue;
25 int e=ps[i];
26 swap(ps[rev_ps[i]],ps[i]);
27 q++;
28 swap(rev_ps[i],rev_ps[e]);
29 }
30 return q;
31 }
32
33 int stug(int e)
34 {
35 for(int i=0;i<n;i++)
36 ps[i]=s[i];
37 for(int i=0;i<=e;i++)
38 swap(ps[x[i]],ps[y[i]]);
39 if(qayl_qan()<=e+1)
40 return 1;
41 else
42 return 0;
43 }
44
45 int bin(int l,int r)
46 {
47 if(l==r)
48 return r;
49
50 int mid=(l+r)/2;
51
52 if(stug(mid))
53 bin(l,mid);
54 else
55 bin(mid+1,r);
56 }
57
58 int findSwapPairs(int N, int S[], int M, int X[], int Y[], int P[], int Q[])
59 {
60 if(M>=N)
61 M=N-1;
62 for(int i=0;i<N;i++)
63 s[i]=S[i];
64 for(int i=0;i<M;i++)
65 {
66 x[i]=X[i];
67 y[i]=Y[i];
68 }
69 n=N;
70 for(int i=0;i<n;i++)
71 ps[i]=s[i];
72 if(qayl_qan()==0)
73 return 0;
74 int t=bin(0,M-1);
75 t++;
76 for(int i=0;i<n;i++)
77 ps[i]=s[i];
78 for(int i=0;i<t;i++)
79 swap(ps[x[i]],ps[y[i]]);
80 for(int i=0;i<n;i++)
81 rev_ps[ps[i]]=i;
82 for(int i=0;i<n;i++)
83 {
84 if(ps[rev_ps[i]]==ps[i])
85 continue;
86 int e=ps[i];
87 swap(ps[rev_ps[i]],ps[i]);
88 v.push_back({ps[i],e});
89 swap(rev_ps[i],rev_ps[e]);
90 }
91 for(int i=0;i<n;i++)
CAPITOLUL 6. IOI 2015 787

92 r[s[i]]=i;
93 for(int i=0;i<t;i++)
94 {
95 swap(s[x[i]],s[y[i]]);
96 swap(r[s[x[i]]],r[s[y[i]]]);
97 if(v.size()>i)
98 {
99 P[i]=r[v[i].first];
100 Q[i]=r[v[i].second];
101 }
102 else
103 {
104 P[i]=0;
105 Q[i]=0;
106 }
107 swap(s[P[i]],s[Q[i]]);
108 swap(r[s[P[i]]],r[s[Q[i]]]);
109 }
110 return t;
111 }
112
113 // -------------- begin grader ------------------
114
115 static char _buffer[1024];
116 static int _currentChar = 0;
117 static int _charsNumber = 0;
118
119 static FILE *_inputFile, *_outputFile;
120
121 static inline int _read()
122 {
123 if (_charsNumber < 0)
124 {
125 exit(1);
126 }
127
128 if (!_charsNumber || _currentChar == _charsNumber)
129 {
130 _charsNumber = (int)fread(_buffer,
131 sizeof(_buffer[0]),
132 sizeof(_buffer),
133 _inputFile);
134 _currentChar = 0;
135 }
136
137 if (_charsNumber <= 0)
138 {
139 return -1;
140 }
141 return _buffer[_currentChar++];
142 }
143
144 static inline int _readInt()
145 {
146 int c, x, s;
147 c = _read();
148 while (c <= 32) c = _read();
149 x = 0;
150 s = 1;
151 if (c == ’-’)
152 {
153 s = -1;
154 c = _read();
155 }
156 while (c > 32)
157 {
158 x *= 10;
159 x += c - ’0’;
160 c = _read();
161 }
162 if (s < 0) x = -x;
163 return x;
164 }
165
166 int main()
167 {
CAPITOLUL 6. IOI 2015 788

168 _inputFile = fopen("../tests/subtaskf/25", "rb");


169 _outputFile = fopen("sorting.out", "w");
170
171 int N, M;
172 N = _readInt();
173 int *S = (int*)malloc(sizeof(int) * (unsigned int)N);
174
175 for (int i = 0; i < N; ++i)
176 S[i] = _readInt();
177
178 M = _readInt();
179
180 int *X = (int*)malloc(sizeof(int) * (unsigned int)M),
181 *Y = (int*)malloc(sizeof(int) * (unsigned int)M);
182 for (int i = 0; i < M; ++i)
183 {
184 X[i] = _readInt();
185 Y[i] = _readInt();
186 }
187
188 int *P = (int*)malloc(sizeof(int) * (unsigned int)M),
189 *Q = (int*)malloc(sizeof(int) * (unsigned int)M);
190
191 int ans = findSwapPairs(N, S, M, X, Y, P, Q);
192
193 fprintf(_outputFile, "%d\n", ans);
194
195 for (int i = 0; i < ans; ++i)
196 fprintf(_outputFile, "%d %d\n", P[i], Q[i]);
197
198 return 0;
199 }
200
201 // -------------- end grader ------------------
202 /*
203 execution time : 1.750 s
204 */

Listing 6.5.2: sorting-114135.cpp


1 // https://oj.uz/submission/114135 200 ms 23028 KB
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "sorting.h"
7
8 #include <bits/stdc++.h>
9
10 #define pii pair<int, int>
11 #define ff first
12 #define ss second
13
14 using namespace std;
15
16 #define NN 200005
17
18 int val[NN];
19 int loc[NN];
20 int S[NN];
21 int n;
22 vector<pii> sw;
23
24 bool in[NN];
25
26 void dfs(int s, int now)
27 {
28 in[now] = 1;
29 if(now == s) return;
30 dfs(s, S[now]);
31 }
32
33 int findSwapPairs(int N, int qq[], int M,
34 int X[], int Y[], int P[], int Q[])
35 {
CAPITOLUL 6. IOI 2015 789

36 n = N;
37 for(int i = 0; i < N; i++)
38 {
39 S[i] = qq[i];
40 val[i] = S[i];
41 loc[val[i]] = i;
42 }
43 int l = 0, r = N, m;
44 while(l != r)
45 {
46 int cyc = 0;
47 m = (l + r) >> 1;
48 for(int i = 0; i < m; i++)
49 swap(S[X[i]], S[Y[i]]);
50
51 for(int i = 0; i < n; i++)
52 {
53 if(in[i]) continue;
54 cyc++;
55 dfs(i, S[i]);
56 }
57 if(n - cyc <= m) r = m;
58 else l = m + 1;
59 memset(in, 0, sizeof in);
60 for(int i = 0; i < n; i++)
61 S[i] = val[i];
62
63 }
64
65 for(int i = 0; i < r; i++)
66 swap(S[X[i]], S[Y[i]]);
67 for(int i = 0; i < n; i++)
68 {
69 while(S[i] != i)
70 {
71 sw.push_back({S[i], i});
72 swap(S[i], S[S[i]]);
73 }
74 }
75
76 for(int i = 0; i < r; i++)
77 {
78 swap(val[X[i]], val[Y[i]]);
79 swap(loc[val[X[i]]], loc[val[Y[i]]]);
80 if(sw.empty())
81 {
82 P[i] = 0;
83 Q[i] = 0;
84 }
85 else
86 {
87 P[i] = loc[sw.back().ff];
88 Q[i] = loc[sw.back().ss];
89 swap(val[P[i]], val[Q[i]]);
90 swap(loc[val[P[i]]], loc[val[Q[i]]]);
91 sw.pop_back();
92 }
93 }
94
95 return l;
96 }
97
98 // -------------- begin grader ------------------
99
100 static char _buffer[1024];
101 static int _currentChar = 0;
102 static int _charsNumber = 0;
103
104 static FILE *_inputFile, *_outputFile;
105
106 static inline int _read()
107 {
108 if (_charsNumber < 0)
109 {
110 exit(1);
111 }
CAPITOLUL 6. IOI 2015 790

112
113 if (!_charsNumber || _currentChar == _charsNumber)
114 {
115 _charsNumber = (int)fread(_buffer,
116 sizeof(_buffer[0]),
117 sizeof(_buffer),
118 _inputFile);
119 _currentChar = 0;
120 }
121
122 if (_charsNumber <= 0)
123 {
124 return -1;
125 }
126 return _buffer[_currentChar++];
127 }
128
129 static inline int _readInt()
130 {
131 int c, x, s;
132 c = _read();
133 while (c <= 32) c = _read();
134 x = 0;
135 s = 1;
136 if (c == ’-’)
137 {
138 s = -1;
139 c = _read();
140 }
141 while (c > 32)
142 {
143 x *= 10;
144 x += c - ’0’;
145 c = _read();
146 }
147 if (s < 0) x = -x;
148 return x;
149 }
150
151 int main()
152 {
153 _inputFile = fopen("../tests/subtaskf/25", "rb");
154 _outputFile = fopen("sorting.out", "w");
155
156 int N, M;
157 N = _readInt();
158 int *S = (int*)malloc(sizeof(int) * (unsigned int)N);
159
160 for (int i = 0; i < N; ++i)
161 S[i] = _readInt();
162
163 M = _readInt();
164
165 int *X = (int*)malloc(sizeof(int) * (unsigned int)M),
166 *Y = (int*)malloc(sizeof(int) * (unsigned int)M);
167 for (int i = 0; i < M; ++i)
168 {
169 X[i] = _readInt();
170 Y[i] = _readInt();
171 }
172
173 int *P = (int*)malloc(sizeof(int) * (unsigned int)M),
174 *Q = (int*)malloc(sizeof(int) * (unsigned int)M);
175
176 int ans = findSwapPairs(N, S, M, X, Y, P, Q);
177
178 fprintf(_outputFile, "%d\n", ans);
179
180 for (int i = 0; i < ans; ++i)
181 fprintf(_outputFile, "%d %d\n", P[i], Q[i]);
182
183 return 0;
184 }
185
186 // -------------- end grader ------------------
187 /*
CAPITOLUL 6. IOI 2015 791

188 execution time : 1.688 s


189 */

Listing 6.5.3: sorting-129522.cpp


1 // https://oj.uz/submission/129522 186 ms 21988 KB
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include "sorting.h"
6
7 #include <vector>
8 #include <iostream>
9
10 using namespace std;
11
12 int findSwapPairs(int n, int S[], int M, int X[], int Y[], int P[], int Q[])
13 {
14 vector<int> perm(n);
15 bool bc = true;
16 for (int i = 0; i < n; i++)
17 {
18 perm[i] = S[i];
19 if (perm[i] != i) bc = false;
20 }
21 if (bc) return 0;
22 vector<int> b = perm;
23 vector<int> beg = perm;
24 for (int i = 0; i < n; i++)
25 {
26 swap(perm[X[i]], perm[Y[i]]);
27 }
28 vector<int> end = perm;
29 int l = 0;
30 int r = n;
31 while (r - l > 1)
32 {
33 int m = (l + r)/2;
34 vector<int> mv = beg;
35 for (int i = l; i < m; i++)
36 {
37 swap(mv[X[i]], mv[Y[i]]);
38 }
39
40 vector<bool> v(n, false);
41 int ctr = 0;
42 for (int i = 0; i < n; i++)
43 {
44 if (v[i]) continue;
45 v[i] = true;
46 int cc = 0;
47 int cr = i;
48 while (mv[cr] != i)
49 {
50 cc++;
51 cr = mv[cr];
52 v[cr] = true;
53 }
54 ctr += cc;
55 }
56
57 if (ctr <= m)
58 {
59 r = m;
60 end = mv;
61 }
62 else
63 {
64 l = m;
65 beg = mv;
66 }
67 }
68
69 vector<pair<int,int>> swp;
70 perm = b;
CAPITOLUL 6. IOI 2015 792

71
72 for (int i = 0; i < n; i++)
73 {
74 while (end[i] != i)
75 {
76 swp.push_back({end[i], end[end[i]]});
77 swap(end[i], end[end[i]]);
78 }
79 }
80
81 vector<int> revind(n);
82 for (int i = 0; i < n; i++)
83 {
84 revind[perm[i]] = i;
85 }
86
87 for (int i = 0; i < r; i++)
88 {
89 swap(revind[perm[X[i]]], revind[perm[Y[i]]]);
90 swap(perm[X[i]], perm[Y[i]]);
91 if (i < swp.size())
92 {
93 Q[i] = revind[swp[i].first];
94 P[i] = revind[swp[i].second];
95 swap(revind[swp[i].first], revind[swp[i].second]);
96 swap(perm[Q[i]], perm[P[i]]);
97 }
98 else
99 {
100 Q[i] = 0;
101 P[i] = 0;
102 }
103 }
104
105 return r;
106 }
107
108 // -------------- begin grader ------------------
109
110 static char _buffer[1024];
111 static int _currentChar = 0;
112 static int _charsNumber = 0;
113
114 static FILE *_inputFile, *_outputFile;
115
116 static inline int _read()
117 {
118 if (_charsNumber < 0)
119 {
120 exit(1);
121 }
122
123 if (!_charsNumber || _currentChar == _charsNumber)
124 {
125 _charsNumber = (int)fread(_buffer,
126 sizeof(_buffer[0]),
127 sizeof(_buffer),
128 _inputFile);
129 _currentChar = 0;
130 }
131
132 if (_charsNumber <= 0)
133 {
134 return -1;
135 }
136 return _buffer[_currentChar++];
137 }
138
139 static inline int _readInt()
140 {
141 int c, x, s;
142 c = _read();
143 while (c <= 32) c = _read();
144 x = 0;
145 s = 1;
146 if (c == ’-’)
CAPITOLUL 6. IOI 2015 793

147 {
148 s = -1;
149 c = _read();
150 }
151 while (c > 32)
152 {
153 x *= 10;
154 x += c - ’0’;
155 c = _read();
156 }
157 if (s < 0) x = -x;
158 return x;
159 }
160
161 int main()
162 {
163 _inputFile = fopen("../tests/subtaskf/25", "rb");
164 _outputFile = fopen("sorting.out", "w");
165
166 int N, M;
167 N = _readInt();
168 int *S = (int*)malloc(sizeof(int) * (unsigned int)N);
169
170 for (int i = 0; i < N; ++i)
171 S[i] = _readInt();
172
173 M = _readInt();
174
175 int *X = (int*)malloc(sizeof(int) * (unsigned int)M),
176 *Y = (int*)malloc(sizeof(int) * (unsigned int)M);
177 for (int i = 0; i < M; ++i)
178 {
179 X[i] = _readInt();
180 Y[i] = _readInt();
181 }
182
183 int *P = (int*)malloc(sizeof(int) * (unsigned int)M),
184 *Q = (int*)malloc(sizeof(int) * (unsigned int)M);
185
186 int ans = findSwapPairs(N, S, M, X, Y, P, Q);
187
188 fprintf(_outputFile, "%d\n", ans);
189
190 for (int i = 0; i < ans; ++i)
191 fprintf(_outputFile, "%d %d\n", P[i], Q[i]);
192
193 return 0;
194 }
195
196 // -------------- end grader ------------------
197 /*
198 execution time : 1.828 s
199 */

Listing 6.5.4: sorting-155927.cpp


1 // https://oj.uz/submission/155927 508 ms 22904 KB
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include "sorting.h"
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 int n,m,*x,*y,*s,*p,*q;
12 int a[500010],b[500010],c[500010],d[500010];
13 bool f(int z)
14 {
15 int i,j;
16 for(i=0;i<n;i++)
17 {
18 a[i]=b[i]=s[i];
19 }
CAPITOLUL 6. IOI 2015 794

20 for(i=0;i<z;i++)
21 {
22 swap(a[x[i]],a[y[i]]);
23 }
24 for(i=0;i<n;i++)
25 {
26 c[a[i]]=i;
27 d[b[i]]=i;
28 }
29 j=0;
30 for(i=0;i<z;i++)
31 {
32 swap(d[b[x[i]]],d[b[y[i]]]);
33 swap(b[x[i]],b[y[i]]);
34 while(j<n&&a[j]==j)j++;
35 if(j==n)p[i]=q[i]=0;
36 else
37 {
38 p[i]=d[j],q[i]=d[a[j]];
39 swap(b[d[j]],b[d[a[j]]]);
40 swap(d[j],d[a[j]]);
41 c[a[j]]=c[j];
42 swap(a[j],a[c[j]]);
43 c[j]=j;
44 }
45 }
46
47 for(i=0;i+1<n;i++) if(a[i]>a[i+1]) return false;
48 return true;
49 }
50
51 int findSwapPairs(int N, int S[], int M, int X[], int Y[], int P[], int Q[])
52 {
53 n=N;s=S;m=M;x=X;y=Y;p=P;q=Q;
54 int lo=0,hi=M;
55 while(lo<hi)
56 {
57 int mid=(lo+hi)/2;
58 if(f(mid))hi=mid;
59 else lo=mid+1;
60 }
61 f(lo);
62 return lo;
63 }
64
65 // -------------- begin grader ------------------
66
67 static char _buffer[1024];
68 static int _currentChar = 0;
69 static int _charsNumber = 0;
70
71 static FILE *_inputFile, *_outputFile;
72
73 static inline int _read()
74 {
75 if (_charsNumber < 0)
76 {
77 exit(1);
78 }
79
80 if (!_charsNumber || _currentChar == _charsNumber)
81 {
82 _charsNumber = (int)fread(_buffer,
83 sizeof(_buffer[0]),
84 sizeof(_buffer),
85 _inputFile);
86 _currentChar = 0;
87 }
88
89 if (_charsNumber <= 0)
90 {
91 return -1;
92 }
93 return _buffer[_currentChar++];
94 }
95
CAPITOLUL 6. IOI 2015 795

96 static inline int _readInt()


97 {
98 int c, x, s;
99 c = _read();
100 while (c <= 32) c = _read();
101 x = 0;
102 s = 1;
103 if (c == ’-’)
104 {
105 s = -1;
106 c = _read();
107 }
108 while (c > 32)
109 {
110 x *= 10;
111 x += c - ’0’;
112 c = _read();
113 }
114 if (s < 0) x = -x;
115 return x;
116 }
117
118 int main()
119 {
120 _inputFile = fopen("../tests/subtaskf/25", "rb");
121 _outputFile = fopen("sorting.out", "w");
122
123 int N, M;
124 N = _readInt();
125 int *S = (int*)malloc(sizeof(int) * (unsigned int)N);
126
127 for (int i = 0; i < N; ++i)
128 S[i] = _readInt();
129
130 M = _readInt();
131
132 int *X = (int*)malloc(sizeof(int) * (unsigned int)M),
133 *Y = (int*)malloc(sizeof(int) * (unsigned int)M);
134 for (int i = 0; i < M; ++i)
135 {
136 X[i] = _readInt();
137 Y[i] = _readInt();
138 }
139
140 int *P = (int*)malloc(sizeof(int) * (unsigned int)M),
141 *Q = (int*)malloc(sizeof(int) * (unsigned int)M);
142
143 int ans = findSwapPairs(N, S, M, X, Y, P, Q);
144
145 fprintf(_outputFile, "%d\n", ans);
146
147 for (int i = 0; i < ans; ++i)
148 fprintf(_outputFile, "%d %d\n", P[i], Q[i]);
149
150 return 0;
151 }
152
153 // -------------- end grader ------------------
154 /*
155 execution time : 2.391 s
156 */

Listing 6.5.5: sorting-225877.cpp


1 // https://oj.uz/submission/225877 442 ms 11492 KB
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include "sorting.h"
6
7 #include<bits/stdc++.h>
8
9 using namespace std;
10
11 #define REP(i, n) for(int i = 0; i < n; i++)
CAPITOLUL 6. IOI 2015 796

12
13 #include "sorting.h"
14
15 int findSwapPairs(int N, int S[], int M, int X[], int Y[], int P[], int Q[])
16 {
17 auto check = [&](int R)
18 {
19 vector<int> s(N), ws(N), wf(N);
20 REP(i, N) s[i] = S[i];
21 auto f = s;
22 REP(i, R) swap(f[X[i]], f[Y[i]]);
23 REP(i, N)
24 {
25 ws[S[i]] = i;
26 wf[f[i]] = i;
27 }
28
29 int j = 0;
30 REP(i, R)
31 {
32 int &p = s[X[i]], &q = s[Y[i]];
33 swap(p, q);
34 swap(ws[p], ws[q]);
35
36 while(j < N && j == f[j]) j++;
37 if(j < N)
38 {
39 int &a = ws[j], &b = ws[f[j]], &x = wf[j], &y = wf[f[j]];
40 P[i] = a, Q[i] = b;
41 swap(a, b), swap(s[a], s[b]);
42 swap(x, y), swap(f[x], f[y]);
43 }
44 else P[i] = Q[i] = 0;
45 }
46
47 REP(i, N) if(i != s[i]) return false;
48 return true;
49 };
50
51 int l = 0, r = M;
52 while(l < r)
53 {
54 int m = (l + r) / 2;
55 if(check(m))
56 r = m;
57 else
58 l = m + 1;
59 }
60
61 check(l);
62 return l;
63 }
64
65 // -------------- begin grader ------------------
66
67 static char _buffer[1024];
68 static int _currentChar = 0;
69 static int _charsNumber = 0;
70
71 static FILE *_inputFile, *_outputFile;
72
73 static inline int _read()
74 {
75 if (_charsNumber < 0)
76 {
77 exit(1);
78 }
79
80 if (!_charsNumber || _currentChar == _charsNumber)
81 {
82 _charsNumber = (int)fread(_buffer,
83 sizeof(_buffer[0]),
84 sizeof(_buffer),
85 _inputFile);
86 _currentChar = 0;
87 }
CAPITOLUL 6. IOI 2015 797

88
89 if (_charsNumber <= 0)
90 {
91 return -1;
92 }
93 return _buffer[_currentChar++];
94 }
95
96 static inline int _readInt()
97 {
98 int c, x, s;
99 c = _read();
100 while (c <= 32) c = _read();
101 x = 0;
102 s = 1;
103 if (c == ’-’)
104 {
105 s = -1;
106 c = _read();
107 }
108 while (c > 32)
109 {
110 x *= 10;
111 x += c - ’0’;
112 c = _read();
113 }
114 if (s < 0) x = -x;
115 return x;
116 }
117
118 int main()
119 {
120 _inputFile = fopen("../tests/subtaskf/25", "rb");
121 _outputFile = fopen("sorting.out", "w");
122
123 int N, M;
124 N = _readInt();
125 int *S = (int*)malloc(sizeof(int) * (unsigned int)N);
126
127 for (int i = 0; i < N; ++i)
128 S[i] = _readInt();
129
130 M = _readInt();
131
132 int *X = (int*)malloc(sizeof(int) * (unsigned int)M),
133 *Y = (int*)malloc(sizeof(int) * (unsigned int)M);
134 for (int i = 0; i < M; ++i)
135 {
136 X[i] = _readInt();
137 Y[i] = _readInt();
138 }
139
140 int *P = (int*)malloc(sizeof(int) * (unsigned int)M),
141 *Q = (int*)malloc(sizeof(int) * (unsigned int)M);
142
143 int ans = findSwapPairs(N, S, M, X, Y, P, Q);
144
145 fprintf(_outputFile, "%d\n", ans);
146
147 for (int i = 0; i < ans; ++i)
148 fprintf(_outputFile, "%d %d\n", P[i], Q[i]);
149
150 return 0;
151 }
152
153 // -------------- end grader ------------------
154 /*
155 execution time : 3.016 s
156 */

Listing 6.5.6: sorting-233228.cpp


1 // https://oj.uz/submission/233228 242 ms 34048 KB
2 #include <stdlib.h>
3 #include <stdio.h>
CAPITOLUL 6. IOI 2015 798

4 #include <string.h>
5
6 #include "sorting.h"
7
8 #include <bits/stdc++.h>
9
10 using namespace std;
11
12 mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
13
14 typedef long long ll;
15 typedef long double ld;
16
17 #define INF 2001001001
18 #define MOD 1000000007
19
20 int N,S[200001];
21 int M,X[600001],Y[600001];
22 int arr[200001];
23 bool vis[200001];
24 int to[200001];
25
26 deque<pair<int,int>>q;
27
28 int pos[200001];
29
30 void dfs(int node)
31 {
32 vis[node]=true;
33 if (!vis[arr[node]]){
34 q.push_back({node,arr[node]});
35 dfs(arr[node]);
36 }
37 }
38
39 bool check(int mid)
40 {
41 q.clear();
42 for (int i=0;i<N;i++)
43 {
44 arr[i]=S[i];
45 vis[i]=false;
46 }
47 for (int i=1;i<=mid;i++)
48 swap(arr[X[i]],arr[Y[i]]);
49 int comps=0;
50 for (int i=0;i<N;i++)
51 {
52 pos[arr[i]]=i;
53 if (!vis[arr[i]])
54 {
55 comps++;
56 q.push_back({i,arr[i]});
57 dfs(arr[i]);
58 q.pop_back();
59 }
60 }
61 return N-comps<=mid;
62 }
63
64 int findSwapPairs(int n,int s[],int m, int x[], int y[], int P[], int Q[])
65 {
66 N=n;
67 for (int i=0;i<N;i++)
68 S[i]=s[i];
69 M=m;
70 for (int i=1;i<=M;i++)
71 {
72 X[i]=x[i-1];
73 Y[i]=y[i-1];
74 }
75 int low=-1,high=M+1;
76 while (low+1<high)
77 {
78 int mid=(low+high)/2;
79 if (check(mid))
CAPITOLUL 6. IOI 2015 799

80 high=mid;
81 else
82 low=mid;
83 }
84
85 check(high);
86 for (int i=high;i>=1;i--)
87 {
88 if (!q.empty()){
89 P[i-1]=pos[q[0].first];
90 Q[i-1]=pos[q[0].second];
91 swap(pos[arr[X[i]]],pos[arr[Y[i]]]);
92 swap(arr[X[i]],arr[Y[i]]);
93 q.pop_front();
94 }
95 }
96
97 for (int i=1;i<=high;i++)
98 {
99 swap(S[X[i]],S[Y[i]]);
100 swap(S[P[i-1]],S[Q[i-1]]);
101 }
102 for (int i=0;i<N;i++)
103 if (S[i]!=i)
104 assert(0);
105 return high;
106 }
107
108 // -------------- begin grader ------------------
109
110 static char _buffer[1024];
111 static int _currentChar = 0;
112 static int _charsNumber = 0;
113
114 static FILE *_inputFile, *_outputFile;
115
116 static inline int _read()
117 {
118 if (_charsNumber < 0)
119 {
120 exit(1);
121 }
122
123 if (!_charsNumber || _currentChar == _charsNumber)
124 {
125 _charsNumber = (int)fread(_buffer,
126 sizeof(_buffer[0]),
127 sizeof(_buffer),
128 _inputFile);
129 _currentChar = 0;
130 }
131
132 if (_charsNumber <= 0)
133 {
134 return -1;
135 }
136 return _buffer[_currentChar++];
137 }
138
139 static inline int _readInt()
140 {
141 int c, x, s;
142 c = _read();
143 while (c <= 32) c = _read();
144 x = 0;
145 s = 1;
146 if (c == ’-’)
147 {
148 s = -1;
149 c = _read();
150 }
151 while (c > 32)
152 {
153 x *= 10;
154 x += c - ’0’;
155 c = _read();
CAPITOLUL 6. IOI 2015 800

156 }
157 if (s < 0) x = -x;
158 return x;
159 }
160
161 int main()
162 {
163 _inputFile = fopen("../tests/subtaskf/25", "rb");
164 _outputFile = fopen("sorting.out", "w");
165
166 int N, M;
167 N = _readInt();
168 int *S = (int*)malloc(sizeof(int) * (unsigned int)N);
169
170 for (int i = 0; i < N; ++i)
171 S[i] = _readInt();
172
173 M = _readInt();
174
175 int *X = (int*)malloc(sizeof(int) * (unsigned int)M),
176 *Y = (int*)malloc(sizeof(int) * (unsigned int)M);
177 for (int i = 0; i < M; ++i)
178 {
179 X[i] = _readInt();
180 Y[i] = _readInt();
181 }
182
183 int *P = (int*)malloc(sizeof(int) * (unsigned int)M),
184 *Q = (int*)malloc(sizeof(int) * (unsigned int)M);
185
186 int ans = findSwapPairs(N, S, M, X, Y, P, Q);
187
188 fprintf(_outputFile, "%d\n", ans);
189
190 for (int i = 0; i < ans; ++i)
191 fprintf(_outputFile, "%d %d\n", P[i], Q[i]);
192
193 return 0;
194 }
195
196 // -------------- end grader ------------------
197 /*
198 execution time : 2.341 s
199 */

6.5.3 *Rezolvare detaliat 

6.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 autos-
tr zi. Fiecare autostrad  conecteaz  dou  localit µi distincte ³i ecare pereche de localit µi este
conectat  direct prin cel mult o autostrad . Pentru ecare 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  ecare or ³el se conecteaz  direct la o singur  alt  localitate ³i ecare municipiu este
conectat direct la trei sau mai multe localit µi.
Urm toarea gur  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 6. IOI 2015 801

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 ecare 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  ecare 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  cel mult 40. Pentru ecare 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 ecare 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 ecare test:
CAPITOLUL 6. IOI 2015 802

ˆ 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  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
2 12 7N
, 2 2 NO none
N N 1
3 13 2
YES none
4 10 7N
, 2 2 YES ecare municipiu este conectat la
exact trei localit µi
5 13 5N
2
YES none
6 39 7N
, 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 ³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 ecare test grader-ul a³eaz  valoarea returnat  de hubDistance ³i num rul de ape-
luri 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 specicarea listei de autostr zi. De notat c  este permis s  modici
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 6. IOI 2015 803

6.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 nd 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, nd 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 nd 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 dierent values in B identies 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 i

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 aren 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 sucient 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 6. IOI 2015 804

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.

6.6.2 Coduri surs 

Listing 6.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 6.6.2: towns-127541.cpp


1 // https://oj.uz/submission/127541 21 ms 1156 KB
2
3 #include "towns.h"
4 #include <assert.h>
CAPITOLUL 6. IOI 2015 805

5 #include <stdio.h>
6 #include <stdlib.h>
7 #include "graderlib.c"
8
9 #include <bits/stdc++.h>
10
11 using namespace std;
12
13 const int maxn = 150;
14 const int inf = 0x3f3f3f3f;
15
16 int qs[maxn][maxn];
17 int deb = 0;
18
19 int n;
20 int query(int a, int b)
21 {
22 assert(a < n && b < n && a >= 0 && b >= 0);
23 if (a > b) swap(a, b);
24 if (qs[a][b] == -1) qs[a][b] = getDistance(a, b);
25 return qs[a][b];
26 }
27
28 int a, b;
29 int B;
30 bool subtree(int x, int y)
31 {
32 int xd = (query(a, x) + query(b, x) - query(a, b)) / 2;
33 int yd = (query(a, y) + query(b, y) - query(a, b)) / 2;
34 return (xd + yd) > query(x, y);
35 }
36
37 int hubDistance(int n, int sub)
38 {
39 deb++;
40 ::n = n;
41 memset(qs, -1, sizeof qs);
42 a = b = B = 0;
43
44 for (int i = 0; i < n; i++)
45 qs[i][i] = 0;
46
47 a = 0, b = 0;
48 for (int i = 0; i < n; i++)
49 if (query(0, a) < query(0, i)) a = i;
50
51 for (int i = 0; i < n; i++)
52 if (query(a, b) < query(a, i)) b = i;
53
54 B = b; b = 0;
55 int hu = -1, out = inf;
56 for (int i = 0; i < n; i++)
57 {
58 int cd = (query(a, i) + query(b, i) - query(a, b)) / 2;
59 int ad = query(a, i) - cd;
60 int bd = query(a, B) - ad;
61
62 if (out > max(ad, bd)) out = max(ad, bd), hu = ad;
63 }
64 assert(hu > -1);
65
66 int lef = 0, rig = 0;
67 vector<int> v;
68 for (int i = 0; i < n; i++)
69 {
70 int cd = (query(a, i) + query(b, i) - query(a, b)) / 2;
71 int ad = query(a, i) - cd;
72
73 if (ad < hu) lef++;
74 else
75 if (ad > hu) rig++;
76 else v.push_back(i);
77 }
78
79 if (max(lef, rig) > n / 2)
80 {
CAPITOLUL 6. IOI 2015 806

81 hu = query(a, B) - hu;
82
83 int lef = 0, rig = 0;
84 vector<int> v;
85 for (int i = 0; i < n; i++)
86 {
87 int cd = (query(a, i) + query(b, i) - query(a, b)) / 2;
88 int ad = query(a, i) - cd;
89
90 if (ad < hu) lef++;
91 else
92 if (ad > hu) rig++;
93 else v.push_back(i);
94 }
95
96 if (max(lef, rig) > n / 2) return -out;
97 }
98
99 if (v.size() == 0)
100 {
101 if (max(lef, rig) > n / 2) return -out;
102 else return out;
103 }
104
105 vector<int> li, buk;
106 li.push_back(v[0]);
107 for (int i = 1; i < v.size(); i++)
108 {
109 if (subtree(v[i], li.back())) buk.push_back(v[i]);
110 else
111 {
112 li.push_back(v[i]);
113 if (!buk.empty())
114 {
115 li.push_back(buk.back());
116 buk.pop_back();
117 }
118 }
119 }
120
121 int cnt = 0;
122 int kan = li.back();
123
124 while (li.size() > 0)
125 {
126 if (subtree(li.back(), kan))
127 {
128 if (li.size() == 1)
129 {
130 buk.push_back(li.back());
131 li.pop_back();
132 }
133 else
134 {
135 li.pop_back();
136 li.pop_back();
137 cnt++;
138 }
139 }
140 else
141 {
142 if (buk.empty()) return out;
143 else
144 {
145 li.pop_back();
146 buk.pop_back();
147 cnt++;
148 }
149 }
150 }
151
152 cnt += buk.size();
153
154 if (cnt > n / 2) return -out;
155 else return out;
156 }
CAPITOLUL 6. IOI 2015 807

157
158 // ------------- begin grader ----------------------
159
160 int main()
161 {
162 FILE *f;
163 f = freopen("../tests/subtask_6/03","r",stdin);
164 assert(f != NULL);
165
166 f = freopen("towns.out","w",stdout);
167 assert(f != NULL);
168
169 int ncase, R, N;
170 int subtask;
171 int ret;
172
173 ret = scanf("%d%d",&subtask,&ncase);
174 assert(ret == 2);
175
176 for (int i = 0; i < ncase; i++)
177 {
178 ret = scanf("%d",&N);
179 assert(ret == 1);
180
181 _ini_query(N,subtask);
182 R=hubDistance(N,subtask);
183
184 printf("%d\n",R);
185 }
186
187 return 0;
188 }
189
190 // ------------- end grader ------------------------
191 /*
192 execution time : 0.328 s
193 */

Listing 6.6.3: towns-134867.cpp


1 // https://oj.uz/submission/134867 22 ms 504 KB
2 #include "towns.h"
3 #include <assert.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include "graderlib.c"
7
8 #include <bits/stdc++.h>
9
10 #define ff first
11 #define ss second
12
13 using namespace std;
14
15 int d[2][115], v;
16 map<int, int> cntr;
17
18 int hubDistance(int N, int sub)
19 {
20 memset(d, 0, sizeof d);
21 cntr.clear();
22 for(int i = 0; i < N; i++)
23 if(d[0][v] < (d[0][i] = getDistance(0, i)))
24 v = i;
25
26 int T = d[0][v], D = 0;
27 for(int i = 0; i < N; i++)
28 D = max(D, d[1][i] = getDistance(v, i));
29 int r = 1000000000;
30
31 for(int i = 0; i < N; i++)
32 {
33 int s = (T + d[1][i] - d[0][i]) >> 1;
34 cntr[s]++;
35 r = min(r, max(s, D - s));
CAPITOLUL 6. IOI 2015 808

36 }
37
38 int chk = -1, l = 0;
39
40 for(auto& x : cntr)
41 {
42 if(l <= N / 2 && N - (l += x.ss) <= N / 2)
43 {
44 if(max(x.ff, D - x.ff) == r)
45 {
46 if(x.ss <= N / 2) return r;
47 chk = x.ff << 1;
48 }
49 }
50 }
51
52 if(chk == -1) return -r;
53 vector<vector<int> > s, t, de;
54 vector<int> la;
55 for(int i = 0; i < N; i++)
56 if(chk == T + d[1][i] - d[0][i])
57 s.push_back({i});
58
59 while(s.size() > 1)
60 {
61 for(int i = 1; i < s.size(); i += 2)
62 {
63 if(getDistance(s[i - 1][0],
64 s[i][0]) == d[0][s[i - 1][0]] + d[1][s[i][0]] - T)
65 {
66 de.push_back(s[i - 1]);
67 de.push_back(s[i]);
68 }
69 else
70 {
71 for(int& x : s[i - 1])
72 s[i].push_back(x);
73 t.push_back(s[i]);
74 }
75 }
76
77 if(s.size() & 1)
78 {
79 de.push_back(s.back());
80 la = s.back();
81 }
82 swap(s, t);
83 t.clear();
84 }
85
86 int sz = 0;
87 if(s.empty()) s.push_back(la);
88 else sz = s[0].size();
89
90 if(s[0].empty()) return r;
91 for(auto& x : de)
92 {
93 if(getDistance(x[0], s[0][0]) != d[0][s[0][0]] + d[1][x[0]] - T)
94 sz += x.size();
95 }
96
97 if(sz > N / 2) return -r;
98 return r;
99 }
100
101 // ------------- begin grader ----------------------
102
103 int main()
104 {
105 FILE *f;
106 f = freopen("../tests/subtask_6/03","r",stdin);
107 assert(f != NULL);
108
109 f = freopen("towns.out","w",stdout);
110 assert(f != NULL);
111
CAPITOLUL 6. IOI 2015 809

112 int ncase, R, N;


113 int subtask;
114 int ret;
115
116 ret = scanf("%d%d",&subtask,&ncase);
117 assert(ret == 2);
118
119 for (int i = 0; i < ncase; i++)
120 {
121 ret = scanf("%d",&N);
122 assert(ret == 1);
123
124 _ini_query(N,subtask);
125 R=hubDistance(N,subtask);
126
127 printf("%d\n",R);
128 }
129
130 return 0;
131 }
132
133 // ------------- end grader ------------------------
134 /*
135 execution time : 0.375 s
136 */

Listing 6.6.4: towns-151462.cpp


1 // https://oj.uz/submission/151462 23 ms 1016 KB
2 #include "towns.h"
3 #include <assert.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include "graderlib.c"
7
8 #include <iostream>
9 #include <algorithm>
10 #include <vector>
11
12 using namespace std;
13
14 const int nmax=205;
15
16 int n;
17 int d[2][nmax];
18 int to[nmax],wh[nmax],sz[nmax];
19 vector<int> domin[nmax];
20
21 int eq(int x,int y)
22 {
23 return (to[x]+to[y]!=getDistance(x,y));
24 }
25
26 bool checkHub(int x)
27 {
28 vector<int> cand;
29 for(int i=0;i<n;i++)
30 {
31 domin[i].clear();
32 sz[i]=0;
33 }
34 for(int i=0;i<n;i++)
35 if(wh[i]==x)
36 cand.push_back(i);
37 int act=0,bal=0;
38 for(auto it: cand)
39 {
40 if(bal==0)
41 {
42 act=it;
43 bal++;
44 }
45 else
46 {
47 if(eq(act,it)) bal++;
CAPITOLUL 6. IOI 2015 810

48 else
49 {
50 domin[act].push_back(it);
51 bal--;
52 }
53 }
54 sz[act]++;
55 }
56 int ap=0;
57 for(auto it:cand)
58 {
59 if(sz[it])
60 {
61 if(eq(act,it)) ap+=sz[it]-(int)domin[it].size();
62 else
63 {
64 for(auto oth: domin[it])
65 {
66 ap+=eq(act,oth);
67 }
68 }
69 }
70 }
71 return (ap<=n/2);
72 }
73
74 int hubDistance(int N, int sub)
75 {
76 int d1=0,R=0,mx=0,d2=0;
77 n=N;
78 for(int i=0;i<2;i++)
79 for(int j=0;j<n;j++)
80 d[i][j]=0;
81 for(int i=1; i<n; i++)
82 {
83 d[0][i]=getDistance(0,i);
84 if(d[0][i]>d[0][d1])
85 d1=i;
86 }
87
88 for(int i=0; i<n; i++)
89 if(i!=d1)
90 {
91 d[1][i]=getDistance(d1,i);
92 if(d[1][i]>mx)
93 mx=d[1][i],d2=i;
94 }
95
96 int minDist=(1<<30);
97 int lim=d[1][d2]-(d[0][d2]+d[1][d2]-d[0][d1])/2;
98 for(int i=0; i<n; i++)
99 {
100 to[i]=(d[0][i]+d[1][i]-d[0][d1])/2;
101 wh[i]=(d[1][i]-to[i]);
102 if(max(wh[i],mx-wh[i])<minDist&&wh[i]<=lim)
103 minDist=max(wh[i],mx-wh[i]);
104 }
105
106 vector<int> hubs;
107 for(int i=0;i<n;i++)
108 if(max(wh[i],mx-wh[i])==minDist&&wh[i]<=lim)
109 hubs.push_back(wh[i]);
110
111 sort(hubs.begin(),hubs.end());
112
113 hubs.resize(unique(hubs.begin(),hubs.end())-hubs.begin());
114
115 bool ok=0;
116 for(auto cen :hubs)
117 {
118 int mici=0,mari=0;
119 for(int i=0;i<n;i++)
120 {
121 if(wh[i]<cen) mici++;
122 if(wh[i]>cen) mari++;
123 }
CAPITOLUL 6. IOI 2015 811

124 if(mici<=n/2&&mari<=n/2)
125 ok=checkHub(cen);
126 }
127
128 R=minDist;
129 if(!ok) R=-R;
130 return R;
131 }
132
133 // ------------- begin grader ----------------------
134
135 int main()
136 {
137 FILE *f;
138 f = freopen("../tests/subtask_6/03","r",stdin);
139 assert(f != NULL);
140
141 f = freopen("towns.out","w",stdout);
142 assert(f != NULL);
143
144 int ncase, R, N;
145 int subtask;
146 int ret;
147
148 ret = scanf("%d%d",&subtask,&ncase);
149 assert(ret == 2);
150
151 for (int i = 0; i < ncase; i++)
152 {
153 ret = scanf("%d",&N);
154 assert(ret == 1);
155
156 _ini_query(N,subtask);
157 R=hubDistance(N,subtask);
158
159 printf("%d\n",R);
160 }
161
162 return 0;
163 }
164
165 // ------------- end grader ------------------------
166 /*
167 execution time : 0.344 s
168 */

Listing 6.6.5: checkerTowns.cpp


1 #include "testlib.h"
2
3 #include<iostream>
4
5 using namespace std;
6
7 const string output_secret = "098d134608c94f7413faac591054ee35";
8
9 int main()
10 {
11 int argc=4;
12
13 char* argv[] =
14 {
15 (char*)"checker",
16 (char*)"../tests/subtask_6/03", // argv[1]=<input-file>
17 (char*)"towns.out", // argv[2]=<output-file>
18 (char*)"../tests/subtask_6/03.a", // argv[3]=<answer-file>
19 };
20 cout<<"argc = "<<argc<<"\n";
21 for(int kk=0;kk<argc;kk++)
22 cout<<argv[kk]<<"\n";
23 cout<<"----------------------\n";
24
25 registerChecker("towns", argc, argv);
26
27 compareRemainingLines(1); // incepand cu linia nr 1
CAPITOLUL 6. IOI 2015 812

28 }

6.6.3 *Rezolvare detaliat 


Capitolul 7

IOI 201432

7.1 Game
Problema 1 - Game 100 de puncte
Authors: Jonathan Moshei 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  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 (e direct, e 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 ecare 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 arma 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  e 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
32
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).

813
CAPITOLUL 7. IOI 2014 814

Î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 nal 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  r spuns no la ultima întrebare
acest lucru nu ar  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 
e 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 ³ier, numit game.c, game.cpp sau game.pas. în acest ³ier vor
 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 7. IOI 2014 815

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

7.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.
An O n4  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-rst search to nd 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 nd 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 7. IOI 2014 816

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.

7.1.2 Coduri surs 

Listing 7.1.1: game-92195.cpp


1 // https://oj.uz/submission/92195 322 ms 16532 KB ...
2
3 #include <bits/stdc++.h>
4
5 #include "game.h"
6
7 int ctr[2000];
8 int n;
9
10 void initialize(int n)
11 {
12 ::n=n;
13 }
14
15 int hasEdge(int u, int v)
16 {
17 if(v<u)
18 {
19 u+=v;
20 v=u-v;
21 u-=v;
22 }
23 ctr[v]++;
CAPITOLUL 7. IOI 2014 817

24 return (ctr[v]==v);
25 }
26
27 // -------------- begin grader ---------------
28
29 int read_int()
30 {
31 int x;
32 assert(scanf("%d", &x) == 1);
33 return x;
34 }
35
36 int main()
37 {
38 auto t1 = clock();
39
40 std::freopen("../tests/03-114.in", "r", stdin) ;
41 std::freopen("game.out", "w", stdout) ;
42
43 int n, u, v;
44 n = read_int();
45
46 auto t2 = clock();
47
48 initialize(n);
49 for (int i = 0; i < n * (n - 1) / 2; i++)
50 {
51 u = read_int();
52 v = read_int();
53 printf("%d\n", hasEdge(u, v));
54 }
55
56 auto t3 = clock();
57
58 fclose(stdout);
59
60 auto t4 = clock();
61
62 // reset console output
63 freopen("CON", "w", stdout);
64
65 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
66 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
67 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
68
69 return 0;
70 }
71
72 // -------------- end grader ---------------
73 /*
74 t2-t1 = 0
75 t3-t2 = 4.312
76 t4-t3 = 0.016
77
78 Process returned 0 (0x0) execution time : 4.359 s
79 Press any key to continue.
80 argc = 4
81 checker
82 ../tests/03-114.in
83 game.out
84 ../tests/03-114.out
85 ----------------------
86 1
87 Correct
88
89 Process returned 0 (0x0) execution time : 2.375 s
90 Press any key to continue.
91 */

Listing 7.1.2: game-117330.cpp


1 // https://oj.uz/submission/117330 329 ms 16376 KB
2
3 #include "game.h"
4 #include <bits/stdc++.h>
CAPITOLUL 7. IOI 2014 818

5
6 using namespace std;
7
8 int cnt[1500];
9 int first_time;
10
11 void initialize(int n) {}
12 int c[1500];
13 int hasEdge(int u, int v) {
14 return ++c[u > v ? u : v] == (u > v ? u : v);
15 }
16
17 // -------------- begin grader ---------------
18
19 int read_int()
20 {
21 int x;
22 assert(scanf("%d", &x) == 1);
23 return x;
24 }
25
26 int main()
27 {
28 auto t1 = clock();
29
30 std::freopen("../tests/03-114.in", "r", stdin) ;
31 std::freopen("game.out", "w", stdout) ;
32
33 int n, u, v;
34 n = read_int();
35
36 auto t2 = clock();
37
38 initialize(n);
39 for (int i = 0; i < n * (n - 1) / 2; i++)
40 {
41 u = read_int();
42 v = read_int();
43 printf("%d\n", hasEdge(u, v));
44 }
45
46 auto t3 = clock();
47
48 fclose(stdout);
49
50 auto t4 = clock();
51
52 // reset console output
53 freopen("CON", "w", stdout);
54
55 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
56 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
57 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
58
59 return 0;
60 }
61
62 // -------------- end grader ---------------
63 /*
64 t2-t1 = 0
65 t3-t2 = 3.765
66 t4-t3 = 0.016
67
68 Process returned 0 (0x0) execution time : 3.828 s
69 Press any key to continue.
70 argc = 4
71 checker
72 ../tests/03-114.in
73 game.out
74 ../tests/03-114.out
75 ----------------------
76 1
77 Correct
78
79 Process returned 0 (0x0) execution time : 2.358 s
80 Press any key to continue.
CAPITOLUL 7. IOI 2014 819

81 */

Listing 7.1.3: game-231330.cpp


1 // https://oj.uz/submission/231330 372 ms 7160 KB
2
3 #include <cstdio>
4 #include <cassert>
5 #include "game.h"
6
7 #include<ctime>
8 #include<iostream>
9
10 int d[1500];
11
12 void initialize(int){}
13
14 int hasEdge(int u,int v)
15 {
16 return ++d[u=u>v?u:v]==u;
17 }
18
19 // -------------- begin grader ---------------
20
21 int read_int()
22 {
23 int x;
24 assert(scanf("%d", &x) == 1);
25 return x;
26 }
27
28 int main()
29 {
30 auto t1 = clock();
31
32 std::freopen("../tests/03-114.in", "r", stdin) ;
33 std::freopen("game.out", "w", stdout) ;
34
35 int n, u, v;
36 n = read_int();
37
38 auto t2 = clock();
39
40 initialize(n);
41 for (int i = 0; i < n * (n - 1) / 2; i++)
42 {
43 u = read_int();
44 v = read_int();
45 printf("%d\n", hasEdge(u, v));
46 }
47
48 auto t3 = clock();
49
50 fclose(stdout);
51
52 auto t4 = clock();
53
54 // reset console output
55 freopen("CON", "w", stdout);
56
57 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
58 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
59 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
60
61 return 0;
62 }
63
64 // -------------- end grader ---------------
65 /*
66 t2-t1 = 0.015
67 t3-t2 = 3.799
68 t4-t3 = 0.016
69
70 Process returned 0 (0x0) execution time : 3.846 s
71 Press any key to continue.
CAPITOLUL 7. IOI 2014 820

72 argc = 4
73 checker
74 ../tests/03-114.in
75 game.out
76 ../tests/03-114.out
77 ----------------------
78 1
79 Correct
80
81 Process returned 0 (0x0) execution time : 2.328 s
82 Press any key to continue.
83 */

Listing 7.1.4: game_n2.cpp


1 // https://oj.uz/submission/231330 372 ms 7160 KB
2
3 #include <cstdio>
4 #include <cassert>
5 #include "game.h"
6
7 #include<ctime>
8 #include<iostream>
9
10 int c[1500];
11
12 void initialize(int){}
13
14 int hasEdge(int u,int v)
15 {
16 return ++c[u > v ? u : v] == (u > v ? u : v);
17 }
18
19 // -------------- begin grader ---------------
20
21 int read_int()
22 {
23 int x;
24 assert(scanf("%d", &x) == 1);
25 return x;
26 }
27
28 int main()
29 {
30 auto t1 = clock();
31
32 std::freopen("../tests/03-114.in", "r", stdin) ;
33 std::freopen("game.out", "w", stdout) ;
34
35 int n, u, v;
36 n = read_int();
37
38 auto t2 = clock();
39
40 initialize(n);
41 for (int i = 0; i < n * (n - 1) / 2; i++)
42 {
43 u = read_int();
44 v = read_int();
45 printf("%d\n", hasEdge(u, v));
46 }
47
48 auto t3 = clock();
49
50 fclose(stdout);
51
52 auto t4 = clock();
53
54 // reset console output
55 freopen("CON", "w", stdout);
56
57 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
58 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
59 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
60
CAPITOLUL 7. IOI 2014 821

61 return 0;
62 }
63
64 // -------------- end grader ---------------
65 /*
66 t2-t1 = 0
67 t3-t2 = 3.791
68 t4-t3 = 0.015
69
70 Process returned 0 (0x0) execution time : 3.838 s
71 Press any key to continue.
72 argc = 4
73 checker
74 ../tests/03-114.in
75 game.out
76 ../tests/03-114.out
77 ----------------------
78 1
79 Correct
80
81 Process returned 0 (0x0) execution time : 2.344 s
82 Press any key to continue.
83 */

Listing 7.1.5: checkerGame.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/03-114.in",
14 (char*)"game.out",
15 (char*)"../tests/03-114.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("game", argc, argv);
24 compareRemainingLines();
25 }

7.1.3 *Rezolvare detaliat 

7.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 gura
CAPITOLUL 7. IOI 2014 822

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 gura 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 nal ajungând în gara 2 aat  în
blocul 3.
Deoarece pot exista mai multe rute posibile, distanµa de la o gar  la alta se dene³te ca ind
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 a  g rile ³i în ce tip de bloc se a  acestea. Singurul indiciu
r mas în sistem este num rul blocului în care se a  gara 0, care se a  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 ecare gar  num rul
³i tipul blocului în care se a .
ˆ 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 nalul execuµiei acestei funcµii
num rul blocului în care se a  gara i trebuie s  se ae în celula location[i].
ˆ stype: un tablou unidimensional de dimensiune n; la nalul execuµiei acestei funcµii tipul
blocului în care se a  gara i trebuie s  se ae î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 blo-
curilor în care se a  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 a  î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 a  în blocuri de tip D, ³i
toate g rile situate la vestul g rii
0 se a  î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 7. IOI 2014 823

Detalii de implementare
Voi trebuie s  înc rcaµi exact un ³ier, denumit rail.c, rail.cpp sau rail.pas. Acest ³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 a³  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

7.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 rst 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 7. IOI 2014 824

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 nd the answer.

7.2.2 Coduri surs 

Listing 7.2.1: rail-117000.cpp


1 //https://oj.uz/submission/117000 77 ms 760 KB
2
3 #include "rail.h"
4
5 #include <bits/stdc++.h>
6
7 #define jizz ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
8 #define pb push_back
9 #define ET cout << "\n"
10 #define MEM(i,j) memset(i,j,sizeof i)
11 #define F first
12 #define S second
13 #define MP make_pair
14 #define ALL(v) v.begin(),v.end()
15 #define DB(a,s,e) {for(int i=s;i<e;++i) cerr << a[i] << " ";ET;}
16
17 using namespace std;
18
19 typedef long long ll;
20 typedef pair<int,int> pii;
CAPITOLUL 7. IOI 2014 825

21 typedef pair<ll,ll> pll;


22
23 const int INF=1e9;
24
25 int gd(int i,int j)
26 {
27 return getDistance(i,j);
28 }
29
30 void findLocation(int N, int first, int location[], int stype[])
31 {
32 location[0]=first,stype[0]=1;
33 set<int> loc[3];
34 if(N==1) return;
35 vector<int> d0(N,0),d1(N,0),vR,vL;
36 deque<pii> vis;
37 for(int i=1;i<N;++i)
38 d0[i]=gd(0,i),vis.pb(MP(d0[i],i));
39 sort(ALL(vis));
40 int x=vis[0].S,tpR,tpL;
41 vis.pop_front(),location[x]=d0[x]+first,stype[x]=2;
42 for(int i=1;i<N;++i)
43 if(i!=x)
44 d1[i]=gd(i,x);
45 while(!vis.empty())
46 {
47 auto tmp=vis[0].S;
48 vis.pop_front();
49 if(d0[tmp]-d1[tmp]==d0[x])
50 if(d1[tmp]<=d0[x])
51 location[tmp]=location[x]-d1[tmp],stype[tmp]=1;
52 else
53 if(vL.empty())
54 location[tmp]=first+2*d0[x]-d0[tmp],
55 stype[tmp]=1,
56 vL.pb(location[tmp]),
57 tpL=tmp;
58 else
59 {
60 int dis=gd(tpL,tmp),
61 delta=location[tpL]+(d0[tpL]+dis-d0[tmp])/2;
62 int p=*lower_bound(vL.rbegin(),vL.rend(),delta);
63 if(p!=delta)
64 location[tmp]=first+2*d0[x]-d0[tmp],
65 stype[tmp]=1,
66 vL.pb(location[tmp]),
67 tpL=tmp;
68 else
69 location[tmp]=location[tpL]+dis,
70 stype[tmp]=2;
71 }
72 else
73 if(vR.empty())
74 location[tmp]=d0[tmp]+first,
75 stype[tmp]=2,
76 vR.pb(location[tmp]),
77 tpR=tmp;
78 else
79 {
80 int dis=gd(tpR,tmp),
81 delta=location[tpR]-(d0[tpR]+dis-d0[tmp])/2;
82 int p=*lower_bound(ALL(vR),delta);
83 if(p!=delta)
84 location[tmp]=d0[tmp]+first,
85 stype[tmp]=2,
86 vR.pb(location[tmp]),
87 tpR=tmp;
88 else
89 location[tmp]=location[tpR]-dis,
90 stype[tmp]=1;
91 }
92 }
93 }
94
95 // ------------- begin grader ---------------------
96
CAPITOLUL 7. IOI 2014 826

97 typedef struct Station


98 {
99 int index;
100 int type;
101 int location;
102 int L,R;
103 } STATION;
104
105 long long cnt;
106 static int S,SUBTASK;
107 static STATION stations[10004];
108
109 int cmp_fun_1(const void *a,const void *b)
110 {
111 STATION c,d;
112 c = *(STATION*)(a);
113 d = *(STATION*)(b);
114 return c.location < d.location ? -1 : 1;
115 }
116
117 int cmp_fun_2(const void *a,const void *b)
118 {
119 STATION c,d;
120 c = *(STATION*)(a);
121 d = *(STATION*)(b);
122 return c.index < d.index ? -1 : 1;
123 }
124
125 void now_I_want_to_getLR()
126 {
127 int now = stations[S-1].index,i;
128 for(i=S-2;i>=0;i--)
129 {
130 stations[i].R = now;
131 if(stations[i].type==2) now = stations[i].index;
132 }
133 now = stations[0].index;
134 for(i=1;i<S;i++)
135 {
136 stations[i].L = now;
137 if(stations[i].type==1) now = stations[i].index;
138 }
139 }
140
141 int getDistance(int x,int y)
142 {
143 cnt++;
144 if(x==y) return 0;
145 if(x<0 || x>=S || y<0 || y>=S) return -1;
146 if(stations[x].location > stations[y].location)
147 {
148 int tmp = x;
149 x = y;
150 y = tmp;
151 }
152
153 int ret = 0;
154 if(stations[x].type==1 && stations[y].type==1)
155 {
156 ret = stations[stations[y].R].location-
157 stations[x].location+
158 stations[stations[y].R].location-
159 stations[y].location;
160 }
161 else
162 if(stations[x].type==1 && stations[y].type==2)
163 {
164 ret = stations[y].location-stations[x].location;
165 }
166 else
167 if(stations[x].type==2 && stations[y].type==2)
168 {
169 ret = stations[x].location-
170 stations[stations[x].L].location+
171 stations[y].location-
172 stations[stations[x].L].location;
CAPITOLUL 7. IOI 2014 827

173 }
174 else
175 if(stations[x].type==2 && stations[y].type==1)
176 {
177 ret = stations[x].location-
178 stations[stations[x].L].location+
179 stations[stations[y].R].location-
180 stations[stations[x].L].location+
181 stations[stations[y].R].location-
182 stations[y].location;
183 }
184
185 return ret;
186 }
187
188 void getInput()
189 {
190 int g;
191 g = scanf("%d",&SUBTASK);
192 g = scanf("%d",&S);
193
194 int s;
195 for (s = 0; s < S; s++)
196 {
197 int type, location;
198 g = scanf(" %d %d",&type,&location);
199 stations[s].index = s;
200 stations[s].location = location;
201 stations[s].type = type;
202 stations[s].L = -1;
203 stations[s].R = -1;
204 }
205
206 qsort(stations, S, sizeof(STATION), cmp_fun_1);
207 now_I_want_to_getLR();
208 qsort(stations, S, sizeof(STATION), cmp_fun_2);
209 }
210
211 int serverGetStationNumber()
212 {
213 return S;
214 }
215
216 int serverGetSubtaskNumber()
217 {
218 return SUBTASK;
219 }
220
221 int serverGetFirstStationLocation()
222 {
223 return stations[0].location;
224 }
225
226 int main()
227 {
228 auto t1 = clock();
229
230 std::freopen("../tests/04-020.in", "r", stdin) ;
231 //std::freopen("rail.out", "w", stdout) ;
232
233 int i;
234 getInput();
235 cnt = 0;
236
237 int location[10005];
238 int type[10005];
239 int ok = 1;
240
241 auto t2 = clock();
242
243 findLocation(S, serverGetFirstStationLocation(),location, type);
244
245 auto t3 = clock();
246
247 if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;
248 if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;
CAPITOLUL 7. IOI 2014 828

249
250 for (i = 0; i < S; i++)
251 if(type[i]!=stations[i].type || location[i]!=stations[i].location)
252 ok = 0;
253
254 if(ok==0) printf("Incorrect\n\n");
255 else printf("Correct\n\n");
256
257 fclose(stdout);
258
259 auto t4 = clock();
260
261 // reset console output
262 freopen("CON", "w", stdout);
263
264 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
265 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
266 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
267
268 return 0;
269 }
270 // -------------- end grader ----------------------
271 /*
272 Correct
273
274 t2-t1 = 0.031
275 t3-t2 = 0.015
276 t4-t3 = 0.016
277
278 Process returned 0 (0x0) execution time : 0.094 s
279 Press any key to continue.
280 */

Listing 7.2.2: rail-121219.cpp


1 // https://oj.uz/submission/121219 80 ms 760 KB
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 #define x first
8 #define y second
9 #define ll long long
10 #define pi pair<int,int>
11 #define pl pair<ll,ll>
12 #define pd pair<double,double>
13 #define ld long double
14 #define pld pair<ld,ld>
15 #define lg length()
16 #define sz size()
17 #define vi vector<int>
18 #define vl vector<ll>
19 #define vp vector<pi>
20 #define vpl vector<pl>
21 #define pb push_back
22 #define INF 1000000005
23 #define LINF 1000000000000000005
24
25 #ifdef LOCAL_DEFINE
26 mt19937 rng(69);
27 #else
28 seed_seq seq
29 {
30 (uint64_t) chrono::
31 duration_cast<chrono::
32 nanoseconds>(chrono::
33 high_resolution_clock::
34 now().time_since_epoch()).count(),
35 //(uint64_t) __builtin_ia32_rdtsc(),
36 //(uint64_t) (uintptr_t) make_unique<char>().get()
37 };
38 mt19937 rng(seq);
39 #endif
40
CAPITOLUL 7. IOI 2014 829

41 #include "rail.h"
42
43 void findLocation(int n, int x, int p[], int t[])
44 {
45 if(n==1)
46 {
47 p[0]=x; t[0]=1;
48 return;
49 }
50 int a[n+5],b[n+5];
51 for(int i=1;i<n;i++) a[i]=getDistance(0,i);
52 int mn=INF,y=-1;
53 for(int i=1;i<n;i++)
54 {
55 if(a[i]<mn) mn=a[i],y=i;
56 }
57 for(int i=0;i<n;i++)
58 {
59 if(i==y) continue;
60 b[i]=getDistance(y,i);
61 }
62 mn=INF;
63 int z=-1;
64 for(int i=0;i<n;i++)
65 {
66 if(i==y) continue;
67 if(b[i]<mn) mn=b[i],z=i;
68 }
69 int diff=b[z]-a[y];
70 p[z]=x-diff;
71 p[y]=p[z]+b[z];
72 t[z]=1;
73 t[y]=2;
74 for(int i=0;i<n;i++)
75 {
76 a[i]+=diff;
77 }
78 vector <pi> l,r;
79 vector <int> u,v;
80 for(int i=0;i<n;i++)
81 {
82 if(i==y || i==z) continue;
83 if(i==0) a[i]=2*(p[y]-p[z])-diff;
84 if(a[i]>b[i]) l.pb({b[i],i});
85 else r.pb({a[i],i});
86 }
87
88 sort(l.begin(),l.end());
89 u.pb(p[z]);
90 int f=z;
91 for(pi i : l)
92 {
93 int d=getDistance(i.y,f);
94 p[i.y]=p[f]+d;
95 if(p[i.y]>=p[z])
96 {
97 p[i.y]=p[y]-b[i.y];
98 t[i.y]=1;
99 u.pb(p[i.y]);
100 f=i.y;
101 }
102 int c=*(lower_bound(u.begin(),u.end(),p[i.y],greater<int>()));
103 if(b[i.y]==p[i.y]+p[y]-2*c && p[i.y]!=c)
104 {
105 t[i.y]=2;
106 }
107 else
108 {
109 p[i.y]=p[y]-b[i.y];
110 t[i.y]=1;
111 u.pb(p[i.y]);
112 f=i.y;
113 }
114 }
115
116 sort(r.begin(),r.end());
CAPITOLUL 7. IOI 2014 830

117 v.pb(p[y]);
118 f=y;
119 for(pi i : r)
120 {
121 int d=getDistance(i.y,f);
122 p[i.y]=p[f]-d;
123 if(p[i.y]<=p[y])
124 {
125 p[i.y]=p[z]+a[i.y];
126 t[i.y]=2;
127 v.pb(p[i.y]);
128 f=i.y;
129 }
130 int c=*(lower_bound(v.begin(),v.end(),p[i.y]));
131 if(a[i.y]==2*c-p[i.y]-p[z] && p[i.y]!=c)
132 {
133 t[i.y]=1;
134 }
135 else
136 {
137 p[i.y]=p[z]+a[i.y];
138 t[i.y]=2;
139 v.pb(p[i.y]);
140 f=i.y;
141 }
142 }
143 }
144
145 // ------------- begin grader ---------------------
146
147 typedef struct Station
148 {
149 int index;
150 int type;
151 int location;
152 int L,R;
153 } STATION;
154
155 long long cnt;
156 static int S,SUBTASK;
157 static STATION stations[10004];
158
159 int cmp_fun_1(const void *a,const void *b)
160 {
161 STATION c,d;
162 c = *(STATION*)(a);
163 d = *(STATION*)(b);
164 return c.location < d.location ? -1 : 1;
165 }
166
167 int cmp_fun_2(const void *a,const void *b)
168 {
169 STATION c,d;
170 c = *(STATION*)(a);
171 d = *(STATION*)(b);
172 return c.index < d.index ? -1 : 1;
173 }
174
175 void now_I_want_to_getLR()
176 {
177 int now = stations[S-1].index,i;
178 for(i=S-2;i>=0;i--)
179 {
180 stations[i].R = now;
181 if(stations[i].type==2) now = stations[i].index;
182 }
183 now = stations[0].index;
184 for(i=1;i<S;i++)
185 {
186 stations[i].L = now;
187 if(stations[i].type==1) now = stations[i].index;
188 }
189 }
190
191 int getDistance(int x,int y)
192 {
CAPITOLUL 7. IOI 2014 831

193 cnt++;
194 if(x==y) return 0;
195 if(x<0 || x>=S || y<0 || y>=S) return -1;
196 if(stations[x].location > stations[y].location)
197 {
198 int tmp = x;
199 x = y;
200 y = tmp;
201 }
202
203 int ret = 0;
204 if(stations[x].type==1 && stations[y].type==1)
205 {
206 ret = stations[stations[y].R].location-
207 stations[x].location+
208 stations[stations[y].R].location-
209 stations[y].location;
210 }
211 else
212 if(stations[x].type==1 && stations[y].type==2)
213 {
214 ret = stations[y].location-stations[x].location;
215 }
216 else
217 if(stations[x].type==2 && stations[y].type==2)
218 {
219 ret = stations[x].location-
220 stations[stations[x].L].location+
221 stations[y].location-
222 stations[stations[x].L].location;
223 }
224 else
225 if(stations[x].type==2 && stations[y].type==1)
226 {
227 ret = stations[x].location-
228 stations[stations[x].L].location+
229 stations[stations[y].R].location-
230 stations[stations[x].L].location+
231 stations[stations[y].R].location-
232 stations[y].location;
233 }
234
235 return ret;
236 }
237
238 void getInput()
239 {
240 int g;
241 g = scanf("%d",&SUBTASK);
242 g = scanf("%d",&S);
243
244 int s;
245 for (s = 0; s < S; s++)
246 {
247 int type, location;
248 g = scanf(" %d %d",&type,&location);
249 stations[s].index = s;
250 stations[s].location = location;
251 stations[s].type = type;
252 stations[s].L = -1;
253 stations[s].R = -1;
254 }
255
256 qsort(stations, S, sizeof(STATION), cmp_fun_1);
257 now_I_want_to_getLR();
258 qsort(stations, S, sizeof(STATION), cmp_fun_2);
259 }
260
261 int serverGetStationNumber()
262 {
263 return S;
264 }
265
266 int serverGetSubtaskNumber()
267 {
268 return SUBTASK;
CAPITOLUL 7. IOI 2014 832

269 }
270
271 int serverGetFirstStationLocation()
272 {
273 return stations[0].location;
274 }
275
276 int main()
277 {
278 auto t1 = clock();
279
280 std::freopen("../tests/04-020.in", "r", stdin) ;
281 //std::freopen("rail.out", "w", stdout) ;
282
283 int i;
284 getInput();
285 cnt = 0;
286
287 int location[10005];
288 int type[10005];
289 int ok = 1;
290
291 auto t2 = clock();
292
293 findLocation(S, serverGetFirstStationLocation(),location, type);
294
295 auto t3 = clock();
296
297 if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;
298 if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;
299
300 for (i = 0; i < S; i++)
301 if(type[i]!=stations[i].type || location[i]!=stations[i].location)
302 ok = 0;
303
304 if(ok==0) printf("Incorrect\n\n");
305 else printf("Correct\n\n");
306
307 fclose(stdout);
308
309 auto t4 = clock();
310
311 // reset console output
312 freopen("CON", "w", stdout);
313
314 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
315 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
316 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
317
318 return 0;
319 }
320 // -------------- end grader ----------------------
321 /*
322 Correct
323
324 t2-t1 = 0.031
325 t3-t2 = 0.015
326 t4-t3 = 0
327
328 Process returned 0 (0x0) execution time : 0.078 s
329 Press any key to continue.
330 */

Listing 7.2.3: rail-125767.cpp


1 // https://oj.uz/submission/125767 87 ms 924 KB
2
3 #include "rail.h"
4 #include <bits/stdc++.h>
5 using namespace std;
6
7 const int mxN=5e3;
8 array<int, 2> a[mxN];
9 map<int, int> mp;
10
CAPITOLUL 7. IOI 2014 833

11 void findLocation(int n, int f, int *p, int *s)


12 {
13 p[0]=f;
14 s[0]=1;
15 mp[f]=0;
16 for(int i=1; i<n; ++i)
17 a[i]={getDistance(0, i), i};
18
19 sort(a, a+n);
20
21 int l=0, r=a[1][1];
22 p[r]=f+a[1][0];
23 s[r]=2;
24 mp[p[r]]=r;
25 for(int i=2; i<n; ++i)
26 {
27 int dl=getDistance(l, a[i][1]),
28 dr=getDistance(r, a[i][1]),
29 m=(p[l]+dl+p[r]-dr)/2,
30 t=mp.find(m)==mp.end()?(m>f?1:2):s[mp[m]];
31
32 if(t^2)
33 {
34 p[a[i][1]]=p[l]+dl;
35 s[a[i][1]]=2;
36 if(p[a[i][1]]>p[r])
37 r=a[i][1];
38 }
39 else
40 {
41 p[a[i][1]]=p[r]-dr;
42 s[a[i][1]]=1;
43 if(p[a[i][1]]<p[l])
44 l=a[i][1];
45 }
46
47 mp[p[a[i][1]]]=a[i][1];
48 }
49 }
50
51 // ------------- begin grader ---------------------
52
53 typedef struct Station
54 {
55 int index;
56 int type;
57 int location;
58 int L,R;
59 } STATION;
60
61 long long cnt;
62 static int S,SUBTASK;
63 static STATION stations[10004];
64
65 int cmp_fun_1(const void *a,const void *b)
66 {
67 STATION c,d;
68 c = *(STATION*)(a);
69 d = *(STATION*)(b);
70 return c.location < d.location ? -1 : 1;
71 }
72
73 int cmp_fun_2(const void *a,const void *b)
74 {
75 STATION c,d;
76 c = *(STATION*)(a);
77 d = *(STATION*)(b);
78 return c.index < d.index ? -1 : 1;
79 }
80
81 void now_I_want_to_getLR()
82 {
83 int now = stations[S-1].index,i;
84 for(i=S-2;i>=0;i--)
85 {
86 stations[i].R = now;
CAPITOLUL 7. IOI 2014 834

87 if(stations[i].type==2) now = stations[i].index;


88 }
89 now = stations[0].index;
90 for(i=1;i<S;i++)
91 {
92 stations[i].L = now;
93 if(stations[i].type==1) now = stations[i].index;
94 }
95 }
96
97 int getDistance(int x,int y)
98 {
99 cnt++;
100 if(x==y) return 0;
101 if(x<0 || x>=S || y<0 || y>=S) return -1;
102 if(stations[x].location > stations[y].location)
103 {
104 int tmp = x;
105 x = y;
106 y = tmp;
107 }
108
109 int ret = 0;
110 if(stations[x].type==1 && stations[y].type==1)
111 {
112 ret = stations[stations[y].R].location-
113 stations[x].location+
114 stations[stations[y].R].location-
115 stations[y].location;
116 }
117 else
118 if(stations[x].type==1 && stations[y].type==2)
119 {
120 ret = stations[y].location-stations[x].location;
121 }
122 else
123 if(stations[x].type==2 && stations[y].type==2)
124 {
125 ret = stations[x].location-
126 stations[stations[x].L].location+
127 stations[y].location-
128 stations[stations[x].L].location;
129 }
130 else
131 if(stations[x].type==2 && stations[y].type==1)
132 {
133 ret = stations[x].location-
134 stations[stations[x].L].location+
135 stations[stations[y].R].location-
136 stations[stations[x].L].location+
137 stations[stations[y].R].location-
138 stations[y].location;
139 }
140
141 return ret;
142 }
143
144 void getInput()
145 {
146 int g;
147 g = scanf("%d",&SUBTASK);
148 g = scanf("%d",&S);
149
150 int s;
151 for (s = 0; s < S; s++)
152 {
153 int type, location;
154 g = scanf(" %d %d",&type,&location);
155 stations[s].index = s;
156 stations[s].location = location;
157 stations[s].type = type;
158 stations[s].L = -1;
159 stations[s].R = -1;
160 }
161
162 qsort(stations, S, sizeof(STATION), cmp_fun_1);
CAPITOLUL 7. IOI 2014 835

163 now_I_want_to_getLR();
164 qsort(stations, S, sizeof(STATION), cmp_fun_2);
165 }
166
167 int serverGetStationNumber()
168 {
169 return S;
170 }
171
172 int serverGetSubtaskNumber()
173 {
174 return SUBTASK;
175 }
176
177 int serverGetFirstStationLocation()
178 {
179 return stations[0].location;
180 }
181
182 int main()
183 {
184 auto t1 = clock();
185
186 std::freopen("../tests/04-020.in", "r", stdin) ;
187 //std::freopen("rail.out", "w", stdout) ;
188
189 int i;
190 getInput();
191 cnt = 0;
192
193 int location[10005];
194 int type[10005];
195 int ok = 1;
196
197 auto t2 = clock();
198
199 findLocation(S, serverGetFirstStationLocation(),location, type);
200
201 auto t3 = clock();
202
203 if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;
204 if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;
205
206 for (i = 0; i < S; i++)
207 if(type[i]!=stations[i].type || location[i]!=stations[i].location)
208 ok = 0;
209
210 if(ok==0) printf("Incorrect\n\n");
211 else printf("Correct\n\n");
212
213 fclose(stdout);
214
215 auto t4 = clock();
216
217 // reset console output
218 freopen("CON", "w", stdout);
219
220 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
221 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
222 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
223
224 return 0;
225 }
226 // -------------- end grader ----------------------
227 /*
228 Correct
229
230 t2-t1 = 0.031
231 t3-t2 = 0.031
232 t4-t3 = 0
233
234 Process returned 0 (0x0) execution time : 0.109 s
235 Press any key to continue.
236 */

Listing 7.2.4: rail-139096.cpp


CAPITOLUL 7. IOI 2014 836

1 // https://oj.uz/submission/139096 87 ms 888 KB
2
3 #include "rail.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 void findLocation(int n, int first, int l[], int t[])
9 {
10 l[0] = first;
11 t[0] = 1;
12 vector <pair <int, int>> v;
13 for (int i = 1; i < n; i++)
14 {
15 v.push_back({getDistance(0, i), i});
16 }
17
18 sort(v.begin(), v.end());
19 l[v[0].second] = first + v[0].first;
20 t[v[0].second] = 2;
21
22 set <int> C, D;
23 C.insert(l[0]); D.insert(l[v[0].second]);
24
25 int L = 0, R = v[0].second;
26 for (int i = 1; i < v.size(); i++)
27 {
28 int id = v[i].second;
29 int x = getDistance(id, L), y = getDistance(id, R);
30 int lcan = l[L] + x, rcan = l[R] - y, res;
31 auto it = --C.upper_bound(lcan);
32 if (y == lcan - *it + l[R] - *it)
33 {
34 res = 1;
35 }
36 else
37 {
38 auto it = D.upper_bound(rcan);
39 if (it != D.end() && x == *it - rcan + *it - l[L])
40 {
41 res = 0;
42 }
43 else
44 {
45 if (v[i].first == 2 * l[v[0].second] - first - rcan) res = 0;
46 else res = 1;
47 }
48 }
49
50 if (res == 1)
51 {
52 l[id] = lcan;
53 t[id] = 2;
54 D.insert(l[id]);
55 if (l[R] < l[id]) R = id;
56 }
57 else
58 {
59 l[id] = rcan;
60 t[id] = 1;
61 C.insert(l[id]);
62 if (l[L] > l[id]) L = id;
63 }
64 }
65 }
66
67 // ------------- begin grader ---------------------
68
69 typedef struct Station
70 {
71 int index;
72 int type;
73 int location;
74 int L,R;
75 } STATION;
CAPITOLUL 7. IOI 2014 837

76
77 long long cnt;
78 static int S,SUBTASK;
79 static STATION stations[10004];
80
81 int cmp_fun_1(const void *a,const void *b)
82 {
83 STATION c,d;
84 c = *(STATION*)(a);
85 d = *(STATION*)(b);
86 return c.location < d.location ? -1 : 1;
87 }
88
89 int cmp_fun_2(const void *a,const void *b)
90 {
91 STATION c,d;
92 c = *(STATION*)(a);
93 d = *(STATION*)(b);
94 return c.index < d.index ? -1 : 1;
95 }
96
97 void now_I_want_to_getLR()
98 {
99 int now = stations[S-1].index,i;
100 for(i=S-2;i>=0;i--)
101 {
102 stations[i].R = now;
103 if(stations[i].type==2) now = stations[i].index;
104 }
105 now = stations[0].index;
106 for(i=1;i<S;i++)
107 {
108 stations[i].L = now;
109 if(stations[i].type==1) now = stations[i].index;
110 }
111 }
112
113 int getDistance(int x,int y)
114 {
115 cnt++;
116 if(x==y) return 0;
117 if(x<0 || x>=S || y<0 || y>=S) return -1;
118 if(stations[x].location > stations[y].location)
119 {
120 int tmp = x;
121 x = y;
122 y = tmp;
123 }
124
125 int ret = 0;
126 if(stations[x].type==1 && stations[y].type==1)
127 {
128 ret = stations[stations[y].R].location-
129 stations[x].location+
130 stations[stations[y].R].location-
131 stations[y].location;
132 }
133 else
134 if(stations[x].type==1 && stations[y].type==2)
135 {
136 ret = stations[y].location-stations[x].location;
137 }
138 else
139 if(stations[x].type==2 && stations[y].type==2)
140 {
141 ret = stations[x].location-
142 stations[stations[x].L].location+
143 stations[y].location-
144 stations[stations[x].L].location;
145 }
146 else
147 if(stations[x].type==2 && stations[y].type==1)
148 {
149 ret = stations[x].location-
150 stations[stations[x].L].location+
151 stations[stations[y].R].location-
CAPITOLUL 7. IOI 2014 838

152 stations[stations[x].L].location+
153 stations[stations[y].R].location-
154 stations[y].location;
155 }
156
157 return ret;
158 }
159
160 void getInput()
161 {
162 int g;
163 g = scanf("%d",&SUBTASK);
164 g = scanf("%d",&S);
165
166 int s;
167 for (s = 0; s < S; s++)
168 {
169 int type, location;
170 g = scanf(" %d %d",&type,&location);
171 stations[s].index = s;
172 stations[s].location = location;
173 stations[s].type = type;
174 stations[s].L = -1;
175 stations[s].R = -1;
176 }
177
178 qsort(stations, S, sizeof(STATION), cmp_fun_1);
179 now_I_want_to_getLR();
180 qsort(stations, S, sizeof(STATION), cmp_fun_2);
181 }
182
183 int serverGetStationNumber()
184 {
185 return S;
186 }
187
188 int serverGetSubtaskNumber()
189 {
190 return SUBTASK;
191 }
192
193 int serverGetFirstStationLocation()
194 {
195 return stations[0].location;
196 }
197
198 int main()
199 {
200 auto t1 = clock();
201
202 std::freopen("../tests/04-020.in", "r", stdin) ;
203 //std::freopen("rail.out", "w", stdout) ;
204
205 int i;
206 getInput();
207 cnt = 0;
208
209 int location[10005];
210 int type[10005];
211 int ok = 1;
212
213 auto t2 = clock();
214
215 findLocation(S, serverGetFirstStationLocation(),location, type);
216
217 auto t3 = clock();
218
219 if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;
220 if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;
221
222 for (i = 0; i < S; i++)
223 if(type[i]!=stations[i].type || location[i]!=stations[i].location)
224 ok = 0;
225
226 if(ok==0) printf("Incorrect\n\n");
227 else printf("Correct\n\n");
CAPITOLUL 7. IOI 2014 839

228
229 fclose(stdout);
230
231 auto t4 = clock();
232
233 // reset console output
234 freopen("CON", "w", stdout);
235
236 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
237 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
238 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
239
240 return 0;
241 }
242 // -------------- end grader ----------------------
243 /*
244 Correct
245
246 t2-t1 = 0.031
247 t3-t2 = 0.015
248 t4-t3 = 0.016
249
250 Process returned 0 (0x0) execution time : 0.094 s
251 Press any key to continue.
252 */

Listing 7.2.5: rail-173712.cpp


1 // https://oj.uz/submission/173712 87 ms 964 KB
2
3 #include "rail.h"
4 #include<bits/stdc++.h>
5
6 using namespace std;
7
8 vector<pair<int, int> >v;
9 set<int>C, D;
10
11 void findLocation(int n, int first, int l[], int t[])
12 {
13 l[0]=first; t[0]=1; //block 0, C station
14 for(int i=1; i<n; i++)
15 v.push_back({getDistance(0, i), i});
16 sort(v.begin(), v.end());
17 l[v[0].second]=first+v[0].first;
18 t[v[0].second]=2; //the first D station
19 C.insert(l[0]);
20 D.insert(l[v[0].second]);
21 int L=0, R=v[0].second;
22 for(int i=1; i<v.size(); i++)
23 {
24 int idx=v[i].second;
25 int lf=getDistance(idx, L);
26 int rg=getDistance(idx, R);
27 int lfcan=l[L]+lf, rgcan=l[R]-rg;
28 bool isC;
29 auto it1=--C.upper_bound(lfcan);
30 if(rg==lfcan-*it1+l[R]-*it1)
31 {
32 isC=false;
33 }
34 else
35 {
36 auto it2=D.upper_bound(rgcan);
37 if(it2!=D.end() && lf==*it2-rgcan+*it2-l[L])
38 {
39 isC=true;
40 }
41 else
42 {
43 if(v[i].first==2*l[v[0].second]-l[0]-rgcan)
44 isC=true;
45 else isC=false;
46 }
47 }
CAPITOLUL 7. IOI 2014 840

48
49 if(isC)
50 {
51 l[idx]=rgcan;
52 t[idx]=1;
53 C.insert(l[idx]);
54 if(l[L]>l[idx]) L=idx;
55 }
56 else
57 {
58 l[idx]=lfcan;
59 t[idx]=2;
60 D.insert(l[idx]);
61 if(l[R]<l[idx]) R=idx;
62 }
63 }
64 }
65
66 // ------------- begin grader ---------------------
67
68 typedef struct Station
69 {
70 int index;
71 int type;
72 int location;
73 int L,R;
74 } STATION;
75
76 long long cnt;
77 static int S,SUBTASK;
78 static STATION stations[10004];
79
80 int cmp_fun_1(const void *a,const void *b)
81 {
82 STATION c,d;
83 c = *(STATION*)(a);
84 d = *(STATION*)(b);
85 return c.location < d.location ? -1 : 1;
86 }
87
88 int cmp_fun_2(const void *a,const void *b)
89 {
90 STATION c,d;
91 c = *(STATION*)(a);
92 d = *(STATION*)(b);
93 return c.index < d.index ? -1 : 1;
94 }
95
96 void now_I_want_to_getLR()
97 {
98 int now = stations[S-1].index,i;
99 for(i=S-2;i>=0;i--)
100 {
101 stations[i].R = now;
102 if(stations[i].type==2) now = stations[i].index;
103 }
104 now = stations[0].index;
105 for(i=1;i<S;i++)
106 {
107 stations[i].L = now;
108 if(stations[i].type==1) now = stations[i].index;
109 }
110 }
111
112 int getDistance(int x,int y)
113 {
114 cnt++;
115 if(x==y) return 0;
116 if(x<0 || x>=S || y<0 || y>=S) return -1;
117 if(stations[x].location > stations[y].location)
118 {
119 int tmp = x;
120 x = y;
121 y = tmp;
122 }
123
CAPITOLUL 7. IOI 2014 841

124 int ret = 0;


125 if(stations[x].type==1 && stations[y].type==1)
126 {
127 ret = stations[stations[y].R].location-
128 stations[x].location+
129 stations[stations[y].R].location-
130 stations[y].location;
131 }
132 else
133 if(stations[x].type==1 && stations[y].type==2)
134 {
135 ret = stations[y].location-stations[x].location;
136 }
137 else
138 if(stations[x].type==2 && stations[y].type==2)
139 {
140 ret = stations[x].location-
141 stations[stations[x].L].location+
142 stations[y].location-
143 stations[stations[x].L].location;
144 }
145 else
146 if(stations[x].type==2 && stations[y].type==1)
147 {
148 ret = stations[x].location-
149 stations[stations[x].L].location+
150 stations[stations[y].R].location-
151 stations[stations[x].L].location+
152 stations[stations[y].R].location-
153 stations[y].location;
154 }
155
156 return ret;
157 }
158
159 void getInput()
160 {
161 int g;
162 g = scanf("%d",&SUBTASK);
163 g = scanf("%d",&S);
164
165 int s;
166 for (s = 0; s < S; s++)
167 {
168 int type, location;
169 g = scanf(" %d %d",&type,&location);
170 stations[s].index = s;
171 stations[s].location = location;
172 stations[s].type = type;
173 stations[s].L = -1;
174 stations[s].R = -1;
175 }
176
177 qsort(stations, S, sizeof(STATION), cmp_fun_1);
178 now_I_want_to_getLR();
179 qsort(stations, S, sizeof(STATION), cmp_fun_2);
180 }
181
182 int serverGetStationNumber()
183 {
184 return S;
185 }
186
187 int serverGetSubtaskNumber()
188 {
189 return SUBTASK;
190 }
191
192 int serverGetFirstStationLocation()
193 {
194 return stations[0].location;
195 }
196
197 int main()
198 {
199 auto t1 = clock();
CAPITOLUL 7. IOI 2014 842

200
201 std::freopen("../tests/04-020.in", "r", stdin) ;
202 //std::freopen("rail.out", "w", stdout) ;
203
204 int i;
205 getInput();
206 cnt = 0;
207
208 int location[10005];
209 int type[10005];
210 int ok = 1;
211
212 auto t2 = clock();
213
214 findLocation(S, serverGetFirstStationLocation(),location, type);
215
216 auto t3 = clock();
217
218 if(SUBTASK==3 && cnt>S*(S-1)) ok = 0;
219 if(SUBTASK==4 && cnt>3*(S-1)) ok = 0;
220
221 for (i = 0; i < S; i++)
222 if(type[i]!=stations[i].type || location[i]!=stations[i].location)
223 ok = 0;
224
225 if(ok==0) printf("Incorrect\n\n");
226 else printf("Correct\n\n");
227
228 fclose(stdout);
229
230 auto t4 = clock();
231
232 // reset console output
233 freopen("CON", "w", stdout);
234
235 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
236 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
237 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
238
239 return 0;
240 }
241 // -------------- end grader ----------------------
242 /*
243 Correct
244
245 t2-t1 = 0.015
246 t3-t2 = 0.016
247 t4-t3 = 0
248
249 Process returned 0 (0x0) execution time : 0.078 s
250 Press any key to continue.
251 */

7.2.3 *Rezolvare detaliat 

7.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 7. IOI 2014 843

Apoi, Jian-Jia parcurge k faze ad ugând sau sc zând c r mizi. Procesul de construire a pe-
retelui se încheie atunci când toate cele k faze sunt parcurse. La ecare 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 nal  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  ecare 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 ecare 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 ecare 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 ecare coloan  dup  parcurgerea tuturor fazelor.
Trebuie s  implementaµi funcµia buildWall.
ˆ buildWall(n, k, op, left, right, height, nalHeight)
 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 nal 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 7. IOI 2014 844

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 ³ier, numit wall.c, wall.cpp sau wall.pas. în acest ³ier se va
implementa funcµia descris  mai sus, utilizând antetul de mai jos. De asemenea veµi include un
³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

7.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 specic position, any cascade of operations applied on it
could be simplied 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
specic index):
CAPITOLUL 7. IOI 2014 845

ˆ 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 veried by simple induction.


With this observation we could now improve the update time. We instead use a segment tree
to maintain the nal 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 rst.
After all queries are processed, we simply scan each position and check (with log n complexity)
the operations applied on it, obtaining the nal 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 7. IOI 2014 846

7.3.2 Coduri surs 

Listing 7.3.1: wall-39307.cpp


1 // https://oj.uz/submission/39307 803 ms 80156 KB
2
3 #include "wall.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 #define oo 2000000000
9 const int N = 2000010;
10 pair<int,int> seg[4 * N];
11
12 void update(int s,int e,int idx,int l,int r,int val,bool b)
13 {
14 val = max(val,seg[idx].first);
15 val = min(val,seg[idx].second);
16 if(s > r || e < l) return;
17 if(s >= l && e <= r)
18 {
19 if(!b) seg[idx].first = val; else seg[idx].second = val;
20 return;
21 }
22 update(s,(s+e)/2,idx*2,l,r,val,b);
23 update((s+e)/2+1,e,idx*2+1,l,r,val,b);
24 }
25
26 void make(int s,int e,int idx,int cur,int *arr)
27 {
28 cur = max(cur,seg[idx].first);
29 cur = min(cur,seg[idx].second);
30 if(s == e)
31 {
32 arr[s] = cur;
33 return;
34 }
35 make(s,(s+e)/2,idx*2,cur,arr);
36 make((s+e)/2+1,e,idx*2+1,cur,arr);
37 }
38
39 void buildWall(int n, int k, int op[], int left[], int right[],
40 int height[], int finalHeight[])
41 {
42 for(int i=1;i<=4 * n;i++) seg[i] = make_pair(0,oo);
43 for(int i=k-1;i>=0;i--)
44 update(0,n-1,1,left[i],right[i],height[i],op[i]-1);
45 make(0,n-1,1,0,finalHeight);
46 return;
47 }
48
49 // ------------ begin grader ------------------
50 int main()
51 {
52 auto t1 = clock();
53
54 std::freopen("../tests/04-030.in", "r", stdin) ;
55 std::freopen("wall.out", "w", stdout) ;
56
57 int n;
58 int k;
59
60 int i, j;
61 int status = 0;
62
63 status = scanf("%d%d", &n, &k);
64 assert(status == 2);
65
66 int* op = (int*)calloc(sizeof(int), k);
67 int* left = (int*)calloc(sizeof(int), k);
68 int* right = (int*)calloc(sizeof(int), k);
69 int* height = (int*)calloc(sizeof(int), k);
70 int* finalHeight = (int*)calloc(sizeof(int), n);
CAPITOLUL 7. IOI 2014 847

71
72 for (i = 0; i < k; i++)
73 {
74 status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
75 assert(status == 4);
76 }
77
78 auto t2 = clock();
79
80 buildWall(n, k, op, left, right, height, finalHeight);
81
82 auto t3 = clock();
83
84 for (j = 0; j < n; j++)
85 printf("%d\n", finalHeight[j]);
86
87 fclose(stdout);
88
89 auto t4 = clock();
90
91 // reset console output
92 freopen("CON", "w", stdout);
93
94 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
95 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
96 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
97
98 return 0;
99 }
100
101 // ------------ end grader --------------------
102 /*
103 t2-t1 = 2.422
104 t3-t2 = 2.25
105 t4-t3 = 1.016
106
107 Process returned 0 (0x0) execution time : 5.750 s
108 Press any key to continue.
109
110 argc = 4
111 checker
112 ../tests/04-030.in
113 wall.out
114 ../tests/04-030.out
115 ----------------------
116 1
117 Correct
118
119 Process returned 0 (0x0) execution time : 5.255 s
120 Press any key to continue.
121 */

93939

Listing 7.3.2: wall-93939.cpp


1 // https://oj.uz/submission/93939 637 ms 69496 KB
2
3 #include "wall.h"
4 #include <bits/stdc++.h>
5 #define left_son (node<<1)
6 #define right_son ((node<<1)|1)
7 #define mid ((st+dr)>>1)
8
9 using namespace std;
10
11 const int lim = 1e5, Nmax = 2e6 + 5;
12
13 class SegTree
14 {
15 int a[Nmax<<2], b[Nmax<<2];
16
17 void combine(int node, int x, int y)
18 {
19 if(b[node] <= x) a[node] = b[node] = x;
20 else if(a[node] >= y) a[node] = b[node] = y;
CAPITOLUL 7. IOI 2014 848

21 else a[node] = max(a[node], x), b[node] = min(b[node], y);


22 }
23
24 void propag(int node)
25 {
26 combine(left_son, a[node], b[node]);
27 combine(right_son, a[node], b[node]);
28 a[node] = 0; b[node] = lim;
29 }
30
31 public:
32 void update(int node, int st, int dr, int L, int R, int A, int B)
33 {
34 if(L <= st && dr <= R)
35 {
36 combine(node, A, B);
37 return;
38 }
39
40 propag(node);
41
42 if(L <= mid) update(left_son, st, mid, L, R, A, B);
43 if(mid < R) update(right_son, mid+1, dr, L, R, A, B);
44 }
45
46 void propag(int node, int st, int dr, int *Ans)
47 {
48 if(st == dr)
49 {
50 Ans[st] = a[node];
51 return;
52 }
53 propag(node);
54 propag(left_son, st, mid, Ans);
55 propag(right_son, mid+1, dr, Ans);
56 }
57
58 void init(int node, int st, int dr)
59 {
60 a[node] = 0, b[node] = lim;
61 if(st == dr) return;
62 init(left_son, st, mid); init(right_son, mid+1, dr);
63 }
64 } aint;
65
66 void buildWall(int n, int k, int op[], int left[], int right[],
67 int height[], int finalHeight[])
68 {
69 aint.init(1, 0, n-1);
70 int i;
71 for(i=0; i<k; ++i)
72 aint.update(1, 0, n-1, left[i], right[i],
73 (op[i] == 1 ? height[i] : 0),
74 (op[i] == 1 ? lim : height[i]));
75
76 aint.propag(1, 0, n-1, finalHeight);
77 }
78
79 // ------------ begin grader ------------------
80
81 int main()
82 {
83 auto t1 = clock();
84
85 std::freopen("../tests/04-030.in", "r", stdin) ;
86 std::freopen("wall.out", "w", stdout) ;
87
88 int n;
89 int k;
90
91 int i, j;
92 int status = 0;
93
94 status = scanf("%d%d", &n, &k);
95 assert(status == 2);
96
CAPITOLUL 7. IOI 2014 849

97 int* op = (int*)calloc(sizeof(int), k);


98 int* left = (int*)calloc(sizeof(int), k);
99 int* right = (int*)calloc(sizeof(int), k);
100 int* height = (int*)calloc(sizeof(int), k);
101 int* finalHeight = (int*)calloc(sizeof(int), n);
102
103 for (i = 0; i < k; i++)
104 {
105 status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
106 assert(status == 4);
107 }
108
109 auto t2 = clock();
110
111 buildWall(n, k, op, left, right, height, finalHeight);
112
113 auto t3 = clock();
114
115 for (j = 0; j < n; j++)
116 printf("%d\n", finalHeight[j]);
117
118 fclose(stdout);
119
120 auto t4 = clock();
121
122 // reset console output
123 freopen("CON", "w", stdout);
124
125 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
126 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
127 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
128 return 0;
129 }
130
131 // ------------ end grader --------------------
132 /*
133 t2-t1 = 2.615
134 t3-t2 = 3.217
135 t4-t3 = 1.016
136
137 Process returned 0 (0x0) execution time : 6.895 s
138 Press any key to continue.
139
140 argc = 4
141 checker
142 ../tests/04-030.in
143 wall.out
144 ../tests/04-030.out
145 ----------------------
146 1
147 Correct
148
149 Process returned 0 (0x0) execution time : 5.221 s
150 Press any key to continue.
151 */

Listing 7.3.3: wall-173006.cpp


1 // https://oj.uz/submission/173006 649 ms 72884 KB
2
3 #include "wall.h"
4 #include <bits/stdc++.h>
5
6 #define adds 1
7 #define remov 2
8 #define left(n) (n<<1)
9 #define right(n) (n<<1|1)
10
11 using namespace std;
12
13 const int last = 1 << 21;
14 const int szt = 5e6;
15 const int maxn = 1<<21;
16 const int inf = 0x3f3f3f3f;
17
CAPITOLUL 7. IOI 2014 850

18 int ql, qr, state, val;


19
20 struct segtree
21 {
22 int mx[szt], mn[szt];
23
24 segtree()
25 {
26 memset(mx, inf, sizeof(mx));
27 }
28
29 void go(int node)
30 {
31 if(state == 2)
32 {
33 mx[node] = min(mx[node], val);
34 mn[node] = min(mn[node], val);
35 }
36 else
37 {
38 mx[node] = max(mx[node], val);
39 mn[node] = max(mn[node], val);
40 }
41 }
42
43 void merge(int par, int kid)
44 {
45 mx[kid] = min(mx[kid], mx[par]);
46 mx[kid] = max(mx[kid], mn[par]);
47 mn[kid] = max(mn[kid], mn[par]);
48 mn[kid] = min(mn[kid], mx[par]);
49 }
50
51 void unlazy(int node)
52 {
53 merge(node, left(node));
54 merge(node, right(node));
55 mx[node] = inf; mn[node] = 0;
56 }
57
58 void update(int node, int l, int r)
59 {
60 if(ql <= l && r <= qr)
61 {
62 go(node);
63 return;
64 }
65 unlazy(node);
66 int mid = (l + r) >> 1;
67 if(ql <= mid) update(left(node), l, mid);
68 if(qr > mid) update(right(node), mid + 1, r);
69 }
70
71 void finish()
72 {
73 for(int i = 1; i < last; i++)unlazy(i);
74 }
75 };
76
77 segtree tree;
78
79 void buildWall(int n, int k, int op[], int left[],
80 int right[], int height[], int ans[])
81 {
82 for(int i = 0; i < k; i++)
83 {
84 state = op[i], ql = left[i] + 1, qr = right[i] + 1, val = height[i];
85 tree.update(1, 1, last);
86 }
87
88 tree.finish();
89 int cnt = 0;
90 for(int i = last; i < last + n; i++)
91 {
92 ans[cnt++] = min(tree.mx[i], tree.mn[i]);
93 }
CAPITOLUL 7. IOI 2014 851

94 return;
95 }
96
97 // ------------ begin grader ------------------
98 int main()
99 {
100 auto t1 = clock();
101
102 std::freopen("../tests/04-030.in", "r", stdin) ;
103 std::freopen("wall.out", "w", stdout) ;
104
105 int n;
106 int k;
107
108 int i, j;
109 int status = 0;
110
111 status = scanf("%d%d", &n, &k);
112 assert(status == 2);
113
114 int* op = (int*)calloc(sizeof(int), k);
115 int* left = (int*)calloc(sizeof(int), k);
116 int* right = (int*)calloc(sizeof(int), k);
117 int* height = (int*)calloc(sizeof(int), k);
118 int* finalHeight = (int*)calloc(sizeof(int), n);
119
120 for (i = 0; i < k; i++)
121 {
122 status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
123 assert(status == 4);
124 }
125
126 auto t2 = clock();
127
128 buildWall(n, k, op, left, right, height, finalHeight);
129
130 auto t3 = clock();
131
132 for (j = 0; j < n; j++)
133 printf("%d\n", finalHeight[j]);
134
135 fclose(stdout);
136
137 auto t4 = clock();
138
139 // reset console output
140 freopen("CON", "w", stdout);
141
142 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
143 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
144 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
145 return 0;
146 }
147
148 // ------------ end grader --------------------
149 /*
150 t2-t1 = 2.688
151 t3-t2 = 4.484
152 t4-t3 = 1
153
154 Process returned 0 (0x0) execution time : 8.234 s
155 Press any key to continue.
156
157 argc = 4
158 checker
159 ../tests/04-030.in
160 wall.out
161 ../tests/04-030.out
162 ----------------------
163 1
164 Correct
165
166 Process returned 0 (0x0) execution time : 5.208 s
167 Press any key to continue.
168 */
CAPITOLUL 7. IOI 2014 852

Listing 7.3.4: wall-232336.cpp


1 // https://oj.uz/submission/232336 692 ms 60536 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <assert.h>
6
7 #include "wall.h"
8
9 #include<ctime>
10 #include<iostream>
11
12 using namespace std;
13
14 const int inf = 1e9, N = 1 << 22;
15 int mx[N], mn[N];
16
17 void change(int v, int d, int u)
18 {
19 mn[v] = max(u, min(d, mn[v]));
20 mx[v] = min(d, max(u, mx[v]));
21 }
22
23 void push(int v)
24 {
25 change(v << 1|0, mn[v], mx[v]);
26 change(v << 1|1, mn[v], mx[v]);
27 mx[v] = 0; mn[v] = inf;
28 }
29
30 int d, u, lo, hi;
31 void update(int v, int l, int r)
32 {
33 if (hi <= l || r <= lo) return;
34 if (lo <= l && r <= hi) return change(v, d, u);
35 push(v); int m = (l + r) / 2;
36 update(v << 1|0, l, m);
37 update(v << 1|1, m, r);
38 }
39
40 void final(int ans[], int v, int l, int r)
41 {
42 if (r - l == 1)
43 return void(ans[l] = min(mx[v], mn[v]));
44 push(v); int m = (l + r) / 2;
45 final(ans, v << 1|0, l, m);
46 final(ans, v << 1|1, m, r);
47 }
48
49 void buildWall(int n, int q, int op[], int l[], int r[], int h[], int ans[])
50 {
51 for (int i = 0; i < N; ++i) mn[i] = inf, mx[i] = 0;
52 for (int i = 0; i < q; ++i)
53 {
54 u = 0; d = inf;
55 lo = l[i]; hi = r[i] + 1;
56 op[i] != 1 ? d = h[i]: u = h[i];
57 update(1, 0, n);
58 }
59 final(ans, 1, 0, n);
60 }
61
62 // ------------ begin grader ------------------
63 int main()
64 {
65 auto t1 = clock();
66
67 std::freopen("../tests/04-030.in", "r", stdin) ;
68 std::freopen("wall.out", "w", stdout) ;
69
70 int n;
71 int k;
72
73 int i, j;
74 int status = 0;
CAPITOLUL 7. IOI 2014 853

75
76 status = scanf("%d%d", &n, &k);
77 assert(status == 2);
78
79 int* op = (int*)calloc(sizeof(int), k);
80 int* left = (int*)calloc(sizeof(int), k);
81 int* right = (int*)calloc(sizeof(int), k);
82 int* height = (int*)calloc(sizeof(int), k);
83 int* finalHeight = (int*)calloc(sizeof(int), n);
84
85 for (i = 0; i < k; i++)
86 {
87 status = scanf("%d%d%d%d", &op[i], &left[i], &right[i], &height[i]);
88 assert(status == 4);
89 }
90
91 auto t2 = clock();
92
93 buildWall(n, k, op, left, right, height, finalHeight);
94
95 auto t3 = clock();
96
97 for (j = 0; j < n; j++)
98 printf("%d\n", finalHeight[j]);
99
100 fclose(stdout);
101
102 auto t4 = clock();
103
104 // reset console output
105 freopen("CON", "w", stdout);
106
107 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
108 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
109 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
110
111 return 0;
112 }
113
114 // ------------ end grader --------------------
115 /*
116 t2-t1 = 0.593
117 t3-t2 = 3.922
118 t4-t3 = 0.813
119
120 Process returned 0 (0x0) execution time : 5.375 s
121 Press any key to continue.
122
123 argc = 4
124 checker
125 ../tests/04-030.in
126 wall.out
127 ../tests/04-030.out
128 ----------------------
129 1
130 Correct
131
132 Process returned 0 (0x0) execution time : 5.203 s
133 Press any key to continue.
134 */

Listing 7.3.5: checkerWall.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
CAPITOLUL 7. IOI 2014 854

13 (char*)"../tests/04-030.in",
14 (char*)"wall.out",
15 (char*)"../tests/04-030.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("wall", argc, argv);
24 compareRemainingLines();
25 }

7.3.3 *Rezolvare detaliat 

7.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 ecare
din urm toarele n  1 etape o persoan  este ad ugat  în reµea de o gazd , care trebuie s  e 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 ecare 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 ecare 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 presupune
alegerea unui grup de persoane din reµea. Deoarece prietenii de obicei au interese similare, e³an-
tionul 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
asociate 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  de-
vin  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 7. IOI 2014 855

Etapele 4 ³i 5 sunt de asemenea prezentate în tabelul de mai sus. Reµeaua nal  este prezen-
tat  în gura 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 ec rei etape ³i valoarea încrederii asociate ec 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 nedenite ³i nu trebuie
s  e 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 ³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 ³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 7. IOI 2014 856

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 a³a valoarea returnat  de f indSample.


Timp maxim de executare/test: 1.0 secunde
Memorie: total 16 MB

7.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 condence 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.
Dene 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 7. IOI 2014 857

 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 nal 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 condence equals to 1, the problem becomes nding 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, conicting with the denition
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Ó nd out maximum cardinality matching in a bipartite graph,
with complexity of O N E  or O N E , depending on dierent 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'. Howe-
ver, the condence 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 nally get the p; q  pair for the last person. The answer will be max p; q  of the last person.
Here we briey 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 7. IOI 2014 858

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

7.4.2 Coduri surs 

Listing 7.4.1: friend-16524.cpp


1 // https://oj.uz/submission/16524 26 ms 3036 KB
2 #include <cstdio>
3 #include <cassert>
4
5 #include<ctime>
6 #include<iostream>
7
8 #define __MAXSIZE__ 100002
9
10 #include "friend.h"
11 #include <algorithm>
12
13 using namespace std;
14
15 int a[100010],b[100010];
16 int findSample(int n,int *c,int *h,int *p)
17 {
18 int i;
19 for(i=0;i<n;++i)a[i]=c[i];
20 for(;--i;)
21 {
22 if(!p[i])
23 {
24 a[h[i]]+=b[i];
25 b[h[i]]+=max(a[i],b[i]);
26 }
27 else
28 if(p[i]==1)
29 {
30 a[h[i]]=max(a[h[i]]+max(a[i],b[i]),b[h[i]]+a[i]);
31 b[h[i]]+=b[i];
32 }
33 else
34 {
35 a[h[i]]=max(a[h[i]]+b[i],b[h[i]]+a[i]);
36 b[h[i]]+=b[i];
37 }
38 }
CAPITOLUL 7. IOI 2014 859

39 return max(a[0],b[0]);
40 }
41
42 // ----------- begin grader --------------------
43
44 // Confidence
45 int confidence[__MAXSIZE__];
46
47 // Host
48 int host[__MAXSIZE__];
49
50 // Protocol
51 int protocol[__MAXSIZE__];
52
53 // Main
54 int main(void)
55 {
56 auto t1 = clock();
57
58 std::freopen("../tests/06-015.in", "r", stdin) ;
59 std::freopen("friend.out", "w", stdout) ;
60
61 int n,i;
62
63 // Number of people
64 assert(scanf("%d",&n)==1);
65
66 // Confidence
67 for(i=0;i<n;i++)
68 assert(scanf("%d",&confidence[i])==1);
69
70 // Host and Protocol
71 for(i=1;i<n;i++)
72 assert(scanf("%d %d",&host[i],&protocol[i])==2);
73
74 auto t2 = clock();
75
76 // Answer
77 printf("%d\n",findSample(n,confidence,host,protocol));
78 auto t3 = clock();
79
80 fclose(stdout);
81
82 auto t4 = clock();
83
84 // reset console output
85 freopen("CON", "w", stdout);
86
87 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
88 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
89 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
90
91 return 0;
92 }
93
94 // ----------- end grader ----------------------
95 /*
96 t2-t1 = 0.359
97 t3-t2 = 0.016
98 t4-t3 = 0
99
100 Process returned 0 (0x0) execution time : 0.422 s
101 Press any key to continue.
102
103 rgc = 4
104 checker
105 ../tests/06-015.in
106 friend.out
107 ../tests/06-015.out
108 ----------------------
109 1
110 Correct
111
112 Process returned 0 (0x0) execution time : 0.047 s
113 Press any key to continue.
114 */
CAPITOLUL 7. IOI 2014 860

Listing 7.4.2: friend-115760.cpp


1 // https://oj.uz/submission/115760 31 ms 2688 KB
2
3 #include <cstdio>
4 #include <cassert>
5
6 #include<ctime>
7 #include<iostream>
8
9 #define __MAXSIZE__ 100002
10
11 using namespace std;
12
13 #include "friend.h"
14 #define max(a, b) ((a) > (b) ? (a) : (b))
15
16 int findSample(int n, int val[], int host[], int typ[])
17 {
18 int ans = 0;
19 for(int i = n - 1; i >= 1; --i)
20 {
21 int &A = val[host[i]], &B = val[i];
22 if(typ[i] == 0) ans += B, A = max(0, A - B);
23 if(typ[i] == 1) A += B;
24 if(typ[i] == 2) A = max(A, B);
25 }
26 return ans + val[0];
27 }
28
29 // ----------- begin grader --------------------
30
31 // Confidence
32 int confidence[__MAXSIZE__];
33
34 // Host
35 int host[__MAXSIZE__];
36
37 // Protocol
38 int protocol[__MAXSIZE__];
39
40 // Main
41 int main(void)
42 {
43 auto t1 = clock();
44
45 std::freopen("../tests/06-015.in", "r", stdin) ;
46 std::freopen("friend.out", "w", stdout) ;
47
48 int n,i;
49
50 // Number of people
51 assert(scanf("%d",&n)==1);
52
53 // Confidence
54 for(i=0;i<n;i++)
55 assert(scanf("%d",&confidence[i])==1);
56
57 // Host and Protocol
58 for(i=1;i<n;i++)
59 assert(scanf("%d %d",&host[i],&protocol[i])==2);
60
61 auto t2 = clock();
62
63 // Answer
64 printf("%d\n",findSample(n,confidence,host,protocol));
65 auto t3 = clock();
66
67 fclose(stdout);
68
69 auto t4 = clock();
70
71 // reset console output
72 freopen("CON", "w", stdout);
73
74 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 7. IOI 2014 861

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


76 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
77
78 return 0;
79 }
80
81 // ----------- end grader ----------------------
82 /*
83 t2-t1 = 0.374
84 t3-t2 = 0
85 t4-t3 = 0.016
86
87 Process returned 0 (0x0) execution time : 0.422 s
88 Press any key to continue.
89
90 argc = 4
91 checker
92 ../tests/06-015.in
93 friend.out
94 ../tests/06-015.out
95 ----------------------
96 1
97 Correct
98
99 Process returned 0 (0x0) execution time : 0.035 s
100 Press any key to continue.
101 */

Listing 7.4.3: friend-119597.cpp


1 // https://oj.uz/submission/119597 31 ms 2688 KB
2
3 //#include <cstdio>
4 //#include <cassert>
5
6 #define __MAXSIZE__ 100002
7 #include "friend.h"
8
9 #include <bits/stdc++.h>
10 using namespace std;
11 int findSample(int n,int c[],int h[],int p[])
12 {
13 int ans=0;
14 for(int i=n-1;i>0;i--)
15 {
16 if(p[i]==0)ans+=c[i],c[h[i]]=max(0,c[h[i]]-c[i]);
17 if(p[i]==1)c[h[i]]+=c[i];
18 if(p[i]==2)c[h[i]]=max(c[h[i]],c[i]);
19 }
20 return ans+c[0];
21 }
22
23 // ----------- begin grader --------------------
24
25 // Confidence
26 int confidence[__MAXSIZE__];
27
28 // Host
29 int host[__MAXSIZE__];
30
31 // Protocol
32 int protocol[__MAXSIZE__];
33
34 // Main
35 int main(void)
36 {
37 auto t1 = clock();
38
39 std::freopen("../tests/06-015.in", "r", stdin) ;
40 std::freopen("friend.out", "w", stdout) ;
41
42 int n,i;
43
44 // Number of people
45 assert(scanf("%d",&n)==1);
CAPITOLUL 7. IOI 2014 862

46
47 // Confidence
48 for(i=0;i<n;i++)
49 assert(scanf("%d",&confidence[i])==1);
50
51 // Host and Protocol
52 for(i=1;i<n;i++)
53 assert(scanf("%d %d",&host[i],&protocol[i])==2);
54
55 auto t2 = clock();
56
57 // Answer
58 printf("%d\n",findSample(n,confidence,host,protocol));
59 auto t3 = clock();
60
61 fclose(stdout);
62
63 auto t4 = clock();
64
65 // reset console output
66 freopen("CON", "w", stdout);
67
68 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
69 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
70 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
71
72 return 0;
73 }
74
75 // ----------- end grader ----------------------
76 /*
77 t2-t1 = 0.469
78 t3-t2 = 0
79 t4-t3 = 0
80
81 Process returned 0 (0x0) execution time : 0.516 s
82 Press any key to continue.
83
84 argc = 4
85 checker
86 ../tests/06-015.in
87 friend.out
88 ../tests/06-015.out
89 ----------------------
90 1
91 Correct
92
93 Process returned 0 (0x0) execution time : 0.031 s
94 Press any key to continue.
95 */

Listing 7.4.4: checkerFriend.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/06-015.in",
14 (char*)"friend.out",
15 (char*)"../tests/06-015.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
CAPITOLUL 7. IOI 2014 863

23 registerChecker("wall", argc, argv);


24 compareRemainingLines();
25 }

7.4.3 *Rezolvare detaliat 

7.5 Gondola
Problema 5 - Gondola 100 de puncte
Author: Michal Forisek. 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 xat . Dup  ce gondola cu num rul i trece prin staµie,
urm toarea gondol  care trece prin staµie va  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  defectat (³i s   fost înlocuite) înainte s   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 conguraµ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  observat .
Vericarea unei secvenµe de gondole
Pentru primele trei subprobleme (subtask-uri) trebuie s  vericaµ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 7. IOI 2014 864

Subproblemele 1, 2, 3
subproblem  puncte n inputSeq
1 5 n & 100 ecare 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  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 sucient 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  e 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 7. IOI 2014 865

 Dac  secvenµa dat  este o secvenµ  de gondole, atunci trebuie s  determinaµi num rul
(care poate  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 ³ier, numit gondola.c, gondola.cpp sau gondola.pas. Acest
³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 ³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 7. IOI 2014 866

7.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 - nd one solution - count all solutions) are closely related.
Subtasks 1-3
In subtask 1, once we see the rst 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 dierence is that subtask 4 allows its
inecient 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 dierent options do we have for its place? If the new
gondola is present in the nal 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
dierent 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 nd the original gondola it ultimately replaced, and
we sort these records according to the new gondola number.
CAPITOLUL 7. IOI 2014 867

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

7.5.2 Coduri surs 

Listing 7.5.1: gondola-7306.cpp


1 // https://oj.uz/submission/7306 24 ms 4412 KB
2 #include <stdio.h>
3 #include <assert.h>
4
5 #include<ctime>
6 #include<iostream>
7
8 #include "gondola.h"
9
10 int gondolaSequence[100001];
11 int replacementSequence[250001];
12
13 #include <algorithm>
14
15 #define MOD 1000000009
16
17 int arr[250001];
18 int now[250001];
19
20 int valid(int n, int inputSeq[])
21 {
22 int i,j;
23 for(i=0;i<n;i++)if(inputSeq[i]<=n)break;
24 if(i<n)
25 for(j=0;j<n;j++)
26 if(inputSeq[j]<=n&&(inputSeq[j]+i)%n!=(inputSeq[i]+j)%n)
27 return 0;
28
29 std::sort(inputSeq,inputSeq+n);
30
31 for(i=1;i<n;i++)
32 if(inputSeq[i-1]==inputSeq[i])
33 return 0;
34 return 1;
35 }
36
37 int replacement(int n, int gondolaSeq[], int replacementSeq[])
38 {
39 int i,j;
40 for(i=0;i<n;i++)arr[gondolaSeq[i]]=i+1;
41 for(i=0;i<n;i++)if(gondolaSeq[i]<=n)break;
42 for(j=0;j<n;j++)now[j]=i<n?(gondolaSeq[i]+j-i+n-1)%n+1:j+1;
43
44 j=0;
45 for(i=0;i<n;i++)if(gondolaSeq[i]>j)j=gondolaSeq[i];
46 for(i=n+1;i<=j;i++)
47 {
48 replacementSeq[i-n-1]=arr[i]>0?now[arr[i]-1]:now[arr[j]-1];
49 now[arr[i]>0?arr[i]-1:arr[j]-1]=i;
50 }
51
52 return j-n;
53 }
54
55 int f(int x,int y)
56 {
57 if(y==0||x==1)return 1;
58 if(y&1)return 1LL*x*f(x,y-1)%MOD;
59 x=f(x,y>>1);
CAPITOLUL 7. IOI 2014 868

60 return 1LL*x*x%MOD;
61 }
62
63 int countReplacement(int n, int inputSeq[])
64 {
65 if(!valid(n,inputSeq))return 0;
66 int i,r=1;
67
68 std::sort(inputSeq,inputSeq+n);
69
70 for(i=0;i<n;i++)
71 {
72 if(inputSeq[i]<=n)continue;
73 r=1LL*r*f(n-i,inputSeq[i]-(i>0?std::max(inputSeq[i-1],n):n)-1)%MOD;
74 }
75
76 return 1LL*r*(inputSeq[0]>n?n:1)%MOD;
77 }
78
79 // -------------- begin grader ------------------
80
81 int main()
82 {
83 auto t1 = clock();
84
85 std::freopen("../tests/10-018.in", "r", stdin) ;
86 std::freopen("gondola.out", "w", stdout) ;
87
88 int i, n, tag;
89 int nr;
90
91 assert(scanf("%d", &tag)==1);
92 assert(scanf("%d", &n)==1);
93
94 for(i=0;i< n;i++)
95 assert( scanf("%d", &gondolaSequence[i]) ==1);
96
97 auto t2 = clock();
98
99 switch (tag)
100 {
101 case 1: case 2: case 3:
102 printf("%d\n", valid(n, gondolaSequence));
103 break;
104
105 case 4: case 5: case 6:
106 nr = replacement(n, gondolaSequence, replacementSequence);
107 printf("%d ", nr);
108 if (nr > 0)
109 {
110 for (i=0; i<nr-1; i++)
111 printf("%d ", replacementSequence[i]);
112 printf("%d\n", replacementSequence[nr-1]);
113 }
114 else printf("\n");
115 break;
116
117 case 7: case 8: case 9: case 10:
118 printf("%d\n", countReplacement(n, gondolaSequence));
119 break;
120 }
121
122 auto t3 = clock();
123
124 fclose(stdout);
125
126 auto t4 = clock();
127
128 // reset console output
129 freopen("CON", "w", stdout);
130
131 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
132 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
133 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
134
135 return 0;
CAPITOLUL 7. IOI 2014 869

136 }
137 // -------------- end grader --------------------
138 /*
139 t2-t1 = 0.016
140 t3-t2 = 0.031
141 t4-t3 = 0
142
143 Process returned 0 (0x0) execution time : 1.141 s
144 Press any key to continue.
145 */

Listing 7.5.2: gondola-93790map.cpp


1 // https://oj.uz/submission/93790 101 ms 10892 KB
2
3 #include "gondola.h"
4 #include <iostream>
5 #include <map>
6 #include <algorithm>
7
8 #include <stdio.h>
9 #include <assert.h>
10
11 #include<ctime>
12
13 int gondolaSequence[100001];
14 int replacementSequence[250001];
15
16 using namespace std;
17
18 const int nmax=100005;
19 const long long mod=1000*1000*1000+9;
20
21 map<int,int> ap;
22
23 int v[nmax];
24 int i,act;
25
26 void permuta(int a[],int b[],int n)
27 {
28 int offset=0;
29 for(int i=0;i<n;i++)
30 if(a[i]<=n)
31 offset=i+1-a[i];
32 for(int i=0;i<n;i++)
33 b[i]=a[(i+offset+n)%n];
34 }
35
36 int valid(int n, int inputSeq[])
37 {
38 permuta(inputSeq,v,n);
39 for(int i=0;i<n;i++)
40 {
41 if(v[i]<=n&&v[i]!=i+1)
42 return 0;
43 if(ap[v[i]]) return 0;
44 ap[v[i]]=1;
45 }
46
47 return 1;
48 }
49
50 //----------------------
51
52 int replacement(int n, int gondolaSeq[], int replacementSeq[])
53 {
54 int l=0;
55 permuta(gondolaSeq,v,n);
56 int mx=0;
57 for(i=0;i<n;i++)
58 {
59 ap[v[i]]=i+1;
60 mx=max(mx,v[i]);
61 }
62 act=n;
CAPITOLUL 7. IOI 2014 870

63 for(i=n+1;i<=mx;i++)
64 if(ap[i])
65 {
66 replacementSeq[l++]=ap[i];
67 act++;
68 while(act<i)
69 {
70 replacementSeq[l++]=act;
71 act++;
72 }
73 }
74 return l;
75 }
76
77 //----------------------
78
79 long long expo(long long A,int B)
80 {
81 long long ret=1,p2=A;
82 for(int p=0;p<=30;p++)
83 {
84 if(((1<<p)&B))
85 ret=(1LL*ret*p2)%mod;
86 p2=(1LL*p2*p2)%mod;
87 }
88 return ret;
89 }
90
91 int countReplacement(int n, int inputSeq[])
92 {
93 long long f=0,ans=1;
94 if(!valid(n,inputSeq)) return 0;
95 bool ordered=1;
96 for(int i=1;i<=n;i++)
97 if(ap[i])
98 ordered=0;
99
100 sort(inputSeq,inputSeq+n);
101
102 for(i=n-2;i>=0;i--)
103 if(inputSeq[i]>n||inputSeq[i+1]>n)
104 {
105 f=n-1-i;if(inputSeq[i]<n) inputSeq[i]=n;
106 ans=(1LL*ans*expo(f,inputSeq[i+1]-inputSeq[i]-1))%mod;
107 }
108
109 if(ordered)
110 {
111 f=n;
112 ans=(1LL*ans*expo(f,inputSeq[0]-n-1))%mod;
113 f=n;
114 ans=(1LL*ans*f)%mod;
115 }
116 return ans;
117 }
118
119 // -------------- begin grader ------------------
120
121 int main()
122 {
123 auto t1 = clock();
124
125 std::freopen("../tests/10-018.in", "r", stdin) ;
126 std::freopen("gondola.out", "w", stdout) ;
127
128 int i, n, tag;
129 int nr;
130
131 assert(scanf("%d", &tag)==1);
132 assert(scanf("%d", &n)==1);
133
134 for(i=0;i< n;i++)
135 assert( scanf("%d", &gondolaSequence[i]) ==1);
136
137 auto t2 = clock();
138
CAPITOLUL 7. IOI 2014 871

139 switch (tag)


140 {
141 case 1: case 2: case 3:
142 printf("%d\n", valid(n, gondolaSequence));
143 break;
144
145 case 4: case 5: case 6:
146 nr = replacement(n, gondolaSequence, replacementSequence);
147 printf("%d ", nr);
148 if (nr > 0)
149 {
150 for (i=0; i<nr-1; i++)
151 printf("%d ", replacementSequence[i]);
152 printf("%d\n", replacementSequence[nr-1]);
153 }
154 else printf("\n");
155 break;
156
157 case 7: case 8: case 9: case 10:
158 printf("%d\n", countReplacement(n, gondolaSequence));
159 break;
160 }
161
162 auto t3 = clock();
163
164 fclose(stdout);
165
166 auto t4 = clock();
167
168 // reset console output
169 freopen("CON", "w", stdout);
170
171 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
172 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
173 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
174
175 return 0;
176 }
177 // -------------- end grader --------------------
178 /*
179 t2-t1 = 0.124
180 t3-t2 = 0.188
181 t4-t3 = 0.016
182
183 Process returned 0 (0x0) execution time : 0.453 s
184 Press any key to continue.
185 */

Listing 7.5.3: gondola-102776.cpp


1 // https://oj.uz/submission/102776 31 ms 2204 KB
2
3 int gondolaSequence[100001];
4 int replacementSequence[250001];
5
6 #include <bits/stdc++.h>
7 #include "gondola.h"
8
9 using namespace std;
10
11 const int md = (int) 1e9 + 9;
12
13 inline int mul(int x, int y)
14 {
15 return (int) ((long long) x * y % md);
16 }
17
18 inline int power(int x, int y)
19 {
20 int res = 1;
21 while (y)
22 {
23 if (y & 1)
24 {
25 res = mul(res, x);
CAPITOLUL 7. IOI 2014 872

26 }
27 x = mul(x, x);
28 y >>= 1;
29 }
30 return res;
31 }
32
33 int valid(int n, int inputSeq[])
34 {
35 vector<int> a(inputSeq, inputSeq + n);
36 sort(a.begin(), a.end());
37 if (unique(a.begin(), a.end()) != a.end())
38 {
39 return 0;
40 }
41
42 int p = min_element(inputSeq, inputSeq + n) - inputSeq;
43 for (int i = 0; i < n; ++i)
44 {
45 if (inputSeq[(p + i) % n] <= n &&
46 inputSeq[(p + i) % n] != inputSeq[p] + i)
47 {
48 return 0;
49 }
50 }
51
52 return 1;
53 }
54
55 int replacement(int n, int gondolaSeq[], int replacementSeq[])
56 {
57 int q = min_element(gondolaSeq, gondolaSeq + n) - gondolaSeq;
58 if (gondolaSeq[q] <= n)
59 {
60 q = (q - (gondolaSeq[q] - 1) + n) % n;
61 }
62
63 int p = max_element(gondolaSeq, gondolaSeq + n) - gondolaSeq;
64 int m = gondolaSeq[p];
65
66 vector<int> pos(m + 1, -1);
67 for (int i = 0; i < n; ++i)
68 {
69 pos[gondolaSeq[i]] = (i - q + n) % n + 1;
70 }
71
72 for (int i = n + 1; i <= m; ++i)
73 {
74 if (pos[i] != -1)
75 {
76 replacementSeq[i - n - 1] = pos[i];
77 }
78 else
79 {
80 replacementSeq[i - n - 1] = pos[m];
81 pos[m] = i;
82 }
83 }
84
85 return m - n;
86 }
87
88 int countReplacement(int n, int inputSeq[])
89 {
90 if (!valid(n, inputSeq))
91 {
92 return 0;
93 }
94
95 sort(inputSeq, inputSeq + n);
96
97 int ans = 1;
98 if (inputSeq[0] > n)
99 {
100 ans = n;
101 }
CAPITOLUL 7. IOI 2014 873

102
103 for (int i = 0; i < n; ++i)
104 {
105 if (inputSeq[i] > n)
106 {
107 ans = mul(ans,
108 power(n - i,
109 inputSeq[i]-max(n, i ? inputSeq[i-1] : n) - 1));
110 }
111 }
112
113 return ans;
114 }
115
116 // -------------- begin grader ------------------
117
118 int main()
119 {
120 auto t1 = clock();
121
122 std::freopen("../tests/10-018.in", "r", stdin) ;
123 std::freopen("gondola.out", "w", stdout) ;
124
125 int i, n, tag;
126 int nr;
127
128 assert(scanf("%d", &tag)==1);
129 assert(scanf("%d", &n)==1);
130
131 for(i=0;i< n;i++)
132 assert( scanf("%d", &gondolaSequence[i]) ==1);
133
134 auto t2 = clock();
135
136 switch (tag)
137 {
138 case 1: case 2: case 3:
139 printf("%d\n", valid(n, gondolaSequence));
140 break;
141
142 case 4: case 5: case 6:
143 nr = replacement(n, gondolaSequence, replacementSequence);
144 printf("%d ", nr);
145 if (nr > 0)
146 {
147 for (i=0; i<nr-1; i++)
148 printf("%d ", replacementSequence[i]);
149 printf("%d\n", replacementSequence[nr-1]);
150 }
151 else printf("\n");
152 break;
153
154 case 7: case 8: case 9: case 10:
155 printf("%d\n", countReplacement(n, gondolaSequence));
156 break;
157 }
158
159 auto t3 = clock();
160
161 fclose(stdout);
162
163 auto t4 = clock();
164
165 // reset console output
166 freopen("CON", "w", stdout);
167
168 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
169 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
170 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
171
172 return 0;
173 }
174 // -------------- end grader --------------------
175 /*
176 t2-t1 = 0.063
177 t3-t2 = 0.046
CAPITOLUL 7. IOI 2014 874

178 t4-t3 = 0
179
180 Process returned 0 (0x0) execution time : 1.844 s
181 Press any key to continue.
182 */

Listing 7.5.4: gondola-153281.cpp


1 // https://oj.uz/submission/153281 37 ms 2552 KB
2
3 int gondolaSequence[100001];
4 int replacementSequence[250001];
5
6 #include "gondola.h"
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 typedef long long ll;
12 typedef pair<int, int> pii;
13 typedef pair<ll, ll> pll;
14
15 const int MAXN = 1e5;
16 const ll MOD = 1e9+9;
17
18 int valid(int N, int *A)
19 {
20 int i, j;
21
22 vector<int> S(A, A+N);
23
24 sort(S.begin(), S.end());
25
26 S.erase(unique(S.begin(), S.end()), S.end());
27
28 if(S.size()!=N) return 0;
29
30 int flag=-1;
31 for(i=0; i<N; i++)
32 {
33 if(A[i]>N) continue;
34 int t=(A[i]-1-i+N)%N;
35
36 if(flag==-1) flag=t;
37 else
38 if(flag!=t) return 0;
39 }
40
41 return 1;
42 }
43
44 int replacement(int N, int *A, int *B)
45 {
46 int i, j, ret=0;
47
48 int flag=0;
49 vector<pii> V;
50 for(i=0; i<N; i++)
51 {
52 if(A[i]<=N)
53 {
54 flag=(A[i]-1-i+N)%N;
55 continue;
56 }
57 V.push_back({A[i], i});
58 }
59
60 for(i=0; i<N; i++) A[i]=(i+flag)%N+1;
61
62 sort(V.begin(), V.end());
63
64 int now=N+1;
65 for(i=0; i<V.size(); i++)
66 {
67 int p=V[i].first, q=V[i].second;
CAPITOLUL 7. IOI 2014 875

68 while(A[q]!=p)
69 {
70 B[ret++]=A[q];
71 A[q]=now++;
72 }
73 }
74 return ret;
75 }
76
77 ll mypow(ll x, ll y)
78 {
79 if(y==0) return 1;
80
81 if(y%2) return mypow(x, y-1)*x%MOD;
82
83 ll ret=mypow(x, y/2);
84 return ret*ret%MOD;
85 }
86
87 int countReplacement(int N, int *A)
88 {
89 int i, j; ll ans=1;
90 if(!valid(N, A)) return 0;
91
92 int flag=-1;
93 vector<int> V; V.push_back(N);
94 for(i=0; i<N; i++)
95 {
96 if(A[i]<=N) flag=0;
97 else V.push_back(A[i]);
98 }
99
100 sort(V.begin(), V.end());
101 for(i=1; i<V.size(); i++)
102 ans=ans*mypow(V.size()-i, V[i]-V[i-1]-1)%MOD;
103
104 if(flag==-1) ans=ans*N%MOD;
105 return ans;
106 }
107
108 // -------------- begin grader ------------------
109
110 int main()
111 {
112 auto t1 = clock();
113
114 std::freopen("../tests/10-018.in", "r", stdin) ;
115 std::freopen("gondola.out", "w", stdout) ;
116
117 int i, n, tag;
118 int nr;
119
120 assert(scanf("%d", &tag)==1);
121 assert(scanf("%d", &n)==1);
122
123 for(i=0;i< n;i++)
124 assert( scanf("%d", &gondolaSequence[i]) ==1);
125
126 auto t2 = clock();
127
128 switch (tag)
129 {
130 case 1: case 2: case 3:
131 printf("%d\n", valid(n, gondolaSequence));
132 break;
133
134 case 4: case 5: case 6:
135 nr = replacement(n, gondolaSequence, replacementSequence);
136 printf("%d ", nr);
137 if (nr > 0)
138 {
139 for (i=0; i<nr-1; i++)
140 printf("%d ", replacementSequence[i]);
141 printf("%d\n", replacementSequence[nr-1]);
142 }
143 else printf("\n");
CAPITOLUL 7. IOI 2014 876

144 break;
145
146 case 7: case 8: case 9: case 10:
147 printf("%d\n", countReplacement(n, gondolaSequence));
148 break;
149 }
150
151 auto t3 = clock();
152
153 fclose(stdout);
154
155 auto t4 = clock();
156
157 // reset console output
158 freopen("CON", "w", stdout);
159
160 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
161 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
162 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
163
164 return 0;
165 }
166 // -------------- end grader --------------------
167 /*
168 t2-t1 = 0.078
169 t3-t2 = 0.062
170 t4-t3 = 0
171
172 Process returned 0 (0x0) execution time : 1.625 s
173 Press any key to continue.
174 */

Listing 7.5.5: checkerGondola.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/10-018.in",
14 (char*)"gondola.out",
15 (char*)"../tests/10-018.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("gondola", argc, argv);
24 compareRemainingLines();
25 }

7.5.3 *Rezolvare detaliat 

7.6 Holiday
Problema 6 - Holiday 100 de puncte
Author: Jakub Lacki, Poland
CAPITOLUL 7. IOI 2014 877

Jian-Jia planic  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 ecare 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 a , 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 planice 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 ecare ora³ este nenegativ.
Constrângeri adiµionale:
CAPITOLUL 7. IOI 2014 878

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 ³ier, cu numele holiday.c, holiday.cpp sau holiday.pas.
Acest ³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  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 a³a valoarea returnat  de funcµia f indM axAttraction.


Timp maxim de executare/test: 5.0 secunde
Memorie: total 64 MB

7.6.1 Indicaµii de rezolvare

For ease of description, we will rst 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 xed 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 xed 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 ag indicating whether this leaf is active or not,
CAPITOLUL 7. IOI 2014 879

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 o
any leaf. It also takes O log n time to nd 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 nd 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 nd 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 rst nd 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 rst 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 o 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 rst move right to a city, then move left from this point, and then nally stop at a city
left of start. Or we rst move left to a city, then move right from this point, and then nally stop
at a city right of start. That is, we only change the direction once.
Without lost of generality, we assume the rst scenario. We rst use the immediately solution
to nd f t for all possible of values t. Then we also use the immediately solution to nd 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 rst 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.

7.6.2 Coduri surs 


CAPITOLUL 7. IOI 2014 880

Listing 7.6.1: holiday-120785.cpp


1 // https://oj.uz/submission/120785 306 ms 45448 KB
2
3 #include "holiday.h"
4 #include <bits/stdc++.h>
5
6 #define upmax(a,b) (a)=max((a),(b))
7 #define INFLL (0x3f3f3f3f3f3f3f3fll)
8
9 using namespace std;
10
11 typedef long long ll;
12
13 const int MAXN = 100055;
14
15 struct NOD
16 {
17 NOD() : sum(0), cnt(0), l(0), r(0) {}
18
19 ll sum;
20 int cnt, l, r;
21 } arr[MAXN*18]; int arrn;
22
23 int newNOD()
24 {
25 return ++arrn;
26 }
27
28 int pst[MAXN];
29
30 int A[MAXN], AO[MAXN], ARO[MAXN];
31
32 ll Ans;
33 int N, X, L;
34
35 void makeTree(int bf, int nw, int s, int e, int x, int r)
36 {
37 arr[nw].cnt = arr[bf].cnt + 1;
38 arr[nw].sum = arr[bf].sum + r;
39
40 if(s == e) return;
41
42 int m = (s+e) >> 1;
43 if(x <= m)
44 {
45 arr[nw].r = arr[bf].r;
46 arr[nw].l = newNOD();
47 makeTree(arr[bf].l, arr[nw].l, s, m, x, r);
48 }
49 else
50 {
51 arr[nw].l = arr[bf].l;
52 arr[nw].r = newNOD();
53 makeTree(arr[bf].r, arr[nw].r, m+1, e, x, r);
54 }
55 }
56
57 ll _get(int bf, int nw, int x)
58 {
59 if(!x) return 0;
60 if(arr[nw].cnt - arr[bf].cnt == x)
61 return arr[nw].sum - arr[bf].sum;
62 int lc = arr[arr[nw].l].cnt - arr[arr[bf].l].cnt;
63 if(x <= lc)
64 return _get(arr[bf].l, arr[nw].l, x);
65
66 return (arr[arr[nw].l].sum - arr[arr[bf].l].sum) +
67 _get(arr[bf].r, arr[nw].r, x-lc);
68 }
69
70 ll get(int s, int e, int x)
71 {
72 return _get(pst[s-1], pst[e], x);
73 }
74
CAPITOLUL 7. IOI 2014 881

75 void f(int s, int e, int p, int q)


76 {
77 if(s > e || p > q) return;
78 int m = (s+e) >> 1;
79 ll hc = -INFLL; int hi = -1;
80
81 for(int x = p, xe = min({N, (L+X+m)/2, q}); x <= xe; x++)
82 {
83 ll t = get(m, x, min(x-m+1, L-(x+x-m-X)));
84 if(t <= hc) continue;
85 hc = t;
86 hi = x;
87 }
88
89 upmax(Ans, hc);
90 f(s, m-1, p, hi);
91 f(m+1, e, hi, q);
92 }
93
94
95 ll getAns()
96 {
97 iota(AO, AO+N+1, 0);
98 sort(AO+1, AO+N+1, [&](int a, int b)
99 {
100 return A[a] > A[b];
101 });
102
103 for(int i = 1; i <= N; i++) ARO[AO[i]] = i;
104
105 for(int i = 1; i <= N; i++)
106 {
107 pst[i] = newNOD();
108 makeTree(pst[i-1], pst[i], 1, N, ARO[i], A[i]);
109 }
110
111 f(max(1, X-L), X, X, N);
112
113 arrn = 0;
114
115 X = N+1-X;
116 reverse(A+1, A+N+1);
117 iota(AO, AO+N+1, 0);
118 sort(AO+1, AO+N+1, [&](int a, int b) {return A[a] > A[b];});
119
120 for(int i = 1; i <= N; i++) ARO[AO[i]] = i;
121 for(int i = 1; i <= N; i++)
122 {
123 pst[i] = newNOD();
124 makeTree(pst[i-1], pst[i], 1, N, ARO[i], A[i]);
125 }
126
127 f(max(1, X-L), X, X, N);
128
129 return Ans;
130 }
131
132 long long int findMaxAttraction(int n, int start, int d, int attraction[])
133 {
134 ::N = n;
135 ::X = start + 1;
136 ::L = d;
137
138 for(int i = 0; i < n; i++)
139 ::A[i+1] = attraction[i];
140
141 return getAns();
142 }
143
144 // ----------- begin grader -------------
145
146 int main()
147 {
148 auto t1 = clock();
149
150 std::freopen("../tests/04-009.in", "r", stdin) ;
CAPITOLUL 7. IOI 2014 882

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


152
153 int n, start, d;
154 int attraction[100000];
155 int i, n_s;
156
157 n_s = scanf("%d %d %d", &n, &start, &d);
158 for (i = 0 ; i < n; ++i)
159 {
160 n_s = scanf("%d", &attraction[i]);
161 }
162
163 auto t2 = clock();
164
165 printf("%lld\n", findMaxAttraction(n, start, d, attraction));
166
167 auto t3 = clock();
168
169 fclose(stdout);
170
171 auto t4 = clock();
172
173 // reset console output
174 freopen("CON", "w", stdout);
175
176 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
177 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
178 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
179
180 return 0;
181 }
182
183 // ---------- end grader ----------------
184 /*
185 t2-t1 = 0.266
186 t3-t2 = 1.031
187 t4-t3 = 0
188
189 Process returned 0 (0x0) execution time : 1.375 s
190 Press any key to continue.
191 */

Listing 7.6.2: holiday-134641.cpp


1 // https://oj.uz/submission/134641 259 ms 49520 KB
2
3 #include "holiday.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 const int N = 1e5 + 2;
9
10 struct st
11 {
12 int l, r, cnt;
13 long long sum;
14 st()
15 {
16 l = r = cnt = sum = 0;
17 }
18 } t[N * 20];
19
20 int d, start, sz;
21 long long ans;
22 vector <int> vec;
23 int root[N], cn;
24
25 void upd(int ov, int v, int pos, int tl = 0, int tr = sz)
26 {
27 if(tl == tr)
28 {
29 t[v].sum = t[ov].sum + vec[tl];
30 t[v].cnt = t[ov].cnt + 1;
31 }
CAPITOLUL 7. IOI 2014 883

32 else
33 {
34 int tm = (tl + tr) >> 1;
35 if(pos <= tm)
36 {
37 if(!t[v].l) t[v].l = ++ cn;
38 t[v].r = t[ov].r;
39 upd(t[ov].l, t[v].l, pos, tl, tm);
40 }
41 else
42 {
43 if(!t[v].r) t[v].r = ++ cn;
44 t[v].l = t[ov].l;
45 upd(t[ov].r, t[v].r, pos, tm + 1, tr);
46 }
47
48 t[v].cnt = t[ t[v].l ].cnt + t[ t[v].r ].cnt;
49 t[v].sum = t[ t[v].l ].sum + t[ t[v].r ].sum;
50 }
51 }
52
53 long long get(int ov, int v, int cnt, int tl = 0, int tr = sz)
54 {
55 if(cnt <= 0) return 0ll;
56 if(tl == tr)
57 {
58 long long k = min(t[v].cnt - t[ov].cnt, cnt);
59 return k * 1ll * vec[tl];
60 }
61
62 int tm = (tl + tr) >> 1;
63 int k = t[ t[v].r ].cnt - t[ t[ov].r ].cnt;
64 if(k <= cnt)
65 return t[ t[v].r ].sum -
66 t[ t[ov].r ].sum +
67 get(t[ov].l, t[v].l, cnt - k, tl, tm);
68
69 return get(t[ov].r, t[v].r, cnt, tm + 1, tr);
70 }
71
72 void calc(int l, int r, int opl, int opr)
73 {
74 if(l > r) return ;
75 int md = (l + r) >> 1, opt = -1;
76 long long ret = -1;
77 for(int i = opl; i <= opr; i ++)
78 {
79 int lenA = (start - md), lenB = (i - start);
80 int cnt = d - lenA - lenB - min(lenA, lenB);
81 long long now = get(root[md], root[i + 1], cnt);
82 if(ret < now)
83 {
84 ret = now;
85 opt = i;
86 }
87 }
88
89 ans = max(ans, ret);
90 calc(l, md - 1, opl, opt);
91 calc(md + 1, r, opt, opr);
92 }
93
94 long long int findMaxAttraction(int n, int startS, int D, int ar[])
95 {
96 d = D;
97 start = startS;
98 vec.push_back(-2e9);
99 for(int i = 0; i < n; i ++)
100 {
101 vec.push_back(ar[i]);
102 }
103
104 sort(vec.begin(),vec.end());
105
106 vec.resize(unique(vec.begin(), vec.end()) - vec.begin());
107
CAPITOLUL 7. IOI 2014 884

108 sz = (int)vec.size() - 1;
109 for(int i = 0; i < n; i ++)
110 {
111 ar[i] = lower_bound(vec.begin(),vec.end(), ar[i])-vec.begin();
112 }
113
114 for(int i = 1; i <= n; i ++)
115 {
116 root[i] = ++ cn;
117 upd(root[i - 1], root[i], ar[i - 1]);
118 }
119
120 calc(0, start, start, n);
121
122 return ans;
123 }
124
125 // ----------- begin grader -------------
126
127 int main()
128 {
129 auto t1 = clock();
130
131 std::freopen("../tests/04-009.in", "r", stdin) ;
132 std::freopen("holiday.out", "w", stdout) ;
133
134 int n, start, d;
135 int attraction[100000];
136 int i, n_s;
137
138 n_s = scanf("%d %d %d", &n, &start, &d);
139 for (i = 0 ; i < n; ++i)
140 {
141 n_s = scanf("%d", &attraction[i]);
142 }
143
144 auto t2 = clock();
145
146 printf("%lld\n", findMaxAttraction(n, start, d, attraction));
147
148 auto t3 = clock();
149
150 fclose(stdout);
151
152 auto t4 = clock();
153
154 // reset console output
155 freopen("CON", "w", stdout);
156
157 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
158 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
159 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
160
161 return 0;
162 }
163
164 // ---------- end grader ----------------
165 /*
166 t2-t1 = 0.187
167 t3-t2 = 0.938
168 t4-t3 = 0
169
170 Process returned 0 (0x0) execution time : 1.234 s
171 Press any key to continue.
172 */

Listing 7.6.3: holiday-211797.cpp


1 // https://oj.uz/submission/211797 250 ms 43776 KB
2
3 #include <bits/stdc++.h>
4 #include "holiday.h"
5
6 using namespace std;
7
CAPITOLUL 7. IOI 2014 885

8 using ll=long long;


9
10 struct pst
11 {
12 struct T
13 {
14 ll x=0;
15 int cnt=0, l=0, r=0;
16 };
17
18 int N, id=0, td=0;
19 vector<T> A;
20 vector<int> D, X;
21
22 pst(vector<int>&& _): N(_.size()), A(1800000), D(N+1), X(move(_)) {}
23
24 void add(int x, int s, int e, int&i)
25 {
26 int p=i;
27 i=++id;
28 A[i]={A[p].x+X[x], A[p].cnt+1, A[p].l, A[p].r};
29 if(s==e) return;
30
31 int m=s+e>>1;
32 if(x<=m) add(x, s, m, A[i].l);
33 else add(x, m+1, e, A[i].r);
34 }
35
36 void add(int x)
37 {
38 td++;
39 add(x, 0, N-1, D[td]=D[td-1]);
40 }
41
42 ll sum(int k, int s, int e, int i, int j)
43 {
44 if(A[j].cnt-A[i].cnt<=k) return A[j].x-A[i].x;
45 if(s==e) return X[s]*ll(k);
46
47 int m=s+e>>1, n=A[A[j].r].cnt-A[A[i].r].cnt;
48 if(k<=n)
49 return sum(k, m+1, e, A[i].r, A[j].r);
50 else
51 return sum(k-n, s, m, A[i].l, A[j].l)+A[A[j].r].x-A[A[i].r].x;
52 }
53
54 ll sum(int l, int r, int k)
55 {
56 return sum(k, 0, N-1, D[l], D[r+1]);
57 }
58 };
59
60 ll findMaxAttraction(int N, int st, int M, int A[])
61 {
62 vector<int> X(A, A+N);
63
64 sort(begin(X), end(X));
65
66 for(int i=0; i<N; i++)
67 A[i]=lower_bound(begin(X), end(X), A[i])-begin(X);
68
69 pst t(move(X));
70 for(int i=0; i<N; i++) t.add(A[i]);
71
72 function <ll(int, int, int, int)>
73 f=[&](int s, int e, int l, int r)
74 {
75 if(s>e || l>r) return ll(0);
76
77 int m=s+e>>1;
78 pair<ll, int> n={-1, -1};
79
80 for(int i=l; i<=r; i++)
81 n=max(n, {t.sum(m, i, M+m-i-min(i-st, st-m)), i});
82
83 return max({n.first,
CAPITOLUL 7. IOI 2014 886

84 f(s, m-1, l, n.second),


85 f(m+1, e, n.second, r)});
86 };
87
88 return f(max(st-M, 0), st, st, min(st+M, N-1));
89 }
90
91 // ----------- begin grader -------------
92
93 int main()
94 {
95 auto t1 = clock();
96
97 std::freopen("../tests/04-009.in", "r", stdin) ;
98 std::freopen("holiday.out", "w", stdout) ;
99
100 int n, start, d;
101 int attraction[100000];
102 int i, n_s;
103
104 n_s = scanf("%d %d %d", &n, &start, &d);
105 for (i = 0 ; i < n; ++i)
106 {
107 n_s = scanf("%d", &attraction[i]);
108 }
109
110 auto t2 = clock();
111
112 printf("%lld\n", findMaxAttraction(n, start, d, attraction));
113
114 auto t3 = clock();
115
116 fclose(stdout);
117
118 auto t4 = clock();
119
120 // reset console output
121 freopen("CON", "w", stdout);
122
123 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
124 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
125 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
126
127 return 0;
128 }
129
130 // ---------- end grader ----------------
131 /*
132 t2-t1 = 0.203
133 t3-t2 = 1.875
134 t4-t3 = 0
135
136 Process returned 0 (0x0) execution time : 2.109 s
137 Press any key to continue.
138 */

Listing 7.6.4: holiday-229391.cpp


1 // https://oj.uz/submission/229391 283 ms 49776 KB
2
3 #include<bits/stdc++.h>
4 #include"holiday.h"
5
6 using namespace std;
7
8 #define ii pair <int, int>
9 #define app push_back
10 #define all(a) a.begin(), a.end()
11 #define bp __builtin_popcountll
12 #define ll long long
13 #define mp make_pair
14 #define f first
15 #define s second
16 #define Time (double)clock()/CLOCKS_PER_SEC
17
CAPITOLUL 7. IOI 2014 887

18 const int N = 1e5 + 7;


19 int start, hol, a[N];
20 vector <int> c;
21
22 struct Node
23 {
24 int cnt;
25 ll sum;
26 int l, r;
27 };
28
29 int ptr = 0;
30 const int V = N * 20;
31 Node d[V];
32 int t[N];
33
34 int newNode()
35 {
36 ++ptr;
37 return ptr - 1;
38 }
39
40 int build(int l, int r)
41 {
42 int t = newNode();
43 if (l == r)
44 return t;
45 int m = (l + r) >> 1;
46 d[t].l = build(l, m);
47 d[t].r = build(m + 1, r);
48 return t;
49 }
50
51 int add(int t, int l, int r, int i)
52 {
53 int ans = newNode();
54 if (l == r)
55 {
56 d[ans].cnt = d[t].cnt + 1;
57 d[ans].sum = d[t].sum + c[i];
58 return ans;
59 }
60
61 int m = (l + r) >> 1;
62 if (i <= m)
63 {
64 d[ans].l = add(d[t].l, l, m, i);
65 d[ans].r = d[t].r;
66 }
67 else
68 {
69 d[ans].l = d[t].l;
70 d[ans].r = add(d[t].r, m + 1, r, i);
71 }
72
73 d[ans].cnt = d[d[ans].l].cnt + d[d[ans].r].cnt;
74 d[ans].sum = d[d[ans].l].sum + d[d[ans].r].sum;
75
76 return ans;
77 }
78
79 ll sum(int tl, int tr, int l, int r, int k)
80 {
81 if (l == r)
82 return (ll)k * c[l];
83
84 int m = (l + r) >> 1;
85 int r_cnt = d[d[tr].r].cnt - d[d[tl].r].cnt;
86 if (k <= r_cnt)
87 return sum(d[tl].r, d[tr].r, m + 1, r, k);
88 else
89 return (d[d[tr].r].sum - d[d[tl].r].sum) +
90 sum(d[tl].l, d[tr].l, l, m, k - r_cnt);
91 }
92
93 ll ans = 0;
CAPITOLUL 7. IOI 2014 888

94 ll get(int l, int r)
95 {
96 int go = (start - l) + (r - start) + min(start - l, r - start);
97 if (hol <= go)
98 return 0;
99 int k = hol - go;
100
101 return sum(t[l], t[r + 1], 0, N, min(r - l + 1, k));
102 }
103
104 int get_opt(int r, int opt_l, int opt_r)
105 {
106 ll nn = 0, opt = opt_l;
107 for (int l = opt_l; l <= opt_r; ++l)
108 {
109 ll t = get(l, r);
110 if (t > nn)
111 {
112 nn = t;
113 opt = l;
114 }
115 }
116
117 ans = max(ans, nn);
118 return opt;
119 }
120
121 void solve(int l, int r, int opt_l, int opt_r)
122 {
123 if (r < l)
124 return;
125 int m = (l + r) >> 1;
126 int opt_m = get_opt(m, opt_l, opt_r);
127
128 solve(l, m - 1, opt_l, opt_m);
129 solve(m + 1, r, opt_m, opt_r);
130 }
131
132 long long int findMaxAttraction(int n, int start_, int hol_, int a_[])
133 {
134 for (int i = 0; i < V; ++i)
135 {
136 d[i].cnt = d[i].sum = 0;
137 d[i].l = d[i].r = -1;
138 }
139
140 start = start_;
141 hol = hol_;
142 for (int i = 0; i < n; ++i)
143 a[i] = a_[i];
144
145 for (int i = 0; i < n; ++i)
146 c.app(a[i]);
147
148 sort(all(c));
149 c.resize(unique(all(c)) - c.begin());
150
151 for (int i = 0; i < n; ++i)
152 a[i] = lower_bound(all(c), a[i]) - c.begin();
153
154 t[0] = build(0, N);
155 for (int i = 0; i < n; ++i)
156 t[i + 1] = add(t[i], 0, N, a[i]);
157
158 solve(start, n - 1, 0, start);
159 return ans;
160 }
161
162 // ----------- begin grader -------------
163
164 int main()
165 {
166 auto t1 = clock();
167
168 std::freopen("../tests/04-009.in", "r", stdin) ;
169 std::freopen("holiday.out", "w", stdout) ;
CAPITOLUL 7. IOI 2014 889

170
171 int n, start, d;
172 int attraction[100000];
173 int i, n_s;
174
175 n_s = scanf("%d %d %d", &n, &start, &d);
176 for (i = 0 ; i < n; ++i)
177 {
178 n_s = scanf("%d", &attraction[i]);
179 }
180
181 auto t2 = clock();
182
183 printf("%lld\n", findMaxAttraction(n, start, d, attraction));
184
185 auto t3 = clock();
186
187 fclose(stdout);
188
189 auto t4 = clock();
190
191 // reset console output
192 freopen("CON", "w", stdout);
193
194 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
195 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
196 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
197
198 return 0;
199 }
200
201 // ---------- end grader ----------------
202 /*
203 t2-t1 = 0.187
204 t3-t2 = 1.156
205 t4-t3 = 0
206
207 Process returned 0 (0x0) execution time : 1.391 s
208 Press any key to continue.
209 */

Listing 7.6.5: checkerHoliday.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/04-009.in",
14 (char*)"holiday.out",
15 (char*)"../tests/04-009.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23
24 registerChecker("holiday", argc, argv);
25 compareRemainingLines();
26 }
CAPITOLUL 7. IOI 2014 890

7.6.3 *Rezolvare detaliat 


Capitolul 8

IOI 201333

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


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

891
CAPITOLUL 8. IOI 2013 892

Dat  ind 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 ecare stil. Nou  imagini din ecare 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  date programului t u în timpul
grad rii.
Imaginea va  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 ec 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 ³ier ce implementeaz  functia travelTime(), dup  cum ur-
meaz :
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 ecare pixel al imaginii.
ˆ G : O matrice de m rime H  W ce conµine cantitatea de verde din ecare pixel al imaginii.
ˆ B : O matrice de m rime H  W ce conµine cantitatea de albastru din ecare pixel al
imaginii.
ˆ Returneaz : Stilului imaginii, care poate  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  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  calculat pe baza num -
rului de imagini pe care programul vostru le clasic  corect.
Presupunând c  veµi clasica corect un procent de P imagini (deci 0 & P & 100):
CAPITOLUL 8. IOI 2013 893

ˆ 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 ³ierul artclass.jpg. Acest ³ier
trebuie sa conµin  o imagine în format JPEG.
V  este permis s  folosiµi orice aplicaµie de procesare grac  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  deniµ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

8.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 nd 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 frequ-
encies.
For the downsample and upsample I took a simple box lter. 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 dierences from the means, with the dierences
scaled using the standard deviations. It's not 100% accurate, but good enough to get a perfect
score.

8.1.2 *Coduri surs 

8.1.3 *Rezolvare detaliat 

8.2 Cave
Problema 2 - Cave 100 de puncte
Authors: Amaury Pouly and Arthur Charguéraud (France)
CAPITOLUL 8. IOI 2013 894

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, aate una în spatele alteia ³i N întrerup toare,
ecare întrerup tor conectat la o u³  diferit .

U³ile sunt numerotate 0, 1, ..., N  1 în ordine, u³a 0 ind cea mai apropiat  de voi.
Întrerup toarele sunt de asemenea numerotate 0, 1, ..., N  1, dar nu cunoa³teµi care întreru-
p tor este conectat la care u³ .
Întrerup toarele se a  la intrarea în pe³ter . Fiecare întrerup tor poate  în poziµia up sau
în poziµia down. Numai una din aceste poziµii este corect  pentru ecare î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 a  în poziµia gre³ita atunci u³a la care este conectat nu se va deschide. Poziµia
corect  poate  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 a  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 ecare întrerup tor ³i cu care u³  este conectat
ecare întrerup tor.
Implementare
Va trebui s  submitaµi un ³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  apelat  de maxim 70,000 de ori.
Parametrii
ˆ S : Un array de dimensiune N , indicând poziµia ecarui întrerup tor. Elementul S i cores-
punde 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 8. IOI 2013 895

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 identicat o combinaµie de întrerup toare care s 
deschid  toate u³ile, ³i u³a la care este conectat ecare întrerup tor.
Parametrii
ˆ S : Un array de lungime N , indicând poziµia corect  a ec 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 ecare î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 ec rui întrerup tor, ³i u³a la care este conectat ecare î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 întreru-
p 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 8. IOI 2013 896

Subtask-uri
Subtask Punctaj Constrângeri adiµionale
1 12 Pentru ecare 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  tot timpul [0, 0, 0, ..., 0].
Task-ul vostru este s  determinaµi cu ce u³  este conectat ecare în-
trerup 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 ³ierul cave.in, care trebuie
s  e î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  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  deniµ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

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

8.2.2 Coduri surs 


CAPITOLUL 8. IOI 2013 897

Listing 8.2.1: cave-1749.cpp


1 // https://oj.uz/submission/1749 284 ms 896 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include<iostream>
7 #include<ctime>
8
9 using namespace std;
10
11 int tryCombination(int S[]);
12 void answer(int S[], int D[]);
13
14 // -------- begin exploreCave(int N) -----------------
15
16 //#include "cave.h"
17
18 int w[14][5000],F[5000];
19 void exploreCave(int N)
20 {
21 int i,j,t,c,ck;
22 for(i=0;i<14;i++)
23 {
24 for(j=0;j<N;j++)
25 {
26 if((1<<i)&j)w[i][j]=1;
27 }
28 }
29
30 for(i=0;i<N;i++)
31 {
32 c=0;
33 t=tryCombination(w[13]);
34 ck=(t==i);
35 for(j=0;j<13;j++)
36 {
37 t=tryCombination(w[j]);
38 if(t==i && !ck)c+=1<<j;
39 if(t!=i && ck)c+=1<<j;
40 }
41 for(j=0;j<14;j++)
42 w[j][c]=ck;
43 F[c]=i;
44 }
45
46 answer(w[0],F);
47 }
48
49 // -------- end exploreCave(int N) -----------------
50
51 #define MAX_N 5000
52 #define MAX_CALLS 70000
53
54 #define fail(s, x...) do { \
55 fprintf(stderr, s "\n", ## x); \
56 exit(1); \
57 } while(0)
58
59 /* Symbol obfuscation */
60 #define N koala
61 #define realS kangaroo
62 #define realD possum
63 #define inv platypus
64 #define num_calls echidna
65
66 static int N;
67 static int realS[MAX_N];
68 static int realD[MAX_N];
69 static int inv[MAX_N];
70 static int num_calls;
71
72 void answer(int S[], int D[])
73 {
74 int i;
CAPITOLUL 8. IOI 2013 898

75 int correct = 1;
76 for (i = 0; i < N; ++i)
77 if (S[i] != realS[i] || D[i] != realD[i])
78 {
79 correct = 0;
80 break;
81 }
82
83 for (i = 0; i < N; ++i)
84 {
85 if (i > 0)
86 printf(" ");
87 printf("%d", S[i]);
88 }
89 printf("\n");
90
91 for (i = 0; i < N; ++i)
92 {
93 if (i > 0)
94 printf(" ");
95 printf("%d", D[i]);
96 }
97 printf("\n");
98
99 //exit(0);
100 }
101
102 int tryCombination(int S[])
103 {
104 int i;
105
106 if (num_calls >= MAX_CALLS)
107 {
108 printf("INCORRECT\nToo many calls to tryCombination().\n");
109 exit(0);
110 }
111 ++num_calls;
112
113 for (i = 0; i < N; ++i)
114 if (S[inv[i]] != realS[inv[i]])
115 return i;
116 return -1;
117 }
118
119 int init()
120 {
121 int i, res;
122
123 res = scanf("%d", &N);
124
125 for (i = 0; i < N; ++i)
126 {
127 res = scanf("%d", &realS[i]);
128 }
129 for (i = 0; i < N; ++i)
130 {
131 res = scanf("%d", &realD[i]);
132 inv[realD[i]] = i;
133 }
134
135 num_calls = 0;
136 return N;
137 }
138
139 int main()
140 {
141 auto t1 = clock();
142
143 std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);
144 std::freopen("cave.out", "w", stdout);
145
146 int N;
147
148 N = init();
149
150 auto t2 = clock();
CAPITOLUL 8. IOI 2013 899

151
152 exploreCave(N);
153
154 auto t3 = clock();
155
156 auto t4 = clock();
157
158 // reset console output
159 freopen("CON", "w", stdout);
160
161 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
162 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
164
165 return 0;
166 }
167 /*
168 t2-t1 = 0.053
169 t3-t2 = 1.487
170 t4-t3 = 0
171
172 Process returned 0 (0x0) execution time : 1.650 s
173 Press any key to continue.
174 */

Listing 8.2.2: cave-16655.cpp


1 // https://oj.uz/submission/16655 331 ms 612 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include<iostream>
7 #include<ctime>
8
9 using namespace std;
10
11 int tryCombination(int S[]);
12 void answer(int S[], int D[]);
13
14 // -------- begin exploreCave(int N) -----------------
15
16 int a[5010],b[5010],chk[5010];
17 void exploreCave(int N)
18 {
19 int i,j,s,e,m,t,p;
20 for(i=0;i<N;++i)
21 {
22 t=tryCombination(a);
23 s=0,e=N-1;
24 while(s<e)
25 {
26 m=(s+e)/2;
27 for(j=s;j<=m;++j)
28 if(!chk[j])
29 a[j]=!a[j];
30
31 p=t,t=tryCombination(a);
32
33 (p==i)^(t==i) ? e=m : s=m+1;
34 }
35
36 if(t==i)
37 a[s]=!a[s];
38 b[s]=i,chk[s]=1;
39 }
40
41 answer(a,b);
42 }
43
44 // -------- end exploreCave(int N) -----------------
45
46 #define MAX_N 5000
47 #define MAX_CALLS 70000
48
CAPITOLUL 8. IOI 2013 900

49 #define fail(s, x...) do { \


50 fprintf(stderr, s "\n", ## x); \
51 exit(1); \
52 } while(0)
53
54 /* Symbol obfuscation */
55 #define N koala
56 #define realS kangaroo
57 #define realD possum
58 #define inv platypus
59 #define num_calls echidna
60
61 static int N;
62 static int realS[MAX_N];
63 static int realD[MAX_N];
64 static int inv[MAX_N];
65 static int num_calls;
66
67 void answer(int S[], int D[])
68 {
69 int i;
70 int correct = 1;
71 for (i = 0; i < N; ++i)
72 if (S[i] != realS[i] || D[i] != realD[i])
73 {
74 correct = 0;
75 break;
76 }
77
78 for (i = 0; i < N; ++i)
79 {
80 if (i > 0)
81 printf(" ");
82 printf("%d", S[i]);
83 }
84 printf("\n");
85
86 for (i = 0; i < N; ++i)
87 {
88 if (i > 0)
89 printf(" ");
90 printf("%d", D[i]);
91 }
92 printf("\n");
93
94 //exit(0);
95 }
96
97 int tryCombination(int S[])
98 {
99 int i;
100
101 if (num_calls >= MAX_CALLS)
102 {
103 printf("INCORRECT\nToo many calls to tryCombination().\n");
104 exit(0);
105 }
106 ++num_calls;
107
108 for (i = 0; i < N; ++i)
109 if (S[inv[i]] != realS[inv[i]])
110 return i;
111 return -1;
112 }
113
114 int init()
115 {
116 int i, res;
117
118 res = scanf("%d", &N);
119
120 for (i = 0; i < N; ++i)
121 {
122 res = scanf("%d", &realS[i]);
123 }
124 for (i = 0; i < N; ++i)
CAPITOLUL 8. IOI 2013 901

125 {
126 res = scanf("%d", &realD[i]);
127 inv[realD[i]] = i;
128 }
129
130 num_calls = 0;
131 return N;
132 }
133
134 int main()
135 {
136 auto t1 = clock();
137
138 std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);
139 std::freopen("cave.out", "w", stdout);
140
141 int N;
142
143 N = init();
144
145 auto t2 = clock();
146
147 exploreCave(N);
148
149 auto t3 = clock();
150
151 auto t4 = clock();
152
153 // reset console output
154 freopen("CON", "w", stdout);
155
156 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
157 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
158 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
159
160 return 0;
161 }
162 /*
163 t2-t1 = 0
164 t3-t2 = 1.791
165 t4-t3 = 0
166
167 Process returned 0 (0x0) execution time : 1.899 s
168 Press any key to continue.
169 */

Listing 8.2.3: cave-170339.cpp


1 // https://oj.uz/submission/170339 275 ms 632 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include<iostream>
7 #include<ctime>
8
9 #include <bits/stdc++.h>
10
11 using namespace std;
12
13 int tryCombination(int S[]);
14 void answer(int S[], int D[]);
15
16 // -------- begin exploreCave(int N) -----------------
17
18 int s[5005], d[5005], res;
19 bool c[5005], pr, dw;
20
21 void exploreCave(int N)
22 {
23 memset(d, -1, sizeof(d));
24 res = tryCombination(s);
25 for(int i = 0; i < N; i++)
26 {
27 if(res == -1 or res > i) pr = true;
CAPITOLUL 8. IOI 2013 902

28 else pr = false;
29 int maxi = N, mini = -1;
30 while(maxi - mini > 1)
31 {
32 int medi = (maxi + mini) / 2;
33 for (int j = mini + 1; j <= medi; j++)
34 {
35 if (c[j]) continue;
36 s[j] = !s[j];
37 }
38 res = tryCombination(s);
39 if (res == -1 or res > i) dw = true;
40 else dw = false;
41 if (dw == pr) mini = medi;
42 else maxi = medi;
43 pr = dw;
44 }
45
46 d[maxi] = i;
47 c[maxi] = true;
48 if(!dw)
49 {
50 s[maxi] = !s[maxi];
51 res = tryCombination(s);
52 }
53 }
54 answer(s, d);
55 }
56
57 // -------- end exploreCave(int N) -----------------
58
59 #define MAX_N 5000
60 #define MAX_CALLS 70000
61
62 #define fail(s, x...) do { \
63 fprintf(stderr, s "\n", ## x); \
64 exit(1); \
65 } while(0)
66
67 /* Symbol obfuscation */
68 #define N koala
69 #define realS kangaroo
70 #define realD possum
71 #define inv platypus
72 #define num_calls echidna
73
74 static int N;
75 static int realS[MAX_N];
76 static int realD[MAX_N];
77 static int inv[MAX_N];
78 static int num_calls;
79
80 void answer(int S[], int D[])
81 {
82 int i;
83 int correct = 1;
84 for (i = 0; i < N; ++i)
85 if (S[i] != realS[i] || D[i] != realD[i])
86 {
87 correct = 0;
88 break;
89 }
90
91 for (i = 0; i < N; ++i)
92 {
93 if (i > 0)
94 printf(" ");
95 printf("%d", S[i]);
96 }
97 printf("\n");
98
99 for (i = 0; i < N; ++i)
100 {
101 if (i > 0)
102 printf(" ");
103 printf("%d", D[i]);
CAPITOLUL 8. IOI 2013 903

104 }
105 printf("\n");
106
107 //exit(0);
108 }
109
110 int tryCombination(int S[])
111 {
112 int i;
113
114 if (num_calls >= MAX_CALLS)
115 {
116 printf("INCORRECT\nToo many calls to tryCombination().\n");
117 exit(0);
118 }
119 ++num_calls;
120
121 for (i = 0; i < N; ++i)
122 if (S[inv[i]] != realS[inv[i]])
123 return i;
124 return -1;
125 }
126
127 int init()
128 {
129 int i, res;
130
131 res = scanf("%d", &N);
132
133 for (i = 0; i < N; ++i)
134 {
135 res = scanf("%d", &realS[i]);
136 }
137 for (i = 0; i < N; ++i)
138 {
139 res = scanf("%d", &realD[i]);
140 inv[realD[i]] = i;
141 }
142
143 num_calls = 0;
144 return N;
145 }
146
147 int main()
148 {
149 auto t1 = clock();
150
151 std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);
152 std::freopen("cave.out", "w", stdout);
153
154 int N;
155
156 N = init();
157
158 auto t2 = clock();
159
160 exploreCave(N);
161
162 auto t3 = clock();
163
164 auto t4 = clock();
165
166 // reset console output
167 freopen("CON", "w", stdout);
168
169 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
170 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
171 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
172
173 return 0;
174 }
175 /*
176 t2-t1 = 0
177 t3-t2 = 1.671
178 t4-t3 = 0
179
CAPITOLUL 8. IOI 2013 904

180 Process returned 0 (0x0) execution time : 1.781 s


181 Press any key to continue.
182 */

Listing 8.2.4: cave-231414.cpp


1 // https://oj.uz/submission/231414 244 ms 640 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include<iostream>
7 #include<ctime>
8
9 #include <bits/stdc++.h>
10
11 using namespace std;
12
13 int tryCombination(int S[]);
14 void answer(int S[], int D[]);
15
16 // -------- begin exploreCave(int N) -----------------
17
18 void exploreCave(int N)
19 {
20 int n=N;
21
22 int R[n+10];
23 int P[n+10];
24 int aux2[n+10];
25
26 int x,y;
27 int z=1;
28 for(int i=0;i<n;i++)
29 {
30 R[i]=1;
31 P[i]=-1;
32 }
33 for(int i=0;i<n;i++)
34 {
35 for(int l=0;l<n;l++){aux2[l]=R[l];}
36
37 int p1=0,p2=n-1;
38 x=tryCombination(aux2);
39 z=0;
40 while(p1!=p2)
41 {
42 int p3=(p1+p2)/2;
43 if(z==1)
44 {
45 for(int l=p3+1;l<=p2;l++)
46 {
47 if(P[l]==-1)
48 {
49 aux2[l]=1;
50 }
51 }
52 }
53 else
54 {
55 for(int l=p1;l<=p3;l++)
56 {
57 if(P[l]==-1)
58 {
59 aux2[l]=0;
60 }
61 }
62 }
63
64 y=tryCombination(aux2);
65
66 if(z==1)
67 {
68 if((x>=i+1 || x==-1) && (y>=i+1 || y==-1)){p2=p3;z=1;}
69 else
CAPITOLUL 8. IOI 2013 905

70 if((x<i+1 && x!=-1) && (y<i+1 && y!=-1)){p2=p3;z=1;}


71 else{p1=p3+1;z=0;}
72 }
73 else
74 {
75 if((x>=i+1 || x==-1) && (y>=i+1 || y==-1)){p1=p3+1;z=0;}
76 else
77 if((x<i+1 && x!=-1) && (y<i+1 && y!=-1)){p1=p3+1;z=0;}
78 else{p2=p3;z=1;}
79 }
80
81 x=y;
82 }
83
84 if(x>=i+1 || x==-1) {R[p1]=aux2[p1];P[p1]=i;}
85 else
86 {
87 if(aux2[p1]==1) {aux2[p1]=0;}
88 else {aux2[p1]=1;}
89
90 R[p1]=aux2[p1];P[p1]=i;
91 }
92 }
93 answer(R,P);
94 }
95
96 // -------- end exploreCave(int N) -----------------
97
98 #define MAX_N 5000
99 #define MAX_CALLS 70000
100
101 #define fail(s, x...) do { \
102 fprintf(stderr, s "\n", ## x); \
103 exit(1); \
104 } while(0)
105
106 /* Symbol obfuscation */
107 #define N koala
108 #define realS kangaroo
109 #define realD possum
110 #define inv platypus
111 #define num_calls echidna
112
113 static int N;
114 static int realS[MAX_N];
115 static int realD[MAX_N];
116 static int inv[MAX_N];
117 static int num_calls;
118
119 void answer(int S[], int D[])
120 {
121 int i;
122 int correct = 1;
123 for (i = 0; i < N; ++i)
124 if (S[i] != realS[i] || D[i] != realD[i])
125 {
126 correct = 0;
127 break;
128 }
129
130 for (i = 0; i < N; ++i)
131 {
132 if (i > 0)
133 printf(" ");
134 printf("%d", S[i]);
135 }
136 printf("\n");
137
138 for (i = 0; i < N; ++i)
139 {
140 if (i > 0)
141 printf(" ");
142 printf("%d", D[i]);
143 }
144 printf("\n");
145
CAPITOLUL 8. IOI 2013 906

146 //exit(0);
147 }
148
149 int tryCombination(int S[])
150 {
151 int i;
152
153 if (num_calls >= MAX_CALLS)
154 {
155 printf("INCORRECT\nToo many calls to tryCombination().\n");
156 exit(0);
157 }
158 ++num_calls;
159
160 for (i = 0; i < N; ++i)
161 if (S[inv[i]] != realS[inv[i]])
162 return i;
163 return -1;
164 }
165
166 int init()
167 {
168 int i, res;
169
170 res = scanf("%d", &N);
171
172 for (i = 0; i < N; ++i)
173 {
174 res = scanf("%d", &realS[i]);
175 }
176 for (i = 0; i < N; ++i)
177 {
178 res = scanf("%d", &realD[i]);
179 inv[realD[i]] = i;
180 }
181
182 num_calls = 0;
183 return N;
184 }
185
186 int main()
187 {
188 auto t1 = clock();
189
190 std::freopen("../tests/random-perm-5000-many-closed.in", "r", stdin);
191 std::freopen("cave.out", "w", stdout);
192
193 int N;
194
195 N = init();
196
197 auto t2 = clock();
198
199 exploreCave(N);
200
201 auto t3 = clock();
202
203 //printf("INCORRECT\nYour solution did not call answer().\n");
204
205 auto t4 = clock();
206
207 // reset console output
208 freopen("CON", "w", stdout);
209
210 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
211 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
212 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
213
214 return 0;
215 }
216 /*
217 t2-t1 = 0
218 t3-t2 = 1.968
219 t4-t3 = 0
220
221 Process returned 0 (0x0) execution time : 2.078 s
CAPITOLUL 8. IOI 2013 907

222 Press any key to continue.


223 */

Listing 8.2.5: checkerCave.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/random-perm-5000-many-closed.in",
14 (char*)"cave.out",
15 (char*)"../tests/random-perm-5000-many-closed.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("cave", argc, argv);
24 compareRemainingLines();
25 }

8.2.3 *Rezolvare detaliat 

8.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 e conectate deloc (deci, M & N  1). Fiecare potec  necesit  un num r de
zile pentru ca Serpent s  o parcurg : acest num r poate  diferit pentru ecare potec  în parte.
Prietenul lui Serpent, Kangaroo, dore³te s  construiasc  N  M  1 noi poteci, astfel încât s 
e posibil ca Serpent s  c l toreasc  între oricare pereche de b ltoace. Kangaroo poate crea poteci
între oricare pereche de b ltoace, iar ecare 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  e 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 8. IOI 2013 908

În imaginea de mai sus, sunt N 12 b ltoace ³i M 8 poteci. Presupunând c  L 2 atunci


ecare potec  nou  va  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 nal 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 ³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 8. IOI 2013 909

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 specic  capetele ³i timpul de parcurgere pentru
ecare 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  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
ecare b ltoac . Cu alte cuvinte, exist  dou  seturi de b ltoace conectate,
³i in ecare 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 ecare b ltoac .
5 12 N & 3, 000
6 23 (Nimic)
Testare
Grader-ul de pe computerul vostru va citi input-ul din ³ierul dreaming.in, care trebuie s 
e î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  e dat în urm torul format:

12 8 2
0 8 4
8 2 2
2 7 4
5 11 3
5 1 7
CAPITOLUL 8. IOI 2013 910

1 3 1
1 9 5
10 6 3

Note de limbaj
C/C++: Trebuie s  faceµi #include "dreaming.h".
Pascal: Trebuie s  deniµ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

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

8.3.2 Coduri surs 

Listing 8.3.1: dreaming-3168.cpp


1 // https://oj.uz/submission/3168 86 ms 9336 KB
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 #include<ctime>
6 #include<iostream>
7
8 #include "dreaming.h"
9 #include<algorithm>
10 #include<vector>
11
12 using namespace std;
13
14 int N,M,L;
15 int Y[100010],tl,inl;
16 int tmp[100010],Q[100010][3];
17 int check[100010];
18
19 struct line
20 {
21 line(){}
CAPITOLUL 8. IOI 2013 911

22 line(int en,int len):en(en),len(len){}


23 int en,len;
24 };
25
26 vector <line> edge[100010];
27
28 int far(int x)
29 {
30 int fr=1,re=0,ret=x,mx=0;
31 Q[0][0]=x;
32 Q[0][1]=0;
33 check[x]=1;
34 while(fr!=re)
35 {
36 int i;
37 for(i=0;i<edge[Q[re][0]].size();i++)
38 {
39 int tx=edge[Q[re][0]][i].en;
40 if(check[tx])continue;
41 check[tx]=1;
42 Q[fr][0]=tx;
43 Q[fr][1]=Q[re][1]+edge[Q[re][0]][i].len;
44 if(mx<Q[fr][1])
45 {
46 mx=Q[fr][1];
47 ret=Q[fr][0];
48 }
49 fr++;
50 }
51 re++;
52 }
53 return ret;
54 }
55
56 void solve(int x)
57 {
58 int t=far(x),fr=1,re=0,tp=0;
59 Q[0][0]=t;
60 Q[0][1]=0;
61 Q[0][2]=-1;
62 check[t]=2;
63 while(fr!=re)
64 {
65 int i;
66 for(i=0;i<edge[Q[re][0]].size();i++)
67 {
68 int tx=edge[Q[re][0]][i].en;
69 if(check[tx]==2) continue;
70 check[tx]=2;
71 Q[fr][0]=tx;
72 Q[fr][1]=Q[re][1]+edge[Q[re][0]][i].len;
73 Q[fr][2]=re;
74 if(Q[fr][1]>Q[tp][1])tp=fr;
75 fr++;
76 }
77 re++;
78 }
79
80 int len=Q[tp][1],tx=0;
81 while(Q[tp][2]!=-1)
82 {
83 if(Q[tp][1]*2>len) tx=Q[tp][1];
84 else
85 {
86 tx=min(tx,len-Q[tp][1]);
87 break;
88 }
89 tp=Q[tp][2];
90 }
91
92 Y[tl]=tx;tl++;
93 inl=max(inl,len);
94 }
95
96 int travelTime(int Nn, int Mm, int Ll, int A[], int B[], int T[])
97 {
CAPITOLUL 8. IOI 2013 912

98 N=Nn,M=Mm,L=Ll;
99 int i;
100 for(i=0;i<M;i++)
101 {
102 edge[A[i]+1].push_back(line(B[i]+1,T[i]));
103 edge[B[i]+1].push_back(line(A[i]+1,T[i]));
104 }
105
106 for(i=1;i<=N;i++)
107 {
108 if(check[i])continue;
109 solve(i);
110 }
111 sort(Y,Y+tl);
112
113 if(tl==1)return inl;
114 if(tl==2)
115 return max(inl,L+Y[tl-1]+Y[tl-2]);
116 else
117 return max(inl,max(L+Y[tl-1]+Y[tl-2],2*L+Y[tl-2]+Y[tl-3]));
118 }
119
120 // ------------- begin grader --------------
121
122 #define fail(s, x...) do { \
123 fprintf(stderr, s "\n", ## x); \
124 exit(1); \
125 } while(0)
126
127 #define MAX_N 100000
128
129 static int A[MAX_N];
130 static int B[MAX_N];
131 static int T[MAX_N];
132
133 int main()
134 {
135 auto t1 = clock();
136
137 std::freopen("../tests/two-sticks-9.in", "r", stdin) ;
138 std::freopen("dreaming.out", "w", stdout) ;
139
140 int N, M, L, i;
141 int res;
142
143 res = scanf("%d%d%d", &N, &M, &L);
144
145 for (i = 0; i < M; i++)
146 res = scanf("%d%d%d", &A[i], &B[i], &T[i]);
147
148 auto t2 = clock();
149
150 int answer = travelTime(N, M, L, A, B, T);
151
152 auto t3 = clock();
153
154 printf("%d\n", answer);
155
156 auto t4 = clock();
157
158 // reset console output
159 freopen("CON", "w", stdout);
160
161 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
162 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
163 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
164
165 return 0;
166 }
167
168 // ------------- end grader ----------------
169 /*
170 t2-t1 = 0.093
171 t3-t2 = 0.188
172 t4-t3 = 0
173
CAPITOLUL 8. IOI 2013 913

174 Process returned 0 (0x0) execution time : 2.656 s


175 Press any key to continue.
176 */

Listing 8.3.2: dreaming-16636.cpp


1 // https://oj.uz/submission/16636 71 ms 12280 KB
2
3 #include<ctime>
4 #include<iostream>
5
6 #include "dreaming.h"
7 #include <stdio.h>
8 #include <algorithm>
9 #include <vector>
10
11 #define MM 100000
12
13 using namespace std;
14
15 int sta[MM+1],chi[MM*2+1],wei[MM*2+1],nxt[MM*2+1];
16 int n,m,l,d[2][MM+1],v[2][MM+1],itr;
17 vector<int> vec;
18 bool ch[MM+1];
19
20 void addEdge(int x,int y,int w)
21 {
22 nxt[++m]=sta[x];
23 chi[m]=y;
24 wei[m]=w;
25 sta[x]=m;
26 }
27
28 int DFS1(int x,int p)
29 {
30 int i;
31
32 for(i=sta[x];i;i=nxt[i])
33 {
34 if(chi[i]!=p)
35 {
36 int k=DFS1(chi[i],x)+wei[i];
37 if(d[0][x]<k)
38 {
39 d[1][x]=d[0][x];
40 v[1][x]=v[0][x];
41 d[0][x]=k;
42 v[0][x]=i;
43 }
44 else
45 if(d[1][x]<k)
46 {
47 d[1][x]=k;
48 v[1][x]=i;
49 }
50 }
51 }
52
53 return d[0][x];
54 }
55
56 int DFS2(int x,int s,int p)
57 {
58 int i,ret;
59
60 if(d[0][x]<s)
61 {
62 d[1][x]=d[0][x];
63 v[1][x]=v[0][x];
64 d[0][x]=s;
65 v[0][x]=-1;
66 }
67 else
68 if(d[1][x]<s)
69 {
CAPITOLUL 8. IOI 2013 914

70 d[1][x]=s;
71 v[1][x]=-1;
72 }
73
74 ret=d[0][x];
75 ch[x]=true;
76
77 for(i=sta[x];i;i=nxt[i])
78 {
79 if(chi[i]!=p)
80 {
81 int k;
82 if(v[0][x]!=i)
83 {
84 k=DFS2(chi[i],d[0][x]+wei[i],x);
85 }
86 else
87 {
88 k=DFS2(chi[i],d[1][x]+wei[i],x);
89 }
90
91 ret=min(ret,k);
92 }
93 }
94
95 itr=max(itr,d[0][x]+d[1][x]);
96 return ret;
97 }
98
99 int travelTime(int _n, int _m, int _l, int A[], int B[], int T[])
100 {
101 int i;
102 n=_n;
103 l=_l;
104 m=0;
105 for(i=0;i<_m;i++)
106 {
107 addEdge(A[i],B[i],T[i]);
108 addEdge(B[i],A[i],T[i]);
109 }
110
111 for(i=0;i<n;i++)
112 {
113 if(!ch[i])
114 {
115 DFS1(i,-1);
116 vec.push_back(DFS2(i,0,-1));
117 }
118 }
119
120 int mx1=-1,mx2=-1,mx3=-1;
121 for(i=0;i<vec.size();i++)
122 {
123 if(mx3<vec[i]){mx3=vec[i];}
124 if(mx2<vec[i]){mx3=mx2;mx2=vec[i];}
125 if(mx1<vec[i]){mx2=mx1;mx1=vec[i];}
126 }
127 if(mx2==-1) return itr;
128 if(mx3==-1) return max(itr,mx1+mx2+l);
129
130 return max(itr,max(mx1+mx2+l,mx2+mx3+l*2));
131 }
132
133 // ------------- begin grader --------------
134
135 #define fail(s, x...) do { \
136 fprintf(stderr, s "\n", ## x); \
137 exit(1); \
138 } while(0)
139
140 #define MAX_N 100000
141
142 static int A[MAX_N];
143 static int B[MAX_N];
144 static int T[MAX_N];
145
CAPITOLUL 8. IOI 2013 915

146 int main()


147 {
148 auto t1 = clock();
149
150 std::freopen("../tests/two-sticks-9.in", "r", stdin) ;
151 std::freopen("dreaming.out", "w", stdout) ;
152
153 int N, M, L, i;
154 int res;
155
156 res = scanf("%d%d%d", &N, &M, &L);
157
158 for (i = 0; i < M; i++)
159 res = scanf("%d%d%d", &A[i], &B[i], &T[i]);
160
161 auto t2 = clock();
162
163 int answer = travelTime(N, M, L, A, B, T);
164
165 auto t3 = clock();
166
167 printf("%d\n", answer);
168
169 auto t4 = clock();
170
171 // reset console output
172 freopen("CON", "w", stdout);
173
174 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
175 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
176 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
177
178 return 0;
179 }
180
181 // ------------- end grader ----------------
182 /*
183 t2-t1 = 0.437
184 t3-t2 = 0.062
185 t4-t3 = 0
186
187 Process returned 0 (0x0) execution time : 2.627 s
188 Press any key to continue.
189 */

Listing 8.3.3: dreaming-81476.cpp


1 // https://oj.uz/submission/81476 96 ms 16376 KB
2
3 #include "dreaming.h"
4 #include <bits/stdc++.h>
5
6 #define fi first
7 #define se second
8 #define lo long long
9 #define inf 1000000009
10 #define md 1000000007
11 #define li 100005
12 #define mp make_pair
13 #define pb push_back
14
15 using namespace std;
16
17 int vis[li],mn,M[li],M2[li],node[li],ss;
18 vector< pair<int,int> > v[li];
19
20 void f(int x)
21 {
22 vis[x]=1;
23 int t;
24 for(int i=0;i<(int)v[x].size();i++)
25 {
26 int go=v[x][i].fi;
27 int knr=v[x][i].se;
28 if(vis[go]==1) continue;
CAPITOLUL 8. IOI 2013 916

29 f(go);
30 t=M[go]+knr;
31 if(M[x]<t)
32 {
33 M2[x]=M[x];
34 M[x]=t;
35 node[x]=go;
36 }
37 else
38 if(M2[x]<t)
39 {
40 M2[x]=t;
41 }
42 }
43
44 ss=max(ss,M[x]+M2[x]);
45 }
46
47 void g(int x,int p,int t)
48 {
49 mn=min(mn,max(M[x],t));
50 for(int i=0;i<(int)v[x].size();i++)
51 {
52 int go=v[x][i].fi;
53 int knr=v[x][i].se;
54 if(go==p) continue;
55 int a=max(t,node[x]==go?M2[x]:M[x]);
56 g(go,x,a+knr);
57 }
58 }
59
60 int travelTime(int n,int m,int l,int *A,int *B,int *T)
61 {
62 int t,x,xx,xxx;
63 for(int i=0;i<m;i++)
64 {
65 v[A[i]].pb(mp(B[i],T[i]));
66 v[B[i]].pb(mp(A[i],T[i]));
67 }
68
69 x=-inf,xx=-inf,xxx=-inf;
70 for(int i=0;i<n;i++)
71 {
72 if(vis[i]==1) continue;
73 mn=1e9;
74 f(i);
75 g(i,-1,0);
76 if(x<mn)
77 {
78 xxx=xx;
79 xx=x;
80 x=mn;
81 }
82 else
83 if(xx<mn)
84 {
85 xxx=xx;
86 xx=mn;
87 }
88 else
89 if(xxx<mn)
90 {
91 xxx=mn;
92 }
93 }
94
95 return max(ss,max(x+xx+l,xx+xxx+l+l));
96 }
97
98 // ------------- begin grader --------------
99
100 #define fail(s, x...) do { \
101 fprintf(stderr, s "\n", ## x); \
102 exit(1); \
103 } while(0)
104
CAPITOLUL 8. IOI 2013 917

105 #define MAX_N 100000


106
107 static int A[MAX_N];
108 static int B[MAX_N];
109 static int T[MAX_N];
110
111 int main()
112 {
113 auto t1 = clock();
114
115 std::freopen("../tests/two-sticks-9.in", "r", stdin) ;
116 std::freopen("dreaming.out", "w", stdout) ;
117
118 int N, M, L, i;
119 int res;
120
121 res = scanf("%d%d%d", &N, &M, &L);
122
123 for (i = 0; i < M; i++)
124 res = scanf("%d%d%d", &A[i], &B[i], &T[i]);
125
126 auto t2 = clock();
127
128 int answer = travelTime(N, M, L, A, B, T);
129
130 auto t3 = clock();
131
132 printf("%d\n", answer);
133
134 auto t4 = clock();
135
136 // reset console output
137 freopen("CON", "w", stdout);
138
139 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
140 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
141 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
142
143 return 0;
144 }
145
146 // ------------- end grader ----------------
147 /*
148 t2-t1 = 0.422
149 t3-t2 = 0.219
150 t4-t3 = 0
151
152 Process returned 0 (0x0) execution time : 2.906 s
153 Press any key to continue.
154 */

Listing 8.3.4: dreaming-172308.cpp


1 // https://oj.uz/submission/172308 117 ms 17272 KB
2
3 #include "dreaming.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 using pii = pair<int, int>;
9
10 #define ff first
11 #define ss second
12
13 vector<pii> edge[100005];
14 int par[100005], X[100005];
15 bool vis[100005];
16
17 pii dfs(int x, int p)
18 {
19 pii ret = {0, x};
20 for (auto i : edge[x])
21 {
22 if (i.ss == p) continue;
CAPITOLUL 8. IOI 2013 918

23 auto t = dfs(i.ss, x);


24 ret = max(ret, pii(t.ff + i.ff, t.ss));
25 }
26 X[x] = ret.ff;
27 par[x] = p;
28 vis[x] = 1;
29 return ret;
30 }
31
32 int travelTime(int N, int M, int L, int A[], int B[], int T[])
33 {
34 for (int i = 0; i < M; ++i)
35 {
36 edge[A[i]].emplace_back(T[i], B[i]);
37 edge[B[i]].emplace_back(T[i], A[i]);
38 }
39
40 vector<int> vec;
41 int ans, dia = 0;
42 for (int i = 0; i < N; ++i)
43 {
44 if (vis[i]) continue;
45 auto t = dfs(dfs(i, i).ss, -1);
46 int x = t.ss, mn = t.ff;
47 for (; x >= 0; x = par[x]) mn = min(mn, max(X[x], t.ff - X[x]));
48 vec.push_back(mn);
49 dia = max(dia, t.ff);
50 }
51
52 sort(vec.rbegin(), vec.rend());
53
54 if (vec.size() > 2)
55 {
56 ans = L + vec[0] + vec[1];
57 for (int i = 2; i < vec.size(); ++i)
58 ans = max(ans, L + L + vec[1] + vec[i]);
59 }
60 else
61 if (vec.size() == 2) ans = L + vec[0] + vec[1];
62 else ans = 0;
63
64 ans = max(ans, dia);
65 return ans;
66 }
67
68 // ------------- begin grader --------------
69
70 #define fail(s, x...) do { \
71 fprintf(stderr, s "\n", ## x); \
72 exit(1); \
73 } while(0)
74
75 #define MAX_N 100000
76
77 static int A[MAX_N];
78 static int B[MAX_N];
79 static int T[MAX_N];
80
81 int main()
82 {
83 auto t1 = clock();
84
85 std::freopen("../tests/two-sticks-9.in", "r", stdin) ;
86 std::freopen("dreaming.out", "w", stdout) ;
87
88 int N, M, L, i;
89 int res;
90
91 res = scanf("%d%d%d", &N, &M, &L);
92
93 for (i = 0; i < M; i++)
94 res = scanf("%d%d%d", &A[i], &B[i], &T[i]);
95
96 auto t2 = clock();
97
98 int answer = travelTime(N, M, L, A, B, T);
CAPITOLUL 8. IOI 2013 919

99
100 auto t3 = clock();
101
102 printf("%d\n", answer);
103
104 auto t4 = clock();
105
106 // reset console output
107 freopen("CON", "w", stdout);
108
109 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
110 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
111 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
112
113 return 0;
114 }
115
116 // ------------- end grader ----------------
117 /*
118 t2-t1 = 0.406
119 t3-t2 = 0.234
120 t4-t3 = 0
121
122 Process returned 0 (0x0) execution time : 2.813 s
123 Press any key to continue.
124 */

Listing 8.3.5: dreaming-198762.cpp


1 // https://oj.uz/submission/198762 143 ms 17272 KB
2
3 #include"dreaming.h"
4 #include<bits/stdc++.h>
5
6 using namespace std;
7
8 vector<pair<int, int> >adj[100005];
9 vector<int>v;
10 bool vis[100005];
11 int par[100005], X[100005];
12
13 pair<int, int> dfs(int nw, int bf)
14 {
15 pair<int, int> rs={0, nw};
16 for(pair<int, int> i:adj[nw])
17 {
18 if(i.second==bf) continue;
19 pair<int, int> rst=dfs(i.second, nw);
20 rs=max(rs, {rst.first+i.first, rst.second});
21 }
22 X[nw]=rs.first;
23 par[nw]=bf; vis[nw]=true;
24 return rs;
25 }
26
27 int travelTime(int n, int m, int L, int A[], int B[], int T[])
28 {
29 for(int i=0; i<m; i++)
30 {
31 adj[A[i]].push_back({T[i], B[i]});
32 adj[B[i]].push_back({T[i], A[i]});
33 }
34
35 int ans, diameter=0;
36 for(int i=0; i<n; i++)
37 {
38 if(vis[i]) continue;
39 pair<int, int> rs=dfs(dfs(i, i).second, -1);
40 int nd=rs.second, mn=rs.first;
41 for(; nd>=0; nd=par[nd])
42 mn=min(mn, max(X[nd], rs.first-X[nd]));
43
44 v.push_back(mn);
45 diameter=max(diameter, rs.first);
46 }
CAPITOLUL 8. IOI 2013 920

47
48 sort(v.begin(), v.end());
49 reverse(v.begin(), v.end());
50
51 if(v.size()>2)
52 {
53 ans=L+v[0]+v[1];
54 for(int i=2; i<v.size(); i++)
55 ans=max(ans, 2*L+v[1]+v[i]);
56 }
57 else
58 if(v.size()==2) ans=L+v[0]+v[1];
59 else ans=0;
60
61 ans=max(ans, diameter);
62 return ans;
63 }
64
65 // ------------- begin grader --------------
66
67 #define fail(s, x...) do { \
68 fprintf(stderr, s "\n", ## x); \
69 exit(1); \
70 } while(0)
71
72 #define MAX_N 100000
73
74 static int A[MAX_N];
75 static int B[MAX_N];
76 static int T[MAX_N];
77
78 int main()
79 {
80 auto t1 = clock();
81
82 std::freopen("../tests/two-sticks-9.in", "r", stdin) ;
83 std::freopen("dreaming.out", "w", stdout) ;
84
85 int N, M, L, i;
86 int res;
87
88 res = scanf("%d%d%d", &N, &M, &L);
89
90 for (i = 0; i < M; i++)
91 res = scanf("%d%d%d", &A[i], &B[i], &T[i]);
92
93 auto t2 = clock();
94
95 int answer = travelTime(N, M, L, A, B, T);
96
97 auto t3 = clock();
98
99 printf("%d\n", answer);
100
101 auto t4 = clock();
102
103 // reset console output
104 freopen("CON", "w", stdout);
105
106 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
107 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
108 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
109
110 return 0;
111 }
112
113 // ------------- end grader ----------------
114 /*
115 t2-t1 = 0.438
116 t3-t2 = 0.234
117 t4-t3 = 0
118
119 Process returned 0 (0x0) execution time : 2.891 s
120 Press any key to continue.
121 */
CAPITOLUL 8. IOI 2013 921

Listing 8.3.6: checkerDreaming.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/two-sticks-9.in",
14 (char*)"dreaming.out",
15 (char*)"../tests/two-sticks-9.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("dreaming", argc, argv);
24 compareRemainingLines();
25 }

8.3.3 *Rezolvare detaliat 

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

Grid-ul rezultat este a³at în imaginea de mai sus. Bazza poate s  întrebe care este CMMDC-ul
urm toarelor zone dreptunghiulare:
CAPITOLUL 8. IOI 2013 922

ˆ 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 a³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 ³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 ecare 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 sucient 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  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  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 ).
ˆ K : Valoarea nou  care va  conµinut  de celula din grid ( 0 & K & 1018 ). Poate  la fel ca
valoarea curent .
CAPITOLUL 8. IOI 2013 923

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 drept-
unghiul 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 ³ierul game.in. Acest ³ier trebuie s 
aib  urm torul format:
ˆ linia 1: R C N
ˆ urm toarele N linii: o acµiune pe linie, în ordinea în care apar
CAPITOLUL 8. IOI 2013 924

Linia pentru ecare acµiune trebuie s  e 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  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 deniµi unit Game. Toµi vectorii sunt indexaµi incepând de la 0 (nu de la
1).
Deoarece întregii din grid pot  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

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

8.4.2 Coduri surs 


CAPITOLUL 8. IOI 2013 925

Listing 8.4.1: game-220385.cpp


1 // https://oj.uz/submission/220385 3289 ms 73500 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "game.h"
6
7 #include<bits/stdc++.h>
8 #include "game.h"
9
10 using namespace std;
11
12 mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
13
14 const int N = 3e5 + 9;
15
16 struct node
17 {
18 node *l, *r;
19 int pos, key, mn, mx;
20 long long val, g;
21 node(int position, long long value)
22 {
23 l = r = nullptr;
24 mn = mx = pos = position;
25 key = rnd();
26 val = g = value;
27 }
28
29 void pull()
30 {
31 g = val;
32 if(l) g = __gcd(g, l->g);
33 if(r) g = __gcd(g, r->g);
34 mn = (l ? l->mn : pos);
35 mx = (r ? r->mx : pos);
36 }
37 };
38
39 //memory O(n)
40 struct treap
41 {
42 node *root;
43 treap()
44 {
45 root = nullptr;
46 }
47
48 void split(node *t, int pos, node *&l, node *&r)
49 {
50 if (t == nullptr)
51 {
52 l = r = nullptr;
53 return;
54 }
55 if (t->pos < pos)
56 {
57 split(t->r, pos, l, r);
58 t->r = l;
59 l = t;
60 }
61 else
62 {
63 split(t->l, pos, l, r);
64 t->l = r;
65 r = t;
66 }
67 t->pull();
68 }
69
70 node* merge(node *l, node *r)
71 {
72 if (!l || !r) return l ? l : r;
73 if (l->key < r->key)
74 {
CAPITOLUL 8. IOI 2013 926

75 l->r = merge(l->r, r);


76 l->pull();
77 return l;
78 }
79 r->l = merge(l, r->l);
80 r->pull();
81 return r;
82 }
83
84 bool find(int pos)
85 {
86 node *t = root;
87 while (t)
88 {
89 if (t->pos == pos) return true;
90 if (t->pos > pos) t = t->l;
91 else t = t->r;
92 }
93 return false;
94 }
95
96 void upd(node *t, int pos, long long val)
97 {
98 if (t->pos == pos)
99 {
100 t->val = val;
101 t->pull();
102 return;
103 }
104 if (t->pos > pos) upd(t->l, pos, val);
105 else upd(t->r, pos, val);
106 t->pull();
107 }
108
109 void insert(int pos, long long val) //set a_pos = val
110 {
111 if (find(pos)) upd(root, pos, val);
112 else
113 {
114 node *l, *r;
115 split(root, pos, l, r);
116 root = merge(merge(l, new node(pos, val)), r);
117 }
118 }
119
120 long long query(node *t, int st, int en)
121 {
122 if (t->mx < st || en < t->mn) return 0;
123 if (st <= t->mn && t->mx <= en) return t->g;
124 long long ans = (st <= t->pos && t->pos <= en ? t->val : 0);
125 if (t->l) ans = __gcd(ans, query(t->l, st, en));
126 if (t->r) ans = __gcd(ans, query(t->r, st, en));
127 return ans;
128 }
129
130 long long query(int l, int r) //gcd of a_i such that l <= i <= r
131 {
132 if (!root) return 0;
133 return query(root, l, r);
134 }
135
136 void print(node *t)
137 {
138 if (!t) return;
139 print(t->l);
140 cout << t->val << " ";
141 print(t->r);
142 }
143 };
144
145 //total memory along with treap = n log n
146 struct ST
147 {
148 ST *l, *r;
149 treap t;
150 int b, e;
CAPITOLUL 8. IOI 2013 927

151
152 ST()
153 {
154 l = r = nullptr;
155 }
156
157 ST(int st, int en)
158 {
159 l = r = nullptr;
160 b = st, e = en;
161 }
162
163 void fix(int pos)
164 {
165 long long val = 0;
166 if (l) val = __gcd(val, l->t.query(pos, pos));
167 if (r) val = __gcd(val, r->t.query(pos, pos));
168 t.insert(pos, val);
169 }
170
171 void upd(int x, int y, long long val) //set a[x][y] = val
172 {
173 if (e < x || x < b) return;
174 if (b == e)
175 {
176 t.insert(y, val);
177 return;
178 }
179
180 if (b != e)
181 {
182 if (x <= (b + e) / 2)
183 {
184 if (!l) l = new ST(b, (b + e) / 2);
185 l->upd(x, y, val);
186 }
187 else
188 {
189 if (!r) r = new ST((b + e) / 2 + 1, e);
190 r->upd(x, y, val);
191 }
192 }
193 fix(y);
194 }
195
196 long long query(int i, int j, int st, int en) //gcd of a[x][y]
197 // such that i <= x <= j && st <= y <= en
198 {
199 if (e < i || j < b) return 0;
200 if (i <= b && e <= j) return t.query(st, en);
201 long long ans = 0;
202 if (l) ans = __gcd(ans, l->query(i, j, st, en));
203 if (r) ans = __gcd(ans, r->query(i, j, st, en));
204 return ans;
205 }
206 };
207
208 int r, c, q;
209 ST t;
210
211 void init(int R, int C)
212 {
213 r = R; c = C;
214 t = ST(0, r - 1);
215 }
216
217 void update(int P, int Q, long long K)
218 {
219 t.upd(P, Q, K);
220 }
221
222 long long calculate(int P, int Q, int U, int V)
223 {
224 return t.query(P, U, Q, V);
225 }
226
CAPITOLUL 8. IOI 2013 928

227 // ------------- begin grader ----------------------


228
229 #define fail(s, x...) do { \
230 fprintf(stderr, s "\n", ## x); \
231 exit(1); \
232 } while(0)
233
234 #define MAX_SIZE 1000000000
235 //#define MAX_N 100000
236 #define MAX_N 272000
237
238 int main()
239 {
240 auto t1 = clock();
241
242 std::freopen("../tests/05.st-4-random-1.in", "r", stdin);
243 std::freopen("game.out", "w", stdout);
244
245 int R, C, N;
246 int P, Q, U, V;
247 long long K;
248 int i, type;
249 int res;
250
251 res = scanf("%d", &R);
252
253 if (res != 1)
254 fail("Failed to read R from input file.");
255 if (R < 1 || R > MAX_SIZE)
256 fail("R is out of bounds.");
257
258 res = scanf("%d", &C);
259
260 if (res != 1)
261 fail("Failed to read C from input file.");
262 if (C < 1 || C > MAX_SIZE)
263 fail("C is out of bounds.");
264
265 res = scanf("%d", &N);
266
267 if (res != 1)
268 fail("Failed to read N from input file.");
269 if (N < 0 || N > MAX_N)
270 fail("N is out of bounds.");
271
272 auto t2 = clock();
273
274 init(R, C);
275
276 auto t3 = clock();
277
278 for (i = 0; i < N; i++)
279 {
280 res = scanf("%d", &type);
281
282 if (type == 1)
283 {
284 res = scanf("%d%d%lld", &P, &Q, &K);
285
286 update(P, Q, K);
287 }
288 else
289 if (type == 2)
290 {
291 res = scanf("%d%d%d%d", &P, &Q, &U, &V);
292
293 printf("%lld\n", calculate(P, Q, U, V));
294 }
295 else
296 fail("Invalid action type in input file.");
297 }
298
299 auto t4 = clock();
300
301 // reset console output
302 freopen("CON", "w", stdout);
CAPITOLUL 8. IOI 2013 929

303
304 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
305 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
306 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
307
308 return 0;
309 }
310
311 // ------------- end grader ----------------------
312 /*
313 t2-t1 = 0
314 t3-t2 = 0
315 t4-t3 = 9.14
316
317 Process returned 0 (0x0) execution time : 9.188 s
318 Press any key to continue.
319 */

Listing 8.4.2: game-224978.cpp


1 // https://oj.uz/submission/224978 3504 ms 82424 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "game.h"
6
7 #include<ctime>
8 #include<iostream>
9
10 using namespace std;
11
12 #define MAXR 1000000000
13 #define MAXC 1000000000
14
15 #include <assert.h>
16 #include <stddef.h>
17
18 long long gcd2(long long X, long long Y)
19 {
20 if(X == 0 || Y == 0)
21 {
22 return X + Y;
23 }
24
25 long long tmp;
26 while (X != Y && Y != 0)
27 {
28 tmp = X;
29 X = Y;
30 Y = tmp % Y;
31 }
32 return X;
33 }
34
35 struct layer2_node
36 {
37 layer2_node(int low, int high)
38 : low(low), high(high), lft(NULL), rht(NULL), value(0LL) { }
39
40 int low;
41 int high;
42 layer2_node* lft;
43 layer2_node* rht;
44 long long value;
45 };
46
47 struct layer1_node
48 {
49 layer1_node() : lft(NULL), rht(NULL), l2(0, MAXC) { }
50
51 layer1_node* lft;
52 layer1_node* rht;
53 layer2_node l2;
54 };
55
CAPITOLUL 8. IOI 2013 930

56 static layer1_node root;


57
58 static void update2(layer2_node* node, int Q, long long K)
59 {
60 int low = node->low;
61 int high = node->high;
62 int mid = (low + high) / 2;
63 if(low + 1 == high)
64 {
65 /* For leaf nodes we just update the value directly. */
66 node->value = K;
67 return;
68 }
69
70 layer2_node*& tgt = Q < mid ? node->lft : node->rht;
71 if(tgt == NULL)
72 {
73 /* If there is no node on this side of the tree create a new leaf node
74 * containing exactly our update point. */
75 tgt = new layer2_node(Q, Q + 1);
76 tgt->value = K;
77 }
78 else
79 if(tgt->low <= Q && Q < tgt->high)
80 {
81 /* If the existing node contains our query point continue
82 * inserting there.
83 */
84 update2(tgt, Q, K);
85 }
86 else
87 {
88 /* Otherwise traverse down the virtual tree until the side of
89 * our query node
90 * and the side of the existing node differ. Create this node
91 * and continue
92 * updating along it. */
93 do
94 {
95 (Q < mid ? high : low) = mid;
96 mid = (low + high) / 2;
97 } while((Q < mid) == (tgt->low < mid));
98
99 layer2_node* nnode = new layer2_node(low, high);
100 (tgt->low < mid ? nnode->lft : nnode->rht) = tgt;
101 tgt = nnode;
102
103 update2(nnode, Q, K);
104 }
105
106 /* Internal nodes get updated as the gcd of its leaves. */
107 node->value = gcd2(node->lft ? node->lft->value : 0,
108 node->rht ? node->rht->value : 0);
109 }
110
111 long long query2(layer2_node* nd, int A, int B)
112 {
113 /* The same as the level 1 queries except the interval each node
114 * represents
115 * may skip some levels of the tree so we store them in the node
116 * itself. */
117 if(nd == NULL || B <= nd->low || nd->high <= A)
118 {
119 return 0;
120 }
121 else
122 if(A <= nd->low && nd->high <= B)
123 {
124 return nd->value;
125 }
126 return gcd2(query2(nd->lft, A, B), query2(nd->rht, A, B));
127 }
128
129 static void update1(layer1_node* node, int low, int high,
130 int P, int Q, long long K)
131 {
CAPITOLUL 8. IOI 2013 931

132 int mid = (low + high) / 2;


133
134 if(low + 1 == high)
135 {
136 update2(&node->l2, Q, K);
137 }
138 else
139 {
140 layer1_node*& nnode = P < mid ? node->lft : node->rht;
141 (P < mid ? high : low) = mid;
142 if(nnode == NULL)
143 {
144 nnode = new layer1_node();
145 }
146 update1(nnode, low, high, P, Q, K);
147
148 /* Compute what value to update with. */
149 K = gcd2(node->lft ? query2(&node->lft->l2, Q, Q + 1) : 0,
150 node->rht ? query2(&node->rht->l2, Q, Q + 1) : 0);
151 update2(&node->l2, Q, K);
152 }
153 }
154
155 long long query1(layer1_node* nd, int low, int high,
156 int A1, int B1, int A2, int B2)
157 {
158 /* Scan down the tree short-circuiting when we reach a node that
159 * contains
160 * our entire query interval and combining the result of the level2
161 * queries.
162 */
163 if(nd == NULL || B1 <= low || high <= A1)
164 {
165 return 0;
166 }
167 else
168 if(A1 <= low && high <= B1)
169 {
170 return query2(&nd->l2, A2, B2);
171 }
172 int mid = (low + high) / 2;
173 return gcd2(query1(nd->lft, low, mid, A1, B1, A2, B2),
174 query1(nd->rht, mid, high, A1, B1, A2, B2));
175 }
176
177 void init(int R, int C) { }
178
179 void update(int P, int Q, long long K)
180 {
181 update1(&root, 0, MAXR, P, Q, K);
182 }
183
184 long long calculate(int P, int Q, int U, int V)
185 {
186 return query1(&root, 0, MAXR, P, U + 1, Q, V + 1);
187 }
188
189 // ------------- begin grader ----------------------
190
191 #define fail(s, x...) do { \
192 fprintf(stderr, s "\n", ## x); \
193 exit(1); \
194 } while(0)
195
196 #define MAX_SIZE 1000000000
197 //#define MAX_N 100000
198 #define MAX_N 272000
199
200 int main()
201 {
202 auto t1 = clock();
203
204 std::freopen("../tests/05.st-4-random-1.in", "r", stdin);
205 std::freopen("game.out", "w", stdout);
206
207 int R, C, N;
CAPITOLUL 8. IOI 2013 932

208 int P, Q, U, V;
209 long long K;
210 int i, type;
211 int res;
212
213 res = scanf("%d", &R);
214
215 if (res != 1)
216 fail("Failed to read R from input file.");
217 if (R < 1 || R > MAX_SIZE)
218 fail("R is out of bounds.");
219
220 res = scanf("%d", &C);
221
222 if (res != 1)
223 fail("Failed to read C from input file.");
224 if (C < 1 || C > MAX_SIZE)
225 fail("C is out of bounds.");
226
227 res = scanf("%d", &N);
228
229 if (res != 1)
230 fail("Failed to read N from input file.");
231 if (N < 0 || N > MAX_N)
232 fail("N is out of bounds.");
233
234 auto t2 = clock();
235
236 init(R, C);
237
238 auto t3 = clock();
239
240 for (i = 0; i < N; i++)
241 {
242 res = scanf("%d", &type);
243
244 if (type == 1)
245 {
246 res = scanf("%d%d%lld", &P, &Q, &K);
247
248 update(P, Q, K);
249 }
250 else
251 if (type == 2)
252 {
253 res = scanf("%d%d%d%d", &P, &Q, &U, &V);
254
255 printf("%lld\n", calculate(P, Q, U, V));
256 }
257 else
258 fail("Invalid action type in input file.");
259 }
260
261 auto t4 = clock();
262
263 // reset console output
264 freopen("CON", "w", stdout);
265
266 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
267 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
268 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
269
270 return 0;
271 }
272
273 // ------------- end grader ----------------------
274 /*
275 t2-t1 = 0
276 t3-t2 = 0
277 t4-t3 = 6.031
278
279 Process returned 0 (0x0) execution time : 6.078 s
280 Press any key to continue.
281 */

Listing 8.4.3: game-225963.cpp


CAPITOLUL 8. IOI 2013 933

1 // https://oj.uz/submission/225963 3732 ms 94012 KB


2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "game.h"
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 int _R, _C;
12
13 struct node2
14 {
15 int l, r;
16 long long v;
17 node2 *L, *R;
18 node2(int a, int b)
19 {
20 l = a, r = b;
21 v = 0;
22 L = R = NULL;
23 }
24 };
25
26 struct node1
27 {
28 int l, r;
29 node2 *now;
30 node1 *L, *R;
31 node1(int a, int b)
32 {
33 l = a, r = b;
34 now = new node2(1, _C);
35 L = R = NULL;
36 }
37 };
38
39 node1 *root;
40
41 long long gcd2(long long X, long long Y)
42 {
43 long long tmp;
44 while (X != Y && Y != 0)
45 {
46 tmp = X;
47 X = Y;
48 Y = tmp % Y;
49 }
50 return X;
51 }
52
53 long long query2(node2 *seg, int a, int b)
54 {
55 if(!seg || seg->r < a || b < seg->l) return 0;
56 if(a <= seg->l && seg->r <= b) return seg->v;
57 return gcd2(query2(seg->L, a, b), query2(seg->R, a, b));
58 }
59
60 long long query1(node1 *seg, int a, int b, int c, int d)
61 {
62 if(!seg || seg->r < a || b < seg->l) return 0;
63 if(a <= seg->l && seg->r <= b) return query2(seg->now, c, d);
64 return gcd2(query1(seg->L, a, b, c, d), query1(seg->R, a, b, c, d));
65 }
66
67 void update2(node2 *&seg, int p, long long k)
68 {
69 if(seg->l == seg->r) return void(seg->v = k);
70 int l = seg->l, r = seg->r;
71 int mid = (l+r)>>1;
72 node2 *&now = (p <= mid) ? seg->L : seg->R;
73 if(!now)
74 {
75 now = new node2(p, p);
CAPITOLUL 8. IOI 2013 934

76 now->v = k;
77 }
78 else
79 if(now->l <= p && p <= now->r) update2(now, p, k);
80 else
81 {
82 while((p <= mid) == (now->l <= mid))
83 {
84 (p <= mid) ? r = mid : l = mid+1;
85 mid = (l+r)>>1;
86 }
87 node2 *next = new node2(l, r);
88 (now->l <= mid) ? next->L = now : next->R = now;
89 now = next;
90 update2(next, p, k);
91 }
92 seg->v = gcd2((seg->L) ? seg->L->v : 0, (seg->R) ? seg->R->v : 0);
93 }
94
95 void update1(node1 *&seg, int p, int q, long long k)
96 {
97 if(seg->l == seg->r) return void(update2(seg->now, q, k));
98 int mid = (seg->l + seg->r)>>1;
99 if(p <= mid)
100 {
101 if(!seg->L) seg->L = new node1(seg->l, mid);
102 update1(seg->L, p, q, k);
103 }
104 else
105 {
106 if(!seg->R) seg->R = new node1(mid+1, seg->r);
107 update1(seg->R, p, q, k);
108 }
109
110 long long v = gcd2((seg->L) ? query2(seg->L->now, q, q) : 0,
111 (seg->R) ? query2(seg->R->now, q, q) : 0);
112 update2(seg->now, q, v);
113 }
114
115 void init(int R, int C)
116 {
117 _R = R, _C = C;
118 root = new node1(1, _R);
119 }
120
121 void update(int P, int Q, long long K)
122 {
123 P++, Q++;
124 update1(root, P, Q, K);
125 }
126
127 long long calculate(int P, int Q, int U, int V)
128 {
129 P++, Q++, U++, V++;
130 return query1(root, P, U, Q, V);
131 }
132
133 // ------------- begin grader ----------------------
134
135 #define fail(s, x...) do { \
136 fprintf(stderr, s "\n", ## x); \
137 exit(1); \
138 } while(0)
139
140 #define MAX_SIZE 1000000000
141 //#define MAX_N 100000
142 #define MAX_N 272000
143
144 int main()
145 {
146 auto t1 = clock();
147
148 std::freopen("../tests/05.st-4-random-1.in", "r", stdin);
149 std::freopen("game.out", "w", stdout);
150
151 int R, C, N;
CAPITOLUL 8. IOI 2013 935

152 int P, Q, U, V;
153 long long K;
154 int i, type;
155 int res;
156
157 res = scanf("%d", &R);
158
159 if (res != 1)
160 fail("Failed to read R from input file.");
161 if (R < 1 || R > MAX_SIZE)
162 fail("R is out of bounds.");
163
164 res = scanf("%d", &C);
165
166 if (res != 1)
167 fail("Failed to read C from input file.");
168 if (C < 1 || C > MAX_SIZE)
169 fail("C is out of bounds.");
170
171 res = scanf("%d", &N);
172
173 if (res != 1)
174 fail("Failed to read N from input file.");
175 if (N < 0 || N > MAX_N)
176 fail("N is out of bounds.");
177
178 auto t2 = clock();
179
180 init(R, C);
181
182 auto t3 = clock();
183
184 for (i = 0; i < N; i++)
185 {
186 res = scanf("%d", &type);
187
188 if (type == 1)
189 {
190 res = scanf("%d%d%lld", &P, &Q, &K);
191
192 update(P, Q, K);
193 }
194 else
195 if (type == 2)
196 {
197 res = scanf("%d%d%d%d", &P, &Q, &U, &V);
198
199 printf("%lld\n", calculate(P, Q, U, V));
200 }
201 else
202 fail("Invalid action type in input file.");
203 }
204
205 auto t4 = clock();
206
207 // reset console output
208 freopen("CON", "w", stdout);
209
210 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
211 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
212 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
213
214 return 0;
215 }
216
217 // ------------- end grader ----------------------
218 /*
219 t2-t1 = 0
220 t3-t2 = 0
221 t4-t3 = 6.859
222
223 Process returned 0 (0x0) execution time : 6.906 s
224 Press any key to continue.
225 */

Listing 8.4.4: checkerGame.cpp


CAPITOLUL 8. IOI 2013 936

1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/05.st-4-random-1.in",
14 (char*)"game.out",
15 (char*)"../tests/05.st-4-random-1.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("game", argc, argv);
24 compareRemainingLines();
25 }

8.4.3 *Rezolvare detaliat 

8.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, ecare 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
armativ care este timpul minim in care acestea pot  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

Cel mai scurt timp pentru a culege toate juc riile este de trei minute:
CAPITOLUL 8. IOI 2013 937

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 ³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 ecare robot weak.
ˆ Y: Un vector de întregi de lungime B conµinând dimensiunile limit  pentru ecare robot
small.
ˆ W: Un vector de întregi de lungime T conµinând greutatea ec rei juc rii.
ˆ S: Un vector de întregi de lungime T conµinând dimensiunea ec 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  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 8. IOI 2013 938

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 ³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 e goale.
Note de limbaj
C/C++ Trebuie s  faceµi #include "robots.h".
Pascal Trebuie sa deniµ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 8. IOI 2013 939

8.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 oor 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 rst decide the actions of the weak robots, starting from the weakest. There will be some
set of toys it can handle. Since they eectively dier 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 nished, the dierence 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.

8.5.2 Coduri surs 

Listing 8.5.1: robot-7026.cpp


1 // https://oj.uz/submission/7026 1884 ms 22552 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "robots.h"
6
7 #include<ctime>
8 #include<iostream>
9
10 #include<algorithm>
11 #include<set>
12 #include<vector>
13
14 int V[1000000];
15 std::pair<int,int> D[1000000];
16 int R[50000];
17 std::vector<int> E[50000];
18
19 int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
20 {
21 int i,j,l,r,mid;
22 for(i=0;i<T;i++)
23 {
24 D[i].first=W[i]+1;
25 D[i].second=S[i]+1;
26 }
CAPITOLUL 8. IOI 2013 940

27 std::sort(X,X+A);
28 std::sort(Y,Y+B);
29 std::sort(D,D+T);
30 for(i=0;i<T;i++)
31 if((!A||D[i].first>X[A-1])&&(!B||D[i].second>Y[B-1]))
32 return -1;
33 l=1;
34 r=T;
35 while(l<r)
36 {
37 mid=(l+r)/2;
38 for(i=0;i<T;i++)V[i]=0;
39 for(i=0;i<B;i++)E[i].clear();
40 for(i=0;i<T;i++)
41 {
42 j=std::lower_bound(Y,Y+B,D[i].second)-Y;
43 if(j<B)E[j].push_back(i);
44 }
45
46 j=0;
47 for(i=B-1;i>=0;i--)
48 {
49 j+=mid;
50 if(j>2e9)j=2e9;
51 while(j>0&&E[i].size())
52 {
53 V[E[i][E[i].size()-1]]=1;
54 E[i].pop_back();
55 j--;
56 }
57 }
58
59 for(i=0;i<A;i++)R[i]=0;
60 j=0;
61 for(i=0;i<T;i++)if(!V[i])
62 {
63 while(j<A&&(X[j]<D[i].first||R[j]==mid))j++;
64 if(j<A)
65 {
66 V[i]=1;
67 R[j]++;
68 }
69 else break;
70 }
71
72 if(i==T)r=mid;
73 else l=mid+1;
74 }
75 return r;
76 }
77
78 // ---------------- begin grader ----------------
79
80 #define fail(s, x...) do { \
81 fprintf(stderr, s "\n", ## x); \
82 exit(1); \
83 } while(0)
84
85 #define MAX_A 50000
86 #define MAX_B 50000
87 #define MAX_T 1000000
88
89 static int X[MAX_A];
90 static int Y[MAX_B];
91 static int W[MAX_T];
92 static int S[MAX_T];
93
94 int main()
95 {
96 auto t1 = clock();
97
98 std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);
99 std::freopen("robot.out", "w", stdout);
100
101 int A, B, T, i;
102 int res;
CAPITOLUL 8. IOI 2013 941

103
104 res = scanf("%d", &A);
105
106 if (res != 1) fail("Failed to read A from input file.");
107 if (A < 0 || A > MAX_A) fail("A is out of bounds.");
108
109 res = scanf("%d", &B);
110
111 if (res != 1) fail("Failed to read B from input file.");
112 if (B < 0 || B > MAX_B) fail("B is out of bounds.");
113
114 res = scanf("%d", &T);
115
116 if (res != 1) fail("Failed to read T from input file.");
117 if (T < 1 || T > MAX_T) fail("T is out of bounds.");
118
119 for (i = 0; i < A; i++)
120 {
121 res = scanf("%d", &X[i]);
122
123 if (res != 1) fail("Failed to read data from input file.");
124 }
125
126 for (i = 0; i < B; i++)
127 {
128 res = scanf("%d", &Y[i]);
129
130 if (res != 1) fail("Failed to read data from input file.");
131 }
132
133 for (i = 0; i < T; i++)
134 {
135 res = scanf("%d%d", &W[i], &S[i]);
136
137 if (res != 2) fail("Failed to read data from input file.");
138 }
139
140 auto t2 = clock();
141
142 int answer = putaway(A, B, T, X, Y, W, S);
143
144 auto t3 = clock();
145
146 printf("%d\n", answer);
147
148 auto t4 = clock();
149
150 // reset console output
151 freopen("CON", "w", stdout);
152
153 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
154 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
155 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
156
157 return 0;
158 }
159
160 // ---------------- end grader ----------------
161
162 /*
163 t2-t1 = 0.515
164 t3-t2 = 8.761
165 t4-t3 = 0
166
167 Process returned 0 (0x0) execution time : 9.308 s
168 Press any key to continue.
169
170 argc = 4
171 checker
172 ../tests/st5-random-highS-highW-clusters.in
173 robot.out
174 ../tests/st5-random-highS-highW-clusters.out
175 ----------------------
176 1
177 Correct
178
CAPITOLUL 8. IOI 2013 942

179 Process returned 0 (0x0) execution time : 0.031 s


180 Press any key to continue.
181 */

Listing 8.5.2: robot-7037.cpp


1 // https://oj.uz/submission/7037 508 ms 22552 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "robots.h"
6
7 #include<ctime>
8 #include<iostream>
9
10 #include<algorithm>
11 #include<set>
12 #include<vector>
13
14 int V[1000000];
15 std::pair<int,int> D[1000000];
16 int R[50000];
17 std::vector<int> E[50000];
18
19 int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
20 {
21 int i,j,k,l,r,mid;
22 for(i=0;i<T;i++)
23 {
24 D[i].first=W[i]+1;
25 D[i].second=S[i]+1;
26 }
27
28 std::sort(X,X+A);
29 std::sort(Y,Y+B);
30 std::sort(D,D+T);
31
32 for(i=0;i<T;i++)
33 if((!A||D[i].first>X[A-1])&&(!B||D[i].second>Y[B-1]))
34 return -1;
35
36 for(i=0;i<B;i++)E[i].clear();
37 for(i=0;i<T;i++)
38 {
39 j=std::lower_bound(Y,Y+B,D[i].second)-Y;
40 if(j<B)E[j].push_back(i);
41 }
42
43 l=1;
44 r=T;
45 while(l<r)
46 {
47 mid=(l+r)/2;
48 for(i=0;i<T;i++)V[i]=0;
49 j=0;
50 for(i=B-1;i>=0;i--)
51 {
52 j+=mid;
53 if(j>2e9)j=2e9;
54 for(k=E[i].size()-1;k>=0&&j>0;k--)
55 {
56 V[E[i][k]]=1;
57 j--;
58 }
59 }
60
61 for(i=0;i<A;i++)R[i]=0;
62 j=0;
63 for(i=0;i<T;i++)if(!V[i])
64 {
65 while(j<A&&(X[j]<D[i].first||R[j]==mid))j++;
66 if(j<A)
67 {
68 V[i]=1;
69 R[j]++;
CAPITOLUL 8. IOI 2013 943

70 }
71 else break;
72 }
73
74 if(i==T)r=mid;
75 else l=mid+1;
76 }
77 return r;
78 }
79
80 // ---------------- begin grader ----------------
81
82 #define fail(s, x...) do { \
83 fprintf(stderr, s "\n", ## x); \
84 exit(1); \
85 } while(0)
86
87 #define MAX_A 50000
88 #define MAX_B 50000
89 #define MAX_T 1000000
90
91 static int X[MAX_A];
92 static int Y[MAX_B];
93 static int W[MAX_T];
94 static int S[MAX_T];
95
96 int main()
97 {
98 auto t1 = clock();
99
100 std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);
101 std::freopen("robot.out", "w", stdout);
102
103 int A, B, T, i;
104 int res;
105
106 res = scanf("%d", &A);
107
108 if (res != 1) fail("Failed to read A from input file.");
109 if (A < 0 || A > MAX_A) fail("A is out of bounds.");
110
111 res = scanf("%d", &B);
112
113 if (res != 1) fail("Failed to read B from input file.");
114 if (B < 0 || B > MAX_B) fail("B is out of bounds.");
115
116 res = scanf("%d", &T);
117
118 if (res != 1) fail("Failed to read T from input file.");
119 if (T < 1 || T > MAX_T) fail("T is out of bounds.");
120
121 for (i = 0; i < A; i++)
122 {
123 res = scanf("%d", &X[i]);
124
125 if (res != 1) fail("Failed to read data from input file.");
126 }
127
128 for (i = 0; i < B; i++)
129 {
130 res = scanf("%d", &Y[i]);
131
132 if (res != 1) fail("Failed to read data from input file.");
133 }
134
135 for (i = 0; i < T; i++)
136 {
137 res = scanf("%d%d", &W[i], &S[i]);
138
139 if (res != 2) fail("Failed to read data from input file.");
140 }
141
142 auto t2 = clock();
143
144 int answer = putaway(A, B, T, X, Y, W, S);
145
CAPITOLUL 8. IOI 2013 944

146 auto t3 = clock();


147
148 printf("%d\n", answer);
149
150 auto t4 = clock();
151
152 // reset console output
153 freopen("CON", "w", stdout);
154
155 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
156 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
157 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
158
159 return 0;
160 }
161
162 // ---------------- end grader ----------------
163
164 /*
165 t2-t1 = 0.531
166 t3-t2 = 1.453
167 t4-t3 = 0
168
169 Process returned 0 (0x0) execution time : 2.047 s
170 Press any key to continue.
171
172 argc = 4
173 checker
174 ../tests/st5-random-highS-highW-clusters.in
175 robot.out
176 ../tests/st5-random-highS-highW-clusters.out
177 ----------------------
178 1
179 Correct
180
181 Process returned 0 (0x0) execution time : 0.031 s
182 Press any key to continue.
183 */

Listing 8.5.3: robot-17227.cpp


1 // https://oj.uz/submission/17227 449 ms 36440 KB
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 #define type(x) __typeof((x).begin())
8 #define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); it++)
9
10 typedef long long ll;
11 typedef pair < int, int > ii;
12
13 const int inf = 1e9 + 333;
14 const ll linf = 1e18 + inf;
15
16 #include "robots.h"
17
18 const int N = 1e6 + 5;
19
20 int n;
21 int A, B;
22 int X[N], Y[N];
23 ii a[N];
24 int id[N], root[N], sizev[N];
25
26 vector < int > v;
27
28 int dsu(int x)
29 {
30 if(x == root[x])
31 return x;
32 return root[x] = dsu(root[x]);
33 }
34
CAPITOLUL 8. IOI 2013 945

35 bool f(int x)
36 {
37 v.clear();
38 for(int i = 0; i <= A; i++)
39 {
40 root[i] = i;
41 sizev[i] = x;
42 }
43
44 for(int i = n - 1; i >= 0; i--)
45 {
46 int go = dsu(id[i]);
47 if(go < A)
48 {
49 sizev[go]--;
50 if(!sizev[go])
51 {
52 root[go] = go + 1;
53 }
54 }
55 else
56 {
57 v.push_back(a[i].second);
58 }
59 }
60
61 int ptr = 0;
62 for(int i = B - 1; i >= 0; i--)
63 {
64 int rem = x;
65 while(ptr < v.size() and rem and Y[i] > v[ptr])
66 {
67 rem--;
68 ptr++;
69 }
70 }
71 return ptr == v.size();
72 }
73
74 int putaway(int A, int B, int n, int X[], int Y[], int W[], int S[])
75 {
76 :: n = n;
77 :: A = A;
78 :: B = B;
79
80 sort(X, X + A);
81 sort(Y, Y + B);
82
83 for(int i = 0; i < A; i++)
84 :: X[i] = X[i];
85
86 for(int i = 0; i < B; i++)
87 :: Y[i] = Y[i];
88
89 for(int i = 0; i < n; i++)
90 {
91 a[i] = ii(S[i], W[i]);
92 }
93
94 sort(a, a + n);
95
96 for(int i = 0; i < n; i++)
97 swap(a[i].first, a[i].second);
98
99 for(int i = 0; i < n; i++)
100 {
101 id[i] = upper_bound(X, X + A, a[i].first) - X;
102 }
103
104 if(!f(n))
105 return -1;
106
107 int l = 1, r = n;
108 while(l < r)
109 {
110 int m = l + r >> 1;
CAPITOLUL 8. IOI 2013 946

111 if(f(m))
112 r = m;
113 else
114 l = m + 1;
115 }
116 return l;
117 }
118
119 // ---------------- begin grader ----------------
120
121 #define fail(s, x...) do { \
122 fprintf(stderr, s "\n", ## x); \
123 exit(1); \
124 } while(0)
125
126 #define MAX_A 50000
127 #define MAX_B 50000
128 #define MAX_T 1000000
129
130 static int XX[MAX_A];
131 static int YY[MAX_B];
132 static int WW[MAX_T];
133 static int SS[MAX_T];
134
135 int main()
136 {
137 auto t1 = clock();
138
139 std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);
140 std::freopen("robot.out", "w", stdout);
141
142 int A, B, T, i;
143 int res;
144
145 res = scanf("%d", &A);
146
147 if (res != 1) fail("Failed to read A from input file.");
148 if (A < 0 || A > MAX_A) fail("A is out of bounds.");
149
150 res = scanf("%d", &B);
151
152 if (res != 1) fail("Failed to read B from input file.");
153 if (B < 0 || B > MAX_B) fail("B is out of bounds.");
154
155 res = scanf("%d", &T);
156
157 if (res != 1) fail("Failed to read T from input file.");
158 if (T < 1 || T > MAX_T) fail("T is out of bounds.");
159
160 for (i = 0; i < A; i++)
161 {
162 res = scanf("%d", &XX[i]);
163
164 if (res != 1) fail("Failed to read data from input file.");
165 }
166
167 for (i = 0; i < B; i++)
168 {
169 res = scanf("%d", &YY[i]);
170
171 if (res != 1) fail("Failed to read data from input file.");
172 }
173
174 for (i = 0; i < T; i++)
175 {
176 res = scanf("%d%d", &WW[i], &SS[i]);
177
178 if (res != 2) fail("Failed to read data from input file.");
179 }
180
181 auto t2 = clock();
182
183 int answer = putaway(A, B, T, XX, YY, WW, SS);
184
185 auto t3 = clock();
186
CAPITOLUL 8. IOI 2013 947

187 printf("%d\n", answer);


188
189 auto t4 = clock();
190
191 // reset console output
192 freopen("CON", "w", stdout);
193
194 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
195 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
196 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
197
198 return 0;
199 }
200
201 // ---------------- end grader ----------------
202
203 /*
204 t2-t1 = 2.005
205 t3-t2 = 1.218
206 t4-t3 = 0
207
208 Process returned 0 (0x0) execution time : 3.255 s
209 Press any key to continue.
210
211 argc = 4
212 checker
213 ../tests/st5-random-highS-highW-clusters.in
214 robot.out
215 ../tests/st5-random-highS-highW-clusters.out
216 ----------------------
217 1
218 Correct
219
220 Process returned 0 (0x0) execution time : 0.031 s
221 Press any key to continue.
222 */

Listing 8.5.4: robot-96364.cpp


1 // https://oj.uz/submission/96364 468 ms 27360 KB
2
3 #include "robots.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 typedef long long ll;
9 const int maxt = 1e6+50;
10
11 vector<pair<int,int>> tw,ts,w;
12 int t;
13 bool used[maxt];
14
15 bool can(int md)
16 {
17 for(int x=0;x<t;x++)
18 used[x] = 0;
19 int i = 0,j = 0;
20 for(int x=0;x<w.size();x++)
21 {
22 if(w[x].second == 1)
23 {
24 int cur = w[x].first;
25 int use = 0;
26 while(use < md && j < t && cur > ts[j].first)
27 {
28 if(used[ts[j].second])
29 {
30 j++;
31 continue;
32 }
33 used[ts[j].second] = 1;
34 j++;
35 use++;
36 }
CAPITOLUL 8. IOI 2013 948

37 }
38 else
39 {
40 int cur = w[x].first;
41 int use = 0;
42 while(use < md && i < t && cur > tw[i].first)
43 {
44 if(used[tw[i].second])
45 {
46 i++;
47 continue;
48 }
49 used[tw[i].second] = 1;
50 i++;
51 use++;
52 }
53 }
54 }
55
56 for(int x=0;x<t;x++)
57 if(!used[x])
58 return 0;
59 return 1;
60 }
61
62 int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
63 {
64 t = T;
65 for(int i=0;i<A;i++)w.push_back({X[i],0});
66 for(int i=0;i<B;i++)w.push_back({Y[i],1});
67 for(int i=0;i<T;i++)tw.push_back({W[i],i});
68 for(int i=0;i<T;i++)ts.push_back({S[i],i});
69
70 sort(w.begin(),w.end());
71 sort(tw.begin(),tw.end());
72 sort(ts.begin(),ts.end());
73
74 int md,lo=0,hi=T,ans=-1;
75 while(lo <= hi)
76 {
77 md = (lo+hi)/2;
78 if(can(md))
79 {
80 ans = md;
81 hi = md-1;
82 }
83 else
84 lo = md+1;
85 }
86 return ans;
87 }
88
89 // ---------------- begin grader ----------------
90
91 #define fail(s, x...) do { \
92 fprintf(stderr, s "\n", ## x); \
93 exit(1); \
94 } while(0)
95
96 #define MAX_A 50000
97 #define MAX_B 50000
98 #define MAX_T 1000000
99
100 static int X[MAX_A];
101 static int Y[MAX_B];
102 static int W[MAX_T];
103 static int S[MAX_T];
104
105 int main()
106 {
107 auto t1 = clock();
108
109 std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);
110 std::freopen("robot.out", "w", stdout);
111
112 int A, B, T, i;
CAPITOLUL 8. IOI 2013 949

113 int res;


114
115 res = scanf("%d", &A);
116
117 if (res != 1) fail("Failed to read A from input file.");
118 if (A < 0 || A > MAX_A) fail("A is out of bounds.");
119
120 res = scanf("%d", &B);
121
122 if (res != 1) fail("Failed to read B from input file.");
123 if (B < 0 || B > MAX_B) fail("B is out of bounds.");
124
125 res = scanf("%d", &T);
126
127 if (res != 1) fail("Failed to read T from input file.");
128 if (T < 1 || T > MAX_T) fail("T is out of bounds.");
129
130 for (i = 0; i < A; i++)
131 {
132 res = scanf("%d", &X[i]);
133
134 if (res != 1) fail("Failed to read data from input file.");
135 }
136
137 for (i = 0; i < B; i++)
138 {
139 res = scanf("%d", &Y[i]);
140
141 if (res != 1) fail("Failed to read data from input file.");
142 }
143
144 for (i = 0; i < T; i++)
145 {
146 res = scanf("%d%d", &W[i], &S[i]);
147
148 if (res != 2) fail("Failed to read data from input file.");
149 }
150
151 auto t2 = clock();
152
153 int answer = putaway(A, B, T, X, Y, W, S);
154
155 auto t3 = clock();
156
157 printf("%d\n", answer);
158
159 auto t4 = clock();
160
161 // reset console output
162 freopen("CON", "w", stdout);
163
164 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
165 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
166 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
167
168 return 0;
169 }
170
171 // ---------------- end grader ----------------
172
173 /*
174 t2-t1 = 2
175 t3-t2 = 2.453
176 t4-t3 = 0
177
178 Process returned 0 (0x0) execution time : 4.484 s
179 Press any key to continue.
180
181 argc = 4
182 checker
183 ../tests/st5-random-highS-highW-clusters.in
184 robot.out
185 ../tests/st5-random-highS-highW-clusters.out
186 ----------------------
187 1
188 Correct
CAPITOLUL 8. IOI 2013 950

189
190 Process returned 0 (0x0) execution time : 0.031 s
191 Press any key to continue.
192 */

Listing 8.5.5: checkerRobot.cpp


1 // https://oj.uz/submission/96364 468 ms 27360 KB
2
3 #include "robots.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 typedef long long ll;
9 const int maxt = 1e6+50;
10
11 vector<pair<int,int>> tw,ts,w;
12 int t;
13 bool used[maxt];
14
15 bool can(int md)
16 {
17 for(int x=0;x<t;x++)
18 used[x] = 0;
19 int i = 0,j = 0;
20 for(int x=0;x<w.size();x++)
21 {
22 if(w[x].second == 1)
23 {
24 int cur = w[x].first;
25 int use = 0;
26 while(use < md && j < t && cur > ts[j].first)
27 {
28 if(used[ts[j].second])
29 {
30 j++;
31 continue;
32 }
33 used[ts[j].second] = 1;
34 j++;
35 use++;
36 }
37 }
38 else
39 {
40 int cur = w[x].first;
41 int use = 0;
42 while(use < md && i < t && cur > tw[i].first)
43 {
44 if(used[tw[i].second])
45 {
46 i++;
47 continue;
48 }
49 used[tw[i].second] = 1;
50 i++;
51 use++;
52 }
53 }
54 }
55
56 for(int x=0;x<t;x++)
57 if(!used[x])
58 return 0;
59 return 1;
60 }
61
62 int putaway(int A, int B, int T, int X[], int Y[], int W[], int S[])
63 {
64 t = T;
65 for(int i=0;i<A;i++)w.push_back({X[i],0});
66 for(int i=0;i<B;i++)w.push_back({Y[i],1});
67 for(int i=0;i<T;i++)tw.push_back({W[i],i});
68 for(int i=0;i<T;i++)ts.push_back({S[i],i});
CAPITOLUL 8. IOI 2013 951

69
70 sort(w.begin(),w.end());
71 sort(tw.begin(),tw.end());
72 sort(ts.begin(),ts.end());
73
74 int md,lo=0,hi=T,ans=-1;
75 while(lo <= hi)
76 {
77 md = (lo+hi)/2;
78 if(can(md))
79 {
80 ans = md;
81 hi = md-1;
82 }
83 else
84 lo = md+1;
85 }
86 return ans;
87 }
88
89 // ---------------- begin grader ----------------
90
91 #define fail(s, x...) do { \
92 fprintf(stderr, s "\n", ## x); \
93 exit(1); \
94 } while(0)
95
96 #define MAX_A 50000
97 #define MAX_B 50000
98 #define MAX_T 1000000
99
100 static int X[MAX_A];
101 static int Y[MAX_B];
102 static int W[MAX_T];
103 static int S[MAX_T];
104
105 int main()
106 {
107 auto t1 = clock();
108
109 std::freopen("../tests/st5-random-highS-highW-clusters.in", "r", stdin);
110 std::freopen("robot.out", "w", stdout);
111
112 int A, B, T, i;
113 int res;
114
115 res = scanf("%d", &A);
116
117 if (res != 1) fail("Failed to read A from input file.");
118 if (A < 0 || A > MAX_A) fail("A is out of bounds.");
119
120 res = scanf("%d", &B);
121
122 if (res != 1) fail("Failed to read B from input file.");
123 if (B < 0 || B > MAX_B) fail("B is out of bounds.");
124
125 res = scanf("%d", &T);
126
127 if (res != 1) fail("Failed to read T from input file.");
128 if (T < 1 || T > MAX_T) fail("T is out of bounds.");
129
130 for (i = 0; i < A; i++)
131 {
132 res = scanf("%d", &X[i]);
133
134 if (res != 1) fail("Failed to read data from input file.");
135 }
136
137 for (i = 0; i < B; i++)
138 {
139 res = scanf("%d", &Y[i]);
140
141 if (res != 1) fail("Failed to read data from input file.");
142 }
143
144 for (i = 0; i < T; i++)
CAPITOLUL 8. IOI 2013 952

145 {
146 res = scanf("%d%d", &W[i], &S[i]);
147
148 if (res != 2) fail("Failed to read data from input file.");
149 }
150
151 auto t2 = clock();
152
153 int answer = putaway(A, B, T, X, Y, W, S);
154
155 auto t3 = clock();
156
157 printf("%d\n", answer);
158
159 auto t4 = clock();
160
161 // reset console output
162 freopen("CON", "w", stdout);
163
164 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
165 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
166 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
167
168 return 0;
169 }
170
171 // ---------------- end grader ----------------
172
173 /*
174 t2-t1 = 2
175 t3-t2 = 2.453
176 t4-t3 = 0
177
178 Process returned 0 (0x0) execution time : 4.484 s
179 Press any key to continue.
180
181 argc = 4
182 checker
183 ../tests/st5-random-highS-highW-clusters.in
184 robot.out
185 ../tests/st5-random-highS-highW-clusters.out
186 ----------------------
187 1
188 Correct
189
190 Process returned 0 (0x0) execution time : 0.031 s
191 Press any key to continue.
192 */

8.5.3 *Rezolvare detaliat 

8.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 gura urmatoare.
CAPITOLUL 8. IOI 2013 953

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 ecare segment de strad .
în continuare veµi primi o serie de E evenimente din unul din urm toarele dou  tipuri:
ˆ change, care modic  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 ecare 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 modicare apar: num rul de wombaµi pe segmentul de top din drumul
vertical 0 se modic  în 5, iar num rul de wombaµi din segmentul de mijloc al drumului
orizontal 1 se modic  î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 8. IOI 2013 954

Implementare
Va trebui sa submitaµi un ³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  conguraµia iniµiala a h rµii, ³i v  permite s  iniµializaµi eventualele
variabile globale ³i structuri de date. Va  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  apelat  atunci când num rul de wombaµi se modic  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 a  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  apelat  când num rul de wombaµi se modic  pe pe segmentul de drum
vertical dintre intersecµiile (P, Q) ³i (P + 1, Q).
Parametrii
CAPITOLUL 8. IOI 2013 955

ˆ P : Indic  între care dou  drumuri orizontale se a  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  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 ecare 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  apeluri c tre changeH() sau changeV()
3 16 R, C & 100, ³i vor  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 ³ierul wombats.in, care va  î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 8. IOI 2013 956

ˆ ...
ˆ 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 ec rui eveniment trebuie s  e î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  e 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  deniµ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

8.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 dicult of the tasks. Apart from being conceptually dicult, 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 aord R
dierent rX, Y x tables. Instead of keeping rX, X  1x at the nest 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 8. IOI 2013 957

8.6.2 Coduri surs 

Listing 8.6.1: wombats-108194.cpp


1 // https://oj.uz/submission/108194 4263 ms 174140 KB
2
3 #include "wombats.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 const int r=5e3, c=200, bs=10, bc=r/bs, sts=1024;
9 int h[r][c-1], v[r][c], st[sts][c][c], o[c+1];
10
11 void upd(int l1, int r1, int i=1, int l2=0, int r2=bc-1)
12 {
13 if(l2==r2)
14 {
15 memset(st[i], 0x3f, sizeof(st[i]));
16 for(int j=0; j<c; ++j)
17 {
18 st[i][j][j]=0;
19 for(int k=l2*bs; k<(l2+1)*bs; ++k)
20 {
21 for(int l=1; l<c; ++l)
22 st[i][j][l]=min(st[i][j][l-1]+h[k][l-1],st[i][j][l]);
23 for(int l=c-1; l; --l)
24 st[i][j][l-1]=min(st[i][j][l]+h[k][l-1],st[i][j][l-1]);
25 for(int l=0; l<c; ++l)
26 st[i][j][l]+=v[k][l];
27 }
28 }
29 return;
30 }
31
32 int m2=(l2+r2)/2;
33 if(l1<=m2)
34 upd(l1, r1, 2*i, l2, m2);
35 if(m2<r1)
36 upd(l1, r1, 2*i+1, m2+1, r2);
37
38 memset(o, 0, 4*c);
39 for(int j1=0; j1<c; ++j1)
40 {
41 for(int j2=c-1; ~j2; --j2)
42 {
43 array<int, 2> d{INT_MAX, 0};
44 for(int k=o[j2]; k<=o[j2+1]; ++k)
45 d=min(array<int,2>{st[2*i][j1][k]+st[2*i+1][k][j2], -k},d);
46 st[i][j1][j2]=d[0];
47 o[j2]=-d[1];
48 }
49 }
50 }
51
52 void init(int r, int c, int h[5000][200], int v[5000][200])
53 {
54 memset(::h, 0x3f, sizeof(::h));
55 for(int i=0; i<r; ++i)
56 for(int j=0; j<c-1; ++j)
57 ::h[i][j]=h[i][j];
58 for(int i=0; i<r-1; ++i)
59 for(int j=0; j<c; ++j)
60 ::v[i][j]=v[i][j];
61 o[::c]=::c-1;
62 upd(0, bc-1);
63 }
64
65 void changeH(int p, int q, int w)
66 {
67 h[p][q]=w;
68 upd(p/bs, p/bs);
69 }
70
CAPITOLUL 8. IOI 2013 958

71 void changeV(int p, int q, int w)


72 {
73 v[p][q]=w;
74 upd(p/bs, p/bs);
75 }
76
77 int escape(int a, int b)
78 {
79 return st[1][a][b];
80 }
81
82 // ------------- begin grader --------------
83
84 #define fail(s, x...) do { \
85 fprintf(stderr, s "\n", ## x); \
86 exit(1); \
87 } while(0)
88
89 static int H[5000][200];
90 static int V[5000][200];
91
92 int main()
93 {
94 auto t1 = clock();
95
96 std::freopen("../tests/100x100-bottom-changes-many-queries.in","r",stdin);
97 std::freopen("wombats.out", "w", stdout);
98
99 int R, C, E, P, Q, W, V1, V2, event, i, j;
100 int res;
101
102 res = scanf("%d%d", &R, &C);
103
104 for (i = 0; i < R; ++i)
105 for (j = 0; j < C-1; ++j)
106 res = scanf("%d", &H[i][j]);
107
108
109 for (i = 0; i < R-1; ++i)
110 for (j = 0; j < C; ++j)
111 res = scanf("%d", &V[i][j]);
112
113 auto t2 = clock();
114
115 init(R, C, H, V);
116
117 res = scanf("%d", &E);
118
119 auto t3 = clock();
120
121 for (i = 0; i < E; i++)
122 {
123 res = scanf("%d", &event);
124
125 if (event == 1)
126 {
127 res = scanf("%d%d%d", &P, &Q, &W);
128
129 changeH(P, Q, W);
130 }
131 else
132 if (event == 2)
133 {
134 res = scanf("%d%d%d", &P, &Q, &W);
135
136 changeV(P, Q, W);
137 }
138 else
139 if (event == 3)
140 {
141 res = scanf("%d%d", &V1, &V2);
142
143 printf("%d\n", escape(V1, V2));
144 }
145 else
146 fail("Invalid event type.");
CAPITOLUL 8. IOI 2013 959

147 }
148
149 auto t4 = clock();
150
151 // reset console output
152 freopen("CON", "w", stdout);
153
154 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
155 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
156 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
157
158 return 0;
159 }
160
161 // ------------- end grader ----------------
162 /*
163 t2-t1 = 0.031
164 t3-t2 = 26.213
165 t4-t3 = 18.542
166
167 Process returned 0 (0x0) execution time : 44.841 s
168 Press any key to continue.
169 */

Listing 8.6.2: wombats-108196.cpp


1 // https://oj.uz/submission/108196 3395 ms 174124 KB
2
3 #pragma GCC optimize("O3")
4 #include "wombats.h"
5 #include <bits/stdc++.h>
6
7 using namespace std;
8
9 const int r=5e3, c=200, bs=10, bc=r/bs, sts=1024;
10 int h[r][c-1], v[r][c], st[sts][c][c], o[c+1];
11
12 void upd(int l1, int r1, int i=1, int l2=0, int r2=bc-1)
13 {
14 if(l2==r2)
15 {
16 memset(st[i], 0x3f, sizeof(st[i]));
17 for(int j=0; j<c; ++j)
18 {
19 st[i][j][j]=0;
20 for(int k=l2*bs; k<(l2+1)*bs; ++k)
21 {
22 for(int l=1; l<c; ++l)
23 st[i][j][l]=min(st[i][j][l-1]+h[k][l-1],st[i][j][l]);
24 for(int l=c-1; l; --l)
25 st[i][j][l-1]=min(st[i][j][l]+h[k][l-1],st[i][j][l-1]);
26 for(int l=0; l<c; ++l)
27 st[i][j][l]+=v[k][l];
28 }
29 }
30 return;
31 }
32
33 int m2=(l2+r2)/2;
34 if(l1<=m2)
35 upd(l1, r1, 2*i, l2, m2);
36 if(m2<r1)
37 upd(l1, r1, 2*i+1, m2+1, r2);
38
39 memset(o, 0, 4*c);
40
41 for(int j1=0; j1<c; ++j1)
42 {
43 for(int j2=c-1; ~j2; --j2)
44 {
45 array<int, 2> d{INT_MAX, 0};
46 for(int k=o[j2]; k<=o[j2+1]; ++k)
47 d=min(array<int,2>{st[2*i][j1][k]+st[2*i+1][k][j2], -k},d);
48 st[i][j1][j2]=d[0];
49 o[j2]=-d[1];
CAPITOLUL 8. IOI 2013 960

50 }
51 }
52 }
53
54 void init(int r, int c, int h[5000][200], int v[5000][200])
55 {
56 memset(::h, 0x3f, sizeof(::h));
57 for(int i=0; i<r; ++i)
58 for(int j=0; j<c-1; ++j)
59 ::h[i][j]=h[i][j];
60 for(int i=0; i<r-1; ++i)
61 for(int j=0; j<c; ++j)
62 ::v[i][j]=v[i][j];
63 o[::c]=::c-1;
64 upd(0, bc-1);
65 }
66
67 void changeH(int p, int q, int w)
68 {
69 h[p][q]=w;
70 upd(p/bs, p/bs);
71 }
72
73 void changeV(int p, int q, int w) {
74 v[p][q]=w;
75 upd(p/bs, p/bs);
76 }
77
78 int escape(int a, int b)
79 {
80 return st[1][a][b];
81 }
82
83 // ------------- begin grader --------------
84
85 #define fail(s, x...) do { \
86 fprintf(stderr, s "\n", ## x); \
87 exit(1); \
88 } while(0)
89
90 static int H[5000][200];
91 static int V[5000][200];
92
93 int main()
94 {
95 auto t1 = clock();
96
97 std::freopen("../tests/100x100-bottom-changes-many-queries.in", "r",stdin);
98 std::freopen("wombats.out", "w", stdout);
99
100 int R, C, E, P, Q, W, V1, V2, event, i, j;
101 int res;
102
103 res = scanf("%d%d", &R, &C);
104
105 for (i = 0; i < R; ++i)
106 for (j = 0; j < C-1; ++j)
107 res = scanf("%d", &H[i][j]);
108
109
110 for (i = 0; i < R-1; ++i)
111 for (j = 0; j < C; ++j)
112 res = scanf("%d", &V[i][j]);
113
114 auto t2 = clock();
115
116 init(R, C, H, V);
117
118 res = scanf("%d", &E);
119
120 auto t3 = clock();
121
122 for (i = 0; i < E; i++)
123 {
124 res = scanf("%d", &event);
125
CAPITOLUL 8. IOI 2013 961

126 if (event == 1)
127 {
128 res = scanf("%d%d%d", &P, &Q, &W);
129
130 changeH(P, Q, W);
131 }
132 else
133 if (event == 2)
134 {
135 res = scanf("%d%d%d", &P, &Q, &W);
136
137 changeV(P, Q, W);
138 }
139 else
140 if (event == 3)
141 {
142 res = scanf("%d%d", &V1, &V2);
143
144 printf("%d\n", escape(V1, V2));
145 }
146 else
147 fail("Invalid event type.");
148 }
149
150 auto t4 = clock();
151
152 // reset console output
153 freopen("CON", "w", stdout);
154
155 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
156 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
157 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
158
159 return 0;
160 }
161
162 // ------------- end grader ----------------
163 /*
164 t2-t1 = 0.041
165 t3-t2 = 9.078
166 t4-t3 = 8.622
167
168 Process returned 0 (0x0) execution time : 19.449 s
169 Press any key to continue.
170 */

Listing 8.6.3: wombats-118871.cpp


1 // https://oj.uz/submission/118871 3904 ms 183532 KB
2
3 #include "wombats.h"
4 #include <bits/stdc++.h>
5
6 using namespace std;
7
8 const int r=5e3, c=200, bs=10, bc=r/bs, sts=1024;
9 int h[r][c-1], v[r][c], st[sts][c][c], o[c+1];
10
11 void upd(int l1, int r1, int i=1, int l2=0, int r2=bc-1)
12 {
13 if(l2==r2)
14 {
15 memset(st[i], 0x3f, sizeof(st[i]));
16 for(int j=0; j<c; ++j)
17 {
18 st[i][j][j]=0;
19 for(int k=l2*bs; k<(l2+1)*bs; ++k)
20 {
21 for(int l=1; l<c; ++l)
22 st[i][j][l]=min(st[i][j][l-1]+h[k][l-1], st[i][j][l]);
23 for(int l=c-1; l; --l)
24 st[i][j][l-1]=min(st[i][j][l]+h[k][l-1],st[i][j][l-1]);
25 for(int l=0; l<c; ++l)
26 st[i][j][l]+=v[k][l];
27 }
CAPITOLUL 8. IOI 2013 962

28 }
29 return;
30 }
31
32 int m2=(l2+r2)/2;
33 if(l1<=m2)
34 upd(l1, r1, 2*i, l2, m2);
35 if(m2<r1)
36 upd(l1, r1, 2*i+1, m2+1, r2);
37 memset(o, 0, 4*c);
38 for(int j1=0; j1<c; ++j1)
39 {
40 for(int j2=c-1; ~j2; --j2)
41 {
42 array<int, 2> d{INT_MAX, 0};
43 for(int k=o[j2]; k<=o[j2+1]; ++k)
44 d=min(array<int, 2>{st[2*i][j1][k]+st[2*i+1][k][j2],-k},d);
45 st[i][j1][j2]=d[0];
46 o[j2]=-d[1];
47 }
48 }
49 }
50
51 void init(int r, int c, int h[5000][200], int v[5000][200])
52 {
53 memset(::h, 0x3f, sizeof(::h));
54 for(int i=0; i<r; ++i)
55 for(int j=0; j<c-1; ++j)
56 ::h[i][j]=h[i][j];
57 for(int i=0; i<r-1; ++i)
58 for(int j=0; j<c; ++j)
59 ::v[i][j]=v[i][j];
60 o[::c]=::c-1;
61 upd(0, bc-1);
62 }
63
64 void changeH(int p, int q, int w)
65 {
66 h[p][q]=w;
67 upd(p/bs, p/bs);
68 }
69
70 void changeV(int p, int q, int w)
71 {
72 v[p][q]=w;
73 upd(p/bs, p/bs);
74 }
75
76 int escape(int a, int b)
77 {
78 return st[1][a][b];
79 }
80
81 // ------------- begin grader --------------
82
83 #define fail(s, x...) do { \
84 fprintf(stderr, s "\n", ## x); \
85 exit(1); \
86 } while(0)
87
88 static int H[5000][200];
89 static int V[5000][200];
90
91 int main()
92 {
93 auto t1 = clock();
94
95 std::freopen("../tests/100x100-bottom-changes-many-queries.in","r",stdin);
96 std::freopen("wombats.out", "w", stdout) ;
97
98 int R, C, E, P, Q, W, V1, V2, event, i, j;
99 int res;
100
101 res = scanf("%d%d", &R, &C);
102
103 for (i = 0; i < R; ++i)
CAPITOLUL 8. IOI 2013 963

104 for (j = 0; j < C-1; ++j)


105 res = scanf("%d", &H[i][j]);
106
107
108 for (i = 0; i < R-1; ++i)
109 for (j = 0; j < C; ++j)
110 res = scanf("%d", &V[i][j]);
111
112 auto t2 = clock();
113
114 init(R, C, H, V);
115
116 res = scanf("%d", &E);
117
118 auto t3 = clock();
119
120 for (i = 0; i < E; i++)
121 {
122 res = scanf("%d", &event);
123
124 if (event == 1)
125 {
126 res = scanf("%d%d%d", &P, &Q, &W);
127
128 changeH(P, Q, W);
129 }
130 else
131 if (event == 2)
132 {
133 res = scanf("%d%d%d", &P, &Q, &W);
134
135 changeV(P, Q, W);
136 }
137 else
138 if (event == 3)
139 {
140 res = scanf("%d%d", &V1, &V2);
141
142 printf("%d\n", escape(V1, V2));
143 }
144 else
145 fail("Invalid event type.");
146 }
147
148 auto t4 = clock();
149
150 // reset console output
151 freopen("CON", "w", stdout);
152
153 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
154 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
155 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
156
157 return 0;
158 }
159
160 // ------------- end grader ----------------
161 /*
162 t2-t1 = 0.03
163 t3-t2 = 26.169
164 t4-t3 = 18.445
165
166 Process returned 0 (0x0) execution time : 47.283 s
167 Press any key to continue.
168 */

Listing 8.6.4: checkerWombats.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
CAPITOLUL 8. IOI 2013 964

8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/100x100-bottom-changes-many-queries.in",
14 (char*)"wombats.out",
15 (char*)"../tests/100x100-bottom-changes-many-queries.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("wombats", argc, argv);
24 compareRemainingLines();
25 }

8.6.3 *Rezolvare detaliat 


Capitolul 9

IOI 201234

9.1 Pebbling odometer


Problema 1 - Pebbling odometer 100 de puncte
Author: Michal Forisek

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 aam 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 specicate 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 identic  printr-o pereche de coordonate (rând, coloan ),
unde ecare coordonat  se a  î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  aat  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  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 
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  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  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
34
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).

965
CAPITOLUL 9. IOI 2012 966

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 modica 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  e 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 a  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 a  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 veric  dac  se a  pietricele
sau dac  se a  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) specic 
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  e sucient 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 sucient de repede. Lungimea execuµiei
este num rul de pa³i: ecare 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 9. IOI 2012 967

În primul exemplu, programul are dimensiunea 4 ³i lungimea execuµiei 4. În al doilea exemplu,


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 , ecare con-
µinând câte 4 pa³i (pebble davinci; border davinci; move; jump leonardo), ³i la nal,
pebble davinci ³i halt.
Subtask 1 [9 puncte]
La început se a  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  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 nal, î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 nal  a matricii este
irelevant .
Limite: Dimensiunea programului & 100, lungimea execuµiei & 200 000.

Subtask 4 [pana la 32 de puncte]


Se a  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 :
ˆ 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 ecare 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 ecare celul  trebuie s  e acela³i ca înainte de rularea programului.
Scorul acestui subtask depinde de dimensiunea programului P a programului trimis. Mai exact,
scorul t u va :
ˆ 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 ³ier pentru ecare subtask, respectând regulile de sintax  spe-
cicate mai sus. Fiecare ³ier trimis poate avea cel mult 5 MB. Pentru ecare subtask, codul
odometrului va  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 9. IOI 2012 968

submisie pentru task-ul x va  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
nal 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  scrise în formatul folosit pentru submit-uri
(de exemplu, unul din cele descrise mai sus).
Descrierea matricei va  dat  cu ajutorul urm torului format: ecare linie a ³ierului trebuie
s  conµin  trei numere R, C ³i P , semnicand faptul c  celula din randul R ³i coloana C conµine
P pietricele. Se presupune c  celulele ce nu sunt specicate în descrierea matriceise nu conµin
pietricele. Pentru exemplicare se consider  urm torul ³ier:

0 10 3
4 5 12

Matricea descris  de acest ³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 ³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 ³ierul GRID_FILE (implicit: matricrea
vid );
ˆ -s GRID_SIDE seteaz  dimensiunea matricei ca ind egal  cu GRID_SIDE  GRID_SIDE
(implicit 256, a³a cum este specicat în problem ); utilizarea unei matrice mai mici ar putea
 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

9.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 nding 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 9.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 9. IOI 2012 969

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

9.1.2 *Coduri surs 

9.1.3 *Rezolvare detaliat 

9.2 Parachute rings


Problema 2 - Parachute rings 100 de puncte
Author: Michal Forisek

O versiune timpurie ³i destul de sosticat  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
 u³or legate între ele, iar ecare inel poate  închis sau deschis din nou. Numim lanµ o secvenµ 
de unul sau mai multe inele legate, în care ecare 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
deniµie, un singur inel este, de asemenea, un lanµ.
CAPITOLUL 9. IOI 2012 970

Evident, întrucât un inel poate  legat nu numai cu dou , dar cu trei ³i chiar mai multe inele,
sunt posibile ³i alte conguraµ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 gura 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 conguraµ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 conguraµia curent . Mai
concret, trebui s  implementezii trei rutine.
ˆ Init(N) - la început aceasta se apeleaz  exact o singur  dat  pentru a aa num rul N de
inele disjuncte din conguraµia iniµial , numerotate de la 0 la N  1 (inclusiv).
ˆ Link(A, B) - cele dou  inele A ³i B vor  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 zice. Evident, apelurile
Link(A,B) ³i Link(B,A) sunt echivalente.
ˆ CountCritical() - returneaz  num rul de inele critice din conguraµ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 conguraµia prezentat  în gura de mai sus.
CAPITOLUL 9. IOI 2012 971

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 ³ier, denumit rings.c, rings.cpp sau rings.pas.
Acest ³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 ³ier.
CAPITOLUL 9. IOI 2012 972

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

9.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-nd d.s., etc.).

9.2.2 Coduri surs 

Listing 9.2.1: rings.cpp


1 /*
2 O(N+M+C) solution for Rings
3
4 N = number of vertices;
5 M = number of calls to Link;
6 C = number of calls to CountCritical.
7
8 Author: Giovanni Paolini
9 */
10
11 #include<ctime>
12 #include<iostream>
13
14 #include <cstdio>
15 #include <vector>
16 #include <cassert>
17
18 using namespace std;
19
20 int const MAXN = 1000000;
21
22 int n;
23 bool quadruplication = 0;
24
25 int numcycles = 0;
26 int cycle_length;// If numcycles==1, here we store the length of the only cycle
27
CAPITOLUL 9. IOI 2012 973

28 int other_endpoint[4][MAXN]; // -1 if the node is not an endpoint,


29 // otherwise the other endpoint
30 vector<int> neighbours[MAXN];
31
32 int destroyed[4]; // The destroyed node of each graph
33 // (only if quadruplication==TRUE)
34
35 int degree[4][MAXN];
36 bool islinear[4]; // Whether each graph is linear or not
37
38 void Init(int k)
39 {
40 n = k;
41 for (int i=0; i<n; ++i)
42 {
43 other_endpoint[0][i] = i;
44 }
45 }
46
47 void add_new_edge(int x, int y)
48 {
49 // Adds an edge in case of quadruplication
50
51 for (int i=0; i<4; ++i)
52 {
53 // Operating on graph i
54 if ( !islinear[i] ) continue;
55 if ( x == destroyed[i] || y == destroyed[i] ) continue;
56
57 degree[i][x]++;
58 degree[i][y]++;
59
60 assert( degree[i][x] <= 3 && degree[i][y] <= 3 );
61
62 if ( degree[i][x] == 3 || degree[i][y] == 3 )
63 {
64 islinear[i] = 0;
65 continue;
66 }
67
68 if ( other_endpoint[i][x] == y )
69 {
70 // Cycle!
71
72 islinear[i] = 0;
73 continue;
74 }
75
76 int a = other_endpoint[i][x];
77 int b = other_endpoint[i][y];
78
79 other_endpoint[i][x] = -1;
80 other_endpoint[i][y] = -1;
81 other_endpoint[i][a] = b;
82 other_endpoint[i][b] = a;
83 }
84 }
85
86 void quadruplicate (int x)
87 {
88 quadruplication = 1;
89
90 destroyed[0] = x;
91 destroyed[1] = neighbours[x][0];
92 destroyed[2] = neighbours[x][1];
93 destroyed[3] = neighbours[x][2];
94
95 for (int i=0; i<4; ++i)
96 {
97 for (int j=0; j<n; ++j)
98 {
99 other_endpoint[i][j] = j;
100 degree[i][j] = 0;
101 }
102 }
103
CAPITOLUL 9. IOI 2012 974

104 for (int i=0; i<4; ++i)


105 {
106 islinear[i] = 1;
107 }
108
109 for (int k=0; k<n; ++k)
110 {
111 for (vector<int>::iterator j = neighbours[k].begin();
112 j != neighbours[k].end(); ++j)
113 {
114
115 if ( k < ( *j) ) add_new_edge( k, ( *j) );
116
117 }
118 }
119
120 }
121
122 void Link(int xx, int yy)
123 {
124
125 int x = xx;
126 int y = yy;
127
128 if ( quadruplication == 0 )
129 {
130
131 neighbours[x].push_back(y);
132 neighbours[y].push_back(x);
133
134 degree[0][x]++;
135 degree[0][y]++;
136
137 // If a node has degree 3, only it or its neighbours can be critical.
138 // So we can keep track of each of the 4 graphs obtained by removing
139 // one of these 4 nodes.
140 if ( degree[0][x] == 3 )
141 {
142 quadruplicate(x);
143 return;
144 }
145
146 if ( degree[0][y] == 3 )
147 {
148 quadruplicate(y);
149 return;
150 }
151
152 // If their degree is < 3, then they were necessarily endpoints!
153
154 if ( other_endpoint[0][x] != y )
155 { // A longer path is formed
156 int a = other_endpoint[0][x];
157 int b = other_endpoint[0][y];
158
159 other_endpoint[0][x] = -1;
160 other_endpoint[0][y] = -1;
161 other_endpoint[0][a] = b;
162 other_endpoint[0][b] = a;
163 }
164 else
165 { // A cycle is formed
166 numcycles++;
167 if ( numcycles == 1 )
168 {
169
170 int length = 1;
171 int previous_node = x;
172 int current_node = neighbours[x][0];
173
174 while ( current_node != x )
175 {
176 int possibility = neighbours[ current_node ][0];
177 if ( possibility == previous_node )
178 possibility = neighbours[ current_node ][1];
179
CAPITOLUL 9. IOI 2012 975

180 previous_node = current_node;


181 current_node = possibility;
182
183 length++;
184 }
185
186 cycle_length = length;
187 }
188 }
189 }
190
191 else
192 {
193 add_new_edge(x,y);
194 }
195 }
196
197 int CountCritical()
198 {
199 if ( quadruplication == 0 )
200 {
201 switch (numcycles)
202 {
203 case 0:
204 return n;
205 case 1:
206 return cycle_length;
207 default:
208 return 0;
209 }
210 }
211 else
212 {
213 int answer = 0;
214 for (int i=0; i<4; ++i)
215 {
216 if ( islinear[i] ) answer++;
217 }
218 return answer;
219 }
220 }
221
222 // ----------------- begin grader ------------------
223
224 #define inbuf_len 1 << 16
225 #define outbuf_len 1 << 16
226
227 void Init(int N);
228 int CountCritical();
229 void Link(int a, int b);
230
231 int main()
232 {
233 auto t1 = clock();
234
235 std::freopen("../input/input54.txt", "r", stdin);
236 std::freopen("rings.out", "w", stdout);
237
238 int tmp;
239
240 /* Set input and output buffering */
241 char *inbuf, *outbuf;
242 inbuf = (char*) malloc(inbuf_len * sizeof(char));
243 outbuf = (char*) malloc(outbuf_len * sizeof(char));
244 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
245 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
246
247 auto t2 = clock();
248
249 int N, L;
250 tmp = scanf("%d %d", &N, &L);
251 assert(tmp == 2);
252 Init(N);
253
254 auto t3 = clock();
255
CAPITOLUL 9. IOI 2012 976

256 int i;
257 for (i = 0; i < L; i++)
258 {
259 int A, B;
260 tmp = scanf("%d", &A);
261
262 if (A == -1)
263 {
264 int critical;
265 critical = CountCritical();
266 printf("%d\n", critical);
267 }
268 else
269 {
270 tmp = scanf("%d", &B);
271 assert(tmp == 1);
272 Link(A, B);
273 }
274 }
275
276 auto t4 = clock();
277
278 // reset console output
279 freopen("CON", "w", stdout);
280
281 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
282 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
283 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
284
285 return 0;
286
287 }
288
289 // ----------------- end grader ------------------
290 /*
291 t2-t1 = 0
292 t3-t2 = 0.016
293 t4-t3 = 3.612
294
295 Process returned 0 (0x0) execution time : 4.346 s
296 Press any key to continue.
297 */

Listing 9.2.2: rings-13540.cpp


1 // https://oj.uz/submission/13540 1111 ms 56956 KB
2
3 #include<ctime>
4 #include<iostream>
5
6 #include<cassert>
7
8 using namespace std;
9
10 int n, cyc, Res;
11 int E[1010000][3], deg[5][1010000], chk[5], Num[5];
12 int par[5][1010000], SZ[1010000];
13 void Init(int N_)
14 {
15 n = N_;
16 Res = n;
17 int i;
18 for (i = 1; i <= n; i++)SZ[i] = 1, par[0][i] = i;
19 }
20
21 int Find(int ck, int a)
22 {
23 if (par[ck][a] == a)return a;
24 return par[ck][a] = Find(ck, par[ck][a]);
25 }
26
27 void Add(int ck, int a, int b)
28 {
29 if (chk[ck])return;
30 if (ck && (Num[ck] == a || Num[ck] == b))return;
CAPITOLUL 9. IOI 2012 977

31 if (!ck)
32 {
33 E[a][deg[0][a]] = b, E[b][deg[0][b]] = a;
34 }
35 deg[ck][a]++, deg[ck][b]++;
36 if (ck && (deg[ck][a] >= 3 || deg[ck][b] >= 3))
37 {
38 chk[ck] = 1;
39 return;
40 }
41 a = Find(ck, a), b = Find(ck, b);
42 if (a == b)
43 {
44 if (!ck)
45 {
46 cyc++;
47 if (cyc == 1)Res = SZ[a];
48 }
49 else chk[ck] = 1;
50 }
51 else
52 {
53 par[ck][a] = b;
54 if (!ck)
55 {
56 SZ[b] += SZ[a];
57 SZ[a] = 0;
58 }
59 }
60 }
61
62 void Make(int a)
63 {
64 int i, j, k, x;
65 Num[1] = a;
66 for (i = 0; i < 3; i++)
67 {
68 Num[2 + i] = E[a][i];
69 }
70 chk[0] = 1;
71 for (k = 1; k <= 4; k++)
72 {
73 for (i = 1; i <= n; i++)par[k][i] = i;
74 for (i = 1; i <= n; i++)
75 {
76 for (j = 0; j < deg[0][i]; j++){
77 x = E[i][j];
78 if (i == Num[k] || x == Num[k] || x > i)continue;
79 Add(k, i, x);
80 }
81 }
82 }
83 }
84
85 void Link(int A, int B)
86 {
87 if (!Res)return;
88 A++, B++;
89 if (!chk[0])
90 {
91 if (cyc == 2)Res = 0;
92 Add(0, A, B);
93 if (deg[0][A] == 3)
94 {
95 Make(A);
96 }
97 else
98 if (deg[0][B] == 3)
99 {
100 Make(B);
101 }
102 }
103 else
104 {
105 int i;
106 for (i = 1; i <= 4; i++)Add(i, A, B);
CAPITOLUL 9. IOI 2012 978

107 }
108 }
109
110 int CountCritical()
111 {
112 if (!chk[0])
113 {
114 if (cyc == 2)Res = 0;
115 return Res;
116 }
117 int i, r = 0;
118 for (i = 1; i <= 4; i++)if (!chk[i])r++;
119 return r;
120 }
121
122 // ----------------- begin grader ------------------
123
124 #define inbuf_len 1 << 16
125 #define outbuf_len 1 << 16
126
127 void Init(int N);
128 int CountCritical();
129 void Link(int a, int b);
130
131 int main()
132 {
133 auto t1 = clock();
134
135 std::freopen("../input/input54.txt", "r", stdin);
136 std::freopen("rings.out", "w", stdout);
137
138 int tmp;
139
140 /* Set input and output buffering */
141 char *inbuf, *outbuf;
142 inbuf = (char*) malloc(inbuf_len * sizeof(char));
143 outbuf = (char*) malloc(outbuf_len * sizeof(char));
144 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
145 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
146
147 auto t2 = clock();
148
149 int N, L;
150 tmp = scanf("%d %d", &N, &L);
151 assert(tmp == 2);
152 Init(N);
153
154 auto t3 = clock();
155
156 int i;
157 for (i = 0; i < L; i++)
158 {
159 int A, B;
160 tmp = scanf("%d", &A);
161
162 if (A == -1)
163 {
164 int critical;
165 critical = CountCritical();
166 printf("%d\n", critical);
167 }
168 else
169 {
170 tmp = scanf("%d", &B);
171 assert(tmp == 1);
172 Link(A, B);
173 }
174 }
175
176 auto t4 = clock();
177
178 // reset console output
179 freopen("CON", "w", stdout);
180
181 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
182 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 9. IOI 2012 979

183 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
184
185 return 0;
186
187 }
188
189 // ----------------- end grader ------------------
190 /*
191 t2-t1 = 0
192 t3-t2 = 0.015
193 t4-t3 = 3.563
194
195 Process returned 0 (0x0) execution time : 3.656 s
196 Press any key to continue.
197 */

Listing 9.2.3: rings-49004.cpp


1 // https://oj.uz/submission/49004 832 ms 50460 KB
2
3 #include <bits/stdc++.h>
4
5 using namespace std;
6
7 typedef pair<int,int> pp;
8
9 vector<pp> links;
10
11 int n;
12
13 struct simulator
14 {
15 int deg [1000010];
16 int oppo[1000010];
17 bool crit;
18 int R;
19
20 void init(int rem)
21 {
22 R=rem;
23 for(int i=0; i<n; ++i) deg[i]=0, oppo[i]=i;
24 crit=true;
25 for(auto& q:links)
26 {
27 update(q.first, q.second);
28 }
29 }
30
31 void update(int a,int b)
32 {
33 if(!crit) return;
34 if(a!=R && b!=R)
35 {
36 ++deg[a], ++deg[b];
37 int p=oppo[a], q=oppo[b];
38 if(b==p)
39 {
40 crit=false;
41 return;
42 }
43 oppo[p]=q; oppo[q]=p;
44 if(deg[a]>=3 || deg[b]>=3)
45 {
46 crit=false;
47 }
48 }
49 }
50 };
51
52 int par[1000010];
53 int sz [1000010];
54 int cyc_cnt;
55 int cyc_size;
56
57 int R(int x)
CAPITOLUL 9. IOI 2012 980

58 {
59 return (par[x]==x)?x:(par[x]=R(par[x]));
60 }
61
62 void join(int a,int b)
63 {
64 a=R(a); b=R(b);
65 if(a == b)
66 {
67 ++cyc_cnt;
68 cyc_size += sz[a];
69 }
70 else
71 {
72 sz[b] += sz[a];
73 par[a] = b;
74 }
75 }
76
77 void Init(int n_)
78 {
79 n=n_;
80 for(int i=0; i<n; ++i) par[i]=i, sz[i]=1;
81 }
82
83 int first_deg[1000010];
84 bool isSim;
85
86 simulator ss[4];
87
88 void Link(int A, int B)
89 {
90 if(!isSim)
91 {
92 ++first_deg[A]; ++first_deg[B];
93 links.push_back({A, B});
94 join(A,B);
95
96 int thr = -1;
97 if(first_deg[A] == 3) thr = A;
98 if(first_deg[B] == 3) thr = B;
99
100 if(thr != -1)
101 {
102 vector<int> inj;
103 for(pp& l:links)
104 {
105 int a,b; tie(a,b)=l;
106 if(a==thr) inj.push_back(b);
107 if(b==thr) inj.push_back(a);
108 }
109 ss[3].init(thr);
110 for(int i=0; i<3; ++i) ss[i].init(inj[i]);
111 isSim = true;
112 }
113 }
114 else
115 {
116 for(int i=0; i<4; ++i)
117 ss[i].update(A, B);
118 }
119 }
120
121 int CountCritical()
122 {
123 if(!isSim)
124 {
125 if(cyc_cnt >= 2)
126 return 0;
127 else return cyc_cnt ? cyc_size : n;
128 }
129 int ans=0;
130 for(int i=0; i<4; ++i) if(ss[i].crit) ++ans;
131 return ans;
132 }
133
CAPITOLUL 9. IOI 2012 981

134 // ----------------- begin grader ------------------


135
136 #define inbuf_len 1 << 16
137 #define outbuf_len 1 << 16
138
139 void Init(int N);
140 int CountCritical();
141 void Link(int a, int b);
142
143 int main()
144 {
145 auto t1 = clock();
146
147 std::freopen("../input/input54.txt", "r", stdin);
148 std::freopen("rings.out", "w", stdout);
149
150 int tmp;
151
152 /* Set input and output buffering */
153 char *inbuf, *outbuf;
154 inbuf = (char*) malloc(inbuf_len * sizeof(char));
155 outbuf = (char*) malloc(outbuf_len * sizeof(char));
156 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
157 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
158
159 auto t2 = clock();
160
161 int N, L;
162 tmp = scanf("%d %d", &N, &L);
163 assert(tmp == 2);
164 Init(N);
165
166 auto t3 = clock();
167
168 int i;
169 for (i = 0; i < L; i++)
170 {
171 int A, B;
172 tmp = scanf("%d", &A);
173
174 if (A == -1)
175 {
176 int critical;
177 critical = CountCritical();
178 printf("%d\n", critical);
179 }
180 else
181 {
182 tmp = scanf("%d", &B);
183 assert(tmp == 1);
184 Link(A, B);
185 }
186 }
187
188 auto t4 = clock();
189
190 // reset console output
191 freopen("CON", "w", stdout);
192
193 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
194 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
195 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
196
197 return 0;
198 }
199
200 // ----------------- end grader ------------------
201 /*
202 t2-t1 = 0
203 t3-t2 = 0.031
204 t4-t3 = 3.5
205
206 Process returned 0 (0x0) execution time : 3.609 s
207 Press any key to continue.
208 */
CAPITOLUL 9. IOI 2012 982

Listing 9.2.4: rings-62499.cpp


1 // https://oj.uz/submission/62499 1040 ms 57172 KB
2
3 #include<ctime>
4 #include<iostream>
5
6 #include<cassert>
7
8 using namespace std;
9
10 int n, cyc, Res;
11 int E[1010000][3], deg[5][1010000], chk[5], Num[5];
12 int par[5][1010000], SZ[1010000];
13 void Init(int N_)
14 {
15 n = N_;
16 Res = n;
17 int i;
18 for (i = 1; i <= n; i++)SZ[i] = 1, par[0][i] = i;
19 }
20
21 int Find(int ck, int a)
22 {
23 if (par[ck][a] == a)return a;
24 return par[ck][a] = Find(ck, par[ck][a]);
25 }
26
27 void Add(int ck, int a, int b)
28 {
29 if (chk[ck])return;
30 if (ck && (Num[ck] == a || Num[ck] == b))return;
31 if (!ck)
32 {
33 E[a][deg[0][a]] = b, E[b][deg[0][b]] = a;
34 }
35 deg[ck][a]++, deg[ck][b]++;
36 if (ck && (deg[ck][a] >= 3 || deg[ck][b] >= 3))
37 {
38 chk[ck] = 1;
39 return;
40 }
41 a = Find(ck, a), b = Find(ck, b);
42 if (a == b)
43 {
44 if (!ck)
45 {
46 cyc++;
47 if (cyc == 1)Res = SZ[a];
48 }
49 else chk[ck] = 1;
50 }
51 else
52 {
53 par[ck][a] = b;
54 if (!ck)
55 {
56 SZ[b] += SZ[a];
57 SZ[a] = 0;
58 }
59 }
60 }
61
62 void Make(int a)
63 {
64 int i, j, k, x;
65 Num[1] = a;
66 for (i = 0; i < 3; i++)
67 {
68 Num[2 + i] = E[a][i];
69 }
70 chk[0] = 1;
71 for (k = 1; k <= 4; k++)
72 {
73 for (i = 1; i <= n; i++)par[k][i] = i;
74 for (i = 1; i <= n; i++)
CAPITOLUL 9. IOI 2012 983

75 {
76 for (j = 0; j < deg[0][i]; j++)
77 {
78 x = E[i][j];
79 if (i == Num[k] || x == Num[k] || x > i)continue;
80 Add(k, i, x);
81 }
82 }
83 }
84 }
85
86 void Link(int A, int B)
87 {
88 if (!Res)return;
89 A++, B++;
90 if (!chk[0])
91 {
92 if (cyc == 2)Res = 0;
93 Add(0, A, B);
94 if (deg[0][A] == 3)
95 {
96 Make(A);
97 }
98 else
99 if (deg[0][B] == 3)
100 {
101 Make(B);
102 }
103 }
104 else
105 {
106 int i;
107 for (i = 1; i <= 4; i++)Add(i, A, B);
108 }
109 }
110
111 int CountCritical()
112 {
113 if (!chk[0])
114 {
115 if (cyc == 2)Res = 0;
116 return Res;
117 }
118 int i, r = 0;
119 for (i = 1; i <= 4; i++)if (!chk[i])r++;
120 return r;
121 }
122
123 // ----------------- begin grader ------------------
124
125 #define inbuf_len 1 << 16
126 #define outbuf_len 1 << 16
127
128 void Init(int N);
129 int CountCritical();
130 void Link(int a, int b);
131
132 int main()
133 {
134 auto t1 = clock();
135
136 std::freopen("../input/input54.txt", "r", stdin);
137 std::freopen("rings.out", "w", stdout);
138
139 int tmp;
140
141 /* Set input and output buffering */
142 char *inbuf, *outbuf;
143 inbuf = (char*) malloc(inbuf_len * sizeof(char));
144 outbuf = (char*) malloc(outbuf_len * sizeof(char));
145 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
146 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
147
148 auto t2 = clock();
149
150 int N, L;
CAPITOLUL 9. IOI 2012 984

151 tmp = scanf("%d %d", &N, &L);


152 assert(tmp == 2);
153 Init(N);
154
155 auto t3 = clock();
156
157 int i;
158 for (i = 0; i < L; i++)
159 {
160 int A, B;
161 tmp = scanf("%d", &A);
162
163 if (A == -1)
164 {
165 int critical;
166 critical = CountCritical();
167 printf("%d\n", critical);
168 }
169 else
170 {
171 tmp = scanf("%d", &B);
172 assert(tmp == 1);
173 Link(A, B);
174 }
175 }
176
177 auto t4 = clock();
178
179 // reset console output
180 freopen("CON", "w", stdout);
181
182 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
183 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
184 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
185
186 return 0;
187
188 }
189
190 // ----------------- end grader ------------------
191 /*
192 t2-t1 = 0
193 t3-t2 = 0.031
194 t4-t3 = 3.468
195
196 Process returned 0 (0x0) execution time : 3.546 s
197 Press any key to continue.
198 */

Listing 9.2.5: rings-223749.cpp


1 // https://oj.uz/submission/223749 861 ms 48336 KB
2
3 #include<bits/stdc++.h>
4
5 using namespace std;
6
7 #define REP(i, n) for(int i = 0; i < n; i++)
8
9 template<class T> int size(T && a) { return (int) a.size(); }
10
11 struct Graph
12 {
13 vector<int> rep, deg;
14 int excluded = -1;
15
16 int find(int x)
17 {
18 return rep[x] < 0 ? x : rep[x] = find(rep[x]);
19 }
20
21 int bicomp = -1;
22 int get_cycle()
23 {
24 return -rep[find(bicomp)];
CAPITOLUL 9. IOI 2012 985

25 }
26
27 void join(int x, int y)
28 {
29 x = find(x), y = find(y);
30 if(x == y)
31 {
32 if(bicomp == -1) bicomp = x;
33 else bicomp = -2;
34 return;
35 }
36 if(rep[x] > rep[y]) swap(x, y);
37 rep[x] += rep[y];
38 rep[y] = x;
39 }
40
41 int max_deg = 0;
42 void add_edge(int a, int b)
43 {
44 if(a == excluded || b == excluded)
45 return;
46 max_deg = max(max_deg, ++deg[a]);
47 max_deg = max(max_deg, ++deg[b]);
48 join(a, b);
49 }
50
51 Graph(int n = 0, int e = -1) : rep(n, -1), deg(n), excluded(e) {}
52 };
53
54 int n;
55 vector<pair<int, int>> edges;
56 Graph graph;
57 vector<Graph> without;
58
59 void Init(int N)
60 {
61 n = N;
62 graph = Graph(n);
63 }
64
65 void Link(int A, int B)
66 {
67 edges.emplace_back(A, B);
68 if(graph.max_deg < 3)
69 {
70 graph.add_edge(A, B);
71 if(graph.max_deg == 3)
72 {
73 if(graph.deg[A] != 3)
74 swap(A, B);
75
76 vector<int> crit = {A};
77 for(auto &[u, v] : edges)
78 {
79 if(u == A) crit.emplace_back(v);
80 if(v == A) crit.emplace_back(u);
81 }
82
83 for(int x : crit)
84 {
85 without.emplace_back(n, x);
86 for(auto &[u, v] : edges)
87 without.back().add_edge(v, u);
88 }
89 }
90 }
91 else
92 {
93 for(auto &g : without)
94 g.add_edge(A, B);
95 }
96 }
97
98 int CountCritical()
99 {
100 if(graph.max_deg < 3)
CAPITOLUL 9. IOI 2012 986

101 {
102 if(graph.bicomp == -1) return n;
103 if(graph.bicomp == -2) return 0;
104 return graph.get_cycle();
105 }
106 else
107 {
108 int ret = 0;
109 for(auto &g : without)
110 {
111 if(g.bicomp == -1 && g.max_deg < 3)
112 ret++;
113 }
114 return ret;
115 }
116 }
117
118 // ----------------- begin grader ------------------
119
120 #define inbuf_len 1 << 16
121 #define outbuf_len 1 << 16
122
123 void Init(int N);
124 int CountCritical();
125 void Link(int a, int b);
126
127 int main()
128 {
129 auto t1 = clock();
130
131 std::freopen("../input/input54.txt", "r", stdin);
132 std::freopen("rings.out", "w", stdout);
133
134 int tmp;
135
136 /* Set input and output buffering */
137 char *inbuf, *outbuf;
138 inbuf = (char*) malloc(inbuf_len * sizeof(char));
139 outbuf = (char*) malloc(outbuf_len * sizeof(char));
140 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
141 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
142
143 auto t2 = clock();
144
145 int N, L;
146 tmp = scanf("%d %d", &N, &L);
147 assert(tmp == 2);
148 Init(N);
149
150 auto t3 = clock();
151
152 int i;
153 for (i = 0; i < L; i++)
154 {
155 int A, B;
156 tmp = scanf("%d", &A);
157
158 if (A == -1)
159 {
160 int critical;
161 critical = CountCritical();
162 printf("%d\n", critical);
163 }
164 else
165 {
166 tmp = scanf("%d", &B);
167 assert(tmp == 1);
168 Link(A, B);
169 }
170 }
171
172 auto t4 = clock();
173
174 // reset console output
175 freopen("CON", "w", stdout);
176
CAPITOLUL 9. IOI 2012 987

177 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
178 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
179 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
180
181 return 0;
182
183 }
184
185 // ----------------- end grader ------------------
186 /*
187 t2-t1 = 0
188 t3-t2 = 0.031
189 t4-t3 = 4.92
190
191 Process returned 0 (0x0) execution time : 5.014 s
192 Press any key to continue.
193 */

Listing 9.2.6: rings-233348.cpp


1 // https://oj.uz/submission/233348 710 ms 41592 KB
2
3 #include<bits/stdc++.h>
4
5 using namespace std;
6
7 #define taskname "A"
8 #define pb push_back
9 #define mp make_pair
10 typedef pair<int,int> ii;
11
12 typedef long double ld;
13 typedef long long ll;
14
15 const int maxn = 1e6 + 5;
16
17 int adj[maxn][2];
18
19 int n;
20
21 struct G
22 {
23 bitset<maxn> is_link;
24 int lab[maxn];
25 int banned = -1;
26 bool ok = 1;
27 void init(int u)
28 {
29 banned = u;
30 for(int i = 0 ; i < n ; ++i)lab[i] = -1;
31 for(int i = 0 ; i < n ; ++i)
32 {
33 for(int j = 0 ; j < 2 ; ++j)
34 {
35 if(adj[i][j] != -1 && i > adj[i][j])
36 {
37 Connect(i,adj[i][j]);
38 }
39 }
40 }
41 }
42
43 int FindLab(int u){return lab[u] < 0 ? u : lab[u] = FindLab(lab[u]);}
44
45 void Connect(int u , int v)
46 {
47 if(ok == 0 && banned != -1)return;
48 if(u == banned)is_link[v] = 1;
49 if(v == banned)is_link[u] = 1;
50 if(u == banned || v == banned)return;
51 u = FindLab(u);
52 v = FindLab(v);
53 if(u == v)
54 {
55 ok = 0;
CAPITOLUL 9. IOI 2012 988

56 return;
57 }
58 if(lab[u] > lab[v])swap(u , v);
59 lab[u] += lab[v];
60 lab[v] = u;
61 }
62 } a[4];
63
64 void Init(int _n)
65 {
66 memset(adj,-1,sizeof adj);
67 n = _n;
68 for(int i = 0 ; i < n ; ++i)a[0].lab[i] = -1;
69 }
70
71 int cycle = 0;
72 int CurState = 0;
73
74 int dfs(int u , int b)
75 {
76 int res = 1;
77 int par = b;
78 while(u != b)
79 {
80 res++;
81 for(int i = 0 ; i < 2 ; ++i)
82 {
83 if(adj[u][i] != -1 && adj[u][i] != par)
84 {
85 par = u;u = adj[u][i];
86 break;
87 }
88 }
89 }
90 return res;
91 }
92
93 int deg[maxn];
94
95 void Link(int a , int b)
96 {
97 if(CurState == 4)return;
98 if(deg[a] < deg[b])swap(a,b);
99 deg[a]++;deg[b]++;
100 if(CurState == 3)
101 {
102 for(int i = 0 ; i < 4 ; ++i)
103 {
104 ::a[i].Connect(a,b);
105 if((a != ::a[i].banned && deg[a] - ::a[i].is_link[a] > 2) ||
106 (b != ::a[i].banned && deg[b] - ::a[i].is_link[b] > 2))
107 ::a[i].ok = 0;
108 }
109 return;
110 }
111
112 if(deg[a] == 3)
113 {
114 CurState = 3;
115 for(int i = 0 ; i < 2 ; ++i)
116 {
117 ::a[i].init(adj[a][i]);
118 }
119 ::a[2].init(b);
120 ::a[3].init(a);
121 for(int i = 0 ; i < 4 ; ++i)
122 {
123 ::a[i].Connect(a,b);
124 if((a != ::a[i].banned && deg[a] - ::a[i].is_link[a] > 2) ||
125 (b != ::a[i].banned && deg[b] - ::a[i].is_link[b] > 2))
126 {
127 ::a[i].ok = 0;
128 }
129 }
130 return;
131 }
CAPITOLUL 9. IOI 2012 989

132
133 for(int i = 0 ; i < 2 ; ++i)
134 {
135 if(adj[a][i] == -1)
136 {
137 adj[a][i] = b;
138 break;
139 }
140 }
141
142 for(int i = 0 ; i < 2 ; ++i)
143 {
144 if(adj[b][i] == -1)
145 {
146 adj[b][i] = a;
147 break;
148 }
149 }
150
151 if(::a[0].FindLab(a) == ::a[0].FindLab(b))
152 {
153 if(CurState == 2)CurState = 4;
154 else cycle = dfs(a , b) , CurState = 2;
155 }
156 else
157 {
158 ::a[0].Connect(a , b);
159 }
160 }
161
162 int CountCritical()
163 {
164 if(CurState == 4)return 0;
165 if(CurState == 3)
166 {
167 int res = 0;
168 for(int i = 0 ; i < 4 ; ++i)
169 {
170 res += a[i].ok;
171 }
172 if(res == 0)CurState = 4;
173 return res;
174 }
175
176 if(CurState == 2)return cycle;
177 return n;
178 }
179
180 // ----------------- begin grader ------------------
181
182 #define inbuf_len 1 << 16
183 #define outbuf_len 1 << 16
184
185 void Init(int N);
186 int CountCritical();
187 void Link(int a, int b);
188
189 int main()
190 {
191 auto t1 = clock();
192
193 std::freopen("../input/input54.txt", "r", stdin);
194 std::freopen("rings.out", "w", stdout);
195
196 int tmp;
197
198 /* Set input and output buffering */
199 char *inbuf, *outbuf;
200 inbuf = (char*) malloc(inbuf_len * sizeof(char));
201 outbuf = (char*) malloc(outbuf_len * sizeof(char));
202 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
203 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
204
205 auto t2 = clock();
206
207 int N, L;
CAPITOLUL 9. IOI 2012 990

208 tmp = scanf("%d %d", &N, &L);


209 assert(tmp == 2);
210 Init(N);
211
212 auto t3 = clock();
213
214 int i;
215 for (i = 0; i < L; i++)
216 {
217 int A, B;
218 tmp = scanf("%d", &A);
219
220 if (A == -1)
221 {
222 int critical;
223 critical = CountCritical();
224 printf("%d\n", critical);
225 }
226 else
227 {
228 tmp = scanf("%d", &B);
229 assert(tmp == 1);
230 Link(A, B);
231 }
232 }
233
234 auto t4 = clock();
235
236 // reset console output
237 freopen("CON", "w", stdout);
238
239 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
240 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
241 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
242
243 return 0;
244
245 }
246
247 // ----------------- end grader ------------------
248 /*
249 t2-t1 = 0
250 t3-t2 = 0.015
251 t4-t3 = 4.063
252
253 Process returned 0 (0x0) execution time : 4.157 s
254 Press any key to continue.
255 */

Listing 9.2.7: checkerRings.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../input/input54.txt",
14 (char*)"rings.out",
15 (char*)"../output/output54.txt",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("rings", argc, argv);
24 compareRemainingLines();
CAPITOLUL 9. IOI 2012 991

25 }

9.2.3 *Rezolvare detaliat 

9.3 Craysh scrivener


Problema 3 - Craysh scrivener 100 de puncte
Author: Bruce Merry
Se spune c  Leonardo a fost un mare admirator al lui Johannes Gutenberg, erarul german
care a inventat tiparul ³i în onoarea sa a proiectat un dispozitiv numit editorul rac - craysh
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  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  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  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 conguraµia textului dup  ecare
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 9. IOI 2012 992

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  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 ³ier, numit scrivener.c, scrivener.cpp sau
scrivener.pas. Acest ³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 ³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 ecare 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, ecare pe câte o linie.
Timp maxim de executare/test: *.* secunde
Memorie: total *** MB
CAPITOLUL 9. IOI 2012 993

9.3.1 Indicaµii de rezolvare

A clever way to get an ecient 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 nal one, after processing all the commands, the nal 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 denitely more sophisticated approach to nd a point in the text. For
this it is sucient 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.

9.3.2 Coduri surs 

Listing 9.3.1: scrivener.cpp


1 /*
2 Solution for Scrivener,
3 with TypeLetter and GetLetter in O(log N)
4 and UndoCommands in O(1).
5
6 Author: Matteo Boscariol
7 */
8
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <assert.h>
12
13 #include<ctime>
14 #include<iostream>
15
16 #define inbuf_len 1 << 16
17 #define outbuf_len 1 << 16
18
19 void Init();
20 void TypeLetter(char L);
21 void UndoCommands(int U);
22 char GetLetter(int P);
23
24 #include<cstdlib>
25 #define MAXC 1000000
26 #define LOGMAXC 20
27
28 class node
29 {
30 public:
31 char l;
32 node** parents;
33 int depth;
34
35 node(char letter, node* parent)
36 {
37 l = letter;
38 if(parent == NULL)
39 {
40 depth=-1;
41 parents = new node*[1];
CAPITOLUL 9. IOI 2012 994

42 parents[0] = NULL;
43 }
44 else
45 {
46 depth=parent->depth+1;
47 parents = new node*[LOGMAXC];
48 parents[0] = parent;
49 for(int i=1, j=2; j<=depth+1; i++, j*=2)
50 {
51 parents[i] = parents[i-1]->parents[i-1];
52 }
53 }
54 };
55 };
56
57 node** command_base;
58 int n_commands;
59 node* current_position;
60 node* tree_root;
61
62 void Init()
63 {
64 command_base = new node*[MAXC];
65 n_commands = 0;
66 current_position = tree_root = new node(’\0’,NULL);
67 }
68
69 void TypeLetter(char L)
70 {
71 command_base[n_commands] = current_position;
72 n_commands++;
73 node* n = new node(L, current_position);
74 current_position = n;
75 }
76
77 void UndoCommands(int U)
78 {
79 node *n = command_base[n_commands - U];
80 command_base[n_commands] = current_position;
81 n_commands++;
82 current_position = n;
83 }
84
85 char GetLetter(int P)
86 {
87 node* n = current_position;
88 int distance = current_position->depth - P;
89
90 for(int shift = LOGMAXC; shift >= 0; shift--)
91 {
92 if((distance >> shift)%2 == 1)
93 n = n->parents[shift];
94 }
95
96 return n->l;
97 }
98
99 // ------------------ begin grader ----------------
100
101 int main()
102 {
103 auto t1 = clock();
104
105 std::freopen("../input/input49.txt", "r", stdin);
106 std::freopen("scrivener.out", "w", stdout);
107
108 int tmp;
109
110 /* Set input and output buffering */
111 char *inbuf, *outbuf;
112 inbuf = (char*) malloc(inbuf_len * sizeof(char));
113 outbuf = (char*) malloc(outbuf_len * sizeof(char));
114 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
115 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
116
117 Init();
CAPITOLUL 9. IOI 2012 995

118
119 auto t2 = clock();
120
121 int cmd_num;
122 tmp = scanf("%d", &cmd_num);
123 assert(tmp == 1);
124
125 auto t3 = clock();
126
127 int i;
128 for (i = 0; i < cmd_num; i++)
129 {
130 char cmd;
131 tmp = scanf(" %c", &cmd);
132 assert(tmp == 1);
133 if (cmd == ’T’)
134 {
135 char letter;
136 tmp = scanf(" %c", &letter);
137 assert(tmp == 1);
138 TypeLetter(letter);
139 }
140 else
141 if (cmd == ’U’)
142 {
143 int number;
144 tmp = scanf("%d", &number);
145 assert(tmp == 1);
146 UndoCommands(number);
147 }
148 else
149 if (cmd == ’P’)
150 {
151 int index;
152 char letter;
153 tmp = scanf("%d", &index);
154 assert(tmp == 1);
155 letter = GetLetter(index);
156 printf("%c\n", letter);
157 }
158 }
159
160 auto t4 = clock();
161
162 // reset console output
163 freopen("CON", "w", stdout);
164
165 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
166 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
167 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
168
169 return 0;
170
171 }
172 // ------------------ end grader ----------------
173 /*
174 t2-t1 = 0.015
175 t3-t2 = 0
176 t4-t3 = 2.313
177
178 Process returned 0 (0x0) execution time : 2.391 s
179 Press any key to continue.
180
181 argc = 4
182 checker
183 ../input/input49.txt
184 scrivener.out
185 ../output/output49.txt
186 ----------------------
187 1
188 Correct
189
190 Process returned 0 (0x0) execution time : 0.078 s
191 Press any key to continue.
192 */
CAPITOLUL 9. IOI 2012 996

Listing 9.3.2: scrivener-7279.cpp


1 // https://oj.uz/submission/7279 604 ms 176648 KB
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define inbuf_len 1 << 16
11 #define outbuf_len 1 << 16
12
13 void Init();
14 void TypeLetter(char L);
15 void UndoCommands(int U);
16 char GetLetter(int P);
17
18 #include <vector>
19 #include <map>
20
21 using namespace std;
22
23 map<char, int> trie[1000001]; int prv[1000001][20];
24 int node,now,ttime,hist[1000001],len[1000001];
25 char last[10000001];
26
27 void Init() {}
28
29 void TypeLetter(char L)
30 {
31 int &n = trie[now][L];
32 if (n == 0)
33 {
34 n = ++node;
35 prv[n][0] = now;
36 for (int i=1;i<20;i++)
37 prv[n][i] = prv[prv[n][i-1]][i-1];
38 len[n] = len[now] + 1;
39 last[n] = L;
40 }
41 hist[++ttime] = now = n;
42 }
43
44 void UndoCommands(int U)
45 {
46 now = hist[ttime-U];
47 hist[++ttime] = now;
48 }
49
50 char GetLetter(int P)
51 {
52 int x = now;
53 P = len[x] - P - 1;
54 for (int i=0;i<20;i++)
55 if (P & (1 << i))
56 x = prv[x][i];
57
58 return last[x];
59 }
60
61 // ------------------ begin grader ----------------
62
63 int main()
64 {
65 auto t1 = clock();
66
67 std::freopen("../input/input49.txt", "r", stdin);
68 std::freopen("scrivener.out", "w", stdout);
69
70 int tmp;
71
72 /* Set input and output buffering */
73 char *inbuf, *outbuf;
74 inbuf = (char*) malloc(inbuf_len * sizeof(char));
CAPITOLUL 9. IOI 2012 997

75 outbuf = (char*) malloc(outbuf_len * sizeof(char));


76 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
77 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
78
79 Init();
80
81 auto t2 = clock();
82
83 int cmd_num;
84 tmp = scanf("%d", &cmd_num);
85 assert(tmp == 1);
86
87 auto t3 = clock();
88
89 int i;
90 for (i = 0; i < cmd_num; i++)
91 {
92 char cmd;
93 tmp = scanf(" %c", &cmd);
94 assert(tmp == 1);
95 if (cmd == ’T’)
96 {
97 char letter;
98 tmp = scanf(" %c", &letter);
99 assert(tmp == 1);
100 TypeLetter(letter);
101 }
102 else
103 if (cmd == ’U’)
104 {
105 int number;
106 tmp = scanf("%d", &number);
107 assert(tmp == 1);
108 UndoCommands(number);
109 }
110 else
111 if (cmd == ’P’)
112 {
113 int index;
114 char letter;
115 tmp = scanf("%d", &index);
116 assert(tmp == 1);
117 letter = GetLetter(index);
118 printf("%c\n", letter);
119 }
120 }
121
122 auto t4 = clock();
123
124 // reset console output
125 freopen("CON", "w", stdout);
126
127 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
128 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
129 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
130
131 return 0;
132
133 }
134 // ------------------ end grader ----------------
135 /*
136 t2-t1 = 0.015
137 t3-t2 = 0
138 t4-t3 = 2.859
139
140 Process returned 0 (0x0) execution time : 3.124 s
141 Press any key to continue.
142 */

Listing 9.3.3: scrivener-18725.cpp


1 // https://oj.uz/submission/18725 408 ms 87988 KB
2
3 #include <stdlib.h>
4 #include <stdio.h>
CAPITOLUL 9. IOI 2012 998

5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 using namespace std;
11
12 #define inbuf_len 1 << 16
13 #define outbuf_len 1 << 16
14
15 void Init();
16 void TypeLetter(char L);
17 void UndoCommands(int U);
18 char GetLetter(int P);
19
20 int go[1000005],len[1000005],t=1,par[1000005][20];
21 char al[1000005];
22
23 void Init(){}
24
25 void TypeLetter(char L)
26 {
27 al[t]=L;
28 len[t]=len[go[t-1]]+1;
29 par[t][0]=go[t-1];
30 int u=1;
31 while(1)
32 {
33 if(len[t]>=(1<<u))
34 {
35 par[t][u]=par[par[t][u-1]][u-1];
36 u++;
37 }
38 else break;
39 }
40
41 go[t]=t;
42 t++;
43 }
44
45 void UndoCommands(int U)
46 {
47 go[t]=go[t-U-1];
48 t++;
49 }
50
51 char GetLetter(int P)
52 {
53 int o=len[go[t-1]];
54 o=o-P-1;
55 if(o==0)return al[go[t-1]];
56 int u=0,h=go[t-1];
57 while(o)
58 {
59 if(o%2)
60 {
61 h=par[h][u];
62 }
63 o/=2;
64 u++;
65 }
66
67 return al[h];
68 }
69
70 // ------------------ begin grader ----------------
71
72 int main()
73 {
74 auto t1 = clock();
75
76 std::freopen("../input/input49.txt", "r", stdin);
77 std::freopen("scrivener.out", "w", stdout);
78
79 int tmp;
80
CAPITOLUL 9. IOI 2012 999

81 /* Set input and output buffering */


82 char *inbuf, *outbuf;
83 inbuf = (char*) malloc(inbuf_len * sizeof(char));
84 outbuf = (char*) malloc(outbuf_len * sizeof(char));
85 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
86 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
87
88 Init();
89
90 auto t2 = clock();
91
92 int cmd_num;
93 tmp = scanf("%d", &cmd_num);
94 assert(tmp == 1);
95
96 auto t3 = clock();
97
98 int i;
99 for (i = 0; i < cmd_num; i++)
100 {
101 char cmd;
102 tmp = scanf(" %c", &cmd);
103 assert(tmp == 1);
104 if (cmd == ’T’)
105 {
106 char letter;
107 tmp = scanf(" %c", &letter);
108 assert(tmp == 1);
109 TypeLetter(letter);
110 }
111 else
112 if (cmd == ’U’)
113 {
114 int number;
115 tmp = scanf("%d", &number);
116 assert(tmp == 1);
117 UndoCommands(number);
118 }
119 else
120 if (cmd == ’P’)
121 {
122 int index;
123 char letter;
124 tmp = scanf("%d", &index);
125 assert(tmp == 1);
126 letter = GetLetter(index);
127 printf("%c\n", letter);
128 }
129 }
130
131 auto t4 = clock();
132
133 // reset console output
134 freopen("CON", "w", stdout);
135
136 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
137 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
138 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
139
140 return 0;
141
142 }
143 // ------------------ end grader ----------------
144 /*
145 t2-t1 = 0
146 t3-t2 = 0
147 t4-t3 = 2.203
148
149 Process returned 0 (0x0) execution time : 2.328 s
150 Press any key to continue.
151 */

Listing 9.3.4: scrivener-230126.cpp


1 // https://oj.uz/submission/230126 438 ms 67448 KB
CAPITOLUL 9. IOI 2012 1000

2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 using namespace std;
11
12 #define inbuf_len 1 << 16
13 #define outbuf_len 1 << 16
14
15 void Init();
16 void TypeLetter(char L);
17 void UndoCommands(int U);
18 char GetLetter(int P);
19
20
21 int cnt, cnt2, par[20][1000001], A[1000001], dep[1000001];
22 char S[1000001];
23
24 void Init() { }
25
26 void TypeLetter(char L)
27 {
28 ++cnt2;
29 S[cnt2] = L;
30 dep[cnt2] = dep[A[cnt]] + 1;
31 par[0][cnt2] = A[cnt];
32 for (int i = 1; i < 20; ++i)
33 par[i][cnt2] = par[i - 1][par[i - 1][cnt2]];
34 ++cnt;
35 A[cnt] = cnt2;
36 }
37
38 void UndoCommands(int U)
39 {
40 int x = cnt - U;
41 ++cnt;
42 A[cnt] = A[x];
43 }
44
45 char GetLetter(int P)
46 {
47 int x = A[cnt];
48 for (int i = 0; i < 20; ++i)
49 if (((dep[x] - P - 1) >> i) & 1)
50 {
51 x = par[i][x];
52 }
53 return S[x];
54 }
55
56 // ------------------ begin grader ----------------
57
58 int main()
59 {
60 auto t1 = clock();
61
62 std::freopen("../input/input49.txt", "r", stdin);
63 std::freopen("scrivener.out", "w", stdout);
64
65 int tmp;
66
67 /* Set input and output buffering */
68 char *inbuf, *outbuf;
69 inbuf = (char*) malloc(inbuf_len * sizeof(char));
70 outbuf = (char*) malloc(outbuf_len * sizeof(char));
71 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
72 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
73
74 Init();
75
76 auto t2 = clock();
77
CAPITOLUL 9. IOI 2012 1001

78 int cmd_num;
79 tmp = scanf("%d", &cmd_num);
80 assert(tmp == 1);
81
82 auto t3 = clock();
83
84 int i;
85 for (i = 0; i < cmd_num; i++)
86 {
87 char cmd;
88 tmp = scanf(" %c", &cmd);
89 assert(tmp == 1);
90 if (cmd == ’T’)
91 {
92 char letter;
93 tmp = scanf(" %c", &letter);
94 assert(tmp == 1);
95 TypeLetter(letter);
96 }
97 else
98 if (cmd == ’U’)
99 {
100 int number;
101 tmp = scanf("%d", &number);
102 assert(tmp == 1);
103 UndoCommands(number);
104 }
105 else
106 if (cmd == ’P’)
107 {
108 int index;
109 char letter;
110 tmp = scanf("%d", &index);
111 assert(tmp == 1);
112 letter = GetLetter(index);
113 printf("%c\n", letter);
114 }
115 }
116
117 auto t4 = clock();
118
119 // reset console output
120 freopen("CON", "w", stdout);
121
122 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
123 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
124 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
125
126 return 0;
127
128 }
129 // ------------------ end grader ----------------
130 /*
131 t2-t1 = 0
132 t3-t2 = 0
133 t4-t3 = 2.359
134
135 Process returned 0 (0x0) execution time : 2.500 s
136 Press any key to continue.
137 */

Listing 9.3.5: checkerScrivener.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
CAPITOLUL 9. IOI 2012 1002

13 (char*)"../input/input49.txt",
14 (char*)"scrivener.out",
15 (char*)"../output/output49.txt",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("scrivener", argc, argv);
24 compareRemainingLines();
25 }

9.3.3 *Rezolvare detaliat 

9.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 planicarea 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 innit de form  p trat  format din celule.
Fiecare celul  se identic  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  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 conguraµ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 9. IOI 2012 1003

Când travers m ora³ul, o s ritur  înseamn  trecerea de la un bloc la un alt bloc adiacent.
Celulele neacoperite nu pot  traversate. Fie v  0, v1 , ..., vN 1 coordonatele a N blocuri plasate
pe grid. Pentru oricare dou  blocuri distincte aate 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
Conguraµ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 a  la coordonatele X i, Y i cu 0 & i & N  1, ³i 1 & X i, Y i & 2  2.
31

Deoarece rezultatul poate  prea mare pentru a  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 ³ier, numit city.c, city.cpp sau city.pas. Acest ³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 9. IOI 2012 1004

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

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

9.4.2 Coduri surs 

Listing 9.4.1: city.cpp


1 /*
2 O(N*logN) solution for City.
3
4 Author: Giovanni Paolini
CAPITOLUL 9. IOI 2012 1005

5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <assert.h>
10
11 #include<ctime>
12
13 #define inbuf_len 1 << 16
14 #define outbuf_len 1 << 16
15
16 int DistanceSum(int N, int *X, int *Y);
17
18 #include <cstdio>
19 #include <iostream>
20 #include <algorithm>
21 #include <vector>
22
23 using namespace std;
24
25 struct Point
26 {
27 int x;
28 int y;
29
30 Point() {}
31
32 Point(int _x, int _y)
33 {
34 x = _x;
35 y = _y;
36 }
37 };
38
39 bool operator< (const Point &a, const Point &b)
40 {
41 if ( a.x != b.x ) return ( a.x < b.x );
42 return ( a.y < b.y );
43 }
44
45 int const MAXN = 1000005;
46
47 int n;
48 Point squares[MAXN];
49
50 void read(int N, int *X, int *Y)
51 {
52 n = N;
53
54 for (int i=0; i<n; ++i)
55 {
56 squares[i] = Point(X[i], Y[i]);
57 }
58
59 }
60
61 void exchange()
62 { // Exchanges the x and y coordinates of all the points.
63 for (int i=0; i<n; ++i)
64 {
65 squares[i] = Point( squares[i].y, squares[i].x );
66 }
67 }
68
69 struct Node
70 {
71 int x;
72 int ymin;
73 int ymax;
74 vector<int> neighbours;
75
76 Node () {}
77
78 Node (int _x, int _ymin, int _ymax)
79 {
80 x = _x;
CAPITOLUL 9. IOI 2012 1006

81 ymin = _ymin;
82 ymax = _ymax;
83 }
84 };
85
86 vector<Node> nodes;
87
88 void make_tree()
89 { // Builds the tree of vertically/horizontally-collapsed nodes
90
91 sort( squares, squares + n );
92 nodes.clear();
93
94 // Create nodes
95
96 int cont = 0;
97 while ( cont < n )
98 {
99 int x = squares[cont].x;
100 int ymin = squares[cont].y;
101
102 int y = ymin;
103
104 int i;
105 for (i=cont+1; i<n; ++i)
106 {
107 if ( squares[i].y == y+1 ) y++;
108 else break;
109 }
110
111 int ymax = y;
112 cont = i;
113
114 nodes.push_back( Node( x, ymin, ymax ) );
115 }
116
117 // Create edges
118
119 int cont1,cont2;
120
121 cont1 = 0;
122 for ( cont2 = 1; cont2 < nodes.size(); cont2++ )
123 {
124 while ( ( nodes[cont1].x + 1 < nodes[cont2].x ) ||
125 ( nodes[cont1].x + 1 == nodes[cont2].x &&
126 nodes[cont1].ymax < nodes[cont2].ymin ) )
127 cont1++;
128
129 int numedges = 0;
130 while ( nodes[cont1].x + 1 == nodes[cont2].x &&
131 nodes[cont1].ymin <= nodes[cont2].ymax )
132 {
133 numedges++;
134 nodes[cont1].neighbours.push_back(cont2);
135 nodes[cont2].neighbours.push_back(cont1);
136 cont1++;
137 }
138
139 if ( numedges > 0 ) cont1--;
140 }
141
142 }
143
144 int weight (int i)
145 {
146 return nodes[i].ymax - nodes[i].ymin + 1;
147 }
148
149 bool visited[MAXN];
150 long long int s; // sum of all weights
151 long long int tot; // required total
152 long long int const MOD = 1000000000;
153
154 long long int dfs (int k)
155 {
156 if ( visited[k] ) return 0;
CAPITOLUL 9. IOI 2012 1007

157
158 visited[k] = 1;
159
160 long long int w = weight(k);
161 for (int i=0; i<nodes[k].neighbours.size(); ++i)
162 {
163 w += dfs( nodes[k].neighbours[i] );
164 w %= MOD;
165 }
166
167 tot += w * (s - w);
168 tot %= MOD;
169
170 return w;
171 }
172
173 long long int sum ()
174 {
175 make_tree();
176
177 s = 0;
178 tot = 0;
179 for (int i=0; i<nodes.size(); ++i)
180 {
181 s += weight(i);
182 s %= MOD;
183 visited[i] = 0;
184 }
185
186 dfs(0);
187
188 return tot;
189 }
190
191 int DistanceSum(int N, int *X, int *Y)
192 {
193 read(N, X, Y);
194
195 // Find the horizontal sum
196 long long int first = sum();
197
198 exchange();
199
200 // Find the vertical sum
201 long long int second = sum();
202
203 long long int sol = first + second;
204 sol %= MOD;
205
206 return sol;
207 }
208
209 // --------------- begin grader --------------
210
211 int main()
212 {
213 auto t1 = clock();
214
215 std::freopen("../input/input40.txt", "r", stdin);
216 std::freopen("city.out", "w", stdout);
217
218 int tmp;
219
220 /* Set input and output buffering */
221 char *inbuf, *outbuf;
222
223 inbuf = (char*) malloc(inbuf_len * sizeof(char));
224 outbuf = (char*) malloc(outbuf_len * sizeof(char));
225
226 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
227 assert(tmp == 0);
228
229 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
230 assert(tmp == 0);
231
232 int N, i;
CAPITOLUL 9. IOI 2012 1008

233 tmp = scanf("%d", &N);


234 assert(tmp == 1);
235
236 int *sq_x, *sq_y;
237 sq_x = (int*) malloc(N * sizeof(int));
238 sq_y = (int*) malloc(N * sizeof(int));
239
240 for (i = 0; i < N; i++)
241 {
242 tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
243 assert(tmp == 2);
244 }
245
246 auto t2 = clock();
247
248 int ds = DistanceSum(N, sq_x, sq_y);
249
250 auto t3 = clock();
251
252 printf("%d\n", ds);
253
254 auto t4 = clock();
255
256 // reset console output
257 freopen("CON", "w", stdout);
258
259 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
260 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
261 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
262
263 return 0;
264
265 }
266
267 // --------------- begin grader --------------
268 /*
269 t2-t1 = 0.063
270 t3-t2 = 0.187
271 t4-t3 = 0
272
273 Process returned 0 (0x0) execution time : 0.297 s
274 Press any key to continue.
275 */

Listing 9.4.2: city-16324.cpp


1 // https://oj.uz/submission/16324 115 ms 8592 KB
2
3 #define inbuf_len 1 << 16
4 #define outbuf_len 1 << 16
5
6 int DistanceSum(int N, int *X, int *Y);
7
8 #include <bits/stdc++.h>
9
10 using namespace std;
11
12 typedef long long ll;
13 typedef pair<int,int> pp;
14 typedef vector<int> vec;
15 typedef vector<vec> mat;
16
17 #define pb push_back
18 #define MOD 1000000000
19
20 struct pt
21 {
22 int x,y,v;
23 pt(){}
24 pt(int x_,int y_):x(x_),y(y_){}
25 bool operator < (const pt& r) const
26 {
27 return x!=r.x?x<r.x:y<r.y;
28 }
29 } s[100000];
CAPITOLUL 9. IOI 2012 1009

30
31 mat path;
32 int n;
33 int cnt[100000];
34
35 ll dfs(int v,int p,ll &ans)
36 {
37 ll ret=0;
38 for(int i=0;i<path[v].size();i++)
39 {
40 int u=path[v][i];
41 if(u==p)continue;
42 ret+=dfs(u,v,ans);
43 }
44 ret+=cnt[v];
45 ans+=(n-ret)*ret;
46 ans%=MOD;
47 return ret;
48 }
49
50 int f(int N, int *X, int *Y)
51 {
52 n=N;
53 for(int i=0;i<N;i++) s[i]=pt(X[i],Y[i]);
54 sort(s,s+N);
55 int sz=0;
56 for(int i=0;i<N;i++)
57 {
58 int p=i;
59 while(s[i].x==s[i+1].x)i++;
60 int v=p;
61 while(v<=i)
62 {
63 while(v<i&&s[v].y+1==s[v+1].y)
64 {
65 s[v].v=sz;
66 v++;
67 }
68 s[v].v=sz;
69 cnt[sz++]=s[v].y-s[p].y+1;
70 p=++v;
71 }
72 }
73
74 path.clear();
75 path.assign(sz,vec());
76
77 for(int i=0;i<N;i++)
78 {
79 pt* it=lower_bound(s,s+N,pt(s[i].x-1,s[i].y));
80 if(it->x==s[i].x-1&&it->y==s[i].y) path[s[i].v].pb(it->v);
81 it=lower_bound(s,s+N,pt(s[i].x+1,s[i].y));
82 if(it->x==s[i].x+1&&it->y==s[i].y) path[s[i].v].pb(it->v);
83 }
84
85 for(int i=0;i<sz;i++)
86 {
87 sort(path[i].begin(),path[i].end());
88 path[i].erase(unique(path[i].begin(),path[i].end()),path[i].end());
89 }
90
91 ll ret=0;
92 dfs(0,0,ret);
93 return (int)ret;
94 }
95
96 int DistanceSum(int N, int *X, int *Y)
97 {
98 return (f(N,X,Y)+f(N,Y,X))%MOD;
99 }
100
101 // --------------- begin grader --------------
102
103 int main()
104 {
105 auto t1 = clock();
CAPITOLUL 9. IOI 2012 1010

106
107 std::freopen("../input/input40.txt", "r", stdin);
108 std::freopen("city.out", "w", stdout);
109
110 int tmp;
111
112 /* Set input and output buffering */
113 char *inbuf, *outbuf;
114
115 inbuf = (char*) malloc(inbuf_len * sizeof(char));
116 outbuf = (char*) malloc(outbuf_len * sizeof(char));
117
118 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
119 assert(tmp == 0);
120
121 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
122 assert(tmp == 0);
123
124 int N, i;
125 tmp = scanf("%d", &N);
126 assert(tmp == 1);
127
128 int *sq_x, *sq_y;
129 sq_x = (int*) malloc(N * sizeof(int));
130 sq_y = (int*) malloc(N * sizeof(int));
131
132 for (i = 0; i < N; i++)
133 {
134 tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
135 assert(tmp == 2);
136 }
137
138 auto t2 = clock();
139
140 int ds = DistanceSum(N, sq_x, sq_y);
141
142 auto t3 = clock();
143
144 printf("%d\n", ds);
145
146 auto t4 = clock();
147
148 // reset console output
149 freopen("CON", "w", stdout);
150
151 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
152 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
153 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
154
155 return 0;
156
157 }
158
159 // --------------- begin grader --------------
160 /*
161 t2-t1 = 0.266
162 t3-t2 = 0.703
163 t4-t3 = 0
164
165 Process returned 0 (0x0) execution time : 1.016 s
166 Press any key to continue.
167 */

Listing 9.4.3: city-18847.cpp


1 // https://oj.uz/submission/18847 51 ms 15584 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 using namespace std;
CAPITOLUL 9. IOI 2012 1011

11
12 #define inbuf_len 1 << 16
13 #define outbuf_len 1 << 16
14
15 int DistanceSum(int N, int *X, int *Y);
16
17 #include<cstdio>
18 #include<algorithm>
19 #include<vector>
20
21 int a,xx,yy,i,j,p,q,back,t,x[100002],y[100002];
22 long long sum,mod=1e9,sz[100002];
23 std::vector<int>G[100002],E[100002];
24
25 struct A
26 {
27 int num,s,e;
28 };
29
30 std::vector<A>T[100002];
31
32 void dfs(int r,int par)
33 {
34 for(int k=0;k<E[r].size();k++)
35 {
36 if(par==E[r][k])continue;
37 dfs(E[r][k],r);
38 sz[r]+=sz[E[r][k]];
39 }
40 sum+=sz[r]*(long long)(a-sz[r]);
41 }
42
43 void f()
44 {
45 t=0;
46 for(i=0;i<=a;i++)
47 G[i].clear(),T[i].clear(),E[i].clear();
48
49 for(i=1;i<=a;i++)
50 {
51 G[x[i]].push_back(y[i]);
52 }
53
54 for(i=0;G[i].size();i++)
55 std::sort(G[i].begin(),G[i].end());
56
57 for(i=0;G[i].size();i++)
58 {
59 back=G[i][0];
60 for(j=0;j<G[i].size();j++)
61 {
62 if(j==G[i].size()-1||G[i][j]+1!=G[i][j+1])
63 {
64 T[i].push_back({t++,back,G[i][j]});
65 sz[t-1]=G[i][j]-back+1;
66 if(j!=G[i].size()-1)back=G[i][j+1];
67 }
68 }
69 }
70
71 for(i=1;T[i].size();i++)
72 {
73 p=0;q=0;
74 while(p<T[i-1].size()&&q<T[i].size())
75 {
76 if(T[i-1][p].e<T[i][q].s||T[i][q].e<T[i-1][p].s)
77 {
78
79 }
80 else
81 {
82 E[T[i-1][p].num].push_back(T[i][q].num);
83 E[T[i][q].num].push_back(T[i-1][p].num);
84 }
85
86 if(T[i-1][p].e<T[i][q].e) p++;
CAPITOLUL 9. IOI 2012 1012

87 else q++;
88 }
89 }
90
91 dfs(0,-1);
92 }
93
94 int DistanceSum (int N, int *X, int *Y)
95 {
96 a=N;
97 xx=2147483647;yy=2147483647;
98 for(i=1;i<=a;i++)
99 x[i]=X[i-1],y[i]=Y[i-1];
100 for(i=1;i<=a;i++)
101 xx=std::min(xx,x[i]),yy=std::min(yy,y[i]);
102 for(i=1;i<=a;i++)
103 x[i]-=xx,y[i]-=yy;
104 f();
105 for(i=1;i<=a;i++)
106 t=x[i],x[i]=y[i],y[i]=t;
107 f();
108 return sum%mod;
109 }
110
111 // --------------- begin grader --------------
112
113 int main()
114 {
115 auto t1 = clock();
116
117 std::freopen("../input/input40.txt", "r", stdin);
118 std::freopen("city.out", "w", stdout);
119
120 int tmp;
121
122 /* Set input and output buffering */
123 char *inbuf, *outbuf;
124
125 inbuf = (char*) malloc(inbuf_len * sizeof(char));
126 outbuf = (char*) malloc(outbuf_len * sizeof(char));
127
128 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
129 assert(tmp == 0);
130
131 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
132 assert(tmp == 0);
133
134 int N, i;
135 tmp = scanf("%d", &N);
136 assert(tmp == 1);
137
138 int *sq_x, *sq_y;
139 sq_x = (int*) malloc(N * sizeof(int));
140 sq_y = (int*) malloc(N * sizeof(int));
141
142 for (i = 0; i < N; i++)
143 {
144 tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
145 assert(tmp == 2);
146 }
147
148 auto t2 = clock();
149
150 int ds = DistanceSum(N, sq_x, sq_y);
151
152 auto t3 = clock();
153
154 printf("%d\n", ds);
155
156 auto t4 = clock();
157
158 // reset console output
159 freopen("CON", "w", stdout);
160
161 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
162 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 9. IOI 2012 1013

163 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
164
165 return 0;
166
167 }
168
169 // --------------- begin grader --------------
170 /*
171 t2-t1 = 0.11
172 t3-t2 = 0.156
173 t4-t3 = 0
174
175 Process returned 0 (0x0) execution time : 0.344 s
176 Press any key to continue.
177 */

Listing 9.4.4: city-32163.cpp


1 // https://oj.uz/submission/32163 73 ms 8268 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define inbuf_len 1 << 16
11 #define outbuf_len 1 << 16
12
13 int DistanceSum(int N, int *X, int *Y);
14
15 #include <algorithm>
16 #include <vector>
17
18 using namespace std;
19
20 typedef long long llong;
21 typedef pair<int, int> pi;
22
23 int n;
24 const int MOD = 1e9;
25 const int PMAX = 2147483646;
26
27 pi point[100000];
28 int idx[100000];
29 int cnt[100000];
30 vector<int> edge[100000];
31
32 bool haveNext(int i)
33 {
34 if (i == n - 1) return false;
35 if (point[i].first != point[i + 1].first) return false;
36 if (point[i].second + 1 != point[i + 1].second) return false;
37 return true;
38 }
39
40 int bsearch(pi x)
41 {
42 int s = 0, e = n - 1;
43 int m;
44
45 while (s < e)
46 {
47 m = (s + e) / 2;
48 if (point[m] == x) return m;
49 if (point[m] < x) s = m + 1;
50 else e = m - 1;
51 }
52
53 if (point[s] == x) return s;
54 return -1;
55 }
56
57 int ans;
CAPITOLUL 9. IOI 2012 1014

58 int dfs(int x, int p)


59 {
60 int ret = cnt[x];
61 for (int i : edge[x])
62 {
63 if (i == p) continue;
64 ret += dfs(i, x);
65 }
66
67 if (x)
68 {
69 llong sum = ret;
70 sum *= n - ret;
71 ans = (ans + sum) % MOD;
72 }
73
74 return ret;
75 }
76
77 int getAns()
78 {
79 ans = 0;
80 sort(point, point + n);
81 idx[0] = 0;
82 edge[0].clear();
83 cnt[0] = 1;
84 for (int i = 1; i < n; ++i)
85 {
86 if (haveNext(i - 1))
87 ++cnt[idx[i] = idx[i - 1]];
88 else
89 cnt[idx[i] = idx[i - 1] + 1] = 1, edge[idx[i]].clear();
90
91 int j = bsearch({ point[i].first - 1, point[i].second });
92
93 if (j == -1) continue;
94
95 if (!edge[idx[i]].empty() && idx[j] == edge[idx[i]].back())
96 continue;
97
98 edge[idx[i]].push_back(idx[j]);
99 edge[idx[j]].push_back(idx[i]);
100 }
101
102 dfs(0, -1);
103 return ans;
104 }
105
106 int DistanceSum(int N, int *X, int *Y)
107 {
108 n = N;
109 for (int i = 0; i < n; ++i)
110 {
111 point[i] = { X[i], Y[i] };
112 }
113
114 int t = getAns();
115 for (int i = 0; i < n; ++i)
116 {
117 point[i] = { Y[i], X[i] };
118 }
119
120 return (getAns() + t) % MOD;
121 }
122
123 // --------------- begin grader --------------
124
125 int main()
126 {
127 auto t1 = clock();
128
129 std::freopen("../input/input40.txt", "r", stdin);
130 std::freopen("city.out", "w", stdout);
131
132 int tmp;
133
CAPITOLUL 9. IOI 2012 1015

134 /* Set input and output buffering */


135 char *inbuf, *outbuf;
136
137 inbuf = (char*) malloc(inbuf_len * sizeof(char));
138 outbuf = (char*) malloc(outbuf_len * sizeof(char));
139
140 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
141 assert(tmp == 0);
142
143 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
144 assert(tmp == 0);
145
146 int N, i;
147 tmp = scanf("%d", &N);
148 assert(tmp == 1);
149
150 int *sq_x, *sq_y;
151 sq_x = (int*) malloc(N * sizeof(int));
152 sq_y = (int*) malloc(N * sizeof(int));
153
154 for (i = 0; i < N; i++)
155 {
156 tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
157 assert(tmp == 2);
158 }
159
160 auto t2 = clock();
161
162 int ds = DistanceSum(N, sq_x, sq_y);
163
164 auto t3 = clock();
165
166 printf("%d\n", ds);
167
168 auto t4 = clock();
169
170 // reset console output
171 freopen("CON", "w", stdout);
172
173 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
174 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
175 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
176
177 return 0;
178
179 }
180
181 // --------------- begin grader --------------
182 /*
183 t2-t1 = 0.063
184 t3-t2 = 0.312
185 t4-t3 = 0
186
187 Process returned 0 (0x0) execution time : 0.438 s
188 Press any key to continue.
189 */

Listing 9.4.5: city-197022.cpp


1 // https://oj.uz/submission/197022 88 ms 10044 KB
2
3 #include <stdlib.h>
4 #include <assert.h>
5
6 #include<ctime>
7 #include<iostream>
8
9 #define inbuf_len 1 << 16
10 #define outbuf_len 1 << 16
11
12 int DistanceSum(int N, int *X, int *Y);
13
14 #include<stdio.h>
15 #include<algorithm>
16 #include<vector>
CAPITOLUL 9. IOI 2012 1016

17
18 using namespace std;
19
20 vector<int>E[101000];
21 int n, SZ[101000], P[101000];
22 long long Res, D[101000], C[101000];
23
24 struct point
25 {
26 int x, y, num;
27 bool operator < (const point &p) const
28 {
29 return x != p.x ? x < p.x : y < p.y;
30 }
31 } w[101000], w2[101000], L[101000];
32
33 void DFS(int a, int pp)
34 {
35 int i;
36 C[a] = SZ[a];
37 for (i = 0; i < E[a].size(); i++)
38 {
39 if (E[a][i] != pp)
40 {
41 DFS(E[a][i], a);
42 C[a] += C[E[a][i]];
43 }
44 }
45
46 Res += C[a] * (n-C[a]);
47 }
48
49 void Do()
50 {
51 int i, cnt = 0, a, b, j, cc = 0;
52 sort(w, w + n);
53 for (i = 0; i < n; i++)
54 {
55 if (!i || w[i].x != w[i - 1].x || w[i - 1].y + 1 != w[i].y)
56 SZ[++cnt] = 0;
57 SZ[cnt]++;
58 w2[i].num = cnt, w2[i].x = w[i].y, w2[i].y = w[i].x;
59 }
60
61 for (i = 1; i <= cnt; i++)P[i] = 0;
62 sort(w2, w2 + n);
63 for (i = 0; i < n - 1; i++)
64 {
65 if (w2[i].x == w2[i + 1].x && w2[i].y + 1 == w2[i + 1].y)
66 {
67 a = w2[i].num, b = w2[i + 1].num;
68 if (P[a] != b)
69 {
70 P[a] = b;
71 L[cc].x = a, L[cc].y = b; cc++;
72 }
73 }
74 }
75
76 for (i = 0; i < cc; i++)
77 {
78 E[L[i].x].push_back(L[i].y);
79 E[L[i].y].push_back(L[i].x);
80 }
81
82 DFS(1, 0);
83 for (i = 1; i <= cnt; i++)E[i].clear();
84 }
85
86 int DistanceSum(int N, int *X, int *Y)
87 {
88 int i;
89 n = N;
90 for (i = 0; i < N; i++)
91 {
92 w[i].x = X[i], w[i].y = Y[i];
CAPITOLUL 9. IOI 2012 1017

93 }
94
95 Do();
96 for (i = 0; i < N; i++)
97 {
98 w[i].x = Y[i], w[i].y = X[i];
99 }
100
101 Do();
102 return Res%1000000000;
103 }
104
105 // --------------- begin grader --------------
106
107 int main()
108 {
109 auto t1 = clock();
110
111 std::freopen("../input/input40.txt", "r", stdin);
112 std::freopen("city.out", "w", stdout);
113
114 int tmp;
115
116 /* Set input and output buffering */
117 char *inbuf, *outbuf;
118
119 inbuf = (char*) malloc(inbuf_len * sizeof(char));
120 outbuf = (char*) malloc(outbuf_len * sizeof(char));
121
122 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
123 assert(tmp == 0);
124
125 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
126 assert(tmp == 0);
127
128 int N, i;
129 tmp = scanf("%d", &N);
130 assert(tmp == 1);
131
132 int *sq_x, *sq_y;
133 sq_x = (int*) malloc(N * sizeof(int));
134 sq_y = (int*) malloc(N * sizeof(int));
135
136 for (i = 0; i < N; i++)
137 {
138 tmp = scanf("%d %d", &sq_x[i], &sq_y[i]);
139 assert(tmp == 2);
140 }
141
142 auto t2 = clock();
143
144 int ds = DistanceSum(N, sq_x, sq_y);
145
146 auto t3 = clock();
147
148 printf("%d\n", ds);
149
150 auto t4 = clock();
151
152 // reset console output
153 freopen("CON", "w", stdout);
154
155 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
156 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
157 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
158
159 return 0;
160
161 }
162
163 // --------------- begin grader --------------
164 /*
165 t2-t1 = 0.063
166 t3-t2 = 0.312
167 t4-t3 = 0
168
CAPITOLUL 9. IOI 2012 1018

169 Process returned 0 (0x0) execution time : 0.422 s


170 Press any key to continue.
171 */

Listing 9.4.6: checkerCity.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../input/input40.txt",
14 (char*)"city.out",
15 (char*)"../output/output40.txt",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("city", argc, argv);
24 compareRemainingLines();
25 }

9.4.3 *Rezolvare detaliat 

9.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 
"online" (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 simplicat. Presupunem c  exist  N culori numerotate de la 0
la N  1, ³i în ecare 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, ecare
î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 9. IOI 2012 1019

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  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 ecare 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  niciodat  necesar  în viitor, asistentul
trebuie s  elimine o astfel de culoare de pe schel .
ˆ Altfel, culoarea eliminat  de pe schel  trebuie s  e aceea care va  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  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 a  deja pe schel , deci asistentul se poate odihni.
ˆ Pentru a treia cerere (culoarea 3), asistentul scoate culoarea 0, schimbând schela în congu-
raµia [3, 2].
ˆ În nal, a patra cerere (culoarea 0) trebuie s  e 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  eliminat culoarea 2 la pasul 3, deci asistentul s-ar  putut odihni din
nou în pasul nal.
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  s  examineze starea schelei
dup  ecare cerere. De exemplu, când folose³te strategia (neoptim ) dat  în exemplul 1, secvenµa
st rilor schelei ar  [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 sucienµ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 decodica aceast  secvenµ  ajut toare ³i o poate utiliza în
alegerile sale.
CAPITOLUL 9. IOI 2012 1020

(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
 executate pe rând, f r  ca acestea s  poat  comunica între ele în timpul execuµiei.
Primul program va  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  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 ecare element curent trebuie s  e procesat
înainte de a  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 (ecare
î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 ecare 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 deniµ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 ecare
dat  primind una din cele N cereri ale lui Leonardo, în ordine. Dup  ecare apel GetRequest,
dac  culoarea returnat  nu se a  pe schel , trebuie apelat PutBack(T) cu T ales de tine. Dac 
culoarea se a  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  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  e 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), Wri-
teAdvice(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  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 9. IOI 2012 1021

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 :
ˆ 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  ³iere în acela³i limbaj de programare.
Primul ³ier trebuie numit advisor.c, advisor.cpp sau advisor.pas. Acest ³ier tre-
buie s  implementeze rutina ComputeAdvice dup  cum a fost descris  mai sus ³i trebuie s 
apeleze WriteAdvice. Al doilea ³ier trebuie numit assistant.c, assistant.cpp sau
assistant.pas. Acest ³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 9. IOI 2012 1022

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
ecare program) cu acela³i nume. Submisiile voastre nu au voie s  interacµioneze cu intrarea/ie³irea
standard, sau cu orice alt ³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 ³ierul advisor.h în programul advisor ³i
assistant.h în programul assistant. Acest lucru poate  realizat inserând în surs  linia:

#include "advisor.h"

sau

#include "assistant.h"

Cele dou  ³iere advisor.h ³i assistant.h vor  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  realizat inserând în surs  linia:

uses advisorlib;

sau

uses assistantlib;

Cele dou  ³iere advisorlib.pas ³i assistantlib.pas vor  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 ³ier


advice.txt conµinând biµii secvenµei ajut toare. Biµii vor  separaµi prin spaµii, iar ³ierul
se va termina cu cifra 2.
Apoi se va trece la execuµia rutinei Assist, ³i se va genera output-ul în care, ecare linie
este e de forma "R [number]", e 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  pus  înapoi pe raft. Output-ul este terminat
cu o linie de forma "E".
Preciz m c  timpul de rulare al evaluatorului ocial poate s  difere puµin faµ  de cel de pe
calculatorul local. Aceast  diferenµ  nu ar trebui s  e semnicativ . Totu³i, v  sf tuim s  utilizaµi
interfaµa de testare pentru a verica dac  soluµia voastr  se încadreaz  în timp.
Limtele de timp ³i memorie
ˆ Limita de timp: 7 secunde.
ˆ Limita de memorie: 256 MB.
CAPITOLUL 9. IOI 2012 1023

9.5.1 Indicaµii de rezolvare

Let us rst 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
scaold 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 scaold): this way
we might specify exactly which color (within the K colors available on the scaold) 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 scaold between active and passive: an active color
is one that will be requested before it is removed from the scaold 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 specied 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.

9.5.2 Coduri surs 

Listing 9.5.1: supper.cpp


1 // https://oj.uz/submission/231107
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <assert.h>
5
6 #include "advisor.h"
7 #include "assistant.h"
8
9 #include <cstdio>
10 #include <queue>
11 #include <cassert>
12
13 #include <stack>
14
15 #include<ctime>
16 #include<iostream>
17
18 using namespace std;
19
20 // ----------------- advisor ----------------
21
22 int const MAXN = 100000;
23
24 struct Color
25 {
26 int id;
27 int np; // next position
CAPITOLUL 9. IOI 2012 1024

28
29 Color () {}
30
31 Color (int _id, int _np)
32 {
33 id = _id;
34 np = _np;
35 }
36 };
37
38 bool operator< (Color const &a, Color const &b)
39 {
40 return ( a.np < b.np );
41 }
42
43 int vnext[MAXN]; // vnext[i] = index of next occurrence of d[i] in array d
44 // (n if it is the last occurrence)
45 int position[MAXN]; // position[j] = index of the next occurrence of color j
46 // in array d (n if it is the last occurrence)
47
48 bool in_scaffolda[MAXN]; // whether color j is in the scaffold or not
49
50 priority_queue<Color> coda;
51
52 int solution[MAXN]; // -1 if no change is necessary,
53 // otherwise the color to be removed
54
55 queue<int> events[MAXN]; // events[j] is the queue of the events of color j:
56 // 1 = used; 0 = removed.
57
58 int accumulated = 0;
59 int counter = 0;
60
61 void ComputeAdvice (int *d, int n, int k, int m)
62 {
63 // Finding the next occurrence of each color
64 for (int i=0; i<n; ++i)
65 {
66 position[i] = n;
67 }
68
69 for (int i=n-1; i>=0; --i)
70 {
71 vnext[i] = position[d[i]];
72 position[d[i]] = i;
73 }
74
75 // Initial settings: inserting colors 0,...,k in the player
76 for (int j=0; j<k; ++j)
77 {
78 in_scaffolda[j] = 1;
79 coda.push( Color( j, position[j] ) );
80 }
81
82 // Finding the solution
83 for (int i=0; i<n; ++i)
84 {
85
86 int color = d[i];
87 if ( in_scaffolda[color] )
88 {
89 coda.push( Color( color, vnext[i] ) );
90 solution[i] = -1;
91 events[ color ].push( 1 );
92 continue;
93 }
94
95 // Removing useless color in scaffold
96 Color useless = coda.top();
97 coda.pop();
98 in_scaffolda[ useless.id ] = 0;
99
100 // Inserting new color in scaffold
101 coda.push( Color( color, vnext[i] ) );
102 in_scaffolda[ color ] = 1;
103
CAPITOLUL 9. IOI 2012 1025

104 // Writing the solution


105 solution[i] = useless.id;
106 events[ useless.id ].push( 0 );
107 events[ color ].push( 1 );
108 }
109
110 // Writing the advice
111 for (int j=0; j<k; ++j)
112 {
113
114 if ( events[j].empty() )
115 {
116 WriteAdvice(0);
117 continue;
118 }
119
120 if ( events[j].front() == 0 )
121 {
122 events[j].pop();
123 WriteAdvice(0);
124 }
125 else
126 {
127 WriteAdvice(1);
128 }
129
130 }
131
132 for (int i=0; i<n; ++i)
133 {
134
135 int color = d[i];
136 assert( events[ color ].front() == 1 );
137 events[ color ].pop();
138
139 if ( events[ color ].empty() )
140 {
141 // The color will not be used or removed any more
142 WriteAdvice(0);
143 continue;
144 }
145
146 if ( events[ color ].front() == 0 )
147 {
148 events[ color ].pop();
149 // The color is now inactive
150 WriteAdvice(0);
151 }
152 else
153 {
154 // The color is now active
155 WriteAdvice(1);
156 }
157
158 }
159 }
160
161 // ----------------- assistant ----------------
162
163 //int const MAXN = 100000;
164
165 bool in_the_scaffold[MAXN];
166 queue<int> passive; // queue of passive colors
167
168 void Assist (unsigned char *real_advice, int n, int k, int l)
169 {
170 for (int i=0; i<k; ++i)
171 {
172 in_the_scaffold[i] = 1;
173 if ( real_advice[i] == 0 ) passive.push(i);
174 }
175
176 for (int i=0; i<n; ++i)
177 {
178 int color = GetRequest();
179
CAPITOLUL 9. IOI 2012 1026

180 if ( !in_the_scaffold[color] )
181 {
182
183 in_the_scaffold[ passive.front() ] = 0;
184 in_the_scaffold[ color ] = 1;
185
186 PutBack( passive.front() );
187 passive.pop();
188
189 }
190
191 if ( real_advice[k+i] == 0 ) passive.push(color);
192 }
193 }
194
195 // ------------------- begin grader ----------------------
196
197 #ifndef TRUE
198 #define TRUE 1
199 #endif
200 #ifndef FALSE
201 #define FALSE 0
202 #endif
203
204 #define inbuf_len 1 << 16
205 #define outbuf_len 1 << 16
206
207 static int N, K, M;
208 static int *C;
209
210 static int R;
211 static unsigned char *A;
212
213 static FILE *fadvice;
214
215 static int current_request = -1;
216 static int *in_scaffold;
217 static int expect_put_back;
218
219 int GetRequest(void)
220 {
221 int req;
222
223 if (expect_put_back)
224 {
225 fprintf(stderr, "Not putting back color when it is not on the scaffold\n");
226 exit(1);
227 }
228
229 req = C[++current_request];
230
231 if (!in_scaffold[req])
232 expect_put_back = TRUE;
233 else
234 expect_put_back = FALSE;
235
236 printf("R %d\n", req);
237 return req;
238 }
239
240 void PutBack(int T)
241 {
242 int req;
243
244 if (!expect_put_back)
245 {
246 fprintf(stderr,"Putting back a color when it is already on the scaffold\n");
247 exit(1);
248 }
249
250 if (!in_scaffold[T])
251 {
252 fprintf(stderr, "Putting back a color that is not on the scaffold\n");
253 exit(1);
254 }
255
CAPITOLUL 9. IOI 2012 1027

256 req = C[current_request];


257 in_scaffold[req] = TRUE;
258 in_scaffold[T] = FALSE;
259 expect_put_back = FALSE;
260
261 printf("P %d\n", T);
262 }
263
264 void WriteAdvice(unsigned char a)
265 {
266 /* Normalize a to be 0 or 1 */
267 a = !!a;
268
269 if (R < M)
270 {
271 fprintf(fadvice, "%hhu ", a);
272 A[R] = a;
273 R++;
274 }
275 else
276 {
277 fprintf(stderr, "Advisor is providing too many bits of advice\n");
278 exit(1);
279 };
280 }
281
282 static inline void input_assert(int ok)
283 {
284 if (!ok)
285 {
286 fprintf(stderr, "Invalid input file.\n");
287 exit(1);
288 }
289 }
290
291 int main()
292 {
293 auto t1 = clock();
294
295 std::freopen("../input/input50.txt", "r", stdin);
296 std::freopen("supper.out", "w", stdout);
297
298 int tmp;
299 int i;
300
301 /* Set input and output buffering */
302 char *inbuf, *outbuf;
303 inbuf = (char*) malloc(inbuf_len * sizeof(char));
304 outbuf = (char*) malloc(outbuf_len * sizeof(char));
305 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
306 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
307
308 fadvice = fopen("advice.txt", "w");
309
310 tmp = scanf("%d %d %d", &N, &K, &M);
311 input_assert(tmp == 3);
312
313 C = (int*) malloc(N * sizeof(int));
314 for (i = 0; i < N; i++)
315 {
316 tmp = scanf("%d", &C[i]);
317 input_assert(tmp == 1);
318 }
319
320 auto t2 = clock();
321
322 A = (unsigned char*) malloc(M * sizeof(int));
323
324 ComputeAdvice(C, N, K, M);
325
326 fprintf(fadvice, "\n2\n");
327
328 auto t3 = clock();
329
330 /* Init the scaffold */
331 in_scaffold = (int*) malloc(N * sizeof(int));
CAPITOLUL 9. IOI 2012 1028

332 for (i = 0; i < K; i++)


333 in_scaffold[i] = TRUE;
334 for (i = K; i < N; i++)
335 in_scaffold[i] = FALSE;
336
337 Assist(A, N, K, R);
338
339 printf("E\n");
340
341 auto t4 = clock();
342
343 // reset console output
344 freopen("CON", "w", stdout);
345
346 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
347 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
348 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
349
350 return 0;
351 }
352 // ------------------- end grader ----------------------

Listing 9.5.2: supper-231107.cpp


1 // https://oj.uz/submission/231107
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <assert.h>
5
6 #include "advisor.h"
7 #include "assistant.h"
8
9 #include <bits/stdc++.h>
10
11 // ----------------- advisor ----------------
12
13 #define lp(i,a,b) for(int i = a ; i < b ; i++)
14 #define pii pair<int,int>
15 #define pb push_back
16 #define mk make_pair
17 #define ff first
18 #define ss second
19
20 const int MAXN = 1e5+10 ;
21 const int inf = 1e9+10 ;
22
23 using namespace std ;
24
25 bool is_active[MAXN*2] , scaffold[MAXN] ;
26 vector<int> cores[MAXN] ;
27 int ptr[MAXN] , active[MAXN] ;
28 set<pii> s ;
29
30 int get_next(int cor)
31 {
32 if( ptr[cor] >= (int)cores[cor].size() ) return inf ;
33 return cores[cor][ ptr[cor]++ ] ;
34 }
35
36 void ComputeAdvice(int *C, int N, int K, int M)
37 {
38 lp(i,0,N) cores[ C[i] ].pb( i ) ;
39 lp(i,0,K)
40 s.insert( mk( get_next(i) , i ) ),
41 scaffold[i] = true,
42 active[i] = N+i ;
43
44 for(int i = 0 ; i < N ; i++ )
45 {
46 active[ C[i] ] = i ;
47
48 if( scaffold[ C[i] ] )
49 {
50 s.erase( s.find( mk( i , C[i] ) ) ) ;
51 s.insert( mk( get_next(C[i]) , C[i] ) ) ;
CAPITOLUL 9. IOI 2012 1029

52
53 continue ;
54 }
55
56 auto par = *prev(s.end()) ;
57 s.erase( prev( s.end() ) ) ;
58
59 scaffold[ par.ss ] = false ;
60 scaffold[ C[i] ] = true ;
61
62 is_active[ active[par.ss] ] = true ;
63
64 int x = get_next( C[i] ) ;
65 if( x <= i ) x = get_next(C[i]) ;
66
67 s.insert( mk( x , C[i] ) ) ;
68 }
69
70 lp(i,0,K) WriteAdvice( is_active[N+i] ) ;
71 lp(i,0,N) WriteAdvice( is_active[i] ) ;
72 }
73
74 // ----------------- assistant ----------------
75
76 #define lp(i,a,b) for(int i = a ; i < b ; i++)
77 #define pii pair<int,int>
78 #define pb push_back
79 #define mk make_pair
80 #define ff first
81 #define ss second
82
83 //const int MAXN = 1e5+10 ;
84
85 using namespace std ;
86
87 set<int> actives ;
88 bool scaff[MAXN] ;
89
90 void Assist(unsigned char *A, int N, int K, int R)
91 {
92 int idx = -1 ;
93 lp(i,0,K)
94 {
95 scaff[i] = true ;
96 if( A[++idx] ) actives.insert(i) ;
97 }
98
99 lp(i,0,N)
100 {
101 int x = GetRequest() ;
102 if( scaff[x] )
103 {
104 if( A[++idx] ) actives.insert( x ) ;
105 continue ;
106 }
107
108 scaff[x] = true ;
109
110 int toErase = *actives.begin() ;
111 actives.erase( actives.begin() ) ;
112
113 scaff[ toErase ] = false ;
114
115 PutBack(toErase) ;
116
117 if( A[++idx] ) actives.insert(x) ;
118
119 }
120 }
121
122 // ------------------- begin grader ----------------------
123
124 #ifndef TRUE
125 #define TRUE 1
126 #endif
127 #ifndef FALSE
CAPITOLUL 9. IOI 2012 1030

128 #define FALSE 0


129 #endif
130
131 #define inbuf_len 1 << 16
132 #define outbuf_len 1 << 16
133
134 static int N, K, M;
135 static int *C;
136
137 static int R;
138 static unsigned char *A;
139
140 static FILE *fadvice;
141
142 static int current_request = -1;
143 static int *in_scaffold;
144 static int expect_put_back;
145
146 int GetRequest(void)
147 {
148 int req;
149
150 if (expect_put_back) {
151 fprintf(stderr, "Not putting back color when it is not on the scaffold\n");
152 exit(1);
153 }
154
155 req = C[++current_request];
156
157 if (!in_scaffold[req])
158 expect_put_back = TRUE;
159 else
160 expect_put_back = FALSE;
161
162 printf("R %d\n", req);
163 return req;
164 }
165
166 void PutBack(int T)
167 {
168 int req;
169
170 if (!expect_put_back)
171 {
172 fprintf(stderr,"Putting back a color when it is already on the scaffold\n");
173 exit(1);
174 }
175
176 if (!in_scaffold[T])
177 {
178 fprintf(stderr, "Putting back a color that is not on the scaffold\n");
179 exit(1);
180 }
181
182 req = C[current_request];
183 in_scaffold[req] = TRUE;
184 in_scaffold[T] = FALSE;
185 expect_put_back = FALSE;
186
187 printf("P %d\n", T);
188 }
189
190 void WriteAdvice(unsigned char a)
191 {
192 /* Normalize a to be 0 or 1 */
193 a = !!a;
194
195 if (R < M)
196 {
197 fprintf(fadvice, "%hhu ", a);
198 A[R] = a;
199 R++;
200 }
201 else
202 {
203 fprintf(stderr, "Advisor is providing too many bits of advice\n");
CAPITOLUL 9. IOI 2012 1031

204 exit(1);
205 };
206 }
207
208 static inline void input_assert(int ok)
209 {
210 if (!ok)
211 {
212 fprintf(stderr, "Invalid input file.\n");
213 exit(1);
214 }
215 }
216
217 int main()
218 {
219 auto t1 = clock();
220
221 std::freopen("../input/input50.txt", "r", stdin);
222 std::freopen("supper.out", "w", stdout);
223
224 int tmp;
225 int i;
226
227 /* Set input and output buffering */
228 char *inbuf, *outbuf;
229 inbuf = (char*) malloc(inbuf_len * sizeof(char));
230 outbuf = (char*) malloc(outbuf_len * sizeof(char));
231 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
232 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
233
234 fadvice = fopen("advice.txt", "w");
235
236 tmp = scanf("%d %d %d", &N, &K, &M);
237 input_assert(tmp == 3);
238
239 C = (int*) malloc(N * sizeof(int));
240 for (i = 0; i < N; i++)
241 {
242 tmp = scanf("%d", &C[i]);
243 input_assert(tmp == 1);
244 }
245
246 auto t2 = clock();
247
248 A = (unsigned char*) malloc(M * sizeof(int));
249
250 ComputeAdvice(C, N, K, M);
251
252 fprintf(fadvice, "\n2\n");
253
254 auto t3 = clock();
255
256 /* Init the scaffold */
257 in_scaffold = (int*) malloc(N * sizeof(int));
258 for (i = 0; i < K; i++)
259 in_scaffold[i] = TRUE;
260 for (i = K; i < N; i++)
261 in_scaffold[i] = FALSE;
262
263 Assist(A, N, K, R);
264
265 printf("E\n");
266
267 auto t4 = clock();
268
269 // reset console output
270 freopen("CON", "w", stdout);
271
272 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
273 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
274 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
275
276 return 0;
277 }
278 // ------------------- end grader ----------------------
CAPITOLUL 9. IOI 2012 1032

9.5.3 *Rezolvare detaliat 

9.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  desemnate de arbitru pentru cele C runde. La urma urmei el e Leonardo, ... ³i el
este sigur c  în ecare 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 ecare 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  [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  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 
cavalerul cu rangul R 3. Dup  acest  rund  rangurile cavalerilor din linie vor  [3, 4]. Runda
nala (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 ind 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 ind cel cu rangul R 3. Linia nal  este format  din cavalerii cu rangurile [3,
4], câ³tig tor ind 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 e 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 9. IOI 2012 1033

ˆ 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  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 ecare i, S i $ E i.

Toate apelurile acestei rutine vor  corecte: E i va  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 semnic  faptul c  cavalerul
întârziat este la începutul liniei, iar P N  1 semnic  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 ³ier, denumit tournament.c\verb,
tournament.cpp sau tournament.pas. Acest µier trebuie s  implementeze subprogramul
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 ³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 9. IOI 2012 1034

9.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 tournament’s 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 nding 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 nd 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 don’t need to actually try a position to see how
far up we can go: it is sucient to proceed as described in the rst 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.

9.6.2 Coduri surs 

Listing 9.6.1: tournament.cpp


1 /*
2 O(N*logN) solution for Tournament.
3
4 Author: Giovanni Paolini
5 */
6
7 #include<ctime>
8
9 #define inbuf_len 1 << 16
10 #define outbuf_len 1 << 16
11
12 #include <cstdio>
13 #include <iostream>
14 #include <cassert>
15 #include <vector>
16 #include <queue>
17
18 using namespace std;
19
20 int const MAXN = 2000000;
21 int const MAXLOGN = 22;
22
23 struct Node
CAPITOLUL 9. IOI 2012 1035

24 {
25 int start;
26 int end;
27
28 bool all_white; // True if the interval contains good knights only
29 bool almost_all_white; // True if the interval contains good knights only,
30 // except at most the first knight
31
32 int best_result; // The maximum length of a victory-path finishing in
33 // that node
34 int where_best; // Where is the best_result achieved
35
36 Node() {}
37
38 Node(int _start, int _end)
39 {
40 start = _start;
41 end = _end;
42 best_result = 0;
43 }
44 };
45
46 int n,c,o;
47
48 bool rankb[MAXN];
49
50 // Range tree
51 int range_tree[MAXLOGN][MAXN];
52 int size_of_the_range_tree; // the size of the range tree
53
54 void make_range_tree()
55 {
56
57 int m = n;
58 int step = 0;
59
60 while ( m > 0 )
61 {
62 for (int i=0; i<m; ++i)
63 {
64 if ( step == 0 ) range_tree[step][i] = 1;
65 else
66 {
67 range_tree[step][i] = range_tree[step-1][2*i] +
68 range_tree[step-1][2*i+1];
69 }
70 }
71
72 if ( m > 1 ) m = (m+1)/2;
73 else m = 0;
74 step++;
75 }
76
77 size_of_the_range_tree = step;
78
79 }
80
81 void change (int k, int delta)
82 { // Change the value of position k by delta
83 int m = k;
84 for (int step = 0; step < size_of_the_range_tree; ++step)
85 {
86 range_tree[step][m] += delta;
87 m /= 2;
88 }
89 }
90
91 int find_knight (int k, int step, int m)
92 { // Find the (initial) position of the k-th living knight,
93 // starting from a given node (step,m) of the range tree
94 if ( k > range_tree[step][m] ) return n;
95
96 if ( step == 0 )
97 {
98 assert(k == 1);
99 return m;
CAPITOLUL 9. IOI 2012 1036

100 }
101
102 if ( range_tree[step-1][2*m] >= k )
103 return find_knight( k, step-1, 2*m );
104 else
105 return find_knight( k - range_tree[step-1][2*m], step-1, 2*m+1 );
106 }
107
108 // Calls tree
109
110 vector<Node> calls_tree;
111
112 int father[MAXN]; // The index of current interval of the knight
113
114 int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
115 {
116 n = N;
117 c = C;
118 int o = R;
119
120 make_range_tree();
121
122 rankb[0] = 1;
123 Node nodo = Node( 0, 0 );
124 nodo.all_white = 1;
125 nodo.almost_all_white = 1;
126 nodo.where_best = 0;
127 calls_tree.push_back( nodo );
128 father[0] = 0;
129
130 for (int i=1; i<n; ++i)
131 {
132 int r = K[i-1];
133 rankb[i] = (r > o);
134 father[i] = i;
135
136 Node nodo = Node( i, i );
137 nodo.all_white = !rankb[i];
138 nodo.almost_all_white = 1;
139 nodo.where_best = i;
140 calls_tree.push_back( nodo );
141 }
142
143 for (int i=0; i<c; ++i)
144 {
145 int s = S[i]+1;
146 int e = E[i]+1;
147
148 int first = find_knight( s, size_of_the_range_tree-1, 0 );
149 int last = find_knight( e+1, size_of_the_range_tree-1, 0 ) - 1;
150
151 Node interval = Node( first, last ); // The new interval
152
153 bool all_white = 1;
154 bool almost_all_white = 1;
155
156 int best_child = -1;
157 int the_best = -1;
158
159 queue<int> to_change;
160
161 for (int j=s; j<=e; ++j)
162 {
163
164 int knight = find_knight( j, size_of_the_range_tree-1, 0 );
165 if ( j > s ) to_change.push( knight );
166 int old_int = father[knight];
167
168 father[knight] = calls_tree.size();
169
170 if ( calls_tree[old_int].all_white == 0 )
171 {
172 all_white = 0;
173
174 if ( j > s ) almost_all_white = 0;
175 else
CAPITOLUL 9. IOI 2012 1037

176 if ( calls_tree[old_int].almost_all_white == 0 )
177 almost_all_white = 0;
178 }
179
180 if ( calls_tree[old_int].best_result > the_best )
181 {
182 the_best = calls_tree[old_int].best_result;
183 best_child = old_int;
184 }
185 }
186
187 while ( !(to_change.empty()) )
188 {
189 int knight = to_change.front();
190 to_change.pop();
191 change( knight, -1 );
192 }
193
194 interval.all_white = all_white;
195 interval.almost_all_white = almost_all_white;
196
197 if ( almost_all_white )
198 {
199 interval.best_result = the_best + 1;
200 interval.where_best = calls_tree[ best_child ].where_best;
201 // Found an interval for which it is possible
202 // to win interval.best_result times by starting in
203 // position interval.where_best
204 }
205 else
206 {
207 interval.best_result = the_best;
208 interval.where_best = calls_tree[ best_child ].where_best;
209 }
210
211 calls_tree.push_back(interval);
212 }
213
214 int t = calls_tree.size();
215
216 return calls_tree[t-1].where_best;
217 }
218
219 // -------------- begin grader -------------------------
220
221 int main()
222 {
223 auto t1 = clock();
224
225 std::freopen("../input/input30.txt", "r", stdin);
226 std::freopen("tournament.out", "w", stdout);
227
228 int tmp;
229
230 /* Set input and output buffering */
231 char *inbuf, *outbuf;
232 inbuf = (char*) malloc(inbuf_len * sizeof(char));
233 outbuf = (char*) malloc(outbuf_len * sizeof(char));
234 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
235 assert(tmp == 0);
236 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
237 assert(tmp == 0);
238
239 auto t2 = clock();
240
241 int N, C, R;
242 int *K, *S, *E;
243 tmp = scanf("%d %d %d", &N, &C, &R);
244 assert(tmp == 3);
245 K = (int*) malloc((N-1) * sizeof(int));
246 S = (int*) malloc(C * sizeof(int));
247 E = (int*) malloc(C * sizeof(int));
248 int i;
249 for (i = 0; i < N-1; i++)
250 {
251 tmp = scanf("%d", &K[i]);
CAPITOLUL 9. IOI 2012 1038

252 assert(tmp == 1);


253 }
254 for (i = 0; i < C; i++)
255 {
256 tmp = scanf("%d %d", &S[i], &E[i]);
257 assert(tmp == 2);
258 }
259 auto t3 = clock();
260
261 printf("%d\n", GetBestPosition(N, C, R, K, S, E));
262
263 auto t4 = clock();
264
265 // reset console output
266 freopen("CON", "w", stdout);
267
268 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
269 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
270 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
271
272 return 0;
273 }
274
275 // -------------- end grader -------------------------
276 /*
277 t2-t1 = 0
278 t3-t2 = 0.187
279 t4-t3 = 0.109
280
281 Process returned 0 (0x0) execution time : 0.375 s
282 Press any key to continue.
283 */

Listing 9.6.2: tournament-2193.cpp


1 // https://oj.uz/submission/2193 46 ms 4616 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 using namespace std;
11
12 #define inbuf_len 1 << 16
13 #define outbuf_len 1 << 16
14
15 const int Z = 1 << 17;
16 int nextv[Z],cnt[Z*2];
17
18 int getnth(int n)
19 {
20 int x = 1;
21 while (x < Z)
22 {
23 x <<= 1;
24 if (cnt[x] < n) n -= cnt[x++];
25 }
26 return x - Z;
27 }
28
29 void up(int x, int p)
30 {
31 x += Z;
32 while (x)
33 {
34 cnt[x] += p;
35 x >>= 1;
36 }
37 }
38
39 int add[Z],res[Z];
40
CAPITOLUL 9. IOI 2012 1039

41 int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
42 {
43 int i,peo,x,y;
44
45 for (i=0;i<N;i++) nextv[i] = i+1, cnt[i+Z] = 1;
46 for (i=Z-1;i>=1;i--) cnt[i] = cnt[i*2] + cnt[i*2+1];
47
48 for (i=0;i<C;i++)
49 {
50 peo = E[i] - S[i];
51 S[i] = getnth(S[i]+1);
52 x = nextv[S[i]];
53 while (peo--)
54 {
55 up(x,-1);
56 x = nextv[x];
57 }
58 E[i] = x - 1;
59 nextv[S[i]] = x;
60 }
61
62 for (i=1;i<N;i++) add[i] = add[i-1] + (K[i-1] > R);
63
64 for (i=0;i<C;i++)
65 {
66 if (add[E[i]] == add[S[i]])
67 {
68 res[S[i]]++;
69 res[E[i]]--;
70 }
71 }
72
73 int s = 0, p = -1, ind;
74 for (i=0;i<N;i++)
75 {
76 s += res[i];
77 if (p < s)
78 {
79 p = s;
80 ind = i;
81 }
82 }
83
84 return ind;
85 }
86
87 // -------------- begin grader -------------------------
88
89 int main()
90 {
91 auto t1 = clock();
92
93 std::freopen("../input/input30.txt", "r", stdin);
94 std::freopen("tournament.out", "w", stdout);
95
96 int tmp;
97
98 /* Set input and output buffering */
99 char *inbuf, *outbuf;
100 inbuf = (char*) malloc(inbuf_len * sizeof(char));
101 outbuf = (char*) malloc(outbuf_len * sizeof(char));
102 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
103 assert(tmp == 0);
104 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
105 assert(tmp == 0);
106
107 auto t2 = clock();
108
109 int N, C, R;
110 int *K, *S, *E;
111 tmp = scanf("%d %d %d", &N, &C, &R);
112 assert(tmp == 3);
113 K = (int*) malloc((N-1) * sizeof(int));
114 S = (int*) malloc(C * sizeof(int));
115 E = (int*) malloc(C * sizeof(int));
116 int i;
CAPITOLUL 9. IOI 2012 1040

117 for (i = 0; i < N-1; i++)


118 {
119 tmp = scanf("%d", &K[i]);
120 assert(tmp == 1);
121 }
122 for (i = 0; i < C; i++) {
123 tmp = scanf("%d %d", &S[i], &E[i]);
124 assert(tmp == 2);
125 }
126 auto t3 = clock();
127
128 printf("%d\n", GetBestPosition(N, C, R, K, S, E));
129
130 auto t4 = clock();
131
132 // reset console output
133 freopen("CON", "w", stdout);
134
135 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
136 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
137 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
138
139 return 0;
140 }
141
142 // -------------- end grader -------------------------
143 /*
144 t2-t1 = 0
145 t3-t2 = 0.046
146 t4-t3 = 0.032
147
148 Process returned 0 (0x0) execution time : 0.109 s
149 Press any key to continue.
150 */

Listing 9.6.3: tournament-4657.cpp


1 // https://oj.uz/submission/4657 44 ms 4036 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define inbuf_len 1 << 16
11 #define outbuf_len 1 << 16
12
13 #include<algorithm>
14
15 using namespace std;
16
17 const int idx = 1<<17;
18 int T[1<<18];
19
20 void update(int a){a+=idx;while(a)T[a]--,a>>=1;}
21
22 int read1(int x)
23 {
24 if(x==-1)return -1;
25 int a=1;
26 while(a<idx)
27 {
28 if(T[2*a]<x)x-=T[2*a],a=2*a+1;
29 else a=2*a;
30 }
31 return a-idx;
32 }
33
34 int read2(int a,int b)
35 {
36 a+=idx,b+=idx;
37 int ret = 0;
38 while(a<=b)
CAPITOLUL 9. IOI 2012 1041

39 {
40 ret=max(ret,T[a]);
41 ret=max(ret,T[b]);
42 a = (a+1)>>1, b = (b-1)>>1;
43 }
44 return ret;
45 }
46
47 int sum[100010],ans;
48 int d[100010];
49
50 int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
51 {
52 int i;
53 for(i=0;i<N;i++)d[i]=i+1,T[i+idx]=1;
54 for(i=idx-1;i;i--)T[i]=T[i<<1] + T[i<<1|1];
55 for(i=0;i<C;i++)
56 {
57 int a = read1(S[i]+1);
58 int tmp = a;
59 for(int j=0;j<E[i]-S[i];j++)
60 {
61 tmp = d[tmp];
62 update(tmp);
63 }
64 d[a] = d[tmp];
65 E[i] = d[tmp]-1;
66 S[i] = a;
67 }
68 for(i=0;i<N-1;i++)T[i+idx]=K[i];
69 for(i=idx-1;i;i--)T[i]=max(T[i<<1],T[i<<1|1]);
70 for(i=0;i<C;i++)
71 {
72 int s = read2(S[i],E[i]-1);
73 if(s<R)sum[S[i]]++,sum[E[i]+1]--;
74 }
75 for(i=0;i<N;i++)sum[i]+=sum[i-1];
76 for(i=0;i<N;i++)if(sum[ans]<sum[i])ans=i;
77 return ans;
78 }
79
80 // -------------- begin grader -------------------------
81
82 int main()
83 {
84 auto t1 = clock();
85
86 std::freopen("../input/input30.txt", "r", stdin);
87 std::freopen("tournament.out", "w", stdout);
88
89 int tmp;
90
91 /* Set input and output buffering */
92 char *inbuf, *outbuf;
93 inbuf = (char*) malloc(inbuf_len * sizeof(char));
94 outbuf = (char*) malloc(outbuf_len * sizeof(char));
95 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
96 assert(tmp == 0);
97 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
98 assert(tmp == 0);
99
100 auto t2 = clock();
101
102 int N, C, R;
103 int *K, *S, *E;
104 tmp = scanf("%d %d %d", &N, &C, &R);
105 assert(tmp == 3);
106 K = (int*) malloc((N-1) * sizeof(int));
107 S = (int*) malloc(C * sizeof(int));
108 E = (int*) malloc(C * sizeof(int));
109 int i;
110 for (i = 0; i < N-1; i++)
111 {
112 tmp = scanf("%d", &K[i]);
113 assert(tmp == 1);
114 }
CAPITOLUL 9. IOI 2012 1042

115 for (i = 0; i < C; i++)


116 {
117 tmp = scanf("%d %d", &S[i], &E[i]);
118 assert(tmp == 2);
119 }
120 auto t3 = clock();
121
122 printf("%d\n", GetBestPosition(N, C, R, K, S, E));
123
124 auto t4 = clock();
125
126 // reset console output
127 freopen("CON", "w", stdout);
128
129 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
130 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
131 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
132
133 return 0;
134 }
135
136 // -------------- end grader -------------------------
137 /*
138 t2-t1 = 0
139 t3-t2 = 0.062
140 t4-t3 = 0.031
141
142 Process returned 0 (0x0) execution time : 0.125 s
143 Press any key to continue.
144 */

Listing 9.6.4: tournament-14774.cpp


1 // https://oj.uz/submission/14774 77 ms 4164 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <assert.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define inbuf_len 1 << 16
11 #define outbuf_len 1 << 16
12
13 #include <cstdio>
14 #include <algorithm>
15
16 using namespace std;
17
18 struct bit
19 {
20 int tree[131073], lim;
21 void add(int x, int v)
22 {
23 while(x <= lim)
24 {
25 tree[x] += v;
26 x += x & -x;
27 }
28 }
29
30 int lbnd(int x)
31 {
32 int p = 0;
33 for(int i=lim/2; i; i >>= 1)
34 {
35 if(tree[p+i] < x)
36 {
37 x -= tree[p+i];
38 p += i;
39 }
40 }
41 return ++p;
42 }
CAPITOLUL 9. IOI 2012 1043

43
44 void init(int n)
45 {
46 for(lim = 1; lim <= n; lim <<= 1);
47 for(int i=1; i<=n; i++) add(i,1);
48 }
49 } bit;
50
51 struct rmq
52 {
53 int tree[265000], lim;
54 void init(int n, int* a)
55 {
56 for(lim = 1; lim <= n; lim <<= 1);
57 for(int i=0; i<n; i++)
58 {
59 tree[lim + i + 1] = a[i];
60 }
61
62 for(int i=lim-1; i; i--)
63 {
64 tree[i] = max(tree[2*i],tree[2*i+1]);
65 }
66 }
67
68 int q(int s, int e)
69 {
70 int ret = 0;
71 s += lim;
72 e += lim;
73 while(s < e)
74 {
75 if(s%2 == 1) ret = max(ret,tree[s++]);
76 if(e%2 == 0) ret = max(ret,tree[e--]);
77 s >>= 1;
78 e >>= 1;
79 }
80 if(s == e) ret = max(ret,tree[s]);
81 return ret;
82 }
83 } rmq;
84
85 int dx[100005];
86
87 int GetBestPosition(int N, int C, int R, int *K, int *S, int *E)
88 {
89 for (int i=0; i<C; i++)
90 {
91 S[i]++;
92 E[i]++;
93 }
94 bit.init(N);
95 rmq.init(N-1,K);
96 for (int i=0; i<C; i++)
97 {
98 int st = bit.lbnd(S[i]);
99 int ed = bit.lbnd(E[i] + 1) - 1;
100 for (int j=E[i]; j>S[i]; j--)
101 {
102 bit.add(bit.lbnd(j),-1);
103 }
104 S[i] = st;
105 E[i] = min(ed,N)-1;
106 if(rmq.q(S[i],E[i]) < R)
107 {
108 dx[S[i]]++;
109 dx[E[i]+1]--;
110 }
111 }
112 for (int i=1; i<=N-1; i++)
113 {
114 dx[i] += dx[i-1];
115 }
116 return (int)(max_element(dx+1,dx+N) - dx - 1);
117 }
118
CAPITOLUL 9. IOI 2012 1044

119 // -------------- begin grader -------------------------


120
121 int main()
122 {
123 auto t1 = clock();
124
125 std::freopen("../input/input30.txt", "r", stdin);
126 std::freopen("tournament.out", "w", stdout);
127
128 int tmp;
129
130 /* Set input and output buffering */
131 char *inbuf, *outbuf;
132 inbuf = (char*) malloc(inbuf_len * sizeof(char));
133 outbuf = (char*) malloc(outbuf_len * sizeof(char));
134 tmp = setvbuf(stdin, inbuf, _IOFBF, inbuf_len);
135 assert(tmp == 0);
136 tmp = setvbuf(stdout, outbuf, _IOFBF, outbuf_len);
137 assert(tmp == 0);
138
139 auto t2 = clock();
140
141 int N, C, R;
142 int *K, *S, *E;
143 tmp = scanf("%d %d %d", &N, &C, &R);
144 assert(tmp == 3);
145 K = (int*) malloc((N-1) * sizeof(int));
146 S = (int*) malloc(C * sizeof(int));
147 E = (int*) malloc(C * sizeof(int));
148 int i;
149 for (i = 0; i < N-1; i++)
150 {
151 tmp = scanf("%d", &K[i]);
152 assert(tmp == 1);
153 }
154 for (i = 0; i < C; i++)
155 {
156 tmp = scanf("%d %d", &S[i], &E[i]);
157 assert(tmp == 2);
158 }
159 auto t3 = clock();
160
161 printf("%d\n", GetBestPosition(N, C, R, K, S, E));
162
163
164 auto t4 = clock();
165
166 // reset console output
167 freopen("CON", "w", stdout);
168
169 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
170 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
171 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
172
173 return 0;
174 }
175
176 // -------------- end grader -------------------------
177 /*
178 t2-t1 = 0
179 t3-t2 = 0.062
180 t4-t3 = 0.063
181
182 Process returned 0 (0x0) execution time : 0.156 s
183 Press any key to continue.
184 */

Listing 9.6.5: checkerTournament.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
CAPITOLUL 9. IOI 2012 1045

7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../input/input30.txt",
14 (char*)"tournament.out",
15 (char*)"../output/output30.txt",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("tournament", argc, argv);
24 compareRemainingLines();
25 }

9.6.3 *Rezolvare detaliat 


Capitolul 10

IOI 201135

10.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 dierent 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 dierent for each group of
students.
Somhed wonders how many dierent 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 nish 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
nish their route at fountain number P .
Your task
Given the information on the fountains and the trails, you have to nd 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.
35
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).

1046
CAPITOLUL 10. IOI 2011 1047

ˆ 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 nd 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 rst 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 rst group, there is only one valid route that rea-
ches 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 rst call answer(1) to report the answer for the rst 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
ˆ Q 1
CAPITOLUL 10. IOI 2011 1048

ˆ 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 les should contain precisely the text  Correct.

10.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 specied 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 ecient 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
¬
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.
CAPITOLUL 10. IOI 2011 1049

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 rst 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
rst 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 nd 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 sucient 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 rst 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 specied 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 nd the destination of each path in
O log K . Thus, the total running time is O M  N Q log K , which is sucient 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 rst 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 .

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 .
CAPITOLUL 10. IOI 2011 1050

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-rst 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 rst 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 rst create an array for storing Fs for each vertex s. We then initialize Fs and C
to 1, and update them during the depth-rst search. We perform the search twice, starting at vP
¬
and vP , respectively.
Since the depth-rst 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 sucient
to obtain full marks for this task.

10.1.2 Coduri surs 

Listing 10.1.1: garden-49449.cpp


1 //https://oj.uz/submission/49449 187 ms 31352 KB
2
3 #include "garden.h"
4 #include "gardenlib.h"
5
6 #include<ctime>
7 #include<iostream>
8
9 #include <cstdio>
10 #include <vector>
11 #include <initializer_list>
12
13 using namespace std;
14
15 #define MAX_M 1000000
16 #define MAX_Q 2000
17
18 static int N, M, P, Q;
19 static int R[MAX_M][2];
20 static int G[MAX_Q];
21 static int solutions[MAX_Q];
22 static int answers[MAX_Q];
23 static int answer_count;
24
25 inline void my_assert(int e) {if (!e) abort();}
26
27 const int INF = 0x3f3f3f3f;
28 const int MAXN = 151515;
29 int minv1[MAXN], minv2[MAXN];
30
31 int outlist[2*MAXN];
32 vector<int> inlist[2*MAXN];
CAPITOLUL 10. IOI 2011 1051

33 bool visit[2*MAXN];
34 int ans1[2*MAXN], ans2[2*MAXN];
35
36 void run(int P, int dist, int *ans, int &res, int oP)
37 {
38 if(visit[P])
39 {
40 if(P == oP) res = dist;
41 return;
42 }
43 visit[P] = true;
44 if(P<N) ans[dist]++;
45 for(auto x: inlist[P])
46 run(x, dist+1, ans, res, oP);
47 }
48
49 void count_routes(int _N, int M, int P, int R[][2], int Q, int G[])
50 {
51 N = _N;
52 for(int i=0; i<N; ++i) minv1[i] = minv2[i] = -1;
53 for(int i=0; i<M; ++i)
54 {
55 int u = R[i][0], v = R[i][1];
56 for(int x: {u, v})
57 {
58 int y = u+v-x;
59 if(minv1[x] == -1) minv1[x] = y;
60 else if(minv2[x] == -1) minv2[x] = y;
61 }
62 }
63
64 for(int i=0; i<N; ++i)
65 if(minv2[i] == -1)
66 minv2[i] = minv1[i];
67
68 for(int i=0; i<2*N; ++i)
69 {
70 int to = (i<N) ? minv1[i%N] : minv2[i%N];
71 if(i%N == minv1[to]) to += N;
72 outlist[i] = to;
73 inlist[to].push_back(i);
74 }
75
76 int res1 = 0x3f3f3f3f, res2 = 0x3f3f3f3f;
77 run(P, 0, ans1, res1, P);
78 for(int i=0; i<2*N; ++i) visit[i] = false;
79 run(P+N, 0, ans2, res2, P+N);
80
81 for(int i=0; i<Q; ++i)
82 {
83 int ans = 0;
84 for(int j=G[i]%res1; j<=G[i] && j <= N; j += res1)
85 ans += ans1[j];
86 for(int j=G[i]%res2; j<=G[i] && j <= N; j += res2)
87 ans += ans2[j];
88 answer(ans);
89 }
90 return ;
91 }
92
93 // ------------- begin grader -----------------------
94
95 void read_input()
96 {
97 int i;
98 my_assert(3==scanf("%d %d %d",&N,&M,&P));
99 for(i=0; i<M; i++)
100 my_assert(2==scanf("%d %d",&R[i][0],&R[i][1]));
101 my_assert(1==scanf("%d",&Q));
102 for(i=0; i<Q; i++)
103 my_assert(1==scanf("%d",&G[i]));
104 for(i=0; i<Q; i++)
105 my_assert(1==scanf("%d",&solutions[i]));
106 }
107
108 void answer(int x)
CAPITOLUL 10. IOI 2011 1052

109 {
110 if(answer_count>=Q)
111 {
112 printf("Incorrect. Too many answers.\n");
113 exit(0);
114 }
115 answers[answer_count] = x;
116 answer_count++;
117 }
118
119 int main()
120 {
121 auto t1 = clock();
122
123 std::freopen("../tests/subtask3/grader.in.14", "r", stdin);
124 std::freopen("garden.out", "w", stdout);
125
126 int correct, i;
127
128 read_input();
129
130 auto t2 = clock();
131
132 answer_count = 0;
133 count_routes(N,M,P,R,Q,G);
134
135 auto t3 = clock();
136
137 if(answer_count!=Q)
138 {
139 printf("Incorrect. Too few answers.\n");
140 exit(0);
141 }
142
143 correct = 1;
144 for(i=0; i<Q; i++)
145 if(answers[i]!=solutions[i])
146 correct = 0;
147
148 if(correct)
149 printf("Correct.\n");
150 else
151 {
152 printf("Incorrect.\n");
153 printf("Expected: ");
154 for(i=0; i<Q; i++)
155 printf("%d ",solutions[i]);
156 printf("\nReturned: ");
157 for(i=0; i<Q; i++)
158 printf("%d ",answers[i]);
159 }
160
161 auto t4 = clock();
162
163 // reset console output
164 freopen("CON", "w", stdout);
165
166 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
167 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
168 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
169 return 0;
170 }
171
172 // ------------- end grader -----------------------
173 /*
174 t2-t1 = 0.484
175 t3-t2 = 0.235
176 t4-t3 = 0
177
178 Process returned 0 (0x0) execution time : 0.781 s
179 Press any key to continue.
180 */

Listing 10.1.2: garden-49789.cpp


CAPITOLUL 10. IOI 2011 1053

1 // https://oj.uz/submission/49789 69 ms 13788 KB
2
3 #include "garden.h"
4 #include "gardenlib.h"
5
6 #include<ctime>
7 #include<iostream>
8
9 #define MAX_M 1000000
10 #define MAX_Q 2000
11
12 static int N, M, P, Q;
13 static int R[MAX_M][2];
14 static int G[MAX_Q];
15 static int solutions[MAX_Q];
16 static int answers[MAX_Q];
17 static int answer_count;
18
19 inline void my_assert(int e) {if (!e) abort();}
20
21 #include <cstring>
22
23 using namespace std;
24
25 void answer(int x);
26
27 const int MAXN = 150000;
28 const int MAXN2 = MAXN * 2;
29
30 int arr[MAXN2 + 10];
31 int dis[2][MAXN2 + 10];
32 bool chk[2][MAXN2 + 10];
33
34 void f(int x, int p)
35 {
36 chk[p % 2][x] = 1;
37 if(arr[x] == p)
38 {
39 dis[p % 2][x] = 1;
40 return;
41 }
42
43 if(!chk[p % 2][arr[x]])
44 f(arr[x], p);
45
46 if(dis[p % 2][arr[x]])
47 dis[p % 2][x] = dis[p % 2][arr[x]] + 1;
48 }
49
50 int mem1[MAXN + 10];
51 int mem2[MAXN + 10];
52 int sum[2][MAXN2 + 10];
53
54 void count_routes(int N, int M, int P, int R[][2], int Q, int G[])
55 {
56 memset(mem1, -1, sizeof mem1);
57 memset(mem2, -1, sizeof mem2);
58 memset(arr, -1, sizeof arr);
59
60 for(int i = 0; i < M; i++)
61 {
62 int x = R[i][0];
63 int y = R[i][1];
64
65 if(mem1[x] == -1)
66 mem1[x] = y;
67 else if(mem2[x] == -1)
68 mem2[x] = y;
69
70 if(mem1[y] == -1)
71 mem1[y] = x;
72 else if(mem2[y] == -1)
73 mem2[y] = x;
74 }
75
76 for(int i = 0; i < N; i++)
CAPITOLUL 10. IOI 2011 1054

77 {
78 if(mem1[mem1[i]] == i && mem2[mem1[i]] != -1)
79 arr[2 * i] = 2 * mem1[i] + 1;
80 else
81 arr[2 * i] = 2 * mem1[i];
82
83 if(mem2[i] != -1)
84 {
85 if(mem1[mem2[i]] == i && mem2[mem2[i]] != -1)
86 arr[2 * i + 1] = 2 * mem2[i] + 1;
87 else
88 arr[2 * i + 1] = 2 * mem2[i];
89 }
90 }
91
92 for(int r = 0; r < 2; r++)
93 for(int i = 0; i < 2 * N; i++)
94 if(!chk[r][i] && arr[i] != -1)
95 f(i, 2 * P + r);
96
97 for(int r = 0; r < 2; r++)
98 for(int i = 0; i < N; i++)
99 if(dis[r][2 * i])
100 sum[r][dis[r][2 * i]]++;
101
102 for(int r = 0; r < 2; r++)
103 {
104 int c = dis[r][2 * P + r];
105 if(!c)
106 continue;
107
108 for(int i = 1; i <= 2 * N; i++)
109 if(i - c >= 1)
110 sum[r][i] += sum[r][i - c];
111 }
112
113 int c[2] = { dis[0][2 * P], dis[1][2 * P + 1] };
114
115 if(!c[0])
116 c[0] = c[1];
117 if(!c[1])
118 c[1] = c[0];
119 for(int i = 0; i < Q; i++)
120 {
121 int ans = 0;
122 for(int r=0;r<2;r++)
123 {
124 int t = G[i];
125 if(t > 2 * N && c[r])
126 t -= ((t - 2 * N + c[r] - 1) / c[r]) * c[r];
127
128 if(t <= 2 * N)
129 ans += sum[r][t];
130 }
131 answer(ans);
132 }
133 }
134
135 // ------------- begin grader -----------------------
136
137 void read_input()
138 {
139 int i;
140 my_assert(3==scanf("%d %d %d",&N,&M,&P));
141 for(i=0; i<M; i++)
142 my_assert(2==scanf("%d %d",&R[i][0],&R[i][1]));
143 my_assert(1==scanf("%d",&Q));
144 for(i=0; i<Q; i++)
145 my_assert(1==scanf("%d",&G[i]));
146 for(i=0; i<Q; i++)
147 my_assert(1==scanf("%d",&solutions[i]));
148 }
149
150 void answer(int x)
151 {
152 if(answer_count>=Q)
CAPITOLUL 10. IOI 2011 1055

153 {
154 printf("Incorrect. Too many answers.\n");
155 exit(0);
156 }
157 answers[answer_count] = x;
158 answer_count++;
159 }
160
161 int main()
162 {
163 auto t1 = clock();
164
165 std::freopen("../tests/subtask3/grader.in.14", "r", stdin);
166 std::freopen("garden.out", "w", stdout);
167
168 int correct, i;
169
170 read_input();
171
172 auto t2 = clock();
173
174 answer_count = 0;
175 count_routes(N,M,P,R,Q,G);
176
177 auto t3 = clock();
178
179 if(answer_count!=Q)
180 {
181 printf("Incorrect. Too few answers.\n");
182 exit(0);
183 }
184
185 correct = 1;
186 for(i=0; i<Q; i++)
187 if(answers[i]!=solutions[i])
188 correct = 0;
189 if(correct)
190 printf("Correct.\n");
191 else
192 {
193 printf("Incorrect.\n");
194 printf("Expected: ");
195 for(i=0; i<Q; i++)
196 printf("%d ",solutions[i]);
197 printf("\nReturned: ");
198 for(i=0; i<Q; i++)
199 printf("%d ",answers[i]);
200 }
201
202 auto t4 = clock();
203
204 // reset console output
205 freopen("CON", "w", stdout);
206
207 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
208 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
209 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
210 return 0;
211 }
212
213 // ------------- end grader -----------------------
214 /*
215 t2-t1 = 0.421
216 t3-t2 = 0.063
217 t4-t3 = 0
218
219 Process returned 0 (0x0) execution time : 0.500 s
220 Press any key to continue.
221 */

Listing 10.1.3: garden-116557.cpp


1 // https://oj.uz/submission/116557 3046 ms 29732 KB
2
3 #include "garden.h"
CAPITOLUL 10. IOI 2011 1056

4 #include "gardenlib.h"
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include<ctime>
9 #include<iostream>
10
11 #include <cstdio>
12 #include <vector>
13 #include <initializer_list>
14
15 #define MAX_M 1000000
16 #define MAX_Q 2000
17
18 static int N, M, P, Q;
19 static int R[MAX_M][2];
20 static int G[MAX_Q];
21 static int solutions[MAX_Q];
22 static int answers[MAX_Q];
23 static int answer_count;
24
25 inline void my_assert(int e) {if (!e) abort();}
26
27 #include <queue>
28
29 using namespace std;
30
31 const int INF=1e9+7;
32
33 static std::vector<int> adj[150005];
34 static int to[300005];
35 static std::vector<int> from[300005];
36
37 static int dist[2][300005];
38
39 static void bfs(int start,int* dist)
40 {
41 std::queue<int> q;
42 q.push(start);
43 while(!q.empty())
44 {
45 int node=q.front();
46 q.pop();
47 for(int child:from[node])
48 {
49 if(!dist[child])
50 {
51 dist[child]=dist[node]+1;
52 q.push(child);
53 }
54 }
55 }
56 }
57
58 void count_routes(int N, int M, int P, int R[][2], int Q, int G[])
59 {
60 for(int i=0;i<M;i++)
61 {
62 adj[R[i][0]].push_back(R[i][1]);
63 adj[R[i][1]].push_back(R[i][0]);
64 }
65
66 for(int i=0;i<N;i++)
67 {
68 to[i<<1]=(adj[i][0]<<1)|(i==adj[adj[i][0]][0]);
69 if(adj[i].size()>1)
70 {
71 to[i<<1|1]=(adj[i][1]<<1)|(i==adj[adj[i][1]][0]);
72 }
73 else
74 {
75 to[i<<1|1]=to[i<<1];
76 }
77 }
78
79 for(int i=0;i<N*2;i++)
CAPITOLUL 10. IOI 2011 1057

80 {
81 from[to[i]].push_back(i);
82 }
83 bfs(P<<1,dist[0]);
84 bfs(P<<1|1,dist[1]);
85 int cycle[2];
86 cycle[0]=dist[0][P<<1]?dist[0][P<<1]:INF;
87 cycle[1]=dist[1][P<<1|1]?dist[1][P<<1|1]:INF;
88 for(int q=0; q<Q; q++)
89 {
90 int K=G[q];
91 int ans=0;
92 for(int i=0;i<N*2;i+=2)
93 {
94 if(dist[0][i]) ans+=(K>=dist[0][i]&&(K-dist[0][i])%cycle[0]==0);
95 if(dist[1][i]) ans+=(K>=dist[1][i]&&(K-dist[1][i])%cycle[1]==0);
96 }
97 answer(ans);
98 }
99 }
100
101 // ------------- begin grader -----------------------
102
103 void read_input()
104 {
105 int i;
106 my_assert(3==scanf("%d %d %d",&N,&M,&P));
107 for(i=0; i<M; i++)
108 my_assert(2==scanf("%d %d",&R[i][0],&R[i][1]));
109 my_assert(1==scanf("%d",&Q));
110 for(i=0; i<Q; i++)
111 my_assert(1==scanf("%d",&G[i]));
112 for(i=0; i<Q; i++)
113 my_assert(1==scanf("%d",&solutions[i]));
114 }
115
116 void answer(int x)
117 {
118 if(answer_count>=Q) {
119 printf("Incorrect. Too many answers.\n");
120 exit(0);
121 }
122 answers[answer_count] = x;
123 answer_count++;
124 }
125
126 int main()
127 {
128 auto t1 = clock();
129
130 std::freopen("../tests/subtask3/grader.in.14", "r", stdin);
131 std::freopen("garden.out", "w", stdout);
132
133 int correct, i;
134
135 read_input();
136
137 auto t2 = clock();
138
139 answer_count = 0;
140 count_routes(N,M,P,R,Q,G);
141
142 auto t3 = clock();
143
144 if(answer_count!=Q)
145 {
146 printf("Incorrect. Too few answers.\n");
147 exit(0);
148 }
149
150 correct = 1;
151 for(i=0; i<Q; i++)
152 if(answers[i]!=solutions[i])
153 correct = 0;
154 if(correct)
155 printf("Correct.\n");
CAPITOLUL 10. IOI 2011 1058

156 else {
157 printf("Incorrect.\n");
158 printf("Expected: ");
159 for(i=0; i<Q; i++)
160 printf("%d ",solutions[i]);
161 printf("\nReturned: ");
162 for(i=0; i<Q; i++)
163 printf("%d ",answers[i]);
164 }
165
166 auto t4 = clock();
167
168 // reset console output
169 freopen("CON", "w", stdout);
170
171 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
172 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
173 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
174
175 return 0;
176 }
177
178 // ------------- end grader -----------------------
179 /*
180 t2-t1 = 0.109
181 t3-t2 = 2.985
182 t4-t3 = 0
183
184 Process returned 0 (0x0) execution time : 3.219 s
185 Press any key to continue.
186 */

Listing 10.1.4: checkerGarden.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/subtask3/grader.in.14",
14 (char*)"garden.out",
15 (char*)"../tests/subtask3/grader.expect.14",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("tournament", argc, argv);
24 compareRemainingLines();
25 }

10.1.3 *Rezolvare detaliat 

10.2 Race
Problema 2 - Race 100 de puncte
Author: Martin Fixman
CAPITOLUL 10. IOI 2011 1059

In conjunction with the IOI, Pattaya City will host a race: the International Olympiad in
Racing (IOR) 2011. As the host, we have to nd 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 dierent 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 specic regulations that require the course to be a path whose total length is
exactly K kilometers, starting and ending in dierent cities. Obviously, no highway (and therefore
also no city) may be used twice on the course to prevent collisions. To minimize trac 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 10. IOI 2011 1060

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 les should contain precisely the text  Correct.

10.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 nd the solution.
With this general approach in mind, we have to answer the following questions.
CAPITOLUL 10. IOI 2011 1061

ˆ How to nd 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 nd if there exists a path of length exactly K that contains u.
˜
If u is one of the endpoints in P , we can nd the path using one application of depth rst
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 nd x and y .
Consider some node w adjacent to u. With one application of DFS, we can nd the set Lw of
all path lengths for all paths starting at u and containing edge u, w.
Hence, to nd x and y , we need to nd 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 nd node u such that after deleting u, each resulting trees are all suciently
"small". In this case, we shall nd 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 rst.
¬
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 nd 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 d the central node in O N log N  time and O N  time.
2.1 Bottom-up approach
We can nd 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 nd 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 dened.
While performing DFS, we compute, for each node v , the number of its descendants D v .
With this information, we can gure 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 10. IOI 2011 1062

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

10.2.2 Coduri surs 

Listing 10.2.1: race-28930.cpp


1 // https://oj.uz/submission/28930 518 ms 66484 KB
2
3 #include "race.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 500000
11
12 static int N, K;
13 static int H[MAX_N][2];
14 static int L[MAX_N];
15 static int solution;
16
17 inline void my_assert(int e) {if (!e) abort();}
18
19 #include <vector>
20 #include <algorithm>
21 #include <unordered_map>
22
23 using namespace std;
24
25 const int MAXN = 200005;
26 struct EDGE { int x, w; };
27 int ans = -1;
28
29 vector <EDGE> v[MAXN];
30
31 void f(int x, int par, unordered_map <int, int> &d, int &L, int &W)
CAPITOLUL 10. IOI 2011 1063

32 {
33 d[0] = 0;
34 for (auto &y : v[x])
35 {
36 if (y.x == par) continue;
37 unordered_map <int, int> d2;
38 int L2 = 0, W2 = 0;
39 f(y.x, x, d2, L2, W2);
40 L2++;
41 W2 += y.w;
42 if (d.size() < d2.size())
43 {
44 d.swap(d2);
45 swap(L, L2);
46 swap(W, W2);
47 }
48
49 for (auto &t : d2)
50 {
51 if (d.count(K - (t.first + W2 + W)))
52 {
53 if (ans == -1 ||
54 ans > d[K - (t.first + W2 + W)] + t.second + L + L2)
55 ans = d[K - (t.first + W2 + W)] + t.second + L + L2;
56 }
57 }
58
59 for (auto &t : d2)
60 {
61 if (t.first + W2 > K) continue;
62 if (t.first + W2 == K)
63 {
64 if (ans == -1 || ans > t.second + L2)
65 ans = t.second + L2;
66 }
67 if (d.count(t.first + W2 - W))
68 d[t.first + W2 - W] = min(d[t.first + W2 - W],
69 t.second + L2 - L);
70 else
71 d[t.first + W2 - W] = t.second + L2 - L;
72 }
73 }
74 }
75
76 int best_path(int _N, int _K, int H[][2], int L[])
77 {
78 N = _N, K = _K;
79 for (int i = 0; i < N - 1; i++)
80 {
81 v[H[i][0]].push_back({ H[i][1], L[i] });
82 v[H[i][1]].push_back({ H[i][0], L[i] });
83 }
84
85 unordered_map <int, int> d;
86 int A = 0, B = 0;
87 f(0, -1, d, A, B);
88
89 return ans;
90 }
91
92 // ----------------------- begin grader -----------------
93
94 void read_input()
95 {
96 int i;
97 my_assert(2==scanf("%d %d",&N,&K));
98 for(i=0; i<N-1; i++)
99 my_assert(3==scanf("%d %d %d",&H[i][0],&H[i][1],&L[i]));
100 my_assert(1==scanf("%d",&solution));
101 }
102
103 int main()
104 {
105 auto t1 = clock();
106
107 std::freopen("../tests/subtask4/grader.in.16", "r", stdin);
CAPITOLUL 10. IOI 2011 1064

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


109
110 int ans;
111 read_input();
112
113 auto t2 = clock();
114
115 ans = best_path(N,K,H,L);
116
117 auto t3 = clock();
118
119 if(ans==solution)
120 printf("Correct.\n");
121 else
122 printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);
123
124 auto t4 = clock();
125
126 // reset console output
127 freopen("CON", "w", stdout);
128
129 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
130 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
131 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
132
133 return 0;
134 }
135
136 // ----------------------- end grader -----------------
137 /*
138 t2-t1 = 0.203
139 t3-t2 = 1.187
140 t4-t3 = 0
141
142 Process returned 0 (0x0) execution time : 1.797 s
143 Press any key to continue.
144 */

Listing 10.2.2: race-112526.cpp


1 // https://oj.uz/submission/112526 588 ms 85672 KB
2
3 #include<ctime>
4 #include<iostream>
5
6 #define MAX_N 500000
7
8 static int N, K;
9 static int H[MAX_N][2];
10 static int L[MAX_N];
11 static int solution;
12
13 inline void my_assert(int e) {if (!e) abort();}
14
15 #include <bits/stdc++.h>
16
17 using namespace std;
18
19 typedef pair<int,int> P;
20
21 #define F first
22 #define S second
23 #define PB push_back
24 #define INF 1000000000
25
26 int n,k,vs[200005],w[200005],la[200005],ans=INF;
27 vector<P>g[200005];
28 map<int,int>m[200005];
29
30 void merge(int x,int y)
31 {
32 if(m[vs[x]].size()<m[vs[y]].size()) swap(x,y);
33 int vx=vs[x],vy=vs[y];
34 for(map<int,int>::iterator i=m[vy].begin(); i!=m[vy].end(); i++)
35 {
CAPITOLUL 10. IOI 2011 1065

36 if(m[vx].find(k-w[vx]-w[vy]-i->F)!=m[vx].end())
37 ans=min(ans,m[vx][k-w[vx]-w[vy]-i->F] + i->S + la[vx]+la[vy]);
38 }
39
40 for(map<int,int>::iterator i=m[vy].begin();i!=m[vy].end();i++)
41 {
42 int c=i->F-w[vx]+w[vy];
43 if(m[vx].find(c)!=m[vx].end())
44 m[vx][c]=min(m[vx][c],i->S-la[vx]+la[vy]);
45 else
46 m[vx][c]=i->S-la[vx]+la[vy];
47 }
48
49 vs[y]=vx;
50 }
51
52 void dfs(int v,int p)
53 {
54 for(int i=0;i<g[v].size();i++)
55 {
56 int u=g[v][i].F,l=g[v][i].S;
57 if(u==p)continue;
58 dfs(u,v);
59 w[vs[u]]+=l;
60 la[vs[u]]++;
61 merge(v,u);
62 }
63 }
64
65 int best_path(int N,int K,int H[][2],int L[])
66 {
67 n=N;
68 k=K;
69 for(int i=0;i<n-1;i++)
70 {
71 int a=H[i][0],b=H[i][1],c=L[i];
72 g[a].PB(P(b,c));
73 g[b].PB(P(a,c));
74 }
75
76 for(int i=0;i<n;i++)
77 vs[i]=i,m[i][0]=0;
78
79 dfs(0,-1);
80
81 if(ans==INF)return -1;
82 else return ans;
83 }
84
85 // ----------------------- begin grader -----------------
86
87 void read_input()
88 {
89 int i;
90 my_assert(2==scanf("%d %d",&N,&K));
91 for(i=0; i<N-1; i++)
92 my_assert(3==scanf("%d %d %d",&H[i][0],&H[i][1],&L[i]));
93 my_assert(1==scanf("%d",&solution));
94 }
95
96 int main()
97 {
98 auto t1 = clock();
99
100 std::freopen("../tests/subtask4/grader.in.16", "r", stdin);
101 std::freopen("rase.out", "w", stdout);
102
103 int ans;
104 read_input();
105
106 auto t2 = clock();
107
108 ans = best_path(N,K,H,L);
109
110 auto t3 = clock();
111
CAPITOLUL 10. IOI 2011 1066

112 if(ans==solution)
113 printf("Correct.\n");
114 else
115 printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);
116
117 auto t4 = clock();
118
119 // reset console output
120 freopen("CON", "w", stdout);
121
122 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
123 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
124 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
125
126 return 0;
127 }
128
129 // ----------------------- end grader -----------------
130 /*
131 t2-t1 = 0.734
132 t3-t2 = 1.532
133 t4-t3 = 0
134
135 Process returned 0 (0x0) execution time : 3.075 s
136 Press any key to continue.
137 */

Listing 10.2.3: race-216556.cpp


1 // https://oj.uz/submission/216556 488 ms 33752 KB
2
3 #include "race.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 500000
11
12 static int N, K;
13 static int H[MAX_N][2];
14 static int L[MAX_N];
15 static int solution;
16
17 inline void my_assert(int e) {if (!e) abort();}
18
19 #include <bits/stdc++.h>
20
21 using namespace std;
22
23 #define in ({int x=0; \
24 int c=getchar(),n=0; \
25 for(;!isdigit(c);c=getchar()) n=(c==’-’); \
26 for(;isdigit(c);c=getchar()) x=x*10+c-’0’; \
27 n?-x:x;})
28
29 mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
30 int rnd(int l,int r){return l+rng()%(r-l+1);}
31
32 #define fasty ios_base::sync_with_stdio(0),cin.tie(0);
33 #define forinc(a,b,c) for(int a=b,_c=c;a<=_c;++a)
34 #define fordec(a,b,c) for(int a=b,_c=c;a>=_c;--a)
35 #define forv(a,b) for(auto&a:b)
36 #define fi first
37 #define se second
38 #define pb push_back
39 #define ii pair<int,int>
40 #define mt make_tuple
41 #define all(a) a.begin(),a.end()
42 #define reset(f, x) memset(f, x, sizeof(f))
43 #define bit(x,i) ((x>>(i-1))&1)
44 #define on(x,i) (x|(1ll<<(i-1)))
45 #define off(x,i) (x&~(1<<(i-1)))
46 #define gg exit(0);
CAPITOLUL 10. IOI 2011 1067

47
48 //#define unx 1
49 #ifndef unx
50 #include "race.h"
51 #endif
52
53 const int NN=200010;
54
55 int n,cnt,k,ret;
56 int sz[NN],dd[NN],f[NN*5];
57 vector<ii> val;
58 vector<ii> ad[NN];
59
60 void dfs1(int u,int p=-1)
61 {
62 sz[u]=1; cnt++;
63 forv(v,ad[u])
64 if(!dd[v.fi] && v.fi!=p)
65 dfs1(v.fi,u), sz[u]+=sz[v.fi];
66 }
67
68 int ftr(int u,int p=-1)
69 {
70 forv(v,ad[u])
71 if(!dd[v.fi] && v.fi!=p && sz[v.fi]>cnt/2)
72 return ftr(v.fi,u);
73 return u;
74 }
75
76 void dfs2(int u,int p,int c,int d)
77 {
78 if(c>k) return;
79 val.push_back({c,d});
80 forv(v,ad[u])
81 if(!dd[v.fi] && v.fi!=p && v.se+c<=k)
82 dfs2(v.fi,u,c+v.se,d+1);
83 }
84
85 void ctr(int u)
86 {
87 cnt=0;
88 dfs1(u);
89 dd[u=ftr(u)]=1;
90
91 f[0]=0;
92 vector<int> reval;
93 forv(v,ad[u])
94 if(!dd[v.fi])
95 {
96 dfs2(v.fi,u,v.se,1);
97 forv(i,val)
98 if(f[k-i.fi]>-1)
99 ret=min(ret,i.se+f[k-i.fi]);
100 forv(i,val)
101 f[i.fi]=f[i.fi]<0 ? i.se :
102 min(f[i.fi],i.se), reval.push_back(i.fi);
103 val.clear();
104 }
105
106 forv(i,reval) f[i]=-1;
107
108 forv(v,ad[u])
109 if(!dd[v.fi]) ctr(v.fi);
110 }
111
112 int best_path(int n,int _k,int h[][2],int l[])
113 {
114 k=_k;
115 ret=n;
116 forinc(i,0,n-2)
117 {
118 int u=h[i][0], v=h[i][1], w=l[i];
119 ad[u].push_back({v,w});
120 ad[v].push_back({u,w});
121 }
122 reset(f,-1);
CAPITOLUL 10. IOI 2011 1068

123 ctr(0);
124 return ret<n ? ret : -1;
125 }
126
127 // ----------------------- begin grader -----------------
128
129 void read_input()
130 {
131 int i;
132 my_assert(2==scanf("%d %d",&N,&K));
133 for(i=0; i<N-1; i++)
134 my_assert(3==scanf("%d %d %d",&H[i][0],&H[i][1],&L[i]));
135 my_assert(1==scanf("%d",&solution));
136 }
137
138 int main()
139 {
140 auto t1 = clock();
141
142 std::freopen("../tests/subtask4/grader.in.16", "r", stdin);
143 std::freopen("rase.out", "w", stdout);
144
145 int ans;
146 read_input();
147
148 auto t2 = clock();
149
150 ans = best_path(N,K,H,L);
151
152 auto t3 = clock();
153
154 if(ans==solution)
155 printf("Correct.\n");
156 else
157 printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);
158
159 auto t4 = clock();
160
161 // reset console output
162 freopen("CON", "w", stdout);
163
164 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
165 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
166 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
167
168 return 0;
169 }
170
171 // ----------------------- end grader -----------------
172 /*
173 t2-t1 = 0.188
174 t3-t2 = 1
175 t4-t3 = 0
176
177 Process returned 0 (0x0) execution time : 1.531 s
178 Press any key to continue.
179 */

Listing 10.2.4: checkerRace.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/subtask4/grader.in.16",
14 (char*)"rase.out",
15 (char*)"../tests/subtask4/grader.expect.16",
CAPITOLUL 10. IOI 2011 1069

16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("race", argc, argv);
24 compareRemainingLines();
25 }

10.2.3 *Rezolvare detaliat 

10.3 Rice Hub


Problema 3 - Rice Hub 100 de puncte
Author: Christian Kauth

In the countryside, you can nd a long straight road known as the Rice Way. Along this road
there are R rice elds. Each eld is located at an integer coordinate between 1 and L, inclusive.
The rice elds will be presented in non-decreasing order of their coordinates. Formally, for
0 & i $ R, rice eld i is at coordinate X i. You may assume that 1 & X 0 & ... & X R  1 & L.
Please note that multiple rice elds 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 elds, 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 elds.
Each rice eld 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 eld to the rice hub is numerically equal to the dierence 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 elds. The elds 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,
eld i is located at X i.
ˆ B - the budget.

Your procedure must nd 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 10. IOI 2011 1070

For this case, there are multiple optimal locations for the hub: you can place it anywhere
between locations 10 and 14, inclusive. The gure above shows one of these optimal locations.
You will then be able to transport rice from elds 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 elds, 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 elds 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 elds; 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 les should contain precisely the text Correct.
CAPITOLUL 10. IOI 2011 1071

10.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 elds located
at r0 & r1 & ... & rK 1 , the transportation cost from all these K elds 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 elds 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 .


1 An O R3  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
elds 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 suces to solve subtask 2.
2 An O R2  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 elds. Thus, for each S , the cost cost S  can be computed in O 1
after precomputing certain prex sums.
Specically, let T i be the sum of all coordinates to the left of rice eld i, i.e., T 0 0 and

= X j .
i1
T i
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 suces to solve subtask 3.
3 An O R log R solution
Applying a binary search to nd the right length in place of a linear search improves the
running time to O R log R and suces 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 elds. In particular, imagine
˜
adding in the rice elds one by one. In iteration i, we add ri and nd (1) Si i, the best solution
that uses only (a subsequence of) the rst i rice elds (i.e., Si N …r0 , ..., ri  1‹, and (2) Si , the
˜

best solution that uses only (a subsequence of) the rst i rice elds 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 prex 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 10. IOI 2011 1072

Since cost …rs , ..., rt ‹ takes O 1 to compute, the running time of this algorithm is O R and
suces to solve all subtasks.

10.3.2 Coduri surs 

Listing 10.3.1: racehub-7321.cpp


1 // https://oj.uz/submission/7321 24 ms 4992 KB
2
3 #include "ricehub.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_R 1000000
11
12 static int R, L;
13 static long long B;
14 static int X[MAX_R];
15 static int solution;
16
17 inline void my_assert(int e) {if (!e) abort();}
18
19
20 int besthub(int N, int L, int *X, long long B)
21 {
22 int A = 0;
23 long long S = 0;
24 for (int i=0,j=0;j<N;)
25 {
26 for (S+=X[j++]-X[(i+j)/2];S>B;S-=X[(i+j)/2]-X[i++]);
27 if (A < j - i) A = j - i;
28 }
29 return A;
30 }
31
32 // --------------- begin grader ---------------
33
34 static void read_input()
35 {
36 int i;
37 my_assert(3==scanf("%d %d %lld",&R,&L,&B));
38
39 for(i=0; i<R; i++)
40 my_assert(1==scanf("%d",&X[i]));
41
42 my_assert(1==scanf("%d",&solution));
43 }
44
45 int main()
46 {
47 auto t1 = clock();
48
49 std::freopen("../tests/subtask4/grader.in.19", "r", stdin);
50 std::freopen("rasehub.out", "w", stdout);
51
52 int ans;
53 read_input();
54
55 auto t2 = clock();
56
57 ans = besthub(R,L,X,B);
58
59 auto t3 = clock();
60
61 if(ans==solution)
62 printf("Correct.\n");
63 else
CAPITOLUL 10. IOI 2011 1073

64 printf("Incorrect. Returned %d instead of %d.\n",ans,solution);


65
66 auto t4 = clock();
67
68 // reset console output
69 freopen("CON", "w", stdout);
70
71 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
72 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
73 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
74
75 return 0;
76 }
77
78 // --------------- end grader ---------------
79 /*
80 t2-t1 = 0.046
81 t3-t2 = 0
82 t4-t3 = 0
83
84 Process returned 0 (0x0) execution time : 0.125 s
85 Press any key to continue.
86 */

Listing 10.3.2: racehub-95096.cpp


1 // https://oj.uz/submission/95096 17 ms 3420 KB
2
3 #include<ctime>
4 #include<iostream>
5
6 #define MAX_R 1000000
7
8 static int R, L;
9 static long long B;
10 static int X[MAX_R];
11 static int solution;
12
13 inline void my_assert(int e) {if (!e) abort();}
14
15 #include <bits/stdc++.h>
16
17 using namespace std;
18
19 typedef long long ll;
20 const int Nmax = 1e5 + 5;
21
22 ll x[Nmax], s[Nmax], lim;
23 int n;
24
25 bool check(int nr)
26 {
27 int i, m;
28 ll curr;
29 for(i=0; i<=n-nr; ++i)
30 {
31 m = (i + i + nr - 1) / 2;
32
33 curr = (m - i + 1) * x[m] - (s[m] - s[i-1]);
34 curr += (s[i+nr-1] - s[m]) - (i+nr-1 - m) * x[m];
35 if(curr <= lim) return 1;
36 }
37 return 0;
38 }
39
40 int besthub(int R, int L, int X[], ll B)
41 {
42 int st = 0, dr = R, mid, i;
43
44 n = R; lim = B;
45 for(i=0; i<n; ++i)
46 x[i] = X[i], s[i] = s[i-1] + x[i];
47
48 while(st <= dr)
49 {
CAPITOLUL 10. IOI 2011 1074

50 mid = (st + dr) / 2;


51 if(check(mid)) st = mid+1;
52 else dr = mid-1;
53 }
54 return dr;
55 }
56
57 // --------------- begin grader ---------------
58
59 static void read_input()
60 {
61 int i;
62 my_assert(3==scanf("%d %d %lld",&R,&L,&B));
63
64 for(i=0; i<R; i++)
65 my_assert(1==scanf("%d",&X[i]));
66
67 my_assert(1==scanf("%d",&solution));
68 }
69
70 int main()
71 {
72 auto t1 = clock();
73
74 std::freopen("../tests/subtask4/grader.in.19", "r", stdin);
75 std::freopen("rasehub.out", "w", stdout);
76
77 int ans;
78 read_input();
79
80 auto t2 = clock();
81
82 ans = besthub(R,L,X,B);
83
84 auto t3 = clock();
85
86 if(ans==solution)
87 printf("Correct.\n");
88 else
89 printf("Incorrect. Returned %d instead of %d.\n",ans,solution);
90
91 auto t4 = clock();
92
93 // reset console output
94 freopen("CON", "w", stdout);
95
96 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
97 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
98 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
99
100 return 0;
101 }
102
103 // --------------- end grader ---------------
104 /*
105 t2-t1 = 0.187
106 t3-t2 = 0
107 t4-t3 = 0
108
109 Process returned 0 (0x0) execution time : 0.266 s
110 Press any key to continue.
111 */

Listing 10.3.3: racehub-113697.cpp


1 // https://oj.uz/submission/113697 16 ms 1792 KB
2
3 #include<ctime>
4 #include<iostream>
5
6 #define MAX_R 1000000
7
8 static int R, L;
9 static long long B;
10 static int X[MAX_R];
CAPITOLUL 10. IOI 2011 1075

11 static int solution;


12
13 inline void my_assert(int e) {if (!e) abort();}
14
15 #include<bits/stdc++.h>
16
17 using namespace std;
18
19 int besthub(int r,int l,int x[],long long int b)
20 {
21 int M=0;
22 int ini=0,fim=0;
23 long long int soma=0;
24 while(fim<r)
25 {
26 if(soma<=b)
27 {
28 M=max(M,fim-ini+1);
29 int med=(fim+ini+1)/2;
30 fim++;
31 soma+=(x[fim]-x[med]);
32 }
33 else
34 {
35 int med=(fim+ini+1)/2;
36 soma-=(x[med]-x[ini]);
37 ini++;
38 }
39 }
40 return M;
41 }
42
43 // --------------- begin grader ---------------
44
45 static void read_input()
46 {
47 int i;
48 my_assert(3==scanf("%d %d %lld",&R,&L,&B));
49
50 for(i=0; i<R; i++)
51 my_assert(1==scanf("%d",&X[i]));
52
53 my_assert(1==scanf("%d",&solution));
54 }
55
56 int main()
57 {
58 auto t1 = clock();
59
60 std::freopen("../tests/subtask4/grader.in.19", "r", stdin);
61 std::freopen("rasehub.out", "w", stdout);
62
63 int ans;
64 read_input();
65
66 auto t2 = clock();
67
68 ans = besthub(R,L,X,B);
69
70 auto t3 = clock();
71
72 if(ans==solution)
73 printf("Correct.\n");
74 else
75 printf("Incorrect. Returned %d instead of %d.\n",ans,solution);
76
77 auto t4 = clock();
78
79 // reset console output
80 freopen("CON", "w", stdout);
81
82 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
83 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
84 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
85
86 return 0;
CAPITOLUL 10. IOI 2011 1076

87 }
88
89 // --------------- end grader ---------------
90 /*
91 t2-t1 = 0.187
92 t3-t2 = 0
93 t4-t3 = 0
94
95 Process returned 0 (0x0) execution time : 0.266 s
96 Press any key to continue.
97 */

Listing 10.3.4: racehub-116283.cpp


1 // https://oj.uz/submission/116283 18 ms 2936 KB
2
3 #include "ricehub.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_R 1000000
11
12 static int R, L;
13 static long long B;
14 static int X[MAX_R];
15 static int solution;
16
17 inline void my_assert(int e) {if (!e) abort();}
18
19 #include <vector>
20
21 int besthub(int R, int L, int X[], long long B)
22 {
23 std::vector<long long> prefix({0});
24 for(int i=0;i<R;i++)
25 {
26 prefix.push_back(prefix.back()+X[i]);
27 }
28 int best=0;
29 int j=1;
30 for(int i=1;i<=R;i++)
31 {
32 while(true)
33 {
34 int kl=(i+j)/2;
35 int kr=(i+j+1)/2;
36 long long cost=(prefix[i]-prefix[kr-1])-(prefix[kl]-prefix[j-1]);
37 if(cost<=B) break;
38 j++;
39 }
40 best=std::max(best,i-j+1);
41 }
42 return best;
43 }
44
45 // --------------- begin grader ---------------
46
47 static void read_input()
48 {
49 int i;
50 my_assert(3==scanf("%d %d %lld",&R,&L,&B));
51
52 for(i=0; i<R; i++)
53 my_assert(1==scanf("%d",&X[i]));
54
55 my_assert(1==scanf("%d",&solution));
56 }
57
58 int main()
59 {
60 auto t1 = clock();
61
CAPITOLUL 10. IOI 2011 1077

62 std::freopen("../tests/subtask4/grader.in.19", "r", stdin);


63 std::freopen("rasehub.out", "w", stdout);
64
65 int ans;
66 read_input();
67
68 auto t2 = clock();
69
70 ans = besthub(R,L,X,B);
71
72 auto t3 = clock();
73
74 if(ans==solution)
75 printf("Correct.\n");
76 else
77 printf("Incorrect. Returned %d instead of %d.\n",ans,solution);
78
79 auto t4 = clock();
80
81 // reset console output
82 freopen("CON", "w", stdout);
83
84 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
85 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
86 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
87
88 return 0;
89 }
90
91 // --------------- end grader ---------------
92 /*
93 t2-t1 = 0.062
94 t3-t2 = 0.016
95 t4-t3 = 0
96
97 Process returned 0 (0x0) execution time : 0.141 s
98 Press any key to continue.
99 */

Listing 10.3.5: checkerRaceHub.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/subtask4/grader.in.19",
14 (char*)"rasehub.out",
15 (char*)"../tests/subtask4/grader.expect.19",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("racehub", argc, argv);
24 compareRemainingLines();
25 }
CAPITOLUL 10. IOI 2011 1078

10.3.3 *Rezolvare detaliat 

10.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 dierent pair of distinct chambers. Running through dierent corridors may require dierent
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 nite 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 10. IOI 2011 1079

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 10. IOI 2011 1080

ˆ 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 les should contain precisely the text  Correct.

10.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. Specically,
we prove that rst, 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 rst and each
parent is visited after all its children); the nal answer is stored in T 0. The computation at
each node involves nding 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 10. IOI 2011 1081

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 dened.
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
36
computed by producing the list rv " N w  T v   L w, v x, sorting this list, and returning
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.

10.4.2 Coduri surs 

Listing 10.4.1: crocodile-10431.cpp


1 //https://oj.uz/submission/10431 576 ms 161392 KB
2
3 #include "crocodile.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 50000
11 #define MAX_M 10000000
12
13 static int N, M;
14 static int R[MAX_M][2];
15 static int L[MAX_M];
16 static int K;
17 static int P[MAX_N];
18 static int solution;
19
20 inline void my_assert(int e) {if (!e) abort();}
21
22 #include <vector>
23 #include <queue>
24 #include <utility>
25
26 using namespace std;
27
28 typedef pair<int,int> pi;
29
30 int v0[100005], v1[100005];
31 priority_queue<pi,vector<pi>,greater<pi> > pq;
32 vector<pi> graph[100005];
33
34 int travel_plan(int N, int M, int R[][2], int L[], int K, int P[])
35 {
36
N w denotes the set of neighbors of w
CAPITOLUL 10. IOI 2011 1082

36 for (int i=0; i<M; i++)


37 {
38 graph[R[i][0]].push_back(pi(L[i],R[i][1]));
39 graph[R[i][1]].push_back(pi(L[i],R[i][0]));
40 }
41
42 for (int i=0; i<K; i++)
43 {
44 v0[P[i]] = 1;
45 pq.push(pi(0,P[i]));
46 }
47
48 while (!pq.empty())
49 {
50 pi x = pq.top();
51 pq.pop();
52 if(v0[x.second] == 0)
53 {
54 v0[x.second] = 1;
55 continue;
56 }
57
58 if(v1[x.second]) continue;
59 v1[x.second] = 1;
60 if(x.second == 0) return x.first;
61 for (int i=0; i<graph[x.second].size(); i++)
62 {
63 pi t = graph[x.second][i];
64 if(v1[t.second]) continue;
65 pq.push(pi(t.first + x.first,t.second));
66 }
67 }
68
69 return -123; // ... !!!
70 }
71
72 // -------------- begin grader -----------------
73
74 void read_input()
75 {
76 int i;
77 my_assert(3==scanf("%d %d %d",&N,&M,&K));
78 for(i=0; i<M; i++)
79 my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
80 for(i=0; i<K; i++)
81 my_assert(1==scanf("%d",&P[i]));
82 my_assert(1==scanf("%d",&solution));
83 }
84
85 int main()
86 {
87 auto t1 = clock();
88
89 std::freopen("../tests/subtask3/grader.in.7", "r", stdin);
90 std::freopen("crocodile.out", "w", stdout);
91
92 int ans;
93 read_input();
94
95 auto t2 = clock();
96
97 ans = travel_plan(N,M,R,L,K,P);
98
99 auto t3 = clock();
100
101 if(ans==solution)
102 printf("Correct.\n");
103 else
104 printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);
105
106 auto t4 = clock();
107
108 // reset console output
109 freopen("CON", "w", stdout);
110
111 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 10. IOI 2011 1083

112 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
113 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
114
115 return 0;
116 }
117
118 // -------------- begin grader -----------------
119 /*
120 t2-t1 = 0.922
121 t3-t2 = 2.704
122 t4-t3 = 0
123
124 Process returned 0 (0x0) execution time : 3.689 s
125 Press any key to continue.
126 */

Listing 10.4.2: crocodile-16656.cpp


1 // https://oj.uz/submission/16656 444 ms 146380 KB
2
3 #include "crocodile.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 50000
11 #define MAX_M 10000000
12
13 static int N, M;
14 static int R[MAX_M][2];
15 static int L[MAX_M];
16 static int K;
17 static int P[MAX_N];
18 static int solution;
19
20 inline void my_assert(int e) {if (!e) abort();}
21
22 #include <queue>
23
24 #define NN 100000
25 #define MM 1000000
26 #define INF 1000000000
27
28 using namespace std;
29
30 struct Inque
31 {
32 int x;
33 int w;
34 Inque(int _x=0,int _w=0):x(_x),w(_w){}
35 bool operator<(const Inque &r)const
36 {
37 return w>r.w;
38 }
39 };
40
41 int sta[NN+1],chi[MM*2+1],wei[MM*2+1],nxt[MM*2+1],m,n;
42 int d[2][NN+1];
43 bool v[NN+1];
44
45 priority_queue<Inque> Q;
46
47 void addEdge(int x,int y,int w,int p)
48 {
49 nxt[p]=sta[x];
50 chi[p]=y;
51 wei[p]=w;
52 sta[x]=p;
53 }
54
55 int travel_plan(int _n, int _m, int R[][2], int L[], int K, int P[])
56 {
57 int i,j;
CAPITOLUL 10. IOI 2011 1084

58
59 n=_n; m=_m;
60
61 for(i=0;i<m;i++)
62 {
63 addEdge(R[i][0],R[i][1],L[i],i*2+1);
64 addEdge(R[i][1],R[i][0],L[i],i*2+2);
65 }
66
67 for(i=0;i<n;i++)
68 {
69 for(j=0;j<2;j++) d[j][i]=INF+1;
70 }
71
72 for(i=0;i<K;i++)
73 {
74 for(j=0;j<2;j++) d[j][P[i]]=0;
75 Q.push(Inque(P[i],0));
76 }
77
78 for(i=0;i<n;i++)
79 {
80 while(!Q.empty() && v[Q.top().x]) Q.pop();
81 if(Q.empty()) break;
82 Inque t=Q.top(); Q.pop();
83 if(t.x==0) break;
84 v[t.x]=true;
85 for(j=sta[t.x];j;j=nxt[j])
86 {
87 bool l=true;
88 if(d[0][chi[j]]>t.w+wei[j])
89 {
90 d[1][chi[j]]=d[0][chi[j]];
91 d[0][chi[j]]=t.w+wei[j];
92 }
93 else
94 if(d[1][chi[j]]>t.w+wei[j])
95 {
96 d[1][chi[j]]=t.w+wei[j];
97 }
98 else
99 {
100 l=false;
101 }
102
103 if(l)
104 {
105 if(d[1][chi[j]]!=INF+1)
106 {
107 Q.push(Inque(chi[j],d[1][chi[j]]));
108 }
109 }
110 }
111 }
112 return d[1][0];
113 }
114
115 // -------------- begin grader -----------------
116
117 void read_input()
118 {
119 int i;
120 my_assert(3==scanf("%d %d %d",&N,&M,&K));
121 for(i=0; i<M; i++)
122 my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
123 for(i=0; i<K; i++)
124 my_assert(1==scanf("%d",&P[i]));
125 my_assert(1==scanf("%d",&solution));
126 }
127
128 int main()
129 {
130 auto t1 = clock();
131
132 std::freopen("../tests/subtask3/grader.in.7", "r", stdin);
133 std::freopen("crocodile.out", "w", stdout);
CAPITOLUL 10. IOI 2011 1085

134
135 int ans;
136 read_input();
137
138 auto t2 = clock();
139
140 ans = travel_plan(N,M,R,L,K,P);
141
142 auto t3 = clock();
143
144 if(ans==solution)
145 printf("Correct.\n");
146 else
147 printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);
148
149 auto t4 = clock();
150
151 // reset console output
152 freopen("CON", "w", stdout);
153
154 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
155 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
156 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
157
158 return 0;
159 }
160
161 // -------------- begin grader -----------------
162 /*
163 t2-t1 = 0.92
164 t3-t2 = 0.407
165 t4-t3 = 0
166
167 Process returned 0 (0x0) execution time : 1.405 s
168 Press any key to continue.
169 */

Listing 10.4.3: crocodile-133704.cpp


1 // https://oj.uz/submission/133704 729 ms 61348 KB
2
3 #include "crocodile.h"
4
5 #include<ctime>
6 #include<iostream>
7
8 #define MAX_N 50000
9 #define MAX_M 10000000
10
11 static int N, M;
12 static int R[MAX_M][2];
13 static int L[MAX_M];
14 static int K;
15 static int P[MAX_N];
16 static int solution;
17
18 inline void my_assert(int e) {if (!e) abort();}
19
20 #include <bits/stdc++.h>
21
22 using namespace std;
23
24 typedef pair<int,int> Pa;
25 const int maxN=100100;
26 struct edge{int v,d,nxt;}a[20*maxN];
27 int vis[maxN],head[maxN];
28 int cnt=0;
29
30 priority_queue < Pa, vector<Pa>, greater<Pa> > Q;
31
32 void add(int u,int v,int d)
33 {
34 a[cnt].v=v;
35 a[cnt].d=d;
36 a[cnt].nxt=head[u];
CAPITOLUL 10. IOI 2011 1086

37 head[u]=cnt++;
38 }
39
40 int travel_plan(int N, int M, int R[][2], int L[], int K, int P[])
41 {
42 for (int i=0;i<N;i++){vis[i]=0;head[i]=-1;}
43 for (int i=0;i<M;i++)
44 {
45 add(R[i][0],R[i][1],L[i]);
46 add(R[i][1],R[i][0],L[i]);
47 }
48
49 for (int i=0;i<K;i++)
50 {
51 Q.push(make_pair(0,P[i]));
52 vis[P[i]]=1;
53 }
54
55 while(!Q.empty())
56 {
57 int dis=Q.top().first,u=Q.top().second;Q.pop();
58 if (++vis[u]!=2)continue;
59 if (u==0)return dis;
60 for (int i=head[u];i!=-1;i=a[i].nxt)
61 Q.push(make_pair(dis+a[i].d,a[i].v));
62 }
63
64 return -123; // !!!
65 }
66
67 // -------------- begin grader -----------------
68
69 void read_input()
70 {
71 int i;
72 my_assert(3==scanf("%d %d %d",&N,&M,&K));
73 for(i=0; i<M; i++)
74 my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
75 for(i=0; i<K; i++)
76 my_assert(1==scanf("%d",&P[i]));
77 my_assert(1==scanf("%d",&solution));
78 }
79
80 int main()
81 {
82 auto t1 = clock();
83
84 std::freopen("../tests/subtask3/grader.in.7", "r", stdin);
85 std::freopen("crocodile.out", "w", stdout);
86
87 int ans;
88 read_input();
89
90 auto t2 = clock();
91
92 ans = travel_plan(N,M,R,L,K,P);
93
94 auto t3 = clock();
95
96 if(ans==solution)
97 printf("Correct.\n");
98 else
99 printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);
100
101 auto t4 = clock();
102
103 // reset console output
104 freopen("CON", "w", stdout);
105
106 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
107 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
108 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
109
110 return 0;
111 }
112
CAPITOLUL 10. IOI 2011 1087

113 // -------------- begin grader -----------------


114 /*
115 t2-t1 = 3.449
116 t3-t2 = 4.718
117 t4-t3 = 0
118
119 Process returned 0 (0x0) execution time : 8.199 s
120 Press any key to continue.
121 */

Listing 10.4.4: crocodile-228833.cpp


1 // https://oj.uz/submission/228833 640 ms 54884 KB
2
3 #include "crocodile.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 50000
11 #define MAX_M 10000000
12
13 static int N, M;
14 static int R[MAX_M][2];
15 static int L[MAX_M];
16 static int K;
17 static int P[MAX_N];
18 static int solution;
19
20 inline void my_assert(int e) {if (!e) abort();}
21
22 #include<bits/stdc++.h>
23
24 using namespace std;
25
26 int travel_plan(int N, int M, int R[][2], int L[], int K, int P[])
27 {
28 vector<vector<pair<int, int>>> adj(N);
29 for(int i = 0; i < M; i++)
30 {
31 int u = R[i][0], v = R[i][1];
32 adj[u].emplace_back(v, L[i]);
33 adj[v].emplace_back(u, L[i]);
34 }
35
36 vector<int> vis(N);
37 priority_queue<pair<int, int>> Q;
38 for(int i = 0; i < K; i++)
39 {
40 vis[P[i]]++;
41 Q.emplace(0, P[i]);
42 }
43
44 while(!Q.empty())
45 {
46 auto [d, v] = Q.top();
47 Q.pop();
48 if(vis[v]++ == 1)
49 {
50 if(v == 0) return -d;
51 for(auto &[u, w] : adj[v])
52 Q.emplace(d - w, u);
53 }
54 }
55
56 return -123; // !!!
57 }
58
59 // -------------- begin grader -----------------
60
61 void read_input()
62 {
63 int i;
CAPITOLUL 10. IOI 2011 1088

64 my_assert(3==scanf("%d %d %d",&N,&M,&K));
65 for(i=0; i<M; i++)
66 my_assert(3==scanf("%d %d %d",&R[i][0],&R[i][1],&L[i]));
67 for(i=0; i<K; i++)
68 my_assert(1==scanf("%d",&P[i]));
69 my_assert(1==scanf("%d",&solution));
70 }
71
72 int main()
73 {
74 auto t1 = clock();
75
76 std::freopen("../tests/subtask3/grader.in.7", "r", stdin);
77 std::freopen("crocodile.out", "w", stdout);
78
79 int ans;
80 read_input();
81
82 auto t2 = clock();
83
84 ans = travel_plan(N,M,R,L,K,P);
85
86 auto t3 = clock();
87
88 if(ans==solution)
89 printf("Correct.\n");
90 else
91 printf("Incorrect. Returned %d, Expected %d.\n",ans,solution);
92
93 auto t4 = clock();
94
95 // reset console output
96 freopen("CON", "w", stdout);
97
98 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
99 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
100 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
101
102 return 0;
103 }
104
105 // -------------- begin grader -----------------
106 /*
107 t2-t1 = 0.906
108 t3-t2 = 5.007
109 t4-t3 = 0
110
111 Process returned 0 (0x0) execution time : 6.023 s
112 Press any key to continue.
113 */

Listing 10.4.5: checkerCrocodile.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/subtask3/grader.in.7",
14 (char*)"crocodile.out",
15 (char*)"../tests/subtask3/grader.expect.7",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
CAPITOLUL 10. IOI 2011 1089

23 registerChecker("crocodile", argc, argv);


24 compareRemainingLines();
25 }

10.4.3 *Rezolvare detaliat 

10.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 dierent 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 10. IOI 2011 1090

ˆ 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 10. IOI 2011 1091

ˆ 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 parti-
cular, 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 les should contain precisely the text  Correct.

10.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 sucient 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 nd 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 10. IOI 2011 1092

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 nd 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 nd the number of cameras
in time O k log N  as follows.
We start by placing the camera at the rst 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 nd the rst 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 rst 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 nd
the current bucket of e we can store a pointer from e, but it takes O k  to nd 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 rst 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 sucient to obtain full
marks for this problem. However, an inecient 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 10. IOI 2011 1093

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

10.5.2 Coduri surs 

Listing 10.5.1: elephants-115857.cpp


1 // https://oj.uz/submission/115857 3493 ms 13384 KB
2
3 #include "elephants.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 1000000
11 #define MAX_M 1000000
12
13 static int N,L,M;
14 static int X[MAX_N];
15 static int ii[MAX_M];
16 static int yy[MAX_M];
17 static int sol[MAX_M];
18
19 inline void my_assert(int e) {if (!e) abort();}
20
21 #include <bits/stdc++.h>
22
23 using namespace std;
24
25 int K, Q;
26
27 int Sq=400;
28 int B[400][1000], sz[400];
29 int D[400][1000], C[400][1000];
30 int A[150505], T[150505];
31
32 void calc(int t)
33 {
34 if (!sz[t]) return;
35 int k=sz[t]-1;
36 for (int i=sz[t]-1; i>=0; i--)
37 {
38 while (B[t][i] + K < B[t][k]) k--;
39 if (k == sz[t]-1) D[t][i] = 1, C[t][i] = B[t][i] + K;
40 else D[t][i] = D[t][k+1] + 1, C[t][i] = C[t][k+1];
41 }
42 }
43
44 void init(int n, int l, int X[])
45 {
46 N=n, K=l;
47 M = (N+Sq-1)/Sq;
48 for (int i=0; i<N; i++)
49 {
50 B[i/Sq][sz[i/Sq]++] = X[i];
51 if (!Q) A[i] = X[i];
52 }
CAPITOLUL 10. IOI 2011 1094

53 for (int i=0; i<M; i++) calc(i);


54 }
55
56 void del(int x)
57 {
58 int t, k;
59 for (t=0; t<M; t++)
60 {
61 if (!sz[t]) continue;
62 k = lower_bound(B[t], B[t]+sz[t], x) - B[t];
63 if (k < sz[t] && B[t][k] == x) break;
64 }
65 sz[t]--;
66 for (; k<sz[t]; k++)
67 B[t][k] = B[t][k+1];
68 calc(t);
69 }
70
71 void add(int x)
72 {
73 int t;
74 for (t=0; t<M-1; t++)
75 if (sz[t] && x <= B[t][sz[t]-1]) break;
76 B[t][sz[t]++] = x;
77 for (int i=sz[t]-1; i>0 && B[t][i-1] > B[t][i]; i--)
78 swap(B[t][i-1], B[t][i]);
79 calc(t);
80 }
81
82 int update(int x, int y)
83 {
84 del(A[x]);
85 A[x] = y;
86 add(y);
87
88 Q++;
89 if (Q % Sq == 0)
90 {
91 int cnt=0;
92 for (int i=0; i<M; i++)
93 {
94 for (int j=0; j<sz[i]; j++) T[cnt++] = B[i][j];
95 sz[i] = 0;
96 }
97 init(N, K, T);
98 }
99
100 int p=-1, ans=0;
101 for (int i=0; i<M; i++)
102 {
103 if (!sz[i]) continue;
104 int k = upper_bound(B[i], B[i]+sz[i], p) - B[i];
105 if (k == sz[i]) continue;
106 ans += D[i][k], p = C[i][k];
107 }
108 return ans;
109 }
110
111 // ------------- begin grader ----------------------
112
113 void read_input()
114 {
115 int i;
116 my_assert(3==scanf("%d %d %d",&N,&L,&M));
117
118 for(i=0; i<N; i++)
119 my_assert(1==scanf("%d",&X[i]));
120
121 for(i=0; i<M; i++)
122 my_assert(3==scanf("%d %d %d",&ii[i],&yy[i],&sol[i]));
123 }
124
125 int main()
126 {
127 auto t1 = clock();
128
CAPITOLUL 10. IOI 2011 1095

129 std::freopen("../tests/subtask5/grader.in.20", "r", stdin);


130 std::freopen("elephants.out", "w", stdout);
131
132 int i, ans;
133
134 read_input();
135
136 auto t2= clock();
137
138 init(N,L,X);
139
140 for(i=0; i<M; i++)
141 {
142 ans = update(ii[i],yy[i]);
143 if(ans==sol[i]) continue;
144
145 printf("Incorrect. In %d-th move, answered %d (%d expected).\n",
146 i+1, ans, sol[i]);
147 return 0;
148 }
149
150 auto t3 = clock();
151
152 printf("Correct.\n");
153
154 auto t4 = clock();
155
156 // reset console output
157 freopen("CON", "w", stdout);
158
159 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
160 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
161 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
162
163 return 0;
164 }
165
166 // ------------- end grader ----------------------
167 /*
168 t2-t1 = 0.25
169 t3-t2 = 0.11
170 t4-t3 = 0
171
172 Process returned 0 (0x0) execution time : 0.391 s
173 Press any key to continue.
174 */

Listing 10.5.2: elephants-192442.cpp


1 // https://oj.uz/submission/192442 2854 ms 12468 KB
2
3 #include "elephants.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 1000000
11 #define MAX_M 1000000
12
13 static int N,L,M;
14 static int X[MAX_N];
15 static int ii[MAX_M];
16 static int yy[MAX_M];
17 static int sol[MAX_M];
18
19 inline void my_assert(int e) {if (!e) abort();}
20
21 #include <bits/stdc++.h>
22
23 using namespace std;
24
25 #define b_size (400)
26
CAPITOLUL 10. IOI 2011 1096

27 int n, l, m;
28 int x[150010], v[150010];
29 int bnum;
30 int ucnt;
31
32 struct Bucket
33 {
34 int sz;
35 int x[2 * b_size + 1];
36 int num[2 * b_size + 1];
37 int bound[2 * b_size + 1];
38 void calc()
39 {
40 int t = sz;
41 for(int i = sz - 1; i >= 0; i--)
42 {
43 while(t > 0 && x[t - 1] > x[i] + l) t--;
44 if(t == sz)
45 num[i] = 1, bound[i] = x[i] + l;
46 else
47 num[i] = num[t] + 1, bound[i] = bound[t];
48 }
49 }
50
51 void ins(int y)
52 {
53 int p = (int)(upper_bound(x, x + sz, y) - x);
54 sz++;
55 for(int i = sz - 1; i > p; i--)
56 x[i] = x[i - 1];
57 x[p] = y;
58 calc();
59 }
60
61 void del(int y)
62 {
63 int p = (int)(lower_bound(x, x + sz, y) - x);
64 sz--;
65 for(int i = p; i < sz; i++)
66 x[i] = x[i + 1];
67 calc();
68 }
69
70 } b[b_size + 1];
71
72 void init(int n, int l, int x[])
73 {
74 ::n = n;
75 ::l = l;
76
77 for(int i = 0; i < n; i++)
78 ::x[i] = x[i];
79
80 for(int i = 0; i < n; i += b_size)
81 {
82 for(int j = i; j < n && j < i + b_size; j++)
83 {
84 b[bnum].x[b[bnum].sz++] = x[j];
85 }
86
87 b[bnum++].calc();
88 }
89 }
90
91 void rebucket()
92 {
93 int cnt = 0;
94 for(int i = 0; i < bnum; i++)
95 {
96 for(int j = 0; j < b[i].sz; j++)
97 {
98 v[cnt++] = b[i].x[j];
99 }
100 }
101
102 bnum = 0;
CAPITOLUL 10. IOI 2011 1097

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


104 {
105 b[bnum].sz = 0;
106 for(int j = i; j < n && j < i + b_size; j++)
107 {
108 b[bnum].x[b[bnum].sz++] = v[j];
109 }
110 b[bnum++].calc();
111 }
112 }
113
114 int update(int i, int y)
115 {
116 int pv = x[i];
117 x[i] = y;
118 for(int i = 0; i < bnum; i++)
119 {
120 if(b[i].sz == 0) continue;
121 if(b[i].x[0] <= pv && b[i].x[b[i].sz - 1] >= pv){b[i].del(pv); break;}
122 }
123
124 for(int i = 0; i < bnum; i++)
125 {
126 if(b[i].sz == 0) continue;
127 if((i == 0 || b[i].x[0] <= y) && (i == bnum - 1 || b[i + 1].x[0] > y))
128 {
129 b[i].ins(y);
130 break;
131 }
132 }
133
134 int ret = 0;
135 int lst = -1;
136 for(int i = 0; i < bnum; i++)
137 {
138 if(lst >= b[i].x[b[i].sz - 1]) continue;
139 if(lst < b[i].x[0])
140 {
141 ret += b[i].num[0];
142 lst = b[i].bound[0];
143 }
144 else
145 {
146 int p = (int)(upper_bound(b[i].x, b[i].x + b[i].sz, lst) - b[i].x);
147 ret += b[i].num[p]; lst = b[i].bound[p];
148 }
149 }
150
151 ucnt++;
152 if(ucnt % (b_size - 5) == 0) rebucket();
153 return ret;
154 }
155
156 // ------------- begin grader ----------------------
157
158 void read_input()
159 {
160 int i;
161 my_assert(3==scanf("%d %d %d",&N,&L,&M));
162
163 for(i=0; i<N; i++)
164 my_assert(1==scanf("%d",&X[i]));
165
166 for(i=0; i<M; i++)
167 my_assert(3==scanf("%d %d %d",&ii[i],&yy[i],&sol[i]));
168 }
169
170 int main()
171 {
172 auto t1 = clock();
173
174 std::freopen("../tests/subtask5/grader.in.20", "r", stdin);
175 std::freopen("elephants.out", "w", stdout);
176
177 int i, ans;
178
CAPITOLUL 10. IOI 2011 1098

179 read_input();
180
181 auto t2= clock();
182
183 init(N,L,X);
184
185 for(i=0; i<M; i++)
186 {
187 ans = update(ii[i],yy[i]);
188 if(ans==sol[i]) continue;
189
190 printf("Incorrect. In %d-th move, answered %d (%d expected).\n",
191 i+1, ans, sol[i]);
192 return 0;
193 }
194
195 auto t3 = clock();
196
197 printf("Correct.\n");
198
199 auto t4 = clock();
200
201 // reset console output
202 freopen("CON", "w", stdout);
203
204 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
205 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
206 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
207
208 return 0;
209 }
210
211 // ------------- end grader ----------------------
212 /*
213 t2-t1 = 0.25
214 t3-t2 = 22.304
215 t4-t3 = 0
216
217 Process returned 0 (0x0) execution time : 22.586 s
218 Press any key to continue.
219 */

Listing 10.5.3: elephants-207733.cpp


1 // https://oj.uz/submission/207733 3742 ms 7704 KB
2
3 #include "elephants.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #define MAX_N 1000000
11 #define MAX_M 1000000
12
13 static int N,L,M;
14 static int X[MAX_N];
15 static int ii[MAX_M];
16 static int yy[MAX_M];
17 static int sol[MAX_M];
18
19 inline void my_assert(int e) {if (!e) abort();}
20
21 #include <bits/stdc++.h>
22
23 #define lb(a, x) lower_bound(begin(a), end(a), x)
24
25 using namespace std;
26
27 constexpr int MX=150000, B=448, C=(MX+B-1)/B;
28
29 struct iii
30 {
31 int x, n, y;
CAPITOLUL 10. IOI 2011 1099

32 };
33
34 bool operator < (const iii& a, int b) { return a.x<b; }
35 bool operator < (int a, const iii& b) { return a<b.x; }
36
37 int A[MX], Q;
38
39 vector<iii> D[C];
40
41 void calc_bucket(vector<iii>& v)
42 {
43 for(int n=v.size(), i=n, p=n; i--;)
44 {
45 while(p && v[i].x<v[p-1].x-M) p--;
46
47 if(p==n)
48 v[i].n=1, v[i].y=v[i].x+M;
49 else
50 v[i].n=v[p].n+1, v[i].y=v[p].y;
51 }
52 }
53
54 void init_buckets()
55 {
56 int k=0;
57 for(auto&v:D)
58 {
59 for(auto[x,n,y]:v) X[k++]=x;
60 v.clear();
61 }
62
63 for(int i=0, j=0; i<N;)
64 {
65 D[j].push_back({X[i], 0, 0});
66 if(++i%B==0 || i==N) calc_bucket(D[j++]);
67 }
68 }
69
70 void del(int x)
71 {
72 for(auto&v:D) if(v.size() && v.front().x<=x && x<=v.back().x)
73 {
74 v.erase(lb(v, x));
75 calc_bucket(v);
76 break;
77 }
78 }
79
80 void add(int x)
81 {
82 int p=0, n=N-1;
83 for(auto&v:D)
84 if(!(n-=v.size()) || v.size() && p<=x && x<=(p=v.back().x))
85 {
86 v.insert(lb(v, x), {x, 0, 0});
87 calc_bucket(v);
88 break;
89 }
90 }
91
92 int answer()
93 {
94 int p=0, n=0;
95 for(auto&v:D) if(v.size() && p<=v.back().x)
96 {
97 auto[x,m,y]=*lb(v, p);
98 n+=m, p=y+1;
99 }
100
101 return n;
102 }
103
104 void init(int n, int m, int E[])
105 {
106 N=n, M=m;
107 for(int i=0; i<C; i++)
CAPITOLUL 10. IOI 2011 1100

108 D[i].reserve(B);
109 for(int i=0; i<N; i++)
110 D[i/B].push_back({A[i]=E[i], 0, 0});
111 }
112
113 int update(int n, int x)
114 {
115 if(Q++%B==0) init_buckets();
116 del(A[n]);
117 add(A[n]=x);
118 return answer();
119 }
120
121 // ------------- begin grader ----------------------
122
123 void read_input()
124 {
125 int i;
126 my_assert(3==scanf("%d %d %d",&N,&L,&M));
127
128 for(i=0; i<N; i++)
129 my_assert(1==scanf("%d",&X[i]));
130
131 for(i=0; i<M; i++)
132 my_assert(3==scanf("%d %d %d",&ii[i],&yy[i],&sol[i]));
133 }
134
135 int main()
136 {
137 auto t1 = clock();
138
139 std::freopen("../tests/subtask5/grader.in.20", "r", stdin);
140 std::freopen("elephants.out", "w", stdout);
141
142 int i, ans;
143
144 read_input();
145
146 auto t2= clock();
147
148 init(N,L,X);
149
150 for(i=0; i<M; i++)
151 {
152 ans = update(ii[i],yy[i]);
153 if(ans==sol[i]) continue;
154
155 printf("Incorrect. In %d-th move, answered %d (%d expected).\n",
156 i+1, ans, sol[i]);
157 return 0;
158 }
159
160 auto t3 = clock();
161
162 printf("Correct.\n");
163
164 auto t4 = clock();
165
166 // reset console output
167 freopen("CON", "w", stdout);
168
169 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
170 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
171 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
172
173 return 0;
174 }
175
176 // ------------- end grader ----------------------
177 /*
178 t2-t1 = 0.249
179 t3-t2 = 1.454
180 t4-t3 = 0
181
182 Process returned 0 (0x0) execution time : 1.734 s
183 Press any key to continue.
CAPITOLUL 10. IOI 2011 1101

184 */

Listing 10.5.4: checkerElephants.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/subtask5/grader.in.20",
14 (char*)"elephants.out",
15 (char*)"../tests/subtask5/grader.expect.20",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("elephants", argc, argv);
24 compareRemainingLines();
25 }

10.5.3 *Rezolvare detaliat 

10.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 ock 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 10. IOI 2011 1102

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

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 dierent 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 specied in the subtask.
ˆ The number of times your procedure encode calls the procedure send must not exceed the
limit K specied 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 sequ-
ence 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 10. IOI 2011 1103

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 10. IOI 2011 1104

ˆ 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 le as static to prevent them from interfering with
variables from other les.
ˆ 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 rst calls encode with the
given data, and then it calls decode with the output your procedure encode produced. In the
rst 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 shues the data by modifying its procedure shue (in C/C++) or
Shue (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 les should contain precisely the text  Correct.

10.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 nd 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 10. IOI 2011 1105

There are many ways to solve this task. It will be instructive to look at the rst 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 shued 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 rst 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 rst.
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 shued 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 sucient 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 10. IOI 2011 1106

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 dene 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 23 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 i 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 rst byte, for example), and therefore numbers from 0 to 255 are sucient.
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
 256  1.47  10
154
without loss of information. There are 256256
261 517
dierent encoded messages
using no more than 261 bytes. This is just more than 256  1.34  10 , the number of dierent
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 dicult.
CAPITOLUL 10. IOI 2011 1107

10.6.2 Coduri surs 

Listing 10.6.1: parrot-224033.cpp


1 //https://oj.uz/submission/224033 173 ms 27832 KB
2
3 #include "encoder.h"
4 #include "decoder.h"
5 #include "encoderlib.h"
6 #include "decoderlib.h"
7
8 #include <bits/stdc++.h>
9
10 using namespace std;
11
12 #define MAX_N 1000
13 #define MAX_M 10000
14
15 static int message[MAX_N];
16 static int N, NN;
17 static int encoded_message[MAX_M];
18 static int M;
19 static int output_message[MAX_N];
20 static int O;
21 static int max_expansion, channel_range;
22
23 // --------------- encode --------------------
24
25 const int MAXP = 330;
26 const int MAXL = 260;
27 const int MAXB = 520;
28 const int MAXV = 600;
29
30 class BigNum
31 {
32 public:
33
34 BigNum() : base( 1000000000 ) {}
35
36 int size() { return num.size(); }
37 int digit(int i) { return num[i]; }
38 void insert(int k) { num.push_back( k ); }
39 void change(int i, int k) { num[i] -= k; }
40
41 BigNum operator + (BigNum& aux)
42 {
43 BigNum ans;
44
45 BigNum A = aux;
46 BigNum B = *this;
47
48 if( A.size() < B.size() ) swap( A , B );
49
50 while( B.size() != A.size() )
51 B.insert( 0 );
52
53 int acc = 0;
54
55 for(int i = 0 ; i < B.size() ; i++)
56 {
57 ans.insert( A.digit(i) + B.digit(i) + acc );
58 acc = 0;
59
60 int curDigit = ans.digit(i);
61
62 if( curDigit >= base )
63 {
64 acc = curDigit/base;
65 ans.change( i , acc*base );
66 }
67 }
68
69 if( acc > 0 ) ans.insert( acc );
70
CAPITOLUL 10. IOI 2011 1108

71 return ans;
72 }
73
74 bool operator <= (BigNum aux)
75 {
76 if( this->size() != aux.size() ) return this->size() < aux.size();
77
78 for(int i = aux.size() - 1 ; i >= 0 ; i--)
79 if( this->digit(i) != aux.digit(i) ) return this->digit(i) < aux.digit(i
);
80
81 return true;
82 }
83
84 bool operator < (BigNum aux)
85 {
86 if( this->size() != aux.size() ) return this->size() < aux.size();
87
88 for(int i = aux.size() - 1 ; i >= 0 ; i--)
89 if( this->digit(i) != aux.digit(i) ) return this->digit(i) < aux.digit(i
);
90
91 return false;
92 }
93
94 private:
95
96 int base;
97
98 vector< int > num;
99 };
100
101 bool hasInit = false;
102
103 BigNum pot[MAXB];
104 BigNum dp[MAXV][MAXL];
105
106 void buildPowers()
107 {
108 pot[0].insert( 1 );
109
110 for(int i = 1 ; i < MAXB ; i++)
111 pot[i] = pot[i - 1] + pot[i - 1];
112 }
113
114 void buildBinomials()
115 {
116 for(int i = 0 ; i < MAXV ; i++)
117 dp[i][0].insert( 1 );
118
119 for(int i = 1 ; i < MAXV ; i++)
120 for(int j = 1 ; j <= i && j < MAXL ; j++)
121 dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
122 }
123
124 void encode(int N, int M[])
125 {
126 if( !hasInit )
127 {
128 buildPowers();
129 buildBinomials();
130
131 hasInit = true;
132 }
133
134 BigNum binary;
135
136 for(int i = 0 ; i < N ; i++)
137 for(int j = 0 ; j < 8 ; j++)
138 if( M[i] & (1 << j) ) binary = binary + pot[ 8*i + j ];
139
140 int remainParrots = 5*N;
141
142 BigNum sum;
143
144 for(int i = -1 ; i < 255 ; i++)
CAPITOLUL 10. IOI 2011 1109

145 {
146 int qtdParrots;
147 int qtdNumbers = 256 - i - 1;
148
149 for(qtdParrots = 0 ; qtdParrots <= remainParrots ; qtdParrots++)
150 {
151 BigNum aux = sum;
152 aux = aux+dp[remainParrots-qtdParrots+qtdNumbers-1][qtdNumbers-1];
153
154 if( binary < aux ) break;
155
156 sum = aux;
157 }
158
159 remainParrots -= qtdParrots;
160
161 if( i == -1 ) continue;
162
163 for(int j = 1 ; j <= qtdParrots ; j++)
164 send( i );
165 }
166
167 while( remainParrots > 0 )
168 {
169 send( 255 );
170 remainParrots--;
171 }
172 }
173
174 // --------------- decode --------------------
175
176 int freq[MAXL];
177
178 void decode(int N, int L, int X[])
179 {
180 if( !hasInit )
181 {
182 buildPowers();
183 buildBinomials();
184
185 hasInit = true;
186 }
187
188 BigNum sum;
189
190 int qtd = 5*N - L;
191
192 for(int i = 0 ; i < qtd ; i++)
193 sum = sum + dp[ 5*N - i + 256 ][ 256 ];
194
195 for(int i = 0 ; i < L ; i++)
196 freq[ X[i] ]++;
197
198 int remainParrots = L;
199
200 for(int i = 0 ; i < 255 ; i++)
201 {
202 int qtdNumbers = 256 - i - 1;
203
204 while( freq[i] > 0 )
205 {
206 sum = sum + dp[ remainParrots + qtdNumbers - 1 ][ qtdNumbers-1];
207
208 freq[i]--;
209 remainParrots--;
210 }
211 }
212
213 BigNum cur;
214
215 vector< int > binary;
216
217 for(int i = 8*N - 1 ; i >= 0 ; i--)
218 {
219 BigNum aux = cur + pot[i];
220
CAPITOLUL 10. IOI 2011 1110

221 if( aux <= sum )


222 {
223 cur = aux;
224 binary.push_back( 1 );
225 }
226 else binary.push_back( 0 );
227 }
228
229 reverse( binary.begin() , binary.end() );
230
231 for(int i = 0 ; i < N ; i++)
232 {
233 int v = 0;
234
235 for(int j = 0 ; j < 8 ; j++)
236 if( binary[8*i + j] == 1 ) v += (1 << j);
237
238 output( v );
239 }
240 }
241
242 // ----------------- begin grader --------------------
243
244 void send(int x)
245 {
246 if(M == MAX_M) {
247 printf("Encoded message too long\n");
248 exit(0);
249 }
250 encoded_message[M] = x;
251 M++;
252 }
253
254 int read_data()
255 {
256 if(NN == M) {
257 printf("Read too many encoded message\n");
258 exit(0);
259 }
260 NN++;
261 return encoded_message[NN-1];
262 }
263
264 void output(int y)
265 {
266 if(O == N)
267 O++;
268 if(O > N)
269 return;
270 output_message[O] = y;
271 O++;
272 }
273
274 static void sort_message(int d)
275 {
276 int i, j, b, bi, t;
277 for(i=0; i<M-1; i++)
278 {
279 bi = i;
280 b = encoded_message[i];
281 for(j=i+1; j<M; j++)
282 if(((d==0) && (encoded_message[j] < b)) ||
283 ((d==1) && (encoded_message[j] > b)))
284 {
285 b = encoded_message[j];
286 bi = j;
287 }
288 t = encoded_message[i];
289 encoded_message[i] = encoded_message[bi];
290 encoded_message[bi] = t;
291 }
292 }
293
294 static void random_shuffle()
295 {
296 int i, t, p;
CAPITOLUL 10. IOI 2011 1111

297 for(i=0; i<M-1; i++)


298 {
299 p = rand()%(M-i);
300 t = encoded_message[i];
301 encoded_message[i] = encoded_message[i+p];
302 encoded_message[i+p] = t;
303 }
304 }
305
306 static void shuffle(int method)
307 {
308 if(method==0)
309 sort_message(0);
310 else if(method==1)
311 sort_message(1);
312 else
313 random_shuffle();
314 }
315
316 static void check_encoded_message()
317 {
318 int i;
319 if(M > max_expansion * N)
320 {
321 printf("Encoded message too long.");
322 exit(0);
323 }
324 for(i=0; i < M; i++)
325 if((encoded_message[i] < 0) ||
326 (encoded_message[i] > channel_range))
327 {
328 printf("Bad encoded integer\n");
329 exit(0);
330 }
331 }
332
333 static int check_output()
334 {
335 int i;
336
337 if(O!=N)
338 return 0;
339 for(i = 0; i < N; i++)
340 if(message[i] != output_message[i])
341 return 0;
342 return 1;
343 }
344
345 int main()
346 {
347 auto t1 = clock();
348
349 std::freopen("../tests/subtask5/grader.in.7", "r", stdin);
350 //std::freopen("parrots.out", "w", stdout);
351
352 int i,tt,t,p,r;
353 int correct;
354
355 scanf("%d",&tt);
356 scanf("%d %d",&max_expansion,&channel_range);
357
358 scanf("%d",&r);
359 srand(r);
360
361 auto t2 = clock();
362
363 for(t=0; t<tt; t++)
364 {
365 scanf("%d",&N);
366 for(i = 0; i < N; i++)
367 scanf("%d",&message[i]);
368
369 M = 0;
370 encode(N,message);
371
372 check_encoded_message();
CAPITOLUL 10. IOI 2011 1112

373
374 scanf("%d",&p);
375 shuffle(p);
376
377 NN = 0;
378 O = 0;
379 decode(N,M,encoded_message);
380
381 if(!check_output())
382 {
383 printf("Incorrect\n");
384 exit(0);
385 }
386 }
387
388 auto t3 = clock();
389
390 printf("Correct.\n");
391
392 fprintf(stderr,"Ratio: %f\n",(float)M/N);
393
394 auto t4 = clock();
395
396 // reset console output
397 freopen("CON", "w", stdout);
398
399 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
400 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
401 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
402
403 return 0;
404 }
405
406 // ----------------- end grader --------------------
407 /*
408 Correct.
409 Ratio: 5.000000
410 t2-t1 = 0
411 t3-t2 = 1.324
412 t4-t3 = 0
413
414 Process returned 0 (0x0) execution time : 1.894 s
415 Press any key to continue.
416 */

Listing 10.6.2: parrots-235627.cpp


1 // https://oj.uz/submission/235627 ... 507 ms 66800 KB
2
3 #include "encoder.h"
4 #include "decoder.h"
5 #include "encoderlib.h"
6 #include "decoderlib.h"
7
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include<ctime>
12 #include<iostream>
13 #include<cstring>
14 #include<algorithm>
15
16 using namespace std;
17
18 #define base 5000000
19
20 #define MAX_N 1000
21 #define MAX_M 10000
22
23 static int message[MAX_N];
24 static int N, NN;
25 static int encoded_message[MAX_M];
26 static int M;
27 static int output_message[MAX_N];
28 static int O;
CAPITOLUL 10. IOI 2011 1113

29 static int max_expansion, channel_range;


30
31 // --------------- encode --------------------
32
33 static int d[350][260][100], pt[70][100], num[100];
34
35 static void adun(int a[], int b[], int c[])
36 {
37 int i, t = 0;
38 c[0] = max(a[0], b[0]);
39 for(i = 1; i <= c[0]; i++)
40 {
41 c[i] = t + a[i] + b[i];
42 t = c[i] / base;
43 c[i] %= base;
44 }
45 if(t != 0)
46 {
47 c[ ++c[0] ] = t;
48 }
49 }
50
51 static void mult(int a[], int x, int b[])
52 {
53 int i, t = 0;
54 b[0] = a[0];
55 for(i = 1; i <= a[0]; i++)
56 {
57 b[i] = a[i] * x + t;
58 t = b[i] / base;
59 b[i] %= base;
60 }
61 while(t != 0)
62 {
63 b[ ++b[0] ] = t % base;
64 t /= base;
65 }
66 }
67
68 static void scad(int a[], int b[])
69 {
70 int i, t = 0;
71 for(i = 1; i <= a[0]; i++)
72 {
73 if(a[i] < b[i] + t)
74 {
75 a[i] = a[i] + base - b[i] - t;
76 t = 1;
77 }
78 else
79 {
80 a[i] -= b[i] + t;
81 t = 0;
82 }
83 }
84
85 while(a[0] > 1 && a[ a[0] ] == 0)
86 {
87 a[0]--;
88 }
89 }
90
91 static int comp(int a[], int b[])
92 {
93 if(a[0] != b[0])
94 {
95 if(a[0] < b[0])
96 {
97 return 1;
98 }
99 return 0;
100 }
101
102 for(int i = a[0]; i >= 1; i--)
103 {
104 if(a[i] != b[i])
CAPITOLUL 10. IOI 2011 1114

105 {
106 if(a[i] < b[i])
107 {
108 return 1;
109 }
110 return 0;
111 }
112 }
113 return 0;
114 }
115
116 static void calcpt(int n)
117 {
118 pt[0][0] = 1; pt[0][1] = 1;
119 for(int i = 1; i <= n; i++)
120 {
121 mult(pt[i - 1], 256, pt[i]);
122 }
123 }
124
125 static void calcdp(int n)
126 {
127 int i, j;
128 memset(num, 0, sizeof(num) );
129 for(j = 0; j < 256; j++)
130 {
131 d[n - 1][j][0] = d[n - 1][j][1] = 1;
132 }
133
134 for(i = n - 2; i >= 0; i--)
135 {
136 for(j = 255; j >= 0; j--)
137 {
138 adun(d[i + 1][j], d[i][j + 1], d[i][j]);
139 }
140 }
141 }
142
143 void encode(int n, int v[])
144 {
145 int i, j, x, k = 5 * n - 1;
146 calcdp(k);
147 calcpt(n);
148 for(i = 0; i < n; i++)
149 {
150 for(j = 0; j < v[i]; j++)
151 {
152 adun(num, pt[n - i - 1], num);
153 }
154 }
155 adun(num, pt[0], num);
156 x = 0;
157 for(i = 0; i < k; i++)
158 {
159 while( comp(d[i][x], num) )
160 {
161 scad(num, d[i][x]);
162 x++;
163 }
164 send(x);
165 }
166 }
167
168 // --------------- decode --------------------
169
170 void decode(int n, int k, int v[])
171 {
172 int i, x, j;
173 memset(num, 0, sizeof(num) );
174 calcpt(n);
175 calcdp(k);
176 sort(v, v + k);
177 for(j = 0; j < v[i]; j++)
178 {
179 adun(num, d[0][j], num);
180 }
CAPITOLUL 10. IOI 2011 1115

181 for(i = 1; i < k; i++)


182 {
183 for(j = v[i - 1]; j < v[i]; j++)
184 {
185 adun(num, d[i][j], num);
186 }
187 }
188 adun(num, pt[0], num);
189 for(i = 0; i < n; i++)
190 {
191 for(j = 0; j < 256; j++)
192 {
193 if(comp(pt[n - i - 1], num) == 0)
194 {
195 output(j);
196 break;
197 }
198 else
199 {
200 scad(num, pt[n - i - 1]);
201 }
202 }
203 }
204 }
205
206 // ----------------- begin grader --------------------
207
208 void send(int x)
209 {
210 if(M == MAX_M)
211 {
212 printf("Encoded message too long\n");
213 exit(0);
214 }
215 encoded_message[M] = x;
216 M++;
217 }
218
219 int read_data()
220 {
221 if(NN == M)
222 {
223 printf("Read too many encoded message\n");
224 exit(0);
225 }
226 NN++;
227 return encoded_message[NN-1];
228 }
229
230 void output(int y)
231 {
232 if(O == N)
233 O++;
234 if(O > N)
235 return;
236 output_message[O] = y;
237 O++;
238 }
239
240 static void sort_message(int d)
241 {
242 int i, j, b, bi, t;
243 for(i=0; i<M-1; i++)
244 {
245 bi = i;
246 b = encoded_message[i];
247 for(j=i+1; j<M; j++)
248 if(((d==0) && (encoded_message[j] < b)) ||
249 ((d==1) && (encoded_message[j] > b)))
250 {
251 b = encoded_message[j];
252 bi = j;
253 }
254 t = encoded_message[i];
255 encoded_message[i] = encoded_message[bi];
256 encoded_message[bi] = t;
CAPITOLUL 10. IOI 2011 1116

257 }
258 }
259
260 static void random_shuffle()
261 {
262 int i, t, p;
263 for(i=0; i<M-1; i++)
264 {
265 p = rand()%(M-i);
266 t = encoded_message[i];
267 encoded_message[i] = encoded_message[i+p];
268 encoded_message[i+p] = t;
269 }
270 }
271
272 static void shuffle(int method)
273 {
274 if(method==0)
275 sort_message(0);
276 else if(method==1)
277 sort_message(1);
278 else
279 random_shuffle();
280 }
281
282 static void check_encoded_message()
283 {
284 int i;
285 if(M > max_expansion * N)
286 {
287 printf("Encoded message too long.");
288 exit(0);
289 }
290
291 for(i=0; i < M; i++)
292 if((encoded_message[i] < 0) ||
293 (encoded_message[i] > channel_range))
294 {
295 printf("Bad encoded integer\n");
296 exit(0);
297 }
298 }
299
300 static int check_output()
301 {
302 int i;
303
304 if(O!=N)
305 return 0;
306 for(i = 0; i < N; i++)
307 if(message[i] != output_message[i])
308 return 0;
309 return 1;
310 }
311
312 int main()
313 {
314 auto t1 = clock();
315
316 std::freopen("../tests/subtask5/grader.in.7", "r", stdin);
317 //std::freopen("parrots.out", "w", stdout);
318
319 int i,tt,t,p,r;
320 int correct;
321
322 scanf("%d",&tt);
323 scanf("%d %d",&max_expansion,&channel_range);
324
325 scanf("%d",&r);
326 srand(r);
327
328 auto t2 = clock();
329
330 for(t=0; t<tt; t++)
331 {
332 scanf("%d",&N);
CAPITOLUL 10. IOI 2011 1117

333 for(i = 0; i < N; i++)


334 scanf("%d",&message[i]);
335
336 M = 0;
337 encode(N,message);
338
339 check_encoded_message();
340
341 scanf("%d",&p);
342 shuffle(p);
343
344 NN = 0;
345 O = 0;
346 decode(N,M,encoded_message);
347
348 if(!check_output())
349 {
350 printf("Incorrect\n");
351 exit(0);
352 }
353 }
354
355 auto t3 = clock();
356
357 printf("Correct.\n");
358
359 fprintf(stderr,"Ratio: %f\n",(float)M/N);
360
361 auto t4 = clock();
362
363 // reset console output
364 freopen("CON", "w", stdout);
365
366 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
367 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
368 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
369
370 return 0;
371 }
372
373 // ----------------- end grader --------------------
374 /*
375 Correct.
376 Ratio: 4.984375
377 t2-t1 = 0
378 t3-t2 = 2.963
379 t4-t3 = 0
380
381 Process returned 0 (0x0) execution time : 3.034 s
382 Press any key to continue.
383 */

10.6.3 *Rezolvare detaliat 


Capitolul 11

IOI 201037

11.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 conrm or to refute each theory in turn.
When Jack conrms 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.
37
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).

1118
CAPITOLUL 11. IOI 2010 1119

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 dierent
combination of murderer, location and weapon as the answer. Each time Solve is called, it must
nd 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 nd 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 le
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 le.
ˆ 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 le.

11.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 xed, 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 mo-
notonic 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 conrming call to Theory was required, so a
total of 20 calls suces.
Here is a Pascal solution that can readily be generalized (the constant, type, and auxiliary
function denitions could be eliminated, but they document the relevant concepts nicely):

Listing 11.1.1: cluedosol.pas


CAPITOLUL 11. IOI 2010 1120

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 denitions, it could look like this:

Listing 11.1.2: cluedosol.cpp


1 void Solve()
2 {
3 int T[] = {0, 6, 10, 6}; // candidate theory, ignore T[0]
4 int i; // result of Theory
5 do
6 {
7 i = Theory(T[1], T[2], T[3]);
8 if (i != 0) --T[i];
9 } while (i != 0);
10 }

11.1.2 Coduri surs 

Listing 11.1.3: cluedo-218031.cpp


1 // https://oj.uz/submission/218031 15 ms 512 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #include "grader.h"
11 #include "cluedo.h"
12
13 static int M,L,W,gotit,cnt,maxcnt;
14
15 #include<bits/stdc++.h>
16
17 using namespace std;
18
19 void Solve()
20 {
CAPITOLUL 11. IOI 2010 1121

21 int m=1,l=1,w=1;
22 while(1)
23 {
24 int r = Theory(m,l,w);
25 if(!r) return ;
26 if(r==1) ++m;
27 if(r==2) ++l;
28 if(r==3) ++w;
29 }
30 }
31
32 // -------------- begin grader ----------------------
33
34 int Theory(int m, int l, int w)
35 {
36 ++cnt;
37 if (m < 1 || m > 6 || l < 1 || l > 10 || w < 1 || w > 6) exit(92);
38
39 if (rand()%2 && m != M) return 1;
40 else
41 if (rand()%2 && l != L) return 2;
42 else
43 if (rand()%2 && w != W) return 3;
44 else
45 if (m != M) return 1;
46 else
47 if (l != L) return 2;
48 else
49 if (w != W) return 3;
50
51 gotit = 1;
52 return 0;
53 }
54
55 int main()
56 {
57 std::freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
58 //std::freopen("cluedo.out", "w", stdout);
59
60 while (3 == scanf("%d%d%d",&M,&L,&W))
61 {
62 cnt = gotit = 0;
63
64 Solve();
65
66 if (cnt > maxcnt) maxcnt = cnt;
67 if (!gotit)
68 {
69 printf("NO\n");
70 return 91;
71 }
72 }
73
74 printf("OK %d\n",maxcnt);
75
76 return 0;
77 }
78
79 // -------------- end grader ----------------------

Listing 11.1.4: cluedo-220599.cpp


1 // https://oj.uz/submission/220599 16 ms 384 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #include "grader.h"
11 #include "cluedo.h"
12
13 static int M,L,W,gotit,cnt,maxcnt;
CAPITOLUL 11. IOI 2010 1122

14
15 void Solve()
16 {
17 int i=1, j=1, k=1, r;
18 while(1)
19 {
20 r = Theory(i, j, k);
21 if(r==0) return;
22 else if(r==1) i++;
23 else if(r==2) j++;
24 else if(r==3) k++;
25 }
26 }
27
28 // -------------- begin grader ----------------------
29
30 int Theory(int m, int l, int w)
31 {
32 ++cnt;
33 if (m < 1 || m > 6 || l < 1 || l > 10 || w < 1 || w > 6) exit(92);
34
35 if (rand()%2 && m != M) return 1;
36 else
37 if (rand()%2 && l != L) return 2;
38 else
39 if (rand()%2 && w != W) return 3;
40 else
41 if (m != M) return 1;
42 else
43 if (l != L) return 2;
44 else
45 if (w != W) return 3;
46
47 gotit = 1;
48 return 0;
49 }
50
51 int main()
52 {
53 std::freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
54 //std::freopen("cluedo.out", "w", stdout);
55
56 while (3 == scanf("%d%d%d",&M,&L,&W))
57 {
58 cnt = gotit = 0;
59
60 Solve();
61
62 if (cnt > maxcnt) maxcnt = cnt;
63 if (!gotit)
64 {
65 printf("NO\n");
66 return 91;
67 }
68 }
69
70 printf("OK %d\n",maxcnt);
71
72 return 0;
73 }
74 // -------------- end grader ----------------------

Listing 11.1.5: cluedo-229322.cpp


1 // https://oj.uz/submission/229322 17 ms 384 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include<ctime>
8 #include<iostream>
9
10 #include "grader.h"
11 #include "cluedo.h"
CAPITOLUL 11. IOI 2010 1123

12
13 static int M,L,W,gotit,cnt,maxcnt;
14
15 #include <bits/stdc++.h>
16
17 void Solve(void)
18 {
19 int i[] = {1, 1, 1};
20 while(true)
21 {
22 int r = Theory(i[0], i[1], i[2]);
23 if(r == 0) return;
24 i[r - 1]++;
25 }
26 }
27
28 // -------------- begin grader ----------------------
29
30 int Theory(int m, int l, int w)
31 {
32 ++cnt;
33 if (m < 1 || m > 6 || l < 1 || l > 10 || w < 1 || w > 6) exit(92);
34
35 if (rand()%2 && m != M) return 1;
36 else
37 if (rand()%2 && l != L) return 2;
38 else
39 if (rand()%2 && w != W) return 3;
40 else
41 if (m != M) return 1;
42 else
43 if (l != L) return 2;
44 else
45 if (w != W) return 3;
46
47 gotit = 1;
48 return 0;
49 }
50
51 int main()
52 {
53 std::freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
54 //std::freopen("cluedo.out", "w", stdout);
55
56 while (3 == scanf("%d%d%d",&M,&L,&W))
57 {
58 cnt = gotit = 0;
59
60 Solve();
61
62 if (cnt > maxcnt) maxcnt = cnt;
63 if (!gotit)
64 {
65 printf("NO\n");
66 return 91;
67 }
68 }
69
70 printf("OK %d\n",maxcnt);
71
72 return 0;
73 }
74
75 // -------------- end grader ----------------------

11.1.3 *Rezolvare detaliat 

11.2 Hotter Colder


Problema 2 - Hotter Colder 100 de puncte
CAPITOLUL 11. IOI 2010 1124

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 rst 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 (rst 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 le contains several lines, each containing N and Jill's number.
ˆ Expected output for sample grader input: the grader will create les grader.out.1
grader.out.2 etc.
CAPITOLUL 11. IOI 2010 1125

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

11.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 eciently 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 dierently, 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 eectively provides a high-low comparison to the midpoint
M . In fact, sign G  P  ˜ Guess G sign J  M  oers 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 11. IOI 2010 1126

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 rst 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 suciently. In K more
K 1
guesses, you can nd 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 11. IOI 2010 1127

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 rst 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 11.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 11.2.2: coldersol2.pas


1 const
2 Colder = -1;
3 Same = 0;
4 Hotter = +1;
5
6 type
7 TResult = Colder .. Hotter;
CAPITOLUL 11. IOI 2010 1128

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;

11.2.2 Coduri surs 

Listing 11.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 11. IOI 2010 1129

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 11. IOI 2010 1130

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 11. IOI 2010 1131

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 11.2.4: hottercolder-173047.cpp


1 // https://oj.uz/submission/173047 895 ms 8172 KB
2
3 #include "grader.h"
4 #include "hottercolder.h"
5
6 #include <stdio.h>
7 #include <math.h>
8 #include <stdlib.h>
9
10 int cal(int n)
11 {
12 return n < 5 ? 1 : (n + 1 >> 1) - cal(n >> 1);
13 }
14
CAPITOLUL 11. IOI 2010 1132

15 int HC(int N)
16 {
17 int L = 1, R = N, prv, nxt, t, W = log(3 * N) / log(2);
18 while (L < R)
19 {
20 if (R == 2)
21 {
22 Guess(1);
23 return Guess(2) < 0 ? 1 : 2;
24 }
25 int cut = W & 1 ? (2 << W - 2) / 3 + 1 : (2 << W - 2) / 3 + 2;
26 nxt = R == N ? cut + cal(R - cut) : cut + cut - 1;
27 prv = cut + cut - nxt;
28 Guess(prv);
29 t = Guess(nxt);
30 if (t == -1) R = prv + nxt - 1 >> 1;
31 if (t == 0) return prv + nxt >> 1;
32 if (t == 1)
33 {
34 L = prv + nxt + 2 >> 1;
35 prv = nxt;
36 while (L < R)
37 {
38 nxt = (L + R >> 1 << 1) - prv;
39 if (nxt == prv) nxt++;
40 if (nxt < 1) nxt = 1;
41 if (nxt > N) nxt = N;
42 t = Guess(nxt);
43 if (t == -1)
44 {
45 if (prv < nxt) R = nxt + prv - 1 >> 1;
46 else L = nxt + prv + 2 >> 1;
47 }
48 if (t == 0) return nxt + prv >> 1;
49 if (t == 1)
50 {
51 if (prv < nxt) L = nxt + prv + 2 >> 1;
52 else R = nxt + prv - 1 >> 1;
53 }
54 prv = nxt;
55 }
56 }
57 W -= 2;
58 }
59 return L;
60 }
61
62 // ----------------------- begin grader --------------------
63
64 static int moves, TT, NN, prev = -1;
65 int Guess(int x){
66 int r;
67 if (prev == -1 || abs(x-TT) == abs(prev-TT)) r = 0;
68 else if (abs(x-TT) > abs(prev-TT)) r = -1;
69 else r = 1;
70 prev = x;
71 if (x < 1 || x > NN) exit(92);
72 moves++;
73 return r;
74 }
75
76 int main()
77 {
78 //freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
79 freopen("../tests/Subtask4-data/grader.in.2", "r", stdin);
80
81 //freopen("hottercolder.out", "w", stdout);
82
83 int n=0,i,t,OK=0,sub1=0,sub2=0,sub3=0;
84 double worst = 999999;
85 while (2 == scanf("%d%d",&NN,&TT))
86 {
87 if (NN > n) n = NN;
88 prev = -1;
89 moves = 0;
90 int h = HC(NN);
CAPITOLUL 11. IOI 2010 1133

91 if (h != TT)
92 {
93 exit(91);
94 }
95 int W = floor(0.00001+log(3*NN)/log(2));
96 double alpha = 2 - (double)moves/W;
97 if (alpha < worst) worst = alpha;
98 // 1 means failure
99 if ( NN <= 500 && moves > 500 ) exit(93);
100 if ( NN <= 500 && moves > 18 ) sub2=1;
101 if ( NN <= 500 && moves > 16 ) sub3=1;
102 OK++;
103 }
104 if (!sub1) printf("OK 1\n");
105 if (!sub2) printf("OK 2\n");
106 if (!sub3) printf("OK 3\n");
107 if (worst > 0) printf("OK 4 alpha %0.2lf\n",worst);
108 return 0;
109 }
110 // ----------------------- end grader --------------------
111 /*
112 OK 1
113 OK 2
114 OK 3
115 OK 4 alpha 1.00
116
117 Process returned 0 (0x0) execution time : 2.979 s
118 Press any key to continue.
119 */

Listing 11.2.5: hottercolder-201977.cpp


1 // https://oj.uz/submission/201977 1672 ms 8192 KB
2
3 #include "grader.h"
4 #include "hottercolder.h"
5
6 #include <stdio.h>
7 #include <math.h>
8 #include <stdlib.h>
9
10 #include <bits/stdc++.h>
11
12 using namespace std;
13
14 typedef long long ll;
15 typedef long double ld;
16 typedef pair<int,int> pi;
17 typedef vector<int> vi;
18 typedef vector<pi> vpi;
19
20 #define f first
21 #define s second
22 #define sz(x) (int)x.size()
23 #define all(x) begin(x), end(x)
24 #define rsz resize
25 #define bk back()
26 #define pb push_back
27
28 #define FOR(i,a,b) for (int i = (a); i < (b); ++i)
29 #define F0R(i,a) FOR(i,0,a)
30 #define ROF(i,a,b) for (int i = (b)-1; i >= (a); --i)
31 #define R0F(i,a) ROF(i,0,a)
32 #define trav(a,x) for (auto& a: x)
33
34 const int MOD = 1e9+7;
35 const ld PI = acos((ld)-1);
36
37 template<class T> bool ckmin(T& a, const T& b)
38 {
39 return b < a ? a = b, 1 : 0;
40 }
41 template<class T> bool ckmax(T& a, const T& b)
42 {
43 return a < b ? a = b, 1 : 0;
CAPITOLUL 11. IOI 2010 1134

44 }
45
46 vi soFar, guess;
47 pi posi;
48 int N;
49
50 bool tri(int x)
51 {
52 guess.pb(Guess(x));
53 if (guess.bk == 0)
54 {
55 if (sz(soFar) && soFar.bk != x)
56 posi.f = posi.s = (soFar.bk+x)/2;
57 }
58 else
59 {
60 if (guess.bk == -1)
61 {
62 if (x < soFar.bk)
63 {
64 ckmax(posi.f,(soFar.bk+x)/2+1);
65 }
66 else
67 {
68 ckmin(posi.s,(soFar.bk+x-1)/2);
69 }
70 }
71 else
72 {
73 if (x < soFar.bk)
74 {
75 ckmin(posi.s,(soFar.bk+x-1)/2);
76 }
77 else
78 {
79 ckmax(posi.f,(soFar.bk+x)/2+1);
80 }
81 }
82 }
83 soFar.pb(x); assert(posi.f <= posi.s);
84 return posi.f == posi.s;
85 }
86
87 int binSearch()
88 {
89 while (1)
90 {
91 int des = posi.f+posi.s-soFar.bk;
92 ckmax(des,1); ckmin(des,N);
93 if (tri(des)) return posi.f;
94 }
95 }
96
97 int HC(int _N)
98 {
99 N = _N;
100 guess.clear(), soFar.clear(); posi = {1,N};
101 if (N == 1) return 1;
102 int m = N/2+1;
103 int a = m-1, b = m;
104 tri(a); if (tri(b)) return posi.f;
105 if (posi.f == 1)
106 {
107 while (1)
108 {
109 if (soFar.bk <= 3)
110 {
111 assert(tri(1));
112 return posi.f;
113 }
114 if (soFar.bk <= 7)
115 {
116 tri(1);
117 return binSearch();
118 }
119 vi one = {3}, two = {7};
CAPITOLUL 11. IOI 2010 1135

120 for (int i = 3;;++i)


121 {
122 if (i&1)
123 {
124 one.pb(one.bk+(1<<i));
125 if (one.bk >= soFar.bk)
126 {
127 int p = one[sz(one)-2];
128 if (tri(p+2)) return posi.f;
129 if (posi.f != 1) return binSearch();
130 if (tri(p)) return posi.f;
131 if (posi.f != 1) return binSearch();
132 break;
133 }
134 }
135 else
136 {
137 two.pb(two.bk+(1<<i));
138 if (two.bk >= soFar.bk)
139 {
140 int p = two[sz(two)-2];
141 if (tri(p+2)) return posi.f;
142 if (posi.f != 1) return binSearch();
143 if (tri(p)) return posi.f;
144 if (posi.f != 1) return binSearch();
145 break;
146 }
147 }
148 }
149 }
150 }
151 else
152 {
153 while (1)
154 {
155 if (soFar.bk >= N+1-3)
156 {
157 assert(tri(N));
158 return posi.f;
159 }
160 if (soFar.bk >= N+1-7)
161 {
162 tri(N);
163 return binSearch();
164 }
165 vi one = {3}, two = {7};
166 for (int i = 3;;++i)
167 {
168 if (i&1)
169 {
170 one.pb(one.bk+(1<<i));
171 if (N+1-one.bk <= soFar.bk)
172 {
173 int p = one[sz(one)-2];
174 if (tri((N+1)-(p+2))) return posi.f;
175 if (posi.s != N) return binSearch();
176 if (tri(N+1-p)) return posi.f;
177 if (posi.s != N) return binSearch();
178 break;
179 }
180 }
181 else
182 {
183 two.pb(two.bk+(1<<i));
184 if (N+1-two.bk <= soFar.bk)
185 {
186 int p = two[sz(two)-2];
187 if (tri((N+1)-(p+2))) return posi.f;
188 if (posi.s != N) return binSearch();
189 if (tri((N+1)-p)) return posi.f;
190 if (posi.s != N) return binSearch();
191 break;
192 }
193 }
194 }
195 }
CAPITOLUL 11. IOI 2010 1136

196 }
197 }
198
199 // ----------------------- begin grader --------------------
200
201 static int moves, TT, NN, prevv = -1;
202
203 int Guess(int x)
204 {
205 int r;
206 if (prevv == -1 || abs(x-TT) == abs(prevv-TT)) r = 0;
207 else
208 if (abs(x-TT) > abs(prevv-TT)) r = -1;
209 else r = 1;
210
211 prevv = x;
212 if (x < 1 || x > NN) exit(92);
213 moves++;
214 return r;
215 }
216
217 int main()
218 {
219 //freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
220 freopen("../tests/Subtask4-data/grader.in.2", "r", stdin);
221
222 //freopen("hottercolder.out", "w", stdout);
223
224 int n=0,i,t,OK=0,sub1=0,sub2=0,sub3=0;
225 double worst = 999999;
226 while (2 == scanf("%d%d",&NN,&TT))
227 {
228 if (NN > n) n = NN;
229 prevv = -1;
230 moves = 0;
231 int h = HC(NN);
232 if (h != TT)
233 {
234 exit(91);
235 }
236 int W = floor(0.00001+log(3*NN)/log(2));
237 double alpha = 2 - (double)moves/W;
238 if (alpha < worst) worst = alpha;
239 // 1 means failure
240 if ( NN <= 500 && moves > 500 ) exit(93);
241 if ( NN <= 500 && moves > 18 ) sub2=1;
242 if ( NN <= 500 && moves > 16 ) sub3=1;
243 OK++;
244 }
245 if (!sub1) printf("OK 1\n");
246 if (!sub2) printf("OK 2\n");
247 if (!sub3) printf("OK 3\n");
248 if (worst > 0) printf("OK 4 alpha %0.2lf\n",worst);
249 return 0;
250 }
251 // ----------------------- end grader --------------------
252 /*
253 OK 1
254 OK 2
255 OK 3
256 OK 4 alpha 1.00
257
258 Process returned 0 (0x0) execution time : 22.549 s
259 Press any key to continue.
260 */
CAPITOLUL 11. IOI 2010 1137

11.2.3 *Rezolvare detaliat 

11.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 dened 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 11. IOI 2010 1138

ˆ Sample grader input: grader.in.1 grader.in.2 etc.


Note: The rst 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 le.
ˆ 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 le.

11.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 suce 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 lling/emptying the array to be sorted. But these improvements
do not aect 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 oset of the nal rectangle is known [O N  possibilities]. Then scan across the row,
using some ecient 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 veried 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 nd the minimum value for which
the answer is "yes".
CAPITOLUL 11. IOI 2010 1139

N.B. An O N log N  algorithm suces for 100% score.


Linear Solution
Mihai P tra³cu 38 (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 nd 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 nd 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 lter 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
O N  Solution for Subtask 2 in Pascal using
2
bucket sort

Listing 11.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
38
(17 July 1982 - 5 June 2012) https://en.wikipedia.org/wiki/Mihai_Patrascu
CAPITOLUL 11. IOI 2010 1140

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 }

11.3.2 Coduri surs 

Listing 11.3.2: quality-208958.cpp


1 // https://oj.uz/submission/208958 2952 ms 176984 KB
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "quality.h"
6
7 #include<ctime>
8
9 #include <unordered_set>
10 #include <unordered_map>
11 #include <algorithm>
12 #include <iostream>
13 #include <fstream>
14 #include <string>
15 #include <cstring>
16 #include <cstdint>
17 #include <cstdlib>
18 #include <cstdio>
19 #include <cctype>
20 #include <cmath>
21 #include <vector>
22 #include <bitset>
23 #include <queue>
24 #include <deque>
25 #include <stack>
26 #include <cmath>
27 #include <tuple>
28 #include <list>
29 #include <set>
30 #include <map>
31 #define add push_back
32 #define m_p make_pair
33 #define fr first
34 #define sc second
35 #define endl ’\n’
36
37 using namespace std;
38
39 typedef long long ll;
40 typedef unsigned long long llu;
41 typedef long double ld;
42 typedef pair<ll, ll> pii;
CAPITOLUL 11. IOI 2010 1141

43 const ll N = 1000005, mod = 998244353, M = 3001;


44 ll n, m, k, i, j, ans, kaskad[N], matrix[M][M], pref[M][M], h, w;
45
46 bool check(ll value)
47 {
48 for (int i = 1; i <= n; ++i)
49 {
50 for (int j = 1; j <= m; ++j)
51 {
52 ll x = -1;
53 if (matrix[i][j] > value)
54 x = 1;
55 else if (matrix[i][j] == value)
56 x = 0;
57 pref[i][j] = pref[i - 1][j] + pref[i][j-1]-pref[i-1][j-1]+x;
58 }
59 }
60 for (int i = h; i <= n; ++i)
61 {
62 for (int j = w; j <= m; ++j)
63 {
64 if (pref[i][j]-pref[i-h][j]-pref[i][j-w]+pref[i-h][j-w] <= 0)
65 return true;
66 }
67 }
68 return false;
69 }
70
71 void BinarySearch(ll left, ll right)
72 {
73 if (right - left <= 1)
74 return;
75 ll mid = (left + right) / 2;
76 if (!check(mid))
77 {
78 BinarySearch(mid, right);
79 }
80 else
81 {
82 ans = mid;
83 BinarySearch(left, mid);
84 }
85 }
86
87 int rectangle(int nn, int mm, int hh, int ww, int a[M][M])
88 {
89 n = nn;
90 m = mm;
91 h = hh;
92 w = ww;
93 for (int i = 0; i < n; ++i)
94 {
95 for (int j = 0; j < m; ++j)
96 {
97 matrix[i + 1][j + 1] = a[i][j];
98 }
99 }
100 BinarySearch(0, n * m);
101 return ans;
102 }
103
104 // ------------ begin grader ---------------
105
106 //static int R,C,H,W,Q[3001][3001],i,j,ans;
107 static int R,C,H,W,Q[3001][3001];
108
109 int main()
110 {
111 auto t1 = clock();
112
113 freopen("../tests/Subtask5-data/grader.in.3-5", "r", stdin);
114 freopen("quality.out", "w", stdout);
115
116 scanf("%d%d%d%d",&R,&C,&H,&W);
117 for (i=0;i<R;i++)
118 for (j=0;j<C;j++) scanf("%d",&Q[i][j]);
CAPITOLUL 11. IOI 2010 1142

119
120 auto t2 = clock();
121
122 ans = rectangle(R,C,H,W,Q);
123
124 auto t3 = clock();
125
126 printf("%d\n",ans);
127
128
129 auto t4 = clock();
130
131 // reset console output
132 freopen("CON", "w", stdout);
133
134 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
135 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
136 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
137
138 return 0;
139 }
140
141 // ------------ end grader ---------------
142 /*
143 t2-t1 = 3.828
144 t3-t2 = 7.109
145 t4-t3 = 0
146
147 Process returned 0 (0x0) execution time : 11.109 s
148 Press any key to continue.
149 */

Listing 11.3.3: quality-234933.cpp


1 // https://oj.uz/submission/234933 2332 ms 216116 KB
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 #include<bits/stdc++.h>
6 #include "quality.h"
7
8 #define ll long long int
9 #define pb push_back
10 #define mp make_pair
11 #define FOR(i,n) for(i=0;i<(n);++i)
12 #define FORe(i,n) for(i=1;i<=(n);++i)
13 #define FORr(i,a,b) for(i=(a);i<(b);++i)
14 #define FORrev(i,n) for(i=(n);i>=0;--i)
15 #define F0R(i,n) for(int i=0;i<(n);++i)
16 #define F0Re(i,n) for(int i=1;i<=(n);++i)
17 #define F0Rr(i,a,b) for(ll i=(a);i<(b);++i)
18 #define F0Rrev(i,n) for(int i=(n);i>=0;--i)
19 #define ii pair<ll,ll>
20 #define vi vector<ll>
21 #define vii vector<ii>
22 #define ff first
23 #define ss second
24 #define cd complex<double>
25 #define vcd vector<cd>
26 #define ldd long double
27 #define dbgLine cout<<"Line : "<<__LINE__<<’\n’
28 #define all(x) (x).begin(),(x).end()
29
30 using namespace std;
31
32 const short int __PRECISION = 10;
33
34 int rect[3000][3000], pre[3000][3000];
35 pair<int, int> Lookup[9000000];
36
37 #define in(t) (Lookup[t].ff > i-H && \
38 Lookup[t].ff <= i && Lookup[t].ss > j-W && \
39 Lookup[t].ss <= j)
40
41 inline bool bs(int R, int C, int H, int W, int target)
CAPITOLUL 11. IOI 2010 1143

42 {
43 if(target < (H*W)/2)
44 return false;
45 F0R(i, R)
46 F0R(j ,C)
47 {
48 pre[i][j]=(rect[i][j]==target ? 0:(rect[i][j]<target ? -1:1))+
49 (i==0 ? 0:pre[i-1][j]) + (j==0 ? 0:pre[i][j-1]) -
50 ((i==0||j==0) ? 0 : pre[i-1][j-1]);
51
52 if(i >= H-1 && j >= W-1)
53 {
54 int tmp = pre[i][j] - (i==H-1 ? 0 : pre[i-H][j]) -
55 (j==W-1 ? 0 : pre[i][j-W]) +
56 ((i==H-1||j==W-1) ? 0 : pre[i-H][j-W]);
57
58 if((tmp < 0) || (tmp == 0 && in(target)))
59 {
60 return true;
61 }
62 }
63 }
64 return false;
65 }
66
67 int rectangle(int R, int C, int H, int W, int( * Q)[3001])
68 {
69 F0R(i, R)
70 F0R(j, C)
71 rect[i][j] = Q[i][j]-1;
72 F0R(i, R)
73 F0R(j, C)
74 Lookup[rect[i][j]] = mp(i, j);
75 int lo = 0, hi = R*C - 1, mid;
76 while(hi > lo + 1)
77 {
78 mid = (hi + lo)/2;
79 if(bs(R, C, H, W, mid))
80 hi = mid;
81 else
82 lo = mid;
83 }
84 return hi+1;
85 }
86
87 // ------------ begin grader ---------------
88
89 static int R,C,H,W,Q[3001][3001],i,j,ans;
90
91 int main()
92 {
93 auto t1 = clock();
94
95 freopen("../tests/Subtask5-data/grader.in.3-5", "r", stdin);
96 freopen("quality.out", "w", stdout);
97
98 scanf("%d%d%d%d",&R,&C,&H,&W);
99 for (i=0;i<R;i++)
100 for (j=0;j<C;j++) scanf("%d",&Q[i][j]);
101
102 auto t2 = clock();
103
104 ans = rectangle(R,C,H,W,Q);
105
106 auto t3 = clock();
107
108 printf("%d\n",ans);
109
110
111 auto t4 = clock();
112
113 // reset console output
114 freopen("CON", "w", stdout);
115
116 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
117 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
CAPITOLUL 11. IOI 2010 1144

118 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
119
120 return 0;
121 }
122
123 // ------------ end grader ---------------
124 /*
125 t2-t1 = 3.921
126 t3-t2 = 8.5
127 t4-t3 = 0
128
129 Process returned 0 (0x0) execution time : 12.578 s
130 Press any key to continue.
131 */

Listing 11.3.4: checkerQuality.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/Subtask5-data/grader.in.3-5",
14 (char*)"../tests/Subtask5-data/grader.expect.3-5",
15 (char*)"quality.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("quality", argc, argv);
24 compareRemainingLines();
25 }

11.3.3 *Rezolvare detaliat 

11.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 11. IOI 2010 1145

The grading server calls excerpt(E) 10 000 times, once for each excerpt in its input le. 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 dened
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-specic
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 nhạc và Thời
trang) là chương tr
55. ISO 3166-2:GU ni akoole ninu ISO 3166-2 , apa opagun ISO 3166 ti International Organi-
zation for Stan
56. 下卡姆斯克 ( 俄文 : Нижнека́мск ; 韃靼語 :
Түбəн Кама/Tübän Kama )是 俄羅斯 韃靼斯坦共和國 æ-
±åŒ—部的一個城市,位於 卡馬河 南岸。 2002å¹´ 人口22
The sample input le 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 rst paragraph
of an article chosen at random from the corresponding Wikipedia edition. Each line of the le
contains:
ˆ The two-letter ISO code for the Wikipedia language edition;
ˆ 100 numbers between 1 and 65 535, representing the rst 100 symbols, in sequence, of the
rst 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 ocial grader uses 10 000 dierent excerpts, selected in the same way from the same 56
Wikipedia editions. However, the grader assigns a dierent number between 0 and 55 to each
language, and a dierent 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 specied
for each of the 10 000 examples, the sample grader will output OKalpha where alpha is the
accuracy.
CAPITOLUL 11. IOI 2010 1146

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

11.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 eld of
information retrieval under the attention. This problem is discussed in detail in the book Infor-
mation Retrieval: Implementing and Evaluating Search Engines by S. Büttcher, 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 oered, the
variability between excerpts from the same language play a negligible role. It has been conrmed
that the statistical resemblance between the provided test input and the ocial 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 specic (personal) language knowledge into a solution.
There are many approaches possible. Rocchio's method, which was informally described in the
task description, suces 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.

11.4.2 Coduri surs 

Listing 11.4.1: lang-8124.cpp


1 // https://oj.uz/submission/8124 4576 ms 112584 KB
2
3 #include <stdio.h>
4 #include <time.h>
5 #include <stdlib.h>
6
7 #include<iostream>
8
9 #include <cmath>
10 #include <string.h>
11
12 #include "grader.h"
13 #include "lang.h"
14
15 using namespace std;
16
17 unsigned short pre3[56][1048576];
18 unsigned short num3[1048576];
19 int pre1[65536][56];
20 int num1[65536];
21 double now[56];
22 const unsigned int mod=1048575;
23
24 void excerpt(int *E)
25 {
26 int c,r,mxnum=0,i,j;
CAPITOLUL 11. IOI 2010 1147

27 unsigned int p3[98];


28 double z,mx;
29 memset(now,0,sizeof(now));
30 p3[0]=(E[0]<<10^E[1]<<5^E[2])&mod;
31 c=num3[p3[0]];
32 if(c!=0)
33 {
34 for(j=0;j<56;j++)
35 {
36 z=cbrt(pre3[j][p3[0]]);
37 now[j]+=2*z*z/(c+1);
38 }
39 }
40 for(i=1;i<98;i++)
41 {
42 p3[i]=(p3[i-1]<<5^E[i+2])&mod;
43 c=num3[p3[i]];
44 if(c!=0)
45 {
46 for(j=0;j<56;++j)
47 {
48 z=cbrt(pre3[j][p3[i]]);
49 now[j]+=z*z/(c+2);
50 }
51 }
52 }
53 for(i=0;i<100;i++)
54 {
55 if(num1[E[i]]!=0)
56 {
57 for(j=0;j<56;++j)
58 {
59 z=cbrt(pre1[E[i]][j]);
60 now[j]+=z*z/num1[E[i]];
61 }
62 }
63 }
64 mx=now[0];
65 for(i=1;i<56;i++)
66 {
67 if(now[i]>mx)
68 {
69 mx=now[i];
70 mxnum=i;
71 }
72 }
73 r=language(mxnum);
74 for(i=0;i<98;i++)
75 {
76 num3[p3[i]]++;
77 pre3[r][p3[i]]++;
78
79 }
80 for(i=0;i<100;i++)
81 {
82 num1[E[i]]++;
83 pre1[E[i]][r]++;
84 }
85 }
86
87 // ------------ begin grader --------------------
88
89 #define N 100
90
91 static char lang[20], lan[100][20];
92 static int lnum, i,j,k,n,nl, uni[N], rightt, tot;
93
94 int language(int L) {
95 if (L < 0 || L >= 56) exit(92);
96 rightt += (L == lnum);
97 tot++;
98 return lnum;
99 }
100
101 int main()
102 {
CAPITOLUL 11. IOI 2010 1148

103 auto t1 = clock();


104
105 freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
106 freopen("language.out", "w", stdout);
107
108 auto t2 = clock();
109
110 for (n=0; 1 == scanf("%s",lang); n++)
111 {
112 for (i=0;i<nl && strcmp(lang,lan[i]);i++);
113 strcpy(lan[i],lang);
114 if (i == nl) {
115 nl++;
116 }
117 lnum = i;
118 for (i=0;i<N;i++) scanf("%d,",&uni[i]);
119 scanf("%*[^\n]");
120 excerpt(uni);
121 }
122
123 auto t3 = clock();
124
125 printf("OK %0.2lf%%\n",100.0*rightt/tot);
126
127 auto t4 = clock();
128
129 // reset console output
130 freopen("CON", "w", stdout);
131
132 printf("OK %0.2lf%%\n",100.0*rightt/tot);
133 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
134 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
135 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
136
137 return 0;
138 }
139
140 // ------------ end grader --------------------
141 /*
142 OK 91.72%
143 t2-t1 = 0.015
144 t3-t2 = 13.578
145 t4-t3 = 0
146
147 Process returned 0 (0x0) execution time : 13.734 s
148 Press any key to continue.
149 */

Listing 11.4.2: lang-12891.cpp


1 // https://oj.uz/submission/12891 1735 ms 32268 KB
2
3 #include <stdio.h>
4 #include <time.h>
5 #include <stdlib.h>
6
7 #include<iostream>
8 #include<cstring>
9
10 #include <map>
11 #include <algorithm>
12 #include <vector>
13 #include "lang.h"
14 #include "grader.h"
15
16 using namespace std;
17
18 struct WD
19 {
20 long long t1, t2;
21 void init()
22 {
23 t1 = t2 = 0;
24 }
25 void addwd(int p, int pos)
CAPITOLUL 11. IOI 2010 1149

26 {
27 if(pos == 0) t1 = p;
28 else t2 |= (1ll << (16 * (4 - pos))) * p;
29 }
30 };
31
32 bool operator<(WD a, WD b)
33 {
34 return a.t1 != b.t1 ? (a.t1 > b.t1) : (a.t2 > b.t2);
35 }
36
37 map<WD,int> mp;
38 const int l = 5;
39
40 void excerpt(int *E)
41 {
42 int cnt[105] = {};
43 vector<WD> v;
44 for (int i=0; i<96; i++)
45 {
46 WD px;
47 px.init();
48 for (int j=0; j<l; j++)
49 {
50 px.addwd(E[i+j],j);
51 }
52
53 map<WD,int> ::iterator it = mp.lower_bound(px);
54
55 if(it != mp.end()) cnt[( *it).second] += 100;
56 it = mp.upper_bound(px);
57 if(it != mp.end()) cnt[( *it).second] += 85;
58 if(it != mp.begin()) cnt[( *--it).second] += 100;
59 v.push_back(px);
60 }
61
62 int mv = (int)(max_element(cnt,cnt+56) - cnt);
63 int t = language(mv);
64 for (int i=0; i<v.size(); i++)
65 {
66 mp[v[i]] = t;
67 }
68 }
69
70 // ------------ begin grader --------------------
71
72 #define N 100
73
74 static char lang[20], lan[100][20];
75 static int lnum, i,j,k,n,nl, uni[N], rightt, tot;
76
77 int language(int L)
78 {
79 if (L < 0 || L >= 56) exit(92);
80 rightt += (L == lnum);
81 tot++;
82 return lnum;
83 }
84
85 int main()
86 {
87 auto t1 = clock();
88
89 freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
90 freopen("language.out", "w", stdout);
91
92 auto t2 = clock();
93
94 for (n=0; 1 == scanf("%s",lang); n++)
95 {
96 for (i=0;i<nl && strcmp(lang,lan[i]);i++);
97 strcpy(lan[i],lang);
98 if (i == nl) {
99 nl++;
100 }
101 lnum = i;
CAPITOLUL 11. IOI 2010 1150

102 for (i=0;i<N;i++) scanf("%d,",&uni[i]);


103 scanf("%*[^\n]");
104 excerpt(uni);
105 }
106
107 auto t3 = clock();
108
109 printf("OK %0.2lf%%\n",100.0*rightt/tot);
110
111 auto t4 = clock();
112
113 // reset console output
114 freopen("CON", "w", stdout);
115
116 printf("OK %0.2lf%%\n",100.0*rightt/tot);
117 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
118 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
119 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
120
121 return 0;
122 }
123
124 // ------------ end grader --------------------
125 /*
126 OK 90.78%
127 t2-t1 = 0
128 t3-t2 = 5.234
129 t4-t3 = 0
130
131 Process returned 0 (0x0) execution time : 5.484 s
132 Press any key to continue.
133 */

Listing 11.4.3: lang-155338.cpp


1 // https://oj.uz/submission/155338 3788 ms 55452 KB
2
3 #include <stdio.h>
4 #include <time.h>
5 #include <stdlib.h>
6
7 #include<iostream>
8
9 #include "grader.h"
10 #include "lang.h"
11
12 #include <bits/stdc++.h>
13 using namespace std;
14
15 const int n = 100, m = 56;
16
17 const int mod = 80021;
18
19 int cnt[57][80808];
20 int cnt2[57][80808];
21 int cnt3[57][80808];
22 int cnt4[57][80808];
23 int lann[57];
24
25 int f(int a, int b)
26 {
27 long long ret = (long long)a << 16;
28 ret |= b;
29 ret %= mod;
30 return ret;
31 }
32
33 int f(int a, int b, int c)
34 {
35 long long ret = (long long)f(a, b) << 16;
36 ret |= c;
37 ret %= mod;
38 return ret;
39 }
40
CAPITOLUL 11. IOI 2010 1151

41 int f(int a, int b, int c, int d)


42 {
43 long long ret = (long long)f(a, b, c) << 16;
44 ret |= d;
45 ret %= mod;
46 return ret;
47 }
48
49 double f(int x)
50 {
51 return (double)x / (x + 1);
52 }
53
54 void excerpt(int *arr)
55 {
56 double mx = 0;
57 int idx = 0;
58 for(int i=0; i<m; i++)
59 {
60 double now = 0;
61 for(int j=0; j<n; j++)
62 {
63 now += f(cnt[i][arr[i]]);
64 if(j >= n-1) continue;
65 now += f(cnt2[i][f(arr[j], arr[j+1])]) * 100;
66 if(j >= n-2) continue;
67 now += f(cnt3[i][f(arr[j], arr[j+1], arr[j+2])]) * 150;
68 if(j >= n-3) continue;
69 now += f(cnt4[i][f(arr[j],arr[j+1],arr[j+2],arr[j+3])])*100;
70 }
71 now /= log(lann[i] + 1);
72 if(now > mx)
73 {
74 mx = now; idx = i;
75 }
76 }
77
78 int ans = language(idx);
79 lann[ans]++;
80 for(int i=0; i<n; i++)
81 {
82 cnt[ans][arr[i]]++;
83 if(i >= n-1) continue;
84 cnt2[ans][f(arr[i], arr[i+1])]++;
85 if(i >= n-2) continue;
86 cnt3[ans][f(arr[i], arr[i+1], arr[i+2])]++;
87 if(i >= n-3) continue;
88 cnt4[ans][f(arr[i], arr[i+1], arr[i+2], arr[i+3])]++;
89 }
90 }
91
92 // ------------ begin grader --------------------
93
94 #define N 100
95
96 static char lang[20], lan[100][20];
97 static int lnum, i,j,k,nn,nl, uni[N], rightt, tot;
98
99
100 int language(int L)
101 {
102 if (L < 0 || L >= 56) exit(92);
103 rightt += (L == lnum);
104 tot++;
105 return lnum;
106 }
107
108 int main()
109 {
110 auto t1 = clock();
111
112 freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
113 freopen("language.out", "w", stdout);
114
115 auto t2 = clock();
116
CAPITOLUL 11. IOI 2010 1152

117 for (nn=0; 1 == scanf("%s",lang); nn++)


118 {
119 for (i=0;i<nl && strcmp(lang,lan[i]);i++);
120 strcpy(lan[i],lang);
121 if (i == nl)
122 {
123 nl++;
124 }
125 lnum = i;
126 for (i=0;i<N;i++) scanf("%d,",&uni[i]);
127 scanf("%*[^\n]");
128 excerpt(uni);
129 }
130
131 auto t3 = clock();
132
133 printf("OK %0.2lf%%\n",100.0*rightt/tot);
134
135 auto t4 = clock();
136
137 // reset console output
138 freopen("CON", "w", stdout);
139
140 printf("OK %0.2lf%%\n",100.0*rightt/tot);
141 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
142 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
143 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
144
145 return 0;
146 }
147
148 // ------------ end grader --------------------
149 /*
150 OK 92.40%
151 t2-t1 = 0
152 t3-t2 = 19.515
153 t4-t3 = 0
154
155 Process returned 0 (0x0) execution time : 19.609 s
156 Press any key to continue.
157 */

Listing 11.4.4: lang-235517.cpp


1 // https://oj.uz/submission/235517 2196 ms 118528 KB
2
3 #include <stdio.h>
4 #include <time.h>
5 #include <stdlib.h>
6
7 #include<iostream>
8
9 #include <bits/stdc++.h>
10 #define FOR(i, x, y) for (int i = x; i < y; i++)
11 typedef long long ll;
12
13 #include "grader.h"
14
15 const int SZ = 100, LANGS = 56, MOD = 539653;
16
17 ll si[SZ], bi[SZ], tr[SZ], qu[SZ];
18 double cnt[LANGS];
19 int freq[LANGS][MOD];
20
21 inline double hyperb(int x) { return x / (x + 1.0); }
22
23 void excerpt(int *E)
24 {
25 FOR(i, 0, SZ - 3)
26 {
27 si[i] = E[i];
28 bi[i] = ((si[i] << 16) + E[i + 1]) % MOD;
29 tr[i] = ((bi[i] << 16) + E[i + 2]) % MOD;
30 qu[i] = ((tr[i] << 16) + E[i + 3]) % MOD;
31 }
CAPITOLUL 11. IOI 2010 1153

32
33 int best = 0;
34 double best_sim = 0;
35 FOR(i, 0, LANGS)
36 {
37 double sim = 0;
38 FOR(j, 0, SZ - 3)
39 {
40 sim += hyperb(freq[i][qu[j]]) * 138;
41 sim += hyperb(freq[i][tr[j]]) * 60;
42 sim += hyperb(freq[i][bi[j]]) * 97;
43 sim += hyperb(freq[i][si[j]]) * 1;
44 }
45 sim /= log(cnt[i] + 1);
46
47 if (sim > best_sim) best = i, best_sim = sim;
48 }
49
50 int ans = language(best);
51 cnt[ans]++;
52 FOR(i, 0, SZ - 3)
53 {
54 freq[ans][qu[i]]++;
55 freq[ans][tr[i]]++;
56 freq[ans][bi[i]]++;
57 freq[ans][si[i]]++;
58 }
59 }
60
61 // ------------ begin grader --------------------
62
63 #define N 100
64
65 static char lang[20], lan[100][20];
66 static int lnum, i,j,k,n,nl, uni[N], rightt, tot;
67
68 int language(int L)
69 {
70 if (L < 0 || L >= 56) exit(92);
71 rightt += (L == lnum);
72 tot++;
73 return lnum;
74 }
75
76 int main()
77 {
78 auto t1 = clock();
79
80 freopen("../tests/Subtask1-data/grader.in.1", "r", stdin);
81 freopen("language.out", "w", stdout);
82
83 auto t2 = clock();
84
85 for (n=0; 1 == scanf("%s",lang); n++)
86 {
87 for (i=0;i<nl && strcmp(lang,lan[i]);i++);
88 strcpy(lan[i],lang);
89 if (i == nl)
90 {
91 nl++;
92 }
93 lnum = i;
94 for (i=0;i<N;i++) scanf("%d,",&uni[i]);
95 scanf("%*[^\n]");
96 excerpt(uni);
97 }
98
99 auto t3 = clock();
100
101 printf("OK %0.2lf%%\n",100.0*rightt/tot);
102
103 auto t4 = clock();
104
105 // reset console output
106 freopen("CON", "w", stdout);
107
CAPITOLUL 11. IOI 2010 1154

108 printf("OK %0.2lf%%\n",100.0*rightt/tot);


109 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
110 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
111 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
112
113 return 0;
114 }
115
116 // ------------ end grader --------------------
117 /*
118 OK 92.51%
119 t2-t1 = 0
120 t3-t2 = 8.062
121 t4-t3 = 0
122
123 Process returned 0 (0x0) execution time : 8.172 s
124 Press any key to continue.
125 */

11.4.3 *Rezolvare detaliat 

11.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 shued 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 rst time he sees both copies of the letter on the
two face up cards. For example, the rst 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 11. IOI 2010 1155

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 rst 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 nishes 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 nishes 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 le 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 le
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 le.
ˆ 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 le.

11.5.1 Indicaµii de rezolvare

Task Author: Gordon Cormack (CAN)

This was intended to be another very easy task, though slightly more dicult 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) suces. This can be programmed with two nested for-loops, and it
solves Subtask 1.
CAPITOLUL 11. IOI 2010 1156

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 rst turn all cards, in pairs, to discover and
record where all the letters are, without caring about turning up equal pairs. In this rst 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 ip them, in sequence, to obtain all remaining candies.
The rst 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 identied in the rst 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 denitions
could be eliminated, but they document the relevant concepts nicely):

Listing 11.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 dentions, it could be coded as follows:

Listing 11.5.2: memory.cpp


1
2 void play()
3 {
CAPITOLUL 11. IOI 2010 1157

4 // letters ’A’ to ’Y’ are converted to integers 0 to 24


5
6 int index[50][2]; // locations of cards; 0 = unknown
7 int lt, k; // traverses index
8 int i; // traverses cards
9 char r; // result of faceup
10
11 // initialize index
12 for (lt = 0; lt < 25; ++lt)
13 {
14 for (k = 0; k < 2; ++k)
15 {
16 index[lt][k] = 0;
17 }
18 }
19
20 // first round
21 for (i = 1; i <= 50; ++i)
22 {
23 r = faceup(i);
24 lt = (int)(r) - (int)(’A’); // int corresponding to char r
25 k = (index[lt][0]) ? 1 : 0;
26 index[lt][k] = i;
27 }
28
29 // second round
30 for (lt = 0; lt < 25; ++lt)
31 {
32 faceup( index[lt][0] ); // result ignored
33 faceup( index[lt][1] ); // result ignored
34 }
35
36 }

11.5.2 Coduri surs 

Listing 11.5.3: memory-206951.cpp


1 // https://oj.uz/submission/206951 8 ms 504 KB
2 #include "memory.h"
3 #include "grader.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include<bits/stdc++.h>
8
9 using namespace std;
10
11 void play()
12 {
13 map<char, vector<int>> mp;
14 for (int i = 1; i <= 50; i++)
15 mp[faceup(i)].push_back(i);
16
17 for (auto p : mp)
18 faceup(p.second[0]), faceup(p.second[1]);
19 }
20
21 // ------------ begin grader --------------------
22
23 static char card[51];
24 static int up[2], is_up[51], candy[25], candies, moves;
25
26 char faceup(int C)
27 {
28 int c0, c1;
29 if (C < 1 || C > 50 || is_up[C])
30 {
31 exit(92);
32 }
CAPITOLUL 11. IOI 2010 1158

33 is_up[C] = 1;
34 up[moves%2] = C;
35 moves++;
36 if (moves%2 == 0)
37 {
38 c0 = card[ up[0] ] - ’A’;
39 c1 = card[ up[1] ] - ’A’;
40 if (c0==c1 && !candy[c0])
41 {
42 candy[c0] = 1;
43 ++candies;
44 }
45 is_up[ up[0] ] = is_up[ up[1] ] = 0;
46 }
47 return card[C];
48 }
49
50 void playgame()
51 {
52 int i;
53 for (i=1;i<=50;i++)
54 {
55 card[i] = getchar();
56 }
57 moves = candies = 0;
58 play();
59 if (candies != 25)
60 {
61 exit(91);
62 }
63 }
64
65 int main()
66 {
67 std::freopen("../tests/Subtask1-data/grader.in.99", "r", stdin);
68 //std::freopen("cluedo.out", "w", stdout);
69
70 playgame();
71 printf("OK %d\n",moves);
72 return 0;
73 }
74 // ------------ end grader --------------------

Listing 11.5.4: memory-217254.cpp


1 // https://oj.uz/submission/217254 6 ms 512 KB
2 #include "memory.h"
3 #include "grader.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include <bits/stdc++.h>
8
9 using namespace std;
10
11 vector<int> a[26];
12
13 void play()
14 {
15 for(int i=1;i <= 50;i++){ char x=faceup(i); a[x-’A’].push_back(i); }
16 for(int i = 0;i < 25;i++) faceup(a[i][0]),faceup(a[i][1]);
17 }
18
19 // ------------ begin grader --------------------
20
21 static char card[51];
22 static int up[2], is_up[51], candy[25], candies, moves;
23
24 char faceup(int C)
25 {
26 int c0, c1;
27 if (C < 1 || C > 50 || is_up[C])
28 {
29 exit(92);
30 }
CAPITOLUL 11. IOI 2010 1159

31 is_up[C] = 1;
32 up[moves%2] = C;
33 moves++;
34 if (moves%2 == 0)
35 {
36 c0 = card[ up[0] ] - ’A’;
37 c1 = card[ up[1] ] - ’A’;
38 if (c0==c1 && !candy[c0])
39 {
40 candy[c0] = 1;
41 ++candies;
42 }
43 is_up[ up[0] ] = is_up[ up[1] ] = 0;
44 }
45 return card[C];
46 }
47
48 void playgame()
49 {
50 int i;
51 for (i=1;i<=50;i++)
52 {
53 card[i] = getchar();
54 }
55 moves = candies = 0;
56 play();
57 if (candies != 25)
58 {
59 exit(91);
60 }
61 }
62
63 int main()
64 {
65 std::freopen("../tests/Subtask1-data/grader.in.98", "r", stdin);
66 //std::freopen("cluedo.out", "w", stdout);
67
68 playgame();
69 printf("OK %d\n",moves);
70 return 0;
71 }
72
73 // ------------ end grader --------------------

Listing 11.5.5: memory-232610.cpp


1 // https://oj.uz/submission/232610 6 ms 436 KB
2 #include "memory.h"
3 #include "grader.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include<bits/stdc++.h>
9
10 using namespace std;
11
12 int A[51][2],B[51];
13 void play()
14 {
15 for (int i = 1; i <= 50; i++)
16 {
17 int x = (faceup(i) - ’A’ + 1);
18 A[x][B[x]++] = i;
19 }
20 for (int i = 1; i <= 25; i++)
21 {
22 faceup(A[i][0]);
23 faceup(A[i][1]);
24 }
25 return ;
26 }
27
28 // ------------ begin grader --------------------
29
CAPITOLUL 11. IOI 2010 1160

30 static char card[51];


31 static int up[2], is_up[51], candy[25], candies, moves;
32
33 char faceup(int C)
34 {
35 int c0, c1;
36 if (C < 1 || C > 50 || is_up[C])
37 {
38 exit(92);
39 }
40 is_up[C] = 1;
41 up[moves%2] = C;
42 moves++;
43 if (moves%2 == 0)
44 {
45 c0 = card[ up[0] ] - ’A’;
46 c1 = card[ up[1] ] - ’A’;
47 if (c0==c1 && !candy[c0])
48 {
49 candy[c0] = 1;
50 ++candies;
51 }
52 is_up[ up[0] ] = is_up[ up[1] ] = 0;
53 }
54 return card[C];
55 }
56
57 void playgame()
58 {
59 int i;
60 for (i=1;i<=50;i++)
61 {
62 card[i] = getchar();
63 }
64 moves = candies = 0;
65 play();
66 if (candies != 25)
67 {
68 exit(91);
69 }
70 }
71
72 int main()
73 {
74 std::freopen("../tests/Subtask1-data/grader.in.97", "r", stdin);
75 //std::freopen("cluedo.out", "w", stdout);
76
77 playgame();
78 printf("OK %d\n",moves);
79 return 0;
80 }
81
82 // ------------ end grader --------------------

11.5.3 *Rezolvare detaliat 


CAPITOLUL 11. IOI 2010 1161

11.6 Trac Congestion


Problema 6 - Trac Congestion 100 de puncte
Author: Jorge Bernadas (VEN)

Although Canada is a large country, many areas are uninha-


bited, and most of the population lives near the southern bor-
der. The Trans-Canada Highway, completed in 1962, connects
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 trac 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
sequence 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 ho-
ckey 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 proportio-
nal 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 ve 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 nal 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 specically, 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 11. IOI 2010 1162

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 rst line of the input le 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 D i 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 le.
ˆ 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 le.

11.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 rst think because of the need to handle
specic 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 suces 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 11. IOI 2010 1163

11.6.2 Coduri surs 

Listing 11.6.1: quality-229321.cpp


1 // https://oj.uz/submission/229321 1353 ms 141292 KB
2 #include "traffic.h"
3 #include <stdio.h>
4
5 #include<iostream>
6 #include<ctime>
7
8 #pragma GCC optimize("Ofast")
9
10 #include<vector>
11 #include<algorithm>
12
13 using namespace std;
14
15 vector<int> g[1000005];
16 long long num[1000005],minv=1000000000000000000;
17 int n,ans;
18
19 long long DFS(int v,int p)
20 {
21 for(auto x:g[v])
22 if(x!=p)
23 num[v]+=DFS(x,v);
24 return num[v];
25 }
26
27 void DP(int v,int p)
28 {
29 for(auto x:g[v])
30 {
31 if(x==p)
32 continue;
33 long long pre=num[v];
34 num[v]-=num[x];
35 num[x]=pre;
36 DP(x,v);
37 num[x]-=num[v];
38 num[v]=pre;
39 }
40
41 long long maxv=0;
42 for(auto x:g[v])
43 maxv=max(maxv,num[x]);
44 if(maxv<minv)
45 {
46 minv=maxv;
47 ans=v;
48 }
49 }
50
51 int LocateCentre(int N,int *P,int *S,int *D)
52 {
53 int i;
54 n=N;
55 for(i=0;i<n;i++)
56 num[i]=P[i];
57 for(i=0;i<n-1;i++)
58 {
59 g[S[i]].emplace_back(D[i]);
60 g[D[i]].emplace_back(S[i]);
61 }
62 DFS(0,-1);
63 DP(0,-1);
64 return ans;
65 }
66
67 // ------------- begin grader -----------------------
68
69 static int N,P[1000000],S[1000000],D[1000000];
70
CAPITOLUL 11. IOI 2010 1164

71 int main()
72 {
73 auto t1 = clock();
74
75 std::freopen("../tests/Subtask4-data/grader.in.52-4", "r", stdin);
76 std::freopen("traffic.out", "w", stdout);
77
78 int i;
79 scanf("%d",&N);
80 for (i=0;i<N;i++) scanf("%d",&P[i]);
81 for (i=0;i<N-1;i++) scanf("%d%d",&S[i],&D[i]);
82
83 auto t2 = clock();
84
85 int r = LocateCentre(N,P,S,D);
86
87 auto t3 = clock();
88
89 printf("%d\n",r);
90
91 auto t4 = clock();
92
93 // reset console output
94 freopen("CON", "w", stdout);
95
96 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
97 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
98 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
99
100 return 0;
101 }
102
103 // ------------- end grader -----------------------
104 /*
105 t2-t1 = 1.031
106 t3-t2 = 1.438
107 t4-t3 = 0
108
109 Process returned 0 (0x0) execution time : 3.406 s
110 Press any key to continue.
111 */

Listing 11.6.2: quality-232654.cpp


1 // https://oj.uz/submission/232654 1206 ms 156712 KB
2 #include "traffic.h"
3 #include <stdio.h>
4
5 #include<iostream>
6 #include<ctime>
7
8 #include<bits/stdc++.h>
9
10 using namespace std;
11
12 int dp[1000006],sum[1000001], C[1000006] , tot;
13 vector <int> adj[1000005];
14
15 void dfs(int v,int p)
16 {
17 dp[v] = sum[v] = 0;
18 int s = 0;
19
20 for (int i:adj[v])
21 {
22 if (i != p)
23 {
24 dfs (i,v);
25 dp[v] = max(dp[v], sum[i] + C[i]);
26 sum[v] += sum[i] + C[i];
27 }
28 }
29 dp[v] = max(dp[v],tot - C[v] - sum[v]);
30 }
31
CAPITOLUL 11. IOI 2010 1165

32 int LocateCentre(int n,int P[],int S[],int D[])


33 {
34 for (int i = 0; i < n-1; i++)
35 adj[S[i]].push_back(D[i]),
36 adj[D[i]].push_back(S[i]);
37 for (int i = 0; i < n; i++)
38 C[i] = P[i], tot += C[i];
39 pair<int,int> ans = {INT_MAX,-1};
40 dfs(0,-1);
41 for (int i = 0; i < n; i++)
42 {
43 ans = min(ans,{dp[i],i});
44 }
45
46 return ans.second;
47 }
48
49 // ------------- begin grader -----------------------
50
51 static int N,P[1000000],S[1000000],D[1000000];
52
53 int main()
54 {
55 auto t1 = clock();
56
57 std::freopen("../tests/Subtask4-data/grader.in.52-4", "r", stdin);
58 std::freopen("traffic.out", "w", stdout);
59
60 int i;
61 scanf("%d",&N);
62 for (i=0;i<N;i++) scanf("%d",&P[i]);
63 for (i=0;i<N-1;i++) scanf("%d%d",&S[i],&D[i]);
64
65 auto t2 = clock();
66
67 int r = LocateCentre(N,P,S,D);
68
69 auto t3 = clock();
70
71 printf("%d\n",r);
72
73 auto t4 = clock();
74
75 // reset console output
76 freopen("CON", "w", stdout);
77
78 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
79 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
80 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
81
82 return 0;
83 }
84
85 // ------------- end grader -----------------------
86 /*
87 t2-t1 = 1.037
88 t3-t2 = 1.516
89 t4-t3 = 0
90
91 Process returned 0 (0x0) execution time : 3.349 s
92 Press any key to continue.
93 */

Listing 11.6.3: quality-236364.cpp


1 // https://oj.uz/submission/236364 1212 ms 156792 KB
2 #include "traffic.h"
3 #include <stdio.h>
4
5 #include<iostream>
6 #include<ctime>
7
8 #include <bits/stdc++.h>
9
10 #define DIM 1000010
CAPITOLUL 11. IOI 2010 1166

11 #define INF 2000000000000000000LL


12
13 using namespace std;
14
15 vector <int> L[DIM];
16 long long sum[DIM];
17 int fth[DIM];
18
19 void dfs (int nod, int tata)
20 {
21 fth[nod] = tata;
22 for (auto vecin : L[nod])
23 if (vecin != tata)
24 {
25 dfs (vecin,nod);
26 sum[nod] += sum[vecin];
27 }
28 }
29
30 int LocateCentre (int n, int p[], int s[], int d[])
31 {
32
33 for (int i=0;i<n-1;i++)
34 {
35 s[i]++, d[i]++;
36 L[s[i]].push_back(d[i]);
37 L[d[i]].push_back(s[i]);
38 }
39
40 long long sum_total = 0;
41 for (int i=1;i<=n;i++)
42 {
43 sum[i] = p[i-1];
44 sum_total += sum[i];
45 }
46
47 dfs (1,0);
48
49 long long mini = INF;
50 int sol = 0;
51 for (int i=1;i<=n;i++)
52 {
53 long long maxi = 0;
54 for (auto vecin : L[i])
55 {
56 if (vecin == fth[i])
57 continue;
58 maxi = max (maxi,sum[vecin]);
59 }
60
61 maxi = max (maxi,sum_total - sum[i]);
62
63 if (maxi < mini)
64 {
65 mini = maxi;
66 sol = i;
67 }
68 }
69
70 return sol - 1;
71
72 }
73
74 // ------------- begin grader -----------------------
75
76 static int N,P[1000000],S[1000000],D[1000000];
77
78 int main()
79 {
80 auto t1 = clock();
81
82 std::freopen("../tests/Subtask4-data/grader.in.52-4", "r", stdin);
83 std::freopen("traffic.out", "w", stdout);
84
85 int i;
86 scanf("%d",&N);
CAPITOLUL 11. IOI 2010 1167

87 for (i=0;i<N;i++) scanf("%d",&P[i]);


88 for (i=0;i<N-1;i++) scanf("%d%d",&S[i],&D[i]);
89
90 auto t2 = clock();
91
92 int r = LocateCentre(N,P,S,D);
93
94 auto t3 = clock();
95
96 printf("%d\n",r);
97
98 auto t4 = clock();
99
100 // reset console output
101 freopen("CON", "w", stdout);
102
103 std::cout <<"t2-t1 = " << (double)(t2 - t1) / CLOCKS_PER_SEC << ’\n’;
104 std::cout <<"t3-t2 = " << (double)(t3 - t2) / CLOCKS_PER_SEC << ’\n’;
105 std::cout <<"t4-t3 = " << (double)(t4 - t3) / CLOCKS_PER_SEC << ’\n’;
106
107 return 0;
108 }
109
110 // ------------- end grader -----------------------
111 /*
112 t2-t1 = 1.047
113 t3-t2 = 1.547
114 t4-t3 = 0
115
116 Process returned 0 (0x0) execution time : 3.438 s
117 Press any key to continue.
118 */

Listing 11.6.4: checkerQuality.cpp


1 #include "testlib.h"
2
3 using namespace std;
4
5 int main()
6 //int main(int argc, char * argv[])
7 {
8 int argc=4;
9
10 char* argv[] =
11 {
12 (char*)"checker",
13 (char*)"../tests/Subtask4-data/grader.in.52-4",
14 (char*)"../tests/Subtask4-data/grader.expect.52-4",
15 (char*)"traffic.out",
16 };
17
18 cout<<"argc = "<<argc<<"\n";
19 for(int kk=0;kk<argc;kk++)
20 cout<<argv[kk]<<"\n";
21 cout<<"----------------------\n";
22
23 registerChecker("traffic", argc, argv);
24 compareRemainingLines();
25 }

11.6.3 *Rezolvare detaliat 

11.7 Maze
Problema 7 - Maze 100 de puncte
Authors: Monika Steinová (CHE/SWK), Michal Forisek (SWK)
CAPITOLUL 11. IOI 2010 1168

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 eld is covered with corn sta-
lks 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 nding 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 eld is represented as text; for example, a 6m by 10m eld 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 eld is transformed into a maze by crushing squares occupied by corn. One crushed square
(the entrance) must be on the edge of the eld. 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 identied 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 les named


field1.txt field2.txt etc. containing maps of cornelds. You are to copy them to les
CAPITOLUL 11. IOI 2010 1169

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 eld described above (of size 6  10) may be found in the le field1.txt. Create a
maze for this eld 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 le field2.txt represents a eld of size 100  100. Create a maze for this eld 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 le field3.txt represents a eld of size 100  100. Create a maze for this eld 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 le field4.txt represents a eld of size 100  100. Create a maze for this eld 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 le field5.txt represents a eld of size 100  100. Create a maze for this eld 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 le field6.txt represents a eld of size 11  11. Create a maze for this eld 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 le field7.txt represents a eld of size 20  20. Create a maze for this eld 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 le field8.txt represents a eld of size 20  20. Create a maze for this eld 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 le field9.txt represents a eld of size 11  21. Create a maze for this eld 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 le fieldA.txt represents a eld of size 200  200. Create a maze for this eld 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 11. IOI 2010 1170

ˆ 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 les
in the implementation folder will be submitted, regardless of which is specied in the submit
command.
ˆ Submit (gedit plugin): Control-J, while editing any .txt le.

11.7.1 Indicaµii de rezolvare

Task Authors: Monika Steinová (CHE/SWK), Michal Forisek (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 eld).
This is also the reason why this task was oered as an output-only task, where the contestants
were given 10 specic instances (corn elds) to tackle. They could do this by hand, or write
programs to analyze these mazes, and write yet other programs (potentially, a dierent program
for each eld) 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

11.7.2 *Coduri surs 

11.7.3 *Rezolvare detaliat 

11.8 Saveit
Problema 8 - Saveit 100 de puncte
Author: Mihai P tra³cu (ROM)
CAPITOLUL 11. IOI 2010 1171

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 rst 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 specied interface. Shared va-
riables, le 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 les.

Example
As an example, consider the diagram on the right.
It shows ve 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 11. IOI 2010 1172

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 rst line of each le 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 le.
ˆ 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 le.

11.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 eciency matters.
However, in this case it is not execution time or memory usage but rather communication eciency:
how to represent some complex data in as few bits as possible, without losing information.
This dierence 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, nd a way to encode adjacency information about Xedef's
package transportation network. Second, to transmit that information with communication e-
ciency.
Briey 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 11. IOI 2010 1173

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

11.8.2 Coduri surs 

Listing 11.8.1: saveit-231998.cpp


1 // https://oj.uz/submission/231998
2
3 #include "grader.h"
4 #include "encoder.h"
5 #include "decoder.h"
6
7 #include <stdio.h>
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include<iostream>
13
14 #include <vector>
15 #include <cmath>
16 #include <cassert>
17 #include <queue>
18
19 using namespace std;
20
21 static int nv, ne, c;
22 static int curbit = 0;
23
24 static int v1[1234567], v2[1234567];
25 static int h[1000][1000], hcnt;
26 static int bits[16000000], nb;
27
28 #define FOR(i,n) for (int i = 0; i < (n); i++)
29
30 void encode_bit(int bit)
31 {
32 bits[nb++] = bit;
33 }
34
35 int decode_bit()
36 {
37 if (curbit >= nb)
38 {
39 exit(92);
CAPITOLUL 11. IOI 2010 1174

40 }
41 return bits[curbit++];
42 }
43
44 void hops(int a, int b, int d)
45 {
46 if (h[a][b] != d)
47 {
48 exit(92);
49 }
50 h[a][b] = -1;
51 hcnt++;
52 }
53
54 // -------------- encoder --------------------
55
56 int const nmax = 1000;
57 int const kmax = 36; // = 40 ... !!!
58
59 using ll = long long;
60
61 std::vector<int> g[1 + nmax];
62
63 std::vector<int> bfs(int n, int node)
64 {
65 std::vector<int> dist(n);
66 std::queue<int> q;
67 q.push(node);
68 dist[node] = 1;
69 while(0 < q.size())
70 {
71 int node = q.front();
72 q.pop();
73 for(int h = 0; h < g[node].size(); h++)
74 {
75 int to = g[node][h];
76 if(dist[to] == 0)
77 {
78 dist[to] = dist[node] + 1;
79 q.push(to);
80 }
81 }
82 }
83 for(int i = 0; i < n; i++)
84 dist[i]--;
85 return dist;
86 }
87
88 std::vector<int> dist[1 + kmax];
89 int far[1 + nmax];
90
91 int const necessary = 35 / 5 * 8;
92
93 void push(ll number, int bits)
94 {
95 for(int i = 0; i < bits; i++)
96 encode_bit(0 < (number & (1LL << i)));
97 }
98
99 void encode(int n, int k, int m, int *v1, int *v2)
100 {
101 for(int i = 0; i < m; i++)
102 {
103 int x = v1[i];
104 int y = v2[i];
105 g[x].push_back(y);
106 g[y].push_back(x);
107 }
108
109 for(int i = 0; i < k; i++)
110 dist[i] = bfs(n, i);
111
112 for(int i = 1;i < n; i++)
113 {
114 for(int h = 0; h < g[i].size(); h++)
115 {
CAPITOLUL 11. IOI 2010 1175

116 int to = g[i][h];


117 if(dist[0][to] + 1 == dist[0][i])
118 {
119 far[i] = to;
120 push(to, 10);
121 break;
122 }
123 }
124 }
125
126 for(int i = 1; i < n; i++)
127 {
128 ll number = 0;
129 int par = far[i];
130 for(int h = 1; h < k; h++)
131 number = number * 3 + (dist[h][i] - dist[h][par]) + 1;
132 push(number, necessary);
133 }
134
135 return;
136 }
137
138 // -------------- decoder --------------------
139
140 using ll = long long;
141 //int const necessary = 35 / 5 * 8;
142 //int const kmax = 40;
143 //int const nmax = 1000;
144
145 int _far[1 + nmax];
146
147 ll extract(int bits)
148 {
149 ll result = 0;
150 for(int i = 0; i < bits; i++)
151 result += decode_bit() * (1LL << i);
152 return result;
153 }
154
155 std::vector<int> _g[1 + nmax];
156 int cost[1 + kmax][1 + nmax];
157 int dp[1 + kmax][1 + nmax];
158
159 void mark(int node, int era)
160 {
161 for(int h = 0; h < _g[node].size(); h++)
162 {
163 int to = _g[node][h];
164 dp[era][to] = dp[era][node] + cost[era][to];
165 mark(to, era);
166 }
167 }
168
169
170 void decode(int n, int k)
171 {
172 for(int i = 1;i < n; i++)
173 {
174 _far[i] = extract(10);
175 _g[_far[i]].push_back(i);
176 }
177
178 for(int i = 1; i < n; i++)
179 {
180 ll number = extract(necessary);
181 cost[0][i] = 1;
182 for(int h = k - 1; 0 < h; h--)
183 {
184 cost[h][i] = number % 3 - 1;
185 number /= 3;
186 }
187 }
188
189 for(int h = 0; h < k; h++)
190 {
191 mark(0, h);
CAPITOLUL 11. IOI 2010 1176

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


193 hops(h, i, dp[h][i] - dp[h][h]);
194 }
195 }
196
197 // ----------------- begin grader ------------------------
198
199 /* and here is the driver */
200
201 int main(int argc, char **argv)
202 {
203 std::freopen("../tests/Subtask1-data/grader.in.25a", "r", stdin);
204
205 //std::freopen("saveit.out", "w", stdout);
206
207 assert(3 == scanf("%i%i%i", &nv, &ne, &c));
208
209 FOR(i,ne)
210 assert(2 == scanf("%i%i", v1+i, v2+i));
211
212 for (int i=0;i<c;i++)
213 for (int j=0;j<nv;j++)
214 scanf("%d",&h[i][j]);
215
216 encode(nv, c, ne, v1, v2);
217
218 decode(nv, c);
219
220 if (hcnt != c*nv)
221 {
222 exit(92);
223 }
224
225 printf("%s 1\n",nb<=16000000?"OK":"NO");
226 printf("%s 2\n",nb<=360000?"OK":"NO");
227 printf("%s 3\n",nb<=80000?"OK":"NO");
228 printf("%s 4\n",nb<=70000?"OK":"NO");
229 }
230
231 // ----------------- end grader ------------------------
232 /*
233 OK 1
234 OK 2
235 OK 3
236 OK 4
237
238 Process returned 0 (0x0) execution time : 0.344 s
239 Press any key to continue.
240 */

Listing 11.8.2: saveit-224546.cpp


1 // https://oj.uz/submission/224546
2
3 #include "grader.h"
4 #include "encoder.h"
5 #include "decoder.h"
6
7 #include<iostream>
8
9 #include <bits/stdc++.h>
10
11 using namespace std;
12
13 static int nv, ne, c;
14 static int curbit = 0;
15
16 static int v1[1234567], v2[1234567];
17 static int h[1000][1000], hcnt;
18 static int bits[16000000], nb;
19
20 #define FOR(i,n) for (int i = 0; i < (n); i++)
21
22 void encode_bit(int bit)
23 {
CAPITOLUL 11. IOI 2010 1177

24 bits[nb++] = bit;
25 }
26
27 int decode_bit()
28 {
29 if (curbit >= nb)
30 {
31 exit(92);
32 }
33 return bits[curbit++];
34 }
35
36 void hops(int a, int b, int d)
37 {
38 if (h[a][b] != d)
39 {
40 exit(92);
41 }
42 h[a][b] = -1;
43 hcnt++;
44 }
45
46 // -------------- encoder --------------------
47
48 int dst[44][1010];
49 vector<int> g[1010];
50 int par[1010];
51 int vis[1010];
52
53 void dfs(int v = 1)
54 {
55 vis[v] = 1;
56 for(auto i : g[v]) if(!vis[i])
57 {
58 par[i] = v; dfs(i);
59 }
60 }
61
62 void bfs(int st)
63 {
64 dst[st][st] = 0;
65 queue<int> q; q.push(st);
66 while(q.size())
67 {
68 int now = q.front(); q.pop();
69 for(auto nxt : g[now]) if(dst[st][nxt] == -1)
70 {
71 dst[st][nxt] = dst[st][now] + 1;
72 q.push(nxt);
73 }
74 }
75 }
76
77 void encode(int N, int H, int M, int *v1, int *v2)
78 {
79 int n = N, m = M, h = H;
80 for(int i=0; i<m; i++)
81 {
82 int s = v1[i] + 1, e = v2[i] + 1;
83 g[s].push_back(e); g[e].push_back(s);
84 }
85
86 dfs();
87
88 for(int i=2; i<=n; i++)
89 {
90 for(int k=0; k<10; k++) encode_bit(!!(par[i] & (1 << k)));
91 }
92
93 memset(dst, -1, sizeof dst);
94 for(int i=1; i<=h; i++) bfs(i);
95
96 for(int i=1; i<=h; i++) for(int j=2; j<=n; j+=5)
97 {
98 int t = 0;
99 for(int k=0; k<5; k++)
CAPITOLUL 11. IOI 2010 1178

100 {
101 int now = dst[i][j+k] - dst[i][par[j+k]] + 1;
102 if(j + k > n) now = 0;
103 t = t * 3 + now;
104 }
105
106 for(int k=0; k<8; k++)
107 encode_bit(!!(t & (1 << k)));
108 }
109 }
110
111 // -------------- decoder --------------------
112
113 /*
114 dst[i][j] - dst[i][par[j]]
115 1 : 0
116 -1 : 11
117 0 : 10
118 */
119
120 int p[1010];
121 int mm[44][1010];
122 int chk[1010];
123 int ans[44][1010];
124
125 void go(int hh, int v)
126 {
127 if(v == 1) return;
128 if(!chk[p[v]]) go(hh, p[v]);
129 ans[hh][v] = ans[hh][p[v]] + mm[hh][v];
130 chk[v] = 1;
131 }
132
133 void decode(int N, int H)
134 {
135 int n = N, h = H;
136 for(int i=2; i<=n; i++)
137 {
138 for(int j=0; j<10; j++) p[i] += (decode_bit() << j);
139 }
140
141 for(int i=1; i<=h; i++) for(int j=2; j<=n; j+=5)
142 {
143 int ret = 0;
144 for(int k=0; k<8; k++)
145 ret += (decode_bit() << k);
146 for(int k=4; k>=0; k--)
147 {
148 mm[i][j+k] = ret % 3 - 1;
149 ret /= 3;
150 }
151 }
152
153 for(int i=1; i<=h; i++)
154 {
155 memset(chk, 0, sizeof chk);
156 chk[1] = 1;
157 for(int j=2; j<=n; j++) go(i, j);
158 }
159
160 for(int i=1; i<=h; i++)
161 for(int j=1; j<=n; j++)
162 hops(i-1, j-1, ans[i][j] - ans[i][i]);
163 }
164
165 // ----------------- begin grader ------------------------
166
167 /* and here is the driver */
168
169 int main(int argc, char **argv)
170 {
171 std::freopen("../tests/Subtask1-data/grader.in.25a", "r", stdin);
172
173 //std::freopen("saveit.out", "w", stdout);
174
175 assert(3 == scanf("%i%i%i", &nv, &ne, &c));
CAPITOLUL 11. IOI 2010 1179

176
177 FOR(i,ne)
178 assert(2 == scanf("%i%i", v1+i, v2+i));
179
180 for (int i=0;i<c;i++)
181 for (int j=0;j<nv;j++)
182 scanf("%d",&h[i][j]);
183
184 encode(nv, c, ne, v1, v2);
185
186 decode(nv, c);
187
188 if (hcnt != c*nv)
189 {
190 exit(92);
191 }
192
193 printf("%s 1\n",nb<=16000000?"OK":"NO");
194 printf("%s 2\n",nb<=360000?"OK":"NO");
195 printf("%s 3\n",nb<=80000?"OK":"NO");
196 printf("%s 4\n",nb<=70000?"OK":"NO");
197 }
198
199 // ----------------- end grader ------------------------
200 /*
201 OK 1
202 OK 2
203 OK 3
204 OK 4
205
206 Process returned 0 (0x0) execution time : 0.398 s
207 Press any key to continue.
208 */

11.8.3 *Rezolvare detaliat 


1180
Anexa A

Secvenµe de cod - utile

A.1 ...

A.1.1 ...

1181
Anexa B

Formule - utile

B.1 ...

B.1.1 ...

1182
Glosar

¯, 913 BST, 225


::, 282, 491, 959 bucket sort, 1138, 1139
::N = n;, 590, 715
::n = n;, 583, 596, 807 calcpt, 1117
[&](){}, 469 calloc, 847
[&](...){...}, 587 cartesian tree, 362
#include <bits/stdc++.h>, 769 ceil, 545
, 509 cerr, 462, 491
__typeof, 491 char* argv[], 389
_readInt(), 741, 766 checker, 389
\, 491 chrono, 929
0x3f3f3f3f, 807 count(), 929
1LL  15, 469 now(), 929
1e18, 583 steady_clock, 929
time_since_epoch(), 929
binary search, 688 cin.tie, 387
class, 763
accumulate, 545 clear(), 45, 477, 809
adun, 1117 comp, 1117
and, 750 complete graph, 856
assign, 282, 359, 618 connected components, 26
auto, 469, 491, 811 connected subgraph, 161
auto&, 753, 809 const, 387, 405
constexpr, 359
backtracking, 856 convex function, 687
backward edges, 161 Convex Hull Trick optimization, 687
balanced binary tree, 320 copy, 45, 750
balanced binary trees, 924 cut vertex, 161
begin(), 876 cycle, 26, 161
BFS, 5, 1004
BFS tree, 343 decision tree, 94
BigNum, 1112 decode, 1117
binary range tree, 1034 dene, 359
binary search, 343, 759, 785, 939, 1071, degree
1092, 1138 out-degree, 554
binary tree, 117, 845 depth-rst search, 815
bipartite graph, 857 depth-rst-search, 362
maximum matching, 857 deque, 587
minimum vertex cover, 857 devide and conquer, 460
bit mask, 553 DFS, 5, 117, 161
bitset, 509, 539 dfs, 305, 387, 431, 857
bool, 462, 469 Dijkstra, 225
bool operator, 1018 Dilworth's theorem, 205
bool operator <, 546, 558, 564 directed graph, 4
breadth rst search, 516 divide and conquer, 642
bridge, 815 Divide and Conquer optimization, 687
brute force, 516, 759, 1138 divide-and-conquer, 95, 1060
brute-force, 94 divide-and-conquer approach, 879
bsearch, 1015 division with remainder, 81

1183
GLOSAR 1184

doubling, 295 max_element, 462, 874


dynamic programming, 81, 182, 518, 539, median, 59
553, 611, 686 memoization, 518
memset, 408, 477, 514, 561, 807, 959
empty(), 387, 769, 809 merge, 362, 405, 412
encode, 1117 min_element, 874
end(), 876 minimum spanning tree, 554
erase, 387, 565, 876 mt19937, 929
Euler cycle, 554 mult, 1117
Euler-Tour, 295
next_permutation, 750
Fenwick tree, 144, 182 nth_element, 69
ll, 305, 359
nd, 467 operator, 412
xed, 491 overhead, 94
oor, 545
Floyd-Warshall, 1004 pair, 76, 387, 431, 480, 509, 766, 847
Flyod-Warshall, 4 path, 161
front(), 387 path from x to y , 5
function, 45, 886 permutations, 553
lambda, 886 post-order, 362
postorder, 1080
greedy, 60, 144, 539, 611, 758, 1091 power(...), 874
greedy coin change algorithm, 81 pre-compute, 4
Greedy method, 857 pre-processing, 686
guess-and-verify algorithm, 1071 precompute, 1092
prex sum, 3
Hall theorem, 759 preprocess, 183
hash table, 82 priority queue, 60, 758, 939, 1023, 1061
heap, 1081, 1138 priority_queue, 76, 1087
emplace, 76
I.d.k.: pop(), 76
I don't know who the author is., v top(), 76
in-degree, 554
independent set, 857 queue, 359, 387, 509, 526
complement, 857
inline, 387, 405, 412, 469, 590 rand(), 469, 477
insert, 565 Range Minimum Queries, 361
int64_t, 561, 565 range minimum query, 3
iota, 45, 305, 590 range tree, 1138
rbegin(), 387
Kahn's algorithm, 4 resize, 359, 387, 462, 811, 884
Knuth's optimization, 687 unique, 884
Konig's theorem, 857 reverse, 618
reverse(...), 615
L, 343 RMQ, 772
lambda, 45 rmq, 1044
lazy propagation, 3, 362 root of the tree, 117
segment tree, 271
lazy-update technique, 845 scad, 1117
lazy_propagation, 378 segment tree, 3, 4, 144, 271, 295, 361, 758,
lca, 500 759, 772, 845, 878, 924, 956
Li Chao tree, 378 SegTree, 849
linear interpolation, 688 selection sort, 784
linked list, 815 set, 387, 467, 482, 509, 564, 750, 772, 1092
linked-list, 225 extra factor, 1092
lower envelope, 687 setprecision, 491
lower_bound, 763, 769, 884 setvbuf, 1013, 1031
lowest-common-ancestor, 362 _IOFBF, 1013, 1031
size(), 809, 876
malloc, 766, 1013, 1031 sizeof, 408, 477, 769
map, 84, 100, 108, 472, 480, 560, 809, 871 sort, 305, 539, 590, 686
GLOSAR 1185

[&](...){...}, 590, 603 template, 387, 431


lambda, 305 testlib.h, 389, 621, 644
spanning tree, 161 topological sort, 4
spanning trees, 485 trage cu tunul, iv
srand(time(NULL));, 469 transitive closure, 4
stack, 182, 362, 687, 766 travelling salesman problem, 554
static, 469 tree, 26, 117
static const, 387 trie, 993
STL maps, 182 typedef, 387, 408, 431
string, 104
struct, 45, 305, 387, 405, 412, 546, 564, 750, unique, 565, 811, 874, 876
769 unordered_map, 491
edges, 305 unsigned, 522
graph, 305 upper_bound, 480, 769
swap, 590, 809 using, 359
sweep line algorithm, 295
sync_with_stdio, 387
vector<queue<int, 69
Tarjan's algorithm, 505 vertex cover, 857
Bibliograe
[1] Aho, A., Hopcroft, J., Ullman, J.D.; Data strutures and algorithms, Addison Wesley, 1983

[2] Andreica M.I.; Elemente de algoritmic  - probleme ³i soluµii, Cibernetica MC, 2011

[3] Andonie R., Gârbacea I.; Algoritmi fundamentali, o perspectiv  C++, Ed. Libris, 1995

[4] Atanasiu, A.; Concursuri de informatic . Editura Petrion, 1995

[5] Bell D., Perr M.; Java for Students, Second Edition, Prentice Hall, 1999

[6] Calude C.; Teoria algoritmilor, Ed. Universit µii Bucure³ti, 1987

[7] Cerchez, E., “erban, M.; Informatic  - manual pentru clasa a X-a., Ed. Polirom, 2000

[8] Cerchez, E.; Informatic  - Culegere de probleme pentru liceu, Ed. Polirom, 2002

[9] Cerchez, E., “erban, M.; Programarea în limbajul C/C++ pentru liceu, Ed. Polirom, 2005

[10] Cori, R.; Lévy, J.J.; Algorithmes et Programmation, Polycopié, version 1.6;
http://w3.edu.polytechnique.fr/informatique/
[11] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Introducere în Algoritmi, Ed Agora, 2000

[12] Cormen, T.H., Leiserson C.E., Rivest, R.L.; Pseudo-Code Language, 1994

[13] Cristea, V.; Giumale, C.; Kalisz, E.; Paunoiu, Al.; Limbajul C standard, Ed. Teora, Bucure³ti,
1992

[14] Erickson J.; Combinatorial Algorithms; http://www.uiuc.edu/~jeffe/

[15] Flanagan, D.; Java in a Nutshell, O'Reilly, 1997.

[16] Giumale C., Negreanu L., C linoiu S.; Proiectarea ³i analiza algoritmilor. Algoritmi de sortare,
Ed. All, 1997

[17] Halim S., Halim F., Competitive programming, 2013

[18] Knuth, D.E.; Arta program rii calculatoarelor, vol. 1: Algoritmi fundamentali, Ed. Teora,
1999.

[19] Knuth, D.E.; Arta programarii calculatoarelor, vol. 2: Algoritmi seminumerici, Ed. Teora,
2000.

[20] Knuth, D.E.; Arta programarii calculatoarelor, vol. 3: Sortare ³i c utare, Ed. Teora, 2001.

[21] Knuth, D.E.; The art of computer programming, vol. 4A: Combinatorial algorithms, Part 1,
Addison Wesley, 2011.

[22] Lambert,K. A., Osborne,M.; Java. A Framework for Programming and Problem Solving,
PWS Publishing, 1999

[23] Laaksonen A.; Guide to competitive programming, Springer, 2017

[24] Livovschi, L.; Georgescu H.; Analiza ³i sinteza algoritmilor. Ed. Enciclopedic , Bucure³ti,
1986.

[25] Niemeyer, P., Peck J.; Exploring Java, O'Reilly, 1997.

1186
BIBLIOGRAFIE 1187

[26] Od gescu, I., Smeureanu, I., “tef nescu, I.; Programarea avansat  a calculatoarelor personale,
Ed. Militar , Bucure³ti 1993

[27] Od gescu, I.; Metode ³i tehnici de programare, Ed. Computer Lobris Agora, Cluj, 1998

[28] Popescu Anastasiu, D.; Puncte de articulaµie ³i punµi în grafuri, Gazeta de Informatic  nr.
5/1993

[29] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/


2007/Info/Lista_probleme_2000-2007.pdf
[30] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/
2007/Info/Rezolvari_C09.pdf

[31] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/


2007/Info/Rezolvari_C10.pdf
[32] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/
2007/Info/Rezolvari_C11.pdf

[33] R bâea, A.; https://math.univ-ovidius.ro/Doc/Admitere/CentruPregatire/


2007/Info/Rezolvari_Baraj.pdf
[34] Skiena S.S., Revilla M.A.; Programming challenges - The Programming Contest Training
Manual, Springer, 2003

[35] Tomescu, I.; Probleme de combinatoric  ³i teoria grafurilor, Editura Didactic  ³i Pedagogic ,
Bucure³ti, 1981

[36] Tomescu, I.; Leu, A.; Matematic  aplicat  în tehnica de calcul, Editura Didactic  ³i Pedago-
gic , Bucure³ti, 1982

[37] Tudor, S.; Informatic  - prolul real intensiv, varianta C++; Editura L&S, Bucure³ti, 2004

[38] Tudor, S.; Hutanu, V,; Informatic  intensiv; Editura L&S, Bucure³ti, 2006

[39] V duva, C.M.; Programarea in JAVA. Microinformatica, 1999

[40] Vi³inescu, R.; Vi³inescu, V.; Programare dinamic  - teorie ³i aplicaµii; GInfo nr. 15/4 2005

[41] Vlada, M.; Conceptul de algoritm - abordare modern , GInfo, 13/2,3 2003

[42] Vlada, M.; Grafuri neorientate ³i aplicaµii. Gazeta de Informatic , 1993

[43] Vlada, M.; Gândirea Algoritmic  - O Filosoe Modern  a Matematicii ³i Informaticii, CNIV-
2003, Editura Universit µii din Bucure³ti, 2003

[44] Weis, M.A.; Data structures and Algorithm Analysis, Ed. The Benjamin/Cummings Pu-
blishing Company. Inc., Redwoods City, California, 1995.

[45] Winston, P.H., Narasimhan, S.; On to JAVA, Addison-Wesley, 1996

[46] Wirth N.; Algorithms + Data Structures = Programs, Prentice Hall, Inc 1976

[47] *** - Gazeta de Informatic , Editura Libris, 1991-2005

[48] *** - https://github.com/DinuCr/CS/blob/master/Info/stuff%20stuff/Re


zolvari_C09.pdf
[49] *** - https://dokumen.tips/documents/rezolvaric09.html

[50] *** - https://www.scribd.com/doc/266218102/Rezolvari-C09

[51] *** - https://www.scribd.com/document/396362669/Rezolvari-C10

[52] *** - https://www.scribd.com/document/344769195/Rezolvari-C11

[53] *** - https://www.scribd.com/document/364077679/Rezolvari-C11-pdf

[54] *** - https://needoc.net/rezolvari-c11-pdf


BIBLIOGRAFIE 1188

[55] *** - https://vdocumente.com/algoritmi-i-structuri-de-date.html

[56] *** - https://pdfslide.net/documents/algoritmi-si-structuri-de-date-


1-note-de-cuprins-1-oji-2002-clasa-a-ix-a-1-11.html
[57] *** - https://www.infoarena.ro/ciorna

[58] *** - https://infoarena.ro/olimpici

[59] *** - https://www.infogim.ro/

[60] *** - https://www.pbinfo.ro/

[61] *** - http://www.cplusplus.com/

[62] *** - http://www.cplusplus.com/doc/tutorial/operators/

[63] *** - http://www.info1cup.com/

[64] *** - http://www.olimpiada.info/

[65] *** - http://www.usaco.org/

[66] *** - http://algopedia.ro/

[67] *** - http://campion.edu.ro/

[68] *** - http://varena.ro/

[69] *** - http://rmi.lbi.ro/rmi_2019/

[70] *** - https://codeforces.com/

[71] *** - https://cpbook.net/

[72] *** - https://csacademy.com/

[73] *** - https://gazeta.info.ro/revigoram-ginfo/

[74] *** - https://oj.uz/problems/source/22

[75] *** - https://profs.info.uaic.ro/~infogim/2019/index.html

[76] *** - https://wandbox.org/

[77] *** - https://en.cppreference.com/w/cpp/language/operator_alternative

[78] *** - https://en.cppreference.com/w/cpp/algorithm

[79] *** - https://www.ejoi2019.si/

[80] *** - https://en.wikipedia.org/wiki/Algorithm

[81] *** - https://en.wikipedia.org/wiki/List_of_algorithms

[82] *** - https://en.wikipedia.org/wiki/List_of_data_structures


Lista autorilor
problemelor ³i indicaµiilor

Adam Karczmarz, 757 Kento Nikaido, 552


Aleksandar Ili¢, 401, 1002
Alireza Farhadi, 159 Ling Yan Hao, 23
Amaury Pouly, 893 Luke Harrison, 1032
Ammar Fathin Sabili, 254
Andreja Ili¢, 1002 Mansur Kutybayev, 770
Angus Ritossa, 91 Martin Fixman, 1058
Arthur Charguéraud, 893 Michal Forisek, 379, 608, 863, 965, 969,
1167, 1170
Bang Ye Wu, 800 Mihai P tra³cu, 1078, 1089, 1170, 1172
Bartosz Tarnawski, 842 Mikhail Pyaderkin, 269
Bruce Merry, 991 Mikhail Tikhomirov, 79, 114
Mohammad Roghani, 292
Chethiya Abeysinghe, 684 Monika Steinová, 379, 733, 921, 1167, 1170
Christian Kauth, 1069
Christopher Chen, 1137, 1138 Nir Lavee, 813
Normunds Vilcins, 1046
Daniel Graf, 514
Danylo Mysak, 142, 215 Peyman Jabbarzade, 180

Ranald Lam Yun Shao, 23


Eryk Kopczynski, 743
Richard Královic, 1018
Gleb Evstropov, 566 Richard Peng, 952
Gordon Cormack, 1118, 1119, 1124, 1144, Riku Kawasaki, 223, 359
1146, 1154, 1155
Saeed Seddighin, 159, 428, 483
Hamed Valizadeh, 458 Shi-Chun Tsai, 537, 538, 639
Helia Ziaei, 292 Shogo Murai, 340
Sun-Yuan Hsieh, 854
Jakub Lacki, 203
Tomasz Idziaszek, 203, 316
Jakub Lacki, Poland, 876
Jittat Fakcharoenphol, 1101 Vytautas Gruslys, 821, 936
Jo Sunghyeon, 1
John Dethridge, 891 Weidong Hu, 782
Jonathan Moshei, 813
Jorge Bernadas, 1161, 1162 Xiao Mao, 57

Kazuhiro Hosaka, 907 Zhenting Zhu, 94

1189

S-ar putea să vă placă și