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

Автор Endagorion, 12 лет назад, По-русски

139A - Петя и книга

Вычтем из количества страниц книги количество страниц, которые Петя успеет прочесть в понедельник. Если результат неположительный, то в понедельник Петя закончит читать. Иначе перейдем ко вторнику и т.д. по циклу, после воскресенье опять рассматриваем понедельник. Заканчиваем, как только количество страниц должно стать неположительным.
Всего нужно пройтись по циклу не более N раз, поскольку на каждой неделе отнимается как минимум одна страница. Сложность - O(n)

139B - Ремонт

Что подразумевалось в условии:

Каждый рулон можно резать только на полоски, длина которых совпадает с высотой комнаты (потому что нельзя делать горизонтальные стыки, и расположение должно быть строго вертикальным). В каждой комнате можно клеить только один тип обоев (в разных комнатах клеить одинаковые типы можно). Для каждой комнаты надо всего лишь определить оптимальный по цене тип обоев.

Ясно, что количество рулонов данного типа, нужных для обклейки выбранной комнаты, есть периметр стен комнаты, деленный на суммарную ширину полосок, получаемых из одного рулона, с округлением вверх. Количество полосок есть длина рулона, деленная на высоту комнаты, с округлением вниз.

Для каждой комнаты перебираем типы обоев и выбираем минимальный по суммарной цене. Сложность - O(mn)

139C - Урок литературы

138A - Урок литературы

Техническая задача. Основная сложность - проверить, рифмуются две строчки или нет.

По условию, чтобы это проверить, надо было отрезать суффиксы, которые начинаются в k-ых с конца гласных и сравнить их. Если в строчке меньше, чем k гласных она не может участвовать в рифмах в принципе (даже с такой же строчкой).

Здесь можно было использовать два указателя, либо пользоваться функцией взятия подстроки (в C++ s.substr(...)). Кроме того, надо было аккуратно разобрать случаи, когда k-ая с конца гласная - первая в строке, когда гласных меньше k, и проч.

Теперь будем поддерживать три логические переменные: aabb, abab и abba, в которых храним, верно ли, что все встреченные нами четверостишия можно отнести к соответствующему типу. Для каждого нового четверостишия их легко обновить - надо сравнить нужные пары строк на рифмуемость.

Если в конце все три переменные - TRUE, то ответ - aaaa. Если все - FALSE, ответ - NO. Иначе выставлена только одна из них, она и является ответом.

Сложность - O(S), где S - сумма длин строк.

139D - Перестановки цифр

138B - Перестановки цифр

Эта задача, кажется, многих сбила с толку в самом начале контеста, хотя мне она казалась простой.

На сколько нулей заканчивается сумма двух произвольных чисел? Для того, чтобы это посчитать, надо сначала пропустить все разряды с конца, где у обоих чисел стоят нули, для следующего проверить, дают ли цифры в нем сумму 10, а потом идти дальше, пока сумма цифр равна 9 (это все просто из сложения столбиком).

Пускай мы фиксировали число общих нулей в конце для двух перестановок. Переберем варианты цифр, дающие в сумме 10. При выборе этих цифр для определения количества нулей достаточно знать, сколько каких цифр осталось свободными в каждом числе. Если в одном числе осталось a0, ..., a9 цифр, а в другом b0, ..., b9, то количество нулей, которые дают оставшиеся цифры равно min(a0, b9) + min(a1, b8) + ... + min(a9, b0). Если изначально сохранить количество вхождений каждой цифры в число, посчитать ответ становится просто.

Теперь надо перебрать количество общих нулей и цифры, дающие в сумме 10, и для каждого варианта посчитать указанную выше функцию (при этом надо не забыть вычесть уже использованные цифры), а после этого правильно восстановить ответ для максимального значения. Сложность - O(10 * 10 * N) = O(N).

Основная подлость, на которую можно было напороться - решить, что надо всегда ставить все нули в конец обоих чисел, а потом подбирать остальные цифры. Это опровергает 4 претест - 1099. Легко проверить, что все варианты с нулями в конце проигрывают 1909 + 1091.

Были и другие баги, например, не учитывать вариант, когда общие нули есть, но следующие цифры не дают в сумме 10.

139E - Грибные гномы - 2

138C - Грибные гномы - 2

Первое, что здесь надо понять - ответ является суммой вероятностей для всех грибов, что каждый из них по отдельности не будет раздавлен (свойство матожидания - линейность, даже при зависимых величинах). Указанная вероятность для каждого гриба равна произведению для всех деревьев, которые могут накрыть этот гриб, что они НЕ упадут в сторону гриба.

Таким образом, мы можем рассмотреть семейство отрезков, соответствующих вариантам того, как могут падать деревья, с соответствующими вероятностями. После этого для каждого гриба перемножаем вероятности отрезков, которые его покрывают, и суммируем произведения.

Способов посчитать это быстро много:

1) Сканирующая прямая. Будем идти слева направо и рассматривать события "отрезок начался", "отрезок кончился", "нашелся гриб" и поддерживать произведение вероятностей отрезков, покрывающих текущую точку. Находим гриб - прибавляем к ответу текущую вероятность.

Чтобы это реализовать, надо сохранить все события в массив (с указанием типа), а потом отсортировать по координате.

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

Это можно побороть в рамках решения:

а) Воспользоваться тем, что различных значений вероятности всего 101, и хранить массив, сколько отрезков с каждой вероятностью встретилось. При обработке гриба проходится по массиву и за ~100 операций вычислять ответ. Описанной проблемы не будет, потому что ошибка не накапливается.

б) Хранить, например, set из встреченных отрезков с вероятностями. При обработке гриба надо пройтись по сету, что делается за линейное время. При этом если перемножении элементов сета число стало очень маленьким (< 1e-10), можно дальше не перемножать и считать ответ нулем. Эта оптимизация отсекает "глубокие" перемножения, поскольку максимальное число, которое надо хранить в сете - 0.99.

в) Прологарифмируем все вероятности и будем складывать логарифмы вместо перемножения, когда надо вычислить вероятность, возводим e в степень суммы. Это, очевидно, решает проблему с точностью.

2) Дерево интервалов.

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

Все вышеуказанные методы работают за O((m + n)log(m + n)), что удовлетворяет ограничениям.

По поводу странных баллов и претеста - проблема с точностью была обнаружена довольно поздно, поэтому борьба с точностью стала дополнительным этапом задачи, и сложность была повышена. Заметить это во время контеста предполагалось крайне сложным, поэтому про претест было написано в условии.

138D - World of Darkraft

Заметим, что для "четных" и "нечетных" клеток (т.е. клеток с четной и нечетной суммой координат) игра происходит независимо: игрок делает ход в одной из них на выбор. Это позволяет применить теорию Шпрага-Гранди для определения выигрышности всей игры.

Далее будем рассматривать, например, только четные клетки. Рассмотрим границу произвольного связного куска после некоторого количества ходов. В нее входят границы поля и диагонали, оставшиеся от предыдуших ходов. Ясно, что каждый кусок выпуклый, поэтому кусок задается четырьмя диагоналями, которые его ограничивают с четырех сторон (для любого куска и любой стороны всегда можно подобрать такой номер, что диагональ с этим номером хотя бы касается куска). Для подсчета функции Гранди этого куска, надо перебрать все клетки нужной четности, которые в нем лежат, и воспользоваться функциями Гранди для получающихся кусков. Так как получающиеся куски меньше исходного, функцию для всевозможных кусков можно посчитать динамических программированием.

После этого надо сравнить функции, соответствующие множеству всех четных клеток и множеству всех нечетных. Если они равны, то ответ - "LOSE", если нет - "WIN" (по свойствам функции Гранди).

Удобнее всего организовать вычисления, заведя четырехмерный массив, где каждое измерение - номер диагонали, ограничивающей кусок с очередной стороны. Перебирать клетки внутри куска теперь удобно по диагоналям, на которых лежит клетка. По номерам диагоналей вычисляются координаты клетки, после чего надо проверить, что она лежит внутри поля и рассмотреть соответствующий ход.

Сложность - O((n + m)4 (количество кусков) mn (количество ходов внутри куска).

138E - Адские ограничения

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

Припишем к строке справа новый символ (при этом к массиву добавится еще один элемент). Предположим, что он не задействован ни в одном из ограничений. Тогда легко видеть, что старая часть массива не изменится.

Пусть новый символ участвует в ограничении "c l r". Количество символов c в каждом суффиксе увеличилось на 1, поэтому можно понять, для каких позиций количество ограничений изменилось:

с[. . -1 . . .с]. . . . . с[ . +1. .c] . . c

r + 2. . . r + 1 . . . l + 1. . . . l. . . .1 - номер вхождения c, считая с конца

(Квадратными скобками обозначен участок строки, на котором произошло изменение.)

Алгоритм таков: будем приписывать к строке по одному символу и поддерживать количество чисел в массиве ограничений, удовлеторяющих L ≤ x ≤ R. При добавлении символа новый элемент массива можно посчитать за O(k). Для каждого из ограничений, содержащих новый символ, найдем отрезок, на котором будет происходить изменения (на этом этапе имеет смысл разделить верхнее и нижнее ограничение на два с весами +1 и -1). Пройдемся по отрезку и изменим значения массива, поддерживая значение счетчика чисел, удовлевторяющих ограничению L ≤ x ≤ R. Теперь значение счетчика - количество подстрок, заканчивающихся в текущем символе и удовлетворяющих ограничениям задачи. Будем прибавлять этот счетчик к ответу после обработки каждого символа. Ясно, что ответ правильный.

Рассмотрим одно ограничение. Видно, что при добавлении каждого нового символа полуинтервал, в котором изменяется состояние этого ограничения, смещается вправо и не пересекается с предыдущим. Поэтому суммарное количество изменений, которые придется проделать для данного ограничения, не превышает n, а суммарное количество изменений по всем ограничениям - nk.

Осталось научиться быстро находить концы соответствующего отрезка изменения. Это легко сделать, если пронумеровать все вхождения каждого символа в строку, или если поддерживать конец предыдущего отрезка изменения для каждого ограничения.

Суммарная сложность - O(nk).

Разбор задач Codeforces Beta Round 99 (Div. 1)
Разбор задач Codeforces Beta Round 99 (Div. 2)
  • Проголосовать: нравится
  • +68
  • Проголосовать: не нравится

»
12 лет назад, # |
  Проголосовать: нравится -7 Проголосовать: не нравится
а в задаче B если длина рулона какого-либо типа обоев меньше высоты стены, то эти обои можно было не рассматривать?
  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +2 Проголосовать: не нравится
    Да, их нельзя поклеить без горизонтальных стыков.
    Наверное, надо было как-то получше это в условии сказать.
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Об этом было очень непросто догадаться. Строчка из условия " При этом рулоны можно резать произвольным образом  " несколько сбивала с толку.
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      плохо походу 4тест поэтому не пошёл
»
12 лет назад, # |
  Проголосовать: нравится +42 Проголосовать: не нравится
Я, наверное, единственный решал задачу B div 1 потоком?
  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится
    Ололо :-)
  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Расскажете? :)
    За сколько придумалось/написалось?
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится +13 Проголосовать: не нравится
      Первый сабмит был в 31 минуту (через 23 минуты после сабмита А). Правда, я в нем забыл одну переменную обнулить и еще 20 минут искал баг, но во всем остальном решение было верным.

      На самом деле, это решение -- просто попытка избавиться от рассмотрения случаев. Идея простая. Заметим, что у нас в конце могут идти сколько-то нулей, затем 2 цифры, дающих в сумме 10, затем цифры, дающие в сумме 9. Для каждой цифры посчитаем сколько раз она встречалась в нашем исходном числе. Теперь переберем, какая пара цифр дает в сумме 10 и удалим из рассмотрения эти цифры (первую для первого числа, вторую для второго).

      Построим граф из 22 вершин: 10 вершин отвечающих за цифры первого числа, 10 за цифры второго, исток и сток. Между цифрами первого и цифрами второго проведем ребра проп. спос +INF, если они в сумме дают 9 или 0. Затем из истока проведем ребра во все цифры первого числа, пропускной способности равной количеству доступных цифр для первого числа. Аналогично, проведем ребра из цифр второго числа в сток. Легко видеть, что размер потока в этом графе плюс один (та пара, которую мы перебираем) равен макс. количеству нулей в конце, при условии что у нас 2 цифры с суммой 10 фиксированы. Ну и после нахождения потока мы можем легко восстановить сами числа.

      Отдельно надо учесть только случай, когда мы не используем пару цифр с суммой 10.
      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится +3 Проголосовать: не нравится
        Простите, а от каких случаев вы избавились ?
        • »
          »
          »
          »
          »
          12 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Ну как... Нужно решить, какие нули пойдут в конец числа, а какие будут в парах с девятками. Можно, конечно, перебрать количество нулей в конце, но можно это и за О(1) сделать. Только для этого нужно подумать. Я решил что если сходу понятно как делать потоком, то незачем тратить время на обдумывание в котором я еще и могу что-то упустить. Тем более, что поток у меня есть написанный и я его просто скопипастил.
          • »
            »
            »
            »
            »
            »
            12 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится
            Спорить не могу, так как не сдал свое решение во время контеста, но не потому что не придумал, а потому что не написал, нормально, ну и с первого раза все таки до конца недодумал :(

            Хотя мне кажеться, что очевидно что сопоставлять нужно так, 9 и 0, 8 и 1, 7 и 2, 6 и 3, 5 и 4 + перебрать как "сделать 10" в конце числа, ну и если остались нули, то незабыть дописать в конец одинаковое максимально возможное их количество.

            Я ето закодил, но уже после контеста. :(

            з.ы. Мне казалось что вы не перебераете, как в конце нужно "сделать 10". Поетому и возник вопрос.
      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Была аналогичная идея, как только прочитал задачу, но хорошо что не сел писать (1000-ка же, какой нафиг поток) :) Просто можно увидеть, что если мы сначала жадно наберем все 9-ки, а потом допишем оставшиеся нули в обоих числах, то ответ мы не ухудшим.
»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Таки не понимаю, что в D делать с краями поля - они не диагональные, как их учитывать?

>Ясно, что каждый кусок выпуклый, поэтому кусок задается четырьмя диагоналями, которые его ограничивают с четырех сторон.

В начале игры вообще все края недиагональные, в любой момент есть куски с недиагональными краями.

Объясните, пожалуйста, поподробнее этот момент.

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    в плохие клетки не нужно делать ходы, тогда они никак не повлияют на функцию Гранди
  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Просто скажем, что у нас к клеткам типов 'X', 'R' и 'L' добавляются клетки типа '#', в которые ходить нельзя. Тогда изначально поле представляет собой квадрат (m + n - 1) x (m + n - 1), в котором по краям четыре треугольника из '#'-клеток, а в центре - исходный прямоугольник n x m.
  • »
    »
    12 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    Мы можем представить, что поле на самом деле бесконечное. Тогда состоянию [d1, d2, d3, d4] соответствует некий прямугольник, расположенный по 45 градусов к осям. Нас интересует только та его часть, которая пересекается с полем.

»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
У меня получалось немного глупая ситуация, что CF выводил мне Time Limited на задаче Ремонт, но на самом деле это был Run-Time при делении на ноль. Ломал голову почему мой код не проходит по времени, а реально был глупый трабл. =(
»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Я решал сканирующей прямой, и проблем с точностью не возникло (никаких доп. действий, кроме как запоминания количества нулевых вероятностей).
»
12 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

В задаче B div 1 можно не перебирать длину хвоста из нулей. Достаточно перебрать пару значений цифр с суммой 10 (то есть перебрать одно, а второе вычислить). Нетрудно заметить, что ответ не ухудшится, если нули класть сначала слева, группируя с девятками, а потом (когда девятки кончатся) - справа. Это так, потому что без нулей девятки бесполезны (после того, как мы зафиксировали пару с суммой 10).

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Ага, то есть жадность верна, только наоборот. =)
    Ну, тем проще задача, казалось бы?
  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится -11 Проголосовать: не нравится

    Еще по поводу этой задачи:

    >Сложность - O(10 * 10 * N) = O(N).

    Наверно все-таки лучше так не писать =)

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится +5 Проголосовать: не нравится
      а чем это плохо?
      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится +5 Проголосовать: не нравится
        Видимо, тем, что основание системы счисления вносит ощутимый вклад во время работы.
        • »
          »
          »
          »
          »
          12 лет назад, # ^ |
          Rev. 2   Проголосовать: нравится +1 Проголосовать: не нравится

          но от этого весомого вклада асимптотика работы алгоритма же не меняется?

          • »
            »
            »
            »
            »
            »
            12 лет назад, # ^ |
              Проголосовать: нравится +5 Проголосовать: не нравится
            Асимптотика получается O(nk2), где k - основание системы счисления.
            • »
              »
              »
              »
              »
              »
              »
              12 лет назад, # ^ |
                Проголосовать: нравится +1 Проголосовать: не нравится
              ну в нашей задаче k = 10 и не меняется, и при увеличении линейно n, сложность увеличивается так же линейно. разве не так?
              • »
                »
                »
                »
                »
                »
                »
                »
                12 лет назад, # ^ |
                  Проголосовать: нравится +9 Проголосовать: не нравится
                Имеется ввиду, что если наш алгоритм работал бы за N*10^17, то формально бы асимптотика тоже была O(N), но алгоритм был бы малопригоден. Все-таки в спортивном программировании значок O() подразумевает, что за ним скрыта маленькая константа (<=10 обычно)
»
10 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

For problem 139D — Digits Permutations, I think the answer miss the case when there is no pair of number sum to 9 but many pairs sum to 10. So we can solve by for each pair of sum of ten number, it will be separated by a pair of not sum to 10 number.

My accepted solution just fail for a simple case : 5173. Answer should be

7513 3517

which create 2 zero instead of 1. I think the problem size should be reduced.

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    the problem wants to maximize the number of 0's at the end of the sum
    so 7513 + 3517 creates one zero, not two.