兔子

题目大意

求fib(n) mod 10^9+9=k的最小n。

做法

我们知道 fib(n)=15[(1+52)n(152)n]
不妨设两个特征根分别为a和b,注意 ab=1
根号5在1e9+9下是存在的,设为g,那么问题变为
anbn=gk 的最小n。
an(1)nan=gk
a2ngkan(1)n=0
an=gk+(5k2+4(1)n)2
不妨分类讨论n为奇数还是偶数,求出根号后变成bsgs问题(只要求解出某个奇偶性的最小n)。
开根号怎么做?可以求出原根然后bsgs。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
map<int,int> s;
const int p=1000000009,g=383008016,inv2=(p+1)/2,maxn=1000000+10,gg=13;
int fib[maxn];
int i,j,k,l,r,t,n,m,q,x,y,z,a,b,ans;
bool czy;
int qsm(int x,int y){
    if (!y) return 1;
    int t=qsm(x,y/2);
    t=(ll)t*t%p;
    if (y%2) t=(ll)t*x%p;
    return t;
}
int bsgs(int a,int b,int f){
    if (f==0&&b==1) return 0;
    int i,k,t=1,r,ans=-1;
    q=floor(sqrt(p));
    r=qsm(a,q);
    //s.clear();
    fo(i,1,q){
        t=(ll)t*r%p;
        if (!s[t]) s[t]=i;
    }
    t=1;
    fo(i,0,q-1){
        //t=qsm(a,i);
        r=(ll)b*t%p;
        t=(ll)t*a%p;
        k=s[r];
        if (k){
            k=k*q-i;
            if (k%2!=f) continue;
            if (ans==-1||k<ans) ans=k;
        }
        //t=(ll)t*a%p;
    }
    r=qsm(a,q);
    t=1;
    fo(i,1,q){
        t=(ll)t*r%p;
        s[t]=0;
    }
    return ans;
}
bool pd(int n){
    /*x=bsgs(gg,n,0);
    if (x==-1) return 0;else return 1;*/
    if (qsm(n,(p-1)/2)==1) return 1;else return 0;
}
int main(){
    freopen("rabbit.in","r",stdin);freopen("rabbit.out","w",stdout);
    czy=1;
    /*fib[0]=0;
    fib[1]=1;
    fo(i,2,maxn-10) fib[i]=(fib[i-1]+fib[i-2])%p;*/
    scanf("%d",&k);
    fo(i,0,maxn-9)
        if (i>maxn-10||fib[i]==k) break;
    if (i<=maxn-10){
        printf("%d\n",i);
        return 0;
    }
    ans=-1;
    a=(ll)(1+g)*inv2%p;
    b=(ll)(1-g)*inv2%p;
    l=(ll)5*k%p*k%p*qsm(4,p-2)%p;
    m=l-1;
    (m+=p)%=p;
    if (pd(m)){
        x=bsgs(gg,m,0);
        x/=2;
        z=y=qsm(gg,x);
        (y+=(ll)g*k%p*inv2%p)%=p;
        (y+=p)%=p;
        (a+=p)%=p;
        n=bsgs(a,y,1);
        if (n!=-1) ans=n;
        y=-z;
        (y+=(ll)g*k%p*inv2%p)%=p;
        (y+=p)%=p;
        (a+=p)%=p;
        n=bsgs(a,y,1);
        if (n!=-1&&(ans==-1||n<ans)) ans=n;
    }
    m=l+1;
    (m+=p)%=p;
    if (pd(m)){
        x=bsgs(gg,m,0);
        x/=2;
        z=y=qsm(gg,x);
        (y+=(ll)g*k%p*inv2%p)%=p;
        (y+=p)%=p;
        (a+=p)%=p;
        n=bsgs(a,y,0);
        if (n!=-1&&(ans==-1||n<ans)) ans=n;
        y=-z;
        (y+=(ll)g*k%p*inv2%p)%=p;
        (y+=p)%=p;
        (a+=p)%=p;
        n=bsgs(a,y,0);
        if (n!=-1&&(ans==-1||n<ans)) ans=n;
    }
    printf("%d\n",ans);
    //printf("%d\n",bsgs(gg,500000001,1));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值