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

在模意义下,使用通项公式没有了精度误差,就变的可以使用了
斐波那契数列的通项公式是:
(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;
}


原文地址:https://www.cnblogs.com/Yuzao/p/8807629.html