关闭

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

607人阅读 评论(2) 收藏 举报
分类:

参考: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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:309418次
    • 积分:12408
    • 等级:
    • 排名:第1311名
    • 原创:969篇
    • 转载:3篇
    • 译文:0篇
    • 评论:54条
    最新评论