164A - Переменная, или Туда и обратно
Кратко о решении — dfs, O(E).
Пустим поиск в глубину из всех вершин, где переменная присваивается. Пометим все посещенные.
Пустим поиск в глубину по обратным ребрам из всех вершин, где переменная используется. Запретим ему проходить по вершинам, где переменная присваивается. Пометим все посещенные.
Вершины помеченные оба раза — это и есть те вершины, где состояние Васи кого-то волнует
164B - Древние берляндские иероглифы
Кратко о решении — метод двух указателей, O(N).
Давайте для удобства предположим, что каждый символ в каждой строке встречается ровно один раз.
Тогда у нас есть перестановка pi (позиция во второй строке i-го символа первой строки)
Решение за квадрат теперь такое: фиксируем начало подстроки и идем по ней вперед, при этом параллельно идем по подпоследовательности и следим, что и там, и там мы не прошли больше круга.
Решение за линию.
Будем теперь идти двумя указателями "начало подстроки" и "конец подстроки". Параллельно будем хранить очередь — текущая парная подпоследовательность. В очереди все pi обязательно возрастают (этого не сложно достичь, т.к. можно прибавлять к pi число n столько раз, сколько нужно. То что подпоследовательность не зациклилась проверяется так queue.first - queue.last < n. Значит, мы можем двигать конец подстроки вперед, пока это неравенство выполнено. Как только конец подстроки нельзя увеличить, увеличиваем начало. Заметим, что начало и конец подстроки также двигаются по кругу.
Альтернативное решение:
После того, как мы увидели перестановку p, можно воспользоваться методом двоичных подъемов и получить решение за O(NlogN), которое тоже получало Accepted.
164C - Автоматное программирование
Кратко о решении — MinCost flow, O(N2 K) или O(NlogN K).
Самое главное было — увидеть граф, где вершины = задания, множества заданий, выполняемые автоматами = вершиннонепересекающиеся пути, а ребра можно было строить двумя способами (см. ниже).
Как сделать пути вершиннонепересекающимися? Раздвоить вершины.
Как добавить стоимость? Между двумя половинками вершины должна быть стоимость - ci.
Первый способ, граф G1 : вершины A, B соединены ребром, если после задания A можно успеть выполнить задание B тем же автоматом. Тогда ребер O(N2).
Второй способ, граф G2 : все вершины упорядочены по si из вершины i ведут два ребра — в i + 1 (не берем i задание, пробуем взять следующее) и в вершину с минимальным j : sj ≥ si + ti (берем задание, переходим к минимальному из возможных следующих). Тогда ребер O(N).
Если вы пользовались первым способом и для поиска пути реализовали одну из вариаций алгоритма Форда-Беллмана, то вы могли получить TL. Собственно пока из-за того, что на java все медленно, TL не подняли, все Форд-Беллманы кроме одного особенного получали заслуженный TL.
Чтобы в G1 использовать Дейкстру с потенциалами, удобнее всего было заметить, что граф ацикличен и первый раз расстояния посчитать за O(E).
Кратко о решении — бинпоиск по ответу, а внутри перебор за O(Fib(K)).
Правильное решение состояло из четырех несложных идей:
1) Нужно из N2 ребер покрыть удаленными вершинами сколько то максимальных. Когда мы покрываем еще не покрытое ребро, у нас 2 варианта это сделать — выбрать 1 конец, выбрать второй конец. Эта идея сама по себе давала асимптотику O(2K * N).
2) Если сделать бинпоиск по ответу, то мы решаем более простую задачу: можно ли покрыть первые X ребер K вершинами? Т.е. правда ли, что размер контролирующего множества в таком графе не более K.
3) Если мы теперь внутри бинпоиска не берем какую-то вершину, то мы должны брать всех ее соседей в уменьшенном графе. Из этого можно сделать 2 вывода: во-первых, если есть вершина степени больше чем K, ее брать обязательно, во-вторых, не выгодно брать вершину степени 1.
4) Теперь решение работает за log * Fib(K) * K, где log — константа от бинпоиска, Fib(K) — K-е число Фибоначчи, K --- максимальная степень вершины в уменьшенном графе.
Почему Fib(K)? Теперь, когда мы делаем выбор, в одной ветке рекурсии K уменьшается на 1, а в другой хотя бы на 2... На лицо числа Фибоначчи.
P.S. Из похожих задач я знаю вот эту: задача 2 с РОИ 2009-2010
Кратко о решении — жадность + куча запросов на отрезке, асимптотика = O(NlogN).
В этой задаче так и не получилось написать очевидное условие...
Расскажу некоторую легенду, чтобы дать понять, что задачка не искусственна, взята из "реальной жизни".
Все вы читали задачу C. Давайте чуть поменяем ее, скажем, что автомат у нас один, а работа должна начаться не раньше li, закончиться на позже ri, а времени на нее уйдет ti. Рассмотрим случай, когда li и ri возрастают.
Попробуем придумать жадное решение: отсортируем все работы по li и будем пытаться "брать". Если можно взять, то выполнять работу будем в минимально возможный момент времени.
Это жадное решение не сложно завалить тестом (L=1, R=10, T=10), (L=2,R=11,T=5), (L=3,R=12,T=5).
Поэтому давайте жадность улучшим, попробуем, если добавить в конец не получается менять на кого-нибудь так, чтобы правый конец (последний использованный момент времени) максимально уменьшился.
Собственно задача E состояла в моделировании процесса. В том, чтобы запрограммировать эту жадность.
Итак, решение:
Для каждого отрезка определим величину shifti = si — li (насколько можно отрезок подвинуть влево).
У нас уже есть k отрезков. Добавляем k + 1-й, не получается. Пробуем сделать замену. Перебираем все отрезки в порядке уменьшения индекса, смотрим, насколько уменьшится правый конец.
Это min(ti, min по j=i+1..k (shiftj)).
Т.е. ни один из отрезков из 1..i уже не даст ответ лучше, чем min(max по j=i+1..k (tj), min по j=i+1..k (shiftj)).
Зачем я выдумал этот максимум? Дело в том, что под минимумом теперь две функции, убывающая и возрастающая. Значит, максимум можно искать бинарным или троичным поиском.
Следующая идея заключается в том, что когда мы нашли оптимальную замену besti, то чтобы пересчитать все shiftj, достаточно сделать вычитание на отрезке besti+1..k
Полученное решение имеет асимптотику O(Nlog2N). Его можно улучшить до O(NlogN), заменив бинпоиск, а внутри него обращение к дереву поиска или дереву отрезков на один спуск по дереву.
UPD:
По просьбам участников выкладываю "разбор" задач A,B,C второго дивизиона.
Задачи второго дивизиона я не готовил, не читал, не решал. Но вроде бы, если почитать, они решаются так, как описано ниже. Так что разбор второго дивизиона не стоит воспринимать как авторский, это просто рассказ "как бы я решал эти задачки". Надеюсь, нигде не наврал.
X = (a1 + a2 + ... + an + b) / n.
Если X меньше максимального ai, то нельзя сделать так, как просят. Иначе нужно вывести числа X — ai.
Решение #1, жадное:
Если между двумя точками больше 11 или меньше 2 символов, то нельзя так разрезать.
Если перед первой точкой больше 8 символов, то нельзя так разрезать.
Если между после последней точки больше 3 символов, то нельзя так разрезать.
А иначе режем жадно :-) Если количество символов между двумя точками равно X, то разрез проводится в позиции min(8,X-1).
Решение #2, DP:
is[i] — можем ли мы разрезать префикс длины i.
Изначально is[0] = 1, остальные нули.
Теперь перебираем i в порядке возрастания и если is[i] = 1, пытаемся сделать переход в i + 1..i + 12.
Основная идея — жадность.
Если минимум на всем массиве равен нулю, то задача естественным образом распадается на подзадачи, а иначе нужно уменьшить весь отрезок на 1.
Решение #1, ищем минимум
Ищем минимум в массиве, вычитаем 1 из всего массива min раз, задача распадается на несколько (т.е. в каждый момент времени, задача = отрезок исходного массива). Минимум на отрезке нужно искать быстро, за O(logN), например.
Решение #2, простое
Будем идти слева направо и хранить, сколько у нас отрезков уже начато торчит налево, пусть это X. Если X < ai, то нужно начать несколько новых отрезков, если же X > ai, то несколько уже начатых нужно закончить в позиции i - 1. Начатые отрезки можно хранить в стэке. Хранить, конечно, нужно только позицию начала. Новый X будет равен ai.
Конец.
Разбор первой правильный?
Я сначала то же самое написал, WA5. ИМХО, первому dfs не надо запрещать ничего, так как в условии ничего не сказано про то, что в хорошем пути не должно быть других вершин, в которых используют значение (кроме последней).
Спасибо за быстрое замечание. В разбор вкралась ошибка. Fixed.
Хотелось бы разбор для див2
Кажется, у меня есть возможность вас порадовать :-) Некоторый разбор только что появился.
Хотелось бы увидеть разбор трех задач с контеста второго дивизиона. Сделайте, пожалуйста, если не сложно. Ибо интересно авторское решение B, которая мне не далась из-за моей несобранности и написания бредо-кода, а также решение C за O(n), как кто-то кричал в комментариях, потому что оно мне не очевидно, ведь скорее всего именно оно является авторской задумкой, а не как у меня за O(nlogn) с деревом отрезков.
Посмотрите на это великолепное решение: Solution
После это шаблона, я на миг подумал что это троллинг.
Хотелось бы получить некоторое пояснение по графу G1 в задаче C. Я правильно понимаю, что для того, чтобы добавить в граф стоимости, нужно раздвоить вершины (каждой задаче соответствует две вершины, между ними ребро пропускной способности 1 и стоимости -c[i])? В разборе про это ни слова. Или можно сделать как-то еще?
И у меня в таком графе (2000 вершин) зашел Форд-Беллман на первой итерации, правда, почти на пределе. Спасибо джаверам :)
Да, правильно. Добавил в разбор две строки...
Как сделать пути вершиннонепересекающимися? Раздвоить вершины.
Как добавить стоимость? Между двумя половинками вершины должна быть стоимость $-c_i$.
По поводу C: я писал немного другой поток, с совсем простым графом: сожмём время, пусть все моменты времени — t1, ..., tk. Они будут вершинами, граф — это цепочка t1 - ... - tk (все рёбра capacity=k и cost=0) + для каждого задания [l, r] стоимости c ребро из r в l стоимости - c (и capacity 1). И во всём этом деле надо найти циркуляцию минимальной стоимости. Правда, на контесте у меня оно не зашло, т.к. я разучился писать циркуляцию, а искать prewritten code — неспортивно. Надо будет додебажить и проверить, влезает ли оно в ТЛ...
У меня не влезло даже когда я уменьшил количество итераций Форда-Беллмана до min(400, M), где M — кол-во вершин в графе.
Ну так кто ж итерациями ФБ делает. По-хорошему, надо вообще писать ФБ-Тарьяна, он мало того что быстрый, так ещё и нахаляву ищет отрицательные циклы.
Да я минкост циркуляцию раньше никогда не писал. На контесте глянул как это сделано на емаксе, написал похожее, но без векторов и сортировок. Все равно медленным оказалось.
ФБ с оптимизацией Тарьяна прошел, причем с запасом почти в 2 раза. Крутой алгоритм :)
P.S.: а простой ФБ без итераций, но и без оптимизации Тарьяна не проходил ни в какую.
Что за оптимизация Тарьяна?
Ровно такое решение у меня прошло с максимальным временем 200 мс (решение).
Так это же решение, которое все писали. Не вижу я там поиска минкост циркуляции, да и ребра идут от левого конца к правому, а не наоборот.
На КФ какие-то уж очень неадекватные люди... За что минусуют этот разбор?
UPD. Когда я это писал, было -3... Как-то это не правильно.
Ну... возможно получилось непонятно =(
Задача A.
Если X больше максимального ai, то нельзя сделать так, как просят. Иначе нужно вывести числа X — ai.
разве неЕсли X меньше максимального ai, то нельзя сделать так, как просят.
Fixed. Спасибо)
Задача С для див. 2. Помогите, пожалуйста, найти ошибку: 1501971
UPD: Всё, дошло. Я слоу(
Как можна найти минимум в массиве за O(logN)?
http://e-maxx.ru/algo/segment_tree#9
Thanks for writing up this. Can we expect an English version?
With Google translate, 164D's method is showed as "binpoisk to answer", what does "binpoisk" mean? Is it same as binary search?
Yes, it's an abbreviation of the two words.
Yes. Binary Search = Binarniy Poisk = Binpoisk.
Thanks to both of you.
Только сейчас понял, что в названии А подсказка к решению. :D Кто-нибудь ею воспользовался на контесте?
Я заметил то, как она подходит под решение, уже после того, как наконец-то понял само решение:)
Это прибавило уверенности в решении, но не более.
Ну так можно гадать что угодно, шанс что в названии будет содержаться подсказка довольно мал. Эта задача лишь исключение.
P.S. Ну да, на задачу 1000 с тимуса данное правило не распространяется :D
Вот еще один пример. Обратите внимание на автора задачи :)
Хотел бы задать вопрос по поводу разбора задачи В из второго дивизиона. В разборе написано: "Если количество символов между двумя точками равно X, то разрез проводится в позиции min(8,X-1)". По-моему, будет логически правильно, если написать "Если количество символов между двумя точками равно X, то разрез проводится в позиции min(**3**,X-1)".
Честно сказать, могу ошибаться, возможно не понял, что автор разбора имел ввиду. Но решение с этой маленькой поправочкой дает Accepted.
Could anyone tell me a detailed explanation about dp approach for problem B. I'am learning DP.
My solution for problem B using DP approach!!!
For Problem C, there are some good comments in announcement section as well. start=si finish=si+ti so times which are important are starts and finishes only. First coordinate compress all the times (starts and finishes) now let we have times like t1 t2 t3... t2n (at max we can have 2*n times) Let there be x distinct time. now connect vertices ti to ti+1 for all 1<=i<=x-1 with capacity k, cost 0 connect vertices ta to tb if we have a task that started at ta and ends at tb-1 (Read question carefully, a task starts at si and finishes at si+ti-1, and at si+ti machine is free). Capacity=1, Cost= -Cost of edge.
Now it becomes mincost maxflow problem with max flow upto k. If u didnt get it then just above graph once for sample input. u will surely understand.
I did reverse version (maximum to minimum) way in C-Range Increments using dsu