Editorial
Автор: Антон Ципко
Розробник: Марко Гришечкін
Будемо нумерувати біти регістру справа наліво починаючи з . Виходить, що перший справа біт регістру "— біт під номером .
Підзадача 1.
Підзадача розв'язується у запити: , , . Можна здогадатися, що -ий біт регістра відповідає за парність числа. Якщо цей біт рівний нулю, то число парне, якщо рівний одиниці "— непарне. Після операції у регістрі під номером буде навпаки: якщо -ий біт рівний , то число у регістрі було парним, а інакше "— непарним. Після зсунення другого регістру спочатку на позиції вліво, а потім на вправо залишиться лише -ий біт, усі інші стануть рівні . Отже, у другому числі буде записано число , якщо перше було парним, і , якщо перше число було непарним.
Підзадача 2.
Для розв'язання цієї підзадачі потрібно використати таку формулу: . Її коректність легко довести, розглядаючи числа , та їх суму у двійковій системі числення: якщо конкретний біт під номером рівний і в , і в , то до суми додається , а якщо лише в числі або лише в числі , то до суми додається . Операцію множення на двійку можна замінити зсувом уліво на одну позицію. 64 рази зробимо такі операції: , , , . За допомогою формули ми розуміємо, що після будь-якої кількості таких операцій сума перших двох чисел буде рівна сумі двох початкових чисел. Також за операції число у регістрі гарантовано перетвориться на нуль, адже ми рази зсували його вліво. З цього маємо, що число, яке стоятиме на позиції , і буде відповіддю. Тому в кінці робимо операцію setValue(3, 2);
Підзадача 3.
Розв'язавши підзадачу , ми навчилися знаходити суму будь-яких двох чисел. В окремому регістрі ми будемо зберігати відповідь "— кількість одиниць. Будемо називати виділенням біта створення регістру, у якому залишився лише один цей біт, а всі інші перетворилися на . Для виділення біта під номером потрібно зсунути регістр на позиції вліво, а потім на позицій вправо. У такий спосіб ми розглянемо окремо кожен з бітів і просумуємо його з регістром-відповіддю. Ми вміємо знаходити суму двох чисел за ітерації, а цього було не достатньо.
Потрібно помітити, що відповідь не може перевищувати , а отже, зберігається в перших бітах. Тому при додаванні регістрів можна робити не ітерації, а всього . Для меншої кількості операцій ми повинні помітити, що після додавання першого біта до відповіді використовується максимум біт (можна робити всього ітерацію замість ), після додавання двох бітів використовується біти (можна робити ітерації), при додаванні бітів використовується біти (можна робити ітерації) і так далі. Цих прискорень достатньо для отримання повного балу.
Підзадача 4.
Для початку зробимо такі операції: , , . Після таких операцій задача зовсім не зміниться (більше число залишиться більшим), проте числа не будуть мати позицій з однаковими бітами. Після цього ми для обох чисел на позиціях та робимо таке: робимо операцію Or між числом і його копією, зсунутою на позицію вправо, потім Or між новим числом і копією нового, зсунутого на позиції вправо, потім зсунутого на позиції вправо, потім на і так до . Після цього задача знову не зміниться, тому що для кожного числа не виникне одиничного біта, старшого за той, що був найстаршим до операцій. Якщо після цього зробити -ого і -ого чисел, менше число перетвориться на , а більше матиме принаймні один біт, рівний 1. Тепер якщо проробити такі ж операції знову, але зсувати вліво, то менше число залишиться -ем, а більше число матиме -ий біт, рівний . Тому якщо після цього зсунути числа на позицій вправо, більше число перетвориться на , а менше на .
Підзадача 5.
Будемо зберігати відповідь в окремому регістрі. Зробимо ітерації. Спочатку зробимо операцію or поточної відповіді і її ж зсунутої на позицію вліво. Також на ітерації ми відокремимо біт із початкового числа й поставимо його найстаршим бітом зсунувши, на позицій вліво. Для кожного біта зробимо рази такі операції: результат операції and регістру біта та поточної відповіді прооримо (зробимо операцію ) з певним іншим регістром, у якому будемо зберігати відповідь для ітерації , а сам біт зсунемо на одну позицію вправо. Після цих операцій потрібно проорити регістр із відповіддю на -ій ітерації та на -ій. Якщо біт був одиницею, то на ітерації відповідь буде мати ще одиницю, приписану зліва від уже записаних. Після ітерацій усі одиничні біти числа зсунуться вправо (те, що і потрібно в задачі, якщо переформулювати).
Підзадача 6.
Розв'язавши підзадачу , ми навчилися перетворювати максимум з двох чисел на , а мінімум на . Також ми вміємо перетворювати число у число за допомогою зсувів на , , , , позиції вліво. Якщо проендити (зробити операцію and) числа та і відповідні початкові числа, тоді максимальне число залишиться тим самим, а мінімальне перетвориться на . Прооривши їх (зробивши операцію or), отримаємо максимум. Зробивши аналогічні операції, але інвертувавши (операція setNot) та , ми знайдемо мінімум. Тепер ми можемо отримати мінімум і максимум серед двох чисел. Тому ми можемо написати сортування бульбашкою, адже для двох чисел можемо в перше число записати мінімум, а в друге число записати максимум, а саме це і потрібно для алгоритму сортування.