4080. AND Раунды

 

Имеется циклический массив A, содержащий n чисел. Во время AND раунда каждый элемент массива A заменяется битовой операцией AND его самого, предыдущего и следующего элементов массива. Все операции производятся одновременно. Сможете ли Вы найти значения всех элементов массива A после выполнения k таких AND раундов?

 

Вход. Первая строка содержит количество тестов t. Далее следуют 2t строк, по 2 на один тест. Первая строка содержит два целых числа n (3 ≤ n ≤ 20000) и k (1 ≤ k ≤ 109). Следующая строка содержит n целых чисел Ai (0 ≤ Ai ≤ 109) – начальные значения массива A.  

 

Выход.  Вывести t строк, по одной для каждого теста. Для каждого теста вывести список из n целых чисел – содержимое массива A после выполнения k AND раундов.

 

Пример входа

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

2

3 1

1 2 3

5 100

1 11 111 1111 11111

0 0 0

1 1 1 1 1

 

 

РЕШЕНИЕ

структуры данных – дерево отрезков

 

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

Пусть изначально число Ai в p-ом бите содержит 0. Тогда после первого раунда числа Ai-1 и Ai+1 в  p-ом бите будут содержать 0 (индексы вычисляются по модулю n). После k-го раунда числа Ai-1, …, Ai-k и Ai+1, …, Ai+k в  p-ом бите будут содержать 0.

Если в p-ом бите Ai изначально находится 0, то;

·        в p-ом бите Ai ни на каком раунде не появится 1;

·        через kn / 2 раундов все числа массива в p-ом бите будут содержать 0.

 

Лемма. Если kn / 2, то для решения задачи достаточно вычислить побитовый AND всех элементов массива и вывести его n раз. Что и будет ответом.

 

Лемма. Если k < n / 2, то на Ai после k раундов подействуют начальные значения Ai-1, …, Ai-k и Ai+1, …, Ai+k. Если одно из них в p-ом бите содержит 0, то после k раундов Ai в в p-ом бите будет также содержать 0. Значит для ответа на вопрос достаточно вычислить побитовый AND элементов Ai-k, …, Ai, …, Ai+k. Это можно совершить за время O(log2n), если на элементах входного массива A построить дерево отрезков, поддерживающее операцию побитового AND.

 

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

Объявим входной массив а. Дерево отрезков храним в массиве SegTree.

 

#define MAX 20010

int a[MAX];

int SegTree[4*MAX];

 

Построение дерева отрезков, которое поддерживает битовую операцию AND.

 

void BuildTree(int *a, int Vertex, int Left, int Right)

{

  if (Left == Right)

    SegTree[Vertex] = a[Left];

  else

  {

    int Middle = (Left + Right) / 2;

    BuildTree(a, 2*Vertex, Left, Middle);

    BuildTree(a, 2*Vertex+1, Middle+1, Right);

    SegTree[Vertex] =  SegTree[2*Vertex] & SegTree[2*Vertex+1];

  }

}

 

Вершине Vertex соответствует отрезок [LeftPos, RightPos]. Функция Query возвращает значение aLeft & aLeft+1 & … & aRight.

 

int Query(int Vertex, int LeftPos, int RightPos, int Left, int Right)

{

  if (Left > Right) return -1;

  if ((Left == LeftPos) && (Right == RightPos))

    return SegTree[Vertex];

 

  int Middle = (LeftPos + RightPos) / 2;

  return Query(2*Vertex, LeftPos, Middle, Left, min(Right,Middle)) &

      Query(2*Vertex+1, Middle+1, RightPos, max(Left,Middle+1),Right);

}

 

Основная часть программы. Читаем входные данные в массив а.

 

scanf("%d",&tests);

while(tests--)

{

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

  for(i = 0; i < n; i++) scanf("%d",&a[i]);

 

Если количество раундов k не меньше половины количества элементов массива, то вычисляем AND всех элементов в переменной res.

 

  if (k >= n / 2)

  {

    for(res = a[0], i = 1; i < n; i++) res &= a[i];

 

Выводим значение res n раз.

 

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

    {

      if (i) printf(" ");

      printf("%d",res);

    }

    printf("\n");

    continue;

  }

 

Для быстрого ответа на каждый тест построим дерево отрезков из элементов массива А.

 

  BuildTree(a,1,0,n-1);

 

Вычисляем значение Ai после k раундов. Оно равно Ai-k & … & Ai &…& Ai+k. Индексы элементов вычисляются по модулю n. Если (ik ) mod n < (i + k) mod n,  то достаточно одного запроса на дереве отрезков. Иначе находим AND элементов от (ik ) mod n до n – 1 и от 0 до (i + k) mod n.

 

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

  {

    int Start = (i - k + n) % n;

    int End = (i + k + n) % n;

    if (Start < End) res = Query(1,0,n-1,Start,End);

    else res = Query(1,0,n-1,Start,n-1) & Query(1,0,n-1,0, End);

    if (i) printf(" ");

    printf("%d",res);

  }

  printf("\n");

}