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

Автор snarknews, история, 7 лет назад, По-русски

Второй раунд SnarkNews Summer Series 2017 заканчивается 18 августа в 22:00 (то есть примерно через 4 часа). Как и несколько предыдущих серий, SNSS-2017 проходит на системе Яндекс.Контест.

Начать участие в серии можно с любого раунда.

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

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

»
7 лет назад, # |
  Проголосовать: нравится +15 Проголосовать: не нравится

Как решались C и F?

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

    С-шка решается жадно. Пусть для определенности мы в начале всегда идем вправо. Для каждой точки можно вывести довольно несложную формулу с четырьмя случаями для времени, когда мы её посетим. Заметим, что мы постепенно посещаем все точки с максимумом модулей разностей координат до начальной 1,2,3... Чем меньше максимум этих значений тем лучше. Поэтому бинпоиском найдём минимальное D, проверяя за O(N), что множество точек, где может располагаться старт не пусто и не занято контрольными точками. Это множество точек — пересечение квадратов с центрами в контрольных точках и сторонами 2D+1 x 2D+1.

    Теперь множество точек, где может располагаться центр образует границу некоторого прямоугольника. Внутренность пустая, т.к. точки там имеют меньшее D. Можно разбирать какие-то случаи, но понятно, что нас интересуют или углы или самые левые, правые, нижние или верхние точки на границах. Найдём эти точки и попытаемся улучшить ответ.

    Повернём все точки на 90 градусов и повторим еще 3 раза.

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

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

    Если мы построим такой граф, то дальше надо просто запустить Дейкстру за квадрат. Чтобы построить граф, просто переберём все пары точек и посчитаем, сколько барьеров пересечёт отрезок между ними с одной важной оптимизацией: когда мы перебираем пару точек (x1, y1) и (x2, y2), то нужно учитывать вертикальные барьеры только с x от min(x1, x2) + 1 до max(x1, x2) - 1 и горизонтальные барьеры с y только с min(y1, y2) + 1 до max(y1, y2) - 1, так как другие барьеры отрезок заведомо не пересечёт (выделить их можно с помощью бинпоиска в отсортированных массивах горизонтальных и вертикальных отрезков).

    UPD. В ответ на вопрос zeliboba. Почему работает быстрее тупого куба: у нас всего 2n + 2 = 1002 (назовём это число m) x-ов (с повторениями), столько же y-ов. Тогда если просуммировать для каждой пары x-ов сколько между ними других x-ов, то получим Мы рассмотрим горизонтальные барьеры суммарно не больше, чем столько раз, аналогично с вертикальными. проверок на пересечение отрезков для m = 1002 уже звучит вменяемо. На самом деле понятно, что это грубоватая оценка сверху, так как у нас всего n = 500 барьеров суммарно.

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

      Куб я придумал на контесте, но что-то не понятно, почему он заходит, там 1000 в кубе вроде бы.

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

Дайте hint по E, пожалуйста.

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

    Я сдал рекурсию с запоминанием. Главное было влезть в ограничение по памяти.

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

      хватает запоминать если оба параметра меньше 1000

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

        Так у меня был тл.

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

          Интересно, у меня зашло

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

            Можешь код рекурсии скинуть? Возможно я в какие-то ветки ненужные шел.

            • »
              »
              »
              »
              »
              »
              »
              7 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится
                  private int goMin(int n, int m) {
                      if (n > m) {
                          return goMin(m, n);
                      }
                      if (m == 2 * n) {
                          return 1;
                      }
                      if (m < SIZE && min[m][n] != 0) {
                          return min[m][n];
                      }
                      int answer = INFTY;
                      boolean skipDouble = false;
                      if ((n & 1) == 0) {
                          int times;
                          if (m > 2 * n) {
                              times = (m - 2 * n - 1) / (n >> 1) + 1;
                          } else {
                              times = 1;
                          }
                          if (times >= 4) {
                              skipDouble = true;
                              times /= 4;
                              answer = Math.min(answer, goMin(n, m - 2 * times * n) + times);
                          } else {
                              answer = Math.min(answer, goMin(n, m - (n >> 1) * times) + times);
                          }
                      }
                      if (m > 2 * n && !skipDouble) {
                          int times = (m - 1) / (2 * n);
                          answer = Math.min(answer, goMin(n, m - 2 * n * times) + times);
                      }
                      if ((m & 1) == 0 && m < 2 * n) {
                          answer = Math.min(answer, goMin(n - (m >> 1), m) + 1);
                      }
                      if (m < SIZE) {
                          min[m][n] = answer;
                      }
                      return answer;
                  }
              
                  private int goMax(int n, int m) {
                      if (n > m) {
                          return goMax(m, n);
                      }
                      if (m < SIZE && max[m][n] != 0) {
                          return max[m][n];
                      }
                      int answer = -INFTY;
                      if (m == 2 * n) {
                          answer = 1;
                      }
                      if ((n & 1) == 0) {
                          int times;
                          if (m > 2 * n) {
                              times = (m - 2 * n - 1) / (n >> 1) + 1;
                          } else {
                              times = 1;
                          }
                          answer = Math.max(answer, goMax(n, m - (n >> 1) * times) + times);
                      } else if (m > 2 * n) {
                          int times = (m - 1) / (2 * n);
                          answer = Math.max(answer, goMax(n, m - 2 * n * times) + times);
                      }
                      if ((m & 1) == 0 && m < 2 * n) {
                          answer = Math.max(answer, goMax(n - (m >> 1), m) + 1);
                      }
                      if (m < SIZE) {
                          max[m][n] = answer;
                      }
                      return answer;
                  }
              

              SIZE таки 2000 оказывается

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

                У меня почти полностью идентичный код, за исключением того, что у меня в паре мест times = max(1, m / (2 * n) — 1) вместо times = (m — 1) / (2 * n).

                У меня заходит только с SIZE = 20000.

»
7 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

Вот такое решение по A получает вердикт "TL 2", хотя в условии стоит ограничение n ≤ 30. Хуже того, это n не соответствует реальному количеству предложений.

#include <iostream>

int main() {
  int p, l, m, n;
  std::cin >> p >> l >> m >> n;
  while (n > 30)
    ;

  std::cout << "4\n";
  return 0;
}

Как часто жюри читает сообщения от участников через тестирующую систему и имеет ли смысл их писать?

»
7 лет назад, # |
Rev. 3   Проголосовать: нравится +15 Проголосовать: не нравится

На четвертый раунд отдельной темы вроде бы нет, поэтому, если можно, спрошу здесь:

1) Как все-таки решается задача B?

2) Почему в задаче C такие странные ограничения?

upd. Понял про С. Можно решать за O(N + M2), если предварительно сжать строку по запросам. А я-то думал, что это для того, чтоб я O(NM) сдал, и с декарткой не парился =/

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

    1) Пусть t0 ≥ ... ≥ t2n - 2. Возьмем нулевой элемент. Теперь по t будет все хорошо, если взять любого из (t1 и t2), из (t3 и t4) и т. д., так как t0 мажорирует любого из t1 и t2, любой из t1 и t2 мажорирует любого из t3 и t4 и т. д. Теперь в каждой паре (b1, b2), (b3, b4), ... будем выбирать максимум.

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

      Спасибо! Я только не могу понять, за что ваш ответ минусуют?(

  • »
    »
    7 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +20 Проголосовать: не нравится

    Наверное, в С задумывался квадрат — если записывать строчку в виде набора атомарных кусков, то таких кусков будет О(M) (каждое "вырезать и перевернуть" добавляет не более двух кусков), и можно делать одну операцию наивно за О(M) — переставляя куски и меняя им флаги реверса; получим O(M2) суммарно.

    Кстати, не в тему — я не заценил идею не писать явно ограничения. Я к тому, что если дать один тест размера 2.5е6, то в той же В у меня пачка векторов свалится по МЛ.

    Upd. А что там в С, нормально NM заходит? :D

    • »
      »
      »
      7 лет назад, # ^ |
      Rev. 4   Проголосовать: нравится +10 Проголосовать: не нравится

      Upd. А что там в С, нормально NM заходит? :D

      Куда оно денется, конечно заходит =) На самом деле, обычный reverse конечно же не заходит. Но если делать тупой swap с изначально развернутой строкой, то заходит за 0.89s без каких либо оптимизаций.
      По правде говоря, почти все попытки соптимайзить заканчивались ТЛем. То есть компилятор лучше всего понимает обычный цикл со swap-ами и оптимайзит его сам =)
      Ну и #pragma GCC optimize("Ofast") надо было не забыть.

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

        (Оффтоп) А что Ofast, на практике часто лучше О3? Я когда-то читал, какие вещи он дополнительно оптимизит, но не вдумывался, какой у этого практический смысл для контестов.

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

        (И еще оффтоп) Ну вы это, к сезону АСМ научили наконец-то кого-нибудь в команде декартку писать? Пора бы уже:)

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

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

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

          Так-то я перед VK Cup`ом почти закодил своппера декарткой =) Но увы, дальше дело пока не продвинулось.