Táto úloha je programátorská. Ako svoje riešenie odovzdaj program vo svojom obľúbenom jazyku a automaticky sa dozvieš, koľko si dostal/a bodov. Ak si takýto typ úloh ešte nikdy neriešil/a, skús sa pozrieť ako by mal vyzerať ideálny program. Ak zatiaľ programovať nevieš, ale chcel/a by si sa naučiť, môžeš skúsiť našu KSP School.
Ak máte akékoľvek otázky ohľadom tejto úlohy, napíšte Kubovi na [email protected].
Mapu už máme a tak nám zostáva posledná vec pred tým, ako sa môžeme vybrať preč. Pred každou dlhou cestou je veľmi dôležité poriadne sa napapať. Naša pani kuchárka Miška si však zabudla so sebou zobrať suroviny, a tak ich potrebujeme zohnať z lokálneho skladu domorodcov. Tento sklad vyzerá veľmi špecificky, sú v ňom dve dlhé poličky pod sebou a veci sú na nich vždy umiestnené v dvojiciach pod sebou.
Aby sa Miška nemusela naťahovať k hornej poličke, dohodla sa so Štepim, že mu bude diktovať suroviny a on ich bude z poličiek skladať. Avšak hneď pri prvej surovine po ktorej Štepi siahol Miška skoro skolabovala. Pýtate sa prečo? Štepi išiel zobrať syr, pod ktorým bola šunka.
Miška totiž pochádza z veľmi poverčivej rodiny a jedna takáto povera hovorí, že ak použijete surovinu, pod ktorou nie je rovnaká surovina, zaručíte si tým otravu jedlom. Štepimu to síce prišlo ako blbosť, rozhodol sa však rešpektovať Miškinu poverčivosť a používať iba suroviny, pod ktorými je tá istá surovina.
Týmto sa ale objavil nový problém - koľko surovín vôbec môžu použiť? Keďže poličky sú veľmi dlhé, potrebuje so zrátaním takýchto surovín vašu pomoc.
Máme dve poličky umiestnené pod sebou a na každej z nich je $n$ surovín. Tieto suroviny sú umiestnené v dvojiciach pod sebou. Pomôžte Štepimu a Miške zistiť koľko sa v sklade nachádza surovín, pod ktorými je rovnaká surovina.
Každá surovina je reprezentovaná nezáporným celým číslom. Keďže poličky môžu byť veľmi dlhé, ich obsah dostanete vo forme úsekov, ktoré obsahujú rovnakú surovinu.
V prvom riadku vstupu je číslo $N$ - počet surovín na každej poličke.
V druhom riadku sa nachádza číslo $X$ - počet úsekov prvej poličky.
Nasleduje $X$ riadkov popisujúcich úseky prvej poličky. V každom z nich je trojica čísel $z_i$, $k_i$ a $s_i$ - označujúca začiatok a koniec úseku (vrátane) a surovinu, ktorá sa v ňom nachádza.
Na ďalšom riadku je číslo $Y$ - počet úsekov druhej poličky. Za ním nasleduje $Y$ riadkov popisujúcich úseky druhej poličky v rovnakom formáte ako úseky prvej poličky.
Je zaručené, že sa žiadne dva úseky na jednej poličke neprekrývajú a že úseky pokrývajú celú poličku. Úseky nemusia byť zoradené v poradí od začiatku po koniec poličky. Vždy platí $1 \leq X, Y \leq N$, $1 \leq z_i \leq k_i \leq N$ a $1 \leq s_i \leq 10^9$.
Existujú 4 sady vstupov, obmedzenia v nich a ich bodovanie sú nasledovné:
| Sada | 1 | 2 | 3 |
|---|---|---|---|
| Body | 10 | 20 | 70 |
| $1 \leq N \leq$ | $100$ | $10^6$ | $10^{15}$ |
| $1 \leq X+Y \leq N$ | $200$ | $2*10^6$ | $5*10^5$ |
Pozor, v sade 3 sa môžu vyskytovať veĺmi veľké hodnoty, ktoré sa nezmestia do
štandardných 32-bitových premenných. V jazykoch iných ako Python je preto potrebné
použiť 64-bitové premenné (long long v C++, int64 v Pascale, a pod.).
Vypíšte jeden riadok a v ňom jedno celé číslo odpovedajúce počtu surovín, pod ktorými je rovnaká surovina.
5
4
1 1 1
2 2 3
3 3 2
4 5 1
2
1 2 3
3 5 1
3
| 1 | 3 | 2 | 1 | 1 |
| 3 | 3 | 1 | 1 | 1 |
5
2
1 3 1
4 5 2
2
1 2 1
3 5 2
4
| 1 | 1 | 1 | 2 | 2 |
| 1 | 1 | 2 | 2 | 2 |
Úloha nám zadala dve množiny intervalov určitých typov a máme zistiť veľkosť prieniku intervalov rovnakého typu.
Úloha je rozdelená do troch sád. Riešenie, ktoré zvládne vyriešiť iba prvú sadu, ani neviem, aké je, keďže najjednoduchšia vec, čo ma vie napadnúť, rieši rovno aj tú druhú.
Toto riešenie je, že si „komplikovaný“ formát intervalov, ktoré sme dostali na vstupe, premeníme na priamočiaru „dekomprimovanú“ reprezentáciu s dvomi poľami A a B dĺžky $N$. Keď už si pamätáme dve polia rovnakej dĺžky, zostane nám iba prejsť cez všetky pozície a skontrolovať, koľko z nich je naozaj v oboch poliach rovnakých.
for i in range(N):
if A[i] == B[i]:
odpoved += 1
# alebo aj iné varianty pre nadšencov pythonu
odpoved = len([i for i in range(N) if A[i] == B[i]])
odpoved = sum(a == b for a, b in zip(A, B))
Časová a pamäťová zložitosť bude O(N), keďže si vytvárame dve polia dĺžky $N$ a prechádzame cez ne.
Predstavme si, že budeme brať suroviny po jednej z oboch koncov poličiek naraz. Niekedy sa nám pošťastí a budeme mať na konci rovnaké suroviny, inokedy budeme mať smolu a budú rôzne. Aj napriek Miškiným poverám ich však odstránime, aby sme mohli pokračovať ďalej.
No ale všimnime si, že keďže suroviny tvoria v poličkách súvislé úseky rovnakého typu, tak či už sa nám pošťastí alebo nie, častokrát bude mať ďalšia dvojica surovín, na ktoré sa pozrieme, rovnaký výsledok. Napríklad sme práve odobrali dvojicu surovín, ktoré boli rovnaké, a vidíme, že suroviny patrili do úsekov, kde v hornej poličke zostáva ešte $1000$ rovnakých surovín a v dolnej $1234$ rovnakých surovín. Tak teraz by sme nemuseli $1000$-krát opakovať to isté. Už od pohľadu vidno, že najbližších $1000$ dvojíc bude rovnakých a vieme si ušetriť strašne veľa roboty. Tak pripočítame $1000$ ku výsledku, zmažeme celý horný úsek a dolný úsek zmenšíme o $1000$, čiže v ňom zostane ešte $234$ surovín.
Toto vieme všeobecne popísať tak, že máme dva zásobníky úsekov pre hornú a dolnú poličku. Nech $m$ je minimum z dĺžok úsekov na vrchu zásobníkov. Potom vieme, že najbližších $m$ dvojíc surovín bude mať rovnaký výsledok, a teda ak sú rovnaké, tak pripočítame $m$ do výsledku a oba úseky zmenšíme o $m$. To má ihneď za následok, že aspoň jeden z týchto úsekov bude prázdny a vieme ho zo zásobníka zmazať. Tento postup vieme opakovať, kým nevyprázdnime oba zásobníky.
V každom kroku odstránime zo zásobníka aspoň jeden prvok, takže počet krokov bude maximálne $X + Y$. Na to, aby toto fungovalo, však musia byť úseky zoradené podľa začiatku. Časová zložitosť je teda $O(X \log X + Y \log Y)$ kvôli triedeniu a pamäťová $O(X + Y)$.
N = int(input())
rows = [sorted(tuple(map(int, input().split())) for _ in range(int(input()))) for _ in range(2)]
rowA, rowB = [[(hi - lo + 1, t) for lo, hi, t in row] for row in rows]
res = 0
while rowA and rowB:
lA, tA = rowA.pop()
lB, tB = rowB.pop()
l = min(lA, lB)
res += l if tA == tB else 0
lA, lB = lA - l, lB - l
if lA:
rowA.append((lA, tA))
if lB:
rowB.append((lB, tB))
print(res)
Keby sme mali úseky už utriedené na vstupe, časová zložitosť by bola iba $O(X + Y)$. Okrem toho si všimnime, že je jedno, či berieme suroviny zľava alebo sprava. Preto, ak by sme mali vstup už utriedený, nemuseli by sme si pamätať druhý zásobník, ale všetko vyhodnocovať zároveň, čo načítavame vstup druhej poličky, čím by sme dosiahli pamäťovú zložitosť $O(X)$.
Aj napriek tomu, že vstup nie je utriedený, vieme stále zlepšiť pamäťovú zložitosť na $O(X)$. Zatiaľ čo všetky úseky z prvej poličky máme zapamätané v utriedenom poli, úseky z druhej poličky nám stačí vyhodnocovať počas načítavania. Vždy, keď načítame úsek druhej poličky, vieme binárne vyhľadať prvý príslušný úsek v prvej poličke a potom vykonať horeopísaný postup.
Takto si nikdy nemusíme pamätať druhú poličku, čiže pamäťová zložitosť bude $O(X)$. Navyše si vieme uvedomiť, že pri vykonávaní horeopísaného postupu $Y$-krát dokopy nevykonáme viac ako $O(X + Y)$ krokov. Časová zložitosť je teda $O(X \log X + Y \log X) = O((X + Y) \log X)$ (všimnime si, že máme tentoraz $\log X$ pri $Y$, keďže binárne vyhľadávame v poli dĺžky $X$).
Samozrejme, toto nebolo nutné na získanie plného počtu bodov.