参考: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
#include
#include
#include
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
#include
#include
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();
}
}