题解 P5491 【【模板】二次剩余】
在写这道题前,我们需要了解一个方程。
NR and QR
若对于奇素数
而当
QR与NR的乘法法则
对于
性质一:
性质二:
性质三:
性质四:
性质与
因为篇幅有限,性质的证明请自行Google。或阅读 《数论概论》
legendre 符号
所以对于
欧拉准则
Cipolla 算法
我们可以找到一个整数
然后
具体证明自行Google.
实现代码
#include <cstdio>
#include <cstdlib>
#include <ctime>
struct complex {
long long x, y;
};
long long w;
complex mul(complex a, complex b, long long p) {
complex ans = {0, 0};
ans.x = (((a.x * b.x) % p + (a.y * b.y) % p * w % p) % p + p) % p;
ans.y = (((a.x * b.y) % p + (a.y * b.x) % p) % p + p) % p;
return ans;
}
long long complexPow(complex a, long long b, long long p) {
complex ans = {1, 0};
for (; b; b >>= 1) {
if (b & 1) ans = mul(ans, a, p);
a = mul(a, a, p);
}
return ans.x % p;
}
long long mypow(long long a, long long b, long long p) {
long long ans = 1;
for (; b; b >>= 1) {
if (b & 1)
ans = (ans * a) % p;
a = (a * a) % p;
}
return ans;
}
long long cipolla(long long n, long long p) {
n %= p;
if (p == 2)
return n;
if (mypow(n, (p - 1) / 2, p) == p - 1)
return -1;
long long a = 1;
while (1) {
a = rand() % p;
w = ((a * a % p - n) % p + p) % p;
if (mypow(w, (p - 1) / 2, p) == p - 1)
break;
}
complex x = {a, 1};
return complexPow(x, (p + 1) / 2, p);
}
int main() {
srand((unsigned)time(0));
int t;
long long n, p;
scanf("%d", &t);
while (t--) {
scanf("%lld %lld", &n, &p);
long long k1 = cipolla(n, p);
if (k1 == -1) {
printf("Hola!\n");
continue;
}
long long k2 = p - k1;
if (k1 > k2)
k1 ^= k2 ^= k1 ^= k2;
if (k1 == k2)
printf("%lld\n", k1);
else
printf("%lld %lld\n", k1, k2);
}
return 0;
}