Marcel si pred vedúcovskou chatou KSP kúpil automatický robotický systém na uskladňovanie korenia od firmy Kuchárske Stroje a Potreby s.r.o., prístroj má tvar kruhovej poličky, v ktorej je jeden rad priehradok, v každej priehradke je niekoľko vrecúšok s jedným druhom korenia.
Prístroj má hlavicu, ktorá vyberá a podáva korenia. Hlavica vždy ukazuje na jedno vrecúško korenia.
Marcel si pozerá svoje recepty a rozmýľa nad tým, ako si rozmiestniť korenia tak, aby nestrávil veľa času čakaním na to, kým mu robot podá korenie. Aby vedel optimalizovať toto rozloženie, tak by od vás potreboval vykonávať $2$ typy udalostí:
Zmeň počet vrecúšok s korením v nejakej priehradke.
Odpovedaj na otázku, ako dlho bude robotovi trvať vybrať konkrétny druh korenia, teda dostať sa zo začiatočnej pozície, vrchu priehradky $x$, na koncovú pozíciu, spodok priehradky $x’$, odkiaľ vie vybrať dané korenie.
Máme jednu cyklickú radu priehradok, dlhú $n$ (v každej priehradke je niekoľko vrecúšok korenia jedného typu). V priehradke $i$ sa nachádza $a_{i}$ vrecúšok.
Poličku obsluhuje robot, ktorý sa vie hýbať tromi spôsobmi:
Pohyb cyklicky doprava - z vrchu priehradky $x$ na vrch priehradky $x+1$, alebo zo spodu priehradky $x$ na spodok priehradky $x+1$,resp na $0$ ak sa $x=n-1$, tento pohyb trvá 1 sekundu.
Pohyb cyklicky doľava - z vrchu priehradky $x$ na vrch priehradky $x-1$, alebo zo spodu priehradky $x$ na spodok priehradky $x-1$, respektíve na $n-1$ ak sa $x=0$, tento pohyb trvá 1 sekundu.
Pohyb dole - Ak sa robot nachádza na vrchu priehradky $i$, tak sa vie za $a_i$ sekúnd dostať na spodok priehradky $i$.
Máme dva typy udalostí:
Zmeň počet vrecúšok v priehradke $x_i$ na $a_{new}$.
Otázka: Zisti, za koľko najmenej sekúnd sa vie robot presunúť, ak sa na začiatku nachádza na vrchu priehradky $x$, a potrebuje sa dostať na spodok priehradky $x’$? Robot môže použiť ľubovoľne veľa pohybov doprava a doľava, a najviac jeden pohyb dolu.
V prvom riadku vstupu sú medzerou oddelené čísla $n,q$, kde $1 \leq n,q \leq 5 \cdot 10^5$, udávajúce počet priehradok a otázok.
Na nasledujúcom riadku sú medzerou oddelené hodnoty $a_i$, počty vrecúšok na kôpkach v priehradkách, kde $1 \leq a_i \leq 10^9$ a $0 \leq i \leq n-1$.
Na nasledujúcich $q$ riadkoch sa nachádzajú udalosti 2 typov:
“! $x$ $a_{new}$” - zmeň hodnotu $a_x$ na $a_{new}$, kde $0 \leq x \leq n-1$ a $1 \leq a_{new} \leq 10^9$.
“? $x$ $x’$” - vypíš minimálny potrebný čas na to, aby sa robot dostal z vrchu priehradky $x$ na spodok priehradky $x’$, kde $0 \leq x,x’ \leq n-1$.
Táto úloha má 8 testovacích sád. V nich sú nasledovné obmedzenia:
| Sada | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| $1 \leq n \leq$ | $100$ | $1\,000$ | $10^6$ | $10^6$ | $10^6$ | $10^6$ | $10^6$ | $2 \cdot 10^6$ |
| $1 \leq q \leq$ | $100$ | $1\,000$ | $10^5$ | $10^5$ | $10^5$ | $10^5$ | $10^5$ | $5 \cdot 10^5$ |
V sadách $3,5,6$ sa navyše nachádzajú iba udalosti typu $?$.
V sadách $3,4$ platí, že vo všetkých udalostiach typu $?$ je $|x-x’|=N/2$, a $N$ je párne.
Vstupy a výstupy pre túto úlohu sú veľké, preto odporúčame používať
FastIO a \n namiesto endl v C++. Upozorňujeme
vás, že pomalšie jazyky ako Python nemusia prechádzať v časovom
limite.
Pre každú udalosť typu $?$ vypíš na samostatný riadok odpoveď.
Input:
5 2
1 1 1 1 1
? 0 0
? 0 4
Output:
1
2
V prvej otázke sa robot vie posunúť dole, za 1 sekundu.
V druhej otázke sa robot posunie doľava a dole, všimnite si, že tento pohyb je cyklický.
Input:
5 3
9 9 9 9 9
? 0 0
! 4 1
? 0 0
Output:
9
3
V prvej otázke sa robot vie posunúť dole za 9 sekúnd.
Po zmene sa v druhej otázke oplatí prejsť doľava, dole za 1 sekundu a doprava, taktiež si všimnite cyklickosť pohybov do strán.
Ako prvé si vieme všimnúť, že sa nám nikdy neoplatí navštíviť vrch (resp. spodok) nejakej priehradky dvakrát.
Dole sa robot musí presunúť práve raz, a preto vo všeobecnosti optimálny pohyb bude vyzerať nasledovne:
Robot sa presunie (pohybmi doprava alebo doľava) na vrch nejakej priehradky (resp. ostane tam, kde sme začínali)
Robot prejde na spodok priehradky
Robot prejde (pohybmi doprava alebo doľava) na spodok cieľovej priehradky (resp. ostane na mieste, ak tam už po druhom kroku je)
Navyše pohyb v prvom a treťom kroku bude zjavne iba doprava, alebo iba doľava (inak by robot navštívil vrch, resp. spodok nejakej priehradky dvakrát). Otázka je teda, ako vybrať priehradku, v ktorej robot vykoná pohyb dole.
Keď je $n$ malé vieme použiť hrubú silu, a vyskúšať všetky možnosti, kde by mohol robot ísť dole.
Ak by sme sa chceli dostať z vrchu priehradky číslo $x$ na spodok priehradky číslo $x’$ tak, že robot prejde dole na pozícii $i$, bude to trvať čas za ktorý sa dostame z $x$ na $i$ (teda $\min(|x-i|, n-|x-i|)$) plus čas na prejdenie dole priehradkou $i$ (čiže $a_i$), plus čas na dostanie sa z vrchu priehradky $i$ na vrch priehradky $x’$.
V čase $O(n)$ na otázku vieme prejsť všetky možnosti a vybrať minimum. Časová zložitosť tohoto riešenia je preto $O(nq)$ – všimnite si, že aj keď vieme udalosti typu ! simulovať v konštantnom čase, v najhoršom prípade nám každá otázka zaberie lineárne dlho.
Pamäťová zložitosť je $O(n)$, pamätáme si iba vstup v poli, ktorý postupne meníme.
Predstavme si teraz, že pre každú udalosť typu ? platí $|x-x’| = n/2$.
Vieme si všimnúť, že nech vyberieme akékoľvek číslo priehradky $i$ v ktorej robot vykoná pohyb dole, tak v horizontálnom smere vždy prejdeme iba vzdialenosť $n/2$.
Tým pádom je optimálne riešenie vybrať $i$ tak, aby sme minimalizovali $a_{i}$, takže vždy vyberieme najmenšie $a_{i}$, to vieme robiť rýchlo aj so zmenami použitím obyčajného intervalového stromu alebo haldy.
Časová zložitosť daného riešenia je $O(n+ q \log n)$.
Ako sme si už všimli, optimálne riešenie vždy vieme rozložiť na 3 typy pohybov:
Pohyb po vrchoch priehradiek v jednom smere.
Pohyb dole.
Pohyb po spodkoch priehradiek v jednom smere.
Pozrime sa na to podrobnejšie. V prvom a druhom kroku máme dve možnosti: ísť doprava, alebo doľava. Takto máme štyri typy ciest, ktorými vie robot ísť: doľava-dole-doľava, doľava-dole-doprava, doprava-dole-doľava, a doprava-dole-doprava.
Následne si ešte rozdelíme prípady podľa toho, či $x < x’$, alebo $x\geq x’$.
Pozrime sa na prípad, že náš pohyb je doprava-dole-doprava. Podľa relatívneho poradia $x$ a $x’$:
Prípad doprava-dole-doprava
Pozorný čitateľ si môže všimnúť, že sme zdanlivo vynechali dva prípady: vo vrchnom obrázku by sa $i$ mohlo nachádzať za $x’$ (teda by sme cestou doľava prešli okolo $x’$). Avšak vtedy by sme horizontálne prešli vyše $n$ priehradiek. V tomto prípade sa však cesta ku $i$ dá zrýchliť, ak by sme išli iba doľava, viď obrázok:
Prípad
doprava-dole-doprava-ale-zle
Takže máme dva prípady. Pozrime sa najskôr na prípad, kde $x < x’$, a dole prejdeme na priehradke číslo $x\leq i\leq x’$. Koľko to bude trvať? $a_i + (i - x) + (x’ - i) = x’ - x + a_i$. Takže na nájdenie najlepšieho indexu $i$ pre tento prípad nám stačí použiť intervalový strom na nájdenie $x\leq i\leq x’$ s najmenším možným $i$.
Čo sa stane ak $x\geq x’$. Rovnakým počítaním si môžme všimnúť, že sa nám oplatí jednoducho nájsť $i$ s najmenším $a_i$ pre ktoré buď platí $i\leq x’$ alebo $i\geq x$). Toto tiež vieme spraviť pomocou intervalového stromu.
Prípad doľava-dole-doľava, je v princípe identický, takže zostávajúce dva zaujímavé prípady sú doľava-dole-doprava a doprava-dole-doprava.
Ďalej sa pozrime na prípad doprava-dole-doľava. V tomto prípade máme štyri možnosti1, ako môžeme uvidieť na nasledujúcom obrázku:
Prípad doprava-dole-doľava
Aby sme sa vyhli ďalšej analýze prípadov (prechod cez $n$), všimnime si, že ak máme pole veľkosti $2n$, kde $a_i = a_{n+i}$, vtedy si môžme predstaviť, že namiesto $i<x$ (pri ktorom by sme museli prejsť cez $n$) prechádzame cez $i+n$. Teraz si vieme spraviť analýzu prípadov:
Prípad $x<x’$
$x’ \leq i \leq x + n$ (tento príklad je na obrázku zvýraznený červenou): táto cesta trvá $a_i + (i-x) + (i - x’) = a_i + 2i - x - x’$
$x + n \leq i \leq x’ + n$ (tento príklad je na obrázku zvýraznený modrou): táto cesta trvá $a_i + (i-(x+n)) + (i - x’) = a_i + 2i - x - x’ - n$ Prípad $x > x’$:
$x \leq i \leq x’ + n$ (tento príklad je na obrázku zvýraznený červenou): táto cesta trvá $a_i + (i-x) + (i - x’) = a_i + 2i - x - x’$
$x’ + n \leq i \leq x + n$ (tento príklad je na obrázku zvýraznený modrou): táto cesta trvá $a_i + (i-x) + (i - (x’+n)) = a_i + 2i - x - x’ - n$
Všimnime si, že v každom z týchto štyroch prípadov sa nám cena rozloží na $a_i + 2i$ a konštantu závisiacu iba od otázky (buď $x+x’+n$ alebo $x+x’$). Prvé číslo vieme pre každý prípad zistiť intervalovým stromom ktorý si na $i$-tej priehradke pamätá čísla $a_i+2i$, druhé číslo záleží od prípadu.
A napokon, aj prípad doľava-dole-doprava vieme riešiť nápodobne, len miesto $a_i + 2i$ potrebujeme hodnoty $a_i-2i$. Ďalšia možnosť je pozorovanie, že toto je to isté ako doprava-dole-doľava ak vymeníme $x$ a $x’$.
Takže aby sme to zhrnuli, potrebujeme niekoľko (dva alebo tri) intervalové stromy2 s hodnotami $a_i$, $a_i + 2i$ (resp. $a_i-2i$). Aby sme nemuseli riešiť prechody cez $n$, cyklickosť poľa implementujeme tak, že máme hodnoty dvakrát za sebou. Pri udalostiach typu ! jednoducho zmeníme hodnoty v intervalových stromoch na vhodných miestach.
Pri udalostiach typu ? si poriadne rozpíšeme možnosti, a spýtame sa vhodné otázky.
Ako rýchlo toto riešenie beží? Pre každú otázku použijeme konštantný počet queries pre intervalový strom, a teda použime $O(\log n)$ operácií pre každú udalosť. Na vybudovanie intervalových stromov a načítanie vstupu potrebujeme lineárny čas, takže dostaneme výslednú časovú zložitosť $O(n+q\log n)$.
Pamäťová zložitosť je $O(n)$, keďže si musíme uložiť vstup a intervalové stromy.
Vzhľadom na veľké množstvo jednotlivých prípadov je sa v tejto úlohe veľmi ľahké pomýliť, preto pri implementácii si odporúčame použiť prístup lenivého programátora3 a prípady si poriadne rozpísať, resp. nakresliť.
V skutočnosti je relevantná presne jedna, v závislosti na hodnote $x-x’$. Pre jednoduchosť uvažujme všetky. ↩
Princíp lenivého programátora je nasledovný: Bežný človek vidí problém, a hneď sa do neho vrhne. Výsledkom toho po troch hodinách bude dlhánsky program, plný chýb. Lenivý programátor sa najskôr hodinu bude zamýšľať, ako si ušetriť čo najviac roboty a okrajových prípadov. Potom to za pol hodinu naprogramuje. (Všimnime si, že lenivý programátor má kratší program, s menej chybami a ešte to celé spravil dvakrát rýchlejšie.) ↩
Korešpondenčný seminár z programovania zastrešuje občianske združenie Trojsten.
Trojsten, o.z.
FMFI UK, Mlynská dolina
842 48 Bratislava
Programátorská súťaž pre základoškolákov
Materiály a úlohy na výučbu programovania
Intenzívny programátorský zážitok v lete