二次剩余形式:
x
2
≡
n
m
o
d
p
x^2\equiv n\ mod\ p
x2≡n mod p,给定n,p,求解x?
这里我们只谈论p为奇素数。p为2时,答案不是0就是1,这点在下面代码有体现(代码是照搬的)。
简明扼要:首先先判断勒让德符号(Legendre symbol)
(
a
p
)
=
a
p
−
1
2
(\frac{a}{p})=a^{\frac{p-1}{2}}
(pa)=a2p−1。
此时有三种情况:
(
n
p
)
=
{
1
,
a
在
模
p
是
二
次
剩
余
−
1
,
a
在
模
p
不
是
二
次
剩
余
0
,
a
=
0
(\frac{n}{p})=\left\{\begin{matrix} 1,a在模p是二次剩余\\ -1,a在模p不是二次剩余\\ 0,a=0 \end{matrix}\right.
(pn)=⎩⎨⎧1,a在模p是二次剩余−1,a在模p不是二次剩余0,a=0
有解的条件为:
(
n
p
)
=
n
p
−
1
2
=
1
(\frac{n}{p})=n^{\frac{p-1}{2}}=1
(pn)=n2p−1=1
那么我们先找到一个a,满足
w
=
a
2
−
n
w=a^2-n
w=a2−n不是模p的二次剩余,即
x
2
≡
w
m
o
d
p
x^2\equiv w\ mod\ p
x2≡w mod p无解,那么
x
≡
(
a
+
w
)
p
+
1
2
x\equiv(a+w)^{\frac{p+1}{2}}
x≡(a+w)2p+1即为解,且这解有两个数,它们互为相反数。
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
using namespace std;
typedef long long LL;
LL quick_mod(LL a, LL b, LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
struct T
{
LL p, d;
};
LL w;
//二次域乘法
T multi_er(T a, T b, LL m)
{
T ans;
ans.p = (a.p * b.p % m + a.d * b.d % m * w % m) % m;
ans.d = (a.p * b.d % m + a.d * b.p % m) % m;
return ans;
}
//二次域上快速幂
T power(T a, LL b, LL m)
{
T ans;
ans.p = 1;
ans.d = 0;
while(b)
{
if(b & 1)
{
ans = multi_er(ans, a, m);
b--;
}
b >>= 1;
a = multi_er(a, a, m);
}
return ans;
}
//求勒让德符号
LL Legendre(LL a, LL p)
{
return quick_mod(a, (p-1)>>1, p);
}
LL mod(LL a, LL m)
{
a %= m;
if(a < 0) a += m;
return a;
}
LL Solve(LL n,LL p)
{
if(n==0) return 0;
if(p == 2) return 1;
if (Legendre(n, p) + 1 == p)
return -1;
LL a = -1, t;
while(true)
{
a = rand() % p;
t = a * a - n;
w = mod(t, p);
if(Legendre(w, p) + 1 == p) break;
}
T tmp;
tmp.p = a;
tmp.d = 1;
T ans = power(tmp, (p + 1)>>1, p);
return ans.p;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, p;
scanf("%d %d",&n,&p);
n %= p;
int a = Solve(n, p);
if(a == -1)
{
puts("No root");
continue;
}
int b = p - a;
if(a > b) swap(a, b);
if(a == b)
printf("%d\n",a);
else
printf("%d %d\n",a,b);
}
return 0;
}
应用:我们可以用二次剩余求解一元二次同余方程。
a
x
2
+
b
x
+
c
≡
0
(
m
o
d
p
)
(
a
≠
0
&
&
g
c
d
(
a
,
q
)
≠
q
&
&
g
c
d
(
b
,
q
)
≠
q
)
ax^2+bx+c\equiv0(mod\ p)\ \ (a\neq0 \&\&gcd(a,q)\neq q\&\&gcd(b,q)\neq q)
ax2+bx+c≡0(mod p) (a̸=0&&gcd(a,q)̸=q&&gcd(b,q)̸=q)
那么上式就等价为:
(
2
a
x
+
b
)
2
≡
(
b
2
−
4
a
c
)
(
m
o
d
p
)
(2ax+b)^2\equiv(b^2-4ac)(mod\ p)
(2ax+b)2≡(b2−4ac)(mod p)
题目:
题意:给出
b
,
c
,
p
=
1
e
9
+
7
b,c,p=1e9+7
b,c,p=1e9+7,找出一对
x
,
y
,
满
足
0
≤
x
≤
y
<
p
(
x
+
y
)
m
o
d
p
=
b
(
x
∗
y
)
m
o
d
p
=
c
x,y,满足0\leq x\leq y< p\\ (x+y)mod\ p=b\\(x*y)mod\ p=c
x,y,满足0≤x≤y<p(x+y)mod p=b(x∗y)mod p=c,如果找不到,输出-1,-1
题解:我们观察第一个式子,且x,y的取值范围,我们可以得到:
x
=
b
−
y
∣
∣
x
=
b
+
p
−
y
x=b-y||x=b+p-y
x=b−y∣∣x=b+p−y,我们分别代入第二条式子,就是我们熟悉的二次剩余了,题目说了唯一解,那么我们只要找到一个就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL quick_mod(LL a, LL b, LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
struct T
{
LL p, d;
};
LL w;
//二次域乘法
T multi_er(T a, T b, LL m)
{
T ans;
ans.p = (a.p * b.p % m + a.d * b.d % m * w % m) % m;
ans.d = (a.p * b.d % m + a.d * b.p % m) % m;
return ans;
}
//二次域上快速幂
T power(T a, LL b, LL m)
{
T ans;
ans.p = 1;
ans.d = 0;
while(b)
{
if(b & 1)
{
ans = multi_er(ans, a, m);
b--;
}
b >>= 1;
a = multi_er(a, a, m);
}
return ans;
}
//求勒让德符号
LL Legendre(LL a, LL p)
{
return quick_mod(a, (p-1)>>1, p);
}
LL mod(LL a, LL m)
{
a %= m;
if(a < 0) a += m;
return a;
}
LL Solve(LL n,LL p)
{
// printf("n=%lld p=%lld\n",n,p);
if(n==0) return 0;
if(p == 2) return 1;
if (Legendre(n, p) + 1 == p)
return -1;
LL a = -1, t;
while(true)
{
a = rand() % p;
t = a * a - n;
w = mod(t, p);
if(Legendre(w, p) + 1 == p) break;
}
T tmp;
tmp.p = a;
tmp.d = 1;
T ans = power(tmp, (p + 1)>>1, p);
return ans.p;
}
bool so(LL b,LL c,LL b_)
{
LL p=1000000007;
// int n, p;
// scanf("%d %d",&n,&p);
// n %= p;
LL a = Solve(b*b-4*c, p);
bool flag=1;
if(a != -1)
{
LL y=(a-b)/(-2);
LL x=b-y;
if((x+y)%p==b_&&(x*y)%p==c){
flag=0;
if(x>y) swap(x,y);
printf("%lld %lld\n",x,y);return 1;
}
y=(p-a-b)/-2;
x=b-y;
if((x+y)%p==b_&&(x*y)%p==c){
flag=0;
if(x>y) swap(x,y);
printf("%lld %lld\n",x,y);return 1;
}
// puts("No root");
}
return 0;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
LL b,c;
scanf("%lld%lld",&b,&c);LL b_=b;
if(so(b,c,b_)) continue;
b+=1000000007;
if(so(b,c,b_)) continue;
else puts("-1 -1");
}
return 0;
}