Майже прості називаються такі натуральні числа, які мають вигляд p^k, де — просте число, а k \ge 2. Наприклад, майже простими є числа: 4, 8, 16, 32, 9, 81, ... .
Оскільки high < 10^{12}, то, виходячи з визначення майже простого числа, можливі значення простого числа задовольняють нерівність p < 10^6.
Згенеруємо масив довжини 10^6, де primes[i] = 1, якщо — просте число, і primes[i] = 0 в іншому випадку.
Далі сформуємо масив , який містить усі майже прості числа, не перевищуючі 10^{12}. Кількість таких чисел складе , і відсортуємо їх за зростанням.
Позначимо через f(a, b) кількість майже простих чисел у відрізку . Тоді
Кількість майже простих чисел на відрізку дорівнює індексу, в який можна вставити число в масив , щоб зберегти його впорядкованість.
Цей індекс можна знайти за допомогою бінарного пошуку — функцією upper_bound.
Приклад
Згенеруємо всі майже прості числа в проміжку від до .
Спочатку запишемо степені простого числа , не перевищуючі :
Потім запишемо степені :
Потім запишемо степені :
І нарешті, запишемо степені :
Квадрат числа дорівнює , що більше , тому степені і більше не потрапляють в заданий діапазон.
Після сортування отримаємо масив:
Розглянемо другий тест: . У цьому діапазоні знаходяться наступні майже прості числа: 4, 8, 9, 16. Їх кількість дорівнює .
Реалізація алгоритму
Оголосимо глобальні масиви. Майже прості числа, не перевищуючі 10^{12}, представляють собою степені простих чисел, не перевищуючих MAX = 10^6.
Усі знайдені майже прості числа будемо зберігати в масив .
#define MAX 1000010
char primes[MAX];
long long m[MAX];Для перевірки чисел на простоту заздалегідь згенеруємо масив .
void gen_primes(void)
{
for (int i = 0; i < MAX; i++) primes[i] = 1;
for (int i = 2; i < sqrt(MAX); i++)
if (primes[i])
for (int j = i * i; j < MAX; j += i) primes[j] = 0;
}Основна частина програми.
gen_primes();Для кожного простого числа заносимо в масив його степені i^2, i^3, i^4, ..., поки чергова ступінь не перевищить 10^{12}. Змінна містить кількість майже простих чисел, доданих у масив . Оскільки значення майже простих чисел не перевищують 10^{12}, для зберігання і обчислень необхідно використовувати -бітові цілі числа (тип long long).
for(i = 2; i < MAX; i++)
if (primes[i])
{
long long temp = 1LL*i*i;
while(temp < 1LL*MAX*MAX)
{
m[ptr++] = temp;
temp *= i;
}
}Сортуємо масив за допомогою стандартної бібліотеки STL.
sort(m,m+ptr);Читаємо кількість вхідних тестів і для кожного інтервалу обчислюємо і виводимо значення
Кожен такий запит обробляється за час O(log_2~ptr) з використанням бінарного пошуку.
scanf("%d", &tests);
while(tests--)
{
scanf("%lld %lld", &l, &h);
printf("%d\n", upper_bound(m,m+ptr,h) - upper_bound(m,m+ptr,l-1));
}Java реалізація
import java.util.*;
public class Main
{
public static ArrayList<Long> primes = new ArrayList<Long>();
public static void GenPrimes()
{
final int MAX_SIZE = 1000001;
BitSet bits = new BitSet(MAX_SIZE);
bits.set(2, MAX_SIZE, true);
for (int i = 2; i < Math.sqrt(MAX_SIZE); i++)
{
if (bits.get(i))
for (int j = i * i; j < MAX_SIZE; j += i)
bits.set(j, false);
}
for (int i = 2; i < MAX_SIZE; i++)
if (bits.get(i))
{
long temp = i;
while ((temp *= i) < 1000000000000L)
primes.add(temp);
}
primes.add(1000000000001L);
Collections.sort(primes);
}
public static void main(String[] args)
{
Scanner con = new Scanner(System.in);
int tests = con.nextInt();
GenPrimes();
for(int i = 0; i < tests; i++)
{
Long low = con.nextLong();
Long high = con.nextLong();
int lIndex = Collections.binarySearch(primes, low);
if (lIndex < 0) lIndex = -(lIndex + 1);
int hIndex = Collections.binarySearch(primes, high);
if (hIndex < 0) hIndex = -(hIndex + 1); else hIndex++;
System.out.println(hIndex - lIndex);
}
}
}