# Разбор задач 2020 года Если Вы не знали, как решать какую-то задачу, и Вам было лень разбираться в коде на Pascal, то это пост специально для Вас. :::warning Убедительная просьба не копировать коды и не пересылать их. Помните -- Вы решаете задачи для себя! ::: ## [1. Неладная фасовка](https://codeforces.com/group/skz9fIV5Hq/contest/306592/problem/A) :::spoiler Подсказка 1 :::info Заметим, что во вводе нам дана арифметическая прогрессия; мы можем за $O(1)$ найти стоимость $n$-го подарка: $A_n = A + D(n-1)$. Если же $D=0$ (т.е. стоимость товаров одинаковая), достаточно просто воспользоваться формулой $n=\lfloor \frac{M}{A} \rfloor$. ::: :::spoiler Подсказка 2 :::info Сумма первых $n$ членов такой арифметической прогрессии: $S_n = \frac{A + A_n}{2} * n$, или $S_n = \frac{2A + D(n-1)}{2} * n$. Нужно найти последнее целое $n$ такое, чтобы $S_n$ не превышало $M$. ::: :::spoiler Подсказка 3 :::info Можно воспользоваться бинарным поиском (т.к. значение $S_n$ возрастает при увеличении $n$) или вывести $n$ из формулы $M = \frac{2A + D(n-1)}{2} * n$. Асимптотика $O(log \hspace{1mm} M)$ или $O(1)$. ::: :::spoiler Разбор :::info Выполнив преобразования формулы $M = \frac{2A + D(n-1)}{2} * n$, получим квадратное уравнение: $Dn^2 + (2A - D)n - 2M = 0$. Дискриминант этого уравнения равен $(2A-D)^2 + 8DM$. Формула $n$: $\lfloor \frac{-(2A-D) + \sqrt{(2A-D)^2 + 8DM}}{2D} \rfloor$. ::: :::spoiler Код (Python, автор: [Wani4ka](https://codeforces.com/profile/Wani4ka)) ```python import math a, d, m = map(int, input().split()) def solve(): if d == 0: return m // a dis = (a*2-d)**2 + d*m*8 return math.floor((d - a*2 + math.sqrt(dis)) / (d*2)) print(solve()) ``` ::: ## [2. Составной плот](https://codeforces.com/group/skz9fIV5Hq/contest/306592/problem/B) :::spoiler Разбор :::info Давайте воспользуемся бинарным поиском для нахождения максимальной стороны примерно за $O(N \hspace{1mm} log \hspace{1mm} max(A_i))$. В функции проверки мы должны найти количество "подбрёвен", которые мы можем получить из исходных брёвен, и убедиться, что оно не меньше переданной нам стороны. ::: :::spoiler Код (C++, автор: [Wani4ka](https://codeforces.com/profile/Wani4ka)) ```cpp #include <bits/stdc++.h> using namespace std; const int N = 1e5+7; int n; long long a[N]; bool ok(long long side) { long long cnt = 0; for (int i = 0; i < n; ++i) cnt += a[i] / side; return cnt >= side; } int main() { cin >> n; for (int i = 0; i < n; ++i) cin >> a[i]; int l = 0, r = N; while (l+1 < r) { int mid = (l+r)/2; if (ok(mid)) l = mid; else r = mid; } cout << l << endl; return 0; } ``` ::: ## [3. Поиск книги](https://codeforces.com/group/skz9fIV5Hq/contest/306592/problem/C) :::spoiler Разбор :::info Сделаем то, о чём сказано в условии. * Первый массив будет хранить префикс-сумму $B_i$. Условно, $pref_i$ - это последний номер книги, который можно встретить в $i$-м шкафу или ему предшествующих. * Второй массив будет хранить информацию о самих шкафах, т.е. в $info_i$ хранится номер комнаты и номер $i$-го шкафа. Ограничения (до $100$ комнат, до $1000$ шкафов в комнате, всего до $10^5$ шкафов) позволяют нам хранить информацию о книгах и шкафах в таком виде. Теперь, когда нам приходит запрос о местоположении очередной книги, мы при помощи бинарного поиска находим в первом массиве номер шкафа, в котором находится эта книга, и выводим информацию о нём из второго массива. Асимптотика: $O(K \hspace{1mm} log \hspace{1mm} sum)$ плюс построение за $O(sum)$, где $sum = \sum\limits_{i=1}^N A_i)$. ::: :::spoiler Код (C++, автор: [Wani4ka](https://codeforces.com/profile/Wani4ka)) ```cpp #include <bits/stdc++.h> using namespace std; int main() { int n, k; cin >> n >> k; vector<pair<int, int>> v; long long x; long long en = 1; for (int i = 1; i <= n; ++i) { cin >> x; for (int j = 1; j <= x; ++j, ++en) v.push_back({i, en}); } en = 0; vector<long long> pref; for (auto to : v) { cin >> x; en += x; pref.push_back(en); } while (k--) { cin >> x; int idx = lower_bound(pref.begin(), pref.end(), x) - pref.begin(); cout << v[idx].first << " " << v[idx].second << endl; } return 0; } ``` ::: ## [4. Рассадка гостей](https://codeforces.com/group/skz9fIV5Hq/contest/306592/problem/D) :::spoiler Разбор :::info Давайте преформулируем задачу на язык олимпиадного программирования: Вам дано число $N$. Вам нужно вывести множество чисел, сумма которого равна N, а НОК максимален. Замечания: 1. Легко доказать, что нам всегда выгодно добавлять в множество либо простое число, либо его степень. Это верно, т.к. на очередной операции мы "по-максимуму" умножим наш ответ (НОК). Давайте напишем динамику $dp_{i,j} = ans$, где $i$ -- параметр суммы чисел из множеста, $j$ -- номер расматриваемого очередного простого числа (или его степень), проще говоря, сколько простых чисел (или их степеней) мы уже рассмотрели, и $ans$ -- максимальный НОК. Переходы: 1. Пропускаем текущее простое число $dp_{i,j} = dp_{i,j - 1}$; 2. Хотим взять очередное простое число (или его степень) $dp_{i,j} = max(dp_{i,j}, dp_{i - (prost_j)^{st}} * (prost_j)^{st})$; Не забываем хранить предков! Самое неприятное в этой задаче было то, что ответ не влезал в long long. Без "длинки", к сожалению, можно было только взять 40 баллов. ::: :::spoiler Код (Python, автор: [Adamenko](https://codeforces.com/profile/Adamenko)) ```python def array1d(n): arr = [] for i in range(n): arr.append(0) return arr def array2d(n, m): arr = [] for i in range(n): arr.append(array1d(m)) return arr dp = array2d(1000 + 1, 200) pr = [] prost = array1d(1005) mac = array2d(1000 + 1, 200) def output(i, j): if i == 0: return if j < 0: i = 0 output(i - mac[i][j], j-1) if mac[i][j]: for k in range(2, mac[i][j]+1): print(i + k - mac[i][j], end=' ') print(i - mac[i][j] + 1, end=' ') pr.append(0) for i in range(2, 1001): if prost[i] == 0: prost[i] = i pr.append(i) for j in pr: if (j * i <= 1000) and (j <= prost[i]): prost[i * j] = j else: break n = int(input()) for i in range(len(pr)): mac[i][0] = i if i == 0: dp[0][0] = 1 else: dp[0][i] = 1 dp[i][0] = 0 for i in range(1, n+1): for j in range(1, len(pr)): dp[i][j] = dp[i][j-1] lol = 1 while lol <= i: if dp[i][j] < lol * dp[i-lol][j-1]: dp[i][j] = lol * dp[i-lol][j-1] mac[i][j] = lol lol *= pr[j] ma = -1 x = -1 y = -1 for i in range(len(pr)): if ma < dp[n][i]: ma = dp[n][i] x = n y = i print(ma) output(x, y) print() ``` :::