Fibonacci Number CodeChef - FN (BSGS+cipolla算法)

CodeChef - FN Fibonacci Number(BSGS+cipolla)

CodeChef - FN

题目大意

给出一个数问其最小与斐波那契数列数列的第几项同余于p

解题思路

斐波那契数列有着通项
F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] F_n=\frac{1}{\sqrt5}[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n] Fn=5 1[(21+5 )n(215 )n]
由此,问题就变成了求解满足
F n ≡ c ( m o d   p ) F_n\equiv c(mod\ p) Fnc(mod p)
的最小n

不妨设 x = 5 , y = 1 + 5 2 ​ x=\sqrt5,y=\frac{1+\sqrt5}{2}​ x=5 ,y=21+5 由此原式就可以转化成
y n − ( − y ) − n ≡ c x ( m o d   p ) y^n-(-y)^{-n}\equiv cx(mod\ p) yn(y)ncx(mod p)
q = y n q=y^n q=yn并令等式两边同乘上 q q q则有
q 2 − c x q − ( − 1 ) n ≡ 0 ( m o d   p ) q^2-cxq-(-1)^n\equiv 0(mod\ p) q2cxq(1)n0(mod p)
由求根公式知
q = c x ± ( c x ) 2 + 4 ( − 1 ) n 2 q=\frac{cx\pm\sqrt{(cx)^2+4(-1)^n}}{2} q=2cx±(cx)2+4(1)n
由此可以通过cipolla算法先求出q再由BSGS求出y

AC代码

#include<bits/stdc++.h>
using namespace std;
#define int long long 
typedef long long LL;
unordered_map<int,int> mp;
LL quick_pow(LL a,LL b,LL p)
{
	LL ans=1;a%=p;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
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=quick_pow(x,(p-1)/2,p);
        if(tmp!=1) return -1;
        while(1){
            tmp=rand()%p;
            LL cur=(tmp*tmp%P+P-x)%P;
            if(quick_pow(cur,(p-1)/2,p)==p-1) break;
        }
        w=(tmp*tmp%P+P-x)%P;
        E A=E(tmp,1);
        return Pow(A,(p+1)/2).x;
    }
}


int BSGS(int B,int N,int P,int sign)//B^L=N(mod p)
{
	mp.clear();
	if(B%P==0) return -1;
	LL ans=0;
	LL m=ceil(sqrt(P));
	for(int i=0;i<=m;i++)
	{
		if(!i) ans=N%P,mp[ans]=i;
		else
		{
			ans=ans*B%P;
			mp[ans]=i;
		}
	}
	LL t=quick_pow(B,m,P);ans=1;
	for(int i=1;i<=m;i++)
	{
		ans=(ans*t)%P;
		if(mp[ans])
		{
			int s=i*m-mp[ans];
			if((s&1)==sign)return (s%P+P)%P;
		}
	}
	return -1;
}
int solve(int c,int p)
{
	int x=Cipolla::calc(5,p);
	int cx=c*x%p;
	int frac12=(p+1)/2;
	int y=(1+x)*frac12%p;
	int ans=p+1;
	int delta=Cipolla::calc(5LL*c*c%p+4,p);
	int yn1=((cx+delta)%p)*frac12%p;
	int yn2=((cx+p-delta)%p)*frac12%p;
	int ason;
	if(~delta){
	ason=BSGS(y,yn1,p,0);
	if(ason!=-1) ans=min(ans,ason);
	ason=BSGS(y,yn2,p,0);
	if(ason!=-1) ans=min(ans,ason);
	}
	delta=Cipolla::calc(5LL*c*c-4,p);
	yn1=((cx+delta)%p)*frac12%p;
	yn2=((cx+p-delta)%p)*frac12%p;
	if(~delta){
	ason=BSGS(y,yn1,p,1);
	if(ason!=-1) ans=min(ans,ason);
	ason=BSGS(y,yn2,p,1);
	if(ason!=-1) ans=min(ans,ason);
	}
	if(ans!=p+1) return ans;
	return -1;
}
int32_t main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int c,p;
		cin>>c>>p;
		cout<<solve(c,p)<<endl;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值