二次剩余
二次剩余
定义
对于任意正整数
用途
求模意义下开根
解法
首先必须满足
引理一:
对于同余方程
引理二:
定义勒让德记号
则
证明:
若
若
若
定理:
设
注意 这里的
证明 :
由引理二可知
由费马小定理可知
所以
所以
因此 由引理一 我们每次在
P5491 【模板】二次剩余
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll w;
struct num{
ll x,y;
};
num mul(num a,num b,ll p)
{
num 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;
}
ll powwR(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1)ans=1ll*ans%p*a%p;
a=a%p*a%p;
b>>=1;
}
return ans%p;
}
ll powwi(num a,ll b,ll p){
num ans={1,0};
while(b){
if(b&1)ans=mul(ans,a,p);
a=mul(a,a,p);
b>>=1;
}
return ans.x%p;
}
ll solve(ll n,ll p)
{
n%=p;
if(p==2)return n;
if(powwR(n,(p-1)/2,p)==p-1)return -1;//不存在
ll a;
while(1)
{
a=rand()%p;
w=((a*a%p-n)%p+p)%p;
if(powwR(w,(p-1)/2,p)==p-1)break;
}
num x={a,1};
return powwi(x,(p+1)/2,p);
}
int main()
{
srand(time(0));
int t;
scanf("%d",&t);
while(t--)
{
ll n,p;
scanf("%lld%lld",&n,&p);
if(!n){
printf("0\n");continue;
}
ll ans1=solve(n,p),ans2;
if(ans1==-1)printf("Hola!\n");
else
{
ans2=p-ans1;
if(ans1>ans2)swap(ans1,ans2);
if(ans1==ans2)printf("%lld\n",ans1);
else printf("%lld %lld\n",ans1,ans2);
}
}
}