Fibonacci Sum 推公式

F ( n ) F(n) F(n)为Fibonacci数列第n项,求 ∑ ( F ( i C ) k , 0 ≤ i ≤ N \sum(F(iC)^k, 0\le i \le N (F(iC)k,0iN
给多组N,C,K, 1 ≤ N , C ≤ 1 0 18 1\le N, C \le 10^{18} 1N,C1018, k ≤ 1 0 5 k\le 10^5 k105

解法:

把斐波那契数列拆成这样
F n = d ( a n − b n ) F_n=d(a^n-b^n) Fn=d(anbn)
d = 1 5 , a = 1 + 5 2 , b = 1 − 5 2 d = \frac{1}{\sqrt5} ,a=\frac{1+\sqrt5}{2},b=\frac{1-\sqrt5}{2} d=5 1,a=21+5 ,b=215
这三个数在题目里不会改变,先使用cipolla求二次剩余以及费马小定理求逆元都求出来,设为常量,我的代码被不设为常量就被卡超时了。
接下来:
∑ i = 0 N ( F ( i C ) k ) = d k ∑ i = 0 N ∑ j = 0 k C k j ( ( a i c ) j ( − b i c ) k − j ) \sum_{i=0}^{N}(F(iC)^k)=d^k\sum_{i=0}^{N}\sum_{j=0}^{k}C_{k}^j((a^{ic})^{j}(-b^{ic})^{k-j}) i=0N(F(iC)k)=dki=0Nj=0kCkj((aic)j(bic)kj)
= d k ∑ j = 0 k ( − 1 ) k − j C k j ∑ i = 0 N ( a j b k − j ) i c =d^k\sum_{j=0}^{k}(-1)^{k-j}C_{k}^{j}\sum_{i=0}^{N}(a^jb^{k-j})^{ic} =dkj=0k(1)kjCkji=0N(ajbkj)ic
X j = ( a j b k − j ) c X_j=(a^jb^{k-j})^c Xj=(ajbkj)c,原式变为:
d k ∑ j = 0 k C k j ( − 1 ) k − j ∑ i = 0 N X j i d^k\sum_{j=0}^{k}C_k^j(-1)^{k-j}\sum_{i=0}^NX_j^i dkj=0kCkj(1)kji=0NXji
并且,有等比数列求和: ∑ i = 0 N X j i = X j N + 1 − 1 X j − 1 \sum_{i=0}^NX_j^i=\frac{X_j^{N+1}-1}{X_j-1} i=0NXji=Xj1XjN+11那么最终, d k ∑ j = 0 k C k j ( − 1 ) k − j X j N + 1 − 1 X j − 1 d^k\sum_{j=0}^{k}C_k^j(-1)^{k-j}\frac{X_j^{N+1}-1}{X_j-1} dkj=0kCkj(1)kjXj1XjN+11
把阶乘,组合数都预处理一下,需要N,C,K的放在里面计算,N和C特别大,对模数欧拉降幂(就是快速幂的时候my_pow(a,b%(p-1))
时间复杂度大概是 200 ∗ O ( k l o g k ) 200*O(klogk) 200O(klogk)

hduoj里提交不能在程序里面跑二次剩余以及多余的求逆元操作,貌似时间卡得很死。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long ll;

const int N = 1e5+5;
const ll mod = 1e9+9;
const ll SQRT = 383008016, RSQRT = 276601605;
const ll A = 691504013, RA = 691504012;
const ll B = 308495997, RB = 308495996;
ll fac[N], in[N];
ll n, c, k;


inline ll ksm(ll a, ll b){
	ll ans = 1%mod;
	while(b){
		if(b&1) ans = ans*a%mod;
		a = a*a%mod;
		b >>= 1;
	}
	return ans%mod;
}

inline ll inv(ll x){
	return ksm(x, mod-2);
}

ll C(ll a, ll b){
	if(a<0 || b > a) return 0;
	return fac[a]*in[b]%mod*in[a-b]%mod;
} 

void init(int n){
	fac[0] = 1;
	for(int i = 1; i <= n; i++) fac[i] = fac[i-1]*(ll)i%mod;
	in[n] = inv(fac[n]);
	for(int i = n-1; i >= 0; i--) in[i] = (ll)(i+1)*in[i+1]%mod;
}
int main(){
	init(1e5);
	int _;
	scanf("%d", &_);
	while(_--){
		scanf("%lld%lld%lld", &n, &c, &k);
		ll sum = 0;
		ll bc = ksm(B, c%(mod-1)), ac = ksm(A, c%(mod-1));
		ll inv_bc = inv(bc);
		ll base = ksm(bc, k), mul = ac*inv_bc%mod;
		for(int i = 0; i <= k; i++){
			ll tmp1, tmp2;
			if(base == 1){
				tmp1 = n%mod;
			}
			else tmp1 = base*(ksm(base, n%(mod-1))+mod-1)%mod*inv(base-1)%mod;
			tmp2 = C(k, i)*tmp1%mod;
			if((k-i)&1){
				sum -= tmp2;
				sum = (sum+mod)%mod;
			}
			else{
				sum = (sum+tmp2)%mod;
			}
			base = base*mul%mod;
		}
		sum = sum*ksm(RSQRT, k)%mod;
		printf("%lld\n", sum);
	}
	return 0;
} 
/*
2
5 1 1
2 1 2
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值