2021.08.13 二次剩余
注:真难啊,心好累。
二次剩余Cipolla算法学习小记_待成熟的葡萄-CSDN博客
【二次剩余】Cipolla(模意义下开根)_HOWARLI的博客-CSDN博客
练习题:
P5491 【模板】二次剩余 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<time.h>
using namespace std;
typedef long long ll;
ll w,t,n,p;
struct node{
ll x,y;
};
node mul(node a,node b,ll p){
node ans={0,0};
ans.x=((a.x*b.x%p+a.y*b.y%p*w%p)+p)%p;//?
ans.y=((a.x*b.y%p+b.x*a.y%p)+p)%p;
return ans;
}
ll powreal(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1)ans=ans%p*a%p;
a=a%p*a%p;
b>>=1;
}
return ans;
}
ll powimagine(node a,ll b,ll p){
node ans={1,0};
while(b){
if(b&1)ans=mul(ans,a,p);
a=mul(a,a,p);
b>>=1;
}
return ans.x;
}
ll cipolla(ll n,ll p){
n%=p;
if(p==2)return n;
if(powreal(n,(p-1)/2,p)==p-1)return -1;//n不是膜p的二次剩余
ll a;
while(1){
a=rand()%p;
w=((a*a%p-n)%p+p)%p;
if(powreal(w,(p-1)/2,p)==p-1)break;//w不是膜p的二次剩余,满足条件
}
node ans={a,1};
return powimagine(ans,(p+1)/2,p);
}
int main(){
srand(time(0));
cin>>t;
while(t--){
cin>>n>>p;
if(!n){
cout<<"0"<<endl;
continue;
}
ll ans=cipolla(n,p);
if(ans==-1)cout<<"Hola!"<<endl;
else{
ll ansi=p-ans;
if(ans>ansi)swap(ans,ansi);
if(ans==ansi)cout<<ans<<endl;
else cout<<ans<<" "<<ansi<<endl;
}
}
return 0;
}