1651. Максимальный XOR

 

Задан набор целых чисел a1a2, ..., an. Для заданного числа x найдите такое ai, что x xor ai максимально.

 

Вход. Первая строка содержит количество чисел n (n ≤ 105) и количество запросов q. Вторая строка содержит целые числа a1, a2, ..., an (0 ≤ ai ≤ 1018). Каждая из следующих q строк содержит одно число x (0 ≤ x ≤ 1018).

 

Выход. Для каждого значения x выведите в отдельной строке такое значение ai, для которого x xor ai максимально.

 

Пример входа

Пример выхода

5 6
5 3 7 2 6
1
2
4
5
3
6
6
5
3
2
5
3

 

 

РЕШЕНИЕ

бор

 

Анализ алгоритма

Занесем все входные числа a1a2, ..., an в бинарный бор. Каждая вершина бора имеет два сына, которые соответствуют битам 0 и 1. Поскольку ai ≤ 1018, 260 > 1018, то каждое ai содержит не более 61 бита (которые нумеруются от 0 до 60).

Теперь покажем как для заданного x найти такое ai, что x xor ai максимально. Пусть x = b1b2bnдвоичное представление числа. Если существует такое i что ai = (через  обозначен инвертированный бит bk), то двоичное представление x xor ai будет состоять из n единиц и является наибольшим из возможных. Двигаемся по бору последовательно по битам . Если для некоторого k в боре пути по биту  нет, то двигаемся по биту bk. В конце прохода по битам x прийдем в вершину, которой соответствует искомое ai.

 

Пример

Числа во входном примере содержат не более 3 бит. Построим бинарный бор, где каждое число представлено тремя битами.

Пусть x = 6 = 1102. Инвертируем биты: inv(1102) = 0012. Двигаемся по бору:

·        первый шаг: бит 0 в боре есть;

·        второй шаг: бита 0 в боре нет, двигаемся по 1;

·        третий шаг: бит 1 в боре есть;

Мы пришли к числу ai = 3.

 

Реализация алгоритма

Объявим бинарный бор trie. В переменной TrieSize храним его размер.

 

#define MAX 60 * 100000

int trie[MAX][2];

int TrieSize;

 

Пусть все биты числа ai вставлены в бор. Если число ai заканчивается в вершине номер v бора, то установим num[v] = ai. Таким образом для любой вершины бора мы будем знать, конечная ли она для некоторого ai, и если ответ утвердительный, то сможем получить и само значение ai.

 

long long num[MAX];

 

Функция Insert вставляет число x в бор. Биты вставляем в порядке от старшего к младшему.

 

void Insert(long long x)

{

  int i, v = 0;

  for (i = 60; i >= 0; i--)

  {

 

Извлекаем i-ый бит числа x в переменную bit.

 

    int bit = x & (1LL << i) ? 1 : 0;

 

Если пути по биту bit в боре нет, то создаем новую вершину.

 

    if (trie[v][bit] == -1)

      trie[v][bit] = TrieSize++;

 

Двигаемся по бору дальше.

 

    v = trie[v][bit];

  }

 

Вершина v – конечная для числа x. Сохраним эту информацию.

 

  num[v] = x;

}

 

Функция Find(x) возвращает такое ai, для которого x xor ai максимально.

 

long long Find(long long x)

{

  int i, v = 0;

  for (i = 60; i >= 0; i--)

  {

 

Двигаемся по бору по битам, реверсным к двоичному представлению числа x.

 

    int bit = x & (1LL << i) ? 0 : 1; // reverese bit

 

Если движение по бору невозможно, двигаемся по другому биту.

 

    if (trie[v][bit] == -1) bit ^= 1;

    v = trie[v][bit];

  }

 

Возвращаем значение ai, соответствующее вершине бора v.

 

  return num[v];

}

 

Основная часть программы. Читаем входные данные. Изначально бор содержит одну вершину (TrieSize = 1).

 

scanf("%d %d", &n, &q);

TrieSize = 1;

memset(trie, -1, sizeof(trie));

memset(num, -1, sizeof(num));

 

Вставляем числа входной последовательности a1a2, ..., an в бинарный бор.

 

for (i = 0;i < n; i++)

{

  scanf("%lld", &x);

  Insert(x);

}

 

Обрабатываем q запросов. Для каждого входного значения x ищем такое значение ai, что x xor ai максимально.

 

for (i = 0;i < q; i++)

{

  scanf("%lld", &x);

  x = Find(x);

  printf("%lld\n", x);

}