3741. Подмассив с максимальным XOR

 

Задан массив целых чисел. Найдите подмассив с максимальным XOR.

 

Вход. Первая строка содержит размер массива n (n ≤ 105). Вторая строка содержит n целых чисел a1a2, ..., an (0 ≤ ai ≤ 1018).

 

Выход. Выведите такие l и r, для которых al xor al+1 xor ... xor ar принимает наибольшее значение среди всех возможных подмассивов [l .. r] (1 l r n). Дальше в этой же строке выведите значение максимального XOR.

 

Пример входа

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

7

2 8 12 4 9 2 3

4 6 15

 

 

РЕШЕНИЕ

бор

 

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

Рассмотрим следующий ряд чисел:

b0 = 0,

b1 = a1,

b2 = a1 xor  a2,

...,

bn = a1 xor  a2 xor … xor an

Числа b0, b1b2, ..., bn будем последовательно заносить в бинарный бор. Пусть b0, b1b2, ..., bi уже находятся в боре. Найдем такое число bk (k < i), что bk xor bi максимально. Учитывая что bk xor bi = ak+1 xor … xor ai, этой операцией мы ищем подмассив с максимальным XOR, который заканчивается в ai. Среди всех таких максимумов ищем наибольший, который и является ответом.

 

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

Объявим бинарный бор trie, сыновья вершин бора соответствуют битам 0 и 1. В переменной TrieSize храним его размер.

 

#define MAX 100001

 

int trie[MAX * 60][2];

long long b[MAX], mx;

int num[MAX * 60], TrieSize;

 

Функция Insert вставляет число x = b[pos] в бор.

 

void Insert(int pos)

{

  int v = 0;

  long long x = b[pos];

 

Вставка числа в бор начинается со старших битов.

 

  for (int i = 62; i >= 0; i--)

  {

 

Переменная bit содержит i-ый бит числа x.

 

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

 

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

 

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

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

 

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

 

    v = trie[v][bit];

  }

 

Вершина v – конечная для числа x = b[pos]. Сохраним в num[v] индекс pos в массиве b.

 

  num[v] = pos;

}

 

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

 

int Find(long long x)

{

  int i, v = 0;

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

  {

 

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

 

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

 

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

 

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

    v = trie[v][bit];

  }

 

Возвращаем индекс pos массива b, соответствующий вершине бора v.

 

  return num[v];

}

 

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

 

scanf("%d", &n);

TrieSize = 1;

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

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

 

Вставим в бор b0 = 0. В переменной mx вычисляем максимальное значение XOR на подмассиве.

 

b[0] = 0;

Insert(0);

mx = 0;

 

Вставляем остальные числа последовательности b1b2, ..., bn в бор.

 

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

{

 

Читаем значение ai = val.

 

  scanf("%lld", &val);

 

Вычисляем bi = bi-1 xor ai.

 

  b[i] = b[i - 1] ^ val;

 

Вставляем значение bi в бор.

 

  Insert(i);

 

Находим такое k, что bk xor bi максимально. Подмассив с максимальным XOR, который заканчивается в позиции i, начинается в k + 1. Отметим, что bk xor bi = ak+1 xor … xor ai.

 

  k = Find(b[i]);

 

Среди всех значений ak+1 xor … xor ai (i = 1, 2, …, n) находим наибольшее.

 

  if ((b[k] ^ b[i]) > mx)

  {

    mx = b[k] ^ b[i];

    l = k + 1;

    r = i;

  }

}

 

Выводим границы l и r искомого подмассива и значение XOR на нем.

 

printf("%d %d %lld\n", l, r, mx);