P5491 【模板】二次剩余 题解
SunsetSamsara · · 题解
二次剩余,是数论中的一大板块。
首先了解一下二次剩余的定义:
如果一个数
关于二次剩余的符号:
勒让德符号 (\dfrac a b)
当
当
当
欧拉判定
欧拉判定:
欧拉判定可以用于
Cippola
Cippola 是以一种特别玄学的方式求出一个数模意义下开的根。像 PollardRho 一样,通过随机的枚举来进行寻找。
证明完毕。
接下来,记答案为
又因为
所以,令
在这篇让人看不懂的题解的最后,附上看人品 AC 的代码:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define int long long
struct Complex{
int x,y;
Complex(int a=0,int b=0){x=a;y=b;}
};
int W;
int mod;
Complex operator+(const Complex &a,const Complex &b){return Complex((a.x+b.x)%mod,(a.y+b.y)%mod);}
Complex operator*(const Complex &a,const Complex &b){return Complex((a.x*b.x%mod+a.y*b.y%mod*W%mod)%mod,(a.x*b.y+a.y*b.x)%mod);}
int qpow(int a,int b,int p){
int ans=1,base=a;a%=p;if(a<0)a+=p;
for(;b;b>>=1){
if(b&1)ans=ans*base%p;
base=base*base%p;
}
return ans;
}
Complex qpow(Complex a,int b,int p){
mod=p;
Complex ans(1,0),base=a;
for(;b;b>>=1){
if(b&1)ans=ans*base;
base=base*base;
}
return ans;
}
Complex judge(int x,int p){return qpow(x,(p-1)>>1,p);}
void solve(){
int x,p;
scanf("%lld%lld",&x,&p);
int q=qpow(x,(p-1)>>1,p);
if(q==0){
puts("0");
return;
}
if(q==p-1){
puts("Hola!");
return;
}
int rd=rand()%p;
while(qpow(rd*rd-x+p,(p-1)>>1,p)!=p-1)rd=rand()%p;
Complex t(rd,1);W=(rd*rd-x+p)%p;
t=qpow(t,(p+1)>>1,p);
int ans1=(t.x+p)%p,ans2=(p-ans1)%p;
if(ans1==ans2)printf("%lld\n",ans1);
else if(ans1<ans2)printf("%lld %lld\n",ans1,ans2);
else printf("%lld %lld\n",ans2,ans1);
}
signed main(){
srand(time(0));
int T;
scanf("%lld",&T);
while(T--)solve();
}