---
# System prepended metadata

title: Разбор задач 2020 года

---

# Разбор задач 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()
```
:::