Свеска
Аутор: Иван Стошић
Текст и тест примери: Иван Стошић
Анализа решења: Слободан Митровић
Тестер: Слободан Митровић
Укратко, решење задатка ће изгледати овако. Користићемо сегментно стабло да одговоримо на упите. Листови у сегментном стаблу ће бити једнаки низу a. Чвор у стаблу који је отац листова a_i, \ldots, a_j ће садржати најмањи број који може бити записан на табли након коначно много потеза игре са бројевима a_i, \ldots, a_j. Овај приступ намеће два главна питања:
- Зашто је за сваки чвор довољно чувати само најмањи број који се може добити играјући се његовим синовима?
- Ако је чвор v отац чворова L и R, како искомбиновати вредности сачуване у L и R да се добије вредност за чвор v?
Да бисмо одговорили на ова питања, посматраћемо овај проблем као задатак са полиномима над \mathbb{Z}_2.
Полиноми над \mathbb{Z}_2 уместо бројева
За број a дефинишемо полином P_a(x) над \mathbb{Z}_2 на следећи начин: P_a(x) садржи x^i акко је i-ти бит броја a једнак 1. Приметимо да ако P_a(x) садржи x^i, онда је коефицијент испред x^i тачно 1. На пример, P_5(x) = x^2 + 1, јер у бинарном запису број 5 је једнак 101.
Тада број а\text{ xor }b одговара полиному P_a(x) + P_b(x). Број 2 \cdot a одговара полиному x \cdot P_a(x), и број \frac a 2 одговара полиному \frac {P_a(x)} x (под условом да је a дељиво са 2).
Задатак без операције дељења
Прво ћемо посматрати задатак у случају да опција дељења није на располагању, тј. можемо да користимо само xor и множење (или, у свету полинома, ако користимо сабирање и множење са x).
Приметимо да за било који полином Q(x) над \mathbb{Z}_2, ако почнемо од полинома P_a(x) можемо добити полином P_a(x) * Q(x) применом само ове две операције. На пример, да бисмо добили P_a(x) * Q(x) почевши од P_a(x), прво за свако x^i које има позитиван коефицијент у Q(x) направимо полином x^i * P_a(x). Потом саберемо све те полиноме.
Нека је A скуп полинома. Из Безуовог става сада следи да се применом ове две операције на скуп полинома из A може добити NZD(A) (највећи заједнички делилац полинома из A). Пошто се сабирањем два (различита) полинома који су дељиви са NZD(A) добија полином дељив са NZD(A) (који је једнак или већи од NZD(A)), и такође пошто се множењем полинома са x добија полином дељив са NZD(A), то значи да је NZD(A) најмањи полином који можемо добити применом ове две операције.
Такође, ако имамо два скупа полинома A и B, тада важи NZD(NZD(A), NZD(B)) = NZD(A \cup B). Ово оправдава приступ описан на почетку у којем, да бисмо израчунали најмањи записан број у неком интервалу (за који смо у случају да користимо само две операције закључили да одговара NZD вредности одговарајућих полинома), смо користили сегментно стабло.
Задатак са операцијом дељења
Досадашњи опис решења овог задатка је подразумевао да не користимо операцију дељења са x. Пошто ова операција практично каже да крајњи полином не треба да буде дељив са x (иначе није најмањи), дељење можемо применити тек на крају или чак у сваком кораку током примене NZD операција. NZD полиноми које добијамо у међу-корацима би се у зависности од примене дељења разликовали само у дељивости са x, што је небитно за крајњи резултат. Дакле, дељење са x је довољно применити само на крајњи резултат и игнорисати до тада.
Смернице за алгоритам
Имплементација било која од два упита у сегментом стаблу захтева O(\log{n}) NZD операција.
Да бисмо нашли NZD два полинома P_a(x) и P_b(x) можемо да искористимо неки од стандардних алгоритама, на пример, Еуклидов алгоритам. Тај цео процес у нашем случају, где такође имамо операцију дељења, може да се уради на једноставан начин. Наиме, почнемо од a и b, и све док су оба различита од 0 радимо следеће. Нека је a \leq b, и нека је c број добијен од a множењем (“shift”-овањем) са 2 све док се c и b разликују по дужини у бинарном запису. Потом b заменимо са b\text{ xor }c, и наставимо процес. Кад један од бројева a или b постане 0, вратимо не-нула међу њима на који је примењено дељење са 2 док је то могуће.