杭电多校第一场05 杭电6755:Fibonacci Sum【斐波那契通项公式】

题目

事先说一下:我是多校的时候ac的,比赛一停就当场换机子了,赛时是1800ms过的,赛后就TLE了,自己加速一下。

题目就是求斐波那契的:
在这里插入图片描述
这个题我和队友思索了很长时间,从各种性质到代换式子都不能解决。然后最后一点时间刚一下通项公式就做出来了。

通项公式: F n = ( 5 ) 5 ∗ ( ( 1 + ( 5 ) 2 ) n + ( 1 − ( 5 ) 2 ) n ) F_n=\frac {\sqrt(5)}{5}*((\frac{1+\sqrt(5)}{2})^n+(\frac{1-\sqrt(5)}{2})^n) Fn=5( 5)((21+( 5))n+(21( 5))n)
为了方便计算和理解让
a= 1 + ( 5 ) 2 \frac{1+\sqrt(5)}{2} 21+( 5)
b= 1 − ( 5 ) 2 \frac{1-\sqrt(5)}{2} 21( 5)

k次方对吧,我们可以说 ( ( 5 ) F c ) k (\sqrt(5)F_c)^k (( 5)Fc)k的每一项是不是就是
C k i ∗ a i c b ( k − 1 ) ∗ c C_k^i *a^{ic}b^{(k-1)*c} Ckiaicb(k1)c
然后发现 ( ( 5 ) F 2 c ) k (\sqrt(5)F_{2c})^k (( 5)F2c)k的每一项竟然是
C k i ∗ a i ∗ 2 c b ( k − 1 ) ∗ 2 c C_k^i *a^{i*2c}b^{(k-1)*{2c}} Ckiai2cb(k1)2c

对应每一项是不是 F 2 c F_{2c} F2c都是 F c F_c Fc的平方,然后类推出来一个结论:

对第i项( 0 < = i < = k 0<=i<=k 0<=i<=k):有一个等比数列,可以求前N项。
代码需要跑T*(K+1)次;

其实我是猜到要O(K)的。
为什么N、C都给了1e18范围,就K给了1e5?T是1e2,如果是O(T*K)时间就能过了,再加上20次常数,这不就是要求的2s吗?NC都不能直接一步一步算,所以一定会被压缩。我一开始想法是构造出来一个矩阵S。 S k S^k Sk就是题目要求的结果。但是失败了。于是想一下到底NC是log级别的压缩还是O(1)的压缩。

最后推出答案。代码如下:(常数可能比较大,杭电换了比比赛时候更慢的机子,不能直接交,草了)
code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <iomanip>
//
using namespace std;
const int INF = 0x3f3f3f3f;//1.06e9大小
const int mod = 1e9 + 9;
const long long mod2 = 998244353;
const long long mod3 = 1e9;
const double PI = 3.14159265;
const double eps =1e-8;
typedef unsigned long long ULL;
typedef long long LL;
#define ms(x, n) memset(x,n,sizeof(x))
#define debug printf("***debug***\n")
#define pii pair<int ,int>
#define X first
#define Y second
#define pb push_back
//以下define不能在团队赛中使用
#define rep(i,from,to) for(int i=from;i<=to;++i)
#define per(i,from,to) for(int i=from:i>=to;--i)
/*
*/
const int MAXN=1e5+20;
const int sq5=616991993;
int t,k,jcn[MAXN],jc[MAXN],a[MAXN],b[MAXN];// a、b二项式两个单元
LL n,c;
//jcn 阶乘逆元,jc 阶乘;
inline LL ksm(LL x,LL n)
{
	LL res=1;
	LL base =x;
	while(n)
    {
		if(n&1)res=res*base%mod;
		base=base*base%mod;
		n>>=1;
	}
	return res;
}

inline LL C(int n,int m)//组合
{
     return 1LL*jcn[n]*jc[m]%mod*jc[n-m]%mod;
}
int main()
{
    int t;
    cin>>t;
    jcn[0]=1;
    for(int i=1;i<=100000;++i)
    {
        jcn[i]=1LL*jcn[i-1]*i%mod;
    }
    jc[100000]=ksm(jcn[100000],mod-2);
    for(int i=100000;i;--i)
    {
        jc[i-1]=1LL*jc[i]*i%mod;
    }
    a[0]=b[0]=1;
    for(int i=1;i<=100000;++i)
    {
        a[i]=1LL*a[i-1]*(1+sq5)%mod*jc[2]%mod,b[i]=1LL*b[i-1]*(1-sq5+mod)%mod*jc[2]%mod;
    }
    while(t--)
	{
        scanf("%lld %lld %d",&n,&c,&k);
        LL ans=0;
        for(int i=0;i<=k;++i)
        {
            LL tmp=1LL*a[i]*b[k-i]%mod,res=0;
            tmp=ksm(tmp,c);
            if(tmp==1)
            {
                res=C(k,i)*(n%mod)%mod;
            }
            else
            {
                res=C(k,i)*((ksm(tmp,(n+1)%(mod-1))-tmp+mod)%mod)%mod*ksm(tmp-1,mod-2)%mod;
            }
            if(!((k-i)&1))(ans+=res)%=mod;
            else (ans+=mod-res)%=mod;
        }
        ans=ans*ksm(ksm(sq5,mod-2),k)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值