Autorzy zadań do Potyczek Algorytmicznych trzymają naprawdę wysoki poziom. Jak oni wymyślają te zadania? Nie wiem.
Krótkie streszczenie treści zadania
W zadaniu Wielki Zderzacz Termionów mamy trzy rodzaje cząstek elementarnych: Czerwony (C), Zielony (Z) i Niebieski (N). Cząstki elementarne ustawia się w rzędzie i reagują ze sobą. Dwa sąsiednie termiony Czerwone mogą zamienić się w jeden Zielony, a dwa sąsiednie Zielone w jeden Czerwony. Niebieski to taki joker, który zamienia się w Czerwony lub Zielony. Niebieskie zamieniają się jako pierwsze i w efekcie dostajemy ciąg złożony z Czerwonych i Zielonych. Ciąg złożony z Czerwonych i Zielonych nazywamy prawidłowym jeśli istnieje kolejność łączenia termionów w taki sposób, aby na końcu otrzymać jeden termion.
W zadaniu pytano o to na ile różnych sposobów można zamienić Niebieskie termiony na Czerwone i Zielone, aby otrzymać ciąg prawidłowy. Następnie na ciągu przeprowadzamy q operacji polegających na zastąpieniu pewnego termionu przez inny. Po każdej takiej operacji należało ponownie odpowiedzieć na to pytanie. Liczbę sposobów podajemy modulo 109+7.
Ciągi alternujące
W pierwszej części rozwiązania zastanowimy się, kiedy ciąg złożony jedynie z termionów C i Z jest poprawny.
Ciąg nazywamy alternującym, jeśli na przemian występują w nim termiony Czerwone i Zielone. Na przykład ciąg CZCZCZC
i ZCZ
są alternujące, natomiast ciągi ZZCC
i CZCZZC
nie są alternujące.
Spostrzeżenie: W ciągu termionów nie możemy wykonać żadnej operacji wtedy i tylko wtedy, gdy ciąg jest alternujący. Dla każdego n
istnieją dokładnie dwa ciągi alternujące.
Niech #C
#Z
i #N
oznacza kolejno: liczbę Czerwonych, Zielonych i Niebieskich termionów w ciągu. Ponadto długość ciągu oznaczmy jako n
.
Jeśli n = 1
to każdy ciąg (są dwa takie ciągi: C
oraz Z
) jest poprawny. W każdym z nich możemy wykonać zero operacji i otrzymamy w ten sposób jeden termion na końcu.
Lemat: Dla n ≥ 2
ciąg o n
elementach NIE jest poprawny wtedy i tylko wtedy, gdy jest alternujący lub gdy 3 dzieli n + #C
.
Dowód (indukcyjny): Istnieją 4 ciągi dla n = 2
: CC
, CZ
, ZC
oraz ZZ
. Ciągi CC
oraz ZZ
są poprawne, a ciągi CZ
oraz ZC
są niepoprawne. Policzmy dla każdego z tych ciągów ile wynosi n + #C
:
- Dla
CC
mamy#C = 2
, a zatemn + #C = 4
, 3 nie dzielin + #C
i ciąg jest poprawny - Dla
CZ
mamy#C = 1
, a zatemn + #C = 3
, 3 dzielin + #C
i ciąg nie jest poprawny - Dla
ZC
mamy#C = 1
, a zatemn + #C = 3
, 3 dzielin + #C
i ciąg nie jest poprawny - Dla
ZZ
mamy#C = 0
, a zatemn + #C = 2
, 3 nie dzielin + #C
i ciąg jest poprawny
Ponadto ciągi CZ
oraz ZC
są alternujące, więc lemat jest prawdziwy dla n = 2
.
Weźmy dowolne n > 2
. Jeśli ciąg jest alternujący to nie da się wykonać żadnego ruchu i ciąg nie jest poprawny. Załóżmy więc, że ciąg nie jest alternujący, możemy wykonać zatem w nim pewien ruch. Rozważmy dwa przypadki:
Jeśli 3 dzieli n + #C
to wykonajmy pewien dozwolony ruch. Niech n'
oraz #C'
oznaczają odpowiednio długość ciągu oraz liczbę Czerwonych termionów po wykonaniu tego ruchu. Jeśli zamieniliśmy dwa termiony Zielone na jeden Czerwony, to liczba termionów zmniejszyła się o jeden, a liczba termionów Czerwonych wzrosła o jeden: n' = n - 1
oraz #C' = #C + 1
. Zatem 3 dzieli n' + #C'
bo n' + #C' = n-1 + #C + 1 = n + #C
. Jeśli z kolei zamienimy dwa termiony Czerwone na jeden zielony, to liczba Czerwonych termionów zmniejszyła się o dwa: n' = n - 1
oraz #C = #C - 2
. Zatem 3 dzieli n' + #C
bo n' + #C = n - 1 + #C - 2 = n + #C - 3
.
Dowód gdy 3 nie dzieli n + #C jest niemal identyczny (wykonanie jednego kroku nie zmienia reszty z dzielenia przez trzy wartości n + #C
). Pozostaje nam udowodnić, że po wykonaniu ruchu nie trafimy na ciąg alternujący. Bez straty ogólności powiedzmy, że zamieniliśmy dwa termiony C i otrzymaliśmy ciąg alternujący ...CZCZCZC...
gdzie pogrubiony termion Z to termion nowopowstały. Zatem ciąg przed zamianą wyglądał tak: ...CZCCCCZC...
i zamiast pogrubionych termionów mogliśmy zamienić te termiony: ...CZCCCCZC...
otrzymując ciąg ...CZZCCZC...
, który nie jest alternujący. Sztuczka nie zadziała jeśli n = 2
, ale założyliśmy, że n > 2
. Nie zadziała też jeśli n = 3
, ale wtedy ciąg wygląda tak: CCC
(lub tak ZZZ
) i w tym przypadku 3 dzieli n + #C
(a założyliśmy że nie dzieli). Co kończy dowód.
Pytanie: Czy wystarczy nam warunek o tym, że 3 dzieli n + #C
? Innymi słowy: czy o ciągach alternujących możemy zapomnieć? No cóż… i tak i nie… To pytanie może wydawać się naiwne, ale jest ważne, bo za chwilę będziemy zliczać ile ciągów poprawnych może postać po zamianie termionów Niebieskich na termiony C i Z i musimy się upewnić, że żadnych z ciągów nie policzymy dwukrotnie.
Lemat: O ciągach alternujących możemy zapomnieć dla n parzystych i nie możemy o nich zapominać dla n nieparzystych.
Dowód: Rozważamy dwa przypadki. W pierwszym z nich załóżmy, że n
jest parzyste i niech k
będzie taką liczbą naturalną, że n = 2k
. Wtedy w każdym ciągu alternującym #C = k
i #Z = k
. Zatem n + #C = 2k + k = 3k
, zatem 3 dzieli n + #C
.
Jeśli n jest nieparzyste, to niech k
będzie taką liczbą naturalną, że n = 2k + 1
. Wtedy #C
jest równe albo k
albo k + 1
wtedy n + #C
jest równe albo 2k + 1 + k = 3k + 1
albo 2k + 1 + k + 1 = 3k + 2
. W żadnym z tych przypadków 3 nie dzieli n + #C
. Co kończy dowód.
W tym rozdziale dogłębnie przeanalizowaliśmy ciągi złożone z termionów C i Z. Pomoże to nam zliczyć liczbę sposobów zamian termionów Niebieskich po których ciąg jest poprawny.
Termiony Niebieskie
Załóżmy teraz, że mamy podany już ciąg termionów wraz z termionami Niebieskimi. Aby móc obliczyć na ile sposobów można zamienić termiony Niebieskie, aby otrzymać ciąg poprawny potrzebujemy znać:
- długość ciągu
- liczbę Czerwonych termionów (
#C
) - liczbę Niebieskich termionów (
#N
) - czy ciąg można zamienić na ciąg alternujący postaci
CZCZ...
- czy ciąg można zamienić na ciąg alternujący postaci
ZCZC...
Długość ciągu się nie zmienia. Liczbę Czerwonych i Niebieskich termionów można łatwo aktualizować gdy zmienia się ciąg (przypominam: w zadaniu ciąg będzie się zmieniał w ten sposób, że jeden termion w ciągu zostanie zamieniony na inny). W jaki sposób można łatwo aktualizować to czy ciąg można zamienić na ciąg alternujący? Jest to dość prosta i dobrze znana sztuczka: zamiast pamiętać czy się da czy się nie da, pamiętamy ile termionów przeszkadza nam do tego, aby ciąg można było zamienić na ciąg alternujący. Dla przykładu w ciągu CZNC
istnieje jeden termion, który przeszkadza ciągowi w byciu ciągiem alternującym CZCZ
. Innymi słowy: ilość termionów, które nam przeszkadzają to liczba termionów Z
stojących na miejscu, gdzie powinno stać C
oraz liczba termionów C
, które stoją tam, gdzie stać powinno Z
. Taką liczbę łatwo uaktualniać z każdą zmianą ciągu, a aby sprawdzić czy ciąg można zamienić na alternujący, wystarczy sprawdzić czy ta wartość jest równa zero.
Liczbę sposobów na które można zamienić termiony N (na ciągi poprawne i niepoprawne) to po prostu 2#N. Wartość tą możemy policzyć za pomocą algorytmu szybkiego potęgowania. Następnie możemy odjąć od tej wartości ciągi niepoprawne. Aby to zrobić musimy policzyć na ile sposobów da się wybrać z #N k termionów, aby k miało odpowiednią resztę z dzielenia przez 3. Dla przypomnienia: liczba możliwych wyborów k termionów spośród n, jest równa symbolowi Newtona C(n, k). Zatem chcemy umieć liczyć następujące sumy:
- S(n, 0) := C(n, 0) + C(n, 3) + C(n, 6) + … + C(n, 3 ⌊n /3⌋)
- S(n, 1) := C(n, 1) + C(n, 4) + C(n, 7) + … + C(n, 3 ⌊n /3⌋ + 1)
- S(n, 2) := C(n, 2) + C(n, 5) + C(n, 8) + … + C(n, 3 ⌊n /3⌋ + 2)
Nie jesteśmy niestety tych wzorów po prostu policzyć w ten sposób, ze względu na złożoność naszego rozwiązania. Na szczęście te wzory można policzyć dużo łatwiej:
- S(n, 0) = (2n + a(n+1)) / 3
- S(n, 1) = (2n + a(n+5)) / 3
- S(n, 2) = (2n + a(n+3)) / 3
Gdzie ciąg a = (1, 2, 1, -1, -2, -1, 1, 2, 1, -1, -2, -1, 1, 2, 1, -1, -2, -1, ... )
(ciąg jest okresowy, okres równy 6). Ponieważ ciąg chcemy liczyć modulo 109+7 musimy policzyć modularną odwrotność 3 (jest ona równa 333333336). 2 podniesione do potęgi n
liczymy za pomocą szybkiego potęgowania.
Nie zapominajmy o tym, że jeśli n jest nieparzyste, musimy odjąć jeszcze ciągi alternujące jeśli można je uzyskać oraz o tym, że dla n = 1 ciągi alternujące tworzą ciągi poprawne.