[数学技巧 等比数列] 斐波那契k次幂和

来源:互联网 发布:手机相贯线软件 编辑:程序博客网 时间:2024/06/05 14:32

参考:http://blog.csdn.net/acdreamers/article/details/23039571


题意:给定,其中,求  的值。


首先k小的情况可以构造矩阵和向量[fi-1^k,fi-1^(k-1)*fi,fi-1^(k-2)*fi^2,....,fi^k,Sum]


#include<cstdio>#include<cstdlib>#include<algorithm>#include<cstring>using namespace std;typedef long long ll;inline char nc(){  static char buf[100000],*p1=buf,*p2=buf;  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }  return *p1++;}inline void read(int &x){  char c=nc(),b=1;  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;}const int M=55;const int P=1e9+9;struct Matrix{  int n;  ll a[M][M];  Matrix(){ }  Matrix(int in,int ii=0){    n=in; memset(a,0,sizeof(a));    if (ii) for (int i=0;i<=n;i++) a[i][i]=1;  }  ll *operator[](int x){    return a[x];  }  friend Matrix operator* (Matrix A,Matrix B){    int n=A.n; Matrix ret(n);    for (int i=0;i<=n;i++)      for (int j=0;j<=n;j++)        for (int k=0;k<=n;k++)  (ret[i][j]+=A[i][k]*B[k][j]%P)%=P;    return ret;  }}A;Matrix Pow(Matrix A,int b){  Matrix ret(A.n,1);  for (;b;b>>=1,A=A*A)    if (b&1)      ret=ret*A;  return ret; }int n,K;ll C[M][M];inline void Pre(){  C[0][0]=1;  for (int i=1;i<=K;i++){    C[i][0]=1;    for (int j=1;j<=i;j++)      C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;  }}ll Ans;int main(){  freopen("t.in","r",stdin);  freopen("t.out","w",stdout);  read(n); read(K); Pre();  A=Matrix(K+1);  for (int a=0;a<=K;a++)    for (int t=0;t<=a;t++)      A[a][K-a+t]=C[a][t];  A[K+1][K+1]=A[K+1][K]=1;  A=Pow(A,n-1);  for (int i=0;i<=K+1;i++)    (Ans+=A[K+1][i])%=P;  printf("%lld\n",Ans);  return 0;}

然后对于这道变态的题

以下来自参考博文


分析:嗯,这道题貌似有难度,如果比较小的话我们可以构造矩阵,实际上这样做也挺麻烦的。

     以前我们做一个大Fibonacci数列模一个大素数都是用矩阵,当然这里素数满足条件:5是模这个素数的二

     次剩余,那么现在要求不要用矩阵来计算这个结果呢?那就是今天我要讨论的问题,本题也是基于这种思路。

 

     本题我们可以直接利用Fibonacci数列的公式进行计算。因为我们知道Fibonacci数列的公式为:


      


     虽然公式中含有根号,但是我们知道是一个整数,而且5是模1000000009的二次剩余。那么可以通过逆元

     和二次剩余的转化来做。比如就可以用中的来代替。


     为了方便表示,我们令:


     


     那么得到


     


     把按照二项式展开得到


     


     对于每一个都这样表示,那么相同的合并后是等比数列,比如对于所有系数为合并后为


     , 其中


     所以到了这里本题就明确了,枚举每一个0,依次计算即可。


     当然对于,我们可以阶乘预处理然后求逆元,至于同样预处理,这样时间少很多。


     可以看出本题条件好在1000000009是素数,而且5是模1000000009的二次剩余。


     经计算2模1000000009的逆元是500000005,而的一个解为383008016


     也就是说对于


     

      

     同理,对于


     


     嗯,貌似还有一个问题没有处理,t = 1时咋办? 这个很简单啦,不用等比求和公式即可。其实吧,这里面

     我们还可以注意到,也可以进一步优化,读者自己体会,就说到这里。


#include<cstdio>#include<cstdlib>#include<algorithm>using namespace std;typedef long long ll;const int P=1000000009;const int INV2=500000005;const int SQRT5=383008016;const int INVSQRT5=276601605;const int A=691504013;const int B=308495997;const int N=100005;ll n,K;ll fac[N],inv[N];ll pa[N],pb[N];inline void Pre(int n){  fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P;  inv[1]=1; for (int i=2;i<=n;i++) inv[i]=(P-P/i)*inv[P%i]%P;  inv[0]=1; for (int i=1;i<=n;i++) inv[i]=inv[i]*inv[i-1]%P;  pa[0]=1; for (int i=1;i<=n;i++) pa[i]=pa[i-1]*A%P;  pb[0]=1; for (int i=1;i<=n;i++) pb[i]=pb[i-1]*B%P;}inline ll C(int n,int m){  return fac[n]*inv[m]%P*inv[n-m]%P;}inline ll Pow(ll a,ll b){  ll ret=1;  for (;b;b>>=1,a=a*a%P)    if (b&1)      ret=ret*a%P;  return ret;}inline ll Inv(ll x){  return Pow(x,P-2);}inline void Solve(){  ll Ans=0;  for (int j=0;j<=K;j++){    ll t=pa[K-j]*pb[j]%P,tem;    tem=t==1?n%P:t*(Pow(t,n)-1+P)%P*Inv(t-1)%P;    if (~j&1)      Ans+=C(K,j)*tem%P,Ans%=P;    else      Ans+=P-C(K,j)*tem%P,Ans%=P;  }  Ans=Ans*Pow(INVSQRT5,K)%P;  printf("%lld\n",Ans);}int main(){  int T;  freopen("t.in","r",stdin);  freopen("t.out","w",stdout);  Pre(100000);  scanf("%d",&T);  while (T--){    scanf("%lld%lld",&n,&K);    Solve();  }}



1 0
原创粉丝点击