HDU - 6755 Fibonacci Sum【Fibonacci数列的幂和】

题目链接

  • 题意:给定 N , C , K N,C,K N,C,K,求 ( F 0 K + F C K + F 2 C K + . . . + F N C K )   m o d   1 e 9 + 9 ({F_{0}}^K+{F_{C}}^K+{F_{2C}}^K+...+{F_{NC}}^K) \ mod\ 1e9+9 (F0K+FCK+F2CK+...+FNCK) mod 1e9+9,其中 F i F_i Fi F i b o n a c c i Fibonacci Fibonacci 数列的第 i i i 项。

思路:

三个前置知识:

  1. F i b o n a c c i Fibonacci Fibonacci 数列的通项公式: F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] F_n=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n] Fn=5 1[(21+5 )n(215 )n]
  2. 二项式定理: ( a + b ) n = ∑ r = 0 n ( n r ) a r b n − r (a+b)^n=\displaystyle \sum^{n}_{r=0}{ {n \choose r}a^rb^{n-r}} (a+b)n=r=0n(rn)arbnr
  3. 二次剩余:一个数 a a a,如果不是 p p p 的倍数且模 p p p 同余某个数的平方,则称 a a a 为模 p p p 的二次剩余。
    暴力发现 5 是模 1e9+9 的二次剩余,并且发现 38300801 6 2 ≡ 5   m o d   1 e 9 + 9 383008016^2\equiv5 \ mod \ 1e9+9 38300801625 mod 1e9+9

F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] = d ( a n − b n ) F_n=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n]=d(a^n-b^n) Fn=5 1[(21+5 )n(215 )n]=d(anbn),那么:

  1. d = 276601605 ≡ 1 5   m o d   1 e 9 + 9 d=276601605\equiv\frac{1}{\sqrt{5}}\ mod \ 1e9+9 d=2766016055 1 mod 1e9+9
  2. a = 691504013 ≡ 1 + 5 2   m o d   1 e 9 + 9 a=691504013\equiv\frac{1+\sqrt{5}}{2}\ mod \ 1e9+9 a=69150401321+5  mod 1e9+9
  3. b = 308495997 ≡ 1 − 5 2   m o d   1 e 9 + 9 b=308495997\equiv\frac{1-\sqrt{5}}{2}\ mod \ 1e9+9 b=308495997215  mod 1e9+9

所以我们可以得到:

F 0 K + F C K + F 2 C K + . . . + F N C K = ∑ i = 0 N F i C K = ∑ i = 0 N [ d ( a i C − b i C ) ] k = ∑ i = 0 N d k ∑ j = 0 K ( K j ) ( − 1 ) K − j ( a i C ) j ( b i C ) K − j = d k ∑ j = 0 K ( K j ) ( − 1 ) K − j ∑ i = 0 N ( ( a j b K − j ) C ) i {F_{0}}^K+{F_{C}}^K+{F_{2C}}^K+...+{F_{NC}}^K\\=\displaystyle \sum^{N}_{i=0}{ {F_{iC}}^{K}}=\displaystyle \sum^{N}_{i=0}{[d(a^{iC}-b^{iC})]^k}\\=\displaystyle \sum^{N}_{i=0}{d^k \displaystyle \sum^{K}_{j=0}{ {K \choose j}(-1)^{K-j}(a^{iC})^j(b^{iC})^{K-j}}}\\=d^k\displaystyle \sum^{K}_{j=0}{ {K \choose j}(-1)^{K-j}\displaystyle \sum^{N}_{i=0}((a^{j}b^{K-j})^C)^i} F0K+FCK+F2CK+...+FNCK=i=0NFiCK=i=0N[d(aiCbiC)]k=i=0Ndkj=0K(jK)(1)Kj(aiC)j(biC)Kj=dkj=0K(jK)(1)Kji=0N((ajbKj)C)i

可以发现 ∑ i = 0 N ( ( a j b K − j ) C ) i \displaystyle \sum^{N}_{i=0}((a^{j}b^{K-j})^C)^i i=0N((ajbKj)C)i是个等比数列, q = ( a j b K − j ) C q=(a^{j}b^{K-j})^C q=(ajbKj)C
根据等比数列前 n n n 项和公式可以求得 S n = ∑ i = 0 N ( ( a j b K − j ) C ) i = q ( q N − 1 ) q − 1 Sn=\displaystyle \sum^{N}_{i=0}((a^{j}b^{K-j})^C)^i=\frac{q(q^N-1)}{q-1} Sn=i=0N((ajbKj)C)i=q1q(qN1)

  • 这样的话,复杂度貌似是 O ( K ∗ l o g ( N ) ) O(K*log(N)) O(Klog(N))

问题1:如何 O ( 1 ) O(1) O(1) 求组合数取模呢?

已知组合数公式: ( n m ) = n ! ( n − m ) ! m ! {n\choose m}=\frac{n!}{(n-m)!m!} (mn)=(nm)!m!n!,因为 K K K 的范围只有 1e5,所以我们可以 O ( K ) O(K) O(K) 预处理求得 1e5 内的阶乘 f a c [ i ] fac[ i ] fac[i],逆元 i n v [ i ] inv[i] inv[i] 以及阶乘的逆元 F a c I n v [ i ] FacInv[i] FacInv[i]
【用到了线性递推求逆元
阶乘的逆元就递推即可 : F a c I n v [ i ] = F a c I n v [ i − 1 ] ∗ i n v [ i ] FacInv[i]=FacInv[i-1]*inv[i] FacInv[i]=FacInv[i1]inv[i]

问题2:如何 O ( 1 ) O(1) O(1) q = ( a j b K − j ) C q=(a^{j}b^{K-j})^C q=(ajbKj)C 呢?

  1. 我们发现当 j ( 0 → K ) j(0\rightarrow K) j(0K) 时,q也是个等比数列,公比为 a c b c \displaystyle\frac{a^c}{b^c} bcac,所以我们可以循环前将 a c b c \displaystyle\frac{a^c}{b^c} bcac 算出来,然后每次循环乘上该公比即可。
  2. 初始 q = ( a 0 b K ) C = ( b K ) C q=(a^{0}b^{K})^C=(b^K)^C q=(a0bK)C=(bK)C,对于 b K b^{K} bK 我们还是可以 O ( K ) O(K) O(K) 预处理出幂的。

优化:欧拉降幂

我们发现 N , C N,C N,C的范围都是1e18,太大了。
因为 p = 1 e 9 + 9 p=1e9+9 p=1e9+9 是个质数,和所有数都互质,所以我们可以利用欧拉降幂来优化快速幂: a c ≡ a c   %   ψ ( p )   m o d   p a^c\equiv a^{c \ \% \ \psi(p)} \ mod \ p acac % ψ(p) mod p , 其中 ψ ( p ) = p − 1 = 1 e 9 + 8 \psi(p)=p-1=1e9+8 ψ(p)=p1=1e9+8


Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define INF 0x3f3f3f3f
#define A 691504013
#define B 308495997
#define inv_rt_5 276601605
#define faiMod 1000000008
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

ll read()
{
    
    
    ll x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {
    
     if(c == '-') f = -f; c = getchar(); }
    while(c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int maxN = 100005;
const ll mod = 1000000009;

ll fpow(ll x, ll y) {
    
    
    ll base = x % mod, ans = 1;
    y %= faiMod;
    while(y) {
    
    
        if(y & 1) ans *= base, ans %= mod;
        base *= base, base %= mod;
        y >>= 1;
    }
    return ans % mod;
}
//ll fd, a, b, d;
//void Find() {
    
    
//    for(ll i = sqrt(mod);; ++ i ) {
    
    
//        if(i * i % mod == 5) {
    
    
//            fd = i; break;
//        }
//    }
//    d = fpow(fd, mod - 2);
//    a = (1 + fd) * fpow(2, mod - 2) % mod;
//    b = (1 - fd + mod) * fpow(2, mod - 2) % mod;
//}
ll N, C, K;
ll powA[maxN], powB[maxN];
ll fac[maxN], inv[maxN], FacInv[maxN];
void Init() {
    
    
    fac[0] = fac[1] = inv[1] = FacInv[0] = FacInv[1] = 1;
    for(int i = 2; i <= 100000; ++ i ) {
    
    
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
        FacInv[i] = FacInv[i - 1] * inv[i] % mod;
    }
    powA[0] = powB[0] = 1;
    for(int i = 1; i <= 100000; ++ i ) {
    
    
        powA[i] = powA[i - 1] * A % mod;
        powB[i] = powB[i - 1] * B % mod;
    }
}
ll getNCM(ll n, ll m) {
    
    
    return fac[n] * FacInv[m] % mod * FacInv[n - m] % mod;
}
int main()
{
    
    
//    Find();
    Init();
    const ll base = A * fpow(B, mod - 2) % mod;
    ll t; t = read();
    while(t -- ) {
    
    
        N = read(), C = read(), K = read();
        ll ans = fpow(inv_rt_5, K) % mod, sum = 0;
        ll q = fpow(powA[0] * powB[K], C);
        ll inc = fpow(base, C);
        for(ll i = 0; i <= K; ++ i ) {
    
    
            ll nCm = getNCM(K, i);
            if((K - i) & 1) nCm = mod - nCm;
            ll Sn = q;
            if(q == 1) {
    
    
                Sn = N % mod;
            } else {
    
    
                Sn *= (fpow(q, N) - 1), Sn %= mod;
                Sn *= fpow(q - 1, mod - 2), Sn %= mod;
            }
            sum += Sn * nCm, sum %= mod;
            q *= inc, q %= mod;
        }
        ans *= sum, ans %= mod;
        cout << ans << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/107523402