Інтервал A[i \ldots j] будемо називати хорошим, якщо всі його елементи належать проміжку [A_i; 2 \cdot A_i]. Оскільки масив відсортований, для цього достатньо виконання умови A_j \le 2 \cdot A_i.
Будемо підтримувати вказівники та так, щоб вказував на перший елемент, що не входить у хороший відрізок з початком у . Іншими словами, відрізок [i \ldots j - 1] хороший, а відрізок [i \ldots j] поганий.
Наприклад, для наступної послідовності
інтервали [2; 2], [2; 3], [2; 4] та є хорошими.
інтервали [2; 6], [2; 7] є поганими.
На кожному кроці виконуємо одну з двох дій:
якщо не вийшов за межі масиву і A_j \le 2 \cdot А_i, розширюємо вікно: збільшуємо на ;
інакше вікно для поточного максимальне: виводимо відповідь j - i і зсуваємо ліву межу, збільшуючи на .
Кожен із вказівників лише збільшується і проходить масив не більше одного разу, тому часова складність алгоритму становить .
Приклад
Розглянемо рух вказівників для наведеного прикладу.
Для заданого стану A_j \le 2 \cdot A_i, тому рухаємо вперед вказівник .
Тепер A_j > 2 \cdot A_i. Вперед слід рухати вказівник . Однак перед його переміщенням виводимо кількість елементів, що лежать між A_i та 2 \cdot A_i включно. Вона дорівнює j - i = 6 - 2 = 4.
Рухаємо вперед, поки A_j \le 2 \cdot A_i.
Тепер A_j > 2 \cdot A_i. Виводимо відповідь для i = 3 (вона дорівнює j - i = 8 - 3 = 5) і збільшуємо на .
Реалізація алгоритму
Читаємо вхідні дані.
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);Ініціалізуємо вказівники i = j = 0.
i = j = 0;Рухаємо вказівники вперед, поки для кожного значення i < n не буде знайдена відповідь.
while (i < n) // [i..j]
{Якщо A_j \le 2 \cdot A_i (але при цьому не вийшло за межі масиву, тобто j < n), то розширюємо поточний інтервал, збільшуючи вказівник .
if ((j < n) && (a[j] <= 2 * a[i])) j++;
else
{При A_j > 2 \cdot A_i виводимо відповідь для індексу та збільшуємо на одиницю.
printf("%d ", j - i);
i++;
}
}Реалізація алгоритму — бінарний пошук, O(n log n)
Читаємо вхідні дані.
scanf("%d", &n);
v.resize(n);
for (i = 0; i < n; i++)
scanf("%d", &v[i]);Для кожного індексу i < n за допомогою бінарного пошуку шукаємо таке найбільше значення індексу , що на інтервалі [i; x - 1] знаходяться числа від A_i до 2 \cdot A_i, а A_x > 2 \cdot A_i. Кількість чисел на інтервалі [i; x - 1] дорівнює x - i.
for (i = 0; i < n; i++)
{
x = upper_bound(v.begin(), v.end(), 2 * v[i]) - v.begin();
printf("%d ", x - i);
}Java реалізація
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 реалізація
Читаємо вхідні дані.
n = int(input())
lst = list(map(int,input().split()))Ініціалізуємо вказівники i = j = 0.
i = j = 0Рухаємо вказівники вперед, поки для кожного значення i < n не буде знайдена відповідь.
while (i < n): # [i..j]Якщо A_j \le 2 \cdot A_i (але при цьому не вийшло за межі масиву, тобто j < n), то розширюємо поточний інтервал, збільшуючи вказівник .
if (j < n and lst[j] <= 2 * lst[i]):
j += 1
else:При A_j > 2 \cdot A_i виводимо відповідь для індексу та збільшуємо на одиницю.
print(j - i, end=" ");
i += 1Python реалізація — бінарний пошук, O(n log n)
from bisect import bisect_rightЧитаємо вхідні дані.
n = int(input())
v = list(map(int, input().split()))Для кожного індексу i < n за допомогою бінарного пошуку шукаємо таке найбільше значення індексу , що на інтервалі [i; x - 1] знаходяться числа від А_i до 2 \cdot A_i, а A_x > 2 \cdot A_i. Кількість чисел на інтервалі [i; x - 1] дорівнює x - i.
for i in range(n):
x = bisect_right(v, 2 * v[i])
print(x - i, end=" ")