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

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

Задача "Доктор" (B в div1, D в div2).
Если сумма всех количеств приемов в очереди меньше чем K, то ответ -1.
Этапом назовем процесс, когда каждый из очереди сделал ровно один прием (то есть очередь пошла заново).

Сделаем бинарный поиск по количеству этапов X. Посчитаем могло ли пройти столько этапов - для этого достаточно пробежать по всем животным из очереди и просуммировать min(a{i}, X) (каждый прошел либо X приемов, либо сколько хотел). Если это число не превзошло K - то могло. Так мы нашли максимальное количество этапов X_max, которое прошло до конца приема доктора.

После этого из всех a{i} вычтем X_max, и выкинем всех животных у которых a{i} стало не положительным. Очевидно, что до конца приема осталось не более чем N этапов - теперь мы их можем просто промоделировать указателем, вычитая 1 из соответствующего a{i}, и удаляя его если оно становиться не положительным.

Асимптотика O(N * log N). Также существуют решения, использующие сортировку, сет и дерево отрезков. Они все имеют такую же асимптотику.


Задача "Трасса" (C в div1, E в div2).
Переберем все возможные комбинации букв, через которые мы пройдем. После это запустим обход в ширину из конечной клетки поля, в учетом того, что можем ходить только по выбранным буквам.
Если начальная клетка недостижима, то пути нету (для данных выбранных букв). Иначе найдем минимальный лексикографический путь, проходящий по зафиксированным буквам.

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

Ответом будет являться минимальная по длине (а при равенстве - минимальная лексикографическая) строка-ответ, полученная среди всех комбинаций букв.
Асимптотика решения O(C{k, 26} * N * M) или O(C{k, 26} * N * M * log max(N, M)) в зависимости от реализации. И то, и другое авторское решение работает не больше секунды.


Задача "Числа" (D div1).
Первый шаг: проверка К на простоту. Если К составное, то ответ 0.
Второй шаг: разбиваем задачу на отрезке [l, r] на две подзадачи на отрезках [1, r] и [1, l - 1].
Вычитаем решение второй из решения первой и получаем ответ.

Теперь рассмотрим два возможных решения подзадачи на отрезке [1, N]:
I) Строгое решение:
Вспомним, как на отрезке [1, N] для всех чисел найти минимальный простой делитель за O(N * log log N) времени и O(N) памяти (на самом деле лучше искать за O(N) но задача прекрасно работает и без этого). Это легко делается с помощью решета Эратосфена, просто для каждого числа запомним минимальный делитель, которым мы вычеркнули это число.

Если N / K int-ов помещается в память то решение элементарно: ответ это количество чисел на отрезке [1, N / K], таких, что их минимальный простой делитель не меньше, чем К (тут можно считать что у 1 минимальный делитель бесконечность).
Имеем количеcтво операций O(N / K * log log (N / K))

Иначе, можно понять, что К небольшое. В частности К не превосходит 100 (на самом деле граница ниже). Пусть P - количество простых чисел не превосходящих К. Среди первых 100 натуральных чисел 25 простых, следовательно P <= 25. А теперь попробуем восстановить ответ с помощью формулы включения-исключения. Переберем все 2^P комбинаций простых чисел меньших К.
Для каждой комбинации определим, сколько существует чисел <= N, которые делятся на каждое простое число из комбинации.
Если в комбинации s чисел, то таких ровно N / K / (p{1} * ... * p{s}), причем если s нечетно то это количество надо вычесть, иначе прибавить к ответу (это и есть суть формул включения-исключения)
С помощью пересчета эту часть можно реализовать за O(2^P) операций и O(2^P) памяти.

II) Без строго обоснования асимптотики:
Для всех чисел до S = sqrt(N) посчитаем минимальных простой делитель. Также выпишем все простые числа до S. Построим рекурсивную функцию F(n, k), которая будет отвечать на поставленную подзадачу для n и k.
Если n / k <= S, то просто пробежимся по всем числам до n / k и просуммируем те, у которых минимальный делитель >= k.
Иначе заметим, что ответ можно вычислить как res = n / k - F(n / k, p{1}) - ... - F(n / k, p{w}),
то есть вычесть все такие числа из диапазона [1, n / k], у которых минимальный делитель меньше чем k.

Точную оценку асимптотики привести не могу) Это работает быстро. Пишется легче, чем предыдущее решение и многие писали что то похожее.
Работает примерно O(sqrt(N) * log(N)), но это эмпирическая оценка без доказательства.

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

14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
ужасно бесит попеременное использование 'n' и 'N'

тег "ipsc" поменяй, пожалуйста)

за разбор спасибо
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Везде N, а n появляется чтобы избежать путаницы с параметром функции F(). Не вижу проблем
    Про тег не понял - напиши подробнее в личку, если не сложно
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      уточняю по 'n' и 'N':

      "...Теперь рассмотрим два возможных решения подзадачи на отрезке [1, N]:

      I) Строгое решение:
      Вспомним, как на отрезке [1, n] для всех чисел..."
14 лет назад, # |
Rev. 2   Проголосовать: нравится +11 Проголосовать: не нравится

А зачем в решении №1 рассматривать первое решение? У меня и так прекрасно работало, так-как большая часть произведений отсеется. (только один вариант , это когда K*K>N ответ 1 или 0, нужно рассмотреть отдельно).
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    прикольно, я об этом не подумал..)
    да, видимо так тоже многие писали)
14 лет назад, # |
Rev. 3   Проголосовать: нравится +1 Проголосовать: не нравится

В задаче C можно уменьшить константу C(26,k). Первое соображение - одна из выбранных букв находится в клетке, смежной со стартовой, а их всего не более 4. Это гарантированно быстрее.
Можно развить идею, но в ряде случаев это будет работать дольше, так как некоторые варианты переберутся несколько раз. Сконденсируем граф, объединив в одну вершину связные компоненты клеток, на которых написана одна и та же буква. В новом графе переберем первые k ходов.

UPD: можно перебирать первые 2 хода в сконденсированном графе, а оставшиеся буквы выбрать обычным образом. Это точно быстрее.
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    да конечно можно) еще можно ставить отсечения всевозможные) но в авторском решении такого не должно быть - оно и так в 5 раз укладывается по времени) позволю себе не писать все хаки по задачам)
  • 14 лет назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится
    А можно такой вопрос, например мы скоденсировали граф и унас получились 3 компоненты 'a', 'b' и 'c'. 'a' и 'c' связи между собой не имеют, и связаны через 'b', как тогда сконденсировать граф так, чтобы была возможность узнать, сколько требуется шагов по 'b' для перехода из 'a'  в 'c'?
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Зачем мы конденсируем граф? Если бы мы этого не делали, то, возможно, пришлось бы перебирать слишком много ходов, чтобы выбрать k цветов. к примеру, стартовая компонента может быть окружена буквами 'a', причем в немалом количестве. чтобы выбрать второй цвет нам нужно как-то из нее выйти.
      Когда мы конденсируем граф, то хотя бы соседние вершины будут разных цветов.

      А знать реальное количество ходов, которое мы сделали и не нужно, цель в том, чтобы не перебирать все C(n,k) вариантов цветов. Многие из них не будут являться цветами, которые могут встретиться на пути из 'S' в 'T'.
      • 14 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Мое утверждение, зная тесты - это тебе поможет вобщем, но не поможет на макс тесте против такой вещи, это уже не оптимизация, а пропихивание)
        1) "Первое соображение - одна из выбранных букв находится в клетке, смежной со стартовой, а их всего не более 4" - с этим я согласен, все остальное уже не нужно.
        2) Без 1) все и так прекрасно.
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Можно нубский вопрос?
Как быстро перебрать все С(26, к)? Кроме рекурсии, конечно
  • 14 лет назад, # ^ |
    Rev. 3   Проголосовать: нравится +1 Проголосовать: не нравится

    А чем не нравится перебирать их рекурсивно?

    int a[K],k;

    void gen(int x,int st){
       if(st==k){
           <doing something>
           return;
       }
       for(int i=x;i<26;i++){
          a[st]=i;
          gen(i+1,st+1);
       }
    }
    int main(){
       gen(0,0);
    }

    вроде самый естественный способ, пишется просто.
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    так как это место не является критическим в проге, то рекурсия самое то)
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Спасибо. Не сразу заметил, что ограничения на К маленькие
      • 14 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        так это все равно работает за суммарный размер всех сгенерированных объектов...
  • 14 лет назад, # ^ |
      Проголосовать: нравится +18 Проголосовать: не нравится
    Самый халявный способ.
    int p[26]={0};
    for(i=26-k;i<26;i++) p[i]=1;
    do
    {
    //some code
    }
    while(next_permutation(p,p+26));
14 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
По задаче Д во второй части строгого решения , по-моему должно быть так : 
нужно взять простые pi < K затем перебирать их множества если в нем четное кол-во элементов то прибавить к ответу N / ( (p{1} * p{2} * ... * p{s}) * K) иначе вычесть. Так посчитается кол-во чисел наименьший делитель которых равен K.
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Я правильно понял, что единственное что имелось ввиду - заменить "не превосходит К" на "меньше К"? Или предлагается дописать 3 решение?
    • 14 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Просто "меньше K" не достаточно. Я вообще считаю, что либо вы неправильно написали разбор, либо решение не правильное)) Могу попозже показать, что мне не нравиться) щас топкодер
      • 14 лет назад, # ^ |
        Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

        Видимо понял про что ты - я не дописал что это все надо вычесть) исправил
14 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится
Не могли бы вы пояснить, как работает второе решение задачи D из разбора на втором примере из условия? (a=12; b=23; k=3) Я понимаю следующим образом. Мы должны рассмотреть интервалы [1;11] и [1;23]. Запускаем сначала f(11,3). Так как n>k*s (11>3*3) , то продолжаем рекурсию, запуская f(3,2). Выполняется условие n<=s*k, поэтому  просто "пробегаемся по всем числам до n / k", то есть от 1 до 1(??). Получаем ответ для n=3;k=2 ноль, что неверно (2 подходит, но мы его не рассмотрели). В чем я неправ?
  • 14 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    да именно до 1. и получаем что таких чисел ровно 1. (на самом деле это и есть 2 но так как мы поделили на k = 2 то на текущей итерации оно будет отражено как 1).
    P.S. эта часть подробно описана в первом решении, так что наверно даже не буду ничего менять чтобы не повторяться)