e-olymp 27. Циклические сдвиги

Задача

Циклический сдвиг

Запишем целое десятичное число $n$ в двоичной системе счисления и образуем все левые циклические сдвиги числа $n$, у которых первая цифра числа переносится в конец.

Например, если $n = 11$, то в двоичной системе это $1011_2$, его циклические сдвиги: $0111_2$, $1110_2$, $1101_2$, $1011_2$. Максимальное значение $m$ у всех полученных таким образом чисел будет иметь число $1110_2 = 14_{10}$.

Для заданного числа $n$ определить максимальное значение $m$.

Входные данные: одно число $n (1 ≤ n ≤ 2\cdot 10^9)$.

Выходные данные: искомое число $m$.

Тесты

Входные данные Выходные данные
11 14
23 30
256 256
257 384
34132 43664

Код программы

Решение задачи

  1. Сначала мы находим степень двойки, большую данного числа;
  2. Далее мы циклически сдвигаем влево данное число на один бит и из полученных чисел выбираем наибольшее.

Ссылки

Условие задачи на e-olymp.com
Решение задачи на ideone.com

BitSegmentedArray

Мне предоставили реализовать класс, который наследует SegmentedArray и характеризуется следующим:

  • принимает большой массив
  • элементы массива — 0 и 1
  • частые запросы get() и set()

Чтобы была возможность хранить очень большой массив, я решила построить класс с помощью битовых операций. Это было возможно, так как элемент моего массива — это 0 и 1, и любое число можно представить в виде последовательности нулей и единиц. Поэтому внутри класса если массив long, в одной ячейке которого может храниться до 64 элементов. Так была решена проблема с расходом памяти.

Из-за представления чисел в двоичном коде, к ним легко применимы битовые операции. Например, для реализации метода get() достаточно использовать битовый сдвиг и битовое «и». Следовательно, метод работает за О(1).

В реализации метода set() мне помогла блочная структура (64 элемента в 1 ячейке массива). А именно: я проверяю границы заданного отрезка, а ячейки, которые попали внутрь полностью просто приравниваю константе: 0 — если все элементы ячейки нули, -1 — если все элементы ячейки единицы.

Точно так же реализован метод add().

С методом min() всё и того проще: ответом служит либо 0, либо 1. Первый же ноль, встреченный на заданном отрезке, определяет ответ.

Также я предположила, что, как и в большинстве олимпиадных задач, индексация массива начинается с единицы. То есть в методе get(), set(), add() и min() необходима сначала уменьшить каждый индекс на единицу, а лишь потом проводить операции над массивом, так как индексация в нём начинается с нуля.

Теоретическая оценка сложности методов:

  • get(i): O(1);
  • set(x, y, val): O((y-x)/64);
  • add(x, y, val): O((y-x)/64) — для val%2==1, и O(1) — для val%2==0;
  • min(x, y) — O((x-y)/64) — в худшем случае (на отрезке все элементы нули), O(1) — в лучшем случае (на отрезке все элементы единицы).

Код программы:

Протестировать программу можно здесь.

Простейшая реализация интерфейса: SimpleSegmentedArray.

Сравнение скорости работы методов: