维护斐波那契数列通项公式

在模意义下,使用通项公式没有了精度误差,就变的可以使用了
斐波那契数列的通项公式是:
\(F(n)=\frac{\frac{\sqrt{5}+1}{2}^{n}-\frac{-\sqrt{5}+1}{2}^{n}}{\sqrt{5}}\)

\(\sqrt{5}\) 在不同的模数下,不一定存在,否则枚举一下或者二次剩余定理弄一下就可以求出来

在通项公式中是实际上可以把两项分开算 \(\frac{\sqrt{5}+1}{2}^{n}\),\(\frac{-\sqrt{5}+1}{2}^{n}\)

但是在分母中还是有一个 \(\sqrt{5}\) 要除,那么我们观察式子,分子的不带 \(\sqrt{5}\) 的项都是相同的,并且指数都是 \(n\),所以最后求出来的结果是一样的,最后相减就抵消了,所以我们只需要关心 \(\sqrt{5}\) 的系数就行了,最关键的是:分母还有一个 \(\sqrt{5}\) 那么不就和分子中的 \(\sqrt{5}\) 抵消掉了,我们根本不用关心\(\sqrt{5}\) 的值了,最后答案就是 \(\sqrt{5}\) 的系数了

不妨把一个数表示成 \(a*\sqrt{5}+b\) 的形式,那么乘法之后还是一个关于 \(\sqrt{5}\) 的表达式
我们定义一个类,重载一下运算就可以了

关于这个东西求逆,列个方程算一下发现逆元就是 \(\frac{-\sqrt{5}*a}{b^2-5*a^2}+\frac{b}{b^2-5*a^2}\),相乘得到 \((0,1)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1100,mod=998244353,inv2=499122177;
ll f[N];
inline int qpow(int x,int k){
    int sum=1;
    while(k){
        if(k&1)sum=1ll*sum*x%mod;
        x=1ll*x*x%mod;k>>=1;
    }
    return sum;
}
struct Num{
    int a,b;//a*sqrt(5)+b
    Num(){a=0;b=0;}
    Num(int _a,int _b){a=_a;b=_b;}
    Num operator *(const Num &p){
        return Num((1ll*b*p.a+1ll*a*p.b)%mod,(1ll*a*p.a*5+1ll*b*p.b)%mod);
    }
    Num operator -(const Num &p){
        return Num((a-p.a+mod)%mod,(b-p.b+mod)%mod);
    }
    Num inv(){
        int t=qpow(((1ll*b*b-1ll*5*a*a)%mod+mod)%mod,mod-2);
        return Num(1ll*(mod-a)*t%mod,1ll*b*t%mod);
    }
}Z=Num(inv2,inv2),F=Num(mod-inv2,inv2);
inline Num qm(Num x,int k){
  Num S=Num(0,1);
  while(k){
      if(k&1)S=S*x;
      x=x*x;k>>=1;
  }
  return S;
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  f[1]=1;
  int n=999;
  for(int i=2;i<=n;i++)f[i]=(f[i-1]+f[i-2])%mod;
  Num S=qm(Z,n),T=qm(F,n);
  S=S-T;
  cout<<S.a<<endl<<f[n]<<endl;
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Yuzao/p/8807629.html