Нехай m = 3k + 7. Тоді шуканий вираз перепишеться у вигляді:
Теорема Вільсона. Якщо — просте число, то
Тобто
Таким чином, якщо — просте число, то число \frac{(m-1)!+1}{m} буде цілим.
У той час
Тоді весь вираз дорівнює
Якщо — складене число, то
оскільки серед множників зліва є всі дільники числа .
У цьому випадку число
буде цілим.
Тоді весь вираз дорівнює
Щоб обчислити S_n, достатньо порахувати кількість простих чисел виду 3k + 7 при 1 \le k \le n.
За допомогою решета Ератосфена будуємо масив , в якому:
Далі, використовуючи масив , будуємо масив , який містить інформацію про прості числа виду 3i + 7:
Тепер
Оскільки в задачі є багато тестів, щоб відповідати на них за константний час, побудуємо префіксні суми:
Тепер для відповіді на задачу для кожного значення достатньо вивести .
Приклад
Нехай m = 7 — просте. Тоді
Нехай m = 6 — складене. Тоді
Обчислимо відповідь для n = 10. Розглянемо всі числа виду 3k + 7, де 1 \le k \le 10. Виділимо серед них прості.
Серед перелічених чисел є простих числа: та . Отже, S_{10} = 4.
Реалізація алгоритму
Оголосимо робочі масиви. Оскільки n \le 10^6, розмір масивів слід встановити рівним 3n + 7 = 3 \cdot 10^6 + 7.
#define MAX 3000010
char used[MAX], a[MAX];Функція Init реалізує решето Ератосфена. Масив містить інформацію про прості числа:
Масив містить інформацію про прості числа виду 3i + 7:
void Init()
{
for (int i = 2; i < MAX; i++)
{
if (!used[i])
{
if ((i - 7) % 3 == 0)
a[(i - 7) / 3] = true;
if (i < sqrt(MAX))
for (int j = i * i; j < MAX; j += i)
used[j] = true;
}
}
}Основна частина програми. Заповнюємо масиви.
Init();Обчислюємо префіксні суми для масиву :
for (int i = 2; i < MAX; ++i)
p[i] = p[i - 1] + a[i];Послідовно обробляємо тестів.
scanf("%d", &tests);
while (tests--)
{
scanf("%d", &n);Для кожного значення виводимо .
printf("%d\n", p[n]);
}