我们定义n==x*xmod p 中如果有非零解称n为p的二次剩余,不过并非所有数都有二次剩余,在此我们只讨论p为奇素数的情况,我们引用l勒让德符号,(a/p)={1,有二次剩余,0,amodp==0,-1,无二次剩余}。
那么如何求解二次剩余的解呢,我们有欧拉准则(a/p)=a的(p-1)/2次这个证明很简单,费马小定理可以看出。
我们使用Cipolla算法去解p的二次剩余
先找到a满足(a*a-n)的(p-1)/2次满足二次非剩余,然后定义i*i=a*a-n,代入即n=(a+i)的(p+1)/2次。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<set>
#include<string.h>
#include<vector>
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
#define int long long
int qpow(int a,int b,int p){
int ans=1;
a%=p;
while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int mod(int a,int p){
return (a%p+p)%p;
}
namesapce Qresidue
int legendre(int a,int p){
return qpow(mod(a,p),(p-1)/2,p);
}
int find_a(int n,int p){
for(int a=0;a<p;++a){
if(lengendre(a*a-n,p)==p-1)return a;
}
return -1;
}
struct expnum{
int a,b;
};
int a,p,n;
expnum mul(expnum i1,expnum i2){
int c=a*a-n;
return {(i1.a*i2.a+i1.b*i2.b%p*c)%p,(i1.a*i2.b+i1.b*i2.a)%p};
}
expnum qpow(expnum a,int n){
expnum ans{1,0};
while(n){
if(n&1)ans=mul(ans,a);
a=mul(a,a);
n>>=1;
}
return ans;
}
int cul(int n,int p){
if(n%p==0)return 0;
if(legendre(n,p)!=1)return -1;
Qresidue::n=n,Qresidue::p=p;
a=find_a(n,p);
return mod(qpow(expnum{a,1},(p+1)/2).a,p));
}
int main(){
cin>>a>>p;
cout<<cul(a,p)<<endl;
return 0;
}