Блог пользователя ivan.popelyshev

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

Условие задачи

Есть N ≤ 1018 пар (Pi, Wi) сгенерированных по следующему алгоритму:
  • Pi = ((A*Pi-1 + B) mod M) + 1
  • Wi = ((C*Wi-1 + D) mod K) + 1
  • Где  M, K ≤ 107, P1, W1, A, B, C, D заданы.
  • Требуется найти:
  1. Количество пар  (Pi, Wi)  таких что не существует пары (Pj, Wj) такой что Pj ≤ Pi и  Wj < Wi либо  Pj < Pi и  Wj ≤ Wi
  2. Количество пар  (Pi, Wi)  таких что не существует пары (Pj, Wj) такой что Pj ≥ Pi и  Wj > Wi либо  Pj > Pi и  Wj ≥ Wi 
Разберемся с первой частью, вторая делается аналогично.
Заметим, что если все Pi одинаковы, то ответ это количество минимумов.
Переходим к двумерной задаче: для каждого P < M будем хранить текущий минимум minp и количество найденных пар вида (P, minp)
Если посчитать эти величины, то ответ ищется просто: для каждого p надо проверить что нет такого p0 < p что minp0 ≤ minp , и в этом случае прибавить к ответу количество  пар вида (P, minp). Это делается за один проход.

Как же искать этот minp ?

Понятно, что через max(M, K) элементов обе последовательности зациклятся. Обработаем этот кусок, выкинем его и рассмотрим циклы, осталось обработать N1 = N - max(M, K) пар.
Получившиеся циклы:
p0, p1, p2, p3,  ... pA - 1
w0, w1, w2, w3, ... wB - 1
Для фиксированного pi важно знать минимум и количеством минимумов тех  w, что попадают в пару вместе с фиксированным pi:
Для p0 они имеют индексы 0, A%B, (2 * A)%B, ...
Для p1 это 1, (A + 1)%B, (2 * A + 1)%B, ...
Для p2 это 2,  (A + 2)%B,  (2 * A + 2)%B,  ... 
Для p(A - 1) это (A - 1)%B,  (2 * A - 1)%B, ... 
Все индексы меньше N1

Вот тут можно поступить по-разному
  • Разбить цикл из w на орбиты элементов которые идут через A и на каждой использовать RMQ. Это  O(max(M, K) * logN) по времени. Можно улучшить до O( max(M, K) ) используя RMQ за O(1) два раза, ведь последовательности могут иметь длины N1 / A и  N1 / A.
  • Cчитать минимум и количество минимумов среди элементов w проиндексированных (i + j * A)%B , 0 ≤ j < 2k для каждого k, и использовать на этом двоичный подъём, "перемножая" массив как при быстром возведении в степень. Это  O(max(M, K) * logN) по времени
В обоих случаях получается и и O(max(M, K)) по памяти. 

Лично я выбрал второе, макстест на Java работал за 17 секунд, но на их 20 тестах программа уложилась в 2 минуты. К тому же можно было запускать это сразу на нескольких компах/ядрах.


Если вы считаете что можете объяснить это лучше и проще, пожалуйста, оставьте свои объяснения в комментариях.
  • Проголосовать: нравится
  • +70
  • Проголосовать: не нравится

»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Извините, но не могли бы вы объяснить следующее: "Понятно, что через max(M, K) элементов обе последовательности зациклятся.". Я интуитивно догадываюсь, а доказать не могу.
  • »
    »
    12 лет назад, # ^ |
    Rev. 4   Проголосовать: нравится +1 Проголосовать: не нравится

    А, это просто.

    Если Wi = Wj то Wi + 1 = Wj + 1, а значит Wi + 2 = Wj + 2 и так далее. При этом различных W не более K. Одно повторение и она зацикливается.

    Ещё раз напишу: надо, чтобы когда зациклились обе по отдельности.
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      А можно еще вас помучать на эту тему?

      Судя по теории все это близко к Линейному конгруентному методу формирования случайных чисел, там действительно период лежит в пределах M. Но в задаче формула другая, и когда я ее пытался решить, то даже в тестовом примере там существовал случай когда периодичность устанавливалась не с первого элемента, а надо было отмотать какое-то количество значений, прежде чем находился элемент который являлся началом периода. Это мне приснилось?
      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Всё так, поэтому я и предлагаю отмотать max(M, K) , так как за этот промежуток гарантированно начнутся повторы.
»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Не понятно почему во втором случае с двоичным подъемом O(max(M, K)) памяти, а не O(max(M, K) log max(M,K)). И, кстати, у тебя на Java не получалось OutOfMemory на каких-нибудь своих тестах? Мне вот пришлось извернуться, чтобы такого не возникало
  • »
    »
    12 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    -xmx2048M помог.

    Если делать аналогично быстрому возведению в степень, логарифма по памяти нету.

    UPD.
    То есть надо возвести в степень весь массив, а не посчитать таблицу для всех 2k и потом для каждого элемента идти.
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      хм... ну у меня только -xmx1G :)
      Так ведь возводить в степень весь массив это долго (за его длину на логарифм), а минимум нужно O(M) раз посчитать. Или я тебя не правильно понимаю?
      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        Ок, конкретизирую:

        В массиве Uk в элементе Uk[i] хранятся минимум и кол-во минимумов среди элементов проиндексированных (i + j * A)%B , 0 ≤ j < 2k .
        Uk вычисляется по Uk - 1, следующей операцией:
        пусть X и Y это Uk - 1, Z это Uk
        Z[i] = min (X[i], Y[(i + 2k)%B])
        Тут есть небольшой подкол со сдвигом, он каждый раз разный. Тем не менее, применив эту ассоциативную операцию ко всем Ui где бит i включён в показателе степени, мы получим что надо. 
        Я прошу кого-нибудь помочь с объяснением.
        • »
          »
          »
          »
          »
          12 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Это понятно все. Я имею ввиду вот что:
          можно хранить log max(M, K) массивов и за логарифм узнавать минимум на отрезке. Ты предлагаешь не хранить логарифм массивов, тогда ты будешь находить минимум не за логарифм, потому что возводишь ты весь массив.
          • »
            »
            »
            »
            »
            »
            12 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится
            Да, всё так. 
            Возводить надо лишь один раз, после этого у нас все нужные данные оказываются на руках, т.е. минимум по конкретному p берется за O(1).

            Весь массив возводится в степень N1 / A , а потом еще возможно что надо домножить на начальный, чтобы получить степень N1 / A.
            • »
              »
              »
              »
              »
              »
              »
              12 лет назад, # ^ |
                Проголосовать: нравится 0 Проголосовать: не нравится
              А круто, действительно, всего две степени будут: и 
»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Спасибо за разбор! Но я немного не понял как считать ответ для оставшихся N1 пар...
Если бы у  нас N1 делилось на НОК(A,B), то  в W  было бы НОД(A,B) орбит элементов через А, и в P было бы НОД(A,B) орбит элементов через В. При этом если пронумеровать орбиты по их первому элементу, то все элементы из орбиты i в P встречались бы в паре со всеми элементами из орбиты i в W.
Тогда берём минимум в первой, минимум во второй - и это пара кандидат на минимальную. Т.е. всего будет НОД(A,B) кандидатов.  А что делать если N1 не делится на НОК ? Я написал какой-то изврат: поочередно то в одном то в другом массиве выбираю минимальный не вычеркнутый элемент, просматриваю всех кто с ним в паре во втором массиве, запоминаю минимум и вычеркиваю все просмотренные. Потом с помощью Фенвика проверяю является ли эта пара кандидатом на ответ (Пока писал это предложение понял, ночью голова не варила... Зачем было ещё Фенвика прикручивать, если потом я всё равно буду проверять можно ли посчитать эту пару как хорошую). В общем в итоге я разбил всё на три части - найти пары для хвоста, для нока, и для N1 % НОК. Но после этого я как последний баран их пихал в массив на 30 лимонов из пар интов и сортил его)) В итоге оно сумело пройти всего 4 теста, примерно по минуте с лишним на тест. Можно поподробней как посчитать для N1 ответ не разбивая на НОК и остаток от НОКа за O(max(M, K) * logN)?
  • »
    »
    12 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +5 Проголосовать: не нравится

    Да, берём две соответствующие орбиты: пусть одна P (берутся элементы из первого цикла идущие через B и соотв. ей из W (берутся элементы из второго цикла через A).

    Возьмём циклический RMQ над W, и ещё массив частичных сумм последовательности из нулей и единиц, где единицы соответствуют элементам минимальным на орбите.
    Возьмём какой-нибудь элемент , с ним в парах встречаются либо N1 / A либо N1 / A элементов подряд из W. Если кол-во маленькое то используем RMQ, а если  оказался больше орбиты, то минимум берётся по всей орбите, а это значит что для вычисления можно использовать частичные суммы - считаем сколько раз орбита полностью влезла, и сколько минимумов в остатке.

    Вот поэтому я писал двоичный подъём.
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      >Возьмём какой-нибудь элемент , с ним в парах встречаются либо ⌊ N1 / A⌋ >либо ⌈N1 / A⌉ элементов подряд из W
      Вы имеете ввиду элементы подряд из соответствующей орбиты? И под подряд понимается циклически подряд?