Загрузил rodzher.zet

Практика по алгоритмам Асимптотика, начало структур

Реклама
SPb HSE, 1 курс, осень 2019/20
Практика по алгоритмам #2
Асимптотика, начало структур
12 сентября
Собрано 11 октября 2019 г. в 21:17
Содержание
1. Асимптотика, начало структур
1.1. Стандартные задачи на стек . . .
1.2. Неасимптотические оптимизации
1.3. Задачи про цикл for . . . . . . .
1.4. Дополнительные задачи . . . . .
.
.
.
.
1
1
1
2
3
.
.
.
.
4
4
4
5
6
3. Домашнее задание
3.1. Обязательная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2. Дополнительная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
7
8
4. Разбор домашнего задания
4.1. Обязательная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2. Дополнительная часть . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
9
11
2. Разбор задач практики
2.1. Стандартные задачи на стек . . .
2.2. Неасимптотические оптимизации
2.3. Задачи про цикл for . . . . . . .
2.4. Дополнительные задачи . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
Асимптотика, начало структур
1.1. Стандартные задачи на стек
1. Проверить правильность скобочной последовательности с несколькими типами скобок.
2. Найти значение арифметического выражения, содержащего числа и символы +-*()ˆ.
3. Даны два выражения с целыми числами и операциями +, -, *.
Суммарная длина не превосходит 106 . Проверить, равны ли значения выражений.
1.2. Неасимптотические оптимизации
1. void go(int n) {
if (n <= 0) return;
a[k++] = n;
go(n - 1);
a[k++] = n;
}
2. void projection(int n, int *x, int *y, double *z, double angle) {
for (int i = 0; i < n; i++)
z[i] = x[i] * cos(angle) + y[i] * sin(angle); // проекция
}
3. int n, p[n], a[n], b[n]; // p - перестановка
for (int k = 0; k < n; k++) {
int cnt = 0;
for (int i = k; i < n - k; i++)
if (a[p[i]] >= b[k])
cnt++;
printf("%d\n", cnt);
}
4. for (int i = 1; i < n; i++) {
vector<int> digits;
for (int j = i, d = 2; j > 0; j /= d, d++)
digits.push_back(j % d);
for (int j = digits.size() - 1; j >= 0; j--)
printf("%d ", digits[j]);
printf("\n");
}
1/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
1.3. Задачи про цикл for
Разминочная задача: ищем такие 𝑎, 𝑏, 𝑐 : 𝑎𝑏 + 𝑏𝑐 + 𝑐𝑎 = 𝑁 .
Оцените сложность фрагментов программ.
1. Ищем такие
for (int a =
for (int b
{ int c =
𝑎, 𝑏, 𝑐 : 𝑎𝑏𝑐 = 𝑁 , 𝑎 + 𝑏 + 𝑐 = min.
1; a <= N; a++)
= 1; a * b <= N; b++)
N / a / b, ... }
2. Ищем такие
for (int a =
for (int b
{ int c =
𝑎, 𝑏, 𝑐 : 𝑎𝑏𝑐 = 𝑁 , 𝑎 + 𝑏 + 𝑐 = min.
1; a * a * a <= N; a++)
= 1; b * b <= N; b++)
N / a / b, ... }
3. Поиск делителей без деления
int b = N;
for (int a = 1; a <= N; a++) {
while (a * b > N)
b--;
if (a * b == N)
printf("%d %d\n", a, b);
}
4. Повторение, мать!
for (int a = 1; a < n; a++)
for (int b = 0; b < n; b += a)
;
5. Partition
int a = 1, b = n, M = x[n / 2];
while (a < b) {
while (x[a] < M) a++;
while (x[b] > M) b--;
if (a <= b) swap(x[a++], x[b--]);
}
Что делает этот код?
6. Перестановки и циклы
for (int i = 1; i < n; i++)
if (used[i] == 0)
for (int j = i; used[j] == 0; j = (j * 17 + 2) % n)
used[j] = 1;
7. Дежавю
int y = N, sum_x = 0, sum_y = 0;
for (int x = 1; x <= N; x++) {
while (x * y > N) y--;
sum_x += x, sum_y += y;
}
Какова асимптотика sum_x и sum_y после выполнения программы?
2/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
1.4. Дополнительные задачи
8. Sqrt*
Сколько работает этот код?
while (N > 2)
N = sqrt(N)
А если бы вместо 2 было 1?
9. Ищем такие
for (int a =
for (int b
{ int c =
𝑎, 𝑏, 𝑐 : 𝑎𝑏𝑐 = 𝑁 , 𝑎 + 𝑏 + 𝑐 = min.
1; a * a * a <= N; a++)
= a; a * b * b <= N; b++)
N / a / b, ... }
10. Решето Эратосфена
for (int p = 2; p < n; p++)
if (min_divisor[p] == 0) // число p простое
for (int x = p + p; x < n; x += p)
if (min_divisor[x] == 0)
min_divisor[x] = p;
11. Странный код
int a[k][n + 1]; // a[i=0..k-1][n] = -infty
int p[k]; // p[i=0..k-1] = 0
for (int i = 0; i < m; i++) { // m <= n * k
int min_j = 0;
for (int j = 0; j < k; j++)
while (a[j][p[j]] < a[min_j][p[min_j]])
min_j = j;
for (int j = 0; j < k; j++)
while (a[j][p[j]] > a[min_j][p[min_j]])
p[j]++;
}
12. Выражение, тождественно равное нулю
Дано выражение с целыми числами и операциями +, -, *. А также переменными 𝑥1 , 𝑥2 , 𝑥3 , . . . .
Суммарная длина не превосходит 106 .
Проверить, является ли выражение тождественным нулём.
3/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
Разбор задач практики
2.1. Стандартные задачи на стек
1. Скобки.
Открывающие скобки кладем в стек. Встретив закрывающую, сравним ее тип с открывающей на вершине стека и сделаем pop. Плохо, если не сошелся тип, либо если стек в конце не
пуст.
Индуктивное предположение для обоснования корректности: 𝑆 – ПСП ⇔ после обработки
алгоритмом строки 𝑆 стек остался в том же состоянии, что до обработки 𝑆.
2. Значение арифметического выражения
Два стека numbers и operators. «Применить оператор» значит вынуть 𝑦, 𝑥 из numbers и ∘
из operators, затем numbers.push(𝑥 ∘ 𝑦).
Идем по выражению слева направо. Встречая число 𝑥, кладем в numbers. Встречая оператор
∘, пока приоритет operators.top() выше или равен, применяем operators.top(), в конце
кладем ∘ в operators.
Открывающие скобки кладем в operators. Встречая закрывающую, применяем верхний
оператор, пока он не открывающая скобка.
Еще надо выполнить все операторы, оставшиеся в стеке после всего прохода по выражению
(вместо этого можно заключить все выражение в скобки). Окончательный результат будет
на вершине numbers.
𝑐
Описанная схема не работает для ˆ : 𝑎 ˆ 𝑏 ˆ 𝑐 = 𝑎𝑏 , а не (𝑎𝑏 )𝑐 . Для ˆ надо снимать со стека
operators только более высокий приоритет, но не равный.
Дело в том, что ˆ – правоассоциативная операция, а не левоассоциативная.
3. Проверка равенства двух длинных выражений
Если вычислять значения явно, могут получиться числа длины ≈ 106 , операции с ними
долгие. Проверим, что их значения равны по модулю 𝑃 = 109 + 7. Для большей верности
можно проверить по модулю нескольких случайных простых чисел в пределах 2 · 109 .
Вероятность того, что выражения не равны, а остатки по модулю случайного числа из [2, 𝑃 ]
6
равны, 6 10𝑃 , так как у их разности не более 106 делителей.
2.2. Неасимптотические оптимизации
1. void go(int n). Вывод – массив 𝑛, 𝑛 − 1, . . . , 2, 1, 1, 2, . . . , 𝑛 − 1, 𝑛. Цикл быстрее рекурсии.
2. Проекция. Сохранить в переменные cos(angle), sin(angle), а не считать 𝑛 раз заново.
3. Перестановка. for (int i = 0; i < n; ++i) ap[i] = a[p[i]] в начало. Мы 𝑛 раз проходили a не по порядку, кэш груcтил.
Также можно ускорить вывод.
4. Разложение всех 𝑖. Объявить digits вне цикла и делать в цикле digits.clear(). Мы 𝑛
раз вызвали его конструктор и деструктор.
Также можно ускорить вывод.
4/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
2.3. Задачи про цикл for
Разминка:
можно перебрать for a=1..n, for b=1..n, for c=1..n. 𝒪(𝑛3 ).
можно перебрать for a=1..n, for b=1..n. 𝒪(𝑛2 ).
можно перебрать for a=1..n, for b=1..n/a. 𝒪(𝑛 log 𝑛).
√
√
√
можно понять, что 𝑎 6 𝑏 6 𝑐 ⇒ 𝑏 6 𝑛 перебрать for a=1.. 𝑛, for b=1.. 𝑛. 𝒪(𝑛).
1. Ищем такие 𝑎, 𝑏, 𝑐 : 𝑎𝑏𝑐 = 𝑁 , 𝑎 + 𝑏 + 𝑐 = min. Для каждого 𝑎 прошли от 1 до ⌊ 𝑁𝑎 ⌋, итого
𝑁 (1 + 21 + 13 + . . . + 𝑁1 ) = Θ(𝑁 log 𝑁 ).
2. Ищем такие 𝑎, 𝑏, 𝑐 : 𝑎𝑏𝑐 = 𝑁 , 𝑎 + 𝑏 + 𝑐 = min. Θ(𝑁 1/3 · 𝑁 1/2 ) = Θ(𝑁 5/6 ).
3. Поиск делителей без деления. Θ(𝑁 ). Время = 𝑁 плюс суммарное время всех while.
Всегда 𝑏 > 0, при 𝑎 = 𝑁 станет 𝑏 = 1, значит, не более 𝑁 раз будет b--.
𝑛
∑︀
𝑛
= Θ(𝑛 log 𝑛).
4. Повторение, мать! Снова
𝑎
𝑎=1
5. Partition. Θ(𝑛). Указатель 𝑎 пройдет от 1 до не более 𝑛, так как всегда впереди него есть 𝑀 .
Если 𝑎 натыкается на 𝑀 , то переносит его вперед. Аналогично 𝑏. В итоге в массиве сначала
будут элементы 6 𝑀 , затем > 𝑀 .
6. Перестановки и циклы. Θ(𝑛), для каждого 𝑗 ровно один раз сделается used[𝑗] = 1.
∑︀ 1
= Θ(𝑁 log 𝑁 ).
7. Дежавю. Время Θ(𝑁 ). 𝑥 = 1 + 2 + . . . + 𝑁 = Θ(𝑁 2 ). 𝑦 =
𝑥
5/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
2.4. Дополнительные задачи
8. Sqrt*
На каждом шаге log 𝑁 уменьшается вдвое, итого Θ(log log 𝑁 ).
Если 2 заменить на 1, то в теории выйдет бесконечо√долго, но на пратике цикл завершается
по достижении 1 + 𝜀, где 𝜀 = 10−15 для типа double. 1 + 𝑥 = 1 + 𝑥2 + 𝑜(𝑥) при 𝑥 → 0, поэтому
время работы Θ(log log 𝑁 + log 1𝜀 ). Тестирование:
int main() { // g++ -O0
int k = 0;
for (double x = 1e9; x > 1; x = sqrt(x))
k++;
printf("%d\n", k); // 57
}
9. Ищем√︁такие 𝑎, 𝑏, 𝑐 : 𝑎𝑏𝑐
= 𝑁 , 𝑎 + 𝑏 + 𝑐 = min.
∫︀ 𝑁 1/3 √︁ 𝑁
∑︀𝑁 1/3 𝑁
1/3
) = Θ(𝑁 1/2+1/6 ) = Θ(𝑁 2/3 ).
= Θ( 1
𝑑𝑎) = Θ(𝑁 1/2 𝑎1/2 |𝑁
1
𝑎=1
𝑎
𝑎
10. Решето Эратосфена. Время Θ(𝑛 log log 𝑛).
Магический факт: 𝑝𝑘 = Θ(𝑘 ln 𝑘).
𝑛/∫︀ln 𝑛
𝑛/∑︀
ln 𝑛
𝑛/ ln 𝑛
𝑛
𝑑𝑥
Тогда сложность
Θ( 𝑘 ln 𝑘 ) = 𝑛 · Θ(
) = 𝑛 · Θ(ln ln 𝑥|2
) = Θ(𝑛 ln ln 𝑛).
𝑥 ln 𝑥
𝑘=2
2
11. Странный код
Каждый указатель 𝑝𝑖 сделает не более 𝑛 шагов вперёд, так как последний столбец −∞.
Еще можно заметить, что первый while каждый раз делает не более одного шага. Итого
𝒪(𝑘(𝑛 + 𝑚)).
12. Выражение, тождественно равное нулю
Несколько раз подставим случайные числа в переменные и проверим, ноль ли. Вычисляем
по модулю 𝑃 = 109 + 7.
Выражение из +-*() – это многочлен, если он не нулевой, то у него не более его степени
.
корней (по модулю 𝑃 тоже). Вероятность попасть в корень равна deg
𝑃
6/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
Домашнее задание
3.1. Обязательная часть
1. (4) Цикл for
Оцените сложность фрагмента программы.
a) for (int i = 1; i < n; i = i + i)
for (int j = 0; j < i; j++)
;
b) for (int i = 0; i < n; i += 3)
for (int j = 1; j < i; j += j)
;
c) for (int i = 1; i < n; i++)
for (int j = n/i; j < i; j++)
;
d) int x = n;
for (int i = n; i >= 1; i--)
for (; x > i; x--)
;
2. (4.5) Неасимптотические оптимизации
Ускорьте код. Не обязательно писать новый код, укажите, что и как оптимизировать.
Не нужно менять сам алгоритм, сделайте чисто техническую оптимизацию.
Обязательно поясните, почему код был медленным и что стало лучше.
a) const int MOD = 1e9; // 0 <= a[i], b[i] < MOD
long long scalarProductMod(int n, long long *a, long long *b) {
long long sum = 0;
for (int i = 0; i < n; i++) sum = (sum + a[i] * b[i]) % MOD;
return sum;
}
b) double Exp(double x, int dep=0, double F=1) {
if (dep >= 20) return 0;
return pow(x, dep) / F + Exp(x, dep+1, F*(dep+1));
}
double result = Exp(0.5); // пример использования
c) В этом пункте просто укажите медленные части.
string ≈ vector<char>, to_string: int → string.
void outputNatural(int n) {
string buf;
for (int i = 1; i <= n; i++) buf += to_string(i) + " ";
cout << buf << endl;
}
3. (3) Подотрезок с заданной суммой
Дана последовательность 𝑎1 , 𝑎2 , · · · , 𝑎𝑛 ∈ N и 𝑆 ∈ N.
𝑟
∑︀
Найти 𝑙, 𝑟 (1 ≤ 𝑙 ≤ 𝑟 ≤ 𝑛) такие, что сумма
𝑎𝑖 = 𝑆. 𝒪(𝑛). Частичный балл (1.5) за 𝒪(𝑛2 ).
𝑖=𝑙
7/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
3.2. Дополнительная часть
1. (1.5) Суммы и интегралы
Можно пользоваться интегралами (см.конспект), есть короткое решение без них.
𝑛
𝑛
∑︀
∑︀
1
1
(a) Докажите, что
=
𝒪(1)
(b)
Оцените
сумму
2
𝑘
𝑘1/2
(c) Докажите, что
𝑘=1
∞
∑︀
𝑘=1
𝑘=1
1
𝑘3/2
= 𝒪(1)
2. (3) Частый элемент
Дана последовательность объектов 𝑎1 , 𝑎2 , . . . , 𝑎𝑛 . Над объектами определена операция сравнения на равенство. Известно, что в последовательности есть элемент присутствующий строго больше, чем 𝑛2 раз. Требуется найти элемент 𝑎 за линейное 𝒪(𝑛) времени и 𝒪(1) дополнительной памяти.
3. (2) Подотрезок с заданной суммой
Дана последовательность 𝑎1 , 𝑎2 , · · · , 𝑎𝑛 ∈ Z и 𝑆 ∈ Z.
𝑟
∑︀
Найти 𝑙, 𝑟 (1 ≤ 𝑙 ≤ 𝑟 ≤ 𝑛) такие, что сумма
𝑎𝑖 = 𝑆. 𝒪(𝑛).
𝑖=𝑙
P.S. В этой задаче можно пользоваться тем, чего мы ещё не проходили.
4. (3) Делители
Пусть 𝑑(𝑛) – количество делителей числа 𝑛. Докажите ∀𝜀 > 0, 𝑑(𝑛) = 𝑜(𝑛𝜀 ).
P.S. Более точно 𝑑(𝑛) ≈ 𝑛1/ log log 𝑛 : https://en.wikipedia.org/wiki/Divisor_function.
8/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
Разбор домашнего задания
4.1. Обязательная часть
1. Цикл for
𝑖 <𝑛
2∑︀
a)
2𝑖 = Θ(𝑛).
𝑖=1
∑︀𝑛/3
∑︀𝑛/3
𝑛
𝑛
𝑛
b)
𝑘=1 log(3𝑘) = 3 log 3 +
𝑘=1 log 𝑘 = Θ(𝑛) + Θ( 3 log 3 ) = Θ(𝑛 log 𝑛).
c) Рассмотрим первый цикл при 𝑖 > 𝑛/2, тогда второй пробегает Θ(𝑖) итераций.
Итого Θ(𝑛2 ).
d) Θ(𝑛). Внутренний цикл по всем итерациям внешнего отработает ровно 𝑛 − 1 раз.
2. Неасимптотические оптимизации
a) Делить медленно, нужно меньше делений.
for (int i = 0; i < n; i++) {
if (LLONG_MAX - a[i] * b[i] < sum) sum %= MOD;
sum += a[i] * b[i];
}
return sum % MOD;
Еще меньше делений:
for (int i = 0; i < n; i++) {
sum += a[i] * b[i];
if (sum >= MOD * MOD) sum -= MOD * MOD;
}
return sum % MOD;
b) Медленные рекурсия и pow.
double Exp(double x) {
double F = 1, Pow = 1, sum = 0;
for (int dep = 1; dep <= 20; dep++)
sum += Pow / F, F *= dep, Pow *= x;
}
% double dzeta(double x) {
%
double x_pow = 1, inv_x = 1 / x, result = 0;
%
for (int i = 0; i < 20; ++i) {
%
result += x_pow, x_pow *= inv_x;
%
}
%
return result;
% }
20
;
Также можно это просто свернуть в прогрессию: 1−1/𝑥
1−1/𝑥
c) При каждом вызове to_string создается и разрушается новая строка.
3. Подотрезок с заданной суммой
∑︀
Переберем 𝑙, найдем для каждого первое 𝑟, что 𝑟𝑙 𝑎𝑖 > 𝑆.
′
Если
∑︀𝑟′ для конкретного 𝑙 нашли конкретное 𝑟, то для меньших 𝑟 < 𝑟 сумма только меньше:
𝑙 < 𝑆 (здесь важна неотрицательность 𝑎𝑖 !).
∑︀ ′
∑︀ ′
Тогда для 𝑙 + 1 тем более не подойдут меньшие 𝑟′ : 𝑟𝑙+1 𝑎𝑖 6 𝑟𝑙 < 𝑆.
9/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
То есть 𝑟 всегда двигается вперед, можно искать его не каждый раз от 0, а от того места,
где остановились в прошлый раз.
sum = 0, r = 0;
for (int l = 0; l < n; ++l) {
while (sum < S) sum += a[r++];
if (sum == S) ok;
sum -= a[l];
}
Способ #2.
sum = 0, l = 0, r = 0;
while (r < n && sum != S) {
if (sum < S) sum += a[r++];
else sum -= a[l++];
}
Корректность. Пусть есть ответ. Пусть 𝑟 дошел до нужного места раньше 𝑙. Тогда сумма
будет > 𝑆, пока 𝑙 тоже не дойдет до места.
Если 𝑙 дошел до нужного места раньше, то наоборот.
10/11
Алгоритмы, осень 2019/20
Практика #2. Асимптотика, начало структур.
4.2. Дополнительная часть
1. Суммы
интегралы
∫︀ +∞
∑︀𝑛 и
∑︀∞ 1
1
a)
<
=
Θ(
2
2
𝑘=1 𝑘
𝑘=1 𝑘
1
−1 ⃒+∞
= Θ( 𝑥−1 ⃒1 ) = Θ(1) = 𝒪(1).
∑︀
∑︀∞ 2𝑖
∑︀∞ 1
1
Способ без интегралов. 𝑘 ∈ [2𝑖 , 2𝑖+1 ) ⇒ 𝑘12 6 212𝑖 ⇒ ∞
𝑘=1 𝑘2 6
𝑖=1 22𝑖 =
𝑖=1 2𝑖 = 2 =
𝒪(1).
∫︀ 𝑛 −1/2
∑︀𝑛
√
√ ⃒𝑛
1
b)
𝑑𝑥) = Θ(2 𝑥⃒1 ) = Θ( 𝑛).
𝑘=1 𝑘1/2 = Θ( 1 𝑥
∑︀√𝑛 2𝑎+1
∑︀
∑︀√𝑛 ∑︀(𝑎+1)2 1
∑︀√𝑛 ∑︀(𝑎+1)2 1
1
=
Способ без интегралов. 𝑛𝑘=1 𝑘1/2
= 𝑎=1
=
2
2
1/2
𝑎=1
𝑎=1 Θ(𝑎) =
𝑘=𝑎
𝑘=𝑎
Θ(𝑎)
𝑘
∑︀√𝑛
√
𝑎=1 Θ(1) = Θ( 𝑛).
c) ?
1
𝑑𝑥)
𝑥2
2. Частый элемент
Если выкидывать парами неравные элементы, то нужный нам обязательно останется.
Как выкидывать парами? Можно завести стек. Если встречаем элемент, не равный вершине
стека, делаем pop и переходим к следующему, так из рассмотрения выкинули текущий и
вершину стека. Если стек пуст, или текущий элемент равен вершине, положим в стек. В
конце в стеке ответ.
Это 𝒪(𝑛) памяти. Но мы держим в стеке одинаковые элементы, достаточно хранить только
этот элемент и размер стека.
3. Подотрезок с заданной суммой
При правой границе 𝑟 нас интересует 𝑙 6 𝑟 : sum[𝑟 + 1] − sum[𝑙] = 𝑆 ⇔ sum[𝑙] = sum[𝑟 + 1] − 𝑆.
Храним в хеш-таблице (unordred_set) sum[𝑖] для всех встреченных 𝑖, ищем там за 𝒪(1)
sum[𝑟 + 1] − 𝑆.
4. Делители
Среди чисел не более 1018 число с наибольшим числом делителей
897612484786617600 = 28 ·34 ·52 ·72 ·11·13·17·19·23·29·31·37
У него целых 103680 различных делителей.
1 𝛼2
По основной теореме арифметики 𝑛 = 𝑝𝛼1∏︀
𝑝2 . . .𝑝𝛼𝑘 𝑘 .
𝜎(𝑛) – число делителей числа 𝑛. 𝜎(𝑛) = 𝑖 (𝛼𝑖 + 1).
Заметим, что 𝑝𝑖 > 𝑖 + 1. Отсюда ясно, что (𝑘 + 1)! 6 𝑛 ⇒ 𝑘 log 𝑘 = 𝒪(log 𝑛) ⇒ 𝑘 = 𝒪( logloglog𝑛 𝑛 ).
𝐶
Предположим, что все 𝛼𝑖 = 1, тогда 𝜎(𝑛) = 2𝑘 6 𝑛 log log 𝑛 = 𝑜(𝑛𝜀 ).
Как быть с 𝛼𝑖 > 1? Предположим, что 𝑝𝑖 = 𝑖 + 1, от этого потенциальное 𝜎(𝑛) только
возрастёт. Заметим, что не выгодно брать 𝛼𝑖 > ⌈log𝑝𝑖 (𝑘 + 1)⌉,
иначе выгодно уменьшить 𝛼𝑖 на 1 и добавить в конец 𝑝𝑘+1 = 𝑘 + 1 в степени 1.
𝑘
𝑘
𝑘
∏︀
∑︀
∑︀
Теперь 𝜎(𝑛) 6 (1+⌈log𝑝𝑖 (𝑘 +1)⌉), log 𝜎(𝑛) 6
log(1+⌈log𝑖+1 (𝑘 +1)⌉) 6 2𝑘 + log(log𝑖 𝑘) =
𝑖=1
2𝑘 +
𝑘
∑︀
𝑖=2
𝑘
log log
= 2𝑘 +
log 𝑖
𝑖=1
𝑘
∑︀
(log log 𝑘 − log log 𝑖) = 2𝑘 + Θ( log𝑘 𝑘 ) ⇒ 𝜎(𝑛) 6 23𝑘 =
𝑖=2
Также можно почитать рассуждение от Миши Тихомирова на codeforces.
11/11
𝑖=2
𝐶
𝑛 log log 𝑛
= 𝑜(𝑛𝜀 )
Скачать