Sieć światłowodowa
Tym razem omówimy zadanie Fiber-optic Network, które pojawiło się na zeszłorocznych zawodach ACM-ICPC Asia Mudanjiang Regional Contest.
W zadaniu tym mamy dane drzewo o wierzchołkach, a dla każdego wierzchołka zadane liczby naturalne (ograniczenia to ). Dobrym przyporządkowaniem będziemy nazywać przyporządkowanie każdemu wierzchołkowi liczby całkowitej z odpowiadającego mu przedziału w taki sposób, aby liczby przyporządkowane wierzchołkom połączonym krawędzią były względnie pierwsze. Dla przejrzystości oznaczmy, że dla danego przyporządkowania liczba przypisana wierzchołkowi to W zadaniu proszą nas, aby dla każdego wierzchołka obliczyć sumę liczb przyporządkowanych mu we wszystkich dobrych przyporządkowaniach. Wyniki należy podać modulo zatem wszystkie działania będziemy wykonywać modulo ta liczba.
Chwila myślenia i odrobina algorytmicznego rozsądku doprowadzają nas do wniosku, że w zadaniu nie ma na tyle "regularności" (cokolwiek by to znaczyło), aby dało się rozwiązać je inaczej niż poprzez wyznaczenie dla każdego wierzchołka oraz każdej możliwej wartości dla ilu dobrych przyporządkowań zachodzi To właśnie będzie naszym celem.
Jak można się spodziewać, zapewne sporo nam pomoże, jeżeli najpierw ukorzenimy nasze drzewo (w dowolnym wierzchołku). Rozwiązanie będzie bazowało na programowaniu dynamicznym. Oznaczymy przez poddrzewo ukorzenione w wierzchołku a przez liczbę dobrych przyporządkowań, dla których zachodzi gdy ograniczymy się jedynie do wierzchołków z poddrzewa Niewątpliwie dla będących liśćmi zachodzi gdy i dla pozostałych wartości Załóżmy teraz, że chcemy wyznaczyć odpowiedź dla wierzchołka który ma synów i mamy już wyznaczone tablice Liczba dobrych przyporządkowań dla poddrzewa spełniających to iloczyn liczb dobrych przyporządkowań dla poddrzew dla których Zatem jeśli wprowadzimy oznaczenie
(zauważmy, że tak zapisana suma jest dobrze określona, gdyż tylko skończona liczba składników jest niezerowa), to dostaniemy wzór
(1) |
Moglibyśmy po prostu przeiterować po wszystkich możliwych wartościach przyporządkowanych każdemu wierzchołkowi ale wtedy otrzymalibyśmy rozwiązanie co najmniej kwadratowe w zależności od co byłoby zdecydowanie zbyt wolne. Dlatego stajemy przed kluczowym problemem szybkiego wyznaczenia dla ustalonego syna Skoro mowa o względnej pierwszości, to z pewnością warto poznać zbiór (różnych) dzielników pierwszych liczby niech będą to liczby Liczby względnie pierwsze z to wszystkie liczby poza tymi, które są podzielne przez którąkolwiek z liczb Dla mamy co umożliwia szybkie obliczenie żądanej wartości, o ile znalibyśmy wcześniej Życie nie jest jednak takie proste i w ogólnym przypadku nie możemy po prostu odjąć dla gdyż wiele ze składników postaci odjęlibyśmy więcej niż raz (konkretnie tyle razy, ile ma dzielników wśród liczb ).
Jeśli wprowadzimy oznaczenia oraz na zbiór liczb podzielnych przez liczbę to dostaniemy zależność
(2) |
Na ratunek w obliczeniu (2) przychodzi nam zasada włączeń i wyłączeń, a właściwie jej minimalne uogólnienie:
Pomimo przerażającego wyglądu wzór ten jest całkiem prosty, ale jeżeli ktoś nie spotkał się z nim wcześniej, warto się zatrzymać i pokontemplować go przez chwilę, np. przekonując się o jego poprawności dla (Oczywiście, w ogólnej wersji wzoru nie ma znaczenia, czym są konkretnie zbiory i pozostaje on prawdziwy dla dowolnych zbiorów.)
Zatem kolejną rzeczą, którą musimy zbadać, są zbiory postaci Wszystkie liczby są pierwsze, więc jeżeli pewna liczba ma być podzielna przez wszystkie z nich, to musi być podzielna przez ich iloczyn (stwierdzenie odwrotne oczywiście też jest prawdziwe), co prowadzi nas do zależności
Dzięki niej wzór (2) możemy wyrazić następująco:
(3) |
Widać, że wielokrotnie spotykamy się tutaj z podobnymi sumami składników zatem wprowadzając oznaczenie możemy przepisać wzór (3) w trochę milszej postaci:
(4) |
Zauważmy, że jeżeli mamy tablicę to jesteśmy w stanie relatywnie szybko wyznaczyć tablicę A konkretnie, aby obliczyć potrzebujemy wysumować składników. Zatem wykonanie tego dla będzie miało złożoność czasową
Zastanówmy się teraz, jaką złożoność ma wyznaczanie za pomocą wzoru (4). Zauważmy, że występują w nim jedynie składniki postaci gdzie jest pewnym dzielnikiem Ponadto nie wszystkie dzielniki uwzględniamy (tylko takie niepodzielne przez kwadraty liczb pierwszych), ale ta obserwacja nie jest nam potrzebna, aby uzyskać sensowne oszacowanie. Zatem, aby wyznaczyć wartości dla musimy wysumować co najwyżej tyle składników, ile jest łącznie dzielników liczb ale ich liczba jest również rzędu Aby szybko się dostać do tych składników, warto na samym początku programu dla każdej liczby od do spamiętać jej bezkwadratowe dzielniki.
Podsumowując, mając tablice dla wszystkich synów wierzchołka możemy w czasie kolejno wyznaczyć tablice i co pozwoli nam skorzystać ze wzoru (1) do wyznaczenia Zatem w sumarycznym czasie możemy tą metodą wyznaczyć odpowiedź dla korzenia drzewa. Uruchamiając powyższy algorytm dla każdej możliwości wyboru korzenia, możemy rozwiązać zadanie w czasie Jednak autorów zadania taka złożoność nie satysfakcjonuje i wymagane jest rozwiązanie
Pomysł pozwalający obliczać nam kompletne informacje dla wszystkich wierzchołków drzewa (a nie tylko te uwzględniające wierzchołki z odpowiadających im poddrzew), polega na spychaniu informacji z korzenia w dół, przesyłając informacje z "naddrzewa". Niech będzie pewnym wierzchołkiem, a jego ojcem w drzewie. Pamiętamy, że tablica jest iloczynem odpowiednich wartości tablic dla sąsiadów w poddrzewie, dlatego jeżeli mamy kompletną tablicę to aby obliczyć wkład naddrzewa o korzeniu w wierzchołku powinniśmy podzielić wartości z tablicy przez odpowiadające wartości z tablicy przeliczyć jeszcze raz wartości tablic i na podstawie nowych wartości (tablice te teraz lekko zmieniają swoje znaczenie, gdyż zmieniamy zbiór sąsiadów, którzy są uwzględniani w ich liczeniu), co pozwala nam wyliczyć kompletną tablicę (tzn. taką, w której uwzględniony jest też ojciec). Aby wyznaczyć kompletne na podstawie informacji z ojca, potrzebujemy wykonać stałą liczbę przeliczeń tablic, z których każde odbywa się w czasie co daje algorytm o sumarycznej złożoności