はまやんはまやんはまやん

hamayanhamayan's blog

夏休みの思い出(2) [yukicoder No.551]

https://yukicoder.me/problems/no/551

前提知識

解説

https://yukicoder.me/submissions/193014

2次方程式を平方完成する。

Ax^2+Bx+C=0
A(x^2+BA^-1x)+C=0
A(x^2+BA^-1x+B^2(4A^2)^-1-B^2(4A^2)^-1)+C=0
A(x + B(2A)^-1)^2 - B^2(4A)^-1+C=0
A(x + B(2A)^-1)^2 = B^2(4A)^-1-C
(x + B(2A)^-1)^2 = (B^2(4A)^-1-C)/A
 
ここで
a = x + B(2A)^-1
b = (B^2(4A)^-1-C)/A
と置くと

a^2 = b (mod P)

これは平方剰余と呼ばれる問題になる。
この合同方程式が解ければ、x = a - B(2A)^-1 で答えが求まるので、これを計算することを考える。
 
まず、解が存在するかであるが、これはオイラーの基準という式でルジャンドル関数を作って判定する。
ルジャンドル関数が1を返せば解があり(平方剰余)、-1を返せば解が無い(平方非剰余)
これを使って、解が無いならば、"-1"を吐いて終了
 
あとは、実際に問題を解くが、色々なアルゴリズムがある。
自分の解法ではpekempeyさんのCipolla's algorithm実装をほぼそのまま使っている。
出た答えをxに戻して答えると答えとなる。

legendre(int b, int P) := a^2 = b(mod P)についてのルジャンドル関数
modsqrt(int b, int P) := a^2 = b(mod P)を満たすaを返す

int P, R, Q, A, B, C;
//---------------------------------------------------------------------------------------------------
int f() { return mul(sub(mul(B, B, modinv(mul(4, A))), C),modinv(A)); }
int rev(int x) { return sub(x, mul(B, modinv(mul(2, A)))); }
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> P >> R >> Q;
    mod = P;

    rep(_, 0, Q) {
        cin >> A >> B >> C;

        int b = f();
        if (legendre(b, P) < 0) {
            printf("-1\n");
            continue;
        }

        int a = modsqrt(b, P);
        if (a == 0) printf("%d\n", rev(0));
        else {
            int aa = P - a;
            a = rev(a), aa = rev(aa);
            if (a > aa) swap(a, aa);
            printf("%d %d\n", a, aa);
        }
    }
}