对于斐波那契问题,一般想到的解法是递推或者矩阵快速幂,但是本题数据非常之大,怎么做都超时,临时想了一下斐波那契的公式,觉得这个浮点误差很大没法写,实际上可以写但是要用到很多知识,本题的总结收获不少(唯一难受的是代码优化能做的都做了自己写的还是超时)
首先给出斐波那契数列的公式:
因为取模的数一般都是质数,而且我们能求出 是有解的,解出的两个解为 和 ,这里我们采用第二个,也就是在这个取模的意义下二者是相等的,设
令 ,而且不难得到
,那么 此时对右式按二项式定理展开得到:
我们发现对于任意的 展开后只需要计算每一块 ,剩余的项构成一个等比数列 ,那么设等比数列的和 并设公比 ,根据等比数列的求和公式直接可以得到 (实际上第一项 无需考虑)
那么实际上就是遍历 ,对每一个 求 累加即可,最后不要忘了乘
本题时间卡的很紧,比赛时过的代码现在交已经 了,对于组合数最好的方法是求阶乘按定义解,这样只需要一次预处理阶乘以及阶乘的逆元。然后 会很大可以欧拉降幂,再者就是每次计算时相当于是乘 除 ,那么递推计算也行(比赛时写的是递推预处理 但现在已经行不通了)
因为优化代码时参考了其他博客,这里贴上链接:https://www.cnblogs.com/stelayuri/p/13357775.html
我自己的超时代码
无所谓吧此题我已经学到很多了
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define lowbit(x) (x&(-x))
#define mkp(x,y) make_pair(x,y)
#define mem(a,x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<double,double> pdd;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const double dinf=1e300;
const ll INF=1e18;
const int Mod=1e9+9;
const int maxn=1e5+10;
ll fac[maxn+10],inv[maxn+10];
ll a,b,pre,mod;
ll qkp(ll x,ll n,ll p){
ll ans=1;
x%=p;
if(n>p) n=n%(p-1); //欧拉降幂
while(n){
if(n&1) ans=ans*x%p;
x=x*x%p;
n>>=1;
}
return ans;
}
void init(ll p){
fac[0]=1LL;
for(int i=1;i<maxn+10;i++) //预处理阶乘
fac[i]=fac[i-1]*i%p;
inv[maxn]=qkp(fac[maxn],p-2,p);
for(int i=maxn-1;i>=0;i--) //预处理阶乘的逆元
inv[i]=inv[i+1]*(i+1)%p;
pre=383008016;
a=(pre+1)*inv[2]%p;
b=(1-pre+p)%p*inv[2]%p;
}
ll getC(int n,int m,ll p){ //得到C(n,m)
return fac[n]*inv[m]%p*inv[n-m]%p;
}
ll solve(ll n,ll c,int k,ll p){
ll ans=0;
ll t1=qkp(a,c,p),t2=qkp(b,c,p); //预处理a^c和b^c
ll fa=qkp(t1,k,p),fb=1,inv_a=qkp(t1,p-2,p); //得到第一次运算的a和b
for(int i=0;i<=k;i++){
ll t=fa*fb%p,res;
if(t==1) res=n%p;
else res=t*(qkp(t,n,p)-1)%p*qkp(t-1,p-2,p)%p; //等比数列求和
res=res*getC(k,i,p)%p; //乘以组合数
//判断符号正负
if(i&1) ans=(ans-res+p)%p;
else ans=(ans+res)%p;
fa=fa*inv_a%p; //更新下次计算的a
fb=fb*t2%p; //更新下次计算的b
}
ll m=qkp(pre,p-2,p); //最后乘上1/sqrt{5}的k次方
ans=ans*qkp(m,k,p)%p;
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
//ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll n,c; int k,t;
init(Mod);
scanf("%d",&t);
while(t--){
scanf("%lld%lld%d",&n,&c,&k);
printf("%lld\n",solve(n,c,k,Mod));
}
return 0;
}