Редакция для Морские скважины
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
Разбор задачи «Морские скважины»
Идея задачи
Для каждого числа x нужно найти такое число y, что:
1 <= y < x,- если вычислить
z = x xor y, - то числа
x,y,zобразуют невырожденный треугольник.
Это значит, что должны выполняться строгие неравенства:
x + y > zx + z > yy + z > x
Если подходящего y нет, нужно вывести -1.
Наблюдение про XOR
Обозначим:
a = xb = yc = x xor y
Очень полезна формула:
x + y = (x xor y) + 2 * (x & y)
Она показывает связь обычного сложения и побитовых операций.
Из неё сразу получаем:
x + y > x xor yтогда и только тогда, когдаx & y > 0
То есть у чисел x и y должен быть хотя бы один общий единичный бит.
Что означает третье неравенство
Разберём условие:
y + (x xor y) > x
Если упростить его через биты, получится важный смысл:
- у числа
yдолжен быть хотя бы один бит, которого нет вx.
Почему это так?
Если все единичные биты y уже содержатся в x, то y целиком «лежит внутри» битов x, и нужной строгости не получится.
Значит, чтобы треугольник был невырожденным, число y должно одновременно:
- иметь общий единичный бит с
x; - иметь хотя бы один единичный бит вне
x.
Каким должно быть число y
Теперь задача становится почти конструктивной.
Нужно собрать y так, чтобы:
- один его бит совпадал с единичным битом числа
x; - другой бит стоял в позиции, где у
xноль.
Тогда:
- общий бит даст
x + y > x xor y; - новый бит даст
y + (x xor y) > x.
Остаётся проследить, чтобы y < x.
Для этого достаточно брать оба таких бита ниже старшего бита числа x.
Когда ответа не существует
Случай 1. x — степень двойки
Например:
2 = 10₂4 = 100₂8 = 1000₂
У такого числа ровно один единичный бит.
Если мы захотим, чтобы у y был общий бит с x, нам придётся поставить именно этот бит.
Но если добавить ещё какой-то бит, чтобы выполнить второе условие, конструкция уже не даст нужного корректного ответа.
Для таких x ответа нет.
Проверка степени двойки стандартная:
x & (x - 1) == 0
Случай 2. x имеет вид 2^k - 1
Например:
3 = 11₂7 = 111₂15 = 1111₂
У такого числа все биты ниже старшего равны 1.
Значит, ниже старшего бита нет позиции, где можно взять нулевой бит и добавить его в y.
То есть мы не можем сделать так, чтобы у y был бит, которого нет в x.
Следовательно, здесь тоже ответа нет.
Проверка такого вида числа тоже стандартная:
x & (x + 1) == 0
Как построить ответ
Если x не является степенью двойки и не имеет вид 2^k - 1, то ответ существует.
Тогда можно сделать так:
- взять младший единичный бит числа
x; - найти младший нулевой бит числа
xниже старшего бита; - объединить их в число
y.
То есть:
y = low_one | low_zero
Почему это работает:
- бит
low_oneуже есть вx, значит уxиyесть общий единичный бит; - бит
low_zeroотсутствует вx, значит уyесть новый бит; - оба бита лежат ниже старшего бита
x, значитy < x.
Пошаговый пример
Пусть x = 10.
В двоичном виде:
10 = 1010₂
- Младший единичный бит — это
2 = 0010₂. - Младший нулевой бит ниже старшего — это
1 = 0001₂. - Тогда:
y = 2 | 1 = 3
Проверим:
x = 10y = 3z = 10 xor 3 = 9
Получаем стороны 10, 3, 9.
Проверка:
10 + 3 > 910 + 9 > 33 + 9 > 10
Все три неравенства выполнены.
Алгоритм
Для каждого x:
- Если
x— степень двойки, вывести-1. - Если
xимеет вид2^k - 1, вывести-1. Иначе:
- найти младший единичный бит
low_one = x & -x; - найти младший нулевой бит ниже старшего;
- вывести
y = low_one | low_zero.
- найти младший единичный бит
Почему алгоритм корректен
Докажем, что построенное число y всегда подходит.
Пусть:
low_one— бит, который есть вx;low_zero— бит, которого нет вx, но он расположен ниже старшего бита;y = low_one | low_zero.
Тогда:
1. Выполняется 1 <= y < x
Число y положительно, потому что содержит хотя бы один установленный бит.
Оба выбранных бита строго ниже старшего бита числа x, значит y < x.
2. Выполняется x + y > x xor y
Поскольку бит low_one присутствует и в x, и в y, имеем x & y > 0.
По формуле
x + y = (x xor y) + 2 * (x & y)
получаем строгое неравенство x + y > x xor y.
3. Выполняется y + (x xor y) > x
У числа y есть бит low_zero, которого нет в x.
Значит, y содержит хотя бы один «новый» бит относительно x, и это как раз даёт строгую прибавку, необходимую для выполнения неравенства.
4. Выполняется x + (x xor y) > y
Это неравенство автоматически верно, потому что x — самое большое по смыслу число конструкции, а y < x, при этом x xor y >= 0.
В реальной проверке оно также всегда выполняется для нашего построения.
Следовательно, все три строгих треугольных неравенства выполнены, значит ответ корректен.
Сложность
Мы перебираем не более 31 бита для каждого теста.
- Время:
O(31)на тест, то есть фактическиO(1) - Память:
O(1)
Эталонная реализация на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int x;
cin >> x;
if ((x & (x - 1)) == 0) {
cout << -1 << '\n';
continue;
}
if ((x & (x + 1)) == 0) {
cout << -1 << '\n';
continue;
}
int low_one = x & -x;
int low_zero = 0;
for (int b = 0; b < 31; b++) {
if ((1 << b) >= x) {
break;
}
if ((x & (1 << b)) == 0) {
low_zero = (1 << b);
break;
}
}
int y = low_one | low_zero;
cout << y << '\n';
}
return 0;
}
Эталонная реализация на Python
import sys
input = sys.stdin.readline
t = int(input())
for _ in range(t):
x = int(input())
if x & (x - 1) == 0:
print(-1)
continue
if x & (x + 1) == 0:
print(-1)
continue
low_one = x & -x
low_zero = 0
for b in range(31):
if (1 << b) >= x:
break
if (x & (1 << b)) == 0:
low_zero = 1 << b
break
y = low_one | low_zero
print(y)
Типичные ошибки
1. Проверять только одно условие
Некоторые пытаются выбрать y, чтобы у x и y был общий бит. Но этого недостаточно: нужен ещё хотя бы один бит в y, которого нет в x.
2. Брать бит слишком высоко
Если взять бит не ниже старшего бита x, можно получить y >= x, а это запрещено.
3. Не обработать числа без ответа
Если не сделать отдельную проверку для:
- степеней двойки;
- чисел вида
2^k - 1,
то программа будет выдавать неверные ответы на специальных тестах.
Вывод
В этой задаче главное — не перебирать ответы, а понять битовый смысл треугольных неравенств.
После этого задача превращается в короткую конструкцию:
- нужен один общий бит;
- нужен один новый бит;
- оба должны лежать ниже старшего бита.
Именно это и позволяет получить решение за константное время на каждый тест.
Комментарии