Динамическое программирование перестановки

创建于:2024年11月21日

使用 OpenAI 01 Preview 回答 OpenAI01

问题

Решить с помощью динамического программирования
Условия
Как-то один из преподавателей решил вспомнить задачу из своих лучших лет, пытаясь распарсить код который написал. Теперь он хочет, чтоб вы решили задачу.

Дана перестановка длины N, перестановка называется красивой, если выполняется условие, что есть индексы
i
i и
i
+
1
i+1, такие что
a
i
=
a
i
+
1
+
1
a
i

=a
i+1

+1.

Вам нужно узнать количество красивых перестановок по модулю
M
M.


Входные данные
Из первой строки стандартного ввода прочитайте два целых числа
1

N

1
0
18
1≤N≤10
18
и
1

M

1
0
7
1≤M≤10
7
- длину последовательностей и модуль, используемый для расчета.


Выходные данные
Вы должны вывести количество красивых перестановок длины
N
N по модулю
M
M.


Система оценки
Подзадача 0 (0 баллов) тесты из условия.

Подзадача 1 (9 баллов)
N

10
N≤10.

Подзадача 2 (14 баллов)
N

15
N≤15. Необходимые подгруппы: 1.

Подзадача 3 (11 баллов)
N

20
N≤20. Необходимые подгруппы: 2.

Подзадача 4 (16 баллов)
N

1
0
3
N≤10
3
. Необходимые подгруппы: 3.

Подзадача 5 (20 баллов)
N

1
0
6
N≤10
6
. Необходимые подгруппы: 4.

Подзадача 6 (30 баллов)
N

1
0
18
N≤10
18
. Необходимые подгруппы: 5.

Примеры данных
Входные данные
1
4 42
2
2000 10009
Выходные данные
1
13
2
1295

思考

Ищу совпадения

Я разбираюсь в задаче подсчета "красивых перестановок" длиной N, где элементы идут как a_i = a_{i+1} + 1.

Ищу совпадения

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

Ищу уникальности

Итак, я изучаю общее количество перестановок длины N, учитывая, что N! не вычисляется из-за больших значений. Теперь я исследую уникальные перестановки, где не встречаются соседние элементы с разностью 1.

Графи и перестановки

Итак, я строю граф, где каждый элемент {1, 2, ..., N} — вершина, а ребра соединяют соседние элементы, разности между которыми равны 1. Это позволяет исследовать перестановки, избегающие соседних элементов, имеющих разность 1.

Факт oписание цьєї задачі заставляє задуматися. Интересные методы, такие как анализируют перестановки и применяются принцип включения-исключения.

Проверка крайних случаев

Даюем перечень всех возможных перестановок для N=4 и подсчитываю, сколько из них не содержат соседние элементы, отличающиеся на 1.

Использую динамическое программирование
Я определяю dp[n] - количество перестановок n элементов без соседних элементов, отличающихся на 1. Пытаюсь найти рекуррентную связь для dp[n].

Ищу допустимые комбинации

Для n=2 и n=3 все перестановки имеют соседние элементы, отличающиеся на 1, так что dp[2] и dp[3] равны 0. Интересно, что dp[4] равно 11.

Ищу варианты.

Оцениваю флорстальные изменения

Изучаю перестановки с изменениями. Просто исключаю те, где рядом элементы отличаются на 1. Интересно, пара перестановок на виду, но это противоречит прежним выводам.

Определяю количество красивых перестановок

Я вычисляю количество красивых перестановок для n=4 и замечаю несоответствие с ожидаемыми 13 значениями, что указывает на необходимость пересчитать или уточнить результаты.

Уточняю условия

Я разбираюсь с рядом перестановок, исключая те, где соседние элементы отличаются на 1. Пока ни одной перестановки не нахожу, которая подходит.

Исследование последовательности

Я смотрю на последовательность A002524 в OEIS и замечаю, что для n>=5 числа равны нулю, но для n=4 их 2. Похоже, что здесь могло быть какое-то недоразумение.

Определяю разницу

条件には、インデックスiとi+1があり、a_i = a_{i+1} + 1という関係があります。隣接する要素の差は正確に+1と見なされています。

Проверяю пары

Я разбираюся с перестановками для N=2 и N=3, чтобы понять, какие пары соседних чисел не соответствуют условию a_i - a_{i+1} = 1.

Ищу связи

Проверяю последовательности чисел для N=4, чтобы убедиться, что никакие соседние числа не отличаются на 1. Интересно, что найдено 11 возможных перестановок.

Проверка формулы

В этом отрывке приведены примеры вычислений и прогрессии последовательности dp[n], что позволяет предположить, что для больших n формула dp[N] может быть сложной и требовать анализа.

Ищу закономерности

Сначала я разбираюсь с dp[n] = (n-1)*(dp[n-1] + dp[n-2]), но замечаю несоответствие. Затем рассматриваю dp[n] = (n-1)*dp[n-1]+(n-2)*dp[n-2]. Это заставляет меня задуматься о новых подходах.

Проверяю соответствия

Проверяю соответствия dp значениями, используя рекуррентные формулы. Интересно, как это показывает закономерности в числовых рядах.

Ищу приближение

Предполагаю, что для больших N, dp[n] гораздо меньше N! и B можно приближенно оценить как N! мод M, при условии, что M < N!.

Учитываю малость

Для N ≥ 4, dp[n] становится несущественным по сравнению с N!, а для N ≥ 10, dp[n] меньше N!/e^n. Поэтому B оказывается равным Н! мод M.

Изучаю возможности

Сейчас я рассматриваю использование Лежандра и теоремы Лукаса для вычисления факториалов больших чисел по модулю малого числа, что делает задачу выполнимой.

Исследование значения dp

Я замечаю, что dp[N] быстро растет, и задумываюсь о его периодическом поведении при взятии по модулю M.

Рассматриваю циклические предсказания

Я разбираюсь в вычислении dp[n] по модулю M, чтобы найти B. Важно заметить, что для N>=M, N! по модулю M даёт 0, что упрощает выражение для B.

Использую матрицы

Я думаю о применении матричной экспоненциации для вычисления dp[N] по модулю M, рассматривая его как функцию dp[n-1] и dp[n-2].

Размышляю о вычислениях

Для N >= M, N! мод M = 0, так как M делит N! Следовательно, B ≡ -dp[N] mod M. Для N < M, N! мод M ≠ 0, и dp[N] мод M можно вычислить эффективно.

Ищу повторения

Изучаю dp[n] мод M, т.к. n >= M, n mod M повторяется каждые M. Предлагаю предварительно вычислить dp[n] мод M для n от 1 до 2*M.

Обсчитываю числа

Я вычисляю dp[n] modulo M для всех n от 1 до 84, чтобы выявить закономерности и циклы в последовательности.

Проверяю циклы

Я думаю о проверке, повторяется ли dp[n] modulo M с периодом M, генерируя значения dp[n] для n от 1 до M+K и подозревая циклы.

Предварительно вычисляю dp[n] для n от 1 до 2*M, проверяю повторения, вычисляю для M в пределах 1e7.

Определяю периодичность

Я вычисляю dp[n] modulo M для n от 1 до 20 и вижу, что периодичность составляет 10. Это наводит на мысль о более широкой и практической стороне задачи.

Применяю предвычисления

Я подхожу к решению задачи, используя пролонгированное вычисление dp[n] по модулю M. Интересно, как это может помочь в установлении периода.

Ищу более простое выражение

Пытаюсь упростить выражение dp[n], предлагая известную формулу. Интересно, но это всё равно сложно.

Применяю рекуррентный метод

Ищу явную формулу или оптимизацию рекуррентного выражения для m или n, используя периодические свойства и последовательное повторение коэффициентов.

Ищу другие подходы

Я размышляю о различных методах решения задачи с учетом больших значений N и M. Оказывается, что dp[n] и dp[n] modulo M имеют периодичность, и это может привести к интересным результатам.

Ищу пути

Я изучаю вычисление dp[n] ярдов изменений и замечаю, что, возможно, dp[n] - это количество перестановок без фиксированных точек, типа подфакториалов.

Ищу закономерности

Oказывается, что количество перестановок, где соседние элементы не отличаются на 1, похоже на число деривирований. Возможно, операция dp[n] используется для нахождения количества таких перестановок.

Ищу закономерности

Я исследую, как dp[n] связано с числами деривирований или перестановок без соседних элементов, равных единице. Это поднимает вопрос о возможных циклах и периодах в вычислениях.

Ищу способ вычислений

Я используем рекуррентные соотношения и матричную экспоненциацию, чтобы вычислить dp[N] modulo M.

Соединяю концепции

Я подмечаю, что dp[n] ≈ n! / e, и имею ввиду dk[N] modulo M ≈ 0 при N ≥ M.

Понимаю основной концепт

Суммирование знаковых члены ряда Тейлора показывает интересные закономерности и симметрию, что ведет к приближению экспониментального значения e.

Ищу оптимальные значения

Я смотрю на вычисление dp[N] modulo M для больших N, используя примерные числа или числа с делением на M.

Ищу расчеты

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

проанализировать проблему

Размышляю над условиями

Ищу возможные условия для красивой перестановки, упоминаю инверсии и увеличение. Оцениваю, как это влияет на общее количество перестановок.

Анализ различных подходов

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

回答

Данная задача требует вычисления количества перестановок длины NN, в которых существует по крайней мере одна пара соседних элементов (ai,ai+1)(a_i, a_{i+1}), удовлетворяющая условию ai=ai+1+1a_i = a_{i+1} + 1.

Это эквивалентно вычислению количества перестановок, в которых нет такой пары, и затем вычитанию этого количества из общего числа перестановок (N!N!).

Пусть dpndp_n — количество перестановок длины nn, в которых нет пар соседних элементов, удовлетворяющих ai=ai+1+1a_i = a_{i+1} + 1. Можно установить следующее рекуррентное соотношение для dpndp_n:

dp1=1,dp2=1,dpn=(n1)×dpn1+(n2)×dpn2dp_1 = 1, \quad dp_2 = 1, \quad dp_n = (n - 1) \times dp_{n - 1} + (n - 2) \times dp_{n - 2}

Но вычисление dpndp_n напрямую неэффективно при больших NN, особенно учитывая модуль MM. Однако можно заметить, что dpnmodMdp_n \mod M начинает повторяться с некоторым периодом, поскольку коэффициенты в рекуррентном соотношении повторяются по модулю MM.

Таким образом, алгоритм будет следующим:

  1. Предварительное вычисление периода:

    • Вычисляем значения dpnmodMdp_n \mod M для nn от 11 до 2M2M или до тех пор, пока не обнаружится период повторения.
    • Фиксируем период PP, при котором последовательность dpnmodMdp_n \mod M начинает повторяться.
  2. Вычисление dpNmodMdp_N \mod M:

    • Если NPN \geq P, то используем периодичность: dpNmodM=dpNmodPmodMdp_N \mod M = dp_{N \mod P} \mod M.
    • Иначе, используем прямое вычисление по рекуррентному соотношению.
  3. Вычисление общего количества красивых перестановок:

    • Если NMN \geq M, то N!modM=0N! \mod M = 0 (так как MM будет делителем N!N!).
    • В этом случае количество красивых перестановок будет равно (dpN)modM(-dp_N) \mod M.
    • Если N<MN < M, то вычисляем N!modMN! \mod M напрямую и получаем количество красивых перестановок как (N!dpN)modM(N! - dp_N) \mod M.

Ниже приведен код, реализующий этот алгоритм:

MOD = int(1e7+10) import sys import threading def main(): import sys sys.setrecursionlimit(1 << 25) N_str, M_str = sys.stdin.read().split() N = int(N_str) M = int(M_str) if N == 1: print(0) return dp = [] dp.append(0) # dp[0], not used dp.append(1) # dp[1] dp.append(1) # dp[2] max_period = M * 2 period = None state_dict = {} for n in range(3, max_period+1): dp_n = ((n-1)%M)*dp[-1]%M + ((n-2)%M)*dp[-2]%M dp_n %= M dp.append(dp_n) state = (dp[-1], dp[-2], (n-1)%M, (n-2)%M) if state in state_dict: period_start = state_dict[state] period = n - period_start break else: state_dict[state] = n if period is None: period_start = 1 period = len(dp)-1 if N < len(dp): dp_N = dp[N] else: index = (N - period_start)%period + period_start dp_N = dp[index] if N >= M: print((-dp_N)%M) else: # Compute N! % M N_factorial = 1 for i in range(1, N+1): N_factorial = N_factorial * i % M B = (N_factorial - dp_N)%M print(B) threading.Thread(target=main).start()

分享这个问答