Блог пользователя Alex_KPR

Автор Alex_KPR, 14 лет назад, По-русски
Задача A. Сон Студента.

В русской версии условия фразу "между любыми двумя пальцами девочки есть палец мальчика" нужно было читать как "между любыми двумя соседними пальцами девочки есть палец мальчика".

Если мальчик пойдёт слева от девочки, то он возьмёт своей правой рукой её левую руку, а оставшиеся руки (левая мальчика и правая девочки) окажутся незадействованными. Если наоборот, мальчик пойдёт справа, то он возьмёт своей левой рукой правую руку девочки. Это написано в условии. Осталось проверить, "совместимы" ли руки для прогулки хотя бы в одном из этих случаев.

Проверить это очень просто: пусть у мальчика B, а у девочки G пальцев, тогда:
  • Если B < G, то им будет удобно, только если B = G - 1; это паттерн GBGBGBG...GBGBG. В этом паттерне нельзя добавить ни один палец девочки так, чтобы не нарушить требования условия.
  • Если B > G, то наихудшая ситуация - это паттерн BBGBBGBB...BBGBB. Отсюда заметим, что должно выполняться 2G + 2 >  = B для возможности прогулки вдвоём.
  • Чтобы не затуманивать логику первых двух случаев, третьим случаем вынесем B = G, но тут всё тривиально: очевидно, что взяться за руки они могут.

Задача B. Тындекс.Бром.

Обозначим за p суммарную длину всех потенциальных адресов c. В этой задаче ограничения были выбраны таким образом, чтобы очевидное решение с асимптотикой O(p· k) не проходило. Ускорять будем следующим образом: для каждой буквы из введённой пользователем строки s сохраним отдельно все позиции букв 'a', 'b', ..., 'z' в отсортированном порядке. Обладая этой информацией, можно искать для текущего символа потенциального адреса ci ближайший из s за логарифмическое время бинарным поиском. Таким образом, получается асимптотика O(p · logk), которая прекрасно проходит по времени. Важно было обратить внимание на ограничения и учесть, что длина потенциального адреса может быть больше, чем 105, а ответ не всегда помещается в 32-битный тип.


Задача C. Инквизиция.

Заметим, что, согласно ограничениям, все треугольники лежат строго внутри квадрата. Это упрощает задачу - не нужно рассматривать дополнительно случаи касания границ шкуры.

Сохраним все отрезки каждого треугольника и все попарно пересечём. После этой операции на каждом отрезке может остаться много точек - результаты пересечения с другими отрезками. Рассмотрим каждый подотрезок этого отрезка такой, который не содержит внутри себя других точек. Этот подотрезок либо целиком лежит на границе пятна, либо целиком лежит внутри пятна.

Чтобы проверить этот факт, можно взять середину подотрезка и проверить её на принадлежность исходным треугольникам. Если эта точка - середина подотрезка - лежит строго внутри некоторого пятна, то и весь отрезок будет лежать внутри, и прибавлять его длину к ответу не нужно. В противном случае, добавим эту длину к ответу и, после обработки всех отрезков таким образом, выведем накопившийся ответ.

Если вы работаете с целыми числами, то учтите, что с заданными ограничениями до 105 вычисление, например, векторного произведения, может превысить 32-битный тип.

Если вы работаете с вещественными числами, то используйте максимально большой тип данных (long double в c++, extended в паскале).


Задача D. Дом для червя.

В этой задаче требуется найти следующий лексикографически эйлеров цикл, заданный из некоторой точки - входа - или сказать, что такого пути не существует. Будем решать эту задачу с конца.

Повторим маршрут Арни. Теперь будем откатываться назад по коридорам, которые он прогрыз, и добавлять рёбра к пустому графу (графу без рёбер). Пусть мы откатили на некотором шаге ребро, которое шло из комнаты x в комнату y. Проверим, существует ли вершина z такая, что есть ребро из x в z и номер вершины z больше номера вершины y. Кроме того, нужно проверить, что ребро (x; z) не является мостом - иначе мы разобьём компоненту связности на несколько и не сможем прогрызть все оставшиеся коридоры.

No solution получается тогда, когда ни на каком шаге нельзя найти требуемую вершину z.

Но когда такое ребро (x; z) оказалось найдено, то мы пройдём по нему и после этого будем искать лексикографически наименьший эйлеров путь. Для этого из каждой текущей вершины будем пытаться идти в вершину с минимально возможным номером. Пойти в вершину с минимально возможным номером можно только тогда, когда ребро, соединяющее эти вершины, не является мостом. Разумеется, нужно выбрасывать недостижимые вершины из графа и не считать их за отдельные компоненты, и не нужно выбрасывать начальную вершину, из которой Арни начал свой путь, иначе есть риск в неё уже не вернуться.

Проверку на то, является ли ребро мостом, можно делать поиском в глубину за O(E). Соответственно, суммарная сложность алгоритма O(N· E2).


Задача E. Вселенское Зло

Автор этой задачи - Василий Вадимов, ННГУ. Выкладываю его собственный разбор этой задачи и благодарю за помощь в проведении раунда.

В этой задаче требовалось найти максимальный поток в сети со структурой в виде цилиндра. Предполагалось динамическое решение за O(mn2n). Известно, что максимальный поток в сети равен величине минимального разреза. Причем все истоки должны принадлежать одной доле разреза, а все стоки --- другой. Тогда будем перебирать вершины слева направо, сверху вниз и добавлять в ту или иную долю разреза, по ходу пересчитывая ответ. Тривиальное решение имеет асимптотику O(2mn) и, очевидно, не укладывается по времени.

Но можно заметить, что то число, которое мы добавим ответу при добавлении новой вершины в какую-либо долю разреза, зависит только от того, к каким долям принадлежат вершины в предыдущем столбце и в том, которому принадлежит добавляемая вершина. Поэтому можно считать такую динамику: какой минимальный разрез можно получить, добавив i столбцов и в последнем столбце маска принадлежности вершин к долям разреза mask. Тогда имеется всего 2n переходов, т.к. различных состояний i - 1-го столбца 2n. Так решение оптимизируется до O(mn22n).

Но и это решение можно улучшить, если динамику считать по изломанному профилю. Будем искать минимальный разрез, если мы добавили i - 1 столбцов полностью и добавили j вершин из i-го столбца и маска принадлежности вершин, у которых соседи справа еще не добавлены, mask. Тогда добавляем j-ю вершину, определяем в какую долю ее поместим, считаем, насколько увеличится величина разреза и делаем переход. Итого mn2n состояний, O(1) переходов из каждого состояния и сложность алгоритма O(mn2n)

Поздравляем участников rng_58 и Ra16bit, решивших эту задачу во время контеста!


Выражаю благодарность ребятам из ННГУ (Владиславу Епифанову, Алексею Шмелёву), которые писали альтернативные решения и задавали вопросы по условиям. Спасибо вам!
Разбор задач Codeforces Beta Round 58
  • Проголосовать: нравится
  • +14
  • Проголосовать: не нравится

14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
А у меня в задаче B 32-битный тип стоял и прошло.Там теста на это не было?
  • 14 лет назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится
    например, в тесте #18 ответ 9'967'201'484, что никак не лезет в 32-битный тип
  • 14 лет назад, # ^ |
    Rev. 4   Проголосовать: нравится +1 Проголосовать: не нравится
    вообще слишком много комментариев я видел из серии "слабый тестсет по B", хотя я, как автор, утверждаю, что тестсет содержал все граничные случаи


    возможно, возникли какие-нибудь проблемы с тестированием? я обязательно поинтересуюсь у администрации CF
    у тебя sch типа int64 ;)

    хотя возможно, что у тебя ответ типа int64, а все вычисления делались в int32 - тогда это нормально, что прошло
14 лет назад, # |
  Проголосовать: нравится +1 Проголосовать: не нравится
Когда примерно появятся результаты?
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Есть ли возможность увидеть авторские решения задач? Особенно интересует решение C на Java без "подгона точности"
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    у нас никто не пишет на java, поэтому решения жюри на этом языке нет

    насколько я знаю, в java есть тип BigDecimal, который может считать с точностью, более чем достаточной для решения этой задачи

    поскольку авторское решение имеет асимптотику O(n3), то, я думаю, что использование этого типа на асимптотически таком же решении не приведёт к вердикту TLE
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Авторы почти всегда (когда не надо работать с радикалами итп) могут написать решения в длинной рациональной арифметике, т.е. абсолютно точно :)
    Например, в этой задаче такое возможно.
  • 14 лет назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится
    Я писал решение на С++, но я нигде не использовал eps. Проверку на то, что отрезки пересекаются (случаи касания в одной точке за пересечение не считаются), я делал в целых числах. В вещественных (обычных double, не long) считал точки пересечения и проверял принадлежность треугольникам (ну потому что целочисленная проверка гарантирует пересечение - на ноль не поделится в слу, если считать Крамером, а середины отрезков будут отстоять на достаточное расстояние от сторон треугольников (к тому же нам надо проверить на "строго внутри"), поэтому eps не нужен). Такое решение зашло.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
> В этой задаче ограничения были выбраны таким образом, чтобы очевидное решение с асимптотикой O(p· k) не проходило.
Вообще-то плохо они были подобраны... Среди решений можно найти успешные решения без бинарного поиска, а с простым перебором от меньшего к большему - и они отрабатывали за 1,5 с. Причем даже на Java, например №308057.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
> В этой задаче ограничения были выбраны таким образом, чтобы очевидное решение с асимптотикой O(p· k) не проходило.
Вообще-то плохо они были подобраны... Среди решений можно найти успешные решения без бинарного поиска, а с простым перебором от меньшего к большему - и они отрабатывали за 1,5 с. Причем даже на Java, например №308057.
14 лет назад, # |
  Проголосовать: нравится +16 Проголосовать: не нравится
В задаче B есть довольно простое решение за O(L*|A|+p), где |A| - размер алфавита, а L - максимальная из длин строк во входном файле.

Можно было для оригинальной строки запомнить для каждой позиции и каждой буквы ближайшую к данной позиции встречу этой буквы. Затем, чтобы ответить на запрос нам достаточно просто пройти по строке-кандидату и для каждой позиции прибавить либо расстояние до ближайшей такой буквы в оригинальной строке, либо же длину потенциальной строки, если такой буквы нет.
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    На некоторых языках с таким подходом есть вероятность упереться в memory limit, тем более память тут считается очень и очень коряво. Потому есть ли смысл рисковать, если даже решение с ассимтотикой O(p*k) проходит, не говоря уже про O(p*log(k))? Наверно смысл максимум только для Си и Паскаля имеется.
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      интересно, сколько раз ты ещё напишешь, что у тебя прошло решение за O(p· k)?

      я бы, возможно, тоже поязвил на твоём месте, если бы моё самолюбие было задето, но это печально - рекламировать плохую асимптотику, ведь большая часть участников CF - участники с небогатым опытом и могут принять твои байки "пишите за квадрат, он проходит!" за чистую монету и слить следующий (и, возможно, далеко не один) контест

      тесты физически не могут проверить все возможные случаи

      да и, в конце концов, их тоже люди делают! моё квадратное решение получало заслуженный TL на двойном таймлимите в 4 секунды и это было достаточным основанием утверждать, что квадрат не проходит, поэтому закрывай эту тему, я не сторонник плясок с бубном
      • 14 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Да я больше о том, что я сколько раз получал memory limit exceed на Java, хотя аналогичное решение на C++ прокатывало... Просто память считают как максимум памяти, который занимал процесс java, и совсем не считаются, что это язык со сборщиком мусора. Потому на этом языке стоит десять раз подумать, перед тем как писать более быстрое решение, но требующее больше памяти. Вот и всё, что хотел сказать. :)
14 лет назад, # |
Rev. 3   Проголосовать: нравится +3 Проголосовать: не нравится
В разборе задачи "A" может стоит поменять условие "= G + 1" на "G - 1"?
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
I saw a lot of O(p.k) solutions pass in time after the contest end.I assume that they are tested against the same system tests used during the contest.

Maybe few cases like
1 1
a
aa......a
where there are around 2*MAXN a's in the second line.(Actually this was a case that i were hacked with during the contest,but it failed due to a runtime error and not TLE).

Another tricky case where people tricked their greedy to work incase we already dont have minimum |j-i| = 0, could be
1 1
a....ab.....b
b....ba.....a
where the a's and b's = MAXN/2

MAXN = 99999 everywhere.

O(p*k) solutions could use the above tests to understand why according to the problem setter were expected to TLE and might have got lucky on the judge.
Hope this helps.Appreciate any comments.
14 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится
Problem B
If you make a queue to find When you find closest position,it will be O(P) :D

My Solution cost least time.
14 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится
"номер вершины z больше номера вершины y" из разбора D
не совсем понятно, что значит номер, и для чего нужно это сравнение?
Просьба к Alex_KPR пояснить

все, кажется понял... вопросов нет, разбор достаточно подробен