Поразрядные операции (олимпиадное программирование)
← К списку тем
Глава 02 · Техника программирования

Поразрядные операции

Число можно рассматривать как ряд нулей и единиц. Поразрядные операции позволяют проверять и менять отдельные биты, а ещё хранить небольшое множество в одном числе.

  • Биты и позиции
  • И, ИЛИ, XOR, НЕ
  • Сдвиги и маски
  • Практика CSES и Codeforces

Сначала разберём запись числа

13 в двоичной системе записывается как 1101: единицы стоят на местах с весом 8, 4 и 1. Позиции нумеруются справа налево, начиная с нуля.

Позиция 3 2 1 0
Вес 8 4 2 1
Бит числа 13 1 1 0 1

1101₂ = 8 + 4 + 0 + 1 = 13₁₀. Маска 1 << k оставляет единицу только в позиции k.

Что делают операторы

Возьмём четырёхбитные записи A = 1010₂ (10) и B = 1100₂ (12). Операция выполняется над каждой парой битов на одной позиции.

Операция Запись Правило для каждого бита Результат
И A & B 1, если обе цифры равны 1 1000₂ = 8
ИЛИ A | B 1, если хотя бы одна цифра равна 1 1110₂ = 14
XOR A ^ B 1, если цифры различаются 0110₂ = 6

~ меняет нули и единицы местами. Если рассматривать только четыре младших бита, то ~1010₂ → 0101₂. Для такого результата в обоих языках запишем (~A) & 15: число 15 = 1111₂ ограничивает результат четырьмя битами.

A << 1 сдвигает биты влево и даёт 10100₂ = 20. A >> 1 сдвигает их вправо и даёт 0101₂ = 5. Для неотрицательных чисел это соответствует умножению или целочисленному делению на два, если результат помещается в выбранный тип.

C++
#include <iostream>
using namespace std;

int main() {
 unsigned int a = 10, b = 12;
 cout << (a & b) << ' ' << (a | b)
 << ' ' << (a ^ b) << '\n';
 cout << (a << 1) << ' '
 << (a >> 1) << '\n';
 cout << ((~a) & 15u) << '\n';
}
Python
a, b = 10, 12

print(a & b, a | b, a ^ b)
print(a << 1, a >> 1)
print((~a) & 15)

Вывод обеих программ: 8 14 6, затем 20 5, затем 5. У Python целые числа не ограничены четырьмя битами, а C++ использует фиксированную ширину типа. Поэтому при демонстрации ~ мы явно ограничиваем результат маской.

Маска: проверяем и меняем один бит

Пусть x = 1010₂ (10). Хотим работать с битом позиции 2 — это третий бит справа. Его маска равна 1 << 2 = 0100₂.

Что сделать Выражение Результат
Проверить x & mask 0: бит сейчас выключен
Установить в 1 x | mask 1110₂ = 14
Сбросить в 0 x & ~mask 1010₂ = 10
Поменять на противоположный x ^ mask 1110₂ = 14
C++
#include <iostream>
using namespace std;

int main() {
 unsigned int x = 10;
 unsigned int mask = 1u << 2;
 cout << ((x & mask) != 0u) << '\n';
 cout << (x | mask) << '\n';
 cout << (x & ~mask) << '\n';
 cout << (x ^ mask) << '\n';
}
Python
x = 10
mask = 1 << 2

print(int((x & mask) != 0))
print(x | mask)
print(x & ~mask)
print(x ^ mask)

Оба примера выводят 0, 14, 10 и 14. В каждом выражении используется исходное значение x; результаты операций не записываются обратно в x.

Проверь себя: какой маской проверить самый правый бит?

1 << 0, то есть 0001₂. Выражение x & 1 равно 1 для нечётного x и 0 для чётного.

Тренажёр: собери два числа из битов

Нажимай на биты чисел A и B, выбери операцию и сначала запиши четыре бита ответа. Затем проверь себя. Биты расположены от позиции 3 слева до позиции 0 справа.

A
0101₂ = 5
B
0011₂ = 3

Результат: ????

 

Если тренажёр не запустился: ответ для начальных чисел

Для A = 0101₂ и B = 0011₂: И → 0001₂, ИЛИ → 0111₂, XOR → 0110₂.

Число как небольшое множество

Договоримся: единица в позиции k означает, что элемент k входит в множество. Тогда 0101₂ описывает множество {0, 2}, а 0110₂ — {1, 2}.

0101₂∪0110₂=0111₂

Объединение получают операцией ИЛИ: 0101 | 0110 = 0111, то есть {0, 1, 2}. Пересечение получают операцией И: 0101 & 0110 = 0100, то есть {2}.

Мини-задание: какой битовой маской записать множество {1, 3}?

1010₂: единицы стоят в позициях 3 и 1.

Практика CSES

Код Грея (Gray Code)

Условие по-русски. По данному числу n составь последовательность из 2ⁿ различных двоичных строк длины n. Любые две соседние строки должны отличаться ровно в одной позиции. Можно вывести любую последовательность, которая выполняет эти условия.

Ввод: одно число n, где 1 ≤ n ≤ 16. Вывод: 2ⁿ строк, каждая на отдельной строке. Лимиты: 1 с, 512 МБ.

Пример ввода
2
Один допустимый вывод
00
01
11
10

Подсказка для проверки: если выполнить XOR двух соседних строк, в результате должна остаться ровно одна единица.

Открыть Gray Code и отправить решение ↗

Проверь свою последовательность

Составь собственный код Грея для n = 2 или n = 3. Запиши каждую строку с новой строки; проверка покажет, выполнены ли все правила задачи.

 

Пример последовательности для n = 2

00 → 01 → 11 → 10. Каждая из четырёх строк встречается один раз; между соседними строками меняется один бит.

Задачи по теме

Где применяются биты: задачи Codeforces

В одних задачах поразрядная операция даёт ключевую идею, в других маска помогает удобно записать очередной вариант перебора. Таблица идёт от простых примеров к задачам, требующим дополнительных алгоритмов.

Задача Что требуется Где работают биты
579A — Raising Bacteria ↗Разведение бактерий

1000

Разбор задачи

Получить ровно x бактерий, добавив за всё время как можно меньше новых. Минимальное число добавлений равно количеству единиц в двоичной записи x. Например, у 5 = 101₂ две единицы.
467B — Fedor and New Game ↗Фёдор и новая игра1100 Посчитать игроков, чьи армии отличаются от армии Фёдора не более чем по k видам солдат. Операция x ^ f отмечает единицами различающиеся биты армий. Нужно сосчитать их и сравнить результат с k.
1097B — Petr and a Combination Lock ↗Пётр и кодовый замок1200 Для каждого поворота выбрать направление и проверить, можно ли вернуться к нулю на шкале в 360°. Бит i маски может означать направление i-го поворота. При n ≤ 15 перебирают все 2ⁿ вариантов.
550B — Preparing Olympiad ↗Подготовка олимпиады1400 Посчитать наборы минимум из двух задач, подходящие по суммарной сложности и разнице между самой лёгкой и самой трудной. Единица в позиции i означает, что задача i выбрана. Маска задаёт один набор; при n ≤ 15 можно проверить все наборы.
276D — Little Girl and Maximum XOR ↗Девочка и максимальный XOR1700 Найти наибольшее a ^ b для двух чисел из отрезка [l, r]. Нужно рассмотреть старший разряд, в котором числа могут различаться. Перебор всех пар не подходит: r достигает 10¹⁸.
339D — Xenia and Bit Operations ↗Ксения и битовые операции1700 После изменений элементов массива заново находить результат последовательных операций ИЛИ и XOR. Операции чередуются на уровнях дерева отрезков. Эту задачу лучше решать после знакомства с деревом отрезков.

Начни с первых четырёх задач. В 1097B и 550B варианты можно перебирать и рекурсией: битовая маска здесь удобна для записи выбора. Числа в зелёных метках — рейтинг сложности Codeforces.

По мотивам разделов 2.3.1–2.3.2 книги А. Лааксонена «Олимпиадное программирование». Лицензия CSES Problem Set: CC BY-NC-SA 4.0. Краткие описания задач Codeforces составлены по их официальным страницам, ссылки указаны в таблице.

Категория: Algorithms | Добавил: bzfar77 (Сегодня)
Просмотров: 6 | Теги: поразрядные операции, codeforces, битовые операции, gray code, маски и сдвиги | Рейтинг: 0.0/0
Всего комментариев: 0
avatar