Tikinti#7329

Tikinti (Həll təhlili)

Körpünün uzunluğunu ikili axtarışla tapaq. Əvvəlcə d \in [Left; Right] = [0; INF] kimi qəbul edək.

Sonra bu alt məsələyə baxaq: tələb olunan körpü uzunluğunu tam olaraq Mid = (Left + Right) / 2 etmək olarmı? Bunun üçün mövcud olanlardan neçə taxta uzunluğu əldə edə biləcəyimizi hesablayacağıq.

Uzunluğu l_i olan taxtadan \lfloor l_i / Mid \rfloor uzunluğu olan taxtalar əldə etmək olar. Bu dəyəri bütün taxtalar üzrə cəmləşdirərək, kəsiklərdən hansı uzunluqda taxtalar əldə edə biləcəyimizi öyrənəcəyik:

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

Əgər belə taxtaların ümumi sayı -dən az deyilsə, bu o deməkdir ki, uzunluğu artırmağa cəhd edə bilərik — Left = Mid + 1 təyin edirik. Əks halda yuxarı həddi azaldırıq: Right = Mid.

Problemə cavab Left - 1 dəyəri olacaqdır.

Misal

Göstərilən nümunədə taxta var. taxtadan ibarət körpü tikilməlidir. Əgər taxta uzunluğunu seçsək, onda mövcud taxtalardan

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

uzunluğu olan taxtalar əldə edə bilərik.

Əgər uzunluğu seçsək, o zaman alınacaq

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

ki bu da zəruri miqdardan azdır.

Alqoritmin tətbiqi

İstifadə olunan sabitləri müəyyən edək. Mövcud taxtaların uzunluqlarını l_i massivində saxlayırıq.

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

cnt funksiyası mövcudlarından tələb olunan uzunluğu olan taxtalar əldə edə biləcəyimiz miqdarı qaytarır. Bu dəyər aşağıdakı cəm ilə bərabərdir:

\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;
}

Proqramın əsas hissəsi. Daxil olunan məlumatları oxuyuruq.

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

d \in [Left; Right] = [0; INF] qiyməti üçün hədlər təyin edirik və ikili axtarışı işə salırıq.

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

cnt(Mid) dəyəri hər biri uzunluğunda olan taxtaların mövcud olandan əldə edilən sayına bərabərdir. Əgər bu miqdar lazım olan sayından azdırsa, təxmin edilən taxta uzunluğunu azaltmaq lazımdır: Right = Mid təyin edirik. Əksinə, sol sərhədi artırırıq: Left = Mid + 1.

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

Cavabı çıxışa veririk.

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