给个传送门
听vectorxj大神讲过二次剩余
这题的第一反应就是求出5的二次剩余,看看能不能解方程。
设
x=5√
,
y=1+x2
那么 1x(yn−(1−y)n)≡C(modP)
分类讨论一下n的就行,那么解一下一元二次方程就能求出 yn
然后一直没想到有BSGS这种算法…
套BSGS就可以求出n了…
二次剩余可以看这个
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
typedef long long ll;
inline ll Pow0(ll x,ll y,ll P){
x%=P; ll ret=1;
for(;y;y>>=1,x=x*x%P) if(y&1) ret=ret*x%P;
return ret;
}
namespace BSGS{
map<ll,ll> M;
ll calc(ll x,ll y,ll p,bool parity){
M.clear(); parity=!parity;
ll S=ceil(sqrt((double)p));
ll cur=1;
for(int i=0;i<S;i++){
if(!M.count(cur)) M[cur]=i;
cur=cur*x%p;
}
for(int i=0;i<S;i++){
ll cur=y*Pow0(Pow0(x,i*S,p),p-2,p)%p;
if(M.count(cur) && (((i*S+M[cur])&1)^parity)) return i*S+M[cur];
}
return -1;
}
}
namespace Cipolla{
ll w,P;
struct E{
ll x,y;
E(ll _x,ll _y):x(_x),y(_y){}
friend E operator *(E a,E b){
return E((a.x*b.x%P+a.y*b.y%P*w)%P,(a.x*b.y%P+a.y*b.x%P)%P);
}
};
inline E Pow(E x,int y){
E ret=E(1,0);
for(;y;y>>=1,x=x*x) if(y&1) ret=ret*x;
return ret;
}
ll calc(ll x,ll p){
P=p; x%=P;
if(x==0) return 0;
ll tmp=Pow0(x,p-1>>1,p);
if(tmp!=1) return -1;
while(1){
tmp=rand()%p;
ll cur=(tmp*tmp%P+P-x)%P;
if(Pow0(cur,p-1>>1,p)==p-1) break;
}
w=(tmp*tmp%P+P-x)%P;
E A=E(tmp,1);
return Pow(A,p+1>>1).x;
}
}
int t,c,p;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&c,&p);
ll sqr5=Cipolla::calc(5,p);
ll cur=sqr5*c%p,inv2=p+1>>1,Y=(1+sqr5)*inv2%p;
ll val1,val2,k,ans=p+1;
k=Cipolla::calc(5LL*c*c%p+4,p);
if(~k){
val1=(cur+k)*inv2%p;
val2=(cur+p-k)*inv2%p;
ll X=BSGS::calc(Y,val1,p,0);
if(~X) ans=min(ans,X);
X=BSGS::calc(Y,val2,p,0);
if(~X) ans=min(ans,X);
}
k=Cipolla::calc((5LL*c*c%p+p-4)%p,p);
if(~k){
val1=(cur+k)*inv2%p;
val2=(cur+p-k)*inv2%p;
ll X=BSGS::calc(Y,val1,p,1);
if(~X) ans=min(ans,X);
X=BSGS::calc(Y,val2,p,1);
if(~X) ans=min(ans,X);
}
if(ans==p+1) puts("-1");
else printf("%lld\n",ans);
}
return 0;
}