二次离散对数有特殊解法(但3次及以上尚未发现),例如有
x2≡nmodp
x
2
≡
n
mod
p
首先方程有解仅
np−12≡1modp
n
p
−
1
2
≡
1
mod
p
记勒让德符号
(np)
(
n
p
)
若n为p的二次剩余(即可以在模p意义下开根号),则勒让德符号为1,反之为-1,倍数为0。
定理:若 a a 使不是模p二次剩余,则 x=(a+w−−√)p+12 x = ( a + w ) p + 1 2 是二次剩余方程 x2≡nmodp x 2 ≡ n mod p 的解。
w−−√ w 为虚数单元,定义复数 c=a+bw c = a + b w ,复数乘法和复数快速幂取模都成立,上式可求。ural 1132代码(疑似某种情况有问题,但是示例都是对的,找不出来):
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
ll QP(ll a, ll b, ll p)
{
ll tmp = 1;
a %= p;
while (b)
{
if (b & 1)tmp = tmp*a%p;
b >>= 1;
a = a*a%p;
}
return tmp;
}
ll le(ll a, ll b)
{
return QP(a, (b - 1) >> 1, b);
}
struct co
{
ll re, im;
};
ll w;
co mul(co c1, co c2,ll p)
{
co c3;
c3.re = (c1.re*c2.re%p + c1.im*c2.im%p*w%p)%p;
c3.im = (c1.re*c2.im%p + c1.im*c2.re%p)%p;
return c3;
}
co QPCO(co c, ll b, ll p)
{
co tmp;
tmp.re = 1; tmp.im = 0;
while (b)
{
if (b & 1)tmp = mul(tmp, c,p);
b >>= 1;
c = mul(c, c,p);
}
return tmp;
}
ll mod(ll a,ll p)
{
a%=p;
if(a<0)a+=p;
return a;
}
ll sl(ll n, ll p)
{
// w = 0;
if (p == 2)return 1;
if (le(n, p) + 1 == p)return -1;
ll a = -1,t;
while (1)
{
a=rand()%p;
//t=((a*a-n)%p+p)%p;
t=a*a-n;
w=mod(t,p);
ll l = le(t, p);
if (l + 1 == p)
{
//w=t;
break;
}
}
co x1;
x1.re = a; x1.im = 1;
return QPCO(x1, (p + 1) >> 1, p).re;
}
int main()
{
int times;
scanf("%d", ×);
for (int i = 0; i < times; i++)
{
ll n,p;
scanf("%d%d", &n, &p);
n %= p;
ll a = sl(n, p);
if (a == -1)
{
printf("No root\n");
continue;
}
else
{
ll b = p - a;
if (a == b)
{
printf("%lld\n", a);
continue;
}
if (a > b)std::swap(a, b);
printf("%lld %lld\n", a, b);
}
}
return 0;
}