Будівництво#7329

Будівництво (Розбір)

Будемо шукати довжину містка за допомогою бінарного пошуку. Спочатку покладемо d \in [Left; Right] = [0; INF].

Розглянемо наступну підзадачу: чи можна скласти потрібний місток довжини точно Mid = (Left + Right) / 2? Для цього порахуємо, скільки дощок довжини можна отримати з наявних.

З дошки довжини l_i можна отримати \lfloor l_i / Mid \rfloor дощок довжини . Просумувавши це значення по всіх дошках, дізнаємося загальну кількість дощок довжини , які можна отримати розрізанням:

\sum_{i=1}^n \lfloor l_i / Mid \rfloor

Якщо загальна кількість таких дощок не менша , то це означає, що можна спробувати збільшити довжину — встановлюємо Left = Mid + 1. Інакше зменшуємо верхню межу: Right = Mid.

Відповіддю на задачу буде значення Left - 1.

Приклад

У наведеному прикладі є дошки. Необхідно побудувати місток, що складається з дощок. Якщо обрати довжину дошки рівною , то з наявних дощок можна отримати

16 / 8 + 12 / 8 + 20 / 8 + 10 / 8 = 6

дощок довжини .

Якщо ж обрати довжину , то вийде

16 / 9 + 12 / 9 + 20 / 9 + 10 / 9 = 5,

дощок, що менше необхідної кількості.

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

Визначимо використовувані константи. Довжини наявних дощок l_i зберігаємо в масиві .

#define INF 2000000010
#define MAX 1000009
long long a[MAX];

Функція cnt повертає кількість дощок, яке можна отримати з наявних, якщо кожна з них повинна мати довжину точно . Це значення дорівнює сумі

\sum_{i=1}^n \lfloor l_i / Mid \rfloor
long long cnt(long long  Mid) 
{
  if (Mid == 0) return INF;
  long long res = 0;
  for(i = 0; i < n; i++)
    res += a[i] / Mid;
  return res;
}

Основна частина програми. Читаємо вхідні дані.

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

Встановимо межі для значення d \in [Left; Right] = [0; INF] і запустимо бінарний пошук.

Left = 0;
Right = INF;
while (Left < Right)
{
  Mid = (Left + Right) / 2;

Значення cnt(Mid) дорівнює кількості дощок, яке можна отримати з наявних, якщо кожна з них повинна мати довжину . Якщо ця кількість менша за потрібне число , слід зменшити передбачувану довжину дошки: встановлюємо Right = Mid. Інакше збільшуємо ліву межу: Left = Mid + 1.

  if (cnt(Mid) < m) Right = Mid;
  else Left = Mid + 1;
}

Виводимо відповідь.

printf("%lld\n",Left - 1);