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;
}