Обработка математики: 100%

Подразделы

Другие разделы

Дата и время

09/04/2025 07:20:57

Авторизация

Имя:
Пароль:
Зарегистрироваться
Восстановить пароль
 

printРазбор задачи B. Вундеркинд

Тема: модулярная арифметика, быстрое возведение в степень, периодические последовательности
Сложность: выше среднего

Правила модулярной арифметики:
(X+Y) mod M = ((X mod M) + (Y mod M)) mod M
(X-Y) mod M = ((X mod M) - (Y mod M) + M) mod M (в случае отрицательного результата нужно добавить к нему M)
(XY) mod M = ((X mod M)  (Y mod M)) mod M
XP mod M = (X mod M)P mod M
Т.е. если к аргументам операций сложения, вычитания, умножения и возведения в степень применить остаток от деления, то результат не изменится. Это позволяет выполнять вычисления, не прибегая к длинной арифметике, в задачах, где требуется найти остаток от деления результата на M.
Для быстрого возведения в степень нужно воспользоваться способом, основанном на двоичном представлении показателя степени. Подобный способ, но для умножения чисел, был придуман еще 4 тысячи лет назад в Древнем Египте.
Известный российский математик Я.И.Перельман в своей книге «Занимательная арифметика» описал русский народный способ умножения. Сущность его в том, что умножение двух чисел сводится к ряду последовательных делений одного числа пополам при одновременном удвоении другого числа. Например: нужно умножить 32 на 13. Это записывается таким образом:
3213
1626
852
4104
2208
1416
Деление пополам продолжают до тех пор, пока в частном не получится 1, параллельно удваивая другое число. Последнее удвоенное число и дает искомый результат. Способ основан на том, что произведение не изменяется, если один множитель уменьшить вдвое, а другой вдвое же увеличить. А в результате многократного повторения этой операции получается искомое произведение: 3213 = 1416.
Если приходится делить нечетное число пополам, то в этом случае от нечетного числа откидывается единица и остаток уже делится пополам; но зато к последнему числу правого столбца нужно будет прибавить все те числа этого столбца, которые стоят против нечетных чисел левого столбца: сумма и будет искомым произведением. Практически это делают так, что все строки с четными левыми числами зачеркивают; остаются только те, которые содержат налево нечетное число. Пример:
1917
934
468
2136
1272
Сложив незачеркнутые числа, получаем правильный результат: 17 + 34 + 272 = 323.
Обоснованность приема станет ясна, если принять во внимание, что
1917 = (18 + 1)17 = 1817 + 17
934 = (8 + 1)34 = 834 + 34, и так далее.
Ясно, что числа 17, 34 и т.п., утрачиваемые при делении нечетного числа пополам, необходимо прибавить к результату последнего умножения, чтобы получить произведение.
У некоторых английских авторов этот способ умножения описывается под названием «русский крестьянский способ». В Германии крестьяне также пользуются им и называют его «русским».
Не исключено, что этот способ дошел до нас из Египта. Одним из основных источников сведений о древнеегипетской математике является папирус, купленный в Луксоре шотландским антикваром Александром Генри Риндом, который сейчас хранится в Британском музее. Папирус был найден в металлическом футляре. В развернутом виде имеет 5.5 м длины при 32 см ширины. Он представляет собой собрание решений 84 задач, имеющих прикладной характер. Папирус относится примерно к 2000-1700 г. до н.э. и представляет собой копию еще более древней рукописи, переписанную писцом Ахмесом. В папирусе используется иератическая система обозначений чисел – условные знаки, которые произошли из иероглифов в результате упрощений и стилизации. В нем есть четыре примера умножения, выполненные по способу, живо напоминающему наш народный русский способ. Вот один из них:
12316.jpg
Что означает следующее:
  9 1 /
18 2
36 4
72 8 /
= 81
Косая черта справа от строки указывает на то, что левое число будет добавлено к результату. Из этого примера видно, что египтяне пользовались приемом умножения, довольно сходным с нашим народным.
При возведении в степень нужно так же делить показатель степени, но вместо удвоения нужно выполнять возведение в квадрат и перемножать квадраты, связанные с нечетными числами. Все операции при этом выполняем по модулю M.
function powModM(A:int64; P:longint):longint;
var R:int64;
begin
  R:=1;
  while p>0 do
  begin
    if P mod 2=1 then
      R:=(R*A) mod M;
    A:=(A*A) mod M;
    P:=P div 2;
  end;
  powModM:=R;
end;
Далее нужно найти N-е число в последовательности, и N может быть очень велико. Для устранения этой сложности воспользуемся принципом Дирихле или "принципом ящиков":
Если кролики рассажены в клетки, причём число кроликов больше числа клеток, то хотя бы в одной из клеток находится более одного кролика.
Все вычисления производятся по модулю M, поэтому Ai могут принимать только значения от 0 до M-1. Среди элементов последовательности с номерами от 0 до M существуют элементы Ai и Aj, такие что i > j и Ai = Aj, так как есть M+1 элемент и M возможных значений. Следующее значение в последовательности зависит только от предыдущего, поэтому  k0 выполняется Ai+k=Aj+k. Т.е. после некоторого j последовательность будет повторяться с периодом равным i-j.
Чтобы найти i и j, можно отмечать в специальном массиве, на каком шаге было получено то или иное значение. Затем нужно убрать из N все периоды, повторяющиеся целое число раз, для этого нужно найти остаток от деления N-j на длину периода i-j.
var i,j,A0,B,P,N,M:longint;
    step,A:array[0..100000] of longint;
...
  for i:=0 to M-1 do
    step[i]:=-1;
  i:=0;
  A[0]:=A0 mod M;
  while (i<N) and (step[A[i]]<0) do
  begin
    step[A[i]]:=i;
    A[i+1]:=(powModM(A[i],P)+B) mod M;
    inc(i);
  end;
  if i<N then
  begin
    j:=step[A[i]];
    i:=(N-j) mod (i-j) + j;
  end;
  writeln(A[i]);
Так как нас интересует только длина периода, то можно не искать начало периодической части, а найти такое j, для которого Aj=AM. Длина периода будет равна M-j.
  A[0]:=A0 mod M;
  for i:=1 to M do
    A[i]:=(powModM(A[i-1],P)+B) mod M;
  if N<=M then
    writeln(A[N])
  else
  begin
    j:=M-1;
    while A[j]<>A[M] do
      dec(j);
    writeln(A[(N-j) mod (M-j) + j]);
  end;
loading