Спешу познакомить вас с авторскими идеями решений задач сегодняшнего раунда. Авторы разбора первых пяти задач — HolkinPV и gridnevvvit, разбор последних двух задач написан мной.
479A - Выражение
В этой задаче нужно было рассмотреть несколько случаев и выбрать лучших ответ. Это можно было сделать так:
int ans = a + b + c;
ans = max(ans, (a + b) * c);
ans = max(ans, a * (b + c));
ans = max(ans, a * b * c);
cout << ans << endl;
479B - Башни
Эта задача решается жадным образом. На каждой итерации нашего алгоритма будем перекладывать кубик с башни с максимальной высотой на башню с минимальной высотой. Для этого можно каждый раз за время O(N) заново находить позиции минимума и максимума в массиве. Итоговое решение тогда будет иметь асимптотику O(KN).
479C - Экзамены, 480A - Экзамены
Эта задача решается жадным образом. Отсортируем все экзамены по неубыванию даты фактической сдачи (то есть по ai), при равенстве по неубыванию даты досрочной сдачи (то есть bi). Рассмотрим экзамены в получившемся порядке и будем поддерживать текущий ответ. В первую очередь будем стараться сдать очередной экзамен досрочно (если это не нарушит условие задачи, то есть если никакой предыщий экзамен мы не сдавали позже досрочной даты этого экзамена). В противном случае сдадим этот экзамен в дату его фактической сдачи. Обновим текущим ответ выбранной датой сдачи этого экзамена.
std::sort(a, a + n); // а - массив пар, где первый элемент фактическая сдача экзамена, а второй элемент - досрочная
int best = -1;
for(int i = 0; i < n; i++) {
if (best <= a[i].second) {
best = a[i].second;
} else {
best = a[i].first;
}
}
479D - Прыжки в длину, 480B - Прыжки в длину
Нетрудно догадаться, что ответом на эту задачу может быть 0, 1 или 2. Если изначально мы можем отмерить расстояния x и y, то сразу выведем 0. Иначе попробуем получить ответ нанесением одной метки. Если это не удастся, в качестве ответа всегда можно вывести две метки, равные x и y соответственно.
Чтобы проверить, можно ли получить ответ 1, переберем каждую из имеющихся отметок, пусть она находится на расстоянии r. Тогда попробуем добавить отметку на одной из позиций: r - x, r + x, r - y, r + y. Если хотя бы в одном из случаев станет возможным отмерить x и y, то можно сразу выводить полученный ответ. Проверить, что можем отмерить x и y, просто. К примеру, пусть мы сделали отметку в r + x (т.е. x мы уже научились отмерять). Тогда надо лишь проверить, что есть отметка в r + x + y или r + x - y, например, бинарным поиском (т.е. отметки отсортированы). Надо также не забыть проверить, что отметки попадают в [0, L].
479E - Катаемся на лифте, 480C - Катаемся на лифте
Эта задача решается при помощи динамического программирования. Состоянием динамики будет пара (i, j), где i — количество посещенных этажей, j — номер этажа, на котором мы сейчас находимся. Начальным состоянием является пара (0, a), конечными состояниями являются пары (k, v), где v — любой этаж от 1 до n.
Чтобы осуществить переход, нужно перебрать следующий посещаемый этаж и пересчитать ответ. Однако нельзя выполнять такой переход в явном виде, поскольку решение будет иметь асимптотику O(N3). Чтобы решение уложилось в требуемый лимит по времени, нужно воспользоваться частичными суммами. При переходе от i к i + 1 предпосчитаем в дополнительном массиве частичные суммы посчитанных ранее динамик (с предыдущей итерации). После этого воспользуемся динамическим программированием “назад” и при подсчете очередного значения будем делать запрос суммы на отрезке в посчитанном массиве частичных сумм. Таким образом, решение будет иметь асимптотику O(N2).
Авторское решение: 8322623
480D - Посылки
Сделаем два наблюдения.
Во-первых, посмотрим на посылки как на отрезки [$in_i, out_i$]. Верно следующее утверждение: если коробка i оказалась в какой-то момент времени сверху коробки j (не обязательно непосредственно сверху), то .
Во-вторых, представим, что на платформе находятся какие-то посылки. Оказывается, что все, что нам нужно знать про этот набор посылок, чтобы понимать, можем ли мы положить что-то сверху, это “остаточная прочность” всех этих посылок. Остаточная прочность определяется так: для каждой посылки из набора остаточная прочность равна прочности этой посылки за вычетом суммарного веса всех стоящих на ней. Для набора посылок остаточная прочность считается как минимум из остаточных прочностей посылок в наборе. Таким образом, новую посылку можно положить, если ее вес не превосходит остаточной прочности уже имеющихся коробок.
Это приводит нас к идее решения задачи динамическим программированием. Пусть в данный момент времени верхняя посылка на платформе имеет номер i, а остаточная прочность имеющихся на платформе коробок равна rs. Сделаем эту пару (i, rs) состоянием динамики: представим, что у нас есть исходная задача, в которой есть только те посылки, которые вкладываются в [ini, outi], а платформа имеет прочность rs. В значении динамики d(i, rs) будем хранить ответ для этой новой задачи. Какие существуют переходы? Мы должны выбрать набор посылок i(1), i(2), ... i(k) таких, что
- outi(j) ≤ ini(j + 1), т.е. отрезки не пересекаются (кроме концов) и отсортированы по времени;
- вес любой из выбранных посылок не превосходит rs.
Этот выбор соответствует следующей последовательности операций: сначала поставить сверху посылки i посылку i(1). Мы перейдем в состояние i(1), min(rs - wi(1), si(1)), прибавив к сумме стоимость коробки i(1) и ответ для нового состояния. Затем мы снимем все коробки, включая i(1), и поставим i(2), сделаем переход, затем снимем все вплоть до i(2), поставим i(3), и т.д.
Поскольку количество состояний в динамике у нас получилось O(NS), то переходы должны суммарно выполняться за O(N). Этого можно добиться, реализовав внутри несложную вспомогательную динамику. Должно получиться решение со сложностью O(N2S). Отметим, что для единообразия можно считать платформу коробкой номер 0, которая приходит раньше всех, и выходит позже всех. Тогда ответ на задачу — просто значение d(0, S).
480E - Парковка
Будем называть приезды машин на парковку событиями.
Рассмотрим следующее решение (оно поможет нам прийти к авторскому): давайте рассмотрим все возможные пустые квадраты в таблице. Их, конечно, много, но допустим, что мы все же можем их перебрать. Если мы зафиксировали квадрат, то давайте поймем, когда он перестанет быть пустым (найдем первое событие парковки машины в этом квадрате). Пусть номер этого события x, а размер квадрата k. Тогда для всех событий с номерами меньшими, чем x, попробуем обновить ответ значением k.
Авторское решение использует принцип разделяй и властвуй. Давайте напишем рекурсивную функцию, которая принимает подтаблицу, границами которой являются r1, r2, c1, c2 (r1 ≤ r2, c1 ≤ c2), а также список всех событий парковки, которые происходят в этой подтаблице. Задача функции — рассмотреть то, как меняются максимальные пустые квадраты в этой таблице по прошествии этих событий, и попытаться обновить ответы для некоторых событий.
Предположим, что c2 - c1 ≤ r2 - r1 (второй случай будет симметричным). Давайте возьмем среднюю строку подтаблицы: r = (r1 + r2) / 2. Разделим все квадраты в подтаблице на три части: те, что строго выше r, те, что строго ниже r, и те, что пересекают r. Для первых двух частей вызовемся рекурсивно (естественно, списки событий при этих вызовах будут свои). Теперь осталось только рассмотреть квадраты, пересекающие (имеющие общие клетки) со строкой r.
По исходной таблице для каждой клетки (r, c) мы можем посчитать расстояние до ближайшей занятой клетки в каждом из четырех направлений (или до края, если такой нет): up(r, c), down(r, c), left(r, c) и right(r, c). Используя эту информацию, мы сейчас построим для строки r две гистограммы: первая представляет из себя массив значений up(r, c) при c1 ≤ c ≤ c2, вторая — массив значений down(r, c) при c1 ≤ c ≤ c2. Здесь я называю массивы гистограммами, потому что они фактически означают высоты столбиков из пустых клеток, начинающихся в строке r. Назовем первую гистограмуу верхней, а вторую — нижней. Рассмотрим все события, происходящие в подтаблице, в порядке их появления. Каждое событие меняет лишь одно значение в гистограмме. Пусть после мы имеем гистограммы после события с номером x, а следующие событие имеет номер y. Тогда, если мы найдем максимальный пустой квадрат в этих гистограммах (пусть его размер k), то мы сможем обновить ответы для всех событий от x до y - 1 значением k.
Осталось только научиться искать максимальный квадрат в двух гистограммах. Это можно сделать с помощью метода двух указателей. Поставим первый указатель в начало. Будем двигать второй указатель до тех пор, пока в гистограммах есть такой квадрат: квадрат со стороной k имеется, если (минимум на отрезке в первой гистограмме) + (минимум на отрезке во второй гистограмме) — 1 >= k. После того, как это свойство нарушилось, двигаем первый указатель. Для того, чтобы находить минимум за O(1), авторское решение для каждой гистограммы заводит очередь с поддержкой минимума за O(1). Таким образом, поиск максимального квадрата осуществляется за линейное время.
Давайте попробуем оценить время работы. Количество действий внутри одного вызова (без учета рекурсивных вызовов в половинах) равно len·q, где len — длина наименьшей из сторон подтаблицы, а q — количество событий в ней. Если мы представим себе дерево рекурсии, то увидим, что через вызов (т.е. каждый второй вызов) величина len уменьшается в два раза. Суммарное количество действий на одном уровне дерева рекурсии равно O(NK), где k — общее количество событий. Поскольку уровней в дереве O(logN), то общая асимптотика O(NKlogN).
Solved E div. 1 a bit differently. I was iterating over events in the reversed order. So instead of adding cars I was removing them. With this approach max square size can only grow and obviously it grows only if new bigger square becomes available and it contains the point you have just cleared. Let's say that so far we have already found a square with the side
biggest_square_side
. Then to check if new square was cleared by removing car from pointP
I simply had a loop like that:there_is_square
method was working similarly to yours histogram approach, but it was not looking for the biggest square, just checking if some specific square exists, so my two pointers were a bit simpler because the offset between them was fixed.Yet another round I have a solution based on amortized time complexity analysis :-) Basically my solution has three loops like that:
This looks like two loops with O(NlogN) inside the innermost loop, but since in total we won't increment
biggest_square_side
more than N times it should give only O(N2logN) if I didn't miss something. Anyway the solution was accepted, though the time is ~2 seconds, I think it's because of me using a lot of smallset<pair>
objects. Submission: 8315473I had similar solution but working in O(NK).
Is it possible to find the largest rectangle between two histograms? Such that we just have to check the row and column of the removed car to find the new greatest rectangle?
problem "479B — Towers" what is the output for the input: 4 1 2 2 4 4
Input: 4 1 2 2 4 4 Output: 2 1 3 2
can it be : 2 1 3 1 ? beacause to convert 2 2 4 4 we can choose any 1 of two's and any 1 of four's . ?
Yes, any of the two answers would be accepted... ;-)
Thanks for quick editorial!
спасибо за разбор задач!
for problem B i am getting wrong answer on pretest 5 . any tricky case please ?
and what should b output for 15 5 5 8 4 7 9 6 3 4 5 2 1 4 8 9 7
Output: 5 5 5 11 14 11 14 10 5 10 2 11
I don't think there is any tricky cases for this problem... If you get the right algorithm, it's pretty much straightforward.
When you submit your code, it is possible to check what is wrong by clicking on the submission ID, it will show you all the cases it passes and the case it fails.
Div2A: Those are not all the possible solutions (there are ab + c and a + bc), but the good thing is that you can prove that these two cases don't happen.
In problem E,DIV 2,is it necessary that after transporting each time,we have to be on a new floor ?
Yeah. It is given in the problem statement: Let us suppose that at the moment you are on the floor number x (initially, you were on floor a). For another trip between floors you choose some floor with number y (y ≠ x)
Suppose i start from floor number=1,and i go to floor 3.Than from 3 we go to 5 ,So from 5 can we go back to 1 or not ?
As in the problem statement, we can only go from floor x to floor y iff |x - y| < |x - b|. Say that a = 1 and b = 6, then we can't do (1, 3, 5, 1), but if b is large enough, let's say b = 10, we can achieve it (since |5 - 1| < |5 - 10|).
UPD: Never mind, I found my mistake. Stupid dp[blah][-1]. Thanks all :)
Can anyone help me with this solution of mine 8331072? Somehow it returns a very large number (maybe a result of a bad modulo) on Codeforces, but runs well on my machine.
usually long long everywhere helps in such problems...
Hi. Does anybody have proof that the greedy algorithm for div2B is correct?
It very easy. If difference between maximal element and minimal element less or equal to one, you can not improve unstability. In other cases, can only improve unstability by this move, and it can only decrease.
Sorry, but that doesn't sound convincing enough. Especially the other cases.
gridnevvvit, anyway thank you for the answer.
Actually, I suppose that more exact proof may be something like this, in my opinion.
We will use the induction by the maximal number of operations k. Let's suppose that for some k we know that the algorithm in the editorial is optimal (it's not difficult to prove for example for k = 1). Now we should prove it for k + 1. Let res be the answer for k + 1 steps using the algorithm. We can show that any first switch which is different from switching between maximal and minimum will lead us to an answer more or equal than res. It can be done by induction hypothesis and some accurate analysis of the cases.
Time: 61 ms, memory: 20 KB Verdict: WRONG_ANSWER Input 19 180 117 148 0 1 19 20 21 28 57 65 68 70 78 88 100 116 154 157 173 179 180 Output 1 60 Answer 2 117 148 Checker comment wrong answer It's impossible to measure some value from wanted list
My solution failed on 12th test. but I think my answer is more correct. Isn't it? If you add 60 you can measure both 117 and 148. Your solution is not the best one. Please fix test, or explain me please the reason of failure:=)
If you add 60 as a new mark, is there any number x such that x - 60 = 117 or x - 60 = 148?. As far as I can see there is no such X.
But you can obtain this numbers with addition 57+60=117 and 48+60+20. = 148
Probably you've missed an important part in the statement:
"Valery believes that with a ruler he can measure the distance of d centimeters, if there is a pair of integers i and j (1 ≤ i ≤ j ≤ n), such that the distance between the i-th and the j-th mark is exactly equal to d (in other words, aj - ai = d)"
what the hell is wrong with Cf now a days. my NKlogN solution == tLE for Div1 E . need to redude into NK.! solution link : http://mirror.codeforces.com/contest/480/submission/17254807. anyone why?
Edit : can be done in NK using max sliding window. got now.
Can anyone explain me Div 2E . I am not able to understand it from the Editorial and comments as well
can anyone tell me my mistake on problem D? my submission
my idea is similar but i use set
sx
which contain mark to be inserted so that we can measure length x, andsy
which contain mark to be inserted so that we can measure length y. If some value insx
also exist insy
then its enough to insert 1 mark. Im getting WA 18 (minimum insert should be 1 but my program output 2)I have a different solution in the exercise A my algorithm has a higher result in the test cases where a = 6, b = 7, c = 1; the answer is 48 but my algorithm gets 49; (6 + 1) * 7;
You can't change the order of numbers
IN 479A when given inputs are 6,7,1;
then it shows '48' but instead it should be (6+1)*7==49.
I know it's a bit too late but it should be corrected.
Read the statement carefully before necroposting it's a div 2 A with 80k solves of course someone would've noticed if there was an error