数论 - Power of Fibonacci - ZOJ 3774
题意:
斐波那契数列:
⎩⎪⎨⎪⎧F0=0,F1=1Fn=Fn−1+Fn−2(n>1)
给定正整数N、K,计算:
(F1)K+(F2)K+(F3)K+...+(FN)K
答案对109+9取模。
Input
There are multiple test cases. The first line of input is an integer T indicates the number of test cases. For each test case:
There are two integers N and K (0 ≤ N ≤ 1018, 1 ≤ K ≤ 100000).
Output
For each test case, output the remainder of the answer divided by 1000000009.
Sample Input
5
10 1
4 20
20 2
9999 99
987654321987654321 98765
Sample Output
143
487832952
74049690
113297124
108672406
分析:
斐波那契数列的通项公式:
Fn=5
1[(21+5
)n−(21−5
)n]
首先,5是模数109的二次剩余
即用满足:x2≡5 mod 109+9 的x来代替 5
mod 109+9。
计算得到一个满足条件的x=383008016
则:
5
1≡276601605 (mod 109+9),21+5
≡691504013 (mod 109+9),21−5
≡308495997 (mod 109+9)
令D=276601605,a=691504013,b=308495997,
原式等价于对数列:
Fnk=Dk(an−bn)k
的部分项求和并对109取模。
观察(an−bn)k,根据二项式展开:
(an−bn)k=Ck0(an)k(bn)0(−1)0+Ck1(an)k−1(bn)1(−1)1+...+Ckr(an)k−r(bn)r(−1)r+...+Ckk(an)0(bn)k(−1)k
记(−1)rCkr=Mr,Snr=(an)k−r(bn)r,上式转化为:
(an−bn)k=M0Sn0+M1Sn1+...+MrSnr+...+MkSnk
则:
Fnk=Dk(M0Sn0+M1Sn1+...+MkSnk)
不论n取何值,Snr前的系数均相同,为Mr,
原式等价于:
(F1)k+(F2)k+...+(FN)k
=M0(i=1∑NS(i)0)+M1(i=1∑NS(i)1)+...+Mk(i=1∑NS(i)k)
本题k≤105,可以从0到k枚举r,而当r为常数时,Si是一个关于i的等比数列,
公比 q=SiSi+1=(ai)k−r(bi)r(ai+1)k−r(bi+1)r=a(k−r)br
则数列Si的公比为Q=a(k−r)br,首项为S1=a(k−r)br=Q
等比数列Si求和:∑i=1NSi=Q−1Q(Qn−1)
因此,我们可以通过枚举r对Mr(∑i=1NSi)求和,即计算:
r=0∑kMr(i=1∑NSi)=r=0∑k(−1)rCkr(Q−1Q(Qn−1)),其中Q=a(k−r)br。
注意:
Q=1时,∑i=1NSi=N,直接累加(−1)rCkr×N即可。
具体细节:
①、求组合数Crk可以通过定义,Ckr=r!⋅(k−r)!k!来解决,需要预处理k!和对应的逆元。
②、将a、b带入,对枚举的每一个r,用快速幂求具体的Q。
优化:
-
观察相邻两项的公比:
Q=ak−rbr①
Q=ak−(r+1)br+1②
发现,①×−(ab)=②
因此,我们可以通过递归由①推②,减少快速幂的计算。
-
费马小定理:模数p=109+9是质数,可以通过费马小定理ap−1≡1(mod p),优化快速幂。
时间复杂度:
O(Tklog(mod)).
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5, mod=1e9+9;
const int a=691504013, b=308495997, D=276601605;
int fac[N+10],inv[N+10];
int quick_pow(ll a,ll b,int mod)
{
int res=1;
a%=mod;
while(b)
{
if(b&1) res=(ll)res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void Init()
{
fac[0]=1;
for(int i=1;i<=N;i++) fac[i]=(ll)fac[i-1]*i%mod;
for(int i=0;i<=N;i++) inv[i]=quick_pow(fac[i],mod-2,mod);
}
int INV(int x)
{
return quick_pow(x,mod-2,mod);
}
int C(int n,int m)
{
if(n<m) return 0;
return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int Sum(ll n,int k)
{
ll ans=0;
int an=quick_pow(a,k,mod),
bn=1;
int ainv=INV(a);
for(int r=0;r<=k;r++)
{
int Q=(ll)an*bn%mod, S;
int Ckr=C(k,r);
if(r&1) Ckr=-Ckr;
if(Q==1) S=n%mod;
else S=(ll)Q*(quick_pow(Q,n%(mod-1),mod)-1)%mod*INV(Q-1)%mod;
S=(ll)Ckr*S%mod;
ans+=S;
an=(ll)an*ainv%mod;
bn=(ll)bn*b%mod;
}
ans=(ans%mod+mod)*quick_pow(D,k,mod)%mod;
return ans;
}
int main()
{
Init();
int T,k;
ll n;
cin>>T;
while(T--)
{
scanf("%lld%d",&n,&k);
printf("%d\n",Sum(n,k));
}
return 0;
}