Задача з паличками#1102

Задача з паличками (Розбір)

Для кожного індексу знаходимо максимальний індекс k~(i \le k \le n), для якого RMinQ(s_{i+1}, ..., s_k) > s_i. Це можна зробити за допомогою бінарного пошуку. Далі шуканий індекс визначається як позиція, на якій досягається значення RMaxQ(s_i, ..., s_k) при i \le j \le k.

Таким чином, для кожного індексу слід знайти максимально можливий індекс , після чого серед усіх знайдених пар визначити найбільшу різницю j - i.

Приклад

Розглянемо третій приклад.

Нехай i = 2~(s_2 = 4). Найбільшим є індекс k = 7, для якого виконується умова RMinQ(s_{i+1}, ..., s_k) > 4. Значення RMaxQ(s_3, ..., s_7) досягається на індексі j = 6.

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

Оголосимо константи та масиви.

#define MAX 50010
#define LOGMAX 16
int dp_max[MAX][LOGMAX], dp_min[MAX][LOGMAX];
int a[MAX];

Функція Build_RMQ_Array будує таблиці для швидких запитів мінімуму () і максимуму ():

void Build_RMQ_Array(int *b)
{
  int i, j;
  for (i = 1; i <= n; i++) 
  {
    dp_min[i][0] = b[i];
    dp_max[i][0] = i;
  }

  for (j = 1; 1 << j <= n; j++)
    for (i = 1; i + (1 << j) - 1 <= n; i++)
    {
      if (b[dp_max[i][j - 1]] > b[dp_max[i + (1 << (j - 1))][j - 1]])
        dp_max[i][j] = dp_max[i][j - 1];
      else
        dp_max[i][j] = dp_max[i + (1 << (j - 1))][j - 1];

      dp_min[i][j] = 
        min(dp_min[i][j - 1], dp_min[i + (1 << (j - 1))][j - 1]);
    }
}

Функція RangeMaxQuery знаходить індекс максимального елемента на підвідрізку .

int RangeMaxQuery(int i, int j)
{
  int k = 0;
  while ((1 << (k + 1)) <= j - i + 1) k++; 

  if (a[dp_max[i][k]] > a[dp_max[j - (1<<k) + 1][k]])
    return dp_max[i][k];
  else
    return dp_max[j - (1<<k) + 1][k];
}

Функція RangeMinQuery знаходить мінімальне значення на підвідрізку .

int RangeMinQuery(int i, int j)
{
  int k = 0;
  while ((1 << (k + 1)) <= j - i + 1) k++; 
  return min(dp_min[i][k],dp_min[j - (1<<k) + 1][k]);
}

Функція BinSearch за допомогою бінарного пошуку на інтервалі [Left; Right] знаходить найбільший індекс , для якого всі елементи між Left + 1 і більші, ніж a[Left].

int BinSearch(int Left, int Right)
{
  int MinValue = a[Left++];
  if (RangeMinQuery(Left,Right) > MinValue) return Right;

  while (Right > Left)
  {
    int Middle = (Left + Right) / 2;
    if (RangeMinQuery(Left,Middle) > MinValue)
      Left = Middle + 1;
    else
      Right = Middle;
  }

  if (dp_min[Left][0] <= MinValue) Left--;
  return Left;
}

Основна частина програми. Послідовно обробляємо тести.

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

Будуємо і масиви.

  Build_RMQ_Array(a);

Для кожного індексу :

  res = 0;
  for(i = 1; i <= n; i++)
  {
    k = BinSearch(i, n);
    j = RangeMaxQuery(i,k);
    if (j - i > res) res = j - i;
  }

Виводимо відповідь. У випадку res = 0 виводити слід .

  if (res == 0) res = -1;
  printf("%d\n",res);
}