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

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

Это решение этой задачи было придумано однажды ночью при разработке совершенно другого контеста :)

Пусть - ответ задачи.

Тогда верен факт (например, можно доказать его из формулы включений-исключений)



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

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

Представим, что мы честно, на листочке, хотим провести расчет по вышеуказанной формуле, например:



Стоп! Так считала наша программа, а не мы. Мы считали вот так:



Это толкает нас к следующей идее: будем представлять наше состояние на шаге j двумя массивами Xj и Yj, имея ввиду



Причем на последнем шаге j = k будем иметь



А переход от шага j к шагу j + 1 выполняется по самой первой формуле с последующим слиянием отсчетов с одинаковым Y (массивы в любой момент поддерживаются сортированными, фактически производится слияние прошлого с обновлением и т.д.)

Сложность этого решения, будет, очевидно,

Утверждение.



Доказательство.

Любое число, попадающее в массив Y, имеет вид для некоторого натурального a. Заметим, что



А число вида задается однозначно либо (самим собой), либо a, из чего очевидным образом следует доказываемое утверждение.

Таким образом, сложность этого решения

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

Решение, написанное с оптимизацией хранения для маленьких отсчетов Y (можно посмотреть мои посылки) на сервере CF выполняется порядка 300мс на худшем тесте и укладывается в 2 секунды для N = 1015, k = 100 (т.е. решает задачу при ограничении на N два порядка больше)

Вопросы, предложения, пожелания?

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

13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
спасибо. но я думаю, что такую задачу в рамках CF никто бы не решил (N <= 10^15).
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Это вторая причина, по которой я так не сделал. Формат CF не позволяет "проталкивать" решение.
13 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится
Можно пропихнуть O(k*sqrt(N)*logN). Правда на контесте оно упало, но после оптимайзов прошло в дорешивании. Решение абсолютно то же самое, только вместо слияния используется сортировка каждый раз (изначально был map).
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Пропихнуть можно. Но оптимизировать решение с изначальной пессимизацией всегда очень долго и утомительно.
»
11 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Объясните, пожалуйста, откуда берется формула. Как ее доказать из формулы включений-исключений?

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

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

    разгруппируем слагаемые, содержащие a1 и не содержащие a1 и применим формулу включений-исключений в обратную сторону.