[HDU6755]Fibonacci Sum

本文深入解析了斐波那契数列的高级求和算法,利用矩阵快速幂和模意义下的快速幂优化,实现了在大规模数据范围内的高效计算。文章详细介绍了如何通过预处理和分块幂技巧,将复杂度降低到O(∑klog⁡P),并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

定义Fibonacci 数列:

F 0 = 0 , F 1 = 1 F n = F n − 1 + F n − 2   ( n > 1 ) F_0=0,F_1=1\\ F_n=F_{n-1}+F_{n-2}\ (n>1) F0=0,F1=1Fn=Fn1+Fn2 (n>1)

给定整数 N , c , k N,c,k N,c,k,求
∑ n = 0 N F n c k m o d    1 0 9 + 9 \sum_{n=0}^NF_{nc}^k\mod{10^9+9} n=0NFnckmod109+9
N , c ≤ 1 0 18 , k ≤ 1 0 5 N,c\le10^{18},k\le10^5 N,c1018,k105.


题解

根据Fibonacci数列通项公式 F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] \displaystyle F_n=\frac{1}{\sqrt5}\Big[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n\Big] Fn=5 1[(21+5 )n(215 )n],有
∑ n = 0 N F n c k = ∑ n = 0 N ( 1 5 ) k [ ( 1 + 5 2 ) n c − ( 1 − 5 2 ) n c ] k , a c = ( 1 + 5 2 ) c , b c = ( 1 − 5 2 ) c = ( 1 5 ) k ∑ n = 0 N ∑ i = 0 k ( − 1 ) i ( k i ) ( a c k − i b c i ) n , t i = a c k − i b c i = ( 1 5 ) k ∑ i = 0 k ( − 1 ) i ( k i ) ∑ n = 0 N t i n = ( 1 5 ) k ∑ i = 0 k ( − 1 ) i ( k i ) ⋅ { t i N + 1 − 1 t i − 1 , t i ≠ 1 N + 1 , t i = 1 \begin{aligned} \sum_{n=0}^NF_{nc}^k&=\sum_{n=0}^N\Big(\frac{1}{\sqrt5}\Big)^k\Big[(\frac{1+\sqrt5}{2})^{nc}-(\frac{1-\sqrt5}{2})^{nc}\Big]^k,\quad a_c=(\frac{1+\sqrt5}{2})^c,b_c=(\frac{1-\sqrt5}{2})^c\\ &=\Big(\frac{1}{\sqrt5}\Big)^k\sum_{n=0}^N\sum_{i=0}^k(-1)^i{k\choose i}(a_c^{k-i}b_c^i)^n,\quad t_i=a_c^{k-i}b_c^i\\ &=\Big(\frac{1}{\sqrt5}\Big)^k\sum_{i=0}^k(-1)^i{k\choose i}\sum_{n=0}^Nt_i^n\\ &=\Big(\frac{1}{\sqrt5}\Big)^k\sum_{i=0}^k(-1)^i{k\choose i}\cdot\left\{\begin{aligned} \frac{t_i^{N+1}-1}{t_i-1},&\quad t_i\neq 1\\ N+1,&\quad t_i=1 \end{aligned}\right. \end{aligned} n=0NFnck=n=0N(5 1)k[(21+5 )nc(215 )nc]k,ac=(21+5 )c,bc=(215 )c=(5 1)kn=0Ni=0k(1)i(ik)(ackibci)n,ti=ackibci=(5 1)ki=0k(1)i(ik)n=0Ntin=(5 1)ki=0k(1)i(ik)ti1tiN+11,N+1,ti=1ti=1

暴力预处理出 38300801 6 2 ≡ 61699199 3 2 ≡ 5 ( m o d 1 0 9 + 9 ) 383008016^2\equiv616991993^2\equiv5\pmod{10^9+9} 383008016261699199325(mod109+9) ,任取一个作为 5 \sqrt5 5 模意义下的值即可。
再求出 a ≡ 1 + 5 2 ( m o d 1 0 9 + 9 ) , b ≡ 1 − 5 2 ( m o d 1 0 9 + 9 ) ) \displaystyle a\equiv\frac{1+\sqrt5}{2}\pmod{10^9+9},b\equiv\frac{1-\sqrt5}{2}\pmod{10^9+9)} a21+5 (mod109+9),b215 (mod109+9))
计算快速幂的时候用 a b ≡ a b m o d    P − 1 ( m o d P ) a^b\equiv a^{b\mod{P-1}}\pmod{P} ababmodP1(modP)降幂优化。
t i t_i ti可以通过 t i = t i − 1 ⋅ ( b a ) c \displaystyle t_i=t_{i-1}\cdot(\frac ba)^c ti=ti1(ab)c递推计算。
时间复杂度 O ( ∑ k log ⁡ P ) O(\sum k\log P) O(klogP)

const int P = 1e9 + 9, sqrt5 = 383008016, invsqrt5 = 276601605, A = 691504013,
          B = 308495997;
inline int FastExp(int a, ll b) {
    int x = 1;
    for (b %= P - 1; b; b >>= 1, a = (ll)a * a % P)
        if (b & 1)
            x = (ll)x * a % P;
    return x;
}
inline void Solve() {
    ll Ans = 0;
    int t = FastExp(FastExp(A, k), c),
        q = FastExp((ll)B * FastExp(A, P - 2) % P, c);
    for (int i = 0; i <= k; ++i, t = (ll)t * q % P)
        if (t == 1)
            Ans += ((i & 1) ? -1 : 1) * (ll)C(k, i) * ((n + 1) % P) % P;
        else
            Ans += ((i & 1) ? -1 : 1) * (ll)C(k, i) * (FastExp(t, n + 1) - 1) % P * FastExp(t - 1, P - 2) % P;

    Ans = (Ans % P + P) % P * FastExp(invsqrt5, k) % P;
    printf("%lld\n", Ans);
}

预处理分块幂 a n = a S ⌊ n S ⌋ + ( n m o d    S ) = a S ⌊ n S ⌋ ⋅ a n m o d    S = f a , 1 ( ⌊ n S ⌋ ) ⋅ f a , 2 ( n m o d    S ) \displaystyle a^n=a^{S\lfloor\frac{n}{S}\rfloor+(n\mod{S})}=a^{S\lfloor\frac{n}{S}\rfloor}\cdot a^{n\mod{S}}=f_{a,1}(\lfloor\frac{n}{S}\rfloor)\cdot f_{a,2}(n\mod{S}) an=aSSn+(nmodS)=aSSnanmodS=fa,1(Sn)fa,2(nmodS),取 S = 2 15 , S 2 > 1 0 9 + 9 S=2^{15},S^2>10^9+9 S=215,S2>109+9,将取模和整除转化为位运算。这样快速幂就可以 O ( 1 ) O(1) O(1)求出。
假设 t i ≠ 1 t_i\neq 1 ti=1,则有
∑ n = 0 N F n c k = ( 1 5 ) k 1 ∏ i = 0 k ( t i − 1 ) ∑ i = 0 k ( − 1 ) i ( k i ) ( t i N + 1 − 1 ) ∏ j ≠ i ( t i − 1 ) \sum_{n=0}^NF_{nc}^k=\Big(\frac{1}{\sqrt5}\Big)^k\frac{1}{\prod\limits_{i=0}^k(t_i-1)}\sum_{i=0}^k(-1)^i{k\choose i}(t_i^{N+1}-1)\prod_{j\neq i}(t_i-1) n=0NFnck=(5 1)ki=0k(ti1)1i=0k(1)i(ik)(tiN+11)j=i(ti1)

预处理出 t i − 1 t_i-1 ti1的前缀积和后缀积,就可以只需要算一次逆元,再特判一下存在 t i = 1 t_i=1 ti=1的情况即可。
注意到 a b = − 1 ab=-1 ab=1,于是有
a k − i b i = { ( − 1 ) i a k − 2 i , i ≤ k 2 ( − 1 ) k − i b 2 i − k , i > k 2 a^{k-i}b^i= \left\{\begin{aligned} (-1)^ia^{k-2i},&\quad i\le\frac{k}{2}\\ (-1)^{k-i}b^{2i-k},&\quad i>\frac{k}{2} \end{aligned}\right. akibi=(1)iak2i,(1)kib2ik,i2ki>2k

这样替换就可以在求 t i t_i ti t i N + 1 t_i^{N+1} tiN+1时少做几次乘法和取模运算。
时间复杂度 O ( P + ∑ k ) O(\sqrt P+\sum k) O(P +k)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, S = (1 << 15) - 1, P = 1e9 + 9, sqrt5 = 383008016,
          invsqrt5 = 276601605, A = 691504013, B = 308495997;
typedef long long ll;
typedef int arr[N];
/*-----------------------------------------------------------------*/
int k;
arr g, pre, suf, fac = {1}, ifac, f_1[2] = {{1}, {1}}, f_2[2] = {{1}, {1}};
ll n, c;
inline int C(int n, int m) {
    return n >= m ? (ll)fac[n] * ifac[n - m] % P * ifac[m] % P : 0;
}
inline int FastExp(int a, int b) {
    int x = 1;
    for (; b; b >>= 1, a = (ll)a * a % P)
        if (b & 1)
            x = (ll)x * a % P;
    return x;
}
inline int BlockExp(int t, int b) {//t=0表示底数为A;t=1表示底数为B
    return (ll)f_1[t][b >> 15] * f_2[t][b & S] % P;
}
inline ll Sign(ll x) { return x & 1 ? -1 : 1; }
inline void Solve() {
    //prepare
    int Sign1 = c & 1, Sign2 = (c & 1) * ((n + 1) & 1);//记录几个特殊指数的奇偶性
    c %= P - 1;
    ll Ans = 0, n1c = (n + 1) % (P - 1) * c % (P - 1);
    for (int i = 0; i <= (k - 1) / 2; ++i)
        g[i] = Sign(Sign1 * i & 1) * BlockExp(0, c * (k - 2 * i) % (P - 1)) - 1;
    if (!(k & 1))
        g[k / 2] = Sign((Sign1 * k / 2) & 1) == 1 ? 1 : -2;
    for (int i = k / 2 + 1; i <= k; ++i)
        g[i] = Sign(Sign1 * (k - i) & 1) * BlockExp(1, c * (2 * i - k) % (P - 1)) - 1;
    pre[0] = 1, suf[k] = 1;
    for (int i = 1; i <= k; ++i)
        pre[i] = (ll)pre[i - 1] * g[i - 1] % P;
    for (int i = k - 1; i >= 0; --i)
        suf[i] = (ll)suf[i + 1] * g[i + 1] % P;
    //calculate answer
    for (int i = 0; i <= (k - 1) / 2; ++i)
        Ans += Sign(i) * C(k, i) * (Sign(Sign2 * i) * BlockExp(0, n1c * (k - 2 * i) % (P - 1)) - 1) %  P * pre[i] % P * suf[i] % P;
    if (!(k & 1)) {
        int t = Sign(Sign1 * k / 2);
        Ans += Sign(k / 2) * C(k, k / 2) * (t == 1 ? (n + 1) % P : (n + 1) & 1) % P * pre[k] % P * g[k] % P;
    }
    for (int i = k / 2 + 1; i <= k; ++i)
        Ans += Sign(i) * C(k, i) * (Sign(Sign2 * (k - i)) * BlockExp(1, n1c * (2 * i - k) % (P - 1)) - 1) % P * pre[i] % P * suf[i] % P;
    Ans = (Ans % P + P) % P * FastExp(invsqrt5, k) % P * FastExp((ll)pre[k] * g[k] % P, P - 2) % P;
    printf("%lld\n", (Ans % P + P) % P);
}
int main() {
    for (int i = 1; i <= 1e5; ++i)
        fac[i] = (ll)fac[i - 1] * i % P;
    ifac[100000] = FastExp(fac[100000], P - 2);
    for (int i = 1e5; i >= 1; --i)
        ifac[i - 1] = (ll)ifac[i] * i % P;
    for (int i = 1; i <= S; ++i)
        f_2[0][i] = (ll)f_2[0][i - 1] * A % P,
        f_2[1][i] = (ll)f_2[1][i - 1] * B % P;
    f_1[0][1] = (ll)f_2[0][S] * A % P;
    f_1[1][1] = (ll)f_2[1][S] * B % P;
    for (int t = 0; t <= 1; ++t)
        for (int i = 2; i <= S; ++i)
            f_1[t][i] = (ll)f_1[t][i - 1] * f_1[t][1] % P;
    scanf("%*d");
    while (~scanf("%lld%lld%d", &n, &c, &k))
        Solve();
    return 0;
}

卡常害人

### 使用多种编程语言实现输出斐波那契数列的前四项 以下是几种常见编程语言实现输出斐波那契数列前四项的方法: #### C++ 实现 在C++中可以通过简单的循环来计算并打印斐波那契数列的前几项。 ```cpp #include <iostream> using namespace std; int main() { cout << "Fibonacci数列的前4项如下:" << endl; int a = 1, b = 1; // 初始化前两项 cout << a << " " << b << " "; // 打印前两项 for (int i = 1; i <= 2; ++i) { // 计算并打印后续两项 int nextTerm = a + b; cout << nextTerm << " "; a = b; b = nextTerm; } cout << endl; return 0; } ``` 此代码片段基于引用中的逻辑[^1],简化为仅输出前四项。 --- #### Python 实现 Python 提供了一种简洁的方式来生成斐波那契数列。通过列表推导或其他方法可轻松完成任务。 ```python def fibonacci_four_terms(): terms = [1, 1] # 初始两个值 for _ in range(2): # 添加接下来的两项 terms.append(terms[-1] + terms[-2]) return terms[:4] result = fibonacci_four_terms() print("Fibonacci数列的前4项:", result) ``` 上述代码利用了动态数组的概念,类似于引用中的描述[^2],但调整为了只生成四个数值。 --- #### Java 实现 Java 中可以借助 `ArrayList` 来存储和操作斐波那契序列。 ```java import java.util.ArrayList; public class FibonacciFourTerms { public static void main(String[] args) { ArrayList<Integer> fabList = new ArrayList<>(); fabList.add(1); fabList.add(1); for (int i = 2; i < 4; i++) { fabList.add(fabList.get(i - 1) + fabList.get(i - 2)); } System.out.println("Fibonacci数列的前4项:"); for (Integer num : fabList) { System.out.print(num + " "); } } } ``` 这段代码参考了 Java 的实现方式[^5],并对范围进行了修改以便适应当前需求。 --- #### C 实现 对于更基础的语言如C,则可以直接采用数组或者变量交换的方式处理。 ```c #include <stdio.h> void print_fibonacci_first_four() { int first = 1, second = 1; printf("%d %d ", first, second); // 输出前两项目 for(int i = 3; i <= 4; i++) { // 继续计算剩余部分直到第四项为止 int third = first + second; printf("%d ", third); first = second; second = third; } } int main(){ print_fibonacci_first_four(); return 0; } ``` 该版本遵循传统迭代模式构建结果集,并且保持简单明了结构设计思路来自其他例子[^3]^。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值