[Решења задатака] 2023/2024 Квалификације - први круг

Три тајна броја

Аутор: Владимир Миловановић
Текст и тест примери: Алекса Милисављевић
Анализа решења: Алекса Милисављевић
Тестирање: Александар Вишњић

Решење када 0 = X = Y < Z

Седам датих бројева су X, Y, Z, X+Y, X+Z, Y+Z и$X+Y+Z$. Како је X = 0 и Y=0, то су ових седам вредности заправо 0, 0, Z, 0, Z, Z и Z. За решавање овог подзадатка, довољно је пронаћи неку вредност која није нула (та вредност мора бити једнака Z) и исписати две 0 (тј. X и Y) и ту вредност (тј. Z).

Решење када 0 \leq X \leq Y \leq Z < 100

У овом случају можемо итерирати по свим могућим вредностима за X, Y и Z. Укупно постоји до 10^6 могућности за X, Y и Z. Потом је довољно да проверимо да ли скуп вредности X, Y, Z, X+Y, X+Z, Y+Z и$X+Y+Z$ за изабрано X, Y и Z одговара вредностима са улаза. Ово можемо постићи на пример сортирањем оба скупа и поређењем сваке вредности.

Главно решење

За главно решење, довољно је да приметимо да је најмања вредност са улаза управо једнака X. Заиста, важи да је X \leq Y, Z, X+Y, X+Z, Y+Z, X+Y+Z. Слично томе, друга најмања вредност је једнака Y, јер Y \leq Z, X+Y, X+Z, Y+Z, X+Y+Z. Коначно, највећа вредност са улаза мора бити једнака једнака X+Y+Z, јер X+Y+Z \geq X,Y,Z, X+Y, X+Z, Y+Z, X+Y+Z. Дакле довољно је пронаћи две најмање вредности (оне редом одговарају X и Y), и одузети их од највеће вредности (што одговара (X+Y+Z) - X - Y = Z).

У претпоследњем подзадатку, низ је дат сортиран и две најмање вредности одговарају прва два елемента низа, док највећа вредност одговара последњем елементу низа. За последњи задатак, можемо да сортирамо низ или да проласком кроз њега одредимо ове вредности.

Морамо да рециклирамо

Аутор: Александар Вишњић
Текст и тест примери: Алекса Милисављевић
Анализа решења: Александар Вишњић
Тестирање: Александар Вишњић

Прва два подзадатка

Извршавамо директну симулацију свих упита. Нека су k и x тренутни параметри упита. Уколико је A_k + x \leq C_k, тада једноставно додајемо x на A_k и завршавамо упит. У супротном, x смањујемо за C_k-A_k, а на вредност A_k уписујемо C_k. Потом повећавамо k за 1 и настављамо извршавање упита са новим вредностима. Уколико изађемо из низа, свакако прекидамо извршавање упита.

Временска сложеност је O(QN), јер се може десити да приликом извршавања упита прођемо кроз целу дужину низа.

Главно решење

Приметимо да редослед извршавања упита није битан. То значи да можемо прво учитати све упите, а потом их одједном све извршити. Тачније, за сваки упит са параметрима k и x најпре додамо x на A_k без вођења рачуна о C_k.

Када то урадимо за сваки упит, довољно је проћи једном по целом низу слева надесно и ажурирати вредности A_k. За тренутни индекс k, ако је A_k> C_k, додајемо A_k-C_k на A_{k+1}, а затим на вредност A_k уписујемо C_k. У супротном, не радимо ништа. Напоменимо да ако је k= n (крај низа), није потребно ажурирати A_{k+1}.

Сложеност проласка по свим упитима је O(Q), док је сложеност једног проласка по целом низу O(hN) Укупна сложеност је O(N+Q).

Преседања

Аутор: Павле Мартиновић
Текст и тест примери: Александар Вишњић
Анализа решења: Александар Вишњић
Тестирање: Павле Мартиновић

Први подзадатак

Све станице можемо сместити на бројевну праву. Коришћењем једне аутобуске линије у новом плану градског превоза можемо се кретати за 2 станице улево или за 2 станице удесно. Самим тим, решење задатка је \frac{|S-T|}{2}.

Други подзадатак

Оригиналан план градског превоза можемо да моделујемо као стабло. Стабло је повезан граф такав да између свака два чвора постоји јединствен несамопресецајући пут. Станице су у ствари чворови, док су аутобуске линије гране. У новом плану, између два чвора постоји грана ако и само ако је њихова удаљеност 2. Приметимо да граф новог плана не мора бити стабло.

Потребно је одредити дужину пута у оригиналном плану између чворова S и T, а затим је поделити са 2. Њу одређујемо применом алгоритма за претрагу графа попут DFS, односно претрагом у дубину. Ако је та дужина D, одговор на задатак је \frac{D}{2}.

Укупна сложеност је O(N).

Трећи подзадатак

Настављамо са језиком теорије графова. Раније смо приметили да у новом графу између два чвора постоји грана ако и само ако је у старом графу њихова удаљеност била 2. Да би сваки чвор који је у старом плану био достижан из S био и даље достижан, потребно је да у његовом графу постоји циклус непарне дужине. Како то није случај по услову подзадатка, можемо закључити да тај граф нема непаран циклус. Такав граф још називамо и бипартитивним. То значи да сваки чвор можемо обојити у две боје на начин да никоја два чворова исте боје нису повезани. Тражење најкраћег пута од S до T је праволинијски. Можемо применити BFS алгоритам директно, тј. претрагу у ширину. Ако алгоритам у оригиналном графу одреди дужину D, одговор на задатак је \frac{D}{2}.

Укупна сложеност је O(N+M).

Главно решење

Подребно је наставити у генералном графу који може имати непарне циклусе. Тражимо најкраћи пут парне дужине између чворова S и T. Потребно је да на неки начин не рачунамо путеве непарне дужине ка решењу, али да их у исто време не занемаримо при налажењу истог. Један начин да то учинимо је да за сваки чвор направимо и његов “непарни” парњак. Дакле, сваки чвор има “парну” и “непарну” верзију, тј. дуплирали смо га. Гране постоје само између чворова различите парности. Сада примећујемо да приликом прелажења из једног чвора у други мењамо тренутну парност чвора. Овим је јасно да је у оваквом графу довољно наћи пут од “парног” S до “парног” T, јер од парности последњег чвора у коме смо завршили зависи и парност пута.

Укупна сложеност је O(N+M).

1 Like

Већа Трка

Аутор: Павле Мартиновић
Текст и тест примери: Павле Мартиновић
Анализа решења: Алекса Милисављевић
Тестирање: Алекса Милисављевић

Решење када N=2

У овом подзадатку, може се десити највише једно претицање. Како је 1 \leq K, гарантује се да ће се десити тачно једно претицање. То претицање ће се десити управо у моменту \frac{x_2-x_1}{v_1-v_2}.

Решење када N \leq 2.000

У овом подзадатку, можемо да посматрамо скуп свих претицања која ће се десити. За свако i<j за које v_i > v_j, важи да ће i претећи j у моменту \frac{x_j-x_i}{v_i-v_j}. Потом можемо да сортирамо све ове вредности и узмемо K-ту најмању вредност. Временска сложеност овог решења је O(N^2 \log N).

Решење када K = 1

Приметимо да се прво претицање мора десити између два суседна тркача. Дакле, довољно је да за свако 1 \leq i < N за које v_i > v_{i+1} израчунамо када ће тркач i претећи тркача i+1. То ће се десити у моменту \frac{x_{i+1}-x_i}{v_i - v_{i+1}}. Треба да испишемо најранији такав моменат. Временска сложеност овог решења је O(N).

Решење када K \leq 300.000

Овај подзадатак захтева генерализацију претходног подзадатка. Знамо да се прво претицање мора десити између два суседна тркача. Да би решили овај подзадатак, потребно је да симулирамо првих K претицања. Ово можемо постићи тако што у некој структури (рецимо set или priority_queue) чувамо моменте када ће неки тркач престићи оног који је тренутно непосредно испред њега (поред момента у којем ће се ово десити, потребно је да чувамо и позицију тркача на којег се то претицање односи). Пронаћемо најранији такав моменат у структури и заменимо редослед та два тркача. Затим убацимо претицања која ће се десити између тркача који су постали суседни након ове размене. Временска сложеност овог решења је O(K \log N).

Главно решење

Применићемо бинарну претрагу по решењу. Сада се задатак своди на следеће питање: “За дато t, колико претицања се десило до тренутка t?”. Прво ћемо пронаћи позицију сваког тркача после t секунди. Претпоставимо, за почетак, да су те позиције јединствене, тј. да се два различита тркача неће наћи на истој позицији после t секунди. Означимо позицију тркача i са y_i. Приметимо да је тркач i претекао све оне тркаче j>i за које важи y_j < y_i. Сортирајмо сада тркаче према њиховим новим позицијама. Означимо са a_i тркача чији индекс одговара i-тој најмањој позицији у низу y. Дакле, тркач a_i је претекао све тркаче a_j за које важи j<i и a_j>a_i. Тј. број претицања је једнак броју парова j<i таквих да a_j > a_i. Ово је познато као проблем пребројавања инверзија у низу. Овај проблем најлакше можемо решити користећи се Фенвиковим стаблом (тј bit-ом) или сегментним стаблом. Пролазимо кроз низ с десна на лево и за дато i на решење додамо број елемената у Фенвиковом стаблу који је мањи од a_i и потом додамо a_i у Фенвиково стабло. Алтернативно, овај проблем се може решити и симулацијом merge sort процедуре.

Требало би анализирати и шта се дешава у случају да су неке позиције након t секунди једнаке. То би значило да се нека претицања дешавају тачно након t секунди. У овом случају ћемо, при поређењу, редослед тркача са истим позицијама одредити на основу њихових оригиналних индекса. У општем случају, тиме ћемо добити број претицања који се десио пре строго момента t. Претпоставимо да се у моменту p десило K-то претицање. Дакле, алгоритам ће за моменат p вратити вредност која је мања од K. То значи да ћемо у бинарној претрази у наредном кораку посматрати вредности које су веће од p. Међутим, за свако t>p, алгоритам ће вратити вредност која је већа или једнака од K. Након неколико итерација бинарне претраге, алгоритам ће стати и исписати вредност p + \varepsilon, што је довољно добро, јер решење треба одредити са грешком до 10^{-4}.

Коначно, прокоментаришимо и саму имплементацију бинарне претраге над бројевима који нису цели. У овом случају, бинарну претрагу можемо имплементирати тако што поредимо разлику десне и леве границе, r-l, са неком малом вредношћу, нпр. 10^{-5}, што је у овом случају довољно добро. У општем случају, ово може да изазове проблеме са прецизношћу и боље је унапред фиксирати број итерација бинарне претраге. На пример 50 итерација гарантује да ће након завршетка бинарне претраге важити r-l \leq \frac{r_0-l_0}{2^{50}}, где су l_0 и r_0 почетне вредности леве и десне границе.

Фејк реклама

Аутор: Александар Вишњић
Текст и тест примери: Алекса Милисављевић
Анализа решења: Александар Вишњић
Тестирање: Димитрије Ердељан и Александар Вишњић

Решење за N\leq 20

Код сваке препреке имамо 2 избора, те рекурзивно решење даје сложеност 2^N,

Решење када m_i=1 за свако 1\leq i\leq N и N \leq 30

Користимо meet in the middle технику. За првих \lfloor \frac{N}{2} \rfloor препрека израчунамо све могуће величине војске (рекурзивно као у претходном подзадатку). Затим, за наредних \lceil \frac{N}{2} \rceil почнемо од неке неодређене величине војске x и симулирамо препреке за њу. Податке чувамо у следећем формату:

  • Ако је почетно x у интервалу [y,z], онда је x+s могућа тренутна величина војске.

Наравно, ових ставки може имати произвољно много, те их чувамо у некој колекцији. Та колекција може бити празна. На почетку та колекција садржи само једну ставку - ону за почетно x и ограничење [-10^9,10^9]

Када пролазимо кроз препреке, због услова m_i=1, имамо два избора:

  • Не радимо ништа, и додајемо ново ограничење за интервал за почетно x на сваку ставку;
  • За сваку почетну ставку, направимо копију и симулирамо додавање a_i на x. Памтимо инкремент, а потом додајемо ново ограничење за почетно x у облику интервала.

При сваком кораку величина колекције не порасте за више од дупло. Крајња величина није већа од 2^N.

Када након извшавања прве половине сазнамо свако могуће x, а након извршавања друге половине добијемо све могуће ставке, можемо их спојити и израчунати највећу могућу величину војске након свих прођених препрека.

Меморијска сложеност је O(2^{N/2}), док је временска O(2^{N/2} \cdot N) због сортирања и бинарне претраге.

Решење када N \leq 30

Слично као у претходном подзадатку, користимо технику meet in the middle. Проблем настаје при ажурирању “*m_i”. У првом делу можемо рекурзивно пронаћи свако могуће x. Али, у другом делу више немамо једноставне ставке. Оне сада постају компликованије функције од x. Ипак, испоставља се да су те функције линеарне. На почетку имамо функцију f(x)=x. Она је очито линеарна. Претпоставимо да пре неке препреке имамо колекцију линеарних функција. Посматрајмо једну од њих, нека је то f(x)=p x + q. За модификовање ње имамо две опције:

  • f(x)=p m_i x + q m_i
  • f(x) = px + q + a_i

Примећујемо да при оба избора, наша функција заиста остаје линеарна. Међутим, додавање нових ограничења за сегмент на оригинално x је нешто компликованије, али изводиво. Можемо целу ставку замислити као функцију са ограниченим доменом (који ће бити неки сегмент).

На крају, из прве половине имамо свако могуће x, а из друге половине имамо сваку могућу линеарну функцију са одговарајућим доменом. Спајање можемо извршити тако што фиксирамо функцију са њеним доменом [l,r]. Приметимо да линеарна функција достиже максимум у крајевима, тј. да тражимо x да буде што ближе “већем крају”. Овим скраћујемо могућност на највише једно могуће x. Сада једноставно прођемо по свакој функцији и евалуирамо је у њеном оптималном x.

Битно је напоменути да се може десити да коефицијенти неке функције изађу из опсега типа long long. Таква је, на пример, функција f(x)=10^{100}x -10^{100}. Она стаје у ограничење [-10^9,10^9] за тачку x=1. Међутим, приметимо да такве функције чији су коефицијенти већи од 10^{18} (по апсолутној вредности) стају у ограничење [l_i,r_i] само у једној тачки. Пошто је домен те функције у ствари једна тачка, довољно је “компресовати” је. Нека је (x_e,y_e=f(x_e)) та тачка (у општем случају “превелике” функције). Њу можемо описати и на следећи начин: f(x) = 0x + y_e. Она очито има коефицијенте у опсегу [-10^9,10^9], те стаје у long long.

Меморијска сложеност је O(2^{N/2}), док је временска O(2^{N/2} \cdot N) због сортирања и бинарне претраге.

Решење када N \leq 40

Користимо идеје из претходног подзадатка. Потребно је смањити временску сложеност на O(2^{N/2}). Приметимо две чињенице:

  • Алгоритам бинарне претраге може се заменити са техником два показивача
  • Сортирање параметара је могуће вршити у O(2^{N/2}) временској сложености.

Јасно је да ако докажемо и изведемо другу чињеницу, укупна сложеност биће O(2^{N/2}).

Замислимо да имамо тренутни низ x_1,x_2,\ldots ,x_k (у првом делу meet in the middle алгоритма ) и да њега ажурирамо новим операцијама “*m” и “+a”. Познато је да ако је почетни низ сортиран, тада су и низови m\cdot x_1,m\cdot x_2, \ldots, m\cdot x_k; a+x_1, a+x_2, \ldots ,a+x_k. (потенцијално у обрнутом редоследу). Сада можемо применити алгоритам налик merge sort . Временска сложеност овог алгоритма сортирања заиста јесте O(2^{N/2}).

Функције слично сортирамо када их припремамо за технику два показивача. Међутим, потребно их је филтрирати у две групе. Једну групу сортирамо по левом крају домена (оне функције са негативним коефицијентом уз x), а другу групу по десном крају домена (оне функције са ненегативним коефицијентом уз x).