Przeskocz do treści

Delta mi!

Informatyczny kącik olimpijski

Wieże

Tomasz Kulczyński

o artykule ...

  • Publikacja w Delcie: marzec 2011
  • Publikacja elektroniczna: 02-03-2011
  • Wersja do druku [application/pdf]: (96 KB)

W tym kąciku zajmiemy się rozwiązaniem zadania pochodzącego z jednej z finałowych rund konkursu TopCoder Open 2010, o nazwie Shrooks on the Board.

obrazek

Zadanie. Jesteśmy proszeni o obliczenie reszty z dzielenia liczby poprawnych ustawień wież na planszy przez zadaną liczbę pierwszą math Plansza jest prostokątna, ma math  wierszy oraz math kolumn. W tym zadaniu wieże są nietypowe: atakują jedynie pola znajdujące się w tym samym wierszu i do tego odległe o co najwyżej math  pól. Poprawne ustawienie to takie, które zawiera co najmniej jedną wieżę i żadna wieża nie atakuje innej.

Zauważmy na początek, że istnieje dokładnie jedno ustawienie, które nie zawiera wież. Będziemy uznawali je za poprawne, a na końcu wynik zmniejszymy o 1. Możemy więc ustawić dowolną liczbę wież w dowolny sposób, byle się nie atakowały. Od tej pory problemy dla poszczególnych wierszy są niezależne. Łatwo zauważyć, że jeżeli wieże w jednym wierszu można ustawić na math  sposobów, to ostatecznym wynikiem będzie reszta z dzielenia liczby math przez  math Do obliczenia tejże reszty wystarczy znać resztę z dzielenia liczby math przez math; koszt czasowy końcowego potęgowania to  math

Cały problem polega więc na wyznaczeniu wartości  math Spróbujmy najpierw wyprowadzić wzór rekurencyjny na  math Mamy następujące możliwości: albo pierwsze pole jest puste, a dalsza część planszy jest jakoś zapełniona wieżami, co daje math  poprawnych ustawień, albo też na pierwszym polu stoi wieża, kolejne math  pól jest pustych, a pozostałe są jakoś zapełnione, co daje dodatkowe math  ustawień. Ostatecznie mamy, że math Pozostaje zaznaczyć, iż dla math przyjmujemy math Daje to prosty algorytm obliczający  math w czasie  math

Inne rozwiązanie otrzymujemy, stosując standardową metodę obliczania wyrazów ciągu zadanego wzorem rekurencyjnym za pomocą szybkiego potęgowania macierzy, patrz artykuł Macierze oczami informatyka Delcie 7/2009. Przypomnijmy tylko, że w metodzie tej przedstawiamy taki oto wektor

display-math

jako iloczyn pewnej macierzy math rozmiaru math i analogicznego wektora zapisanego dla math Wówczas zadanie sprowadza się do obliczenia macierzy  math co możemy wykonać w czasie  math

Spróbujmy teraz podejść do problemu od innej strony. Pokażemy, że sposobów ustawienia math wież w wierszu długości  math jest tyle samo, co wyborów math elementów spośród math elementów. Dla ustalenia uwagi przyjmijmy, że wieża zajmuje swoje pole oraz math  pól na prawo od niego. W takim razie możemy skonstruować bijekcję między ustawieniami wież a wyborami elementów. Mając dane ustawienie wież, po prostu usuwamy math  pól na prawo od każdej wieży poza ostatnią i otrzymujemy wybór math pól spośród math pól. Odwrotnie, mając dany pewien wybór math elementów spośród math elementów, ustawiamy wieże w polach odpowiadających tym elementom i po każdej (poza ostatnią) dokładamy dodatkowo math  wolnych pól.

Żądanych ustawień wież jest zatem math Oczywiście, musi być math czyli

display-math

Aby obliczyć resztę z dzielenia math przez math wystarczy umieć szybko obliczać reszty z dzielenia symboli Newtona przez math W tym celu korzystamy z twierdzenia Lucasa, które orzeka, że jeśli math jest liczbą pierwszą i math to math daje taką samą resztę z dzielenia przez math co math Zauważmy teraz, że zarówno reszty z dzielenia liczb math przez math jak i ich odwrotności modulo math możemy obliczyć w czasie  math – te drugie według wzorów

pict

Wówczas resztę z dzielenia math przez  math obliczamy w czasie stałym, natomiast math ponownie zapisujemy w postaci math i zaczynamy od początku. W ten sposób symbol Newtona math obliczamy w czasie math Możemy więc obliczyć  math wykonując mathoperacji. Można śmiało powiedzieć, że jest to math operacji, gdyż math a zadanie jest interesujące tylko dla  math

Żadne z powyższych rozwiązań nie jest jeszcze satysfakcjonujące. Aby uzyskać lepszy wynik, połączymy ostatnie dwa rozwiązania. Jeśli math to uruchamiamy rozwiązanie z mnożeniem macierzy, a w przeciwnym przypadku rozwiązanie przez symbole Newtona. Ostatecznie, całe zadanie rozwiązujemy w czasie math przy zużyciu pamięci rzędu math