Школьный этап, 9–11 классы всероссийской олимпиады по информатике: 5 задач с официальным разбором, к каждой наше решение на C++.
К задаче 1Школьный этап ВсОШ по информатике 2026/27, 9–11 классы.
Задачи тура
- 1Доставка в Простоквашиноматематика, моделирование
- 2Места в автобусематематика, разбор случаев
- 3Три Кирилла и их ростматематика, комбинаторика
- 4Очередь на заправкумоделирование
- 5Мост из отрезковконструктив, геометрия
Отмечайте решённые: 0 из 5
1. Доставка в Простоквашино
Недавно почтальон Печкин прочитал в газете о новых роботах-доставщиках и решил использовать одного из них для развоза почты. Он планирует внедрять данную инновацию в деревне Простоквашино, которую можно представить как длинную улицу: в начале — отделение почты, за ним — жилые дома, расположенные в ряд на равном расстоянии друг от друга.
Чтобы проехать от отделения почты до первого дома или от одного дома до соседнего, роботу Печкина требуется единиц энергии. Если робот имеет меньше единиц энергии, он не сможет доехать до следующего дома. При этом предусмотрительный почтальон возле каждого дома разместил зарядную станцию, на которой уровень заряда робота может увеличиться на единиц. Каждой зарядной станцией робот может воспользоваться только один раз.
В качестве «тестового задания» Печкин хочет доставить с помощью робота журнал «Мурзилка» дяде Фёдору, который живет в -м по счёту от почты доме. Теперь ему предстоит выяснить, на какое минимальное число единиц энергии ему необходимо изначально зарядить робота, чтобы тот мог доехать от почты до дома дяди Фёдора.
Формат входных данных
В первой строке входных данных содержится целое число () — количество энергии, необходимое роботу для того, чтобы проехать расстояние между двумя домами.
Во второй строке содержится целое число () — количество энергии, на которое робот может зарядиться у каждого дома.
В третьей строке содержится целое число () — номер (по счёту от здания почты) дома дяди Фёдора.
Гарантируется, что .
Формат выходных данных
Выведите одно число — минимальное количество единиц энергии, на которое Печкину необходимо зарядить робота.
Система оценки
Решения, правильно работающие при , будут оцениваться в 30 баллов.
Решения, правильно работающие при , будут оцениваться в 75 баллов.
Решения, корректно работающие при , будут оцениваться в 35 баллов.
Замечание
В первом примере из условия достаточно зарядить робота на 6 единиц энергии. На дорогу до первого дома он потратит 2 единицы, и у первого дома он зарядится на 1 единицу, у него останется 5 единиц. После зарядки у второго дома останется 4 единицы энергии, у третьего — 3, у четвёртого — 2 и доставщику хватит энергии, чтобы добраться до пятого дома. Можно показать, что меньшего количества энергии роботу не хватит.
| стандартный ввод | стандартный вывод |
|---|---|
2 1 5 | 6 |
| стандартный ввод | стандартный вывод |
|---|---|
2 4 10 | 2 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
Сначала рассмотрим переборное решение. Будем перебирать возможное начальное количество энергии в переменной ans, начиная с нуля. Для каждого значения проверим, сможет ли робот доехать до дома дяди Фёдора. Для этого будем моделировать его движение: сначала уменьшим запас энергии на , а если он не стал отрицательным, увеличим его на . Если робот смог совершить все поездок между соседними зданиями, то текущее значение ans и будет ответом.
Пример такого решения.
a = int(input())
b = int(input())
k = int(input())
ans = 0
while True:
cc = ans
for i in range(k):
cc -= a
if cc < 0:
break
cc += b
else:
print(ans)
break
ans += 1Это решение перебирает все возможные значения ответа и для каждого из них моделирует движение робота. Если ответ равен , то время работы составляет . В худшем случае это слишком долго, поэтому попробуем улучшить алгоритм.
Заметим, что для проверки начального заряда не обязательно моделировать движение робота отдельно для каждого возможного ответа. Вместо этого можно один раз проследить за изменением его энергии, считая, что изначально она равна нулю. При этом в ходе передвижения робота значение его энергии может стать отрицательным, разрешим роботу передвигаться с отрицательной энергией, запоминая минимальное (отрицательное) значение энергии, которое было у робота в ходе поездки.
Введём переменную cnt, которая будет показывать, как изменился запас энергии относительно начального значения. При каждом перемещении робота на расстояние между соседними зданиями будем уменьшать cnt на , а затем запоминать минимальное значение этой переменной в opt. После этого увеличим cnt на , поскольку робот может воспользоваться зарядной станцией у достигнутого дома.
Начальный заряд должен быть не меньше модуля минимального значения opt. Действительно, если в некоторый момент значение cnt равно , то для сохранения неотрицательного запаса энергии к этому моменту необходимо было изначально иметь не менее единиц энергии.
Пример такого решения.
a = int(input())
b = int(input())
k = int(input())
cnt = 0
opt = 0
for i in range(k):
cnt -= a
opt = min(opt, cnt)
cnt += b
print(-opt)В этом решении каждый шаг моделирования выполняется за , поэтому общая сложность составляет . Такое решение проходит тесты с ограничением , но для полного решения при его недостаточно.
Рассмотрим теперь решение за .
Если , то после подзарядки запас энергии робота увеличивается или остаётся прежним. Поэтому достаточно изначально зарядить робота на единиц энергии: он сможет доехать до первого дома, а затем после каждой подзарядки у него будет достаточно энергии для следующего перемещения. Если , робот может добраться до любого дома без начального заряда, что также соответствует этой формуле.
Теперь рассмотрим случай . После каждого перемещения и последующей подзарядки запас энергии уменьшается на единиц, то есть заряд робота будет уменьшаться при передвижении к последующему дому. Нам нужно так зарядить робота в начале, чтобы он смог доехать до последнего дома, значит, нужно рассмотреть случай, когда робот доезжает до последнего дома с нулевым зарядом. Суммарно на передвижение ему понадобится единиц энергии, но при этом он раз зарядится на единиц энергии. Если его первоначальный заряд был равен , то тогда , откуда .
Итак, ответ можно вычислить по формуле
Пример полного решения.
a = int(input())
b = int(input())
k = int(input())
if b >= a:
print(a)
else:
print(k * a - (k - 1) * b)Это решение использует постоянное число арифметических операций, поэтому его временная сложность составляет , а дополнительная память — . По условию , поэтому вычисления укладываются в заданные ограничения и решение набирает 100 баллов.
Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long a, b, k;
cin >> a >> b >> k;
if (b >= a) {
cout << a << "\n";
} else {
cout << k * a - (k - 1) * b << "\n";
}
return 0;
}2. Места в автобусе
Нумерация мест в автобусе устроена так, как показано на рисунке.

На рисунке изображены первые пять рядов мест. Если в автобусе больше пяти рядов, последующие ряды нумеруются аналогично.
Два друга купили билеты на места с номерами и . Они планируют играть в шахматы во время поездки и хотят определить, насколько удобно им будет играть на этих местах. Они разделили варианты расположения мест на четыре категории.
1. Идеально удобно — друзья сидят рядом, с одной стороны от прохода. Например, места 1 и 2 или 7 и 8.
2. Довольно удобно — друзья сидят в одном ряду, по разные стороны от прохода, оба места находятся у прохода. Например, места 2 и 4.
3. Можно играть — друзья сидят в соседних рядах, с одной и той же стороны от прохода. Например, места 1 и 5 или 4 и 7.
4. Невозможно играть — во всех остальных случаях.
Помогите определить, насколько удобно друзьям играть в шахматы.
Формат входных данных
В первой строке входных данных записано целое число — номер места первого друга.
Во второй строке записано целое число — номер места второго друга.
Числа и различны и удовлетворяют условиям , .
Формат выходных данных
Программа должна вывести одно целое число от 1 до 4 — категорию удобства расположения мест.
| стандартный ввод | стандартный вывод |
|---|---|
1 2 | 1 |
| стандартный ввод | стандартный вывод |
|---|---|
4 2 | 2 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
В каждом ряду находится четыре места, причём проход разделяет их на две пары. Номера мест в первом ряду — 1, 2, 3, 4, во втором — 5, 6, 7, 8 и так далее. Также заметим, что у прохода находятся места с чётными номерами, а места с нечётными номерами находятся у окна.
Будем считать, что ряды нумеруются с нуля. Тогда номер ряда, в котором находится место с номером , можно вычислить по формуле (x - 1) // 4, где // — целочисленное деление. Аналогично вычислим номер ряда для места .
В начале выполнения программы удобно сделать так, чтобы . Если изначально , поменяем значения переменных местами.
Теперь рассмотрим все четыре категории расположения мест.
1. Идеально удобно.
В этом случае друзья сидят рядом, с одной стороны от прохода. Такие места имеют номера 1 и 2, 5 и 6, 9 и 10 и так далее либо 3 и 4, 7 и 8, 11 и 12 и так далее.
Поскольку , для первой пары мест в ряду номер должен быть нечётным, а номер должен равняться . Кроме того, оба места должны находиться в одном ряду.
2. Довольно удобно.
В этом случае друзья сидят в одном ряду по разные стороны от прохода, причём оба места находятся у прохода. У прохода находятся места с чётными номерами, поэтому оба значения и должны быть чётными, а также места должны находиться в одном ряду.
3. Можно играть.
В этом случае друзья сидят на соседних рядах с одной и той же стороны от прохода. Например, места 1 и 5, а также места 4 и 8.
В этом случае нам неважно, находятся ли места у прохода или у окна, они только должны находиться в соседних рядах и по одну сторону от прохода. Рассмотрим места с номерами x + x % 2 и y + y % 2, где % — операция взятия остатка от деления. В результате преобразования нечётный номер места сменится на номер соседнего с ним места у прохода. То есть места x + x % 2 и y + y % 2 будут находиться оба у прохода, и они будут либо совпадать с местами и , либо будут соседними с ними.
Два места у прохода будут находиться друг за другом (то есть в соседних рядах по одну сторону от прохода), если их номера отличаются на 4. То есть в третьем случае нужно проверить условие x + x % 2 + 4 == y + y % 2.
Если ни одно из перечисленных условий не выполняется, расположение мест относится к четвёртой категории.
Полное решение выглядит следующим образом.
x = int(input())
y = int(input())
if x > y:
x, y = y, x
row1 = (x - 1) // 4
row2 = (y - 1) // 4
if row1 == row2 and x % 2 == 1 and y == x + 1:
print(1)
elif row1 == row2 and x % 2 == 0 and y % 2 == 0:
print(2)
elif x + x % 2 + 4 == y + y % 2:
print(3)
else:
print(4)Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long x, y;
cin >> x >> y;
if (x > y) swap(x, y);
long long rowX = (x - 1) / 4;
long long rowY = (y - 1) / 4;
int category;
if (rowX == rowY && x % 2 == 1 && y == x + 1) {
category = 1;
} else if (rowX == rowY && x % 2 == 0 && y % 2 == 0) {
category = 2;
} else if (x + x % 2 + 4 == y + y % 2) {
category = 3;
} else {
category = 4;
}
cout << category << "\n";
return 0;
}3. Три Кирилла и их рост
Три Кирилла участвовали в турнире по волейболу. Чтобы различать всех Кириллов, им дали номера «первый», «второй» и «третий».
Оказалось, что первый Кирилл не выше второго, а второй — не выше третьего. Также друзья в перерыве между мероприятиями решили встать друг другу на головы и поставить блок, и таким образом выяснили, что их суммарный рост равен .
Кириллы сообщили вам число и просят посчитать количество различных вариантов значений их роста. Два варианта считаются различными, если у хотя бы одного Кирилла отличается рост в этих вариантах.
Иными словами, если обозначить рост первого, второго и третьего Кириллов целыми положительными числами , и соответственно, необходимо найти количество троек целых положительных чисел — таких что и .
Формат входных данных
Программа получает на вход одно целое число () — сумму ростов Кириллов.
Формат выходных данных
Программа должна вывести одно целое число — количество возможных вариантов роста Кириллов.
Обратите внимание на то, что значение ответа в этой задаче может превышать максимальное возможное значение 32-битной целочисленной переменной, поэтому необходимо использовать 64-битные целочисленные типы данных (тип int64 в языке Pascal, тип long long в C++, тип long в Java и C#).
Система оценки
Решения, правильно работающие при , будут оцениваться в 25 баллов.
Решения, правильно работающие при , будут оцениваться в 50 баллов.
Решения, правильно работающие при , будут оцениваться в 80 баллов.
Замечание
Во втором примере из условия подходят следующие варианты роста: .
| стандартный ввод | стандартный вывод |
|---|---|
3 | 1 |
| стандартный ввод | стандартный вывод |
|---|---|
7 | 4 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
Сначала рассмотрим решение полным перебором. Будем перебирать все возможные значения роста первого Кирилла , второго Кирилла и третьего Кирилла . При этом сразу учтём условие , чтобы не рассматривать лишние варианты. Если сумма ростов равна , увеличим счётчик ответа.
n = int(input())
ans = 0
for a in range(1, n + 1):
for b in range(a, n + 1):
for c in range(b, n + 1):
if a + b + c == n:
ans += 1
print(ans)Каждый из трёх циклов может выполнить до итераций. Следовательно, временная сложность решения составляет . При больших значениях такое решение работает слишком долго.
Попробуем избавиться от одного цикла. Если мы уже выбрали рост первого Кирилла и рост второго Кирилла , то рост третьего Кирилла однозначно определяется условием на сумму .
Остаётся убедиться в том, то рост третьего Кирилла не меньше роста второго, то есть . Если это условие выполняется, найден ещё один подходящий вариант.
n = int(input())
ans = 0
for a in range(1, n + 1):
for b in range(a, n + 1):
c = n - a - b
if c >= b:
ans += 1
print(ans)Теперь используются только два вложенных цикла, поэтому временная сложность составляет . Это решение быстрее предыдущего, но при его также недостаточно.
Рассмотрим следующую оптимизацию. Заметим, что при фиксированном росте второго Кирилла можно непосредственно посчитать количество подходящих значений роста первого Кирилла .
Из условия монотонности ростов следует, что .
Кроме того, поскольку и , получаем , откуда .
Таким образом, рост первого Кирилла может принимать любое целое значение от 1 до .
Если эта величина положительна, количество подходящих значений равно ей. Если же она неположительна, подходящих значений нет. Поэтому для каждого значения можно увеличить ответ на .
Получаем следующее решение.
n = int(input())
ans = 0
for b in range(1, n):
ans += max(0, min(b, n - 2 * b))
print(ans)В этом решении один цикл, поэтому временная сложность составляет . Такое решение проходит тесты с ограничением , но для полного решения необходимо избавиться и от цикла.
Для этого заметим, что слагаемые в полученной сумме имеют достаточно простой вид.
Если , то , поэтому количество подходящих значений роста первого Кирилла равно . Обозначим .
Сумма значений всех от 1 до равна .
Осталось рассмотреть значения . Для них количество подходящих значений равно , так как в этом случае , если эта величина положительна, и нулю в противном случае.
При увеличении на единицу величина уменьшается на два. Следовательно, оставшиеся положительные слагаемые образуют последовательность чисел одинаковой чётности, начиная с . Необходимо складывать числа до тех пор, пока они остаются положительными.
Количество членов этой арифметической прогрессии равно , округлённому вверх, а последний член этой арифметической прогрессии имеет такую же чётность, что и число , поэтому он равен . Сумма прогрессии может быть вычислена по формуле
Таким образом, можно вычислить ответ без перебора всех значений .
n = int(input())
m = n // 3
ans = m * (m + 1) // 2
m += 1
k = n - 2 * m
ans += (k + 2 - k % 2) * ((k + 1) // 2) // 2
print(ans)Сложность такого решения составляет .
Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n;
cin >> n;
long long m = n / 3;
long long answer = m * (m + 1) / 2;
long long k = n - 2 * (m + 1);
if (k > 0) {
answer += (k + 2 - k % 2) * ((k + 1) / 2) / 2;
}
cout << answer << "\n";
return 0;
}4. Очередь на заправку
На заправке есть две колонки: одна предназначена для машин с горловиной бензобака справа, другая — для машин с горловиной слева. Каждая машина может заправиться только на подходящей для неё колонке.
Перед заправкой образовалась очередь. -я машина в очереди заправляется минут, также для каждой машины известно расположение горловины бензобака. Очередь к колонкам общая, машины в очереди не могут обгонять друг друга. Поэтому следующая машина может начать заправку только после того, как предыдущая машина заедет на заправку, и только если подходящая для неё колонка свободна.
Сколько времени потребуется, чтобы заправить все машины? Изначально обе колонки свободны. Временем, необходимым для передвижения машин, следует пренебречь.
Формат входных данных
Первая строка входных данных содержит одно целое число () — количество машин.
Каждая из следующих строк содержит одно целое число (, ). Знак числа обозначает расположение горловины бензобака ( для горловины слева, иначе справа), модуль — продолжительность заправки.
Формат выходных данных
Программа должна вывести одно целое число — сколько минут необходимо на заправку всех машин.
Обратите внимание на то, что значение ответа в этой задаче может превышать максимальное возможное значение 32-битной целочисленной переменной, поэтому необходимо использовать 64-битные целочисленные типы данных (тип int64 в языке Pascal, тип long long в C++, тип long в Java и C#).
Система оценки
Решения, правильно работающие при , будут оцениваться в 30 баллов.
Решения, правильно работающие при , будут оцениваться в 50 баллов.
Замечание
Пояснение к примеру из условия.
- Первая машина заезжает сразу и заправляется 1 минуту;
- вторая машина ждёт 1 минуту, чтобы освободилась её колонка, затем заезжает и заправляется 2 минуты;
- третья машина заезжает сразу после второй и заправляется 3 минуты.
Поскольку вторая и третья машины начинают заправку одновременно, последняя машина заканчивает заправку через минуты после начала заправки первой.
| стандартный ввод | стандартный вывод |
|---|---|
3 -1 -2 3 | 4 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
30 баллов можно получить, моделируя процесс поминутно. Сложность такого решения , и оно не представляет интереса, поэтому сразу перейдём к полному решению.
Будем последовательно рассматривать машины в порядке их расположения в очереди. Для каждой машины необходимо определить, когда она сможет начать заправку.
Поскольку на заправке две колонки, достаточно хранить временную отметку момента освобождения каждой из них. Будем использовать для этого список free из двух элементов. Изначально обе колонки свободны, поэтому оба элемента равны нулю. Колонки будем нумеровать с нуля, как и элемент списка.
Если время заправки очередной машины положительно, то её горловина расположена справа, и она должна воспользоваться колонкой 0. Если время отрицательно, то горловина расположена слева, и машине нужна колонка 1. Для удобства будем хранить номер подходящей колонки в переменной i, а продолжительность заправки вычислять как модуль числа .
Пусть curr_time — время, когда предыдущая машина начала заправку. Очередная машина не может начать заправку раньше этого момента. Кроме того, выбранная колонка должна быть свободна. Поэтому время начала заправки равно максимуму из curr_time и времени освобождения соответствующей колонки: curr_time = max(curr_time, free[i]).
После этого обновим время освобождения колонки. Если машина заправляется минут, то колонка освободится через минут после начала заправки: free[i] = curr_time + |t|.
Таким образом, для каждой машины достаточно выполнить несколько операций. После обработки всех машин ответом будет максимальное время освобождения двух колонок, поскольку необходимо дождаться окончания заправки всех машин.
Полное решение выглядит следующим образом.
n = int(input())
free = [0, 0]
curr_time = 0
for _ in range(n):
t = int(input())
if t > 0:
i = 0
else:
i = 1
curr_time = max(curr_time, free[i])
free[i] = curr_time + abs(t)
print(max(free))Каждая машина обрабатывается ровно один раз, причём на её обработку требуется постоянное число операций. Поэтому временная сложность алгоритма составляет .
Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
long long freeAt[2] = {0, 0};
long long currentTime = 0;
for (int i = 0; i < n; i++) {
long long t;
cin >> t;
int pump = t > 0 ? 0 : 1;
currentTime = max(currentTime, freeAt[pump]);
freeAt[pump] = currentTime + llabs(t);
}
cout << max(freeAt[0], freeAt[1]) << "\n";
return 0;
}5. Мост из отрезков
У вас есть фрагментов для строительства моста. Каждый фрагмент имеет единичную длину. Используя все эти фрагменты, постройте мост, соединяющий точку с точкой . Мост представляет собой несамопересекающуюся ломаную, состоящую из отрезков единичной длины. Концы каждого отрезка должны иметь целочисленные координаты, а каждый отрезок должен быть параллелен одной из осей координат.
Формат входных данных
Первая строка входных данных содержит целое число — количество фрагментов моста, . В следующих двух строках содержатся целые числа и — координаты конца моста. Числа и — целые, по модулю не превосходят , при этом точка не совпадает с началом координат.
Формат выходных данных
Если такой мост построить нельзя, программа должна вывести число .
Если такой мост построить можно, программа должна вывести строку, в каждой строке должно быть два целых числа — координаты вершин ломаной в порядке обхода. Первая вершина должна иметь координаты , последняя — . Две соседние вершины ломаной должны находиться на расстоянии 1. Ломаная не должна иметь самопересечений. Можно вывести любой подходящий мост.
Замечание
Мост из первого примера изображён на рисунке.

| стандартный ввод | стандартный вывод |
|---|---|
7 2 -1 | 0 0 1 0 1 -1 1 -2 2 -2 3 -2 3 -1 2 -1 |
| стандартный ввод | стандартный вывод |
|---|---|
1 1000 1000 | -1 |
Официальный разборСначала попробуйте решить сами
Наше решение на C++Полный код на C++17
Официальный разбор
Задачу можно сформулировать по-другому: необходимо построить несамопересекающийся маршрут длины из точки в точку , проходящий по линиям целочисленной сетки. Каждый отрезок маршрута соединяет две соседние целочисленные точки и имеет единичную длину. Поэтому за один шаг ровно одна координата меняется на или .
Чтобы добраться из в , необходимо сделать не менее горизонтальных и вертикальных шагов. Следовательно, минимальное число отрезков равно . Если , построить мост невозможно.
Кроме того, каждый шаг меняет чётность суммы координат. Поэтому чётность суммы координат конечной точки должна совпадать с чётностью числа . Если это условие не выполняется, мост также построить нельзя. Именно эти две проверки выполняются в начале программы.
Если , то мост необходимо построить по кратчайшему пути. Достаточно пройти шагов по горизонтали в сторону координаты , а затем шагов по вертикали в сторону координаты . Пример такого пути показан на рисунке.

Если , то необходимо добавить к кратчайшему пути дополнительные отрезки. Поскольку чётность и должна совпадать, разность чётна. Значит, можно добавить отрезки парами, количество пар равно .
Добавлять дополнительные отрезки будем так. Сначала выполняются шагов в направлении, противоположном вертикальному направлению к конечной точке. Затем выполняются все горизонтальные шаги в направлении , после чего выполняются вертикальных шагов в направлении конечной точки.
Первые дополнительные шаги компенсируются последними: на вертикальном участке путь сначала отходит от конечной точки, а затем возвращается. Это позволяет использовать все отрезков, при этом получится несамопересекающаяся ломаная.
На рисунке показано построение для , , . Здесь .

В примере решения функция sign возвращает 1, если аргумент неотрицателен, и в противном случае. Значение функции sign соответствует движению в направлении точки, а для движения в противоположном направлении необходимо взять противоположное значение. Переменные dir1x и dir1y задают направление горизонтальных шагов, а dir2x и dir2y — направление вертикальных шагов. Количества шагов в этих направлениях хранятся в cnt1 и cnt2.
Затем программа выводит начальную точку и координаты каждой следующей вершины:
1. выполняет extra шагов против направления второго участка;
2. выполняет cnt1 шагов в направлении первого участка;
3. выполняет extra + cnt2 шагов в направлении второго участка.
Но если горизонтального участка нет, то первоначальное движение в противоположном направлении вдоль оси и последующее движение к конечной точке пересекутся. Это произойдёт, если . Вместо того чтобы отдельно рассматривать этот случай, просто поменяем горизонтальные и вертикальные участки местами, то есть сначала будем передвигаться вдоль оси , затем — вдоль оси на ненулевое значение (потому что числа и не могут быть равны 0 одновременно), затем снова вдоль оси в другом направлении.
Пример решения. Это решение работает и в случае , так как тогда и дополнительные шаги не совершаются.
import sys
n = int(input())
x = int(input())
y = int(input())
if (x + y) % 2 != n % 2 or n < abs(x) + abs(y):
print(-1)
sys.exit(0)
def sign(a):
if a >= 0:
return 1
else:
return -1
dir1x = sign(x)
dir1y = 0
cnt1 = abs(x)
dir2x = 0
dir2y = sign(y)
cnt2 = abs(y)
if x == 0:
dir1x, dir2x = dir2x, dir1x
dir1y, dir2y = dir2y, dir1y
cnt1, cnt2 = cnt2, cnt1
extra = (n - abs(x) - abs(y)) // 2
x = 0
y = 0
print(x, y)
for i in range(extra):
x -= dir2x
y -= dir2y
print(x, y)
for i in range(cnt1):
x += dir1x
y += dir1y
print(x, y)
for i in range(extra + cnt2):
x += dir2x
y += dir2y
print(x, y)Наше решение на C++
#include <bits/stdc++.h>
using namespace std;
int sign(long long value) {
return value >= 0 ? 1 : -1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n, x, y;
cin >> n >> x >> y;
long long minimal = llabs(x) + llabs(y);
if (((x + y) % 2 + 2) % 2 != n % 2 || n < minimal) {
cout << -1 << "\n";
return 0;
}
int dir1x = sign(x), dir1y = 0;
long long cnt1 = llabs(x);
int dir2x = 0, dir2y = sign(y);
long long cnt2 = llabs(y);
if (x == 0) {
swap(dir1x, dir2x);
swap(dir1y, dir2y);
swap(cnt1, cnt2);
}
long long extra = (n - minimal) / 2;
long long cx = 0, cy = 0;
string out;
auto emit = [&]() {
out += to_string(cx);
out += ' ';
out += to_string(cy);
out += '\n';
};
emit();
for (long long i = 0; i < extra; i++) {
cx -= dir2x;
cy -= dir2y;
emit();
}
for (long long i = 0; i < cnt1; i++) {
cx += dir1x;
cy += dir1y;
emit();
}
for (long long i = 0; i < extra + cnt2; i++) {
cx += dir2x;
cy += dir2y;
emit();
}
cout << out;
return 0;
}Сначала решите сами
Возьмите столько времени, сколько было на олимпиаде, и только потом открывайте разбор.
Другие туры
2026/27
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы6 задач
- Школьный этап, 5–6 классы5 задач
2025/26
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы6 задач
- Школьный этап, 5–6 классы5 задач
- Муниципальный этап, 9–11 классы5 задач
- Муниципальный этап, 7–8 классы6 задач
2024/25
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы7 задач
- Школьный этап, 5–6 классы5 задач
- Муниципальный этап, 9–11 классы5 задач
- Муниципальный этап, 7–8 классы7 задач
2023/24
- Школьный этап, 9–11 классы5 задач
- Школьный этап, 7–8 классы7 задач
- Школьный этап, 5–6 классы5 задач
- Муниципальный этап, 9–11 классы5 задач
- Муниципальный этап, 7–8 классы7 задач