原题题面
The Fibonacci numbers are defined as below:
Given three integers
,
and
, calculate the following summation:
Since the answer can be huge, output it modulo
(
).
输入样例
2
5 1 1
2 1 2
输出样例
12
2
题面分析
众所周知, 斐波那契数列的通项公式是
因为通式带根号,所以要想办法用二次剩余处理,
具体过程见我去年牛客的这篇博客->2019南昌H题
我们可以轻松 求出
或者
。
以
为例,我们可以得到
我们记
,则
虽然可以在
的时间内求出单个
,但
都是
,因此考虑别的办法。
注意到
时,
,将其展开,由二项式定理可以得到
当
时,把
改为
,可以得到
注意到,当列举
相同的项时,
内的项是个等比。
首项是
,公比是
。
因此我们可以考虑枚举
,每一次去计算一个等比数列的和即可。
所以我们可以整理式子,得到
当然要处理一下公比是1的情况,即
此时
内的项为
接下来预处理
的阶乘,
的阶乘逆元(为了快速处理
)。
当然算到这步可能还会超时。所以依旧需要优化。
优化1:关于计算首项
和公比的
次方,从
项变为
项时其实相当于乘上了
,这样每次就可以递推
求出首项(以及公比),同理公比的
次也可以用类似方法处理。
优化2:欧拉降幂。本题的
是个质数,所以可以直接用欧拉降幂
AC代码(873ms)
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+9;
const long long MAXN=1e5;
long long factor[MAXN+10];
long long invFactor[MAXN+10];
long long invn[MAXN+10];
const long long A=691504013;
const long long invA=691504012;
const long long B=308495997;
const long long D=276601605;
//F(n)=D(A^n-B^n)
inline long long quick_pow(long long a, long long b)
{
long long ans=1, base=a;
while(b!=0)
{
if (b&1)
ans=(long long) ans*base%mod;
base=(long long) base*base%mod;
b>>=1;
}
return ans;
}
inline long long MOD(long long a, long long b)
{
a+=b;
if (a>=mod)
a-=mod;
return a;
}
inline void init()
{
factor[0]=invFactor[0]=invn[0]=factor[1]=invFactor[1]=invn[1]=1;
for(int i=2; i<=MAXN; i++)
{
factor[i]=factor[i-1]*i%mod;
invn[i]=(long long) (mod-mod/i)*invn[mod%i]%mod;
invFactor[i]=invFactor[i-1]*invn[i]%mod;
}
}
inline long long getC(long long m, long long n)
{
if (n<0 || m<0 || m>n)
return 0;
long long ans=factor[n];
ans=(long long) ans*invFactor[m]%mod;
ans=(long long) ans*invFactor[n-m]%mod;
return ans;
}
int main()
{
init();
int t;
scanf("%d", &t);
while(t--)
{
long long n, c, k;
scanf("%lld%lld%lld", &n, &c, &k);
long long ans=0;
long long a1=quick_pow(quick_pow(A, k), c%(mod-1));
long long q=quick_pow((long long) invA*B%mod, c%(mod-1));
long long n1=n%mod;
long long n2=n%(mod-1);
long long a1power=quick_pow(a1, n2);
long long qpower=quick_pow(q, n2);
for(int i=0; i<=k; i++)
{
long long sum=getC(i, k);
if (i&1)
{
sum=mod-sum;
}
if (a1==1)
{
ans=(ans+(long long) sum*n1%mod)%mod;
}
else
{
sum=(long long) sum*((long long) a1*(a1power-1+mod)%mod)%mod;
sum=(long long) sum*quick_pow(a1-1, mod-2)%mod;
ans=MOD(ans, sum);
}
a1=(long long) a1*q%mod;
a1power=(long long) a1power*qpower%mod;
}
printf("%lld\n", (long long) ans*quick_pow(D, k)%mod);
}
}
后记
再也不用long double的快速乘了,我是傻逼。
DrGilbert 2020.7.22