[Решења задатака] 2023/2024 Државно

Б1: Неоподниз

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

Решење када N \leq 5, K = 2

За сваки префикс, можемо пробати прво све могуће ниске дужине 1, па све могуће ниске дужине 2, итд. док не нађемо решење.

Проверу за конкретну ниску T можемо урадити тако што пролазимо слева надесно кроз префикс ниске S и упарујемо редом карактере чим наиђемо на њих. Уколико смо пронашли све карактере (истим редом као у T), онда јесте подниз те ниске, у супротном није.

Временска сложеност је O(K^N \cdot N^2), а меморијска O(N).

Решење када је ниска уређена лексикографски

Приметимо да уколико префикс ниске S не садржи свих K различитих карактера, одговор ће бити 1 (ниска која се састоји само од карактера који се ту не налази).

Пошто је ниска уређена лексикографски, постојаће неко x тако да је првих x одговора 1 (јер још нису додати сви карактери), а осталих N-x одговора биће 2 - нпр. ниска ba, која се свакако не појављује у нисци, пошто није уређена лексикографски.

Временска и меморијска сложеност је O(N).

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

Слично обзервацији из претходног подзадатка, оптимално је да први карактер нашег решења за неки префикс, буде карактер који се први пут појављује што касније - до тог карактера сви резултати биће 1.

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

Ово размишљање настављамо да примењујемо док не одредимо све резултате.

Временска сложеност је O(N^2), а меморијска O(N).

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

Решење за претходни подзадатак се може убрзати тако што ћемо чувати матрицу димензија K \times N, где за сваку позицију и сваки карактер чувамо када ће се следећи пут појавити. Ово можемо једноставно израчунати, кренувши уназад кроз ниску S и памтећи кад смо последњи пут видели сваки од карактера.

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

Временска и меморијска сложеност је O(NK).

Б2: Домски Слетач

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

NM \leq 20

У овом подзадатку је довољно изгенерисати све могуће путеве (нпр. бектрекинг алгоритмом) и проверити за свако поље колико су неистомишљеници удаљени од тог пута. На крају само исписујемо минимум свих збирних удаљености.

N=2

У овом подзадатку је кључно приметити да тиме што посматрана матрица има два реда, Домски Слетач ће се само једном спустити у други (нижи) ред. Нека то буде из поља (1, X) у поље (2, X). Како неистомишљеници могу такође да се крећу само доле и десно, сви неистомишљеници из првог реда десно од поља (1,X), ће морати да се спусте једно поље наниже. Са друге стране, неистомишљеници из другог реда лево од поља (2,X), ће морати да иду на десну страну до поља (2, X). Коначна сума за фиксирано X биће \sum_{j=X+1}^M a_{1j} + \sum_{j=1}^{X-1} \left( a_{2j}\cdot (X-j) \right). Множење са (X-j) заправо одређује колико путању ће прећи неистомишљеници из другог реда. Итерираћемо по X, а тражене суме можемо да израчунамо пре тога како не бисмо добили квадратну сложеност. Коначно, временска сложеност овог приступа је \mathcal{O}(M).

NM \leq 10^4

Приметимо да се у прошлом подзадатку сваки неистомишљеник кретао само доле или само десно. Ако уопштимо закључак добијамо да ако фиксирамо путању Домског Слетача и уколико се неистомишљеник налази испод ње, најближе поље путање ће управо бити оно које је прво гледајући само надесно. Слично, неистомишљеницима који су изнад путање је оптимално да се спусте само доле до првог поља путање. Имајући у обзир да и Домски Слетач и неистомишљеници крећу само доле и десно, може се лако показати да су им то најближа поља.

Уз овај закључак можемо да формирамо стање динамичког програмирања: dp[i][j] нам представља одговор на поставку задатка али за подматрицу до i-тог реда и j-те колоне (колика је минимална дистанца коју сви неистомишљеници из ове подматрице морају да пређу да би стигли до путање Домског Слетача који се креће од поља (1,1) до поља (i,j)). Јасно, у почетку је dp[1][x] = dp[y][1] = 0 за свако x=1,2,\ldots,M и y=1,2,\ldots,N. У поље (i,j) можемо доћи с горње стране, односно из поља (i-1,j), или с леве стране односно из поља (i,j-1). У првом случају минимална дистанца биће dp[i-1][j] + \sum_{k=1}^{j-1} \left( a_{ik}\cdot (j-k) \right), јер неистомишљеници из i-тог реда с леве стране од тренутног поља баш долазе у поље (i,j). Сличним резоновањем, у другом случају минимална дистанце биће. На крају, прелаз динамичког програмирања биће dp[i][j-1]+\sum_{k=1}^{i-1}\left( a_{kj}\cdot (i-k)\right). За dp[i][j] узимамо мању од ове две вредности. Коначно решење је dp[N][M]. Укупна временска сложеност овог алгоритма је \mathcal{O}(NM(N+M)).

Решење без додатних ограничења

Једино што нас спречава да достигнемо жељену сложеност \mathcal{O}(NM) у претходном решењу јесте споро рачунање прелаза за dp[i][j]. Приметимо да исто као и у другом подзадатку можемо да пре рачунања прелаза и стања додатне суме. Односно, израчунаћемо r[i][j] најмања дистанца да сви неистомишљеници из реда i а у колонама 1,2,\ldots,j-1 дођу до поља (i,j). Слично, c[i][j] најмања дистанца да сви неистомишљеници из колоне ј а у редовима 1,2,\ldots,i-1 дођу до поља (i,j). Прелазе рачунамо по формули r[i][j] = r[i][j-1] + \sum_{k=1}^{j-1}a_{ij} (посматрамо ово на следећи начин: као да су до суседног поља лево сви неистомишљеници дошли и онда сви заједно прешли за једно поље удесно се померили) и c[i][j] = c[i-1][j] + \sum_{k=1}^{i-1}a_{ij}. Приметимо да ове додатне суме нису ништа него префиксне суме по редовима и колонама, које можемо такође унапред да израчунамо и обележимо са pref\_r[i][j] и pref\_c[i][j]. Сада нам прелази изледају овако: r[i][j] = r[i][j-1] + pref\_r[i][j-1] и c[i][j] = c[i-1][j] + pref\_c[i-1][j]. Коначно, почетни прелаз из претходног подзадатка добија следећу форму dp[i][j] = \min(dp[i-1][j] + r[i][j], dp[i][j-1] + c[i][j]). Временска сложеност овог решења је \mathcal{O}(NM).

Напомена

У скоро свим подзадацима је потребно да се користи 64-битни тип података за рачунање, јер крајњи одговор може да буде знатно већи од 2\cdot 10^9.

Б3: Старући Прстен

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

Решење када P_i=0

Приметимо да ћемо у овом случају увек радити директан напад, тако да после k потеза ћемо нанети укупно S_k=D_1+D_2+\cdots+D_k поена штете. Овај низ је сортиран, те бинарном претрагом за сваки упит можемо да нађемо најмањи елемент S који је већи од траженог H_i. Сложеност је O(N+Q\log N).

Решење када D_i=0

У овом случају чемо увек радити напад отровом, тако да после k потеза ћемо нанети укупно S_k=k\cdot P_1+(k-1)\cdot P_2+\cdots+1\cdot P_k поена штете. Ако срачунамо овај низ, можемо завршити бинарном претрагом, као и у првом подзадатку. Ово се, међутим, лако може урадити тако што се узме парцијална сума парцијалних сума низа P_i. Сложеност је O(N+Q\log N).

Решење када D_i=0

У овом случају чемо увек радити напад отровом, тако да после k потеза ћемо нанети укупно S_k=k\cdot P_1+(k-1)\cdot P_2+\cdots+1\cdot P_k поена штете. Ако срачунамо овај низ, можемо завршити бинарном претрагом, као и у првом подзадатку. Ово се, међутим, лако може урадити тако што се узме парцијална сума парцијалних сума низа P_i. Сложеност је O(N+Q\log N).

Решење када је Q\leq 10

У даљим подзадацима, слично прва два, ћемо означити са S_k колико највише штете можемо нанети у првих k потеза. У овом подзадатку ћемо показти како се произвољно S_k може срачунати у O(N). Заиста, приметимо да ако знамо да ћемо радити тачно k потеза, онда у неком i-том потезу имамо избор да или радимо D_i штете директним нападом, или P_i отрова које ће трајати k-i+1 потеза, односно нанети укупно (k-i+1)P_i штете. Пошто је k фиксирано, потребно је само изабрати већу од ове две вредности. Сада радимо бинарну претрагу по S, али рачунајући конкретне вредности за S_i само кад нам требају (јер је низ свакако сортиран). Ово нам даје сложеност O(QN\log N).

Решење када нема упита првог типа

У овом случају можемо лако срачунати низ S_i. Заиста, због услова важи да једини потез кад је можда оптимално применити директан напад последњи пред убијање, те су нам једине опције за S_k или оно што је било у другом подзатаку, или k\cdot P_1+(k-1)\cdot P_2+\cdots+2\cdot P_{k-1}+D_k. Узимајући максимум од те две вредности, налазимо низ S_k, и можемо опет да завршимо бинарном претрагом као у прва два подзадатка. Сложеност O(N+Q\log N).

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

У овом решењу ћемо срачунати све вредности S_k у O(N), ослањајући се на опсервације из трећег подзадатка. Главно је видети да за свако i, како померамо k ће нам прво бити оптимално радити директан напад, па онда од k\ge i+\lfloor\frac{D_i}{P_i}\rfloor ће бити оптимално радити напад отровом. Сада померамо k и запамтимо “догађаје” облика од када је оптимално радити напад отровом. Када наиђемо на нови догађај прерачунамо вредност нанете штете до сад ако бисмо урадили напад отровом уместо директног напада у том потезу, и додамо тај отров на неки бројач тоталног отрова. Затим у сваком потезу додамо тотали отров и нађемо нову вредност низа S. Овиме смо срачунали низ S и можемо опет бинарном претрагом довршити задатак у O(N+Q\log N)

А1: И/или

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

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

Рекурзивно испробавање свих могућих примена операција осваја поене у овом подзадатку. Битно је оптимизовати ту рекурзију и не посетити ниједан низ више од двапут у истој команди. То се може остварити употребом скупа у коме чувамо све посећене низове.

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

Приметимо да примена операције A_i = A_i or A_{i+1}, \ A_{i+1} = A_i and A_{i+1} има тенденцију да “битове помера улево”. Поновљеном применом овог поступка, слично као у алгоритму “bubble sort”, можемо ефективно сортирати тренутни низ и наћи K-ти по реду елемент. Дословна груба имплементација доводи до решења другог подзадатка. Временска ложеност је O(QN^2).

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

Фиксирајмо неко 0 \leq i < 30. Приметимо да се након примене операције укупан број елемената низа који садрже “бит” 2^i не мења. Уочимо поступак из претходног подзадатка. Резултат је тај да се сви “битови” налазе “збијено лево”. Тачније, између сваке две јединице се такође налазе све јединице на одређеној месној вредности у бинарном запису.

Сада је довољно избројати сваки “бит”, и у зависности од K одредити да ли се он налази у K-том елементу крајњег низа A или не. Директна симулација команди доводи до временске сложености O(NQ\log M), где је M највећа вредност низа A .

Четврти подзадатак

Слично као у претходним подзадацима, бројимо колико се пута “бит” појављује у низу. У овом случају је то једноставније јер су елементи низа 0 и 1. Дакле, бројимо само број јединица. За команду типа 1, подешавамо бројач на нову одговарајућу вредност на основу промењеног елемента низа. За команду типа 0, дајемо одговор у зависности од ажурираног броја јединица. Временска сложеност је O(N).

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

Спајамо идеје из трећег и четвртог подзадатка. За сваки “бит” (од 0 до 29), бројимо колико се пута појављује у низу. За команду типа 1, подешавамо бројаче на одговарајуће нове вредности. За команду типа 0, дајемо одговор слично као у трећем подзадатку. Код сваке команде пролазимо по највише 30 битова. Ово даје решење временске сложености O((N+Q)\log M), где је M највећа вредност низа A.

A2: Кики

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

Почетна запажања

Пре свега, јасно је да морамо да користимо 64-битни тип података, јер решење може бити драстично веће од 2\cdot10^9.

Затим, какво стање Лименом дефинитивно не доноси победу? Приметимо да уколико је неки чвор обојен или има обојеног суседа, онда неће бити проблематичан - у смислу да је исфорсирано која боја ће морати да се појави на том чвору. И заправо, врло лако можемо да покажемо да уколико сви чворови испуњавају својство да су или обојени (било којом бојом) или је неки од њихових суседа обојени (поново било којом бојом), онда је то дефинитивно победничка позиција за Лименог. Наравно, лако се и показује да уколико ово не важи, постоји начин да Кики победи (обоји један од чворова који не задовољавају својство обрнуто од очекиване боје).

Шта нам је очекивана боја? Јасно је да уколико имамо неко решење где неке чворове бојимо плавом, а неке друге црвеном бојом, такође је и супротно бојење решење. Ако се претварамо да бојимо само једном бојом, онда можемо на крају само да видимо како да их расподелимо са бојама, једино је важно да не постоје два чвора исте боје на непарном растојању. Нпр, не одговара ако имамо секвенцу чворова црвено-необојено-необојено-црвено, јер унутрашњи чворови морају бити плави а суседни су. Када на крају обојимо ‘једном бојом’ стабло, можемо само да пустимо претрагу у ширину/дубину од произвољног чвора и у односу на парност дубине одлучујемо која боја ће бити додељена том чвору уколико је предвиђено да буде обојен (нпр парне дубине црвеном, непарне плавом).

Успутно, скуп чворова који тражимо се назива доминантан скуп графа/стабла (Доминантни скуп — Википедија). Међу свим таквим ми бирамо минималан.

N \leq 20

У овом подзадатку је довољно да испробамо свих 2^N могућих бојења стабла и видимо које је задовољавајуће и има најмању збирну тежину. Временска сложеност овог решења је \mathcal{O}(N\cdot 2^N).

Чвор 1 је повезан са чворовима 2, 3, \ldots, N

Овакав тип графова се назива звезда (Star (graph theory) - Wikipedia). Једна варијанта је да обојимо само чвор 1. Друга је да обојимо све остале чворове. У оба та случаја добијамо доминантан скуп и потребно је само да изаберемо онај чија је сума тежина мања. У оба случаја бојимо чворове једном бојом по избору. Временска сложеност овог решења је \mathcal{O}(N).

Чвор i је повезан са чвором i+1, за i=1,2,\ldots,N-1

Овакав тип графова се назива пут (Path graph - Wikipedia). Сада задатак практично постаје: дат је низ и потребно је да изаберемо елементе тако да је сваки елемент или изабран или неки његов сусед изабран, а тако да је сума тих елемената минимална. Ово можемо решити на неколико начина, од којих је најинтуитивнији динамичко програмирање.

Једно од могућих стања јесте dp[i][0/1][0/1] - односно колика је минимална сума елемената тако да је до i-тог валидно изабрано, док нам друга и трећа координата означавају да ли су претходна два елемента обојена. У случају када претходна два нису обојена, приморани смо да обојимо тренутни, у свим осталим случајевима можемо да бирамо. На крају нас интересује стање последњег елемента. Временска сложеност овог решења је \mathcal{O}(N).

Све тежине су једнаке међусобно

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

- Изаберимо чвор $X$ са највећим бројем суседа који су листови и обојимо тај чвор
- Бришемо из стабла чвор $X$ и све његове суседе степена мањег или једнаког $2$
- Понављамо поступак рекурзивно док не буду обрисани сви чворови

Подсетник да бојимо у одговарајућу боју на основу почетних закључака. Временска сложеност овог приступа је \mathcal{O}(N) или \mathcal{O}(N\log_2 N), у зависности од имплементације.

Решење без додатних ограничења

За цео задатак је потребно применити поново динамичко програмирање. За чвор кажемо да има исфорсирану боју (односно да је исфорсиран чвор) уколико је обојен он или неки његов сусед.

За почетак ћемо кореновати стабло у произвољном чвору r. Стање ће нам бити dp[i][0/1] - минимална сума обојених чворова у подстаблу чвора i тако да је то оптимално бојење, а притом чвор i јесте форсиран (уколико је друга координата 1), односно није нужно форсиран (уколико је друга координата 0).

Прецизније користимо технику bottom-up динамичког програмирања. Крећемо од листова стабла и рачунамо на горе стања. У ред додајемо чворове чија су сва деца већ обиђена. За листове постављамо dp[l][0] = 0 и dp[l][1] = 1.

Када рачунамо dp[i][1] имамо два избора. Први јесте да обојимо чвор i и онда деца не морају да имају од раније исфорсирану боју. Други јесте да једно од деце буде обојено, затим деца тог детета не морају да имају исфорсирану боју, а сва остала деца чвора i такође морају да имају исфорсирану боју. По свој деци тражимо миниммум. Математички записано: dp[i][1] = min(a[i] + \sum_{x \in deca[i]}dp[x][0], min_{x \in deca[i]}(a[x] + \sum_{y \in deca[x]}dp[y][0] + \sum_{z \in deca[i], z \neq x} dp[z][1])).

Када рачунамо dp[i][0] битно нам је да сва деца чвора i имају исфорсирану боју, јер им чвор i то неће омогућити. Тако да је прелаз $dp[i][0] = min(\sum_{x \in deca[i]}dp[1], dp[i][1]).

На крају, решење је dp[r][1]. Временска сложеност овог решења је \mathcal{O}(N) или \mathcal{O}(N \log(N)), у зависности од имплементације.

Напомена

Подзадатак N \leq 1000 је изостављен јер представља само спорију имплементацију општег решења.

A3: Палиндромска Шетња

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

Решење када је у сваком упиту c = `a`

У овом подзадатку је свака шетња палиндром. Довољно је проверити да ли су два чвора у истој компоненти повезаности. Ово можемо да постигнемо тако што користимо чувамо којој компоненти припада који чвор. Сваки упит у којем се граном повезују два чвора из исте компоненте игноришемо. Када се спајају два чвора из различитих компоненти, додајемо нову грану између њих и пуштамо bfs у којем све чворове до којих је могуће стићи до једног из та два стављамо у исту компоненту. Ово решење ради у сложености O(N^2 + Q). За бољу сложеност је потребно користити адекватну структуру, нпр. dsu (disjoint set union).

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

Да би решили овај подзадатак, морамо да приметимо пар ствари. Посматрајмо следећу релацију над неуређеним паровима чворова:

  • (a,b) је у релацији са (x,y) \leftrightarrow могуће је стићи палиндромском шетњом од a до b акко је могуће стићи палиндромском шетњом од x до y .

Приметимо да је ово релација еквиваленције. Штавише, приметимо да је у свакој класи екиваленције у којој су палиндромске шетње бар један неуређен пар облика (a,a) или облика (a,b), где постоји грана између a и b. Додавањем гране између чворова u и v са карактером c стапа неке класе еквиваленције у једну. Посматрајмо све гране (x,y) на којима је написан карактер c. Тада се стапају тачно следеће класе еквиваленције:

  • (x,u) и (y,v) у једну класу еквиваленције (јер ако је постоји палиндромска шетња између x и u, тада постоји и палиндромска шетња између y и v, нпр продужавањем претходне гранама (x,y) и (u,v)).

  • (x,v) и (y,u) у једну класу еквиваленције.

Према томе за сваку додату грану, можемо итерирати по свим претходним додатим гранама истог типа и спајати одговарајуће класе еквиваленције. Ово решење ради у временској сложености O(N^4 + N^2 \log N + Q \log N).

Решење без додатних ограничења

За овај подзадатак, неопходно је оптимизовати претходно решење. Можемо приметити да није неопходно итерирати кроз све претходне гране, већ можемо да прескочимо гране које би формирале циклус који не мења бипартитност компоненте повезаности на истом карактеру. Формално, претпоставимо да додајемо грану између u и v са карактером c. Тада, разликујемо следеће случајеве:

  • u и v до сада нису били повезани у подграфу који је формиран гранама са карактером c. Тада процесирамо ову грану.
  • u и v јесу у истој компоненти повезаности и та компонента је бипартитна, али додавањем нове гране, та компонента престаје бити бипартитна. Тада процесирамо ову грану.
  • У супротном игноришемо додавање гране. Пажљивијом анализом класа које се стапају у једну из решења претходног подзадатка се може видети да грана у овом случају не би стопила никоје две класе еквиваленције, нити би утицала на неке наредне упите.

Према томе, за сваки карактер, грана које треба да процесирамо има O(N) (зашто?). Ово решење ради у сложености O(N^2 \cdot |\Sigma| \cdot \log N).