İki dəfə çox#11286

İki dəfə çox (Həll təhlili)

Əgər A[i \ldots j] intervalının bütün elementləri [A_i; 2 \cdot A_i] aralığında yerləşirsə, biz bu intervalı yaxşı adlandırırıq. Massiv sıralandığı üçün A_j \le 2 \cdot A_i şərtinin ödənilməsi kifayətdir.

Biz iki göstərici və saxlayırıq, belə ki göstəricisi -dən başlayan yaxşı intervala aid olmayan ilk elementi göstərir. Başqa sözlə, [i \ldots j - 1] intervalı yaxşıdır, [i \ldots j] intervalı isə pisdir.

Misal üçün, aşağıdakı ardıcıllıq üçün:

Hər addımda biz aşağıdakı iki əməldən birini yerinə yetiririk:

Hər göstərici yalnız irəli hərəkət edir və massivi ən çoxu bir dəfə gəzir, ona görə də alqoritmin zaman mürəkkəbliyi -dir.

Misal

Verilmiş misal üçün göstəricilərin hərəkətinə baxaq.

Verilmiş şərt üçün A_j \le 2 \cdot A_i, ona görə də göstəricisini irəli aparırıq.

İndi A_j > 2 \cdot A_i. göstəricisini irəli aparmalıyıq. Lakin, bunu etməzdən əvvəl, A_i və 2 \cdot A_i arasında (daxil olmaqla) yerləşən elementlərin sayını çap edirik. O, j - i = 6 - 2 = 4-ə bərabərdir.

A_j \le 2 \cdot A_i olana qədər -ni irəli aparırıq.

İndi A_j > 2 \cdot A_i. i = 3 üçün cavabı çap edirik (o, j - i = 8 - 3 = 5-ə bərabərdir) və -ni artırırıq.

Alqoritmin realizasiyası

Giriş məlumatlarını oxuyuruq.

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

i = j = 0 göstəricilərini ilkinləşdiririk.

i = j = 0;

Hər i < n qiyməti üçün cavab tapılana qədər göstəriciləri irəli aparırıq.

while (i < n) // [i..j]
{

Əgər A_j \le 2 \cdot A_i (və massivin hüdudlarını aşmayıbsa, yəni j < n), cari intervalı göstəricisini artıraraq genişləndiririk.

  if ((j < n) && (a[j] <= 2 * a[i])) j++;
  else
  {

Əgər A_j > 2 \cdot A_i, indeksi üçün cavabı çap edirik və -ni bir vahid artırırıq.

    printf("%d ", j - i);
    i++;
  }
}

Alqoritmin realizasiyası — ikili axtarış, O(n log n)

Giriş məlumatlarını oxuyuruq.

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

Hər i < n indeksi üçün, ikili axtarış istifadə edərək, A_i-dən 2 \cdot A_i-yə qədər olan ədədlərin [i; x - 1] intervalında yerləşdiyi və A_x > 2 \cdot A_i olduğu ən böyük indeksi qiymətini tapırıq. [i; x - 1] intervalındakı ədədlərin sayı x - i-yə bərabərdir.

for (i = 0; i < n; i++)
{
  x = upper_bound(v.begin(), v.end(), 2 * v[i]) - v.begin();
  printf("%d ", x - i);
}

Java realizasiyası

import java.util.*;
 
public class Main
{
  public static void main(String[] args)
  {
    Scanner con = new Scanner(System.in);
    int n = con.nextInt();
    int m[] = new int[n];
    for(int i = 0; i < n; i++)
      m[i] = con.nextInt();

    int i = 0, j = 0;
    while (i < n) // [i..j]
    {
      if ((j < n) && (m[j] <= 2 * m[i])) j++;
      else
      {
        System.out.print(j - i + " ");
        i++;
      }
    }

    con.close();
  }
}

Python realizasiyası

Giriş məlumatlarını oxuyuruq.

n = int(input())
lst = list(map(int,input().split()))

i = j = 0 göstəricilərini ilkinləşdiririk.

i = j = 0

Hər i < n qiyməti üçün cavab tapılana qədər göstəriciləri irəli aparırıq.

while (i < n): # [i..j]

Əgər A_j \le 2 \cdot A_i (və massivin hüdudlarını aşmayıbsa, yəni j < n), cari intervalı göstəricisini artıraraq genişləndiririk.

  if (j < n and lst[j] <= 2 * lst[i]):
    j += 1
  else:

Əgər A_j > 2 \cdot A_i, indeksi üçün cavabı çap edirik və -ni bir vahid artırırıq.

    print(j - i, end=" ");
    i += 1

Python realizasiyası — ikili axtarış, O(n log n)

from bisect import bisect_right

Giriş məlumatlarını oxuyuruq.

n = int(input())
v = list(map(int, input().split()))

Hər i < n indeksi üçün, ikili axtarış istifadə edərək, A_i-dən 2 \cdot A_i-yə qədər olan ədədlərin [i; x - 1] intervalında yerləşdiyi və A_x > 2 \cdot A_i olduğu ən böyük indeksi qiymətini tapırıq. [i; x - 1] intervalındakı ədədlərin sayı x - i-yə bərabərdir.

for i in range(n):
  x = bisect_right(v, 2 * v[i])
  print(x - i, end=" ")