BOI 2016
CEOI 2017
BOI 2018
BOI 2019
BOI 2020
CEOI 2020
Napój Wielkiej Potęgi
Dany jest graf o wierzchołkach () oraz początkowo bez żadnych krawędzi.
Każdy wierzchołek ma podaną wysokość .
Następuje modyfikacji grafu,
gdzie modyfikacja w momencie polega na dodaniu lub usunięciu
krawędzi między wierzchołkami oraz
(zależnie od tego, czy teraz była w grafie, czy nie).
Zagwarantowane jest, że stopień każdego wierzchołka w każdym momencie
wynosi co najwyżej .
Po otrzymaniu informacji o wszystkich modyfikacjach,
należy odpowiadać online na zapytań,
każde opisane parą wierzchołków oraz momentem .
Odpowiedź na zapytanie to najmniejsza różnica ,
gdzie jest sąsiadem oraz jest sąsiadem w momencie .
Podzadanie 2
Dla wystarczy dla każdego zapytania zaaplikować na grafie
wszystkie modyfikacje grafu do danego momentu ,
a następnie przeiterować się po każdym sąsiedzie oraz .
Daje to algorytm o złożoności czasowej lub ,
zależnie od sposobu przechowywania grafu
(za pomocą typów jak set można usuwać elementy w złożoności ).
Zamiast iterować się po każdej parze sąsiadów w ,
można najpierw posortować sąsiadów według ich wysokości,
a potem gąsienicą dla kolejnych sąsiadów utrzymywać sąsiada o najmniejszej
wyższej wysokości (albo alternatywnie znajdywać takiego sąsiada za pomocą wyszukiwania binarnego),
co daje złożoność .
W tym podzadaniu takie podejście nie było potrzebne, ale przyda się w kolejnych podzadaniach.
Podzadanie 5 -- rozwiązanie ze spamiętywaniem co pierwiastek
W tym podzadaniu zachodzi .
Co modyfikacji można zapisać aktualny graf,
dzięki czemu, mając zapytanie w momencie ,
zamiast budować cały graf od nowa, można znaleźć najbliższą wcześniejszą zapisaną modyfikację
(która jest odległa o co najwyżej modyfikacji)
i zaaplikować na niej późniejsze modyfikacje, aby otrzymać graf w momencie .
Daje to rozwiązanie o złożoności
oraz pamięci .
Podzadanie 5 -- rozwiązanie z trzymaniem sąsiadów tylko po aktualizacji
Jeżeli w momencie modyfikacja krawędzi nie dotyczyła wierzchołka ,
to lista jego sąsiadów jest taka sama, co w momencie .
Można dla każdej modyfikacji dotyczącej wierzchołka zapisać sobie jego listę sąsiadów
po tej modyfikacji, a następnie dla danego zapytania znaleźć ostatnią aktualną listę sąsiadów (na przykład wyszukiwaniem binarnym)
i na niej wyszukać odpowiedź.
Łącznie nie będzie więcej niż kopii listy sąsiadów
(jest to zsumowana liczba list po wszystkich wierzchołkach),
a każda lista sąsiadów jest rozmiaru co najwyżej .
Daje to rozwiązanie o złożoności
oraz pamięci .
Podzadanie 5 -- rozwiązanie z drzewami trwałymi
Można skorzystać z trwałych zrównoważonych drzew wyszukiwań binarnych
lub trwałych drzew przedziałowych,
aby utrzymać dla każdego momentu listę sąsiadów wszystkich wierzchołków.
Takie rozwiązania zazwyczaj nie otrzymywały 100 punktów
przez zbyt dużą złożoność lub stałą,
zarówno czasową, jak i pamięciową
(struktury trwałe wymagają dodatkowego czynnika
w pamięci).
Podzadanie 5 -- rozwiązanie z drzewem przedziałowym po czasie
Każda krawędź jest w grafie w momentach będącą sumą rozłącznych przedziałów.
Wszystkich takich przedziałów jest co najwyżej .
Można stworzyć drzewo przedziałowe
(analogiczne do
opisanego
w tym artykule).
Takie drzewo ma co najmniej liści i dla każdego przedziału
istnienia pewnej krawędzi , rozbija się ten przedział na wierzchołki bazowe i do nich
(na przykład do set-a) wrzuca się krawędź .
Wtedy jest spełniony warunek, że dla każdego momentu,
set wszystkich krawędzi grafu w tym momencie
to dokładnie złączenie setów krawędzi na ścieżce od odpowiedniego liścia do korzenia drzewa przedziałowego.
Stworzenie takiego drzewa ma złożoność czasową
i pamięciową,
ponieważ każda krawędź może wystąpić w wierzchołkach bazowych.
Aby otrzymać listę sąsiadów dla danego momentu ,
można dla każdego wierzchołka na ścieżce od -tego liścia do korzenia
wyszukać w tych set-ach krawędzie o takim wierzchołku
i złączyć takie listy krawędzi w jedną.
Dlatego złożoność czasowa rozwiązania wynosi .
Pełne zadanie -- zapisanie listy krawędzi co ileś modyfikacji
Żeby rozwiązać zadanie na pełnych limitach, można połączyć
rozwiązanie ze spamiętywaniem co pierwiastek
z
rozwiązaniem z trzymaniem sąsiadów tylko po aktualizacji.
Można zapisać listę sąsiadów danego wierzchołka
co modyfikacji listy sąsiadów tego wierzchołka,
dla pewnej wybranej stałej .
Dodatkowo też trzeba zapisać uporządkowaną listę zmian każdego wierzchołka.
Wtedy, by otrzymać listę sąsiadów wierzchołka w momencie ,
trzeba znaleźć (na przykład wyszukiwaniem binarnym)
ostatnią zapisaną listę i ręcznie dodać co najwyżej krawędzi do tej listy sąsiadów.
Stałą należy tak dobrać, aby jak najbardziej zmniejszyć czas działania programu,
ale tak, aby wciąż zmieścić się w limitach pamięci.
Można na przykład wybrać .
Złożoność czasowa wynosi
oraz pamięciowa wynosi .
Pełne zadanie -- wiele drzew przedziałowych po czasie
Można wykonać optymalizację
rozwiązania z drzewem przedziałowym po czasie,
aby działała na pełnych limitach.
Zamiast wyszukiwać binarnie odpowiednią listę krawędzi
wśród set'ów wierzchołków na ścieżce od liścia do korzeni,
można stworzyć drzewo przedziałowe po czasie
oddzielnie dla każdego wierzchołka ,
co eliminuje to wyszukiwanie binarne.
Ponieważ nie można utrzymywać takiego pełnego drzewa binarnego
o liściach dla każdego wierzchołka ,
to trzeba utrzymywać te drzewa przedziałowe w skompresowanej formie
(czyli ma to być drzewo wskaźnikowe lub drzewo przedziałowe skonstruowane offline).
Daje to rozwiązanie o złożoności czasowej
oraz pamięciowej .