Przeskocz do treści

Delta mi!

Jaskinia

Tomasz Idziaszek

o artykule ...

  • Publikacja w Delcie: kwiecień 2012
  • Publikacja elektroniczna: 01-04-2012
  • Wersja do druku [application/pdf]: (83 KB)

W tym kąciku omówimy zadanie Jaskinia, które pojawiło się w zeszłym roku na Akademickich Mistrzostwach Polski w Programowaniu Zespołowym.

Tytułowa jaskinia składa się z math komnat połączonych math korytarzami, które tworzą drzewo. Grotołazi chcą podzielić się na kilka grup, a następnie chcą przydzielić każdej grupie zbiór komnat, tak by każda komnata została przydzielona dokładnie jednej ekipie grotołazów oraz by każda grupa dostała tyle samo komnat do zbadania i była w stanie poruszać się pomiędzy przydzielonymi komnatami bez przechodzenia przez komnaty innych ekip. Na ile grup mogą podzielić się grotołazi?

Poniższa obserwacja daje warunek konieczny i wystarczający na istnienie podziału:

Lemat. Drzewo o math wierzchołkach można podzielić na spójne kawałki rozmiaru math wtedy i tylko wtedy, gdy w tym drzewie znajduje się dokładnie math krawędzi, które łączą poddrzewa o rozmiarach będących wielokrotnościami math

Dowód (prosty). Jeśli podzieliliśmy drzewo na kawałki rozmiaru math zgodnie z warunkami zadania, to tych kawałków jest math Krawędzi, które łączą wierzchołki należące do różnych kawałków, jest dokładnie  math a ponieważ poddrzewa połączone takimi krawędziami składają się z całych kawałków, wobec tego rozmiar każdego z tych poddrzew jest podzielny przez math Krawędzie wewnątrz kawałków nie mają tej własności.

W drugą stronę: usuwamy kolejno wszystkie math „dobrych” krawędzi. Po każdym usunięciu dostajemy zbiór niepustych kawałków o rozmiarach podzielnych przez math Na końcu zostanie math kawałków, czyli wszystkie muszą mieć rozmiar math


Powyższa obserwacja pozwala nam na stworzenie następującego rozwiązania: ukorzeniamy drzewo w dowolnym wierzchołku i dla każdego math przechodzimy drzewo od liści do korzenia, zliczając „dobre” krawędzie, tzn. takie, które łączą poddrzewa o rozmiarach będących wielokrotnościami math (rozmiary poddrzew obliczamy na bieżąco prostym programowaniem dynamicznym).

Złożoność czasowa tego rozwiązania to math  gdzie math oznacza liczbę dzielników math Wśród liczb math możemy znaleźć takie, które mają dość dużo dzielników (rekord to math), zatem powyższe rozwiązanie jest niezbyt szybkie.

Można to zrobić sprytniej, wykonując tylko jedno przeszukiwanie drzewa. Rozważmy bowiem krawędź, która łączy dwa poddrzewa o rozmiarach math oraz  math i zastanówmy się, dla jakich math będzie ona „dobrą” krawędzią. Ano, dla takich math które dzielą zarówno math jak i  math zatem dla wszystkich dzielników liczby math Możemy zatem postąpić następująco. W tablicy math będziemy zliczać „dobre” krawędzie. W pierwszym kroku przechodzimy drzewo od liści do korzenia i dla każdej krawędzi zwiększamy licznik math Przejście drzewa wykonujemy w czasie math  gdyż dla każdej krawędzi obliczenie największego wspólnego dzielnika możemy wykonać w czasie math  korzystając z algorytmu Euklidesa.

Następnie przeglądamy dzielniki math w kolejności rosnącej i dla każdego takiego dzielnika math zwiększamy o wartość math liczniki math dla math Jeśli dla każdego math w poszukiwaniu jego dzielników przejrzymy wszystkie liczby mniejsze od math to ten krok wykonamy w czasie math  czyli czasie proporcjonalnym do sumy dzielników math czyli math

Odpowiedzią są wszystkie liczby math które spełniają math Złożoność czasowa tego rozwiązania to math  Przejście drzewa moglibyśmy zrealizować w czasie liniowym, gdybyśmy znali dla każdego math wartość math Można te wartości obliczyć następującym algorytmem, który przypomina metodę sita Eratostenesa:

display-math

Po wykonaniu powyższego kodu będziemy mieli math dla każdego math Dla każdego math wewnętrzna pętla wykona się math razy, zatem w całym algorytmie wykona się math razy. Ostatecznie daje nam to algorytm o złożoności czasowej math