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

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

Предлагаю обсудить задачи.

  • Проголосовать: нравится
  • +10
  • Проголосовать: не нравится

13 лет назад, # |
  Проголосовать: нравится +4 Проголосовать: не нравится
Как решается F? И как запихать A в TL ?
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    В А после пропихиваний зашло следующее:
    Сначала генерятся целочисленные смещения p[i], что p[i].x^2 + p[i].y^2 == r^2 их что-то порядка 512 максимум таких точек.
    Потом посортить все точки p, посортить все точки входные a, и тогда для каждой входной точки и каждого смещения p надо проверить, что a[i] + p[j] лежит в a. Сначала делал бин поиском, но это не зашло. Потом сделал так:
    Будем идти по точкам a в отсортированном порядке и поддерживать указатели на кандидатов на равенство с a[i] + p[j] (size(p) указателей), когда мы переходим к новый точке - все эти указатели насколько-то сдвигаются вправо (потому что все посорчено). В итоге получается n * size(p).
    Но и это с трудом зашло, пришлось переделать все на массивы и прочие мелкие хаки.
    • 13 лет назад, # ^ |
        Проголосовать: нравится +11 Проголосовать: не нравится
      у нас зашел бинпоиск, когда убрали весь STL, кроме sort
    • 13 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

      моё решение вроде такое же. Но с бинпоиском поучало ТЛ 12. Я убрал весь STL написал свой бинпоиск и т д -> результат опять ТЛ 12. Тогда я убрал бинпоиск вообще и сделал хеш-таблицу -> ТЛ 12. Очень странно

  • 13 лет назад, # ^ |
      Проголосовать: нравится -40 Проголосовать: не нравится
    В F запихано такое решение за n*(2^(количество делителей x))*logn;
    Для х находишь все делители. После этого для каждой маски из делителей находишь остатки. Очевидно, что разность между двумя числами делится тогда и только тогда на маску, когда у этих двух чисел один и тот же остаток. Дальше формула включения-исключения
    • 13 лет назад, # ^ |
        Проголосовать: нравится +3 Проголосовать: не нравится
      А у нас был TL 9 в решении за n*(2^(количество простых делителей x)).
      • 13 лет назад, # ^ |
        Rev. 2   Проголосовать: нравится -37 Проголосовать: не нравится

        Пихать потому что не умеете. Нужно было обычные массивы, никаких мапов и векторов.

        • 13 лет назад, # ^ |
            Проголосовать: нравится +18 Проголосовать: не нравится
          Ну это же бред полный, когда правильное решение нужно заталкивать ногами.
          Что по А, что по Ф.
          • 13 лет назад, # ^ |
              Проголосовать: нравится +31 Проголосовать: не нравится
            Это называется не "бред полный" а СПбГУ-стайл контест :)
          • 13 лет назад, # ^ |
              Проголосовать: нравится +8 Проголосовать: не нравится
            Так очень часто )) и не только у СПбГУ ;)
          • 13 лет назад, # ^ |
              Проголосовать: нравится +21 Проголосовать: не нравится
            Правильное решение не нужно заталкивать. По А проходит такое деоптимизированое решение http://pastebin.com/xbgWHtKf с чтением выводом через cin/cout, использованием STL и криво вычисленными векторами длины R.
        • 13 лет назад, # ^ |
            Проголосовать: нравится -14 Проголосовать: не нравится
          О, пришел Грандмастер Пихания - Адепт Ордена Всезапихивания и навел порядок.
          А ТЛЕ9 получало решение на чистых массивах, использующее хэш на массивах же. Что тут скажешь? Если вы решили писать решение за O(2^p(X)*NlogN) - вам повезло, а вот если решили за O(2^p(X)*N) - вам не повезло. Это про качество задачи.

          Вообще же мое мнение о контесте... Думаю, 90% постов в обсуждении, содержащие слова "пихание", "толкание", "запихивание" и "пляски с бубном" говорят о качестве контеста сами за себя. Хотя многие задачи были интересные.
          • 13 лет назад, # ^ |
              Проголосовать: нравится +11 Проголосовать: не нравится
            Действительно, в задаче F сортировка в среднем работает чуть быстрее, чем хэши. Это связано с тем, что объём кэша на проверяющем сервере невелик (1 мегабайт), и вся хэш-таблица в него не помещается. Объём кэша на сервере — откытая информация; даже если эту страницу не найти, можно его выяснить на пробном туре.

            Вообще, сортировка ведёт себя достаточно локально, а хэш-таблица страдает от кэш-промахов очень сильно. Это знание, которое, как мне кажется, стоит получать вместе с изучением сортировки и хэш-таблиц.

            В соответствии с этим, после замены размера хэш-таблицы (а у вас он всё время 20 миллионов) на 218=262144, ваше самое первое решение вместо TL/9 получает TL/10, а последнее вместо TL/9 получает OK и работает за 3.9 секунды из 5 предоставленных.

            С одной стороны, я понимаю, что со стороны участника неприятно придумать идею решения задачи и не сдать её из-за реализации. С другой стороны, Открытый Кубок — соревнование не по theoretical computer science, а по программированию, и единственное, что гарантируется участникам в этой связи — что решение жюри укладывается в TL как минимум дважды. Вы же не будете использовать фибоначчиевы кучи и потом говорить, что они должны были работать быстрее, потому что у них меньше асимптотика?..

            Наконец, о 90% постов — голословное утверждение, приведите точную статистику.
            • 13 лет назад, # ^ |
              Rev. 7   Проголосовать: нравится 0 Проголосовать: не нравится

              По поводу голословных утверждений - вы еще обратите внимание, я назвал товарища JKeeJ1e30 "Грандмастером Пихания - Адептом Ордена Всезапихивания", а он таковым официально не является, то есть это тоже голословное утверждение!


              Вообще я наврал везде - контест был просто супер, таймлимиты были ни разу не жесткие (и таблица результатов со своими +N и -N, +NN или -NN это наглядно демонстрирует), вопросов и постов про запихивание (http://mirror.codeforces.com/comments/2890#comment-58396), пихание (http://mirror.codeforces.com/comments/2890#comment-58424), толкание и пляски с бубнами (http://mirror.codeforces.com/comments/2890#comment-58439) не было, и многие задачи тоже не были интересными.

              Такая правда Вас больше устраивает? Enjoy!

              P.S. По поводу "олимпиада все-таки по программированию а не по theoretical computer science": даже TopCoder признал после проблем со скоростью вычислений очень малых чисел, что проблемы, связанные с особенностями архитектуры НЕ должны быть проблемой участников. Моя здравая логика подсказывает мне то же самое.
              • 13 лет назад, # ^ |
                Rev. 4   Проголосовать: нравится +24 Проголосовать: не нравится

                Контест был супер, задачи - отличные. -N и -NN - это в основном запихивание кривых решений с не той асимптотикой. Задача A - как заметил Rizvanov (http://mirror.codeforces.com/blog/entry/2877#comment-58461), заходит даже на кривом stl, если писать правильное решение. Как написать B, чтобы не надо было пихать, написал ilyakor (http://mirror.codeforces.com/blog/entry/2877#comment-58442). Про F ничего говорить не буду - по ней куча чистых плюсов. Лично у меня заработало первое, что пришло в голову. По G - avm написал про асимптотически лучшее решение (http://mirror.codeforces.com/blog/entry/2877#comment-58531).

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

                • 13 лет назад, # ^ |
                  Rev. 2   Проголосовать: нравится -29 Проголосовать: не нравится

                  Я ж и говорю - крутой контест! Но я знаю несколько участников, которые в следующем году не станут тратить 5 часов своего времени и кучу невосстанавливающихся нервных клеток на ГП СПб...


                  На том и разойдемся.
                  • 13 лет назад, # ^ |
                      Проголосовать: нравится +9 Проголосовать: не нравится
                    Жаль, что вы не хотите продолжить обсуждение в конструктивной манере.

                    В таком случае мне осталось предупредить, что седьмой этап X Открытого Кубка по плану готовит примерно тот же коллектив авторов.

                    И напоследок — хорошие новости: нервные клетки всё-таки восстанавливаются.
                    • 13 лет назад, # ^ |
                      Rev. 4   Проголосовать: нравится -10 Проголосовать: не нравится

                      ===================================================

                      Я считаю, что конструктивно что-то обсуждать с людьми, которые, глядя на шарик, который мне кажется желтым, говорят, что он синий - бесполезно. Тут нечего обсуждать - это разные видения мира. Я считаю что куча грязи и минусов в таблице и средний success rate по задачам 17% - это обычно неквалифицированное жюри, которое не в состоянии оценить возможности участников/сложность задач и поставить нормальные ограничения по времени. Если кто-то считает, что проблема в участниках - ради Бога. Петру и туристу со товарищи, наверное, действительно интересно.

                      А я лучше погуляю, спасибо что предупредили про седьмой тур.

                      P.S. А про клетки я снова наврал, как и про JKeeJ1e30 и про контест.
                    • 13 лет назад, # ^ |
                      Rev. 2   Проголосовать: нравится +5 Проголосовать: не нравится

                      =====================================
                      Товарищи авторы!
                      Вы, конечно, делайте задачи какие хотите, а мы, конечно, будем решать все подряд, да ещё и благодарны будем, ибо дарёному коню ну ты понел.
                      Замечание только такое. Задачи, где необходимо многопопыточное упихивание либо (в альтернативу ему) хорошее знание неких тонкостей языка программирования и/или устройства железа, не соответствуют политике ACM. Если авторы задач этот факт осознают и их такое положение дел устраивает, то тогда да, я умываю руки.

                      P.S. Хотя нет, формулировка не такая. Слово "необходимо" нужно заменить на фразу "в решении может существенно помочь", так правильнее будет.

                      • 13 лет назад, # ^ |
                          Проголосовать: нравится +19 Проголосовать: не нравится
                        ==========================================
                        1. А есть ли какой-нибудь пруфлинк про политику ACM, и какой конкретно этап имеется в виду (финал?)?

                        2. Про разницу конкретно сортировки и хэш-таблицы — не согласен, что это знание “о железе”. Большая константа у хэш-таблицы — это факт именно из теории алгоритмов, точнее, удобная для теоретика интерпретация того, что случается на практике. Точно так же, как в задачах на какие-нибудь деревья за часто проходят решения корневой оптимизацией за . И бывает, что их не то что не отсечь, они даже быстрее работают. Да, можно сказать, что такие задачи — плохие, некрасивые, неудобные для теоретика. Но они встречаются регулярно, и не только на контестах СПбГУ.

                        3. Я вообще не сторонник того, чтобы при составлении задач проводить такую границу: вот эти знания о программировании — хорошие, теоретические, на них задачу дать можно; а все остальные — плохие, некрасивые, должно быть можно сдать задачу и без них. В частности, по-моему, уметь искать самое узкое место в своей программе ничуть не менее ценно, чем, например, различать алгоритмы Форда-Беллмана и Дейкстры по асимптотике. И я считаю, что как составитель задачи вправе в какой-то момент потребовать от участников применить это умение.

                        4. Бывают задачи, в которых правильное по асимптотике решение работает моментально, а неправильное не проходит по времени с десятикратным запасом. Бывают задачи, в которых ограничения подобрать таким образом невозможно. Эти задачи тоже ценны.
                        • 13 лет назад, # ^ |
                            Проголосовать: нравится -8 Проголосовать: не нравится
                          =====================================================================
                          1. Ну как, конечно же, есть. Вот про финал, там есть такое предложение: "Problem authors should assume that English is the second language of the reader, and they must thoroughly explain any culture or discipline-specific aspects". А вот и про наш полуфинал, там есть такое: "As far as possible, problems will avoid dependence on detailed knowledge of a particular applications area or particular contest language". Да и практикой подтверждается - не припомню ни одной задачи с четырёх последних NEERC, где приходилось бы что-то пропихивать, - там всегда получается так, что если асимптотика хорошая, то проходит с большим запасом, а если плохая, то никакая магия низкоуровневых оптимизаций тебе не поможет. Конкретный пример такой задачи - D с NEERC-2008, там квадрат чётко отсекается от квадрата на логарифм.
                          2-3. О вкусах спорить не будем. Я указал объективную причину, почему некоторого рода задачи плохи, если предполагать, что основная миссия Открытого Кубка - это как бы тренировка команд для наилучшего выступления на ACM-ICPC. Если мы этого не предполагаем (а это вполне допустимо не предполагать, ведь среди участников OpenCup много и ветеранов ACM), то тогда мой аргумент снимается.
                          4. Теоретически-то понятно, как делать так, чтобы проходили алгоритмы только с задуманной автором асимптотикой: поднимать ограничения на входные данные на несколько порядков, но это, конечно, довольно дорого, ведь и ограничения по времени и памяти придётся поднять. Тут мы можем только сказать, что задача отсечения асимптотически плохих решений будет решена с дальнейшим прогрессом вычислительной техники :)
                          • 13 лет назад, # ^ |
                              Проголосовать: нравится 0 Проголосовать: не нравится
                            ====================================
                            1. Про финал не убедил, в этой фразе нет никакого отделения алгоритмической части решения от программирования вообще. Про полуфинал тоже фраза не совсем о том. Вот то, что практикой подтверждается, это уже гораздо больше похоже на правду.

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

                            2-3. Вкусы тут ни при чём. А у OpenCup вообще не сказано, какая миссия, каждый понимает её как ему нужно. Снарк мне на такой вопрос ответил, что одна из миссий — “соревнование на контестах разных авторов, разных школ и разных стилей”.

                            4. Возможно, не боянами к этому моменту останутся только задачи, в которых надо отсечь от .
                          • 13 лет назад, # ^ |
                              Проголосовать: нравится +13 Проголосовать: не нравится
                            Я отвечу только про задачу D с NEERC 2008.
                            Дело в том, что я ее по некоторым причинам очень хорошо помню :-)
                            http://neerc.ifmo.ru/past/2008/standings-with-time.html
                            Так вот:
                            (1) Там НЕ проходил N^2log времени и N^2 памяти (см 5-е место), хотя решение с N^2log времени и N памяти проходило и считалось "правильным" (так же, как и N^2-ное решение).
                            (2) N^2log времени и N^2 памяти таки заталкивался (см. 3-е место :-)
                            • 13 лет назад, # ^ |
                                Проголосовать: нравится +8 Проголосовать: не нравится

                              ==================
                              Если и правда заталкивался, то это печально :(
                              А как там можно было сделать за линию по памяти? O_o Я-то имел в виду обычный алгоритм Дейкстры с бинарным деревом.

                              • 13 лет назад, # ^ |
                                  Проголосовать: нравится +9 Проголосовать: не нравится
                                Основная идея правильного решения задачи: или по X, или по Y нужно идти все время в одном направлении.
                                А реализовать эту идею можно за N^2logN или N^2 времени и (в обоих случаях) N памяти.
                        • 13 лет назад, # ^ |
                            Проголосовать: нравится +5 Проголосовать: не нравится
                          ========================================
                          По поводу поиска bottleneck'ов в контексте сервера opencup'а говорить очень странно - не помню, когда последний раз в "проблемной" задаче bottleneck локально и на сервере совпадал.

                          На самом деле проблема СПбГУ-шных контестов вовсе не в жёстких ТЛях, а в тестах, наполненных максимальными  тесткейсами. Из-за этого приходится считать миллисекунды :( Не очень круто, если при 10 миллисекундах на тесткейс задача не проходит. Понятно, что это "как на финале", но на финале никогда не бывает тестов, набитых максимальными тесткейсами.
                          • 13 лет назад, # ^ |
                              Проголосовать: нравится +13 Проголосовать: не нравится
                            ================================
                            Нет, это не совсем “как на финале”, ведь там не указано, сколько точно и каких может быть кейсов.

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

                            Соответственно, набор тестов будем считать полным, если он отсекает (на практике — старается отсекать) все неэффективные с учётом ограничений решения. Зато никаких умолчаний типа “кейсов сотни, но не тысячи”, и “больших из них всего парочка”.
            • 13 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится
              А как опытным путем (т.е. сабмитами) выяснить используемый размер кэша на сервере ? Речь не обязательно об открытом кубке. Пишем на C++, преимущественно под MVS, если таковое имеется в предлагаемых языках.
              • 13 лет назад, # ^ |
                  Проголосовать: нравится 0 Проголосовать: не нравится
                ==============================================
                Ну заводить массив большого размера и прыгать по нему рандомно 500 миллионов шагов. И с разными размерами массива сабмитить. Как только вылезете за кеш - время работы начнёт расти сильно быстрее.
              • 13 лет назад, # ^ |
                  Проголосовать: нравится 0 Проголосовать: не нравится
                Да, например, как ilyakor описал выше.

                Можно ещё попробовать вставить вызов CPUID и сравнивать с предполагаемым значением (типа, если 1 Mb, то сделать TL, если 2, то WA, если 4, то PE, иначе RE). Правда, в каких-нибудь judge это может быть запрещено.
            • 13 лет назад, # ^ |
                Проголосовать: нравится +8 Проголосовать: не нравится

              Хеши быстрее сортировки, если правильно их писать. Зачем размер хеш-таблицы 20 миллионов для меня не понятно, там ведь максимум 10^5 элементов хранится.

              Либо у вас неоптимальное решение (которое кладёт 20 миллионов чего-то в хеш мапу) либо не умеете писать хеш-таблицу по челевечески.

              • 13 лет назад, # ^ |
                  Проголосовать: нравится 0 Проголосовать: не нравится
                ===================================
                Ну как, чем больше размер хеш-таблицы тем короче будут списки, и кол-во операций на вставку и на проверку есть ли элемент в таблице будет меньше. 
                • 13 лет назад, # ^ |
                    Проголосовать: нравится +4 Проголосовать: не нравится
                  ===================================================
                  Это если бы не придумали кеш. А с ним получается такое несложное арифметическое построение. Предположим, что хеш идеальный, то есть ведёт себя как случайное число, равномерно распределённое на диапазоне.

                  Пусть размер кеша 1 мегабайт, и пусть он всегда заполнен элементами хеш-таблицы. Если наша хеш-таблица занимает 2 мегабайта, вероятность ткнуть мимо равна . Если таблица занимает 10 мегабайт, вероятность ткнуть мимо равна .

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

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

                  Если таблица в 10 раз больше, чем количество элементов, и её размер равен 10 мегабайтам, в среднем мы тыкаем в элементов. Получаем время работы .

                  Видно, что чем больше k, тем второе время хуже первого.

                  Если бы не было этого эффекта, было бы всегда выгодно заводить хеш-таблицу на все 256 мегабайт данной в задаче памяти. Но ведь почему-то обычно так не делают...
                • 13 лет назад, # ^ |
                  Rev. 2   Проголосовать: нравится +3 Проголосовать: не нравится

                  Не буду выдумывать хитрые формулы, просто скажу из моей практики: размер хеш таблицы должен быть такой, сколько элементов ты туда добавляешь. я чаще беру степень двойки которая не меньше n. Например для 10^5 беру 1 << 17. Это выгодно ещё и тем, что не нужно операции остатка от деления, можно юзать битовые операции. x % hash_size == x & (hash_size - 1), если hash_size = 1 << k

13 лет назад, # |
  Проголосовать: нравится +12 Проголосовать: не нравится
В частности, интересует задача F.
Кто-нибудь смог на Джаве сдать включение/исключение простых делителей числа X? TL9 и всё.
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    То есть какое решение писал я:
    Находим все простые делители X за sqrt(X), дальше перебираем рекурсией все комбинации, и для каждого делителя считаем количество пар с одинаковым остатком (тут сначала был только хешмап, потом для маленьких делителей добавил массив).
    Это количество плохих пар получается, дальше вычитаем из общего кол-ва = ответ.
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Делали тоже самое только на С++. С map был ТЛ9. Заменили на массив + сортировка - прошла.
    • 13 лет назад, # ^ |
        Проголосовать: нравится +5 Проголосовать: не нравится
      Заменяешь числа на остатки, и с помощью сорта находишь количество пар одинаковых чисел.
      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Ага, сдалось.
        И ведь я писал с сортом, у меня на локальном компе раза в 2 медленнее работало.
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Мы тоже так писали и постоянно был TL9. 
13 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

Аналогичная проблема :(

13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Расскажите ещё про G, пожалуйста. Там всё-таки жадность с каким-то модифицированным Хаффманом или всё-таки хитрая динамика? Пробовали писать перебор с отсечениями - работает долго.
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Я сдал хитрой динамикой. Но её пришлось сильно упихивать на всём, что только можно.
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      А там где-нибудь участвовала мапа с ключами до 230? :)
    • 13 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится +5 Проголосовать: не нравится

      Если коротко. Заметим, что у нас есть некоторое полное двоичное дерево высоты L, каждой букве соответствует некоторая вершина дерева, ни одна из букв в таком дереве не является потомком другой. Тогда если буква расположена на высоте h, то она "съедает" 2^(L-h) листьев. Заметим, что код можно построить тогда и только тогда, когда sum(2^(L-h[i])) <= 2^L. Тогда можно написать тупую динамику: dp[i][j] - ответ, если мы закодировали уже i букв и потратили j листьев. Проблема лишь в том, что j может быть до 2^L, т.е. примерно до 10^9.

      Сделаем следующее. Посортим все буквы по возрастанию кол-ва использований. Тогда длины их кодов будут невозрастать. Поэтому если у нас есть буква с кодом длины h, т.е. съевшая 2^(L-h) листьев, то количество листьев, потраченных на предыдущие буквы, можно округлить вверх до числа, кратного 2^(L-h). Тогда получаем такую динамику: dp[i][j][k] - ответ, если мы закодировали i букв, у последней длина кода - j, и всего потрачено листьев k*2^(L-j). Пересчитывается она за константу, и с большим скрипом влезает в tl.

      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Круто! Спасибо.
        Нам не хватило осознания того факта, что можно округлить.
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Можно и нехитрой динамикой. Сортим все буквы по частоте (т.к. итоговое дерево префиксов можно повращать, чтобы всё было отсорчено). Теперь считаем такую динамику: пусть у нас вершина дерева на расстоянии len от корня, мы хотим в этой вершине закодировать все буквы из отрезка [l, r]. Перебираем, где отрезок разбиваем на 2 поддерева, - получаем динамику с O(n2l) состояниями и временем работы O(n3l). После этого замечаем, что точка оптимального подразбиения для [l, r] не левее, чем для [l, r-1] - получаем O(n2l). А после этого пляшем с бубном, чтобы утолкать в ТЛ.
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    У нас такая динамика - какой уровень, сколько букв мы уже закодировали (первых в посорченных по убыванию фриквенси), сколько свободных листьев на текущем уровне (но не более, чем число оставшихся букв). Переход - либо кладем букву на этом уровне, либо идем на следующий уровень. Получается O(n2l). А дальше оптимизация по времени
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Как пишется жадность за O(NL), можно прочитать здесь: http://en.wikipedia.org/wiki/Package-merge_algorithm
13 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

кто-нибудь знает, как решается I ?

  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Перебор по коду на стороне сервера)
    Хотя у Zhukov team: Zhukov он тоже не прошёл)
  • 13 лет назад, # ^ |
      Проголосовать: нравится +28 Проголосовать: не нравится
    Пишешь чекер, по правилам, которые в условии описаны.

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

    Я рандомно комбинировал вот такие строки: "NE", "EN", "ES", "SE", "SW", "WS", "NW", "WN".
    Сдалась комбинация, локальна дающая 900-970 score с использование rand на С++.
    • 13 лет назад, # ^ |
        Проголосовать: нравится +18 Проголосовать: не нравится
      То, что я писал на контесте, но не успел, и потом оно зашло в дорешивании: сгенерируем N тестов, потом будем делать каждый ход или два так, чтобы минимизировать сумму f(позиция в каждом тесте) для некоторой функции f. Если f - просто расстояние до выхода, то не работает - в некоторый момент упирается в локальный минимум. Сработало f = -2^(-расстояние_до_выхода). Тогда при тренировке на N=20000 тестах оно сходится за 5000 шагов (на всех 20000 тестах приходит в выход), и на независимом тест-сете получается в среднем 800 шагов на тест (а на трейнинг сете 700).

      Вот код.

      В пост призывается Gassa, чтобы рассказать, что решение жюри дает в среднем 500 шагов, и как оно это делает :)
      • 13 лет назад, # ^ |
          Проголосовать: нравится +16 Проголосовать: не нравится
        У меня тоже 800 (на 20 тестах жюри 699–808).

        Идея примерно такая же. Будем генерировать случайные лабиринты по алгоритму и сортировать их по расстоянию до цели.

        Как только лабиринтов стало много (скажем, 10000), берём тот из них, до которого расстояние минимально, и проходим его (естественно, сдвигаясь так же во всех остальных и во всех будущих лабиринтах). После этого догенерируем ещё лабиринтов так, чтобы у нас висело 10000 непройденных, и опять пройдём тот, который быстрее всего пройти. Будем повторять, пока не кончится длина ответа. Это уже где-то 1000–1100 штрафа.

        Улучшение: при равенстве расстояний (а после первых нескольких лабиринтов минимальное расстояние будет почти всегда 1, и будут сотни лабиринтов на таком расстоянии) выбираем шаг в ту сторону, в которую лабиринтов за этот шаг пройдётся больше. Это важно: сразу становится 700–800 штрафа.

        Ещё нужно, чтобы генератор работал быстро (например, в поиске двух наиболее удалённых клеток притворимся, что граф — дерево), так как с такими константами генерируются миллионы лабиринтов, а длина ответа всё не становится 10000.

        Константы (размер поддерживаемого множества и желаемую длину ответа) можно подкрутить так, чтобы и работало минут 5–10, и штраф был 800–900.

        Ещё я пробовал генетический алгоритм, который строит ответ по маленьким кусочкам длины 100 (здесь и далее в решении все константы взяты с потолка). Для каждого кусочка сгенерируем 1000 лабиринтов и будем оценивать ответы по тому, сколько из них пройдено. Популяция — 100 путей, сначала просто пишем случайные буквы. Скрещивание — начало и 50 от одного + 50 от другого, мутация — какие-то символы ответа заменить на прохождение с этого места какого-нибудь лабиринта. Сгенерировали несколько поколений, 100 лучших кусочков зафиксировали как начала путей и с ними аналогично строим 101..200 символы ответа, и так далее.

        Правда, этот алгоритм часа за полтора с трудом добивается штрафа 830–960, но в реализации есть что улучшить.
        • 13 лет назад, # ^ |
            Проголосовать: нравится +8 Проголосовать: не нравится
          Ага, видимо у тебя то же самое решение, только с 2->inf.
13 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

Как пихать B?


У нас 220 * mulmod(u32,u32,u33) -- предподсчёт.

 +

Ответ 200 * 212 * lower_bound(x, x + 220).

mulmod(a,b) работает за число битов в b + число битов в ( (a*b в gl(2)) >> 32 ).
  • 13 лет назад, # ^ |
      Проголосовать: нравится +10 Проголосовать: не нравится
    Meet in the middle мы так и не упихнули, хоть и очень старались (но некоторые команды упихнули).
    В итоге написали мнговенно работающее решение с КТО. Найдём ответ по модулям 65535 и 65537. Как же это сделать? Пусть ответ n, . Тогда Xn65537 = A65535, где X = x65535, A - запрос. Аналогично остаток по модулю 65535. Итого запрос делается за 2 бинпоиска.
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      что ты понимаешь под meet-in-the-middle относительно этой задачи?

      giant-step-baby-step?

      я запомнил в хеш таблице для всех 0 <= i < (1 << 16), map[(X^(i << 16)] = i;

      потом домножал A на X и искал это в таблице.

      на C++ с самописной хеш-таблицей прошло спокойно.


      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Да. Это ведь обычный meet in the middle.

        Есть подозрение, что у нас тормозил вовсе не поиск (который хоть и двоичный поиск, но всего с 1-2 промахами кеша), а умножение многочленов по модулю. Но это фиг поймёшь - на нашем компе на 500 кейсах работало полсекунды, но он 1) 64-битный, 2) с большим кешом.
        • 13 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          а как делали умножение и остаток? я многочлен в long long хранил.

          умножение за ~32 битовых операции с ними и остаток тоже.

          • 13 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится
            Ну да, умножение и взятие остатка суммарно за 32 * 8 битовых операций с long long, 64 операции инкремента int'а и 2 вызова функции.
            • 13 лет назад, # ^ |
                Проголосовать: нравится +5 Проголосовать: не нравится
              Мы, когда запихивали, одной из оптимизаций сделали запоминание giant step, и перебор baby step. Ведь чтобы сделать baby step, нужно умножить на x и взять остаток, а это за 3 битовые операции делается - poly <<= 1; if ((poly & (1L << 32)) != 0) poly ^= MOD;
              • 13 лет назад, # ^ |
                  Проголосовать: нравится +8 Проголосовать: не нравится

                Кстати, при аккуратной реализации всё в 32-битный целый тип помещалось.

                Не знаю, на сколько это быстрее long long'ов, но думаю, что хоть немного, но тоже ускоряет.


  • 13 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится
    Сорри, не туда
  • 13 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +5 Проголосовать: не нравится

    Нам помог __gnu_cxx::hash_map. В meet-in-the-middle били степень на 2 одинаковые части.

    • 13 лет назад, # ^ |
        Проголосовать: нравится +10 Проголосовать: не нравится
      Да, если вместо lower_bound использовать самописную хэш-таблицу, тоже нормально проходит.
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Вроде как используя идею табличного CRC можно было mulmod за O(1) писать.
13 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится
Извините, а D как решается?
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Ответ Second на простых числах и степенях 2-ки, иначе First. Выяснилось перебором.
    • 13 лет назад, # ^ |
        Проголосовать: нравится +16 Проголосовать: не нравится
      мы доказали. причем оказалось, что во время контеста доказали криво, но оно зашло) после контеста осознали, что доказательство кривое и нашли верное.

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

      если начетного делителя нет, то n=2^x. по сути все вершины можно разбить на 2 цикла в периодом 2 и они будут работать как и в 1м случае. тут 2й игрок пользуется вышеописанной стратегией 1го игрока.

      ну и еще случай когда ходов вообще делать нельзя (n простое) тривиален - тут выигрывает 2й игрок.
    • 13 лет назад, # ^ |
        Проголосовать: нравится +5 Проголосовать: не нравится
      Я написал тупая динамика для N < 25, но не видел ничего полезнoго... Ripatti, спасибо за доказательство!
    • 13 лет назад, # ^ |
        Проголосовать: нравится +13 Проголосовать: не нравится
      Мы писали честное решение за O(количество_делителей_n^2 * log(n)). По времени не зашло, а посмотреть ответы для первых нескольких n додумались только в последний час. Провал.
      • 13 лет назад, # ^ |
          Проголосовать: нравится +8 Проголосовать: не нравится
        А можешь рассказать?
        • 13 лет назад, # ^ |
            Проголосовать: нравится +5 Проголосовать: не нравится
          Пусть есть m-угольник, каким-то ходом отметим k-угольник. Тогда можно убедиться, что то что останется развалится на несколько правильных многоугольников, таких, что каждый возможный ход помечает вершины только в одном из этих правильных многоугольников. Получаем, что от одной игры мы можем перейти к сумме игр. Количество_делителей_n^2 на перебор m и k, логарифм на то, чтобы найти на какие многоугольники развалится игра после хода.
          • 13 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится
            Я тоже пытался сдать такое решение. Я даже убедился, что без мультитеста оно работает за считанные миллисекунды. Решение действительно красивое и жалко, что оно не должно проходить, а забитая в OEIS последовательность дает OK без бревен и попыток пройти TL.
            • 13 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится
              Во-первых, как выяснилось, оно всё-таки неверное.

              Во-вторых, по правилам OEIS пользоваться было нельзя.
          • 13 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится
            Неправда же. Когда простых делителей у n больше двух. Пусть n = 30, k = 2, отметили точки 0 и 15. Неверно, что оставшееся развалилось в сумму 14 игр размера 2: например, мы можем теперь отметить треугольник из точек 1, 11 и 21. Или я чего-то не понимаю в решении?

            Хотя при таком решении ответ на вопрос “кто выиграет при правильной игре, начиная с пустой доски” будет такой же: проигрышные партии так и остались проигрышными, а в выигрышных нам важно, чтобы был такой ход, что — нечётное простое число, и после этого хода разбиение на независимые подзадачи действительно работает.
            • 13 лет назад, # ^ |
                Проголосовать: нравится +5 Проголосовать: не нравится
              Многоугольники, на которые развалится оставшееся после одного хода, совсем не обязательно многоугольники размера k. Например, если n=30, k=2, результатом будет два многоугольника по 10 и четыре "многоугольника" по два.
              • 13 лет назад, # ^ |
                  Проголосовать: нравится +5 Проголосовать: не нравится
                А какие именно многоугольники по 10, как они строятся? Кажется, что если в многоугольнике лежат треугольники (скажем, точки 1, 11 и 21), его размер должен делиться на три.
                • 13 лет назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится
                  Многоугольники 1, 4, ..., 28 и 2, 5, ..., 29.

                  Значит мое решение, которое я писал на контесте, (а оно разбило бы именно так) неверно. Что характерно, прошло 19 тестов, а на 20-м упало по времени.
                  • 13 лет назад, # ^ |
                      Проголосовать: нравится 0 Проголосовать: не нравится
                    20 тест — это 100 раз число с 1344 различными делителями.

                    Решение-то может и неверно, но ответ у него верный :) . Косвенное подтверждение — до этого места есть все тесты 1001..2000.
13 лет назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

А как люди решали C? У нас была идея приблизить дробью (просто цепной дробью), а после этого считать количество целых точек q'y - p'x > 0. Но для этого надо уметь решать полученную задачу, что вроде бы баян, но само по себе сложно. Она так делается и все умеют писать решение для ax + by > 0 по памяти, или тут что-то проще?

13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Подскажите как решается задача K (div 2).
  • 13 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +3 Проголосовать: не нравится

    Сначала нужно из префиксной записи сделать нормальную, а затем из нормальной сделать постфиксную.

    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Есть решение гораздо проще. Из-за небольших ограничений здесь можно было обнаглеть и делать все в лоб. Сохраним все в виде массива строк. Теперь слагаемым может быть не только обычное число, но и целое выражение в постфиксной нотации. Будем выполнять следующий набор операций до тех пор, пока не останется всего одно слагаемое:
      1) Найдем самую левую позицию i такую что: Ai - знак арифметической операции, Ai+1 и Ai+2 - слагаемые. Такая позиция точно должна существовать, если слагаемых больше одного.
      2) Превратим Ai в слагаемое вида: Ai+1 + Пробел + Ai+2 + Пробел + Ai. То есть переведем этот участок выражения в постфиксную форму.
      3) Удалим Ai+1 и Ai+2 из массива (со смещением, конечно же).
      В конце у нас останется одно слагаемое - все исходное выражение в постфиксной нотации. Асимптотика такого решения равна O(много буков), но я прикинул, что больше 108 простейших операций там точно не выполнится.
      • 13 лет назад, # ^ |
          Проголосовать: нравится +10 Проголосовать: не нравится
        Можно еще рекурсивно.
        Если текущий символ (обозначим его с) - плюс или минус, вызовем функцию еще 2 раза подряд (т.е. для операндов этого оператора) - получим строки s и t, и возвратим s + t + c.
        Иначе возвращаем c.
13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Не хочу спамить в отдельном посте, интересует вопрос: на каких серверах, кроме CF, можно запустить виртуальные контесты (например, какие-нибудь полуфиналы/четвертьфиналы ICPC)?