Omówienie zadania Plan metra (I etap XXV OI)
W zadaniu mamy dane dwa ciągi:
- \(d_i\) – odległość (cena biletu do) wierzchołka \(i\) od wierzchołka \(1\) dla \(2 \le i \le n-1\)
- \(l_i\) – odległość (cena biletu do) wierzchołka \(i\) od wierzchołka \(n\) dla \(2 \le i \le n-1\)
Na podstawie tych danych chcemy ustalić, czy opisują one jakieś krawędziowo-ważone drzewo. Jeśli tak, chcemy wypisać krawędzie dowolnego drzewa pasującego do otrzymanego opisu.
Początkowe obserwacje
Jako że w zadaniu chcemy otrzymać dowolne drzewo pasujące do opisu, postaramy się skonstruować je w jak najprostszy sposób.
Przypatrzmy się ścieżce z wierzchołka \(1\) do wierzchołka \(n\). Okazuje się, że możemy rozważać tylko drzewa, w których każdy wierzchołek leży na ścieżce z \(1\) do \(n\) albo jest połączony krawędzią z wierzchołkiem z tej ścieżki. Dowód jest dosyć prosty:
Rozważmy drzewo zgodne z opisem zadanym przez ciągi \(l\) i \(d\), które jednak nie jest w formie zaproponowanej powyżej. Musi wtedy istnieć liść \(x\) \(\notin \{1, n\}\), który nie jest połączony krawędzią z żadnym wierzchołkiem ze ścieżki. Niech \(y\) będzie wierzchołkiem ze ścieżki, który jest najbliższy do \(x\).
Możemy teraz usunąć jedyną krawędź, którą ma \(x\) oraz dodać krawędź z \(y\) do \(x\) o długości ścieżki z \(y\) do \(x\). W ten sposób zachowujemy odległości \(x\) do wierzchołków \(1\) i \(n\), a powstały graf nadal jest drzewem. Wykonując tę operację wiele razy dochodzimy od dowolnej odpowiedzi do odpowiedzi w pożądanej formie.
Podzadanie \(n \leq 10\), \(l_i, d_i \leq 200\)
W tym podzadaniu możemy sprawdzić każdą potencjalną długość ścieżki z \(1\) do \(n\). Konstrukcja dla długości \(x\) wygląda następująco:
Na początek szukamy wszystkich wierzchołków, które leżą na ścieżce z \(1\) do \(n\). Są to te, dla których zarówno \(d_i\) jak i \(l_i\) są mniejsze od \(x\). Aby otrzymać kolejność musimy je posortować po \(l_i\)
Teraz dla każdego wierzchołka spoza ścieżki szukamy wierzchołka ze ścieżki, z którym może on być połączony krawędzią. Jeśli wierzchołek \(i\) ma być połączony z wierzchołkiem \(j\), to długość krawędzi musi być równa \(d_i - d_j\). Musi ona jednak również być równa \(l_i - l_j\) oraz być większa od 0. Więc przy próbie dodania krawędzi między \(i\) oraz \(j\) sprawdzamy równość \(d_i - d_j = l_i - l_j\) oraz nierówność \(d_i > d_j\)
Jeśli próba takiej konstrukcji nie powiodła się to próbujemy jeszcze raz dla większego \(x\). W przeciwnym wypadku mamy odpowiedź.
Rozwiązanie to działa więc w czasie \(O(z \cdot n^2)\), gdzie \(z\) to zakres długości potencjalnej ścieżki.
Podzadanie \(n \leq 3000\)
Okazuje się, że łatwo jesteśmy w stanie pozbyć się \(z\) ze złożoności. Wystarczy zastanowić się jak wygląda ścieżka z \(1\) do \(n\). Mamy do rozważenia 2 przypadki:
- Wierzchołki \(1\) i \(n\) są połączone krawędzią.
- Na ścieżce z \(1\) do \(n\) jest co najmniej jeden wierzchołek pośredni.
1. Wierzchołki \(1\) i \(n\) są połączone krawędzią.
W tym przypadku rozwiązanie jest bardzo proste. Niech \(x\) będzie długością krawędzi z \(1\) do \(n\). Każdy wierzchołek musi być połączony krawędzią z wierzchołkiem \(1\) lub \(n\). Oczywiście będzie połączony z tym, do którego ma bliżej.
A więc wierzchołek \(i\), dla którego \(d_i < l_i\) jest połączony krawędzią długości \(d_i\) z wierzchołkiem \(1\). Musi wtedy zachodzić \(l_i = x + d_i\), bo ścieżka z \(n\) do \(i\) składa się z krawędzi z \(n\) do \(1\) oraz z \(1\) do \(i\). Analogicznie dla wierzchołków połączonych krawędzią z wierzchołkiem \(n\) dostajemy \(d_i = x + l_i\).
Musimy więc tylko sprawdzić, czy dla każdego wierzchołka wartość \(|l_i - d_i|\) jest taka sama. Będzie to x, czyli długość krawędzi z \(1\) do \(n\). Złożoność w tym przypadku to \(O(n)\).
2. Na ścieżce z \(1\) do \(n\) jest co najmniej jeden wierzchołek pośredni.
Zauważmy, że możemy jednoznacznie wyznaczyć wierzchołki z tej ścieżki. Będą na niej leżały tylko te wierzchołki, dla których wartość \(d_i + l_i\) jest minimalna - bardziej formalnie \(i\) leży na ścieżce wtedy i tylko wtedy, gdy \(d_i + l_i = \min(l_j + d_j \, | \, 2 \leq j \leq n - 1)\). Jest tak dlatego, że dla każdego wierzchołka spoza tej ścieżki, \(d_i + l_i\) zawiera w sobie długość ścieżki powiększoną o długości dodatkowych krawędzi.
Możemy teraz ponownie zastosować metodę z poprzedniego podzadania do odnalezienia dla każdego wierzchołka spoza ścieżki wierzchołka, z którym może być połączony krawędzią.
Złożoność tego przypadku oraz całego rozwiązania jest wynosi \(O(n^2)\).
Pełne rozwiązanie
Ulepszymy teraz znajdowanie wierzchołka ze ścieżki, do którego chcemy połączyć wierzchołek spoza ścieżki.
Wcześniej, dla każdego wierzchołka \(i\) spoza ścieżki, sprawdzaliśmy czy może być połączony z wierzchołkiem \(j\) ze ścieżki, za pomocą warunku \(d_i - d_j = l_i - l_j\). Ta równość jest jednak równoważna następującej: \(d_i - l_i = d_j - l_j\). Jest to dużo lepszy warunek, ponieważ po obu stronach równości mamy wartości dotyczące tylko jednego wierzchołka.
Możemy zatem dla każdego wierzchołka ze ścieżki zapamiętać wartość \(d_i - l_i\) i dla każdego wierzchołka spoza ścieżki sprawdzić czy istnieje wierzchołek na ścieżce, który ma taką samą wartość. Co więcej, nie musimy już sprawdzać czy \(d_i > d_j\), ponieważ wynika to ze sposobu wyboru wierzchołków na ścieżce.
Zapamiętywanie możemy zrealizować na przykład za pomocą unordered_map
. Zarówno dorzucenie wartości \(d_i - l_i\) jak i zapytanie o tę wartość działa średnio w czasie stałym.
Zagwarantuje to nam złożoność czasową rozwiązania \(O(n \log n)\). Logarytm występuje tu ze względu na konieczność posortowania wierzchołków na ścieżce.
Opis przygotowano w ramach projektu "Mistrzostwa w Algorytmice i Programowaniu - Uczniowie" - zadania finansowanego ze środków Ministerstwa Cyfryzacji.
![]() |
![]() |
![]() |
![]() |
![]() |