Поле за модулем#12317

Поле за модулем (Розбір)

Число \sqrt{c} ірраціональне, тому обчислювати вираз у дійсних числах немає сенсу: при до 10^{18} це неможливо через точність. Зауважимо, однак, що будь-який цілий степінь числа a + b\sqrt{c} знову має вигляд x + y\sqrt{c}, оскільки \sqrt{c} \cdot \sqrt{c} = c. Таким чином, обчислення слід виконувати в розширеному полі:

Z_p (\sqrt{c}) = \{ x + y \sqrt{c}~|~x,y \in Z_p\}

Правила операцій:

(x_1 + y_1 \sqrt{c}) + (x_2 + y_2 \sqrt{c}) = (x_1 + x_2 ) + (y_1 + y_2) \sqrt{c}
(x_1 + y_1 \sqrt{c}) \cdot (x_2 + y_2 \sqrt{c}) = (x_1 x_2 + cy_1 y_2) + (x_1 y_2 + x_2 y_1 ) \sqrt{c}

Після визначення операції множення елементи поля Z_p (\sqrt{c}) можна підносити до степеня стандартним методом швидкого піднесення до степеня за час O(log_2n). Це дозволить обчислити (a + b \sqrt{c})^n~mod~p.

Приклад

У першому прикладі

(2 + \sqrt{5})^2~mod~17 = (4 + 4 \sqrt{5} + 5)~mod~17 = 9 + 4 \sqrt{5}

Розглянемо другий приклад:

(3 + 2 \sqrt{5})^{10}~mod~17

Нехай x = 3 + 2 \sqrt{5}. Обчислимо його степені:

x^2 = (3 + 2 \sqrt{5})^2~mod~17 = (9 + 12 \sqrt{5} + 20)~mod~17 = 12 + 12 \sqrt{5} \\ x^4 = (12 + 12 \sqrt{5})^2~mod~17 = (144 + 288 \sqrt{5} + 720)~mod~17 = 14 + 16 \sqrt{5} \\ x^8 = (14 + 16 \sqrt{5})^2~mod~17 = (196 + 448 \sqrt{5} + 1280)~mod~17 = 14 + 6 \sqrt{5}

Тепер можна обчислити результат:

x^{10} = x^8 \cdot x^2 = (14 + 6 \sqrt{5})(12 + 12 \sqrt{5})~mod~17 = \\ (168 + 72 \sqrt{5} + 168 \sqrt{5} + 360)~mod~17 = 1 + 2 \sqrt{5}

Реалізація алгоритму

Оголосимо структуру для зберігання числа вигляду x + y \sqrt{c}.

struct Num
{
  long long x, y; // x + y*sqrt(c)
};

Реалізуємо множення в полі Z_p (\sqrt{c}). Функція mult повертає добуток чисел і .

Num mult(Num &a, Num &b)
{
  Num res;
  res.x = (a.x * b.x + c * a.y % p * b.y) % p;
  res.y = (a.x * b.y + a.y * b.x) % p;
  return res;
}

Функція pow реалізує піднесення до степеня base^n, де — число в полі Z_p (\sqrt{c}).

Num pow(Num base, long long n)
{
  Num res = { 1, 0 }; // одиниця поля
  while (n > 0)
  {
    if (n & 1)
      res = mult(res, base);
    base = mult(base, base);
    n >>= 1;
  }
  return res;
}

Основна частина програми. Читаємо вхідні дані.

scanf("%lld %lld %lld %lld %lld", &a, &b, &c, &n, &p);

Ініціалізуємо початкове число start = a + b \sqrt{c}.

Num start = { a % p, b % p };

Обчислюємо та виводимо відповідь ans = start^n~mod~p = (a + b \sqrt{c})^n~mod~p.

Num ans = pow(start, n);
printf("%lld %lld\n", ans.x, ans.y);