Codeforces 1809F

Codeforces 1809F

January 5, 2026

link do zadania: https://codeforces.com/contest/1809/problem/F

Treść

NN miast (oznaczonych 1,2,,n{1, 2, \dots, n}) znajduje się na pewnym okręgu w tej kolejności. Z miasta ii możemy przedostać się do (i+1)(i+1)-wszego (gdzie dla nn jako kolejne przyjmujemy miasto 11).

Niech f(i)f(i) oznacza minimalny koszt podróży wokół okręgu, jeśli zaczynamy w mieście ii. Dla każdego ii chcemy znaleźć wartość f(i)f(i).

Poruszanie się wymaga paliwa, a nasz pojazd ma kk-litrowy zbiornik (więcej nie zmieścimy w danej jednostce czasu). W mieście ii koszt 1l1 l paliwa wynosi bib_i (zauważ, że bib_i przyjmuje bardzo specyficzne wartości). Wiemy też, że odległość z miasta ii do kolejnego wynosi aia_i.

Limity

W liczbach całkowitych:

  • 1t1041 \leq t \leq 10^{4} (liczba przypadków testowych)
  • 3n21053 \leq n \leq 2 \cdot 10^{5} (liczba miast)
  • 1k1091 \leq k \leq 10^{9} (maksymalna pojemność zbiornika)
  • 1aik1 \leq a_i \leq k (odległość do następnego miasta)
  • 1bi21 \leq b_i \leq 2 (koszt paliwa w mieście)
  • n2105\sum n \leq 2 \cdot 10^{5}

Rozwiązanie

Pierwszym typowym w takich zadaniach pomysłem jest “rozwinięcie” okrągu w prostą. Tworzymy dwie kopie każdego miasta, czyli ii oraz i+ni+n odpowiadają temu samemu miastu. Wtedy każda podróż po okręgu może być reprezentowana jako przedział [l,l+n1][l, l+n-1].

Brute force

Symulujemy proces podróży dla każdego ii. Możemy zastosować podobny trick jak w zadaniu CF 1238G Adilbek and the Watering System. W każdym momencie czasu trzymamy najlepsze kk litrów paliwa, które mogliśmy kupić. Będziemy dodawać nowe i wyrzucać to najdroższe. Przy przejściu z miasta ii do kolejnego dokonujemy zakupu najtańszych aia_i paliwa, które zapamiętaliśmy.

Warto dodać, że w takim rozwiązaniu możemy pominąć założenie o bi{1,2}b_i \in \{1, 2\}. Możemy użyć mapy (potrzebujemy min\textit{min} oraz max\textit{max}, można też użyć dwustronnej kolejki priorytetowej), wtedy dla każdego początku ll złożoność czasowa wyniesie O(nlog2(n))\mathcal{O}(n \cdot log_{2}(n)). Zatem ostatecznie mamy O(n2log2(n))\mathcal{O}(n^{2} \cdot log_{2}(n)).

Brute force, ale sprytniejszy

Spróbujmy wykorzystać dodatkowe założenie, które ominęliśmy w poprzednim podejściu.

Po pierwsze, zauważmy, że koszt podroˊz˙yi=1nai\textit{koszt podróży} \geq \sum_{i=1}^{n} a_i. Zatem możemy od razu dodać taką wartość do wyniku, a koszty bib_i zmniejszyć o 11, uzyskując odpowiednio {0,1}\{0, 1\}. Czyli teraz płacimy tylko w miastach o pierwotnym koszcie 22 (dalej nazywanych typem 11), a w pozostałych (typ 00) paliwo dostajemy za darmo.

Kiedy coś jest darmowe, oczywistym jest, że chcemy to wykorzystać. W miastach typu 00 będziemy brać maksymalnie dużo paliwa. W miastach typu 11 weźmiemy zupełne minimum - tylko tyle, aby dojechać do następnego. (Nie chcemy przecież sytuacji, gdzie zostało nam jakieś paliwo, za które musieliśmy zapłacić, gdy w nowym mieście możemy je mieć za darmo.)

Stąd pomysł, żeby dla każdego miasta wyznaczyć ostatnie miasto typu 00, które pojawia się przed nim. W nim kupimy minimum z tego, ile będziemy potrzebować na dalszą podróż i kk (wliczyliśmy to do wyniku na samym początku). Teraz “łańcuch” (11 \cdot miasto typu 00, xx \cdot miasto typu 11) miast będzie zużywał to paliwo, w pewnym momencie się ono wyczerpie (lub będzie go niewystarczająco) i dokonamy normalnego zakupu.

Wszystko możemy zrobić dla pojedynczego ll w O(n)\mathcal{O}(n), powtarzamy nn razy, stąd O(n2)\mathcal{O}(n^2).

Wzorcówka

Zamiast brutalnie symulować proces dla każdego miasta, zauważmy, że duży fragment podróży jest wspólny dla początków w ii oraz w i+1i+1.

Zatem spróbujmy wykorzystać zwykłe policzenie wyniku dla l=1l=1 i “indukcyjnie” zamieniać go w wynik dla l=2,3,,nl=2, 3, \dots, n w czasie O(szybko)\mathcal{O}(\textit{szybko}).

Intuicyjne jest utrzymywanie przedziału [l,r][l, r] i przesuwanie obu końców o 11 w prawo. Takie przesunięcie może utworzyć nowy łańcuch lub przeciąć stary.

Ogólnie, miasto ii ma dwa różne możliwe koszty: c0,ic_{0, i} (z wpływem poprzedzającego go miasta typu 00) oraz c1,ic_{1, i} (bez tego wpływu).

Zauważmy, że dodanie miasta na koniec (przedłużanie istniejącego łańcucha lub tworzenie nowego) jest proste. Wystarczy sprawdzić, czy początek łańcucha znajduje się w obecnym przedziale. Pozostaje rozwiązać problem aktualizowania łańcucha, który przecinamy poprzez przesunięcie ll.

Ale i to jest proste i szybkie, ponieważ sumarycznie miast w łańcuchach jest O(n)\mathcal{O}(n). Możemy robić “two-pointers”, gdzie zamieniamy wyniki miast z c0,ic_{0, i} na c1,ic_{1, i} na prefiksie utrzymywanego przedziału.

Zatem końcowa złożoność to amortyzowane O(n)\mathcal{O}(n).

moje rozwiązanie: https://codeforces.com/contest/1809/submission/356610808

Alternatywne rozwiązanie (An Son Nguyen)

Spróbujmy w inny sposób zoptymalizować brutalne rozwiązanie. Weźmy dowolny indeks o najmniejszym bib_i i ustawmy go jako “punkt startowy”. Zauważmy, że każdą optymalną podróż od indeksu ii po całym okręgu możemy robić na 2 segmenty: od indeksu ii z pustym zbiornikiem do punktu startowego z pustym zbiornikiem, oraz od punktu startowego z pustym zbiornikiem do ii z pustym zbiornikiem. Jest tak, gdyż zawsze będzie nam się opłacało kupować jak najwięcej litrów w miejscach o minimalnym bib_i. Koszt przejścia drugiego segmentu dla każdego indeksu będziemy obliczać wcześniej omawianym algorytmem zachłannym, zaczynając od punktu startowego i poruszając się zgodnie ze skierowaniem krawędzi. Koszt pierwszych segmentów będziemy zaś liczyć “od końca”, poruszając się od punktu startowego w kierunku przeciwnym do skierowania krawdędzi. Wystarczy nam do tego proste programowanie dynamiczne z gąsienicą.

Ponownie, końcowa złożoność to amortyzowane O(n)\mathcal{O}(n).

kod Ana Sona: https://codeforces.com/contest/1809/submission/273910141

Refleksja nad zadaniem

Warto zastanowić się, jak rozwiązać zadanie przy innych ograniczeniach, przykładowo dla k=k=\infty i dowolnych biZ+b_i \in \mathbb{Z}_{+}. Również ciekawa jest mocniejsza wersja oryginalnego zadania dla dowolnych biZb_i \in \mathbb{Z}.