二次剩余
- x2≡n (mod p),p是奇素数。
- 如果存在一个n满足以上方程有解,那么就称n是p的一个二次剩余。
判断
- 勒让德符号(pn)=n2p−1
- (pn)=1:是二次剩余。因为如果是二次剩余的话,存在n=n21,那么就有np−1≡1(mod p),即n2p−1≡1(mod p)
- (pn)=−1:不是1那不就只能是-1了吗。无二次剩余相当于是对于任意一个i都存在一个j使得ij=n(mod p),i!=j,那么(p−1)!=n2p−1(将i,j两两组再一起),又有(p−1)!=−1(除了1和p-1以外两两相乘为1),所以上式成立。
- (pn)=0:p∣n
开根号
- 上面的判断并不是重点,关键是怎么求出来它的一个解。
- 首先在[0..p−1]随机找到一个a,令w=a2−n,要求(pw)=−1,解就是(a+w)2p+1
- 因为只有一半的w是二次剩余,所以有21的概率随机到,期望2次即可。
- 证明:
- 首先根据二项式定理可以得到(a+w)p=ap+wp (mod p)
- 因为w2p−1=−1,所以wp−1=−1,所以wp=−w。
- 还有ap=a (mod p)
- 所以(a+w)p=ap+wp=a−w (mod p)
- 那么(a+w)p+1=(a+w)(a−w)=a2−w=n
- 所以如果存在解的话,那么这个解就是(a+w)2p+1
- 需要注意的是w实际上在mod p意义下是没有值的,但是因为这个解是存在的,所以只需要带入一个(a+bw)进去快速幂,最后的w是会被消掉的。
luogu5491
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll long long
#define ull unsigned long long
using namespace std;
ll T,n,p;
ull sd;
ll rd(){
sd^=sd>>7,sd^=sd<<19,sd^=sd>>29;
return sd>>1;
}
ll ksm(ll x,ll y){
ll s=1;
for(;y;y/=2,x=x*x%p) if (y&1)
s=s*x%p;
return s;
}
ll W;
struct num{ll a,b;num(ll _a=0,ll _b=0){a=_a,b=_b;}};
num operator*(num x,num y){return num((x.a*y.a+x.b*y.b%p*W)%p,(x.a*y.b+x.b*y.a)%p);}
ll Ksm(ll a,ll y){
num s(1,0),x(a,1);
for(;y;y/=2,x=x*x) if (y&1)
s=s*x;
return s.a;
}
int main(){
scanf("%lld",&T);
sd=19260817;
while (T--){
scanf("%lld%lld",&n,&p);
ll tp=ksm(n,(p-1)/2);
if (tp==1){
ll a=rd()%p,w=(a*a-n+p)%p;
while (ksm(w,(p-1)/2)==1) a=rd()%p,w=(a*a-n+p)%p;
W=w; ll x=Ksm(a,(p+1)/2);
x=min(x,p-x);
printf("%lld %lld
",x,p-x);
} else
if (tp==p-1) printf("Hola!
");
else printf("0
");
}
}